Speed up --detach

Also DRYer as well as more robust since single instance cleanup, binding
etc. happen in single process.
This commit is contained in:
Kovid Goyal 2025-04-24 08:13:58 +05:30
parent 55aa1d20f7
commit 9296bc3060
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
8 changed files with 20 additions and 50 deletions

View file

@ -1,7 +1,6 @@
#!/usr/bin/env python
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
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')

View file

@ -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,

View file

@ -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, ""},

View file

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

View file

@ -10,8 +10,8 @@
#include <stdbool.h>
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;

View file

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

View file

@ -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)

View file

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