[client] all: use the defined keyboard mapping for keybinds

Fixes #1007
This commit is contained in:
Geoffrey McRae 2022-06-29 18:24:53 +10:00
parent ed0cae84c8
commit da04a6dd54
12 changed files with 151 additions and 76 deletions

View File

@ -209,6 +209,16 @@ done:
close(fd); close(fd);
} }
static int getCharcode(uint32_t key)
{
xkb_keysym_t sym = xkb_state_key_get_one_sym(wlWm.xkbState, key);
if (sym == XKB_KEY_NoSymbol)
return 0;
sym = xkb_keysym_to_upper(sym);
return xkb_keysym_to_utf32(sym);
}
static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard, static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
uint32_t serial, struct wl_surface * surface, struct wl_array * keys) uint32_t serial, struct wl_surface * surface, struct wl_array * keys)
{ {
@ -221,7 +231,7 @@ static void keyboardEnterHandler(void * data, struct wl_keyboard * keyboard,
uint32_t * key; uint32_t * key;
wl_array_for_each(key, keys) wl_array_for_each(key, keys)
app_handleKeyPress(*key); app_handleKeyPress(*key, getCharcode(*key));
} }
static void keyboardLeaveHandler(void * data, struct wl_keyboard * keyboard, static void keyboardLeaveHandler(void * data, struct wl_keyboard * keyboard,
@ -242,9 +252,9 @@ static void keyboardKeyHandler(void * data, struct wl_keyboard * keyboard,
return; return;
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) if (state == WL_KEYBOARD_KEY_STATE_PRESSED)
app_handleKeyPress(key); app_handleKeyPress(key, getCharcode(key));
else else
app_handleKeyRelease(key); app_handleKeyRelease(key, getCharcode(key));
if (!wlWm.xkbState || !app_isOverlayMode() || state != WL_KEYBOARD_KEY_STATE_PRESSED) if (!wlWm.xkbState || !app_isOverlayMode() || state != WL_KEYBOARD_KEY_STATE_PRESSED)
return; return;

View File

@ -36,6 +36,8 @@
#include <X11/extensions/Xpresent.h> #include <X11/extensions/Xpresent.h>
#include <X11/Xcursor/Xcursor.h> #include <X11/Xcursor/Xcursor.h>
#include <xkbcommon/xkbcommon.h>
#include <GL/glx.h> #include <GL/glx.h>
#include <GL/glxext.h> #include <GL/glxext.h>
@ -514,6 +516,10 @@ static bool x11Init(const LG_DSInitParams params)
goto fail_window; goto fail_window;
} }
XDisplayKeycodes(x11.display, &x11.minKeycode, &x11.maxKeycode);
x11.keysyms = XGetKeyboardMapping(x11.display, x11.minKeycode,
x11.maxKeycode - x11.minKeycode, &x11.symsPerKeycode);
XIFreeDeviceInfo(devinfo); XIFreeDeviceInfo(devinfo);
XQueryExtension(x11.display, "XInputExtension", &x11.xinputOp, &event, &error); XQueryExtension(x11.display, "XInputExtension", &x11.xinputOp, &event, &error);
@ -738,6 +744,9 @@ static void x11Free(void)
if (x11.cursors[i]) if (x11.cursors[i])
XFreeCursor(x11.display, x11.cursors[i]); XFreeCursor(x11.display, x11.cursors[i]);
if (x11.keysyms)
XFree(x11.keysyms);
XCloseDisplay(x11.display); XCloseDisplay(x11.display);
} }
@ -1035,6 +1044,17 @@ static void setFocus(bool focused, double x, double y)
app_handleFocusEvent(focused); app_handleFocusEvent(focused);
} }
static int getCharcode(int detail)
{
if (detail < x11.minKeycode || detail > x11.maxKeycode)
return 0;
KeySym sym = x11.keysyms[(detail - x11.minKeycode) *
x11.symsPerKeycode];
sym = xkb_keysym_to_upper(sym);
return xkb_keysym_to_utf32(sym);
}
static void x11XInputEvent(XGenericEventCookie *cookie) static void x11XInputEvent(XGenericEventCookie *cookie)
{ {
static int button_state = 0; static int button_state = 0;
@ -1126,7 +1146,8 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return; return;
XIDeviceEvent *device = cookie->data; XIDeviceEvent *device = cookie->data;
app_handleKeyPress(device->detail - 8); app_handleKeyPress(device->detail - x11.minKeycode,
getCharcode(device->detail));
if (!x11.xic || !app_isOverlayMode()) if (!x11.xic || !app_isOverlayMode())
return; return;
@ -1176,7 +1197,8 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return; return;
XIDeviceEvent *device = cookie->data; XIDeviceEvent *device = cookie->data;
app_handleKeyRelease(device->detail - 8); app_handleKeyRelease(device->detail - x11.minKeycode,
getCharcode(device->detail));
if (!x11.xic || !app_isOverlayMode()) if (!x11.xic || !app_isOverlayMode())
return; return;
@ -1205,7 +1227,8 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return; return;
XIRawEvent *raw = cookie->data; XIRawEvent *raw = cookie->data;
app_handleKeyPress(raw->detail - 8); app_handleKeyPress(raw->detail - x11.minKeycode,
getCharcode(raw->detail));
return; return;
} }
@ -1215,7 +1238,8 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
return; return;
XIRawEvent *raw = cookie->data; XIRawEvent *raw = cookie->data;
app_handleKeyRelease(raw->detail - 8); app_handleKeyRelease(raw->detail - x11.minKeycode,
getCharcode(raw->detail));
return; return;
} }

