1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-06-23 03:25:19 -04:00

portal: add bidirectional syncing with freedesktop color-scheme, read in

addition to the existing write
This commit is contained in:
bbedward
2026-06-22 14:52:10 -04:00
parent b5e2e68a22
commit 5b28a63f75
4 changed files with 133 additions and 41 deletions
+72 -2
View File
@@ -6,6 +6,7 @@ import (
"os"
"sync"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/pkg/dbusutil"
"github.com/godbus/dbus/v5"
)
@@ -94,9 +95,65 @@ func (m *Manager) initializeSettings() error {
return fmt.Errorf("failed to update settings state: %w", err)
}
go m.watchSettingsChanges()
return nil
}
func (m *Manager) watchSettingsChanges() {
conn, err := dbus.ConnectSessionBus()
if err != nil {
log.Warnf("color-scheme watcher: session bus connect: %v", err)
return
}
if err := conn.AddMatchSignal(
dbus.WithMatchInterface(dbusPortalSettingsInterface),
dbus.WithMatchMember("SettingChanged"),
); err != nil {
log.Warnf("Failed to watch portal settings changes: %v", err)
conn.Close()
return
}
signals := make(chan *dbus.Signal, 64)
conn.Signal(signals)
for sig := range signals {
if sig.Name != dbusPortalSettingsInterface+".SettingChanged" {
continue
}
if len(sig.Body) < 3 {
continue
}
namespace, _ := sig.Body[0].(string)
key, _ := sig.Body[1].(string)
if namespace != "org.freedesktop.appearance" || key != "color-scheme" {
continue
}
variant, ok := sig.Body[2].(dbus.Variant)
if !ok {
continue
}
colorScheme, ok := dbusutil.As[uint32](variant)
if !ok {
continue
}
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 {
m.NotifySubscribers()
}
}
}
func (m *Manager) updateAccountsState() error {
if !m.state.Accounts.Available || m.accountsObj == nil {
return fmt.Errorf("accounts service not available")
@@ -134,10 +191,23 @@ func (m *Manager) updateSettingsState() error {
var variant dbus.Variant
err := m.settingsObj.Call(dbusPortalSettingsInterface+".ReadOne", 0, "org.freedesktop.appearance", "color-scheme").Store(&variant)
if err != nil {
return err
// Older xdg-desktop-portal versions only expose the deprecated Read.
var nested dbus.Variant
if rerr := m.settingsObj.Call(dbusPortalSettingsInterface+".Read", 0, "org.freedesktop.appearance", "color-scheme").Store(&nested); rerr != nil {
log.Warnf("color-scheme: ReadOne (%v) and Read (%v) both failed", err, rerr)
return err
}
variant = nested
}
if colorScheme, ok := dbusutil.As[uint32](variant); ok {
colorScheme, ok := dbusutil.As[uint32](variant)
if !ok {
// Read double-wraps the value in a variant.
if inner, innerOk := variant.Value().(dbus.Variant); innerOk {
colorScheme, ok = dbusutil.As[uint32](inner)
}
}
if ok {
m.stateMutex.Lock()
m.state.Settings.ColorScheme = colorScheme
m.stateMutex.Unlock()
+12 -27
View File
@@ -175,8 +175,7 @@ Item {
property bool barSurfacesLoaded: true
function recreateBarSurfaces() {
log.info("Recreating bar surfaces, screens:", Quickshell.screens.length,
Quickshell.screens.map(s => s.name).join(","));
log.info("Recreating bar surfaces, screens:", Quickshell.screens.length, Quickshell.screens.map(s => s.name).join(","));
if (barSurfacesLoaded)
barSurfacesLoaded = false;
barSurfaceReloadAction.schedule();
@@ -345,12 +344,7 @@ Item {
}
function triggerSurfaceRecovery(source) {
log.info("Surface recovery triggered by:", source,
"screens:", Quickshell.screens.length,
Quickshell.screens.map(s => s.name).join(","),
"barLoaded:", root.barSurfacesLoaded,
"frameLoaded:", root.frameSurfacesLoaded,
"dockEnabled:", root.dockEnabled);
log.info("Surface recovery triggered by:", source, "screens:", Quickshell.screens.length, Quickshell.screens.map(s => s.name).join(","), "barLoaded:", root.barSurfacesLoaded, "frameLoaded:", root.frameSurfacesLoaded, "dockEnabled:", root.dockEnabled);
surfaceResumeRecoveryTimer.pass = 0;
surfaceResumeRecoveryTimer.interval = 800;
surfaceResumeRecoveryTimer.restart();
@@ -361,15 +355,11 @@ Item {
function onScreensChanged() {
const hasReal = root._hasRealScreen();
const currentNames = root._getRealScreenNames();
log.info("Screens changed:", Quickshell.screens.length,
Quickshell.screens.map(s => "'" + s.name + "'").join(","),
"hasReal:", hasReal, "hadReal:", root.hadRealScreen);
log.info("Screens changed:", Quickshell.screens.length, Quickshell.screens.map(s => "'" + s.name + "'").join(","), "hasReal:", hasReal, "hadReal:", root.hadRealScreen);
const fullReconnect = !root.hadRealScreen && hasReal;
const partialReconnect = root.previousRealScreenNames.length > 0
&& currentNames.some(name => !root.previousRealScreenNames.includes(name));
const partialReconnect = root.previousRealScreenNames.length > 0 && currentNames.some(name => !root.previousRealScreenNames.includes(name));
if (fullReconnect || partialReconnect) {
log.info("Screen reconnect detected, scheduling surface recovery",
"full:", fullReconnect, "partial:", partialReconnect);
log.info("Screen reconnect detected, scheduling surface recovery", "full:", fullReconnect, "partial:", partialReconnect);
root.scheduleScreenReconnectRecovery();
}
root.hadRealScreen = hasReal;
@@ -429,9 +419,7 @@ Item {
property int pass: 0
onTriggered: {
pass++;
log.info("Surface recovery pass", pass,
"screens:", Quickshell.screens.length,
Quickshell.screens.map(s => s.name).join(","));
log.info("Surface recovery pass", pass, "screens:", Quickshell.screens.length, Quickshell.screens.map(s => s.name).join(","));
root.recreateBarSurfaces();
@@ -457,11 +445,12 @@ Item {
Component.onCompleted: {
dockRecreateDebounce.start();
// Force PolkitService singleton to initialize
PolkitService.polkitAvailable;
// Force DisplayConfigState singleton to initialize so auto-config runs at startup
DisplayConfigState.hasOutputBackend;
loginSoundTimer.start();
// These are dummy references just to trigger the singletons onCompleted to trigger
PolkitService.polkitAvailable;
DisplayConfigState.hasOutputBackend;
PortalService.systemColorScheme;
}
Loader {
@@ -1041,11 +1030,7 @@ Item {
target: SessionService
function onSessionResumed() {
log.info("Session resumed: screens:", Quickshell.screens.length,
Quickshell.screens.map(s => s.name).join(","),
"barLoaded:", root.barSurfacesLoaded,
"frameLoaded:", root.frameSurfacesLoaded,
"dockEnabled:", root.dockEnabled);
log.info("Session resumed: screens:", Quickshell.screens.length, Quickshell.screens.map(s => s.name).join(","), "barLoaded:", root.barSurfacesLoaded, "frameLoaded:", root.frameSurfacesLoaded, "dockEnabled:", root.dockEnabled);
root.pendingOsdResumeReloads = 2;
osdResumeRecreateTimer.interval = 400;
+3
View File
@@ -58,6 +58,7 @@ Singleton {
signal openUrlRequested(string url)
signal appPickerRequested(var data)
signal screensaverStateUpdate(var data)
signal freedesktopStateUpdate(var data)
signal clipboardStateUpdate(var data)
signal locationStateUpdate(var data)
signal sysupdateStateUpdate(var data)
@@ -384,6 +385,8 @@ Singleton {
screensaverInhibited = data.inhibited || false;
screensaverInhibitors = data.inhibitors || [];
screensaverStateUpdate(data);
} else if (service === "freedesktop") {
freedesktopStateUpdate(data);
} else if (service === "dbus") {
dbusSignalReceived(data.subscriptionId || "", data);
} else if (service === "clipboard") {
+46 -12
View File
@@ -87,17 +87,22 @@ Singleton {
}
}
function getSystemColorScheme() {
if (typeof SettingsData !== "undefined" && SettingsData.syncModeWithPortal === false) {
function evaluateColorScheme() {
if (typeof SettingsData === "undefined" || !SettingsData.syncModeWithPortal)
return;
}
if (!freedeskAvailable)
if (!settingsPortalAvailable)
return;
DMSService.sendRequest("freedesktop.settings.getColorScheme", null, response => {
if (response.result) {
systemColorScheme = response.result.value || 0;
}
});
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;
const shouldBeLight = systemColorScheme !== 1;
if (Theme.isLightMode === shouldBeLight)
return;
Theme.setLightMode(shouldBeLight, true, false);
}
function setLightMode(isLightMode) {
@@ -176,6 +181,36 @@ Singleton {
colorSchemeDetector.running = true;
}
Connections {
target: typeof SettingsData !== "undefined" ? SettingsData : null
function onSyncModeWithPortalChanged() {
if (SettingsData.syncModeWithPortal)
root.evaluateColorScheme();
}
}
Connections {
target: DMSService
function onFreedesktopStateUpdate(data) {
if (!data || !data.settings)
return;
root.settingsPortalAvailable = data.settings.available === true;
root.systemColorScheme = data.settings.colorScheme || 0;
root.evaluateColorScheme();
}
}
Connections {
target: typeof Theme !== "undefined" ? Theme : null
function onWorkerRunningChanged() {
if (!Theme.workerRunning)
root.evaluateColorScheme();
}
}
Connections {
target: DMSService
@@ -232,9 +267,8 @@ Singleton {
DMSService.sendRequest("freedesktop.getState", null, response => {
if (response.result && response.result.settings) {
settingsPortalAvailable = response.result.settings.available || false;
if (settingsPortalAvailable && SettingsData.syncModeWithPortal) {
getSystemColorScheme();
}
systemColorScheme = response.result.settings.colorScheme || 0;
evaluateColorScheme();
}
});
}