summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location
diff options
context:
space:
mode:
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/handlers.go61
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/manager.go175
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/types.go28
3 files changed, 264 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/handlers.go
new file mode 100644
index 0000000..c9d556c
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/handlers.go
@@ -0,0 +1,61 @@
+package location
+
+import (
+ "encoding/json"
+ "fmt"
+ "net"
+
+ "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
+)
+
+type LocationEvent struct {
+ Type string `json:"type"`
+ Data State `json:"data"`
+}
+
+func HandleRequest(conn net.Conn, req models.Request, manager *Manager) {
+ switch req.Method {
+ case "location.getState":
+ handleGetState(conn, req, manager)
+ case "location.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 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()
+ event := LocationEvent{
+ Type: "state_changed",
+ Data: initialState,
+ }
+
+ if err := json.NewEncoder(conn).Encode(models.Response[LocationEvent]{
+ ID: req.ID,
+ Result: &event,
+ }); err != nil {
+ return
+ }
+
+ for state := range stateChan {
+ event := LocationEvent{
+ Type: "state_changed",
+ Data: state,
+ }
+ if err := json.NewEncoder(conn).Encode(models.Response[LocationEvent]{
+ Result: &event,
+ }); err != nil {
+ return
+ }
+ }
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/manager.go
new file mode 100644
index 0000000..24bcd95
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/manager.go
@@ -0,0 +1,175 @@
+package location
+
+import (
+ "time"
+
+ "github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation"
+ "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
+)
+
+func NewManager(client geolocation.Client) (*Manager, error) {
+ currLocation, err := client.GetLocation()
+ if err != nil {
+ log.Warnf("Failed to get initial location: %v", err)
+ }
+
+ m := &Manager{
+ client: client,
+ dirty: make(chan struct{}),
+ stopChan: make(chan struct{}),
+
+ state: &State{
+ Latitude: currLocation.Latitude,
+ Longitude: currLocation.Longitude,
+ },
+ }
+
+ if err := m.startSignalPump(); err != nil {
+ return nil, err
+ }
+
+ m.notifierWg.Add(1)
+ go m.notifier()
+
+ return m, nil
+}
+
+func (m *Manager) Close() {
+ close(m.stopChan)
+ m.notifierWg.Wait()
+
+ m.sigWG.Wait()
+
+ m.subscribers.Range(func(key string, ch chan State) bool {
+ close(ch)
+ m.subscribers.Delete(key)
+ return true
+ })
+}
+
+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 ch, ok := m.subscribers.LoadAndDelete(id); ok {
+ close(ch)
+ }
+}
+
+func (m *Manager) startSignalPump() error {
+ m.sigWG.Add(1)
+ go func() {
+ defer m.sigWG.Done()
+
+ subscription := m.client.Subscribe("locationManager")
+ defer m.client.Unsubscribe("locationManager")
+
+ for {
+ select {
+ case <-m.stopChan:
+ return
+ case location, ok := <-subscription:
+ if !ok {
+ return
+ }
+
+ m.handleLocationChange(location)
+ }
+ }
+ }()
+
+ return nil
+}
+
+func (m *Manager) handleLocationChange(location geolocation.Location) {
+ m.stateMutex.Lock()
+ defer m.stateMutex.Unlock()
+
+ m.state.Latitude = location.Latitude
+ m.state.Longitude = location.Longitude
+
+ m.notifySubscribers()
+}
+
+func (m *Manager) notifySubscribers() {
+ select {
+ case m.dirty <- struct{}{}:
+ default:
+ }
+}
+
+func (m *Manager) GetState() State {
+ m.stateMutex.RLock()
+ defer m.stateMutex.RUnlock()
+ if m.state == nil {
+ return State{
+ Latitude: 0.0,
+ Longitude: 0.0,
+ }
+ }
+ stateCopy := *m.state
+ return stateCopy
+}
+
+func (m *Manager) notifier() {
+ defer m.notifierWg.Done()
+ const minGap = 200 * time.Millisecond
+ timer := time.NewTimer(minGap)
+ timer.Stop()
+ var pending bool
+
+ for {
+ select {
+ case <-m.stopChan:
+ timer.Stop()
+ return
+ case <-m.dirty:
+ if pending {
+ continue
+ }
+ pending = true
+ timer.Reset(minGap)
+ case <-timer.C:
+ if !pending {
+ continue
+ }
+
+ currentState := m.GetState()
+
+ if m.lastNotified != nil && !stateChanged(m.lastNotified, &currentState) {
+ pending = false
+ continue
+ }
+
+ m.subscribers.Range(func(key string, ch chan State) bool {
+ select {
+ case ch <- currentState:
+ default:
+ log.Warn("Location: subscriber channel full, dropping update")
+ }
+ return true
+ })
+
+ stateCopy := currentState
+ m.lastNotified = &stateCopy
+ pending = false
+ }
+ }
+}
+
+func stateChanged(old, new *State) bool {
+ if old == nil || new == nil {
+ return true
+ }
+ if old.Latitude != new.Latitude {
+ return true
+ }
+ if old.Longitude != new.Longitude {
+ return true
+ }
+
+ return false
+}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/types.go
new file mode 100644
index 0000000..1f3d518
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/location/types.go
@@ -0,0 +1,28 @@
+package location
+
+import (
+ "sync"
+
+ "github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation"
+ "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
+)
+
+type State struct {
+ Latitude float64 `json:"latitude"`
+ Longitude float64 `json:"longitude"`
+}
+
+type Manager struct {
+ state *State
+ stateMutex sync.RWMutex
+
+ client geolocation.Client
+
+ stopChan chan struct{}
+ sigWG sync.WaitGroup
+
+ subscribers syncmap.Map[string, chan State]
+ dirty chan struct{}
+ notifierWg sync.WaitGroup
+ lastNotified *State
+}