From c68372543437e68a50c7add6963c549be171850b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 13 Sep 2017 14:17:21 +0530 Subject: [PATCH] Get rid of the timers infrastructure It is not needed with a pure state machine. Note that drag scrolling still has to be ported from using timers. --- kitty/boss.py | 32 +---- kitty/child-monitor.c | 50 ++++--- kitty/data-types.c | 43 +++++- kitty/data-types.h | 16 --- kitty/screen.c | 2 +- kitty/timers.c | 285 --------------------------------------- kitty/window.py | 8 -- kitty_tests/datatypes.py | 22 +-- 8 files changed, 75 insertions(+), 383 deletions(-) delete mode 100644 kitty/timers.c diff --git a/kitty/boss.py b/kitty/boss.py index b042b19d3..872753d0d 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -13,7 +13,7 @@ from .constants import ( ) from .fast_data_types import ( GLFW_MOUSE_BUTTON_1, GLFW_PRESS, GLFW_REPEAT, ChildMonitor, - Timers as _Timers, destroy_global_data, destroy_sprite_map, + destroy_global_data, destroy_sprite_map, glfw_post_empty_event, layout_sprite_map ) from .fonts.render import render_cell_wrapper, set_font_family @@ -25,33 +25,6 @@ from .tabs import SpecialWindow, TabManager from .utils import safe_print -class Timers(_Timers): - - def __init__(self): - _Timers.__init__(self) - self.timer_hash = {} - - def add(self, delay, timer, *args): - # Needed because bound methods are recreated on every access - timer = self.timer_hash.setdefault(timer, timer) - return _Timers.add(self, delay, timer, args) if args else _Timers.add(self, delay, timer) - - def add_if_before(self, delay, timer, *args): - # Needed because bound methods are recreated on every access - timer = self.timer_hash.setdefault(timer, timer) - return _Timers.add_if_before(self, delay, timer, *args) - - def add_if_missing(self, delay, timer, *args): - # Needed because bound methods are recreated on every access - timer = self.timer_hash.setdefault(timer, timer) - return _Timers.add_if_missing(self, delay, timer, *args) - - def remove(self, timer): - # Needed because bound methods are recreated on every access - timer = self.timer_hash.setdefault(timer, timer) - return _Timers.remove_event(self, timer) - - class DumpCommands: # {{{ def __init__(self, args): @@ -90,10 +63,9 @@ class Boss: self.window_is_focused = True self.glfw_window_title = None self.shutting_down = False - self.ui_timers = Timers() self.child_monitor = ChildMonitor( opts.repaint_delay / 1000.0, glfw_window.window_id(), - self.on_child_death, self.ui_timers, + self.on_child_death, DumpCommands(args) if args.dump_commands or args.dump_bytes else None) set_boss(self) self.current_font_size = opts.font_size diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 87bf43637..cfefd0776 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -83,6 +83,15 @@ set_thread_name(const char *name) { #define INCREF_CHILD(x) XREF_CHILD(x, Py_INCREF) #define DECREF_CHILD(x) XREF_CHILD(x, Py_DECREF) +// The max time (in secs) to wait for events from the window system +// before ticking over the main loop. Negative values mean wait forever. +static double maximum_wait = -1.0; + +static inline void +set_maximum_wait(double val) { + if (val >= 0 && (val < maximum_wait || maximum_wait < 0)) maximum_wait = val; +} + static void handle_signal(int sig_num) { int save_err = errno; @@ -112,12 +121,12 @@ self_pipe(int fds[2]) { static PyObject * new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { ChildMonitor *self; - PyObject *dump_callback, *death_notify, *timers, *wid; + PyObject *dump_callback, *death_notify, *wid; int ret; double repaint_delay; if (created) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; } - if (!PyArg_ParseTuple(args, "dOOOO", &repaint_delay, &wid, &death_notify, &timers, &dump_callback)) return NULL; + if (!PyArg_ParseTuple(args, "dOOO", &repaint_delay, &wid, &death_notify, &dump_callback)) return NULL; glfw_window_id = PyLong_AsVoidPtr(wid); created = true; if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) { @@ -133,7 +142,6 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { self = (ChildMonitor *)type->tp_alloc(type, 0); if (self == NULL) return PyErr_NoMemory(); self->death_notify = death_notify; Py_INCREF(death_notify); - self->timers = (Timers*)timers; Py_INCREF(timers); if (dump_callback != Py_None) { self->dump_callback = dump_callback; Py_INCREF(dump_callback); parse_func = parse_worker_dump; @@ -151,7 +159,6 @@ dealloc(ChildMonitor* self) { pthread_mutex_destroy(&children_lock); Py_CLEAR(self->dump_callback); Py_CLEAR(self->death_notify); - Py_CLEAR(self->timers); Py_TYPE(self)->tp_free((PyObject*)self); while (remove_queue_count) { remove_queue_count--; @@ -326,7 +333,7 @@ parse_input(ChildMonitor *self) { DECREF_CHILD(scratch[i]); } if (!parse_needed) { - timers_add_if_before(self->timers, self->repaint_delay - time_since_last_parse, Py_None, NULL); + set_maximum_wait(self->repaint_delay - time_since_last_parse); } } @@ -438,7 +445,7 @@ cursor_width(double w, bool vert) { extern void cocoa_update_title(PyObject*); static inline void -render_cursor(ChildMonitor *self, Window *w, double now) { +render_cursor(Window *w, double now) { ScreenRenderData *rd = &w->render_data; if (rd->screen->scrolled_by || ! screen_is_cursor_visible(rd->screen)) return; double time_since_start_blink = now - global_state.cursor_blink_zero_time; @@ -449,8 +456,9 @@ render_cursor(ChildMonitor *self, Window *w, double now) { int d = (int)(OPT(cursor_blink_interval) * 1000); int n = t / d; do_draw_cursor = n % 2 == 0 ? true : false; - double delay = MAX(0, ((n + 1) * d / 1000) - time_since_start_blink); - timers_add_if_before(self->timers, delay, Py_None, NULL); + double bucket = (n + 1) * d; + double delay = (bucket / 1000.0) - time_since_start_blink; + set_maximum_wait(delay); } if (do_draw_cursor) { Cursor *cursor = rd->screen->cursor; @@ -469,7 +477,7 @@ render_cursor(ChildMonitor *self, Window *w, double now) { } static inline bool -render(ChildMonitor *self, double *timeout, double now) { +render(ChildMonitor *self, double now) { double time_since_last_render = now - last_render_at; if (time_since_last_render > self->repaint_delay) { draw_borders(); @@ -481,10 +489,16 @@ render(ChildMonitor *self, double *timeout, double now) { for (size_t i = 0; i < tab->num_windows; i++) { Window *w = tab->windows + i; #define WD w->render_data - if (w->visible && WD.screen) draw_cells(WD.vao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen); + if (w->visible && WD.screen) { + draw_cells(WD.vao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen); + if (WD.screen->start_visual_bell_at != 0) { + double bell_left = global_state.opts.visual_bell_duration - (now - WD.screen->start_visual_bell_at); + set_maximum_wait(bell_left); + } + } } Window *w = tab->windows + tab->active_window; - if (w->visible && WD.screen) render_cursor(self, w, now); + if (w->visible && WD.screen) render_cursor(w, now); if (w->title && w->title != global_state.application_title) { global_state.application_title = w->title; glfwSetWindowTitle(glfw_window_id, PyUnicode_AsUTF8(w->title)); @@ -497,7 +511,7 @@ render(ChildMonitor *self, double *timeout, double now) { glfwSwapBuffers(glfw_window_id); last_render_at = now; } else { - *timeout = self->repaint_delay - time_since_last_render; + set_maximum_wait(self->repaint_delay - time_since_last_render); } return true; } @@ -553,20 +567,16 @@ cm_thread_write(PyObject UNUSED *self, PyObject *args) { static PyObject* main_loop(ChildMonitor *self) { #define main_loop_doc "The main thread loop" - double timeout = 0, t; - while (!glfwWindowShouldClose(glfw_window_id)) { double now = monotonic(); - if (!render(self, &timeout, now)) break; + maximum_wait = -1; + if (!render(self, now)) break; if (global_state.mouse_visible && OPT(mouse_hide_wait) > 0 && now - global_state.last_mouse_activity_at > OPT(mouse_hide_wait)) { glfwSetInputMode(glfw_window_id, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); global_state.mouse_visible = false; } - t = timers_timeout(self->timers); - timeout = MIN(timeout, t); - if (timeout < 0) glfwWaitEvents(); - else if (timeout > 0) glfwWaitEventsTimeout(timeout); - timers_call(self->timers); + if (maximum_wait < 0) glfwWaitEvents(); + else if (maximum_wait > 0) glfwWaitEventsTimeout(maximum_wait); parse_input(self); } if (PyErr_Occurred()) return NULL; diff --git a/kitty/data-types.c b/kitty/data-types.c index 475707d5e..6f1c5590b 100644 --- a/kitty/data-types.c +++ b/kitty/data-types.c @@ -13,6 +13,44 @@ #include #endif +/* To millisecond (10^-3) */ +#define SEC_TO_MS 1000 + +/* To microseconds (10^-6) */ +#define MS_TO_US 1000 +#define SEC_TO_US (SEC_TO_MS * MS_TO_US) + +/* To nanoseconds (10^-9) */ +#define US_TO_NS 1000 +#define MS_TO_NS (MS_TO_US * US_TO_NS) +#define SEC_TO_NS (SEC_TO_MS * MS_TO_NS) + +/* Conversion from nanoseconds */ +#define NS_TO_MS (1000 * 1000) +#define NS_TO_US (1000) + +#ifdef __APPLE__ +#include +static mach_timebase_info_data_t timebase = {0}; +static inline double monotonic_() { + return ((double)(mach_absolute_time() * timebase.numer) / timebase.denom)/SEC_TO_NS; +} +#else +#include +static inline double monotonic_() { + struct timespec ts = {0}; +#ifdef CLOCK_HIGHRES + clock_gettime(CLOCK_HIGHRES, &ts); +#elif CLOCK_MONOTONIC_RAW + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); +#else + clock_gettime(CLOCK_MONOTONIC, &ts); +#endif + return (((double)ts.tv_nsec) / SEC_TO_NS) + (double)ts.tv_sec; +} +#endif + +double monotonic() { return monotonic_(); } static PyObject* wcwidth_wrap(PyObject UNUSED *self, PyObject *chr) { @@ -87,7 +125,6 @@ static struct PyModuleDef module = { extern int init_LineBuf(PyObject *); extern int init_HistoryBuf(PyObject *); extern int init_Cursor(PyObject *); -extern int init_Timers(PyObject *); extern int init_ChildMonitor(PyObject *); extern int init_Line(PyObject *); extern int init_ColorProfile(PyObject *); @@ -112,13 +149,15 @@ PyInit_fast_data_types(void) { m = PyModule_Create(&module); if (m == NULL) return NULL; +#ifdef __APPLE__ + mach_timebase_info(&timebase); +#endif if (m != NULL) { if (!init_LineBuf(m)) return NULL; if (!init_HistoryBuf(m)) return NULL; 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_Screen(m)) return NULL; diff --git a/kitty/data-types.h b/kitty/data-types.h index bc2a39674..1567ff93a 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -265,21 +265,10 @@ typedef struct { PyObject *args; } TimerEvent; -typedef struct { - PyObject_HEAD - - TimerEvent *events, *buf1, *buf2; - size_t capacity; - size_t count; - bool in_call; -} Timers; -PyTypeObject Timers_Type; - typedef struct { PyObject_HEAD PyObject *dump_callback, *update_screen, *death_notify; - Timers *timers; double repaint_delay; unsigned int count; bool shutting_down; @@ -317,11 +306,6 @@ void cursor_reset_display_attrs(Cursor*); void set_sprite_position(Cell *cell, Cell *previous_cell); double monotonic(); -double timers_timeout(Timers*); -void timers_call(Timers*); -bool timers_add(Timers *self, double delay, bool, PyObject *callback, PyObject *args); -bool timers_add_if_missing(Timers *self, double delay, PyObject *callback, PyObject *args); -bool timers_add_if_before(Timers *self, double delay, PyObject *callback, PyObject *args); PyObject* cm_thread_write(PyObject *self, PyObject *args); bool set_iutf8(int, bool); diff --git a/kitty/screen.c b/kitty/screen.c index a6ed8982b..6a8829abe 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -1061,7 +1061,7 @@ screen_set_cursor(Screen *self, unsigned int mode, uint8_t secondary) { shape = 0; blink = false; if (mode > 0) { blink = mode % 2; - shape = (mode < 3) ? CURSOR_BLOCK : (mode < 5) ? CURSOR_UNDERLINE : (mode < 7) ? CURSOR_BEAM : 0; + shape = (mode < 3) ? CURSOR_BLOCK : (mode < 5) ? CURSOR_UNDERLINE : (mode < 7) ? CURSOR_BEAM : NO_CURSOR_SHAPE; } if (shape != self->cursor->shape || blink != self->cursor->blink) { self->cursor->shape = shape; self->cursor->blink = blink; diff --git a/kitty/timers.c b/kitty/timers.c deleted file mode 100644 index 924dee61a..000000000 --- a/kitty/timers.c +++ /dev/null @@ -1,285 +0,0 @@ -/* - * timers.c - * Copyright (C) 2017 Kovid Goyal - * - * Distributed under terms of the GPL3 license. - */ - -#ifdef __APPLE__ -#define EXTRA_INIT mach_timebase_info(&timebase); -#endif -#include "data-types.h" -#include -/* To millisecond (10^-3) */ -#define SEC_TO_MS 1000 - -/* To microseconds (10^-6) */ -#define MS_TO_US 1000 -#define SEC_TO_US (SEC_TO_MS * MS_TO_US) - -/* To nanoseconds (10^-9) */ -#define US_TO_NS 1000 -#define MS_TO_NS (MS_TO_US * US_TO_NS) -#define SEC_TO_NS (SEC_TO_MS * MS_TO_NS) - -/* Conversion from nanoseconds */ -#define NS_TO_MS (1000 * 1000) -#define NS_TO_US (1000) - -#ifdef __APPLE__ -#include -static mach_timebase_info_data_t timebase = {0}; -static inline double monotonic_() { - return ((double)(mach_absolute_time() * timebase.numer) / timebase.denom)/SEC_TO_NS; -} -#else -#include -static inline double monotonic_() { - struct timespec ts = {0}; -#ifdef CLOCK_HIGHRES - clock_gettime(CLOCK_HIGHRES, &ts); -#elif CLOCK_MONOTONIC_RAW - clock_gettime(CLOCK_MONOTONIC_RAW, &ts); -#else - clock_gettime(CLOCK_MONOTONIC, &ts); -#endif - return (((double)ts.tv_nsec) / SEC_TO_NS) + (double)ts.tv_sec; -} -#endif - -double monotonic() { return monotonic_(); } - -static PyObject * -new(PyTypeObject *type, PyObject UNUSED *args, PyObject UNUSED *kwds) { - Timers *self; - - self = (Timers *)type->tp_alloc(type, 0); - self->capacity = 1024; - self->count = 0; - self->in_call = false; - self->buf1 = (TimerEvent*)PyMem_Calloc(2 * self->capacity, sizeof(TimerEvent)); - if (self->buf1 == NULL) { Py_CLEAR(self); return PyErr_NoMemory(); } - self->events = self->buf1; - self->buf2 = self->buf1 + self->capacity; - return (PyObject*) self; -} - -static void -dealloc(Timers* self) { - if (self->events) { - for (size_t i = 0; i < self->count; i++) { - Py_CLEAR(self->events[i].callback); Py_CLEAR(self->events[i].args); - } - PyMem_Free(self->buf1); self->events = NULL; - } - Py_TYPE(self)->tp_free((PyObject*)self); -} - - -static int -compare_events(const void *a, const void *b) { - double av = ((TimerEvent*)(a))->at, bv = ((TimerEvent*)(b))->at; - return av > bv ? 1 : (av == bv ? 0 : -1); -} - - -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 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); - return true; -} - -static inline bool -timers_add_(Timers *self, double at, bool update, PyObject *callback, PyObject *args) { - if (self->in_call) { PyErr_SetString(PyExc_ValueError, "Cannot add timers in a timer handler"); return false; } - for (size_t i = 0; i < self->count; i++) { - if (self->events[i].callback == callback) { - self->events[i].at = update ? at : MIN(at, self->events[i].at); - Py_CLEAR(self->events[i].args); - self->events[i].args = args; - Py_XINCREF(args); - qsort(self->events, self->count, sizeof(TimerEvent), compare_events); - return true; - } - } - return _add(self, at, callback, args); -} - -bool -timers_add(Timers *self, double delay, bool update, PyObject *callback, PyObject *args) { - return timers_add_(self, monotonic_() + delay, update, callback, args); -} - - -static PyObject * -add(Timers *self, PyObject *fargs) { -#define add_doc "add(delay, callback, args) -> Add callback, replacing it if it already exists" - PyObject *callback, *args = NULL; - double delay; - if (!PyArg_ParseTuple(fargs, "dO|O", &delay, &callback, &args)) return NULL; - if (!timers_add(self, delay, true, callback, args)) return NULL; - Py_RETURN_NONE; -} - -bool -timers_add_if_before(Timers *self, double delay, PyObject *callback, PyObject *args) { - double at = monotonic_() + delay; - if (self->in_call) { PyErr_SetString(PyExc_ValueError, "Cannot add timers in a timer handler"); return false; } - for (size_t i = 0; i < self->count; i++) { - if (self->events[i].at < at) { - return true; - } - } - return timers_add_(self, at, true, callback, args); -} - -static PyObject * -add_if_before(Timers *self, PyObject *fargs) { -#define add_if_before_doc "add_if_before(delay, callback, args) -> Add callback, unless another callback scheduled before this one already exists." - PyObject *callback, *args = NULL; - double delay; - if (!PyArg_ParseTuple(fargs, "dO|O", &delay, &callback, &args)) return NULL; - - if (!timers_add_if_before(self, delay, callback, args)) return NULL; - Py_RETURN_NONE; -} - - -bool -timers_add_if_missing(Timers *self, double delay, PyObject *callback, PyObject *args) { - if (self->in_call) { PyErr_SetString(PyExc_ValueError, "Cannot add timers in a timer handler"); return false; } - 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" - PyObject *callback, *args = NULL; - double delay; - if (!PyArg_ParseTuple(fargs, "dO|O", &delay, &callback, &args)) return NULL; - - if (!timers_add_if_missing(self, delay, callback, args)) return NULL; - Py_RETURN_NONE; -} - -static PyObject * -remove_event(Timers *self, PyObject *callback) { -#define remove_event_doc "remove(callback) -> Remove the event with the specified callback, if present" - TimerEvent *other = self->events == self->buf1 ? self->buf2 : self->buf1; - size_t i, j; - if (self->in_call) { PyErr_SetString(PyExc_ValueError, "Cannot remove timers in a timer handler"); return false; } - for (i = 0, j = 0; i < self->count; i++) { - if (self->events[i].callback != callback) { - other[j].callback = self->events[i].callback; other[j].at = self->events[i].at; other[j].args = self->events[i].args; - j++; - } else { - Py_CLEAR(self->events[i].callback); Py_CLEAR(self->events[i].args); - } - } - self->events = other; - self->count = j; - Py_RETURN_NONE; -} - -double -timers_timeout(Timers *self) { - if (self->count < 1) return -1; - 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" - if (self->count < 1) { Py_RETURN_NONE; } - double ans = self->events[0].at - monotonic_(); - return PyFloat_FromDouble(MAX(0, ans)); -} - -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; - self->in_call = true; - bool needs_sort = false; - for (i = 0, j = 0; i < self->count; i++) { - bool remove = false; - if (self->events[i].at <= now) { // expired, call it - /* PyObject_Print(self->events[i].callback, stdout, 1); */ - /* printf("\n"); */ - remove = true; - if (self->events[i].callback != Py_None) { - PyObject *ret = self->events[i].args ? PyObject_CallObject(self->events[i].callback, self->events[i].args) : PyObject_CallFunctionObjArgs(self->events[i].callback, NULL); - if (ret == NULL) PyErr_Print(); - else { - if (ret != Py_None && PyFloat_Check(ret)) { - self->events[i].at = monotonic_() + PyFloat_AS_DOUBLE(ret); - remove = false; - needs_sort = true; - } - Py_DECREF(ret); - } - } - } - if (remove) { - Py_CLEAR(self->events[i].callback); Py_CLEAR(self->events[i].args); - } else { - other[j].callback = self->events[i].callback; other[j].at = self->events[i].at; other[j].args = self->events[i].args; - j++; - } - } - self->events = other; - self->count = j; - self->in_call = false; - if (needs_sort) qsort(self->events, self->count, sizeof(TimerEvent), compare_events); -} - -static PyObject * -call(Timers *self) { -#define call_doc "call() -> Dispatch all expired events" - timers_call(self); - Py_RETURN_NONE; -} - -// Boilerplate {{{ -static PyMethodDef methods[] = { - METHOD(add, METH_VARARGS) - METHOD(add_if_missing, METH_VARARGS) - METHOD(add_if_before, METH_VARARGS) - METHOD(remove_event, METH_O) - METHOD(timeout, METH_NOARGS) - METHOD(call, METH_NOARGS) - {NULL} /* Sentinel */ -}; - - -PyTypeObject Timers_Type = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "fast_data_types.Timers", - .tp_basicsize = sizeof(Timers), - .tp_dealloc = (destructor)dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = "Timers", - .tp_methods = methods, - .tp_new = new, -}; - - -INIT_TYPE(Timers) -// }}} diff --git a/kitty/window.py b/kitty/window.py index 524b383ef..0eb0cf6a2 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -39,10 +39,6 @@ window_counter = count() next(window_counter) -def clear_visual_bell(): - pass - - class Window: def __init__(self, tab, child, opts, args): @@ -59,7 +55,6 @@ class Window: self.title = appname self.is_visible_in_layout = True self.child, self.opts = child, opts - self.start_visual_bell_at = None self.screen = Screen(self, 24, 80, opts.scrollback_lines) self.char_grid = CharGrid(self.screen, opts) self.current_pty_size = None @@ -125,9 +120,6 @@ class Window: boss = get_boss() boss.request_attention() glfw_post_empty_event() - if self.opts.visual_bell_duration > 0: - # ensure that the UI thread wakes up and clears the visual bell - boss.ui_timers.add(self.opts.visual_bell_duration + 0.01, clear_visual_bell) def use_utf8(self, on): get_boss().child_monitor.set_iutf8(self.window_id, on) diff --git a/kitty_tests/datatypes.py b/kitty_tests/datatypes.py index 0de9a547e..ea532a6ad 100644 --- a/kitty_tests/datatypes.py +++ b/kitty_tests/datatypes.py @@ -7,7 +7,7 @@ from unittest import skipIf from kitty.config import build_ansi_color_table, defaults from kitty.fast_data_types import ( - REVERSE, ColorProfile, Cursor as C, HistoryBuf, LineBuf, Timers, + REVERSE, ColorProfile, Cursor as C, HistoryBuf, LineBuf, sprite_map_set_layout, sprite_map_set_limits, sprite_position_for ) from kitty.utils import sanitize_title, wcwidth @@ -367,23 +367,3 @@ class TestDataTypes(BaseTest): a = [] hb.as_ansi(a.append) self.ae(a, ['\x1b[0m' + str(hb.line(i)) + '\n' for i in range(hb.count - 1, -1, -1)]) - - def test_timers(self): - t = Timers() - a = [] - t.add(10, a.append, (1,)) - t.add(0, a.append, (2,)) - t.add_if_missing(20, a.append, (3,)) - self.ae(t.timeout(), 0) - t.call(), t.call() - self.ae(a, [2]) - del a[:] - t.add(200, a.append, (4,)) - d = t.timeout() - self.assertGreater(d, 8) - self.assertLess(d, 10.1) - t.call() - self.ae(a, []) - t.add(0, a.append, (5,)) - t.call() - self.ae(a, [5])