diff --git a/glfw/x11_init.c b/glfw/x11_init.c index 89fcb7d4c..78f0021e2 100644 --- a/glfw/x11_init.c +++ b/glfw/x11_init.c @@ -34,6 +34,7 @@ #include #include #include +#include // Check whether the specified atom is supported @@ -589,6 +590,23 @@ Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) return cursor; } +static inline GLFWbool +selfPipe(int fds[2]) { + int flags; + flags = pipe(fds); + if (flags != 0) return GLFW_FALSE; + for (int i = 0; i < 2; i++) { + flags = fcntl(fds[i], F_GETFD); + if (flags == -1) { return GLFW_FALSE; } + if (fcntl(fds[i], F_SETFD, flags | FD_CLOEXEC) == -1) { return GLFW_FALSE; } + flags = fcntl(fds[i], F_GETFL); + if (flags == -1) { return GLFW_FALSE; } + if (fcntl(fds[i], F_SETFL, flags | O_NONBLOCK) == -1) { return GLFW_FALSE; } + } + return GLFW_TRUE; +} + + ////////////////////////////////////////////////////////////////////////// ////// GLFW platform API ////// @@ -599,6 +617,13 @@ int _glfwPlatformInit(void) XInitThreads(); XrmInitialize(); + if (!selfPipe(_glfw.x11.eventLoopData.wakeupFds)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: failed to create self pipe"); + return GLFW_FALSE; + } + _glfw.x11.display = XOpenDisplay(NULL); if (!_glfw.x11.display) { @@ -617,6 +642,12 @@ int _glfwPlatformInit(void) return GLFW_FALSE; } + _glfw.x11.eventLoopData.fds[0].fd = _glfw.x11.eventLoopData.wakeupFds[0]; + _glfw.x11.eventLoopData.fds[1].fd = ConnectionNumber(_glfw.x11.display); + _glfw.x11.eventLoopData.fds[0].events = POLLIN; + _glfw.x11.eventLoopData.fds[1].events = POLLIN; + _glfw.x11.eventLoopData.fds[2].events = POLLIN; + _glfw.x11.screen = DefaultScreen(_glfw.x11.display); _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); _glfw.x11.context = XUniqueContext(); @@ -668,6 +699,17 @@ void _glfwPlatformTerminate(void) { XCloseDisplay(_glfw.x11.display); _glfw.x11.display = NULL; + if (_glfw.x11.eventLoopData.wakeupFds[0] > 0) + { + close(_glfw.x11.eventLoopData.wakeupFds[0]); + _glfw.x11.eventLoopData.wakeupFds[0] = -1; + } + if (_glfw.x11.eventLoopData.wakeupFds[1] > 0) + { + _glfw.x11.eventLoopData.wakeupFds[1] = -1; + close(_glfw.x11.eventLoopData.wakeupFds[1]); + } + _glfw.x11.eventLoopData.fds[0].fd = -1; } if (_glfw.x11.xcursor.handle) diff --git a/glfw/x11_platform.h b/glfw/x11_platform.h index 05ac35dcf..bb4ee1105 100644 --- a/glfw/x11_platform.h +++ b/glfw/x11_platform.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -378,6 +379,11 @@ typedef struct _GLFWlibraryX11 PFN_XRenderFindVisualFormat FindVisualFormat; } xrender; + struct { + struct pollfd fds[3]; + int wakeupFds[2]; + } eventLoopData; + } _GLFWlibraryX11; // X11-specific per-monitor data diff --git a/glfw/x11_window.c b/glfw/x11_window.c index 39ea13577..6db24266d 100644 --- a/glfw/x11_window.c +++ b/glfw/x11_window.c @@ -30,8 +30,6 @@ #include #include -#include - #include #include #include @@ -50,50 +48,67 @@ #define _GLFW_XDND_VERSION 5 +static inline void +drainFd(int fd) { + static char drain_buf[64]; + while(1) { + ssize_t len = read(fd, drain_buf, sizeof(drain_buf)); + if (len < 0) { + if (errno == EINTR) continue; + break; + } + break; + } +} -// Wait for data to arrive using select + +// Wait for data to arrive using poll // This avoids blocking other threads via the per-display Xlib lock that also // covers GLX functions // static GLFWbool waitForEvent(double* timeout) { - fd_set fds; - const int fd = ConnectionNumber(_glfw.x11.display); - int count = fd + 1; + nfds_t count = 2; #if defined(__linux__) - if (_glfw.linjs.inotify > fd) - count = _glfw.linjs.inotify + 1; + if (_glfw.linjs.inotify > 0) + { + count = 3; + _glfw.x11.eventLoopData.fds[2].fd = _glfw.linjs.inotify; + } #endif for (;;) { - FD_ZERO(&fds); - FD_SET(fd, &fds); -#if defined(__linux__) - if (_glfw.linjs.inotify > 0) - FD_SET(_glfw.linjs.inotify, &fds); -#endif - + for (nfds_t i = 0; i < count; i++) _glfw.x11.eventLoopData.fds[i].revents = 0; if (timeout) { - const long seconds = (long) *timeout; - const long microseconds = (long) ((*timeout - seconds) * 1e6); - struct timeval tv = { seconds, microseconds }; + const int milliseconds = (int) ((*timeout) * 1000); const uint64_t base = _glfwPlatformGetTimerValue(); - - const int result = select(count, &fds, NULL, NULL, &tv); - const int error = errno; - + const int result = poll(_glfw.x11.eventLoopData.fds, count, milliseconds); *timeout -= (_glfwPlatformGetTimerValue() - base) / (double) _glfwPlatformGetTimerFrequency(); if (result > 0) + { + if (_glfw.x11.eventLoopData.fds[0].revents && POLLIN) drainFd(_glfw.x11.eventLoopData.fds[0].fd); return GLFW_TRUE; - if ((result == -1 && error == EINTR) || *timeout <= 0.0) + } + if (result == 0) return GLFW_FALSE; + if (*timeout > 0 && (errno == EINTR || errno == EAGAIN)) continue; + return GLFW_FALSE; + } + else { + const int result = poll(_glfw.x11.eventLoopData.fds, count, -1); + if (result > 0) + { + if (_glfw.x11.eventLoopData.fds[0].revents && POLLIN) drainFd(_glfw.x11.eventLoopData.fds[0].fd); + return GLFW_TRUE; + } + if (result == 0) + return GLFW_FALSE; + if (errno != EINTR && errno != EAGAIN) return GLFW_FALSE; } - else if (select(count, &fds, NULL, NULL, NULL) != -1 || errno != EINTR) - return GLFW_TRUE; } } @@ -2597,6 +2612,11 @@ void _glfwPlatformPostEmptyEvent(void) XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event); XFlush(_glfw.x11.display); + while(1) + { + if (write(_glfw.x11.eventLoopData.wakeupFds[1], "w", 1) >= 0 || errno != EINTR) + break; + } } void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos)