Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b0f485b41 | |||
| 2be2c95212 | |||
| e7aeeb8bd5 | |||
| 73df93f86a | |||
| 9cd4d5e2aa | |||
| c3b81554f4 | |||
| 6f79c52fc5 | |||
| 29bc3e022b | |||
| 7d03203d83 |
+2
-2
@@ -7,8 +7,8 @@ android {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 29
|
||||
|
||||
versionName "9.1"
|
||||
versionCode = 220
|
||||
versionName "9.2"
|
||||
versionCode = 222
|
||||
}
|
||||
|
||||
flavorDimensions "root"
|
||||
|
||||
@@ -86,8 +86,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
OnSystemUiVisibilityChangeListener, GameGestures, StreamView.InputCallbacks,
|
||||
PerfOverlayListener
|
||||
{
|
||||
private int lastMouseX = Integer.MIN_VALUE;
|
||||
private int lastMouseY = Integer.MIN_VALUE;
|
||||
private int lastButtonState = 0;
|
||||
|
||||
// Only 2 touches are supported
|
||||
@@ -97,6 +95,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
private static final int REFERENCE_HORIZ_RES = 1280;
|
||||
private static final int REFERENCE_VERT_RES = 720;
|
||||
|
||||
private static final int STYLUS_DEAD_ZONE_DELAY = 250;
|
||||
private static final int STYLUS_DEAD_ZONE_RADIUS = 50;
|
||||
|
||||
private static final int THREE_FINGER_TAP_THRESHOLD = 300;
|
||||
|
||||
private ControllerHandler controllerHandler;
|
||||
@@ -118,6 +119,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
private boolean grabbedInput = true;
|
||||
private boolean grabComboDown = false;
|
||||
private StreamView streamView;
|
||||
private long stylusDownTime = 0;
|
||||
private float stylusDownX, stylusDownY;
|
||||
|
||||
private boolean isHidingOverlays;
|
||||
private TextView notificationOverlayView;
|
||||
@@ -228,7 +231,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
streamView.setOnCapturedPointerListener(new View.OnCapturedPointerListener() {
|
||||
@Override
|
||||
public boolean onCapturedPointer(View view, MotionEvent motionEvent) {
|
||||
return handleMotionEvent(motionEvent);
|
||||
return handleMotionEvent(view, motionEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1141,7 +1144,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
}
|
||||
|
||||
// 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
|
||||
if (!grabbedInput) {
|
||||
return false;
|
||||
@@ -1155,11 +1159,13 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
else if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0 ||
|
||||
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 ||
|
||||
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE ||
|
||||
(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;
|
||||
|
||||
@@ -1175,13 +1181,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
byte vScrollClicks = (byte) event.getAxisValue(MotionEvent.AXIS_VSCROLL);
|
||||
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 ((event.getButtonState() & MotionEvent.BUTTON_PRIMARY) != 0) {
|
||||
@@ -1192,8 +1191,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
}
|
||||
}
|
||||
|
||||
if ((changedButtons & MotionEvent.BUTTON_SECONDARY) != 0) {
|
||||
if ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY) != 0) {
|
||||
// Mouse secondary or stylus primary is right click (stylus down is left click)
|
||||
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);
|
||||
}
|
||||
else {
|
||||
@@ -1201,8 +1201,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
}
|
||||
}
|
||||
|
||||
if ((changedButtons & MotionEvent.BUTTON_TERTIARY) != 0) {
|
||||
if ((event.getButtonState() & MotionEvent.BUTTON_TERTIARY) != 0) {
|
||||
// Mouse tertiary or stylus secondary is middle click
|
||||
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);
|
||||
}
|
||||
else {
|
||||
@@ -1230,31 +1231,60 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle stylus presses
|
||||
if (event.getPointerCount() == 1 && event.getActionIndex() == 0) {
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
if (event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS) {
|
||||
stylusDownTime = SystemClock.uptimeMillis();
|
||||
stylusDownX = event.getX(0);
|
||||
stylusDownY = event.getY(0);
|
||||
|
||||
// Stylus is left click
|
||||
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_LEFT);
|
||||
} else if (event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER) {
|
||||
stylusDownTime = SystemClock.uptimeMillis();
|
||||
stylusDownX = event.getX(0);
|
||||
stylusDownY = event.getY(0);
|
||||
|
||||
// Eraser is right click
|
||||
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
|
||||
}
|
||||
}
|
||||
else if (event.getActionMasked() == MotionEvent.ACTION_UP || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
|
||||
if (event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS) {
|
||||
// It looks odd to set these on ACTION_UP, but it makes sense.
|
||||
// The last "down" position is actually when we come up.
|
||||
stylusDownTime = SystemClock.uptimeMillis();
|
||||
stylusDownX = event.getX(0);
|
||||
stylusDownY = event.getY(0);
|
||||
|
||||
// Stylus is left click
|
||||
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_LEFT);
|
||||
} else if (event.getToolType(0) == MotionEvent.TOOL_TYPE_ERASER) {
|
||||
stylusDownTime = SystemClock.uptimeMillis();
|
||||
stylusDownX = event.getX(0);
|
||||
stylusDownY = event.getY(0);
|
||||
|
||||
// Eraser is right click
|
||||
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get relative axis values if we can
|
||||
if (inputCaptureProvider.eventHasRelativeMouseAxes(event)) {
|
||||
// Send the deltas straight from the motion event
|
||||
conn.sendMouseMove((short) inputCaptureProvider.getRelativeAxisX(event),
|
||||
(short) inputCaptureProvider.getRelativeAxisY(event));
|
||||
|
||||
// We have to also update the position Android thinks the cursor is at
|
||||
// in order to avoid jumping when we stop moving or click.
|
||||
lastMouseX = (int)event.getX();
|
||||
lastMouseY = (int)event.getY();
|
||||
}
|
||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
// 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.
|
||||
lastMouseX = (int)event.getX();
|
||||
lastMouseY = (int)event.getY();
|
||||
}
|
||||
else {
|
||||
// Don't process the history. We just want the current position now.
|
||||
updateMousePosition((int)event.getX(), (int)event.getY());
|
||||
else if (view != null) {
|
||||
// Otherwise send absolute position
|
||||
updateMousePosition(view, event);
|
||||
}
|
||||
|
||||
lastButtonState = event.getButtonState();
|
||||
}
|
||||
// This case is for touch-based input devices
|
||||
// This case is for fingers
|
||||
else
|
||||
{
|
||||
if (virtualController != null &&
|
||||
@@ -1357,48 +1387,51 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
return handleMotionEvent(event) || super.onTouchEvent(event);
|
||||
return handleMotionEvent(null, event) || super.onTouchEvent(event);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
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) {
|
||||
// Send a mouse move if we already have a mouse location
|
||||
// and the mouse coordinates change
|
||||
if (lastMouseX != Integer.MIN_VALUE &&
|
||||
lastMouseY != Integer.MIN_VALUE &&
|
||||
!(lastMouseX == eventX && lastMouseY == eventY))
|
||||
private void updateMousePosition(View view, MotionEvent event) {
|
||||
// X and Y are already relative to the provided view object
|
||||
float eventX = event.getX(0);
|
||||
float eventY = event.getY(0);
|
||||
|
||||
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;
|
||||
int deltaY = eventY - lastMouseY;
|
||||
|
||||
// Scale the deltas if the device resolution is different
|
||||
// than the stream resolution
|
||||
deltaX = (int)Math.round((double)deltaX * (REFERENCE_HORIZ_RES / (double)streamView.getWidth()));
|
||||
deltaY = (int)Math.round((double)deltaY * (REFERENCE_VERT_RES / (double)streamView.getHeight()));
|
||||
|
||||
conn.sendMouseMove((short)deltaX, (short)deltaY);
|
||||
if (SystemClock.uptimeMillis() - stylusDownTime <= STYLUS_DEAD_ZONE_DELAY &&
|
||||
Math.sqrt(Math.pow(eventX - stylusDownX, 2) + Math.pow(eventY - stylusDownY, 2)) <= STYLUS_DEAD_ZONE_RADIUS) {
|
||||
// Ignore small inputs shortly after the stylus has been pressed. This ensures users can reliably double click.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Update pointer location for delta calculation next time
|
||||
lastMouseX = eventX;
|
||||
lastMouseY = 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), view.getWidth());
|
||||
eventY = Math.min(Math.max(eventY, 0), view.getHeight());
|
||||
|
||||
conn.sendMousePosition((short)eventX, (short)eventY, (short)view.getWidth(), (short)view.getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGenericMotion(View v, MotionEvent event) {
|
||||
return handleMotionEvent(event);
|
||||
public boolean onGenericMotion(View view, MotionEvent event) {
|
||||
return handleMotionEvent(view, event);
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
return handleMotionEvent(event);
|
||||
public boolean onTouch(View view, MotionEvent event) {
|
||||
return handleMotionEvent(view, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -332,6 +332,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
context.device = device;
|
||||
context.external = true;
|
||||
|
||||
context.vendorId = device.getVendorId();
|
||||
context.productId = device.getProductId();
|
||||
|
||||
context.leftStickDeadzoneRadius = (float) stickDeadzone;
|
||||
context.rightStickDeadzoneRadius = (float) stickDeadzone;
|
||||
context.triggerDeadzone = 0.13f;
|
||||
@@ -445,7 +448,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
String devName = dev.getName();
|
||||
|
||||
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("Product ID: "+dev.getProductId());
|
||||
}
|
||||
@@ -455,6 +458,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
context.id = dev.getId();
|
||||
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()) {
|
||||
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 ryRange = getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_RY);
|
||||
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.hasKeys(KeyEvent.KEYCODE_BUTTON_C)[0]) {
|
||||
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
|
||||
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
|
||||
// use VID/PID instead
|
||||
if (dev.getVendorId() == 0x18d1 && dev.getProductId() == 0x2c40) {
|
||||
@@ -610,7 +618,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
if (devName.contains("ASUS Gamepad")) {
|
||||
// We can only do this check on KitKat or higher, but it doesn't matter since ATV
|
||||
// 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);
|
||||
if (!hasStartKey[0] && !hasStartKey[1]) {
|
||||
context.backIsStart = true;
|
||||
@@ -907,6 +915,23 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
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 &&
|
||||
context.hatYAxis == -1 &&
|
||||
@@ -1597,6 +1622,9 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
||||
public int id;
|
||||
public boolean external;
|
||||
|
||||
public int vendorId;
|
||||
public int productId;
|
||||
|
||||
public float leftStickDeadzoneRadius;
|
||||
public float rightStickDeadzoneRadius;
|
||||
public float triggerDeadzone;
|
||||
|
||||
+8
-8
@@ -1,17 +1,22 @@
|
||||
package com.limelight.binding.input.capture;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.view.InputDevice;
|
||||
import android.view.MotionEvent;
|
||||
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;
|
||||
|
||||
public AndroidNativePointerCaptureProvider(View targetView) {
|
||||
public AndroidNativePointerCaptureProvider(Activity activity, View targetView) {
|
||||
super(activity, targetView);
|
||||
this.targetView = targetView;
|
||||
}
|
||||
|
||||
@@ -31,11 +36,6 @@ public class AndroidNativePointerCaptureProvider extends InputCaptureProvider {
|
||||
targetView.releasePointerCapture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCapturingActive() {
|
||||
return targetView.hasPointerCapture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean eventHasRelativeMouseAxes(MotionEvent event) {
|
||||
return event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE;
|
||||
|
||||
+5
-30
@@ -7,55 +7,30 @@ import android.os.Build;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.PointerIcon;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
public class AndroidPointerIconCaptureProvider extends InputCaptureProvider {
|
||||
private ViewGroup rootViewGroup;
|
||||
private View targetView;
|
||||
private Context context;
|
||||
|
||||
public AndroidPointerIconCaptureProvider(Activity activity) {
|
||||
public AndroidPointerIconCaptureProvider(Activity activity, View targetView) {
|
||||
this.context = activity;
|
||||
this.rootViewGroup = (ViewGroup) activity.getWindow().getDecorView();
|
||||
this.targetView = targetView;
|
||||
}
|
||||
|
||||
public static boolean isCaptureProviderSupported() {
|
||||
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
|
||||
public void enableCapture() {
|
||||
super.enableCapture();
|
||||
setPointerIconOnAllViews(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_NULL));
|
||||
targetView.setPointerIcon(PointerIcon.getSystemIcon(context, PointerIcon.TYPE_NULL));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableCapture() {
|
||||
super.disableCapture();
|
||||
setPointerIconOnAllViews(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);
|
||||
targetView.setPointerIcon(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ public class InputCaptureManager {
|
||||
public static InputCaptureProvider getInputCaptureProvider(Activity activity, EvdevListener rootListener) {
|
||||
if (AndroidNativePointerCaptureProvider.isCaptureProviderSupported()) {
|
||||
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.
|
||||
// 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
|
||||
// so we want to only use it if there's no other option.
|
||||
LimeLog.info("Using Android N+ pointer hiding");
|
||||
return new AndroidPointerIconCaptureProvider(activity);
|
||||
return new AndroidPointerIconCaptureProvider(activity, activity.findViewById(R.id.surfaceView));
|
||||
}
|
||||
else {
|
||||
LimeLog.info("Mouse capture not available");
|
||||
|
||||
@@ -3,6 +3,8 @@ package com.limelight.binding.input.driver;
|
||||
public abstract class AbstractController {
|
||||
|
||||
private final int deviceId;
|
||||
private final int vendorId;
|
||||
private final int productId;
|
||||
|
||||
private UsbDriverListener listener;
|
||||
|
||||
@@ -15,6 +17,14 @@ public abstract class AbstractController {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
public int getVendorId() {
|
||||
return vendorId;
|
||||
}
|
||||
|
||||
public int getProductId() {
|
||||
return productId;
|
||||
}
|
||||
|
||||
protected void setButtonFlag(int buttonFlag, int data) {
|
||||
if (data != 0) {
|
||||
buttonFlags |= buttonFlag;
|
||||
@@ -32,9 +42,11 @@ public abstract class AbstractController {
|
||||
public abstract boolean start();
|
||||
public abstract void stop();
|
||||
|
||||
public AbstractController(int deviceId, UsbDriverListener listener) {
|
||||
public AbstractController(int deviceId, UsbDriverListener listener, int vendorId, int productId) {
|
||||
this.deviceId = deviceId;
|
||||
this.listener = listener;
|
||||
this.vendorId = vendorId;
|
||||
this.productId = productId;
|
||||
}
|
||||
|
||||
public abstract void rumble(short lowFreqMotor, short highFreqMotor);
|
||||
|
||||
@@ -22,7 +22,7 @@ public abstract class AbstractXboxController extends AbstractController {
|
||||
protected UsbEndpoint inEndpt, outEndpt;
|
||||
|
||||
public AbstractXboxController(UsbDevice device, UsbDeviceConnection connection, int deviceId, UsbDriverListener listener) {
|
||||
super(deviceId, listener);
|
||||
super(deviceId, listener, device.getVendorId(), device.getProductId());
|
||||
this.device = device;
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@@ -234,6 +234,10 @@ public class NvConnection {
|
||||
return;
|
||||
}
|
||||
context.connListener.stageComplete(appName);
|
||||
} catch (GfeHttpResponseException e) {
|
||||
e.printStackTrace();
|
||||
context.connListener.displayMessage(e.getMessage());
|
||||
context.connListener.stageFailed(appName, e.getErrorCode());
|
||||
} catch (XmlPullParserException | IOException e) {
|
||||
e.printStackTrace();
|
||||
context.connListener.displayMessage(e.getMessage());
|
||||
@@ -287,6 +291,13 @@ public class NvConnection {
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -186,9 +186,20 @@ public class NvHTTP {
|
||||
}
|
||||
|
||||
private static void verifyResponseStatus(XmlPullParser xpp) throws GfeHttpResponseException {
|
||||
int statusCode = Integer.parseInt(xpp.getAttributeValue(XmlPullParser.NO_NAMESPACE, "status_code"));
|
||||
if (statusCode != 200) {
|
||||
throw new GfeHttpResponseException(statusCode, xpp.getAttributeValue(XmlPullParser.NO_NAMESPACE, "status_message"));
|
||||
String statusCodeText = xpp.getAttributeValue(XmlPullParser.NO_NAMESPACE, "status_code");
|
||||
if (statusCodeText == null) {
|
||||
throw new GfeHttpResponseException(418, "Status code is missing");
|
||||
}
|
||||
try {
|
||||
int statusCode = Integer.parseInt(statusCodeText);
|
||||
if (statusCode != 200) {
|
||||
throw new GfeHttpResponseException(statusCode, xpp.getAttributeValue(XmlPullParser.NO_NAMESPACE, "status_message"));
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
// It seems like GFE 3.20.3.63 is returning garbage for status_code in rare cases.
|
||||
// Surface this in a more friendly way rather than crashing.
|
||||
throw new GfeHttpResponseException(418, "Status code is not a number: "+statusCodeText);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,7 +342,7 @@ public class NvHTTP {
|
||||
throw new FileNotFoundException(url);
|
||||
}
|
||||
else {
|
||||
throw new IOException("HTTP request failed: "+response.code());
|
||||
throw new GfeHttpResponseException(response.code(), response.message());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -239,6 +239,8 @@ public class MoonBridge {
|
||||
|
||||
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 sendMultiControllerInput(short controllerNumber,
|
||||
|
||||
@@ -10,6 +10,12 @@ Java_com_limelight_nvstream_jni_MoonBridge_sendMouseMove(JNIEnv *env, jclass cla
|
||||
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
|
||||
Java_com_limelight_nvstream_jni_MoonBridge_sendMouseButton(JNIEnv *env, jclass clazz, jbyte buttonEvent, jbyte mouseButton) {
|
||||
LiSendMouseButtonEvent(buttonEvent, mouseButton);
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ buildscript {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.6.2'
|
||||
classpath 'com.android.tools.build:gradle:3.6.3'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -7,7 +7,8 @@ Streaming performance may vary based on your client device and network setup. HD
|
||||
* Streams games purchased from any store
|
||||
* Works on your home network or over the Internet/LTE
|
||||
* 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
|
||||
* Force feedback support
|
||||
* Local co-op with up to 4 connected controllers
|
||||
|
||||
Reference in New Issue
Block a user