SDL: wayland: Implement FlashWindow

From d956636c85aafc055d37153d52370e9b1c2c5929 Mon Sep 17 00:00:00 2001
From: Ethan Lee <[EMAIL REDACTED]>
Date: Fri, 4 Jun 2021 18:39:47 -0400
Subject: [PATCH] wayland: Implement FlashWindow

---
 src/video/wayland/SDL_waylandvideo.c  |  1 +
 src/video/wayland/SDL_waylandwindow.c | 68 +++++++++++++++++++++++----
 src/video/wayland/SDL_waylandwindow.h |  1 +
 3 files changed, 61 insertions(+), 9 deletions(-)

diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index bae83ac19..7503d81c8 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -216,6 +216,7 @@ Wayland_CreateDevice(int devindex)
     device->SetWindowTitle = Wayland_SetWindowTitle;
     device->DestroyWindow = Wayland_DestroyWindow;
     device->SetWindowHitTest = Wayland_SetWindowHitTest;
+    device->FlashWindow = Wayland_FlashWindow;
 
     device->SetClipboardText = Wayland_SetClipboardText;
     device->GetClipboardText = Wayland_GetClipboardText;
diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c
index 1c69d54f8..35ee3a207 100644
--- a/src/video/wayland/SDL_waylandwindow.c
+++ b/src/video/wayland/SDL_waylandwindow.c
@@ -848,11 +848,31 @@ static const struct xdg_activation_token_v1_listener activation_listener_xdg = {
     handle_xdg_activation_done
 };
 
-void Wayland_RaiseWindow(_THIS, SDL_Window *window)
+/* The xdg-activation protocol considers "activation" to be one of two things:
+ *
+ * 1: Raising a window to the top and flashing the titlebar
+ * 2: Flashing the titlebar while keeping the window where it is
+ *
+ * As you might expect from Wayland, the general policy is to go with #2 unless
+ * the client can prove to the compositor beyond a reasonable doubt that raising
+ * the window will not be malicuous behavior.
+ *
+ * For SDL this means RaiseWindow and FlashWindow both use the same protocol,
+ * but in different ways: RaiseWindow will provide as _much_ information as
+ * possible while FlashWindow will provide as _little_ information as possible,
+ * to nudge the compositor into doing what we want.
+ *
+ * This isn't _strictly_ what the protocol says will happen, but this is what
+ * current implementations are doing (as of writing, YMMV in the far distant
+ * future).
+ *
+ * -flibit
+ */
+static void
+Wayland_activate_window(SDL_VideoData *data, SDL_WindowData *wind,
+                        struct wl_surface *surface,
+                        uint32_t serial, struct wl_seat *seat)
 {
-    SDL_VideoData *data = _this->driverdata;
-    SDL_WindowData *wind = window->driverdata;
-
     if (data->activation_manager) {
         if (wind->activation_token != NULL) {
             /* We're about to overwrite this with a new request */
@@ -869,17 +889,47 @@ void Wayland_RaiseWindow(_THIS, SDL_Window *window)
          * Hypothetically we could set the app_id from data->classname, but
          * that part of the API is for _external_ programs, not ourselves.
          *
-         * As for a serial, this Raise event is arbitrary and doesn't come from
-         * an event, so it's actually very likely that this token will be
-         * ignored! TODO: Maybe add support for Raise via serial?
-         *
          * -flibit
          */
-        xdg_activation_token_v1_set_surface(wind->activation_token, wind->surface);
+        if (surface != NULL) {
+            xdg_activation_token_v1_set_surface(wind->activation_token, surface);
+        }
+        if (seat != NULL) {
+            xdg_activation_token_v1_set_serial(wind->activation_token, serial, seat);
+        }
         xdg_activation_token_v1_commit(wind->activation_token);
     }
 }
 
+void
+Wayland_RaiseWindow(_THIS, SDL_Window *window)
+{
+    SDL_WindowData *wind = window->driverdata;
+
+    /* FIXME: This Raise event is arbitrary and doesn't come from an event, so
+     * it's actually very likely that this token will be ignored! Maybe add
+     * support for passing serials (and the associated seat) so this can have
+     * a better chance of actually raising the window.
+     * -flibit
+     */
+    Wayland_activate_window(_this->driverdata,
+                            wind,
+                            wind->surface,
+                            0,
+                            NULL);
+}
+
+int
+Wayland_FlashWindow(_THIS, SDL_Window *window, Uint32 flash_count)
+{
+    Wayland_activate_window(_this->driverdata,
+                            window->driverdata,
+                            NULL,
+                            0,
+                            NULL);
+    return 0;
+}
+
 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
 static void SDLCALL
 QtExtendedSurface_OnHintChanged(void *userdata, const char *name,
diff --git a/src/video/wayland/SDL_waylandwindow.h b/src/video/wayland/SDL_waylandwindow.h
index 49b3ee447..320fd3e51 100644
--- a/src/video/wayland/SDL_waylandwindow.h
+++ b/src/video/wayland/SDL_waylandwindow.h
@@ -113,6 +113,7 @@ extern void Wayland_SuspendScreenSaver(_THIS);
 extern SDL_bool
 Wayland_GetWindowWMInfo(_THIS, SDL_Window * window, SDL_SysWMinfo * info);
 extern int Wayland_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
+extern int Wayland_FlashWindow(_THIS, SDL_Window * window, Uint32 flash_count);
 
 extern void Wayland_HandlePendingResize(SDL_Window *window);