// Package backend defines the abstraction layer for window-manager-specific // monitor management. Each supported compositor (Hyprland, Sway, etc.) // implements the Backend interface, encapsulating how monitors are detected, // configured, and how hotplug events are received. // // This is the only WM-dependent code in the project. To add support for a // new window manager, implement this interface and register the backend in // cmd/monitor-lets-go/main.go. package backend import "context" // MonitorInfo represents a physical display as reported by the compositor. // Fields use JSON tags matching hyprctl -j output; other compositors // populate equivalent fields. type MonitorInfo struct { Name string `json:"name"` Description string `json:"description"` Make string `json:"make"` Model string `json:"model"` Serial string `json:"serial"` Width int `json:"width"` Height int `json:"height"` RefreshRate float64 `json:"refreshRate"` X int `json:"x"` Y int `json:"y"` Scale float64 `json:"scale"` Enabled bool `json:"enabled"` } // MonitorConfig describes the desired state of a single monitor. // Used in configuration and passed to ApplyLayout. type MonitorConfig struct { Name string `yaml:"name"` Enabled bool `yaml:"enabled"` Mode string `yaml:"mode"` // "1920x1080@144", "preferred", or empty Position string `yaml:"position"` // "0x0", "auto", or empty Scale float64 `yaml:"scale"` // 1.0, 1.5, etc. } // State represents the current operational mode of the daemon. type State int const ( StateUnknown State = iota StatePortable // built-in display only StateDocked // external monitors connected ) // String returns a human-readable state name used for config keys and logging. func (s State) String() string { switch s { case StatePortable: return "portable" case StateDocked: return "docked" default: return "unknown" } } // EventType identifies the kind of monitor hotplug event. type EventType int const ( EventMonitorAdded EventType = iota EventMonitorRemoved ) // Event carries a single monitor hotplug notification from the compositor. type Event struct { Type EventType MonitorName string } // Backend is the abstraction over a window manager's monitor management. // Each implementation handles compositor-specific APIs for querying monitors, // applying layouts, and subscribing to hotplug events. type Backend interface { // Name returns a human-readable backend identifier (e.g. "hyprland"). Name() string // GetMonitors returns all monitors known to the compositor, // both active and inactive (physically connected but disabled). GetMonitors(ctx context.Context) ([]MonitorInfo, error) // ApplyLayout applies a list of monitor configurations atomically. // The backend must ensure no intermediate state where zero monitors // are enabled is ever visible. ApplyLayout(ctx context.Context, monitors []MonitorConfig) error // Events returns a channel of hotplug events and a channel of errors. // The caller must read from both channels. When ctx is cancelled, // the backend closes both channels and stops listening. Events(ctx context.Context) (<-chan Event, <-chan error) // Close releases any resources held by the backend. Close() error }