Initial stab at switching to poll() instead of select()
Also avoid python code in the child monitoring inner loop
This commit is contained in:
parent
40254625d9
commit
6176607ac4
@ -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:
|
||||
|
||||
204
kitty/child-monitor.c
Normal file
204
kitty/child-monitor.c
Normal file
@ -0,0 +1,204 @@
|
||||
/*
|
||||
* child-monitor.c
|
||||
* Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* 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)
|
||||
// }}}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <poll.h>
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
#include <Python.h>
|
||||
#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*);
|
||||
|
||||
@ -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();
|
||||
}
|
||||
#endif
|
||||
_parse_bytes(screen, screen->read_buf, len, dump_callback);
|
||||
if(len > 0) { Py_RETURN_TRUE; }
|
||||
Py_RETURN_FALSE;
|
||||
return true;
|
||||
}
|
||||
#undef FNAME
|
||||
// }}}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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) {
|
||||
if (!timers_add_if_missing(self, delay, callback, args)) return NULL;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
}
|
||||
at = monotonic() + delay;
|
||||
|
||||
return _add(self, at, callback, args);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -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,9 +107,13 @@ 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):
|
||||
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):
|
||||
@ -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)
|
||||
# }}}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user