Infrastructure for moving the run loop into GLFW

This is needed on Cocoa, where Apple expects to be in control of the run
loop.
This commit is contained in:
Kovid Goyal 2019-02-25 13:58:45 +05:30
parent 13254ac4d5
commit da507dfd19
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
12 changed files with 129 additions and 6 deletions

23
glfw/backend_utils.c vendored
View file

@ -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;

View file

@ -27,15 +27,18 @@
#pragma once
#include <poll.h>
#include <unistd.h>
#include <stdbool.h>
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);

2
glfw/dbus_glfw.c vendored
View file

@ -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) {

6
glfw/glfw3.h vendored
View file

@ -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.
*

20
glfw/init.c vendored
View file

@ -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);
}

4
glfw/internal.h vendored
View file

@ -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);

4
glfw/wl_init.c vendored
View file

@ -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, &registryListener, NULL);

25
glfw/x11_init.c vendored
View file

@ -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);
}

12
kitty/glfw-wrapper.c generated
View file

@ -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());

18
kitty/glfw-wrapper.h generated
View file

@ -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

View file

@ -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[] = {

View file

@ -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);