diff --git a/modules/home/wayland/apps/eww/default.nix b/modules/home/wayland/apps/eww/default.nix index 815ee87..adcebe3 100755 --- a/modules/home/wayland/apps/eww/default.nix +++ b/modules/home/wayland/apps/eww/default.nix @@ -2,6 +2,16 @@ let ensureDaemon = pkgs.custom.eww-ensure-daemon; + ewwCleanStart = pkgs.writeShellScript "eww-clean-start" '' + uid="$(${pkgs.coreutils}/bin/id -u)" + runtime_dir="''${XDG_RUNTIME_DIR:-/run/user/$uid}" + + ${pkgs.procps}/bin/pkill -TERM -x -u "$uid" eww >/dev/null 2>&1 || true + ${pkgs.coreutils}/bin/sleep 0.2 + ${pkgs.procps}/bin/pkill -KILL -x -u "$uid" eww >/dev/null 2>&1 || true + ${pkgs.coreutils}/bin/rm -f "$runtime_dir"/eww-server_* + ''; + openOnCurrentScreen = pkgs.writeShellScriptBin "eww-open-on-current-screen" '' window="$1" shift @@ -75,9 +85,9 @@ in { }; Service = { Type = "simple"; - ExecStartPre = "${pkgs.bash}/bin/bash -c 'rm -f \"\${XDG_RUNTIME_DIR:-/run/user/$(id -u)}\"/eww-server_*'"; + ExecStartPre = "${ewwCleanStart}"; ExecStart = "${lib.getExe pkgs.eww} daemon --no-daemonize"; - Restart = "on-failure"; + Restart = "always"; RestartSec = 1; }; Install.WantedBy = [ "graphical-session.target" ]; diff --git a/packages/eww-ensure-daemon/default.nix b/packages/eww-ensure-daemon/default.nix index ee9af90..c4a983c 100644 --- a/packages/eww-ensure-daemon/default.nix +++ b/packages/eww-ensure-daemon/default.nix @@ -1,19 +1,58 @@ -{ lib, writeShellScriptBin, eww, systemd }: +{ lib +, writeShellScriptBin +, coreutils +, eww +, procps +, systemd +, util-linux +}: # `eww open` silently becomes a NEW daemon when it can't reach the socket, # rebinding the same socket path and orphaning the previous daemon. The old # daemon keeps rendering its layer-shell surfaces but no longer answers any # `eww close` — that is the "zombie panel I can't close" / "bar opened twice". -# Every open path must go through here first, so the socket is always live -# before a client touches it and no client ever self-daemonizes. +# Every open path must go through here first, so the socket is always live and +# owned by the managed user service before a client touches it. writeShellScriptBin "eww-ensure-daemon" '' - ${lib.getExe eww} ping >/dev/null 2>&1 && exit 0 + eww=${lib.getExe eww} + uid="$(${coreutils}/bin/id -u)" + runtime_dir="''${XDG_RUNTIME_DIR:-/run/user/$uid}" + lock_file="$runtime_dir/eww-ensure-daemon.lock" - ${systemd}/bin/systemctl --user start eww.service >/dev/null 2>&1 || true + service_active() { + ${systemd}/bin/systemctl --user is-active --quiet eww.service + } - for _ in $(seq 1 100); do - ${lib.getExe eww} ping >/dev/null 2>&1 && exit 0 - sleep 0.1 - done - exit 1 + daemon_healthy() { + "$eww" ping >/dev/null 2>&1 && service_active + } + + wait_for_ping() { + i=0 + while [ "$i" -lt 100 ]; do + daemon_healthy && return 0 + ${coreutils}/bin/sleep 0.1 + i=$((i + 1)) + done + return 1 + } + + daemon_healthy && exit 0 + + ${coreutils}/bin/mkdir -p "$runtime_dir" + ( + ${util-linux}/bin/flock -x 9 + + daemon_healthy && exit 0 + + ${systemd}/bin/systemctl --user stop eww.service >/dev/null 2>&1 || true + ${procps}/bin/pkill -TERM -x -u "$uid" eww >/dev/null 2>&1 || true + ${coreutils}/bin/sleep 0.2 + ${procps}/bin/pkill -KILL -x -u "$uid" eww >/dev/null 2>&1 || true + ${coreutils}/bin/rm -f "$runtime_dir"/eww-server_* + + ${systemd}/bin/systemctl --user reset-failed eww.service >/dev/null 2>&1 || true + ${systemd}/bin/systemctl --user start eww.service >/dev/null 2>&1 || true + wait_for_ping + ) 9>"$lock_file" ''