Merge pull request #821 from mahkoh/jorth/resize
config: add resize action
This commit is contained in:
commit
9d4ae64237
14 changed files with 344 additions and 264 deletions
360
book/AGENTS.md
360
book/AGENTS.md
|
|
@ -1,299 +1,135 @@
|
||||||
# Jay Book -- Agent Instructions
|
# Jay Book -- Agent Instructions
|
||||||
|
|
||||||
This file provides context for AI agents working on the Jay user documentation
|
User-facing [mdbook](https://rust-lang.github.io/mdBook/) documentation for
|
||||||
book (mdbook). Read this before making changes.
|
the [Jay Wayland compositor](https://github.com/mahkoh/jay). Target audience
|
||||||
|
is end users, not developers. Goal is feature discoverability.
|
||||||
|
|
||||||
## What is this?
|
## Quick reference
|
||||||
|
|
||||||
An [mdbook](https://rust-lang.github.io/mdBook/) providing comprehensive
|
### Book files (`book/src/`)
|
||||||
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 table of contents is `SUMMARY.md`. Key chapter-to-topic mapping:
|
||||||
|
|
||||||
The book is feature-complete. All 29 chapters cover every known Jay feature:
|
| File | Covers |
|
||||||
a features overview, installation (including AUR packages), running, the
|
|------|--------|
|
||||||
control center, all configuration sections, tiling, workspaces, floating
|
| `configuration/shortcuts.md` | Shortcuts, actions (simple + parameterized), marks, named actions, virtual outputs, actions in window rules |
|
||||||
windows, mouse interactions, input modes, window/client rules, screen sharing,
|
| `configuration/outputs.md` | Monitor config, VRR, tearing, scaling, transforms |
|
||||||
HDR & color management, the full CLI reference, and troubleshooting.
|
| `configuration/inputs.md` | Input devices, per-device settings |
|
||||||
|
| `configuration/misc.md` | Color management, libei, floating defaults, ui-drag |
|
||||||
|
| `window-rules.md` | Window/client rules, privileges, capabilities |
|
||||||
|
| `control-center.md` | All 11 control center panes |
|
||||||
|
| `cli.md` | All CLI subcommands, JSON output |
|
||||||
|
| `input-modes.md` | Modal keybinding system (push/pop/latch/clear) |
|
||||||
|
| `floating.md` | Floating windows, window management mode |
|
||||||
|
| `hdr.md` | HDR & color management walkthrough |
|
||||||
|
|
||||||
Three rounds of source-code verification have been completed and all
|
### Source-of-truth files (from repo root)
|
||||||
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:
|
| File | What it tells you |
|
||||||
|
|------|-------------------|
|
||||||
- Converted ~45 two-column tables to mdbook definition lists across 16 files.
|
| `toml-spec/spec/spec.yaml` | **Canonical** TOML config spec: every key, action, match criterion, type |
|
||||||
- Lowercased all keyboard shortcut modifiers to match TOML parser expectations.
|
| `toml-config/src/default-config.toml` | Built-in default config (keybindings, startup actions) |
|
||||||
- Reformatted long inline TOML tables into multiline format with trailing
|
| `toml-config/src/config/parsers/action.rs` | Action parser — see which `type` strings are accepted |
|
||||||
commas and 4-space indentation.
|
| `toml-config/src/lib.rs` | Action dispatch — `window_or_seat!` macro shows which actions work in window rules |
|
||||||
- Created a dedicated Features chapter (`features.md`) consolidating content
|
| `src/config/handler.rs` | Config handler; `update_capabilities` shows capability replacement semantics |
|
||||||
from the old `docs/features.md` and the introduction; removed the duplicated
|
| `src/cli/*.rs` | CLI subcommands (clap definitions) |
|
||||||
feature list from `introduction.md`.
|
| `src/control_center/cc_*.rs` | Control center pane implementations (verify field names/ordering here) |
|
||||||
- Added a comprehensive "Granting Privileges" section to `window-rules.md` and
|
| `toml-config/src/config/parsers/exec.rs` | Exec parser (string, array, or table forms) |
|
||||||
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".
|
|
||||||
|
|
||||||
A fourth update documented the new `--json` and `--all-json-fields` global
|
|
||||||
CLI flags, which enable machine-readable JSONL output from all query/status
|
|
||||||
subcommands. A new "JSON Output" section was added to `cli.md` listing all
|
|
||||||
supported commands, the JSONL format, field omission behavior, and `jq`
|
|
||||||
usage examples.
|
|
||||||
|
|
||||||
**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
|
### Known spec.yaml bugs
|
||||||
|
|
||||||
- `px-per-wheel-scroll` is listed as `kind: boolean` but the parser
|
- `px-per-wheel-scroll`: listed as `kind: boolean` but parser uses `fltorint`
|
||||||
(`toml-config/src/config/parsers/input.rs`) uses `fltorint` (a number).
|
(a number). Book correctly documents it as numeric.
|
||||||
The documentation correctly describes it as a numeric value.
|
|
||||||
|
|
||||||
## Critical facts to get right
|
## Critical facts
|
||||||
|
|
||||||
1. **Config replacement semantics.** Once `~/.config/jay/config.toml` exists,
|
1. **Config replacement.** `~/.config/jay/config.toml` replaces the entire
|
||||||
the entire built-in default configuration is replaced -- not merged. Even an
|
built-in default — not merged. Empty file = no shortcuts, nothing.
|
||||||
empty config file means no shortcuts, no startup actions, nothing. Users must
|
Users must run `jay config init`.
|
||||||
run `jay config init` to get a config pre-populated with the defaults.
|
|
||||||
|
|
||||||
2. **Config reload.** By default, Jay does not automatically reload
|
2. **Config reload.** Manual by default (`alt-shift-r` / `reload-config-toml`).
|
||||||
config.toml. Reload must be triggered manually via the `alt-shift-r`
|
`auto-reload = true` enables inotify watching with 400 ms debounce.
|
||||||
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
|
3. **Actions are composable.** Anywhere an action is accepted, an array works.
|
||||||
(OpenGL via libEGL+libGLESv2, or Vulkan via libvulkan + a GPU driver).
|
Named actions (`$name`) add reuse.
|
||||||
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
|
4. **Exec action gotcha.** Plain string = program name only (no arg splitting).
|
||||||
actions can be used instead. Named actions (`$name`) add another layer of
|
`exec = "notify-send 'hello'"` is wrong — use array form.
|
||||||
reuse.
|
|
||||||
|
|
||||||
5. **Match systems.** Outputs, connectors, DRM devices, inputs, clients, and
|
5. **Capability replacement.** When any client rule matches, its capabilities
|
||||||
windows each have their own match system. All support the same logical
|
**replace** defaults entirely (not additive). Verify against
|
||||||
combinators: AND (multiple fields in one table), OR (array of matchers),
|
`handler.rs:update_capabilities`.
|
||||||
`not`, `all`, `any`, `exactly`.
|
|
||||||
|
|
||||||
6. **Input modes.** Jay supports an input mode stack (`push-mode`,
|
6. **Window rule actions.** Actions using `window_or_seat!` in `lib.rs` work
|
||||||
`latch-mode`, `pop-mode`, `clear-modes`). Each mode can define its own
|
in both shortcuts (focused window) and window rules (matched window).
|
||||||
shortcuts and inherit from a parent mode. This is how vim-style modal
|
The list in shortcuts.md "Actions in window rules" must stay in sync.
|
||||||
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
|
7. **Window/client rules are reactive.** Re-evaluated when criteria change.
|
||||||
matching criteria change (e.g. a window's title changes). The `latch` action
|
`latch` fires when a rule stops matching.
|
||||||
fires when a previously-matching rule stops matching.
|
|
||||||
|
|
||||||
8. **Control center.** Opened with `alt-c` (default) or `jay control-center`.
|
8. **VRR vs tearing subtlety.** VRR variant1/2 use "fullscreen"; tearing
|
||||||
It is an egui-based GUI with 11 panes: Clients, Color Management,
|
variant3 says "a single application is displayed" (not necessarily
|
||||||
Compositor, GPUs, Idle, Input, Look and Feel, Outputs (with visual
|
fullscreen). Always check spec.yaml wording.
|
||||||
arrangement editor), Virtual Outputs, Window Search, and Xwayland.
|
|
||||||
|
|
||||||
9. **VRR and tearing.** Both have per-output overrides and multiple mode
|
## Style rules
|
||||||
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`,
|
- **Audience:** users, not developers. Explain what, not how.
|
||||||
`is-touch`, `is-tablet-tool`, `is-tablet-pad`, `is-gesture`, `is-switch`.
|
- **Code fences:** ` ```shell ` for commands (prefix `~$`), ` ```toml ` for config.
|
||||||
Note that `is-switch` exists in the parser but is missing from spec.yaml.
|
- **Admonitions:** `> [!NOTE]`, `> [!TIP]`, `> [!WARNING]`, `> [!IMPORTANT]`.
|
||||||
|
- **Key combos:** backticks, lowercase modifiers: `alt-shift-c`, `ctrl-alt-F2`,
|
||||||
|
`alt-Return`. Never `Alt+C`.
|
||||||
|
- **Definition lists** for two-column term/description. Tables only for 3+ data columns.
|
||||||
|
- **TOML formatting:** multiline with trailing commas, 4-space indent.
|
||||||
|
- **Examples:** practical, not abstract. Link to
|
||||||
|
[spec.generated.md](https://github.com/mahkoh/jay/blob/master/toml-spec/spec/spec.generated.md)
|
||||||
|
for exhaustive listings.
|
||||||
|
- **Control center docs:** verify field names, ordering, and conditional
|
||||||
|
visibility against `cc_*.rs` source files. Labels must match exactly.
|
||||||
|
|
||||||
11. **Features chapter is the single source for the feature list.** The
|
## Common tasks
|
||||||
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
|
### Documenting a new action
|
||||||
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
|
1. Read `git diff` for the commit introducing the action. Key files:
|
||||||
forms: a plain string (program name only, no arguments), an array of
|
- `toml-spec/spec/spec.yaml` — spec entry (description, fields, examples)
|
||||||
strings (first element is the program, rest are arguments), or a table
|
- `toml-config/src/config/parsers/action.rs` — parser (field names, types, defaults)
|
||||||
with `prog`/`shell`, `args`, `env`, `privileged`, and `tag` fields. A
|
- `toml-config/src/lib.rs` — dispatch (check if `window_or_seat!` is used)
|
||||||
common mistake is using a plain string with spaces like
|
- `jay-config/src/input.rs` and/or `jay-config/src/window.rs` — Rust API
|
||||||
`exec = "notify-send 'hello'"` -- this treats the entire string as the
|
|
||||||
program name. Use the array form instead:
|
|
||||||
`exec = ["notify-send", "hello"]`.
|
|
||||||
|
|
||||||
## Style guidelines
|
2. Edit `book/src/configuration/shortcuts.md`:
|
||||||
|
- **Simple actions** (no fields): add to the appropriate list in the
|
||||||
|
"Simple actions" section.
|
||||||
|
- **Parameterized actions** (has fields): add a new `###` subsection before
|
||||||
|
"Other parameterized actions". Include definition list for fields and
|
||||||
|
practical TOML examples.
|
||||||
|
- **Also parameterized but minor:** just add a `- name -- description`
|
||||||
|
bullet to the "Other parameterized actions" list.
|
||||||
|
|
||||||
- Write for users, not developers. Explain what things do, not how they are
|
3. If `window_or_seat!` is used in `lib.rs`, add the action name to the
|
||||||
implemented.
|
"Actions in window rules" list at the bottom of `shortcuts.md`.
|
||||||
- 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
|
### Documenting a new config field
|
||||||
|
|
||||||
When making changes, verify against source code. The most error-prone areas
|
1. Read `toml-spec/spec/spec.yaml` for the field definition.
|
||||||
are:
|
2. Identify which book chapter covers that config section (see table above).
|
||||||
|
3. Add the field with a definition-list entry or example, matching the
|
||||||
|
existing style of that chapter.
|
||||||
|
|
||||||
1. **Control center pane documentation** -- Field names, ordering, and
|
### Documenting a new CLI subcommand
|
||||||
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
|
1. Read `src/cli/*.rs` for clap definitions.
|
||||||
between modes and between VRR vs tearing. Always check spec.yaml wording.
|
2. Edit `book/src/cli.md`. Follow the existing pattern for subcommand docs.
|
||||||
|
3. If the subcommand has `--json` support, mention it in the "JSON Output" section.
|
||||||
|
|
||||||
3. **Exec action examples** -- A plain string is the program name only (no
|
### Documenting a control center change
|
||||||
argument splitting). Use arrays or tables when arguments are needed.
|
|
||||||
|
|
||||||
4. **Capability/privilege semantics** -- Capabilities from matching rules
|
1. Read the relevant `src/control_center/cc_*.rs` file.
|
||||||
replace defaults; they do not add to them. Verify against
|
2. Edit `book/src/control-center.md`. Match field names, ordering, and
|
||||||
`src/config/handler.rs:update_capabilities` if in doubt.
|
conditional visibility exactly.
|
||||||
|
|
||||||
## Building the book
|
## Building
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
~$ cd book
|
~$ cd book && mdbook build # outputs to book/book/
|
||||||
~$ mdbook build # outputs to book/book/
|
~$ cd book && mdbook serve # preview at http://localhost:3000
|
||||||
~$ mdbook serve # local preview at http://localhost:3000
|
|
||||||
```
|
|
||||||
|
|
||||||
Requires [mdbook](https://github.com/rust-lang/mdBook) to be installed:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
~$ cargo install mdbook
|
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -196,6 +196,46 @@ logo-ctrl-shift-Left = {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Resizing windows
|
||||||
|
|
||||||
|
The `resize` action resizes the focused window by adjusting its edges.
|
||||||
|
Each of the four optional fields (`dx1`, `dy1`, `dx2`, `dy2`) specifies a
|
||||||
|
pixel offset for one edge of the window. Fields default to `0` when omitted.
|
||||||
|
|
||||||
|
`dx1`
|
||||||
|
: Change at the left edge (negative = grow left, positive = shrink left)
|
||||||
|
|
||||||
|
`dy1`
|
||||||
|
: Change at the top edge (negative = grow up, positive = shrink down)
|
||||||
|
|
||||||
|
`dx2`
|
||||||
|
: Change at the right edge (positive = grow right, negative = shrink right)
|
||||||
|
|
||||||
|
`dy2`
|
||||||
|
: Change at the bottom edge (positive = grow down, negative = shrink down)
|
||||||
|
|
||||||
|
Growing the window by 10 pixels on the right:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[shortcuts]
|
||||||
|
alt-Right = { type = "resize", dx2 = 10 }
|
||||||
|
```
|
||||||
|
|
||||||
|
Shrinking the window by 10 pixels on the right:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[shortcuts]
|
||||||
|
alt-Left = { type = "resize", dx2 = -10 }
|
||||||
|
```
|
||||||
|
|
||||||
|
Moving the window 10 pixels to the right without changing its size (both
|
||||||
|
left and right edges shift by the same amount):
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[shortcuts]
|
||||||
|
alt-shift-Right = { type = "resize", dx1 = 10, dx2 = 10 }
|
||||||
|
```
|
||||||
|
|
||||||
### Other parameterized actions
|
### Other parameterized actions
|
||||||
|
|
||||||
- `set-keymap` -- change the active keymap
|
- `set-keymap` -- change the active keymap
|
||||||
|
|
@ -499,7 +539,7 @@ affected actions are: `move-left`, `move-down`, `move-up`, `move-right`,
|
||||||
`split-horizontal`, `split-vertical`, `toggle-split`, `tile-horizontal`,
|
`split-horizontal`, `split-vertical`, `toggle-split`, `tile-horizontal`,
|
||||||
`tile-vertical`, `show-single`, `show-all`, `toggle-fullscreen`,
|
`tile-vertical`, `show-single`, `show-all`, `toggle-fullscreen`,
|
||||||
`enter-fullscreen`, `exit-fullscreen`, `close`, `toggle-floating`, `float`,
|
`enter-fullscreen`, `exit-fullscreen`, `close`, `toggle-floating`, `float`,
|
||||||
`tile`, `toggle-float-pinned`, `pin-float`, and `unpin-float`.
|
`tile`, `toggle-float-pinned`, `pin-float`, `unpin-float`, and `resize`.
|
||||||
|
|
||||||
Similarly, `kill-client` applies to the matched window's client in a window
|
Similarly, `kill-client` applies to the matched window's client in a window
|
||||||
rule, or to the matched client in a client rule.
|
rule, or to the matched client in a client rule.
|
||||||
|
|
|
||||||
|
|
@ -826,6 +826,16 @@ impl ConfigClient {
|
||||||
self.send(&ClientMessage::WindowClose { window });
|
self.send(&ClientMessage::WindowClose { window });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resize_window(&self, window: Window, dx1: i32, dy1: i32, dx2: i32, dy2: i32) {
|
||||||
|
self.send(&ClientMessage::WindowResize {
|
||||||
|
window,
|
||||||
|
dx1,
|
||||||
|
dy1,
|
||||||
|
dx2,
|
||||||
|
dy2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn focus_seat_parent(&self, seat: Seat) {
|
pub fn focus_seat_parent(&self, seat: Seat) {
|
||||||
self.send(&ClientMessage::FocusSeatParent { seat });
|
self.send(&ClientMessage::FocusSeatParent { seat });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -864,6 +864,13 @@ pub enum ClientMessage<'a> {
|
||||||
CleanLogsOlderThan {
|
CleanLogsOlderThan {
|
||||||
time: SystemTime,
|
time: SystemTime,
|
||||||
},
|
},
|
||||||
|
WindowResize {
|
||||||
|
window: Window,
|
||||||
|
dx1: i32,
|
||||||
|
dy1: i32,
|
||||||
|
dx2: i32,
|
||||||
|
dy2: i32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
|
||||||
|
|
@ -650,6 +650,11 @@ impl Seat {
|
||||||
pub fn warp_mouse_to_focus(self) {
|
pub fn warp_mouse_to_focus(self) {
|
||||||
get!().seat_warp_mouse_to_focus(self)
|
get!().seat_warp_mouse_to_focus(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resizes the focused window.
|
||||||
|
pub fn resize(self, dx1: i32, dy1: i32, dx2: i32, dy2: i32) {
|
||||||
|
self.window().resize(dx1, dy1, dx2, dy2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A focus-follows-mouse mode.
|
/// A focus-follows-mouse mode.
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,11 @@ impl Window {
|
||||||
pub fn toggle_float_pinned(self) {
|
pub fn toggle_float_pinned(self) {
|
||||||
self.set_float_pinned(!self.float_pinned());
|
self.set_float_pinned(!self.float_pinned());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resizes the window.
|
||||||
|
pub fn resize(self, dx1: i32, dy1: i32, dx2: i32, dy2: i32) {
|
||||||
|
get!().resize_window(self, dx1, dy1, dx2, dy2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A window matcher.
|
/// A window matcher.
|
||||||
|
|
|
||||||
|
|
@ -2623,6 +2623,18 @@ impl ConfigProxyHandler {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_window_resize(
|
||||||
|
&self,
|
||||||
|
window: Window,
|
||||||
|
dx1: i32,
|
||||||
|
dy1: i32,
|
||||||
|
dx2: i32,
|
||||||
|
dy2: i32,
|
||||||
|
) -> Result<(), CphError> {
|
||||||
|
self.get_window(window)?.tl_resize(dx1, dy1, dx2, dy2);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_window_exists(&self, window: Window) {
|
fn handle_window_exists(&self, window: Window) {
|
||||||
self.respond(Response::WindowExists {
|
self.respond(Response::WindowExists {
|
||||||
exists: self.get_window(window).is_ok(),
|
exists: self.get_window(window).is_ok(),
|
||||||
|
|
@ -3381,6 +3393,15 @@ impl ConfigProxyHandler {
|
||||||
ClientMessage::CreateVirtualOutput { name } => self.handle_create_virtual_output(name),
|
ClientMessage::CreateVirtualOutput { name } => self.handle_create_virtual_output(name),
|
||||||
ClientMessage::RemoveVirtualOutput { name } => self.handle_remove_virtual_output(name),
|
ClientMessage::RemoveVirtualOutput { name } => self.handle_remove_virtual_output(name),
|
||||||
ClientMessage::CleanLogsOlderThan { time } => self.handle_clean_logs_older_than(time),
|
ClientMessage::CleanLogsOlderThan { time } => self.handle_clean_logs_older_than(time),
|
||||||
|
ClientMessage::WindowResize {
|
||||||
|
window,
|
||||||
|
dx1,
|
||||||
|
dy1,
|
||||||
|
dx2,
|
||||||
|
dy2,
|
||||||
|
} => self
|
||||||
|
.handle_window_resize(window, dx1, dy1, dx2, dy2)
|
||||||
|
.wrn("window_resize")?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ pub trait ToplevelNode: ToplevelNodeBase {
|
||||||
fn tl_set_float(&self, float: Option<&Rc<FloatNode>>);
|
fn tl_set_float(&self, float: Option<&Rc<FloatNode>>);
|
||||||
fn tl_mark_ancestor_fullscreen(&self, fullscreen: bool);
|
fn tl_mark_ancestor_fullscreen(&self, fullscreen: bool);
|
||||||
fn tl_mark_fullscreen(&self, fullscreen: bool);
|
fn tl_mark_fullscreen(&self, fullscreen: bool);
|
||||||
|
fn tl_resize(&self, dx1: i32, dy1: i32, dx2: i32, dy2: i32);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: ToplevelNodeBase> ToplevelNode for T {
|
impl<T: ToplevelNodeBase> ToplevelNode for T {
|
||||||
|
|
@ -250,6 +251,18 @@ impl<T: ToplevelNodeBase> ToplevelNode for T {
|
||||||
self.tl_mark_ancestor_fullscreen(fullscreen);
|
self.tl_mark_ancestor_fullscreen(fullscreen);
|
||||||
self.tl_mark_fullscreen_ext();
|
self.tl_mark_fullscreen_ext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tl_resize(&self, dx1: i32, dy1: i32, dx2: i32, dy2: i32) {
|
||||||
|
let Some(parent) = self.tl_data().parent.get() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let pos = self.node_absolute_position();
|
||||||
|
let x1 = (dx1 != 0).then_some(pos.x1().saturating_add(dx1));
|
||||||
|
let y1 = (dy1 != 0).then_some(pos.y1().saturating_add(dy1));
|
||||||
|
let x2 = (dx2 != 0).then_some(pos.x2().saturating_add(dx2));
|
||||||
|
let y2 = (dy2 != 0).then_some(pos.y2().saturating_add(dy2));
|
||||||
|
parent.cnode_resize_child(self, x1, y1, x2, y2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ToplevelNodeBase: Node {
|
pub trait ToplevelNodeBase: Node {
|
||||||
|
|
|
||||||
|
|
@ -190,6 +190,12 @@ pub enum Action {
|
||||||
RemoveVirtualOutput {
|
RemoveVirtualOutput {
|
||||||
name: String,
|
name: String,
|
||||||
},
|
},
|
||||||
|
Resize {
|
||||||
|
dx1: i32,
|
||||||
|
dy1: i32,
|
||||||
|
dx2: i32,
|
||||||
|
dy2: i32,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use {
|
||||||
config::{
|
config::{
|
||||||
Action, SimpleCommand,
|
Action, SimpleCommand,
|
||||||
context::Context,
|
context::Context,
|
||||||
extractor::{Extractor, ExtractorError, arr, bol, n32, opt, str, val},
|
extractor::{Extractor, ExtractorError, arr, bol, n32, opt, s32, str, val},
|
||||||
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
parser::{DataType, ParseResult, Parser, UnexpectedDataType},
|
||||||
parsers::{
|
parsers::{
|
||||||
StringParser, StringParserError,
|
StringParser, StringParserError,
|
||||||
|
|
@ -495,6 +495,21 @@ impl ActionParser<'_> {
|
||||||
name: name.value.to_string(),
|
name: name.value.to_string(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_resize(&mut self, ext: &mut Extractor<'_>) -> ParseResult<Self> {
|
||||||
|
let (dx1, dy1, dx2, dy2) = ext.extract((
|
||||||
|
opt(s32("dx1")),
|
||||||
|
opt(s32("dy1")),
|
||||||
|
opt(s32("dx2")),
|
||||||
|
opt(s32("dy2")),
|
||||||
|
))?;
|
||||||
|
Ok(Action::Resize {
|
||||||
|
dx1: dx1.despan().unwrap_or(0),
|
||||||
|
dy1: dy1.despan().unwrap_or(0),
|
||||||
|
dx2: dx2.despan().unwrap_or(0),
|
||||||
|
dy2: dy2.despan().unwrap_or(0),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parser for ActionParser<'_> {
|
impl Parser for ActionParser<'_> {
|
||||||
|
|
@ -556,6 +571,7 @@ impl Parser for ActionParser<'_> {
|
||||||
"latch-mode" => self.parse_latch_mode(&mut ext),
|
"latch-mode" => self.parse_latch_mode(&mut ext),
|
||||||
"create-virtual-output" => self.parse_create_virtual_output(&mut ext),
|
"create-virtual-output" => self.parse_create_virtual_output(&mut ext),
|
||||||
"remove-virtual-output" => self.parse_remove_virtual_output(&mut ext),
|
"remove-virtual-output" => self.parse_remove_virtual_output(&mut ext),
|
||||||
|
"resize" => self.parse_resize(&mut ext),
|
||||||
v => {
|
v => {
|
||||||
ext.ignore_unused();
|
ext.ignore_unused();
|
||||||
return Err(ActionParserError::UnknownType(v.to_string()).spanned(ty.span));
|
return Err(ActionParserError::UnknownType(v.to_string()).spanned(ty.span));
|
||||||
|
|
|
||||||
|
|
@ -482,6 +482,9 @@ impl Action {
|
||||||
}
|
}
|
||||||
Action::CreateVirtualOutput { name } => b.new(move || create_virtual_output(&name)),
|
Action::CreateVirtualOutput { name } => b.new(move || create_virtual_output(&name)),
|
||||||
Action::RemoveVirtualOutput { name } => b.new(move || remove_virtual_output(&name)),
|
Action::RemoveVirtualOutput { name } => b.new(move || remove_virtual_output(&name)),
|
||||||
|
Action::Resize { dx1, dy1, dx2, dy2 } => {
|
||||||
|
window_or_seat!(s, s.resize(dx1, dy1, dx2, dy2))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -608,6 +608,34 @@
|
||||||
"type",
|
"type",
|
||||||
"name"
|
"name"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Resizes the focused window.\n\n- Example (Growing the window by 2 pixels on the left):\n\n ```toml\n [shortcuts]\n alt-x = { type = \"resize\", dx1 = -2 }\n ```\n\n- Example (Moving the window 2 pixels to the left):\n\n ```toml\n [shortcuts]\n alt-x = { type = \"resize\", dx1 = -2, dx2 = -2 }\n ```\n",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "resize"
|
||||||
|
},
|
||||||
|
"dx1": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The change at the left edge of the window."
|
||||||
|
},
|
||||||
|
"dy1": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The change at the top edge of the window."
|
||||||
|
},
|
||||||
|
"dx2": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The change at the right edge of the window."
|
||||||
|
},
|
||||||
|
"dy2": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The change at the bottom edge of the window."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -889,6 +889,58 @@ This table is a tagged union. The variant is determined by the `type` field. It
|
||||||
|
|
||||||
The value of this field should be a string.
|
The value of this field should be a string.
|
||||||
|
|
||||||
|
- `resize`:
|
||||||
|
|
||||||
|
Resizes the focused window.
|
||||||
|
|
||||||
|
- Example (Growing the window by 2 pixels on the left):
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[shortcuts]
|
||||||
|
alt-x = { type = "resize", dx1 = -2 }
|
||||||
|
```
|
||||||
|
|
||||||
|
- Example (Moving the window 2 pixels to the left):
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[shortcuts]
|
||||||
|
alt-x = { type = "resize", dx1 = -2, dx2 = -2 }
|
||||||
|
```
|
||||||
|
|
||||||
|
The table has the following fields:
|
||||||
|
|
||||||
|
- `dx1` (optional):
|
||||||
|
|
||||||
|
The change at the left edge of the window.
|
||||||
|
|
||||||
|
The value of this field should be a number.
|
||||||
|
|
||||||
|
The numbers should be integers.
|
||||||
|
|
||||||
|
- `dy1` (optional):
|
||||||
|
|
||||||
|
The change at the top edge of the window.
|
||||||
|
|
||||||
|
The value of this field should be a number.
|
||||||
|
|
||||||
|
The numbers should be integers.
|
||||||
|
|
||||||
|
- `dx2` (optional):
|
||||||
|
|
||||||
|
The change at the right edge of the window.
|
||||||
|
|
||||||
|
The value of this field should be a number.
|
||||||
|
|
||||||
|
The numbers should be integers.
|
||||||
|
|
||||||
|
- `dy2` (optional):
|
||||||
|
|
||||||
|
The change at the bottom edge of the window.
|
||||||
|
|
||||||
|
The value of this field should be a number.
|
||||||
|
|
||||||
|
The numbers should be integers.
|
||||||
|
|
||||||
|
|
||||||
<a name="types-BarPosition"></a>
|
<a name="types-BarPosition"></a>
|
||||||
### `BarPosition`
|
### `BarPosition`
|
||||||
|
|
|
||||||
|
|
@ -851,6 +851,44 @@ Action:
|
||||||
description: The name of the output.
|
description: The name of the output.
|
||||||
required: true
|
required: true
|
||||||
kind: string
|
kind: string
|
||||||
|
resize:
|
||||||
|
description: |
|
||||||
|
Resizes the focused window.
|
||||||
|
|
||||||
|
- Example (Growing the window by 2 pixels on the left):
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[shortcuts]
|
||||||
|
alt-x = { type = "resize", dx1 = -2 }
|
||||||
|
```
|
||||||
|
|
||||||
|
- Example (Moving the window 2 pixels to the left):
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[shortcuts]
|
||||||
|
alt-x = { type = "resize", dx1 = -2, dx2 = -2 }
|
||||||
|
```
|
||||||
|
fields:
|
||||||
|
dx1:
|
||||||
|
description: The change at the left edge of the window.
|
||||||
|
required: false
|
||||||
|
kind: number
|
||||||
|
integer_only: true
|
||||||
|
dy1:
|
||||||
|
description: The change at the top edge of the window.
|
||||||
|
required: false
|
||||||
|
kind: number
|
||||||
|
integer_only: true
|
||||||
|
dx2:
|
||||||
|
description: The change at the right edge of the window.
|
||||||
|
required: false
|
||||||
|
kind: number
|
||||||
|
integer_only: true
|
||||||
|
dy2:
|
||||||
|
description: The change at the bottom edge of the window.
|
||||||
|
required: false
|
||||||
|
kind: number
|
||||||
|
integer_only: true
|
||||||
|
|
||||||
|
|
||||||
Exec:
|
Exec:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue