diff options
| author | Kent Daleng <lolexplode@gmail.com> | 2025-08-17 16:05:41 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-08-17 17:05:41 +0300 |
| commit | dc93f1c1fd7b67e2da5af2ffada732b9ddeb2d6a (patch) | |
| tree | a2f2938a7df17c196be7016dc5fe1fc9f75fb484 /docs/wiki/Configuration:-Window-Rules.md | |
| parent | a6febb86aa5af0df7bf2792ca027ef95a503d599 (diff) | |
| download | niri-dc93f1c1fd7b67e2da5af2ffada732b9ddeb2d6a.tar.gz niri-dc93f1c1fd7b67e2da5af2ffada732b9ddeb2d6a.tar.bz2 niri-dc93f1c1fd7b67e2da5af2ffada732b9ddeb2d6a.zip | |
github wiki replacement / mkdocs-docs (#2147)
* Add wiki based on mkdocs
* wording fixes
* fix github bg color on narrow
* Fix left sidebar section headers being bigger than pages
* fix hover accent
* fix list rendering on fractional layout
* fix videos
* fix automatic full links
* remove redundant commented css
* improve dark mode contrast
* update pygments for better child node coloring
* update logo
* remove blank lines
* add systemd language hint
---------
Co-authored-by: Ivan Molodetskikh <yalterz@gmail.com>
Diffstat (limited to 'docs/wiki/Configuration:-Window-Rules.md')
| -rw-r--r-- | docs/wiki/Configuration:-Window-Rules.md | 919 |
1 files changed, 919 insertions, 0 deletions
diff --git a/docs/wiki/Configuration:-Window-Rules.md b/docs/wiki/Configuration:-Window-Rules.md new file mode 100644 index 00000000..66267560 --- /dev/null +++ b/docs/wiki/Configuration:-Window-Rules.md @@ -0,0 +1,919 @@ +### Overview + +Window rules let you adjust behavior for individual windows. +They have `match` and `exclude` directives that control which windows the rule should apply to, and a number of properties that you can set. + +Window rules are processed in order of appearance in the config file. +This means that you can put more generic rules first, then override them for specific windows later. +For example: + +```kdl +// Set open-maximized to true for all windows. +window-rule { + open-maximized true +} + +// Then, for Alacritty, set open-maximized back to false. +window-rule { + match app-id="Alacritty" + open-maximized false +} +``` + +> [!TIP] +> In general, you cannot "unset" a property in a later rule, only set it to a different value. +> Use the `exclude` directives to avoid applying a rule for specific windows. + +Here are all matchers and properties that a window rule could have: + +```kdl +window-rule { + match title="Firefox" + match app-id="Alacritty" + match is-active=true + match is-focused=false + match is-active-in-column=true + match is-floating=true + match is-window-cast-target=true + match is-urgent=true + match at-startup=true + + // Properties that apply once upon window opening. + default-column-width { proportion 0.75; } + default-window-height { fixed 500; } + open-on-output "Some Company CoolMonitor 1234" + open-on-workspace "chat" + open-maximized true + open-fullscreen true + open-floating true + open-focused false + + // Properties that apply continuously. + draw-border-with-background false + opacity 0.5 + block-out-from "screencast" + // block-out-from "screen-capture" + variable-refresh-rate true + default-column-display "tabbed" + default-floating-position x=100 y=200 relative-to="bottom-left" + scroll-factor 0.75 + + focus-ring { + // off + on + width 4 + active-color "#7fc8ff" + inactive-color "#505050" + urgent-color "#9b0000" + // active-gradient from="#80c8ff" to="#bbddff" angle=45 + // inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view" + // urgent-gradient from="#800" to="#a33" angle=45 + } + + border { + // Same as focus-ring. + } + + shadow { + // on + off + softness 40 + spread 5 + offset x=0 y=5 + draw-behind-window true + color "#00000064" + // inactive-color "#00000064" + } + + tab-indicator { + active-color "red" + inactive-color "gray" + urgent-color "blue" + // active-gradient from="#80c8ff" to="#bbddff" angle=45 + // inactive-gradient from="#505050" to="#808080" angle=45 relative-to="workspace-view" + // urgent-gradient from="#800" to="#a33" angle=45 + } + + geometry-corner-radius 12 + clip-to-geometry true + tiled-state true + baba-is-float true + + min-width 100 + max-width 200 + min-height 300 + max-height 300 +} +``` + +### Window Matching + +Each window rule can have several `match` and `exclude` directives. +In order for the rule to apply, a window needs to match *any* of the `match` directives, and *none* of the `exclude` directives. + +```kdl +window-rule { + // Match all Telegram windows... + match app-id=r#"^org\.telegram\.desktop$"# + + // ...except the media viewer window. + exclude title="^Media viewer$" + + // Properties to apply. + open-on-output "HDMI-A-1" +} +``` + +Match and exclude directives have the same syntax. +There can be multiple *matchers* in one directive, then the window should match all of them for the directive to apply. + +```kdl +window-rule { + // Match Firefox windows with Gmail in title. + match app-id="firefox" title="Gmail" +} + +window-rule { + // Match Firefox, but only when it is active... + match app-id="firefox" is-active=true + + // ...or match Telegram... + match app-id=r#"^org\.telegram\.desktop$"# + + // ...but don't match the Telegram media viewer. + // If you open a tab in Firefox titled "Media viewer", + // it will not be excluded because it doesn't match the app-id + // of this exclude directive. + exclude app-id=r#"^org\.telegram\.desktop$"# title="Media viewer" +} +``` + +Let's look at the matchers in more detail. + +#### `title` and `app-id` + +These are regular expressions that should match anywhere in the window title and app ID respectively. +You can read about the supported regular expression syntax [here](https://docs.rs/regex/latest/regex/#syntax). + +```kdl +// Match windows with title containing "Mozilla Firefox", +// or windows with app ID containing "Alacritty". +window-rule { + match title="Mozilla Firefox" + match app-id="Alacritty" +} +``` + +Raw KDL strings can be helpful for writing out regular expressions: + +```kdl +window-rule { + exclude app-id=r#"^org\.keepassxc\.KeePassXC$"# +} +``` + +You can find the title and the app ID of the currently focused window by running `niri msg focused-window`. + +> [!TIP] +> Another way to find the window title and app ID is to configure the `wlr/taskbar` module in [Waybar](https://github.com/Alexays/Waybar) to include them in the tooltip: +> +> ```json +> "wlr/taskbar": { +> "tooltip-format": "{title} | {app_id}", +> } +> ``` + +#### `is-active` + +Can be `true` or `false`. +Matches active windows (same windows that have the active border / focus ring color). + +Every workspace on the focused monitor will have one active window. +This means that you will usually have multiple active windows (one per workspace), and when you switch between workspaces, you can see two active windows at once. + +```kdl +window-rule { + match is-active=true +} +``` + +#### `is-focused` + +Can be `true` or `false`. +Matches the window that has the keyboard focus. + +Contrary to `is-active`, there can only be a single focused window. +Also, when opening a layer-shell application launcher or pop-up menu, the keyboard focus goes to layer-shell. +While layer-shell has the keyboard focus, windows will not match this rule. + +```kdl +window-rule { + match is-focused=true +} +``` + +#### `is-active-in-column` + +<sup>Since: 0.1.6</sup> + +Can be `true` or `false`. +Matches the window that is the "active" window in its column. + +Contrary to `is-active`, there is always one `is-active-in-column` window in each column. +It is the window that was last focused in the column, i.e. the one that will gain focus if this column is focused. + +<sup>Since: 25.01</sup> This rule will match `true` during the initial window opening. + +```kdl +window-rule { + match is-active-in-column=true +} +``` + +#### `is-floating` + +<sup>Since: 25.01</sup> + +Can be `true` or `false`. +Matches floating windows. + +> [!NOTE] +> This matcher will apply only after the window is already open. +> This means that you cannot use it to change the window opening properties like `default-window-height` or `open-on-workspace`. + +```kdl +window-rule { + match is-floating=true +} +``` + +#### `is-window-cast-target` + +<sup>Since: 25.02</sup> + +Can be `true` or `false`. +Matches `true` for windows that are target of an ongoing window screencast. + +> [!NOTE] +> This only matches individual-window screencasts. +> It will not match windows that happen to be visible in a monitor screencast, for example. + +```kdl +// Indicate screencasted windows with red colors. +window-rule { + match is-window-cast-target=true + + focus-ring { + active-color "#f38ba8" + inactive-color "#7d0d2d" + } + + border { + inactive-color "#7d0d2d" + } + + shadow { + color "#7d0d2d70" + } + + tab-indicator { + active-color "#f38ba8" + inactive-color "#7d0d2d" + } +} +``` + +Example: + + + +#### `is-urgent` + +<sup>Since: 25.05</sup> + +Can be `true` or `false`. +Matches windows that request the user's attention. + +```kdl +window-rule { + match is-urgent=true +} +``` + +#### `at-startup` + +<sup>Since: 0.1.6</sup> + +Can be `true` or `false`. +Matches during the first 60 seconds after starting niri. + +This is useful for properties like `open-on-output` which you may want to apply only right after starting niri. + +```kdl +// Open windows on the HDMI-A-1 monitor at niri startup, but not afterwards. +window-rule { + match at-startup=true + open-on-output "HDMI-A-1" +} +``` + +### Window Opening Properties + +These properties apply once, when a window first opens. + +To be precise, they apply at the point when niri sends the initial configure request to the window. + +#### `default-column-width` + +Set the default width for the new window. + +This works for floating windows too, despite the word "column" in the name. + +```kdl +// Give Blender and GIMP some guaranteed width on opening. +window-rule { + match app-id="^blender$" + + // GIMP app ID contains the version like "gimp-2.99", + // so we only match the beginning (with ^) and not the end. + match app-id="^gimp" + + default-column-width { fixed 1200; } +} +``` + +#### `default-window-height` + +<sup>Since: 25.01</sup> + +Set the default height for the new window. + +```kdl +// Open the Firefox picture-in-picture window as floating with 480×270 size. +window-rule { + match app-id="firefox$" title="^Picture-in-Picture$" + + open-floating true + default-column-width { fixed 480; } + default-window-height { fixed 270; } +} +``` + +#### `open-on-output` + +Make the window open on a specific output. + +If such an output does not exist, the window will open on the currently focused output as usual. + +If the window opens on an output that is not currently focused, the window will not be automatically focused. + +```kdl +// Open Firefox and Telegram (but not its Media Viewer) +// on a specific monitor. +window-rule { + match app-id="firefox$" + match app-id=r#"^org\.telegram\.desktop$"# + exclude app-id=r#"^org\.telegram\.desktop$"# title="^Media viewer$" + + open-on-output "HDMI-A-1" + // Or: + // open-on-output "Some Company CoolMonitor 1234" +} +``` + +<sup>Since: 0.1.9</sup> `open-on-output` can now use monitor manufacturer, model, and serial. +Before, it could only use the connector name. + +#### `open-on-workspace` + +<sup>Since: 0.1.6</sup> + +Make the window open on a specific [named workspace](./Configuration:-Named-Workspaces.md). + +If such a workspace does not exist, the window will open on the currently focused workspace as usual. + +If the window opens on an output that is not currently focused, the window will not be automatically focused. + +```kdl +// Open Fractal on the "chat" workspace. +window-rule { + match app-id=r#"^org\.gnome\.Fractal$"# + + open-on-workspace "chat" +} +``` + +#### `open-maximized` + +Make the window open as a maximized column. + +```kdl +// Maximize Firefox by default. +window-rule { + match app-id="firefox$" + + open-maximized true +} +``` + +#### `open-fullscreen` + +Make the window open fullscreen. + +```kdl +window-rule { + open-fullscreen true +} +``` + +You can also set this to `false` to *prevent* a window from opening fullscreen. + +```kdl +// Make the Telegram media viewer open in windowed mode. +window-rule { + match app-id=r#"^org\.telegram\.desktop$"# title="^Media viewer$" + + open-fullscreen false +} +``` + +#### `open-floating` + +<sup>Since: 25.01</sup> + +Make the window open in the floating layout. + +```kdl +// Open the Firefox picture-in-picture window as floating. +window-rule { + match app-id="firefox$" title="^Picture-in-Picture$" + + open-floating true +} +``` + +You can also set this to `false` to *prevent* a window from opening in the floating layout. + +```kdl +// Open all windows in the tiling layout, overriding any auto-floating logic. +window-rule { + open-floating false +} +``` + +#### `open-focused` + +<sup>Since: 25.01</sup> + +Set this to `false` to prevent this window from being automatically focused upon opening. + +```kdl +// Don't give focus to the GIMP startup splash screen. +window-rule { + match app-id="^gimp" title="^GIMP Startup$" + + open-focused false +} +``` + +You can also set this to `true` to focus the window, even if normally it wouldn't get auto-focused. + +```kdl +// Always focus the KeePassXC-Browser unlock dialog. +// +// This dialog opens parented to the KeePassXC window rather than the browser, +// so it doesn't get auto-focused by default. +window-rule { + match app-id=r#"^org\.keepassxc\.KeePassXC$"# title="^Unlock Database - KeePassXC$" + + open-focused true +} +``` + +### Dynamic Properties + +These properties apply continuously to open windows. + +#### `block-out-from` + +You can block out windows from xdg-desktop-portal screencasts. +They will be replaced with solid black rectangles. + +This can be useful for password managers or messenger windows, etc. +For layer-shell notification pop-ups and the like, you can use a [`block-out-from` layer rule](./Configuration:-Layer-Rules.md#block-out-from). + + + +To preview and set up this rule, check the `preview-render` option in the debug section of the config. + +> [!CAUTION] +> The window is **not** blocked out from third-party screenshot tools. +> If you open some screenshot tool with preview while screencasting, blocked out windows **will be visible** on the screencast. + +The built-in screenshot UI is not affected by this problem though. +If you open the screenshot UI while screencasting, you will be able to select the area to screenshot while seeing all windows normally, but on a screencast the selection UI will display with windows blocked out. + +```kdl +// Block out password managers from screencasts. +window-rule { + match app-id=r#"^org\.keepassxc\.KeePassXC$"# + match app-id=r#"^org\.gnome\.World\.Secrets$"# + + block-out-from "screencast" +} +``` + +Alternatively, you can block out the window out of *all* screen captures, including third-party screenshot tools. +This way you avoid accidentally showing the window on a screencast when opening a third-party screenshot preview. + +This setting will still let you use the interactive built-in screenshot UI, but it will block out the window from the fully automatic screenshot actions, such as `screenshot-screen` and `screenshot-window`. +The reasoning is that with an interactive selection, you can make sure that you avoid screenshotting sensitive content. + +```kdl +window-rule { + block-out-from "screen-capture" +} +``` + +> [!WARNING] +> Be careful when blocking out windows based on a dynamically changing window title. +> +> For example, you might try to block out specific Firefox tabs like this: +> +> ```kdl +> window-rule { +> // Doesn't quite work! Try to block out the Gmail tab. +> match app-id="firefox$" title="- Gmail " +> +> block-out-from "screencast" +> } +> ``` +> +> It will work, but when switching from a sensitive tab to a regular tab, the contents of the sensitive tab **will show up on a screencast** for an instant. +> +> This is because window title (and app ID) are not double-buffered in the Wayland protocol, so they are not tied to specific window contents. +> There's no robust way for Firefox to synchronize visibly showing a different tab and changing the window title. + +#### `opacity` + +Set the opacity of the window. +`0.0` is fully transparent, `1.0` is fully opaque. +This is applied on top of the window's own opacity, so semitransparent windows will become even more transparent. + +Opacity is applied to every surface of the window individually, so subsurfaces and pop-up menus will show window content behind them. + + + +Also, focus ring and border with background will show through semitransparent windows (see `prefer-no-csd` and the `draw-border-with-background` window rule below). + +Opacity can be toggled on or off for a window using the [`toggle-window-rule-opacity`](./Configuration:-Key-Bindings.md#toggle-window-rule-opacity) action. + +```kdl +// Make inactive windows semitransparent. +window-rule { + match is-active=false + + opacity 0.95 +} +``` + +#### `variable-refresh-rate` + +<sup>Since: 0.1.9</sup> + +If set to true, whenever this window displays on an output with on-demand VRR, it will enable VRR on that output. + +```kdl +// Configure some output with on-demand VRR. +output "HDMI-A-1" { + variable-refresh-rate on-demand=true +} + +// Enable on-demand VRR when mpv displays on the output. +window-rule { + match app-id="^mpv$" + + variable-refresh-rate true +} +``` + +#### `default-column-display` + +<sup>Since: 25.02</sup> + +Set the default display mode for columns created from this window. + +This is used any time a window goes into its own column. +For example: +- Opening a new window. +- Expelling a window into its own column. +- Moving a window from the floating layout to the tiling layout. + +```kdl +// Make Evince windows open as tabbed columns. +window-rule { + match app-id="^evince$" + + default-column-display "tabbed" +} +``` + +#### `default-floating-position` + +<sup>Since: 25.01</sup> + +Set the initial position for this window when it opens on, or moves to the floating layout. + +Afterward, the window will remember its last floating position. + +By default, new floating windows open at the center of the screen, and windows from the tiling layout open close to their visual screen position. + +The position uses logical coordinates relative to the working area. +By default, they are relative to the top-left corner of the working area, but you can change this by setting `relative-to` to one of these values: `top-left`, `top-right`, `bottom-left`, `bottom-right`, `top`, `bottom`, `left`, or `right`. + +For example, if you have a bar at the top, then `x=0 y=0` will put the top-left corner of the window directly below the bar. +If instead you write `x=0 y=0 relative-to="top-right"`, then the top-right corner of the window will align with the top-right corner of the workspace, also directly below the bar. +When only one side is specified (e.g. top) the window will align to the center of that side. + +The coordinates change direction based on `relative-to`. +For example, by default (top-left), `x=100 y=200` will put the window 100 pixels to the right and 200 pixels down from the top-left corner. +If you use `x=100 y=200 relative-to="bottom-left"`, it will put the window 100 pixels to the right and 200 pixels *up* from the bottom-left corner. + +```kdl +// Open the Firefox picture-in-picture window at the bottom-left corner of the screen +// with a small gap. +window-rule { + match app-id="firefox$" title="^Picture-in-Picture$" + + default-floating-position x=32 y=32 relative-to="bottom-left" +} +``` + +You can use single-side `relative-to` to get a dropdown-like effect. + +```kdl +// Example: a "dropdown" terminal. +window-rule { + // Match by "dropdown" app ID. + // You need to set this app ID when running your terminal, e.g.: + // spawn "alacritty" "--class" "dropdown" + match app-id="^dropdown$" + + // Open it as floating. + open-floating true + // Anchor to the top edge of the screen. + default-floating-position x=0 y=0 relative-to="top" + // Half of the screen high. + default-window-height { proportion 0.5; } + // 80% of the screen wide. + default-column-width { proportion 0.8; } +} +``` + +#### `scroll-factor` + +<sup>Since: 25.02</sup> + +Set a scroll factor for all scroll events sent to a window. + +This will be multiplied with the scroll factor set for your input device in the [input section](./Configuration:-Input.md#pointing-devices). + +```kdl +// Make scrolling in Firefox a bit slower. +window-rule { + match app-id="firefox$" + + scroll-factor 0.75 +} +``` + +#### `draw-border-with-background` + +Override whether the border and the focus ring draw with a background. + +Set this to `true` to draw them as solid colored rectangles even for windows which agreed to omit their client-side decorations. +Set this to `false` to draw them as borders around the window even for windows which use client-side decorations. + +This property can be useful for rectangular windows that do not support the xdg-decoration protocol. + +| With Background | Without Background | +| ------------------------------------------------ | --------------------------------------------------- | +|  |  | + +```kdl +window-rule { + draw-border-with-background false +} +``` + +#### `focus-ring` and `border` + +<sup>Since: 0.1.6</sup> + +Override the focus ring and border options for the window. + +These rules have the same options as the normal [`focus-ring` and `border` config in the layout section](./Configuration:-Layout.md#focus-ring-and-border), so check the documentation there. + +However, in addition to `off` to disable the border/focus ring, this window rule has an `on` flag that enables the border/focus ring for the window even if it was otherwise disabled. +The `on` flag has precedence over the `off` flag, in case both are set. + +```kdl +window-rule { + focus-ring { + off + width 2 + } +} + +window-rule { + border { + on + width 8 + } +} +``` + +#### `shadow` + +<sup>Since: 25.02</sup> + +Override the shadow options for the window. + +This rule has the same options as the normal [`shadow` config in the layout section](./Configuration:-Layout.md#shadow), so check the documentation there. + +However, in addition to `on` to enable the shadow, this window rule has an `off` flag that disables the shadow for the window even if it was otherwise enabled. +The `on` flag has precedence over the `off` flag, in case both are set. + +```kdl +// Turn on shadows for floating windows. +window-rule { + match is-floating=true + + shadow { + on + } +} +``` + +#### `tab-indicator` + +<sup>Since: 25.02</sup> + +Override the tab indicator options for the window. + +Options in this rule match the same options as the normal [`tab-indicator` config in the layout section](./Configuration:-Layout.md#tab-indicator), so check the documentation there. + +```kdl +// Make KeePassXC tab have a dark red inactive color. +window-rule { + match app-id=r#"^org\.keepassxc\.KeePassXC$"# + + tab-indicator { + inactive-color "darkred" + } +} +``` + +#### `geometry-corner-radius` + +<sup>Since: 0.1.6</sup> + +Set the corner radius of the window. + +On its own, this setting will only affect the border and the focus ring—they will round their corners to match the geometry corner radius. +If you'd like to force-round the corners of the window itself, set [`clip-to-geometry true`](#clip-to-geometry) in addition to this setting. + +```kdl +window-rule { + geometry-corner-radius 12 +} +``` + +The radius is set in logical pixels, and controls the radius of the window itself, that is, the inner radius of the border: + + + +Instead of one radius, you can set four, for each corner. +The order is the same as in CSS: top-left, top-right, bottom-right, bottom-left. + +```kdl +window-rule { + geometry-corner-radius 8 8 0 0 +} +``` + +This way, you can match GTK 3 applications which have square bottom corners: + + + +#### `clip-to-geometry` + +<sup>Since: 0.1.6</sup> + +Clips the window to its visual geometry. + +This will cut out any client-side window shadows, and also round window corners according to `geometry-corner-radius`. + + + +```kdl +window-rule { + clip-to-geometry true +} +``` + +Enable border, set [`geometry-corner-radius`](#geometry-corner-radius) and `clip-to-geometry`, and you've got a classic setup: + + + +```kdl +prefer-no-csd + +layout { + focus-ring { + off + } + + border { + width 2 + } +} + +window-rule { + geometry-corner-radius 12 + clip-to-geometry true +} +``` + +#### `tiled-state` + +<sup>Since: 25.05</sup> + +Informs the window that it is tiled. +Usually, windows will react by becoming rectangular and hiding their client-side shadows. +Windows that snap their size to a grid (e.g. terminals like [foot](https://codeberg.org/dnkl/foot)) will usually disable this snapping when they are tiled. + +By default, niri will set the tiled state to `true` together with [`prefer-no-csd`](./Configuration:-Miscellaneous.md#prefer-no-csd) in order to improve behavior for apps that don't support server-side decorations. +You can use this window rule to override this, for example to get rectangular windows with CSD. + +```kdl +// Make tiled windows rectangular while using CSD. +window-rule { + match is-floating=false + + tiled-state true +} +``` + +#### `baba-is-float` + +<sup>Since: 25.02</sup> + +Make your windows FLOAT up and down. + +This is an April Fools' 2025 feature. + +```kdl +window-rule { + match is-floating=true + + baba-is-float true +} +``` + +<video controls src="https://github.com/user-attachments/assets/3f4cb1a4-40b2-4766-98b7-eec014c19509"> + +https://github.com/user-attachments/assets/3f4cb1a4-40b2-4766-98b7-eec014c19509 + +</video> + +#### Size Overrides + +You can amend the window's minimum and maximum size in logical pixels. + +Keep in mind that the window itself always has a final say in its size. +These values instruct niri to never ask the window to be smaller than the minimum you set, or to be bigger than the maximum you set. + +> [!NOTE] +> `max-height` will only apply to automatically-sized windows if it is equal to `min-height`. +> Either set it equal to `min-height`, or change the window height manually after opening it with `set-window-height`. +> +> This is a limitation of niri's window height distribution algorithm. + +```kdl +window-rule { + min-width 100 + max-width 200 + min-height 300 + max-height 300 +} +``` + +```kdl +// Fix OBS with server-side decorations missing a minimum width. +window-rule { + match app-id=r#"^com\.obsproject\.Studio$"# + + min-width 876 +} +``` |
