1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-06-27 13:35:18 -04:00

core: improve how DMS handles multiple-sessions under the same user

This commit is contained in:
bbedward
2026-06-26 10:09:00 -04:00
parent 0bc89429bc
commit 03d86f78f4
5 changed files with 168 additions and 17 deletions
+7
View File
@@ -101,6 +101,13 @@ func getServerSocketPath() string {
runtimeDir = os.TempDir() runtimeDir = os.TempDir()
} }
if parentPID, ok := sessionParentPID(os.Getenv("WAYLAND_DISPLAY")); ok {
sessionSock := filepath.Join(runtimeDir, fmt.Sprintf("danklinux-%d.sock", parentPID))
if _, err := os.Stat(sessionSock); err == nil {
return sessionSock
}
}
entries, err := os.ReadDir(runtimeDir) entries, err := os.ReadDir(runtimeDir)
if err != nil { if err != nil {
return filepath.Join(runtimeDir, "danklinux.sock") return filepath.Join(runtimeDir, "danklinux.sock")
+75 -6
View File
@@ -101,14 +101,23 @@ func getPIDFilePath() string {
return filepath.Join(getRuntimeDir(), fmt.Sprintf("danklinux-%d.pid", os.Getpid())) return filepath.Join(getRuntimeDir(), fmt.Sprintf("danklinux-%d.pid", os.Getpid()))
} }
func getSessionFilePath() string {
return filepath.Join(getRuntimeDir(), fmt.Sprintf("danklinux-%d.session", os.Getpid()))
}
func writePIDFile(childPID int) error { func writePIDFile(childPID int) error {
pidFile := getPIDFilePath() pidFile := getPIDFilePath()
if display := os.Getenv("WAYLAND_DISPLAY"); display != "" {
if err := os.WriteFile(getSessionFilePath(), []byte(display), 0o644); err != nil {
log.Warnf("Failed to write session file: %v", err)
}
}
return os.WriteFile(pidFile, []byte(strconv.Itoa(childPID)), 0o644) return os.WriteFile(pidFile, []byte(strconv.Itoa(childPID)), 0o644)
} }
func removePIDFile() { func removePIDFile() {
pidFile := getPIDFilePath() os.Remove(getPIDFilePath())
os.Remove(pidFile) os.Remove(getSessionFilePath())
} }
func getAllDMSPIDs() []int { func getAllDMSPIDs() []int {
@@ -390,9 +399,11 @@ func killShell() {
} }
for _, entry := range entries { for _, entry := range entries {
if strings.HasPrefix(entry.Name(), "danklinux-") && strings.HasSuffix(entry.Name(), ".pid") { if !strings.HasPrefix(entry.Name(), "danklinux-") {
pidFile := filepath.Join(dir, entry.Name()) continue
os.Remove(pidFile) }
if strings.HasSuffix(entry.Name(), ".pid") || strings.HasSuffix(entry.Name(), ".session") {
os.Remove(filepath.Join(dir, entry.Name()))
} }
} }
} }
@@ -609,7 +620,7 @@ func parseTargetsFromIPCShowOutput(output string) ipcTargets {
func buildQsIPCBaseArgs() ([]string, error) { func buildQsIPCBaseArgs() ([]string, error) {
cmdArgs := []string{"ipc"} cmdArgs := []string{"ipc"}
switch pid, ok := getFirstDMSPID(); { switch pid, ok := getSessionDMSPID(); {
case ok: case ok:
cmdArgs = append(cmdArgs, "--pid", strconv.Itoa(pid)) cmdArgs = append(cmdArgs, "--pid", strconv.Itoa(pid))
default: default:
@@ -710,6 +721,64 @@ func getFirstDMSPID() (int, bool) {
return 0, false return 0, false
} }
func sessionParentPID(display string) (int, bool) {
if display == "" {
return 0, false
}
dir := getRuntimeDir()
entries, err := os.ReadDir(dir)
if err != nil {
return 0, false
}
for _, entry := range entries {
name := entry.Name()
if !strings.HasPrefix(name, "danklinux-") || !strings.HasSuffix(name, ".session") {
continue
}
data, err := os.ReadFile(filepath.Join(dir, name))
if err != nil || strings.TrimSpace(string(data)) != display {
continue
}
parentStr := strings.TrimSuffix(strings.TrimPrefix(name, "danklinux-"), ".session")
parentPID, err := strconv.Atoi(parentStr)
if err != nil {
continue
}
return parentPID, true
}
return 0, false
}
func getSessionDMSPID() (int, bool) {
parentPID, ok := sessionParentPID(os.Getenv("WAYLAND_DISPLAY"))
if !ok {
return getFirstDMSPID()
}
data, err := os.ReadFile(filepath.Join(getRuntimeDir(), fmt.Sprintf("danklinux-%d.pid", parentPID)))
if err != nil {
return getFirstDMSPID()
}
pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
if err != nil {
return getFirstDMSPID()
}
proc, err := os.FindProcess(pid)
if err != nil || proc.Signal(syscall.Signal(0)) != nil {
return getFirstDMSPID()
}
return pid, true
}
func runShellIPCCommand(args []string) { func runShellIPCCommand(args []string) {
if len(args) == 0 { if len(args) == 0 {
printIPCHelp() printIPCHelp()
+52 -2
View File
@@ -122,6 +122,10 @@ func (o *Options) ColorsOutput() string {
return filepath.Join(o.StateDir, "dms-colors.json") return filepath.Join(o.StateDir, "dms-colors.json")
} }
func (o *Options) colorsStaging() string {
return o.ColorsOutput() + ".tmp"
}
func (o *Options) ShouldSkipTemplate(name string) bool { func (o *Options) ShouldSkipTemplate(name string) bool {
if o.SkipTemplates == "" { if o.SkipTemplates == "" {
return false return false
@@ -134,6 +138,38 @@ func (o *Options) ShouldSkipTemplate(name string) bool {
return false return false
} }
func acquireMatugenLock(stateDir string) (*os.File, error) {
f, err := os.OpenFile(filepath.Join(stateDir, "matugen.lock"), os.O_CREATE|os.O_RDWR, 0o644)
if err != nil {
return nil, fmt.Errorf("failed to open matugen lock: %w", err)
}
deadline := time.Now().Add(45 * time.Second)
for {
switch err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err {
case nil:
return f, nil
case syscall.EWOULDBLOCK:
if time.Now().After(deadline) {
f.Close()
return nil, fmt.Errorf("timed out waiting for matugen lock")
}
time.Sleep(100 * time.Millisecond)
default:
f.Close()
return nil, fmt.Errorf("failed to lock matugen: %w", err)
}
}
}
func releaseMatugenLock(f *os.File) {
if f == nil {
return
}
_ = syscall.Flock(int(f.Fd()), syscall.LOCK_UN)
f.Close()
}
func Run(opts Options) error { func Run(opts Options) error {
if opts.StateDir == "" { if opts.StateDir == "" {
return fmt.Errorf("state-dir is required") return fmt.Errorf("state-dir is required")
@@ -167,6 +203,12 @@ func Run(opts Options) error {
return fmt.Errorf("failed to create state dir: %w", err) return fmt.Errorf("failed to create state dir: %w", err)
} }
lock, err := acquireMatugenLock(opts.StateDir)
if err != nil {
return err
}
defer releaseMatugenLock(lock)
log.Infof("Building theme: %s %s (%s)", opts.Kind, opts.Value, opts.Mode) log.Infof("Building theme: %s %s (%s)", opts.Kind, opts.Value, opts.Mode)
changed, buildErr := buildOnce(&opts) changed, buildErr := buildOnce(&opts)
@@ -188,6 +230,8 @@ func Run(opts Options) error {
} }
func buildOnce(opts *Options) (bool, error) { func buildOnce(opts *Options) (bool, error) {
defer os.Remove(opts.colorsStaging())
cfgFile, err := os.CreateTemp("", "matugen-config-*.toml") cfgFile, err := os.CreateTemp("", "matugen-config-*.toml")
if err != nil { if err != nil {
return false, fmt.Errorf("failed to create temp config: %w", err) return false, fmt.Errorf("failed to create temp config: %w", err)
@@ -275,10 +319,16 @@ func buildOnce(opts *Options) (bool, error) {
} }
} }
newColors, _ := os.ReadFile(opts.ColorsOutput()) newColors, err := os.ReadFile(opts.colorsStaging())
if err != nil {
return false, fmt.Errorf("matugen did not produce colors output: %w", err)
}
if bytes.Equal(oldColors, newColors) && len(oldColors) > 0 { if bytes.Equal(oldColors, newColors) && len(oldColors) > 0 {
return false, nil return false, nil
} }
if err := os.Rename(opts.colorsStaging(), opts.ColorsOutput()); err != nil {
return false, fmt.Errorf("failed to commit colors output: %w", err)
}
if opts.ColorsOnly { if opts.ColorsOnly {
return true, nil return true, nil
@@ -346,7 +396,7 @@ func buildMergedConfig(opts *Options, cfgFile *os.File, tmpDir string) error {
input_path = '%s/matugen/templates/dank.json' input_path = '%s/matugen/templates/dank.json'
output_path = '%s' output_path = '%s'
`, opts.ShellDir, opts.ColorsOutput()) `, opts.ShellDir, opts.colorsStaging())
if opts.ColorsOnly { if opts.ColorsOnly {
return nil return nil
+1 -1
View File
@@ -437,7 +437,7 @@ func TestBuildMergedConfigColorsOnly(t *testing.T) {
content := string(output) content := string(output)
assert.Contains(t, content, "[templates.dank]") assert.Contains(t, content, "[templates.dank]")
assert.Contains(t, content, "output_path = '"+filepath.Join(opts.StateDir, "dms-colors.json")+"'") assert.Contains(t, content, "output_path = '"+opts.colorsStaging()+"'")
assert.NotContains(t, content, "[templates.gtk]") assert.NotContains(t, content, "[templates.gtk]")
assert.False(t, strings.Contains(content, "output_path = 'CONFIG_DIR/"), "colors-only config should not emit app template outputs") assert.False(t, strings.Contains(content, "output_path = 'CONFIG_DIR/"), "colors-only config should not emit app template outputs")
} }
+33 -8
View File
@@ -104,6 +104,8 @@ Singleton {
signal matugenCompleted(string mode, string result) signal matugenCompleted(string mode, string result)
property var matugenColors: ({}) property var matugenColors: ({})
property var _pendingGenerateParams: null property var _pendingGenerateParams: null
property int _colorsRetryCount: 0
property double _lastGenerateMs: 0
property bool themeModeAutomationActive: false property bool themeModeAutomationActive: false
property bool dmsServiceWasDisconnected: true property bool dmsServiceWasDisconnected: true
@@ -1771,6 +1773,7 @@ Singleton {
if (!matugenAvailable || isGreeterMode) if (!matugenAvailable || isGreeterMode)
return; return;
_lastGenerateMs = Date.now();
_pendingGenerateParams = true; _pendingGenerateParams = true;
_themeGenerateDebounce.restart(); _themeGenerateDebounce.restart();
} }
@@ -2217,6 +2220,7 @@ Singleton {
} }
onLoaded: { onLoaded: {
_colorsRetryCount = 0;
if (currentTheme === dynamic) if (currentTheme === dynamic)
colorsFileLoadFailed = false; colorsFileLoadFailed = false;
parseAndLoadColors(); parseAndLoadColors();
@@ -2227,14 +2231,28 @@ Singleton {
} }
onLoadFailed: function (error) { onLoadFailed: function (error) {
if (currentTheme === dynamic) { if (currentTheme !== dynamic)
log.warn("Dynamic colors file load failed, marking for regeneration"); return;
colorsFileLoadFailed = true;
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode); if (SessionData.isGreeterMode)
if (!isGreeterMode && matugenAvailable && rawWallpaperPath) { return;
log.debug("Matugen available, triggering immediate regeneration");
generateSystemThemesFromCurrentTheme(); if (workerRunning) {
} colorsReloadRetry.restart();
return;
}
if (_colorsRetryCount < 3) {
_colorsRetryCount++;
colorsReloadRetry.restart();
return;
}
colorsFileLoadFailed = true;
const stale = Date.now() - _lastGenerateMs > 5000;
if (matugenAvailable && rawWallpaperPath && stale) {
log.debug("Dynamic colors unrecoverable, regenerating");
generateSystemThemesFromCurrentTheme();
} }
} }
@@ -2243,6 +2261,13 @@ Singleton {
} }
} }
Timer {
id: colorsReloadRetry
interval: 150
repeat: false
onTriggered: dynamicColorsFileView.reload()
}
IpcHandler { IpcHandler {
target: "theme" target: "theme"