Window Rules
Rules live in rules.toml. Aqueous hot-reloads the file, re-evaluates managed
windows and layer surfaces, and preserves the previous snapshot after a parse
failure.
Discover identities
Section titled “Discover identities”aqueousctl windowsaqueousctl windows --jsonaqueousctl inspect --ruleaqueousctl sceneinspect --rule emits ready-to-paste blocks. Native applications normally use
app_id; XWayland applications use class. scene reveals layer-shell
namespaces.
Matching
Section titled “Matching”Rules are evaluated from top to bottom and the first match wins. Every
managed [[window]] block needs at least one of app_id, class, title,
tag, or content_type; a matcher-free block is discarded. Unmanaged X11
rules use the separate scope described below and can also match window_type.
When several matchers are present, all must match.
String matchers are anchored, case-sensitive globs: * matches any number of
characters and ? matches one. A string without wildcards must match the
complete identifier.
[[window]]app_id = "org.example.Editor"title = "Preferences*"floating = truewidth = 900height = 640
[[window]]app_id = "org.example.Editor"workspace = 2Put narrow rules before broad rules. These examples change floating placement and workspace assignment while preserving the workspace layout.
Omitting layout leaves the workspace layout unchanged. To select Game Mode
with a rule, set layout = "game-mode" (or its alias "game_mode") explicitly.
Visual-only, placement-only, and scrolling-width rules can all omit layout.
An unset layout in the first matching rule does not inherit a later rule’s layout.
Complete field reference
Section titled “Complete field reference”Matchers
Section titled “Matchers”| Field | Values and behavior |
|---|---|
app_id |
Glob for native xdg_toplevel.app_id. |
class |
Glob for X11 WM_CLASS. |
title |
Glob for the current window title. |
content_type |
none, photo, video, or game, matching the client’s wp_content_type_v1 commit. |
Any rule containing content_type is deliberately restricted to blur,
opacity, hdr_expand, buffer_scale_policy, and overlay_plane, even when
it also contains an identity matcher. Content type often arrives after map, so
it must not move or rearrange an established window.
Layout, placement, and state
Section titled “Layout, placement, and state”| Field | Values and behavior |
|---|---|
layout |
Any top-level layout ID; omitted preserves the workspace layout. Game Mode requires explicit game-mode or game_mode. float, floating, and stack alias stacking; selecting a stacking alias also marks the window floating. |
output |
Exact enabled connector name. Without workspace, use that output’s active workspace. |
workspace |
1-based workspace. Without output, retain normal admission output. |
floating |
Force or clear persistent floating presentation. |
fullscreen |
Request true fullscreen. It is not treated as a Game Mode anchor. |
ignore_struts |
Use the full output instead of its usable area. |
stack_layer |
below, normal, or above. |
focus |
false excludes the window from compositor focus actions. |
fixed_position |
Reject compositor pointer and keyboard geometry changes. |
skip_switcher |
Exclude from MRU cycling and overview. |
skip_taskbar |
Export taskbar suppression through window-info state. |
placement_policy |
cascade, center, under-pointer, or minimal-overlap. |
width, height |
Positive floating dimensions. |
x, y |
Offsets from the usable-area origin; zero centers that axis. |
Output and workspace form one initial-placement target. Rules do not activate an inactive workspace. A missing/disabled output falls back to normal admission and does not move the established window when that output later appears.
Scrolling width rules
Section titled “Scrolling width rules”Start an application with the full-width scrolling preset enabled:
[[window]]app_id = "firefox"scrolling_full_width = trueThis is the initial equivalent of Super+Shift+Z: it expands the window’s
scrolling column, preserving the current layout when layout is omitted.
false starts with the preset disabled; omitting the field leaves it alone.
Manual toggles, horizontal resizing, and size reset override it until a
different matcher becomes active. As with other placement fields, it is ignored
by rules containing content_type.
A custom base width uses scrolling_width, a finite fraction greater than zero
and at most one:
[[window]]app_id = "firefox"scrolling_width = 0.65This gives the complete tile, including borders, 65% of the local scrolling viewport after reserved areas and outer gaps. It preserves the layout and height, including in scrolling composable regions and Game Mode remainders. Full width temporarily takes precedence; turning it off reveals the base width. Stacked members share a width owner. Horizontal resizing overrides the current members’ fractions; vertical resizing preserves them. Size reset returns to the global column fraction. Put the field in the first matching rule.
Game Mode geometry
Section titled “Game Mode geometry”Set layout = "game-mode" on the matching rule to establish an anchor. Geometry
fields such as anchor, size, and scale do not select Game Mode on their own.
| Field | Values and behavior |
|---|---|
anchor |
center, top, bottom, left, or right. |
size |
native, exact pixels such as 2560x1440, or fractions such as 0.70x0.80. |
scale |
Positive multiplier applied after resolving the requested size. |
[game_mode]remainder_layout = "scrolling"fallback_layout = "tile"gaps_inner = 8
[[window]]app_id = "steam_app_*"layout = "game-mode"output = "DP-1"workspace = 9anchor = "center"size = "0.70x0.80"scale = 1.0blur = falseopacity = 1.0The remainder and fallback accept all leaf layouts. Each surviving side column receives an independent remainder instance. A flush edge anchor collapses that side, and the fallback fills the output when no anchor matches.
Visual, buffer, HDR, and overlay policy
Section titled “Visual, buffer, HDR, and overlay policy”| Field | Values and behavior |
|---|---|
blur |
Per-window blur eligibility; global blur must also be enabled. |
opacity |
0.0 through 1.0, overriding global/focus opacity. |
buffer_scale_policy |
native or integer-ceil. |
hdr_expand |
Force or suppress Auto HDR highlight expansion. |
overlay_plane |
off or prefer. |
integer-ceil requests a larger integer-scale client buffer without changing
logical geometry. It can sharpen some toolkit output while increasing memory
and GPU bandwidth; Aqueous ships no application-specific opt-ins.
Overlay preference requires [render].overlay_planes = true before startup or
the -drm-overlay-planes switch. Aqueous tests one deterministic candidate per
output. It composes normally when TEST_ONLY rejects the state, when content
overlaps, during capture/software-cursor forcing, or when scaling, transforms,
effects, opacity, clipping, or format constraints require composition.
[[window]]content_type = "game"blur = falseopacity = 1.0hdr_expand = trueoverlay_plane = "prefer"Rule ownership and manual overrides
Section titled “Rule ownership and manual overrides”Workspace/output placement, floating, fullscreen, and stack layer have lifecycle ownership. When a rule first becomes active, Aqueous remembers the previous value and applies the request. A manual move or state toggle marks that property overridden, so later manage cycles do not fight the user. A different matching rule starts a fresh ownership lifecycle.
Game Mode has a workspace-level claim: after an explicit game rule establishes
an anchor, closing the anchor or removing its layout on reload leaves the
workspace in Game Mode using fallback_layout. Select another layout manually
to release that claim. Rules with no layout do not create a new Game Mode claim.
Blur, opacity, HDR expansion, scaling, and overlay preference are visual or client-presentation properties and are recalculated from the active rule.
Only one fullscreen owner is retained per output. Fullscreen has no border and uses the full output; maximized windows use the usable area unless global state policy says otherwise.
Layer-shell rules
Section titled “Layer-shell rules”[[layer]] rules are also ordered first-match globs:
[[layer]]namespace = "waybar"blur = trueblur_popups = true
[[layer]]namespace = "screen-locker"blur = falseblur_popups = falsenamespace is required. blur_popups covers XDG popups and nested popups only
when the main layer is also blurred. Main surfaces and popups currently use
rectangular blur bounds rather than their pixel alpha when using rule-driven
fallback blur.
In Vulkan effects builds, clients using ext-background-effect-v1 can supply
exact blur regions without a matching rule. An explicit blur = false rule
vetoes those requests; for layer popups, a matching rule must allow both blur
and blur_popups. A committed client mask, including an empty or null mask,
takes precedence over fallback blur. Destroying the effect object restores
fallback on the next surface commit. Requests are per surface and do not
propagate to popups or subsurfaces. Global blur disable always wins.
Window purpose tags
Section titled “Window purpose tags”Native applications using xdg-toplevel-tag-v1 can identify a window’s purpose
independently of its translated title. Combine a tag with app_id:
[[window]]app_id = "org.example.Editor"tag = "settings"floating = trueTags use the same glob matching, are client-provided, and need not be unique.
An unset tag does not match, even with "*"; an explicitly empty tag can match
"". Changing a tag re-evaluates rules through the usual manual-override policy.
aqueousctl windows --json reports tags and descriptions; inspect --rule
includes a literal matcher for a nonempty tag. XWayland windows have no native tag.
XWayland notifications, menus, and tooltips
Section titled “XWayland notifications, menus, and tooltips”Unmanaged X11 override-redirect surfaces have a separate rule scope. Inspect
the actual class/title/type with aqueousctl windows --json or inspect --rule;
for example, a Steam version may expose notification windows like this:
[[window]]scope = "override_redirect"class = "Steam*"window_type = "notification"opacity = 1.0focus = falseoutput = "DP-1"x = 24y = 24scope defaults to "managed". Override-redirect rules require at least one of
class, title, or window_type; all supplied matchers must match. Only
scope, those matchers, opacity, focus = false, output, x, and y are
supported. Unsupported fields or focus = true reject the reload and retain the
previous rules.
x/y are logical offsets from the selected output’s full origin. An omitted
axis keeps the client-requested offset. An unavailable output leaves client
placement in effect until it becomes available. Removing a rule restores the
latest client position and inherited opacity/focus policy. These surfaces remain
outside workspaces and taskbars; they do not become managed windows.
Reload only rules
Section titled “Reload only rules”[keybinds]reload_rules = "Super+Shift+R"This avoids running reload-time [[exec]] commands while iterating on rules.
Compatible settings frontends can edit ordered rules through the
configuration helper, which validates changes before saving.
