From bd288bd18fa94ffbda467196774010a8ca3b738c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 29 Nov 2021 19:26:16 +0530 Subject: [PATCH] Linux: Fix release event for the final key in a compose sequence not being reported. Fixes #4285 --- glfw/cocoa_window.m | 6 +++--- glfw/glfw3.h | 4 ++++ glfw/input.c | 40 +++++++++++++++++++++++++--------------- glfw/internal.h | 7 +------ glfw/xkb_glfw.c | 2 +- kitty/glfw-wrapper.h | 4 ++++ 6 files changed, 38 insertions(+), 25 deletions(-) diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 84c845a0e..39df56c2c 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -1250,7 +1250,7 @@ is_ascii_control_char(char x) { const bool process_text = !window->ns.textInputFilterCallback || window->ns.textInputFilterCallback(key, mods, keycode, flags) != 1; _glfw.ns.text[0] = 0; if (keycode == 0x33 /* backspace */ || keycode == 0x35 /* escape */) [self unmarkText]; - GLFWkeyevent glfw_keyevent = {.key = key, .native_key = keycode, .action = GLFW_PRESS, .mods = mods}; + GLFWkeyevent glfw_keyevent = {.key = key, .native_key = keycode, .native_key_id = keycode, .action = GLFW_PRESS, .mods = mods}; if (!_glfw.ns.unicodeData) { // Using the cocoa API for key handling is disabled, as there is no // reliable way to handle dead keys using it. Only use it if the @@ -1357,7 +1357,7 @@ is_ascii_control_char(char x) { action = GLFW_PRESS; } - GLFWkeyevent glfw_keyevent = {.key = key, .native_key = [event keyCode], .action = action, .mods = mods}; + GLFWkeyevent glfw_keyevent = {.key = key, .native_key = [event keyCode], .native_key_id = [event keyCode], .action = action, .mods = mods}; _glfwInputKeyboard(window, &glfw_keyevent); } @@ -1367,7 +1367,7 @@ is_ascii_control_char(char x) { const uint32_t key = translateKey(keycode, true); const int mods = translateFlags([event modifierFlags]); - GLFWkeyevent glfw_keyevent = {.key = key, .native_key = keycode, .action = GLFW_RELEASE, .mods = mods}; + GLFWkeyevent glfw_keyevent = {.key = key, .native_key = keycode, .native_key_id = keycode, .action = GLFW_RELEASE, .mods = mods}; add_alternate_keys(&glfw_keyevent, event); debug_key("\x1b[32mRelease:\x1b[m native_key: 0x%x (%s) glfw_key: 0x%x %s\n", keycode, safe_name_for_keycode(keycode), key, format_mods(mods)); diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 234cbd507..1218d7045 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1245,6 +1245,10 @@ typedef struct GLFWkeyevent // A value of GLFW_IME_PREEDIT_CHANGED means the pre-edit text for the input event has been changed. // A value of GLFW_IME_COMMIT_TEXT means the text should be committed. GLFWIMEState ime_state; + + // For internal use only. On Linux it is the actual keycode reported by the windowing system, in contrast + // to native_key which can be the result of a compose operation. On macOS it is the same as native_key. + uint32_t native_key_id; } GLFWkeyevent; /*! @brief The function pointer type for error callbacks. diff --git a/glfw/input.c b/glfw/input.c index 7e4bee864..624feaded 100644 --- a/glfw/input.c +++ b/glfw/input.c @@ -274,11 +274,11 @@ static bool parseMapping(_GLFWmapping* mapping, const char* string) ////////////////////////////////////////////////////////////////////////// static void -set_key_action(_GLFWwindow *window, uint32_t key, int val, int idx) { +set_key_action(_GLFWwindow *window, const GLFWkeyevent *ev, int action, int idx) { const unsigned sz = arraysz(window->activated_keys); if (idx < 0) { for (unsigned i = 0; i < sz; i++) { - if (window->activated_keys[i].key == 0) { + if (window->activated_keys[i].native_key_id == 0) { idx = i; break; } @@ -286,18 +286,18 @@ set_key_action(_GLFWwindow *window, uint32_t key, int val, int idx) { if (idx < 0) { idx = sz - 1; memmove(window->activated_keys, window->activated_keys + 1, sizeof(window->activated_keys[0]) * (sz - 1)); - window->activated_keys[sz - 1].key = key; + window->activated_keys[sz - 1].native_key_id = 0; } } - if (val == GLFW_RELEASE) { + if (action == GLFW_RELEASE) { memset(window->activated_keys + idx, 0, sizeof(window->activated_keys[0])); if (idx < (int)sz - 1) { memmove(window->activated_keys + idx, window->activated_keys + idx + 1, sizeof(window->activated_keys[0]) * (sz - 1 - idx)); memset(window->activated_keys + sz - 1, 0, sizeof(window->activated_keys[0])); } } else { - window->activated_keys[idx].key = key; - window->activated_keys[idx].action = val; + window->activated_keys[idx] = *ev; + window->activated_keys[idx].text = NULL; } } @@ -305,30 +305,39 @@ set_key_action(_GLFWwindow *window, uint32_t key, int val, int idx) { // void _glfwInputKeyboard(_GLFWwindow* window, GLFWkeyevent* ev) { - if (ev->key > 0) + if (ev->native_key_id > 0) { bool repeated = false; int idx = -1; int current_action = GLFW_RELEASE; const unsigned sz = arraysz(window->activated_keys); for (unsigned i = 0; i < sz; i++) { - if (window->activated_keys[i].key == ev->key) { + if (window->activated_keys[i].native_key_id == ev->native_key_id) { idx = i; current_action = window->activated_keys[i].action; break; } } - if (ev->action == GLFW_RELEASE && current_action == GLFW_RELEASE) - return; + if (ev->action == GLFW_RELEASE) { + if (current_action == GLFW_RELEASE) return; + if (idx > -1) { + const GLFWkeyevent *press_event = window->activated_keys + idx; + if (press_event->action == GLFW_PRESS || press_event->action == GLFW_REPEAT) { + // Compose sequences under X11 give a different key value for press and release events + // but we want the same key value so override it. + ev->native_key = press_event->native_key; + ev->key = press_event->key; + ev->shifted_key = press_event->shifted_key; + ev->alternate_key = press_event->alternate_key; + } + } + } if (ev->action == GLFW_PRESS && current_action == GLFW_PRESS) repeated = true; - if (ev->action == GLFW_RELEASE && window->stickyKeys) - set_key_action(window, ev->key, _GLFW_STICK, idx); - else - set_key_action(window, ev->key, ev->action, idx); + set_key_action(window, ev, (ev->action == GLFW_RELEASE && window->stickyKeys) ? _GLFW_STICK : ev->action, idx); if (repeated) ev->action = GLFW_REPEAT; @@ -823,7 +832,8 @@ GLFWAPI GLFWKeyAction glfwGetKey(GLFWwindow* handle, uint32_t key) if (current_action == _GLFW_STICK) { // Sticky mode: release key now - set_key_action(window, key, GLFW_RELEASE, idx); + GLFWkeyevent ev = {0}; + set_key_action(window, &ev, GLFW_RELEASE, idx); current_action = GLFW_PRESS; } diff --git a/glfw/internal.h b/glfw/internal.h index 12591cfba..9c505c613 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -402,11 +402,6 @@ struct _GLFWcontext _GLFWcontextOSMesa osmesa; }; -typedef struct GLFWKeyState { - uint32_t key; - char action; -} GLFWKeyState; - // Window and context structure // struct _GLFWwindow @@ -437,7 +432,7 @@ struct _GLFWwindow bool lockKeyMods; int cursorMode; char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1]; - GLFWKeyState activated_keys[16]; + GLFWkeyevent activated_keys[16]; // Virtual cursor position when cursor is disabled double virtualCursorPosX, virtualCursorPosY; bool rawMouseMotion; diff --git a/glfw/xkb_glfw.c b/glfw/xkb_glfw.c index 61871255e..c1be96ec6 100644 --- a/glfw/xkb_glfw.c +++ b/glfw/xkb_glfw.c @@ -866,7 +866,7 @@ glfw_xkb_handle_key_event(_GLFWwindow *window, _GLFWXKBData *xkb, xkb_keycode_t const xkb_keysym_t *syms, *clean_syms, *default_syms; xkb_keysym_t xkb_sym, shifted_xkb_sym = XKB_KEY_NoSymbol, alternate_xkb_sym = XKB_KEY_NoSymbol; xkb_keycode_t code_for_sym = xkb_keycode, ibus_keycode = xkb_keycode; - GLFWkeyevent glfw_ev = {.action = GLFW_PRESS}; + GLFWkeyevent glfw_ev = {.action = GLFW_PRESS, .native_key_id = xkb_keycode}; #ifdef _GLFW_WAYLAND code_for_sym += 8; #else diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 16d52ad96..103e3dee5 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -983,6 +983,10 @@ typedef struct GLFWkeyevent // A value of GLFW_IME_PREEDIT_CHANGED means the pre-edit text for the input event has been changed. // A value of GLFW_IME_COMMIT_TEXT means the text should be committed. GLFWIMEState ime_state; + + // For internal use only. On Linux it is the actual keycode reported by the windowing system, in contrast + // to native_key which can be the result of a compose operation. On macOS it is the same as native_key. + uint32_t native_key_id; } GLFWkeyevent; /*! @brief The function pointer type for error callbacks.