macOS: Allow to set custom app icon automatically
This commit is contained in:
parent
98eacb2067
commit
a1029418f8
@ -40,6 +40,8 @@ Detailed list of changes
|
||||
|
||||
- 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]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
19
docs/faq.rst
19
docs/faq.rst
@ -287,7 +287,24 @@ homepage:
|
||||
:target: https://github.com/samholmes/whiskers
|
||||
:width: 256
|
||||
|
||||
On macOS you can change the icon by following the steps:
|
||||
On macOS you can put :file:`kitty.app.icns` or :file:`kitty.app.png` in the
|
||||
:ref:`kitty configuration directory <confloc>`, and this icon will be applied
|
||||
automatically at startup if the app bundle has no custom icon. This is
|
||||
convenient because app updates under macOS will replace the entire 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.
|
||||
|
||||
You can set custom icon via CLI, which can be used in shell scripts:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
# Set kitty.icns as the icon for currently running kitty
|
||||
kitty +runpy 'from kitty.fast_data_types import cocoa_set_app_icon; import sys; cocoa_set_app_icon(*sys.argv[1:]); print("OK")' kitty.icns
|
||||
|
||||
# Set the icon for app bundle specified by the path
|
||||
kitty +runpy 'from kitty.fast_data_types import cocoa_set_app_icon; import sys; cocoa_set_app_icon(*sys.argv[1:]); print("OK")' /path/to/icon.png /Applications/kitty.app
|
||||
|
||||
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
|
||||
|
||||
@ -862,6 +862,98 @@ 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 {
|
||||
|
||||
const char *icon_path = NULL, *app_path = NULL;
|
||||
if (!PyArg_ParseTuple(args, "s|z", &icon_path, &app_path)) return NULL;
|
||||
if (!icon_path || icon_path[0] == '\0') {
|
||||
PyErr_SetString(PyExc_TypeError, "Empty icon file path");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NSString *custom_icon_path = [NSString stringWithUTF8String:icon_path];
|
||||
NSString *bundle_path = @"";
|
||||
if (!app_path) {
|
||||
bundle_path = [[NSBundle mainBundle] bundlePath];
|
||||
if (!bundle_path || bundle_path.length == 0) bundle_path = @"/Applications/kitty.app";
|
||||
} else if (app_path[0] != '\0') {
|
||||
bundle_path = [NSString stringWithUTF8String:app_path];
|
||||
}
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:custom_icon_path]) {
|
||||
PyErr_Format(PyExc_FileNotFoundError, "Icon file not found: %s", [custom_icon_path UTF8String]);
|
||||
return NULL;
|
||||
}
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:bundle_path]) {
|
||||
PyErr_Format(PyExc_FileNotFoundError, "Application bundle not found: %s", [bundle_path UTF8String]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NSImage *icon_image = [[NSImage alloc] initWithContentsOfFile:custom_icon_path];
|
||||
BOOL result = [[NSWorkspace sharedWorkspace] setIcon:icon_image forFile:bundle_path options:NSExcludeQuickDrawElementsIconCreationOption];
|
||||
[icon_image release];
|
||||
if (result) Py_RETURN_NONE;
|
||||
PyErr_Format(PyExc_OSError, "Failed to set custom icon %s for %s", [custom_icon_path UTF8String], [bundle_path UTF8String]);
|
||||
return NULL;
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
cocoa_set_dock_icon(PyObject UNUSED *self, PyObject *args) {
|
||||
@autoreleasepool {
|
||||
|
||||
const char *icon_path = NULL;
|
||||
if (!PyArg_ParseTuple(args, "s", &icon_path)) return NULL;
|
||||
if (!icon_path || icon_path[0] == '\0') {
|
||||
PyErr_SetString(PyExc_TypeError, "Empty icon file path");
|
||||
return NULL;
|
||||
}
|
||||
NSString *custom_icon_path = [NSString stringWithUTF8String:icon_path];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:custom_icon_path]) {
|
||||
NSImage *icon_image = [[[NSImage alloc] initWithContentsOfFile:custom_icon_path] autorelease];
|
||||
[NSApplication sharedApplication].applicationIconImage = icon_image;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
} // autoreleasepool
|
||||
}
|
||||
|
||||
static NSSound *beep_sound = nil;
|
||||
|
||||
static void
|
||||
@ -923,6 +1015,9 @@ 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 */
|
||||
};
|
||||
|
||||
|
||||
@ -730,7 +730,19 @@ def cocoa_get_lang() -> Optional[str]:
|
||||
pass
|
||||
|
||||
|
||||
def cocoa_set_url_handler(url_scheme: str, bundle_id: Optional[str]) -> None:
|
||||
def cocoa_set_url_handler(url_scheme: str, bundle_id: Optional[str] = None) -> None:
|
||||
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
|
||||
|
||||
|
||||
def cocoa_set_dock_icon(icon_path: str) -> None:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@ -135,6 +135,20 @@ def get_macos_shortcut_for(
|
||||
return ans
|
||||
|
||||
|
||||
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():
|
||||
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)
|
||||
break
|
||||
|
||||
|
||||
def set_x11_window_icon() -> None:
|
||||
# max icon size on X11 64bits is 128x128
|
||||
path, ext = os.path.splitext(logo_png_file)
|
||||
@ -167,8 +181,11 @@ def _run_app(opts: Options, args: CLIOptions, prewarm: PrewarmProcess, bad_lines
|
||||
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
|
||||
if is_macos and opts.macos_custom_beam_cursor:
|
||||
|
||||
if opts.macos_custom_beam_cursor:
|
||||
set_custom_ibeam_cursor()
|
||||
set_macos_app_custom_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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user