From da507dfd19d184081b1f75170fe6354b55bd9695 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 25 Feb 2019 13:58:45 +0530 Subject: [PATCH] Infrastructure for moving the run loop into GLFW This is needed on Cocoa, where Apple expects to be in control of the run loop. --- glfw/backend_utils.c | 23 +++++++++++++++++++++-- glfw/backend_utils.h | 8 +++++++- glfw/dbus_glfw.c | 2 +- glfw/glfw3.h | 6 ++++++ glfw/init.c | 20 ++++++++++++++++++++ glfw/internal.h | 4 ++++ glfw/wl_init.c | 4 ++-- glfw/x11_init.c | 25 +++++++++++++++++++++++++ kitty/glfw-wrapper.c | 12 ++++++++++++ kitty/glfw-wrapper.h | 18 ++++++++++++++++++ kitty/glfw.c | 10 ++++++++++ kitty/state.h | 3 +++ 12 files changed, 129 insertions(+), 6 deletions(-) diff --git a/glfw/backend_utils.c b/glfw/backend_utils.c index 4c34c62fb..43f1e281e 100644 --- a/glfw/backend_utils.c +++ b/glfw/backend_utils.c @@ -55,6 +55,7 @@ addWatch(EventLoopData *eld, const char* name, int fd, int events, int enabled, w->fd = fd; w->events = events; w->enabled = enabled; w->callback = cb; w->callback_data = cb_data; + w->free = NULL; w->id = ++watch_counter; update_fds(eld); return w->id; @@ -65,6 +66,10 @@ addWatch(EventLoopData *eld, const char* name, int fd, int events, int enabled, if (eld->which[i].id == item_id) { \ eld->which##_count--; \ if (i < eld->which##_count) { \ + if (eld->which[i].callback_data && eld->which[i].free) { \ + eld->which[i].free(eld->which[i].id, eld->which[i].callback_data); \ + eld->which[i].callback_data = NULL; eld->which[i].free = NULL; \ + } \ memmove(eld->which + i, eld->which + i + 1, sizeof(eld->which[0]) * (eld->which##_count - i)); \ } \ update_func(eld); break; \ @@ -102,7 +107,7 @@ update_timers(EventLoopData *eld) { } id_type -addTimer(EventLoopData *eld, const char *name, double interval, int enabled, timer_callback_func cb, void *cb_data) { +addTimer(EventLoopData *eld, const char *name, double interval, int enabled, bool repeats, timer_callback_func cb, void *cb_data, GLFWuserdatafreefun free) { if (eld->timers_count >= sizeof(eld->timers)/sizeof(eld->timers[0])) { _glfwInputError(GLFW_PLATFORM_ERROR, "Too many timers added"); return 0; @@ -111,8 +116,10 @@ addTimer(EventLoopData *eld, const char *name, double interval, int enabled, tim t->interval = interval; t->name = name; t->trigger_at = enabled ? monotonic() + interval : DBL_MAX; + t->repeats = repeats; t->callback = cb; t->callback_data = cb_data; + t->free = free; t->id = ++timer_counter; update_timers(eld); return t->id; @@ -123,6 +130,14 @@ removeTimer(EventLoopData *eld, id_type timer_id) { removeX(timers, timer_id, update_timers); } +void +removeAllTimers(EventLoopData *eld) { + for (nfds_t i = 0; i < eld->timers_count; i++) { + if (eld->timers[i].free && eld->timers[i].callback_data) eld->timers[i].free(eld->timers[i].id, eld->timers[i].callback_data); + } + eld->timers_count = 0; +} + void toggleTimer(EventLoopData *eld, id_type timer_id, int enabled) { for (nfds_t i = 0; i < eld->timers_count; i++) { @@ -182,7 +197,7 @@ dispatchEvents(EventLoopData *eld) { unsigned dispatchTimers(EventLoopData *eld) { if (!eld->timers_count || eld->timers[0].trigger_at == DBL_MAX) return 0; - static struct { timer_callback_func func; id_type id; void* data; } dispatches[sizeof(eld->timers)/sizeof(eld->timers[0])]; + static struct { timer_callback_func func; id_type id; void* data; bool repeats; } dispatches[sizeof(eld->timers)/sizeof(eld->timers[0])]; unsigned num_dispatches = 0; double now = monotonic(); for (nfds_t i = 0; i < eld->timers_count && eld->timers[i].trigger_at <= now; i++) { @@ -190,11 +205,15 @@ dispatchTimers(EventLoopData *eld) { dispatches[num_dispatches].func = eld->timers[i].callback; dispatches[num_dispatches].id = eld->timers[i].id; dispatches[num_dispatches].data = eld->timers[i].callback_data; + dispatches[num_dispatches].repeats = eld->timers[i].repeats; num_dispatches++; } // we dispatch separately so that the callbacks can modify timers for (unsigned i = 0; i < num_dispatches; i++) { dispatches[i].func(dispatches[i].id, dispatches[i].data); + if (!dispatches[i].repeats) { + removeTimer(eld, dispatches[i].id); + } } if (num_dispatches) update_timers(eld); return num_dispatches; diff --git a/glfw/backend_utils.h b/glfw/backend_utils.h index 46bab4f89..dc3e0e6ee 100644 --- a/glfw/backend_utils.h +++ b/glfw/backend_utils.h @@ -27,15 +27,18 @@ #pragma once #include #include +#include typedef unsigned long long id_type; typedef void(*watch_callback_func)(int, int, void*); typedef void(*timer_callback_func)(id_type, void*); +typedef void (* GLFWuserdatafreefun)(id_type, void*); typedef struct { int fd, events, enabled, ready; watch_callback_func callback; void *callback_data; + GLFWuserdatafreefun free; id_type id; const char *name; } Watch; @@ -45,7 +48,9 @@ typedef struct { double interval, trigger_at; timer_callback_func callback; void *callback_data; + GLFWuserdatafreefun free; const char *name; + bool repeats; } Timer; @@ -61,8 +66,9 @@ typedef struct { id_type addWatch(EventLoopData *eld, const char *name, int fd, int events, int enabled, watch_callback_func cb, void *cb_data); void removeWatch(EventLoopData *eld, id_type watch_id); void toggleWatch(EventLoopData *eld, id_type watch_id, int enabled); -id_type addTimer(EventLoopData *eld, const char *name, double interval, int enabled, timer_callback_func cb, void *cb_data); +id_type addTimer(EventLoopData *eld, const char *name, double interval, int enabled, bool repeats, timer_callback_func cb, void *cb_data, GLFWuserdatafreefun free); void removeTimer(EventLoopData *eld, id_type timer_id); +void removeAllTimers(EventLoopData *eld); void toggleTimer(EventLoopData *eld, id_type timer_id, int enabled); void changeTimerInterval(EventLoopData *eld, id_type timer_id, double interval); double prepareForPoll(EventLoopData *eld, double timeout); diff --git a/glfw/dbus_glfw.c b/glfw/dbus_glfw.c index 499ec1a63..f73278f7a 100644 --- a/glfw/dbus_glfw.c +++ b/glfw/dbus_glfw.c @@ -109,7 +109,7 @@ add_dbus_timeout(DBusTimeout *timeout, void *data) { int enabled = dbus_timeout_get_enabled(timeout) ? 1 : 0; double interval = ((double)dbus_timeout_get_interval(timeout)) / 1000.0; if (interval < 0) return FALSE; - id_type timer_id = addTimer(dbus_data->eld, data, interval, enabled, on_dbus_timer_ready, timeout); + id_type timer_id = addTimer(dbus_data->eld, data, interval, enabled, true, on_dbus_timer_ready, timeout, NULL); if (!timer_id) return FALSE; id_type *idp = malloc(sizeof(id_type)); if (!idp) { diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 456e6497a..1ee5394b5 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1509,6 +1509,8 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); */ typedef void (* GLFWjoystickfun)(int,int); +typedef void (* GLFWuserdatafun)(unsigned long long, void*); + /*! @brief Video mode type. * * This describes a single video mode. @@ -1655,6 +1657,10 @@ typedef struct GLFWgamepadstate * @ingroup init */ GLFWAPI int glfwInit(void); +GLFWAPI void glfwRunMainLoop(void); +GLFWAPI void glfwStopMainLoop(void); +GLFWAPI unsigned long long glfwAddTimer(double interval, bool repeats, GLFWuserdatafun callback, void * callback_data, GLFWuserdatafun free_callback); +GLFWAPI void glfwRemoveTimer(unsigned long long); /*! @brief Terminates the GLFW library. * diff --git a/glfw/init.c b/glfw/init.c index bbbe18ad0..3f554c6cd 100644 --- a/glfw/init.c +++ b/glfw/init.c @@ -320,3 +320,23 @@ GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) _GLFW_SWAP_POINTERS(_glfwErrorCallback, cbfun); return cbfun; } + + +GLFWAPI void glfwRunMainLoop(void) +{ + _GLFW_REQUIRE_INIT(); + _glfwPlatformRunMainLoop(); +} + +GLFWAPI void glfwStopMainLoop(void) { + _GLFW_REQUIRE_INIT(); + _glfwPlatformStopMainLoop(); +} + +GLFWAPI unsigned long long glfwAddTimer(double interval, bool repeats, GLFWuserdatafun callback, void *callback_data, GLFWuserdatafun free_callback) { + return _glfwPlatformAddTimer(interval, repeats, callback, callback_data, free_callback); +} + +GLFWAPI void glfwRemoveTimer(unsigned long long timer_id) { + _glfwPlatformRemoveTimer(timer_id); +} diff --git a/glfw/internal.h b/glfw/internal.h index 6e118133b..333f52643 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -783,5 +783,9 @@ void _glfwTerminateVulkan(void); const char* _glfwGetVulkanResultString(VkResult result); _GLFWwindow* _glfwFocusedWindow(); _GLFWwindow* _glfwWindowForId(GLFWid id); +void _glfwPlatformRunMainLoop(void); +void _glfwPlatformStopMainLoop(void); +unsigned long long _glfwPlatformAddTimer(double interval, bool repeats, GLFWuserdatafreefun callback, void *callback_data, GLFWuserdatafreefun free_callback); +void _glfwPlatformRemoveTimer(unsigned long long timer_id); char* _glfw_strdup(const char* source); diff --git a/glfw/wl_init.c b/glfw/wl_init.c index 5a3571d2d..1e19f10bd 100644 --- a/glfw/wl_init.c +++ b/glfw/wl_init.c @@ -711,8 +711,8 @@ int _glfwPlatformInit(void) } initPollData(&_glfw.wl.eventLoopData, _glfw.wl.eventLoopData.wakeupFds[0], wl_display_get_fd(_glfw.wl.display)); glfw_dbus_init(&_glfw.wl.dbus, &_glfw.wl.eventLoopData); - _glfw.wl.keyRepeatInfo.keyRepeatTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-key-repeat", 0.5, 0, dispatchPendingKeyRepeats, NULL); - _glfw.wl.cursorAnimationTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-cursor-animation", 0.5, 0, animateCursorImage, NULL); + _glfw.wl.keyRepeatInfo.keyRepeatTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-key-repeat", 0.5, 0, true, dispatchPendingKeyRepeats, NULL, NULL); + _glfw.wl.cursorAnimationTimer = addTimer(&_glfw.wl.eventLoopData, "wayland-cursor-animation", 0.5, 0, true, animateCursorImage, NULL, NULL); _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display); wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL); diff --git a/glfw/x11_init.c b/glfw/x11_init.c index d64ab4acb..c793176c0 100644 --- a/glfw/x11_init.c +++ b/glfw/x11_init.c @@ -675,6 +675,7 @@ int _glfwPlatformInit(void) void _glfwPlatformTerminate(void) { + removeAllTimers(&_glfw.x11.eventLoopData); if (_glfw.x11.helperWindowHandle) { if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) == @@ -768,3 +769,27 @@ const char* _glfwPlatformGetVersionString(void) #endif ; } + +static GLFWbool keep_going; + +void _glfwPlatformStopMainLoop(void) { + if (keep_going) { + keep_going = GLFW_FALSE; + _glfwPlatformPostEmptyEvent(); + } +} + +void _glfwPlatformRunMainLoop(void) { + keep_going = GLFW_TRUE; + while(keep_going) { + _glfwPlatformWaitEvents(); + } +} + +unsigned long long _glfwPlatformAddTimer(double interval, bool repeats, GLFWuserdatafreefun callback, void *callback_data, GLFWuserdatafreefun free_callback) { + return addTimer(&_glfw.x11.eventLoopData, "user timer", interval, 1, repeats, callback, callback_data, free_callback); +} + +void _glfwRemoveTimer(unsigned long long timer_id) { + removeTimer(&_glfw.x11.eventLoopData, timer_id); +} diff --git a/kitty/glfw-wrapper.c b/kitty/glfw-wrapper.c index 6fd8f804b..733d734a9 100644 --- a/kitty/glfw-wrapper.c +++ b/kitty/glfw-wrapper.c @@ -17,6 +17,18 @@ load_glfw(const char* path) { *(void **) (&glfwInit_impl) = dlsym(handle, "glfwInit"); if (glfwInit_impl == NULL) fail("Failed to load glfw function glfwInit with error: %s", dlerror()); + *(void **) (&glfwRunMainLoop_impl) = dlsym(handle, "glfwRunMainLoop"); + if (glfwRunMainLoop_impl == NULL) fail("Failed to load glfw function glfwRunMainLoop with error: %s", dlerror()); + + *(void **) (&glfwStopMainLoop_impl) = dlsym(handle, "glfwStopMainLoop"); + if (glfwStopMainLoop_impl == NULL) fail("Failed to load glfw function glfwStopMainLoop with error: %s", dlerror()); + + *(void **) (&glfwAddTimer_impl) = dlsym(handle, "glfwAddTimer"); + if (glfwAddTimer_impl == NULL) fail("Failed to load glfw function glfwAddTimer with error: %s", dlerror()); + + *(void **) (&glfwRemoveTimer_impl) = dlsym(handle, "glfwRemoveTimer"); + if (glfwRemoveTimer_impl == NULL) fail("Failed to load glfw function glfwRemoveTimer with error: %s", dlerror()); + *(void **) (&glfwTerminate_impl) = dlsym(handle, "glfwTerminate"); if (glfwTerminate_impl == NULL) fail("Failed to load glfw function glfwTerminate with error: %s", dlerror()); diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index eec79140c..5d808264b 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -1266,6 +1266,8 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); */ typedef void (* GLFWjoystickfun)(int,int); +typedef void (* GLFWuserdatafun)(unsigned long long, void*); + /*! @brief Video mode type. * * This describes a single video mode. @@ -1424,6 +1426,22 @@ typedef int (*glfwInit_func)(); glfwInit_func glfwInit_impl; #define glfwInit glfwInit_impl +typedef void (*glfwRunMainLoop_func)(); +glfwRunMainLoop_func glfwRunMainLoop_impl; +#define glfwRunMainLoop glfwRunMainLoop_impl + +typedef void (*glfwStopMainLoop_func)(); +glfwStopMainLoop_func glfwStopMainLoop_impl; +#define glfwStopMainLoop glfwStopMainLoop_impl + +typedef unsigned long long (*glfwAddTimer_func)(double, bool, GLFWuserdatafun, void *, GLFWuserdatafun); +glfwAddTimer_func glfwAddTimer_impl; +#define glfwAddTimer glfwAddTimer_impl + +typedef void (*glfwRemoveTimer_func)(unsigned long); +glfwRemoveTimer_func glfwRemoveTimer_impl; +#define glfwRemoveTimer glfwRemoveTimer_impl + typedef void (*glfwTerminate_func)(); glfwTerminate_func glfwTerminate_impl; #define glfwTerminate glfwTerminate_impl diff --git a/kitty/glfw.c b/kitty/glfw.c index fd75dd0f4..1dda8a455 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -1113,6 +1113,16 @@ 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) { + return glfwAddTimer(interval, repeats, callback, callback_data, free_callback); +} + +void +remove_main_loop_timer(id_type timer_id) { + glfwRemoveTimer(timer_id); +} + // Boilerplate {{{ static PyMethodDef module_methods[] = { diff --git a/kitty/state.h b/kitty/state.h index 5b54ce004..488a37f71 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -221,3 +221,6 @@ bool application_quit_requested(); void request_application_quit(); #endif void request_frame_render(OSWindow *w); +typedef void (* timer_callback_fun)(id_type, void*); +id_type add_main_loop_timer(double interval, bool repeats, timer_callback_fun callback, void *callback_data, timer_callback_fun free_callback); +void remove_main_loop_timer(id_type timer_id);