Use a pipe rather than a signal to wait for terminal ready
Simpler code, more robust since there is no longer a race between the installation of the signal handler and the dispatch of the signal
This commit is contained in:
parent
094ddd9333
commit
eb2ec1833c
@ -47,35 +47,22 @@ write_to_stderr(const char *text) {
|
|||||||
|
|
||||||
#define exit_on_err(m) { write_to_stderr(m); write_to_stderr(": "); write_to_stderr(strerror(errno)); exit(EXIT_FAILURE); }
|
#define exit_on_err(m) { write_to_stderr(m); write_to_stderr(": "); write_to_stderr(strerror(errno)); exit(EXIT_FAILURE); }
|
||||||
|
|
||||||
static sig_atomic_t sigwinch_arrived;
|
|
||||||
|
|
||||||
void handle_sigwinch(int signal) {
|
|
||||||
if (signal == SIGWINCH) sigwinch_arrived = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
wait_for_sigwinch() {
|
wait_for_terminal_ready(int fd) {
|
||||||
sigwinch_arrived = 0;
|
char data;
|
||||||
sigset_t mask, oldmask;
|
while(1) {
|
||||||
struct sigaction sa;
|
int ret = read(fd, &data, 1);
|
||||||
sa.sa_handler = handle_sigwinch;
|
if (ret == -1 && (errno == EINTR || errno == EAGAIN)) continue;
|
||||||
sa.sa_flags = SA_RESTART;
|
break;
|
||||||
if (sigaction(SIGWINCH, &sa, NULL) == -1) {
|
|
||||||
exit_on_err("Failed to set the SIGWINCH signal handler");
|
|
||||||
}
|
}
|
||||||
sigemptyset(&mask);
|
|
||||||
sigaddset(&mask, SIGWINCH);
|
|
||||||
sigprocmask(SIG_BLOCK, &mask, &oldmask);
|
|
||||||
while(!sigwinch_arrived) sigsuspend(&oldmask);
|
|
||||||
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
spawn(PyObject *self UNUSED, PyObject *args) {
|
spawn(PyObject *self UNUSED, PyObject *args) {
|
||||||
PyObject *argv_p, *env_p;
|
PyObject *argv_p, *env_p;
|
||||||
int master, slave, stdin_read_fd, stdin_write_fd;
|
int master, slave, stdin_read_fd, stdin_write_fd, ready_read_fd, ready_write_fd;
|
||||||
char *cwd, *exe;
|
char *cwd, *exe;
|
||||||
if (!PyArg_ParseTuple(args, "ssO!O!iiii", &exe, &cwd, &PyTuple_Type, &argv_p, &PyTuple_Type, &env_p, &master, &slave, &stdin_read_fd, &stdin_write_fd)) return NULL;
|
if (!PyArg_ParseTuple(args, "ssO!O!iiiiii", &exe, &cwd, &PyTuple_Type, &argv_p, &PyTuple_Type, &env_p, &master, &slave, &stdin_read_fd, &stdin_write_fd, &ready_read_fd, &ready_write_fd)) return NULL;
|
||||||
char name[2048] = {0};
|
char name[2048] = {0};
|
||||||
if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
if (ttyname_r(slave, name, sizeof(name) - 1) != 0) { PyErr_SetFromErrno(PyExc_OSError); return NULL; }
|
||||||
char **argv = serialize_string_tuple(argv_p);
|
char **argv = serialize_string_tuple(argv_p);
|
||||||
@ -98,8 +85,10 @@ spawn(PyObject *self UNUSED, PyObject *args) {
|
|||||||
#endif
|
#endif
|
||||||
close(tfd);
|
close(tfd);
|
||||||
|
|
||||||
// Wait for SIGWINCH which indicates kitty has setup the screen object
|
// Wait for READY_SIGNAL which indicates kitty has setup the screen object
|
||||||
wait_for_sigwinch();
|
close(ready_write_fd);
|
||||||
|
wait_for_terminal_ready(ready_read_fd);
|
||||||
|
close(ready_read_fd);
|
||||||
|
|
||||||
// Redirect stdin/stdout/stderr to the pty
|
// Redirect stdin/stdout/stderr to the pty
|
||||||
if (dup2(slave, 1) == -1) exit_on_err("dup2() failed for fd number 1");
|
if (dup2(slave, 1) == -1) exit_on_err("dup2() failed for fd number 1");
|
||||||
|
|||||||
@ -101,6 +101,8 @@ class Child:
|
|||||||
remove_cloexec(slave)
|
remove_cloexec(slave)
|
||||||
fast_data_types.set_iutf8(master, True)
|
fast_data_types.set_iutf8(master, True)
|
||||||
stdin, self.stdin = self.stdin, None
|
stdin, self.stdin = self.stdin, None
|
||||||
|
ready_read_fd, ready_write_fd = os.pipe()
|
||||||
|
remove_cloexec(ready_read_fd)
|
||||||
if stdin is not None:
|
if stdin is not None:
|
||||||
stdin_read_fd, stdin_write_fd = os.pipe()
|
stdin_read_fd, stdin_write_fd = os.pipe()
|
||||||
remove_cloexec(stdin_read_fd)
|
remove_cloexec(stdin_read_fd)
|
||||||
@ -119,16 +121,22 @@ class Child:
|
|||||||
# Some macOS machines need the shell to have argv[0] prefixed by
|
# Some macOS machines need the shell to have argv[0] prefixed by
|
||||||
# hyphen, see https://github.com/kovidgoyal/kitty/issues/247
|
# hyphen, see https://github.com/kovidgoyal/kitty/issues/247
|
||||||
argv[0] = ('-' + exe.split('/')[-1])
|
argv[0] = ('-' + exe.split('/')[-1])
|
||||||
pid = fast_data_types.spawn(exe, self.cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd)
|
pid = fast_data_types.spawn(exe, self.cwd, tuple(argv), env, master, slave, stdin_read_fd, stdin_write_fd, ready_read_fd, ready_write_fd)
|
||||||
os.close(slave)
|
os.close(slave)
|
||||||
self.pid = pid
|
self.pid = pid
|
||||||
self.child_fd = master
|
self.child_fd = master
|
||||||
if stdin is not None:
|
if stdin is not None:
|
||||||
os.close(stdin_read_fd)
|
os.close(stdin_read_fd)
|
||||||
fast_data_types.thread_write(stdin_write_fd, stdin)
|
fast_data_types.thread_write(stdin_write_fd, stdin)
|
||||||
|
os.close(ready_read_fd)
|
||||||
|
self.terminal_ready_fd = ready_write_fd
|
||||||
fcntl.fcntl(self.child_fd, fcntl.F_SETFL, fcntl.fcntl(self.child_fd, fcntl.F_GETFL) | os.O_NONBLOCK)
|
fcntl.fcntl(self.child_fd, fcntl.F_SETFL, fcntl.fcntl(self.child_fd, fcntl.F_GETFL) | os.O_NONBLOCK)
|
||||||
return pid
|
return pid
|
||||||
|
|
||||||
|
def mark_terminal_ready(self):
|
||||||
|
os.close(self.terminal_ready_fd)
|
||||||
|
self.terminal_ready_fd = -1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cmdline(self):
|
def cmdline(self):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -16,13 +16,12 @@ from .constants import (
|
|||||||
)
|
)
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM, CELL_PROGRAM,
|
BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM, CELL_PROGRAM,
|
||||||
CELL_SPECIAL_PROGRAM, CSI, DCS, DECORATION, DIM,
|
CELL_SPECIAL_PROGRAM, CSI, DCS, DECORATION, DIM, GRAPHICS_PREMULT_PROGRAM,
|
||||||
GRAPHICS_PREMULT_PROGRAM, GRAPHICS_PROGRAM, OSC, REVERSE, SCROLL_FULL,
|
GRAPHICS_PROGRAM, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE,
|
||||||
SCROLL_LINE, SCROLL_PAGE, STRIKETHROUGH, Screen, add_window,
|
STRIKETHROUGH, Screen, add_window, cell_size_for_window, compile_program,
|
||||||
cell_size_for_window, compile_program, get_clipboard_string,
|
get_clipboard_string, glfw_post_empty_event, init_cell_program,
|
||||||
glfw_post_empty_event, init_cell_program, set_clipboard_string,
|
set_clipboard_string, set_titlebar_color, set_window_render_data,
|
||||||
set_titlebar_color, set_window_render_data, update_window_title,
|
update_window_title, update_window_visibility, viewport_for_window
|
||||||
update_window_visibility, viewport_for_window
|
|
||||||
)
|
)
|
||||||
from .keys import keyboard_mode_name
|
from .keys import keyboard_mode_name
|
||||||
from .rgb import to_color
|
from .rgb import to_color
|
||||||
@ -104,6 +103,7 @@ class Window:
|
|||||||
def __init__(self, tab, child, opts, args, override_title=None):
|
def __init__(self, tab, child, opts, args, override_title=None):
|
||||||
self.action_on_close = None
|
self.action_on_close = None
|
||||||
self.layout_data = None
|
self.layout_data = None
|
||||||
|
self.pty_resized_once = False
|
||||||
self.needs_attention = False
|
self.needs_attention = False
|
||||||
self.override_title = override_title
|
self.override_title = override_title
|
||||||
self.overlay_window_id = None
|
self.overlay_window_id = None
|
||||||
@ -200,6 +200,9 @@ class Window:
|
|||||||
sg = self.update_position(new_geometry)
|
sg = self.update_position(new_geometry)
|
||||||
self.needs_layout = False
|
self.needs_layout = False
|
||||||
boss.child_monitor.resize_pty(self.id, *current_pty_size)
|
boss.child_monitor.resize_pty(self.id, *current_pty_size)
|
||||||
|
if not self.pty_resized_once:
|
||||||
|
self.pty_resized_once = True
|
||||||
|
self.child.mark_terminal_ready()
|
||||||
else:
|
else:
|
||||||
sg = self.update_position(new_geometry)
|
sg = self.update_position(new_geometry)
|
||||||
self.geometry = g = new_geometry
|
self.geometry = g = new_geometry
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user