|
|
@@ -495,117 +495,102 @@ void Wayland_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window)
|
|
|
WAYLAND_wl_display_flush(d->display);
|
|
|
}
|
|
|
|
|
|
-static int dispatch_queued_events(SDL_VideoData *viddata)
|
|
|
-{
|
|
|
- int rc;
|
|
|
-
|
|
|
- /*
|
|
|
- * NOTE: When reconnection is implemented, check if libdecor needs to be
|
|
|
- * involved in the reconnection process.
|
|
|
- */
|
|
|
-#ifdef HAVE_LIBDECOR_H
|
|
|
- if (viddata->shell.libdecor) {
|
|
|
- libdecor_dispatch(viddata->shell.libdecor, 0);
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
- rc = WAYLAND_wl_display_dispatch_pending(viddata->display);
|
|
|
- return rc >= 0 ? 1 : rc;
|
|
|
-}
|
|
|
-
|
|
|
int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS)
|
|
|
{
|
|
|
SDL_VideoData *d = _this->internal;
|
|
|
SDL_WaylandSeat *seat;
|
|
|
- bool key_repeat_active = false;
|
|
|
-
|
|
|
- WAYLAND_wl_display_flush(d->display);
|
|
|
+ Uint64 start = SDL_GetTicksNS();
|
|
|
+ const int display_fd = WAYLAND_wl_display_get_fd(d->display);
|
|
|
+ int ret;
|
|
|
+ bool poll_alarm_set = false;
|
|
|
|
|
|
#ifdef SDL_USE_IME
|
|
|
SDL_Window *keyboard_focus = SDL_GetKeyboardFocus();
|
|
|
if (!d->text_input_manager && keyboard_focus && SDL_TextInputActive(keyboard_focus)) {
|
|
|
- SDL_IME_PumpEvents();
|
|
|
+ // If a DBus IME is active with no text input protocol, periodically wake to poll it.
|
|
|
+ if (timeoutNS < 0 || SDL_MS_TO_NS(200) <= timeoutNS) {
|
|
|
+ timeoutNS = SDL_MS_TO_NS(200);
|
|
|
+ poll_alarm_set = true;
|
|
|
+ }
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-#ifdef SDL_USE_LIBDBUS
|
|
|
- SDL_DBus_PumpEvents();
|
|
|
-#endif
|
|
|
-
|
|
|
// If key repeat is active, we'll need to cap our maximum wait time to handle repeats
|
|
|
wl_list_for_each (seat, &d->seat_list, link) {
|
|
|
if (keyboard_repeat_is_set(&seat->keyboard.repeat)) {
|
|
|
- Wayland_SeatSetKeymap(seat);
|
|
|
-
|
|
|
- const Uint64 elapsed = SDL_GetTicksNS() - seat->keyboard.repeat.sdl_press_time_ns;
|
|
|
- if (keyboard_repeat_handle(&seat->keyboard.repeat, elapsed)) {
|
|
|
- // A repeat key event was already due
|
|
|
- return 1;
|
|
|
- } else {
|
|
|
- const Uint64 next_repeat_wait_time = (seat->keyboard.repeat.next_repeat_ns - elapsed) + 1;
|
|
|
- if (timeoutNS >= 0) {
|
|
|
- timeoutNS = SDL_min(timeoutNS, next_repeat_wait_time);
|
|
|
- } else {
|
|
|
- timeoutNS = next_repeat_wait_time;
|
|
|
- }
|
|
|
- key_repeat_active = true;
|
|
|
+ const Uint64 elapsed = start - seat->keyboard.repeat.sdl_press_time_ns;
|
|
|
+ const Uint64 next_repeat_wait_time = (seat->keyboard.repeat.next_repeat_ns - elapsed) + 1;
|
|
|
+ if (timeoutNS < 0 || next_repeat_wait_time <= timeoutNS) {
|
|
|
+ timeoutNS = next_repeat_wait_time;
|
|
|
+ poll_alarm_set = true;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /* wl_display_prepare_read() will return -1 if the default queue is not empty.
|
|
|
- * If the default queue is empty, it will prepare us for our SDL_IOReady() call. */
|
|
|
if (WAYLAND_wl_display_prepare_read(d->display) == 0) {
|
|
|
- // Use SDL_IOR_NO_RETRY to ensure SIGINT will break us out of our wait
|
|
|
- int err = SDL_IOReady(WAYLAND_wl_display_get_fd(d->display), SDL_IOR_READ | SDL_IOR_NO_RETRY, timeoutNS);
|
|
|
- if (err > 0) {
|
|
|
- // There are new events available to read
|
|
|
- WAYLAND_wl_display_read_events(d->display);
|
|
|
- return dispatch_queued_events(d);
|
|
|
- } else if (err == 0) {
|
|
|
- int ret = 0;
|
|
|
-
|
|
|
- // No events available within the timeout
|
|
|
- WAYLAND_wl_display_cancel_read(d->display);
|
|
|
+ if (timeoutNS > 0) {
|
|
|
+ const Uint64 now = SDL_GetTicksNS();
|
|
|
+ const Uint64 elapsed = now - start;
|
|
|
+ start = now;
|
|
|
+ timeoutNS = elapsed <= timeoutNS ? timeoutNS - elapsed : 0;
|
|
|
+ }
|
|
|
|
|
|
- // If key repeat is active, we might have woken up to generate a key event
|
|
|
- if (key_repeat_active) {
|
|
|
- wl_list_for_each (seat, &d->seat_list, link) {
|
|
|
- if (keyboard_repeat_is_set(&seat->keyboard.repeat)) {
|
|
|
- Wayland_SeatSetKeymap(seat);
|
|
|
+ ret = WAYLAND_wl_display_flush(d->display);
|
|
|
|
|
|
- const Uint64 elapsed = SDL_GetTicksNS() - seat->keyboard.repeat.sdl_press_time_ns;
|
|
|
- if (keyboard_repeat_handle(&seat->keyboard.repeat, elapsed)) {
|
|
|
- ++ret;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ if (ret == -1 && errno == EAGAIN) {
|
|
|
+ // Unable to write to the socket; poll until the socket can be written to, it times out, or is interrupted.
|
|
|
+ ret = SDL_IOReady(display_fd, SDL_IOR_WRITE | SDL_IOR_NO_RETRY, timeoutNS);
|
|
|
+
|
|
|
+ if (ret <= 0) {
|
|
|
+ // The poll operation timed out or experienced an error, so see if there are any events to read without waiting.
|
|
|
+ timeoutNS = 0;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- return ret;
|
|
|
- } else {
|
|
|
- // Error returned from poll()/select()
|
|
|
+ if (ret < 0) {
|
|
|
+ // Pump events on an interrupt or broken pipe to handle the error.
|
|
|
+ WAYLAND_wl_display_cancel_read(d->display);
|
|
|
+ return errno == EINTR || errno == EPIPE ? 1 : ret;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (timeoutNS > 0) {
|
|
|
+ const Uint64 now = SDL_GetTicksNS();
|
|
|
+ const Uint64 elapsed = now - start;
|
|
|
+ start = now;
|
|
|
+ timeoutNS = elapsed <= timeoutNS ? timeoutNS - elapsed : 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Use SDL_IOR_NO_RETRY to catch EINTR.
|
|
|
+ ret = SDL_IOReady(display_fd, SDL_IOR_READ | SDL_IOR_NO_RETRY, timeoutNS);
|
|
|
+ if (ret <= 0) {
|
|
|
+ // Timeout or error, cancel the read.
|
|
|
WAYLAND_wl_display_cancel_read(d->display);
|
|
|
|
|
|
- if (errno == EINTR) {
|
|
|
- /* If the wait was interrupted by a signal, we may have generated a
|
|
|
- * SDL_EVENT_QUIT event. Let the caller know to call SDL_PumpEvents(). */
|
|
|
- return 1;
|
|
|
+ // The poll timed out with no data to read, but signal the caller to pump events if polling is required.
|
|
|
+ if (ret == 0) {
|
|
|
+ return poll_alarm_set ? 1 : 0;
|
|
|
} else {
|
|
|
- return err;
|
|
|
+ // Pump events on an interrupt or broken pipe to handle the error.
|
|
|
+ return errno == EINTR || errno == EPIPE ? 1 : ret;
|
|
|
}
|
|
|
}
|
|
|
- } else {
|
|
|
- // We already had pending events
|
|
|
- return dispatch_queued_events(d);
|
|
|
+
|
|
|
+ ret = WAYLAND_wl_display_read_events(d->display);
|
|
|
+ if (ret == -1) {
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ // Signal to the caller that there might be an event available.
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
void Wayland_PumpEvents(SDL_VideoDevice *_this)
|
|
|
{
|
|
|
SDL_VideoData *d = _this->internal;
|
|
|
SDL_WaylandSeat *seat;
|
|
|
- int err;
|
|
|
+ const int display_fd = WAYLAND_wl_display_get_fd(d->display);
|
|
|
+ int ret = 0;
|
|
|
|
|
|
#ifdef SDL_USE_IME
|
|
|
SDL_Window *keyboard_focus = SDL_GetKeyboardFocus();
|
|
|
@@ -618,37 +603,70 @@ void Wayland_PumpEvents(SDL_VideoDevice *_this)
|
|
|
SDL_DBus_PumpEvents();
|
|
|
#endif
|
|
|
|
|
|
+ // Synthesize key repeat events.
|
|
|
+ wl_list_for_each (seat, &d->seat_list, link) {
|
|
|
+ if (keyboard_repeat_is_set(&seat->keyboard.repeat)) {
|
|
|
+ Wayland_SeatSetKeymap(seat);
|
|
|
+
|
|
|
+ const Uint64 elapsed = SDL_GetTicksNS() - seat->keyboard.repeat.sdl_press_time_ns;
|
|
|
+ keyboard_repeat_handle(&seat->keyboard.repeat, elapsed);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
#ifdef HAVE_LIBDECOR_H
|
|
|
if (d->shell.libdecor) {
|
|
|
libdecor_dispatch(d->shell.libdecor, 0);
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
- WAYLAND_wl_display_flush(d->display);
|
|
|
-
|
|
|
- /* wl_display_prepare_read() will return -1 if the default queue is not empty.
|
|
|
- * If the default queue is empty, it will prepare us for our SDL_IOReady() call. */
|
|
|
- if (WAYLAND_wl_display_prepare_read(d->display) == 0) {
|
|
|
- if (SDL_IOReady(WAYLAND_wl_display_get_fd(d->display), SDL_IOR_READ, 0) > 0) {
|
|
|
- WAYLAND_wl_display_read_events(d->display);
|
|
|
- } else {
|
|
|
- WAYLAND_wl_display_cancel_read(d->display);
|
|
|
+ /* If the queue isn't empty, dispatch any old events, and try to prepare for reading again.
|
|
|
+ * If preparing to read returns -1 on the second try, wl_display_read_events() enqueued new
|
|
|
+ * events at some point between dispatching the old events and preparing for the read,
|
|
|
+ * probably from another thread, which means that the events in the queue are current.
|
|
|
+ */
|
|
|
+ ret = WAYLAND_wl_display_prepare_read(d->display);
|
|
|
+ if (ret == -1) {
|
|
|
+ ret = WAYLAND_wl_display_dispatch_pending(d->display);
|
|
|
+ if (ret < 0) {
|
|
|
+ goto connection_error;
|
|
|
}
|
|
|
+
|
|
|
+ ret = WAYLAND_wl_display_prepare_read(d->display);
|
|
|
}
|
|
|
|
|
|
- // Dispatch any pre-existing pending events or new events we may have read
|
|
|
- err = WAYLAND_wl_display_dispatch_pending(d->display);
|
|
|
+ if (ret == 0) {
|
|
|
+ ret = WAYLAND_wl_display_flush(d->display);
|
|
|
|
|
|
- wl_list_for_each (seat, &d->seat_list, link) {
|
|
|
- if (keyboard_repeat_is_set(&seat->keyboard.repeat)) {
|
|
|
- Wayland_SeatSetKeymap(seat);
|
|
|
+ if (ret == -1 && errno == EAGAIN) {
|
|
|
+ // Unable to write to the socket; wait a brief time to see if it becomes writable.
|
|
|
+ ret = SDL_IOReady(display_fd, SDL_IOR_WRITE, SDL_MS_TO_NS(4));
|
|
|
+ if (ret > 0) {
|
|
|
+ ret = WAYLAND_wl_display_flush(d->display);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- const Uint64 elapsed = SDL_GetTicksNS() - seat->keyboard.repeat.sdl_press_time_ns;
|
|
|
- keyboard_repeat_handle(&seat->keyboard.repeat, elapsed);
|
|
|
+ // If the compositor closed the socket, just jump to the error handler.
|
|
|
+ if (ret < 0 && errno == EPIPE) {
|
|
|
+ WAYLAND_wl_display_cancel_read(d->display);
|
|
|
+ goto connection_error;
|
|
|
}
|
|
|
+
|
|
|
+ ret = SDL_IOReady(display_fd, SDL_IOR_READ, 0);
|
|
|
+ if (ret > 0) {
|
|
|
+ ret = WAYLAND_wl_display_read_events(d->display);
|
|
|
+ if (ret == 0) {
|
|
|
+ ret = WAYLAND_wl_display_dispatch_pending(d->display);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ WAYLAND_wl_display_cancel_read(d->display);
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ ret = WAYLAND_wl_display_dispatch_pending(d->display);
|
|
|
}
|
|
|
|
|
|
- if (err < 0 && !d->display_disconnected) {
|
|
|
+connection_error:
|
|
|
+ if (ret < 0 && !d->display_disconnected) {
|
|
|
/* Something has failed with the Wayland connection -- for example,
|
|
|
* the compositor may have shut down and closed its end of the socket,
|
|
|
* or there is a library-specific error.
|
|
|
@@ -659,9 +677,7 @@ void Wayland_PumpEvents(SDL_VideoDevice *_this)
|
|
|
d->display_disconnected = 1;
|
|
|
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "Wayland display connection closed by server (fatal)");
|
|
|
|
|
|
- /* Only send a single quit message, as application shutdown might call
|
|
|
- * SDL_PumpEvents
|
|
|
- */
|
|
|
+ // Only send a single quit message, as application shutdown might call SDL_PumpEvents().
|
|
|
SDL_SendQuit();
|
|
|
}
|
|
|
}
|