Implement a solution for Linux/Wayland.

This works by scanning through all levels of a given key, then through
all modifier masks that can produce the given level, until we find a
mask that matches the one the user requested.
This commit is contained in:
Yukari Hafner 2026-06-17 13:58:07 +02:00
parent a27b7d32de
commit 2e147c7bbe
No known key found for this signature in database
GPG key ID: A65ADD33A582C0E2
3 changed files with 72 additions and 5 deletions

View file

@ -691,7 +691,13 @@ int _glfwInitWayland(void)
_glfw.wl.xkb.keymap_unref = (PFN_xkb_keymap_unref)
_glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_unref");
_glfw.wl.xkb.keymap_mod_get_index = (PFN_xkb_keymap_mod_get_index)
_glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index");
_glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_index");
_glfw.wl.xkb.keymap_mod_get_mask2 = (PFN_xkb_keymap_mod_get_mask2)
_glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_mod_get_mask2");
_glfw.wl.xkb.keymap_key_get_mods_for_level = (PFN_xkb_keymap_key_get_mods_for_level)
_glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_key_get_mods_for_level");
_glfw.wl.xkb.keymap_num_levels_for_key = (PFN_xkb_keymap_num_levels_for_key)
_glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_num_levels_for_key");
_glfw.wl.xkb.keymap_key_repeats = (PFN_xkb_keymap_key_repeats)
_glfwPlatformGetModuleSymbol(_glfw.wl.xkb.handle, "xkb_keymap_key_repeats");
_glfw.wl.xkb.keymap_key_get_syms_by_level = (PFN_xkb_keymap_key_get_syms_by_level)

View file

@ -185,6 +185,9 @@ typedef void (* PFN_xkb_context_unref)(struct xkb_context*);
typedef struct xkb_keymap* (* PFN_xkb_keymap_new_from_string)(struct xkb_context*, const char*, enum xkb_keymap_format, enum xkb_keymap_compile_flags);
typedef void (* PFN_xkb_keymap_unref)(struct xkb_keymap*);
typedef xkb_mod_index_t (* PFN_xkb_keymap_mod_get_index)(struct xkb_keymap*, const char*);
typedef xkb_mod_mask_t (* PFN_xkb_keymap_mod_get_mask2)(struct xkb_keymap*, const xkb_mod_index_t);
typedef xkb_level_index_t (* PFN_xkb_keymap_num_levels_for_key)(struct xkb_keymap*, const xkb_keycode_t, const xkb_layout_index_t);
typedef size_t (* PFN_xkb_keymap_key_get_mods_for_level)(struct xkb_keymap*, const xkb_keycode_t, const xkb_layout_index_t, const xkb_level_index_t, xkb_mod_mask_t *, const size_t);
typedef int (* PFN_xkb_keymap_key_repeats)(struct xkb_keymap*, xkb_keycode_t);
typedef int (* PFN_xkb_keymap_key_get_syms_by_level)(struct xkb_keymap*,xkb_keycode_t,xkb_layout_index_t,xkb_level_index_t,const xkb_keysym_t**);
typedef struct xkb_state* (* PFN_xkb_state_new)(struct xkb_keymap*);
@ -200,6 +203,9 @@ typedef int (* PFN_xkb_keysym_to_utf8)(xkb_keysym_t, char*, size_t);
#define xkb_keymap_new_from_string _glfw.wl.xkb.keymap_new_from_string
#define xkb_keymap_unref _glfw.wl.xkb.keymap_unref
#define xkb_keymap_mod_get_index _glfw.wl.xkb.keymap_mod_get_index
#define xkb_keymap_mod_get_mask2 _glfw.wl.xkb.keymap_mod_get_mask2
#define xkb_keymap_num_levels_for_key _glfw.wl.xkb.keymap_num_levels_for_key
#define xkb_keymap_key_get_mods_for_level _glfw.wl.xkb.keymap_key_get_mods_for_level
#define xkb_keymap_key_repeats _glfw.wl.xkb.keymap_key_repeats
#define xkb_keymap_key_get_syms_by_level _glfw.wl.xkb.keymap_key_get_syms_by_level
#define xkb_state_new _glfw.wl.xkb.state_new
@ -524,8 +530,11 @@ typedef struct _GLFWlibraryWayland
PFN_xkb_keymap_new_from_string keymap_new_from_string;
PFN_xkb_keymap_unref keymap_unref;
PFN_xkb_keymap_mod_get_index keymap_mod_get_index;
PFN_xkb_keymap_mod_get_mask2 keymap_mod_get_mask2;
PFN_xkb_keymap_key_repeats keymap_key_repeats;
PFN_xkb_keymap_key_get_syms_by_level keymap_key_get_syms_by_level;
PFN_xkb_keymap_num_levels_for_key keymap_num_levels_for_key;
PFN_xkb_keymap_key_get_mods_for_level keymap_key_get_mods_for_level;
PFN_xkb_state_new state_new;
PFN_xkb_state_unref state_unref;
PFN_xkb_state_key_get_syms state_key_get_syms;

