From 89bdef7994a20b5285454bf42c7d2b53b98d5754 Mon Sep 17 00:00:00 2001 From: Olmo Kramer Date: Tue, 9 Nov 2021 23:17:36 +0100 Subject: [PATCH] herbstluftwm: init module --- .github/CODEOWNERS | 3 + modules/misc/news.nix | 8 + modules/modules.nix | 1 + .../services/window-managers/herbstluftwm.nix | 171 ++++++++++++++++++ tests/default.nix | 1 + .../window-managers/herbstluftwm/autostart | 48 +++++ .../window-managers/herbstluftwm/default.nix | 1 + .../herbstluftwm-simple-config.nix | 44 +++++ 8 files changed, 277 insertions(+) create mode 100644 modules/services/window-managers/herbstluftwm.nix create mode 100755 tests/modules/services/window-managers/herbstluftwm/autostart create mode 100644 tests/modules/services/window-managers/herbstluftwm/default.nix create mode 100644 tests/modules/services/window-managers/herbstluftwm/herbstluftwm-simple-config.nix diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 01c8b4be4..abddaa5a5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -357,6 +357,9 @@ /modules/services/window-managers/bspwm @ncfavier /tests/modules/services/window-managers/bspwm @ncfavier +/modules/services/window-managers/herbstluftwm @olmokramer +/tests/modules/services/window-managers/herbstluftwm @olmokramer + /modules/services/window-managers/i3-sway/i3.nix @sumnerevans /tests/modules/services/window-managers/i3 @sumnerevans diff --git a/modules/misc/news.nix b/modules/misc/news.nix index d910f9547..9c194d77a 100644 --- a/modules/misc/news.nix +++ b/modules/misc/news.nix @@ -2328,6 +2328,14 @@ in A new module is available: 'services.systembus-notify'. ''; } + + { + time = "2021-12-31T09:39:20+00:00"; + condition = hostPlatform.isLinux; + message = '' + A new module is available: 'xsession.windowManager.herbstluftwm'. + ''; + } ]; }; } diff --git a/modules/modules.nix b/modules/modules.nix index 258c329c2..534d9c55c 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -231,6 +231,7 @@ let ./services/volnoti.nix ./services/window-managers/awesome.nix ./services/window-managers/bspwm/default.nix + ./services/window-managers/herbstluftwm.nix ./services/window-managers/i3-sway/i3.nix ./services/window-managers/i3-sway/sway.nix ./services/window-managers/i3-sway/swaynag.nix diff --git a/modules/services/window-managers/herbstluftwm.nix b/modules/services/window-managers/herbstluftwm.nix new file mode 100644 index 000000000..efa38bce5 --- /dev/null +++ b/modules/services/window-managers/herbstluftwm.nix @@ -0,0 +1,171 @@ +{ config, lib, pkgs, ... }: + +let + + cfg = config.xsession.windowManager.herbstluftwm; + + renderValue = val: + if lib.isBool val then + if val then "true" else "false" + else + lib.escapeShellArg val; + + renderSettings = settings: + lib.concatStringsSep "\n" (lib.mapAttrsToList + (name: value: "herbstclient set ${name} ${renderValue value}") settings); + + renderKeybinds = keybinds: + lib.concatStringsSep "\n" + (lib.mapAttrsToList (key: cmd: "herbstclient keybind ${key} ${cmd}") + keybinds); + + renderMousebinds = mousebinds: + lib.concatStringsSep "\n" + (lib.mapAttrsToList (btn: cmd: "herbstclient mousebind ${btn} ${cmd}") + mousebinds); + + renderRules = rules: + lib.concatStringsSep "\n" (map (rule: "herbstclient rule ${rule}") rules); + + settingType = lib.types.oneOf [ lib.types.bool lib.types.int lib.types.str ]; + +in { + meta.maintainers = [ lib.hm.maintainers.olmokramer ]; + + options.xsession.windowManager.herbstluftwm = { + enable = lib.mkEnableOption "herbstluftwm window manager."; + + package = lib.mkOption { + type = lib.types.package; + default = pkgs.herbstluftwm; + defaultText = lib.literalExpression "pkgs.herbstluftwm"; + description = '' + Package providing the herbstluftwm and + herbstclient commands. + ''; + }; + + settings = lib.mkOption { + type = lib.types.attrsOf settingType; + default = { }; + example = lib.literalExpression '' + { + gapless_grid = false; + window_border_width = 1; + window_border_active_color = "#FF0000"; + } + ''; + description = "Herbstluftwm settings."; + }; + + keybinds = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = { }; + example = lib.literalExpression '' + { + Mod4-o = "split right"; + Mod4-u = "split bottom"; + } + ''; + description = "Herbstluftwm keybinds."; + }; + + mousebinds = lib.mkOption { + type = lib.types.attrsOf lib.types.str; + default = { }; + example = lib.literalExpression '' + { + Mod4-B1 = "move"; + Mod4-B3 = "resize"; + } + ''; + description = "Herbstluftwm mousebinds."; + }; + + rules = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + example = lib.literalExpression '' + [ + "windowtype~'_NET_WM_WINDOW_TYPE_(DIALOG|UTILITY|SPLASH)' focus=on pseudotile=on" + "windowtype~'_NET_WM_WINDOW_TYPE_(NOTIFICATION|DOCK|DESKTOP)' manage=off" + ] + ''; + description = "Herbstluftwm rules."; + }; + + tags = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + example = lib.literalExpression '' + [ "work" "browser" "music" "gaming" ] + ''; + description = "Tags to create on startup."; + }; + + extraConfig = lib.mkOption { + type = lib.types.lines; + default = ""; + example = '' + herbstclient set_layout max + herbstclient detect_monitors + ''; + description = '' + Extra configuration lines to add verbatim to + $XDG_CONFIG_HOME/herbstluftwm/autostart. + ''; + }; + }; + + config = lib.mkIf cfg.enable { + assertions = [ + (lib.hm.assertions.assertPlatform "xsession.windowManager.herbstluftwm" + pkgs lib.platforms.linux) + ]; + + home.packages = [ cfg.package ]; + + xsession.windowManager.command = "${cfg.package}/bin/herbstluftwm --locked"; + + xdg.configFile."herbstluftwm/autostart".source = + pkgs.writeShellScript "herbstluftwm-autostart" '' + shopt -s expand_aliases + + # shellcheck disable=SC2142 + alias herbstclient='set -- "$@" ";"' + set -- + + herbstclient emit_hook reload + + # Reset everything. + herbstclient attr theme.tiling.reset 1 + herbstclient attr theme.floating.reset 1 + herbstclient keyunbind --all + herbstclient unrule --all + + ${renderSettings cfg.settings} + + if ${cfg.package}/bin/herbstclient object_tree tags.by-name | ${pkgs.gnugrep}/bin/grep default; then + herbstclient rename default ${ + lib.escapeShellArg (builtins.head cfg.tags) + } + fi + + for tag in ${lib.escapeShellArgs cfg.tags}; do + herbstclient add "$tag" + done + + ${renderKeybinds cfg.keybinds} + + ${renderMousebinds cfg.mousebinds} + + ${renderRules cfg.rules} + + ${cfg.extraConfig} + + herbstclient unlock + + ${cfg.package}/bin/herbstclient chain ";" "$@" + ''; + }; +} diff --git a/tests/default.nix b/tests/default.nix index f1445ba6c..e2964179f 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -145,6 +145,7 @@ import nmt { ./modules/services/syncthing ./modules/services/trayer ./modules/services/window-managers/bspwm + ./modules/services/window-managers/herbstluftwm ./modules/services/window-managers/i3 ./modules/services/window-managers/sway ./modules/services/wlsunset diff --git a/tests/modules/services/window-managers/herbstluftwm/autostart b/tests/modules/services/window-managers/herbstluftwm/autostart new file mode 100755 index 000000000..bff4f16fc --- /dev/null +++ b/tests/modules/services/window-managers/herbstluftwm/autostart @@ -0,0 +1,48 @@ +#!/nix/store/00000000000000000000000000000000-bash-5.1-p12/bin/bash +shopt -s expand_aliases + +# shellcheck disable=SC2142 +alias herbstclient='set -- "$@" ";"' +set -- + +herbstclient emit_hook reload + +# Reset everything. +herbstclient attr theme.tiling.reset 1 +herbstclient attr theme.floating.reset 1 +herbstclient keyunbind --all +herbstclient unrule --all + +herbstclient set always_show_frame true +herbstclient set default_frame_layout 'max' +herbstclient set frame_bg_active_color '#000000' +herbstclient set frame_gap '12' +herbstclient set frame_padding '-12' + +if @herbstluftwm@/bin/herbstclient object_tree tags.by-name | /nix/store/00000000000000000000000000000000-gnugrep-3.7/bin/grep default; then + herbstclient rename default '1' +fi + +for tag in '1' 'with space' 'wə1rd#ch@rs'\'''; do + herbstclient add "$tag" +done + +herbstclient keybind Mod4-1 use 1 +herbstclient keybind Mod4-2 use 2 +herbstclient keybind Mod4-Alt-Tab cycle -1 +herbstclient keybind Mod4-Tab cycle 1 + +herbstclient mousebind Mod4-B1 move +herbstclient mousebind Mod4-B3 resize + +herbstclient rule focus=on +herbstclient rule windowtype~'_NET_WM_WINDOW_TYPE_(DIALOG|UTILITY|SPLASH)' focus=on pseudotile=on +herbstclient rule class~'[Pp]inentry' instance=pinentry focus=on floating=on floatplacement=center keys_inactive='.*' + +herbstclient use 1 + + +herbstclient unlock + +@herbstluftwm@/bin/herbstclient chain ";" "$@" + diff --git a/tests/modules/services/window-managers/herbstluftwm/default.nix b/tests/modules/services/window-managers/herbstluftwm/default.nix new file mode 100644 index 000000000..04b9f89c7 --- /dev/null +++ b/tests/modules/services/window-managers/herbstluftwm/default.nix @@ -0,0 +1 @@ +{ herbstluftwm-simple-config = ./herbstluftwm-simple-config.nix; } diff --git a/tests/modules/services/window-managers/herbstluftwm/herbstluftwm-simple-config.nix b/tests/modules/services/window-managers/herbstluftwm/herbstluftwm-simple-config.nix new file mode 100644 index 000000000..27e871505 --- /dev/null +++ b/tests/modules/services/window-managers/herbstluftwm/herbstluftwm-simple-config.nix @@ -0,0 +1,44 @@ +{ lib, pkgs, ... }: + +{ + xsession.windowManager.herbstluftwm = { + enable = true; + settings = { + always_show_frame = true; + default_frame_layout = "max"; + frame_bg_active_color = "#000000"; + frame_gap = 12; + frame_padding = -12; + }; + keybinds = { + "Mod4-1" = "use 1"; + "Mod4-2" = "use 2"; + "Mod4-Tab" = "cycle 1"; + "Mod4-Alt-Tab" = "cycle -1"; + }; + mousebinds = { + "Mod4-B1" = "move"; + "Mod4-B3" = "resize"; + }; + rules = [ + "focus=on" + "windowtype~'_NET_WM_WINDOW_TYPE_(DIALOG|UTILITY|SPLASH)' focus=on pseudotile=on" + "class~'[Pp]inentry' instance=pinentry focus=on floating=on floatplacement=center keys_inactive='.*'" + ]; + tags = [ "1" "with space" "wə1rd#ch@rs'" ]; + extraConfig = '' + herbstclient use 1 + ''; + }; + + test.stubs.herbstluftwm = { }; + + nmt.script = '' + autostart=home-files/.config/herbstluftwm/autostart + assertFileExists "$autostart" + assertFileIsExecutable "$autostart" + + normalizedAutostart=$(normalizeStorePaths "$autostart") + assertFileContent "$normalizedAutostart" ${./autostart} + ''; +}