Skip to content

Commit 7931321

Browse files
authored
X11: Handle WM_STATE transitions to detect Withdrawn/Iconic states (#14770)
When running SDL3 applications on tiling window managers like i3, moving a window to an invisible workspace does not trigger SDL_WINDOW_MINIMIZED or SDL_WINDOW_HIDDEN. Consequently, the application continues rendering at full speed (VSync dependent), consuming unnecessary GPU/CPU resources even when not visible. When a workspace is hidden, i3(and possible other tiling WMs) unmaps the container and sets the client window state to WithdrawnState (via the WM_STATE atom). Previously, the SDL3 X11 backend ignored changes to WM_STATE during PropertyNotify events, failing to detect this transition.
1 parent 07ecddb commit 7931321

File tree

3 files changed

+32
-0
lines changed

3 files changed

+32
-0
lines changed

src/video/x11/SDL_x11events.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2085,6 +2085,36 @@ static void X11_DispatchEvent(SDL_VideoDevice *_this, XEvent *xevent)
20852085
if (changed & SDL_WINDOW_OCCLUDED) {
20862086
SDL_SendWindowEvent(data->window, (flags & SDL_WINDOW_OCCLUDED) ? SDL_EVENT_WINDOW_OCCLUDED : SDL_EVENT_WINDOW_EXPOSED, 0, 0);
20872087
}
2088+
} else if (xevent->xproperty.atom == videodata->atoms.WM_STATE) {
2089+
/* Support for ICCCM-compliant window managers (like i3) that change
2090+
WM_STATE to WithdrawnState without sending UnmapNotify or updating
2091+
_NET_WM_STATE when moving windows to invisible workspaces. */
2092+
Atom type;
2093+
int format;
2094+
unsigned long nitems, bytes_after;
2095+
unsigned char *prop_data = NULL;
2096+
2097+
if (X11_XGetWindowProperty(display, data->xwindow, videodata->atoms.WM_STATE,
2098+
0L, 2L, False, videodata->atoms.WM_STATE,
2099+
&type, &format, &nitems, &bytes_after, &prop_data) == Success) {
2100+
if (nitems > 0) {
2101+
// WM_STATE: 0=Withdrawn, 1=Normal, 3=Iconic
2102+
Uint32 state = *(Uint32 *)prop_data;
2103+
2104+
if (state == 0 || state == 3) { // Withdrawn or Iconic
2105+
if (!(data->window->flags & SDL_WINDOW_MINIMIZED)) {
2106+
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_MINIMIZED, 0, 0);
2107+
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_OCCLUDED, 0, 0);
2108+
}
2109+
} else if (state == 1) { // NormalState
2110+
if (data->window->flags & SDL_WINDOW_MINIMIZED) {
2111+
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_RESTORED, 0, 0);
2112+
SDL_SendWindowEvent(data->window, SDL_EVENT_WINDOW_EXPOSED, 0, 0);
2113+
}
2114+
}
2115+
}
2116+
X11_XFree(prop_data);
2117+
}
20882118
} else if (xevent->xproperty.atom == videodata->atoms.XKLAVIER_STATE) {
20892119
/* Hack for Ubuntu 12.04 (etc) that doesn't send MappingNotify
20902120
events when the keyboard layout changes (for example,

src/video/x11/SDL_x11video.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ static bool X11_VideoInit(SDL_VideoDevice *_this)
362362
GET_ATOM(WM_TAKE_FOCUS);
363363
GET_ATOM(WM_NAME);
364364
GET_ATOM(WM_TRANSIENT_FOR);
365+
GET_ATOM(WM_STATE);
365366
GET_ATOM(_NET_WM_STATE);
366367
GET_ATOM(_NET_WM_STATE_HIDDEN);
367368
GET_ATOM(_NET_WM_STATE_FOCUSED);

src/video/x11/SDL_x11video.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ struct SDL_VideoData
7373
Atom WM_TAKE_FOCUS;
7474
Atom WM_NAME;
7575
Atom WM_TRANSIENT_FOR;
76+
Atom WM_STATE;
7677
Atom _NET_WM_STATE;
7778
Atom _NET_WM_STATE_HIDDEN;
7879
Atom _NET_WM_STATE_FOCUSED;

0 commit comments

Comments
 (0)