From f368b663f1fd245fff1736569eb40fbd463f001a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 07:38:50 +0300 Subject: [PATCH 001/107] Bump org.jetbrains.kotlin:kotlin-stdlib-jdk8 from 1.9.23 to 1.9.24 (#624) Bumps [org.jetbrains.kotlin:kotlin-stdlib-jdk8](https://github.com/JetBrains/kotlin) from 1.9.23 to 1.9.24. - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/v1.9.24/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.9.23...v1.9.24) [no ci] --- updated-dependencies: - dependency-name: org.jetbrains.kotlin:kotlin-stdlib-jdk8 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 5f70921ac..61eaf9fa1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -46,7 +46,7 @@ android { dependencies { //noinspection DifferentStdlibGradleVersion - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.23" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.24" implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0' implementation 'androidx.preference:preference:1.2.1' compileOnly project(':shell-loader:stub') From 6fbd33211c8c60be852bb9f0023f735774399611 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 May 2024 07:39:08 +0300 Subject: [PATCH 002/107] Bump com.android.tools.build:gradle from 8.3.2 to 8.4.0 (#619) Bumps com.android.tools.build:gradle from 8.3.2 to 8.4.0. [no ci] --- updated-dependencies: - dependency-name: com.android.tools.build:gradle dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f6796ccd1..a838838e3 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { dependencies { // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files - classpath 'com.android.tools.build:gradle:8.3.2' + classpath 'com.android.tools.build:gradle:8.4.0' } } From 74b17233e1c58e1a628c8a777bc762070769309c Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Wed, 8 May 2024 08:22:10 +0300 Subject: [PATCH 003/107] Unset CLASSPATH when `-xstartup` is used --- app/src/main/cpp/lorie/InitOutput.c | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/cpp/lorie/InitOutput.c b/app/src/main/cpp/lorie/InitOutput.c index 10f311399..a400a54ed 100644 --- a/app/src/main/cpp/lorie/InitOutput.c +++ b/app/src/main/cpp/lorie/InitOutput.c @@ -135,6 +135,7 @@ static void* ddxReadyThread(unused void* cookie) { char DISPLAY[16] = ""; sprintf(DISPLAY, ":%s", display); setenv("DISPLAY", DISPLAY, 1); + unsetenv("CLASSPATH"); execlp("sh", "sh", "-c", xstartup, NULL); dprintf(2, "Failed to start command `sh -c \"%s\"`: %s\n", xstartup, strerror(errno)); abort(); From 778a4bfa0efa5656ec4ae3c51d5edc0abd16d718 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Tue, 14 May 2024 12:09:22 +0300 Subject: [PATCH 004/107] Mention disabling SELinux in readme.md. [no ci] --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e6218c83e..08df0cb84 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,9 @@ If you plan to use the program with chroot or unshare, you must to run it as roo This directory must be accessible from the shell from which you launch termux-x11, i.e. it must be in the same SELinux context, same mount namespace, and so on. Also you must set `XKB_CONFIG_ROOT` environment variable pointing to container's `/usr/share/X11/xkb` directory, otherwise you will have `xkbcomp`-related errors. You can get loader for nightly build from an artifact of [last successful build](https://github.com/termux/termux-x11/actions/workflows/debug_build.yml) +Do not forget to disable SELinux ``` +setenforce 0 export TMPDIR=/path/to/chroot/container/tmp export CLASSPATH=$(/system/bin/pm path com.termux.x11 | cut -d: -f2) /system/bin/app_process / com.termux.x11.CmdEntryPoint :0 From af12d3fe3f33d8d083f3015317a35c0062a7d773 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 May 2024 07:42:08 +0300 Subject: [PATCH 005/107] Bump androidx.lifecycle:lifecycle-viewmodel-ktx from 2.7.0 to 2.8.0 (#632) Bumps androidx.lifecycle:lifecycle-viewmodel-ktx from 2.7.0 to 2.8.0. [no ci] --- updated-dependencies: - dependency-name: androidx.lifecycle:lifecycle-viewmodel-ktx dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 61eaf9fa1..34ae590f5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -47,7 +47,7 @@ android { dependencies { //noinspection DifferentStdlibGradleVersion implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.24" - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0' implementation 'androidx.preference:preference:1.2.1' compileOnly project(':shell-loader:stub') } From 35affb8bedb6ad3b3053e44ea1ee4b22d6681882 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 May 2024 07:42:32 +0300 Subject: [PATCH 006/107] Bump androidx.annotation:annotation from 1.7.1 to 1.8.0 (#633) Bumps androidx.annotation:annotation from 1.7.1 to 1.8.0. [no ci] --- updated-dependencies: - dependency-name: androidx.annotation:annotation dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- shell-loader/stub/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell-loader/stub/build.gradle b/shell-loader/stub/build.gradle index 1f6f894bd..dcbb96414 100644 --- a/shell-loader/stub/build.gradle +++ b/shell-loader/stub/build.gradle @@ -25,5 +25,5 @@ android { } dependencies { - implementation 'androidx.annotation:annotation:1.7.1' + implementation 'androidx.annotation:annotation:1.8.0' } From a67cf082e8c269e1c9f470b2c3cdb8ab6bcbc7e3 Mon Sep 17 00:00:00 2001 From: Ronald Y <46513942+knyipab@users.noreply.github.com> Date: Thu, 16 May 2024 15:10:24 +0800 Subject: [PATCH 007/107] Introducing the preference controlling EK bar opacity. Co-authored-by: Twaik Yont --- app/src/main/java/com/termux/x11/LoriePreferences.java | 5 +++++ app/src/main/java/com/termux/x11/MainActivity.java | 2 ++ app/src/main/res/xml/preferences.xml | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/app/src/main/java/com/termux/x11/LoriePreferences.java b/app/src/main/java/com/termux/x11/LoriePreferences.java index 4cbb39537..bfe302254 100644 --- a/app/src/main/java/com/termux/x11/LoriePreferences.java +++ b/app/src/main/java/com/termux/x11/LoriePreferences.java @@ -141,6 +141,7 @@ void updatePreferencesLayout() { findPreference("dexMetaKeyCapture").setVisible(false); SeekBarPreference scalePreference = findPreference("displayScale"); SeekBarPreference capturedPointerSpeedFactor = findPreference("capturedPointerSpeedFactor"); + SeekBarPreference opacityEKBar = findPreference("opacityEKBar"); scalePreference.setMin(30); scalePreference.setMax(200); scalePreference.setSeekBarIncrement(10); @@ -149,6 +150,10 @@ void updatePreferencesLayout() { capturedPointerSpeedFactor.setMax(200); capturedPointerSpeedFactor.setSeekBarIncrement(1); capturedPointerSpeedFactor.setShowSeekBarValue(true); + opacityEKBar.setMin(10); + opacityEKBar.setMax(100); + opacityEKBar.setSeekBarIncrement(1); + opacityEKBar.setShowSeekBarValue(true); switch (p.getString("displayResolutionMode", "native")) { case "scaled": diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index b375c332a..f68f92501 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -556,6 +556,8 @@ void onPreferencesChanged(String key) { buttons.setVisibility(View.GONE); } + getTerminalToolbarViewPager().setAlpha(((float) p.getInt("opacityEKBar", 100))/100); + lorieView.requestLayout(); lorieView.invalidate(); } diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 536e8784f..f500bc09f 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -166,6 +166,12 @@ android:defaultValue="false" android:key="filterOutWinkey" /> + + From 5ec435f0c3c9027689a245af23dd3c37fceb98c1 Mon Sep 17 00:00:00 2001 From: Ronald Y <46513942+knyipab@users.noreply.github.com> Date: Thu, 16 May 2024 15:13:55 +0800 Subject: [PATCH 008/107] Introducing the option to adjust display height for EK bar Co-authored-by: Twaik Yont --- app/src/main/java/com/termux/x11/MainActivity.java | 3 ++- app/src/main/res/xml/preferences.xml | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index f68f92501..0c6f768d6 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -615,6 +615,7 @@ private void setTerminalToolbarView() { (mExtraKeys.getExtraKeysInfo() == null ? 0 : mExtraKeys.getExtraKeysInfo().getMatrix().length)); terminalToolbarViewPager.setLayoutParams(layoutParams); } + frm.setPadding(0, 0, 0, preferences.getBoolean("adjustHeightForEK", false) && terminalToolbarViewPager.getVisibility() == View.VISIBLE ? terminalToolbarViewPager.getHeight() : 0); }, 200); } @@ -633,6 +634,7 @@ public void toggleExtraKeys(boolean visible, boolean saveState) { parent.removeView(pager); parent.addView(pager, 0); } + frm.setPadding(0, 0, 0, preferences.getBoolean("adjustHeightForEK", false) && show ? pager.getHeight() : 0); if (enabled && saveState) { SharedPreferences.Editor edit = preferences.edit(); @@ -803,7 +805,6 @@ public void onUserLeaveHint() { public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, @NonNull Configuration newConfig) { toggleExtraKeys(!isInPictureInPictureMode, false); - frm.setPadding(0, 0, 0, 0); super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); } diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index f500bc09f..eee1f59ff 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -64,6 +64,12 @@ android:title="Keep Screen On" android:defaultValue="true" android:key="keepScreenOn" /> + + Date: Thu, 16 May 2024 12:52:02 +0300 Subject: [PATCH 009/107] Fix simulated touchscreen single tap behaviour --- app/src/main/cpp/lorie/android.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/cpp/lorie/android.c b/app/src/main/cpp/lorie/android.c index 537a5bc84..f4eabf474 100644 --- a/app/src/main/cpp/lorie/android.c +++ b/app/src/main/cpp/lorie/android.c @@ -339,7 +339,7 @@ void handleLorieEvents(int fd, __unused int ready, __unused void *ignored) { case 1: // BUTTON_LEFT case 2: // BUTTON_MIDDLE case 3: // BUTTON_RIGHT - QueuePointerEvents(lorieMouse, e.mouse.down ? ButtonPress : ButtonRelease, e.mouse.detail, 0, &mask); + QueuePointerEvents(lorieMouse, e.mouse.down ? ButtonPress : ButtonRelease, e.mouse.detail, POINTER_RELATIVE, NULL); break; case 4: // BUTTON_SCROLL if (e.mouse.x) { From 8f16d0d8cb42d7a366a4ce2fec76196f3748eea4 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 16 May 2024 15:12:00 +0300 Subject: [PATCH 010/107] Disable hold-to-move while double-tap-to-move enabled Fixes #626 --- .../com/termux/x11/input/InputStrategyInterface.java | 11 +++++++---- .../java/com/termux/x11/input/TouchInputHandler.java | 5 +++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/termux/x11/input/InputStrategyInterface.java b/app/src/main/java/com/termux/x11/input/InputStrategyInterface.java index e3a9e55d9..e95ff850a 100644 --- a/app/src/main/java/com/termux/x11/input/InputStrategyInterface.java +++ b/app/src/main/java/com/termux/x11/input/InputStrategyInterface.java @@ -29,7 +29,7 @@ public interface InputStrategyInterface { * @param button The button value for the tap event. * @return A boolean representing whether the event was handled. */ - boolean onPressAndHold(int button); + boolean onPressAndHold(int button, boolean force); /** * Called when a MotionEvent is received. This method allows the input strategy to store or @@ -49,7 +49,7 @@ public interface InputStrategyInterface { class NullInputStrategy implements InputStrategyInterface { @Override public void onTap(int button) {} - @Override public boolean onPressAndHold(int button) { return false; } + @Override public boolean onPressAndHold(int button, boolean force) { return false; } @Override public void onScroll(float distanceX, float distanceY) {} @Override public void onMotionEvent(MotionEvent event) {} } @@ -152,7 +152,7 @@ public void onTap(int button) { } @Override - public boolean onPressAndHold(int button) { + public boolean onPressAndHold(int button, boolean force) { mInjector.sendMouseDown(button, false); mHeldButton = button; return true; @@ -210,7 +210,10 @@ public void onTap(int button) { } @Override - public boolean onPressAndHold(int button) { + public boolean onPressAndHold(int button, boolean force) { + if (mInjector.tapToMove && !force) + return false; + mInjector.sendMouseDown(button, true); mHeldButton = button; return true; 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 d90a805e3..4f2c0184d 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -431,7 +431,8 @@ public boolean onDoubleTapEvent(MotionEvent e) { case MotionEvent.ACTION_DOWN: if (mInjector.tapToMove && mInputStrategy instanceof InputStrategyInterface.TrackpadInputStrategy) { mGestureListenerHandler.removeMessages(InputStub.BUTTON_LEFT); - onLongPress(1, e.getX(), e.getY()); + if (mInputStrategy.onPressAndHold(InputStub.BUTTON_LEFT, true)) + mIsDragging = true; } break; case MotionEvent.ACTION_MOVE: @@ -460,7 +461,7 @@ public void onLongPress(int pointerCount, float x, float y) { moveCursorToScreenPoint(x, y); } - if (mInputStrategy.onPressAndHold(button)) + if (mInputStrategy.onPressAndHold(button, false)) mIsDragging = true; } From 2f26555286ac1c8ab7d5e2718a7be5fc5934915c Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 16 May 2024 15:25:12 +0300 Subject: [PATCH 011/107] Introducing option to disable volume key capturing Fixes #629 --- app/src/main/java/com/termux/x11/LoriePreferences.java | 2 +- app/src/main/java/com/termux/x11/MainActivity.java | 8 ++++++-- app/src/main/res/xml/preferences.xml | 5 +++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/termux/x11/LoriePreferences.java b/app/src/main/java/com/termux/x11/LoriePreferences.java index bfe302254..211e862dc 100644 --- a/app/src/main/java/com/termux/x11/LoriePreferences.java +++ b/app/src/main/java/com/termux/x11/LoriePreferences.java @@ -177,7 +177,7 @@ void updatePreferencesLayout() { findPreference("displayResolutionCustom").setVisible(false); } - findPreference("hideEKOnVolDown").setEnabled(p.getBoolean("showAdditionalKbd", false)); + findPreference("hideEKOnVolDown").setEnabled(p.getBoolean("showAdditionalKbd", false) && p.getBoolean("captureVolumeKeys", true)); findPreference("dexMetaKeyCapture").setEnabled(!p.getBoolean("enableAccessibilityServiceAutomatically", false)); findPreference("enableAccessibilityServiceAutomatically").setEnabled(!p.getBoolean("dexMetaKeyCapture", false)); findPreference("filterOutWinkey").setEnabled(p.getBoolean("enableAccessibilityServiceAutomatically", false)); diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 0c6f768d6..ba33aeb45 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -92,6 +92,7 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo static InputMethodManager inputMethodManager; private boolean mClientConnected = false; private View.OnKeyListener mLorieKeyListener; + boolean captureVolumeKeys = false; private boolean filterOutWinKey = false; private boolean hideEKOnVolDown = false; private boolean toggleIMEUsingBackKey = false; @@ -172,6 +173,9 @@ public void swipeDown() { } }, new InputEventSender(lorieView)); mLorieKeyListener = (v, k, e) -> { + if (!captureVolumeKeys && (k == KEYCODE_VOLUME_DOWN || k == KEYCODE_VOLUME_UP)) + return false; + if (hideEKOnVolDown && k == KEYCODE_VOLUME_DOWN) { if (e.getAction() == ACTION_UP) toggleExtraKeys(); @@ -499,6 +503,7 @@ void onPreferencesChanged(String key) { mInputHandler.setApplyDisplayScaleFactorToTouchpad(p.getBoolean("scaleTouchpad", true)); mInputHandler.setTransformCapturedPointer(p.getString("transformCapturedPointer", "no")); mInputHandler.setCapturedPointerSpeedFactor(((float) p.getInt("capturedPointerSpeedFactor", 100))/100); + captureVolumeKeys = p.getBoolean("captureVolumeKeys", true); if (!p.getBoolean("pointerCapture", false) && lorieView.hasPointerCapture()) lorieView.releasePointerCapture(); @@ -657,8 +662,7 @@ public void toggleExtraKeys() { public boolean handleKey(KeyEvent e) { if (filterOutWinKey && (e.getKeyCode() == KEYCODE_META_LEFT || e.getKeyCode() == KEYCODE_META_RIGHT || e.isMetaPressed())) return false; - mLorieKeyListener.onKey(getLorieView(), e.getKeyCode(), e); - return true; + return mLorieKeyListener.onKey(getLorieView(), e.getKeyCode(), e); } @SuppressLint("ObsoleteSdkInt") diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index eee1f59ff..6dfbbb887 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -132,6 +132,11 @@ android:defaultValue="true" android:key="showAdditionalKbd" /> + + Date: Thu, 16 May 2024 16:32:55 +0300 Subject: [PATCH 012/107] Add an option to use termux-app's additional key bar behaviour. Fixes #604 --- app/src/main/java/com/termux/x11/MainActivity.java | 7 +++++++ .../java/com/termux/x11/utils/TermuxX11ExtraKeys.java | 9 +++++++++ app/src/main/res/xml/preferences.xml | 6 ++++++ 3 files changed, 22 insertions(+) diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index ba33aeb45..6d50653ee 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -96,6 +96,7 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo private boolean filterOutWinKey = false; private boolean hideEKOnVolDown = false; private boolean toggleIMEUsingBackKey = false; + private boolean useTermuxEKBarBehaviour = false; private static final int KEY_BACK = 158; private final BroadcastReceiver receiver = new BroadcastReceiver() { @@ -173,6 +174,7 @@ public void swipeDown() { } }, new InputEventSender(lorieView)); mLorieKeyListener = (v, k, e) -> { + InputDevice dev = e.getDevice(); if (!captureVolumeKeys && (k == KEYCODE_VOLUME_DOWN || k == KEYCODE_VOLUME_UP)) return false; @@ -199,6 +201,10 @@ public void swipeDown() { } } + // Do not steal dedicated buttons from a full external keyboard. + if (useTermuxEKBarBehaviour && mExtraKeys != null && (dev == null || dev.isVirtual())) + mExtraKeys.unsetSpecialKeys(); + return mInputHandler.sendKeyEvent(v, e); }; @@ -538,6 +544,7 @@ void onPreferencesChanged(String key) { KeyInterceptor.shutdown(); hideEKOnVolDown = p.getBoolean("hideEKOnVolDown", false); + useTermuxEKBarBehaviour = p.getBoolean("useTermuxEKBarBehaviour", false); toggleIMEUsingBackKey = p.getBoolean("toggleIMEUsingBackKey", true); int requestedOrientation = p.getBoolean("forceLandscape", false) ? diff --git a/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java b/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java index 8097ee69b..5fcf4df8f 100644 --- a/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java +++ b/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java @@ -122,6 +122,15 @@ protected void onTerminalExtraKeyButtonClick(@SuppressWarnings("unused") View vi } } + public void unsetSpecialKeys() { + if (mExtraKeysView == null) + return; + mExtraKeysView.readSpecialButton(SpecialButton.CTRL, true); + mExtraKeysView.readSpecialButton(SpecialButton.ALT, true); + mExtraKeysView.readSpecialButton(SpecialButton.SHIFT, true); + mExtraKeysView.readSpecialButton(SpecialButton.META, true); + } + @Override public boolean performExtraKeyButtonHapticFeedback(View view, ExtraKeyButton buttonInfo, Button button) { MainActivity.handler.postDelayed(() -> { diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 6dfbbb887..1f06ae68f 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -142,6 +142,12 @@ android:defaultValue="false" android:key="hideEKOnVolDown" /> + + Date: Thu, 16 May 2024 18:05:15 +0300 Subject: [PATCH 013/107] Fix termux-like EK bar behaviour --- app/src/main/java/com/termux/x11/MainActivity.java | 6 +++++- .../com/termux/x11/utils/TermuxX11ExtraKeys.java | 13 +++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 6d50653ee..5a28e1894 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -175,6 +175,8 @@ public void swipeDown() { }, new InputEventSender(lorieView)); mLorieKeyListener = (v, k, e) -> { InputDevice dev = e.getDevice(); + boolean result; + if (!captureVolumeKeys && (k == KEYCODE_VOLUME_DOWN || k == KEYCODE_VOLUME_UP)) return false; @@ -201,11 +203,13 @@ public void swipeDown() { } } + result = mInputHandler.sendKeyEvent(v, e); + // Do not steal dedicated buttons from a full external keyboard. if (useTermuxEKBarBehaviour && mExtraKeys != null && (dev == null || dev.isVirtual())) mExtraKeys.unsetSpecialKeys(); - return mInputHandler.sendKeyEvent(v, e); + return result; }; lorieParent.setOnTouchListener((v, e) -> mInputHandler.handleTouchEvent(lorieParent, lorieView, e)); diff --git a/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java b/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java index 5fcf4df8f..03988fa98 100644 --- a/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java +++ b/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java @@ -125,10 +125,15 @@ protected void onTerminalExtraKeyButtonClick(@SuppressWarnings("unused") View vi public void unsetSpecialKeys() { if (mExtraKeysView == null) return; - mExtraKeysView.readSpecialButton(SpecialButton.CTRL, true); - mExtraKeysView.readSpecialButton(SpecialButton.ALT, true); - mExtraKeysView.readSpecialButton(SpecialButton.SHIFT, true); - mExtraKeysView.readSpecialButton(SpecialButton.META, true); + + if (Boolean.TRUE.equals(mExtraKeysView.readSpecialButton(SpecialButton.CTRL, true))) + mActivity.getLorieView().sendKeyEvent(0, KeyEvent.KEYCODE_CTRL_LEFT, false); + if (Boolean.TRUE.equals(mExtraKeysView.readSpecialButton(SpecialButton.ALT, true))) + mActivity.getLorieView().sendKeyEvent(0, KeyEvent.KEYCODE_ALT_LEFT, false); + if (Boolean.TRUE.equals(mExtraKeysView.readSpecialButton(SpecialButton.SHIFT, true))) + mActivity.getLorieView().sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, false); + if (Boolean.TRUE.equals(mExtraKeysView.readSpecialButton(SpecialButton.META, true))) + mActivity.getLorieView().sendKeyEvent(0, KeyEvent.KEYCODE_META_LEFT, false); } @Override From fc101564721ab7d90cc00e50d2f88b876c5776f8 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 16 May 2024 18:51:44 +0300 Subject: [PATCH 014/107] Fix layout calculations after fullscreen or cutout options changed. Fixes #623 --- .../java/com/termux/x11/MainActivity.java | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 5a28e1894..f4bb782f2 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -79,7 +79,6 @@ @SuppressWarnings({"deprecation", "unused"}) public class MainActivity extends AppCompatActivity implements View.OnApplyWindowInsetsListener { static final String ACTION_STOP = "com.termux.x11.ACTION_STOP"; - static final String REQUEST_LAUNCH_EXTERNAL_DISPLAY = "request_launch_external_display"; public static Handler handler = new Handler(); FrameLayout frm; @@ -99,6 +98,9 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo private boolean useTermuxEKBarBehaviour = false; private static final int KEY_BACK = 158; + private static boolean oldFullscreen = false; + private static boolean oldHideCutout = false; + private final BroadcastReceiver receiver = new BroadcastReceiver() { @SuppressLint("UnspecifiedRegisterReceiverFlag") @Override @@ -113,7 +115,7 @@ public void onReceive(Context context, Intent intent) { CmdEntryPoint.requestConnection(); Log.v("Lorie", "Disconnected"); - runOnUiThread(() -> clientConnectedStateChanged(false)); //recreate()); //onPreferencesChanged("")); + runOnUiThread(() -> clientConnectedStateChanged(false)); }, 0); onReceiveConnection(); @@ -153,7 +155,10 @@ protected void onCreate(Bundle savedInstanceState) { e.putString("touchMode", "1"); e.apply(); } - + + oldFullscreen = preferences.getBoolean("fullscreen", false); + oldHideCutout = preferences.getBoolean("hideCutout", false); + preferences.registerOnSharedPreferenceChangeListener((sharedPreferences, key) -> onPreferencesChanged(key)); getWindow().setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | FLAG_KEEP_SCREEN_ON | FLAG_TRANSLUCENT_STATUS, 0); @@ -743,9 +748,17 @@ public void onWindowFocusChanged(boolean hasFocus) { Window window = getWindow(); View decorView = window.getDecorView(); boolean fullscreen = p.getBoolean("fullscreen", false); + boolean hideCutout = p.getBoolean("hideCutout", false); boolean reseed = p.getBoolean("Reseed", true); - fullscreen = fullscreen || getIntent().getBooleanExtra(REQUEST_LAUNCH_EXTERNAL_DISPLAY, false); + if (oldHideCutout != hideCutout || oldFullscreen != fullscreen) { + oldHideCutout = hideCutout; + oldFullscreen = fullscreen; + // For some reason cutout or fullscreen change makes layout calculations wrong and invalid. + // I did not find simple and reliable way to fix it so it is better to start from the beginning. + recreate(); + return; + } int requestedOrientation = p.getBoolean("forceLandscape", false) ? ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -754,7 +767,7 @@ public void onWindowFocusChanged(boolean hasFocus) { if (hasFocus) { if (SDK_INT >= VERSION_CODES.P) { - if (p.getBoolean("hideCutout", false)) + if (hideCutout) getWindow().getAttributes().layoutInDisplayCutoutMode = (SDK_INT >= VERSION_CODES.R) ? LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS : LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; From ee1bd84fabf19d59302ca11c12d575e14e044be1 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 16 May 2024 20:03:47 +0300 Subject: [PATCH 015/107] Introducing option to intercept system shortcuts only when pointer is also intercepted Fixes #606 --- .../java/com/termux/x11/LoriePreferences.java | 1 + .../java/com/termux/x11/MainActivity.java | 7 ++-- .../termux/x11/input/InputEventSender.java | 9 ++++- .../termux/x11/input/TouchInputHandler.java | 33 +++++++++++-------- .../com/termux/x11/utils/KeyInterceptor.java | 6 ++-- .../termux/x11/utils/X11ToolbarViewPager.java | 4 +++ app/src/main/res/xml/preferences.xml | 5 +++ 7 files changed, 47 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/termux/x11/LoriePreferences.java b/app/src/main/java/com/termux/x11/LoriePreferences.java index 211e862dc..65189a8d2 100644 --- a/app/src/main/java/com/termux/x11/LoriePreferences.java +++ b/app/src/main/java/com/termux/x11/LoriePreferences.java @@ -180,6 +180,7 @@ void updatePreferencesLayout() { findPreference("hideEKOnVolDown").setEnabled(p.getBoolean("showAdditionalKbd", false) && p.getBoolean("captureVolumeKeys", true)); findPreference("dexMetaKeyCapture").setEnabled(!p.getBoolean("enableAccessibilityServiceAutomatically", false)); findPreference("enableAccessibilityServiceAutomatically").setEnabled(!p.getBoolean("dexMetaKeyCapture", false)); + findPreference("keyCaptureOnlyWhenPointerIntercepted").setEnabled(p.getBoolean("dexMetaKeyCapture", false) || p.getBoolean("enableAccessibilityServiceAutomatically", false)); findPreference("filterOutWinkey").setEnabled(p.getBoolean("enableAccessibilityServiceAutomatically", false)); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index f4bb782f2..f0a9cb2b7 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -96,6 +96,7 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo private boolean hideEKOnVolDown = false; private boolean toggleIMEUsingBackKey = false; private boolean useTermuxEKBarBehaviour = false; + public boolean dexMetaKeyCapture = false; private static final int KEY_BACK = 158; private static boolean oldFullscreen = false; @@ -522,7 +523,9 @@ void onPreferencesChanged(String key) { if (!p.getBoolean("pointerCapture", false) && lorieView.hasPointerCapture()) lorieView.releasePointerCapture(); - SamsungDexUtils.dexMetaKeyCapture(this, p.getBoolean("dexMetaKeyCapture", false)); + KeyInterceptor.keyCaptureOnlyWhenPointerIntercepted = p.getBoolean("keyCaptureOnlyWhenPointerIntercepted", false); + dexMetaKeyCapture = p.getBoolean("dexMetaKeyCapture", false); + SamsungDexUtils.dexMetaKeyCapture(this, !KeyInterceptor.keyCaptureOnlyWhenPointerIntercepted && dexMetaKeyCapture); setTerminalToolbarView(); onWindowFocusChanged(true); @@ -804,7 +807,7 @@ public void onWindowFocusChanged(boolean hasFocus) { window.setSoftInputMode((reseed ? SOFT_INPUT_ADJUST_RESIZE : SOFT_INPUT_ADJUST_PAN) | SOFT_INPUT_STATE_HIDDEN); ((FrameLayout) findViewById(android.R.id.content)).getChildAt(0).setFitsSystemWindows(!fullscreen); - SamsungDexUtils.dexMetaKeyCapture(this, hasFocus && p.getBoolean("dexMetaKeyCapture", false)); + SamsungDexUtils.dexMetaKeyCapture(this, hasFocus && !KeyInterceptor.keyCaptureOnlyWhenPointerIntercepted && dexMetaKeyCapture); } @Override diff --git a/app/src/main/java/com/termux/x11/input/InputEventSender.java b/app/src/main/java/com/termux/x11/input/InputEventSender.java index 83d5e499b..6f40ac905 100644 --- a/app/src/main/java/com/termux/x11/input/InputEventSender.java +++ b/app/src/main/java/com/termux/x11/input/InputEventSender.java @@ -15,6 +15,10 @@ import android.view.MotionEvent; import android.view.View; +import com.termux.x11.MainActivity; +import com.termux.x11.utils.KeyInterceptor; +import com.termux.x11.utils.SamsungDexUtils; + import java.util.List; import java.util.TreeSet; @@ -204,8 +208,11 @@ else if (e.getUnicodeChar() != 0) if (e.getRepeatCount() > 0) return true; - if (pointerCapture && keyCode == KEYCODE_ESCAPE && !pressed) + if (pointerCapture && keyCode == KEYCODE_ESCAPE && !pressed) { v.releasePointerCapture(); + if (KeyInterceptor.keyCaptureOnlyWhenPointerIntercepted && MainActivity.getInstance().dexMetaKeyCapture) + SamsungDexUtils.dexMetaKeyCapture(MainActivity.getInstance(), false); + } // We try to send all other key codes to the host directly. return mInjector.sendKeyEvent(scancode, keyCode, pressed); 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 4f2c0184d..d16490b77 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -19,6 +19,10 @@ import androidx.annotation.IntDef; import androidx.core.math.MathUtils; +import com.termux.x11.MainActivity; +import com.termux.x11.utils.KeyInterceptor; +import com.termux.x11.utils.SamsungDexUtils; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -66,7 +70,7 @@ public class TouchInputHandler { private InputStrategyInterface mInputStrategy; private final InputEventSender mInjector; - private final Context mContext; + private final MainActivity mActivity; /** * Used for tracking swipe gestures. Only the Y-direction is needed for responding to swipe-up @@ -100,7 +104,7 @@ public class TouchInputHandler { @CapturedPointerTransformation int capturedPointerTransformation = CapturedPointerTransformation.NONE; - private TouchInputHandler(Context ctx, RenderData renderData, RenderStub renderStub, + private TouchInputHandler(MainActivity activity, RenderData renderData, RenderStub renderStub, final InputEventSender injector, boolean isTouchpad) { if (renderStub == null || injector == null) throw new NullPointerException(); @@ -108,10 +112,10 @@ private TouchInputHandler(Context ctx, RenderData renderData, RenderStub renderS mRenderStub = renderStub; mRenderData = renderData != null ? renderData :new RenderData(); mInjector = injector; - mContext = ctx; + mActivity = activity; GestureListener listener = new GestureListener(); - mScroller = new GestureDetector(/*desktop*/ ctx, listener, null, false); + mScroller = new GestureDetector(/*desktop*/ activity, listener, null, false); // If long-press is enabled, the gesture-detector will not emit any further onScroll // notifications after the onLongPress notification. Since onScroll is being used for @@ -119,24 +123,24 @@ private TouchInputHandler(Context ctx, RenderData renderData, RenderStub renderS // down too long. mScroller.setIsLongpressEnabled(false); - mTapDetector = new TapGestureDetector(/*desktop*/ ctx, listener); - mSwipePinchDetector = new SwipeDetector(/*desktop*/ ctx); + mTapDetector = new TapGestureDetector(/*desktop*/ activity, listener); + mSwipePinchDetector = new SwipeDetector(/*desktop*/ activity); // The threshold needs to be bigger than the ScaledTouchSlop used by the gesture-detectors, // so that a gesture cannot be both a tap and a swipe. It also needs to be small enough so // that intentional swipes are usually detected. - float density = /*desktop*/ ctx.getResources().getDisplayMetrics().density; + float density = /*desktop*/ activity.getResources().getDisplayMetrics().density; mSwipeThreshold = 40 * density; // mEdgeSlopInPx = ViewConfiguration.get(/*desktop*/ ctx).getScaledEdgeSlop(); setInputMode(InputMode.TRACKPAD); - mDexListener = new DexListener(ctx); - mTouchpadHandler = isTouchpad ? null : new TouchInputHandler(ctx, mRenderData, renderStub, injector, true); + mDexListener = new DexListener(activity); + mTouchpadHandler = isTouchpad ? null : new TouchInputHandler(activity, mRenderData, renderStub, injector, true); } - public TouchInputHandler(Context ctx, RenderStub renderStub, final InputEventSender injector) { - this(ctx, null, renderStub, injector, false); + public TouchInputHandler(MainActivity activity, RenderStub renderStub, final InputEventSender injector) { + this(activity, null, renderStub, injector, false); } boolean isDexEvent(MotionEvent event) { @@ -163,8 +167,11 @@ public boolean handleTouchEvent(View view0, View view, MotionEvent event) { if (!view.isFocused() && event.getAction() == MotionEvent.ACTION_DOWN) view.requestFocus(); - if (mInjector.pointerCapture && !view.hasPointerCapture() && event.getAction() == MotionEvent.ACTION_UP) + if (mInjector.pointerCapture && !view.hasPointerCapture() && event.getAction() == MotionEvent.ACTION_UP) { view.requestPointerCapture(); + if (KeyInterceptor.keyCaptureOnlyWhenPointerIntercepted && mActivity.dexMetaKeyCapture) + SamsungDexUtils.dexMetaKeyCapture(mActivity, true); + } if (event.getToolType(event.getActionIndex()) == MotionEvent.TOOL_TYPE_STYLUS) return mStylusListener.onTouch(event); @@ -261,7 +268,7 @@ public void setInputMode(@InputMode int inputMode) { if (inputMode == InputMode.TOUCH) mInputStrategy = new InputStrategyInterface.NullInputStrategy(); else if (inputMode == InputMode.SIMULATED_TOUCH) - mInputStrategy = new InputStrategyInterface.SimulatedTouchInputStrategy(mRenderData, mInjector, mContext); + mInputStrategy = new InputStrategyInterface.SimulatedTouchInputStrategy(mRenderData, mInjector, mActivity); else mInputStrategy = new InputStrategyInterface.TrackpadInputStrategy(mInjector); } diff --git a/app/src/main/java/com/termux/x11/utils/KeyInterceptor.java b/app/src/main/java/com/termux/x11/utils/KeyInterceptor.java index d2e03662f..86a594b8a 100644 --- a/app/src/main/java/com/termux/x11/utils/KeyInterceptor.java +++ b/app/src/main/java/com/termux/x11/utils/KeyInterceptor.java @@ -13,6 +13,7 @@ public class KeyInterceptor extends AccessibilityService { LinkedHashSet pressedKeys = new LinkedHashSet<>(); + public static boolean keyCaptureOnlyWhenPointerIntercepted = false; private static KeyInterceptor self; public KeyInterceptor() { @@ -39,6 +40,9 @@ public boolean onKeyEvent(KeyEvent event) { && (instance.findViewById(R.id.terminal_toolbar_text_input) == null || !instance.findViewById(R.id.terminal_toolbar_text_input).isFocused()); + if (intercept && keyCaptureOnlyWhenPointerIntercepted && !instance.getWindow().getDecorView().hasPointerCapture()) + intercept = false; + if (intercept || (event.getAction() == KeyEvent.ACTION_UP && pressedKeys.contains(event.getKeyCode()))) ret = instance.handleKey(event); @@ -50,8 +54,6 @@ public boolean onKeyEvent(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_UP) pressedKeys.remove(event.getKeyCode()); - Log.d("KeyInterceptor", "" + (event.getUnicodeChar() != 0 ? (char) event.getUnicodeChar() : "") + " " + (event.getCharacters() != null ? event.getCharacters() : "") + " " + (ret ? " " : " not ") + "intercepted event " + event); - return ret; } diff --git a/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java b/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java index 06dfb33cf..d307cc1c6 100644 --- a/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java +++ b/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java @@ -1,8 +1,10 @@ package com.termux.x11.utils; import android.annotation.SuppressLint; +import android.content.SharedPreferences; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; +import android.preference.PreferenceManager; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -76,6 +78,8 @@ public Object instantiateItem(@NonNull ViewGroup collection, int position) { editText.setOnCapturedPointerListener((v2, e2) -> { v2.releasePointerCapture(); + if (KeyInterceptor.keyCaptureOnlyWhenPointerIntercepted && mActivity.dexMetaKeyCapture) + SamsungDexUtils.dexMetaKeyCapture(mActivity, false); return false; }); diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 1f06ae68f..c4233771d 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -177,6 +177,11 @@ android:defaultValue="false" android:key="enableAccessibilityServiceAutomatically" /> + + Date: Sun, 19 May 2024 09:35:52 +0300 Subject: [PATCH 016/107] Release special keys when hiding EK bar Fixes #637 --- app/src/main/java/com/termux/x11/MainActivity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index f0a9cb2b7..72315b4de 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -657,6 +657,8 @@ public void toggleExtraKeys(boolean visible, boolean saveState) { } else { parent.removeView(pager); parent.addView(pager, 0); + if (mExtraKeys != null) + mExtraKeys.unsetSpecialKeys(); } frm.setPadding(0, 0, 0, preferences.getBoolean("adjustHeightForEK", false) && show ? pager.getHeight() : 0); From 3c8cc7591dc7d0a8c6f21afed915c42e1a980b48 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Sun, 19 May 2024 09:42:36 +0300 Subject: [PATCH 017/107] Adding option to disable hardware keyboard workaround. Fixes #635 --- app/src/main/java/com/termux/x11/LorieView.java | 6 ++++++ app/src/main/res/xml/preferences.xml | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/app/src/main/java/com/termux/x11/LorieView.java b/app/src/main/java/com/termux/x11/LorieView.java index 846726881..58429db86 100644 --- a/app/src/main/java/com/termux/x11/LorieView.java +++ b/app/src/main/java/com/termux/x11/LorieView.java @@ -42,6 +42,7 @@ interface PixelFormat { private ClipboardManager clipboard; private long lastClipboardTimestamp = System.currentTimeMillis(); private static boolean clipboardSyncEnabled = false; + private static boolean hardwareKbdScancodesWorkaround = false; private Callback mCallback; private final Point p = new Point(); private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() { @@ -204,6 +205,7 @@ public void sendMouseWheelEvent(float deltaX, float deltaY) { @Override public boolean dispatchKeyEventPreIme(KeyEvent event) { + if (hardwareKbdScancodesWorkaround) return false; Activity a = getActivity(); return (a instanceof MainActivity) && ((MainActivity) a).handleKey(event); } @@ -215,6 +217,10 @@ static void setClipboardSyncEnabled(boolean enabled) { setClipboardSyncEnabled(enabled, enabled); } + static void setHardwareKbdScancodesWorkaroundEnabled(boolean enabled) { + hardwareKbdScancodesWorkaround = enabled; + } + // It is used in native code void setClipboardText(String text) { clipboard.setPrimaryClip(ClipData.newPlainText("X11 clipboard", text)); diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index c4233771d..883176ae0 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -160,6 +160,12 @@ android:defaultValue="false" android:key="preferScancodes"/> + + Date: Sun, 19 May 2024 10:53:08 +0300 Subject: [PATCH 018/107] Fix hardware keyboard workaround toggle. --- app/src/main/java/com/termux/x11/MainActivity.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 72315b4de..92bda9a44 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -558,6 +558,7 @@ void onPreferencesChanged(String key) { hideEKOnVolDown = p.getBoolean("hideEKOnVolDown", false); useTermuxEKBarBehaviour = p.getBoolean("useTermuxEKBarBehaviour", false); toggleIMEUsingBackKey = p.getBoolean("toggleIMEUsingBackKey", true); + LorieView.setHardwareKbdScancodesWorkaroundEnabled(p.getBoolean("hardwareKbdScancodesWorkaround", true)); int requestedOrientation = p.getBoolean("forceLandscape", false) ? ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; From cc88fde817ec5aa89a969f0e25b47315dd8d1a56 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Sun, 19 May 2024 11:20:43 +0300 Subject: [PATCH 019/107] Suppress some compiler warnings [no ci] --- app/src/main/cpp/CMakeLists.txt | 7 ++++++- app/src/main/cpp/recipes/pixman.cmake | 3 ++- app/src/main/cpp/recipes/tirpc.cmake | 2 +- app/src/main/cpp/recipes/xkbcomp.cmake | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index a5fc639d4..7c96a8a21 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -57,7 +57,12 @@ set(test_compile_options "-fno-strict-aliasing" "-Wno-unused-but-set-variable" "-Wno-unused-parameter" - "-Wno-unused-variable") + "-Wno-unused-variable" + "-Wno-unused-function" + "-Wno-missing-prototypes" + "-Wno-macro-redefined" + "-Wno-uninitialized" + "-Wno-shadow") set(CMAKE_REQUIRED_QUIET_OLD ${CMAKE_REQUIRED_QUIET}) set(CMAKE_REQUIRED_QUIET 1) diff --git a/app/src/main/cpp/recipes/pixman.cmake b/app/src/main/cpp/recipes/pixman.cmake index 4833b5923..c91bc3261 100644 --- a/app/src/main/cpp/recipes/pixman.cmake +++ b/app/src/main/cpp/recipes/pixman.cmake @@ -60,7 +60,8 @@ set(PIXMAN_CFLAGS "-DPACKAGE=\"pixman\"" "-DTLS=__thread" "-DSIZEOF_LONG=${SIZEOF_LONG}" - "-DUSE_OPENMP=1") + "-DUSE_OPENMP=1" + "-Wno-unknown-attributes") if("${CMAKE_ANDROID_ARCH_ABI}" STREQUAL "arm64-v8a") set(PIXMAN_SRC ${PIXMAN_SRC} diff --git a/app/src/main/cpp/recipes/tirpc.cmake b/app/src/main/cpp/recipes/tirpc.cmake index 6ba9944cf..15492e8b4 100644 --- a/app/src/main/cpp/recipes/tirpc.cmake +++ b/app/src/main/cpp/recipes/tirpc.cmake @@ -47,4 +47,4 @@ target_compile_options(tirpc PRIVATE "-DPORTMAP" "-DINET6=1" "-DHAVE_FEATURES_H= "-DHAVE_GETRPCBYNAME=1" "-DHAVE_GETRPCBYNUMBER=1" "-DHAVE_SETRPCENT=1" "-DHAVE_ENDRPCENT=1" "-DHAVE_GETRPCENT=1" "-UHAVE_GSSAPI_GSSAPI_EXT_H" "-UAUTHDES_SUPPORT" "-Dquad_t=long long" "-Du_quad_t=unsigned long long" "-Dgetdtablesize()=sysconf(_SC_OPEN_MAX)" "-D_GNU_SOURCE" - "-Wall" "-pipe" "-fPIC" "-DPIC") + "-Wall" "-pipe" "-fPIC" "-DPIC" "-Wno-deprecated-non-prototype" "-Wno-macro-redefined") diff --git a/app/src/main/cpp/recipes/xkbcomp.cmake b/app/src/main/cpp/recipes/xkbcomp.cmake index 0fcc7c740..a7a2a336c 100644 --- a/app/src/main/cpp/recipes/xkbcomp.cmake +++ b/app/src/main/cpp/recipes/xkbcomp.cmake @@ -65,7 +65,7 @@ target_include_directories(xkbcomp "${CMAKE_CURRENT_BINARY_DIR}") target_link_libraries(xkbcomp PRIVATE xorgproto) target_link_options(xkbcomp PRIVATE "-fPIE" "-fPIC") -target_compile_options(xkbcomp PRIVATE ${common_compile_options} "-fvisibility=hidden" "-DHAVE_STRCASECMP" "-DHAVE_STRDUP" "-DDFLT_XKB_CONFIG_ROOT=\"/\"" "-DHAVE_SYS_IOCTL_H" "-fPIE" "-fPIC" "-DPACKAGE_VERSION=\"2.70\"") +target_compile_options(xkbcomp PRIVATE ${common_compile_options} "-fvisibility=hidden" "-DHAVE_STRCASECMP" "-DHAVE_STRDUP" "-DDFLT_XKB_CONFIG_ROOT=\"/\"" "-DHAVE_SYS_IOCTL_H" "-fPIE" "-fPIC" "-DPACKAGE_VERSION=\"2.70\"" "-Wno-shadow") target_apply_patch(xkbcomp "${CMAKE_CURRENT_SOURCE_DIR}/xkbcomp" "${CMAKE_CURRENT_SOURCE_DIR}/patches/xkbcomp.patch") target_apply_patch(xkbfile "${CMAKE_CURRENT_SOURCE_DIR}/libxkbfile" "${CMAKE_CURRENT_SOURCE_DIR}/patches/xkbfile.patch") target_apply_patch(X11 "${CMAKE_CURRENT_SOURCE_DIR}/libx11" "${CMAKE_CURRENT_SOURCE_DIR}/patches/x11.patch") From c0715b10944209a22bae0ee41064179619dff546 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Wed, 22 May 2024 20:59:38 +0300 Subject: [PATCH 020/107] Fix IDE errors with unresolved R.* symbols. [no ci] --- shell-loader/build.gradle | 2 +- shell-loader/src/main/java/com/termux/x11/Loader.java | 1 + shell-loader/stub/build.gradle | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/shell-loader/build.gradle b/shell-loader/build.gradle index a543df766..058122787 100755 --- a/shell-loader/build.gradle +++ b/shell-loader/build.gradle @@ -1,7 +1,7 @@ //file:noinspection UnnecessaryQualifiedReference, GrDeprecatedAPIUsage apply plugin: 'com.android.application' -android.namespace 'com.termux.x11' +android.namespace 'com.termux.x11.shell_loader' android.defaultConfig.minSdkVersion 21 android.compileSdkVersion 28 android.buildTypes.debug.signingConfig null diff --git a/shell-loader/src/main/java/com/termux/x11/Loader.java b/shell-loader/src/main/java/com/termux/x11/Loader.java index 706fec2e4..519933cc5 100755 --- a/shell-loader/src/main/java/com/termux/x11/Loader.java +++ b/shell-loader/src/main/java/com/termux/x11/Loader.java @@ -1,4 +1,5 @@ package com.termux.x11; +import com.termux.x11.shell_loader.BuildConfig; public class Loader { /** diff --git a/shell-loader/stub/build.gradle b/shell-loader/stub/build.gradle index dcbb96414..d27526976 100644 --- a/shell-loader/stub/build.gradle +++ b/shell-loader/stub/build.gradle @@ -3,7 +3,7 @@ plugins { } android { - namespace 'com.termux.x11' + namespace 'com.termux.x11.stub' compileSdkVersion 30 defaultConfig { minSdkVersion 24 From c420364047a1783d491e0b9e913f4f621d661c91 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 May 2024 22:28:01 +0300 Subject: [PATCH 021/107] Bump org.jetbrains.kotlin:kotlin-stdlib-jdk8 from 1.9.24 to 2.0.0 [no ci] updated-dependencies: - dependency-name: org.jetbrains.kotlin:kotlin-stdlib-jdk8 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 34ae590f5..d85442637 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -46,7 +46,7 @@ android { dependencies { //noinspection DifferentStdlibGradleVersion - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.24" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.0" implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0' implementation 'androidx.preference:preference:1.2.1' compileOnly project(':shell-loader:stub') From 1d036f653a92b9c2b94fd69a00a3438aeba7ec93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 May 2024 22:28:21 +0300 Subject: [PATCH 022/107] Bump com.android.tools.build:gradle from 8.4.0 to 8.4.1 [no ci] updated-dependencies: - dependency-name: com.android.tools.build:gradle dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a838838e3..e0af7c7e1 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { dependencies { // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files - classpath 'com.android.tools.build:gradle:8.4.0' + classpath 'com.android.tools.build:gradle:8.4.1' } } From 79ee02a78b1b7b85b3078b90699a6bea7933d48d Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Mon, 27 May 2024 09:29:41 +0300 Subject: [PATCH 023/107] Making hardware mouse pointer speed depend on display density --- .../main/java/com/termux/x11/LorieView.java | 12 +++-- .../java/com/termux/x11/MainActivity.java | 22 +++++----- .../termux/x11/input/TouchInputHandler.java | 44 +++++++++---------- 3 files changed, 36 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/termux/x11/LorieView.java b/app/src/main/java/com/termux/x11/LorieView.java index 58429db86..dcc3c6f7c 100644 --- a/app/src/main/java/com/termux/x11/LorieView.java +++ b/app/src/main/java/com/termux/x11/LorieView.java @@ -14,6 +14,7 @@ import android.graphics.drawable.ColorDrawable; import android.preference.PreferenceManager; import android.util.AttributeSet; +import android.util.DisplayMetrics; import android.util.Log; import android.view.KeyEvent; import android.view.Surface; @@ -212,13 +213,10 @@ public boolean dispatchKeyEventPreIme(KeyEvent event) { ClipboardManager.OnPrimaryClipChangedListener clipboardListener = this::handleClipboardChange; - static void setClipboardSyncEnabled(boolean enabled) { - clipboardSyncEnabled = enabled; - setClipboardSyncEnabled(enabled, enabled); - } - - static void setHardwareKbdScancodesWorkaroundEnabled(boolean enabled) { - hardwareKbdScancodesWorkaround = enabled; + public void reloadPreferences(SharedPreferences p) { + hardwareKbdScancodesWorkaround = p.getBoolean("hardwareKbdScancodesWorkaround", true); + clipboardSyncEnabled = p.getBoolean("clipboardEnable", false); + setClipboardSyncEnabled(clipboardSyncEnabled, clipboardSyncEnabled); } // It is used in native code diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 92bda9a44..eea7972ae 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -38,6 +38,7 @@ import android.preference.PreferenceManager; import android.provider.Settings; import android.service.notification.StatusBarNotification; +import android.util.DisplayMetrics; import android.util.Log; import android.view.DragEvent; import android.view.InputDevice; @@ -492,7 +493,7 @@ void tryConnect() { LorieView.connect(fd.detachFd()); getLorieView().triggerCallback(); clientConnectedStateChanged(true); - LorieView.setClipboardSyncEnabled(PreferenceManager.getDefaultSharedPreferences(this).getBoolean("clipboardEnable", false)); + getLorieView().reloadPreferences(PreferenceManager.getDefaultSharedPreferences(this)); } else handler.postDelayed(this::tryConnect, 500); } catch (Exception e) { @@ -511,14 +512,8 @@ void onPreferencesChanged(String key) { SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(this); LorieView lorieView = getLorieView(); - int mode = Integer.parseInt(p.getString("touchMode", "1")); - mInputHandler.setInputMode(mode); - mInputHandler.setTapToMove(p.getBoolean("tapToMove", false)); - mInputHandler.setPreferScancodes(p.getBoolean("preferScancodes", false)); - mInputHandler.setPointerCaptureEnabled(p.getBoolean("pointerCapture", false)); - mInputHandler.setApplyDisplayScaleFactorToTouchpad(p.getBoolean("scaleTouchpad", true)); - mInputHandler.setTransformCapturedPointer(p.getString("transformCapturedPointer", "no")); - mInputHandler.setCapturedPointerSpeedFactor(((float) p.getInt("capturedPointerSpeedFactor", 100))/100); + mInputHandler.reloadPreferences(p); + lorieView.reloadPreferences(p); captureVolumeKeys = p.getBoolean("captureVolumeKeys", true); if (!p.getBoolean("pointerCapture", false) && lorieView.hasPointerCapture()) lorieView.releasePointerCapture(); @@ -529,7 +524,6 @@ void onPreferencesChanged(String key) { setTerminalToolbarView(); onWindowFocusChanged(true); - LorieView.setClipboardSyncEnabled(p.getBoolean("clipboardEnable", false)); lorieView.triggerCallback(); @@ -558,7 +552,6 @@ void onPreferencesChanged(String key) { hideEKOnVolDown = p.getBoolean("hideEKOnVolDown", false); useTermuxEKBarBehaviour = p.getBoolean("useTermuxEKBarBehaviour", false); toggleIMEUsingBackKey = p.getBoolean("toggleIMEUsingBackKey", true); - LorieView.setHardwareKbdScancodesWorkaroundEnabled(p.getBoolean("hardwareKbdScancodesWorkaround", true)); int requestedOrientation = p.getBoolean("forceLandscape", false) ? ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -884,4 +877,11 @@ private void checkXEvents() { getLorieView().handleXEvents(); handler.postDelayed(this::checkXEvents, 300); } + + public static void getRealMetrics(DisplayMetrics m) { + if (getInstance() != null && + getInstance().getLorieView() != null && + getInstance().getLorieView().getDisplay() != null) + getInstance().getLorieView().getDisplay().getRealMetrics(m); + } } 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 d16490b77..cb9db56e1 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -6,9 +6,11 @@ import android.annotation.SuppressLint; import android.content.Context; +import android.content.SharedPreferences; import android.graphics.PointF; import android.os.Handler; import android.os.Build; +import android.util.DisplayMetrics; import android.view.GestureDetector; import android.view.InputDevice; import android.view.KeyEvent; @@ -71,6 +73,7 @@ public class TouchInputHandler { private InputStrategyInterface mInputStrategy; private final InputEventSender mInjector; private final MainActivity mActivity; + private final DisplayMetrics mMetrics = new DisplayMetrics() ; /** * Used for tracking swipe gestures. Only the Y-direction is needed for responding to swipe-up @@ -255,13 +258,13 @@ public void handleClientSizeChanged(int w, int h) { public void handleHostSizeChanged(int w, int h) { mRenderData.imageWidth = w; mRenderData.imageHeight = h; -// mPanGestureBounds = new Rect(mEdgeSlopInPx, mEdgeSlopInPx, w - mEdgeSlopInPx, h - mEdgeSlopInPx); moveCursorToScreenPoint((float) w/2, (float) h/2); if (mTouchpadHandler != null) mTouchpadHandler.handleHostSizeChanged(w, h); resetTransformation(); + MainActivity.getRealMetrics(mMetrics); } public void setInputMode(@InputMode int inputMode) { @@ -273,28 +276,14 @@ else if (inputMode == InputMode.SIMULATED_TOUCH) mInputStrategy = new InputStrategyInterface.TrackpadInputStrategy(mInjector); } - public void setTapToMove(boolean enabled) { - mInjector.tapToMove = enabled; - } - - public void setPreferScancodes(boolean enabled) { - mInjector.preferScancodes = enabled; - } - - public void setPointerCaptureEnabled(boolean enabled) { - mInjector.pointerCapture = enabled; - } - - public void setApplyDisplayScaleFactorToTouchpad(boolean enabled) { - mInjector.scaleTouchpad = enabled; - } - - public void setCapturedPointerSpeedFactor(float value) { - mInjector.capturedPointerSpeedFactor = value; - } - - public void setTransformCapturedPointer(String value) { - switch (value) { + public void reloadPreferences(SharedPreferences p) { + setInputMode(Integer.parseInt(p.getString("touchMode", "1"))); + mInjector.tapToMove = p.getBoolean("tapToMove", false); + mInjector.preferScancodes = p.getBoolean("preferScancodes", false); + mInjector.pointerCapture = p.getBoolean("pointerCapture", false); + mInjector.scaleTouchpad = p.getBoolean("scaleTouchpad", true); + mInjector.capturedPointerSpeedFactor = ((float) p.getInt("capturedPointerSpeedFactor", 100))/100; + switch (p.getString("transformCapturedPointer", "no")) { case "c": capturedPointerTransformation = CapturedPointerTransformation.CLOCKWISE; break; @@ -307,6 +296,8 @@ public void setTransformCapturedPointer(String value) { default: capturedPointerTransformation = CapturedPointerTransformation.NONE; } + + MainActivity.getRealMetrics(mMetrics); } private void moveCursorByOffset(float deltaX, float deltaY) { @@ -519,6 +510,7 @@ boolean mouseButtonDown(int mask) { {MotionEvent.BUTTON_SECONDARY, InputStub.BUTTON_RIGHT} }; + /** @noinspection ReassignedVariable, SuspiciousNameCombination*/ @SuppressLint("ClickableViewAccessibility") boolean onTouch(View v, MotionEvent e) { if (e.getAction() == MotionEvent.ACTION_SCROLL) { @@ -552,7 +544,10 @@ boolean onTouch(View v, MotionEvent e) { x = -x; y = -y; break; } - mInjector.sendCursorMove(mInjector.capturedPointerSpeedFactor * x, mInjector.capturedPointerSpeedFactor * y, true); + x *= mInjector.capturedPointerSpeedFactor * mMetrics.density; + y *= mInjector.capturedPointerSpeedFactor * mMetrics.density; + + mInjector.sendCursorMove(x, y, true); if (axis_relative_x && mTouchpadHandler != null) mTouchpadHandler.mTapDetector.onTouchEvent(e); } @@ -646,6 +641,7 @@ private boolean hasFlags(MotionEvent e, int flags) { return (e.getFlags() & flags) == flags; } + @SuppressLint({"WrongConstant", "InlinedApi"}) private boolean isScrollingEvent(MotionEvent e) { return hasFlags(e, 0x14000000) || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && e.getClassification() == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE); } From d8286e2c6409c29b8c436c4f404316daf5a914fe Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Tue, 28 May 2024 11:11:49 +0300 Subject: [PATCH 024/107] Making key interception behaviour not dependent on pointer interception --- .../java/com/termux/x11/LoriePreferences.java | 37 +++++------------ .../main/java/com/termux/x11/LorieView.java | 3 +- .../java/com/termux/x11/MainActivity.java | 27 +++++++----- .../termux/x11/input/InputEventSender.java | 14 +++---- .../termux/x11/input/TouchInputHandler.java | 41 +++++++++++++++---- .../com/termux/x11/utils/KeyInterceptor.java | 10 +---- .../com/termux/x11/utils/SamsungDexUtils.java | 39 ++++++++++-------- .../termux/x11/utils/X11ToolbarViewPager.java | 4 +- app/src/main/res/xml/preferences.xml | 6 +-- 9 files changed, 94 insertions(+), 87 deletions(-) diff --git a/app/src/main/java/com/termux/x11/LoriePreferences.java b/app/src/main/java/com/termux/x11/LoriePreferences.java index 65189a8d2..532ba0896 100644 --- a/app/src/main/java/com/termux/x11/LoriePreferences.java +++ b/app/src/main/java/com/termux/x11/LoriePreferences.java @@ -155,32 +155,15 @@ void updatePreferencesLayout() { opacityEKBar.setSeekBarIncrement(1); opacityEKBar.setShowSeekBarValue(true); - switch (p.getString("displayResolutionMode", "native")) { - case "scaled": - findPreference("displayScale").setVisible(true); - findPreference("displayResolutionExact").setVisible(false); - findPreference("displayResolutionCustom").setVisible(false); - break; - case "exact": - findPreference("displayScale").setVisible(false); - findPreference("displayResolutionExact").setVisible(true); - findPreference("displayResolutionCustom").setVisible(false); - break; - case "custom": - findPreference("displayScale").setVisible(false); - findPreference("displayResolutionExact").setVisible(false); - findPreference("displayResolutionCustom").setVisible(true); - break; - default: - findPreference("displayScale").setVisible(false); - findPreference("displayResolutionExact").setVisible(false); - findPreference("displayResolutionCustom").setVisible(false); - } + String displayResMode = p.getString("displayResolutionMode", "native"); + findPreference("displayScale").setVisible(displayResMode.contentEquals("scaled")); + findPreference("displayResolutionExact").setVisible(displayResMode.contentEquals("exact")); + findPreference("displayResolutionCustom").setVisible(displayResMode.contentEquals("custom")); findPreference("hideEKOnVolDown").setEnabled(p.getBoolean("showAdditionalKbd", false) && p.getBoolean("captureVolumeKeys", true)); findPreference("dexMetaKeyCapture").setEnabled(!p.getBoolean("enableAccessibilityServiceAutomatically", false)); findPreference("enableAccessibilityServiceAutomatically").setEnabled(!p.getBoolean("dexMetaKeyCapture", false)); - findPreference("keyCaptureOnlyWhenPointerIntercepted").setEnabled(p.getBoolean("dexMetaKeyCapture", false) || p.getBoolean("enableAccessibilityServiceAutomatically", false)); + findPreference("pauseKeyInterceptingWithEsc").setEnabled(p.getBoolean("dexMetaKeyCapture", false) || p.getBoolean("enableAccessibilityServiceAutomatically", false)); findPreference("filterOutWinkey").setEnabled(p.getBoolean("enableAccessibilityServiceAutomatically", false)); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) @@ -253,7 +236,7 @@ public boolean onPreferenceClick(@NonNull Preference preference) { .setPositiveButton("OK", (dialog, whichButton) -> { String text = config.getText().toString(); - text = text.length() > 0 ? text : TermuxX11ExtraKeys.DEFAULT_IVALUE_EXTRA_KEYS; + text = !text.isEmpty() ? text : TermuxX11ExtraKeys.DEFAULT_IVALUE_EXTRA_KEYS; preferences .edit() .putString("extra_keys_config", text) @@ -293,7 +276,8 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { .setNegativeButton("OK", null) .create() .show(); - } else e.printStackTrace(); + } else //noinspection CallToPrintStackTrace + e.printStackTrace(); return false; } } @@ -445,7 +429,8 @@ public void onReceive(Context context, Intent intent) { "Please, launch this command using ADB:\n" + "adb shell pm grant com.termux.x11 android.permission.WRITE_SECURE_SETTINGS"); return; - } else e.printStackTrace(); + } else //noinspection CallToPrintStackTrace + e.printStackTrace(); } break; } @@ -465,7 +450,7 @@ public void onReceive(Context context, Intent intent) { } catch (NumberFormatException | PatternSyntaxException ignored) { v = 100; } - edit.putInt("capturedPointerSpeedFactor", Integer.parseInt(newValue)); + edit.putInt("capturedPointerSpeedFactor", v); break; } case "displayDensity": { diff --git a/app/src/main/java/com/termux/x11/LorieView.java b/app/src/main/java/com/termux/x11/LorieView.java index dcc3c6f7c..d3ec9b278 100644 --- a/app/src/main/java/com/termux/x11/LorieView.java +++ b/app/src/main/java/com/termux/x11/LorieView.java @@ -14,7 +14,6 @@ import android.graphics.drawable.ColorDrawable; import android.preference.PreferenceManager; import android.util.AttributeSet; -import android.util.DisplayMetrics; import android.util.Log; import android.view.KeyEvent; import android.view.Surface; @@ -229,7 +228,7 @@ void setClipboardText(String text) { lastClipboardTimestamp = System.currentTimeMillis() + 150; } - // It is used in native code + /** @noinspection unused*/ // It is used in native code void requestClipboard() { if (!clipboardSyncEnabled) { sendClipboardEvent("".getBytes(StandardCharsets.UTF_8)); diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index eea7972ae..9b7cb195e 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -97,7 +97,6 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo private boolean hideEKOnVolDown = false; private boolean toggleIMEUsingBackKey = false; private boolean useTermuxEKBarBehaviour = false; - public boolean dexMetaKeyCapture = false; private static final int KEY_BACK = 158; private static boolean oldFullscreen = false; @@ -210,7 +209,7 @@ public void swipeDown() { } } - result = mInputHandler.sendKeyEvent(v, e); + result = mInputHandler.sendKeyEvent(e); // Do not steal dedicated buttons from a full external keyboard. if (useTermuxEKBarBehaviour && mExtraKeys != null && (dev == null || dev.isVirtual())) @@ -515,12 +514,6 @@ void onPreferencesChanged(String key) { mInputHandler.reloadPreferences(p); lorieView.reloadPreferences(p); captureVolumeKeys = p.getBoolean("captureVolumeKeys", true); - if (!p.getBoolean("pointerCapture", false) && lorieView.hasPointerCapture()) - lorieView.releasePointerCapture(); - - KeyInterceptor.keyCaptureOnlyWhenPointerIntercepted = p.getBoolean("keyCaptureOnlyWhenPointerIntercepted", false); - dexMetaKeyCapture = p.getBoolean("dexMetaKeyCapture", false); - SamsungDexUtils.dexMetaKeyCapture(this, !KeyInterceptor.keyCaptureOnlyWhenPointerIntercepted && dexMetaKeyCapture); setTerminalToolbarView(); onWindowFocusChanged(true); @@ -616,7 +609,7 @@ public ViewPager getTerminalToolbarViewPager() { private void setTerminalToolbarView() { final ViewPager terminalToolbarViewPager = getTerminalToolbarViewPager(); - terminalToolbarViewPager.setAdapter(new X11ToolbarViewPager.PageAdapter(this, (v, k, e) -> mInputHandler.sendKeyEvent(getLorieView(), e))); + terminalToolbarViewPager.setAdapter(new X11ToolbarViewPager.PageAdapter(this, (v, k, e) -> mInputHandler.sendKeyEvent(e))); terminalToolbarViewPager.addOnPageChangeListener(new X11ToolbarViewPager.OnPageChangeListener(this, terminalToolbarViewPager)); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); @@ -803,7 +796,6 @@ public void onWindowFocusChanged(boolean hasFocus) { window.setSoftInputMode((reseed ? SOFT_INPUT_ADJUST_RESIZE : SOFT_INPUT_ADJUST_PAN) | SOFT_INPUT_STATE_HIDDEN); ((FrameLayout) findViewById(android.R.id.content)).getChildAt(0).setFitsSystemWindows(!fullscreen); - SamsungDexUtils.dexMetaKeyCapture(this, hasFocus && !KeyInterceptor.keyCaptureOnlyWhenPointerIntercepted && dexMetaKeyCapture); } @Override @@ -884,4 +876,19 @@ public static void getRealMetrics(DisplayMetrics m) { getInstance().getLorieView().getDisplay() != null) getInstance().getLorieView().getDisplay().getRealMetrics(m); } + + public static void setCapturingEnabled(boolean enabled) { + if (getInstance() == null || getInstance().mInputHandler == null) + return; + + getInstance().mInputHandler.setCapturingEnabled(enabled); + } + + public boolean shouldInterceptKeys() { + View textInput = findViewById(R.id.terminal_toolbar_text_input); + if (mInputHandler == null || !hasWindowFocus() || (textInput != null && textInput.isFocused())) + return false; + + return mInputHandler.shouldInterceptKeys(); + } } diff --git a/app/src/main/java/com/termux/x11/input/InputEventSender.java b/app/src/main/java/com/termux/x11/input/InputEventSender.java index 6f40ac905..3a1c9f4f8 100644 --- a/app/src/main/java/com/termux/x11/input/InputEventSender.java +++ b/app/src/main/java/com/termux/x11/input/InputEventSender.java @@ -13,11 +13,8 @@ import android.graphics.PointF; import android.view.KeyEvent; import android.view.MotionEvent; -import android.view.View; import com.termux.x11.MainActivity; -import com.termux.x11.utils.KeyInterceptor; -import com.termux.x11.utils.SamsungDexUtils; import java.util.List; import java.util.TreeSet; @@ -38,6 +35,8 @@ public final class InputEventSender { public boolean pointerCapture = false; public boolean scaleTouchpad = false; public float capturedPointerSpeedFactor = 100; + public boolean dexMetaKeyCapture = false; + public boolean pauseKeyInterceptingWithEsc = false; /** Set of pressed keys for which we've sent TextEvent. */ private final TreeSet mPressedTextKeys; @@ -136,7 +135,7 @@ public void sendTouchEvent(MotionEvent event, RenderData renderData) { * key-events or text-events. This contains some logic for handling some special keys, and * avoids sending a key-up event for a key that was previously injected as a text-event. */ - public boolean sendKeyEvent(View v, KeyEvent e) { + public boolean sendKeyEvent(KeyEvent e) { int keyCode = e.getKeyCode(); boolean pressed = e.getAction() == KeyEvent.ACTION_DOWN; @@ -208,11 +207,8 @@ else if (e.getUnicodeChar() != 0) if (e.getRepeatCount() > 0) return true; - if (pointerCapture && keyCode == KEYCODE_ESCAPE && !pressed) { - v.releasePointerCapture(); - if (KeyInterceptor.keyCaptureOnlyWhenPointerIntercepted && MainActivity.getInstance().dexMetaKeyCapture) - SamsungDexUtils.dexMetaKeyCapture(MainActivity.getInstance(), false); - } + if (keyCode == KEYCODE_ESCAPE && !pressed && e.hasNoModifiers()) + MainActivity.setCapturingEnabled(false); // We try to send all other key codes to the host directly. return mInjector.sendKeyEvent(scancode, keyCode, pressed); 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 cb9db56e1..830728093 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 androidx.core.math.MathUtils; import com.termux.x11.MainActivity; -import com.termux.x11.utils.KeyInterceptor; import com.termux.x11.utils.SamsungDexUtils; import java.lang.annotation.Retention; @@ -73,7 +72,9 @@ public class TouchInputHandler { private InputStrategyInterface mInputStrategy; private final InputEventSender mInjector; private final MainActivity mActivity; - private final DisplayMetrics mMetrics = new DisplayMetrics() ; + private final DisplayMetrics mMetrics = new DisplayMetrics(); + + private boolean keyIntercepting = false; /** * Used for tracking swipe gestures. Only the Y-direction is needed for responding to swipe-up @@ -170,11 +171,8 @@ public boolean handleTouchEvent(View view0, View view, MotionEvent event) { if (!view.isFocused() && event.getAction() == MotionEvent.ACTION_DOWN) view.requestFocus(); - if (mInjector.pointerCapture && !view.hasPointerCapture() && event.getAction() == MotionEvent.ACTION_UP) { - view.requestPointerCapture(); - if (KeyInterceptor.keyCaptureOnlyWhenPointerIntercepted && mActivity.dexMetaKeyCapture) - SamsungDexUtils.dexMetaKeyCapture(mActivity, true); - } + if (event.getAction() == MotionEvent.ACTION_UP) + setCapturingEnabled(true); if (event.getToolType(event.getActionIndex()) == MotionEvent.TOOL_TYPE_STYLUS) return mStylusListener.onTouch(event); @@ -276,6 +274,19 @@ else if (inputMode == InputMode.SIMULATED_TOUCH) mInputStrategy = new InputStrategyInterface.TrackpadInputStrategy(mInjector); } + public void setCapturingEnabled(boolean enabled) { + if (mInjector.pointerCapture && enabled) + mActivity.getLorieView().requestPointerCapture(); + else + mActivity.getLorieView().releasePointerCapture(); + + if (mInjector.pauseKeyInterceptingWithEsc) { + if (mInjector.dexMetaKeyCapture) + SamsungDexUtils.dexMetaKeyCapture(mActivity, enabled); + keyIntercepting = enabled; + } + } + public void reloadPreferences(SharedPreferences p) { setInputMode(Integer.parseInt(p.getString("touchMode", "1"))); mInjector.tapToMove = p.getBoolean("tapToMove", false); @@ -283,6 +294,8 @@ public void reloadPreferences(SharedPreferences p) { mInjector.pointerCapture = p.getBoolean("pointerCapture", false); mInjector.scaleTouchpad = p.getBoolean("scaleTouchpad", true); mInjector.capturedPointerSpeedFactor = ((float) p.getInt("capturedPointerSpeedFactor", 100))/100; + mInjector.dexMetaKeyCapture = p.getBoolean("dexMetaKeyCapture", false); + mInjector.pauseKeyInterceptingWithEsc = p.getBoolean("pauseKeyInterceptingWithEsc", false); switch (p.getString("transformCapturedPointer", "no")) { case "c": capturedPointerTransformation = CapturedPointerTransformation.CLOCKWISE; @@ -298,6 +311,16 @@ public void reloadPreferences(SharedPreferences p) { } MainActivity.getRealMetrics(mMetrics); + + if (!p.getBoolean("pointerCapture", false) && mActivity.getLorieView().hasPointerCapture()) + mActivity.getLorieView().releasePointerCapture(); + + keyIntercepting = !mInjector.pauseKeyInterceptingWithEsc || mActivity.getLorieView().hasPointerCapture(); + SamsungDexUtils.dexMetaKeyCapture(mActivity, mInjector.dexMetaKeyCapture && keyIntercepting); + } + + public boolean shouldInterceptKeys() { + return !mInjector.pauseKeyInterceptingWithEsc || keyIntercepting; } private void moveCursorByOffset(float deltaX, float deltaY) { @@ -488,8 +511,8 @@ private boolean screenPointLiesOutsideImageBoundary(float screenX, float screenY } } - public boolean sendKeyEvent(View view, KeyEvent event) { - return mInjector.sendKeyEvent(view, event); + public boolean sendKeyEvent(KeyEvent event) { + return mInjector.sendKeyEvent(event); } private class HardwareMouseListener { diff --git a/app/src/main/java/com/termux/x11/utils/KeyInterceptor.java b/app/src/main/java/com/termux/x11/utils/KeyInterceptor.java index 86a594b8a..8d784217a 100644 --- a/app/src/main/java/com/termux/x11/utils/KeyInterceptor.java +++ b/app/src/main/java/com/termux/x11/utils/KeyInterceptor.java @@ -1,19 +1,16 @@ package com.termux.x11.utils; import android.accessibilityservice.AccessibilityService; -import android.util.Log; import android.view.KeyEvent; import android.view.accessibility.AccessibilityEvent; import com.termux.x11.MainActivity; -import com.termux.x11.R; import java.util.LinkedHashSet; public class KeyInterceptor extends AccessibilityService { LinkedHashSet pressedKeys = new LinkedHashSet<>(); - public static boolean keyCaptureOnlyWhenPointerIntercepted = false; private static KeyInterceptor self; public KeyInterceptor() { @@ -36,12 +33,7 @@ public boolean onKeyEvent(KeyEvent event) { if (instance == null) return false; - boolean intercept = instance.hasWindowFocus() - && (instance.findViewById(R.id.terminal_toolbar_text_input) == null - || !instance.findViewById(R.id.terminal_toolbar_text_input).isFocused()); - - if (intercept && keyCaptureOnlyWhenPointerIntercepted && !instance.getWindow().getDecorView().hasPointerCapture()) - intercept = false; + boolean intercept = instance.shouldInterceptKeys(); if (intercept || (event.getAction() == KeyEvent.ACTION_UP && pressedKeys.contains(event.getKeyCode()))) ret = instance.handleKey(event); diff --git a/app/src/main/java/com/termux/x11/utils/SamsungDexUtils.java b/app/src/main/java/com/termux/x11/utils/SamsungDexUtils.java index 40c972bfb..eb3f04c5d 100644 --- a/app/src/main/java/com/termux/x11/utils/SamsungDexUtils.java +++ b/app/src/main/java/com/termux/x11/utils/SamsungDexUtils.java @@ -9,28 +9,35 @@ public class SamsungDexUtils { private static final String TAG = SamsungDexUtils.class.getSimpleName(); - static public boolean available() { + private static Method requestMetaKeyEventMethod; + private static Object manager; + + static { try { - Class semWindowManager = Class.forName("com.samsung.android.view.SemWindowManager"); - semWindowManager.getMethod("getInstance"); - semWindowManager.getDeclaredMethod("requestMetaKeyEvent", android.content.ComponentName.class, boolean.class); - return true; - } catch (Exception ignored) {} - return false; + Class clazz = Class.forName("com.samsung.android.view.SemWindowManager"); + Method obtain = clazz.getMethod("getInstance"); + requestMetaKeyEventMethod = clazz.getDeclaredMethod("requestMetaKeyEvent", android.content.ComponentName.class, boolean.class); + manager = obtain.invoke(null); + android.util.Log.d(TAG, "com.samsung.android.view.SemWindowManager is available"); + } catch (Exception ignored) { + requestMetaKeyEventMethod = null; + manager = null; + android.util.Log.d(TAG, "com.samsung.android.view.SemWindowManager is not available"); + } } - static public void dexMetaKeyCapture(Activity activity, boolean enable) { - try { - Class semWindowManager = Class.forName("com.samsung.android.view.SemWindowManager"); - Method getInstanceMethod = semWindowManager.getMethod("getInstance"); - Object manager = getInstanceMethod.invoke(null); + static public boolean available() { + return requestMetaKeyEventMethod != null && manager != null; + } - Method requestMetaKeyEvent = semWindowManager.getDeclaredMethod("requestMetaKeyEvent", android.content.ComponentName.class, boolean.class); - requestMetaKeyEvent.invoke(manager, activity.getComponentName(), enable); + static public void dexMetaKeyCapture(Activity activity, boolean enable) { + if (!available()) + return; - Log.d(TAG, "com.samsung.android.view.SemWindowManager.requestMetaKeyEvent: success"); + try { + requestMetaKeyEventMethod.invoke(manager, activity.getComponentName(), enable); } catch (Exception it) { - Log.d(TAG, "Could not call com.samsung.android.view.SemWindowManager.requestMetaKeyEvent "); + Log.d(TAG, "Could not call com.samsung.android.view.SemWindowManager.requestMetaKeyEvent"); Log.d(TAG, it.getClass().getCanonicalName() + ": " + it.getMessage()); } } diff --git a/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java b/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java index d307cc1c6..2199f6807 100644 --- a/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java +++ b/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java @@ -77,9 +77,7 @@ public Object instantiateItem(@NonNull ViewGroup collection, int position) { }); editText.setOnCapturedPointerListener((v2, e2) -> { - v2.releasePointerCapture(); - if (KeyInterceptor.keyCaptureOnlyWhenPointerIntercepted && mActivity.dexMetaKeyCapture) - SamsungDexUtils.dexMetaKeyCapture(mActivity, false); + MainActivity.setCapturingEnabled(false); return false; }); diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 883176ae0..900bbb2d2 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -184,9 +184,9 @@ android:key="enableAccessibilityServiceAutomatically" /> + android:key="pauseKeyInterceptingWithEsc" /> From 79ca755330c7c0b8f819473204b59c477ec8ba40 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Tue, 28 May 2024 12:09:31 +0300 Subject: [PATCH 025/107] Making DRI3 optional to make lavapipe work Fixes #639 --- app/src/main/cpp/lorie/InitOutput.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/src/main/cpp/lorie/InitOutput.c b/app/src/main/cpp/lorie/InitOutput.c index a400a54ed..555ee455f 100644 --- a/app/src/main/cpp/lorie/InitOutput.c +++ b/app/src/main/cpp/lorie/InitOutput.c @@ -108,10 +108,11 @@ typedef struct { JavaVM* vm; JNIEnv* env; + Bool dri3; } lorieScreenInfo, *lorieScreenInfoPtr; ScreenPtr pScreenPtr; -static lorieScreenInfo lorieScreen = { .root.width = 1280, .root.height = 1024 }; +static lorieScreenInfo lorieScreen = { .root.width = 1280, .root.height = 1024, .dri3 = TRUE }; static lorieScreenInfoPtr pvfb = &lorieScreen; static char *xstartup = NULL; @@ -167,6 +168,9 @@ static void* ddxReadyThread(unused void* cookie) { void ddxReady(void) { + if (!xstartup) + return; + pthread_t t; pthread_create(&t, NULL, ddxReadyThread, NULL); } @@ -198,6 +202,7 @@ void ddxUseMsg(void) { ErrorF("-xstartup \"command\" start `command` after server startup\n"); ErrorF("-legacy-drawing use legacy drawing, without using AHardwareBuffers\n"); ErrorF("-force-bgra force flipping colours (RGBA->BGRA)\n"); + ErrorF("-disable-dri3 disabling DRI3 support (to let lavapipe work)\n"); } int ddxProcessArgument(unused int argc, unused char *argv[], unused int i) { @@ -217,6 +222,11 @@ int ddxProcessArgument(unused int argc, unused char *argv[], unused int i) { return 1; } + if (strcmp(argv[i], "-disable-dri3") == 0) { + pvfb->dri3 = FALSE; + return 1; + } + return 0; } @@ -563,7 +573,7 @@ lorieScreenInit(ScreenPtr pScreen, unused int argc, unused char **argv) { || !miSetVisualTypesAndMasks(24, ((1 << TrueColor) | (1 << DirectColor)), 8, TrueColor, 0xFF0000, 0x00FF00, 0x0000FF) || !miSetPixmapDepths() || !fbScreenInit(pScreen, NULL, pvfb->root.width, pvfb->root.height, monitorResolution, monitorResolution, 0, 32) - || !lorieInitDri3(pScreen) + || !(!pvfb->dri3 || lorieInitDri3(pScreen)) || !fbPictureInit(pScreen, 0, 0) || !lorieRandRInit(pScreen) || !miPointerInitialize(pScreen, &loriePointerSpriteFuncs, &loriePointerCursorFuncs, TRUE) From 18eab1ce066e7f0e815e09d5bdbdb8d93448f2d3 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Tue, 28 May 2024 16:15:40 +0300 Subject: [PATCH 026/107] Make termux-x11 real display name via RANDR extension --- .../aidl/com/termux/x11/ICmdEntryInterface.aidl | 2 +- app/src/main/cpp/lorie/InitOutput.c | 15 ++++++++++++++- app/src/main/cpp/lorie/android.c | 11 ++++++++--- app/src/main/cpp/lorie/lorie.h | 1 + .../main/java/com/termux/x11/CmdEntryPoint.java | 2 +- .../main/java/com/termux/x11/MainActivity.java | 3 +-- 6 files changed, 26 insertions(+), 8 deletions(-) diff --git a/app/src/main/aidl/com/termux/x11/ICmdEntryInterface.aidl b/app/src/main/aidl/com/termux/x11/ICmdEntryInterface.aidl index 943b50cab..bd4b44a3b 100644 --- a/app/src/main/aidl/com/termux/x11/ICmdEntryInterface.aidl +++ b/app/src/main/aidl/com/termux/x11/ICmdEntryInterface.aidl @@ -2,7 +2,7 @@ package com.termux.x11; // This interface is used by utility on termux side. interface ICmdEntryInterface { - void windowChanged(in Surface surface); + void windowChanged(in Surface surface, String name); ParcelFileDescriptor getXConnection(); ParcelFileDescriptor getLogcatOutput(); } \ No newline at end of file diff --git a/app/src/main/cpp/lorie/InitOutput.c b/app/src/main/cpp/lorie/InitOutput.c index 555ee455f..95a4b61a5 100644 --- a/app/src/main/cpp/lorie/InitOutput.c +++ b/app/src/main/cpp/lorie/InitOutput.c @@ -520,6 +520,8 @@ lorieRandRInit(ScreenPtr pScreen) { RRCrtcPtr crtc; RRModePtr mode; + char screenName[1024] = "screen"; + if (!RRScreenInit(pScreen)) return FALSE; @@ -534,7 +536,8 @@ lorieRandRInit(ScreenPtr pScreen) { || !(mode = lorieCvt(pScreen->width, pScreen->height, 30)) || !(crtc = RRCrtcCreate(pScreen, NULL)) || !RRCrtcGammaSetSize(crtc, 256) - || !(output = RROutputCreate(pScreen, "screen", 6, NULL)) + || !(output = RROutputCreate(pScreen, screenName, sizeof(screenName), NULL)) + || (output->nameLength = strlen(output->name), FalseNoop()) || !RROutputSetClones(output, NULL, 0) || !RROutputSetModes(output, &mode, 1, 0) || !RROutputSetCrtcs(output, &crtc, 1) @@ -604,6 +607,16 @@ CursorForDevice(DeviceIntPtr pDev) { return NULL; } +Bool lorieChangeScreenName(unused ClientPtr pClient, void *closure) { + RROutputPtr output = RRFirstOutput(pScreenPtr); + memset(output->name, 0, 1024); + strncpy(output->name, closure, 1024); + output->name[1023] = '\0'; + output->nameLength = strlen(output->name); + free(closure); + return TRUE; +} + Bool lorieChangeWindow(unused ClientPtr pClient, void *closure) { jobject surface = (jobject) closure; renderer_set_window(pvfb->env, surface, pvfb->root.buffer); diff --git a/app/src/main/cpp/lorie/android.c b/app/src/main/cpp/lorie/android.c index f4eabf474..f6aeaee8f 100644 --- a/app/src/main/cpp/lorie/android.c +++ b/app/src/main/cpp/lorie/android.c @@ -250,7 +250,12 @@ Java_com_termux_x11_CmdEntryPoint_start(JNIEnv *env, unused jclass cls, jobjectA } JNIEXPORT void JNICALL -Java_com_termux_x11_CmdEntryPoint_windowChanged(JNIEnv *env, unused jobject cls, jobject surface) { +Java_com_termux_x11_CmdEntryPoint_windowChanged(JNIEnv *env, unused jobject cls, jobject surface, jstring jname) { + const char *name = !jname ? NULL : (*env)->GetStringUTFChars(env, jname, JNI_FALSE); + QueueWorkProc(lorieChangeScreenName, NULL, name ? strndup(name, 1024) : strdup("screen")); + if (name) + (*env)->ReleaseStringUTFChars(env, jname, name); + QueueWorkProc(lorieChangeWindow, NULL, surface ? (*env)->NewGlobalRef(env, surface) : NULL); } @@ -263,7 +268,7 @@ static Bool sendConfigureNotify(unused ClientPtr pClient, void *closure) { return TRUE; } -static Bool handleClipboardAnnounce(unused ClientPtr pClient, void *closure) { +static Bool handleClipboardAnnounce(unused ClientPtr pClient, __unused void *closure) { // This must be done only on X server thread. lorieHandleClipboardAnnounce(); return TRUE; @@ -553,7 +558,7 @@ Java_com_termux_x11_LorieView_setClipboardSyncEnabled(unused JNIEnv* env, unused } JNIEXPORT void JNICALL -Java_com_termux_x11_LorieView_sendClipboardAnnounce(JNIEnv *env, jobject thiz) { +Java_com_termux_x11_LorieView_sendClipboardAnnounce(JNIEnv *env, __unused jobject thiz) { if (conn_fd != -1) { lorieEvent e = { .type = EVENT_CLIPBOARD_ANNOUNCE }; write(conn_fd, &e, sizeof(e)); diff --git a/app/src/main/cpp/lorie/lorie.h b/app/src/main/cpp/lorie/lorie.h index 8071a8137..6e3d4c788 100644 --- a/app/src/main/cpp/lorie/lorie.h +++ b/app/src/main/cpp/lorie/lorie.h @@ -6,6 +6,7 @@ #define unused __attribute__((unused)) void lorieSetVM(JavaVM* vm); +Bool lorieChangeScreenName(ClientPtr pClient, void *closure); Bool lorieChangeWindow(ClientPtr pClient, void *closure); void lorieConfigureNotify(int width, int height, int framerate); void lorieEnableClipboardSync(Bool enable); diff --git a/app/src/main/java/com/termux/x11/CmdEntryPoint.java b/app/src/main/java/com/termux/x11/CmdEntryPoint.java index 19d450a65..5a8a6bb45 100644 --- a/app/src/main/java/com/termux/x11/CmdEntryPoint.java +++ b/app/src/main/java/com/termux/x11/CmdEntryPoint.java @@ -211,7 +211,7 @@ public static Context createContext() { } public static native boolean start(String[] args); - public native void windowChanged(Surface surface); + public native void windowChanged(Surface surface, String name); public native ParcelFileDescriptor getXConnection(); public native ParcelFileDescriptor getLogcatOutput(); private static native boolean connected(); diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 9b7cb195e..10cfbe96a 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -69,7 +69,6 @@ import com.termux.x11.input.TouchInputHandler; import com.termux.x11.utils.FullscreenWorkaround; import com.termux.x11.utils.KeyInterceptor; -import com.termux.x11.utils.SamsungDexUtils; import com.termux.x11.utils.TermuxX11ExtraKeys; import com.termux.x11.utils.X11ToolbarViewPager; @@ -234,7 +233,7 @@ public void swipeDown() { if (service != null) { try { - service.windowChanged(sfc); + service.windowChanged(sfc, lorieView.getDisplay() != null ? lorieView.getDisplay().getName() : "screen"); } catch (RemoteException e) { Log.e("MainActivity", "failed to send windowChanged request", e); } From 8cf06a48288be626ce823a4a74d68479df5fadef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 30 May 2024 08:18:19 +0300 Subject: [PATCH 027/107] Bump androidx.lifecycle:lifecycle-viewmodel-ktx from 2.8.0 to 2.8.1 updated-dependencies: - dependency-name: androidx.lifecycle:lifecycle-viewmodel-ktx dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index d85442637..4753a7a65 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -47,7 +47,7 @@ android { dependencies { //noinspection DifferentStdlibGradleVersion implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.0" - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.1' implementation 'androidx.preference:preference:1.2.1' compileOnly project(':shell-loader:stub') } From 33b68ed0c09a6e76b8b30aa67ac430ba3f1af8a8 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 30 May 2024 08:20:57 +0300 Subject: [PATCH 028/107] ci: replacing `wrapper-validation-action` with `wrapper-validation` --- .github/workflows/debug_build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/debug_build.yml b/.github/workflows/debug_build.yml index e4c672165..f337a92d0 100644 --- a/.github/workflows/debug_build.yml +++ b/.github/workflows/debug_build.yml @@ -27,7 +27,7 @@ jobs: with: submodules: recursive - name: Validation - uses: gradle/wrapper-validation-action@v3 + uses: gradle/actions/wrapper-validation@v3 - name: Java setup uses: actions/setup-java@v4 From 941402e5eb502f3857b05b2e92736ce152899c6f Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 30 May 2024 10:16:38 +0300 Subject: [PATCH 029/107] Adding info about killing activity and X server to README.md [no ci] --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 08df0cb84..571417853 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,20 @@ export CLASSPATH=$(/system/bin/pm path com.termux.x11 | cut -d: -f2) /system/bin/app_process / com.termux.x11.CmdEntryPoint :0 ``` +### Force stopping X server (running in termux background, not an activity) + +termux-x11's X server runs in process with name "app_process", not "termux-x11". But you can kill it by searching "com.termux.x11" in commandline. +So killing it will look like +``` +pkill -f com.termux.x11 +``` + +### Closing Android activity (running in foreground, not X server) + +``` +am broadcast -a com.termux.x11.ACTION_STOP -p com.termux.x11 +``` + ### Logs If you need to obtain logs from the `com.termux.x11` application, set the `TERMUX_X11_DEBUG` environment variable to 1, like this: From 676f14e35d717a61a7f3257d55ac52e0989c259e Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Sat, 1 Jun 2024 21:39:33 +0300 Subject: [PATCH 030/107] Fixing shell-loader's message and link --- shell-loader/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shell-loader/build.gradle b/shell-loader/build.gradle index 058122787..b012344a8 100755 --- a/shell-loader/build.gradle +++ b/shell-loader/build.gradle @@ -19,9 +19,9 @@ android.defaultConfig.buildConfigField "String", "logTag", "\"Termux:X11 loader\ android.defaultConfig.buildConfigField "int", "SIGNATURE", String.valueOf(Arrays.hashCode(keyStore.getCertificate(signingConfig.keyAlias).getEncoded())) android.defaultConfig.buildConfigField "String", "CLASS_ID", "\"com.termux.x11.CmdEntryPoint\"" android.defaultConfig.buildConfigField "String", "packageNotInstalledErrorText", - """\"Termux:X11 application is not found.\" + - \"You should choose latest successful workflow here: https://github.com/termux/termux-x11/actions/workflows/debug_build.yml\" + - \"After this you should download \\"termux-x11-ARCH-debug\\" or \\"termux-x11-universal-debug\\" artifact and install apk contained in this zip file.\"""" + """\"Termux:X11 application is not found.\\n\" + + \"You should choose latest release here: \\n https://github.com/termux/termux-x11/releases/tag/nightly\\n\" + + \"After this you should download \\"termux-x11-ARCH-debug.apk\\" or \\"termux-x11-universal-debug.apk\\" and install it.\\n\"""" android.defaultConfig.buildConfigField "String", "packageSignatureMismatchErrorText", "\"Signature verification of target application " + android.namespace + " failed.\\nPlease, reinstall both termux-x11 package and Termux:X11 application from the same source\"" android.defaultConfig.buildConfigField "String", "COMMIT", "\"" + ("git rev-parse HEAD\n".execute().getText().trim() ?: (System.getenv('CURRENT_COMMIT') ?: "NO_COMMIT")) + "\"" From 99efcc2e41f886c584ee7db8c0622d9294113df4 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Sun, 2 Jun 2024 00:27:53 +0300 Subject: [PATCH 031/107] [loader]: Fixing application id broken by changing android.namespace. --- shell-loader/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/shell-loader/build.gradle b/shell-loader/build.gradle index b012344a8..9c18cd558 100755 --- a/shell-loader/build.gradle +++ b/shell-loader/build.gradle @@ -15,6 +15,7 @@ def signingConfig = project(':app').android.signingConfigs.debug def keyStore = java.security.KeyStore.getInstance(java.security.KeyStore.getDefaultType()) keyStore.load(new FileInputStream(signingConfig.storeFile), signingConfig.keyPassword.toCharArray()) +android.defaultConfig.buildConfigField "String", "APPLICATION_ID", "\"com.termux.x11\"" android.defaultConfig.buildConfigField "String", "logTag", "\"Termux:X11 loader\"" android.defaultConfig.buildConfigField "int", "SIGNATURE", String.valueOf(Arrays.hashCode(keyStore.getCertificate(signingConfig.keyAlias).getEncoded())) android.defaultConfig.buildConfigField "String", "CLASS_ID", "\"com.termux.x11.CmdEntryPoint\"" From 47bcda181d6bb685e70332a3d4db154bff7bf666 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jun 2024 14:31:10 +0300 Subject: [PATCH 032/107] Bump com.android.tools.build:gradle from 8.4.1 to 8.4.2 [no ci] updated-dependencies: - dependency-name: com.android.tools.build:gradle dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e0af7c7e1..a39fa6754 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { dependencies { // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files - classpath 'com.android.tools.build:gradle:8.4.1' + classpath 'com.android.tools.build:gradle:8.4.2' } } From b4d7a98551dd14e71c09621e0493d48ed08bf6d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Jun 2024 14:31:29 +0300 Subject: [PATCH 033/107] Bump androidx.lifecycle:lifecycle-viewmodel-ktx from 2.8.1 to 2.8.2 [no ci] updated-dependencies: - dependency-name: androidx.lifecycle:lifecycle-viewmodel-ktx dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 4753a7a65..c138e30da 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -47,7 +47,7 @@ android { dependencies { //noinspection DifferentStdlibGradleVersion implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.0" - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.1' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.2' implementation 'androidx.preference:preference:1.2.1' compileOnly project(':shell-loader:stub') } From 65a3a513226b4e2a3af4f99191a859f789aec639 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 13 Jun 2024 14:31:47 +0300 Subject: [PATCH 034/107] Update Gradle Wrapper from 8.7 to 8.8. [no ci] Signed-off-by: gradle-update-robot Co-authored-by: gradle-update-robot --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fcbbad6dd..515ab9d5f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=194717442575a6f96e1c1befa2c30e9a4fc90f701d7aee33eb879b79e7ff05c0 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip +distributionSha256Sum=f8b4f4772d302c8ff580bc40d0f56e715de69b163546944f787c87abf209c961 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a426..b740cf133 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. From 5afb5e097af31af4ec13f85bc8f9577b03a12091 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 07:38:55 +0300 Subject: [PATCH 035/107] Bump com.android.tools.build:gradle from 8.4.2 to 8.5.0 [no ci] Bumps com.android.tools.build:gradle from 8.4.2 to 8.5.0. --- updated-dependencies: - dependency-name: com.android.tools.build:gradle dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a39fa6754..b69d11d91 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { dependencies { // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files - classpath 'com.android.tools.build:gradle:8.4.2' + classpath 'com.android.tools.build:gradle:8.5.0' } } From f7a4bd8ad6475ec7ec493aa88be5411def32a9c8 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Tue, 4 Jun 2024 13:55:05 +0300 Subject: [PATCH 036/107] Experimental full stylus event support Fixes extra key bar behaviour on some devices Implementing dialog with nested preferences for Preferences activity --- app/src/main/cpp/lorie/InitInput.c | 106 +++++++++++-- app/src/main/cpp/lorie/android.c | 129 +++++++++++++--- app/src/main/cpp/lorie/dri3.c | 6 +- app/src/main/cpp/lorie/lorie.h | 2 +- .../termux/extrakeys/ExtraKeysView.java | 10 +- .../java/com/termux/x11/LoriePreferences.java | 83 ++++++++-- .../main/java/com/termux/x11/LorieView.java | 5 + .../java/com/termux/x11/MainActivity.java | 87 +++++------ .../termux/x11/input/InputEventSender.java | 7 + .../java/com/termux/x11/input/InputStub.java | 2 + .../termux/x11/input/TouchInputHandler.java | 142 +++++++++++++++++- .../termux/x11/utils/TermuxX11ExtraKeys.java | 14 +- .../termux/x11/utils/X11ToolbarViewPager.java | 7 +- app/src/main/res/layout/preference.xml | 113 ++++++++++++++ app/src/main/res/values/styles.xml | 39 ++++- app/src/main/res/xml/preferences.xml | 78 ++++++---- 16 files changed, 682 insertions(+), 148 deletions(-) create mode 100644 app/src/main/res/layout/preference.xml diff --git a/app/src/main/cpp/lorie/InitInput.c b/app/src/main/cpp/lorie/InitInput.c index 6b3254860..6b509ff8b 100644 --- a/app/src/main/cpp/lorie/InitInput.c +++ b/app/src/main/cpp/lorie/InitInput.c @@ -31,15 +31,17 @@ from The Open Group. #include "scrnintstr.h" #include "inputstr.h" #include +#include #include "mipointer.h" #include "xkbsrv.h" #include "xserver-properties.h" #include "exevents.h" #include "renderer.h" -#define unused __attribute__((unused)) +#define XI_PEN "TERMUX-X11 PEN" +#define XI_ERASER "TERMUX-X11 ERASER" -unused DeviceIntPtr lorieMouse, lorieTouch, lorieKeyboard; +__unused DeviceIntPtr lorieMouse, lorieTouch, lorieKeyboard, loriePen, lorieEraser; void ProcessInputEvents(void) { @@ -47,7 +49,7 @@ ProcessInputEvents(void) { } void -DDXRingBell(unused int volume, unused int pitch, unused int duration) {} +DDXRingBell(__unused int volume, __unused int pitch, __unused int duration) {} static int lorieKeybdProc(DeviceIntPtr pDevice, int onoff) { @@ -72,8 +74,7 @@ lorieKeybdProc(DeviceIntPtr pDevice, int onoff) { } static Bool -lorieInitPointerButtons(DeviceIntPtr device) -{ +lorieInitPointerButtons(DeviceIntPtr device) { #define NBUTTONS 10 BYTE map[NBUTTONS + 1]; int i; @@ -98,9 +99,8 @@ lorieInitPointerButtons(DeviceIntPtr device) #undef NBUTTONS } -unused static int -lorieMouseProc(DeviceIntPtr device, int what) -{ +__unused static int +lorieMouseProc(DeviceIntPtr device, int what) { #define NAXES 4 Atom axes_labels[NAXES] = { 0 }; @@ -142,8 +142,7 @@ lorieMouseProc(DeviceIntPtr device, int what) } static int -lorieTouchProc(DeviceIntPtr device, int what) -{ +lorieTouchProc(DeviceIntPtr device, int what) { #define NTOUCHPOINTS 20 #define NBUTTONS 1 #define NAXES 2 @@ -185,8 +184,90 @@ lorieTouchProc(DeviceIntPtr device, int what) #undef NTOUCHPOINTS } +static int +lorieStylusProc(DeviceIntPtr device, int what) { +#define NBUTTONS 3 +#define NAXES 6 + Atom btn_labels[NBUTTONS] = { 0 }; + Atom axes_labels[NAXES] = { 0 }; + BYTE map[NBUTTONS + 1] = { 0 }; + int i; + + switch (what) { + case DEVICE_INIT: + device->public.on = FALSE; + + for (i = 1; i <= NBUTTONS; i++) + map[i] = i; + + axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_X); + axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_Y); + axes_labels[2] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_PRESSURE); + axes_labels[3] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_TILT_X); + axes_labels[4] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_TILT_Y); + axes_labels[5] = XIGetKnownProperty(AXIS_LABEL_PROP_ABS_WHEEL); + + /* Valuators - match the xf86-input-wacom ranges */ + if (!InitValuatorClassDeviceStruct(device, NAXES, axes_labels, GetMotionHistorySize(), Absolute) + || !InitValuatorAxisStruct(device, 0, axes_labels[0], 0, 0x3FFFF, 10000, 0, 10000, Absolute) + || !InitValuatorAxisStruct(device, 1, axes_labels[1], 0, 0x3FFFF, 10000, 0, 10000, Absolute) + || !InitValuatorAxisStruct(device, 2, axes_labels[2], 0, 0xFFFF, 1, 0, 1, Absolute) // pressure + || !InitValuatorAxisStruct(device, 3, axes_labels[3], -64, 63, 57, 0, 57, Absolute) // tilt x + || !InitValuatorAxisStruct(device, 4, axes_labels[4], -64, 63, 57, 0, 57, Absolute) // tilt y + || !InitValuatorAxisStruct(device, 5, axes_labels[5], -900, 899, 1, 0, 1, Absolute) // abs wheel (airbrush) or rotation (artpen) + || !InitPtrFeedbackClassDeviceStruct(device, (PtrCtrlProcPtr) NoopDDA) + || !InitButtonClassDeviceStruct(device, NBUTTONS, btn_labels, map)) + return BadValue; + + return Success; + + case DEVICE_ON: + device->public.on = TRUE; + return Success; + + case DEVICE_OFF: + case DEVICE_CLOSE: + device->public.on = FALSE; + return Success; + } + + return BadMatch; +#undef NAXES +#undef NBUTTONS +} + +void +lorieSetStylusEnabled(Bool enabled) { + __android_log_print(ANDROID_LOG_DEBUG, "LorieNative", "Requested stylus: %d, current loriePen %p, current lorieEraser %p\n", enabled, loriePen, lorieEraser); + if (enabled) { + if (loriePen == NULL) { + loriePen = AddInputDevice(serverClient, lorieStylusProc, TRUE); + AssignTypeAndName(loriePen, MakeAtom(XI_PEN, sizeof(XI_PEN) - 1, TRUE), "Lorie pen"); + ActivateDevice(loriePen, FALSE); + EnableDevice(loriePen, TRUE); + AttachDevice(NULL, loriePen, inputInfo.pointer); + } + if (lorieEraser == NULL) { + lorieEraser = AddInputDevice(serverClient, lorieStylusProc, TRUE); + AssignTypeAndName(lorieEraser, MakeAtom(XI_ERASER, sizeof(XI_ERASER) - 1, TRUE), "Lorie eraser"); + ActivateDevice(lorieEraser, FALSE); + EnableDevice(lorieEraser, TRUE); + AttachDevice(NULL, lorieEraser, inputInfo.pointer); + } + } else { + if (loriePen != NULL) { + RemoveDevice(loriePen, TRUE); + loriePen = NULL; + } + if (lorieEraser != NULL) { + RemoveDevice(lorieEraser, TRUE); + lorieEraser = NULL; + } + } +} + void -InitInput(unused int argc, unused char *argv[]) { +InitInput(__unused int argc, __unused char *argv[]) { lorieMouse = AddInputDevice(serverClient, lorieMouseProc, TRUE); lorieTouch = AddInputDevice(serverClient, lorieTouchProc, TRUE); lorieKeyboard = AddInputDevice(serverClient, lorieKeybdProc, TRUE); @@ -197,7 +278,6 @@ InitInput(unused int argc, unused char *argv[]) { } void -CloseInput(void) -{ +CloseInput(void) { mieqFini(); } diff --git a/app/src/main/cpp/lorie/android.c b/app/src/main/cpp/lorie/android.c index f6aeaee8f..2e4527b39 100644 --- a/app/src/main/cpp/lorie/android.c +++ b/app/src/main/cpp/lorie/android.c @@ -29,7 +29,7 @@ static int argc = 0; static char** argv = NULL; static int conn_fd = -1; extern char *__progname; // NOLINT(bugprone-reserved-identifier) -extern DeviceIntPtr lorieMouse, lorieTouch, lorieKeyboard; +extern DeviceIntPtr lorieMouse, lorieTouch, lorieKeyboard, loriePen, lorieEraser; extern ScreenPtr pScreenPtr; extern int ucs2keysym(long ucs); void lorieKeysymKeyboardEvent(KeySym keysym, int down); @@ -42,6 +42,8 @@ typedef enum { EVENT_TOUCH, EVENT_MOUSE, EVENT_KEY, + EVENT_STYLUS, + EVENT_STYLUS_ENABLE, EVENT_UNICODE, EVENT_CLIPBOARD_ENABLE, EVENT_CLIPBOARD_ANNOUNCE, @@ -68,6 +70,17 @@ typedef union { uint16_t key; uint8_t state; } key; + struct { + uint8_t t; + float x, y; + uint16_t pressure; + int8_t tilt_x, tilt_y; + int16_t orientation; + uint8_t buttons, eraser; + } stylus; + struct { + uint8_t t, enable; + } stylusEnable; struct { uint8_t t; uint32_t code; @@ -93,7 +106,7 @@ static struct { jmethodID toString; } CharBuffer = {0}; -static void* startServer(unused void* cookie) { +static void* startServer(__unused void* cookie) { lorieSetVM((JavaVM*) cookie); char* envp[] = { NULL }; exit(dix_main(argc, (char**) argv, envp)); @@ -127,7 +140,7 @@ static jclass FindMethodOrDie(JNIEnv *env, jclass clazz, const char* name, const } JNIEXPORT jboolean JNICALL -Java_com_termux_x11_CmdEntryPoint_start(JNIEnv *env, unused jclass cls, jobjectArray args) { +Java_com_termux_x11_CmdEntryPoint_start(JNIEnv *env, __unused jclass cls, jobjectArray args) { pthread_t t; JavaVM* vm = NULL; // execv's argv array is a bit incompatible with Java's String[], so we do some converting here... @@ -250,7 +263,7 @@ Java_com_termux_x11_CmdEntryPoint_start(JNIEnv *env, unused jclass cls, jobjectA } JNIEXPORT void JNICALL -Java_com_termux_x11_CmdEntryPoint_windowChanged(JNIEnv *env, unused jobject cls, jobject surface, jstring jname) { +Java_com_termux_x11_CmdEntryPoint_windowChanged(JNIEnv *env, __unused jobject cls, jobject surface, jstring jname) { const char *name = !jname ? NULL : (*env)->GetStringUTFChars(env, jname, JNI_FALSE); QueueWorkProc(lorieChangeScreenName, NULL, name ? strndup(name, 1024) : strdup("screen")); if (name) @@ -259,7 +272,7 @@ Java_com_termux_x11_CmdEntryPoint_windowChanged(JNIEnv *env, unused jobject cls, QueueWorkProc(lorieChangeWindow, NULL, surface ? (*env)->NewGlobalRef(env, surface) : NULL); } -static Bool sendConfigureNotify(unused ClientPtr pClient, void *closure) { +static Bool sendConfigureNotify(__unused ClientPtr pClient, void *closure) { // This must be done only on X server thread. lorieEvent* e = closure; __android_log_print(ANDROID_LOG_ERROR, "tx11-request", "window changed: %d %d", e->screenSize.width, e->screenSize.height); @@ -268,19 +281,20 @@ static Bool sendConfigureNotify(unused ClientPtr pClient, void *closure) { return TRUE; } -static Bool handleClipboardAnnounce(unused ClientPtr pClient, __unused void *closure) { +static Bool handleClipboardAnnounce(__unused ClientPtr pClient, __unused void *closure) { // This must be done only on X server thread. lorieHandleClipboardAnnounce(); return TRUE; } -static Bool handleClipboardData(unused ClientPtr pClient, void *closure) { +static Bool handleClipboardData(__unused ClientPtr pClient, void *closure) { // This must be done only on X server thread. lorieHandleClipboardData(closure); return TRUE; } void handleLorieEvents(int fd, __unused int ready, __unused void *ignored) { + DrawablePtr screenDrawable = &pScreenPtr->GetScreenPixmap(pScreenPtr)->drawable; ValuatorMask mask; lorieEvent e = {0}; valuator_mask_zero(&mask); @@ -306,8 +320,11 @@ void handleLorieEvents(int fd, __unused int ready, __unused void *ignored) { double x, y; DDXTouchPointInfoPtr touch = TouchFindByDDXID(lorieTouch, e.touch.id, FALSE); - x = (float) e.touch.x * 0xFFFF / (float) pScreenPtr->GetScreenPixmap(pScreenPtr)->drawable.width; - y = (float) e.touch.y * 0xFFFF / (float) pScreenPtr->GetScreenPixmap(pScreenPtr)->drawable.height; + x = (float) e.touch.x * 0xFFFF / (float) screenDrawable->width; + y = (float) e.touch.y * 0xFFFF / (float) screenDrawable->height; + + x = max(min(x, screenDrawable->width), 0); + y = max(min(y, screenDrawable->height), 0); // Avoid duplicating events if (touch && touch->active) { @@ -326,17 +343,61 @@ void handleLorieEvents(int fd, __unused int ready, __unused void *ignored) { if (e.touch.type == XI_TouchEnd && (!touch || !touch->active)) break; - __android_log_print(ANDROID_LOG_ERROR, "tx11-request", "touch event: %d %d %d %d", e.touch.type, e.touch.id, e.touch.x, e.touch.y); valuator_mask_set_double(&mask, 0, x); valuator_mask_set_double(&mask, 1, y); QueueTouchEvents(lorieTouch, e.touch.type, e.touch.id, 0, &mask); break; } + case EVENT_STYLUS: { + int button; + static int buttons_prev = 0; + uint32_t released, pressed, diff; + DeviceIntPtr device = e.stylus.eraser ? lorieEraser : loriePen; + if (!device) { + __android_log_print(ANDROID_LOG_DEBUG, "LorieNative", "got stylus event but device is not requested\n"); + break; + } + __android_log_print(ANDROID_LOG_DEBUG, "LorieNative", "got stylus event %f %f %d %d %d %d %p\n", e.stylus.x, e.stylus.y, e.stylus.pressure, e.stylus.tilt_x, e.stylus.tilt_y, e.stylus.orientation, device); + + valuator_mask_set_double(&mask, 0, max(min(e.stylus.x, screenDrawable->width), 0)); + valuator_mask_set_double(&mask, 1, max(min(e.stylus.y, screenDrawable->height), 0)); + valuator_mask_set_double(&mask, 2, e.stylus.pressure); + valuator_mask_set_double(&mask, 3, e.stylus.tilt_x); + valuator_mask_set_double(&mask, 4, e.stylus.tilt_y); + valuator_mask_set_double(&mask, 5, e.stylus.orientation); + QueuePointerEvents(device, MotionNotify, 0, POINTER_ABSOLUTE | POINTER_DESKTOP, &mask); + + diff = buttons_prev ^ e.stylus.buttons; + released = diff & ~e.stylus.buttons; + pressed = diff & e.stylus.buttons; + + button = 1; + for (int i=0; i<3; i++) { + if (released & 0x1) + QueuePointerEvents(device, ButtonRelease, button, POINTER_RELATIVE, NULL); + if (pressed & 0x1) + QueuePointerEvents(device, ButtonPress, button, POINTER_RELATIVE, NULL); + button++; + released >>= 1; + pressed >>= 1; + } + buttons_prev = e.stylus.buttons; + + break; + } + case EVENT_STYLUS_ENABLE: { + lorieSetStylusEnabled(e.stylusEnable.enable); + break; + } case EVENT_MOUSE: { int flags; switch(e.mouse.detail) { case 0: // BUTTON_UNDEFINED flags = (e.mouse.relative) ? POINTER_RELATIVE | POINTER_ACCELERATE : POINTER_ABSOLUTE | POINTER_SCREEN | POINTER_NORAW; + if (!e.mouse.relative) { + e.mouse.x = max(0, min(e.mouse.x, screenDrawable->width)); + e.mouse.y = max(0, min(e.mouse.y, screenDrawable->height)); + } valuator_mask_set_double(&mask, 0, (double) e.mouse.x); valuator_mask_set_double(&mask, 1, (double) e.mouse.y); QueuePointerEvents(lorieMouse, MotionNotify, 0, flags, &mask); @@ -407,14 +468,14 @@ void lorieRequestClipboard(void) { } } -static Bool addFd(unused ClientPtr pClient, void *closure) { +static Bool addFd(__unused ClientPtr pClient, void *closure) { InputThreadRegisterDev((int) (int64_t) closure, handleLorieEvents, NULL); conn_fd = (int) (int64_t) closure; return TRUE; } JNIEXPORT jobject JNICALL -Java_com_termux_x11_CmdEntryPoint_getXConnection(JNIEnv *env, unused jobject cls) { +Java_com_termux_x11_CmdEntryPoint_getXConnection(JNIEnv *env, __unused jobject cls) { int client[2]; jclass ParcelFileDescriptorClass = (*env)->FindClass(env, "android/os/ParcelFileDescriptor"); jmethodID adoptFd = (*env)->GetStaticMethodID(env, ParcelFileDescriptorClass, "adoptFd", "(I)Landroid/os/ParcelFileDescriptor;"); @@ -435,7 +496,7 @@ void* logcatThread(void *arg) { } JNIEXPORT jobject JNICALL -Java_com_termux_x11_CmdEntryPoint_getLogcatOutput(JNIEnv *env, unused jobject cls) { +Java_com_termux_x11_CmdEntryPoint_getLogcatOutput(JNIEnv *env, __unused jobject cls) { jclass ParcelFileDescriptorClass = (*env)->FindClass(env, "android/os/ParcelFileDescriptor"); jmethodID adoptFd = (*env)->GetStaticMethodID(env, ParcelFileDescriptorClass, "adoptFd", "(I)Landroid/os/ParcelFileDescriptor;"); const char *debug = getenv("TERMUX_X11_DEBUG"); @@ -474,7 +535,7 @@ static inline void checkConnection(JNIEnv* env) { } JNIEXPORT void JNICALL -Java_com_termux_x11_LorieView_connect(unused JNIEnv* env, unused jobject cls, jint fd) { +Java_com_termux_x11_LorieView_connect(__unused JNIEnv* env, __unused jobject cls, jint fd) { if (!Charset.self) { // Init clipboard-related JNI stuff Charset.self = FindClassOrDie(env, "java/nio/charset/Charset"); @@ -529,7 +590,7 @@ Java_com_termux_x11_LorieView_handleXEvents(JNIEnv *env, jobject thiz) { } JNIEXPORT void JNICALL -Java_com_termux_x11_LorieView_startLogcat(JNIEnv *env, unused jobject cls, jint fd) { +Java_com_termux_x11_LorieView_startLogcat(JNIEnv *env, __unused jobject cls, jint fd) { log(DEBUG, "Starting logcat with output to given fd"); switch(fork()) { @@ -549,7 +610,7 @@ Java_com_termux_x11_LorieView_startLogcat(JNIEnv *env, unused jobject cls, jint } JNIEXPORT void JNICALL -Java_com_termux_x11_LorieView_setClipboardSyncEnabled(unused JNIEnv* env, unused jobject cls, jboolean enable, __unused jboolean ignored) { +Java_com_termux_x11_LorieView_setClipboardSyncEnabled(__unused JNIEnv* env, __unused jobject cls, jboolean enable, __unused jboolean ignored) { if (conn_fd != -1) { lorieEvent e = { .clipboardEnable = { .t = EVENT_CLIPBOARD_ENABLE, .enable = enable } }; write(conn_fd, &e, sizeof(e)); @@ -567,7 +628,7 @@ Java_com_termux_x11_LorieView_sendClipboardAnnounce(JNIEnv *env, __unused jobjec } JNIEXPORT void JNICALL -Java_com_termux_x11_LorieView_sendClipboardEvent(JNIEnv *env, unused jobject thiz, jbyteArray text) { +Java_com_termux_x11_LorieView_sendClipboardEvent(JNIEnv *env, __unused jobject thiz, jbyteArray text) { if (conn_fd != -1 && text) { jsize length = (*env)->GetArrayLength(env, text); jbyte* str = (*env)->GetByteArrayElements(env, text, NULL); @@ -580,7 +641,7 @@ Java_com_termux_x11_LorieView_sendClipboardEvent(JNIEnv *env, unused jobject thi } JNIEXPORT void JNICALL -Java_com_termux_x11_LorieView_sendWindowChange(unused JNIEnv* env, unused jobject cls, jint width, jint height, jint framerate) { +Java_com_termux_x11_LorieView_sendWindowChange(__unused JNIEnv* env, __unused jobject cls, jint width, jint height, jint framerate) { if (conn_fd != -1) { lorieEvent e = { .screenSize = { .t = EVENT_SCREEN_SIZE, .width = width, .height = height, .framerate = framerate } }; write(conn_fd, &e, sizeof(e)); @@ -589,7 +650,7 @@ Java_com_termux_x11_LorieView_sendWindowChange(unused JNIEnv* env, unused jobjec } JNIEXPORT void JNICALL -Java_com_termux_x11_LorieView_sendMouseEvent(unused JNIEnv* env, unused jobject cls, jfloat x, jfloat y, jint which_button, jboolean button_down, jboolean relative) { +Java_com_termux_x11_LorieView_sendMouseEvent(__unused JNIEnv* env, __unused jobject cls, jfloat x, jfloat y, jint which_button, jboolean button_down, jboolean relative) { if (conn_fd != -1) { lorieEvent e = { .mouse = { .t = EVENT_MOUSE, .x = x, .y = y, .detail = which_button, .down = button_down, .relative = relative } }; write(conn_fd, &e, sizeof(e)); @@ -598,7 +659,7 @@ Java_com_termux_x11_LorieView_sendMouseEvent(unused JNIEnv* env, unused jobject } JNIEXPORT void JNICALL -Java_com_termux_x11_LorieView_sendTouchEvent(unused JNIEnv* env, unused jobject cls, jint action, jint id, jint x, jint y) { +Java_com_termux_x11_LorieView_sendTouchEvent(__unused JNIEnv* env, __unused jobject cls, jint action, jint id, jint x, jint y) { if (conn_fd != -1 && action != -1) { lorieEvent e = { .touch = { .t = EVENT_TOUCH, .type = action, .id = id, .x = x, .y = y } }; write(conn_fd, &e, sizeof(e)); @@ -606,8 +667,28 @@ Java_com_termux_x11_LorieView_sendTouchEvent(unused JNIEnv* env, unused jobject } } +JNIEXPORT void JNICALL +Java_com_termux_x11_LorieView_sendStylusEvent(JNIEnv *env, __unused jobject thiz, jfloat x, jfloat y, + jint pressure, jint tilt_x, jint tilt_y, + jint orientation, jint buttons, jboolean eraser) { + if (conn_fd != -1) { + lorieEvent e = { .stylus = { .t = EVENT_STYLUS, .x = x, .y = y, .pressure = pressure, .tilt_x = tilt_x, .tilt_y = tilt_y, .orientation = orientation, .buttons = buttons, .eraser = eraser } }; + write(conn_fd, &e, sizeof(e)); + checkConnection(env); + } +} + +JNIEXPORT void JNICALL +Java_com_termux_x11_LorieView_requestStylusEnabled(JNIEnv *env, __unused jclass clazz, jboolean enabled) { + if (conn_fd != -1) { + lorieEvent e = { .stylusEnable = { .t = EVENT_STYLUS_ENABLE, .enable = enabled } }; + write(conn_fd, &e, sizeof(e)); + checkConnection(env); + } +} + JNIEXPORT jboolean JNICALL -Java_com_termux_x11_LorieView_sendKeyEvent(unused JNIEnv* env, unused jobject cls, jint scan_code, jint key_code, jboolean key_down) { +Java_com_termux_x11_LorieView_sendKeyEvent(__unused JNIEnv* env, __unused jobject cls, jint scan_code, jint key_code, jboolean key_down) { if (conn_fd != -1) { int code = (scan_code) ?: android_to_linux_keycode[key_code]; log(DEBUG, "Sending key: %d (%d %d %d)", code + 8, scan_code, key_code, key_down); @@ -620,7 +701,7 @@ Java_com_termux_x11_LorieView_sendKeyEvent(unused JNIEnv* env, unused jobject cl } JNIEXPORT void JNICALL -Java_com_termux_x11_LorieView_sendTextEvent(JNIEnv *env, unused jobject thiz, jbyteArray text) { +Java_com_termux_x11_LorieView_sendTextEvent(JNIEnv *env, __unused jobject thiz, jbyteArray text) { if (conn_fd != -1 && text) { jsize length = (*env)->GetArrayLength(env, text); jbyte *str = (*env)->GetByteArrayElements(env, text, NULL); @@ -655,7 +736,7 @@ Java_com_termux_x11_LorieView_sendTextEvent(JNIEnv *env, unused jobject thiz, jb } JNIEXPORT void JNICALL -Java_com_termux_x11_LorieView_sendUnicodeEvent(JNIEnv *env, unused jobject thiz, jint code) { +Java_com_termux_x11_LorieView_sendUnicodeEvent(JNIEnv *env, __unused jobject thiz, jint code) { if (conn_fd != -1) { log(DEBUG, "Sending unicode event: %lc (U+%X)", code, code); lorieEvent e = { .unicode = { .t = EVENT_UNICODE, .code = code } }; @@ -675,7 +756,7 @@ void exit(int code) { #if 1 // It is needed to redirect stderr to logcat -static void* stderrToLogcatThread(unused void* cookie) { +static void* stderrToLogcatThread(__unused void* cookie) { FILE *fp; int p[2]; size_t len; diff --git a/app/src/main/cpp/lorie/dri3.c b/app/src/main/cpp/lorie/dri3.c index f508528c5..81594d953 100644 --- a/app/src/main/cpp/lorie/dri3.c +++ b/app/src/main/cpp/lorie/dri3.c @@ -431,7 +431,7 @@ lorieDestroyPixmap(PixmapPtr pPixmap) { } static PixmapPtr loriePixmapFromFds(ScreenPtr screen, CARD8 num_fds, const int *fds, CARD16 width, CARD16 height, - const CARD32 *strides, const CARD32 *offsets, CARD8 depth, unused CARD8 bpp, CARD64 modifier) { + const CARD32 *strides, const CARD32 *offsets, CARD8 depth, __unused CARD8 bpp, CARD64 modifier) { const CARD64 AHARDWAREBUFFER_SOCKET_FD = 1255; const CARD64 RAW_MMAPPABLE_FD = 1274; PixmapPtr pixmap = NullPixmap; @@ -535,13 +535,13 @@ static PixmapPtr loriePixmapFromFds(ScreenPtr screen, CARD8 num_fds, const int * return NULL; } -static int lorieGetFormats(unused ScreenPtr screen, CARD32 *num_formats, CARD32 **formats) { +static int lorieGetFormats(__unused ScreenPtr screen, CARD32 *num_formats, CARD32 **formats) { *num_formats = 0; *formats = NULL; return TRUE; } -static int lorieGetModifiers(unused ScreenPtr screen, unused uint32_t format, uint32_t *num_modifiers, uint64_t **modifiers) { +static int lorieGetModifiers(__unused ScreenPtr screen, __unused uint32_t format, uint32_t *num_modifiers, uint64_t **modifiers) { *num_modifiers = 0; *modifiers = NULL; return TRUE; diff --git a/app/src/main/cpp/lorie/lorie.h b/app/src/main/cpp/lorie/lorie.h index 6e3d4c788..865864068 100644 --- a/app/src/main/cpp/lorie/lorie.h +++ b/app/src/main/cpp/lorie/lorie.h @@ -3,7 +3,6 @@ #include #include #include "linux/input-event-codes.h" -#define unused __attribute__((unused)) void lorieSetVM(JavaVM* vm); Bool lorieChangeScreenName(ClientPtr pClient, void *closure); @@ -16,6 +15,7 @@ void lorieRequestClipboard(void); void lorieHandleClipboardAnnounce(void); void lorieHandleClipboardData(const char* data); Bool lorieInitDri3(ScreenPtr pScreen); +void lorieSetStylusEnabled(Bool enabled); static int android_to_linux_keycode[304] = { [ 4 /* ANDROID_KEYCODE_BACK */] = KEY_ESC, diff --git a/app/src/main/java/com/termux/shared/termux/extrakeys/ExtraKeysView.java b/app/src/main/java/com/termux/shared/termux/extrakeys/ExtraKeysView.java index 9fda1b3a7..aae449d1a 100644 --- a/app/src/main/java/com/termux/shared/termux/extrakeys/ExtraKeysView.java +++ b/app/src/main/java/com/termux/shared/termux/extrakeys/ExtraKeysView.java @@ -34,6 +34,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.termux.x11.utils.TermuxX11ExtraKeys; + /** * A {@link View} showing extra keys (such as Escape, Ctrl, Alt) not normally available on an Android soft * keyboards. @@ -280,13 +282,11 @@ public Map getDefaultSpecialButtons(ExtraKeys /** * Reload this instance of {@link ExtraKeysView} with the info passed in {@code extraKeysInfo}. - * - * @param extraKeysInfo The {@link ExtraKeysInfo} that defines the necessary info for the extra keys. - * @param heightPx The height in pixels of the parent surrounding the {@link ExtraKeysView}. It must - * be a single child. */ @SuppressLint("ClickableViewAccessibility") - public void reload(ExtraKeysInfo extraKeysInfo, float heightPx) { + public void reload() { + TermuxX11ExtraKeys.setExtraKeys(); + ExtraKeysInfo extraKeysInfo = TermuxX11ExtraKeys.getExtraKeysInfo(); if (extraKeysInfo == null) return; diff --git a/app/src/main/java/com/termux/x11/LoriePreferences.java b/app/src/main/java/com/termux/x11/LoriePreferences.java index 532ba0896..f54de0821 100644 --- a/app/src/main/java/com/termux/x11/LoriePreferences.java +++ b/app/src/main/java/com/termux/x11/LoriePreferences.java @@ -39,12 +39,17 @@ import android.text.method.LinkMovementMethod; import android.util.Log; +import android.view.InputDevice; +import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; +import androidx.fragment.app.DialogFragment; + import com.termux.x11.utils.KeyInterceptor; import com.termux.x11.utils.SamsungDexUtils; import com.termux.x11.utils.TermuxX11ExtraKeys; @@ -52,13 +57,13 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; - import java.io.IOException; import java.io.StringWriter; import java.io.PrintWriter; import java.util.Arrays; import java.util.Map; import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.PatternSyntaxException; @SuppressWarnings("deprecation") @@ -83,7 +88,7 @@ public void onReceive(Context context, Intent intent) { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - loriePreferenceFragment = new LoriePreferenceFragment(); + loriePreferenceFragment = new LoriePreferenceFragment(null); getSupportFragmentManager().beginTransaction().replace(android.R.id.content, loriePreferenceFragment).commit(); ActionBar actionBar = getSupportActionBar(); @@ -121,6 +126,16 @@ public boolean onOptionsItemSelected(MenuItem item) { } public static class LoriePreferenceFragment extends PreferenceFragmentCompat implements OnPreferenceChangeListener, Preference.OnPreferenceClickListener { + final String preference; + /** @noinspection unused*/ // Used by `androidx.fragment.app.Fragment.instantiate`... + public LoriePreferenceFragment() { + this(null); + } + + public LoriePreferenceFragment(String preference) { + this.preference = preference; + } + @Override public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable String rootKey) { SharedPreferences p = getPreferenceManager().getSharedPreferences(); @@ -132,6 +147,23 @@ public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable S } addPreferencesFromResource(R.xml.preferences); + findPreference("showAdditionalKbd").setLayoutResource(R.layout.preference); + + // Hide what user should not see in this instance + PreferenceGroup g = getPreferenceScreen(); + if (preference != null) { + for (int i=0; i < g.getPreferenceCount(); i++) { + if (g.getPreference(i) instanceof PreferenceGroup) { + g.getPreference(i).setVisible(g.getPreference(i).getKey().contentEquals(preference)); + } + } + } else { + for (int i=0; i < g.getPreferenceCount(); i++) { + if (g.getPreference(i) instanceof PreferenceGroup) { + g.getPreference(i).setVisible(!g.getPreference(i).getKey().contentEquals("ekbar")); + } + } + } } @SuppressWarnings("ConstantConditions") @@ -180,6 +212,21 @@ void updatePreferencesLayout() { findPreference("scaleTouchpad").setVisible("1".equals(p.getString("touchMode", "1")) && !"native".equals(p.getString("displayResolutionMode", "native"))); findPreference("showMouseHelper").setEnabled("1".equals(p.getString("touchMode", "1"))); + AtomicBoolean stylusAvailable = new AtomicBoolean(false); + Arrays.stream(InputDevice.getDeviceIds()) + .mapToObj(InputDevice::getDevice) + .filter(Objects::nonNull) + .forEach((device) -> { + //noinspection DataFlowIssue + if (device.supportsSource(InputDevice.SOURCE_STYLUS)) + stylusAvailable.set(true); + }); + + findPreference("showStylusClickOverride").setVisible(stylusAvailable.get()); + findPreference("stylusIsMouse").setVisible(stylusAvailable.get()); + findPreference("stylusButtonContactModifierMode").setEnabled(!p.getBoolean("stylusIsMouse", false)); + findPreference("stylusButtonContactModifierMode").setVisible(stylusAvailable.get()); + boolean requestNotificationPermissionVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission(requireContext(), POST_NOTIFICATIONS) == PERMISSION_DENIED; @@ -205,20 +252,25 @@ void setListeners(PreferenceGroup g) { for (int i=0; i < g.getPreferenceCount(); i++) { g.getPreference(i).setOnPreferenceChangeListener(this); g.getPreference(i).setOnPreferenceClickListener(this); - g.getPreference(i).setSingleLineTitle(false); if (g.getPreference(i) instanceof PreferenceGroup) setListeners((PreferenceGroup) g.getPreference(i)); } } - @Override - public boolean onPreferenceClick(@NonNull Preference preference) { - if ("enableAccessibilityService".contentEquals(preference.getKey())) { - Intent intent = new Intent(android.provider.Settings.ACTION_ACCESSIBILITY_SETTINGS); - startActivityForResult(intent, 0); + void setMultilineTitle(PreferenceGroup g) { + for (int i=0; i < g.getPreferenceCount(); i++) { + if (g.getPreference(i) instanceof PreferenceGroup) + setListeners((PreferenceGroup) g.getPreference(i)); + else { + g.getPreference(i).onDependencyChanged(g.getPreference(i), true); + g.getPreference(i).onDependencyChanged(g.getPreference(i), false); + } } + } + @Override + public boolean onPreferenceClick(@NonNull Preference preference) { if ("extra_keys_config".contentEquals(preference.getKey())) { @SuppressLint("InflateParams") View view = getLayoutInflater().inflate(R.layout.extra_keys_config, null, false); @@ -343,7 +395,7 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { intent.setPackage("com.termux.x11"); requireContext().sendBroadcast(intent); - handler.postAtTime(this::updatePreferencesLayout, 100); + setMultilineTitle(getPreferenceScreen()); return true; } } @@ -616,4 +668,17 @@ else if (context.checkSelfPermission(WRITE_SECURE_SETTINGS) != PERMISSION_GRANTE } static Handler handler = new Handler(); + + public void onClick(View view) { + new NestedPreferenceFragment().show(getSupportFragmentManager(), "ekbar"); + } + + public static class NestedPreferenceFragment extends DialogFragment { + @Nullable @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + getChildFragmentManager().beginTransaction().replace(android.R.id.content, new LoriePreferenceFragment("ekbar")).commit(); + + return null; + } + } } diff --git a/app/src/main/java/com/termux/x11/LorieView.java b/app/src/main/java/com/termux/x11/LorieView.java index d3ec9b278..65be57ea7 100644 --- a/app/src/main/java/com/termux/x11/LorieView.java +++ b/app/src/main/java/com/termux/x11/LorieView.java @@ -24,6 +24,7 @@ import androidx.annotation.NonNull; import com.termux.x11.input.InputStub; +import com.termux.x11.input.TouchInputHandler; import java.nio.charset.StandardCharsets; import java.util.regex.PatternSyntaxException; @@ -272,6 +273,8 @@ public void onWindowFocusChanged(boolean hasFocus) { checkForClipboardChange(); } else clipboard.removePrimaryClipChangedListener(clipboardListener); + + TouchInputHandler.refreshInputDevices(); } static native void connect(int fd); @@ -283,6 +286,8 @@ public void onWindowFocusChanged(boolean hasFocus) { static native void sendWindowChange(int width, int height, int framerate); public native void sendMouseEvent(float x, float y, int whichButton, boolean buttonDown, boolean relative); public native void sendTouchEvent(int action, int id, int x, int y); + public native void sendStylusEvent(float x, float y, int pressure, int tiltX, int tiltY, int orientation, int buttons, boolean eraser); + static public native void requestStylusEnabled(boolean enabled); public native boolean sendKeyEvent(int scanCode, int keyCode, boolean keyDown); public native void sendTextEvent(byte[] text); public native void sendUnicodeEvent(int code); diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 10cfbe96a..7174e149d 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -606,64 +606,52 @@ public ViewPager getTerminalToolbarViewPager() { } private void setTerminalToolbarView() { - final ViewPager terminalToolbarViewPager = getTerminalToolbarViewPager(); - - terminalToolbarViewPager.setAdapter(new X11ToolbarViewPager.PageAdapter(this, (v, k, e) -> mInputHandler.sendKeyEvent(e))); - terminalToolbarViewPager.addOnPageChangeListener(new X11ToolbarViewPager.OnPageChangeListener(this, terminalToolbarViewPager)); + final ViewPager pager = getTerminalToolbarViewPager(); + ViewGroup parent = (ViewGroup) pager.getParent(); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); - boolean enabled = preferences.getBoolean("showAdditionalKbd", true); - boolean showNow = enabled && preferences.getBoolean("additionalKbdVisible", true); + boolean showNow = mClientConnected && + preferences.getBoolean("showAdditionalKbd", true) && + preferences.getBoolean("additionalKbdVisible", true); - terminalToolbarViewPager.setVisibility(showNow ? View.VISIBLE : View.GONE); - findViewById(R.id.terminal_toolbar_view_pager).requestFocus(); + pager.setVisibility(showNow ? View.VISIBLE : View.INVISIBLE); - handler.postDelayed(() -> { - if (mExtraKeys != null) { - ViewGroup.LayoutParams layoutParams = terminalToolbarViewPager.getLayoutParams(); - layoutParams.height = Math.round(37.5f * getResources().getDisplayMetrics().density * - (mExtraKeys.getExtraKeysInfo() == null ? 0 : mExtraKeys.getExtraKeysInfo().getMatrix().length)); - terminalToolbarViewPager.setLayoutParams(layoutParams); - } - frm.setPadding(0, 0, 0, preferences.getBoolean("adjustHeightForEK", false) && terminalToolbarViewPager.getVisibility() == View.VISIBLE ? terminalToolbarViewPager.getHeight() : 0); - }, 200); + if (showNow) { + pager.setAdapter(new X11ToolbarViewPager.PageAdapter(this, (v, k, e) -> mInputHandler.sendKeyEvent(e))); + pager.clearOnPageChangeListeners(); + pager.addOnPageChangeListener(new X11ToolbarViewPager.OnPageChangeListener(this, pager)); + pager.bringToFront(); + } else { + parent.removeView(pager); + parent.addView(pager, 0); + if (mExtraKeys != null) + mExtraKeys.unsetSpecialKeys(); + } + + ViewGroup.LayoutParams layoutParams = pager.getLayoutParams(); + layoutParams.height = Math.round(37.5f * getResources().getDisplayMetrics().density * + (TermuxX11ExtraKeys.getExtraKeysInfo() == null ? 0 : TermuxX11ExtraKeys.getExtraKeysInfo().getMatrix().length)); + pager.setLayoutParams(layoutParams); + + frm.setPadding(0, 0, 0, preferences.getBoolean("adjustHeightForEK", false) && showNow ? layoutParams.height : 0); + getLorieView().requestFocus(); } public void toggleExtraKeys(boolean visible, boolean saveState) { - runOnUiThread(() -> { - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); - boolean enabled = preferences.getBoolean("showAdditionalKbd", true); - ViewPager pager = getTerminalToolbarViewPager(); - ViewGroup parent = (ViewGroup) pager.getParent(); - boolean show = enabled && mClientConnected && visible; - - if (show) { - setTerminalToolbarView(); - getTerminalToolbarViewPager().bringToFront(); - } else { - parent.removeView(pager); - parent.addView(pager, 0); - if (mExtraKeys != null) - mExtraKeys.unsetSpecialKeys(); - } - frm.setPadding(0, 0, 0, preferences.getBoolean("adjustHeightForEK", false) && show ? pager.getHeight() : 0); - - if (enabled && saveState) { - SharedPreferences.Editor edit = preferences.edit(); - edit.putBoolean("additionalKbdVisible", show); - edit.commit(); - } + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + boolean enabled = preferences.getBoolean("showAdditionalKbd", true); - pager.setVisibility(show ? View.VISIBLE : View.INVISIBLE); + if (enabled && mClientConnected && saveState) { + SharedPreferences.Editor edit = preferences.edit(); + edit.putBoolean("additionalKbdVisible", visible); + edit.commit(); + } - getLorieView().requestFocus(); - }); + setTerminalToolbarView(); } public void toggleExtraKeys() { - int visibility = getTerminalToolbarViewPager().getVisibility(); - toggleExtraKeys(visibility != View.VISIBLE, true); - getLorieView().requestFocus(); + toggleExtraKeys(getTerminalToolbarViewPager().getVisibility() != View.VISIBLE, true); } public boolean handleKey(KeyEvent e) { @@ -821,7 +809,10 @@ public void onUserLeaveHint() { @Override public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, @NonNull Configuration newConfig) { - toggleExtraKeys(!isInPictureInPictureMode, false); + if (isInPictureInPictureMode) + toggleExtraKeys(false, false); + else + setTerminalToolbarView(); super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); } @@ -849,7 +840,7 @@ void clientConnectedStateChanged(boolean connected) { runOnUiThread(()-> { SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(this); mClientConnected = connected; - toggleExtraKeys(connected && p.getBoolean("additionalKbdVisible", true), true); + setTerminalToolbarView(); findViewById(R.id.mouse_buttons).setVisibility(p.getBoolean("showMouseHelper", false) && "1".equals(p.getString("touchMode", "1")) && mClientConnected ? View.VISIBLE : View.GONE); findViewById(R.id.stub).setVisibility(connected?View.INVISIBLE:View.VISIBLE); getLorieView().setVisibility(connected?View.VISIBLE:View.INVISIBLE); diff --git a/app/src/main/java/com/termux/x11/input/InputEventSender.java b/app/src/main/java/com/termux/x11/input/InputEventSender.java index 3a1c9f4f8..2ec8e6d11 100644 --- a/app/src/main/java/com/termux/x11/input/InputEventSender.java +++ b/app/src/main/java/com/termux/x11/input/InputEventSender.java @@ -37,6 +37,8 @@ public final class InputEventSender { public float capturedPointerSpeedFactor = 100; public boolean dexMetaKeyCapture = false; public boolean pauseKeyInterceptingWithEsc = false; + public boolean stylusIsMouse = false; + public boolean stylusButtonContactModifierMode = false; /** Set of pressed keys for which we've sent TextEvent. */ private final TreeSet mPressedTextKeys; @@ -55,6 +57,11 @@ public void sendMouseEvent(PointF pos, int button, boolean down, boolean relativ mInjector.sendMouseEvent(pos != null ? (int) pos.x : 0, pos != null ? (int) pos.y : 0, button, down, relative); } + public void sendStylusEvent(float x, float y, int pressure, int tiltX, int tiltY, int orientation, int buttons, boolean eraser) { + mInjector.sendStylusEvent(x, y, pressure, tiltX, tiltY, orientation, buttons, eraser); + android.util.Log.d("STYLUS_EVENT", "transformed x " + x + " y " + y + " pressure " + pressure + " tiltX " + tiltX + " tiltY " + tiltY + " orientation " + orientation + " buttons " + buttons + " eraser " + eraser); + } + public void sendMouseDown(int button, boolean relative) { if (!buttons.contains(button)) return; diff --git a/app/src/main/java/com/termux/x11/input/InputStub.java b/app/src/main/java/com/termux/x11/input/InputStub.java index 79f7b67e7..a354f68db 100644 --- a/app/src/main/java/com/termux/x11/input/InputStub.java +++ b/app/src/main/java/com/termux/x11/input/InputStub.java @@ -41,4 +41,6 @@ public interface InputStub { /** Sends an event, not flushing connection. */ void sendTouchEvent(int action, int pointerId, int x, int y); + + void sendStylusEvent(float x, float y, int pressure, int tiltX, int tiltY, int orientation, int buttons, boolean eraser); } 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 830728093..4089f37ec 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.content.SharedPreferences; import android.graphics.PointF; +import android.hardware.input.InputManager; import android.os.Handler; import android.os.Build; import android.util.DisplayMetrics; @@ -21,11 +22,16 @@ import androidx.annotation.IntDef; import androidx.core.math.MathUtils; +import com.termux.x11.LorieView; import com.termux.x11.MainActivity; import com.termux.x11.utils.SamsungDexUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; /** * This class is responsible for handling Touch input from the user. Touch events which manipulate @@ -141,12 +147,46 @@ private TouchInputHandler(MainActivity activity, RenderData renderData, RenderSt setInputMode(InputMode.TRACKPAD); mDexListener = new DexListener(activity); mTouchpadHandler = isTouchpad ? null : new TouchInputHandler(activity, mRenderData, renderStub, injector, true); + + refreshInputDevices(); + ((InputManager) mActivity.getSystemService(Context.INPUT_SERVICE)).registerInputDeviceListener(new InputManager.InputDeviceListener() { + @Override + public void onInputDeviceAdded(int deviceId) { + refreshInputDevices(); + } + + @Override + public void onInputDeviceRemoved(int deviceId) { + refreshInputDevices(); + } + + @Override + public void onInputDeviceChanged(int deviceId) { + refreshInputDevices(); + } + }, null); + } public TouchInputHandler(MainActivity activity, RenderStub renderStub, final InputEventSender injector) { this(activity, null, renderStub, injector, false); } + static public void refreshInputDevices() { + AtomicBoolean stylusAvailable = new AtomicBoolean(false); + Arrays.stream(InputDevice.getDeviceIds()) + .mapToObj(InputDevice::getDevice) + .filter(Objects::nonNull) + .forEach((device) -> { + //noinspection DataFlowIssue + android.util.Log.d("STYLUS", "found device " + device.getName() + " sources " + device.getSources()); + if (device.supportsSource(InputDevice.SOURCE_STYLUS)) + stylusAvailable.set(true); + }); + android.util.Log.d("STYLUS", "requesting stylus " + stylusAvailable.get()); + LorieView.requestStylusEnabled(stylusAvailable.get()); + } + boolean isDexEvent(MotionEvent event) { int SOURCE_DEX = InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN; return ((event.getSource() & SOURCE_DEX) == SOURCE_DEX) @@ -174,7 +214,8 @@ public boolean handleTouchEvent(View view0, View view, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_UP) setCapturingEnabled(true); - if (event.getToolType(event.getActionIndex()) == MotionEvent.TOOL_TYPE_STYLUS) + if (event.getToolType(event.getActionIndex()) == MotionEvent.TOOL_TYPE_STYLUS + || event.getToolType(event.getActionIndex()) == MotionEvent.TOOL_TYPE_ERASER) return mStylusListener.onTouch(event); if (!isDexEvent(event) && (event.getToolType(event.getActionIndex()) == MotionEvent.TOOL_TYPE_MOUSE @@ -295,6 +336,8 @@ public void reloadPreferences(SharedPreferences p) { mInjector.scaleTouchpad = p.getBoolean("scaleTouchpad", true); mInjector.capturedPointerSpeedFactor = ((float) p.getInt("capturedPointerSpeedFactor", 100))/100; mInjector.dexMetaKeyCapture = p.getBoolean("dexMetaKeyCapture", false); + mInjector.stylusIsMouse = p.getBoolean("stylusIsMouse", false); + mInjector.stylusButtonContactModifierMode = p.getBoolean("stylusButtonContactModifierMode", false); mInjector.pauseKeyInterceptingWithEsc = p.getBoolean("pauseKeyInterceptingWithEsc", false); switch (p.getString("transformCapturedPointer", "no")) { case "c": @@ -592,8 +635,105 @@ private class StylusListener { private static final int ACTION_PRIMARY_DOWN = 0xd3; private static final int ACTION_PRIMARY_UP = 0xd4; + private float x = 0, y = 0, pressure = 0, tilt = 0, orientation = 0; + private int buttons = 0; + + private int convertOrientation(float value) { + int newValue = (int) (((value * 180 / Math.PI) + 360) % 360); + if (newValue > 180) + newValue = (newValue - 360) % 360; + return newValue; + } + + private boolean hasButton(MotionEvent e, int button) { + return (e.getButtonState() & button) == button; + } + + int extractButtons(MotionEvent e) { + if (mInjector.stylusButtonContactModifierMode) { + if (e.getAction() == MotionEvent.ACTION_DOWN || e.getAction() == MotionEvent.ACTION_MOVE) { + if (hasButton(e, MotionEvent.BUTTON_STYLUS_SECONDARY)) + return (1 << 1); + if (hasButton(e, MotionEvent.BUTTON_STYLUS_PRIMARY)) + return (1 << 2); + else + return STYLUS_INPUT_HELPER_MODE; + } else return 0; + } else { + int buttons = 0; + if (e.getAction() == MotionEvent.ACTION_DOWN || e.getAction() == MotionEvent.ACTION_MOVE) + buttons = STYLUS_INPUT_HELPER_MODE; + if (hasButton(e, MotionEvent.BUTTON_STYLUS_SECONDARY)) + buttons |= (1 << 1); + if (hasButton(e, MotionEvent.BUTTON_STYLUS_PRIMARY)) + buttons |= (1 << 2); + + return buttons; + } + } + + public boolean isExternal(InputDevice d) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) + return d.isExternal(); + + try { + // isExternal is a hidden method that is not accessible through the SDK_INT before Android Q + //noinspection DataFlowIssue + return (Boolean) InputDevice.class.getMethod("isExternal").invoke(d); + } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + return false; + } + } + @SuppressLint("ClickableViewAccessibility") boolean onTouch(MotionEvent e) { + if (mInjector.stylusIsMouse) + return onTouchMouse(e); + + int action = e.getAction(); + int tiltX = 0, tiltY = 0; + int newButtons = extractButtons(e); + float newX = e.getX(e.getActionIndex()), newY = e.getY(e.getActionIndex()); + InputDevice.MotionRange rangeX = e.getDevice().getMotionRange(MotionEvent.AXIS_X); + InputDevice.MotionRange rangeY = e.getDevice().getMotionRange(MotionEvent.AXIS_Y); + + if (MainActivity.getInstance().getLorieView().hasPointerCapture() && + isExternal(e.getDevice()) && rangeX != null && rangeY != null) { + newX *= mRenderData.imageWidth / rangeX.getMax(); + newY *= mRenderData.imageHeight / rangeY.getMax(); + } else { + newX *= mRenderData.scale.x; + newY *= mRenderData.scale.y; + } + + if (x == newX && y == newY && pressure == e.getPressure() && tilt == e.getAxisValue(MotionEvent.AXIS_TILT) && + orientation == e.getAxisValue(MotionEvent.AXIS_ORIENTATION) && buttons == newButtons) + return true; + + if (e.getDevice().getMotionRange(MotionEvent.AXIS_TILT) != null && + e.getDevice().getMotionRange(MotionEvent.AXIS_ORIENTATION) != null) { + orientation = e.getAxisValue(MotionEvent.AXIS_ORIENTATION); + tilt = e.getAxisValue(MotionEvent.AXIS_TILT); + tiltX = (int) Math.round((float) Math.asin(-Math.sin(orientation) * Math.sin(tilt)) * 63.5 - 0.5); + tiltY = (int) Math.round((float) Math.asin( Math.cos(orientation) * Math.sin(tilt)) * 63.5 - 0.5); + } + + android.util.Log.d("STYLUS_EVENT", "action " + action + " x " + newX + " y " + newY + " pressure " + e.getPressure() + " tilt " + e.getAxisValue(MotionEvent.AXIS_TILT) + " orientation " + e.getAxisValue(MotionEvent.AXIS_ORIENTATION)); + mInjector.sendStylusEvent( + x = newX, + y = newY, + (int) ((pressure = e.getPressure()) * 65535), + tiltX, + tiltY, + convertOrientation(orientation), + buttons = newButtons, + e.getToolType(e.getActionIndex()) == MotionEvent.TOOL_TYPE_ERASER); + + return true; + } + + @SuppressLint("ClickableViewAccessibility") + boolean onTouchMouse(MotionEvent e) { int action = e.getAction(); float scaledX = e.getX(e.getActionIndex()) * mRenderData.scale.x, scaledY = e.getY(e.getActionIndex()) * mRenderData.scale.y; if (mRenderData.setCursorPosition(scaledX, scaledY)) diff --git a/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java b/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java index 03988fa98..74a0169cb 100644 --- a/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java +++ b/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java @@ -31,11 +31,11 @@ public class TermuxX11ExtraKeys implements ExtraKeysView.IExtraKeysView { @SuppressWarnings("FieldCanBeLocal") - private final String LOG_TAG = "TermuxX11ExtraKeys"; + private static final String LOG_TAG = "TermuxX11ExtraKeys"; private final View.OnKeyListener mEventListener; private final MainActivity mActivity; private final ExtraKeysView mExtraKeysView; - private ExtraKeysInfo mExtraKeysInfo; + static private ExtraKeysInfo mExtraKeysInfo; private boolean ctrlDown; private boolean altDown; @@ -204,9 +204,9 @@ public void onLorieExtraKeyButtonClick(View view, String key, boolean ctrlDown, * Set the terminal extra keys and style. */ @SuppressWarnings("deprecation") - private void setExtraKeys() { + public static void setExtraKeys() { mExtraKeysInfo = null; - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mActivity); + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(MainActivity.getInstance()); try { // The mMap stores the extra key and style string values while loading properties @@ -215,20 +215,20 @@ private void setExtraKeys() { String extrakeys = preferences.getString("extra_keys_config", TermuxX11ExtraKeys.DEFAULT_IVALUE_EXTRA_KEYS); mExtraKeysInfo = new ExtraKeysInfo(extrakeys, "extra-keys-style", ExtraKeysConstants.CONTROL_CHARS_ALIASES); } catch (JSONException e) { - Toast.makeText(mActivity, "Could not load and set the \"extra-keys\" property from the properties file: " + e, Toast.LENGTH_LONG).show(); + Toast.makeText(MainActivity.getInstance(), "Could not load and set the \"extra-keys\" property from the properties file: " + e, Toast.LENGTH_LONG).show(); Log.e(LOG_TAG, "Could not load and set the \"extra-keys\" property from the properties file: ", e); try { mExtraKeysInfo = new ExtraKeysInfo(TermuxX11ExtraKeys.DEFAULT_IVALUE_EXTRA_KEYS, "default", ExtraKeysConstants.CONTROL_CHARS_ALIASES); } catch (JSONException e2) { - Toast.makeText(mActivity, "Can't create default extra keys", Toast.LENGTH_LONG).show(); + Toast.makeText(MainActivity.getInstance(), "Can't create default extra keys", Toast.LENGTH_LONG).show(); Log.e(LOG_TAG, "Could create default extra keys: ", e); mExtraKeysInfo = null; } } } - public ExtraKeysInfo getExtraKeysInfo() { + public static ExtraKeysInfo getExtraKeysInfo() { if (mExtraKeysInfo == null) setExtraKeys(); return mExtraKeysInfo; diff --git a/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java b/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java index 2199f6807..d5b12f326 100644 --- a/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java +++ b/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java @@ -54,10 +54,7 @@ public Object instantiateItem(@NonNull ViewGroup collection, int position) { layout = inflater.inflate(R.layout.view_terminal_toolbar_extra_keys, collection, false); ExtraKeysView extraKeysView = (ExtraKeysView) layout; mActivity.mExtraKeys = new TermuxX11ExtraKeys(mEventListener, mActivity, extraKeysView); - int mTerminalToolbarDefaultHeight = mActivity.getTerminalToolbarViewPager().getLayoutParams().height; - int height = mTerminalToolbarDefaultHeight * - ((mActivity.mExtraKeys.getExtraKeysInfo() == null) ? 0 : mActivity.mExtraKeys.getExtraKeysInfo().getMatrix().length); - extraKeysView.reload(mActivity.mExtraKeys.getExtraKeysInfo(), height); + extraKeysView.reload(); extraKeysView.setExtraKeysViewClient(mActivity.mExtraKeys); extraKeysView.setOnHoverListener((v, e) -> true); extraKeysView.setOnGenericMotionListener((v, e) -> true); @@ -68,7 +65,7 @@ public Object instantiateItem(@NonNull ViewGroup collection, int position) { editText.setOnEditorActionListener((v, actionId, event) -> { String textToSend = editText.getText().toString(); - if (textToSend.length() == 0) textToSend = "\r"; + if (textToSend.isEmpty()) textToSend = "\r"; KeyEvent e = new KeyEvent(0, textToSend, KeyCharacterMap.VIRTUAL_KEYBOARD, 0); mEventListener.onKey(mActivity.getLorieView(), 0, e); diff --git a/app/src/main/res/layout/preference.xml b/app/src/main/res/layout/preference.xml new file mode 100644 index 000000000..54c83d0d4 --- /dev/null +++ b/app/src/main/res/layout/preference.xml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index a0928f0b4..2534947c3 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,8 +1,45 @@ - + + + + + + diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 900bbb2d2..bda144919 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -1,6 +1,6 @@ - + - - + + + + @@ -137,17 +143,6 @@ android:defaultValue="true" android:key="captureVolumeKeys"/> - - - - + android:key="enableAccessibilityService"> + + - - - - @@ -215,4 +202,33 @@ android:title="Request notification permission" android:key="requestNotificationPermission" /> + + + + + + + + + + + + From 5dcc839be18eb0f9f32f3bbad4b612c6cce2b1b0 Mon Sep 17 00:00:00 2001 From: TudbuT Date: Wed, 5 Jun 2024 23:29:17 +0200 Subject: [PATCH 037/107] fix touchpad transform having no effect --- app/src/main/java/com/termux/x11/input/TouchInputHandler.java | 3 +++ 1 file changed, 3 insertions(+) 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 4089f37ec..5e798c54b 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -360,6 +360,9 @@ public void reloadPreferences(SharedPreferences p) { keyIntercepting = !mInjector.pauseKeyInterceptingWithEsc || mActivity.getLorieView().hasPointerCapture(); SamsungDexUtils.dexMetaKeyCapture(mActivity, mInjector.dexMetaKeyCapture && keyIntercepting); + + if(mTouchpadHandler != null) + mTouchpadHandler.reloadPreferences(p); } public boolean shouldInterceptKeys() { From 907440a1766193aa90677e753b39cddf7ce76c39 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Tue, 18 Jun 2024 09:04:01 +0300 Subject: [PATCH 038/107] Fixing hardware keyboard workaround for some devices. Fixes #648 --- app/src/main/java/com/termux/x11/LorieView.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/src/main/java/com/termux/x11/LorieView.java b/app/src/main/java/com/termux/x11/LorieView.java index 65be57ea7..c6002900c 100644 --- a/app/src/main/java/com/termux/x11/LorieView.java +++ b/app/src/main/java/com/termux/x11/LorieView.java @@ -13,12 +13,15 @@ import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.preference.PreferenceManager; +import android.text.InputType; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; import androidx.annotation.Keep; import androidx.annotation.NonNull; @@ -217,6 +220,7 @@ public void reloadPreferences(SharedPreferences p) { hardwareKbdScancodesWorkaround = p.getBoolean("hardwareKbdScancodesWorkaround", true); clipboardSyncEnabled = p.getBoolean("clipboardEnable", false); setClipboardSyncEnabled(clipboardSyncEnabled, clipboardSyncEnabled); + TouchInputHandler.refreshInputDevices(); } // It is used in native code @@ -277,6 +281,17 @@ public void onWindowFocusChanged(boolean hasFocus) { TouchInputHandler.refreshInputDevices(); } + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; + + // Note that IME_ACTION_NONE cannot be used as that makes it impossible to input newlines using the on-screen + // keyboard on Android TV (see https://github.com/termux/termux-app/issues/221). + outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN; + + return super.onCreateInputConnection(outAttrs); + } + static native void connect(int fd); native void handleXEvents(); static native void startLogcat(int fd); From 2559047707bf16e6f0773e8ddbdbb45d2f54d108 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Tue, 18 Jun 2024 09:06:57 +0300 Subject: [PATCH 039/107] Fixing stylus-related preference descriptions Fixes #656 --- app/src/main/res/xml/preferences.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index bda144919..be2446e9a 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -80,19 +80,19 @@ From 02111a8b3bc41e1395596f5f62d43fc1484051c5 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Wed, 19 Jun 2024 10:56:43 +0300 Subject: [PATCH 040/107] Fixing some stylus behaviour. Unifying mousemode and regular stylus behaviour handlers Making "Show IME with external keyboard" preference without requiring "SHOW_IME_WITH_HARD_KEYBOARD" system preference Making "Apply display scale factor to touchpad" preference visible all the time, requiring ""Touchscreen input mode" to be "Trackpad" and "Display resolution mode" to be not "native" Making stylus handler track stylus presssure instead of requiring action to be MotionEvent.ACTION_DOWN or MotionEvent.ACTION_MOVE. Android on Samsung devices sends 0xD3, 0xD4, 0xD5 events when stylus button is pressed, and probably actions on other devices may be different. --- app/src/main/cpp/lorie/android.c | 38 ++++--- .../java/com/termux/x11/LoriePreferences.java | 53 +-------- .../main/java/com/termux/x11/LorieView.java | 2 +- .../java/com/termux/x11/MainActivity.java | 27 ++++- .../termux/x11/input/InputEventSender.java | 6 +- .../java/com/termux/x11/input/InputStub.java | 2 +- .../termux/x11/input/TouchInputHandler.java | 105 ++++++++---------- app/src/main/res/xml/preferences.xml | 4 +- 8 files changed, 104 insertions(+), 133 deletions(-) diff --git a/app/src/main/cpp/lorie/android.c b/app/src/main/cpp/lorie/android.c index 2e4527b39..0530a20aa 100644 --- a/app/src/main/cpp/lorie/android.c +++ b/app/src/main/cpp/lorie/android.c @@ -76,7 +76,7 @@ typedef union { uint16_t pressure; int8_t tilt_x, tilt_y; int16_t orientation; - uint8_t buttons, eraser; + uint8_t buttons, eraser, mouse; } stylus; struct { uint8_t t, enable; @@ -349,35 +349,39 @@ void handleLorieEvents(int fd, __unused int ready, __unused void *ignored) { break; } case EVENT_STYLUS: { - int button; static int buttons_prev = 0; uint32_t released, pressed, diff; - DeviceIntPtr device = e.stylus.eraser ? lorieEraser : loriePen; + DeviceIntPtr device = e.stylus.mouse ? lorieMouse : (e.stylus.eraser ? lorieEraser : loriePen); if (!device) { __android_log_print(ANDROID_LOG_DEBUG, "LorieNative", "got stylus event but device is not requested\n"); break; } - __android_log_print(ANDROID_LOG_DEBUG, "LorieNative", "got stylus event %f %f %d %d %d %d %p\n", e.stylus.x, e.stylus.y, e.stylus.pressure, e.stylus.tilt_x, e.stylus.tilt_y, e.stylus.orientation, device); + __android_log_print(ANDROID_LOG_DEBUG, "LorieNative", "got stylus event %f %f %d %d %d %d %s\n", e.stylus.x, e.stylus.y, e.stylus.pressure, e.stylus.tilt_x, e.stylus.tilt_y, e.stylus.orientation, + device == lorieMouse ? "lorieMouse" : (device == loriePen ? "loriePen" : "lorieEraser")); valuator_mask_set_double(&mask, 0, max(min(e.stylus.x, screenDrawable->width), 0)); valuator_mask_set_double(&mask, 1, max(min(e.stylus.y, screenDrawable->height), 0)); - valuator_mask_set_double(&mask, 2, e.stylus.pressure); - valuator_mask_set_double(&mask, 3, e.stylus.tilt_x); - valuator_mask_set_double(&mask, 4, e.stylus.tilt_y); - valuator_mask_set_double(&mask, 5, e.stylus.orientation); - QueuePointerEvents(device, MotionNotify, 0, POINTER_ABSOLUTE | POINTER_DESKTOP, &mask); + if (device != lorieMouse) { + valuator_mask_set_double(&mask, 2, e.stylus.pressure); + valuator_mask_set_double(&mask, 3, e.stylus.tilt_x); + valuator_mask_set_double(&mask, 4, e.stylus.tilt_y); + valuator_mask_set_double(&mask, 5, e.stylus.orientation); + } + QueuePointerEvents(device, MotionNotify, 0, POINTER_ABSOLUTE | POINTER_DESKTOP | (device == lorieMouse ? POINTER_NORAW : 0), &mask); diff = buttons_prev ^ e.stylus.buttons; released = diff & ~e.stylus.buttons; pressed = diff & e.stylus.buttons; - button = 1; for (int i=0; i<3; i++) { - if (released & 0x1) - QueuePointerEvents(device, ButtonRelease, button, POINTER_RELATIVE, NULL); - if (pressed & 0x1) - QueuePointerEvents(device, ButtonPress, button, POINTER_RELATIVE, NULL); - button++; + if (released & 0x1) { + QueuePointerEvents(device, ButtonRelease, i + 1, POINTER_RELATIVE, NULL); + __android_log_print(ANDROID_LOG_DEBUG, "LorieNative", "sending %d press", i+1); + } + if (pressed & 0x1) { + QueuePointerEvents(device, ButtonPress, i + 1, POINTER_RELATIVE, NULL); + __android_log_print(ANDROID_LOG_DEBUG, "LorieNative", "sending %d release", i+1); + } released >>= 1; pressed >>= 1; } @@ -670,9 +674,9 @@ Java_com_termux_x11_LorieView_sendTouchEvent(__unused JNIEnv* env, __unused jobj JNIEXPORT void JNICALL Java_com_termux_x11_LorieView_sendStylusEvent(JNIEnv *env, __unused jobject thiz, jfloat x, jfloat y, jint pressure, jint tilt_x, jint tilt_y, - jint orientation, jint buttons, jboolean eraser) { + jint orientation, jint buttons, jboolean eraser, jboolean mouse) { if (conn_fd != -1) { - lorieEvent e = { .stylus = { .t = EVENT_STYLUS, .x = x, .y = y, .pressure = pressure, .tilt_x = tilt_x, .tilt_y = tilt_y, .orientation = orientation, .buttons = buttons, .eraser = eraser } }; + lorieEvent e = { .stylus = { .t = EVENT_STYLUS, .x = x, .y = y, .pressure = pressure, .tilt_x = tilt_x, .tilt_y = tilt_y, .orientation = orientation, .buttons = buttons, .eraser = eraser, .mouse = mouse } }; write(conn_fd, &e, sizeof(e)); checkConnection(env); } diff --git a/app/src/main/java/com/termux/x11/LoriePreferences.java b/app/src/main/java/com/termux/x11/LoriePreferences.java index f54de0821..1d8003db9 100644 --- a/app/src/main/java/com/termux/x11/LoriePreferences.java +++ b/app/src/main/java/com/termux/x11/LoriePreferences.java @@ -25,7 +25,6 @@ import android.os.Handler; import android.os.IBinder; import android.preference.PreferenceManager; -import android.provider.Settings; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; @@ -69,7 +68,6 @@ @SuppressWarnings("deprecation") public class LoriePreferences extends AppCompatActivity { static final String ACTION_PREFERENCES_CHANGED = "com.termux.x11.ACTION_PREFERENCES_CHANGED"; - static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard"; LoriePreferenceFragment loriePreferenceFragment; private final BroadcastReceiver receiver = new BroadcastReceiver() { @@ -147,6 +145,7 @@ public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable S } addPreferencesFromResource(R.xml.preferences); + //noinspection DataFlowIssue findPreference("showAdditionalKbd").setLayoutResource(R.layout.preference); // Hide what user should not see in this instance @@ -209,7 +208,9 @@ void updatePreferencesLayout() { int modeValue = Integer.parseInt(p.getString("touchMode", "1")) - 1; String mode = getResources().getStringArray(R.array.touchscreenInputModesEntries)[modeValue]; findPreference("touchMode").setSummary(mode); - findPreference("scaleTouchpad").setVisible("1".equals(p.getString("touchMode", "1")) && !"native".equals(p.getString("displayResolutionMode", "native"))); + boolean scaleTouchpadEnabled = "1".equals(p.getString("touchMode", "1")) && !"native".equals(p.getString("displayResolutionMode", "native")); + findPreference("scaleTouchpad").setEnabled(scaleTouchpadEnabled); + findPreference("scaleTouchpad").setSummary(scaleTouchpadEnabled ? "" : android.text.Html.fromHtml("Requires \"Touchscreen input mode\" to be \"Trackpad\" and \"Display resolution mode\" to be not \"native\"")); findPreference("showMouseHelper").setEnabled("1".equals(p.getString("touchMode", "1"))); AtomicBoolean stylusAvailable = new AtomicBoolean(false); @@ -224,7 +225,6 @@ void updatePreferencesLayout() { findPreference("showStylusClickOverride").setVisible(stylusAvailable.get()); findPreference("stylusIsMouse").setVisible(stylusAvailable.get()); - findPreference("stylusButtonContactModifierMode").setEnabled(!p.getBoolean("stylusIsMouse", false)); findPreference("stylusButtonContactModifierMode").setVisible(stylusAvailable.get()); boolean requestNotificationPermissionVisible = @@ -236,13 +236,6 @@ void updatePreferencesLayout() { @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); - SharedPreferences preferences = getPreferenceManager().getSharedPreferences(); - - String showImeEnabled = Settings.Secure.getString(requireActivity().getContentResolver(), SHOW_IME_WITH_HARD_KEYBOARD); - if (showImeEnabled == null) showImeEnabled = "0"; - SharedPreferences.Editor p = Objects.requireNonNull(preferences).edit(); - p.putBoolean("showIMEWhileExternalConnected", showImeEnabled.contentEquals("1")); - p.apply(); setListeners(getPreferenceScreen()); updatePreferencesLayout(); @@ -314,26 +307,6 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { Log.e("Preferences", "changed preference: " + key); handler.postDelayed(this::updatePreferencesLayout, 100); - if ("showIMEWhileExternalConnected".contentEquals(key)) { - boolean enabled = newValue.toString().contentEquals("true"); - try { - Settings.Secure.putString(requireActivity().getContentResolver(), SHOW_IME_WITH_HARD_KEYBOARD, enabled ? "1" : "0"); - } catch (Exception e) { - if (e instanceof SecurityException) { - new AlertDialog.Builder(requireActivity()) - .setTitle("Permission denied") - .setMessage("Android requires WRITE_SECURE_SETTINGS permission to change this setting.\n" + - "Please, launch this command using ADB:\n" + - "adb shell pm grant com.termux.x11 android.permission.WRITE_SECURE_SETTINGS") - .setNegativeButton("OK", null) - .create() - .show(); - } else //noinspection CallToPrintStackTrace - e.printStackTrace(); - return false; - } - } - if ("displayScale".contentEquals(key)) { int scale = (Integer) newValue; if (scale % 10 != 0) { @@ -469,23 +442,6 @@ public void onReceive(Context context, Intent intent) { continue; switch (key) { - case "showIMEWhileExternalConnected": { - boolean enabled = "true".contentEquals(newValue); - try { - Settings.Secure.putString(context.getContentResolver(), SHOW_IME_WITH_HARD_KEYBOARD, enabled ? "1" : "0"); - } catch (Exception e) { - if (e instanceof SecurityException) { - setResultCode(1); - setResultData("Permission denied.\n" + - "Android requires WRITE_SECURE_SETTINGS permission to change `show_ime_with_hard_keyboard` setting.\n" + - "Please, launch this command using ADB:\n" + - "adb shell pm grant com.termux.x11 android.permission.WRITE_SECURE_SETTINGS"); - return; - } else //noinspection CallToPrintStackTrace - e.printStackTrace(); - } - break; - } case "displayScale": { int scale = Integer.parseInt(newValue); if (scale % 10 != 0) { @@ -620,6 +576,7 @@ else if (context.checkSelfPermission(WRITE_SECURE_SETTINGS) != PERMISSION_GRANTE } break; } + case "showIMEWhileExternalConnected": case "displayStretch": case "Reseed": case "PIP": diff --git a/app/src/main/java/com/termux/x11/LorieView.java b/app/src/main/java/com/termux/x11/LorieView.java index c6002900c..d981aa2e7 100644 --- a/app/src/main/java/com/termux/x11/LorieView.java +++ b/app/src/main/java/com/termux/x11/LorieView.java @@ -301,7 +301,7 @@ public InputConnection onCreateInputConnection(EditorInfo outAttrs) { static native void sendWindowChange(int width, int height, int framerate); public native void sendMouseEvent(float x, float y, int whichButton, boolean buttonDown, boolean relative); public native void sendTouchEvent(int action, int id, int x, int y); - public native void sendStylusEvent(float x, float y, int pressure, int tiltX, int tiltY, int orientation, int buttons, boolean eraser); + public native void sendStylusEvent(float x, float y, int pressure, int tiltX, int tiltY, int orientation, int buttons, boolean eraser, boolean mouseMode); static public native void requestStylusEnabled(boolean enabled); public native boolean sendKeyEvent(int scanCode, int keyCode, boolean keyDown); public native void sendTextEvent(byte[] text); diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 7174e149d..84434a82c 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -52,6 +52,7 @@ import android.view.WindowInsets; import android.view.inputmethod.InputMethodManager; import android.widget.Button; +import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.LinearLayout; @@ -89,6 +90,8 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo private final int mNotificationId = 7892; NotificationManager mNotificationManager; static InputMethodManager inputMethodManager; + private static boolean showIMEWhileExternalConnected = true; + private static boolean externalKeyboardConnected = false; private boolean mClientConnected = false; private View.OnKeyListener mLorieKeyListener; boolean captureVolumeKeys = false; @@ -294,10 +297,10 @@ private void initStylusAuxButtons() { overlay.setOnCapturedPointerListener((v, e) -> true); overlay.setVisibility(stylusMenuEnabled ? View.VISIBLE : View.GONE); View.OnClickListener listener = view -> { - TouchInputHandler.STYLUS_INPUT_HELPER_MODE = (view.equals(left) ? 1 : (view.equals(middle) ? 2 : (view.equals(right) ? 3 : 0))); + TouchInputHandler.STYLUS_INPUT_HELPER_MODE = (view.equals(left) ? 1 : (view.equals(middle) ? 2 : (view.equals(right) ? 4 : 0))); left.setAlpha((TouchInputHandler.STYLUS_INPUT_HELPER_MODE == 1) ? menuSelectedTrasparency : menuUnselectedTrasparency); middle.setAlpha((TouchInputHandler.STYLUS_INPUT_HELPER_MODE == 2) ? menuSelectedTrasparency : menuUnselectedTrasparency); - right.setAlpha((TouchInputHandler.STYLUS_INPUT_HELPER_MODE == 3) ? menuSelectedTrasparency : menuUnselectedTrasparency); + right.setAlpha((TouchInputHandler.STYLUS_INPUT_HELPER_MODE == 4) ? menuSelectedTrasparency : menuUnselectedTrasparency); visibility.setAlpha(menuUnselectedTrasparency); }; @@ -544,6 +547,7 @@ void onPreferencesChanged(String key) { hideEKOnVolDown = p.getBoolean("hideEKOnVolDown", false); useTermuxEKBarBehaviour = p.getBoolean("useTermuxEKBarBehaviour", false); toggleIMEUsingBackKey = p.getBoolean("toggleIMEUsingBackKey", true); + showIMEWhileExternalConnected = p.getBoolean("showIMEWhileExternalConnected", true); int requestedOrientation = p.getBoolean("forceLandscape", false) ? ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -831,8 +835,13 @@ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { */ public static void toggleKeyboardVisibility(Context context) { Log.d("MainActivity", "Toggling keyboard visibility"); - if(inputMethodManager != null) - inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + if(inputMethodManager != null) { + android.util.Log.d("toggleKeyboardVisibility", "externalKeyboardConnected " + externalKeyboardConnected + " showIMEWhileExternalConnected " + showIMEWhileExternalConnected); + if (!externalKeyboardConnected || showIMEWhileExternalConnected) + inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + else + inputMethodManager.hideSoftInputFromWindow(getInstance().getLorieView().getWindowToken(), 0); + } } @SuppressWarnings("SameParameterValue") @@ -881,4 +890,14 @@ public boolean shouldInterceptKeys() { return mInputHandler.shouldInterceptKeys(); } + + public void setExternalKeyboardConnected(boolean connected) { + externalKeyboardConnected = connected; + EditText textInput = findViewById(R.id.terminal_toolbar_text_input); + if (textInput != null) + textInput.setShowSoftInputOnFocus(!connected || showIMEWhileExternalConnected); + if (connected && !showIMEWhileExternalConnected) + inputMethodManager.hideSoftInputFromWindow(getLorieView().getWindowToken(), 0); + getLorieView().requestFocus(); + } } diff --git a/app/src/main/java/com/termux/x11/input/InputEventSender.java b/app/src/main/java/com/termux/x11/input/InputEventSender.java index 2ec8e6d11..a28cf34e4 100644 --- a/app/src/main/java/com/termux/x11/input/InputEventSender.java +++ b/app/src/main/java/com/termux/x11/input/InputEventSender.java @@ -57,9 +57,9 @@ public void sendMouseEvent(PointF pos, int button, boolean down, boolean relativ mInjector.sendMouseEvent(pos != null ? (int) pos.x : 0, pos != null ? (int) pos.y : 0, button, down, relative); } - public void sendStylusEvent(float x, float y, int pressure, int tiltX, int tiltY, int orientation, int buttons, boolean eraser) { - mInjector.sendStylusEvent(x, y, pressure, tiltX, tiltY, orientation, buttons, eraser); - android.util.Log.d("STYLUS_EVENT", "transformed x " + x + " y " + y + " pressure " + pressure + " tiltX " + tiltX + " tiltY " + tiltY + " orientation " + orientation + " buttons " + buttons + " eraser " + eraser); + public void sendStylusEvent(float x, float y, int pressure, int tiltX, int tiltY, int orientation, int buttons, boolean eraser, boolean mouse) { + mInjector.sendStylusEvent(x, y, pressure, tiltX, tiltY, orientation, buttons, eraser, mouse); + android.util.Log.d("STYLUS_EVENT", "transformed x " + x + " y " + y + " pressure " + pressure + " tiltX " + tiltX + " tiltY " + tiltY + " orientation " + orientation + " buttons " + buttons + " eraser " + eraser + " mouseMode + mouse); } public void sendMouseDown(int button, boolean relative) { diff --git a/app/src/main/java/com/termux/x11/input/InputStub.java b/app/src/main/java/com/termux/x11/input/InputStub.java index a354f68db..873e92b50 100644 --- a/app/src/main/java/com/termux/x11/input/InputStub.java +++ b/app/src/main/java/com/termux/x11/input/InputStub.java @@ -42,5 +42,5 @@ public interface InputStub { /** Sends an event, not flushing connection. */ void sendTouchEvent(int action, int pointerId, int x, int y); - void sendStylusEvent(float x, float y, int pressure, int tiltX, int tiltY, int orientation, int buttons, boolean eraser); + void sendStylusEvent(float x, float y, int pressure, int tiltX, int tiltY, int orientation, int buttons, boolean eraser, boolean mouseMode); } 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 5e798c54b..adf168afe 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -41,7 +41,7 @@ public class TouchInputHandler { private static final float EPSILON = 0.001f; - public static int STYLUS_INPUT_HELPER_MODE = 1; //1 = Left Click, 2 Middle Click, 3 Right Click + public static int STYLUS_INPUT_HELPER_MODE = 1; // 1 = Left Click, 2 Middle Click, 4 Right Click /** Used to set/store the selected input mode. */ @SuppressWarnings("unused") @@ -150,18 +150,24 @@ private TouchInputHandler(MainActivity activity, RenderData renderData, RenderSt refreshInputDevices(); ((InputManager) mActivity.getSystemService(Context.INPUT_SERVICE)).registerInputDeviceListener(new InputManager.InputDeviceListener() { + /** @noinspection DataFlowIssue*/ @Override public void onInputDeviceAdded(int deviceId) { + android.util.Log.d("InputDeviceListener", "added " + InputDevice.getDevice(deviceId).getName()); refreshInputDevices(); } + /** @noinspection DataFlowIssue*/ @Override public void onInputDeviceRemoved(int deviceId) { + android.util.Log.d("InputDeviceListener", "removed " + InputDevice.getDevice(deviceId).getName()); refreshInputDevices(); } + /** @noinspection DataFlowIssue*/ @Override public void onInputDeviceChanged(int deviceId) { + android.util.Log.d("InputDeviceListener", "changed " + InputDevice.getDevice(deviceId).getName()); refreshInputDevices(); } }, null); @@ -174,17 +180,28 @@ public TouchInputHandler(MainActivity activity, RenderStub renderStub, final Inp static public void refreshInputDevices() { AtomicBoolean stylusAvailable = new AtomicBoolean(false); + AtomicBoolean externalKeyboardAvailable = new AtomicBoolean(false); + android.util.Log.d("DEVICES", "external keyboard connected " + stylusAvailable.get()); Arrays.stream(InputDevice.getDeviceIds()) .mapToObj(InputDevice::getDevice) .filter(Objects::nonNull) .forEach((device) -> { //noinspection DataFlowIssue - android.util.Log.d("STYLUS", "found device " + device.getName() + " sources " + device.getSources()); + android.util.Log.d("DEVICES", "found device \"" + device.getName() + "\" " + + (device.supportsSource(InputDevice.SOURCE_STYLUS) ? ((isExternal(device) ? "external " : "") + "stylus ") : "") + + ((device.supportsSource(InputDevice.SOURCE_KEYBOARD) && device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) ? ((isExternal(device) ? "external " : "") + "keyboard ") : "") + + "sources " + String.format("0x%08X", device.getSources()) + " " + device.supportsSource(InputDevice.SOURCE_KEYBOARD) + " " + (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) + " " + isExternal(device) + " " + (device.supportsSource(InputDevice.SOURCE_KEYBOARD) && device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC && isExternal(device))); + if (device.supportsSource(InputDevice.SOURCE_STYLUS)) stylusAvailable.set(true); + + if (device.supportsSource(InputDevice.SOURCE_KEYBOARD) && device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC && isExternal(device)) + externalKeyboardAvailable.set(true); }); - android.util.Log.d("STYLUS", "requesting stylus " + stylusAvailable.get()); + android.util.Log.d("DEVICES", "requesting stylus " + stylusAvailable.get()); + android.util.Log.d("DEVICES", "external keyboard connected " + externalKeyboardAvailable.get()); LorieView.requestStylusEnabled(stylusAvailable.get()); + MainActivity.getInstance().setExternalKeyboardConnected(externalKeyboardAvailable.get()); } boolean isDexEvent(MotionEvent event) { @@ -328,12 +345,27 @@ public void setCapturingEnabled(boolean enabled) { } } + public static boolean isExternal(InputDevice d) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) + return d.isExternal(); + + try { + // isExternal is a hidden method that is not accessible through the SDK_INT before Android Q + //noinspection DataFlowIssue + return (Boolean) InputDevice.class.getMethod("isExternal").invoke(d); + } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + return false; + } + } + public void reloadPreferences(SharedPreferences p) { setInputMode(Integer.parseInt(p.getString("touchMode", "1"))); mInjector.tapToMove = p.getBoolean("tapToMove", false); mInjector.preferScancodes = p.getBoolean("preferScancodes", false); mInjector.pointerCapture = p.getBoolean("pointerCapture", false); - mInjector.scaleTouchpad = p.getBoolean("scaleTouchpad", true); + mInjector.scaleTouchpad = p.getBoolean("scaleTouchpad", true) && + "1".equals(p.getString("touchMode", "1")) && + !"native".equals(p.getString("displayResolutionMode", "native")); mInjector.capturedPointerSpeedFactor = ((float) p.getInt("capturedPointerSpeedFactor", 100))/100; mInjector.dexMetaKeyCapture = p.getBoolean("dexMetaKeyCapture", false); mInjector.stylusIsMouse = p.getBoolean("stylusIsMouse", false); @@ -632,8 +664,6 @@ boolean onTouch(View v, MotionEvent e) { } private class StylusListener { - private int button = 0; - // I've got this on SM-N770F private static final int ACTION_PRIMARY_DOWN = 0xd3; private static final int ACTION_PRIMARY_UP = 0xd4; @@ -654,7 +684,7 @@ private boolean hasButton(MotionEvent e, int button) { int extractButtons(MotionEvent e) { if (mInjector.stylusButtonContactModifierMode) { - if (e.getAction() == MotionEvent.ACTION_DOWN || e.getAction() == MotionEvent.ACTION_MOVE) { + if (e.getPressure() > 0) { if (hasButton(e, MotionEvent.BUTTON_STYLUS_SECONDARY)) return (1 << 1); if (hasButton(e, MotionEvent.BUTTON_STYLUS_PRIMARY)) @@ -664,7 +694,7 @@ int extractButtons(MotionEvent e) { } else return 0; } else { int buttons = 0; - if (e.getAction() == MotionEvent.ACTION_DOWN || e.getAction() == MotionEvent.ACTION_MOVE) + if (e.getPressure() > 0) buttons = STYLUS_INPUT_HELPER_MODE; if (hasButton(e, MotionEvent.BUTTON_STYLUS_SECONDARY)) buttons |= (1 << 1); @@ -675,33 +705,20 @@ int extractButtons(MotionEvent e) { } } - public boolean isExternal(InputDevice d) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) - return d.isExternal(); - - try { - // isExternal is a hidden method that is not accessible through the SDK_INT before Android Q - //noinspection DataFlowIssue - return (Boolean) InputDevice.class.getMethod("isExternal").invoke(d); - } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - return false; - } - } - @SuppressLint("ClickableViewAccessibility") boolean onTouch(MotionEvent e) { - if (mInjector.stylusIsMouse) - return onTouchMouse(e); - int action = e.getAction(); int tiltX = 0, tiltY = 0; int newButtons = extractButtons(e); float newX = e.getX(e.getActionIndex()), newY = e.getY(e.getActionIndex()); - InputDevice.MotionRange rangeX = e.getDevice().getMotionRange(MotionEvent.AXIS_X); - InputDevice.MotionRange rangeY = e.getDevice().getMotionRange(MotionEvent.AXIS_Y); + InputDevice dev = e.getDevice(); + InputDevice.MotionRange rangeX = dev.getMotionRange(MotionEvent.AXIS_X); + InputDevice.MotionRange rangeY = dev.getMotionRange(MotionEvent.AXIS_Y); + boolean hasTilt = e.getDevice().getMotionRange(MotionEvent.AXIS_TILT) != null; + boolean hasOrientation = e.getDevice().getMotionRange(MotionEvent.AXIS_ORIENTATION) != null; if (MainActivity.getInstance().getLorieView().hasPointerCapture() && - isExternal(e.getDevice()) && rangeX != null && rangeY != null) { + isExternal(dev) && rangeX != null && rangeY != null) { newX *= mRenderData.imageWidth / rangeX.getMax(); newY *= mRenderData.imageHeight / rangeY.getMax(); } else { @@ -713,15 +730,14 @@ boolean onTouch(MotionEvent e) { orientation == e.getAxisValue(MotionEvent.AXIS_ORIENTATION) && buttons == newButtons) return true; - if (e.getDevice().getMotionRange(MotionEvent.AXIS_TILT) != null && - e.getDevice().getMotionRange(MotionEvent.AXIS_ORIENTATION) != null) { + if (hasTilt && hasOrientation) { orientation = e.getAxisValue(MotionEvent.AXIS_ORIENTATION); tilt = e.getAxisValue(MotionEvent.AXIS_TILT); tiltX = (int) Math.round((float) Math.asin(-Math.sin(orientation) * Math.sin(tilt)) * 63.5 - 0.5); tiltY = (int) Math.round((float) Math.asin( Math.cos(orientation) * Math.sin(tilt)) * 63.5 - 0.5); } - android.util.Log.d("STYLUS_EVENT", "action " + action + " x " + newX + " y " + newY + " pressure " + e.getPressure() + " tilt " + e.getAxisValue(MotionEvent.AXIS_TILT) + " orientation " + e.getAxisValue(MotionEvent.AXIS_ORIENTATION)); + android.util.Log.d("STYLUS_EVENT", "action " + action + " x " + newX + " y " + newY + " pressure " + e.getPressure() + " tilt " + e.getAxisValue(MotionEvent.AXIS_TILT) + " orientation " + e.getAxisValue(MotionEvent.AXIS_ORIENTATION) + " buttonState " + e.getButtonState() + " extractedButtons " + newButtons); mInjector.sendStylusEvent( x = newX, y = newY, @@ -730,33 +746,8 @@ boolean onTouch(MotionEvent e) { tiltY, convertOrientation(orientation), buttons = newButtons, - e.getToolType(e.getActionIndex()) == MotionEvent.TOOL_TYPE_ERASER); - - return true; - } - - @SuppressLint("ClickableViewAccessibility") - boolean onTouchMouse(MotionEvent e) { - int action = e.getAction(); - float scaledX = e.getX(e.getActionIndex()) * mRenderData.scale.x, scaledY = e.getY(e.getActionIndex()) * mRenderData.scale.y; - if (mRenderData.setCursorPosition(scaledX, scaledY)) - mInjector.sendCursorMove(scaledX, scaledY, false); - - if (action == MotionEvent.ACTION_DOWN || action == ACTION_PRIMARY_DOWN) { - button = STYLUS_INPUT_HELPER_MODE; - if (button == 1) { - if (e.isButtonPressed(MotionEvent.BUTTON_STYLUS_PRIMARY)) - button = 3; - if (e.isButtonPressed(MotionEvent.BUTTON_STYLUS_SECONDARY)) - button = 2; - } - } - - if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_UP || action == ACTION_PRIMARY_DOWN || action == ACTION_PRIMARY_UP) - mInjector.sendMouseEvent(mRenderData.getCursorPosition(), button, (action == MotionEvent.ACTION_DOWN || action == ACTION_PRIMARY_DOWN), false); - - if (action == MotionEvent.ACTION_UP) - button = 0; + e.getToolType(e.getActionIndex()) == MotionEvent.TOOL_TYPE_ERASER, + mInjector.stylusIsMouse); return true; } diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index be2446e9a..aa7637ac2 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -145,8 +145,8 @@ Date: Wed, 19 Jun 2024 11:25:02 +0300 Subject: [PATCH 041/107] Fix building --- app/src/main/java/com/termux/x11/LoriePreferences.java | 2 +- app/src/main/java/com/termux/x11/input/InputEventSender.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/termux/x11/LoriePreferences.java b/app/src/main/java/com/termux/x11/LoriePreferences.java index 1d8003db9..feaf9b11c 100644 --- a/app/src/main/java/com/termux/x11/LoriePreferences.java +++ b/app/src/main/java/com/termux/x11/LoriePreferences.java @@ -210,7 +210,7 @@ void updatePreferencesLayout() { findPreference("touchMode").setSummary(mode); boolean scaleTouchpadEnabled = "1".equals(p.getString("touchMode", "1")) && !"native".equals(p.getString("displayResolutionMode", "native")); findPreference("scaleTouchpad").setEnabled(scaleTouchpadEnabled); - findPreference("scaleTouchpad").setSummary(scaleTouchpadEnabled ? "" : android.text.Html.fromHtml("Requires \"Touchscreen input mode\" to be \"Trackpad\" and \"Display resolution mode\" to be not \"native\"")); + findPreference("scaleTouchpad").setSummary(scaleTouchpadEnabled ? "" : "Requires \"Touchscreen input mode\" to be \"Trackpad\" and \"Display resolution mode\" to be not \"native\""); findPreference("showMouseHelper").setEnabled("1".equals(p.getString("touchMode", "1"))); AtomicBoolean stylusAvailable = new AtomicBoolean(false); diff --git a/app/src/main/java/com/termux/x11/input/InputEventSender.java b/app/src/main/java/com/termux/x11/input/InputEventSender.java index a28cf34e4..e03f339fd 100644 --- a/app/src/main/java/com/termux/x11/input/InputEventSender.java +++ b/app/src/main/java/com/termux/x11/input/InputEventSender.java @@ -59,7 +59,7 @@ public void sendMouseEvent(PointF pos, int button, boolean down, boolean relativ public void sendStylusEvent(float x, float y, int pressure, int tiltX, int tiltY, int orientation, int buttons, boolean eraser, boolean mouse) { mInjector.sendStylusEvent(x, y, pressure, tiltX, tiltY, orientation, buttons, eraser, mouse); - android.util.Log.d("STYLUS_EVENT", "transformed x " + x + " y " + y + " pressure " + pressure + " tiltX " + tiltX + " tiltY " + tiltY + " orientation " + orientation + " buttons " + buttons + " eraser " + eraser + " mouseMode + mouse); + android.util.Log.d("STYLUS_EVENT", "transformed x " + x + " y " + y + " pressure " + pressure + " tiltX " + tiltX + " tiltY " + tiltY + " orientation " + orientation + " buttons " + buttons + " eraser " + eraser + " mouseMode " + mouse); } public void sendMouseDown(int button, boolean relative) { From ee8674f10a737f25bdd9b125f93e48b44a62818c Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 20 Jun 2024 17:11:04 +0300 Subject: [PATCH 042/107] Introducing "Configure response to user actions" preferences. Preferences "Toggle keyboard using back key", "Capture volume keys" and "Hide additional keyboard on Volume Down key press" were removed in prior to new "Configure response to user actions" preferences. Fixing direct touch behaviour. --- app/src/main/cpp/lorie/android.c | 26 +--- .../java/com/termux/x11/LoriePreferences.java | 62 +++++++-- .../main/java/com/termux/x11/LorieView.java | 1 - .../java/com/termux/x11/MainActivity.java | 56 ++------ .../java/com/termux/x11/input/InputStub.java | 1 - .../termux/x11/input/TouchInputHandler.java | 125 ++++++++++++------ app/src/main/res/layout/main_activity.xml | 11 ++ app/src/main/res/values/arrays.xml | 8 ++ app/src/main/res/values/strings.xml | 5 +- app/src/main/res/values/styles.xml | 7 + app/src/main/res/xml/preferences.xml | 63 ++++++--- 11 files changed, 226 insertions(+), 139 deletions(-) diff --git a/app/src/main/cpp/lorie/android.c b/app/src/main/cpp/lorie/android.c index 0530a20aa..33ced72b1 100644 --- a/app/src/main/cpp/lorie/android.c +++ b/app/src/main/cpp/lorie/android.c @@ -317,18 +317,13 @@ void handleLorieEvents(int fd, __unused int ready, __unused void *ignored) { break; } case EVENT_TOUCH: { - double x, y; + double x = max(min((float) e.touch.x, screenDrawable->width), 0); + double y = max(min((float) e.touch.y, screenDrawable->height), 0); DDXTouchPointInfoPtr touch = TouchFindByDDXID(lorieTouch, e.touch.id, FALSE); - x = (float) e.touch.x * 0xFFFF / (float) screenDrawable->width; - y = (float) e.touch.y * 0xFFFF / (float) screenDrawable->height; - - x = max(min(x, screenDrawable->width), 0); - y = max(min(y, screenDrawable->height), 0); - // Avoid duplicating events if (touch && touch->active) { - double oldx, oldy; + double oldx = 0, oldy = 0; if (e.touch.type == XI_TouchUpdate && valuator_mask_fetch_double(touch->valuators, 0, &oldx) && valuator_mask_fetch_double(touch->valuators, 1, &oldy) && @@ -343,8 +338,8 @@ void handleLorieEvents(int fd, __unused int ready, __unused void *ignored) { if (e.touch.type == XI_TouchEnd && (!touch || !touch->active)) break; - valuator_mask_set_double(&mask, 0, x); - valuator_mask_set_double(&mask, 1, y); + valuator_mask_set_double(&mask, 0, x * 0xFFFF / (float) screenDrawable->width); + valuator_mask_set_double(&mask, 1, y * 0xFFFF / (float) screenDrawable->height); QueueTouchEvents(lorieTouch, e.touch.type, e.touch.id, 0, &mask); break; } @@ -739,17 +734,6 @@ Java_com_termux_x11_LorieView_sendTextEvent(JNIEnv *env, __unused jobject thiz, } } -JNIEXPORT void JNICALL -Java_com_termux_x11_LorieView_sendUnicodeEvent(JNIEnv *env, __unused jobject thiz, jint code) { - if (conn_fd != -1) { - log(DEBUG, "Sending unicode event: %lc (U+%X)", code, code); - lorieEvent e = { .unicode = { .t = EVENT_UNICODE, .code = code } }; - write(conn_fd, &e, sizeof(e)); - - checkConnection(env); - } -} - void abort(void) { _exit(134); } diff --git a/app/src/main/java/com/termux/x11/LoriePreferences.java b/app/src/main/java/com/termux/x11/LoriePreferences.java index feaf9b11c..9107921ef 100644 --- a/app/src/main/java/com/termux/x11/LoriePreferences.java +++ b/app/src/main/java/com/termux/x11/LoriePreferences.java @@ -20,6 +20,8 @@ import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; +import androidx.fragment.app.FragmentManager; +import androidx.preference.ListPreference; import androidx.preference.Preference; import android.os.Handler; @@ -156,12 +158,6 @@ public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable S g.getPreference(i).setVisible(g.getPreference(i).getKey().contentEquals(preference)); } } - } else { - for (int i=0; i < g.getPreferenceCount(); i++) { - if (g.getPreference(i) instanceof PreferenceGroup) { - g.getPreference(i).setVisible(!g.getPreference(i).getKey().contentEquals("ekbar")); - } - } } } @@ -191,10 +187,11 @@ void updatePreferencesLayout() { findPreference("displayResolutionExact").setVisible(displayResMode.contentEquals("exact")); findPreference("displayResolutionCustom").setVisible(displayResMode.contentEquals("custom")); - findPreference("hideEKOnVolDown").setEnabled(p.getBoolean("showAdditionalKbd", false) && p.getBoolean("captureVolumeKeys", true)); findPreference("dexMetaKeyCapture").setEnabled(!p.getBoolean("enableAccessibilityServiceAutomatically", false)); findPreference("enableAccessibilityServiceAutomatically").setEnabled(!p.getBoolean("dexMetaKeyCapture", false)); - findPreference("pauseKeyInterceptingWithEsc").setEnabled(p.getBoolean("dexMetaKeyCapture", false) || p.getBoolean("enableAccessibilityServiceAutomatically", false)); + boolean pauseKeyInterceptingWithEscEnabled = p.getBoolean("dexMetaKeyCapture", false) || p.getBoolean("enableAccessibilityServiceAutomatically", false); + findPreference("pauseKeyInterceptingWithEsc").setEnabled(pauseKeyInterceptingWithEscEnabled); + findPreference("pauseKeyInterceptingWithEsc").setSummary(pauseKeyInterceptingWithEscEnabled ? "" : "Requires intercepting system shortcuts with Dex mode or with Accessibility service"); findPreference("filterOutWinkey").setEnabled(p.getBoolean("enableAccessibilityServiceAutomatically", false)); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) @@ -203,7 +200,9 @@ void updatePreferencesLayout() { findPreference("displayResolutionMode").setSummary(p.getString("displayResolutionMode", "native")); findPreference("displayResolutionExact").setSummary(p.getString("displayResolutionExact", "1280x1024")); findPreference("displayResolutionCustom").setSummary(p.getString("displayResolutionCustom", "1280x1024")); - findPreference("displayStretch").setEnabled("exact".contentEquals(p.getString("displayResolutionMode", "native")) || "custom".contentEquals(p.getString("displayResolutionMode", "native"))); + boolean displayStretchEnabled = "exact".contentEquals(p.getString("displayResolutionMode", "native")) || "custom".contentEquals(p.getString("displayResolutionMode", "native")); + findPreference("displayStretch").setEnabled(displayStretchEnabled); + findPreference("displayStretch").setSummary(displayStretchEnabled ? "" : "Requires \"display resolution mode\" to be \"exact\" or \"custom\""); int modeValue = Integer.parseInt(p.getString("touchMode", "1")) - 1; String mode = getResources().getStringArray(R.array.touchscreenInputModesEntries)[modeValue]; @@ -211,6 +210,7 @@ void updatePreferencesLayout() { boolean scaleTouchpadEnabled = "1".equals(p.getString("touchMode", "1")) && !"native".equals(p.getString("displayResolutionMode", "native")); findPreference("scaleTouchpad").setEnabled(scaleTouchpadEnabled); findPreference("scaleTouchpad").setSummary(scaleTouchpadEnabled ? "" : "Requires \"Touchscreen input mode\" to be \"Trackpad\" and \"Display resolution mode\" to be not \"native\""); + findPreference("scaleTouchpad").setSummary(scaleTouchpadEnabled ? "" : "Requires \"Touchscreen input mode\" to be \"Trackpad\" and \"Display resolution mode\" to be not \"native\""); findPreference("showMouseHelper").setEnabled("1".equals(p.getString("touchMode", "1"))); AtomicBoolean stylusAvailable = new AtomicBoolean(false); @@ -231,6 +231,32 @@ void updatePreferencesLayout() { Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && ContextCompat.checkSelfPermission(requireContext(), POST_NOTIFICATIONS) == PERMISSION_DENIED; findPreference("requestNotificationPermission").setVisible(requestNotificationPermissionVisible); + + setNoActionOptionText(findPreference("volumeDownAction"), "android volume control"); + setNoActionOptionText(findPreference("volumeUpAction"), "android volume control"); + setDefaultOptionText(findPreference("swipeDownAction"), "toggle additional key bar"); + setDefaultOptionText(findPreference("swipeUpAction"), "none"); + setDefaultOptionText(findPreference("volumeDownAction"), "send volume down"); + setDefaultOptionText(findPreference("volumeUpAction"), "send volume up"); + setDefaultOptionText(findPreference("backButtonAction"), "toggle soft keyboard"); + } + + private void setNoActionOptionText(Preference preference, CharSequence text) { + ListPreference p = (ListPreference) preference; + CharSequence[] options = p.getEntries(); + for (int i=0; i= Build.VERSION_CODES.TIRAMISU && "requestNotificationPermission".contentEquals(preference.getKey())) ActivityCompat.requestPermissions(requireActivity(), new String[]{ POST_NOTIFICATIONS }, 101); @@ -627,15 +657,23 @@ else if (context.checkSelfPermission(WRITE_SECURE_SETTINGS) != PERMISSION_GRANTE static Handler handler = new Handler(); public void onClick(View view) { - new NestedPreferenceFragment().show(getSupportFragmentManager(), "ekbar"); + new NestedPreferenceFragment("ekbar").show(getSupportFragmentManager()); } public static class NestedPreferenceFragment extends DialogFragment { + private final String fragment; + NestedPreferenceFragment(String fragment) { + this.fragment = fragment; + } + @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - getChildFragmentManager().beginTransaction().replace(android.R.id.content, new LoriePreferenceFragment("ekbar")).commit(); - + getChildFragmentManager().beginTransaction().replace(android.R.id.content, new LoriePreferenceFragment(fragment)).commit(); return null; } + + public void show(@NonNull FragmentManager manager) { + show(manager, fragment); + } } } diff --git a/app/src/main/java/com/termux/x11/LorieView.java b/app/src/main/java/com/termux/x11/LorieView.java index d981aa2e7..d99fbfcc5 100644 --- a/app/src/main/java/com/termux/x11/LorieView.java +++ b/app/src/main/java/com/termux/x11/LorieView.java @@ -305,7 +305,6 @@ public InputConnection onCreateInputConnection(EditorInfo outAttrs) { static public native void requestStylusEnabled(boolean enabled); public native boolean sendKeyEvent(int scanCode, int keyCode, boolean keyDown); public native void sendTextEvent(byte[] text); - public native void sendUnicodeEvent(int code); static { System.loadLibrary("Xlorie"); diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 84434a82c..905938ba8 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -3,7 +3,6 @@ import static android.Manifest.permission.WRITE_SECURE_SETTINGS; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Build.VERSION.SDK_INT; -import static android.view.InputDevice.KEYBOARD_TYPE_ALPHABETIC; import static android.view.KeyEvent.*; import static android.view.WindowManager.LayoutParams.*; import static com.termux.x11.CmdEntryPoint.ACTION_START; @@ -66,7 +65,6 @@ import com.termux.x11.input.InputEventSender; import com.termux.x11.input.InputStub; -import com.termux.x11.input.TouchInputHandler.RenderStub; import com.termux.x11.input.TouchInputHandler; import com.termux.x11.utils.FullscreenWorkaround; import com.termux.x11.utils.KeyInterceptor; @@ -94,12 +92,8 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo private static boolean externalKeyboardConnected = false; private boolean mClientConnected = false; private View.OnKeyListener mLorieKeyListener; - boolean captureVolumeKeys = false; private boolean filterOutWinKey = false; - private boolean hideEKOnVolDown = false; - private boolean toggleIMEUsingBackKey = false; private boolean useTermuxEKBarBehaviour = false; - private static final int KEY_BACK = 158; private static boolean oldFullscreen = false; private static boolean oldHideCutout = false; @@ -171,47 +165,15 @@ protected void onCreate(Bundle savedInstanceState) { frm = findViewById(R.id.frame); findViewById(R.id.preferences_button).setOnClickListener((l) -> startActivity(new Intent(this, LoriePreferences.class) {{ setAction(Intent.ACTION_MAIN); }})); findViewById(R.id.help_button).setOnClickListener((l) -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/termux/termux-x11/blob/master/README.md#running-graphical-applications")))); + findViewById(R.id.exit_button).setOnClickListener((l) -> finish()); LorieView lorieView = findViewById(R.id.lorieView); View lorieParent = (View) lorieView.getParent(); - mInputHandler = new TouchInputHandler(this, new RenderStub.NullStub() { - @Override - public void swipeDown() { - toggleExtraKeys(); - } - }, new InputEventSender(lorieView)); + mInputHandler = new TouchInputHandler(this, new InputEventSender(lorieView)); mLorieKeyListener = (v, k, e) -> { InputDevice dev = e.getDevice(); - boolean result; - - if (!captureVolumeKeys && (k == KEYCODE_VOLUME_DOWN || k == KEYCODE_VOLUME_UP)) - return false; - - if (hideEKOnVolDown && k == KEYCODE_VOLUME_DOWN) { - if (e.getAction() == ACTION_UP) - toggleExtraKeys(); - return true; - } - - if (k == KEYCODE_BACK) { - if (e.isFromSource(InputDevice.SOURCE_MOUSE) || e.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) { - if (e.getRepeatCount() != 0) // ignore auto-repeat - return true; - if (e.getAction() == KeyEvent.ACTION_UP || e.getAction() == KeyEvent.ACTION_DOWN) - lorieView.sendMouseEvent(-1, -1, InputStub.BUTTON_RIGHT, e.getAction() == KeyEvent.ACTION_DOWN, true); - return true; - } - - if (e.getScanCode() == KEY_BACK && e.getDevice().getKeyboardType() != KEYBOARD_TYPE_ALPHABETIC || e.getScanCode() == 0) { - if (toggleIMEUsingBackKey && e.getAction() == ACTION_UP) - toggleKeyboardVisibility(MainActivity.this); - - return true; - } - } - - result = mInputHandler.sendKeyEvent(e); + boolean result = mInputHandler.sendKeyEvent(e); // Do not steal dedicated buttons from a full external keyboard. if (useTermuxEKBarBehaviour && mExtraKeys != null && (dev == null || dev.isVirtual())) @@ -515,7 +477,6 @@ void onPreferencesChanged(String key) { mInputHandler.reloadPreferences(p); lorieView.reloadPreferences(p); - captureVolumeKeys = p.getBoolean("captureVolumeKeys", true); setTerminalToolbarView(); onWindowFocusChanged(true); @@ -544,9 +505,7 @@ void onPreferencesChanged(String key) { } else if (checkSelfPermission(WRITE_SECURE_SETTINGS) == PERMISSION_GRANTED) KeyInterceptor.shutdown(); - hideEKOnVolDown = p.getBoolean("hideEKOnVolDown", false); useTermuxEKBarBehaviour = p.getBoolean("useTermuxEKBarBehaviour", false); - toggleIMEUsingBackKey = p.getBoolean("toggleIMEUsingBackKey", true); showIMEWhileExternalConnected = p.getBoolean("showIMEWhileExternalConnected", true); int requestedOrientation = p.getBoolean("forceLandscape", false) ? @@ -841,6 +800,8 @@ public static void toggleKeyboardVisibility(Context context) { inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); else inputMethodManager.hideSoftInputFromWindow(getInstance().getLorieView().getWindowToken(), 0); + + getInstance().getLorieView().requestFocus(); } } @@ -864,6 +825,13 @@ void clientConnectedStateChanged(boolean connected) { }); } + public static boolean isConnected() { + if (getInstance() == null) + return false; + + return getInstance().mClientConnected; + } + private void checkXEvents() { getLorieView().handleXEvents(); handler.postDelayed(this::checkXEvents, 300); diff --git a/app/src/main/java/com/termux/x11/input/InputStub.java b/app/src/main/java/com/termux/x11/input/InputStub.java index 873e92b50..3b0de2473 100644 --- a/app/src/main/java/com/termux/x11/input/InputStub.java +++ b/app/src/main/java/com/termux/x11/input/InputStub.java @@ -37,7 +37,6 @@ public interface InputStub { * methods. */ void sendTextEvent(byte[] utf8Bytes); - void sendUnicodeEvent(int code); /** Sends an event, not flushing connection. */ void sendTouchEvent(int action, int pointerId, int x, int y); 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 adf168afe..dffca5a51 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -4,8 +4,14 @@ package com.termux.x11.input; +import static android.view.InputDevice.KEYBOARD_TYPE_ALPHABETIC; +import static android.view.KeyEvent.KEYCODE_BACK; +import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN; +import static android.view.KeyEvent.KEYCODE_VOLUME_UP; + import android.annotation.SuppressLint; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.graphics.PointF; import android.hardware.input.InputManager; @@ -22,6 +28,7 @@ import androidx.annotation.IntDef; import androidx.core.math.MathUtils; +import com.termux.x11.LoriePreferences; import com.termux.x11.LorieView; import com.termux.x11.MainActivity; import com.termux.x11.utils.SamsungDexUtils; @@ -32,6 +39,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; /** * This class is responsible for handling Touch input from the user. Touch events which manipulate @@ -64,7 +72,6 @@ public class TouchInputHandler { } private final RenderData mRenderData; - private final RenderStub mRenderStub; private final GestureDetector mScroller; private final TapGestureDetector mTapDetector; private final StylusListener mStylusListener = new StylusListener(); @@ -80,6 +87,15 @@ public class TouchInputHandler { private final MainActivity mActivity; private final DisplayMetrics mMetrics = new DisplayMetrics(); + private final Consumer noAction = (down) -> {}; + private Consumer swipeUpAction = noAction; + private Consumer swipeDownAction = noAction; + private Consumer volumeUpAction = noAction; + private Consumer volumeDownAction = noAction; + private Consumer backButtonAction = noAction; + + private static final int KEY_BACK = 158; + private boolean keyIntercepting = false; /** @@ -114,12 +130,11 @@ public class TouchInputHandler { @CapturedPointerTransformation int capturedPointerTransformation = CapturedPointerTransformation.NONE; - private TouchInputHandler(MainActivity activity, RenderData renderData, RenderStub renderStub, + private TouchInputHandler(MainActivity activity, RenderData renderData, final InputEventSender injector, boolean isTouchpad) { - if (renderStub == null || injector == null) + if (injector == null) throw new NullPointerException(); - mRenderStub = renderStub; mRenderData = renderData != null ? renderData :new RenderData(); mInjector = injector; mActivity = activity; @@ -146,7 +161,7 @@ private TouchInputHandler(MainActivity activity, RenderData renderData, RenderSt setInputMode(InputMode.TRACKPAD); mDexListener = new DexListener(activity); - mTouchpadHandler = isTouchpad ? null : new TouchInputHandler(activity, mRenderData, renderStub, injector, true); + mTouchpadHandler = isTouchpad ? null : new TouchInputHandler(activity, mRenderData, injector, true); refreshInputDevices(); ((InputManager) mActivity.getSystemService(Context.INPUT_SERVICE)).registerInputDeviceListener(new InputManager.InputDeviceListener() { @@ -157,10 +172,9 @@ public void onInputDeviceAdded(int deviceId) { refreshInputDevices(); } - /** @noinspection DataFlowIssue*/ @Override public void onInputDeviceRemoved(int deviceId) { - android.util.Log.d("InputDeviceListener", "removed " + InputDevice.getDevice(deviceId).getName()); + android.util.Log.d("InputDeviceListener", "device removed"); refreshInputDevices(); } @@ -174,8 +188,8 @@ public void onInputDeviceChanged(int deviceId) { } - public TouchInputHandler(MainActivity activity, RenderStub renderStub, final InputEventSender injector) { - this(activity, null, renderStub, injector, false); + public TouchInputHandler(MainActivity activity, final InputEventSender injector) { + this(activity, null, injector, false); } static public void refreshInputDevices() { @@ -190,7 +204,7 @@ static public void refreshInputDevices() { android.util.Log.d("DEVICES", "found device \"" + device.getName() + "\" " + (device.supportsSource(InputDevice.SOURCE_STYLUS) ? ((isExternal(device) ? "external " : "") + "stylus ") : "") + ((device.supportsSource(InputDevice.SOURCE_KEYBOARD) && device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) ? ((isExternal(device) ? "external " : "") + "keyboard ") : "") + - "sources " + String.format("0x%08X", device.getSources()) + " " + device.supportsSource(InputDevice.SOURCE_KEYBOARD) + " " + (device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC) + " " + isExternal(device) + " " + (device.supportsSource(InputDevice.SOURCE_KEYBOARD) && device.getKeyboardType() == InputDevice.KEYBOARD_TYPE_ALPHABETIC && isExternal(device))); + "sources " + String.format("0x%08X", device.getSources())); if (device.supportsSource(InputDevice.SOURCE_STYLUS)) stylusAvailable.set(true); @@ -393,10 +407,27 @@ public void reloadPreferences(SharedPreferences p) { keyIntercepting = !mInjector.pauseKeyInterceptingWithEsc || mActivity.getLorieView().hasPointerCapture(); SamsungDexUtils.dexMetaKeyCapture(mActivity, mInjector.dexMetaKeyCapture && keyIntercepting); + swipeUpAction = extractUserActionFromPreferences(p, "swipeUp", noAction); + swipeDownAction = extractUserActionFromPreferences(p, "swipeDown", (down) -> { if (down) mActivity.toggleExtraKeys(); }); + volumeUpAction = extractUserActionFromPreferences(p, "volumeUp", (down) -> MainActivity.getInstance().getLorieView().sendKeyEvent(0, KEYCODE_VOLUME_UP, down)); + volumeDownAction = extractUserActionFromPreferences(p, "volumeDown", (down) -> MainActivity.getInstance().getLorieView().sendKeyEvent(0, KEYCODE_VOLUME_DOWN, down)); + backButtonAction = extractUserActionFromPreferences(p, "backButton", (down) -> { if (down) MainActivity.toggleKeyboardVisibility(mActivity); } ); + if(mTouchpadHandler != null) mTouchpadHandler.reloadPreferences(p); } + private Consumer extractUserActionFromPreferences(SharedPreferences p, String name, Consumer defaultAction) { + switch(p.getString(name + "Action", name.contains("volume") ? "no" : "default")) { + case "no action": return noAction; + case "toggle soft keyboard": return (down) -> { if (down) MainActivity.toggleKeyboardVisibility(mActivity); }; + case "toggle additional key bar": return (down) -> { if (down) mActivity.toggleExtraKeys(); }; + case "open preferences": return (down) -> { if (down) mActivity.startActivity(new Intent(mActivity, LoriePreferences.class) {{ setAction(Intent.ACTION_MAIN); }}); }; + case "release pointer and keyboard capture": return (down) -> { if (down) setCapturingEnabled(false); }; + default: return defaultAction; + } + } + public boolean shouldInterceptKeys() { return !mInjector.pauseKeyInterceptingWithEsc || keyIntercepting; } @@ -425,9 +456,9 @@ private void moveCursorToScreenPoint(float screenX, float screenY) { /** Processes a (multi-finger) swipe gesture. */ private boolean onSwipe() { if (mTotalMotionY > mSwipeThreshold) - mRenderStub.swipeDown(); + swipeDownAction.accept(true); else if (mTotalMotionY < -mSwipeThreshold) - mRenderStub.swipeUp(); + swipeUpAction.accept(true); else return false; @@ -520,7 +551,6 @@ public void onTap(int pointerCount, float x, float y) { mGestureListenerHandler.sendEmptyMessageDelayed(InputStub.BUTTON_LEFT, ViewConfiguration.getDoubleTapTimeout()); } - private float mLastFocusX; private float mLastFocusY; @Override @@ -589,8 +619,48 @@ private boolean screenPointLiesOutsideImageBoundary(float screenX, float screenY } } - public boolean sendKeyEvent(KeyEvent event) { - return mInjector.sendKeyEvent(event); + public boolean sendKeyEvent(KeyEvent e) { + int k = e.getKeyCode(); + + if (!MainActivity.isConnected()) { + if (e.getKeyCode() == KEYCODE_BACK) + mActivity.finish(); + + return false; + } + + if (k == KEYCODE_VOLUME_DOWN) { + if (volumeDownAction == noAction) + return false; + + volumeDownAction.accept(e.getAction() == KeyEvent.ACTION_DOWN); + return true; + } + + if (k == KEYCODE_VOLUME_UP) { + if (volumeUpAction == noAction) + return false; + + volumeUpAction.accept(e.getAction() == KeyEvent.ACTION_DOWN); + return true; + } + + if (k == KEYCODE_BACK) { + if (e.isFromSource(InputDevice.SOURCE_MOUSE) || e.isFromSource(InputDevice.SOURCE_MOUSE_RELATIVE)) { + if (e.getRepeatCount() != 0) // ignore auto-repeat + return true; + if (e.getAction() == KeyEvent.ACTION_UP || e.getAction() == KeyEvent.ACTION_DOWN) + mActivity.getLorieView().sendMouseEvent(-1, -1, InputStub.BUTTON_RIGHT, e.getAction() == KeyEvent.ACTION_DOWN, true); + return true; + } + + if (e.getScanCode() == KEY_BACK && e.getDevice().getKeyboardType() != KEYBOARD_TYPE_ALPHABETIC || e.getScanCode() == 0) { + backButtonAction.accept(e.getAction() == KeyEvent.ACTION_DOWN); + return true; + } + } + + return mInjector.sendKeyEvent(e); } private class HardwareMouseListener { @@ -664,10 +734,6 @@ boolean onTouch(View v, MotionEvent e) { } private class StylusListener { - // I've got this on SM-N770F - private static final int ACTION_PRIMARY_DOWN = 0xd3; - private static final int ACTION_PRIMARY_UP = 0xd4; - private float x = 0, y = 0, pressure = 0, tilt = 0, orientation = 0; private int buttons = 0; @@ -888,25 +954,4 @@ public boolean onSingleTapConfirmed(MotionEvent e) { return true; } } - - - /** - * Interface with a set of functions to control the behavior of the remote host renderer. - */ - public interface RenderStub { - /** - * Informs the stub that swipe was performed. - */ - void swipeUp(); - - /** - * Informs the stub that swipe was performed. - */ - void swipeDown(); - - class NullStub implements RenderStub { - @Override public void swipeUp() {} - @Override public void swipeDown() {} - } - } } diff --git a/app/src/main/res/layout/main_activity.xml b/app/src/main/res/layout/main_activity.xml index 26c76458a..c1f062720 100644 --- a/app/src/main/res/layout/main_activity.xml +++ b/app/src/main/res/layout/main_activity.xml @@ -78,6 +78,17 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/help_button_text" /> + + + +