From 6e9380d4269682bbf0c4c95335627fa9d1260309 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Wed, 10 Jul 2024 15:24:52 +0700 Subject: [PATCH] river: add systemd.runInService option Add a runInService option that allows users to run `systemctl --user start river`. This makes lifecycles of river be managed by systemd and fixes long shutdown times & graphics-session.target not stopping. --- modules/services/window-managers/river.nix | 70 +++++++++++++++---- .../services/window-managers/river/init | 7 +- 2 files changed, 58 insertions(+), 19 deletions(-) diff --git a/modules/services/window-managers/river.nix b/modules/services/window-managers/river.nix index 06d45841..6a788a57 100644 --- a/modules/services/window-managers/river.nix +++ b/modules/services/window-managers/river.nix @@ -1,16 +1,14 @@ { config, lib, pkgs, ... }: - let inherit (lib) types; cfg = config.wayland.windowManager.river; # Systemd integration variables = builtins.concatStringsSep " " cfg.systemd.variables; - extraCommands = builtins.concatStringsSep " " - (map (f: "&& ${f}") cfg.systemd.extraCommands); - systemdActivation = '' - ${pkgs.dbus}/bin/dbus-update-activation-environment --systemd ${variables} ${extraCommands} - ''; + systemdActivation = builtins.concatStringsSep " && " ([ + "${pkgs.dbus}/bin/dbus-update-activation-environment --systemd ${variables}" + ] ++ (lib.optional cfg.systemd.runInService "systemd-notify --ready") + ++ cfg.systemd.extraCommands); toValue = val: if lib.isString val || lib.isDerivation val then @@ -28,7 +26,6 @@ let # Intermediary function that converts some value (attrs, str, ...) to one or several commands. toArgs = path: value: - let stringValue = lib.concatStringsSep " " (path ++ [ (toValue value) ]); finalValue = if lib.isAttrs value then @@ -47,7 +44,6 @@ let toCommand = basePath: attrs: lib.concatLists (lib.mapAttrsToList (key: value: let path = basePath ++ [ key ]; in toArgs path value) attrs); - in { meta.maintainers = [ lib.maintainers.GaetanLepage ]; @@ -81,6 +77,25 @@ in { ''; }; + runInService = lib.mkEnableOption null // { + default = false; + description = '' + Whether river should run inside systemd (`true`) or outside of systemd + (`false`). + + Running inside systemd means river lifecycle is fully known/managed by + systemd. Stopping your computer or river crashing will stop the + appropriate targets and will make sure everything stays in sync. + + If river runs inside systemd, river logs will be available with + {command}`journalctl`. + + To start river, you will need to run + {command}`systemctl --user start river` + and not run it from the command line. + ''; + }; + variables = lib.mkOption { type = types.listOf types.str; default = [ @@ -100,10 +115,11 @@ in { extraCommands = lib.mkOption { type = types.listOf types.str; - default = [ + default = if (!cfg.systemd.runInService) then [ "systemctl --user stop river-session.target" "systemctl --user start river-session.target" - ]; + ] else + [ ]; description = "Extra commands to be run after D-Bus activation."; }; }; @@ -180,16 +196,17 @@ in { ### SHELL VARIABLES ### ${config.lib.shell.exportAll cfg.extraSessionVariables} + '' + (lib.optionalString cfg.systemd.enable '' + ### SYSTEMD INTEGRATION ### + ${systemdActivation} + '') + '' + ### CONFIGURATION ### ${lib.concatStringsSep "\n" (toCommand [ "riverctl" ] cfg.settings)} ### EXTRA CONFIGURATION ### ${cfg.extraConfig} - - '' + (lib.optionalString cfg.systemd.enable '' - ### SYSTEMD INTEGRATION ### - ${systemdActivation} - '')); + ''); # Systemd integration systemd.user.targets.river-session = lib.mkIf cfg.systemd.enable { @@ -197,11 +214,34 @@ in { Description = "river compositor session"; Documentation = [ "man:systemd.special(7)" ]; BindsTo = [ "graphical-session.target" ]; + Before = [ "graphical-session.target" ]; Wants = [ "graphical-session-pre.target" ]; After = [ "graphical-session-pre.target" ]; + RefuseManualStart = if cfg.systemd.runInService then "yes" else "no"; + StopWhenUnneeded = "yes"; }; }; + systemd.user.services.river = + lib.mkIf (cfg.systemd.enable && cfg.systemd.runInService) { + Unit = { + Description = "River compositor"; + Documentation = "man:river(1)"; + BindsTo = [ "river-session.target" ]; + Before = [ "river-session.target" ]; + }; + + Service = { + Type = "notify"; + # /bin/sh -lc is used to get env/session vars (and path). + ExecStart = "/bin/sh -lc ${pkgs.river}/bin/river"; + TimeoutStopSec = 10; + NotifyAccess = "all"; + ExecStopPost = + "${pkgs.systemd}/bin/systemctl --user unset-environment ${variables}"; + }; + }; + systemd.user.targets.tray = { Unit = { Description = "Home Manager System Tray"; diff --git a/tests/modules/services/window-managers/river/init b/tests/modules/services/window-managers/river/init index c266ca9e..6f341c25 100755 --- a/tests/modules/services/window-managers/river/init +++ b/tests/modules/services/window-managers/river/init @@ -6,6 +6,9 @@ export BAR="bar" export FOO="foo" export FOURTY_TWO="42" +### SYSTEMD INTEGRATION ### +@dbus@/bin/dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP NIXOS_OZONE_WL XCURSOR_THEME XCURSOR_SIZE && systemctl --user stop river-session.target && systemctl --user start river-session.target + ### CONFIGURATION ### riverctl attach-mode bottom riverctl background-color 0x002b36 @@ -61,7 +64,3 @@ some extra config -### SYSTEMD INTEGRATION ### -@dbus@/bin/dbus-update-activation-environment --systemd DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP NIXOS_OZONE_WL XCURSOR_THEME XCURSOR_SIZE && systemctl --user stop river-session.target && systemctl --user start river-session.target - -