Skip to content

steamcompmgr: write back NET_WM_STATE on state changes - #2322

Open
eerii wants to merge 2 commits into
ValveSoftware:masterfrom
eerii:net-wm-state
Open

steamcompmgr: write back NET_WM_STATE on state changes#2322
eerii wants to merge 2 commits into
ValveSoftware:masterfrom
eerii:net-wm-state

Conversation

@eerii

@eerii eerii commented Aug 13, 2026

Copy link
Copy Markdown

Gamescope honors NET_WM_STATE client messages by updating its internal window state, but never writes the property back onto the client window. Clients like Firefox only consider a state change acknowledged after receiving a PropertyNotify on that property, so fullscreen requests never completed (See Firefox bug 1867649). This patch writes the state back to the window.

Tested using Firefox 153, and gamescope -- firefox, then navigating to YouTube and playing a video, and enabling and disabling fullscreen.

Note: While I'm working on supporting running Firefox natively on Wayland in Gamescope, this is a fix for running it using XWayland.

@oSoMoN

@oSoMoN

oSoMoN commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

There are several issues with the way gamescope handles (un)fullscreen requests:

  • handle_net_wm_state() changes the value of the isFullscreen property on the window, but the actual resizing is asynchronous (happens at the next run of DetermineAndApplyFocus())
  • DetermineAndApplyFocus() only operates on the currently focused window, so a window that requested fullscreen won't be resized until it gets focus (more asynchronicity)

Setting the atom on the window synchronously after receving the request, before the window has had a chance to be resized, probably violates the EWMH spec ("The Window Manager MUST keep this property updated to reflect the current state of the window.").

Additionally, the Steam window is always considered fullscreen (see window_is_fullscreen()) by gamescope, but I'm not sure whether the client expects _NET_WM_STATE_FULLSCREEN to be set, this would need to be tested carefully.

I think it'd be worth looking into decoupling the resizing of a window that requested (un)fullscreen from focus handling.

@eerii

eerii commented Aug 14, 2026

Copy link
Copy Markdown
Author

Thanks for the feedback!

I moved the window resizing to a helper function, and it is now called in handle_net_wm_state, so it is applied when we get the request, not only when we focus the window.

Then, _NET_WM_STATE should be now written after the resize request is queued, so we should be more in line with the spec, reflecting the current state of the window. Following the spec, it also just replaces _NET_WM_STATE with the states Gamescope holds, discarding the unknown ones.

Regarding Steam, we use isFullscreen to get the state, not window_is_fullscreen() (which includes Steam Big Picture), so the "always fullscreen" is not mirrored into the atom.

There is one preexisting issue that I found, which is that going out of fullscreen only restores geometry for fixed-size windows, the resizable clients need to restore themselves. The spec says that "the Window Manager is responsible for restoring the original geometry after a switch from fullscreen back to normal window", so it's something we should fix (not sure if here or in a followup).

I have tried this both with Firefox (toggling fullscreen a YouTube video still works), and also launching Steam, which is always in big picture mode and in fullscreen. Then I tried launching a game, and toggling fullscreen, and the behaviour appears correct, and identical to how it looks on master.

2026-08-14_12-50-20.mp4
2026-08-14_12-57-48.mp4
2026-08-14_12-46-25.mp4
2026-08-14_12-56-30.mp4

Comment thread src/steamcompmgr.cpp
if (w->isFullscreen) atoms[count++] = ctx->atoms.netWMStateFullscreenAtom;
if (w->skipTaskbar) atoms[count++] = ctx->atoms.netWMStateSkipTaskbarAtom;
if (w->skipPager) atoms[count++] = ctx->atoms.netWMStateSkipPagerAtom;
XChangeProperty(ctx->dpy, w->xwayland().id, ctx->atoms.netWMStateAtom,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does firefox care about _NET_WM_STATE_SKIP_TASKBAR and _NET_WM_STATE_SKIP_PAGER?

This code unconditionally calls XChangeProperty(), even if none of these 3 properties has actually changed. It would probably be better and safer to first call XGetWindowProperty() and to change only the properties we care about, leaving the others (including those that gamescope doesn't handle) unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants