diff --git a/kitty/boss.py b/kitty/boss.py index ffc8dcfff..59357b201 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -44,6 +44,16 @@ class Timers(_Timers): 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) @@ -398,7 +408,7 @@ class Boss: d = int(self.opts.cursor_blink_interval * 1000) n = t // d draw_cursor = n % 2 == 0 - self.ui_timers.add_if_missing( + self.ui_timers.add_if_before( ((n + 1) * d / 1000) - now, wakeup_for_cursor_blink_render) if draw_cursor: with self.cursor_program: diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index d347153b2..5e9aad045 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -321,7 +321,7 @@ parse_input(ChildMonitor *self) { DECREF_CHILD(scratch[i]); } if (!parse_needed) { - timers_add(self->timers, self->repaint_delay - time_since_last_parse, false, Py_None, NULL); + timers_add_if_before(self->timers, self->repaint_delay - time_since_last_parse, Py_None, NULL); } } diff --git a/kitty/data-types.h b/kitty/data-types.h index f47720cef..0b82b5cdc 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -355,6 +355,7 @@ 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); bool set_iutf8(int, bool); color_type colorprofile_to_color(ColorProfile *self, color_type entry, color_type defval); diff --git a/kitty/timers.c b/kitty/timers.c index ccfe25d3c..0fc3efc65 100644 --- a/kitty/timers.c +++ b/kitty/timers.c @@ -96,10 +96,8 @@ _add(Timers *self, double at, PyObject *callback, PyObject *args) { return true; } -bool -timers_add(Timers *self, double delay, bool update, PyObject *callback, PyObject *args) { - double at = monotonic_() + delay; - +static inline bool +timers_add_(Timers *self, double at, bool update, PyObject *callback, PyObject *args) { 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); @@ -113,6 +111,11 @@ timers_add(Timers *self, double delay, bool update, PyObject *callback, PyObject 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) { @@ -124,6 +127,28 @@ add(Timers *self, PyObject *fargs) { Py_RETURN_NONE; } +bool +timers_add_if_before(Timers *self, double delay, PyObject *callback, PyObject *args) { + double at = monotonic_() + delay; + 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) { @@ -188,6 +213,8 @@ timers_call(Timers *self) { size_t i, j; for (i = 0, j = 0; i < self->count; i++) { if (self->events[i].at <= now) { // expired, call it + /* PyObject_Print(self->events[i].callback, stdout, 1); */ + /* printf("\n"); */ 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(); @@ -214,6 +241,7 @@ call(Timers *self) { 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)