手機 - Zinwa Q25 - 如何修改Termux-X11按鍵



參考資訊:
https://github.com/steward-fu/website/releases/tag/q25

termux-x11_mod_key_commit-6d62752.diff

diff --git a/app/src/main/cpp/lorie/activity.c b/app/src/main/cpp/lorie/activity.c
index 8a51c11..a809984 100644
--- a/app/src/main/cpp/lorie/activity.c
+++ b/app/src/main/cpp/lorie/activity.c
@@ -22,6 +22,11 @@
 #pragma ide diagnostic ignored "ConstantFunctionResult"
 #define log(prio, ...) __android_log_print(ANDROID_LOG_ ## prio, "LorieNative", __VA_ARGS__)
 
+static struct {
+    jfloat x;
+    jfloat y;
+} mouse;
+
 extern volatile int conn_fd; // The only variable from shared with X server code.
 
 static struct {
@@ -294,11 +299,16 @@ static void sendWindowChange(__unused JNIEnv* env, __unused jobject cls, jint wi
     }
 }
 
+static int last_code[2] = { 0 };
 static void sendMouseEvent(__unused JNIEnv* env, __unused jobject cls, jfloat x, jfloat y, jint which_button, jboolean button_down, jboolean relative) {
     if (conn_fd != -1) {
         if (which_button > 0)
             (*env)->CallVoidMethod(env, globalThiz, MainActivity.resetIme);
         lorieEvent e = { .mouse = { .t = EVENT_MOUSE, .x = x, .y = y, .detail = which_button, .down = button_down, .relative = relative } };
+
+        mouse.x = x;
+        mouse.y = y;
+        //log(DEBUG, "Steward, mouse: %d (x:%f, y:%f, down:%d, relative:%d)", which_button, x, y, button_down, relative);
         write(conn_fd, &e, sizeof(e));
     }
 }
@@ -306,6 +316,7 @@ static void sendMouseEvent(__unused JNIEnv* env, __unused jobject cls, jfloat x,
 static void sendTouchEvent(__unused JNIEnv* env, __unused jobject cls, jint action, jint id, jint x, jint y) {
     if (conn_fd != -1 && action != -1) {
         lorieEvent e = { .touch = { .t = EVENT_TOUCH, .type = action, .id = id, .x = x, .y = y } };
+        log(DEBUG, "Sending touch: %d (x:%d, y:%d, id:%d)", action, x, y, id);
         write(conn_fd, &e, sizeof(e));
     }
 }
@@ -327,12 +338,344 @@ static void requestStylusEnabled(__unused JNIEnv *env, __unused jclass clazz, jb
     }
 }
 
