GHOST/Wayland: replace roundtrip with dispatch_pending

Add a non-blocking version wrapper for wl_display_dispatch_pending.
This uses roughly the same logic as Wayland_PumpEvents in SDL.
Noticed this when investigating T100855.

Note that performing a round-trip doesn't seem necessary from looking
into QT/GTK & SDL event handling loops.
This commit is contained in:
Campbell Barton 2022-11-10 18:03:32 +11:00
parent c5b36aa940
commit deb8ae6bd1
Notes: blender-bot 2023-02-14 11:24:03 +01:00
Referenced by commit 2a17fd40a5, Fix non-interactive window borders after changes to event handling
3 changed files with 108 additions and 1 deletions

View File

@ -310,6 +310,11 @@ elseif(WITH_GHOST_X11 OR WITH_GHOST_WAYLAND)
add_definitions(-DHAVE_MEMFD_CREATE)
endif()
check_symbol_exists(poll "poll.h" HAVE_POLL)
if(HAVE_POLL)
add_definitions(-DHAVE_POLL)
endif()
list(APPEND SRC
intern/GHOST_SystemWayland.cpp
intern/GHOST_WindowWayland.cpp

View File

@ -69,6 +69,10 @@
#include <cstring>
#include <mutex>
#ifdef HAVE_POLL
# include <poll.h>
#endif
/* Logging, use `ghost.wl.*` prefix. */
#include "CLG_log.h"
@ -1456,6 +1460,85 @@ static int memfd_create_sealed(const char *name)
#endif /* !HAVE_MEMFD_CREATE */
}
enum {
GWL_IOR_READ = 1 << 0,
GWL_IOR_WRITE = 1 << 1,
GWL_IOR_NO_RETRY = 1 << 2,
};
static int file_descriptor_is_io_ready(int fd, const int flags, const int timeout_ms)
{
int result;
GHOST_ASSERT(flags & (GWL_IOR_READ | GWL_IOR_WRITE), "X");
/* Note: We don't bother to account for elapsed time if we get EINTR */
do {
#ifdef HAVE_POLL
struct pollfd info;
info.fd = fd;
info.events = 0;
if (flags & GWL_IOR_READ) {
info.events |= POLLIN | POLLPRI;
}
if (flags & GWL_IOR_WRITE) {
info.events |= POLLOUT;
}
result = poll(&info, 1, timeout_ms);
#else
fd_set rfdset, *rfdp = nullptr;
fd_set wfdset, *wfdp = nullptr;
struct timeval tv, *tvp = nullptr;
/* If this assert triggers we'll corrupt memory here */
GHOST_ASSERT(fd >= 0 && fd < FD_SETSIZE, "X");
if (flags & GWL_IOR_READ) {
FD_ZERO(&rfdset);
FD_SET(fd, &rfdset);
rfdp = &rfdset;
}
if (flags & GWL_IOR_WRITE) {
FD_ZERO(&wfdset);
FD_SET(fd, &wfdset);
wfdp = &wfdset;
}
if (timeout_ms >= 0) {
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
tvp = &tv;
}
result = select(fd + 1, rfdp, wfdp, nullptr, tvp);
#endif /* !HAVE_POLL */
} while (result < 0 && errno == EINTR && !(flags & GWL_IOR_NO_RETRY));
return result;
}
static int ghost_wl_display_event_pump(struct wl_display *wl_display)
{
/* Based on SDL's `Wayland_PumpEvents`. */
int err;
if (wl_display_prepare_read(wl_display) == 0) {
/* Use #GWL_IOR_NO_RETRY to ensure #SIGINT will break us out of our wait. */
if (file_descriptor_is_io_ready(
wl_display_get_fd(wl_display), GWL_IOR_READ | GWL_IOR_NO_RETRY, 0) > 0) {
err = wl_display_read_events(wl_display);
}
else {
wl_display_cancel_read(wl_display);
err = 0;
}
}
else {
err = wl_display_dispatch_pending(wl_display);
}
return err;
}
static size_t ghost_wl_shm_format_as_size(enum wl_shm_format format)
{
switch (format) {
@ -5169,7 +5252,7 @@ bool GHOST_SystemWayland::processEvents(bool waitForEvent)
}
}
else {
if (wl_display_roundtrip(display_->wl_display) == -1) {
if (ghost_wl_display_event_pump(display_->wl_display) == -1) {
ghost_wl_display_report_error(display_->wl_display);
}
}

View File

@ -14,6 +14,11 @@ extern "C" {
WAYLAND_DYNLOAD_FN(wl_display_connect)
WAYLAND_DYNLOAD_FN(wl_display_disconnect)
WAYLAND_DYNLOAD_FN(wl_display_dispatch)
WAYLAND_DYNLOAD_FN(wl_display_dispatch_pending)
WAYLAND_DYNLOAD_FN(wl_display_get_fd)
WAYLAND_DYNLOAD_FN(wl_display_prepare_read)
WAYLAND_DYNLOAD_FN(wl_display_read_events)
WAYLAND_DYNLOAD_FN(wl_display_cancel_read)
WAYLAND_DYNLOAD_FN(wl_display_roundtrip)
WAYLAND_DYNLOAD_FN(wl_display_flush)
WAYLAND_DYNLOAD_FN(wl_display_get_error)
@ -68,6 +73,11 @@ struct WaylandDynload_Client {
void WL_DYN_FN(wl_display_disconnect)(struct wl_display *display);
int WL_DYN_FN(wl_display_dispatch)(struct wl_display *display);
int WL_DYN_FN(wl_display_roundtrip)(struct wl_display *display);
int WL_DYN_FN(wl_display_dispatch_pending)(struct wl_display *display);
int WL_DYN_FN(wl_display_get_fd)(struct wl_display *display);
int WL_DYN_FN(wl_display_prepare_read)(struct wl_display *display);
int WL_DYN_FN(wl_display_read_events)(struct wl_display *display);
void WL_DYN_FN(wl_display_cancel_read)(struct wl_display *display);
int WL_DYN_FN(wl_display_flush)(struct wl_display *display);
int WL_DYN_FN(wl_display_get_error)(struct wl_display *display);
void WL_DYN_FN(wl_log_set_handler_client)(wl_log_func_t);
@ -103,6 +113,15 @@ struct WaylandDynload_Client {
# define wl_display_disconnect(...) \
(*wayland_dynload_client.wl_display_disconnect)(__VA_ARGS__)
# define wl_display_dispatch(...) (*wayland_dynload_client.wl_display_dispatch)(__VA_ARGS__)
# define wl_display_dispatch_pending(...) \
(*wayland_dynload_client.wl_display_dispatch)(__VA_ARGS__)
# define wl_display_get_fd(...) (*wayland_dynload_client.wl_display_get_fd)(__VA_ARGS__)
# define wl_display_prepare_read(...) \
(*wayland_dynload_client.wl_display_prepare_read)(__VA_ARGS__)
# define wl_display_read_events(...) \
(*wayland_dynload_client.wl_display_read_events)(__VA_ARGS__)
# define wl_display_cancel_read(...) \
(*wayland_dynload_client.wl_display_cancel_read)(__VA_ARGS__)
# define wl_display_roundtrip(...) (*wayland_dynload_client.wl_display_roundtrip)(__VA_ARGS__)
# define wl_display_flush(...) (*wayland_dynload_client.wl_display_flush)(__VA_ARGS__)
# define wl_display_get_error(...) (*wayland_dynload_client.wl_display_get_error)(__VA_ARGS__)