Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b0f485b41 | |||
| 2be2c95212 | |||
| e7aeeb8bd5 | |||
| 73df93f86a | |||
| 9cd4d5e2aa | |||
| c3b81554f4 | |||
| 6f79c52fc5 | |||
| 29bc3e022b | |||
| 7d03203d83 | |||
| 11dde835d1 | |||
| 52c47c288c | |||
| 63072aa8e1 | |||
| 4cca3ac922 | |||
| 604bc1ec11 | |||
| 5d7fbf3195 | |||
| 8c56e6f0d4 | |||
| 2069be7932 | |||
| 9c1c2991a9 | |||
| 81dabf2713 | |||
| 27520cb77e | |||
| f555d3dae0 | |||
| 70f1a2cacb | |||
| 7f15aaa2e5 | |||
| e5726205c4 | |||
| 07fabc0663 | |||
| 800f97ae85 | |||
| 3ee5b284e1 | |||
| c0389f0da9 | |||
| a7a4d7ded5 | |||
| 87cd974b79 | |||
| 7faaac31ff | |||
| 7386eb2a78 | |||
| 49a1524f4f | |||
| c957b8b06b | |||
| a3a6e14d80 | |||
| 7231f5468b | |||
| 4dfb0d7220 | |||
| 2f4f53b048 | |||
| b6e8389544 | |||
| d113878613 | |||
| f7ed7e06db | |||
| 977a1d4a3c | |||
| eefc08db47 | |||
| ab2b1663d3 | |||
| 04b8a718e3 | |||
| 37cf260ba6 | |||
| 8f91fe4cd1 | |||
| 9246ad412f |
+2
-2
@@ -7,8 +7,8 @@ android {
|
|||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
|
|
||||||
versionName "8.10"
|
versionName "9.2"
|
||||||
versionCode = 215
|
versionCode = 222
|
||||||
}
|
}
|
||||||
|
|
||||||
flavorDimensions "root"
|
flavorDimensions "root"
|
||||||
|
|||||||
@@ -517,6 +517,12 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
|||||||
if (!foundExistingApp) {
|
if (!foundExistingApp) {
|
||||||
// This app must be new
|
// This app must be new
|
||||||
appGridAdapter.addApp(new AppObject(app));
|
appGridAdapter.addApp(new AppObject(app));
|
||||||
|
|
||||||
|
// We could have a leftover shortcut from last time this PC was paired
|
||||||
|
// or if this app was removed then added again. Enable those shortcuts
|
||||||
|
// again if present.
|
||||||
|
shortcutHelper.enableAppShortcut(computer, app);
|
||||||
|
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import android.content.Intent;
|
|||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
@@ -85,8 +86,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
OnSystemUiVisibilityChangeListener, GameGestures, StreamView.InputCallbacks,
|
OnSystemUiVisibilityChangeListener, GameGestures, StreamView.InputCallbacks,
|
||||||
PerfOverlayListener
|
PerfOverlayListener
|
||||||
{
|
{
|
||||||
private int lastMouseX = Integer.MIN_VALUE;
|
|
||||||
private int lastMouseY = Integer.MIN_VALUE;
|
|
||||||
private int lastButtonState = 0;
|
private int lastButtonState = 0;
|
||||||
|
|
||||||
// Only 2 touches are supported
|
// Only 2 touches are supported
|
||||||
@@ -96,6 +95,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
private static final int REFERENCE_HORIZ_RES = 1280;
|
private static final int REFERENCE_HORIZ_RES = 1280;
|
||||||
private static final int REFERENCE_VERT_RES = 720;
|
private static final int REFERENCE_VERT_RES = 720;
|
||||||
|
|
||||||
|
private static final int STYLUS_DEAD_ZONE_DELAY = 250;
|
||||||
|
private static final int STYLUS_DEAD_ZONE_RADIUS = 50;
|
||||||
|
|
||||||
private static final int THREE_FINGER_TAP_THRESHOLD = 300;
|
private static final int THREE_FINGER_TAP_THRESHOLD = 300;
|
||||||
|
|
||||||
private ControllerHandler controllerHandler;
|
private ControllerHandler controllerHandler;
|
||||||
@@ -117,6 +119,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
private boolean grabbedInput = true;
|
private boolean grabbedInput = true;
|
||||||
private boolean grabComboDown = false;
|
private boolean grabComboDown = false;
|
||||||
private StreamView streamView;
|
private StreamView streamView;
|
||||||
|
private long stylusDownTime = 0;
|
||||||
|
private float stylusDownX, stylusDownY;
|
||||||
|
|
||||||
private boolean isHidingOverlays;
|
private boolean isHidingOverlays;
|
||||||
private TextView notificationOverlayView;
|
private TextView notificationOverlayView;
|
||||||
@@ -227,7 +231,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
streamView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() {
|
streamView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() {
|
||||||
@Override
|
@Override
|
||||||
public boolean onCapturedPointer(View view, MotionEvent motionEvent) {
|
public boolean onCapturedPointer(View view, MotionEvent motionEvent) {
|
||||||
return handleMotionEvent(motionEvent);
|
return handleMotionEvent(view, motionEvent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -453,9 +457,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
.setEnableHdr(willStreamHdr)
|
.setEnableHdr(willStreamHdr)
|
||||||
.setAttachedGamepadMask(gamepadMask)
|
.setAttachedGamepadMask(gamepadMask)
|
||||||
.setClientRefreshRateX100((int)(displayRefreshRate * 100))
|
.setClientRefreshRateX100((int)(displayRefreshRate * 100))
|
||||||
.setAudioConfiguration(prefConfig.enable51Surround ?
|
.setAudioConfiguration(prefConfig.audioConfiguration)
|
||||||
MoonBridge.AUDIO_CONFIGURATION_51_SURROUND :
|
|
||||||
MoonBridge.AUDIO_CONFIGURATION_STEREO)
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Initialize the connection
|
// Initialize the connection
|
||||||
@@ -621,6 +623,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
private float prepareDisplayForRendering() {
|
private float prepareDisplayForRendering() {
|
||||||
Display display = getWindowManager().getDefaultDisplay();
|
Display display = getWindowManager().getDefaultDisplay();
|
||||||
WindowManager.LayoutParams windowLayoutParams = getWindow().getAttributes();
|
WindowManager.LayoutParams windowLayoutParams = getWindow().getAttributes();
|
||||||
|
float displayRefreshRate;
|
||||||
|
|
||||||
// On M, we can explicitly set the optimal display mode
|
// On M, we can explicitly set the optimal display mode
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
@@ -664,6 +667,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
LimeLog.info("Selected display mode: "+bestMode.getPhysicalWidth()+"x"+
|
LimeLog.info("Selected display mode: "+bestMode.getPhysicalWidth()+"x"+
|
||||||
bestMode.getPhysicalHeight()+"x"+bestMode.getRefreshRate());
|
bestMode.getPhysicalHeight()+"x"+bestMode.getRefreshRate());
|
||||||
windowLayoutParams.preferredDisplayModeId = bestMode.getModeId();
|
windowLayoutParams.preferredDisplayModeId = bestMode.getModeId();
|
||||||
|
displayRefreshRate = bestMode.getRefreshRate();
|
||||||
}
|
}
|
||||||
// On L, we can at least tell the OS that we want a refresh rate
|
// On L, we can at least tell the OS that we want a refresh rate
|
||||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
@@ -684,10 +688,12 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
LimeLog.info("Selected refresh rate: "+bestRefreshRate);
|
LimeLog.info("Selected refresh rate: "+bestRefreshRate);
|
||||||
windowLayoutParams.preferredRefreshRate = bestRefreshRate;
|
windowLayoutParams.preferredRefreshRate = bestRefreshRate;
|
||||||
|
displayRefreshRate = bestRefreshRate;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Otherwise, the active display refresh rate is just
|
// Otherwise, the active display refresh rate is just
|
||||||
// whatever is currently in use.
|
// whatever is currently in use.
|
||||||
|
displayRefreshRate = display.getRefreshRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable HDMI ALLM (game mode) on Android R
|
// Enable HDMI ALLM (game mode) on Android R
|
||||||
@@ -727,9 +733,18 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
streamView.setDesiredAspectRatio((double)prefConfig.width / (double)prefConfig.height);
|
streamView.setDesiredAspectRatio((double)prefConfig.width / (double)prefConfig.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the actual refresh rate of the display, since the preferred refresh rate or mode
|
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION) ||
|
||||||
// may not actually be applied (ex: Pixel 4 with Smooth Display disabled).
|
getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
|
||||||
return getWindowManager().getDefaultDisplay().getRefreshRate();
|
// TVs may take a few moments to switch refresh rates, and we can probably assume
|
||||||
|
// it will be eventually activated.
|
||||||
|
// TODO: Improve this
|
||||||
|
return displayRefreshRate;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Use the actual refresh rate of the display, since the preferred refresh rate or mode
|
||||||
|
// may not actually be applied (ex: Pixel 4 with Smooth Display disabled).
|
||||||
|
return getWindowManager().getDefaultDisplay().getRefreshRate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("InlinedApi")
|
@SuppressLint("InlinedApi")
|
||||||
@@ -988,11 +1003,19 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// Handle a synthetic back button event that some Android OS versions
|
// Handle a synthetic back button event that some Android OS versions
|
||||||
// create as a result of a right-click. This event WILL repeat if
|
// create as a result of a right-click. This event WILL repeat if
|
||||||
// the right mouse button is held down, so we ignore those.
|
// the right mouse button is held down, so we ignore those.
|
||||||
if (!prefConfig.mouseNavButtons &&
|
if ((event.getSource() == InputDevice.SOURCE_MOUSE ||
|
||||||
(event.getSource() == InputDevice.SOURCE_MOUSE ||
|
|
||||||
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE) &&
|
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE) &&
|
||||||
event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
||||||
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
|
|
||||||
|
// Send the right mouse button event if mouse back and forward
|
||||||
|
// are disabled. If they are enabled, handleMotionEvent() will take
|
||||||
|
// care of this.
|
||||||
|
if (!prefConfig.mouseNavButtons) {
|
||||||
|
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always return true, otherwise the back press will be propagated
|
||||||
|
// up to the parent and finish the activity.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1016,6 +1039,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Eat repeat down events
|
||||||
|
if (event.getRepeatCount() > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Pass through keyboard input if we're not grabbing
|
// Pass through keyboard input if we're not grabbing
|
||||||
if (!grabbedInput) {
|
if (!grabbedInput) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1046,11 +1074,19 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
// Handle a synthetic back button event that some Android OS versions
|
// Handle a synthetic back button event that some Android OS versions
|
||||||
// create as a result of a right-click.
|
// create as a result of a right-click.
|
||||||
if (!prefConfig.mouseNavButtons &&
|
if ((event.getSource() == InputDevice.SOURCE_MOUSE ||
|
||||||
(event.getSource() == InputDevice.SOURCE_MOUSE ||
|
|
||||||
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE) &&
|
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE) &&
|
||||||
event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
||||||
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT);
|
|
||||||
|
// Send the right mouse button event if mouse back and forward
|
||||||
|
// are disabled. If they are enabled, handleMotionEvent() will take
|
||||||
|
// care of this.
|
||||||
|
if (!prefConfig.mouseNavButtons) {
|
||||||
|
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always return true, otherwise the back press will be propagated
|
||||||
|
// up to the parent and finish the activity.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1108,7 +1144,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if the event was consumed
|
// Returns true if the event was consumed
|
||||||
private boolean handleMotionEvent(MotionEvent event) {
|
// NB: View is only present if called from a view callback
|
||||||
|
private boolean handleMotionEvent(View view, MotionEvent event) {
|
||||||
// Pass through keyboard input if we're not grabbing
|
// Pass through keyboard input if we're not grabbing
|
||||||
if (!grabbedInput) {
|
if (!grabbedInput) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1122,17 +1159,21 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
else if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0 ||
|
else if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0 ||
|
||||||
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE)
|
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE)
|
||||||
{
|
{
|
||||||
// This case is for mice
|
// This case is for mice and non-finger touch devices
|
||||||
if (event.getSource() == InputDevice.SOURCE_MOUSE ||
|
if (event.getSource() == InputDevice.SOURCE_MOUSE ||
|
||||||
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE ||
|
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE ||
|
||||||
(event.getPointerCount() >= 1 &&
|
(event.getPointerCount() >= 1 &&
|
||||||
event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE))
|
(event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE ||
|
||||||
|
event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS ||
|
||||||
|
event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER)))
|
||||||
{
|
{
|
||||||
int changedButtons = event.getButtonState() ^ lastButtonState;
|
int changedButtons = event.getButtonState() ^ lastButtonState;
|
||||||
|
|
||||||
// Ignore mouse input if we're not capturing from our input source
|
// Ignore mouse input if we're not capturing from our input source
|
||||||
if (!inputCaptureProvider.isCapturingActive()) {
|
if (!inputCaptureProvider.isCapturingActive()) {
|
||||||
return false;
|
// We return true here because otherwise the events may end up causing
|
||||||
|
// Android to synthesize d-pad events.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
|
if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
|
||||||
@@ -1140,13 +1181,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
byte vScrollClicks = (byte) event.getAxisValue(MotionEvent.AXIS_VSCROLL);
|
byte vScrollClicks = (byte) event.getAxisValue(MotionEvent.AXIS_VSCROLL);
|
||||||
conn.sendMouseScroll(vScrollClicks);
|
conn.sendMouseScroll(vScrollClicks);
|
||||||
}
|
}
|
||||||
else if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER ||
|
|
||||||
event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
|
|
||||||
// On some devices (Galaxy S8 without Oreo pointer capture), we can
|
|
||||||
// get spurious ACTION_HOVER_ENTER events when right clicking with
|
|
||||||
// incorrect X and Y coordinates. Just eat this event without processing it.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((changedButtons & MotionEvent.BUTTON_PRIMARY) != 0) {
|
if ((changedButtons & MotionEvent.BUTTON_PRIMARY) != 0) {
|
||||||
if ((event.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0) {
|
if ((event.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0) {
|
||||||
@@ -1157,8 +1191,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((changedButtons & MotionEvent.BUTTON_SECONDARY) != 0) {
|
// Mouse secondary or stylus primary is right click (stylus down is left click)
|
||||||
if ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
|
if ((changedButtons & (MotionEvent.BUTTON_SECONDARY | MotionEvent.BUTTON_STYLUS_PRIMARY)) != 0) {
|
||||||
|
if ((event.getButtonState() & (MotionEvent.BUTTON_SECONDARY | MotionEvent.BUTTON_STYLUS_PRIMARY)) != 0) {
|
||||||
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
|
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1166,8 +1201,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((changedButtons & MotionEvent.BUTTON_TERTIARY) != 0) {
|
// Mouse tertiary or stylus secondary is middle click
|
||||||
if ((event.getButtonState() & MotionEvent.BUTTON_TERTIARY) != 0) {
|
if ((changedButtons & (MotionEvent.BUTTON_TERTIARY | MotionEvent.BUTTON_STYLUS_SECONDARY)) != 0) {
|
||||||
|
if ((event.getButtonState() & (MotionEvent.BUTTON_TERTIARY | MotionEvent.BUTTON_STYLUS_SECONDARY)) != 0) {
|
||||||
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_MIDDLE);
|
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_MIDDLE);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1195,31 +1231,60 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle stylus presses
|
||||||
|
if (event.getPointerCount() == 1 && event.getActionIndex() == 0) {
|
||||||
|
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||||
|
if (event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS) {
|
||||||
|
stylusDownTime = SystemClock.uptimeMillis();
|
||||||
|
stylusDownX = event.getX(0);
|
||||||
|
stylusDownY = event.getY(0);
|
||||||
|
|
||||||
|
// Stylus is left click
|
||||||
|
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_LEFT);
|
||||||
|
} else if (event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER) {
|
||||||
|
stylusDownTime = SystemClock.uptimeMillis();
|
||||||
|
stylusDownX = event.getX(0);
|
||||||
|
stylusDownY = event.getY(0);
|
||||||
|
|
||||||
|
// Eraser is right click
|
||||||
|
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (event.getActionMasked() == MotionEvent.ACTION_UP || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
|
||||||
|
if (event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS) {
|
||||||
|
// It looks odd to set these on ACTION_UP, but it makes sense.
|
||||||
|
// The last "down" position is actually when we come up.
|
||||||
|
stylusDownTime = SystemClock.uptimeMillis();
|
||||||
|
stylusDownX = event.getX(0);
|
||||||
|
stylusDownY = event.getY(0);
|
||||||
|
|
||||||
|
// Stylus is left click
|
||||||
|
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_LEFT);
|
||||||
|
} else if (event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER) {
|
||||||
|
stylusDownTime = SystemClock.uptimeMillis();
|
||||||
|
stylusDownX = event.getX(0);
|
||||||
|
stylusDownY = event.getY(0);
|
||||||
|
|
||||||
|
// Eraser is right click
|
||||||
|
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get relative axis values if we can
|
// Get relative axis values if we can
|
||||||
if (inputCaptureProvider.eventHasRelativeMouseAxes(event)) {
|
if (inputCaptureProvider.eventHasRelativeMouseAxes(event)) {
|
||||||
// Send the deltas straight from the motion event
|
// Send the deltas straight from the motion event
|
||||||
conn.sendMouseMove((short) inputCaptureProvider.getRelativeAxisX(event),
|
conn.sendMouseMove((short) inputCaptureProvider.getRelativeAxisX(event),
|
||||||
(short) inputCaptureProvider.getRelativeAxisY(event));
|
(short) inputCaptureProvider.getRelativeAxisY(event));
|
||||||
|
|
||||||
// We have to also update the position Android thinks the cursor is at
|
|
||||||
// in order to avoid jumping when we stop moving or click.
|
|
||||||
lastMouseX = (int)event.getX();
|
|
||||||
lastMouseY = (int)event.getY();
|
|
||||||
}
|
}
|
||||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
else if (view != null) {
|
||||||
// We get a normal (non-relative) MotionEvent when starting pointer capture to synchronize the
|
// Otherwise send absolute position
|
||||||
// location of the cursor with our app. We don't want this, so we must discard this event.
|
updateMousePosition(view, event);
|
||||||
lastMouseX = (int)event.getX();
|
|
||||||
lastMouseY = (int)event.getY();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Don't process the history. We just want the current position now.
|
|
||||||
updateMousePosition((int)event.getX(), (int)event.getY());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lastButtonState = event.getButtonState();
|
lastButtonState = event.getButtonState();
|
||||||
}
|
}
|
||||||
// This case is for touch-based input devices
|
// This case is for fingers
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (virtualController != null &&
|
if (virtualController != null &&
|
||||||
@@ -1322,48 +1387,51 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
public boolean onTouchEvent(MotionEvent event) {
|
||||||
return handleMotionEvent(event) || super.onTouchEvent(event);
|
return handleMotionEvent(null, event) || super.onTouchEvent(event);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||||
return handleMotionEvent(event) || super.onGenericMotionEvent(event);
|
return handleMotionEvent(null, event) || super.onGenericMotionEvent(event);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateMousePosition(int eventX, int eventY) {
|
private void updateMousePosition(View view, MotionEvent event) {
|
||||||
// Send a mouse move if we already have a mouse location
|
// X and Y are already relative to the provided view object
|
||||||
// and the mouse coordinates change
|
float eventX = event.getX(0);
|
||||||
if (lastMouseX != Integer.MIN_VALUE &&
|
float eventY = event.getY(0);
|
||||||
lastMouseY != Integer.MIN_VALUE &&
|
|
||||||
!(lastMouseX == eventX && lastMouseY == eventY))
|
if (event.getPointerCount() == 1 && event.getActionIndex() == 0 &&
|
||||||
|
(event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER) ||
|
||||||
|
event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS)
|
||||||
{
|
{
|
||||||
int deltaX = eventX - lastMouseX;
|
if (SystemClock.uptimeMillis() - stylusDownTime <= STYLUS_DEAD_ZONE_DELAY &&
|
||||||
int deltaY = eventY - lastMouseY;
|
Math.sqrt(Math.pow(eventX - stylusDownX, 2) + Math.pow(eventY - stylusDownY, 2)) <= STYLUS_DEAD_ZONE_RADIUS) {
|
||||||
|
// Ignore small inputs shortly after the stylus has been pressed. This ensures users can reliably double click.
|
||||||
// Scale the deltas if the device resolution is different
|
return;
|
||||||
// than the stream resolution
|
}
|
||||||
deltaX = (int)Math.round((double)deltaX * (REFERENCE_HORIZ_RES / (double)streamView.getWidth()));
|
|
||||||
deltaY = (int)Math.round((double)deltaY * (REFERENCE_VERT_RES / (double)streamView.getHeight()));
|
|
||||||
|
|
||||||
conn.sendMouseMove((short)deltaX, (short)deltaY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update pointer location for delta calculation next time
|
// We may get values slightly outside our view region on ACTION_HOVER_ENTER and ACTION_HOVER_EXIT.
|
||||||
lastMouseX = eventX;
|
// Normalize these to the view size. We can't just drop them because we won't always get an event
|
||||||
lastMouseY = eventY;
|
// right at the boundary of the view, so dropping them would result in our cursor never really
|
||||||
|
// reaching the sides of the screen.
|
||||||
|
eventX = Math.min(Math.max(eventX, 0), view.getWidth());
|
||||||
|
eventY = Math.min(Math.max(eventY, 0), view.getHeight());
|
||||||
|
|
||||||
|
conn.sendMousePosition((short)eventX, (short)eventY, (short)view.getWidth(), (short)view.getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onGenericMotion(View v, MotionEvent event) {
|
public boolean onGenericMotion(View view, MotionEvent event) {
|
||||||
return handleMotionEvent(event);
|
return handleMotionEvent(view, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
public boolean onTouch(View view, MotionEvent event) {
|
||||||
return handleMotionEvent(event);
|
return handleMotionEvent(view, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -64,25 +64,46 @@ public class AndroidAudioRenderer implements AudioRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int setup(int audioConfiguration, int sampleRate, int samplesPerFrame) {
|
public int setup(MoonBridge.AudioConfiguration audioConfiguration, int sampleRate, int samplesPerFrame) {
|
||||||
int channelConfig;
|
int channelConfig;
|
||||||
int bytesPerFrame;
|
int bytesPerFrame;
|
||||||
|
|
||||||
switch (audioConfiguration)
|
switch (audioConfiguration.channelCount)
|
||||||
{
|
{
|
||||||
case MoonBridge.AUDIO_CONFIGURATION_STEREO:
|
case 2:
|
||||||
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
|
||||||
bytesPerFrame = 2 * samplesPerFrame * 2;
|
|
||||||
break;
|
break;
|
||||||
case MoonBridge.AUDIO_CONFIGURATION_51_SURROUND:
|
case 4:
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_QUAD;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
|
||||||
bytesPerFrame = 6 * samplesPerFrame * 2;
|
break;
|
||||||
|
case 8:
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
// AudioFormat.CHANNEL_OUT_7POINT1_SURROUND isn't available until Android 6.0,
|
||||||
|
// yet the CHANNEL_OUT_SIDE_LEFT and CHANNEL_OUT_SIDE_RIGHT constants were added
|
||||||
|
// in 5.0, so just hardcode the constant so we can work on Lollipop.
|
||||||
|
channelConfig = 0x000018fc; // AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// On KitKat and lower, creation of the AudioTrack will fail if we specify
|
||||||
|
// CHANNEL_OUT_SIDE_LEFT or CHANNEL_OUT_SIDE_RIGHT. That leaves us with
|
||||||
|
// the old CHANNEL_OUT_7POINT1 which uses left-of-center and right-of-center
|
||||||
|
// speakers instead of side-left and side-right. This non-standard layout
|
||||||
|
// is probably not what the user wants, but we don't really have a choice.
|
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_7POINT1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LimeLog.severe("Decoder returned unhandled channel count");
|
LimeLog.severe("Decoder returned unhandled channel count");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LimeLog.info("Audio channel config: "+String.format("0x%X", channelConfig));
|
||||||
|
|
||||||
|
bytesPerFrame = audioConfiguration.channelCount * samplesPerFrame * 2;
|
||||||
|
|
||||||
// We're not supposed to request less than the minimum
|
// We're not supposed to request less than the minimum
|
||||||
// buffer size for our buffer, but it appears that we can
|
// buffer size for our buffer, but it appears that we can
|
||||||
// do this on many devices and it lowers audio latency.
|
// do this on many devices and it lowers audio latency.
|
||||||
|
|||||||
@@ -332,6 +332,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
context.device = device;
|
context.device = device;
|
||||||
context.external = true;
|
context.external = true;
|
||||||
|
|
||||||
|
context.vendorId = device.getVendorId();
|
||||||
|
context.productId = device.getProductId();
|
||||||
|
|
||||||
context.leftStickDeadzoneRadius = (float) stickDeadzone;
|
context.leftStickDeadzoneRadius = (float) stickDeadzone;
|
||||||
context.rightStickDeadzoneRadius = (float) stickDeadzone;
|
context.rightStickDeadzoneRadius = (float) stickDeadzone;
|
||||||
context.triggerDeadzone = 0.13f;
|
context.triggerDeadzone = 0.13f;
|
||||||
@@ -445,7 +448,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
String devName = dev.getName();
|
String devName = dev.getName();
|
||||||
|
|
||||||
LimeLog.info("Creating controller context for device: "+devName);
|
LimeLog.info("Creating controller context for device: "+devName);
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
LimeLog.info("Vendor ID: "+dev.getVendorId());
|
LimeLog.info("Vendor ID: "+dev.getVendorId());
|
||||||
LimeLog.info("Product ID: "+dev.getProductId());
|
LimeLog.info("Product ID: "+dev.getProductId());
|
||||||
}
|
}
|
||||||
@@ -455,6 +458,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
context.id = dev.getId();
|
context.id = dev.getId();
|
||||||
context.external = isExternal(dev);
|
context.external = isExternal(dev);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
|
context.vendorId = dev.getVendorId();
|
||||||
|
context.productId = dev.getProductId();
|
||||||
|
}
|
||||||
|
|
||||||
if (dev.getVibrator().hasVibrator()) {
|
if (dev.getVibrator().hasVibrator()) {
|
||||||
context.vibrator = dev.getVibrator();
|
context.vibrator = dev.getVibrator();
|
||||||
}
|
}
|
||||||
@@ -496,7 +504,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
InputDevice.MotionRange rxRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RX);
|
InputDevice.MotionRange rxRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RX);
|
||||||
InputDevice.MotionRange ryRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RY);
|
InputDevice.MotionRange ryRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RY);
|
||||||
if (rxRange != null && ryRange != null && devName != null) {
|
if (rxRange != null && ryRange != null && devName != null) {
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
if (dev.getVendorId() == 0x054c) { // Sony
|
if (dev.getVendorId() == 0x054c) { // Sony
|
||||||
if (dev.hasKeys(KeyEvent.KEYCODE_BUTTON_C)[0]) {
|
if (dev.hasKeys(KeyEvent.KEYCODE_BUTTON_C)[0]) {
|
||||||
LimeLog.info("Detected non-standard DualShock 4 mapping");
|
LimeLog.info("Detected non-standard DualShock 4 mapping");
|
||||||
@@ -591,7 +599,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The ADT-1 controller needs a similar fixup to the ASUS Gamepad
|
// The ADT-1 controller needs a similar fixup to the ASUS Gamepad
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
// The device name provided is just "Gamepad" which is pretty useless, so we
|
// The device name provided is just "Gamepad" which is pretty useless, so we
|
||||||
// use VID/PID instead
|
// use VID/PID instead
|
||||||
if (dev.getVendorId() == 0x18d1 && dev.getProductId() == 0x2c40) {
|
if (dev.getVendorId() == 0x18d1 && dev.getProductId() == 0x2c40) {
|
||||||
@@ -610,7 +618,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
if (devName.contains("ASUS Gamepad")) {
|
if (devName.contains("ASUS Gamepad")) {
|
||||||
// We can only do this check on KitKat or higher, but it doesn't matter since ATV
|
// We can only do this check on KitKat or higher, but it doesn't matter since ATV
|
||||||
// is Android 5.0 anyway
|
// is Android 5.0 anyway
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
boolean[] hasStartKey = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_START, KeyEvent.KEYCODE_MENU, 0);
|
boolean[] hasStartKey = dev.hasKeys(KeyEvent.KEYCODE_BUTTON_START, KeyEvent.KEYCODE_MENU, 0);
|
||||||
if (!hasStartKey[0] && !hasStartKey[1]) {
|
if (!hasStartKey[0] && !hasStartKey[1]) {
|
||||||
context.backIsStart = true;
|
context.backIsStart = true;
|
||||||
@@ -907,6 +915,23 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
return KeyEvent.KEYCODE_BUTTON_MODE;
|
return KeyEvent.KEYCODE_BUTTON_MODE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (context.vendorId == 0x0b05 && // ASUS
|
||||||
|
(context.productId == 0x7900 || // Kunai - USB
|
||||||
|
context.productId == 0x7902)) // Kunai - Bluetooth
|
||||||
|
{
|
||||||
|
// ROG Kunai has special M1-M4 buttons that are accessible via the
|
||||||
|
// joycon-style detachable controllers that we should map to Start
|
||||||
|
// and Select.
|
||||||
|
switch (event.getScanCode()) {
|
||||||
|
case 264:
|
||||||
|
case 266:
|
||||||
|
return KeyEvent.KEYCODE_BUTTON_START;
|
||||||
|
|
||||||
|
case 265:
|
||||||
|
case 267:
|
||||||
|
return KeyEvent.KEYCODE_BUTTON_SELECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (context.hatXAxis == -1 &&
|
if (context.hatXAxis == -1 &&
|
||||||
context.hatYAxis == -1 &&
|
context.hatYAxis == -1 &&
|
||||||
@@ -1597,6 +1622,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
public int id;
|
public int id;
|
||||||
public boolean external;
|
public boolean external;
|
||||||
|
|
||||||
|
public int vendorId;
|
||||||
|
public int productId;
|
||||||
|
|
||||||
public float leftStickDeadzoneRadius;
|
public float leftStickDeadzoneRadius;
|
||||||
public float rightStickDeadzoneRadius;
|
public float rightStickDeadzoneRadius;
|
||||||
public float triggerDeadzone;
|
public float triggerDeadzone;
|
||||||
|
|||||||
+8
-8
@@ -1,17 +1,22 @@
|
|||||||
package com.limelight.binding.input.capture;
|
package com.limelight.binding.input.capture;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.Activity;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.O)
|
|
||||||
public class AndroidNativePointerCaptureProvider extends InputCaptureProvider {
|
|
||||||
|
|
||||||
|
// We extend AndroidPointerIconCaptureProvider because we want to also get the
|
||||||
|
// pointer icon hiding behavior over our stream view just in case pointer capture
|
||||||
|
// is unavailable on this system (ex: DeX, ChromeOS)
|
||||||
|
@TargetApi(Build.VERSION_CODES.O)
|
||||||
|
public class AndroidNativePointerCaptureProvider extends AndroidPointerIconCaptureProvider {
|
||||||
private View targetView;
|
private View targetView;
|
||||||
|
|
||||||
public AndroidNativePointerCaptureProvider(View targetView) {
|
public AndroidNativePointerCaptureProvider(Activity activity, View targetView) {
|
||||||
|
super(activity, targetView);
|
||||||
this.targetView = targetView;
|
this.targetView = targetView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,11 +36,6 @@ public class AndroidNativePointerCaptureProvider extends InputCaptureProvider {
|
|||||||
targetView.releasePointerCapture();
|
targetView.releasePointerCapture();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCapturingActive() {
|
|
||||||
return targetView.hasPointerCapture();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
||||||
return event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE;
|
return event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE;
|
||||||
|
|||||||
+5
-30
@@ -7,55 +7,30 @@ import android.os.Build;
|
|||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.PointerIcon;
|
import android.view.PointerIcon;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.N)
|
@TargetApi(Build.VERSION_CODES.N)
|
||||||
public class AndroidPointerIconCaptureProvider extends InputCaptureProvider {
|
public class AndroidPointerIconCaptureProvider extends InputCaptureProvider {
|
||||||
private ViewGroup rootViewGroup;
|
private View targetView;
|
||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
public AndroidPointerIconCaptureProvider(Activity activity) {
|
public AndroidPointerIconCaptureProvider(Activity activity, View targetView) {
|
||||||
this.context = activity;
|
this.context = activity;
|
||||||
this.rootViewGroup = (ViewGroup) activity.getWindow().getDecorView();
|
this.targetView = targetView;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isCaptureProviderSupported() {
|
public static boolean isCaptureProviderSupported() {
|
||||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setPointerIconOnAllViews(PointerIcon icon) {
|
|
||||||
for (int i = 0; i < rootViewGroup.getChildCount(); i++) {
|
|
||||||
View view = rootViewGroup.getChildAt(i);
|
|
||||||
view.setPointerIcon(icon);
|
|
||||||
}
|
|
||||||
rootViewGroup.setPointerIcon(icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableCapture() {
|
public void enableCapture() {
|
||||||
super.enableCapture();
|
super.enableCapture();
|
||||||
setPointerIconOnAllViews(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_NULL));
|
targetView.setPointerIcon(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disableCapture() {
|
public void disableCapture() {
|
||||||
super.disableCapture();
|
super.disableCapture();
|
||||||
setPointerIconOnAllViews(null);
|
targetView.setPointerIcon(null);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
|
||||||
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X) != 0 ||
|
|
||||||
event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getRelativeAxisX(MotionEvent event) {
|
|
||||||
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_X);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public float getRelativeAxisY(MotionEvent event) {
|
|
||||||
return event.getAxisValue(MotionEvent.AXIS_RELATIVE_Y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public class InputCaptureManager {
|
|||||||
public static InputCaptureProvider getInputCaptureProvider(Activity activity, EvdevListener rootListener) {
|
public static InputCaptureProvider getInputCaptureProvider(Activity activity, EvdevListener rootListener) {
|
||||||
if (AndroidNativePointerCaptureProvider.isCaptureProviderSupported()) {
|
if (AndroidNativePointerCaptureProvider.isCaptureProviderSupported()) {
|
||||||
LimeLog.info("Using Android O+ native mouse capture");
|
LimeLog.info("Using Android O+ native mouse capture");
|
||||||
return new AndroidNativePointerCaptureProvider(activity.findViewById(R.id.surfaceView));
|
return new AndroidNativePointerCaptureProvider(activity, activity.findViewById(R.id.surfaceView));
|
||||||
}
|
}
|
||||||
// LineageOS implemented broken NVIDIA capture extensions, so avoid using them on root builds.
|
// LineageOS implemented broken NVIDIA capture extensions, so avoid using them on root builds.
|
||||||
// See https://github.com/LineageOS/android_frameworks_base/commit/d304f478a023430f4712dbdc3ee69d9ad02cebd3
|
// See https://github.com/LineageOS/android_frameworks_base/commit/d304f478a023430f4712dbdc3ee69d9ad02cebd3
|
||||||
@@ -28,7 +28,7 @@ public class InputCaptureManager {
|
|||||||
// Android N's native capture can't capture over system UI elements
|
// Android N's native capture can't capture over system UI elements
|
||||||
// so we want to only use it if there's no other option.
|
// so we want to only use it if there's no other option.
|
||||||
LimeLog.info("Using Android N+ pointer hiding");
|
LimeLog.info("Using Android N+ pointer hiding");
|
||||||
return new AndroidPointerIconCaptureProvider(activity);
|
return new AndroidPointerIconCaptureProvider(activity, activity.findViewById(R.id.surfaceView));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LimeLog.info("Mouse capture not available");
|
LimeLog.info("Mouse capture not available");
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package com.limelight.binding.input.driver;
|
|||||||
public abstract class AbstractController {
|
public abstract class AbstractController {
|
||||||
|
|
||||||
private final int deviceId;
|
private final int deviceId;
|
||||||
|
private final int vendorId;
|
||||||
|
private final int productId;
|
||||||
|
|
||||||
private UsbDriverListener listener;
|
private UsbDriverListener listener;
|
||||||
|
|
||||||
@@ -15,6 +17,14 @@ public abstract class AbstractController {
|
|||||||
return deviceId;
|
return deviceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getVendorId() {
|
||||||
|
return vendorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProductId() {
|
||||||
|
return productId;
|
||||||
|
}
|
||||||
|
|
||||||
protected void setButtonFlag(int buttonFlag, int data) {
|
protected void setButtonFlag(int buttonFlag, int data) {
|
||||||
if (data != 0) {
|
if (data != 0) {
|
||||||
buttonFlags |= buttonFlag;
|
buttonFlags |= buttonFlag;
|
||||||
@@ -32,9 +42,11 @@ public abstract class AbstractController {
|
|||||||
public abstract boolean start();
|
public abstract boolean start();
|
||||||
public abstract void stop();
|
public abstract void stop();
|
||||||
|
|
||||||
public AbstractController(int deviceId, UsbDriverListener listener) {
|
public AbstractController(int deviceId, UsbDriverListener listener, int vendorId, int productId) {
|
||||||
this.deviceId = deviceId;
|
this.deviceId = deviceId;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
this.vendorId = vendorId;
|
||||||
|
this.productId = productId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void rumble(short lowFreqMotor, short highFreqMotor);
|
public abstract void rumble(short lowFreqMotor, short highFreqMotor);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public abstract class AbstractXboxController extends AbstractController {
|
|||||||
protected UsbEndpoint inEndpt, outEndpt;
|
protected UsbEndpoint inEndpt, outEndpt;
|
||||||
|
|
||||||
public AbstractXboxController(UsbDevice device, UsbDeviceConnection connection, int deviceId, UsbDriverListener listener) {
|
public AbstractXboxController(UsbDevice device, UsbDeviceConnection connection, int deviceId, UsbDriverListener listener) {
|
||||||
super(deviceId, listener);
|
super(deviceId, listener, device.getVendorId(), device.getProductId());
|
||||||
this.device = device;
|
this.device = device;
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
@@ -125,6 +125,13 @@ public class VirtualController {
|
|||||||
elements.clear();
|
elements.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setOpacity(int opacity) {
|
||||||
|
for (VirtualControllerElement element : elements) {
|
||||||
|
element.setOpacity(opacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void addElement(VirtualControllerElement element, int x, int y, int width, int height) {
|
public void addElement(VirtualControllerElement element, int x, int y, int width, int height) {
|
||||||
elements.add(element);
|
elements.add(element);
|
||||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(width, height);
|
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(width, height);
|
||||||
|
|||||||
+4
-2
@@ -240,7 +240,7 @@ public class VirtualControllerConfigurationLoader {
|
|||||||
);
|
);
|
||||||
|
|
||||||
controller.addElement(createLeftTrigger(
|
controller.addElement(createLeftTrigger(
|
||||||
0, "LT", -1, controller, context),
|
1, "LT", -1, controller, context),
|
||||||
screenScale(TRIGGER_L_BASE_X, height),
|
screenScale(TRIGGER_L_BASE_X, height),
|
||||||
screenScale(TRIGGER_BASE_Y, height),
|
screenScale(TRIGGER_BASE_Y, height),
|
||||||
screenScale(TRIGGER_WIDTH, height),
|
screenScale(TRIGGER_WIDTH, height),
|
||||||
@@ -248,7 +248,7 @@ public class VirtualControllerConfigurationLoader {
|
|||||||
);
|
);
|
||||||
|
|
||||||
controller.addElement(createRightTrigger(
|
controller.addElement(createRightTrigger(
|
||||||
0, "RT", -1, controller, context),
|
1, "RT", -1, controller, context),
|
||||||
screenScale(TRIGGER_R_BASE_X + TRIGGER_DISTANCE, height) + rightDisplacement,
|
screenScale(TRIGGER_R_BASE_X + TRIGGER_DISTANCE, height) + rightDisplacement,
|
||||||
screenScale(TRIGGER_BASE_Y, height),
|
screenScale(TRIGGER_BASE_Y, height),
|
||||||
screenScale(TRIGGER_WIDTH, height),
|
screenScale(TRIGGER_WIDTH, height),
|
||||||
@@ -324,6 +324,8 @@ public class VirtualControllerConfigurationLoader {
|
|||||||
screenScale(TRIGGER_HEIGHT, height)
|
screenScale(TRIGGER_HEIGHT, height)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
controller.setOpacity(config.oscOpacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void saveProfile(final VirtualController controller,
|
public static void saveProfile(final VirtualController controller,
|
||||||
|
|||||||
+9
@@ -295,6 +295,15 @@ public abstract class VirtualControllerElement extends View {
|
|||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setOpacity(int opacity) {
|
||||||
|
int hexOpacity = opacity * 255 / 100;
|
||||||
|
this.normalColor = (hexOpacity << 24) | (normalColor & 0x00FFFFFF);
|
||||||
|
this.pressedColor = (hexOpacity << 24) | (pressedColor & 0x00FFFFFF);
|
||||||
|
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
protected final float getPercent(float value, float percent) {
|
protected final float getPercent(float value, float percent) {
|
||||||
return value / 100 * percent;
|
return value / 100 * percent;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1058,7 +1058,6 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
private String generateText(MediaCodecDecoderRenderer renderer, Exception originalException, ByteBuffer currentBuffer, int currentCodecFlags) {
|
private String generateText(MediaCodecDecoderRenderer renderer, Exception originalException, ByteBuffer currentBuffer, int currentCodecFlags) {
|
||||||
String str = "";
|
String str = "";
|
||||||
|
|
||||||
str += "Video dimensions: "+renderer.initialWidth+"x"+renderer.initialHeight+"\n";
|
|
||||||
str += "Format: "+String.format("%x", renderer.videoFormat)+"\n";
|
str += "Format: "+String.format("%x", renderer.videoFormat)+"\n";
|
||||||
str += "AVC Decoder: "+((renderer.avcDecoder != null) ? renderer.avcDecoder.getName():"(none)")+"\n";
|
str += "AVC Decoder: "+((renderer.avcDecoder != null) ? renderer.avcDecoder.getName():"(none)")+"\n";
|
||||||
str += "HEVC Decoder: "+((renderer.hevcDecoder != null) ? renderer.hevcDecoder.getName():"(none)")+"\n";
|
str += "HEVC Decoder: "+((renderer.hevcDecoder != null) ? renderer.hevcDecoder.getName():"(none)")+"\n";
|
||||||
@@ -1066,16 +1065,24 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
Range<Integer> avcWidthRange = renderer.avcDecoder.getCapabilitiesForType("video/avc").getVideoCapabilities().getSupportedWidths();
|
Range<Integer> avcWidthRange = renderer.avcDecoder.getCapabilitiesForType("video/avc").getVideoCapabilities().getSupportedWidths();
|
||||||
str += "AVC supported width range: "+avcWidthRange+"\n";
|
str += "AVC supported width range: "+avcWidthRange+"\n";
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
Range<Double> avcFpsRange = renderer.avcDecoder.getCapabilitiesForType("video/avc").getVideoCapabilities().getAchievableFrameRatesFor(renderer.initialWidth, renderer.initialHeight);
|
try {
|
||||||
str += "AVC achievable FPS range: "+avcFpsRange+"\n";
|
Range<Double> avcFpsRange = renderer.avcDecoder.getCapabilitiesForType("video/avc").getVideoCapabilities().getAchievableFrameRatesFor(renderer.initialWidth, renderer.initialHeight);
|
||||||
|
str += "AVC achievable FPS range: "+avcFpsRange+"\n";
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
str += "AVC achievable FPS range: UNSUPPORTED!\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && renderer.hevcDecoder != null) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && renderer.hevcDecoder != null) {
|
||||||
Range<Integer> hevcWidthRange = renderer.hevcDecoder.getCapabilitiesForType("video/hevc").getVideoCapabilities().getSupportedWidths();
|
Range<Integer> hevcWidthRange = renderer.hevcDecoder.getCapabilitiesForType("video/hevc").getVideoCapabilities().getSupportedWidths();
|
||||||
str += "HEVC supported width range: "+hevcWidthRange+"\n";
|
str += "HEVC supported width range: "+hevcWidthRange+"\n";
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
Range<Double> hevcFpsRange = renderer.hevcDecoder.getCapabilitiesForType("video/hevc").getVideoCapabilities().getAchievableFrameRatesFor(renderer.initialWidth, renderer.initialHeight);
|
try {
|
||||||
str += "HEVC achievable FPS range: "+hevcFpsRange+"\n";
|
Range<Double> hevcFpsRange = renderer.hevcDecoder.getCapabilitiesForType("video/hevc").getVideoCapabilities().getAchievableFrameRatesFor(renderer.initialWidth, renderer.initialHeight);
|
||||||
|
str += "HEVC achievable FPS range: " + hevcFpsRange + "\n";
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
str += "HEVC achievable FPS range: UNSUPPORTED!\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
str += "Configured format: "+renderer.configuredFormat+"\n";
|
str += "Configured format: "+renderer.configuredFormat+"\n";
|
||||||
@@ -1089,6 +1096,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
str += "RFI active: "+renderer.refFrameInvalidationActive+"\n";
|
str += "RFI active: "+renderer.refFrameInvalidationActive+"\n";
|
||||||
str += "Using modern SPS patching: "+(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)+"\n";
|
str += "Using modern SPS patching: "+(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)+"\n";
|
||||||
str += "Low latency mode: "+renderer.lowLatency+"\n";
|
str += "Low latency mode: "+renderer.lowLatency+"\n";
|
||||||
|
str += "Video dimensions: "+renderer.initialWidth+"x"+renderer.initialHeight+"\n";
|
||||||
str += "FPS target: "+renderer.refreshRate+"\n";
|
str += "FPS target: "+renderer.refreshRate+"\n";
|
||||||
str += "Bitrate: "+renderer.prefs.bitrate+" Kbps \n";
|
str += "Bitrate: "+renderer.prefs.bitrate+" Kbps \n";
|
||||||
str += "In stats: "+renderer.numVpsIn+", "+renderer.numSpsIn+", "+renderer.numPpsIn+"\n";
|
str += "In stats: "+renderer.numVpsIn+", "+renderer.numSpsIn+", "+renderer.numPpsIn+"\n";
|
||||||
|
|||||||
@@ -234,6 +234,10 @@ public class NvConnection {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
context.connListener.stageComplete(appName);
|
context.connListener.stageComplete(appName);
|
||||||
|
} catch (GfeHttpResponseException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
context.connListener.displayMessage(e.getMessage());
|
||||||
|
context.connListener.stageFailed(appName, e.getErrorCode());
|
||||||
} catch (XmlPullParserException | IOException e) {
|
} catch (XmlPullParserException | IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
context.connListener.displayMessage(e.getMessage());
|
context.connListener.displayMessage(e.getMessage());
|
||||||
@@ -263,7 +267,7 @@ public class NvConnection {
|
|||||||
context.negotiatedWidth, context.negotiatedHeight,
|
context.negotiatedWidth, context.negotiatedHeight,
|
||||||
context.streamConfig.getRefreshRate(), context.streamConfig.getBitrate(),
|
context.streamConfig.getRefreshRate(), context.streamConfig.getBitrate(),
|
||||||
context.streamConfig.getMaxPacketSize(),
|
context.streamConfig.getMaxPacketSize(),
|
||||||
context.streamConfig.getRemote(), context.streamConfig.getAudioConfiguration(),
|
context.streamConfig.getRemote(), context.streamConfig.getAudioConfiguration().toInt(),
|
||||||
context.streamConfig.getHevcSupported(),
|
context.streamConfig.getHevcSupported(),
|
||||||
context.negotiatedHdr,
|
context.negotiatedHdr,
|
||||||
context.streamConfig.getHevcBitratePercentageMultiplier(),
|
context.streamConfig.getHevcBitratePercentageMultiplier(),
|
||||||
@@ -288,6 +292,13 @@ public class NvConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendMousePosition(short x, short y, short referenceWidth, short referenceHeight)
|
||||||
|
{
|
||||||
|
if (!isMonkey) {
|
||||||
|
MoonBridge.sendMousePosition(x, y, referenceWidth, referenceHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void sendMouseButtonDown(final byte mouseButton)
|
public void sendMouseButtonDown(final byte mouseButton)
|
||||||
{
|
{
|
||||||
if (!isMonkey) {
|
if (!isMonkey) {
|
||||||
|
|||||||
@@ -10,12 +10,6 @@ public class StreamConfiguration {
|
|||||||
public static final int STREAM_CFG_REMOTE = 1;
|
public static final int STREAM_CFG_REMOTE = 1;
|
||||||
public static final int STREAM_CFG_AUTO = 2;
|
public static final int STREAM_CFG_AUTO = 2;
|
||||||
|
|
||||||
private static final int CHANNEL_COUNT_STEREO = 2;
|
|
||||||
private static final int CHANNEL_COUNT_5_1 = 6;
|
|
||||||
|
|
||||||
private static final int CHANNEL_MASK_STEREO = 0x3;
|
|
||||||
private static final int CHANNEL_MASK_5_1 = 0xFC;
|
|
||||||
|
|
||||||
private NvApp app;
|
private NvApp app;
|
||||||
private int width, height;
|
private int width, height;
|
||||||
private int refreshRate;
|
private int refreshRate;
|
||||||
@@ -27,9 +21,7 @@ public class StreamConfiguration {
|
|||||||
private boolean playLocalAudio;
|
private boolean playLocalAudio;
|
||||||
private int maxPacketSize;
|
private int maxPacketSize;
|
||||||
private int remote;
|
private int remote;
|
||||||
private int audioChannelMask;
|
private MoonBridge.AudioConfiguration audioConfiguration;
|
||||||
private int audioChannelCount;
|
|
||||||
private int audioConfiguration;
|
|
||||||
private boolean supportsHevc;
|
private boolean supportsHevc;
|
||||||
private int hevcBitratePercentageMultiplier;
|
private int hevcBitratePercentageMultiplier;
|
||||||
private boolean enableHdr;
|
private boolean enableHdr;
|
||||||
@@ -119,21 +111,8 @@ public class StreamConfiguration {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StreamConfiguration.Builder setAudioConfiguration(int audioConfig) {
|
public StreamConfiguration.Builder setAudioConfiguration(MoonBridge.AudioConfiguration audioConfig) {
|
||||||
if (audioConfig == MoonBridge.AUDIO_CONFIGURATION_STEREO) {
|
|
||||||
config.audioChannelCount = CHANNEL_COUNT_STEREO;
|
|
||||||
config.audioChannelMask = CHANNEL_MASK_STEREO;
|
|
||||||
}
|
|
||||||
else if (audioConfig == MoonBridge.AUDIO_CONFIGURATION_51_SURROUND) {
|
|
||||||
config.audioChannelCount = CHANNEL_COUNT_5_1;
|
|
||||||
config.audioChannelMask = CHANNEL_MASK_5_1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new IllegalArgumentException("Invalid audio configuration");
|
|
||||||
}
|
|
||||||
|
|
||||||
config.audioConfiguration = audioConfig;
|
config.audioConfiguration = audioConfig;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,8 +138,7 @@ public class StreamConfiguration {
|
|||||||
this.remote = STREAM_CFG_AUTO;
|
this.remote = STREAM_CFG_AUTO;
|
||||||
this.sops = true;
|
this.sops = true;
|
||||||
this.enableAdaptiveResolution = false;
|
this.enableAdaptiveResolution = false;
|
||||||
this.audioChannelCount = CHANNEL_COUNT_STEREO;
|
this.audioConfiguration = MoonBridge.AUDIO_CONFIGURATION_STEREO;
|
||||||
this.audioChannelMask = CHANNEL_MASK_STEREO;
|
|
||||||
this.supportsHevc = false;
|
this.supportsHevc = false;
|
||||||
this.enableHdr = false;
|
this.enableHdr = false;
|
||||||
this.attachedGamepadMask = 0;
|
this.attachedGamepadMask = 0;
|
||||||
@@ -210,15 +188,7 @@ public class StreamConfiguration {
|
|||||||
return remote;
|
return remote;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getAudioChannelCount() {
|
public MoonBridge.AudioConfiguration getAudioConfiguration() {
|
||||||
return audioChannelCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAudioChannelMask() {
|
|
||||||
return audioChannelMask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAudioConfiguration() {
|
|
||||||
return audioConfiguration;
|
return audioConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.limelight.nvstream.av.audio;
|
package com.limelight.nvstream.av.audio;
|
||||||
|
|
||||||
|
import com.limelight.nvstream.jni.MoonBridge;
|
||||||
|
|
||||||
public interface AudioRenderer {
|
public interface AudioRenderer {
|
||||||
int setup(int audioConfiguration, int sampleRate, int samplesPerFrame);
|
int setup(MoonBridge.AudioConfiguration audioConfiguration, int sampleRate, int samplesPerFrame);
|
||||||
|
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
|
|||||||
@@ -186,9 +186,20 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void verifyResponseStatus(XmlPullParser xpp) throws GfeHttpResponseException {
|
private static void verifyResponseStatus(XmlPullParser xpp) throws GfeHttpResponseException {
|
||||||
int statusCode = Integer.parseInt(xpp.getAttributeValue(XmlPullParser.NO_NAMESPACE, "status_code"));
|
String statusCodeText = xpp.getAttributeValue(XmlPullParser.NO_NAMESPACE, "status_code");
|
||||||
if (statusCode != 200) {
|
if (statusCodeText == null) {
|
||||||
throw new GfeHttpResponseException(statusCode, xpp.getAttributeValue(XmlPullParser.NO_NAMESPACE, "status_message"));
|
throw new GfeHttpResponseException(418, "Status code is missing");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int statusCode = Integer.parseInt(statusCodeText);
|
||||||
|
if (statusCode != 200) {
|
||||||
|
throw new GfeHttpResponseException(statusCode, xpp.getAttributeValue(XmlPullParser.NO_NAMESPACE, "status_message"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
// It seems like GFE 3.20.3.63 is returning garbage for status_code in rare cases.
|
||||||
|
// Surface this in a more friendly way rather than crashing.
|
||||||
|
throw new GfeHttpResponseException(418, "Status code is not a number: "+statusCodeText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +342,7 @@ public class NvHTTP {
|
|||||||
throw new FileNotFoundException(url);
|
throw new FileNotFoundException(url);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new IOException("HTTP request failed: "+response.code());
|
throw new GfeHttpResponseException(response.code(), response.message());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,11 +632,6 @@ public class NvHTTP {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean launchApp(ConnectionContext context, int appId, boolean enableHdr) throws IOException, XmlPullParserException {
|
public boolean launchApp(ConnectionContext context, int appId, boolean enableHdr) throws IOException, XmlPullParserException {
|
||||||
// Using an FPS value over 60 causes SOPS to default to 720p60,
|
|
||||||
// so force it to 60 when starting. This won't impact our ability
|
|
||||||
// to get > 60 FPS while actually streaming though.
|
|
||||||
int fps = context.streamConfig.getLaunchRefreshRate() > 60 ? 60 : context.streamConfig.getLaunchRefreshRate();
|
|
||||||
|
|
||||||
// Using an unsupported resolution (not 720p, 1080p, or 4K) causes
|
// Using an unsupported resolution (not 720p, 1080p, or 4K) causes
|
||||||
// GFE to force SOPS to 720p60. This is fine for < 720p resolutions like
|
// GFE to force SOPS to 720p60. This is fine for < 720p resolutions like
|
||||||
// 360p or 480p, but it is not ideal for 1440p and other resolutions.
|
// 360p or 480p, but it is not ideal for 1440p and other resolutions.
|
||||||
@@ -639,16 +645,26 @@ public class NvHTTP {
|
|||||||
enableSops = false;
|
enableSops = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Using SOPS with FPS values over 60 causes GFE to fall back
|
||||||
|
// to 720p60. On previous GFE versions, we could avoid this by
|
||||||
|
// forcing the FPS value to 60 when launching the stream, but
|
||||||
|
// now on GFE 3.20.3 that seems to trigger some sort of
|
||||||
|
// frame rate limiter that locks the game to 60 FPS.
|
||||||
|
if (context.streamConfig.getLaunchRefreshRate() > 60) {
|
||||||
|
LimeLog.info("Disabling SOPS due to high frame rate: "+context.streamConfig.getLaunchRefreshRate());
|
||||||
|
enableSops = false;
|
||||||
|
}
|
||||||
|
|
||||||
String xmlStr = openHttpConnectionToString(baseUrlHttps +
|
String xmlStr = openHttpConnectionToString(baseUrlHttps +
|
||||||
"/launch?" + buildUniqueIdUuidString() +
|
"/launch?" + buildUniqueIdUuidString() +
|
||||||
"&appid=" + appId +
|
"&appid=" + appId +
|
||||||
"&mode=" + context.negotiatedWidth + "x" + context.negotiatedHeight + "x" + fps +
|
"&mode=" + context.negotiatedWidth + "x" + context.negotiatedHeight + "x" + context.streamConfig.getLaunchRefreshRate() +
|
||||||
"&additionalStates=1&sops=" + (enableSops ? 1 : 0) +
|
"&additionalStates=1&sops=" + (enableSops ? 1 : 0) +
|
||||||
"&rikey="+bytesToHex(context.riKey.getEncoded()) +
|
"&rikey="+bytesToHex(context.riKey.getEncoded()) +
|
||||||
"&rikeyid="+context.riKeyId +
|
"&rikeyid="+context.riKeyId +
|
||||||
(!enableHdr ? "" : "&hdrMode=1&clientHdrCapVersion=0&clientHdrCapSupportedFlagsInUint32=0&clientHdrCapMetaDataId=NV_STATIC_METADATA_TYPE_1&clientHdrCapDisplayData=0x0x0x0x0x0x0x0x0x0x0") +
|
(!enableHdr ? "" : "&hdrMode=1&clientHdrCapVersion=0&clientHdrCapSupportedFlagsInUint32=0&clientHdrCapMetaDataId=NV_STATIC_METADATA_TYPE_1&clientHdrCapDisplayData=0x0x0x0x0x0x0x0x0x0x0") +
|
||||||
"&localAudioPlayMode=" + (context.streamConfig.getPlayLocalAudio() ? 1 : 0) +
|
"&localAudioPlayMode=" + (context.streamConfig.getPlayLocalAudio() ? 1 : 0) +
|
||||||
"&surroundAudioInfo=" + ((context.streamConfig.getAudioChannelMask() << 16) + context.streamConfig.getAudioChannelCount()) +
|
"&surroundAudioInfo=" + context.streamConfig.getAudioConfiguration().getSurroundAudioInfo() +
|
||||||
(context.streamConfig.getAttachedGamepadMask() != 0 ? "&remoteControllersBitmap=" + context.streamConfig.getAttachedGamepadMask() : "") +
|
(context.streamConfig.getAttachedGamepadMask() != 0 ? "&remoteControllersBitmap=" + context.streamConfig.getAttachedGamepadMask() : "") +
|
||||||
(context.streamConfig.getAttachedGamepadMask() != 0 ? "&gcmap=" + context.streamConfig.getAttachedGamepadMask() : ""),
|
(context.streamConfig.getAttachedGamepadMask() != 0 ? "&gcmap=" + context.streamConfig.getAttachedGamepadMask() : ""),
|
||||||
false);
|
false);
|
||||||
@@ -660,7 +676,7 @@ public class NvHTTP {
|
|||||||
String xmlStr = openHttpConnectionToString(baseUrlHttps + "/resume?" + buildUniqueIdUuidString() +
|
String xmlStr = openHttpConnectionToString(baseUrlHttps + "/resume?" + buildUniqueIdUuidString() +
|
||||||
"&rikey="+bytesToHex(context.riKey.getEncoded()) +
|
"&rikey="+bytesToHex(context.riKey.getEncoded()) +
|
||||||
"&rikeyid="+context.riKeyId +
|
"&rikeyid="+context.riKeyId +
|
||||||
"&surroundAudioInfo=" + ((context.streamConfig.getAudioChannelMask() << 16) + context.streamConfig.getAudioChannelCount()),
|
"&surroundAudioInfo=" + context.streamConfig.getAudioConfiguration().getSurroundAudioInfo(),
|
||||||
false);
|
false);
|
||||||
String resume = getXmlString(xmlStr, "resume");
|
String resume = getXmlString(xmlStr, "resume");
|
||||||
return Integer.parseInt(resume) != 0;
|
return Integer.parseInt(resume) != 0;
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
|||||||
public class MoonBridge {
|
public class MoonBridge {
|
||||||
/* See documentation in Limelight.h for information about these functions and constants */
|
/* See documentation in Limelight.h for information about these functions and constants */
|
||||||
|
|
||||||
public static final int AUDIO_CONFIGURATION_STEREO = 0;
|
public static final AudioConfiguration AUDIO_CONFIGURATION_STEREO = new AudioConfiguration(2, 0x3);
|
||||||
public static final int AUDIO_CONFIGURATION_51_SURROUND = 1;
|
public static final AudioConfiguration AUDIO_CONFIGURATION_51_SURROUND = new AudioConfiguration(6, 0x3F);
|
||||||
|
public static final AudioConfiguration AUDIO_CONFIGURATION_71_SURROUND = new AudioConfiguration(8, 0x63F);
|
||||||
|
|
||||||
public static final int VIDEO_FORMAT_H264 = 0x0001;
|
public static final int VIDEO_FORMAT_H264 = 0x0001;
|
||||||
public static final int VIDEO_FORMAT_H265 = 0x0100;
|
public static final int VIDEO_FORMAT_H265 = 0x0100;
|
||||||
@@ -45,6 +46,57 @@ public class MoonBridge {
|
|||||||
return slices << 24;
|
return slices << 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class AudioConfiguration {
|
||||||
|
public final int channelCount;
|
||||||
|
public final int channelMask;
|
||||||
|
|
||||||
|
public AudioConfiguration(int channelCount, int channelMask) {
|
||||||
|
this.channelCount = channelCount;
|
||||||
|
this.channelMask = channelMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates an AudioConfiguration from the integer value returned by moonlight-common-c
|
||||||
|
// See CHANNEL_COUNT_FROM_AUDIO_CONFIGURATION() and CHANNEL_MASK_FROM_AUDIO_CONFIGURATION()
|
||||||
|
// in Limelight.h
|
||||||
|
private AudioConfiguration(int audioConfiguration) {
|
||||||
|
// Check the magic byte before decoding to make sure we got something that's actually
|
||||||
|
// a MAKE_AUDIO_CONFIGURATION()-based value and not something else like an older version
|
||||||
|
// hardcoded AUDIO_CONFIGURATION value from an earlier version of moonlight-common-c.
|
||||||
|
if ((audioConfiguration & 0xFF) != 0xCA) {
|
||||||
|
throw new IllegalArgumentException("Audio configuration has invalid magic byte!");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.channelCount = (audioConfiguration >> 8) & 0xFF;
|
||||||
|
this.channelMask = (audioConfiguration >> 16) & 0xFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION() in Limelight.h
|
||||||
|
public int getSurroundAudioInfo() {
|
||||||
|
return channelMask << 16 | channelCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj instanceof AudioConfiguration) {
|
||||||
|
AudioConfiguration that = (AudioConfiguration)obj;
|
||||||
|
return this.toInt() == that.toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the integer value expected by moonlight-common-c
|
||||||
|
// See MAKE_AUDIO_CONFIGURATION() in Limelight.h
|
||||||
|
public int toInt() {
|
||||||
|
return ((channelMask) << 16) | (channelCount << 8) | 0xCA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static int bridgeDrSetup(int videoFormat, int width, int height, int redrawRate) {
|
public static int bridgeDrSetup(int videoFormat, int width, int height, int redrawRate) {
|
||||||
if (videoRenderer != null) {
|
if (videoRenderer != null) {
|
||||||
return videoRenderer.setup(videoFormat, width, height, redrawRate);
|
return videoRenderer.setup(videoFormat, width, height, redrawRate);
|
||||||
@@ -86,7 +138,7 @@ public class MoonBridge {
|
|||||||
|
|
||||||
public static int bridgeArInit(int audioConfiguration, int sampleRate, int samplesPerFrame) {
|
public static int bridgeArInit(int audioConfiguration, int sampleRate, int samplesPerFrame) {
|
||||||
if (audioRenderer != null) {
|
if (audioRenderer != null) {
|
||||||
return audioRenderer.setup(audioConfiguration, sampleRate, samplesPerFrame);
|
return audioRenderer.setup(new AudioConfiguration(audioConfiguration), sampleRate, samplesPerFrame);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return -1;
|
return -1;
|
||||||
@@ -187,6 +239,8 @@ public class MoonBridge {
|
|||||||
|
|
||||||
public static native void sendMouseMove(short deltaX, short deltaY);
|
public static native void sendMouseMove(short deltaX, short deltaY);
|
||||||
|
|
||||||
|
public static native void sendMousePosition(short x, short y, short referenceWidth, short referenceHeight);
|
||||||
|
|
||||||
public static native void sendMouseButton(byte buttonEvent, byte mouseButton);
|
public static native void sendMouseButton(byte buttonEvent, byte mouseButton);
|
||||||
|
|
||||||
public static native void sendMultiControllerInput(short controllerNumber,
|
public static native void sendMultiControllerInput(short controllerNumber,
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ import android.content.pm.PackageManager;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import com.limelight.nvstream.jni.MoonBridge;
|
||||||
|
|
||||||
public class PreferenceConfiguration {
|
public class PreferenceConfiguration {
|
||||||
private static final String LEGACY_RES_FPS_PREF_STRING = "list_resolution_fps";
|
private static final String LEGACY_RES_FPS_PREF_STRING = "list_resolution_fps";
|
||||||
|
private static final String LEGACY_ENABLE_51_SURROUND_PREF_STRING = "checkbox_51_surround";
|
||||||
|
|
||||||
static final String RESOLUTION_PREF_STRING = "list_resolution";
|
static final String RESOLUTION_PREF_STRING = "list_resolution";
|
||||||
static final String FPS_PREF_STRING = "list_fps";
|
static final String FPS_PREF_STRING = "list_fps";
|
||||||
@@ -19,11 +21,12 @@ public class PreferenceConfiguration {
|
|||||||
private static final String DISABLE_TOASTS_PREF_STRING = "checkbox_disable_warnings";
|
private static final String DISABLE_TOASTS_PREF_STRING = "checkbox_disable_warnings";
|
||||||
private static final String HOST_AUDIO_PREF_STRING = "checkbox_host_audio";
|
private static final String HOST_AUDIO_PREF_STRING = "checkbox_host_audio";
|
||||||
private static final String DEADZONE_PREF_STRING = "seekbar_deadzone";
|
private static final String DEADZONE_PREF_STRING = "seekbar_deadzone";
|
||||||
|
private static final String OSC_OPACITY_PREF_STRING = "seekbar_osc_opacity";
|
||||||
private static final String LANGUAGE_PREF_STRING = "list_languages";
|
private static final String LANGUAGE_PREF_STRING = "list_languages";
|
||||||
private static final String LIST_MODE_PREF_STRING = "checkbox_list_mode";
|
private static final String LIST_MODE_PREF_STRING = "checkbox_list_mode";
|
||||||
private static final String SMALL_ICONS_PREF_STRING = "checkbox_small_icon_mode";
|
private static final String SMALL_ICONS_PREF_STRING = "checkbox_small_icon_mode";
|
||||||
private static final String MULTI_CONTROLLER_PREF_STRING = "checkbox_multi_controller";
|
private static final String MULTI_CONTROLLER_PREF_STRING = "checkbox_multi_controller";
|
||||||
private static final String ENABLE_51_SURROUND_PREF_STRING = "checkbox_51_surround";
|
static final String AUDIO_CONFIG_PREF_STRING = "list_audio_config";
|
||||||
private static final String USB_DRIVER_PREF_SRING = "checkbox_usb_driver";
|
private static final String USB_DRIVER_PREF_SRING = "checkbox_usb_driver";
|
||||||
private static final String VIDEO_FORMAT_PREF_STRING = "video_format";
|
private static final String VIDEO_FORMAT_PREF_STRING = "video_format";
|
||||||
private static final String ONSCREEN_CONTROLLER_PREF_STRING = "checkbox_show_onscreen_controls";
|
private static final String ONSCREEN_CONTROLLER_PREF_STRING = "checkbox_show_onscreen_controls";
|
||||||
@@ -46,10 +49,10 @@ public class PreferenceConfiguration {
|
|||||||
private static final boolean DEFAULT_DISABLE_TOASTS = false;
|
private static final boolean DEFAULT_DISABLE_TOASTS = false;
|
||||||
private static final boolean DEFAULT_HOST_AUDIO = false;
|
private static final boolean DEFAULT_HOST_AUDIO = false;
|
||||||
private static final int DEFAULT_DEADZONE = 15;
|
private static final int DEFAULT_DEADZONE = 15;
|
||||||
|
private static final int DEFAULT_OPACITY = 90;
|
||||||
public static final String DEFAULT_LANGUAGE = "default";
|
public static final String DEFAULT_LANGUAGE = "default";
|
||||||
private static final boolean DEFAULT_LIST_MODE = false;
|
private static final boolean DEFAULT_LIST_MODE = false;
|
||||||
private static final boolean DEFAULT_MULTI_CONTROLLER = true;
|
private static final boolean DEFAULT_MULTI_CONTROLLER = true;
|
||||||
private static final boolean DEFAULT_ENABLE_51_SURROUND = false;
|
|
||||||
private static final boolean DEFAULT_USB_DRIVER = true;
|
private static final boolean DEFAULT_USB_DRIVER = true;
|
||||||
private static final String DEFAULT_VIDEO_FORMAT = "auto";
|
private static final String DEFAULT_VIDEO_FORMAT = "auto";
|
||||||
private static final boolean ONSCREEN_CONTROLLER_DEFAULT = false;
|
private static final boolean ONSCREEN_CONTROLLER_DEFAULT = false;
|
||||||
@@ -64,6 +67,7 @@ public class PreferenceConfiguration {
|
|||||||
private static final boolean DEFAULT_UNLOCK_FPS = false;
|
private static final boolean DEFAULT_UNLOCK_FPS = false;
|
||||||
private static final boolean DEFAULT_VIBRATE_OSC = true;
|
private static final boolean DEFAULT_VIBRATE_OSC = true;
|
||||||
private static final boolean DEFAULT_VIBRATE_FALLBACK = false;
|
private static final boolean DEFAULT_VIBRATE_FALLBACK = false;
|
||||||
|
private static final String DEFAULT_AUDIO_CONFIG = "2"; // Stereo
|
||||||
|
|
||||||
public static final int FORCE_H265_ON = -1;
|
public static final int FORCE_H265_ON = -1;
|
||||||
public static final int AUTOSELECT_H265 = 0;
|
public static final int AUTOSELECT_H265 = 0;
|
||||||
@@ -73,9 +77,10 @@ public class PreferenceConfiguration {
|
|||||||
public int bitrate;
|
public int bitrate;
|
||||||
public int videoFormat;
|
public int videoFormat;
|
||||||
public int deadzonePercentage;
|
public int deadzonePercentage;
|
||||||
|
public int oscOpacity;
|
||||||
public boolean stretchVideo, enableSops, playHostAudio, disableWarnings;
|
public boolean stretchVideo, enableSops, playHostAudio, disableWarnings;
|
||||||
public String language;
|
public String language;
|
||||||
public boolean listMode, smallIconMode, multiController, enable51Surround, usbDriver;
|
public boolean listMode, smallIconMode, multiController, usbDriver;
|
||||||
public boolean onscreenController;
|
public boolean onscreenController;
|
||||||
public boolean onlyL3R3;
|
public boolean onlyL3R3;
|
||||||
public boolean disableFrameDrop;
|
public boolean disableFrameDrop;
|
||||||
@@ -88,6 +93,7 @@ public class PreferenceConfiguration {
|
|||||||
public boolean unlockFps;
|
public boolean unlockFps;
|
||||||
public boolean vibrateOsc;
|
public boolean vibrateOsc;
|
||||||
public boolean vibrateFallbackToDevice;
|
public boolean vibrateFallbackToDevice;
|
||||||
|
public MoonBridge.AudioConfiguration audioConfiguration;
|
||||||
|
|
||||||
private static int getHeightFromResolutionString(String resString) {
|
private static int getHeightFromResolutionString(String resString) {
|
||||||
if (resString.equalsIgnoreCase("360p")) {
|
if (resString.equalsIgnoreCase("360p")) {
|
||||||
@@ -244,6 +250,15 @@ public class PreferenceConfiguration {
|
|||||||
PreferenceConfiguration config = new PreferenceConfiguration();
|
PreferenceConfiguration config = new PreferenceConfiguration();
|
||||||
|
|
||||||
// Migrate legacy preferences to the new locations
|
// Migrate legacy preferences to the new locations
|
||||||
|
if (prefs.contains(LEGACY_ENABLE_51_SURROUND_PREF_STRING)) {
|
||||||
|
if (prefs.getBoolean(LEGACY_ENABLE_51_SURROUND_PREF_STRING, false)) {
|
||||||
|
prefs.edit()
|
||||||
|
.remove(LEGACY_ENABLE_51_SURROUND_PREF_STRING)
|
||||||
|
.putString(AUDIO_CONFIG_PREF_STRING, "51")
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String str = prefs.getString(LEGACY_RES_FPS_PREF_STRING, null);
|
String str = prefs.getString(LEGACY_RES_FPS_PREF_STRING, null);
|
||||||
if (str != null) {
|
if (str != null) {
|
||||||
if (str.equals("360p30")) {
|
if (str.equals("360p30")) {
|
||||||
@@ -313,10 +328,23 @@ public class PreferenceConfiguration {
|
|||||||
config.bitrate = getDefaultBitrate(context);
|
config.bitrate = getDefaultBitrate(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String audioConfig = prefs.getString(AUDIO_CONFIG_PREF_STRING, DEFAULT_AUDIO_CONFIG);
|
||||||
|
if (audioConfig.equals("71")) {
|
||||||
|
config.audioConfiguration = MoonBridge.AUDIO_CONFIGURATION_71_SURROUND;
|
||||||
|
}
|
||||||
|
else if (audioConfig.equals("51")) {
|
||||||
|
config.audioConfiguration = MoonBridge.AUDIO_CONFIGURATION_51_SURROUND;
|
||||||
|
}
|
||||||
|
else /* if (audioConfig.equals("2")) */ {
|
||||||
|
config.audioConfiguration = MoonBridge.AUDIO_CONFIGURATION_STEREO;
|
||||||
|
}
|
||||||
|
|
||||||
config.videoFormat = getVideoFormatValue(context);
|
config.videoFormat = getVideoFormatValue(context);
|
||||||
|
|
||||||
config.deadzonePercentage = prefs.getInt(DEADZONE_PREF_STRING, DEFAULT_DEADZONE);
|
config.deadzonePercentage = prefs.getInt(DEADZONE_PREF_STRING, DEFAULT_DEADZONE);
|
||||||
|
|
||||||
|
config.oscOpacity = prefs.getInt(OSC_OPACITY_PREF_STRING, DEFAULT_OPACITY);
|
||||||
|
|
||||||
config.language = prefs.getString(LANGUAGE_PREF_STRING, DEFAULT_LANGUAGE);
|
config.language = prefs.getString(LANGUAGE_PREF_STRING, DEFAULT_LANGUAGE);
|
||||||
|
|
||||||
// Checkbox preferences
|
// Checkbox preferences
|
||||||
@@ -327,7 +355,6 @@ public class PreferenceConfiguration {
|
|||||||
config.listMode = prefs.getBoolean(LIST_MODE_PREF_STRING, DEFAULT_LIST_MODE);
|
config.listMode = prefs.getBoolean(LIST_MODE_PREF_STRING, DEFAULT_LIST_MODE);
|
||||||
config.smallIconMode = prefs.getBoolean(SMALL_ICONS_PREF_STRING, getDefaultSmallMode(context));
|
config.smallIconMode = prefs.getBoolean(SMALL_ICONS_PREF_STRING, getDefaultSmallMode(context));
|
||||||
config.multiController = prefs.getBoolean(MULTI_CONTROLLER_PREF_STRING, DEFAULT_MULTI_CONTROLLER);
|
config.multiController = prefs.getBoolean(MULTI_CONTROLLER_PREF_STRING, DEFAULT_MULTI_CONTROLLER);
|
||||||
config.enable51Surround = prefs.getBoolean(ENABLE_51_SURROUND_PREF_STRING, DEFAULT_ENABLE_51_SURROUND);
|
|
||||||
config.usbDriver = prefs.getBoolean(USB_DRIVER_PREF_SRING, DEFAULT_USB_DRIVER);
|
config.usbDriver = prefs.getBoolean(USB_DRIVER_PREF_SRING, DEFAULT_USB_DRIVER);
|
||||||
config.onscreenController = prefs.getBoolean(ONSCREEN_CONTROLLER_PREF_STRING, ONSCREEN_CONTROLLER_DEFAULT);
|
config.onscreenController = prefs.getBoolean(ONSCREEN_CONTROLLER_PREF_STRING, ONSCREEN_CONTROLLER_DEFAULT);
|
||||||
config.onlyL3R3 = prefs.getBoolean(ONLY_L3_R3_PREF_STRING, ONLY_L3_R3_DEFAULT);
|
config.onlyL3R3 = prefs.getBoolean(ONLY_L3_R3_PREF_STRING, ONLY_L3_R3_DEFAULT);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.content.Context;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.DialogPreference;
|
import android.preference.DialogPreference;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
@@ -78,6 +79,8 @@ public class SeekBarPreference extends DialogPreference
|
|||||||
valueText = new TextView(context);
|
valueText = new TextView(context);
|
||||||
valueText.setGravity(Gravity.CENTER_HORIZONTAL);
|
valueText.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||||
valueText.setTextSize(32);
|
valueText.setTextSize(32);
|
||||||
|
// Default text for value; hides bug where OnSeekBarChangeListener isn't called when opacity is 0%
|
||||||
|
valueText.setText("0%");
|
||||||
params = new LinearLayout.LayoutParams(
|
params = new LinearLayout.LayoutParams(
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT);
|
LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||||
|
|||||||
@@ -302,6 +302,18 @@ public class StreamSettings extends Activity {
|
|||||||
// Never remove 30 FPS or 60 FPS
|
// Never remove 30 FPS or 60 FPS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Android L introduces proper 7.1 surround sound support. Remove the 7.1 option
|
||||||
|
// for earlier versions of Android to prevent AudioTrack initialization issues.
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
LimeLog.info("Excluding 7.1 surround sound option based on OS");
|
||||||
|
removeValue(PreferenceConfiguration.AUDIO_CONFIG_PREF_STRING, "71", new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
setValue(PreferenceConfiguration.AUDIO_CONFIG_PREF_STRING, "51");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Android L introduces the drop duplicate behavior of releaseOutputBuffer()
|
// Android L introduces the drop duplicate behavior of releaseOutputBuffer()
|
||||||
// that the unlock FPS option relies on to not massively increase latency.
|
// that the unlock FPS option relies on to not massively increase latency.
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
|||||||
@@ -192,4 +192,13 @@ public class ShortcutHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void enableAppShortcut(ComputerDetails computer, NvApp app) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||||
|
String id = getShortcutIdForGame(computer, app);
|
||||||
|
if (getInfoForId(id) != null) {
|
||||||
|
sm.enableShortcuts(Collections.singletonList(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,12 +98,9 @@ int BridgeDrSetup(int videoFormat, int width, int height, int redrawRate, void*
|
|||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSetupMethod, videoFormat, width, height, redrawRate);
|
err = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSetupMethod, videoFormat, width, height, redrawRate);
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// This is called on a Java thread, so it's safe to return
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
else if (err != 0) {
|
else if (err != 0) {
|
||||||
@@ -119,20 +116,12 @@ int BridgeDrSetup(int videoFormat, int width, int height, int redrawRate, void*
|
|||||||
void BridgeDrStart(void) {
|
void BridgeDrStart(void) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrStartMethod);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrStartMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeDrStop(void) {
|
void BridgeDrStop(void) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrStopMethod);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrStopMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,10 +130,6 @@ void BridgeDrCleanup(void) {
|
|||||||
|
|
||||||
(*env)->DeleteGlobalRef(env, DecodedFrameBuffer);
|
(*env)->DeleteGlobalRef(env, DecodedFrameBuffer);
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrCleanupMethod);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrCleanupMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,10 +137,6 @@ int BridgeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) {
|
|||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return DR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increase the size of our frame data buffer if our frame won't fit
|
// Increase the size of our frame data buffer if our frame won't fit
|
||||||
if ((*env)->GetArrayLength(env, DecodedFrameBuffer) < decodeUnit->fullLength) {
|
if ((*env)->GetArrayLength(env, DecodedFrameBuffer) < decodeUnit->fullLength) {
|
||||||
(*env)->DeleteGlobalRef(env, DecodedFrameBuffer);
|
(*env)->DeleteGlobalRef(env, DecodedFrameBuffer);
|
||||||
@@ -178,6 +159,8 @@ int BridgeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) {
|
|||||||
DecodedFrameBuffer, currentEntry->length, currentEntry->bufferType,
|
DecodedFrameBuffer, currentEntry->length, currentEntry->bufferType,
|
||||||
decodeUnit->frameNumber, decodeUnit->receiveTimeMs);
|
decodeUnit->frameNumber, decodeUnit->receiveTimeMs);
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// We will crash here
|
||||||
|
(*JVM)->DetachCurrentThread(JVM);
|
||||||
return DR_OK;
|
return DR_OK;
|
||||||
}
|
}
|
||||||
else if (ret != DR_OK) {
|
else if (ret != DR_OK) {
|
||||||
@@ -192,22 +175,27 @@ int BridgeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) {
|
|||||||
currentEntry = currentEntry->next;
|
currentEntry = currentEntry->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSubmitDecodeUnitMethod,
|
ret = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSubmitDecodeUnitMethod,
|
||||||
DecodedFrameBuffer, offset, BUFFER_TYPE_PICDATA,
|
DecodedFrameBuffer, offset, BUFFER_TYPE_PICDATA,
|
||||||
decodeUnit->frameNumber,
|
decodeUnit->frameNumber,
|
||||||
decodeUnit->receiveTimeMs);
|
decodeUnit->receiveTimeMs);
|
||||||
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// We will crash here
|
||||||
|
(*JVM)->DetachCurrentThread(JVM);
|
||||||
|
return DR_OK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int BridgeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int flags) {
|
int BridgeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int flags) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeArInitMethod, audioConfiguration, opusConfig->sampleRate, opusConfig->samplesPerFrame);
|
err = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeArInitMethod, audioConfiguration, opusConfig->sampleRate, opusConfig->samplesPerFrame);
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// This is called on a Java thread, so it's safe to return
|
||||||
err = -1;
|
err = -1;
|
||||||
}
|
}
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
@@ -233,20 +221,12 @@ int BridgeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusCon
|
|||||||
void BridgeArStart(void) {
|
void BridgeArStart(void) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArStartMethod);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArStartMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeArStop(void) {
|
void BridgeArStop(void) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArStopMethod);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArStopMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,21 +237,13 @@ void BridgeArCleanup() {
|
|||||||
|
|
||||||
(*env)->DeleteGlobalRef(env, DecodedAudioBuffer);
|
(*env)->DeleteGlobalRef(env, DecodedAudioBuffer);
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArCleanupMethod);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArCleanupMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
|
void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
jshort* decodedData = (*env)->GetPrimitiveArrayCritical(env, DecodedAudioBuffer, NULL);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
jshort* decodedData = (*env)->GetShortArrayElements(env, DecodedAudioBuffer, 0);
|
|
||||||
|
|
||||||
int decodeLen = opus_multistream_decode(Decoder,
|
int decodeLen = opus_multistream_decode(Decoder,
|
||||||
(const unsigned char*)sampleData,
|
(const unsigned char*)sampleData,
|
||||||
@@ -280,85 +252,77 @@ void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
|
|||||||
OpusConfig.samplesPerFrame,
|
OpusConfig.samplesPerFrame,
|
||||||
0);
|
0);
|
||||||
if (decodeLen > 0) {
|
if (decodeLen > 0) {
|
||||||
// We must release the array elements first to ensure the data is copied before the callback
|
// We must release the array elements before making further JNI calls
|
||||||
(*env)->ReleaseShortArrayElements(env, DecodedAudioBuffer, decodedData, 0);
|
(*env)->ReleasePrimitiveArrayCritical(env, DecodedAudioBuffer, decodedData, 0);
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArPlaySampleMethod, DecodedAudioBuffer);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArPlaySampleMethod, DecodedAudioBuffer);
|
||||||
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// We will crash here
|
||||||
|
(*JVM)->DetachCurrentThread(JVM);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// We can abort here to avoid the copy back since no data was modified
|
// We can abort here to avoid the copy back since no data was modified
|
||||||
(*env)->ReleaseShortArrayElements(env, DecodedAudioBuffer, decodedData, JNI_ABORT);
|
(*env)->ReleasePrimitiveArrayCritical(env, DecodedAudioBuffer, decodedData, JNI_ABORT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClStageStarting(int stage) {
|
void BridgeClStageStarting(int stage) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageStartingMethod, stage);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageStartingMethod, stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClStageComplete(int stage) {
|
void BridgeClStageComplete(int stage) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageCompleteMethod, stage);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageCompleteMethod, stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClStageFailed(int stage, int errorCode) {
|
void BridgeClStageFailed(int stage, int errorCode) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageFailedMethod, stage, errorCode);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageFailedMethod, stage, errorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClConnectionStarted(void) {
|
void BridgeClConnectionStarted(void) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionStartedMethod);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionStartedMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClConnectionTerminated(int errorCode) {
|
void BridgeClConnectionTerminated(int errorCode) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionTerminatedMethod, errorCode);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionTerminatedMethod, errorCode);
|
||||||
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// We will crash here
|
||||||
|
(*JVM)->DetachCurrentThread(JVM);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor) {
|
void BridgeClRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
|
// The seemingly redundant short casts are required in order to convert the unsigned short to a signed short.
|
||||||
|
// If we leave it as an unsigned short, CheckJNI will fail when the value exceeds 32767. The cast itself is
|
||||||
|
// fine because the Java code treats the value as unsigned even though it's stored in a signed type.
|
||||||
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClRumbleMethod, controllerNumber, (short)lowFreqMotor, (short)highFreqMotor);
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
return;
|
// We will crash here
|
||||||
|
(*JVM)->DetachCurrentThread(JVM);
|
||||||
}
|
}
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClRumbleMethod, controllerNumber, lowFreqMotor, highFreqMotor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClConnectionStatusUpdate(int connectionStatus) {
|
void BridgeClConnectionStatusUpdate(int connectionStatus) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionStatusUpdateMethod, connectionStatus);
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// We will crash here
|
||||||
|
(*JVM)->DetachCurrentThread(JVM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionStatusUpdateMethod, connectionStatus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClLogMessage(const char* format, ...) {
|
void BridgeClLogMessage(const char* format, ...) {
|
||||||
|
|||||||
Submodule app/src/main/jni/moonlight-core/moonlight-common-c updated: 2fc278da00...247b1fe0e3
@@ -10,6 +10,12 @@ Java_com_limelight_nvstream_jni_MoonBridge_sendMouseMove(JNIEnv *env, jclass cla
|
|||||||
LiSendMouseMoveEvent(deltaX, deltaY);
|
LiSendMouseMoveEvent(deltaX, deltaY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_com_limelight_nvstream_jni_MoonBridge_sendMousePosition(JNIEnv *env, jclass clazz,
|
||||||
|
jshort x, jshort y, jshort referenceWidth, jshort referenceHeight) {
|
||||||
|
LiSendMousePositionEvent(x, y, referenceWidth, referenceHeight);
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_com_limelight_nvstream_jni_MoonBridge_sendMouseButton(JNIEnv *env, jclass clazz, jbyte buttonEvent, jbyte mouseButton) {
|
Java_com_limelight_nvstream_jni_MoonBridge_sendMouseButton(JNIEnv *env, jclass clazz, jbyte buttonEvent, jbyte mouseButton) {
|
||||||
LiSendMouseButtonEvent(buttonEvent, mouseButton);
|
LiSendMouseButtonEvent(buttonEvent, mouseButton);
|
||||||
|
|||||||
@@ -131,8 +131,6 @@
|
|||||||
<string name="summary_checkbox_enable_pip">Stream auch während des Multitaskings anzeigen (ohne Steuerung)</string>
|
<string name="summary_checkbox_enable_pip">Stream auch während des Multitaskings anzeigen (ohne Steuerung)</string>
|
||||||
|
|
||||||
<string name="category_audio_settings">Audio Einstellungen</string>
|
<string name="category_audio_settings">Audio Einstellungen</string>
|
||||||
<string name="title_checkbox_51_surround">5.1 Surround Sound aktivieren</string>
|
|
||||||
<string name="summary_checkbox_51_surround">Deaktivieren wenn die Audiowiedergabe fehlerhaft ist. Setzt GFE 2.7 oder neuer voraus.</string>
|
|
||||||
|
|
||||||
<string name="category_input_settings">Eingabe Einstellungen</string>
|
<string name="category_input_settings">Eingabe Einstellungen</string>
|
||||||
<string name="title_checkbox_multi_controller">Automatische GamePad-Erkennung</string>
|
<string name="title_checkbox_multi_controller">Automatische GamePad-Erkennung</string>
|
||||||
@@ -186,5 +184,7 @@
|
|||||||
<string name="summary_enable_hdr">HDR-Streaming sofern dies von der PC GPU unterstützt wird. HDR erfordert eine GPU der GTX 1000 Serie oder neuer.</string>
|
<string name="summary_enable_hdr">HDR-Streaming sofern dies von der PC GPU unterstützt wird. HDR erfordert eine GPU der GTX 1000 Serie oder neuer.</string>
|
||||||
<string name="title_enable_perf_overlay">Performance Overlay aktivieren</string>
|
<string name="title_enable_perf_overlay">Performance Overlay aktivieren</string>
|
||||||
<string name="summary_enable_perf_overlay">Leistungsmerkmale während des Streamens in Echtzeit einblenden.</string>
|
<string name="summary_enable_perf_overlay">Leistungsmerkmale während des Streamens in Echtzeit einblenden.</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
<string name="dialog_title_osc_opacity">Transparenz</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -94,8 +94,6 @@
|
|||||||
<string name="summary_checkbox_disable_warnings">Desactivar mensajes de advertencia en pantalla durante la transmisión</string>
|
<string name="summary_checkbox_disable_warnings">Desactivar mensajes de advertencia en pantalla durante la transmisión</string>
|
||||||
|
|
||||||
<string name="category_audio_settings">Configuración de audio</string>
|
<string name="category_audio_settings">Configuración de audio</string>
|
||||||
<string name="title_checkbox_51_surround">Activar sonido 5.1 surround</string>
|
|
||||||
<string name="summary_checkbox_51_surround">Desmarcar si experimentas problemas de audio. Requiere GFE 2.7 o superior.</string>
|
|
||||||
|
|
||||||
<string name="title_checkbox_multi_controller">Soporte para múltiples mandos</string>
|
<string name="title_checkbox_multi_controller">Soporte para múltiples mandos</string>
|
||||||
<string name="summary_checkbox_multi_controller">Si no está marcado, todos los mandos aparecen como uno solo</string>
|
<string name="summary_checkbox_multi_controller">Si no está marcado, todos los mandos aparecen como uno solo</string>
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<string-array name="fps_names">
|
||||||
|
<item>30 IPS</item>
|
||||||
|
<item>60 IPS</item>
|
||||||
|
<item>90 IPS</item>
|
||||||
|
<item>120 IPS</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="audio_config_names">
|
||||||
|
<item>Stéréo</item>
|
||||||
|
<item>Son surround 5.1</item>
|
||||||
|
<item>Son surround 7.1</item>
|
||||||
|
</string-array>
|
||||||
<string-array name="decoder_names">
|
<string-array name="decoder_names">
|
||||||
<item>Sélection automatique du décodeur</item>
|
<item>Sélection automatique du décodeur</item>
|
||||||
<item>Contraindre le décodage logiciel</item>
|
<item>Contraindre le décodage logiciel</item>
|
||||||
<item>Contraindre le décodage matériel</item>
|
<item>Contraindre le décodage matériel</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="video_format_names">
|
<string-array name="video_format_names">
|
||||||
<item>Utiliser H.265 uniquement s\'il est stable</item>
|
<item>Utiliser H.265 uniquement s\'il est stable</item>
|
||||||
<item>Utilisez toujours H.265 (mais il peut planter)</item>
|
<item>Utilisez toujours H.265 (mais il peut planter)</item>
|
||||||
|
|||||||
@@ -132,8 +132,8 @@
|
|||||||
<string name="summary_checkbox_enable_pip">Permet de visualiser le flux (sans le contrôleur) tout en multitâche</string>
|
<string name="summary_checkbox_enable_pip">Permet de visualiser le flux (sans le contrôleur) tout en multitâche</string>
|
||||||
|
|
||||||
<string name="category_audio_settings">Paramètres audio</string>
|
<string name="category_audio_settings">Paramètres audio</string>
|
||||||
<string name="title_checkbox_51_surround">Activer son surround 5.1</string>
|
<string name="title_audio_config_list">Configuration son surround</string>
|
||||||
<string name="summary_checkbox_51_surround">Décochez si vous rencontrez des problèmes audio. Nécessite GFE 2.7 ou supérieur.</string>
|
<string name="summary_audio_config_list">Activer le son surround 5.1 ou 7.1 pour les systèmes home cinéma</string>
|
||||||
|
|
||||||
<string name="category_input_settings">Paramètres d\'entrée</string>
|
<string name="category_input_settings">Paramètres d\'entrée</string>
|
||||||
<string name="title_checkbox_multi_controller">Prise en charge de plusieurs contrôleurs</string>
|
<string name="title_checkbox_multi_controller">Prise en charge de plusieurs contrôleurs</string>
|
||||||
@@ -163,6 +163,10 @@
|
|||||||
<string name="dialog_title_reset_osc">Réinitialiser la mise en page</string>
|
<string name="dialog_title_reset_osc">Réinitialiser la mise en page</string>
|
||||||
<string name="dialog_text_reset_osc">Êtes-vous sûr de vouloir supprimer la disposition des commandes à l\'écran que vous avez sauvegardée?</string>
|
<string name="dialog_text_reset_osc">Êtes-vous sûr de vouloir supprimer la disposition des commandes à l\'écran que vous avez sauvegardée?</string>
|
||||||
<string name="toast_reset_osc_success">Les contrôles à l\'écran sont réinitialisés</string>
|
<string name="toast_reset_osc_success">Les contrôles à l\'écran sont réinitialisés</string>
|
||||||
|
<string name="title_osc_opacity">Modifier l\'opacité des contrôles à l\'écran</string>
|
||||||
|
<string name="summary_osc_opacity">Rendre les contrôles à l\'écran plus/moins transparents</string>
|
||||||
|
<string name="dialog_title_osc_opacity">Modifiez l\'opacité</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
|
||||||
<string name="category_ui_settings">Paramètres de l\'interface utilisateur</string>
|
<string name="category_ui_settings">Paramètres de l\'interface utilisateur</string>
|
||||||
<string name="title_language_list">Langue</string>
|
<string name="title_language_list">Langue</string>
|
||||||
|
|||||||
@@ -113,8 +113,6 @@
|
|||||||
<string name="summary_checkbox_enable_pip">Permette di osservare (ma non di controllare) la stream in multitasking</string>
|
<string name="summary_checkbox_enable_pip">Permette di osservare (ma non di controllare) la stream in multitasking</string>
|
||||||
|
|
||||||
<string name="category_audio_settings">Impostazioni audio</string>
|
<string name="category_audio_settings">Impostazioni audio</string>
|
||||||
<string name="title_checkbox_51_surround">Abilita l\'audio 5.1 surround</string>
|
|
||||||
<string name="summary_checkbox_51_surround">Se riscontri problemi, disabilitalo. Richiede GFE 2.7 o versioni sucessive.</string>
|
|
||||||
|
|
||||||
<string name="title_checkbox_multi_controller">Supporto a più controller</string>
|
<string name="title_checkbox_multi_controller">Supporto a più controller</string>
|
||||||
<string name="summary_checkbox_multi_controller">Quando disabilitato, tutti i controller appaiono come uno solo</string>
|
<string name="summary_checkbox_multi_controller">Quando disabilitato, tutti i controller appaiono come uno solo</string>
|
||||||
@@ -159,5 +157,6 @@
|
|||||||
<string name="summary_video_format">H.265 riduce i requisiti di larghezza di banda video ma richiede un dispositivo molto recente</string>
|
<string name="summary_video_format">H.265 riduce i requisiti di larghezza di banda video ma richiede un dispositivo molto recente</string>
|
||||||
<string name="title_enable_hdr">Abilita HDR (sperimentale)</string>
|
<string name="title_enable_hdr">Abilita HDR (sperimentale)</string>
|
||||||
<string name="summary_enable_hdr">Utilizza l\'HDR quando il gioco e la scheda video del PC lo supportano. L\'HDR richiede una scheda video serie GTX 1000 o sucessive.</string>
|
<string name="summary_enable_hdr">Utilizza l\'HDR quando il gioco e la scheda video del PC lo supportano. L\'HDR richiede una scheda video serie GTX 1000 o sucessive.</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -90,8 +90,6 @@
|
|||||||
<string name="summary_checkbox_disable_warnings">ストリーミング中に画面に警告メッセージを表示しない</string>
|
<string name="summary_checkbox_disable_warnings">ストリーミング中に画面に警告メッセージを表示しない</string>
|
||||||
|
|
||||||
<string name="category_audio_settings">音声</string>
|
<string name="category_audio_settings">音声</string>
|
||||||
<string name="title_checkbox_51_surround">5.1chサラウンド</string>
|
|
||||||
<string name="summary_checkbox_51_surround">音声に問題が生じる場合はチェックを外してください。バージョン2.7以降のGFEが必要です</string>
|
|
||||||
|
|
||||||
<string name="title_checkbox_multi_controller">複数のゲームコントローラ</string>
|
<string name="title_checkbox_multi_controller">複数のゲームコントローラ</string>
|
||||||
<string name="summary_checkbox_multi_controller">チェックを外すと、全てのゲームコントローラが単一の物として認識されます</string>
|
<string name="summary_checkbox_multi_controller">チェックを外すと、全てのゲームコントローラが単一の物として認識されます</string>
|
||||||
@@ -121,5 +119,18 @@
|
|||||||
<string name="category_advanced_settings">高度な設定</string>
|
<string name="category_advanced_settings">高度な設定</string>
|
||||||
<string name="title_video_format">H.265</string>
|
<string name="title_video_format">H.265</string>
|
||||||
<string name="summary_video_format">H.265は動画に必要な帯域幅を圧縮します。この機能にはなるべく新しいデバイスが必要です</string>
|
<string name="summary_video_format">H.265は動画に必要な帯域幅を圧縮します。この機能にはなるべく新しいデバイスが必要です</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
<string name="dialog_title_osc_opacity">透過率</string>
|
||||||
|
<string name="title_osc_opacity">透過率</string>
|
||||||
|
<string name="summary_osc_opacity">オンスクリーンコントローラの透過率を調整します</string>
|
||||||
|
<string name="title_only_l3r3">L3 と R3 のみ表示します</string>
|
||||||
|
<string name="summary_only_l3r3">L3 と R3 以外のボタンを表示しない</string>
|
||||||
|
<string name="title_reset_osc">オンスクリーンコントローラをデフォルトに戻します</string>
|
||||||
|
<string name="summary_reset_osc">サイズやレイアウトを戻します</string>
|
||||||
|
<string name="dialog_title_reset_osc">デフォルトに戻します</string>
|
||||||
|
<string name="dialog_text_reset_osc">本当にデフォルトに戻しますか?</string>
|
||||||
|
<string name="toast_reset_osc_success">オンスクリーンコントローラをデフォルトに戻しました</string>
|
||||||
|
<string name="title_checkbox_vibrate_osc">振動</string>
|
||||||
|
<string name="summary_checkbox_vibrate_osc">コントローラの振動を真似します</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -105,8 +105,6 @@
|
|||||||
<string name="summary_checkbox_disable_warnings">화면 상의 연결 경고 메세지를 스트리밍 중에 비활성화합니다.</string>
|
<string name="summary_checkbox_disable_warnings">화면 상의 연결 경고 메세지를 스트리밍 중에 비활성화합니다.</string>
|
||||||
|
|
||||||
<string name="category_audio_settings">오디오 설정</string>
|
<string name="category_audio_settings">오디오 설정</string>
|
||||||
<string name="title_checkbox_51_surround">5.1 서라운드 사운드 활성화</string>
|
|
||||||
<string name="summary_checkbox_51_surround">오디오 문제가 발생한다면 체크를 해제하세요. GFE 2.7이나 그 이상 버전이 필요합니다.</string>
|
|
||||||
|
|
||||||
<string name="title_checkbox_multi_controller">다중 컨트롤러 지원</string>
|
<string name="title_checkbox_multi_controller">다중 컨트롤러 지원</string>
|
||||||
<string name="summary_checkbox_multi_controller">이 옵션을 선택하지 않으면 모든 컨트롤러가 하나로 표시됩니다</string>
|
<string name="summary_checkbox_multi_controller">이 옵션을 선택하지 않으면 모든 컨트롤러가 하나로 표시됩니다</string>
|
||||||
@@ -136,5 +134,6 @@
|
|||||||
<string name="category_advanced_settings">고급 설정</string>
|
<string name="category_advanced_settings">고급 설정</string>
|
||||||
<string name="title_video_format">H.265 설정 변경</string>
|
<string name="title_video_format">H.265 설정 변경</string>
|
||||||
<string name="summary_video_format">H.265는 비디오 대역폭 요구사항을 낮춰주지만 최신 장치가 필요합니다.</string>
|
<string name="summary_video_format">H.265는 비디오 대역폭 요구사항을 낮춰주지만 최신 장치가 필요합니다.</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -94,8 +94,6 @@
|
|||||||
<string name="summary_checkbox_disable_warnings">Verberg on-screen verbindingswaarschuwingen tijdens het streamen</string>
|
<string name="summary_checkbox_disable_warnings">Verberg on-screen verbindingswaarschuwingen tijdens het streamen</string>
|
||||||
|
|
||||||
<string name="category_audio_settings">Geluidsinstellingen</string>
|
<string name="category_audio_settings">Geluidsinstellingen</string>
|
||||||
<string name="title_checkbox_51_surround">Gebruik 5.1 surround sound</string>
|
|
||||||
<string name="summary_checkbox_51_surround">Gebruik dit niet als er problemen zijn met de audio. Vereist GFE 2.7 of hoger.</string>
|
|
||||||
|
|
||||||
<string name="title_checkbox_multi_controller">Multi-gamepad support</string>
|
<string name="title_checkbox_multi_controller">Multi-gamepad support</string>
|
||||||
<string name="summary_checkbox_multi_controller">Wanneer uitgevinkt, alle controllers verschijnen als één.</string>
|
<string name="summary_checkbox_multi_controller">Wanneer uitgevinkt, alle controllers verschijnen als één.</string>
|
||||||
@@ -125,5 +123,6 @@
|
|||||||
<string name="category_advanced_settings">Geavanceerde Instellingen</string>
|
<string name="category_advanced_settings">Geavanceerde Instellingen</string>
|
||||||
<string name="title_video_format">Verander H.265 instellingen</string>
|
<string name="title_video_format">Verander H.265 instellingen</string>
|
||||||
<string name="summary_video_format">H.265 verlaagt video bandbreedte vereisten maar benodigdt een recent apparaat.</string>
|
<string name="summary_video_format">H.265 verlaagt video bandbreedte vereisten maar benodigdt een recent apparaat.</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string-array name="audio_config_names">
|
||||||
|
<item>Stereo</item>
|
||||||
|
<item>Sunet Surround 5.1</item>
|
||||||
|
<item>Sunet Surround 7.1</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="decoder_names">
|
||||||
|
<item>Auto-selectează decodorul</item>
|
||||||
|
<item>Forțează decodarea Software</item>
|
||||||
|
<item>Forțează decodarea Hardware</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="video_format_names">
|
||||||
|
<item>Folosește H.265 doar dacă e stabil</item>
|
||||||
|
<item>Folosește H.265 mereu (se poate bloca)</item>
|
||||||
|
<item>Nu folosi H.265</item>
|
||||||
|
</string-array>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,193 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Shortcut strings -->
|
||||||
|
<string name="scut_deleted_pc">PC șters</string>
|
||||||
|
<string name="scut_not_paired">PC neîmperecheat</string>
|
||||||
|
<string name="scut_pc_not_found">PC negăsit</string>
|
||||||
|
<string name="scut_invalid_uuid">PC-ul este invalid</string>
|
||||||
|
<string name="scut_invalid_app_id">Aplicația este invalidă</string>
|
||||||
|
|
||||||
|
<!-- Help strings -->
|
||||||
|
<string name="help_loading_title">Ajutor</string>
|
||||||
|
<string name="help_loading_msg">Se încarcă pagina de ajutor…</string>
|
||||||
|
|
||||||
|
<!-- PC view menu entries -->
|
||||||
|
<string name="pcview_menu_app_list">Vezi lista de jocuri</string>
|
||||||
|
<string name="pcview_menu_pair_pc">Împerechează PC-ul</string>
|
||||||
|
<string name="pcview_menu_unpair_pc">Desperechează</string>
|
||||||
|
<string name="pcview_menu_send_wol">Trimite o cerere Wake-On-LAN</string>
|
||||||
|
<string name="pcview_menu_delete_pc">Șterge PC</string>
|
||||||
|
<string name="pcview_menu_details">Vezi detalii</string>
|
||||||
|
|
||||||
|
<!-- Pair messages -->
|
||||||
|
<string name="pairing">Se împerechează…</string>
|
||||||
|
<string name="pair_pc_offline">PC-ul nu este accesibil</string>
|
||||||
|
<string name="pair_pc_ingame">PC-ul rulează un joc acum. Trebuie să închizi jocul pentru a-l putea împerechea.</string>
|
||||||
|
<string name="pair_pairing_title">Se împerechează</string>
|
||||||
|
<string name="pair_pairing_msg">Te rugăm să introduci urmatorul PIN în PC-ul pe care îl împerechezi:</string>
|
||||||
|
<string name="pair_incorrect_pin">PIN-ul este greșit</string>
|
||||||
|
<string name="pair_fail">Împerecherea a eșuat</string>
|
||||||
|
<string name="pair_already_in_progress">Împerecherea este deja în curs</string>
|
||||||
|
|
||||||
|
<!-- WOL messages -->
|
||||||
|
<string name="wol_pc_online">PC-ul este accesibil</string>
|
||||||
|
<string name="wol_no_mac">Nu s-a putut porni PC-ul deoarece GFE nu a comunicat o adresa MAC</string>
|
||||||
|
<string name="wol_waking_pc">Se pornește PC-ul…</string>
|
||||||
|
<string name="wol_waking_msg">Poate dura puțin până PC-ul pornește. Dacă nu pornește, verifică dacă este configurat corect pentru Wake-On-LAN.</string>
|
||||||
|
<string name="wol_fail">Nu s-au putut trimite pachetele Wake-On-LAN</string>
|
||||||
|
|
||||||
|
<!-- Unpair messages -->
|
||||||
|
<string name="unpairing">Desperecherechere…</string>
|
||||||
|
<string name="unpair_success">Desperecherechere efectuată cu succes</string>
|
||||||
|
<string name="unpair_fail">Desperecherea a eșuat</string>
|
||||||
|
<string name="unpair_error">Dispozitivul nu este împerecheat</string>
|
||||||
|
|
||||||
|
<!-- Errors -->
|
||||||
|
<string name="error_pc_offline">PC-ul este inaccesibil</string>
|
||||||
|
<string name="error_manager_not_running">Serviciul ComputerManager nu este pornit. Te rugăm să aștepți câteva secunde sau să repornești aplicația.</string>
|
||||||
|
<string name="error_unknown_host">Nu am putut identifica hostul</string>
|
||||||
|
<string name="error_404">GFE a returnat un cod de eroare HTTP 404. Asigură-te ca PC-ul are o placa video suportată.
|
||||||
|
Este posibil sa apară această eroare daca folosești o alta aplicație de remote desktop. Încearcă să repornești PC-ul sau să reinstalezi GFE.
|
||||||
|
</string>
|
||||||
|
<string name="title_decoding_error">Decodorul video s-a închis în mod neașteptat</string>
|
||||||
|
<string name="message_decoding_error">Moonlight s-a închis în mod neașteptat datorită unei incompatibilități cu decodorul video al acestui dispozitiv. Asigură-te ca folosești ultima versiune de GFE. Dacă problema persistă, ajustează setările de streaming.</string>
|
||||||
|
<string name="title_decoding_reset">Resetează setările video</string>
|
||||||
|
<string name="message_decoding_reset">Decodorul video al acestui dispozitiv continuă să se blocheze folosind setările video curente. Au fost resetate cele implicite.</string>
|
||||||
|
<string name="error_usb_prohibited">Accesul USB este interzis de către administratorul dispozitivului. Verifică setarile Knox sau MDM.</string>
|
||||||
|
<string name="unable_to_pin_shortcut">Launcher-ul tău curent nu permite crearea de scurtături fixate.</string>
|
||||||
|
<string name="video_decoder_init_failed">Inițializarea decodorului video a eșuat. Este posibil ca acest dispozitiv să nu suporte rezoluția sau rata cadrelor selectată.</string>
|
||||||
|
|
||||||
|
<!-- Start application messages -->
|
||||||
|
<string name="conn_establishing_title">Se stabilește conexiunea</string>
|
||||||
|
<string name="conn_establishing_msg">Se pornește conexiunea</string>
|
||||||
|
<string name="conn_metered">Atenție: Conexiunea ta curentă este contorizată!</string>
|
||||||
|
<string name="conn_client_latency">Latența medie a decodării cadrelor:</string>
|
||||||
|
<string name="conn_client_latency_hw">latența decodorului hardware:</string>
|
||||||
|
<string name="conn_hardware_latency">Latența medie a decodării cadrelor (hardware):</string>
|
||||||
|
<string name="conn_starting">Se pornește</string>
|
||||||
|
<string name="conn_error_title">Eroare la conectare</string>
|
||||||
|
<string name="conn_error_msg">Pornirea a eșuat</string>
|
||||||
|
<string name="conn_terminated_title">Conexiunea închisă</string>
|
||||||
|
<string name="conn_terminated_msg">Conexiunea a fost terminată</string>
|
||||||
|
|
||||||
|
<!-- General strings -->
|
||||||
|
<string name="ip_hint">Adresa IP a PC-ului cu GFE</string>
|
||||||
|
<string name="searching_pc">Se caută PC-uri cu GameStream activat…\n\n
|
||||||
|
Asigură-te ca GameStream este activat în setările Geforce Experience SHIELD.</string>
|
||||||
|
<string name="yes">Da</string>
|
||||||
|
<string name="no">Nu</string>
|
||||||
|
<string name="lost_connection">S-a pierdut conexiunea catre PC</string>
|
||||||
|
<string name="title_details">Detalii</string>
|
||||||
|
<string name="help">Ajutor</string>
|
||||||
|
<string name="delete_pc_msg">Sigur dorești să ștergi acest PC?</string>
|
||||||
|
<string name="slow_connection_msg">Conexiune inceată catre PC\nRedu rata de biți</string>
|
||||||
|
<string name="poor_connection_msg">Conexiune slabă catre PC</string>
|
||||||
|
<string name="perf_overlay_text">Dimensiunile video: %1$s\nDecodor: %2$s\nRata cadrelor estimata PC: %3$.2f FPS\nRata cadrelor primite din rețea: %4$.2f FPS\nRata de afisare a cadrelor: %5$.2f FPS\nCadre pierdute de rețea: %6$.2f%%\nTimpul mediu de primire: %7$.2f ms\nTimpul mediu de decodare: %8$.2f ms</string>
|
||||||
|
|
||||||
|
<!-- AppList activity -->
|
||||||
|
<string name="applist_connect_msg">Se conectează la PC…</string>
|
||||||
|
<string name="applist_menu_resume">Continuă Sesiunea</string>
|
||||||
|
<string name="applist_menu_quit">Închide Sesiunea</string>
|
||||||
|
<string name="applist_menu_quit_and_start">Închide Jocul Curent si Pornește</string>
|
||||||
|
<string name="applist_menu_cancel">Anulează</string>
|
||||||
|
<string name="applist_menu_details">Vezi detalii</string>
|
||||||
|
<string name="applist_menu_scut">Creează o scurtătură</string>
|
||||||
|
<string name="applist_menu_tv_channel">Adaugă la canal</string>
|
||||||
|
<string name="applist_refresh_title">Lista de aplicații</string>
|
||||||
|
<string name="applist_refresh_msg">Reîmprospătare aplicații…</string>
|
||||||
|
<string name="applist_refresh_error_title">Eroare</string>
|
||||||
|
<string name="applist_refresh_error_msg">Nu s-a putut obține lista de aplicații</string>
|
||||||
|
<string name="applist_quit_app">Închidere în curs</string>
|
||||||
|
<string name="applist_quit_success">Închis cu succes</string>
|
||||||
|
<string name="applist_quit_fail">Nu s-a putut închide lista</string>
|
||||||
|
<string name="applist_quit_confirmation">Sigur dorești să închizi aplicația curentă? Toate datele nesalvate vor fi pierdute.</string>
|
||||||
|
<string name="applist_details_id">ID-ul aplicației:</string>
|
||||||
|
|
||||||
|
<!-- Add computer manually activity -->
|
||||||
|
<string name="title_add_pc">Adaugă PC manual</string>
|
||||||
|
<string name="msg_add_pc">Conectare în curs…</string>
|
||||||
|
<string name="addpc_fail">Nu s-a putut efectua conectarea la adresa introdusă. Asigurăte ca porturile nu sunt blocate in firewall.</string>
|
||||||
|
<string name="addpc_success">PC adăugat cu succes</string>
|
||||||
|
<string name="addpc_unknown_host">Nu am putut identifica adresa PC-ului. Asigură-te că ai introdus-o corect.</string>
|
||||||
|
<string name="addpc_enter_ip">Trebuie să introduci o adresa IP</string>
|
||||||
|
<string name="addpc_wrong_sitelocal">Adresa introdusă nu pare corectă. Pentru conectare prin Internet, este nevoie de adresa publică a routerului.</string>
|
||||||
|
|
||||||
|
<!-- Preferences -->
|
||||||
|
<string name="category_basic_settings">Setări de bază</string>
|
||||||
|
<string name="title_resolution_list">Rezolutia video</string>
|
||||||
|
<string name="summary_resolution_list">Crește-o pentru a îmbunătăți claritatea imaginii. Descrește-o pentru dispozitive neperformante sau conexiune slabă.</string>
|
||||||
|
<string name="title_fps_list">Rata cadrelor</string>
|
||||||
|
<string name="summary_fps_list">Crește-o pentru a îmbunătăți fluiditatea imaginilor. Descrește-o pentru dispozitive neperformante sau conexiune slabă.</string>
|
||||||
|
<string name="title_seekbar_bitrate">Rata de biți</string>
|
||||||
|
<string name="summary_seekbar_bitrate">Crește-o pentru a îmbunătăți calitatea imaginilor. Descrește-o pentru dispozitive neperformante sau conexiune slabă.</string>
|
||||||
|
<string name="suffix_seekbar_bitrate">Kbps</string>
|
||||||
|
<string name="title_unlock_fps">Deblochează toate ratele de cadre posibile</string>
|
||||||
|
<string name="summary_unlock_fps">Fluxul video de rate mai mari poate reduce latența folosind dispozitive performante, dar poate introduce erori daca nu sunt suportate.</string>
|
||||||
|
<string name="title_checkbox_stretch_video">Întindeți video pe ecranul complet</string>
|
||||||
|
<string name="title_checkbox_disable_warnings">Dezactivează mesajele de avertizare</string>
|
||||||
|
<string name="summary_checkbox_disable_warnings">Dezactivează mesajele de avertizare privind rețeaua în timpul conexiunii</string>
|
||||||
|
<string name="title_checkbox_enable_pip">Activează modul Picture-In-Picture</string>
|
||||||
|
<string name="summary_checkbox_enable_pip">Permite vizualizarea (dar nu și controlul) când efectuezi multitasking</string>
|
||||||
|
|
||||||
|
<string name="category_audio_settings">Setări Audio</string>
|
||||||
|
<string name="title_audio_config_list">Configurarea sunetului surround</string>
|
||||||
|
<string name="summary_audio_config_list">Activeaza sunetul 5.1 sau 7.1 pentru sisteme home-theater</string>
|
||||||
|
|
||||||
|
<string name="category_input_settings">Setări de control</string>
|
||||||
|
<string name="title_checkbox_multi_controller">Detectează automat prezența controllerelor.</string>
|
||||||
|
<string name="summary_checkbox_multi_controller">Dezactivarea acestei opțiuni implică prezența constantă a unui controller</string>
|
||||||
|
<string name="title_checkbox_vibrate_fallback">Simuleaza efectul de vibratie</string>
|
||||||
|
<string name="summary_checkbox_vibrate_fallback">Dacă controllerul nu suportă vibrații, va vibra dispozitivul în schimb.</string>
|
||||||
|
<string name="title_seekbar_deadzone">Zona moartă a stickului analogic</string>
|
||||||
|
<string name="suffix_seekbar_deadzone">%</string>
|
||||||
|
<string name="title_checkbox_xb1_driver">Driver pentru controllerele de Xbox 360/One</string>
|
||||||
|
<string name="summary_checkbox_xb1_driver">Activează un driver USB pentru dispozitivele fără suport nativ pentru controllere Xbox</string>
|
||||||
|
<string name="title_checkbox_usb_bind_all">Inlocuiește driverul implicit pentru controllere</string>
|
||||||
|
<string name="summary_checkbox_usb_bind_all">Forțează driverul USB Moonlight să preia toate controllerele Xbox suportate</string>
|
||||||
|
<string name="title_checkbox_mouse_emulation">Simulează mouse cu controllerul</string>
|
||||||
|
<string name="summary_checkbox_mouse_emulation">Apăsarea lungă pe butonul Start schimba modul de operare a controllerului în modul mouse.</string>
|
||||||
|
<string name="title_checkbox_mouse_nav_buttons">Activează butoanele de înainte și înapoi ale mousului</string>
|
||||||
|
<string name="summary_checkbox_mouse_nav_buttons">Această opțiune poate afecta click dreapta pentru unele dispozitive problematice.</string>
|
||||||
|
|
||||||
|
<string name="category_on_screen_controls_settings">Setări ale controalelor pe ecran</string>
|
||||||
|
<string name="title_checkbox_show_onscreen_controls">Afișează controale pe ecran</string>
|
||||||
|
<string name="summary_checkbox_show_onscreen_controls">Afișează un controller virtual pe ecran</string>
|
||||||
|
<string name="title_checkbox_vibrate_osc">Activează vibrațiile</string>
|
||||||
|
<string name="summary_checkbox_vibrate_osc">Dispozitivul va vibra asemănător unui controller</string>
|
||||||
|
<string name="title_only_l3r3">Afișează doar L3 si R3</string>
|
||||||
|
<string name="summary_only_l3r3">Ascunde toate butoanele în afară de L3 and R3</string>
|
||||||
|
<string name="title_reset_osc">Șterge schema salvată a controalelor</string>
|
||||||
|
<string name="summary_reset_osc">Resetează toate controalele de pe ecran la poziția și dimensiunea implicită</string>
|
||||||
|
<string name="dialog_title_reset_osc">Resetarea schemei de controale</string>
|
||||||
|
<string name="dialog_text_reset_osc">Sigur dorești să ștergi schema salvată a controalelor de pe ecran?</string>
|
||||||
|
<string name="toast_reset_osc_success">Controalele de pe ecran au fost resetate la setarile implicite</string>
|
||||||
|
<string name="title_osc_opacity">Modifică opacitatea controalelor de pe ecran</string>
|
||||||
|
<string name="summary_osc_opacity">Ajustează gradul de transparență al controalelor de pe ecran</string>
|
||||||
|
<string name="dialog_title_osc_opacity">Modifică opacitatea</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
|
||||||
|
<string name="category_ui_settings">Setari UI</string>
|
||||||
|
<string name="title_language_list">Limba (Language)</string>
|
||||||
|
<string name="summary_language_list">Limba folosită de către Moonlight</string>
|
||||||
|
<string name="title_checkbox_list_mode">Folosește liste în loc de grile</string>
|
||||||
|
<string name="summary_checkbox_list_mode">Aplicațiile și PC-urile vor fi afișate in liste in loc de grile</string>
|
||||||
|
<string name="title_checkbox_small_icon_mode">Folosește iconițe mici</string>
|
||||||
|
<string name="summary_checkbox_small_icon_mode">Iconițele folosite în grile vor fi mici pentru a încăpea mai multe odata</string>
|
||||||
|
|
||||||
|
<string name="category_host_settings">Setările PC-ului gazdă</string>
|
||||||
|
<string name="title_checkbox_enable_sops">Optimizarea setărilor de joc</string>
|
||||||
|
<string name="summary_checkbox_enable_sops">Permite GFE să modifice setările jocurilor pentru experiența optimă</string>
|
||||||
|
<string name="title_checkbox_host_audio">Redă audio si pe PC</string>
|
||||||
|
<string name="summary_checkbox_host_audio">Sunetul se va auzi atat pe acest dispozitiv cât și pe PC</string>
|
||||||
|
|
||||||
|
<string name="category_advanced_settings">Setări avansate</string>
|
||||||
|
<string name="title_disable_frame_drop">Nu pierde cadre intenționat</string>
|
||||||
|
<string name="summary_disable_frame_drop">Poate să reducă micro-stuttering pe anumite device-uri, dar s-ar putea să crească latența</string>
|
||||||
|
<string name="title_video_format">Modifica setările H.265</string>
|
||||||
|
<string name="summary_video_format">H.265 funcționează cu o conexiune mai slaba, dar necesită un dispozitiv recent, performant</string>
|
||||||
|
<string name="title_enable_hdr">Activează HDR (Experimental)</string>
|
||||||
|
<string name="summary_enable_hdr">Folosește HDR daca aplicația si placa video suportă. Necesită o placa video seria GTX 1000 sau mai nouă.</string>
|
||||||
|
<string name="title_enable_perf_overlay">Activează statisticile de performanță</string>
|
||||||
|
<string name="summary_enable_perf_overlay">Afișează în timp real statisticile de performanță ale conexiunii.</string>
|
||||||
|
|
||||||
|
</resources>
|
||||||
@@ -96,8 +96,6 @@
|
|||||||
<string name="summary_checkbox_disable_warnings">Выключить экранные предупреждения о соединении во время трансляции</string>
|
<string name="summary_checkbox_disable_warnings">Выключить экранные предупреждения о соединении во время трансляции</string>
|
||||||
|
|
||||||
<string name="category_audio_settings">Аудио Настройки</string>
|
<string name="category_audio_settings">Аудио Настройки</string>
|
||||||
<string name="title_checkbox_51_surround">Включить объёмный звук 5.1</string>
|
|
||||||
<string name="summary_checkbox_51_surround">Отключите, если появляются аудио проблемы. Требуется GFE 2.7 или выше.</string>
|
|
||||||
|
|
||||||
<string name="title_checkbox_multi_controller">Поддержка нескольких контроллеров</string>
|
<string name="title_checkbox_multi_controller">Поддержка нескольких контроллеров</string>
|
||||||
<string name="summary_checkbox_multi_controller">Когда отключена, все контроллеры определяются как один</string>
|
<string name="summary_checkbox_multi_controller">Когда отключена, все контроллеры определяются как один</string>
|
||||||
@@ -183,4 +181,5 @@
|
|||||||
<string name="summary_fps_list">Увеличение для более плавного видео потока. Уменьшите для лучшей производительности на более слабых устройствах.</string>
|
<string name="summary_fps_list">Увеличение для более плавного видео потока. Уменьшите для лучшей производительности на более слабых устройствах.</string>
|
||||||
<string name="scut_invalid_uuid">Указанный PC недействителен</string>
|
<string name="scut_invalid_uuid">Указанный PC недействителен</string>
|
||||||
<string name="scut_invalid_app_id">Указанное приложение недействительно</string>
|
<string name="scut_invalid_app_id">Указанное приложение недействительно</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<string-array name="decoder_names">
|
<string-array name="decoder_names">
|
||||||
<item>自动选择解码器</item>
|
<item>自动选择解码器</item>
|
||||||
<item>强制软解</item>
|
<item>强制软解</item>
|
||||||
<item>强制硬解</item>
|
<item>强制硬解</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="video_format_names">
|
<string-array name="video_format_names">
|
||||||
<item>如果稳定才使用H.265</item>
|
<item>如果稳定才使用H.265</item>
|
||||||
<item>强制使用H.265(不稳定)</item>
|
<item>强制使用H.265(不稳定)</item>
|
||||||
<item>不使用H.265</item>
|
<item>不使用H.265</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="audio_config_names">
|
||||||
|
<item>立体声</item>
|
||||||
|
<item>5.1环绕声</item>
|
||||||
|
<item>7.1环绕声</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -134,8 +134,8 @@
|
|||||||
<string name="summary_checkbox_enable_pip">允许多任务时观看串流画面(但不操作)</string>
|
<string name="summary_checkbox_enable_pip">允许多任务时观看串流画面(但不操作)</string>
|
||||||
|
|
||||||
<string name="category_audio_settings"> 音频设置 </string>
|
<string name="category_audio_settings"> 音频设置 </string>
|
||||||
<string name="title_checkbox_51_surround"> 启用 5.1 环绕音效 </string>
|
<string name="title_audio_config_list"> 环绕声设置 </string>
|
||||||
<string name="summary_checkbox_51_surround"> 如果你的声音听起来有问题请禁用。 \n 需要GFE 2.7或更高版本 </string>
|
<string name="summary_audio_config_list"> 为家庭影院系统启用5.1或7.1环绕声 </string>
|
||||||
|
|
||||||
<string name="category_input_settings">输入设置</string>
|
<string name="category_input_settings">输入设置</string>
|
||||||
<string name="title_checkbox_multi_controller"> 自动检测手柄 </string>
|
<string name="title_checkbox_multi_controller"> 自动检测手柄 </string>
|
||||||
@@ -205,6 +205,10 @@
|
|||||||
|
|
||||||
<string name="title_enable_perf_overlay"> 启用性能信息 </string>
|
<string name="title_enable_perf_overlay"> 启用性能信息 </string>
|
||||||
<string name="summary_enable_perf_overlay"> 在串流中显示实时性能信息 </string>
|
<string name="summary_enable_perf_overlay"> 在串流中显示实时性能信息 </string>
|
||||||
|
<string name="title_osc_opacity">更改屏幕按钮透明度</string>
|
||||||
|
<string name="dialog_title_osc_opacity">透明度</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
<string name="summary_osc_opacity">令屏幕按钮变得更透明/更不透明</string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<string-array name="decoder_names">
|
<string-array name="decoder_names">
|
||||||
<item>自動選擇解碼器</item>
|
<item>自動選擇解碼器</item>
|
||||||
<item>強制軟解</item>
|
<item>強制軟解</item>
|
||||||
<item>強制硬解</item>
|
<item>強制硬解</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="video_format_names">
|
<string-array name="video_format_names">
|
||||||
<item>如果穩定才使用H.265</item>
|
<item>如果穩定才使用H.265</item>
|
||||||
<item>強制使用H.265(不穩定)</item>
|
<item>強制使用H.265(不穩定)</item>
|
||||||
<item>不使用H.265</item>
|
<item>不使用H.265</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="audio_config_names">
|
||||||
|
<item>身歷聲</item>
|
||||||
|
<item>5.1環繞聲</item>
|
||||||
|
<item>7.1環繞聲</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -134,8 +134,8 @@
|
|||||||
<string name="summary_checkbox_enable_pip">允許多工時觀看串流畫面(但不操作)</string>
|
<string name="summary_checkbox_enable_pip">允許多工時觀看串流畫面(但不操作)</string>
|
||||||
|
|
||||||
<string name="category_audio_settings"> 音訊設置 </string>
|
<string name="category_audio_settings"> 音訊設置 </string>
|
||||||
<string name="title_checkbox_51_surround"> 啟用 5.1 環繞音效 </string>
|
<string name="title_audio_config_list"> 環繞聲設置 </string>
|
||||||
<string name="summary_checkbox_51_surround"> 如果你的聲音聽起來有問題請禁用。 \n 需要GFE 2.7或更高版本 </string>
|
<string name="summary_audio_config_list"> 為家庭劇院系統啟用5.1或7.1環繞聲 </string>
|
||||||
|
|
||||||
<string name="category_input_settings">輸入設置</string>
|
<string name="category_input_settings">輸入設置</string>
|
||||||
<string name="title_checkbox_multi_controller"> 自動檢測手柄 </string>
|
<string name="title_checkbox_multi_controller"> 自動檢測手柄 </string>
|
||||||
@@ -205,6 +205,10 @@
|
|||||||
|
|
||||||
<string name="title_enable_perf_overlay"> 啟用性能資訊 </string>
|
<string name="title_enable_perf_overlay"> 啟用性能資訊 </string>
|
||||||
<string name="summary_enable_perf_overlay"> 在串流中顯示即時性能資訊 </string>
|
<string name="summary_enable_perf_overlay"> 在串流中顯示即時性能資訊 </string>
|
||||||
|
<string name="title_osc_opacity">更改屏幕按鈕透明度</string>
|
||||||
|
<string name="dialog_title_osc_opacity">透明度</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
<string name="summary_osc_opacity">令屏幕按钮變得更透明/更不透明</string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -30,6 +30,17 @@
|
|||||||
<item>120</item>
|
<item>120</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="audio_config_names">
|
||||||
|
<item>Stereo</item>
|
||||||
|
<item>5.1 Surround Sound</item>
|
||||||
|
<item>7.1 Surround Sound</item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="audio_config_values" translatable="false">
|
||||||
|
<item>2</item>
|
||||||
|
<item>51</item>
|
||||||
|
<item>71</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
<string-array name="language_names" translatable="false">
|
<string-array name="language_names" translatable="false">
|
||||||
<item>Default</item>
|
<item>Default</item>
|
||||||
<item>English</item>
|
<item>English</item>
|
||||||
@@ -43,6 +54,7 @@
|
|||||||
<item>Español</item>
|
<item>Español</item>
|
||||||
<item>Français</item>
|
<item>Français</item>
|
||||||
<item>Deutsch</item>
|
<item>Deutsch</item>
|
||||||
|
<item>Română</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="language_values" translatable="false">
|
<string-array name="language_values" translatable="false">
|
||||||
<item>default</item>
|
<item>default</item>
|
||||||
@@ -57,6 +69,7 @@
|
|||||||
<item>es</item>
|
<item>es</item>
|
||||||
<item>fr</item>
|
<item>fr</item>
|
||||||
<item>de</item>
|
<item>de</item>
|
||||||
|
<item>ro</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="decoder_names">
|
<string-array name="decoder_names">
|
||||||
|
|||||||
@@ -135,8 +135,8 @@
|
|||||||
<string name="summary_checkbox_enable_pip">Allows the stream to be viewed (but not controlled) while multitasking</string>
|
<string name="summary_checkbox_enable_pip">Allows the stream to be viewed (but not controlled) while multitasking</string>
|
||||||
|
|
||||||
<string name="category_audio_settings">Audio Settings</string>
|
<string name="category_audio_settings">Audio Settings</string>
|
||||||
<string name="title_checkbox_51_surround">Enable 5.1 surround sound</string>
|
<string name="title_audio_config_list">Surround sound configuration</string>
|
||||||
<string name="summary_checkbox_51_surround">Uncheck if you experience audio issues. Requires GFE 2.7 or higher.</string>
|
<string name="summary_audio_config_list">Enable 5.1 or 7.1 surround sound for home-theater systems</string>
|
||||||
|
|
||||||
<string name="category_input_settings">Input Settings</string>
|
<string name="category_input_settings">Input Settings</string>
|
||||||
<string name="title_checkbox_multi_controller">Automatic gamepad presence detection</string>
|
<string name="title_checkbox_multi_controller">Automatic gamepad presence detection</string>
|
||||||
@@ -166,6 +166,10 @@
|
|||||||
<string name="dialog_title_reset_osc">Reset Layout</string>
|
<string name="dialog_title_reset_osc">Reset Layout</string>
|
||||||
<string name="dialog_text_reset_osc">Are you sure you want to delete your saved on-screen controls layout?</string>
|
<string name="dialog_text_reset_osc">Are you sure you want to delete your saved on-screen controls layout?</string>
|
||||||
<string name="toast_reset_osc_success">On-screen controls reset to default</string>
|
<string name="toast_reset_osc_success">On-screen controls reset to default</string>
|
||||||
|
<string name="title_osc_opacity">Change opacity of on-screen controls</string>
|
||||||
|
<string name="summary_osc_opacity">Make the on-screen controls more/less transparent</string>
|
||||||
|
<string name="dialog_title_osc_opacity">Change opacity</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
|
||||||
<string name="category_ui_settings">UI Settings</string>
|
<string name="category_ui_settings">UI Settings</string>
|
||||||
<string name="title_language_list">Language</string>
|
<string name="title_language_list">Language</string>
|
||||||
|
|||||||
@@ -43,11 +43,13 @@
|
|||||||
android:defaultValue="false" />
|
android:defaultValue="false" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory android:title="@string/category_audio_settings">
|
<PreferenceCategory android:title="@string/category_audio_settings">
|
||||||
<CheckBoxPreference
|
<ListPreference
|
||||||
android:key="checkbox_51_surround"
|
android:key="list_audio_config"
|
||||||
android:title="@string/title_checkbox_51_surround"
|
android:title="@string/title_audio_config_list"
|
||||||
android:summary="@string/summary_checkbox_51_surround"
|
android:summary="@string/summary_audio_config_list"
|
||||||
android:defaultValue="false" />
|
android:entries="@array/audio_config_names"
|
||||||
|
android:entryValues="@array/audio_config_values"
|
||||||
|
android:defaultValue="2" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory android:title="@string/category_input_settings">
|
<PreferenceCategory android:title="@string/category_input_settings">
|
||||||
<!--com.limelight.preferences.SeekBarPreference
|
<!--com.limelight.preferences.SeekBarPreference
|
||||||
@@ -107,6 +109,17 @@
|
|||||||
android:key="checkbox_only_show_L3R3"
|
android:key="checkbox_only_show_L3R3"
|
||||||
android:summary="@string/summary_only_l3r3"
|
android:summary="@string/summary_only_l3r3"
|
||||||
android:title="@string/title_only_l3r3" />
|
android:title="@string/title_only_l3r3" />
|
||||||
|
<com.limelight.preferences.SeekBarPreference
|
||||||
|
android:key="seekbar_osc_opacity"
|
||||||
|
android:dependency="checkbox_show_onscreen_controls"
|
||||||
|
android:dialogMessage="@string/summary_osc_opacity"
|
||||||
|
seekbar:min="0"
|
||||||
|
seekbar:step="1"
|
||||||
|
android:max="100"
|
||||||
|
android:defaultValue="90"
|
||||||
|
android:summary="@string/summary_osc_opacity"
|
||||||
|
android:text="@string/suffix_osc_opacity"
|
||||||
|
android:title="@string/dialog_title_osc_opacity" />
|
||||||
<com.limelight.preferences.ConfirmDeleteOscPreference
|
<com.limelight.preferences.ConfirmDeleteOscPreference
|
||||||
android:title="@string/title_reset_osc"
|
android:title="@string/title_reset_osc"
|
||||||
android:summary="@string/summary_reset_osc"
|
android:summary="@string/summary_reset_osc"
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ buildscript {
|
|||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.6.1'
|
classpath 'com.android.tools.build:gradle:3.6.3'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
- Configurable transparency for on-screen controls
|
||||||
|
- Fixed a crash when pinning an app shortcut to the home screen
|
||||||
|
- Fixed right click unexpectedly stopping the stream on some devices
|
||||||
|
- Improved key repeating behavior while streaming
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
- Improved performance during periods of packet loss
|
||||||
|
- Improved prioritization of control data sent back to the PC
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
- Added 7.1 surround sound support
|
||||||
|
- Fixed a crash on some devices during a high intensity rumble event
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
- Improved support for GFE 3.20.3.63
|
||||||
|
- Audio performance optimization
|
||||||
|
- Added Romanian translation
|
||||||
|
- Updated Simplified Chinese and Traditional Chinese translations
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- Improved stylus support including direct mouse control
|
||||||
|
- Improved mouse support for ChromeOS and Samsung DeX
|
||||||
|
- Improved mouse support for devices running Android 7.0 and earlier
|
||||||
|
- Improved mapping for Start and Select on the ROG Kunai
|
||||||
|
- Fixed a crash when GeForce Experience returns an invalid status code value
|
||||||
@@ -6,8 +6,9 @@ Streaming performance may vary based on your client device and network setup. HD
|
|||||||
* Open-source and completely free (no ads, IAPs, or "Pro")
|
* Open-source and completely free (no ads, IAPs, or "Pro")
|
||||||
* Streams games purchased from any store
|
* Streams games purchased from any store
|
||||||
* Works on your home network or over the Internet/LTE
|
* Works on your home network or over the Internet/LTE
|
||||||
* Up to 4K 120 FPS HDR streaming with 5.1 surround sound
|
* Up to 4K 120 FPS HDR streaming with 7.1 surround sound
|
||||||
* Keyboard and mouse support (with Android 8.0 or rooted device)
|
* Keyboard and mouse support (best with Android 8.0 or later)
|
||||||
|
* Stylus/S-Pen support
|
||||||
* Supports PS3, PS4, Xbox 360, Xbox One, and Android gamepads
|
* Supports PS3, PS4, Xbox 360, Xbox One, and Android gamepads
|
||||||
* Force feedback support
|
* Force feedback support
|
||||||
* Local co-op with up to 4 connected controllers
|
* Local co-op with up to 4 connected controllers
|
||||||
|
|||||||
Reference in New Issue
Block a user