mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-06-26 13:05:20 -04:00
core: improve how DMS handles multiple-sessions under the same user
This commit is contained in:
@@ -101,6 +101,13 @@ func getServerSocketPath() string {
|
||||
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)
|
||||
if err != nil {
|
||||
return filepath.Join(runtimeDir, "danklinux.sock")
|
||||
|
||||
+75
-6
@@ -101,14 +101,23 @@ func getPIDFilePath() string {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
func removePIDFile() {
|
||||
pidFile := getPIDFilePath()
|
||||
os.Remove(pidFile)
|
||||
os.Remove(getPIDFilePath())
|
||||
os.Remove(getSessionFilePath())
|
||||
}
|
||||
|
||||
func getAllDMSPIDs() []int {
|
||||
@@ -390,9 +399,11 @@ func killShell() {
|
||||
}
|
||||
|
||||
for _, entry := range entries {
|
||||
if strings.HasPrefix(entry.Name(), "danklinux-") && strings.HasSuffix(entry.Name(), ".pid") {
|
||||
pidFile := filepath.Join(dir, entry.Name())
|
||||
os.Remove(pidFile)
|
||||
if !strings.HasPrefix(entry.Name(), "danklinux-") {
|
||||
continue
|
||||
}
|
||||
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) {
|
||||
cmdArgs := []string{"ipc"}
|
||||
switch pid, ok := getFirstDMSPID(); {
|
||||
switch pid, ok := getSessionDMSPID(); {
|
||||
case ok:
|
||||
cmdArgs = append(cmdArgs, "--pid", strconv.Itoa(pid))
|
||||
default:
|
||||
@@ -710,6 +721,64 @@ func getFirstDMSPID() (int, bool) {
|
||||
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) {
|
||||
if len(args) == 0 {
|
||||
printIPCHelp()
|
||||
|
||||
@@ -122,6 +122,10 @@ func (o *Options) ColorsOutput() string {
|
||||
return filepath.Join(o.StateDir, "dms-colors.json")
|
||||
}
|
||||
|
||||
func (o *Options) colorsStaging() string {
|
||||
return o.ColorsOutput() + ".tmp"
|
||||
}
|
||||
|
||||
func (o *Options) ShouldSkipTemplate(name string) bool {
|
||||
if o.SkipTemplates == "" {
|
||||
return false
|
||||
@@ -134,6 +138,38 @@ func (o *Options) ShouldSkipTemplate(name string) bool {
|
||||
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 {
|
||||
if opts.StateDir == "" {
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
changed, buildErr := buildOnce(&opts)
|
||||
@@ -188,6 +230,8 @@ func Run(opts Options) error {
|
||||
}
|
||||
|
||||
func buildOnce(opts *Options) (bool, error) {
|
||||
defer os.Remove(opts.colorsStaging())
|
||||
|
||||
cfgFile, err := os.CreateTemp("", "matugen-config-*.toml")
|
||||
if err != nil {
|
||||
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 {
|
||||
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 {
|
||||
return true, nil
|
||||
@@ -346,7 +396,7 @@ func buildMergedConfig(opts *Options, cfgFile *os.File, tmpDir string) error {
|
||||
input_path = '%s/matugen/templates/dank.json'
|
||||
output_path = '%s'
|
||||
|
||||
`, opts.ShellDir, opts.ColorsOutput())
|
||||
`, opts.ShellDir, opts.colorsStaging())
|
||||
|
||||
if opts.ColorsOnly {
|
||||
return nil
|
||||
|
||||
@@ -437,7 +437,7 @@ func TestBuildMergedConfigColorsOnly(t *testing.T) {
|
||||
|
||||
content := string(output)
|
||||
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.False(t, strings.Contains(content, "output_path = 'CONFIG_DIR/"), "colors-only config should not emit app template outputs")
|
||||
}
|
||||
|
||||
@@ -104,6 +104,8 @@ Singleton {
|
||||
signal matugenCompleted(string mode, string result)
|
||||
property var matugenColors: ({})
|
||||
property var _pendingGenerateParams: null
|
||||
property int _colorsRetryCount: 0
|
||||
property double _lastGenerateMs: 0
|
||||
|
||||
property bool themeModeAutomationActive: false
|
||||
property bool dmsServiceWasDisconnected: true
|
||||
@@ -1771,6 +1773,7 @@ Singleton {
|
||||
if (!matugenAvailable || isGreeterMode)
|
||||
return;
|
||||
|
||||
_lastGenerateMs = Date.now();
|
||||
_pendingGenerateParams = true;
|
||||
_themeGenerateDebounce.restart();
|
||||
}
|
||||
@@ -2217,6 +2220,7 @@ Singleton {
|
||||
}
|
||||
|
||||
onLoaded: {
|
||||
_colorsRetryCount = 0;
|
||||
if (currentTheme === dynamic)
|
||||
colorsFileLoadFailed = false;
|
||||
parseAndLoadColors();
|
||||
@@ -2227,14 +2231,28 @@ Singleton {
|
||||
}
|
||||
|
||||
onLoadFailed: function (error) {
|
||||
if (currentTheme === dynamic) {
|
||||
log.warn("Dynamic colors file load failed, marking for regeneration");
|
||||
colorsFileLoadFailed = true;
|
||||
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode);
|
||||
if (!isGreeterMode && matugenAvailable && rawWallpaperPath) {
|
||||
log.debug("Matugen available, triggering immediate regeneration");
|
||||
generateSystemThemesFromCurrentTheme();
|
||||
}
|
||||
if (currentTheme !== dynamic)
|
||||
return;
|
||||
|
||||
if (SessionData.isGreeterMode)
|
||||
return;
|
||||
|
||||
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 {
|
||||
target: "theme"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user