SDL: wayland: Set the source surface of the request when raising a window

From b82d6b8e4e48050fe9a721946894b3d88f5f3730 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Fri, 7 Jul 2023 14:23:25 -0400
Subject: [PATCH] wayland: Set the source surface of the request when raising a
 window

The xdg_activation token requires specifying the surface that is the source of the activation request, not the surface of the target window. Setting this to the target window was causing raise requests to be blocked by the compositor, as if the surface noted as the origin of the request didn't currently have focus, it appeared to the compositor as though it was trying to 'steal' focus.
---
 src/video/wayland/SDL_waylandwindow.c | 59 ++++++++++-----------------
 1 file changed, 21 insertions(+), 38 deletions(-)

diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c
index 974dcd6a053c..ee2c63cdd784 100644
--- a/src/video/wayland/SDL_waylandwindow.c
+++ b/src/video/wayland/SDL_waylandwindow.c
@@ -1688,20 +1688,22 @@ static const struct xdg_activation_token_v1_listener activation_listener_xdg = {
  *
  * -flibit
  */
-static void Wayland_activate_window(SDL_VideoData *data, SDL_WindowData *wind,
-                                    struct wl_surface *surface,
-                                    uint32_t serial, struct wl_seat *seat)
+static void Wayland_activate_window(SDL_VideoData *data, SDL_WindowData *target_wind, SDL_bool set_serial)
 {
+    struct SDL_WaylandInput * input = data->input;
+    SDL_Window *focus = SDL_GetKeyboardFocus();
+    struct wl_surface *requesting_surface = focus ? focus->driverdata->surface : NULL;
+
     if (data->activation_manager) {
-        if (wind->activation_token != NULL) {
+        if (target_wind->activation_token != NULL) {
             /* We're about to overwrite this with a new request */
-            xdg_activation_token_v1_destroy(wind->activation_token);
+            xdg_activation_token_v1_destroy(target_wind->activation_token);
         }
 
-        wind->activation_token = xdg_activation_v1_get_activation_token(data->activation_manager);
-        xdg_activation_token_v1_add_listener(wind->activation_token,
+        target_wind->activation_token = xdg_activation_v1_get_activation_token(data->activation_manager);
+        xdg_activation_token_v1_add_listener(target_wind->activation_token,
                                              &activation_listener_xdg,
-                                             wind);
+                                             target_wind);
 
         /* Note that we are not setting the app_id here.
          *
@@ -1710,47 +1712,28 @@ static void Wayland_activate_window(SDL_VideoData *data, SDL_WindowData *wind,
          *
          * -flibit
          */
-        if (surface != NULL) {
-            xdg_activation_token_v1_set_surface(wind->activation_token, surface);
+        if (requesting_surface != NULL) {
+            /* This specifies the surface from which the activation request is originating, not the activation target surface. */
+            xdg_activation_token_v1_set_surface(target_wind->activation_token, requesting_surface);
         }
-        if (seat != NULL) {
-            xdg_activation_token_v1_set_serial(wind->activation_token, serial, seat);
+        if (set_serial && input && input->seat) {
+            xdg_activation_token_v1_set_serial(target_wind->activation_token, input->last_implicit_grab_serial, input->seat);
         }
-        xdg_activation_token_v1_commit(wind->activation_token);
+        xdg_activation_token_v1_commit(target_wind->activation_token);
     }
 }
 
 void Wayland_RaiseWindow(SDL_VideoDevice *_this, SDL_Window *window)
 {
-    SDL_WindowData *wind = window->driverdata;
-    struct SDL_WaylandInput * input = _this->driverdata->input;
-    struct wl_seat *seat = NULL;
-    Uint32 serial = 0;
-
-    /* Pass the seat and last serial from a key event, mouse button press,
-     * touch down event, or tablet tool event to the activation token in order
-     * to increases the chances of the window being activated, as compositors
-     * may require an activation to be in response to an event.
-     */
-    if (input) {
-        seat = input->seat;
-        serial = input->last_implicit_grab_serial;
-    }
-
-    Wayland_activate_window(_this->driverdata,
-                            wind,
-                            wind->surface,
-                            serial,
-                            seat);
+    Wayland_activate_window(_this->driverdata, window->driverdata, SDL_TRUE);
 }
 
 int Wayland_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation)
 {
-    Wayland_activate_window(_this->driverdata,
-                            window->driverdata,
-                            NULL,
-                            0,
-                            NULL);
+    /* Not setting the serial will specify 'urgency' without switching focus as per
+     * https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/9#note_854977
+     */
+    Wayland_activate_window(_this->driverdata, window->driverdata, SDL_FALSE);
     return 0;
 }