1
0
Fork 0
forked from wry/wry

docs: add book

This commit is contained in:
Claude 2026-03-20 19:14:07 +01:00 committed by Julian Orth
parent c9d6fb9e40
commit d14105eb1a
43 changed files with 7254 additions and 1204 deletions

40
.github/workflows/publish-page.yml vendored Normal file
View file

@ -0,0 +1,40 @@
name: publish-page
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Install mdbook
run: |
VERSION=0.5.2
URL=https://github.com/rust-lang/mdBook/releases/download/v${VERSION}/mdbook-v${VERSION}-x86_64-unknown-linux-gnu.tar.gz
mkdir mdbook
curl -sSL $URL | tar -xz --directory=mdbook
- name: Create root
run: |
mkdir page
- name: Build book
run: |
cd book
../mdbook/mdbook build
mv book ../page
- uses: actions/upload-pages-artifact@v4
with:
path: page
publish:
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
permissions:
pages: write
id-token: write
steps:
- uses: actions/deploy-pages@v4

View file

@ -2,21 +2,24 @@
[![crates.io](https://img.shields.io/crates/v/jay-compositor.svg)](http://crates.io/crates/jay-compositor)
Jay is a Wayland compositor.
Jay is a Wayland compositor for Linux with an i3-like tiling layout,
Vulkan and OpenGL rendering, multi-GPU support, screen sharing, and more.
![screenshot.png](static/screenshot.png)
## Features
## Quick Start
See [features.md](./docs/features.md).
```shell
~$ cargo install --locked jay-compositor
~$ jay run
```
## Configuration
See the **[Jay Book](https://mahkoh.github.io/jay/book)** for detailed
installation instructions (including dependencies), configuration,
features, and more.
See [config.md](./docs/config.md).
## Building and Setup
See [setup.md](./docs/setup.md).
The auto-generated [Configuration Spec](./toml-spec/spec/spec.generated.md)
provides an exhaustive reference of every TOML config option.
## License

1
book/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/book

293
book/AGENTS.md Normal file
View file

@ -0,0 +1,293 @@
# Jay Book -- Agent Instructions
This file provides context for AI agents working on the Jay user documentation
book (mdbook). Read this before making changes.
## What is this?
An [mdbook](https://rust-lang.github.io/mdBook/) providing comprehensive
user-facing documentation for the [Jay Wayland compositor](https://github.com/mahkoh/jay).
The target audience is end users, not developers. The goal is feature
discoverability: every capability Jay offers should be documented here in a
way that users can find and understand.
## Current status
The book is feature-complete. All 29 chapters cover every known Jay feature:
a features overview, installation (including AUR packages), running, the
control center, all configuration sections, tiling, workspaces, floating
windows, mouse interactions, input modes, window/client rules, screen sharing,
HDR & color management, the full CLI reference, and troubleshooting.
Three rounds of source-code verification have been completed and all
identified issues have been fixed. The book should be accurate against the
codebase as of the latest review.
A subsequent style and formatting pass has been completed:
- Converted ~45 two-column tables to mdbook definition lists across 16 files.
- Lowercased all keyboard shortcut modifiers to match TOML parser expectations.
- Reformatted long inline TOML tables into multiline format with trailing
commas and 4-space indentation.
- Created a dedicated Features chapter (`features.md`) consolidating content
from the old `docs/features.md` and the introduction; removed the duplicated
feature list from `introduction.md`.
- Added a comprehensive "Granting Privileges" section to `window-rules.md` and
privilege troubleshooting to `troubleshooting.md`.
- Clarified `jay unlock` usage in `configuration/idle.md` and `cli.md`.
- Added AUR package (`jay`, `jay-git`) mentions to `installation.md`.
- Added a dedicated HDR & Color Management chapter (`hdr.md`) with
end-to-end walkthrough, cross-referenced from `features.md`, `outputs.md`,
and `misc.md`.
- Integrated remaining information from `docs/` and `README.md` that was
missing from the book: `JAY_NO_REALTIME` env var and `config.so`/SCHED_RR
interaction in `installation.md`, rule loop client-kill consequences and
Xwayland override-redirect auto-focus exception in `window-rules.md`,
license (GPLv3) and community Discord link in `introduction.md`, ArchWiki
XKB link in `keymaps.md`.
A third review pass fixed:
- `window-rules.md`: exec action example used a plain string with spaces
(`exec = "notify-send 'Firefox connected'"`) which would be treated as a
single program name; changed to array form
(`exec = ["notify-send", "Firefox connected"]`).
- `window-rules.md`: default capabilities paragraph incorrectly said rule
capabilities are "added on top" of defaults; corrected to say they
**replace** defaults entirely.
- `troubleshooting.md`: said Jay "requires at least one working renderer to
start"; corrected to say that without one, no GPU can be initialized and
nothing will be displayed (Jay itself still starts).
- `outputs.md`: VRR variant3 description said "describes its content as"
instead of the correct protocol term "describes its content type as".
**Future work might include:**
- Keeping the book in sync as Jay adds new features or changes behavior.
- Adding screenshots or diagrams (especially for the control center and tiling).
- Reviewing after spec.yaml changes (the spec is the canonical source for
config options).
## Directory layout
```
book/
AGENTS.md -- this file
book.toml -- mdbook configuration
src/
SUMMARY.md -- table of contents (defines chapter ordering)
introduction.md
features.md -- feature overview, protocol table, CLI help output
installation.md -- deps, AUR, crates.io, git builds, CAP_SYS_NICE
running.md -- includes control center intro and default keybindings
control-center.md -- detailed tour of all 11 panes
configuration/
index.md -- config overview, jay config init, file semantics, auto-reload
keymaps.md
shortcuts.md
outputs.md
inputs.md
gpu.md
idle.md
theme.md
environment.md
status-bar.md
startup.md
xwayland.md
misc.md -- color management, libei, floating, ui-drag, etc.
tiling.md
workspaces.md
floating.md
mouse.md
input-modes.md
window-rules.md -- includes "Granting Privileges" section
cli.md
screen-sharing.md
hdr.md -- HDR & color management walkthrough
troubleshooting.md -- includes privilege troubleshooting
```
## Source of truth
The authoritative source for what Jay supports is the source code itself. Key
reference files:
- `toml-spec/spec/spec.yaml` -- Full TOML config specification. Every config
key, action, match criterion, and type is defined here. The auto-generated
`toml-spec/spec/spec.generated.md` is derived from this file.
- `toml-config/src/default-config.toml` -- The built-in default configuration.
Contains default keybindings, startup actions (mako, wl-tray-bridge), etc.
- `src/cli/*.rs` -- CLI subcommands and their arguments (clap definitions).
Jay has 24 CLI subcommands with nesting up to 4 levels deep.
- `src/control_center/*.rs` -- Control center pane implementations. Each pane
has its own file (cc_outputs.rs, cc_gpus.rs, cc_input.rs, cc_clients.rs,
cc_window.rs, cc_idle.rs, cc_compositor.rs, cc_look_and_feel.rs,
cc_xwayland.rs, cc_color_management.rs, cc_virtual_outputs.rs). When
documenting control center fields, always verify against these source files
for correct ordering, labels, and conditional visibility.
- `toml-config/src/config/parsers/color.rs` -- Color parser. Accepts 3, 4, 6,
or 8 hex digits (`#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`). Note: spec.yaml
says `#rrggbba` (7 digits) which is a typo -- the actual format is 8 digits.
- `toml-config/src/config/parsers/exec.rs` -- Exec parser. Accepts string,
array, or table. Understanding these three forms is important for writing
correct TOML examples.
- `src/config/handler.rs` -- Contains `update_capabilities`, which shows that
client rule capabilities **replace** defaults (accumulator starts from zero).
- The `docs/` directory has been removed. All of its content has been
integrated into the book. The README now points to the hosted book at
`https://mahkoh.github.io/jay/book`.
### Known spec.yaml bugs
- `px-per-wheel-scroll` is listed as `kind: boolean` but the parser
(`toml-config/src/config/parsers/input.rs`) uses `fltorint` (a number).
The documentation correctly describes it as a numeric value.
## Critical facts to get right
1. **Config replacement semantics.** Once `~/.config/jay/config.toml` exists,
the entire built-in default configuration is replaced -- not merged. Even an
empty config file means no shortcuts, no startup actions, nothing. Users must
run `jay config init` to get a config pre-populated with the defaults.
2. **Config reload.** By default, Jay does not automatically reload
config.toml. Reload must be triggered manually via the `alt-shift-r`
shortcut (default) or the `reload-config-toml` action. However, setting
`auto-reload = true` in config.toml enables inotify-based file watching
with a 400 ms debounce (parsed in
`toml-config/src/config/parsers/config.rs:209`, acted on in
`toml-config/src/lib.rs:1348-1358`).
3. **Renderer requirement.** Jay requires at least one working renderer
(OpenGL via libEGL+libGLESv2, or Vulkan via libvulkan + a GPU driver).
Without one, no GPU can be initialized and nothing will be displayed. (Jay
itself still starts -- the metal backend initializes logind, udev, and
libinput successfully -- but no DRM devices can be set up, so the user sees
a black screen.) These libraries are loaded at runtime, not linked at build
time. Vulkan is the recommended renderer; OpenGL is legacy and receives no
new features.
4. **Actions are composable.** Anywhere an action is accepted, an array of
actions can be used instead. Named actions (`$name`) add another layer of
reuse.
5. **Match systems.** Outputs, connectors, DRM devices, inputs, clients, and
windows each have their own match system. All support the same logical
combinators: AND (multiple fields in one table), OR (array of matchers),
`not`, `all`, `any`, `exactly`.
6. **Input modes.** Jay supports an input mode stack (`push-mode`,
`latch-mode`, `pop-mode`, `clear-modes`). Each mode can define its own
shortcuts and inherit from a parent mode. This is how vim-style modal
keybindings are implemented. Note: there are NO `resize-left/right/up/down`
actions -- resizing is mouse-only. Use `move-*` actions in examples.
7. **Window/client rules are reactive.** Rules are re-evaluated whenever the
matching criteria change (e.g. a window's title changes). The `latch` action
fires when a previously-matching rule stops matching.
8. **Control center.** Opened with `alt-c` (default) or `jay control-center`.
It is an egui-based GUI with 11 panes: Clients, Color Management,
Compositor, GPUs, Idle, Input, Look and Feel, Outputs (with visual
arrangement editor), Virtual Outputs, Window Search, and Xwayland.
9. **VRR and tearing.** Both have per-output overrides and multiple mode
variants (never, always, variant1/2/3). Important distinction: VRR
variant1/2 use "fullscreen" as the criterion. Tearing variant3 says "a
single application is displayed" (not necessarily fullscreen) -- check the
spec.yaml wording carefully.
10. **Input device type flags.** The full list is: `is-keyboard`, `is-pointer`,
`is-touch`, `is-tablet-tool`, `is-tablet-pad`, `is-gesture`, `is-switch`.
Note that `is-switch` exists in the parser but is missing from spec.yaml.
11. **Features chapter is the single source for the feature list.** The
introduction links to `features.md` but does not duplicate any feature
bullets. All feature descriptions, the protocol support table, and CLI help
output live in `features.md` only.
12. **Privilege separation.** Jay supports granting elevated Wayland protocol
access via three methods: `privileged = true` in window rules with
`jay run-privileged`, connection tags with client rules, and client match
rules by PID/sandboxing. This is documented in the "Granting Privileges"
section of `window-rules.md`. **Important:** when any client rule matches,
its capabilities **replace** the defaults entirely (they are not added on
top). Multiple matching rules are unioned together, but the defaults
(`layer-shell | drm-lease` for unsandboxed, `drm-lease` for sandboxed)
are only used when no rule matches at all.
13. **Exec action formats.** The `exec` field in exec actions accepts three
forms: a plain string (program name only, no arguments), an array of
strings (first element is the program, rest are arguments), or a table
with `prog`/`shell`, `args`, `env`, `privileged`, and `tag` fields. A
common mistake is using a plain string with spaces like
`exec = "notify-send 'hello'"` -- this treats the entire string as the
program name. Use the array form instead:
`exec = ["notify-send", "hello"]`.
## Style guidelines
- Write for users, not developers. Explain what things do, not how they are
implemented.
- Use ` ```shell ` for shell commands, prefixed with `~$` and `sudo` where
appropriate.
- Use ` ```toml ` for TOML configuration examples.
- Use **mdbook admonitions** (`> [!NOTE]`, `> [!TIP]`, `> [!WARNING]`,
`> [!IMPORTANT]`) instead of `> **Note:**` blockquote patterns. mdbook
0.5.2+ is installed and supports this syntax.
- **Keyboard shortcuts** must be enclosed in backticks and use **lowercase
modifiers** -- this matches the format the TOML parser expects. Keysym names
retain their original case. Examples: `alt-shift-c`, `ctrl-alt-F2`,
`alt-Return`. Never write `Alt-Shift-c` or `Alt+C`.
- **Tables vs definition lists.** Use mdbook definition lists (`Term\n:
Description`) for two-column term-to-description mappings. Keep true
grid-like tables only when they have 3+ meaningful data columns (e.g.,
package names per distro, the protocol support table with version numbers).
- **TOML formatting.** Break long inline TOML tables over multiple lines with
trailing commas (TOML 1.1 syntax). Use 4-space indentation inside TOML code
blocks, never 2-space.
- Keep examples practical -- show real use cases, not abstract syntax.
- Link to the auto-generated spec (`spec.generated.md`) for exhaustive field
listings. The book should teach concepts and workflows; the spec is the
complete reference.
- Each chapter should be self-contained enough to be useful on its own, but
cross-reference related chapters where helpful.
- Avoid duplicating large tables of every possible value. Summarize, give
examples, and link to the spec for the full list.
- When documenting the control center, always verify field names, ordering,
and conditional visibility against the `src/control_center/cc_*.rs` source
files. UI labels must match exactly (case-sensitive).
## Verification methodology
When making changes, verify against source code. The most error-prone areas
are:
1. **Control center pane documentation** -- Field names, ordering, and
conditional visibility must match the `show_*` functions in the cc_*.rs
files. Fields are often conditional (only shown for certain device types or
when certain values are set).
2. **VRR/tearing mode descriptions** -- The exact semantics differ subtly
between modes and between VRR vs tearing. Always check spec.yaml wording.
3. **Exec action examples** -- A plain string is the program name only (no
argument splitting). Use arrays or tables when arguments are needed.
4. **Capability/privilege semantics** -- Capabilities from matching rules
replace defaults; they do not add to them. Verify against
`src/config/handler.rs:update_capabilities` if in doubt.
## Building the book
```shell
~$ cd book
~$ mdbook build # outputs to book/book/
~$ mdbook serve # local preview at http://localhost:3000
```
Requires [mdbook](https://github.com/rust-lang/mdBook) to be installed:
```shell
~$ cargo install mdbook
```

7
book/book.toml Normal file
View file

@ -0,0 +1,7 @@
[book]
title = "The Jay Book"
language = "en"
src = "src"
[output.html]
git-repository-url = "https://github.com/mahkoh/jay"

45
book/src/SUMMARY.md Normal file
View file

@ -0,0 +1,45 @@
# Summary
[Introduction](introduction.md)
# Overview
- [Features](features.md)
# Getting Started
- [Installation](installation.md)
- [Running Jay](running.md)
- [Control Center](control-center.md)
# Configuration
- [Configuration Overview](configuration/index.md)
- [Keymaps & Repeat Rate](configuration/keymaps.md)
- [Shortcuts](configuration/shortcuts.md)
- [Startup Actions](configuration/startup.md)
- [Environment Variables](configuration/environment.md)
- [Outputs (Monitors)](configuration/outputs.md)
- [Input Devices](configuration/inputs.md)
- [GPUs](configuration/gpu.md)
- [Idle & Screen Locking](configuration/idle.md)
- [Theme & Appearance](configuration/theme.md)
- [Status Bar](configuration/status-bar.md)
- [Xwayland](configuration/xwayland.md)
- [Miscellaneous](configuration/misc.md)
# Using Jay
- [Tiling](tiling.md)
- [Workspaces](workspaces.md)
- [Floating Windows](floating.md)
- [Mouse Interactions](mouse.md)
- [Input Modes](input-modes.md)
- [Window & Client Rules](window-rules.md)
- [Screen Sharing](screen-sharing.md)
- [HDR & Color Management](hdr.md)
# Reference
- [Command-Line Interface](cli.md)
- [Troubleshooting](troubleshooting.md)

663
book/src/cli.md Normal file
View file

@ -0,0 +1,663 @@
# Command-Line Interface
Jay provides a comprehensive CLI for controlling the compositor, managing
displays, input devices, clients, and more. All subcommands communicate with
the running compositor over the Wayland protocol unless otherwise noted.
> [!TIP]
> Generate shell completions for tab-completion support:
>
> ```shell
> ~$ jay generate-completion bash > ~/.local/share/bash-completion/completions/jay
> ~$ jay generate-completion zsh > ~/.zfunc/_jay
> ~$ jay generate-completion fish > ~/.config/fish/completions/jay.fish
> ~$ jay generate-completion elvish # pipe to appropriate location
> ~$ jay generate-completion powershell # pipe to appropriate location
> ```
Every subcommand accepts a global `--log-level` option (`trace`, `debug`,
`info`, `warn`, `error`, `off`) that controls the verbosity of the CLI tool itself.
---
## Running
### `jay run`
Start the compositor.
```shell
~$ jay run
```
Optionally specify which backends to try (comma-separated, tried in order):
```shell
~$ jay run --backends x11,metal,headless
```
The default order is `x11,metal`. The first backend that can be started is
used. `metal` is the native DRM/KMS backend for bare-metal sessions.
### `jay quit`
Stop the running compositor.
```shell
~$ jay quit
```
### `jay pid`
Print the PID of the running compositor.
```shell
~$ jay pid
```
### `jay version`
Print the Jay version.
```shell
~$ jay version
```
---
## Configuration
### `jay config init`
Generate a configuration file pre-populated with all defaults.
```shell
~$ jay config init
```
If a config already exists, pass `--overwrite` to replace it (the old file is
backed up):
```shell
~$ jay config init --overwrite
```
> [!IMPORTANT]
> Once a config file exists, the entire built-in default configuration is
> replaced -- not merged. An empty config file means no shortcuts, no startup
> actions, nothing. Always use `jay config init` to start with a working
> configuration.
### `jay config path`
Print the path to the config file.
```shell
~$ jay config path
```
### `jay config open-dir`
Open the configuration directory with `xdg-open`.
```shell
~$ jay config open-dir
```
---
## Display & Graphics
All display and GPU commands live under `jay randr`.
### Showing Current Settings
```shell
~$ jay randr
~$ jay randr show
~$ jay randr show --modes # include all available modes
~$ jay randr show --formats # include all available framebuffer formats
```
### GPU / Card Commands
Set the primary (render) GPU:
```shell
~$ jay randr card card0 primary
```
Set the graphics API:
```shell
~$ jay randr card card0 api vulkan
~$ jay randr card card0 api opengl
```
Toggle direct scanout:
```shell
~$ jay randr card card0 direct-scanout enable
~$ jay randr card card0 direct-scanout disable
```
Adjust the page-flip margin (in milliseconds, default 1.5):
```shell
~$ jay randr card card0 timing set-flip-margin 1.5
```
### Output Commands
Enable or disable an output:
```shell
~$ jay randr output DP-1 enable
~$ jay randr output DP-1 disable
```
Set the position:
```shell
~$ jay randr output DP-1 position 1920 0
```
Set the scale (fractional scaling supported):
```shell
~$ jay randr output DP-1 scale 1.5
~$ jay randr output DP-1 scale --round-to-float 1.333
```
Set the mode (width, height, refresh rate in Hz):
```shell
~$ jay randr output DP-1 mode 2560 1440 144.0
```
Set the transform:
```shell
~$ jay randr output DP-1 transform rotate-90
~$ jay randr output DP-1 transform none
```
Available transforms: `none`, `rotate-90`, `rotate-180`, `rotate-270`, `flip`,
`flip-rotate-90`, `flip-rotate-180`, `flip-rotate-270`.
Override the non-desktop setting:
```shell
~$ jay randr output DP-1 non-desktop true
~$ jay randr output DP-1 non-desktop default
```
Configure VRR (variable refresh rate):
```shell
~$ jay randr output DP-1 vrr set-mode always
~$ jay randr output DP-1 vrr set-mode variant3
~$ jay randr output DP-1 vrr set-cursor-hz 240
~$ jay randr output DP-1 vrr set-cursor-hz none
```
VRR modes: `never`, `always`, `variant1` (one or more fullscreen apps),
`variant2` (single fullscreen app), `variant3` (single fullscreen app with
game/video content type). Pass `none` to `set-cursor-hz` to remove the cursor
Hz cap.
Configure tearing:
```shell
~$ jay randr output DP-1 tearing set-mode variant3
```
Tearing modes: `never`, `always`, `variant1` (one or more applications
displayed fullscreen), `variant2` (single application displayed fullscreen),
`variant3` (default -- single displayed application that requested tearing).
Set the framebuffer format:
```shell
~$ jay randr output DP-1 format set xrgb8888
```
Set the output color space and EOTF (for HDR):
```shell
~$ jay randr output DP-1 colors set bt2020 pq
~$ jay randr output DP-1 colors set default default
```
Set the brightness (in cd/m^2, or `default`):
```shell
~$ jay randr output DP-1 brightness 203
~$ jay randr output DP-1 brightness default
```
Set the blend space:
```shell
~$ jay randr output DP-1 blend-space linear
~$ jay randr output DP-1 blend-space srgb
```
Enable or disable native gamut usage:
```shell
~$ jay randr output DP-1 use-native-gamut true
~$ jay randr output DP-1 use-native-gamut false
```
### Virtual Outputs
```shell
~$ jay randr virtual-output create my-virtual-display
~$ jay randr virtual-output remove my-virtual-display
```
---
## Input
All input commands live under `jay input`.
### Showing Input Devices
```shell
~$ jay input
~$ jay input show
~$ jay input show -v # verbose output with device details
```
### Seat Commands
Show seat information:
```shell
~$ jay input seat default show
~$ jay input seat default show -v
```
Set keyboard repeat rate (repeats per second, initial delay in ms):
```shell
~$ jay input seat default set-repeat-rate 25 600
```
Set the keymap from RMLVO names:
```shell
~$ jay input seat default set-keymap-from-names -l de
~$ jay input seat default set-keymap-from-names -l us -v intl -o compose:ralt
```
Set the keymap from a file (or stdin):
```shell
~$ jay input seat default set-keymap /path/to/keymap.xkb
```
Retrieve the current keymap:
```shell
~$ jay input seat default keymap > current.xkb
```
Toggle hardware cursor:
```shell
~$ jay input seat default use-hardware-cursor true
~$ jay input seat default use-hardware-cursor false
```
Set cursor size:
```shell
~$ jay input seat default set-cursor-size 24
```
Configure the simple (XCompose-based) input method:
```shell
~$ jay input seat default simple-im enable
~$ jay input seat default simple-im disable
~$ jay input seat default simple-im reload
```
### Device Commands
Show device information:
```shell
~$ jay input device 42 show
```
Set acceleration profile and speed:
```shell
~$ jay input device 42 set-accel-profile flat
~$ jay input device 42 set-accel-profile adaptive
~$ jay input device 42 set-accel-speed -0.5
```
Configure tap behavior:
```shell
~$ jay input device 42 set-tap-enabled true
~$ jay input device 42 set-tap-drag-enabled true
~$ jay input device 42 set-tap-drag-lock-enabled false
```
Set left-handed mode:
```shell
~$ jay input device 42 set-left-handed true
```
Set natural scrolling:
```shell
~$ jay input device 42 set-natural-scrolling true
```
Set pixels per scroll-wheel step:
```shell
~$ jay input device 42 set-px-per-wheel-scroll 30.0
```
Set the transform matrix (2x2):
```shell
~$ jay input device 42 set-transform-matrix 1.0 0.0 0.0 1.0
```
Set the calibration matrix (2x3, for touchscreens):
```shell
~$ jay input device 42 set-calibration-matrix 1.0 0.0 0.0 0.0 1.0 0.0
```
Set the click method:
```shell
~$ jay input device 42 set-click-method clickfinger
~$ jay input device 42 set-click-method button-areas
~$ jay input device 42 set-click-method none
```
Toggle middle button emulation:
```shell
~$ jay input device 42 set-middle-button-emulation true
```
Set a per-device keymap:
```shell
~$ jay input device 42 set-keymap /path/to/keymap.xkb
~$ jay input device 42 set-keymap-from-names -l de
~$ jay input device 42 keymap > device.xkb
```
Attach / detach a device from a seat:
```shell
~$ jay input device 42 attach default
~$ jay input device 42 detach
```
Map a device to a specific output:
```shell
~$ jay input device 42 map-to-output DP-1
~$ jay input device 42 remove-mapping
```
---
## Idle & Locking
### `jay idle`
Show idle status, including the current interval, grace period, and active
inhibitors:
```shell
~$ jay idle
~$ jay idle status
```
### `jay idle set`
Set the idle interval. Durations can be specified in flexible formats:
```shell
~$ jay idle set 10m
~$ jay idle set 1m 30s
~$ jay idle set disabled
```
### `jay idle set-grace-period`
Set the grace period (screens go black but are not locked/disabled):
```shell
~$ jay idle set-grace-period 30s
~$ jay idle set-grace-period disabled
```
### `jay unlock`
Unlock the compositor. This is useful when the screen locker crashes and the
session remains locked. Run it from another TTY or via SSH. You must set
`WAYLAND_DISPLAY` to the socket of the Jay compositor:
```shell
~$ WAYLAND_DISPLAY=wayland-1 jay unlock
```
---
## Logging
### `jay log`
Open the log file in `less`:
```shell
~$ jay log
~$ jay log -f # follow mode (like tail -f)
~$ jay log -e # jump to end of log
~$ jay log --path # print the log file path instead
```
### `jay set-log-level`
Change the log level at runtime:
```shell
~$ jay set-log-level debug
~$ jay set-log-level info
```
Available levels: `trace`, `debug`, `info`, `warn`, `error`, `off`.
---
## Screenshots
### `jay screenshot`
Take a screenshot of the entire display:
```shell
~$ jay screenshot
~$ jay screenshot --format png
~$ jay screenshot --format qoi
~$ jay screenshot my-screenshot.png
```
If no filename is given, the screenshot is saved as
`%Y-%m-%d-%H%M%S_jay.<ext>` in the current directory. The filename supports
strftime format specifiers.
---
## Clients & Windows
### `jay clients`
List all connected clients:
```shell
~$ jay clients
~$ jay clients show all
```
Show a specific client by ID:
```shell
~$ jay clients show id 42
```
Interactively select a window and show its client:
```shell
~$ jay clients show select-window
```
Kill a client by ID:
```shell
~$ jay clients kill id 42
```
Interactively select a window and kill its client:
```shell
~$ jay clients kill select-window
```
### `jay tree query`
Inspect the compositor's surface tree:
```shell
~$ jay tree query root
~$ jay tree query -r root # recursive
~$ jay tree query -r --all-clients root # show client details for every node
~$ jay tree query workspace-name main
~$ jay tree query select-workspace
~$ jay tree query select-window
```
---
## Xwayland
### `jay xwayland`
Show Xwayland status (scaling mode, implied scale):
```shell
~$ jay xwayland
~$ jay xwayland status
```
### `jay xwayland set-scaling-mode`
```shell
~$ jay xwayland set-scaling-mode default
~$ jay xwayland set-scaling-mode downscaled
```
In `downscaled` mode, X11 windows are rendered at the highest integer scale and
then downscaled, which can improve sharpness on HiDPI displays.
---
## Color Management
### `jay color-management`
Show color management status:
```shell
~$ jay color-management
~$ jay color-management status
```
### `jay color-management enable / disable`
```shell
~$ jay color-management enable
~$ jay color-management disable
```
---
## Other Commands
### `jay control-center`
Open the [Control Center](control-center.md) GUI:
```shell
~$ jay control-center
```
### `jay portal`
Run the Jay desktop portal (provides screen sharing and other XDG desktop
portal interfaces):
```shell
~$ jay portal
```
Normally the portal is started automatically. This command is for running it
manually or debugging.
### `jay seat-test`
Test input events from a seat. Prints all keyboard, pointer, touch, gesture,
tablet, and switch events to stdout:
```shell
~$ jay seat-test
~$ jay seat-test default
~$ jay seat-test -a # test all seats simultaneously
```
### `jay run-privileged`
Run a program with access to a privileged Wayland socket:
```shell
~$ jay run-privileged my-program --arg1
```
### `jay run-tagged`
Run a program with a tagged Wayland connection. All Wayland connections from the
spawned process tree will carry the specified tag, which can be matched in
[client rules](window-rules.md):
```shell
~$ jay run-tagged my-tag firefox
```
### `jay generate-completion`
Generate shell completion scripts:
```shell
~$ jay generate-completion bash
~$ jay generate-completion zsh
~$ jay generate-completion fish
~$ jay generate-completion elvish
~$ jay generate-completion powershell
```

View file

@ -0,0 +1,76 @@
# Environment Variables
Jay can set environment variables that are inherited by all programs it spawns.
## Setting environment variables at startup
Use the `[env]` table in your config to define variables that apply to every
application launched by the compositor:
```toml
[env]
GTK_THEME = "Adwaita:dark"
QT_QPA_PLATFORM = "wayland"
MOZ_ENABLE_WAYLAND = "1"
```
These variables are set when the config is loaded. Programs started after that
point will inherit them.
## Changing variables at runtime
### set-env
Use the `set-env` action to add or update environment variables while the
compositor is running. This affects all programs started **after** the action
runs:
```toml
[shortcuts]
alt-F11 = {
type = "set-env",
env = { GTK_THEME = "Adwaita:dark" },
}
alt-F12 = {
type = "set-env",
env = { GTK_THEME = "Adwaita" },
}
```
### unset-env
Use the `unset-env` action to remove environment variables:
```toml
[shortcuts]
alt-F10 = { type = "unset-env", env = ["GTK_THEME"] }
```
You can unset multiple variables at once with an array:
```toml
[shortcuts]
alt-F10 = {
type = "unset-env",
env = ["GTK_THEME", "QT_QPA_PLATFORM"],
}
```
## Per-process environment variables
When using the table form of the `exec` action, you can set environment
variables that apply only to that specific process:
```toml
[shortcuts]
alt-Return = {
type = "exec",
exec = {
prog = "alacritty",
env = { TERM = "xterm-256color" },
},
}
```
These per-process variables are merged with (and override) the global
environment for that single execution.

View file

@ -0,0 +1,293 @@
# GPUs
Jay configures graphics cards (DRM devices) through the `[[drm-devices]]`
array. This is primarily useful on multi-GPU systems for selecting the render
device, choosing a graphics API, and tuning per-device settings.
> [!NOTE]
> DRM device configuration in `config.toml` is only applied when a device is
> first discovered after the configuration is loaded. To change settings at
> runtime, use `jay randr` or the `configure-drm-device` action.
## Matching GPUs
Every `[[drm-devices]]` entry requires a `match` field. When `match` is a
**table**, all specified fields must match (AND). When `match` is an **array**,
any entry matching is sufficient (OR).
### By PCI vendor and model (recommended)
PCI IDs are stable, unique identifiers. Use `jay randr` to find them:
```shell
~$ jay randr
```
```toml
[[drm-devices]]
match = {
pci-vendor = 0x1002,
pci-model = 0x73ff,
}
gfx-api = "Vulkan"
```
### By vendor or model name
```toml
[[drm-devices]]
match.vendor = "Advanced Micro Devices, Inc. [AMD/ATI]"
```
```toml
[[drm-devices]]
match.model = "Raphael"
```
### By syspath or devnode
The `syspath` is usually stable across reboots:
```toml
[[drm-devices]]
match.syspath = "/sys/devices/pci0000:00/0000:00:08.1/0000:14:00.0"
```
The `devnode` (e.g. `/dev/dri/card0`) is typically not stable.
## Naming GPUs
Assign a `name` to reference a device from other parts of the config:
```toml
[[drm-devices]]
name = "dedicated"
match = {
pci-vendor = 0x1002,
pci-model = 0x73ff,
}
[[drm-devices]]
name = "integrated"
match = {
pci-vendor = 0x1002,
pci-model = 0x164e,
}
```
Names can then be used in `render-device`, shortcuts, and actions:
```toml
render-device.name = "dedicated"
```
```toml
[shortcuts]
alt-v = {
type = "configure-drm-device",
dev = {
match.name = "dedicated",
gfx-api = "Vulkan",
},
}
alt-o = {
type = "configure-drm-device",
dev = {
match.name = "dedicated",
gfx-api = "OpenGl",
},
}
```
## Graphics API
Jay supports two rendering backends per device:
`Vulkan`
: Uses libvulkan. The primary renderer -- use this unless you have a specific
reason not to. Required for HDR. All devices in the system must support DRM
format modifiers (most do, except AMD GPUs older than RX 5000).
`OpenGl`
: Uses libEGL + libGLESv2. Maintained for backwards compatibility only. No new
features will be added to this renderer.
### Per-device API
```toml
[[drm-devices]]
match = {
pci-vendor = 0x1002,
pci-model = 0x73ff,
}
gfx-api = "Vulkan"
```
### Default API for all devices
Set the top-level `gfx-api` to apply to any device without a per-device
override:
```toml
gfx-api = "Vulkan"
```
This only takes effect for devices discovered after the config is loaded.
## Direct scanout
Direct scanout lets the compositor hand a client's buffer directly to the
display hardware, bypassing composition. This can reduce latency and power
usage, but may cause visual glitches with some hardware or applications.
### Per-device
```toml
[[drm-devices]]
match = {
pci-vendor = 0x1002,
pci-model = 0x73ff,
}
direct-scanout = false
```
### Global toggle
```toml
direct-scanout = false
```
## Flip margin
The flip margin is the time (in milliseconds) between the compositor
initiating a page flip and the output's vertical blank event. It determines
the minimum achievable input latency. The default is 1.5 ms.
```toml
[[drm-devices]]
match = {
pci-vendor = 0x1002,
pci-model = 0x73ff,
}
flip-margin-ms = 2.0
```
If the margin is set too small, the compositor will dynamically increase it to
avoid missed frames.
## Explicit sync
Explicit sync coordinates buffer access between the compositor and GPU drivers.
It is enabled by default and generally should not be disabled:
```toml
explicit-sync = true
```
> [!WARNING]
> This setting cannot be changed after the compositor has started. It can only
> be set in `config.toml` before launching Jay.
## Render device
On multi-GPU systems, select which GPU performs compositing with the
`render-device` field. The first matching device is used:
```toml
render-device.name = "dedicated"
[[drm-devices]]
name = "dedicated"
match = {
pci-vendor = 0x1002,
pci-model = 0x73ff,
}
[[drm-devices]]
name = "integrated"
match = {
pci-vendor = 0x1002,
pci-model = 0x164e,
}
gfx-api = "OpenGl"
```
You can also match directly without naming:
```toml
render-device = {
pci-vendor = 0x1002,
pci-model = 0x73ff,
}
```
> [!NOTE]
> Changing the render device at runtime (via the `set-render-device` action)
> may cause windows to become invisible until they are resized or otherwise
> redrawn.
## Runtime changes
### Listing GPUs
```shell
~$ jay randr
```
This shows all DRM devices with their PCI IDs, vendor/model names, current
API, and other settings.
### Changing settings at runtime
Use `jay randr` subcommands:
```shell
~$ jay randr card <card> api vulkan
~$ jay randr card <card> direct-scanout enable
~$ jay randr card <card> primary
```
### Using shortcuts
The `configure-drm-device` action applies settings from a keybinding:
```toml
[shortcuts]
alt-F5 = {
type = "configure-drm-device",
dev = {
match.name = "dedicated",
gfx-api = "Vulkan",
},
}
alt-F6 = {
type = "configure-drm-device",
dev = {
match.name = "dedicated",
gfx-api = "OpenGl",
},
}
```
The `set-render-device` action switches the compositing GPU:
```toml
[shortcuts]
alt-F7 = { type = "set-render-device", dev.name = "dedicated" }
alt-F8 = { type = "set-render-device", dev.name = "integrated" }
```
## Hardware cursor
The `use-hardware-cursor` top-level setting controls whether the hardware cursor
plane is used. Disabling this forces software cursor rendering, which can be
useful for debugging.
```toml
use-hardware-cursor = true # default
```
## Full reference
For the exhaustive list of all DRM device fields, match criteria, and related
types, see the [auto-generated specification](https://github.com/mahkoh/jay/blob/master/toml-spec/spec/spec.generated.md).

View file

@ -0,0 +1,186 @@
# Idle & Screen Locking
Jay can detect when the system is idle and automatically run an action -- most
commonly launching a screen locker. The idle timeout, grace period, and
on-idle action are all configurable.
## Idle timeout
Set how long the system must be idle before the on-idle action fires. Specify
`minutes` and/or `seconds`:
```toml
idle.minutes = 10
```
```toml
idle = { minutes = 5, seconds = 30 }
```
If all values are explicitly set to 0, the idle timeout is disabled entirely:
```toml
idle = { minutes = 0 }
```
> [!NOTE]
> The idle timeout defined in `config.toml` cannot be changed by reloading the
> configuration. Use `jay idle` or the `configure-idle` action to change it at
> runtime.
## Grace period
The grace period is a warning phase between the timeout expiring and the actual
idle event. During the grace period, the screen goes black but outputs are not
yet disabled and the `on-idle` action has not yet fired. Any input during this
period cancels the idle transition.
The default grace period is 5 seconds. Configure it with:
```toml
idle.grace-period.seconds = 3
```
```toml
idle.grace-period = { minutes = 0, seconds = 10 }
```
Set both values to 0 to disable the grace period (immediately fire the on-idle
action when the timeout expires):
```toml
idle.grace-period = { seconds = 0 }
```
## On-idle action
The `on-idle` field defines what happens when the idle timeout (plus grace
period) elapses. The most common use is launching a screen locker:
```toml
on-idle = {
type = "exec",
exec = {
prog = "swaylock",
privileged = true,
},
}
```
> [!IMPORTANT]
> Screen lockers that use the Wayland session lock protocol (like swaylock)
> need `privileged = true` in the exec configuration. This grants the process
> the necessary permissions to lock the session.
You can also combine multiple actions:
```toml
on-idle = [
{
type = "exec",
exec = {
prog = "swaylock",
privileged = true,
},
},
{ type = "exec", exec = ["notify-send", "System locked"] },
]
```
## Complete example
A typical idle and screen-locking setup:
```toml
idle = {
minutes = 10,
grace-period = { seconds = 5 },
}
on-idle = {
type = "exec",
exec = {
prog = "swaylock",
privileged = true,
},
}
```
This means:
1. After 10 minutes of inactivity, the screen goes black (grace period begins).
2. If no input occurs within 5 seconds, swaylock is launched and outputs are
disabled.
3. Any input during the grace period cancels the transition and restores the
display.
## Runtime changes
### Checking idle status
```shell
~$ jay idle status
```
### Changing the idle timeout
```shell
~$ jay idle set 5m
~$ jay idle set 1m30s
~$ jay idle set disabled
```
### Changing the grace period
```shell
~$ jay idle set-grace-period 10s
~$ jay idle set-grace-period 0s
```
### Duration format
The CLI accepts durations in a flexible format:
| Example | Meaning |
|----------------------|------------------------|
| `1m` | 1 minute |
| `1m5s` | 1 minute 5 seconds |
| `1min 5sec` | 1 minute 5 seconds |
| `90s` | 90 seconds |
| `disabled` | Disable the timeout |
### Unlocking
If the compositor is locked (e.g. the screen locker crashed), you can unlock
it from another TTY or by SSH-ing into the machine. You must set
`WAYLAND_DISPLAY` to the socket of the Jay compositor, since you are running
the command outside the compositor session:
```shell
~$ WAYLAND_DISPLAY=wayland-1 jay unlock
```
Use `jay pid` with the same `WAYLAND_DISPLAY` value to verify you are
targeting the correct compositor instance.
### Using shortcuts
The `configure-idle` action lets you change idle settings from a keybinding:
```toml
[shortcuts]
alt-F9 = {
type = "configure-idle",
idle = {
minutes = 5,
grace-period = { seconds = 3 },
},
}
alt-F10 = {
type = "configure-idle",
idle = { minutes = 0 },
}
```
## Full reference
For the exhaustive list of all idle-related fields and types, see the
[auto-generated specification](https://github.com/mahkoh/jay/blob/master/toml-spec/spec/spec.generated.md).

View file

@ -0,0 +1,130 @@
# Configuration Overview
Jay is configured through a single TOML file located at:
```
~/.config/jay/config.toml
```
If this file does not exist, Jay uses built-in defaults that provide a
reasonable starting configuration with common shortcuts, a US QWERTY keymap,
and other sensible settings.
> [!WARNING]
> Once `config.toml` exists, the **entire** built-in default configuration is
> replaced -- not merged. Even a completely empty file means no shortcuts, no
> startup actions, nothing. Always start from a full config rather than writing
> one from scratch.
## Initializing the config
The easiest way to get started is to let Jay write the defaults for you:
```shell
~$ jay config init
```
This creates `~/.config/jay/config.toml` pre-populated with the full default
configuration. You can then edit it to suit your needs.
If you already have a config file and want to reset it:
```shell
~$ jay config init --overwrite
```
The old file will be backed up to `config.toml.1` (or `.2`, `.3`, etc.) before
being replaced.
## Other config subcommands
Print the path to the config file:
```shell
~$ jay config path
```
Open the config directory in your file manager:
```shell
~$ jay config open-dir
```
## Reloading the configuration
By default, Jay does not automatically reload `config.toml` when it changes on
disk. To apply changes, trigger a reload manually:
- Press `alt-shift-r` (the default shortcut), or
- Use the `reload-config-toml` action in any other action context (e.g. a named
action or a window rule).
Most settings take effect immediately on reload. A few exceptions (like
`log-level`, `explicit-sync`, and initial `drm-devices` settings) only apply
at compositor startup.
### Automatic reloading
To have Jay watch `config.toml` for changes and reload automatically, add:
```toml
auto-reload = true
```
When enabled, Jay uses inotify to monitor the config file and its parent
directories. Changes are debounced — the config is reloaded 400 ms after the
last write, so rapid successive saves don't cause multiple reloads. If the file
contents haven't actually changed, the reload is skipped.
Setting `auto-reload = false` will stop the watcher. Removing the key entirely
leaves the watcher state unchanged (if it was running, it keeps running until
the compositor restarts).
## Named actions
You can define reusable actions in the `[actions]` table and reference them
anywhere an action is accepted by prefixing the name with `$`:
```toml
[actions]
launch-terminal = { type = "exec", exec = "alacritty" }
launch-browser = { type = "exec", exec = "firefox" }
[shortcuts]
alt-Return = "$launch-terminal"
alt-b = "$launch-browser"
```
Named actions can reference other named actions. The `max-action-depth` setting
controls the maximum recursion depth to prevent infinite loops (default: 16):
```toml
max-action-depth = 32
```
## Composable actions
Anywhere an action is accepted, you can use an **array of actions** instead.
This applies to shortcuts, startup hooks, named actions, and any other action
field:
```toml
[shortcuts]
alt-q = [
{ type = "exec", exec = ["notify-send", "Goodbye!"] },
"quit",
]
```
## Advanced: shared library configuration
For users who need programmatic configuration beyond what TOML offers, Jay also
supports configuration via a compiled Rust shared library using the
[jay-config](https://docs.rs/jay-config) crate. This is an advanced option --
the TOML config is sufficient for the vast majority of use cases.
## Full specification
This book covers the most common configuration options with explanations and
examples. For an exhaustive listing of every field, type, and action, see the
[auto-generated specification](https://github.com/mahkoh/jay/blob/master/toml-spec/spec/spec.generated.md).

View file

@ -0,0 +1,336 @@
# Input Devices
Jay configures input devices through the `[[inputs]]` array. Each entry matches
one or more devices and applies settings such as acceleration, tap behavior,
and device-to-output mapping.
> [!NOTE]
> Input configuration defined in `config.toml` is only applied to devices
> connected after the configuration is loaded. To change settings for
> already-connected devices, use `jay input` or the `configure-input` action.
## Matching input devices
Every `[[inputs]]` entry requires a `match` field. When `match` is a
**table**, all specified fields must match (AND logic). When `match` is an
**array**, any entry matching is sufficient (OR logic).
### By device name
```toml
[[inputs]]
match.name = "Logitech G300s Optical Gaming Mouse"
left-handed = true
```
Run `jay input` to see the names of all connected input devices.
### By device type
Match all devices of a given type using boolean flags:
```toml
[[inputs]]
match.is-pointer = true
natural-scrolling = true
```
Available type flags: `is-keyboard`, `is-pointer`, `is-touch`,
`is-tablet-tool`, `is-tablet-pad`, `is-gesture`, `is-switch`.
### By syspath or devnode
The `syspath` is usually stable across reboots and useful when you have
multiple identical devices:
```toml
[[inputs]]
match.syspath = "/sys/devices/pci0000:00/0000:00:08.1/0000:14:00.4/usb5/5-1/5-1.1/5-1.1.2/5-1.1.2:1.0"
left-handed = true
```
The `devnode` (e.g. `/dev/input/event4`) is typically not stable across
reboots.
### Combining criteria
AND -- all fields in a single table must match:
```toml
[[inputs]]
match = { name = "SynPS/2 Synaptics TouchPad", is-pointer = true }
tap-enabled = true
```
OR -- any entry in an array may match:
```toml
[[inputs]]
match = [
{ name = "Logitech G300s Optical Gaming Mouse" },
{ name = "Razer DeathAdder V2" },
]
left-handed = true
```
## Tagging devices
Assign a `tag` to reference a device from shortcuts or actions:
```toml
[[inputs]]
tag = "mouse"
match.is-pointer = true
[shortcuts]
alt-l = {
type = "configure-input",
input = {
match.tag = "mouse",
left-handed = true,
},
}
alt-r = {
type = "configure-input",
input = {
match.tag = "mouse",
left-handed = false,
},
}
```
Tags work similarly to output names -- they let you refer to matched devices
elsewhere in the configuration.
## Libinput settings
These settings map directly to libinput device options. See the
[libinput documentation](https://wayland.freedesktop.org/libinput/doc/latest/)
for detailed explanations of each.
### Acceleration
```toml
[[inputs]]
match.is-pointer = true
accel-profile = "Flat"
accel-speed = 0.0
```
| Field | Values | Description |
|----------------|----------------------------|----------------------------------------|
| `accel-profile`| `Flat` or `Adaptive` | Pointer acceleration curve |
| `accel-speed` | `-1.0` to `1.0` | Speed within the selected profile |
### Tap and click
```toml
[[inputs]]
match.is-pointer = true
tap-enabled = true
tap-drag-enabled = true
tap-drag-lock-enabled = false
click-method = "clickfinger"
```
| Field | Values | Description |
|-------------------------|--------------------------------------------|-------------------------------------------|
| `tap-enabled` | `true` / `false` | Tap-to-click on touchpads |
| `tap-drag-enabled` | `true` / `false` | Tap-and-drag on touchpads |
| `tap-drag-lock-enabled` | `true` / `false` | Keep drag active after lifting finger |
| `click-method` | `none`, `button-areas`, `clickfinger` | How physical clicks are interpreted |
### Other libinput options
```toml
[[inputs]]
match.is-pointer = true
left-handed = true
natural-scrolling = true
middle-button-emulation = true
```
`left-handed`
: Swap left and right buttons
`natural-scrolling`
: Reverse scroll direction ("macOS-style")
`middle-button-emulation`
: Simultaneous left+right click produces a middle click
## Scroll speed
Control how many pixels each scroll wheel detent produces:
```toml
[[inputs]]
match.is-pointer = true
px-per-wheel-scroll = 30
```
This setting maps to the legacy `wl_pointer.axis` event that is mostly unused
nowadays. It has no effect on applications that don't use this event.
## Transform matrix
Apply a 2x2 matrix to relative motion events. This is useful for adjusting
pointer speed independently of libinput acceleration:
```toml
[[inputs]]
match.is-pointer = true
transform-matrix = [[0.35, 0], [0, 0.35]]
```
The example above reduces pointer speed to 35% of normal. The identity matrix
is `[[1, 0], [0, 1]]`.
## Calibration matrix
A 2x3 matrix for absolute input devices (touchscreens). This is passed
directly to libinput:
```toml
[[inputs]]
match.is-touch = true
calibration-matrix = [[0, 1, 0], [-1, 0, 1]]
```
The example above rotates touch input by 90 degrees.
## Per-device keymap
Override the global keymap for a specific keyboard:
```toml
[[inputs]]
match.name = "ZSA Technology Labs Inc ErgoDox EZ"
keymap.rmlvo = {
layout = "us",
options = "compose:ralt",
}
```
The override becomes active when a key is pressed on that device. See the
[Keymaps & Repeat Rate](keymaps.md) chapter for the full range of keymap
options.
## Mapping to outputs
Map tablets and touchscreens to a specific output so that the input area
corresponds to the correct display:
```toml
[[outputs]]
name = "left"
match.serial-number = "33K03894SL0"
[[inputs]]
match.name = "Wacom Bamboo Comic 2FG Pen"
output.name = "left"
```
You can also map by connector:
```toml
[[inputs]]
match.name = "Wacom Bamboo Comic 2FG Pen"
output.connector = "DP-1"
```
To remove a mapping at runtime, use the `remove-mapping` field in a
`configure-input` action:
```toml
[shortcuts]
alt-x = {
type = "configure-input",
input = {
match.tag = "wacom",
remove-mapping = true,
},
}
```
## Lid switch events
Lid switch devices report when a laptop lid is opened or closed. Use the
`on-lid-closed` and `on-lid-opened` fields to trigger actions. These fields
only work in the top-level `[[inputs]]` array:
```toml
[[inputs]]
match.name = "<lid switch name>"
on-lid-closed = {
type = "configure-connector",
connector = {
match.name = "eDP-1",
enabled = false,
},
}
on-lid-opened = {
type = "configure-connector",
connector = {
match.name = "eDP-1",
enabled = true,
},
}
```
Run `jay input` to find the name of your lid switch device.
## Convertible (2-in-1) events
For convertible laptops that switch between laptop and tablet form factors:
```toml
[[inputs]]
match.name = "<switch name>"
on-converted-to-laptop = "$enable-keyboard"
on-converted-to-tablet = "$disable-keyboard"
```
These fields only work in the top-level `[[inputs]]` array.
## Runtime changes
### Listing devices
```shell
~$ jay input
```
This shows all connected input devices with their names, syspaths, devnodes,
type flags, and current settings.
### Changing settings at runtime
```shell
~$ jay input device <id> set-accel-profile flat
~$ jay input device <id> set-accel-speed 0.5
~$ jay input device <id> set-tap-enabled true
~$ jay input device <id> set-left-handed true
~$ jay input device <id> set-natural-scrolling true
~$ jay input device <id> set-transform-matrix 0.35 0 0 0.35
```
### Using shortcuts
The `configure-input` action applies settings from a keybinding:
```toml
[shortcuts]
alt-n = {
type = "configure-input",
input = {
match.tag = "touchpad",
natural-scrolling = true,
},
}
```
## Full reference
For the exhaustive list of all input fields, match criteria, and related types,
see the [auto-generated specification](https://github.com/mahkoh/jay/blob/master/toml-spec/spec/spec.generated.md).

View file

@ -0,0 +1,144 @@
# Keymaps & Repeat Rate
Jay uses XKB keymaps for keyboard layout configuration. The default keymap is
US QWERTY.
## Setting the keymap
There are several ways to define a keymap.
### Using RMLVO names (recommended)
The simplest approach is to specify the layout using RMLVO (Rules, Model,
Layout, Variants, Options) names:
```toml
keymap.rmlvo = { layout = "de" }
```
You can specify any combination of RMLVO fields:
```toml
keymap.rmlvo = {
layout = "us,de",
variants = "dvorak,",
options = "grp:ctrl_space_toggle",
}
```
All fields are optional. When a field is omitted, Jay checks the corresponding
environment variable, then falls back to a default:
| Field | Environment Variable | Default |
|------------|--------------------------|----------|
| `rules` | `XKB_DEFAULT_RULES` | `evdev` |
| `model` | `XKB_DEFAULT_MODEL` | `pc105` |
| `layout` | `XKB_DEFAULT_LAYOUT` | `us` |
| `variants` | `XKB_DEFAULT_VARIANTS` | *(none)* |
| `options` | `XKB_DEFAULT_OPTIONS` | *(none)* |
### Using a raw XKB string
You can provide a complete XKB keymap as a multi-line string. See the
[ArchWiki XKB guide](https://wiki.archlinux.org/title/X_keyboard_extension)
for background on the format.
```toml
keymap = """
xkb_keymap {
xkb_keycodes { include "evdev+aliases(qwerty)" };
xkb_types { include "complete" };
xkb_compat { include "complete" };
xkb_symbols { include "pc+us+inet(evdev)" };
};
"""
```
### Loading from a file
Point to an XKB file. Relative paths are resolved from the config directory
(`~/.config/jay/`):
```toml
keymap.path = "./my-keymap.xkb"
```
## Named keymaps
You can define multiple named keymaps and switch between them at runtime.
Define them with the `[[keymaps]]` array, then select the default with
`keymap.name`:
```toml
keymap.name = "laptop"
[[keymaps]]
name = "laptop"
rmlvo = { layout = "us" }
[[keymaps]]
name = "external"
rmlvo = { layout = "de", options = "compose:ralt" }
```
Each entry in `[[keymaps]]` must have a `name` and exactly one of `map`,
`path`, or `rmlvo`.
### Switching keymaps at runtime
Use the `set-keymap` action to switch between named keymaps:
```toml
[shortcuts]
alt-F9 = { type = "set-keymap", keymap.name = "laptop" }
alt-F10 = { type = "set-keymap", keymap.name = "external" }
```
You can also switch keymaps from the command line:
```shell
~$ jay input seat default set-keymap-from-names --layout de
```
## Repeat rate
The repeat rate controls how keys behave when held down. It has two parameters:
- `rate` -- number of key repeats per second
- `delay` -- milliseconds to wait before repeating begins
```toml
repeat-rate = { rate = 25, delay = 250 }
```
### Changing repeat rate at runtime
Use the `set-repeat-rate` action:
```toml
[shortcuts]
alt-F11 = {
type = "set-repeat-rate",
rate = { rate = 40, delay = 200 },
}
```
Or from the command line:
```shell
~$ jay input seat default set-repeat-rate 40 200
```
## Per-device keymaps
You can override the keymap for specific input devices using the `[[inputs]]`
array. For example, to use a different layout for an external keyboard:
```toml
[[inputs]]
match.name = "My External Keyboard"
keymap.rmlvo = { layout = "de" }
```
See the [Input Devices](inputs.md) chapter for more on matching and configuring
individual devices.

View file

@ -0,0 +1,207 @@
# Miscellaneous
This chapter covers smaller configuration options that don't warrant their own
chapter.
## Color Management
The Wayland color management protocol lets applications communicate color space
information to the compositor. It is disabled by default.
```toml
[color-management]
enabled = true
```
> [!NOTE]
> Changing this setting has no effect on applications that are already running.
The CLI and control center (**Color Management** pane) can also toggle it:
```shell
~$ jay color-management enable
~$ jay color-management disable
~$ jay color-management status
```
See [HDR & Color Management](../hdr.md) for a complete guide to HDR output and
the color management protocol.
## Libei
[libei](https://gitlab.freedesktop.org/libinput/libei) allows applications to
emulate input events. By default, applications can only access libei through
the portal (which prompts the user for permission). Setting `enable-socket`
exposes an unauthenticated socket that any application can use without a prompt.
```toml
libei.enable-socket = false # default
```
## UI Drag
Controls whether workspaces and tiles can be dragged with the mouse, and how
far the pointer must move before a drag begins.
```toml
ui-drag = { enabled = true, threshold = 10 } # defaults
```
Set `enabled = false` to disable drag-and-drop rearrangement entirely. Increase
`threshold` if you find yourself accidentally starting drags.
## Floating Window Pin Icon
Floating windows can show a small pin icon. This is hidden by default.
```toml
[float]
show-pin-icon = true
```
## Workspace Capture
Controls whether newly created workspaces can be captured (e.g. for screen
sharing). The default is `true`.
```toml
workspace-capture = false
```
## Simple Input Method
Jay includes a built-in XCompose-based input method. It is enabled by default
but only activates when no external input method is running.
```toml
[simple-im]
enabled = true # default
```
Related actions for use in shortcuts:
`enable-simple-im`
: Enable the built-in input method
`disable-simple-im`
: Disable the built-in input method
`toggle-simple-im-enabled`
: Toggle the built-in input method
`reload-simple-im`
: Reload XCompose files without restarting
`enable-unicode-input`
: Start Unicode codepoint input (requires active IM)
## Log Level
Sets the compositor's log verbosity. Valid values: `trace`, `debug`, `info`,
`warn`, `error`.
```toml
log-level = "info"
```
This setting **cannot** be changed by reloading the configuration. Use the CLI
instead:
```shell
~$ jay set-log-level debug
```
## Focus Follows Mouse
When enabled, moving the pointer over a window automatically gives it keyboard
focus.
```toml
focus-follows-mouse = true # default
```
## Window Management Key
Designates a key that, while held, enables window management mode. In this
mode, the left mouse button moves floating windows and the right mouse button
resizes any window.
```toml
window-management-key = "Alt_L"
```
The value should be a keysym name (see the
[xkbcommon keysym list](https://github.com/xkbcommon/libxkbcommon/blob/master/include/xkbcommon/xkbcommon-keysyms.h)
with the `XKB_KEY_` prefix removed).
## Middle-Click Paste
Controls whether middle-clicking pastes the primary selection. Changing this
has no effect on running applications.
```toml
middle-click-paste = true # default
```
## Pointer Revert Key
Pressing this key cancels any active grabs, drags, or selections, returning the
pointer to its default state. The default is `Escape`.
```toml
pointer-revert-key = "Escape" # default
```
Set it to `NoSymbol` to disable this functionality entirely:
```toml
pointer-revert-key = "NoSymbol"
```
## Fallback Output Mode
Determines which output is used when no particular output is specified -- for
example, when placing a newly opened window or choosing which workspace to move
with `move-to-output`.
`cursor`
: Use the output the cursor is on (default)
`focus`
: Use the output the focused window is on
```toml
fallback-output-mode = "cursor" # default
```
## Focus History
Configures the behavior of the `focus-prev` and `focus-next` actions.
`only-visible`
: Only cycle to windows that are already visible. Default: `false`.
`same-workspace`
: Only cycle to windows on the current workspace. Default: `false`.
If `only-visible` is `false`, switching to a non-visible window will make it
visible first.
```toml
[focus-history]
only-visible = true
same-workspace = true
```
## Control Center Fonts
The `[egui]` table configures fonts used by the control center (an egui-based
GUI).
```toml
[egui]
proportional-fonts = ["sans-serif", "Noto Sans", "Noto Color Emoji"] # default
monospace-fonts = ["monospace", "Noto Sans Mono", "Noto Color Emoji"] # default
```
Override these lists to use your preferred fonts in the control center UI.

View file

@ -0,0 +1,472 @@
# Outputs (Monitors)
Jay configures monitors through the `[[outputs]]` array. Each entry matches
one or more connected displays and applies settings such as position, scale,
mode, and color management.
> [!NOTE]
> Output configuration defined in `config.toml` is only applied the first time
> a matching output is connected after the compositor starts. To change
> settings at runtime, use `jay randr` or the `configure-output` action.
## Matching outputs
Every `[[outputs]]` entry requires a `match` field that selects which monitors
the settings apply to. You can match by serial number, connector name,
manufacturer, or model.
When `match` is a **table**, all specified fields must match (AND logic). When
`match` is an **array**, any entry matching is sufficient (OR logic).
### By serial number (recommended)
The serial number is a unique identifier that stays the same regardless of
which port the monitor is plugged into:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
scale = 1.5
```
Run `jay randr` to find the serial number of your connected displays.
### By connector name
```toml
[[outputs]]
match.connector = "DP-1"
scale = 1.25
```
Connector names (like `DP-1`, `HDMI-A-1`, `eDP-1`) can change if you move
cables between ports.
### By manufacturer and model
```toml
[[outputs]]
match = { manufacturer = "BNQ", model = "BenQ GW2480" }
scale = 1.25
```
When multiple fields appear in a single table, all must match.
### Combining criteria (OR)
Use an array to match any of several outputs with the same settings:
```toml
[[outputs]]
match = [
{ serial-number = "33K03894SL0" },
{ serial-number = "ETW1M02062SL0" },
]
scale = 2.0
```
## Naming outputs
Assign a `name` to reference an output from other parts of the config -- for
example, when mapping a tablet to a specific monitor or using shortcuts to
reconfigure outputs at runtime:
```toml
[[outputs]]
name = "left"
match.serial-number = "33K03894SL0"
x = 0
y = 0
[[outputs]]
name = "right"
match.serial-number = "ETW1M02062SL0"
x = 1920
y = 0
```
Other rules can then reference these names:
```toml
# Map a drawing tablet to the left monitor
[[inputs]]
match.name = "Wacom Intuos Pro M Pen"
output.name = "left"
```
```toml
# Rotate a named output with a shortcut
[shortcuts]
alt-r = {
type = "configure-output",
output = {
match.name = "right",
transform = "rotate-90",
},
}
```
## Position
The `x` and `y` fields place outputs in compositor space. Coordinates are
integers >= 0 and represent the top-left corner of the output:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
x = 0
y = 0
[[outputs]]
match.serial-number = "ETW1M02062SL0"
x = 2560
y = 0
```
> [!TIP]
> The control center (`alt-c` by default) includes a visual output arrangement
> editor where you can drag monitors into position.
## Scale
Set fractional scaling with a number greater than 0:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
scale = 1.5
```
Common values: `1.0` (no scaling), `1.25`, `1.5`, `2.0`.
## Transform
Rotate or flip the output. The available values are:
`none`
: No transformation
`rotate-90`
: Rotate 90 degrees counter-clockwise
`rotate-180`
: Rotate 180 degrees
`rotate-270`
: Rotate 270 degrees counter-clockwise
`flip`
: Flip around the vertical axis
`flip-rotate-90`
: Flip vertically, then rotate 90 degrees counter-clockwise
`flip-rotate-180`
: Flip vertically, then rotate 180 degrees
`flip-rotate-270`
: Flip vertically, then rotate 270 degrees counter-clockwise
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
transform = "rotate-90"
```
## Mode
Set the resolution and refresh rate with the `mode` field. If `refresh-rate` is
omitted, the first available mode with the specified resolution is used:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
mode = {
width = 2560,
height = 1440,
refresh-rate = 144.0,
}
```
Use `jay randr` to see all available modes for each output:
```shell
~$ jay randr show --modes
```
## Variable Refresh Rate (VRR)
VRR (also known as FreeSync or Adaptive Sync) allows the display to vary its
refresh rate to match the content being rendered, reducing stuttering and
tearing.
Configure VRR with the `vrr` field:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
vrr = { mode = "variant1", cursor-hz = 90 }
```
### VRR modes
`never`
: VRR is always off (default)
`always`
: VRR is always on
`variant1`
: VRR is on when one or more applications are displayed fullscreen
`variant2`
: VRR is on when exactly one application is displayed fullscreen
`variant3`
: VRR is on when a single application is displayed fullscreen and describes its
content type as video or game via the `wp_content_type_v1` protocol
### Cursor refresh rate
When VRR is active, cursor movement can cause the screen to spike to maximum
refresh rate. The `cursor-hz` field limits cursor-triggered updates:
```toml
vrr = { mode = "always", cursor-hz = 90 }
```
Set `cursor-hz = "none"` for unbounded cursor updates (the default). A numeric
value means the cursor is updated at that rate in Hz, or faster if the
application is already driving updates above that rate.
You can also set default VRR settings for all outputs at the top level:
```toml
vrr = { mode = "variant1", cursor-hz = 90 }
```
Per-output settings override the top-level default.
## Tearing
Tearing allows frames to be presented immediately instead of waiting for
vertical blank, reducing input latency at the cost of visible tearing
artifacts.
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
tearing.mode = "variant3"
```
### Tearing modes
`never`
: Tearing is never enabled
`always`
: Tearing is always enabled
`variant1`
: Tearing is enabled when one or more applications are displayed fullscreen
`variant2`
: Tearing is enabled when a single application is displayed fullscreen
`variant3`
: Tearing is enabled when a single application is displayed and has requested tearing (default)
The default tearing mode is `variant3`. Like VRR, per-output settings override
top-level defaults:
```toml
tearing.mode = "never"
```
## Framebuffer format
The default framebuffer format is `xrgb8888`. You can change it to any DRM
fourcc format:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
format = "rgb565"
```
Common formats include `xrgb8888`, `argb8888`, `xbgr8888`, `abgr8888`,
`rgb565`, and many others. See the
[auto-generated specification](https://github.com/mahkoh/jay/blob/master/toml-spec/spec/spec.generated.md) for the full list.
## HDR and color management
Jay supports HDR output through color space, transfer function, and brightness
settings. These require the Vulkan renderer. For a conceptual overview and
step-by-step guide, see [HDR & Color Management](../hdr.md).
### Color space
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
color-space = "bt2020"
```
Values: `default` (usually sRGB) or `bt2020`.
### Transfer function (EOTF)
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
transfer-function = "pq"
```
Values: `default` (usually gamma 2.2) or `pq` (Perceptual Quantizer, used for
HDR10).
### Brightness
Set SDR content brightness in cd/m^2 or use `"default"`:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
brightness = 80
```
The default depends on the transfer function:
- With `default` EOTF: the maximum brightness of the display, anchored at
80 cd/m^2. Setting a value below 80 creates HDR headroom.
- With `pq`: 203 cd/m^2.
This setting has no effect unless the Vulkan renderer is in use.
## Blend space
Controls how colors are blended when compositing overlapping surfaces:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
blend-space = "linear"
```
`srgb`
: Classic desktop blending in sRGB space (default)
`linear`
: Physically correct blending in linear light -- produces brighter results
## Native gamut
By default, Jay assumes displays use sRGB primaries (matching the behavior of
most other compositors). In reality, many displays have a wider gamut.
Setting `use-native-gamut = true` tells Jay to use the primaries advertised in
the display's EDID. This can produce more accurate colors and allows
color-managed applications to use the full gamut:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
use-native-gamut = true
```
This has no effect when the display is explicitly operating in a wide color
space (e.g. BT.2020).
## Connector configuration
The `[[connectors]]` array lets you enable or disable physical display
connectors. This is useful for permanently disabling a port:
```toml
[[connectors]]
match.name = "eDP-1"
enabled = false
```
Connector configuration is applied when the connector is first discovered by
the compositor, which typically happens only at startup.
## Lid switch (auto-disable laptop screen)
On laptops, you can automatically disable the built-in display when the lid is
closed using the `[[inputs]]` array. The lid switch is an input device:
```toml
[[inputs]]
match.name = "<lid switch name>"
on-lid-closed = {
type = "configure-connector",
connector = {
match.name = "eDP-1",
enabled = false,
},
}
on-lid-opened = {
type = "configure-connector",
connector = {
match.name = "eDP-1",
enabled = true,
},
}
```
Run `jay input` to find the name of your lid switch device. See the
[Input Devices](inputs.md) chapter for more details.
## Runtime changes
Output settings in `config.toml` are only applied when a display is first
connected after compositor startup. For runtime changes, use the `jay randr`
CLI or the `configure-output` action.
### Listing outputs
```shell
~$ jay randr
```
This shows all connected outputs with their connector names, serial numbers,
current modes, scales, transforms, and available modes.
### Changing settings at runtime
```shell
~$ jay randr output <name-or-connector> scale 1.5
~$ jay randr output <name-or-connector> mode 2560 1440 144.0
~$ jay randr output <name-or-connector> position 1920 0
~$ jay randr output <name-or-connector> transform rotate-90
~$ jay randr output <name-or-connector> enable
~$ jay randr output <name-or-connector> disable
```
### Using shortcuts
The `configure-output` action lets you change output settings from a
keybinding:
```toml
[shortcuts]
alt-F7 = {
type = "configure-output",
output = {
match.name = "right",
transform = "none",
},
}
alt-F8 = {
type = "configure-output",
output = {
match.name = "right",
transform = "rotate-90",
},
}
```
## Full reference
For the exhaustive list of all output fields, match criteria, and related
types, see the [auto-generated specification](https://github.com/mahkoh/jay/blob/master/toml-spec/spec/spec.generated.md).

View file

@ -0,0 +1,505 @@
# Shortcuts
Shortcuts bind key combinations to actions. They are the primary way to
interact with Jay.
## Basic syntax
Shortcuts are defined in the `[shortcuts]` table. The left side is a key
combination; the right side is an action:
```toml
[shortcuts]
alt-q = "quit"
alt-Return = { type = "exec", exec = "alacritty" }
alt-shift-c = "close"
```
### Key format
Key combinations follow the pattern `MODIFIER-MODIFIER-KEYSYM`:
```
(MOD-)*KEYSYM
```
**Keysym names** are unmodified XKB keysym names from [xkbcommon-keysyms.h](https://github.com/xkbcommon/libxkbcommon/blob/master/include/xkbcommon/xkbcommon-keysyms.h)
with the `XKB_KEY_` prefix removed. Use the unmodified keysym -- write
`shift-q`, not `shift-Q`.
### Modifiers
The available modifiers are:
`shift`
: Shift key
`ctrl`
: Control key
`alt`
: Alt key
`logo`
: Super/Meta/Windows key
`lock`
: Lock modifier
`caps`
: Caps Lock
`num`
: Num Lock
`mod1`
: Mod1 (typically Alt)
`mod2`
: Mod2 (typically Num Lock)
`mod3`
: Mod3
`mod4`
: Mod4 (typically Super)
`mod5`
: Mod5
`release`
: Fire on key **release** instead of press
The `release` modifier is special: it causes the action to trigger when the key
is released rather than when it is pressed.
## Simple actions
Simple actions are written as plain strings. Here are the most commonly used
ones:
**Focus and movement:**
```toml
[shortcuts]
alt-h = "focus-left"
alt-j = "focus-down"
alt-k = "focus-up"
alt-l = "focus-right"
alt-shift-h = "move-left"
alt-shift-j = "move-down"
alt-shift-k = "move-up"
alt-shift-l = "move-right"
```
**Layout:**
```toml
[shortcuts]
alt-d = "split-horizontal"
alt-v = "split-vertical"
alt-t = "toggle-split"
alt-m = "toggle-mono"
alt-f = "focus-parent"
```
**Window management:**
```toml
[shortcuts]
alt-u = "toggle-fullscreen"
alt-shift-f = "toggle-floating"
alt-shift-c = "close"
```
**Compositor control:**
```toml
[shortcuts]
alt-q = "quit"
alt-shift-r = "reload-config-toml"
```
**Other useful simple actions:**
- `consume` -- consume the key event (prevent it from reaching applications).
Key-press events that trigger shortcuts are consumed by default; key-release
events are forwarded by default. Consuming key-release events can cause keys
to get stuck in the focused application.
- `forward` -- forward the key event to the focused application (the inverse
of `consume`)
- `none` -- unbind this key combination (useful for overriding defaults or
inherited mode bindings)
- `disable-pointer-constraint` -- release a pointer lock/confinement
- `focus-parent` -- move focus to the parent container
- `toggle-bar`, `show-bar`, `hide-bar` -- control the status bar
- `open-control-center` -- open the Jay control center GUI
- `warp-mouse-to-focus` -- warp the cursor to the center of the focused window
- `kill-client` -- forcefully disconnect a client (in a window rule, kills the
window's client; in a client rule, kills the matched client; has no effect
in plain shortcuts)
- `focus-below`, `focus-above` -- move focus to the layer below or above the
current layer
- `focus-tiles` -- focus the tile layer
- `create-mark`, `jump-to-mark` -- interactively create or jump to a mark
(the next pressed key identifies the mark). See [Marks](#marks) below.
- `enable-window-management`, `disable-window-management` -- programmatically
enable or disable [window management mode](../floating.md#window-management-mode)
- `reload-config-so` -- reload the shared-library configuration (`config.so`)
See the [specification](https://github.com/mahkoh/jay/blob/master/toml-spec/spec/spec.generated.md) for the full list of simple
actions.
## Parameterized actions
Actions that need arguments are written as tables with a `type` field:
### Launching programs
```toml
[shortcuts]
alt-Return = { type = "exec", exec = "alacritty" }
alt-p = { type = "exec", exec = "bemenu-run" }
```
### Switching virtual terminals
```toml
[shortcuts]
ctrl-alt-F1 = { type = "switch-to-vt", num = 1 }
ctrl-alt-F2 = { type = "switch-to-vt", num = 2 }
```
### Workspaces
```toml
[shortcuts]
alt-F1 = { type = "show-workspace", name = "1" }
alt-F2 = { type = "show-workspace", name = "2" }
alt-shift-F1 = { type = "move-to-workspace", name = "1" }
alt-shift-F2 = { type = "move-to-workspace", name = "2" }
```
### Moving workspaces to outputs
```toml
[shortcuts]
logo-ctrl-shift-Right = {
type = "move-to-output",
direction = "right",
}
logo-ctrl-shift-Left = {
type = "move-to-output",
direction = "left",
}
```
### Other parameterized actions
- `set-keymap` -- change the active keymap
- `set-repeat-rate` -- change the keyboard repeat rate
- `set-env` -- set environment variables for future spawned programs
- `unset-env` -- remove environment variables
- `configure-connector` -- enable/disable a monitor
- `configure-input` -- change input device settings
- `configure-output` -- change output settings
- `configure-idle` -- change the idle timeout
- `configure-direct-scanout` -- enable or disable direct scanout
- `configure-drm-device` -- apply settings to a DRM device
- `set-theme` -- change theme settings
- `set-log-level` -- change the compositor log level
- `set-gfx-api` -- set the graphics API for new DRM devices (usually only
effective at startup)
- `set-render-device` -- set the render device for compositing
- `define-action` -- define or redefine a named action at runtime
- `undefine-action` -- remove a named action
- `create-mark` -- create a mark with an explicit ID (see [Marks](#marks))
- `jump-to-mark` -- jump to a mark with an explicit ID
- `copy-mark` -- copy a mark from one ID to another
- `create-virtual-output` -- create a virtual output
- `remove-virtual-output` -- remove a virtual output
See the [specification](https://github.com/mahkoh/jay/blob/master/toml-spec/spec/spec.generated.md) for the complete list.
## Running multiple actions
Use an array to run several actions from a single shortcut:
```toml
[shortcuts]
alt-q = [
{ type = "exec", exec = ["notify-send", "Goodbye!"] },
"quit",
]
```
## The exec action in detail
The `exec` field accepts three forms:
**A simple string** -- the program name with no arguments:
```toml
alt-Return = { type = "exec", exec = "alacritty" }
```
**An array of strings** -- the program name followed by arguments:
```toml
alt-n = { type = "exec", exec = ["notify-send", "Hello", "World"] }
```
**A table** -- full control over execution. Exactly one of `prog` or `shell`
must be specified:
```toml
# Using prog + args
alt-n = {
type = "exec",
exec = {
prog = "notify-send",
args = ["Hello"],
env = { LANG = "en_US.UTF-8" },
},
}
# Using shell (runs as: $SHELL -c "command")
alt-s = {
type = "exec",
exec = {
shell = "grim - | wl-copy",
privileged = true,
},
}
```
Table fields:
`prog`
: Program to execute (mutually exclusive with `shell`)
`shell`
: Shell command to run via `$SHELL -c` (mutually exclusive with `prog`)
`args`
: Arguments array (only with `prog`)
`env`
: Per-process environment variables
`privileged`
: If `true`, grants access to privileged Wayland protocols (default: `false`)
`tag`
: Tag to apply to all Wayland connections spawned by this process
### Practical examples
Volume control with `pactl`:
```toml
[shortcuts]
XF86AudioRaiseVolume = {
type = "exec",
exec = ["pactl", "set-sink-volume", "0", "+5%"],
}
XF86AudioLowerVolume = {
type = "exec",
exec = ["pactl", "set-sink-volume", "0", "-5%"],
}
XF86AudioMute = {
type = "exec",
exec = ["pactl", "set-sink-mute", "0", "toggle"],
}
```
Taking a screenshot and copying to clipboard:
```toml
[shortcuts]
Print = {
type = "exec",
exec = {
shell = "grim - | wl-copy",
privileged = true,
},
}
```
## Complex shortcuts
Complex shortcuts provide additional control via the `[complex-shortcuts]`
table. They support:
- **`mod-mask`** -- controls which modifiers are considered when matching.
Set to `""` to ignore all modifiers.
- **`action`** -- the action to run on key press (defaults to `"none"`).
- **`latch`** -- an action to run when the key is **released**.
### Volume keys regardless of modifiers
The volume keys should work whether or not Alt, Shift, etc. are held:
```toml
[complex-shortcuts.XF86AudioRaiseVolume]
mod-mask = ""
action = {
type = "exec",
exec = ["pactl", "set-sink-volume", "0", "+5%"],
}
[complex-shortcuts.XF86AudioLowerVolume]
mod-mask = ""
action = {
type = "exec",
exec = ["pactl", "set-sink-volume", "0", "-5%"],
}
```
### Push-to-talk
Unmute audio while a key is held, mute on release:
```toml
[complex-shortcuts.alt-x]
action = {
type = "exec",
exec = ["pactl", "set-sink-mute", "0", "0"],
}
latch = {
type = "exec",
exec = ["pactl", "set-sink-mute", "0", "1"],
}
```
The `latch` action fires when the triggering key (`x` in this case) is
released, regardless of any other keys pressed at that time.
## Marks
Marks let you tag a window and quickly jump back to it later, similar to marks
in Vim.
### Interactive marks
The simplest way to use marks is interactively. Bind `create-mark` and
`jump-to-mark` as simple string actions:
```toml
[shortcuts]
alt-m = "create-mark"
alt-apostrophe = "jump-to-mark"
```
When you press `alt-m`, Jay waits for the next key press (e.g. `a`) and
assigns the mark to the currently focused window. When you press
`alt-apostrophe` followed by `a`, Jay focuses the marked window.
### Hard-coded marks
You can skip the interactive step by specifying a mark ID directly:
```toml
[shortcuts]
alt-shift-1 = { type = "create-mark", id.key = "1" }
alt-1 = { type = "jump-to-mark", id.key = "1" }
```
Mark IDs can be identified by a key name (`id.key`) or by an arbitrary string
(`id.name`):
```toml
[shortcuts]
alt-shift-b = { type = "create-mark", id.name = "browser" }
alt-b = { type = "jump-to-mark", id.name = "browser" }
```
Key names use Linux input event code names with the `KEY_` prefix removed, all
lowercase (see the [Linux input event codes](https://github.com/torvalds/linux/blob/master/include/uapi/linux/input-event-codes.h)).
### Copying marks
The `copy-mark` action copies a mark from one ID to another:
```toml
[shortcuts]
alt-c = { type = "copy-mark", src.key = "a", dst.name = "backup" }
```
## Named actions
Named actions provide another layer of reuse. Define them in the `[actions]`
table and reference them with `$name`:
```toml
[actions]
my-layout = [
"split-horizontal",
{ type = "exec", exec = "alacritty" },
]
[shortcuts]
alt-l = "$my-layout"
```
You can redefine named actions at runtime using the `define-action` and
`undefine-action` parameterized actions:
```toml
[shortcuts]
alt-shift-q = {
type = "define-action",
name = "my-layout",
action = "quit",
}
```
## Virtual outputs
Virtual outputs can be created and removed via actions. A virtual output has
the connector name `VO-{name}` and the serial number `{name}`. A newly created
virtual output is initially disabled.
```toml
[shortcuts]
alt-shift-v = {
type = "create-virtual-output",
name = "screen-share",
}
alt-shift-x = {
type = "remove-virtual-output",
name = "screen-share",
}
```
You can pre-configure the virtual output using connector and output match
rules:
```toml
[[connectors]]
match.name = "VO-screen-share"
enabled = true
[[outputs]]
match.connector = "VO-screen-share"
mode = {
width = 1920,
height = 1080,
refresh-rate = 120.0,
}
```
## Actions in window rules
When certain simple actions are used inside a [window rule](../window-rules.md),
they apply to the **matched window** instead of the focused window. The
affected actions are: `move-left`, `move-down`, `move-up`, `move-right`,
`split-horizontal`, `split-vertical`, `toggle-split`, `tile-horizontal`,
`tile-vertical`, `show-single`, `show-all`, `toggle-fullscreen`,
`enter-fullscreen`, `exit-fullscreen`, `close`, `toggle-floating`, `float`,
`tile`, `toggle-float-pinned`, `pin-float`, and `unpin-float`.
Similarly, `kill-client` applies to the matched window's client in a window
rule, or to the matched client in a client rule.

View file

@ -0,0 +1,104 @@
# Startup Actions
Jay provides hooks that run actions at specific points during compositor
startup and during idle transitions.
## on-graphics-initialized
This hook runs after the GPU has been initialized and the compositor is ready
to display graphical content. It is the right place to start **graphical
applications** such as notification daemons, system tray bridges, status bars,
and similar programs.
```toml
on-graphics-initialized = { type = "exec", exec = "mako" }
```
To start multiple programs, use an array of actions:
```toml
on-graphics-initialized = [
{ type = "exec", exec = "mako" },
{ type = "exec", exec = "wl-tray-bridge" },
]
```
> [!NOTE]
> The built-in default configuration starts [mako](https://github.com/emersion/mako)
> (notification daemon) and [wl-tray-bridge](https://github.com/mahkoh/wl-tray-bridge)
> (system tray bridge) in `on-graphics-initialized`. Once you create a config
> file, these defaults are replaced -- include them in your config if you want
> to keep them.
This hook runs when the config is first loaded after compositor startup. It
does **not** re-run on config reload.
## on-startup
This hook runs as early as possible when the compositor starts -- **before**
graphics are initialized. Do not start graphical applications here; they will
likely fail to connect to the display.
Use `on-startup` for tasks like setting environment variables or other
non-graphical initialization:
```toml
on-startup = {
type = "set-env",
env = { XDG_CURRENT_DESKTOP = "jay" },
}
```
This hook has **no effect on config reload** -- it only runs once when the
compositor first starts.
## on-idle
This hook runs when the compositor transitions to the idle state (i.e., after
the configured idle timeout expires with no user input). The most common use
case is starting a screen locker.
```toml
on-idle = {
type = "exec",
exec = {
prog = "swaylock",
privileged = true,
},
}
```
> [!NOTE]
> Screen lockers need `privileged = true` to access the privileged Wayland
> protocols required for locking the session.
You can combine idle with a grace period. The idle timeout and grace period are
configured separately in the `[idle]` section (see [Idle & Screen
Locking](idle.md)):
```toml
idle = { minutes = 10 }
on-idle = {
type = "exec",
exec = {
prog = "swaylock",
privileged = true,
},
}
```
Like the other hooks, `on-idle` accepts arrays of actions:
```toml
on-idle = [
{ type = "exec", exec = ["notify-send", "Going idle..."] },
{
type = "exec",
exec = {
prog = "swaylock",
privileged = true,
},
},
]
```

View file

@ -0,0 +1,97 @@
# Status Bar
Jay includes a built-in bar that displays workspace tabs, status text, tray
icons (via [wl-tray-bridge](https://github.com/mahkoh/wl-tray-bridge)), and a
clock. The status text is provided by an external program that you configure
in the `[status]` table.
## Configuring a Status Program
The `[status]` table has three fields:
`format`
: Message format: `plain`, `pango`, or `i3bar`. Optional.
`exec`
: How to start the program (string, array, or table). Required.
`i3bar-separator`
: Separator between i3bar components (default `" | "`). Optional.
### Format
`plain`
: Plain text output
`pango`
: Output containing Pango markup
`i3bar`
: JSON output in i3bar protocol format
### Exec
The `exec` field accepts the same forms used elsewhere in the configuration:
- **String** -- the program name: `exec = "i3status"`
- **Array** -- program and arguments: `exec = ["i3status", "-c", "~/.config/i3status/config"]`
- **Table** -- full control over program, arguments, and environment (see the
spec for details)
## Example: i3status
[i3status](https://i3wm.org/i3status/) is a popular status line generator.
```toml
[status]
format = "i3bar"
exec = "i3status"
```
> [!NOTE]
> i3status defaults to plain-text output. You must explicitly configure it to
> use i3bar format by adding `output_format = "i3bar"` to your
> `~/.config/i3status/config`:
>
> ```
> general {
> output_format = "i3bar"
> }
> ```
## Example: Custom Script
A simple shell script that prints the date every second:
```toml
[status]
format = "plain"
exec = { shell = "while true; do date '+%Y-%m-%d %H:%M:%S'; sleep 1; done" }
```
## Changing the Status Program at Runtime
The `set-status` action lets you switch the status program from a shortcut.
Omit the `status` field to reset the status text to empty.
```toml
[shortcuts]
# Switch to i3status
alt-F10 = {
type = "set-status",
status = {
format = "i3bar",
exec = "i3status",
},
}
# Clear the status text
alt-F11 = { type = "set-status" }
```
## Bar Appearance
The bar's visual appearance (height, background color, text color, position, and
font) is configured in the `[theme]` table. See the
[Theme & Appearance](theme.md) chapter for details. The bar can be shown or
hidden with the `show-bar` top-level setting or the `toggle-bar` action.

View file

@ -0,0 +1,205 @@
# Theme & Appearance
Jay's visual appearance -- colors, fonts, sizes, and layout -- is controlled by
the `[theme]` table and a handful of top-level toggles. Every setting described
here can also be adjusted at runtime from the control center's **Look and Feel**
pane, which provides color pickers and live preview.
## Colors
Colors are specified as hex strings in one of four formats:
`#rgb` (e.g. `#f00`)
: Short RGB
`#rrggbb` (e.g. `#ff0000`)
: Full RGB
`#rgba` (e.g. `#f008`)
: Short RGB + alpha
`#rrggbbaa` (e.g. `#ff000080`)
: Full RGB + alpha
The available color keys in the `[theme]` table are:
`bg-color`
: Desktop background
`bar-bg-color`
: Bar background
`bar-status-text-color`
: Status text in the bar
`border-color`
: Borders between tiled windows
`focused-title-bg-color`
: Background of the focused window's title
`focused-title-text-color`
: Text color of the focused window's title
`unfocused-title-bg-color`
: Background of unfocused window titles
`unfocused-title-text-color`
: Text color of unfocused window titles
`focused-inactive-title-bg-color`
: Background of focused-but-inactive titles
`focused-inactive-title-text-color`
: Text color of focused-but-inactive titles
`attention-requested-bg-color`
: Background of titles that have requested attention
`captured-focused-title-bg-color`
: Background of focused titles that are being recorded
`captured-unfocused-title-bg-color`
: Background of unfocused titles that are being recorded
`separator-color`
: Separator between title bars and window content
`highlight-color`
: Accent color used to highlight parts of the UI
"Focused-inactive" refers to a window that was most recently focused in its
container but whose container is not the active one. The "captured" colors apply
when a window is being recorded (e.g. via screen sharing).
### Example
```toml
[theme]
bg-color = "#1e1e2e"
bar-bg-color = "#181825"
bar-status-text-color = "#cdd6f4"
border-color = "#313244"
focused-title-bg-color = "#89b4fa"
focused-title-text-color = "#1e1e2e"
unfocused-title-bg-color = "#313244"
unfocused-title-text-color = "#cdd6f4"
attention-requested-bg-color = "#f38ba8"
highlight-color = "#f5c2e7"
```
## Sizes
`border-width`
: Width of borders between windows (px)
`title-height`
: Height of window title tabs (px)
`bar-height`
: Height of the bar (px). Defaults to the same as `title-height`.
`bar-separator-width`
: Width of the bar's bottom separator (px). Default: `1`.
```toml
[theme]
border-width = 2
title-height = 24
bar-height = 28
```
## Fonts
`font`
: General font for the compositor
`title-font`
: Font used in window title bars. Defaults to the same as `font`.
`bar-font`
: Font used in the status bar. Defaults to the same as `font`.
```toml
[theme]
font = "JetBrains Mono 10"
title-font = "Inter 10"
bar-font = "Inter 10"
```
## Bar Position
The `bar-position` field controls whether the bar appears at the top or bottom
of each output. The default is `top`.
```toml
[theme]
bar-position = "bottom"
```
## Changing the Theme at Runtime
Use the `set-theme` action in a shortcut to change theme properties on the fly:
```toml
[shortcuts]
alt-F9 = { type = "set-theme", theme.bg-color = "#000000" }
```
Only the fields you include are changed; everything else stays the same.
## Showing and Hiding UI Elements
These top-level settings control whether the bar and title bars are visible:
`show-bar`
: Show the built-in status bar. Default: `true`.
`show-titles`
: Show window title bars. Default: `true`.
```toml
show-bar = false
show-titles = false
```
Corresponding actions let you toggle these at runtime:
`show-bar`
: Shows the bar
`hide-bar`
: Hides the bar
`toggle-bar`
: Toggles bar visibility
`show-titles`
: Shows title bars
`hide-titles`
: Hides title bars
`toggle-titles`
: Toggles title bars
```toml
[shortcuts]
alt-b = "toggle-bar"
alt-t = "toggle-titles"
```
## Workspace Display Order
The `workspace-display-order` top-level setting controls how workspace tabs
appear in the bar:
`manual`
: Workspaces can be reordered by dragging (default)
`sorted`
: Workspaces are displayed in alphabetical order
```toml
workspace-display-order = "sorted"
```

View file

@ -0,0 +1,91 @@
# Xwayland
Xwayland provides compatibility with legacy X11 applications inside a Wayland
session. Jay starts Xwayland automatically by default.
## Configuration
The `[xwayland]` table controls Xwayland behavior:
`enabled`
: Whether Xwayland is started. Default: `true`.
`scaling-mode`
: How X11 windows are scaled on HiDPI outputs. Default: `default`.
```toml
[xwayland]
enabled = true
scaling-mode = "default"
```
### Scaling Modes
`default`
: Render at the lowest scale, then upscale to other outputs
`downscaled`
: Render at the highest integer scale, then downscale to match each output
The `downscaled` mode produces sharper text and UI on HiDPI monitors but has a
significant performance cost. For example, on a 3840x2160 output at 1.5x scale,
a fullscreen X11 window would be rendered at 5120x2880 (integer scale 2) and
then downscaled -- roughly doubling the pixel count. This mode also requires
X11 applications to handle scaling themselves (e.g. `GDK_SCALE=2`).
## CLI
You can inspect and change Xwayland settings from the command line:
```shell
~$ jay xwayland status
~$ jay xwayland set-scaling-mode default
~$ jay xwayland set-scaling-mode downscaled
```
Xwayland settings are also available in the control center's **Xwayland** pane.
## Disabling Xwayland
If you don't need X11 compatibility, disabling Xwayland avoids starting the
X server entirely:
```toml
[xwayland]
enabled = false
```
## Matching X11 Windows in Rules
Window rules can target X11 windows using properties that only exist on X
clients. These fields are available in the `match` table of `[[windows]]`
rules:
`x-class` / `x-class-regex`
: Match the X11 WM_CLASS class (verbatim or regex)
`x-instance` / `x-instance-regex`
: Match the X11 WM_CLASS instance (verbatim or regex)
`x-role` / `x-role-regex`
: Match the X11 WM_WINDOW_ROLE (verbatim or regex)
For example, to float all GIMP tool windows:
```toml
[[windows]]
match.x-class = "Gimp"
match.x-role-regex = "gimp-(toolbox|dock)"
initial-tile-state = "floating"
```
### Matching at the Client Level
Client rules support the `is-xwayland` field to match (or exclude) the
Xwayland client itself:
```toml
[[clients]]
match.is-xwayland = true
# ... grant capabilities, etc.
```

608
book/src/control-center.md Normal file
View file

@ -0,0 +1,608 @@
# Control Center
The control center is Jay's built-in graphical interface for inspecting and
modifying compositor settings. It provides a convenient alternative to editing
configuration files or running CLI commands -- most settings that can be changed
in `config.toml` or via the CLI can also be changed here.
> [!NOTE]
> Changes made in the control center are not persisted across compositor
> restarts. To make settings permanent, add them to your `config.toml`.
> [!TIP]
> The control center consumes GPU and CPU resources while open. Close it when
> not in use to avoid reducing compositor performance.
## Opening the Control Center
- Press `alt-c` (the default shortcut for the `open-control-center` action).
- Run the CLI command:
```shell
~$ jay control-center
```
## Interface Overview
The control center window has a sidebar on the left listing all available panes
and a central panel area on the right.
- **Click** a pane name in the sidebar to open it.
- **Multiple panes** can be open at the same time -- they appear as tabs in the
panel area.
- **Drag** pane tabs to rearrange them or to split the panel area into
side-by-side or stacked layouts.
- **Close** a pane by clicking its X button or middle-clicking its tab.
## Panes
### Compositor
General information and top-level controls for the running compositor.
Repository
: Link to the Jay GitHub repository
Version
: The running Jay version
PID
: The compositor process ID
WAYLAND_DISPLAY
: The Wayland socket name (shown when available)
Config DIR
: Path to the active configuration directory (shown when available)
Libei Socket
: Toggle the libei input emulation socket
LIBEI_SOCKET
: The socket name (shown when the Libei Socket toggle is enabled)
Workspace Display Order
: Dropdown to select how workspaces are ordered in the bar
Log Level
: Dropdown to change the active log level at runtime (shown when the logger is available)
Log File
: Click to copy the log file path to the clipboard (shown when the logger is available)
Buttons at the bottom:
- **Quit** -- stop the compositor.
- **Reload Config** -- reload the configuration file.
- **Switch to VT** -- switch to another virtual terminal (with a numeric input
to select which one).
### Outputs
The Outputs pane is the largest and most interactive pane. It has two sub-views:
a visual **arrangement editor** and per-connector **settings**.
#### Arrangement editor
A 2D preview of your monitor layout. Monitors are drawn as labeled rectangles
at their configured positions and sizes.
- **Click** a monitor to select it (highlighted with a shadow).
- **Drag** a selected monitor to reposition it.
- **Scroll** to zoom in and out.
- **Middle-click drag** or **right-click drag** to pan the viewport.
- **Arrow keys** nudge the selected monitor by 1 pixel.
- **Snap to neighbor** -- when enabled, dragged monitors snap to the edges of
neighboring monitors within a 10-pixel threshold. Hold **Shift** to
temporarily invert the snapping behavior.
- **Guide lines** -- optional horizontal and vertical lines at the edges of all
monitors, helping you align them precisely.
A **Zoom To Fit** checkbox in the top bar auto-scales the view to fit all
monitors. It is disabled when you manually pan or zoom.
Arrangement settings (accessible via the Settings button):
Show guide lines
: Draw alignment guide lines
Snap to neighbor
: Snap edges when dragging (hold Shift to invert)
Show arrangement area
: Toggle the visual arrangement sub-pane
Layout
: How the arrangement and settings are split: Auto, Vertical, or Horizontal
Show disconnected heads
: Include outputs that are no longer connected
Show disabled heads
: Include outputs that are disabled
#### Staged changes
Changes made in the Outputs pane are **staged** -- they are not applied
immediately. Three buttons in the top bar control the workflow:
- **Test** -- validates the staged changes against the display backend without
applying them. Errors are shown in the pane.
- **Commit** -- applies all staged changes. The pane title shows `Outputs (*)`
when there are uncommitted changes.
- **Reset** -- discards all staged changes and reverts to the live state.
This is displayed as a checkbox; checking it resets the staged changes.
When a staged value differs from the live value, the current (live) value is
shown alongside with a `^ current` annotation.
#### Per-connector settings
Each connected display appears as a collapsible section with the connector name,
manufacturer, and model. Inside:
Serial Number
: Read-only identifier
Enabled
: Toggle the connector on or off
Position
: X and Y coordinates in compositor space
Scale
: Fractional scaling factor, with +/- buttons for fine adjustment
Mode
: Resolution and refresh rate (dropdown when multiple modes are available)
Physical Size (mm)
: Read-only physical dimensions in millimeters
Size
: Read-only computed pixel dimensions of the output
Transform
: Rotation and mirroring (none, rotate-90, rotate-180, rotate-270, flip, and flipped rotations)
Custom Brightness
: Toggle whether to use a custom SDR content brightness
Brightness
: Brightness value in cd/m^2 (shown when Custom Brightness is enabled)
Colorimetry
: Color space (depends on monitor capabilities)
EOTF
: Transfer function (depends on monitor capabilities)
Format
: Framebuffer pixel format
Tearing
: Tearing mode. When set to a "Fullscreen" mode, a **Limit Windows** checkbox appears; inside that, a **Requests Tearing** checkbox filters by whether the window has requested tearing.
VRR Active
: Read-only indicator of whether VRR is currently active (only shown when the monitor supports VRR)
VRR
: Variable refresh rate mode (only shown when the monitor supports VRR). When set to a "Fullscreen" mode, a **Limit Windows** checkbox appears; inside that, a **Limit Content Types** checkbox enables filtering by content type (**Photos**, **Videos**, **Games** checkboxes).
Non-desktop
: Read-only indicator (Yes/No) of whether the connector is inherently non-desktop
Override
: Force the connector to be treated as desktop or non-desktop
Blend Space
: How colors are blended during compositing (sRGB or linear)
Use Native Gamut
: Use the display's advertised color primaries instead of assuming sRGB
Native Gamut
: Read-only CIE xy primaries for red, green, blue, and white point
Limit Cursor HZ
: Toggle to limit cursor-triggered refresh rate when VRR is active
Cursor HZ
: Cursor refresh rate value (shown when Limit Cursor HZ is enabled)
Flip Margin (ms)
: Read-only page-flip margin for this connector
### Virtual Outputs
Manage headless virtual outputs. These are useful for screen sharing, testing,
or running applications on a display without a physical monitor.
- View the list of existing virtual outputs.
- **Add** a new virtual output by entering a name and clicking Add.
- **Remove** an existing virtual output by clicking its X button.
### GPUs
Inspect and configure graphics cards (DRM devices). Each GPU appears as a
collapsible section showing its device path and model name.
Vendor
: Read-only GPU vendor name
Model
: Read-only GPU model name
Devnode
: Read-only device path
Syspath
: Read-only sysfs path
PCI ID
: Read-only vendor:model in hex
Dev
: Read-only major:minor device numbers
API
: Dropdown to select the graphics API -- Vulkan (recommended) or the legacy OpenGL renderer
Primary Device
: Checkbox to make this GPU the render device
Direct Scanout
: Toggle direct scanout (bypasses composition for lower latency)
Flip Margin
: Adjust the page-flip margin in milliseconds, with +/- buttons for 0.1 ms steps
Connectors
: List of display connectors attached to this GPU
### Input
The Input pane is divided into per-seat and per-device sections.
#### Per-seat settings
Each seat (typically just `default`) appears as a collapsible section:
Repeat Rate
: Key repeat speed, with +/- 20 buttons
Repeat Delay
: Initial delay before key repeat begins, with +/- 20 buttons
Cursor Size
: Size of the seat cursor in pixels
Simple IM
: Toggle the built-in XCompose-based input method
Hardware Cursor
: Toggle hardware cursor rendering
Pointer Revert Key
: Text field for the keysym name of the cancel key
Focus Follows Mouse
: Toggle whether moving the pointer over a window gives it focus
Fallback Output Mode
: Dropdown to choose between cursor-based and focus-based output selection
Below the settings grid:
- **Focus History** -- checkboxes for "Only Visible" and "Same Workspace".
- **Reload Simple IM** -- button to reload XCompose files without restarting.
##### Keymap management
Each seat has a full keymap management section:
- **Copy Keymap** -- copies the current keymap text to the clipboard.
- **Load Default Keymap** -- restores the compositor's default keymap.
- **Backup / Restore Keymap** -- save and restore a keymap backup.
- **Load Keymap from Clipboard** -- paste a keymap from the clipboard.
- **Create Keymap from Names** -- build a keymap from RMLVO (Rules, Model,
Layout, Variant, Options) fields. Rules and Model have a text input and a
"Default" checkbox; Layouts, Variants, and Options have text inputs only.
Click **Load** to apply.
#### Per-device settings
Each input device appears as a collapsible section. The available settings
depend on the device's capabilities:
Seat
: Dropdown to assign the device to a seat, with a Detach button. Shown for all devices.
Syspath / Devnode
: Read-only device paths. Shown for all devices.
Capabilities
: Read-only list (e.g. Keyboard, Pointer, Touch). Shown for all devices.
Natural Scrolling
: Toggle scroll direction. Shown for devices that support it.
Scroll Distance (px)
: Pixels per legacy scroll event. Shown for pointer devices.
Accel Profile
: Dropdown: Flat or Adaptive. Shown for devices with acceleration.
Accel Speed
: Numeric input (0.0 to 1.0). Shown for devices with acceleration.
Click Method
: Dropdown: none, button-areas, clickfinger. Shown for devices that support it.
Tap Enabled
: Toggle tap-to-click. Shown for touchpads.
Tap Drag Enabled
: Toggle tap-and-drag. Shown for touchpads.
Tap Drag Lock Enabled
: Toggle tap-drag lock. Shown for touchpads.
Left Handed
: Swap primary and secondary buttons. Shown for devices that support it.
Middle Button Emulation
: Simultaneous left+right produces middle click. Shown for devices that support it.
Output
: Dropdown to map the device to a specific output (only has effect for touch and tablet devices), with a Detach button. Shown for all devices.
Transform Matrix
: 2x2 matrix applied to relative motion. Shown for pointer devices.
Calibration Matrix
: 2x3 matrix for absolute input calibration. Shown for devices that support it.
Device Keymap
: Override the seat keymap for this device, with full keymap management UI and a "Use Seat Keymap" button to revert. Shown for keyboards.
### Idle
Configure the screensaver and idle behavior:
Interval
: Minutes and seconds of inactivity before the on-idle action fires
Grace period
: Minutes and seconds of the warning phase (screen goes black but is not yet locked)
Inhibitors
: Collapsible list showing which applications are currently preventing idle (e.g. video players), with a count in the header
### Look and Feel
Visual customization with live preview. Changes take effect immediately.
Show Bar
: Toggle the status bar
Bar Position
: Dropdown to select the bar position
Show Titles
: Toggle window title bars
Primary Selection
: Toggle middle-click paste (requires application restart to take effect)
UI Drag
: Toggle whether workspaces and tiles can be dragged
UI Drag Threshold (px)
: Minimum distance in pixels before a drag begins
Float Pin Icon
: Show the pin icon on floating windows even when not pinned
Float Above Fullscreen
: Show floating windows above fullscreen windows
Font
: Text field for the main compositor font family
Title Font
: Override font for window title bars (empty = use main font)
Bar Font
: Override font for the status bar (empty = use main font)
Three reset buttons at the bottom: **Reset Sizes**, **Reset Colors**, and
**Reset Fonts**.
#### Sizes
A collapsible section with numeric inputs for every theme size: border widths,
title heights, bar height, gaps, and other spacing values.
#### Colors
A collapsible section with **color pickers** for every theme color. Click a
color swatch to open a full RGBA color picker with sliders and hex input.
This includes colors for backgrounds, borders, text, the status bar, focused
and unfocused windows, attention indicators, and more.
### Clients
Inspect and manage connected Wayland clients.
A **Filter** toggle at the top enables the composable filter builder (see
[Filtering](#filtering) below). When filtering is off, all clients are shown.
Each client appears as a collapsible section showing its ID and process name.
Expand it to see:
ID
: Client identifier
PID
: Process ID
UID
: User ID
comm
: Process name
exe
: Executable path
Sandboxed
: Whether the client is sandboxed (only shown for sandboxed clients)
Secure
: Whether the client uses the privileged socket (only shown for secure clients)
Xwayland
: Shown only for X11 clients
Sandbox Engine
: Sandbox engine name (shown when sandboxed)
App ID
: Sandbox application ID (shown when sandboxed)
Instance ID
: Sandbox instance ID (shown when sandboxed)
Tag
: The connection tag, if any
Kill
: Button to forcefully disconnect the client
Capabilities
: Collapsible list of effective Wayland capabilities
Windows
: Collapsible list of all windows owned by this client
Click the **open in new pane** icon on any client to open a dedicated pane for
that client, allowing you to keep it visible while browsing other panes.
### Window Search
Search and filter windows across the compositor using the composable filter
builder (see [Filtering](#filtering) below).
Each matching window appears as a collapsible section showing its title. Expand
it to see:
ID
: Window identifier
Title
: Window title
Workspace
: Which workspace the window is on
Type
: Container, xdg_toplevel, X Window, or Placeholder
Tag
: Toplevel tag (set via window rules); only shown for xdg_toplevel windows
X11 properties
: Class, Instance, and Role (only shown for Xwayland windows)
App ID
: Application identifier
Floating
: Whether the window is floating
Visible
: Whether the window is visible
Urgent
: Whether the window has the urgency flag
Fullscreen
: Whether the window is fullscreen
Content Type
: The content type hint (photo, video, game), if set
Client
: Full client details (same as the Clients pane)
Click the **open in new pane** icon on any window to open a dedicated pane for
that window.
### Xwayland
Manage the Xwayland compatibility layer for running X11 applications:
Enabled
: Toggle Xwayland on or off
Scaling Mode
: Dropdown: `default` or `downscaled` (renders at highest integer scale then downscales for sharper text on HiDPI)
DISPLAY
: Read-only X11 display number (only shown when Xwayland is running)
Running
: Whether Xwayland is currently running
PID
: Xwayland process ID (only shown when Xwayland is running)
Kill
: Button to forcefully terminate Xwayland (only shown when Xwayland is running)
Client
: Collapsible section with full client details for the Xwayland process (only shown when Xwayland is running)
### Color Management
Configure the Wayland color management protocol:
Enabled
: Toggle the color management protocol for clients
Available
: Read-only indicator of whether color management is available with the current renderer and hardware
## Filtering
The **Clients** and **Window Search** panes share a composable filter system
for narrowing down results. The filter builder works as follows:
At the top level, select a combinator or a leaf criterion from the dropdown:
- **Not** -- inverts a single child criterion.
- **All** -- all child criteria must match (AND).
- **Any** -- at least one child criterion must match (OR).
- **Exactly(n)** -- exactly *n* child criteria must match (with a numeric input
for *n*).
Compound criteria contain a list of children. Click **Add** to append a new
criterion; click the X button on any child to remove it. Criteria can be nested
to arbitrary depth.
Leaf criteria vary by context:
**Client criteria:** Comm, Exe, Tag, Sandbox Engine, Sandbox App ID, Sandbox
Instance ID (all regex-matched text fields with a "Regex" checkbox), Sandboxed,
Is Xwayland (boolean), UID, PID (numeric inputs).
**Window criteria:** Title, App ID, Tag, Workspace, X Class, X Instance, X Role
(all regex-matched text fields), Floating, Visible, Urgent, Fullscreen
(boolean), Content Types (checkboxes for Photo, Video, Game), and **Client**
(a nested client criterion builder for filtering by the owning client's
properties).
Text-matching criteria have a **Regex** checkbox. When unchecked, the input is
matched as a literal string. When checked, it is treated as a regular
expression. Invalid regex patterns show an error message.

290
book/src/features.md Normal file
View file

@ -0,0 +1,290 @@
# Features
This chapter provides a high-level overview of what Jay can do. Each feature
links to the chapter where it is covered in detail.
## Configuration
Jay can be configured via:
- a **declarative TOML file**, or
- a **shared library** that gets injected into the compositor for programmatic
control.
Most users will use the TOML file. The configuration supports composable
actions, [input modes](input-modes.md) for vim-style modal keybindings, and
powerful [match rules](window-rules.md). See
[Configuration Overview](configuration/index.md) for details on getting started
and how the config file works.
## i3 Look and Feel
Jay provides an i3-inspired tiling layout with manual tiling,
horizontal/vertical splits, fullscreen, and floating windows. Its appearance is
based on the default i3 look and feel.
Colors, sizes, and fonts can all be customized. See
[Theme & Appearance](configuration/theme.md) for details, and
[Tiling](tiling.md) and [Floating Windows](floating.md) for how window
management works.
## Stability
Jay has been stable for a long time. Crashes and incorrect behavior in released
versions are rare.
Jay also aims to be forward and backward compatible for existing setups,
allowing you to upgrade or downgrade the compositor without having to adjust
your configuration.
There is a small but growing integration test suite that is used to ensure this.
## Command-Line Interface
Jay has a comprehensive CLI that can be used to inspect and configure the
compositor at runtime:
```
~$ jay
A wayland compositor
Usage: jay [OPTIONS] <COMMAND>
Commands:
run Run the compositor
config Create/modify the toml config
generate-completion Generate shell completion scripts for jay
log Open the log file
set-log-level Sets the log level
quit Stop the compositor
unlock Unlocks the compositor
screenshot Take a screenshot
idle Inspect/modify the idle (screensaver) settings
run-privileged Run a privileged program
run-tagged Run a program with a connection tag
seat-test Tests the events produced by a seat
portal Run the desktop portal
randr Inspect/modify graphics card and connector settings
input Inspect/modify input settings
xwayland Inspect/modify xwayland settings
color-management Inspect/modify the color-management settings
clients Inspect/manipulate the connected clients
tree Inspect the surface tree
control-center Opens the control center
version Prints the Jay version and exits
pid Prints the Jay PID and exits
help Print this message or the help of the given subcommand(s)
Options:
--log-level <LOG_LEVEL> The log level [default: info] [possible values: trace, debug, info, warn, error, off]
-h, --help Print help
```
See the full [Command-Line Interface](cli.md) reference for details.
## Control Center
Jay includes a built-in GUI control center (opened with `alt-c`) for managing
outputs, input devices, GPUs, idle settings, color management, and more --
without editing the config file. See [Control Center](control-center.md).
## Multi-Monitor Support
Jay can be used with multiple monitors with hot-plug and hot-unplug support.
When a monitor is unplugged, all of its workspaces are automatically moved to
one of the remaining monitors. When the monitor is plugged in again, these
workspaces are restored.
See [Outputs (Monitors)](configuration/outputs.md) for configuration options.
## Multi-GPU Support
Jay can be used with multiple GPUs and monitors connected to different GPUs.
One GPU is always used for rendering the desktop. You can change this GPU at
runtime.
See [GPUs](configuration/gpu.md) for details.
## Screen Sharing
Jay supports screen sharing via xdg-desktop-portal. Three capture modes are
available:
- **Window capture** -- share a single window.
- **Output capture** -- share an entire monitor.
- **Workspace capture** -- like output capture, but only one workspace is shown.
See [Screen Sharing](screen-sharing.md) for setup instructions.
## Screen Locking
Jay can automatically lock your screen and disable outputs after inactivity.
See [Idle & Screen Locking](configuration/idle.md) for configuration options.
## Notifications
Jay supports the `zwlr_layer_shell_v1` protocol used by notification daemons
such as [mako](https://github.com/emersion/mako), which is launched
automatically by the default configuration.
## Fractional Scaling
Jay supports per-monitor fractional scaling. Scale factors can be set per
output in the config file or at runtime via the control center and CLI.
See [Outputs (Monitors)](configuration/outputs.md) for details.
## OpenGL and Vulkan
Jay can use either OpenGL or Vulkan for rendering. Vulkan offers better
performance and memory usage but OpenGL is still provided for older hardware.
You can change the rendering API at runtime without restarting the compositor.
See [GPUs](configuration/gpu.md) for details.
## Explicit Sync
Jay supports explicit sync for compatibility with Nvidia hardware. This
requires Linux 6.7 or later.
## Xwayland
Jay supports running X11 applications seamlessly through Xwayland. See
[Xwayland](configuration/xwayland.md) for configuration options.
## Clipboard Managers
Jay supports clipboard managers via the `zwlr_data_control_manager_v1` and
`ext_data_control_manager_v1` protocols.
## Privilege Separation
Jay splits protocols into unprivileged and privileged protocols. By default,
applications only have access to unprivileged protocols. This means that tools
like screen lockers, status bars, and clipboard managers need to be explicitly
granted access.
Jay provides several ways to grant privileges, from giving a program full
access to all privileged protocols down to granting individual capabilities to
specific tagged processes. See
[Granting Privileges](window-rules.md#granting-privileges) for a detailed
guide and the [Protocol Support](#protocol-support) section below for the full
list of protocols and their privilege requirements.
## Push to Talk
Jay's shortcut system allows you to execute an action when a key is pressed and
a different action when the key is released, enabling push-to-talk
functionality. See [Shortcuts](configuration/shortcuts.md) for details.
## VR
Jay supports leasing VR headsets to applications via the
`wp_drm_lease_device_v1` protocol.
## Adaptive Sync
Jay supports adaptive sync (VRR) with configurable cursor refresh rates.
See [Outputs (Monitors)](configuration/outputs.md) for per-output VRR settings.
## Tearing
Jay supports tearing presentation for games. See
[Outputs (Monitors)](configuration/outputs.md) for per-output tearing settings.
## Low Input Latency
Jay uses frame scheduling to achieve input latency as low as 1.5 ms.
## Color Management & HDR
Jay supports the Wayland color management protocol and HDR10 output with
per-monitor color space, transfer function, brightness, and blend space
controls. See [HDR & Color Management](hdr.md) for a full walkthrough.
## Night Light
Jay supports night-light applications via the
`zwlr_gamma_control_manager_v1` protocol.
## Window and Client Rules
Jay supports powerful, reactive window and client rules. Rules are
re-evaluated whenever matching criteria change (e.g. a window's title changes).
See [Window & Client Rules](window-rules.md) for details.
## Protocol Support
Jay supports a large number of Wayland protocols. Protocols marked as
**Privileged** are only accessible to applications that have been explicitly
granted access. See
[Granting Privileges](window-rules.md#granting-privileges) for how to do this.
| Protocol | Version | Privileged |
|------------------------------------------------------|---------|------------|
| ext_data_control_manager_v1 | 1 | Yes |
| ext_foreign_toplevel_image_capture_source_manager_v1 | 1 | |
| ext_foreign_toplevel_list_v1 | 1 | Yes |
| ext_idle_notifier_v1 | 2 | Yes |
| ext_image_copy_capture_manager_v1 | 1[^2] | Yes |
| ext_output_image_capture_source_manager_v1 | 1 | |
| ext_session_lock_manager_v1 | 1 | Yes |
| ext_transient_seat_manager_v1 | 1[^3] | Yes |
| ext_workspace_manager_v1 | 1 | Yes |
| jay_popup_ext_manager_v1 | 1 | |
| jay_tray_v1 | 1 | |
| org_kde_kwin_server_decoration_manager | 1 | |
| wl_compositor | 7 | |
| wl_data_device_manager | 4 | |
| wl_drm | 2 | |
| wl_fixes | 1 | |
| wl_output | 4 | |
| wl_seat | 10 | |
| wl_shm | 2 | |
| wl_subcompositor | 1 | |
| wp_alpha_modifier_v1 | 1 | |
| wp_color_manager_v1 | 2 | |
| wp_color_representation_manager_v1 | 1 | |
| wp_commit_timing_manager_v1 | 1 | |
| wp_content_type_manager_v1 | 1 | |
| wp_cursor_shape_manager_v1 | 2 | |
| wp_drm_lease_device_v1 | 1 | |
| wp_fifo_manager_v1 | 1 | |
| wp_fractional_scale_manager_v1 | 1 | |
| wp_linux_drm_syncobj_manager_v1 | 1 | |
| wp_pointer_warp_v1 | 1 | |
| wp_presentation | 2 | |
| wp_security_context_manager_v1 | 1 | |
| wp_single_pixel_buffer_manager_v1 | 1 | |
| wp_tearing_control_manager_v1 | 1 | |
| wp_viewporter | 1 | |
| xdg_activation_v1 | 1 | |
| xdg_toplevel_drag_manager_v1 | 1 | |
| xdg_toplevel_tag_manager_v1 | 1 | |
| xdg_wm_base | 7 | |
| xdg_wm_dialog_v1 | 1 | |
| zwlr_data_control_manager_v1 | 2 | Yes |
| zwlr_foreign_toplevel_manager_v1 | 3 | Yes |
| zwlr_gamma_control_manager_v1 | 1 | Yes |
| zwlr_layer_shell_v1 | 5 | No[^1] |
| zwlr_output_manager_v1 | 4 | Yes |
| zwlr_screencopy_manager_v1 | 3 | Yes |
| zwlr_virtual_pointer_manager_v1 | 2 | Yes |
| zwp_idle_inhibit_manager_v1 | 1 | |
| zwp_input_method_manager_v2 | 1 | Yes |
| zwp_linux_dmabuf_v1 | 5 | |
| zwp_pointer_constraints_v1 | 1 | |
| zwp_pointer_gestures_v1 | 3 | |
| zwp_primary_selection_device_manager_v1 | 1 | |
| zwp_relative_pointer_manager_v1 | 1 | |
| zwp_tablet_manager_v2 | 2 | |
| zwp_text_input_manager_v3 | 1 | |
| zwp_virtual_keyboard_manager_v1 | 1 | Yes |
| zxdg_decoration_manager_v1 | 2 | |
| zxdg_output_manager_v1 | 3 | |
[^1]: Sandboxes can restrict access to this protocol.
[^2]: Cursors are always composited.
[^3]: Seat creation is always rejected.

139
book/src/floating.md Normal file
View file

@ -0,0 +1,139 @@
# Floating Windows
Floating windows are not part of the tiling layout. They hover above tiled
windows and can be freely moved and resized. Any tiled window can be made
floating, and any floating window can be returned to the tiling layout.
## Toggling Between Tiled and Floating
`alt-shift-f` -- `toggle-floating`
: Toggle the focused window between tiled and floating
You can also double-click a window's title bar to toggle between the two states.
For explicit control without toggling:
```toml
[shortcuts]
alt-shift-t = "tile" # Make the focused window tiled
alt-shift-g = "float" # Make the focused window floating
```
## Moving Floating Windows
Drag a floating window's title bar to move it. The window follows the cursor
until you release the mouse button.
In [window management mode](#window-management-mode), you can left-drag
anywhere on a floating window to move it -- you do not need to target the
title bar.
## Resizing Floating Windows
Drag a floating window's border to resize it. The cursor changes to a resize
indicator when hovering over the border.
In [window management mode](#window-management-mode), you can right-drag
anywhere on a floating window to resize it.
## Pinning
A pinned floating window stays visible across workspace switches. This is
useful for things like a sticky terminal, a video player, or a chat window that
you want on screen at all times.
`pin-float`
: Pin the focused floating window
`unpin-float`
: Unpin the focused floating window
`toggle-float-pinned`
: Toggle pinning on the focused floating window
Example shortcut configuration:
```toml
[shortcuts]
alt-shift-p = "toggle-float-pinned"
```
You can also right-click a floating window's title bar to pin or unpin it.
### Pin Icon
By default, the pin icon is hidden. Enable it to get a clickable pin indicator
on floating windows:
```toml
[float]
show-pin-icon = true
```
When the pin icon is visible, click it to pin or unpin the window.
## Float Above Fullscreen
By default, floating windows are hidden below fullscreen windows. You can
change this so floating windows render above fullscreen windows:
`enable-float-above-fullscreen`
: Floating windows render above fullscreen windows
`disable-float-above-fullscreen`
: Floating windows are hidden below fullscreen windows
`toggle-float-above-fullscreen`
: Toggle the behavior
Example:
```toml
[shortcuts]
alt-shift-a = "toggle-float-above-fullscreen"
```
This is a global setting -- it affects all floating windows, not just the
focused one.
## Window Rules: Initial Tile State
You can force specific windows to start as floating (or tiled) using the
`initial-tile-state` field in a [window rule](window-rules.md):
```toml
[[windows]]
match.app-id = "pavucontrol"
initial-tile-state = "floating"
[[windows]]
match.app-id = "mpv"
initial-tile-state = "floating"
```
Valid values are `"floating"` and `"tiled"`.
## Window Management Mode
Window management mode makes it easier to move and resize windows without
needing to target specific UI elements like title bars or borders. Set the
`window-management-key` to a keysym -- holding that key activates the mode:
```toml
window-management-key = "Alt_L"
```
While the key is held:
- **Left-drag** anywhere on a floating window to move it.
- **Right-drag** anywhere on a floating window to resize it.
This mode also works with tiled windows and popups. See
[Mouse Interactions](mouse.md#window-management-mode) for the full list of
interactions available in this mode.
> [!NOTE]
> Entering window management mode disables all pointer constraints. This can be
> used to break out of games or other applications that grab the pointer, but
> it also means pointer-dependent applications will behave differently while
> the key is held.

315
book/src/hdr.md Normal file
View file

@ -0,0 +1,315 @@
# HDR & Color Management
Jay supports HDR (High Dynamic Range) output and the Wayland color management
protocol. This chapter explains how HDR works in Jay and walks through
enabling it.
## Prerequisites
HDR in Jay requires:
- The **Vulkan renderer**. The OpenGL renderer does not support color
management or HDR. Vulkan is the default when available; you can verify
with `jay randr show` (look for the "Api" field under your GPU) or switch
to it in the [control center](control-center.md) GPUs pane.
- A **monitor that supports HDR**. The display must advertise PQ (Perceptual
Quantizer) support and BT.2020 colorimetry through its EDID. Run
`jay randr show` to see which color spaces and EOTFs your display supports.
## Quick start
Enable HDR10 on a specific output using the CLI:
```shell
~$ jay randr output DP-1 colors set bt2020 pq
```
Or in your configuration file:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
color-space = "bt2020"
transfer-function = "pq"
```
Enable the color management protocol so that applications can communicate
their color spaces to the compositor:
```shell
~$ jay color-management enable
```
Or in your configuration file:
```toml
[color-management]
enabled = true
```
To return to SDR:
```shell
~$ jay randr output DP-1 colors set default default
```
> [!NOTE]
> Run `jay randr` to find connector names and serial numbers for your
> displays.
## Concepts
### Color space
The color space determines the range of colors (gamut) the output uses.
`default`
: sRGB gamut. This is the standard gamut for desktop displays.
`bt2020`
: BT.2020 gamut. A much wider gamut used by HDR10 and modern cinema standards.
### Transfer function (EOTF)
The transfer function (technically the Electro-Optical Transfer Function)
controls how encoded pixel values are mapped to display luminance.
`default`
: Gamma 2.2. The traditional SDR transfer function.
`pq`
: Perceptual Quantizer (SMPTE ST 2084). The HDR10 transfer function, designed
to cover luminance levels from 0 to 10,000 cd/m^2.
Setting `color-space = "bt2020"` and `transfer-function = "pq"` together
activates HDR10 mode. Jay sends the appropriate HDR metadata infoframe to the
display, including the display's mastering luminance and primaries from its
EDID.
### Brightness
The brightness setting controls SDR content luminance in cd/m^2. This
determines how bright non-HDR content appears on an HDR display.
With the `default` (gamma 2.2) transfer function, the default brightness is
the maximum the display supports, anchored at 80 cd/m^2. Setting brightness
below 80 makes SDR content dimmer while creating HDR headroom above it.
With the `pq` transfer function, the default brightness is 203 cd/m^2 (the
ITU reference level for SDR content in an HDR signal).
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
color-space = "bt2020"
transfer-function = "pq"
brightness = 250
```
```shell
~$ jay randr output DP-1 brightness 250
~$ jay randr output DP-1 brightness default
```
> [!NOTE]
> Brightness has no effect unless the Vulkan renderer is in use.
### Blend space
When Jay composites overlapping translucent surfaces, the blend space
determines in which color space the alpha blending is performed.
`srgb`
: Classic desktop blending in sRGB space. This is the default and matches the
behavior of most other compositors.
`linear`
: Blending in linear light. This is physically correct but can make
semi-transparent elements appear brighter than expected.
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
blend-space = "linear"
```
```shell
~$ jay randr output DP-1 blend-space linear
```
### Native gamut
By default, Jay assumes all displays use sRGB primaries. This matches the
behavior of most other compositors, but most modern displays actually have a
wider gamut (e.g. 95% DCI-P3 coverage). Because the display interprets
sRGB-intended color values in its wider native gamut, colors appear more
saturated than they should.
Setting `use-native-gamut = true` tells Jay the display's actual primaries
from its EDID, allowing the compositor to map colors correctly. This produces
less saturated but more accurate colors, and allows color-managed applications
to address the full display gamut.
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
use-native-gamut = true
```
```shell
~$ jay randr output DP-1 use-native-gamut true
```
This setting has no effect when the display is already operating in a wide
color space like BT.2020.
### Framebuffer format
The default framebuffer format (`xrgb8888`) uses 8 bits per channel, which
can cause banding in HDR content. For HDR, consider a higher-precision format:
- `xrgb2101010` or `argb2101010` -- 10 bits per channel. Good balance between
precision and performance.
- `xbgr16161616f` or `abgr16161616f` -- 16-bit floating point per channel.
Maximum precision.
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
color-space = "bt2020"
transfer-function = "pq"
format = "xrgb2101010"
```
```shell
~$ jay randr output DP-1 format set xrgb2101010
```
> [!TIP]
> Run `jay randr show --formats` to see which formats your display supports.
## Color management protocol
The Wayland color management protocol (`wp_color_manager_v1`) lets
applications communicate their color space to the compositor. This enables
color-aware applications to render correctly regardless of the output's color
settings -- for example, an image viewer can tag its surfaces as sRGB and Jay
will map the colors to the output's actual gamut and transfer function.
The protocol is **disabled by default** and must be enabled explicitly:
```toml
[color-management]
enabled = true
```
Or at runtime:
```shell
~$ jay color-management enable
```
> [!NOTE]
> Changing this setting has no effect on applications that are already running.
> They must be restarted to discover the protocol.
Color management availability requires both the protocol to be enabled **and**
the Vulkan renderer to be active. Check with:
```shell
~$ jay color-management status
```
This prints one of:
`Enabled`
: The protocol is enabled and available to clients.
`Enabled (Unavailable)`
: The protocol is enabled but unavailable -- usually because the OpenGL
renderer is in use.
`Disabled`
: The protocol is disabled.
The [control center](control-center.md) **Color Management** pane shows the
same information as a toggle and a read-only availability indicator.
### Supported capabilities
Jay's color management implementation supports:
- **Parametric image descriptions** with custom primaries, luminances, and
transfer functions (including power curves).
- **Named primaries**: sRGB, BT.2020, DCI-P3, Display P3, Adobe RGB, and
others.
- **Named transfer functions**: sRGB, PQ (ST 2084), gamma 2.2, gamma 2.4,
BT.1886, linear, and others.
- **Mastering display metadata** for HDR content.
- **Windows scRGB** compatibility.
ICC profile creation is not supported.
## Checking display capabilities
Use `jay randr show` to inspect what your display supports:
```shell
~$ jay randr show
```
The output for each connector includes:
- **Color spaces** -- which color spaces the display supports (e.g. `default`,
`bt2020`), with the current setting marked.
- **EOTFs** -- which transfer functions the display supports (e.g. `default`,
`pq`), with the current setting marked.
- **Brightness range** -- minimum and maximum luminance from the EDID, in
cd/m^2.
- **Current brightness** -- displayed if a custom value is set.
- **Blend space** -- current blend space setting.
- **Native gamut** -- the display's CIE xy primaries for red, green, blue, and
white point.
## Complete example
A typical HDR configuration for a monitor that supports HDR10:
```toml
[[outputs]]
match.serial-number = "33K03894SL0"
color-space = "bt2020"
transfer-function = "pq"
brightness = 203
format = "xrgb2101010"
blend-space = "linear"
[color-management]
enabled = true
```
This sets the output to HDR10 with 10-bit color, physically correct blending,
and enables the color management protocol so applications can communicate
their color spaces.
## Control center
The Outputs pane in the [control center](control-center.md) (`alt-c` by
default) provides GUI controls for all HDR-related settings per output:
Colorimetry, EOTF, Custom Brightness, Format, Blend Space, Use Native Gamut,
and a read-only Native Gamut display. See
[Outputs (Monitors)](configuration/outputs.md) for details on each field.
The Color Management pane has a toggle to enable/disable the protocol and a
read-only indicator showing whether color management is currently available.
## See also
- [Outputs (Monitors)](configuration/outputs.md) -- per-output configuration
reference including all color and HDR fields
- [Miscellaneous](configuration/misc.md) -- the `[color-management]` config
table
- [Command-Line Interface](cli.md) -- `jay randr output` color commands and
`jay color-management`
- [GPUs](configuration/gpu.md) -- renderer selection (Vulkan is required for
HDR)

177
book/src/input-modes.md Normal file
View file

@ -0,0 +1,177 @@
# Input Modes
Jay supports an **input mode stack** that enables vim-style modal keybindings.
When a mode is active, its shortcuts take effect instead of (or in addition to)
the top-level shortcuts. Multiple modes can be stacked, and the topmost mode
always wins.
## Defining Modes
Modes are defined in the `[modes]` table. Each mode is a named sub-table that
can contain its own `shortcuts` and `complex-shortcuts`:
```toml
[modes."move".shortcuts]
h = "move-left"
j = "move-down"
k = "move-up"
l = "move-right"
Escape = "pop-mode"
```
## Inheritance
By default, a mode inherits all shortcuts from the top-level `[shortcuts]`
table. Any key not explicitly defined in the mode falls through to the
inherited shortcuts.
To inherit from a different mode instead, set the `parent` field:
```toml
[modes."navigation"]
parent = "base"
[modes."navigation".shortcuts]
w = "focus-up"
a = "focus-left"
s = "focus-down"
d = "focus-right"
```
Here, `navigation` inherits from the mode called `base` rather than from the
top-level shortcuts.
### Unbinding Inherited Shortcuts
Use `"none"` as the action value to unbind a shortcut that would otherwise be
inherited:
```toml
[modes."locked".shortcuts]
# Disable alt-q (quit) while in this mode
alt-q = "none"
```
## Activating and Deactivating Modes
Jay provides four actions for managing the mode stack:
`push-mode`
: Push a named mode onto the stack. It stays active until explicitly removed.
`latch-mode`
: Temporarily push a mode. It auto-pops after the next shortcut fires.
`pop-mode`
: Remove the topmost mode from the stack.
`clear-modes`
: Clear the entire mode stack, returning to top-level shortcuts.
### push-mode
```toml
[shortcuts]
alt-r = { type = "push-mode", name = "resize" }
```
After pressing `alt-r`, the `resize` mode's shortcuts take effect. It remains
active until something pops it.
### latch-mode
```toml
[shortcuts]
alt-x = { type = "latch-mode", name = "navigation" }
```
After pressing `alt-x`, the `navigation` mode is pushed. The very next shortcut
that fires will use the mode's bindings, and then the mode is automatically
popped. This is ideal for **leader key** patterns.
### pop-mode
```toml
[modes."resize".shortcuts]
Escape = "pop-mode"
```
Pressing `Escape` while in `resize` mode removes it from the stack.
### clear-modes
```toml
[modes."resize".shortcuts]
ctrl-c = "clear-modes"
```
This removes all modes from the stack at once, returning to the top-level
shortcuts regardless of how many modes have been stacked.
## Practical Examples
### Move Mode
A dedicated mode for moving windows with `h`/`j`/`k`/`l`, activated by
`alt-r` and exited with `Escape`:
```toml
[shortcuts]
alt-r = { type = "push-mode", name = "move" }
[modes."move".shortcuts]
h = "move-left"
j = "move-down"
k = "move-up"
l = "move-right"
Escape = "pop-mode"
```
While in move mode, pressing `h` moves the focused window left, `l` moves it
right, and so on -- without needing a modifier key. All other inherited
shortcuts (like `alt-q` to quit) remain available. Press `Escape` to return
to normal.
### Leader Key
A leader key pattern where pressing `alt-x` followed by one key triggers a
mode shortcut, then immediately returns to normal:
```toml
[shortcuts]
alt-x = { type = "latch-mode", name = "leader" }
[modes."leader".shortcuts]
t = { type = "exec", exec = "alacritty" }
b = { type = "exec", exec = "firefox" }
f = { type = "exec", exec = "thunar" }
q = "quit"
```
Press `alt-x` then `t` to launch a terminal. Because `latch-mode` auto-pops
after one shortcut, you are immediately back to normal shortcuts.
### Nested Modes
Modes can push other modes, creating layers of context:
```toml
[shortcuts]
alt-n = { type = "push-mode", name = "navigate" }
[modes."navigate".shortcuts]
m = { type = "push-mode", name = "move" }
Escape = "pop-mode"
[modes."move".shortcuts]
h = "move-left"
l = "move-right"
Escape = "pop-mode"
```
`alt-n` enters navigation mode. From there, pressing `m` enters move mode
on top of it. `Escape` pops the current mode one level at a time. Use
`clear-modes` if you want a shortcut that returns directly to the top level.
See [spec.generated.md](https://github.com/mahkoh/jay/blob/master/toml-spec/spec/spec.generated.md) for the full
specification of `InputMode`, `push-mode`, `latch-mode`, and related actions.

162
book/src/installation.md Normal file
View file

@ -0,0 +1,162 @@
# Installation
## Compile-Time Dependencies
The following libraries must be installed before building Jay. They are linked
at build time.
| Library | Arch Linux | Fedora | Debian / Ubuntu |
|---------------|----------------|----------------------|----------------------|
| libinput | `libinput` | `libinput-devel` | `libinput-dev` |
| libgbm | `mesa` | `mesa-libgbm-devel` | `libgbm-dev` |
| libudev | `systemd-libs` | `systemd-devel` | `libudev-dev` |
| libpangocairo | `pango` | `pango-devel` | `libpango1.0-dev` |
| libfontconfig | `fontconfig` | `fontconfig-devel` | `libfontconfig-dev` |
**One-liner install commands:**
Arch Linux:
```shell
~$ sudo pacman -S libinput mesa systemd-libs pango fontconfig
```
Fedora:
```shell
~$ sudo dnf install libinput-devel mesa-libgbm-devel systemd-devel pango-devel fontconfig-devel
```
Debian / Ubuntu:
```shell
~$ sudo apt install libinput-dev libgbm-dev libudev-dev libpango1.0-dev libfontconfig-dev
```
You also need a C compiler (GCC or Clang) and the latest stable version of
Rust. Install Rust with [rustup](https://rustup.rs/):
```shell
~$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
```
## Runtime Dependencies
These libraries are loaded dynamically at runtime. Jay requires **at least one**
working renderer -- without one, no GPU can be initialized and nothing will be
displayed.
| Library | Purpose | Arch Linux | Fedora | Debian / Ubuntu |
|--------------------|-------------------------------|----------------------|---------------------------------|------------------------|
| libEGL + libGLESv2 | OpenGL renderer (legacy) | `mesa` or `libglvnd` | `mesa-libEGL` + `mesa-libGLES` | `libegl1` + `libgles2` |
| libvulkan | Vulkan renderer (recommended) | `vulkan-icd-loader` | `vulkan-loader` | `libvulkan1` |
For Vulkan, you also need the driver for your GPU:
| GPU | Arch Linux | Fedora | Debian / Ubuntu |
|--------|-----------------|-----------------------|-----------------------|
| AMD | `vulkan-radeon` | `mesa-vulkan-drivers` | `mesa-vulkan-drivers` |
| Intel | `vulkan-intel` | `mesa-vulkan-drivers` | `mesa-vulkan-drivers` |
| Nvidia | `nvidia-utils` | `xorg-x11-drv-nvidia` | `nvidia-vulkan-icd` |
### Optional Runtime Dependencies
- **Linux 6.7 or later** -- required for explicit sync (needed for Nvidia GPUs).
- **Xwayland** -- required for running X11 applications.
- **PipeWire** -- required for screen sharing.
- **logind** (part of systemd) -- required when running Jay from a virtual terminal or display manager.
## Building
### AUR (Arch Linux)
Arch Linux users can install Jay from the AUR. Two packages are available:
- **[jay](https://aur.archlinux.org/packages/jay)** -- builds the latest
released version.
- **[jay-git](https://aur.archlinux.org/packages/jay-git)** -- builds from the
latest commit on the master branch.
Install with your preferred AUR helper, for example:
```shell
~$ yay -S jay
```
The AUR packages handle all compile-time dependencies automatically.
### From crates.io (recommended)
```shell
~$ cargo install --locked jay-compositor
```
This installs the `jay` binary to `~/.cargo/bin/jay`.
### From git (latest development version)
```shell
~$ cargo install --locked --git https://github.com/mahkoh/jay.git jay-compositor
```
### From a local clone
```shell
~$ git clone https://github.com/mahkoh/jay.git
~$ cd jay
~$ cargo build --release
```
The binary is then available at `./target/release/jay`.
## CAP_SYS_NICE (Optional)
Granting `CAP_SYS_NICE` to the Jay binary can improve responsiveness when the
CPU or GPU are under heavy load:
```shell
~$ sudo setcap cap_sys_nice=p $(which jay)
```
When this capability is available, Jay will elevate its scheduler to `SCHED_RR`
(real-time round-robin) and create Vulkan queues with the highest available
priority. Both of these help Jay maintain smooth frame delivery under
contention.
Jay drops all capabilities almost immediately after startup. A dedicated thread
retains `CAP_SYS_NICE` solely for creating elevated Vulkan queues later.
> [!NOTE]
> You need to re-run the `setcap` command each time you update the Jay binary.
### SCHED_RR and config.so
`SCHED_RR` and `config.so` are mutually exclusive: running untrusted code at
real-time priority would be a security risk. Jay enforces this as follows:
- If `config.so` exists in the config directory, Jay skips the `SCHED_RR`
elevation (elevated Vulkan queues are still created).
- If Jay has already elevated to `SCHED_RR`, it refuses to load `config.so`.
You can also skip `SCHED_RR` explicitly by setting `JAY_NO_REALTIME=1`:
```shell
~$ JAY_NO_REALTIME=1 jay run
```
This still allows elevated Vulkan queues and does not affect `config.so`
loading.
The mutual exclusion can be overridden at compile time by building Jay with
`JAY_ALLOW_REALTIME_CONFIG_SO=1`.
## Recommended Applications
The following applications work well with Jay:
- **[Alacritty](https://alacritty.org/)** -- the default terminal emulator in the built-in configuration.
- **[bemenu](https://github.com/Cloudef/bemenu)** -- the default application launcher in the built-in configuration.
- **[xdg-desktop-portal-gtk4](https://github.com/mahkoh/xdg-desktop-portal-gtk4)** -- a file-picker portal with thumbnail support. Used automatically when installed.
- **[wl-tray-bridge](https://github.com/mahkoh/wl-tray-bridge)** -- shows D-Bus StatusNotifierItem applications as tray icons.
- **[mako](https://github.com/emersion/mako)** -- a notification daemon. Launched automatically by the default configuration.
- **[window-to-tray](https://github.com/mahkoh/wl-proxy/tree/master/apps/window-to-tray)** -- run most Wayland applications as tray applications (e.g. `window-to-tray pavucontrol-qt`).

46
book/src/introduction.md Normal file
View file

@ -0,0 +1,46 @@
# Introduction
> [!NOTE]
> This book was written entirely by Claude Opus 4.6 and will likely also be
> maintained in the future with the help of AI to ensure a consistent tone of
> voice, style, and quality.
>
> All contents have been reviewed by humans but there might still be some
> mistakes left. That would put it in good company since the artisanal,
> hand-crafted spec.yaml, that contains the TOML config specification, contains
> plenty of mistakes as well.
>
> The writing is of high quality in my estimation, certainly better than what I
> would have produced in any reasonable amount of time. There are few obvious
> _slop_ indicators. The AI sometimes uses superlatives ("ideal", "best") that I
> would not use myself, but I've seen this style in other pieces of
> documentation, so maybe it is just a style that aims to be approachable or to
> guide users towards good solutions.
>
> If you find something objectionable in this book, do not hesitate to open an
> issue.
Jay is a Wayland compositor for Linux with an i3-inspired tiling layout. It
supports Vulkan and OpenGL rendering, multi-GPU setups, fractional scaling,
variable refresh rate (VRR), tearing presentation, HDR, and screen sharing via
xdg-desktop-portal. X11 applications are supported through Xwayland.
Jay is configured through a declarative TOML file, with an optional advanced
mode that uses a shared library for programmatic control. A built-in
[control center](control-center.md) (opened with `alt-c`) provides a full GUI
for inspecting and changing compositor settings at runtime -- including output
arrangement, input devices, color management, GPU selection, and more. A
comprehensive command-line interface makes scripting and automation
straightforward.
See the [Features](features.md) chapter for a comprehensive overview of what
Jay can do, or jump straight to [Installation](installation.md) to get started.
## License
Jay is free software licensed under the
[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.html).
## Community
[Discord server (unofficial)](https://discord.gg/Hby736z28G)

103
book/src/mouse.md Normal file
View file

@ -0,0 +1,103 @@
# Mouse Interactions
Jay supports a range of mouse-driven interactions for managing windows,
workspaces, and layout. This page is a comprehensive reference for all of them.
## Tiling
**Resizing tiles.** Drag the separator between two tiles to resize them. The
cursor changes to a resize indicator when hovering over a separator.
**Moving tiles.** Drag a tile's title bar to move it within or between
containers. While dragging:
- Drop it onto another position in a container to rearrange tiles.
- Drop it onto a workspace tab in the bar to move it to that workspace.
- Drop it onto the bar outside any workspace tab to create a new workspace
for it.
**Double-click a tile's title** to toggle it between tiled and floating. See
[Floating Windows](floating.md) for details.
**Right-click any title** in a container to toggle mono mode (showing one window
at a time vs. all side by side). See [Tiling -- Mono Mode](tiling.md#mono-mode).
**Scroll over a title** in mono mode to cycle between the tiles in that
container.
## Floating Windows
**Drag a floating window's title** to move it.
**Drag a floating window's border** to resize it.
**Double-click a floating window's title** to toggle it back to tiled.
**Right-click a floating window's title** to pin or unpin it. Pinned floating
windows stay visible across workspace switches.
**Click the pin icon** (if visible) to pin or unpin the window. The pin icon
can be enabled with:
```toml
[float]
show-pin-icon = true
```
## Workspaces
**Scroll over the bar** to switch between workspaces on that output.
**Drag workspace titles** in the bar to reorder them. This only works in manual
display order mode (the default). See
[Workspaces -- Display Order](workspaces.md#workspace-display-order).
## Window Management Mode
Window management mode enables additional mouse interactions that do not require
targeting specific UI elements. Configure it by setting `window-management-key`
to a keysym:
```toml
window-management-key = "Alt_L"
```
**Hold the configured key** to enter window management mode. While held:
- **Left-drag** anywhere on a floating window, tile, popup, or fullscreen window
to **move** it.
- **Right-drag** anywhere on a floating window, tile, or popup to **resize** it.
This is especially useful for:
- Moving or resizing floating windows without needing to precisely target the
title bar or border.
- Moving tiled windows without targeting the title bar.
- Moving fullscreen windows (not possible without this mode).
- Resizing tiles without targeting the separator.
> [!NOTE]
> Entering window management mode disables all pointer constraints. This means
> you can use it to move the pointer out of applications that have grabbed it
> (such as games in fullscreen), but pointer-dependent applications will behave
> differently while the key is held.
## Other
**Toplevel selection.** Some actions (like screen sharing) ask you to select a
window, indicated by a purple overlay. During this selection, right-click a
tile's title to select the entire container instead of an individual tile.
**Canceling interactions.** Press `Escape` to cancel any in-progress mouse
interaction (dragging, resizing, selection, etc.). The cancel key can be changed
with the `pointer-revert-key` configuration:
```toml
# Use a different key to cancel mouse interactions
pointer-revert-key = "grave"
# Disable the cancel key entirely
pointer-revert-key = "NoSymbol"
```
The default is `Escape`.

127
book/src/running.md Normal file
View file

@ -0,0 +1,127 @@
# Running Jay
## From a Virtual Terminal
Switch to a virtual terminal (e.g. `ctrl-alt-F2`), log in, and run:
```shell
~$ jay run
```
## From a Display Manager
To make Jay appear as a session option in your display manager (GDM, SDDM,
etc.), install the session file.
If you have the repository checked out:
```shell
~$ sudo cp etc/jay.desktop /usr/share/wayland-sessions/jay.desktop
```
Otherwise, create it manually:
```shell
~$ sudo tee /usr/share/wayland-sessions/jay.desktop > /dev/null << 'EOF'
[Desktop Entry]
Name=Jay
Comment=A Wayland Compositor
Exec=jay run
Type=Application
DesktopNames=jay
EOF
```
Then log out and select **Jay** from the session list.
> [!NOTE]
> If you installed Jay via `cargo install`, the `jay` binary lives in
> `~/.cargo/bin/`. Your display manager may not include this directory in its
> `PATH`. Either add `~/.cargo/bin` to the system `PATH`, or copy/symlink the
> binary to `/usr/local/bin`:
>
> ```shell
> ~$ sudo ln -s ~/.cargo/bin/jay /usr/local/bin/jay
> ```
## The Control Center
Once Jay is running, press `alt-c` to open the
[control center](control-center.md) -- a built-in GUI that lets you inspect and
modify most compositor settings without editing config files or running CLI
commands. From the control center you can:
- Rearrange monitors with a visual drag-and-drop editor.
- Configure input devices -- acceleration, tap behavior, keymaps, and more.
- Switch GPUs and graphics APIs.
- Adjust theme colors, fonts, borders, and gaps with live color pickers.
- Manage idle timeouts and screen locking.
- Search and filter windows and clients.
- Toggle Xwayland and color management.
You can also open it from the command line with `jay control-center`. See the
[Control Center](control-center.md) chapter for a full tour of every pane.
## Default Keybindings
Jay ships with a built-in default configuration. The most important default
keybindings are listed below.
> [!NOTE]
> [Alacritty](https://alacritty.org/) and
> [bemenu](https://github.com/Cloudef/bemenu) must be installed for the default
> terminal and launcher bindings to work.
`Super_L` (left Windows key)
: Open Alacritty terminal
`alt-p`
: Open bemenu application launcher
`alt-q`
: Quit Jay
`alt-h` / `j` / `k` / `l`
: Move focus (left/down/up/right)
`alt-shift-h` / `j` / `k` / `l`
: Move focused window
`alt-d`
: Split horizontally
`alt-v`
: Split vertically
`alt-u`
: Toggle fullscreen
`alt-shift-f`
: Toggle floating
`alt-c`
: Open the control center
`alt-shift-c`
: Close focused window
`alt-t`
: Toggle split direction
`alt-m`
: Toggle mono (stacking) layout
`alt-f`
: Focus parent container
`alt-shift-r`
: Reload configuration
The defaults also include `ctrl-alt-F1` through `F12` for switching virtual
terminals, `alt-F1` through `F12` for switching workspaces, and
`alt-shift-F1` through `F12` for moving windows to workspaces.
Once you create a configuration file, the built-in defaults are entirely
replaced -- even an empty config file means no shortcuts. Run `jay config init`
to generate a config pre-populated with all the defaults. See the
[Configuration Overview](configuration/index.md) chapter for details.

111
book/src/screen-sharing.md Normal file
View file

@ -0,0 +1,111 @@
# Screen Sharing
Jay supports screen sharing via
[xdg-desktop-portal](https://github.com/flatpak/xdg-desktop-portal). Three
capture types are available:
- **Window capture** -- share a single window.
- **Output capture** -- share an entire monitor.
- **Workspace capture** -- like output capture, but only a single workspace is
shown.
## Requirements
[PipeWire](https://pipewire.org/) must be installed and running. Verify with:
```shell
~$ systemctl --user status pipewire
```
## Portal Setup
Jay implements its own portal backend for the `ScreenCast` and `RemoteDesktop`
interfaces. Two configuration files must be installed so that
`xdg-desktop-portal` knows to use Jay's backend.
### If the Repository is Checked Out
```shell
~$ sudo cp etc/jay.portal /usr/share/xdg-desktop-portal/portals/jay.portal
~$ sudo cp etc/jay-portals.conf /usr/share/xdg-desktop-portal/jay-portals.conf
```
### If Installed via cargo install
Create the files manually:
```shell
~$ sudo tee /usr/share/xdg-desktop-portal/portals/jay.portal > /dev/null << 'EOF'
[portal]
DBusName=org.freedesktop.impl.portal.desktop.jay
Interfaces=org.freedesktop.impl.portal.ScreenCast;org.freedesktop.impl.portal.RemoteDesktop;
EOF
```
```shell
~$ sudo tee /usr/share/xdg-desktop-portal/jay-portals.conf > /dev/null << 'EOF'
[preferred]
default=gtk
org.freedesktop.impl.portal.ScreenCast=jay
org.freedesktop.impl.portal.RemoteDesktop=jay
org.freedesktop.impl.portal.Inhibit=none
org.freedesktop.impl.portal.FileChooser=gtk4
EOF
```
### Restart the Portal
After installing the files, restart the portal service:
```shell
~$ systemctl --user restart xdg-desktop-portal
```
## Configuration
### workspace-capture
The top-level `workspace-capture` setting controls whether newly created
workspaces can be captured via workspace capture. The default is `true`:
```toml
workspace-capture = false
```
Set this to `false` if you want to prevent workspace-level capture by default.
### Capture Indicator Colors
When a window is being recorded, its title bar color changes to make the
capture visually obvious. You can customize these colors in the `[theme]`
table:
```toml
[theme]
captured-focused-title-bg-color = "#900000"
captured-unfocused-title-bg-color = "#5f0000"
```
- `captured-focused-title-bg-color` -- background color of focused title bars
that are being recorded.
- `captured-unfocused-title-bg-color` -- background color of unfocused title
bars that are being recorded.
## The jay portal Command
Jay's portal backend is normally started automatically when a screen-sharing
request comes in via D-Bus activation. If you need to start it manually for
debugging purposes:
```shell
~$ jay portal
```
## Troubleshooting
If screen sharing does not work:
1. Verify PipeWire is running: `systemctl --user status pipewire`
2. Verify the portal files are installed in `/usr/share/xdg-desktop-portal/`.
3. Restart the portal: `systemctl --user restart xdg-desktop-portal`
4. Check the Jay log for errors: `jay log`

186
book/src/tiling.md Normal file
View file

@ -0,0 +1,186 @@
# Tiling
Jay uses an i3-like tiling layout. Windows are arranged automatically in
containers that can be split horizontally or vertically. Containers can be nested
to create complex layouts.
## Splitting Containers
When you split a window, Jay wraps it in a new container with the specified
direction. Subsequent windows opened in that container are placed side by side
(horizontal split) or stacked top to bottom (vertical split).
`alt-d` -- `split-horizontal`
: Split the focused window horizontally
`alt-v` -- `split-vertical`
: Split the focused window vertically
`alt-t` -- `toggle-split`
: Toggle the container's split direction
You can also set the direction explicitly without toggling:
```toml
[shortcuts]
alt-shift-d = "tile-horizontal"
alt-shift-v = "tile-vertical"
```
The `tile-horizontal` action sets the container to horizontal, and
`tile-vertical` sets it to vertical -- unlike `split-horizontal`/`split-vertical`
which wrap the window in a new container first.
## Moving Focus
Move keyboard focus between windows with the directional focus actions:
`alt-h` -- `focus-left`
: Move focus left
`alt-j` -- `focus-down`
: Move focus down
`alt-k` -- `focus-up`
: Move focus up
`alt-l` -- `focus-right`
: Move focus right
Focus crosses container boundaries, so you can navigate across your entire
layout with these four keys.
## Moving Windows
Move the focused window within or between containers:
`alt-shift-h` -- `move-left`
: Move window left
`alt-shift-j` -- `move-down`
: Move window down
`alt-shift-k` -- `move-up`
: Move window up
`alt-shift-l` -- `move-right`
: Move window right
When a window reaches the edge of its container, the move action pushes it into
the adjacent container.
## Focus Parent
Press `alt-f` (`focus-parent`) to move focus from a window to its parent
container. This is useful when you want to operate on an entire group of
windows at once. For example, focusing a parent container and then using
`move-left` moves the whole group rather than a single window.
## Mono Mode
By default, a container shows all its children side by side. Mono mode changes
this so only one child is visible at a time, similar to a tabbed view.
`alt-m` -- `toggle-mono`
: Toggle between mono and side-by-side
You can also right-click any title in a container to toggle mono mode.
In mono mode, scroll over the title bar to cycle between windows in the
container.
For explicit control without toggling:
```toml
[shortcuts]
alt-s = "show-single" # Enter mono mode
alt-a = "show-all" # Exit mono mode
```
## Fullscreen
Press `alt-u` (`toggle-fullscreen`) to make the focused window fill the entire
output, hiding the bar and other windows. Press it again to return to the tiled
layout.
For explicit control:
```toml
[shortcuts]
alt-shift-u = "enter-fullscreen"
alt-ctrl-u = "exit-fullscreen"
```
## Resizing Tiles
Drag the separators between tiles with the mouse to resize them. The separator
changes the cursor to a resize indicator when hovered.
In [window management mode](mouse.md#window-management-mode), you can also
right-drag anywhere on a tile to resize it without needing to target the
separator.
## Closing Windows
Press `alt-shift-c` (`close`) to request the focused window to close. This
sends a polite close request to the application -- it is not a forceful kill.
```toml
[shortcuts]
alt-shift-c = "close"
```
## Toggling Floating
Double-click a tile's title bar to toggle it between tiled and floating. See
[Floating Windows](floating.md) for more details.
## Summary of Tiling Actions
`split-horizontal`
: Wrap focused window in a horizontal container
`split-vertical`
: Wrap focused window in a vertical container
`toggle-split`
: Toggle container split direction
`tile-horizontal`
: Set container direction to horizontal
`tile-vertical`
: Set container direction to vertical
`focus-left/right/up/down`
: Move keyboard focus
`move-left/right/up/down`
: Move focused window
`focus-parent`
: Focus the parent container
`toggle-mono`
: Toggle mono mode
`show-single`
: Enter mono mode
`show-all`
: Exit mono mode
`toggle-fullscreen`
: Toggle fullscreen
`enter-fullscreen`
: Enter fullscreen
`exit-fullscreen`
: Exit fullscreen
`close`
: Request focused window to close
`toggle-floating`
: Toggle between tiled and floating

315
book/src/troubleshooting.md Normal file
View file

@ -0,0 +1,315 @@
# Troubleshooting
## Jay doesn't start / black screen
Jay requires **at least one working renderer** -- without one, no GPU can be
initialized and nothing will be displayed. It supports Vulkan (via libvulkan
plus a GPU-specific driver) and OpenGL (via libEGL and libGLESv2). These
libraries are loaded at runtime, not linked at build time. Vulkan is the
primary renderer and should be used whenever possible; the OpenGL renderer is
maintained only for backwards compatibility.
Check that the required libraries are installed:
```shell
~$ ls /usr/lib/libEGL.so* /usr/lib/libGLESv2.so* /usr/lib/libvulkan.so* 2>/dev/null
```
> [!NOTE]
> On some distributions (e.g. Fedora, Debian), 64-bit libraries live in
> `/usr/lib64/` or `/usr/lib/x86_64-linux-gnu/` instead.
If nothing is listed, install the appropriate packages. See the
[Installation](installation.md) chapter for package names by distribution.
**Nvidia users:**
- You need **Linux 6.7 or later** for explicit sync support, which is required
for Nvidia GPUs.
- Make sure the Nvidia Vulkan ICD (Installable Client Driver) is installed.
Jay's Vulkan renderer is the recommended option on Nvidia hardware.
## No applications open
The built-in default configuration binds:
- `Super_L` (left Windows key) -- open [Alacritty](https://alacritty.org/).
- `alt-p` -- open [bemenu](https://github.com/Cloudef/bemenu) as an
application launcher.
If neither application is installed, nothing will happen when you press these
keys. Either install them or create a custom configuration with your preferred
applications:
```shell
~$ jay config init
```
Then edit `~/.config/jay/config.toml` to change the terminal and launcher
bindings.
> [!IMPORTANT]
> If you created a config file manually (e.g. by touching an empty file), it
> will have **no shortcuts at all**. Jay replaces the entire built-in default
> when any config file exists. Always use `jay config init` to start with a
> working configuration.
## Application doesn't have access to a protocol
Jay splits Wayland protocols into unprivileged and privileged. By default,
applications only have access to unprivileged protocols. If a program like a
screen locker, status bar, clipboard manager, or screen-capture tool is not
working, it likely needs access to one or more privileged protocols.
Common symptoms include:
- **swaylock** does nothing or fails to lock the screen (needs `session-lock`).
- **waybar** or **i3bar** shows no workspace information (needs
`foreign-toplevel-list`).
- **wl-copy** / **cliphist** cannot access the clipboard (needs
`data-control`).
- **grim** or **slurp** cannot capture the screen (needs `screencopy`).
**Quick fix -- grant all privileges:**
The simplest approach is to launch the program with full access to all
privileged protocols. In your config, set `privileged = true` in the exec
action:
```toml
on-idle = {
type = "exec",
exec = {
prog = "swaylock",
privileged = true,
},
}
```
Or from the command line:
```shell
~$ jay run-privileged waybar
```
**Better fix -- grant only the capabilities needed:**
Use a client rule to grant specific capabilities:
```toml
[[clients]]
match.comm = "waybar"
capabilities = ["layer-shell", "foreign-toplevel-list"]
```
See [Granting Privileges](window-rules.md#granting-privileges) for the full
list of capabilities and more advanced approaches using connection tags.
## Wrong keyboard layout
The default keyboard layout is US QWERTY. To change it:
**Option 1 -- edit the config:**
```shell
~$ jay config init
```
Then edit the `keymap.rmlvo` section in `~/.config/jay/config.toml`:
```toml
[keymap.rmlvo]
layout = "de"
```
**Option 2 -- change it at runtime:**
```shell
~$ jay input seat default set-keymap-from-names -l de
~$ jay input seat default set-keymap-from-names -l us -v intl
```
This takes effect immediately but does not persist across restarts unless
configured in the config file.
## Screen sharing doesn't work
Screen sharing requires PipeWire and the Jay desktop portal.
**1. Check that PipeWire is running:**
```shell
~$ systemctl --user status pipewire
```
If it is not running, start it:
```shell
~$ systemctl --user start pipewire
```
**2. Check that the portal files are installed:**
Jay needs two files to be found by the XDG desktop portal framework:
- A portal definition file (e.g. `/usr/share/xdg-desktop-portal/portals/jay.portal`).
- A portal configuration file (e.g. `/usr/share/xdg-desktop-portal/jay-portals.conf`).
These files are included in the Jay repository under `etc/`. If you built Jay
from source and did not install them, copy them manually:
```shell
~$ sudo cp etc/jay.portal /usr/share/xdg-desktop-portal/portals/
~$ sudo cp etc/jay-portals.conf /usr/share/xdg-desktop-portal/
```
**3. Restart the portal:**
```shell
~$ systemctl --user restart xdg-desktop-portal
```
See the [Screen Sharing](screen-sharing.md) chapter for more details.
## X11 applications don't work
Jay uses Xwayland to run X11 applications.
**1. Install Xwayland:**
- Arch Linux: `sudo pacman -S xorg-xwayland`
- Fedora: `sudo dnf install xorg-x11-server-Xwayland`
- Debian/Ubuntu: `sudo apt install xwayland`
**2. Check your configuration:**
If you have a config file, make sure Xwayland is not explicitly disabled:
```toml
[xwayland]
# Make sure this is not set to false:
# enabled = false
```
Xwayland is enabled by default. You can also check its status at runtime:
```shell
~$ jay xwayland status
```
## Display manager doesn't show Jay
**1. Check that the session file exists:**
```shell
~$ ls /usr/share/wayland-sessions/jay.desktop
```
If it does not exist, create it:
```shell
~$ sudo tee /usr/share/wayland-sessions/jay.desktop > /dev/null << 'EOF'
[Desktop Entry]
Name=Jay
Comment=A Wayland Compositor
Exec=jay run
Type=Application
DesktopNames=jay
EOF
```
**2. Check that `jay` is in the system PATH:**
If you installed Jay via `cargo install`, the binary is at `~/.cargo/bin/jay`.
Display managers typically do not include `~/.cargo/bin` in their PATH. Either:
- Add `~/.cargo/bin` to the system PATH, or
- Create a symlink:
```shell
~$ sudo ln -s ~/.cargo/bin/jay /usr/local/bin/jay
```
## How to check logs
Open the log file in a pager:
```shell
~$ jay log
```
Follow the log in real time (like `tail -f`):
```shell
~$ jay log -f
```
Print just the log file path:
```shell
~$ jay log --path
```
Increase log verbosity at runtime:
```shell
~$ jay set-log-level debug
```
To set the log level at startup, add it to your config:
```toml
log-level = "debug"
```
> [!NOTE]
> The `log-level` config setting is read at startup and cannot be changed by
> reloading the configuration. Use `jay set-log-level` for runtime changes.
## Performance issues
If you experience dropped frames, stuttering, or high latency, try the
following:
**1. Use the Vulkan renderer:**
The Vulkan renderer is generally faster and supports more features (e.g. HDR,
direct scanout). Check your current API and switch if needed:
```shell
~$ jay randr show
~$ jay randr card card0 api vulkan
```
**2. Set `CAP_SYS_NICE`:**
Granting `CAP_SYS_NICE` allows Jay to use real-time scheduling and high-priority
Vulkan queues, which improves responsiveness under load:
```shell
~$ sudo setcap cap_sys_nice=p $(which jay)
```
> You need to re-run this command each time you update the Jay binary.
**3. Adjust the flip margin:**
The flip margin controls the time between initiating a page flip and the
display's vblank. A smaller margin reduces input latency but risks missed
frames. The default is 1.5 ms:
```shell
~$ jay randr card card0 timing set-flip-margin 1.5
```
If you see missed frames, try increasing it. If you want lower latency, try
decreasing it -- Jay will dynamically increase it if the margin is too small.
**4. Enable direct scanout:**
Direct scanout allows fullscreen applications to bypass the compositor's
rendering pipeline entirely, reducing both latency and GPU usage:
```shell
~$ jay randr card card0 direct-scanout enable
```

529
book/src/window-rules.md Normal file
View file

@ -0,0 +1,529 @@
# 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.
`capabilities`
: Wayland protocol access granted to matching clients.
`sandbox-bounding-capabilities`
: Upper bounds for protocols available to child sandboxes.
### 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.
`tag` / `tag-regex`
: The connection tag of the client.
### Granting Privileges
Jay splits Wayland protocols into unprivileged and privileged. By default,
applications only have access to unprivileged protocols. This means that tools
like screen lockers, status bars, screen-capture utilities, and clipboard
managers will not work unless you explicitly grant them the necessary
privileges.
See the [Protocol Support](features.md#protocol-support) table in the Features
chapter for the full list of protocols and whether they are privileged.
There are three ways to grant privileges, from simplest to most fine-grained.
#### 1. Grant all privileges via `privileged = true` (exec) or `jay run-privileged`
The simplest approach gives a program access to **all** privileged protocols.
This is appropriate for trusted tools like screen lockers where you don't want
to think about which specific protocols they need.
In the config, set `privileged = true` in the exec table:
```toml
on-idle = {
type = "exec",
exec = {
prog = "swaylock",
privileged = true,
},
}
```
From the command line, use `jay run-privileged`:
```shell
~$ jay run-privileged waybar
```
Both methods connect the program to a privileged Wayland socket that grants
access to all privileged protocols.
#### 2. Grant capabilities via connection tags
Connection tags let you combine the CLI with client rules for precise control.
You tag a program at launch time, then write a client rule that matches
the tag and grants specific capabilities.
First, launch the program with a tag -- either from the command line:
```shell
~$ jay run-tagged bar waybar
```
Or from the config using the `tag` field in an exec action:
```toml
[shortcuts]
alt-w = {
type = "exec",
exec = {
prog = "waybar",
tag = "bar",
},
}
```
Then write a client rule that matches the tag and grants capabilities:
```toml
[[clients]]
match.tag = "bar"
capabilities = ["layer-shell", "foreign-toplevel-list"]
```
This way, only the specific instance you launched with the tag receives the
privileges -- other programs with the same binary name do not.
Available capability values: `none`, `all`, `data-control`,
`virtual-keyboard`, `foreign-toplevel-list`, `idle-notifier`, `session-lock`,
`layer-shell`, `screencopy`, `seat-manager`, `drm-lease`, `input-method`,
`workspace-manager`, `foreign-toplevel-manager`, `head-manager`,
`gamma-control-manager`, `virtual-pointer`.
**Default capabilities:** unsandboxed clients receive `layer-shell` and
`drm-lease`. Sandboxed clients receive only `drm-lease`. If any client rule
matches, its capabilities **replace** the defaults entirely. If multiple rules
match, their capabilities are unioned together, but the defaults are not
included unless a matching rule also grants them.
#### 3. Grant capabilities via client match rules
Client rules can also match programs by properties like their executable name
instead of a tag. This is convenient when you always want a given program to
have certain capabilities, regardless of how it was launched:
```toml
[[clients]]
match.comm = "waybar"
capabilities = ["layer-shell", "foreign-toplevel-list"]
# Vim 9.2 uses the data-control protocol for seamless wayland integration.
[[clients]]
match.comm = "vim"
match.sandboxed = false
capabilities = "data-control"
# Older versions use wl-copy and wl-paste.
[[clients]]
match.any = [
{ comm = "wl-copy" },
{ comm = "wl-paste" },
]
match.sandboxed = false
capabilities = "data-control"
```
> [!NOTE]
> Client match criteria like `comm`, `exe`, and `pid` are checked when a
> client connects. Any process with a matching name receives the specified
> capabilities. If you need to restrict privileges to programs you launch
> yourself, use connection tags (method 2) instead.
#### Bounding capabilities (sandboxes)
Capabilities can never exceed the client's **bounding capabilities**. Use
`sandbox-bounding-capabilities` on a client rule to set the upper bound for
protocols available to sandboxes created by that client:
```toml
[[clients]]
match.comm = "flatpak-portal"
sandbox-bounding-capabilities = ["drm-lease", "layer-shell"]
```
## 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"],
}
```
### Grant Protocol Access to a Trusted App
```toml
[[clients]]
match.comm = "swaylock"
capabilities = ["session-lock", "layer-shell"]
[[clients]]
match.comm = "waybar"
capabilities = ["layer-shell", "foreign-toplevel-list"]
```
### 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.

157
book/src/workspaces.md Normal file
View file

@ -0,0 +1,157 @@
# Workspaces
Workspaces are virtual desktops that group windows together. Each workspace
lives on an output (monitor) and contains its own tiling layout. Jay creates
workspaces on demand and automatically manages them when monitors are connected
or disconnected.
## Switching Workspaces
Use the `show-workspace` action to switch to a workspace. In the default
configuration, `alt-F1` through `alt-F12` switch to workspaces named "1"
through "12":
```toml
[shortcuts]
alt-F1 = { type = "show-workspace", name = "1" }
alt-F2 = { type = "show-workspace", name = "2" }
alt-F3 = { type = "show-workspace", name = "3" }
# ... and so on through alt-F12
```
If the workspace does not yet exist, it is created on the output that currently
contains the cursor. You can override this by specifying an output:
```toml
[shortcuts]
alt-F1 = {
type = "show-workspace",
name = "1",
output.name = "left",
}
```
You can also scroll over the bar to cycle through workspaces on that output.
## Moving Windows to Workspaces
Use the `move-to-workspace` action to send the focused window to a different
workspace. The default bindings are `alt-shift-F1` through `alt-shift-F12`:
```toml
[shortcuts]
alt-shift-F1 = { type = "move-to-workspace", name = "1" }
alt-shift-F2 = { type = "move-to-workspace", name = "2" }
alt-shift-F3 = { type = "move-to-workspace", name = "3" }
# ... and so on through alt-shift-F12
```
You can also drag a tile's title onto a workspace tab in the bar to move it to
that workspace. Dragging a tile onto the bar outside any workspace tab creates a
new workspace for it.
## Moving Workspaces Between Outputs
The `move-to-output` action moves a workspace to a different output. You can
target the output by name or by direction:
```toml
[shortcuts]
# Move the current workspace to a named output
alt-o = { type = "move-to-output", output.name = "right" }
# Move the current workspace in a direction
logo-ctrl-shift-Right = {
type = "move-to-output",
direction = "right",
}
logo-ctrl-shift-Left = {
type = "move-to-output",
direction = "left",
}
logo-ctrl-shift-Up = {
type = "move-to-output",
direction = "up",
}
logo-ctrl-shift-Down = {
type = "move-to-output",
direction = "down",
}
```
You can also move a specific workspace by name:
```toml
[shortcuts]
alt-o = {
type = "move-to-output",
workspace = "1",
output.name = "right",
}
```
If `workspace` is omitted, the currently active workspace is moved.
## Workspace Display Order
Workspaces appear as tabs in the bar. Their order can be configured in two
modes:
- **manual** (default) -- workspaces appear in the order they were created and
can be reordered by dragging their titles in the bar.
- **sorted** -- workspaces are sorted alphabetically. Dragging to reorder is
disabled.
Set the order in your configuration:
```toml
workspace-display-order = "sorted"
```
You can also change this at runtime in the control center.
## Hot-Plug and Hot-Unplug
Jay handles monitor connections gracefully:
- When a monitor is **unplugged**, its workspaces are automatically migrated to
one of the remaining monitors.
- When the monitor is **plugged back in**, those workspaces are restored to it.
This means you never lose your workspace layout when docking or undocking a
laptop.
## Workspace Capture
By default, newly created workspaces can be captured for screen sharing. You
can disable this globally:
```toml
workspace-capture = false
```
When workspace capture is enabled, screen-sharing applications can share
individual workspaces (in addition to full outputs and individual windows). See
[Screen Sharing](screen-sharing.md) for more details.
## Matching Windows by Workspace
In [window rules](window-rules.md), you can match windows based on the
workspace they are on:
```toml
[[windows]]
match.workspace = "music"
action = "enter-fullscreen"
```
The `workspace-regex` field is also available for pattern matching:
```toml
[[windows]]
match.workspace-regex = "^(music|video)$"
action = "enter-fullscreen"
```
Since window rules are reactive, these rules are re-evaluated whenever a window
moves to a different workspace.

View file

@ -1,521 +0,0 @@
# Configuration
Jay can be configured via
- a declarative TOML file or
- a shared library that gets injected into the compositor.
## Shared Library Configuration
This is described in the [rustdoc](https://docs.rs/jay-config) of the configuration crate.
## TOML Configuration
The configuration file is stored under `$HOME/.config/jay/config.toml`.
If you don't have such a file, the default configuration will be used.
The full format of this file is described in the auto-generated file [spec.generated.md](../toml-spec/spec/spec.generated.md).
You can also get auto completion with the auto-generated JSON Schema linked from that document.
The following code block contains the annotated default configuration.
Below that we will describe individual usecases.
```toml
# The keymap that is used for shortcuts and also sent to clients.
keymap = """
xkb_keymap {
xkb_keycodes { include "evdev+aliases(qwerty)" };
xkb_types { include "complete" };
xkb_compat { include "complete" };
xkb_symbols { include "pc+us+inet(evdev)" };
};
"""
# An action that will be executed when the GPU has been initialized.
on-graphics-initialized = [
{ type = "exec", exec = "mako" },
{ type = "exec", exec = "wl-tray-bridge" },
]
# Shortcuts that are processed by the compositor.
# The left hand side should be a key, possibly prefixed with modifiers.
# The right hand side should be an action.
[shortcuts]
# The focus-X actions move the keyboard focus to next window on the X.
alt-h = "focus-left"
alt-j = "focus-down"
alt-k = "focus-up"
alt-l = "focus-right"
# The move-X actions move window that has the keyboard focus to the X.
alt-shift-h = "move-left"
alt-shift-j = "move-down"
alt-shift-k = "move-up"
alt-shift-l = "move-right"
# The split-X action places the currently focused window in a container
# and sets the split direction of the container to X.
alt-d = "split-horizontal"
alt-v = "split-vertical"
# The toggle-split action changes the split direction of the current
# container.
alt-t = "toggle-split"
# The toggle-mono action changes whether the current container shows
# a single window or all windows next to each other.
alt-m = "toggle-mono"
# The toggle-fullscreen action toggles the current window between
# windowed and fullscreen.
alt-u = "toggle-fullscreen"
# The focus-parent action moves the keyboard focus to the parent of
# the currently focused window.
alt-f = "focus-parent"
# The open-control-center action opens the control center.
alt-c = "open-control-center"
# The close action requests the currently focused window to close.
alt-shift-c = "close"
# The toggle-floating action changes the currently focused window between
# floating and tiled.
alt-shift-f = "toggle-floating"
# All actions above are so-called simple actions that are identified by
# a string. More complex actions take parameters and are written as a table.
# For example, the exec action spawns an application and has the exec field
# that describes how to spawn the application.
Super_L = { type = "exec", exec = "alacritty" }
alt-p = { type = "exec", exec = "bemenu-run" }
# The quit action terminates the compositor.
alt-q = "quit"
# The reload-config-toml action reloads the TOML configuration file.
alt-shift-r = "reload-config-toml"
# The switch-to-vt action switches to a different virtual terminal.
ctrl-alt-F1 = { type = "switch-to-vt", num = 1 }
ctrl-alt-F2 = { type = "switch-to-vt", num = 2 }
ctrl-alt-F3 = { type = "switch-to-vt", num = 3 }
ctrl-alt-F4 = { type = "switch-to-vt", num = 4 }
ctrl-alt-F5 = { type = "switch-to-vt", num = 5 }
ctrl-alt-F6 = { type = "switch-to-vt", num = 6 }
ctrl-alt-F7 = { type = "switch-to-vt", num = 7 }
ctrl-alt-F8 = { type = "switch-to-vt", num = 8 }
ctrl-alt-F9 = { type = "switch-to-vt", num = 9 }
ctrl-alt-F10 = { type = "switch-to-vt", num = 10 }
ctrl-alt-F11 = { type = "switch-to-vt", num = 11 }
ctrl-alt-F12 = { type = "switch-to-vt", num = 12 }
# The show-workspace action switches to a workspace. If the workspace is not
# currently being used, it is created on the output that contains the pointer.
alt-F1 = { type = "show-workspace", name = "1" }
alt-F2 = { type = "show-workspace", name = "2" }
alt-F3 = { type = "show-workspace", name = "3" }
alt-F4 = { type = "show-workspace", name = "4" }
alt-F5 = { type = "show-workspace", name = "5" }
alt-F6 = { type = "show-workspace", name = "6" }
alt-F7 = { type = "show-workspace", name = "7" }
alt-F8 = { type = "show-workspace", name = "8" }
alt-F9 = { type = "show-workspace", name = "9" }
alt-F10 = { type = "show-workspace", name = "10" }
alt-F11 = { type = "show-workspace", name = "11" }
alt-F12 = { type = "show-workspace", name = "12" }
# The move-to-workspace action moves the currently focused window to a workspace.
alt-shift-F1 = { type = "move-to-workspace", name = "1" }
alt-shift-F2 = { type = "move-to-workspace", name = "2" }
alt-shift-F3 = { type = "move-to-workspace", name = "3" }
alt-shift-F4 = { type = "move-to-workspace", name = "4" }
alt-shift-F5 = { type = "move-to-workspace", name = "5" }
alt-shift-F6 = { type = "move-to-workspace", name = "6" }
alt-shift-F7 = { type = "move-to-workspace", name = "7" }
alt-shift-F8 = { type = "move-to-workspace", name = "8" }
alt-shift-F9 = { type = "move-to-workspace", name = "9" }
alt-shift-F10 = { type = "move-to-workspace", name = "10" }
alt-shift-F11 = { type = "move-to-workspace", name = "11" }
alt-shift-F12 = { type = "move-to-workspace", name = "12" }
```
### Configuring Keymaps and Repeat Rates
The keymap can be configured via the top-level `keymap` field.
```toml
keymap = """
xkb_keymap {
xkb_keycodes { include "evdev+aliases(qwerty)" };
xkb_types { include "complete" };
xkb_compat { include "complete" };
xkb_symbols { include "pc+us+inet(evdev)" };
};
"""
```
The format is described in the ArchWiki: https://wiki.archlinux.org/title/X_keyboard_extension
If you want to use multiple keymaps, you can assign names to them:
```toml
keymap.name = "laptop"
[[keymaps]]
name = "laptop"
path = "./laptop-keymap.xkb"
[[keymaps]]
name = "external"
path = "./external-keymap.xkb"
```
Such paths are relative to the configuration file.
You can also write the map inline in this format:
```toml
[[keymaps]]
name = "external"
map = "..."
```
If you want to switch the keymap with a shortcut, use the `set-keymap` action:
```toml
[shortcuts]
alt-j = { type = "set-keymap", keymap.name = "laptop" }
alt-k = { type = "set-keymap", keymap.name = "external" }
```
The keyboard repeat rate is configured via the top-level `repeat-rate` field.
```toml
repeat-rate = { rate = 25, delay = 250 }
```
You can change this at runtime with the `set-repeat-rate` action:
```toml
[shortcuts]
alt-x = { type = "set-repeat-rate", rate = { rate = 25, delay = 250 } }
```
Note that you can change all of this from the command line with the `jay input` command.
### Configuring Shortcuts
Shortcuts are configured in the top-level `shortcuts` table.
```toml
[shortcuts]
alt-h = "focus-left"
```
The left-hand side should be a key that can optionally be prefixed with modifiers.
The right-hand side should be an action.
See [spec.generated.md](../toml-spec/spec/spec.generated.md) for a full list of actions.
### Complex Shortcuts
If you need more control over shortcut execution, you can use the `complex-shortcuts` table.
```toml
[complex-shortcuts.alt-x]
action = { type = "exec", exec = ["pactl", "set-sink-mute", "0", "1"] }
latch = { type = "exec", exec = ["pactl", "set-sink-mute", "0", "0"] }
```
This mutes the audio output while the key is pressed and un-mutes once the `x` key is released.
The order in which `alt` and `x` are released does not matter for this.
This can also be used to implement push to talk.
See the specification for more details.
### Running Multiple Actions
In every place that accepts an action, you can also run multiple actions by wrapping them
in an array:
```toml
[shortcuts]
alt-h = ["focus-left", "focus-up"]
```
### Spawning Applications
You can spawn applications by using the `exec` action:
```toml
Super_L = { type = "exec", exec = "alacritty" }
```
The `exec` field can be either a string, an array of strings, or a table.
When a string is used, it should be the name of the application.
When an array is used, it should be the name of the application followed by arguments.
```toml
Super_L = { type = "exec", exec = ["alacritty", "-e", "date"] }
```
When a table is used, you can additionally specify
- environment variables to pass to the application,
- whether the application should have access to privileged protocols.
See the specification for more details.
### Running an Action at Startup
If you want to run an action at startup, you can use the top-level `on-graphics-initialized`
field:
```toml
on-graphics-initialized = [
{ type = "exec", exec = "mako" },
{ type = "exec", exec = "wl-tray-bridge" },
]
```
### Setting Environment Variables
You can set environment variables with the the top level `env` table.
```toml
[env]
GTK_THEME = "Adwaita:dark"
```
These environment variables are passed to all applications started afterwards.
You can also use the `set-env` action to modify these variables:
```toml
[shortcuts]
alt-l = { type = "set-env", env.GTK_THEME = "Adwaita:dark" }
```
The `unset-env` action is similar.
See the specification for more details.
### Using a Status Program
You can configure a status program with the top-level `status` table.
```toml
[status]
format = "i3bar"
exec = "i3status"
```
The `format` field specifies the format used by the status program.
Possible values are `plain`, `pango`, and `i3bar`.
The `exec` field specifies how to start the status program.
Note that i3status will not automatically use i3bar format when started this way.
You have to explicitly opt into i3bar format in your i3status configuration.
See the specification for more details.
### Configuring Idle Timeout and Actions
You can configure the idle timeout with the top-level `idle` table.
```toml
idle.minutes = 10
```
If you want to lock the screen when this timeout happens, you can use the `on-idle` table.
```toml
on-idle = { type = "exec", exec = { prog = "swaylock", privileged = true } }
```
See the specification for more details.
### Configuring GPUs
You can configure GPUs with the top-level `drm-devices` array.
```toml
[[drm-devices]]
name = "dedicated"
match = { pci-vendor = 0x1002, pci-model = 0x73ff }
[[drm-devices]]
name = "integrated"
match = { pci-vendor = 0x1002, pci-model = 0x164e }
gfx-api = "OpenGl"
```
For each device, you can configure the following properties:
- Whether direct scanout is enabled on monitors connected to this device.
- Which API to use for this device (OpenGL or Vulkan).
You can assign names to these device to refer to them elsewhere.
The `match` field is used to identify the device.
Unless you have two identical graphics cards installed, using the pci-vendor and model
fields is usually the best choice.
You can get these values by running `jay randr`.
You can select the device used for rendering the desktop with the top-level `render-device` field.
```toml
render-device.name = "dedicated"
```
You can modify the render device and configure GPUs at runtime with the `set-render-device`
and `configure-drm-device` actions.
You can use the top-level `gfx-api` field to set the default API used (unless overwritten for specific device).
```toml
gfx-api = "Vulkan"
```
See the specification for more details.
### Configuring Monitors
You can configure monitors with the top-level `outputs` field.
```toml
[[outputs]]
name = "left"
match.serial-number = "33K03894SL0"
x = 0
y = 0
[[outputs]]
name = "right"
match.serial-number = "ETW1M02062SL0"
x = 1920
y = 0
```
For each output, you can configure the following properties:
- The x, y coordinates in global compositor space.
- The scale to use for the monitor.
- The transformation to apply to the content (rotation, mirroring).
- The mode to use for the monitor.
You can query the available modes and modify these properties from the command line with
the `jay randr` command.
The `match` field selects the monitors the configuration applies to.
The serial number is usually a good unique identifier.
You can assign a name to monitors to refer to them in other places.
You can use the `configure-output` action to change this configuration at runtime.
See the specification for more details.
### Configuring Connectors
Connectors are the physical ports at the back of your GPU.
You can configure them with the top-level `connectors` array.
```toml
[[connectors]]
name = "eDP-1"
enabled = false
```
Currently you can only use this to disable or enable connectors.
This is useful to disable the internal monitor of a laptop when the laptop is closed.
You can use the `configure-connector` action to change this configuration at runtime.
See the specification for more details.
### Disabling Connectors of Closed Laptops
If a laptop has a switch that is signaled when the laptop is closed, you can configure
the built-in connector to be disabled automatically:
```toml
[[inputs]]
match.name = "<switch name>"
on-lid-closed = { type = "configure-connector", connector = { match.name = "eDP-1", enabled = false } }
on-lid-opened = { type = "configure-connector", connector = { match.name = "eDP-1", enabled = true } }
```
See the specification for more details.
### Configuring Input Devices
You can configure input devices with the top-level `inputs` array.
```toml
[[inputs]]
tag = "mice"
match.is-pointer = true
left-handed = true
transform-matrix = [[0.35, 0], [0, 0.35]]
tap-enabled = true
```
For each input device you can configure the following properties:
- The libinput acceleration profile.
- The libinput acceleration speed.
- The libinput tap setting.
- The libinput tap-drag setting.
- The libinput tap-drag-lock setting.
- The libinput left-handed setting.
- The libinput natural-scrolling setting.
- The number of pixels to scroll per scroll-wheel dedent.
- A transformation matrix to apply to relative movements.
You can inspect and modify these settings from the command line with the `jay input` command.
The `match` field selects the input devices to operate on.
You can assign a `tag` to input devices to refer to them elsewhere.
You can use the `configure-input` action to change these settings at runtime.
See the specification for more details.
### Mapping Tablets to Outputs
You can map tablets to outputs using the `output` property:
```toml
[[outputs]]
name = "left"
match.serial-number = "33K03894SL0"
[[inputs]]
match.name = "Wacom Bamboo Comic 2FG Pen"
output.name = "left"
```
See the specification for more details.
### Theming
You can configure the colors, sizes, and fonts used by the compositor with the top-level `theme` table.
```toml
[theme]
bg-color = "#ff000"
```
See the specification for more details.
### Tray Icons and Menus
The default configuration will try to start [wl-tray-bridge] to give you access to tray
icons and menus.
[wl-tray-bridge]: https://github.com/mahkoh/wl-tray-bridge
### Window and Client Rules
This is described in [window-and-client-rules.md](window-and-client-rules.md).

View file

@ -1,229 +0,0 @@
# Features
## Configuration
Jay can be configured via
- a declarative TOML file or
- a shared library that gets injected into the compositor.
See [config.md](config.md) for more details.
## i3 Look and Feel
Jay's appearance is based on the default i3 look and feel.
Colors, sizes, and fonts can be customized.
## Stability
Jay has been stable for a long time.
Crashes and incorrect behavior in released versions are very rare.
Jay also aims to be forward and backward compatible for existing setups, allowing you to
upgrade or downgrade the compositor without having to adjust your configuration.
There is a small but growing integration test suite that is used to ensure this.
## CLI
Jay has a CLI that can be used to configure the compositor at runtime.
```
~$ jay
A wayland compositor
Usage: jay [OPTIONS] <COMMAND>
Commands:
run Run the compositor
config Create/modify the toml config
generate-completion Generate shell completion scripts for jay
log Open the log file
set-log-level Sets the log level
quit Stop the compositor
unlock Unlocks the compositor
screenshot Take a screenshot
idle Inspect/modify the idle (screensaver) settings
run-privileged Run a privileged program
run-tagged Run a program with a connection tag
seat-test Tests the events produced by a seat
portal Run the desktop portal
randr Inspect/modify graphics card and connector settings
input Inspect/modify input settings
xwayland Inspect/modify xwayland settings
color-management Inspect/modify the color-management settings
clients Inspect/manipulate the connected clients
tree Inspect the surface tree
control-center Opens the control center
version Prints the Jay version and exits
pid Prints the Jay PID and exits
help Print this message or the help of the given subcommand(s)
Options:
--log-level <LOG_LEVEL> The log level [default: info] [possible values: trace, debug, info, warn, error, off]
-h, --help Print help
```
## Multi-Monitor Support
Jay can be used with multiple monitors with hot-plug and hot-unplug support.
When a monitor is unplugged, all workspaces are automatically moved one of the remaining
monitors.
When the monitor is plugged in again, these workspaces are restored.
## Multi-GPU Support
Jay can be used with multiple GPUs and monitors connected to different GPUs.
One GPU is always used for rendering the desktop.
You can change this GPU at runtime.
## Screen Sharing
Jay supports screen sharing via xdg-desktop-portal.
There are three supported modes:
- Window capture
- Output capture
- Workspace capture which is like output capture except that only one workspace will be
shown.
## Screen Locking
Jay can automatically lock your screen and disable outputs after inactivity.
## Notifications
Jay supports the zwlr_layer_shell_v1 protocol used by notification daemons.
## Fractional Scaling
Jay supports per-monitor fractional scaling.
## OpenGL and Vulkan
Jay can use either OpenGL or Vulkan for rendering.
Vulkan offers better performance and memory usage but OpenGL is still provided for
older hardware.
You can change the API at runtime without restarting the compositor.
## Explicit Sync
Jay supports explicit sync for compatibility with Nvidia hardware.
## Clipboard Managers
Jay supports clipboard managers via `zwlr_data_control_manager_v1`.
## Privilege Separation
Jay splits protocols into unprivileged and privileged protocols.
By default, applications only have access to unprivileged protocols.
You can explicitly opt into giving applications access to privileged protocols via the Jay CLI or shortcuts.
## Push to Talk
Jay's shortcut system allows you to execute an action when a key is pressed and to execute a different action when the key is released.
## VR
Jay supports leasing VR headsets to applications.
## Adaptive Sync
Jay supports adaptive sync with configurable cursor refresh rates.
## Tearing
Jay supports tearing presentation for games.
## Low Input Latency
Jay uses frame scheduling to achieve input latency as low as 1.5 ms.
## Color Management & HDR
Jay supports the color management protocol and HDR10.
## Night Light
Jay supports night-light applications via `zwlr_gamma_control_manager_v1`.
## Window and Client Rules
Jay supports powerful window and client rules.
See [window-and-client-rules.md](window-and-client-rules.md) for more details.
## Protocol Support
Jay supports the following wayland protocols:
| Global | Version | Privileged |
|------------------------------------------------------|:----------------|---------------|
| ext_data_control_manager_v1 | 1 | Yes |
| ext_foreign_toplevel_image_capture_source_manager_v1 | 1 | |
| ext_foreign_toplevel_list_v1 | 1 | Yes |
| ext_idle_notifier_v1 | 2 | Yes |
| ext_image_copy_capture_manager_v1 | 1[^composited] | Yes |
| ext_output_image_capture_source_manager_v1 | 1 | |
| ext_session_lock_manager_v1 | 1 | Yes |
| ext_transient_seat_manager_v1 | 1[^ts_rejected] | Yes |
| ext_workspace_manager_v1 | 1 | Yes |
| jay_popup_ext_manager_v1 | 1 | |
| jay_tray_v1 | 1 | |
| org_kde_kwin_server_decoration_manager | 1 | |
| wl_compositor | 7 | |
| wl_data_device_manager | 4 | |
| wl_drm | 2 | |
| wl_fixes | 1 | |
| wl_output | 4 | |
| wl_seat | 10 | |
| wl_shm | 2 | |
| wl_subcompositor | 1 | |
| wp_alpha_modifier_v1 | 1 | |
| wp_color_manager_v1 | 2 | |
| wp_color_representation_manager_v1 | 1 | |
| wp_commit_timing_manager_v1 | 1 | |
| wp_content_type_manager_v1 | 1 | |
| wp_cursor_shape_manager_v1 | 2 | |
| wp_drm_lease_device_v1 | 1 | |
| wp_fifo_manager_v1 | 1 | |
| wp_fractional_scale_manager_v1 | 1 | |
| wp_linux_drm_syncobj_manager_v1 | 1 | |
| wp_pointer_warp_v1 | 1 | |
| wp_presentation | 2 | |
| wp_security_context_manager_v1 | 1 | |
| wp_single_pixel_buffer_manager_v1 | 1 | |
| wp_tearing_control_manager_v1 | 1 | |
| wp_viewporter | 1 | |
| xdg_activation_v1 | 1 | |
| xdg_toplevel_drag_manager_v1 | 1 | |
| xdg_toplevel_tag_manager_v1 | 1 | |
| xdg_wm_base | 7 | |
| xdg_wm_dialog_v1 | 1 | |
| zwlr_data_control_manager_v1 | 2 | Yes |
| zwlr_foreign_toplevel_manager_v1 | 3 | Yes |
| zwlr_gamma_control_manager_v1 | 1 | Yes |
| zwlr_layer_shell_v1 | 5 | No[^lsaccess] |
| zwlr_output_manager_v1 | 4 | Yes |
| zwlr_screencopy_manager_v1 | 3 | Yes |
| zwlr_virtual_pointer_manager_v1 | 2 | Yes |
| zwp_idle_inhibit_manager_v1 | 1 | |
| zwp_input_method_manager_v2 | 1 | Yes |
| zwp_linux_dmabuf_v1 | 5 | |
| zwp_pointer_constraints_v1 | 1 | |
| zwp_pointer_gestures_v1 | 3 | |
| zwp_primary_selection_device_manager_v1 | 1 | |
| zwp_relative_pointer_manager_v1 | 1 | |
| zwp_tablet_manager_v2 | 2 | |
| zwp_text_input_manager_v3 | 1 | |
| zwp_virtual_keyboard_manager_v1 | 1 | Yes |
| zxdg_decoration_manager_v1 | 2 | |
| zxdg_output_manager_v1 | 3 | |
[^lsaccess]: Sandboxes can restrict access to this protocol.
[^ts_rejected]: Seat creation is always rejected.
[^composited]: Cursors are always composited.

View file

@ -1,53 +0,0 @@
# Mouse Interactions
Jay supports a number of mouse-based interactions that might be hard to discover. This
file documents all of them.
- Inside a tiled container, the tiles can be resized by dragging the separators.
- Floating windows can be resized by dragging the borders.
- Floating windows can be moved by dragging the title.
- Tiles inside containers can be moved by dragging the title.
- Dragging them onto an existing workspace moves them to that workspace.
- Dragging them onto the bar outside an existing workspace creates a new workspace.
- Workspaces can be moved by dragging their titles with the mouse.
- In a container in mono layout, scrolling over the title switches between tiles.
- Scrolling over the bar switches between workspaces.
- Double clicking on a tile title/floating window title switches between floating and
tiling.
- Right clicking on any title in a container switches the container between mono and tiled
layout.
- Right clicking on the title of a floating window pins/unpins the window. (Pinned windows
are visible on all workspaces.)
- When the pin icon is visible on a floating window, left clicking the icon pins/unpins
the window.
- When selecting a toplevel (noticeable by the purple overlay), right clicking on the
title of any tile in a container selects that container.
- Any long running mouse interaction can be canceled by pressing escape.
## Window Management Mode
Window-management mode makes more interactions available. See the configurable
`window-management-key`.
- In window-management mode, floating windows, tiles, and popups can be resized by
dragging their contents with the right mouse button.
- In window-management mode, floating windows, tiles, popups, and fullscreen windows can
be moved by dragging their contents with the left mouse button.
- Entering window-management mode disables all pointer constraints and can therefore be
used to move the pointer out of windows that have grabbed the pointer.

View file

@ -1,127 +0,0 @@
# Building
## Compile-time Dependencies
The following libraries must be installed before compiling Jay:
- libinput.so
- libgbm.so
- libudev.so
- libpangocairo-1.0.so
- libfontconfig.so
You must also have a C compiler (GCC or Clang) and the latest version of rust installed.
You can install rust with [rustup](https://rustup.rs/).
## Runtime Dependencies
Most of these dependencies are optional and will enable additional features.
- Linux 6.7: Required for explicit sync.
- Xwayland: Required for running X applications.
- Pipewire: Required for screen sharing.
- logind (part of systemd): Required when running Jay from a virtual terminal.
- libEGL.so and libGLESv2.so: Required for the OpenGL renderer.
- libvulkan.so: Required for the Vulkan renderer.
Note that Jay will not work if neither the OpenGL nor the Vulkan renderer are available.
## Compiling
To compile the latest stable version of Jay, run
```
cargo install --locked jay-compositor
```
This will install Jay under `$HOME/.cargo/bin/jay`.
If you want to use the latest version from git, run
```
cargo install --locked --git https://github.com/mahkoh/jay.git jay-compositor
```
If you only want to build Jay without installing it, run the following command from within this repository:
```
cargo build --release
```
The binary is then available under `./target/release/jay`.
## Running with CAP_SYS_NICE
Jay supports being started with CAP_SYS_NICE capabilities. For example, such
capabilities can be added to the binary via
```shell
~# setcap cap_sys_nice=p jay
```
If CAP_SYS_NICE is available, Jay will, by default, elevate its scheduler to
SCHED_RR and create Vulkan queues with the highest available priority. This can
improve responsiveness if the CPU or GPU are under high load.
If Jay is started with the environment variable `JAY_NO_REALTIME=1` or a
`config.so` exists, then Jay will not elevate its scheduler but will still
create elevated Vulkan queues.
Jay will drop all capabilities almost immediately after being started. Before
that, it will spawn a dedicated thread that retains the CAP_SYS_NICE capability
to create elevated Vulkan queues later.
If Jay has elevated its scheduler to SCHED_RR, then it will refuse to load
`config.so` configurations. Otherwise unprivileged applications would be able
to run arbitrary code with SCHED_RR by crafting a dedicated `config.so`. This
behavior can be overridden by compiling Jay with
`JAY_ALLOW_REALTIME_CONFIG_SO=1`.
# Setup
## Configuration
See [config.md](./config.md).
## Screen Sharing
This step is only required to enable screen sharing.
1. Copy `../etc/jay.portal` to `/usr/share/xdg-desktop-portal/portals/jay.portal`.
2. Copy `../etc/jay-portals.conf` to `/usr/share/xdg-desktop-portal/jay-portals.conf`.
Then restart `xdg-deskop-portal`.
## Recommended Applications
- [xdg-desktop-portal-gtk4](https://github.com/mahkoh/xdg-desktop-portal-gtk4)
Provides a file-picker portal that supports thumbnails. It will be used
automatically if it is installed.
- [wl-tray-bridge](https://github.com/mahkoh/wl-tray-bridge)
Shows applications using the D-Bus StatusNotifierItem protocol as tray icons.
- [window-to-tray](https://github.com/mahkoh/wl-proxy/tree/master/apps/window-to-tray)
Allows you to run most wayland applications as tray applications. For example,
to get a quick-access volume mixer in the tray:
```shell
~$ window-to-tray pavucontrol-qt
```
# Mouse Interactions
See [mouse-interactions.md](./mouse-interactions.md).
# Running
1. Switch to a virtual terminal by pressing `ctrl-alt-F2` (or F3, F4, ...).
2. Run `jay run`.
If you have not yet changed the default configuration, you can
- quit Jay by pressing `alt-q`,
- start Alacritty by pressing the left Windows key.

View file

@ -1,261 +0,0 @@
# Window and Client Rules
Jay supports powerful window and client rules similar to i3.
## Example
```toml
# Move spotify to workspace 3 and fullscreen it.
[[windows]]
match.client.sandbox-app-id = "com.spotify.Client"
action = [
{ type = "move-to-workspace", name = "3" },
"enter-fullscreen",
]
# Spawn the Chromium screen sharing window, the GIMP splash screen, and the
# JetBrains splash screen floating and without focus stealing.
[[windows]]
match.any = [
{ title-regex = 'is sharing (your screen|a window)\.$', client.comm = "chromium" },
{ title = "GIMP Startup", app-id = "gimp" },
{ title = "splash", x-class-regex = "^jetbrains-(clion|rustrover)$" }
]
initial-tile-state = "floating"
auto-focus = false
# Spawn the JetBrains project selector floating.
[[windows]]
match.title-regex = "^Welcome to (RustRover|CLion)$"
match.x-class-regex = "^jetbrains-(clion|rustrover)$"
initial-tile-state = "floating"
```
## General Principles
Each rule consists of three components:
1. Criteria that determine which clients/windows the rule applies to.
2. An action to execute when a client/window starts matching the rule.
3. An action to execute when a client/window stops matching the rule.
Each rule can be assigned a name which allows other rules to refer to it.
Additionally, rules have ad-hoc properties for things that are not easily
expressed via actions, such as whether a window should be mapped floating or
tiled.
```toml
[[windows]]
name = "..." # the rule name
match = { } # the rule criteria
action = "..." # the action to run on start
latch = "..." # the action to run on stop
```
Rules are re-evaluated whenever any of the referenced criteria changes. That is,
if you have the following rule
```toml
[[windows]]
match.title = "VIM"
action = "enter-fullscreen"
```
then the window will enter fullscreen whenever title changes from something that
is not `VIM` to `VIM`. For window rules, if you only want to match windows that
have just been mapped, you can set the `just-mapped` criterion to `true`:
```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.
Rules can trigger each other. For example:
```toml
[[windows]]
match.fullscreen = false
action = "enter-fullscreen"
[[windows]]
match.fullscreen = true
action = "exit-fullscreen"
```
This causes an infinite repetition of switching between windowed and fullscreen.
Jay prevents such loops from locking up the compositor by never performing more
than 1000 action callbacks before yielding to other work. However, they will
still cause the compositor to use 100% CPU and will likely cause affected
clients to be killed, since they won't be able to receive wayland messages fast
enough.
## Combining Criteria
Criteria can be combined with the following operations:
- `any` - match if any of a number of criteria match
- `all` - match if all of a number of criteria match
- `not` - match if a criterion does not match
- `exactly` - match if an exact number of criteria match
- `name` - match if another window rule with that name matches
```toml
# match windows that have the title `chromium` or `spotify`
match.any = [
{ title = "chromium" },
{ title = "spotify" },
]
# match windows whose title match both `chro` and `mium`
match.all = [
{ title-regex = "chro" },
{ title-regex = "mium" },
]
# match windows whose title is not `firefox`
match.not.title = "firefox"
# match windows whose title is `VIM` or whose clients are sandboxed, but not
# both
match.exactly.num = 1
match.exactly.list = [
{ title = "VIM" },
{ client.sandboxed = true },
]
# match if another rule called `another-rule-name` matches
match.name = "another-rule-name"
```
A criterion object has multiple fields, for example
```toml
match.title = "abc"
match.app-id = "xyz"
```
These fields are implicitly combined with `all` operator. That is, this behaves
just like
```toml
match.all = [
{ title = "abc" },
{ app-id = "xyz" },
]
```
## Finding Criteria Values
To determine which values to use in criteria, the `jay` executable provides the
subcommands `jay clients` and `jay tree` to inspect currently active clients and
open windows. For example
```text
~$ jay tree query select-window
- 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
```
In this case, `select-window` allows you to interactively select a window and
then prints its properties.
## Client Rules
```toml
# start executable `b` whenever a client with executable `A` connects
[[clients]]
match.exe = "A"
action = { type = "exec", exec = "b" }
```
All properties that can be referred to in client criteria are currently
constant over the lifetime of the client.
### Client Criteria
The full specification of client criteria can be found in
[spec.generated.md](../toml-spec/spec/spec.generated.md).
- `sandboxed` - Matches clients that are/aren't sandboxed.
- `sandbox-engine`, `sandbox-engine-regex` - Matches the sandbox engine that was
used to wrap this client. Usually `org.flatpak`.
- `sandbox-app-id`, `sandbox-app-id-regex` - Matches the app-id provided by the
sandbox engine
- `sandbox-instance-id-id`, `sandbox-instance-id-regex` - Matches the
instance-id provided by the sandbox engine
- `uid`, `pid` - Matches the UID/PID of the client.
- `is-xwayland` - Matches if the client is/isn't Xwayland.
- `comm`, `comm-regex` - Matches the `/proc/self/comm` of the client.
- `exe`, `exe-regex` - Matches the `/proc/self/exe` of the client.
- `tag`, `tag-regex` - Matches the tag of the client.
## Window Rules
## Ad-hoc Window Rules
Rule actions are evaluated asynchronously. For window rules, this means that
they are evaluated after the window has been mapped but before it is displayed
for the first time. This makes them ill-suited for things that need to be fixed
during the mapping process. Ad-hoc window rules can be used to bridge this gap:
```toml
[[windows]]
match.title = "chromium"
initial-tile-state = "floating"
auto-focus = false
```
The `initial-tile-state` rule can be used to define whether the window is mapped
tiled or floating. If no such rule exists, this is determined via heuristics.
If multiple such rules exist and match a window, the compositor picks one at
random.
The `auto-focus` rule determines if the window is automatically focused when it
is mapped. If no such rule exists, newly mapped windows always get the keyboard
focus except in some cases involving Xwayland. If multiple such rules exist and
match a window, then the window _does not_ get the focus if _any_ of them is set
to `false`.
## Window Criteria
The full specification of window criteria can be found in
[spec.generated.md](../toml-spec/spec/spec.generated.md).
- `types` - Matches the type of a window. Currently there are four types:
containers, placeholders, xdg toplevels, and X windows. If the rule does not
contain such a criterion, the rule will only match windows created by clients,
that is, xdg toplevels and X windows.
- `client` - This is a client criterion. See above.
- `title`, `title-regex` - Matches the title of the window.
- `app-id`, `app-id-regex` - Matches the XDG app-id of the window.
- `floating` - Matches if the window is/isn't floating.
- `visible` - Matches if the window is/isn't visible.
- `urgent` - Matches if the window wants/doesn't want attentions.
- `focused` - Matches if the window is/isn't focused.
- `fullscreen` - Matches if the window is/isn't fullscreen.
- `just-mapped` - Matches if the window has/hasn't just been mapped. This is
true for a single frame after the window has been mapped.
- `tag`, `tag-regex` - Matches the XDG toplevel tag of the window.
- `x-class`, `x-class-regex` - Matches the X class of the window.
- `x-instance`, `x-instance-regex` - Matches the X instance of the window.
- `x-role`, `x-role-regex` - Matches the X role of the window.
- `workspace`, `workspace-regex` - Matches the workspace of the window.
- `content-types` - Matches the content type of a window. Currently there are
three types: photos, videos, and games.

View file

@ -400,7 +400,7 @@ action = "enter-fullscreen"
```
These rules are described in detail in
[window-and-client-rules.md](docs/window-and-client-rules.md).
[Window & Client Rules](https://mahkoh.github.io/jay/book/window-rules.html).
## Window Management

View file

@ -844,7 +844,7 @@
},
"Color": {
"type": "string",
"description": "A color.\n\nThe format should be one of the following:\n\n- `#rgb`\n- `#rrggbb`\n- `#rgba`\n- `#rrggbba`\n"
"description": "A color.\n\nThe format should be one of the following:\n\n- `#rgb`\n- `#rrggbb`\n- `#rgba`\n- `#rrggbbaa`\n"
},
"ColorManagement": {
"description": "Describes color-management settings.\n\n- Example:\n\n ```toml\n [color-management]\n enabled = true\n ```\n",
@ -1109,6 +1109,10 @@
"description": "Configures the order of workspaces displayed.\n\nThe default is `manual`.\n\n- Example:\n\n ```toml\n workspace-display-order = \"sorted\"\n ```\n",
"$ref": "#/$defs/WorkspaceDisplayOrder"
},
"auto-reload": {
"type": "boolean",
"description": "Configures whether the compositor automatically reloads the configuration when\nthe config file changes on disk.\n\nWhen enabled, the compositor uses inotify to watch the config file and its parent\ndirectories for changes. Changes are debounced with a 400ms delay to avoid\nredundant reloads from rapid successive writes. If the file contents have not\nchanged, the reload is skipped.\n\nSetting this to `false` and will stop the file watcher. Removing this key entirely\nleaves the watcher state unchanged.\n\nThe default is `false`.\n\n- Example:\n\n ```toml\n auto-reload = true\n ```\n"
},
"simple-im": {
"description": "Configures the simple, XCompose based input method.\n\nBy default, the input method is enabled. \n\nEven if the input method is enabled, it will only be used if there is no\nrunning external IM.\n\n- Example:\n\n ```toml\n [simple-im]\n enabled = false\n ```\n",
"$ref": "#/$defs/SimpleIm"
@ -1638,6 +1642,10 @@
"description": "Whether the devices has been identified as a tablet pad.\n\n- Example:\n\n ```toml\n [[inputs]]\n match.is-tablet-tool = true\n tap-enabled = true\n ```\n"
},
"is-gesture": {
"type": "boolean",
"description": "Whether the devices has been identified as a gesture device.\n\n- Example:\n\n ```toml\n [[inputs]]\n match.is-gesture = true\n ```\n"
},
"is-switch": {
"type": "boolean",
"description": "Whether the devices has been identified as a switch.\n\n- Example:\n\n ```toml\n [[inputs]]\n match.is-switch = true\n ```\n"
}

View file

@ -1420,7 +1420,7 @@ The format should be one of the following:
- `#rgb`
- `#rrggbb`
- `#rgba`
- `#rrggbba`
- `#rrggbbaa`
Values of this type should be strings.
@ -2269,6 +2269,29 @@ The table has the following fields:
The value of this field should be a [WorkspaceDisplayOrder](#types-WorkspaceDisplayOrder).
- `auto-reload` (optional):
Configures whether the compositor automatically reloads the configuration when
the config file changes on disk.
When enabled, the compositor uses inotify to watch the config file and its parent
directories for changes. Changes are debounced with a 400ms delay to avoid
redundant reloads from rapid successive writes. If the file contents have not
changed, the reload is skipped.
Setting this to `false` and will stop the file watcher. Removing this key entirely
leaves the watcher state unchanged.
The default is `false`.
- Example:
```toml
auto-reload = true
```
The value of this field should be a boolean.
- `simple-im` (optional):
Configures the simple, XCompose based input method.
@ -3534,6 +3557,19 @@ The table has the following fields:
- `is-gesture` (optional):
Whether the devices has been identified as a gesture device.
- Example:
```toml
[[inputs]]
match.is-gesture = true
```
The value of this field should be a boolean.
- `is-switch` (optional):
Whether the devices has been identified as a switch.
- Example:

View file

@ -1208,7 +1208,7 @@ Color:
- `#rgb`
- `#rrggbb`
- `#rgba`
- `#rrggbba`
- `#rrggbbaa`
ConnectorMatch:
@ -1626,6 +1626,18 @@ InputMatch:
tap-enabled = true
```
is-gesture:
kind: boolean
required: false
description: |
Whether the devices has been identified as a gesture device.
- Example:
```toml
[[inputs]]
match.is-gesture = true
```
is-switch:
kind: boolean
required: false
description: |
@ -3081,6 +3093,28 @@ Config:
```toml
workspace-display-order = "sorted"
```
auto-reload:
kind: boolean
required: false
description: |
Configures whether the compositor automatically reloads the configuration when
the config file changes on disk.
When enabled, the compositor uses inotify to watch the config file and its parent
directories for changes. Changes are debounced with a 400ms delay to avoid
redundant reloads from rapid successive writes. If the file contents have not
changed, the reload is skipped.
Setting this to `false` and will stop the file watcher. Removing this key entirely
leaves the watcher state unchanged.
The default is `false`.
- Example:
```toml
auto-reload = true
```
simple-im:
ref: SimpleIm
required: false