feat(display): menu-driven per-output resolution, in-flake
Phase 3 of the in-flake-state philosophy (mirroring night-light + the
keyboard-layout watcher): a System ▸ Display menu item picks an output's
resolution and persists it into the flake instead of a runtime-only tweak.
- rofi.nix: the `display` case reads `hyprctl monitors -j` (auto-skips the
chooser with one output), lists the output's advertised modes (rounded
refresh, deduped, highest-first) plus the preferred/highres/highrr
tokens, applies the pick INSTANTLY via `hyprctl keyword monitor` —
keeping the output's current position + scale, so only the resolution
changes — then persists it with `nomarchy-theme-sync set
settings.monitors.<name> <mode> --no-switch` (no rebuild).
- theme.nix: seed `settings.monitors = {}` (output-name -> "WxH@R") so a
sparse/older state file still evaluates; exposed via nomarchy.settings.
- hyprland.nix: `resolvedMonitors` overlays settings.monitors onto
nomarchy.monitors by name — a declared output keeps its other fields
with only the resolution swapped, an undeclared output (wildcard-only,
e.g. the laptop panel) becomes a fresh rule. So a later rebuild bakes
the menu pick into the generated monitor rule — the monitor twin of the
keyboard graduation. The menu pick wins over a hand-set resolution (it's
the explicit live action).
Monitor connector names are dot-free, so a direct dotted set-path is safe
(no whole-map read-modify-write like the keyboard watcher needs).
VM-verified via a headless-Hyprland nixosTest (run + removed; too heavy
for a CI gate): the graduation baked the rule into the generated
hyprland.conf, the set/get round-trip persisted, and `hyprctl keyword
monitor` actually changed the reported resolution. Only the interactive
rofi picker isn't headlessly driveable (same caveat as the keyboard watcher).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,32 @@ let
|
||||
++ lib.optional (m.extra != "") m.extra
|
||||
);
|
||||
|
||||
# Menu-remembered per-output resolutions (settings.monitors: output-name ->
|
||||
# "WxH@R", written instantly by the Display menu — see rofi.nix) overlaid onto
|
||||
# the declarative nomarchy.monitors by name: a declared output keeps its
|
||||
# position/scale/etc and only its resolution changes; an output covered solely
|
||||
# by the `,preferred,auto,1` wildcard (e.g. the laptop's built-in panel)
|
||||
# becomes a new rule with default position/scale. The menu writes the in-flake
|
||||
# state with --no-switch (no rebuild) and applies the mode live via hyprctl;
|
||||
# this bakes the same choice on the next rebuild — the monitor twin of the
|
||||
# keyboard graduation. The menu pick wins over a hand-set resolution (it's the
|
||||
# explicit live action); other declared fields are untouched.
|
||||
resOverrides = config.nomarchy.settings.monitors;
|
||||
# Skeleton for an output that's only menu-set (mirrors the monitorType defaults).
|
||||
monitorDefaults = {
|
||||
resolution = "preferred"; position = "auto"; scale = 1;
|
||||
transform = null; mirror = null; bitdepth = null; vrr = null; extra = "";
|
||||
};
|
||||
declaredNames = map (m: m.name) config.nomarchy.monitors;
|
||||
resolvedMonitors =
|
||||
map (m: if resOverrides ? ${m.name}
|
||||
then m // { resolution = resOverrides.${m.name}; }
|
||||
else m)
|
||||
config.nomarchy.monitors
|
||||
++ lib.mapAttrsToList
|
||||
(name: res: monitorDefaults // { inherit name; resolution = res; })
|
||||
(lib.filterAttrs (name: _: ! lib.elem name declaredNames) resOverrides);
|
||||
|
||||
# Candidate layouts offered by the new-keyboard picker: the session
|
||||
# layout(s) (nomarchy.keyboard.layout, comma-split for a multi-layout
|
||||
# session) plus the extra nomarchy.keyboard.layouts pool. The pool is
|
||||
@@ -163,7 +189,7 @@ in
|
||||
# and Hyprland applies them on hotplug. mkDefault so a downstream
|
||||
# `monitor = [...]` replaces the lot (the live ISO uses mkForce too).
|
||||
monitor = lib.mkDefault
|
||||
([ ",preferred,auto,1" ] ++ map monitorRule config.nomarchy.monitors);
|
||||
([ ",preferred,auto,1" ] ++ map monitorRule resolvedMonitors);
|
||||
|
||||
# exec-once is a list at normal priority, so downstream additions
|
||||
# CONCATENATE (your autostart runs alongside ours) rather than
|
||||
|
||||
Reference in New Issue
Block a user