From e98e8c155667c993332f2af4b80b7393c7618470 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Fri, 24 Feb 2023 16:41:17 +0200 Subject: [PATCH 01/21] WIP moving fake compositor to CmdEntryPoint, getting rid of background service, making MainActivity work as X11 client... --- .gitmodules | 29 +- app/build.gradle | 14 +- app/src/main/AndroidManifest.xml | 13 +- .../com/termux/x11/ICmdEntryInterface.aidl | 7 + .../com/termux/x11/ITermuxX11Internal.aidl | 8 - .../src/main/cpp/CMakeLists.txt | 44 +- app/src/main/cpp/libXau | 1 + .../src/main/cpp/libxcb | 0 .../src/main/cpp/libxcb-errors | 0 .../src/main/cpp/libxcvt | 0 app/src/main/cpp/loading_sign.c | 5 + .../main/cpp/lorie-client/lorie-client.cpp | 16 +- .../cpp/lorie-client}/lorie_message_queue.cpp | 0 .../lorie-client}/lorie_message_queue.hpp | 0 app/src/main/{jni => cpp}/lorie/Android.mk | 6 - app/src/main/cpp/lorie/compositor.cpp | 337 +++++++ app/src/main/{jni => cpp}/lorie/en_us.xkbmap | 0 .../main/cpp/lorie/lorie_wayland_server.cpp | 100 ++ .../lorie/lorie_wayland_server.hpp | 77 -- app/src/main/cpp/wayland | 1 + app/src/main/{jni => cpp}/wayland.patch | 0 app/src/main/{jni => cpp}/wayland2.patch | 0 app/src/main/cpp/xcbproto | 1 + app/src/main/cpp/xorgproto | 1 + .../java/com/termux/x11/CmdEntryPoint.java | 321 +++---- .../java/com/termux/x11/LoriePreferences.java | 24 +- .../java/com/termux/x11/LorieService.java | 495 ---------- .../java/com/termux/x11/MainActivity.java | 210 ++++- .../termux/x11/TermuxX11StarterReceiver.java | 155 ---- .../termux/x11/starter/ActivityManager.java | 197 ---- .../java/com/termux/x11/starter/Compat.java | 77 -- app/src/main/jni/Android.mk | 23 - app/src/main/jni/Application.mk | 1 - app/src/main/jni/lorie/android.cpp | 379 -------- .../main/jni/lorie/backend/android/keymaps.h | 245 ----- .../backend/android/locale/android-keycodes.h | 752 --------------- .../backend/android/locale/android-utils.c | 175 ---- .../backend/android/locale/evdev-keycodes.h | 296 ------ .../lorie/backend/android/locale/generator | Bin 29168 -> 0 bytes .../lorie/backend/android/locale/generator.c | 360 -------- .../android/locale/input-event-codes.h | 861 ------------------ .../jni/lorie/backend/android/locale/log.h | 56 -- .../jni/lorie/backend/android/locale/make.sh | 10 - .../jni/lorie/backend/android/locale/test.c | 58 -- .../main/jni/lorie/backend/android/utils.c | 30 - app/src/main/jni/lorie/compositor.cpp | 272 ------ app/src/main/jni/lorie/include/ashmem.h | 47 - app/src/main/jni/lorie/include/log.h | 23 - .../jni/lorie/include/lorie_compositor.hpp | 88 -- app/src/main/jni/lorie/include/shm.h | 22 - .../main/jni/lorie/lorie_message_queue.cpp | 43 - .../main/jni/lorie/lorie_wayland_server.cpp | 210 ----- app/src/main/jni/lorie/utils/log.cpp | 74 -- app/src/main/jni/starter/Android.mk | 7 - app/src/main/jni/starter/starter.c | 182 ---- app/src/main/jni/wayland | 1 - app/stub/build.gradle | 28 - app/stub/src/main/AndroidManifest.xml | 1 - .../aidl/android/content/IIntentReceiver.aidl | 33 - .../android/app/ActivityManagerNative.java | 9 - .../java/android/app/ActivityTaskManager.java | 7 - .../java/android/app/IActivityManager.java | 31 - .../android/app/IActivityTaskManager.java | 23 - .../java/android/app/IApplicationThread.java | 4 - .../main/java/android/app/ProfilerInfo.java | 4 - .../java/android/content/IIntentSender.java | 32 - .../main/java/android/os/ServiceManager.java | 13 - .../android/internal/app/IAppOpsService.java | 11 - settings.gradle | 2 - x11-client-experimental/.gitignore | 1 - x11-client-experimental/build.gradle | 64 -- .../src/main/AndroidManifest.xml | 39 - .../termux/x11/ICmdEntryPointInterface.aidl | 8 - x11-client-experimental/src/main/cpp/libXau | 1 - .../src/main/cpp/loading_sign.c | 5 - .../main/cpp/lorie/lorie_message_queue.hpp | 17 - x11-client-experimental/src/main/cpp/xcbproto | 1 - .../src/main/cpp/xorgproto | 1 - .../src/main/ic_launcher-web.png | Bin 22338 -> 0 bytes .../java/com/termux/x11/CmdEntryPoint.java | 63 -- .../java/com/termux/x11/MainActivity.java | 74 -- .../res/drawable-anydpi-v24/ic_x11_icon.xml | 22 - .../main/res/drawable-hdpi/ic_x11_icon.png | Bin 786 -> 0 bytes .../main/res/drawable-mdpi/ic_x11_icon.png | Bin 522 -> 0 bytes .../main/res/drawable-xhdpi/ic_x11_icon.png | Bin 1056 -> 0 bytes .../main/res/drawable-xxhdpi/ic_x11_icon.png | Bin 1670 -> 0 bytes .../res/layout/getting_started_activity.xml | 128 --- .../src/main/res/layout/main_activity.xml | 20 - .../res/layout/partial_primary_toolbar.xml | 22 - .../main/res/layout/preferences_toolbar.xml | 7 - .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 - .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 - .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 1927 -> 0 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 2984 -> 0 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 3279 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 1286 -> 0 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 1802 -> 0 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 2023 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 2723 -> 0 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 4087 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 4614 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 4279 -> 0 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 7071 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 7579 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 6124 -> 0 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 10554 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 10839 -> 0 bytes .../src/main/res/values/arrays.xml | 11 - .../src/main/res/values/colors.xml | 8 - .../src/main/res/values/dimens.xml | 5 - .../res/values/ic_launcher_background.xml | 4 - .../src/main/res/values/strings.xml | 39 - .../src/main/res/values/styles.xml | 47 - .../src/main/res/xml/preferences.xml | 52 -- .../src/main/res/xml/shortcuts.xml | 15 - 115 files changed, 831 insertions(+), 6385 deletions(-) create mode 100644 app/src/main/aidl/com/termux/x11/ICmdEntryInterface.aidl delete mode 100644 app/src/main/aidl/com/termux/x11/ITermuxX11Internal.aidl rename {x11-client-experimental => app}/src/main/cpp/CMakeLists.txt (76%) create mode 160000 app/src/main/cpp/libXau rename {x11-client-experimental => app}/src/main/cpp/libxcb (100%) rename {x11-client-experimental => app}/src/main/cpp/libxcb-errors (100%) rename {x11-client-experimental => app}/src/main/cpp/libxcvt (100%) create mode 100644 app/src/main/cpp/loading_sign.c rename x11-client-experimental/src/main/cpp/lorie/lorie.cpp => app/src/main/cpp/lorie-client/lorie-client.cpp (97%) rename {x11-client-experimental/src/main/cpp/lorie => app/src/main/cpp/lorie-client}/lorie_message_queue.cpp (100%) rename app/src/main/{jni/lorie/include => cpp/lorie-client}/lorie_message_queue.hpp (100%) rename app/src/main/{jni => cpp}/lorie/Android.mk (77%) create mode 100644 app/src/main/cpp/lorie/compositor.cpp rename app/src/main/{jni => cpp}/lorie/en_us.xkbmap (100%) create mode 100644 app/src/main/cpp/lorie/lorie_wayland_server.cpp rename app/src/main/{jni => cpp}/lorie/lorie_wayland_server.hpp (68%) create mode 160000 app/src/main/cpp/wayland rename app/src/main/{jni => cpp}/wayland.patch (100%) rename app/src/main/{jni => cpp}/wayland2.patch (100%) create mode 160000 app/src/main/cpp/xcbproto create mode 160000 app/src/main/cpp/xorgproto delete mode 100644 app/src/main/java/com/termux/x11/LorieService.java delete mode 100644 app/src/main/java/com/termux/x11/TermuxX11StarterReceiver.java delete mode 100644 app/src/main/java/com/termux/x11/starter/ActivityManager.java delete mode 100644 app/src/main/java/com/termux/x11/starter/Compat.java delete mode 100644 app/src/main/jni/Android.mk delete mode 100644 app/src/main/jni/Application.mk delete mode 100644 app/src/main/jni/lorie/android.cpp delete mode 100644 app/src/main/jni/lorie/backend/android/keymaps.h delete mode 100644 app/src/main/jni/lorie/backend/android/locale/android-keycodes.h delete mode 100644 app/src/main/jni/lorie/backend/android/locale/android-utils.c delete mode 100644 app/src/main/jni/lorie/backend/android/locale/evdev-keycodes.h delete mode 100755 app/src/main/jni/lorie/backend/android/locale/generator delete mode 100644 app/src/main/jni/lorie/backend/android/locale/generator.c delete mode 100644 app/src/main/jni/lorie/backend/android/locale/input-event-codes.h delete mode 100644 app/src/main/jni/lorie/backend/android/locale/log.h delete mode 100755 app/src/main/jni/lorie/backend/android/locale/make.sh delete mode 100644 app/src/main/jni/lorie/backend/android/locale/test.c delete mode 100644 app/src/main/jni/lorie/backend/android/utils.c delete mode 100644 app/src/main/jni/lorie/compositor.cpp delete mode 100644 app/src/main/jni/lorie/include/ashmem.h delete mode 100644 app/src/main/jni/lorie/include/log.h delete mode 100644 app/src/main/jni/lorie/include/lorie_compositor.hpp delete mode 100644 app/src/main/jni/lorie/include/shm.h delete mode 100644 app/src/main/jni/lorie/lorie_message_queue.cpp delete mode 100644 app/src/main/jni/lorie/lorie_wayland_server.cpp delete mode 100755 app/src/main/jni/lorie/utils/log.cpp delete mode 100755 app/src/main/jni/starter/Android.mk delete mode 100755 app/src/main/jni/starter/starter.c delete mode 160000 app/src/main/jni/wayland delete mode 100644 app/stub/build.gradle delete mode 100644 app/stub/src/main/AndroidManifest.xml delete mode 100644 app/stub/src/main/aidl/android/content/IIntentReceiver.aidl delete mode 100644 app/stub/src/main/java/android/app/ActivityManagerNative.java delete mode 100644 app/stub/src/main/java/android/app/ActivityTaskManager.java delete mode 100644 app/stub/src/main/java/android/app/IActivityManager.java delete mode 100644 app/stub/src/main/java/android/app/IActivityTaskManager.java delete mode 100644 app/stub/src/main/java/android/app/IApplicationThread.java delete mode 100644 app/stub/src/main/java/android/app/ProfilerInfo.java delete mode 100644 app/stub/src/main/java/android/content/IIntentSender.java delete mode 100644 app/stub/src/main/java/android/os/ServiceManager.java delete mode 100644 app/stub/src/main/java/com/android/internal/app/IAppOpsService.java delete mode 100755 x11-client-experimental/.gitignore delete mode 100755 x11-client-experimental/build.gradle delete mode 100644 x11-client-experimental/src/main/AndroidManifest.xml delete mode 100644 x11-client-experimental/src/main/aidl/com/termux/x11/ICmdEntryPointInterface.aidl delete mode 160000 x11-client-experimental/src/main/cpp/libXau delete mode 100644 x11-client-experimental/src/main/cpp/loading_sign.c delete mode 100644 x11-client-experimental/src/main/cpp/lorie/lorie_message_queue.hpp delete mode 160000 x11-client-experimental/src/main/cpp/xcbproto delete mode 160000 x11-client-experimental/src/main/cpp/xorgproto delete mode 100644 x11-client-experimental/src/main/ic_launcher-web.png delete mode 100644 x11-client-experimental/src/main/java/com/termux/x11/CmdEntryPoint.java delete mode 100644 x11-client-experimental/src/main/java/com/termux/x11/MainActivity.java delete mode 100644 x11-client-experimental/src/main/res/drawable-anydpi-v24/ic_x11_icon.xml delete mode 100644 x11-client-experimental/src/main/res/drawable-hdpi/ic_x11_icon.png delete mode 100644 x11-client-experimental/src/main/res/drawable-mdpi/ic_x11_icon.png delete mode 100644 x11-client-experimental/src/main/res/drawable-xhdpi/ic_x11_icon.png delete mode 100644 x11-client-experimental/src/main/res/drawable-xxhdpi/ic_x11_icon.png delete mode 100644 x11-client-experimental/src/main/res/layout/getting_started_activity.xml delete mode 100644 x11-client-experimental/src/main/res/layout/main_activity.xml delete mode 100644 x11-client-experimental/src/main/res/layout/partial_primary_toolbar.xml delete mode 100644 x11-client-experimental/src/main/res/layout/preferences_toolbar.xml delete mode 100644 x11-client-experimental/src/main/res/mipmap-anydpi-v26/ic_launcher.xml delete mode 100644 x11-client-experimental/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml delete mode 100644 x11-client-experimental/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 x11-client-experimental/src/main/res/mipmap-hdpi/ic_launcher_foreground.png delete mode 100644 x11-client-experimental/src/main/res/mipmap-hdpi/ic_launcher_round.png delete mode 100644 x11-client-experimental/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 x11-client-experimental/src/main/res/mipmap-mdpi/ic_launcher_foreground.png delete mode 100644 x11-client-experimental/src/main/res/mipmap-mdpi/ic_launcher_round.png delete mode 100644 x11-client-experimental/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 x11-client-experimental/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png delete mode 100644 x11-client-experimental/src/main/res/mipmap-xhdpi/ic_launcher_round.png delete mode 100644 x11-client-experimental/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 x11-client-experimental/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png delete mode 100644 x11-client-experimental/src/main/res/mipmap-xxhdpi/ic_launcher_round.png delete mode 100644 x11-client-experimental/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 x11-client-experimental/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png delete mode 100644 x11-client-experimental/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png delete mode 100644 x11-client-experimental/src/main/res/values/arrays.xml delete mode 100644 x11-client-experimental/src/main/res/values/colors.xml delete mode 100644 x11-client-experimental/src/main/res/values/dimens.xml delete mode 100644 x11-client-experimental/src/main/res/values/ic_launcher_background.xml delete mode 100644 x11-client-experimental/src/main/res/values/strings.xml delete mode 100644 x11-client-experimental/src/main/res/values/styles.xml delete mode 100644 x11-client-experimental/src/main/res/xml/preferences.xml delete mode 100644 x11-client-experimental/src/main/res/xml/shortcuts.xml diff --git a/.gitmodules b/.gitmodules index b7a790d44..4e886ce89 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,27 +1,28 @@ -[submodule "app/src/main/jni/wayland"] - path = app/src/main/jni/wayland +[submodule "app/src/main/cpp/wayland"] + path = app/src/main/cpp/wayland url = https://gitlab.freedesktop.org/wayland/wayland ignore = dirty -[submodule "x11-client-experimental/src/main/cpp/libxcb"] - path = x11-client-experimental/src/main/cpp/libxcb +[submodule "app/src/main/cpp/libxcb"] + path = app/src/main/cpp/libxcb url = https://gitlab.freedesktop.org/xorg/lib/libxcb ignore = dirty -[submodule "x11-client-experimental/src/main/cpp/xorgproto"] - path = x11-client-experimental/src/main/cpp/xorgproto +[submodule "app/src/main/cpp/xorgproto"] + path = app/src/main/cpp/xorgproto url = https://gitlab.freedesktop.org/xorg/proto/xorgproto ignore = dirty -[submodule "x11-client-experimental/src/main/cpp/xcbproto"] - path = x11-client-experimental/src/main/cpp/xcbproto +[submodule "app/src/main/cpp/xcbproto"] + path = app/src/main/cpp/xcbproto url = https://gitlab.freedesktop.org/xorg/proto/xcbproto ignore = dirty -[submodule "x11-client-experimental/src/main/cpp/libXau"] - path = x11-client-experimental/src/main/cpp/libXau +[submodule "app/src/main/cpp/libXau"] + path = app/src/main/cpp/libXau url = https://gitlab.freedesktop.org/xorg/lib/libxau ignore = dirty -[submodule "x11-client-experimental/src/main/cpp/libxcb-errors"] - path = x11-client-experimental/src/main/cpp/libxcb-errors +[submodule "app/src/main/cpp/libxcb-errors"] + path = app/src/main/cpp/libxcb-errors url = https://gitlab.freedesktop.org/xorg/lib/libxcb-errors ignore = dirty -[submodule "x11-client-experimental/src/main/cpp/libxcvt"] - path = x11-client-experimental/src/main/cpp/libxcvt +[submodule "app/src/main/cpp/libxcvt"] + path = app/src/main/cpp/libxcvt url = https://gitlab.freedesktop.org/xorg/lib/libxcvt + ignore = dirty diff --git a/app/build.gradle b/app/build.gradle index e68fc7c7a..2ef616d53 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,8 +20,8 @@ android { versionName "1.02.06" externalNativeBuild { - ndkBuild { - arguments "WAYLAND_GENERATED=${waylandOut}" + cmake { + arguments "-DWAYLAND_GENERATED_DIR=${waylandOut}" } } } @@ -52,8 +52,8 @@ android { } externalNativeBuild { - ndkBuild { - path "src/main/jni/Android.mk" + cmake { + path "src/main/cpp/CMakeLists.txt" } } @@ -69,8 +69,8 @@ android { task patchWayland { println("Patch can fail here, it is normal if a patch was already applied") - ant.patch(patchfile: "${project.projectDir}/src/main/jni/wayland.patch", dir: "${project.projectDir}/src/main/jni/wayland/src", failonerror:false) - ant.patch(patchfile: "${project.projectDir}/src/main/jni/wayland2.patch", dir: "${project.projectDir}/src/main/jni/wayland", failonerror:false) + ant.patch(patchfile: "${project.projectDir}/src/main/cpp/wayland.patch", dir: "${project.projectDir}/src/main/cpp/wayland/src", failonerror:false) + ant.patch(patchfile: "${project.projectDir}/src/main/cpp/wayland2.patch", dir: "${project.projectDir}/src/main/cpp/wayland", failonerror:false) } tasks.matching { it.name.toLowerCase().contains("ndk") && !it.name.toLowerCase().contains("clean") } @@ -95,5 +95,5 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' implementation 'androidx.annotation:annotation:1.5.0' implementation 'androidx.drawerlayout:drawerlayout:1.1.1' - compileOnly project(':app:stub') + compileOnly project(':shell-loader:stub') } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 83b7f8077..125a85dee 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,12 +18,7 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme" - tools:ignore="GoogleAppIndexingWarning" - > - + tools:ignore="GoogleAppIndexingWarning"> + android:resizeableActivity="true" + android:exported="true"> @@ -54,9 +50,6 @@ android:name=".GettingStartedActivity" android:theme="@style/Theme.BaseActivity.DayNight.NoActionBar" android:exported="true" /> - diff --git a/app/src/main/aidl/com/termux/x11/ICmdEntryInterface.aidl b/app/src/main/aidl/com/termux/x11/ICmdEntryInterface.aidl new file mode 100644 index 000000000..8c125d557 --- /dev/null +++ b/app/src/main/aidl/com/termux/x11/ICmdEntryInterface.aidl @@ -0,0 +1,7 @@ +package com.termux.x11; + +// This interface is used by utility on termux side. +interface ICmdEntryInterface { + void outputResize(int width, int height); + ParcelFileDescriptor getXConnection(); +} \ No newline at end of file diff --git a/app/src/main/aidl/com/termux/x11/ITermuxX11Internal.aidl b/app/src/main/aidl/com/termux/x11/ITermuxX11Internal.aidl deleted file mode 100644 index d3c4aa868..000000000 --- a/app/src/main/aidl/com/termux/x11/ITermuxX11Internal.aidl +++ /dev/null @@ -1,8 +0,0 @@ -package com.termux.x11; - -// This interface is used by utility on termux side. -interface ITermuxX11Internal { - ParcelFileDescriptor getWaylandFD(); - ParcelFileDescriptor getLogFD(); - void finish(); -} \ No newline at end of file diff --git a/x11-client-experimental/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt similarity index 76% rename from x11-client-experimental/src/main/cpp/CMakeLists.txt rename to app/src/main/cpp/CMakeLists.txt index aa5adcf4e..5ec8b33a6 100644 --- a/x11-client-experimental/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -4,6 +4,28 @@ set(CMAKE_CXX_STANDARD 20) find_package(Python3 REQUIRED) +#################################################################################################### +######################################### LIBWAYLAND ############################################### +#################################################################################################### + +set(WAYLAND_SOURCES connection.c event-loop.c wayland-protocol.c wayland-server.c wayland-shm.c wayland-os.c wayland-util.c) +list(TRANSFORM WAYLAND_SOURCES PREPEND ${CMAKE_CURRENT_SOURCE_DIR}/wayland/src/) +add_library(wayland-server SHARED ${WAYLAND_SOURCES}) +target_include_directories(wayland-server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/wayland ${CMAKE_CURRENT_SOURCE_DIR}/wayland/src) + +#################################################################################################### +########################################### LORIE ################################################## +#################################################################################################### + +file(GLOB WAYLAND_GENERATED "${WAYLAND_GENERATED_DIR}/*.cpp") +add_library(lorie SHARED + ${CMAKE_CURRENT_SOURCE_DIR}/lorie/compositor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/lorie/lorie_wayland_server.cpp + ${WAYLAND_GENERATED} + ) +target_include_directories(lorie PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/lorie ${WAYLAND_GENERATED_DIR}) +target_link_libraries(lorie wayland-server "-landroid" "-llog") + # Nothing special here, only generating xcb headers and building xcb... #################################################################################################### @@ -73,6 +95,15 @@ target_include_directories(xcb PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/libxcb/src/" target_compile_options(xcb PRIVATE "-DXCB_QUEUE_BUFFER_SIZE=16384" "-DHAVE_SENDMSG") target_link_libraries(xcb Xau) +# Those are stubs and needed to check if termux-built xcb behaves the same way +# All the functions they should contain are contained in libxcb itself +add_library(xcb-shm SHARED ${CMAKE_CURRENT_SOURCE_DIR}/loading_sign.c) +add_library(xcb-xfixes SHARED ${CMAKE_CURRENT_SOURCE_DIR}/loading_sign.c) +add_library(xcb-xinput SHARED ${CMAKE_CURRENT_SOURCE_DIR}/loading_sign.c) +add_library(xcb-damage SHARED ${CMAKE_CURRENT_SOURCE_DIR}/loading_sign.c) + +# TODO: strip unneded symbols to reduce shared library size... + #################################################################################################### ####################################### LIBXCB-ERRORS ############################################## #################################################################################################### @@ -106,13 +137,10 @@ add_library(xcvt "${CMAKE_CURRENT_SOURCE_DIR}/libxcvt/lib/libxcvt.c") target_include_directories(xcvt PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/libxcvt/include") #################################################################################################### -########################################### LORIE ################################################## +####################################### LORIE-CLIENT ############################################### #################################################################################################### -add_library(xcb-shm SHARED ${CMAKE_CURRENT_SOURCE_DIR}/loading_sign.c) -add_library(xcb-xfixes SHARED ${CMAKE_CURRENT_SOURCE_DIR}/loading_sign.c) -add_library(xcb-xinput SHARED ${CMAKE_CURRENT_SOURCE_DIR}/loading_sign.c) -add_library(xcb-damage SHARED ${CMAKE_CURRENT_SOURCE_DIR}/loading_sign.c) - -add_library(lorie SHARED lorie/lorie.cpp lorie/lorie_message_queue.cpp) -target_link_libraries(lorie xcbproto xcb xcb-errors xcb-shm xcb-xfixes xcb-xinput xcb-damage xcvt "-llog" "-landroid") +add_library(lorie-client SHARED + ${CMAKE_CURRENT_SOURCE_DIR}/lorie-client/lorie-client.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/lorie-client/lorie_message_queue.cpp) +target_link_libraries(lorie-client xcbproto xcb xcb-errors xcb-shm xcb-xfixes xcb-xinput xcb-damage xcvt "-llog" "-landroid") \ No newline at end of file diff --git a/app/src/main/cpp/libXau b/app/src/main/cpp/libXau new file mode 160000 index 000000000..c52f54e95 --- /dev/null +++ b/app/src/main/cpp/libXau @@ -0,0 +1 @@ +Subproject commit c52f54e9533046a52edf84bcc02abedc2dbcb1a7 diff --git a/x11-client-experimental/src/main/cpp/libxcb b/app/src/main/cpp/libxcb similarity index 100% rename from x11-client-experimental/src/main/cpp/libxcb rename to app/src/main/cpp/libxcb diff --git a/x11-client-experimental/src/main/cpp/libxcb-errors b/app/src/main/cpp/libxcb-errors similarity index 100% rename from x11-client-experimental/src/main/cpp/libxcb-errors rename to app/src/main/cpp/libxcb-errors diff --git a/x11-client-experimental/src/main/cpp/libxcvt b/app/src/main/cpp/libxcvt similarity index 100% rename from x11-client-experimental/src/main/cpp/libxcvt rename to app/src/main/cpp/libxcvt diff --git a/app/src/main/cpp/loading_sign.c b/app/src/main/cpp/loading_sign.c new file mode 100644 index 000000000..a89680216 --- /dev/null +++ b/app/src/main/cpp/loading_sign.c @@ -0,0 +1,5 @@ +//#include + +//static void __attribute__((__constructor__)) load() { +// puts("libxcb is loaded from apk...\n"); +//} diff --git a/x11-client-experimental/src/main/cpp/lorie/lorie.cpp b/app/src/main/cpp/lorie-client/lorie-client.cpp similarity index 97% rename from x11-client-experimental/src/main/cpp/lorie/lorie.cpp rename to app/src/main/cpp/lorie-client/lorie-client.cpp index 2f2dce90f..9cd2b11dc 100644 --- a/x11-client-experimental/src/main/cpp/lorie/lorie.cpp +++ b/app/src/main/cpp/lorie-client/lorie-client.cpp @@ -645,6 +645,7 @@ Java_com_termux_x11_MainActivity_start(unused JNIEnv *env, unused jobject thiz, extern "C" JNIEXPORT void JNICALL + Java_com_termux_x11_MainActivity_surface(JNIEnv *env, jobject thiz, jobject sfc, jint width, jint height) { ANativeWindow *win = sfc ? ANativeWindow_fromSurface(env, sfc) : nullptr; if (win) @@ -652,19 +653,4 @@ Java_com_termux_x11_MainActivity_surface(JNIEnv *env, jobject thiz, jobject sfc, ALOGE("Got new surface %p", win); lorie_client.post([=] { lorie_client.surface_changed(win, width, height); }); -} - -extern "C" -JNIEXPORT jint JNICALL -Java_com_termux_x11_CmdEntryPoint_connect(JNIEnv *env, jclass clazz) { - struct sockaddr_un remote{ .sun_family=AF_UNIX, .sun_path="/data/data/com.termux/files/usr/tmp/.X11-unix/X0" }; - int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - if (sockfd == -1) { - perror("socket"); - return -1; - } - - std::cerr << "Connecting..." << std::endl; - connect(sockfd, reinterpret_cast(&remote), sizeof(remote)); // NOLINT(bugprone-unused-return-value) - return sockfd; } \ No newline at end of file diff --git a/x11-client-experimental/src/main/cpp/lorie/lorie_message_queue.cpp b/app/src/main/cpp/lorie-client/lorie_message_queue.cpp similarity index 100% rename from x11-client-experimental/src/main/cpp/lorie/lorie_message_queue.cpp rename to app/src/main/cpp/lorie-client/lorie_message_queue.cpp diff --git a/app/src/main/jni/lorie/include/lorie_message_queue.hpp b/app/src/main/cpp/lorie-client/lorie_message_queue.hpp similarity index 100% rename from app/src/main/jni/lorie/include/lorie_message_queue.hpp rename to app/src/main/cpp/lorie-client/lorie_message_queue.hpp diff --git a/app/src/main/jni/lorie/Android.mk b/app/src/main/cpp/lorie/Android.mk similarity index 77% rename from app/src/main/jni/lorie/Android.mk rename to app/src/main/cpp/lorie/Android.mk index 112e316ae..660ed14b0 100644 --- a/app/src/main/jni/lorie/Android.mk +++ b/app/src/main/cpp/lorie/Android.mk @@ -5,16 +5,10 @@ include $(CLEAR_VARS) LOCAL_MODULE := lorie LOCAL_SRC_FILES := \ compositor.cpp \ - lorie_message_queue.cpp \ - utils/log.cpp \ - android.cpp \ - \ - backend/android/utils.c \ lorie_wayland_server.cpp \ $(wildcard $(WAYLAND_GENERATED)/*.cpp) LOCAL_CXXFLAGS := -std=c++20 -#LOCAL_CXXFLAGS += -finstrument-functions LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/../wayland/src $(LOCAL_PATH)/../wayland $(WAYLAND_GENERATED) LOCAL_LDLIBS := -lEGL -lGLESv2 -llog -landroid LOCAL_SHARED_LIBRARIES := wayland-server diff --git a/app/src/main/cpp/lorie/compositor.cpp b/app/src/main/cpp/lorie/compositor.cpp new file mode 100644 index 000000000..ccd70f37c --- /dev/null +++ b/app/src/main/cpp/lorie/compositor.cpp @@ -0,0 +1,337 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DEFAULT_SOCKET_NAME "termux-x11" +#define DEFAULT_SOCKET_PATH "/data/data/com.termux/files/usr/tmp" + +#include + +#ifndef LOG_TAG +#define LOG_TAG "LorieNative" +#endif + +#ifndef LOG +#define LOG(prio, ...) __android_log_print(prio, LOG_TAG, __VA_ARGS__) +#endif + +#define LOGV(...) LOG(ANDROID_LOG_VERBOSE, __VA_ARGS__) +#define LOGE(...) LOG(ANDROID_LOG_ERROR, __VA_ARGS__) + +#pragma ide diagnostic ignored "EndlessLoop" +#pragma clang diagnostic ignored "-Wshadow" + +class lorie_message_queue { +public: + lorie_message_queue() { + std::unique_lock lock(mutex); + + fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (fd == -1) { + LOGE("Failed to create socketpair for message queue: %s", strerror(errno)); + return; + } + }; + + void write(std::function func) { + static uint64_t i = 1; + std::unique_lock lock(mutex); + queue.push(std::move(func)); + ::write(fd, &i, sizeof(uint64_t)); + }; + + static int run(int, uint32_t, void* data) { + auto q = reinterpret_cast(data); + static uint64_t i = 0; + std::unique_lock lock(q->mutex); + std::function fn; + ::read(q->fd, &i, sizeof(uint64_t)); + while(!q->queue.empty()){ + fn = q->queue.front(); + q->queue.pop(); + + lock.unlock(); + fn(); + lock.lock(); + } + + return 0; + }; + + int fd; +private: + std::mutex mutex; + std::queue> queue; +}; + +class lorie_compositor: public wayland::display_t { +public: + explicit lorie_compositor(int dpi); + + void report_mode(int width, int height); + + wayland::global_compositor_t global_compositor{this}; + wayland::global_seat_t global_seat{this}; + wayland::global_output_t global_output{this}; + wayland::global_shell_t global_shell{this}; + wayland::global_xdg_wm_base_t global_xdg_wm_base{this}; + + lorie_message_queue queue; + + int dpi; +}; + +using namespace wayland; + +__asm__ ( + " .global blob\n" + " .global blob_size\n" + " .section .rodata\n" + " blob:\n" + " .incbin \"en_us.xkbmap\"\n" + " 1:\n" + " blob_size:\n" + " .int 1b - blob" + ); + +extern jbyte blob[]; +extern int blob_size; + +#include +#include + +#define ASHMEM_SET_SIZE _IOW(0x77, 3, size_t) +static inline int +os_create_anonymous_file(size_t size) { + int fd, ret; + long flags; + fd = open("/dev/ashmem", O_RDWR | O_CLOEXEC); + if (fd < 0) + return fd; + ret = ioctl(fd, ASHMEM_SET_SIZE, size); + if (ret < 0) + goto err; + flags = fcntl(fd, F_GETFD); + if (flags == -1) + goto err; + if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) + goto err; + return fd; + err: + close(fd); + return ret; +} + +lorie_compositor::lorie_compositor(int dpi): dpi(dpi) { + // these calls do no overwrite existing values so it is ok. + setenv("WAYLAND_DISPLAY", DEFAULT_SOCKET_NAME, 0); + setenv("XDG_RUNTIME_DIR", DEFAULT_SOCKET_PATH, 0); + + // wl_display_add_socket creates socket with name $WAYLAND_DISPLAY in folder $XDG_RUNTIME_DIR + wl_display_add_socket(*this, nullptr); + + on_client = [=](client_t* client) { + client->on_destroy = [=] {}; + }; + global_compositor.on_bind = [=] (client_t*, compositor_t* compositor) { + compositor->on_create_region = [](region_t* region) { region->on__destroy = [=] { region->destroy(); }; }; + compositor->on_create_surface = [=](surface_t* surface) { + // We need not to send frame callback. That is a sign for Xwayland to send new buffer + surface->on_attach = [=](buffer_t* b, int32_t, int32_t) { + // sending WL_BUFFER_RELEASE + wl_resource_post_event(*b, 0); + }; + surface->on__destroy = [=] { surface->destroy(); }; + }; + }; + global_seat.on_bind = [=](client_t* client, seat_t* seat) { + seat->capabilities (seat_capability::keyboard | seat_capability::pointer); + seat->name("default"); + + seat->on_get_pointer = [](pointer_t* pointer) { pointer->on_release = [=] { pointer->destroy(); }; }; + seat->on_get_keyboard = [=](keyboard_t* kbd) { + kbd->on_release = [=] { kbd->destroy(); }; + int fd = os_create_anonymous_file(blob_size); + if (fd == -1) return; + void *dest = mmap(nullptr, blob_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + memcpy(dest, blob, blob_size); + munmap(dest, blob_size); + + struct stat s = {}; + fstat(fd, &s); + + kbd->keymap(wayland::keyboard_keymap_format::xkb_v1, fd, s.st_size); + close (fd); + }; + seat->on_get_touch = [](touch_t* touch) { touch->on_release = [=] { touch->destroy(); }; }; + }; + global_output.on_bind = [=, this](client_t*, output_t* output) { + report_mode(800, 600); + output->on_release = [=]{ output->destroy(); }; + }; + global_shell.on_bind = [](client_t*, shell_t*) {}; + global_xdg_wm_base.on_bind = [](client_t*, xdg_wm_base_t* wm_base) { + wm_base->on__destroy = [=]() { wm_base->destroy(); }; + }; + + LOGV("Starting compositor"); + wl_display_init_shm (*this); + wl_event_loop_add_fd(wl_display_get_event_loop(*this), queue.fd, + WL_EVENT_READABLE, &lorie_message_queue::run, &queue); + + // We do not really need to terminate it, user can terminate the whole process. + std::thread([=]{ + while (true) { + wl_display_flush_clients(*this); + wl_event_loop_dispatch(wl_display_get_event_loop(*this), 200); + } + }).detach(); +} + +void lorie_compositor::report_mode(int width, int height) { + int mm_width = int(width * 25.4 / dpi); + int mm_height = int(height * 25.4 / dpi); + wl_client* client; + + // We do not store any data of clients so just send it to everybody (but only Xwayland is connected); + wl_client_for_each(client, wl_display_get_client_list(*this)) { + auto data = std::make_tuple(width, height, mm_width, mm_height); + // We do not store pointer to that resource, so iterate + wl_client_for_each_resource(client, [](wl_resource *r, void *d) { + auto [width, height, mm_width, mm_height] = *(reinterpret_cast(d)); + const wl_interface *output_interface = &wayland::detail::output_interface; + if (wl_resource_instance_of(r, output_interface, output_interface)) { + auto output = reinterpret_cast(resource_t::get(r)); + output->geometry(0, 0, mm_width, mm_height, output_subpixel::unknown, "Lorie", "none", output_transform::normal); + output->scale(1.0); + output->mode(output_mode::current | output_mode::preferred, width, height, 60000); + output->done(); + return WL_ITERATOR_STOP; + } + return WL_ITERATOR_CONTINUE; + }, &data); + } +} + +static lorie_compositor* compositor{}; +static int display = 0; + +extern "C" +JNIEXPORT void JNICALL +Java_com_termux_x11_CmdEntryPoint_spawnCompositor([[maybe_unused]] JNIEnv *env, + [[maybe_unused]] jobject thiz, + jint d, jint dpi) { + display = d; + compositor = new lorie_compositor(dpi); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_termux_x11_CmdEntryPoint_outputResize([[maybe_unused]] JNIEnv *env, + [[maybe_unused]] jobject thiz, + jint width, jint height) { + if (compositor) + compositor->report_mode(width, height); +} + +extern "C" JNIEXPORT jboolean JNICALL +Java_com_termux_x11_CmdEntryPoint_socketExists([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jclass clazz) { + // Here we should try to connect socket... + struct sockaddr_un local{ .sun_family=AF_UNIX }; + int len, fd = socket(PF_LOCAL, SOCK_STREAM, 0); + if(fd == -1) { + perror("Unable to create socket"); + return 1; + } + + snprintf(local.sun_path, sizeof(local.sun_path), "%s/%s", + getenv("XDG_RUNTIME_DIR") ?: DEFAULT_SOCKET_PATH, + getenv("WAYLAND_SOCKET") ?: DEFAULT_SOCKET_NAME); + + len = int(strlen(local.sun_path) + sizeof(local.sun_family)); + return connect(fd, (struct sockaddr *)&local, len) != -1; +} + +void exit(JNIEnv* env, int code) { + // app_process writes exception to logcat if a process dies with exit() or abort(), so + jclass systemClass = env->FindClass("java/lang/System"); + jmethodID exitMethod = env->GetStaticMethodID(systemClass, "exit", "(I)V"); + env->CallStaticVoidMethod(systemClass, exitMethod, code); +} + +extern "C" JNIEXPORT void JNICALL +Java_com_termux_x11_CmdEntryPoint_exec(JNIEnv *env, [[maybe_unused]] jclass clazz, jobjectArray jargv) { + // execv's argv array is a bit incompatible with Java's String[], so we do some converting here... + int argc = env->GetArrayLength(jargv) + 2; // Leading executable path and terminating NULL + char *argv[argc]; + memset(argv, 0, sizeof(char*) * argc); + + argv[0] = (char*) "Xwayland"; + argv[argc] = nullptr; // Terminating NULL + for(int i=1; i(env->GetObjectArrayElement(jargv, i - 1)); + const char *pjc = env->GetStringUTFChars(js, JNI_FALSE); + argv[i] = static_cast(calloc(strlen(pjc) + 1, sizeof(char))); //Extra char for the terminating NULL + strcpy((char *) argv[i], pjc); + env->ReleaseStringUTFChars(js, pjc); + } + + // This process should be shut if Xwayland dies... + int fds[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + fcntl(fds[1], F_SETFD, fcntl(fds[1], F_GETFD) | FD_CLOEXEC); + ALooper_addFd(ALooper_prepare(0), fds[1], ALOOPER_POLL_CALLBACK, + ALOOPER_EVENT_INVALID | ALOOPER_EVENT_HANGUP | ALOOPER_EVENT_ERROR, + [](int, int, void* data) { + dprintf(2, "Xwayland died, no need to go on...\n"); + exit(reinterpret_cast(data), 0); + return 0; + }, env); + + switch(fork()) { + case -1: + perror("fork"); + exit(env, 1); + break; + case 0: //child + execvp(argv[0], argv); + perror("execvp"); + default: //parent + close(fds[0]); + break; + } +} + +extern "C" +JNIEXPORT jint JNICALL +Java_com_termux_x11_CmdEntryPoint_connect([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jclass clazz) { + struct sockaddr_un local { .sun_family=AF_UNIX, .sun_path="/data/data/com.termux/files/usr/tmp/.X11-unix/X0" }; + int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (sockfd == -1) { + perror("socket"); + return -1; + } + + snprintf(local.sun_path, sizeof(local.sun_path), "%s/.x11-unix/X%d", + getenv("TMPDIR") ?: DEFAULT_SOCKET_PATH, display); + + connect(sockfd, reinterpret_cast(&local), sizeof(local)); // NOLINT(bugprone-unused-return-value) + return sockfd; +} \ No newline at end of file diff --git a/app/src/main/jni/lorie/en_us.xkbmap b/app/src/main/cpp/lorie/en_us.xkbmap similarity index 100% rename from app/src/main/jni/lorie/en_us.xkbmap rename to app/src/main/cpp/lorie/en_us.xkbmap diff --git a/app/src/main/cpp/lorie/lorie_wayland_server.cpp b/app/src/main/cpp/lorie/lorie_wayland_server.cpp new file mode 100644 index 000000000..2a4856c8c --- /dev/null +++ b/app/src/main/cpp/lorie/lorie_wayland_server.cpp @@ -0,0 +1,100 @@ +#include +#include +#include +#include +#include + +#pragma clang diagnostic ignored "-Wshadow" +#pragma ide diagnostic ignored "cppcoreguidelines-pro-type-static-cast-downcast" + +using namespace wayland; + + +/* display_t methods */ +static void display_destroyed(wl_listener* l, void* data) { + auto d = static_cast(l); + if (d->on_destroy) + d->on_destroy(); +} + +static void client_created(wl_listener* l, void* data) { + if (!data) return; + auto c = static_cast(data); + auto d = reinterpret_cast(wl_display_get_destroy_listener(wl_client_get_display(c), &display_destroyed)); + auto new_client = new client_t(c); + if (d->on_client) + d->on_client(new_client); +} + +display_t::display_t(): +display(wl_display_create()), +wl_listener{{}, &display_destroyed}, +client_created_listener{{}, &client_created}{ + wl_display_add_destroy_listener(display, this); + wl_display_add_client_created_listener(display, &client_created_listener); +} + +display_t::~display_t() { + wl_display_destroy_clients(display); + wl_display_destroy(display); +} + +/* client_t methods */ +wl_listener client_resource_created {{}, [](wl_listener*, void* d) { + if (d == nullptr || *(static_cast(d)) != &wl_buffer_interface) + return; + new buffer_t(static_cast(d)); +}}; + +static void client_destroy_callback(struct wl_listener *listener, void *) { + if (listener == nullptr) return; + auto c = static_cast(listener); + if (c->on_destroy) + c->on_destroy(); + delete c; +}; + +client_t::client_t(wl_client* client): wl_listener{{}, &client_destroy_callback}, client(client) { + wl_client_add_destroy_listener(*this, this); + wl_client_add_resource_created_listener(*this, &client_resource_created); +} + +client_t* client_t::get(wl_client* client) { + return client ? static_cast(wl_client_get_destroy_listener(client, &client_destroy_callback)) : nullptr; +} + +void client_t::destroy() { + wl_client_destroy(client); +} + +/* resource_t methods */ +void resource_t::resource_destroyed(wl_listener* that, void*) { + auto r = static_cast(that); + if (r->on_destroy) + r->on_destroy(); + delete r; +} + +resource_t::resource_t(client_t* client, uint32_t id, uint32_t version, +wl_interface* iface, wl_dispatcher_func_t dispatcher): +wl_listener{{}, &resource_destroyed}, +m_client(client), display(wl_client_get_display(*client)), +resource(wl_resource_create(*client, iface, version, id)), +version(version) { + wl_resource_add_destroy_listener(resource, this); + wl_resource_set_dispatcher(resource, dispatcher, iface, nullptr, nullptr); +} + +static inline client_t* client_get(wl_resource* c) { + wl_client* client; + if (c == nullptr || (client = wl_resource_get_client(c)) == nullptr) + return nullptr; + return client_t::get(client); +} + +resource_t::resource_t(wl_resource *r): +wl_listener{{}, &resource_destroyed}, +m_client(client_get(r)), display(wl_client_get_display(*m_client)), +resource(r), version(wl_resource_get_version(r)) { + wl_resource_add_destroy_listener(resource, this); +} \ No newline at end of file diff --git a/app/src/main/jni/lorie/lorie_wayland_server.hpp b/app/src/main/cpp/lorie/lorie_wayland_server.hpp similarity index 68% rename from app/src/main/jni/lorie/lorie_wayland_server.hpp rename to app/src/main/cpp/lorie/lorie_wayland_server.hpp index c41a22735..2dd97e9e2 100644 --- a/app/src/main/jni/lorie/lorie_wayland_server.hpp +++ b/app/src/main/cpp/lorie/lorie_wayland_server.hpp @@ -28,26 +28,6 @@ namespace wayland { } ~display_t(); - - void add_fd_listener(int fd, uint32_t mask, std::function listener); - - void add_socket_fd(int fd); - - inline void add_socket_auto() { - wl_display_add_socket_auto(display); - } - - inline void adopt_client(int fd) { - auto r = wl_client_create(display, fd); - } - - inline void init_shm() { - wl_display_init_shm(display); - } - - inline void terminate() { - running = false; - } inline void run() { while (running) { @@ -55,19 +35,9 @@ namespace wayland { wl_event_loop_dispatch(wl_display_get_event_loop(display), 200); } } - - inline void set_log_handler(wl_log_func_t f) { - wl_log_set_handler_server(f); - } - - inline uint32_t next_serial() { - return wl_display_next_serial(*this); - }; }; class client_t: public wl_listener { - protected: - std::any userdata; public: std::function on_destroy = nullptr; explicit client_t(wl_client* client); @@ -78,12 +48,6 @@ namespace wayland { static client_t* get(wl_client*); - std::any& user_data() { - return userdata; - } - - void post_implementation_error(const std::string& string); - void destroy(); inline bool operator==(const client_t& other) { @@ -143,10 +107,6 @@ namespace wayland { static inline resource_t* get(void* r) { return get(static_cast(r)); }; - - std::any& user_data() { - return userdata; - } inline int id() { return wl_resource_get_id(*this); @@ -159,8 +119,6 @@ namespace wayland { inline void post_error(uint32_t code, const std::string& msg) const { wl_resource_post_error(resource, code, "%s", msg.c_str()); }; - - static uint32_t timestamp(); virtual ~resource_t() = default; }; @@ -190,41 +148,6 @@ namespace wayland { operator wl_buffer *() const { return reinterpret_cast(const_cast(this)); }; - - inline void release() { - wl_resource_post_event(resource, 0); - } - - [[nodiscard]] - inline bool is_shm() { - return wl_shm_buffer_get(*this) != nullptr; - } - - [[nodiscard]] - inline void *shm_data() const { - return wl_shm_buffer_get_data( - wl_shm_buffer_get(*this)); - } - - [[nodiscard]] - inline int32_t shm_stride() const { - return wl_shm_buffer_get_stride(wl_shm_buffer_get(*this)); - } - - [[nodiscard]] - inline uint32_t shm_format() const { - return wl_shm_buffer_get_format(wl_shm_buffer_get(*this)); - } - - [[nodiscard]] - inline int32_t shm_width() const { - return wl_shm_buffer_get_width(wl_shm_buffer_get(*this)); - } - - [[nodiscard]] - inline int32_t shm_height() const { - return wl_shm_buffer_get_height(wl_shm_buffer_get(*this)); - } }; } diff --git a/app/src/main/cpp/wayland b/app/src/main/cpp/wayland new file mode 160000 index 000000000..1ef773be7 --- /dev/null +++ b/app/src/main/cpp/wayland @@ -0,0 +1 @@ +Subproject commit 1ef773be76ec0f45312622da7720ae6917bdf789 diff --git a/app/src/main/jni/wayland.patch b/app/src/main/cpp/wayland.patch similarity index 100% rename from app/src/main/jni/wayland.patch rename to app/src/main/cpp/wayland.patch diff --git a/app/src/main/jni/wayland2.patch b/app/src/main/cpp/wayland2.patch similarity index 100% rename from app/src/main/jni/wayland2.patch rename to app/src/main/cpp/wayland2.patch diff --git a/app/src/main/cpp/xcbproto b/app/src/main/cpp/xcbproto new file mode 160000 index 000000000..842d91316 --- /dev/null +++ b/app/src/main/cpp/xcbproto @@ -0,0 +1 @@ +Subproject commit 842d91316243eb1f2e208231acc1512c2cf43a1f diff --git a/app/src/main/cpp/xorgproto b/app/src/main/cpp/xorgproto new file mode 160000 index 000000000..1b6e63b2c --- /dev/null +++ b/app/src/main/cpp/xorgproto @@ -0,0 +1 @@ +Subproject commit 1b6e63b2c38a0cf6cd1c70bfc5483cac0d1710e1 diff --git a/app/src/main/java/com/termux/x11/CmdEntryPoint.java b/app/src/main/java/com/termux/x11/CmdEntryPoint.java index 1a63aff60..5f78c955d 100644 --- a/app/src/main/java/com/termux/x11/CmdEntryPoint.java +++ b/app/src/main/java/com/termux/x11/CmdEntryPoint.java @@ -1,236 +1,163 @@ package com.termux.x11; import android.annotation.SuppressLint; -import android.app.AppOpsManager; -import android.content.ComponentName; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; -import android.os.IBinder; import android.os.Looper; -import android.os.Parcel; import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import java.io.IOException; -import java.io.PrintStream; -import java.util.Objects; +import java.io.DataInputStream; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Arrays; +import java.util.List; -import com.termux.x11.starter.ActivityManager; -import com.termux.x11.starter.Compat; - -@SuppressLint("UnsafeDynamicallyLoadedCode") -@SuppressWarnings({"unused", "RedundantThrows", "SameParameterValue", "FieldCanBeLocal"}) public class CmdEntryPoint { - @SuppressLint("SdCardPath") - private final String XwaylandPath = "/data/data/com.termux/files/usr/bin/Xwayland"; - private final ComponentName TermuxX11Component = - ComponentName.unflattenFromString("com.termux.x11/.TermuxX11StarterReceiver"); - private String[] args; - private Service svc; - private ParcelFileDescriptor logFD; + public static final String ACTION_START = "com.termux.x11.CmdEntryPoint.ACTION_START"; + public static final int PORT = 7892; + public static final byte[] MAGIC = "0xDEADBEEF".getBytes(); + + // this constant is used in Xwayland itself + // https://github.com/freedesktop/xorg-xserver/blob/ccdd431cd8f1cabae9d744f0514b6533c438908c/hw/xwayland/xwayland-screen.c#L66 + public static final int DEFAULT_DPI = 96; + + @SuppressLint("StaticFieldLeak") + private static final Context ctx = android.app.ActivityThread.systemMain().getSystemContext(); + private static final Handler handler = new Handler(); /** * Command-line entry point. + * [1] + * Application creates socket with name $WAYLAND_DISPLAY in folder $XDG_RUNTIME_DIR + * If these environment variables are unset they are specified as + * WAYLAND_DISPLAY="termux-x11" + * XDG_RUNTIME_DIR="/data/data/com.termux/files/usr/tmp" + * Any commandline arguments passed to this program are passed to Xwayland. + * The only exception is `--no-xwayland-start`, read [4]. + * + * [2] + * Please, do not set WAYLAND_DISPLAY to "wayland-0", lots of programs (primarily GTK-based) + * are trying to connect the compositor and crashing. Compositor in termux-x11 is a fake and a stub. + * It is only designed to handle Xwayland. + * + * [3] + * Also you should set TMPDIR environment variable in the case if you are running Xwayland in + * non-termux environment. That is the only way to localize X connection socket. + * TMPDIR should be accessible by termux-x11 + * + * [4] + * You can run `termux-x11 --no-xwayland-start` to spawn compositor, but not to spawn Termux's + * Xwayland in the case you want to use Xwayland contained in chroot environment. + * Do not forget about [2] to avoid application crashes. + * + * [5] + * You can specify DPI by using `-dpi` option. + * It is Xwayland option, but termux-x11 also handles it. * * @param args The command-line arguments */ public static void main(String[] args) { - try { - (new Thread(() -> { - try { - (new CmdEntryPoint()).onRun(args); - } catch (Throwable e) { - e.printStackTrace(); - } - })).start(); - - synchronized (Thread.currentThread()) { - Looper.loop(); - } - } catch (Throwable e) { - e.printStackTrace(); - } + handler.post(() -> new CmdEntryPoint(args)); + Looper.loop(); } - Runnable failedToStartActivity = () -> { - System.err.println("Android reported activity started but we did not get any respond"); - System.err.println("Looks like we failed to start activity."); - exit(1); - }; - - public void onRun(String[] args) throws Throwable { - CmdEntryPoint.this.args = args; - checkXdgRuntimeDir(); - prepareLogFD(); - if (!Compat.havePermission(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW)) { - System.err.println("Looks like " + Compat.callingPackage + - " lacks \"Draw Over Apps\" permission."); - System.err.println("You can grant " + Compat.callingPackage + - " the \"Draw Over Apps\" permission from its App Info activity:"); - System.err.println("\tAndroid Settings -> Apps -> Termux -> Advanced -> Draw over other apps."); - } + @SuppressWarnings("FieldCanBeLocal") + private int dpi = DEFAULT_DPI; + @SuppressWarnings("FieldCanBeLocal") + private int display = 0; - if (checkWaylandSocket()) { - System.err.println("termux-x11 is already running"); - startXwayland(); + CmdEntryPoint(String[] args) { + if (socketExists()) { + System.err.println("termux-x11 is already running. You can kill it with command `pkill -9 Xwayland`"); + handler.post(() -> System.exit(0)); } else { - svc = new Service(); - handler.postDelayed(failedToStartActivity, 2000); - boolean launched = startActivity(svc); - } - } - - public void onTransact(int code, Parcel data, Parcel reply, int flags) { - handler.removeCallbacks(failedToStartActivity); - } - - private void prepareLogFD() { - logFD = ParcelFileDescriptor.adoptFd(openLogFD()); - } - - private ParcelFileDescriptor getWaylandFD() throws Throwable { - try { - ParcelFileDescriptor pfd; - int fd = createWaylandSocket(); - if (fd == -1) - throw new Exception("Failed to bind a socket"); - - pfd = ParcelFileDescriptor.adoptFd(fd); - - System.err.println(pfd); - return pfd; - } finally { - System.err.println("Lorie requested fd"); - } - } + List a = Arrays.asList(args); + try { + int dpiIndex = a.lastIndexOf("-dpi"); + if (dpiIndex != -1 && a.size() >= dpiIndex) + dpi = Integer.parseInt(a.get(dpiIndex + 1)); + } catch (NumberFormatException ignored) {} // No need to handle it, anyway user will see Xwayland error - private void startXwayland() { - try { - boolean started = false; - for (int i = 0; i < 200; i++) { - if (checkWaylandSocket()) { - started = true; - Thread.sleep(200); - startXwayland(args); - break; - } - Thread.sleep(100); + try { + for (String arg: a) + if (arg.startsWith(":")) + display = Integer.parseInt(arg.substring(1)); + } catch (NumberFormatException ignored) {} // No need to handle it, anyway user will see Xwayland error + + spawnCompositor(display, dpi); + spawnListeningThread(); + sendBroadcast(); + System.err.println("Starting Xwayland"); + + if (!a.contains("--no-xwayland-start")) { + exec(args); } - if (!started) - System.err.println("Failed to connect to Termux:X11. Something went wrong."); - } catch (Exception e) { - e.printStackTrace(); } - exit(0); - } - - private void onFinish() { - System.err.println("App sent finishing command"); - startXwayland(); } - private boolean startActivity(IBinder token) throws Throwable { + void sendBroadcast() { Bundle bundle = new Bundle(); - bundle.putBinder("", token); - - Intent intent = new Intent(); - intent.putExtra("com.termux.x11.starter", bundle); - intent.setComponent(TermuxX11Component); - - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP| - Intent.FLAG_ACTIVITY_SINGLE_TOP); - - int res = Compat.startActivity(intent); - - PrintStream out = System.err; - boolean launched = false; - out.println("res = " + res); - switch (res) { - case ActivityManager.START_SUCCESS: - case ActivityManager.START_SWITCHES_CANCELED: - case ActivityManager.START_DELIVERED_TO_TOP: - case ActivityManager.START_TASK_TO_FRONT: - launched = true; - break; - case ActivityManager.START_INTENT_NOT_RESOLVED: - out.println( - "Error: Activity not started, unable to " - + "resolve " + intent); - break; - case ActivityManager.START_CLASS_NOT_FOUND: - out.println("Error: Activity class " + - Objects.requireNonNull(intent.getComponent()).toShortString() - + " does not exist."); - break; - case ActivityManager.START_PERMISSION_DENIED: - out.println( - "Error: Activity not started, you do not " - + "have permission to access it."); - break; - default: - out.println( - "Error: Activity not started, unknown error code " + res); - break; - } - - System.err.println("Activity is" + (launched?"":" not") + " started"); - - return launched; - } - - private void startXwayland(String[] args) throws IOException { - System.err.println("Starting Xwayland"); - exec(XwaylandPath, args); - } - - class Service extends ITermuxX11Internal.Stub { - @Override - public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws RemoteException { - CmdEntryPoint.this.onTransact(code, data, reply, flags); - return super.onTransact(code, data, reply, flags); - } - - @Override - public ParcelFileDescriptor getWaylandFD() throws RemoteException { - System.err.println("Got getWaylandFD"); - try { - return CmdEntryPoint.this.getWaylandFD(); - } catch (Throwable e) { - throw new RemoteException(e.getMessage()); + // We should not care about multiple instances, it should be called only by `Termux:X11` app + // which is single instance... + bundle.putBinder("", new ICmdEntryInterface.Stub() { + @Override + public void outputResize(int width, int height) { + CmdEntryPoint.this.outputResize(width, height); } - } - @Override - public ParcelFileDescriptor getLogFD() throws RemoteException { - System.err.println("Got getLogFD"); - System.err.println(logFD); - return logFD; - } + @Override + public ParcelFileDescriptor getXConnection() { + return ParcelFileDescriptor.adoptFd(connect()); + } + }); - @Override - public void finish() throws RemoteException { - System.err.println("Got finish request"); - handler.postDelayed(CmdEntryPoint.this::onFinish, 10); - } + Intent intent = new Intent(ACTION_START); + intent.putExtra("com.termux.x11.starter", bundle); + intent.setPackage("com.termux.x11"); + ctx.sendBroadcast(intent); } - private void exit(int status) { - exit(50, status); - } - private void exit(int delay, int status) { - handler.postDelayed(() -> System.exit(status), delay); + void spawnListeningThread() { + new Thread(() -> { + /* + The purpose of this function is simple. If the application has not been launched + before running termux-x11, the initial sendBroadcast had no effect because no one + received the intent. To allow the application to reconnect freely, we will listen on + port `PORT` and when receiving a magic phrase, we will send another intent. + */ + try (ServerSocket listeningSocket = + new ServerSocket(PORT, 0, InetAddress.getByName("127.0.0.1"))) { + listeningSocket.setReuseAddress(true); + while(true) { + try (Socket client = listeningSocket.accept()) { + // We should ensure that it is some + byte[] b = new byte[MAGIC.length]; + DataInputStream reader = new DataInputStream(client.getInputStream()); + reader.readFully(b); + if (Arrays.equals(MAGIC, b)) { + System.err.println("New client connection!"); + sendBroadcast(); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + }).start(); } - private native void checkXdgRuntimeDir(); - private native int createWaylandSocket(); - private native boolean checkWaylandSocket(); - private native int openLogFD(); - private static native void exec(String path, String[] argv); - @SuppressWarnings("FieldMayBeFinal") - private static Handler handler = new Handler(); + private native void spawnCompositor(int display, int dpi); + private native void outputResize(int width, int height); + private static native boolean socketExists(); + private static native void exec(String[] argv); + private static native int connect(); static { - System.loadLibrary("x11-starter"); + System.loadLibrary("lorie"); } } diff --git a/app/src/main/java/com/termux/x11/LoriePreferences.java b/app/src/main/java/com/termux/x11/LoriePreferences.java index 76e396d74..cdc677d15 100644 --- a/app/src/main/java/com/termux/x11/LoriePreferences.java +++ b/app/src/main/java/com/termux/x11/LoriePreferences.java @@ -1,7 +1,6 @@ package com.termux.x11; import android.content.SharedPreferences; -import android.inputmethodservice.Keyboard; import android.os.Bundle; import android.preference.Preference; import android.preference.PreferenceFragment; @@ -13,7 +12,7 @@ import android.util.Log; import android.view.MenuItem; -public class LoriePreferences extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener { +public class LoriePreferences extends AppCompatActivity { static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard"; LoriePreferenceFragment loriePreferenceFragment; @@ -33,21 +32,6 @@ protected void onCreate(Bundle savedInstanceState) { } } - @Override - public void onResume() { - super.onResume(); - if (loriePreferenceFragment != null) - loriePreferenceFragment.getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this); - - } - - @Override - public void onPause() { - if (loriePreferenceFragment != null) - loriePreferenceFragment.getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); - super.onPause(); - } - @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); @@ -60,11 +44,7 @@ public boolean onOptionsItemSelected(MenuItem item) { return super.onOptionsItemSelected(item); } - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - LorieService.start(LorieService.ACTION_PREFERENCES_CHANGED); - } - + @SuppressWarnings("deprecation") public static class LoriePreferenceFragment extends PreferenceFragment implements PreferenceScreen.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { @Override diff --git a/app/src/main/java/com/termux/x11/LorieService.java b/app/src/main/java/com/termux/x11/LorieService.java deleted file mode 100644 index 01f94a994..000000000 --- a/app/src/main/java/com/termux/x11/LorieService.java +++ /dev/null @@ -1,495 +0,0 @@ -package com.termux.x11; - -import android.annotation.SuppressLint; -import android.app.ActivityManager; -import android.app.AlertDialog; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.graphics.PixelFormat; -import android.net.Uri; -import android.os.Build; -import android.os.Handler; -import android.os.IBinder; -import android.os.PowerManager; -import android.preference.PreferenceManager; -import android.provider.Settings; - -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; -import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; -import androidx.appcompat.app.AppCompatActivity; -import androidx.core.app.NotificationCompat; - -import android.util.Log; -import android.view.InputDevice; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.View; -import android.view.WindowInsets; -import android.widget.FrameLayout; - -import com.termux.x11.utils.KeyboardUtils; -import com.termux.x11.utils.SamsungDexUtils; - - -@SuppressWarnings({"ConstantConditions", "SameParameterValue", "SdCardPath"}) -@SuppressLint({"ClickableViewAccessibility", "StaticFieldLeak"}) -public class LorieService extends Service implements View.OnApplyWindowInsetsListener { - static final String LAUNCHED_BY_COMPATION = "com.termux.x11.launched_by_companion"; - static final String ACTION_STOP_SERVICE = "com.termux.x11.service_stop"; - static final String ACTION_START_FROM_ACTIVITY = "com.termux.x11.start_from_activity"; - static final String ACTION_START_PREFERENCES_ACTIVITY = "com.termux.x11.start_preferences_activity"; - static final String ACTION_PREFERENCES_CHANGED = "com.termux.x11.preferences_changed"; - - private static LorieService instance = null; - long compositor; - private static final ServiceEventListener listener = new ServiceEventListener(); - private static MainActivity act; - - private TouchParser mTP; - - public LorieService(){ - instance = this; - } - - static void setMainActivity(MainActivity activity) { - act = activity; - } - - static void start(String action) { - Intent intent = new Intent(act, LorieService.class); - intent.setAction(action); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - act.startForegroundService(intent); - } else { - act.startService(intent); - } - } - - @SuppressLint({"BatteryLife", "ObsoleteSdkInt"}) - @Override - public void onCreate() { - - if (isServiceRunningInForeground(this, LorieService.class)) return; - - compositor = createLorieThread(); - if (compositor == 0) { - Log.e("LorieService", "compositor thread was not created"); - return; - } - - instance = this; - Log.e("LorieService", "created"); - - Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class); - notificationIntent.putExtra("foo_bar_extra_key", "foo_bar_extra_value"); - notificationIntent.setAction(Long.toString(System.currentTimeMillis())); - - Intent exitIntent = new Intent(getApplicationContext(), LorieService.class); - exitIntent.setAction(ACTION_STOP_SERVICE); - - Intent preferencesIntent = new Intent(getApplicationContext(), LoriePreferences.class); - preferencesIntent.setAction(ACTION_START_PREFERENCES_ACTIVITY); - - PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - PendingIntent pendingExitIntent = PendingIntent.getService(getApplicationContext(), 0, exitIntent, PendingIntent.FLAG_IMMUTABLE); - PendingIntent pendingPreferencesIntent = PendingIntent.getActivity(getApplicationContext(), 0, preferencesIntent, PendingIntent.FLAG_IMMUTABLE); - - //For creating the Foreground Service - int priority = Notification.PRIORITY_HIGH; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) - priority = NotificationManager.IMPORTANCE_HIGH; - NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - String channelId = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? getNotificationChannel(notificationManager) : ""; - Notification notification = new NotificationCompat.Builder(this, channelId) - .setContentTitle("Termux:X11") - .setSmallIcon(R.drawable.ic_x11_icon) - .setContentText("Pull down to show options") - .setContentIntent(pendingIntent) - .setOngoing(true) - .setPriority(priority) - .setShowWhen(false) - .setColor(0xFF607D8B) - .addAction(0, "Preferences", pendingPreferencesIntent) - .addAction(0, "Exit", pendingExitIntent) - .build(); - startForeground(1, notification); - - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - String packageName = getPackageName(); - PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - if (!pm.isIgnoringBatteryOptimizations(packageName)) { - Intent whitelist = new Intent(); - whitelist.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); - whitelist.setData(Uri.parse("package:" + packageName)); - whitelist.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(whitelist); - } - } - } - - @RequiresApi(Build.VERSION_CODES.O) - private String getNotificationChannel(NotificationManager notificationManager){ - String channelId = getResources().getString(R.string.app_name); - String channelName = getResources().getString(R.string.app_name); - NotificationChannel channel = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH); - channel.setImportance(NotificationManager.IMPORTANCE_NONE); - channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); - notificationManager.createNotificationChannel(channel); - return channelId; - } - - public static boolean isServiceRunningInForeground(Context context, Class serviceClass) { - ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { - if (serviceClass.getName().equals(service.service.getClassName())) { - return service.foreground; - } - } - return false; - } - - private void onPreferencesChanged() { - if (instance == null || act == null) return; - - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(instance); - - int mode = Integer.parseInt(preferences.getString("touchMode", "1")); - instance.mTP.setMode(mode); - - act.getWindow().getDecorView().setOnApplyWindowInsetsListener(act); - - if (preferences.getBoolean("showAdditionalKbd", true)) - act.kbd.setVisibility(View.VISIBLE); - else - act.kbd.setVisibility(View.INVISIBLE); - - if (preferences.getBoolean("dexMetaKeyCapture", false)) { - SamsungDexUtils.dexMetaKeyCapture(act, false); - } - - Log.e("LorieService", "Preferences changed"); - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - Log.e("LorieService", "start"); - String action = intent.getAction(); - - if (action.equals(ACTION_START_FROM_ACTIVITY)) { - act.onLorieServiceStart(this); - } - - if (action.equals(ACTION_STOP_SERVICE)) { - Log.e("LorieService", action); - terminate(); - act.finish(); - stopSelf(); - } - - onPreferencesChanged(); - - return START_REDELIVER_INTENT; - } - - private static void sendRunCommandInternal(Context ctx) { - Intent intent = new Intent(); - intent.setClassName("com.termux", "com.termux.app.RunCommandService"); - intent.setAction("com.termux.RUN_COMMAND"); - intent.putExtra("com.termux.RUN_COMMAND_PATH", - "/data/data/com.termux/files/usr/libexec/termux-x11/termux-startx11"); - intent.putExtra("com.termux.RUN_COMMAND_BACKGROUND", true); - Log.d("LorieService", "sendRunCommand: " + intent); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - ctx.startForegroundService(intent); - } else { - ctx.startService(intent); - } - } - - public static void sendRunCommand(AppCompatActivity act) { - final String ERROR_MESSAGE = - "It is impossible to start without " + - "com.termux.permission.RUN_COMMAND permission. " + - "Sorry."; - if (act.checkSelfPermission("com.termux.permission.RUN_COMMAND") == PackageManager.PERMISSION_GRANTED) { - LorieService.sendRunCommandInternal(act); - } else { - Log.d("MainActivity", "We have no permission to sendRunCommand(). Requesting it."); - ActivityResultLauncher requestPermissionLauncher = - act.registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { - if (isGranted) { - sendRunCommandInternal(act); - } else { - new AlertDialog.Builder(act) - .setTitle("Insufficient permission") - .setMessage(ERROR_MESSAGE) - .setPositiveButton(android.R.string.yes, - (dialog, which) -> act.finish()) - .setIcon(android.R.drawable.ic_dialog_alert) - .show(); - - } - }); - requestPermissionLauncher.launch("com.termux.permission.RUN_COMMAND"); - } - } - - - @Override - public void onDestroy() { - Log.e("LorieService", "destroyed"); - } - - static LorieService getInstance() { - if (instance == null) { - Log.e("LorieService", "Instance was requested, but no instances available"); - } - return instance; - } - - void setListeners(@NonNull SurfaceView view, @NonNull SurfaceView cursor) { - Context a = view.getRootView().findViewById(android.R.id.content).getContext(); - if (!(a instanceof MainActivity)) { - Log.e("LorieService", "Context is not an activity!!!"); - } - - act = (MainActivity) a; - - view.setFocusable(true); - view.setFocusableInTouchMode(true); - view.requestFocus(); - - listener.svc = this; - listener.setAsListenerTo(view, cursor); - - mTP = new TouchParser(view, listener); - onPreferencesChanged(); - } - - static View.OnKeyListener getOnKeyListener() { - return listener; - } - - @Override - public IBinder onBind(Intent intent) { - return null; - } - - @SuppressWarnings("SameParameterValue") - private static class ServiceEventListener implements SurfaceHolder.Callback, View.OnTouchListener, View.OnKeyListener, View.OnHoverListener, View.OnGenericMotionListener, TouchParser.OnTouchParseListener { - LorieService svc; - - @SuppressLint("WrongConstant") - private void setAsListenerTo(SurfaceView view, SurfaceView cursor) { - view.getHolder().addCallback(this); - view.setOnTouchListener(this); - view.setOnHoverListener(this); - view.setOnGenericMotionListener(this); - view.setOnKeyListener(this); - surfaceChanged(view.getHolder(), PixelFormat.UNKNOWN, view.getWidth(), view.getHeight()); - - cursor.getHolder().addCallback(new SurfaceHolder.Callback() { - @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { - cursor.getHolder().setFormat(PixelFormat.TRANSLUCENT); - } - @Override - public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) { - cursor.getHolder().setFormat(PixelFormat.TRANSLUCENT); - svc.cursorChanged(holder.getSurface()); - } - @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) { - svc.cursorChanged(null); - } - }); - } - - public void onPointerButton(int button, int state) { - if (svc == null) return; - svc.pointerButton(button, state); - } - - public void onPointerMotion(int x, int y) { - if (svc == null) return; - svc.pointerMotion(x, y); - } - - public void onPointerScroll(int axis, float value) { - if (svc == null) return; - svc.pointerScroll(axis, value); - } - - @Override - public boolean onTouch(View v, MotionEvent e) { - if (svc == null) return false; - return svc.mTP.onTouchEvent(e); - } - - @Override - public boolean onGenericMotion(View v, MotionEvent e) { - if (svc == null) return false; - return svc.mTP.onTouchEvent(e); - } - - @Override - public boolean onHover(View v, MotionEvent e) { - if (svc == null) return false; - return svc.mTP.onTouchEvent(e); - } - - private boolean isSource(KeyEvent e, int source) { - return (e.getSource() & source) == source; - } - - private boolean rightPressed = false; // Prevent right button press event from being repeated - private boolean middlePressed = false; // Prevent middle button press event from being repeated - @Override - public boolean onKey(View v, int keyCode, KeyEvent e) { - if (svc == null) return false; - int action = 0; - - if (keyCode == KeyEvent.KEYCODE_BACK) { - if ( - isSource(e, InputDevice.SOURCE_MOUSE) && - rightPressed != (e.getAction() == KeyEvent.ACTION_DOWN) - ) { - svc.pointerButton(TouchParser.BTN_RIGHT, (e.getAction() == KeyEvent.ACTION_DOWN) ? TouchParser.ACTION_DOWN : TouchParser.ACTION_UP); - rightPressed = (e.getAction() == KeyEvent.ACTION_DOWN); - } else if (e.getAction() == KeyEvent.ACTION_UP) { - KeyboardUtils.toggleKeyboardVisibility(act); - if (act.kbd!=null) - act.kbd.requestFocus(); - - } - return true; - } - - if ( - keyCode == KeyEvent.KEYCODE_MENU && - isSource(e, InputDevice.SOURCE_MOUSE) && - middlePressed != (e.getAction() == KeyEvent.ACTION_DOWN) - ) { - svc.pointerButton(TouchParser.BTN_MIDDLE, (e.getAction() == KeyEvent.ACTION_DOWN) ? TouchParser.ACTION_DOWN : TouchParser.ACTION_UP); - middlePressed = (e.getAction() == KeyEvent.ACTION_DOWN); - return true; - } - - if (e.getAction() == KeyEvent.ACTION_DOWN) action = TouchParser.ACTION_DOWN; - if (e.getAction() == KeyEvent.ACTION_UP) action = TouchParser.ACTION_UP; - svc.keyboardKey(action, keyCode, e.isShiftPressed() ? 1 : 0, e.getCharacters()); - return true; - } - - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - svc.windowChanged(holder.getSurface(), width, height); - } - - @Override public void surfaceCreated(SurfaceHolder holder) {} - @SuppressLint("WrongConstant") - @Override public void surfaceDestroyed(SurfaceHolder holder) { - surfaceChanged(holder, PixelFormat.UNKNOWN, 0, 0); - } - - } - - static void sleep(long millis) { - try { - Thread.sleep(millis); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - static final Handler handler = new Handler(); - private abstract static class Task implements Runnable { - public Task() { - handler.post(this); - } - @Override - public void run() { - LorieService svc = getInstance(); - if (svc == null || svc.compositor == 0 || act == null) { - handler.postDelayed(this, 100); - return; - } - - run(svc); - } - public abstract void run(LorieService svc); - } - - public static void adoptWaylandFd(int fd) { - new Task() { - @Override - public void run(LorieService svc) { - svc.passWaylandFD(fd); - } - }; - } - - @SuppressLint("WrongConstant") - @Override - public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { - SurfaceView c = v.getRootView().findViewById(R.id.lorieView); - SurfaceHolder h = (c != null) ? c.getHolder() : null; - if (h != null) - handler.postDelayed(() -> listener.surfaceChanged(h, PixelFormat.UNKNOWN, 0, 0), 100); - return insets; - } - - @SuppressWarnings("unused") - // It is used in native code - void setRendererVisibility(boolean visible) { - act.runOnUiThread(()-> { - act.findViewById(R.id.stub).setVisibility(visible?View.INVISIBLE:View.VISIBLE); - act.findViewById(R.id.lorieView).setVisibility(visible?View.VISIBLE:View.INVISIBLE); - }); - } - - @SuppressWarnings("unused") - // It is used in native code - void setCursorVisibility(boolean visible) { - act.runOnUiThread(()-> act.findViewById(R.id.cursorView).setVisibility(visible?View.VISIBLE:View.INVISIBLE)); - } - - @SuppressWarnings("unused") - // It is used in native code - void setCursorRect(int x, int y, int w, int h) { - act.runOnUiThread(()-> { - SurfaceView v = act.findViewById(R.id.cursorView); - FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(w, h); - params.setMargins(x, y, 0, 0); - - v.setLayoutParams(params); - v.setVisibility(View.VISIBLE); - }); - } - - private native void cursorChanged(Surface surface); - private native void windowChanged(Surface surface, int width, int height); - private native void pointerMotion(int x, int y); - private native void pointerScroll(int axis, float value); - private native void pointerButton(int button, int type); - private native void keyboardKey(int key, int type, int shift, String characters); - private native void passWaylandFD(int fd); - private native long createLorieThread(); - private native void terminate(); - public static native void startLogcatForFd(int fd); - - static { - System.loadLibrary("lorie"); - } -} diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 48098b7c6..9fb592252 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -1,17 +1,29 @@ package com.termux.x11; +import static com.termux.x11.CmdEntryPoint.ACTION_START; + +import android.annotation.SuppressLint; import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.res.Configuration; +import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Build; import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; import android.preference.PreferenceManager; -import android.util.Log; import android.view.Gravity; +import android.view.InputDevice; import android.view.KeyEvent; import android.view.PointerIcon; +import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; @@ -24,18 +36,13 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; +import com.termux.x11.utils.KeyboardUtils; import com.termux.x11.utils.PermissionUtils; import com.termux.x11.utils.SamsungDexUtils; -import static com.termux.x11.LorieService.ACTION_START_PREFERENCES_ACTIVITY; -import static com.termux.x11.LorieService.handler; - -import java.util.Objects; - -public class MainActivity extends AppCompatActivity implements View.OnApplyWindowInsetsListener { +public class MainActivity extends AppCompatActivity implements View.OnApplyWindowInsetsListener, TouchParser.OnTouchParseListener { static final String REQUEST_LAUNCH_EXTERNAL_DISPLAY = "request_launch_external_display"; private static final int[] keys = { @@ -51,8 +58,11 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo AdditionalKeyboardView kbd; FrameLayout frm; + private TouchParser mTP; + private final ServiceEventListener listener = new ServiceEventListener(); + private ICmdEntryInterface service = null; - @Override + @SuppressLint({"AppCompatMethod", "ObsoleteSdkInt"}) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -61,8 +71,9 @@ protected void onCreate(Bundle savedInstanceState) { setFullScreenForExternalDisplay(); } - LorieService.setMainActivity(this); - LorieService.start(LorieService.ACTION_START_FROM_ACTIVITY); + preferences.registerOnSharedPreferenceChangeListener((sharedPreferences, key) -> onPreferencesChanged()); + + onLorieServiceStart(); getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); @@ -76,7 +87,7 @@ protected void onCreate(Bundle savedInstanceState) { Button preferencesButton = findViewById(R.id.preferences_button); preferencesButton.setOnClickListener((l) -> { Intent i = new Intent(this, LoriePreferences.class); - i.setAction(ACTION_START_PREFERENCES_ACTIVITY); + i.setAction(Intent.ACTION_MAIN); startActivity(i); }); @@ -85,9 +96,36 @@ protected void onCreate(Bundle savedInstanceState) { getDecorView(). setPointerIcon(PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL)); - Intent i = getIntent(); - if (i != null && i.getBooleanExtra(LorieService.LAUNCHED_BY_COMPATION, false)) { - LorieService.sendRunCommand(this); + registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (ACTION_START.equals(intent.getAction())) { + try { + IBinder b = intent.getBundleExtra("").getBinder(""); + service = ICmdEntryInterface.Stub.asInterface(b); + ParcelFileDescriptor fd = service.getXConnection(); + if (fd != null) + connect(fd.detachFd()); + } catch (Exception ignored) {} + } + } + }, new IntentFilter(ACTION_START)); + } + + void onPreferencesChanged() { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + int mode = Integer.parseInt(preferences.getString("touchMode", "1")); + mTP.setMode(mode); + + getWindow().getDecorView().setOnApplyWindowInsetsListener(this); + + if (preferences.getBoolean("showAdditionalKbd", true)) + kbd.setVisibility(View.VISIBLE); + else + kbd.setVisibility(View.INVISIBLE); + + if (preferences.getBoolean("dexMetaKeyCapture", false)) { + SamsungDexUtils.dexMetaKeyCapture(this, false); } } @@ -149,12 +187,18 @@ public void onConfigurationChanged(@NonNull Configuration newConfig) { orientation = newConfig.orientation; } - public void onLorieServiceStart(LorieService instance) { + public void onLorieServiceStart() { SurfaceView lorieView = findViewById(R.id.lorieView); SurfaceView cursorView = findViewById(R.id.cursorView); - instance.setListeners(lorieView, cursorView); - kbd.reload(keys, lorieView, LorieService.getOnKeyListener()); + lorieView.setFocusable(true); + lorieView.setFocusableInTouchMode(true); + lorieView.requestFocus(); + + listener.setAsListenerTo(lorieView, cursorView); + + mTP = new TouchParser(lorieView, this); + kbd.reload(keys, lorieView, listener); } @Override @@ -206,9 +250,10 @@ public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, @Non } } + @SuppressLint("WrongConstant") @Override public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(LorieService.getInstance()); + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); if (preferences.getBoolean("showAdditionalKbd", true) && kbd != null) { handler.postDelayed(() -> { Rect r = new Rect(); @@ -228,6 +273,131 @@ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { }, 100); } - return LorieService.getInstance().onApplyWindowInsets(v, insets); + SurfaceView c = v.getRootView().findViewById(R.id.lorieView); + SurfaceHolder h = (c != null) ? c.getHolder() : null; + if (h != null) + handler.postDelayed(() -> windowChanged(h.getSurface(), 0, 0), 100); + return insets; + } + + @SuppressWarnings("SameParameterValue") + private class ServiceEventListener implements View.OnKeyListener { + @SuppressLint({"WrongConstant", "ClickableViewAccessibility"}) + private void setAsListenerTo(SurfaceView view, SurfaceView cursor) { + view.setOnTouchListener((v, e) -> mTP.onTouchEvent(e)); + view.setOnHoverListener((v, e) -> mTP.onTouchEvent(e)); + view.setOnGenericMotionListener((v, e) -> mTP.onTouchEvent(e)); + view.setOnKeyListener(this); + windowChanged(view.getHolder().getSurface(), view.getWidth(), view.getHeight()); + + cursor.getHolder().addCallback(new SurfaceHolder.Callback() { + @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { + cursor.getHolder().setFormat(PixelFormat.TRANSLUCENT); + } + @Override public void surfaceChanged(@NonNull SurfaceHolder holder, int f, int w, int h) { + cursor.getHolder().setFormat(PixelFormat.TRANSLUCENT); + cursorChanged(holder.getSurface()); + } + @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) { + cursorChanged(null); + } + }); + + view.getHolder().addCallback(new SurfaceHolder.Callback() { + @Override public void surfaceCreated(@NonNull SurfaceHolder holder) {} + @Override public void surfaceChanged(@NonNull SurfaceHolder holder, int f, int w, int h) { + windowChanged(holder.getSurface(), w, h); + if (service != null) { + try { + service.outputResize(w, h); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + } + @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) { + windowChanged(null, 0, 0); + } + }); + } + + private boolean isSource(KeyEvent e, int source) { + return (e.getSource() & source) == source; + } + + private boolean rightPressed = false; // Prevent right button press event from being repeated + private boolean middlePressed = false; // Prevent middle button press event from being repeated + @Override + public boolean onKey(View v, int keyCode, KeyEvent e) { + int action = 0; + + if (keyCode == KeyEvent.KEYCODE_BACK) { + if (isSource(e, InputDevice.SOURCE_MOUSE) && + rightPressed != (e.getAction() == KeyEvent.ACTION_DOWN)) { + onPointerButton(TouchParser.BTN_RIGHT, (e.getAction() == KeyEvent.ACTION_DOWN) ? TouchParser.ACTION_DOWN : TouchParser.ACTION_UP); + rightPressed = (e.getAction() == KeyEvent.ACTION_DOWN); + } else if (e.getAction() == KeyEvent.ACTION_UP) { + KeyboardUtils.toggleKeyboardVisibility(MainActivity.this); + if (kbd!=null) + kbd.requestFocus(); + } + return true; + } + + if (keyCode == KeyEvent.KEYCODE_MENU && + isSource(e, InputDevice.SOURCE_MOUSE) && + middlePressed != (e.getAction() == KeyEvent.ACTION_DOWN)) { + onPointerButton(TouchParser.BTN_MIDDLE, (e.getAction() == KeyEvent.ACTION_DOWN) ? TouchParser.ACTION_DOWN : TouchParser.ACTION_UP); + middlePressed = (e.getAction() == KeyEvent.ACTION_DOWN); + return true; + } + + if (e.getAction() == KeyEvent.ACTION_DOWN) action = TouchParser.ACTION_DOWN; + if (e.getAction() == KeyEvent.ACTION_UP) action = TouchParser.ACTION_UP; + onKeyboardKey(action, keyCode, e.isShiftPressed() ? 1 : 0, e.getCharacters()); + return true; + } + } + + @SuppressWarnings("unused") + // It is used in native code + void setRendererVisibility(boolean visible) { + runOnUiThread(()-> { + findViewById(R.id.stub).setVisibility(visible?View.INVISIBLE:View.VISIBLE); + findViewById(R.id.lorieView).setVisibility(visible?View.VISIBLE:View.INVISIBLE); + }); + } + + @SuppressWarnings("unused") + // It is used in native code + void setCursorVisibility(boolean visible) { + runOnUiThread(()-> findViewById(R.id.cursorView).setVisibility(visible?View.VISIBLE:View.INVISIBLE)); + } + + @SuppressWarnings("unused") + // It is used in native code + void setCursorRect(int x, int y, int w, int h) { + runOnUiThread(()-> { + SurfaceView v = findViewById(R.id.cursorView); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(w, h); + params.setMargins(x, y, 0, 0); + + v.setLayoutParams(params); + v.setVisibility(View.VISIBLE); + }); + } + + static Handler handler = new Handler(); + + private native long connect(int fd); + private native void cursorChanged(Surface surface); + private native void windowChanged(Surface surface, int width, int height); + public native void onPointerMotion(int x, int y); + public native void onPointerScroll(int axis, float value); + public native void onPointerButton(int button, int type); + private native void onKeyboardKey(int key, int type, int shift, String characters); + + static { + System.loadLibrary("lorie-client"); } } diff --git a/app/src/main/java/com/termux/x11/TermuxX11StarterReceiver.java b/app/src/main/java/com/termux/x11/TermuxX11StarterReceiver.java deleted file mode 100644 index a27138ef9..000000000 --- a/app/src/main/java/com/termux/x11/TermuxX11StarterReceiver.java +++ /dev/null @@ -1,155 +0,0 @@ -package com.termux.x11; - -import android.app.Activity; -import android.app.ActivityOptions; -import android.content.Intent; -import android.os.Build; -import android.os.Bundle; -import android.os.IBinder; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.provider.Settings; -import android.util.Log; -import android.view.Display; -import android.widget.Toast; - -import androidx.annotation.Nullable; -import androidx.core.hardware.display.DisplayManagerCompat; - -public class TermuxX11StarterReceiver extends Activity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - Intent intent = getIntent(); - if (intent != null) - handleIntent(intent); - - Intent launchIntent = new Intent(this, MainActivity.class); - launchIntent.putExtra(LorieService.LAUNCHED_BY_COMPATION, true); - launchIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - - Bundle bundle = createLaunchParams(launchIntent); - startActivity(launchIntent, bundle); - finish(); - } - - /** - * Creates bundle for launching on external display (if supported) and - * adds any necessary intent flags. - * @param launchIntent - * @return Bundle - */ - private Bundle createLaunchParams(Intent launchIntent) { - Display externalDisplay = findExternalDisplay(); - - /* - Multi-display support was added back in Oreo, but proper keyboard / mouse input mapping to display wasn't. So we would only - be able to view, but not interact with anything on external display. So instead, also checking to see if Android 10 desktop - mode is enabled, which does have proper keyboard + mouse support on external displays. - */ - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || !hasEnabledAndroidDesktopModeSetting() || externalDisplay == null) { - return Bundle.EMPTY; - } - ActivityOptions options = ActivityOptions - .makeBasic() - .setLaunchDisplayId(externalDisplay.getDisplayId()); - - launchIntent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK); - launchIntent.putExtra(MainActivity.REQUEST_LAUNCH_EXTERNAL_DISPLAY, true); - return options.toBundle(); - } - - /** - * Finds first display that is not our built-in. - * @return - External display if found, otherwise null. - */ - @Nullable - private Display findExternalDisplay() { - DisplayManagerCompat displayManager = DisplayManagerCompat.getInstance(this); - for (Display display : displayManager.getDisplays()) { - // id 0 is built-in screen - if (display.getDisplayId() != 0) { - return display; - } - } - return null; - } - - /** - * Checks to see if experimental Android Desktop mode developer setting is enabled, - * which was introduced in Android 10. - * @return true if enabled, false otherwise - */ - private boolean hasEnabledAndroidDesktopModeSetting() { - try { - int value = Settings.Global.getInt(getContentResolver(), "force_desktop_mode_on_external_displays"); - return value == 1; - } catch (Settings.SettingNotFoundException e) { - return false; - } - } - - private void log(String s) { - Log.e("NewIntent", s); - } - - private void handleIntent(Intent intent) { - final String extraName = "com.termux.x11.starter"; - Bundle bundle; - IBinder token; - ITermuxX11Internal svc; - ParcelFileDescriptor pfd = null; - String toastText; - - // We do not use Object.equals(Object obj) for the case same intent was passed twice - if (intent == null) - return; - - toastText = intent.getStringExtra("toast"); - if (toastText != null) - Toast.makeText(this, toastText, Toast.LENGTH_LONG).show(); - - bundle = intent.getBundleExtra(extraName); - if (bundle == null) { - log("Got intent without " + extraName + " bundle"); - return; - } - - token = bundle.getBinder(""); - if (token == null) { - log("got " + extraName + " extra but it has no Binder token"); - return; - } - - svc = ITermuxX11Internal.Stub.asInterface(token); - if (svc == null) { - log("Could not create " + extraName + " service proxy"); - return; - } - - try { - pfd = svc.getWaylandFD(); - if (pfd != null) - LorieService.adoptWaylandFd(pfd.getFd()); - } catch (Exception e) { - log("Failed to receive ParcelFileDescriptor"); - e.printStackTrace(); - } - - try { - pfd = svc.getLogFD(); - if (pfd != null) { - LorieService.startLogcatForFd(pfd.getFd()); - } - } catch (Exception e) { - log("Failed to receive ParcelFileDescriptor"); - e.printStackTrace(); - } - - try { - svc.finish(); - } catch (RemoteException e) { - e.printStackTrace(); - } - } -} diff --git a/app/src/main/java/com/termux/x11/starter/ActivityManager.java b/app/src/main/java/com/termux/x11/starter/ActivityManager.java deleted file mode 100644 index 5f2d96597..000000000 --- a/app/src/main/java/com/termux/x11/starter/ActivityManager.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.termux.x11.starter; - -/** - * \@hide-hidden constants - */ -public class ActivityManager { - private static final int FIRST_START_FATAL_ERROR_CODE = -100; - private static final int LAST_START_FATAL_ERROR_CODE = -1; - private static final int FIRST_START_SUCCESS_CODE = 0; - private static final int LAST_START_SUCCESS_CODE = 99; - private static final int FIRST_START_NON_FATAL_ERROR_CODE = 100; - private static final int LAST_START_NON_FATAL_ERROR_CODE = 199; - - /** - * Result for IActivityManager.startVoiceActivity: active session is currently hidden. - * @hide - */ - public static final int START_VOICE_HIDDEN_SESSION = FIRST_START_FATAL_ERROR_CODE; - - /** - * Result for IActivityManager.startVoiceActivity: active session does not match - * the requesting token. - * @hide - */ - public static final int START_VOICE_NOT_ACTIVE_SESSION = FIRST_START_FATAL_ERROR_CODE + 1; - - /** - * Result for IActivityManager.startActivity: trying to start a background user - * activity that shouldn't be displayed for all users. - * @hide - */ - public static final int START_NOT_CURRENT_USER_ACTIVITY = FIRST_START_FATAL_ERROR_CODE + 2; - - /** - * Result for IActivityManager.startActivity: trying to start an activity under voice - * control when that activity does not support the VOICE category. - * @hide - */ - public static final int START_NOT_VOICE_COMPATIBLE = FIRST_START_FATAL_ERROR_CODE + 3; - - /** - * Result for IActivityManager.startActivity: an error where the - * start had to be canceled. - * @hide - */ - public static final int START_CANCELED = FIRST_START_FATAL_ERROR_CODE + 4; - - /** - * Result for IActivityManager.startActivity: an error where the - * thing being started is not an activity. - * @hide - */ - public static final int START_NOT_ACTIVITY = FIRST_START_FATAL_ERROR_CODE + 5; - - /** - * Result for IActivityManager.startActivity: an error where the - * caller does not have permission to start the activity. - * @hide - */ - public static final int START_PERMISSION_DENIED = FIRST_START_FATAL_ERROR_CODE + 6; - - /** - * Result for IActivityManager.startActivity: an error where the - * caller has requested both to forward a result and to receive - * a result. - * @hide - */ - public static final int START_FORWARD_AND_REQUEST_CONFLICT = FIRST_START_FATAL_ERROR_CODE + 7; - - /** - * Result for IActivityManager.startActivity: an error where the - * requested class is not found. - * @hide - */ - public static final int START_CLASS_NOT_FOUND = FIRST_START_FATAL_ERROR_CODE + 8; - - /** - * Result for IActivityManager.startActivity: an error where the - * given Intent could not be resolved to an activity. - * @hide - */ - public static final int START_INTENT_NOT_RESOLVED = FIRST_START_FATAL_ERROR_CODE + 9; - - /** - * Result for IActivityManager.startAssistantActivity: active session is currently hidden. - * @hide - */ - public static final int START_ASSISTANT_HIDDEN_SESSION = FIRST_START_FATAL_ERROR_CODE + 10; - - /** - * Result for IActivityManager.startAssistantActivity: active session does not match - * the requesting token. - * @hide - */ - public static final int START_ASSISTANT_NOT_ACTIVE_SESSION = FIRST_START_FATAL_ERROR_CODE + 11; - - /** - * Result for IActivityManaqer.startActivity: the activity was started - * successfully as normal. - * @hide - */ - public static final int START_SUCCESS = FIRST_START_SUCCESS_CODE; - - /** - * Result for IActivityManaqer.startActivity: the caller asked that the Intent not - * be executed if it is the recipient, and that is indeed the case. - * @hide - */ - public static final int START_RETURN_INTENT_TO_CALLER = FIRST_START_SUCCESS_CODE + 1; - - /** - * Result for IActivityManaqer.startActivity: activity wasn't really started, but - * a task was simply brought to the foreground. - * @hide - */ - public static final int START_TASK_TO_FRONT = FIRST_START_SUCCESS_CODE + 2; - - /** - * Result for IActivityManaqer.startActivity: activity wasn't really started, but - * the given Intent was given to the existing top activity. - * @hide - */ - public static final int START_DELIVERED_TO_TOP = FIRST_START_SUCCESS_CODE + 3; - - /** - * Result for IActivityManaqer.startActivity: request was canceled because - * app switches are temporarily canceled to ensure the user's last request - * (such as pressing home) is performed. - * @hide - */ - public static final int START_SWITCHES_CANCELED = FIRST_START_NON_FATAL_ERROR_CODE; - - /** - * Result for IActivityManaqer.startActivity: a new activity was attempted to be started - * while in Lock Task Mode. - * @hide - */ - public static final int START_RETURN_LOCK_TASK_MODE_VIOLATION = - FIRST_START_NON_FATAL_ERROR_CODE + 1; - - /** - * Result for IActivityManaqer.startActivity: a new activity start was aborted. Never returned - * externally. - * @hide - */ - public static final int START_ABORTED = FIRST_START_NON_FATAL_ERROR_CODE + 2; - - /** - * Flag for IActivityManaqer.startActivity: do special start mode where - * a new activity is launched only if it is needed. - * @hide - */ - public static final int START_FLAG_ONLY_IF_NEEDED = 1<<0; - - /** - * Flag for IActivityManaqer.startActivity: launch the app for - * debugging. - * @hide - */ - public static final int START_FLAG_DEBUG = 1<<1; - - /** - * Flag for IActivityManaqer.startActivity: launch the app for - * allocation tracking. - * @hide - */ - public static final int START_FLAG_TRACK_ALLOCATION = 1<<2; - - /** - * Flag for IActivityManaqer.startActivity: launch the app with - * native debugging support. - * @hide - */ - public static final int START_FLAG_NATIVE_DEBUGGING = 1<<3; - - /** - * Result for IActivityManaqer.broadcastIntent: success! - * @hide - */ - public static final int BROADCAST_SUCCESS = 0; - - /** - * Result for IActivityManaqer.broadcastIntent: attempt to broadcast - * a sticky intent without appropriate permission. - * @hide - */ - public static final int BROADCAST_STICKY_CANT_HAVE_PERMISSION = -1; - - /** - * Result for IActivityManager.broadcastIntent: trying to send a broadcast - * to a stopped user. Fail. - * @hide - */ - public static final int BROADCAST_FAILED_USER_STOPPED = -2; - - -} diff --git a/app/src/main/java/com/termux/x11/starter/Compat.java b/app/src/main/java/com/termux/x11/starter/Compat.java deleted file mode 100644 index 0524d1638..000000000 --- a/app/src/main/java/com/termux/x11/starter/Compat.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.termux.x11.starter; - -import android.annotation.SuppressLint; -import android.app.ActivityManagerNative; -import android.app.ActivityTaskManager; -import android.app.AppOpsManager; -import android.app.IActivityManager; -import android.app.IActivityTaskManager; -import android.content.Context; -import android.content.Intent; -import android.os.Build; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.ServiceManager; -import android.os.Process; - -import com.android.internal.app.IAppOpsService; - -import java.lang.reflect.InvocationTargetException; - -public class Compat { - public static final String callingPackage = "com.termux"; - public static int startActivity(Intent i) { - try { - if (Build.VERSION.SDK_INT >= 30) { - IActivityTaskManager taskManager = ActivityTaskManager.getService(); - return taskManager.startActivity(null, callingPackage, null, i, null, null, null, -1, 0, null, null); - } else if (Build.VERSION.SDK_INT == 29) { - IActivityTaskManager taskManager = ActivityTaskManager.getService(); - return taskManager.startActivity(null, callingPackage, i, null, null, null, -1, 0, null, null); - } else { - IActivityManager activityManager; - IBinder binder = ServiceManager.getService("activity"); - if (Build.VERSION.SDK_INT >= 26) - activityManager = IActivityManager.Stub.asInterface(binder); - else - activityManager = ActivityManagerNative.asInterface(binder); - - return activityManager.startActivityAsUser(null, callingPackage, i, null, null, null, -1, 0, null, null, 0); - } - } catch (RemoteException e) { - e.printStackTrace(); - } - return ActivityManager.START_PERMISSION_DENIED; - } - - @SuppressWarnings({"JavaReflectionMemberAccess", "SameParameterValue"}) - @SuppressLint("PrivateApi") - public static boolean havePermission(String permission) { - try { - // We do not need Context to use checkOpNoThrow/unsafeCheckOpNoThrow so it can be null - IBinder binder = ServiceManager.getService("appops"); - IAppOpsService service = IAppOpsService.Stub.asInterface(binder); - AppOpsManager appops = (AppOpsManager) Class.forName("android.app.AppOpsManager") - .getDeclaredConstructor(Context.class, IAppOpsService.class) - .newInstance(null, service); - - if (appops == null) return false; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - int allowed; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - allowed = appops.unsafeCheckOpNoThrow(permission, Process.myUid(), callingPackage); - } else { - allowed = appops.checkOpNoThrow(permission, Process.myUid(), callingPackage); - } - - return (allowed == AppOpsManager.MODE_ALLOWED); - } else { - return false; - } - } catch ( ClassNotFoundException | NoSuchMethodException | IllegalAccessException - | InstantiationException | InvocationTargetException e) { - e.printStackTrace(); - } - return false; - } -} diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk deleted file mode 100644 index 1714bd259..000000000 --- a/app/src/main/jni/Android.mk +++ /dev/null @@ -1,23 +0,0 @@ -ROOT_PATH := $(call my-dir) -include $(ROOT_PATH)/lorie/Android.mk -include $(ROOT_PATH)/starter/Android.mk - -LOCAL_PATH:= $(ROOT_PATH)/wayland/src - -include $(CLEAR_VARS) -LOCAL_MODULE := wayland-server -LOCAL_SRC_FILES := \ - connection.c \ - event-loop.c \ - wayland-protocol.c \ - wayland-server.c \ - wayland-shm.c \ - wayland-os.c \ - wayland-util.c - -#LOCAL_SRC_FILES += ../../lorie/utils/log.cpp -#LOCAL_CFLAGS := -finstrument-functions -#LOCAL_LDFLAGS := -llog - -LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LOCAL_PATH)/.. -include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk deleted file mode 100644 index ce095350e..000000000 --- a/app/src/main/jni/Application.mk +++ /dev/null @@ -1 +0,0 @@ -APP_STL := c++_static diff --git a/app/src/main/jni/lorie/android.cpp b/app/src/main/jni/lorie/android.cpp deleted file mode 100644 index bf5ceb7d4..000000000 --- a/app/src/main/jni/lorie/android.cpp +++ /dev/null @@ -1,379 +0,0 @@ -#include -#include "lorie_compositor.hpp" -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define DEFAULT_DPI 96 - -#pragma ide diagnostic ignored "hicpp-signed-bitwise" -#define unused __attribute__((__unused__)) -#define always_inline __attribute__((always_inline)) inline - -JavaVM *vm{}; - -jfieldID lorie_compositor::compositor_field_id{}; - -lorie_compositor::lorie_compositor(jobject thiz): lorie_compositor() { - this->thiz = thiz; - self = std::thread([=, this]{ - vm->AttachCurrentThread(&env, nullptr); - compositor_field_id = env->GetFieldID(env->GetObjectClass(thiz), "compositor", "J"); - set_renderer_visibility_id = env->GetMethodID(env->GetObjectClass(thiz), "setRendererVisibility", "(Z)V"); - set_cursor_visibility_id = env->GetMethodID(env->GetObjectClass(thiz), "setCursorVisibility", "(Z)V"); - set_cursor_rect_id =env->GetMethodID( env->GetObjectClass(thiz), "setCursorRect", "(IIII)V"); - - set_renderer_visibility = [=](bool visible) { - env->CallVoidMethod(thiz, set_renderer_visibility_id, visible); - }; - - set_cursor_visibility = [=](JNIEnv* jenv, bool visible) { - jenv->CallVoidMethod(thiz, set_cursor_visibility_id, visible); - if (!visible || !screen.sfc) - set_cursor_position = [=](JNIEnv*, int, int) {}; - else - set_cursor_position = [=](JNIEnv* jenv, int x, int y) { - if (!cursor.sfc) - return; - auto data = cursor.sfc ? any_cast(cursor.sfc->user_data()) : nullptr; - auto b = data->buffer ?: nullptr; - int sx = x - cursor.hotspot_x; - int sy = y - cursor.hotspot_y; - int w = b ? b->shm_width() : 0; - int h = b ? b->shm_height() : 0; - jenv->CallVoidMethod(thiz, set_cursor_rect_id, sx, sy, w, h); - }; - }; - - run(); - - vm->DetachCurrentThread(); - }); -} - -__asm__ ( - " .global blob\n" - " .global blob_size\n" - " .section .rodata\n" - " blob:\n" - " .incbin \"en_us.xkbmap\"\n" - " 1:\n" - " blob_size:\n" - " .int 1b - blob" -); - -extern jbyte blob[]; -extern int blob_size; - -void lorie_compositor::get_keymap(int *fd, int *size) { // NOLINT(readability-convert-member-functions-to-static) - int keymap_fd = os_create_anonymous_file(blob_size); - void *dest = mmap(nullptr, blob_size, PROT_READ | PROT_WRITE, MAP_SHARED, keymap_fd, 0); - memcpy(dest, blob, blob_size); - munmap(dest, blob_size); - - struct stat s = {}; - fstat(keymap_fd, &s); - *size = s.st_size; // NOLINT(cppcoreguidelines-narrowing-conversions) - *fd = keymap_fd; -} - -// For some reason both static_cast and reinterpret_cast returning 0 when casting b.bits. -static always_inline uint32_t* cast(void* p) { union { void* a; uint32_t* b; } c {p}; return c.b; } // NOLINT(cppcoreguidelines-pro-type-member-init) - -static always_inline void blit_exact(EGLNativeWindowType win, const uint32_t* src, int width, int height) { - if (width == 0 || height == 0) { - width = ANativeWindow_getWidth(win); - height = ANativeWindow_getHeight(win); - } - ARect bounds{ 0, 0, width, height }; - ANativeWindow_Buffer b{}; - - ANativeWindow_acquire(win); - auto ret = ANativeWindow_setBuffersGeometry(win, width, height, AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM); - if (ret != 0) { - LOGE("Failed to set buffers geometry (%d)", ret); - return; - } - - ret = ANativeWindow_lock(win, &b, &bounds); - if (ret != 0) { - LOGE("Failed to lock"); - return; - } - - uint32_t* dst = cast(b.bits); - if (src) { - for (int i = 0; i < height; i++) { - for (int j = 0; j < width; j++) { - uint32_t s = src[width * i + j]; - // Cast BGRA to RGBA - dst[b.stride * i + j] = (s & 0xFF000000) | ((s & 0x00FF0000) >> 16) | (s & 0x0000FF00) | ((s & 0x000000FF) << 16); - } - } - } else - memset(dst, 0, b.stride*b.height); - - ret = ANativeWindow_unlockAndPost(win); - if (ret != 0) { - LOGE("Failed to post"); - return; - } - - ANativeWindow_release(win); -} - -void lorie_compositor::blit(EGLNativeWindowType win, wayland::surface_t* sfc) { - if (!win) - return; - - auto buffer = sfc ? any_cast(sfc->user_data())->buffer : nullptr; - if (buffer && buffer->is_shm() && buffer->shm_data()) - blit_exact(win, cast(buffer->shm_data()), buffer->shm_width(), buffer->shm_height()); - else - blit_exact(win, nullptr, 0, 0); -} - -template struct wrapper_impl; -template -struct wrapper_impl { - // Be careful and do passing jobjects here!!! - [[maybe_unused]] static always_inline R execute(JNIEnv* env, jobject obj, A... args) { - auto native = lorie_compositor::compositor_field_id ? reinterpret_cast(env->GetLongField(obj, lorie_compositor::compositor_field_id)) : nullptr; - if (native != nullptr) - return (native->*f)(args...); - return static_cast(defaultValue); - } - - [[maybe_unused]] static always_inline void queue(JNIEnv* env, jobject obj, A... args) { - auto native = lorie_compositor::compositor_field_id ? reinterpret_cast(env->GetLongField(obj, lorie_compositor::compositor_field_id)) : nullptr; - if (native != nullptr) - native->post([=]{ (native->*f)(args...); }); - } -}; -template -auto execute = wrapper_impl::execute; -template -auto queue = wrapper_impl::queue; - -[[maybe_unused]] static always_inline lorie_compositor* get(JNIEnv* env, jobject obj) { - return lorie_compositor::compositor_field_id ? - reinterpret_cast(env->GetLongField(obj, lorie_compositor::compositor_field_id)) : - nullptr; -} - -/////////////////////////////////////////////////////////// - -#define JNI_DECLARE_INNER(package, classname, methodname ) \ - Java_ ## package ## _ ## classname ## _ ## methodname -#define JNI_DECLARE(classname, methodname) \ - JNI_DECLARE_INNER(com_termux_x11, classname, methodname) - -extern "C" JNIEXPORT jlong JNICALL -JNI_DECLARE(LorieService, createLorieThread)(JNIEnv *env, jobject thiz) { -#if 0 - // It is needed to redirect stderr to logcat - setenv("WAYLAND_DEBUG", "1", 1); - new std::thread([]{ - FILE *fp; - int p[2]; - size_t read, len; - char* line = nullptr; - pipe(p); - fp = fdopen(p[0], "r"); - - dup2(p[1], 2); - while ((read = getline(&line, &len, fp)) != -1) { - __android_log_write(ANDROID_LOG_VERBOSE, "WAYLAND_STDERR", line); - } - }); -#endif - return (jlong) new lorie_compositor(env->NewGlobalRef(thiz)); -} - -extern "C" JNIEXPORT void JNICALL -JNI_DECLARE(LorieService, passWaylandFD)(JNIEnv *env, jobject thiz, jint fd) { - LOGI("JNI: got fd %d", fd); - execute<&lorie_compositor::add_socket_fd>(env, thiz, fd); -} - -extern "C" JNIEXPORT void JNICALL -JNI_DECLARE(LorieService, terminate)(JNIEnv *env, jobject obj) { - auto b = reinterpret_cast(env->GetLongField(obj, lorie_compositor::compositor_field_id)); - b->terminate(); - b->post([]{}); - b->self.join(); - env->SetLongField(obj, lorie_compositor::compositor_field_id, 0); - delete b; -} - -extern "C" JNIEXPORT void JNICALL -JNI_DECLARE(LorieService, cursorChanged)(JNIEnv *env, jobject thiz, jobject surface) { - EGLNativeWindowType win = surface?ANativeWindow_fromSurface(env, surface):nullptr; - auto c = get(env, thiz); - c->post([=]{ c->cursor.win = win; }); -} - -extern "C" JNIEXPORT void JNICALL -JNI_DECLARE(LorieService, windowChanged)(JNIEnv *env, jobject thiz, jobject surface, jint w, jint h) { - EGLNativeWindowType win = surface?ANativeWindow_fromSurface(env, surface):nullptr; - queue<&lorie_compositor::output_resize>(env, thiz, win, w, h, int(w*25.4/DEFAULT_DPI), int(h*25.4/DEFAULT_DPI)); -} - -extern "C" JNIEXPORT void JNICALL -JNI_DECLARE(LorieService, pointerMotion)(JNIEnv *env, jobject thiz, jint x, jint y) { - queue<&lorie_compositor::pointer_motion>(env, thiz, x, y); - if (lorie_compositor::compositor_field_id) { - auto native = - lorie_compositor::compositor_field_id ? - reinterpret_cast(env->GetLongField(thiz,lorie_compositor::compositor_field_id)): - nullptr; - if (native) { - native->set_cursor_visibility(env, true); - native->set_cursor_position(env, x, y); - } - } -} - -extern "C" JNIEXPORT void JNICALL -JNI_DECLARE(LorieService, pointerScroll)(JNIEnv *env, jobject thiz, jint axis, jfloat value) { - queue<&lorie_compositor::pointer_scroll>(env, thiz, axis, value); - if (lorie_compositor::compositor_field_id) { - auto native = - lorie_compositor::compositor_field_id ? - reinterpret_cast(env->GetLongField(thiz,lorie_compositor::compositor_field_id)): - nullptr; - if (native) { - native->set_cursor_visibility(env, true); - } - } -} - -extern "C" JNIEXPORT void JNICALL -JNI_DECLARE(LorieService, pointerButton)(JNIEnv *env, jobject thiz, jint button, jint type) { - queue<&lorie_compositor::pointer_button>(env, thiz, uint32_t(button), uint32_t(type)); - if (lorie_compositor::compositor_field_id) { - auto native = - lorie_compositor::compositor_field_id ? - reinterpret_cast(env->GetLongField(thiz,lorie_compositor::compositor_field_id)): - nullptr; - if (native) { - native->set_cursor_visibility(env, true); - } - } -} - -extern "C" void get_character_data(char** layout, int *shift, int *ec, char *ch); -extern "C" void android_keycode_get_eventcode(int kc, int *ec, int *shift); - -extern "C" JNIEXPORT void JNICALL -JNI_DECLARE(LorieService, keyboardKey)(JNIEnv *env, jobject thiz, - jint type, jint key_code, jint jshift, jstring characters_) { - char *characters = nullptr; - - int event_code = 0; - int shift = jshift; - if (characters_ != nullptr) characters = (char*) env->GetStringUTFChars(characters_, nullptr); - if (key_code && !characters) { - android_keycode_get_eventcode(key_code, &event_code, &shift); - LOGE("kc: %d ec: %d", key_code, event_code); - } - if (!key_code && characters) { - char *layout = nullptr; - get_character_data(&layout, &shift, &event_code, characters); - } - LOGE("Keyboard input: keyCode: %d; eventCode: %d; characters: %s; shift: %d, type: %d", key_code, event_code, characters, shift, type); - - if (shift || jshift) - queue<&lorie_compositor::keyboard_key>(env, thiz, 42, wayland::keyboard_key_state::pressed); - - // For some reason Android do not send ACTION_DOWN for non-English characters - if (characters) - queue<&lorie_compositor::keyboard_key>(env, thiz, event_code, wayland::keyboard_key_state::pressed); - - queue<&lorie_compositor::keyboard_key>(env, thiz, event_code, wayland::keyboard_key_state(type)); - - if (shift || jshift) - queue<&lorie_compositor::keyboard_key>(env, thiz, 42, wayland::keyboard_key_state::released); - - if (characters_ != nullptr) env->ReleaseStringUTFChars(characters_, characters); -} - -static bool sameUid(int pid) { - char path[32] = {0}; - struct stat s = {0}; - sprintf(path, "/proc/%d", pid); - stat(path, &s); - return s.st_uid == getuid(); -} - -static void killAllLogcats() { - DIR* proc; - struct dirent* dir_elem; - char path[64] = {0}, link[64] = {0}; - pid_t pid, self = getpid(); - if ((proc = opendir("/proc")) == nullptr) { - LOGE("opendir: %s", strerror(errno)); - return; - } - - while((dir_elem = readdir(proc)) != nullptr) { - if (!(pid = (pid_t) atoi (dir_elem->d_name)) || pid == self || !sameUid(pid)) // NOLINT(cert-err34-c) - continue; - - memset(path, 0, sizeof(path)); - memset(link, 0, sizeof(link)); - sprintf(path, "/proc/%d/exe", pid); - if (readlink(path, link, sizeof(link)) < 0) { - LOGE("readlink %s: %s", path, strerror(errno)); - continue; - } - if (strstr(link, "/logcat") != nullptr) { - if (kill(pid, SIGKILL) < 0) { - LOGE("kill %d (%s): %s", pid, link, strerror(errno)); - } - } - } -} - -void fork(const std::function& f) { - switch(fork()) { - case -1: LOGE("fork: %s", strerror(errno)); return; - case 0: f(); return; - default: return; - } -} - -extern "C" JNIEXPORT void JNICALL -Java_com_termux_x11_LorieService_startLogcatForFd(unused JNIEnv *env, unused jclass clazz, jint fd) { - killAllLogcats(); - - LOGI("Starting logcat with output to given fd"); - fork([]() { - execl("/system/bin/logcat", "logcat", "-c", nullptr); - LOGE("exec logcat: %s", strerror(errno)); - }); - - fork([fd]() { - dup2(fd, 1); - dup2(fd, 2); - execl("/system/bin/logcat", "logcat", nullptr); - LOGE("exec logcat: %s", strerror(errno)); - }); -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wshadow" -extern "C" jint JNI_OnLoad(JavaVM* vm, [[maybe_unused]] void* reserved) { - ::vm = vm; - return JNI_VERSION_1_6; -} -#pragma clang diagnostic pop diff --git a/app/src/main/jni/lorie/backend/android/keymaps.h b/app/src/main/jni/lorie/backend/android/keymaps.h deleted file mode 100644 index 2f433858b..000000000 --- a/app/src/main/jni/lorie/backend/android/keymaps.h +++ /dev/null @@ -1,245 +0,0 @@ -#include -#define SYM_LENGTH 7 -#define KEYCODE_MIN 8 -#define KEYCODE_MAX 255 -#define NOSYM {{0}, {0}} -struct lorie_keymap { - char *name; - struct keysym { - char normal[SYM_LENGTH]; - char shift[SYM_LENGTH]; - } keysyms[KEYCODE_MAX - KEYCODE_MIN]; -}; - -struct lorie_keymap lorie_keymap_ru = { - .name = (char*) "ru", - .keysyms = { - NOSYM, // eventCode: 0 - NOSYM, // eventCode: 1 - {{49, 0, 0, 0, 0, 0, 0}, {33, 0, 0, 0, 0, 0, 0}}, // eventCode: 2; normal: "1"; shift: "!"; - {{50, 0, 0, 0, 0, 0, 0}, {34, 0, 0, 0, 0, 0, 0}}, // eventCode: 3; normal: "2"; shift: """; - {{51, 0, 0, 0, 0, 0, 0}, {-30, -124, -106, 0, 0, 0, 0}}, // eventCode: 4; normal: "3"; shift: "№"; - {{52, 0, 0, 0, 0, 0, 0}, {59, 0, 0, 0, 0, 0, 0}}, // eventCode: 5; normal: "4"; shift: ";"; - {{53, 0, 0, 0, 0, 0, 0}, {37, 0, 0, 0, 0, 0, 0}}, // eventCode: 6; normal: "5"; shift: "%"; - {{54, 0, 0, 0, 0, 0, 0}, {58, 0, 0, 0, 0, 0, 0}}, // eventCode: 7; normal: "6"; shift: ":"; - {{55, 0, 0, 0, 0, 0, 0}, {63, 0, 0, 0, 0, 0, 0}}, // eventCode: 8; normal: "7"; shift: "?"; - {{56, 0, 0, 0, 0, 0, 0}, {42, 0, 0, 0, 0, 0, 0}}, // eventCode: 9; normal: "8"; shift: "*"; - {{57, 0, 0, 0, 0, 0, 0}, {40, 0, 0, 0, 0, 0, 0}}, // eventCode: 10; normal: "9"; shift: "("; - {{48, 0, 0, 0, 0, 0, 0}, {41, 0, 0, 0, 0, 0, 0}}, // eventCode: 11; normal: "0"; shift: ")"; - {{45, 0, 0, 0, 0, 0, 0}, {95, 0, 0, 0, 0, 0, 0}}, // eventCode: 12; normal: "-"; shift: "_"; - {{61, 0, 0, 0, 0, 0, 0}, {43, 0, 0, 0, 0, 0, 0}}, // eventCode: 13; normal: "="; shift: "+"; - {{8, 0, 0, 0, 0, 0, 0}, {8, 0, 0, 0, 0, 0, 0}}, // eventCode: 14; - NOSYM, // eventCode: 15; normal: " "; - {{-48, -71, 0, 0, 0, 0, 0}, {-48, -103, 0, 0, 0, 0, 0}}, // eventCode: 16; normal: "й"; shift: "Й"; - {{-47, -122, 0, 0, 0, 0, 0}, {-48, -90, 0, 0, 0, 0, 0}}, // eventCode: 17; normal: "ц"; shift: "Ц"; - {{-47, -125, 0, 0, 0, 0, 0}, {-48, -93, 0, 0, 0, 0, 0}}, // eventCode: 18; normal: "у"; shift: "У"; - {{-48, -70, 0, 0, 0, 0, 0}, {-48, -102, 0, 0, 0, 0, 0}}, // eventCode: 19; normal: "к"; shift: "К"; - {{-48, -75, 0, 0, 0, 0, 0}, {-48, -107, 0, 0, 0, 0, 0}}, // eventCode: 20; normal: "е"; shift: "Е"; - {{-48, -67, 0, 0, 0, 0, 0}, {-48, -99, 0, 0, 0, 0, 0}}, // eventCode: 21; normal: "н"; shift: "Н"; - {{-48, -77, 0, 0, 0, 0, 0}, {-48, -109, 0, 0, 0, 0, 0}}, // eventCode: 22; normal: "г"; shift: "Г"; - {{-47, -120, 0, 0, 0, 0, 0}, {-48, -88, 0, 0, 0, 0, 0}}, // eventCode: 23; normal: "ш"; shift: "Ш"; - {{-47, -119, 0, 0, 0, 0, 0}, {-48, -87, 0, 0, 0, 0, 0}}, // eventCode: 24; normal: "щ"; shift: "Щ"; - {{-48, -73, 0, 0, 0, 0, 0}, {-48, -105, 0, 0, 0, 0, 0}}, // eventCode: 25; normal: "з"; shift: "З"; - {{-47, -123, 0, 0, 0, 0, 0}, {-48, -91, 0, 0, 0, 0, 0}}, // eventCode: 26; normal: "х"; shift: "Х"; - {{-47, -118, 0, 0, 0, 0, 0}, {-48, -86, 0, 0, 0, 0, 0}}, // eventCode: 27; normal: "ъ"; shift: "Ъ"; - {{13, 0, 0, 0, 0, 0, 0}, {13, 0, 0, 0, 0, 0, 0}}, // eventCode: 28; - NOSYM, // eventCode: 29 - {{-47, -124, 0, 0, 0, 0, 0}, {-48, -92, 0, 0, 0, 0, 0}}, // eventCode: 30; normal: "ф"; shift: "Ф"; - {{-47, -117, 0, 0, 0, 0, 0}, {-48, -85, 0, 0, 0, 0, 0}}, // eventCode: 31; normal: "ы"; shift: "Ы"; - {{-48, -78, 0, 0, 0, 0, 0}, {-48, -110, 0, 0, 0, 0, 0}}, // eventCode: 32; normal: "в"; shift: "В"; - {{-48, -80, 0, 0, 0, 0, 0}, {-48, -112, 0, 0, 0, 0, 0}}, // eventCode: 33; normal: "а"; shift: "А"; - {{-48, -65, 0, 0, 0, 0, 0}, {-48, -97, 0, 0, 0, 0, 0}}, // eventCode: 34; normal: "п"; shift: "П"; - {{-47, -128, 0, 0, 0, 0, 0}, {-48, -96, 0, 0, 0, 0, 0}}, // eventCode: 35; normal: "р"; shift: "Р"; - {{-48, -66, 0, 0, 0, 0, 0}, {-48, -98, 0, 0, 0, 0, 0}}, // eventCode: 36; normal: "о"; shift: "О"; - {{-48, -69, 0, 0, 0, 0, 0}, {-48, -101, 0, 0, 0, 0, 0}}, // eventCode: 37; normal: "л"; shift: "Л"; - {{-48, -76, 0, 0, 0, 0, 0}, {-48, -108, 0, 0, 0, 0, 0}}, // eventCode: 38; normal: "д"; shift: "Д"; - {{-48, -74, 0, 0, 0, 0, 0}, {-48, -106, 0, 0, 0, 0, 0}}, // eventCode: 39; normal: "ж"; shift: "Ж"; - {{-47, -115, 0, 0, 0, 0, 0}, {-48, -83, 0, 0, 0, 0, 0}}, // eventCode: 40; normal: "э"; shift: "Э"; - {{-47, -111, 0, 0, 0, 0, 0}, {-48, -127, 0, 0, 0, 0, 0}}, // eventCode: 41; normal: "ё"; shift: "Ё"; - NOSYM, // eventCode: 42; - {{92, 0, 0, 0, 0, 0, 0}, {47, 0, 0, 0, 0, 0, 0}}, // eventCode: 43; normal: "\"; shift: "/"; - {{-47, -113, 0, 0, 0, 0, 0}, {-48, -81, 0, 0, 0, 0, 0}}, // eventCode: 44; normal: "я"; shift: "Я"; - {{-47, -121, 0, 0, 0, 0, 0}, {-48, -89, 0, 0, 0, 0, 0}}, // eventCode: 45; normal: "ч"; shift: "Ч"; - {{-47, -127, 0, 0, 0, 0, 0}, {-48, -95, 0, 0, 0, 0, 0}}, // eventCode: 46; normal: "с"; shift: "С"; - {{-48, -68, 0, 0, 0, 0, 0}, {-48, -100, 0, 0, 0, 0, 0}}, // eventCode: 47; normal: "м"; shift: "М"; - {{-48, -72, 0, 0, 0, 0, 0}, {-48, -104, 0, 0, 0, 0, 0}}, // eventCode: 48; normal: "и"; shift: "И"; - {{-47, -126, 0, 0, 0, 0, 0}, {-48, -94, 0, 0, 0, 0, 0}}, // eventCode: 49; normal: "т"; shift: "Т"; - {{-47, -116, 0, 0, 0, 0, 0}, {-48, -84, 0, 0, 0, 0, 0}}, // eventCode: 50; normal: "ь"; shift: "Ь"; - {{-48, -79, 0, 0, 0, 0, 0}, {-48, -111, 0, 0, 0, 0, 0}}, // eventCode: 51; normal: "б"; shift: "Б"; - {{-47, -114, 0, 0, 0, 0, 0}, {-48, -82, 0, 0, 0, 0, 0}}, // eventCode: 52; normal: "ю"; shift: "Ю"; - {{46, 0, 0, 0, 0, 0, 0}, {44, 0, 0, 0, 0, 0, 0}}, // eventCode: 53; normal: "."; shift: ","; - } -}; - -struct lorie_keymap *lorie_keymaps[] = {&lorie_keymap_ru, NULL}; - -struct lorie_keymap_android { - int eventCode; - int shift; -} lorie_keymap_android[] = { - {0, 0}, {0, 0}, {0, 0}, {0, 0}, - {1, 0}, // keycode 4 - {0, 0}, {0, 0}, - {11, 0}, // keycode 7 - {2, 0}, // keycode 8 - {3, 0}, // keycode 9 - {4, 0}, // keycode 10 - {5, 0}, // keycode 11 - {6, 0}, // keycode 12 - {7, 0}, // keycode 13 - {8, 0}, // keycode 14 - {9, 0}, // keycode 15 - {10, 0}, // keycode 16 - {9, 1}, // keycode 17 - {4, 1}, // keycode 18 - {103, 0}, // keycode 19 - {108, 0}, // keycode 20 - {105, 0}, // keycode 21 - {106, 0}, // keycode 22 - {0, 0}, - {115, 0}, // keycode 24 - {114, 0}, // keycode 25 - {116, 0}, // keycode 26 - {212, 0}, // keycode 27 - {0, 0}, - {30, 0}, // keycode 29 - {48, 0}, // keycode 30 - {46, 0}, // keycode 31 - {32, 0}, // keycode 32 - {18, 0}, // keycode 33 - {33, 0}, // keycode 34 - {34, 0}, // keycode 35 - {35, 0}, // keycode 36 - {23, 0}, // keycode 37 - {36, 0}, // keycode 38 - {37, 0}, // keycode 39 - {38, 0}, // keycode 40 - {50, 0}, // keycode 41 - {49, 0}, // keycode 42 - {24, 0}, // keycode 43 - {25, 0}, // keycode 44 - {16, 0}, // keycode 45 - {19, 0}, // keycode 46 - {31, 0}, // keycode 47 - {20, 0}, // keycode 48 - {22, 0}, // keycode 49 - {47, 0}, // keycode 50 - {17, 0}, // keycode 51 - {45, 0}, // keycode 52 - {21, 0}, // keycode 53 - {44, 0}, // keycode 54 - {51, 0}, // keycode 55 - {52, 0}, // keycode 56 - {56, 0}, // keycode 57 - {100, 0}, // keycode 58 - {42, 0}, // keycode 59 - {54, 0}, // keycode 60 - {15, 0}, // keycode 61 - {57, 0}, // keycode 62 - {0, 0}, - {150, 0}, // keycode 64 - {155, 0}, // keycode 65 - {28, 0}, // keycode 66 - {14, 0}, // keycode 67 - {41, 0}, // keycode 68 - {12, 0}, // keycode 69 - {13, 0}, // keycode 70 - {26, 0}, // keycode 71 - {27, 0}, // keycode 72 - {43, 0}, // keycode 73 - {39, 0}, // keycode 74 - {40, 0}, // keycode 75 - {53, 0}, // keycode 76 - {3, 1}, // keycode 77 - {0, 0}, {0, 0}, {0, 0}, - {13, 1}, // keycode 81 - {139, 0}, // keycode 82 - {0, 0}, - {217, 0}, // keycode 84 - {164, 0}, // keycode 85 - {625, 0}, // keycode 86 - {163, 0}, // keycode 87 - {165, 0}, // keycode 88 - {168, 0}, // keycode 89 - {208, 0}, // keycode 90 - {248, 0}, // keycode 91 - {104, 0}, // keycode 92 - {109, 0}, // keycode 93 - {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, - {1, 0}, // keycode 111 - {111, 0}, // keycode 112 - {29, 0}, // keycode 113 - {97, 0}, // keycode 114 - {58, 0}, // keycode 115 - {70, 0}, // keycode 116 - {125, 0}, // keycode 117 - {126, 0}, // keycode 118 - {0, 0}, - {99, 0}, // keycode 120 - {411, 0}, // keycode 121 - {102, 0}, // keycode 122 - {107, 0}, // keycode 123 - {110, 0}, // keycode 124 - {159, 0}, // keycode 125 - {207, 0}, // keycode 126 - {0, 0}, - {160, 0}, // keycode 128 - {161, 0}, // keycode 129 - {167, 0}, // keycode 130 - {59, 0}, // keycode 131 - {60, 0}, // keycode 132 - {61, 0}, // keycode 133 - {62, 0}, // keycode 134 - {63, 0}, // keycode 135 - {64, 0}, // keycode 136 - {65, 0}, // keycode 137 - {66, 0}, // keycode 138 - {67, 0}, // keycode 139 - {68, 0}, // keycode 140 - {87, 0}, // keycode 141 - {88, 0}, // keycode 142 - {69, 0}, // keycode 143 - {82, 0}, // keycode 144 - {79, 0}, // keycode 145 - {80, 0}, // keycode 146 - {81, 0}, // keycode 147 - {75, 0}, // keycode 148 - {76, 0}, // keycode 149 - {77, 0}, // keycode 150 - {71, 0}, // keycode 151 - {72, 0}, // keycode 152 - {73, 0}, // keycode 153 - {98, 0}, // keycode 154 - {55, 0}, // keycode 155 - {74, 0}, // keycode 156 - {78, 0}, // keycode 157 - {83, 0}, // keycode 158 - {121, 0}, // keycode 159 - {96, 0}, // keycode 160 - {117, 0}, // keycode 161 - {179, 0}, // keycode 162 - {180, 0}, // keycode 163 - {113, 0}, // keycode 164 - {358, 0}, // keycode 165 - {402, 0}, // keycode 166 - {403, 0}, // keycode 167 - {418, 0}, // keycode 168 - {419, 0}, // keycode 169 - {377, 0}, // keycode 170 - {0, 0}, {0, 0}, {0, 0}, - {156, 0}, // keycode 174 - {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, - {398, 0}, // keycode 183 - {399, 0}, // keycode 184 - {400, 0}, // keycode 185 - {401, 0}, // keycode 186 - {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, - {429, 0}, // keycode 207 - {397, 0}, // keycode 208 - {387, 0}, // keycode 209 - {140, 0}, // keycode 210 - {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, - {224, 0}, // keycode 220 - {225, 0}, // keycode 221 - {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0} -}; diff --git a/app/src/main/jni/lorie/backend/android/locale/android-keycodes.h b/app/src/main/jni/lorie/backend/android/locale/android-keycodes.h deleted file mode 100644 index a8528d0ab..000000000 --- a/app/src/main/jni/lorie/backend/android/locale/android-keycodes.h +++ /dev/null @@ -1,752 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef __ANDROID_KEYCODES_H__ -#define __ANDROID_KEYCODES_H__ - -/** Key code constant: Unknown key code. */ -#define ANDROID_KEYCODE_UNKNOWN 0 -/** Key code constant: Soft Left key. - * Usually situated below the display on phones and used as a multi-function - * feature key for selecting a software defined function shown on the bottom left - * of the display. */ -#define ANDROID_KEYCODE_SOFT_LEFT 1 -/** Key code constant: Soft Right key. - * Usually situated below the display on phones and used as a multi-function - * feature key for selecting a software defined function shown on the bottom right - * of the display. */ -#define ANDROID_KEYCODE_SOFT_RIGHT 2 -/** Key code constant: Home key. - * This key is handled by the framework and is never delivered to applications. */ -#define ANDROID_KEYCODE_HOME 3 -/** Key code constant: Back key. */ -#define ANDROID_KEYCODE_BACK 4 -/** Key code constant: Call key. */ -#define ANDROID_KEYCODE_CALL 5 -/** Key code constant: End Call key. */ -#define ANDROID_KEYCODE_ENDCALL 6 -/** Key code constant: '0' key. */ -#define ANDROID_KEYCODE_0 7 -/** Key code constant: '1' key. */ -#define ANDROID_KEYCODE_1 8 -/** Key code constant: '2' key. */ -#define ANDROID_KEYCODE_2 9 -/** Key code constant: '3' key. */ -#define ANDROID_KEYCODE_3 10 -/** Key code constant: '4' key. */ -#define ANDROID_KEYCODE_4 11 -/** Key code constant: '5' key. */ -#define ANDROID_KEYCODE_5 12 -/** Key code constant: '6' key. */ -#define ANDROID_KEYCODE_6 13 -/** Key code constant: '7' key. */ -#define ANDROID_KEYCODE_7 14 -/** Key code constant: '8' key. */ -#define ANDROID_KEYCODE_8 15 -/** Key code constant: '9' key. */ -#define ANDROID_KEYCODE_9 16 -/** Key code constant: '*' key. */ -#define ANDROID_KEYCODE_STAR 17 -/** Key code constant: '#' key. */ -#define ANDROID_KEYCODE_POUND 18 -/** Key code constant: Directional Pad Up key. - * May also be synthesized from trackball motions. */ -#define ANDROID_KEYCODE_DPAD_UP 19 -/** Key code constant: Directional Pad Down key. - * May also be synthesized from trackball motions. */ -#define ANDROID_KEYCODE_DPAD_DOWN 20 -/** Key code constant: Directional Pad Left key. - * May also be synthesized from trackball motions. */ -#define ANDROID_KEYCODE_DPAD_LEFT 21 -/** Key code constant: Directional Pad Right key. - * May also be synthesized from trackball motions. */ -#define ANDROID_KEYCODE_DPAD_RIGHT 22 -/** Key code constant: Directional Pad Center key. - * May also be synthesized from trackball motions. */ -#define ANDROID_KEYCODE_DPAD_CENTER 23 -/** Key code constant: Volume Up key. - * Adjusts the speaker volume up. */ -#define ANDROID_KEYCODE_VOLUME_UP 24 -/** Key code constant: Volume Down key. - * Adjusts the speaker volume down. */ -#define ANDROID_KEYCODE_VOLUME_DOWN 25 -/** Key code constant: Power key. */ -#define ANDROID_KEYCODE_POWER 26 -/** Key code constant: Camera key. - * Used to launch a camera application or take pictures. */ -#define ANDROID_KEYCODE_CAMERA 27 -/** Key code constant: Clear key. */ -#define ANDROID_KEYCODE_CLEAR 28 -/** Key code constant: 'A' key. */ -#define ANDROID_KEYCODE_A 29 -/** Key code constant: 'B' key. */ -#define ANDROID_KEYCODE_B 30 -/** Key code constant: 'C' key. */ -#define ANDROID_KEYCODE_C 31 -/** Key code constant: 'D' key. */ -#define ANDROID_KEYCODE_D 32 -/** Key code constant: 'E' key. */ -#define ANDROID_KEYCODE_E 33 -/** Key code constant: 'F' key. */ -#define ANDROID_KEYCODE_F 34 -/** Key code constant: 'G' key. */ -#define ANDROID_KEYCODE_G 35 -/** Key code constant: 'H' key. */ -#define ANDROID_KEYCODE_H 36 -/** Key code constant: 'I' key. */ -#define ANDROID_KEYCODE_I 37 -/** Key code constant: 'J' key. */ -#define ANDROID_KEYCODE_J 38 -/** Key code constant: 'K' key. */ -#define ANDROID_KEYCODE_K 39 -/** Key code constant: 'L' key. */ -#define ANDROID_KEYCODE_L 40 -/** Key code constant: 'M' key. */ -#define ANDROID_KEYCODE_M 41 -/** Key code constant: 'N' key. */ -#define ANDROID_KEYCODE_N 42 -/** Key code constant: 'O' key. */ -#define ANDROID_KEYCODE_O 43 -/** Key code constant: 'P' key. */ -#define ANDROID_KEYCODE_P 44 -/** Key code constant: 'Q' key. */ -#define ANDROID_KEYCODE_Q 45 -/** Key code constant: 'R' key. */ -#define ANDROID_KEYCODE_R 46 -/** Key code constant: 'S' key. */ -#define ANDROID_KEYCODE_S 47 -/** Key code constant: 'T' key. */ -#define ANDROID_KEYCODE_T 48 -/** Key code constant: 'U' key. */ -#define ANDROID_KEYCODE_U 49 -/** Key code constant: 'V' key. */ -#define ANDROID_KEYCODE_V 50 -/** Key code constant: 'W' key. */ -#define ANDROID_KEYCODE_W 51 -/** Key code constant: 'X' key. */ -#define ANDROID_KEYCODE_X 52 -/** Key code constant: 'Y' key. */ -#define ANDROID_KEYCODE_Y 53 -/** Key code constant: 'Z' key. */ -#define ANDROID_KEYCODE_Z 54 -/** Key code constant: ',' key. */ -#define ANDROID_KEYCODE_COMMA 55 -/** Key code constant: '.' key. */ -#define ANDROID_KEYCODE_PERIOD 56 -/** Key code constant: Left Alt modifier key. */ -#define ANDROID_KEYCODE_ALT_LEFT 57 -/** Key code constant: Right Alt modifier key. */ -#define ANDROID_KEYCODE_ALT_RIGHT 58 -/** Key code constant: Left Shift modifier key. */ -#define ANDROID_KEYCODE_SHIFT_LEFT 59 -/** Key code constant: Right Shift modifier key. */ -#define ANDROID_KEYCODE_SHIFT_RIGHT 60 -/** Key code constant: Tab key. */ -#define ANDROID_KEYCODE_TAB 61 -/** Key code constant: Space key. */ -#define ANDROID_KEYCODE_SPACE 62 -/** Key code constant: Symbol modifier key. - * Used to enter alternate symbols. */ -#define ANDROID_KEYCODE_SYM 63 -/** Key code constant: Explorer special function key. - * Used to launch a browser application. */ -#define ANDROID_KEYCODE_EXPLORER 64 -/** Key code constant: Envelope special function key. - * Used to launch a mail application. */ -#define ANDROID_KEYCODE_ENVELOPE 65 -/** Key code constant: Enter key. */ -#define ANDROID_KEYCODE_ENTER 66 -/** Key code constant: Backspace key. - * Deletes characters before the insertion point, unlike {@link #KEYCODE_FORWARD_DEL}. */ -#define ANDROID_KEYCODE_DEL 67 -/** Key code constant: '`' (backtick) key. */ -#define ANDROID_KEYCODE_GRAVE 68 -/** Key code constant: '-'. */ -#define ANDROID_KEYCODE_MINUS 69 -/** Key code constant: '=' key. */ -#define ANDROID_KEYCODE_EQUALS 70 -/** Key code constant: '[' key. */ -#define ANDROID_KEYCODE_LEFT_BRACKET 71 -/** Key code constant: ']' key. */ -#define ANDROID_KEYCODE_RIGHT_BRACKET 72 -/** Key code constant: '\' key. */ -#define ANDROID_KEYCODE_BACKSLASH 73 -/** Key code constant: ';' key. */ -#define ANDROID_KEYCODE_SEMICOLON 74 -/** Key code constant: ''' (apostrophe) key. */ -#define ANDROID_KEYCODE_APOSTROPHE 75 -/** Key code constant: '/' key. */ -#define ANDROID_KEYCODE_SLASH 76 -/** Key code constant: '@' key. */ -#define ANDROID_KEYCODE_AT 77 -/** Key code constant: Number modifier key. - * Used to enter numeric symbols. - * This key is not Num Lock; it is more like {@link #KEYCODE_ALT_LEFT} and is - * interpreted as an ALT key by {@link android.text.method.MetaKeyKeyListener}. */ -#define ANDROID_KEYCODE_NUM 78 -/** Key code constant: Headset Hook key. - * Used to hang up calls and stop media. */ -#define ANDROID_KEYCODE_HEADSETHOOK 79 -/** Key code constant: Camera Focus key. - * Used to focus the camera. */ -#define ANDROID_KEYCODE_FOCUS 80 // *Camera* focus -/** Key code constant: '+' key. */ -#define ANDROID_KEYCODE_PLUS 81 -/** Key code constant: Menu key. */ -#define ANDROID_KEYCODE_MENU 82 -/** Key code constant: Notification key. */ -#define ANDROID_KEYCODE_NOTIFICATION 83 -/** Key code constant: Search key. */ -#define ANDROID_KEYCODE_SEARCH 84 -/** Key code constant: Play/Pause media key. */ -#define ANDROID_KEYCODE_MEDIA_PLAY_PAUSE 85 -/** Key code constant: Stop media key. */ -#define ANDROID_KEYCODE_MEDIA_STOP 86 -/** Key code constant: Play Next media key. */ -#define ANDROID_KEYCODE_MEDIA_NEXT 87 -/** Key code constant: Play Previous media key. */ -#define ANDROID_KEYCODE_MEDIA_PREVIOUS 88 -/** Key code constant: Rewind media key. */ -#define ANDROID_KEYCODE_MEDIA_REWIND 89 -/** Key code constant: Fast Forward media key. */ -#define ANDROID_KEYCODE_MEDIA_FAST_FORWARD 90 -/** Key code constant: Mute key. - * Mutes the microphone, unlike {@link #KEYCODE_VOLUME_MUTE}. */ -#define ANDROID_KEYCODE_MUTE 91 -/** Key code constant: Page Up key. */ -#define ANDROID_KEYCODE_PAGE_UP 92 -/** Key code constant: Page Down key. */ -#define ANDROID_KEYCODE_PAGE_DOWN 93 -/** Key code constant: Picture Symbols modifier key. - * Used to switch symbol sets (Emoji, Kao-moji). */ -#define ANDROID_KEYCODE_PICTSYMBOLS 94 // switch symbol-sets (Emoji,Kao-moji) -/** Key code constant: Switch Charset modifier key. - * Used to switch character sets (Kanji, Katakana). */ -#define ANDROID_KEYCODE_SWITCH_CHARSET 95 // switch char-sets (Kanji,Katakana) -/** Key code constant: A Button key. - * On a game controller, the A button should be either the button labeled A - * or the first button on the bottom row of controller buttons. */ -#define ANDROID_KEYCODE_BUTTON_A 96 -/** Key code constant: B Button key. - * On a game controller, the B button should be either the button labeled B - * or the second button on the bottom row of controller buttons. */ -#define ANDROID_KEYCODE_BUTTON_B 97 -/** Key code constant: C Button key. - * On a game controller, the C button should be either the button labeled C - * or the third button on the bottom row of controller buttons. */ -#define ANDROID_KEYCODE_BUTTON_C 98 -/** Key code constant: X Button key. - * On a game controller, the X button should be either the button labeled X - * or the first button on the upper row of controller buttons. */ -#define ANDROID_KEYCODE_BUTTON_X 99 -/** Key code constant: Y Button key. - * On a game controller, the Y button should be either the button labeled Y - * or the second button on the upper row of controller buttons. */ -#define ANDROID_KEYCODE_BUTTON_Y 100 -/** Key code constant: Z Button key. - * On a game controller, the Z button should be either the button labeled Z - * or the third button on the upper row of controller buttons. */ -#define ANDROID_KEYCODE_BUTTON_Z 101 -/** Key code constant: L1 Button key. - * On a game controller, the L1 button should be either the button labeled L1 (or L) - * or the top left trigger button. */ -#define ANDROID_KEYCODE_BUTTON_L1 102 -/** Key code constant: R1 Button key. - * On a game controller, the R1 button should be either the button labeled R1 (or R) - * or the top right trigger button. */ -#define ANDROID_KEYCODE_BUTTON_R1 103 -/** Key code constant: L2 Button key. - * On a game controller, the L2 button should be either the button labeled L2 - * or the bottom left trigger button. */ -#define ANDROID_KEYCODE_BUTTON_L2 104 -/** Key code constant: R2 Button key. - * On a game controller, the R2 button should be either the button labeled R2 - * or the bottom right trigger button. */ -#define ANDROID_KEYCODE_BUTTON_R2 105 -/** Key code constant: Left Thumb Button key. - * On a game controller, the left thumb button indicates that the left (or only) - * joystick is pressed. */ -#define ANDROID_KEYCODE_BUTTON_THUMBL 106 -/** Key code constant: Right Thumb Button key. - * On a game controller, the right thumb button indicates that the right - * joystick is pressed. */ -#define ANDROID_KEYCODE_BUTTON_THUMBR 107 -/** Key code constant: Start Button key. - * On a game controller, the button labeled Start. */ -#define ANDROID_KEYCODE_BUTTON_START 108 -/** Key code constant: Select Button key. - * On a game controller, the button labeled Select. */ -#define ANDROID_KEYCODE_BUTTON_SELECT 109 -/** Key code constant: Mode Button key. - * On a game controller, the button labeled Mode. */ -#define ANDROID_KEYCODE_BUTTON_MODE 110 -/** Key code constant: Escape key. */ -#define ANDROID_KEYCODE_ESCAPE 111 -/** Key code constant: Forward Delete key. - * Deletes characters ahead of the insertion point, unlike {@link #KEYCODE_DEL}. */ -#define ANDROID_KEYCODE_FORWARD_DEL 112 -/** Key code constant: Left Control modifier key. */ -#define ANDROID_KEYCODE_CTRL_LEFT 113 -/** Key code constant: Right Control modifier key. */ -#define ANDROID_KEYCODE_CTRL_RIGHT 114 -/** Key code constant: Caps Lock key. */ -#define ANDROID_KEYCODE_CAPS_LOCK 115 -/** Key code constant: Scroll Lock key. */ -#define ANDROID_KEYCODE_SCROLL_LOCK 116 -/** Key code constant: Left Meta modifier key. */ -#define ANDROID_KEYCODE_META_LEFT 117 -/** Key code constant: Right Meta modifier key. */ -#define ANDROID_KEYCODE_META_RIGHT 118 -/** Key code constant: Function modifier key. */ -#define ANDROID_KEYCODE_FUNCTION 119 -/** Key code constant: System Request / Print Screen key. */ -#define ANDROID_KEYCODE_SYSRQ 120 -/** Key code constant: Break / Pause key. */ -#define ANDROID_KEYCODE_BREAK 121 -/** Key code constant: Home Movement key. - * Used for scrolling or moving the cursor around to the start of a line - * or to the top of a list. */ -#define ANDROID_KEYCODE_MOVE_HOME 122 -/** Key code constant: End Movement key. - * Used for scrolling or moving the cursor around to the end of a line - * or to the bottom of a list. */ -#define ANDROID_KEYCODE_MOVE_END 123 -/** Key code constant: Insert key. - * Toggles insert / overwrite edit mode. */ -#define ANDROID_KEYCODE_INSERT 124 -/** Key code constant: Forward key. - * Navigates forward in the history stack. Complement of {@link #KEYCODE_BACK}. */ -#define ANDROID_KEYCODE_FORWARD 125 -/** Key code constant: Play media key. */ -#define ANDROID_KEYCODE_MEDIA_PLAY 126 -/** Key code constant: Pause media key. */ -#define ANDROID_KEYCODE_MEDIA_PAUSE 127 -/** Key code constant: Close media key. - * May be used to close a CD tray, for example. */ -#define ANDROID_KEYCODE_MEDIA_CLOSE 128 -/** Key code constant: Eject media key. - * May be used to eject a CD tray, for example. */ -#define ANDROID_KEYCODE_MEDIA_EJECT 129 -/** Key code constant: Record media key. */ -#define ANDROID_KEYCODE_MEDIA_RECORD 130 -/** Key code constant: F1 key. */ -#define ANDROID_KEYCODE_F1 131 -/** Key code constant: F2 key. */ -#define ANDROID_KEYCODE_F2 132 -/** Key code constant: F3 key. */ -#define ANDROID_KEYCODE_F3 133 -/** Key code constant: F4 key. */ -#define ANDROID_KEYCODE_F4 134 -/** Key code constant: F5 key. */ -#define ANDROID_KEYCODE_F5 135 -/** Key code constant: F6 key. */ -#define ANDROID_KEYCODE_F6 136 -/** Key code constant: F7 key. */ -#define ANDROID_KEYCODE_F7 137 -/** Key code constant: F8 key. */ -#define ANDROID_KEYCODE_F8 138 -/** Key code constant: F9 key. */ -#define ANDROID_KEYCODE_F9 139 -/** Key code constant: F10 key. */ -#define ANDROID_KEYCODE_F10 140 -/** Key code constant: F11 key. */ -#define ANDROID_KEYCODE_F11 141 -/** Key code constant: F12 key. */ -#define ANDROID_KEYCODE_F12 142 -/** Key code constant: Num Lock key. - * This is the Num Lock key; it is different from {@link #KEYCODE_NUM}. - * This key alters the behavior of other keys on the numeric keypad. */ -#define ANDROID_KEYCODE_NUM_LOCK 143 -/** Key code constant: Numeric keypad '0' key. */ -#define ANDROID_KEYCODE_NUMPAD_0 144 -/** Key code constant: Numeric keypad '1' key. */ -#define ANDROID_KEYCODE_NUMPAD_1 145 -/** Key code constant: Numeric keypad '2' key. */ -#define ANDROID_KEYCODE_NUMPAD_2 146 -/** Key code constant: Numeric keypad '3' key. */ -#define ANDROID_KEYCODE_NUMPAD_3 147 -/** Key code constant: Numeric keypad '4' key. */ -#define ANDROID_KEYCODE_NUMPAD_4 148 -/** Key code constant: Numeric keypad '5' key. */ -#define ANDROID_KEYCODE_NUMPAD_5 149 -/** Key code constant: Numeric keypad '6' key. */ -#define ANDROID_KEYCODE_NUMPAD_6 150 -/** Key code constant: Numeric keypad '7' key. */ -#define ANDROID_KEYCODE_NUMPAD_7 151 -/** Key code constant: Numeric keypad '8' key. */ -#define ANDROID_KEYCODE_NUMPAD_8 152 -/** Key code constant: Numeric keypad '9' key. */ -#define ANDROID_KEYCODE_NUMPAD_9 153 -/** Key code constant: Numeric keypad '/' key (for division). */ -#define ANDROID_KEYCODE_NUMPAD_DIVIDE 154 -/** Key code constant: Numeric keypad '*' key (for multiplication). */ -#define ANDROID_KEYCODE_NUMPAD_MULTIPLY 155 -/** Key code constant: Numeric keypad '-' key (for subtraction). */ -#define ANDROID_KEYCODE_NUMPAD_SUBTRACT 156 -/** Key code constant: Numeric keypad '+' key (for addition). */ -#define ANDROID_KEYCODE_NUMPAD_ADD 157 -/** Key code constant: Numeric keypad '.' key (for decimals or digit grouping). */ -#define ANDROID_KEYCODE_NUMPAD_DOT 158 -/** Key code constant: Numeric keypad ',' key (for decimals or digit grouping). */ -#define ANDROID_KEYCODE_NUMPAD_COMMA 159 -/** Key code constant: Numeric keypad Enter key. */ -#define ANDROID_KEYCODE_NUMPAD_ENTER 160 -/** Key code constant: Numeric keypad '=' key. */ -#define ANDROID_KEYCODE_NUMPAD_EQUALS 161 -/** Key code constant: Numeric keypad '(' key. */ -#define ANDROID_KEYCODE_NUMPAD_LEFT_PAREN 162 -/** Key code constant: Numeric keypad ')' key. */ -#define ANDROID_KEYCODE_NUMPAD_RIGHT_PAREN 163 -/** Key code constant: Volume Mute key. - * Mutes the speaker, unlike {@link #KEYCODE_MUTE}. - * This key should normally be implemented as a toggle such that the first press - * mutes the speaker and the second press restores the original volume. */ -#define ANDROID_KEYCODE_VOLUME_MUTE 164 -/** Key code constant: Info key. - * Common on TV remotes to show additional information related to what is - * currently being viewed. */ -#define ANDROID_KEYCODE_INFO 165 -/** Key code constant: Channel up key. - * On TV remotes, increments the television channel. */ -#define ANDROID_KEYCODE_CHANNEL_UP 166 -/** Key code constant: Channel down key. - * On TV remotes, decrements the television channel. */ -#define ANDROID_KEYCODE_CHANNEL_DOWN 167 -/** Key code constant: Zoom in key. */ -#define ANDROID_KEYCODE_ZOOM_IN 168 -/** Key code constant: Zoom out key. */ -#define ANDROID_KEYCODE_ZOOM_OUT 169 -/** Key code constant: TV key. - * On TV remotes, switches to viewing live TV. */ -#define ANDROID_KEYCODE_TV 170 -/** Key code constant: Window key. - * On TV remotes, toggles picture-in-picture mode or other windowing functions. - * On Android Wear devices, triggers a display offset. */ -#define ANDROID_KEYCODE_WINDOW 171 -/** Key code constant: Guide key. - * On TV remotes, shows a programming guide. */ -#define ANDROID_KEYCODE_GUIDE 172 -/** Key code constant: DVR key. - * On some TV remotes, switches to a DVR mode for recorded shows. */ -#define ANDROID_KEYCODE_DVR 173 -/** Key code constant: Bookmark key. - * On some TV remotes, bookmarks content or web pages. */ -#define ANDROID_KEYCODE_BOOKMARK 174 -/** Key code constant: Toggle captions key. - * Switches the mode for closed-captioning text, for example during television shows. */ -#define ANDROID_KEYCODE_CAPTIONS 175 -/** Key code constant: Settings key. - * Starts the system settings activity. */ -#define ANDROID_KEYCODE_SETTINGS 176 -/** Key code constant: TV power key. - * On TV remotes, toggles the power on a television screen. */ -#define ANDROID_KEYCODE_TV_POWER 177 -/** Key code constant: TV input key. - * On TV remotes, switches the input on a television screen. */ -#define ANDROID_KEYCODE_TV_INPUT 178 -/** Key code constant: Set-top-box power key. - * On TV remotes, toggles the power on an external Set-top-box. */ -#define ANDROID_KEYCODE_STB_POWER 179 -/** Key code constant: Set-top-box input key. - * On TV remotes, switches the input mode on an external Set-top-box. */ -#define ANDROID_KEYCODE_STB_INPUT 180 -/** Key code constant: A/V Receiver power key. - * On TV remotes, toggles the power on an external A/V Receiver. */ -#define ANDROID_KEYCODE_AVR_POWER 181 -/** Key code constant: A/V Receiver input key. - * On TV remotes, switches the input mode on an external A/V Receiver. */ -#define ANDROID_KEYCODE_AVR_INPUT 182 -/** Key code constant: Red "programmable" key. - * On TV remotes, acts as a contextual/programmable key. */ -#define ANDROID_KEYCODE_PROG_RED 183 -/** Key code constant: Green "programmable" key. - * On TV remotes, actsas a contextual/programmable key. */ -#define ANDROID_KEYCODE_PROG_GREEN 184 -/** Key code constant: Yellow "programmable" key. - * On TV remotes, acts as a contextual/programmable key. */ -#define ANDROID_KEYCODE_PROG_YELLOW 185 -/** Key code constant: Blue "programmable" key. - * On TV remotes, acts as a contextual/programmable key. */ -#define ANDROID_KEYCODE_PROG_BLUE 186 -/** Key code constant: App switch key. - * Should bring up the application switcher dialog. */ -#define ANDROID_KEYCODE_APP_SWITCH 187 -/** Key code constant: Generic Game Pad Button #1.*/ -#define ANDROID_KEYCODE_BUTTON_1 188 -/** Key code constant: Generic Game Pad Button #2.*/ -#define ANDROID_KEYCODE_BUTTON_2 189 -/** Key code constant: Generic Game Pad Button #3.*/ -#define ANDROID_KEYCODE_BUTTON_3 190 -/** Key code constant: Generic Game Pad Button #4.*/ -#define ANDROID_KEYCODE_BUTTON_4 191 -/** Key code constant: Generic Game Pad Button #5.*/ -#define ANDROID_KEYCODE_BUTTON_5 192 -/** Key code constant: Generic Game Pad Button #6.*/ -#define ANDROID_KEYCODE_BUTTON_6 193 -/** Key code constant: Generic Game Pad Button #7.*/ -#define ANDROID_KEYCODE_BUTTON_7 194 -/** Key code constant: Generic Game Pad Button #8.*/ -#define ANDROID_KEYCODE_BUTTON_8 195 -/** Key code constant: Generic Game Pad Button #9.*/ -#define ANDROID_KEYCODE_BUTTON_9 196 -/** Key code constant: Generic Game Pad Button #10.*/ -#define ANDROID_KEYCODE_BUTTON_10 197 -/** Key code constant: Generic Game Pad Button #11.*/ -#define ANDROID_KEYCODE_BUTTON_11 198 -/** Key code constant: Generic Game Pad Button #12.*/ -#define ANDROID_KEYCODE_BUTTON_12 199 -/** Key code constant: Generic Game Pad Button #13.*/ -#define ANDROID_KEYCODE_BUTTON_13 200 -/** Key code constant: Generic Game Pad Button #14.*/ -#define ANDROID_KEYCODE_BUTTON_14 201 -/** Key code constant: Generic Game Pad Button #15.*/ -#define ANDROID_KEYCODE_BUTTON_15 202 -/** Key code constant: Generic Game Pad Button #16.*/ -#define ANDROID_KEYCODE_BUTTON_16 203 -/** Key code constant: Language Switch key. - * Toggles the current input language such as switching between English and Japanese on - * a QWERTY keyboard. On some devices, the same function may be performed by - * pressing Shift+Spacebar. */ -#define ANDROID_KEYCODE_LANGUAGE_SWITCH 204 -/** Key code constant: Manner Mode key. - * Toggles silent or vibrate mode on and off to make the device behave more politely - * in certain settings such as on a crowded train. On some devices, the key may only - * operate when long-pressed. */ -#define ANDROID_KEYCODE_MANNER_MODE 205 -/** Key code constant: 3D Mode key. - * Toggles the display between 2D and 3D mode. */ -#define ANDROID_KEYCODE_3D_MODE 206 -/** Key code constant: Contacts special function key. - * Used to launch an address book application. */ -#define ANDROID_KEYCODE_CONTACTS 207 -/** Key code constant: Calendar special function key. - * Used to launch a calendar application. */ -#define ANDROID_KEYCODE_CALENDAR 208 -/** Key code constant: Music special function key. - * Used to launch a music player application. */ -#define ANDROID_KEYCODE_MUSIC 209 -/** Key code constant: Calculator special function key. - * Used to launch a calculator application. */ -#define ANDROID_KEYCODE_CALCULATOR 210 -/** Key code constant: Japanese full-width / half-width key. */ -#define ANDROID_KEYCODE_ZENKAKU_HANKAKU 211 -/** Key code constant: Japanese alphanumeric key. */ -#define ANDROID_KEYCODE_EISU 212 -/** Key code constant: Japanese non-conversion key. */ -#define ANDROID_KEYCODE_MUHENKAN 213 -/** Key code constant: Japanese conversion key. */ -#define ANDROID_KEYCODE_HENKAN 214 -/** Key code constant: Japanese katakana / hiragana key. */ -#define ANDROID_KEYCODE_KATAKANA_HIRAGANA 215 -/** Key code constant: Japanese Yen key. */ -#define ANDROID_KEYCODE_YEN 216 -/** Key code constant: Japanese Ro key. */ -#define ANDROID_KEYCODE_RO 217 -/** Key code constant: Japanese kana key. */ -#define ANDROID_KEYCODE_KANA 218 -/** Key code constant: Assist key. - * Launches the global assist activity. Not delivered to applications. */ -#define ANDROID_KEYCODE_ASSIST 219 -/** Key code constant: Brightness Down key. - * Adjusts the screen brightness down. */ -#define ANDROID_KEYCODE_BRIGHTNESS_DOWN 220 -/** Key code constant: Brightness Up key. - * Adjusts the screen brightness up. */ -#define ANDROID_KEYCODE_BRIGHTNESS_UP 221 -/** Key code constant: Audio Track key. - * Switches the audio tracks. */ -#define ANDROID_KEYCODE_MEDIA_AUDIO_TRACK 222 -/** Key code constant: Sleep key. - * Puts the device to sleep. Behaves somewhat like {@link #KEYCODE_POWER} but it - * has no effect if the device is already asleep. */ -#define ANDROID_KEYCODE_SLEEP 223 -/** Key code constant: Wakeup key. - * Wakes up the device. Behaves somewhat like {@link #KEYCODE_POWER} but it - * has no effect if the device is already awake. */ -#define ANDROID_KEYCODE_WAKEUP 224 -/** Key code constant: Pairing key. - * Initiates peripheral pairing mode. Useful for pairing remote control - * devices or game controllers, especially if no other input mode is - * available. */ -#define ANDROID_KEYCODE_PAIRING 225 -/** Key code constant: Media Top Menu key. - * Goes to the top of media menu. */ -#define ANDROID_KEYCODE_MEDIA_TOP_MENU 226 -/** Key code constant: '11' key. */ -#define ANDROID_KEYCODE_11 227 -/** Key code constant: '12' key. */ -#define ANDROID_KEYCODE_12 228 -/** Key code constant: Last Channel key. - * Goes to the last viewed channel. */ -#define ANDROID_KEYCODE_LAST_CHANNEL 229 -/** Key code constant: TV data service key. - * Displays data services like weather, sports. */ -#define ANDROID_KEYCODE_TV_DATA_SERVICE 230 -/** Key code constant: Voice Assist key. - * Launches the global voice assist activity. Not delivered to applications. */ -#define ANDROID_KEYCODE_VOICE_ASSIST 231 -/** Key code constant: Radio key. - * Toggles TV service / Radio service. */ -#define ANDROID_KEYCODE_TV_RADIO_SERVICE 232 -/** Key code constant: Teletext key. - * Displays Teletext service. */ -#define ANDROID_KEYCODE_TV_TELETEXT 233 -/** Key code constant: Number entry key. - * Initiates to enter multi-digit channel nubmber when each digit key is assigned - * for selecting separate channel. Corresponds to Number Entry Mode (0x1D) of CEC - * User Control Code. */ -#define ANDROID_KEYCODE_TV_NUMBER_ENTRY 234 -/** Key code constant: Analog Terrestrial key. - * Switches to analog terrestrial broadcast service. */ -#define ANDROID_KEYCODE_TV_TERRESTRIAL_ANALOG 235 -/** Key code constant: Digital Terrestrial key. - * Switches to digital terrestrial broadcast service. */ -#define ANDROID_KEYCODE_TV_TERRESTRIAL_DIGITAL 236 -/** Key code constant: Satellite key. - * Switches to digital satellite broadcast service. */ -#define ANDROID_KEYCODE_TV_SATELLITE 237 -/** Key code constant: BS key. - * Switches to BS digital satellite broadcasting service available in Japan. */ -#define ANDROID_KEYCODE_TV_SATELLITE_BS 238 -/** Key code constant: CS key. - * Switches to CS digital satellite broadcasting service available in Japan. */ -#define ANDROID_KEYCODE_TV_SATELLITE_CS 239 -/** Key code constant: BS/CS key. - * Toggles between BS and CS digital satellite services. */ -#define ANDROID_KEYCODE_TV_SATELLITE_SERVICE 240 -/** Key code constant: Toggle Network key. - * Toggles selecting broacast services. */ -#define ANDROID_KEYCODE_TV_NETWORK 241 -/** Key code constant: Antenna/Cable key. - * Toggles broadcast input source between antenna and cable. */ -#define ANDROID_KEYCODE_TV_ANTENNA_CABLE 242 -/** Key code constant: HDMI #1 key. - * Switches to HDMI input #1. */ -#define ANDROID_KEYCODE_TV_INPUT_HDMI_1 243 -/** Key code constant: HDMI #2 key. - * Switches to HDMI input #2. */ -#define ANDROID_KEYCODE_TV_INPUT_HDMI_2 244 -/** Key code constant: HDMI #3 key. - * Switches to HDMI input #3. */ -#define ANDROID_KEYCODE_TV_INPUT_HDMI_3 245 -/** Key code constant: HDMI #4 key. - * Switches to HDMI input #4. */ -#define ANDROID_KEYCODE_TV_INPUT_HDMI_4 246 -/** Key code constant: Composite #1 key. - * Switches to composite video input #1. */ -#define ANDROID_KEYCODE_TV_INPUT_COMPOSITE_1 247 -/** Key code constant: Composite #2 key. - * Switches to composite video input #2. */ -#define ANDROID_KEYCODE_TV_INPUT_COMPOSITE_2 248 -/** Key code constant: Component #1 key. - * Switches to component video input #1. */ -#define ANDROID_KEYCODE_TV_INPUT_COMPONENT_1 249 -/** Key code constant: Component #2 key. - * Switches to component video input #2. */ -#define ANDROID_KEYCODE_TV_INPUT_COMPONENT_2 250 -/** Key code constant: VGA #1 key. - * Switches to VGA (analog RGB) input #1. */ -#define ANDROID_KEYCODE_TV_INPUT_VGA_1 251 -/** Key code constant: Audio description key. - * Toggles audio description off / on. */ -#define ANDROID_KEYCODE_TV_AUDIO_DESCRIPTION 252 -/** Key code constant: Audio description mixing volume up key. - * Louden audio description volume as compared with normal audio volume. */ -#define ANDROID_KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP 253 -/** Key code constant: Audio description mixing volume down key. - * Lessen audio description volume as compared with normal audio volume. */ -#define ANDROID_KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN 254 -/** Key code constant: Zoom mode key. - * Changes Zoom mode (Normal, Full, Zoom, Wide-zoom, etc.) */ -#define ANDROID_KEYCODE_TV_ZOOM_MODE 255 -/** Key code constant: Contents menu key. - * Goes to the title list. Corresponds to Contents Menu (0x0B) of CEC User Control - * Code */ -#define ANDROID_KEYCODE_TV_CONTENTS_MENU 256 -/** Key code constant: Media context menu key. - * Goes to the context menu of media contents. Corresponds to Media Context-sensitive - * Menu (0x11) of CEC User Control Code. */ -#define ANDROID_KEYCODE_TV_MEDIA_CONTEXT_MENU 257 -/** Key code constant: Timer programming key. - * Goes to the timer recording menu. Corresponds to Timer Programming (0x54) of - * CEC User Control Code. */ -#define ANDROID_KEYCODE_TV_TIMER_PROGRAMMING 258 -/** Key code constant: Help key. */ -#define ANDROID_KEYCODE_HELP 259 -/** Key code constant: Navigate to previous key. - * Goes backward by one item in an ordered collection of items. */ -#define ANDROID_KEYCODE_NAVIGATE_PREVIOUS 260 -/** Key code constant: Navigate to next key. - * Advances to the next item in an ordered collection of items. */ -#define ANDROID_KEYCODE_NAVIGATE_NEXT 261 -/** Key code constant: Navigate in key. - * Activates the item that currently has focus or expands to the next level of a navigation - * hierarchy. */ -#define ANDROID_KEYCODE_NAVIGATE_IN 262 -/** Key code constant: Navigate out key. - * Backs out one level of a navigation hierarchy or collapses the item that currently has - * focus. */ -#define ANDROID_KEYCODE_NAVIGATE_OUT 263 -/** Key code constant: Primary stem key for Wear - * Main power/reset button on watch. */ -#define ANDROID_KEYCODE_STEM_PRIMARY 264 -/** Key code constant: Generic stem key 1 for Wear */ -#define ANDROID_KEYCODE_STEM_1 265 -/** Key code constant: Generic stem key 2 for Wear */ -#define ANDROID_KEYCODE_STEM_2 266 -/** Key code constant: Generic stem key 3 for Wear */ -#define ANDROID_KEYCODE_STEM_3 267 -/** Key code constant: Directional Pad Up-Left */ -#define ANDROID_KEYCODE_DPAD_UP_LEFT 268 -/** Key code constant: Directional Pad Down-Left */ -#define ANDROID_KEYCODE_DPAD_DOWN_LEFT 269 -/** Key code constant: Directional Pad Up-Right */ -#define ANDROID_KEYCODE_DPAD_UP_RIGHT 270 -/** Key code constant: Directional Pad Down-Right */ -#define ANDROID_KEYCODE_DPAD_DOWN_RIGHT 271 -/** Key code constant: Skip forward media key. */ -#define ANDROID_KEYCODE_MEDIA_SKIP_FORWARD 272 -/** Key code constant: Skip backward media key. */ -#define ANDROID_KEYCODE_MEDIA_SKIP_BACKWARD 273 -/** Key code constant: Step forward media key. - * Steps media forward, one frame at a time. */ -#define ANDROID_KEYCODE_MEDIA_STEP_FORWARD 274 -/** Key code constant: Step backward media key. - * Steps media backward, one frame at a time. */ -#define ANDROID_KEYCODE_MEDIA_STEP_BACKWARD 275 -/** Key code constant: put device to sleep unless a wakelock is held. */ -#define ANDROID_KEYCODE_SOFT_SLEEP 276 -/** Key code constant: Cut key. */ -#define ANDROID_KEYCODE_CUT 277 -/** Key code constant: Copy key. */ -#define ANDROID_KEYCODE_COPY 278 -/** Key code constant: Paste key. */ -#define ANDROID_KEYCODE_PASTE 279 -/** Key code constant: Consumed by the system for navigation up */ -#define ANDROID_KEYCODE_SYSTEM_NAVIGATION_UP 280 -/** Key code constant: Consumed by the system for navigation down */ -#define ANDROID_KEYCODE_SYSTEM_NAVIGATION_DOWN 281 -/** Key code constant: Consumed by the system for navigation left*/ -#define ANDROID_KEYCODE_SYSTEM_NAVIGATION_LEFT 282 -/** Key code constant: Consumed by the system for navigation right */ -#define ANDROID_KEYCODE_SYSTEM_NAVIGATION_RIGHT 283 -/** Key code constant: Show all apps */ -#define ANDROID_KEYCODE_ALL_APPS 284 -/** Key code constant: Refresh key. */ -#define ANDROID_KEYCODE_REFRESH 285 - -#define ANDROID_KEYCODE_LAST ANDROID_KEYCODE_REFRESH - -#endif // __ANDROID_KEYCODES_H__ diff --git a/app/src/main/jni/lorie/backend/android/locale/android-utils.c b/app/src/main/jni/lorie/backend/android/locale/android-utils.c deleted file mode 100644 index a249519fa..000000000 --- a/app/src/main/jni/lorie/backend/android/locale/android-utils.c +++ /dev/null @@ -1,175 +0,0 @@ -#pragma clang diagnostic push -#pragma ide diagnostic ignored "readability-non-const-parameter" -#pragma ide diagnostic ignored "OCDFAInspection" -#include -#include -#include "input-event-codes.h" -#include "android-keycodes.h" -#include "evdev-keycodes.h" - -#define KEYCODE1(c) case ANDROID_KEYCODE_##c : *eventCode = KEY_##c; return 1 -#define KEYCODE2(kc, ec) case ANDROID_KEYCODE_##kc : *eventCode = KEY_##ec; return 1 -#define KEYCODE2shift(kc, ec) case ANDROID_KEYCODE_##kc : *eventCode = KEY_##ec; *shift = 1; return 1 -int android_keycode_to_linux_event_code(int keyCode, int *eventCode, int *shift) { - //if (!eventCode || !shift) return; - switch (keyCode) { - KEYCODE1(1); - KEYCODE1(2); - KEYCODE1(3); - KEYCODE1(4); - KEYCODE1(5); - KEYCODE1(6); - KEYCODE1(7); - KEYCODE1(8); - KEYCODE1(9); - KEYCODE1(0); - KEYCODE1(A); - KEYCODE1(B); - KEYCODE1(C); - KEYCODE1(D); - KEYCODE1(E); - KEYCODE1(F); - KEYCODE1(G); - KEYCODE1(H); - KEYCODE1(I); - KEYCODE1(J); - KEYCODE1(K); - KEYCODE1(L); - KEYCODE1(M); - KEYCODE1(N); - KEYCODE1(O); - KEYCODE1(P); - KEYCODE1(Q); - KEYCODE1(R); - KEYCODE1(S); - KEYCODE1(T); - KEYCODE1(U); - KEYCODE1(V); - KEYCODE1(W); - KEYCODE1(X); - KEYCODE1(Y); - KEYCODE1(Z); - KEYCODE1(COMMA); - KEYCODE2(PERIOD, DOT); - KEYCODE2(DPAD_UP, UP); - KEYCODE2(DPAD_LEFT, LEFT); - KEYCODE2(DPAD_DOWN, DOWN); - KEYCODE2(DPAD_RIGHT, RIGHT); - KEYCODE2(DPAD_CENTER, ENTER); - KEYCODE2(ALT_LEFT, LEFTALT); - KEYCODE2(ALT_RIGHT, RIGHTALT); - KEYCODE2(SHIFT_LEFT, LEFTSHIFT); - KEYCODE2(SHIFT_RIGHT, RIGHTSHIFT); - KEYCODE1(TAB); - KEYCODE1(SPACE); - KEYCODE2(EXPLORER, WWW); - KEYCODE2(ENVELOPE, MAIL); - KEYCODE1(ENTER); - KEYCODE2(DEL, BACKSPACE); - KEYCODE1(GRAVE); - KEYCODE1(MINUS); - KEYCODE2(EQUALS, EQUAL); - KEYCODE2(LEFT_BRACKET, LEFTBRACE); - KEYCODE2(RIGHT_BRACKET, RIGHTBRACE); - KEYCODE1(BACKSLASH); - KEYCODE1(SEMICOLON); - KEYCODE1(APOSTROPHE); - KEYCODE1(SLASH); - KEYCODE2shift(AT, 2); - KEYCODE2shift(POUND, 3); - KEYCODE2shift(STAR, 8); - KEYCODE2shift(PLUS, EQUAL); - KEYCODE1(MENU); - KEYCODE1(SEARCH); - KEYCODE2(MEDIA_PLAY_PAUSE, PLAYPAUSE); - KEYCODE2(MEDIA_PLAY, PLAY); - KEYCODE2(MEDIA_STOP, STOP_RECORD); - KEYCODE2(MEDIA_NEXT, NEXTSONG); - KEYCODE2(MEDIA_PREVIOUS, PREVIOUSSONG); - KEYCODE2(MEDIA_REWIND, REWIND); - KEYCODE2(MEDIA_FAST_FORWARD, FASTFORWARD); - KEYCODE2(MEDIA_CLOSE, CLOSECD); - KEYCODE2(MEDIA_EJECT, EJECTCD); - KEYCODE2(MEDIA_RECORD, RECORD); - KEYCODE2(MUTE, MICMUTE); - KEYCODE2(PAGE_UP, PAGEUP); - KEYCODE2(PAGE_DOWN, PAGEDOWN); - KEYCODE2(ESCAPE, ESC); - KEYCODE2(FORWARD_DEL, DELETE); - KEYCODE2(CTRL_LEFT, LEFTCTRL); - KEYCODE2(CTRL_RIGHT, RIGHTCTRL); - KEYCODE2(CAPS_LOCK, CAPSLOCK); - KEYCODE2(SCROLL_LOCK, SCROLLLOCK); - KEYCODE2(NUM_LOCK, NUMLOCK); - KEYCODE2(META_LEFT, LEFTMETA); - KEYCODE2(META_RIGHT, RIGHTMETA); - KEYCODE1(SYSRQ); // Print screen key - KEYCODE1(BREAK); // Pause key - KEYCODE2(MOVE_HOME, HOME); - KEYCODE2(MOVE_END, END); - KEYCODE1(INSERT); - KEYCODE1(FORWARD); - KEYCODE2(BACK, ESC); - KEYCODE1(F1); - KEYCODE1(F2); - KEYCODE1(F3); - KEYCODE1(F4); - KEYCODE1(F5); - KEYCODE1(F6); - KEYCODE1(F7); - KEYCODE1(F8); - KEYCODE1(F9); - KEYCODE1(F10); - KEYCODE1(F11); - KEYCODE1(F12); - KEYCODE2(NUMPAD_0, KP0); - KEYCODE2(NUMPAD_1, KP1); - KEYCODE2(NUMPAD_2, KP2); - KEYCODE2(NUMPAD_3, KP3); - KEYCODE2(NUMPAD_4, KP4); - KEYCODE2(NUMPAD_5, KP5); - KEYCODE2(NUMPAD_6, KP6); - KEYCODE2(NUMPAD_7, KP7); - KEYCODE2(NUMPAD_8, KP8); - KEYCODE2(NUMPAD_9, KP9); - KEYCODE2(NUMPAD_DIVIDE, KPSLASH); - KEYCODE2(NUMPAD_MULTIPLY, KPASTERISK); - KEYCODE2(NUMPAD_SUBTRACT, KPMINUS); - KEYCODE2(NUMPAD_ADD, KPPLUS); - KEYCODE2(NUMPAD_DOT, KPDOT); - KEYCODE2(NUMPAD_COMMA, KPCOMMA); - KEYCODE2(NUMPAD_ENTER, KPENTER); - KEYCODE2(NUMPAD_EQUALS, KPEQUAL); - KEYCODE2(NUMPAD_LEFT_PAREN, KPLEFTPAREN); - KEYCODE2(NUMPAD_RIGHT_PAREN, KPRIGHTPAREN); - KEYCODE1(POWER); - KEYCODE1(CAMERA); - KEYCODE2(VOLUME_MUTE, MUTE); - KEYCODE2(VOLUME_UP, VOLUMEUP); - KEYCODE2(VOLUME_DOWN, VOLUMEDOWN); - KEYCODE1(INFO); - KEYCODE2(CHANNEL_UP, CHANNELUP); - KEYCODE2(CHANNEL_DOWN, CHANNELDOWN); - KEYCODE2(ZOOM_IN, ZOOMIN); - KEYCODE2(ZOOM_OUT, ZOOMOUT); - KEYCODE1(TV); - KEYCODE2(BOOKMARK, BOOKMARKS); - KEYCODE2(PROG_RED, RED); - KEYCODE2(PROG_GREEN, GREEN); - KEYCODE2(PROG_YELLOW, YELLOW); - KEYCODE2(PROG_BLUE, BLUE); - KEYCODE2(CONTACTS, ADDRESSBOOK); - KEYCODE1(CALENDAR); - KEYCODE2(MUSIC, PLAYER); - KEYCODE2(CALCULATOR, CALC); - KEYCODE2(BRIGHTNESS_DOWN, BRIGHTNESSDOWN); - KEYCODE2(BRIGHTNESS_UP, BRIGHTNESSUP); - default: *eventCode = KEY_RESERVED; return 0; - } - *eventCode = KEY_RESERVED; - return 0; -} -#undef KEYCODE1 -#undef KEYCODE2 - -#pragma clang diagnostic pop diff --git a/app/src/main/jni/lorie/backend/android/locale/evdev-keycodes.h b/app/src/main/jni/lorie/backend/android/locale/evdev-keycodes.h deleted file mode 100644 index 951a87942..000000000 --- a/app/src/main/jni/lorie/backend/android/locale/evdev-keycodes.h +++ /dev/null @@ -1,296 +0,0 @@ -#pragma clang diagnostic push -#pragma ide diagnostic ignored "OCUnusedMacroInspection" -#ifndef __EVDEV_KEYCODES_H__ -#define EVDEV___EVDEV_KEYCODES_H__ - -// Added for pc105 compatibility -#define EVDEV_LSGT 94 - -#define EVDEV_TLDE 49 -#define EVDEV_AE01 10 -#define EVDEV_AE02 11 -#define EVDEV_AE03 12 -#define EVDEV_AE04 13 -#define EVDEV_AE05 14 -#define EVDEV_AE06 15 -#define EVDEV_AE07 16 -#define EVDEV_AE08 17 -#define EVDEV_AE09 18 -#define EVDEV_AE10 19 -#define EVDEV_AE11 20 -#define EVDEV_AE12 21 -#define EVDEV_BKSP 22 - -#define EVDEV_TAB 23 -#define EVDEV_AD01 24 -#define EVDEV_AD02 25 -#define EVDEV_AD03 26 -#define EVDEV_AD04 27 -#define EVDEV_AD05 28 -#define EVDEV_AD06 29 -#define EVDEV_AD07 30 -#define EVDEV_AD08 31 -#define EVDEV_AD09 32 -#define EVDEV_AD10 33 -#define EVDEV_AD11 34 -#define EVDEV_AD12 35 -#define EVDEV_BKSL 51 -#define EVDEV_AC12 EVDEV_BKSL -#define EVDEV_RTRN 36 - -#define EVDEV_CAPS 66 -#define EVDEV_AC01 38 -#define EVDEV_AC02 39 -#define EVDEV_AC03 40 -#define EVDEV_AC04 41 -#define EVDEV_AC05 42 -#define EVDEV_AC06 43 -#define EVDEV_AC07 44 -#define EVDEV_AC08 45 -#define EVDEV_AC09 46 -#define EVDEV_AC10 47 -#define EVDEV_AC11 48 - -#define EVDEV_LFSH 50 -#define EVDEV_AB01 52 -#define EVDEV_AB02 53 -#define EVDEV_AB03 54 -#define EVDEV_AB04 55 -#define EVDEV_AB05 56 -#define EVDEV_AB06 57 -#define EVDEV_AB07 58 -#define EVDEV_AB08 59 -#define EVDEV_AB09 60 -#define EVDEV_AB10 61 -#define EVDEV_RTSH 62 - -#define EVDEV_LALT 64 -#define EVDEV_LCTL 37 -#define EVDEV_SPCE 65 -#define EVDEV_RCTL 105 -#define EVDEV_RALT 108 -// Microsoft keyboard extra keys -#define EVDEV_LWIN 133 -#define EVDEV_RWIN 134 -#define EVDEV_COMP 135 -#define EVDEV_MENU EVDEV_COMP - -#define EVDEV_ESC 9 -#define EVDEV_FK01 67 -#define EVDEV_FK02 68 -#define EVDEV_FK03 69 -#define EVDEV_FK04 70 -#define EVDEV_FK05 71 -#define EVDEV_FK06 72 -#define EVDEV_FK07 73 -#define EVDEV_FK08 74 -#define EVDEV_FK09 75 -#define EVDEV_FK10 76 -#define EVDEV_FK11 95 -#define EVDEV_FK12 96 - -#define EVDEV_PRSC 107 -//#define EVDEV_SYRQ 107 -#define EVDEV_SCLK 78 -#define EVDEV_PAUS 127 -//#define EVDEV_BRK 419 - -#define EVDEV_INS 118 -#define EVDEV_HOME 110 -#define EVDEV_PGUP 112 -#define EVDEV_DELE 119 -#define EVDEV_END 115 -#define EVDEV_PGDN 117 - -#define EVDEV_UP 111 -#define EVDEV_LEFT 113 -#define EVDEV_DOWN 116 -#define EVDEV_RGHT 114 - -#define EVDEV_NMLK 77 -#define EVDEV_KPDV 106 -#define EVDEV_KPMU 63 -#define EVDEV_KPSU 82 - -#define EVDEV_KP7 79 -#define EVDEV_KP8 80 -#define EVDEV_KP9 81 -#define EVDEV_KPAD 86 - -#define EVDEV_KP4 83 -#define EVDEV_KP5 84 -#define EVDEV_KP6 85 - -#define EVDEV_KP1 87 -#define EVDEV_KP2 88 -#define EVDEV_KP3 89 -#define EVDEV_KPEN 104 - -#define EVDEV_KP0 90 -#define EVDEV_KPDL 91 -#define EVDEV_KPEQ 125 - -#define EVDEV_FK13 191 -#define EVDEV_FK14 192 -#define EVDEV_FK15 193 -#define EVDEV_FK16 194 -#define EVDEV_FK17 195 -#define EVDEV_FK18 196 -#define EVDEV_FK19 197 -#define EVDEV_FK20 198 -#define EVDEV_FK21 199 -#define EVDEV_FK22 200 -#define EVDEV_FK23 201 -#define EVDEV_FK24 202 - -// Keys that are generated on Japanese keyboards - -//#define EVDEV_HZTG 93 // Hankaku/Zenkakau toggle - not actually used -#define EVDEV_HZTG TLDE -#define EVDEV_HKTG 101 // Hiragana/Katakana toggle -#define EVDEV_AB11 97 // backslash/underscore -#define EVDEV_HENK 100 // Henkan -#define EVDEV_MUHE 102 // Muhenkan -#define EVDEV_AE13 132 // Yen -#define EVDEV_KATA 98 // Katakana -#define EVDEV_HIRA 99 // Hiragana -#define EVDEV_JPCM 103 // KPJPComma -// #define EVDEV_RO 97 // Romaji - -// Keys that are generated on Korean keyboards - -#define EVDEV_HNGL 130 // Hangul Latin toggle -#define EVDEV_HJCV 131 // Hangul to Hanja conversion - - // Solaris compatibility - -#define EVDEV_LMTA EVDEV_LWIN -#define EVDEV_RMTA EVDEV_RWIN -#define EVDEV_MUTE 121 -#define EVDEV_VOL_MINUS 122 -#define EVDEV_VOL_PLUS 123 -#define EVDEV_POWR 124 -#define EVDEV_STOP 136 -#define EVDEV_AGAI 137 -#define EVDEV_PROP 138 -#define EVDEV_UNDO 139 -#define EVDEV_FRNT 140 -#define EVDEV_COPY 141 -#define EVDEV_OPEN 142 -#define EVDEV_PAST 143 -#define EVDEV_FIND 144 -#define EVDEV_CUT 145 -#define EVDEV_HELP 146 - -// Extended keys that may be generated on "Internet" keyboards. -// evdev has standardize names for these. - -#define EVDEV_LNFD 109 // #define EVDEV_KEY_LINEFEED 101 -#define EVDEV_I120 120 // #define EVDEV_KEY_MACRO 112 -#define EVDEV_I126 126 // #define EVDEV_KEY_KPPLUSMINUS 118 -#define EVDEV_I128 128 // #define EVDEV_KEY_SCALE 120 -#define EVDEV_I129 129 // #define EVDEV_KEY_KPCOMMA 121 -#define EVDEV_I147 147 // #define EVDEV_KEY_MENU 139 -#define EVDEV_I148 148 // #define EVDEV_KEY_CALC 140 -#define EVDEV_I149 149 // #define EVDEV_KEY_SETUP 141 -#define EVDEV_I150 150 // #define EVDEV_KEY_SLEEP 142 -#define EVDEV_I151 151 // #define EVDEV_KEY_WAKEUP 143 -#define EVDEV_I152 152 // #define EVDEV_KEY_FILE 144 -#define EVDEV_I153 153 // #define EVDEV_KEY_SENDFILE 145 -#define EVDEV_I154 154 // #define EVDEV_KEY_DELETEFILE 146 -#define EVDEV_I155 155 // #define EVDEV_KEY_XFER 147 -#define EVDEV_I156 156 // #define EVDEV_KEY_PROG1 148 -#define EVDEV_I157 157 // #define EVDEV_KEY_PROG2 149 -#define EVDEV_I158 158 // #define EVDEV_KEY_WWW 150 -#define EVDEV_I159 159 // #define EVDEV_KEY_MSDOS 151 -#define EVDEV_I160 160 // #define EVDEV_KEY_COFFEE 152 -#define EVDEV_I161 161 // #define EVDEV_KEY_DIRECTION 153 -#define EVDEV_I162 162 // #define EVDEV_KEY_CYCLEWINDOWS 154 -#define EVDEV_I163 163 // #define EVDEV_KEY_MAIL 155 -#define EVDEV_I164 164 // #define EVDEV_KEY_BOOKMARKS 156 -#define EVDEV_I165 165 // #define EVDEV_KEY_COMPUTER 157 -#define EVDEV_I166 166 // #define EVDEV_KEY_BACK 158 -#define EVDEV_I167 167 // #define EVDEV_KEY_FORWARD 159 -#define EVDEV_I168 168 // #define EVDEV_KEY_CLOSECD 160 -#define EVDEV_I169 169 // #define EVDEV_KEY_EJECTCD 161 -#define EVDEV_I170 170 // #define EVDEV_KEY_EJECTCLOSECD 162 -#define EVDEV_I171 171 // #define EVDEV_KEY_NEXTSONG 163 -#define EVDEV_I172 172 // #define EVDEV_KEY_PLAYPAUSE 164 -#define EVDEV_I173 173 // #define EVDEV_KEY_PREVIOUSSONG 165 -#define EVDEV_I174 174 // #define EVDEV_KEY_STOPCD 166 -#define EVDEV_I175 175 // #define EVDEV_KEY_RECORD 167 -#define EVDEV_I176 176 // #define EVDEV_KEY_REWIND 168 -#define EVDEV_I177 177 // #define EVDEV_KEY_PHONE 169 -#define EVDEV_I178 178 // #define EVDEV_KEY_ISO 170 -#define EVDEV_I179 179 // #define EVDEV_KEY_CONFIG 171 -#define EVDEV_I180 180 // #define EVDEV_KEY_HOMEPAGE 172 -#define EVDEV_I181 181 // #define EVDEV_KEY_REFRESH 173 -#define EVDEV_I182 182 // #define EVDEV_KEY_EXIT 174 -#define EVDEV_I183 183 // #define EVDEV_KEY_MOVE 175 -#define EVDEV_I184 184 // #define EVDEV_KEY_EDIT 176 -#define EVDEV_I185 185 // #define EVDEV_KEY_SCROLLUP 177 -#define EVDEV_I186 186 // #define EVDEV_KEY_SCROLLDOWN 178 -#define EVDEV_I187 187 // #define EVDEV_KEY_KPLEFTPAREN 179 -#define EVDEV_I188 188 // #define EVDEV_KEY_KPRIGHTPAREN 180 -#define EVDEV_I189 189 // #define EVDEV_KEY_NEW 181 -#define EVDEV_I190 190 // #define EVDEV_KEY_REDO 182 -#define EVDEV_I208 208 // #define EVDEV_KEY_PLAYCD 200 -#define EVDEV_I209 209 // #define EVDEV_KEY_PAUSECD 201 -#define EVDEV_I210 210 // #define EVDEV_KEY_PROG3 202 -#define EVDEV_I211 211 // #define EVDEV_KEY_PROG4 203 conflicts with AB11 -#define EVDEV_I212 212 // #define EVDEV_KEY_DASHBOARD 204 -#define EVDEV_I213 213 // #define EVDEV_KEY_SUSPEND 205 -#define EVDEV_I214 214 // #define EVDEV_KEY_CLOSE 206 -#define EVDEV_I215 215 // #define EVDEV_KEY_PLAY 207 -#define EVDEV_I216 216 // #define EVDEV_KEY_FASTFORWARD 208 -#define EVDEV_I217 217 // #define EVDEV_KEY_BASSBOOST 209 -#define EVDEV_I218 218 // #define EVDEV_KEY_PRINT 210 -#define EVDEV_I219 219 // #define EVDEV_KEY_HP 211 -#define EVDEV_I220 220 // #define EVDEV_KEY_CAMERA 212 -#define EVDEV_I221 221 // #define EVDEV_KEY_SOUND 213 -#define EVDEV_I222 222 // #define EVDEV_KEY_QUESTION 214 -#define EVDEV_I223 223 // #define EVDEV_KEY_EMAIL 215 -#define EVDEV_I224 224 // #define EVDEV_KEY_CHAT 216 -#define EVDEV_I225 225 // #define EVDEV_KEY_SEARCH 217 -#define EVDEV_I226 226 // #define EVDEV_KEY_CONNECT 218 -#define EVDEV_I227 227 // #define EVDEV_KEY_FINANCE 219 -#define EVDEV_I228 228 // #define EVDEV_KEY_SPORT 220 -#define EVDEV_I229 229 // #define EVDEV_KEY_SHOP 221 -#define EVDEV_I230 230 // #define EVDEV_KEY_ALTERASE 222 -#define EVDEV_I231 231 // #define EVDEV_KEY_CANCEL 223 -#define EVDEV_I232 232 // #define EVDEV_KEY_BRIGHTNESSDOWN 224 -#define EVDEV_I233 233 // #define EVDEV_KEY_BRIGHTNESSUP 225 -#define EVDEV_I234 234 // #define EVDEV_KEY_MEDIA 226 -#define EVDEV_I235 235 // #define EVDEV_KEY_SWITCHVIDEOMODE 227 -#define EVDEV_I236 236 // #define EVDEV_KEY_KBDILLUMTOGGLE 228 -#define EVDEV_I237 237 // #define EVDEV_KEY_KBDILLUMDOWN 229 -#define EVDEV_I238 238 // #define EVDEV_KEY_KBDILLUMUP 230 -#define EVDEV_I239 239 // #define EVDEV_KEY_SEND 231 -#define EVDEV_I240 240 // #define EVDEV_KEY_REPLY 232 -#define EVDEV_I241 241 // #define EVDEV_KEY_FORWARDMAIL 233 -#define EVDEV_I242 242 // #define EVDEV_KEY_SAVE 234 -#define EVDEV_I243 243 // #define EVDEV_KEY_DOCUMENTS 235 -#define EVDEV_I244 244 // #define EVDEV_KEY_BATTERY 236 -#define EVDEV_I245 245 // #define EVDEV_KEY_BLUETOOTH 237 -#define EVDEV_I246 246 // #define EVDEV_KEY_WLAN 238 -#define EVDEV_I247 247 // #define EVDEV_KEY_UWB 239 -#define EVDEV_I248 248 // #define EVDEV_KEY_UNKNOWN 240 -#define EVDEV_I249 249 // #define EVDEV_KEY_VIDEO_NEXT 241 -#define EVDEV_I250 250 // #define EVDEV_KEY_VIDEO_PREV 242 -#define EVDEV_I251 251 // #define EVDEV_KEY_BRIGHTNESS_CYCLE 243 -#define EVDEV_I252 252 // #define EVDEV_KEY_BRIGHTNESS_ZERO 244 -#define EVDEV_I253 253 // #define EVDEV_KEY_DISPLAY_OFF 245 -#define EVDEV_I254 254 // #define EVDEV_KEY_WWAN 246 -#define EVDEV_I255 255 // #define EVDEV_KEY_RFKILL 247 - -// Fake keycodes for virtual keys -#define EVDEV_LVL3 92 -#define EVDEV_MDSW 203 -#define EVDEV_ALT 204 -#define EVDEV_META 205 -#define EVDEV_SUPR 206 -#define EVDEV_HYPR 207 - -#endif // __EVDEV_KEYCODES_H__ - -#pragma clang diagnostic pop diff --git a/app/src/main/jni/lorie/backend/android/locale/generator b/app/src/main/jni/lorie/backend/android/locale/generator deleted file mode 100755 index d0274f7997d3dcbc711e23689232eae1ccb94c25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29168 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include "evdev-keycodes.h" -#include "log.h" - -#include "android-keycodes.h" - -#define KEYCODE_MIN 8 -#define KEYCODE_MAX 255 -#define EVDEV_OFFSET 8 -#define SYM_LENGTH 7 - -int android_keycode_to_linux_event_code(int keyCode, int *eventCode, int *shift); - -struct keysym { - int eventCode; - char normal[SYM_LENGTH]; - char shift[SYM_LENGTH]; -}; - -#define KEYMAPS_LENGTH 1 -struct keymap { - char *name; - struct keysym keysyms[KEYCODE_MAX - KEYCODE_MIN]; -} keymaps[KEYMAPS_LENGTH] = {0}; - -void handler(int sig) { - void *array[10]; - size_t size; - - // get void*'s for all entries on the stack - size = backtrace(array, 10); - - // print out all the frames to stderr - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); - exit(1); -} - -#define K(c1, c2) case EVDEV_##c1: return KEY_##c2 -int keyCode2eventCode(int kc) { - int keyCode = kc - KEYCODE_MIN; - switch(keyCode) { - K(TLDE, GRAVE); - - K(AE01, 1); - K(AE02, 2); - K(AE03, 3); - K(AE04, 4); - K(AE05, 5); - K(AE06, 6); - K(AE07, 7); - K(AE08, 8); - K(AE09, 9); - K(AE10, 0); - K(AE11, MINUS); - K(AE12, EQUAL); - K(BKSP, BACKSPACE); - - K(TAB, TAB); - K(AD01, Q); - K(AD02, W); - K(AD03, E); - K(AD04, R); - K(AD05, T); - K(AD06, Y); - K(AD07, U); - K(AD08, I); - K(AD09, O); - K(AD10, P); - K(AD11, LEFTBRACE); - K(AD12, RIGHTBRACE); - //K(BKSL, BACKSLASH); // DUPLICATE - K(RTRN, ENTER); - - K(CAPS, CAPSLOCK); - K(AC01, A); - K(AC02, S); - K(AC03, D); - K(AC04, F); - K(AC05, G); - K(AC06, H); - K(AC07, J); - K(AC08, K); - K(AC09, L); - K(AC10, SEMICOLON); - K(AC11, APOSTROPHE); - K(AC12, BACKSLASH); - - K(LFSH, LEFTSHIFT); - K(AB01, Z); - K(AB02, X); - K(AB03, C); - K(AB04, V); - K(AB05, B); - K(AB06, N); - K(AB07, M); - K(AB08, COMMA); - K(AB09, DOT); - K(AB10, SLASH); - K(RTSH, RIGHTSHIFT); - default: return KEY_UNKNOWN; - } - return KEY_UNKNOWN; -} - -struct iterator_args { - struct keymap *keymap; - uint8_t symcase; -}; -void iterate_normal(struct xkb_keymap *xkb_keymap, xkb_keycode_t key, void *data) { - struct iterator_args *ita = data; - if (!ita || !ita->keymap) return; - - struct keymap *keymap = ita->keymap; - struct xkb_state *xkb_state = NULL; - char *buf = NULL; - xkb_keysym_t ks = 0; - - xkb_state = xkb_state_new(xkb_keymap); - if (xkb_state == NULL) { - LOGE("failed to create xkb_state"); - }; - - if (ita->symcase) { - xkb_state_update_key(xkb_state, KEY_LEFTSHIFT + EVDEV_OFFSET, XKB_KEY_DOWN); - buf = keymap->keysyms[key].shift; - } else { - buf = keymap->keysyms[key].normal; - } - - ks = xkb_state_key_get_one_sym(xkb_state, key); - xkb_keysym_to_utf8(ks, buf, SYM_LENGTH); - - xkb_state_unref(xkb_state); -} - -struct keymap* parse_keymap(struct xkb_context *ctx, char *name) { - if (!ctx || !name) return NULL; - - struct xkb_rule_names xkb_names = {0}; - struct xkb_keymap *xkb_keymap = NULL; - struct iterator_args ita = {0}; - struct keymap *result = NULL; - - xkb_names.rules = strdup("evdev"); - xkb_names.model = strdup("pc105"); - xkb_names.layout = strdup(name); - - xkb_keymap = xkb_keymap_new_from_names(ctx, &xkb_names, 0); - if (xkb_keymap == NULL) { - LOGE("failed to compile global XKB keymap\n"); - LOGE(" tried rules %s, model %s, layout %s, variant %s, " - "options %s\n", - xkb_names.rules, xkb_names.model, - xkb_names.layout, xkb_names.variant, - xkb_names.options); - goto end; - } - - result = calloc(1, sizeof(struct keymap)); - if (!result) { - LOGE("failed to allocate struct keymap"); - goto end; - } - - result->name = strdup(name); - - ita.keymap = result; - ita.symcase = 0; - xkb_keymap_key_for_each(xkb_keymap, iterate_normal, &ita); - - ita.symcase = 1; - xkb_keymap_key_for_each(xkb_keymap, iterate_normal, &ita); - - for (int i=0; i<(KEYCODE_MAX-KEYCODE_MIN);i++) - result->keysyms[i].eventCode = keyCode2eventCode(i+KEYCODE_MIN); - -end: - if (xkb_keymap) xkb_keymap_unref(xkb_keymap); - if (xkb_names.rules) free((void*)xkb_names.rules); - if (xkb_names.model) free((void*)xkb_names.model); - if (xkb_names.layout) free((void*)xkb_names.layout); - return result; -} - -void print_header(void) { - printf( "#include \n" - "#define SYM_LENGTH %d\n" - "#define KEYCODE_MIN 8\n" - "#define KEYCODE_MAX 255\n" - "#define NOSYM {{0}, {0}}\n" - "struct lorie_keymap {\n" - "\tchar *name;\n" - "\tstruct keysym {\n" - "\t\tchar normal[SYM_LENGTH];\n" - "\t\tchar shift[SYM_LENGTH];\n" - "\t} keysyms[KEYCODE_MAX - KEYCODE_MIN];\n" - "};\n\n", - SYM_LENGTH - ); -} - -void print_sym(char *sym) { - int i; - if (strlen(sym)) { - printf("{"); - for(i=0; i 0 && i < SYM_LENGTH) printf(", "); - printf("%d", (inormal) && strlen(keysym->shift)) { - printf("\t\t{"); - print_sym(keysym->normal); - printf(", "); - print_sym(keysym->shift); - printf("}"); - } else printf("\t\tNOSYM"); -} - -int ks_displayable(char *sym) { - if (strlen(sym) == 0) return 0; - if (strlen(sym) == 1) { - switch(sym[0]) { - case 8: - case 10: - case 13: - case 27: - case 30: - case 127: - return 0; - } - } - return 1; -} - -int find_keysym_by_eventcode(struct keymap* keymap, int eventCode) { - for (int i=0; i<(KEYCODE_MAX-KEYCODE_MIN); i++) { - if (keymap->keysyms[i].eventCode == eventCode) - return i; - } - return -1; -} - -void keymap_print(struct keymap* keymap) { - if (!keymap) return; - int i, j, latestEventCode; - - printf( "struct lorie_keymap lorie_keymap_%s = {\n" - "\t.name = (char*) \"%s\",\n" - "\t.keysyms = {\n", - keymap->name, keymap->name - ); - #if 0 - for (i=0; i<(KEYCODE_MAX-KEYCODE_MIN); i++) { - print_keysym(&keymap->keysyms[i]); - if (i!=(KEYCODE_MAX-KEYCODE_MIN-1)) printf(","); - - printf(" // keyCode: %d; eventCode: %d;", i + KEYCODE_MIN, keymap->keysyms[i].eventCode); - if (ks_displayable(i, keymap->keysyms[i].normal)) printf(" normal: \"%s\";", keymap->keysyms[i].normal); - if (ks_displayable(i, keymap->keysyms[i].shift)) printf(" shift: \"%s\";", keymap->keysyms[i].shift); - printf("\n"); - } - #else - latestEventCode = 0; - for (i=0; i<(KEYCODE_MAX-KEYCODE_MIN); i++) { - if (keymap->keysyms[i].eventCode > latestEventCode && - keymap->keysyms[i].eventCode != KEY_UNKNOWN && - strlen(keymap->keysyms[i].normal) && - strlen(keymap->keysyms[i].shift)) // check if it is displayable and known - latestEventCode = keymap->keysyms[i].eventCode; - } - //printf("\n\n\nLatest eventCode = %d\n\n\n\n", latestEventCode); - for (i=0; i<=latestEventCode; i++) { - j = find_keysym_by_eventcode(keymap, i); - struct keysym* k = &keymap->keysyms[j]; - if (j >= 0) { - print_keysym(k); - if (j!=(KEYCODE_MAX-KEYCODE_MIN-1)) printf(","); - - printf(" // eventCode: %d;", i); - if (ks_displayable(k->normal)) printf(" normal: \"%s\";", k->normal); - if (ks_displayable(k->shift)) printf(" shift: \"%s\";", k->shift); - printf("\n"); - } else printf("\t\tNOSYM, // eventCode: %d\n", i); - } - #endif - printf("\t}\n};\n"); -} - -int main(void) { - signal(SIGSEGV, handler); - int i; - struct xkb_context *xkb_context = NULL; - struct xkb_rule_names xkb_names = {0}; - struct xkb_keymap *xkb_keymap = NULL; - struct keymap* map = NULL; - //char *keymaps[] = { "us", NULL }; - //char *keymaps[] = { "ru", "il", NULL }; - char *keymaps[] = { "ru", NULL }; - - if (xkb_context == NULL) { - xkb_context = xkb_context_new(0); - if (xkb_context == NULL) { - LOGE("failed to create XKB context\n"); - return 1; - } - } - print_header(); - for (i=0; keymaps[i]; i++) { - map = parse_keymap(xkb_context, keymaps[i]); - if (!map) { - LOGE("failed to parse keymap \"us\""); - return 1; - } - keymap_print(map); - } - - printf("\nstruct lorie_keymap *lorie_keymaps[] = {"); - for (i=0; keymaps[i]; i++) { - printf("&lorie_keymap_%s, ", keymaps[i]); - } - printf("NULL};\n\n"); - printf("struct lorie_keymap_android {\n"); - printf("\tint eventCode;\n"); - printf("\tint shift;\n"); - printf("} lorie_keymap_android[] = {\n"); - - int lastWasNull = 0; - for (i=0; i<=ANDROID_KEYCODE_LAST; i++) { - int evCode = 0; - int shift = 0; - if (android_keycode_to_linux_event_code(i, &evCode, &shift)) { - if (lastWasNull) printf("\n"); - printf("\t{%d, %d}%s // keycode %d\n", evCode, shift, (i!=ANDROID_KEYCODE_LAST)?",":"", i); - lastWasNull = 0; - } else { - if (!lastWasNull) printf("\t"); - printf("{0, 0}%s", (i!=ANDROID_KEYCODE_LAST)?", ":"\n"); - lastWasNull = 1; - } - }; - printf("};\n"); -} diff --git a/app/src/main/jni/lorie/backend/android/locale/input-event-codes.h b/app/src/main/jni/lorie/backend/android/locale/input-event-codes.h deleted file mode 100644 index a9902a7c2..000000000 --- a/app/src/main/jni/lorie/backend/android/locale/input-event-codes.h +++ /dev/null @@ -1,861 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * Input event codes - * - * *** IMPORTANT *** - * This file is not only included from C-code but also from devicetree source - * files. As such this file MUST only contain comments and defines. - * - * Copyright (c) 1999-2002 Vojtech Pavlik - * Copyright (c) 2015 Hans de Goede - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - */ -#ifndef _INPUT_EVENT_CODES_H -#define _INPUT_EVENT_CODES_H - -/* - * Device properties and quirks - */ - -#define INPUT_PROP_POINTER 0x00 /* needs a pointer */ -#define INPUT_PROP_DIRECT 0x01 /* direct input devices */ -#define INPUT_PROP_BUTTONPAD 0x02 /* has button(s) under pad */ -#define INPUT_PROP_SEMI_MT 0x03 /* touch rectangle only */ -#define INPUT_PROP_TOPBUTTONPAD 0x04 /* softbuttons at top of pad */ -#define INPUT_PROP_POINTING_STICK 0x05 /* is a pointing stick */ -#define INPUT_PROP_ACCELEROMETER 0x06 /* has accelerometer */ - -#define INPUT_PROP_MAX 0x1f -#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1) - -/* - * Event types - */ - -#define EV_SYN 0x00 -#define EV_KEY 0x01 -#define EV_REL 0x02 -#define EV_ABS 0x03 -#define EV_MSC 0x04 -#define EV_SW 0x05 -#define EV_LED 0x11 -#define EV_SND 0x12 -#define EV_REP 0x14 -#define EV_FF 0x15 -#define EV_PWR 0x16 -#define EV_FF_STATUS 0x17 -#define EV_MAX 0x1f -#define EV_CNT (EV_MAX+1) - -/* - * Synchronization events. - */ - -#define SYN_REPORT 0 -#define SYN_CONFIG 1 -#define SYN_MT_REPORT 2 -#define SYN_DROPPED 3 -#define SYN_MAX 0xf -#define SYN_CNT (SYN_MAX+1) - -/* - * Keys and buttons - * - * Most of the keys/buttons are modeled after USB HUT 1.12 - * (see http://www.usb.org/developers/hidpage). - * Abbreviations in the comments: - * AC - Application Control - * AL - Application Launch Button - * SC - System Control - */ - -#define KEY_RESERVED 0 -#define KEY_ESC 1 -#define KEY_1 2 -#define KEY_2 3 -#define KEY_3 4 -#define KEY_4 5 -#define KEY_5 6 -#define KEY_6 7 -#define KEY_7 8 -#define KEY_8 9 -#define KEY_9 10 -#define KEY_0 11 -#define KEY_MINUS 12 -#define KEY_EQUAL 13 -#define KEY_BACKSPACE 14 -#define KEY_TAB 15 -#define KEY_Q 16 -#define KEY_W 17 -#define KEY_E 18 -#define KEY_R 19 -#define KEY_T 20 -#define KEY_Y 21 -#define KEY_U 22 -#define KEY_I 23 -#define KEY_O 24 -#define KEY_P 25 -#define KEY_LEFTBRACE 26 -#define KEY_RIGHTBRACE 27 -#define KEY_ENTER 28 -#define KEY_LEFTCTRL 29 -#define KEY_A 30 -#define KEY_S 31 -#define KEY_D 32 -#define KEY_F 33 -#define KEY_G 34 -#define KEY_H 35 -#define KEY_J 36 -#define KEY_K 37 -#define KEY_L 38 -#define KEY_SEMICOLON 39 -#define KEY_APOSTROPHE 40 -#define KEY_GRAVE 41 -#define KEY_LEFTSHIFT 42 -#define KEY_BACKSLASH 43 -#define KEY_Z 44 -#define KEY_X 45 -#define KEY_C 46 -#define KEY_V 47 -#define KEY_B 48 -#define KEY_N 49 -#define KEY_M 50 -#define KEY_COMMA 51 -#define KEY_DOT 52 -#define KEY_SLASH 53 -#define KEY_RIGHTSHIFT 54 -#define KEY_KPASTERISK 55 -#define KEY_LEFTALT 56 -#define KEY_SPACE 57 -#define KEY_CAPSLOCK 58 -#define KEY_F1 59 -#define KEY_F2 60 -#define KEY_F3 61 -#define KEY_F4 62 -#define KEY_F5 63 -#define KEY_F6 64 -#define KEY_F7 65 -#define KEY_F8 66 -#define KEY_F9 67 -#define KEY_F10 68 -#define KEY_NUMLOCK 69 -#define KEY_SCROLLLOCK 70 -#define KEY_KP7 71 -#define KEY_KP8 72 -#define KEY_KP9 73 -#define KEY_KPMINUS 74 -#define KEY_KP4 75 -#define KEY_KP5 76 -#define KEY_KP6 77 -#define KEY_KPPLUS 78 -#define KEY_KP1 79 -#define KEY_KP2 80 -#define KEY_KP3 81 -#define KEY_KP0 82 -#define KEY_KPDOT 83 - -#define KEY_ZENKAKUHANKAKU 85 -#define KEY_102ND 86 -#define KEY_F11 87 -#define KEY_F12 88 -#define KEY_RO 89 -#define KEY_KATAKANA 90 -#define KEY_HIRAGANA 91 -#define KEY_HENKAN 92 -#define KEY_KATAKANAHIRAGANA 93 -#define KEY_MUHENKAN 94 -#define KEY_KPJPCOMMA 95 -#define KEY_KPENTER 96 -#define KEY_RIGHTCTRL 97 -#define KEY_KPSLASH 98 -#define KEY_SYSRQ 99 -#define KEY_RIGHTALT 100 -#define KEY_LINEFEED 101 -#define KEY_HOME 102 -#define KEY_UP 103 -#define KEY_PAGEUP 104 -#define KEY_LEFT 105 -#define KEY_RIGHT 106 -#define KEY_END 107 -#define KEY_DOWN 108 -#define KEY_PAGEDOWN 109 -#define KEY_INSERT 110 -#define KEY_DELETE 111 -#define KEY_MACRO 112 -#define KEY_MUTE 113 -#define KEY_VOLUMEDOWN 114 -#define KEY_VOLUMEUP 115 -#define KEY_POWER 116 /* SC System Power Down */ -#define KEY_KPEQUAL 117 -#define KEY_KPPLUSMINUS 118 -#define KEY_PAUSE 119 -#define KEY_SCALE 120 /* AL Compiz Scale (Expose) */ - -#define KEY_KPCOMMA 121 -#define KEY_HANGEUL 122 -#define KEY_HANGUEL KEY_HANGEUL -#define KEY_HANJA 123 -#define KEY_YEN 124 -#define KEY_LEFTMETA 125 -#define KEY_RIGHTMETA 126 -#define KEY_COMPOSE 127 - -#define KEY_STOP 128 /* AC Stop */ -#define KEY_AGAIN 129 -#define KEY_PROPS 130 /* AC Properties */ -#define KEY_UNDO 131 /* AC Undo */ -#define KEY_FRONT 132 -#define KEY_COPY 133 /* AC Copy */ -#define KEY_OPEN 134 /* AC Open */ -#define KEY_PASTE 135 /* AC Paste */ -#define KEY_FIND 136 /* AC Search */ -#define KEY_CUT 137 /* AC Cut */ -#define KEY_HELP 138 /* AL Integrated Help Center */ -#define KEY_MENU 139 /* Menu (show menu) */ -#define KEY_CALC 140 /* AL Calculator */ -#define KEY_SETUP 141 -#define KEY_SLEEP 142 /* SC System Sleep */ -#define KEY_WAKEUP 143 /* System Wake Up */ -#define KEY_FILE 144 /* AL Local Machine Browser */ -#define KEY_SENDFILE 145 -#define KEY_DELETEFILE 146 -#define KEY_XFER 147 -#define KEY_PROG1 148 -#define KEY_PROG2 149 -#define KEY_WWW 150 /* AL Internet Browser */ -#define KEY_MSDOS 151 -#define KEY_COFFEE 152 /* AL Terminal Lock/Screensaver */ -#define KEY_SCREENLOCK KEY_COFFEE -#define KEY_ROTATE_DISPLAY 153 /* Display orientation for e.g. tablets */ -#define KEY_DIRECTION KEY_ROTATE_DISPLAY -#define KEY_CYCLEWINDOWS 154 -#define KEY_MAIL 155 -#define KEY_BOOKMARKS 156 /* AC Bookmarks */ -#define KEY_COMPUTER 157 -#define KEY_BACK 158 /* AC Back */ -#define KEY_FORWARD 159 /* AC Forward */ -#define KEY_CLOSECD 160 -#define KEY_EJECTCD 161 -#define KEY_EJECTCLOSECD 162 -#define KEY_NEXTSONG 163 -#define KEY_PLAYPAUSE 164 -#define KEY_PREVIOUSSONG 165 -#define KEY_STOPCD 166 -#define KEY_RECORD 167 -#define KEY_REWIND 168 -#define KEY_PHONE 169 /* Media Select Telephone */ -#define KEY_ISO 170 -#define KEY_CONFIG 171 /* AL Consumer Control Configuration */ -#define KEY_HOMEPAGE 172 /* AC Home */ -#define KEY_REFRESH 173 /* AC Refresh */ -#define KEY_EXIT 174 /* AC Exit */ -#define KEY_MOVE 175 -#define KEY_EDIT 176 -#define KEY_SCROLLUP 177 -#define KEY_SCROLLDOWN 178 -#define KEY_KPLEFTPAREN 179 -#define KEY_KPRIGHTPAREN 180 -#define KEY_NEW 181 /* AC New */ -#define KEY_REDO 182 /* AC Redo/Repeat */ - -#define KEY_F13 183 -#define KEY_F14 184 -#define KEY_F15 185 -#define KEY_F16 186 -#define KEY_F17 187 -#define KEY_F18 188 -#define KEY_F19 189 -#define KEY_F20 190 -#define KEY_F21 191 -#define KEY_F22 192 -#define KEY_F23 193 -#define KEY_F24 194 - -#define KEY_PLAYCD 200 -#define KEY_PAUSECD 201 -#define KEY_PROG3 202 -#define KEY_PROG4 203 -#define KEY_DASHBOARD 204 /* AL Dashboard */ -#define KEY_SUSPEND 205 -#define KEY_CLOSE 206 /* AC Close */ -#define KEY_PLAY 207 -#define KEY_FASTFORWARD 208 -#define KEY_BASSBOOST 209 -#define KEY_PRINT 210 /* AC Print */ -#define KEY_HP 211 -#define KEY_CAMERA 212 -#define KEY_SOUND 213 -#define KEY_QUESTION 214 -#define KEY_EMAIL 215 -#define KEY_CHAT 216 -#define KEY_SEARCH 217 -#define KEY_CONNECT 218 -#define KEY_FINANCE 219 /* AL Checkbook/Finance */ -#define KEY_SPORT 220 -#define KEY_SHOP 221 -#define KEY_ALTERASE 222 -#define KEY_CANCEL 223 /* AC Cancel */ -#define KEY_BRIGHTNESSDOWN 224 -#define KEY_BRIGHTNESSUP 225 -#define KEY_MEDIA 226 - -#define KEY_SWITCHVIDEOMODE 227 /* Cycle between available video - outputs (Monitor/LCD/TV-out/etc) */ -#define KEY_KBDILLUMTOGGLE 228 -#define KEY_KBDILLUMDOWN 229 -#define KEY_KBDILLUMUP 230 - -#define KEY_SEND 231 /* AC Send */ -#define KEY_REPLY 232 /* AC Reply */ -#define KEY_FORWARDMAIL 233 /* AC Forward Msg */ -#define KEY_SAVE 234 /* AC Save */ -#define KEY_DOCUMENTS 235 - -#define KEY_BATTERY 236 - -#define KEY_BLUETOOTH 237 -#define KEY_WLAN 238 -#define KEY_UWB 239 - -#define KEY_UNKNOWN 240 - -#define KEY_VIDEO_NEXT 241 /* drive next video source */ -#define KEY_VIDEO_PREV 242 /* drive previous video source */ -#define KEY_BRIGHTNESS_CYCLE 243 /* brightness up, after max is min */ -#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manual - brightness control is off, - rely on ambient */ -#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO -#define KEY_DISPLAY_OFF 245 /* display device to off state */ - -#define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */ -#define KEY_WIMAX KEY_WWAN -#define KEY_RFKILL 247 /* Key that controls all radios */ - -#define KEY_MICMUTE 248 /* Mute / unmute the microphone */ - -/* Code 255 is reserved for special needs of AT keyboard driver */ - -#define BTN_MISC 0x100 -#define BTN_0 0x100 -#define BTN_1 0x101 -#define BTN_2 0x102 -#define BTN_3 0x103 -#define BTN_4 0x104 -#define BTN_5 0x105 -#define BTN_6 0x106 -#define BTN_7 0x107 -#define BTN_8 0x108 -#define BTN_9 0x109 - -#define BTN_MOUSE 0x110 -#define BTN_LEFT 0x110 -#define BTN_RIGHT 0x111 -#define BTN_MIDDLE 0x112 -#define BTN_SIDE 0x113 -#define BTN_EXTRA 0x114 -#define BTN_FORWARD 0x115 -#define BTN_BACK 0x116 -#define BTN_TASK 0x117 - -#define BTN_JOYSTICK 0x120 -#define BTN_TRIGGER 0x120 -#define BTN_THUMB 0x121 -#define BTN_THUMB2 0x122 -#define BTN_TOP 0x123 -#define BTN_TOP2 0x124 -#define BTN_PINKIE 0x125 -#define BTN_BASE 0x126 -#define BTN_BASE2 0x127 -#define BTN_BASE3 0x128 -#define BTN_BASE4 0x129 -#define BTN_BASE5 0x12a -#define BTN_BASE6 0x12b -#define BTN_DEAD 0x12f - -#define BTN_GAMEPAD 0x130 -#define BTN_SOUTH 0x130 -#define BTN_A BTN_SOUTH -#define BTN_EAST 0x131 -#define BTN_B BTN_EAST -#define BTN_C 0x132 -#define BTN_NORTH 0x133 -#define BTN_X BTN_NORTH -#define BTN_WEST 0x134 -#define BTN_Y BTN_WEST -#define BTN_Z 0x135 -#define BTN_TL 0x136 -#define BTN_TR 0x137 -#define BTN_TL2 0x138 -#define BTN_TR2 0x139 -#define BTN_SELECT 0x13a -#define BTN_START 0x13b -#define BTN_MODE 0x13c -#define BTN_THUMBL 0x13d -#define BTN_THUMBR 0x13e - -#define BTN_DIGI 0x140 -#define BTN_TOOL_PEN 0x140 -#define BTN_TOOL_RUBBER 0x141 -#define BTN_TOOL_BRUSH 0x142 -#define BTN_TOOL_PENCIL 0x143 -#define BTN_TOOL_AIRBRUSH 0x144 -#define BTN_TOOL_FINGER 0x145 -#define BTN_TOOL_MOUSE 0x146 -#define BTN_TOOL_LENS 0x147 -#define BTN_TOOL_QUINTTAP 0x148 /* Five fingers on trackpad */ -#define BTN_STYLUS3 0x149 -#define BTN_TOUCH 0x14a -#define BTN_STYLUS 0x14b -#define BTN_STYLUS2 0x14c -#define BTN_TOOL_DOUBLETAP 0x14d -#define BTN_TOOL_TRIPLETAP 0x14e -#define BTN_TOOL_QUADTAP 0x14f /* Four fingers on trackpad */ - -#define BTN_WHEEL 0x150 -#define BTN_GEAR_DOWN 0x150 -#define BTN_GEAR_UP 0x151 - -#define KEY_OK 0x160 -#define KEY_SELECT 0x161 -#define KEY_GOTO 0x162 -#define KEY_CLEAR 0x163 -#define KEY_POWER2 0x164 -#define KEY_OPTION 0x165 -#define KEY_INFO 0x166 /* AL OEM Features/Tips/Tutorial */ -#define KEY_TIME 0x167 -#define KEY_VENDOR 0x168 -#define KEY_ARCHIVE 0x169 -#define KEY_PROGRAM 0x16a /* Media Select Program Guide */ -#define KEY_CHANNEL 0x16b -#define KEY_FAVORITES 0x16c -#define KEY_EPG 0x16d -#define KEY_PVR 0x16e /* Media Select Home */ -#define KEY_MHP 0x16f -#define KEY_LANGUAGE 0x170 -#define KEY_TITLE 0x171 -#define KEY_SUBTITLE 0x172 -#define KEY_ANGLE 0x173 -#define KEY_ZOOM 0x174 -#define KEY_MODE 0x175 -#define KEY_KEYBOARD 0x176 -#define KEY_SCREEN 0x177 -#define KEY_PC 0x178 /* Media Select Computer */ -#define KEY_TV 0x179 /* Media Select TV */ -#define KEY_TV2 0x17a /* Media Select Cable */ -#define KEY_VCR 0x17b /* Media Select VCR */ -#define KEY_VCR2 0x17c /* VCR Plus */ -#define KEY_SAT 0x17d /* Media Select Satellite */ -#define KEY_SAT2 0x17e -#define KEY_CD 0x17f /* Media Select CD */ -#define KEY_TAPE 0x180 /* Media Select Tape */ -#define KEY_RADIO 0x181 -#define KEY_TUNER 0x182 /* Media Select Tuner */ -#define KEY_PLAYER 0x183 -#define KEY_TEXT 0x184 -#define KEY_DVD 0x185 /* Media Select DVD */ -#define KEY_AUX 0x186 -#define KEY_MP3 0x187 -#define KEY_AUDIO 0x188 /* AL Audio Browser */ -#define KEY_VIDEO 0x189 /* AL Movie Browser */ -#define KEY_DIRECTORY 0x18a -#define KEY_LIST 0x18b -#define KEY_MEMO 0x18c /* Media Select Messages */ -#define KEY_CALENDAR 0x18d -#define KEY_RED 0x18e -#define KEY_GREEN 0x18f -#define KEY_YELLOW 0x190 -#define KEY_BLUE 0x191 -#define KEY_CHANNELUP 0x192 /* Channel Increment */ -#define KEY_CHANNELDOWN 0x193 /* Channel Decrement */ -#define KEY_FIRST 0x194 -#define KEY_LAST 0x195 /* Recall Last */ -#define KEY_AB 0x196 -#define KEY_NEXT 0x197 -#define KEY_RESTART 0x198 -#define KEY_SLOW 0x199 -#define KEY_SHUFFLE 0x19a -#define KEY_BREAK 0x19b -#define KEY_PREVIOUS 0x19c -#define KEY_DIGITS 0x19d -#define KEY_TEEN 0x19e -#define KEY_TWEN 0x19f -#define KEY_VIDEOPHONE 0x1a0 /* Media Select Video Phone */ -#define KEY_GAMES 0x1a1 /* Media Select Games */ -#define KEY_ZOOMIN 0x1a2 /* AC Zoom In */ -#define KEY_ZOOMOUT 0x1a3 /* AC Zoom Out */ -#define KEY_ZOOMRESET 0x1a4 /* AC Zoom */ -#define KEY_WORDPROCESSOR 0x1a5 /* AL Word Processor */ -#define KEY_EDITOR 0x1a6 /* AL Text Editor */ -#define KEY_SPREADSHEET 0x1a7 /* AL Spreadsheet */ -#define KEY_GRAPHICSEDITOR 0x1a8 /* AL Graphics Editor */ -#define KEY_PRESENTATION 0x1a9 /* AL Presentation App */ -#define KEY_DATABASE 0x1aa /* AL Database App */ -#define KEY_NEWS 0x1ab /* AL Newsreader */ -#define KEY_VOICEMAIL 0x1ac /* AL Voicemail */ -#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */ -#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */ -#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */ -#define KEY_BRIGHTNESS_TOGGLE KEY_DISPLAYTOGGLE -#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */ -#define KEY_LOGOFF 0x1b1 /* AL Logoff */ - -#define KEY_DOLLAR 0x1b2 -#define KEY_EURO 0x1b3 - -#define KEY_FRAMEBACK 0x1b4 /* Consumer - transport controls */ -#define KEY_FRAMEFORWARD 0x1b5 -#define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */ -#define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */ -#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */ -#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */ -#define KEY_IMAGES 0x1ba /* AL Image Browser */ - -#define KEY_DEL_EOL 0x1c0 -#define KEY_DEL_EOS 0x1c1 -#define KEY_INS_LINE 0x1c2 -#define KEY_DEL_LINE 0x1c3 - -#define KEY_FN 0x1d0 -#define KEY_FN_ESC 0x1d1 -#define KEY_FN_F1 0x1d2 -#define KEY_FN_F2 0x1d3 -#define KEY_FN_F3 0x1d4 -#define KEY_FN_F4 0x1d5 -#define KEY_FN_F5 0x1d6 -#define KEY_FN_F6 0x1d7 -#define KEY_FN_F7 0x1d8 -#define KEY_FN_F8 0x1d9 -#define KEY_FN_F9 0x1da -#define KEY_FN_F10 0x1db -#define KEY_FN_F11 0x1dc -#define KEY_FN_F12 0x1dd -#define KEY_FN_1 0x1de -#define KEY_FN_2 0x1df -#define KEY_FN_D 0x1e0 -#define KEY_FN_E 0x1e1 -#define KEY_FN_F 0x1e2 -#define KEY_FN_S 0x1e3 -#define KEY_FN_B 0x1e4 - -#define KEY_BRL_DOT1 0x1f1 -#define KEY_BRL_DOT2 0x1f2 -#define KEY_BRL_DOT3 0x1f3 -#define KEY_BRL_DOT4 0x1f4 -#define KEY_BRL_DOT5 0x1f5 -#define KEY_BRL_DOT6 0x1f6 -#define KEY_BRL_DOT7 0x1f7 -#define KEY_BRL_DOT8 0x1f8 -#define KEY_BRL_DOT9 0x1f9 -#define KEY_BRL_DOT10 0x1fa - -#define KEY_NUMERIC_0 0x200 /* used by phones, remote controls, */ -#define KEY_NUMERIC_1 0x201 /* and other keypads */ -#define KEY_NUMERIC_2 0x202 -#define KEY_NUMERIC_3 0x203 -#define KEY_NUMERIC_4 0x204 -#define KEY_NUMERIC_5 0x205 -#define KEY_NUMERIC_6 0x206 -#define KEY_NUMERIC_7 0x207 -#define KEY_NUMERIC_8 0x208 -#define KEY_NUMERIC_9 0x209 -#define KEY_NUMERIC_STAR 0x20a -#define KEY_NUMERIC_POUND 0x20b -#define KEY_NUMERIC_A 0x20c /* Phone key A - HUT Telephony 0xb9 */ -#define KEY_NUMERIC_B 0x20d -#define KEY_NUMERIC_C 0x20e -#define KEY_NUMERIC_D 0x20f - -#define KEY_CAMERA_FOCUS 0x210 -#define KEY_WPS_BUTTON 0x211 /* WiFi Protected Setup key */ - -#define KEY_TOUCHPAD_TOGGLE 0x212 /* Request switch touchpad on or off */ -#define KEY_TOUCHPAD_ON 0x213 -#define KEY_TOUCHPAD_OFF 0x214 - -#define KEY_CAMERA_ZOOMIN 0x215 -#define KEY_CAMERA_ZOOMOUT 0x216 -#define KEY_CAMERA_UP 0x217 -#define KEY_CAMERA_DOWN 0x218 -#define KEY_CAMERA_LEFT 0x219 -#define KEY_CAMERA_RIGHT 0x21a - -#define KEY_ATTENDANT_ON 0x21b -#define KEY_ATTENDANT_OFF 0x21c -#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */ -#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */ - -#define BTN_DPAD_UP 0x220 -#define BTN_DPAD_DOWN 0x221 -#define BTN_DPAD_LEFT 0x222 -#define BTN_DPAD_RIGHT 0x223 - -#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ -#define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */ - -#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */ -#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */ -#define KEY_JOURNAL 0x242 /* AL Log/Journal/Timecard */ -#define KEY_CONTROLPANEL 0x243 /* AL Control Panel */ -#define KEY_APPSELECT 0x244 /* AL Select Task/Application */ -#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */ -#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */ -#define KEY_ASSISTANT 0x247 /* AL Context-aware desktop assistant */ - -#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ -#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ - -#define KEY_KBDINPUTASSIST_PREV 0x260 -#define KEY_KBDINPUTASSIST_NEXT 0x261 -#define KEY_KBDINPUTASSIST_PREVGROUP 0x262 -#define KEY_KBDINPUTASSIST_NEXTGROUP 0x263 -#define KEY_KBDINPUTASSIST_ACCEPT 0x264 -#define KEY_KBDINPUTASSIST_CANCEL 0x265 - -/* Diagonal movement keys */ -#define KEY_RIGHT_UP 0x266 -#define KEY_RIGHT_DOWN 0x267 -#define KEY_LEFT_UP 0x268 -#define KEY_LEFT_DOWN 0x269 - -#define KEY_ROOT_MENU 0x26a /* Show Device's Root Menu */ -/* Show Top Menu of the Media (e.g. DVD) */ -#define KEY_MEDIA_TOP_MENU 0x26b -#define KEY_NUMERIC_11 0x26c -#define KEY_NUMERIC_12 0x26d -/* - * Toggle Audio Description: refers to an audio service that helps blind and - * visually impaired consumers understand the action in a program. Note: in - * some countries this is referred to as "Video Description". - */ -#define KEY_AUDIO_DESC 0x26e -#define KEY_3D_MODE 0x26f -#define KEY_NEXT_FAVORITE 0x270 -#define KEY_STOP_RECORD 0x271 -#define KEY_PAUSE_RECORD 0x272 -#define KEY_VOD 0x273 /* Video on Demand */ -#define KEY_UNMUTE 0x274 -#define KEY_FASTREVERSE 0x275 -#define KEY_SLOWREVERSE 0x276 -/* - * Control a data application associated with the currently viewed channel, - * e.g. teletext or data broadcast application (MHEG, MHP, HbbTV, etc.) - */ -#define KEY_DATA 0x277 -#define KEY_ONSCREEN_KEYBOARD 0x278 - -#define BTN_TRIGGER_HAPPY 0x2c0 -#define BTN_TRIGGER_HAPPY1 0x2c0 -#define BTN_TRIGGER_HAPPY2 0x2c1 -#define BTN_TRIGGER_HAPPY3 0x2c2 -#define BTN_TRIGGER_HAPPY4 0x2c3 -#define BTN_TRIGGER_HAPPY5 0x2c4 -#define BTN_TRIGGER_HAPPY6 0x2c5 -#define BTN_TRIGGER_HAPPY7 0x2c6 -#define BTN_TRIGGER_HAPPY8 0x2c7 -#define BTN_TRIGGER_HAPPY9 0x2c8 -#define BTN_TRIGGER_HAPPY10 0x2c9 -#define BTN_TRIGGER_HAPPY11 0x2ca -#define BTN_TRIGGER_HAPPY12 0x2cb -#define BTN_TRIGGER_HAPPY13 0x2cc -#define BTN_TRIGGER_HAPPY14 0x2cd -#define BTN_TRIGGER_HAPPY15 0x2ce -#define BTN_TRIGGER_HAPPY16 0x2cf -#define BTN_TRIGGER_HAPPY17 0x2d0 -#define BTN_TRIGGER_HAPPY18 0x2d1 -#define BTN_TRIGGER_HAPPY19 0x2d2 -#define BTN_TRIGGER_HAPPY20 0x2d3 -#define BTN_TRIGGER_HAPPY21 0x2d4 -#define BTN_TRIGGER_HAPPY22 0x2d5 -#define BTN_TRIGGER_HAPPY23 0x2d6 -#define BTN_TRIGGER_HAPPY24 0x2d7 -#define BTN_TRIGGER_HAPPY25 0x2d8 -#define BTN_TRIGGER_HAPPY26 0x2d9 -#define BTN_TRIGGER_HAPPY27 0x2da -#define BTN_TRIGGER_HAPPY28 0x2db -#define BTN_TRIGGER_HAPPY29 0x2dc -#define BTN_TRIGGER_HAPPY30 0x2dd -#define BTN_TRIGGER_HAPPY31 0x2de -#define BTN_TRIGGER_HAPPY32 0x2df -#define BTN_TRIGGER_HAPPY33 0x2e0 -#define BTN_TRIGGER_HAPPY34 0x2e1 -#define BTN_TRIGGER_HAPPY35 0x2e2 -#define BTN_TRIGGER_HAPPY36 0x2e3 -#define BTN_TRIGGER_HAPPY37 0x2e4 -#define BTN_TRIGGER_HAPPY38 0x2e5 -#define BTN_TRIGGER_HAPPY39 0x2e6 -#define BTN_TRIGGER_HAPPY40 0x2e7 - -/* We avoid low common keys in module aliases so they don't get huge. */ -#define KEY_MIN_INTERESTING KEY_MUTE -#define KEY_MAX 0x2ff -#define KEY_CNT (KEY_MAX+1) - -/* - * Relative axes - */ - -#define REL_X 0x00 -#define REL_Y 0x01 -#define REL_Z 0x02 -#define REL_RX 0x03 -#define REL_RY 0x04 -#define REL_RZ 0x05 -#define REL_HWHEEL 0x06 -#define REL_DIAL 0x07 -#define REL_WHEEL 0x08 -#define REL_MISC 0x09 -/* - * 0x0a is reserved and should not be used in input drivers. - * It was used by HID as REL_MISC+1 and userspace needs to detect if - * the next REL_* event is correct or is just REL_MISC + n. - * We define here REL_RESERVED so userspace can rely on it and detect - * the situation described above. - */ -#define REL_RESERVED 0x0a -#define REL_WHEEL_HI_RES 0x0b -#define REL_HWHEEL_HI_RES 0x0c -#define REL_MAX 0x0f -#define REL_CNT (REL_MAX+1) - -/* - * Absolute axes - */ - -#define ABS_X 0x00 -#define ABS_Y 0x01 -#define ABS_Z 0x02 -#define ABS_RX 0x03 -#define ABS_RY 0x04 -#define ABS_RZ 0x05 -#define ABS_THROTTLE 0x06 -#define ABS_RUDDER 0x07 -#define ABS_WHEEL 0x08 -#define ABS_GAS 0x09 -#define ABS_BRAKE 0x0a -#define ABS_HAT0X 0x10 -#define ABS_HAT0Y 0x11 -#define ABS_HAT1X 0x12 -#define ABS_HAT1Y 0x13 -#define ABS_HAT2X 0x14 -#define ABS_HAT2Y 0x15 -#define ABS_HAT3X 0x16 -#define ABS_HAT3Y 0x17 -#define ABS_PRESSURE 0x18 -#define ABS_DISTANCE 0x19 -#define ABS_TILT_X 0x1a -#define ABS_TILT_Y 0x1b -#define ABS_TOOL_WIDTH 0x1c - -#define ABS_VOLUME 0x20 - -#define ABS_MISC 0x28 - -/* - * 0x2e is reserved and should not be used in input drivers. - * It was used by HID as ABS_MISC+6 and userspace needs to detect if - * the next ABS_* event is correct or is just ABS_MISC + n. - * We define here ABS_RESERVED so userspace can rely on it and detect - * the situation described above. - */ -#define ABS_RESERVED 0x2e - -#define ABS_MT_SLOT 0x2f /* MT slot being modified */ -#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ -#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ -#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ -#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ -#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ -#define ABS_MT_POSITION_X 0x35 /* Center X touch position */ -#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */ -#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ -#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ -#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ -#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ -#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */ -#define ABS_MT_TOOL_X 0x3c /* Center X tool position */ -#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */ - - -#define ABS_MAX 0x3f -#define ABS_CNT (ABS_MAX+1) - -/* - * Switch events - */ - -#define SW_LID 0x00 /* set = lid shut */ -#define SW_TABLET_MODE 0x01 /* set = tablet mode */ -#define SW_HEADPHONE_INSERT 0x02 /* set = inserted */ -#define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" - set = radio enabled */ -#define SW_RADIO SW_RFKILL_ALL /* deprecated */ -#define SW_MICROPHONE_INSERT 0x04 /* set = inserted */ -#define SW_DOCK 0x05 /* set = plugged into dock */ -#define SW_LINEOUT_INSERT 0x06 /* set = inserted */ -#define SW_JACK_PHYSICAL_INSERT 0x07 /* set = mechanical switch set */ -#define SW_VIDEOOUT_INSERT 0x08 /* set = inserted */ -#define SW_CAMERA_LENS_COVER 0x09 /* set = lens covered */ -#define SW_KEYPAD_SLIDE 0x0a /* set = keypad slide out */ -#define SW_FRONT_PROXIMITY 0x0b /* set = front proximity sensor active */ -#define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */ -#define SW_LINEIN_INSERT 0x0d /* set = inserted */ -#define SW_MUTE_DEVICE 0x0e /* set = device disabled */ -#define SW_PEN_INSERTED 0x0f /* set = pen inserted */ -#define SW_MAX 0x0f -#define SW_CNT (SW_MAX+1) - -/* - * Misc events - */ - -#define MSC_SERIAL 0x00 -#define MSC_PULSELED 0x01 -#define MSC_GESTURE 0x02 -#define MSC_RAW 0x03 -#define MSC_SCAN 0x04 -#define MSC_TIMESTAMP 0x05 -#define MSC_MAX 0x07 -#define MSC_CNT (MSC_MAX+1) - -/* - * LEDs - */ - -#define LED_NUML 0x00 -#define LED_CAPSL 0x01 -#define LED_SCROLLL 0x02 -#define LED_COMPOSE 0x03 -#define LED_KANA 0x04 -#define LED_SLEEP 0x05 -#define LED_SUSPEND 0x06 -#define LED_MUTE 0x07 -#define LED_MISC 0x08 -#define LED_MAIL 0x09 -#define LED_CHARGING 0x0a -#define LED_MAX 0x0f -#define LED_CNT (LED_MAX+1) - -/* - * Autorepeat values - */ - -#define REP_DELAY 0x00 -#define REP_PERIOD 0x01 -#define REP_MAX 0x01 -#define REP_CNT (REP_MAX+1) - -/* - * Sounds - */ - -#define SND_CLICK 0x00 -#define SND_BELL 0x01 -#define SND_TONE 0x02 -#define SND_MAX 0x07 -#define SND_CNT (SND_MAX+1) - -#endif diff --git a/app/src/main/jni/lorie/backend/android/locale/log.h b/app/src/main/jni/lorie/backend/android/locale/log.h deleted file mode 100644 index b3da08ee5..000000000 --- a/app/src/main/jni/lorie/backend/android/locale/log.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifdef __ANDROID__ -#include - -#ifndef LOG_TAG -#define LOG_TAG "LorieNative" -#endif - -#define LOG(prio, ...) __android_log_print(prio, LOG_TAG, __VA_ARGS__) - -#define LOGI(...) LOG(ANDROID_LOG_INFO, __VA_ARGS__) -#define LOGW(...) LOG(ANDROID_LOG_WARN, __VA_ARGS__) -#define LOGD(...) LOG(ANDROID_LOG_DEBUG, __VA_ARGS__) -#define LOGV(...) LOG(ANDROID_LOG_VERBOSE, __VA_ARGS__) -#define LOGE(...) LOG(ANDROID_LOG_ERROR, __VA_ARGS__) -#define LOGF(...) LOG(ANDROID_LOG_FATAL, __VA_ARGS__) - -#else -#include - -#define LOG(prio, ...) { printf(prio __VA_ARGS__); printf("\n"); } - -#define LOGI(...) LOG("I: " , __VA_ARGS__) -#define LOGW(...) LOG("W: " , __VA_ARGS__) -#define LOGD(...) LOG("D: " , __VA_ARGS__) -#define LOGV(...) LOG("V: " , __VA_ARGS__) -#define LOGE(...) LOG("E: " , __VA_ARGS__) -#define LOGF(...) LOG("F: " , __VA_ARGS__) - -#endif -#ifdef DBG -#undef DBG -#endif - -#define DBG LOGD("Here! %s %d", __FILE__, __LINE__) - -extern int trace_funcs; -#if defined(TRACE_FUNCS) && !defined(__ANDROID__) -#include -#include -void __attribute__((no_instrument_function)) -__cyg_profile_func_enter (void *func, void *caller) { - if (!trace_funcs) return; - Dl_info info; - if (dladdr(func, &info)) - LOGD ("enter %p [%s] %s\n", func, (info.dli_fname) ? info.dli_fname : "?", info.dli_sname ? info.dli_sname : "?"); -} -void __attribute__((no_instrument_function)) -__cyg_profile_func_exit (void *func, void *caller) { - if (!trace_funcs) return; - Dl_info info; - if (dladdr(func, &info)) - LOGD ("leave %p [%s] %s\n", func, (info.dli_fname) ? info.dli_fname : "?", info.dli_sname ? info.dli_sname : "?"); -} - -#define static // backtrace do not report static function names -#endif diff --git a/app/src/main/jni/lorie/backend/android/locale/make.sh b/app/src/main/jni/lorie/backend/android/locale/make.sh deleted file mode 100755 index db06240e6..000000000 --- a/app/src/main/jni/lorie/backend/android/locale/make.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -e -x -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -cd ${DIR} -rm -f test generator -gcc -o generator generator.c android-utils.c -lxkbcommon -g -rdynamic -#./generator -./generator > ../keymaps.h -#gcc -o test test.c -lX11 -g -rdynamic -#./test diff --git a/app/src/main/jni/lorie/backend/android/locale/test.c b/app/src/main/jni/lorie/backend/android/locale/test.c deleted file mode 100644 index 77315370e..000000000 --- a/app/src/main/jni/lorie/backend/android/locale/test.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "keymaps.h" -void handler(int sig) { - void *array[10]; - size_t size; - - // get void*'s for all entries on the stack - size = backtrace(array, 10); - - // print out all the frames to stderr - fprintf(stderr, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, STDERR_FILENO); - exit(1); -} - -void getSymLayout1(int keyCode, char* sym) { - int i; - for (i=0; lorie_keymaps[i]; i++) { - if (lorie_keymaps[i]->keysyms[keyCode - KEYCODE_MIN].normal[0] == sym[0]) { - printf("aasdasd\n"); - return; - } - } - printf("lorie_keymaps[i]->keysyms[%d].normal[0] = %c; sym[0] = %c\n", - keyCode + KEYCODE_MIN, - lorie_keymaps[0]->keysyms[keyCode - KEYCODE_MIN].normal[0], - sym[0]); - printf("aqweqweqwe\n"); -} - -char* getSymLayout(int keyCode, char* sym) { - int i, j; - for (i=0; lorie_keymaps[i]; i++) { - for (j=0; j<(KEYCODE_MAX-KEYCODE_MIN); j++) { - if (!strcmp(sym, lorie_keymaps[i]->keysyms[j].normal)) return lorie_keymaps[i]->name; - } - } - for (i=0; lorie_keymaps[i]; i++) { - for (j=0; j<(KEYCODE_MAX-KEYCODE_MIN); j++) { - if (!strcmp(sym, lorie_keymaps[i]->keysyms[j].shift)) return lorie_keymaps[i]->name; - } - } - return "unknown"; -} - -int main(void){ - signal(SIGSEGV, handler); - - char *teststrings[] = {"q", "w", "e", "1", "2", "3", "ф", "ы", "в", "ф", "א", "ת", "ה", NULL}; - for (int i=0; teststrings[i]; i++) { - printf("sym: %s, layout: %s\n", teststrings[i], getSymLayout(0, teststrings[i])); - } -} diff --git a/app/src/main/jni/lorie/backend/android/utils.c b/app/src/main/jni/lorie/backend/android/utils.c deleted file mode 100644 index 6a9594074..000000000 --- a/app/src/main/jni/lorie/backend/android/utils.c +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include "keymaps.h" -void get_character_data(char** layout, int *shift, int *ec, char *ch) { - int i; - for (i=0; lorie_keymaps[i]; i++) { - for ((*ec)=0; (*ec)<(KEYCODE_MAX-KEYCODE_MIN); (*ec)++) { - if (!strcmp(ch, lorie_keymaps[i]->keysyms[*ec].normal)) { - *layout = lorie_keymaps[i]->name; - *shift = 0; - return; - } - if (!strcmp(ch, lorie_keymaps[i]->keysyms[*ec].shift)) { - *layout = lorie_keymaps[i]->name; - *shift = 1; - return; - } - } - } - *ec = 0; - *shift = 0; -} - -void android_keycode_get_eventcode(int kc, int *ec, int *shift) { - if (lorie_keymap_android[kc].eventCode != 0) { - *ec = lorie_keymap_android[kc].eventCode; - *shift = lorie_keymap_android[kc].shift; - } else { - *ec = *shift = 0; - } -}; diff --git a/app/src/main/jni/lorie/compositor.cpp b/app/src/main/jni/lorie/compositor.cpp deleted file mode 100644 index 2a4aab4a4..000000000 --- a/app/src/main/jni/lorie/compositor.cpp +++ /dev/null @@ -1,272 +0,0 @@ -#include -#include - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-parameter" -#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection" - -using namespace wayland; - -lorie_compositor::lorie_compositor() { - on_client = [=](client_t* client) { - client->user_data() = new client_data; - client->on_destroy = [=] { - bool request_redraw{}; - - if (screen.sfc && screen.sfc->client() == client) { - screen.sfc = nullptr; - request_redraw = true; - } - - if (cursor.sfc && cursor.sfc->client() == client) { - screen.sfc = nullptr; - request_redraw = true; - } - - if (request_redraw) - redraw(true); - - if(!screen.sfc) - set_renderer_visibility(false); - LOGI("Client destroyed"); - }; - }; - global_compositor.on_bind = [=] (client_t*, compositor_t* compositor) { - compositor->on_create_region = [](region_t* region) { - region->on__destroy = [=] { - region->destroy(); - }; - }; - compositor->on_create_surface = [=](surface_t* surface) { - auto data = new surface_data; - data->id = surface->id(); // For debugging purposes - surface->user_data() = data; - surface->on_attach = [=](buffer_t* b, int32_t, int32_t) { - data->buffer = b; - }; - surface->on_damage = [=](int32_t, int32_t, int32_t, int32_t) { - data->damaged = true; - }; - surface->on_damage_buffer = [=](int32_t, int32_t, int32_t, int32_t) { - data->damaged = true; - }; - surface->on_frame = [=] (callback_t* cb) { - data->frame_callback = cb; - }; - surface->on_commit = [=] { - redraw(); - if (data->buffer) - data->buffer->release(); - if (data->frame_callback) { - data->frame_callback->done(resource_t::timestamp()); - data->frame_callback = nullptr; - } - }; - surface->on__destroy = [=] { - surface->destroy(); - }; - }; - }; - global_seat.on_bind = [=, this](client_t* client, seat_t* seat) { - seat->capabilities (seat_capability::touch | seat_capability::keyboard | seat_capability::pointer); - seat->name("default"); - - auto data = any_cast(client->user_data()); - - seat->on_get_pointer = [=, this](pointer_t* pointer) { - LOGV("Client requested seat pointer"); - data->pointer = pointer; - if (screen.sfc) - pointer->enter(next_serial(), screen.sfc, 0, 0); - - pointer->on_set_cursor = [=](uint32_t, wayland::surface_t* sfc, int32_t x, int32_t y) { - cursor.sfc = sfc; - cursor.hotspot_x = x; - cursor.hotspot_y = y; - - if (sfc) - any_cast(sfc->user_data())->damaged = true; - }; - pointer->on_release = [=] { - pointer->destroy(); - }; - }; - seat->on_get_keyboard = [=](keyboard_t* kbd) { - LOGV("Client requested seat keyboard"); - data->kbd = kbd; - - int fd = -1, size = -1; - get_keymap(&fd, &size); - if (fd == -1 || size == -1) { - LOGE("Error while getting keymap from backend"); - return; - } - - kbd->keymap(wayland::keyboard_keymap_format::xkb_v1, fd, size); - close (fd); - - wl_array keys{}; - wl_array_init(&keys); - - if (screen.sfc) - kbd->enter(next_serial(), screen.sfc, &keys); - - kbd->on_release = [=] { - kbd->destroy(); - }; - }; - seat->on_get_touch = [=](touch_t* touch) { - data->touch = touch; - touch->on_release = [=] { - touch->destroy(); - }; - }; - }; - global_output.on_bind = [=, this](client_t* client, output_t* output) { - auto data = any_cast(client->user_data()); - data->output = output; - report_mode(output); - - output->on_release = [=]{ - output->destroy(); - }; - }; - global_shell.on_bind = [=](client_t* client, shell_t* shell) { - auto data = any_cast(client->user_data()); - - shell->on_get_shell_surface = [=] (shell_surface_t* shell, surface_t* sfc) { - shell->on_set_toplevel = [=] () { - wl_array keys{}; - wl_array_init(&keys); - screen.sfc = sfc; - redraw(); - set_renderer_visibility(sfc != nullptr); - - if (data->pointer) - data->pointer->enter(next_serial(),sfc, 0, 0); - - if(data->kbd) - data->kbd->enter(next_serial(), sfc, &keys); - - auto buffer = sfc ? any_cast(sfc->user_data())->buffer : nullptr; - if (buffer) - shell->configure(shell_surface_resize::none, buffer->shm_width(), buffer->shm_height()); - }; - }; - }; - global_xdg_wm_base.on_bind = [=, this](client_t* client, xdg_wm_base_t* wm_base) { - auto data = any_cast(client->user_data()); - wm_base->on_get_xdg_surface = [=, this](xdg_surface_t* xdg_surface, surface_t* sfc) { - xdg_surface->on_get_toplevel = [=, this](xdg_toplevel_t*) { - wl_array keys{}; - wl_array_init(&keys); - screen.sfc = sfc; - redraw(); - set_renderer_visibility(sfc != nullptr); - - if (data->pointer) - data->pointer->enter(next_serial(),sfc, 0, 0); - - if(data->kbd) - data->kbd->enter(next_serial(), sfc, &keys); - }; - }; - wm_base->on__destroy = [=]() { wm_base->destroy(); }; - }; - - LOGV("Starting compositor"); - wl_display_init_shm (*this); - add_fd_listener(queue.get_fd(), WL_EVENT_READABLE, [&](int, uint){ queue.run(); return 0; }); -} - -void lorie_compositor::redraw(bool force) { - if (screen.win) { - auto data = screen.sfc ? any_cast(screen.sfc->user_data()) : nullptr; - bool damaged = (data && data->damaged) || force; - if (damaged) - blit(screen.win, screen.sfc); - if (data) - data->damaged = false; - } - - if (cursor.win) { - auto data = cursor.sfc ? any_cast(cursor.sfc->user_data()) : nullptr; - bool damaged = (data && data->damaged) || force; - if (damaged) - blit(cursor.win, cursor.sfc); - if (data) - data->damaged = false; - } -} - -void lorie_compositor::post(std::function f) { - queue.write(std::move(f)); -} - -void lorie_compositor::output_resize(EGLNativeWindowType win, int real_width, int real_height, int physical_width, int physical_height) { - // Xwayland segfaults without that line - LOGV("JNI: window is changed: %p %dx%d (%dmm x %dmm)", win, real_width, real_height, physical_width, physical_height); - if (real_width == 0 || real_height == 0 || physical_width == 0 || physical_height == 0) return; - screen.real_width = real_width; - screen.real_height = real_height; - screen.physical_width = physical_width; - screen.physical_height = physical_height; - screen.win = win; - - if (screen.sfc) { - auto data = any_cast(screen.sfc->client()->user_data()); - report_mode(data->output); - } -} - -void lorie_compositor::report_mode(wayland::output_t* output) const { - output->geometry(0, 0, screen.physical_width, screen.physical_height, output_subpixel::unknown, "Lorie", "none", output_transform::normal); - output->scale(1.0); - output->mode(output_mode::current | output_mode::preferred, screen.real_width, screen.real_height, 60000); - output->done(); -} - -void lorie_compositor::pointer_motion(int x, int y) { - LOGV("JNI: pointer motion %dx%d", x, y); - if (!screen.sfc) - return; - - auto data = any_cast(screen.sfc->client()->user_data()); - - data->pointer->motion(resource_t::timestamp(), double(x), double(y)); - data->pointer->frame(); -} - -void lorie_compositor::pointer_scroll(int axis, float value) { - LOGV("JNI: pointer scroll %d %f", axis, value); - if (!screen.sfc) - return; - - auto data = any_cast(screen.sfc->client()->user_data()); - - data->pointer->axis_discrete(pointer_axis(axis), (value >= 0) ? 1 : -1); - data->pointer->axis(resource_t::timestamp(), pointer_axis(axis), value); - data->pointer->frame(); -} - -void lorie_compositor::pointer_button(int button, int state) { - LOGV("JNI: pointer button %d type %d", button, state); - if (!screen.sfc) - return; - - auto data = any_cast(screen.sfc->client()->user_data()); - - LOGI("pointer button: %d %d", button, state); - data->pointer->button(next_serial(), resource_t::timestamp(), button, pointer_button_state(state)); - data->pointer->frame(); -} - -void lorie_compositor::keyboard_key(uint32_t key, keyboard_key_state state) { - if (!screen.sfc) - return; - - auto data = any_cast(screen.sfc->client()->user_data()); - data->kbd->key (next_serial(), resource_t::timestamp(), key, keyboard_key_state(state)); -} - -#pragma clang diagnostic pop diff --git a/app/src/main/jni/lorie/include/ashmem.h b/app/src/main/jni/lorie/include/ashmem.h deleted file mode 100644 index 1f5397927..000000000 --- a/app/src/main/jni/lorie/include/ashmem.h +++ /dev/null @@ -1,47 +0,0 @@ -/**************************************************************************** - **************************************************************************** - *** - *** This header was automatically generated from a Linux kernel header - *** of the same name, to make information necessary for userspace to - *** call into the kernel available to libc. It contains only constants, - *** structures, and macros generated from the original header, and thus, - *** contains no copyrightable information. - *** - **************************************************************************** - ****************************************************************************/ -#ifndef _LINUX_ASHMEM_H -#define _LINUX_ASHMEM_H - -#include -#include -#include - -#define ASHMEM_NAME_LEN 256 - -#define ASHMEM_NAME_DEF "dev/ashmem" - -#define ASHMEM_NOT_PURGED 0 -#define ASHMEM_WAS_PURGED 1 - -#define ASHMEM_IS_UNPINNED 0 -#define ASHMEM_IS_PINNED 1 - -struct ashmem_pin { - uint32_t offset; - uint32_t len; -}; - -#define __ASHMEMIOC 0x77 - -#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN]) -#define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN]) -#define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t) -#define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4) -#define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long) -#define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6) -#define ASHMEM_PIN _IOW(__ASHMEMIOC, 7, struct ashmem_pin) -#define ASHMEM_UNPIN _IOW(__ASHMEMIOC, 8, struct ashmem_pin) -#define ASHMEM_GET_PIN_STATUS _IO(__ASHMEMIOC, 9) -#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10) - -#endif diff --git a/app/src/main/jni/lorie/include/log.h b/app/src/main/jni/lorie/include/log.h deleted file mode 100644 index c97558d12..000000000 --- a/app/src/main/jni/lorie/include/log.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#pragma clang diagnostic push -#pragma ide diagnostic ignored "OCUnusedMacroInspection" -#ifdef ANDROID -#include - -#ifndef LOG_TAG -#define LOG_TAG "LorieNative" -#endif - -#ifndef LOG -#define LOG(prio, ...) __android_log_print(prio, LOG_TAG, __VA_ARGS__) -#endif - -#define LOGI(...) LOG(ANDROID_LOG_INFO, __VA_ARGS__) -#define LOGW(...) LOG(ANDROID_LOG_WARN, __VA_ARGS__) -#define LOGD(...) LOG(ANDROID_LOG_DEBUG, __VA_ARGS__) -#define LOGV(...) LOG(ANDROID_LOG_VERBOSE, __VA_ARGS__) -#define LOGE(...) LOG(ANDROID_LOG_ERROR, __VA_ARGS__) -#define LOGF(...) LOG(ANDROID_LOG_FATAL, __VA_ARGS__) - -#endif -#pragma clang diagnostic pop \ No newline at end of file diff --git a/app/src/main/jni/lorie/include/lorie_compositor.hpp b/app/src/main/jni/lorie/include/lorie_compositor.hpp deleted file mode 100644 index d9ef2bd62..000000000 --- a/app/src/main/jni/lorie/include/lorie_compositor.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include "log.h" - -#ifdef ANDROID -#include -#include -#include - -typedef ANativeWindow* EGLNativeWindowType; -#else -typedef void* EGLNativeWindowType; -#endif - -class lorie_compositor: public wayland::display_t { -public: - lorie_compositor(); -// compositor features - void start(); - void post(std::function f); - void redraw(bool force = false); - - void output_resize(EGLNativeWindowType win, int real_width, int real_height, int physical_width, int physical_height); - void report_mode(wayland::output_t* output) const; - - void pointer_motion(int x, int y); // absolute values - void pointer_scroll(int axis, float value); - void pointer_button(int button, int state); - void keyboard_key(uint32_t key, wayland::keyboard_key_state state); - - struct client_data { - wayland::output_t* output{}; - wayland::pointer_t* pointer{}; - wayland::keyboard_t* kbd{}; - wayland::touch_t* touch{}; - }; - - struct surface_data { - uint32_t x{}, y{}, id{}; - bool damaged{}; - wayland::buffer_t *buffer{}; - wayland::callback_t *frame_callback{}; - }; - - struct { - int real_width, real_height; - int physical_width, physical_height; - wayland::surface_t* sfc; - EGLNativeWindowType win; - } screen{}; - struct { - int width, height; - int hotspot_x, hotspot_y; - int x, y; - wayland::surface_t* sfc; - EGLNativeWindowType win; - } cursor{}; - static void blit(EGLNativeWindowType win, wayland::surface_t* sfc); - std::function set_renderer_visibility = [](bool){}; - std::function set_cursor_visibility = [](JNIEnv*, bool){}; - std::function set_cursor_position = [](JNIEnv*, int, int){}; - -// backend features - void get_keymap(int *fd, int *size); - -//private: - wayland::global_compositor_t global_compositor{this}; - wayland::global_seat_t global_seat{this}; - wayland::global_output_t global_output{this}; - wayland::global_shell_t global_shell{this}; - wayland::global_xdg_wm_base_t global_xdg_wm_base{this}; - - lorie_message_queue queue; - -#ifdef ANDROID - JNIEnv *env{}; - jobject thiz{}; - static jfieldID compositor_field_id; - jmethodID set_renderer_visibility_id{}; - jmethodID set_cursor_visibility_id{}; - jmethodID set_cursor_rect_id{}; - lorie_compositor(jobject thiz); - std::thread self; -#endif -}; diff --git a/app/src/main/jni/lorie/include/shm.h b/app/src/main/jni/lorie/include/shm.h deleted file mode 100644 index f2ff3b7dc..000000000 --- a/app/src/main/jni/lorie/include/shm.h +++ /dev/null @@ -1,22 +0,0 @@ -#include "ashmem.h" - -static inline int -os_create_anonymous_file(size_t size) { - int fd, ret; - long flags; - fd = open("/dev/ashmem", O_RDWR | O_CLOEXEC); - if (fd < 0) - return fd; - ret = ioctl(fd, ASHMEM_SET_SIZE, size); - if (ret < 0) - goto err; - flags = fcntl(fd, F_GETFD); - if (flags == -1) - goto err; - if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) - goto err; - return fd; - err: - close(fd); - return ret; -} \ No newline at end of file diff --git a/app/src/main/jni/lorie/lorie_message_queue.cpp b/app/src/main/jni/lorie/lorie_message_queue.cpp deleted file mode 100644 index 868c7ab57..000000000 --- a/app/src/main/jni/lorie/lorie_message_queue.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -lorie_message_queue::lorie_message_queue() { - std::unique_lock lock(mutex); - - fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); - if (fd == -1) { - LOGE("Failed to create socketpair for message queue: %s", strerror(errno)); - return; - } -} - -void lorie_message_queue::write(std::function func) { - static uint64_t i = 1; - std::unique_lock lock(mutex); - queue.push(func); - ::write(fd, &i, sizeof(uint64_t)); -} - -void lorie_message_queue::run() { - static uint64_t i = 0; - std::unique_lock lock(mutex); - std::function fn; - ::read(fd, &i, sizeof(uint64_t)); - while(!queue.empty()){ - fn = queue.front(); - queue.pop(); - - lock.unlock(); - fn(); - lock.lock(); - } -} - -int lorie_message_queue::get_fd() { - return fd; -} diff --git a/app/src/main/jni/lorie/lorie_wayland_server.cpp b/app/src/main/jni/lorie/lorie_wayland_server.cpp deleted file mode 100644 index c73cbac06..000000000 --- a/app/src/main/jni/lorie/lorie_wayland_server.cpp +++ /dev/null @@ -1,210 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#pragma clang diagnostic ignored "-Wshadow" -#pragma ide diagnostic ignored "cppcoreguidelines-pro-type-static-cast-downcast" - -using namespace wayland; - - -/* display_t methods */ -static void display_destroyed(wl_listener* l, void* data) { - auto d = static_cast(l); - if (d->on_destroy) - d->on_destroy(); -} - -static void client_created(wl_listener* l, void* data) { - if (!data) return; - auto c = static_cast(data); - auto d = reinterpret_cast(wl_display_get_destroy_listener(wl_client_get_display(c), &display_destroyed)); - auto new_client = new client_t(c); - if (d->on_client) - d->on_client(new_client); -} - -display_t::display_t(): -display(wl_display_create()), -wl_listener{{}, &display_destroyed}, -client_created_listener{{}, &client_created}{ - wl_display_add_destroy_listener(display, this); - wl_display_add_client_created_listener(display, &client_created_listener); -} - -class fd_listener: public wl_listener { -protected: - std::function dispatch; - static void destroy(wl_listener* that, void *) { - auto listener = static_cast(that); - wl_list_remove(&listener->link); - wl_event_source_remove(listener->source); - delete listener; - } -public: - wl_event_source* source = nullptr; - explicit fd_listener(wl_event_loop* loop, std::function dispatch_func): - wl_listener{{}, &destroy}, dispatch(std::move(dispatch_func)) { - wl_event_loop_add_destroy_listener(loop, this); - } - static int fire(int fd, uint32_t mask, void *data) { - auto listener = static_cast(data); - if (listener) - return listener->dispatch(fd, mask); - return 0; - } -}; - -void display_t::add_fd_listener(int fd, uint32_t mask, std::function listener) { - wl_event_loop* loop = wl_display_get_event_loop(display); - auto l = new fd_listener(loop, std::move(listener)); - l->source = wl_event_loop_add_fd(loop, fd, mask, fd_listener::fire, l); -} - -// It is wl_display_socket_add_fd version which drops server fd if it is faulty. -void display_t::add_socket_fd(int fd) { - { -#if 0 - wl_display_add_socket_fd(display, fd); -#else - class [[maybe_unused]] server_socket_listener: public wl_listener { - public: - wl_display* dpy; - wl_event_source* src; - explicit server_socket_listener(wl_display* dpy): - wl_listener{{}, &destroy}, dpy(dpy) {} - static void destroy(wl_listener* l, void* d) { - auto thiz = reinterpret_cast(l); - wl_event_source_remove(thiz->src); - delete thiz; - } - static int event(int fd, uint32_t mask, void *data) - { - auto l = reinterpret_cast(data); - if (mask & WL_EVENT_ERROR || mask & WL_EVENT_HANGUP) { - l->destroy(l, nullptr); - return 0; - } - - struct sockaddr_un name{}; - socklen_t length; - int client_fd; - - length = sizeof name; - client_fd = accept4(fd, (struct sockaddr *) &name, &length, SOCK_CLOEXEC); - if (client_fd < 0) { - LOGE("failed to accept: %s\n", strerror(errno)); - if (errno == EBADF || errno == ENOTSOCK || errno == EPERM) { - l->destroy(l, nullptr); - return 0; - } - } else - if (!wl_client_create(l->dpy, client_fd)) - close(client_fd); - - return 1; - } - static int set_cloexec_or_close(int fd) { - long flags; - - if (fd == -1) return -1; - flags = fcntl(fd, F_GETFD); - if (flags == -1) goto err; - if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) goto err; - - return fd; - - err: - close(fd); - return -1; - }; - }; - - uint32_t mask = WL_EVENT_READABLE | WL_EVENT_ERROR | WL_EVENT_HANGUP; - - listen(fd, 128); - wl_event_loop* loop = wl_display_get_event_loop(display); - auto listener = new server_socket_listener(display); - listener->src = wl_event_loop_add_fd(loop, fd, mask, server_socket_listener::event, listener); - wl_event_loop_add_destroy_listener(loop, listener); -#endif - } -} - -display_t::~display_t() { - wl_display_destroy_clients(display); - wl_display_destroy(display); -} - -/* client_t methods */ -wl_listener client_resource_created {{}, [](wl_listener*, void* d) { - if (d == nullptr || *(static_cast(d)) != &wl_buffer_interface) - return; - new buffer_t(static_cast(d)); -}}; - -static void client_destroy_callback(struct wl_listener *listener, void *) { - if (listener == nullptr) return; - auto c = static_cast(listener); - if (c->on_destroy) - c->on_destroy(); - delete c; -}; - -client_t::client_t(wl_client* client): wl_listener{{}, &client_destroy_callback}, client(client) { - wl_client_add_destroy_listener(*this, this); - wl_client_add_resource_created_listener(*this, &client_resource_created); -} - -client_t* client_t::get(wl_client* client) { - return client ? static_cast(wl_client_get_destroy_listener(client, &client_destroy_callback)) : nullptr; -} - -void client_t::post_implementation_error(const std::string& string) { - wl_client_post_implementation_error(client, "%s", string.c_str()); -} - -void client_t::destroy() { - wl_client_destroy(client); -} - -/* resource_t methods */ -void resource_t::resource_destroyed(wl_listener* that, void*) { - auto r = static_cast(that); - if (r->on_destroy) - r->on_destroy(); - delete r; -} - -resource_t::resource_t(client_t* client, uint32_t id, uint32_t version, -wl_interface* iface, wl_dispatcher_func_t dispatcher): -wl_listener{{}, &resource_destroyed}, -m_client(client), display(wl_client_get_display(*client)), -resource(wl_resource_create(*client, iface, version, id)), -version(version) { - wl_resource_add_destroy_listener(resource, this); - wl_resource_set_dispatcher(resource, dispatcher, iface, nullptr, nullptr); -} - -static inline client_t* client_get(wl_resource* c) { - wl_client* client; - if (c == nullptr || (client = wl_resource_get_client(c)) == nullptr) - return nullptr; - return client_t::get(client); -} - -resource_t::resource_t(wl_resource *r): -wl_listener{{}, &resource_destroyed}, -m_client(client_get(r)), display(wl_client_get_display(*m_client)), -resource(r), version(wl_resource_get_version(r)) { - wl_resource_add_destroy_listener(resource, this); -} - -uint32_t resource_t::timestamp() { - timespec t = {0}; - clock_gettime (CLOCK_MONOTONIC, &t); - return t.tv_sec * 1000 + t.tv_nsec / 1000000; -} diff --git a/app/src/main/jni/lorie/utils/log.cpp b/app/src/main/jni/lorie/utils/log.cpp deleted file mode 100755 index 30924520c..000000000 --- a/app/src/main/jni/lorie/utils/log.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#pragma ide diagnostic ignored "readability-inconsistent-declaration-parameter-name" -#pragma ide diagnostic ignored "bugprone-reserved-identifier" -#include // for dladdr -#include // for __cxa_demangle -#include - -#include -#include - -#define LOG(prio, ...) { \ - __android_log_print(prio, "LorieProfile", __VA_ARGS__); \ - char p = ((prio == ANDROID_LOG_VERBOSE)?'V': \ - ((prio == ANDROID_LOG_DEBUG) ?'D': \ - ((prio == ANDROID_LOG_INFO) ?'I': \ - ((prio == ANDROID_LOG_WARN) ?'W': \ - ((prio == ANDROID_LOG_ERROR) ?'E': \ - ((prio == ANDROID_LOG_FATAL) ?'F':'U')))))); \ - printf("%s/%c: ", "LorieProfile", p); \ - printf(__VA_ARGS__); \ - printf("\n"); \ -} - -#include "../include/log.h" -#include -#include - -bool enabled = true; -#define no_instrument void __attribute__((no_instrument_function)) __attribute__ ((visibility ("default"))) - -using namespace std; -extern "C" { - -extern void *blacklist[]; -#define skip_blacklisted(f) for (int z=0; blacklist[z]!=NULL; z++) if (blacklist[z]==(f)) return - -static thread_local int level = -1; - -no_instrument __attribute__((__constructor__(5))) i() { - if (getenv("LORIE_DEBUG")) - enabled = true; - if (getenv("LORIE_NDEBUG")) - enabled = false; -} - -no_instrument print_func(void *func, int enter) { - Dl_info info; - if (dladdr(func, &info) && info.dli_sname != nullptr) { - int status; - char *demangled = abi::__cxa_demangle(info.dli_sname,nullptr, nullptr, &status); - LOGD("%d%*c%s %s", gettid(), level, ' ', enter ? ">" : "<", status == 0 ? demangled : info.dli_sname); - free(demangled); - } -} - -no_instrument __cyg_profile_func_enter (void *func, [[maybe_unused]] void *caller) { - if (!enabled) return; - skip_blacklisted(func); - level++; - print_func(func, 1); -} - -no_instrument __cyg_profile_func_exit (void *func, [[maybe_unused]] void *caller) { - if (!enabled) return; - skip_blacklisted(func); - print_func(func, 0); - level--; -} - -void *blacklist[] = { - (void*) __cyg_profile_func_enter, - (void*) __cyg_profile_func_exit, - (void*) print_func -}; -} // extern "C" diff --git a/app/src/main/jni/starter/Android.mk b/app/src/main/jni/starter/Android.mk deleted file mode 100755 index 75d901c41..000000000 --- a/app/src/main/jni/starter/Android.mk +++ /dev/null @@ -1,7 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := x11-starter -LOCAL_SRC_FILES := starter.c -LOCAL_LDLIBS := -llog -ldl -include $(BUILD_SHARED_LIBRARY) diff --git a/app/src/main/jni/starter/starter.c b/app/src/main/jni/starter/starter.c deleted file mode 100755 index 93b8fd31d..000000000 --- a/app/src/main/jni/starter/starter.c +++ /dev/null @@ -1,182 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define unused __attribute__((__unused__)) - -#define DEFAULT_PREFIX "/data/data/com.termux/files/usr" -#define DEFAULT_XDG_RUNTIME_DIR DEFAULT_PREFIX "/tmp" -#define DEFAULT_SOCKET_NAME "termux-x11" - -static int socket_action(int* fd, char* path, - int (*action)(int, const struct sockaddr *, socklen_t)) { - if (!fd || !action) { - errno = -EINVAL; - return -1; - } - - struct sockaddr_un local; - size_t len; - - if((*fd = socket(PF_LOCAL, SOCK_STREAM, 0)) == -1) - { - perror("socket"); - return *fd; - } - - local.sun_family = AF_UNIX; - strcpy(local.sun_path, path); - len = strlen(local.sun_path) + sizeof(local.sun_family); - return action(*fd, (struct sockaddr *)&local, len); -} - -JNIEXPORT void JNICALL -Java_com_termux_x11_CmdEntryPoint_checkXdgRuntimeDir(unused JNIEnv *env, unused jobject thiz) { - char* XDG_RUNTIME_DIR = getenv("XDG_RUNTIME_DIR"); - if (!XDG_RUNTIME_DIR || strlen(XDG_RUNTIME_DIR) == 0) { - printf("$XDG_RUNTIME_DIR is unset.\n"); - printf("Exporting default value (%s).\n", DEFAULT_XDG_RUNTIME_DIR); - setenv("XDG_RUNTIME_DIR", DEFAULT_XDG_RUNTIME_DIR, true); - } -} - -static const char *getWaylandSocketPath() { - static char* path = NULL; - if (path != NULL) - return path; - - path = malloc(256 * sizeof(char)); - memset(path, 0, 256 * sizeof(char)); - - char* XDG_RUNTIME_DIR = getenv("XDG_RUNTIME_DIR"); - if (!XDG_RUNTIME_DIR || strlen(XDG_RUNTIME_DIR) == 0) { - printf("$XDG_RUNTIME_DIR is unset"); - exit(1); - } - - sprintf(path, "%s/%s", XDG_RUNTIME_DIR, DEFAULT_SOCKET_NAME); - return path; -} - -JNIEXPORT jboolean JNICALL -Java_com_termux_x11_CmdEntryPoint_checkWaylandSocket(unused JNIEnv *env, unused jobject thiz) { - int fd; - errno = 0; - - if (socket_action(&fd, (char *) getWaylandSocketPath(), connect) != -1) { - close(fd); - return 1; - } - - return 0; -} - -JNIEXPORT jint JNICALL -Java_com_termux_x11_CmdEntryPoint_createWaylandSocket(unused JNIEnv *env, unused jobject thiz) { - int fd; - errno = 0; - - unlink(getWaylandSocketPath()); - if (socket_action(&fd, (char *) getWaylandSocketPath(), bind) < 0) { - perror("socket"); - return -1; - } - - return fd; -} - -#pragma clang diagnostic push -#pragma ide diagnostic ignored "hicpp-signed-bitwise" -JNIEXPORT jint JNICALL -Java_com_termux_x11_CmdEntryPoint_openLogFD(unused JNIEnv *env, unused jobject thiz) { - const char* TERMUX_X11_LOG_FILE = getenv("TERMUX_X11_LOG_FILE"); - int sv[2]; /* the pair of socket descriptors */ - if (TERMUX_X11_LOG_FILE == NULL || strlen(TERMUX_X11_LOG_FILE) == 0) - return -1; - - int logfd = open(TERMUX_X11_LOG_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0666); - if (logfd < 0) { - perror("open logfile"); - return -1; - } - - if (pipe(sv) == -1) { - perror("pipe"); - return -1; - } - - fchmod (sv[0], 0777); - fchmod (sv[1], 0777); - - switch(fork()) { - /* - * Android do not allow another process to write to tty or pipe fd of our process. - * We can not force allowing even using chmod. - * That is a reason we are using pipe and cat here. - */ - case 0: { - int new_stderr = dup(2); - close(sv[1]); - dup2(sv[0], 0); - dup2(logfd, 1); - dup2(logfd, 2); - close(sv[0]); - close(logfd); - - printf("cat started (%d)\n", getpid()); - - struct pollfd pfd = {0}; - pfd.fd = 0; - pfd.events = POLLIN | POLLHUP; - - poll(&pfd, 1, 10000); - - execl("/data/data/com.termux/files/usr/bin/cat", "cat", NULL); - dprintf(new_stderr, "execl cat: %s\n", strerror(errno)); - return -1; - } - case -1: - return -1; - default: - close(sv[0]); - close(logfd); - return sv[1]; - } -} - -JNIEXPORT void JNICALL -Java_com_termux_x11_CmdEntryPoint_exec(JNIEnv *env, jclass clazz, jstring jpath, jobjectArray jargv) { - // execv's argv array is a bit incompatible with Java's String[], so we do some converting here... - int argc = (*env)->GetArrayLength(env, jargv) + 2; // Leading executable path and terminating NULL - char *argv[argc]; - memset(argv, 0, sizeof(char*) * argc); - - for(int i=0; iGetObjectArrayElement(env, jargv, i - 1) : jpath; - const char *pjc = (*env)->GetStringUTFChars(env, js, false); - argv[i] = calloc(strlen(pjc)+1, sizeof(char)); //Extra char for the terminating NULL - strcpy((char *) argv[i], pjc); - (*env)->ReleaseStringUTFChars(env, js, pjc); - } - argv[argc] = NULL; // Terminating NULL - setenv("WAYLAND_DISPLAY", DEFAULT_SOCKET_NAME, 1); - - execv(argv[0], argv); - perror("execv"); - exit(1); -} - -#pragma clang diagnostic pop diff --git a/app/src/main/jni/wayland b/app/src/main/jni/wayland deleted file mode 160000 index 8135e856e..000000000 --- a/app/src/main/jni/wayland +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8135e856ebd79872f886466e9cee39affb7d9ee8 diff --git a/app/stub/build.gradle b/app/stub/build.gradle deleted file mode 100644 index 93e9e403a..000000000 --- a/app/stub/build.gradle +++ /dev/null @@ -1,28 +0,0 @@ -plugins { - id('com.android.library') -} - -android { - compileSdkVersion 30 - defaultConfig { - minSdkVersion 24 - //noinspection ExpiredTargetSdkVersion - targetSdkVersion 28 - } - buildFeatures { - buildConfig false - } - buildTypes { - release { - minifyEnabled false - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } -} - -dependencies { - implementation 'androidx.annotation:annotation:1.5.0' -} diff --git a/app/stub/src/main/AndroidManifest.xml b/app/stub/src/main/AndroidManifest.xml deleted file mode 100644 index f682fe267..000000000 --- a/app/stub/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/app/stub/src/main/aidl/android/content/IIntentReceiver.aidl b/app/stub/src/main/aidl/android/content/IIntentReceiver.aidl deleted file mode 100644 index cf00a2342..000000000 --- a/app/stub/src/main/aidl/android/content/IIntentReceiver.aidl +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import android.content.Intent; -import android.os.Bundle; - -/** - * System private API for dispatching intent broadcasts. This is given to the - * activity manager as part of registering for an intent broadcasts, and is - * called when it receives intents. - * - * {@hide} - */ -oneway interface IIntentReceiver { - // Android 6+, unchanged. - void performReceive(in Intent intent, int resultCode, String data, - in Bundle extras, boolean ordered, boolean sticky, int sendingUser); -} \ No newline at end of file diff --git a/app/stub/src/main/java/android/app/ActivityManagerNative.java b/app/stub/src/main/java/android/app/ActivityManagerNative.java deleted file mode 100644 index cc8bf93e4..000000000 --- a/app/stub/src/main/java/android/app/ActivityManagerNative.java +++ /dev/null @@ -1,9 +0,0 @@ -package android.app; - -import android.os.IBinder; - -public class ActivityManagerNative { - public static IActivityManager asInterface(IBinder obj) { - throw new RuntimeException("STUB"); - } -} \ No newline at end of file diff --git a/app/stub/src/main/java/android/app/ActivityTaskManager.java b/app/stub/src/main/java/android/app/ActivityTaskManager.java deleted file mode 100644 index 367f48500..000000000 --- a/app/stub/src/main/java/android/app/ActivityTaskManager.java +++ /dev/null @@ -1,7 +0,0 @@ -package android.app; - -public class ActivityTaskManager { - public static IActivityTaskManager getService() { - throw new RuntimeException("STUB"); - } -} diff --git a/app/stub/src/main/java/android/app/IActivityManager.java b/app/stub/src/main/java/android/app/IActivityManager.java deleted file mode 100644 index 62c00b025..000000000 --- a/app/stub/src/main/java/android/app/IActivityManager.java +++ /dev/null @@ -1,31 +0,0 @@ -package android.app; - -import android.content.Intent; -import android.os.Binder; -import android.os.Bundle; -import android.os.IBinder; -import android.os.IInterface; -import android.os.RemoteException; - -import androidx.annotation.RequiresApi; - -import java.util.List; - -public interface IActivityManager extends IInterface { - int checkPermission(String permission, int pid, int uid) - throws RemoteException; - - int startActivityAsUser(IApplicationThread caller, String callingPackage, - Intent intent, String resolvedType, IBinder resultTo, String resultWho, - int requestCode, int flags, ProfilerInfo profilerInfo, - Bundle options, int userId) - throws RemoteException; - - @RequiresApi(26) - abstract class Stub extends Binder implements IActivityManager { - - public static IActivityManager asInterface(IBinder obj) { - throw new RuntimeException("STUB"); - } - } -} diff --git a/app/stub/src/main/java/android/app/IActivityTaskManager.java b/app/stub/src/main/java/android/app/IActivityTaskManager.java deleted file mode 100644 index 01f2b6940..000000000 --- a/app/stub/src/main/java/android/app/IActivityTaskManager.java +++ /dev/null @@ -1,23 +0,0 @@ -package android.app; - -import android.content.Intent; -import android.os.Bundle; -import android.os.IBinder; -import android.os.IInterface; -import android.os.RemoteException; -import android.content.IIntentSender; - -public interface IActivityTaskManager extends IInterface { - int startActivity(IApplicationThread caller, String callingPackage, String callingFeatureId, - Intent intent, String resolvedType, IBinder resultTo, String resultWho, - int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) - throws RemoteException; - int startActivity(IApplicationThread caller, String callingPackage, Intent intent, - String resolvedType, IBinder resultTo, String resultWho, int requestCode, - int flags, ProfilerInfo profilerInfo, Bundle options) - throws RemoteException; - IIntentSender getIntentSender(int type, String packageName, IBinder token, - String resultWho, int requestCode, Intent[] intents, String[] resolvedTypes, - int flags, Bundle options, int userId) - throws RemoteException; -} diff --git a/app/stub/src/main/java/android/app/IApplicationThread.java b/app/stub/src/main/java/android/app/IApplicationThread.java deleted file mode 100644 index 340553149..000000000 --- a/app/stub/src/main/java/android/app/IApplicationThread.java +++ /dev/null @@ -1,4 +0,0 @@ -package android.app; - -public interface IApplicationThread { -} \ No newline at end of file diff --git a/app/stub/src/main/java/android/app/ProfilerInfo.java b/app/stub/src/main/java/android/app/ProfilerInfo.java deleted file mode 100644 index dce3921c7..000000000 --- a/app/stub/src/main/java/android/app/ProfilerInfo.java +++ /dev/null @@ -1,4 +0,0 @@ -package android.app; - -public class ProfilerInfo { -} \ No newline at end of file diff --git a/app/stub/src/main/java/android/content/IIntentSender.java b/app/stub/src/main/java/android/content/IIntentSender.java deleted file mode 100644 index 597cf2251..000000000 --- a/app/stub/src/main/java/android/content/IIntentSender.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.content; - -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; - -public interface IIntentSender { - // Android 8+ - void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, - IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) - throws RemoteException; - // Android 6+ - int send(int code, Intent intent, String resolvedType, - IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) - throws RemoteException; -} \ No newline at end of file diff --git a/app/stub/src/main/java/android/os/ServiceManager.java b/app/stub/src/main/java/android/os/ServiceManager.java deleted file mode 100644 index 67496631b..000000000 --- a/app/stub/src/main/java/android/os/ServiceManager.java +++ /dev/null @@ -1,13 +0,0 @@ -package android.os; - -public class ServiceManager { - /** - * Returns a reference to a service with the given name. - * - * @param name the name of the service to get - * @return a reference to the service, or null if the service doesn't exist - */ - public static IBinder getService(String name) { - throw new RuntimeException("STUB"); - } -} diff --git a/app/stub/src/main/java/com/android/internal/app/IAppOpsService.java b/app/stub/src/main/java/com/android/internal/app/IAppOpsService.java deleted file mode 100644 index 1e2d8bcad..000000000 --- a/app/stub/src/main/java/com/android/internal/app/IAppOpsService.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.android.internal.app; - -import android.os.IBinder; - -public interface IAppOpsService extends android.os.IInterface { - public static abstract class Stub extends android.os.Binder implements IAppOpsService { - public static IAppOpsService asInterface(IBinder obj) { - throw new RuntimeException("STUB"); - } - } -} diff --git a/settings.gradle b/settings.gradle index 26d47a38f..7996fd291 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,3 @@ include ':shell-loader:stub' include ':shell-loader' -include ':app:stub' include ':app' -include ':x11-client-experimental' diff --git a/x11-client-experimental/.gitignore b/x11-client-experimental/.gitignore deleted file mode 100755 index 42afabfd2..000000000 --- a/x11-client-experimental/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/x11-client-experimental/build.gradle b/x11-client-experimental/build.gradle deleted file mode 100755 index 4d17aa465..000000000 --- a/x11-client-experimental/build.gradle +++ /dev/null @@ -1,64 +0,0 @@ -apply plugin: 'com.android.application' - -android { - compileSdkVersion 32 - defaultConfig { - // It will not interfere with main app because they are not considered to be installed at the same time. - applicationId "com.termux.x11" - minSdkVersion 24 - // Note: targetSdkVersion affects only tests, - // normally, even though this is packaged as apk, - // it's not loaded as apk so targetSdkVersion is ignored. - // targetSdkVersion this must be < 28 because this application accesses hidden apis - //noinspection ExpiredTargetSdkVersion,OldTargetApi - targetSdkVersion 28 - versionCode 1 - versionName "0.1" - } - - signingConfigs { - debug { - storeFile file('../app/testkey_untrusted.jks') - keyAlias 'alias' - storePassword 'xrj45yWGLbsO7W0v' - keyPassword 'xrj45yWGLbsO7W0v' - } - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - externalNativeBuild { - cmake { - path "src/main/cpp/CMakeLists.txt" - } - } - - packagingOptions { - jniLibs { - // This will allow us to use shared libraries inside *.apk, without unpacking - // @agnostic-apollo is the best - // https://github.com/termux/termux-x11/commit/6cdfb75c4451eef63f114a93baef5ba73b7cfd8c#commitcomment-77856313 - useLegacyPackaging false - } - } -} - -dependencies { - implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' - implementation 'com.termux.termux-app:termux-shared:-SNAPSHOT' - implementation 'com.google.android.material:material:1.8.0' - implementation fileTree(dir: 'libs', include: ['*.jar']) - //noinspection GradleDependency - implementation 'androidx.appcompat:appcompat:1.3.1' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test:runner:1.5.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - implementation 'androidx.annotation:annotation:1.5.0' - implementation 'androidx.drawerlayout:drawerlayout:1.1.1' - compileOnly project(':app:stub') - compileOnly project(':shell-loader:stub') -} \ No newline at end of file diff --git a/x11-client-experimental/src/main/AndroidManifest.xml b/x11-client-experimental/src/main/AndroidManifest.xml deleted file mode 100644 index 501da0e49..000000000 --- a/x11-client-experimental/src/main/AndroidManifest.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/x11-client-experimental/src/main/aidl/com/termux/x11/ICmdEntryPointInterface.aidl b/x11-client-experimental/src/main/aidl/com/termux/x11/ICmdEntryPointInterface.aidl deleted file mode 100644 index 95b59a0c4..000000000 --- a/x11-client-experimental/src/main/aidl/com/termux/x11/ICmdEntryPointInterface.aidl +++ /dev/null @@ -1,8 +0,0 @@ -// ICmdEntryPointInterface.aidl -package com.termux.x11; - -// Declare any non-default types here with import statements - -interface ICmdEntryPointInterface { - ParcelFileDescriptor getConnectionFd(); -} \ No newline at end of file diff --git a/x11-client-experimental/src/main/cpp/libXau b/x11-client-experimental/src/main/cpp/libXau deleted file mode 160000 index 14fdf25db..000000000 --- a/x11-client-experimental/src/main/cpp/libXau +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 14fdf25db9f21c8f3ad37f0d32a5b8e726efdc0d diff --git a/x11-client-experimental/src/main/cpp/loading_sign.c b/x11-client-experimental/src/main/cpp/loading_sign.c deleted file mode 100644 index b7a0acf74..000000000 --- a/x11-client-experimental/src/main/cpp/loading_sign.c +++ /dev/null @@ -1,5 +0,0 @@ -#include - -static void __attribute__((__constructor__)) load() { - puts("libxcb is loaded from apk...\n"); -} diff --git a/x11-client-experimental/src/main/cpp/lorie/lorie_message_queue.hpp b/x11-client-experimental/src/main/cpp/lorie/lorie_message_queue.hpp deleted file mode 100644 index 5cfbee2d6..000000000 --- a/x11-client-experimental/src/main/cpp/lorie/lorie_message_queue.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include -#include -#include - -class lorie_message_queue { -public: - lorie_message_queue(); - void write(std::function func); - - void run(); - int get_fd(); -private: - int fd; - std::mutex mutex; - std::queue> queue; -}; diff --git a/x11-client-experimental/src/main/cpp/xcbproto b/x11-client-experimental/src/main/cpp/xcbproto deleted file mode 160000 index ed461f379..000000000 --- a/x11-client-experimental/src/main/cpp/xcbproto +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ed461f379b6cde5bea7bc99d253c270b37298401 diff --git a/x11-client-experimental/src/main/cpp/xorgproto b/x11-client-experimental/src/main/cpp/xorgproto deleted file mode 160000 index 824001c94..000000000 --- a/x11-client-experimental/src/main/cpp/xorgproto +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 824001c947cb1962209c6a8f2c63c2637877220d diff --git a/x11-client-experimental/src/main/ic_launcher-web.png b/x11-client-experimental/src/main/ic_launcher-web.png deleted file mode 100644 index d7497dee51ac2b5808f49c3ed16c392ab27bffb0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22338 zcmeFZ_g52b*e*H=ARPn*MXE}Zs-Pf9i3Mp20@8)>Dowido~W;d-a(`)U3!-qRFK{U zgwT{Oy@pVd{RH0c?6uBX=j?xA`@=|PGBZ!P>vdmu0v~Fs(9@i!fgp%pP4%u01i`_d zaER&@_;1_m=RO1(P^;a&^~h^r=H=z>_m$e7u%jaH zXSuz`tI9<2m4`Kx>Z17?0Yi~N^%8M=jccdSzelmN(V~^^vh_D=?}>_(oyp*LYF6)$ zlGd=3g#OG+|DL{0b>aUWRwF3sHqZT(I%iq@Z3=Ztke+b$Gz1kVMThl_vWE3c_=NTR zp%l_+if?<;`_o(uMGb@OTZ~(*-Pqb$OD^KrA7w3&K^b!FIm>%=f_#0iF)2^012Oj@ zDA%(ijr$^k`Ge%CgS}uf$hoJuXJbTDwXV(P%4>`e1mQ$=Ia7E39lKxR=C%f&K|y%# zH^;KOKX83of*eA8NawGIJ*!$5A;W7xP=poxA00;3^TGr|9Ucbt%TGr@DTgqK*Fh3a zt;*4ys(^wZ=WH%!=QXx~%yUjI5VVe9uljInHS!NpRW58F2BED9*S*eAf2j&09-Y$l zb|r%vxeOO_*i{=9bPhW=~#&1jB=?S>&BKcn685x9N z#`kM$M4%s)uN6t?GfdQh`P_G@rGk#2T=PX_XaT>|V{n&1y%B|~sRog;ekbV zVhA#48t1Bbtp_D49a&#H*TF%$^GRiTWigHUbUl$S?1<>;^&JkOmF=JZWW(t}n&iKh z7m`SwN; zxbmlzex0`qy=zP`xYN;@$O>L4P?r9qnBH6p<}6KAu#I>vBMd=?_t{iJpG%WngrrYH zxLIO;oi+?=d{X+OjK1+1y0(tEHhNT~aV>o79%*LYW7<#0w8(lX&C`#F_WymgkJ4NV z<~&a9>E<|o%T0f1cZ}#sgI?{MnF<9)g)REK_S44{Qy0h^73tA6DqG}EkAOhG7v#J? zrx}XoqabgTg@i7VY|dp-h<%`$i>gTteMXpo^L+;SMI8^bGurGtOqSX?{yfA4zV{^& z-YDH@$_C9-qqDuz!OAtu$F2^kLFA1B@bxSIJ!*{3h6Uzz1_nXsGt6ieCWsu|dKw(T z8HgM`CI)M5I~lVe91Tt(M$e!*snGu9ysr|W83Z`P`!lltw9hk zID-a9>C@h>!hrpVG#JV^nk5{p&F{b=(&Y3b75#k{`!j>mINI67?oNCKFWFcHHOtp3PM0saC6wcg=wx1|45}n;^7f)auY?wp(>}_V?|1z zG}CzRFJ?!QHV1r>l~L)i*lnTr*6#AiCGgB5G5)EUw&*`=YZBn8zps;>ymsT))9|+5 zk9TI90$D20K&;WpH$6XKS;H0gg^Jk0GLplJKeJ!)TpcU5>AP6%wipE-^LH?>)Utyu zS2sIMe!KTho#XMwG}3T;O~G=t)8C9RIr zq)LYCeD~(T8U|a+gTQ2^tAw}bXs35h2iIAhJn15MyukOjjPb818An}M5hJypMQ+Q# zA_M+sZa&wFoqvCmbefRI?vg#m1ei)YO^8<54u0Y}S$Pl?uW26`{iApy9B^}{eT(s2T|w9C@xmjhC5$V&FI^?E<(zjHv*+JCiHfktzt>Iy z;b?r|#z6d4@|0g%dZ_Zw%ks;`mK|~S6nrlqSGy#*3}=+k|D=-an%oh5y*PSI~(zVn7Ia%##vsDbwP@86R4zpg9}=EbX&M=Y(~ zITlY?u^T_HkWg#g`NHUUVLuJ6t2g-JEk;T$E0Y=1@>nJV4hmvK48qc1D2~B< zH41Hc6ZDV*1X`rkgC;E&H@5X)4d+FWs3o=T9XB;HuISqZ8 z_J+DJHyCqp2oNE)923%)1SX~r6_85XV|#hM%z`=(wGh1*ofFlVK8;y z_?e$5Wv#hoyp~b;{zS7q`TX~{`?DT!xGJJetKe>m=l~n5o|NR_PL&5}0)e3O@is-O zQ88i>6$$Ch-LHWVUlE6C@EdNHot@!4io)m9KbV=BqvnDzjxQE)YY$r8`8*H^ zM3mO?&cUI;4~!8qPG&|WPhLN6@EfUAA*-?`fXy*D>-&KSHQ*VR6Zz{4qgFdGw zGyQ?{v&Ao|s|QPL@3I5f#-j#Mg@OW6SV5B5P(gg3c@U}cio`?Ltw3xcjzZM5=I0pC z?DefKPRP__h~Ee4M6r;H{B)OkOHD?M3LTKzqeQG5_D9X%ZF%?ZodpqhZ2i%9BQNU; zJR2@dwx4VmAKHT53q9ZbHj$AMw#bshW{vEL9)4n&t(k1G;+}nK`&=sbi|z8#PtC}p z701!?T+1@%|DNJRA-~bYVs+=e^u)-HO&U#*xZVKc6bG!g+f_+PX)C0rC>;6s;6dWU z9lme%HETsW1r!qlJvx&u-TtUN0*E$%wjrX{XnQkvmxhb5l|1ItKSJl2?rF{EFrjCh zD6ZN6>YYD=YTDPA%X&VX{WTW61>5FCy?|55PuP7#O{&KVTWUVNuQ|YT>>13BqT^N# z0&wG=mE$yDoPWQp+a0x=yK;%V(FfN1BtzsDtBC$KUKs62@u=|eNRqmj1S{$YzlfVH zNWD#YWoHFD?1H~_tSwRQNVIqvaTn`U$;F%@bN+A2Gh>WIV|+a9*}MyWIZ? zT8MrIq_9(PFlK?Wx1=fer~!(7&n9O4{#Bm*QD^yYeTrLa)9cN{BzFfky_P3UHS%Jq z*T$1-uZ>R*iEt&t-qT#2O!W^dlPmL2Z8R86F4$eja&yRo_O9jM|O$b2MupWx&U9!$prAbb!7&Zy|%?ijSvfoO& z{(A3lRHlgRC{ybB1K9R_hRUOzO<^Y$p&hr^MTy*`I06QB=)>LEy>P4Jz$Oi3YbB;z z4*YqWQbf_{9^#^6pn5eDjRNkvs_1$M)QibS7d`GGX%tCj+0 zff07VG8UtV0bBR0i#UeB`rN=(y1&7!A`gsj0KXxo;Jdp$!dyMLFd1sq^&!DuDy2bU z{C2KMRUv;g;rLMqNT-g+92g~+#eRO^AQlFH`1HuZr#Rf6nZeoDqK1DmZ_1ydLN?u~72qY!$Kg>ffV_@w~X-@wb(*>1ctB#?k4k>_ijq?!M|}PPA@uz9(&Z?gW63#nydn(hupd}pz3|F~dWih9C@fBHlL@2_(Q z@9x5aUz=guJerZ-9GMB*?3+m`_1WY#l6xL=;nXe3=eK9La7R0f69fA10>)#7^(2vr zF^*PuPgb#pH7$O_kAR=AR5+h=1Zl*HURBCqf+m+yOT8Ql7owQ+G#{L=zu58Z6nitK zGKof1B)cUxR%)95CH?L8ao+NzBzh4E>u(($x$o3nE|vyyhJO|o6nGRH-PhC67I-T9 zb6Z>6;LOh$TNj_tU$4Dd9Gq?1{@`N&BbZq#J&g1H2*RVshEM6flSpFLPE=;JO2_uX z!-O*hI;SnoCejb~Q$7&3hlAO~YzrTcg+|jI8$xtXVma_y;#Js+%j-XLb>|dU>c<$J ztH>Lx=?Zbb4)$%#_XcqRbk_1M%PLxO4JvNr* zR2zr(cgw|pndUZ$LDY9pwGIChE5fVXCm?1@ zqv$#p;s9*K?Q{@RWT-}}+rg)NmXfPrH}nhS7O#Eld*#z6_;qFW*B;Gt@~QH#UyILL z=vtgjV2|BXz^SoZwD53?5KXyZtpDKr#igw6Scid{)^MrOcAhehwy=>dOSQYGXFrQy z46o@K+}#ccs+Db}I&sjEc*uv(zB!0>uA$cS04D7)$~X8~uU7Ca`M3kD_sALSJMwK& z_$gc61t@^$o&_OYD4{!HDQ z-kWWL3IYDYr$AT|y)pemAh+Z+cqbJ+;x8XUCCzpq*_0n4A?35xCDWDXuvT#ZU#*1h zhXlp6M({5FYN+oC4%<3EKG*f3tv`ZLDw0u{L?iA*)0k2$cQ2A*d!$cg#k1ERrKLd} z-8QK6dQQzgXfTm@)i6$rgTGAh^^N^gzz(2!A)FAql-0HP5flM$jR&E#gD(=T8Mo!} zKm;D~E_*u1Ie|s`-UR!A?$Ewx)HB$t49%6Z{sGs^Tpu|T&BkYsdRI%8GP05wE=%0} zcAoFqqS6IAcFCqO9L{Z7I}@SxWBc!f(NNdIrIraVqbKpwQAF_tA)e0jd|QIy(U<#v z1DQmeM*F(5@>+K%F`qjpM2bFvGdzL{OsX2P_hCB5(6&5bthcXJE-lYbYJnf0ZeTPr znHOWvJ)(1u{;##!;?fcxk_-9*@)YK4br^eUJBgADdb2&n#-&$;8#gmOlfBch3=@R!p@vNS!CZb9Xli$flUWY?_FP@qLAY z^NY{Uv69Ck%MyBp)@!yge)~drH#05S^?R;!Nu(M`x}K(xlQC5@yB~9zi8Ggpt~x{?IG(MrNU|1e&d+JXcG^X!`cCuV-Fmvx`XI#Nn#Nsd9vG6qH z!aLo4nqaNXgYfeD;!RO1zT)eXy3mg?Q)HdYR5qTf2y|X7zgp@Zz=Pik)pyxlIpyLr zE3%3058Hfk0bVBfcJD6vu@gntzEm{wTQD13H;%@^bc0pdtTj(-)Fi? zj%Ej5&|7*|^)qjM*4{&k@v0orj0r$3HD{w<)oZlgBUVZV?D4wBHV>P?jL|!B?S!NK zR$r!iOV1{a(xI1n5Z8p1*SdMp0YR9;du#5x1ipG8RmS}fJ=3pUV~IeciTmP**)KSe zBX7Ro83 zt{Aw?r=%hshu3}@?Z2{-yn9s9_*YrEMkgXIHFqOJ$^Xgw{14Z42(?XGngzCeXALl- zl=|gyVzm2hFLS3YJ}KwHj%|0N={QZVpj5rY6v)dfA5%P}{#E0i=j}!ddxxE=;&U$c z>%;MnEE$0Por}N3MJLBz_xB8>vazPqdjWdQm!+bOw^CIwV!NiAb@z3G%DkJ71pp6p zEgm^zr3);r-eV3=kKC@j+wdX(5^CRliS135KdPSY+2P(sAkU36T>M*E{x}uuI!JAQ@y2h!3BuL!uW1+ zK1csO4}5IQ_mxmVf(Jh2k5d}+l;6H53;LJiYfFR4pv|uf29`c|A11-GC`vt6MirIL z=PaO8R>y1FlHT>r0e#N-7$}}4_cOfBmCFz3dnU{fYm4e`Dm)){}JkYpMG`NZI{VHkY06 z@w3YZ4`vQzd|H6F`W{L#<6|e|j*J$Km+$8x$NiWuvD%Y%HJ&`@uW1h5 z+lgTm+>W};!d$6R#GE7Z*e7wTX#Qx&FfBk!fA{hI)H4EhbMiDzO4!_qMtJkn97 zPM%SgQZ0PB690{hew41#WmX-_P5*O%W)elLm;HOaLJi?Uj-bW$mypd4chAl`FZnTJ z<+R}BQ|z)oD{}1FhwfXw3D;V0sF8l0)wE1eFM+UT>I^zBxFoP0(adshd_&Ec1Milp zPBhg*6-tv2Nt16_ z^2vUrw7HTw4H>^nmeG_c?Qt5N%k;6-<&ozIwlJru+|T%l$!s-0FC`(pmy`w_`$1H;BU=t!|}ZUQ6`zbbin0jBeq zW~g>vH<+AH8OnN1*-`ub$U>z>hk4-7A~|j2gd_j#o%B7eVAau3Ux2JgQZDW7+oTcj z!W^*ld#&H736iL9hNNAx+uXHYREq>yhH=9-&)VmZjq)Pqbxc)Fd$W7!>{|Hw_7X;u zHkBeq&0_Ks4XiYw=w;+Ly{+K6nj}VnkdW-B4IQm=O1+r8<7aIuG3lZ6;=>KXMK1}OOI?cy2&CZaaR3)tU z*=y`WFD}15t?)G8b{$5KIqd-&JGmQe$8o-GCp2_s?`=U>EV_;5(y^@v2~#%tg5tqv zy}X&t*^bH>DA}agd&|ljgv`3l!iXp2=1(mC%|AD&8?a<}kD4C<93D|#q}!W=a} zKQsNY%hGD^i^W-)~Hy~O#3o$a+VXz422Mi}N(sipH% zSIH@I+BZKJN)K8iFLkjqHB8X-;y%!1v)vtj>g&@M&T!BcdFgG-ut6fh%h zD2&AdzYNLlncdWd3Q=Q*4F0`l`aL|Cqw7^qJh{0^`S13go}RjtVF0^u-t_2{|DM9! zG1$Nct?VLic)ar)-=UEGC0f*Utz$(1^8S+oIa49-zlCo;z>Jn&vH3Eb9-x*`ISe$Z zEmkDHhe9KJcTp0v>v4l3Ln~Ert^t$%=58_(`T0@q87vO>hHbxs_Dy?WMZwHkI7eG- zcw&3uV6x>%ehK$miVMf)yb?@{5w${j!+SHsA^a3!dq8W>54wpX9~4wvSG-tSBmOnJ2@>F1!j?Q$DSLl#aF7^% zSZBH!ebaOG%p?x?Bc$!0O?+vCi={3L-q-bM^_Sf0!#xkJgzNlgPNMK+T{sE;6t7rP zHLKkv%SU1+mMK*OrIs5`quU~Xms**djErv@$&NJ%5gxeSB%gW~PLFOUXewJy_v+7u zq8o(mL8_)TzaO)@WQI;r@cobn`NU0P#Ug69CqF3%*OsoW74Vo7%6Uh&R`ar;!!5O^ zvQ@dS?H|kYO%#xD2n58dr;279?omAGtlWZ?^O(0q@tcJQjR5D-v=)ViZJJe@G2XvXzLFLi z2v92-qtVR0TJ^@y-UJt)J0f^%ji-2DI~Lix%2#y?G6;bhdYaqES*H#~P2EI=oxweoLADocm%Gja*0?&gTRJwNXue>7rTzPd zp2m+xSnu@QqpbzxxGz~ryZhM6KZ0F0D$N+d{ZsX4&0=om3jQBuiRzl_+{33- zje}Yr+O+a~+UlNNI1WS*fA2cE4E(qP_F};whC1AMTesb>!Ho+8LOBI8P%VJ^MAcjrmu@#H!#&SSNQKY81<=$J2^ViG)`hav=v@Awfr5z zdm;=wpPF~_iW(#ZH%GML_`g%^maS^@H&F#ex?;&mXyf0>Y(KP0pYHD0O|fzBRg?oySm98ht*9*rkW| zPM>Y9*#**AR*?JnSHHTnIG4{>|9=C}DI46i*BcX|d^9g})JwgYS29^?(1TRK5I zGmN)elYt#zlCPDf2*A(A>tx+$PYo}7kPj`iPHOqob7t=e-h~({#CVb0{%!JdUNXUn zjubW6=awZvZ73=7@X1h>Oe%qRl%H^T z;4Ovs&_gE3U5207pm7tJ9PiwKmQuxveq(d+*;#;WYPo#`VCF&}W)gcQTm%YTZMBzfVzU|-^&dV7de437CkP1bRjZhoT|XfxDF6g4)9nw-{m8yvH8?&^g;-YM znypy8^#jbHNT-$$5`?bLr_A-G9k0Gv5j(%0xb-A*S;C!6N4+6#~@kGjyL z2@Xk|7nql+YPbvyD>__27A{D4Wj1enBd0)g50u7>Yr%295wzsE#%p8 z-cb8zeERI(k6jrG#K_St1NQ6X*%lpdj`8gbmu<3o0PL2!FX>~!)bN{@Mqivij?}&H zKYpW>Thg+Psb~PqnG62&@R_>2C_m&whd}MRxHA8 z^*{Ee&Vvf4?rTUig;nV7kY~sx=&iI{)KsMDNv7*-Ez;Q2qXi13PqFDQNKo(4<@)Wv z!cb8-m&^2!k`uglOc~-7gcz#2B$nQmOKY9}mHDN@Zn(RyRWLdJA0@d)L0;a*y1xdf zRsGiZU6uvT7eAe^ITNaW?wutDQx1a6c&E_Y`kMud9%vZ^VmesvuIr%nunhGrq@20bTd%U1yCJWCCW;^1syP&RscEDC9 z$n<7DQbOlJbvHAj4@a(HC}Hc;WW1Ps_KB;_^TZYzGDQ4jMQ)GorBv61fT_k8b8CY} zbo$8N49Slyk`G{3_Zgytxh(2WXfI7OKr^ytycThIB>0_vIY}cnI?kKIh{{T1`(Q%^JT7~+C|9&~$ z`D?99+?5jK=HqC6mo&8ra%x0=B9zZDdQafqcs(~{>gWUtRh{ygRMg2UoG7n7l48<8ULqdyM7Q?bDqYk#|mX{dhjTt`3`r5Ak0uICwbX( z4W=3ajn&(sE8Z{J23EaLs#!y3s4{JZLGzew6G%>cbogcF!f;k-pvMxWm#-O`bNtuu zN-^|ndl>2IV@o11y*&NcA`D>^#6@l|KdJ|@EykQid%kgx^Y5bZVnfdr!XxIhpr-OR zK_z#TnN{}s12k}SEQhu~bAG!|5(5QOuBaT=bpIWGl23+C@!vH{)%xdATkiz{}@R8__y`Mc5L8z>Yo7MdS${b|okpIA5UjwMu z{V=L%5Xt)xzvYTuVsn?mY> zVqWKTkf%T0dgpT&Lk?)1e5$7+*>ZV(^(3krMJVlw=zZoRRofjXrv5YsmzsZtx1}m! zw4BHe>#)JpXVF7d_ ze$@Te#8vq7Lvr-%VesBf57;}0c!OO};fo@0AN*kyk64;d*5m*#gZ{-RN5uOr-2VJu z_lC>QuRRR^2q2@KLI;Y@56r@o1`6&CMcIl_n{gHH+`=48qmitj)n10gqo10hh`J2=0YNW?Qn(snYaKf+ z&@D_dp3KQ9TM&+zW-X9sHsK)zcs*65-An{)+f|J6Ug6 zd!^KE`1ac5AHg4&xHN5a8j0l{T4 zI!=bX(UOSjd24Dp7jtE6o-}PLC_{Gpl~Ip9YNPoqC|G%cB4&b&Q;HE?0ULTDN7WnU z?2FH98xWU*lHluZhw$FrIEZukk!mG_qv*i*7L$f;Qpvwa=5UchzNS1S$z<;lyqu@e zz?1w-(T&BcO);Z14=*91+Q>8kycg3(}2i0RFrnz{OI-6+b`lrGqG_=qOm!!-;auq&Qjj zGK=n{5qWNpxlVRc<<=j@CFiz&gF+$~euZEDk@T*eFy)8YP!ULMRTNT@n~DCOOw%@e zwA9W?D?ny@)p781tVP#{!KDm^`jMXYS+{yAgwxhm1$oZT7%>z5!=oer8s;=DqR4z` zTr^KbBx-wcQW+}rAHCgRHlmfK8FZ4cATB@3iKyEdGN}aF3eyjtiW2TXjlb->d*q0s z9aDL$h{OJd{0mUE6BT-i32_XBOsT#-+_aAWQ|fWoH{lF>N`4k*b-dOWq)AyyD{qTy zK5F_>`Rpz~W!F@%~0V+li`w zSflKv_LKW}ezd}_5&+5-!)8+1o&1@XuPDN<#0P(WWvPo1a7puS_yQDA6CQyKbL*5S zNv3Iasu%9~l=e|=g65pG{d?IPEd!RS#_uZ-ODXjdj@W^fe*tGHG4ED`B5Rrym0RgY ziH7i8mP<3!&97$v%&}Ebqq&a`et7MtUgxJf-WbTB)S~T4nA3Qfb+|X%WEk^l4h|6z zgh*lZ^T-hfFt$?+u}Ha?2T>5`RdU`RMG<~-d#tV^yd~N2^2N30v&ik5`j4K_HHXok zhxHBTfwWTQ2O0mVkLIiwn>vjV9Zg94g!?J7-5l+Y}Aqe z@p3>-QhJ&#Riir%fT*^Dt+cdNfFWBJ*jl&$A@d%GX+nkmMmQX=5_ts<>z*3j@FOz^u^Sq=;TfJ%It>> zM*u19us*Zoa9RMGSA2FUF^(bjS@cE6n~o>iflfE6HUIjOM7i6dO5r+T#66$Bw`Dul zbgj^v1#-20+_U|w#jg!h*^KJrcAW=cv6L)F&|1LlDv^}-NZ zx;yhOt}fsKn3GKWqW?H!+Uw!Z19Y@mlx<<2HLVq0_xdK!8$fO}QxbW;?T3qBvxH8( z>r|#mt>bvj=AZ#`q>^Xq5Whv@>2290=)u!mVR50WBE+pcp|{3!p{P^nv#=X~aUqR8XXfoPYInGfUHL5Ci$(FHIJ-{6b zYNf=$$WFbF?>4*u$%Ik7X|4S(*VfGdq^fn=2$E~M1SO|lmF=&HSYFjNo8TJgnr?$k z_lGXrPkFPx5QJvS0z?=-+ir}W39Lp5S@>8s6>ysD460o$*{|C^ii$fzCbIEOVg@z3 zQ9mCO2pN%gpbt05({ht0e(Fn$ab46ExaXDh<=wGSL&%3(*R1(1|GJ80pe7ZJMmXnl zeHBaK<{nXRDLdZY8Eu{L==mWR&)USz-FpLF zYv6}|3=l^i6j)#r$t-`&I2T=tKMqx(ck`DY#oM?aQ`NQ`&|)T)@3rzgg&$F~LJm`j zOmdo)yLLjT3jZHvkXwifR5QkmP9LZ3NUhiCP$8^X7+bV8@+oN&{#P#!-M%|_iwPyPO^yn4g3sS3kqaxW%y zIyd>0A?~(IzREwcz1cDLTz!wN{-yYk?z}^lRF&y|r0DjmXT0Y2uz%j|@KZj2b&GZ2 zK>)(g!WCY}c&@v!1EVNAiQc+msKn(4^<$00wo$_tZw zV}Ki5sbO{A-+s15l~{E<_6F?BRUY$zh&%961(x}XX^9;~P1yPtFQne{jd|ce7XQh~ zebF#I#-e?p+WHp9+V*+#n`cn-`i*I~Ug<2`q=m_HD=88>Vzabp;=|eK;@;zoJ7VeY zeVyD3x&Rf8oqNyf?$hW@PIA2QNk9U-+{t*ghf?Ch~q=TscwGaW%BseSDTIgRWfGMwm*fM z6A8>dcIMqlvi=i{X;^x>vtM_t)3lCgvqIyYCKoQxE7=PVE7^Lb@7<&@-=c?oDbGzf zto!WyGFa(kD1kTbWpBa6kq!1(jaNxW&_?n^08V^KXDO%sxBn=x0(b(ZyqhTUDzbl z{j+%xr0HV0BI4qI)KNulK2X4lrsYU%V-)dWZ&rF9&vz09M>K-!zTbT9T04i|AMLpWTF3fu{VJ{j#d@aV#j$=%SzyRCBUVsb~ZAGuzjkdQ0pK1?L zQ^QK1a0z)Mk02ky9fe=mOqq-@sK_h+M7;HSZ9KYQ)m)baG6r>Z0w~X#iyFE zx(iU)pUHw=E{Z8H;zgGy=Gw33XNoS&&MftXTIe|=6S$acOqhC~`hdLty7gT{ei)A9 zHL)**Q8mynynRXlZ)tazU-m%Lw0ifJfA#K1?L&ZL-KlQ)&077XA=e%`qO1?dS~q4+ zc&>nO=4_D%sDyKaUp+wNIormQiUff=(Uta(Z08Z zJHiNWr!7{!>8Ma$`9p}nbIqBmu^^k6w}gyju`_P>blQz-mLjv%aofCm0-2!iuJ+m9 z`6Yole1bJYms$6;hp03#`x268G5&cP_lK>Ws1dj?aA$cK7+dkz zh$k*?hL=ZVNQR#9X)cp;a?Ium_XrL;6N-x5n z*0!C(|1YXXh_E&sZUBiFIq;zf*{|&V{EIu2&x`i;wK>I|Fw1zEw1~#4Bm!o2z)_7@ zFlOS(7l2)1v&f9aAer!7<<}i{7cR|4|L(FhSleVCSQ!Cv+7bi&%0e8J#A)1__9cl^ z|8W?j#O1dtanX}xwr!Og4i|z})vY(**8O9PIn&jh{vD;l%=l&||Nn2F}MDrG7Oz@uCS=rHwQQ zClM51$=*`bJ|O#diFmIV9_pz#;O978Uw`u#iYOwVn&W~q-IDY)JZm)Zx%}{FA(}H# z3)-)tL1@dsgxFLdO$M2B+P5`BMK81*b+jU3dNa7j(M@k>5hGSb1l!%PICkx?o&`w6 z0m9AkMYqJewWCedeM&xLScp!hv(@5}4!5_fxf=f26INM!`4!}2ZI!}8Ng50raZsXJ z1d5+5lJGq{7)9|)T@5Su+Hx;#g4uFIo$AESQ6qZLxW9uM4BYZI*i4iHthWe>@tPk# z0L-I&zYePhnU5v}GgitHEJrkCEwiPmN<=D%{?{=_y-8a?`(77kmCDE}T zA5rv8q#fyM*E%9&+s2x{j`YSNf-#BE`{v0DkYMQK)^1MeAA|X4-gCJsGrvO^)c_5# zQ}=lgSu9`3>JRp+b6(zN$BX`Sa|?uP~*Ag*v;d${(_QknhdA* zQIt`w_F}?p@|$Y@tZY|`M(sm-O)qBjKPePbKNoad-ZvJ4)+fr6yMWWm&npw60S7&~8$QPzYa71uE3NSMlIuY=+8Foz9i{OHqASh18H3 z5^)s}vsv8jc#k&*P?Dnow}V8w>crxfeMF`J6?A4XJGF9I9Nl!SIrQAsZ%OfMaxZ%K zf-f^ppi(V5_R!aa_ul*c`W(TzfHV%X;Jx$Jv!sSR~0u{~bf!D5SC$w=yBQGN*b#SVdx2<5(X z%YWR^o#6~I-U8E|M1L`#{5r*}h&av7_$qrt^`o6&BRkq-lw7IMPkrb*=eGZ&7m(( zh?s=fX9a#)o*rICcDOrIp7{fSkv6urGx5BT z8Lg=I$UIPownxoOxi&nEG_C4qvh}2l82U^um`~PO$~M!g>4PS61IZ zNmt3ILM}kobg&yNPfi@FZ+FpiFM|_g|aM*o?5qXU+ipWldy}Aj-ISS_2;Ck zDMMOXxdMXO`td9Swyn8oavnGK-+yq7TVF2&OfQ(74x! z+4gx*UEbR0HHyj4fkd}Np?LaoN?t2%v61uoY62eO!aMDA5uJ=mHr?c32B>4^Q+&JY z^q7t_MBqGBL8YkEcT@|YL|A`Rvic44M)b|@T-@zK*Dc8M6^*-*@ z)YR3ocgzF!Y>Zfb={0bSAa>~s_)6}mw{2YDccgA~W`v)f(g6~|`>PB}3eD%87kE^6 z%Z*=Frq5i*QdXwAG%5AdqsXlRx-5h1)mNsytFLsJdw{>PKRiw{F%|EYdXJnwMJrF$ zsM7;?{u4c5^BNw23yYp+Y3C;Hm|^I*jC z%n{?g*hs~lE%nTA3H-QecD|!mIKic%Mu4~_6v6&eG{bkRmIgTwZw(ci+F>uDYSu6y z_w$kOw`c?kPFb6&?nQccZp{Ik1 zxH)&VktJQ@Kotu4qco@tS@Au9#LsEr=Hq|H=?4F8U0u9xps`L+y2yn3O>FI`;9 zNt@3r*b)nXo(tme)2qTz_aCPhP@VprZj8Qew|EY};Mt$C>9l`4K2j95t_~Zwdb%QG z=?7{$_8coR)%!?;#V;s)R$<(s`Xp^GutQ^cl0=L-9u6G*A|7sOnTQ|R4WxEoJSXRt zGm`eccdbJh*89}v!G~)bqpoSqYKhG5jsY%2-)Mom?fE$yrB}Wo<6bky4*uhBshgAP z$JDP*I6L;xgAyY|`4SoN{p;Y2s;ZtWlX2T=Gz|kR$N3KQ>2V4n8J@>LDXiCERj9>>d*-GBLbdKUQCv^e-WvO{8&qgz_@120Hk6@I*r zTe42!+}IiXTEn{DE>gsYw>h!8koW;%)I9Tm;GwXY?MkrjNpWD?ZaQ;Lgd7o{xN~k) zS4&~m-MhwXZ{A(VhZIP|=y50T`9h-7Uj2;u&%&T{1r58Ts+0(BH zA@7w(u3a)|?TLIaQ6aTDn$y!qx3)6L7g7s~l*73dqPENKoqz|=o7Cs$LCe`_r3#^Z(Fu^aLFG7sT;j-t zA2K#~v%HH-QDD6e3M8mPOwZY7rK@**+=Qn>s4qyiMEqOL@}td;o7l+t!CJyK$FfASY z=4|xBf{kpz;TMOV10vwk=wJf;w}cFzU|K-#z&-5?3ff(T#3WhQAHm|jRnG?I0Gpyb zjLmW-wpaJO+{Zoxn;!L!I5C#-(1neHWAH)Z7f^yKANJhoI#>!?QU|noZ_^u@! z5K_9wvS%(sUp!Cn&i>^F%xdc2fdRm*a;OCym(zd6oYh8fD#tCYr=f|QsW%?hOAR}K z-qv~BP6c+uWBEND=Z%Nl{@Z;PC=AsxyaICe8f;sO z^8iz>1T@W_gQW(HS*G0}E*Uu4#a|`q-Y@9T{rN^x9s2K2OcAyRKP)vMr!iTmMFbV6 z*srfBO$cA}B_kDlJzRz8(Vw)4fyqLofDFoI5JT_$9gJRUIOwkY9^gj4vAsCZmALKI z1%~<(LQ|*^)Z@j)Kz*^H8spgVr^tjk)o(Mf+Mvho6q?k9EQ(0Y0X=cT0N^UO9L5AZ zz!3KHs@K+*L@Z#(G^7VyQP=JCK_f3Ynin!}Zoo{WWCQ&b zBPnm}W!?$dy0_hx6%X-3P6;q+5Bb<@csu z>6BSL7%6%B*5U0@lA^z{fL4k%xT#E&!1-VMEn>KQs_9uC#A!P&$5h@k`Lv@E#Bj<|hkHi8X6V z7Okh-l4PYoD>EqwFMcvf@|acz^`>u+76!kUS#|x1;(?C$gY`A-**MQh|6$Qm4~Ii+ zsb#P{Zp^jL?7tOvlkorcFSQG)EFpfhHc@Z#;kuobrohbqv*<;8J6l`Pq=sdmZesC2 z-)Bl%xTi^1NwS$QMDX#QkeT#*>M@MnX`KYw>kO!;9&Z`xdQ@;qDrn2j$TzM~7dNf3 z01}fZ;}i!dZy#isH*3Jkkc764&!o~GkOR5%%IpTf1Zz8GiO1VQy2!S^{+&?+*wUHC z7i5*mTObAigRz?AIU(Z~zXSrVku2vv147UWhZbU6w4mi^`Lie`RKUF>_@hQ{|FnwbH4II1Qnz}2{yG@CPNb@kf_P5Gs=kF%q35|gL z0wq*_*SSvce$r)g3NRcQ+~A7wA!wX;8Uf^eP(6wprajq91g`%(Xtkb`1Cu6+75bXX z#N_G$fn;(5j`p>|uYQmwNyF{4a3R;AD8&ynfzL75as?AX3y~!N`ba+k0M>#e8pSU` z2(390aNuy<%!xZvI#`yvY*u$8{_yw*4$!PNg8pu9<)pHCWqLrB)1-J;qKx$-&&g1x z`c!ygfxxKR+vhtZ5*Rpd@E)_2S&rN7Neii#s#yQCz{CLLWL7|`NTvqtPU59N_iH4D zC9MS?nOGK}cSV32XFI^{?6EsU@K`m#0W;jEB&{PP;`KK-n@>_w2b5E-fb#*r5ugsD zd4AAl{@;>CJT6b$T^+}cnN-cIDFKYt<|8FBS7Jb}TfqGdl`4d6ntM>8s z>l>StCt|G}{0wxFclwj$ZDhBO2%K6bU_$}XhJY}jS(}l93$Ne4?EnLJ@SSl2z51Q_ zlYyQ5ngAf+1FbL(#z^ylwzHp{8l85hj-Sl74vb6($zyh8Db13KBcN-xE%~NQ(b*Fz zApJOkCGZ0z2Xnww={RmHEhxE6dAzuyQyVQj@x{egs{Qb#Ai722XU{$x`*QVwa3Umw z4XF93U;z$MQD;j8rDCvJ8~WP;xm02b#&293(BVnOHVhvXd((L<0uSFiO%DxC<8&EzDB zue3T&$^t+q7OBI(>{jG3-SNPq0zLYM^Nn=>S2Nch4&}DSzi(ztR4N%HVW^apt~LoX zDYUpiG?<3`@Cl`Bl8@Sm|~W?A)c)%%a$Uql1} z`5(?y>`lw?AM1m#KBL(XP69jyh4z;kYD0A*q(5q|3(U!JEuPWuyKOjxi#r8=c_B(P z*pdJdjEzN7AIKEvNrW|RK^DXB_v2PYW6_{$P6mWDl?%a?qUI*9+`mawe>*W(II3$E zwhQ9H^u~E7HcZ+SI2Hp43;|3u7l{UVCH|{eV`2U=C&MAc=iBn#EV7AFzhc@N{1_(x zJ(17jXk}+`{R~5r?If@s9C@4aNnc<8rEh7VHUwL`EbMYa2=BUXw-(C*|EqxJ`0^Jn^<BZ_o)JY% zZL{2bqp;SrZH=U%Px>(6_nz%vx-;T%aO?%I2|j3mkK@>p1gFHp!q&Xaa^MOm1fS=& zH(3ns_icS03vF$C!c0>Q!QN804?!H`{Wst{!Sc1-`0uIQRTqMCw1Z3Q}-7;%LTBPELM3^@hz>U*sbn@4hMU+#J$< z<4o}PU74?|gv;v!pEfI3?w6U0it&rmf=@O~M{(c)k-^|_{|v`~L6H&nDx(~Oyp2fzh(H@pK@lW5tk07aYBvo_?Is*bK&E6YR0JW_8;_3M`lwI zx!4OQn38Isc9sO@HPfX)O;u$>;NJ9f58BO{AlF(<@Sve~0|rQ=_HyTi6NQdjDAGeooCrcZd!mWeu}uluXnrwb;Tiwb zW17z%ob#*F?C0k$<87yv`=SUn`N9UonT=c)Tm&kvnp^sKFV8v+5QY|Ih4g|cPB?aH zpZ2{@JTmtb@_qxd3iut4l8)4B%|Va^J0II+;8Iwnj|{6|FUgJ5(*jhmM`Z61RU$&y zFF7F2i9swx*(;M(ureb=(zYJ))9PHk_+VX(=_+VB;dSiX1g?`yxe`aFNPq5tjn(P; zl`F>0vG5Y#f2c|>aU-Q?uV3op1(H*R4TviqY$kG19kbDCX{ZC~&cr_BOYYg&kD(F4hEQRB5~VmXCTOoR%U&) zAa+mtA-UX98B^rAzb8NAXU^n=Yo*^>e<*5Uxd`W_8!-FXx+eIlyiv|L9gYMwk+;4z z+P{Kr3Z}(1&*TjC%MSb@+E@7xy+rY^%KUQ8ty}W8THHZAm93?lffnw=*}!^4k|no} zz2=+M#Tu3$Et)E3nsfI@TILeEwfOG>=BbbnJ0n|J=~}C=mX@-zIC&SDv#mgxZ?0B% zPqQvujkBHbTyq;EXg@+?tp^IREk$$cnPDHw)LA|Zd4C5>1xZoFif?1P-FBdECr~#A z=|1G_X<469_EorqpdmT9vGyLeZ}+}Fw7C1dXFv4J(RnP`6d672AD?Bh8^ zl8Xc>Wgnb%fSFur25f&LsJT2QuzRO+L^ zvnMPu%l27ObzYuT@=)dJ1+cGc_Wm*?`_w?T+lqu8xqN<%t{t`|VLt+k?TlEcv#Asr(8x ze&`a8A^IvKa?qY$6W}X^gh#+9s^{6e;UmliS7L@RYM+0N^@|=#cSrzeF%#vo(=Y<{VB zdMdl5WrT434w|RGl@~Sra$q7M=s5wQ-xPli=^07>xc75RnOa0cR)jEub?m8k#n*z$ z!-L*!a-E(j%pVj&6A;Vc_IaAUZpvpFr1MYa6CL#yiwwvh@UlGiy@dk95gSWNgwUnZ zzse_eBZW0Aa~WKMWnDppB1EY2u-EtwTH-dBl&a6?&1EK`S+mSC)9>S}mOjLk9a;9l zS=0%)M2TD4NMTml@mFn)eK4$X2yese_}5zq1*N&<&VWXBW}YI2c`4HR zm^J@0VP_g3#DVs)l08+qd<{Rt5e2t2Y3d8v+7@V}R84+CXL!_bOp;GBi&HE;rc||v zz$iJQeE$V@2bUo}W?TkekfD3Wqzo?YLGD9AmOZwEj406ETz-?5_U~k$< z)CqJ|dDa^|-}CrxO{N+li(*#IxpEyaW&w}JUS(;Pzu$CcuZ0D9u{#oW3h#?`G4_}+mbO$>0eN(&eAx03{k=uSPRLuA>UV0|M5 z39fGT&IXdGmtFp5i7$kOCtfoSLeVT=C8h={*^XvSH#^O7pXwF@vwP`7*{65+-qU)A zO0MgrWyz#dY1Qln4kUrACNqf;v=;X9!%X@0T0X8+tJCZS_9TlMdYH91K4ngoEzBD` zN~@KNJMBb=ku+$@2qhxnpb9f7QhEz2hB3m})e;x!x#b%0Xl^V-u+Oc(~taYc{24N9<8^?`#)*dJS E4tDO9+yDRo diff --git a/x11-client-experimental/src/main/java/com/termux/x11/CmdEntryPoint.java b/x11-client-experimental/src/main/java/com/termux/x11/CmdEntryPoint.java deleted file mode 100644 index 70efe5e8e..000000000 --- a/x11-client-experimental/src/main/java/com/termux/x11/CmdEntryPoint.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.termux.x11; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; -import android.os.Looper; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.util.Log; - -import java.util.Arrays; - -import dalvik.system.PathClassLoader; - -public class CmdEntryPoint { - public static void main(String[] args) throws Exception { -// if (Arrays.stream(args).noneMatch(s->s.equals("asd"))) { -// Context ctx = android.app.ActivityThread.systemMain().getSystemContext(); -// PackageManager pm = ctx.getPackageManager(); -// ApplicationInfo target = pm.getApplicationInfo("com.termux.x11", 0); -// @SuppressLint("SdCardPath") -// String librarySearchPath = "/data/data/com.termux/files/usr/lib/:" + target.sourceDir + "!/lib/" + Build.SUPPORTED_ABIS[0] + "/"; -// PathClassLoader classLoader = new PathClassLoader(target.sourceDir, librarySearchPath, -// ClassLoader.getSystemClassLoader()); -// Class targetClass = Class.forName("com.termux.x11.CmdEntryPoint", true, classLoader); -// targetClass.getMethod("main", String[].class).invoke(null, (Object) new String[]{"asd"}); -// return; -// } -// -// System.loadLibrary("lorie"); - - System.err.println("CmdEntryPoint started"); - Context ctx = android.app.ActivityThread.systemMain().getSystemContext(); - - Intent intent = new Intent(); - Bundle bundle = new Bundle(); - intent.setPackage("com.termux.x11"); - intent.setAction("a"); - intent.putExtra("", bundle); - bundle.putBinder("", new ICmdEntryPointInterface.Stub() { - @Override - public ParcelFileDescriptor getConnectionFd() throws RemoteException { - int fd = connect(); - System.err.println("Sending fd " + fd); - return (fd == -1) ? null : ParcelFileDescriptor.adoptFd(fd); - } - }); - - ctx.sendBroadcast(intent); - - Looper.loop(); - } - - static native int connect(); - - static { - System.loadLibrary("lorie"); - } -} diff --git a/x11-client-experimental/src/main/java/com/termux/x11/MainActivity.java b/x11-client-experimental/src/main/java/com/termux/x11/MainActivity.java deleted file mode 100644 index e4f3e5a65..000000000 --- a/x11-client-experimental/src/main/java/com/termux/x11/MainActivity.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.termux.x11; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Bundle; -import android.util.Log; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; - -public class MainActivity extends AppCompatActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.main_activity); - BroadcastReceiver receiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - try { - Log.i("App", "Got intent"); - ICmdEntryPointInterface iface = ICmdEntryPointInterface.Stub.asInterface(intent.getBundleExtra("").getBinder("")); - int fd = iface.getConnectionFd().detachFd(); - Log.i("App", "Starting with fd "+ fd); - start(fd); - } catch (Exception e) { - e.printStackTrace(); - } - } - }; - registerReceiver(receiver, new IntentFilter("a")); - - SurfaceView v = findViewById(R.id.lorieView); - v.getHolder().addCallback(new SurfaceHolder.Callback() { - @Override - public void surfaceCreated(@NonNull SurfaceHolder holder) { - Log.e("asd", "surfaceCreated"); - surface(holder.getSurface(), 0, 0); - } - - @Override - public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) { - Log.e("asd", "surfaceChanged"); - surface(holder.getSurface(), width, height); - } - - @Override - public void surfaceDestroyed(@NonNull SurfaceHolder holder) { - Log.e("asd", "surfaceDestroyed"); - surface(null, 0, 0); - } - }); - } - - @Override - public void setTheme(int resId) { - super.setTheme(R.style.NoActionBar); - } - - @Override - public void onBackPressed() { - } - - private native void start(int fd); - private native void surface(Surface sfc, int width, int height); - - static { - System.loadLibrary("lorie"); - } -} diff --git a/x11-client-experimental/src/main/res/drawable-anydpi-v24/ic_x11_icon.xml b/x11-client-experimental/src/main/res/drawable-anydpi-v24/ic_x11_icon.xml deleted file mode 100644 index 3046d3563..000000000 --- a/x11-client-experimental/src/main/res/drawable-anydpi-v24/ic_x11_icon.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - diff --git a/x11-client-experimental/src/main/res/drawable-hdpi/ic_x11_icon.png b/x11-client-experimental/src/main/res/drawable-hdpi/ic_x11_icon.png deleted file mode 100644 index 2d86741b2357679b9d40ee9546aed0e3693e1c2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 786 zcmV+t1MU2YP)ali5yg!tbZ8*eq>{qI zh_G%@^nh#<4GM!Isp$RA^!Bg%FKl*uotJm!UGTx-?7jZI&VH=DPF`NlayAw$olZ{$ zS3sXdFPJh4-MQ3h2l-*0x2N59DYV!>y-DCH_yVe;d`&*Mssyzug4PB4t{KhJO$Tor z0sR1TLg?(EUq?3fQ@t`vk3mrY|Ak-(+y{wlZSoE!sFUC(7yw=17}yPVg3VwacnRKt znb|yb^T8t}s5a2!@F@Z}zzArT6PArYW`WBbxRrayw)Y$`VfnVP{SX)jR&fSrM-8UQ z2p|P!a2Z^73oJH#mw{oD8&r z7@*rn@PyI#BiPGlnZ3e*tFckW)EeUy&e&ETvGL#o`|bgs4d49%1FGbhaaUuRwFLD( zV$JN=>Bv#ChZY0~sF?lBT#Xf$=z0&KMI7`lsCDcov1f9_2gq|`71}2IWe6TuL`sb7 zpp@^DN-8+x6VPgo8PhuSSs8eu)m{Lub4HdM?-SsG64Y9cfc_SKUJ=(>Vo9t0VCFhd zHt1JJK%YRlqyI{_*ZFOBD>{C-iKPqbRp@zjb0-xo2KU z(dOfSUI}U++iR)SAEcf#k7JJ|D6`WTz;`luu3RnMEY?K3IP3*0CBqi0igP)UWXt+bRYrMQrG0lQ9}1yHIdu*wDIbk?SL?UUQ~4%`MCb%Pb@wDa!C zf;ve>;Dd{Qaok9XR_`R84nK;O7Wj3vOR8lwoEmV&Nwo85ikF-EDtQ9);GAltd)NxW3HalFZ-ZV?E#d~mDdI*~R|<6U zm2Kex$HZ{Gztd zMih~Syb1}eXfw2EGh0pT_8&MGhReOPymz0U?tvdZ-#v5YoI5jT&ddu7^TZQRJn&c7?;WH)Mltl2LdD4ud@cJ2Svf(r3ZcgIS?;d>Uw>4)1~sk{}wvAqQKd zKo{xxX&of7fXQyAn~q13;Q`LMt00Xw8@@@!Km*wh7b#7)nF3oiD^@eD z?xqdTmC~C_zmr5txi%fHRqS8_cNDw?4oXVzWX%B9QQKURQkG3;)r!0IEz*C(j)JU= zkfhcY%>WKiyDV94ZauWLDj2UKU6v_wI9nZ(AWAg?xJE6ST#Cb!TB`uqf=+GNT(4kdh;mQLrO~fP9Kqoo_`popke=gHBZjO_6Mcs()<~vOGwJ!jC-a&msDlj} zS2(=6e%)IHD%0B)7PUcrR=9Pn^wB}xD!;)QR$*!sd>N7pWAk3r=k}q#OrcYDyHEmV zIt@Eb&0P{(k7cP<*ZDd0wwauy68V~BQJS0JOO8Q9UFzI&o?Yy|;;%rsXDMI}rBuJ7 zgM*}did(2osWm@X=h^w7mCHWq68$(Dsvf diff --git a/x11-client-experimental/src/main/res/drawable-xxhdpi/ic_x11_icon.png b/x11-client-experimental/src/main/res/drawable-xxhdpi/ic_x11_icon.png deleted file mode 100644 index 8f9022f492a1b2040ef7cfbd5b35ad218bf174bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1670 zcmV;126_33P)V8P!SUYJNMrGF<(Fa!KHE5kJji8BDW zh1oJUj)5-*{v#0R7hrh;0xe>8tdDc>mBLl@DsW(o_FVw{TN!^>q_38T0fzwJ6^gVp zCfkkyey5Dv4LmuCcl;FK9|D2!7d<~>n+5`3VdKtBa+kP4xQccIhec@53O3%VLfHD- zgd(*?WXF|k^LQOtQV0Sa1^h-RQiEk1PNdy3ho6AQ6;c3s5wKq%(0<@pOJg4jd`IaU z?YN{6qwITytLQ^uCB`Sf4q_*8r}B3@MZiliDhE#FJ`Da|lf-TrM4%Gj+d`2r^nZ$d zwh>qlybgF1r7X*UkCK2rz%Nh;dPc>Iz@kFV)kXt<7m9Q(O&GDnaGY}BWEN)ZDJZ|= zVf0vFGjM@Wpr6rakY75_aIC|DpDKOyksm~%*V*!KwuCv2UX$DwyUgRY$p;MXuE#jW zaLkRY{cZGlDey)T>NyN%lN4q!X##c4=OhTIHF3VJd>J$(=sqRssJ~+TcnVnkO%NmLmA-xWu{DW7Qzu10K(w zT`pWjUjxrZp)>($7mKCNFsutT0{;Zw&R3gFVTyojH51zk9OTg^O>vC`n`xz#u(7wX z=jI9Rzk*g@i*=FcS6HK&&xBYgd^3jH)-Yf8L73tf?Usn;w#1pLDY5=HiGW*kKH&2sfIldoVH7>u!kjkI zoTi5DXgD+Qplw;$R~b2B6JQnaRN!mu^I4XD^fJo+G3(ES>F7Sq1oj45=Fnyms}<`o zlj0W@t1bK0#nkW0Fty!Y+x2CNwEeIecs2WMr|sewZUW>c_U8*u^C|3wQ6cNbZ)p1M zq@1qXqf}cKLFn=f_1_qz@tc}S?6F|jN#t-OU>HAm#FS*Nf%C?Iv%+#MJBLb1!|6UoHitB7 z28*k()G?l%r3vuWPg@dbn9}cbNy9oH16xBFglWgGN?+g8Zn<3;MBZ&lpixSnb&`hD ze2nV|XiO<({0Egc=(|eFkxbEVvXBBb zD*a86o>27q7&lpH$ZXbr8_%Yxq_ION(x#9C<>IkkaUhz>I>ZiYZR>|N84QPL z7NJO@tskB}M+t;+j-_EkadBh|-z{X%wwsnZu~Gp1SSZq6hJ6nLey&z8!#J^Gx<;Y{ z`j#rqFQjhUU;3RsXvRLB3hk0pkx2%1^>%herZ^SS(~o4q9mkJ28Fu=xTlpR*IX>Xn z3x%twOSkotIBH!T=QPq*&Qin9qrA?;CTdO}8bXXafKE(ArCN`E@J{6u$}>2Z8r!9T zS(@`Ob%%PMeYU~ExWn0{OcTsj+T%rsx7ZFG&E0M2_)@{`jZg5Bfu(V$2=8Y1P~NwI zxd*g;O4KK-<5+B?LyOcg=NY$IxQh4>iMK(MeRGNuSG)fd%~RONyMr5$fQezePk$KyNd1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/x11-client-experimental/src/main/res/layout/main_activity.xml b/x11-client-experimental/src/main/res/layout/main_activity.xml deleted file mode 100644 index 07d092926..000000000 --- a/x11-client-experimental/src/main/res/layout/main_activity.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - diff --git a/x11-client-experimental/src/main/res/layout/partial_primary_toolbar.xml b/x11-client-experimental/src/main/res/layout/partial_primary_toolbar.xml deleted file mode 100644 index 6124c04b2..000000000 --- a/x11-client-experimental/src/main/res/layout/partial_primary_toolbar.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/x11-client-experimental/src/main/res/layout/preferences_toolbar.xml b/x11-client-experimental/src/main/res/layout/preferences_toolbar.xml deleted file mode 100644 index 151d34ec5..000000000 --- a/x11-client-experimental/src/main/res/layout/preferences_toolbar.xml +++ /dev/null @@ -1,7 +0,0 @@ - - diff --git a/x11-client-experimental/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/x11-client-experimental/src/main/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 036d09bc5..000000000 --- a/x11-client-experimental/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/x11-client-experimental/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/x11-client-experimental/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 036d09bc5..000000000 --- a/x11-client-experimental/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/x11-client-experimental/src/main/res/mipmap-hdpi/ic_launcher.png b/x11-client-experimental/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index dcba64030acee4ea9d9b410f9b3fe91e94c83e91..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1927 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 E|HFx|IsgCw diff --git a/x11-client-experimental/src/main/res/mipmap-hdpi/ic_launcher_round.png b/x11-client-experimental/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index f8fbd7a1c717f5702803aaed47e5f4b530bfb0ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3279 zcmV;=3^4PFP)FHsnVSpL8&-u>G^z_`m^-WdX zs=D{KO4YfC1;J7W#P9!a4iwgkK~1lQ9{g`!Il?SSI7=Ph+cNZFaAWW$xY0S=R<>yt zhxo7aM{kC4467N+7|tXI zF_z(^L=c{gwrUVh8h`#I-CM3to$7!^t?^{2lp&_k2&CvjqYP1%)E8<0I{dKa-Y;YL zkEGM>NsKO{#CtxYKFRg9L&O^hMzIfrbHSk-%lRXcHe{-=#^G$m8>hW)2B~0@Z`x8*tTsOsMTt7(V|0KrBjh`lgx9}jo#+<}IM1`Qxlsi~=8 zX=!P2hr4(0E@mf2SQXnQfJ6kvx8X9WIsP>Ja4qH^SP#KnG(WOflsI07nmoHyxk#y$F8E|uR zGnk+?Yt}$liTD3|Lmj5H>&Yi21oTW>bf|HXI%$PAlnNBEEGbJ|jZ&^%_&oC$VzcFor~GXgd*Pk>_u ziEyYm8TOT=!p@QmC|H*TJ_80oSXdb3<>e_QNR;s>2G^E?N0GS&O36VW0gDzbk~Ova z_wPeQM1&%OHWU}Zr`gLOGT5)#kKyY`KX>?gT`X)VUJsj#^I=!|ij-;)PANG_IMMLj;|v+O^Z|WAO-+p! zNvK)qd{NWn2+GMwhx3V}peEK0PNc?Y2pt_At?@fQKRL;JZWuwl%+YdYCVogaM#hf*XyL+zf`gWlbno6hAy$#bM@Z0)!D})2!|X=Y&J;bSL;OX#{L6(vEp%&z>!q8u~J9AN>*fAoNAjYd0|Sy0q_o zhBHtRb=7Vv`i4!NZ2tph`eq4xcc!G|<^#&uz8LlLSL5YMQjC1<+ z>n8+^_V)G~DN15uqBcQn6uig?ii`A7O3(!c8-D4H=#1s4{L0)W$f&KjsaA9l04A<~4xDIheX&oD&rlrQwe-9l_|EC+I_FVmFS= zhv=~ZN;7<1Fwui|)M8N7ms?z5G=i+GtRN{VN!A}>d^C0H)MkRVrYwM)Z}o?oIqvXf zTGPmc`PG;)W5C1112Qu+wfs?Yd^B>TaIHwtfltz*>S#QKj|f&u(0ysZXvx)nela#d zB1v1fZk3IXu3ftZ0RaI5LD^Xu@YT{8Q1iA2+)D6){aHycGiDm}>eUNYtXQF;A?TdZ zg!zF1BkyA7kM`|Y4b^|03*|d94ISornd=yQ

(c6~~M|ZS3mm3a3w>mW_{&9617m z1`QJ8pe+Tt@XgXGaBIE~{P><96pZtLq?u8IPvHqd$DELm(46O4GA$bH?F?nb3Gl;# zx8d-oD+EU!s(hG74g%!}@@-FoFsCXlEtPfH)z#I4AYvqqBiLV-#lhd0x!wKUK&V;~ z3MX^sz-I-iP+GJWN{XA}o)Sh{OypP?>Dv=BW_ZHMybw6}K?r=jDN|{xAtmT_IfA@M zy^L;R2pnl1J$fYThtM>zA|Mi!lam9%!9g%OXb9}iNQCp5QSi^K2&m2(1vm2};D-(4 zplZuRsQPFMTq})&vjt;d{hR?1>hl_Ow^PG}2@@bcKVK<9qRdYi{CI-IQOv15oHPOp zUN>&skR8d;RB%M%bFq|^6v63=eh1BKdto;0EL;Vj6)l5(#VN3tW%6YkSHd4Qu7R}~ z%Y-W8uwlalj2=B&D899lp!?*HRnlR;M|%wO81-V|OwP0_Dk=mAjMrgKHGlqmA@4FK z{Qdov5>!n&sRluaZVZ1nW}t`;6h|*~#8PrlqtLAQk*-^}ZbIN^OvvSZdIH5uG*POt zMjNB(E)3me4n?)%y6EpuC7jtoZ8x2p}1w@t3Gfpu&1t? zG@k6rEmlyZLFiL37Qw(z&hM;Txl+l9(pegmNgT*7uCkyn22-%4h$J+fbLY;<=33?D z<-!nd;^aJ}g;mz77IqZbP>`gkvWW4*6`N}4)H4QnOO`AVR&~hv9jrgM=AXqH(5X|Wi~_7`;*6`FspnXdUGD-0+@sZx z;j|%IgcE{~A3v7Ov#@ZlWQ7M03?${!lhy$1P*}2UL3_TXT-4Uk`gtN6Pf;UV*I$SaXgcnYtQlzXn#=Tv<@KfOBx>xS6Ga#bxiSn)c<}tBm-N0t?D= zJ=hIWIFjnk$gGf@G%CnDsM_HP4wlrg7t^G7xmj3q*ictTn&UE!IhxWd%7WH!%Iu)h9Q)wC13x2gknL0i(lfYrwkZo#?uk2Q88F^v+9K1FN3-P>Dp zO?adGr$ zXlGNBj;6<0Um~ft>_mO+)JN*5$+r75_%nM?Vy|Z<#58bZHMUVx8f1va_h;F?@qs7) z)R*2W6MEQ*1s71_ND;_7JCD8Yr(A7E9P~i3$FWUpn>5CY?rErt zo7m@rv|#dknv461Vyj>1{T=!auQ`an!QWyVq%FQ=N{)0dvwFx99}?J7fayqU|1g6d zgrlFewe?Vj5EPnW5+h&=0^j2~_FXVuqiWicK8SwnN^Ln%+h+ZkE-6_y^l$f~MWg-5 zG+ap=+zI{ZoD*HsmwwZO+IVG1(dXF3hqYOgHrNqt>6}{EG;l{9{{?Iu9j-QkCP@GQ N002ovPDHLkV1gAbMS%bS diff --git a/x11-client-experimental/src/main/res/mipmap-mdpi/ic_launcher.png b/x11-client-experimental/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 404d8c149b258aad886edef71119bba506144b08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1286 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 diff --git a/x11-client-experimental/src/main/res/mipmap-mdpi/ic_launcher_round.png b/x11-client-experimental/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index b6ff6e2bf2f4410a1e719fd313fce4e6dd02541e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2023 zcmV20Gl&V1~gr*_3rT%IZZ7OPFZ4g^f1G1dnb93Lz#*#Sn;wUQ0jR%i(nqX0)~0)ncSshZCRE05`r{>69jDpcL*L6 zJY{&yp0%+uX{?ULg{UN>oJ4SyV1NM3!~m;{Z8+-bW7;kSXW#gD2ElC!p)rC{6EP;K zdz-bX;AUgm{DXJQ5Q0vwVLS$qlG&VMh$j{FQS65d{i4Yw7 z2FKCHkm;mz>S?rba&m&!)>ar98G+H!QHuKddQhv?dONnaw}-&MKv=$fIe2?}Q~3G$ zK}bl5EU>IrtA(JTAaHSUky{|~@16QgNl@g#t*~O8S11$^92^YY-QA*qCr_ThmMvSr z#>PfgfJUQ%f`S6rvu6(_k28iOi;9Y%s;X)n%F4=M&6+iknwkowrKRBO>npcT;Juq* zw%jZTijoZ3eR6U#^!N9RBJSV6Pe)CkfWpE;sI07n*w|Pq)YjG(qN1WE2-vuBBZP&8 z(Xx63@SL_uXMqn*S0#2g4t92SP*PGN3P4=HejPkLJ@pDeg1VA}5V69K&ZL9A9aJT) zg0Ctv;cWGO$o)7G!rl&m@}gW?)_?$k(=~4N75oCRn9C~lMxNQTXG2?Co9GWiLqpKe z&;TlxN>)H)Ss}D!#lrdYXwbajM&B)Re;vy5cGLIhBUw8$;8a}!oUAW`@-m|{fE<1K z#Z3rH+c>8&7#XnBTeohBv+(TMGe}8Ep(er$D9zgszb6O5kC{<$xT2hvaddQ~?}GyU zAv81;5)%`t>EP&}X()hGpYGKsfajLUhg4w(PMR9`(G=q2R3%5Sf!ts(EO0c4$0@kiwOJ~d7-JKG}XM!(OS69L}$1)+pM6&Kt@K!IETK_ zm<>_kUd9CcEDfcz*p|<%l^01gH#ds{&@axNI|oXo65NTsUrq>uKQ{Zqk%gYl5G&iNgyCL(93HX!1g%{vK@b_d=2lR)ot}ao;^XJbYJv|*f z39+L)5}{|~LTK3?3xzrRz~A2=va+%$(WIm#>JMm+xRBvmaQ;X>w0^z~{NB4J|3)$t}@$lEyWpFyq7j~p2iX*Ml>0tHh)wKRB zM|;p^#lbHZwn9Vo0YgqKaKnRwrM!T}lNBI1^otiSL;*cLJ+N-wIvOSq9XbR%Vpc)( z)=;>e^A6lN_yPP-kqY0|WDslz-KJ&GuyrZyid_Kv_huVf2c!agc>!;-X)#+sKtKT8 zym@n6NZr1DJGE_0C316f>1^l@7Q!*z0XV12hOa9K`RWX~bT|uYi!&kmy_K+HMIa<3 zBtUU-u_*%nVOAqvE|YO$OxUhmxgwGu9v+6%r%%)M03!^B)s-t(nuYN2aBy>TGsTJL z@B(HM{A7**Bwtfg1212`6a}CdjPDlMA9S+BQ%s5wXG|4*49sr{f$KGgb?2 za4xV_SSc*W3YKdN%k`KsyXRBufq?;osr59TS_r)MG0x*si2#ECjLm|xvomybbco5^ z;NT$C)zwW%=3e3G_Y;KjDYb%Ija!YS)8*ymF(XTBKh9lUf8^O zvq^T3(F#)xb8z(NQMoPU9WI5ik=xYU5fn&O<0huPyyw}#&nr!!2mNz?7(A3l< zH~J-vqtei^3Vu!bmAAWyhzLqTKu%7MS;s1vEAjPB!G;YRq_*Eh;A!lbQOOpyID#Rm zDOmwR-^e9_aWnb>1Vy|?P$6?JW9d^l!E|aEC!%9Uu94$5ykAkCu#3Ik#fV zIiE2|>vfYhb>zN`^`U-MKSNNlX`PGNE9ta=C0GAW!gR1sJ8L6=wP{kEpN`qUPV5OZ z1U>}Ogh(-SyiS(0JR<02c*LIJyGyKWG&@<)u(njz`ptxjo!M)c<7o+25JVEZ&k)I; zX;~Q!tD~~Uk3HmOM9J({&CL$}{=tdCksD<-M|{e5{yegaFUD2)IB002ovPDHLk FV1n4%)x!V) diff --git a/x11-client-experimental/src/main/res/mipmap-xhdpi/ic_launcher.png b/x11-client-experimental/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 17efe24c0bb1dea20eef0e3737a43a5146becd62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2723 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�#Z{`p>N;5MQjVI#MszaX#`s${24Q5K<(PK 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 diff --git a/x11-client-experimental/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/x11-client-experimental/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png deleted file mode 100644 index c1283789a2d365531e0cdf6b1dfd86928e3e24f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7071 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 diff --git a/x11-client-experimental/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/x11-client-experimental/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 90d86bc54273d0e9d2cfca8852be7f2231e46147..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6124 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 diff --git a/x11-client-experimental/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/x11-client-experimental/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 0aae7116dacffc544fef4f62232b437719cab31b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10839 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 - - - Emulate mouse - Emulate touchpad - - - 0 - 1 - - \ No newline at end of file diff --git a/x11-client-experimental/src/main/res/values/colors.xml b/x11-client-experimental/src/main/res/values/colors.xml deleted file mode 100644 index 01baea95b..000000000 --- a/x11-client-experimental/src/main/res/values/colors.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - #008577 - #00574B - #D81B60 - #DC143C - #FC143C - diff --git a/x11-client-experimental/src/main/res/values/dimens.xml b/x11-client-experimental/src/main/res/values/dimens.xml deleted file mode 100644 index 7605196c6..000000000 --- a/x11-client-experimental/src/main/res/values/dimens.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - 16dp - 4dip - \ No newline at end of file diff --git a/x11-client-experimental/src/main/res/values/ic_launcher_background.xml b/x11-client-experimental/src/main/res/values/ic_launcher_background.xml deleted file mode 100644 index beab31f75..000000000 --- a/x11-client-experimental/src/main/res/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #000000 - \ No newline at end of file diff --git a/x11-client-experimental/src/main/res/values/strings.xml b/x11-client-experimental/src/main/res/values/strings.xml deleted file mode 100644 index ddfc11c20..000000000 --- a/x11-client-experimental/src/main/res/values/strings.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - ]> - - &TERMUX_X11_APP_NAME; - &TERMUX_X11_APP_NAME; is a plugin app for the &TERMUX_APP_NAME; app. - \n\nCheck &TERMUX_APP_NAME; app github %1$s and &TERMUX_X11_APP_NAME; app github %2$s for more info. - - - The storage permission must be granted - to &TERMUX_X11_APP_NAME; app to... - Grant Storage Permission - - Android battery optimizations - should be disabled for the &TERMUX_X11_APP_NAME; app so that… - Check https://developer.android.com/about/versions/oreo/background for more info and - https://developer.android.com/guide/components/foreground-services#background-start-restrictions - for more info. - - \n\nAlso check https://dontkillmyapp.com for info on vendor specific app killers. - Depending on vendor you may need to do things like enable AutoStart, disable DuraSpeed, - enable `Display pop-up windows while running in the background` for the app. - Disable Battery Optimizations - - The display over other - apps permission should be granted to &TERMUX_X11_APP_NAME; app for starting foreground - activities from background. Check https://developer.android.com/guide/components/activities/background-starts - for more info. - - Grant Draw Over Apps Permission - - Already Granted - Already Disabled - Not conected - Getting started - Preferences - diff --git a/x11-client-experimental/src/main/res/values/styles.xml b/x11-client-experimental/src/main/res/values/styles.xml deleted file mode 100644 index 92cf37110..000000000 --- a/x11-client-experimental/src/main/res/values/styles.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/x11-client-experimental/src/main/res/xml/preferences.xml b/x11-client-experimental/src/main/res/xml/preferences.xml deleted file mode 100644 index 812cb887b..000000000 --- a/x11-client-experimental/src/main/res/xml/preferences.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/x11-client-experimental/src/main/res/xml/shortcuts.xml b/x11-client-experimental/src/main/res/xml/shortcuts.xml deleted file mode 100644 index d48471eaa..000000000 --- a/x11-client-experimental/src/main/res/xml/shortcuts.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - From 388e79aecf92cc7f68d71c9eb2877dc81c42a6a2 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Sat, 25 Feb 2023 23:58:59 +0200 Subject: [PATCH 02/21] Reconnecting to existing session. --- app/src/main/AndroidManifest.xml | 1 + .../main/cpp/lorie-client/lorie-client.cpp | 444 ++++++++---------- app/src/main/cpp/lorie/compositor.cpp | 9 +- .../java/com/termux/x11/CmdEntryPoint.java | 49 +- .../java/com/termux/x11/MainActivity.java | 73 ++- 5 files changed, 295 insertions(+), 281 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 125a85dee..8d836b139 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + diff --git a/app/src/main/cpp/lorie-client/lorie-client.cpp b/app/src/main/cpp/lorie-client/lorie-client.cpp index 9cd2b11dc..c4dc09681 100644 --- a/app/src/main/cpp/lorie-client/lorie-client.cpp +++ b/app/src/main/cpp/lorie-client/lorie-client.cpp @@ -239,119 +239,6 @@ class xcb_connection { } } fixes {*this}; - struct { - xcb_connection& self; - xcb_randr_get_screen_resources_reply_t* res{}; - const char* temporary_name = "temporary"; - void init() { - { - auto reply = xcb(randr_query_version, 1, 1); - self.handle_error(reply, "Error querying RANDR extension"); - free(reply); - } - - refresh(); - } - - void refresh() { - auto screen = xcb_setup_roots_iterator(xcb_get_setup (self.conn)).data; - free(res); - res = xcb(randr_get_screen_resources, screen->root); - self.handle_error(res, "Error during refreshing RANDR modes."); - } - - xcb_randr_mode_t get_id_for_mode(const char *name) { - refresh(); - char *mode_names = reinterpret_cast(xcb_randr_get_screen_resources_names(res)); - auto modes = xcb_randr_get_screen_resources_modes(res); - for (int i = 0; i < xcb_randr_get_screen_resources_modes_length(res); i++) { - auto& mode = modes[i]; - char mode_name[64]{}; - snprintf(mode_name, mode.name_len+1, "%s", mode_names); - mode_names += mode.name_len; - - if (!strcmp(mode_name, name)) { - return mode.id; - } - } - return 0; - } - - void create_mode(const char* name, libxcvt_mode_info *mode_info) { - bool is_temporary = strcmp(name, temporary_name) == 0; - if (!is_temporary && get_id_for_mode(name)) - delete_mode(name); - - if (is_temporary && get_id_for_mode(name)) - return; - - auto screen = xcb_setup_roots_iterator(xcb_get_setup (self.conn)).data; - xcb_randr_mode_info_t mode{}; - mode.width = mode_info->hdisplay; - mode.height = mode_info->vdisplay; - mode.dot_clock = mode_info->dot_clock * 1000; - mode.hsync_start = mode_info->hsync_start; - mode.hsync_end = mode_info->hsync_end; - mode.htotal = mode_info->htotal; - mode.vsync_start = mode_info->vsync_start; - mode.vsync_end = mode_info->vsync_end; - mode.vtotal = mode_info->vtotal; - mode.mode_flags = mode_info->mode_flags; - mode.name_len = strlen(name); - { - auto reply = xcb(randr_create_mode, screen->root, mode, mode.name_len, name); - self.handle_error(reply, "Failed to create RANDR mode"); - } - - refresh(); - xcb_randr_mode_t mode_id = get_id_for_mode(name); - if (!mode_id) { - throw std::runtime_error("Failed to find RANDR mode we just created"); - } - xcb_check(randr_add_output_mode_checked, xcb_randr_get_screen_resources_outputs(res)[0], mode_id); - self.handle_error("Failed to add RANDR mode we just created to screen"); - } - - void delete_mode(const char* name) { - xcb_randr_mode_t mode_id = get_id_for_mode(name); - if (mode_id) { - xcb_check(randr_delete_output_mode_checked, xcb_randr_get_screen_resources_outputs(res)[0], mode_id); - self.handle_error("Failed to detach RANDR mode from output"); - - xcb_check(randr_destroy_mode_checked, mode_id); - self.handle_error("Failed to destroy RANDR mode we just detached from output"); - - refresh(); - } - } - - void switch_to_mode(const char *name) { - xcb_randr_mode_t mode_id = XCB_NONE; - xcb_randr_output_t* outputs{}; - int noutput = 0; - if (name) { - mode_id = get_id_for_mode(name); - if (mode_id == 0) return; - outputs = xcb_randr_get_screen_resources_outputs(res); - noutput = xcb_randr_get_screen_resources_outputs_length(res); - } - - ALOGE("crts len %d", xcb_randr_get_screen_resources_crtcs_length(res)); - - auto reply = xcb(randr_set_crtc_config, xcb_randr_get_screen_resources_crtcs(res)[0], XCB_CURRENT_TIME, - res->config_timestamp, 0, 0, mode_id, XCB_RANDR_ROTATION_ROTATE_0, - noutput, outputs); - self.handle_error(reply,"Failed to switch RANDR mode"); - }; - - void set_screen_size(u16 width, u16 height, u32 mm_width, u32 mm_height) { - auto screen = xcb_setup_roots_iterator(xcb_get_setup (self.conn)).data; - xcb_check(randr_set_screen_size_checked, screen->root, width, height, mm_width, mm_height); - self.handle_error("Failed to set RANDR screen size"); - } - - } randr {*this}; - void init(int sockfd) { xcb_connection_t* new_conn = xcb_connect_to_fd(sockfd, nullptr); int conn_err = xcb_connection_has_error(new_conn); @@ -415,10 +302,14 @@ os_create_anonymous_file(size_t size) { static always_inline uint32_t* cast(void* p) { union { void* a; uint32_t* b; } c {p}; return c.b; } // NOLINT(cppcoreguidelines-pro-type-member-init) static always_inline void blit_exact(ANativeWindow* win, const uint32_t* src, int width, int height) { + if (!win) + return; + if (width == 0 || height == 0) { width = ANativeWindow_getWidth(win); height = ANativeWindow_getHeight(win); } + ARect bounds{ 0, 0, width, height }; ANativeWindow_Buffer b{}; @@ -465,15 +356,17 @@ class lorie_client { xcb_connection c; struct { - ANativeWindow* win{}; - u32 width{}; - u32 height{}; + ANativeWindow* win; + u32 width, height; - i32 shmfd{}; - xcb_shm_seg_t shmseg{}; - u32 *shmaddr{}; - } screen; - ANativeWindow* cursor{}; + i32 shmfd; + xcb_shm_seg_t shmseg; + u32 *shmaddr; + } screen {}; + struct { + ANativeWindow* win; + u32 width, height, x, y, xhot, yhot; + } cursor{}; lorie_client() { @@ -501,131 +394,128 @@ class lorie_client { if (screen.win) ANativeWindow_release(screen.win); + if (win) + ANativeWindow_acquire(win); screen.win = win; screen.width = width; screen.height = height; - - if (c.conn) - change_resolution(width, height); } - void change_resolution(u16 width, u16 height) { - char mode_name[128]{}; - - auto mi = libxcvt_gen_mode_info(width, height, 60, false, false); - ALOGE("Changing resolution to %dx%d", mi->hdisplay, mi->vdisplay); - sprintf(mode_name, "TERMUX:X11 %dx%d", mi->hdisplay, mi->vdisplay); + void cursor_changed(ANativeWindow* win) { + if (cursor.win) + ANativeWindow_release(cursor.win); - int mm_width = int(25.4*width/120); - int mm_height = int(25.4*height/120); - - xcb_grab_server(c.conn); - try { - ALOGE("line %d", __LINE__); - c.randr.create_mode(c.randr.temporary_name, mi); - c.randr.switch_to_mode(nullptr); - ALOGE("line %d", __LINE__); - c.randr.set_screen_size(mi->hdisplay, mi->vdisplay, mm_width, mm_height); - c.randr.switch_to_mode(c.randr.temporary_name); - ALOGE("line %d", __LINE__); - c.randr.delete_mode(mode_name); - c.randr.create_mode(mode_name, mi); // NOLINT(cppcoreguidelines-narrowing-conversions) - ALOGE("line %d", __LINE__); - c.randr.switch_to_mode(mode_name); - c.randr.delete_mode(c.randr.temporary_name); - ALOGE("line %d", __LINE__); - } catch (std::runtime_error& e) { - xcb_ungrab_server(c.conn); - throw e; - } - xcb_ungrab_server(c.conn); + if (win) + ANativeWindow_acquire(win); + cursor.win = win; } void adopt_connection_fd(int fd) { - ALOGE("Connecting to fd %d", fd); - c.init(fd); - xcb_screen_t* scr = xcb_setup_roots_iterator(xcb_get_setup(c.conn)).data; - - xcb_change_window_attributes(c.conn, scr->root, XCB_CW_EVENT_MASK, (const int[]) { XCB_EVENT_MASK_STRUCTURE_NOTIFY }); - c.fixes.select_input(scr->root, XCB_XFIXES_CURSOR_NOTIFY_MASK_DISPLAY_CURSOR); - c.damage.create(scr->root, XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES); - struct { - xcb_input_event_mask_t head; - xcb_input_xi_event_mask_t mask; - } mask{}; - mask.head.deviceid = c.input.client_pointer_id(); - mask.head.mask_len = sizeof(mask.mask) / sizeof(uint32_t); - mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_MOTION; - c.input.select_events(scr->root, 1, &mask.head); - - screen.shmseg = xcb_generate_id(c.conn); - - if (screen.shmaddr) - munmap(screen.shmaddr, DEFAULT_SHMSEG_LENGTH); - if (screen.shmfd) - close(screen.shmfd); - - ALOGE("Creating file..."); - screen.shmfd = os_create_anonymous_file(DEFAULT_SHMSEG_LENGTH); - if (screen.shmfd < 1) { - ALOGE("Error opening file: %s", strerror(errno)); - } - fchmod(screen.shmfd, 0777); - ALOGE("Attaching file..."); - screen.shmaddr = static_cast(mmap(nullptr, 8096*8096*4, - PROT_READ | PROT_WRITE, - MAP_SHARED, screen.shmfd, 0)); - if (screen.shmaddr == MAP_FAILED) { - ALOGE("Map failed: %s", strerror(errno)); - } - c.shm.attach_fd(screen.shmseg, screen.shmfd, 0); - - if (screen.win) { - change_resolution(screen.width, screen.height); - } - - int event_mask = ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT | ALOOPER_EVENT_INVALID | ALOOPER_EVENT_HANGUP | ALOOPER_EVENT_ERROR; - ALooper_addFd(ALooper_forThread(), fd, ALOOPER_EVENT_INPUT, event_mask, [](int, int mask, void *d) { - auto self = reinterpret_cast(d); - if (mask & (ALOOPER_EVENT_INVALID | ALOOPER_EVENT_HANGUP | ALOOPER_EVENT_ERROR)) { - xcb_disconnect(self->c.conn); - self->c.conn = nullptr; - ALOGE("Disconnected"); - return 0; + try { + ALOGE("Connecting to fd %d", fd); + c.init(fd); + xcb_screen_t *scr = xcb_setup_roots_iterator(xcb_get_setup(c.conn)).data; + + xcb_change_window_attributes(c.conn, scr->root, XCB_CW_EVENT_MASK, + (const int[]) {XCB_EVENT_MASK_STRUCTURE_NOTIFY}); + c.fixes.select_input(scr->root, XCB_XFIXES_CURSOR_NOTIFY_MASK_DISPLAY_CURSOR); + c.damage.create(scr->root, XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES); + struct { + xcb_input_event_mask_t head; + xcb_input_xi_event_mask_t mask; + } mask{}; + mask.head.deviceid = c.input.client_pointer_id(); + mask.head.mask_len = sizeof(mask.mask) / sizeof(uint32_t); + mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_MOTION; + c.input.select_events(scr->root, 1, &mask.head); + + screen.shmseg = xcb_generate_id(c.conn); + + if (screen.shmaddr) + munmap(screen.shmaddr, DEFAULT_SHMSEG_LENGTH); + if (screen.shmfd) + close(screen.shmfd); + + ALOGE("Creating file..."); + screen.shmfd = os_create_anonymous_file(DEFAULT_SHMSEG_LENGTH); + if (screen.shmfd < 1) { + ALOGE("Error opening file: %s", strerror(errno)); } - - self->connection_poll_func(); - return 1; - }, this); + fchmod(screen.shmfd, 0777); + ALOGE("Attaching file..."); + screen.shmaddr = static_cast(mmap(nullptr, 8096 * 8096 * 4, + PROT_READ | PROT_WRITE, + MAP_SHARED, screen.shmfd, 0)); + if (screen.shmaddr == MAP_FAILED) { + ALOGE("Map failed: %s", strerror(errno)); + } + c.shm.attach_fd(screen.shmseg, screen.shmfd, 0); + + int event_mask = ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT | ALOOPER_EVENT_INVALID | + ALOOPER_EVENT_HANGUP | ALOOPER_EVENT_ERROR; + ALooper_addFd(ALooper_forThread(), fd, ALOOPER_EVENT_INPUT, event_mask, + [](int, int mask, void *d) { + auto self = reinterpret_cast(d); + if (mask & (ALOOPER_EVENT_INVALID | ALOOPER_EVENT_HANGUP | + ALOOPER_EVENT_ERROR)) { + xcb_disconnect(self->c.conn); + self->c.conn = nullptr; + self->set_renderer_visibility(false); + ALOGE("Disconnected"); + return 0; + } + + self->connection_poll_func(); + return 1; + }, this); + + set_renderer_visibility(true); + } catch (std::exception& e) { + ALOGE("Failed to adopt X connection socket: %s", e.what()); + } } void connection_poll_func() { - xcb_generic_event_t *event; - const char* ext; - xcb_screen_t* s = xcb_setup_roots_iterator(xcb_get_setup(c.conn)).data; - - while((event = xcb_poll_for_event(c.conn))) { - if (event->response_type == 0) { - c.err = reinterpret_cast(event); - c.handle_error("Error processing XCB events"); - } else if (event->response_type == XCB_CONFIGURE_NOTIFY) { - ALOGE("Configure notification. "); - auto e = reinterpret_cast(event); - ALOGE("old w: %d h: %d", s->width_in_pixels, s->height_in_pixels); - ALOGE("new w: %d h: %d", e->width, e->height); - s->width_in_pixels = e->width; - s->height_in_pixels = e->height; - } else if (c.damage.is_damage_notify_event(event)) { - try { - c.shm.get(s->root, 0, 0, s->width_in_pixels, s->height_in_pixels, ~0, // NOLINT(cppcoreguidelines-narrowing-conversions) - XCB_IMAGE_FORMAT_Z_PIXMAP, screen.shmseg, 0); - - blit_exact(screen.win, screen.shmaddr, s->width_in_pixels, s->height_in_pixels); - } catch (std::runtime_error &err) { - continue; - } - } //else - // ALOGE("some other event %s of %s", xcb_errors_get_name_for_core_event(c.err_ctx, event->response_type, &ext), (ext ?: "core")); + try { + xcb_generic_event_t *event; + const char *ext; + xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(c.conn)).data; + while ((event = xcb_poll_for_event(c.conn))) { + if (event->response_type == 0) { + c.err = reinterpret_cast(event); + c.handle_error("Error processing XCB events"); + } else if (event->response_type == XCB_CONFIGURE_NOTIFY) { + auto e = reinterpret_cast(event); + s->width_in_pixels = e->width; + s->height_in_pixels = e->height; + } else if (c.damage.is_damage_notify_event(event)) { + try { + c.shm.get(s->root, 0, 0, s->width_in_pixels, s->height_in_pixels, + ~0, // NOLINT(cppcoreguidelines-narrowing-conversions) + XCB_IMAGE_FORMAT_Z_PIXMAP, screen.shmseg, 0); + blit_exact(screen.win, screen.shmaddr, s->width_in_pixels, + s->height_in_pixels); + } catch (std::runtime_error &err) { + continue; + } + } else if (c.fixes.is_cursor_notify_event(event)) { + if (cursor.win) { + auto reply = c.fixes.get_cursor_image(); + if (reply) { + cursor.width = reply->width; + cursor.height = reply->height; + cursor.xhot = reply->xhot; + cursor.yhot = reply->yhot; + u32* image = xcb_xfixes_get_cursor_image_cursor_image(reply); + blit_exact(cursor.win, image, reply->width, reply->height); + } + free(reply); + } + } //else + // ALOGE("some other event %s of %s", xcb_errors_get_name_for_core_event(c.err_ctx, event->response_type, &ext), (ext ?: "core")); + } + } catch (std::exception& e) { + ALOGE("Failure during processing X events: %s", e.what()); } } @@ -635,22 +525,98 @@ class lorie_client { runner_thread.join(); close(queue.get_fd()); } -} lorie_client; // NOLINT(cert-err58-cpp) + + JNIEnv* env{}; + jobject thiz{}; + jmethodID set_renderer_visibility_id{}; + jmethodID set_cursor_rect_id{}; + void init_jni(JavaVM* vm, jobject thiz) { + queue.write([=, this] { + this->thiz = thiz; + vm->AttachCurrentThread(&env, nullptr); + set_renderer_visibility_id = + env->GetMethodID(env->GetObjectClass(thiz),"setRendererVisibility","(Z)V"); + set_cursor_rect_id = + env->GetMethodID(env->GetObjectClass(thiz),"setCursorRect","(IIII)V"); + }); + } + + void set_renderer_visibility(bool visible) const { + if (!set_renderer_visibility_id) { + ALOGE("Something is wrong, `set_renderer_visibility` is null"); + return; + } + + env->CallVoidMethod(thiz, set_renderer_visibility_id, visible); + } +} client; // NOLINT(cert-err58-cpp) extern "C" JNIEXPORT void JNICALL -Java_com_termux_x11_MainActivity_start(unused JNIEnv *env, unused jobject thiz, jint fd) { - lorie_client.post([fd] { lorie_client.adopt_connection_fd(fd); }); +Java_com_termux_x11_MainActivity_init(JNIEnv *env, jobject thiz) { + // Of course I could do that from JNI_OnLoad, but anyway I need to register `thiz` as class instance; + JavaVM *vm; + env->GetJavaVM(&vm); + client.init_jni(vm, env->NewGlobalRef(thiz)); } extern "C" JNIEXPORT void JNICALL +Java_com_termux_x11_MainActivity_connect(JNIEnv *env, [[maybe_unused]] jobject thiz, jint fd) { + client.post([fd] { client.adopt_connection_fd(fd); }); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_termux_x11_MainActivity_cursorChanged(JNIEnv *env, [[maybe_unused]] jobject thiz, jobject sfc) { + ANativeWindow *win = sfc ? ANativeWindow_fromSurface(env, sfc) : nullptr; + if (win) + ANativeWindow_acquire(win); + + ALOGE("Cursor: got new surface %p", win); + client.post([=] { client.cursor_changed(win); }); +} -Java_com_termux_x11_MainActivity_surface(JNIEnv *env, jobject thiz, jobject sfc, jint width, jint height) { +extern "C" +JNIEXPORT void JNICALL +Java_com_termux_x11_MainActivity_windowChanged(JNIEnv *env, [[maybe_unused]] jobject thiz, jobject sfc, + jint width, jint height) { ANativeWindow *win = sfc ? ANativeWindow_fromSurface(env, sfc) : nullptr; if (win) ANativeWindow_acquire(win); - ALOGE("Got new surface %p", win); - lorie_client.post([=] { lorie_client.surface_changed(win, width, height); }); + ALOGE("Surface: got new surface %p", win); + client.post([=] { client.surface_changed(win, width, height); }); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_termux_x11_MainActivity_onPointerMotion(JNIEnv *env, jobject thiz, jint x, jint y) { + env->CallVoidMethod(thiz, + client.set_cursor_rect_id, + x - client.cursor.xhot, + y - client.cursor.yhot, + client.cursor.width, + client.cursor.height); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_termux_x11_MainActivity_onPointerScroll(JNIEnv *env, jobject thiz, jint axis, + jfloat value) { + // TODO: implement onPointerScroll() +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_termux_x11_MainActivity_onPointerButton(JNIEnv *env, jobject thiz, jint button, + jint type) { + // TODO: implement onPointerButton() +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_termux_x11_MainActivity_onKeyboardKey(JNIEnv *env, jobject thiz, jint key, jint type, + jint shift, jstring characters) { + // TODO: implement onKeyboardKey() } \ No newline at end of file diff --git a/app/src/main/cpp/lorie/compositor.cpp b/app/src/main/cpp/lorie/compositor.cpp index ccd70f37c..650e7f48b 100644 --- a/app/src/main/cpp/lorie/compositor.cpp +++ b/app/src/main/cpp/lorie/compositor.cpp @@ -188,6 +188,7 @@ lorie_compositor::lorie_compositor(int dpi): dpi(dpi) { }; global_shell.on_bind = [](client_t*, shell_t*) {}; global_xdg_wm_base.on_bind = [](client_t*, xdg_wm_base_t* wm_base) { + wm_base->on_get_xdg_surface = [](xdg_surface_t*, surface_t*) {}; wm_base->on__destroy = [=]() { wm_base->destroy(); }; }; @@ -329,9 +330,13 @@ Java_com_termux_x11_CmdEntryPoint_connect([[maybe_unused]] JNIEnv *env, [[maybe_ return -1; } - snprintf(local.sun_path, sizeof(local.sun_path), "%s/.x11-unix/X%d", + snprintf(local.sun_path, sizeof(local.sun_path), "%s/.X11-unix/X%d", getenv("TMPDIR") ?: DEFAULT_SOCKET_PATH, display); - connect(sockfd, reinterpret_cast(&local), sizeof(local)); // NOLINT(bugprone-unused-return-value) + if (connect(sockfd, reinterpret_cast(&local), sizeof(local)) < 0) { + LOGE("Failed to connect %s: %s\n", local.sun_path, strerror(errno)); + return -1; + } + return sockfd; } \ No newline at end of file diff --git a/app/src/main/java/com/termux/x11/CmdEntryPoint.java b/app/src/main/java/com/termux/x11/CmdEntryPoint.java index 5f78c955d..bff89e316 100644 --- a/app/src/main/java/com/termux/x11/CmdEntryPoint.java +++ b/app/src/main/java/com/termux/x11/CmdEntryPoint.java @@ -7,6 +7,7 @@ import android.os.Handler; import android.os.Looper; import android.os.ParcelFileDescriptor; +import android.util.Log; import java.io.DataInputStream; import java.net.InetAddress; @@ -15,7 +16,7 @@ import java.util.Arrays; import java.util.List; -public class CmdEntryPoint { +public class CmdEntryPoint extends ICmdEntryInterface.Stub { public static final String ACTION_START = "com.termux.x11.CmdEntryPoint.ACTION_START"; public static final int PORT = 7892; public static final byte[] MAGIC = "0xDEADBEEF".getBytes(); @@ -25,8 +26,8 @@ public class CmdEntryPoint { public static final int DEFAULT_DPI = 96; @SuppressLint("StaticFieldLeak") - private static final Context ctx = android.app.ActivityThread.systemMain().getSystemContext(); - private static final Handler handler = new Handler(); + private static Handler handler; + private final Context ctx; /** * Command-line entry point. @@ -60,6 +61,7 @@ public class CmdEntryPoint { * @param args The command-line arguments */ public static void main(String[] args) { + handler = new Handler(); handler.post(() -> new CmdEntryPoint(args)); Looper.loop(); } @@ -70,6 +72,7 @@ public static void main(String[] args) { private int display = 0; CmdEntryPoint(String[] args) { + ctx = android.app.ActivityThread.systemMain().getSystemContext(); if (socketExists()) { System.err.println("termux-x11 is already running. You can kill it with command `pkill -9 Xwayland`"); handler.post(() -> System.exit(0)); @@ -90,38 +93,28 @@ public static void main(String[] args) { spawnCompositor(display, dpi); spawnListeningThread(); sendBroadcast(); - System.err.println("Starting Xwayland"); if (!a.contains("--no-xwayland-start")) { + System.err.println("Starting Xwayland"); exec(args); } } } void sendBroadcast() { - Bundle bundle = new Bundle(); // We should not care about multiple instances, it should be called only by `Termux:X11` app // which is single instance... - bundle.putBinder("", new ICmdEntryInterface.Stub() { - @Override - public void outputResize(int width, int height) { - CmdEntryPoint.this.outputResize(width, height); - } - - @Override - public ParcelFileDescriptor getXConnection() { - return ParcelFileDescriptor.adoptFd(connect()); - } - }); + Bundle bundle = new Bundle(); + bundle.putBinder("", this); Intent intent = new Intent(ACTION_START); - intent.putExtra("com.termux.x11.starter", bundle); + intent.putExtra("", bundle); intent.setPackage("com.termux.x11"); ctx.sendBroadcast(intent); } void spawnListeningThread() { - new Thread(() -> { + new Thread(() -> { // New thread is needed to avoid android.os.NetworkOnMainThreadException /* The purpose of this function is simple. If the application has not been launched before running termux-x11, the initial sendBroadcast had no effect because no one @@ -133,6 +126,7 @@ void spawnListeningThread() { listeningSocket.setReuseAddress(true); while(true) { try (Socket client = listeningSocket.accept()) { + System.err.println("Somebody connected!"); // We should ensure that it is some byte[] b = new byte[MAGIC.length]; DataInputStream reader = new DataInputStream(client.getInputStream()); @@ -151,8 +145,25 @@ void spawnListeningThread() { }).start(); } + public static void requestConnection() { + System.err.println("Requesting connection..."); + new Thread(() -> { // New thread is needed to avoid android.os.NetworkOnMainThreadException + try (Socket socket = new Socket("127.0.0.1", CmdEntryPoint.PORT)){ + socket.getOutputStream().write(CmdEntryPoint.MAGIC); + } catch (Exception e) { + Log.e("CmdEntryPoint", "Something went wrong when we requested connection", e); + } + }).start(); + } + + @Override + public ParcelFileDescriptor getXConnection() { + int fd = connect(); + return fd == -1 ? null : ParcelFileDescriptor.adoptFd(fd); + } + private native void spawnCompositor(int display, int dpi); - private native void outputResize(int width, int height); + public native void outputResize(int width, int height); private static native boolean socketExists(); private static native void exec(String[] argv); private static native int connect(); diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 9fb592252..7c3202eb2 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -1,6 +1,7 @@ package com.termux.x11; import static com.termux.x11.CmdEntryPoint.ACTION_START; +import static com.termux.x11.CmdEntryPoint.requestConnection; import android.annotation.SuppressLint; import android.app.Activity; @@ -19,6 +20,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.preference.PreferenceManager; +import android.util.Log; import android.view.Gravity; import android.view.InputDevice; import android.view.KeyEvent; @@ -62,6 +64,13 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo private final ServiceEventListener listener = new ServiceEventListener(); private ICmdEntryInterface service = null; + private int screenWidth = 0; + private int screenHeight = 0; + + public MainActivity() { + init(); + } + @SuppressLint({"AppCompatMethod", "ObsoleteSdkInt"}) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -73,8 +82,6 @@ protected void onCreate(Bundle savedInstanceState) { preferences.registerOnSharedPreferenceChangeListener((sharedPreferences, key) -> onPreferencesChanged()); - onLorieServiceStart(); - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN); @@ -91,6 +98,18 @@ protected void onCreate(Bundle savedInstanceState) { startActivity(i); }); + SurfaceView lorieView = findViewById(R.id.lorieView); + SurfaceView cursorView = findViewById(R.id.cursorView); + + lorieView.setFocusable(true); + lorieView.setFocusableInTouchMode(true); + lorieView.requestFocus(); + + listener.setAsListenerTo(lorieView, cursorView); + + mTP = new TouchParser(lorieView, this); + kbd.reload(keys, lorieView, listener); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) getWindow(). getDecorView(). @@ -103,13 +122,35 @@ public void onReceive(Context context, Intent intent) { try { IBinder b = intent.getBundleExtra("").getBinder(""); service = ICmdEntryInterface.Stub.asInterface(b); - ParcelFileDescriptor fd = service.getXConnection(); - if (fd != null) - connect(fd.detachFd()); - } catch (Exception ignored) {} + service.asBinder().linkToDeath(() -> { + service = null; + CmdEntryPoint.requestConnection(); + }, 0); + onReceiveConnection(); + } catch (Exception e) { + Log.e("MainActivity", "Something went wrong while we extracted connection details from binder.", e); + } } } }, new IntentFilter(ACTION_START)); + + requestConnection(); + } + + void onReceiveConnection() { + try { + if (service != null && service.asBinder().isBinderAlive()) { + ParcelFileDescriptor fd = service.getXConnection(); + if (fd != null) { + connect(fd.detachFd()); + service.outputResize(screenWidth, screenHeight); + } + else + handler.postDelayed(this::onReceiveConnection, 500); + } + } catch (Exception e) { + Log.e("MainActivity", "Something went wrong while we were establishing connection", e); + } } void onPreferencesChanged() { @@ -187,20 +228,6 @@ public void onConfigurationChanged(@NonNull Configuration newConfig) { orientation = newConfig.orientation; } - public void onLorieServiceStart() { - SurfaceView lorieView = findViewById(R.id.lorieView); - SurfaceView cursorView = findViewById(R.id.cursorView); - - lorieView.setFocusable(true); - lorieView.setFocusableInTouchMode(true); - lorieView.requestFocus(); - - listener.setAsListenerTo(lorieView, cursorView); - - mTP = new TouchParser(lorieView, this); - kbd.reload(keys, lorieView, listener); - } - @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); @@ -309,6 +336,8 @@ private void setAsListenerTo(SurfaceView view, SurfaceView cursor) { windowChanged(holder.getSurface(), w, h); if (service != null) { try { + screenWidth = w; + screenHeight = h; service.outputResize(w, h); } catch (RemoteException e) { e.printStackTrace(); @@ -365,6 +394,7 @@ void setRendererVisibility(boolean visible) { runOnUiThread(()-> { findViewById(R.id.stub).setVisibility(visible?View.INVISIBLE:View.VISIBLE); findViewById(R.id.lorieView).setVisibility(visible?View.VISIBLE:View.INVISIBLE); + findViewById(R.id.cursorView).setVisibility(visible?View.VISIBLE:View.INVISIBLE); }); } @@ -389,7 +419,8 @@ void setCursorRect(int x, int y, int w, int h) { static Handler handler = new Handler(); - private native long connect(int fd); + private native void init(); + private native void connect(int fd); private native void cursorChanged(Surface surface); private native void windowChanged(Surface surface, int width, int height); public native void onPointerMotion(int x, int y); From 6a247fe8b91d6adfe7edcda092d7d60e0f81d426 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Sun, 26 Feb 2023 02:25:22 +0200 Subject: [PATCH 03/21] Basic pointer support. Scroll does not work yet. --- .../android-keycodes-to-x11-keysyms.h | 300 ++++++++++++++++++ .../main/cpp/lorie-client/lorie-client.cpp | 119 +++++-- .../main/java/com/termux/x11/TouchParser.java | 14 +- 3 files changed, 392 insertions(+), 41 deletions(-) create mode 100644 app/src/main/cpp/lorie-client/android-keycodes-to-x11-keysyms.h diff --git a/app/src/main/cpp/lorie-client/android-keycodes-to-x11-keysyms.h b/app/src/main/cpp/lorie-client/android-keycodes-to-x11-keysyms.h new file mode 100644 index 000000000..1d95b03a6 --- /dev/null +++ b/app/src/main/cpp/lorie-client/android-keycodes-to-x11-keysyms.h @@ -0,0 +1,300 @@ +#include + +/* + * Indexes in this list match to keycode name in comment. + * Do not change order, only change values. + * This list is only for special keys which do not produce + * Unicode symbols or its value is stable. + * Unicode symbols will be handled in keysym resolver + * which keeps track on Xserver's keymap, otherways non-English + * keyboard layouts will be broken. + */ +int keysyms = { + /* ANDROID_KEYCODE_UNKNOWN */ 0, + /* ANDROID_KEYCODE_SOFT_LEFT */ 0, + /* ANDROID_KEYCODE_SOFT_RIGHT */ 0, + /* ANDROID_KEYCODE_HOME */ 0, + /* ANDROID_KEYCODE_BACK */ XK_Escape, + /* ANDROID_KEYCODE_CALL */ 0, + /* ANDROID_KEYCODE_ENDCALL */ 0, + /* ANDROID_KEYCODE_0 */ 0, + /* ANDROID_KEYCODE_1 */ 0, + /* ANDROID_KEYCODE_2 */ 0, + /* ANDROID_KEYCODE_3 */ 0, + /* ANDROID_KEYCODE_4 */ 0, + /* ANDROID_KEYCODE_5 */ 0, + /* ANDROID_KEYCODE_6 */ 0, + /* ANDROID_KEYCODE_7 */ 0, + /* ANDROID_KEYCODE_8 */ 0, + /* ANDROID_KEYCODE_9 */ 0, + /* ANDROID_KEYCODE_STAR */ 0, + /* ANDROID_KEYCODE_POUND */ 0, + /* ANDROID_KEYCODE_DPAD_UP */ XK_Up, + /* ANDROID_KEYCODE_DPAD_DOWN */ XK_Down, + /* ANDROID_KEYCODE_DPAD_LEFT */ XK_Left, + /* ANDROID_KEYCODE_DPAD_RIGHT */ XK_Right, + /* ANDROID_KEYCODE_DPAD_CENTER */ XK_Return, + /* ANDROID_KEYCODE_VOLUME_UP */ 0, + /* ANDROID_KEYCODE_VOLUME_DOWN */ 0, + /* ANDROID_KEYCODE_POWER */ 0, + /* ANDROID_KEYCODE_CAMERA */ 0, + /* ANDROID_KEYCODE_CLEAR */ 0, + /* ANDROID_KEYCODE_A */ 0, + /* ANDROID_KEYCODE_B */ 0, + /* ANDROID_KEYCODE_C */ 0, + /* ANDROID_KEYCODE_D */ 0, + /* ANDROID_KEYCODE_E */ 0, + /* ANDROID_KEYCODE_F */ 0, + /* ANDROID_KEYCODE_G */ 0, + /* ANDROID_KEYCODE_H */ 0, + /* ANDROID_KEYCODE_I */ 0, + /* ANDROID_KEYCODE_J */ 0, + /* ANDROID_KEYCODE_K */ 0, + /* ANDROID_KEYCODE_L */ 0, + /* ANDROID_KEYCODE_M */ 0, + /* ANDROID_KEYCODE_N */ 0, + /* ANDROID_KEYCODE_O */ 0, + /* ANDROID_KEYCODE_P */ 0, + /* ANDROID_KEYCODE_Q */ 0, + /* ANDROID_KEYCODE_R */ 0, + /* ANDROID_KEYCODE_S */ 0, + /* ANDROID_KEYCODE_T */ 0, + /* ANDROID_KEYCODE_U */ 0, + /* ANDROID_KEYCODE_V */ 0, + /* ANDROID_KEYCODE_W */ 0, + /* ANDROID_KEYCODE_X */ 0, + /* ANDROID_KEYCODE_Y */ 0, + /* ANDROID_KEYCODE_Z */ 0, + /* ANDROID_KEYCODE_COMMA */ 0, + /* ANDROID_KEYCODE_PERIOD */ 0, + /* ANDROID_KEYCODE_ALT_LEFT */ XK_Alt_L, + /* ANDROID_KEYCODE_ALT_RIGHT */ XK_Alt_R, + /* ANDROID_KEYCODE_SHIFT_LEFT */ XK_Shift_L, + /* ANDROID_KEYCODE_SHIFT_RIGHT */ XK_Shift_R, + /* ANDROID_KEYCODE_TAB */ XK_Tab, + /* ANDROID_KEYCODE_SPACE */ XK_space, + /* ANDROID_KEYCODE_SYM */ 0, + /* ANDROID_KEYCODE_EXPLORER */ 0, + /* ANDROID_KEYCODE_ENVELOPE */ 0, + /* ANDROID_KEYCODE_ENTER */ XK_Return, + /* ANDROID_KEYCODE_DEL */ XK_BackSpace, + /* ANDROID_KEYCODE_GRAVE */ 0, + /* ANDROID_KEYCODE_MINUS */ 0, + /* ANDROID_KEYCODE_EQUALS */ 0, + /* ANDROID_KEYCODE_LEFT_BRACKET */ 0, + /* ANDROID_KEYCODE_RIGHT_BRACKET */ 0, + /* ANDROID_KEYCODE_BACKSLASH */ 0, + /* ANDROID_KEYCODE_SEMICOLON */ 0, + /* ANDROID_KEYCODE_APOSTROPHE */ 0, + /* ANDROID_KEYCODE_SLASH */ 0, + /* ANDROID_KEYCODE_AT */ 0, + /* ANDROID_KEYCODE_NUM */ 0, + /* ANDROID_KEYCODE_HEADSETHOOK */ 0, + /* ANDROID_KEYCODE_FOCUS */ 0, + /* ANDROID_KEYCODE_PLUS */ 0, + /* ANDROID_KEYCODE_MENU */ 0, + /* ANDROID_KEYCODE_NOTIFICATION */ 0, + /* ANDROID_KEYCODE_SEARCH */ 0, + /* ANDROID_KEYCODE_MEDIA_PLAY_PAUSE */ 0, + /* ANDROID_KEYCODE_MEDIA_STOP */ 0, + /* ANDROID_KEYCODE_MEDIA_NEXT */ 0, + /* ANDROID_KEYCODE_MEDIA_PREVIOUS */ 0, + /* ANDROID_KEYCODE_MEDIA_REWIND */ 0, + /* ANDROID_KEYCODE_MEDIA_FAST_FORWARD */ 0, + /* ANDROID_KEYCODE_MUTE */ 0, + /* ANDROID_KEYCODE_PAGE_UP */ XK_Page_Up, + /* ANDROID_KEYCODE_PAGE_DOWN */ XK_Page_Down, + /* ANDROID_KEYCODE_PICTSYMBOLS */ 0, + /* ANDROID_KEYCODE_SWITCH_CHARSET */ 0, + /* ANDROID_KEYCODE_BUTTON_A */ 0, + /* ANDROID_KEYCODE_BUTTON_B */ 0, + /* ANDROID_KEYCODE_BUTTON_C */ 0, + /* ANDROID_KEYCODE_BUTTON_X */ 0, + /* ANDROID_KEYCODE_BUTTON_Y */ 0, + /* ANDROID_KEYCODE_BUTTON_Z */ 0, + /* ANDROID_KEYCODE_BUTTON_L1 */ 0, + /* ANDROID_KEYCODE_BUTTON_R1 */ 0, + /* ANDROID_KEYCODE_BUTTON_L2 */ 0, + /* ANDROID_KEYCODE_BUTTON_R2 */ 0, + /* ANDROID_KEYCODE_BUTTON_THUMBL */ 0, + /* ANDROID_KEYCODE_BUTTON_THUMBR */ 0, + /* ANDROID_KEYCODE_BUTTON_START */ 0, + /* ANDROID_KEYCODE_BUTTON_SELECT */ 0, + /* ANDROID_KEYCODE_BUTTON_MODE */ 0, + /* ANDROID_KEYCODE_ESCAPE */ XK_Escape, + /* ANDROID_KEYCODE_FORWARD_DEL */ XK_Delete, + /* ANDROID_KEYCODE_CTRL_LEFT */ XK_Control_L, + /* ANDROID_KEYCODE_CTRL_RIGHT */ XK_Control_R, + /* ANDROID_KEYCODE_CAPS_LOCK */ XK_Caps_Lock, + /* ANDROID_KEYCODE_SCROLL_LOCK */ XK_Scroll_Lock, + /* ANDROID_KEYCODE_META_LEFT */ XK_Super_L, + /* ANDROID_KEYCODE_META_RIGHT */ XK_Super_R, + /* ANDROID_KEYCODE_FUNCTION */ 0, + /* ANDROID_KEYCODE_SYSRQ */ XK_Print, + /* ANDROID_KEYCODE_BREAK */ XK_Break, + /* ANDROID_KEYCODE_MOVE_HOME */ XK_Home, + /* ANDROID_KEYCODE_MOVE_END */ XK_End, + /* ANDROID_KEYCODE_INSERT */ XK_Insert, + /* ANDROID_KEYCODE_FORWARD */ 0, + /* ANDROID_KEYCODE_MEDIA_PLAY */ 0, + /* ANDROID_KEYCODE_MEDIA_PAUSE */ 0, + /* ANDROID_KEYCODE_MEDIA_CLOSE */ 0, + /* ANDROID_KEYCODE_MEDIA_EJECT */ 0, + /* ANDROID_KEYCODE_MEDIA_RECORD */ 0, + /* ANDROID_KEYCODE_F1 */ XK_F1, + /* ANDROID_KEYCODE_F2 */ XK_F2, + /* ANDROID_KEYCODE_F3 */ XK_F3, + /* ANDROID_KEYCODE_F4 */ XK_F4 + /* ANDROID_KEYCODE_F5 */ XK_F5, + /* ANDROID_KEYCODE_F6 */ XK_F6, + /* ANDROID_KEYCODE_F7 */ XK_F7, + /* ANDROID_KEYCODE_F8 */ XK_F8, + /* ANDROID_KEYCODE_F9 */ XK_F9, + /* ANDROID_KEYCODE_F10 */ XK_F10, + /* ANDROID_KEYCODE_F11 */ XK_F11, + /* ANDROID_KEYCODE_F12 */ XK_F12, + /* ANDROID_KEYCODE_NUM_LOCK */ XK_Num_Lock, + /* ANDROID_KEYCODE_NUMPAD_0 */ XK_KP_0, + /* ANDROID_KEYCODE_NUMPAD_1 */ XK_KP_1, + /* ANDROID_KEYCODE_NUMPAD_2 */ XK_KP_2, + /* ANDROID_KEYCODE_NUMPAD_3 */ XK_KP_3, + /* ANDROID_KEYCODE_NUMPAD_4 */ XK_KP_4, + /* ANDROID_KEYCODE_NUMPAD_5 */ XK_KP_5, + /* ANDROID_KEYCODE_NUMPAD_6 */ XK_KP_6, + /* ANDROID_KEYCODE_NUMPAD_7 */ XK_KP_7, + /* ANDROID_KEYCODE_NUMPAD_8 */ XK_KP_8, + /* ANDROID_KEYCODE_NUMPAD_9 */ XK_KP_9, + /* ANDROID_KEYCODE_NUMPAD_DIVIDE */ 0, + /* ANDROID_KEYCODE_NUMPAD_MULTIPLY */ 0, + /* ANDROID_KEYCODE_NUMPAD_SUBTRACT */ 0, + /* ANDROID_KEYCODE_NUMPAD_ADD */ 0, + /* ANDROID_KEYCODE_NUMPAD_DOT */ 0, + /* ANDROID_KEYCODE_NUMPAD_COMMA */ 0, + /* ANDROID_KEYCODE_NUMPAD_ENTER */ XK_Return, + /* ANDROID_KEYCODE_NUMPAD_EQUALS */ 0, + /* ANDROID_KEYCODE_NUMPAD_LEFT_PAREN */ 0, + /* ANDROID_KEYCODE_NUMPAD_RIGHT_PAREN */ 0, + /* ANDROID_KEYCODE_VOLUME_MUTE */ 0, + /* ANDROID_KEYCODE_INFO */ 0, + /* ANDROID_KEYCODE_CHANNEL_UP */ 0, + /* ANDROID_KEYCODE_CHANNEL_DOWN */ 0, + /* ANDROID_KEYCODE_ZOOM_IN */ 0, + /* ANDROID_KEYCODE_ZOOM_OUT */ 0, + /* ANDROID_KEYCODE_TV */ 0, + /* ANDROID_KEYCODE_WINDOW */ 0, + /* ANDROID_KEYCODE_GUIDE */ 0, + /* ANDROID_KEYCODE_DVR */ 0, + /* ANDROID_KEYCODE_BOOKMARK */ 0, + /* ANDROID_KEYCODE_CAPTIONS */ 0, + /* ANDROID_KEYCODE_SETTINGS */ 0, + /* ANDROID_KEYCODE_TV_POWER */ 0, + /* ANDROID_KEYCODE_TV_INPUT */ 0, + /* ANDROID_KEYCODE_STB_POWER */ 0, + /* ANDROID_KEYCODE_STB_INPUT */ 0, + /* ANDROID_KEYCODE_AVR_POWER */ 0, + /* ANDROID_KEYCODE_AVR_INPUT */ 0, + /* ANDROID_KEYCODE_PROG_RED */ 0, + /* ANDROID_KEYCODE_PROG_GREEN */ 0, + /* ANDROID_KEYCODE_PROG_YELLOW */ 0, + /* ANDROID_KEYCODE_PROG_BLUE */ 0, + /* ANDROID_KEYCODE_APP_SWITCH */ 0, + /* ANDROID_KEYCODE_BUTTON_1 */ 0, + /* ANDROID_KEYCODE_BUTTON_2 */ 0, + /* ANDROID_KEYCODE_BUTTON_3 */ 0, + /* ANDROID_KEYCODE_BUTTON_4 */ 0, + /* ANDROID_KEYCODE_BUTTON_5 */ 0, + /* ANDROID_KEYCODE_BUTTON_6 */ 0, + /* ANDROID_KEYCODE_BUTTON_7 */ 0, + /* ANDROID_KEYCODE_BUTTON_8 */ 0, + /* ANDROID_KEYCODE_BUTTON_9 */ 0, + /* ANDROID_KEYCODE_BUTTON_10 */ 0, + /* ANDROID_KEYCODE_BUTTON_11 */ 0, + /* ANDROID_KEYCODE_BUTTON_12 */ 0, + /* ANDROID_KEYCODE_BUTTON_13 */ 0, + /* ANDROID_KEYCODE_BUTTON_14 */ 0, + /* ANDROID_KEYCODE_BUTTON_15 */ 0, + /* ANDROID_KEYCODE_BUTTON_16 */ 0, + /* ANDROID_KEYCODE_LANGUAGE_SWITCH */ 0, + /* ANDROID_KEYCODE_MANNER_MODE */ 0, + /* ANDROID_KEYCODE_3D_MODE */ 0, + /* ANDROID_KEYCODE_CONTACTS */ 0, + /* ANDROID_KEYCODE_CALENDAR */ 0, + /* ANDROID_KEYCODE_MUSIC */ 0, + /* ANDROID_KEYCODE_CALCULATOR */ 0, + /* ANDROID_KEYCODE_ZENKAKU_HANKAKU */ 0, + /* ANDROID_KEYCODE_EISU */ 0, + /* ANDROID_KEYCODE_MUHENKAN */ 0, + /* ANDROID_KEYCODE_HENKAN */ 0, + /* ANDROID_KEYCODE_KATAKANA_HIRAGANA */ 0, + /* ANDROID_KEYCODE_YEN */ 0, + /* ANDROID_KEYCODE_RO */ 0, + /* ANDROID_KEYCODE_KANA */ 0, + /* ANDROID_KEYCODE_ASSIST */ 0, + /* ANDROID_KEYCODE_BRIGHTNESS_DOWN */ 0, + /* ANDROID_KEYCODE_BRIGHTNESS_UP */ 0, + /* ANDROID_KEYCODE_MEDIA_AUDIO_TRACK */ 0, + /* ANDROID_KEYCODE_SLEEP */ 0, + /* ANDROID_KEYCODE_WAKEUP */ 0, + /* ANDROID_KEYCODE_PAIRING */ 0, + /* ANDROID_KEYCODE_MEDIA_TOP_MENU */ 0, + /* ANDROID_KEYCODE_11 */ 0, + /* ANDROID_KEYCODE_12 */ 0, + /* ANDROID_KEYCODE_LAST_CHANNEL */ 0, + /* ANDROID_KEYCODE_TV_DATA_SERVICE */ 0, + /* ANDROID_KEYCODE_VOICE_ASSIST */ 0, + /* ANDROID_KEYCODE_TV_RADIO_SERVICE */ 0, + /* ANDROID_KEYCODE_TV_TELETEXT */ 0, + /* ANDROID_KEYCODE_TV_NUMBER_ENTRY */ 0, + /* ANDROID_KEYCODE_TV_TERRESTRIAL_ANALOG */ 0, + /* ANDROID_KEYCODE_TV_TERRESTRIAL_DIGITAL */ 0, + /* ANDROID_KEYCODE_TV_SATELLITE */ 0, + /* ANDROID_KEYCODE_TV_SATELLITE_BS */ 0, + /* ANDROID_KEYCODE_TV_SATELLITE_CS */ 0, + /* ANDROID_KEYCODE_TV_SATELLITE_SERVICE */ 0, + /* ANDROID_KEYCODE_TV_NETWORK */ 0, + /* ANDROID_KEYCODE_TV_ANTENNA_CABLE */ 0, + /* ANDROID_KEYCODE_TV_INPUT_HDMI_1 */ 0, + /* ANDROID_KEYCODE_TV_INPUT_HDMI_2 */ 0, + /* ANDROID_KEYCODE_TV_INPUT_HDMI_3 */ 0, + /* ANDROID_KEYCODE_TV_INPUT_HDMI_4 */ 0, + /* ANDROID_KEYCODE_TV_INPUT_COMPOSITE_1 */ 0, + /* ANDROID_KEYCODE_TV_INPUT_COMPOSITE_2 */ 0, + /* ANDROID_KEYCODE_TV_INPUT_COMPONENT_1 */ 0, + /* ANDROID_KEYCODE_TV_INPUT_COMPONENT_2 */ 0, + /* ANDROID_KEYCODE_TV_INPUT_VGA_1 */ 0, + /* ANDROID_KEYCODE_TV_AUDIO_DESCRIPTION */ 0, + /* ANDROID_KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP */ 0, + /* ANDROID_KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN */ 0, + /* ANDROID_KEYCODE_TV_ZOOM_MODE */ 0, + /* ANDROID_KEYCODE_TV_CONTENTS_MENU */ 0, + /* ANDROID_KEYCODE_TV_MEDIA_CONTEXT_MENU */ 0, + /* ANDROID_KEYCODE_TV_TIMER_PROGRAMMING */ 0, + /* ANDROID_KEYCODE_HELP */ 0, + /* ANDROID_KEYCODE_NAVIGATE_PREVIOUS */ 0, + /* ANDROID_KEYCODE_NAVIGATE_NEXT */ 0, + /* ANDROID_KEYCODE_NAVIGATE_IN */ 0, + /* ANDROID_KEYCODE_NAVIGATE_OUT */ 0, + /* ANDROID_KEYCODE_STEM_PRIMARY */ 0, + /* ANDROID_KEYCODE_STEM_1 */ 0, + /* ANDROID_KEYCODE_STEM_2 */ 0, + /* ANDROID_KEYCODE_STEM_3 */ 0, + /* ANDROID_KEYCODE_DPAD_UP_LEFT */ 0, + /* ANDROID_KEYCODE_DPAD_DOWN_LEFT */ 0, + /* ANDROID_KEYCODE_DPAD_UP_RIGHT */ 0, + /* ANDROID_KEYCODE_DPAD_DOWN_RIGHT */ 0, + /* ANDROID_KEYCODE_MEDIA_SKIP_FORWARD */ 0, + /* ANDROID_KEYCODE_MEDIA_SKIP_BACKWARD */ 0, + /* ANDROID_KEYCODE_MEDIA_STEP_FORWARD */ 0, + /* ANDROID_KEYCODE_MEDIA_STEP_BACKWARD */ 0, + /* ANDROID_KEYCODE_SOFT_SLEEP */ 0, + /* ANDROID_KEYCODE_CUT */ 0, + /* ANDROID_KEYCODE_COPY */ 0, + /* ANDROID_KEYCODE_PASTE */ 0, + /* ANDROID_KEYCODE_SYSTEM_NAVIGATION_UP */ 0, + /* ANDROID_KEYCODE_SYSTEM_NAVIGATION_DOWN */ 0, + /* ANDROID_KEYCODE_SYSTEM_NAVIGATION_LEFT */ 0, + /* ANDROID_KEYCODE_SYSTEM_NAVIGATION_RIGHT */ 0, + /* ANDROID_KEYCODE_ALL_APPS */ 0, + /* ANDROID_KEYCODE_REFRESH */ 0 + /* ANDROID_KEYCODE_LAST */ +} diff --git a/app/src/main/cpp/lorie-client/lorie-client.cpp b/app/src/main/cpp/lorie-client/lorie-client.cpp index c4dc09681..197812b88 100644 --- a/app/src/main/cpp/lorie-client/lorie-client.cpp +++ b/app/src/main/cpp/lorie-client/lorie-client.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -239,6 +240,15 @@ class xcb_connection { } } fixes {*this}; + struct { + xcb_connection &self; + void init() { + auto reply = xcb(test_get_version, 2, 2); + self.handle_error(reply, "Error querying XFIXES extension"); + free(reply); + } + } xtest {*this}; + void init(int sockfd) { xcb_connection_t* new_conn = xcb_connect_to_fd(sockfd, nullptr); int conn_err = xcb_connection_has_error(new_conn); @@ -268,7 +278,7 @@ class xcb_connection { xcb_errors_context_new(conn, &err_ctx); shm.init(); - //randr.init(); + xtest.init(); damage.init(); input.init(); fixes.init(); @@ -380,8 +390,8 @@ class lorie_client { void runner() { ALooper_prepare(0); ALooper_addFd(ALooper_forThread(), queue.get_fd(), ALOOPER_EVENT_INPUT, ALOOPER_POLL_CALLBACK, [](int, int, void *d) { - auto thiz = reinterpret_cast (d); - thiz->queue.run(); + auto client = reinterpret_cast(d); + client->queue.run(); return 1; }, this); @@ -408,6 +418,8 @@ class lorie_client { if (win) ANativeWindow_acquire(win); cursor.win = win; + + refresh_cursor(); } void adopt_connection_fd(int fd) { @@ -451,6 +463,8 @@ class lorie_client { } c.shm.attach_fd(screen.shmseg, screen.shmfd, 0); + refresh_cursor(); + int event_mask = ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT | ALOOPER_EVENT_INVALID | ALOOPER_EVENT_HANGUP | ALOOPER_EVENT_ERROR; ALooper_addFd(ALooper_forThread(), fd, ALOOPER_EVENT_INPUT, event_mask, @@ -470,15 +484,53 @@ class lorie_client { }, this); set_renderer_visibility(true); + refresh_screen(); } catch (std::exception& e) { ALOGE("Failed to adopt X connection socket: %s", e.what()); } } + void refresh_cursor() { + if (cursor.win && c.conn) { + auto reply = c.fixes.get_cursor_image(); + if (reply) { + cursor.width = reply->width; + cursor.height = reply->height; + cursor.xhot = reply->xhot; + cursor.yhot = reply->yhot; + u32* image = xcb_xfixes_get_cursor_image_cursor_image(reply); + blit_exact(cursor.win, image, reply->width, reply->height); + } + free(reply); + env->CallVoidMethod(thiz, + set_cursor_rect_id, + cursor.x - cursor.xhot, + cursor.y - cursor.yhot, + cursor.width, + cursor.height); + } + } + + void refresh_screen() { + try { + if (screen.win && c.conn) { + xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(c.conn)).data; + c.shm.get(s->root, 0, 0, s->width_in_pixels, s->height_in_pixels, + ~0, // NOLINT(cppcoreguidelines-narrowing-conversions) + XCB_IMAGE_FORMAT_Z_PIXMAP, screen.shmseg, 0); + blit_exact(screen.win, screen.shmaddr, s->width_in_pixels, + s->height_in_pixels); + } + } catch (std::runtime_error &err) { + ALOGE("Refreshing screen failed: %s", err.what()); + } + // post([=]{ refresh_screen(); }); // this line makes video stream smoother but it consumes a lot of cpu. + } + void connection_poll_func() { try { xcb_generic_event_t *event; - const char *ext; + // const char *ext; xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(c.conn)).data; while ((event = xcb_poll_for_event(c.conn))) { if (event->response_type == 0) { @@ -489,28 +541,9 @@ class lorie_client { s->width_in_pixels = e->width; s->height_in_pixels = e->height; } else if (c.damage.is_damage_notify_event(event)) { - try { - c.shm.get(s->root, 0, 0, s->width_in_pixels, s->height_in_pixels, - ~0, // NOLINT(cppcoreguidelines-narrowing-conversions) - XCB_IMAGE_FORMAT_Z_PIXMAP, screen.shmseg, 0); - blit_exact(screen.win, screen.shmaddr, s->width_in_pixels, - s->height_in_pixels); - } catch (std::runtime_error &err) { - continue; - } + refresh_screen(); } else if (c.fixes.is_cursor_notify_event(event)) { - if (cursor.win) { - auto reply = c.fixes.get_cursor_image(); - if (reply) { - cursor.width = reply->width; - cursor.height = reply->height; - cursor.xhot = reply->xhot; - cursor.yhot = reply->yhot; - u32* image = xcb_xfixes_get_cursor_image_cursor_image(reply); - blit_exact(cursor.win, image, reply->width, reply->height); - } - free(reply); - } + refresh_cursor(); } //else // ALOGE("some other event %s of %s", xcb_errors_get_name_for_core_event(c.err_ctx, event->response_type, &ext), (ext ?: "core")); } @@ -530,9 +563,9 @@ class lorie_client { jobject thiz{}; jmethodID set_renderer_visibility_id{}; jmethodID set_cursor_rect_id{}; - void init_jni(JavaVM* vm, jobject thiz) { + void init_jni(JavaVM* vm, jobject obj) { queue.write([=, this] { - this->thiz = thiz; + thiz = obj; vm->AttachCurrentThread(&env, nullptr); set_renderer_visibility_id = env->GetMethodID(env->GetObjectClass(thiz),"setRendererVisibility","(Z)V"); @@ -562,7 +595,7 @@ Java_com_termux_x11_MainActivity_init(JNIEnv *env, jobject thiz) { extern "C" JNIEXPORT void JNICALL -Java_com_termux_x11_MainActivity_connect(JNIEnv *env, [[maybe_unused]] jobject thiz, jint fd) { +Java_com_termux_x11_MainActivity_connect([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject thiz, jint fd) { client.post([fd] { client.adopt_connection_fd(fd); }); } @@ -598,25 +631,43 @@ Java_com_termux_x11_MainActivity_onPointerMotion(JNIEnv *env, jobject thiz, jint y - client.cursor.yhot, client.cursor.width, client.cursor.height); + + client.cursor.x = x; + client.cursor.y = y; + if (client.c.conn) { + client.post([=] { + xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(client.c.conn)).data; + xcb_test_fake_input(client.c.conn, XCB_MOTION_NOTIFY, false, XCB_CURRENT_TIME, s->root, x, y, 0); + xcb_flush(client.c.conn); + }); + } } extern "C" JNIEXPORT void JNICALL -Java_com_termux_x11_MainActivity_onPointerScroll(JNIEnv *env, jobject thiz, jint axis, - jfloat value) { +Java_com_termux_x11_MainActivity_onPointerScroll([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject thiz, + [[maybe_unused]] jint axis, [[maybe_unused]] jfloat value) { // TODO: implement onPointerScroll() + // How to send pointer scroll with XTEST? } extern "C" JNIEXPORT void JNICALL -Java_com_termux_x11_MainActivity_onPointerButton(JNIEnv *env, jobject thiz, jint button, +Java_com_termux_x11_MainActivity_onPointerButton([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject thiz, jint button, jint type) { - // TODO: implement onPointerButton() + if (client.c.conn) { + client.post([=] { + xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(client.c.conn)).data; + xcb_test_fake_input(client.c.conn, type, button, XCB_CURRENT_TIME, s->root, 0, 0, 0); + xcb_flush(client.c.conn); + }); + } } extern "C" JNIEXPORT void JNICALL -Java_com_termux_x11_MainActivity_onKeyboardKey(JNIEnv *env, jobject thiz, jint key, jint type, - jint shift, jstring characters) { +Java_com_termux_x11_MainActivity_onKeyboardKey([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject thiz, + [[maybe_unused]] jint key, [[maybe_unused]] jint type, + [[maybe_unused]] jint shift, [[maybe_unused]] jstring characters) { // TODO: implement onKeyboardKey() } \ No newline at end of file diff --git a/app/src/main/java/com/termux/x11/TouchParser.java b/app/src/main/java/com/termux/x11/TouchParser.java index a39deb22b..a6a938ca3 100644 --- a/app/src/main/java/com/termux/x11/TouchParser.java +++ b/app/src/main/java/com/termux/x11/TouchParser.java @@ -30,8 +30,8 @@ @SuppressWarnings("unused") public class TouchParser { - private static final int WL_STATE_PRESSED = 1; - private static final int WL_STATE_RELEASED = 0; + private static final int XCB_BUTTON_PRESS = 4; + private static final int XCB_BUTTON_RELEASE = 5; private static final int WL_POINTER_AXIS_VERTICAL_SCROLL = 0; private static final int WL_POINTER_AXIS_HORIZONTAL_SCROLL = 1; @@ -39,12 +39,12 @@ public class TouchParser { static final int TOUCH_MODE_MOUSE = 0; static final int TOUCH_MODE_TOUCHPAD = 1; - static final int BTN_LEFT = 0x110; - static final int BTN_RIGHT = 0x111; - static final int BTN_MIDDLE = 0x112; + static final int BTN_LEFT = 1; + static final int BTN_MIDDLE = 2; + static final int BTN_RIGHT = 3; - static final int ACTION_DOWN = WL_STATE_PRESSED; - static final int ACTION_UP = WL_STATE_RELEASED; + static final int ACTION_DOWN = XCB_BUTTON_PRESS; + static final int ACTION_UP = XCB_BUTTON_RELEASE; private static final int AXIS_X = WL_POINTER_AXIS_HORIZONTAL_SCROLL; private static final int AXIS_Y = WL_POINTER_AXIS_VERTICAL_SCROLL; From 71cbf9e8adfe5d67833b8c8fc562a40df2092aa9 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Sun, 26 Feb 2023 23:28:02 +0200 Subject: [PATCH 04/21] Current [non] progress with delayed tasks... --- .gitmodules | 4 + app/src/main/cpp/CMakeLists.txt | 7 +- app/src/main/cpp/libxkbcommon | 1 + .../main/cpp/lorie-client/lorie-client.cpp | 46 +++--- .../cpp/lorie-client/lorie_message_queue.cpp | 49 ------ .../cpp/lorie-client/lorie_message_queue.hpp | 147 +++++++++++++++++- 6 files changed, 181 insertions(+), 73 deletions(-) create mode 160000 app/src/main/cpp/libxkbcommon delete mode 100644 app/src/main/cpp/lorie-client/lorie_message_queue.cpp diff --git a/.gitmodules b/.gitmodules index 4e886ce89..4baaa3431 100644 --- a/.gitmodules +++ b/.gitmodules @@ -26,3 +26,7 @@ path = app/src/main/cpp/libxcvt url = https://gitlab.freedesktop.org/xorg/lib/libxcvt ignore = dirty +[submodule "app/src/main/cpp/libxkbcommon"] + path = app/src/main/cpp/libxkbcommon + url = https://github.com/xkbcommon/libxkbcommon + ignore = dirty diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 5ec8b33a6..547c09901 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -3,6 +3,7 @@ project(lorie) set(CMAKE_CXX_STANDARD 20) find_package(Python3 REQUIRED) +find_package(BISON 3.0 REQUIRED) #################################################################################################### ######################################### LIBWAYLAND ############################################### @@ -136,11 +137,15 @@ target_link_libraries(xcb-errors xcbproto) add_library(xcvt "${CMAKE_CURRENT_SOURCE_DIR}/libxcvt/lib/libxcvt.c") target_include_directories(xcvt PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/libxcvt/include") +#################################################################################################### +####################################### LIBXKB-COMMON ############################################## +#################################################################################################### + #################################################################################################### ####################################### LORIE-CLIENT ############################################### #################################################################################################### add_library(lorie-client SHARED ${CMAKE_CURRENT_SOURCE_DIR}/lorie-client/lorie-client.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/lorie-client/lorie_message_queue.cpp) + ) target_link_libraries(lorie-client xcbproto xcb xcb-errors xcb-shm xcb-xfixes xcb-xinput xcb-damage xcvt "-llog" "-landroid") \ No newline at end of file diff --git a/app/src/main/cpp/libxkbcommon b/app/src/main/cpp/libxkbcommon new file mode 160000 index 000000000..5b5ec0ee2 --- /dev/null +++ b/app/src/main/cpp/libxkbcommon @@ -0,0 +1 @@ +Subproject commit 5b5ec0ee2781fb540c60f7f554787cef1e2aaa87 diff --git a/app/src/main/cpp/lorie-client/lorie-client.cpp b/app/src/main/cpp/lorie-client/lorie-client.cpp index 197812b88..18f39fed5 100644 --- a/app/src/main/cpp/lorie-client/lorie-client.cpp +++ b/app/src/main/cpp/lorie-client/lorie-client.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -22,6 +21,14 @@ #include #include #include + +#if 1 +#define ALOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "LorieX11Client", fmt, ## __VA_ARGS__) +#else +#define ALOGE(fmt, ...) printf(fmt, ## __VA_ARGS__); printf("\n") +#endif +#define unused __attribute__((unused)) + #include "lorie_message_queue.hpp" // To avoid reopening new segment on every screen resolution @@ -31,13 +38,6 @@ #pragma ide diagnostic ignored "ConstantParameter" #pragma ide diagnostic ignored "cppcoreguidelines-narrowing-conversions" -#if 1 -#define ALOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "LorieX11Client", fmt, ## __VA_ARGS__) -#else -#define ALOGE(fmt, ...) printf(fmt, ## __VA_ARGS__); printf("\n") -#endif -#define unused __attribute__((unused)) - typedef uint8_t u8 unused; typedef uint16_t u16 unused; typedef uint32_t u32 unused; @@ -383,8 +383,8 @@ class lorie_client { runner_thread = std::thread([=, this] { runner(); }); } - void post(std::function task) { - queue.write(std::move(task)); + void post(std::function task, long ms_delay = 0) { + queue.post(std::move(task), ms_delay); } void runner() { @@ -494,24 +494,32 @@ class lorie_client { if (cursor.win && c.conn) { auto reply = c.fixes.get_cursor_image(); if (reply) { + env->CallVoidMethod(thiz, + set_cursor_rect_id, + cursor.x - cursor.xhot, + cursor.y - cursor.yhot, + cursor.width, + cursor.height); cursor.width = reply->width; cursor.height = reply->height; cursor.xhot = reply->xhot; cursor.yhot = reply->yhot; u32* image = xcb_xfixes_get_cursor_image_cursor_image(reply); + env->CallVoidMethod(thiz, + set_cursor_rect_id, + cursor.x - cursor.xhot, + cursor.y - cursor.yhot, + cursor.width, + cursor.height); blit_exact(cursor.win, image, reply->width, reply->height); } free(reply); - env->CallVoidMethod(thiz, - set_cursor_rect_id, - cursor.x - cursor.xhot, - cursor.y - cursor.yhot, - cursor.width, - cursor.height); } } void refresh_screen() { + //post([=] { ALOGE("DELAYED!!!"); refresh_screen(); }, 1000); // post with delay does not work... + ALOGE("REFRESH!!!"); try { if (screen.win && c.conn) { xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(c.conn)).data; @@ -553,7 +561,7 @@ class lorie_client { } ~lorie_client() { - queue.write([=, this] { terminate = true; }); + queue.post([=, this] { terminate = true; }); if (runner_thread.joinable()) runner_thread.join(); close(queue.get_fd()); @@ -564,7 +572,7 @@ class lorie_client { jmethodID set_renderer_visibility_id{}; jmethodID set_cursor_rect_id{}; void init_jni(JavaVM* vm, jobject obj) { - queue.write([=, this] { + post([=, this] { thiz = obj; vm->AttachCurrentThread(&env, nullptr); set_renderer_visibility_id = @@ -670,4 +678,4 @@ Java_com_termux_x11_MainActivity_onKeyboardKey([[maybe_unused]] JNIEnv *env, [[m [[maybe_unused]] jint key, [[maybe_unused]] jint type, [[maybe_unused]] jint shift, [[maybe_unused]] jstring characters) { // TODO: implement onKeyboardKey() -} \ No newline at end of file +} diff --git a/app/src/main/cpp/lorie-client/lorie_message_queue.cpp b/app/src/main/cpp/lorie-client/lorie_message_queue.cpp deleted file mode 100644 index dceb8cdac..000000000 --- a/app/src/main/cpp/lorie-client/lorie_message_queue.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "lorie_message_queue.hpp" -#if 1 -#define ALOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "LorieX11Client", fmt, ## __VA_ARGS__) -#else -#define ALOGE(fmt, ...) printf(fmt, ## __VA_ARGS__); printf("\n") -#endif - -lorie_message_queue::lorie_message_queue() { - std::unique_lock lock(mutex); - - fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); - if (fd == -1) { - ALOGE("Failed to create socketpair for message queue: %s", strerror(errno)); - return; - } -} - -void lorie_message_queue::write(std::function func) { - static uint64_t i = 1; - std::unique_lock lock(mutex); - queue.push(std::move(func)); - ::write(fd, &i, sizeof(uint64_t)); -} - -void lorie_message_queue::run() { - static uint64_t i = 0; - std::unique_lock lock(mutex); - std::function fn; - ::read(fd, &i, sizeof(uint64_t)); - while(!queue.empty()){ - fn = queue.front(); - queue.pop(); - - lock.unlock(); - fn(); - lock.lock(); - } -} - -int lorie_message_queue::get_fd() { - return fd; -} diff --git a/app/src/main/cpp/lorie-client/lorie_message_queue.hpp b/app/src/main/cpp/lorie-client/lorie_message_queue.hpp index 5cfbee2d6..1a46fe68f 100644 --- a/app/src/main/cpp/lorie-client/lorie_message_queue.hpp +++ b/app/src/main/cpp/lorie-client/lorie_message_queue.hpp @@ -1,17 +1,156 @@ #pragma once +#include #include +#include +#include #include #include +#include +#include +#include +#include +#include +#include + +static inline timespec operator+(const timespec& lhs, const timespec& rhs) { + timespec result{}; + result.tv_sec = lhs.tv_sec + rhs.tv_sec; + result.tv_nsec = lhs.tv_nsec + rhs.tv_nsec; + if (result.tv_nsec >= 1000000000L) { + ++result.tv_sec; + result.tv_nsec -= 1000000000L; + } + return result; +} + +static inline timespec operator-(const timespec& lhs, const timespec& rhs) { + timespec result{}; + result.tv_sec = lhs.tv_sec - rhs.tv_sec; + result.tv_nsec = lhs.tv_nsec - rhs.tv_nsec; + if (result.tv_nsec < 0) { + --result.tv_sec; + result.tv_nsec += 1000000000L; + } + return result; +} + +static inline bool operator<(const timespec& lhs, const timespec& rhs) { + if (lhs.tv_sec < rhs.tv_sec) { + return true; + } + if (lhs.tv_sec > rhs.tv_sec) { + return false; + } + return lhs.tv_nsec < rhs.tv_nsec; +} + +static inline timespec now() { + timespec now; // NOLINT(cppcoreguidelines-pro-type-member-init) + clock_gettime(CLOCK_MONOTONIC, &now); + return now; +} + +static inline timespec ms_to_timespec(long ms) { + return timespec { ms / 1000, (ms % 1000) * 1000000 }; +} + +void timespec_to_human_readable(const timespec& ts, const char* comment) { + time_t sec = ts.tv_sec; + tm timeinfo{}; + gmtime_r(&sec, &timeinfo); + ALOGE("%-10s %02d:%02d:%02d.%03ld", comment, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, ts.tv_nsec / 1000000); +} class lorie_message_queue { public: - lorie_message_queue(); - void write(std::function func); + lorie_message_queue() { + timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); + if (timer_fd < 0) { + ALOGE("Failed to create timerfd: %s", strerror(errno)); + } + + fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (fd < 0) { + ALOGE("Failed to create eventfd: %s", strerror(errno)); + } + // For some reason epoll_fd does not dispatch timerfd's events... + std::thread([=] { + struct pollfd pfd = { .fd = timer_fd, .events = POLLIN }; + while (poll(&pfd, 1, 1000) >= 0) { + if (pfd.revents & POLLIN) { + post([=] { dispatch_delayed(); }); + } + }; + }).detach(); + } + ~lorie_message_queue() { close(timer_fd); close(fd); } + + void post(std::function func, long ms_delay = 0) { + if (!ms_delay) { + static uint64_t i = 1; + std::unique_lock lock(mutex); + queue.push(std::move(func)); + ::write(fd, &i, sizeof(uint64_t)); + } else { + delayed_queue.emplace(task_t{ now() + ms_to_timespec(ms_delay), std::move(func) }); + reset_timer(); + } + } + + void run() { + static uint64_t i = 0; + std::unique_lock lock(mutex); + std::function fn; + ::read(fd, &i, sizeof(uint64_t)); + while(!queue.empty()){ + fn = queue.front(); + queue.pop(); - void run(); - int get_fd(); + lock.unlock(); + fn(); + lock.lock(); + } + } + + void dispatch_delayed() { + while (!delayed_queue.empty() && delayed_queue.top().expiration < now()) { + auto f = delayed_queue.top().f; + delayed_queue.pop(); + f(); + } + + reset_timer(); + } + + int get_fd() { + return fd; + } private: int fd; std::mutex mutex; std::queue> queue; +private: + void reset_timer() { + if (delayed_queue.empty()) + return; + + timespec planned = (delayed_queue.top().expiration - now() < ms_to_timespec(0)) + ? ms_to_timespec(20) + : delayed_queue.top().expiration - now(); + itimerspec t = { {}, planned }; + if (timerfd_settime(timer_fd, 0, &t, nullptr) < 0) + ALOGE("Failed to set timerfd: %s", strerror(errno)); + } + + struct task_t { + timespec expiration; + std::function f; + + bool operator<(const task_t& rhs) const { + return rhs.expiration < expiration; + } + }; + + int timer_fd; + std::priority_queue delayed_queue; }; From d893a3ea67e565a20ab364360ac3d3aa021a4da0 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Mon, 27 Feb 2023 20:28:49 +0200 Subject: [PATCH 05/21] Fixing drawing permonance and delayed tasks... --- app/src/main/cpp/CMakeLists.txt | 2 +- .../main/cpp/lorie-client/lorie-client.cpp | 155 +++++++++----- .../cpp/lorie-client/lorie_message_queue.hpp | 199 +++++++++--------- app/src/main/cpp/lorie/compositor.cpp | 2 + .../java/com/termux/x11/MainActivity.java | 5 +- 5 files changed, 214 insertions(+), 149 deletions(-) diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 547c09901..5a253097c 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -148,4 +148,4 @@ target_include_directories(xcvt PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/libxcvt/incl add_library(lorie-client SHARED ${CMAKE_CURRENT_SOURCE_DIR}/lorie-client/lorie-client.cpp ) -target_link_libraries(lorie-client xcbproto xcb xcb-errors xcb-shm xcb-xfixes xcb-xinput xcb-damage xcvt "-llog" "-landroid") \ No newline at end of file +target_link_libraries(lorie-client xcbproto xcb xcb-errors xcb-shm xcb-xfixes xcb-xinput xcb-damage xcvt "-llog" "-landroid") diff --git a/app/src/main/cpp/lorie-client/lorie-client.cpp b/app/src/main/cpp/lorie-client/lorie-client.cpp index 18f39fed5..7e5d2f99c 100644 --- a/app/src/main/cpp/lorie-client/lorie-client.cpp +++ b/app/src/main/cpp/lorie-client/lorie-client.cpp @@ -19,15 +19,20 @@ #include #include #include +#include #include #include +#include +#include #if 1 #define ALOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "LorieX11Client", fmt, ## __VA_ARGS__) #else #define ALOGE(fmt, ...) printf(fmt, ## __VA_ARGS__); printf("\n") #endif + #define unused __attribute__((unused)) +#define always_inline inline __attribute__((__always_inline__)) #include "lorie_message_queue.hpp" @@ -47,8 +52,6 @@ typedef int16_t i16 unused; typedef int32_t i32 unused; typedef int64_t i64 unused; -#define always_inline inline __attribute__((__always_inline__)) - #define xcb(name, ...) xcb_ ## name ## _reply(self.conn, xcb_ ## name(self.conn, ## __VA_ARGS__), &self.err) #define xcb_check(name, ...) self.err = xcb_request_check(self.conn, xcb_ ## name(self.conn, ## __VA_ARGS__)) @@ -285,8 +288,6 @@ class xcb_connection { } }; -#define ASHMEM_SET_SIZE _IOW(0x77, 3, size_t) - static inline int os_create_anonymous_file(size_t size) { int fd, ret; @@ -294,7 +295,7 @@ os_create_anonymous_file(size_t size) { fd = open("/dev/ashmem", O_RDWR | O_CLOEXEC); if (fd < 0) return fd; - ret = ioctl(fd, ASHMEM_SET_SIZE, size); + ret = ioctl(fd, /** ASHMEM_SET_SIZE */ _IOW(0x77, 3, size_t), size); if (ret < 0) goto err; flags = fcntl(fd, F_GETFD); @@ -383,24 +384,77 @@ class lorie_client { runner_thread = std::thread([=, this] { runner(); }); } - void post(std::function task, long ms_delay = 0) { + void post(std::function task) { + queue.post(std::move(task)); + } + + void post_delayed(std::function task, long ms_delay) { queue.post(std::move(task), ms_delay); } + int efd{}, xconn {-1}; void runner() { - ALooper_prepare(0); - ALooper_addFd(ALooper_forThread(), queue.get_fd(), ALOOPER_EVENT_INPUT, ALOOPER_POLL_CALLBACK, [](int, int, void *d) { - auto client = reinterpret_cast(d); - client->queue.run(); - return 1; - }, this); + struct epoll_event event{}; + struct epoll_event events[16]; + int ret; + + efd = epoll_create1(0); + if(efd < 0) { + ALOGE("epoll_create: %s", strerror(errno)); + abort(); + } + + event.data.fd = queue.efd; + event.events = EPOLLIN; + ret = epoll_ctl(efd, EPOLL_CTL_ADD, queue.efd, &event); + if(ret < 0) { + ALOGE("queue event epoll_ctl: %s", strerror(errno)); + abort(); + } - while(!terminate) ALooper_pollAll(500, nullptr, nullptr, nullptr); + event.data.fd = queue.timer; + event.events = EPOLLIN; + ret = epoll_ctl(efd, EPOLL_CTL_ADD, queue.timer, &event); + if(ret < 0) { + ALOGE("queue timer epoll_ctl: %s", strerror(errno)); + abort(); + } - ALooper_release(ALooper_forThread()); + while(!terminate) { + errno = 0; + int n = epoll_wait(efd, events, 16, 1000); + if (n == 0) + continue; + if (errno) + ALOGE("Epoll returned %s", strerror(errno)); + + for (int i=0; i(d); - if (mask & (ALOOPER_EVENT_INVALID | ALOOPER_EVENT_HANGUP | - ALOOPER_EVENT_ERROR)) { - xcb_disconnect(self->c.conn); - self->c.conn = nullptr; - self->set_renderer_visibility(false); - ALOGE("Disconnected"); - return 0; - } - - self->connection_poll_func(); - return 1; - }, this); + ALOGE("Adding connection with fd %d to poller", fd); + epoll_event event{}; + event.data.fd = fd; + event.events = EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLET | EPOLLHUP | EPOLLNVAL | EPOLLMSG; + int ret; + ret = epoll_ctl(efd, EPOLL_CTL_ADD, fd, &event); + if(ret < 0) { + ALOGE("x conection epoll_ctl: %s", strerror(errno)); + abort(); + } + + xconn = fd; set_renderer_visibility(true); refresh_screen(); @@ -518,25 +574,23 @@ class lorie_client { } void refresh_screen() { - //post([=] { ALOGE("DELAYED!!!"); refresh_screen(); }, 1000); // post with delay does not work... - ALOGE("REFRESH!!!"); - try { - if (screen.win && c.conn) { + if (screen.win && c.conn) { + try { xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(c.conn)).data; c.shm.get(s->root, 0, 0, s->width_in_pixels, s->height_in_pixels, ~0, // NOLINT(cppcoreguidelines-narrowing-conversions) XCB_IMAGE_FORMAT_Z_PIXMAP, screen.shmseg, 0); blit_exact(screen.win, screen.shmaddr, s->width_in_pixels, s->height_in_pixels); + } catch (std::runtime_error &err) { + ALOGE("Refreshing screen failed: %s", err.what()); } - } catch (std::runtime_error &err) { - ALOGE("Refreshing screen failed: %s", err.what()); } - // post([=]{ refresh_screen(); }); // this line makes video stream smoother but it consumes a lot of cpu. } void connection_poll_func() { try { + bool need_redraw = false; xcb_generic_event_t *event; // const char *ext; xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(c.conn)).data; @@ -549,12 +603,15 @@ class lorie_client { s->width_in_pixels = e->width; s->height_in_pixels = e->height; } else if (c.damage.is_damage_notify_event(event)) { - refresh_screen(); + need_redraw = true; } else if (c.fixes.is_cursor_notify_event(event)) { refresh_cursor(); } //else // ALOGE("some other event %s of %s", xcb_errors_get_name_for_core_event(c.err_ctx, event->response_type, &ext), (ext ?: "core")); } + + if (need_redraw) + refresh_screen(); } catch (std::exception& e) { ALOGE("Failure during processing X events: %s", e.what()); } @@ -564,7 +621,6 @@ class lorie_client { queue.post([=, this] { terminate = true; }); if (runner_thread.joinable()) runner_thread.join(); - close(queue.get_fd()); } JNIEnv* env{}; @@ -642,12 +698,12 @@ Java_com_termux_x11_MainActivity_onPointerMotion(JNIEnv *env, jobject thiz, jint client.cursor.x = x; client.cursor.y = y; + if (client.c.conn) { - client.post([=] { - xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(client.c.conn)).data; - xcb_test_fake_input(client.c.conn, XCB_MOTION_NOTIFY, false, XCB_CURRENT_TIME, s->root, x, y, 0); - xcb_flush(client.c.conn); - }); + // XCB is thread safe, no need to post this to dispatch thread. + xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(client.c.conn)).data; + xcb_test_fake_input(client.c.conn, XCB_MOTION_NOTIFY, false, XCB_CURRENT_TIME, s->root, x,y, 0); + xcb_flush(client.c.conn); } } @@ -664,11 +720,10 @@ JNIEXPORT void JNICALL Java_com_termux_x11_MainActivity_onPointerButton([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject thiz, jint button, jint type) { if (client.c.conn) { - client.post([=] { - xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(client.c.conn)).data; - xcb_test_fake_input(client.c.conn, type, button, XCB_CURRENT_TIME, s->root, 0, 0, 0); - xcb_flush(client.c.conn); - }); + // XCB is thread safe, no need to post this to dispatch thread. + xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(client.c.conn)).data; + xcb_test_fake_input(client.c.conn, type, button, XCB_CURRENT_TIME, s->root, 0, 0, 0); + xcb_flush(client.c.conn); } } diff --git a/app/src/main/cpp/lorie-client/lorie_message_queue.hpp b/app/src/main/cpp/lorie-client/lorie_message_queue.hpp index 1a46fe68f..a6f8ec0c2 100644 --- a/app/src/main/cpp/lorie-client/lorie_message_queue.hpp +++ b/app/src/main/cpp/lorie-client/lorie_message_queue.hpp @@ -10,98 +10,50 @@ #include #include #include -#include -static inline timespec operator+(const timespec& lhs, const timespec& rhs) { - timespec result{}; - result.tv_sec = lhs.tv_sec + rhs.tv_sec; - result.tv_nsec = lhs.tv_nsec + rhs.tv_nsec; - if (result.tv_nsec >= 1000000000L) { - ++result.tv_sec; - result.tv_nsec -= 1000000000L; - } - return result; -} - -static inline timespec operator-(const timespec& lhs, const timespec& rhs) { - timespec result{}; - result.tv_sec = lhs.tv_sec - rhs.tv_sec; - result.tv_nsec = lhs.tv_nsec - rhs.tv_nsec; - if (result.tv_nsec < 0) { - --result.tv_sec; - result.tv_nsec += 1000000000L; - } - return result; -} - -static inline bool operator<(const timespec& lhs, const timespec& rhs) { - if (lhs.tv_sec < rhs.tv_sec) { - return true; - } - if (lhs.tv_sec > rhs.tv_sec) { - return false; - } - return lhs.tv_nsec < rhs.tv_nsec; -} - -static inline timespec now() { - timespec now; // NOLINT(cppcoreguidelines-pro-type-member-init) - clock_gettime(CLOCK_MONOTONIC, &now); - return now; -} - -static inline timespec ms_to_timespec(long ms) { - return timespec { ms / 1000, (ms % 1000) * 1000000 }; -} - -void timespec_to_human_readable(const timespec& ts, const char* comment) { - time_t sec = ts.tv_sec; - tm timeinfo{}; - gmtime_r(&sec, &timeinfo); - ALOGE("%-10s %02d:%02d:%02d.%03ld", comment, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, ts.tv_nsec / 1000000); -} +static always_inline timespec operator+(const timespec& lhs, const timespec& rhs); +static always_inline timespec operator-(const timespec& lhs, const timespec& rhs); +static always_inline bool operator<(const timespec& lhs, const timespec& rhs); +static always_inline timespec now(); +static always_inline timespec ms_to_timespec(long ms); +static always_inline long timespec_to_ms(timespec& spec); +static always_inline void timespec_to_human_readable(const timespec& ts, const char* comment); class lorie_message_queue { public: + ALooper* looper{}; + + int efd; + lorie_message_queue() { - timer_fd = timerfd_create(CLOCK_MONOTONIC, 0); - if (timer_fd < 0) { + efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (efd == -1) { + ALOGE("Failed to create socketpair for message queue: %s", strerror(errno)); + return; + } + + timer = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); + if (timer < 0) { ALOGE("Failed to create timerfd: %s", strerror(errno)); + return; } - - fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); - if (fd < 0) { - ALOGE("Failed to create eventfd: %s", strerror(errno)); - } - // For some reason epoll_fd does not dispatch timerfd's events... - std::thread([=] { - struct pollfd pfd = { .fd = timer_fd, .events = POLLIN }; - while (poll(&pfd, 1, 1000) >= 0) { - if (pfd.revents & POLLIN) { - post([=] { dispatch_delayed(); }); - } - }; - }).detach(); } - ~lorie_message_queue() { close(timer_fd); close(fd); } void post(std::function func, long ms_delay = 0) { - if (!ms_delay) { - static uint64_t i = 1; - std::unique_lock lock(mutex); + std::unique_lock lock(mutex); + if (ms_delay <= 0) { queue.push(std::move(func)); - ::write(fd, &i, sizeof(uint64_t)); + ::write(efd, (const uint64_t[]) {1}, sizeof(uint64_t)); } else { delayed_queue.emplace(task_t{ now() + ms_to_timespec(ms_delay), std::move(func) }); reset_timer(); } - } + }; void run() { - static uint64_t i = 0; - std::unique_lock lock(mutex); std::function fn; - ::read(fd, &i, sizeof(uint64_t)); + std::unique_lock lock(mutex); + ::read(efd, (void*) (const uint64_t[]) {1}, sizeof(uint64_t)); while(!queue.empty()){ fn = queue.front(); queue.pop(); @@ -110,37 +62,23 @@ class lorie_message_queue { fn(); lock.lock(); } - } - + }; void dispatch_delayed() { + std::unique_lock lock(mutex); while (!delayed_queue.empty() && delayed_queue.top().expiration < now()) { - auto f = delayed_queue.top().f; + auto fn = delayed_queue.top().f; delayed_queue.pop(); - f(); + + lock.unlock(); + fn(); + lock.lock(); } reset_timer(); } - int get_fd() { - return fd; - } -private: - int fd; - std::mutex mutex; - std::queue> queue; + int timer; private: - void reset_timer() { - if (delayed_queue.empty()) - return; - - timespec planned = (delayed_queue.top().expiration - now() < ms_to_timespec(0)) - ? ms_to_timespec(20) - : delayed_queue.top().expiration - now(); - itimerspec t = { {}, planned }; - if (timerfd_settime(timer_fd, 0, &t, nullptr) < 0) - ALOGE("Failed to set timerfd: %s", strerror(errno)); - } struct task_t { timespec expiration; @@ -151,6 +89,73 @@ class lorie_message_queue { } }; - int timer_fd; + void reset_timer() { + if (delayed_queue.empty()) { + timerfd_settime(timer, 0, &zero, nullptr); + return; + } + + itimerspec t = { {}, delayed_queue.top().expiration }; + if (timerfd_settime(timer, TFD_TIMER_ABSTIME, &t, nullptr) < 0) + ALOGE("Failed to set timerfd: %s", strerror(errno)); + } + + std::mutex mutex; + std::queue> queue; std::priority_queue delayed_queue; + + static constexpr itimerspec zero = {{0, 0}, {0, 0}}; }; + +static always_inline timespec operator+(const timespec& lhs, const timespec& rhs) { + timespec result{}; + result.tv_sec = lhs.tv_sec + rhs.tv_sec; + result.tv_nsec = lhs.tv_nsec + rhs.tv_nsec; + if (result.tv_nsec >= 1000000000L) { + ++result.tv_sec; + result.tv_nsec -= 1000000000L; + } + return result; +} + +static always_inline timespec operator-(const timespec& lhs, const timespec& rhs) { + timespec result{}; + result.tv_sec = lhs.tv_sec - rhs.tv_sec; + result.tv_nsec = lhs.tv_nsec - rhs.tv_nsec; + if (result.tv_nsec < 0) { + --result.tv_sec; + result.tv_nsec += 1000000000L; + } + return result; +} + +static always_inline bool operator<(const timespec& lhs, const timespec& rhs) { + if (lhs.tv_sec < rhs.tv_sec) { + return true; + } + if (lhs.tv_sec > rhs.tv_sec) { + return false; + } + return lhs.tv_nsec < rhs.tv_nsec; +} + +static always_inline timespec now() { + timespec now; // NOLINT(cppcoreguidelines-pro-type-member-init) + clock_gettime(CLOCK_MONOTONIC, &now); + return now; +} + +static always_inline timespec ms_to_timespec(long ms) { + return timespec { ms / 1000, (ms % 1000) * 1000000 }; +} + +static always_inline long timespec_to_ms(timespec spec) { + return spec.tv_sec * 1000 + spec.tv_nsec / 1000000 ; +} + +static always_inline void timespec_to_human_readable(const timespec& ts, const char* comment) { + time_t sec = ts.tv_sec; + tm timeinfo{}; + gmtime_r(&sec, &timeinfo); + ALOGE("%-10s %02d:%02d:%02d.%03ld", comment, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, ts.tv_nsec / 1000000); +} diff --git a/app/src/main/cpp/lorie/compositor.cpp b/app/src/main/cpp/lorie/compositor.cpp index 650e7f48b..ffa3f85bc 100644 --- a/app/src/main/cpp/lorie/compositor.cpp +++ b/app/src/main/cpp/lorie/compositor.cpp @@ -207,6 +207,8 @@ lorie_compositor::lorie_compositor(int dpi): dpi(dpi) { } void lorie_compositor::report_mode(int width, int height) { + if (width == 0 || height == 0) + return; int mm_width = int(width * 25.4 / dpi); int mm_height = int(height * 25.4 / dpi); wl_client* client; diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 7c3202eb2..47e32cb32 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -401,7 +401,10 @@ void setRendererVisibility(boolean visible) { @SuppressWarnings("unused") // It is used in native code void setCursorVisibility(boolean visible) { - runOnUiThread(()-> findViewById(R.id.cursorView).setVisibility(visible?View.VISIBLE:View.INVISIBLE)); + View cursor = findViewById(R.id.cursorView); + int visibility = visible?View.VISIBLE:View.INVISIBLE; + if (cursor.getVisibility() != visibility) + runOnUiThread(()-> findViewById(R.id.cursorView).setVisibility(visibility)); } @SuppressWarnings("unused") From 1628809158b7531cf612889ef5b9a728e3293c11 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Tue, 28 Feb 2023 00:56:41 +0200 Subject: [PATCH 06/21] Basic super primitive keyboard support --- app/src/main/cpp/CMakeLists.txt | 25 +- .../android-keycodes-to-x11-keysyms.h | 8 +- .../main/cpp/lorie-client/lorie-client.cpp | 424 ++++-------------- .../cpp/lorie-client/lorie_message_queue.hpp | 81 +++- .../main/cpp/lorie-client/xcb-connection.hpp | 388 ++++++++++++++++ .../java/com/termux/x11/MainActivity.java | 23 +- 6 files changed, 580 insertions(+), 369 deletions(-) create mode 100644 app/src/main/cpp/lorie-client/xcb-connection.hpp diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 5a253097c..5790e9272 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -3,7 +3,6 @@ project(lorie) set(CMAKE_CXX_STANDARD 20) find_package(Python3 REQUIRED) -find_package(BISON 3.0 REQUIRED) #################################################################################################### ######################################### LIBWAYLAND ############################################### @@ -141,11 +140,27 @@ target_include_directories(xcvt PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/libxcvt/incl ####################################### LIBXKB-COMMON ############################################## #################################################################################################### +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/config.h "") +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/config.c + "#include + const struct xkb_keymap_format_ops text_v1_keymap_format_ops = {};") +set(XKBCOMMON_SOURCES atom.c context.c context-priv.c keysym.c keysym-utf.c keymap.c keymap-priv.c + state.c text.c utf8.c utils.c x11/keymap.c x11/state.c x11/util.c) +list(TRANSFORM XKBCOMMON_SOURCES PREPEND ${CMAKE_CURRENT_SOURCE_DIR}/libxkbcommon/src/) +set(XKBCOMMON_SOURCES ${XKBCOMMON_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/config.c) +add_library(xkbcommon ${XKBCOMMON_SOURCES}) +target_compile_options(xkbcommon PRIVATE + "-DHAVE_STRNDUP=1" "-DDEFAULT_XKB_LAYOUT=\"us\"" "-DDEFAULT_XKB_MODEL=\"pc105\"" + "-DDEFAULT_XKB_OPTIONS=NULL" "-DDEFAULT_XKB_RULES=\"evdev\"" "-DDEFAULT_XKB_VARIANT=NULL" + "-DDFLT_XKB_CONFIG_EXTRA_PATH=\"/data/data/com.termux.x11/files\"" + "-DDFLT_XKB_CONFIG_ROOT=\"/data/data/com.termux.x11/files\"") +target_include_directories(xkbcommon PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/libxkbcommon/src") +target_include_directories(xkbcommon PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/libxkbcommon/include") +target_link_libraries(xkbcommon xcbproto) + #################################################################################################### ####################################### LORIE-CLIENT ############################################### #################################################################################################### -add_library(lorie-client SHARED - ${CMAKE_CURRENT_SOURCE_DIR}/lorie-client/lorie-client.cpp - ) -target_link_libraries(lorie-client xcbproto xcb xcb-errors xcb-shm xcb-xfixes xcb-xinput xcb-damage xcvt "-llog" "-landroid") +add_library(lorie-client SHARED ${CMAKE_CURRENT_SOURCE_DIR}/lorie-client/lorie-client.cpp) +target_link_libraries(lorie-client xkbcommon xcbproto xcb xcb-errors xcb-shm xcb-xfixes xcb-xinput xcb-damage xcvt "-llog" "-landroid") diff --git a/app/src/main/cpp/lorie-client/android-keycodes-to-x11-keysyms.h b/app/src/main/cpp/lorie-client/android-keycodes-to-x11-keysyms.h index 1d95b03a6..73692917a 100644 --- a/app/src/main/cpp/lorie-client/android-keycodes-to-x11-keysyms.h +++ b/app/src/main/cpp/lorie-client/android-keycodes-to-x11-keysyms.h @@ -1,3 +1,5 @@ +#define XK_MISCELLANY +#define XK_LATIN1 #include /* @@ -9,7 +11,7 @@ * which keeps track on Xserver's keymap, otherways non-English * keyboard layouts will be broken. */ -int keysyms = { +int android_to_keysyms[] = { /* ANDROID_KEYCODE_UNKNOWN */ 0, /* ANDROID_KEYCODE_SOFT_LEFT */ 0, /* ANDROID_KEYCODE_SOFT_RIGHT */ 0, @@ -144,7 +146,7 @@ int keysyms = { /* ANDROID_KEYCODE_F1 */ XK_F1, /* ANDROID_KEYCODE_F2 */ XK_F2, /* ANDROID_KEYCODE_F3 */ XK_F3, - /* ANDROID_KEYCODE_F4 */ XK_F4 + /* ANDROID_KEYCODE_F4 */ XK_F4, /* ANDROID_KEYCODE_F5 */ XK_F5, /* ANDROID_KEYCODE_F6 */ XK_F6, /* ANDROID_KEYCODE_F7 */ XK_F7, @@ -297,4 +299,4 @@ int keysyms = { /* ANDROID_KEYCODE_ALL_APPS */ 0, /* ANDROID_KEYCODE_REFRESH */ 0 /* ANDROID_KEYCODE_LAST */ -} +}; diff --git a/app/src/main/cpp/lorie-client/lorie-client.cpp b/app/src/main/cpp/lorie-client/lorie-client.cpp index 7e5d2f99c..5b0af1936 100644 --- a/app/src/main/cpp/lorie-client/lorie-client.cpp +++ b/app/src/main/cpp/lorie-client/lorie-client.cpp @@ -1,14 +1,7 @@ #include #include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include @@ -24,6 +17,7 @@ #include #include #include +#include #if 1 #define ALOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "LorieX11Client", fmt, ## __VA_ARGS__) @@ -31,10 +25,12 @@ #define ALOGE(fmt, ...) printf(fmt, ## __VA_ARGS__); printf("\n") #endif -#define unused __attribute__((unused)) #define always_inline inline __attribute__((__always_inline__)) #include "lorie_message_queue.hpp" +#include "xcb-connection.hpp" +#include "android-keycodes-to-x11-keysyms.h" + // To avoid reopening new segment on every screen resolution // change we can open it only once with some maximal size @@ -43,251 +39,6 @@ #pragma ide diagnostic ignored "ConstantParameter" #pragma ide diagnostic ignored "cppcoreguidelines-narrowing-conversions" -typedef uint8_t u8 unused; -typedef uint16_t u16 unused; -typedef uint32_t u32 unused; -typedef uint64_t u64 unused; -typedef int8_t i8 unused; -typedef int16_t i16 unused; -typedef int32_t i32 unused; -typedef int64_t i64 unused; - -#define xcb(name, ...) xcb_ ## name ## _reply(self.conn, xcb_ ## name(self.conn, ## __VA_ARGS__), &self.err) -#define xcb_check(name, ...) self.err = xcb_request_check(self.conn, xcb_ ## name(self.conn, ## __VA_ARGS__)) - -class xcb_connection { -private: - -public: - xcb_connection_t *conn{}; - xcb_generic_error_t* err{}; // not thread-safe, but the whole class should be used in separate thread - xcb_errors_context_t *err_ctx{}; - - template - always_inline void handle_error(REPLY* reply, std::string description) { // NOLINT(performance-unnecessary-value-param) - if (err) { - const char* ext{}; - const char* err_name = xcb_errors_get_name_for_error(err_ctx, err->error_code, &ext); - std::string err_text = description + "\n" + - "XCB Error of failed request: " + (ext?:"") + "::" + err_name + "\n" + - " Major opcode of failed request: " + std::to_string(err->major_code) + " (" + - xcb_errors_get_name_for_major_code(err_ctx, err->major_code) + ")\n" + - " Minor opcode of failed request: " + std::to_string(err->minor_code) + " (" + - xcb_errors_get_name_for_minor_code(err_ctx, err->major_code, err->minor_code) + ")\n" + - " Serial number of failed request: " + std::to_string(err->sequence) + "\n" + - " Current serial number in output stream: " + std::to_string(err->full_sequence); - - free(reply); - free(err); - err = nullptr; - throw std::runtime_error(err_text); - } - } - - always_inline void handle_error(std::string description) { // NOLINT(performance-unnecessary-value-param) - if (err) { - const char* ext{}; - const char* err_name = xcb_errors_get_name_for_error(err_ctx, err->error_code, &ext); - std::string err_text = description + "\n" + - "XCB Error of failed request: " + (ext?:"") + "::" + err_name + "\n" + - " Major opcode of failed request: " + std::to_string(err->major_code) + " (" + - xcb_errors_get_name_for_major_code(err_ctx, err->major_code) + ")\n" + - " Minor opcode of failed request: " + std::to_string(err->minor_code) + " (" + - xcb_errors_get_name_for_minor_code(err_ctx, err->major_code, err->minor_code) + ")\n" + - " Serial number of failed request: " + std::to_string(err->sequence) + "\n" + - " Current serial number in output stream: " + std::to_string(err->full_sequence); - free(err); - err = nullptr; - throw std::runtime_error(err_text); - } - } - - struct { - xcb_connection& self; - i32 first_event{}; - void init() { - { - auto reply = xcb(shm_query_version); - self.handle_error(reply, "Error querying MIT-SHM extension"); - free(reply); - } - { - auto reply = xcb(query_extension, 6, "DAMAGE"); - self.handle_error(reply, "Error querying DAMAGE extension"); - first_event = reply->first_event; - free(reply); - } - }; - - void attach_fd(u32 seg, i32 fd, u8 ro) { - xcb_check(shm_attach_fd, seg, fd, ro); - self.handle_error("Error attaching file descriptor through MIT-SHM extension"); - }; - - void unused detach(u32 seg) { - xcb_check(shm_detach, seg); - self.handle_error("Error attaching shared segment through MIT-SHM extension"); - } - - xcb_shm_get_image_reply_t* get(xcb_drawable_t d, i16 x, i16 y, i16 w, i16 h, u32 m, u8 f, xcb_shm_seg_t s, u32 o) { - auto reply = xcb(shm_get_image, d, x, y, w, h, m, f, s, o); - self.handle_error(reply, "Error getting shm image through MIT-SHM extension"); - return reply; - }; - } shm {*this}; - - struct { - xcb_connection& self; - i32 first_event{}; - - void init() { - { - auto reply = xcb(query_extension, 6, "DAMAGE"); - self.handle_error(reply, "Error querying DAMAGE extension"); - first_event = reply->first_event; - free(reply); - } - { - auto reply = xcb(damage_query_version, 1, 1); - self.handle_error(reply, "Error querying DAMAGE extension"); - free(reply); - } - } - - void create(xcb_drawable_t d, uint8_t l) { - xcb_check(damage_create, xcb_generate_id(self.conn), d, l); - self.handle_error("Error creating damage"); - } - - inline bool is_damage_notify_event(xcb_generic_event_t *ev) const { - return ev->response_type == (first_event + XCB_DAMAGE_NOTIFY); - } - } damage {*this}; - - struct { - xcb_connection& self; - i32 opcode{}; - void init() { - { - auto reply = xcb(query_extension, 15, "XInputExtension"); - self.handle_error(reply, "Error querying XInputExtension extension"); - opcode = reply->major_opcode; - free(reply); - } - { - auto reply = xcb(input_get_extension_version, 15, "XInputExtension"); - self.handle_error(reply, "Error querying XInputExtension extension"); - free(reply); - } - { - auto reply = xcb(input_xi_query_version, 2, 2); - self.handle_error(reply, "Error querying XInputExtension extension"); - free(reply); - } - } - - xcb_input_device_id_t client_pointer_id() { - xcb_input_device_id_t id; - auto reply = xcb(input_xi_get_client_pointer, XCB_NONE); - self.handle_error(reply, "Error getting client pointer device id"); - id = reply->deviceid; - free(reply); - return id; - } - - void select_events(xcb_window_t window, uint16_t num_mask, const xcb_input_event_mask_t *masks){ - xcb_check(input_xi_select_events, window, num_mask, masks); - self.handle_error("Error selecting Xi events"); - } - - inline bool is_raw_motion_event(xcb_generic_event_t *ev) const { - union { // NOLINT(cppcoreguidelines-pro-type-member-init) - xcb_generic_event_t *event; - xcb_ge_event_t *ge; - }; - event = ev; - return ev->response_type == XCB_GE_GENERIC && /* cookie */ ge->pad0 == opcode && ge->event_type == XCB_INPUT_RAW_MOTION; - } - } input {*this}; - - struct { - xcb_connection& self; - int first_event{}; - void init() { - { - auto reply = xcb(query_extension, 6, "XFIXES"); - self.handle_error(reply, "Error querying XFIXES extension"); - first_event = reply->first_event; - free(reply); - } - { - auto reply = xcb(xfixes_query_version, 4, 0); - self.handle_error(reply, "Error querying XFIXES extension"); - free(reply); - } - } - - void select_input(xcb_window_t window, uint32_t mask) { - xcb_check(xfixes_select_cursor_input, window, mask); - self.handle_error("Error querying selecting XFIXES input"); - } - - bool is_cursor_notify_event(xcb_generic_event_t* e) const { - return e->response_type == first_event + XCB_XFIXES_CURSOR_NOTIFY; - } - - xcb_xfixes_get_cursor_image_reply_t* unused get_cursor_image() { - auto reply = xcb(xfixes_get_cursor_image); - self.handle_error(reply, "Error getting XFIXES cursor image"); - return reply; - } - } fixes {*this}; - - struct { - xcb_connection &self; - void init() { - auto reply = xcb(test_get_version, 2, 2); - self.handle_error(reply, "Error querying XFIXES extension"); - free(reply); - } - } xtest {*this}; - - void init(int sockfd) { - xcb_connection_t* new_conn = xcb_connect_to_fd(sockfd, nullptr); - int conn_err = xcb_connection_has_error(new_conn); - if (conn_err) { - const char *s; - switch (conn_err) { -#define c(name) case name: s = static_cast(#name); break - c(XCB_CONN_ERROR); - c(XCB_CONN_CLOSED_EXT_NOTSUPPORTED); - c(XCB_CONN_CLOSED_MEM_INSUFFICIENT); - c(XCB_CONN_CLOSED_REQ_LEN_EXCEED); - c(XCB_CONN_CLOSED_PARSE_ERR); - c(XCB_CONN_CLOSED_INVALID_SCREEN); - c(XCB_CONN_CLOSED_FDPASSING_FAILED); - default: - s = "UNKNOWN"; -#undef c - } - throw std::runtime_error(std::string() + "XCB connection has error: " + s); - } - - if (err_ctx) - xcb_errors_context_free(err_ctx); - if (conn) - xcb_disconnect(conn); - conn = new_conn; - xcb_errors_context_new(conn, &err_ctx); - - shm.init(); - xtest.init(); - damage.init(); - input.init(); - fixes.init(); - } -}; - static inline int os_create_anonymous_file(size_t size) { int fd, ret; @@ -360,9 +111,10 @@ static always_inline void blit_exact(ANativeWindow* win, const uint32_t* src, in class lorie_client { public: - lorie_message_queue queue; + lorie_looper looper; std::thread runner_thread; bool terminate = false; + bool paused = false; xcb_connection c; @@ -381,76 +133,20 @@ class lorie_client { lorie_client() { - runner_thread = std::thread([=, this] { runner(); }); + runner_thread = std::thread([=, this] { + while(!terminate) { + looper.dispatch(1000); + } + }); } void post(std::function task) { - queue.post(std::move(task)); + looper.post(std::move(task)); } + [[maybe_unused]] void post_delayed(std::function task, long ms_delay) { - queue.post(std::move(task), ms_delay); - } - - int efd{}, xconn {-1}; - void runner() { - struct epoll_event event{}; - struct epoll_event events[16]; - int ret; - - efd = epoll_create1(0); - if(efd < 0) { - ALOGE("epoll_create: %s", strerror(errno)); - abort(); - } - - event.data.fd = queue.efd; - event.events = EPOLLIN; - ret = epoll_ctl(efd, EPOLL_CTL_ADD, queue.efd, &event); - if(ret < 0) { - ALOGE("queue event epoll_ctl: %s", strerror(errno)); - abort(); - } - - event.data.fd = queue.timer; - event.events = EPOLLIN; - ret = epoll_ctl(efd, EPOLL_CTL_ADD, queue.timer, &event); - if(ret < 0) { - ALOGE("queue timer epoll_ctl: %s", strerror(errno)); - abort(); - } - - while(!terminate) { - errno = 0; - int n = epoll_wait(efd, events, 16, 1000); - if (n == 0) - continue; - if (errno) - ALOGE("Epoll returned %s", strerror(errno)); - - for (int i=0; iroot, 0, 0, s->width_in_pixels, s->height_in_pixels, @@ -606,8 +306,7 @@ class lorie_client { need_redraw = true; } else if (c.fixes.is_cursor_notify_event(event)) { refresh_cursor(); - } //else - // ALOGE("some other event %s of %s", xcb_errors_get_name_for_core_event(c.err_ctx, event->response_type, &ext), (ext ?: "core")); + } } if (need_redraw) @@ -618,7 +317,7 @@ class lorie_client { } ~lorie_client() { - queue.post([=, this] { terminate = true; }); + looper.post([=, this] { terminate = true; }); if (runner_thread.joinable()) runner_thread.join(); } @@ -732,5 +431,54 @@ JNIEXPORT void JNICALL Java_com_termux_x11_MainActivity_onKeyboardKey([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject thiz, [[maybe_unused]] jint key, [[maybe_unused]] jint type, [[maybe_unused]] jint shift, [[maybe_unused]] jstring characters) { - // TODO: implement onKeyboardKey() + if (client.c.conn) { + ALOGE("Sending key %d %d", key, android_to_keysyms[key]); + if (android_to_keysyms[key] != 0) { + xkb_keycode_t keycode = 0; + ALOGE("Trying to send android keycode %d which corresponds to keysym 0x%X", key, android_to_keysyms[key]); + for (auto& code: client.c.xkb.codes) { + if (code.keysym == android_to_keysyms[key]) + keycode = code.keycode; + } + if (keycode != 0) { + ALOGE("Sending keycode %d", keycode); + xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(client.c.conn)).data; + auto cookie = xcb_test_fake_input(client.c.conn, type, keycode, + XCB_CURRENT_TIME, s->root, 0, 0, 0); + client.c.err = xcb_request_check(client.c.conn, cookie); + client.c.handle_error("Sending fake keypress failed"); + } + } + } +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_termux_x11_MainActivity_nativeResume([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject thiz) { + client.post([] { + client.paused = false; + client.refresh_screen(); + }); } +extern "C" +JNIEXPORT void JNICALL +Java_com_termux_x11_MainActivity_nativePause([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject thiz) { + client.paused = true; +} + +#if 0 +// It is needed to redirect stderr to logcat +[[maybe_unused]] std::thread stderr_to_logcat_thread([]{ + FILE *fp; + int p[2]; + size_t len; + char* line = nullptr; + pipe(p); + fp = fdopen(p[0], "r"); + + dup2(p[1], 2); + while ((getline(&line, &len, fp)) != -1) { + __android_log_write(ANDROID_LOG_VERBOSE, "stderr", line); + } +}); +#endif diff --git a/app/src/main/cpp/lorie-client/lorie_message_queue.hpp b/app/src/main/cpp/lorie-client/lorie_message_queue.hpp index a6f8ec0c2..3eb65b933 100644 --- a/app/src/main/cpp/lorie-client/lorie_message_queue.hpp +++ b/app/src/main/cpp/lorie-client/lorie_message_queue.hpp @@ -9,26 +9,29 @@ #include #include #include -#include +#include +#include static always_inline timespec operator+(const timespec& lhs, const timespec& rhs); static always_inline timespec operator-(const timespec& lhs, const timespec& rhs); static always_inline bool operator<(const timespec& lhs, const timespec& rhs); static always_inline timespec now(); static always_inline timespec ms_to_timespec(long ms); -static always_inline long timespec_to_ms(timespec& spec); -static always_inline void timespec_to_human_readable(const timespec& ts, const char* comment); +[[maybe_unused]] static always_inline long timespec_to_ms(timespec& spec); +[[maybe_unused]] static always_inline void timespec_to_human_readable(const timespec& ts, const char* comment); -class lorie_message_queue { +class lorie_looper { public: - ALooper* looper{}; - - int efd; + lorie_looper() { + efd = epoll_create1(0); + if (efd < 0) { + ALOGE("Failed to create epoll: %s", strerror(errno)); + return; + } - lorie_message_queue() { - efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); - if (efd == -1) { - ALOGE("Failed to create socketpair for message queue: %s", strerror(errno)); + evfd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (evfd < 0) { + ALOGE("Failed to create eventfd: %s", strerror(errno)); return; } @@ -37,23 +40,62 @@ class lorie_message_queue { ALOGE("Failed to create timerfd: %s", strerror(errno)); return; } + + add_fd(evfd, EPOLLIN, [=, this] (uint32_t) { dispatch_queue(); }); + add_fd(timer, EPOLLIN, [=, this] (uint32_t) { dispatch_delayed_queue(); }); + } + + ~lorie_looper() { + close(efd); + close(timer); + close(evfd); } void post(std::function func, long ms_delay = 0) { std::unique_lock lock(mutex); if (ms_delay <= 0) { queue.push(std::move(func)); - ::write(efd, (const uint64_t[]) {1}, sizeof(uint64_t)); + ::write(evfd, (const uint64_t[]) {1}, sizeof(uint64_t)); } else { delayed_queue.emplace(task_t{ now() + ms_to_timespec(ms_delay), std::move(func) }); reset_timer(); } }; - void run() { + void add_fd(int fd, uint32_t mask, std::function callback) { + struct epoll_event event { .events=mask, .data ={ .fd=fd } }; + event.data.fd = fd; + event.events = mask; + { + std::lock_guard lock(mutex); + callbacks[fd] = std::move(callback); + epoll_ctl(efd, EPOLL_CTL_ADD, fd, &event); + } + } + + void remove_fd(int fd) { + std::lock_guard lock(mutex); + callbacks.erase(fd); + epoll_ctl(efd, EPOLL_CTL_DEL, fd, nullptr); + } + + void dispatch(int timeout) { + std::vector events(1024); + int num_events = epoll_wait(efd, events.data(), events.size(), timeout); + for (int i = 0; i < num_events; ++i) { + int fd = events[i].data.fd; + if (callbacks.count(fd) != 0) { + callbacks[fd](events[i].events); + } + } + } +private: + int efd, evfd, timer; + + void dispatch_queue() { std::function fn; std::unique_lock lock(mutex); - ::read(efd, (void*) (const uint64_t[]) {1}, sizeof(uint64_t)); + ::read(evfd, (void*) (const uint64_t[]) {1}, sizeof(uint64_t)); while(!queue.empty()){ fn = queue.front(); queue.pop(); @@ -63,7 +105,8 @@ class lorie_message_queue { lock.lock(); } }; - void dispatch_delayed() { + + void dispatch_delayed_queue() { std::unique_lock lock(mutex); while (!delayed_queue.empty() && delayed_queue.top().expiration < now()) { auto fn = delayed_queue.top().f; @@ -77,9 +120,6 @@ class lorie_message_queue { reset_timer(); } - int timer; -private: - struct task_t { timespec expiration; std::function f; @@ -103,6 +143,7 @@ class lorie_message_queue { std::mutex mutex; std::queue> queue; std::priority_queue delayed_queue; + std::unordered_map> callbacks; static constexpr itimerspec zero = {{0, 0}, {0, 0}}; }; @@ -149,11 +190,11 @@ static always_inline timespec ms_to_timespec(long ms) { return timespec { ms / 1000, (ms % 1000) * 1000000 }; } -static always_inline long timespec_to_ms(timespec spec) { +[[maybe_unused]] static always_inline long timespec_to_ms(timespec spec) { return spec.tv_sec * 1000 + spec.tv_nsec / 1000000 ; } -static always_inline void timespec_to_human_readable(const timespec& ts, const char* comment) { +[[maybe_unused]] static always_inline void timespec_to_human_readable(const timespec& ts, const char* comment) { time_t sec = ts.tv_sec; tm timeinfo{}; gmtime_r(&sec, &timeinfo); diff --git a/app/src/main/cpp/lorie-client/xcb-connection.hpp b/app/src/main/cpp/lorie-client/xcb-connection.hpp new file mode 100644 index 000000000..746c3b54f --- /dev/null +++ b/app/src/main/cpp/lorie-client/xcb-connection.hpp @@ -0,0 +1,388 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define explicit c_explicit +#include +#undef explicit + +#define xcb(name, ...) xcb_ ## name ## _reply(self.conn, xcb_ ## name(self.conn, ## __VA_ARGS__), &self.err) +#define xcb_check(name, ...) self.err = xcb_request_check(self.conn, xcb_ ## name(self.conn, ## __VA_ARGS__)) + +[[maybe_unused]] typedef uint8_t u8; +[[maybe_unused]] typedef uint16_t u16; +[[maybe_unused]] typedef uint32_t u32; +[[maybe_unused]] typedef uint64_t u64; +[[maybe_unused]] typedef int8_t i8; +[[maybe_unused]] typedef int16_t i16; +[[maybe_unused]] typedef int32_t i32; +[[maybe_unused]] typedef int64_t i64; + +class xcb_connection { +private: + +public: + xcb_connection_t *conn{}; + xcb_generic_error_t* err{}; // not thread-safe, but the whole class should be used in separate thread + xcb_errors_context_t *err_ctx{}; + + template + always_inline void handle_error(REPLY* reply, std::string description) { // NOLINT(performance-unnecessary-value-param) + if (err) { + const char* ext{}; + const char* err_name = xcb_errors_get_name_for_error(err_ctx, err->error_code, &ext); + std::string err_text = description + "\n" + + "XCB Error of failed request: " + (ext?:"") + "::" + err_name + "\n" + + " Major opcode of failed request: " + std::to_string(err->major_code) + " (" + + xcb_errors_get_name_for_major_code(err_ctx, err->major_code) + ")\n" + + " Minor opcode of failed request: " + std::to_string(err->minor_code) + " (" + + xcb_errors_get_name_for_minor_code(err_ctx, err->major_code, err->minor_code) + ")\n" + + " Serial number of failed request: " + std::to_string(err->sequence) + "\n" + + " Current serial number in output stream: " + std::to_string(err->full_sequence); + + free(reply); + free(err); + err = nullptr; + throw std::runtime_error(err_text); + } + } + + always_inline void handle_error(std::string description) { // NOLINT(performance-unnecessary-value-param) + if (err) { + const char* ext{}; + const char* err_name = xcb_errors_get_name_for_error(err_ctx, err->error_code, &ext); + std::string err_text = description + "\n" + + "XCB Error of failed request: " + (ext?:"") + "::" + err_name + "\n" + + " Major opcode of failed request: " + std::to_string(err->major_code) + " (" + + xcb_errors_get_name_for_major_code(err_ctx, err->major_code) + ")\n" + + " Minor opcode of failed request: " + std::to_string(err->minor_code) + " (" + + xcb_errors_get_name_for_minor_code(err_ctx, err->major_code, err->minor_code) + ")\n" + + " Serial number of failed request: " + std::to_string(err->sequence) + "\n" + + " Current serial number in output stream: " + std::to_string(err->full_sequence); + free(err); + err = nullptr; + throw std::runtime_error(err_text); + } + } + + struct { + xcb_connection& self; + i32 first_event{}; + void init() { + { + auto reply = xcb(shm_query_version); + self.handle_error(reply, "Error querying MIT-SHM extension"); + free(reply); + } + { + auto reply = xcb(query_extension, 6, "DAMAGE"); + self.handle_error(reply, "Error querying DAMAGE extension"); + first_event = reply->first_event; + free(reply); + } + }; + + void attach_fd(u32 seg, i32 fd, u8 ro) { + xcb_check(shm_attach_fd, seg, fd, ro); + self.handle_error("Error attaching file descriptor through MIT-SHM extension"); + }; + + [[maybe_unused]] void detach(u32 seg) { + xcb_check(shm_detach, seg); + self.handle_error("Error attaching shared segment through MIT-SHM extension"); + } + + xcb_shm_get_image_reply_t* get(xcb_drawable_t d, i16 x, i16 y, i16 w, i16 h, u32 m, u8 f, xcb_shm_seg_t s, u32 o) { + auto reply = xcb(shm_get_image, d, x, y, w, h, m, f, s, o); + self.handle_error(reply, "Error getting shm image through MIT-SHM extension"); + return reply; + }; + } shm {*this}; + + struct { + xcb_connection& self; + i32 first_event{}; + + void init() { + { + auto reply = xcb(query_extension, 6, "DAMAGE"); + self.handle_error(reply, "Error querying DAMAGE extension"); + first_event = reply->first_event; + free(reply); + } + { + auto reply = xcb(damage_query_version, 1, 1); + self.handle_error(reply, "Error querying DAMAGE extension"); + free(reply); + } + } + + void create(xcb_drawable_t d, uint8_t l) { + xcb_check(damage_create, xcb_generate_id(self.conn), d, l); + self.handle_error("Error creating damage"); + } + + inline bool is_damage_notify_event(xcb_generic_event_t *ev) const { + return ev->response_type == (first_event + XCB_DAMAGE_NOTIFY); + } + } damage {*this}; + + struct { + xcb_connection& self; + i32 opcode{}; + void init() { + { + auto reply = xcb(query_extension, 15, "XInputExtension"); + self.handle_error(reply, "Error querying XInputExtension extension"); + opcode = reply->major_opcode; + free(reply); + } + { + auto reply = xcb(input_get_extension_version, 15, "XInputExtension"); + self.handle_error(reply, "Error querying XInputExtension extension"); + free(reply); + } + { + auto reply = xcb(input_xi_query_version, 2, 2); + self.handle_error(reply, "Error querying XInputExtension extension"); + free(reply); + } + } + + xcb_input_device_id_t client_pointer_id() { + xcb_input_device_id_t id; + auto reply = xcb(input_xi_get_client_pointer, XCB_NONE); + self.handle_error(reply, "Error getting client pointer device id"); + id = reply->deviceid; + free(reply); + return id; + } + + void select_events(xcb_window_t window, uint16_t num_mask, const xcb_input_event_mask_t *masks){ + xcb_check(input_xi_select_events, window, num_mask, masks); + self.handle_error("Error selecting Xi events"); + } + + [[maybe_unused]] inline bool is_raw_motion_event(xcb_generic_event_t *ev) const { + union { // NOLINT(cppcoreguidelines-pro-type-member-init) + xcb_generic_event_t *event; + xcb_ge_event_t *ge; + }; + event = ev; + return ev->response_type == XCB_GE_GENERIC && /* cookie */ ge->pad0 == opcode && ge->event_type == XCB_INPUT_RAW_MOTION; + } + } input {*this}; + + struct { + xcb_connection& self; + int first_event{}; + void init() { + { + auto reply = xcb(query_extension, 6, "XFIXES"); + self.handle_error(reply, "Error querying XFIXES extension"); + first_event = reply->first_event; + free(reply); + } + { + auto reply = xcb(xfixes_query_version, 4, 0); + self.handle_error(reply, "Error querying XFIXES extension"); + free(reply); + } + } + + void select_input(xcb_window_t window, uint32_t mask) { + xcb_check(xfixes_select_cursor_input, window, mask); + self.handle_error("Error querying selecting XFIXES input"); + } + + bool is_cursor_notify_event(xcb_generic_event_t* e) const { + return e->response_type == first_event + XCB_XFIXES_CURSOR_NOTIFY; + } + + xcb_xfixes_get_cursor_image_reply_t* get_cursor_image() { + auto reply = xcb(xfixes_get_cursor_image); + self.handle_error(reply, "Error getting XFIXES cursor image"); + return reply; + } + } fixes {*this}; + + struct { + xcb_connection &self; + void init() { + auto reply = xcb(test_get_version, 2, 2); + self.handle_error(reply, "Error querying XFIXES extension"); + free(reply); + } + } xtest {*this}; + + struct { + xcb_connection &self; + xkb_context* context{}; + int core_kbd{}; + + struct code { + uint32_t ucs; + xkb_keysym_t keysym; + xkb_keycode_t keycode; + xkb_layout_index_t layout; + xkb_mod_mask_t modifiers; + }; + + struct binding { + uint32_t ucs; + xkb_keycode_t keycode; + timespec expires; + }; + + std::vector codes; + std::vector bindings; + void init() { + if (context) { + xkb_context_unref(context); + } + + context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (!context) + throw std::runtime_error("Failed to setup xkb_context"); + + if (!xkb_x11_setup_xkb_extension(self.conn, + XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION, + XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, nullptr, nullptr, + nullptr, nullptr)) + + throw std::runtime_error("Failed to setup xkb extension"); + + enum { + required_events = + (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | + XCB_XKB_EVENT_TYPE_MAP_NOTIFY | + XCB_XKB_EVENT_TYPE_STATE_NOTIFY), + + required_nkn_details = + (XCB_XKB_NKN_DETAIL_KEYCODES), + + required_map_parts = + (XCB_XKB_MAP_PART_KEY_TYPES | + XCB_XKB_MAP_PART_KEY_SYMS | + XCB_XKB_MAP_PART_MODIFIER_MAP | + XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | + XCB_XKB_MAP_PART_KEY_ACTIONS | + XCB_XKB_MAP_PART_VIRTUAL_MODS | + XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP), + + required_state_details = + (XCB_XKB_STATE_PART_MODIFIER_BASE | + XCB_XKB_STATE_PART_MODIFIER_LATCH | + XCB_XKB_STATE_PART_MODIFIER_LOCK | + XCB_XKB_STATE_PART_GROUP_BASE | + XCB_XKB_STATE_PART_GROUP_LATCH | + XCB_XKB_STATE_PART_GROUP_LOCK), + }; + + static const xcb_xkb_select_events_details_t details = { + .affectNewKeyboard = required_nkn_details, + .newKeyboardDetails = required_nkn_details, + .affectState = required_state_details, + .stateDetails = required_state_details, + }; + + core_kbd = xkb_x11_get_core_keyboard_device_id(self.conn); + if (core_kbd == -1) + throw std::runtime_error("Failed to get core keyboard id"); + + xcb_check(xkb_select_events, XCB_XKB_ID_USE_CORE_KBD, required_events, 0, 0, required_map_parts, required_map_parts, &details); + self.handle_error("Failed to select xkb events"); + + reload_keymaps(true); + } + + void reload_keymaps(bool reload_bindings = false) { + auto keymap = xkb_x11_keymap_new_from_device(context, self.conn, core_kbd, XKB_KEYMAP_COMPILE_NO_FLAGS); + + codes.clear(); + + auto iter = [&](xkb_keycode_t key) { + int count; + const xkb_keysym_t *sym; + for (auto &i: bindings) { + // in the case if some other app like x11vnc or chrome-remote-desktop rebinded this keycode... + if (i.keycode == key) { + if (xkb_keymap_num_layouts_for_key(keymap, key) == 0) { + if (i.ucs != 0) + i = {0, i.keycode, now() + ms_to_timespec(200)}; + } else { + count = xkb_keymap_key_get_syms_by_level(keymap, key, 0, 0, &sym); + i = {(count)?(*sym):0, key, now() + ms_to_timespec(200)}; + } + return; + } + } + if (xkb_keymap_num_layouts_for_key(keymap, key) == 0) { + // if this key has no bindings it is interesting for rebinding purposes... + // it will only happen if this key not in bindings yet... + bindings.push_back({0, key, now()}); + } else { + for (xkb_layout_index_t layout=0; layout < xkb_keymap_num_layouts_for_key(keymap, key); layout++) + for (xkb_level_index_t level=0; level < xkb_keymap_num_levels_for_key(keymap, key, layout); level++) { + count = xkb_keymap_key_get_syms_by_level(keymap, key, layout, level, &sym); + if (count && *sym != 0) { + uint32_t ucs = xkb_keysym_to_utf32(*sym); + xkb_mod_mask_t mod = 0; + xkb_keymap_key_get_mods_for_level(keymap, key, layout, level, &mod, 1); + codes.push_back({ucs, *sym, key, layout, mod}); + } + } + } + }; + xkb_keymap_key_for_each(keymap, [](xkb_keymap *, xkb_keycode_t key, void *d) { + (*reinterpret_cast(d))(key); + }, static_cast(&iter)); + + // `codes` will be used in binary search, so we should sort it by unicode symbol value + std::sort(codes.begin(), codes.end(), [](code& a, code& b){ return a.ucs < b.ucs; }); + } + } xkb {*this}; + + void init(int sockfd) { + xcb_connection_t* new_conn = xcb_connect_to_fd(sockfd, nullptr); + int conn_err = xcb_connection_has_error(new_conn); + if (conn_err) { + const char *s; + switch (conn_err) { +#define c(name) case name: s = static_cast(#name); break + c(XCB_CONN_ERROR); + c(XCB_CONN_CLOSED_EXT_NOTSUPPORTED); + c(XCB_CONN_CLOSED_MEM_INSUFFICIENT); + c(XCB_CONN_CLOSED_REQ_LEN_EXCEED); + c(XCB_CONN_CLOSED_PARSE_ERR); + c(XCB_CONN_CLOSED_INVALID_SCREEN); + c(XCB_CONN_CLOSED_FDPASSING_FAILED); + default: + s = "UNKNOWN"; +#undef c + } + throw std::runtime_error(std::string() + "XCB connection has error: " + s); + } + + if (err_ctx) + xcb_errors_context_free(err_ctx); + if (conn) + xcb_disconnect(conn); + conn = new_conn; + xcb_errors_context_new(conn, &err_ctx); + + shm.init(); + xtest.init(); + damage.init(); + input.init(); + fixes.init(); + xkb.init(); + } +}; \ No newline at end of file diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 47e32cb32..99c5ec456 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -105,6 +105,9 @@ protected void onCreate(Bundle savedInstanceState) { lorieView.setFocusableInTouchMode(true); lorieView.requestFocus(); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(1, 1); + cursorView.setLayoutParams(params); + listener.setAsListenerTo(lorieView, cursorView); mTP = new TouchParser(lorieView, this); @@ -173,6 +176,7 @@ void onPreferencesChanged() { @Override public void onResume() { super.onResume(); + nativeResume(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); if (prefs.getBoolean("dexMetaKeyCapture", false)) { SamsungDexUtils.dexMetaKeyCapture(this, true); @@ -185,6 +189,9 @@ public void onPause() { if (preferences.getBoolean("dexMetaKeyCapture", false)) { SamsungDexUtils.dexMetaKeyCapture(this, false); } + + // We do not really need to draw while application is in background. + nativePause(); super.onPause(); } @@ -309,6 +316,9 @@ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { @SuppressWarnings("SameParameterValue") private class ServiceEventListener implements View.OnKeyListener { + public static final int KeyPress = 2; // synchronized with X.h + public static final int KeyRelease = 3; // synchronized with X.h + @SuppressLint({"WrongConstant", "ClickableViewAccessibility"}) private void setAsListenerTo(SurfaceView view, SurfaceView cursor) { view.setOnTouchListener((v, e) -> mTP.onTouchEvent(e)); @@ -381,9 +391,14 @@ public boolean onKey(View v, int keyCode, KeyEvent e) { return true; } - if (e.getAction() == KeyEvent.ACTION_DOWN) action = TouchParser.ACTION_DOWN; - if (e.getAction() == KeyEvent.ACTION_UP) action = TouchParser.ACTION_UP; - onKeyboardKey(action, keyCode, e.isShiftPressed() ? 1 : 0, e.getCharacters()); + switch(e.getAction()) { + case KeyEvent.ACTION_DOWN: + onKeyboardKey(keyCode, KeyPress, e.isShiftPressed() ? 1 : 0, e.getCharacters()); + break; + case KeyEvent.ACTION_UP: + onKeyboardKey(keyCode, KeyRelease, e.isShiftPressed() ? 1 : 0, e.getCharacters()); + break; + } return true; } } @@ -430,6 +445,8 @@ void setCursorRect(int x, int y, int w, int h) { public native void onPointerScroll(int axis, float value); public native void onPointerButton(int button, int type); private native void onKeyboardKey(int key, int type, int shift, String characters); + private native void nativeResume(); + private native void nativePause(); static { System.loadLibrary("lorie-client"); From 272d79fad00e04ff13e3cdb251a13c037a7096b9 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Wed, 1 Mar 2023 21:42:39 +0200 Subject: [PATCH 07/21] Basic multiple layout support (using setxkbmap). Only software keyboard. --- .gitmodules | 3 + app/src/main/cpp/CMakeLists.txt | 15 ++- app/src/main/cpp/libandroid-support | 1 + .../android-keycodes-to-x11-keysyms.h | 109 +++++++++--------- .../main/cpp/lorie-client/lorie-client.cpp | 60 ++++++---- .../main/cpp/lorie-client/xcb-connection.hpp | 90 +++++++++++++++ .../termux/x11/AdditionalKeyboardView.java | 1 + .../java/com/termux/x11/MainActivity.java | 109 ++++++++++++++---- .../com/termux/x11/utils/KeyboardUtils.java | 79 ++++++++++--- 9 files changed, 350 insertions(+), 117 deletions(-) create mode 160000 app/src/main/cpp/libandroid-support diff --git a/.gitmodules b/.gitmodules index 4baaa3431..537b59c8d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -30,3 +30,6 @@ path = app/src/main/cpp/libxkbcommon url = https://github.com/xkbcommon/libxkbcommon ignore = dirty +[submodule "app/src/main/cpp/libandroid-support"] + path = app/src/main/cpp/libandroid-support + url = https://github.com/termux/libandroid-support/ diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 5790e9272..b87b39553 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -158,9 +158,22 @@ target_include_directories(xkbcommon PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" "${CM target_include_directories(xkbcommon PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/libxkbcommon/include") target_link_libraries(xkbcommon xcbproto) +#################################################################################################### +#################################### LIBANDROID-SUPPORT ############################################ +#################################################################################################### + +set(ANDROID_SUPPORT_MUSL_CTYPE iswalnum.c iswalpha.c iswblank.c iswcntrl.c iswctype.c iswdigit.c + iswgraph.c iswlower.c iswprint.c iswpunct.c iswspace.c iswupper.c iswxdigit.c towctrans.c + wcswidth.c wctrans.c) +set(ANDROID_SUPPORT_MUSL_MULTIBYTE internal.c mblen.c mbsnrtowcs.c mbsrtowcs.c) +list(TRANSFORM ANDROID_SUPPORT_MUSL_CTYPE PREPEND ${CMAKE_CURRENT_SOURCE_DIR}/libandroid-support/src/musl-ctype/) +list(TRANSFORM ANDROID_SUPPORT_MUSL_MULTIBYTE PREPEND ${CMAKE_CURRENT_SOURCE_DIR}/libandroid-support/src/musl-multibyte/) +add_library(android-support STATIC ${ANDROID_SUPPORT_MUSL_CTYPE} ${ANDROID_SUPPORT_MUSL_MULTIBYTE}) + #################################################################################################### ####################################### LORIE-CLIENT ############################################### #################################################################################################### add_library(lorie-client SHARED ${CMAKE_CURRENT_SOURCE_DIR}/lorie-client/lorie-client.cpp) -target_link_libraries(lorie-client xkbcommon xcbproto xcb xcb-errors xcb-shm xcb-xfixes xcb-xinput xcb-damage xcvt "-llog" "-landroid") +target_link_libraries(lorie-client android-support xkbcommon xcbproto xcb xcb-errors xcb-shm + xcb-xfixes xcb-xinput xcb-damage xcvt "-llog" "-landroid") diff --git a/app/src/main/cpp/libandroid-support b/app/src/main/cpp/libandroid-support new file mode 160000 index 000000000..2c033e0fb --- /dev/null +++ b/app/src/main/cpp/libandroid-support @@ -0,0 +1 @@ +Subproject commit 2c033e0fb6004db5b7d2775ec5512ae3b201e54f diff --git a/app/src/main/cpp/lorie-client/android-keycodes-to-x11-keysyms.h b/app/src/main/cpp/lorie-client/android-keycodes-to-x11-keysyms.h index 73692917a..d2c81c2eb 100644 --- a/app/src/main/cpp/lorie-client/android-keycodes-to-x11-keysyms.h +++ b/app/src/main/cpp/lorie-client/android-keycodes-to-x11-keysyms.h @@ -5,11 +5,6 @@ /* * Indexes in this list match to keycode name in comment. * Do not change order, only change values. - * This list is only for special keys which do not produce - * Unicode symbols or its value is stable. - * Unicode symbols will be handled in keysym resolver - * which keeps track on Xserver's keymap, otherways non-English - * keyboard layouts will be broken. */ int android_to_keysyms[] = { /* ANDROID_KEYCODE_UNKNOWN */ 0, @@ -19,56 +14,58 @@ int android_to_keysyms[] = { /* ANDROID_KEYCODE_BACK */ XK_Escape, /* ANDROID_KEYCODE_CALL */ 0, /* ANDROID_KEYCODE_ENDCALL */ 0, - /* ANDROID_KEYCODE_0 */ 0, - /* ANDROID_KEYCODE_1 */ 0, - /* ANDROID_KEYCODE_2 */ 0, - /* ANDROID_KEYCODE_3 */ 0, - /* ANDROID_KEYCODE_4 */ 0, - /* ANDROID_KEYCODE_5 */ 0, - /* ANDROID_KEYCODE_6 */ 0, - /* ANDROID_KEYCODE_7 */ 0, - /* ANDROID_KEYCODE_8 */ 0, - /* ANDROID_KEYCODE_9 */ 0, - /* ANDROID_KEYCODE_STAR */ 0, - /* ANDROID_KEYCODE_POUND */ 0, + /* ANDROID_KEYCODE_0 */ XK_0, + /* ANDROID_KEYCODE_1 */ XK_1, + /* ANDROID_KEYCODE_2 */ XK_2, + /* ANDROID_KEYCODE_3 */ XK_3, + /* ANDROID_KEYCODE_4 */ XK_4, + /* ANDROID_KEYCODE_5 */ XK_5, + /* ANDROID_KEYCODE_6 */ XK_6, + /* ANDROID_KEYCODE_7 */ XK_7, + /* ANDROID_KEYCODE_8 */ XK_8, + /* ANDROID_KEYCODE_9 */ XK_9, + /* ANDROID_KEYCODE_STAR */ XK_asterisk, + /* ANDROID_KEYCODE_POUND */ XK_numbersign, /* ANDROID_KEYCODE_DPAD_UP */ XK_Up, /* ANDROID_KEYCODE_DPAD_DOWN */ XK_Down, /* ANDROID_KEYCODE_DPAD_LEFT */ XK_Left, /* ANDROID_KEYCODE_DPAD_RIGHT */ XK_Right, /* ANDROID_KEYCODE_DPAD_CENTER */ XK_Return, - /* ANDROID_KEYCODE_VOLUME_UP */ 0, - /* ANDROID_KEYCODE_VOLUME_DOWN */ 0, + /* ANDROID_KEYCODE_VOLUME_UP */ 0x1008FF13, // XF86XK_AudioRaiseVolume + /* ANDROID_KEYCODE_VOLUME_DOWN */ 0x1008FF11, // XF86XK_AudioLowerVolume /* ANDROID_KEYCODE_POWER */ 0, /* ANDROID_KEYCODE_CAMERA */ 0, /* ANDROID_KEYCODE_CLEAR */ 0, - /* ANDROID_KEYCODE_A */ 0, - /* ANDROID_KEYCODE_B */ 0, - /* ANDROID_KEYCODE_C */ 0, - /* ANDROID_KEYCODE_D */ 0, - /* ANDROID_KEYCODE_E */ 0, - /* ANDROID_KEYCODE_F */ 0, - /* ANDROID_KEYCODE_G */ 0, - /* ANDROID_KEYCODE_H */ 0, - /* ANDROID_KEYCODE_I */ 0, - /* ANDROID_KEYCODE_J */ 0, - /* ANDROID_KEYCODE_K */ 0, - /* ANDROID_KEYCODE_L */ 0, - /* ANDROID_KEYCODE_M */ 0, - /* ANDROID_KEYCODE_N */ 0, - /* ANDROID_KEYCODE_O */ 0, - /* ANDROID_KEYCODE_P */ 0, - /* ANDROID_KEYCODE_Q */ 0, - /* ANDROID_KEYCODE_R */ 0, - /* ANDROID_KEYCODE_S */ 0, - /* ANDROID_KEYCODE_T */ 0, - /* ANDROID_KEYCODE_U */ 0, - /* ANDROID_KEYCODE_V */ 0, - /* ANDROID_KEYCODE_W */ 0, - /* ANDROID_KEYCODE_X */ 0, - /* ANDROID_KEYCODE_Y */ 0, - /* ANDROID_KEYCODE_Z */ 0, - /* ANDROID_KEYCODE_COMMA */ 0, - /* ANDROID_KEYCODE_PERIOD */ 0, + /* Latin keysyms from this map will be used + * only when there are modifiers with keycode */ + /* ANDROID_KEYCODE_A */ XK_a, + /* ANDROID_KEYCODE_B */ XK_b, + /* ANDROID_KEYCODE_C */ XK_c, + /* ANDROID_KEYCODE_D */ XK_d, + /* ANDROID_KEYCODE_E */ XK_e, + /* ANDROID_KEYCODE_F */ XK_f, + /* ANDROID_KEYCODE_G */ XK_g, + /* ANDROID_KEYCODE_H */ XK_h, + /* ANDROID_KEYCODE_I */ XK_i, + /* ANDROID_KEYCODE_J */ XK_j, + /* ANDROID_KEYCODE_K */ XK_k, + /* ANDROID_KEYCODE_L */ XK_l, + /* ANDROID_KEYCODE_M */ XK_m, + /* ANDROID_KEYCODE_N */ XK_n, + /* ANDROID_KEYCODE_O */ XK_o, + /* ANDROID_KEYCODE_P */ XK_p, + /* ANDROID_KEYCODE_Q */ XK_q, + /* ANDROID_KEYCODE_R */ XK_r, + /* ANDROID_KEYCODE_S */ XK_s, + /* ANDROID_KEYCODE_T */ XK_t, + /* ANDROID_KEYCODE_U */ XK_u, + /* ANDROID_KEYCODE_V */ XK_v, + /* ANDROID_KEYCODE_W */ XK_w, + /* ANDROID_KEYCODE_X */ XK_x, + /* ANDROID_KEYCODE_Y */ XK_y, + /* ANDROID_KEYCODE_Z */ XK_z, + /* ANDROID_KEYCODE_COMMA */ XK_comma, + /* ANDROID_KEYCODE_PERIOD */ XK_period, /* ANDROID_KEYCODE_ALT_LEFT */ XK_Alt_L, /* ANDROID_KEYCODE_ALT_RIGHT */ XK_Alt_R, /* ANDROID_KEYCODE_SHIFT_LEFT */ XK_Shift_L, @@ -80,15 +77,15 @@ int android_to_keysyms[] = { /* ANDROID_KEYCODE_ENVELOPE */ 0, /* ANDROID_KEYCODE_ENTER */ XK_Return, /* ANDROID_KEYCODE_DEL */ XK_BackSpace, - /* ANDROID_KEYCODE_GRAVE */ 0, - /* ANDROID_KEYCODE_MINUS */ 0, - /* ANDROID_KEYCODE_EQUALS */ 0, - /* ANDROID_KEYCODE_LEFT_BRACKET */ 0, - /* ANDROID_KEYCODE_RIGHT_BRACKET */ 0, - /* ANDROID_KEYCODE_BACKSLASH */ 0, - /* ANDROID_KEYCODE_SEMICOLON */ 0, - /* ANDROID_KEYCODE_APOSTROPHE */ 0, - /* ANDROID_KEYCODE_SLASH */ 0, + /* ANDROID_KEYCODE_GRAVE */ XK_grave, + /* ANDROID_KEYCODE_MINUS */ XK_minus, + /* ANDROID_KEYCODE_EQUALS */ XK_equal, + /* ANDROID_KEYCODE_LEFT_BRACKET */ XK_bracketleft, + /* ANDROID_KEYCODE_RIGHT_BRACKET */ XK_bracketright, + /* ANDROID_KEYCODE_BACKSLASH */ XK_backslash, + /* ANDROID_KEYCODE_SEMICOLON */ XK_semicolon, + /* ANDROID_KEYCODE_APOSTROPHE */ XK_apostrophe, + /* ANDROID_KEYCODE_SLASH */ XK_slash, /* ANDROID_KEYCODE_AT */ 0, /* ANDROID_KEYCODE_NUM */ 0, /* ANDROID_KEYCODE_HEADSETHOOK */ 0, diff --git a/app/src/main/cpp/lorie-client/lorie-client.cpp b/app/src/main/cpp/lorie-client/lorie-client.cpp index 5b0af1936..2960c0cbd 100644 --- a/app/src/main/cpp/lorie-client/lorie-client.cpp +++ b/app/src/main/cpp/lorie-client/lorie-client.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -21,6 +22,7 @@ #if 1 #define ALOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "LorieX11Client", fmt, ## __VA_ARGS__) +#define ALOGV(fmt, ...) __android_log_print(ANDROID_LOG_VERBOSE, "LorieX11Client", fmt, ## __VA_ARGS__) #else #define ALOGE(fmt, ...) printf(fmt, ## __VA_ARGS__); printf("\n") #endif @@ -288,6 +290,13 @@ class lorie_client { } } + void send_keysym(xcb_keysym_t keysym, int meta_state) { + if (c.conn) + post([=] { + c.xkb.send_keysym(keysym, meta_state); + }); + } + void connection_poll_func() { try { bool need_redraw = false; @@ -428,28 +437,37 @@ Java_com_termux_x11_MainActivity_onPointerButton([[maybe_unused]] JNIEnv *env, [ extern "C" JNIEXPORT void JNICALL -Java_com_termux_x11_MainActivity_onKeyboardKey([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject thiz, - [[maybe_unused]] jint key, [[maybe_unused]] jint type, - [[maybe_unused]] jint shift, [[maybe_unused]] jstring characters) { - if (client.c.conn) { - ALOGE("Sending key %d %d", key, android_to_keysyms[key]); - if (android_to_keysyms[key] != 0) { - xkb_keycode_t keycode = 0; - ALOGE("Trying to send android keycode %d which corresponds to keysym 0x%X", key, android_to_keysyms[key]); - for (auto& code: client.c.xkb.codes) { - if (code.keysym == android_to_keysyms[key]) - keycode = code.keycode; - } - if (keycode != 0) { - ALOGE("Sending keycode %d", keycode); - xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(client.c.conn)).data; - auto cookie = xcb_test_fake_input(client.c.conn, type, keycode, - XCB_CURRENT_TIME, s->root, 0, 0, 0); - client.c.err = xcb_request_check(client.c.conn, cookie); - client.c.handle_error("Sending fake keypress failed"); - } - } +Java_com_termux_x11_MainActivity_onKeySym([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject thiz, + jint keycode, jint unicodeChar, jstring str, jint meta_state) { + wchar_t unicode = unicodeChar; + mbstate_t ps {}; + + ALOGE("1"); + + if (unicode && !meta_state) { + client.send_keysym(xkb_utf32_to_keysym(unicode), meta_state); + return; + } + + ALOGE("2"); + if (android_to_keysyms[keycode]) { + client.send_keysym(android_to_keysyms[keycode], meta_state); + return; + } + + ALOGE("3"); + if (str) { + const char *characters = env->GetStringUTFChars(str, nullptr); + const char *chars = characters; // mbsrtowcs will set it to nullptr, but we still need to release it + if (!chars) + return; + + while (mbsrtowcs(&unicode, &chars, 1, &ps) > 0) + client.send_keysym(xkb_utf32_to_keysym(unicode), meta_state); + + env->ReleaseStringUTFChars(str, characters); } + ALOGE("4"); } extern "C" diff --git a/app/src/main/cpp/lorie-client/xcb-connection.hpp b/app/src/main/cpp/lorie-client/xcb-connection.hpp index 746c3b54f..e2b8fa7f2 100644 --- a/app/src/main/cpp/lorie-client/xcb-connection.hpp +++ b/app/src/main/cpp/lorie-client/xcb-connection.hpp @@ -13,6 +13,8 @@ #include #undef explicit +#include + #define xcb(name, ...) xcb_ ## name ## _reply(self.conn, xcb_ ## name(self.conn, ## __VA_ARGS__), &self.err) #define xcb_check(name, ...) self.err = xcb_request_check(self.conn, xcb_ ## name(self.conn, ## __VA_ARGS__)) @@ -348,6 +350,94 @@ class xcb_connection { // `codes` will be used in binary search, so we should sort it by unicode symbol value std::sort(codes.begin(), codes.end(), [](code& a, code& b){ return a.ucs < b.ucs; }); } + + void send_keysym(xcb_keysym_t keysym, int meta_state) { + char buf[64]{}; + xkb_keysym_get_name(keysym, buf, sizeof(buf)); + ALOGV("Sending keysym %s", buf); + + auto code = std::find_if(codes.begin(), codes.end(), [&c = keysym](auto& code) { + return code.keysym == c; + }); + + if (code == codes.end()) { + xkb_keysym_get_name(keysym, buf, sizeof(buf)); + ALOGE("Code for keysym 0x%X (%s) not found...", keysym, buf); + return; + } + + xcb_flush(self.conn); + + xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(self.conn)).data; + + // Since we do not track state of modifiers we should reset them before we finish here. + // Getting current modifiers + u32 modifiers = code->modifiers; +// { +// auto reply = xcb(query_pointer, s->root); +// if (reply) +// modifiers &= ~reply->mask; +// +// ALOGE("pointer mods %d", reply->mask); +// free(reply); +// } + + u8 current_group = 0; + { + auto reply = xcb(xkb_get_state, XCB_XKB_ID_USE_CORE_KBD); + current_group = reply->group; + modifiers &= ~reply->mods; + ALOGE("pointer mods %d", reply->mods); + free(reply); + } + + if (meta_state & AMETA_CTRL_ON) + modifiers |= XCB_MOD_MASK_CONTROL; + + if (meta_state & AMETA_SHIFT_ON) + modifiers |= XCB_MOD_MASK_SHIFT; + + if (meta_state & AMETA_ALT_ON) + modifiers |= XCB_MOD_MASK_1; + + if (meta_state & AMETA_META_ON) + modifiers |= XCB_MOD_MASK_4; + + auto reply = xcb(get_modifier_mapping); + self.handle_error("Failed to get keyboard modifiers."); + + for (int mod_index = XCB_MAP_INDEX_SHIFT; mod_index <= XCB_MAP_INDEX_5; mod_index++) { + if (modifiers & (1 << mod_index)) { + for (int mod_key = 0; mod_key < reply->keycodes_per_modifier; mod_key++) { + int keycode = xcb_get_modifier_mapping_keycodes(reply) + [mod_index * reply->keycodes_per_modifier + mod_key]; + if (keycode) + xcb_test_fake_input(self.conn, XCB_KEY_PRESS, keycode, XCB_CURRENT_TIME, + s->root, 0, 0, 0); + } + } + } + + xcb_xkb_latch_lock_state(self.conn, XCB_XKB_ID_USE_CORE_KBD, 0, 0, 1, code->layout, 0, 0, 0); + xcb_test_fake_input(self.conn, XCB_KEY_PRESS, code->keycode, XCB_CURRENT_TIME, s->root, 0, 0, 0); + xcb_test_fake_input(self.conn, XCB_KEY_RELEASE, code->keycode, XCB_CURRENT_TIME, s->root, 0, 0, 0); + xcb_xkb_latch_lock_state(self.conn, XCB_XKB_ID_USE_CORE_KBD, 0, 0, 1, current_group, 0, 0, 0); + + for (int mod_index = XCB_MAP_INDEX_SHIFT; mod_index <= XCB_MAP_INDEX_5; mod_index++) { + if (modifiers & (1 << mod_index)) { + for (int mod_key = 0; mod_key < reply->keycodes_per_modifier; mod_key++) { + int keycode = xcb_get_modifier_mapping_keycodes(reply) + [mod_index * reply->keycodes_per_modifier + mod_key]; + if (keycode) + xcb_test_fake_input(self.conn, XCB_KEY_RELEASE, keycode, + XCB_CURRENT_TIME, s->root, 0, 0, 0); + } + } + } + + free(reply); + xcb_flush(self.conn); + } } xkb {*this}; void init(int sockfd) { diff --git a/app/src/main/java/com/termux/x11/AdditionalKeyboardView.java b/app/src/main/java/com/termux/x11/AdditionalKeyboardView.java index e03b6ca65..7dd48f406 100644 --- a/app/src/main/java/com/termux/x11/AdditionalKeyboardView.java +++ b/app/src/main/java/com/termux/x11/AdditionalKeyboardView.java @@ -4,6 +4,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.graphics.Rect; +import android.os.Build; import android.preference.PreferenceManager; import androidx.appcompat.widget.AppCompatTextView; import android.util.AttributeSet; diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 99c5ec456..19d11063c 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -29,6 +29,7 @@ import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; +import android.view.ViewGroup; import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; @@ -42,8 +43,10 @@ import com.termux.x11.utils.KeyboardUtils; import com.termux.x11.utils.PermissionUtils; +import com.termux.shared.termux.settings.properties.TermuxAppSharedProperties; import com.termux.x11.utils.SamsungDexUtils; + public class MainActivity extends AppCompatActivity implements View.OnApplyWindowInsetsListener, TouchParser.OnTouchParseListener { static final String REQUEST_LAUNCH_EXTERNAL_DISPLAY = "request_launch_external_display"; @@ -60,12 +63,14 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo AdditionalKeyboardView kbd; FrameLayout frm; + KeyboardUtils.KeyboardHeightProvider kbdHeightListener; private TouchParser mTP; private final ServiceEventListener listener = new ServiceEventListener(); private ICmdEntryInterface service = null; private int screenWidth = 0; private int screenHeight = 0; + TermuxAppSharedProperties mProperties; public MainActivity() { init(); @@ -74,6 +79,7 @@ public MainActivity() { @SuppressLint({"AppCompatMethod", "ObsoleteSdkInt"}) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mProperties = TermuxAppSharedProperties.init(this); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); if (didRequestLaunchExternalDisplay() || preferences.getBoolean("fullscreen", false)) { @@ -118,6 +124,23 @@ protected void onCreate(Bundle savedInstanceState) { getDecorView(). setPointerIcon(PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL)); + + //if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) + kbdHeightListener = new KeyboardUtils.KeyboardHeightProvider(this, getWindowManager(), findViewById(android.R.id.content), + (keyboardHeight, keyboardOpen, isLandscape) -> { + Log.e("ADDKBD", "show " + keyboardOpen + " height " + keyboardHeight + " land " + isLandscape); + @SuppressLint("CutPasteId") + AdditionalKeyboardView v = findViewById(R.id.additionalKbd); + v.setVisibility(keyboardOpen?View.VISIBLE:View.INVISIBLE); + FrameLayout.LayoutParams p = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, v.getHeight()); + //p.gravity = Gravity.BOTTOM; + p.setMargins(0, keyboardHeight, 0, 0); + + v.setLayoutParams(params); + v.setVisibility(View.VISIBLE); + + }); + registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -160,13 +183,13 @@ void onPreferencesChanged() { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); int mode = Integer.parseInt(preferences.getString("touchMode", "1")); mTP.setMode(mode); +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) +// getWindow().getDecorView().setOnApplyWindowInsetsListener(this); - getWindow().getDecorView().setOnApplyWindowInsetsListener(this); - - if (preferences.getBoolean("showAdditionalKbd", true)) + /*if (preferences.getBoolean("showAdditionalKbd", true)) kbd.setVisibility(View.VISIBLE); else - kbd.setVisibility(View.INVISIBLE); + kbd.setVisibility(View.INVISIBLE);*/ if (preferences.getBoolean("dexMetaKeyCapture", false)) { SamsungDexUtils.dexMetaKeyCapture(this, false); @@ -227,7 +250,8 @@ public void onConfigurationChanged(@NonNull Configuration newConfig) { InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE); View view = getCurrentFocus(); if (view == null) { - view = new View(this); + view = findViewById(R.id.lorieView); + view.requestFocus(); } imm.hideSoftInputFromWindow(view.getWindowToken(), 0); } @@ -294,7 +318,7 @@ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { getWindow().getDecorView().getWindowVisibleDisplayFrame(r); WindowInsetsCompat rootInsets = WindowInsetsCompat.toWindowInsetsCompat(kbd.getRootWindowInsets()); boolean isSoftKbdVisible = rootInsets.isVisible(WindowInsetsCompat.Type.ime()); - kbd.setVisibility(isSoftKbdVisible ? View.VISIBLE : View.INVISIBLE); +// kbd.setVisibility(isSoftKbdVisible ? View.VISIBLE : View.INVISIBLE); FrameLayout.LayoutParams p = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); if (preferences.getBoolean("Reseed", true)) { @@ -303,7 +327,7 @@ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { p.topMargin = r.bottom - r.top - kbd.getHeight(); } - kbd.setLayoutParams(p); +// kbd.setLayoutParams(p); }, 100); } @@ -368,8 +392,6 @@ private boolean isSource(KeyEvent e, int source) { private boolean middlePressed = false; // Prevent middle button press event from being repeated @Override public boolean onKey(View v, int keyCode, KeyEvent e) { - int action = 0; - if (keyCode == KeyEvent.KEYCODE_BACK) { if (isSource(e, InputDevice.SOURCE_MOUSE) && rightPressed != (e.getAction() == KeyEvent.ACTION_DOWN)) { @@ -377,8 +399,7 @@ public boolean onKey(View v, int keyCode, KeyEvent e) { rightPressed = (e.getAction() == KeyEvent.ACTION_DOWN); } else if (e.getAction() == KeyEvent.ACTION_UP) { KeyboardUtils.toggleKeyboardVisibility(MainActivity.this); - if (kbd!=null) - kbd.requestFocus(); + findViewById(R.id.lorieView).requestFocus(); } return true; } @@ -391,14 +412,62 @@ public boolean onKey(View v, int keyCode, KeyEvent e) { return true; } - switch(e.getAction()) { - case KeyEvent.ACTION_DOWN: - onKeyboardKey(keyCode, KeyPress, e.isShiftPressed() ? 1 : 0, e.getCharacters()); - break; - case KeyEvent.ACTION_UP: - onKeyboardKey(keyCode, KeyRelease, e.isShiftPressed() ? 1 : 0, e.getCharacters()); - break; - } + /* + * A KeyEvent is generated in Android when the user interacts with the device's + * physical keyboard or software keyboard. If the KeyEvent is generated by a physical key, + * or a software key that has a corresponding physical key, it will contain a key code. + * If the key that is pressed generates a symbol, such as a letter or number, + * the symbol can be retrieved by calling the KeyEvent::getUnicodeChar() method. + * If the physical key requires the user to press the Shift key to enter the symbol, + * the software keyboard will emulate the Shift key press. + * However, in the case of a non-English keyboard layout, KeyEvent::getUnicodeChar() + * will not return 0 symbol for non-English keyboard layouts, keycode also will be set to 0, + * action will be set to KeyEvent.ACTION_MULTIPLE and the symbol data can only be retrieved + * by calling the KeyEvent::getCharacters() method. + * + * For special keys like Tab and Return, both the key code and Unicode symbol are set in + * the KeyEvent object. However, this is not the case for all keys, so to determine whether + * a key is special or not, we will rely only on the keycode value. + * + * Android sends emulated Shift key press event along with some key events. + * Since we cannot determine the event source from JNI, we ignore the emulated Shift + * key press in JNI. In this case, it becomes much easier to handle Shift key press events + * coming from a physical keyboard or ExtraKeysView. + * + * To avoid handling modifier states we will simply ignore modifier keys and + * ACTION_DOWN if they come from soft keyboard. + * + */ + Log.e("KEY", " " + e + " " + e.isShiftPressed() + " " + e.isAltPressed() + " " + e.isCtrlPressed() + " " + e.isMetaPressed()); + if (e.getDevice().isVirtual()) { + Log.e("KEY", "Virtual"); + if (e.getAction() == KeyEvent.ACTION_UP || e.getAction() == KeyEvent.ACTION_MULTIPLE) { + Log.e("KEY", "Up or multiple"); + switch (keyCode) { + case KeyEvent.KEYCODE_SHIFT_LEFT: + case KeyEvent.KEYCODE_SHIFT_RIGHT: + case KeyEvent.KEYCODE_ALT_LEFT: + case KeyEvent.KEYCODE_ALT_RIGHT: + case KeyEvent.KEYCODE_CTRL_LEFT: + case KeyEvent.KEYCODE_CTRL_RIGHT: + case KeyEvent.KEYCODE_META_LEFT: + case KeyEvent.KEYCODE_META_RIGHT: + Log.e("KEY", "meta key"); + break; + default: + Log.e("KEY", "other key"); + onKeySym(keyCode, e.getUnicodeChar(), e.getCharacters(), e.getMetaState()); + } + } + } // else +// switch(e.getAction()) { +// case KeyEvent.ACTION_DOWN: +// onKeyboardKey(keyCode, KeyPress, e.isShiftPressed() ? 1 : 0, e.getUnicodeChar(), e.getCharacters()); +// break; +// case KeyEvent.ACTION_UP: +// onKeyboardKey(keyCode, KeyRelease, e.isShiftPressed() ? 1 : 0, e.getUnicodeChar(), e.getCharacters()); +// break; +// } return true; } } @@ -444,7 +513,7 @@ void setCursorRect(int x, int y, int w, int h) { public native void onPointerMotion(int x, int y); public native void onPointerScroll(int axis, float value); public native void onPointerButton(int button, int type); - private native void onKeyboardKey(int key, int type, int shift, String characters); + private native void onKeySym(int keyCode, int unicode, String str, int metaState); private native void nativeResume(); private native void nativePause(); diff --git a/app/src/main/java/com/termux/x11/utils/KeyboardUtils.java b/app/src/main/java/com/termux/x11/utils/KeyboardUtils.java index cfc408b71..0ad4cbc86 100644 --- a/app/src/main/java/com/termux/x11/utils/KeyboardUtils.java +++ b/app/src/main/java/com/termux/x11/utils/KeyboardUtils.java @@ -1,12 +1,19 @@ package com.termux.x11.utils; +import android.annotation.SuppressLint; import android.app.Activity; import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; +import android.util.DisplayMetrics; +import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.content.Context; +import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; +import android.widget.LinearLayout; +import android.widget.PopupWindow; import java.util.HashMap; @@ -25,15 +32,13 @@ public class KeyboardUtils implements ViewTreeObserver.OnGlobalLayoutListener private float mScreenDensity; private static HashMap sListenerMap = new HashMap<>(); - public interface SoftKeyboardToggleListener - { + public interface SoftKeyboardToggleListener { void onToggleSoftKeyboard(boolean isVisible); } @Override - public void onGlobalLayout() - { + public void onGlobalLayout() { Rect r = new Rect(); mRootView.getWindowVisibleDisplayFrame(r); @@ -52,8 +57,7 @@ public void onGlobalLayout() * @param act calling activity * @param listener callback */ - public static void addKeyboardToggleListener(Activity act, SoftKeyboardToggleListener listener) - { + public static void addKeyboardToggleListener(Activity act, SoftKeyboardToggleListener listener) { removeKeyboardToggleListener(listener); sListenerMap.put(listener, new KeyboardUtils(act, listener)); @@ -63,8 +67,7 @@ public static void addKeyboardToggleListener(Activity act, SoftKeyboardToggleLis * Remove a registered listener * @param listener {@link SoftKeyboardToggleListener} */ - public static void removeKeyboardToggleListener(SoftKeyboardToggleListener listener) - { + public static void removeKeyboardToggleListener(SoftKeyboardToggleListener listener) { if(sListenerMap.containsKey(listener)) { KeyboardUtils k = sListenerMap.get(listener); @@ -77,8 +80,7 @@ public static void removeKeyboardToggleListener(SoftKeyboardToggleListener liste /** * Remove all registered keyboard listeners */ - public static void removeAllKeyboardToggleListeners() - { + public static void removeAllKeyboardToggleListeners() { for(SoftKeyboardToggleListener l : sListenerMap.keySet()) sListenerMap.get(l).removeListener(); @@ -89,8 +91,7 @@ public static void removeAllKeyboardToggleListeners() * Manually toggle soft keyboard visibility * @param context calling context */ - public static void toggleKeyboardVisibility(Context context) - { + public static void toggleKeyboardVisibility(Context context) { InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); if(inputMethodManager != null) inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); @@ -100,23 +101,20 @@ public static void toggleKeyboardVisibility(Context context) * Force closes the soft keyboard * @param activeView the view with the keyboard focus */ - public void forceCloseKeyboard(View activeView) - { + public void forceCloseKeyboard(View activeView) { InputMethodManager inputMethodManager = (InputMethodManager) activeView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); if(inputMethodManager != null) { inputMethodManager.hideSoftInputFromWindow(activeView.getWindowToken(), 0); - } + } } - private void removeListener() - { + private void removeListener() { mCallback = null; mRootView.getViewTreeObserver().removeOnGlobalLayoutListener(this); } - private KeyboardUtils(Activity act, SoftKeyboardToggleListener listener) - { + private KeyboardUtils(Activity act, SoftKeyboardToggleListener listener) { mCallback = listener; mRootView = ((ViewGroup) act.findViewById(android.R.id.content)).getChildAt(0); @@ -124,5 +122,48 @@ private KeyboardUtils(Activity act, SoftKeyboardToggleListener listener) mScreenDensity = act.getResources().getDisplayMetrics().density; } + public static class KeyboardHeightProvider extends PopupWindow { + public KeyboardHeightProvider(Context context, WindowManager windowManager, View parentView, KeyboardHeightListener listener) { + super(context); + + LinearLayout popupView = new LinearLayout(context); + popupView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + popupView.getViewTreeObserver().addOnGlobalLayoutListener(() -> { + DisplayMetrics metrics = new DisplayMetrics(); + windowManager.getDefaultDisplay().getMetrics(metrics); + + Rect rect = new Rect(); + popupView.getWindowVisibleDisplayFrame(rect); + + int keyboardHeight = metrics.heightPixels - (rect.bottom - rect.top); + @SuppressLint("InternalInsetResource") + int resourceID = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceID > 0) { + keyboardHeight -= context.getResources().getDimensionPixelSize(resourceID); + } + if (keyboardHeight < 100) { + keyboardHeight = 0; + } + boolean isLandscape = metrics.widthPixels > metrics.heightPixels; + boolean keyboardOpen = keyboardHeight > 0; + if (listener != null) { + listener.onKeyboardHeightChanged(keyboardHeight, keyboardOpen, isLandscape); + } + }); + + setContentView(popupView); + + setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); + setWidth(0); + setHeight(ViewGroup.LayoutParams.MATCH_PARENT); + setBackgroundDrawable(new ColorDrawable(0)); + + parentView.post(() -> showAtLocation(parentView, Gravity.NO_GRAVITY, 0, 0)); + } + } + public interface KeyboardHeightListener { + void onKeyboardHeightChanged(int keyboardHeight, boolean keyboardOpen, boolean isLandscape); + } } From 1ea405c6fdbc46cccacc0fd51bc49564282ac3c2 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Wed, 1 Mar 2023 23:05:32 +0200 Subject: [PATCH 08/21] Basic ExtraKeys support --- app/build.gradle | 2 +- .../main/cpp/lorie-client/lorie-client.cpp | 2 + .../java/com/termux/x11/MainActivity.java | 164 ++++++++------ .../termux/x11/utils/TermuxX11ExtraKeys.java | 202 ++++++++++++++++++ .../termux/x11/utils/X11ToolbarViewPager.java | 105 +++++++++ app/src/main/res/layout/main_activity.xml | 24 +-- .../view_terminal_toolbar_extra_keys.xml | 8 + .../view_terminal_toolbar_text_input.xml | 16 ++ 8 files changed, 444 insertions(+), 79 deletions(-) create mode 100644 app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java create mode 100644 app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java create mode 100644 app/src/main/res/layout/view_terminal_toolbar_extra_keys.xml create mode 100644 app/src/main/res/layout/view_terminal_toolbar_text_input.xml diff --git a/app/build.gradle b/app/build.gradle index 2ef616d53..9d731e317 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -84,7 +84,7 @@ wayland { dependencies { implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' - implementation 'com.termux.termux-app:termux-shared:-SNAPSHOT' + implementation 'com.termux.termux-app:termux-shared:2f5a6f7de6' implementation 'com.google.android.material:material:1.8.0' implementation fileTree(dir: 'libs', include: ['*.jar']) //noinspection GradleDependency diff --git a/app/src/main/cpp/lorie-client/lorie-client.cpp b/app/src/main/cpp/lorie-client/lorie-client.cpp index 2960c0cbd..17127d54a 100644 --- a/app/src/main/cpp/lorie-client/lorie-client.cpp +++ b/app/src/main/cpp/lorie-client/lorie-client.cpp @@ -307,6 +307,8 @@ class lorie_client { if (event->response_type == 0) { c.err = reinterpret_cast(event); c.handle_error("Error processing XCB events"); + } else if (event->response_type == XCB_MAPPING_NOTIFY) { + c.xkb.reload_keymaps(); } else if (event->response_type == XCB_CONFIGURE_NOTIFY) { auto e = reinterpret_cast(event); s->width_in_pixels = e->width; diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 19d11063c..4f9ffc3fe 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -40,37 +40,30 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.view.WindowInsetsCompat; +import androidx.viewpager.widget.ViewPager; +import com.termux.shared.termux.extrakeys.ExtraKeysView; import com.termux.x11.utils.KeyboardUtils; import com.termux.x11.utils.PermissionUtils; -import com.termux.shared.termux.settings.properties.TermuxAppSharedProperties; import com.termux.x11.utils.SamsungDexUtils; +import com.termux.x11.utils.TermuxX11ExtraKeys; +import com.termux.x11.utils.X11ToolbarViewPager; public class MainActivity extends AppCompatActivity implements View.OnApplyWindowInsetsListener, TouchParser.OnTouchParseListener { static final String REQUEST_LAUNCH_EXTERNAL_DISPLAY = "request_launch_external_display"; - private static final int[] keys = { - KeyEvent.KEYCODE_ESCAPE, - KeyEvent.KEYCODE_TAB, - KeyEvent.KEYCODE_CTRL_LEFT, - KeyEvent.KEYCODE_ALT_LEFT, - KeyEvent.KEYCODE_DPAD_UP, - KeyEvent.KEYCODE_DPAD_DOWN, - KeyEvent.KEYCODE_DPAD_LEFT, - KeyEvent.KEYCODE_DPAD_RIGHT, - }; - - AdditionalKeyboardView kbd; FrameLayout frm; KeyboardUtils.KeyboardHeightProvider kbdHeightListener; private TouchParser mTP; private final ServiceEventListener listener = new ServiceEventListener(); private ICmdEntryInterface service = null; + private int mTerminalToolbarDefaultHeight; + public TermuxX11ExtraKeys mExtraKeys; + private ExtraKeysView mExtraKeysView; private int screenWidth = 0; private int screenHeight = 0; - TermuxAppSharedProperties mProperties; public MainActivity() { init(); @@ -79,7 +72,6 @@ public MainActivity() { @SuppressLint({"AppCompatMethod", "ObsoleteSdkInt"}) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mProperties = TermuxAppSharedProperties.init(this); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); if (didRequestLaunchExternalDisplay() || preferences.getBoolean("fullscreen", false)) { @@ -95,7 +87,7 @@ protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.main_activity); - kbd = findViewById(R.id.additionalKbd); + //kbd = findViewById(R.id.additionalKbd); frm = findViewById(R.id.frame); Button preferencesButton = findViewById(R.id.preferences_button); preferencesButton.setOnClickListener((l) -> { @@ -107,17 +99,14 @@ protected void onCreate(Bundle savedInstanceState) { SurfaceView lorieView = findViewById(R.id.lorieView); SurfaceView cursorView = findViewById(R.id.cursorView); - lorieView.setFocusable(true); - lorieView.setFocusableInTouchMode(true); - lorieView.requestFocus(); - FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(1, 1); cursorView.setLayoutParams(params); listener.setAsListenerTo(lorieView, cursorView); mTP = new TouchParser(lorieView, this); - kbd.reload(keys, lorieView, listener); + setTerminalToolbarView(); + toggleTerminalToolbar(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) getWindow(). @@ -126,20 +115,20 @@ protected void onCreate(Bundle savedInstanceState) { //if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) - kbdHeightListener = new KeyboardUtils.KeyboardHeightProvider(this, getWindowManager(), findViewById(android.R.id.content), - (keyboardHeight, keyboardOpen, isLandscape) -> { - Log.e("ADDKBD", "show " + keyboardOpen + " height " + keyboardHeight + " land " + isLandscape); - @SuppressLint("CutPasteId") - AdditionalKeyboardView v = findViewById(R.id.additionalKbd); - v.setVisibility(keyboardOpen?View.VISIBLE:View.INVISIBLE); - FrameLayout.LayoutParams p = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, v.getHeight()); - //p.gravity = Gravity.BOTTOM; - p.setMargins(0, keyboardHeight, 0, 0); - - v.setLayoutParams(params); - v.setVisibility(View.VISIBLE); - - }); +// kbdHeightListener = new KeyboardUtils.KeyboardHeightProvider(this, getWindowManager(), findViewById(android.R.id.content), +// (keyboardHeight, keyboardOpen, isLandscape) -> { +// Log.e("ADDKBD", "show " + keyboardOpen + " height " + keyboardHeight + " land " + isLandscape); +// @SuppressLint("CutPasteId") +// AdditionalKeyboardView v = findViewById(R.id.additionalKbd); +// v.setVisibility(keyboardOpen?View.VISIBLE:View.INVISIBLE); +// FrameLayout.LayoutParams p = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, v.getHeight()); +// //p.gravity = Gravity.BOTTOM; +// p.setMargins(0, keyboardHeight, 0, 0); +// +// v.setLayoutParams(params); +// v.setVisibility(View.VISIBLE); +// +// }); registerReceiver(new BroadcastReceiver() { @Override @@ -161,6 +150,10 @@ public void onReceive(Context context, Intent intent) { }, new IntentFilter(ACTION_START)); requestConnection(); + + lorieView.setFocusable(true); + lorieView.setFocusableInTouchMode(true); + lorieView.requestFocus(); } void onReceiveConnection() { @@ -226,6 +219,45 @@ public void setTheme(int resId) { R.style.FullScreen_ExternalDisplay : R.style.NoActionBar); } + public ExtraKeysView getExtraKeysView() { + return mExtraKeysView; + } + + public void setExtraKeysView(ExtraKeysView extraKeysView) { + mExtraKeysView = extraKeysView; + } + + public SurfaceView getLorieView() { + return findViewById(R.id.lorieView); + } + + public ViewPager getTerminalToolbarViewPager() { + return findViewById(R.id.terminal_toolbar_view_pager); + } + + + private void setTerminalToolbarView() { + final ViewPager terminalToolbarViewPager = getTerminalToolbarViewPager(); + + ViewGroup.LayoutParams layoutParams = terminalToolbarViewPager.getLayoutParams(); + mTerminalToolbarDefaultHeight = layoutParams.height; + + terminalToolbarViewPager.setAdapter(new X11ToolbarViewPager.PageAdapter(this, listener)); + terminalToolbarViewPager.addOnPageChangeListener(new X11ToolbarViewPager.OnPageChangeListener(this, terminalToolbarViewPager)); + } + + public void toggleTerminalToolbar() { + final ViewPager terminalToolbarViewPager = getTerminalToolbarViewPager(); + if (terminalToolbarViewPager == null) return; + + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + boolean showNow = preferences.getBoolean("Toolbar", true); + + terminalToolbarViewPager.setVisibility(showNow ? View.VISIBLE : View.GONE); + findViewById(R.id.terminal_toolbar_view_pager).requestFocus(); + } + + private boolean didRequestLaunchExternalDisplay() { return getIntent().getBooleanExtra(REQUEST_LAUNCH_EXTERNAL_DISPLAY, false); } @@ -246,7 +278,7 @@ private void setFullScreenForExternalDisplay() { public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); - if (newConfig.orientation != orientation && kbd != null && kbd.getVisibility() == View.VISIBLE) { + if (newConfig.orientation != orientation /*&& kbd != null && kbd.getVisibility() == View.VISIBLE*/) { InputMethodManager imm = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE); View view = getCurrentFocus(); if (view == null) { @@ -290,19 +322,19 @@ public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, @Non SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); if (isInPictureInPictureMode) { - if (kbd.getVisibility() != View.INVISIBLE) - kbd.setVisibility(View.INVISIBLE); +// if (kbd.getVisibility() != View.INVISIBLE) +// kbd.setVisibility(View.INVISIBLE); frm.setPadding(0, 0, 0, 0); - } else { - if (kbd.getVisibility() != View.VISIBLE) - if (preferences.getBoolean("showAdditionalKbd", true)) { - kbd.setVisibility(View.VISIBLE); - int paddingDp = 35; - float density = this.getResources().getDisplayMetrics().density; - int paddingPixel = (int) (paddingDp * density); - frm.setPadding(0, 0, 0, paddingPixel); - } - } + } // else { +// if (kbd.getVisibility() != View.VISIBLE) +// if (preferences.getBoolean("showAdditionalKbd", true)) { +// kbd.setVisibility(View.VISIBLE); +// int paddingDp = 35; +// float density = this.getResources().getDisplayMetrics().density; +// int paddingPixel = (int) (paddingDp * density); +// frm.setPadding(0, 0, 0, paddingPixel); +// } +// } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); } @@ -312,24 +344,24 @@ public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, @Non @Override public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); - if (preferences.getBoolean("showAdditionalKbd", true) && kbd != null) { - handler.postDelayed(() -> { - Rect r = new Rect(); - getWindow().getDecorView().getWindowVisibleDisplayFrame(r); - WindowInsetsCompat rootInsets = WindowInsetsCompat.toWindowInsetsCompat(kbd.getRootWindowInsets()); - boolean isSoftKbdVisible = rootInsets.isVisible(WindowInsetsCompat.Type.ime()); -// kbd.setVisibility(isSoftKbdVisible ? View.VISIBLE : View.INVISIBLE); - - FrameLayout.LayoutParams p = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); - if (preferences.getBoolean("Reseed", true)) { - p.gravity = Gravity.BOTTOM | Gravity.CENTER; - } else { - p.topMargin = r.bottom - r.top - kbd.getHeight(); - } - -// kbd.setLayoutParams(p); - }, 100); - } +// if (preferences.getBoolean("showAdditionalKbd", true) && kbd != null) { +// handler.postDelayed(() -> { +// Rect r = new Rect(); +// getWindow().getDecorView().getWindowVisibleDisplayFrame(r); +// WindowInsetsCompat rootInsets = WindowInsetsCompat.toWindowInsetsCompat(kbd.getRootWindowInsets()); +// boolean isSoftKbdVisible = rootInsets.isVisible(WindowInsetsCompat.Type.ime()); +//// kbd.setVisibility(isSoftKbdVisible ? View.VISIBLE : View.INVISIBLE); +// +// FrameLayout.LayoutParams p = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT); +// if (preferences.getBoolean("Reseed", true)) { +// p.gravity = Gravity.BOTTOM | Gravity.CENTER; +// } else { +// p.topMargin = r.bottom - r.top - kbd.getHeight(); +// } +// +//// kbd.setLayoutParams(p); +// }, 100); +// } SurfaceView c = v.getRootView().findViewById(R.id.lorieView); SurfaceHolder h = (c != null) ? c.getHolder() : null; diff --git a/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java b/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java new file mode 100644 index 000000000..a51569afe --- /dev/null +++ b/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java @@ -0,0 +1,202 @@ +package com.termux.x11.utils; + +import static com.termux.shared.termux.extrakeys.ExtraKeysConstants.PRIMARY_KEY_CODES_FOR_STRINGS; + +import android.annotation.SuppressLint; +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.Intent; +import android.text.TextUtils; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.viewpager.widget.ViewPager; + +import com.google.android.material.button.MaterialButton; +import com.termux.shared.logger.Logger; +import com.termux.shared.termux.extrakeys.ExtraKeyButton; +import com.termux.shared.termux.extrakeys.ExtraKeysConstants; +import com.termux.shared.termux.extrakeys.ExtraKeysInfo; +import com.termux.shared.termux.extrakeys.ExtraKeysView; +import com.termux.shared.termux.extrakeys.SpecialButton; +import com.termux.shared.termux.settings.properties.TermuxPropertyConstants; +import com.termux.x11.LoriePreferences; +import com.termux.x11.MainActivity; +import com.termux.x11.R; + +import org.json.JSONException; + +public class TermuxX11ExtraKeys implements ExtraKeysView.IExtraKeysView { + + private final View.OnKeyListener mEventListener; + private final MainActivity mActivity; + private final ExtraKeysView mExtraKeysView; + private ExtraKeysInfo mExtraKeysInfo; + + private int metaAltState = 0; + + public TermuxX11ExtraKeys(@NonNull View.OnKeyListener eventlistener, MainActivity mact, ExtraKeysView extrakeysview) { + mEventListener = eventlistener; + mActivity = mact; + mExtraKeysView = extrakeysview; + } + + private final KeyCharacterMap mVirtualKeyboardKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); + static final String ACTION_START_PREFERENCES_ACTIVITY = "com.termux.x11.start_preferences_activity"; + + @Override + public void onExtraKeyButtonClick(View view, ExtraKeyButton buttonInfo, MaterialButton button) { + if (buttonInfo.isMacro()) { + String[] keys = buttonInfo.getKey().split(" "); + boolean ctrlDown = false; + boolean altDown = false; + boolean shiftDown = false; + boolean fnDown = false; + for (String key : keys) { + if (SpecialButton.CTRL.getKey().equals(key)) { + ctrlDown = true; + } else if (SpecialButton.ALT.getKey().equals(key)) { + altDown = true; + } else if (SpecialButton.SHIFT.getKey().equals(key)) { + shiftDown = true; + } else if (SpecialButton.FN.getKey().equals(key)) { + fnDown = true; + } else { + ctrlDown = false; + altDown = false; + shiftDown = false; + fnDown = false; + } + onLorieExtraKeyButtonClick(view, key, ctrlDown, altDown, shiftDown, fnDown); + } + } else { + onLorieExtraKeyButtonClick(view, buttonInfo.getKey(), false, false, false, false); + } + } + + protected void onTerminalExtraKeyButtonClick(View view, String key, boolean ctrlDown, boolean altDown, boolean shiftDown, boolean fnDown) { + if (PRIMARY_KEY_CODES_FOR_STRINGS.containsKey(key)) { + Integer keyCode = PRIMARY_KEY_CODES_FOR_STRINGS.get(key); + if (keyCode == null) return; + int metaState = 0; + + if (ctrlDown) { + metaState |= KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON; + metaAltState |= KeyEvent.KEYCODE_CTRL_LEFT | KeyEvent.KEYCODE_CTRL_RIGHT; + } + + if (altDown) { + metaState |= KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON; + metaAltState |= KeyEvent.KEYCODE_ALT_LEFT | KeyEvent.KEYCODE_ALT_RIGHT; + } + + if (shiftDown) { + metaState |= KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON; + } + + if (fnDown) { + metaState |= KeyEvent.META_FUNCTION_ON; + } + + + mEventListener.onKey(mActivity.getLorieView(), keyCode, new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, keyCode, 0, metaState)); + mEventListener.onKey(mActivity.getLorieView(), keyCode, new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 0, metaState)); + + } else { + // not a control char + key.codePoints().forEach(codePoint -> { + char[] ch; + ch = Character.toChars(codePoint); + KeyEvent[] events = mVirtualKeyboardKeyCharacterMap.getEvents(ch); + if (events != null) { + for (KeyEvent event : events) { + + int keyCode = event.getKeyCode(); + + if (metaAltState != 0) { + mEventListener.onKey(mActivity.getLorieView(), keyCode, new KeyEvent(metaAltState, keyCode)); + } + + mEventListener.onKey(mActivity.getLorieView(), keyCode, new KeyEvent(KeyEvent.ACTION_UP, keyCode)); + + if (metaAltState != 0) { + mEventListener.onKey(mActivity.getLorieView(), keyCode, new KeyEvent(metaAltState, keyCode)); + metaAltState = 0; + } + } + } + }); + } + } + + @Override + public boolean performExtraKeyButtonHapticFeedback(View view, ExtraKeyButton buttonInfo, MaterialButton button) { + return false; + } + + public void paste(CharSequence input) { + KeyEvent[] events = mVirtualKeyboardKeyCharacterMap.getEvents(input.toString().toCharArray()); + if (events != null) { + for (KeyEvent event : events) { + int keyCode = event.getKeyCode(); + mEventListener.onKey(mActivity.getLorieView(), keyCode, event); + } + } + } + + private ViewPager getToolbarViewPager() { + return mActivity.findViewById(R.id.terminal_toolbar_view_pager); + } + + @SuppressLint("RtlHardcoded") + public void onLorieExtraKeyButtonClick(View view, String key, boolean ctrlDown, boolean altDown, boolean shiftDown, boolean fnDown) { + if ("KEYBOARD".equals(key)) { + + if (getToolbarViewPager()!=null) { + getToolbarViewPager().requestFocus(); + KeyboardUtils.toggleKeyboardVisibility(mActivity); + } + + } else if ("DRAWER".equals(key)) { + Intent preferencesIntent = new Intent(mActivity, LoriePreferences.class); + preferencesIntent.setAction(ACTION_START_PREFERENCES_ACTIVITY); + mActivity.startActivity(preferencesIntent); + + } else if ("PASTE".equals(key)) { + + ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clipData = clipboard.getPrimaryClip(); + + if (clipData != null) { + CharSequence pasted = clipData.getItemAt(0).coerceToText(mActivity); + if (!TextUtils.isEmpty(pasted)) paste(pasted); + } + + } else { + onTerminalExtraKeyButtonClick(view, key, ctrlDown, altDown, shiftDown, fnDown); + } + } + + /** + * Set the terminal extra keys and style. + */ + private void setExtraKeys() { + mExtraKeysInfo = null; + try { + mExtraKeysInfo = new ExtraKeysInfo(TermuxPropertyConstants.DEFAULT_IVALUE_EXTRA_KEYS, TermuxPropertyConstants.DEFAULT_IVALUE_EXTRA_KEYS_STYLE, ExtraKeysConstants.CONTROL_CHARS_ALIASES); + } catch (JSONException e2) { + Logger.showToast(mActivity, "Can't create default extra keys",true); + Logger.logStackTraceWithMessage("TermuxX11ExtraKeys", "Could create default extra keys: ", e2); + mExtraKeysInfo = null; + } + } + + public 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 new file mode 100644 index 000000000..824e72365 --- /dev/null +++ b/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java @@ -0,0 +1,105 @@ +package com.termux.x11.utils; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.KeyEvent; +import android.view.KeyCharacterMap; + +import android.widget.EditText; + +import androidx.annotation.NonNull; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import com.termux.shared.termux.extrakeys.ExtraKeysView; +import com.termux.x11.MainActivity; +import com.termux.x11.R; + +public class X11ToolbarViewPager { + public static class PageAdapter extends PagerAdapter { + + final MainActivity act; + private final View.OnKeyListener mEventListener; + private final KeyCharacterMap mVirtualKeyboardKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); + + public PageAdapter(MainActivity activity, View.OnKeyListener listen) { + this.act = activity; + this.mEventListener = listen; + } + + @Override + public int getCount() { + return 2; + } + + @Override + public boolean isViewFromObject(@NonNull View view, @NonNull Object object) { + return view == object; + } + + @NonNull + @Override + public Object instantiateItem(@NonNull ViewGroup collection, int position) { + LayoutInflater inflater = LayoutInflater.from(act); + View layout; + if (position == 0) { + layout = inflater.inflate(R.layout.view_terminal_toolbar_extra_keys, collection, false); + ExtraKeysView extraKeysView = (ExtraKeysView) layout; + act.mExtraKeys = new TermuxX11ExtraKeys(mEventListener, act, extraKeysView); + int mTerminalToolbarDefaultHeight = act.getTerminalToolbarViewPager().getLayoutParams().height; + int height = mTerminalToolbarDefaultHeight * + ((act.mExtraKeys.getExtraKeysInfo() == null) ? 0 : act.mExtraKeys.getExtraKeysInfo().getMatrix().length); + extraKeysView.reload(act.mExtraKeys.getExtraKeysInfo(), height); + extraKeysView.setExtraKeysViewClient(act.mExtraKeys); + act.setExtraKeysView(extraKeysView); + } else { + layout = inflater.inflate(R.layout.view_terminal_toolbar_text_input, collection, false); + final EditText editText = layout.findViewById(R.id.terminal_toolbar_text_input); + + editText.setOnEditorActionListener((v, actionId, event) -> { + String textToSend = editText.getText().toString(); + if (textToSend.length() == 0) textToSend = "\r"; + + KeyEvent[] events = mVirtualKeyboardKeyCharacterMap.getEvents(textToSend.toCharArray()); + for (KeyEvent evnt : events) { + int keyCode = evnt.getKeyCode(); + mEventListener.onKey(act.getLorieView(), keyCode, evnt); + } + + editText.setText(""); + return true; + }); + } + collection.addView(layout); + return layout; + } + + @Override + public void destroyItem(@NonNull ViewGroup collection, int position, @NonNull Object view) { + collection.removeView((View) view); + } + + } + + public static class OnPageChangeListener extends ViewPager.SimpleOnPageChangeListener { + + final MainActivity act; + final ViewPager mTerminalToolbarViewPager; + + public OnPageChangeListener(MainActivity activity, ViewPager viewPager) { + this.act = activity; + this.mTerminalToolbarViewPager = viewPager; + } + + @Override + public void onPageSelected(int position) { + if (position == 0) { + act.getLorieView().requestFocus(); + } else { + final EditText editText = mTerminalToolbarViewPager.findViewById(R.id.terminal_toolbar_text_input); + if (editText != null) editText.requestFocus(); + } + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/main_activity.xml b/app/src/main/res/layout/main_activity.xml index 8800a0c5d..a61fe5813 100644 --- a/app/src/main/res/layout/main_activity.xml +++ b/app/src/main/res/layout/main_activity.xml @@ -87,18 +87,18 @@ - + android:layout_height="37.5dp" + android:background="@color/black" + android:layout_gravity="bottom|center"/> - - + diff --git a/app/src/main/res/layout/view_terminal_toolbar_extra_keys.xml b/app/src/main/res/layout/view_terminal_toolbar_extra_keys.xml new file mode 100644 index 000000000..a921343cf --- /dev/null +++ b/app/src/main/res/layout/view_terminal_toolbar_extra_keys.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_terminal_toolbar_text_input.xml b/app/src/main/res/layout/view_terminal_toolbar_text_input.xml new file mode 100644 index 000000000..ed0b23ded --- /dev/null +++ b/app/src/main/res/layout/view_terminal_toolbar_text_input.xml @@ -0,0 +1,16 @@ + + \ No newline at end of file From 4574fd91ab57edb0c5ce6940c69f5e8c1f1bd4f2 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 2 Mar 2023 00:04:59 +0200 Subject: [PATCH 09/21] Physical keyboard support. Fixing ExtraKeys's text input view. Now it can send text if all the characters of this text are stored in one or more keyboard layouts. Making key listener ignore Android's autorepeat. --- .../main/cpp/lorie-client/lorie-client.cpp | 18 +++++++--- .../main/cpp/lorie-client/xcb-connection.hpp | 30 ++++++++++++++++ .../java/com/termux/x11/MainActivity.java | 36 ++++++++++++------- .../termux/x11/utils/X11ToolbarViewPager.java | 9 ++--- 4 files changed, 69 insertions(+), 24 deletions(-) diff --git a/app/src/main/cpp/lorie-client/lorie-client.cpp b/app/src/main/cpp/lorie-client/lorie-client.cpp index 17127d54a..fe48fea3e 100644 --- a/app/src/main/cpp/lorie-client/lorie-client.cpp +++ b/app/src/main/cpp/lorie-client/lorie-client.cpp @@ -297,6 +297,13 @@ class lorie_client { }); } + void send_key(xcb_keysym_t keysym, u8 type) { + if (c.conn) + post([=] { + c.xkb.send_key(keysym, type); + }); + } + void connection_poll_func() { try { bool need_redraw = false; @@ -440,24 +447,26 @@ Java_com_termux_x11_MainActivity_onPointerButton([[maybe_unused]] JNIEnv *env, [ extern "C" JNIEXPORT void JNICALL Java_com_termux_x11_MainActivity_onKeySym([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject thiz, - jint keycode, jint unicodeChar, jstring str, jint meta_state) { + jint keycode, jint unicodeChar, jstring str, jint meta_state, jint type) { wchar_t unicode = unicodeChar; mbstate_t ps {}; - ALOGE("1"); + /* When event is triggered by software keyboard type is always 0 */ + if (type) { + client.send_key(android_to_keysyms[keycode], type); + return; + } if (unicode && !meta_state) { client.send_keysym(xkb_utf32_to_keysym(unicode), meta_state); return; } - ALOGE("2"); if (android_to_keysyms[keycode]) { client.send_keysym(android_to_keysyms[keycode], meta_state); return; } - ALOGE("3"); if (str) { const char *characters = env->GetStringUTFChars(str, nullptr); const char *chars = characters; // mbsrtowcs will set it to nullptr, but we still need to release it @@ -469,7 +478,6 @@ Java_com_termux_x11_MainActivity_onKeySym([[maybe_unused]] JNIEnv *env, [[maybe_ env->ReleaseStringUTFChars(str, characters); } - ALOGE("4"); } extern "C" diff --git a/app/src/main/cpp/lorie-client/xcb-connection.hpp b/app/src/main/cpp/lorie-client/xcb-connection.hpp index e2b8fa7f2..6b8d1eff7 100644 --- a/app/src/main/cpp/lorie-client/xcb-connection.hpp +++ b/app/src/main/cpp/lorie-client/xcb-connection.hpp @@ -438,6 +438,36 @@ class xcb_connection { free(reply); xcb_flush(self.conn); } + + void send_key(xcb_keysym_t keysym, u8 type) { + // SurfaceView can only receive Unicode input from a virtual keyboard. + // Since SurfaceView is not a text editor, it cannot receive Unicode input from a + // physical keyboard. This is actually beneficial for us as we receive original + // input events, albeit with encoded keycodes. Since we receive original keycodes + // regardless of the layout, we can simply pass them to X11 as is, and the desktop + // environment will determine in which layout the user will be typing. + // Here we do not change current layout and do not change modifiers because + // this function should only be triggered by hardware keyboard or extra key bar. + char buf[64]{}; + xkb_keysym_get_name(keysym, buf, sizeof(buf)); + ALOGV("Sending keysym %s", buf); + + auto code = std::find_if(codes.begin(), codes.end(), [&c = keysym](auto& code) { + return code.keysym == c; + }); + + if (code == codes.end()) { + xkb_keysym_get_name(keysym, buf, sizeof(buf)); + ALOGE("Code for keysym 0x%X (%s) not found...", keysym, buf); + return; + } + + xcb_flush(self.conn); + + xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(self.conn)).data; + xcb_test_fake_input(self.conn, type, code->keycode, XCB_CURRENT_TIME, s->root, 0, 0, 0); + xcb_flush(self.conn); + } } xkb {*this}; void init(int sockfd) { diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 4f9ffc3fe..b9473fb61 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -422,8 +422,14 @@ private boolean isSource(KeyEvent e, int source) { private boolean rightPressed = false; // Prevent right button press event from being repeated private boolean middlePressed = false; // Prevent middle button press event from being repeated + @SuppressWarnings("DanglingJavadoc") @Override public boolean onKey(View v, int keyCode, KeyEvent e) { + // Ignoring Android's autorepeat. + if (e.getRepeatCount() > 0) + return true; + + if (keyCode == KeyEvent.KEYCODE_BACK) { if (isSource(e, InputDevice.SOURCE_MOUSE) && rightPressed != (e.getAction() == KeyEvent.ACTION_DOWN)) { @@ -444,7 +450,7 @@ public boolean onKey(View v, int keyCode, KeyEvent e) { return true; } - /* + /** * A KeyEvent is generated in Android when the user interacts with the device's * physical keyboard or software keyboard. If the KeyEvent is generated by a physical key, * or a software key that has a corresponding physical key, it will contain a key code. @@ -471,7 +477,7 @@ public boolean onKey(View v, int keyCode, KeyEvent e) { * */ Log.e("KEY", " " + e + " " + e.isShiftPressed() + " " + e.isAltPressed() + " " + e.isCtrlPressed() + " " + e.isMetaPressed()); - if (e.getDevice().isVirtual()) { + if (e.getDevice() == null || e.getDevice().isVirtual()) { Log.e("KEY", "Virtual"); if (e.getAction() == KeyEvent.ACTION_UP || e.getAction() == KeyEvent.ACTION_MULTIPLE) { Log.e("KEY", "Up or multiple"); @@ -488,18 +494,22 @@ public boolean onKey(View v, int keyCode, KeyEvent e) { break; default: Log.e("KEY", "other key"); - onKeySym(keyCode, e.getUnicodeChar(), e.getCharacters(), e.getMetaState()); + onKeySym(keyCode, e.getUnicodeChar(), e.getCharacters(), e.getMetaState(), 0); } } - } // else -// switch(e.getAction()) { -// case KeyEvent.ACTION_DOWN: -// onKeyboardKey(keyCode, KeyPress, e.isShiftPressed() ? 1 : 0, e.getUnicodeChar(), e.getCharacters()); -// break; -// case KeyEvent.ACTION_UP: -// onKeyboardKey(keyCode, KeyRelease, e.isShiftPressed() ? 1 : 0, e.getUnicodeChar(), e.getCharacters()); -// break; -// } + } else + /** + * Android does not send us non-latin symbols from physical keyboard + * so we can trust those events and not emulate typing. + */ + switch(e.getAction()) { + case KeyEvent.ACTION_DOWN: + onKeySym(keyCode, 0, null, 0, KeyPress); + break; + case KeyEvent.ACTION_UP: + onKeySym(keyCode, 0, null, 0, KeyRelease); + break; + } return true; } } @@ -545,7 +555,7 @@ void setCursorRect(int x, int y, int w, int h) { public native void onPointerMotion(int x, int y); public native void onPointerScroll(int axis, float value); public native void onPointerButton(int button, int type); - private native void onKeySym(int keyCode, int unicode, String str, int metaState); + private native void onKeySym(int keyCode, int unicode, String str, int metaState, int type); private native void nativeResume(); private native void nativePause(); 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 824e72365..9fce33efe 100644 --- a/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java +++ b/app/src/main/java/com/termux/x11/utils/X11ToolbarViewPager.java @@ -1,5 +1,6 @@ package com.termux.x11.utils; +import android.view.InputDevice; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -60,12 +61,8 @@ public Object instantiateItem(@NonNull ViewGroup collection, int position) { editText.setOnEditorActionListener((v, actionId, event) -> { String textToSend = editText.getText().toString(); if (textToSend.length() == 0) textToSend = "\r"; - - KeyEvent[] events = mVirtualKeyboardKeyCharacterMap.getEvents(textToSend.toCharArray()); - for (KeyEvent evnt : events) { - int keyCode = evnt.getKeyCode(); - mEventListener.onKey(act.getLorieView(), keyCode, evnt); - } + KeyEvent e = new KeyEvent(0, textToSend, KeyCharacterMap.VIRTUAL_KEYBOARD, 0); + mEventListener.onKey(act.getLorieView(), 0, e); editText.setText(""); return true; From 252706d67969605ffb459a81af0eb1e685724be4 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 2 Mar 2023 00:34:24 +0200 Subject: [PATCH 10/21] Added toggling ExtraKeys bar with triple finger swipe down. --- .../java/com/termux/x11/MainActivity.java | 9 +++++++++ .../main/java/com/termux/x11/TouchParser.java | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index b9473fb61..a3bc5f1ce 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -24,6 +24,7 @@ import android.view.Gravity; import android.view.InputDevice; import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.PointerIcon; import android.view.Surface; import android.view.SurfaceHolder; @@ -370,6 +371,13 @@ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { return insets; } + @Override + public void toggleExtraKeys() { + int visibility = getTerminalToolbarViewPager().getVisibility(); + int newVisibility = (visibility != View.VISIBLE) ? View.VISIBLE : View.GONE; + getTerminalToolbarViewPager().setVisibility(newVisibility); + } + @SuppressWarnings("SameParameterValue") private class ServiceEventListener implements View.OnKeyListener { public static final int KeyPress = 2; // synchronized with X.h @@ -554,6 +562,7 @@ void setCursorRect(int x, int y, int w, int h) { private native void windowChanged(Surface surface, int width, int height); public native void onPointerMotion(int x, int y); public native void onPointerScroll(int axis, float value); + public native void onPointerButton(int button, int type); private native void onKeySym(int keyCode, int unicode, String str, int metaState, int type); private native void nativeResume(); diff --git a/app/src/main/java/com/termux/x11/TouchParser.java b/app/src/main/java/com/termux/x11/TouchParser.java index a6a938ca3..c0ab88f27 100644 --- a/app/src/main/java/com/termux/x11/TouchParser.java +++ b/app/src/main/java/com/termux/x11/TouchParser.java @@ -53,6 +53,7 @@ public interface OnTouchParseListener { void onPointerButton(int button, int state); void onPointerMotion(int x, int y); void onPointerScroll(int axis, float value); + void toggleExtraKeys(); } private int mTouchSlopSquare; @@ -67,6 +68,7 @@ public interface OnTouchParseListener { private static final int TOUCH_SLOP = 8; private static final int DOUBLE_TAP_SLOP = 100; private static final int DOUBLE_TAP_TOUCH_SLOP = TOUCH_SLOP; + private static final int TRIPLE_FINGER_SWING_TRESHOLD = 10; // constants for Message.what used by GestureHandler below private static final int SHOW_PRESS = 1; @@ -321,6 +323,8 @@ && isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) { case MotionEvent.ACTION_UP: mStillDown = false; + currentTripleFingerTriggered = false; + currentTripleFingerScrollValue = 0; MotionEvent currentUpEvent = MotionEvent.obtain(ev); if (mMode == TOUCH_MODE_MOUSE) { stopDrag(); @@ -368,6 +372,8 @@ && isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) { return handled; } + float currentTripleFingerScrollValue = 0; + boolean currentTripleFingerTriggered = false; private void onScroll(MotionEvent ev, float scrollX, float scrollY) { if (ev.getPointerCount() == 1) { if (mMode == TOUCH_MODE_MOUSE) { @@ -392,6 +398,20 @@ private void onScroll(MotionEvent ev, float scrollX, float scrollY) { mListener.onPointerScroll(MotionEvent.AXIS_Y, (int)scrollX); if (scrollY != 0) mListener.onPointerScroll(MotionEvent.AXIS_X, (int)scrollY); + } else if (ev.getPointerCount() == 3) { + if (currentTripleFingerTriggered) + return; + + if (scrollY > 0) + currentTripleFingerScrollValue = 0; // some kind of reset + if (scrollY < 0) + currentTripleFingerScrollValue -= scrollY; + + if (currentTripleFingerScrollValue < -30 || currentTripleFingerScrollValue > 30) { + mListener.toggleExtraKeys(); + currentTripleFingerScrollValue = 0; + currentTripleFingerTriggered = true; + } } } From b12bda621c88a6a512d5bae7c57779a030e88501 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 2 Mar 2023 01:10:13 +0200 Subject: [PATCH 11/21] Fixing broken `wl_shell` protocol to handle old Xwayland... --- app/src/main/cpp/lorie/compositor.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/cpp/lorie/compositor.cpp b/app/src/main/cpp/lorie/compositor.cpp index ffa3f85bc..fe7d0f892 100644 --- a/app/src/main/cpp/lorie/compositor.cpp +++ b/app/src/main/cpp/lorie/compositor.cpp @@ -186,7 +186,9 @@ lorie_compositor::lorie_compositor(int dpi): dpi(dpi) { report_mode(800, 600); output->on_release = [=]{ output->destroy(); }; }; - global_shell.on_bind = [](client_t*, shell_t*) {}; + global_shell.on_bind = [](client_t*, shell_t* shell) { + shell->on_get_shell_surface = [](shell_surface_t*, surface_t*){}; + }; global_xdg_wm_base.on_bind = [](client_t*, xdg_wm_base_t* wm_base) { wm_base->on_get_xdg_surface = [](xdg_surface_t*, surface_t*) {}; wm_base->on__destroy = [=]() { wm_base->destroy(); }; From 8ddb65472b1dfea0dd1a59d4df1735417f7ec826 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 2 Mar 2023 02:23:42 +0200 Subject: [PATCH 12/21] Basic chroot Xorg support... Enabling stderr-to-logcat by default to catch xkbcommon errors. --- .../main/cpp/lorie-client/lorie-client.cpp | 71 +++++++++++++------ .../main/cpp/lorie-client/xcb-connection.hpp | 12 ++++ .../java/com/termux/x11/MainActivity.java | 2 +- .../termux/x11/utils/TermuxX11ExtraKeys.java | 12 +--- 4 files changed, 65 insertions(+), 32 deletions(-) diff --git a/app/src/main/cpp/lorie-client/lorie-client.cpp b/app/src/main/cpp/lorie-client/lorie-client.cpp index fe48fea3e..1d52c61b6 100644 --- a/app/src/main/cpp/lorie-client/lorie-client.cpp +++ b/app/src/main/cpp/lorie-client/lorie-client.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -176,6 +177,52 @@ class lorie_client { refresh_cursor(); } + void attach_region() { + if (screen.shmaddr) + munmap(screen.shmaddr, DEFAULT_SHMSEG_LENGTH); + if (screen.shmfd) + close(screen.shmfd); + + ALOGE("Creating ashmem file..."); + screen.shmfd = os_create_anonymous_file(DEFAULT_SHMSEG_LENGTH); + if (screen.shmfd < 0) { + ALOGE("Error opening file: %s", strerror(errno)); + } else { + fchmod(screen.shmfd, 0777); + ALOGE("Attaching file..."); + screen.shmaddr = static_cast(mmap(nullptr, DEFAULT_SHMSEG_LENGTH, + PROT_READ | PROT_WRITE, + MAP_SHARED, screen.shmfd, 0)); + if (screen.shmaddr == MAP_FAILED) { + ALOGE("Map failed: %s", strerror(errno)); + } + } + + // There is a chance that Xwayland we are trying to connect is non-termux, try another way + if (screen.shmfd < 0) { + screen.shmfd = c.shm.create_segment(screen.shmseg, DEFAULT_SHMSEG_LENGTH, 0); + if (screen.shmfd < 0) + ALOGE("Failed to retrieve shared segment file descriptor..."); + else { + struct statfs info{}; + fstatfs(screen.shmfd, &info); + if (info.f_type != TMPFS_MAGIC) { + ALOGE("Shared segment is not hosted on tmpfs. You are likely doing something wrong. " + "Check if /run/shm, /var/tmp and /tmp are tmpfs."); + } else { + ALOGE("Attaching shared memory segment..."); + screen.shmaddr = static_cast(mmap(nullptr, DEFAULT_SHMSEG_LENGTH, + PROT_READ | PROT_WRITE, + MAP_SHARED, screen.shmfd, 0)); + if (screen.shmaddr == MAP_FAILED) { + ALOGE("Map failed: %s", strerror(errno)); + } + } + } + } + c.shm.attach_fd(screen.shmseg, screen.shmfd, 0); + } + void adopt_connection_fd(int fd) { try { static int old_fd = -1; @@ -201,26 +248,7 @@ class lorie_client { screen.shmseg = xcb_generate_id(c.conn); - if (screen.shmaddr) - munmap(screen.shmaddr, DEFAULT_SHMSEG_LENGTH); - if (screen.shmfd) - close(screen.shmfd); - - ALOGE("Creating file..."); - screen.shmfd = os_create_anonymous_file(DEFAULT_SHMSEG_LENGTH); - if (screen.shmfd < 1) { - ALOGE("Error opening file: %s", strerror(errno)); - } - fchmod(screen.shmfd, 0777); - ALOGE("Attaching file..."); - screen.shmaddr = static_cast(mmap(nullptr, 8096 * 8096 * 4, - PROT_READ | PROT_WRITE, - MAP_SHARED, screen.shmfd, 0)); - if (screen.shmaddr == MAP_FAILED) { - ALOGE("Map failed: %s", strerror(errno)); - } - c.shm.attach_fd(screen.shmseg, screen.shmfd, 0); - + attach_region(); refresh_cursor(); @@ -494,7 +522,8 @@ Java_com_termux_x11_MainActivity_nativePause([[maybe_unused]] JNIEnv *env, [[may client.paused = true; } -#if 0 +// I need this to catch initialisation errors of libxkbcommon. +#if 1 // It is needed to redirect stderr to logcat [[maybe_unused]] std::thread stderr_to_logcat_thread([]{ FILE *fp; diff --git a/app/src/main/cpp/lorie-client/xcb-connection.hpp b/app/src/main/cpp/lorie-client/xcb-connection.hpp index 6b8d1eff7..0333e9dd8 100644 --- a/app/src/main/cpp/lorie-client/xcb-connection.hpp +++ b/app/src/main/cpp/lorie-client/xcb-connection.hpp @@ -96,6 +96,18 @@ class xcb_connection { self.handle_error("Error attaching file descriptor through MIT-SHM extension"); }; + int create_segment(u32 seg, u32 size, u8 ro) { + int fd; + auto reply = xcb(shm_create_segment, seg, size, ro); + self.handle_error("Error creating shared segment through MIT-SHM extension"); + if (reply->nfd != 1) { + std::runtime_error("Error creating shared segment through MIT-SHM extension: did not get file descriptor"); + } + fd = xcb_shm_create_segment_reply_fds(self.conn, reply)[0]; + free(reply); + return fd; + } + [[maybe_unused]] void detach(u32 seg) { xcb_check(shm_detach, seg); self.handle_error("Error attaching shared segment through MIT-SHM extension"); diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index a3bc5f1ce..04aa727df 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -564,7 +564,7 @@ void setCursorRect(int x, int y, int w, int h) { public native void onPointerScroll(int axis, float value); public native void onPointerButton(int button, int type); - private native void onKeySym(int keyCode, int unicode, String str, int metaState, int type); + public native void onKeySym(int keyCode, int unicode, String str, int metaState, int type); private native void nativeResume(); private native void nativePause(); 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 a51569afe..bf6c02f47 100644 --- a/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java +++ b/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java @@ -38,9 +38,9 @@ public class TermuxX11ExtraKeys implements ExtraKeysView.IExtraKeysView { private int metaAltState = 0; - public TermuxX11ExtraKeys(@NonNull View.OnKeyListener eventlistener, MainActivity mact, ExtraKeysView extrakeysview) { + public TermuxX11ExtraKeys(@NonNull View.OnKeyListener eventlistener, MainActivity activity, ExtraKeysView extrakeysview) { mEventListener = eventlistener; - mActivity = mact; + mActivity = activity; mExtraKeysView = extrakeysview; } @@ -101,10 +101,8 @@ protected void onTerminalExtraKeyButtonClick(View view, String key, boolean ctrl metaState |= KeyEvent.META_FUNCTION_ON; } - mEventListener.onKey(mActivity.getLorieView(), keyCode, new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, keyCode, 0, metaState)); mEventListener.onKey(mActivity.getLorieView(), keyCode, new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 0, metaState)); - } else { // not a control char key.codePoints().forEach(codePoint -> { @@ -154,27 +152,21 @@ private ViewPager getToolbarViewPager() { @SuppressLint("RtlHardcoded") public void onLorieExtraKeyButtonClick(View view, String key, boolean ctrlDown, boolean altDown, boolean shiftDown, boolean fnDown) { if ("KEYBOARD".equals(key)) { - if (getToolbarViewPager()!=null) { getToolbarViewPager().requestFocus(); KeyboardUtils.toggleKeyboardVisibility(mActivity); } - } else if ("DRAWER".equals(key)) { Intent preferencesIntent = new Intent(mActivity, LoriePreferences.class); preferencesIntent.setAction(ACTION_START_PREFERENCES_ACTIVITY); mActivity.startActivity(preferencesIntent); - } else if ("PASTE".equals(key)) { - ClipboardManager clipboard = (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE); ClipData clipData = clipboard.getPrimaryClip(); - if (clipData != null) { CharSequence pasted = clipData.getItemAt(0).coerceToText(mActivity); if (!TextUtils.isEmpty(pasted)) paste(pasted); } - } else { onTerminalExtraKeyButtonClick(view, key, ctrlDown, altDown, shiftDown, fnDown); } From 32236a1d1d269aefd1540d30f474411531a75771 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 2 Mar 2023 02:33:14 +0200 Subject: [PATCH 13/21] Fix appearing of extra keys when it is unchecked in preferences. --- app/src/main/java/com/termux/x11/MainActivity.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 04aa727df..b6113b455 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -12,7 +12,6 @@ import android.content.SharedPreferences; import android.content.res.Configuration; import android.graphics.PixelFormat; -import android.graphics.Rect; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -21,10 +20,8 @@ import android.os.RemoteException; import android.preference.PreferenceManager; import android.util.Log; -import android.view.Gravity; import android.view.InputDevice; import android.view.KeyEvent; -import android.view.MotionEvent; import android.view.PointerIcon; import android.view.Surface; import android.view.SurfaceHolder; @@ -40,7 +37,6 @@ import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.view.WindowInsetsCompat; import androidx.viewpager.widget.ViewPager; import com.termux.shared.termux.extrakeys.ExtraKeysView; @@ -107,7 +103,7 @@ protected void onCreate(Bundle savedInstanceState) { mTP = new TouchParser(lorieView, this); setTerminalToolbarView(); - toggleTerminalToolbar(); + toggleX11Toolbar(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) getWindow(). @@ -247,12 +243,12 @@ private void setTerminalToolbarView() { terminalToolbarViewPager.addOnPageChangeListener(new X11ToolbarViewPager.OnPageChangeListener(this, terminalToolbarViewPager)); } - public void toggleTerminalToolbar() { + public void toggleX11Toolbar() { final ViewPager terminalToolbarViewPager = getTerminalToolbarViewPager(); if (terminalToolbarViewPager == null) return; SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); - boolean showNow = preferences.getBoolean("Toolbar", true); + boolean showNow = preferences.getBoolean("showAdditionalKbd", true); terminalToolbarViewPager.setVisibility(showNow ? View.VISIBLE : View.GONE); findViewById(R.id.terminal_toolbar_view_pager).requestFocus(); @@ -373,6 +369,10 @@ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { @Override public void toggleExtraKeys() { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + if (!preferences.getBoolean("showAdditionalKbd", true)) + return; + int visibility = getTerminalToolbarViewPager().getVisibility(); int newVisibility = (visibility != View.VISIBLE) ? View.VISIBLE : View.GONE; getTerminalToolbarViewPager().setVisibility(newVisibility); From 34ea025cf8400e88fea2bd7ac876bc0ce56f3a5e Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 2 Mar 2023 10:58:32 +0200 Subject: [PATCH 14/21] Adding an ability to output logcat of `com.termux.x11` via TERMUX_X11_DEBUG=1 environment variable. --- .../com/termux/x11/ICmdEntryInterface.aidl | 1 + .../main/cpp/lorie-client/lorie-client.cpp | 65 ++++++++++++++++ app/src/main/cpp/lorie/compositor.cpp | 27 +++++++ .../extrakeys/SpecialButtonStateChecker.java | 14 ++++ .../java/com/termux/x11/CmdEntryPoint.java | 11 +++ .../java/com/termux/x11/MainActivity.java | 43 +++-------- .../termux/x11/utils/TermuxX11ExtraKeys.java | 74 ++++++++----------- 7 files changed, 158 insertions(+), 77 deletions(-) create mode 100644 app/src/main/java/com/termux/shared/termux/extrakeys/SpecialButtonStateChecker.java diff --git a/app/src/main/aidl/com/termux/x11/ICmdEntryInterface.aidl b/app/src/main/aidl/com/termux/x11/ICmdEntryInterface.aidl index 8c125d557..c13edb781 100644 --- a/app/src/main/aidl/com/termux/x11/ICmdEntryInterface.aidl +++ b/app/src/main/aidl/com/termux/x11/ICmdEntryInterface.aidl @@ -4,4 +4,5 @@ package com.termux.x11; interface ICmdEntryInterface { void outputResize(int width, int height); ParcelFileDescriptor getXConnection(); + ParcelFileDescriptor getLogcatOutput(); } \ No newline at end of file diff --git a/app/src/main/cpp/lorie-client/lorie-client.cpp b/app/src/main/cpp/lorie-client/lorie-client.cpp index 1d52c61b6..32944d9f8 100644 --- a/app/src/main/cpp/lorie-client/lorie-client.cpp +++ b/app/src/main/cpp/lorie-client/lorie-client.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #if 1 #define ALOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "LorieX11Client", fmt, ## __VA_ARGS__) @@ -539,3 +540,67 @@ Java_com_termux_x11_MainActivity_nativePause([[maybe_unused]] JNIEnv *env, [[may } }); #endif + +static bool sameUid(int pid) { + char path[32] = {0}; + struct stat s = {0}; + sprintf(path, "/proc/%d", pid); + stat(path, &s); + return s.st_uid == getuid(); +} + +static void killAllLogcats() { + DIR* proc; + struct dirent* dir_elem; + char path[64] = {0}, link[64] = {0}; + pid_t pid, self = getpid(); + if ((proc = opendir("/proc")) == nullptr) { + ALOGE("opendir: %s", strerror(errno)); + return; + } + + while((dir_elem = readdir(proc)) != nullptr) { + if (!(pid = (pid_t) atoi (dir_elem->d_name)) || pid == self || !sameUid(pid)) // NOLINT(cert-err34-c) + continue; + + memset(path, 0, sizeof(path)); + memset(link, 0, sizeof(link)); + sprintf(path, "/proc/%d/exe", pid); + if (readlink(path, link, sizeof(link)) < 0) { + ALOGE("readlink %s: %s", path, strerror(errno)); + continue; + } + if (strstr(link, "/logcat") != nullptr) { + if (kill(pid, SIGKILL) < 0) { + ALOGE("kill %d (%s): %s", pid, link, strerror(errno)); + } + } + } +} + +void fork(const std::function& f) { + switch(fork()) { + case -1: ALOGE("fork: %s", strerror(errno)); return; + case 0: f(); return; + default: return; + } +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_termux_x11_MainActivity_startLogcat(JNIEnv *env, jobject thiz, jint fd) { + killAllLogcats(); + + ALOGV("Starting logcat with output to given fd"); + fork([]() { + execl("/system/bin/logcat", "logcat", "-c", nullptr); + ALOGE("exec logcat: %s", strerror(errno)); + }); + + fork([fd]() { + dup2(fd, 1); + dup2(fd, 2); + execl("/system/bin/logcat", "logcat", nullptr); + ALOGE("exec logcat: %s", strerror(errno)); + }); +} \ No newline at end of file diff --git a/app/src/main/cpp/lorie/compositor.cpp b/app/src/main/cpp/lorie/compositor.cpp index fe7d0f892..cd1f5716f 100644 --- a/app/src/main/cpp/lorie/compositor.cpp +++ b/app/src/main/cpp/lorie/compositor.cpp @@ -343,4 +343,31 @@ Java_com_termux_x11_CmdEntryPoint_connect([[maybe_unused]] JNIEnv *env, [[maybe_ } return sockfd; +} + +extern "C" +JNIEXPORT jint JNICALL +Java_com_termux_x11_CmdEntryPoint_stderr(JNIEnv *env, jclass clazz) { + const char *debug = getenv("TERMUX_X11_DEBUG"); + if (debug && !strcmp(debug, "1")) { + int p[2]; + pipe(p); + fchmod(p[1], 0777); + if (!fork()) { + close(p[1]); + FILE *fp = fdopen(p[0], "r"); + size_t len; + char *line = nullptr; + + while ((getline(&line, &len, fp)) != -1) { + printf("com.termux.x11 logcat: %s", line); + } + exit(env, 0); + } + close(p[0]); + + return p[1]; + } + + return -1; } \ No newline at end of file diff --git a/app/src/main/java/com/termux/shared/termux/extrakeys/SpecialButtonStateChecker.java b/app/src/main/java/com/termux/shared/termux/extrakeys/SpecialButtonStateChecker.java new file mode 100644 index 000000000..c358a1f79 --- /dev/null +++ b/app/src/main/java/com/termux/shared/termux/extrakeys/SpecialButtonStateChecker.java @@ -0,0 +1,14 @@ +package com.termux.shared.termux.extrakeys; + +public class SpecialButtonStateChecker { + public static boolean isActive(SpecialButtonState state) { + if (state == null) + return false; + return state.isActive; + } + public static boolean isLocked(SpecialButtonState state) { + if (state == null) + return false; + return state.isLocked; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/termux/x11/CmdEntryPoint.java b/app/src/main/java/com/termux/x11/CmdEntryPoint.java index bff89e316..78c930d4b 100644 --- a/app/src/main/java/com/termux/x11/CmdEntryPoint.java +++ b/app/src/main/java/com/termux/x11/CmdEntryPoint.java @@ -58,6 +58,10 @@ public class CmdEntryPoint extends ICmdEntryInterface.Stub { * You can specify DPI by using `-dpi` option. * It is Xwayland option, but termux-x11 also handles it. * + * [6] + * Start termux-x11 with TERMUX_X11_DEBUG=1 flag to redirect Termux:X11 logcat to termux-x11 stderr. + * `env TERMUX_X11_DEBUG=1 termux-x11 :0 -ac` + * * @param args The command-line arguments */ public static void main(String[] args) { @@ -162,11 +166,18 @@ public ParcelFileDescriptor getXConnection() { return fd == -1 ? null : ParcelFileDescriptor.adoptFd(fd); } + @Override + public ParcelFileDescriptor getLogcatOutput() { + int fd = stderr(); + return fd == -1 ? null : ParcelFileDescriptor.adoptFd(fd); + } + private native void spawnCompositor(int display, int dpi); public native void outputResize(int width, int height); private static native boolean socketExists(); private static native void exec(String[] argv); private static native int connect(); + private static native int stderr(); static { System.loadLibrary("lorie"); diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index b6113b455..6e057b192 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -49,6 +49,8 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindowInsetsListener, TouchParser.OnTouchParseListener { static final String REQUEST_LAUNCH_EXTERNAL_DISPLAY = "request_launch_external_display"; + public static final int KeyPress = 2; // synchronized with X.h + public static final int KeyRelease = 3; // synchronized with X.h FrameLayout frm; KeyboardUtils.KeyboardHeightProvider kbdHeightListener; @@ -110,23 +112,6 @@ protected void onCreate(Bundle savedInstanceState) { getDecorView(). setPointerIcon(PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL)); - - //if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) -// kbdHeightListener = new KeyboardUtils.KeyboardHeightProvider(this, getWindowManager(), findViewById(android.R.id.content), -// (keyboardHeight, keyboardOpen, isLandscape) -> { -// Log.e("ADDKBD", "show " + keyboardOpen + " height " + keyboardHeight + " land " + isLandscape); -// @SuppressLint("CutPasteId") -// AdditionalKeyboardView v = findViewById(R.id.additionalKbd); -// v.setVisibility(keyboardOpen?View.VISIBLE:View.INVISIBLE); -// FrameLayout.LayoutParams p = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, v.getHeight()); -// //p.gravity = Gravity.BOTTOM; -// p.setMargins(0, keyboardHeight, 0, 0); -// -// v.setLayoutParams(params); -// v.setVisibility(View.VISIBLE); -// -// }); - registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -139,6 +124,11 @@ public void onReceive(Context context, Intent intent) { CmdEntryPoint.requestConnection(); }, 0); onReceiveConnection(); + + ParcelFileDescriptor logcatOutput = service.getLogcatOutput(); + if (logcatOutput != null) { + startLogcat(logcatOutput.detachFd()); + } } catch (Exception e) { Log.e("MainActivity", "Something went wrong while we extracted connection details from binder.", e); } @@ -173,13 +163,6 @@ void onPreferencesChanged() { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); int mode = Integer.parseInt(preferences.getString("touchMode", "1")); mTP.setMode(mode); -// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) -// getWindow().getDecorView().setOnApplyWindowInsetsListener(this); - - /*if (preferences.getBoolean("showAdditionalKbd", true)) - kbd.setVisibility(View.VISIBLE); - else - kbd.setVisibility(View.INVISIBLE);*/ if (preferences.getBoolean("dexMetaKeyCapture", false)) { SamsungDexUtils.dexMetaKeyCapture(this, false); @@ -372,7 +355,7 @@ public void toggleExtraKeys() { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); if (!preferences.getBoolean("showAdditionalKbd", true)) return; - + int visibility = getTerminalToolbarViewPager().getVisibility(); int newVisibility = (visibility != View.VISIBLE) ? View.VISIBLE : View.GONE; getTerminalToolbarViewPager().setVisibility(newVisibility); @@ -380,9 +363,6 @@ public void toggleExtraKeys() { @SuppressWarnings("SameParameterValue") private class ServiceEventListener implements View.OnKeyListener { - public static final int KeyPress = 2; // synchronized with X.h - public static final int KeyRelease = 3; // synchronized with X.h - @SuppressLint({"WrongConstant", "ClickableViewAccessibility"}) private void setAsListenerTo(SurfaceView view, SurfaceView cursor) { view.setOnTouchListener((v, e) -> mTP.onTouchEvent(e)); @@ -484,11 +464,8 @@ public boolean onKey(View v, int keyCode, KeyEvent e) { * ACTION_DOWN if they come from soft keyboard. * */ - Log.e("KEY", " " + e + " " + e.isShiftPressed() + " " + e.isAltPressed() + " " + e.isCtrlPressed() + " " + e.isMetaPressed()); if (e.getDevice() == null || e.getDevice().isVirtual()) { - Log.e("KEY", "Virtual"); if (e.getAction() == KeyEvent.ACTION_UP || e.getAction() == KeyEvent.ACTION_MULTIPLE) { - Log.e("KEY", "Up or multiple"); switch (keyCode) { case KeyEvent.KEYCODE_SHIFT_LEFT: case KeyEvent.KEYCODE_SHIFT_RIGHT: @@ -498,10 +475,8 @@ public boolean onKey(View v, int keyCode, KeyEvent e) { case KeyEvent.KEYCODE_CTRL_RIGHT: case KeyEvent.KEYCODE_META_LEFT: case KeyEvent.KEYCODE_META_RIGHT: - Log.e("KEY", "meta key"); break; default: - Log.e("KEY", "other key"); onKeySym(keyCode, e.getUnicodeChar(), e.getCharacters(), e.getMetaState(), 0); } } @@ -568,6 +543,8 @@ void setCursorRect(int x, int y, int w, int h) { private native void nativeResume(); private native void nativePause(); + private native void startLogcat(int fd); + static { System.loadLibrary("lorie-client"); } 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 bf6c02f47..0002a1170 100644 --- a/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java +++ b/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java @@ -1,6 +1,8 @@ package com.termux.x11.utils; import static com.termux.shared.termux.extrakeys.ExtraKeysConstants.PRIMARY_KEY_CODES_FOR_STRINGS; +import static com.termux.x11.MainActivity.KeyPress; +import static com.termux.x11.MainActivity.KeyRelease; import android.annotation.SuppressLint; import android.content.ClipData; @@ -8,6 +10,7 @@ import android.content.Context; import android.content.Intent; import android.text.TextUtils; +import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.View; @@ -29,6 +32,9 @@ import org.json.JSONException; +import static com.termux.shared.termux.extrakeys.SpecialButtonStateChecker.isActive; +import static com.termux.shared.termux.extrakeys.SpecialButtonStateChecker.isLocked; + public class TermuxX11ExtraKeys implements ExtraKeysView.IExtraKeysView { private final View.OnKeyListener mEventListener; @@ -36,7 +42,9 @@ public class TermuxX11ExtraKeys implements ExtraKeysView.IExtraKeysView { private final ExtraKeysView mExtraKeysView; private ExtraKeysInfo mExtraKeysInfo; - private int metaAltState = 0; + private boolean ctrlDown; + private boolean altDown; + private boolean shiftDown; public TermuxX11ExtraKeys(@NonNull View.OnKeyListener eventlistener, MainActivity activity, ExtraKeysView extrakeysview) { mEventListener = eventlistener; @@ -49,6 +57,7 @@ public TermuxX11ExtraKeys(@NonNull View.OnKeyListener eventlistener, MainActivit @Override public void onExtraKeyButtonClick(View view, ExtraKeyButton buttonInfo, MaterialButton button) { + Log.e("keys", "key " + buttonInfo.getDisplay()); if (buttonInfo.isMacro()) { String[] keys = buttonInfo.getKey().split(" "); boolean ctrlDown = false; @@ -78,60 +87,37 @@ public void onExtraKeyButtonClick(View view, ExtraKeyButton buttonInfo, Material } protected void onTerminalExtraKeyButtonClick(View view, String key, boolean ctrlDown, boolean altDown, boolean shiftDown, boolean fnDown) { - if (PRIMARY_KEY_CODES_FOR_STRINGS.containsKey(key)) { - Integer keyCode = PRIMARY_KEY_CODES_FOR_STRINGS.get(key); - if (keyCode == null) return; - int metaState = 0; - - if (ctrlDown) { - metaState |= KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON; - metaAltState |= KeyEvent.KEYCODE_CTRL_LEFT | KeyEvent.KEYCODE_CTRL_RIGHT; - } + if (this.ctrlDown != ctrlDown) { + this.ctrlDown = ctrlDown; + mActivity.onKeySym(KeyEvent.KEYCODE_CTRL_LEFT, 0, null, 0, ctrlDown ? KeyPress : KeyRelease); + } - if (altDown) { - metaState |= KeyEvent.META_ALT_ON | KeyEvent.META_ALT_LEFT_ON; - metaAltState |= KeyEvent.KEYCODE_ALT_LEFT | KeyEvent.KEYCODE_ALT_RIGHT; - } + if (this.altDown != altDown) { + this.altDown = altDown; + mActivity.onKeySym(KeyEvent.KEYCODE_ALT_LEFT, 0, null, 0, altDown ? KeyPress : KeyRelease); + } - if (shiftDown) { - metaState |= KeyEvent.META_SHIFT_ON | KeyEvent.META_SHIFT_LEFT_ON; - } + if (this.shiftDown != shiftDown) { + this.shiftDown = shiftDown; + mActivity.onKeySym(KeyEvent.KEYCODE_SHIFT_LEFT, 0, null, 0, shiftDown ? KeyPress : KeyRelease); + } - if (fnDown) { - metaState |= KeyEvent.META_FUNCTION_ON; - } + if (PRIMARY_KEY_CODES_FOR_STRINGS.containsKey(key)) { + Integer keyCode = PRIMARY_KEY_CODES_FOR_STRINGS.get(key); + if (keyCode == null) return; - mEventListener.onKey(mActivity.getLorieView(), keyCode, new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, keyCode, 0, metaState)); - mEventListener.onKey(mActivity.getLorieView(), keyCode, new KeyEvent(0, 0, KeyEvent.ACTION_UP, keyCode, 0, metaState)); + mActivity.onKeySym(keyCode, 0, null, 0, 0); } else { // not a control char - key.codePoints().forEach(codePoint -> { - char[] ch; - ch = Character.toChars(codePoint); - KeyEvent[] events = mVirtualKeyboardKeyCharacterMap.getEvents(ch); - if (events != null) { - for (KeyEvent event : events) { - - int keyCode = event.getKeyCode(); - - if (metaAltState != 0) { - mEventListener.onKey(mActivity.getLorieView(), keyCode, new KeyEvent(metaAltState, keyCode)); - } - - mEventListener.onKey(mActivity.getLorieView(), keyCode, new KeyEvent(KeyEvent.ACTION_UP, keyCode)); - - if (metaAltState != 0) { - mEventListener.onKey(mActivity.getLorieView(), keyCode, new KeyEvent(metaAltState, keyCode)); - metaAltState = 0; - } - } - } - }); + key.codePoints().forEach(codePoint -> mActivity.onKeySym(0, codePoint, null, 0, 0)); } } @Override public boolean performExtraKeyButtonHapticFeedback(View view, ExtraKeyButton buttonInfo, MaterialButton button) { + Log.e("keys", "key " + buttonInfo.getKey() + " active " + isActive(mActivity.getExtraKeysView().getSpecialButtons().get(SpecialButton.valueOf(buttonInfo.getKey())))); + Log.e("keys", "key " + buttonInfo.getKey() + " locked " + isLocked(mActivity.getExtraKeysView().getSpecialButtons().get(SpecialButton.valueOf(buttonInfo.getKey())))); + return false; } From fbb95d31d57dbec922993442939c6a89bde90b4b Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 2 Mar 2023 12:02:40 +0200 Subject: [PATCH 15/21] ExtraKeys special button fix. --- .../java/com/termux/x11/MainActivity.java | 13 ++++++++++- .../termux/x11/utils/TermuxX11ExtraKeys.java | 22 +++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 6e057b192..98acab3e5 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -235,6 +235,17 @@ public void toggleX11Toolbar() { terminalToolbarViewPager.setVisibility(showNow ? View.VISIBLE : View.GONE); findViewById(R.id.terminal_toolbar_view_pager).requestFocus(); + + if (mExtraKeys == null) { + handler.postDelayed(() -> { + if (mExtraKeys != null) { + ViewGroup.LayoutParams layoutParams = terminalToolbarViewPager.getLayoutParams(); + layoutParams.height = Math.round(mTerminalToolbarDefaultHeight * + (mExtraKeys.getExtraKeysInfo() == null ? 0 : mExtraKeys.getExtraKeysInfo().getMatrix().length)); + terminalToolbarViewPager.setLayoutParams(layoutParams); + } + }, 200); + } } @@ -529,7 +540,7 @@ void setCursorRect(int x, int y, int w, int h) { }); } - static Handler handler = new Handler(); + public static Handler handler = new Handler(); private native void init(); private native void connect(int fd); 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 0002a1170..8ae3d2cf7 100644 --- a/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java +++ b/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java @@ -115,8 +115,26 @@ protected void onTerminalExtraKeyButtonClick(View view, String key, boolean ctrl @Override public boolean performExtraKeyButtonHapticFeedback(View view, ExtraKeyButton buttonInfo, MaterialButton button) { - Log.e("keys", "key " + buttonInfo.getKey() + " active " + isActive(mActivity.getExtraKeysView().getSpecialButtons().get(SpecialButton.valueOf(buttonInfo.getKey())))); - Log.e("keys", "key " + buttonInfo.getKey() + " locked " + isLocked(mActivity.getExtraKeysView().getSpecialButtons().get(SpecialButton.valueOf(buttonInfo.getKey())))); + MainActivity.handler.postDelayed(() -> { + int pressed; + switch (buttonInfo.getKey()) { + case "CTRL": + pressed = Boolean.TRUE.equals(mExtraKeysView.readSpecialButton(SpecialButton.CTRL, false)) + ? KeyPress : KeyRelease; + mActivity.onKeySym(KeyEvent.KEYCODE_CTRL_LEFT, 0, null, 0, pressed); + break; + case "ALT": + pressed = Boolean.TRUE.equals(mExtraKeysView.readSpecialButton(SpecialButton.ALT, false)) + ? KeyPress : KeyRelease; + mActivity.onKeySym(KeyEvent.KEYCODE_ALT_LEFT, 0, null, 0, pressed); + break; + case "SHIFT": + pressed = Boolean.TRUE.equals(mExtraKeysView.readSpecialButton(SpecialButton.SHIFT, false)) + ? KeyPress : KeyRelease; + mActivity.onKeySym(KeyEvent.KEYCODE_SHIFT_LEFT, 0, null, 0, pressed); + break; + } + }, 100); return false; } From 57d6938a1f9b2f5a3b3db19631d9323d8ada8912 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 2 Mar 2023 12:52:31 +0200 Subject: [PATCH 16/21] Fix for `xkbcommon: ERROR: failed to add default include path` --- app/src/main/cpp/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index b87b39553..cc36fb0d5 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -152,8 +152,8 @@ add_library(xkbcommon ${XKBCOMMON_SOURCES}) target_compile_options(xkbcommon PRIVATE "-DHAVE_STRNDUP=1" "-DDEFAULT_XKB_LAYOUT=\"us\"" "-DDEFAULT_XKB_MODEL=\"pc105\"" "-DDEFAULT_XKB_OPTIONS=NULL" "-DDEFAULT_XKB_RULES=\"evdev\"" "-DDEFAULT_XKB_VARIANT=NULL" - "-DDFLT_XKB_CONFIG_EXTRA_PATH=\"/data/data/com.termux.x11/files\"" - "-DDFLT_XKB_CONFIG_ROOT=\"/data/data/com.termux.x11/files\"") + "-DDFLT_XKB_CONFIG_EXTRA_PATH=\"/data/data/com.termux.x11\"" + "-DDFLT_XKB_CONFIG_ROOT=\"/data/data/com.termux.x11\"") target_include_directories(xkbcommon PRIVATE "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/libxkbcommon/src") target_include_directories(xkbcommon PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/libxkbcommon/include") target_link_libraries(xkbcommon xcbproto) From a49389c65920fdb65338700a3059650cdaf019de Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 2 Mar 2023 14:32:04 +0200 Subject: [PATCH 17/21] Fixing extra key appearing after preferences changed. Updatig documentation of CmdEntryPoint. --- app/src/main/AndroidManifest.xml | 8 +++- .../extrakeys/SpecialButtonStateChecker.java | 14 ------ .../java/com/termux/x11/CmdEntryPoint.java | 8 +++- .../java/com/termux/x11/LoriePreferences.java | 6 +++ .../java/com/termux/x11/MainActivity.java | 46 +++++++++---------- .../termux/x11/utils/TermuxX11ExtraKeys.java | 3 -- 6 files changed, 40 insertions(+), 45 deletions(-) delete mode 100644 app/src/main/java/com/termux/shared/termux/extrakeys/SpecialButtonStateChecker.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8d836b139..5248e55c2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,7 +20,9 @@ android:supportsRtl="true" android:theme="@style/AppTheme" tools:ignore="GoogleAppIndexingWarning"> - @@ -49,6 +52,7 @@ diff --git a/app/src/main/java/com/termux/shared/termux/extrakeys/SpecialButtonStateChecker.java b/app/src/main/java/com/termux/shared/termux/extrakeys/SpecialButtonStateChecker.java deleted file mode 100644 index c358a1f79..000000000 --- a/app/src/main/java/com/termux/shared/termux/extrakeys/SpecialButtonStateChecker.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.termux.shared.termux.extrakeys; - -public class SpecialButtonStateChecker { - public static boolean isActive(SpecialButtonState state) { - if (state == null) - return false; - return state.isActive; - } - public static boolean isLocked(SpecialButtonState state) { - if (state == null) - return false; - return state.isLocked; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/termux/x11/CmdEntryPoint.java b/app/src/main/java/com/termux/x11/CmdEntryPoint.java index 78c930d4b..76e4763eb 100644 --- a/app/src/main/java/com/termux/x11/CmdEntryPoint.java +++ b/app/src/main/java/com/termux/x11/CmdEntryPoint.java @@ -47,12 +47,16 @@ public class CmdEntryPoint extends ICmdEntryInterface.Stub { * [3] * Also you should set TMPDIR environment variable in the case if you are running Xwayland in * non-termux environment. That is the only way to localize X connection socket. - * TMPDIR should be accessible by termux-x11 + * TMPDIR should be accessible by termux-x11. * * [4] - * You can run `termux-x11 --no-xwayland-start` to spawn compositor, but not to spawn Termux's + * You can run `termux-x11 :0 --no-xwayland-start` to spawn compositor, but not to spawn Termux's * Xwayland in the case you want to use Xwayland contained in chroot environment. * Do not forget about [2] to avoid application crashes. + * Also you must specify the same display number you will set to Xwayland. + * You must start `termux-x11` as root if you want to make it work with chroot'd Xwayland. + * `root@termux: ~# WAYLAND_DISPLAY="termux-x11" XDG_RUNTIME_DIR=//var/run/1000/ TMDIR=//tmp/ termux-x11 :0 --no-xwayland-start` + * `root@chroot: ~# WAYLAND_DISPLAY="termux-x11" XDG_RUNTIME_DIR=//var/run/1000/ TMDIR=//tmp/ Xwayland :0 -ac` * * [5] * You can specify DPI by using `-dpi` option. diff --git a/app/src/main/java/com/termux/x11/LoriePreferences.java b/app/src/main/java/com/termux/x11/LoriePreferences.java index cdc677d15..4e8a78ebb 100644 --- a/app/src/main/java/com/termux/x11/LoriePreferences.java +++ b/app/src/main/java/com/termux/x11/LoriePreferences.java @@ -1,5 +1,6 @@ package com.termux.x11; +import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.Preference; @@ -14,6 +15,7 @@ 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; @@ -93,6 +95,10 @@ public boolean onPreferenceChange(Preference preference, Object newValue) { return false; } } + + Intent intent = new Intent(ACTION_PREFERENCES_CHANGED); + intent.setPackage("com.termux.x11"); + getContext().sendBroadcast(intent); return true; } } diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 98acab3e5..38dff4097 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -2,6 +2,7 @@ import static com.termux.x11.CmdEntryPoint.ACTION_START; import static com.termux.x11.CmdEntryPoint.requestConnection; +import static com.termux.x11.LoriePreferences.ACTION_PREFERENCES_CHANGED; import android.annotation.SuppressLint; import android.app.Activity; @@ -57,7 +58,6 @@ public class MainActivity extends AppCompatActivity implements View.OnApplyWindo private TouchParser mTP; private final ServiceEventListener listener = new ServiceEventListener(); private ICmdEntryInterface service = null; - private int mTerminalToolbarDefaultHeight; public TermuxX11ExtraKeys mExtraKeys; private ExtraKeysView mExtraKeysView; @@ -105,13 +105,15 @@ protected void onCreate(Bundle savedInstanceState) { mTP = new TouchParser(lorieView, this); setTerminalToolbarView(); - toggleX11Toolbar(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) getWindow(). getDecorView(). setPointerIcon(PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL)); + IntentFilter filter = new IntentFilter(ACTION_START); + filter.addAction(ACTION_PREFERENCES_CHANGED); + registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -132,9 +134,11 @@ public void onReceive(Context context, Intent intent) { } catch (Exception e) { Log.e("MainActivity", "Something went wrong while we extracted connection details from binder.", e); } + } else if (ACTION_PREFERENCES_CHANGED.equals(intent.getAction())) { + onPreferencesChanged(); } } - }, new IntentFilter(ACTION_START)); + }, filter); requestConnection(); @@ -167,6 +171,8 @@ void onPreferencesChanged() { if (preferences.getBoolean("dexMetaKeyCapture", false)) { SamsungDexUtils.dexMetaKeyCapture(this, false); } + + setTerminalToolbarView(); } @Override @@ -215,20 +221,11 @@ public ViewPager getTerminalToolbarViewPager() { return findViewById(R.id.terminal_toolbar_view_pager); } - private void setTerminalToolbarView() { final ViewPager terminalToolbarViewPager = getTerminalToolbarViewPager(); - ViewGroup.LayoutParams layoutParams = terminalToolbarViewPager.getLayoutParams(); - mTerminalToolbarDefaultHeight = layoutParams.height; - terminalToolbarViewPager.setAdapter(new X11ToolbarViewPager.PageAdapter(this, listener)); terminalToolbarViewPager.addOnPageChangeListener(new X11ToolbarViewPager.OnPageChangeListener(this, terminalToolbarViewPager)); - } - - public void toggleX11Toolbar() { - final ViewPager terminalToolbarViewPager = getTerminalToolbarViewPager(); - if (terminalToolbarViewPager == null) return; SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); boolean showNow = preferences.getBoolean("showAdditionalKbd", true); @@ -240,7 +237,7 @@ public void toggleX11Toolbar() { handler.postDelayed(() -> { if (mExtraKeys != null) { ViewGroup.LayoutParams layoutParams = terminalToolbarViewPager.getLayoutParams(); - layoutParams.height = Math.round(mTerminalToolbarDefaultHeight * + layoutParams.height = Math.round(layoutParams.height * (mExtraKeys.getExtraKeysInfo() == null ? 0 : mExtraKeys.getExtraKeysInfo().getMatrix().length)); terminalToolbarViewPager.setLayoutParams(layoutParams); } @@ -248,6 +245,17 @@ public void toggleX11Toolbar() { } } + @Override + public void toggleExtraKeys() { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + if (!preferences.getBoolean("showAdditionalKbd", true)) + return; + + int visibility = getTerminalToolbarViewPager().getVisibility(); + int newVisibility = (visibility != View.VISIBLE) ? View.VISIBLE : View.GONE; + getTerminalToolbarViewPager().setVisibility(newVisibility); + } + private boolean didRequestLaunchExternalDisplay() { return getIntent().getBooleanExtra(REQUEST_LAUNCH_EXTERNAL_DISPLAY, false); @@ -280,6 +288,7 @@ public void onConfigurationChanged(@NonNull Configuration newConfig) { } orientation = newConfig.orientation; + setTerminalToolbarView(); } @Override @@ -361,17 +370,6 @@ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { return insets; } - @Override - public void toggleExtraKeys() { - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); - if (!preferences.getBoolean("showAdditionalKbd", true)) - return; - - int visibility = getTerminalToolbarViewPager().getVisibility(); - int newVisibility = (visibility != View.VISIBLE) ? View.VISIBLE : View.GONE; - getTerminalToolbarViewPager().setVisibility(newVisibility); - } - @SuppressWarnings("SameParameterValue") private class ServiceEventListener implements View.OnKeyListener { @SuppressLint({"WrongConstant", "ClickableViewAccessibility"}) 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 8ae3d2cf7..74c648ab9 100644 --- a/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java +++ b/app/src/main/java/com/termux/x11/utils/TermuxX11ExtraKeys.java @@ -32,9 +32,6 @@ import org.json.JSONException; -import static com.termux.shared.termux.extrakeys.SpecialButtonStateChecker.isActive; -import static com.termux.shared.termux.extrakeys.SpecialButtonStateChecker.isLocked; - public class TermuxX11ExtraKeys implements ExtraKeysView.IExtraKeysView { private final View.OnKeyListener mEventListener; From 401b4a4ae298557668273ba71081b9ca82edd5c9 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 2 Mar 2023 15:27:38 +0200 Subject: [PATCH 18/21] Scrolling support --- app/src/main/cpp/lorie-client/lorie-client.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/cpp/lorie-client/lorie-client.cpp b/app/src/main/cpp/lorie-client/lorie-client.cpp index 32944d9f8..75e92f77f 100644 --- a/app/src/main/cpp/lorie-client/lorie-client.cpp +++ b/app/src/main/cpp/lorie-client/lorie-client.cpp @@ -458,7 +458,19 @@ JNIEXPORT void JNICALL Java_com_termux_x11_MainActivity_onPointerScroll([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject thiz, [[maybe_unused]] jint axis, [[maybe_unused]] jfloat value) { // TODO: implement onPointerScroll() - // How to send pointer scroll with XTEST? + if (client.c.conn) { + bool press_shift = axis == 1; // AXIS_X + int button = value < 0 ? XCB_BUTTON_INDEX_4 : XCB_BUTTON_INDEX_5; + xcb_screen_t *s = xcb_setup_roots_iterator(xcb_get_setup(client.c.conn)).data; + if (press_shift) + client.send_key(XK_Shift_L, XCB_KEY_PRESS); + client.post([=] { + xcb_test_fake_input(client.c.conn, XCB_BUTTON_PRESS, button, XCB_CURRENT_TIME, s->root, 0, 0, 0); + xcb_test_fake_input(client.c.conn, XCB_BUTTON_RELEASE, button, XCB_CURRENT_TIME, s->root, 0, 0, 0); + }); + if (press_shift) + client.send_key(XK_Shift_L, XCB_KEY_RELEASE); + } } extern "C" From 504bd20f147f7333b52a5e2d8a64980c29c851af Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Thu, 2 Mar 2023 16:43:51 +0200 Subject: [PATCH 19/21] Fix appearing ExtraKeys in PiP mode. --- .../main/cpp/lorie-client/xcb-connection.hpp | 2 +- .../java/com/termux/x11/MainActivity.java | 23 +++++++------------ 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/app/src/main/cpp/lorie-client/xcb-connection.hpp b/app/src/main/cpp/lorie-client/xcb-connection.hpp index 0333e9dd8..b9dd77731 100644 --- a/app/src/main/cpp/lorie-client/xcb-connection.hpp +++ b/app/src/main/cpp/lorie-client/xcb-connection.hpp @@ -517,4 +517,4 @@ class xcb_connection { fixes.init(); xkb.init(); } -}; \ No newline at end of file +}; diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 38dff4097..8b3e0f57d 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -256,7 +256,6 @@ public void toggleExtraKeys() { getTerminalToolbarViewPager().setVisibility(newVisibility); } - private boolean didRequestLaunchExternalDisplay() { return getIntent().getBooleanExtra(REQUEST_LAUNCH_EXTERNAL_DISPLAY, false); } @@ -319,22 +318,16 @@ public void onUserLeaveHint() { @Override public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, @NonNull Configuration newConfig) { - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + ViewPager pager = getTerminalToolbarViewPager(); + ViewGroup parent = (ViewGroup) pager.getParent(); if (isInPictureInPictureMode) { -// if (kbd.getVisibility() != View.INVISIBLE) -// kbd.setVisibility(View.INVISIBLE); - frm.setPadding(0, 0, 0, 0); - } // else { -// if (kbd.getVisibility() != View.VISIBLE) -// if (preferences.getBoolean("showAdditionalKbd", true)) { -// kbd.setVisibility(View.VISIBLE); -// int paddingDp = 35; -// float density = this.getResources().getDisplayMetrics().density; -// int paddingPixel = (int) (paddingDp * density); -// frm.setPadding(0, 0, 0, paddingPixel); -// } -// } + parent.removeView(pager); + parent.addView(pager, 0); + } else + pager.bringToFront(); + + frm.setPadding(0, 0, 0, 0); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); } From 877df08b6b729ebaa4bad1b41ffea2a63209d58b Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Fri, 3 Mar 2023 01:18:37 +0200 Subject: [PATCH 20/21] Trying to fix root issues... --- app/src/main/cpp/lorie/compositor.cpp | 6 ++++++ app/src/main/java/com/termux/x11/CmdEntryPoint.java | 7 +++++++ app/src/main/res/values/strings.xml | 2 +- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/app/src/main/cpp/lorie/compositor.cpp b/app/src/main/cpp/lorie/compositor.cpp index cd1f5716f..9a82d321b 100644 --- a/app/src/main/cpp/lorie/compositor.cpp +++ b/app/src/main/cpp/lorie/compositor.cpp @@ -370,4 +370,10 @@ Java_com_termux_x11_CmdEntryPoint_stderr(JNIEnv *env, jclass clazz) { } return -1; +} + +extern "C" +JNIEXPORT jint JNICALL +Java_com_termux_x11_CmdEntryPoint_getuid(JNIEnv *env, jclass clazz) { + return getuid(); } \ No newline at end of file diff --git a/app/src/main/java/com/termux/x11/CmdEntryPoint.java b/app/src/main/java/com/termux/x11/CmdEntryPoint.java index 76e4763eb..31ec12b3d 100644 --- a/app/src/main/java/com/termux/x11/CmdEntryPoint.java +++ b/app/src/main/java/com/termux/x11/CmdEntryPoint.java @@ -109,6 +109,7 @@ public static void main(String[] args) { } } + @SuppressLint("WrongConstant") void sendBroadcast() { // We should not care about multiple instances, it should be called only by `Termux:X11` app // which is single instance... @@ -118,6 +119,11 @@ void sendBroadcast() { Intent intent = new Intent(ACTION_START); intent.putExtra("", bundle); intent.setPackage("com.termux.x11"); + + if (getuid() == 0 || getuid() == 2000) { + intent.setFlags(/* FLAG_RECEIVER_FROM_SHELL */ 0x00400000); + } + ctx.sendBroadcast(intent); } @@ -182,6 +188,7 @@ public ParcelFileDescriptor getLogcatOutput() { private static native void exec(String[] argv); private static native int connect(); private static native int stderr(); + private static native int getuid(); static { System.loadLibrary("lorie"); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ddfc11c20..9a2e7e3c0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -33,7 +33,7 @@ Already Granted Already Disabled - Not conected + Not connected Getting started Preferences From a0d9d1a5e67bef055c6ef16449a6fc643295d852 Mon Sep 17 00:00:00 2001 From: Twaik Yont Date: Fri, 3 Mar 2023 02:23:20 +0200 Subject: [PATCH 21/21] Fixing root starting issues --- .../main/cpp/lorie-client/lorie-client.cpp | 52 ++++++++++++------- .../main/cpp/lorie-client/xcb-connection.hpp | 2 +- .../java/com/termux/x11/CmdEntryPoint.java | 2 +- .../java/com/termux/x11/MainActivity.java | 2 + 4 files changed, 36 insertions(+), 22 deletions(-) diff --git a/app/src/main/cpp/lorie-client/lorie-client.cpp b/app/src/main/cpp/lorie-client/lorie-client.cpp index 75e92f77f..2ba53e9ee 100644 --- a/app/src/main/cpp/lorie-client/lorie-client.cpp +++ b/app/src/main/cpp/lorie-client/lorie-client.cpp @@ -198,30 +198,42 @@ class lorie_client { ALOGE("Map failed: %s", strerror(errno)); } } + try { + c.shm.attach_fd(screen.shmseg, screen.shmfd, 0); + return; + } catch (std::runtime_error &e) { + ALOGE("%s", e.what()); + if (screen.shmaddr && screen.shmaddr != MAP_FAILED) + munmap(screen.shmaddr, DEFAULT_SHMSEG_LENGTH); + if (screen.shmfd != -1) + close(screen.shmfd); + + ALOGE("Trying to retrieve shared segment..."); + } // There is a chance that Xwayland we are trying to connect is non-termux, try another way + screen.shmfd = c.shm.create_segment(screen.shmseg, DEFAULT_SHMSEG_LENGTH, 0); if (screen.shmfd < 0) { - screen.shmfd = c.shm.create_segment(screen.shmseg, DEFAULT_SHMSEG_LENGTH, 0); - if (screen.shmfd < 0) - ALOGE("Failed to retrieve shared segment file descriptor..."); - else { - struct statfs info{}; - fstatfs(screen.shmfd, &info); - if (info.f_type != TMPFS_MAGIC) { - ALOGE("Shared segment is not hosted on tmpfs. You are likely doing something wrong. " - "Check if /run/shm, /var/tmp and /tmp are tmpfs."); - } else { - ALOGE("Attaching shared memory segment..."); - screen.shmaddr = static_cast(mmap(nullptr, DEFAULT_SHMSEG_LENGTH, - PROT_READ | PROT_WRITE, - MAP_SHARED, screen.shmfd, 0)); - if (screen.shmaddr == MAP_FAILED) { - ALOGE("Map failed: %s", strerror(errno)); - } + ALOGE("Failed to retrieve shared segment file descriptor..."); + return; + } else { + struct statfs info{}; + fstatfs(screen.shmfd, &info); + if (info.f_type != TMPFS_MAGIC) { + ALOGE("Shared segment is not hosted on tmpfs. You are likely doing something wrong. " + "Check if /run/shm, /var/tmp and /tmp are tmpfs."); + close(screen.shmfd); + screen.shmfd = -1; + } else { + ALOGE("Attaching shared memory segment..."); + screen.shmaddr = static_cast(mmap(nullptr, DEFAULT_SHMSEG_LENGTH, + PROT_READ | PROT_WRITE, + MAP_SHARED, screen.shmfd, 0)); + if (screen.shmaddr == MAP_FAILED) { + ALOGE("Map failed: %s", strerror(errno)); } } } - c.shm.attach_fd(screen.shmseg, screen.shmfd, 0); } void adopt_connection_fd(int fd) { @@ -538,7 +550,7 @@ Java_com_termux_x11_MainActivity_nativePause([[maybe_unused]] JNIEnv *env, [[may // I need this to catch initialisation errors of libxkbcommon. #if 1 // It is needed to redirect stderr to logcat -[[maybe_unused]] std::thread stderr_to_logcat_thread([]{ +[[maybe_unused]] std::thread stderr_to_logcat_thread([]{ // NOLINT(cert-err58-cpp) FILE *fp; int p[2]; size_t len; @@ -600,7 +612,7 @@ void fork(const std::function& f) { extern "C" JNIEXPORT void JNICALL -Java_com_termux_x11_MainActivity_startLogcat(JNIEnv *env, jobject thiz, jint fd) { +Java_com_termux_x11_MainActivity_startLogcat([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject thiz, jint fd) { killAllLogcats(); ALOGV("Starting logcat with output to given fd"); diff --git a/app/src/main/cpp/lorie-client/xcb-connection.hpp b/app/src/main/cpp/lorie-client/xcb-connection.hpp index b9dd77731..27826e2ba 100644 --- a/app/src/main/cpp/lorie-client/xcb-connection.hpp +++ b/app/src/main/cpp/lorie-client/xcb-connection.hpp @@ -92,7 +92,7 @@ class xcb_connection { }; void attach_fd(u32 seg, i32 fd, u8 ro) { - xcb_check(shm_attach_fd, seg, fd, ro); + xcb_check(shm_attach_fd_checked, seg, fd, ro); self.handle_error("Error attaching file descriptor through MIT-SHM extension"); }; diff --git a/app/src/main/java/com/termux/x11/CmdEntryPoint.java b/app/src/main/java/com/termux/x11/CmdEntryPoint.java index 31ec12b3d..bcdfa846c 100644 --- a/app/src/main/java/com/termux/x11/CmdEntryPoint.java +++ b/app/src/main/java/com/termux/x11/CmdEntryPoint.java @@ -119,7 +119,7 @@ void sendBroadcast() { Intent intent = new Intent(ACTION_START); intent.putExtra("", bundle); intent.setPackage("com.termux.x11"); - + if (getuid() == 0 || getuid() == 2000) { intent.setFlags(/* FLAG_RECEIVER_FROM_SHELL */ 0x00400000); } diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 8b3e0f57d..596126e2c 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -119,6 +119,7 @@ protected void onCreate(Bundle savedInstanceState) { public void onReceive(Context context, Intent intent) { if (ACTION_START.equals(intent.getAction())) { try { + Log.v("LorieBroadcastReceiver", "Got new ACTION_START intent"); IBinder b = intent.getBundleExtra("").getBinder(""); service = ICmdEntryInterface.Stub.asInterface(b); service.asBinder().linkToDeath(() -> { @@ -150,6 +151,7 @@ public void onReceive(Context context, Intent intent) { void onReceiveConnection() { try { if (service != null && service.asBinder().isBinderAlive()) { + Log.v("LorieBroadcastReceiver", "trying to extract X connection socket."); ParcelFileDescriptor fd = service.getXConnection(); if (fd != null) { connect(fd.detachFd());