stasis does not run the configured actions through a login shell, so /etc/profile never applies and nothing resolves via /run/current-system/sw/bin. The old comment claimed the opposite and listed only the binaries stasis spawns directly, which left swaylock unreachable: `niri msg` worked, so the chain looked healthy while the lock step never ran. The failure is silent. The spawn fails, stasis logs the action as started, sees the immediate exit, treats the step as finished and stops there -- the daemon then sits in "waiting for idle" with no lock and no dpms. Verified: with an absolute path in the config, `stasis trigger lock_screen` locks; with a bare `swaylock` and the old PATH it exits instantly. brightnessctl comes along for the laptop's brightness step, which has the same problem.
198 lines
7.2 KiB
Nix
198 lines
7.2 KiB
Nix
# ABOUTME: systemd system and user services for the desktop session.
|
|
# ABOUTME: Rebuilt from moonarch/defaults/etc/systemd/, whose units use FHS paths.
|
|
|
|
{ config, pkgs, lib, moonarchPkgs, ... }:
|
|
|
|
{
|
|
# --- Audio ---
|
|
services.pipewire = {
|
|
enable = true;
|
|
alsa.enable = true;
|
|
alsa.support32Bit = true;
|
|
pulse.enable = true;
|
|
jack.enable = true;
|
|
};
|
|
|
|
# --- Hardware ---
|
|
hardware.bluetooth.enable = true;
|
|
services.blueman.enable = true;
|
|
services.udisks2.enable = true;
|
|
services.upower.enable = true;
|
|
services.gvfs.enable = true;
|
|
services.fwupd.enable = true;
|
|
# power-profiles-daemon over auto-cpufreq: on amd-pstate in active mode the
|
|
# platform decides the frequency from the min/max and EPP registers alone, so
|
|
# the governor is the wrong knob. ppd writes EPP and the ACPI platform_profile;
|
|
# where neither exists it falls back to a driver that touches no hardware.
|
|
services.power-profiles-daemon.enable = true;
|
|
|
|
# --- Firewall, replaces the ufw calls in post-install.sh ---
|
|
networking.firewall.enable = true;
|
|
|
|
# --- Session services ---
|
|
#
|
|
# Rebuilt rather than copied: the upstream units reference /usr/bin/,
|
|
# which does not exist under Nix.
|
|
|
|
systemd.user.services.kanshi = {
|
|
description = "Dynamic display configuration";
|
|
partOf = [ "graphical-session.target" ];
|
|
after = [ "graphical-session.target" ];
|
|
wantedBy = [ "graphical-session.target" ];
|
|
# Without a config kanshi exits with a parse error and, with
|
|
# Restart=on-failure, spins. Monitor profiles are per machine.
|
|
unitConfig.ConditionPathExists = "%h/.config/kanshi/config";
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
ExecStart = lib.getExe pkgs.kanshi;
|
|
Restart = "on-failure";
|
|
RestartSec = 3;
|
|
};
|
|
};
|
|
|
|
# --- System services from defaults/etc/systemd/system/ ---
|
|
#
|
|
# Rebuilt rather than copied, same reason as the user units: the originals
|
|
# reference /usr/bin.
|
|
|
|
systemd.services.moonarch-batsaver = {
|
|
description = "Restore battery conservation mode threshold";
|
|
after = [ "sysinit.target" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
unitConfig.ConditionPathExists = [
|
|
"/sys/class/power_supply/BAT0/charge_control_end_threshold"
|
|
"/var/lib/moonarch/batsaver-threshold"
|
|
];
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
ExecStart = "${moonarchPkgs.moonarch-scripts}/bin/moonarch-batsaver-restore";
|
|
NoNewPrivileges = true;
|
|
ProtectHome = true;
|
|
PrivateTmp = true;
|
|
ProtectKernelModules = true;
|
|
ProtectControlGroups = true;
|
|
RestrictNamespaces = true;
|
|
RestrictRealtime = true;
|
|
LockPersonality = true;
|
|
};
|
|
};
|
|
|
|
systemd.services.moonarch-camera = {
|
|
description = "Switch off all cameras on boot";
|
|
after = [ "sysinit.target" ];
|
|
wantedBy = [ "multi-user.target" ];
|
|
unitConfig.ConditionPathExistsGlob = "/sys/class/video4linux/video*";
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
ExecStart = "${moonarchPkgs.moonarch-scripts}/bin/moonarch-camera-boot";
|
|
NoNewPrivileges = true;
|
|
ProtectHome = true;
|
|
PrivateTmp = true;
|
|
ProtectKernelModules = true;
|
|
ProtectControlGroups = true;
|
|
RestrictNamespaces = true;
|
|
RestrictRealtime = true;
|
|
LockPersonality = true;
|
|
};
|
|
};
|
|
|
|
# The bar, launcher and every popout. A unit rather than a spawn-at-startup so
|
|
# a crash does not leave the session without a bar until the next login.
|
|
systemd.user.services.quickshell = {
|
|
description = "Quickshell bar and shell";
|
|
partOf = [ "graphical-session.target" ];
|
|
after = [ "graphical-session.target" ];
|
|
wantedBy = [ "graphical-session.target" ];
|
|
# The widgets shell out to niri, nmcli, powerprofilesctl, the moonarch
|
|
# scripts and a dozen more, and which ones is a property of the QML, not of
|
|
# this file. As a spawn-at-startup it inherited the session PATH; the unit
|
|
# gets the same set rather than a list that goes stale on the next widget.
|
|
path = [ config.system.path ];
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
ExecStart = "${lib.getExe pkgs.quickshell} -c moonarch";
|
|
Restart = "on-failure";
|
|
RestartSec = 3;
|
|
};
|
|
};
|
|
|
|
# nixpkgs ships the package but no module, and the package carries no unit of
|
|
# its own, so it is declared here. Mirrors /usr/lib/systemd/user/stasis.service.
|
|
systemd.user.services.stasis = {
|
|
description = "Stasis Wayland Idle Manager";
|
|
partOf = [ "graphical-session.target" ];
|
|
after = [ "graphical-session.target" ];
|
|
wantedBy = [ "graphical-session.target" ];
|
|
# Every binary a configured action names has to be listed here. stasis does
|
|
# not run the actions through a login shell, so /etc/profile never applies
|
|
# and nothing resolves via /run/current-system/sw/bin. A missing binary is
|
|
# silent: the spawn fails, stasis logs the action as started, sees the
|
|
# immediate exit, treats the step as finished and stops there — which is how
|
|
# swaylock was absent for the whole idle chain while `niri msg` worked.
|
|
# pactl is why pulseaudio appears at all — it stays off systemPackages, see
|
|
# modules/desktop.nix. brightnessctl is for the laptop's brightness step.
|
|
path = with pkgs; [
|
|
bashNonInteractive
|
|
niri
|
|
pulseaudio
|
|
libnotify
|
|
swaylock
|
|
brightnessctl
|
|
];
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
ExecStart = "${lib.getExe pkgs.stasis}";
|
|
ExecReload = "${lib.getExe pkgs.stasis} reload";
|
|
Restart = "on-failure";
|
|
RestartSec = 2;
|
|
};
|
|
};
|
|
|
|
systemd.user.services.cliphist-text = {
|
|
description = "Clipboard history manager (text)";
|
|
partOf = [ "graphical-session.target" ];
|
|
wantedBy = [ "graphical-session.target" ];
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
ExecStartPre = "${pkgs.runtimeShell} -c 'mkdir -p $XDG_RUNTIME_DIR/cliphist && ${pkgs.cliphist}/bin/cliphist wipe'";
|
|
ExecStart = "${pkgs.wl-clipboard}/bin/wl-paste --watch ${pkgs.cliphist}/bin/cliphist -db-path %t/cliphist/db store";
|
|
Restart = "on-failure";
|
|
RestartSec = 3;
|
|
};
|
|
};
|
|
|
|
systemd.user.services.cliphist-image = {
|
|
description = "Clipboard history manager (image)";
|
|
partOf = [ "graphical-session.target" ];
|
|
after = [ "cliphist-text.service" ];
|
|
wantedBy = [ "graphical-session.target" ];
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
ExecStart = "${pkgs.wl-clipboard}/bin/wl-paste --type image --watch ${pkgs.cliphist}/bin/cliphist -db-path %t/cliphist/db store";
|
|
Restart = "on-failure";
|
|
RestartSec = 3;
|
|
};
|
|
};
|
|
|
|
# Night light stays disabled by default: it is a user toggle. Enabling it
|
|
# here would create a system-scope symlink that overrides any
|
|
# `systemctl --user disable`.
|
|
systemd.user.services.wlsunset = {
|
|
description = "Wlsunset night light (blue light filter)";
|
|
partOf = [ "graphical-session.target" ];
|
|
after = [ "graphical-session.target" "kanshi.service" ];
|
|
wantedBy = [ ];
|
|
serviceConfig = {
|
|
Type = "simple";
|
|
ExecStartPre = "${pkgs.coreutils}/bin/sleep 2";
|
|
ExecStart = "${lib.getExe pkgs.wlsunset} -T 6500 -t 5000 -S 00:00 -s 00:01";
|
|
Restart = "on-failure";
|
|
RestartSec = 3;
|
|
};
|
|
};
|
|
|
|
# The polkit agent is not a service here: niri spawns it via
|
|
# spawn-at-startup in defaults/xdg/niri/config.kdl. Only the FHS path it
|
|
# references has to exist — see modules/desktop.nix.
|
|
}
|