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.
This commit is contained in:
parent
f3cd0a4966
commit
c683725434
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -13,6 +13,44 @@
|
||||
#include <gperftools/profiler.h>
|
||||
#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 <mach/mach_time.h>
|
||||
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 <time.h>
|
||||
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;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
285
kitty/timers.c
285
kitty/timers.c
@ -1,285 +0,0 @@
|
||||
/*
|
||||
* timers.c
|
||||
* Copyright (C) 2017 Kovid Goyal <kovid at kovidgoyal.net>
|
||||
*
|
||||
* Distributed under terms of the GPL3 license.
|
||||
*/
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define EXTRA_INIT mach_timebase_info(&timebase);
|
||||
#endif
|
||||
#include "data-types.h"
|
||||
#include <stdlib.h>
|
||||
/* 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 <mach/mach_time.h>
|
||||
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 <time.h>
|
||||
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)
|
||||
// }}}
|
||||
@ -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)
|
||||
|
||||
@ -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])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user