From 974bbea20b7a87f792f7c10c26b011c0907a05fa Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Sat, 22 Jun 2024 19:09:50 -0400 Subject: [PATCH] x11: Add keymap support XkbKeycodeToKeySym is replaced with XkbLookupKeySym, which can take the modifier states. The associated cmake check has been renamed for consistency. Only the XKB path is currently handled. The deprecated XKeycodeToKeysym path is TODO. --- cmake/sdlchecks.cmake | 2 +- include/build_config/SDL_build_config.h.cmake | 2 +- include/build_config/SDL_build_config_macos.h | 2 +- src/video/x11/SDL_x11dyn.h | 2 +- src/video/x11/SDL_x11events.c | 1 + src/video/x11/SDL_x11keyboard.c | 140 ++++++++++-------- src/video/x11/SDL_x11keyboard.h | 2 +- src/video/x11/SDL_x11sym.h | 6 +- src/video/x11/SDL_x11video.h | 2 +- 9 files changed, 87 insertions(+), 72 deletions(-) diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake index 85816a2c5..aecb98fe5 100644 --- a/cmake/sdlchecks.cmake +++ b/cmake/sdlchecks.cmake @@ -371,7 +371,7 @@ macro(CheckX11) set(SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS 1) endif() - check_symbol_exists(XkbKeycodeToKeysym "X11/Xlib.h;X11/XKBlib.h" SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM) + check_symbol_exists(XkbLookupKeySym "X11/Xlib.h;X11/XKBlib.h" SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM) if(SDL_X11_XCURSOR AND HAVE_XCURSOR_H AND XCURSOR_LIB) set(HAVE_X11_XCURSOR TRUE) diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index 348856a21..e12b96b36 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -400,7 +400,7 @@ #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 @SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2@ #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR @SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR@ #cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS @SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS@ -#cmakedefine SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM @SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM@ +#cmakedefine SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM @SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM@ #cmakedefine SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS @SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS@ #cmakedefine SDL_VIDEO_DRIVER_X11_XCURSOR @SDL_VIDEO_DRIVER_X11_XCURSOR@ #cmakedefine SDL_VIDEO_DRIVER_X11_XDBE @SDL_VIDEO_DRIVER_X11_XDBE@ diff --git a/include/build_config/SDL_build_config_macos.h b/include/build_config/SDL_build_config_macos.h index 02cd3ae4c..597cf8874 100644 --- a/include/build_config/SDL_build_config_macos.h +++ b/include/build_config/SDL_build_config_macos.h @@ -194,7 +194,7 @@ #define SDL_VIDEO_DRIVER_X11_XRANDR 1 #define SDL_VIDEO_DRIVER_X11_XSCRNSAVER 1 #define SDL_VIDEO_DRIVER_X11_XSHAPE 1 -#define SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM 1 +#define SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM 1 #ifdef MAC_OS_X_VERSION_10_8 /* diff --git a/src/video/x11/SDL_x11dyn.h b/src/video/x11/SDL_x11dyn.h index 73d8953e8..b045d12cb 100644 --- a/src/video/x11/SDL_x11dyn.h +++ b/src/video/x11/SDL_x11dyn.h @@ -28,7 +28,7 @@ #include #include -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM #include #endif diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 08dddeb33..043db8de5 100644 --- a/src/video/x11/SDL_x11events.c +++ b/src/video/x11/SDL_x11events.c @@ -1131,6 +1131,7 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent) printf("window %p: KeymapNotify!\n", data); #endif if (SDL_GetKeyboardFocus() != NULL) { + X11_UpdateKeymap(_this, SDL_TRUE); X11_ReconcileKeyboardState(_this); } } else if (xevent->type == MappingNotify) { diff --git a/src/video/x11/SDL_x11keyboard.c b/src/video/x11/SDL_x11keyboard.c index b4d8d88a8..543c36e4a 100644 --- a/src/video/x11/SDL_x11keyboard.c +++ b/src/video/x11/SDL_x11keyboard.c @@ -72,7 +72,7 @@ static SDL_bool X11_ScancodeIsRemappable(SDL_Scancode scancode) /* This function only correctly maps letters and numbers for keyboards in US QWERTY layout */ static SDL_Scancode X11_KeyCodeToSDLScancode(SDL_VideoDevice *_this, KeyCode keycode) { - const KeySym keysym = X11_KeyCodeToSym(_this, keycode, 0); + const KeySym keysym = X11_KeyCodeToSym(_this, keycode, 0, 0); if (keysym == NoSymbol) { return SDL_SCANCODE_UNKNOWN; @@ -81,24 +81,15 @@ static SDL_Scancode X11_KeyCodeToSDLScancode(SDL_VideoDevice *_this, KeyCode key return SDL_GetScancodeFromKeySym(keysym, keycode); } -static Uint32 X11_KeyCodeToUcs4(SDL_VideoDevice *_this, KeyCode keycode, unsigned char group) -{ - KeySym keysym = X11_KeyCodeToSym(_this, keycode, group); - - if (keysym == NoSymbol) { - return 0; - } - - return SDL_KeySymToUcs4(keysym); -} - -KeySym -X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode keycode, unsigned char group) +KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode keycode, unsigned char group, unsigned int mod_mask) { SDL_VideoData *data = _this->driverdata; KeySym keysym; + unsigned int mods_ret[16]; -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM + SDL_zero(mods_ret); + +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM if (data->xkb) { int num_groups = XkbKeyNumGroups(data->xkb, keycode); unsigned char info = XkbKeyGroupInfo(data->xkb, keycode); @@ -118,13 +109,16 @@ X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode keycode, unsigned char group) group %= num_groups; } } - keysym = X11_XkbKeycodeToKeysym(data->display, keycode, group, 0); - } else { + + if (X11_XkbLookupKeySym(data->display, keycode, XkbBuildCoreState(mod_mask, group), mods_ret, &keysym) == NoSymbol) { + keysym = NoSymbol; + } + } else +#endif + { + /* TODO: Handle groups and modifiers on the legacy path. */ keysym = X11_XKeycodeToKeysym(data->display, keycode, 0); } -#else - keysym = X11_XKeycodeToKeysym(data->display, keycode, 0); -#endif return keysym; } @@ -153,7 +147,7 @@ int X11_InitKeyboard(SDL_VideoDevice *_this) int distance; Bool xkb_repeat = 0; -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM { int xkb_major = XkbMajorVersion; int xkb_minor = XkbMinorVersion; @@ -334,6 +328,21 @@ int X11_InitKeyboard(SDL_VideoDevice *_this) void X11_UpdateKeymap(SDL_VideoDevice *_this, SDL_bool send_event) { + struct Keymod_masks + { + SDL_Keymod sdl_mask; + unsigned int xkb_mask; + } const keymod_masks[] = { + { SDL_KMOD_NONE, 0 }, + { SDL_KMOD_SHIFT, ShiftMask }, + { SDL_KMOD_CAPS, LockMask }, + { SDL_KMOD_SHIFT | SDL_KMOD_CAPS, ShiftMask | LockMask }, + { SDL_KMOD_MODE, Mod5Mask }, + { SDL_KMOD_MODE | SDL_KMOD_SHIFT, Mod5Mask | ShiftMask }, + { SDL_KMOD_MODE | SDL_KMOD_CAPS, Mod5Mask | LockMask }, + { SDL_KMOD_MODE | SDL_KMOD_SHIFT | SDL_KMOD_CAPS, Mod5Mask | ShiftMask | LockMask } + }; + SDL_VideoData *data = _this->driverdata; int i; SDL_Scancode scancode; @@ -342,7 +351,7 @@ void X11_UpdateKeymap(SDL_VideoDevice *_this, SDL_bool send_event) keymap = SDL_CreateKeymap(); -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM if (data->xkb) { XkbStateRec state; X11_XkbGetUpdatedMap(data->display, XkbAllClientInfoMask, data->xkb); @@ -353,49 +362,54 @@ void X11_UpdateKeymap(SDL_VideoDevice *_this, SDL_bool send_event) } #endif - // FIXME: Need to get the mapping for all modifiers, not just the first one - for (i = 0; i < SDL_arraysize(data->key_layout); i++) { - Uint32 key; - SDL_Keycode keycode; + for (int m = 0; m < SDL_arraysize(keymod_masks); ++m) { + for (i = 0; i < SDL_arraysize(data->key_layout); i++) { + SDL_Keycode keycode; - /* Make sure this is a valid scancode */ - scancode = data->key_layout[i]; - if (scancode == SDL_SCANCODE_UNKNOWN) { - continue; - } - - /* See if there is a UCS keycode for this scancode */ - key = X11_KeyCodeToUcs4(_this, (KeyCode)i, group); - if (key) { - keycode = (SDL_Keycode)key; - } else { - SDL_Scancode keyScancode = X11_KeyCodeToSDLScancode(_this, (KeyCode)i); - - switch (keyScancode) { - case SDL_SCANCODE_UNKNOWN: - keycode = SDLK_UNKNOWN; - break; - case SDL_SCANCODE_RETURN: - keycode = SDLK_RETURN; - break; - case SDL_SCANCODE_ESCAPE: - keycode = SDLK_ESCAPE; - break; - case SDL_SCANCODE_BACKSPACE: - keycode = SDLK_BACKSPACE; - break; - case SDL_SCANCODE_TAB: - keycode = SDLK_TAB; - break; - case SDL_SCANCODE_DELETE: - keycode = SDLK_DELETE; - break; - default: - keycode = SDL_SCANCODE_TO_KEYCODE(keyScancode); - break; + /* Make sure this is a valid scancode */ + scancode = data->key_layout[i]; + if (scancode == SDL_SCANCODE_UNKNOWN) { + continue; } + + KeySym keysym = X11_KeyCodeToSym(_this, i, group, keymod_masks[m].xkb_mask); + + /* Note: The default SDL scancode table sets this to right alt instead of AltGr/Mode, so handle it separately. */ + if (keysym != XK_ISO_Level3_Shift) { + keycode = SDL_KeySymToUcs4(keysym); + } else { + keycode = SDLK_MODE; + } + + if (!keycode) { + SDL_Scancode keyScancode = SDL_GetScancodeFromKeySym(keysym, (KeyCode)i); + + switch (keyScancode) { + case SDL_SCANCODE_UNKNOWN: + keycode = SDLK_UNKNOWN; + break; + case SDL_SCANCODE_RETURN: + keycode = SDLK_RETURN; + break; + case SDL_SCANCODE_ESCAPE: + keycode = SDLK_ESCAPE; + break; + case SDL_SCANCODE_BACKSPACE: + keycode = SDLK_BACKSPACE; + break; + case SDL_SCANCODE_TAB: + keycode = SDLK_TAB; + break; + case SDL_SCANCODE_DELETE: + keycode = SDLK_DELETE; + break; + default: + keycode = SDL_SCANCODE_TO_KEYCODE(keyScancode); + break; + } + } + SDL_SetKeymapEntry(keymap, scancode, keymod_masks[m].sdl_mask, keycode); } - SDL_SetKeymapEntry(keymap, scancode, SDL_KMOD_NONE, keycode); } SDL_SetKeymap(keymap, send_event); } @@ -404,7 +418,7 @@ void X11_QuitKeyboard(SDL_VideoDevice *_this) { SDL_VideoData *data = _this->driverdata; -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM if (data->xkb) { X11_XkbFreeKeyboard(data->xkb, 0, True); data->xkb = NULL; diff --git a/src/video/x11/SDL_x11keyboard.h b/src/video/x11/SDL_x11keyboard.h index 9164e04ec..5e0ed2803 100644 --- a/src/video/x11/SDL_x11keyboard.h +++ b/src/video/x11/SDL_x11keyboard.h @@ -33,6 +33,6 @@ extern SDL_bool X11_HasScreenKeyboardSupport(SDL_VideoDevice *_this); extern void X11_ShowScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); extern void X11_HideScreenKeyboard(SDL_VideoDevice *_this, SDL_Window *window); extern SDL_bool X11_IsScreenKeyboardShown(SDL_VideoDevice *_this, SDL_Window *window); -extern KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode, unsigned char group); +extern KeySym X11_KeyCodeToSym(SDL_VideoDevice *_this, KeyCode, unsigned char group, unsigned int mod_mask); #endif /* SDL_x11keyboard_h_ */ diff --git a/src/video/x11/SDL_x11sym.h b/src/video/x11/SDL_x11sym.h index aa1ea9991..b21c8e7a5 100644 --- a/src/video/x11/SDL_x11sym.h +++ b/src/video/x11/SDL_x11sym.h @@ -178,12 +178,12 @@ SDL_X11_SYM(Bool,XGetEventData,(Display* a,XGenericEventCookie* b),(a,b),return) SDL_X11_SYM(void,XFreeEventData,(Display* a,XGenericEventCookie* b),(a,b),) #endif -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM SDL_X11_SYM(Bool,XkbQueryExtension,(Display* a,int * b,int * c,int * d,int * e, int *f),(a,b,c,d,e,f),return) #if NeedWidePrototypes -SDL_X11_SYM(KeySym,XkbKeycodeToKeysym,(Display* a,unsigned int b,int c,int d),(a,b,c,d),return) +SDL_X11_SYM(Bool,XkbLookupKeySym,(Display* a, unsigned int b, unsigned int c, unsigned int* d, KeySym* e),(a,b,c,d,e),return) #else -SDL_X11_SYM(KeySym,XkbKeycodeToKeysym,(Display* a,KeyCode b,int c,int d),(a,b,c,d),return) +SDL_X11_SYM(Bool,XkbLookupKeySym,(Display* a, KeyCode b, unsigned int c, unsigned int* d, KeySym* e),(a,b,c,d,e),return) #endif SDL_X11_SYM(Status,XkbGetState,(Display* a,unsigned int b,XkbStatePtr c),(a,b,c),return) SDL_X11_SYM(Status,XkbGetUpdatedMap,(Display* a,unsigned int b,XkbDescPtr c),(a,b,c),return) diff --git a/src/video/x11/SDL_x11video.h b/src/video/x11/SDL_x11video.h index dbe1a6d53..80b5c4799 100644 --- a/src/video/x11/SDL_x11video.h +++ b/src/video/x11/SDL_x11video.h @@ -121,7 +121,7 @@ struct SDL_VideoData int xrandr_event_base; -#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBKEYCODETOKEYSYM +#ifdef SDL_VIDEO_DRIVER_X11_HAS_XKBLOOKUPKEYSYM XkbDescPtr xkb; #endif int xkb_event;