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 (
|
from .fast_data_types import (
|
||||||
GLFW_MOUSE_BUTTON_1, GLFW_PRESS, GLFW_REPEAT, ChildMonitor,
|
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
|
glfw_post_empty_event, layout_sprite_map
|
||||||
)
|
)
|
||||||
from .fonts.render import render_cell_wrapper, set_font_family
|
from .fonts.render import render_cell_wrapper, set_font_family
|
||||||
@ -25,33 +25,6 @@ from .tabs import SpecialWindow, TabManager
|
|||||||
from .utils import safe_print
|
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: # {{{
|
class DumpCommands: # {{{
|
||||||
|
|
||||||
def __init__(self, args):
|
def __init__(self, args):
|
||||||
@ -90,10 +63,9 @@ class Boss:
|
|||||||
self.window_is_focused = True
|
self.window_is_focused = True
|
||||||
self.glfw_window_title = None
|
self.glfw_window_title = None
|
||||||
self.shutting_down = False
|
self.shutting_down = False
|
||||||
self.ui_timers = Timers()
|
|
||||||
self.child_monitor = ChildMonitor(
|
self.child_monitor = ChildMonitor(
|
||||||
opts.repaint_delay / 1000.0, glfw_window.window_id(),
|
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)
|
DumpCommands(args) if args.dump_commands or args.dump_bytes else None)
|
||||||
set_boss(self)
|
set_boss(self)
|
||||||
self.current_font_size = opts.font_size
|
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 INCREF_CHILD(x) XREF_CHILD(x, Py_INCREF)
|
||||||
#define DECREF_CHILD(x) XREF_CHILD(x, Py_DECREF)
|
#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
|
static void
|
||||||
handle_signal(int sig_num) {
|
handle_signal(int sig_num) {
|
||||||
int save_err = errno;
|
int save_err = errno;
|
||||||
@ -112,12 +121,12 @@ self_pipe(int fds[2]) {
|
|||||||
static PyObject *
|
static PyObject *
|
||||||
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
|
||||||
ChildMonitor *self;
|
ChildMonitor *self;
|
||||||
PyObject *dump_callback, *death_notify, *timers, *wid;
|
PyObject *dump_callback, *death_notify, *wid;
|
||||||
int ret;
|
int ret;
|
||||||
double repaint_delay;
|
double repaint_delay;
|
||||||
|
|
||||||
if (created) { PyErr_SetString(PyExc_RuntimeError, "Can have only a single ChildMonitor instance"); return NULL; }
|
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);
|
glfw_window_id = PyLong_AsVoidPtr(wid);
|
||||||
created = true;
|
created = true;
|
||||||
if ((ret = pthread_mutex_init(&children_lock, NULL)) != 0) {
|
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);
|
self = (ChildMonitor *)type->tp_alloc(type, 0);
|
||||||
if (self == NULL) return PyErr_NoMemory();
|
if (self == NULL) return PyErr_NoMemory();
|
||||||
self->death_notify = death_notify; Py_INCREF(death_notify);
|
self->death_notify = death_notify; Py_INCREF(death_notify);
|
||||||
self->timers = (Timers*)timers; Py_INCREF(timers);
|
|
||||||
if (dump_callback != Py_None) {
|
if (dump_callback != Py_None) {
|
||||||
self->dump_callback = dump_callback; Py_INCREF(dump_callback);
|
self->dump_callback = dump_callback; Py_INCREF(dump_callback);
|
||||||
parse_func = parse_worker_dump;
|
parse_func = parse_worker_dump;
|
||||||
@ -151,7 +159,6 @@ dealloc(ChildMonitor* self) {
|
|||||||
pthread_mutex_destroy(&children_lock);
|
pthread_mutex_destroy(&children_lock);
|
||||||
Py_CLEAR(self->dump_callback);
|
Py_CLEAR(self->dump_callback);
|
||||||
Py_CLEAR(self->death_notify);
|
Py_CLEAR(self->death_notify);
|
||||||
Py_CLEAR(self->timers);
|
|
||||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||||
while (remove_queue_count) {
|
while (remove_queue_count) {
|
||||||
remove_queue_count--;
|
remove_queue_count--;
|
||||||
@ -326,7 +333,7 @@ parse_input(ChildMonitor *self) {
|
|||||||
DECREF_CHILD(scratch[i]);
|
DECREF_CHILD(scratch[i]);
|
||||||
}
|
}
|
||||||
if (!parse_needed) {
|
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*);
|
extern void cocoa_update_title(PyObject*);
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
render_cursor(ChildMonitor *self, Window *w, double now) {
|
render_cursor(Window *w, double now) {
|
||||||
ScreenRenderData *rd = &w->render_data;
|
ScreenRenderData *rd = &w->render_data;
|
||||||
if (rd->screen->scrolled_by || ! screen_is_cursor_visible(rd->screen)) return;
|
if (rd->screen->scrolled_by || ! screen_is_cursor_visible(rd->screen)) return;
|
||||||
double time_since_start_blink = now - global_state.cursor_blink_zero_time;
|
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 d = (int)(OPT(cursor_blink_interval) * 1000);
|
||||||
int n = t / d;
|
int n = t / d;
|
||||||
do_draw_cursor = n % 2 == 0 ? true : false;
|
do_draw_cursor = n % 2 == 0 ? true : false;
|
||||||
double delay = MAX(0, ((n + 1) * d / 1000) - time_since_start_blink);
|
double bucket = (n + 1) * d;
|
||||||
timers_add_if_before(self->timers, delay, Py_None, NULL);
|
double delay = (bucket / 1000.0) - time_since_start_blink;
|
||||||
|
set_maximum_wait(delay);
|
||||||
}
|
}
|
||||||
if (do_draw_cursor) {
|
if (do_draw_cursor) {
|
||||||
Cursor *cursor = rd->screen->cursor;
|
Cursor *cursor = rd->screen->cursor;
|
||||||
@ -469,7 +477,7 @@ render_cursor(ChildMonitor *self, Window *w, double now) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
render(ChildMonitor *self, double *timeout, double now) {
|
render(ChildMonitor *self, double now) {
|
||||||
double time_since_last_render = now - last_render_at;
|
double time_since_last_render = now - last_render_at;
|
||||||
if (time_since_last_render > self->repaint_delay) {
|
if (time_since_last_render > self->repaint_delay) {
|
||||||
draw_borders();
|
draw_borders();
|
||||||
@ -481,10 +489,16 @@ render(ChildMonitor *self, double *timeout, double now) {
|
|||||||
for (size_t i = 0; i < tab->num_windows; i++) {
|
for (size_t i = 0; i < tab->num_windows; i++) {
|
||||||
Window *w = tab->windows + i;
|
Window *w = tab->windows + i;
|
||||||
#define WD w->render_data
|
#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;
|
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) {
|
if (w->title && w->title != global_state.application_title) {
|
||||||
global_state.application_title = w->title;
|
global_state.application_title = w->title;
|
||||||
glfwSetWindowTitle(glfw_window_id, PyUnicode_AsUTF8(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);
|
glfwSwapBuffers(glfw_window_id);
|
||||||
last_render_at = now;
|
last_render_at = now;
|
||||||
} else {
|
} else {
|
||||||
*timeout = self->repaint_delay - time_since_last_render;
|
set_maximum_wait(self->repaint_delay - time_since_last_render);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -553,20 +567,16 @@ cm_thread_write(PyObject UNUSED *self, PyObject *args) {
|
|||||||
static PyObject*
|
static PyObject*
|
||||||
main_loop(ChildMonitor *self) {
|
main_loop(ChildMonitor *self) {
|
||||||
#define main_loop_doc "The main thread loop"
|
#define main_loop_doc "The main thread loop"
|
||||||
double timeout = 0, t;
|
|
||||||
|
|
||||||
while (!glfwWindowShouldClose(glfw_window_id)) {
|
while (!glfwWindowShouldClose(glfw_window_id)) {
|
||||||
double now = monotonic();
|
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)) {
|
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);
|
glfwSetInputMode(glfw_window_id, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
||||||
global_state.mouse_visible = false;
|
global_state.mouse_visible = false;
|
||||||
}
|
}
|
||||||
t = timers_timeout(self->timers);
|
if (maximum_wait < 0) glfwWaitEvents();
|
||||||
timeout = MIN(timeout, t);
|
else if (maximum_wait > 0) glfwWaitEventsTimeout(maximum_wait);
|
||||||
if (timeout < 0) glfwWaitEvents();
|
|
||||||
else if (timeout > 0) glfwWaitEventsTimeout(timeout);
|
|
||||||
timers_call(self->timers);
|
|
||||||
parse_input(self);
|
parse_input(self);
|
||||||
}
|
}
|
||||||
if (PyErr_Occurred()) return NULL;
|
if (PyErr_Occurred()) return NULL;
|
||||||
|
|||||||
@ -13,6 +13,44 @@
|
|||||||
#include <gperftools/profiler.h>
|
#include <gperftools/profiler.h>
|
||||||
#endif
|
#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*
|
static PyObject*
|
||||||
wcwidth_wrap(PyObject UNUSED *self, PyObject *chr) {
|
wcwidth_wrap(PyObject UNUSED *self, PyObject *chr) {
|
||||||
@ -87,7 +125,6 @@ static struct PyModuleDef module = {
|
|||||||
extern int init_LineBuf(PyObject *);
|
extern int init_LineBuf(PyObject *);
|
||||||
extern int init_HistoryBuf(PyObject *);
|
extern int init_HistoryBuf(PyObject *);
|
||||||
extern int init_Cursor(PyObject *);
|
extern int init_Cursor(PyObject *);
|
||||||
extern int init_Timers(PyObject *);
|
|
||||||
extern int init_ChildMonitor(PyObject *);
|
extern int init_ChildMonitor(PyObject *);
|
||||||
extern int init_Line(PyObject *);
|
extern int init_Line(PyObject *);
|
||||||
extern int init_ColorProfile(PyObject *);
|
extern int init_ColorProfile(PyObject *);
|
||||||
@ -112,13 +149,15 @@ PyInit_fast_data_types(void) {
|
|||||||
|
|
||||||
m = PyModule_Create(&module);
|
m = PyModule_Create(&module);
|
||||||
if (m == NULL) return NULL;
|
if (m == NULL) return NULL;
|
||||||
|
#ifdef __APPLE__
|
||||||
|
mach_timebase_info(&timebase);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (m != NULL) {
|
if (m != NULL) {
|
||||||
if (!init_LineBuf(m)) return NULL;
|
if (!init_LineBuf(m)) return NULL;
|
||||||
if (!init_HistoryBuf(m)) return NULL;
|
if (!init_HistoryBuf(m)) return NULL;
|
||||||
if (!init_Line(m)) return NULL;
|
if (!init_Line(m)) return NULL;
|
||||||
if (!init_Cursor(m)) return NULL;
|
if (!init_Cursor(m)) return NULL;
|
||||||
if (!init_Timers(m)) return NULL;
|
|
||||||
if (!init_ChildMonitor(m)) return NULL;
|
if (!init_ChildMonitor(m)) return NULL;
|
||||||
if (!init_ColorProfile(m)) return NULL;
|
if (!init_ColorProfile(m)) return NULL;
|
||||||
if (!init_Screen(m)) return NULL;
|
if (!init_Screen(m)) return NULL;
|
||||||
|
|||||||
@ -265,21 +265,10 @@ typedef struct {
|
|||||||
PyObject *args;
|
PyObject *args;
|
||||||
} TimerEvent;
|
} TimerEvent;
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
PyObject_HEAD
|
|
||||||
|
|
||||||
TimerEvent *events, *buf1, *buf2;
|
|
||||||
size_t capacity;
|
|
||||||
size_t count;
|
|
||||||
bool in_call;
|
|
||||||
} Timers;
|
|
||||||
PyTypeObject Timers_Type;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
|
|
||||||
PyObject *dump_callback, *update_screen, *death_notify;
|
PyObject *dump_callback, *update_screen, *death_notify;
|
||||||
Timers *timers;
|
|
||||||
double repaint_delay;
|
double repaint_delay;
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
bool shutting_down;
|
bool shutting_down;
|
||||||
@ -317,11 +306,6 @@ void cursor_reset_display_attrs(Cursor*);
|
|||||||
void set_sprite_position(Cell *cell, Cell *previous_cell);
|
void set_sprite_position(Cell *cell, Cell *previous_cell);
|
||||||
|
|
||||||
double monotonic();
|
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);
|
PyObject* cm_thread_write(PyObject *self, PyObject *args);
|
||||||
bool set_iutf8(int, bool);
|
bool set_iutf8(int, bool);
|
||||||
|
|
||||||
|
|||||||
@ -1061,7 +1061,7 @@ screen_set_cursor(Screen *self, unsigned int mode, uint8_t secondary) {
|
|||||||
shape = 0; blink = false;
|
shape = 0; blink = false;
|
||||||
if (mode > 0) {
|
if (mode > 0) {
|
||||||
blink = mode % 2;
|
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) {
|
if (shape != self->cursor->shape || blink != self->cursor->blink) {
|
||||||
self->cursor->shape = shape; self->cursor->blink = 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)
|
next(window_counter)
|
||||||
|
|
||||||
|
|
||||||
def clear_visual_bell():
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Window:
|
class Window:
|
||||||
|
|
||||||
def __init__(self, tab, child, opts, args):
|
def __init__(self, tab, child, opts, args):
|
||||||
@ -59,7 +55,6 @@ class Window:
|
|||||||
self.title = appname
|
self.title = appname
|
||||||
self.is_visible_in_layout = True
|
self.is_visible_in_layout = True
|
||||||
self.child, self.opts = child, opts
|
self.child, self.opts = child, opts
|
||||||
self.start_visual_bell_at = None
|
|
||||||
self.screen = Screen(self, 24, 80, opts.scrollback_lines)
|
self.screen = Screen(self, 24, 80, opts.scrollback_lines)
|
||||||
self.char_grid = CharGrid(self.screen, opts)
|
self.char_grid = CharGrid(self.screen, opts)
|
||||||
self.current_pty_size = None
|
self.current_pty_size = None
|
||||||
@ -125,9 +120,6 @@ class Window:
|
|||||||
boss = get_boss()
|
boss = get_boss()
|
||||||
boss.request_attention()
|
boss.request_attention()
|
||||||
glfw_post_empty_event()
|
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):
|
def use_utf8(self, on):
|
||||||
get_boss().child_monitor.set_iutf8(self.window_id, 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.config import build_ansi_color_table, defaults
|
||||||
from kitty.fast_data_types import (
|
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
|
sprite_map_set_layout, sprite_map_set_limits, sprite_position_for
|
||||||
)
|
)
|
||||||
from kitty.utils import sanitize_title, wcwidth
|
from kitty.utils import sanitize_title, wcwidth
|
||||||
@ -367,23 +367,3 @@ class TestDataTypes(BaseTest):
|
|||||||
a = []
|
a = []
|
||||||
hb.as_ansi(a.append)
|
hb.as_ansi(a.append)
|
||||||
self.ae(a, ['\x1b[0m' + str(hb.line(i)) + '\n' for i in range(hb.count - 1, -1, -1)])
|
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