From 0e248b3faa1551555320a7522e8297229ad72788 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 29 Aug 2018 09:01:52 +0530 Subject: [PATCH] Write escape codes to children atomically Ensure a partial escape code is never written --- kitty/child-monitor.c | 23 +++++++++++++++++++---- kitty/data-types.h | 2 +- kitty/keys.c | 8 ++++---- kitty/screen.c | 24 +++++++++++++++++++----- 4 files changed, 43 insertions(+), 14 deletions(-) diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 606fe396a..063baa0a6 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -246,9 +246,18 @@ add_child(ChildMonitor *self, PyObject *args) { } bool -schedule_write_to_child(unsigned long id, const char *data, size_t sz) { +schedule_write_to_child(unsigned long id, unsigned int num, ...) { ChildMonitor *self = the_monitor; bool found = false; + const char *data; + size_t sz = 0; + va_list ap; + va_start(ap, num); + for (unsigned int i = 0; i < num; i++) { + data = va_arg(ap, const char*); + sz += va_arg(ap, size_t); + } + va_end(ap); children_mutex(lock); for (size_t i = 0; i < self->count; i++) { if (children[i].id == id) { @@ -266,8 +275,14 @@ schedule_write_to_child(unsigned long id, const char *data, size_t sz) { screen->write_buf = PyMem_RawRealloc(screen->write_buf, screen->write_buf_sz); if (screen->write_buf == NULL) { fatal("Out of memory."); } } - memcpy(screen->write_buf + screen->write_buf_used, data, sz); - screen->write_buf_used += sz; + va_start(ap, num); + for (unsigned int i = 0; i < num; i++) { + data = va_arg(ap, const char*); + size_t dsz = va_arg(ap, size_t); + memcpy(screen->write_buf + screen->write_buf_used, data, dsz); + screen->write_buf_used += dsz; + } + va_end(ap); if (screen->write_buf_sz > BUFSIZ && screen->write_buf_used < BUFSIZ) { screen->write_buf_sz = BUFSIZ; screen->write_buf = PyMem_RawRealloc(screen->write_buf, screen->write_buf_sz); @@ -288,7 +303,7 @@ needs_write(ChildMonitor UNUSED *self, PyObject *args) { unsigned long id, sz; const char *data; if (!PyArg_ParseTuple(args, "ks#", &id, &data, &sz)) return NULL; - if (schedule_write_to_child(id, data, sz)) { Py_RETURN_TRUE; } + if (schedule_write_to_child(id, 1, data, (size_t)sz)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } diff --git a/kitty/data-types.h b/kitty/data-types.h index eb6ca402d..8b9a6c2e0 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -271,7 +271,7 @@ const char* cursor_as_sgr(Cursor*, Cursor*); double monotonic(); PyObject* cm_thread_write(PyObject *self, PyObject *args); -bool schedule_write_to_child(unsigned long id, const char *data, size_t sz); +bool schedule_write_to_child(unsigned long id, unsigned int num, ...); bool set_iutf8(int, bool); color_type colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval); diff --git a/kitty/keys.c b/kitty/keys.c index ff5d77f33..0a56fa619 100644 --- a/kitty/keys.c +++ b/kitty/keys.c @@ -80,12 +80,12 @@ send_key_to_child(Window *w, int key, int mods, int action) { const char *data = key_to_bytes(key, screen->modes.mDECCKM, screen->modes.mEXTENDED_KEYBOARD, mods, action); if (data) { if (screen->modes.mEXTENDED_KEYBOARD) { - if (*data == 1) schedule_write_to_child(w->id, (data + 1), 1); + if (*data == 1) schedule_write_to_child(w->id, 1, (data + 1), 1); else write_escape_code_to_child(screen, APC, data + 1); } else { if (*data > 2 && data[1] == 0x1b && data[2] == '[') { // CSI code write_escape_code_to_child(screen, CSI, data + 3); - } else schedule_write_to_child(w->id, (data + 1), *data); + } else schedule_write_to_child(w->id, 1, (data + 1), *data); } } } @@ -141,7 +141,7 @@ on_key_input(int key, int scancode, int action, int mods, const char* text, int return; case 2: // commit text if (text && *text) { - schedule_write_to_child(w->id, text, strlen(text)); + schedule_write_to_child(w->id, 1, text, strlen(text)); debug("committed pre-edit text: %s\n", text); } else debug("committed pre-edit text: (null)\n"); return; @@ -184,7 +184,7 @@ on_key_input(int key, int scancode, int action, int mods, const char* text, int bool ok_to_send = action == GLFW_PRESS || action == GLFW_REPEAT || screen->modes.mEXTENDED_KEYBOARD; if (ok_to_send) { if (has_text) { - schedule_write_to_child(w->id, text, strlen(text)); + schedule_write_to_child(w->id, 1, text, strlen(text)); debug("sent text to child\n"); } else { send_key_to_child(w, key, mods, action); diff --git a/kitty/screen.c b/kitty/screen.c index d9df21e95..ffe8e539a 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -499,10 +499,15 @@ select_graphic_rendition(Screen *self, unsigned int *params, unsigned int count, } else cursor_from_sgr(self->cursor, params, count); } +static inline void +write_to_test_child(Screen *self, const char *data, size_t sz) { + PyObject *r = PyObject_CallMethod(self->test_child, "write", "y#", data, sz); if (r == NULL) PyErr_Print(); Py_CLEAR(r); +} + static inline void write_to_child(Screen *self, const char *data, size_t sz) { - if (self->window_id) schedule_write_to_child(self->window_id, data, sz); - if (self->test_child != Py_None) { PyObject *r = PyObject_CallMethod(self->test_child, "write", "y#", data, sz); if (r == NULL) PyErr_Print(); Py_CLEAR(r); } + if (self->window_id) schedule_write_to_child(self->window_id, 1, data, sz); + if (self->test_child != Py_None) { write_to_test_child(self, data, sz); } } void @@ -527,9 +532,18 @@ write_escape_code_to_child(Screen *self, unsigned char which, const char *data) default: fatal("Unknown escape code to write: %u", which); } - write_to_child(self, prefix, strlen(prefix)); - write_to_child(self, data, strlen(data)); - if (suffix[0]) write_to_child(self, suffix, strlen(suffix)); + if (self->window_id) { + if (suffix[0]) { + schedule_write_to_child(self->window_id, 3, prefix, strlen(prefix), data, strlen(data), suffix, strlen(suffix)); + } else { + schedule_write_to_child(self->window_id, 2, prefix, strlen(prefix), data, strlen(data)); + } + } + if (self->test_child != Py_None) { + write_to_test_child(self, prefix, strlen(prefix)); + write_to_test_child(self, data, strlen(data)); + if (suffix[0]) write_to_test_child(self, suffix, strlen(suffix)); + } } static inline bool