[client] keybind: use physical keycodes for layout-independent hotkeys

Fixes #1298
This commit is contained in:
Geoffrey McRae
2026-07-30 10:21:26 +10:00
parent deb447c3ce
commit e754f5841e
14 changed files with 45 additions and 100 deletions

View File

@@ -39,7 +39,6 @@
#include <stdarg.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#define SHADER_MOUSE_VALID (UINT32_C(1) << 31)
@@ -465,11 +464,9 @@ void app_handleKeyPressInternal(int sc)
{
g_state.escapeAction = sc;
KeybindHandle handle;
int charcode = g_state.ds->getCharCode(sc);
ll_forEachNL(g_state.bindings, item, handle)
{
if ((handle->sc && handle->sc == sc ) ||
(handle->charcode && handle->charcode == charcode))
if (handle->sc == sc)
{
handle->callback(sc, handle->opaque);
break;
@@ -849,18 +846,12 @@ void app_showRecord(bool show)
overlayStatus_set(LG_USER_STATUS_RECORDING, show);
}
KeybindHandle app_registerKeybind(int sc, int charcode, KeybindFn callback,
void * opaque, const char * description)
KeybindHandle app_registerKeybind(int sc, KeybindFn callback, void * opaque,
const char * description)
{
if (charcode != 0 && sc != 0)
if (sc <= KEY_RESERVED || sc >= KEY_MAX || !linux_to_display[sc])
{
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");
DEBUG_ERROR("invalid keybind keycode: %d", sc);
return NULL;
}
@@ -869,8 +860,7 @@ KeybindHandle app_registerKeybind(int sc, int charcode, KeybindFn callback,
// don't allow duplicate binds
ll_forEachNL(g_state.bindings, item, handle)
{
if ((sc && handle->sc == sc ) ||
(charcode && handle->charcode == charcode))
if (handle->sc == sc)
{
DEBUG_INFO("Key already bound");
return NULL;
@@ -885,7 +875,6 @@ KeybindHandle app_registerKeybind(int sc, int charcode, KeybindFn callback,
}
handle->sc = sc;
handle->charcode = charcode;
handle->callback = callback;
handle->description = description;
handle->opaque = opaque;

View File

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

View File

@@ -254,7 +254,6 @@ struct CBRequest
struct KeybindHandle
{
int sc;
int charcode;
KeybindFn callback;
const char * description;
void * opaque;

View File

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

View File

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

View File

@@ -70,10 +70,7 @@ static int help_render(void * udata, bool interactive, struct Rect * windowRects
continue;
igTableNextColumn();
if (handle->sc)
igText("%s+%s", escapeName, linux_to_display[handle->sc]);
else
igText("%s+%c", escapeName, handle->charcode);
igText("%s+%s", escapeName, linux_to_display[handle->sc]);
igTableNextColumn();
igTextUnformatted(handle->description, NULL);
}