feat(monitor-scaling): per-monitor scale targeting with layout-safe position recompute - #11854
Open
ButtercupRobrts wants to merge 11 commits into
Open
ButtercupRobrts wants to merge 11 commits into
ButtercupRobrts wants to merge 11 commits into
Conversation
…nitor rule
Rewrite scale persistence so a change lands on the target monitor's own
hl.monitor() entry in monitors.lua instead of the shared
omarchy_monitor_scale variable or the output = "" catch-all, which a
named rule overrides on every reload -- the silent-revert bug behind
upstream issue 9950.
- set_scale gains an optional [monitor] target (default: focused) and
replays the monitor's live x/y in the hyprctl eval apply instead of
position = "auto", so configured layouts like -1200x0 survive.
- New persist_monitor_scale runs a single awk pass that blanks comments
and string contents at identical byte offsets, captures multi-line
hl.monitor({...}) blocks, and rewrites or inserts scale = for blocks
keyed by connector name or a desc: selector prefix-matching the
monitor's description or make/model/serial.
- When no rule names the output, a single-line name-keyed hl.monitor()
rule carrying live mode/position is appended -- the shape the
clamshell parser already reads.
- Every write is preceded by a timestamped monitors.lua.bak.<ts> backup
and goes through the file (cat > / >>) so a symlinked config stays a
symlink; GDK_SCALE is still pinned to the nearest integer.
- The bare invocation still prints the focused monitor's scale for
omarchy-monitor-state.
Fixture-per-shape coverage for the awk rewriter: variable-referencing and multi-line rules rewritten in place, scale-less rules gaining the key, desc:-keyed rules matched by monitor description, line- and block-commented rules ignored in favor of an append, the literal catch-all and other monitors' rules left byte-identical, plus the timestamped backup and symlink write-through guarantees.
Targeted set and step runs apply and persist to the named monitor with its live position; unknown names, unsafe names, and extra arguments are rejected non-zero with no eval and no write; the bare call still prints the focused monitor's scale; the audit log records the targeted name.
… Lua blanking pass
…e changes A fixed logical position encodes the old scale, so rescaling an edge-adjacent monitor strands a cursor-trapping gap or creates an overlap (upstream issue omacom#10922). set_scale now fetches the monitor array once, recomputes the position via recompute_monitor_position (adjacency candidates within ~5px, largest shared edge wins, minimal clamp for growth into non-adjacent monitors, abort back to live on a new overlap), and applies scale+position+transform in one atomic hyprctl eval followed by a verify-read. The audit log gains pos= and note= fields appended at line end, and a BASH_SOURCE guard makes the script sourceable for unit tests.
…'s own rule A corrected live position that never reaches monitors.lua recreates the overlap/gap on every reload. The awk rewriter now collects scale and position edits as (start, end, replacement) spans against the untouched block_nostr and applies them highest-offset-first -- splicing immediately would shift the block and invalidate later span offsets whenever position precedes scale. OMARCHY_NEW_POSITION flows to the rewriter through the environment (never -v), set only when the recompute moved the monitor; auto* positions and missing keys are never written since they re-derive on every reload.
GDK_SCALE is a global, so letting it follow whichever monitor was last scaled corrupts it on mixed-DPI setups: downscaling the densest display used to drop the global and shrink XWayland apps everywhere. The value is now round(max(new scale, every other monitor's reported scale)) from the same monitors -j fetch the recompute consumes. Round (nearest integer) stays correct because xwayland.force_zero_scaling makes this size fidelity, not sharpness.
Open
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
omarchy hyprland monitor scalinggains an optional monitor argument (scaling 1.6 HDMI-A-1) and becomes layout-safe: rescaling an edge-adjacent monitor now recomputes its position so shared edges stay shared, instead of overlapping a neighbor or stranding a cursor-trapping gap.Three pieces, one file:
hl.monitor()rule in~/.config/hypr/monitors.lua— by connector name ordesc:selector — rather than the sharedomarchy_monitor_scalecatch-all. Rules without a scale key gain one; monitors with no rule get a single-line appended rule. A timestamped.bak.<timestamp>backup precedes every write.hyprctl eval; a post-eval read-back warns on divergence since Hyprland can re-snap scales.omarchy_gdk_scale=round(max(all monitor scales)). Previously it rounded the last-scaled monitor — scaling a 1.25x secondary set the global GTK/XWayland factor to 1 and blurry-upscaled every denser display. Behavior change: the next scale operation writes a different value than before on multi-scale setups; this is a fix, flagged for review.Bonus fix riding along: the eval and appended rules now replay
transform— rescaling a portrait monitor no longer silently un-rotates it.Design note for reviewers
The in-place rewrite is a small Lua-aware awk pass (comments and string interiors blanked at stable byte offsets, edits spliced descending-offset). The simpler alternative — append-only rules, relying on Hyprland's last-match-wins — was considered and rejected: it accumulates shadowing duplicates and makes user edits to named rules silently stop applying. If upstream prefers append-only simplicity over file hygiene, the position/GDK logic carries over unchanged.
Test plan
bash test/shell.d/monitor-scaling-test.sh— 67 cases green: persistence shapes (named/desc:/multi-line/var-ref/auto rules), adjacency truth table (grow/shrink/both axes/±5px tolerance/deliberate gaps/sandwich ties/float-noise scales/portrait transforms), clamp+abort paths, atomic-eval single-call assertion, audit-field pinningmonitor-state-test,monitor-clamshell-scale-test,monitor-testall green./test/cligreen;./test/shellshows no delta vs baselinehyprctl reload, and leavesGDK_SCALEat the densest display's value