diff --git a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java index 0da29c634..3b9b6c517 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -14,10 +14,12 @@ import android.content.Intent; import android.content.SharedPreferences; import android.graphics.PointF; +import android.hardware.display.DisplayManager; import android.hardware.input.InputManager; import android.os.Handler; import android.os.Build; import android.util.DisplayMetrics; +import android.view.Display; import android.view.GestureDetector; import android.view.InputDevice; import android.view.KeyEvent; @@ -63,13 +65,15 @@ public class TouchInputHandler { int TOUCH = 3; } - @IntDef({CapturedPointerTransformation.NONE, CapturedPointerTransformation.CLOCKWISE, CapturedPointerTransformation.COUNTER_CLOCKWISE, CapturedPointerTransformation.UPSIDE_DOWN}) + @IntDef({CapturedPointerTransformation.AUTO, CapturedPointerTransformation.NONE, CapturedPointerTransformation.COUNTER_CLOCKWISE, CapturedPointerTransformation.UPSIDE_DOWN, CapturedPointerTransformation.CLOCKWISE}) @Retention(RetentionPolicy.SOURCE) public @interface CapturedPointerTransformation { + // values correspond to transformation needed given getRotation(), e.g. getRotation() = 1 requires counter-clockwise transformation + int AUTO = -1; int NONE = 0; - int CLOCKWISE = 1; - int COUNTER_CLOCKWISE = 2; - int UPSIDE_DOWN = 3; + int COUNTER_CLOCKWISE = 1; + int UPSIDE_DOWN = 2; + int CLOCKWISE = 3; } private final RenderData mRenderData; @@ -128,8 +132,40 @@ public class TouchInputHandler { * is performing a drag operation. */ private boolean mIsDragging; + private static DisplayManager mDisplayManager; + private static int mDisplayRotation; + private static final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { + @Override + public void onDisplayAdded(int displayId) { + mDisplayRotation = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getRotation() % 4; + } - @CapturedPointerTransformation int capturedPointerTransformation = CapturedPointerTransformation.NONE; + @Override + public void onDisplayRemoved(int displayId) { + mDisplayRotation = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getRotation() % 4; + } + + @Override + public void onDisplayChanged(int displayId) { + mDisplayRotation = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getRotation() % 4; + } + }; + + @CapturedPointerTransformation static int capturedPointerTransformation = CapturedPointerTransformation.NONE; + private final int[][] buttons = { + {MotionEvent.BUTTON_PRIMARY, InputStub.BUTTON_LEFT}, + {MotionEvent.BUTTON_TERTIARY, InputStub.BUTTON_MIDDLE}, + {MotionEvent.BUTTON_SECONDARY, InputStub.BUTTON_RIGHT} + }; + private int savedBS = 0; + private int currentBS = 0; + boolean isMouseButtonChanged(int mask) { + return (savedBS & mask) != (currentBS & mask); + } + + boolean mouseButtonDown(int mask) { + return ((currentBS & mask) != 0); + } private TouchInputHandler(MainActivity activity, RenderData renderData, final InputEventSender injector, boolean isTouchpad) { @@ -139,6 +175,11 @@ private TouchInputHandler(MainActivity activity, RenderData renderData, mRenderData = renderData != null ? renderData :new RenderData(); mInjector = injector; mActivity = activity; + if (mDisplayManager == null) { + mDisplayManager = (DisplayManager) mActivity.getSystemService(Context.DISPLAY_SERVICE); + mDisplayRotation = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY).getRotation() % 4; + mDisplayManager.registerDisplayListener(mDisplayListener, null); + } GestureListener listener = new GestureListener(); mScroller = new GestureDetector(/*desktop*/ activity, listener, null, false); @@ -227,6 +268,13 @@ boolean isDexEvent(MotionEvent event) { } public boolean handleTouchEvent(View view0, View view, MotionEvent event) { + // Regular touchpads and Dex touchpad (in captured mode) send events as finger too, + // but they should be handled as touchscreens with trackpad mode. + if (event.getToolType(event.getActionIndex()) == MotionEvent.TOOL_TYPE_FINGER && + mTouchpadHandler != null && (event.getSource() & InputDevice.SOURCE_TOUCHPAD) == InputDevice.SOURCE_TOUCHPAD) { + return mTouchpadHandler.handleTouchEvent(view0, view, event); + } + if (view0 != view) { int[] view0Location = new int[2]; int[] viewLocation = new int[2]; @@ -252,21 +300,14 @@ public boolean handleTouchEvent(View view0, View view, MotionEvent event) { if (!isDexEvent(event) && (event.getToolType(event.getActionIndex()) == MotionEvent.TOOL_TYPE_MOUSE || (event.getSource() & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) - || (event.getSource() & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE - || (event.getPointerCount() == 1 && mTouchpadHandler == null - && (event.getSource() & InputDevice.SOURCE_TOUCHPAD) == InputDevice.SOURCE_TOUCHPAD)) + || (event.getSource() & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE) return mHMListener.onTouch(view, event); if (event.getToolType(event.getActionIndex()) == MotionEvent.TOOL_TYPE_FINGER) { - // Dex touchpad sends events as finger, but it should be considered as a mouse. + // Dex touchpad (in non-captured mode) sends events as finger, but it should be considered as a mouse. if (isDexEvent(event) && mDexListener.onTouch(view, event)) return true; - // Regular touchpads and Dex touchpad send events as finger too, - // but they should be handled as touchscreens with trackpad mode. - if (mTouchpadHandler != null && (event.getSource() & InputDevice.SOURCE_TOUCHPAD) == InputDevice.SOURCE_TOUCHPAD) - return mTouchpadHandler.handleTouchEvent(view, view, event); - // Give the underlying input strategy a chance to observe the current motion event before // passing it to the gesture detectors. This allows the input strategy to react to the // event or save the payload for use in recreating the gesture remotely. @@ -281,6 +322,14 @@ public boolean handleTouchEvent(View view0, View view, MotionEvent event) { mTapDetector.onTouchEvent(event); mSwipePinchDetector.onTouchEvent(event); + // For hardware touchpad in DeX (captured mode), handle physical click buttons + if ((event.getSource() & InputDevice.SOURCE_TOUCHPAD) == InputDevice.SOURCE_TOUCHPAD) { + currentBS = event.getButtonState(); + for (int[] button: buttons) + if (isMouseButtonChanged(button[0])) + mInjector.sendMouseEvent(null, button[1], mouseButtonDown(button[0]), true); + savedBS = currentBS; + } switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: mSuppressCursorMovement = false; @@ -339,7 +388,9 @@ public void handleHostSizeChanged(int w, int h) { } public void setInputMode(@InputMode int inputMode) { - if (inputMode == InputMode.TOUCH) + if (mTouchpadHandler == null) + mInputStrategy = new InputStrategyInterface.TrackpadInputStrategy(mInjector); + else if (inputMode == InputMode.TOUCH) mInputStrategy = new InputStrategyInterface.NullInputStrategy(); else if (inputMode == InputMode.SIMULATED_TOUCH) mInputStrategy = new InputStrategyInterface.SimulatedTouchInputStrategy(mRenderData, mInjector, mActivity); @@ -396,6 +447,9 @@ public void reloadPreferences(Prefs p) { case "ud": capturedPointerTransformation = CapturedPointerTransformation.UPSIDE_DOWN; break; + case "at": + capturedPointerTransformation = CapturedPointerTransformation.AUTO; + break; default: capturedPointerTransformation = CapturedPointerTransformation.NONE; } @@ -490,6 +544,28 @@ private class GestureListener extends GestureDetector.SimpleOnGestureListener public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { int pointerCount = e2.getPointerCount(); + // For captured touchpad pointer: + // Automatic (for touchpad) mode is needed because touchpads ignore screen orientation and report physical X and Y + if ((e2.getSource() & InputDevice.SOURCE_TOUCHPAD) == InputDevice.SOURCE_TOUCHPAD + && mInputStrategy instanceof InputStrategyInterface.TrackpadInputStrategy) { + float temp; + int transform = capturedPointerTransformation == CapturedPointerTransformation.AUTO ? + mDisplayRotation : capturedPointerTransformation; + switch (transform) { + case CapturedPointerTransformation.NONE: + break; + case CapturedPointerTransformation.CLOCKWISE: + temp = distanceX; distanceX = -distanceY; distanceY = temp; break; + case CapturedPointerTransformation.COUNTER_CLOCKWISE: + temp = distanceX; distanceX = distanceY; distanceY = -temp; break; + case CapturedPointerTransformation.UPSIDE_DOWN: + distanceX = -distanceX; distanceY = -distanceY; break; + } + distanceX *= mInjector.capturedPointerSpeedFactor; + distanceY *= mInjector.capturedPointerSpeedFactor; + } + + if (pointerCount >= 3 && !mSwipeCompleted) { // Note that distance values are reversed. For example, dragging a finger in the // direction of increasing Y coordinate (downwards) results in distanceY being diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 54bf05fe5..a10b56ae0 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -15,12 +15,14 @@ Clockwise Counter clockwise Upside down + Automatic (for touchpad) no c cc ud + at native