diff options
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode')
3 files changed, 0 insertions, 639 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/handlers.go deleted file mode 100644 index 6891875..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/handlers.go +++ /dev/null @@ -1,154 +0,0 @@ -package thememode - -import ( - "encoding/json" - "fmt" - "net" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/params" -) - -func HandleRequest(conn net.Conn, req models.Request, manager *Manager) { - if manager == nil { - models.RespondError(conn, req.ID, "theme mode manager not initialized") - return - } - - switch req.Method { - case "theme.auto.getState": - handleGetState(conn, req, manager) - case "theme.auto.setEnabled": - handleSetEnabled(conn, req, manager) - case "theme.auto.setMode": - handleSetMode(conn, req, manager) - case "theme.auto.setSchedule": - handleSetSchedule(conn, req, manager) - case "theme.auto.setLocation": - handleSetLocation(conn, req, manager) - case "theme.auto.setUseIPLocation": - handleSetUseIPLocation(conn, req, manager) - case "theme.auto.trigger": - handleTrigger(conn, req, manager) - case "theme.auto.subscribe": - handleSubscribe(conn, req, manager) - default: - models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) - } -} - -func handleGetState(conn net.Conn, req models.Request, manager *Manager) { - models.Respond(conn, req.ID, manager.GetState()) -} - -func handleSetEnabled(conn net.Conn, req models.Request, manager *Manager) { - enabled, err := params.Bool(req.Params, "enabled") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - manager.SetEnabled(enabled) - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto enabled set"}) -} - -func handleSetMode(conn net.Conn, req models.Request, manager *Manager) { - mode, err := params.String(req.Params, "mode") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if mode != "time" && mode != "location" { - models.RespondError(conn, req.ID, "invalid mode") - return - } - - manager.SetMode(mode) - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto mode set"}) -} - -func handleSetSchedule(conn net.Conn, req models.Request, manager *Manager) { - startHour, err := params.Int(req.Params, "startHour") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - startMinute, err := params.Int(req.Params, "startMinute") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - endHour, err := params.Int(req.Params, "endHour") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - endMinute, err := params.Int(req.Params, "endMinute") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.ValidateSchedule(startHour, startMinute, endHour, endMinute); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - manager.SetSchedule(startHour, startMinute, endHour, endMinute) - models.Respond(conn, req.ID, manager.GetState()) -} - -func handleSetLocation(conn net.Conn, req models.Request, manager *Manager) { - lat, err := params.Float(req.Params, "latitude") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - lon, err := params.Float(req.Params, "longitude") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - manager.SetLocation(lat, lon) - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto location set"}) -} - -func handleSetUseIPLocation(conn net.Conn, req models.Request, manager *Manager) { - use, err := params.Bool(req.Params, "use") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - manager.SetUseIPLocation(use) - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto IP location set"}) -} - -func handleTrigger(conn net.Conn, req models.Request, manager *Manager) { - manager.TriggerUpdate() - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "theme auto update triggered"}) -} - -func handleSubscribe(conn net.Conn, req models.Request, manager *Manager) { - clientID := fmt.Sprintf("client-%p", conn) - stateChan := manager.Subscribe(clientID) - defer manager.Unsubscribe(clientID) - - initialState := manager.GetState() - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - ID: req.ID, - Result: &initialState, - }); err != nil { - return - } - - for state := range stateChan { - if err := json.NewEncoder(conn).Encode(models.Response[State]{ - Result: &state, - }); err != nil { - return - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/manager.go deleted file mode 100644 index f7644ad..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/manager.go +++ /dev/null @@ -1,462 +0,0 @@ -package thememode - -import ( - "errors" - "sync" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/loginctl" - "github.com/AvengeMedia/DankMaterialShell/core/internal/server/wayland" - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" -) - -const ( - defaultStartHour = 18 - defaultStartMinute = 0 - defaultEndHour = 6 - defaultEndMinute = 0 - defaultElevationTwilight = -6.0 - defaultElevationDaylight = 3.0 -) - -type Manager struct { - config Config - configMutex sync.RWMutex - - state *State - stateMutex sync.RWMutex - - subscribers syncmap.Map[string, chan State] - - locationMutex sync.RWMutex - cachedIPLat *float64 - cachedIPLon *float64 - - geoClient geolocation.Client - - stopChan chan struct{} - updateTrigger chan struct{} - wg sync.WaitGroup -} - -func NewManager() *Manager { - m := &Manager{ - config: Config{ - Enabled: false, - Mode: "time", - StartHour: defaultStartHour, - StartMinute: defaultStartMinute, - EndHour: defaultEndHour, - EndMinute: defaultEndMinute, - ElevationTwilight: defaultElevationTwilight, - ElevationDaylight: defaultElevationDaylight, - }, - stopChan: make(chan struct{}), - updateTrigger: make(chan struct{}, 1), - } - - m.updateState(time.Now()) - - m.wg.Add(1) - go m.schedulerLoop() - - return m -} - -func (m *Manager) GetState() State { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - if m.state == nil { - return State{Config: m.getConfig()} - } - stateCopy := *m.state - return stateCopy -} - -func (m *Manager) Subscribe(id string) chan State { - ch := make(chan State, 64) - m.subscribers.Store(id, ch) - return ch -} - -func (m *Manager) Unsubscribe(id string) { - if val, ok := m.subscribers.LoadAndDelete(id); ok { - close(val) - } -} - -func (m *Manager) SetEnabled(enabled bool) { - m.configMutex.Lock() - if m.config.Enabled == enabled { - m.configMutex.Unlock() - return - } - m.config.Enabled = enabled - m.configMutex.Unlock() - m.TriggerUpdate() -} - -func (m *Manager) SetMode(mode string) { - m.configMutex.Lock() - if m.config.Mode == mode { - m.configMutex.Unlock() - return - } - m.config.Mode = mode - m.configMutex.Unlock() - m.TriggerUpdate() -} - -func (m *Manager) SetSchedule(startHour, startMinute, endHour, endMinute int) { - m.configMutex.Lock() - changed := m.config.StartHour != startHour || - m.config.StartMinute != startMinute || - m.config.EndHour != endHour || - m.config.EndMinute != endMinute - if !changed { - m.configMutex.Unlock() - return - } - m.config.StartHour = startHour - m.config.StartMinute = startMinute - m.config.EndHour = endHour - m.config.EndMinute = endMinute - m.configMutex.Unlock() - m.TriggerUpdate() -} - -func (m *Manager) SetLocation(lat, lon float64) { - m.configMutex.Lock() - if m.config.Latitude != nil && m.config.Longitude != nil && - *m.config.Latitude == lat && *m.config.Longitude == lon && !m.config.UseIPLocation { - m.configMutex.Unlock() - return - } - m.config.Latitude = &lat - m.config.Longitude = &lon - m.config.UseIPLocation = false - m.configMutex.Unlock() - - m.locationMutex.Lock() - m.cachedIPLat = nil - m.cachedIPLon = nil - m.locationMutex.Unlock() - - m.TriggerUpdate() -} - -func (m *Manager) SetUseIPLocation(use bool) { - m.configMutex.Lock() - if m.config.UseIPLocation == use { - m.configMutex.Unlock() - return - } - m.config.UseIPLocation = use - if use { - m.config.Latitude = nil - m.config.Longitude = nil - } - m.configMutex.Unlock() - - if use { - m.locationMutex.Lock() - m.cachedIPLat = nil - m.cachedIPLon = nil - m.locationMutex.Unlock() - } - - m.TriggerUpdate() -} - -func (m *Manager) TriggerUpdate() { - select { - case m.updateTrigger <- struct{}{}: - default: - } -} - -func (m *Manager) Close() { - select { - case <-m.stopChan: - return - default: - close(m.stopChan) - } - m.wg.Wait() - m.subscribers.Range(func(key string, ch chan State) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) -} - -func (m *Manager) WatchLoginctl(lm *loginctl.Manager) { - ch := lm.Subscribe("thememode") - m.wg.Add(1) - go func() { - defer m.wg.Done() - defer lm.Unsubscribe("thememode") - for { - select { - case <-m.stopChan: - return - case state, ok := <-ch: - if !ok { - return - } - if state.PreparingForSleep { - continue - } - m.TriggerUpdate() - } - } - }() -} - -func (m *Manager) schedulerLoop() { - defer m.wg.Done() - - var timer *time.Timer - for { - config := m.getConfig() - now := time.Now() - var isLight bool - var next time.Time - if config.Enabled { - isLight, next = m.computeSchedule(now, config) - } else { - m.stateMutex.RLock() - if m.state != nil { - isLight = m.state.IsLight - } - m.stateMutex.RUnlock() - next = now.Add(24 * time.Hour) - } - - m.updateStateWithValues(config, isLight, next) - - waitDur := time.Until(next) - if !config.Enabled { - waitDur = 24 * time.Hour - } - if waitDur < time.Second { - waitDur = time.Second - } - - if timer != nil { - timer.Stop() - } - timer = time.NewTimer(waitDur) - - select { - case <-m.stopChan: - timer.Stop() - return - case <-m.updateTrigger: - timer.Stop() - continue - case <-timer.C: - continue - } - } -} - -func (m *Manager) updateState(now time.Time) { - config := m.getConfig() - var isLight bool - var next time.Time - if config.Enabled { - isLight, next = m.computeSchedule(now, config) - } else { - m.stateMutex.RLock() - if m.state != nil { - isLight = m.state.IsLight - } - m.stateMutex.RUnlock() - next = now.Add(24 * time.Hour) - } - m.updateStateWithValues(config, isLight, next) -} - -func (m *Manager) updateStateWithValues(config Config, isLight bool, next time.Time) { - newState := State{ - Config: config, - IsLight: isLight, - NextTransition: next, - } - - m.stateMutex.Lock() - if m.state != nil && statesEqual(m.state, &newState) { - m.stateMutex.Unlock() - return - } - m.state = &newState - m.stateMutex.Unlock() - - m.notifySubscribers() -} - -func (m *Manager) notifySubscribers() { - state := m.GetState() - m.subscribers.Range(func(key string, ch chan State) bool { - select { - case ch <- state: - default: - } - return true - }) -} - -func (m *Manager) getConfig() Config { - m.configMutex.RLock() - defer m.configMutex.RUnlock() - return m.config -} - -func (m *Manager) SetGeoClient(client geolocation.Client) { - m.geoClient = client -} - -func (m *Manager) getLocation(config Config) (*float64, *float64) { - if config.Latitude != nil && config.Longitude != nil { - return config.Latitude, config.Longitude - } - if !config.UseIPLocation { - return nil, nil - } - if m.geoClient == nil { - return nil, nil - } - - m.locationMutex.RLock() - if m.cachedIPLat != nil && m.cachedIPLon != nil { - lat, lon := m.cachedIPLat, m.cachedIPLon - m.locationMutex.RUnlock() - return lat, lon - } - m.locationMutex.RUnlock() - - location, err := m.geoClient.GetLocation() - if err != nil { - return nil, nil - } - - m.locationMutex.Lock() - m.cachedIPLat = &location.Latitude - m.cachedIPLon = &location.Longitude - m.locationMutex.Unlock() - - return m.cachedIPLat, m.cachedIPLon -} - -func statesEqual(a, b *State) bool { - if a == nil || b == nil { - return a == b - } - if a.IsLight != b.IsLight || !a.NextTransition.Equal(b.NextTransition) { - return false - } - return a.Config == b.Config -} - -func (m *Manager) computeSchedule(now time.Time, config Config) (bool, time.Time) { - switch config.Mode { - case "location": - return m.computeLocationSchedule(now, config) - default: - return computeTimeSchedule(now, config) - } -} - -func computeTimeSchedule(now time.Time, config Config) (bool, time.Time) { - startMinutes := config.StartHour*60 + config.StartMinute - endMinutes := config.EndHour*60 + config.EndMinute - currentMinutes := now.Hour()*60 + now.Minute() - - startTime := time.Date(now.Year(), now.Month(), now.Day(), config.StartHour, config.StartMinute, 0, 0, now.Location()) - endTime := time.Date(now.Year(), now.Month(), now.Day(), config.EndHour, config.EndMinute, 0, 0, now.Location()) - - if startMinutes == endMinutes { - next := startTime - if !next.After(now) { - next = next.Add(24 * time.Hour) - } - return true, next - } - - if startMinutes < endMinutes { - if currentMinutes < startMinutes { - return true, startTime - } - if currentMinutes >= endMinutes { - return true, startTime.Add(24 * time.Hour) - } - return false, endTime - } - - if currentMinutes >= startMinutes { - return false, endTime.Add(24 * time.Hour) - } - if currentMinutes < endMinutes { - return false, endTime - } - return true, startTime -} - -func (m *Manager) computeLocationSchedule(now time.Time, config Config) (bool, time.Time) { - lat, lon := m.getLocation(config) - if lat == nil || lon == nil { - currentIsLight := false - m.stateMutex.RLock() - if m.state != nil { - currentIsLight = m.state.IsLight - } - m.stateMutex.RUnlock() - return currentIsLight, now.Add(10 * time.Minute) - } - - times, cond := wayland.CalculateSunTimesWithTwilight(*lat, *lon, now, config.ElevationTwilight, config.ElevationDaylight) - switch cond { - case wayland.SunMidnightSun: - return true, startOfNextDay(now) - case wayland.SunPolarNight: - return false, startOfNextDay(now) - } - - if now.Before(times.Sunrise) { - return false, times.Sunrise - } - if now.Before(times.Sunset) { - return true, times.Sunset - } - - nextDay := startOfNextDay(now) - nextTimes, nextCond := wayland.CalculateSunTimesWithTwilight(*lat, *lon, nextDay, config.ElevationTwilight, config.ElevationDaylight) - switch nextCond { - case wayland.SunMidnightSun: - return true, startOfNextDay(nextDay) - case wayland.SunPolarNight: - return false, startOfNextDay(nextDay) - } - - return false, nextTimes.Sunrise -} - -func startOfNextDay(t time.Time) time.Time { - next := t.Add(24 * time.Hour) - return time.Date(next.Year(), next.Month(), next.Day(), 0, 0, 0, 0, next.Location()) -} - -func validateHourMinute(hour, minute int) bool { - return hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59 -} - -func (m *Manager) ValidateSchedule(startHour, startMinute, endHour, endMinute int) error { - if !validateHourMinute(startHour, startMinute) || !validateHourMinute(endHour, endMinute) { - return errInvalidTime - } - return nil -} - -var errInvalidTime = errors.New("invalid schedule time") diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/types.go deleted file mode 100644 index 72e6468..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/types.go +++ /dev/null @@ -1,23 +0,0 @@ -package thememode - -import "time" - -type Config struct { - Enabled bool `json:"enabled"` - Mode string `json:"mode"` - StartHour int `json:"startHour"` - StartMinute int `json:"startMinute"` - EndHour int `json:"endHour"` - EndMinute int `json:"endMinute"` - Latitude *float64 `json:"latitude,omitempty"` - Longitude *float64 `json:"longitude,omitempty"` - UseIPLocation bool `json:"useIPLocation"` - ElevationTwilight float64 `json:"elevationTwilight"` - ElevationDaylight float64 `json:"elevationDaylight"` -} - -type State struct { - Config Config `json:"config"` - IsLight bool `json:"isLight"` - NextTransition time.Time `json:"nextTransition"` -} |