SDL: wayland: Mark window as MOUSE_CAPTURE while a mouse button is down.

From f600364b8a575e03f140ab18eeabaeffb8f0b392 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Sat, 6 Aug 2022 09:19:52 -0400
Subject: [PATCH] wayland: Mark window as MOUSE_CAPTURE while a mouse button is
 down.

Wayland works like SDL's "auto capture" feature already, tracking the mouse
globally only while a drag is occuring, and this is the only way to get mouse
input outside the window.

Setting this flag ourselves lets SDL_CaptureMouse() work in the most common
use case without actually implementing CaptureMouse for the backend, including
SDL's auto capture feature.

Fixes #6010.
---
 src/video/wayland/SDL_waylandevents.c   | 25 ++++++++++++++++++++++++-
 src/video/wayland/SDL_waylandevents_c.h |  2 ++
 2 files changed, 26 insertions(+), 1 deletion(-)

diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 2b547faf673..14a28430c98 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -545,7 +545,7 @@ pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_t serial,
     enum wl_pointer_button_state state = state_w;
     uint32_t sdl_button;
 
-    if  (input->pointer_focus) {
+    if (window) {
         switch (button) {
             case BTN_LEFT:
                 sdl_button = SDL_BUTTON_LEFT;
@@ -569,6 +569,23 @@ pointer_handle_button_common(struct SDL_WaylandInput *input, uint32_t serial,
                 return;
         }
 
+        /* Wayland won't let you "capture" the mouse, but it will
+           automatically track the mouse outside the window if you
+           drag outside of it, until you let go of all buttons (even
+           if you add or remove presses outside the window, as long
+           as any button is still down, the capture remains) */
+        if (state) {  /* update our mask of currently-pressed buttons */
+            input->buttons_pressed |= SDL_BUTTON(sdl_button);
+        } else {
+            input->buttons_pressed &= ~(SDL_BUTTON(sdl_button));
+        }
+
+        if (input->buttons_pressed != 0) {
+            window->sdlwindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
+        } else {
+            window->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
+        }
+
         Wayland_data_device_set_serial(input->data_device, serial);
 
         SDL_SendMouseButton(window->sdlwindow, 0,
@@ -913,11 +930,17 @@ keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
                       uint32_t serial, struct wl_surface *surface)
 {
     struct SDL_WaylandInput *input = data;
+    SDL_WindowData *window;
 
     if (!surface || !SDL_WAYLAND_own_surface(surface)) {
         return;
     }
 
+    window = wl_surface_get_user_data(surface);
+    if (window) {
+        window->sdlwindow->flags &= ~SDL_WINDOW_MOUSE_CAPTURE;
+    }
+
     /* Stop key repeat before clearing keyboard focus */
     keyboard_repeat_clear(&input->keyboard_repeat);
 
diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h
index 59dc0c8afaa..9bcb2c9b155 100644
--- a/src/video/wayland/SDL_waylandevents_c.h
+++ b/src/video/wayland/SDL_waylandevents_c.h
@@ -88,6 +88,8 @@ struct SDL_WaylandInput {
     wl_fixed_t sx_w;
     wl_fixed_t sy_w;
 
+    uint32_t buttons_pressed;
+
     double dx_frac;
     double dy_frac;