Browse Source

Make changing raw input mode on Windows a very fast operation (take 2)

The first approach had the drawback that it accumulated handles while raw input was paused, eventually crashing the application.

Now we'll keep reading raw events from the queue, we just won't deliver them to  the application.
Sam Lantinga 1 day ago
parent
commit
b9c227e07d

+ 2 - 2
src/video/windows/SDL_windowsevents.c

@@ -786,7 +786,7 @@ static void WIN_HandleRawKeyboardInput(Uint64 timestamp, SDL_VideoData *data, HA
     SDL_SendKeyboardKey(timestamp, keyboardID, rawcode, code, down);
     SDL_SendKeyboardKey(timestamp, keyboardID, rawcode, code, down);
 }
 }
 
 
-void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start, bool process_input)
+void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start)
 {
 {
     SDL_VideoData *data = _this->internal;
     SDL_VideoData *data = _this->internal;
     UINT size, i, count, total = 0;
     UINT size, i, count, total = 0;
@@ -832,7 +832,7 @@ void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start, bool process_in
         }
         }
     }
     }
 
 
-    if (total > 0 && process_input) {
+    if (total > 0) {
         Uint64 delta = poll_finish - poll_start;
         Uint64 delta = poll_finish - poll_start;
         UINT mouse_total = 0;
         UINT mouse_total = 0;
         for (i = 0, input = (RAWINPUT *)data->rawinput; i < total; ++i, input = NEXTRAWINPUTBLOCK(input)) {
         for (i = 0, input = (RAWINPUT *)data->rawinput; i < total; ++i, input = NEXTRAWINPUTBLOCK(input)) {

+ 1 - 1
src/video/windows/SDL_windowsevents.h

@@ -29,7 +29,7 @@ extern HINSTANCE SDL_Instance;
 
 
 extern LRESULT CALLBACK WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam);
 extern LRESULT CALLBACK WIN_KeyboardHookProc(int nCode, WPARAM wParam, LPARAM lParam);
 extern LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
 extern LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
-extern void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start, bool process_input);
+extern void WIN_PollRawInput(SDL_VideoDevice *_this, Uint64 poll_start);
 extern void WIN_PumpEvents(SDL_VideoDevice *_this);
 extern void WIN_PumpEvents(SDL_VideoDevice *_this);
 extern void WIN_PumpEventsForHWND(SDL_VideoDevice *_this, HWND hwnd);
 extern void WIN_PumpEventsForHWND(SDL_VideoDevice *_this, HWND hwnd);
 extern void WIN_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);
 extern void WIN_SendWakeupEvent(SDL_VideoDevice *_this, SDL_Window *window);

+ 22 - 46
src/video/windows/SDL_windowsrawinput.c

@@ -40,7 +40,6 @@
 typedef struct
 typedef struct
 {
 {
     bool done;
     bool done;
-    bool paused;
     Uint32 flags;
     Uint32 flags;
     HANDLE ready_event;
     HANDLE ready_event;
     HANDLE signal_event;
     HANDLE signal_event;
@@ -48,7 +47,6 @@ typedef struct
 } RawInputThreadData;
 } RawInputThreadData;
 
 
 static RawInputThreadData thread_data = {
 static RawInputThreadData thread_data = {
-    false,
     false,
     false,
     0,
     0,
     INVALID_HANDLE_VALUE,
     INVALID_HANDLE_VALUE,
@@ -106,34 +104,22 @@ static DWORD WINAPI WIN_RawInputThread(LPVOID param)
     // Tell the parent we're ready to go!
     // Tell the parent we're ready to go!
     SetEvent(data->ready_event);
     SetEvent(data->ready_event);
 
 
-    while (!data->done) {
-        Uint64 idle_begin = SDL_GetTicksNS();
-        while (!data->done && !data->paused &&
-               // The high-order word of GetQueueStatus() will let us know if there's currently raw input to be processed.
-               // If there isn't, then we'll wait for new data to arrive with MsgWaitForMultipleObjects().
-               ((HIWORD(GetQueueStatus(QS_RAWINPUT)) & QS_RAWINPUT) ||
-                (MsgWaitForMultipleObjects(1, &data->signal_event, FALSE, INFINITE, QS_RAWINPUT) == WAIT_OBJECT_0 + 1))) {
-
-            Uint64 idle_end = SDL_GetTicksNS();
-            Uint64 idle_time = idle_end - idle_begin;
-            Uint64 usb_8khz_interval = SDL_US_TO_NS(125);
-            Uint64 poll_start = idle_time < usb_8khz_interval ? _this->internal->last_rawinput_poll : idle_end;
+    Uint64 idle_begin = SDL_GetTicksNS();
+    while (!data->done &&
+            // The high-order word of GetQueueStatus() will let us know if there's currently raw input to be processed.
+            // If there isn't, then we'll wait for new data to arrive with MsgWaitForMultipleObjects().
+            ((HIWORD(GetQueueStatus(QS_RAWINPUT)) & QS_RAWINPUT) ||
+            (MsgWaitForMultipleObjects(1, &data->signal_event, FALSE, INFINITE, QS_RAWINPUT) == WAIT_OBJECT_0 + 1))) {
 
 
-            WIN_PollRawInput(_this, poll_start, true);
-
-            // Reset idle_begin for the next go-around
-            idle_begin = SDL_GetTicksNS();
-        }
+        Uint64 idle_end = SDL_GetTicksNS();
+        Uint64 idle_time = idle_end - idle_begin;
+        Uint64 usb_8khz_interval = SDL_US_TO_NS(125);
+        Uint64 poll_start = idle_time < usb_8khz_interval ? _this->internal->last_rawinput_poll : idle_end;
 
 
-        if (data->paused) {
-            // Wait for the resume signal
-            while (data->paused) {
-                WaitForSingleObject(data->signal_event, INFINITE);
-            }
+        WIN_PollRawInput(_this, poll_start);
 
 
-            // Flush raw input queued while paused
-            WIN_PollRawInput(_this, SDL_GetTicksNS(), false);
-        }
+        // Reset idle_begin for the next go-around
+        idle_begin = SDL_GetTicksNS();
     }
     }
 
 
     if (_this->internal->raw_input_fake_pen_id) {
     if (_this->internal->raw_input_fake_pen_id) {
@@ -155,7 +141,6 @@ static DWORD WINAPI WIN_RawInputThread(LPVOID param)
 static void CleanupRawInputThreadData(RawInputThreadData *data)
 static void CleanupRawInputThreadData(RawInputThreadData *data)
 {
 {
     if (data->thread != INVALID_HANDLE_VALUE) {
     if (data->thread != INVALID_HANDLE_VALUE) {
-        data->paused = false;
         data->done = true;
         data->done = true;
         SetEvent(data->signal_event);
         SetEvent(data->signal_event);
         WaitForSingleObject(data->thread, 3000);
         WaitForSingleObject(data->thread, 3000);
@@ -174,20 +159,10 @@ static void CleanupRawInputThreadData(RawInputThreadData *data)
     }
     }
 }
 }
 
 
-bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags, bool force)
+bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags)
 {
 {
     bool result = false;
     bool result = false;
 
 
-    if (thread_data.thread != INVALID_HANDLE_VALUE && !force) {
-        if (flags) {
-            thread_data.paused = false;
-        } else {
-            thread_data.paused = true;
-        }
-        SetEvent(thread_data.signal_event);
-        return true;
-    }
-
     CleanupRawInputThreadData(&thread_data);
     CleanupRawInputThreadData(&thread_data);
 
 
     if (flags) {
     if (flags) {
@@ -248,13 +223,14 @@ static bool WIN_UpdateRawInputEnabled(SDL_VideoDevice *_this)
     if (data->raw_keyboard_flag_inputsink) {
     if (data->raw_keyboard_flag_inputsink) {
         flags |= RAW_KEYBOARD_FLAG_INPUTSINK;
         flags |= RAW_KEYBOARD_FLAG_INPUTSINK;
     }
     }
+
+    // Leave the thread running, as it takes several ms to shut it down and spin it up.
+    // We'll continue processing them so they don't back up in the thread event queue,
+    // but we won't deliver raw events in the application.
+    flags |= (data->raw_input_enabled & (ENABLE_RAW_MOUSE_INPUT | ENABLE_RAW_KEYBOARD_INPUT));
+
     if (flags != data->raw_input_enabled) {
     if (flags != data->raw_input_enabled) {
-        bool force = false;
-        if ((flags ^ data->raw_input_enabled) & (RAW_KEYBOARD_FLAG_NOHOTKEYS | RAW_KEYBOARD_FLAG_INPUTSINK)) {
-            // The keyboard flags have changed
-            force = true;
-        }
-        if (WIN_SetRawInputEnabled(_this, flags, force)) {
+        if (WIN_SetRawInputEnabled(_this, flags)) {
             data->raw_input_enabled = flags;
             data->raw_input_enabled = flags;
         } else {
         } else {
             return false;
             return false;
@@ -338,7 +314,7 @@ bool WIN_SetRawKeyboardFlag_Inputsink(SDL_VideoDevice *_this, bool enabled)
 
 
 #else
 #else
 
 
-bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags, bool force)
+bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags)
 {
 {
     return SDL_Unsupported();
     return SDL_Unsupported();
 }
 }

+ 1 - 1
src/video/windows/SDL_windowsrawinput.h

@@ -23,7 +23,7 @@
 #ifndef SDL_windowsrawinput_h_
 #ifndef SDL_windowsrawinput_h_
 #define SDL_windowsrawinput_h_
 #define SDL_windowsrawinput_h_
 
 
-extern bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags, bool force);
+extern bool WIN_SetRawInputEnabled(SDL_VideoDevice *_this, Uint32 flags);
 extern bool WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, bool enabled);
 extern bool WIN_SetRawMouseEnabled(SDL_VideoDevice *_this, bool enabled);
 extern bool WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, bool enabled);
 extern bool WIN_SetRawKeyboardEnabled(SDL_VideoDevice *_this, bool enabled);
 extern bool WIN_SetRawKeyboardFlag_NoHotkeys(SDL_VideoDevice *_this, bool enabled);
 extern bool WIN_SetRawKeyboardFlag_NoHotkeys(SDL_VideoDevice *_this, bool enabled);

+ 3 - 1
src/video/windows/SDL_windowsvideo.c

@@ -671,7 +671,9 @@ void WIN_VideoQuit(SDL_VideoDevice *_this)
     SDL_RemoveHintCallback(SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS, UpdateWindowsEnableMenuMnemonics, NULL);
     SDL_RemoveHintCallback(SDL_HINT_WINDOWS_ENABLE_MENU_MNEMONICS, UpdateWindowsEnableMenuMnemonics, NULL);
     SDL_RemoveHintCallback(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, UpdateWindowFrameUsableWhileCursorHidden, NULL);
     SDL_RemoveHintCallback(SDL_HINT_WINDOW_FRAME_USABLE_WHILE_CURSOR_HIDDEN, UpdateWindowFrameUsableWhileCursorHidden, NULL);
 
 
-    WIN_SetRawInputEnabled(_this, 0, true);
+    WIN_SetRawMouseEnabled(_this, false);
+    WIN_SetRawKeyboardEnabled(_this, false);
+    WIN_SetRawInputEnabled(_this, 0);
     WIN_QuitGameInput(_this);
     WIN_QuitGameInput(_this);
 
 
 #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
 #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)