+static jboolean sendMySpecialKey(jint s, jint code, jboolean key_down)
+{
+    lorieEvent t = { .key = { .t = EVENT_KEY, .key = code + 8, .state = key_down } };
+
+    //log(DEBUG, "Steward, special, %d, %d, %d", s, code, key_down);
+    if (key_down) {
+        t.key.key = s + 8;
+        t.key.state = 1;
+        t.key.t = EVENT_KEY;
+        write(conn_fd, &t, sizeof(t));
+
+        t.key.key = code + 8;
+        write(conn_fd, &t, sizeof(t));
+
+        last_code[0] = s;
+        last_code[1] = code;
+    }
+    else {
+        t.key.key = code + 8;
+        t.key.state = 0;
+        t.key.t = EVENT_KEY;
+        write(conn_fd, &t, sizeof(t));
+
+        t.key.key = s + 8;
+        t.key.state = 0;
+        write(conn_fd, &t, sizeof(t));
+    }
+
+    return true;
+}
+
 static jboolean sendKeyEvent(__unused JNIEnv* env, __unused jobject cls, jint scan_code, jint key_code, jboolean key_down) {
     if (conn_fd != -1) {
+        #define MOUSE_MOV_3 3
+        #define MOUSE_MOV_5 5
+        static int alt_press = 0;
+        static int need_release = 0;
+        static int iso3_press = 0;
+        static int ctrl_press = 0;
+        static int shift_press = 0;
+
         int code = (scan_code) ?: android_to_linux_keycode[key_code];
-        log(DEBUG, "Sending key: %d (%d %d %d)", code + 8, scan_code, key_code, key_down);
-        lorieEvent e = { .key = { .t = EVENT_KEY, .key = code + 8, .state = key_down } };
-        write(conn_fd, &e, sizeof(e));
+        lorieEvent em = {
+            .mouse = {
+                .t = EVENT_MOUSE,
+                .x = 0,
+                .y = 0,
+                .detail = 3,
+                .down = !!key_down,
+                .relative = 1
+            }
+        };
+        //log(DEBUG, "Steward, original key: %d (%d %d %d)", code + 8, scan_code, key_code, key_down);
+
+        // ATL or ISO3
+        if ((code + 8 != 64) && (code + 8 != 62))   {
+            last_code[0] = 0;
+            last_code[1] = code;
+        }
+
+        switch (code + 8)   {
+        case 111: // Up
+            break;
+        case 116: // Down
+            break;
+        case 113: // Left
+            break;
+        case 114: // Right
+            break;
+        case 588: // APPSwitch
+            last_code[1] = code = (alt_press || iso3_press) ? KEY_PAGEUP : KEY_UP;
+            break;
+        case 166: // Back
+            last_code[1] = code = (alt_press || iso3_press) ? KEY_PAGEDOWN : KEY_DOWN;
+            break;
+        case 239: // Call
+            last_code[1] = code = (alt_press || iso3_press) ? KEY_HOME : KEY_LEFT;
+            break;
+        case 180: // EndCall
+            last_code[1] = code = (alt_press || iso3_press) ? KEY_END : KEY_RIGHT;
+            break;
+        case 64: // ALT
+            alt_press = !!key_down;
+
+            if (iso3_press) {
+                return sendMySpecialKey(KEY_LEFTALT, KEY_TAB, key_down);
+            }
+
+            if (alt_press == 0) {
+                lorieEvent t = { .key = { .t = EVENT_KEY, .key = 0, .state = 0 } };
+
+                //log(DEBUG, "Steward, alt, %d, %d", last_code[0], last_code[1]);
+                need_release = 0;
+                t.key.key = last_code[0] + 8;
+                write(conn_fd, &t, sizeof(t));
+
+                t.key.key = last_code[1] + 8;
+                write(conn_fd, &t, sizeof(t));
+            }
+            return true;
+        case 50: // LSHIFT
+            shift_press = !!key_down;
+            last_code[1] = code = KEY_LEFTSHIFT;
+            break;
+        case 108: // SYM
+            ctrl_press = !!key_down;
+            last_code[1] = code = KEY_RIGHTCTRL;
+            break;
+        case 62: // RSHIFT
+            iso3_press = !!key_down;
+
+            if (iso3_press == 0) {
+                lorieEvent t = { .key = { .t = EVENT_KEY, .key = 0, .state = 0 } };
+
+                //log(DEBUG, "Steward, iso3, %d, %d", last_code[0], last_code[1]);
+                need_release = 0;
+                t.key.key = last_code[0] + 8;
+                write(conn_fd, &t, sizeof(t));
+
+                t.key.key = last_code[1] + 8;
+                write(conn_fd, &t, sizeof(t));
+            }
+            return true;
+        case 24: // Q
+            if (alt_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_3, key_down);
+            }
+            break;
+        case 25: // W
+            if (alt_press) {
+                last_code[1] = code = KEY_1;
+            }
+            if (iso3_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_BACKSLASH, key_down);
+            }
+            break;
+        case 26: // E
+            if (alt_press) {
+                last_code[1] = code = KEY_2;
+            }
+            if (iso3_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_5, key_down);
+            }
+            break;
+        case 27: // R
+            if (alt_press) {
+                last_code[1] = code = KEY_3;
+            }
+            if (iso3_press) {
+                last_code[1] = code = KEY_BACKSLASH;
+            }
+            break;
+        case 28: // T
+            if (alt_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_9, key_down);
+            }
+            if (iso3_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_LEFTBRACE, key_down);
+            }
+            break;
+        case 29: // Y
+            if (alt_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_0, key_down);
+            }
+            if (iso3_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_RIGHTBRACE, key_down);
+            }
+            break;
+        case 30: // U
+            if (alt_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_MINUS, key_down);
+            }
+            if (iso3_press) {
+                last_code[1] = code = KEY_EQUAL;
+            }
+            break;
+        case 31: // I
+            if (alt_press) {
+                last_code[1] = code = KEY_MINUS;
+            }
+            if (iso3_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_GRAVE, key_down);
+            }
+            break;
+        case 32: // O
+            if (alt_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_EQUAL, key_down);
+            }
+            break;
+        case 33: // P
+            if (alt_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_2, key_down);
+            }
+            break;
+        case 38: // A
+            if (alt_press || iso3_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_8, key_down);
+            }
+            break;
+        case 39: // S
+            if (alt_press) {
+                last_code[1] = code = KEY_4;
+            }
+            break;
+        case 40: // D
+            if (alt_press) {
+                last_code[1] = code = KEY_5;
+            }
+            break;
+        case 41: // F
+            if (alt_press) {
+                last_code[1] = code = KEY_6;
+            }
+            break;
+        case 42: // G
+            if (alt_press) {
+                last_code[1] = code = KEY_SLASH;
+            }
+            if (iso3_press) {
+                last_code[1] = code = KEY_LEFTBRACE;
+            }
+            break;
+        case 43: // H
+            if (alt_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_SEMICOLON, key_down);
+            }
+            if (iso3_press) {
+                last_code[1] = code = KEY_RIGHTBRACE;
+            }
+            break;
+        case 44: // J
+            if (alt_press) {
+                last_code[1] = code = KEY_SEMICOLON;
+            }
+            break;
+        case 45: // K
+            if (alt_press) {
+                last_code[1] = code = KEY_APOSTROPHE;
+            }
+            if (iso3_press) {
+                last_code[1] = code = KEY_GRAVE;
+            }
+            break;
+        case 46: // L
+            if (alt_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_APOSTROPHE, key_down);
+            }
+            break;
+        case 22: // BACKSPACE
+            if (alt_press) {
+                last_code[1] = code = KEY_TAB;
+            }
+            break;
+        case 52: // Z
+            if (alt_press || iso3_press) {
+                last_code[1] = code = KEY_7;
+            }
+            break;
+        case 53: // X
+            if (alt_press) {
+                last_code[1] = code = KEY_8;
+            }
+            if (iso3_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_6, key_down);
+            }
+            break;
+        case 54: // C
+            if (alt_press) {
+                last_code[1] = code = KEY_9;
+            }
+            if (iso3_press) {
+                last_code[1] = code = KEY_F9;
+            }
+            break;
+        case 55: // V
+            if (alt_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_SLASH, key_down);
+            }
+            if (iso3_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_COMMA, key_down);
+            }
+            break;
+        case 56: // B
+            if (alt_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_1, key_down);
+            }
+            if (iso3_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_DOT, key_down);
+            }
+            break;
+        case 57: // N
+            if (alt_press) {
+                last_code[1] = code = KEY_COMMA;
+            }
+            break;
+        case 58: // M
+            if (alt_press) {
+                last_code[1] = code = KEY_DOT;
+            }
+            break;
+        case 49: // $
+            if (alt_press) {
+                return sendMySpecialKey(KEY_LEFTSHIFT, KEY_7, key_down);
+            }
+
+            return sendMySpecialKey(KEY_LEFTSHIFT, KEY_4, key_down);
+        case 36: // Enter
+            if (alt_press) {
+                last_code[1] = code = KEY_ESC;
+            }
+            break;
+        case 19: // 0
+            if (alt_press) {
+                last_code[1] = code = KEY_0;
+            }
+            else {
+                em.mouse.t = EVENT_MOUSE;
+                em.mouse.x = 0;
+                em.mouse.y = 0;
+                em.mouse.detail = 1;
+                em.mouse.down = !!key_down;
+                em.mouse.relative = 1;
+                write(conn_fd, &em, sizeof(em));
+                return true;
+            }
+            break;
+        case 65: // Space
+            last_code[1] = code = KEY_SPACE;
+
+            if (iso3_press) {
+                return sendMySpecialKey(KEY_LEFTALT, KEY_SPACE, key_down);
+            }
+            break;
+        }
+
+        //log(DEBUG, "Steward, %d, %d(%d), %d", last_code[0], last_code[1], code, key_down);
+        lorieEvent ek = { .key = { .t = EVENT_KEY, .key = code + 8, .state = key_down } };
+        write(conn_fd, &ek, sizeof(ek));
     }
 
     return true;