View File

@ -54,6 +54,10 @@ struct X11DSState
Window window; Window window;
XVisualInfo * visual; XVisualInfo * visual;
int minKeycode, maxKeycode;
int symsPerKeycode;
KeySym * keysyms;
//Extended Window Manager Hints //Extended Window Manager Hints
//ref: https://specifications.freedesktop.org/wm-spec/latest/ //ref: https://specifications.freedesktop.org/wm-spec/latest/
bool ewmhSupport; bool ewmhSupport;

View File

@ -58,9 +58,9 @@ void app_resyncMouseBasic(void);
void app_handleButtonPress(int button); void app_handleButtonPress(int button);
void app_handleButtonRelease(int button); void app_handleButtonRelease(int button);
void app_handleWheelMotion(double motion); void app_handleWheelMotion(double motion);
void app_handleKeyPress(int scancode);
void app_handleKeyRelease(int scancode);
void app_handleKeyboardTyped(const char * typed); void app_handleKeyboardTyped(const char * typed);
void app_handleKeyPress(int scancode, int charcode);
void app_handleKeyRelease(int scancode, int charcode);
void app_handleKeyboardModifiers(bool ctrl, bool shift, bool alt, bool super); void app_handleKeyboardModifiers(bool ctrl, bool shift, bool alt, bool super);
void app_handleKeyboardLEDs(bool numLock, bool capsLock, bool scrollLock); void app_handleKeyboardLEDs(bool numLock, bool capsLock, bool scrollLock);
void app_handleEnterEvent(bool entered); void app_handleEnterEvent(bool entered);
@ -155,12 +155,14 @@ void app_showRecord(bool show);
/** /**
* Register a handler for the <super>+<key> combination * Register a handler for the <super>+<key> combination
* @param sc The scancode to register * @param sc The scancode to register
* @param charcode The charcode to register (used instead of sc if non zero)
* @param callback The function to be called when the combination is pressed * @param callback The function to be called when the combination is pressed
* @param opaque A pointer to be passed to the callback, may be NULL * @param opaque A pointer to be passed to the callback, may be NULL
* @retval A handle for the binding or NULL on failure. * @retval A handle for the binding or NULL on failure.
* The caller is required to release the handle via `app_releaseKeybind` when it is no longer required * The caller is required to release the handle via `app_releaseKeybind` when it is no longer required
*/ */
KeybindHandle app_registerKeybind(int sc, KeybindFn callback, void * opaque, const char * description); KeybindHandle app_registerKeybind(int sc, int charcode, KeybindFn callback,
void * opaque, const char * description);
/** /**
* Release an existing key binding * Release an existing key binding

View File

@ -157,7 +157,7 @@ bool egl_desktopInit(EGL * egl, EGL_Desktop ** desktop_, EGLDisplay * display,
return false; return false;
} }
app_registerKeybind(KEY_N, toggleNV, desktop, app_registerKeybind(0, 'N', toggleNV, desktop,
"Toggle night vision mode"); "Toggle night vision mode");
desktop->nvMax = option_get_int("egl", "nvGainMax"); desktop->nvMax = option_get_int("egl", "nvGainMax");

View File

@ -38,6 +38,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <ctype.h>
bool app_isRunning(void) bool app_isRunning(void)
{ {
@ -115,7 +116,7 @@ void app_handleFocusEvent(bool focused)
if (g_params.releaseKeysOnFocusLoss) if (g_params.releaseKeysOnFocusLoss)
for (int key = 0; key < KEY_MAX; key++) for (int key = 0; key < KEY_MAX; key++)
if (g_state.keyDown[key]) if (g_state.keyDown[key])
app_handleKeyRelease(key); app_handleKeyRelease(key, 0);
g_state.escapeActive = false; g_state.escapeActive = false;
@ -304,7 +305,7 @@ void app_handleWheelMotion(double motion)
g_state.io->MouseWheel -= motion; g_state.io->MouseWheel -= motion;
} }
void app_handleKeyPress(int sc) void app_handleKeyPress(int sc, int charcode)
{ {
if (!app_isOverlayMode() || !g_state.io->WantCaptureKeyboard) if (!app_isOverlayMode() || !g_state.io->WantCaptureKeyboard)
{ {
@ -319,9 +320,16 @@ void app_handleKeyPress(int sc)
if (g_state.escapeActive) if (g_state.escapeActive)
{ {
g_state.escapeAction = sc; g_state.escapeAction = sc;
KeybindHandle handle = g_state.bindings[sc]; KeybindHandle handle;
if (handle) ll_forEachNL(g_state.bindings, item, handle)
{
if ((handle->sc && handle->sc == sc ) ||
(handle->charcode && handle->charcode == charcode))
{
handle->callback(sc, handle->opaque); handle->callback(sc, handle->opaque);
break;
}
}
return; return;
} }
} }
@ -357,7 +365,7 @@ void app_handleKeyPress(int sc)
} }
} }
void app_handleKeyRelease(int sc) void app_handleKeyRelease(int sc, int charcode)
{ {
if (g_state.escapeActive) if (g_state.escapeActive)
{ {
@ -679,16 +687,35 @@ void app_showRecord(bool show)
overlayStatus_set(LG_USER_STATUS_RECORDING, show); overlayStatus_set(LG_USER_STATUS_RECORDING, show);
} }
KeybindHandle app_registerKeybind(int sc, KeybindFn callback, void * opaque, const char * description) KeybindHandle app_registerKeybind(int sc, int charcode, KeybindFn callback,
void * opaque, const char * description)
{ {
if (charcode != 0 && sc != 0)
{
DEBUG_ERROR("invalid keybind, one of scancode or charcode must be 0");
return NULL;
}
if (charcode && islower(charcode))
{
DEBUG_ERROR("invalid keybind, charcode must be uppercase");
return NULL;
}
KeybindHandle handle;
// don't allow duplicate binds // don't allow duplicate binds
if (g_state.bindings[sc]) ll_forEachNL(g_state.bindings, item, handle)
{
if ((sc && handle->sc == sc ) ||
(charcode && handle->charcode == charcode))
{ {
DEBUG_INFO("Key already bound"); DEBUG_INFO("Key already bound");
return NULL; return NULL;
} }
}
KeybindHandle handle = malloc(sizeof(*handle)); handle = malloc(sizeof(*handle));
if (!handle) if (!handle)
{ {
DEBUG_ERROR("out of memory"); DEBUG_ERROR("out of memory");
@ -696,11 +723,12 @@ KeybindHandle app_registerKeybind(int sc, KeybindFn callback, void * opaque, con
} }
handle->sc = sc; handle->sc = sc;
handle->charcode = charcode;
handle->callback = callback; handle->callback = callback;
handle->description = description;
handle->opaque = opaque; handle->opaque = opaque;
g_state.bindings[sc] = handle; ll_push(g_state.bindings, handle);
g_state.keyDescription[sc] = description;
return handle; return handle;
} }
@ -709,19 +737,16 @@ void app_releaseKeybind(KeybindHandle * handle)
if (!*handle) if (!*handle)
return; return;
g_state.bindings[(*handle)->sc] = NULL; ll_removeData(g_state.bindings, *handle);
free(*handle); free(*handle);
*handle = NULL; *handle = NULL;
} }
void app_releaseAllKeybinds(void) void app_releaseAllKeybinds(void)
{ {
for(int i = 0; i < KEY_MAX; ++i) KeybindHandle * handle;
if (g_state.bindings[i]) while(ll_shift(g_state.bindings, (void **)&handle))
{ free(handle);
free(g_state.bindings[i]);
g_state.bindings[i] = NULL;
}
} }
GraphHandle app_registerGraph(const char * name, RingBuffer buffer, GraphHandle app_registerGraph(const char * name, RingBuffer buffer,

View File

@ -127,15 +127,15 @@ static void bind_toggleKey(int sc, void * opaque)
void keybind_commonRegister(void) void keybind_commonRegister(void)
{ {
app_registerKeybind(KEY_F, bind_fullscreen , NULL, app_registerKeybind(0, 'F', bind_fullscreen , NULL,
"Full screen toggle"); "Full screen toggle");
app_registerKeybind(KEY_V, bind_video , NULL, app_registerKeybind(0, 'V', bind_video , NULL,
"Video stream toggle"); "Video stream toggle");
app_registerKeybind(KEY_R, bind_rotate , NULL, app_registerKeybind(0, 'R', bind_rotate , NULL,
"Rotate the output clockwise by 90° increments"); "Rotate the output clockwise by 90° increments");
app_registerKeybind(KEY_Q, bind_quit , NULL, app_registerKeybind(0, 'Q', bind_quit , NULL,
"Quit"); "Quit");
app_registerKeybind(KEY_O, bind_toggleOverlay, NULL, app_registerKeybind(0, 'O', bind_toggleOverlay, NULL,
"Toggle overlay"); "Toggle overlay");
} }
@ -145,30 +145,30 @@ void keybind_spiceRegister(void)
static bool firstTime = true; static bool firstTime = true;
if (firstTime) if (firstTime)
{ {
app_registerKeybind(KEY_I, bind_input, NULL, app_registerKeybind(0, 'I', bind_input, NULL,
"Spice keyboard & mouse toggle"); "Spice keyboard & mouse toggle");
app_registerKeybind(KEY_INSERT, bind_mouseSens, (void *) true , app_registerKeybind(KEY_INSERT, 0, bind_mouseSens, (void *) true ,
"Increase mouse sensitivity in capture mode"); "Increase mouse sensitivity 0, in capture mode");
app_registerKeybind(KEY_DELETE, bind_mouseSens, (void *) false, app_registerKeybind(KEY_DELETE, 0, bind_mouseSens, (void *) false,
"Descrease mouse sensitivity in capture mode"); "Descrease mouse sensitivity in capture mode");
app_registerKeybind(KEY_UP , bind_toggleKey, (void *) PS2_VOLUME_UP , app_registerKeybind(KEY_UP , 0 , bind_toggleKey, (void *) PS2_VOLUME_UP ,
"Send volume up to the guest"); "Send volume up to the guest");
app_registerKeybind(KEY_DOWN, bind_toggleKey, (void *) PS2_VOLUME_DOWN, app_registerKeybind(KEY_DOWN, 0 , bind_toggleKey, (void *) PS2_VOLUME_DOWN,
"Send volume down to the guest"); "Send volume down to the guest");
app_registerKeybind(KEY_M , bind_toggleKey, (void *) PS2_MUTE , app_registerKeybind(0 , 'M', bind_toggleKey, (void *) PS2_MUTE ,
"Send mute to the guest"); "Send mute to the guest");
app_registerKeybind(KEY_LEFTMETA , bind_passthrough, NULL, app_registerKeybind(KEY_LEFTMETA , 0, bind_passthrough, NULL,
"Send LWin to the guest"); "Send LWin to the guest");
app_registerKeybind(KEY_RIGHTMETA, bind_passthrough, NULL, app_registerKeybind(KEY_RIGHTMETA, 0, bind_passthrough, NULL,
"Send RWin to the guest"); "Send RWin to the guest");
#if ENABLE_AUDIO #if ENABLE_AUDIO
if (audio_supportsRecord()) if (audio_supportsRecord())
{ {
app_registerKeybind(KEY_E, audio_recordToggleKeybind, NULL, app_registerKeybind(0, 'E', audio_recordToggleKeybind, NULL,
"Toggle audio recording"); "Toggle audio recording");
} }
#endif #endif
@ -186,29 +186,29 @@ void keybind_spiceRegister(void)
/* register OS based keybinds */ /* register OS based keybinds */
if (app_guestIsLinux()) if (app_guestIsLinux())
{ {
handles[handleCount++] = app_registerKeybind(KEY_F1 , bind_ctrlAltFn, NULL, handles[handleCount++] = app_registerKeybind(KEY_F1 , 0, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F1 to the guest"); "Send Ctrl+Alt+F1 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F2 , bind_ctrlAltFn, NULL, handles[handleCount++] = app_registerKeybind(KEY_F2 , 0, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F2 to the guest"); "Send Ctrl+Alt+F2 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F3 , bind_ctrlAltFn, NULL, handles[handleCount++] = app_registerKeybind(KEY_F3 , 0, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F3 to the guest"); "Send Ctrl+Alt+F3 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F4 , bind_ctrlAltFn, NULL, handles[handleCount++] = app_registerKeybind(KEY_F4 , 0, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F4 to the guest"); "Send Ctrl+Alt+F4 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F5 , bind_ctrlAltFn, NULL, handles[handleCount++] = app_registerKeybind(KEY_F5 , 0, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F5 to the guest"); "Send Ctrl+Alt+F5 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F6 , bind_ctrlAltFn, NULL, handles[handleCount++] = app_registerKeybind(KEY_F6 , 0, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F6 to the guest"); "Send Ctrl+Alt+F6 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F7 , bind_ctrlAltFn, NULL, handles[handleCount++] = app_registerKeybind(KEY_F7 , 0, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F7 to the guest"); "Send Ctrl+Alt+F7 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F8 , bind_ctrlAltFn, NULL, handles[handleCount++] = app_registerKeybind(KEY_F8 , 0, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F8 to the guest"); "Send Ctrl+Alt+F8 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F9 , bind_ctrlAltFn, NULL, handles[handleCount++] = app_registerKeybind(KEY_F9 , 0, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F9 to the guest"); "Send Ctrl+Alt+F9 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F10, bind_ctrlAltFn, NULL, handles[handleCount++] = app_registerKeybind(KEY_F10, 0, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F10 to the guest"); "Send Ctrl+Alt+F10 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F11, bind_ctrlAltFn, NULL, handles[handleCount++] = app_registerKeybind(KEY_F11, 0, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F11 to the guest"); "Send Ctrl+Alt+F11 to the guest");
handles[handleCount++] = app_registerKeybind(KEY_F12, bind_ctrlAltFn, NULL, handles[handleCount++] = app_registerKeybind(KEY_F12, 0, bind_ctrlAltFn, NULL,
"Send Ctrl+Alt+F12 to the guest"); "Send Ctrl+Alt+F12 to the guest");
} }
} }

View File

@ -1693,6 +1693,7 @@ static void lg_shutdown(void)
} }
app_releaseAllKeybinds(); app_releaseAllKeybinds();
ll_free(g_state.bindings);
if (g_state.dsInitialized) if (g_state.dsInitialized)
g_state.ds->free(); g_state.ds->free();
@ -1742,6 +1743,8 @@ int main(int argc, char * argv[])
egl_dynProcsInit(); egl_dynProcsInit();
gl_dynProcsInit(); gl_dynProcsInit();
g_state.bindings = ll_new();
g_state.overlays = ll_new(); g_state.overlays = ll_new();
app_registerOverlay(&LGOverlaySplash, NULL); app_registerOverlay(&LGOverlaySplash, NULL);
app_registerOverlay(&LGOverlayConfig, NULL); app_registerOverlay(&LGOverlayConfig, NULL);

View File

@ -90,8 +90,7 @@ struct AppState
uint64_t escapeTime; uint64_t escapeTime;
int escapeAction; int escapeAction;
bool escapeHelp; bool escapeHelp;
KeybindHandle bindings[KEY_MAX]; struct ll * bindings;
const char * keyDescription[KEY_MAX];
bool keyDown[KEY_MAX]; bool keyDown[KEY_MAX];
bool haveSrcSize; bool haveSrcSize;
@ -228,7 +227,9 @@ struct CBRequest
struct KeybindHandle struct KeybindHandle
{ {
int sc; int sc;
int charcode;
KeybindFn callback; KeybindFn callback;
const char * description;
void * opaque; void * opaque;
}; };

View File

@ -53,7 +53,7 @@ static void fps_earlyInit(void)
static bool fps_init(void ** udata, const void * params) static bool fps_init(void ** udata, const void * params)
{ {
app_registerKeybind(KEY_D, showFPSKeybind, NULL, "FPS display toggle"); app_registerKeybind(0, 'D', showFPSKeybind, NULL, "FPS display toggle");
showFPS = option_get_bool("win", "showFPS"); showFPS = option_get_bool("win", "showFPS");
return true; return true;
} }

View File

@ -78,7 +78,7 @@ static void graphs_earlyInit(void)
static bool graphs_init(void ** udata, const void * params) static bool graphs_init(void ** udata, const void * params)
{ {
app_overlayConfigRegister("Performance Metrics", configCallback, NULL); app_overlayConfigRegister("Performance Metrics", configCallback, NULL);
app_registerKeybind(KEY_T, showTimingKeybind, NULL, app_registerKeybind(0, 'T', showTimingKeybind, NULL,
"Show frame timing information"); "Show frame timing information");
return true; return true;
} }

View File

@ -63,13 +63,19 @@ static int help_render(void * udata, bool interactive, struct Rect * windowRects
igTableNextColumn(); igTableNextColumn();
igText("Toggle capture mode"); igText("Toggle capture mode");
for (int i = 0; i < KEY_MAX; ++i) KeybindHandle handle;
if (g_state.keyDescription[i]) ll_forEachNL(g_state.bindings, item, handle)
{ {
if (!handle->description)
continue;
igTableNextColumn(); igTableNextColumn();
igText("%s+%s", escapeName, linux_to_display[i]); if (handle->sc)
igText("%s+%s", escapeName, linux_to_display[handle->sc]);
else
igText("%s+%c", escapeName, handle->charcode);
igTableNextColumn(); igTableNextColumn();
igText(g_state.keyDescription[i]); igText(handle->description);
} }
igEndTable(); igEndTable();