summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode
diff options
context:
space:
mode:
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/handlers.go154
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/manager.go462
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/thememode/types.go23
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"`
-}