improve eww
This commit is contained in:
@@ -1,94 +1,104 @@
|
||||
{ lib, config, pkgs, ... }:
|
||||
let
|
||||
ensureDaemon = pkgs.custom.eww-ensure-daemon;
|
||||
ewwBar = "${pkgs.custom.eww-bar}/bin/eww-bar";
|
||||
|
||||
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
|
||||
|
||||
${ensureDaemon}/bin/eww-ensure-daemon || exit 1
|
||||
|
||||
screen="$(hyprctl monitors -j | ${lib.getExe pkgs.jq} -r '.[] | select(.focused == true) | .name' | head -n1)"
|
||||
|
||||
if [ -n "$screen" ]; then
|
||||
exec ${lib.getExe pkgs.eww} open "$window" --screen "$screen" "$@"
|
||||
fi
|
||||
|
||||
exec ${lib.getExe pkgs.eww} open "$window" "$@"
|
||||
'';
|
||||
|
||||
# Escape hatch: tears down every eww process (including orphaned daemons that
|
||||
# own unreachable surfaces), clears the stale socket, and brings back a single
|
||||
# daemon with the bar. `pkill -x` matches the process name exactly so it can
|
||||
# never match a wrapper script or a shell that merely mentions eww.
|
||||
ewwReset = pkgs.writeShellScriptBin "eww-reset" ''
|
||||
${pkgs.systemd}/bin/systemctl --user stop eww.service >/dev/null 2>&1 || true
|
||||
${pkgs.procps}/bin/pkill -x -u "$(id -u)" eww >/dev/null 2>&1 || true
|
||||
sleep 1
|
||||
${pkgs.procps}/bin/pkill -9 -x -u "$(id -u)" eww >/dev/null 2>&1 || true
|
||||
rm -f "''${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"/eww-server_*
|
||||
|
||||
${ensureDaemon}/bin/eww-ensure-daemon || exit 1
|
||||
exec ${lib.getExe pkgs.eww} open bar --screen 0
|
||||
'';
|
||||
|
||||
# Wraps a static script file with a bash launcher that prepends Nix store
|
||||
# bin dirs to PATH — keeps the source files unchanged.
|
||||
# Every script ships as a bash wrapper that pins PATH to its dependencies
|
||||
# and inlines the shared helpers, so the sources under bar/scripts stay
|
||||
# plain shell files.
|
||||
baseInputs = with pkgs; [ coreutils findutils gawk gnugrep gnused systemd util-linux ];
|
||||
mkScript = name: src: inputs: pkgs.writeShellScriptBin name ''
|
||||
export PATH="${lib.makeBinPath inputs}:$PATH"
|
||||
export PATH="${lib.makeBinPath (baseInputs ++ inputs)}:$PATH"
|
||||
${builtins.readFile ./bar/scripts/_lib.sh}
|
||||
${builtins.readFile src}
|
||||
'';
|
||||
|
||||
scripts = {
|
||||
"scripts/brightness" = mkScript "brightness" ./bar/scripts/brightness [ pkgs.brightnessctl ];
|
||||
"scripts/nightlight" = mkScript "nightlight" ./bar/scripts/nightlight [ pkgs.wlsunset ];
|
||||
"scripts/read-mode" = mkScript "read-mode" ./bar/scripts/read-mode [ pkgs.hyprland ];
|
||||
"scripts/airplane-mode" = mkScript "airplane-mode" ./bar/scripts/airplane-mode [ pkgs.util-linux ];
|
||||
"scripts/do-not-disturb" = mkScript "do-not-disturb" ./bar/scripts/do-not-disturb [ pkgs.dunst ];
|
||||
"scripts/panel-toggle" = mkScript "panel-toggle" ./bar/scripts/panel-toggle [ pkgs.eww pkgs.util-linux openOnCurrentScreen ];
|
||||
"scripts/powermenu-toggle" = mkScript "powermenu-toggle" ./bar/scripts/powermenu-toggle [ pkgs.eww openOnCurrentScreen ];
|
||||
"scripts/power-save" = mkScript "power-save" ./bar/scripts/power-save [ pkgs.eww ];
|
||||
"scripts/radio" = mkScript "radio" ./bar/scripts/radio [ pkgs.eww pkgs.jq pkgs.curl pkgs.mpv pkgs.playerctl ];
|
||||
"scripts/color-pick" = mkScript "color-pick" ./bar/scripts/color-pick [ pkgs.eww pkgs.hyprpicker ];
|
||||
"scripts/media" = mkScript "media" ./bar/scripts/media [ pkgs.playerctl ];
|
||||
"scripts/lock" = mkScript "lock" ./bar/scripts/lock [ pkgs.eww pkgs.hyprlock ];
|
||||
"scripts/screenshot" = mkScript "screenshot" ./bar/scripts/screenshot [ pkgs.eww pkgs.hyprshot pkgs.satty pkgs.wl-clipboard ];
|
||||
"scripts/sys/gpu" = mkScript "gpu" ./bar/scripts/sys/gpu [ pkgs.custom.amdgpu_top pkgs.jq ];
|
||||
"scripts/sys/memory" = mkScript "memory" ./bar/scripts/sys/memory [ pkgs.ripgrep ];
|
||||
"scripts/volume" = mkScript "volume" ./bar/scripts/volume [ pkgs.ripgrep ];
|
||||
"scripts/weather" = mkScript "weather" ./bar/scripts/weather [ pkgs.jq pkgs.curl ];
|
||||
"scripts/workspaces" = mkScript "workspaces" ./bar/scripts/workspaces [ pkgs.jaq pkgs.socat pkgs.ripgrep ];
|
||||
scripts = with pkgs; {
|
||||
# bar
|
||||
"scripts/workspaces" = mkScript "workspaces" ./bar/scripts/workspaces [ hyprland jaq socat ripgrep ];
|
||||
"scripts/panel-toggle" = mkScript "panel-toggle" ./bar/scripts/panel-toggle [ eww custom.eww-bar ];
|
||||
"scripts/powermenu-toggle" = mkScript "powermenu-toggle" ./bar/scripts/powermenu-toggle [ eww custom.eww-bar ];
|
||||
"scripts/net/net" = mkScript "net" ./bar/scripts/net/net [ iproute2 wpa_supplicant ];
|
||||
"scripts/net/bt" = mkScript "bt" ./bar/scripts/net/bt [ jq glib ];
|
||||
"scripts/net/bt-toggle" = mkScript "bt-toggle" ./bar/scripts/net/bt-toggle [ bluez ];
|
||||
"scripts/sys/gpu" = mkScript "gpu" ./bar/scripts/sys/gpu [ custom.amdgpu_top jq ];
|
||||
"scripts/sys/memory" = mkScript "memory" ./bar/scripts/sys/memory [ ];
|
||||
"scripts/sys/battery" = mkScript "battery" ./bar/scripts/sys/battery [ ];
|
||||
"scripts/sys/cputemp" = mkScript "cputemp" ./bar/scripts/sys/cputemp [ ];
|
||||
# popup panels
|
||||
"scripts/volume" = mkScript "volume" ./bar/scripts/volume [ wireplumber pulseaudio ];
|
||||
"scripts/brightness" = mkScript "brightness" ./bar/scripts/brightness [ brightnessctl ];
|
||||
"scripts/weather" = mkScript "weather" ./bar/scripts/weather [ jq curl ];
|
||||
"scripts/radio" = mkScript "radio" ./bar/scripts/radio [ eww jq curl mpv playerctl wireplumber ];
|
||||
"scripts/media" = mkScript "media" ./bar/scripts/media [ playerctl ];
|
||||
# quick actions (long-running ones are detached via systemd-run)
|
||||
"scripts/nightlight" = mkScript "nightlight" ./bar/scripts/nightlight [ eww wlsunset procps ];
|
||||
"scripts/read-mode" = mkScript "read-mode" ./bar/scripts/read-mode [ eww hyprland ];
|
||||
"scripts/airplane-mode" = mkScript "airplane-mode" ./bar/scripts/airplane-mode [ eww ];
|
||||
"scripts/do-not-disturb" = mkScript "do-not-disturb" ./bar/scripts/do-not-disturb [ eww dunst ];
|
||||
"scripts/power-save" = mkScript "power-save" ./bar/scripts/power-save [ eww power-profiles-daemon ];
|
||||
"scripts/lock" = mkScript "lock" ./bar/scripts/lock [ eww hyprlock ];
|
||||
"scripts/screenshot" = mkScript "screenshot" ./bar/scripts/screenshot [ eww hyprshot satty wl-clipboard ];
|
||||
"scripts/color-pick" = mkScript "color-pick" ./bar/scripts/color-pick [ eww hyprpicker ];
|
||||
"scripts/wallpaper" = mkScript "wallpaper" ./bar/scripts/wallpaper [ eww ];
|
||||
};
|
||||
in {
|
||||
|
||||
config = lib.mkIf (config.usercfg.wm == "Wayland") {
|
||||
home.packages = [ pkgs.eww openOnCurrentScreen ensureDaemon ewwReset ];
|
||||
home.packages = [ pkgs.eww pkgs.custom.eww-bar ];
|
||||
|
||||
# One explicitly-managed daemon, so no client ever races to spawn a rival.
|
||||
# ExecStartPre clears a socket left behind by an unclean exit, which would
|
||||
# otherwise make the fresh daemon look reachable to clients before it binds.
|
||||
# The daemon and the bar are one unit: it starts and stops with the
|
||||
# graphical session, opens the bar once the daemon answers, and is
|
||||
# restarted on crash. `eww-bar` (packages/eww-bar) does the heavy
|
||||
# lifting; kanshi calls `eww-bar place` to move the bar between outputs.
|
||||
systemd.user.services.eww = {
|
||||
Unit = {
|
||||
Description = "eww daemon";
|
||||
Description = "eww daemon and bar";
|
||||
Documentation = "https://elkowar.github.io/eww/";
|
||||
PartOf = [ "graphical-session.target" ];
|
||||
After = [ "graphical-session.target" ];
|
||||
ConditionEnvironment = "WAYLAND_DISPLAY";
|
||||
# Cap crash loops (e.g. a broken config) without giving up for good:
|
||||
# the watchdog resets the counter and tries again later.
|
||||
StartLimitIntervalSec = 60;
|
||||
StartLimitBurst = 5;
|
||||
};
|
||||
Service = {
|
||||
Type = "simple";
|
||||
ExecStartPre = "${ewwCleanStart}";
|
||||
ExecStartPre = "${ewwBar} clean";
|
||||
ExecStart = "${lib.getExe pkgs.eww} daemon --no-daemonize";
|
||||
ExecStartPost = "${ewwBar} open";
|
||||
ExecReload = "${ewwBar} reload";
|
||||
Restart = "always";
|
||||
RestartSec = 1;
|
||||
RestartSec = 2;
|
||||
TimeoutStartSec = 30;
|
||||
TimeoutStopSec = 5;
|
||||
};
|
||||
Install.WantedBy = [ "graphical-session.target" ];
|
||||
};
|
||||
|
||||
# Liveness check: `eww ping` only proves the socket is up, so the check
|
||||
# also lists the windows through the GTK main loop. Two consecutive
|
||||
# failures (≈40 s) restart the daemon; a missing bar is simply reopened.
|
||||
systemd.user.services.eww-watchdog = {
|
||||
Unit = {
|
||||
Description = "eww health check";
|
||||
After = [ "eww.service" ];
|
||||
};
|
||||
Service = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "${ewwBar} check";
|
||||
};
|
||||
};
|
||||
systemd.user.timers.eww-watchdog = {
|
||||
Unit = {
|
||||
Description = "Periodic eww health check";
|
||||
PartOf = [ "graphical-session.target" ];
|
||||
After = [ "graphical-session.target" ];
|
||||
};
|
||||
Timer = {
|
||||
OnActiveSec = "30s";
|
||||
OnUnitActiveSec = "20s";
|
||||
AccuracySec = "5s";
|
||||
};
|
||||
Install.WantedBy = [ "graphical-session.target" ];
|
||||
};
|
||||
@@ -97,13 +107,14 @@ in {
|
||||
{
|
||||
"eww" = {
|
||||
source = lib.cleanSourceWith {
|
||||
# scripts/ is shipped through the wrappers below, never verbatim
|
||||
filter = name: _type:
|
||||
let
|
||||
baseName = baseNameOf (toString name);
|
||||
relPath = lib.removePrefix (toString ./bar + "/") (toString name);
|
||||
in !(lib.hasSuffix ".nix" baseName)
|
||||
&& (baseName != "_colors.scss")
|
||||
&& !(builtins.elem relPath (builtins.attrNames scripts));
|
||||
&& !(lib.hasPrefix "scripts/" relPath);
|
||||
src = lib.cleanSource ./bar/.;
|
||||
};
|
||||
recursive = true;
|
||||
|
||||
Reference in New Issue
Block a user