View file

@ -2978,6 +2978,28 @@ void _glfwSetCursorModeWayland(_GLFWwindow* window, int mode)
_glfwSetCursorWayland(window, window->cursor);
}
// Translates an xkb modifier state mask
//
static xkb_mod_mask_t translateStateInverse(int mods)
{
xkb_mod_mask_t mask = 0;
if (mods & GLFW_MOD_SHIFT)
mask |= xkb_keymap_mod_get_mask2(_glfw.wl.xkb.keymap, _glfw.wl.xkb.shiftIndex);
if (mods & GLFW_MOD_CONTROL)
mask |= xkb_keymap_mod_get_mask2(_glfw.wl.xkb.keymap, _glfw.wl.xkb.controlIndex);
if (mods & GLFW_MOD_ALT)
mask |= xkb_keymap_mod_get_mask2(_glfw.wl.xkb.keymap, _glfw.wl.xkb.altIndex);
if (mods & GLFW_MOD_SUPER)
mask |= xkb_keymap_mod_get_mask2(_glfw.wl.xkb.keymap, _glfw.wl.xkb.superIndex);
if (mods & GLFW_MOD_CAPS_LOCK)
mask |= xkb_keymap_mod_get_mask2(_glfw.wl.xkb.keymap, _glfw.wl.xkb.capsLockIndex);
if (mods & GLFW_MOD_NUM_LOCK)
mask |= xkb_keymap_mod_get_mask2(_glfw.wl.xkb.keymap, _glfw.wl.xkb.numLockIndex);
return mask;
}
const char* _glfwGetScancodeNameWayland(int scancode, int modifiers)
{
if (scancode < 0 || scancode > 255)
@ -3002,14 +3024,44 @@ const char* _glfwGetScancodeNameWayland(int scancode, int modifiers)
return NULL;
}
// FIXME: can't seem to find any way in libxkbcommon to translate an xkb_mod_mask_t into
// a xkb_level_index_t . Sigh.
int level = (modifiers & GLFW_MOD_SHIFT)? 1 : 0;
xkb_level_index_t found_level = 0;
if (0 < modifiers)
{
// Figure out the corresponding level set for the modifier set
// that the user requested. This involves a relatively annoying
// loop of trying things out. Could cache this information if
// lookup speed is of concern.
xkb_mod_mask_t mask = translateStateInverse(modifiers);
const int num_levels = xkb_keymap_num_levels_for_key(_glfw.wl.xkb.keymap,
keycode,
layout);
xkb_level_index_t level;
for (level = 0; level < num_levels && found_level == 0; ++level)
{
size_t num_masks;
xkb_mod_mask_t masks[128];
num_masks = xkb_keymap_key_get_mods_for_level(_glfw.wl.xkb.keymap,
keycode,
layout,
level,
masks,
128);
for (int i=0; i < num_masks; ++i)
{
if ((masks[i] & mask) == mask)
{
found_level = level;
break;
}
}
}
}
const xkb_keysym_t* keysyms = NULL;
xkb_keymap_key_get_syms_by_level(_glfw.wl.xkb.keymap,
keycode,
layout,
level,
found_level,
&keysyms);
if (keysyms == NULL)
{