diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-30 20:26:03 +0100 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-30 20:26:03 +0100 |
| commit | 5c8aaa581a1c92bd23b0bfb06734f1042facddb4 (patch) | |
| tree | 5c2641577fd10269731013c29364befebc04af28 | |
| parent | cfad2b0218dd05676a9a520f21911e49e5e4e5b9 (diff) | |
| download | quickshell-5c8aaa581a1c92bd23b0bfb06734f1042facddb4.tar.gz quickshell-5c8aaa581a1c92bd23b0bfb06734f1042facddb4.zip | |
docs: spec for the monitors settings page
The page owns sections/monitors.lua and regenerates it whole. A change
is tried live through hyprctl eval and written only on Keep; Revert or a
15s timeout is hyprctl reload, so a setting that blanks a screen never
reaches the file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| -rw-r--r-- | docs/superpowers/specs/2026-10-30-monitors-page-design.md | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/docs/superpowers/specs/2026-10-30-monitors-page-design.md b/docs/superpowers/specs/2026-10-30-monitors-page-design.md new file mode 100644 index 0000000..3d06740 --- /dev/null +++ b/docs/superpowers/specs/2026-10-30-monitors-page-design.md @@ -0,0 +1,96 @@ +# Monitors: a settings page for the outputs + +Date: 2026-10-30 + +## Goal + +Set up the connected monitors from the settings window: mode, scale, +rotation, VRR and position, with a to-scale layout to drag them into place. +A change is tried live and only written once kept, so a setting that blanks +a screen never reaches the config. + +## Where it lives + +`desktop/modules/monitors/`, a settings `Module` in the Hyprland group of the +sidebar, beside Keyboard shortcuts and Options: + +- `MonitorsModule.qml`: reads `hyprctl monitors -j`, holds the draft, runs + the eval, writes the file, runs the revert. +- `MonitorsPage.qml`: the layout canvas and one card per monitor. +- `Mon.js`: pure helpers, no QML: the Lua for a monitor and for the whole + file, logical size, snapping, mode deduplication. +- `test_mon.js`: `node desktop/modules/monitors/test_mon.js`, the one check. +- `README.md`. + +## The file + +The page owns `~/.config/hypr/sections/monitors.lua` (in `hypr-theme`, +required by `hyprland.lua`) and regenerates it whole: a header saying the +keystone settings window writes it, then one `hl.monitor({ ... })` block per +connected output, in `hyprctl monitors` order, with the fields `output`, +`mode`, `position`, `scale`, `transform`, and `vrr` only when on. Only +connected outputs are written; a monitor that is unplugged when the page +saves drops out of the file. Hand comments in the current file (two) are +not kept. + +## Draft + +Read from `hyprctl monitors -j` each time the page opens and after every +`configreloaded` raw event, unless an Apply is pending. Per monitor: + +- `mode`: `WxH@R` from `availableModes`, duplicates removed (the ASUS lists + `1920x1080@120.00Hz` twice). Shown with refresh to two decimals. The + current mode is matched against the list by width, height and the nearest + refresh, since `refreshRate` reads `99.94300` against `99.94Hz`. +- `scale`: a field, any number Hyprland accepts. +- `transform`: 0, 90, 180, 270 and a flipped toggle, stored as 0..7. +- `vrr`: a toggle. +- `position`: one of `auto`, `auto-left`, `auto-right`, `auto-up`, + `auto-down`, `auto-center-left`, `auto-center-right`, `auto-center-up`, + `auto-center-down`, or exact `XxY`. hyprctl only reports the resolved + `x`/`y`, so the page starts from the exact position; picking an auto value + is a choice in the card, and a drag always sets exact coordinates. + +Dirty when any field differs from what was read. SaveBar carries Apply and +Discard. + +## Layout canvas + +Each monitor is a rectangle at its logical size: width and height swapped +for an odd transform, both divided by scale. The whole arrangement is fitted +to the canvas with a margin. Each rectangle shows the output name and model. +Dragging moves one; on release its position snaps so that a near edge (within +a threshold in logical pixels) lies on an edge of another monitor, and it +becomes exact `XxY` in the draft, rounded to integers. A monitor set to an +auto position is drawn where Hyprland last put it, and dragging it switches +it to exact. + +## Apply, keep, revert + +1. Apply runs `hyprctl eval` with every monitor's `hl.monitor` line in one + call. Exit 7 is a refusal: nothing changed, the error text shows in + SaveBar's `error`, the draft stays dirty. +2. On success the change is live. A bar shows "Keep these settings? + Reverting in 15s", counting down, with Keep and Revert. +3. Keep writes `monitors.lua` through `FileView.setText`; Hyprland's + autoreload rereads it, which is now the same settings. +4. Revert, or the countdown reaching zero, runs `hyprctl reload`, which + reapplies the untouched file. The draft is reread from hyprctl. + +Closing the settings window while pending counts as Revert. If the shell dies +while pending, the file is still the old one and the next reload restores it. + +## Not handled + +Disabling an output, mirroring, bit depth and colour management; settings for +an output that is not connected; renaming outputs. The island and the window +switcher's hot corner are tied to DP-1 by name, which a move does not affect. + +## Check + +`test_mon.js` asserts: the Lua emitted for a monitor, including `vrr` only +when on; the logical size of a rotated and of a scaled monitor; a drag within +the threshold snapping to the other monitor's edge and one outside it not +snapping; duplicate modes collapsing; the nearest-refresh match picking +`99.94` for `99.943`. The eval refusal path was measured by hand: `transform += 9` exits 7 with `value 9 is more than the maximum of 7`. |
