# The desktop's background programs, as systemd user services. On CachyOS most # of these were started by hyprland.lua (hl.exec_cmd at hyprland.start). # # uwsm starts Hyprland and then graphical-session.target; every service here is # wanted by that target and stopped with it, so they come up after the # compositor is ready and go away at logout. They are restarted if they crash. # # systemctl --user status waybar is it running, and its last log lines # journalctl --user -u waybar -f follow its log # systemctl --user restart waybar e.g. after changing its config # # Slices are uwsm's: session-graphical for the shell itself (bar, idle, wallpaper, # notifications), background-graphical for helpers. { config, lib, pkgs, ... }: let graphical = { description, exec, slice ? "background-graphical.slice", after ? [ ], unit ? { }, service ? { }, }: { Unit = { Description = description; PartOf = [ "graphical-session.target" ]; After = [ "graphical-session.target" ] ++ after; Requisite = [ "graphical-session.target" ]; } // unit; Service = { ExecStart = exec; Restart = "on-failure"; RestartSec = 2; Slice = slice; } // service; Install.WantedBy = [ "graphical-session.target" ]; }; configDir = "${config.xdg.configHome}"; in { systemd.user.services = { waybar = graphical { description = "Waybar"; exec = lib.getExe pkgs.waybar; slice = "session-graphical.slice"; service.ExecReload = "${pkgs.coreutils}/bin/kill -SIGUSR2 $MAINPID"; }; hypridle = graphical { description = "Hyprland's idle daemon"; exec = lib.getExe pkgs.hypridle; slice = "session-graphical.slice"; unit.ConditionEnvironment = "WAYLAND_DISPLAY"; }; hyprpaper = graphical { description = "Hyprland wallpaper daemon"; exec = lib.getExe pkgs.hyprpaper; slice = "session-graphical.slice"; }; dunst = graphical { description = "Dunst notification daemon"; exec = lib.getExe pkgs.dunst; slice = "session-graphical.slice"; service = { Type = "dbus"; BusName = "org.freedesktop.Notifications"; }; }; # The Waybar flyouts (control centre, notifications). Started with the # session so the first click on the Waybar button opens instantly. eww = graphical { description = "Eww daemon for the Waybar flyouts"; exec = "${lib.getExe pkgs.eww} --config ${configDir}/waybar/eww daemon --no-daemonize"; }; hyprpolkitagent = graphical { description = "Hyprland polkit agent"; exec = "${pkgs.hyprpolkitagent}/libexec/hyprpolkitagent"; slice = "session-graphical.slice"; }; # Puts Wine/Proton tray icons (Battle.net) into Waybar's tray, so it needs # the tray to be there first. xembedsniproxy = graphical { description = "XEmbed to StatusNotifierItem tray bridge"; exec = lib.getExe pkgs.xembedsniproxy; after = [ "waybar.service" ]; # Restarting it while a Wine app is open breaks that app's tray icon until # the app is restarted, so a rebuild leaves the running one alone. unit.X-RestartIfChanged = false; }; # Forwards the X11 clipboard (Wine/Proton games) to Wayland whatever has # focus. Waits for Xwayland by itself. xwl-clipbridge = graphical { description = "X11 to Wayland clipboard bridge"; exec = "${pkgs.python3-desktop}/bin/python3 ${configDir}/hypr/scripts/xwl-clipbridge"; service.Environment = [ "PATH=${lib.makeBinPath [ pkgs.wl-clipboard ]}" ]; }; # The apps opened at login (Signal, Discord, Faugus, OpenDeck, WowUp); see # config/hypr/scripts/autostart-apps. It starts each one as its own unit and # exits; the window placement rules for them are in hyprland.lua. autostart-apps = graphical { description = "Open the apps that start at login"; exec = "${configDir}/hypr/scripts/autostart-apps"; after = [ "waybar.service" ]; service = { Type = "oneshot"; Restart = "no"; # The script finds the apps (and pgrep, uwsm, hyprctl, ...) on the # system and user profiles, whatever environment the session imported. Environment = [ "PATH=${ lib.makeBinPath [ pkgs.python3-desktop ] }:/etc/profiles/per-user/${config.home.username}/bin:/run/current-system/sw/bin" ]; # It waits for the tray and for Signal and Discord to open, up to about # two minutes; more than the 15 s default the CachyOS settings set. TimeoutStartSec = "3min"; }; unit.X-RestartIfChanged = false; }; }; # Network and Bluetooth live in the control centre, not the tray. blueman-applet # still runs, from the autostart entry services.blueman installs (uwsm runs it # as app-blueman@autostart.service), for pairing prompts and notifications; # only its tray icon is turned off (ShowConnected needs that icon). dconf.settings."org/blueman/general".plugin-list = [ "!StatusIcon" "!ShowConnected" ]; # Bitwarden at login. This one stays an XDG autostart entry, as on CachyOS # (uwsm runs those as systemd units too: app-bitwarden@autostart.service); the # entry just needs NixOS's name for the program. xdg.configFile."autostart/bitwarden.desktop".text = '' [Desktop Entry] Type=Application Name=Bitwarden Comment=Bitwarden startup script Exec=${lib.getExe pkgs.bitwarden-desktop} StartupNotify=false Terminal=false ''; }