summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl
diff options
context:
space:
mode:
authorNippy <nippy@rp1.hu>2026-05-09 13:33:09 +0200
committerNippy <nippy@rp1.hu>2026-05-09 13:33:09 +0200
commita2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5 (patch)
tree1a8769217f84bfe9d6216fafaadaf8cdd876d457 /raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl
parent34b9c34f982b2596cfa9e368a2d7f74e9a17477c (diff)
downloadRaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.tar.gz
RaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.zip
raveos update
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/actions.go88
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/constants.go10
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers.go156
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers_test.go500
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager.go723
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager_test.go296
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor.go155
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor_test.go298
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types.go76
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types_test.go50
10 files changed, 0 insertions, 2352 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/actions.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/actions.go
deleted file mode 100644
index 858248e..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/actions.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package loginctl
-
-import (
- "fmt"
-)
-
-func (m *Manager) Lock() error {
- if m.sessionObj == nil {
- return fmt.Errorf("session object not available")
- }
- err := m.sessionObj.Call(dbusSessionInterface+".Lock", 0).Err
- if err != nil {
- if refreshErr := m.refreshSessionBinding(); refreshErr == nil {
- err = m.sessionObj.Call(dbusSessionInterface+".Lock", 0).Err
- }
- if err != nil {
- return fmt.Errorf("failed to lock session: %w", err)
- }
- }
- return nil
-}
-
-func (m *Manager) Unlock() error {
- err := m.sessionObj.Call(dbusSessionInterface+".Unlock", 0).Err
- if err != nil {
- if refreshErr := m.refreshSessionBinding(); refreshErr == nil {
- err = m.sessionObj.Call(dbusSessionInterface+".Unlock", 0).Err
- }
- if err != nil {
- return fmt.Errorf("failed to unlock session: %w", err)
- }
- }
- return nil
-}
-
-func (m *Manager) Activate() error {
- err := m.sessionObj.Call(dbusSessionInterface+".Activate", 0).Err
- if err != nil {
- if refreshErr := m.refreshSessionBinding(); refreshErr == nil {
- err = m.sessionObj.Call(dbusSessionInterface+".Activate", 0).Err
- }
- if err != nil {
- return fmt.Errorf("failed to activate session: %w", err)
- }
- }
- return nil
-}
-
-func (m *Manager) SetIdleHint(idle bool) error {
- err := m.sessionObj.Call(dbusSessionInterface+".SetIdleHint", 0, idle).Err
- if err != nil {
- if refreshErr := m.refreshSessionBinding(); refreshErr == nil {
- err = m.sessionObj.Call(dbusSessionInterface+".SetIdleHint", 0, idle).Err
- }
- if err != nil {
- return fmt.Errorf("failed to set idle hint: %w", err)
- }
- }
- return nil
-}
-
-func (m *Manager) Terminate() error {
- err := m.sessionObj.Call(dbusSessionInterface+".Terminate", 0).Err
- if err != nil {
- if refreshErr := m.refreshSessionBinding(); refreshErr == nil {
- err = m.sessionObj.Call(dbusSessionInterface+".Terminate", 0).Err
- }
- if err != nil {
- return fmt.Errorf("failed to terminate session: %w", err)
- }
- }
- return nil
-}
-
-func (m *Manager) SetLockBeforeSuspend(enabled bool) {
- m.lockBeforeSuspend.Store(enabled)
-}
-
-func (m *Manager) SetSleepInhibitorEnabled(enabled bool) {
- m.sleepInhibitorEnabled.Store(enabled)
- if enabled {
- // Re-acquire inhibitor if enabled
- m.acquireSleepInhibitor()
- } else {
- // Release inhibitor if disabled
- m.releaseSleepInhibitor()
- }
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/constants.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/constants.go
deleted file mode 100644
index 1a29588..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/constants.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package loginctl
-
-const (
- dbusDest = "org.freedesktop.login1"
- dbusPath = "/org/freedesktop/login1"
- dbusManagerInterface = "org.freedesktop.login1.Manager"
- dbusSessionInterface = "org.freedesktop.login1.Session"
- dbusUserInterface = "org.freedesktop.login1.User"
- dbusPropsInterface = "org.freedesktop.DBus.Properties"
-)
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers.go
deleted file mode 100644
index 564e996..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers.go
+++ /dev/null
@@ -1,156 +0,0 @@
-package loginctl
-
-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) {
- switch req.Method {
- case "loginctl.getState":
- handleGetState(conn, req, manager)
- case "loginctl.lock":
- handleLock(conn, req, manager)
- case "loginctl.unlock":
- handleUnlock(conn, req, manager)
- case "loginctl.activate":
- handleActivate(conn, req, manager)
- case "loginctl.setIdleHint":
- handleSetIdleHint(conn, req, manager)
- case "loginctl.setLockBeforeSuspend":
- handleSetLockBeforeSuspend(conn, req, manager)
- case "loginctl.setSleepInhibitorEnabled":
- handleSetSleepInhibitorEnabled(conn, req, manager)
- case "loginctl.lockerReady":
- handleLockerReady(conn, req, manager)
- case "loginctl.terminate":
- handleTerminate(conn, req, manager)
- case "loginctl.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 handleLock(conn net.Conn, req models.Request, manager *Manager) {
- if err := manager.Lock(); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "locked"})
-}
-
-func handleUnlock(conn net.Conn, req models.Request, manager *Manager) {
- if err := manager.Unlock(); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "unlocked"})
-}
-
-func handleActivate(conn net.Conn, req models.Request, manager *Manager) {
- if err := manager.Activate(); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "activated"})
-}
-
-func handleSetIdleHint(conn net.Conn, req models.Request, manager *Manager) {
- idle, err := params.Bool(req.Params, "idle")
- if err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
-
- if err := manager.SetIdleHint(idle); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "idle hint set"})
-}
-
-func handleSetLockBeforeSuspend(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.SetLockBeforeSuspend(enabled)
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "lock before suspend set"})
-}
-
-func handleSetSleepInhibitorEnabled(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.SetSleepInhibitorEnabled(enabled)
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "sleep inhibitor setting updated"})
-}
-
-func handleLockerReady(conn net.Conn, req models.Request, manager *Manager) {
- manager.lockTimerMu.Lock()
- if manager.lockTimer != nil {
- manager.lockTimer.Stop()
- manager.lockTimer = nil
- }
- manager.lockTimerMu.Unlock()
-
- id := manager.sleepCycleID.Load()
- manager.releaseForCycle(id)
-
- if manager.inSleepCycle.Load() {
- manager.signalLockerReady()
- }
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "ok"})
-}
-
-func handleTerminate(conn net.Conn, req models.Request, manager *Manager) {
- if err := manager.Terminate(); err != nil {
- models.RespondError(conn, req.ID, err.Error())
- return
- }
- models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "terminated"})
-}
-
-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 := SessionEvent{
- Type: EventStateChanged,
- Data: initialState,
- }
- if err := json.NewEncoder(conn).Encode(models.Response[SessionEvent]{
- ID: req.ID,
- Result: &event,
- }); err != nil {
- return
- }
-
- for state := range stateChan {
- event := SessionEvent{
- Type: EventStateChanged,
- Data: state,
- }
- if err := json.NewEncoder(conn).Encode(models.Response[SessionEvent]{
- Result: &event,
- }); err != nil {
- return
- }
- }
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers_test.go
deleted file mode 100644
index d3169e4..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/handlers_test.go
+++ /dev/null
@@ -1,500 +0,0 @@
-package loginctl
-
-import (
- "bytes"
- "encoding/json"
- "net"
- "sync"
- "testing"
- "time"
-
- mockdbus "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/github.com/godbus/dbus/v5"
- "github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
- "github.com/godbus/dbus/v5"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/require"
-)
-
-type mockNetConn struct {
- net.Conn
- readBuf *bytes.Buffer
- writeBuf *bytes.Buffer
- closed bool
-}
-
-func newMockNetConn() *mockNetConn {
- return &mockNetConn{
- readBuf: &bytes.Buffer{},
- writeBuf: &bytes.Buffer{},
- }
-}
-
-func (m *mockNetConn) Read(b []byte) (n int, err error) {
- return m.readBuf.Read(b)
-}
-
-func (m *mockNetConn) Write(b []byte) (n int, err error) {
- return m.writeBuf.Write(b)
-}
-
-func (m *mockNetConn) Close() error {
- m.closed = true
- return nil
-}
-
-func TestRespondError_Loginctl(t *testing.T) {
- conn := newMockNetConn()
- models.RespondError(conn, 123, "test error")
-
- var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Equal(t, "test error", resp.Error)
- assert.Nil(t, resp.Result)
-}
-
-func TestRespond_Loginctl(t *testing.T) {
- conn := newMockNetConn()
- result := models.SuccessResult{Success: true, Message: "test"}
- models.Respond(conn, 123, result)
-
- var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Empty(t, resp.Error)
- require.NotNil(t, resp.Result)
- assert.True(t, resp.Result.Success)
- assert.Equal(t, "test", resp.Result.Message)
-}
-
-func TestHandleGetState(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- SessionID: "1",
- Locked: false,
- Active: true,
- SessionType: "wayland",
- SessionClass: "user",
- UserName: "testuser",
- },
- stateMutex: sync.RWMutex{},
- }
-
- conn := newMockNetConn()
- req := models.Request{ID: 123, Method: "loginctl.getState"}
-
- handleGetState(conn, req, manager)
-
- var resp models.Response[SessionState]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Empty(t, resp.Error)
- require.NotNil(t, resp.Result)
- assert.Equal(t, "1", resp.Result.SessionID)
- assert.False(t, resp.Result.Locked)
- assert.True(t, resp.Result.Active)
-}
-
-func TestHandleLock(t *testing.T) {
- t.Run("successful lock", func(t *testing.T) {
- mockSessionObj := mockdbus.NewMockBusObject(t)
- mockCall := &dbus.Call{Err: nil}
- mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Lock", dbus.Flags(0)).Return(mockCall)
-
- manager := &Manager{
- state: &SessionState{},
- stateMutex: sync.RWMutex{},
- sessionObj: mockSessionObj,
- }
-
- conn := newMockNetConn()
- req := models.Request{ID: 123, Method: "loginctl.lock"}
- handleLock(conn, req, manager)
-
- var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Empty(t, resp.Error)
- require.NotNil(t, resp.Result)
- assert.True(t, resp.Result.Success)
- assert.Equal(t, "locked", resp.Result.Message)
- })
-
- t.Run("lock fails", func(t *testing.T) {
- mockSessionObj := mockdbus.NewMockBusObject(t)
- mockCall := &dbus.Call{Err: assert.AnError}
- mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Lock", dbus.Flags(0)).Return(mockCall)
-
- manager := &Manager{
- state: &SessionState{},
- stateMutex: sync.RWMutex{},
- sessionObj: mockSessionObj,
- }
-
- conn := newMockNetConn()
- req := models.Request{ID: 123, Method: "loginctl.lock"}
- handleLock(conn, req, manager)
-
- var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Contains(t, resp.Error, "failed to lock session")
- })
-}
-
-func TestHandleUnlock(t *testing.T) {
- t.Run("successful unlock", func(t *testing.T) {
- mockSessionObj := mockdbus.NewMockBusObject(t)
- mockCall := &dbus.Call{Err: nil}
- mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Unlock", dbus.Flags(0)).Return(mockCall)
-
- manager := &Manager{
- state: &SessionState{},
- stateMutex: sync.RWMutex{},
- sessionObj: mockSessionObj,
- }
-
- conn := newMockNetConn()
- req := models.Request{ID: 123, Method: "loginctl.unlock"}
- handleUnlock(conn, req, manager)
-
- var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Empty(t, resp.Error)
- require.NotNil(t, resp.Result)
- assert.True(t, resp.Result.Success)
- assert.Equal(t, "unlocked", resp.Result.Message)
- })
-
- t.Run("unlock fails", func(t *testing.T) {
- mockSessionObj := mockdbus.NewMockBusObject(t)
- mockCall := &dbus.Call{Err: assert.AnError}
- mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Unlock", dbus.Flags(0)).Return(mockCall)
-
- manager := &Manager{
- state: &SessionState{},
- stateMutex: sync.RWMutex{},
- sessionObj: mockSessionObj,
- }
-
- conn := newMockNetConn()
- req := models.Request{ID: 123, Method: "loginctl.unlock"}
- handleUnlock(conn, req, manager)
-
- var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Contains(t, resp.Error, "failed to unlock session")
- })
-}
-
-func TestHandleActivate(t *testing.T) {
- t.Run("successful activate", func(t *testing.T) {
- mockSessionObj := mockdbus.NewMockBusObject(t)
- mockCall := &dbus.Call{Err: nil}
- mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Activate", dbus.Flags(0)).Return(mockCall)
-
- manager := &Manager{
- state: &SessionState{},
- stateMutex: sync.RWMutex{},
- sessionObj: mockSessionObj,
- }
-
- conn := newMockNetConn()
- req := models.Request{ID: 123, Method: "loginctl.activate"}
- handleActivate(conn, req, manager)
-
- var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Empty(t, resp.Error)
- require.NotNil(t, resp.Result)
- assert.True(t, resp.Result.Success)
- assert.Equal(t, "activated", resp.Result.Message)
- })
-
- t.Run("activate fails", func(t *testing.T) {
- mockSessionObj := mockdbus.NewMockBusObject(t)
- mockCall := &dbus.Call{Err: assert.AnError}
- mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Activate", dbus.Flags(0)).Return(mockCall)
-
- manager := &Manager{
- state: &SessionState{},
- stateMutex: sync.RWMutex{},
- sessionObj: mockSessionObj,
- }
-
- conn := newMockNetConn()
- req := models.Request{ID: 123, Method: "loginctl.activate"}
- handleActivate(conn, req, manager)
-
- var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Contains(t, resp.Error, "failed to activate session")
- })
-}
-
-func TestHandleSetIdleHint(t *testing.T) {
- t.Run("missing idle parameter", func(t *testing.T) {
- manager := &Manager{
- state: &SessionState{},
- stateMutex: sync.RWMutex{},
- }
-
- conn := newMockNetConn()
- req := models.Request{
- ID: 123,
- Method: "loginctl.setIdleHint",
- Params: map[string]any{},
- }
-
- handleSetIdleHint(conn, req, manager)
-
- var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Contains(t, resp.Error, "missing or invalid 'idle' parameter")
- })
-
- t.Run("successful set idle hint true", func(t *testing.T) {
- mockSessionObj := mockdbus.NewMockBusObject(t)
- mockCall := &dbus.Call{Err: nil}
- mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.SetIdleHint", dbus.Flags(0), true).Return(mockCall)
-
- manager := &Manager{
- state: &SessionState{},
- stateMutex: sync.RWMutex{},
- sessionObj: mockSessionObj,
- }
-
- conn := newMockNetConn()
- req := models.Request{
- ID: 123,
- Method: "loginctl.setIdleHint",
- Params: map[string]any{
- "idle": true,
- },
- }
-
- handleSetIdleHint(conn, req, manager)
-
- var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Empty(t, resp.Error)
- require.NotNil(t, resp.Result)
- assert.True(t, resp.Result.Success)
- assert.Equal(t, "idle hint set", resp.Result.Message)
- })
-
- t.Run("set idle hint fails", func(t *testing.T) {
- mockSessionObj := mockdbus.NewMockBusObject(t)
- mockCall := &dbus.Call{Err: assert.AnError}
- mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.SetIdleHint", dbus.Flags(0), false).Return(mockCall)
-
- manager := &Manager{
- state: &SessionState{},
- stateMutex: sync.RWMutex{},
- sessionObj: mockSessionObj,
- }
-
- conn := newMockNetConn()
- req := models.Request{
- ID: 123,
- Method: "loginctl.setIdleHint",
- Params: map[string]any{
- "idle": false,
- },
- }
-
- handleSetIdleHint(conn, req, manager)
-
- var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Contains(t, resp.Error, "failed to set idle hint")
- })
-}
-
-func TestHandleTerminate(t *testing.T) {
- t.Run("successful terminate", func(t *testing.T) {
- mockSessionObj := mockdbus.NewMockBusObject(t)
- mockCall := &dbus.Call{Err: nil}
- mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Terminate", dbus.Flags(0)).Return(mockCall)
-
- manager := &Manager{
- state: &SessionState{},
- stateMutex: sync.RWMutex{},
- sessionObj: mockSessionObj,
- }
-
- conn := newMockNetConn()
- req := models.Request{ID: 123, Method: "loginctl.terminate"}
- handleTerminate(conn, req, manager)
-
- var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Empty(t, resp.Error)
- require.NotNil(t, resp.Result)
- assert.True(t, resp.Result.Success)
- assert.Equal(t, "terminated", resp.Result.Message)
- })
-
- t.Run("terminate fails", func(t *testing.T) {
- mockSessionObj := mockdbus.NewMockBusObject(t)
- mockCall := &dbus.Call{Err: assert.AnError}
- mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Terminate", dbus.Flags(0)).Return(mockCall)
-
- manager := &Manager{
- state: &SessionState{},
- stateMutex: sync.RWMutex{},
- sessionObj: mockSessionObj,
- }
-
- conn := newMockNetConn()
- req := models.Request{ID: 123, Method: "loginctl.terminate"}
- handleTerminate(conn, req, manager)
-
- var resp models.Response[models.SuccessResult]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Contains(t, resp.Error, "failed to terminate session")
- })
-}
-
-func TestHandleRequest(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- SessionID: "1",
- Locked: false,
- },
- stateMutex: sync.RWMutex{},
- }
-
- t.Run("unknown method", func(t *testing.T) {
- conn := newMockNetConn()
- req := models.Request{
- ID: 123,
- Method: "loginctl.unknown",
- }
-
- HandleRequest(conn, req, manager)
-
- var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Contains(t, resp.Error, "unknown method")
- })
-
- t.Run("valid method - getState", func(t *testing.T) {
- conn := newMockNetConn()
- req := models.Request{
- ID: 123,
- Method: "loginctl.getState",
- }
-
- HandleRequest(conn, req, manager)
-
- var resp models.Response[SessionState]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- assert.Empty(t, resp.Error)
- })
-
- t.Run("lock method", func(t *testing.T) {
- mockSessionObj := mockdbus.NewMockBusObject(t)
- mockCall := &dbus.Call{Err: nil}
- mockSessionObj.EXPECT().Call("org.freedesktop.login1.Session.Lock", mock.Anything).Return(mockCall)
-
- manager.sessionObj = mockSessionObj
-
- conn := newMockNetConn()
- req := models.Request{
- ID: 123,
- Method: "loginctl.lock",
- }
-
- HandleRequest(conn, req, manager)
-
- var resp models.Response[any]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- require.NoError(t, err)
-
- assert.Equal(t, 123, resp.ID)
- })
-}
-
-func TestHandleSubscribe(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- SessionID: "1",
- Locked: false,
- },
- stateMutex: sync.RWMutex{},
- }
-
- conn := newMockNetConn()
- req := models.Request{ID: 123, Method: "loginctl.subscribe"}
-
- done := make(chan bool)
- go func() {
- handleSubscribe(conn, req, manager)
- done <- true
- }()
-
- time.Sleep(50 * time.Millisecond)
-
- conn.Close()
-
- if conn.writeBuf.Len() > 0 {
- var resp models.Response[SessionEvent]
- err := json.NewDecoder(conn.writeBuf).Decode(&resp)
- if err == nil {
- assert.Equal(t, 123, resp.ID)
- require.NotNil(t, resp.Result)
- assert.Equal(t, EventStateChanged, resp.Result.Type)
- assert.Equal(t, "1", resp.Result.Data.SessionID)
- }
- }
-
- select {
- case <-done:
- case <-time.After(100 * time.Millisecond):
- }
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager.go
deleted file mode 100644
index 0c51e64..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager.go
+++ /dev/null
@@ -1,723 +0,0 @@
-package loginctl
-
-import (
- "context"
- "fmt"
- "os"
- "sync"
- "time"
-
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil"
- "github.com/godbus/dbus/v5"
-)
-
-func NewManager() (*Manager, error) {
- conn, err := dbus.ConnectSystemBus()
- if err != nil {
- return nil, fmt.Errorf("failed to connect to system bus: %w", err)
- }
-
- m := &Manager{
- state: &SessionState{},
- stateMutex: sync.RWMutex{},
-
- stopChan: make(chan struct{}),
- conn: conn,
- dirty: make(chan struct{}, 1),
- signals: make(chan *dbus.Signal, 256),
- }
- m.sleepInhibitorEnabled.Store(true)
-
- if err := m.initialize(); err != nil {
- conn.Close()
- return nil, err
- }
-
- if err := m.acquireSleepInhibitor(); err != nil {
- fmt.Fprintf(os.Stderr, "sleep inhibitor unavailable: %v\n", err)
- }
-
- m.notifierWg.Add(1)
- go m.notifier()
-
- if err := m.startSignalPump(); err != nil {
- m.Close()
- return nil, err
- }
-
- return m, nil
-}
-
-func (m *Manager) initialize() error {
- m.managerObj = m.conn.Object(dbusDest, dbus.ObjectPath(dbusPath))
-
- m.initializeFallbackDelay()
-
- sessionID, sessionPath, err := m.discoverSession()
- if err != nil {
- return fmt.Errorf("failed to get session path: %w", err)
- }
-
- m.stateMutex.Lock()
- m.state.SessionID = sessionID
- m.state.SessionPath = string(sessionPath)
- m.sessionPath = sessionPath
- m.stateMutex.Unlock()
-
- m.sessionObj = m.conn.Object(dbusDest, sessionPath)
-
- if err := m.updateSessionState(); err != nil {
- return err
- }
-
- return nil
-}
-
-func (m *Manager) discoverSession() (string, dbus.ObjectPath, error) {
- // 1. Explicit XDG_SESSION_ID
- if id := os.Getenv("XDG_SESSION_ID"); id != "" {
- if path, err := m.getSession(id); err == nil {
- fmt.Fprintf(os.Stderr, "loginctl: using XDG_SESSION_ID=%s\n", id)
- return id, path, nil
- }
- }
-
- // 2. PID-based lookup (works when caller is inside a session cgroup)
- if id, path, err := m.getSessionByPID(uint32(os.Getpid())); err == nil {
- fmt.Fprintf(os.Stderr, "loginctl: found session %s via PID\n", id)
- return id, path, nil
- }
-
- // 3. User's primary display session (handles UWSM and similar)
- if id, path, err := m.getUserDisplaySession(); err == nil {
- fmt.Fprintf(os.Stderr, "loginctl: found session %s via User.Display\n", id)
- return id, path, nil
- }
-
- // 4. Score all sessions for current UID
- if id, path, err := m.findBestSession(); err == nil {
- fmt.Fprintf(os.Stderr, "loginctl: found session %s via ListSessions scoring\n", id)
- return id, path, nil
- }
-
- // 5. Last resort: "self"
- path, err := m.getSession("self")
- if err != nil {
- return "", "", fmt.Errorf("%w", err)
- }
- return "self", path, nil
-}
-
-func (m *Manager) getSession(id string) (dbus.ObjectPath, error) {
- var out dbus.ObjectPath
- err := m.managerObj.Call(dbusManagerInterface+".GetSession", 0, id).Store(&out)
- if err != nil {
- return "", err
- }
- return out, nil
-}
-
-func (m *Manager) getSessionByPID(pid uint32) (string, dbus.ObjectPath, error) {
- var path dbus.ObjectPath
- if err := m.managerObj.Call(dbusManagerInterface+".GetSessionByPID", 0, pid).Store(&path); err != nil {
- return "", "", err
- }
-
- sessionObj := m.conn.Object(dbusDest, path)
- var id dbus.Variant
- if err := sessionObj.Call(dbusPropsInterface+".Get", 0, dbusSessionInterface, "Id").Store(&id); err != nil {
- return "", "", err
- }
- return id.Value().(string), path, nil
-}
-
-func (m *Manager) getUserDisplaySession() (string, dbus.ObjectPath, error) {
- uid := uint32(os.Getuid())
-
- var userPath dbus.ObjectPath
- if err := m.managerObj.Call(dbusManagerInterface+".GetUser", 0, uid).Store(&userPath); err != nil {
- return "", "", err
- }
-
- userObj := m.conn.Object(dbusDest, userPath)
- var display dbus.Variant
- if err := userObj.Call(dbusPropsInterface+".Get", 0, dbusUserInterface, "Display").Store(&display); err != nil {
- return "", "", err
- }
-
- pair, ok := display.Value().([]any)
- if !ok || len(pair) < 2 {
- return "", "", fmt.Errorf("unexpected Display format")
- }
-
- sessionID, _ := pair[0].(string)
- sessionPath, _ := pair[1].(dbus.ObjectPath)
- if sessionID == "" || sessionPath == "" {
- return "", "", fmt.Errorf("empty Display session")
- }
-
- return sessionID, sessionPath, nil
-}
-
-type sessionCandidate struct {
- id string
- path dbus.ObjectPath
-}
-
-func (m *Manager) findBestSession() (string, dbus.ObjectPath, error) {
- // ListSessions returns a(susso): [][]any where each entry is [id, uid, name, seat, path]
- var raw [][]any
- if err := m.managerObj.Call(dbusManagerInterface+".ListSessions", 0).Store(&raw); err != nil {
- return "", "", err
- }
-
- uid := uint32(os.Getuid())
- var candidates []sessionCandidate
- for _, entry := range raw {
- if len(entry) < 5 {
- continue
- }
- entryUID, _ := entry[1].(uint32)
- if entryUID != uid {
- continue
- }
- id, _ := entry[0].(string)
- path, _ := entry[4].(dbus.ObjectPath)
- if id != "" && path != "" {
- candidates = append(candidates, sessionCandidate{id: id, path: path})
- }
- }
- if len(candidates) == 0 {
- return "", "", fmt.Errorf("no sessions for uid %d", uid)
- }
-
- bestScore := -1
- var best sessionCandidate
- for _, c := range candidates {
- score := m.scoreSession(c.path)
- if score > bestScore {
- bestScore = score
- best = c
- }
- }
- if bestScore < 0 {
- return "", "", fmt.Errorf("no viable session found")
- }
- return best.id, best.path, nil
-}
-
-func (m *Manager) scoreSession(path dbus.ObjectPath) int {
- obj := m.conn.Object(dbusDest, path)
- var props map[string]dbus.Variant
- if err := obj.Call(dbusPropsInterface+".GetAll", 0, dbusSessionInterface).Store(&props); err != nil {
- return -1
- }
-
- getStr := func(key string) string {
- if v, ok := props[key]; ok {
- if s, ok := v.Value().(string); ok {
- return s
- }
- }
- return ""
- }
- getBool := func(key string) bool {
- if v, ok := props[key]; ok {
- if b, ok := v.Value().(bool); ok {
- return b
- }
- }
- return false
- }
- getUint32 := func(key string) uint32 {
- if v, ok := props[key]; ok {
- if u, ok := v.Value().(uint32); ok {
- return u
- }
- }
- return 0
- }
-
- class := getStr("Class")
- if class != "user" {
- return -1
- }
- if getBool("Remote") {
- return -1
- }
-
- score := 0
-
- if getBool("Active") {
- score += 100
- }
-
- switch getStr("Type") {
- case "wayland", "x11":
- score += 80
- case "tty":
- score += 10
- }
-
- if v, ok := props["Seat"]; ok {
- if seatArr, ok := v.Value().([]any); ok && len(seatArr) >= 1 {
- if seat, ok := seatArr[0].(string); ok && seat != "" {
- score += 40
- if seat == "seat0" {
- score += 10
- }
- }
- }
- }
-
- if getUint32("VTNr") > 0 {
- score += 20
- }
-
- return score
-}
-
-func (m *Manager) refreshSessionBinding() error {
- if m.managerObj == nil || m.conn == nil {
- return fmt.Errorf("manager not fully initialized")
- }
-
- sessionPath, err := m.getSession(m.state.SessionID)
- if err != nil {
- return fmt.Errorf("failed to get session path: %w", err)
- }
-
- m.stateMutex.RLock()
- currentPath := m.sessionPath
- m.stateMutex.RUnlock()
-
- if sessionPath == currentPath {
- return nil
- }
-
- m.stopSignalPump()
-
- m.stateMutex.Lock()
- m.state.SessionPath = string(sessionPath)
- m.sessionPath = sessionPath
- m.stateMutex.Unlock()
-
- m.sessionObj = m.conn.Object(dbusDest, sessionPath)
-
- if err := m.updateSessionState(); err != nil {
- return err
- }
-
- m.signals = make(chan *dbus.Signal, 256)
- return m.startSignalPump()
-}
-
-func (m *Manager) updateSessionState() error {
- ctx := context.Background()
- props, err := m.getSessionProperties(ctx)
- if err != nil {
- return err
- }
-
- m.stateMutex.Lock()
- defer m.stateMutex.Unlock()
-
- m.state.Active = dbusutil.GetOr(props, "Active", m.state.Active)
- m.state.IdleHint = dbusutil.GetOr(props, "IdleHint", m.state.IdleHint)
- m.state.IdleSinceHint = dbusutil.GetOr(props, "IdleSinceHint", m.state.IdleSinceHint)
- if lockedHint, ok := dbusutil.Get[bool](props, "LockedHint"); ok {
- m.state.LockedHint = lockedHint
- m.state.Locked = lockedHint
- }
- m.state.SessionType = dbusutil.GetOr(props, "Type", m.state.SessionType)
- m.state.SessionClass = dbusutil.GetOr(props, "Class", m.state.SessionClass)
- if v, ok := props["User"]; ok {
- if userArr, ok := v.Value().([]any); ok && len(userArr) >= 1 {
- if uid, ok := userArr[0].(uint32); ok {
- m.state.User = uid
- }
- }
- }
- m.state.UserName = dbusutil.GetOr(props, "Name", m.state.UserName)
- m.state.RemoteHost = dbusutil.GetOr(props, "RemoteHost", m.state.RemoteHost)
- m.state.Service = dbusutil.GetOr(props, "Service", m.state.Service)
- m.state.TTY = dbusutil.GetOr(props, "TTY", m.state.TTY)
- m.state.Display = dbusutil.GetOr(props, "Display", m.state.Display)
- m.state.Remote = dbusutil.GetOr(props, "Remote", m.state.Remote)
- if v, ok := props["Seat"]; ok {
- if seatArr, ok := v.Value().([]any); ok && len(seatArr) >= 1 {
- if seatID, ok := seatArr[0].(string); ok {
- m.state.Seat = seatID
- }
- }
- }
- m.state.VTNr = dbusutil.GetOr(props, "VTNr", m.state.VTNr)
-
- return nil
-}
-
-func (m *Manager) getSessionProperties(ctx context.Context) (map[string]dbus.Variant, error) {
- var props map[string]dbus.Variant
- err := m.sessionObj.CallWithContext(ctx, dbusPropsInterface+".GetAll", 0, dbusSessionInterface).Store(&props)
- if err != nil {
- return nil, err
- }
- return props, nil
-}
-
-func (m *Manager) acquireSleepInhibitor() error {
- if !m.sleepInhibitorEnabled.Load() {
- return nil
- }
-
- m.inhibitMu.Lock()
- defer m.inhibitMu.Unlock()
-
- if m.inhibitFile != nil {
- return nil
- }
-
- if m.managerObj == nil {
- return fmt.Errorf("manager object not available")
- }
-
- file, err := m.inhibit("sleep", "DankMaterialShell", "Lock before suspend", "delay")
- if err != nil {
- return err
- }
-
- m.inhibitFile = file
- return nil
-}
-
-func (m *Manager) inhibit(what, who, why, mode string) (*os.File, error) {
- var fd dbus.UnixFD
- err := m.managerObj.Call(dbusManagerInterface+".Inhibit", 0, what, who, why, mode).Store(&fd)
- if err != nil {
- return nil, err
- }
- return os.NewFile(uintptr(fd), "inhibit"), nil
-}
-
-func (m *Manager) releaseSleepInhibitor() {
- m.inhibitMu.Lock()
- f := m.inhibitFile
- m.inhibitFile = nil
- m.inhibitMu.Unlock()
- if f != nil {
- f.Close()
- }
-}
-
-func (m *Manager) releaseForCycle(id uint64) {
- if !m.inSleepCycle.Load() || m.sleepCycleID.Load() != id {
- return
- }
- m.releaseSleepInhibitor()
-}
-
-func (m *Manager) initializeFallbackDelay() {
- var maxDelayUSec uint64
- err := m.managerObj.Call(
- dbusPropsInterface+".Get",
- 0,
- dbusManagerInterface,
- "InhibitDelayMaxUSec",
- ).Store(&maxDelayUSec)
-
- if err != nil {
- m.fallbackDelay = 2 * time.Second
- return
- }
-
- maxDelay := time.Duration(maxDelayUSec) * time.Microsecond
- computed := (maxDelay * 8) / 10
-
- if computed < 2*time.Second {
- m.fallbackDelay = 2 * time.Second
- } else if computed > 4*time.Second {
- m.fallbackDelay = 4 * time.Second
- } else {
- m.fallbackDelay = computed
- }
-}
-
-func (m *Manager) newLockerReadyCh() chan struct{} {
- m.lockerReadyChMu.Lock()
- defer m.lockerReadyChMu.Unlock()
- m.lockerReadyCh = make(chan struct{})
- return m.lockerReadyCh
-}
-
-func (m *Manager) signalLockerReady() {
- m.lockerReadyChMu.Lock()
- ch := m.lockerReadyCh
- if ch != nil {
- close(ch)
- m.lockerReadyCh = nil
- }
- m.lockerReadyChMu.Unlock()
-}
-
-func (m *Manager) snapshotState() SessionState {
- m.stateMutex.RLock()
- defer m.stateMutex.RUnlock()
- return *m.state
-}
-
-func stateChangedMeaningfully(old, new *SessionState) bool {
- if old.Locked != new.Locked {
- return true
- }
- if old.LockedHint != new.LockedHint {
- return true
- }
- if old.Active != new.Active {
- return true
- }
- if old.IdleHint != new.IdleHint {
- return true
- }
- if old.PreparingForSleep != new.PreparingForSleep {
- return true
- }
- return false
-}
-
-func (m *Manager) GetState() SessionState {
- return m.snapshotState()
-}
-
-func (m *Manager) Subscribe(id string) chan SessionState {
- ch := make(chan SessionState, 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) notifier() {
- defer m.notifierWg.Done()
- const minGap = 100 * 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.snapshotState()
-
- if m.lastNotifiedState != nil && !stateChangedMeaningfully(m.lastNotifiedState, &currentState) {
- pending = false
- continue
- }
-
- m.subscribers.Range(func(key string, ch chan SessionState) bool {
- select {
- case ch <- currentState:
- default:
- }
- return true
- })
-
- stateCopy := currentState
- m.lastNotifiedState = &stateCopy
- pending = false
- }
- }
-}
-
-func (m *Manager) notifySubscribers() {
- select {
- case m.dirty <- struct{}{}:
- default:
- }
-}
-
-func (m *Manager) startSignalPump() error {
- m.conn.Signal(m.signals)
-
- if err := m.conn.AddMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusPropsInterface),
- dbus.WithMatchMember("PropertiesChanged"),
- ); err != nil {
- m.conn.RemoveSignal(m.signals)
- return err
- }
- if err := m.conn.AddMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusSessionInterface),
- dbus.WithMatchMember("Lock"),
- ); err != nil {
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusPropsInterface),
- dbus.WithMatchMember("PropertiesChanged"),
- )
- m.conn.RemoveSignal(m.signals)
- return err
- }
- if err := m.conn.AddMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusSessionInterface),
- dbus.WithMatchMember("Unlock"),
- ); err != nil {
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusPropsInterface),
- dbus.WithMatchMember("PropertiesChanged"),
- )
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusSessionInterface),
- dbus.WithMatchMember("Lock"),
- )
- m.conn.RemoveSignal(m.signals)
- return err
- }
- if err := m.conn.AddMatchSignal(
- dbus.WithMatchObjectPath(dbus.ObjectPath(dbusPath)),
- dbus.WithMatchInterface(dbusManagerInterface),
- dbus.WithMatchMember("PrepareForSleep"),
- ); err != nil {
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusPropsInterface),
- dbus.WithMatchMember("PropertiesChanged"),
- )
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusSessionInterface),
- dbus.WithMatchMember("Lock"),
- )
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusSessionInterface),
- dbus.WithMatchMember("Unlock"),
- )
- m.conn.RemoveSignal(m.signals)
- return err
- }
-
- if err := m.conn.AddMatchSignal(
- dbus.WithMatchObjectPath("/org/freedesktop/DBus"),
- dbus.WithMatchInterface("org.freedesktop.DBus"),
- dbus.WithMatchMember("NameOwnerChanged"),
- ); err != nil {
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusPropsInterface),
- dbus.WithMatchMember("PropertiesChanged"),
- )
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusSessionInterface),
- dbus.WithMatchMember("Lock"),
- )
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusSessionInterface),
- dbus.WithMatchMember("Unlock"),
- )
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath(dbus.ObjectPath(dbusPath)),
- dbus.WithMatchInterface(dbusManagerInterface),
- dbus.WithMatchMember("PrepareForSleep"),
- )
- m.conn.RemoveSignal(m.signals)
- return err
- }
-
- m.sigWG.Add(1)
- go func() {
- defer m.sigWG.Done()
- for {
- select {
- case <-m.stopChan:
- return
- case sig, ok := <-m.signals:
- if !ok {
- return
- }
- if sig == nil {
- continue
- }
- m.handleDBusSignal(sig)
- }
- }
- }()
- return nil
-}
-
-func (m *Manager) stopSignalPump() {
- if m.conn == nil {
- return
- }
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusPropsInterface),
- dbus.WithMatchMember("PropertiesChanged"),
- )
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusSessionInterface),
- dbus.WithMatchMember("Lock"),
- )
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath(m.sessionPath),
- dbus.WithMatchInterface(dbusSessionInterface),
- dbus.WithMatchMember("Unlock"),
- )
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath(dbus.ObjectPath(dbusPath)),
- dbus.WithMatchInterface(dbusManagerInterface),
- dbus.WithMatchMember("PrepareForSleep"),
- )
- m.conn.RemoveMatchSignal(
- dbus.WithMatchObjectPath("/org/freedesktop/DBus"),
- dbus.WithMatchInterface("org.freedesktop.DBus"),
- dbus.WithMatchMember("NameOwnerChanged"),
- )
-
- m.conn.RemoveSignal(m.signals)
- close(m.signals)
-
- m.sigWG.Wait()
-}
-
-func (m *Manager) Close() {
- close(m.stopChan)
- m.notifierWg.Wait()
-
- m.stopSignalPump()
-
- m.releaseSleepInhibitor()
-
- m.subscribers.Range(func(key string, ch chan SessionState) bool {
- close(ch)
- m.subscribers.Delete(key)
- return true
- })
-
- if m.conn != nil {
- m.conn.Close()
- }
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager_test.go
deleted file mode 100644
index 148ee60..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/manager_test.go
+++ /dev/null
@@ -1,296 +0,0 @@
-package loginctl
-
-import (
- "sync"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestManager_GetState(t *testing.T) {
- state := &SessionState{
- SessionID: "1",
- Locked: false,
- Active: true,
- IdleHint: false,
- SessionType: "wayland",
- SessionClass: "user",
- UserName: "testuser",
- }
-
- manager := &Manager{
- state: state,
- stateMutex: sync.RWMutex{},
- }
-
- result := manager.GetState()
- assert.Equal(t, "1", result.SessionID)
- assert.False(t, result.Locked)
- assert.True(t, result.Active)
- assert.Equal(t, "wayland", result.SessionType)
- assert.Equal(t, "testuser", result.UserName)
-}
-
-func TestManager_Subscribe(t *testing.T) {
- manager := &Manager{
- state: &SessionState{},
- }
-
- ch := manager.Subscribe("test-client")
- assert.NotNil(t, ch)
- assert.Equal(t, 64, cap(ch))
-
- _, exists := manager.subscribers.Load("test-client")
- assert.True(t, exists)
-}
-
-func TestManager_Unsubscribe(t *testing.T) {
- manager := &Manager{
- state: &SessionState{},
- }
-
- ch := manager.Subscribe("test-client")
-
- manager.Unsubscribe("test-client")
-
- _, ok := <-ch
- assert.False(t, ok)
-
- _, exists := manager.subscribers.Load("test-client")
- assert.False(t, exists)
-}
-
-func TestManager_Unsubscribe_NonExistent(t *testing.T) {
- manager := &Manager{
- state: &SessionState{},
- }
-
- // Unsubscribe a non-existent client should not panic
- assert.NotPanics(t, func() {
- manager.Unsubscribe("non-existent")
- })
-}
-
-func TestManager_NotifySubscribers(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- SessionID: "1",
- Locked: false,
- },
- stateMutex: sync.RWMutex{},
- stopChan: make(chan struct{}),
- dirty: make(chan struct{}, 1),
- }
- manager.notifierWg.Add(1)
- go manager.notifier()
-
- ch := make(chan SessionState, 10)
- manager.subscribers.Store("test-client", ch)
-
- manager.notifySubscribers()
-
- select {
- case state := <-ch:
- assert.Equal(t, "1", state.SessionID)
- assert.False(t, state.Locked)
- case <-time.After(200 * time.Millisecond):
- t.Fatal("did not receive state update")
- }
-
- close(manager.stopChan)
- manager.notifierWg.Wait()
-}
-
-func TestManager_NotifySubscribers_Debounce(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- SessionID: "1",
- Locked: false,
- },
- stateMutex: sync.RWMutex{},
- stopChan: make(chan struct{}),
- dirty: make(chan struct{}, 1),
- }
- manager.notifierWg.Add(1)
- go manager.notifier()
-
- ch := make(chan SessionState, 10)
- manager.subscribers.Store("test-client", ch)
-
- manager.notifySubscribers()
- manager.notifySubscribers()
- manager.notifySubscribers()
-
- receivedCount := 0
- timeout := time.After(200 * time.Millisecond)
- for {
- select {
- case <-ch:
- receivedCount++
- case <-timeout:
- assert.Equal(t, 1, receivedCount, "should receive exactly one debounced update")
- close(manager.stopChan)
- manager.notifierWg.Wait()
- return
- }
- }
-}
-
-func TestManager_Close(t *testing.T) {
- manager := &Manager{
- state: &SessionState{},
- stateMutex: sync.RWMutex{},
- stopChan: make(chan struct{}),
- }
-
- ch1 := make(chan SessionState, 1)
- ch2 := make(chan SessionState, 1)
- manager.subscribers.Store("client1", ch1)
- manager.subscribers.Store("client2", ch2)
-
- manager.Close()
-
- select {
- case <-manager.stopChan:
- case <-time.After(100 * time.Millisecond):
- t.Fatal("stopChan not closed")
- }
-
- _, ok1 := <-ch1
- _, ok2 := <-ch2
- assert.False(t, ok1, "ch1 should be closed")
- assert.False(t, ok2, "ch2 should be closed")
-
- count := 0
- manager.subscribers.Range(func(key string, ch chan SessionState) bool {
- count++
- return true
- })
- assert.Equal(t, 0, count)
-}
-
-func TestManager_GetState_ThreadSafe(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- SessionID: "1",
- Locked: false,
- Active: true,
- },
- stateMutex: sync.RWMutex{},
- }
-
- done := make(chan bool)
- for i := 0; i < 10; i++ {
- go func() {
- state := manager.GetState()
- assert.Equal(t, "1", state.SessionID)
- assert.True(t, state.Active)
- done <- true
- }()
- }
-
- for i := 0; i < 10; i++ {
- select {
- case <-done:
- case <-time.After(1 * time.Second):
- t.Fatal("timeout waiting for goroutines")
- }
- }
-}
-
-func TestStateChangedMeaningfully(t *testing.T) {
- tests := []struct {
- name string
- old *SessionState
- new *SessionState
- expected bool
- }{
- {
- name: "no change",
- old: &SessionState{Locked: false, Active: true, IdleHint: false},
- new: &SessionState{Locked: false, Active: true, IdleHint: false},
- expected: false,
- },
- {
- name: "locked changed",
- old: &SessionState{Locked: false, Active: true, IdleHint: false},
- new: &SessionState{Locked: true, Active: true, IdleHint: false},
- expected: true,
- },
- {
- name: "active changed",
- old: &SessionState{Locked: false, Active: true, IdleHint: false},
- new: &SessionState{Locked: false, Active: false, IdleHint: false},
- expected: true,
- },
- {
- name: "idle hint changed",
- old: &SessionState{Locked: false, Active: true, IdleHint: false},
- new: &SessionState{Locked: false, Active: true, IdleHint: true},
- expected: true,
- },
- {
- name: "locked hint changed",
- old: &SessionState{Locked: false, Active: true, LockedHint: false},
- new: &SessionState{Locked: false, Active: true, LockedHint: true},
- expected: true,
- },
- {
- name: "preparing for sleep changed",
- old: &SessionState{Locked: false, Active: true, PreparingForSleep: false},
- new: &SessionState{Locked: false, Active: true, PreparingForSleep: true},
- expected: true,
- },
- {
- name: "non-meaningful change (username)",
- old: &SessionState{Locked: false, Active: true, UserName: "user1"},
- new: &SessionState{Locked: false, Active: true, UserName: "user2"},
- expected: false,
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := stateChangedMeaningfully(tt.old, tt.new)
- assert.Equal(t, tt.expected, result)
- })
- }
-}
-
-func TestManager_SnapshotState(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- SessionID: "1",
- Locked: false,
- Active: true,
- UserName: "testuser",
- },
- stateMutex: sync.RWMutex{},
- }
-
- snapshot := manager.snapshotState()
- assert.Equal(t, "1", snapshot.SessionID)
- assert.False(t, snapshot.Locked)
- assert.True(t, snapshot.Active)
- assert.Equal(t, "testuser", snapshot.UserName)
-
- snapshot.Locked = true
- assert.False(t, manager.state.Locked)
-}
-
-func TestNewManager(t *testing.T) {
- t.Run("attempts to create manager", func(t *testing.T) {
- manager, err := NewManager()
- if err != nil {
- assert.Nil(t, manager)
- } else {
- assert.NotNil(t, manager)
- assert.NotNil(t, manager.state)
- assert.NotNil(t, &manager.subscribers)
- assert.NotNil(t, manager.stopChan)
-
- manager.Close()
- }
- })
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor.go
deleted file mode 100644
index 4c0605a..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor.go
+++ /dev/null
@@ -1,155 +0,0 @@
-package loginctl
-
-import (
- "time"
-
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil"
- "github.com/godbus/dbus/v5"
-)
-
-func (m *Manager) handleDBusSignal(sig *dbus.Signal) {
- switch sig.Name {
- case dbusSessionInterface + ".Lock":
- m.stateMutex.Lock()
- m.state.Locked = true
- m.state.LockedHint = true
- m.stateMutex.Unlock()
- m.notifySubscribers()
-
- if m.sleepInhibitorEnabled.Load() && m.inSleepCycle.Load() {
- id := m.sleepCycleID.Load()
- m.lockTimerMu.Lock()
- if m.lockTimer != nil {
- m.lockTimer.Stop()
- }
- m.lockTimer = time.AfterFunc(m.fallbackDelay, func() {
- m.releaseForCycle(id)
- })
- m.lockTimerMu.Unlock()
- }
-
- case dbusSessionInterface + ".Unlock":
- m.stateMutex.Lock()
- m.state.Locked = false
- m.state.LockedHint = false
- m.stateMutex.Unlock()
- m.notifySubscribers()
-
- // Cancel the lock timer if it's still running
- m.lockTimerMu.Lock()
- if m.lockTimer != nil {
- m.lockTimer.Stop()
- m.lockTimer = nil
- }
- m.lockTimerMu.Unlock()
-
- // Re-acquire the sleep inhibitor (acquireSleepInhibitor checks the enabled flag)
- m.acquireSleepInhibitor()
-
- case dbusManagerInterface + ".PrepareForSleep":
- if len(sig.Body) == 0 {
- return
- }
- preparing, _ := sig.Body[0].(bool)
-
- if preparing {
- cycleID := m.sleepCycleID.Add(1)
- m.inSleepCycle.Store(true)
-
- if m.lockBeforeSuspend.Load() {
- m.Lock()
- }
-
- readyCh := m.newLockerReadyCh()
- go func(id uint64, ch <-chan struct{}) {
- <-ch
- if m.inSleepCycle.Load() && m.sleepCycleID.Load() == id {
- m.releaseSleepInhibitor()
- }
- }(cycleID, readyCh)
- } else {
- m.inSleepCycle.Store(false)
- m.signalLockerReady()
- m.refreshSessionBinding()
- m.acquireSleepInhibitor()
- }
-
- m.stateMutex.Lock()
- m.state.PreparingForSleep = preparing
- m.stateMutex.Unlock()
- m.notifySubscribers()
-
- case dbusPropsInterface + ".PropertiesChanged":
- m.handlePropertiesChanged(sig)
-
- case "org.freedesktop.DBus.NameOwnerChanged":
- if len(sig.Body) == 3 {
- name, _ := sig.Body[0].(string)
- oldOwner, _ := sig.Body[1].(string)
- newOwner, _ := sig.Body[2].(string)
- if name == dbusDest && oldOwner != "" && newOwner != "" {
- m.updateSessionState()
- if !m.inSleepCycle.Load() {
- m.acquireSleepInhibitor()
- }
- m.notifySubscribers()
- }
- }
- }
-}
-
-func (m *Manager) handlePropertiesChanged(sig *dbus.Signal) {
- if len(sig.Body) < 2 {
- return
- }
-
- iface, ok := sig.Body[0].(string)
- if !ok || iface != dbusSessionInterface {
- return
- }
-
- changes, ok := sig.Body[1].(map[string]dbus.Variant)
- if !ok {
- return
- }
-
- var needsUpdate bool
-
- for key, variant := range changes {
- switch key {
- case "Active":
- if val, ok := dbusutil.As[bool](variant); ok {
- m.stateMutex.Lock()
- m.state.Active = val
- m.stateMutex.Unlock()
- needsUpdate = true
- }
- case "IdleHint":
- if val, ok := dbusutil.As[bool](variant); ok {
- m.stateMutex.Lock()
- m.state.IdleHint = val
- m.stateMutex.Unlock()
- needsUpdate = true
- }
- case "IdleSinceHint":
- if val, ok := dbusutil.As[uint64](variant); ok {
- m.stateMutex.Lock()
- m.state.IdleSinceHint = val
- m.stateMutex.Unlock()
- needsUpdate = true
- }
- case "LockedHint":
- if val, ok := dbusutil.As[bool](variant); ok {
- m.stateMutex.Lock()
- m.state.LockedHint = val
- m.state.Locked = val
- m.stateMutex.Unlock()
- needsUpdate = true
- }
- }
- }
-
- if needsUpdate {
- m.notifySubscribers()
- }
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor_test.go
deleted file mode 100644
index ecbb57f..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/monitor_test.go
+++ /dev/null
@@ -1,298 +0,0 @@
-package loginctl
-
-import (
- "sync"
- "testing"
-
- "github.com/godbus/dbus/v5"
- "github.com/stretchr/testify/assert"
-)
-
-func TestManager_HandleDBusSignal_Lock(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- Locked: false,
- LockedHint: false,
- },
- stateMutex: sync.RWMutex{},
- dirty: make(chan struct{}, 1),
- }
-
- sig := &dbus.Signal{
- Name: "org.freedesktop.login1.Session.Lock",
- }
-
- manager.handleDBusSignal(sig)
-
- manager.stateMutex.RLock()
- defer manager.stateMutex.RUnlock()
- assert.True(t, manager.state.Locked)
- assert.True(t, manager.state.LockedHint)
-}
-
-func TestManager_HandleDBusSignal_Unlock(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- Locked: true,
- LockedHint: true,
- },
- stateMutex: sync.RWMutex{},
- dirty: make(chan struct{}, 1),
- }
-
- sig := &dbus.Signal{
- Name: "org.freedesktop.login1.Session.Unlock",
- }
-
- manager.handleDBusSignal(sig)
-
- manager.stateMutex.RLock()
- defer manager.stateMutex.RUnlock()
- assert.False(t, manager.state.Locked)
- assert.False(t, manager.state.LockedHint)
-}
-
-func TestManager_HandleDBusSignal_PrepareForSleep(t *testing.T) {
- t.Run("preparing for sleep - true", func(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- PreparingForSleep: false,
- },
- stateMutex: sync.RWMutex{},
- dirty: make(chan struct{}, 1),
- }
-
- sig := &dbus.Signal{
- Name: "org.freedesktop.login1.Manager.PrepareForSleep",
- Body: []any{true},
- }
-
- manager.handleDBusSignal(sig)
-
- manager.stateMutex.RLock()
- defer manager.stateMutex.RUnlock()
- assert.True(t, manager.state.PreparingForSleep)
- })
-
- t.Run("preparing for sleep - false", func(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- PreparingForSleep: true,
- },
- stateMutex: sync.RWMutex{},
- dirty: make(chan struct{}, 1),
- }
-
- sig := &dbus.Signal{
- Name: "org.freedesktop.login1.Manager.PrepareForSleep",
- Body: []any{false},
- }
-
- manager.handleDBusSignal(sig)
-
- manager.stateMutex.RLock()
- defer manager.stateMutex.RUnlock()
- assert.False(t, manager.state.PreparingForSleep)
- })
-
- t.Run("empty body", func(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- PreparingForSleep: false,
- },
- stateMutex: sync.RWMutex{},
- dirty: make(chan struct{}, 1),
- }
-
- sig := &dbus.Signal{
- Name: "org.freedesktop.login1.Manager.PrepareForSleep",
- Body: []any{},
- }
-
- manager.handleDBusSignal(sig)
-
- manager.stateMutex.RLock()
- defer manager.stateMutex.RUnlock()
- assert.False(t, manager.state.PreparingForSleep)
- })
-}
-
-func TestManager_HandlePropertiesChanged(t *testing.T) {
- t.Run("active property changed", func(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- Active: false,
- },
- stateMutex: sync.RWMutex{},
- dirty: make(chan struct{}, 1),
- }
-
- sig := &dbus.Signal{
- Name: "org.freedesktop.DBus.Properties.PropertiesChanged",
- Body: []any{
- "org.freedesktop.login1.Session",
- map[string]dbus.Variant{
- "Active": dbus.MakeVariant(true),
- },
- },
- }
-
- manager.handlePropertiesChanged(sig)
-
- manager.stateMutex.RLock()
- defer manager.stateMutex.RUnlock()
- assert.True(t, manager.state.Active)
- })
-
- t.Run("idle hint property changed", func(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- IdleHint: false,
- },
- stateMutex: sync.RWMutex{},
- dirty: make(chan struct{}, 1),
- }
-
- sig := &dbus.Signal{
- Name: "org.freedesktop.DBus.Properties.PropertiesChanged",
- Body: []any{
- "org.freedesktop.login1.Session",
- map[string]dbus.Variant{
- "IdleHint": dbus.MakeVariant(true),
- },
- },
- }
-
- manager.handlePropertiesChanged(sig)
-
- manager.stateMutex.RLock()
- defer manager.stateMutex.RUnlock()
- assert.True(t, manager.state.IdleHint)
- })
-
- t.Run("idle since hint property changed", func(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- IdleSinceHint: 0,
- },
- stateMutex: sync.RWMutex{},
- dirty: make(chan struct{}, 1),
- }
-
- sig := &dbus.Signal{
- Name: "org.freedesktop.DBus.Properties.PropertiesChanged",
- Body: []any{
- "org.freedesktop.login1.Session",
- map[string]dbus.Variant{
- "IdleSinceHint": dbus.MakeVariant(uint64(123456789)),
- },
- },
- }
-
- manager.handlePropertiesChanged(sig)
-
- manager.stateMutex.RLock()
- defer manager.stateMutex.RUnlock()
- assert.Equal(t, uint64(123456789), manager.state.IdleSinceHint)
- })
-
- t.Run("locked hint property changed", func(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- LockedHint: false,
- Locked: false,
- },
- stateMutex: sync.RWMutex{},
- dirty: make(chan struct{}, 1),
- }
-
- sig := &dbus.Signal{
- Name: "org.freedesktop.DBus.Properties.PropertiesChanged",
- Body: []any{
- "org.freedesktop.login1.Session",
- map[string]dbus.Variant{
- "LockedHint": dbus.MakeVariant(true),
- },
- },
- }
-
- manager.handlePropertiesChanged(sig)
-
- manager.stateMutex.RLock()
- defer manager.stateMutex.RUnlock()
- assert.True(t, manager.state.LockedHint)
- assert.True(t, manager.state.Locked)
- })
-
- t.Run("wrong interface", func(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- Active: false,
- },
- stateMutex: sync.RWMutex{},
- dirty: make(chan struct{}, 1),
- }
-
- sig := &dbus.Signal{
- Name: "org.freedesktop.DBus.Properties.PropertiesChanged",
- Body: []any{
- "org.freedesktop.SomeOtherInterface",
- map[string]dbus.Variant{
- "Active": dbus.MakeVariant(true),
- },
- },
- }
-
- manager.handlePropertiesChanged(sig)
-
- manager.stateMutex.RLock()
- defer manager.stateMutex.RUnlock()
- assert.False(t, manager.state.Active)
- })
-
- t.Run("empty body", func(t *testing.T) {
- manager := &Manager{
- state: &SessionState{},
- stateMutex: sync.RWMutex{},
- dirty: make(chan struct{}, 1),
- }
-
- sig := &dbus.Signal{
- Name: "org.freedesktop.DBus.Properties.PropertiesChanged",
- Body: []any{},
- }
-
- assert.NotPanics(t, func() {
- manager.handlePropertiesChanged(sig)
- })
- })
-
- t.Run("multiple properties changed", func(t *testing.T) {
- manager := &Manager{
- state: &SessionState{
- Active: false,
- IdleHint: false,
- },
- stateMutex: sync.RWMutex{},
- dirty: make(chan struct{}, 1),
- }
-
- sig := &dbus.Signal{
- Name: "org.freedesktop.DBus.Properties.PropertiesChanged",
- Body: []any{
- "org.freedesktop.login1.Session",
- map[string]dbus.Variant{
- "Active": dbus.MakeVariant(true),
- "IdleHint": dbus.MakeVariant(true),
- },
- },
- }
-
- manager.handlePropertiesChanged(sig)
-
- manager.stateMutex.RLock()
- defer manager.stateMutex.RUnlock()
- assert.True(t, manager.state.Active)
- assert.True(t, manager.state.IdleHint)
- })
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types.go
deleted file mode 100644
index 76c7414..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package loginctl
-
-import (
- "os"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
- "github.com/godbus/dbus/v5"
-)
-
-type SessionState struct {
- SessionID string `json:"sessionId"`
- SessionPath string `json:"sessionPath"`
- Locked bool `json:"locked"`
- Active bool `json:"active"`
- IdleHint bool `json:"idleHint"`
- IdleSinceHint uint64 `json:"idleSinceHint"`
- LockedHint bool `json:"lockedHint"`
- SessionType string `json:"sessionType"`
- SessionClass string `json:"sessionClass"`
- User uint32 `json:"user"`
- UserName string `json:"userName"`
- RemoteHost string `json:"remoteHost"`
- Service string `json:"service"`
- TTY string `json:"tty"`
- Display string `json:"display"`
- Remote bool `json:"remote"`
- Seat string `json:"seat"`
- VTNr uint32 `json:"vtnr"`
- PreparingForSleep bool `json:"preparingForSleep"`
-}
-
-type EventType string
-
-const (
- EventStateChanged EventType = "state_changed"
- EventLock EventType = "lock"
- EventUnlock EventType = "unlock"
- EventPrepareForSleep EventType = "prepare_for_sleep"
- EventIdleHintChanged EventType = "idle_hint_changed"
- EventLockedHintChanged EventType = "locked_hint_changed"
-)
-
-type SessionEvent struct {
- Type EventType `json:"type"`
- Data SessionState `json:"data"`
-}
-
-type Manager struct {
- state *SessionState
- stateMutex sync.RWMutex
- subscribers syncmap.Map[string, chan SessionState]
- stopChan chan struct{}
- conn *dbus.Conn
- sessionPath dbus.ObjectPath
- managerObj dbus.BusObject
- sessionObj dbus.BusObject
- dirty chan struct{}
- notifierWg sync.WaitGroup
- lastNotifiedState *SessionState
- signals chan *dbus.Signal
- sigWG sync.WaitGroup
- inhibitMu sync.Mutex
- inhibitFile *os.File
- lockBeforeSuspend atomic.Bool
- inSleepCycle atomic.Bool
- sleepCycleID atomic.Uint64
- lockerReadyChMu sync.Mutex
- lockerReadyCh chan struct{}
- lockTimerMu sync.Mutex
- lockTimer *time.Timer
- sleepInhibitorEnabled atomic.Bool
- fallbackDelay time.Duration
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types_test.go
deleted file mode 100644
index 383300f..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/loginctl/types_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package loginctl
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestEventType_Constants(t *testing.T) {
- assert.Equal(t, EventType("state_changed"), EventStateChanged)
- assert.Equal(t, EventType("lock"), EventLock)
- assert.Equal(t, EventType("unlock"), EventUnlock)
- assert.Equal(t, EventType("prepare_for_sleep"), EventPrepareForSleep)
- assert.Equal(t, EventType("idle_hint_changed"), EventIdleHintChanged)
- assert.Equal(t, EventType("locked_hint_changed"), EventLockedHintChanged)
-}
-
-func TestSessionState_Struct(t *testing.T) {
- state := SessionState{
- SessionID: "1",
- Locked: false,
- Active: true,
- SessionType: "wayland",
- User: 1000,
- UserName: "testuser",
- }
-
- assert.Equal(t, "1", state.SessionID)
- assert.True(t, state.Active)
- assert.False(t, state.Locked)
- assert.Equal(t, "wayland", state.SessionType)
- assert.Equal(t, uint32(1000), state.User)
- assert.Equal(t, "testuser", state.UserName)
-}
-
-func TestSessionEvent_Struct(t *testing.T) {
- state := SessionState{
- SessionID: "1",
- Locked: true,
- }
-
- event := SessionEvent{
- Type: EventLock,
- Data: state,
- }
-
- assert.Equal(t, EventLock, event.Type)
- assert.Equal(t, "1", event.Data.SessionID)
- assert.True(t, event.Data.Locked)
-}