1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-08-01 19:18:28 -04:00

portal: prevent feedback loop with the gtk4 hack to make it react to

color scheme changes
This commit is contained in:
bbedward
2026-07-04 20:42:33 -04:00
parent 30ceda97a0
commit 8a85ab06a1
6 changed files with 136 additions and 11 deletions
+14
View File
@@ -912,6 +912,18 @@ func refreshGTK(mode ColorMode) {
}
}
var colorSchemeEchoHook func(scheme string)
func SetColorSchemeEchoHook(hook func(scheme string)) {
colorSchemeEchoHook = hook
}
func expectColorSchemeEcho(scheme string) {
if colorSchemeEchoHook != nil {
colorSchemeEchoHook(scheme)
}
}
func refreshGTK4() {
output, err := utils.GsettingsGet("org.gnome.desktop.interface", "color-scheme")
if err != nil {
@@ -926,11 +938,13 @@ func refreshGTK4() {
toggle = "prefer-dark"
}
expectColorSchemeEcho(toggle)
if err := utils.GsettingsSet("org.gnome.desktop.interface", "color-scheme", toggle); err != nil {
log.Warnf("Failed to toggle color-scheme for GTK4 refresh: %v", err)
return
}
time.Sleep(50 * time.Millisecond)
expectColorSchemeEcho(current)
if err := utils.GsettingsSet("org.gnome.desktop.interface", "color-scheme", current); err != nil {
log.Warnf("Failed to restore color-scheme for GTK4 refresh: %v", err)
}
+39 -1
View File
@@ -5,6 +5,7 @@ import (
"fmt"
"os"
"sync"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil"
@@ -100,6 +101,41 @@ func (m *Manager) initializeSettings() error {
return nil
}
// ExpectColorSchemeEcho registers a self-write so the watcher swallows its SettingChanged echo instead of forwarding it as an external change.
func (m *Manager) ExpectColorSchemeEcho(scheme string) {
var value uint32
switch scheme {
case "prefer-dark":
value = 1
case "prefer-light":
value = 2
}
m.selfEchoMu.Lock()
m.selfEchoes = append(m.selfEchoes, colorSchemeEcho{value: value, expires: time.Now().Add(10 * time.Second)})
m.selfEchoMu.Unlock()
}
func (m *Manager) consumeSelfEcho(value uint32) bool {
m.selfEchoMu.Lock()
defer m.selfEchoMu.Unlock()
now := time.Now()
kept := m.selfEchoes[:0]
consumed := false
for _, echo := range m.selfEchoes {
if now.After(echo.expires) {
continue
}
if !consumed && echo.value == value {
consumed = true
continue
}
kept = append(kept, echo)
}
m.selfEchoes = kept
return consumed
}
func (m *Manager) watchSettingsChanges() {
conn, err := dbus.ConnectSessionBus()
if err != nil {
@@ -142,13 +178,15 @@ func (m *Manager) watchSettingsChanges() {
continue
}
selfInflicted := m.consumeSelfEcho(colorScheme)
m.stateMutex.Lock()
changed := m.state.Settings.ColorScheme != colorScheme || !m.state.Settings.Available
m.state.Settings.ColorScheme = colorScheme
m.state.Settings.Available = true
m.stateMutex.Unlock()
if changed {
if changed && !selfInflicted {
m.NotifySubscribers()
}
}
@@ -141,3 +141,45 @@ func TestManager_SettingsState_Modification(t *testing.T) {
original := manager.GetState()
assert.Equal(t, uint32(0), original.Settings.ColorScheme)
}
func TestManager_SelfEcho_ConsumesRegisteredWrites(t *testing.T) {
manager := &Manager{state: &FreedeskState{}}
manager.ExpectColorSchemeEcho("prefer-dark")
manager.ExpectColorSchemeEcho("default")
assert.True(t, manager.consumeSelfEcho(1))
assert.True(t, manager.consumeSelfEcho(0))
assert.False(t, manager.consumeSelfEcho(1))
assert.False(t, manager.consumeSelfEcho(0))
}
func TestManager_SelfEcho_ExternalChangePassesThrough(t *testing.T) {
manager := &Manager{state: &FreedeskState{}}
manager.ExpectColorSchemeEcho("prefer-dark")
assert.False(t, manager.consumeSelfEcho(2))
assert.True(t, manager.consumeSelfEcho(1))
}
func TestManager_SelfEcho_ConsumesOnePerRegistration(t *testing.T) {
manager := &Manager{state: &FreedeskState{}}
manager.ExpectColorSchemeEcho("prefer-dark")
manager.ExpectColorSchemeEcho("prefer-dark")
assert.True(t, manager.consumeSelfEcho(1))
assert.True(t, manager.consumeSelfEcho(1))
assert.False(t, manager.consumeSelfEcho(1))
}
func TestManager_SelfEcho_SchemeMapping(t *testing.T) {
manager := &Manager{state: &FreedeskState{}}
manager.ExpectColorSchemeEcho("prefer-light")
assert.True(t, manager.consumeSelfEcho(2))
manager.ExpectColorSchemeEcho("default")
assert.True(t, manager.consumeSelfEcho(0))
}
@@ -2,11 +2,17 @@ package freedesktop
import (
"sync"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/pkg/syncmap"
"github.com/godbus/dbus/v5"
)
type colorSchemeEcho struct {
value uint32
expires time.Time
}
type AccountsState struct {
Available bool `json:"available"`
UserPath string `json:"userPath"`
@@ -63,4 +69,6 @@ type Manager struct {
screensaverCookieCounter uint32
screensaverFreedesktopClaimed bool
screensaverGnomeClaimed bool
selfEchoMu sync.Mutex
selfEchoes []colorSchemeEcho
}
+2
View File
@@ -17,6 +17,7 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/geolocation"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/matugen"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/apppicker"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/bluez"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/brightness"
@@ -190,6 +191,7 @@ func InitializeFreedeskManager() error {
}
freedesktopManager = manager
matugen.SetColorSchemeEchoHook(manager.ExpectColorSchemeEcho)
log.Info("Freedesktop manager initialized")
return nil
+31 -10
View File
@@ -87,22 +87,43 @@ Singleton {
}
}
function evaluateColorScheme() {
function canSyncColorScheme() {
if (typeof SettingsData === "undefined" || !SettingsData.syncModeWithPortal)
return;
return false;
if (!settingsPortalAvailable)
return;
return false;
if (typeof SessionData !== "undefined" && SessionData.themeModeAutoEnabled)
return;
if (typeof Theme === "undefined")
return;
// Defer mid-generation: setLightMode's regen would be dropped, and DMS's own transient color-scheme toggle would be misread. Re-run on worker completion.
if (Theme.workerRunning)
return false;
return typeof Theme !== "undefined";
}
// Only follow values stable for the settle window — the GTK4-refresh toggle reverts within ~50ms and following it would loop.
function evaluateColorScheme() {
if (!canSyncColorScheme())
return;
const shouldBeLight = systemColorScheme !== 1;
if (Theme.isLightMode === shouldBeLight)
if (Theme.isLightMode === shouldBeLight) {
colorSchemeSettleTimer.stop();
return;
Theme.setLightMode(shouldBeLight, true, false);
}
colorSchemeSettleTimer.restart();
}
Timer {
id: colorSchemeSettleTimer
interval: 750
onTriggered: {
if (!root.canSyncColorScheme())
return;
const shouldBeLight = root.systemColorScheme !== 1;
if (Theme.isLightMode === shouldBeLight)
return;
if (Theme.workerRunning) {
restart();
return;
}
Theme.setLightMode(shouldBeLight, true, false);
}
}
function setLightMode(isLightMode) {