diff --git a/docs/changelog.rst b/docs/changelog.rst index 87f4ab1b8..fca70e345 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -70,6 +70,10 @@ Changelog - Add support for the CSI t escape code to query window and cell sizes (:iss:`581`) +- Linux: When using layouts that map the keys to non-ascii characters, + map shortcuts using the ascii equivalents, from the default layout. + (:iss:`606`) + - Linux: Fix fonts not being correctly read from TrueType Collection (.ttc) files (:iss:`577`) diff --git a/glfw/xkb_glfw.c b/glfw/xkb_glfw.c index 8a256ec14..92d1c01f8 100644 --- a/glfw/xkb_glfw.c +++ b/glfw/xkb_glfw.c @@ -181,6 +181,10 @@ glfw_xkb_release(_GLFWXKBData *xkb) { xkb_keymap_unref(xkb->keymap); xkb->keymap = NULL; } + if (xkb->default_keymap) { + xkb_keymap_unref(xkb->default_keymap); + xkb->default_keymap = NULL; + } if (xkb->state) { xkb_state_unref(xkb->state); xkb->state = NULL; @@ -189,6 +193,10 @@ glfw_xkb_release(_GLFWXKBData *xkb) { xkb_state_unref(xkb->clean_state); xkb->clean_state = NULL; } + if (xkb->default_state) { + xkb_state_unref(xkb->default_state); + xkb->default_state = NULL; + } if (xkb->context) { xkb_context_unref(xkb->context); xkb->context = NULL; @@ -272,6 +280,21 @@ glfw_xkb_compile_keymap(_GLFWXKBData *xkb, const char *map_str) { xkb->modifiers = 0; xkb->activeUnknownModifiers = 0; } + if (!xkb->default_keymap && xkb->context) { + // The system default keymap, can be overridden by the XKB_DEFAULT_RULES + // env var, see + // https://xkbcommon.org/doc/current/structxkb__rule__names.html + static struct xkb_rule_names default_rule_names = {0}; + xkb->default_keymap = xkb_keymap_new_from_names(xkb->context, &default_rule_names, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (xkb->default_keymap) { + xkb->default_state = xkb_state_new(keymap); + if (!xkb->default_state) { + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create default XKB state, fallback key processing is unavailable"); + } + } else { + _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to create default XKB keymap, fallback key processing is unavailable"); + } + } return ok; } @@ -379,7 +402,7 @@ format_xkb_mods(_GLFWXKBData *xkb, const char* name, xkb_mod_mask_t mods) { void glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t scancode, int action) { - const xkb_keysym_t *syms, *clean_syms; + const xkb_keysym_t *syms, *clean_syms, *default_syms; xkb_keysym_t glfw_sym; xkb_keycode_t code_for_sym = scancode; #ifdef _GLFW_WAYLAND @@ -422,6 +445,15 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t if (text[0]) { debug("%s: %s ", text_type, text); } } int glfw_keycode = glfw_key_for_sym(glfw_sym); - debug("%sglfw_key: %s\n", format_mods(xkb->modifiers), _glfwGetKeyName(glfw_keycode)); + if (glfw_keycode == GLFW_KEY_UNKNOWN) { + int num_default_syms = xkb_state_key_get_syms(xkb->default_state, code_for_sym, &default_syms); + if (num_default_syms > 0) { + glfw_sym = default_syms[0]; + glfw_keycode = glfw_key_for_sym(glfw_sym); + } + debug("%sglfw_fallback_key: %s\n", format_mods(xkb->modifiers), _glfwGetKeyName(glfw_keycode)); + } else { + debug("%sglfw_key: %s\n", format_mods(xkb->modifiers), _glfwGetKeyName(glfw_keycode)); + } _glfwInputKeyboard(window, glfw_keycode, glfw_sym, action, xkb->modifiers, text, 0); } diff --git a/glfw/xkb_glfw.h b/glfw/xkb_glfw.h index 083bafab6..a80efe161 100644 --- a/glfw/xkb_glfw.h +++ b/glfw/xkb_glfw.h @@ -35,8 +35,10 @@ typedef struct { struct xkb_context* context; struct xkb_keymap* keymap; + struct xkb_keymap* default_keymap; struct xkb_state* state; struct xkb_state* clean_state; + struct xkb_state* default_state; struct xkb_compose_state* composeState; xkb_mod_index_t controlIdx;