編譯步驟:

$ cd
$ wget https://download.oracle.com/java/24/latest/jdk-24_linux-x64_bin.tar.gz
$ tar xvf jdk-24_linux-x64_bin.tar.gz -C ~/
$ export JAVA_HOME=~/jdk-24.0.1
$ export CLASSPATH=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar
$ export PATH=$JAVA_HOME/bin:$PATH

$ cd
$ wget https://dl.google.com/dl/android/studio/ide-zips/2025.2.2.8/android-studio-2025.2.2.8-linux.tar.gz
$ tar xvf android-studio-2025.2.2.8-linux.tar.gz
$ cd android-studio
$ wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
$ unzip commandlinetools-linux-11076708_latest.zip
$ mv cmdline-tools/ latest
$ mkdir cmdline-tools
$ mv latest/ cmdline-tools/
$ yes | ./cmdline-tools/latest/bin/sdkmanager --licenses
$ export ANDROID_HOME=`pwd`/android-studio/

$ cd
$ git clone --recurse-submodules https://github.com/termux/termux-x11 
$ cd termux-x11
$ git checkout 6d62752
$ wget https://github.com/steward-fu/website/releases/download/q25/termux-x11_mod_key_commit-6d62752.diff
$ git apply termux-x11_mod_key_commit-6d62752.diff
$ ./gradlew build

P.S. 編譯後,安裝app-arm64-v8a-debug.apk即可

安裝後,修改如下設定:

Settings => Accessibility => Termux:X11 KeyInterceptor => On
Termux-X11 => Keyboard => Enforce char based input => On
Termux-X11 => Keyboard => Prefer scancodes when possible => On
Q25Edge Trackpad => Mouse (Gesuture) mode

Q25按鍵Debian按鍵+MYALT+MYISO3
CallLEFTHOMEHOME
AppSwitchUPPAGEUPPAGEUP
BackDOWNPAGEDOWNPAGEDOWN
EndCallRIGHTENDEND
TrackballMouseMouse Right Button
Qq#
Ww1|
Ee2%
Rr3\
Tt({
Yy)}
Uu_~
Ii-=
Oo+
Pp@
Aa**
Ss4
Dd5
Ff6
Gg/[
Hh:]
Jj;
Kk'`
Ll"
BackspaceBackspaceTab
altMYALTAlt+Tab
Zz77
Xx8^
Cc9F9
Vv?
Bb!
Nn,
Mm.
$$&
EnterEnterEscape
ShiftShift
0Mouse Left Button0
SpaceSpaceAlt+Space
symCtrl
ShiftMYISO3