diff --git a/kitty/boss.py b/kitty/boss.py index ba5270b2a..300cea63a 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -5,7 +5,6 @@ import inspect import io import os -import select import signal import struct from functools import wraps @@ -24,7 +23,8 @@ from .constants import ( from .fast_data_types import ( GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA, GLFW_CURSOR, GLFW_CURSOR_HIDDEN, GLFW_CURSOR_NORMAL, GLFW_MOUSE_BUTTON_1, GLFW_PRESS, GLFW_REPEAT, - drain_read, glBlendFunc, glfw_post_empty_event, glViewport, Timers as _Timers + ChildMonitor, Timers as _Timers, drain_read, glBlendFunc, + glfw_post_empty_event, glViewport ) from .fonts.render import set_font_family from .keys import ( @@ -83,6 +83,33 @@ def callback(func): return f +class DumpCommands: # {{{ + + def __init__(self, args): + self.draw_dump_buf = [] + if args.dump_bytes: + self.dump_bytes_to = open(args.dump_bytes, 'wb') + + def __call__(self, *a): + if a: + if a[0] == 'draw': + if a[1] is None: + if self.draw_dump_buf: + safe_print('draw', ''.join(self.draw_dump_buf)) + self.draw_dump_buf = [] + else: + self.draw_dump_buf.append(a[1]) + elif a[0] == 'bytes': + self.dump_bytes_to.write(a[1]) + self.dump_bytes_to.flush() + else: + if self.draw_dump_buf: + safe_print('draw', ''.join(self.draw_dump_buf)) + self.draw_dump_buf = [] + safe_print(*a) +# }}} + + class Boss(Thread): daemon = True @@ -98,16 +125,15 @@ class Boss(Thread): self.pending_resize = False self.resize_gl_viewport = False self.shutting_down = False - self.screen_update_delay = opts.repaint_delay / 1000.0 self.signal_fd = handle_unix_signals() self.read_wakeup_fd, self.write_wakeup_fd = pipe2() - self.read_dispatch_map = { - self.signal_fd: self.signal_received, - self.read_wakeup_fd: self.on_wakeup} self.timers = Timers() self.ui_timers = Timers() + self.child_monitor = ChildMonitor( + self.read_wakeup_fd, self.on_wakeup, self.signal_fd, self.signal_received, self.timers, + opts.repaint_delay / 1000.0, + DumpCommands(args) if args.dump_commands or args.dump_bytes else None) self.pending_ui_thread_calls = Queue() - self.write_dispatch_map = {} set_boss(self) self.current_font_size = opts.font_size cell_size.width, cell_size.height = set_font_family(opts) @@ -171,15 +197,13 @@ class Boss(Thread): import traceback safe_print(traceback.format_exc()) - def add_child_fd(self, child_fd, read_ready, write_ready): + def add_child_fd(self, child_fd, window): ' Must be called in child thread ' - self.read_dispatch_map[child_fd] = read_ready - self.write_dispatch_map[child_fd] = write_ready + self.child_monitor.add_child(child_fd, window.screen, window.close, window.write_ready, window.update_screen) def remove_child_fd(self, child_fd): ' Must be called in child thread ' - self.read_dispatch_map.pop(child_fd, None) - self.write_dispatch_map.pop(child_fd, None) + self.child_monitor.remove_child(child_fd) def queue_ui_action(self, func, *args): self.pending_ui_thread_calls.put((func, args)) @@ -209,21 +233,7 @@ class Boss(Thread): self.queue_ui_action(self.gui_close_window, window) def run(self): - while not self.shutting_down: - all_readers = list(self.read_dispatch_map) - all_writers = [ - w.child_fd for w in self.iterwindows() if w.write_buf] - readers, writers, _ = select.select( - all_readers, all_writers, [], self.timers.timeout()) - for r in readers: - self.read_dispatch_map[r]() - for w in writers: - self.write_dispatch_map[w]() - self.timers.call() - for w in self.iterwindows(): - if w.screen.is_dirty(): - self.timers.add_if_missing( - self.screen_update_delay, w.update_screen) + self.child_monitor.loop() @callback def on_window_resize(self, window, w, h): @@ -501,6 +511,7 @@ class Boss(Thread): signal.signal(signal.SIGINT, signal.SIG_DFL) signal.signal(signal.SIGTERM, signal.SIG_DFL) self.shutting_down = True + self.child_monitor.shutdown() wakeup() self.join() for t in self.tab_manager: diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c new file mode 100644 index 000000000..154693238 --- /dev/null +++ b/kitty/child-monitor.c @@ -0,0 +1,204 @@ +/* + * child-monitor.c + * Copyright (C) 2017 Kovid Goyal + * + * Distributed under terms of the GPL3 license. + */ + +#include "data-types.h" + +#define EXTRA_FDS 2 + +static PyObject * +new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { + ChildMonitor *self; + PyObject *wakeup_func, *signal_func, *dump_callback; + Timers *timers; + int wakeup_fd, signal_fd; + double delay; + + if (!PyArg_ParseTuple(args, "iOiOOdO", &wakeup_fd, &wakeup_func, &signal_fd, &signal_func, &timers, &delay, &dump_callback)) return NULL; + self = (ChildMonitor *)type->tp_alloc(type, 0); + if (self == NULL) return PyErr_NoMemory(); + self->wakeup_func = wakeup_func; Py_INCREF(wakeup_func); + self->signal_func = signal_func; Py_INCREF(signal_func); + self->timers = timers; Py_INCREF(timers); + self->dump_callback = dump_callback; Py_INCREF(dump_callback); + self->count = 0; self->children = NULL; + self->fds = (struct pollfd*)PyMem_Calloc(EXTRA_FDS, sizeof(struct pollfd)); + self->repaint_delay = delay; + if (self->fds == NULL) { Py_CLEAR(self); return PyErr_NoMemory(); } + self->fds[0].fd = wakeup_fd; self->fds[1].fd = signal_fd; + self->fds[0].events = POLLIN; self->fds[0].events = POLLIN; + + return (PyObject*) self; +} + +#define FREE_CHILD(x) \ + Py_CLEAR((x).screen); Py_CLEAR((x).on_exit); Py_CLEAR((x).write_func); Py_CLEAR((x).update_screen); + +static void +dealloc(ChildMonitor* self) { + Py_CLEAR(self->wakeup_func); Py_CLEAR(self->signal_func); Py_CLEAR(self->timers); Py_CLEAR(self->dump_callback); + for (size_t i = 0; i < self->count; i++) { + FREE_CHILD(self->children[i]); + } + PyMem_Free(self->fds); PyMem_Free(self->children); + Py_TYPE(self)->tp_free((PyObject*)self); +} + +#define COPY_CHILD(src, dest) \ + dest.screen = src.screen; dest.on_exit = src.on_exit; dest.write_func = src.write_func; dest.needs_write = src.needs_write; dest.update_screen = src.update_screen + +#define COPY_POLL_FD(src, dest) \ + dest.fd = src.fd; dest.events = src.events; + +static PyObject * +add_child(ChildMonitor *self, PyObject *args) { +#define add_child_doc "add_child(fd, screen, on_exit, write_func, update_screen) -> Add a child." + int fd; + PyObject *on_exit, *write_func, *update_screen; + Screen *screen; + if (!PyArg_ParseTuple(args, "iOOOO", &fd, &screen, &on_exit, &write_func, &update_screen)) return NULL; + + Child *children = (Child*)PyMem_Calloc(self->count + 1, sizeof(Child)); + struct pollfd *fds = (struct pollfd*)PyMem_Calloc(EXTRA_FDS + 1 + self->count, sizeof(struct pollfd)); + if (children == NULL || fds == NULL) { PyMem_Free(children); PyMem_Free(fds); return PyErr_NoMemory();} + for (size_t i = 0; i < self->count; i++) { + COPY_CHILD(self->children[i], children[i]); + } + for (size_t i = 0; i < self->count + EXTRA_FDS; i++) { + COPY_POLL_FD(self->fds[i], fds[i]); + } + fds[EXTRA_FDS + self->count].fd = fd; + fds[EXTRA_FDS + self->count].events = POLLIN; +#define ADDATTR(x) children[self->count].x = x; Py_INCREF(x); + ADDATTR(screen); ADDATTR(on_exit); ADDATTR(write_func); ADDATTR(update_screen); + self->count++; + PyMem_Free(self->fds); PyMem_Free(self->children); + self->fds = fds; self->children = children; + Py_RETURN_NONE; +} + +static PyObject * +remove_child(ChildMonitor *self, PyObject *pyfd) { +#define remove_child_doc "remove_child(fd) -> Remove a child." + size_t i; bool found = false; + int fd = (int)PyLong_AsLong(pyfd); + for (i = 0; i < self->count; i++) { + if (self->fds[EXTRA_FDS + i].fd == fd) { found = true; break; } + } + if (!found) { Py_RETURN_FALSE; } + Child *children = self->count > 1 ? (Child*)PyMem_Calloc(self->count - 1, sizeof(Child)) : NULL; + struct pollfd *fds = (struct pollfd*)PyMem_Calloc(EXTRA_FDS + self->count - 1, sizeof(struct pollfd)); + if ((self->count > 1 && children == NULL) || fds == NULL) { PyMem_Free(children); PyMem_Free(fds); return PyErr_NoMemory();} + for (size_t s = 0, d = 0; s < self->count; s++) { + if (s != i) { + COPY_CHILD(self->children[s], children[d]); + COPY_POLL_FD(self->fds[s + EXTRA_FDS], fds[d + EXTRA_FDS]); + d++; + } else { + FREE_CHILD(self->children[s]); + } + } + for (i = 0; i < EXTRA_FDS; i++) { + COPY_POLL_FD(self->fds[i], fds[i]); + } + self->count--; + PyMem_Free(self->fds); PyMem_Free(self->children); + self->fds = fds; self->children = children; + Py_RETURN_TRUE; +} + +static PyObject * +needs_write(ChildMonitor *self, PyObject *args) { +#define needs_write_doc "needs_write(fd, yesno) -> Mark a child as needing write or not." + int fd, yesno; + if (!PyArg_ParseTuple(args, "ip", &fd, &yesno)) return NULL; + for (size_t i = 0; i < self->count; i++) { + if (self->fds[EXTRA_FDS + i].fd == fd) { + self->children[i].needs_write = (bool)yesno; + Py_RETURN_TRUE; + } + } + Py_RETURN_FALSE; +} + +static PyObject * +loop(ChildMonitor *self) { +#define loop_doc "loop() -> The monitor loop." + size_t i; + int ret, timeout; + bool has_more; + PyObject *t; + while (LIKELY(!self->shutting_down)) { + for (i = 0; i < self->count + EXTRA_FDS; i++) self->fds[i].revents = 0; + for (i = EXTRA_FDS; i < EXTRA_FDS + self->count; i++) self->fds[i].events = self->children[i - EXTRA_FDS].needs_write ? POLLOUT | POLLIN : POLLIN; + timeout = (int)(timers_timeout(self->timers) * 1000); + Py_BEGIN_ALLOW_THREADS; + ret = poll(self->fds, self->count + EXTRA_FDS, timeout); + Py_END_ALLOW_THREADS; + if (ret > 0) { +#define PYCALL(x) t = PyObject_CallObject(x, NULL); if (t == NULL) PyErr_Print(); else Py_DECREF(t); + if (self->fds[0].revents && POLLIN) { PYCALL(self->wakeup_func); } + if (self->fds[1].revents && POLLIN) { PYCALL(self->signal_func); } + for (i = 0; i < self->count; i++) { + if (self->fds[EXTRA_FDS + i].revents & (POLLIN | POLLHUP)) { + if (LIKELY(self->dump_callback == Py_None)) has_more = read_bytes(self->fds[EXTRA_FDS + i].fd, self->children[i].screen, NULL); + else has_more = read_bytes(self->fds[EXTRA_FDS + i].fd, self->children[i].screen, self->dump_callback); + if (!has_more) { PYCALL(self->children[i].on_exit); } + } + if (self->fds[EXTRA_FDS + i].revents & POLLOUT) { + PYCALL(self->children[i].write_func); + } + } + } else if (ret < 0) { + if (errno != EAGAIN && errno != EINTR) { + perror("Call to poll() failed"); + } + continue; + } + timers_call(self->timers); + for (i = 0; i < self->count; i++) { + if (self->children[i].screen->change_tracker->dirty) { + if (!timers_add_if_missing(self->timers, self->repaint_delay, self->children->update_screen, NULL)) PyErr_Print(); + } + } + } + Py_RETURN_NONE; +} + + +static PyObject * +shutdown(ChildMonitor *self) { +#define shutdown_doc "shutdown() -> Shutdown the monitor loop." + self->shutting_down = true; + Py_RETURN_NONE; +} + +// Boilerplate {{{ +static PyMethodDef methods[] = { + METHOD(add_child, METH_VARARGS) + METHOD(remove_child, METH_O) + METHOD(needs_write, METH_VARARGS) + METHOD(loop, METH_NOARGS) + METHOD(shutdown, METH_NOARGS) + {NULL} /* Sentinel */ +}; + + +PyTypeObject ChildMonitor_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "fast_data_types.ChildMonitor", + .tp_basicsize = sizeof(ChildMonitor), + .tp_dealloc = (destructor)dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "ChildMonitor", + .tp_methods = methods, + .tp_new = new, +}; + + +INIT_TYPE(ChildMonitor) +// }}} + diff --git a/kitty/data-types.c b/kitty/data-types.c index bdaab92e1..98c29da80 100644 --- a/kitty/data-types.c +++ b/kitty/data-types.c @@ -69,8 +69,6 @@ static PyMethodDef module_methods[] = { {"drain_read", (PyCFunction)drain_read, METH_O, ""}, {"parse_bytes", (PyCFunction)parse_bytes, METH_VARARGS, ""}, {"parse_bytes_dump", (PyCFunction)parse_bytes_dump, METH_VARARGS, ""}, - {"read_bytes", (PyCFunction)read_bytes, METH_VARARGS, ""}, - {"read_bytes_dump", (PyCFunction)read_bytes_dump, METH_VARARGS, ""}, {"redirect_std_streams", (PyCFunction)redirect_std_streams, METH_VARARGS, ""}, {"wcwidth", (PyCFunction)wcwidth_wrap, METH_O, ""}, {"change_wcwidth", (PyCFunction)change_wcwidth_wrap, METH_O, ""}, @@ -111,6 +109,7 @@ PyInit_fast_data_types(void) { if (!init_Line(m)) return NULL; if (!init_Cursor(m)) return NULL; if (!init_Timers(m)) return NULL; + if (!init_ChildMonitor(m)) return NULL; if (!init_ColorProfile(m)) return NULL; if (!init_SpriteMap(m)) return NULL; if (!init_ChangeTracker(m)) return NULL; diff --git a/kitty/data-types.h b/kitty/data-types.h index b179c13f8..8c0a10c7e 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -10,6 +10,7 @@ #include #include +#include #define PY_SSIZE_T_CLEAN #include #define UNUSED __attribute__ ((unused)) @@ -318,6 +319,26 @@ typedef struct { } Timers; PyTypeObject Timers_Type; +typedef struct { + Screen *screen; + PyObject *on_exit, *write_func, *update_screen; + bool needs_write; +} Child; + +typedef struct { + PyObject_HEAD + + PyObject *wakeup_func, *signal_func, *dump_callback; + Timers *timers; + int wakeup_fd, singal_fd; + struct pollfd *fds; + Child *children; + size_t count; + bool shutting_down; + double repaint_delay; +} ChildMonitor; +PyTypeObject ChildMonitor_Type; + #define left_shift_line(line, at, num) \ for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \ COPY_CELL(line, __i__ + (num), line, __i__) \ @@ -335,6 +356,7 @@ int init_LineBuf(PyObject *); int init_HistoryBuf(PyObject *); int init_Cursor(PyObject *); int init_Timers(PyObject *); +int init_ChildMonitor(PyObject *); int init_Line(PyObject *); int init_ColorProfile(PyObject *); int init_SpriteMap(PyObject *); @@ -343,8 +365,8 @@ int init_Screen(PyObject *); int init_Face(PyObject *); int init_Window(PyObject *); PyObject* create_256_color_table(); -PyObject* read_bytes_dump(PyObject UNUSED *, PyObject *); -PyObject* read_bytes(PyObject UNUSED *, PyObject *); +bool read_bytes(int fd, Screen *screen, PyObject *dump_callback); +bool read_bytes_dump(int fd, Screen *screen, PyObject *dump_callback); PyObject* parse_bytes_dump(PyObject UNUSED *, PyObject *); PyObject* parse_bytes(PyObject UNUSED *, PyObject *); uint32_t decode_utf8(uint32_t*, uint32_t*, uint8_t byte); @@ -379,6 +401,10 @@ void historybuf_add_line(HistoryBuf *self, const Line *line); void historybuf_rewrap(HistoryBuf *self, HistoryBuf *other); void historybuf_init_line(HistoryBuf *self, index_type num, Line *l); +double timers_timeout(Timers*); +void timers_call(Timers*); +bool timers_add_if_missing(Timers *self, double delay, PyObject *callback, PyObject *args); + unsigned int safe_wcwidth(uint32_t ch); void change_wcwidth(bool use9); void screen_align(Screen*); diff --git a/kitty/parser.c b/kitty/parser.c index 45bfdbd8d..d2a1c783a 100644 --- a/kitty/parser.c +++ b/kitty/parser.c @@ -745,17 +745,9 @@ FNAME(parse_bytes)(PyObject UNUSED *self, PyObject *args) { Py_RETURN_NONE; } -PyObject* -FNAME(read_bytes)(PyObject UNUSED *self, PyObject *args) { - PyObject *dump_callback = NULL; +bool +FNAME(read_bytes)(int fd, Screen *screen, PyObject *dump_callback) { Py_ssize_t len; - Screen *screen; - int fd; -#ifdef DUMP_COMMANDS - if (!PyArg_ParseTuple(args, "OOi", &dump_callback, &screen, &fd)) return NULL; -#else - if (!PyArg_ParseTuple(args, "Oi", &screen, &fd)) return NULL; -#endif while(true) { Py_BEGIN_ALLOW_THREADS; @@ -763,20 +755,18 @@ FNAME(read_bytes)(PyObject UNUSED *self, PyObject *args) { Py_END_ALLOW_THREADS; if (len == -1) { if (errno == EINTR) continue; - if (errno == EIO) { Py_RETURN_FALSE; } - return PyErr_SetFromErrno(PyExc_OSError); + if (errno != EIO) perror("Call to read() from child fd failed"); + return false; } /* PyObject_Print(Py_BuildValue("y#", screen->read_buf, len), stderr, 0); */ break; } + if (UNLIKELY(len == 0)) return false; #ifdef DUMP_COMMANDS - if (len > 0) { - Py_XDECREF(PyObject_CallFunction(dump_callback, "sy#", "bytes", screen->read_buf, len)); PyErr_Clear(); - } + Py_XDECREF(PyObject_CallFunction(dump_callback, "sy#", "bytes", screen->read_buf, len)); PyErr_Clear(); #endif _parse_bytes(screen, screen->read_buf, len, dump_callback); - if(len > 0) { Py_RETURN_TRUE; } - Py_RETURN_FALSE; + return true; } #undef FNAME // }}} diff --git a/kitty/tabs.py b/kitty/tabs.py index fd0cb6016..802ed259a 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -116,7 +116,7 @@ class Tab: window = Window(self, child, self.opts, self.args) if override_title is not None: window.title = window.override_title = override_title - get_boss().add_child_fd(child.child_fd, window.read_ready, window.write_ready) + get_boss().add_child_fd(child.child_fd, window) self.active_window_idx = self.current_layout.add_window(self.windows, window, self.active_window_idx) self.relayout_borders() glfw_post_empty_event() diff --git a/kitty/timers.c b/kitty/timers.c index 9b4369ca4..5e32d8f65 100644 --- a/kitty/timers.c +++ b/kitty/timers.c @@ -80,18 +80,18 @@ compare_events(const void *a, const void *b) { } -static inline PyObject* +static inline bool _add(Timers *self, double at, PyObject *callback, PyObject *args) { size_t i; if (self->count >= self->capacity) { PyErr_SetString(PyExc_ValueError, "Too many timers"); - return NULL; + return false; } i = self->count++; self->events[i].at = at; self->events[i].callback = callback; self->events[i].args = args; Py_INCREF(callback); Py_XINCREF(args); qsort(self->events, self->count, sizeof(TimerEvent), compare_events); - Py_RETURN_NONE; + return true; } @@ -115,25 +115,31 @@ add(Timers *self, PyObject *fargs) { } } - return _add(self, at, callback, args); + if (!_add(self, at, callback, args)) return NULL; + Py_RETURN_NONE; } + +bool +timers_add_if_missing(Timers *self, double delay, PyObject *callback, PyObject *args) { + for (size_t i = 0; i < self->count; i++) { + if (self->events[i].callback == callback) { + return true; + } + } + return _add(self, monotonic() + delay, callback, args); +} + + static PyObject * add_if_missing(Timers *self, PyObject *fargs) { #define add_if_missing_doc "add_if_missing(delay, callback, args) -> Add callback, unless it already exists" - size_t i; PyObject *callback, *args = NULL; - double delay, at; + double delay; if (!PyArg_ParseTuple(fargs, "dO|O", &delay, &callback, &args)) return NULL; - for (i = 0; i < self->count; i++) { - if (self->events[i].callback == callback) { - Py_RETURN_NONE; - } - } - at = monotonic() + delay; - - return _add(self, at, callback, args); + if (!timers_add_if_missing(self, delay, callback, args)) return NULL; + Py_RETURN_NONE; } static PyObject * @@ -154,6 +160,12 @@ remove_event(Timers *self, PyObject *callback) { Py_RETURN_NONE; } +double +timers_timeout(Timers *self) { + double ans = self->events[0].at - monotonic(); + return MAX(0, ans); +} + static PyObject * timeout(Timers *self) { #define timeout_doc "timeout() -> The time in seconds until the next event" @@ -162,10 +174,9 @@ timeout(Timers *self) { return PyFloat_FromDouble(MAX(0, ans)); } -static PyObject * -call(Timers *self) { -#define call_doc "call() -> Dispatch all expired events" - if (self->count < 1) { Py_RETURN_NONE; } +void +timers_call(Timers *self) { + if (self->count < 1) return; TimerEvent *other = self->events == self->buf1 ? self->buf2 : self->buf1; double now = monotonic(); size_t i, j; @@ -182,6 +193,12 @@ call(Timers *self) { } self->events = other; self->count = j; +} + +static PyObject * +call(Timers *self) { +#define call_doc "call() -> Dispatch all expired events" + timers_call(self); Py_RETURN_NONE; } diff --git a/kitty/window.py b/kitty/window.py index 15326c07f..5865b8018 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -5,22 +5,24 @@ import os import weakref from collections import deque -from functools import partial from time import monotonic from .char_grid import CharGrid, DynamicColor -from .constants import wakeup, get_boss, appname, WindowGeometry, is_key_pressed, mouse_button_pressed, cell_size +from .constants import ( + WindowGeometry, appname, cell_size, get_boss, is_key_pressed, + mouse_button_pressed, wakeup +) from .fast_data_types import ( - BRACKETED_PASTE_START, BRACKETED_PASTE_END, Screen, read_bytes_dump, - read_bytes, GLFW_MOD_SHIFT, GLFW_MOUSE_BUTTON_1, GLFW_PRESS, - GLFW_MOUSE_BUTTON_MIDDLE, GLFW_RELEASE, glfw_post_empty_event, - GLFW_MOUSE_BUTTON_5, ANY_MODE, MOTION_MODE, GLFW_KEY_LEFT_SHIFT, - GLFW_KEY_RIGHT_SHIFT, GLFW_KEY_UP, GLFW_KEY_DOWN, GLFW_MOUSE_BUTTON_4 + ANY_MODE, BRACKETED_PASTE_END, BRACKETED_PASTE_START, GLFW_KEY_DOWN, + GLFW_KEY_LEFT_SHIFT, GLFW_KEY_RIGHT_SHIFT, GLFW_KEY_UP, GLFW_MOD_SHIFT, + GLFW_MOUSE_BUTTON_1, GLFW_MOUSE_BUTTON_4, GLFW_MOUSE_BUTTON_5, + GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, GLFW_RELEASE, MOTION_MODE, Screen, + glfw_post_empty_event ) from .keys import get_key_map -from .mouse import encode_mouse_event, PRESS, RELEASE, MOVE, DRAG +from .mouse import DRAG, MOVE, PRESS, RELEASE, encode_mouse_event from .terminfo import get_capabilities -from .utils import sanitize_title, get_primary_selection, parse_color_set, safe_print +from .utils import get_primary_selection, parse_color_set, sanitize_title DYNAMIC_COLOR_CODES = { 10: DynamicColor.default_fg, @@ -30,13 +32,11 @@ DYNAMIC_COLOR_CODES = { 19: DynamicColor.highlight_fg, } DYNAMIC_COLOR_CODES.update({k+100: v for k, v in DYNAMIC_COLOR_CODES.items()}) -dump_bytes_opened = False class Window: def __init__(self, tab, child, opts, args): - global dump_bytes_opened self.tabref = weakref.ref(tab) self.override_title = None self.last_mouse_cursor_pos = 0, 0 @@ -50,12 +50,6 @@ class Window: self.child_fd = child.child_fd self.start_visual_bell_at = None self.screen = Screen(self, 24, 80, opts.scrollback_lines) - self.read_bytes = partial(read_bytes_dump, self.dump_commands) if args.dump_commands or args.dump_bytes else read_bytes - if args.dump_bytes: - mode = 'ab' if dump_bytes_opened else 'wb' - self.dump_bytes_to = open(args.dump_bytes, mode) - dump_bytes_opened = True - self.draw_dump_buf = [] self.write_buf = memoryview(b'') self.char_grid = CharGrid(self.screen, opts) @@ -104,10 +98,6 @@ class Window: # existing buffers in char_grid. The rest of the cleanup must be # performed in the GUI thread. - def read_ready(self): - if self.read_bytes(self.screen, self.child_fd) is False: - self.close() # EOF - def write_ready(self): while self.write_buf: try: @@ -117,10 +107,14 @@ class Window: if not n: return self.write_buf = self.write_buf[n:] + if not self.write_buf: + get_boss().child_monitor.needs_write(self.child_fd, False) def write_to_child(self, data): - self.write_buf = memoryview(self.write_buf.tobytes() + data) - wakeup() + if data: + self.write_buf = memoryview(self.write_buf.tobytes() + data) + get_boss().child_monitor.needs_write(self.child_fd, True) + wakeup() def bell(self): if self.opts.enable_audio_bell: @@ -356,22 +350,3 @@ class Window: self.char_grid.scroll('full', False) glfw_post_empty_event() # }}} - - def dump_commands(self, *a): # {{{ - if a: - if a[0] == 'draw': - if a[1] is None: - if self.draw_dump_buf: - safe_print('draw', ''.join(self.draw_dump_buf)) - self.draw_dump_buf = [] - else: - self.draw_dump_buf.append(a[1]) - elif a[0] == 'bytes': - self.dump_bytes_to.write(a[1]) - self.dump_bytes_to.flush() - else: - if self.draw_dump_buf: - safe_print('draw', ''.join(self.draw_dump_buf)) - self.draw_dump_buf = [] - safe_print(*a) - # }}}