On Fri Jun 19 10:03:12 2026 +0000, Matteo Bruni wrote:
Pushed a new version that takes care of TightVNC's era Xvnc keycode -> scancode mapping. There's probably room for further cleanup and simplification, but I thought it might be better to show where I'm currently at early rather than late. Basically I'm keeping only the old vnc mapping tables (the other ones we had are effectively an approximation of the fixed mapping we're using starting from 863836da) with some limited dynamic lookup for "character" keys. FWIW, the mapping I'm getting seems to match https://github.com/TurboVNC/tightvnc/blob/main/vnc_unixsrc/Xvnc/programs/Xse... and I guess we could consider using an alternate static mapping function when detecting this. Well I'm still not sure it's worth keeping code for such very specific use case, if tightvncserver is the only X server we've found that doesn't use evdev-compatible scancodes (for the low value range), and if it's been broken because of XShape errors and been like this for a while without anybody complaining.
Note that in addition to it being broken because of XShape, it seems that tightvncserver simply doesn't support XKB at all, and perhaps this is simply the source of our troubles. The Xkb xlib calls we do seem to mostly cope with the missing extension, but we could probably use that information to special case this scenario, if we really want to keep support for it. With the idea of getting rid of the fuzzy layout detection, it seems possible to treat all missing XKB support under a unique workaround. For instance a fallback keycode -> scancode mapping table like this seems to do the trick for me: ``` static WORD default_keyc2scan[0x100]; static BOOL use_xkb; static void init_default_keyc2scan( Display *display ) { static const WORD keysyms[] = { 0, XK_Escape, XK_1, XK_2, XK_3, XK_4, XK_5, XK_6, XK_7, XK_8, XK_9, XK_0, XK_minus, XK_equal, XK_BackSpace, XK_Tab, XK_q, XK_w, XK_e, XK_r, XK_t, XK_y, XK_u, XK_i, XK_o, XK_p, XK_braceleft, XK_braceright, XK_Return, XK_Control_L, XK_a, XK_s, XK_d, XK_f, XK_g, XK_h, XK_j, XK_k, XK_l, XK_semicolon, XK_apostrophe, XK_grave, XK_Shift_L, XK_backslash, XK_z, XK_x, XK_c, XK_v, XK_b, XK_n, XK_m, XK_comma, XK_period, XK_slash, XK_Shift_R, XK_asterisk, XK_Alt_L, XK_space, XK_Caps_Lock, XK_F1, XK_F2, XK_F3, XK_F4, XK_F5, XK_F6, XK_F7, XK_F8, XK_F9, XK_F10, XK_Num_Lock, XK_Scroll_Lock, XK_KP_7, XK_KP_8, XK_KP_9, XK_KP_Subtract, XK_KP_4, XK_KP_5, XK_KP_6, XK_KP_Add, XK_KP_1, XK_KP_2, XK_KP_3, XK_KP_0, XK_KP_Decimal, [0x5a] = XK_ISO_Level3_Shift, [KEY_RIGHTCTRL] = XK_Control_R, [KEY_LEFTMETA] = XK_Meta_L, [KEY_RIGHTMETA] = XK_Meta_R, [KEY_RIGHTALT] = XK_Alt_R, [KEY_HOME] = XK_Home, [KEY_END] = XK_End, [KEY_PAGEUP] = XK_Page_Up, [KEY_PAGEDOWN] = XK_Page_Down, [KEY_UP] = XK_Up, [KEY_DOWN] = XK_Down, [KEY_LEFT] = XK_Left, [KEY_RIGHT] = XK_Right, [KEY_F11] = XK_F11, [KEY_F12] = XK_F12, [KEY_DELETE] = XK_Delete, [KEY_KPENTER] = XK_KP_Enter, }; for (unsigned int keyc = min_keycode; keyc <= max_keycode; keyc++) { XKeyEvent event = { .display = display, .keycode = keyc }; unsigned int k; KeySym keysym; if (!(keysym = XLookupKeysym( &event, 0 ))) continue; for (k = 1; k < ARRAY_SIZE(keysyms); k++) if (keysyms[k] == keysym) break; if (k < ARRAY_SIZE(keysyms)) default_keyc2scan[keyc - min_keycode] = k; else WARN( "Failed to map keyc %#x keysym %#lx\n", keyc, keysym ); } } static WORD keyc2scan( unsigned int keycode, unsigned int state ) { unsigned int key = keycode - 8; if (!use_xkb) return default_keyc2scan[keycode - min_keycode]; /* ... */ /* in init_keyboard_layouts */ if (!use_xkb) init_default_keyc2scan( display ); /* ... */ /* in x11drv_init_keyboard */ use_xkb = XkbUseExtension( display, NULL, NULL ); ``` -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10963#note_143597