# 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: ```toml [[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 `ClientMatch` specifying 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/comm` value. `exe` / `exe-regex` : The client's `/proc/pid/exe` path. ## Window Rules Window rules operate on individual windows. They are defined with `[[windows]]` entries: ```toml [[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 `WindowMatch` specifying 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-focus` : `true`/`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-mapped` : `true` for 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: ```toml [[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: ```toml [[windows]] match.any = [ { title = "chromium" }, { title = "spotify" }, ] ``` ### NOT (Negation) ```toml [[windows]] match.not.title = "firefox" ``` ### ALL (Explicit AND) ```toml [[windows]] match.all = [ { title-regex = "chro" }, { title-regex = "mium" }, ] ``` ### EXACTLY (N of M) Match if exactly N of the listed criteria match: ```toml [[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: ```toml [[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. - **`action`** fires each time a window transitions from not-matching to matching. - **`latch`** fires 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: ```toml [[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 ```toml [[windows]] match.app-id = "pavucontrol" initial-tile-state = "floating" ``` ### Move a Specific App to a Workspace ```toml [[windows]] match.client.sandbox-app-id = "com.spotify.Client" action = { type = "move-to-workspace", name = "3" } ``` ### Float Splash Screens Without Stealing Focus ```toml [[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 ```toml [[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 ```toml [[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: ```shell ~$ jay tree query select-window ``` Example output: ```text - 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: ```shell ~$ 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](https://github.com/mahkoh/jay/blob/master/toml-spec/spec/spec.generated.md) for the full specification of `WindowRule`, `WindowMatch`, `ClientRule`, `ClientMatch`, and all available actions.