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:
@@ -183,6 +183,9 @@ func (d *Daemon) pollCheck(ctx context.Context) {
|
||||
}
|
||||
|
||||
// determineState checks whether any external monitor is physically connected.
|
||||
// Synthetic outputs that never represent a real external display (e.g.
|
||||
// Hyprland's FALLBACK monitor) are excluded by MatchesExternal, so a stuck
|
||||
// docked config cannot keep the daemon in docked mode after undocking.
|
||||
func (d *Daemon) determineState(monitors []backend.MonitorInfo) backend.State {
|
||||
for _, m := range monitors {
|
||||
// Skip phantom or disconnected monitors.
|
||||
@@ -191,7 +194,14 @@ func (d *Daemon) determineState(monitors []backend.MonitorInfo) backend.State {
|
||||
}
|
||||
// A monitor is physically present if it reports non-zero dimensions.
|
||||
if m.Width > 0 && m.Height > 0 {
|
||||
if d.config.MatchesExternal(m.Name, m.Description) {
|
||||
if d.config.MatchesExternal(config.ExternalMonitor{
|
||||
Name: m.Name,
|
||||
Description: m.Description,
|
||||
Serial: m.Serial,
|
||||
Width: m.Width,
|
||||
Height: m.Height,
|
||||
RefreshRate: m.RefreshRate,
|
||||
}) {
|
||||
return backend.StateDocked
|
||||
}
|
||||
}
|
||||
@@ -261,11 +271,19 @@ func (d *Daemon) applyState(ctx context.Context, state backend.State) error {
|
||||
}
|
||||
|
||||
// anyExternalConnected returns true if at least one configured external
|
||||
// monitor is physically present.
|
||||
// monitor is physically present. Synthetic outputs (e.g. Hyprland's FALLBACK)
|
||||
// are excluded via MatchesExternal.
|
||||
func (d *Daemon) anyExternalConnected(monitors []backend.MonitorInfo) bool {
|
||||
for _, m := range monitors {
|
||||
if m.Width > 0 && m.Height > 0 && m.Name != "" {
|
||||
if d.config.MatchesExternal(m.Name, m.Description) {
|
||||
if d.config.MatchesExternal(config.ExternalMonitor{
|
||||
Name: m.Name,
|
||||
Description: m.Description,
|
||||
Serial: m.Serial,
|
||||
Width: m.Width,
|
||||
Height: m.Height,
|
||||
RefreshRate: m.RefreshRate,
|
||||
}) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user