From 05f0839add6f93994b0241fdf29b35085b784e6a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 24 Apr 2025 21:07:29 +0530 Subject: [PATCH] Work on moving launch services handling into C This will make it faster and also more robust, not recuring hacks like re-execs. --- kitty/constants.py | 53 ++++++++++-------------------------------- kitty/launcher/main.c | 33 ++++++++++++++++++++++++-- kitty/launcher/utils.h | 4 ++-- kitty/logging.c | 4 ---- kitty/main.py | 5 +++- 5 files changed, 49 insertions(+), 50 deletions(-) diff --git a/kitty/constants.py b/kitty/constants.py index 918f3dd8c..6dc376af3 100644 --- a/kitty/constants.py +++ b/kitty/constants.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # License: GPL v3 Copyright: 2016, Kovid Goyal -import errno import os import pwd import sys @@ -32,6 +31,7 @@ is_running_from_develop: bool = False RC_ENCRYPTION_PROTOCOL_VERSION = '1' website_base_url = 'https://sw.kovidgoyal.net/kitty/' default_pager_for_help = ('less', '-iRXF') +launched_by_launch_services = getattr(sys, 'kitty_run_data')['launched_by_launch_services'] if getattr(sys, 'frozen', False): extensions_dir: str = getattr(sys, 'kitty_run_data')['extensions_dir'] @@ -86,46 +86,17 @@ def kitten_exe() -> str: def _get_config_dir() -> str: - if 'KITTY_CONFIG_DIRECTORY' in os.environ: - return os.path.abspath(os.path.expanduser(os.environ['KITTY_CONFIG_DIRECTORY'])) - - locations = [] - if 'XDG_CONFIG_HOME' in os.environ: - locations.append(os.path.abspath(os.path.expanduser(os.environ['XDG_CONFIG_HOME']))) - locations.append(os.path.expanduser('~/.config')) - if is_macos: - locations.append(os.path.expanduser('~/Library/Preferences')) - for loc in filter(None, os.environ.get('XDG_CONFIG_DIRS', '').split(os.pathsep)): - locations.append(os.path.abspath(os.path.expanduser(loc))) - for loc in locations: - if loc: - q = os.path.join(loc, appname) - if os.access(q, os.W_OK) and os.path.exists(os.path.join(q, 'kitty.conf')): - return q - - def make_tmp_conf() -> None: - import atexit - import tempfile - ans = tempfile.mkdtemp(prefix='kitty-conf-') - - def cleanup() -> None: - import shutil - with suppress(Exception): - shutil.rmtree(ans) - atexit.register(cleanup) - - candidate = os.path.abspath(os.path.expanduser(os.environ.get('XDG_CONFIG_HOME') or '~/.config')) - ans = os.path.join(candidate, appname) - try: - os.makedirs(os.path.realpath(ans), exist_ok=True) - except FileExistsError: - raise SystemExit(f'A file {ans} already exists. It must be a directory, not a file.') - except PermissionError: - make_tmp_conf() - except OSError as err: - if err.errno != errno.EROFS: # Error other than read-only file system - raise - make_tmp_conf() + cdir = getattr(sys, 'kitty_run_data').get('config_dir', '') + if cdir: + return str(cdir) + import atexit + import tempfile + ans = tempfile.mkdtemp(prefix='kitty-conf-') + def cleanup() -> None: + import shutil + with suppress(Exception): + shutil.rmtree(ans) + atexit.register(cleanup) return ans diff --git a/kitty/launcher/main.c b/kitty/launcher/main.c index 3fbfaa375..e9766eee9 100644 --- a/kitty/launcher/main.c +++ b/kitty/launcher/main.c @@ -16,12 +16,14 @@ #else #include #endif +#include #include #include #include #include #include #include "launcher.h" +#include "utils.h" #ifndef KITTY_LIB_PATH #define KITTY_LIB_PATH "../.." @@ -45,9 +47,10 @@ safe_realpath(const char* src, char *buf, size_t buf_sz) { #endif typedef struct { - const char *exe, *exe_dir, *lc_ctype, *lib_dir; + const char *exe, *exe_dir, *lc_ctype, *lib_dir, *config_dir; char **argv; int argc; + bool launched_by_launch_services; } RunData; static bool @@ -70,6 +73,20 @@ set_kitty_run_data(RunData *run_data, bool from_source, wchar_t *extensions_dir) PyObject *ed = PyUnicode_FromWideChar(extensions_dir, -1); S(extensions_dir, ed); } + PyObject *lbls = run_data->launched_by_launch_services ? Py_True : Py_False; + Py_INCREF(lbls); + S(launched_by_launch_services, lbls); + + char buf[PATH_MAX + 1]; + if (run_data->config_dir == NULL) { + if (get_config_dir(buf, sizeof(buf))) run_data->config_dir = buf; + } + if (run_data->config_dir) { + PyObject *cdir = PyUnicode_DecodeFSDefaultAndSize(run_data->config_dir, strlen(run_data->config_dir)); + if (!cdir) { PyErr_Print(); return false; } + S(config_dir, cdir); + } + #undef S int ret = PySys_SetObject("kitty_run_data", ans); Py_CLEAR(ans); @@ -493,9 +510,18 @@ int main(int argc, char *argv[], char* envp[]) { char exe[PATH_MAX+1] = {0}; char exe_dir_buf[PATH_MAX+1] = {0}; RAII_ALLOC(const char, lc_ctype, NULL); + bool launched_by_launch_services = false; + const char *config_dir = NULL; #ifdef __APPLE__ lc_ctype = getenv("LC_CTYPE"); if (lc_ctype) lc_ctype = strdup(lc_ctype); + if (getenv("KITTY_LAUNCHED_BY_LAUNCH_SERVICES")) { + launched_by_launch_services = true; + unsetenv("KITTY_LAUNCHED_BY_LAUNCH_SERVICES"); + char buf[PATH_MAX+1]; + if (!get_config_dir(buf,sizeof(buf))) buf[0] = 0; + config_dir = buf; + } #endif if (!read_exe_path(exe, sizeof(exe))) return 1; strncpy(exe_dir_buf, exe, sizeof(exe_dir_buf)); @@ -510,7 +536,10 @@ int main(int argc, char *argv[], char* envp[]) { } if (num < 0 || num >= PATH_MAX) { fprintf(stderr, "Failed to create path to kitty lib\n"); return 1; } - RunData run_data = {.exe = exe, .exe_dir = exe_dir, .lib_dir = lib, .argc = argc, .argv = argv, .lc_ctype = lc_ctype}; + RunData run_data = { + .exe = exe, .exe_dir = exe_dir, .lib_dir = lib, .argc = argc, .argv = argv, .lc_ctype = lc_ctype, + .launched_by_launch_services=launched_by_launch_services, .config_dir = config_dir, + }; ret = run_embedded(&run_data); single_instance_main(-1, NULL, NULL); Py_FinalizeEx(); diff --git a/kitty/launcher/utils.h b/kitty/launcher/utils.h index 9a34c3c4d..6c1ebe57c 100644 --- a/kitty/launcher/utils.h +++ b/kitty/launcher/utils.h @@ -41,7 +41,7 @@ home_path_for(const char *username) { return NULL; } -static inline void +static void expand_tilde(const char* path, char *ans, size_t ans_sz) { if (path[0] != '~') { snprintf(ans, ans_sz, "%s", path); @@ -162,7 +162,7 @@ is_dir_ok_for_config(char *q) { return access(q, W_OK) == 0; } -static inline bool +static bool get_config_dir(char *output, size_t outputsz) { const char *q; char buf1[PATH_MAX], buf2[PATH_MAX]; diff --git a/kitty/logging.c b/kitty/logging.c index f45919373..e874143f2 100644 --- a/kitty/logging.c +++ b/kitty/logging.c @@ -85,9 +85,5 @@ static PyMethodDef module_methods[] = { bool init_logging(PyObject *module) { if (PyModule_AddFunctions(module, module_methods) != 0) return false; -#ifdef __APPLE__ - // This env var can be either 1 or 2 - if (getenv("KITTY_LAUNCHED_BY_LAUNCH_SERVICES") != NULL) use_os_log = true; -#endif return true; } diff --git a/kitty/main.py b/kitty/main.py index 12e78a5a2..b8b6253df 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -26,6 +26,7 @@ from .constants import ( is_wayland, kitten_exe, kitty_exe, + launched_by_launch_services, logo_png_file, running_in_kitty, supports_window_occlusion, @@ -46,6 +47,7 @@ from .fast_data_types import ( set_custom_cursor, set_default_window_icon, set_options, + set_use_os_log, ) from .fonts.render import dump_font_debug, set_font_family from .options.types import Options @@ -479,9 +481,10 @@ def _main() -> None: running_in_kitty(True) args = sys.argv[1:] - if is_macos and os.environ.pop('KITTY_LAUNCHED_BY_LAUNCH_SERVICES', None) == '1': + if is_macos and launched_by_launch_services: os.chdir(os.path.expanduser('~')) args = macos_cmdline(args) + set_use_os_log(True) try: cwd_ok = os.path.isdir(os.getcwd()) except Exception: