Share X11 fd polling logic with Wayland

This moves the X11 polling implementation to a separate file where it
can be used by either the X11 or Wayland backend or both.

This code should be POSIX compatible where necessary but will use the
lower latency but non-standard polling functions ppoll or pollts where
those are available.

This commit is based on work by OlivierSohn and kovidgoyal.

Fixes #1281
Closes #1285
This commit is contained in:
Camilla Löwy 2022-03-03 21:26:11 +01:00
parent 203a7c59d2
commit bb9d699ae6
7 changed files with 127 additions and 61 deletions

View File

@ -60,6 +60,7 @@ if (GLFW_BUILD_X11 OR GLFW_BUILD_WAYLAND)
if (CMAKE_SYSTEM_NAME STREQUAL "Linux") if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_sources(glfw PRIVATE linux_joystick.h linux_joystick.c) target_sources(glfw PRIVATE linux_joystick.h linux_joystick.c)
endif() endif()
target_sources(glfw PRIVATE posix_poll.h posix_poll.c)
endif() endif()
if (GLFW_BUILD_WAYLAND) if (GLFW_BUILD_WAYLAND)

83
src/posix_poll.c Normal file
View File

@ -0,0 +1,83 @@
//========================================================================
// GLFW 3.4 POSIX - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2022 Camilla Löwy <elmindreda@glfw.org>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
//========================================================================
// It is fine to use C99 in this file because it will not be built with VS
//========================================================================
#if defined(__linux__)
#define _GNU_SOURCE
#endif
#include "internal.h"
#include <signal.h>
#include <time.h>
#include <errno.h>
GLFWbool _glfwPollPOSIX(struct pollfd* fds, nfds_t count, double* timeout)
{
for (;;)
{
if (timeout)
{
const uint64_t base = _glfwPlatformGetTimerValue();
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__)
const time_t seconds = (time_t) *timeout;
const long nanoseconds = (long) ((*timeout - seconds) * 1e9);
const struct timespec ts = { seconds, nanoseconds };
const int result = ppoll(fds, count, &ts, NULL);
#elif defined(__NetBSD__)
const time_t seconds = (time_t) *timeout;
const long nanoseconds = (long) ((*timeout - seconds) * 1e9);
const struct timespec ts = { seconds, nanoseconds };
const int result = pollts(fds, count, &ts, NULL);
#else
const int milliseconds = (int) (*timeout * 1e3);
const int result = poll(fds, count, milliseconds);
#endif
const int error = errno; // clock_gettime may overwrite our error
*timeout -= (_glfwPlatformGetTimerValue() - base) /
(double) _glfwPlatformGetTimerFrequency();
if (result > 0)
return GLFW_TRUE;
else if (result == -1 && error != EINTR && error != EAGAIN)
return GLFW_FALSE;
else if (*timeout <= 0.0)
return GLFW_FALSE;
}
else
{
const int result = poll(fds, count, -1);
if (result > 0)
return GLFW_TRUE;
else if (result == -1 && errno != EINTR && errno != EAGAIN)
return GLFW_FALSE;
}
}
}

32
src/posix_poll.h Normal file
View File

@ -0,0 +1,32 @@
//========================================================================
// GLFW 3.4 POSIX - www.glfw.org
//------------------------------------------------------------------------
// Copyright (c) 2022 Camilla Löwy <elmindreda@glfw.org>
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would
// be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not
// be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source
// distribution.
//
//========================================================================
// It is fine to use C99 in this file because it will not be built with VS
//========================================================================
#include <poll.h>
GLFWbool _glfwPollPOSIX(struct pollfd* fds, nfds_t count, double* timeout);

View File

@ -43,6 +43,7 @@ typedef VkResult (APIENTRY *PFN_vkCreateWaylandSurfaceKHR)(VkInstance,const VkWa
typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*);
#include "xkb_unicode.h" #include "xkb_unicode.h"
#include "posix_poll.h"
typedef int (* PFN_wl_display_flush)(struct wl_display *display); typedef int (* PFN_wl_display_flush)(struct wl_display *display);
typedef void (* PFN_wl_display_cancel_read)(struct wl_display *display); typedef void (* PFN_wl_display_cancel_read)(struct wl_display *display);

View File

@ -735,7 +735,7 @@ static GLFWbool flushDisplay(void)
return GLFW_TRUE; return GLFW_TRUE;
} }
static void handleEvents(int timeout) static void handleEvents(double* timeout)
{ {
struct pollfd fds[] = struct pollfd fds[] =
{ {
@ -763,7 +763,7 @@ static void handleEvents(int timeout)
return; return;
} }
if (poll(fds, 3, timeout) > 0) if (_glfwPollPOSIX(fds, 3, timeout))
{ {
if (fds[0].revents & POLLIN) if (fds[0].revents & POLLIN)
{ {
@ -1168,17 +1168,18 @@ GLFWbool _glfwRawMouseMotionSupportedWayland(void)
void _glfwPollEventsWayland(void) void _glfwPollEventsWayland(void)
{ {
handleEvents(0); double timeout = 0.0;
handleEvents(&timeout);
} }
void _glfwWaitEventsWayland(void) void _glfwWaitEventsWayland(void)
{ {
handleEvents(-1); handleEvents(NULL);
} }
void _glfwWaitEventsTimeoutWayland(double timeout) void _glfwWaitEventsTimeoutWayland(double timeout)
{ {
handleEvents((int) (timeout * 1e3)); handleEvents(&timeout);
} }
void _glfwPostEmptyEventWayland(void) void _glfwPostEmptyEventWayland(void)
@ -1729,7 +1730,7 @@ const char* _glfwGetClipboardStringWayland(void)
close(fds[1]); close(fds[1]);
// XXX: this is a huge hack, this function shouldnt be synchronous! // XXX: this is a huge hack, this function shouldnt be synchronous!
handleEvents(-1); handleEvents(NULL);
while (1) while (1)
{ {

View File

@ -453,6 +453,7 @@ typedef VkResult (APIENTRY *PFN_vkCreateXcbSurfaceKHR)(VkInstance,const VkXcbSur
typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice,uint32_t,xcb_connection_t*,xcb_visualid_t); typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice,uint32_t,xcb_connection_t*,xcb_visualid_t);
#include "xkb_unicode.h" #include "xkb_unicode.h"
#include "posix_poll.h"
#define GLFW_X11_WINDOW_STATE _GLFWwindowX11 x11; #define GLFW_X11_WINDOW_STATE _GLFWwindowX11 x11;
#define GLFW_X11_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11; #define GLFW_X11_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11;

View File

@ -27,18 +27,12 @@
// It is fine to use C99 in this file because it will not be built with VS // It is fine to use C99 in this file because it will not be built with VS
//======================================================================== //========================================================================
#if defined(__linux__)
#define _GNU_SOURCE
#endif
#include "internal.h" #include "internal.h"
#include <X11/cursorfont.h> #include <X11/cursorfont.h>
#include <X11/Xmd.h> #include <X11/Xmd.h>
#include <poll.h> #include <poll.h>
#include <signal.h>
#include <time.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
@ -62,53 +56,6 @@
#define _GLFW_XDND_VERSION 5 #define _GLFW_XDND_VERSION 5
// Wait for data to arrive on any of the specified file descriptors
//
static GLFWbool waitForData(struct pollfd* fds, nfds_t count, double* timeout)
{
for (;;)
{
if (timeout)
{
const uint64_t base = _glfwPlatformGetTimerValue();
#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__)
const time_t seconds = (time_t) *timeout;
const long nanoseconds = (long) ((*timeout - seconds) * 1e9);
const struct timespec ts = { seconds, nanoseconds };
const int result = ppoll(fds, count, &ts, NULL);
#elif defined(__NetBSD__)
const time_t seconds = (time_t) *timeout;
const long nanoseconds = (long) ((*timeout - seconds) * 1e9);
const struct timespec ts = { seconds, nanoseconds };
const int result = pollts(fds, count, &ts, NULL);
#else
const int milliseconds = (int) (*timeout * 1e3);
const int result = poll(fds, count, milliseconds);
#endif
const int error = errno; // clock_gettime may overwrite our error
*timeout -= (_glfwPlatformGetTimerValue() - base) /
(double) _glfwPlatformGetTimerFrequency();
if (result > 0)
return GLFW_TRUE;
else if (result == -1 && error != EINTR && error != EAGAIN)
return GLFW_FALSE;
else if (*timeout <= 0.0)
return GLFW_FALSE;
}
else
{
const int result = poll(fds, count, -1);
if (result > 0)
return GLFW_TRUE;
else if (result == -1 && errno != EINTR && errno != EAGAIN)
return GLFW_FALSE;
}
}
}
// Wait for event data to arrive on the X11 display socket // Wait for event data to arrive on the X11 display socket
// 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
@ -119,7 +66,7 @@ static GLFWbool waitForX11Event(double* timeout)
while (!XPending(_glfw.x11.display)) while (!XPending(_glfw.x11.display))
{ {
if (!waitForData(&fd, 1, timeout)) if (!_glfwPollPOSIX(&fd, 1, timeout))
return GLFW_FALSE; return GLFW_FALSE;
} }
@ -146,7 +93,7 @@ static GLFWbool waitForAnyEvent(double* timeout)
while (!XPending(_glfw.x11.display)) while (!XPending(_glfw.x11.display))
{ {
if (!waitForData(fds, count, timeout)) if (!_glfwPollPOSIX(fds, count, timeout))
return GLFW_FALSE; return GLFW_FALSE;
for (int i = 1; i < count; i++) for (int i = 1; i < count; i++)