SDL: Always send raw mouse button state changes

From 4a00d34a867f5ae9d1839caa9360e240ee81c474 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 30 Mar 2024 07:30:43 -0700
Subject: [PATCH] Always send raw mouse button state changes

Fixes https://github.com/libsdl-org/SDL/issues/9395
---
 src/video/windows/SDL_windowsevents.c | 101 ++++++++++++--------------
 src/video/windows/SDL_windowswindow.h |   2 +-
 2 files changed, 49 insertions(+), 54 deletions(-)

diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index ba12c3ff3333b..fb66bf94d4b57 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -234,9 +234,7 @@ static void WIN_CheckWParamMouseButton(Uint64 timestamp, SDL_bool bwParamMousePr
  */
 static void WIN_CheckWParamMouseButtons(Uint64 timestamp, WPARAM wParam, SDL_WindowData *data, SDL_MouseID mouseID)
 {
-    Uint64 unique_bits = wParam;
-
-    if (unique_bits != data->mouse_button_flags) {
+    if (wParam != data->mouse_button_flags) {
         Uint32 mouseFlags = SDL_GetMouseState(NULL, NULL);
 
         /* WM_LBUTTONDOWN and friends handle button swapping for us. No need to check SM_SWAPBUTTON here.  */
@@ -246,55 +244,7 @@ static void WIN_CheckWParamMouseButtons(Uint64 timestamp, WPARAM wParam, SDL_Win
         WIN_CheckWParamMouseButton(timestamp, (wParam & MK_XBUTTON1), mouseFlags, SDL_FALSE, data, SDL_BUTTON_X1, mouseID);
         WIN_CheckWParamMouseButton(timestamp, (wParam & MK_XBUTTON2), mouseFlags, SDL_FALSE, data, SDL_BUTTON_X2, mouseID);
 
-        data->mouse_button_flags = unique_bits;
-    }
-}
-
-static void WIN_CheckRawMouseButtons(Uint64 timestamp, HANDLE hDevice, ULONG rawButtons, SDL_WindowData *data, SDL_MouseID mouseID)
-{
-    // Add a flag to distinguish raw mouse buttons from wParam above
-    Uint64 unique_bits = 0x8000000 | (uintptr_t)hDevice;
-    unique_bits <<= 32;
-    unique_bits |= rawButtons;
-
-    if (unique_bits != data->mouse_button_flags) {
-        Uint32 mouseFlags = SDL_GetMouseButtonState(SDL_GetMouse(), mouseID, SDL_FALSE);
-        SDL_bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0;
-        if (swapButtons && hDevice == NULL) {
-            /* Touchpad, already has buttons swapped */
-            swapButtons = SDL_FALSE;
-        }
-        if (rawButtons & RI_MOUSE_BUTTON_1_DOWN) {
-            WIN_CheckWParamMouseButton(timestamp, (rawButtons & RI_MOUSE_BUTTON_1_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_LEFT, mouseID);
-        }
-        if (rawButtons & RI_MOUSE_BUTTON_1_UP) {
-            WIN_CheckWParamMouseButton(timestamp, !(rawButtons & RI_MOUSE_BUTTON_1_UP), mouseFlags, swapButtons, data, SDL_BUTTON_LEFT, mouseID);
-        }
-        if (rawButtons & RI_MOUSE_BUTTON_2_DOWN) {
-            WIN_CheckWParamMouseButton(timestamp, (rawButtons & RI_MOUSE_BUTTON_2_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_RIGHT, mouseID);
-        }
-        if (rawButtons & RI_MOUSE_BUTTON_2_UP) {
-            WIN_CheckWParamMouseButton(timestamp, !(rawButtons & RI_MOUSE_BUTTON_2_UP), mouseFlags, swapButtons, data, SDL_BUTTON_RIGHT, mouseID);
-        }
-        if (rawButtons & RI_MOUSE_BUTTON_3_DOWN) {
-            WIN_CheckWParamMouseButton(timestamp, (rawButtons & RI_MOUSE_BUTTON_3_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_MIDDLE, mouseID);
-        }
-        if (rawButtons & RI_MOUSE_BUTTON_3_UP) {
-            WIN_CheckWParamMouseButton(timestamp, !(rawButtons & RI_MOUSE_BUTTON_3_UP), mouseFlags, swapButtons, data, SDL_BUTTON_MIDDLE, mouseID);
-        }
-        if (rawButtons & RI_MOUSE_BUTTON_4_DOWN) {
-            WIN_CheckWParamMouseButton(timestamp, (rawButtons & RI_MOUSE_BUTTON_4_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_X1, mouseID);
-        }
-        if (rawButtons & RI_MOUSE_BUTTON_4_UP) {
-            WIN_CheckWParamMouseButton(timestamp, !(rawButtons & RI_MOUSE_BUTTON_4_UP), mouseFlags, swapButtons, data, SDL_BUTTON_X1, mouseID);
-        }
-        if (rawButtons & RI_MOUSE_BUTTON_5_DOWN) {
-            WIN_CheckWParamMouseButton(timestamp, (rawButtons & RI_MOUSE_BUTTON_5_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_X2, mouseID);
-        }
-        if (rawButtons & RI_MOUSE_BUTTON_5_UP) {
-            WIN_CheckWParamMouseButton(timestamp, !(rawButtons & RI_MOUSE_BUTTON_5_UP), mouseFlags, swapButtons, data, SDL_BUTTON_X2, mouseID);
-        }
-        data->mouse_button_flags = unique_bits;
+        data->mouse_button_flags = wParam;
     }
 }
 
@@ -534,6 +484,15 @@ WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam)
     return 1;
 }
 
+static SDL_bool WIN_SwapButtons(HANDLE hDevice)
+{
+    if (hDevice == NULL) {
+        /* Touchpad, already has buttons swapped */
+        return SDL_FALSE;
+    }
+    return GetSystemMetrics(SM_SWAPBUTTON) != 0;
+}
+
 static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_VideoData *data, HANDLE hDevice, RAWMOUSE *rawmouse)
 {
     if (!data->raw_mouse_enabled) {
@@ -629,7 +588,43 @@ static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_VideoData *data, HANDL
         data->last_raw_mouse_position.x = x;
         data->last_raw_mouse_position.y = y;
     }
-    WIN_CheckRawMouseButtons(timestamp, hDevice, rawmouse->usButtonFlags, windowdata, mouseID);
+
+    if (rawmouse->usButtonFlags) {
+        static struct {
+            USHORT usButtonFlags;
+            Uint8 button;
+            Uint8 state;
+        } raw_buttons[] = {
+            { RI_MOUSE_LEFT_BUTTON_DOWN, SDL_BUTTON_LEFT, SDL_PRESSED },
+            { RI_MOUSE_LEFT_BUTTON_UP, SDL_BUTTON_LEFT, SDL_RELEASED },
+            { RI_MOUSE_RIGHT_BUTTON_DOWN, SDL_BUTTON_RIGHT, SDL_PRESSED },
+            { RI_MOUSE_RIGHT_BUTTON_UP, SDL_BUTTON_RIGHT, SDL_RELEASED },
+            { RI_MOUSE_MIDDLE_BUTTON_DOWN, SDL_BUTTON_MIDDLE, SDL_PRESSED },
+            { RI_MOUSE_MIDDLE_BUTTON_UP, SDL_BUTTON_MIDDLE, SDL_RELEASED },
+            { RI_MOUSE_BUTTON_4_DOWN, SDL_BUTTON_X1, SDL_PRESSED },
+            { RI_MOUSE_BUTTON_4_UP, SDL_BUTTON_X1, SDL_RELEASED },
+            { RI_MOUSE_BUTTON_5_DOWN, SDL_BUTTON_X2, SDL_PRESSED },
+            { RI_MOUSE_BUTTON_5_UP, SDL_BUTTON_X2, SDL_RELEASED }
+        };
+
+        for (int i = 0; i < SDL_arraysize(raw_buttons); ++i) {
+            if (rawmouse->usButtonFlags & raw_buttons[i].usButtonFlags) {
+                Uint8 button = raw_buttons[i].button;
+                Uint8 state = raw_buttons[i].state;
+
+                if (button == SDL_BUTTON_LEFT) {
+                    if (WIN_SwapButtons(hDevice)) {
+                        button = SDL_BUTTON_RIGHT;
+                    }
+                } else if (button == SDL_BUTTON_RIGHT) {
+                    if (WIN_SwapButtons(hDevice)) {
+                        button = SDL_BUTTON_LEFT;
+                    }
+                }
+                SDL_SendMouseButton(timestamp, window, mouseID, state, button);
+            }
+        }
+    }
 }
 
 static void WIN_HandleRawKeyboardInput(Uint64 timestamp, SDL_VideoData *data, HANDLE hDevice, RAWKEYBOARD *rawkeyboard)
diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h
index e2811d8deedde..760c1a7a93c33 100644
--- a/src/video/windows/SDL_windowswindow.h
+++ b/src/video/windows/SDL_windowswindow.h
@@ -52,7 +52,7 @@ struct SDL_WindowData
     HBITMAP hbm;
     WNDPROC wndproc;
     HHOOK keyboard_hook;
-    Uint64 mouse_button_flags;
+    WPARAM mouse_button_flags;
     LPARAM last_pointer_update;
     WCHAR high_surrogate;
     SDL_bool initializing;