--- name: niri-multi-monitor-screenshot-capture description: Diagnose flameshot screenshot failures on niri with 2+ outputs (portal single-output limit) and capture all monitors via niri-native per-output actions --- # Niri multi-monitor screenshot capture Flameshot works on a single niri output, dies the moment a second output connects. Diagnose via compositor journal, capture full desk with niri-native per-output actions — not grim, not portal. ## Diagnose (one pass) 1. `journalctl --user -u niri --since today | grep -i screenshot` - `WARN niri::niri: error taking a screenshot: Condition failed: outputs.len() == 1 (2 vs 1)` → flameshot's portal path (xdg-desktop-portal-gnome → niri GNOME-Screenshot D-Bus → `screenshot_all_outputs` `ensure!(outputs.len()==1)`). Upstream niri#4313, unfixed in 26.04. Symptoms: error bubbles appearing **after a delay** (portal round-trip), count = number of hotkey presses. 2. `niri msg outputs` — count active outputs. 1 output = flameshot fine; the bug is dock-state-dependent (portable works, docked breaks). 3. Per-mode status: `flameshot screen` (focused output) works docked; `flameshot gui` region works (mixed-res scaling bug = flameshot#4449); `flameshot full` dead on 2+. ## Capture all monitors (niri-native) niri 26.04 has NO all-output capture: screenshot UI is single-output by design (`screenshot_ui.rs`: "allows only single-output selections for now"); D-Bus path hard-limited. Script pattern: ```bash prev=$(niri msg --json focused-output | jq -r .name) while IFS= read -r mon; do [ "$mon" != "$prev" ] && niri msg action focus-monitor "$mon" sleep 0.15 # let compositor render a frame after focus move niri msg action screenshot-screen --path "$out/Screenshot from $ts $mon.png" done < <(niri msg --json outputs | jq -r '.[].name') # restore focus: niri msg action focus-monitor "$prev" ``` CLI facts (`niri msg action screenshot-screen --help`): `--path` must be ABSOLUTE; `-d/--write-to-disk` default true (also saves per `screenshot-path`); `-p/--show-pointer` default true. ## KDL gotchas (bit me twice; bats `niri validate` catches them) - KDL line comments are `//`, NEVER `#` — `#` = parse storm "identifiers cannot be used as arguments". - `screenshot-path` is a ROOT-level config node (`config.screenshot_path` in config.rs); do NOT nest it in `output {}` — root-level bare node validates and works; `output { ... }` without a name/match arg fails: "additional argument `name` is required". - Root-level `prefer-no-csd` is a silent no-op (output-section property) — root unknown nodes only warn. ## Verify - Run the script live; `file` each PNG (valid dimensions per monitor); `niri msg --json focused-output` equals pre-run monitor. - `bats tests/` incl. niri-config assembly validate on every desk profile before commit. - Deploy: symlink script into `~/.local/bin` (repo `scripts/` convention, like `desk.sh`); bind `spawn-sh "name"` in keybinds.kdl; niri hot-reloads config on file change.