From be03da0c544d63623564c0dbce471860fd6d22ef Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 25 Aug 2023 16:08:34 +0530 Subject: [PATCH] macOS: Fix keyboard shortcuts in the Apple global menubar not being changed when reloading the config --- docs/changelog.rst | 2 ++ kitty/boss.py | 20 ++++++++++++++++---- kitty/cocoa_window.m | 23 +++++++++++++++++++++-- kitty/fast_data_types.pyi | 2 ++ kitty/glfw.c | 16 ++++++++++++++++ kitty/main.py | 13 ++++++++++--- 6 files changed, 67 insertions(+), 9 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 1018d9a32..42d241c1b 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -51,6 +51,8 @@ Detailed list of changes - kitten icat: Fix image being displayed one cell to the right when using both ``--place`` and ``--unicode-placeholder`` (:iss:`6556`) +- macOS: Fix keyboard shortcuts in the Apple global menubar not being changed when reloading the config + - Fix a crash when resizing an OS Window that is displaying more than one image and the new size is smaller than the image needs (:iss:`6555`) - Remote control: Allow using a random TCP port as the remote control socket and also allow using TCP sockets in :opt:`listen_on` diff --git a/kitty/boss.py b/kitty/boss.py index 01883c5ee..2498cbe8a 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -369,15 +369,21 @@ def __init__( ) set_boss(self) self.args = args - self.global_shortcuts_map: KeyMap = {v: k for k, v in global_shortcuts.items()} - self.global_shortcuts = global_shortcuts self.mouse_handler: Optional[Callable[[WindowSystemMouseEvent], None]] = None - self.update_keymap() + self.update_keymap(global_shortcuts) if is_macos: from .fast_data_types import cocoa_set_notification_activated_callback cocoa_set_notification_activated_callback(notification_activated) - def update_keymap(self) -> None: + def update_keymap(self, global_shortcuts:Optional[Dict[str, SingleKey]] = None) -> None: + if global_shortcuts is None: + if is_macos: + from .main import set_cocoa_global_shortcuts + global_shortcuts = set_cocoa_global_shortcuts(get_options()) + else: + global_shortcuts = {} + self.global_shortcuts_map: KeyMap = {v: k for k, v in global_shortcuts.items()} + self.global_shortcuts = global_shortcuts self.keymap = get_options().keymap.copy() for sc in self.global_shortcuts.values(): self.keymap.pop(sc, None) @@ -2426,7 +2432,13 @@ def apply_new_options(self, opts: Options) -> None: os_window_font_size(os_window_id, opts.font_size, True) tm.resize() # Update key bindings + if is_macos: + from .fast_data_types import cocoa_clear_global_shortcuts + cocoa_clear_global_shortcuts() self.update_keymap() + if is_macos: + from .fast_data_types import cocoa_recreate_global_menu + cocoa_recreate_global_menu() # Update misc options try: set_background_image(opts.background_image, tuple(self.os_window_map), True, opts.background_image_layout) diff --git a/kitty/cocoa_window.m b/kitty/cocoa_window.m index ae40efa84..8ea47d145 100644 --- a/kitty/cocoa_window.m +++ b/kitty/cocoa_window.m @@ -558,6 +558,7 @@ - (BOOL)openFileURLs:(NSPasteboard*)pasteboard @end // global menu {{{ + void cocoa_create_global_menu(void) { NSString* app_name = find_app_name(); @@ -697,7 +698,25 @@ - (BOOL)openFileURLs:(NSPasteboard*)pasteboard NSMenu *m = [[NSMenu alloc] initWithTitle:[NSString stringWithFormat:@" :: %@", title]]; [title_menu setSubmenu:m]; [m release]; -} // }}} +} + +void +cocoa_clear_global_shortcuts(void) { + memset(&global_shortcuts, 0, sizeof(global_shortcuts)); +} + +void +cocoa_recreate_global_menu(void) { + if (title_menu != NULL) { + NSMenu *bar = [NSApp mainMenu]; + [bar removeItem:title_menu]; + } + title_menu = NULL; + cocoa_create_global_menu(); +} + + +// }}} #define NSLeftAlternateKeyMask (0x000020 | NSEventModifierFlagOption) #define NSRightAlternateKeyMask (0x000040 | NSEventModifierFlagOption) @@ -946,7 +965,7 @@ - (BOOL)openFileURLs:(NSPasteboard*)pasteboard bool init_cocoa(PyObject *module) { - memset(&global_shortcuts, 0, sizeof(global_shortcuts)); + cocoa_clear_global_shortcuts(); if (PyModule_AddFunctions(module, module_methods) != 0) return false; register_at_exit_cleanup_func(COCOA_CLEANUP_FUNC, cleanup); return true; diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index f2f775eb1..aaceac0ee 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -1537,3 +1537,5 @@ def mask_kitty_signals_process_wide() -> None: ... def is_modifier_key(key: int) -> bool: ... def base64_encode(src: Union[bytes,str], add_padding: bool = False) -> bytes: ... def base64_decode(src: Union[bytes,str]) -> bytes: ... +def cocoa_recreate_global_menu() -> None: ... +def cocoa_clear_global_shortcuts() -> None: ... diff --git a/kitty/glfw.c b/kitty/glfw.c index 6e41861aa..33ce3a3c8 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -18,11 +18,13 @@ extern void cocoa_focus_window(void *w); extern long cocoa_window_number(void *w); extern void cocoa_create_global_menu(void); +extern void cocoa_recreate_global_menu(void); extern void cocoa_system_beep(const char*); extern void cocoa_set_activation_policy(bool); extern bool cocoa_alt_option_key_pressed(unsigned long); extern void cocoa_toggle_secure_keyboard_entry(void); extern void cocoa_hide(void); +extern void cocoa_clear_global_shortcuts(void); extern void cocoa_hide_others(void); extern void cocoa_minimize(void *w); extern void cocoa_set_uncaught_exception_handler(void); @@ -1751,6 +1753,17 @@ request_frame_render(OSWindow *w) { w->render_state = RENDER_FRAME_REQUESTED; } +static PyObject* +py_recreate_global_menu(PyObject *self UNUSED, PyObject *args UNUSED) { + cocoa_recreate_global_menu(); + Py_RETURN_NONE; +} + +static PyObject* +py_clear_global_shortcuts(PyObject *self UNUSED, PyObject *args UNUSED) { + cocoa_clear_global_shortcuts(); + Py_RETURN_NONE; +} #else static void @@ -1955,6 +1968,9 @@ static PyMethodDef module_methods[] = { METHODB(strip_csi, METH_O), #ifndef __APPLE__ METHODB(dbus_send_notification, METH_VARARGS), +#else + {"cocoa_recreate_global_menu", (PyCFunction)py_recreate_global_menu, METH_NOARGS, ""}, + {"cocoa_clear_global_shortcuts", (PyCFunction)py_clear_global_shortcuts, METH_NOARGS, ""}, #endif METHODB(cocoa_window_id, METH_O), METHODB(cocoa_hide_app, METH_NOARGS), diff --git a/kitty/main.py b/kitty/main.py index 61464e827..c984ae591 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -214,7 +214,7 @@ def set_x11_window_icon() -> None: log_error(err) -def _run_app(opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine] = ()) -> None: +def set_cocoa_global_shortcuts(opts: Options) -> Dict[str, SingleKey]: global_shortcuts: Dict[str, SingleKey] = {} if is_macos: from collections import defaultdict @@ -241,13 +241,20 @@ def _run_app(opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine] = ()) val = get_macos_shortcut_for(func_map, f'open_url {website_url()}', lookup_name='open_kitty_website') if val is not None: global_shortcuts['open_kitty_website'] = val + return global_shortcuts + +def _run_app(opts: Options, args: CLIOptions, bad_lines: Sequence[BadLine] = ()) -> None: + if is_macos: + global_shortcuts = set_cocoa_global_shortcuts(opts) if opts.macos_custom_beam_cursor: set_custom_ibeam_cursor() set_macos_app_custom_icon() + else: + global_shortcuts = {} + if not is_wayland(): # no window icons on wayland + set_x11_window_icon() - if not is_wayland() and not is_macos: # no window icons on wayland - set_x11_window_icon() with cached_values_for(run_app.cached_values_name) as cached_values: startup_sessions = tuple(create_sessions(opts, args, default_session=opts.startup_session)) wincls = (startup_sessions[0].os_window_class if startup_sessions else '') or args.cls or appname