X11: Clean up event wait timeout logic

Simplify interface and implement timeout updating.
This commit is contained in:
Camilla Berglund 2016-08-10 00:33:07 +02:00
parent f88a609000
commit ad9233e620

View File

@ -53,13 +53,11 @@
// This avoids blocking other threads via the per-display Xlib lock that also // This avoids blocking other threads via the per-display Xlib lock that also
// covers GLX functions // covers GLX functions
// //
void selectDisplayConnection(struct timeval* timeout) static GLFWbool waitForEvent(double* timeout)
{ {
fd_set fds; fd_set fds;
int result, count;
const int fd = ConnectionNumber(_glfw.x11.display); const int fd = ConnectionNumber(_glfw.x11.display);
int count = fd + 1;
count = fd + 1;
FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(fd, &fds); FD_SET(fd, &fds);
@ -69,15 +67,29 @@ void selectDisplayConnection(struct timeval* timeout)
if (fd < _glfw.linux_js.inotify) if (fd < _glfw.linux_js.inotify)
count = _glfw.linux_js.inotify + 1; count = _glfw.linux_js.inotify + 1;
#endif #endif
for (;;)
// NOTE: Only retry on EINTR if there is no timeout, as select is not
// required to update it for the time elapsed
// TODO: Update timeout value manually
do
{ {
result = select(count, &fds, NULL, NULL, timeout); if (timeout)
{
const long seconds = (long) *timeout;
const long microseconds = (long) ((*timeout - seconds) * 1e6);
struct timeval tv = { seconds, microseconds };
const uint64_t base = _glfwPlatformGetTimerValue();
const int result = select(count, &fds, NULL, NULL, &tv);
const int error = errno;
*timeout -= (_glfwPlatformGetTimerValue() - base) /
(double) _glfwPlatformGetTimerFrequency();
if (result > 0)
return GLFW_TRUE;
if ((result == -1 && error == EINTR) || *timeout <= 0.0)
return GLFW_FALSE;
}
else if (select(count, &fds, NULL, NULL, NULL) != -1 || errno != EINTR)
return GLFW_TRUE;
} }
while (result == -1 && errno == EINTR && timeout == NULL);
} }
// Returns whether the window is iconified // Returns whether the window is iconified
@ -818,7 +830,7 @@ static void pushSelectionToManager(_GLFWwindow* window)
} }
} }
selectDisplayConnection(NULL); waitForEvent(NULL);
} }
} }
@ -1793,16 +1805,14 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
if (!_glfwPlatformWindowVisible(window) && if (!_glfwPlatformWindowVisible(window) &&
_glfw.x11.NET_REQUEST_FRAME_EXTENTS) _glfw.x11.NET_REQUEST_FRAME_EXTENTS)
{ {
uint64_t base;
XEvent event; XEvent event;
double timeout = 0.5;
// Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to
// function before the window is mapped // function before the window is mapped
sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS, sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS,
0, 0, 0, 0, 0); 0, 0, 0, 0, 0);
base = _glfwPlatformGetTimerValue();
// HACK: Use a timeout because earlier versions of some window managers // HACK: Use a timeout because earlier versions of some window managers
// (at least Unity, Fluxbox and Xfwm) failed to send the reply // (at least Unity, Fluxbox and Xfwm) failed to send the reply
// They have been fixed but broken versions are still in the wild // They have been fixed but broken versions are still in the wild
@ -1813,21 +1823,12 @@ void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window,
isFrameExtentsEvent, isFrameExtentsEvent,
(XPointer) window)) (XPointer) window))
{ {
double remaining; if (!waitForEvent(&timeout))
struct timeval timeout;
remaining = 0.5 - (_glfwPlatformGetTimerValue() - base) /
(double) _glfwPlatformGetTimerFrequency();
if (remaining <= 0.0)
{ {
_glfwInputError(GLFW_PLATFORM_ERROR, _glfwInputError(GLFW_PLATFORM_ERROR,
"X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue"); "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue");
return; return;
} }
timeout.tv_sec = 0;
timeout.tv_usec = (long) (remaining * 1e6);
selectDisplayConnection(&timeout);
} }
} }
@ -2048,28 +2049,17 @@ void _glfwPlatformPollEvents(void)
void _glfwPlatformWaitEvents(void) void _glfwPlatformWaitEvents(void)
{ {
while (!XPending(_glfw.x11.display)) while (!XPending(_glfw.x11.display))
selectDisplayConnection(NULL); waitForEvent(NULL);
_glfwPlatformPollEvents(); _glfwPlatformPollEvents();
} }
void _glfwPlatformWaitEventsTimeout(double timeout) void _glfwPlatformWaitEventsTimeout(double timeout)
{ {
const double deadline = timeout + _glfwPlatformGetTimerValue() /
(double) _glfwPlatformGetTimerFrequency();
while (!XPending(_glfw.x11.display)) while (!XPending(_glfw.x11.display))
{ {
const double remaining = deadline - _glfwPlatformGetTimerValue() / if (!waitForEvent(&timeout))
(double) _glfwPlatformGetTimerFrequency(); break;
if (remaining <= 0.0)
return;
const long seconds = (long) remaining;
const long microseconds = (long) ((remaining - seconds) * 1e6);
struct timeval tv = { seconds, microseconds };
selectDisplayConnection(&tv);
} }
_glfwPlatformPollEvents(); _glfwPlatformPollEvents();
@ -2261,7 +2251,7 @@ const char* _glfwPlatformGetClipboardString(_GLFWwindow* window)
window->x11.handle, CurrentTime); window->x11.handle, CurrentTime);
while (!XCheckTypedEvent(_glfw.x11.display, SelectionNotify, &event)) while (!XCheckTypedEvent(_glfw.x11.display, SelectionNotify, &event))
selectDisplayConnection(NULL); waitForEvent(NULL);
if (event.xselection.property == None) if (event.xselection.property == None)
continue; continue;