Files
command-center/omp-docs/tools/niri/displays.md
T
Frank abb8a2a9d0 fix(autoswitch): WorkspacesChanged output-set diff; unit paths; deploy symlinks
- niri 26.04 has no hotplug event: OutputConfigChanged is only an ACK
  (niri-ipc). Watch WorkspacesChanged, diff sorted output set, apply on
  change. Proven live: eDP-1 off/on -> 3 output-set transitions, each
  matched + applied + reloaded.
- unit: %h/.local/bin/desk-autoswitch.sh, quoted Environment, drop
  User/Group (user unit); add desk-autoswitch.sh bin symlink
- desk.sh: silence validate/load-config/notify stdout
- deploy.sh: deploy repo symlinks (find -type l), so .local/bin links land
- docs: event semantics corrected (displays diagram, log)
2026-10-09 14:46:17 -07:00

6.0 KiB

type, title, description, tags, verified
type title description tags verified
reference Display Configuration Niri output configuration on blankpad — EDID-identity-keyed desk profiles, hotplug auto-switching, portrait transforms, position pinning, and GPU notes (AMD Cezanne now; Intel Arc render-node pin is dankware-legacy).
niri
display
monitors
rotation
desk-profiles
2026-10-09

Display Configuration

Niri output config on blankpad is a desk-profile system: the laptop travels between desks, connector names (DP-2, HDMI-A-1) reshuffle per dock, so panels are configured by EDID identity (manufacturer model serial), and positions live in per-desk files. A hotplug watcher and a manual picker keep the live layout matched to reality.

Mechanism: ~/.config/niri/cfg/display.kdl is a symlink to the active profile (desk-1-home.kdl, desk-2-portable.kdl, …). config.kdl includes it with include optional=true so a missing link is a warning, not a parse failure. Probe: niri msg outputs is authoritative (connector, full EDID long name, modes, transform, scale, position, VRR). wlr-randr remains available.


The desk-1 rig (home)

┌──────────────┬──────────────────────────────┬──────────┐
│  PM1601      │   Sceptre M34 ultrawide      │  eDP-1   │
│  portrait    │   3440x1440 @ 239.999        │ 1920x1080│
│  1200x1920   │   landscape, scale 1         │ scale 1  │
│  rot 90      │   position 1581,-600         │ 5021,-240│
└──────────────┴──────────────────────────────┴──────────┘
  HDMI-A-1            DP-2 (focus-at-startup)     eDP-1 (fallback focus)
Panel EDID long name Keyed by Notes
Sceptre M34 ultrawide Sceptre Tech Inc Sceptre M34 Unknown identity mode "3440x1440@239.999", focus-at-startup
PM1601 portrait not captured connector HDMI-A-1 dock-dependent; run desk.sh capture when plugged in, then re-key
eDP-1 laptop panel Lenovo Group Limited 0x40A9 Unknown identity scale 1.0 (deliberate; Ghostty grid at 1.25 rejected 2026-10-09), second focus-at-startup

VRR: DP-2 supports it; left disabled by decision (2026-10-09). Add variable-refresh-rate per output if that ever changes.

focus-at-startup on both M34 and eDP-1: niri focuses in config order, so the ultrawide wins when present and the laptop panel is the fallback.

Desk switching

graph LR
    P[plug / unplug] --> E[niri event-stream<br/>WorkspacesChanged → output-set diff]
    E --> M[desk-autoswitch.sh<br/>debounced matcher]
    M --> R{profile requires<br/>subset of connected?}
    R -->|best match| A[desk.sh apply:<br/>validate - relink - reload - notify]
    R -->|none| K[keep current + notify]
    D[Mod+Shift+D] --> C[desk.sh cycle] --> A
  • Matcher: each profile declares // requires: <long name> (all must be connected) and // optional: comment lines; the most-specific eligible profile wins.
  • Apply order is load-config-safe: niri validate the current config first, relink display.kdl, then niri msg action load-config-file, then a noctalia notification names the active desk. A profile that fails validation can never go live.
  • Manual override: Mod+Shift+D cycles profiles (wrong auto-pick, or same panels, new layout).
  • New desk: desk.sh capture prints connected long names → copy into a new desk-N-<name>.kdl (see desk-2-portable.kdl skeleton, portable monitor EDID pending first capture).
  • Unit: blankpad-desk-autoswitch.service (systemd user, ConditionHost=blankpad).

The config pattern

// desk-1-home.kdl — one block per panel, keyed by identity
output "Sceptre Tech Inc Sceptre M34 Unknown" {
    mode "3440x1440@239.999"
    scale 1
    transform "normal"
    position x=1581 y=-600
    focus-at-startup
}

Four rules learned the hard way:

  1. One output, one block. Two blocks matching the same output (connector name and long name) fight over properties — transform reverts are the classic symptom. Desk profiles are self-contained: a panel's identity, mode, transform, and position live in exactly one block per file.
  2. Key by EDID identity, not connector. Connector names swap between docks; the EDID long name travels with the panel. Serials on these panels read Unknown, so the full long name (manufacturer model serial) is the match string — copy it verbatim from niri msg outputs.
  3. Positions are absolute, not relational. A disconnected panel leaves a hole; that is exactly why positions belong to the desk profile, not a global file.
  4. Modes must match what the panel reports (niri msg outputs → Mode: current …). A mode the panel cannot drive leaves the output dark.

GPU assignment

blankpad is a single AMD Cezanne iGPU (/dev/dri/renderD128, PCI 07:00.0). No render-node pin exists in misc.kdl — and must not: the blankpad override file deliberately deletes the pin.

The environment { render-node "/dev/dri/renderD129" } pin found in the base misc.kdl is dankware-legacy (Intel Arc iGPU + discrete GPU hybrid; pin kept the iGPU as display owner and sidesteps the suspend/resume deadlock documented in the sleep-wake investigation). Deploying that base file on blankpad pins a render node that does not exist — this is why blankpad runs the zz-blankpad override layer (see [[index|the per-host symlink matrix]]).

Reload

niri msg action load-config-file reloads live. Never SIGHUP the process — see tips.

See Also

  • Niri — compositor overview and config layout
  • tips — reload path, niri msg layers/casts, includes
  • noctalia-shell — notification shown on desk switch
  • cachyos — operating system, including wlr-randr