From 2fe8b0071127d5c17610edd6f3e7e7b18e274e58 Mon Sep 17 00:00:00 2001 From: Asmir A Date: Mon, 27 Jul 2026 23:39:24 +0200 Subject: [PATCH] home/fuji: add river wm --- fuji/configuration.nix | 19 ++++++- home/common/river.nix | 111 +++++++++++++++++++++++++++++++++++++++++ home/fuji/home.nix | 4 +- 3 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 home/common/river.nix diff --git a/fuji/configuration.nix b/fuji/configuration.nix index f039c94..093b0d9 100644 --- a/fuji/configuration.nix +++ b/fuji/configuration.nix @@ -8,6 +8,19 @@ }: let USER = "akill"; + + river-session = pkgs.runCommand "river-session" + { passthru.providedSessions = [ "river" ]; } + '' + install -Dm444 ${pkgs.writeText "river.desktop" '' + [Desktop Entry] + Name=River + Comment=A non-monolithic Wayland compositor + Exec=${lib.getExe pkgs.river} + Type=Application + DesktopNames=river + ''} $out/share/wayland-sessions/river.desktop + ''; in { imports = [ ./wireguard.nix ]; @@ -206,6 +219,7 @@ in systemPackages = with pkgs; [ maliit-keyboard android-tools + river ]; sessionVariables = { @@ -242,7 +256,6 @@ in enable = true; enableSSHSupport = true; }; - hyprland.enable = true; steam = { enable = true; remotePlay.openFirewall = true; @@ -262,6 +275,7 @@ in xdg.portal = { enable = true; wlr.enable = true; + config.river.default = [ "wlr" "gtk" ]; }; documentation.dev.enable = true; @@ -318,6 +332,8 @@ in upower.enable = lib.mkForce false; userborn.enable = true; + displayManager.sessionPackages = [ river-session ]; + greetd = { enable = true; settings = { @@ -328,6 +344,7 @@ in --remember \ --remember-session \ --greeting 'Welcome to NixOS' \ + --sessions ${config.services.displayManager.sessionData.desktops}/share/wayland-sessions \ --cmd sway ''; user = "greeter"; diff --git a/home/common/river.nix b/home/common/river.nix new file mode 100644 index 0000000..90bde8a --- /dev/null +++ b/home/common/river.nix @@ -0,0 +1,111 @@ +{ 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" ]; + }; + }; +} diff --git a/home/fuji/home.nix b/home/fuji/home.nix index 64c0869..c05f014 100644 --- a/home/fuji/home.nix +++ b/home/fuji/home.nix @@ -1,6 +1,8 @@ { config , lib , pkgs +, inputs +, system , ... }: let @@ -13,6 +15,7 @@ in ../common/zsh.nix ../common/i3status-rust.nix ../common/sway.nix + ../common/river.nix ../common/i3.nix ./home_packages.nix ../common/whatsapp-for-linux.nix @@ -69,7 +72,6 @@ in wayland.windowManager = { sway.enable = true; - hyprland.enable = false; }; programs = {