From 9296bc30601962a7ad4aac133ef6b1a60a64b4f6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 24 Apr 2025 08:13:58 +0530 Subject: [PATCH] Speed up --detach Also DRYer as well as more robust since single instance cleanup, binding etc. happen in single process. --- kittens/panel/main.py | 9 --------- kitty/cli.py | 7 +++++-- kitty/data-types.c | 12 ------------ kitty/fast_data_types.pyi | 4 ---- kitty/launcher/launcher.h | 4 ++-- kitty/launcher/main.c | 13 +++++++++++++ kitty/main.py | 2 -- kitty/utils.py | 19 ------------------- 8 files changed, 20 insertions(+), 50 deletions(-) diff --git a/kittens/panel/main.py b/kittens/panel/main.py index b7beaa252..ad4bac018 100644 --- a/kittens/panel/main.py +++ b/kittens/panel/main.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # License: GPL v3 Copyright: 2018, Kovid Goyal -import os import sys from collections.abc import Callable from contextlib import suppress @@ -349,14 +348,6 @@ def handle_single_instance_command(boss: BossType, sys_args: Sequence[str], envi def main(sys_args: list[str]) -> None: global args args, items = parse_panel_args(sys_args[1:]) - talk_fd = -1 - if args.single_instance: - si_data = os.environ.get('KITTY_SI_DATA', '') - if si_data: - talk_fd = int(si_data) - if args.detach: - from kitty.utils import detach - detach(talk_fd, log_file=args.detached_log or os.devnull) sys.argv = ['kitty'] if args.debug_rendering: sys.argv.append('--debug-rendering') diff --git a/kitty/cli.py b/kitty/cli.py index 12e9955c3..18a115b05 100644 --- a/kitty/cli.py +++ b/kitty/cli.py @@ -909,11 +909,14 @@ Change to the specified directory when launching. --detach type=bool-set -condition=not is_macos -Detach from the controlling terminal, if any. Not available on macOS. On macOS +Detach from the controlling terminal, if any. On macOS use :code:`open -a kitty.app -n` instead. +--detached-log +Path to a log file to store STDOUT/STDERR when using :option:`--detach` + + --session completion=type:file ext:session relative:conf group:"Session files" Path to a file containing the startup :italic:`session` (tabs, windows, layout, diff --git a/kitty/data-types.c b/kitty/data-types.c index 5922b98d4..a9abb8540 100644 --- a/kitty/data-types.c +++ b/kitty/data-types.c @@ -66,17 +66,6 @@ process_group_map(PyObject *self UNUSED, PyObject *args UNUSED) { } #endif -static PyObject* -redirect_std_streams(PyObject UNUSED *self, PyObject *args, PyObject *kw) { - char *in = "", *out = "", *err = ""; - static const char* kwlist[] = {"stdin", "stdout", "stderr", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kw, "|sss", (char**)kwlist, &in, &out, &err)) return NULL; - if (in[0] && freopen(in, "r", stdin) == NULL) return PyErr_SetFromErrno(PyExc_OSError); - if (out[0] && freopen(out, "a", stdout) == NULL) return PyErr_SetFromErrno(PyExc_OSError); - if (out[0] && freopen(err, "a", stderr) == NULL) return PyErr_SetFromErrno(PyExc_OSError); - Py_RETURN_NONE; -} - static PyObject* pybase64_encode(PyObject UNUSED *self, PyObject *const *args, Py_ssize_t nargs) { int add_padding = 0; @@ -664,7 +653,6 @@ static PyMethodDef module_methods[] = { {"char_props_for", py_char_props_for, METH_O, ""}, {"split_into_graphemes", (PyCFunction)split_into_graphemes, METH_O, ""}, {"thread_write", (PyCFunction)cm_thread_write, METH_VARARGS, ""}, - {"redirect_std_streams", (PyCFunction)(void (*) (void))(redirect_std_streams), METH_VARARGS | METH_KEYWORDS, NULL}, {"locale_is_valid", (PyCFunction)locale_is_valid, METH_VARARGS, ""}, {"shm_open", (PyCFunction)py_shm_open, METH_VARARGS, ""}, {"shm_unlink", (PyCFunction)py_shm_unlink, METH_VARARGS, ""}, diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 6797bba6c..b65985be9 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -335,10 +335,6 @@ def log_error_string(s: str) -> None: pass -def redirect_std_streams(stdin: str = '', stdout: str = '', stderr: str = '') -> None: - pass - - def glfw_get_key_name(key: int, native_key: int) -> Optional[str]: pass diff --git a/kitty/launcher/launcher.h b/kitty/launcher/launcher.h index 29047e279..d4ece6199 100644 --- a/kitty/launcher/launcher.h +++ b/kitty/launcher/launcher.h @@ -10,8 +10,8 @@ #include typedef struct CLIOptions { - const char *session, *instance_group; - bool single_instance, version_requested, wait_for_single_instance_window_close; + const char *session, *instance_group, *detached_log; + bool single_instance, version_requested, wait_for_single_instance_window_close, detach; int open_url_count; char **open_urls; } CLIOptions; diff --git a/kitty/launcher/main.c b/kitty/launcher/main.c index f4ec24129..25168ccd5 100644 --- a/kitty/launcher/main.c +++ b/kitty/launcher/main.c @@ -378,6 +378,8 @@ handle_option_value: opts.session = arg; } else if (strcmp(current_option_expecting_argument, "instance-group") == 0) { opts.instance_group = arg; + } else if (strcmp(current_option_expecting_argument, "detached-log") == 0) { + opts.detached_log = arg; } current_option_expecting_argument[0] = 0; } else { @@ -397,11 +399,14 @@ handle_option_value: opts.single_instance = true; } else if (strcmp(arg+2, "wait-for-single-instance-window-close") == 0) { opts.wait_for_single_instance_window_close = true; + } else if (strcmp(arg+2, "detach") == 0) { + opts.detach = true; } else if (!is_boolean_flag(arg+2)) { strncpy(current_option_expecting_argument, arg+2, sizeof(current_option_expecting_argument)-1); } } else { memcpy(current_option_expecting_argument, arg+2, equal - (arg + 2)); + current_option_expecting_argument[equal - (arg + 2)] = 0; arg = equal + 1; goto handle_option_value; } @@ -432,6 +437,14 @@ handle_option_value: } exit(0); } + if (opts.detach) { + if (fork() != 0) exit(0); + setsid(); + if (!(opts.session && ((opts.session[0] == '-' && opts.session[1] == 0) || strcmp(opts.session, "/dev/stdin") == 0))) freopen("/dev/null", "r", stdin); + if (!opts.detached_log || !opts.detached_log[0]) opts.detached_log = "/dev/null"; + freopen(opts.detached_log, "ab", stdout); + freopen(opts.detached_log, "ab", stderr); + } unsetenv("KITTY_SI_DATA"); if (opts.single_instance) { char igbuf[256]; diff --git a/kitty/main.py b/kitty/main.py index 3052ea98e..12e78a5a2 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -56,7 +56,6 @@ from .shaders import CompileError, load_shader_programs from .types import LayerShellConfig from .utils import ( cleanup_ssh_control_masters, - detach, expandvars, get_custom_window_icon, log_error, @@ -514,7 +513,6 @@ def _main() -> None: if cli_opts.session == '-': from .session import PreReadSession cli_opts.session = PreReadSession(sys.stdin.read(), os.environ) - detach(talk_fd) if cli_opts.replay_commands: from kitty.client import main as client_main client_main(cli_opts.replay_commands) diff --git a/kitty/utils.py b/kitty/utils.py index def5464cb..e9f1a0f39 100644 --- a/kitty/utils.py +++ b/kitty/utils.py @@ -257,25 +257,6 @@ def open_url(url: str, program: str | list[str] = 'default', cwd: str | None = N return open_cmd(command_for_open(program), url, cwd=cwd, extra_env=extra_env) -def detach(*preserve_fds: int, fork: bool = True, setsid: bool = True, redirect: bool = True, log_file: str = os.devnull) -> None: - reset = [] - for fd in preserve_fds: - if fd > -1 and not os.get_inheritable(fd): - os.set_inheritable(fd, True) - reset.append(fd) - if fork: - # Detach from the controlling process. - if os.fork() != 0: - raise SystemExit(0) - for fd in reset: - os.set_inheritable(fd, False) - if setsid: - os.setsid() - if redirect: - from .fast_data_types import redirect_std_streams - redirect_std_streams(stdin=os.devnull, stdout=log_file, stderr=log_file) - - def init_startup_notification_x11(window_handle: int, startup_id: str | None = None) -> Optional['StartupCtx']: # https://specifications.freedesktop.org/startup-notification-spec/startup-notification-latest.txt from kitty.fast_data_types import init_x11_startup_notification