Files
nixos_flake_config/home/common/river.nix
T
2026-07-27 23:39:24 +02:00

112 lines
4.4 KiB
Nix

{ pkgs
, inputs
, system
, lib
, ...
}:
let
quickshell_bar = inputs.quickshell_bar.packages.${system}.default;
att_wm = inputs.att_wm.packages.${system}.default;
cliphist_river = pkgs.writeShellScriptBin "cliphist_river" ''
${lib.getExe pkgs.cliphist} list | \
${lib.getExe pkgs.wofi} --dmenu --insensitive | \
${lib.getExe pkgs.cliphist} decode | \
${pkgs.wl-clipboard}/bin/wl-copy
'';
screenshot_clip = pkgs.writeShellScriptBin "screenshot_clip" ''
GEOM="$(${lib.getExe pkgs.slurp} -d)"
${lib.getExe pkgs.grim} -g "$GEOM" - | ${pkgs.wl-clipboard}/bin/wl-copy
'';
in
{
# att_wm is configured at compile time, dwm-style — there is no config file to
# generate here, and the bindings live in its own src/config.zig (Alt as mod,
# nine tags, master/monocle/tabbed). To override them without forking, build
# with a replacement config:
# inputs.att_wm.packages.${system}.default.override { configFile = ./config.zig; }
#
# It spawns `foot` and `wmenu-run` by bare name, so both have to be on PATH;
# cliphist_river and screenshot_clip are here for a future config.zig to spawn
# the same way. The package also carries att_wmctl, the CLI that drives the
# window manager over its IPC socket.
home.packages = [
att_wm
pkgs.wmenu
cliphist_river
screenshot_clip
];
# river runs $XDG_CONFIG_HOME/river/init at startup if it is executable, and
# that is the whole of river's configuration surface — it implements no
# window management itself, that is att_wm over river-window-management-v1.
xdg.configFile."river/init" = {
executable = true;
text = ''
#!${pkgs.runtimeShell}
# greetd discards the session's stdio, and river/att_wm log to stderr only
# without this a failing window manager is a silent blank screen.
log="''${XDG_STATE_HOME:-$HOME/.local/state}/river.log"
mkdir -p "$(dirname "$log")"
exec >>"$log" 2>&1
echo "=== river init $(date -Is) ==="
# river's session file sets no DesktopNames, so the portal backend
# (xdg.portal.config.river in fuji/configuration.nix) needs this.
export XDG_CURRENT_DESKTOP=river
export XDG_SESSION_TYPE=wayland
# Hand the session env to systemd/D-Bus, then bring up the target the
# user services hang off same dance the home-manager sway module does.
${pkgs.dbus}/bin/dbus-update-activation-environment --systemd \
DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP XDG_SESSION_TYPE \
NIXOS_OZONE_WL XCURSOR_THEME XCURSOR_SIZE
systemctl --user reset-failed
systemctl --user start river-session.target
# The bar (same one sway and hyprland use) picks its backend from the
# environment alone: with no $HYPRLAND_INSTANCE_SIGNATURE and no
# $SWAYSOCK/$I3SOCK it falls through to att_wm, so nothing needs setting
# here. It retries the connection every second, which is why starting it
# before att_wm is fine and why it must start after river exported
# WAYLAND_DISPLAY, since that name is part of the socket path. Both sides
# derive $XDG_RUNTIME_DIR/att_wm-$WAYLAND_DISPLAY.sock independently, so
# nothing here has to agree on it.
${quickshell_bar}/bin/quickshell-bar &
bar=$!
# att_wm comes from the flake input (git.project-cloud.net/asmir/att_wm),
# so the session runs exactly the revision flake.lock pins. Iterating on
# it now means `nix flake update att_wm` + rebuild; to test a working tree
# without that, run it under a nested river via the repo's run-nested.sh.
#
# Not exec'd: when att_wm exits we still want to tear the target down.
# att_wm's Alt+Ctrl+Shift+q (exit_session) ends river itself.
echo "starting ${lib.getExe att_wm}"
${lib.getExe att_wm}
echo "=== att_wm exited with $? at $(date -Is) ==="
# The bar is a child of this script rather than of att_wm, so it outlives
# it: reap it here so an att_wm exit doesn't leave a bar floating over a
# session with no window manager.
kill "$bar" 2>/dev/null || true
systemctl --user stop river-session.target
'';
};
systemd.user.targets.river-session = {
Unit = {
Description = "river compositor session";
Documentation = [ "man:systemd.special(7)" ];
BindsTo = [ "graphical-session.target" ];
Wants = [ "graphical-session-pre.target" ];
After = [ "graphical-session-pre.target" ];
};
};
}