Skip to content

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.

Terminal window
aqueousctl windows
aqueousctl windows --json
aqueousctl inspect --rule
aqueousctl scene

inspect --rule emits ready-to-paste blocks. Native applications normally use app_id; XWayland applications use class. scene reveals layer-shell namespaces.

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 = true
width = 900
height = 640
[[window]]
app_id = "org.example.Editor"
workspace = 2

Put 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.

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.

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.

Start an application with the full-width scrolling preset enabled:

[[window]]
app_id = "firefox"
scrolling_full_width = true

This 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.65

This 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.

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 = 9
anchor = "center"
size = "0.70x0.80"
scale = 1.0
blur = false
opacity = 1.0

The 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.

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 = false
opacity = 1.0
hdr_expand = true
overlay_plane = "prefer"

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]] rules are also ordered first-match globs:

[[layer]]
namespace = "waybar"
blur = true
blur_popups = true
[[layer]]
namespace = "screen-locker"
blur = false
blur_popups = false

namespace 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.

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 = true

Tags 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.0
focus = false
output = "DP-1"
x = 24
y = 24

scope 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.

[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.

© 2026 Seafoam LabsShelly Chel