Initial commit: monitor-lets-go — automatic monitor layout daemon
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user