From e05ca10fbcb3b652f1c159259b4cb6fe81b0c50f Mon Sep 17 00:00:00 2001 From: AlphaBs Date: Fri, 22 Mar 2024 02:37:19 +0900 Subject: [PATCH 1/8] Adding Samsung Galaxy Tab touchpad gestures tap, long tap, two-finger tap, two-finger scroll tested on SM-X800, with EFDT970 --- .../termux/x11/input/TouchInputHandler.java | 46 +++++++++++++------ 1 file changed, 32 insertions(+), 14 deletions(-) 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 bf70a0c42..7cf3ea33e 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -21,6 +21,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import android.util.Log; + /** * This class is responsible for handling Touch input from the user. Touch events which manipulate * the local canvas are handled in this class and any input which should be sent to the remote host @@ -553,6 +555,7 @@ private class DexListener extends GestureDetector.SimpleOnGestureListener { private final GestureDetector mScroller; private int savedBS = 0; private int currentBS = 0; + private boolean onTap = false; private boolean mIsDragging = false; private boolean mIsScrolling = false; DexListener(Context ctx) { @@ -561,6 +564,12 @@ private class DexListener extends GestureDetector.SimpleOnGestureListener { private final Handler handler = new Handler(); private final Runnable mouseDownRunnable = () -> mInjector.sendMouseEvent(mRenderData.getCursorPosition(), InputStub.BUTTON_LEFT, true, false); + private final int[][] buttons = { + {MotionEvent.BUTTON_PRIMARY, InputStub.BUTTON_LEFT}, + {MotionEvent.BUTTON_TERTIARY, InputStub.BUTTON_MIDDLE}, + {MotionEvent.BUTTON_SECONDARY, InputStub.BUTTON_RIGHT} + }; + boolean isMouseButtonChanged(int mask) { return (savedBS & mask) != (currentBS & mask); } @@ -569,15 +578,17 @@ boolean mouseButtonDown(int mask) { return ((currentBS & mask) != 0); } - void checkButtons(MotionEvent e) { + boolean checkButtons(MotionEvent e) { + boolean isHandled = false; currentBS = e.getButtonState(); - if (isMouseButtonChanged(MotionEvent.BUTTON_PRIMARY)) - mInjector.sendMouseEvent(mRenderData.getCursorPosition(), InputStub.BUTTON_LEFT, mouseButtonDown(MotionEvent.BUTTON_PRIMARY), false); - if (isMouseButtonChanged(MotionEvent.BUTTON_TERTIARY)) - mInjector.sendMouseEvent(mRenderData.getCursorPosition(), InputStub.BUTTON_MIDDLE, mouseButtonDown(MotionEvent.BUTTON_TERTIARY), false); - if (isMouseButtonChanged(MotionEvent.BUTTON_SECONDARY)) - mInjector.sendMouseEvent(mRenderData.getCursorPosition(), InputStub.BUTTON_RIGHT, mouseButtonDown(MotionEvent.BUTTON_SECONDARY), false); + for (int[] button: buttons) { + if (isMouseButtonChanged(button[0])) { + mInjector.sendMouseEvent(mRenderData.getCursorPosition(), button[1], mouseButtonDown(button[0]), false); + isHandled = true; + } + } savedBS = currentBS; + return isHandled; } private boolean hasFlags(MotionEvent e, int flags) { @@ -585,13 +596,14 @@ private boolean hasFlags(MotionEvent e, int flags) { } boolean onTouch(@SuppressWarnings("unused") View v, MotionEvent e) { + Log.d("TX11Touch", "action: " + MotionEvent.actionToString(e.getActionMasked()) + ", flags: " + Integer.toHexString(e.getFlags()) + ", state: " + e.getButtonState() + ", class: " + e.getClassification() + ", pos: " + e.getX() + ", " + e.getY()); switch(e.getActionMasked()) { case MotionEvent.ACTION_BUTTON_PRESS: case MotionEvent.ACTION_BUTTON_RELEASE: + onTap = false; mScroller.onGenericMotionEvent(e); handler.removeCallbacks(mouseDownRunnable); mIsDragging = false; - checkButtons(e); return true; case MotionEvent.ACTION_HOVER_MOVE: { @@ -601,8 +613,11 @@ boolean onTouch(@SuppressWarnings("unused") View v, MotionEvent e) { return true; } case MotionEvent.ACTION_DOWN: - checkButtons(e); - if (hasFlags(e, 0x14000000)) { + if (!checkButtons(e)) { + onTap = true; + mInjector.sendMouseEvent(mRenderData.getCursorPosition(), InputStub.BUTTON_LEFT, true, false); + } + if (hasFlags(e, 0x10000000)) { mIsScrolling = true; mScroller.onTouchEvent(e); } else if (hasFlags(e, 0x4000000)) { @@ -611,8 +626,11 @@ boolean onTouch(@SuppressWarnings("unused") View v, MotionEvent e) { } return true; case MotionEvent.ACTION_UP: - checkButtons(e); - if (hasFlags(e, 0x14000000)) { + if (!checkButtons(e) && onTap) { + mInjector.sendMouseEvent(mRenderData.getCursorPosition(), InputStub.BUTTON_LEFT, false, false); + onTap = false; + } + if (hasFlags(e, 0x10000000)) { mScroller.onTouchEvent(e); mIsScrolling = false; } @@ -623,9 +641,9 @@ else if (hasFlags(e, 0x4000000)) { return true; case MotionEvent.ACTION_MOVE: - if (mIsScrolling && hasFlags(e, 0x14000000)) + if (mIsScrolling && hasFlags(e, 0x10000000)) mScroller.onTouchEvent(e); - else if (mIsDragging && hasFlags(e, 0x4000000)) { + else if ((mIsDragging && hasFlags(e, 0x4000000)) || onTap) { float scaledX = e.getX() * mRenderData.scale.x, scaledY = e.getY() * mRenderData.scale.y; if (mRenderData.setCursorPosition(scaledX, scaledY)) mInjector.sendCursorMove(scaledX, scaledY, false); From 20c6fea8eed1a35c5db92745fa416b6d7904bd54 Mon Sep 17 00:00:00 2001 From: AlphaBs Date: Fri, 22 Mar 2024 12:46:53 +0900 Subject: [PATCH 2/8] Fixing DexListener: two-finger scroll gesture should not send mousedown / mouseup event --- .../termux/x11/input/TouchInputHandler.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) 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 7cf3ea33e..c781fe228 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -596,7 +596,7 @@ private boolean hasFlags(MotionEvent e, int flags) { } boolean onTouch(@SuppressWarnings("unused") View v, MotionEvent e) { - Log.d("TX11Touch", "action: " + MotionEvent.actionToString(e.getActionMasked()) + ", flags: " + Integer.toHexString(e.getFlags()) + ", state: " + e.getButtonState() + ", class: " + e.getClassification() + ", pos: " + e.getX() + ", " + e.getY()); + boolean isButtonHandled; switch(e.getActionMasked()) { case MotionEvent.ACTION_BUTTON_PRESS: case MotionEvent.ACTION_BUTTON_RELEASE: @@ -613,23 +613,20 @@ boolean onTouch(@SuppressWarnings("unused") View v, MotionEvent e) { return true; } case MotionEvent.ACTION_DOWN: - if (!checkButtons(e)) { - onTap = true; - mInjector.sendMouseEvent(mRenderData.getCursorPosition(), InputStub.BUTTON_LEFT, true, false); - } + isButtonHandled = checkButtons(e); if (hasFlags(e, 0x10000000)) { mIsScrolling = true; mScroller.onTouchEvent(e); } else if (hasFlags(e, 0x4000000)) { mIsDragging = true; handler.postDelayed(mouseDownRunnable, 0); + } else if (!isButtonHandled) { + onTap = true; + mInjector.sendMouseEvent(mRenderData.getCursorPosition(), InputStub.BUTTON_LEFT, true, false); } return true; case MotionEvent.ACTION_UP: - if (!checkButtons(e) && onTap) { - mInjector.sendMouseEvent(mRenderData.getCursorPosition(), InputStub.BUTTON_LEFT, false, false); - onTap = false; - } + isButtonHandled = checkButtons(e); if (hasFlags(e, 0x10000000)) { mScroller.onTouchEvent(e); mIsScrolling = false; @@ -638,7 +635,10 @@ else if (hasFlags(e, 0x4000000)) { mInjector.sendMouseEvent(mRenderData.getCursorPosition(), InputStub.BUTTON_LEFT, false, false); mIsDragging = false; } - + else if (!isButtonHandled && onTap) { + mInjector.sendMouseEvent(mRenderData.getCursorPosition(), InputStub.BUTTON_LEFT, false, false); + onTap = false; + } return true; case MotionEvent.ACTION_MOVE: if (mIsScrolling && hasFlags(e, 0x10000000)) From 4d522b0ea7926c863bd416756beca56ad6a4c064 Mon Sep 17 00:00:00 2001 From: AlphaBs Date: Fri, 22 Mar 2024 12:55:34 +0900 Subject: [PATCH 3/8] Fixing DexListener: reset all states when the user removes their hand from the trackpad --- .../main/java/com/termux/x11/input/TouchInputHandler.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 c781fe228..f10739005 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -600,9 +600,9 @@ boolean onTouch(@SuppressWarnings("unused") View v, MotionEvent e) { switch(e.getActionMasked()) { case MotionEvent.ACTION_BUTTON_PRESS: case MotionEvent.ACTION_BUTTON_RELEASE: - onTap = false; mScroller.onGenericMotionEvent(e); handler.removeCallbacks(mouseDownRunnable); + onTap = false; mIsDragging = false; checkButtons(e); return true; @@ -649,6 +649,12 @@ else if ((mIsDragging && hasFlags(e, 0x4000000)) || onTap) { mInjector.sendCursorMove(scaledX, scaledY, false); } return true; + case MotionEvent.ACTION_HOVER_EXIT: // when the user removes their hand from the trackpad, all states should be reset + case MotionEvent.ACTION_CANCEL: + onTap = false; + mIsScrolling = false; + mIsDragging = false; + return true; } return false; } From 92d21425648ad9a24408d88feb682a7e16b1c820 Mon Sep 17 00:00:00 2001 From: AlphaBs Date: Fri, 22 Mar 2024 19:54:29 +0900 Subject: [PATCH 4/8] Fixing DexListener: detect scrolling by getClassification() --- .../java/com/termux/x11/input/TouchInputHandler.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) 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 f10739005..057f10b02 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -595,6 +595,10 @@ private boolean hasFlags(MotionEvent e, int flags) { return (e.getFlags() & flags) == flags; } + private boolean isScrollingEvent(MotionEvent e) { + return hasFlags(e, 0x14000000) || e.getClassification() == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE; + } + boolean onTouch(@SuppressWarnings("unused") View v, MotionEvent e) { boolean isButtonHandled; switch(e.getActionMasked()) { @@ -614,7 +618,7 @@ boolean onTouch(@SuppressWarnings("unused") View v, MotionEvent e) { } case MotionEvent.ACTION_DOWN: isButtonHandled = checkButtons(e); - if (hasFlags(e, 0x10000000)) { + if (isScrollingEvent(e)) { mIsScrolling = true; mScroller.onTouchEvent(e); } else if (hasFlags(e, 0x4000000)) { @@ -627,7 +631,7 @@ boolean onTouch(@SuppressWarnings("unused") View v, MotionEvent e) { return true; case MotionEvent.ACTION_UP: isButtonHandled = checkButtons(e); - if (hasFlags(e, 0x10000000)) { + if (isScrollingEvent(e)) { mScroller.onTouchEvent(e); mIsScrolling = false; } @@ -641,7 +645,7 @@ else if (!isButtonHandled && onTap) { } return true; case MotionEvent.ACTION_MOVE: - if (mIsScrolling && hasFlags(e, 0x10000000)) + if (mIsScrolling && isScrollingEvent(e)) mScroller.onTouchEvent(e); else if ((mIsDragging && hasFlags(e, 0x4000000)) || onTap) { float scaledX = e.getX() * mRenderData.scale.x, scaledY = e.getY() * mRenderData.scale.y; From 339cbd4eb49f55829b40f4d009742369caa2f1c9 Mon Sep 17 00:00:00 2001 From: AlphaBs Date: Fri, 22 Mar 2024 20:46:18 +0900 Subject: [PATCH 5/8] Update app/src/main/java/com/termux/x11/input/TouchInputHandler.java Co-authored-by: Twaik Yont --- app/src/main/java/com/termux/x11/input/TouchInputHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 057f10b02..88bbe3e57 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -596,7 +596,7 @@ private boolean hasFlags(MotionEvent e, int flags) { } private boolean isScrollingEvent(MotionEvent e) { - return hasFlags(e, 0x14000000) || e.getClassification() == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE; + return hasFlags(e, 0x14000000) || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && e.getClassification() == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE); } boolean onTouch(@SuppressWarnings("unused") View v, MotionEvent e) { From 4eafcba554bbf4e7f8dec4914cf109abe8feb2d1 Mon Sep 17 00:00:00 2001 From: AlphaBs Date: Fri, 22 Mar 2024 20:53:07 +0900 Subject: [PATCH 6/8] Add missing imports --- app/src/main/java/com/termux/x11/input/TouchInputHandler.java | 1 + 1 file changed, 1 insertion(+) 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 88bbe3e57..19ca099ad 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -8,6 +8,7 @@ import android.content.Context; import android.graphics.PointF; import android.os.Handler; +import android.os.Build; import android.view.GestureDetector; import android.view.InputDevice; import android.view.KeyEvent; From aac523d63e9e488855f5d6121bf1ca57a42f6caf Mon Sep 17 00:00:00 2001 From: AlphaBs Date: Fri, 22 Mar 2024 22:29:38 +0900 Subject: [PATCH 7/8] Update app/src/main/java/com/termux/x11/input/TouchInputHandler.java Co-authored-by: Twaik Yont --- app/src/main/java/com/termux/x11/input/TouchInputHandler.java | 1 - 1 file changed, 1 deletion(-) 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 19ca099ad..3d40bd980 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -22,7 +22,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import android.util.Log; /** * This class is responsible for handling Touch input from the user. Touch events which manipulate From 84e2e3809e80645637b28e3df207d476d04bcd23 Mon Sep 17 00:00:00 2001 From: AlphaBs Date: Sun, 24 Mar 2024 19:39:15 +0900 Subject: [PATCH 8/8] support two-finger drag press the left button with one finger and move to drag with the other --- app/src/main/java/com/termux/x11/input/TouchInputHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3d40bd980..d2c725481 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -606,7 +606,7 @@ boolean onTouch(@SuppressWarnings("unused") View v, MotionEvent e) { case MotionEvent.ACTION_BUTTON_RELEASE: mScroller.onGenericMotionEvent(e); handler.removeCallbacks(mouseDownRunnable); - onTap = false; + onTap = e.getActionMasked() == MotionEvent.ACTION_BUTTON_PRESS; mIsDragging = false; checkButtons(e); return true;