feat: serial:/size: external matching, exclude Hyprland FALLBACK output

- external list now accepts serial: and size: prefixes; desc: matches
  serial too. Fixes docked-mode deadlock where Hyprland's synthetic
  FALLBACK output (created when the built-in is disabled by a stale
  docked config) was treated as a real external monitor, leaving the
  daemon stuck in docked after undocking.
- modes name resolution gains serial: prefix (resolveSerial).
- add -log-level flag for observability.
- add unit tests for external matching and name resolution.
- update README and example config.
This commit is contained in:
Maksim Totmin
2026-08-06 12:25:47 +07:00
parent 3515608fd3
commit ab6cad0505
10 changed files with 393 additions and 31 deletions
+17 -7
View File
@@ -144,11 +144,15 @@ restore_on_exit: true
# External monitors that trigger docked mode.
# Plain name: matches connector (DP-1, HDMI-A-1).
# desc: prefix: matches by monitor description (survives port rename).
# desc: prefix: matches by monitor description or serial (survives port rename).
# serial: prefix: matches by serial number.
# size: prefix: matches by resolution, optionally with refresh rate (size:2560x1440@165).
# Optional — if omitted or empty, the daemon auto-detects external monitors:
# any display whose connector is not eDP-/LVDS-/DSI- is treated as external.
external: # optional
- desc:Dell Inc. DELL U2723QE
- serial:3342300033911
- size:3440x1440
- DP-9
- DP-10
@@ -197,19 +201,22 @@ hooks:
### Monitor matching
Three match modes for the `modes` section (the `external` list supports plain names and `desc:`):
Match modes for the `modes` section and the `external` list. The `external` list supports all four modes below (plus auto-detect when empty); the `modes` section supports all four as `name:` values:
| Syntax | Matches | Use case |
|---|---|---|
| `DP-1` | Exact connector name | Simple setups, built-in displays |
| `desc:Dell U2723QE` | Substring in monitor description or serial | Survives port rename across different docks |
| `size:2560x1440` | Exact pixel dimensions | Two identical monitors with same resolution |
| `serial:3342300033911` | Substring in serial number | Uniquely identifies a specific monitor |
| `size:2560x1440` | Exact pixel dimensions | Match by resolution instead of connector name |
| `size:2560x1440@165` | Dimensions + refresh rate | Disambiguate identical models |
Sway description format: `make model serial_widthxheight` (with serial omitted if `Unknown`).
Run `hyprctl monitors all` (Hyprland) or `swaymsg -t get_outputs` (Sway) to see your monitor names and descriptions.
Run `hyprctl monitors all` (Hyprland) or `swaymsg -t get_outputs` (Sway) to see your monitor names, descriptions, and serials.
**desc: and size: in modes** — when a monitor name in `modes` uses `desc:` or `size:`, the daemon resolves it to the actual connector name at runtime. Ambiguous matches (a prefix matching multiple monitors with identical resolution) cause an error. For `size:`, add `@R` (refresh rate) to disambiguate monitors with the same resolution. Unresolvable names also cause an error.
**desc:, serial: and size: in modes** — when a monitor name in `modes` uses `desc:`, `serial:`, or `size:`, the daemon resolves it to the actual connector name at runtime. Ambiguous matches (a prefix matching multiple monitors) cause an error. For `size:`, add `@R` (refresh rate) to disambiguate monitors with the same resolution. Unresolvable names also cause an error.
**External list** — entries are checked against every detected monitor; a single match triggers docked mode. When the list is empty, any non-internal connector is treated as external.
### Backend-specific configuration
@@ -351,20 +358,23 @@ Check the hook command works from a terminal first. Hooks run via `sh -c`, so sh
### Monitor names changed after reboot
Use `desc:` or `size:` prefix matching instead of connector names. Both survive port renames across different docks and reboots.
Use `desc:`, `serial:`, or `size:` prefix matching instead of connector names. All three survive port renames across different docks and reboots.
**Hyprland:** `hyprctl monitors all` to see descriptions.
**Hyprland:** `hyprctl monitors all` to see descriptions and serials.
**Sway:** `swaymsg -t get_outputs` to see names, make/model, serial, and native resolution.
```yaml
external:
- desc:Dell Inc. DELL U2723QE # survives port rename
- serial:3342300033911 # match a specific monitor by serial
modes:
docked:
monitors:
- name: desc:Dell Inc. DELL U2723QE # also works here
enabled: true
- name: serial:3342300033911 # and here
enabled: true
- name: size:2560x1440@165 # match by dimensions + refresh
enabled: true
```