1
0
Fork 0
forked from wry/wry

config: add resize action

This commit is contained in:
Julian Orth 2026-03-20 17:25:48 +01:00
parent 15c157b7a6
commit a1905ab971
14 changed files with 344 additions and 264 deletions

View file

@ -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
``` ```

View file

@ -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.

View file

@ -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 });
} }

View file

@ -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)]

View file

@ -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.

View file

@ -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.

View file

@ -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(())
} }

View file

@ -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 {

View file

@ -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)]

View file

@ -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));

View file

@ -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))
}
} }
} }
} }

View file

@ -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"
]
} }
] ]
} }

View file

@ -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`

View file

@ -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: