From deb8ae6bd1edb0983d9ac972b2c95090f4c5e642 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 10 Nov 2022 18:03:32 +1100 Subject: 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. --- intern/ghost/CMakeLists.txt | 5 ++ intern/ghost/intern/GHOST_SystemWayland.cpp | 85 +++++++++++++++++++++- .../extern/wayland_dynload_client.h | 19 +++++ 3 files changed, 108 insertions(+), 1 deletion(-) diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index ea21d831b0c..5c559072625 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -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 diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index 528aa6e1884..7948112c53c 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -69,6 +69,10 @@ #include #include +#ifdef HAVE_POLL +# include +#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); } } diff --git a/intern/wayland_dynload/extern/wayland_dynload_client.h b/intern/wayland_dynload/extern/wayland_dynload_client.h index bf1e2f89c18..22ec33b1ef2 100644 --- a/intern/wayland_dynload/extern/wayland_dynload_client.h +++ b/intern/wayland_dynload/extern/wayland_dynload_client.h @@ -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__) -- cgit v1.2.3