diff --git a/client/displayservers/Wayland/input.c b/client/displayservers/Wayland/input.c index fdd66801..867afa97 100644 --- a/client/displayservers/Wayland/input.c +++ b/client/displayservers/Wayland/input.c @@ -225,6 +225,20 @@ done: close(fd); } +bool waylandGetKeyLabel(int key, char * label, size_t size) +{ + if (!wlWm.xkbState) + return false; + + key += 8; // xkb scancode is evdev scancode + 8 + xkb_keysym_t sym = xkb_state_key_get_one_sym(wlWm.xkbState, key); + sym = xkb_keysym_to_upper(sym); + + const uint32_t codepoint = xkb_keysym_to_utf32(sym); + return codepoint > 0x20 && codepoint != 0x7F && + xkb_keysym_to_utf8(sym, label, size) > 0; +} + static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard, uint32_t serial, struct wl_surface * surface, struct wl_array * keys) { diff --git a/client/displayservers/Wayland/wayland.c b/client/displayservers/Wayland/wayland.c index 8fa79eee..94d3ac90 100644 --- a/client/displayservers/Wayland/wayland.c +++ b/client/displayservers/Wayland/wayland.c @@ -320,6 +320,7 @@ struct LG_DisplayServerOps LGDS_Wayland = .uncapturePointer = waylandUncapturePointer, .grabKeyboard = waylandGrabKeyboard, .ungrabKeyboard = waylandUngrabKeyboard, + .getKeyLabel = waylandGetKeyLabel, .warpPointer = waylandWarpPointer, .realignPointer = waylandRealignPointer, .isValidPointerPos = waylandIsValidPointerPos, diff --git a/client/displayservers/Wayland/wayland.h b/client/displayservers/Wayland/wayland.h index 29ef390e..1d80297b 100644 --- a/client/displayservers/Wayland/wayland.h +++ b/client/displayservers/Wayland/wayland.h @@ -378,6 +378,7 @@ void waylandUncapturePointer(void); void waylandRealignPointer(void); void waylandWarpPointer(int x, int y, bool exiting); void waylandGuestPointerUpdated(double x, double y, double localX, double localY); +bool waylandGetKeyLabel(int key, char * label, size_t size); // output module bool waylandOutputInit(void); void waylandOutputFree(void); diff --git a/client/displayservers/X11/x11.c b/client/displayservers/X11/x11.c index 1c563934..1bdff6c3 100644 --- a/client/displayservers/X11/x11.c +++ b/client/displayservers/X11/x11.c @@ -39,6 +39,9 @@ #include #include #include +#include + +#include #include #include @@ -88,6 +91,7 @@ static void x11SetFullscreen(bool fs); static int x11EventThread(void * unused); static void x11XInputEvent(XGenericEventCookie *cookie); static void x11XPresentEvent(XGenericEventCookie *cookie); +static void x11UpdateKeyboardGroup(void); static void x11GrabPointer(void); static void x11DoPresent(uint64_t msc) @@ -568,8 +572,8 @@ static bool x11Init(const LG_DSInitParams params) goto fail_window; } - int maxKeycode; - XDisplayKeycodes(x11.display, &x11.minKeycode, &maxKeycode); + XDisplayKeycodes(x11.display, &x11.minKeycode, &x11.maxKeycode); + x11UpdateKeyboardGroup(); XIFreeDeviceInfo(devinfo); @@ -1076,16 +1080,41 @@ static void updateModifiers(void) ); } +static void x11UpdateKeyboardGroup(void) +{ + XkbStateRec state; + if (XkbGetState(x11.display, XkbUseCoreKbd, &state) == Success) + atomic_store(&x11.keyboardGroup, state.group); +} + static void setFocus(bool focused, double x, double y) { if (x11.focused == focused) return; + if (focused) + x11UpdateKeyboardGroup(); + x11.focused = focused; app_updateCursorPos(x, y); app_handleFocusEvent(focused); } +static bool x11GetKeyLabel(int sc, char * label, size_t size) +{ + const int keycode = sc + x11.minKeycode; + if (keycode < x11.minKeycode || keycode > x11.maxKeycode) + return false; + + xkb_keysym_t sym = XkbKeycodeToKeysym( + x11.display, keycode, atomic_load(&x11.keyboardGroup), 0); + sym = xkb_keysym_to_upper(sym); + + const uint32_t codepoint = xkb_keysym_to_utf32(sym); + return codepoint > 0x20 && codepoint != 0x7F && + xkb_keysym_to_utf8(sym, label, size) > 0; +} + static void x11XInputEvent(XGenericEventCookie *cookie) { static int button_state = 0; @@ -1217,6 +1246,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie) return; XIDeviceEvent *device = cookie->data; + atomic_store(&x11.keyboardGroup, device->group.effective); app_handleKeyPress(device->detail - x11.minKeycode); if (!x11.xic || !app_isOverlayMode()) @@ -2001,6 +2031,7 @@ struct LG_DisplayServerOps LGDS_X11 = .ungrabPointer = x11UngrabPointer, .capturePointer = x11CapturePointer, .uncapturePointer = x11UncapturePointer, + .getKeyLabel = x11GetKeyLabel, .grabKeyboard = x11GrabKeyboard, .ungrabKeyboard = x11UngrabKeyboard, .warpPointer = x11WarpPointer, diff --git a/client/displayservers/X11/x11.h b/client/displayservers/X11/x11.h index 4741695a..5e64211b 100644 --- a/client/displayservers/X11/x11.h +++ b/client/displayservers/X11/x11.h @@ -60,7 +60,8 @@ struct X11DSState XVisualInfo * visual; X11WM * wm; - int minKeycode; + int minKeycode, maxKeycode; + _Atomic(unsigned int) keyboardGroup; //Extended Window Manager Hints //ref: https://specifications.freedesktop.org/wm-spec/latest/ diff --git a/client/include/interface/displayserver.h b/client/include/interface/displayserver.h index 197a6553..de8e7e8f 100644 --- a/client/include/interface/displayserver.h +++ b/client/include/interface/displayserver.h @@ -22,6 +22,7 @@ #define _H_I_DISPLAYSERVER_ #include +#include #include #include "common/types.h" #include "common/debug.h" @@ -206,6 +207,9 @@ struct LG_DisplayServerOps void (*capturePointer)(void); void (*uncapturePointer)(void); + /* get the active keymap's display label for the provided Linux keycode */ + bool (*getKeyLabel)(int sc, char * label, size_t size); + /* exiting = true if the warp is to leave the window */ void (*warpPointer)(int x, int y, bool exiting); @@ -283,6 +287,7 @@ struct LG_DisplayServerOps DEBUG_ASSERT((x)->ungrabPointer ); \ DEBUG_ASSERT((x)->capturePointer ); \ DEBUG_ASSERT((x)->uncapturePointer ); \ + DEBUG_ASSERT((x)->getKeyLabel ); \ DEBUG_ASSERT((x)->warpPointer ); \ DEBUG_ASSERT((x)->realignPointer ); \ DEBUG_ASSERT((x)->isValidPointerPos ); \ diff --git a/client/src/overlay/help.c b/client/src/overlay/help.c index 199dfd69..592fe29e 100644 --- a/client/src/overlay/help.c +++ b/client/src/overlay/help.c @@ -56,7 +56,11 @@ static int help_render(void * udata, bool interactive, struct Rect * windowRects if (igBeginTable("Help", 2, 0, (ImVec2) { 0.0f, 0.0f }, 0.0f)) { + char escapeLabel[8]; const char * escapeName = linux_to_display[g_params.escapeKey]; + if (g_state.ds->getKeyLabel( + g_params.escapeKey, escapeLabel, sizeof(escapeLabel))) + escapeName = escapeLabel; igTableNextColumn(); igText("%s", escapeName); @@ -69,8 +73,13 @@ static int help_render(void * udata, bool interactive, struct Rect * windowRects if (!handle->description) continue; + char keyLabel[8]; + const char * keyName = linux_to_display[handle->sc]; + if (g_state.ds->getKeyLabel(handle->sc, keyLabel, sizeof(keyLabel))) + keyName = keyLabel; + igTableNextColumn(); - igText("%s+%s", escapeName, linux_to_display[handle->sc]); + igText("%s+%s", escapeName, keyName); igTableNextColumn(); igTextUnformatted(handle->description, NULL); }