From ca4840717b6cd86f154478419fb15dd576bfb57e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 15 Jan 2022 13:56:18 +0530 Subject: [PATCH] macOS: Fix using shortcuts from the global menu bar as subsequent key presses in a multi key mapping not working Fixes #4519 --- docs/changelog.rst | 3 +++ glfw/cocoa_init.m | 32 +++++++++++++++++--------------- glfw/cocoa_window.m | 2 +- glfw/glfw3.h | 3 +++ glfw/input.c | 8 ++++++++ glfw/internal.h | 2 ++ kitty/glfw-wrapper.c | 6 ++++++ kitty/glfw-wrapper.h | 8 ++++++++ kitty/glfw.c | 5 +++++ kitty/state.c | 6 +++++- kitty/state.h | 1 + 11 files changed, 59 insertions(+), 17 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index c717157c2..e2ded6385 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -114,6 +114,9 @@ Detailed list of changes - macOS: Fix a few key-presses causing beeps from cocoa text input system (:iss:`4489`) +- macOS: Fix using shortcuts from the global menu bar as subsequent key presses + in a multi key mapping not working (:iss:`4519`) + 0.24.1 [2022-01-06] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/glfw/cocoa_init.m b/glfw/cocoa_init.m index 07d096d4a..0ce7bed21 100644 --- a/glfw/cocoa_init.m +++ b/glfw/cocoa_init.m @@ -765,25 +765,27 @@ int _glfwPlatformInit(void) { debug_key("---------------- key down -------------------\n"); debug_key("%s\n", [[event description] UTF8String]); - // first check if there is global menu bar shortcut - if ([[NSApp mainMenu] performKeyEquivalent:event]) { - debug_key("keyDown triggerred global menu bar action ignoring\n"); - last_keydown_shortcut_event.virtual_key_code = [event keyCode]; - last_keydown_shortcut_event.timestamp = [event timestamp]; - return nil; - } - // now check if there is a useful apple shortcut - int global_shortcut = is_active_apple_global_shortcut(event); - if (is_useful_apple_global_shortcut(global_shortcut)) { - debug_key("keyDown triggerred global macOS shortcut ignoring\n"); - last_keydown_shortcut_event.virtual_key_code = [event keyCode]; - last_keydown_shortcut_event.timestamp = [event timestamp]; - return event; + if (!_glfw.ignoreOSKeyboardProcessing) { + // first check if there is a global menu bar shortcut + if (_glfw.ignoreOSKeyboardProcessing && [[NSApp mainMenu] performKeyEquivalent:event]) { + debug_key("keyDown triggerred global menu bar action ignoring\n"); + last_keydown_shortcut_event.virtual_key_code = [event keyCode]; + last_keydown_shortcut_event.timestamp = [event timestamp]; + return nil; + } + // now check if there is a useful apple shortcut + int global_shortcut = is_active_apple_global_shortcut(event); + if (is_useful_apple_global_shortcut(global_shortcut)) { + debug_key("keyDown triggerred global macOS shortcut ignoring\n"); + last_keydown_shortcut_event.virtual_key_code = [event keyCode]; + last_keydown_shortcut_event.timestamp = [event timestamp]; + return event; + } } last_keydown_shortcut_event.virtual_key_code = 0xffff; NSWindow *kw = [NSApp keyWindow]; if (kw && kw.contentView) [kw.contentView keyDown:event]; - else debug_key("keyUp ignored as no keyWindow present\n"); + else debug_key("keyDown ignored as no keyWindow present\n"); return nil; }; diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index e8f58fbe7..fa38e3f55 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -1112,7 +1112,7 @@ is_ascii_control_char(char x) { const NSUInteger flags = [event modifierFlags]; const int mods = translateFlags(flags); const uint32_t key = translateKey(keycode, true); - const bool process_text = !window->ns.textInputFilterCallback || window->ns.textInputFilterCallback(key, mods, keycode, flags) != 1; + const bool process_text = !_glfw.ignoreOSKeyboardProcessing && (!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, .native_key_id = keycode, .action = GLFW_PRESS, .mods = mods}; diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 1218d7045..3f10e1e18 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -4119,6 +4119,9 @@ GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* */ GLFWAPI void glfwPostEmptyEvent(void); +GLFWAPI bool glfwGetIgnoreOSKeyboardProcessing(void); +GLFWAPI void glfwSetIgnoreOSKeyboardProcessing(bool enabled); + /*! @brief Returns the value of an input option for the specified window. * * This function returns the value of an input option for the specified window. diff --git a/glfw/input.c b/glfw/input.c index 624feaded..eff7250da 100644 --- a/glfw/input.c +++ b/glfw/input.c @@ -667,6 +667,14 @@ void _glfwCenterCursorInContentArea(_GLFWwindow* window) ////// GLFW public API ////// ////////////////////////////////////////////////////////////////////////// +GLFWAPI bool glfwGetIgnoreOSKeyboardProcessing(void) { + return _glfw.ignoreOSKeyboardProcessing; +} + +GLFWAPI void glfwSetIgnoreOSKeyboardProcessing(bool enabled) { + _glfw.ignoreOSKeyboardProcessing = enabled; +} + GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) { _GLFWwindow* window = (_GLFWwindow*) handle; diff --git a/glfw/internal.h b/glfw/internal.h index 9c505c613..7e7c76c76 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -584,6 +584,8 @@ struct _GLFWlibrary _GLFWtls contextSlot; _GLFWmutex errorLock; + bool ignoreOSKeyboardProcessing; + struct { bool available; void* handle; diff --git a/kitty/glfw-wrapper.c b/kitty/glfw-wrapper.c index 25188c65b..46f0ba106 100644 --- a/kitty/glfw-wrapper.c +++ b/kitty/glfw-wrapper.c @@ -249,6 +249,12 @@ load_glfw(const char* path) { *(void **) (&glfwPostEmptyEvent_impl) = dlsym(handle, "glfwPostEmptyEvent"); if (glfwPostEmptyEvent_impl == NULL) fail("Failed to load glfw function glfwPostEmptyEvent with error: %s", dlerror()); + *(void **) (&glfwGetIgnoreOSKeyboardProcessing_impl) = dlsym(handle, "glfwGetIgnoreOSKeyboardProcessing"); + if (glfwGetIgnoreOSKeyboardProcessing_impl == NULL) fail("Failed to load glfw function glfwGetIgnoreOSKeyboardProcessing with error: %s", dlerror()); + + *(void **) (&glfwSetIgnoreOSKeyboardProcessing_impl) = dlsym(handle, "glfwSetIgnoreOSKeyboardProcessing"); + if (glfwSetIgnoreOSKeyboardProcessing_impl == NULL) fail("Failed to load glfw function glfwSetIgnoreOSKeyboardProcessing with error: %s", dlerror()); + *(void **) (&glfwGetInputMode_impl) = dlsym(handle, "glfwGetInputMode"); if (glfwGetInputMode_impl == NULL) fail("Failed to load glfw function glfwGetInputMode with error: %s", dlerror()); diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 103e3dee5..0b9b1750b 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1916,6 +1916,14 @@ typedef void (*glfwPostEmptyEvent_func)(void); GFW_EXTERN glfwPostEmptyEvent_func glfwPostEmptyEvent_impl; #define glfwPostEmptyEvent glfwPostEmptyEvent_impl +typedef bool (*glfwGetIgnoreOSKeyboardProcessing_func)(void); +GFW_EXTERN glfwGetIgnoreOSKeyboardProcessing_func glfwGetIgnoreOSKeyboardProcessing_impl; +#define glfwGetIgnoreOSKeyboardProcessing glfwGetIgnoreOSKeyboardProcessing_impl + +typedef void (*glfwSetIgnoreOSKeyboardProcessing_func)(bool); +GFW_EXTERN glfwSetIgnoreOSKeyboardProcessing_func glfwSetIgnoreOSKeyboardProcessing_impl; +#define glfwSetIgnoreOSKeyboardProcessing glfwSetIgnoreOSKeyboardProcessing_impl + typedef int (*glfwGetInputMode_func)(GLFWwindow*, int); GFW_EXTERN glfwGetInputMode_func glfwGetInputMode_impl; #define glfwGetInputMode glfwGetInputMode_impl diff --git a/kitty/glfw.c b/kitty/glfw.c index 26c7fc412..d269eb114 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -1564,6 +1564,11 @@ strip_csi(PyObject *self UNUSED, PyObject *src) { return PyUnicode_FromString(buf); } +void +set_ignore_os_keyboard_processing(bool enabled) { + glfwSetIgnoreOSKeyboardProcessing(enabled); +} + // Boilerplate {{{ static PyMethodDef module_methods[] = { diff --git a/kitty/state.c b/kitty/state.c index 1de2752eb..c221d5d2d 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -703,7 +703,11 @@ PYWRAP1(set_options) { Py_RETURN_NONE; } -BOOL_SET(in_sequence_mode) +PYWRAP1(set_in_sequence_mode) { + global_state.in_sequence_mode = PyObject_IsTrue(args); + set_ignore_os_keyboard_processing(global_state.in_sequence_mode); + Py_RETURN_NONE; +} static void init_screen_render_data(OSWindow *osw, const WindowGeometry *g, ScreenRenderData *d) { diff --git a/kitty/state.h b/kitty/state.h index db88486e2..1631ed502 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -337,3 +337,4 @@ uint8_t* draw_single_ascii_char(const char ch, size_t *result_width, size_t *res bool is_os_window_fullscreen(OSWindow *); void update_ime_position(Window* w, Screen *screen); bool update_ime_position_for_window(id_type window_id, bool force); +void set_ignore_os_keyboard_processing(bool enabled);