mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2026-10-20 15:27:18 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
232b897abc | ||
|
|
efd076bc6c | ||
|
|
cc877480ff | ||
|
|
363145a284 | ||
|
|
755571ad33 | ||
|
|
39edb55721 | ||
|
|
15aa7ecc2e | ||
|
|
ce9e91153e | ||
|
|
9ee0a46606 | ||
|
|
20dc351f4c | ||
|
|
c30c54d562 | ||
|
|
45ff51c0d2 | ||
|
|
5b86e99138 | ||
|
|
0c72910eb7 | ||
|
|
3b0f485b41 | ||
|
|
2be2c95212 | ||
|
|
e7aeeb8bd5 | ||
|
|
73df93f86a | ||
|
|
9cd4d5e2aa | ||
|
|
c3b81554f4 | ||
|
|
6f79c52fc5 | ||
|
|
29bc3e022b | ||
|
|
7d03203d83 | ||
|
|
11dde835d1 | ||
|
|
52c47c288c | ||
|
|
63072aa8e1 | ||
|
|
4cca3ac922 | ||
|
|
604bc1ec11 | ||
|
|
5d7fbf3195 | ||
|
|
8c56e6f0d4 | ||
|
|
2069be7932 | ||
|
|
9c1c2991a9 | ||
|
|
81dabf2713 | ||
|
|
27520cb77e |
No files matched your search
+2
-2
@@ -7,8 +7,8 @@ android {
|
|||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
|
|
||||||
versionName "9.0"
|
versionName "9.5"
|
||||||
versionCode = 219
|
versionCode = 224
|
||||||
}
|
}
|
||||||
|
|
||||||
flavorDimensions "root"
|
flavorDimensions "root"
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ import com.limelight.binding.input.ControllerHandler;
|
|||||||
import com.limelight.binding.input.KeyboardTranslator;
|
import com.limelight.binding.input.KeyboardTranslator;
|
||||||
import com.limelight.binding.input.capture.InputCaptureManager;
|
import com.limelight.binding.input.capture.InputCaptureManager;
|
||||||
import com.limelight.binding.input.capture.InputCaptureProvider;
|
import com.limelight.binding.input.capture.InputCaptureProvider;
|
||||||
import com.limelight.binding.input.TouchContext;
|
import com.limelight.binding.input.touch.AbsoluteTouchContext;
|
||||||
|
import com.limelight.binding.input.touch.RelativeTouchContext;
|
||||||
import com.limelight.binding.input.driver.UsbDriverService;
|
import com.limelight.binding.input.driver.UsbDriverService;
|
||||||
import com.limelight.binding.input.evdev.EvdevListener;
|
import com.limelight.binding.input.evdev.EvdevListener;
|
||||||
|
import com.limelight.binding.input.touch.TouchContext;
|
||||||
import com.limelight.binding.input.virtual_controller.VirtualController;
|
import com.limelight.binding.input.virtual_controller.VirtualController;
|
||||||
import com.limelight.binding.video.CrashListener;
|
import com.limelight.binding.video.CrashListener;
|
||||||
import com.limelight.binding.video.MediaCodecDecoderRenderer;
|
import com.limelight.binding.video.MediaCodecDecoderRenderer;
|
||||||
@@ -86,8 +88,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
|
||||||
@@ -97,6 +97,12 @@ 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_DOWN_DEAD_ZONE_DELAY = 100;
|
||||||
|
private static final int STYLUS_DOWN_DEAD_ZONE_RADIUS = 20;
|
||||||
|
|
||||||
|
private static final int STYLUS_UP_DEAD_ZONE_DELAY = 150;
|
||||||
|
private static final int STYLUS_UP_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;
|
||||||
@@ -118,6 +124,10 @@ 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 lastAbsTouchUpTime = 0;
|
||||||
|
private long lastAbsTouchDownTime = 0;
|
||||||
|
private float lastAbsTouchUpX, lastAbsTouchUpY;
|
||||||
|
private float lastAbsTouchDownX, lastAbsTouchDownY;
|
||||||
|
|
||||||
private boolean isHidingOverlays;
|
private boolean isHidingOverlays;
|
||||||
private TextView notificationOverlayView;
|
private TextView notificationOverlayView;
|
||||||
@@ -228,7 +238,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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -466,9 +476,14 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
// Initialize touch contexts
|
// Initialize touch contexts
|
||||||
for (int i = 0; i < touchContextMap.length; i++) {
|
for (int i = 0; i < touchContextMap.length; i++) {
|
||||||
touchContextMap[i] = new TouchContext(conn, i,
|
if (!prefConfig.touchscreenTrackpad) {
|
||||||
REFERENCE_HORIZ_RES, REFERENCE_VERT_RES,
|
touchContextMap[i] = new AbsoluteTouchContext(conn, i, streamView);
|
||||||
streamView);
|
}
|
||||||
|
else {
|
||||||
|
touchContextMap[i] = new RelativeTouchContext(conn, i,
|
||||||
|
REFERENCE_HORIZ_RES, REFERENCE_VERT_RES,
|
||||||
|
streamView);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use sustained performance mode on N+ to ensure consistent
|
// Use sustained performance mode on N+ to ensure consistent
|
||||||
@@ -1141,7 +1156,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;
|
||||||
@@ -1155,11 +1171,13 @@ 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;
|
||||||
|
|
||||||
@@ -1170,18 +1188,28 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Always update the position before sending any button events. If we're
|
||||||
|
// dealing with a stylus without hover support, our position might be
|
||||||
|
// significantly different than before.
|
||||||
|
if (inputCaptureProvider.eventHasRelativeMouseAxes(event)) {
|
||||||
|
// Send the deltas straight from the motion event
|
||||||
|
short deltaX = (short)inputCaptureProvider.getRelativeAxisX(event);
|
||||||
|
short deltaY = (short)inputCaptureProvider.getRelativeAxisY(event);
|
||||||
|
|
||||||
|
if (deltaX != 0 || deltaY != 0) {
|
||||||
|
conn.sendMouseMove(deltaX, deltaY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (view != null) {
|
||||||
|
// Otherwise send absolute position
|
||||||
|
updateMousePosition(view, event);
|
||||||
|
}
|
||||||
|
|
||||||
if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
|
if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
|
||||||
// Send the vertical scroll packet
|
// Send the vertical scroll packet
|
||||||
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) {
|
||||||
@@ -1192,8 +1220,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 {
|
||||||
@@ -1201,8 +1230,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 {
|
||||||
@@ -1230,31 +1260,47 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get relative axis values if we can
|
// Handle stylus presses
|
||||||
if (inputCaptureProvider.eventHasRelativeMouseAxes(event)) {
|
if (event.getPointerCount() == 1 && event.getActionIndex() == 0) {
|
||||||
// Send the deltas straight from the motion event
|
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||||
conn.sendMouseMove((short) inputCaptureProvider.getRelativeAxisX(event),
|
if (event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS) {
|
||||||
(short) inputCaptureProvider.getRelativeAxisY(event));
|
lastAbsTouchDownTime = SystemClock.uptimeMillis();
|
||||||
|
lastAbsTouchDownX = event.getX(0);
|
||||||
|
lastAbsTouchDownY = event.getY(0);
|
||||||
|
|
||||||
// We have to also update the position Android thinks the cursor is at
|
// Stylus is left click
|
||||||
// in order to avoid jumping when we stop moving or click.
|
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_LEFT);
|
||||||
lastMouseX = (int)event.getX();
|
} else if (event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER) {
|
||||||
lastMouseY = (int)event.getY();
|
lastAbsTouchDownTime = SystemClock.uptimeMillis();
|
||||||
}
|
lastAbsTouchDownX = event.getX(0);
|
||||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
lastAbsTouchDownY = event.getY(0);
|
||||||
// We get a normal (non-relative) MotionEvent when starting pointer capture to synchronize the
|
|
||||||
// location of the cursor with our app. We don't want this, so we must discard this event.
|
// Eraser is right click
|
||||||
lastMouseX = (int)event.getX();
|
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
|
||||||
lastMouseY = (int)event.getY();
|
}
|
||||||
}
|
}
|
||||||
else {
|
else if (event.getActionMasked() == MotionEvent.ACTION_UP || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
|
||||||
// Don't process the history. We just want the current position now.
|
if (event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS) {
|
||||||
updateMousePosition((int)event.getX(), (int)event.getY());
|
lastAbsTouchUpTime = SystemClock.uptimeMillis();
|
||||||
|
lastAbsTouchUpX = event.getX(0);
|
||||||
|
lastAbsTouchUpY = event.getY(0);
|
||||||
|
|
||||||
|
// Stylus is left click
|
||||||
|
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_LEFT);
|
||||||
|
} else if (event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER) {
|
||||||
|
lastAbsTouchUpTime = SystemClock.uptimeMillis();
|
||||||
|
lastAbsTouchUpX = event.getX(0);
|
||||||
|
lastAbsTouchUpY = event.getY(0);
|
||||||
|
|
||||||
|
// Eraser is right click
|
||||||
|
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 &&
|
||||||
@@ -1264,6 +1310,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (view == null && !prefConfig.touchscreenTrackpad) {
|
||||||
|
// Absolute touch events should be dropped outside our view.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int actionIndex = event.getActionIndex();
|
int actionIndex = event.getActionIndex();
|
||||||
|
|
||||||
int eventX = (int)event.getX(actionIndex);
|
int eventX = (int)event.getX(actionIndex);
|
||||||
@@ -1293,7 +1344,10 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
{
|
{
|
||||||
case MotionEvent.ACTION_POINTER_DOWN:
|
case MotionEvent.ACTION_POINTER_DOWN:
|
||||||
case MotionEvent.ACTION_DOWN:
|
case MotionEvent.ACTION_DOWN:
|
||||||
context.touchDownEvent(eventX, eventY);
|
for (TouchContext touchContext : touchContextMap) {
|
||||||
|
touchContext.setPointerCount(event.getPointerCount());
|
||||||
|
}
|
||||||
|
context.touchDownEvent(eventX, eventY, true);
|
||||||
break;
|
break;
|
||||||
case MotionEvent.ACTION_POINTER_UP:
|
case MotionEvent.ACTION_POINTER_UP:
|
||||||
case MotionEvent.ACTION_UP:
|
case MotionEvent.ACTION_UP:
|
||||||
@@ -1306,9 +1360,12 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
context.touchUpEvent(eventX, eventY);
|
context.touchUpEvent(eventX, eventY);
|
||||||
|
for (TouchContext touchContext : touchContextMap) {
|
||||||
|
touchContext.setPointerCount(event.getPointerCount() - 1);
|
||||||
|
}
|
||||||
if (actionIndex == 0 && event.getPointerCount() > 1 && !context.isCancelled()) {
|
if (actionIndex == 0 && event.getPointerCount() > 1 && !context.isCancelled()) {
|
||||||
// The original secondary touch now becomes primary
|
// The original secondary touch now becomes primary
|
||||||
context.touchDownEvent((int)event.getX(1), (int)event.getY(1));
|
context.touchDownEvent((int)event.getX(1), (int)event.getY(1), false);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MotionEvent.ACTION_MOVE:
|
case MotionEvent.ACTION_MOVE:
|
||||||
@@ -1340,6 +1397,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
case MotionEvent.ACTION_CANCEL:
|
case MotionEvent.ACTION_CANCEL:
|
||||||
for (TouchContext aTouchContext : touchContextMap) {
|
for (TouchContext aTouchContext : touchContextMap) {
|
||||||
aTouchContext.cancelTouch();
|
aTouchContext.cancelTouch();
|
||||||
|
aTouchContext.setPointerCount(0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -1357,48 +1415,67 @@ 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;
|
switch (event.getActionMasked()) {
|
||||||
int deltaY = eventY - lastMouseY;
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
case MotionEvent.ACTION_HOVER_ENTER:
|
||||||
|
case MotionEvent.ACTION_HOVER_EXIT:
|
||||||
|
case MotionEvent.ACTION_HOVER_MOVE:
|
||||||
|
if (SystemClock.uptimeMillis() - lastAbsTouchUpTime <= STYLUS_UP_DEAD_ZONE_DELAY &&
|
||||||
|
Math.sqrt(Math.pow(eventX - lastAbsTouchUpX, 2) + Math.pow(eventY - lastAbsTouchUpY, 2)) <= STYLUS_UP_DEAD_ZONE_RADIUS) {
|
||||||
|
// Enforce a small deadzone between touch up and hover or touch down to allow more precise double-clicking
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// Scale the deltas if the device resolution is different
|
case MotionEvent.ACTION_MOVE:
|
||||||
// than the stream resolution
|
case MotionEvent.ACTION_UP:
|
||||||
deltaX = (int)Math.round((double)deltaX * (REFERENCE_HORIZ_RES / (double)streamView.getWidth()));
|
if (SystemClock.uptimeMillis() - lastAbsTouchDownTime <= STYLUS_DOWN_DEAD_ZONE_DELAY &&
|
||||||
deltaY = (int)Math.round((double)deltaY * (REFERENCE_VERT_RES / (double)streamView.getHeight()));
|
Math.sqrt(Math.pow(eventX - lastAbsTouchDownX, 2) + Math.pow(eventY - lastAbsTouchDownY, 2)) <= STYLUS_DOWN_DEAD_ZONE_RADIUS) {
|
||||||
|
// Enforce a small deadzone between touch down and move or touch up to allow more precise double-clicking
|
||||||
conn.sendMouseMove((short)deltaX, (short)deltaY);
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
||||||
@@ -1480,9 +1557,21 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
// Display the error dialog if it was an unexpected termination.
|
// Display the error dialog if it was an unexpected termination.
|
||||||
// Otherwise, just finish the activity immediately.
|
// Otherwise, just finish the activity immediately.
|
||||||
if (errorCode != 0) {
|
if (errorCode != MoonBridge.ML_ERROR_GRACEFUL_TERMINATION) {
|
||||||
|
String message;
|
||||||
|
|
||||||
|
switch (errorCode) {
|
||||||
|
case MoonBridge.ML_ERROR_NO_VIDEO_TRAFFIC:
|
||||||
|
message = getResources().getString(R.string.no_video_received_error);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
message = getResources().getString(R.string.conn_terminated_msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
Dialog.displayDialog(Game.this, getResources().getString(R.string.conn_terminated_title),
|
Dialog.displayDialog(Game.this, getResources().getString(R.string.conn_terminated_title),
|
||||||
getResources().getString(R.string.conn_terminated_msg), true);
|
message, true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
finish();
|
finish();
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -819,6 +827,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Override mode button for 8BitDo controllers
|
||||||
|
if (context.vendorId == 0x2dc8 && event.getScanCode() == 306) {
|
||||||
|
return KeyEvent.KEYCODE_BUTTON_MODE;
|
||||||
|
}
|
||||||
|
|
||||||
if (context.usesLinuxGamepadStandardFaceButtons) {
|
if (context.usesLinuxGamepadStandardFaceButtons) {
|
||||||
// Android's Generic.kl swaps BTN_NORTH and BTN_WEST
|
// Android's Generic.kl swaps BTN_NORTH and BTN_WEST
|
||||||
switch (event.getScanCode()) {
|
switch (event.getScanCode()) {
|
||||||
@@ -907,6 +920,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 &&
|
||||||
@@ -966,6 +996,21 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
return keyCode;
|
return keyCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int handleFlipFaceButtons(int keyCode) {
|
||||||
|
switch (keyCode) {
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_A:
|
||||||
|
return KeyEvent.KEYCODE_BUTTON_B;
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_B:
|
||||||
|
return KeyEvent.KEYCODE_BUTTON_A;
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_X:
|
||||||
|
return KeyEvent.KEYCODE_BUTTON_Y;
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_Y:
|
||||||
|
return KeyEvent.KEYCODE_BUTTON_X;
|
||||||
|
default:
|
||||||
|
return keyCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Vector2d populateCachedVector(float x, float y) {
|
private Vector2d populateCachedVector(float x, float y) {
|
||||||
// Reinitialize our cached Vector2d object
|
// Reinitialize our cached Vector2d object
|
||||||
inputVector.initialize(x, y);
|
inputVector.initialize(x, y);
|
||||||
@@ -1231,6 +1276,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
}
|
}
|
||||||
|
|
||||||
int keyCode = handleRemapping(context, event);
|
int keyCode = handleRemapping(context, event);
|
||||||
|
|
||||||
|
if (prefConfig.flipFaceButtons) {
|
||||||
|
keyCode = handleFlipFaceButtons(keyCode);
|
||||||
|
}
|
||||||
|
|
||||||
if (keyCode == 0) {
|
if (keyCode == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1392,6 +1442,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
}
|
}
|
||||||
|
|
||||||
int keyCode = handleRemapping(context, event);
|
int keyCode = handleRemapping(context, event);
|
||||||
|
|
||||||
|
if (prefConfig.flipFaceButtons) {
|
||||||
|
keyCode = handleFlipFaceButtons(keyCode);
|
||||||
|
}
|
||||||
|
|
||||||
if (keyCode == 0) {
|
if (keyCode == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1597,6 +1652,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");
|
||||||
|
|||||||
@@ -75,8 +75,10 @@ public class ShieldCaptureProvider extends InputCaptureProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
||||||
return event.getAxisValue(AXIS_RELATIVE_X) != 0 ||
|
// All mouse events should use relative axes, even if they are zero. This avoids triggering
|
||||||
event.getAxisValue(AXIS_RELATIVE_Y) != 0;
|
// cursor jumps if we get an event with no associated motion, like ACTION_DOWN or ACTION_UP.
|
||||||
|
return event.getPointerCount() == 1 && event.getActionIndex() == 0 &&
|
||||||
|
event.getToolType(0) == MotionEvent.TOOL_TYPE_MOUSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public class XboxOneController extends AbstractXboxController {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private static final byte[] FW2015_INIT = {0x05, 0x20, 0x00, 0x01, 0x00};
|
private static final byte[] FW2015_INIT = {0x05, 0x20, 0x00, 0x01, 0x00};
|
||||||
|
private static final byte[] ONE_S_INIT = {0x05, 0x20, 0x00, 0x0f, 0x06};
|
||||||
private static final byte[] HORI_INIT = {0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a,
|
private static final byte[] HORI_INIT = {0x01, 0x20, 0x00, 0x09, 0x00, 0x04, 0x20, 0x3a,
|
||||||
0x00, 0x00, 0x00, (byte)0x80, 0x00};
|
0x00, 0x00, 0x00, (byte)0x80, 0x00};
|
||||||
private static final byte[] PDP_INIT1 = {0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14};
|
private static final byte[] PDP_INIT1 = {0x0a, 0x20, 0x00, 0x03, 0x00, 0x01, 0x14};
|
||||||
@@ -38,6 +39,8 @@ public class XboxOneController extends AbstractXboxController {
|
|||||||
new InitPacket(0x0e6f, 0x0165, HORI_INIT),
|
new InitPacket(0x0e6f, 0x0165, HORI_INIT),
|
||||||
new InitPacket(0x0f0d, 0x0067, HORI_INIT),
|
new InitPacket(0x0f0d, 0x0067, HORI_INIT),
|
||||||
new InitPacket(0x0000, 0x0000, FW2015_INIT),
|
new InitPacket(0x0000, 0x0000, FW2015_INIT),
|
||||||
|
new InitPacket(0x045e, 0x02ea, ONE_S_INIT),
|
||||||
|
new InitPacket(0x045e, 0x0b00, ONE_S_INIT),
|
||||||
new InitPacket(0x0e6f, 0x0000, PDP_INIT1),
|
new InitPacket(0x0e6f, 0x0000, PDP_INIT1),
|
||||||
new InitPacket(0x0e6f, 0x0000, PDP_INIT2),
|
new InitPacket(0x0e6f, 0x0000, PDP_INIT2),
|
||||||
new InitPacket(0x24c6, 0x541a, RUMBLE_INIT1),
|
new InitPacket(0x24c6, 0x541a, RUMBLE_INIT1),
|
||||||
|
|||||||
@@ -0,0 +1,272 @@
|
|||||||
|
package com.limelight.binding.input.touch;
|
||||||
|
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.limelight.nvstream.NvConnection;
|
||||||
|
import com.limelight.nvstream.input.MouseButtonPacket;
|
||||||
|
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
|
public class AbsoluteTouchContext implements TouchContext {
|
||||||
|
private int lastTouchDownX = 0;
|
||||||
|
private int lastTouchDownY = 0;
|
||||||
|
private long lastTouchDownTime = 0;
|
||||||
|
private int lastTouchUpX = 0;
|
||||||
|
private int lastTouchUpY = 0;
|
||||||
|
private long lastTouchUpTime = 0;
|
||||||
|
private int lastTouchLocationX = 0;
|
||||||
|
private int lastTouchLocationY = 0;
|
||||||
|
private boolean cancelled;
|
||||||
|
private boolean confirmedLongPress;
|
||||||
|
private boolean confirmedTap;
|
||||||
|
private Timer longPressTimer;
|
||||||
|
private Timer tapDownTimer;
|
||||||
|
private float accumulatedScrollDelta;
|
||||||
|
|
||||||
|
private final NvConnection conn;
|
||||||
|
private final int actionIndex;
|
||||||
|
private final View targetView;
|
||||||
|
|
||||||
|
private static final int SCROLL_SPEED_DIVISOR = 20;
|
||||||
|
|
||||||
|
private static final int LONG_PRESS_TIME_THRESHOLD = 650;
|
||||||
|
private static final int LONG_PRESS_DISTANCE_THRESHOLD = 30;
|
||||||
|
|
||||||
|
private static final int DOUBLE_TAP_TIME_THRESHOLD = 250;
|
||||||
|
private static final int DOUBLE_TAP_DISTANCE_THRESHOLD = 60;
|
||||||
|
|
||||||
|
private static final int TOUCH_DOWN_DEAD_ZONE_TIME_THRESHOLD = 100;
|
||||||
|
private static final int TOUCH_DOWN_DEAD_ZONE_DISTANCE_THRESHOLD = 20;
|
||||||
|
|
||||||
|
public AbsoluteTouchContext(NvConnection conn, int actionIndex, View view)
|
||||||
|
{
|
||||||
|
this.conn = conn;
|
||||||
|
this.actionIndex = actionIndex;
|
||||||
|
this.targetView = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getActionIndex()
|
||||||
|
{
|
||||||
|
return actionIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean touchDownEvent(int eventX, int eventY, boolean isNewFinger)
|
||||||
|
{
|
||||||
|
if (!isNewFinger) {
|
||||||
|
// We don't handle finger transitions for absolute mode
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastTouchLocationX = lastTouchDownX = eventX;
|
||||||
|
lastTouchLocationY = lastTouchDownY = eventY;
|
||||||
|
lastTouchDownTime = SystemClock.uptimeMillis();
|
||||||
|
cancelled = confirmedTap = confirmedLongPress = false;
|
||||||
|
accumulatedScrollDelta = 0;
|
||||||
|
|
||||||
|
if (actionIndex == 0) {
|
||||||
|
// Start the timers
|
||||||
|
startTapDownTimer();
|
||||||
|
startLongPressTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean distanceExceeds(int deltaX, int deltaY, double limit) {
|
||||||
|
return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2)) > limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updatePosition(int eventX, int eventY) {
|
||||||
|
// We may get values slightly outside our view region on ACTION_HOVER_ENTER and ACTION_HOVER_EXIT.
|
||||||
|
// Normalize these to the view size. We can't just drop them because we won't always get an event
|
||||||
|
// 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), targetView.getWidth());
|
||||||
|
eventY = Math.min(Math.max(eventY, 0), targetView.getHeight());
|
||||||
|
|
||||||
|
conn.sendMousePosition((short)eventX, (short)eventY, (short)targetView.getWidth(), (short)targetView.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void touchUpEvent(int eventX, int eventY)
|
||||||
|
{
|
||||||
|
if (cancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionIndex == 0) {
|
||||||
|
// Cancel the timers
|
||||||
|
cancelLongPressTimer();
|
||||||
|
cancelTapDownTimer();
|
||||||
|
|
||||||
|
// Raise the mouse buttons that we currently have down
|
||||||
|
if (confirmedLongPress) {
|
||||||
|
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT);
|
||||||
|
}
|
||||||
|
else if (confirmedTap) {
|
||||||
|
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_LEFT);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If we get here, this means that the tap completed within the touch down
|
||||||
|
// deadzone time. We'll need to send the touch down and up events now at the
|
||||||
|
// original touch down position.
|
||||||
|
tapConfirmed();
|
||||||
|
try {
|
||||||
|
// FIXME: Sleeping on the main thread sucks
|
||||||
|
Thread.sleep(50);
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_LEFT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastTouchLocationX = lastTouchUpX = eventX;
|
||||||
|
lastTouchLocationY = lastTouchUpY = eventY;
|
||||||
|
lastTouchUpTime = SystemClock.uptimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void startLongPressTimer() {
|
||||||
|
longPressTimer = new Timer(true);
|
||||||
|
longPressTimer.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (AbsoluteTouchContext.this) {
|
||||||
|
// Check if someone cancelled us
|
||||||
|
if (longPressTimer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uncancellable now
|
||||||
|
longPressTimer = null;
|
||||||
|
|
||||||
|
// This timer should have already expired, but cancel it just in case
|
||||||
|
cancelTapDownTimer();
|
||||||
|
|
||||||
|
// Switch from a left click to a right click after a long press
|
||||||
|
confirmedLongPress = true;
|
||||||
|
if (confirmedTap) {
|
||||||
|
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_LEFT);
|
||||||
|
}
|
||||||
|
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, LONG_PRESS_TIME_THRESHOLD);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void cancelLongPressTimer() {
|
||||||
|
if (longPressTimer != null) {
|
||||||
|
longPressTimer.cancel();
|
||||||
|
longPressTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void startTapDownTimer() {
|
||||||
|
tapDownTimer = new Timer(true);
|
||||||
|
tapDownTimer.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (AbsoluteTouchContext.this) {
|
||||||
|
// Check if someone cancelled us
|
||||||
|
if (tapDownTimer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uncancellable now
|
||||||
|
tapDownTimer = null;
|
||||||
|
|
||||||
|
// Start our tap
|
||||||
|
tapConfirmed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, TOUCH_DOWN_DEAD_ZONE_TIME_THRESHOLD);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void cancelTapDownTimer() {
|
||||||
|
if (tapDownTimer != null) {
|
||||||
|
tapDownTimer.cancel();
|
||||||
|
tapDownTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tapConfirmed() {
|
||||||
|
if (confirmedTap || confirmedLongPress) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmedTap = true;
|
||||||
|
cancelTapDownTimer();
|
||||||
|
|
||||||
|
// Left button down at original position
|
||||||
|
if (lastTouchDownTime - lastTouchUpTime > DOUBLE_TAP_TIME_THRESHOLD ||
|
||||||
|
distanceExceeds(lastTouchDownX - lastTouchUpX, lastTouchDownY - lastTouchUpY, DOUBLE_TAP_DISTANCE_THRESHOLD)) {
|
||||||
|
// Don't reposition for finger down events within the deadzone. This makes double-clicking easier.
|
||||||
|
updatePosition(lastTouchDownX, lastTouchDownY);
|
||||||
|
}
|
||||||
|
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_LEFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean touchMoveEvent(int eventX, int eventY)
|
||||||
|
{
|
||||||
|
if (cancelled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actionIndex == 0) {
|
||||||
|
if (distanceExceeds(eventX - lastTouchDownX, eventY - lastTouchDownY, LONG_PRESS_DISTANCE_THRESHOLD)) {
|
||||||
|
// Moved too far since touch down. Cancel the long press timer.
|
||||||
|
cancelLongPressTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore motion within the deadzone period after touch down
|
||||||
|
if (confirmedTap || distanceExceeds(eventX - lastTouchDownX, eventY - lastTouchDownY, TOUCH_DOWN_DEAD_ZONE_DISTANCE_THRESHOLD)) {
|
||||||
|
tapConfirmed();
|
||||||
|
updatePosition(eventX, eventY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (actionIndex == 1) {
|
||||||
|
accumulatedScrollDelta += (eventY - lastTouchLocationY) / (float)SCROLL_SPEED_DIVISOR;
|
||||||
|
if ((short)accumulatedScrollDelta != 0) {
|
||||||
|
conn.sendMouseHighResScroll((short)accumulatedScrollDelta);
|
||||||
|
accumulatedScrollDelta -= (short)accumulatedScrollDelta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastTouchLocationX = eventX;
|
||||||
|
lastTouchLocationY = eventY;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelTouch() {
|
||||||
|
cancelled = true;
|
||||||
|
|
||||||
|
// Cancel the timers
|
||||||
|
cancelLongPressTimer();
|
||||||
|
cancelTapDownTimer();
|
||||||
|
|
||||||
|
// Raise the mouse buttons
|
||||||
|
if (confirmedLongPress) {
|
||||||
|
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT);
|
||||||
|
}
|
||||||
|
else if (confirmedTap) {
|
||||||
|
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_LEFT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPointerCount(int pointerCount) {
|
||||||
|
if (actionIndex == 0 && pointerCount > 1) {
|
||||||
|
cancelTouch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
-8
@@ -1,5 +1,6 @@
|
|||||||
package com.limelight.binding.input;
|
package com.limelight.binding.input.touch;
|
||||||
|
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.limelight.nvstream.NvConnection;
|
import com.limelight.nvstream.NvConnection;
|
||||||
@@ -8,7 +9,7 @@ import com.limelight.nvstream.input.MouseButtonPacket;
|
|||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
public class TouchContext {
|
public class RelativeTouchContext implements TouchContext {
|
||||||
private int lastTouchX = 0;
|
private int lastTouchX = 0;
|
||||||
private int lastTouchY = 0;
|
private int lastTouchY = 0;
|
||||||
private int originalTouchX = 0;
|
private int originalTouchX = 0;
|
||||||
@@ -32,8 +33,8 @@ public class TouchContext {
|
|||||||
private static final int TAP_TIME_THRESHOLD = 250;
|
private static final int TAP_TIME_THRESHOLD = 250;
|
||||||
private static final int DRAG_TIME_THRESHOLD = 650;
|
private static final int DRAG_TIME_THRESHOLD = 650;
|
||||||
|
|
||||||
public TouchContext(NvConnection conn, int actionIndex,
|
public RelativeTouchContext(NvConnection conn, int actionIndex,
|
||||||
int referenceWidth, int referenceHeight, View view)
|
int referenceWidth, int referenceHeight, View view)
|
||||||
{
|
{
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
this.actionIndex = actionIndex;
|
this.actionIndex = actionIndex;
|
||||||
@@ -42,6 +43,7 @@ public class TouchContext {
|
|||||||
this.targetView = view;
|
this.targetView = view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getActionIndex()
|
public int getActionIndex()
|
||||||
{
|
{
|
||||||
return actionIndex;
|
return actionIndex;
|
||||||
@@ -57,7 +59,7 @@ public class TouchContext {
|
|||||||
|
|
||||||
private boolean isTap()
|
private boolean isTap()
|
||||||
{
|
{
|
||||||
long timeDelta = System.currentTimeMillis() - originalTouchTime;
|
long timeDelta = SystemClock.uptimeMillis() - originalTouchTime;
|
||||||
|
|
||||||
return isWithinTapBounds(lastTouchX, lastTouchY) && timeDelta <= TAP_TIME_THRESHOLD;
|
return isWithinTapBounds(lastTouchX, lastTouchY) && timeDelta <= TAP_TIME_THRESHOLD;
|
||||||
}
|
}
|
||||||
@@ -72,7 +74,8 @@ public class TouchContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean touchDownEvent(int eventX, int eventY)
|
@Override
|
||||||
|
public boolean touchDownEvent(int eventX, int eventY, boolean isNewFinger)
|
||||||
{
|
{
|
||||||
// Get the view dimensions to scale inputs on this touch
|
// Get the view dimensions to scale inputs on this touch
|
||||||
xFactor = referenceWidth / (double)targetView.getWidth();
|
xFactor = referenceWidth / (double)targetView.getWidth();
|
||||||
@@ -80,7 +83,7 @@ public class TouchContext {
|
|||||||
|
|
||||||
originalTouchX = lastTouchX = eventX;
|
originalTouchX = lastTouchX = eventX;
|
||||||
originalTouchY = lastTouchY = eventY;
|
originalTouchY = lastTouchY = eventY;
|
||||||
originalTouchTime = System.currentTimeMillis();
|
originalTouchTime = SystemClock.uptimeMillis();
|
||||||
cancelled = confirmedDrag = confirmedMove = false;
|
cancelled = confirmedDrag = confirmedMove = false;
|
||||||
distanceMoved = 0;
|
distanceMoved = 0;
|
||||||
|
|
||||||
@@ -92,6 +95,7 @@ public class TouchContext {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void touchUpEvent(int eventX, int eventY)
|
public void touchUpEvent(int eventX, int eventY)
|
||||||
{
|
{
|
||||||
if (cancelled) {
|
if (cancelled) {
|
||||||
@@ -128,7 +132,7 @@ public class TouchContext {
|
|||||||
dragTimer.schedule(new TimerTask() {
|
dragTimer.schedule(new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
synchronized (TouchContext.this) {
|
synchronized (RelativeTouchContext.this) {
|
||||||
// Check if someone already set move
|
// Check if someone already set move
|
||||||
if (confirmedMove) {
|
if (confirmedMove) {
|
||||||
return;
|
return;
|
||||||
@@ -179,6 +183,7 @@ public class TouchContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean touchMoveEvent(int eventX, int eventY)
|
public boolean touchMoveEvent(int eventX, int eventY)
|
||||||
{
|
{
|
||||||
if (eventX != lastTouchX || eventY != lastTouchY)
|
if (eventX != lastTouchX || eventY != lastTouchY)
|
||||||
@@ -223,6 +228,7 @@ public class TouchContext {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void cancelTouch() {
|
public void cancelTouch() {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
|
|
||||||
@@ -235,7 +241,11 @@ public class TouchContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isCancelled() {
|
public boolean isCancelled() {
|
||||||
return cancelled;
|
return cancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPointerCount(int pointerCount) {}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.limelight.binding.input.touch;
|
||||||
|
|
||||||
|
public interface TouchContext {
|
||||||
|
int getActionIndex();
|
||||||
|
void setPointerCount(int pointerCount);
|
||||||
|
boolean touchDownEvent(int eventX, int eventY, boolean isNewFinger);
|
||||||
|
boolean touchMoveEvent(int eventX, int eventY);
|
||||||
|
void touchUpEvent(int eventX, int eventY);
|
||||||
|
void cancelTouch();
|
||||||
|
boolean isCancelled();
|
||||||
|
}
|
||||||
+8
-4
@@ -205,7 +205,8 @@ public class VirtualControllerConfigurationLoader {
|
|||||||
|
|
||||||
controller.addElement(createDigitalButton(
|
controller.addElement(createDigitalButton(
|
||||||
VirtualControllerElement.EID_A,
|
VirtualControllerElement.EID_A,
|
||||||
ControllerPacket.A_FLAG, 0, 1, "A", -1, controller, context),
|
!config.flipFaceButtons ? ControllerPacket.A_FLAG : ControllerPacket.B_FLAG, 0, 1,
|
||||||
|
!config.flipFaceButtons ? "A" : "B", -1, controller, context),
|
||||||
screenScale(BUTTON_BASE_X, height) + rightDisplacement,
|
screenScale(BUTTON_BASE_X, height) + rightDisplacement,
|
||||||
screenScale(BUTTON_BASE_Y + 2 * BUTTON_SIZE, height),
|
screenScale(BUTTON_BASE_Y + 2 * BUTTON_SIZE, height),
|
||||||
screenScale(BUTTON_SIZE, height),
|
screenScale(BUTTON_SIZE, height),
|
||||||
@@ -214,7 +215,8 @@ public class VirtualControllerConfigurationLoader {
|
|||||||
|
|
||||||
controller.addElement(createDigitalButton(
|
controller.addElement(createDigitalButton(
|
||||||
VirtualControllerElement.EID_B,
|
VirtualControllerElement.EID_B,
|
||||||
ControllerPacket.B_FLAG, 0, 1, "B", -1, controller, context),
|
config.flipFaceButtons ? ControllerPacket.A_FLAG : ControllerPacket.B_FLAG, 0, 1,
|
||||||
|
config.flipFaceButtons ? "A" : "B", -1, controller, context),
|
||||||
screenScale(BUTTON_BASE_X + BUTTON_SIZE, height) + rightDisplacement,
|
screenScale(BUTTON_BASE_X + BUTTON_SIZE, height) + rightDisplacement,
|
||||||
screenScale(BUTTON_BASE_Y + BUTTON_SIZE, height),
|
screenScale(BUTTON_BASE_Y + BUTTON_SIZE, height),
|
||||||
screenScale(BUTTON_SIZE, height),
|
screenScale(BUTTON_SIZE, height),
|
||||||
@@ -223,7 +225,8 @@ public class VirtualControllerConfigurationLoader {
|
|||||||
|
|
||||||
controller.addElement(createDigitalButton(
|
controller.addElement(createDigitalButton(
|
||||||
VirtualControllerElement.EID_X,
|
VirtualControllerElement.EID_X,
|
||||||
ControllerPacket.X_FLAG, 0, 1, "X", -1, controller, context),
|
!config.flipFaceButtons ? ControllerPacket.X_FLAG : ControllerPacket.Y_FLAG, 0, 1,
|
||||||
|
!config.flipFaceButtons ? "X" : "Y", -1, controller, context),
|
||||||
screenScale(BUTTON_BASE_X - BUTTON_SIZE, height) + rightDisplacement,
|
screenScale(BUTTON_BASE_X - BUTTON_SIZE, height) + rightDisplacement,
|
||||||
screenScale(BUTTON_BASE_Y + BUTTON_SIZE, height),
|
screenScale(BUTTON_BASE_Y + BUTTON_SIZE, height),
|
||||||
screenScale(BUTTON_SIZE, height),
|
screenScale(BUTTON_SIZE, height),
|
||||||
@@ -232,7 +235,8 @@ public class VirtualControllerConfigurationLoader {
|
|||||||
|
|
||||||
controller.addElement(createDigitalButton(
|
controller.addElement(createDigitalButton(
|
||||||
VirtualControllerElement.EID_Y,
|
VirtualControllerElement.EID_Y,
|
||||||
ControllerPacket.Y_FLAG, 0, 1, "Y", -1, controller, context),
|
config.flipFaceButtons ? ControllerPacket.X_FLAG : ControllerPacket.Y_FLAG, 0, 1,
|
||||||
|
config.flipFaceButtons ? "X" : "Y", -1, controller, context),
|
||||||
screenScale(BUTTON_BASE_X, height) + rightDisplacement,
|
screenScale(BUTTON_BASE_X, height) + rightDisplacement,
|
||||||
screenScale(BUTTON_BASE_Y, height),
|
screenScale(BUTTON_BASE_Y, height),
|
||||||
screenScale(BUTTON_SIZE, height),
|
screenScale(BUTTON_SIZE, height),
|
||||||
|
|||||||
@@ -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());
|
||||||
@@ -287,6 +291,13 @@ public class NvConnection {
|
|||||||
MoonBridge.sendMouseMove(deltaX, deltaY);
|
MoonBridge.sendMouseMove(deltaX, deltaY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
@@ -337,6 +348,12 @@ public class NvConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendMouseHighResScroll(final short scrollAmount) {
|
||||||
|
if (!isMonkey) {
|
||||||
|
MoonBridge.sendMouseHighResScroll(scrollAmount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static String findExternalAddressForMdns(String stunHostname, int stunPort) {
|
public static String findExternalAddressForMdns(String stunHostname, int stunPort) {
|
||||||
return MoonBridge.findExternalAddressIP4(stunHostname, stunPort);
|
return MoonBridge.findExternalAddressIP4(stunHostname, stunPort);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"));
|
// We use Long.parseLong() because in rare cases GFE can send back a status code of
|
||||||
|
// 0xFFFFFFFF, which will cause Integer.parseInt() to throw a NumberFormatException due
|
||||||
|
// to exceeding Integer.MAX_VALUE. We'll get the desired error code of -1 by just casting
|
||||||
|
// the resulting long into an int.
|
||||||
|
int statusCode = (int)Long.parseLong(xpp.getAttributeValue(XmlPullParser.NO_NAMESPACE, "status_code"));
|
||||||
if (statusCode != 200) {
|
if (statusCode != 200) {
|
||||||
throw new GfeHttpResponseException(statusCode, xpp.getAttributeValue(XmlPullParser.NO_NAMESPACE, "status_message"));
|
String statusMsg = xpp.getAttributeValue(XmlPullParser.NO_NAMESPACE, "status_message");
|
||||||
|
if (statusCode == -1 && "Invalid".equals(statusMsg)) {
|
||||||
|
// Special case handling an audio capture error which GFE doesn't
|
||||||
|
// provide any useful status message for.
|
||||||
|
statusCode = 418;
|
||||||
|
statusMsg = "Missing audio capture device. Reinstall GeForce Experience.";
|
||||||
|
}
|
||||||
|
throw new GfeHttpResponseException(statusCode, statusMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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,10 +645,20 @@ 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 +
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ public class MoonBridge {
|
|||||||
public static final int CONN_STATUS_OKAY = 0;
|
public static final int CONN_STATUS_OKAY = 0;
|
||||||
public static final int CONN_STATUS_POOR = 1;
|
public static final int CONN_STATUS_POOR = 1;
|
||||||
|
|
||||||
|
public static final int ML_ERROR_GRACEFUL_TERMINATION = 0;
|
||||||
|
public static final int ML_ERROR_NO_VIDEO_TRAFFIC = -100;
|
||||||
|
|
||||||
private static AudioRenderer audioRenderer;
|
private static AudioRenderer audioRenderer;
|
||||||
private static VideoDecoderRenderer videoRenderer;
|
private static VideoDecoderRenderer videoRenderer;
|
||||||
private static NvConnectionListener connectionListener;
|
private static NvConnectionListener connectionListener;
|
||||||
@@ -239,6 +242,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,
|
||||||
@@ -256,6 +261,8 @@ public class MoonBridge {
|
|||||||
|
|
||||||
public static native void sendMouseScroll(byte scrollClicks);
|
public static native void sendMouseScroll(byte scrollClicks);
|
||||||
|
|
||||||
|
public static native void sendMouseHighResScroll(short scrollAmount);
|
||||||
|
|
||||||
public static native String getStageName(int stage);
|
public static native String getStageName(int stage);
|
||||||
|
|
||||||
public static native String findExternalAddressIP4(String stunHostName, int stunPort);
|
public static native String findExternalAddressIP4(String stunHostName, int stunPort);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ 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";
|
||||||
@@ -26,8 +26,7 @@ public class PreferenceConfiguration {
|
|||||||
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 AUDIO_CONFIG_PREF_STRING = "list_audio_config";
|
static final String AUDIO_CONFIG_PREF_STRING = "list_audio_config";
|
||||||
private static final String LEGACY_ENABLE_51_SURROUND_PREF_STRING = "checkbox_51_surround";
|
|
||||||
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";
|
||||||
@@ -42,6 +41,8 @@ public class PreferenceConfiguration {
|
|||||||
static final String UNLOCK_FPS_STRING = "checkbox_unlock_fps";
|
static final String UNLOCK_FPS_STRING = "checkbox_unlock_fps";
|
||||||
private static final String VIBRATE_OSC_PREF_STRING = "checkbox_vibrate_osc";
|
private static final String VIBRATE_OSC_PREF_STRING = "checkbox_vibrate_osc";
|
||||||
private static final String VIBRATE_FALLBACK_PREF_STRING = "checkbox_vibrate_fallback";
|
private static final String VIBRATE_FALLBACK_PREF_STRING = "checkbox_vibrate_fallback";
|
||||||
|
private static final String FLIP_FACE_BUTTONS_PREF_STRING = "checkbox_flip_face_buttons";
|
||||||
|
private static final String TOUCHSCREEN_TRACKPAD_PREF_STRING = "checkbox_touchscreen_trackpad";
|
||||||
|
|
||||||
static final String DEFAULT_RESOLUTION = "720p";
|
static final String DEFAULT_RESOLUTION = "720p";
|
||||||
static final String DEFAULT_FPS = "60";
|
static final String DEFAULT_FPS = "60";
|
||||||
@@ -68,6 +69,8 @@ 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 boolean DEFAULT_FLIP_FACE_BUTTONS = false;
|
||||||
|
private static final boolean DEFAULT_TOUCHSCREEN_TRACKPAD = true;
|
||||||
private static final String DEFAULT_AUDIO_CONFIG = "2"; // Stereo
|
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;
|
||||||
@@ -81,7 +84,7 @@ public class PreferenceConfiguration {
|
|||||||
public int oscOpacity;
|
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, usbDriver;
|
public boolean listMode, smallIconMode, multiController, usbDriver, flipFaceButtons;
|
||||||
public boolean onscreenController;
|
public boolean onscreenController;
|
||||||
public boolean onlyL3R3;
|
public boolean onlyL3R3;
|
||||||
public boolean disableFrameDrop;
|
public boolean disableFrameDrop;
|
||||||
@@ -94,6 +97,7 @@ public class PreferenceConfiguration {
|
|||||||
public boolean unlockFps;
|
public boolean unlockFps;
|
||||||
public boolean vibrateOsc;
|
public boolean vibrateOsc;
|
||||||
public boolean vibrateFallbackToDevice;
|
public boolean vibrateFallbackToDevice;
|
||||||
|
public boolean touchscreenTrackpad;
|
||||||
public MoonBridge.AudioConfiguration audioConfiguration;
|
public MoonBridge.AudioConfiguration audioConfiguration;
|
||||||
|
|
||||||
private static int getHeightFromResolutionString(String resString) {
|
private static int getHeightFromResolutionString(String resString) {
|
||||||
@@ -369,6 +373,8 @@ public class PreferenceConfiguration {
|
|||||||
config.unlockFps = prefs.getBoolean(UNLOCK_FPS_STRING, DEFAULT_UNLOCK_FPS);
|
config.unlockFps = prefs.getBoolean(UNLOCK_FPS_STRING, DEFAULT_UNLOCK_FPS);
|
||||||
config.vibrateOsc = prefs.getBoolean(VIBRATE_OSC_PREF_STRING, DEFAULT_VIBRATE_OSC);
|
config.vibrateOsc = prefs.getBoolean(VIBRATE_OSC_PREF_STRING, DEFAULT_VIBRATE_OSC);
|
||||||
config.vibrateFallbackToDevice = prefs.getBoolean(VIBRATE_FALLBACK_PREF_STRING, DEFAULT_VIBRATE_FALLBACK);
|
config.vibrateFallbackToDevice = prefs.getBoolean(VIBRATE_FALLBACK_PREF_STRING, DEFAULT_VIBRATE_FALLBACK);
|
||||||
|
config.flipFaceButtons = prefs.getBoolean(FLIP_FACE_BUTTONS_PREF_STRING, DEFAULT_FLIP_FACE_BUTTONS);
|
||||||
|
config.touchscreenTrackpad = prefs.getBoolean(TOUCHSCREEN_TRACKPAD_PREF_STRING, DEFAULT_TOUCHSCREEN_TRACKPAD);
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,9 +141,17 @@ public class StreamSettings extends Activity {
|
|||||||
// hide on-screen controls category on non touch screen devices
|
// hide on-screen controls category on non touch screen devices
|
||||||
if (!getActivity().getPackageManager().
|
if (!getActivity().getPackageManager().
|
||||||
hasSystemFeature("android.hardware.touchscreen")) {
|
hasSystemFeature("android.hardware.touchscreen")) {
|
||||||
PreferenceCategory category =
|
{
|
||||||
(PreferenceCategory) findPreference("category_onscreen_controls");
|
PreferenceCategory category =
|
||||||
screen.removePreference(category);
|
(PreferenceCategory) findPreference("category_onscreen_controls");
|
||||||
|
screen.removePreference(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
PreferenceCategory category =
|
||||||
|
(PreferenceCategory) findPreference("category_input_settings");
|
||||||
|
category.removePreference(findPreference("checkbox_touchscreen_trackpad"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove PiP mode on devices pre-Oreo
|
// Remove PiP mode on devices pre-Oreo
|
||||||
@@ -302,6 +310,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) {
|
||||||
|
|||||||
@@ -243,7 +243,7 @@ void BridgeArCleanup() {
|
|||||||
void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
|
void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
jshort* decodedData = (*env)->GetShortArrayElements(env, DecodedAudioBuffer, 0);
|
jshort* decodedData = (*env)->GetPrimitiveArrayCritical(env, DecodedAudioBuffer, NULL);
|
||||||
|
|
||||||
int decodeLen = opus_multistream_decode(Decoder,
|
int decodeLen = opus_multistream_decode(Decoder,
|
||||||
(const unsigned char*)sampleData,
|
(const unsigned char*)sampleData,
|
||||||
@@ -252,8 +252,8 @@ 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)) {
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
@@ -263,7 +263,7 @@ void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
|
|||||||
}
|
}
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Submodule app/src/main/jni/moonlight-core/moonlight-common-c updated: c2471157c0...c8faf3539b.
@@ -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);
|
||||||
@@ -43,6 +49,11 @@ Java_com_limelight_nvstream_jni_MoonBridge_sendMouseScroll(JNIEnv *env, jclass c
|
|||||||
LiSendScrollEvent(scrollClicks);
|
LiSendScrollEvent(scrollClicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JNIEXPORT void JNICALL
|
||||||
|
Java_com_limelight_nvstream_jni_MoonBridge_sendMouseHighResScroll(JNIEnv *env, jclass clazz, jshort scrollAmount) {
|
||||||
|
LiSendHighResScrollEvent(scrollAmount);
|
||||||
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_com_limelight_nvstream_jni_MoonBridge_stopConnection(JNIEnv *env, jclass clazz) {
|
Java_com_limelight_nvstream_jni_MoonBridge_stopConnection(JNIEnv *env, jclass clazz) {
|
||||||
LiStopConnection();
|
LiStopConnection();
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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,6 +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_audio_config_list"> 环绕声设置 </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>
|
||||||
|
|||||||
@@ -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,6 +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_audio_config_list"> 環繞聲設置 </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>
|
||||||
|
|||||||
@@ -54,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>
|
||||||
@@ -68,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">
|
||||||
|
|||||||
@@ -61,6 +61,7 @@
|
|||||||
<string name="error_usb_prohibited">USB access is prohibited by your device administrator. Check your Knox or MDM settings.</string>
|
<string name="error_usb_prohibited">USB access is prohibited by your device administrator. Check your Knox or MDM settings.</string>
|
||||||
<string name="unable_to_pin_shortcut">Your current launcher does not allow for creating pinned shortcuts.</string>
|
<string name="unable_to_pin_shortcut">Your current launcher does not allow for creating pinned shortcuts.</string>
|
||||||
<string name="video_decoder_init_failed">Video decoder failed to initialize. Your device may not support the selected resolution or frame rate.</string>
|
<string name="video_decoder_init_failed">Video decoder failed to initialize. Your device may not support the selected resolution or frame rate.</string>
|
||||||
|
<string name="no_video_received_error">No video received from host. Check the host PC\'s firewall and port forwarding rules.</string>
|
||||||
|
|
||||||
<!-- Start application messages -->
|
<!-- Start application messages -->
|
||||||
<string name="conn_establishing_title">Establishing Connection</string>
|
<string name="conn_establishing_title">Establishing Connection</string>
|
||||||
@@ -139,6 +140,8 @@
|
|||||||
<string name="summary_audio_config_list">Enable 5.1 or 7.1 surround sound for home-theater systems</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_touchscreen_trackpad">Use the touchscreen as a trackpad</string>
|
||||||
|
<string name="summary_checkbox_touchscreen_trackpad">If enabled, the touchscreen acts like a trackpad. If disabled, the touchscreen directly controls the mouse cursor.</string>
|
||||||
<string name="title_checkbox_multi_controller">Automatic gamepad presence detection</string>
|
<string name="title_checkbox_multi_controller">Automatic gamepad presence detection</string>
|
||||||
<string name="summary_checkbox_multi_controller">Unchecking this option forces a gamepad to always be present</string>
|
<string name="summary_checkbox_multi_controller">Unchecking this option forces a gamepad to always be present</string>
|
||||||
<string name="title_checkbox_vibrate_fallback">Emulate rumble support with vibration</string>
|
<string name="title_checkbox_vibrate_fallback">Emulate rumble support with vibration</string>
|
||||||
@@ -153,6 +156,8 @@
|
|||||||
<string name="summary_checkbox_mouse_emulation">Long pressing the Start button will switch the gamepad into mouse mode</string>
|
<string name="summary_checkbox_mouse_emulation">Long pressing the Start button will switch the gamepad into mouse mode</string>
|
||||||
<string name="title_checkbox_mouse_nav_buttons">Enable back and forward mouse buttons</string>
|
<string name="title_checkbox_mouse_nav_buttons">Enable back and forward mouse buttons</string>
|
||||||
<string name="summary_checkbox_mouse_nav_buttons">Enabling this option may break right clicking on some buggy devices</string>
|
<string name="summary_checkbox_mouse_nav_buttons">Enabling this option may break right clicking on some buggy devices</string>
|
||||||
|
<string name="title_checkbox_flip_face_buttons">Flip face buttons</string>
|
||||||
|
<string name="summary_checkbox_flip_face_buttons">Switches the face buttons A/B and X/Y for gamepads and the on-screen controls</string>
|
||||||
|
|
||||||
<string name="category_on_screen_controls_settings">On-screen Controls Settings</string>
|
<string name="category_on_screen_controls_settings">On-screen Controls Settings</string>
|
||||||
<string name="title_checkbox_show_onscreen_controls">Show on-screen controls</string>
|
<string name="title_checkbox_show_onscreen_controls">Show on-screen controls</string>
|
||||||
|
|||||||
@@ -51,13 +51,19 @@
|
|||||||
android:entryValues="@array/audio_config_values"
|
android:entryValues="@array/audio_config_values"
|
||||||
android:defaultValue="2" />
|
android:defaultValue="2" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory android:title="@string/category_input_settings">
|
<PreferenceCategory android:title="@string/category_input_settings"
|
||||||
|
android:key="category_input_settings">
|
||||||
<!--com.limelight.preferences.SeekBarPreference
|
<!--com.limelight.preferences.SeekBarPreference
|
||||||
android:key="seekbar_deadzone"
|
android:key="seekbar_deadzone"
|
||||||
android:defaultValue="15"
|
android:defaultValue="15"
|
||||||
android:max="50"
|
android:max="50"
|
||||||
android:text="@string/suffix_seekbar_deadzone"
|
android:text="@string/suffix_seekbar_deadzone"
|
||||||
android:title="@string/title_seekbar_deadzone"/-->
|
android:title="@string/title_seekbar_deadzone"/-->
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="checkbox_touchscreen_trackpad"
|
||||||
|
android:title="@string/title_checkbox_touchscreen_trackpad"
|
||||||
|
android:summary="@string/summary_checkbox_touchscreen_trackpad"
|
||||||
|
android:defaultValue="true" />
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:key="checkbox_multi_controller"
|
android:key="checkbox_multi_controller"
|
||||||
android:title="@string/title_checkbox_multi_controller"
|
android:title="@string/title_checkbox_multi_controller"
|
||||||
@@ -89,6 +95,11 @@
|
|||||||
android:title="@string/title_checkbox_vibrate_fallback"
|
android:title="@string/title_checkbox_vibrate_fallback"
|
||||||
android:summary="@string/summary_checkbox_vibrate_fallback"
|
android:summary="@string/summary_checkbox_vibrate_fallback"
|
||||||
android:defaultValue="false" />
|
android:defaultValue="false" />
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="checkbox_flip_face_buttons"
|
||||||
|
android:title="@string/title_checkbox_flip_face_buttons"
|
||||||
|
android:summary="@string/summary_checkbox_flip_face_buttons"
|
||||||
|
android:defaultValue="false" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory android:title="@string/category_on_screen_controls_settings"
|
<PreferenceCategory android:title="@string/category_on_screen_controls_settings"
|
||||||
android:key="category_onscreen_controls">
|
android:key="category_onscreen_controls">
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ buildscript {
|
|||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.6.2'
|
classpath 'com.android.tools.build:gradle:3.6.3'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
- Improved stylus deadzone behavior
|
||||||
|
- Fixed incorrect mouse position on styluses without hover support
|
||||||
|
- Fixed mouse clicks causing cursor jumping on Nvidia Shield devices
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
- Direct touchscreen mouse control is now supported! (disable touchscreen trackpad mode in settings)
|
||||||
|
- Added an option to switch the A/B and X/Y face buttons on gamepads and on-screen controls
|
||||||
|
- Added a special error message if no video was received instead of just displaying a black screen
|
||||||
|
- Improved support for Xbox One S controllers using the new 4.8 firmware
|
||||||
|
- Improved handling of audio capture errors reported by GeForce Experience
|
||||||
@@ -7,7 +7,8 @@ Streaming performance may vary based on your client device and network setup. HD
|
|||||||
* 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 7.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