diff --git a/app/src/main/java/com/limelight/Game.java b/app/src/main/java/com/limelight/Game.java index 5d214508a3..a98b0e9ffb 100644 --- a/app/src/main/java/com/limelight/Game.java +++ b/app/src/main/java/com/limelight/Game.java @@ -1826,18 +1826,41 @@ else if (event.getAction() == MotionEvent.ACTION_UP) { // 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) { - if (prefConfig.absoluteMouseMode) { - // NB: view may be null, but we can unconditionally use streamView because we don't need to adjust - // relative axis deltas for the position of the streamView within the parent's coordinate system. - conn.sendMouseMoveAsMousePosition(deltaX, deltaY, (short)streamView.getWidth(), (short)streamView.getHeight()); + float rawX = inputCaptureProvider.getRelativeAxisX(event); + float rawY = inputCaptureProvider.getRelativeAxisY(event); + + // Some keyboard trackpads (e.g. Samsung Galaxy Tab S11 Ultra Pro Keyboard) + // report AXIS_RELATIVE_X/Y rotated 90° clockwise: the driver sends physical-Y + // as AXIS_RELATIVE_X and physical-X as AXIS_RELATIVE_Y. Correcting means + // reading Y as X and negating the new Y. + if (prefConfig.rotateTouchpadAxes && eventSource == InputDevice.SOURCE_TOUCHPAD) { + float tmp = rawX; + rawX = rawY; + rawY = -tmp; + } + + // Samsung keyboard trackpads send two-finger scroll as ACTION_MOVE with + // getPointerCount() == 2, using the same REL_X/REL_Y axes as cursor movement + // rather than ACTION_SCROLL with AXIS_VSCROLL/HSCROLL. + if (eventSource == InputDevice.SOURCE_TOUCHPAD && event.getPointerCount() == 2) { + if (rawX != 0 || rawY != 0) { + int scrollDir = prefConfig.naturalScroll ? 1 : -1; + conn.sendMouseHighResScroll((short)(rawY * 10 * scrollDir)); + conn.sendMouseHighResHScroll((short)(-rawX * 10 * scrollDir)); } - else { - conn.sendMouseMove(deltaX, deltaY); + } else { + short deltaX = (short) rawX; + short deltaY = (short) rawY; + + if (deltaX != 0 || deltaY != 0) { + if (prefConfig.absoluteMouseMode) { + // NB: view may be null, but we can unconditionally use streamView because we don't need to adjust + // relative axis deltas for the position of the streamView within the parent's coordinate system. + conn.sendMouseMoveAsMousePosition(deltaX, deltaY, (short)streamView.getWidth(), (short)streamView.getHeight()); + } + else { + conn.sendMouseMove(deltaX, deltaY); + } } } } @@ -1878,8 +1901,9 @@ else if (view != null) { if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) { // Send the vertical scroll packet - conn.sendMouseHighResScroll((short)(event.getAxisValue(MotionEvent.AXIS_VSCROLL) * 120)); - conn.sendMouseHighResHScroll((short)(event.getAxisValue(MotionEvent.AXIS_HSCROLL) * 120)); + int scrollDir = prefConfig.naturalScroll ? -1 : 1; + conn.sendMouseHighResScroll((short)(event.getAxisValue(MotionEvent.AXIS_VSCROLL) * 120 * scrollDir)); + conn.sendMouseHighResHScroll((short)(event.getAxisValue(MotionEvent.AXIS_HSCROLL) * 120 * scrollDir)); } if ((changedButtons & MotionEvent.BUTTON_PRIMARY) != 0) { diff --git a/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java b/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java index 8ed01e3610..920e62cbac 100644 --- a/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java +++ b/app/src/main/java/com/limelight/preferences/PreferenceConfiguration.java @@ -68,6 +68,8 @@ public enum AnalogStickForScrolling { private static final String GAMEPAD_TOUCHPAD_AS_MOUSE_PREF_STRING = "checkbox_gamepad_touchpad_as_mouse"; private static final String GAMEPAD_MOTION_SENSORS_PREF_STRING = "checkbox_gamepad_motion_sensors"; private static final String GAMEPAD_MOTION_FALLBACK_PREF_STRING = "checkbox_gamepad_motion_fallback"; + private static final String ROTATE_TOUCHPAD_AXES_PREF_STRING = "checkbox_rotate_touchpad_axes"; + private static final String NATURAL_SCROLL_PREF_STRING = "checkbox_natural_scroll"; static final String DEFAULT_RESOLUTION = "1280x720"; static final String DEFAULT_FPS = "60"; @@ -108,6 +110,8 @@ public enum AnalogStickForScrolling { private static final boolean DEFAULT_GAMEPAD_TOUCHPAD_AS_MOUSE = false; private static final boolean DEFAULT_GAMEPAD_MOTION_SENSORS = true; private static final boolean DEFAULT_GAMEPAD_MOTION_FALLBACK = false; + private static final boolean DEFAULT_ROTATE_TOUCHPAD_AXES = false; + private static final boolean DEFAULT_NATURAL_SCROLL = false; public static final int FRAME_PACING_MIN_LATENCY = 0; public static final int FRAME_PACING_BALANCED = 1; @@ -155,6 +159,8 @@ public enum AnalogStickForScrolling { public boolean gamepadMotionSensors; public boolean gamepadTouchpadAsMouse; public boolean gamepadMotionSensorsFallbackToDevice; + public boolean rotateTouchpadAxes; + public boolean naturalScroll; public static boolean isNativeResolution(int width, int height) { // It's not a native resolution if it matches an existing resolution option @@ -601,6 +607,8 @@ else if (audioConfig.equals("51")) { config.gamepadTouchpadAsMouse = prefs.getBoolean(GAMEPAD_TOUCHPAD_AS_MOUSE_PREF_STRING, DEFAULT_GAMEPAD_TOUCHPAD_AS_MOUSE); config.gamepadMotionSensors = prefs.getBoolean(GAMEPAD_MOTION_SENSORS_PREF_STRING, DEFAULT_GAMEPAD_MOTION_SENSORS); config.gamepadMotionSensorsFallbackToDevice = prefs.getBoolean(GAMEPAD_MOTION_FALLBACK_PREF_STRING, DEFAULT_GAMEPAD_MOTION_FALLBACK); + config.rotateTouchpadAxes = prefs.getBoolean(ROTATE_TOUCHPAD_AXES_PREF_STRING, DEFAULT_ROTATE_TOUCHPAD_AXES); + config.naturalScroll = prefs.getBoolean(NATURAL_SCROLL_PREF_STRING, DEFAULT_NATURAL_SCROLL); return config; } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9cc2215aaa..c0f4ed4d1f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -199,6 +199,10 @@ If enabled, the touchscreen acts like a trackpad. If disabled, the touchscreen directly controls the mouse cursor. Remote desktop mouse mode This can make mouse acceleration behave more naturally for remote desktop usage, but it is incompatible with many games. + Rotate external trackpad axes + Enable if your keyboard trackpad moves the cursor in the wrong direction (e.g. up/down movement causes left/right movement). Fixes devices like the Samsung Galaxy Tab Pro Keyboard. + Invert scroll direction + Reverses the scroll direction for trackpad and mouse scroll wheel. Enable back and forward mouse buttons Enabling this option may break right clicking on some buggy devices diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 5b09082b35..3bf0ab9cbf 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -144,6 +144,16 @@ android:title="@string/title_checkbox_absolute_mouse_mode" android:summary="@string/summary_checkbox_absolute_mouse_mode" android:defaultValue="false" /> + +