aboutsummaryrefslogtreecommitdiffstats
path: root/modules/by-name/ri
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xmodules/by-name/ri/river/init_base.sh33
-rw-r--r--modules/by-name/ri/river/keymap.nix192
-rw-r--r--modules/by-name/ri/river/module.nix283
-rw-r--r--modules/by-name/ri/river/river-start/package.nix23
-rwxr-xr-xmodules/by-name/ri/river/river-start/river-start.sh19
5 files changed, 547 insertions, 3 deletions
diff --git a/modules/by-name/ri/river/init_base.sh b/modules/by-name/ri/river/init_base.sh
new file mode 100755
index 00000000..edd7827f
--- /dev/null
+++ b/modules/by-name/ri/river/init_base.sh
@@ -0,0 +1,33 @@
+#!/usr/bin/env sh
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+RIVER_LOG_FILE="$HOME/.cache/river/init-log"
+
+echo >"$RIVER_LOG_FILE"
+
+err_fail() {
+ if ! "$@"; then
+ output=""
+ for arg in "$@"; do
+ if [ -z "$output" ]; then
+ output="'$arg'"
+ else
+ output="$output '$arg'"
+ fi
+ done
+ printf "%s failed!\n" "$output" >>"$RIVER_LOG_FILE"
+ fi
+}
+exec 1>>"$RIVER_LOG_FILE"
+exec 2>>"$RIVER_LOG_FILE"
+
+# Start of the generated stuff.
diff --git a/modules/by-name/ri/river/keymap.nix b/modules/by-name/ri/river/keymap.nix
new file mode 100644
index 00000000..897d11df
--- /dev/null
+++ b/modules/by-name/ri/river/keymap.nix
@@ -0,0 +1,192 @@
+{
+ lib,
+ pkgs,
+ libraries,
+ config,
+ ...
+}: let
+ index2tag = input: builtins.toString (libraries.base.pow 2 (input - 1));
+
+ mkTagCommand = name: index: [name (index2tag index)];
+ mkSpawn' = pkg: binaryName: args: further: (further
+ // (let
+ maybeSpace =
+ if args == ""
+ then ""
+ else " ";
+ maybeQuote =
+ if args == ""
+ then ""
+ else "\"";
+ in {
+ command = [
+ "spawn"
+ "${lib.getExe pkgs.notify-run} \"${lib.getExe' pkg binaryName}\"${maybeSpace}${maybeQuote}${args}${maybeQuote}"
+ ];
+
+ description = "${binaryName}${maybeSpace}${args}";
+ }));
+
+ mkSpawn = pkg: args: further: (mkSpawn' pkg pkg.meta.mainProgram args further);
+
+ setMode = path: {
+ command = [
+ "enter-mode"
+ (builtins.concatStringsSep "" path)
+ ];
+ };
+
+ cfg = config.soispha.programs.river;
+ inherit (config.soispha.services.mpd.mpc) mppPackage;
+in {
+ soispha.programs.river.init.mappings.keymap = lib.mkIf cfg.enable (lib.fixedPoints.fix
+ (self: {
+ "<LEFT_SUPER>" = {
+ # Spawn standard programs
+ "r" = {
+ "a" = mkSpawn' pkgs.foot "footclient" "" {};
+ "b" = mkSpawn pkgs.tskm "open select" {once = true;};
+ "k" = mkSpawn pkgs.keepassxc "" {once = true;};
+ # "s" = mkSpawn pkgs.signal-desktop "" {once = true;};
+ "p" = mkSpawn pkgs.screenshot_persistent "" {once = true;};
+ "i" = mkSpawn pkgs.anki "" {once = true;};
+ };
+
+ # Client changes
+ "c" = {
+ "F" = setMode ["<LEFT_META>" "f"];
+
+ "f" = ["toggle-fullscreen"];
+ "c" = ["close"];
+ " " = ["toggle-float"];
+ "<ENTER>" = ["zoom"];
+ };
+
+ # This is a fill in for <Super-L>, as that is otherwise nearly impossible to input.
+ "l" = self."<LEFT_SUPER>".x.l;
+
+ # River compositor control.
+ "x" = {
+ "q" = ["exit"];
+ "l" = mkSpawn pkgs.lock "" {once = true;};
+ "h" = mkSpawn pkgs.systemd-toggle "--user i3bar-river.service" {once = true;};
+ };
+
+ # Media control
+ "m" = {
+ "u" = mkSpawn' pkgs.wireplumber "wpctl" "set-volume @DEFAULT_SINK@ 5%+" {};
+ "d" = mkSpawn' pkgs.wireplumber "wpctl" "set-volume @DEFAULT_SINK@ 5%-" {};
+ "m" = mkSpawn' pkgs.wireplumber "wpctl" "set-mute @DEFAULT_SINK@ toggle" {};
+ "n" = mkSpawn mppPackage "next" {};
+ "p" = mkSpawn mppPackage "prev" {};
+ "t" = mkSpawn mppPackage "toggle" {};
+ "g" = mkSpawn' pkgs.mpdpopm "mpdpopm" "rating inc" {once = true;};
+ "b" = mkSpawn' pkgs.mpdpopm "mpdpopm" "rating decr" {once = true;};
+ };
+
+ # Select tags for view.
+ "v" = {
+ "c" = setMode ["<LEFT_META>" "c"];
+ "f" = setMode ["<LEFT_META>" "f"];
+
+ "t" = ["swap" "next"];
+ "n" = ["swap" "previous"];
+ "T" = ["send-to-output" "next"];
+ "N" = ["send-to-output" "previous"];
+
+ "0" = ["set-view-tags" (builtins.toString ((libraries.base.pow 2 32) - 1))];
+ "p" = ["send-to-previous-tags"];
+
+ "1" = mkTagCommand "set-view-tags" 1;
+ "2" = mkTagCommand "set-view-tags" 2;
+ "3" = mkTagCommand "set-view-tags" 3;
+ "4" = mkTagCommand "set-view-tags" 4;
+ "5" = mkTagCommand "set-view-tags" 5;
+ "6" = mkTagCommand "set-view-tags" 6;
+ "7" = mkTagCommand "set-view-tags" 7;
+ "8" = mkTagCommand "set-view-tags" 8;
+ "9" = mkTagCommand "set-view-tags" 9;
+
+ # Add tags to view.
+ "a" = {
+ "1" = mkTagCommand "toggle-view-tags" 1;
+ "2" = mkTagCommand "toggle-view-tags" 2;
+ "3" = mkTagCommand "toggle-view-tags" 3;
+ "4" = mkTagCommand "toggle-view-tags" 4;
+ "5" = mkTagCommand "toggle-view-tags" 5;
+ "6" = mkTagCommand "toggle-view-tags" 6;
+ "7" = mkTagCommand "toggle-view-tags" 7;
+ "8" = mkTagCommand "toggle-view-tags" 8;
+ "9" = mkTagCommand "toggle-view-tags" 9;
+ };
+ };
+
+ # Select tags to focus,
+ # and change focus
+ "f" = {
+ "c" = setMode ["<LEFT_META>" "c"];
+ "v" = setMode ["<LEFT_META>" "v"];
+
+ "t" = ["focus-view" "next"];
+ "n" = ["focus-view" "previous"];
+ "T" = ["focus-output" "next"];
+ "N" = ["focus-output" "previous"];
+
+ "0" = ["set-focused-tags" (builtins.toString ((libraries.base.pow 2 32) - 1))];
+ "p" = ["focus-previous-tags"];
+
+ "1" = mkTagCommand "set-focused-tags" 1;
+ "2" = mkTagCommand "set-focused-tags" 2;
+ "3" = mkTagCommand "set-focused-tags" 3;
+ "4" = mkTagCommand "set-focused-tags" 4;
+ "5" = mkTagCommand "set-focused-tags" 5;
+ "6" = mkTagCommand "set-focused-tags" 6;
+ "7" = mkTagCommand "set-focused-tags" 7;
+ "8" = mkTagCommand "set-focused-tags" 8;
+ "9" = mkTagCommand "set-focused-tags" 9;
+ };
+ };
+
+ # Screen locking
+ "<Super-l>" = self."<LEFT_SUPER>".x.l;
+
+ # Audio
+ "<MEDIA_RAISEVOLUME>" =
+ self."<LEFT_SUPER>".m.u
+ // {
+ allow_locked = true;
+ };
+ "<MEDIA_LOWERVOLUME>" =
+ self."<LEFT_SUPER>".m.d
+ // {
+ allow_locked = true;
+ };
+ "<MEDIA_MUTEVOLUME>" =
+ self."<LEFT_SUPER>".m.m
+ // {
+ allow_locked = true;
+ };
+ "<MEDIA_TRACKNEXT>" =
+ self."<LEFT_SUPER>".m.n
+ // {
+ allow_locked = true;
+ };
+ "<MEDIA_TRACKPREVIOUS>" =
+ self."<LEFT_SUPER>".m.p
+ // {
+ allow_locked = true;
+ };
+ "<MEDIA_PLAY>" =
+ self."<LEFT_SUPER>".m.t
+ // {
+ allow_locked = true;
+ };
+
+ # Mouse
+ "<Alt-<MOUSE_LEFT>>" = ["move-view"];
+ "<Alt-<MOUSE_RIGHT>>" = ["resize-view"];
+
+ # Screenshot
+ "<PRINTSCREEN>" = self."<LEFT_SUPER>".r.p;
+ }));
+}
diff --git a/modules/by-name/ri/river/module.nix b/modules/by-name/ri/river/module.nix
index a059da4d..f9ecea0f 100644
--- a/modules/by-name/ri/river/module.nix
+++ b/modules/by-name/ri/river/module.nix
@@ -1,21 +1,298 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
{
config,
lib,
- qmk_firmware,
system,
+ pkgs,
+ externalBinaries,
...
}: let
cfg = config.soispha.programs.river;
+ esa = lib.strings.escapeShellArg;
+ riverctl = lib.getExe' cfg.package "riverctl";
+
+ river-start = pkgs.callPackage ./river-start/package.nix {};
+
+ mkOutputFlags = output: flags: let
+ expandedFlags = builtins.concatStringsSep " " (lib.attrsets.mapAttrsToList (flag: value: "--${esa flag} ${esa value}") flags);
+ in ''
+ err_fail ${lib.getExe pkgs.wlr-randr} --output ${esa output} ${expandedFlags}
+ '';
+ screenSetupCode = builtins.concatStringsSep "" (lib.attrsets.mapAttrsToList mkOutputFlags cfg.init.screenSetupCode);
+
+ mkLrProgram = input: let
+ program = builtins.concatStringsSep " " (
+ if lib.isDerivation input
+ then [(lib.getExe input)]
+ else builtins.map esa input
+ );
+ in "err_fail ${program} &";
+ longRunningPrograms =
+ builtins.concatStringsSep "\n" (builtins.map mkLrProgram
+ cfg.init.autoStart);
+
+ keymapFormat = pkgs.formats.json {};
+ keymapGenerate = name: value:
+ pkgs.runCommandLocal "mk-${name}-and-check" {
+ nativeBuildInputs = [pkgs.river-mk-keymap];
+ preferLocalBuild = true;
+
+ env = {
+ JSON_FILE = keymapFormat.generate name value;
+ };
+ } ''
+ river-mk-keymap --keymap "$JSON_FILE" init --dry-run;
+ cp "$JSON_FILE" "$out"
+ '';
+
+ keymappings = ''
+ err_fail ${riverctl} keyboard-layout ${esa cfg.init.mappings.layout}
+ err_fail ${lib.getExe pkgs.river-mk-keymap} --keymap ${keymapGenerate "keys.json" cfg.init.mappings.keymap} init
+ '';
+
+ mkRule = {
+ app-id,
+ title,
+ action,
+ }: ''
+ err_fail ${riverctl} rule-add -app-id ${esa app-id} -title ${esa title} ${esa action}
+ '';
+ ruleSetup = builtins.concatStringsSep "" (builtins.map mkRule cfg.init.rules);
+
+ mkSetting = name: maybe_values: let
+ rawValues =
+ if builtins.isString maybe_values
+ then [maybe_values]
+ else maybe_values;
+ values = builtins.concatStringsSep " " (builtins.map esa rawValues);
+ in ''
+ err_fail ${riverctl} ${esa name} ${values}
+ '';
+ generalSettings =
+ builtins.concatStringsSep "" (lib.attrsets.mapAttrsToList mkSetting
+ cfg.init.generalSettings);
+
+ mkInput = name: arguments:
+ builtins.concatStringsSep "" (builtins.map (argumentLine: mkSetting "input" ([name] ++ argumentLine)) arguments);
+ inputs =
+ builtins.concatStringsSep "" (lib.attrsets.mapAttrsToList mkInput cfg.init.inputs);
in {
options.soispha.programs.river = {
enable = lib.mkEnableOption "river";
+
+ package = lib.mkPackageOption pkgs "river-classic" {};
+
unicodeInput = {
enable = lib.mkEnableOption "udev rules for rawhid based unicode input";
};
+
+ init = {
+ mappings = {
+ layout = lib.mkOption {
+ type = lib.types.str;
+ description = "The keymap to use";
+ default = "dvorak-modified";
+ };
+
+ keymap = lib.mkOption {
+ type = lib.types.submodule {
+ freeformType = keymapFormat.type;
+
+ options = {};
+ };
+ default = {};
+
+ description = ''
+ Configuration for river-mk-keymap via `keys.json`.
+ '';
+ };
+ };
+
+ rules = lib.mkOption {
+ type = lib.types.listOf (lib.types.attrsOf lib.types.str);
+ default = [];
+
+ example = ''
+ [
+ {
+ app-id = "*";
+ title = "floating please";
+ action = "float";
+ }
+ {
+ app-id = "*";
+ title = "*";
+ action = "ssd";
+ }
+ ]
+ '';
+
+ description = ''
+ Configuration for river's rules.
+ '';
+ };
+
+ generalSettings = lib.mkOption {
+ type = lib.types.attrsOf (lib.types.either (lib.types.listOf lib.types.str) lib.types.str);
+ description = "Simple key value settings.";
+ default = {};
+ example = ''
+ {
+ background-color = "0x002b36";
+ set-repeat = ["50" "300"];
+ hide-cursor = ["when-typing" "enabled"];
+ }
+ '';
+ };
+
+ inputs = lib.mkOption {
+ type = lib.types.attrsOf (lib.types.listOf (lib.types.listOf lib.types.str));
+ description = "Options to set per input device";
+ default = {};
+ example = ''
+ {
+ pointer-1133-49970-Logitech_Gaming_Mouse_G502 = [["pointer-accel" "0"] ["accel-profile" "none"]];
+ pointer-12951-6505-ZSA_Technology_Labs_Moonlander_Mark_I = [["pointer-accel" "0"] ["accel-profile" "none"]];
+ }
+ '';
+ };
+
+ autoStart = lib.mkOption {
+ type =
+ lib.types.listOf (lib.types.either lib.types.package (lib.types.listOf
+ lib.types.str));
+ description = "List of programs to be started at river start";
+ example = ''
+ [
+ ''${lib.getExe pkgs.foot}
+ ]
+ '';
+ };
+
+ screenSetupCode = lib.mkOption {
+ type = lib.types.attrsOf (lib.types.attrsOf lib.types.str);
+ default = {};
+ description = ''
+ `wlr-randr` flags to set up outputs. The attribute names are the `--output` keys
+ and the attrs are flag value pairs to setup.
+ '';
+ example = ''
+ {
+ "Virtual-1" = {mode = "1920x1080";};
+ "DP-2" = {pos = "2560,0";};
+ "DP-1" = {scale = "1.5"; pos = "0,0";};
+ }
+ '';
+ };
+ };
};
+ imports = [
+ ./keymap.nix
+ ];
+
config = lib.mkIf cfg.enable {
- # TODO: Migrate the complete river module <2024-12-30>
- services.udev.packages = lib.mkIf cfg.unicodeInput.enable [qmk_firmware.packages.${system}.qmk_unicode_type];
+ services.udev.packages = lib.mkIf cfg.unicodeInput.enable [externalBinaries.qmk_firmware.packages.${system}.qmk_unicode_type];
+
+ home-manager.users.soispha = {
+ xdg.desktopEntries."river" = {
+ name = "river";
+ exec = lib.getExe river-start;
+ comment = "A non-monolithic Wayland compositor";
+
+ settings = {
+ X-DesktopNames = "river";
+ Type = "Application";
+ };
+ };
+
+ home.sessionVariables = {
+ WM = "river";
+ XDG_CURRENT_DESKTOP = "river";
+ DESKTOP_SESSION = "river";
+
+ # Export Wayland env Vars {{{
+ QT_QPA_PLATFORM = "wayland";
+ QT_QPA_PLATFORMTHEME = "qt5ct"; # needs qt5ct
+ CLUTTER_BACKEND = "wayland";
+ SDL_VIDEODRIVER = "wayland"; # might brake some things
+ # }}}
+ };
+
+ home.packages = [
+ river-start
+ pkgs.sw
+ ];
+
+ xdg.configFile."river/init" = {
+ executable = true;
+ text = let
+ mkHeading = text: other_stuff: ''
+ # ${text}
+ ${other_stuff}
+ '';
+
+ sessionVars =
+ (builtins.attrNames config.environment.sessionVariables)
+ ++ (builtins.attrNames config.home-manager.users.soispha.home.sessionVariables)
+ ++ [
+ "WAYLAND_DISPLAY"
+ "DISPLAY"
+ "XDG_RUNTIME_DIR"
+ ];
+
+ part = acc: vars: let
+ firstTen = lib.lists.take 5 vars;
+ in
+ if firstTen == []
+ then acc
+ else part (acc ++ [firstTen]) (lib.lists.removePrefix firstTen vars);
+
+ partedSessionVars = part [] sessionVars;
+
+ mkEnvSet = prefix: vars: let
+ stringVars = builtins.concatStringsSep " " vars;
+ in ''err_fail ${riverctl} spawn "${prefix} ${stringVars}"'';
+
+ dbusEnvs =
+ builtins.map
+ (mkEnvSet "${lib.getExe' pkgs.dbus "dbus-update-activation-environment"} --verbose --systemd")
+ partedSessionVars;
+ systemdUserEnvs =
+ builtins.map
+ (mkEnvSet "systemctl --user --verbose import-environment")
+ partedSessionVars;
+ in
+ builtins.readFile ./init_base.sh
+ +
+ # bash
+ mkHeading "Environment variables" ''
+ export XDG_CURRENT_DESKTOP=river DESKTOP_SESSION=river;
+ ''
+ + mkHeading "Informing dbus about changed env-vars" (builtins.concatStringsSep "\n" dbusEnvs)
+ + mkHeading "Informing systemd user about changed env-vars" (builtins.concatStringsSep "\n" systemdUserEnvs)
+ + mkHeading "Starting graphical-session-target" ''
+ err_fail systemctl --user start nixos-fake-graphical-session.target
+ ''
+ + mkHeading "Key Mappings" keymappings
+ + mkHeading "Rules" ruleSetup
+ + mkHeading "General Settings" generalSettings
+ + mkHeading "Input Section" inputs
+ + mkHeading "Screen setup code" screenSetupCode
+ + mkHeading "Background services" longRunningPrograms
+ + mkHeading "Layout Setup" ''
+ err_fail ${riverctl} default-layout rivertile
+ ${lib.getExe' cfg.package "rivertile"} -main-ratio 0.5 -view-padding 1 -outer-padding 0
+ '';
+ };
+ };
};
}
diff --git a/modules/by-name/ri/river/river-start/package.nix b/modules/by-name/ri/river/river-start/package.nix
new file mode 100644
index 00000000..3ae204b7
--- /dev/null
+++ b/modules/by-name/ri/river/river-start/package.nix
@@ -0,0 +1,23 @@
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+{
+ writeShellApplication,
+ river-classic,
+}:
+writeShellApplication {
+ name = "river-start";
+ text = builtins.readFile ./river-start.sh;
+ runtimeInputs = [
+ river-classic
+ ];
+ meta = {
+ mainProgram = "river-start";
+ };
+}
diff --git a/modules/by-name/ri/river/river-start/river-start.sh b/modules/by-name/ri/river/river-start/river-start.sh
new file mode 100755
index 00000000..55eddfcf
--- /dev/null
+++ b/modules/by-name/ri/river/river-start/river-start.sh
@@ -0,0 +1,19 @@
+#! /usr/bin/env sh
+
+# nixos-config - My current NixOS configuration
+#
+# Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# This file is part of my nixos-config.
+#
+# You should have received a copy of the License along with this program.
+# If not, see <https://www.gnu.org/licenses/gpl-3.0.txt>.
+
+RIVER_LOG_FILE="$HOME/.cache/river/wm-log"
+
+[ -d "$(dirname "$RIVER_LOG_FILE")" ] || mkdir --parents "$(dirname "$RIVER_LOG_FILE")"
+
+exec river -log-level info >"$RIVER_LOG_FILE" 2>&1
+
+# vim: ft=sh