Refactor linux backend event loops

Allow waiting for events on an arbitrary number of fds. Needed
for async DBUS integration.
This commit is contained in:
Kovid Goyal 2018-07-09 13:28:51 +05:30
parent 3a4b614ae0
commit e91eb27e56
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
10 changed files with 161 additions and 70 deletions

115
glfw/backend_utils.c vendored Normal file
View File

@ -0,0 +1,115 @@
/*
* backend_utils.c
* Copyright (C) 2018 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#define _GNU_SOURCE
#include "backend_utils.h"
#include <string.h>
#include <errno.h>
#ifdef __NetBSD__
#define ppoll pollts
#endif
void
update_fds(EventLoopData *eld) {
eld->fds_count = 0;
for (nfds_t i = 0; i < eld->watches_count; i++) {
Watch *w = eld->watches + i;
if (w->enabled) {
eld->fds[eld->fds_count].fd = w->fd;
eld->fds[eld->fds_count].events = w->events;
eld->fds_count++;
}
}
}
void
addWatch(EventLoopData *eld, int fd, int events, int enabled, watch_callback_func cb, void *cb_data) {
removeWatch(eld, fd);
if (eld->watches_count >= sizeof(eld->watches)/sizeof(eld->watches[0])) return;
Watch *w = eld->watches + eld->watches_count++;
w->fd = fd; w->events = events; w->enabled = enabled;
w->callback = cb;
w->callback_data = cb_data;
update_fds(eld);
}
void
removeWatch(EventLoopData *eld, int fd) {
for (nfds_t i = 0; i < eld->watches_count; i++) {
if (eld->watches[i].fd == fd) {
eld->watches_count--;
if (i < eld->watches_count) {
memmove(eld->watches + i, eld->watches + i + 1, sizeof(eld->watches[0]) * (eld->watches_count - i));
}
update_fds(eld);
break;
}
}
}
void
toggleWatch(EventLoopData *eld, int fd, int enabled) {
for (nfds_t i = 0; i < eld->watches_count; i++) {
if (eld->watches[i].fd == fd) {
if (eld->watches[i].enabled != enabled) {
eld->watches[i].enabled = enabled;
update_fds(eld);
}
break;
}
}
}
void
prepareForPoll(EventLoopData *eld) {
for (nfds_t i = 0; i < eld->fds_count; i++) eld->fds[i].revents = 0;
}
int
pollWithTimeout(struct pollfd *fds, nfds_t nfds, double timeout) {
const long seconds = (long) timeout;
const long nanoseconds = (long) ((timeout - seconds) * 1e9);
struct timespec tv = { seconds, nanoseconds };
return ppoll(fds, nfds, &tv, NULL);
}
void
dispatchEvents(EventLoopData *eld) {
for (unsigned w = 0, f = 0; f < eld->fds_count; f++) {
while(eld->watches[w].fd != eld->fds[f].fd) w++;
Watch *ww = eld->watches + w;
if (eld->fds[f].revents & ww->events) {
ww->ready = 1;
if (ww->callback) ww->callback(ww->fd, eld->fds[f].revents, ww->callback_data);
} else ww->ready = 0;
}
}
static void
drain_wakeup_fd(int fd, int events, void* data) {
static char drain_buf[64];
while(read(fd, drain_buf, sizeof(drain_buf)) < 0 && errno == EINTR);
}
void
initPollData(EventLoopData *eld, int wakeup_fd, int display_fd) {
addWatch(eld, display_fd, POLLIN, 1, NULL, NULL);
addWatch(eld, wakeup_fd, POLLIN, 1, drain_wakeup_fd, NULL);
}
void
closeFds(int *fds, size_t count) {
while(count--) {
if (*fds > 0) {
close(*fds);
*fds = -1;
}
fds++;
}
}

56
glfw/backend_utils.h vendored
View File

@ -25,42 +25,30 @@
//======================================================================== //========================================================================
#pragma once #pragma once
#include <unistd.h>
#include <poll.h> #include <poll.h>
#include <errno.h> #include <unistd.h>
#ifdef __NetBSD__ typedef void(*watch_callback_func)(int, int, void*);
#define ppoll pollts
#endif
static inline void typedef struct {
drainFd(int fd) { int fd, events, enabled, ready;
static char drain_buf[64]; watch_callback_func callback;
while(read(fd, drain_buf, sizeof(drain_buf)) < 0 && errno == EINTR); void *callback_data;
} } Watch;
typedef struct {
struct pollfd fds[32];
int wakeupFds[2];
nfds_t watches_count, fds_count;
Watch watches[32];
} EventLoopData;
static inline int void addWatch(EventLoopData *eld, int fd, int events, int enabled, watch_callback_func cb, void *cb_data);
pollWithTimeout(struct pollfd *fds, nfds_t nfds, double timeout) { void removeWatch(EventLoopData *eld, int fd);
const long seconds = (long) timeout; void toggleWatch(EventLoopData *eld, int fd, int enabled);
const long nanoseconds = (long) ((timeout - seconds) * 1e9); void prepareForPoll(EventLoopData *eld);
struct timespec tv = { seconds, nanoseconds }; int pollWithTimeout(struct pollfd *fds, nfds_t nfds, double timeout);
return ppoll(fds, nfds, &tv, NULL); void dispatchEvents(EventLoopData *eld);
} void closeFds(int *fds, size_t count);
void initPollData(EventLoopData *eld, int wakeup_fd, int display_fd);
static inline void
initPollData(struct pollfd *fds, int wakeup_fd, int display_fd) {
fds[0].fd = wakeup_fd; fds[1].fd = display_fd;
fds[0].events = POLLIN; fds[1].events = POLLIN;
}
static inline void
closeFds(int *fds, size_t count) {
while(count--) {
if (*fds > 0) {
close(*fds);
*fds = -1;
}
fds++;
}
}

View File

@ -283,7 +283,6 @@ def main():
for src in files_to_copy: for src in files_to_copy:
shutil.copy2(src, '.') shutil.copy2(src, '.')
shutil.copy2(glfw_header, '.') shutil.copy2(glfw_header, '.')
patch_in_file('internal.h', lambda x: x.replace('../include/GLFW/', ''))
patch_in_file('cocoa_window.m', lambda x: re.sub( patch_in_file('cocoa_window.m', lambda x: re.sub(
r'[(]void[)]loadMainMenu.+?}', '(void)loadMainMenu\n{ // removed by Kovid as it generated compiler warnings \n}\n', x, flags=re.DOTALL)) r'[(]void[)]loadMainMenu.+?}', '(void)loadMainMenu\n{ // removed by Kovid as it generated compiler warnings \n}\n', x, flags=re.DOTALL))
json.dump( json.dump(

View File

@ -84,6 +84,7 @@
"ibus_glfw.c", "ibus_glfw.c",
"egl_context.c", "egl_context.c",
"osmesa_context.c", "osmesa_context.c",
"backend_utils.c",
"linux_joystick.c", "linux_joystick.c",
"null_joystick.c" "null_joystick.c"
] ]
@ -139,6 +140,7 @@
"glx_context.c", "glx_context.c",
"egl_context.c", "egl_context.c",
"osmesa_context.c", "osmesa_context.c",
"backend_utils.c",
"linux_joystick.c", "linux_joystick.c",
"null_joystick.c" "null_joystick.c"
] ]

2
glfw/wl_init.c vendored
View File

@ -664,7 +664,7 @@ int _glfwPlatformInit(void)
"Wayland: Failed to connect to display"); "Wayland: Failed to connect to display");
return GLFW_FALSE; return GLFW_FALSE;
} }
initPollData(_glfw.wl.eventLoopData.fds, _glfw.wl.eventLoopData.wakeupFds[0], wl_display_get_fd(_glfw.wl.display)); initPollData(&_glfw.wl.eventLoopData, _glfw.wl.eventLoopData.wakeupFds[0], wl_display_get_fd(_glfw.wl.display));
_glfw.wl.registry = wl_display_get_registry(_glfw.wl.display); _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display);
wl_registry_add_listener(_glfw.wl.registry, &registryListener, NULL); wl_registry_add_listener(_glfw.wl.registry, &registryListener, NULL);

6
glfw/wl_platform.h vendored
View File

@ -49,6 +49,7 @@ typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR
#else #else
#include "null_joystick.h" #include "null_joystick.h"
#endif #endif
#include "backend_utils.h"
#include "xkb_glfw.h" #include "xkb_glfw.h"
#include "egl_context.h" #include "egl_context.h"
#include "osmesa_context.h" #include "osmesa_context.h"
@ -230,10 +231,7 @@ typedef struct _GLFWlibraryWayland
PFN_wl_egl_window_resize window_resize; PFN_wl_egl_window_resize window_resize;
} egl; } egl;
struct { EventLoopData eventLoopData;
struct pollfd fds[2];
int wakeupFds[2];
} eventLoopData;
} _GLFWlibraryWayland; } _GLFWlibraryWayland;

17
glfw/wl_window.c vendored
View File

@ -735,19 +735,18 @@ handleEvents(double timeout)
dispatchPendingKeyRepeats(); dispatchPendingKeyRepeats();
timeout = adjustTimeoutForKeyRepeat(timeout); timeout = adjustTimeoutForKeyRepeat(timeout);
GLFWbool read_ok = GLFW_FALSE; GLFWbool read_ok = GLFW_FALSE;
for (nfds_t i = 0; i < 2; i++) _glfw.wl.eventLoopData.fds[i].revents = 0; prepareForPoll(&_glfw.wl.eventLoopData);
if (timeout >= 0) { if (timeout >= 0) {
const int result = pollWithTimeout(_glfw.wl.eventLoopData.fds, 2, timeout); const int result = pollWithTimeout(_glfw.wl.eventLoopData.fds, _glfw.wl.eventLoopData.fds_count, timeout);
if (result > 0) if (result > 0) {
{ dispatchEvents(&_glfw.wl.eventLoopData);
if (_glfw.wl.eventLoopData.fds[0].revents & POLLIN) drainFd(_glfw.wl.eventLoopData.fds[0].fd); read_ok = _glfw.wl.eventLoopData.watches[0].ready;
read_ok = _glfw.wl.eventLoopData.fds[1].revents & POLLIN;
} }
} else { } else {
if (poll(_glfw.wl.eventLoopData.fds, 2, -1) > 0) { if (poll(_glfw.wl.eventLoopData.fds, _glfw.wl.eventLoopData.fds_count, -1) > 0) {
if (_glfw.wl.eventLoopData.fds[0].revents & POLLIN) drainFd(_glfw.wl.eventLoopData.fds[0].fd); dispatchEvents(&_glfw.wl.eventLoopData);
read_ok = _glfw.wl.eventLoopData.fds[1].revents & POLLIN; read_ok = _glfw.wl.eventLoopData.watches[0].ready;
} }
} }
if (read_ok) { if (read_ok) {

5
glfw/x11_init.c vendored
View File

@ -640,8 +640,7 @@ int _glfwPlatformInit(void)
return GLFW_FALSE; return GLFW_FALSE;
} }
initPollData(_glfw.x11.eventLoopData.fds, _glfw.x11.eventLoopData.wakeupFds[0], ConnectionNumber(_glfw.x11.display)); initPollData(&_glfw.x11.eventLoopData, _glfw.x11.eventLoopData.wakeupFds[0], ConnectionNumber(_glfw.x11.display));
_glfw.x11.eventLoopData.fds[2].events = POLLIN;
_glfw.x11.screen = DefaultScreen(_glfw.x11.display); _glfw.x11.screen = DefaultScreen(_glfw.x11.display);
_glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen);
@ -660,6 +659,8 @@ int _glfwPlatformInit(void)
#if defined(__linux__) #if defined(__linux__)
if (!_glfwInitJoysticksLinux()) if (!_glfwInitJoysticksLinux())
return GLFW_FALSE; return GLFW_FALSE;
if (_glfw.linjs.inotify > 0)
addWatch(&_glfw.x11.eventLoopData, _glfw.linjs.inotify, POLLIN, 1, NULL, NULL);
#endif #endif
_glfwInitTimerPOSIX(); _glfwInitTimerPOSIX();

6
glfw/x11_platform.h vendored
View File

@ -53,6 +53,7 @@
// The libxkb library is used for improved keyboard support // The libxkb library is used for improved keyboard support
#include "xkb_glfw.h" #include "xkb_glfw.h"
#include "backend_utils.h"
typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int); typedef XRRCrtcGamma* (* PFN_XRRAllocGamma)(int);
typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*); typedef void (* PFN_XRRFreeCrtcInfo)(XRRCrtcInfo*);
@ -383,10 +384,7 @@ typedef struct _GLFWlibraryX11
PFN_XRenderFindVisualFormat FindVisualFormat; PFN_XRenderFindVisualFormat FindVisualFormat;
} xrender; } xrender;
struct { EventLoopData eventLoopData;
struct pollfd fds[3];
int wakeupFds[2];
} eventLoopData;
} _GLFWlibraryX11; } _GLFWlibraryX11;

21
glfw/x11_window.c vendored
View File

@ -56,18 +56,11 @@
// //
static GLFWbool waitForEvent(double* timeout) static GLFWbool waitForEvent(double* timeout)
{ {
nfds_t count = 2; nfds_t count = _glfw.x11.eventLoopData.fds_count;
#if defined(__linux__)
if (_glfw.linjs.inotify > 0)
{
count = 3;
_glfw.x11.eventLoopData.fds[2].fd = _glfw.linjs.inotify;
}
#endif
for (;;) for (;;)
{ {
for (nfds_t i = 0; i < count; i++) _glfw.x11.eventLoopData.fds[i].revents = 0; prepareForPoll(&_glfw.x11.eventLoopData);
if (timeout) if (timeout)
{ {
const uint64_t base = _glfwPlatformGetTimerValue(); const uint64_t base = _glfwPlatformGetTimerValue();
@ -75,9 +68,8 @@ static GLFWbool waitForEvent(double* timeout)
*timeout -= (_glfwPlatformGetTimerValue() - base) / *timeout -= (_glfwPlatformGetTimerValue() - base) /
(double) _glfwPlatformGetTimerFrequency(); (double) _glfwPlatformGetTimerFrequency();
if (result > 0) if (result > 0) {
{ dispatchEvents(&_glfw.x11.eventLoopData);
if (_glfw.x11.eventLoopData.fds[0].revents & POLLIN) drainFd(_glfw.x11.eventLoopData.fds[0].fd);
return GLFW_TRUE; return GLFW_TRUE;
} }
if (result == 0) if (result == 0)
@ -87,9 +79,8 @@ static GLFWbool waitForEvent(double* timeout)
} }
else { else {
const int result = poll(_glfw.x11.eventLoopData.fds, count, -1); const int result = poll(_glfw.x11.eventLoopData.fds, count, -1);
if (result > 0) if (result > 0) {
{ dispatchEvents(&_glfw.x11.eventLoopData);
if (_glfw.x11.eventLoopData.fds[0].revents & POLLIN) drainFd(_glfw.x11.eventLoopData.fds[0].fd);
return GLFW_TRUE; return GLFW_TRUE;
} }
if (result == 0) if (result == 0)