Add infrastructure for timers to the event loop
This commit is contained in:
parent
10f5461cd8
commit
9b73d25151
@ -10,6 +10,7 @@
|
||||
#include "screen.h"
|
||||
#include "fonts.h"
|
||||
#include "charsets.h"
|
||||
#include "timers.h"
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <float.h>
|
||||
@ -727,12 +728,35 @@ cm_thread_write(PyObject UNUSED *self, PyObject *args) {
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static EventLoopData main_event_loop = {0};
|
||||
|
||||
static inline void
|
||||
wait_for_events() {
|
||||
maximum_wait = prepare_for_poll(&main_event_loop, maximum_wait);
|
||||
event_loop_wait(maximum_wait);
|
||||
dispatch_timers(&main_event_loop);
|
||||
maximum_wait = -1;
|
||||
}
|
||||
|
||||
static void
|
||||
python_timer_callback(id_type timer_id, void *data) {
|
||||
PyObject *callback = (PyObject*)data;
|
||||
unsigned long long id = timer_id;
|
||||
PyObject *ret = PyObject_CallFunction(callback, "K", id);
|
||||
if (ret == NULL) PyErr_Print();
|
||||
else Py_DECREF(ret);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
add_python_timer(PyObject *self UNUSED, PyObject *args) {
|
||||
PyObject *callback;
|
||||
double interval;
|
||||
const char *name;
|
||||
if (!PyArg_ParseTuple(args, "sOd", &name, &callback, &interval)) return NULL;
|
||||
unsigned long long timer_id = add_timer(&main_event_loop, name, interval, 1, python_timer_callback, callback);
|
||||
return Py_BuildValue("K", timer_id);
|
||||
}
|
||||
|
||||
static inline void
|
||||
process_pending_resizes(double now) {
|
||||
global_state.has_pending_resizes = false;
|
||||
@ -1379,6 +1403,7 @@ static PyMethodDef methods[] = {
|
||||
METHOD(main_loop, METH_NOARGS)
|
||||
METHOD(mark_for_close, METH_VARARGS)
|
||||
METHOD(resize_pty, METH_VARARGS)
|
||||
METHODB(add_python_timer, METH_VARARGS),
|
||||
{"set_iutf8", (PyCFunction)pyset_iutf8, METH_VARARGS, ""},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
110
kitty/timers.c
Normal file
110
kitty/timers.c
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* timers.c
|
||||
* Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#include "timers.h"
|
||||
#include <float.h>
|
||||
|
||||
static id_type timer_counter = 0;
|
||||
|
||||
static int
|
||||
compare_timers(const void *a_, const void *b_) {
|
||||
const Timer *a = (const Timer*)a_, *b = (const Timer*)b_;
|
||||
return (a->trigger_at > b->trigger_at) ? 1 : (a->trigger_at < b->trigger_at) ? -1 : 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
update_timers(EventLoopData *eld) {
|
||||
if (eld->timers_count > 1) qsort(eld->timers, eld->timers_count, sizeof(eld->timers[0]), compare_timers);
|
||||
}
|
||||
|
||||
id_type
|
||||
add_timer(EventLoopData *eld, const char *name, double interval, int enabled, timer_callback_func cb, void *cb_data) {
|
||||
if (eld->timers_count >= sizeof(eld->timers)/sizeof(eld->timers[0])) {
|
||||
fprintf(stderr, "Too many timers added\n");
|
||||
return 0;
|
||||
}
|
||||
Timer *t = eld->timers + eld->timers_count++;
|
||||
t->interval = interval;
|
||||
t->name = name;
|
||||
t->trigger_at = enabled ? monotonic() + interval : DBL_MAX;
|
||||
t->callback = cb;
|
||||
t->callback_data = cb_data;
|
||||
t->id = ++timer_counter;
|
||||
update_timers(eld);
|
||||
return t->id;
|
||||
}
|
||||
|
||||
#define removeX(which, item_id, update_func) {\
|
||||
for (nfds_t i = 0; i < eld->which##_count; i++) { \
|
||||
if (eld->which[i].id == item_id) { \
|
||||
eld->which##_count--; \
|
||||
if (i < eld->which##_count) { \
|
||||
memmove(eld->which + i, eld->which + i + 1, sizeof(eld->which[0]) * (eld->which##_count - i)); \
|
||||
} \
|
||||
update_func(eld); break; \
|
||||
}}}
|
||||
|
||||
void
|
||||
remove_timer(EventLoopData *eld, id_type timer_id) {
|
||||
removeX(timers, timer_id, update_timers);
|
||||
}
|
||||
|
||||
void
|
||||
toggle_timer(EventLoopData *eld, id_type timer_id, int enabled) {
|
||||
for (nfds_t i = 0; i < eld->timers_count; i++) {
|
||||
if (eld->timers[i].id == timer_id) {
|
||||
double trigger_at = enabled ? (monotonic() + eld->timers[i].interval) : DBL_MAX;
|
||||
if (trigger_at != eld->timers[i].trigger_at) {
|
||||
eld->timers[i].trigger_at = trigger_at;
|
||||
update_timers(eld);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
change_timer_interval(EventLoopData *eld, id_type timer_id, double interval) {
|
||||
for (nfds_t i = 0; i < eld->timers_count; i++) {
|
||||
if (eld->timers[i].id == timer_id) {
|
||||
eld->timers[i].interval = interval;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
double
|
||||
prepare_for_poll(EventLoopData *eld, double timeout) {
|
||||
if (!eld->timers_count || eld->timers[0].trigger_at == DBL_MAX) return timeout;
|
||||
double now = monotonic(), next_repeat_at = eld->timers[0].trigger_at;
|
||||
if (timeout < 0 || now + timeout > next_repeat_at) {
|
||||
timeout = next_repeat_at <= now ? 0 : next_repeat_at - now;
|
||||
}
|
||||
return timeout;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
dispatch_timers(EventLoopData *eld) {
|
||||
if (!eld->timers_count || eld->timers[0].trigger_at == DBL_MAX) return 0;
|
||||
static struct { timer_callback_func func; id_type id; void* data; } dispatches[sizeof(eld->timers)/sizeof(eld->timers[0])];
|
||||
unsigned int num_dispatches = 0;
|
||||
double now = monotonic();
|
||||
for (nfds_t i = 0; i < eld->timers_count && eld->timers[i].trigger_at <= now; i++) {
|
||||
eld->timers[i].trigger_at = now + eld->timers[i].interval;
|
||||
dispatches[num_dispatches].func = eld->timers[i].callback;
|
||||
dispatches[num_dispatches].id = eld->timers[i].id;
|
||||
dispatches[num_dispatches].data = eld->timers[i].callback_data;
|
||||
num_dispatches++;
|
||||
}
|
||||
// we dispatch separately so that the callbacks can modify timers
|
||||
for (unsigned i = 0; i < num_dispatches; i++) {
|
||||
dispatches[i].func(dispatches[i].id, dispatches[i].data);
|
||||
}
|
||||
if (num_dispatches) update_timers(eld);
|
||||
return num_dispatches;
|
||||
}
|
||||
33
kitty/timers.h
Normal file
33
kitty/timers.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "data-types.h"
|
||||
|
||||
typedef void(*timer_callback_func)(id_type, void*);
|
||||
|
||||
typedef struct {
|
||||
id_type id;
|
||||
double interval, trigger_at;
|
||||
timer_callback_func callback;
|
||||
void *callback_data;
|
||||
const char *name;
|
||||
} Timer;
|
||||
|
||||
|
||||
typedef struct {
|
||||
nfds_t timers_count;
|
||||
Timer timers[128];
|
||||
} EventLoopData;
|
||||
|
||||
|
||||
double prepare_for_poll(EventLoopData *eld, double timeout);
|
||||
id_type add_timer(EventLoopData *eld, const char *name, double interval, int enabled, timer_callback_func cb, void *cb_data);
|
||||
void remove_timer(EventLoopData *eld, id_type timer_id);
|
||||
void toggle_time(EventLoopData *eld, id_type timer_id, int enabled);
|
||||
void change_timer_interval(EventLoopData *eld, id_type timer_id, double interval);
|
||||
unsigned int dispatch_timers(EventLoopData *eld);
|
||||
Loading…
x
Reference in New Issue
Block a user