8.3 KiB
Window & Client Rules
Jay supports powerful, reactive rules for controlling how clients connect and
how windows behave. Rules are defined in the [[clients]] and [[windows]]
arrays in your configuration file.
Client Rules
Client rules operate on Wayland clients (processes). They are defined with
[[clients]] entries:
[[clients]]
match.exe = "/usr/bin/firefox"
action = { type = "exec", exec = ["notify-send", "Firefox connected"] }
Structure
Each client rule can have the following fields:
name- A name for cross-referencing this rule from other rules.
match- A
ClientMatchspecifying which clients this rule applies to. action- An action to run when a client starts matching.
latch- An action to run when a client stops matching.
Client Match Criteria
All client match criteria are constant over the lifetime of a client. If no fields are set, all clients are matched. If multiple fields are set, they are implicitly AND-combined.
sandboxed- Whether the client is sandboxed (
true/false). sandbox-engine/sandbox-engine-regex- The sandbox engine (e.g.
org.flatpak). sandbox-app-id/sandbox-app-id-regex- The app ID provided by the sandbox.
sandbox-instance-id/sandbox-instance-id-regex- The instance ID from the sandbox.
uid- The user ID of the client.
pid- The process ID of the client.
is-xwayland- Whether the client is Xwayland (
true/false). comm/comm-regex- The client's
/proc/pid/commvalue. exe/exe-regex- The client's
/proc/pid/exepath.
Window Rules
Window rules operate on individual windows. They are defined with [[windows]]
entries:
[[windows]]
match.app-id = "org.gnome.Nautilus"
initial-tile-state = "floating"
Structure
Each window rule can have the following fields:
name- A name for cross-referencing this rule from other rules.
match- A
WindowMatchspecifying which windows this rule applies to. action- An action to run when a window starts matching.
latch- An action to run when a window stops matching.
initial-tile-state"floating"or"tiled"-- force the initial tile state.auto-focustrue/false-- whether the window gets focus on map. Without a matching rule, newly mapped windows always receive focus (except for Xwayland override-redirect windows such as menus and tooltips, which bypass the normal mapping path).
The initial-tile-state and auto-focus fields are ad-hoc properties.
They are evaluated synchronously during the mapping process (before the window
is first displayed), unlike action which runs asynchronously after mapping.
If multiple rules match and any sets auto-focus to false, the window will
not be focused.
Window Match Criteria
If no fields are set, all windows are matched. If multiple fields are set, they
are implicitly AND-combined. Without a types criterion, rules only match
client-created windows (XDG toplevels and X windows).
types- Window type mask:
none,any,container,xdg-toplevel,x-window,client-window. client- A nested
ClientMatch-- matches the window's owning client. title/title-regex- The window title.
app-id/app-id-regex- The XDG app ID.
floating- Whether the window is floating.
visible- Whether the window is visible.
urgent- Whether the window has the urgency flag.
focused- Whether the window has keyboard focus.
fullscreen- Whether the window is fullscreen.
just-mappedtruefor one compositor iteration after the window maps.tag/tag-regex- The XDG toplevel tag.
x-class/x-class-regex- The X11 class (X windows only).
x-instance/x-instance-regex- The X11 instance (X windows only).
x-role/x-role-regex- The X11 role (X windows only).
workspace/workspace-regex- The workspace the window is on.
content-types- Content type mask:
none,any,photo,video,game.
Combining Criteria
All match objects (both ClientMatch and WindowMatch) support the same
logical combinators.
AND (Multiple Fields)
Multiple fields in one match table are implicitly AND-combined:
[[windows]]
match.title = "VIM"
match.app-id = "Alacritty"
This matches only windows whose title is VIM and whose app ID is
Alacritty.
OR (Array of Matchers)
An array of match objects matches if any element matches:
[[windows]]
match.any = [
{ title = "chromium" },
{ title = "spotify" },
]
NOT (Negation)
[[windows]]
match.not.title = "firefox"
ALL (Explicit AND)
[[windows]]
match.all = [
{ title-regex = "chro" },
{ title-regex = "mium" },
]
EXACTLY (N of M)
Match if exactly N of the listed criteria match:
[[windows]]
match.exactly.num = 1
match.exactly.list = [
{ title = "VIM" },
{ client.sandboxed = true },
]
Cross-referencing Rules by Name
Rules can reference other rules by name:
[[windows]]
name = "spotify-windows"
match.client.sandbox-app-id = "com.spotify.Client"
[[windows]]
match.name = "spotify-windows"
action = "enter-fullscreen"
Reactive Behavior
Rules are re-evaluated dynamically whenever any referenced criterion
changes. For example, if a rule matches on title, it is re-checked every time
the window title changes.
actionfires each time a window transitions from not-matching to matching.latchfires each time a window transitions from matching to not-matching.
just-mapped
If you only want a rule to fire once when a window first appears, add
just-mapped = true to the match:
[[windows]]
match.title = "VIM"
match.just-mapped = true
action = "enter-fullscreen"
This is similar to the initial-title criterion found in some other
compositors.
Loop Protection
Rules can trigger each other. For example, one rule could fullscreen a window while another exits fullscreen on the same condition, creating a loop. Jay prevents such loops from locking up the compositor by capping action callbacks at 1000 iterations before yielding to other work. However, such loops will still cause 100% CPU usage and will likely cause affected clients to be killed, since they won't be able to receive Wayland messages fast enough.
Practical Examples
Force an App to Start Floating
[[windows]]
match.app-id = "pavucontrol"
initial-tile-state = "floating"
Move a Specific App to a Workspace
[[windows]]
match.client.sandbox-app-id = "com.spotify.Client"
action = { type = "move-to-workspace", name = "3" }
Float Splash Screens Without Stealing Focus
[[windows]]
match.any = [
{ title = "GIMP Startup", app-id = "gimp" },
{
title = "splash",
x-class-regex = "^jetbrains-(clion|rustrover)$",
},
]
initial-tile-state = "floating"
auto-focus = false
Run a Command When a Window Appears
[[windows]]
match.app-id = "firefox"
match.just-mapped = true
action = {
type = "exec",
exec = ["notify-send", "Firefox window opened"],
}
Suppress Focus Stealing for Chromium Screen-Share Windows
[[windows]]
match.title-regex = 'is sharing (your screen|a window)\.$'
match.client.comm = "chromium"
initial-tile-state = "floating"
auto-focus = false
Introspection
Jay provides several ways to discover the property values you need for writing rules.
jay tree
Interactively select a window and print its properties:
~$ jay tree query select-window
Example output:
- xdg-toplevel:
id: 258ae697663a1b8abc7e4da9570ad36f
pos: 1920x36 + 1920x1044
client:
id: 15
uid: 1000
pid: 2159136
comm: chromium
exe: /usr/lib/chromium/chromium
title: YouTube - Chromium
app-id: chromium
workspace: 2
visible
jay clients
Inspect the client owning a window:
~$ jay clients show select-window
Control Center Window Search
The control center (opened with alt-c by default, or jay control-center)
includes a Window Search pane where you can search and filter windows using
composable criteria -- helpful for experimenting with match expressions before
putting them in your config.
See spec.generated.md for the full
specification of WindowRule, WindowMatch, ClientRule, ClientMatch, and
all available actions.