diff options
| author | Nippy <nippy@rp1.hu> | 2026-05-09 13:33:09 +0200 |
|---|---|---|
| committer | Nippy <nippy@rp1.hu> | 2026-05-09 13:33:09 +0200 |
| commit | a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5 (patch) | |
| tree | 1a8769217f84bfe9d6216fafaadaf8cdd876d457 /raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/server.go | |
| parent | 34b9c34f982b2596cfa9e368a2d7f74e9a17477c (diff) | |
| download | RaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.tar.gz RaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.zip | |
raveos update
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/server.go')
| -rw-r--r-- | raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/server.go | 1758 |
1 files changed, 0 insertions, 1758 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/server.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/server.go deleted file mode 100644 index 58acb62..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/server.go +++ /dev/null @@ -1,1758 +0,0 @@ -package server - -import ( - "bufio" - "encoding/json" - "fmt" - "net" - "os" - "path/filepath" - "strconv" - "strings" - "sync" - "sync/atomic" - "syscall" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation" - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/apppicker" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/bluez" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/brightness" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/clipboard" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/cups" - serverDbus "github.com/AvengeMedia/DankMaterialShell/core/internal/server/dbus" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/dwl" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/evdev" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/extworkspace" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/freedesktop" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/location" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/loginctl" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/network" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/thememode" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/trayrecovery" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/wayland" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlcontext" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/wlroutput" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -const APIVersion = 24 - -var CLIVersion = "dev" - -type Capabilities struct { - Capabilities []string `json:"capabilities"` -} - -type ServerInfo struct { - APIVersion int `json:"apiVersion"` - CLIVersion string `json:"cliVersion,omitempty"` - Capabilities []string `json:"capabilities"` -} - -type ServiceEvent struct { - Service string `json:"service"` - Data any `json:"data"` -} - -var networkManager *network.Manager -var loginctlManager *loginctl.Manager -var freedesktopManager *freedesktop.Manager -var waylandManager *wayland.Manager -var bluezManager *bluez.Manager -var appPickerManager *apppicker.Manager -var cupsManager *cups.Manager -var dwlManager *dwl.Manager -var extWorkspaceManager *extworkspace.Manager -var brightnessManager *brightness.Manager -var wlrOutputManager *wlroutput.Manager -var evdevManager *evdev.Manager -var clipboardManager *clipboard.Manager -var dbusManager *serverDbus.Manager -var wlContext *wlcontext.SharedContext -var themeModeManager *thememode.Manager -var trayRecoveryManager *trayrecovery.Manager -var locationManager *location.Manager -var geoClientInstance geolocation.Client - -const dbusClientID = "dms-dbus-client" - -var capabilitySubscribers syncmap.Map[string, chan ServerInfo] -var cupsSubscribers syncmap.Map[string, bool] -var cupsSubscriberCount atomic.Int32 -var extWorkspaceAvailable atomic.Bool -var extWorkspaceInitMutex sync.Mutex - -func getSocketDir() string { - if runtime := os.Getenv("XDG_RUNTIME_DIR"); runtime != "" { - return runtime - } - - if os.Getuid() == 0 { - if _, err := os.Stat("/run"); err == nil { - return "/run/dankdots" - } - return "/var/run/dankdots" - } - - return os.TempDir() -} - -func GetSocketPath() string { - return filepath.Join(getSocketDir(), fmt.Sprintf("danklinux-%d.sock", os.Getpid())) -} - -func FindSocket() (string, error) { - dir := getSocketDir() - entries, err := os.ReadDir(dir) - if err != nil { - return "", err - } - - for _, entry := range entries { - if strings.HasPrefix(entry.Name(), "danklinux-") && strings.HasSuffix(entry.Name(), ".sock") { - return filepath.Join(dir, entry.Name()), nil - } - } - return "", fmt.Errorf("no dms socket found") -} - -func cleanupStaleSockets() { - dir := getSocketDir() - entries, err := os.ReadDir(dir) - if err != nil { - return - } - - for _, entry := range entries { - if !strings.HasPrefix(entry.Name(), "danklinux-") || !strings.HasSuffix(entry.Name(), ".sock") { - continue - } - - pidStr := strings.TrimPrefix(entry.Name(), "danklinux-") - pidStr = strings.TrimSuffix(pidStr, ".sock") - pid, err := strconv.Atoi(pidStr) - if err != nil { - continue - } - - process, err := os.FindProcess(pid) - if err != nil { - socketPath := filepath.Join(dir, entry.Name()) - os.Remove(socketPath) - log.Debugf("Removed stale socket: %s", socketPath) - continue - } - - err = process.Signal(syscall.Signal(0)) - if err != nil { - socketPath := filepath.Join(dir, entry.Name()) - os.Remove(socketPath) - log.Debugf("Removed stale socket: %s", socketPath) - } - } -} - -func InitializeNetworkManager() error { - manager, err := network.NewManager() - if err != nil { - log.Warnf("Failed to initialize network manager: %v", err) - return err - } - - networkManager = manager - - log.Info("Network manager initialized") - return nil -} - -func InitializeLoginctlManager() error { - manager, err := loginctl.NewManager() - if err != nil { - log.Warnf("Failed to initialize loginctl manager: %v", err) - return err - } - - loginctlManager = manager - - log.Info("Loginctl manager initialized") - return nil -} - -func InitializeFreedeskManager() error { - manager, err := freedesktop.NewManager() - if err != nil { - log.Warnf("Failed to initialize freedesktop manager: %v", err) - return err - } - - freedesktopManager = manager - - log.Info("Freedesktop manager initialized") - return nil -} - -func InitializeWaylandManager() error { - log.Info("Attempting to initialize Wayland gamma control...") - - if wlContext == nil { - ctx, err := wlcontext.New() - if err != nil { - log.Errorf("Failed to create shared Wayland context: %v", err) - return err - } - wlContext = ctx - } - - config := wayland.DefaultConfig() - manager, err := wayland.NewManager(wlContext.Display(), config) - if err != nil { - log.Errorf("Failed to initialize wayland manager: %v", err) - return err - } - - waylandManager = manager - - log.Info("Wayland gamma control initialized successfully") - return nil -} - -func InitializeBluezManager() error { - manager, err := bluez.NewManager() - if err != nil { - log.Warnf("Failed to initialize bluez manager: %v", err) - return err - } - - bluezManager = manager - - log.Info("Bluez manager initialized") - return nil -} - -func InitializeAppPickerManager() error { - manager := apppicker.NewManager() - appPickerManager = manager - log.Info("AppPicker manager initialized") - return nil -} - -func InitializeCupsManager() error { - manager, err := cups.NewManager() - if err != nil { - log.Warnf("Failed to initialize cups manager: %v", err) - return err - } - - cupsManager = manager - - log.Info("CUPS manager initialized") - return nil -} - -func InitializeDwlManager() error { - log.Info("Attempting to initialize DWL IPC...") - - if wlContext == nil { - ctx, err := wlcontext.New() - if err != nil { - log.Errorf("Failed to create shared Wayland context: %v", err) - return err - } - wlContext = ctx - } - - manager, err := dwl.NewManager(wlContext.Display()) - if err != nil { - log.Debug("Failed to initialize dwl manager: %v", err) - return err - } - - dwlManager = manager - - log.Info("DWL IPC initialized successfully") - return nil -} - -func InitializeBrightnessManager() error { - manager, err := brightness.NewManager() - if err != nil { - log.Warnf("Failed to initialize brightness manager: %v", err) - return err - } - - brightnessManager = manager - - log.Info("Brightness manager initialized") - return nil -} - -func InitializeExtWorkspaceManager() error { - log.Info("Attempting to initialize ExtWorkspace...") - - if wlContext == nil { - ctx, err := wlcontext.New() - if err != nil { - log.Errorf("Failed to create shared Wayland context: %v", err) - return err - } - wlContext = ctx - } - - manager, err := extworkspace.NewManager(wlContext.Display()) - if err != nil { - log.Debug("Failed to initialize extworkspace manager: %v", err) - return err - } - - extWorkspaceManager = manager - - log.Info("ExtWorkspace initialized successfully") - return nil -} - -func InitializeWlrOutputManager() error { - log.Info("Attempting to initialize WlrOutput management...") - - if wlContext == nil { - ctx, err := wlcontext.New() - if err != nil { - log.Errorf("Failed to create shared Wayland context: %v", err) - return err - } - wlContext = ctx - } - - manager, err := wlroutput.NewManager(wlContext.Display()) - if err != nil { - log.Debug("Failed to initialize wlroutput manager: %v", err) - return err - } - - wlrOutputManager = manager - - log.Info("WlrOutput management initialized successfully") - return nil -} - -func InitializeEvdevManager() error { - manager, err := evdev.InitializeManager() - if err != nil { - log.Warnf("Failed to initialize evdev manager: %v", err) - return err - } - - evdevManager = manager - - log.Info("Evdev manager initialized") - return nil -} - -func InitializeClipboardManager() error { - log.Info("Attempting to initialize clipboard manager...") - - if wlContext == nil { - ctx, err := wlcontext.New() - if err != nil { - log.Errorf("Failed to create shared Wayland context: %v", err) - return err - } - wlContext = ctx - } - - config := clipboard.LoadConfig() - manager, err := clipboard.NewManager(wlContext, config) - if err != nil { - log.Errorf("Failed to initialize clipboard manager: %v", err) - return err - } - - clipboardManager = manager - - log.Info("Clipboard manager initialized successfully") - return nil -} - -func InitializeDbusManager() error { - manager, err := serverDbus.NewManager() - if err != nil { - log.Warnf("Failed to initialize dbus manager: %v", err) - return err - } - - dbusManager = manager - - log.Info("DBus manager initialized") - return nil -} - -func InitializeThemeModeManager() error { - manager := thememode.NewManager() - themeModeManager = manager - - log.Info("Theme mode automation manager initialized") - return nil -} - -func InitializeTrayRecoveryManager() error { - manager, err := trayrecovery.NewManager() - if err != nil { - return err - } - - trayRecoveryManager = manager - - log.Info("TrayRecovery manager initialized") - return nil -} - -func InitializeLocationManager(geoClient geolocation.Client) error { - manager, err := location.NewManager(geoClient) - if err != nil { - log.Warnf("Failed to initialize location manager: %v", err) - return err - } - - locationManager = manager - - log.Info("Location manager initialized") - return nil -} - -func handleConnection(conn net.Conn) { - defer conn.Close() - - caps := getCapabilities() - capsData, _ := json.Marshal(caps) - conn.Write(capsData) - conn.Write([]byte("\n")) - scanner := bufio.NewScanner(conn) - for scanner.Scan() { - line := scanner.Bytes() - - var req models.Request - if err := json.Unmarshal(line, &req); err != nil { - log.Warnf("handleConnection: Failed to unmarshal JSON: %v, line: %s", err, string(line)) - models.RespondError(conn, 0, "invalid json") - continue - } - - go RouteRequest(conn, req) - } -} - -func getCapabilities() Capabilities { - caps := []string{"plugins"} - - if networkManager != nil { - caps = append(caps, "network") - } - - if loginctlManager != nil { - caps = append(caps, "loginctl") - } - - if freedesktopManager != nil { - caps = append(caps, "freedesktop") - } - - if waylandManager != nil { - caps = append(caps, "gamma") - } - - if bluezManager != nil { - caps = append(caps, "bluetooth") - } - - if appPickerManager != nil { - caps = append(caps, "browser") - } - - if cupsManager != nil { - caps = append(caps, "cups") - } - - if dwlManager != nil { - caps = append(caps, "dwl") - } - - if extWorkspaceAvailable.Load() { - caps = append(caps, "extworkspace") - } - - if brightnessManager != nil { - caps = append(caps, "brightness") - } - - if wlrOutputManager != nil { - caps = append(caps, "wlroutput") - } - - if evdevManager != nil { - caps = append(caps, "evdev") - } - - if clipboardManager != nil { - caps = append(caps, "clipboard") - } - - if themeModeManager != nil { - caps = append(caps, "theme.auto") - } - - if dbusManager != nil { - caps = append(caps, "dbus") - } - - return Capabilities{Capabilities: caps} -} - -func getServerInfo() ServerInfo { - caps := []string{"plugins"} - - if networkManager != nil { - caps = append(caps, "network") - } - - if loginctlManager != nil { - caps = append(caps, "loginctl") - } - - if freedesktopManager != nil { - caps = append(caps, "freedesktop") - } - - if waylandManager != nil { - caps = append(caps, "gamma") - } - - if bluezManager != nil { - caps = append(caps, "bluetooth") - } - - if appPickerManager != nil { - caps = append(caps, "browser") - } - - if cupsManager != nil { - caps = append(caps, "cups") - } - - if dwlManager != nil { - caps = append(caps, "dwl") - } - - if extWorkspaceAvailable.Load() { - caps = append(caps, "extworkspace") - } - - if brightnessManager != nil { - caps = append(caps, "brightness") - } - - if wlrOutputManager != nil { - caps = append(caps, "wlroutput") - } - - if evdevManager != nil { - caps = append(caps, "evdev") - } - - if clipboardManager != nil { - caps = append(caps, "clipboard") - } - - if themeModeManager != nil { - caps = append(caps, "theme.auto") - } - - if locationManager != nil { - caps = append(caps, "location") - } - - if dbusManager != nil { - caps = append(caps, "dbus") - } - - return ServerInfo{ - APIVersion: APIVersion, - CLIVersion: CLIVersion, - Capabilities: caps, - } -} - -func notifyCapabilityChange() { - info := getServerInfo() - capabilitySubscribers.Range(func(key string, ch chan ServerInfo) bool { - select { - case ch <- info: - default: - } - return true - }) -} - -func handleSubscribe(conn net.Conn, req models.Request) { - clientID := fmt.Sprintf("meta-client-%p", conn) - - var services []string - if servicesParam, ok := models.Get[[]any](req, "services"); ok { - for _, s := range servicesParam { - if str, ok := s.(string); ok { - services = append(services, str) - } - } - } - - if len(services) == 0 { - services = []string{"all"} - } - - subscribeAll := false - for _, s := range services { - if s == "all" { - subscribeAll = true - break - } - } - - var wg sync.WaitGroup - eventChan := make(chan ServiceEvent, 256) - stopChan := make(chan struct{}) - - capChan := make(chan ServerInfo, 64) - capabilitySubscribers.Store(clientID+"-capabilities", capChan) - - wg.Add(1) - go func() { - defer wg.Done() - defer capabilitySubscribers.Delete(clientID + "-capabilities") - - for { - select { - case info, ok := <-capChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "server", Data: info}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - - shouldSubscribe := func(service string) bool { - if subscribeAll { - return true - } - for _, s := range services { - if s == service { - return true - } - } - return false - } - - if shouldSubscribe("network") && networkManager != nil { - wg.Add(1) - netChan := networkManager.Subscribe(clientID + "-network") - go func() { - defer wg.Done() - defer networkManager.Unsubscribe(clientID + "-network") - - initialState := networkManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "network", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-netChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "network", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("network.credentials") && networkManager != nil { - wg.Add(1) - credChan := networkManager.SubscribeCredentials(clientID + "-credentials") - go func() { - defer wg.Done() - defer networkManager.UnsubscribeCredentials(clientID + "-credentials") - - for { - select { - case prompt, ok := <-credChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "network.credentials", Data: prompt}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("loginctl") && loginctlManager != nil { - wg.Add(1) - loginChan := loginctlManager.Subscribe(clientID + "-loginctl") - go func() { - defer wg.Done() - defer loginctlManager.Unsubscribe(clientID + "-loginctl") - - initialState := loginctlManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "loginctl", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-loginChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "loginctl", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("freedesktop") && freedesktopManager != nil { - wg.Add(1) - freedesktopChan := freedesktopManager.Subscribe(clientID + "-freedesktop") - go func() { - defer wg.Done() - defer freedesktopManager.Unsubscribe(clientID + "-freedesktop") - - initialState := freedesktopManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "freedesktop", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-freedesktopChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "freedesktop", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("freedesktop.screensaver") && freedesktopManager != nil { - wg.Add(1) - screensaverChan := freedesktopManager.SubscribeScreensaver(clientID + "-screensaver") - go func() { - defer wg.Done() - defer freedesktopManager.UnsubscribeScreensaver(clientID + "-screensaver") - - initialState := freedesktopManager.GetScreensaverState() - select { - case eventChan <- ServiceEvent{Service: "freedesktop.screensaver", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-screensaverChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "freedesktop.screensaver", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("gamma") && waylandManager != nil { - wg.Add(1) - waylandChan := waylandManager.Subscribe(clientID + "-gamma") - go func() { - defer wg.Done() - defer waylandManager.Unsubscribe(clientID + "-gamma") - - initialState := waylandManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "gamma", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-waylandChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "gamma", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("theme.auto") && themeModeManager != nil { - wg.Add(1) - themeAutoChan := themeModeManager.Subscribe(clientID + "-theme-auto") - go func() { - defer wg.Done() - defer themeModeManager.Unsubscribe(clientID + "-theme-auto") - - initialState := themeModeManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "theme.auto", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-themeAutoChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "theme.auto", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("bluetooth") && bluezManager != nil { - wg.Add(1) - bluezChan := bluezManager.Subscribe(clientID + "-bluetooth") - go func() { - defer wg.Done() - defer bluezManager.Unsubscribe(clientID + "-bluetooth") - - initialState := bluezManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "bluetooth", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-bluezChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "bluetooth", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("bluetooth.pairing") && bluezManager != nil { - wg.Add(1) - pairingChan := bluezManager.SubscribePairing(clientID + "-pairing") - go func() { - defer wg.Done() - defer bluezManager.UnsubscribePairing(clientID + "-pairing") - - for { - select { - case prompt, ok := <-pairingChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "bluetooth.pairing", Data: prompt}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("browser") && appPickerManager != nil { - wg.Add(1) - appPickerChan := appPickerManager.Subscribe(clientID + "-browser") - go func() { - defer wg.Done() - defer appPickerManager.Unsubscribe(clientID + "-browser") - - for { - select { - case event, ok := <-appPickerChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "browser.open_requested", Data: event}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("cups") { - cupsSubscribers.Store(clientID+"-cups", true) - count := cupsSubscriberCount.Add(1) - - if count == 1 { - if err := InitializeCupsManager(); err != nil { - log.Warnf("Failed to initialize CUPS manager for subscription: %v", err) - } else { - notifyCapabilityChange() - } - } - - if cupsManager != nil { - wg.Add(1) - cupsChan := cupsManager.Subscribe(clientID + "-cups") - go func() { - defer wg.Done() - defer func() { - cupsManager.Unsubscribe(clientID + "-cups") - cupsSubscribers.Delete(clientID + "-cups") - count := cupsSubscriberCount.Add(-1) - - if count == 0 { - log.Info("Last CUPS subscriber disconnected, shutting down CUPS manager") - if cupsManager != nil { - cupsManager.Close() - cupsManager = nil - notifyCapabilityChange() - } - } - }() - - initialState := cupsManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "cups", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-cupsChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "cups", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - } - - if shouldSubscribe("dwl") && dwlManager != nil { - wg.Add(1) - dwlChan := dwlManager.Subscribe(clientID + "-dwl") - go func() { - defer wg.Done() - defer dwlManager.Unsubscribe(clientID + "-dwl") - - initialState := dwlManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "dwl", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-dwlChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "dwl", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("extworkspace") { - if extWorkspaceManager == nil && extWorkspaceAvailable.Load() { - extWorkspaceInitMutex.Lock() - if extWorkspaceManager == nil { - if err := InitializeExtWorkspaceManager(); err != nil { - log.Warnf("Failed to initialize ExtWorkspace manager for subscription: %v", err) - } - } - extWorkspaceInitMutex.Unlock() - } - - if extWorkspaceManager != nil { - wg.Add(1) - extWorkspaceChan := extWorkspaceManager.Subscribe(clientID + "-extworkspace") - go func() { - defer wg.Done() - defer extWorkspaceManager.Unsubscribe(clientID + "-extworkspace") - - initialState := extWorkspaceManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "extworkspace", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-extWorkspaceChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "extworkspace", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - } - - if shouldSubscribe("brightness") && brightnessManager != nil { - wg.Add(2) - brightnessStateChan := brightnessManager.Subscribe(clientID + "-brightness-state") - brightnessUpdateChan := brightnessManager.SubscribeUpdates(clientID + "-brightness-updates") - - go func() { - defer wg.Done() - defer brightnessManager.Unsubscribe(clientID + "-brightness-state") - - initialState := brightnessManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "brightness", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-brightnessStateChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "brightness", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - - go func() { - defer wg.Done() - defer brightnessManager.UnsubscribeUpdates(clientID + "-brightness-updates") - - for { - select { - case update, ok := <-brightnessUpdateChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "brightness.update", Data: update}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("wlroutput") && wlrOutputManager != nil { - wg.Add(1) - wlrOutputChan := wlrOutputManager.Subscribe(clientID + "-wlroutput") - go func() { - defer wg.Done() - defer wlrOutputManager.Unsubscribe(clientID + "-wlroutput") - - initialState := wlrOutputManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "wlroutput", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-wlrOutputChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "wlroutput", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("evdev") && evdevManager != nil { - wg.Add(1) - evdevChan := evdevManager.Subscribe(clientID + "-evdev") - go func() { - defer wg.Done() - defer evdevManager.Unsubscribe(clientID + "-evdev") - - initialState := evdevManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "evdev", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-evdevChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "evdev", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("clipboard") && clipboardManager != nil { - wg.Add(1) - clipboardChan := clipboardManager.Subscribe(clientID + "-clipboard") - go func() { - defer wg.Done() - defer clipboardManager.Unsubscribe(clientID + "-clipboard") - - initialState := clipboardManager.GetState() - select { - case eventChan <- ServiceEvent{Service: "clipboard", Data: initialState}: - case <-stopChan: - return - } - - for { - select { - case state, ok := <-clipboardChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "clipboard", Data: state}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - if shouldSubscribe("dbus") && dbusManager != nil { - wg.Add(1) - dbusChan := dbusManager.SubscribeSignals(dbusClientID) - go func() { - defer wg.Done() - defer dbusManager.UnsubscribeSignals(dbusClientID) - - for { - select { - case event, ok := <-dbusChan: - if !ok { - return - } - select { - case eventChan <- ServiceEvent{Service: "dbus", Data: event}: - case <-stopChan: - return - } - case <-stopChan: - return - } - } - }() - } - - go func() { - wg.Wait() - close(eventChan) - }() - - info := getServerInfo() - if err := json.NewEncoder(conn).Encode(models.Response[ServiceEvent]{ - ID: req.ID, - Result: &ServiceEvent{Service: "server", Data: info}, - }); err != nil { - close(stopChan) - return - } - - for event := range eventChan { - if err := json.NewEncoder(conn).Encode(models.Response[ServiceEvent]{ - ID: req.ID, - Result: &event, - }); err != nil { - close(stopChan) - return - } - } -} - -func cleanupManagers() { - if networkManager != nil { - networkManager.Close() - } - if loginctlManager != nil { - loginctlManager.Close() - } - if freedesktopManager != nil { - freedesktopManager.Close() - } - if waylandManager != nil { - waylandManager.Close() - } - if bluezManager != nil { - bluezManager.Close() - } - if appPickerManager != nil { - appPickerManager.Close() - } - if cupsManager != nil { - cupsManager.Close() - } - if dwlManager != nil { - dwlManager.Close() - } - if extWorkspaceManager != nil { - extWorkspaceManager.Close() - } - if brightnessManager != nil { - brightnessManager.Close() - } - if wlrOutputManager != nil { - wlrOutputManager.Close() - } - if evdevManager != nil { - evdevManager.Close() - } - if clipboardManager != nil { - clipboardManager.Close() - } - if dbusManager != nil { - dbusManager.Close() - } - if themeModeManager != nil { - themeModeManager.Close() - } - if trayRecoveryManager != nil { - trayRecoveryManager.Close() - } - if wlContext != nil { - wlContext.Close() - } - if locationManager != nil { - locationManager.Close() - } - if geoClientInstance != nil { - geoClientInstance.Close() - } -} - -func Start(printDocs bool) error { - cleanupStaleSockets() - - socketPath := GetSocketPath() - os.Remove(socketPath) - - listener, err := net.Listen("unix", socketPath) - if err != nil { - return err - } - defer listener.Close() - defer cleanupManagers() - - log.Infof("DMS API Server listening on: %s", socketPath) - log.Infof("API Version: %d", APIVersion) - log.Info("Protocol: JSON over Unix socket") - log.Info("Request format: {\"id\": <any>, \"method\": \"...\", \"params\": {...}}") - log.Info("Response format: {\"id\": <any>, \"result\": {...}} or {\"id\": <any>, \"error\": \"...\"}") - log.Info("") - if printDocs { - log.Info("Available methods:") - log.Info(" ping - Test connection") - log.Info(" getServerInfo - Get server info (API version and capabilities)") - log.Info(" subscribe - Subscribe to multiple services (params: services [default: all])") - log.Info("Plugins:") - log.Info(" plugins.list - List all plugins") - log.Info(" plugins.listInstalled - List installed plugins") - log.Info(" plugins.install - Install plugin (params: name)") - log.Info(" plugins.uninstall - Uninstall plugin (params: name)") - log.Info(" plugins.update - Update plugin (params: name)") - log.Info(" plugins.search - Search plugins (params: query, category?, compositor?, capability?)") - log.Info("Network:") - log.Info(" network.getState - Get current network state") - log.Info(" network.wifi.scan - Scan for WiFi networks (params: device?)") - log.Info(" network.wifi.networks - Get WiFi network list") - log.Info(" network.wifi.connect - Connect to WiFi (params: ssid, password?, username?, device?, eapMethod?, phase2Auth?, caCertPath?, clientCertPath?, privateKeyPath?, useSystemCACerts?)") - log.Info(" network.wifi.disconnect - Disconnect WiFi (params: device?)") - log.Info(" network.wifi.forget - Forget network (params: ssid)") - log.Info(" network.wifi.toggle - Toggle WiFi radio") - log.Info(" network.wifi.enable - Enable WiFi") - log.Info(" network.wifi.disable - Disable WiFi") - log.Info(" network.wifi.setAutoconnect - Set network autoconnect (params: ssid, autoconnect)") - log.Info(" network.ethernet.connect - Connect Ethernet") - log.Info(" network.ethernet.connect.config - Connect Ethernet to a specific configuration") - log.Info(" network.ethernet.disconnect - Disconnect Ethernet") - log.Info(" network.vpn.profiles - List VPN profiles") - log.Info(" network.vpn.active - List active VPN connections") - log.Info(" network.vpn.connect - Connect VPN (params: uuidOrName|name|uuid, singleActive?)") - log.Info(" network.vpn.disconnect - Disconnect VPN (params: uuidOrName|name|uuid)") - log.Info(" network.vpn.disconnectAll - Disconnect all VPNs") - log.Info(" network.vpn.clearCredentials - Clear saved VPN credentials (params: uuidOrName|name|uuid)") - log.Info(" network.vpn.plugins - List available VPN plugins") - log.Info(" network.vpn.import - Import VPN from file (params: file|path, name?)") - log.Info(" network.vpn.getConfig - Get VPN configuration (params: uuid|name|uuidOrName)") - log.Info(" network.vpn.updateConfig - Update VPN configuration (params: uuid, name?, autoconnect?, data?)") - log.Info(" network.vpn.delete - Delete VPN connection (params: uuid|name|uuidOrName)") - log.Info(" network.preference.set - Set preference (params: preference [auto|wifi|ethernet])") - log.Info(" network.info - Get network info (params: ssid)") - log.Info(" network.credentials.submit - Submit credentials for prompt (params: token, secrets, save?)") - log.Info(" network.credentials.cancel - Cancel credential prompt (params: token)") - log.Info(" network.subscribe - Subscribe to network state changes (streaming)") - log.Info("Loginctl:") - log.Info(" loginctl.getState - Get current session state") - log.Info(" loginctl.lock - Lock session") - log.Info(" loginctl.unlock - Unlock session") - log.Info(" loginctl.activate - Activate session") - log.Info(" loginctl.setIdleHint - Set idle hint (params: idle)") - log.Info(" loginctl.setLockBeforeSuspend - Set lock before suspend (params: enabled)") - log.Info(" loginctl.setSleepInhibitorEnabled - Enable/disable sleep inhibitor (params: enabled)") - log.Info(" loginctl.lockerReady - Signal locker UI is ready (releases sleep inhibitor)") - log.Info(" loginctl.terminate - Terminate session") - log.Info(" loginctl.subscribe - Subscribe to session state changes (streaming)") - log.Info("Freedesktop:") - log.Info(" freedesktop.getState - Get accounts & settings state") - log.Info(" freedesktop.accounts.setIconFile - Set profile icon (params: path)") - log.Info(" freedesktop.accounts.setRealName - Set real name (params: name)") - log.Info(" freedesktop.accounts.setEmail - Set email (params: email)") - log.Info(" freedesktop.accounts.setLanguage - Set language (params: language)") - log.Info(" freedesktop.accounts.setLocation - Set location (params: location)") - log.Info(" freedesktop.accounts.getUserIconFile - Get user icon (params: username)") - log.Info(" freedesktop.settings.getColorScheme - Get color scheme") - log.Info(" freedesktop.settings.setIconTheme - Set icon theme (params: iconTheme)") - log.Info("Wayland:") - log.Info(" wayland.gamma.getState - Get current gamma control state") - log.Info(" wayland.gamma.setTemperature - Set temperature range (params: low, high)") - log.Info(" wayland.gamma.setLocation - Set location (params: latitude, longitude)") - log.Info(" wayland.gamma.setManualTimes - Set manual times (params: sunrise, sunset)") - log.Info(" wayland.gamma.setGamma - Set gamma value (params: gamma)") - log.Info(" wayland.gamma.setEnabled - Enable/disable gamma control (params: enabled)") - log.Info(" wayland.gamma.subscribe - Subscribe to gamma state changes (streaming)") - log.Info("Theme automation:") - log.Info(" theme.auto.getState - Get current theme automation state") - log.Info(" theme.auto.setEnabled - Enable/disable theme automation (params: enabled)") - log.Info(" theme.auto.setMode - Set automation mode (params: mode [time|location])") - log.Info(" theme.auto.setSchedule - Set time schedule (params: startHour, startMinute, endHour, endMinute)") - log.Info(" theme.auto.setLocation - Set location (params: latitude, longitude)") - log.Info(" theme.auto.setUseIPLocation - Use IP location (params: use)") - log.Info(" theme.auto.trigger - Trigger immediate re-evaluation") - log.Info(" theme.auto.subscribe - Subscribe to theme automation state changes (streaming)") - log.Info("Bluetooth:") - log.Info(" bluetooth.getState - Get current bluetooth state") - log.Info(" bluetooth.startDiscovery - Start device discovery") - log.Info(" bluetooth.stopDiscovery - Stop device discovery") - log.Info(" bluetooth.setPowered - Set adapter power state (params: powered)") - log.Info(" bluetooth.pair - Pair with device (params: device)") - log.Info(" bluetooth.connect - Connect to device (params: device)") - log.Info(" bluetooth.disconnect - Disconnect from device (params: device)") - log.Info(" bluetooth.remove - Remove/unpair device (params: device)") - log.Info(" bluetooth.trust - Trust device (params: device)") - log.Info(" bluetooth.untrust - Untrust device (params: device)") - log.Info(" bluetooth.pairing.submit - Submit pairing response (params: token, secrets?, accept?)") - log.Info(" bluetooth.pairing.cancel - Cancel pairing prompt (params: token)") - log.Info(" bluetooth.subscribe - Subscribe to bluetooth state changes (streaming)") - log.Info("CUPS:") - log.Info(" cups.getPrinters - Get printers list") - log.Info(" cups.getJobs - Get non-completed jobs list (params: printerName)") - log.Info(" cups.pausePrinter - Pause printer (params: printerName)") - log.Info(" cups.resumePrinter - Resume printer (params: printerName)") - log.Info(" cups.cancelJob - Cancel job (params: printerName, jobID)") - log.Info(" cups.purgeJobs - Cancel all jobs (params: printerName)") - log.Info("DWL:") - log.Info(" dwl.getState - Get current dwl state (tags, windows, layouts, keyboard)") - log.Info(" dwl.setTags - Set active tags (params: output, tagmask, toggleTagset)") - log.Info(" dwl.setClientTags - Set focused client tags (params: output, andTags, xorTags)") - log.Info(" dwl.setLayout - Set layout (params: output, index)") - log.Info(" dwl.subscribe - Subscribe to dwl state changes (streaming)") - log.Info(" Output state includes:") - log.Info(" - tags : Tag states (active, clients, focused)") - log.Info(" - layoutSymbol : Current layout name") - log.Info(" - title : Focused window title") - log.Info(" - appId : Focused window app ID") - log.Info(" - kbLayout : Current keyboard layout") - log.Info(" - keymode : Current keybind mode") - log.Info("ExtWorkspace:") - log.Info(" extworkspace.getState - Get current workspace state (groups, workspaces)") - log.Info(" extworkspace.activateWorkspace - Activate workspace (params: groupID, workspaceID)") - log.Info(" extworkspace.deactivateWorkspace - Deactivate workspace (params: groupID, workspaceID)") - log.Info(" extworkspace.removeWorkspace - Remove workspace (params: groupID, workspaceID)") - log.Info(" extworkspace.createWorkspace - Create workspace (params: groupID, name)") - log.Info(" extworkspace.subscribe - Subscribe to workspace state changes (streaming)") - log.Info("Brightness:") - log.Info(" brightness.getState - Get current brightness state for all devices") - log.Info(" brightness.setBrightness - Set device brightness (params: device, percent)") - log.Info(" brightness.increment - Increment device brightness (params: device, step?)") - log.Info(" brightness.decrement - Decrement device brightness (params: device, step?)") - log.Info(" brightness.rescan - Rescan for brightness devices (e.g., after plugging in monitor)") - log.Info(" brightness.subscribe - Subscribe to brightness state changes (streaming)") - log.Info(" Subscription events:") - log.Info(" - brightness : Full device list (on rescan, DDC discovery, device changes)") - log.Info(" - brightness.update: Single device update (on brightness change for efficiency)") - log.Info("WlrOutput:") - log.Info(" wlroutput.getState - Get current output configuration state") - log.Info(" wlroutput.applyConfiguration - Apply output configuration (params: heads)") - log.Info(" wlroutput.testConfiguration - Test output configuration without applying (params: heads)") - log.Info(" wlroutput.subscribe - Subscribe to output state changes (streaming)") - log.Info(" Head configuration params:") - log.Info(" - name : Output name (required)") - log.Info(" - enabled : Enable/disable output (required)") - log.Info(" - modeId : Mode ID from available modes (optional)") - log.Info(" - customMode : Custom mode {width, height, refresh} (optional)") - log.Info(" - position : Position {x, y} (optional)") - log.Info(" - transform : Transform value (optional)") - log.Info(" - scale : Scale value (optional)") - log.Info(" - adaptiveSync : Adaptive sync state (optional)") - log.Info("Evdev:") - log.Info(" evdev.getState - Get current evdev state (caps lock)") - log.Info(" evdev.subscribe - Subscribe to evdev state changes (streaming)") - log.Info("Clipboard:") - log.Info(" clipboard.getState - Get clipboard state (enabled, history, current)") - log.Info(" clipboard.getHistory - Get clipboard history with previews") - log.Info(" clipboard.getEntry - Get full entry by ID (params: id)") - log.Info(" clipboard.deleteEntry - Delete entry by ID (params: id)") - log.Info(" clipboard.clearHistory - Clear all clipboard history") - log.Info(" clipboard.copy - Copy text to clipboard (params: text)") - log.Info(" clipboard.paste - Get current clipboard text") - log.Info(" clipboard.search - Search history (params: query?, mimeType?, isImage?, limit?, offset?, before?, after?)") - log.Info(" clipboard.getConfig - Get clipboard configuration") - log.Info(" clipboard.setConfig - Set configuration (params: maxHistory?, maxEntrySize?, autoClearDays?, clearAtStartup?)") - log.Info(" clipboard.subscribe - Subscribe to clipboard state changes (streaming)") - log.Info("Location:") - log.Info(" location.getState - Get current location state") - log.Info(" location.subscribe - Subscribe to location changes (streaming)") - log.Info("") - } - log.Info("Initializing managers...") - log.Info("") - - go func() { - ticker := time.NewTicker(30 * time.Second) - defer ticker.Stop() - - if err := InitializeNetworkManager(); err != nil { - log.Warnf("Network manager unavailable: %v", err) - } else { - notifyCapabilityChange() - return - } - - for range ticker.C { - if networkManager != nil { - return - } - if err := InitializeNetworkManager(); err == nil { - log.Info("Network manager initialized") - notifyCapabilityChange() - return - } - } - }() - - loginctlReady := make(chan struct{}) - freedesktopReady := make(chan struct{}) - - go func() { - defer close(loginctlReady) - if err := InitializeLoginctlManager(); err != nil { - log.Warnf("Loginctl manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - go func() { - defer close(freedesktopReady) - if err := InitializeFreedeskManager(); err != nil { - log.Warnf("Freedesktop manager unavailable: %v", err) - } else if freedesktopManager != nil { - freedesktopManager.NotifySubscribers() - notifyCapabilityChange() - } - }() - - // Bridge loginctl lock state to the freedesktop/gnome screensaver - // ActiveChanged signal so apps like Bitwarden can detect screen lock. - go func() { - <-loginctlReady - <-freedesktopReady - - if loginctlManager == nil || freedesktopManager == nil { - return - } - - ch := loginctlManager.Subscribe("dms-lock-bridge") - defer loginctlManager.Unsubscribe("dms-lock-bridge") - - initial := loginctlManager.GetState() - lastLocked := initial.Locked - freedesktopManager.SetScreenLockActive(lastLocked) - - for state := range ch { - if state.Locked != lastLocked { - lastLocked = state.Locked - freedesktopManager.SetScreenLockActive(lastLocked) - } - } - }() - - if err := InitializeWaylandManager(); err != nil { - log.Warnf("Wayland manager unavailable: %v", err) - } - - if err := InitializeThemeModeManager(); err != nil { - log.Warnf("Theme mode manager unavailable: %v", err) - } else { - notifyCapabilityChange() - go func() { - <-loginctlReady - if loginctlManager == nil { - return - } - themeModeManager.WatchLoginctl(loginctlManager) - }() - } - - go func() { - <-loginctlReady - if loginctlManager == nil { - return - } - if err := InitializeTrayRecoveryManager(); err != nil { - log.Warnf("TrayRecovery manager unavailable: %v", err) - } else { - trayRecoveryManager.WatchLoginctl(loginctlManager) - } - }() - - go func() { - geoClient := geolocation.NewClient() - geoClientInstance = geoClient - - if waylandManager != nil { - waylandManager.SetGeoClient(geoClient) - } - if themeModeManager != nil { - themeModeManager.SetGeoClient(geoClient) - } - - if err := InitializeLocationManager(geoClient); err != nil { - log.Warnf("Location manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - go func() { - if err := InitializeBluezManager(); err != nil { - log.Warnf("Bluez manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - if err := InitializeAppPickerManager(); err != nil { - log.Debugf("AppPicker manager unavailable: %v", err) - } - - if err := InitializeDwlManager(); err != nil { - log.Debugf("DWL manager unavailable: %v", err) - } - - if extworkspace.CheckCapability() { - extWorkspaceAvailable.Store(true) - log.Info("ExtWorkspace capability detected and will be available on subscription") - } else { - log.Debug("ExtWorkspace capability not available") - extWorkspaceAvailable.Store(false) - } - - if err := InitializeWlrOutputManager(); err != nil { - log.Debugf("WlrOutput manager unavailable: %v", err) - } - - fatalErrChan := make(chan error, 1) - if wlrOutputManager != nil { - go func() { - err := <-wlrOutputManager.FatalError() - fatalErrChan <- fmt.Errorf("WlrOutput fatal error: %w", err) - }() - } - if wlContext != nil { - go func() { - err := <-wlContext.FatalError() - fatalErrChan <- fmt.Errorf("wayland context fatal error: %w", err) - }() - } - - go func() { - if err := InitializeBrightnessManager(); err != nil { - log.Warnf("Brightness manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - go func() { - if err := InitializeEvdevManager(); err != nil { - log.Debugf("Evdev manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - go func() { - if err := InitializeClipboardManager(); err != nil { - log.Warnf("Clipboard manager unavailable: %v", err) - } - if wlContext != nil { - wlContext.Start() - log.Info("Wayland event dispatcher started") - } - }() - - go func() { - if err := InitializeDbusManager(); err != nil { - log.Warnf("DBus manager unavailable: %v", err) - } else { - notifyCapabilityChange() - } - }() - - log.Info("") - log.Infof("Ready! Capabilities: %v", getCapabilities().Capabilities) - - listenerErrChan := make(chan error, 1) - - go func() { - for { - conn, err := listener.Accept() - if err != nil { - listenerErrChan <- err - return - } - go handleConnection(conn) - } - }() - - select { - case err := <-listenerErrChan: - return err - case err := <-fatalErrChan: - return err - } -} |