From 901e00cab11d2fc6004af038aaba89b68f31b925 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 25 Sep 2022 18:11:17 +0530 Subject: [PATCH] Cleanup previous PR 1) Dont use deprecated code 2) Always set the dock icon on startup as the dock icon doesnt change till the dock is restarted 3) Update the app icon automatically if the mtime on the custom icon in the config dir is newer than the mtime of the sentinel file apple puts inside the application bundle to indicate it has a custom icon --- docs/changelog.rst | 3 ++- docs/faq.rst | 20 +++++++++++------- kitty/cocoa_window.m | 34 ------------------------------- kitty/fast_data_types.pyi | 4 ---- kitty/main.py | 43 ++++++++++++++++++++++++--------------- 5 files changed, 42 insertions(+), 62 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 014aebfee..a56c968cd 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -38,9 +38,10 @@ Detailed list of changes 0.26.4 [future] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- macOS: Allow changing the kitty icon by placing a custom icon in the kitty config folder (:pull:`5464`) + - X11: Fix a regression in the previous release that caused pasting from GTK based applications to have extra newlines (:iss:`5528`) -- macOS: Allow to set custom app icon automatically (:pull:`5464`) 0.26.3 [2022-09-22] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/faq.rst b/docs/faq.rst index db916a128..61170b84c 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -289,12 +289,16 @@ homepage: On macOS you can put :file:`kitty.app.icns` or :file:`kitty.app.png` in the :ref:`kitty configuration directory `, and this icon will be applied -automatically at startup if the :file:`kitty.app` bundle has no custom icon. This is -convenient because updates under macOS replace the entire :file:`kitty.app` bundle -and the custom icon will be removed as well. To automatically update a new icon -at startup, you need to remove the custom icon on :file:`kitty.app` first. +automatically at startup. Unfortunately, Apple's Dock does not change its +cached icon so the custom icon will revert when kitty is quit. Run the +following to force the Dock to update its cached icons: -You can set custom icon via CLI, which can be used in shell scripts: +.. code-block:: sh + + rm /var/folders/*/*/*/com.apple.dock.iconcache; killall Dock + +If you prefer not to keep a custom icon in the kitty config folder, you can +also set it with the following command: .. code-block:: sh @@ -308,9 +312,11 @@ You can also change the icon manually by following the steps: #. Find :file:`kitty.app` in the Applications folder, select it and press :kbd:`⌘+I` #. Drag :file:`kitty.icns` onto the application icon in the kitty info pane -#. Delete the icon cache and restart Dock:: +#. Delete the icon cache and restart Dock: - $ rm /var/folders/*/*/*/com.apple.dock.iconcache; killall Dock +.. code-block:: sh + + rm /var/folders/*/*/*/com.apple.dock.iconcache; killall Dock How do I map key presses in kitty to different keys in the terminal program? diff --git a/kitty/cocoa_window.m b/kitty/cocoa_window.m index 11ec6bcc8..32d761c67 100644 --- a/kitty/cocoa_window.m +++ b/kitty/cocoa_window.m @@ -862,39 +862,6 @@ cocoa_set_url_handler(PyObject UNUSED *self, PyObject *args) { } // autoreleasepool } -static PyObject* -cocoa_app_has_custom_icon(PyObject UNUSED *self, PyObject *args) { - @autoreleasepool { - - const char *app_path = NULL; - if (!PyArg_ParseTuple(args, "|z", &app_path)) return NULL; - NSString *bundle_path; - if (app_path && app_path[0] != '\0') bundle_path = [NSString stringWithUTF8String:app_path]; - else bundle_path = [[NSBundle mainBundle] bundlePath]; - if (!bundle_path || bundle_path.length == 0) bundle_path = @"/Applications/kitty.app"; - - // These APIs have been marked as deprecated. - // However support for NSURLCustomIconKey has never been implemented by Apple (so far, macOS 12.5.x and below). - // so the following NSImage icon_image will be nil even if a custom icon is set: - // [[NSURL fileURLWithPath:bundle_path] getResourceValue:&icon_image forKey:NSURLCustomIconKey error:nil] -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - FSRef ref; - FSCatalogInfo catalog_info; - OSStatus err = FSPathMakeRef((const UInt8 *)[bundle_path fileSystemRepresentation], &ref, NULL); - if (err == noErr) { - err = FSGetCatalogInfo(&ref, kFSCatInfoFinderInfo, &catalog_info, NULL, NULL, NULL); - if (err == noErr) { - FileInfo *file_info = (FileInfo *)(&catalog_info.finderInfo); - if ((file_info->finderFlags & kHasCustomIcon) != 0) Py_RETURN_TRUE; - } - } -#pragma clang diagnostic pop - Py_RETURN_FALSE; - - } // autoreleasepool -} - static PyObject* cocoa_set_app_icon(PyObject UNUSED *self, PyObject *args) { @autoreleasepool { @@ -1015,7 +982,6 @@ static PyMethodDef module_methods[] = { {"cocoa_send_notification", (PyCFunction)cocoa_send_notification, METH_VARARGS, ""}, {"cocoa_set_notification_activated_callback", (PyCFunction)set_notification_activated_callback, METH_O, ""}, {"cocoa_set_url_handler", (PyCFunction)cocoa_set_url_handler, METH_VARARGS, ""}, - {"cocoa_app_has_custom_icon", (PyCFunction)cocoa_app_has_custom_icon, METH_VARARGS, ""}, {"cocoa_set_app_icon", (PyCFunction)cocoa_set_app_icon, METH_VARARGS, ""}, {"cocoa_set_dock_icon", (PyCFunction)cocoa_set_dock_icon, METH_VARARGS, ""}, {NULL, NULL, 0, NULL} /* Sentinel */ diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 6b00983c4..8f2717a81 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -734,10 +734,6 @@ def cocoa_set_url_handler(url_scheme: str, bundle_id: Optional[str] = None) -> N pass -def cocoa_app_has_custom_icon(app_path: Optional[str] = None) -> bool: - pass - - def cocoa_set_app_icon(icon_path: str, app_path: Optional[str] = None) -> None: pass diff --git a/kitty/main.py b/kitty/main.py index b29968760..e49a6c539 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -16,14 +16,13 @@ from .cli_stub import CLIOptions from .conf.utils import BadLine from .config import cached_values_for from .constants import ( - appname, beam_cursor_data_file, clear_handled_signals, config_dir, - glfw_path, is_macos, is_wayland, kitty_exe, logo_png_file, - running_in_kitty, website_url + appname, beam_cursor_data_file, clear_handled_signals, config_dir, glfw_path, + is_macos, is_wayland, kitty_exe, logo_png_file, running_in_kitty, website_url, ) from .fast_data_types import ( - GLFW_IBEAM_CURSOR, GLFW_MOD_ALT, GLFW_MOD_SHIFT, SingleKey, - create_os_window, free_font_data, glfw_init, glfw_terminate, load_png_data, - set_custom_cursor, set_default_window_icon, set_options + GLFW_IBEAM_CURSOR, GLFW_MOD_ALT, GLFW_MOD_SHIFT, SingleKey, create_os_window, + free_font_data, glfw_init, glfw_terminate, load_png_data, set_custom_cursor, + set_default_window_icon, set_options, ) from .fonts.box_drawing import set_scale from .fonts.render import set_font_family @@ -33,8 +32,8 @@ from .os_window_size import initial_window_size_func from .prewarm import PrewarmProcess, fork_prewarm_process from .session import create_sessions, get_os_window_sizing_data from .utils import ( - cleanup_ssh_control_masters, detach, expandvars, log_error, - single_instance, startup_notification_handler, unix_socket_paths + cleanup_ssh_control_masters, detach, expandvars, log_error, single_instance, + startup_notification_handler, unix_socket_paths, ) from .window import load_shader_programs @@ -135,17 +134,29 @@ def get_macos_shortcut_for( return ans +def safe_mtime(path: str) -> Optional[float]: + with suppress(OSError): + return os.path.getmtime(path) + return None + + def set_macos_app_custom_icon() -> None: for name in ('kitty.app.icns', 'kitty.app.png'): icon_path = os.path.join(config_dir, name) - if os.path.exists(icon_path): - from .fast_data_types import cocoa_app_has_custom_icon, cocoa_set_app_icon, cocoa_set_dock_icon - if not cocoa_app_has_custom_icon(): + custom_icon_mtime = safe_mtime(icon_path) + if custom_icon_mtime is not None: + from .fast_data_types import cocoa_set_app_icon, cocoa_set_dock_icon + krd = getattr(sys, 'kitty_run_data') + bundle_path = os.path.dirname(os.path.dirname(krd.get('bundle_exe_dir'))) + icon_sentinel = os.path.join(bundle_path, 'Icon\r') + sentinel_mtime = safe_mtime(icon_sentinel) + if sentinel_mtime is None or sentinel_mtime < custom_icon_mtime: cocoa_set_app_icon(icon_path) - # kitty dock icon doesn't refresh automatically, so set it explicitly - # This has the drawback that the dock icon reverts to the original icon after exiting the application, - # even if the custom icon has been successfully updated, until the next launch. - cocoa_set_dock_icon(icon_path) + # macOS Dock does not reload icons until it is restarted, so we set + # the application icon here. This will revert when kitty quits, but + # cant be helped since there appears to be no way to get the dock + # to reload short of killing it. + cocoa_set_dock_icon(icon_path) break @@ -225,7 +236,7 @@ class AppRunner: free_font_data() # must free font data before glfw/freetype/fontconfig/opengl etc are finalized if is_macos: from kitty.fast_data_types import ( - cocoa_set_notification_activated_callback + cocoa_set_notification_activated_callback, ) cocoa_set_notification_activated_callback(None)