Use datatype monotonic_t instead of double to keep track of time

The time is stored in a signed 64 bit integer with nanosecond accuracy. This eliminates the possibility of floating-point inaccuracies.
`monotonic_t` can currently hold values large enough to work correctly for more than 200 years into the future.
Using a typedef instead of directly using `int64_t` everywhere will also allow easily changing the datatype in the future should the need arise for more precise or bigger time values.
This commit is contained in:
Luflosi
2019-08-02 11:49:02 -05:00
parent 4ec1a8d9c3
commit f3b9ff5f9f
33 changed files with 298 additions and 243 deletions

View File

@@ -11,6 +11,7 @@
#include "screen.h"
#include "fonts.h"
#include "charsets.h"
#include "monotonic.h"
#include <termios.h>
#include <unistd.h>
#include <float.h>
@@ -34,7 +35,7 @@ extern PyTypeObject Screen_Type;
#endif
#define USE_RENDER_FRAMES (global_state.has_render_frames && OPT(sync_to_monitor))
static void (*parse_func)(Screen*, PyObject*, double);
static void (*parse_func)(Screen*, PyObject*, monotonic_t);
typedef struct {
char *data;
@@ -110,10 +111,10 @@ static size_t reaped_pids_count = 0;
// 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 monotonic_t maximum_wait = -1;
static inline void
set_maximum_wait(double val) {
set_maximum_wait(monotonic_t val) {
if (val >= 0 && (val < maximum_wait || maximum_wait < 0)) maximum_wait = val;
}
@@ -296,11 +297,11 @@ shutdown_monitor(ChildMonitor *self, PyObject *a UNUSED) {
}
static inline bool
do_parse(ChildMonitor *self, Screen *screen, double now) {
do_parse(ChildMonitor *self, Screen *screen, monotonic_t now) {
bool input_read = false;
screen_mutex(lock, read);
if (screen->read_buf_sz || screen->pending_mode.used) {
double time_since_new_input = now - screen->new_input_at;
monotonic_t time_since_new_input = now - screen->new_input_at;
if (time_since_new_input >= OPT(input_delay)) {
bool read_buf_full = screen->read_buf_sz >= READ_BUF_SZ;
input_read = true;
@@ -308,7 +309,7 @@ do_parse(ChildMonitor *self, Screen *screen, double now) {
if (read_buf_full) wakeup_io_loop(self, false); // Ensure the read fd has POLLIN set
screen->new_input_at = 0;
if (screen->pending_mode.activated_at) {
double time_since_pending = MAX(0, now - screen->pending_mode.activated_at);
monotonic_t time_since_pending = MAX(0, now - screen->pending_mode.activated_at);
set_maximum_wait(screen->pending_mode.wait_time - time_since_pending);
}
} else set_maximum_wait(OPT(input_delay) - time_since_new_input);
@@ -322,7 +323,7 @@ parse_input(ChildMonitor *self) {
// Parse all available input that was read in the I/O thread.
size_t count = 0, remove_count = 0;
bool input_read = false;
double now = monotonic();
monotonic_t now = monotonic();
PyObject *msg = NULL;
children_mutex(lock);
while (remove_queue_count) {
@@ -485,22 +486,22 @@ pyset_iutf8(ChildMonitor *self, PyObject *args) {
extern void cocoa_update_menu_bar_title(PyObject*);
static inline void
collect_cursor_info(CursorRenderInfo *ans, Window *w, double now, OSWindow *os_window) {
collect_cursor_info(CursorRenderInfo *ans, Window *w, monotonic_t now, OSWindow *os_window) {
ScreenRenderData *rd = &w->render_data;
Cursor *cursor = rd->screen->cursor;
ans->x = cursor->x; ans->y = cursor->y;
ans->is_visible = false;
if (rd->screen->scrolled_by || !screen_is_cursor_visible(rd->screen)) return;
double time_since_start_blink = now - os_window->cursor_blink_zero_time;
monotonic_t time_since_start_blink = now - os_window->cursor_blink_zero_time;
bool cursor_blinking = OPT(cursor_blink_interval) > 0 && os_window->is_focused && (OPT(cursor_stop_blinking_after) == 0 || time_since_start_blink <= OPT(cursor_stop_blinking_after));
bool do_draw_cursor = true;
if (cursor_blinking) {
int t = (int)(time_since_start_blink * 1000);
int d = (int)(OPT(cursor_blink_interval) * 1000);
int t = monotonic_t_to_ms(time_since_start_blink);
int d = monotonic_t_to_ms(OPT(cursor_blink_interval));
int n = t / d;
do_draw_cursor = n % 2 == 0 ? true : false;
double bucket = (n + 1) * d;
double delay = (bucket / 1000.0) - time_since_start_blink;
monotonic_t bucket = ms_to_monotonic_t((n + 1) * d);
monotonic_t delay = bucket - time_since_start_blink;
set_maximum_wait(delay);
}
if (!do_draw_cursor) { ans->is_visible = false; return; }
@@ -526,7 +527,7 @@ update_window_title(Window *w, OSWindow *os_window) {
}
static inline bool
prepare_to_render_os_window(OSWindow *os_window, double now, unsigned int *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows, bool *all_windows_have_same_bg) {
prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int *active_window_id, color_type *active_window_bg, unsigned int *num_visible_windows, bool *all_windows_have_same_bg) {
#define TD os_window->tab_bar_render_data
bool needs_render = os_window->needs_render;
os_window->needs_render = false;
@@ -555,10 +556,10 @@ prepare_to_render_os_window(OSWindow *os_window, double now, unsigned int *activ
if (*num_visible_windows == 1) first_window_bg = window_bg;
if (first_window_bg != window_bg) all_windows_have_same_bg = false;
if (w->last_drag_scroll_at > 0) {
if (now - w->last_drag_scroll_at >= 0.02) {
if (now - w->last_drag_scroll_at >= ms_to_monotonic_t(20ll)) {
if (drag_scroll(w, os_window)) {
w->last_drag_scroll_at = now;
set_maximum_wait(0.02);
set_maximum_wait(ms_to_monotonic_t(20ll));
needs_render = true;
} else w->last_drag_scroll_at = 0;
} else set_maximum_wait(now - w->last_drag_scroll_at);
@@ -579,7 +580,7 @@ prepare_to_render_os_window(OSWindow *os_window, double now, unsigned int *activ
}
static inline void
render_os_window(OSWindow *os_window, double now, unsigned int active_window_id, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg) {
render_os_window(OSWindow *os_window, monotonic_t now, unsigned int active_window_id, color_type active_window_bg, unsigned int num_visible_windows, bool all_windows_have_same_bg) {
// ensure all pixels are cleared to background color at least once in every buffer
if (os_window->clear_count++ < 3) blank_os_window(os_window);
Tab *tab = os_window->tabs + os_window->active_tab;
@@ -601,7 +602,7 @@ render_os_window(OSWindow *os_window, double now, unsigned int active_window_id,
bool is_active_window = i == tab->active_window;
draw_cells(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx * x_ratio, WD.dy * y_ratio, WD.screen, os_window, is_active_window, true);
if (WD.screen->start_visual_bell_at != 0) {
double bell_left = OPT(visual_bell_duration) - (now - WD.screen->start_visual_bell_at);
monotonic_t bell_left = OPT(visual_bell_duration) - (now - WD.screen->start_visual_bell_at);
set_maximum_wait(bell_left);
}
w->cursor_visible_at_last_render = WD.screen->cursor_render_info.is_visible; w->last_cursor_x = WD.screen->cursor_render_info.x; w->last_cursor_y = WD.screen->cursor_render_info.y; w->last_cursor_shape = WD.screen->cursor_render_info.shape;
@@ -638,17 +639,17 @@ draw_resizing_text(OSWindow *w) {
}
static inline bool
no_render_frame_received_recently(OSWindow *w, double now, double max_wait) {
no_render_frame_received_recently(OSWindow *w, monotonic_t now, monotonic_t max_wait) {
bool ans = now - w->last_render_frame_received_at > max_wait;
if (ans) log_error("No render frame received in %f seconds, re-requesting at: %f", max_wait, now);
if (ans) log_error("No render frame received in %f seconds, re-requesting at: %f", monotonic_t_to_s_double(max_wait), monotonic_t_to_s_double(now));
return ans;
}
static inline void
render(double now, bool input_read) {
render(monotonic_t now, bool input_read) {
EVDBG("input_read: %d", input_read);
static double last_render_at = -DBL_MAX;
double time_since_last_render = now - last_render_at;
static monotonic_t last_render_at = MONOTONIC_T_MIN;
monotonic_t time_since_last_render = now - last_render_at;
if (!input_read && time_since_last_render < OPT(repaint_delay)) {
set_maximum_wait(OPT(repaint_delay) - time_since_last_render);
return;
@@ -662,7 +663,7 @@ render(double now, bool input_read) {
continue;
}
if (USE_RENDER_FRAMES && w->render_state != RENDER_FRAME_READY) {
if (w->render_state == RENDER_FRAME_NOT_REQUESTED || no_render_frame_received_recently(w, now, 0.25)) request_frame_render(w);
if (w->render_state == RENDER_FRAME_NOT_REQUESTED || no_render_frame_received_recently(w, now, ms_to_monotonic_t(250ll))) request_frame_render(w);
continue;
}
make_os_window_context_current(w);
@@ -806,7 +807,7 @@ add_python_timer(PyObject *self UNUSED, PyObject *args) {
double interval;
int repeats = 1;
if (!PyArg_ParseTuple(args, "Od|p", &callback, &interval, &repeats)) return NULL;
unsigned long long timer_id = add_main_loop_timer(interval, repeats ? true: false, python_timer_callback, callback, python_timer_cleanup);
unsigned long long timer_id = add_main_loop_timer(s_double_to_monotonic_t(interval), repeats ? true: false, python_timer_callback, callback, python_timer_cleanup);
Py_INCREF(callback);
return Py_BuildValue("K", timer_id);
}
@@ -821,7 +822,7 @@ remove_python_timer(PyObject *self UNUSED, PyObject *args) {
static inline void
process_pending_resizes(double now) {
process_pending_resizes(monotonic_t now) {
global_state.has_pending_resizes = false;
for (size_t i = 0; i < global_state.num_os_windows; i++) {
OSWindow *w = global_state.os_windows + i;
@@ -830,11 +831,11 @@ process_pending_resizes(double now) {
if (w->live_resize.from_os_notification) {
if (w->live_resize.os_says_resize_complete || (now - w->live_resize.last_resize_event_at) > 1) update_viewport = true;
} else {
double debounce_time = OPT(resize_debounce_time);
monotonic_t debounce_time = OPT(resize_debounce_time);
// if more than one resize event has occurred, wait at least 0.2 secs
// before repainting, to avoid rapid transitions between the cells banner
// and the normal screen
if (w->live_resize.num_of_resize_events > 1 && OPT(resize_draw_strategy) == RESIZE_DRAW_SIZE) debounce_time = MAX(0.2, debounce_time);
if (w->live_resize.num_of_resize_events > 1 && OPT(resize_draw_strategy) == RESIZE_DRAW_SIZE) debounce_time = MAX(ms_to_monotonic_t(200ll), debounce_time);
if (now - w->live_resize.last_resize_event_at >= debounce_time) update_viewport = true;
else {
global_state.has_pending_resizes = true;
@@ -916,7 +917,7 @@ process_global_state(void *data) {
maximum_wait = -1;
bool state_check_timer_enabled = false;
double now = monotonic();
monotonic_t now = monotonic();
if (global_state.has_pending_resizes) process_pending_resizes(now);
bool input_read = parse_input(self);
render(now, input_read);
@@ -1167,7 +1168,7 @@ io_loop(void *data) {
size_t i;
int ret;
bool has_more, data_received, has_pending_wakeups = false;
double last_main_loop_wakeup_at = -1, now = -1;
monotonic_t last_main_loop_wakeup_at = -1, now = -1;
Screen *screen;
ChildMonitor *self = (ChildMonitor*)data;
set_thread_name("KittyChildMon");
@@ -1188,8 +1189,8 @@ io_loop(void *data) {
}
if (has_pending_wakeups) {
now = monotonic();
double time_delta = OPT(input_delay) - (now - last_main_loop_wakeup_at);
if (time_delta >= 0) ret = poll(fds, self->count + EXTRA_FDS, (int)ceil(1000 * time_delta));
monotonic_t time_delta = OPT(input_delay) - (now - last_main_loop_wakeup_at);
if (time_delta >= 0) ret = poll(fds, self->count + EXTRA_FDS, monotonic_t_to_ms(time_delta));
else ret = 0;
} else {
ret = poll(fds, self->count + EXTRA_FDS, -1);

View File

@@ -7,6 +7,7 @@
#include "state.h"
#include "monotonic.h"
#include <Cocoa/Cocoa.h>
#include <AvailabilityMacros.h>
@@ -411,7 +412,7 @@ cocoa_get_lang(PyObject UNUSED *self) {
} // autoreleasepool
}
double
monotonic_t
cocoa_cursor_blink_interval(void) {
@autoreleasepool {
@@ -425,7 +426,7 @@ cocoa_cursor_blink_interval(void) {
} else if (period_ms) {
ans = period_ms;
}
return ans > max_value ? 0.0 : ans;
return ans > max_value ? 0ll : ms_double_to_monotonic_t(ans);
} // autoreleasepool
}

View File

@@ -22,31 +22,10 @@
#ifdef WITH_PROFILER
#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)
#include "monotonic.h"
#ifdef __APPLE__
#include <libproc.h>
#include <mach/mach_time.h>
static mach_timebase_info_data_t timebase = {0};
static inline double monotonic_(void) {
return ((double)(mach_absolute_time() * timebase.numer) / timebase.denom)/SEC_TO_NS;
}
static PyObject*
user_cache_dir() {
@@ -73,25 +52,7 @@ process_group_map() {
free(buf);
return ans;
}
#else
#include <time.h>
static inline double monotonic_(void) {
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
static double start_time = 0;
double monotonic() { return monotonic_() - start_time; }
static PyObject*
redirect_std_streams(PyObject UNUSED *self, PyObject *args) {
@@ -254,9 +215,8 @@ PyInit_fast_data_types(void) {
m = PyModule_Create(&module);
if (m == NULL) return NULL;
#ifdef __APPLE__
mach_timebase_info(&timebase);
#endif
start_time = monotonic_();
init_monotonic();
if (m != NULL) {
if (!init_logging(m)) return NULL;

View File

@@ -292,7 +292,6 @@ void apply_sgr_to_cells(GPUCell *first_cell, unsigned int cell_count, unsigned i
const char* cell_as_sgr(const GPUCell *, const GPUCell *);
const char* cursor_as_sgr(const Cursor *);
double monotonic(void);
PyObject* cm_thread_write(PyObject *self, PyObject *args);
bool schedule_write_to_child(unsigned long id, unsigned int num, ...);
bool set_iutf8(int, bool);

11
kitty/glfw-wrapper.h generated
View File

@@ -7,6 +7,7 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "../kitty/monotonic.h"
@@ -1487,11 +1488,11 @@ typedef void (*glfwStopMainLoop_func)(void);
glfwStopMainLoop_func glfwStopMainLoop_impl;
#define glfwStopMainLoop glfwStopMainLoop_impl
typedef unsigned long long (*glfwAddTimer_func)(double, bool, GLFWuserdatafun, void *, GLFWuserdatafun);
typedef unsigned long long (*glfwAddTimer_func)(monotonic_t, bool, GLFWuserdatafun, void *, GLFWuserdatafun);
glfwAddTimer_func glfwAddTimer_impl;
#define glfwAddTimer glfwAddTimer_impl
typedef void (*glfwUpdateTimer_func)(unsigned long long, double, bool);
typedef void (*glfwUpdateTimer_func)(unsigned long long, monotonic_t, bool);
glfwUpdateTimer_func glfwUpdateTimer_impl;
#define glfwUpdateTimer glfwUpdateTimer_impl
@@ -1659,7 +1660,7 @@ typedef void (*glfwGetWindowContentScale_func)(GLFWwindow*, float*, float*);
glfwGetWindowContentScale_func glfwGetWindowContentScale_impl;
#define glfwGetWindowContentScale glfwGetWindowContentScale_impl
typedef double (*glfwGetDoubleClickInterval_func)(GLFWwindow*);
typedef monotonic_t (*glfwGetDoubleClickInterval_func)(GLFWwindow*);
glfwGetDoubleClickInterval_func glfwGetDoubleClickInterval_impl;
#define glfwGetDoubleClickInterval glfwGetDoubleClickInterval_impl
@@ -1911,11 +1912,11 @@ typedef const char* (*glfwGetClipboardString_func)(GLFWwindow*);
glfwGetClipboardString_func glfwGetClipboardString_impl;
#define glfwGetClipboardString glfwGetClipboardString_impl
typedef double (*glfwGetTime_func)(void);
typedef monotonic_t (*glfwGetTime_func)(void);
glfwGetTime_func glfwGetTime_impl;
#define glfwGetTime glfwGetTime_impl
typedef void (*glfwSetTime_func)(double);
typedef void (*glfwSetTime_func)(monotonic_t);
glfwSetTime_func glfwSetTime_impl;
#define glfwSetTime glfwSetTime_impl

View File

@@ -7,6 +7,7 @@
#include "state.h"
#include "glfw_tests.h"
#include "fonts.h"
#include "monotonic.h"
#include <structmember.h>
#include "glfw-wrapper.h"
extern bool cocoa_make_window_resizable(void *w, bool);
@@ -17,7 +18,7 @@ extern void cocoa_set_activation_policy(bool);
extern void cocoa_set_titlebar_color(void *w, color_type color);
extern bool cocoa_alt_option_key_pressed(unsigned long);
extern size_t cocoa_get_workspace_ids(void *w, size_t *workspace_ids, size_t array_sz);
extern double cocoa_cursor_blink_interval(void);
extern monotonic_t cocoa_cursor_blink_interval(void);
#if GLFW_KEY_LAST >= MAX_KEY_COUNT
@@ -83,7 +84,7 @@ log_event(const char *format, ...) {
{
va_list vl;
fprintf(stderr, "[%.4f] ", glfwGetTime());
fprintf(stderr, "[%.4f] ", monotonic_t_to_s_double(glfwGetTime()));
va_start(vl, format);
vfprintf(stderr, format, vl);
va_end(vl);
@@ -248,7 +249,7 @@ cursor_enter_callback(GLFWwindow *w, int entered) {
if (!set_callback_window(w)) return;
if (entered) {
show_mouse_cursor(w);
double now = monotonic();
monotonic_t now = monotonic();
global_state.callback_os_window->last_mouse_activity_at = now;
if (is_window_ready_for_callbacks()) enter_event();
request_tick_callback();
@@ -261,7 +262,7 @@ mouse_button_callback(GLFWwindow *w, int button, int action, int mods) {
if (!set_callback_window(w)) return;
show_mouse_cursor(w);
mods_at_last_key_or_button_event = mods;
double now = monotonic();
monotonic_t now = monotonic();
global_state.callback_os_window->last_mouse_activity_at = now;
if (button >= 0 && (unsigned int)button < arraysz(global_state.callback_os_window->mouse_button_pressed)) {
global_state.callback_os_window->mouse_button_pressed[button] = action == GLFW_PRESS ? true : false;
@@ -275,7 +276,7 @@ static void
cursor_pos_callback(GLFWwindow *w, double x, double y) {
if (!set_callback_window(w)) return;
show_mouse_cursor(w);
double now = monotonic();
monotonic_t now = monotonic();
global_state.callback_os_window->last_mouse_activity_at = now;
global_state.callback_os_window->cursor_blink_zero_time = now;
global_state.callback_os_window->mouse_x = x * global_state.callback_os_window->viewport_x_ratio;
@@ -289,7 +290,7 @@ static void
scroll_callback(GLFWwindow *w, double xoffset, double yoffset, int flags) {
if (!set_callback_window(w)) return;
show_mouse_cursor(w);
double now = monotonic();
monotonic_t now = monotonic();
global_state.callback_os_window->last_mouse_activity_at = now;
if (is_window_ready_for_callbacks()) scroll_event(xoffset, yoffset, flags);
request_tick_callback();
@@ -308,7 +309,7 @@ window_focus_callback(GLFWwindow *w, int focused) {
focus_in_event();
global_state.callback_os_window->last_focused_counter = ++focus_counter;
}
double now = monotonic();
monotonic_t now = monotonic();
global_state.callback_os_window->last_mouse_activity_at = now;
global_state.callback_os_window->cursor_blink_zero_time = now;
if (is_window_ready_for_callbacks()) {
@@ -602,10 +603,10 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
#undef CC
if (OPT(click_interval) < 0) OPT(click_interval) = glfwGetDoubleClickInterval(glfw_window);
if (OPT(cursor_blink_interval) < 0) {
OPT(cursor_blink_interval) = 0.5;
OPT(cursor_blink_interval) = ms_to_monotonic_t(500ll);
#ifdef __APPLE__
double cbi = cocoa_cursor_blink_interval();
if (cbi >= 0) OPT(cursor_blink_interval) = cbi / 2000.0;
monotonic_t cbi = cocoa_cursor_blink_interval();
if (cbi >= 0) OPT(cursor_blink_interval) = cbi / 2;
#endif
}
is_first_window = false;
@@ -655,7 +656,7 @@ create_os_window(PyObject UNUSED *self, PyObject *args) {
cocoa_make_window_resizable(glfwGetCocoaWindow(glfw_window), OPT(macos_window_resizable));
} else log_error("Failed to load glfwGetCocoaWindow");
#endif
double now = monotonic();
monotonic_t now = monotonic();
w->is_focused = true;
w->cursor_blink_zero_time = now;
w->last_mouse_activity_at = now;
@@ -854,9 +855,9 @@ get_clipboard_string(PYNOARG) {
void
ring_audio_bell(OSWindow *w UNUSED) {
static double last_bell_at = -1;
double now = monotonic();
if (now - last_bell_at <= 0.1) return;
static monotonic_t last_bell_at = -1;
monotonic_t now = monotonic();
if (now - last_bell_at <= ms_to_monotonic_t(100ll)) return;
last_bell_at = now;
#ifdef __APPLE__
if (w->handle) {
@@ -1139,12 +1140,12 @@ dbus_send_notification(PyObject *self UNUSED, PyObject *args) {
#endif
id_type
add_main_loop_timer(double interval, bool repeats, timer_callback_fun callback, void *callback_data, timer_callback_fun free_callback) {
add_main_loop_timer(monotonic_t interval, bool repeats, timer_callback_fun callback, void *callback_data, timer_callback_fun free_callback) {
return glfwAddTimer(interval, repeats, callback, callback_data, free_callback);
}
void
update_main_loop_timer(id_type timer_id, double interval, bool enabled) {
update_main_loop_timer(id_type timer_id, monotonic_t interval, bool enabled) {
glfwUpdateTimer(timer_id, interval, enabled);
}

View File

@@ -121,7 +121,7 @@ trim_predicate(Image *img) {
static int
oldest_last(const void* a, const void *b) {
double ans = ((Image*)(b))->atime - ((Image*)(a))->atime;
monotonic_t ans = ((Image*)(b))->atime - ((Image*)(a))->atime;
return ans < 0 ? -1 : (ans == 0 ? 0 : 1);
}

View File

@@ -6,6 +6,7 @@
#pragma once
#include "data-types.h"
#include "monotonic.h"
typedef struct {
unsigned char action, transmission_type, compressed, delete_action;
@@ -50,7 +51,7 @@ typedef struct {
ImageRef *refs;
size_t refcnt, refcap;
double atime;
monotonic_t atime;
size_t used_storage;
} Image;

View File

@@ -6,6 +6,7 @@
*/
#include "data-types.h"
#include "monotonic.h"
#define CMD_BUF_SZ 2048
@@ -34,13 +35,13 @@ add_char(char buf[CMD_BUF_SZ], size_t *pos, char ch, PyObject *ans) {
}
static inline bool
read_response(int fd, double timeout, PyObject *ans) {
read_response(int fd, monotonic_t timeout, PyObject *ans) {
static char buf[CMD_BUF_SZ];
size_t pos = 0;
enum ReadState {START, STARTING_ESC, P, AT, K, I, T, T2, Y, HYPHEN, C, M, BODY, TRAILING_ESC};
enum ReadState state = START;
char ch;
double end_time = monotonic() + timeout;
monotonic_t end_time = monotonic() + timeout;
while(monotonic() <= end_time) {
ssize_t len = read(fd, &ch, 1);
if (len == 0) continue;
@@ -94,7 +95,7 @@ read_command_response(PyObject *self UNUSED, PyObject *args) {
int fd;
PyObject *ans;
if (!PyArg_ParseTuple(args, "idO!", &fd, &timeout, &PyList_Type, &ans)) return NULL;
if (!read_response(fd, timeout, ans)) return NULL;
if (!read_response(fd, s_double_to_monotonic_t(timeout), ans)) return NULL;
Py_RETURN_NONE;
}

84
kitty/monotonic.h Normal file
View File

@@ -0,0 +1,84 @@
/*
* monotonic.h
* Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#pragma once
#include <stdint.h>
#include <time.h>
#define MONOTONIC_T_MAX INT64_MAX
#define MONOTONIC_T_MIN INT64_MIN
typedef int64_t monotonic_t;
static inline monotonic_t calc_nano_time(struct timespec time) {
int64_t result = (monotonic_t)time.tv_sec;
result *= 1000LL;
result *= 1000LL;
result *= 1000LL;
result += (monotonic_t)time.tv_nsec;
return result;
}
static inline struct timespec calc_time(monotonic_t nsec) {
struct timespec result;
result.tv_sec = nsec / (1000LL * 1000LL * 1000LL);
result.tv_nsec = nsec % (1000LL * 1000LL * 1000LL);
return result;
}
static inline monotonic_t s_double_to_monotonic_t(double time) {
time *= 1000.0;
time *= 1000.0;
time *= 1000.0;
return (monotonic_t)time;
}
static inline monotonic_t ms_double_to_monotonic_t(double time) {
time *= 1000.0;
time *= 1000.0;
return (monotonic_t)time;
}
static inline monotonic_t s_to_monotonic_t(monotonic_t time) {
return time * 1000ll * 1000ll * 1000ll;
}
static inline monotonic_t ms_to_monotonic_t(monotonic_t time) {
return time * 1000ll * 1000ll;
}
static inline int monotonic_t_to_ms(monotonic_t time) {
return time / 1000ll / 1000ll;
}
static inline double monotonic_t_to_s_double(monotonic_t time) {
return (double)time / 1000.0 / 1000.0 / 1000.0;
}
static monotonic_t start_time = 0;
static inline monotonic_t monotonic_(void) {
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 calc_nano_time(ts);
}
static inline monotonic_t monotonic(void) {
return monotonic_() - start_time;
}
static inline void init_monotonic(void) {
start_time = monotonic_();
}

View File

@@ -13,6 +13,7 @@
#include <math.h>
#include "glfw-wrapper.h"
#include "control-codes.h"
#include "monotonic.h"
static MouseShape mouse_cursor_shape = BEAM;
typedef enum MouseActions { PRESS, RELEASE, DRAG, MOVE } MouseAction;
@@ -292,8 +293,8 @@ HANDLER(handle_move_event) {
bool handle_in_kitty = !in_tracking_mode || has_terminal_select_modifiers;
if (handle_in_kitty) {
if (screen->selection.in_progress && button == GLFW_MOUSE_BUTTON_LEFT) {
double now = monotonic();
if ((now - w->last_drag_scroll_at) >= 0.02 || mouse_cell_changed) {
monotonic_t now = monotonic();
if ((now - w->last_drag_scroll_at) >= ms_to_monotonic_t(20ll) || mouse_cell_changed) {
update_drag(false, w, false, 0);
w->last_drag_scroll_at = now;
}
@@ -338,7 +339,7 @@ distance(double x1, double y1, double x2, double y2) {
HANDLER(add_click) {
ClickQueue *q = &w->click_queue;
if (q->length == CLICK_QUEUE_SZ) { memmove(q->clicks, q->clicks + 1, sizeof(Click) * (CLICK_QUEUE_SZ - 1)); q->length--; }
double now = monotonic();
monotonic_t now = monotonic();
#define N(n) (q->clicks[q->length - n])
N(0).at = now; N(0).button = button; N(0).modifiers = modifiers; N(0).x = w->mouse_pos.x; N(0).y = w->mouse_pos.y;
q->length++;

View File

@@ -9,6 +9,7 @@
#include "screen.h"
#include "graphics.h"
#include "charsets.h"
#include "monotonic.h"
#include <time.h>
extern PyTypeObject Screen_Type;
@@ -1196,7 +1197,7 @@ end:
}
static inline void
do_parse_bytes(Screen *screen, const uint8_t *read_buf, const size_t read_buf_sz, double now, PyObject *dump_callback DUMP_UNUSED) {
do_parse_bytes(Screen *screen, const uint8_t *read_buf, const size_t read_buf_sz, monotonic_t now, PyObject *dump_callback DUMP_UNUSED) {
enum STATE {START, PARSE_PENDING, PARSE_READ_BUF, QUEUE_PENDING};
enum STATE state = START;
size_t read_buf_pos = 0;
@@ -1272,7 +1273,7 @@ FNAME(parse_bytes)(PyObject UNUSED *self, PyObject *args) {
void
FNAME(parse_worker)(Screen *screen, PyObject *dump_callback, double now) {
FNAME(parse_worker)(Screen *screen, PyObject *dump_callback, monotonic_t now) {
#ifdef DUMP_COMMANDS
if (screen->read_buf_sz) {
Py_XDECREF(PyObject_CallFunction(dump_callback, "sy#", "bytes", screen->read_buf, screen->read_buf_sz)); PyErr_Clear();

View File

@@ -1730,7 +1730,7 @@ static PyObject*
set_pending_timeout(Screen *self, PyObject *val) {
if (!PyFloat_Check(val)) { PyErr_SetString(PyExc_TypeError, "timeout must be a float"); return NULL; }
PyObject *ans = PyFloat_FromDouble(self->pending_mode.wait_time);
self->pending_mode.wait_time = PyFloat_AS_DOUBLE(val);
self->pending_mode.wait_time = s_double_to_monotonic_t(PyFloat_AS_DOUBLE(val));
return ans;
}

View File

@@ -7,6 +7,7 @@
#pragma once
#include "graphics.h"
#include "monotonic.h"
typedef enum ScrollTypes { SCROLL_LINE = -999999, SCROLL_PAGE, SCROLL_FULL } ScrollType;
@@ -86,13 +87,13 @@ typedef struct {
bool *tabstops, *main_tabstops, *alt_tabstops;
ScreenModes modes;
ColorProfile *color_profile;
double start_visual_bell_at;
monotonic_t start_visual_bell_at;
uint32_t parser_buf[PARSER_BUF_SZ];
unsigned int parser_state, parser_text_start, parser_buf_pos;
bool parser_has_pending_text;
uint8_t read_buf[READ_BUF_SZ], *write_buf;
double new_input_at;
monotonic_t new_input_at;
size_t read_buf_sz, write_buf_sz, write_buf_used;
pthread_mutex_t read_buf_lock, write_buf_lock;
@@ -101,7 +102,7 @@ typedef struct {
struct {
size_t capacity, used, stop_buf_pos;
uint8_t *buf;
double activated_at, wait_time;
monotonic_t activated_at, wait_time;
int state;
uint8_t stop_buf[32];
} pending_mode;
@@ -110,8 +111,8 @@ typedef struct {
} Screen;
void parse_worker(Screen *screen, PyObject *dump_callback, double now);
void parse_worker_dump(Screen *screen, PyObject *dump_callback, double now);
void parse_worker(Screen *screen, PyObject *dump_callback, monotonic_t now);
void parse_worker_dump(Screen *screen, PyObject *dump_callback, monotonic_t now);
void screen_align(Screen*);
void screen_restore_cursor(Screen *);
void screen_save_cursor(Screen *);

View File

@@ -300,9 +300,14 @@ color_as_int(PyObject *color) {
#undef I
}
static inline double
repaint_delay(PyObject *val) {
return (double)(PyLong_AsUnsignedLong(val)) / 1000.0;
static inline monotonic_t
parse_s_double_to_monotonic_t(PyObject *val) {
return s_double_to_monotonic_t(PyFloat_AsDouble(val));
}
static inline monotonic_t
parse_ms_long_to_monotonic_t(PyObject *val) {
return ms_to_monotonic_t(PyLong_AsUnsignedLong(val));
}
static int kitty_mod = 0;
@@ -390,11 +395,11 @@ PYWRAP1(set_options) {
#define S(name, convert) SS(name, OPT(name), convert)
SS(kitty_mod, kitty_mod, PyLong_AsLong);
S(hide_window_decorations, PyObject_IsTrue);
S(visual_bell_duration, PyFloat_AsDouble);
S(visual_bell_duration, parse_s_double_to_monotonic_t);
S(enable_audio_bell, PyObject_IsTrue);
S(focus_follows_mouse, PyObject_IsTrue);
S(cursor_blink_interval, PyFloat_AsDouble);
S(cursor_stop_blinking_after, PyFloat_AsDouble);
S(cursor_blink_interval, parse_s_double_to_monotonic_t);
S(cursor_stop_blinking_after, parse_s_double_to_monotonic_t);
S(background_opacity, PyFloat_AsDouble);
S(dim_opacity, PyFloat_AsDouble);
S(dynamic_background_opacity, PyObject_IsTrue);
@@ -404,14 +409,14 @@ PYWRAP1(set_options) {
S(cursor_shape, PyLong_AsLong);
S(url_style, PyLong_AsUnsignedLong);
S(tab_bar_edge, PyLong_AsLong);
S(mouse_hide_wait, PyFloat_AsDouble);
S(mouse_hide_wait, parse_s_double_to_monotonic_t);
S(wheel_scroll_multiplier, PyFloat_AsDouble);
S(touch_scroll_multiplier, PyFloat_AsDouble);
S(open_url_modifiers, convert_mods);
S(rectangle_select_modifiers, convert_mods);
S(terminal_select_modifiers, convert_mods);
S(click_interval, PyFloat_AsDouble);
S(resize_debounce_time, PyFloat_AsDouble);
S(click_interval, parse_s_double_to_monotonic_t);
S(resize_debounce_time, parse_s_double_to_monotonic_t);
S(url_color, color_as_int);
S(background, color_as_int);
S(foreground, color_as_int);
@@ -420,8 +425,8 @@ PYWRAP1(set_options) {
default_color = 0;
S(inactive_border_color, color_as_int);
S(bell_border_color, color_as_int);
S(repaint_delay, repaint_delay);
S(input_delay, repaint_delay);
S(repaint_delay, parse_ms_long_to_monotonic_t);
S(input_delay, parse_ms_long_to_monotonic_t);
S(sync_to_monitor, PyObject_IsTrue);
S(close_on_child_death, PyObject_IsTrue);
S(window_alert_on_bell, PyObject_IsTrue);

View File

@@ -7,6 +7,7 @@
#pragma once
#include "data-types.h"
#include "screen.h"
#include "monotonic.h"
#define OPT(name) global_state.opts.name
@@ -14,7 +15,8 @@ typedef enum { LEFT_EDGE, TOP_EDGE, RIGHT_EDGE, BOTTOM_EDGE } Edge;
typedef enum { RESIZE_DRAW_STATIC, RESIZE_DRAW_SCALED, RESIZE_DRAW_BLANK, RESIZE_DRAW_SIZE } ResizeDrawStrategy;
typedef struct {
double visual_bell_duration, cursor_blink_interval, cursor_stop_blinking_after, mouse_hide_wait, click_interval, wheel_scroll_multiplier, touch_scroll_multiplier;
monotonic_t visual_bell_duration, cursor_blink_interval, cursor_stop_blinking_after, mouse_hide_wait, click_interval;
double wheel_scroll_multiplier, touch_scroll_multiplier;
bool enable_audio_bell;
CursorShape cursor_shape;
unsigned int open_url_modifiers;
@@ -24,7 +26,7 @@ typedef struct {
unsigned int scrollback_pager_history_size;
char_type select_by_word_characters[256]; size_t select_by_word_characters_count;
color_type url_color, background, foreground, active_border_color, inactive_border_color, bell_border_color;
double repaint_delay, input_delay;
monotonic_t repaint_delay, input_delay;
bool focus_follows_mouse, hide_window_decorations;
bool macos_hide_from_tasks, macos_quit_when_last_window_closed, macos_window_resizable, macos_traditional_fullscreen;
unsigned int macos_option_as_alt;
@@ -44,7 +46,7 @@ typedef struct {
bool close_on_child_death;
bool window_alert_on_bell;
bool debug_keyboard;
double resize_debounce_time;
monotonic_t resize_debounce_time;
MouseShape pointer_shape_when_grabbed;
} Options;
@@ -59,7 +61,7 @@ typedef struct {
} WindowGeometry;
typedef struct {
double at;
monotonic_t at;
int button, modifiers;
double x, y;
} Click;
@@ -83,7 +85,7 @@ typedef struct {
} mouse_pos;
WindowGeometry geometry;
ClickQueue click_queue;
double last_drag_scroll_at;
monotonic_t last_drag_scroll_at;
} Window;
typedef struct {
@@ -114,7 +116,7 @@ typedef struct {
enum RENDER_STATE { RENDER_FRAME_NOT_REQUESTED, RENDER_FRAME_REQUESTED, RENDER_FRAME_READY };
typedef struct {
double last_resize_event_at;
monotonic_t last_resize_event_at;
bool in_progress;
bool from_os_notification;
bool os_says_resize_complete;
@@ -134,7 +136,7 @@ typedef struct {
ScreenRenderData tab_bar_render_data;
bool tab_bar_data_updated;
bool is_focused;
double cursor_blink_zero_time, last_mouse_activity_at;
monotonic_t cursor_blink_zero_time, last_mouse_activity_at;
double mouse_x, mouse_y;
double logical_dpi_x, logical_dpi_y, font_sz_in_pts;
bool mouse_button_pressed[20];
@@ -151,7 +153,7 @@ typedef struct {
id_type temp_font_group_id;
double pending_scroll_pixels;
enum RENDER_STATE render_state;
double last_render_frame_received_at;
monotonic_t last_render_frame_received_at;
id_type last_focused_counter;
ssize_t gvao_idx;
} OSWindow;
@@ -239,8 +241,8 @@ void request_frame_render(OSWindow *w);
void request_tick_callback(void);
typedef void (* timer_callback_fun)(id_type, void*);
typedef void (* tick_callback_fun)(void*);
id_type add_main_loop_timer(double interval, bool repeats, timer_callback_fun callback, void *callback_data, timer_callback_fun free_callback);
id_type add_main_loop_timer(monotonic_t interval, bool repeats, timer_callback_fun callback, void *callback_data, timer_callback_fun free_callback);
void remove_main_loop_timer(id_type timer_id);
void update_main_loop_timer(id_type timer_id, double interval, bool enabled);
void update_main_loop_timer(id_type timer_id, monotonic_t interval, bool enabled);
void run_main_loop(tick_callback_fun, void*);
void stop_main_loop(void);