From bf12808fa107d7a0aa5b9343ddefbf6acc8629d7 Mon Sep 17 00:00:00 2001
From: Cameron Gutman <[EMAIL REDACTED]>
Date: Fri, 16 Jan 2026 22:00:55 -0600
Subject: [PATCH] window: Set grab window flags even if grab fails
SDL3 made keyboard/mouse grab failable (in which case the window
flags are not set), but SDL2 apps expect the old behavior where
SDL_WINDOW_KEYBOARD_GRABBED and SDL_WINDOW_MOUSE_GRABBED are set
regardless of whether the grab was successful.
---
src/sdl2_compat.c | 65 ++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 62 insertions(+), 3 deletions(-)
diff --git a/src/sdl2_compat.c b/src/sdl2_compat.c
index 7daa1cd1..e55e5acd 100644
--- a/src/sdl2_compat.c
+++ b/src/sdl2_compat.c
@@ -212,6 +212,8 @@ do { \
#define PROP_WINDOW_EXPECTED_HEIGHT "sdl2-compat.window.expected_height"
#define PROP_WINDOW_EXPECTED_SCALE "sdl2-compat.window.expected_scale"
#define PROP_WINDOW_GAMMA_RAMP "sdl2-compat.window.gamma_ramp"
+#define PROP_WINDOW_MOUSE_GRABBED "sdl2-compat.window.mouse_grabbed"
+#define PROP_WINDOW_KEYBOARD_GRABBED "sdl2-compat.window.keyboard_grabbed"
#define PROP_RENDERER_BATCHING "sdl2-compat.renderer.batching"
#define PROP_RENDERER_RELATIVE_SCALING "sdl2-compat.renderer.relative-scaling"
#define PROP_RENDERER_INTEGER_SCALE "sdl2-compat.renderer.integer_scale"
@@ -9054,6 +9056,7 @@ SDL_GetWindowFlags(SDL_Window *window)
{
Uint32 flags3 = (Uint32) SDL3_GetWindowFlags(window);
Uint32 flags = (flags3 & ~(SDL2_WINDOW_SHOWN | SDL2_WINDOW_FULLSCREEN | SDL2_WINDOW_FULLSCREEN_DESKTOP | SDL2_WINDOW_SKIP_TASKBAR | SDL2_WINDOW_ALWAYS_ON_TOP));
+ SDL_PropertiesID props;
/* If we get no flags back from SDL3, check if the window is actually valid */
if (flags3 == 0 && SDL3_GetWindowID(window) == 0) {
@@ -9061,6 +9064,8 @@ SDL_GetWindowFlags(SDL_Window *window)
return 0;
}
+ props = SDL3_GetWindowProperties(window);
+
if ((flags3 & SDL2_WINDOW_HIDDEN) == 0) {
flags |= SDL2_WINDOW_SHOWN;
}
@@ -9077,6 +9082,15 @@ SDL_GetWindowFlags(SDL_Window *window)
if (flags3 & SDL3_WINDOW_ALWAYS_ON_TOP) {
flags |= SDL2_WINDOW_ALWAYS_ON_TOP;
}
+
+ /* SDL2 sets grab flags unconditionally, but SDL3 sets them only on success */
+ if (SDL3_GetBooleanProperty(props, PROP_WINDOW_KEYBOARD_GRABBED, false)) {
+ flags |= SDL_WINDOW_KEYBOARD_GRABBED;
+ }
+ if (SDL3_GetBooleanProperty(props, PROP_WINDOW_MOUSE_GRABBED, false)) {
+ flags |= SDL_WINDOW_MOUSE_GRABBED;
+ }
+
return flags;
}
@@ -9248,6 +9262,14 @@ SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags)
}
if (window) {
+ SDL_PropertiesID props = SDL3_GetWindowProperties(window);
+
+ if (flags & SDL_WINDOW_KEYBOARD_GRABBED) {
+ SDL3_SetBooleanProperty(props, PROP_WINDOW_KEYBOARD_GRABBED, true);
+ }
+ if (flags & SDL_WINDOW_MOUSE_GRABBED) {
+ SDL3_SetBooleanProperty(props, PROP_WINDOW_MOUSE_GRABBED, true);
+ }
if (exclusive_fullscreen) {
ApplyFullscreenMode(window);
SDL3_SetWindowFullscreen(window, true);
@@ -9781,28 +9803,65 @@ SDL_RestoreWindow(SDL_Window *window)
SDL_DECLSPEC void SDLCALL
SDL_SetWindowGrab(SDL_Window *window, SDL2_bool grabbed)
{
- SDL3_SetWindowMouseGrab(window, grabbed);
+ SDL_SetWindowMouseGrab(window, grabbed);
if (SDL3_GetHintBoolean("SDL_GRAB_KEYBOARD", false)) {
- SDL3_SetWindowKeyboardGrab(window, grabbed);
+ SDL_SetWindowKeyboardGrab(window, grabbed);
}
}
SDL_DECLSPEC SDL2_bool SDLCALL
SDL_GetWindowGrab(SDL_Window *window)
{
- return SDL3_GetWindowKeyboardGrab(window) || SDL3_GetWindowMouseGrab(window) ? SDL2_TRUE : SDL2_FALSE;
+ if (SDL3_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS) {
+ SDL_PropertiesID props = SDL3_GetWindowProperties(window);
+ return SDL3_GetBooleanProperty(props, PROP_WINDOW_KEYBOARD_GRABBED, false) ||
+ SDL3_GetBooleanProperty(props, PROP_WINDOW_MOUSE_GRABBED, false) ? SDL2_TRUE : SDL2_FALSE;
+ }
+
+ /* Input focus is required to be the grab window */
+ return SDL2_FALSE;
}
SDL_DECLSPEC void SDLCALL
SDL_SetWindowKeyboardGrab(SDL_Window *window, SDL2_bool grabbed)
{
+ SDL_Window *prev_grab_wind = SDL3_GetGrabbedWindow();
+
+ if (!window) {
+ SDL3_SetError("Invalid window");
+ return;
+ }
+
SDL3_SetWindowKeyboardGrab(window, grabbed);
+
+ if (prev_grab_wind && prev_grab_wind != window && grabbed) {
+ /* Stealing grab from another window ends both types of grab */
+ SDL_PropertiesID prev_wind_props = SDL3_GetWindowProperties(prev_grab_wind);
+ SDL3_SetBooleanProperty(prev_wind_props, PROP_WINDOW_KEYBOARD_GRABBED, false);
+ SDL3_SetBooleanProperty(prev_wind_props, PROP_WINDOW_MOUSE_GRABBED, false);
+ }
+ SDL3_SetBooleanProperty(SDL3_GetWindowProperties(window), PROP_WINDOW_KEYBOARD_GRABBED, grabbed);
}
SDL_DECLSPEC void SDLCALL
SDL_SetWindowMouseGrab(SDL_Window *window, SDL2_bool grabbed)
{
+ SDL_Window *prev_grab_wind = SDL3_GetGrabbedWindow();
+
+ if (!window) {
+ SDL3_SetError("Invalid window");
+ return;
+ }
+
SDL3_SetWindowMouseGrab(window, grabbed);
+
+ if (prev_grab_wind && prev_grab_wind != window && grabbed) {
+ /* Stealing grab from another window ends both types of grab */
+ SDL_PropertiesID prev_wind_props = SDL3_GetWindowProperties(prev_grab_wind);
+ SDL3_SetBooleanProperty(prev_wind_props, PROP_WINDOW_KEYBOARD_GRABBED, false);
+ SDL3_SetBooleanProperty(prev_wind_props, PROP_WINDOW_MOUSE_GRABBED, false);
+ }
+ SDL3_SetBooleanProperty(SDL3_GetWindowProperties(window), PROP_WINDOW_MOUSE_GRABBED, grabbed);
}
/* SDL3 added a return value and renamed this. We just throw the value away for SDL2. */