Work on moving launch services handling into C

This will make it faster and also more robust, not recuring hacks like
re-execs.
This commit is contained in:
Kovid Goyal 2025-04-24 21:07:29 +05:30
parent 76ac66fc8c
commit 05f0839add
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
5 changed files with 49 additions and 50 deletions

View file

@ -1,7 +1,6 @@
#!/usr/bin/env python
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import errno
import os
import pwd
import sys
@ -32,6 +31,7 @@ class Version(NamedTuple):
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

View file

@ -16,12 +16,14 @@
#else
#include <limits.h>
#endif
#include <pwd.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <wchar.h>
#include <fcntl.h>
#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();

View file

@ -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];

View file

@ -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;
}

View file

@ -26,6 +26,7 @@
is_wayland,
kitten_exe,
kitty_exe,
launched_by_launch_services,
logo_png_file,
running_in_kitty,
supports_window_occlusion,
@ -46,6 +47,7 @@
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: