From 24565fa89c8b9a836a5a99e48b3fc1e07f90589e Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 25 Oct 2026 18:04:07 -0400 Subject: [PATCH] video: Add a "VideoPreQuit" method, move Windows hotplug shutdown to it. This prevents the thread from adding devices after the rest of the video subsystem has gone down. Now the thread is gone before that. Fixes #16437. --- src/video/SDL_sysvideo.h | 9 +++++++++ src/video/SDL_video.c | 4 ++++ src/video/windows/SDL_windowsvideo.c | 10 +++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h index 2132c57fd307c..ff1c8b30d499d 100644 --- a/src/video/SDL_sysvideo.h +++ b/src/video/SDL_sysvideo.h @@ -225,6 +225,15 @@ struct SDL_VideoDevice */ bool (*VideoInit)(SDL_VideoDevice *_this); + /* + * Called when Video subsystem is beginning to terminate. This allows for + * special case code that need to happen before internal SDL bits + * clean up that must happen before VideoQuit(). An example is stopping + * a hotplug thread that might add mice/keyboards after SDL has cleaned + * up its internal list. Most work is meant to happen in VideoQuit, though. + */ + void (*VideoPreQuit)(SDL_VideoDevice *_this); + /* * Reverse the effects VideoInit() -- called if VideoInit() fails or * if the application is shutting down the video subsystem. diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c index 874065afbfb32..bd8ae2d8a981e 100644 --- a/src/video/SDL_video.c +++ b/src/video/SDL_video.c @@ -4718,6 +4718,10 @@ void SDL_QuitVideo(void) _this->is_quitting = true; + if (_this->VideoPreQuit) { + _this->VideoPreQuit(_this); + } + // Halt event processing before doing anything else #if 0 // This was moved to the end to fix a memory leak SDL_QuitPen(); diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c index 4824b76f69e51..30e4e5339553a 100644 --- a/src/video/windows/SDL_windowsvideo.c +++ b/src/video/windows/SDL_windowsvideo.c @@ -136,6 +136,7 @@ struct ITaskbarList3 // Initialization/Query functions static bool WIN_VideoInit(SDL_VideoDevice *_this); static void WIN_VideoQuit(SDL_VideoDevice *_this); +static void WIN_VideoPreQuit(SDL_VideoDevice *_this); // Hints bool g_WindowsEnableMessageLoop = true; @@ -327,6 +328,7 @@ static SDL_VideoDevice *WIN_CreateDevice(void) // Set the function pointers device->VideoInit = WIN_VideoInit; + device->VideoPreQuit = WIN_VideoPreQuit; device->VideoQuit = WIN_VideoQuit; #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) device->RefreshDisplays = WIN_RefreshDisplays; @@ -669,6 +671,13 @@ static bool WIN_VideoInit(SDL_VideoDevice *_this) return true; } +void WIN_VideoPreQuit(SDL_VideoDevice *_this) +{ +#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) + WIN_QuitDeviceNotification(); // stop hotplug thread before the rest of the video subsystem cleans up. +#endif +} + void WIN_VideoQuit(SDL_VideoDevice *_this) { SDL_VideoData *data = _this->internal; @@ -685,7 +694,6 @@ void WIN_VideoQuit(SDL_VideoDevice *_this) #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES) WIN_QuitModes(_this); - WIN_QuitDeviceNotification(); WIN_QuitKeyboard(_this); WIN_QuitMouse(_this);