detached 3df83c3

docs(xwayland): describe fullscreen, keyboard grab, output hint and steam_app rules

commit
3df83c312e938c78f69307645be2aeeb02c30d8b
author
brice · · 2026-10-22 15:45 UTC
committer
brice · · 2026-10-22 17:55 UTC
parent
d71e4a4
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +19 -3
M README.md +1 -1
@@ -149,7 +149,7 @@ See [Configuration](docs/config.md) for all keys, actions, bindings and automati
149149 - Explicit sync carries client fences to the GPU and KMS, enabled by default with no flag. HDR10 on capable displays includes configurable SDR brightness.
150150 - Separate workspaces and fractional scale per output; workspaces move when a monitor is unplugged and return when it reconnects.
151151 - Hidden apps get frame callbacks once per second and the suspended state, whether on another workspace, in a hidden stash, off screen or under fullscreen. Captured windows keep their output's refresh rate.
152-- Steam and other X11 clients run through xwayland-satellite, or through the experimental built-in window manager (`xwayland = builtin`; shows menus, popups and tooltips; no clipboard or XSETTINGS yet). Wine can run natively on Wayland.
152+- Steam and other X11 clients run through xwayland-satellite, or through the experimental built-in window manager (`xwayland = builtin`; menus, popups and tooltips, fullscreen games with keyboard grab; no clipboard or XSETTINGS yet). Wine can run natively on Wayland.
153153
154154 ### Windows and workspaces
155155
@@ -149,7 +149,7 @@ See [Configuration](docs/config.md) for all keys, actions, bindings and automati
149 - Explicit sync carries client fences to the GPU and KMS, enabled by default with no flag. HDR10 on capable displays includes configurable SDR brightness.149 - Explicit sync carries client fences to the GPU and KMS, enabled by default with no flag. HDR10 on capable displays includes configurable SDR brightness.
150 - Separate workspaces and fractional scale per output; workspaces move when a monitor is unplugged and return when it reconnects.150 - Separate workspaces and fractional scale per output; workspaces move when a monitor is unplugged and return when it reconnects.
151 - Hidden apps get frame callbacks once per second and the suspended state, whether on another workspace, in a hidden stash, off screen or under fullscreen. Captured windows keep their output's refresh rate.151 - Hidden apps get frame callbacks once per second and the suspended state, whether on another workspace, in a hidden stash, off screen or under fullscreen. Captured windows keep their output's refresh rate.
152-- Steam and other X11 clients run through xwayland-satellite, or through the experimental built-in window manager (`xwayland = builtin`; shows menus, popups and tooltips; no clipboard or XSETTINGS yet). Wine can run natively on Wayland.152+- Steam and other X11 clients run through xwayland-satellite, or through the experimental built-in window manager (`xwayland = builtin`; menus, popups and tooltips, fullscreen games with keyboard grab; no clipboard or XSETTINGS yet). Wine can run natively on Wayland.
153 153
154 ### Windows and workspaces154 ### Windows and workspaces
155 155
M docs/config.md +1 -0
@@ -159,6 +159,7 @@ rule.calc.floating = on
159159 - A rule never moves your focus or view.
160160 - Proton games (X11) use `steam_app_<id>` as app ID, taken from `WM_CLASS`.
161161 - An X11 window that opens exactly on one output's area opens on that output, unless a rule sets `monitor`.
162+- A rule can float a Proton game: `rule.games.app-id = steam_app_\d+` with `rule.games.floating = on`. Without a rule, X11 games tile until they ask for fullscreen.
162163 - To find an app ID: `neferwl state | jq '.windows[].app_id'`.
163164
164165 ## Output placement
@@ -159,6 +159,7 @@ rule.calc.floating = on
159 - A rule never moves your focus or view.159 - A rule never moves your focus or view.
160 - Proton games (X11) use `steam_app_<id>` as app ID, taken from `WM_CLASS`.160 - Proton games (X11) use `steam_app_<id>` as app ID, taken from `WM_CLASS`.
161 - An X11 window that opens exactly on one output's area opens on that output, unless a rule sets `monitor`.161 - An X11 window that opens exactly on one output's area opens on that output, unless a rule sets `monitor`.
162+- A rule can float a Proton game: `rule.games.app-id = steam_app_\d+` with `rule.games.floating = on`. Without a rule, X11 games tile until they ask for fullscreen.
162 - To find an app ID: `neferwl state | jq '.windows[].app_id'`.163 - To find an app ID: `neferwl state | jq '.windows[].app_id'`.
163 164
164 ## Output placement165 ## Output placement
M docs/desktop.md +9 -0
@@ -60,6 +60,15 @@ xwayland = off # no X11 support
6060
6161 With `xwayland = builtin` (experimental), neferwl starts `Xwayland` itself and manages X11 windows: they tile, take focus and close like Wayland windows. Menus, popups and tooltips of X11 apps are shown above their window and do not take the keyboard focus. Clipboard and XSETTINGS are not supported; use `satellite` if you need them.
6262
63+Games with `builtin`:
64+
65+- **Fullscreen:** an X11 window can ask for fullscreen (`_NET_WM_STATE_FULLSCREEN`) and leave it again; `toggle-fullscreen` works too. A request within 1 second of the window mapping is ignored, so a launcher that remembers a fullscreen size does not cover the windows it opens next. The app can ask again later.
66+- **Output:** a window that opens exactly at one output's area opens on that output, and its fullscreen fills it.
67+- **Keyboard grab:** an X11 app that grabs the keyboard inhibits the compositor binds only while it is focused and fullscreen; a windowed app cannot lock the binds. There is no escape bind, as with Wayland shortcut inhibitors; leave by unfullscreening from the app or switching away with the mouse.
68+- **Pointer:** mouse-look works through relative pointer and pointer constraints.
69+- **App ID:** Proton windows are named `steam_app_<id>`; match them with a [window rule](config.md#window-rules).
70+- **Bypass hint:** `_NET_WM_BYPASS_COMPOSITOR` is read but changes nothing; direct scanout is chosen from the window state.
71+
6372 ## Session locking
6473
6574 NeferWL supports `ext-session-lock-v1` with an external locker such as swaylock or hyprlock. The locker checks the password; NeferWL hides the desktop and blocks input.
@@ -60,6 +60,15 @@ xwayland = off # no X11 support
60 60
61 With `xwayland = builtin` (experimental), neferwl starts `Xwayland` itself and manages X11 windows: they tile, take focus and close like Wayland windows. Menus, popups and tooltips of X11 apps are shown above their window and do not take the keyboard focus. Clipboard and XSETTINGS are not supported; use `satellite` if you need them.61 With `xwayland = builtin` (experimental), neferwl starts `Xwayland` itself and manages X11 windows: they tile, take focus and close like Wayland windows. Menus, popups and tooltips of X11 apps are shown above their window and do not take the keyboard focus. Clipboard and XSETTINGS are not supported; use `satellite` if you need them.
62 62
63+Games with `builtin`:
64+
65+- **Fullscreen:** an X11 window can ask for fullscreen (`_NET_WM_STATE_FULLSCREEN`) and leave it again; `toggle-fullscreen` works too. A request within 1 second of the window mapping is ignored, so a launcher that remembers a fullscreen size does not cover the windows it opens next. The app can ask again later.
66+- **Output:** a window that opens exactly at one output's area opens on that output, and its fullscreen fills it.
67+- **Keyboard grab:** an X11 app that grabs the keyboard inhibits the compositor binds only while it is focused and fullscreen; a windowed app cannot lock the binds. There is no escape bind, as with Wayland shortcut inhibitors; leave by unfullscreening from the app or switching away with the mouse.
68+- **Pointer:** mouse-look works through relative pointer and pointer constraints.
69+- **App ID:** Proton windows are named `steam_app_<id>`; match them with a [window rule](config.md#window-rules).
70+- **Bypass hint:** `_NET_WM_BYPASS_COMPOSITOR` is read but changes nothing; direct scanout is chosen from the window state.
71+
63 ## Session locking72 ## Session locking
64 73
65 NeferWL supports `ext-session-lock-v1` with an external locker such as swaylock or hyprlock. The locker checks the password; NeferWL hides the desktop and blocks input.74 NeferWL supports `ext-session-lock-v1` with an external locker such as swaylock or hyprlock. The locker checks the password; NeferWL hides the desktop and blocks input.
M docs/dev/headless.md +8 -2
@@ -66,7 +66,7 @@ WAYLAND_DISPLAY=<name> go run ./examples/testime -preedit nihon -commit 日本
6666
6767 `make e2e-xwayland` runs the end-to-end test of the built-in window manager (`xwayland = builtin`). It is behind the `xwayland` build tag, so `go test ./...` and `make check` only compile it (`go vet -tags xwayland`). It needs `Xwayland` on `PATH` and a GPU, and skips without Xwayland.
6868
69-The test starts a headless NeferWL with a real `Xwayland` on the first free X display (a live session's `:0` stays untouched) and runs `internal/app/testdata/x11probe`, a small X11 client (`-class`, `-title`, `-size`, `-sleep`) that paints its window green, prints `mapped WxH` and `focus`, and exits 0 on `WM_DELETE_WINDOW`. With `-override` it also maps a magenta override-redirect window (`-override-at`, `-override-size`, `-override-type menu|tooltip`, `-override-for`, `-override-shape`, `-override-dismiss-on-focusout`) and prints `override mapped`, `override unmapped` or `dismissed`. It checks:
69+The test starts a headless NeferWL with a real `Xwayland` on the first free X display (a live session's `:0` stays untouched) and runs `internal/app/testdata/x11probe`, a small X11 client (`-class`, `-title`, `-size`, `-sleep`, `-at X,Y`) that paints its window green, prints `mapped WxH` and `focus`, and exits 0 on `WM_DELETE_WINDOW`. With `-override` it also maps a magenta override-redirect window (`-override-at`, `-override-size`, `-override-type menu|tooltip`, `-override-for`, `-override-shape`, `-override-dismiss-on-focusout`) and prints `override mapped`, `override unmapped` or `dismissed`. `-fullscreen` (with `-fullscreen-delay`, `-fullscreen-off`) requests `_NET_WM_STATE_FULLSCREEN` by client message and prints `fullscreen WxH` / `fullscreen off WxH` when the window manager publishes the state; `-keyboard-grab` calls `XGrabKeyboard` and prints `grab`. `-at` also sends a ConfigureRequest with the window rect before the map, like games do. It checks:
7070
7171 - the state lists one visible `X11Probe` window with the probe's pid, and the screenshot is mostly green inside its tile;
7272 - the probe receives focus;
@@ -74,7 +74,13 @@ The test starts a headless NeferWL with a real `Xwayland` on the first free X di
7474 - `key Super+q` closes the focused probe within 2 seconds and the state drops to one window;
7575 - an override-redirect menu is drawn magenta above the green toplevel at its X position, is not listed in the state, leaves focus on the toplevel and disappears from the screenshot once the probe unmaps it;
7676 - a menu is dismissed when its owner gets `FocusOut` because another X11 window took the focus;
77-- a menu with an empty input shape is still drawn and still not a window.
77+- a menu with an empty input shape is still drawn and still not a window;
78+- a fullscreen request 1.5 s after the map fills the output (all green, no gap) and `Super+Shift+f` leaves it; the client can also leave it itself; a request at the map stays tiled (the 1 s grace);
79+- with the keyboard grab, `Super+q` does not close a fullscreen focused client and works again once it leaves fullscreen; on a windowed client it closes it;
80+- on two outputs, a window created at the second output's origin opens on `HEADLESS-2` and fullscreens there;
81+- a window rule `rule.games.app-id = steam_app_\d+` floats an X11 window of class `steam_app_413150`, and not another class.
82+
83+Not covered headless: relative pointer lock (needs a real pointer device) and Xwayland output hotplug (`--size` is fixed); these are unit-tested in the `wayland` adapter.
7884
7985 Run it by hand with `go test -tags xwayland -race ./internal/app -run TestXwayland -count=1 -v`.
8086
@@ -66,7 +66,7 @@ WAYLAND_DISPLAY=<name> go run ./examples/testime -preedit nihon -commit 日本
66 66
67 `make e2e-xwayland` runs the end-to-end test of the built-in window manager (`xwayland = builtin`). It is behind the `xwayland` build tag, so `go test ./...` and `make check` only compile it (`go vet -tags xwayland`). It needs `Xwayland` on `PATH` and a GPU, and skips without Xwayland.67 `make e2e-xwayland` runs the end-to-end test of the built-in window manager (`xwayland = builtin`). It is behind the `xwayland` build tag, so `go test ./...` and `make check` only compile it (`go vet -tags xwayland`). It needs `Xwayland` on `PATH` and a GPU, and skips without Xwayland.
68 68
69-The test starts a headless NeferWL with a real `Xwayland` on the first free X display (a live session's `:0` stays untouched) and runs `internal/app/testdata/x11probe`, a small X11 client (`-class`, `-title`, `-size`, `-sleep`) that paints its window green, prints `mapped WxH` and `focus`, and exits 0 on `WM_DELETE_WINDOW`. With `-override` it also maps a magenta override-redirect window (`-override-at`, `-override-size`, `-override-type menu|tooltip`, `-override-for`, `-override-shape`, `-override-dismiss-on-focusout`) and prints `override mapped`, `override unmapped` or `dismissed`. It checks:69+The test starts a headless NeferWL with a real `Xwayland` on the first free X display (a live session's `:0` stays untouched) and runs `internal/app/testdata/x11probe`, a small X11 client (`-class`, `-title`, `-size`, `-sleep`, `-at X,Y`) that paints its window green, prints `mapped WxH` and `focus`, and exits 0 on `WM_DELETE_WINDOW`. With `-override` it also maps a magenta override-redirect window (`-override-at`, `-override-size`, `-override-type menu|tooltip`, `-override-for`, `-override-shape`, `-override-dismiss-on-focusout`) and prints `override mapped`, `override unmapped` or `dismissed`. `-fullscreen` (with `-fullscreen-delay`, `-fullscreen-off`) requests `_NET_WM_STATE_FULLSCREEN` by client message and prints `fullscreen WxH` / `fullscreen off WxH` when the window manager publishes the state; `-keyboard-grab` calls `XGrabKeyboard` and prints `grab`. `-at` also sends a ConfigureRequest with the window rect before the map, like games do. It checks:
70 70
71 - the state lists one visible `X11Probe` window with the probe's pid, and the screenshot is mostly green inside its tile;71 - the state lists one visible `X11Probe` window with the probe's pid, and the screenshot is mostly green inside its tile;
72 - the probe receives focus;72 - the probe receives focus;
@@ -74,7 +74,13 @@ The test starts a headless NeferWL with a real `Xwayland` on the first free X di
74 - `key Super+q` closes the focused probe within 2 seconds and the state drops to one window;74 - `key Super+q` closes the focused probe within 2 seconds and the state drops to one window;
75 - an override-redirect menu is drawn magenta above the green toplevel at its X position, is not listed in the state, leaves focus on the toplevel and disappears from the screenshot once the probe unmaps it;75 - an override-redirect menu is drawn magenta above the green toplevel at its X position, is not listed in the state, leaves focus on the toplevel and disappears from the screenshot once the probe unmaps it;
76 - a menu is dismissed when its owner gets `FocusOut` because another X11 window took the focus;76 - a menu is dismissed when its owner gets `FocusOut` because another X11 window took the focus;
77-- a menu with an empty input shape is still drawn and still not a window.77+- a menu with an empty input shape is still drawn and still not a window;
78+- a fullscreen request 1.5 s after the map fills the output (all green, no gap) and `Super+Shift+f` leaves it; the client can also leave it itself; a request at the map stays tiled (the 1 s grace);
79+- with the keyboard grab, `Super+q` does not close a fullscreen focused client and works again once it leaves fullscreen; on a windowed client it closes it;
80+- on two outputs, a window created at the second output's origin opens on `HEADLESS-2` and fullscreens there;
81+- a window rule `rule.games.app-id = steam_app_\d+` floats an X11 window of class `steam_app_413150`, and not another class.
82+
83+Not covered headless: relative pointer lock (needs a real pointer device) and Xwayland output hotplug (`--size` is fixed); these are unit-tested in the `wayland` adapter.
78 84
79 Run it by hand with `go test -tags xwayland -race ./internal/app -run TestXwayland -count=1 -v`.85 Run it by hand with `go test -tags xwayland -race ./internal/app -run TestXwayland -count=1 -v`.
80 86

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 26

Tags 10

↑↓ moveenter openesc close