wayland: Implement WaitEventTimeout() and SendWakeupEvent() We can have spurious wakeups in WaitEventTimeout() due to Wayland events that don't end up causing us to generate an SDL event. Fortunately for us, SDL_WaitEventTimeout_Device() handles this situation properly by calling WaitEventTimeout() again with an adjusted timeout.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 7a03039..9a17b0a 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -169,12 +169,13 @@ touch_surface(SDL_TouchID id)
return NULL;
}
-/* Returns the time till next repeat, or 0 if no key is down. */
-static void
+/* Returns SDL_TRUE if a key repeat event was due */
+static SDL_bool
keyboard_repeat_handle(SDL_WaylandKeyboardRepeat* repeat_info, uint32_t now)
{
+ SDL_bool ret = SDL_FALSE;
if (!repeat_info->is_key_down || !repeat_info->is_initialized) {
- return;
+ return ret;
}
while (repeat_info->next_repeat_ms <= now) {
if (repeat_info->scancode != SDL_SCANCODE_UNKNOWN) {
@@ -184,7 +185,9 @@ keyboard_repeat_handle(SDL_WaylandKeyboardRepeat* repeat_info, uint32_t now)
SDL_SendKeyboardText(repeat_info->text);
}
repeat_info->next_repeat_ms += 1000 / repeat_info->repeat_rate;
+ ret = SDL_TRUE;
}
+ return ret;
}
static void
@@ -212,6 +215,77 @@ keyboard_repeat_set(SDL_WaylandKeyboardRepeat* repeat_info,
}
void
+Wayland_SendWakeupEvent(_THIS, SDL_Window *window)
+{
+ SDL_VideoData *d = _this->driverdata;
+
+ /* TODO: Maybe use a pipe to avoid the compositor round trip? */
+ wl_display_sync(d->display);
+ WAYLAND_wl_display_flush(d->display);
+}
+
+int
+Wayland_WaitEventTimeout(_THIS, int timeout)
+{
+ SDL_VideoData *d = _this->driverdata;
+ struct SDL_WaylandInput *input = d->input;
+ SDL_bool key_repeat_active = SDL_FALSE;
+
+ WAYLAND_wl_display_flush(d->display);
+
+#ifdef SDL_USE_IME
+ if (d->text_input_manager == NULL && SDL_GetEventState(SDL_TEXTINPUT) == SDL_ENABLE) {
+ SDL_IME_PumpEvents();
+ }
+#endif
+
+ /* If key repeat is active, we'll need to cap our maximum wait time to handle repeats */
+ if (input && input->keyboard_repeat.is_initialized && input->keyboard_repeat.is_key_down) {
+ uint32_t now = SDL_GetTicks();
+ if (keyboard_repeat_handle(&input->keyboard_repeat, now)) {
+ /* A repeat key event was already due */
+ return 1;
+ } else {
+ uint32_t next_repeat_wait_time = (input->keyboard_repeat.next_repeat_ms - now) + 1;
+ if (timeout >= 0) {
+ timeout = SDL_min(timeout, next_repeat_wait_time);
+ } else {
+ timeout = next_repeat_wait_time;
+ }
+ key_repeat_active = SDL_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) {
+ if (SDL_IOReady(WAYLAND_wl_display_get_fd(d->display), SDL_FALSE, timeout) > 0) {
+ /* There are new events available to read */
+ WAYLAND_wl_display_read_events(d->display);
+ WAYLAND_wl_display_dispatch_pending(d->display);
+ return 1;
+ } else {
+ /* No events available within the timeout */
+ WAYLAND_wl_display_cancel_read(d->display);
+
+ /* If key repeat is active, we might have woken up to generate a key event */
+ if (key_repeat_active) {
+ uint32_t now = SDL_GetTicks();
+ if (keyboard_repeat_handle(&input->keyboard_repeat, now)) {
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+ } else {
+ /* We already had pending events */
+ WAYLAND_wl_display_dispatch_pending(d->display);
+ return 1;
+ }
+}
+
+void
Wayland_PumpEvents(_THIS)
{
SDL_VideoData *d = _this->driverdata;
diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h
index e4b76d8..699d2b3 100644
--- a/src/video/wayland/SDL_waylandevents_c.h
+++ b/src/video/wayland/SDL_waylandevents_c.h
@@ -83,6 +83,8 @@ struct SDL_WaylandInput {
};
extern void Wayland_PumpEvents(_THIS);
+extern void Wayland_SendWakeupEvent(_THIS, SDL_Window *window);
+extern int Wayland_WaitEventTimeout(_THIS, int timeout);
extern void Wayland_add_data_device_manager(SDL_VideoData *d, uint32_t id, uint32_t version);
extern void Wayland_add_text_input_manager(SDL_VideoData *d, uint32_t id, uint32_t version);
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index 03464d1..71f6fa8 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -169,6 +169,9 @@ Wayland_DeleteDevice(SDL_VideoDevice *device)
WAYLAND_wl_display_flush(data->display);
WAYLAND_wl_display_disconnect(data->display);
}
+ if (device->wakeup_lock) {
+ SDL_DestroyMutex(device->wakeup_lock);
+ }
SDL_free(data);
SDL_free(device);
SDL_WAYLAND_UnloadSymbols();
@@ -212,6 +215,7 @@ Wayland_CreateDevice(int devindex)
}
device->driverdata = data;
+ device->wakeup_lock = SDL_CreateMutex();
/* Set the function pointers */
device->VideoInit = Wayland_VideoInit;
@@ -222,6 +226,8 @@ Wayland_CreateDevice(int devindex)
device->SuspendScreenSaver = Wayland_SuspendScreenSaver;
device->PumpEvents = Wayland_PumpEvents;
+ device->WaitEventTimeout = Wayland_WaitEventTimeout;
+ device->SendWakeupEvent = Wayland_SendWakeupEvent;
device->GL_SwapWindow = Wayland_GLES_SwapWindow;
device->GL_GetSwapInterval = Wayland_GLES_GetSwapInterval;