From a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5 Mon Sep 17 00:00:00 2001 From: Nippy Date: Sat, 9 May 2026 13:33:09 +0200 Subject: raveos update --- .../core/internal/server/freedesktop/actions.go | 112 ---- .../internal/server/freedesktop/actions_test.go | 145 ----- .../core/internal/server/freedesktop/constants.go | 23 - .../core/internal/server/freedesktop/handlers.go | 154 ------ .../internal/server/freedesktop/handlers_test.go | 581 --------------------- .../core/internal/server/freedesktop/manager.go | 206 -------- .../internal/server/freedesktop/manager_test.go | 143 ----- .../internal/server/freedesktop/screensaver.go | 349 ------------- .../server/freedesktop/screensaver_test.go | 102 ---- .../core/internal/server/freedesktop/types.go | 66 --- .../core/internal/server/freedesktop/types_test.go | 70 --- 11 files changed, 1951 deletions(-) delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/constants.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver_test.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types_test.go (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop') diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions.go deleted file mode 100644 index 52515d1..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions.go +++ /dev/null @@ -1,112 +0,0 @@ -package freedesktop - -import ( - "fmt" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/utils" - "github.com/godbus/dbus/v5" -) - -func (m *Manager) SetIconFile(iconPath string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetIconFile", 0, iconPath).Err - if err != nil { - return fmt.Errorf("failed to set icon file: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) SetRealName(name string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetRealName", 0, name).Err - if err != nil { - return fmt.Errorf("failed to set real name: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) SetEmail(email string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetEmail", 0, email).Err - if err != nil { - return fmt.Errorf("failed to set email: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) SetLanguage(language string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetLanguage", 0, language).Err - if err != nil { - return fmt.Errorf("failed to set language: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) SetLocation(location string) error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - err := m.accountsObj.Call(dbusAccountsUserInterface+".SetLocation", 0, location).Err - if err != nil { - return fmt.Errorf("failed to set location: %w", err) - } - - m.updateAccountsState() - return nil -} - -func (m *Manager) GetUserIconFile(username string) (string, error) { - if m.systemConn == nil { - return "", fmt.Errorf("accounts service not available") - } - - accountsManager := m.systemConn.Object(dbusAccountsDest, dbus.ObjectPath(dbusAccountsPath)) - - var userPath dbus.ObjectPath - err := accountsManager.Call(dbusAccountsInterface+".FindUserByName", 0, username).Store(&userPath) - if err != nil { - return "", fmt.Errorf("user not found: %w", err) - } - - userObj := m.systemConn.Object(dbusAccountsDest, userPath) - variant, err := userObj.GetProperty(dbusAccountsUserInterface + ".IconFile") - if err != nil { - return "", err - } - - var iconFile string - if err := variant.Store(&iconFile); err != nil { - return "", err - } - - return iconFile, nil -} - -func (m *Manager) SetIconTheme(iconTheme string) error { - if err := utils.GsettingsSet("org.gnome.desktop.interface", "icon-theme", iconTheme); err != nil { - return fmt.Errorf("failed to set icon theme: %w", err) - } - return nil -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions_test.go deleted file mode 100644 index e2c21e8..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/actions_test.go +++ /dev/null @@ -1,145 +0,0 @@ -package freedesktop - -import ( - "sync" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestManager_SetIconFile(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetIconFile("/path/to/icon.png") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_SetRealName(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetRealName("New Name") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_SetEmail(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetEmail("test@example.com") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_SetLanguage(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetLanguage("en_US.UTF-8") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_SetLocation(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.SetLocation("Test Location") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_GetUserIconFile(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - iconFile, err := manager.GetUserIconFile("testuser") - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - assert.Empty(t, iconFile) - }) -} - -func TestManager_UpdateAccountsState(t *testing.T) { - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.updateAccountsState() - assert.Error(t, err) - assert.Contains(t, err.Error(), "accounts service not available") - }) -} - -func TestManager_UpdateSettingsState(t *testing.T) { - t.Run("settings not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Settings: SettingsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - err := manager.updateSettingsState() - assert.Error(t, err) - assert.Contains(t, err.Error(), "settings portal not available") - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/constants.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/constants.go deleted file mode 100644 index 12db87b..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/constants.go +++ /dev/null @@ -1,23 +0,0 @@ -package freedesktop - -const ( - dbusAccountsDest = "org.freedesktop.Accounts" - dbusAccountsPath = "/org/freedesktop/Accounts" - dbusAccountsInterface = "org.freedesktop.Accounts" - dbusAccountsUserInterface = "org.freedesktop.Accounts.User" - - dbusPortalDest = "org.freedesktop.portal.Desktop" - dbusPortalPath = "/org/freedesktop/portal/desktop" - dbusPortalSettingsInterface = "org.freedesktop.portal.Settings" - - dbusPropsInterface = "org.freedesktop.DBus.Properties" - - dbusScreensaverName = "org.freedesktop.ScreenSaver" - dbusScreensaverPath = "/ScreenSaver" - dbusScreensaverPath2 = "/org/freedesktop/ScreenSaver" - dbusScreensaverInterface = "org.freedesktop.ScreenSaver" - - dbusGnomeScreensaverName = "org.gnome.ScreenSaver" - dbusGnomeScreensaverPath = "/org/gnome/ScreenSaver" - dbusGnomeScreensaverInterface = "org.gnome.ScreenSaver" -) diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers.go deleted file mode 100644 index c3090fa..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers.go +++ /dev/null @@ -1,154 +0,0 @@ -package freedesktop - -import ( - "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 "freedesktop.getState": - handleGetState(conn, req, manager) - case "freedesktop.accounts.setIconFile": - handleSetIconFile(conn, req, manager) - case "freedesktop.accounts.setRealName": - handleSetRealName(conn, req, manager) - case "freedesktop.accounts.setEmail": - handleSetEmail(conn, req, manager) - case "freedesktop.accounts.setLanguage": - handleSetLanguage(conn, req, manager) - case "freedesktop.accounts.setLocation": - handleSetLocation(conn, req, manager) - case "freedesktop.accounts.getUserIconFile": - handleGetUserIconFile(conn, req, manager) - case "freedesktop.settings.getColorScheme": - handleGetColorScheme(conn, req, manager) - case "freedesktop.settings.setIconTheme": - handleSetIconTheme(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 handleSetIconFile(conn net.Conn, req models.Request, manager *Manager) { - iconPath, err := params.String(req.Params, "path") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetIconFile(iconPath); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "icon file set"}) -} - -func handleSetRealName(conn net.Conn, req models.Request, manager *Manager) { - name, err := params.String(req.Params, "name") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetRealName(name); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "real name set"}) -} - -func handleSetEmail(conn net.Conn, req models.Request, manager *Manager) { - email, err := params.String(req.Params, "email") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetEmail(email); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "email set"}) -} - -func handleSetLanguage(conn net.Conn, req models.Request, manager *Manager) { - language, err := params.String(req.Params, "language") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetLanguage(language); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "language set"}) -} - -func handleSetLocation(conn net.Conn, req models.Request, manager *Manager) { - location, err := params.String(req.Params, "location") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetLocation(location); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "location set"}) -} - -func handleGetUserIconFile(conn net.Conn, req models.Request, manager *Manager) { - username, err := params.String(req.Params, "username") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - iconFile, err := manager.GetUserIconFile(username) - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Value: iconFile}) -} - -func handleGetColorScheme(conn net.Conn, req models.Request, manager *Manager) { - if err := manager.updateSettingsState(); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - state := manager.GetState() - models.Respond(conn, req.ID, map[string]uint32{"colorScheme": state.Settings.ColorScheme}) -} - -func handleSetIconTheme(conn net.Conn, req models.Request, manager *Manager) { - iconTheme, err := params.String(req.Params, "iconTheme") - if err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - if err := manager.SetIconTheme(iconTheme); err != nil { - models.RespondError(conn, req.ID, err.Error()) - return - } - - models.Respond(conn, req.ID, models.SuccessResult{Success: true, Message: "icon theme set"}) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers_test.go deleted file mode 100644 index f2cb5a2..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/handlers_test.go +++ /dev/null @@ -1,581 +0,0 @@ -package freedesktop - -import ( - "bytes" - "encoding/json" - "net" - "sync" - "testing" - - 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 mockGetAllAccountsProperties() *dbus.Call { - props := map[string]dbus.Variant{ - "IconFile": dbus.MakeVariant("/path/to/icon.png"), - "RealName": dbus.MakeVariant("Test"), - "UserName": dbus.MakeVariant("test"), - "AccountType": dbus.MakeVariant(int32(0)), - "HomeDirectory": dbus.MakeVariant("/home/test"), - "Shell": dbus.MakeVariant("/bin/bash"), - "Email": dbus.MakeVariant(""), - "Language": dbus.MakeVariant(""), - "Location": dbus.MakeVariant(""), - "Locked": dbus.MakeVariant(false), - "PasswordMode": dbus.MakeVariant(int32(1)), - } - return &dbus.Call{Err: nil, Body: []any{props}} -} - -func TestRespondError_Freedesktop(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_Freedesktop(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: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - RealName: "Test User", - UID: 1000, - }, - Settings: SettingsState{ - Available: true, - ColorScheme: 1, - }, - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "freedesktop.getState"} - - handleGetState(conn, req, manager) - - var resp models.Response[FreedeskState] - 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.Accounts.Available) - assert.Equal(t, "testuser", resp.Result.Accounts.UserName) - assert.True(t, resp.Result.Settings.Available) - assert.Equal(t, uint32(1), resp.Result.Settings.ColorScheme) -} - -func TestHandleSetIconFile(t *testing.T) { - t.Run("missing path parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setIconFile", - Params: map[string]any{}, - } - - handleSetIconFile(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 'path' parameter") - }) - - t.Run("successful set icon file", func(t *testing.T) { - mockAccountsObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockAccountsObj.EXPECT().Call("org.freedesktop.Accounts.User.SetIconFile", dbus.Flags(0), "/path/to/icon.png").Return(mockCall) - mockAccountsObj.EXPECT().CallWithContext(mock.Anything, "org.freedesktop.DBus.Properties.GetAll", dbus.Flags(0), "org.freedesktop.Accounts.User").Return(mockGetAllAccountsProperties()) - - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - }, - }, - stateMutex: sync.RWMutex{}, - accountsObj: mockAccountsObj, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setIconFile", - Params: map[string]any{ - "path": "/path/to/icon.png", - }, - } - - handleSetIconFile(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, "icon file set", resp.Result.Message) - }) - - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setIconFile", - Params: map[string]any{ - "path": "/path/to/icon.png", - }, - } - - handleSetIconFile(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, "accounts service not available") - }) -} - -func TestHandleSetRealName(t *testing.T) { - t.Run("missing name parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setRealName", - Params: map[string]any{}, - } - - handleSetRealName(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 'name' parameter") - }) - - t.Run("successful set real name", func(t *testing.T) { - mockAccountsObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockAccountsObj.EXPECT().Call("org.freedesktop.Accounts.User.SetRealName", dbus.Flags(0), "New Name").Return(mockCall) - mockAccountsObj.EXPECT().CallWithContext(mock.Anything, "org.freedesktop.DBus.Properties.GetAll", dbus.Flags(0), "org.freedesktop.Accounts.User").Return(mockGetAllAccountsProperties()) - - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - }, - }, - stateMutex: sync.RWMutex{}, - accountsObj: mockAccountsObj, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setRealName", - Params: map[string]any{ - "name": "New Name", - }, - } - - handleSetRealName(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, "real name set", resp.Result.Message) - }) -} - -func TestHandleSetEmail(t *testing.T) { - t.Run("missing email parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setEmail", - Params: map[string]any{}, - } - - handleSetEmail(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 'email' parameter") - }) - - t.Run("successful set email", func(t *testing.T) { - mockAccountsObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{Err: nil} - mockAccountsObj.EXPECT().Call("org.freedesktop.Accounts.User.SetEmail", dbus.Flags(0), "test@example.com").Return(mockCall) - mockAccountsObj.EXPECT().CallWithContext(mock.Anything, "org.freedesktop.DBus.Properties.GetAll", dbus.Flags(0), "org.freedesktop.Accounts.User").Return(mockGetAllAccountsProperties()) - - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - }, - }, - stateMutex: sync.RWMutex{}, - accountsObj: mockAccountsObj, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setEmail", - Params: map[string]any{ - "email": "test@example.com", - }, - } - - handleSetEmail(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, "email set", resp.Result.Message) - }) -} - -func TestHandleSetLanguage(t *testing.T) { - t.Run("missing language parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setLanguage", - Params: map[string]any{}, - } - - handleSetLanguage(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 'language' parameter") - }) -} - -func TestHandleSetLocation(t *testing.T) { - t.Run("missing location parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.setLocation", - Params: map[string]any{}, - } - - handleSetLocation(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 'location' parameter") - }) -} - -func TestHandleGetUserIconFile(t *testing.T) { - t.Run("missing username parameter", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.getUserIconFile", - Params: map[string]any{}, - } - - handleGetUserIconFile(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 'username' parameter") - }) - - t.Run("accounts not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.accounts.getUserIconFile", - Params: map[string]any{ - "username": "testuser", - }, - } - - handleGetUserIconFile(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, "accounts service not available") - }) -} - -func TestHandleGetColorScheme(t *testing.T) { - t.Run("settings not available", func(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Settings: SettingsState{ - Available: false, - }, - }, - stateMutex: sync.RWMutex{}, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "freedesktop.settings.getColorScheme"} - - handleGetColorScheme(conn, req, manager) - - var resp models.Response[map[string]uint32] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Contains(t, resp.Error, "settings portal not available") - }) - - t.Run("successful get color scheme", func(t *testing.T) { - mockSettingsObj := mockdbus.NewMockBusObject(t) - mockCall := &dbus.Call{ - Err: nil, - Body: []any{dbus.MakeVariant(uint32(1))}, - } - mockSettingsObj.EXPECT().Call("org.freedesktop.portal.Settings.ReadOne", dbus.Flags(0), "org.freedesktop.appearance", "color-scheme").Return(mockCall) - - manager := &Manager{ - state: &FreedeskState{ - Settings: SettingsState{ - Available: true, - }, - }, - stateMutex: sync.RWMutex{}, - settingsObj: mockSettingsObj, - } - - conn := newMockNetConn() - req := models.Request{ID: 123, Method: "freedesktop.settings.getColorScheme"} - - handleGetColorScheme(conn, req, manager) - - var resp models.Response[map[string]uint32] - 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, uint32(1), (*resp.Result)["colorScheme"]) - }) -} - -func TestHandleRequest(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - }, - }, - stateMutex: sync.RWMutex{}, - } - - t.Run("unknown method", func(t *testing.T) { - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: "freedesktop.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: "freedesktop.getState", - } - - HandleRequest(conn, req, manager) - - var resp models.Response[FreedeskState] - err := json.NewDecoder(conn.writeBuf).Decode(&resp) - require.NoError(t, err) - - assert.Equal(t, 123, resp.ID) - assert.Empty(t, resp.Error) - }) - - t.Run("all method routes", func(t *testing.T) { - tests := []string{ - "freedesktop.accounts.setIconFile", - "freedesktop.accounts.setRealName", - "freedesktop.accounts.setEmail", - "freedesktop.accounts.setLanguage", - "freedesktop.accounts.setLocation", - "freedesktop.accounts.getUserIconFile", - "freedesktop.settings.getColorScheme", - } - - for _, method := range tests { - conn := newMockNetConn() - req := models.Request{ - ID: 123, - Method: method, - Params: map[string]any{}, - } - - 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) - // Will have errors due to missing params or service unavailable - // but the method routing should work - } - }) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager.go deleted file mode 100644 index 054f859..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager.go +++ /dev/null @@ -1,206 +0,0 @@ -package freedesktop - -import ( - "context" - "fmt" - "os" - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil" - "github.com/godbus/dbus/v5" -) - -func NewManager() (*Manager, error) { - systemConn, err := dbus.ConnectSystemBus() - if err != nil { - return nil, fmt.Errorf("failed to connect to system bus: %w", err) - } - - sessionConn, err := dbus.ConnectSessionBus() - if err != nil { - sessionConn = nil - } - - m := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{}, - Settings: SettingsState{}, - Screensaver: ScreensaverState{}, - }, - stateMutex: sync.RWMutex{}, - systemConn: systemConn, - sessionConn: sessionConn, - currentUID: uint64(os.Getuid()), - } - - m.initializeAccounts() - m.initializeSettings() - m.initializeScreensaver() - - return m, nil -} - -func (m *Manager) initializeAccounts() error { - accountsManager := m.systemConn.Object(dbusAccountsDest, dbus.ObjectPath(dbusAccountsPath)) - - var userPath dbus.ObjectPath - err := accountsManager.Call(dbusAccountsInterface+".FindUserById", 0, int64(m.currentUID)).Store(&userPath) - if err != nil { - m.stateMutex.Lock() - m.state.Accounts.Available = false - m.stateMutex.Unlock() - return err - } - - m.accountsObj = m.systemConn.Object(dbusAccountsDest, userPath) - - m.stateMutex.Lock() - m.state.Accounts.Available = true - m.state.Accounts.UserPath = string(userPath) - m.state.Accounts.UID = m.currentUID - m.stateMutex.Unlock() - - if err := m.updateAccountsState(); err != nil { - return fmt.Errorf("failed to update accounts state: %w", err) - } - - return nil -} - -func (m *Manager) initializeSettings() error { - if m.sessionConn == nil { - m.stateMutex.Lock() - m.state.Settings.Available = false - m.stateMutex.Unlock() - return fmt.Errorf("no session bus connection") - } - - m.settingsObj = m.sessionConn.Object(dbusPortalDest, dbus.ObjectPath(dbusPortalPath)) - - var variant dbus.Variant - err := m.settingsObj.Call(dbusPortalSettingsInterface+".ReadOne", 0, "org.freedesktop.appearance", "color-scheme").Store(&variant) - if err != nil { - m.stateMutex.Lock() - m.state.Settings.Available = false - m.stateMutex.Unlock() - return err - } - - m.stateMutex.Lock() - m.state.Settings.Available = true - m.stateMutex.Unlock() - - if err := m.updateSettingsState(); err != nil { - return fmt.Errorf("failed to update settings state: %w", err) - } - - return nil -} - -func (m *Manager) updateAccountsState() error { - if !m.state.Accounts.Available || m.accountsObj == nil { - return fmt.Errorf("accounts service not available") - } - - ctx := context.Background() - props, err := m.getAccountProperties(ctx) - if err != nil { - return err - } - - m.stateMutex.Lock() - defer m.stateMutex.Unlock() - - m.state.Accounts.IconFile = dbusutil.GetOr(props, "IconFile", "") - m.state.Accounts.RealName = dbusutil.GetOr(props, "RealName", "") - m.state.Accounts.UserName = dbusutil.GetOr(props, "UserName", "") - m.state.Accounts.AccountType = dbusutil.GetOr(props, "AccountType", int32(0)) - m.state.Accounts.HomeDirectory = dbusutil.GetOr(props, "HomeDirectory", "") - m.state.Accounts.Shell = dbusutil.GetOr(props, "Shell", "") - m.state.Accounts.Email = dbusutil.GetOr(props, "Email", "") - m.state.Accounts.Language = dbusutil.GetOr(props, "Language", "") - m.state.Accounts.Location = dbusutil.GetOr(props, "Location", "") - m.state.Accounts.Locked = dbusutil.GetOr(props, "Locked", false) - m.state.Accounts.PasswordMode = dbusutil.GetOr(props, "PasswordMode", int32(0)) - - return nil -} - -func (m *Manager) updateSettingsState() error { - if !m.state.Settings.Available || m.settingsObj == nil { - return fmt.Errorf("settings portal not available") - } - - var variant dbus.Variant - err := m.settingsObj.Call(dbusPortalSettingsInterface+".ReadOne", 0, "org.freedesktop.appearance", "color-scheme").Store(&variant) - if err != nil { - return err - } - - if colorScheme, ok := dbusutil.As[uint32](variant); ok { - m.stateMutex.Lock() - m.state.Settings.ColorScheme = colorScheme - m.stateMutex.Unlock() - } - - return nil -} - -func (m *Manager) getAccountProperties(ctx context.Context) (map[string]dbus.Variant, error) { - var props map[string]dbus.Variant - err := m.accountsObj.CallWithContext(ctx, dbusPropsInterface+".GetAll", 0, dbusAccountsUserInterface).Store(&props) - if err != nil { - return nil, err - } - return props, nil -} - -func (m *Manager) GetState() FreedeskState { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - return *m.state -} - -func (m *Manager) Subscribe(id string) chan FreedeskState { - ch := make(chan FreedeskState, 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) NotifySubscribers() { - state := m.GetState() - m.subscribers.Range(func(key string, ch chan FreedeskState) bool { - select { - case ch <- state: - default: - } - return true - }) -} - -func (m *Manager) Close() { - m.subscribers.Range(func(key string, ch chan FreedeskState) bool { - close(ch) - m.subscribers.Delete(key) - return true - }) - - m.screensaverSubscribers.Range(func(key string, ch chan ScreensaverState) bool { - close(ch) - m.screensaverSubscribers.Delete(key) - return true - }) - - if m.systemConn != nil { - m.systemConn.Close() - } - if m.sessionConn != nil { - m.sessionConn.Close() - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager_test.go deleted file mode 100644 index b3617f5..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/manager_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package freedesktop - -import ( - "sync" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestManager_GetState(t *testing.T) { - state := &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - RealName: "Test User", - UID: 1000, - }, - Settings: SettingsState{ - Available: true, - ColorScheme: 1, - }, - } - - manager := &Manager{ - state: state, - stateMutex: sync.RWMutex{}, - } - - result := manager.GetState() - assert.True(t, result.Accounts.Available) - assert.Equal(t, "testuser", result.Accounts.UserName) - assert.Equal(t, "Test User", result.Accounts.RealName) - assert.Equal(t, uint64(1000), result.Accounts.UID) - assert.True(t, result.Settings.Available) - assert.Equal(t, uint32(1), result.Settings.ColorScheme) -} - -func TestManager_GetState_ThreadSafe(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - }, - Settings: SettingsState{ - Available: true, - ColorScheme: 1, - }, - }, - stateMutex: sync.RWMutex{}, - } - - done := make(chan bool) - for i := 0; i < 10; i++ { - go func() { - state := manager.GetState() - assert.True(t, state.Accounts.Available) - assert.Equal(t, "testuser", state.Accounts.UserName) - done <- true - }() - } - - for i := 0; i < 10; i++ { - <-done - } -} - -func TestManager_Close(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - systemConn: nil, - sessionConn: nil, - } - - assert.NotPanics(t, func() { - manager.Close() - }) -} - -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.systemConn) - - manager.Close() - } - }) -} - -func TestManager_GetState_EmptyState(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{}, - stateMutex: sync.RWMutex{}, - } - - result := manager.GetState() - assert.False(t, result.Accounts.Available) - assert.Empty(t, result.Accounts.UserName) - assert.False(t, result.Settings.Available) - assert.Equal(t, uint32(0), result.Settings.ColorScheme) -} - -func TestManager_AccountsState_Modification(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - }, - }, - stateMutex: sync.RWMutex{}, - } - - state := manager.GetState() - state.Accounts.UserName = "modifieduser" - - original := manager.GetState() - assert.Equal(t, "testuser", original.Accounts.UserName) -} - -func TestManager_SettingsState_Modification(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Settings: SettingsState{ - Available: true, - ColorScheme: 0, - }, - }, - stateMutex: sync.RWMutex{}, - } - - state := manager.GetState() - state.Settings.ColorScheme = 1 - - original := manager.GetState() - assert.Equal(t, uint32(0), original.Settings.ColorScheme) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver.go deleted file mode 100644 index 095a4c0..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver.go +++ /dev/null @@ -1,349 +0,0 @@ -package freedesktop - -import ( - "fmt" - "path/filepath" - "strings" - "sync/atomic" - "time" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/log" - "github.com/godbus/dbus/v5" - "github.com/godbus/dbus/v5/introspect" -) - -type screensaverHandler struct { - manager *Manager -} - -func screensaverIntrospectIface(ifaceName string) introspect.Interface { - return introspect.Interface{ - Name: ifaceName, - Methods: []introspect.Method{ - { - Name: "Inhibit", - Args: []introspect.Arg{ - {Name: "application_name", Type: "s", Direction: "in"}, - {Name: "reason_for_inhibit", Type: "s", Direction: "in"}, - {Name: "cookie", Type: "u", Direction: "out"}, - }, - }, - { - Name: "UnInhibit", - Args: []introspect.Arg{ - {Name: "cookie", Type: "u", Direction: "in"}, - }, - }, - { - Name: "GetActive", - Args: []introspect.Arg{ - {Name: "active", Type: "b", Direction: "out"}, - }, - }, - { - Name: "SetActive", - Args: []introspect.Arg{ - {Name: "active", Type: "b", Direction: "in"}, - }, - }, - { - Name: "Lock", - }, - }, - Signals: []introspect.Signal{ - { - Name: "ActiveChanged", - Args: []introspect.Arg{ - {Name: "new_value", Type: "b"}, - }, - }, - }, - } -} - -func (m *Manager) initializeScreensaver() error { - if m.sessionConn == nil { - m.stateMutex.Lock() - m.state.Screensaver.Available = false - m.stateMutex.Unlock() - return nil - } - - handler := &screensaverHandler{manager: m} - - m.screensaverFreedesktopClaimed = m.claimScreensaverName(handler, - dbusScreensaverName, dbusScreensaverInterface, dbusScreensaverPath, dbusScreensaverPath2) - m.screensaverGnomeClaimed = m.claimScreensaverName(handler, - dbusGnomeScreensaverName, dbusGnomeScreensaverInterface, dbusGnomeScreensaverPath) - - if !m.screensaverFreedesktopClaimed && !m.screensaverGnomeClaimed { - log.Warn("No screensaver interface could be claimed") - m.stateMutex.Lock() - m.state.Screensaver.Available = false - m.stateMutex.Unlock() - return nil - } - - go m.watchPeerDisconnects() - - m.stateMutex.Lock() - m.state.Screensaver.Available = true - m.state.Screensaver.Active = false - m.state.Screensaver.Inhibited = false - m.state.Screensaver.Inhibitors = []ScreensaverInhibitor{} - m.stateMutex.Unlock() - - log.Info("Screensaver listener initialized") - return nil -} - -func (m *Manager) claimScreensaverName(handler *screensaverHandler, name, iface string, paths ...dbus.ObjectPath) bool { - reply, err := m.sessionConn.RequestName(name, dbus.NameFlagDoNotQueue) - if err != nil { - log.Warnf("Failed to request screensaver name %s: %v", name, err) - return false - } - if reply != dbus.RequestNameReplyPrimaryOwner { - log.Warnf("Screensaver name %s already owned by another process", name) - return false - } - if err := m.exportScreensaverOnPaths(handler, iface, paths...); err != nil { - log.Warnf("Failed to export screensaver on %s: %v", name, err) - return false - } - log.Infof("Claimed %s on session bus", name) - return true -} - -// exportScreensaverOnPaths exports the handler and introspection on the given -// paths under the specified interface name. -func (m *Manager) exportScreensaverOnPaths(handler *screensaverHandler, ifaceName string, paths ...dbus.ObjectPath) error { - iface := screensaverIntrospectIface(ifaceName) - for _, path := range paths { - if err := m.sessionConn.Export(handler, path, ifaceName); err != nil { - return fmt.Errorf("export handler on %s: %w", path, err) - } - node := &introspect.Node{ - Name: string(path), - Interfaces: []introspect.Interface{ - introspect.IntrospectData, - iface, - }, - } - if err := m.sessionConn.Export(introspect.NewIntrospectable(node), path, "org.freedesktop.DBus.Introspectable"); err != nil { - log.Warnf("Failed to export introspectable on %s: %v", path, err) - } - } - return nil -} - -func (h *screensaverHandler) Inhibit(sender dbus.Sender, appName, reason string) (uint32, *dbus.Error) { - if appName == "" { - return 0, dbus.NewError("org.freedesktop.DBus.Error.InvalidArgs", []any{"application name required"}) - } - - if reason == "" { - return 0, dbus.NewError("org.freedesktop.DBus.Error.InvalidArgs", []any{"reason required"}) - } - - if strings.Contains(strings.ToLower(reason), "audio") && !strings.Contains(strings.ToLower(reason), "video") { - log.Debugf("Ignoring audio-only inhibit from %s: %s", appName, reason) - return 0, nil - } - - if idx := strings.LastIndex(appName, "/"); idx != -1 && idx < len(appName)-1 { - appName = appName[idx+1:] - } - appName = filepath.Base(appName) - - cookie := atomic.AddUint32(&h.manager.screensaverCookieCounter, 1) - - inhibitor := ScreensaverInhibitor{ - Cookie: cookie, - AppName: appName, - Reason: reason, - Peer: string(sender), - StartTime: time.Now().Unix(), - } - - h.manager.stateMutex.Lock() - h.manager.state.Screensaver.Inhibitors = append(h.manager.state.Screensaver.Inhibitors, inhibitor) - h.manager.state.Screensaver.Inhibited = len(h.manager.state.Screensaver.Inhibitors) > 0 - h.manager.stateMutex.Unlock() - - log.Infof("Screensaver inhibited by %s (%s): %s -> cookie %08X", appName, sender, reason, cookie) - - h.manager.NotifyScreensaverSubscribers() - - return cookie, nil -} - -func (h *screensaverHandler) UnInhibit(sender dbus.Sender, cookie uint32) *dbus.Error { - h.manager.stateMutex.Lock() - defer h.manager.stateMutex.Unlock() - - found := false - inhibitors := h.manager.state.Screensaver.Inhibitors - for i, inh := range inhibitors { - if inh.Cookie != cookie { - continue - } - log.Infof("Screensaver uninhibited by %s (%s) cookie %08X", inh.AppName, sender, cookie) - h.manager.state.Screensaver.Inhibitors = append(inhibitors[:i], inhibitors[i+1:]...) - found = true - break - } - - if !found { - log.Debugf("UnInhibit: no match for cookie %08X", cookie) - return nil - } - - h.manager.state.Screensaver.Inhibited = len(h.manager.state.Screensaver.Inhibitors) > 0 - - go h.manager.NotifyScreensaverSubscribers() - - return nil -} - -func (m *Manager) watchPeerDisconnects() { - if m.sessionConn == nil { - return - } - - if err := m.sessionConn.AddMatchSignal( - dbus.WithMatchInterface("org.freedesktop.DBus"), - dbus.WithMatchMember("NameOwnerChanged"), - ); err != nil { - log.Warnf("Failed to watch peer disconnects: %v", err) - return - } - - signals := make(chan *dbus.Signal, 64) - m.sessionConn.Signal(signals) - - for sig := range signals { - if sig.Name != "org.freedesktop.DBus.NameOwnerChanged" { - continue - } - if len(sig.Body) < 3 { - continue - } - - name, ok1 := sig.Body[0].(string) - newOwner, ok2 := sig.Body[2].(string) - if !ok1 || !ok2 { - continue - } - if newOwner != "" { - continue - } - - m.removeInhibitorsByPeer(name) - } -} - -func (m *Manager) removeInhibitorsByPeer(peer string) { - m.stateMutex.Lock() - defer m.stateMutex.Unlock() - - var remaining []ScreensaverInhibitor - var removed []ScreensaverInhibitor - for _, inh := range m.state.Screensaver.Inhibitors { - if inh.Peer == peer { - removed = append(removed, inh) - continue - } - remaining = append(remaining, inh) - } - - if len(removed) == 0 { - return - } - - for _, inh := range removed { - log.Infof("Screensaver: peer %s died, removing inhibitor from %s (cookie %08X)", peer, inh.AppName, inh.Cookie) - } - - m.state.Screensaver.Inhibitors = remaining - m.state.Screensaver.Inhibited = len(remaining) > 0 - - go m.NotifyScreensaverSubscribers() -} - -func (m *Manager) GetScreensaverState() ScreensaverState { - m.stateMutex.RLock() - defer m.stateMutex.RUnlock() - return m.state.Screensaver -} - -func (m *Manager) SubscribeScreensaver(id string) chan ScreensaverState { - ch := make(chan ScreensaverState, 64) - m.screensaverSubscribers.Store(id, ch) - return ch -} - -func (m *Manager) UnsubscribeScreensaver(id string) { - if val, ok := m.screensaverSubscribers.LoadAndDelete(id); ok { - close(val) - } -} - -func (m *Manager) NotifyScreensaverSubscribers() { - state := m.GetScreensaverState() - m.screensaverSubscribers.Range(func(key string, ch chan ScreensaverState) bool { - select { - case ch <- state: - default: - } - return true - }) -} - -func (h *screensaverHandler) GetActive() (bool, *dbus.Error) { - h.manager.stateMutex.RLock() - active := h.manager.state.Screensaver.Active - h.manager.stateMutex.RUnlock() - return active, nil -} - -func (h *screensaverHandler) SetActive(active bool) *dbus.Error { - h.manager.SetScreenLockActive(active) - return nil -} - -func (h *screensaverHandler) Lock() *dbus.Error { - h.manager.SetScreenLockActive(true) - return nil -} - -func (m *Manager) SetScreenLockActive(active bool) { - m.stateMutex.Lock() - changed := m.state.Screensaver.Active != active - m.state.Screensaver.Active = active - m.stateMutex.Unlock() - - if !changed { - return - } - - log.Infof("Screen lock active changed: %v", active) - defer m.NotifyScreensaverSubscribers() - - if m.sessionConn == nil { - return - } - if m.screensaverFreedesktopClaimed { - if err := m.sessionConn.Emit(dbusScreensaverPath, dbusScreensaverInterface+".ActiveChanged", active); err != nil { - log.Warnf("Failed to emit ActiveChanged on %s: %v", dbusScreensaverPath, err) - } - if err := m.sessionConn.Emit(dbusScreensaverPath2, dbusScreensaverInterface+".ActiveChanged", active); err != nil { - log.Warnf("Failed to emit ActiveChanged on %s: %v", dbusScreensaverPath2, err) - } - } - if m.screensaverGnomeClaimed { - if err := m.sessionConn.Emit(dbusGnomeScreensaverPath, dbusGnomeScreensaverInterface+".ActiveChanged", active); err != nil { - log.Warnf("Failed to emit ActiveChanged on %s: %v", dbusGnomeScreensaverPath, err) - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver_test.go deleted file mode 100644 index 324c17e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/screensaver_test.go +++ /dev/null @@ -1,102 +0,0 @@ -package freedesktop - -import ( - "sync" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestSetScreenLockActive_ChangesState(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Screensaver: ScreensaverState{Available: true}, - }, - stateMutex: sync.RWMutex{}, - } - - assert.False(t, manager.GetScreensaverState().Active) - - manager.SetScreenLockActive(true) - assert.True(t, manager.GetScreensaverState().Active) - - manager.SetScreenLockActive(false) - assert.False(t, manager.GetScreensaverState().Active) -} - -func TestSetScreenLockActive_NoChangeNoDuplicate(t *testing.T) { - ch := make(chan ScreensaverState, 64) - manager := &Manager{ - state: &FreedeskState{ - Screensaver: ScreensaverState{Available: true, Active: false}, - }, - stateMutex: sync.RWMutex{}, - } - manager.screensaverSubscribers.Store("test", ch) - defer manager.screensaverSubscribers.Delete("test") - - // Setting to same value should not notify - manager.SetScreenLockActive(false) - - select { - case <-ch: - t.Fatal("should not have received notification for no-change") - case <-time.After(50 * time.Millisecond): - // Expected: no notification - } -} - -func TestSetScreenLockActive_NotifiesSubscribers(t *testing.T) { - ch := make(chan ScreensaverState, 64) - manager := &Manager{ - state: &FreedeskState{ - Screensaver: ScreensaverState{Available: true, Active: false}, - }, - stateMutex: sync.RWMutex{}, - } - manager.screensaverSubscribers.Store("test", ch) - defer manager.screensaverSubscribers.Delete("test") - - manager.SetScreenLockActive(true) - - select { - case state := <-ch: - assert.True(t, state.Active) - case <-time.After(time.Second): - t.Fatal("timeout waiting for subscriber notification") - } -} - -func TestSetScreenLockActive_NilSessionConn(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Screensaver: ScreensaverState{Available: true}, - }, - stateMutex: sync.RWMutex{}, - } - - assert.NotPanics(t, func() { - manager.SetScreenLockActive(true) - }) - assert.True(t, manager.GetScreensaverState().Active) -} - -func TestGetActive_ReturnsCurrentState(t *testing.T) { - manager := &Manager{ - state: &FreedeskState{ - Screensaver: ScreensaverState{Available: true, Active: true}, - }, - stateMutex: sync.RWMutex{}, - } - - handler := &screensaverHandler{manager: manager} - active, dbusErr := handler.GetActive() - assert.Nil(t, dbusErr) - assert.True(t, active) -} - -func TestScreensaverState_ActiveDefaultsFalse(t *testing.T) { - state := ScreensaverState{} - assert.False(t, state.Active) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types.go deleted file mode 100644 index 0a9517a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types.go +++ /dev/null @@ -1,66 +0,0 @@ -package freedesktop - -import ( - "sync" - - "github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap" - "github.com/godbus/dbus/v5" -) - -type AccountsState struct { - Available bool `json:"available"` - UserPath string `json:"userPath"` - IconFile string `json:"iconFile"` - RealName string `json:"realName"` - UserName string `json:"userName"` - AccountType int32 `json:"accountType"` - HomeDirectory string `json:"homeDirectory"` - Shell string `json:"shell"` - Email string `json:"email"` - Language string `json:"language"` - Location string `json:"location"` - Locked bool `json:"locked"` - PasswordMode int32 `json:"passwordMode"` - UID uint64 `json:"uid"` -} - -type SettingsState struct { - Available bool `json:"available"` - ColorScheme uint32 `json:"colorScheme"` -} - -type ScreensaverInhibitor struct { - Cookie uint32 `json:"cookie"` - AppName string `json:"appName"` - Reason string `json:"reason"` - Peer string `json:"peer"` - StartTime int64 `json:"startTime"` -} - -type ScreensaverState struct { - Available bool `json:"available"` - Active bool `json:"active"` - Inhibited bool `json:"inhibited"` - Inhibitors []ScreensaverInhibitor `json:"inhibitors"` -} - -type FreedeskState struct { - Accounts AccountsState `json:"accounts"` - Settings SettingsState `json:"settings"` - Screensaver ScreensaverState `json:"screensaver"` -} - -type Manager struct { - state *FreedeskState - stateMutex sync.RWMutex - systemConn *dbus.Conn - sessionConn *dbus.Conn - accountsObj dbus.BusObject - settingsObj dbus.BusObject - currentUID uint64 - subscribers syncmap.Map[string, chan FreedeskState] - screensaverSubscribers syncmap.Map[string, chan ScreensaverState] - screensaverCookieCounter uint32 - screensaverFreedesktopClaimed bool - screensaverGnomeClaimed bool -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types_test.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types_test.go deleted file mode 100644 index deb0327..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/server/freedesktop/types_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package freedesktop - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestAccountsState_Struct(t *testing.T) { - state := AccountsState{ - Available: true, - UserPath: "/org/freedesktop/Accounts/User1000", - RealName: "Test User", - UserName: "testuser", - Locked: false, - UID: 1000, - } - - assert.True(t, state.Available) - assert.Equal(t, "/org/freedesktop/Accounts/User1000", state.UserPath) - assert.Equal(t, "Test User", state.RealName) - assert.Equal(t, "testuser", state.UserName) - assert.Equal(t, uint64(1000), state.UID) - assert.False(t, state.Locked) -} - -func TestSettingsState_Struct(t *testing.T) { - state := SettingsState{ - Available: true, - ColorScheme: 1, // Dark mode - } - - assert.True(t, state.Available) - assert.Equal(t, uint32(1), state.ColorScheme) -} - -func TestFreedeskState_Struct(t *testing.T) { - state := FreedeskState{ - Accounts: AccountsState{ - Available: true, - UserName: "testuser", - UID: 1000, - }, - Settings: SettingsState{ - Available: true, - ColorScheme: 0, // Light mode - }, - } - - assert.True(t, state.Accounts.Available) - assert.Equal(t, "testuser", state.Accounts.UserName) - assert.True(t, state.Settings.Available) - assert.Equal(t, uint32(0), state.Settings.ColorScheme) -} - -func TestAccountsState_DefaultValues(t *testing.T) { - state := AccountsState{} - - assert.False(t, state.Available) - assert.Empty(t, state.UserPath) - assert.Empty(t, state.UserName) - assert.Equal(t, uint64(0), state.UID) -} - -func TestSettingsState_DefaultValues(t *testing.T) { - state := SettingsState{} - - assert.False(t, state.Available) - assert.Equal(t, uint32(0), state.ColorScheme) -} -- cgit v1.3