aboutsummaryrefslogtreecommitdiff
path: root/modules/services/window-managers
diff options
context:
space:
mode:
authorNéfix Estrada <nefixestrada@gmail.com>2018-08-28 15:46:24 +0200
committerRobert Helgesson <robert@rycee.net>2020-01-26 13:43:13 +0100
commitb270fcef2fdbf9144c1f4b3528d35b455e116082 (patch)
treeccbb19c2e9520654f0876dbb4a698938fd8bbda0 /modules/services/window-managers
parent244d7953252617073a83b95ffc4dd1f6e1fae803 (diff)
bspwm: add module
PR #362, #981 Co-authored-by: Vincent Breitmoser <look@my.amazin.horse>
Diffstat (limited to 'modules/services/window-managers')
-rw-r--r--modules/services/window-managers/bspwm/default.nix71
-rw-r--r--modules/services/window-managers/bspwm/options.nix221
2 files changed, 292 insertions, 0 deletions
diff --git a/modules/services/window-managers/bspwm/default.nix b/modules/services/window-managers/bspwm/default.nix
new file mode 100644
index 00000000000..e9fa064a8ba
--- /dev/null
+++ b/modules/services/window-managers/bspwm/default.nix
@@ -0,0 +1,71 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.xsession.windowManager.bspwm;
+ bspwm = cfg.package;
+
+ camelToSnake = s:
+ builtins.replaceStrings lib.upperChars (map (c: "_${c}") lib.lowerChars) s;
+
+ formatConfig = n: v:
+ let
+ formatList = x:
+ if isList x
+ then throw "can not convert 2-dimensional lists to bspwm format"
+ else formatValue x;
+
+ formatValue = v:
+ if isBool v then (if v then "true" else "false")
+ else if isList v then concatMapStringsSep ", " formatList v
+ else if isString v then "${lib.strings.escapeShellArg v}"
+ else toString v;
+ in
+ "bspc config ${n} ${formatValue v}";
+
+ formatMonitors = n: v: "bspc monitor ${n} -d ${concatStringsSep " " v}";
+
+ formatRules = target: directiveOptions:
+ let
+ formatDirective = n: v:
+ if isBool v then (if v then "${camelToSnake n}=on" else "${camelToSnake n}=off")
+ else if (n == "desktop" || n == "node") then "${camelToSnake n}='${v}'"
+ else "${camelToSnake n}=${lib.strings.escapeShellArg v}";
+
+ directives = filterAttrs (n: v: v != null && !(lib.strings.hasPrefix "_" n)) directiveOptions;
+ directivesStr = builtins.concatStringsSep " " (mapAttrsToList formatDirective directives);
+ in
+ "bspc rule -a ${target} ${directivesStr}";
+
+ formatStartupPrograms = map (s: "${s} &");
+
+in
+
+{
+ options = import ./options.nix { inherit pkgs; inherit lib; };
+
+ config = mkIf cfg.enable {
+ home.packages = [ bspwm ];
+ xsession.windowManager.command =
+ let
+ configFile = pkgs.writeShellScript "bspwmrc" (
+ concatStringsSep "\n" (
+ (mapAttrsToList formatMonitors cfg.monitors)
+ ++ (mapAttrsToList formatConfig cfg.settings)
+ ++ (mapAttrsToList formatRules cfg.rules)
+ ++ [ ''
+ # java gui fixes
+ export _JAVA_AWT_WM_NONREPARENTING=1
+ bspc rule -a sun-awt-X11-XDialogPeer state=floating
+ '' ]
+ ++ [ cfg.extraConfig ]
+ ++ (formatStartupPrograms cfg.startupPrograms)
+ )
+ );
+ configCmdOpt = optionalString (cfg.settings != null) "-c ${configFile}";
+ in
+ "${cfg.package}/bin/bspwm ${configCmdOpt}";
+ };
+}
diff --git a/modules/services/window-managers/bspwm/options.nix b/modules/services/window-managers/bspwm/options.nix
new file mode 100644
index 00000000000..9d1955aa57c
--- /dev/null
+++ b/modules/services/window-managers/bspwm/options.nix
@@ -0,0 +1,221 @@
+{ pkgs, lib }:
+
+with lib;
+
+let
+
+ rule = types.submodule {
+ options = {
+ monitor = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = "The monitor where the rule should be applied.";
+ example = "HDMI-0";
+ };
+
+ desktop = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = "The desktop where the rule should be applied.";
+ example = "^8";
+ };
+
+ node = mkOption {
+ type = types.nullOr types.str;
+ default = null;
+ description = "The node where the rule should be applied.";
+ example = "1";
+ };
+
+ state = mkOption {
+ type = types.nullOr (types.enum [ "tiled" "pseudo_tiled" "floating" "fullscreen" ]);
+ default = null;
+ description = "The state in which a new window should spawn.";
+ example = "floating";
+ };
+
+ layer = mkOption {
+ type = types.nullOr (types.enum [ "below" "normal" "above" ]);
+ default = null;
+ description = "The layer where a new window should spawn.";
+ example = "above";
+ };
+
+ splitDir = mkOption {
+ type = types.nullOr (types.enum [ "north" "west" "south" "east" ]);
+ default = null;
+ description = "The direction where the container is going to be split.";
+ example = "south";
+ };
+
+ splitRatio = mkOption {
+ type = types.nullOr types.float;
+ default = null;
+ description = ''
+ The ratio between the new window and the previous existing window in
+ the desktop.
+ '';
+ example = 0.65;
+ };
+
+ hidden = mkOption {
+ type = types.nullOr types.bool;
+ default = null;
+ description = ''Whether the node should occupy any space.'';
+ example = true;
+ };
+
+ sticky = mkOption {
+ type = types.nullOr types.bool;
+ default = null;
+ description = "Whether the node should stay on the focused desktop.";
+ example = true;
+ };
+
+ private = mkOption {
+ type = types.nullOr types.bool;
+ default = null;
+ description = ''
+ Whether the node should stay in the same tiling position and size.
+ '';
+ example = true;
+ };
+
+ locked = mkOption {
+ type = types.nullOr types.bool;
+ default = null;
+ description = ''
+ Whether the node should ignore <command>node --close</command>
+ messages.
+ '';
+ example = true;
+ };
+
+ marked = mkOption {
+ type = types.nullOr types.bool;
+ default = null;
+ description = "Whether the node will be marked for deferred actions.";
+ example = true;
+ };
+
+ center = mkOption {
+ type = types.nullOr types.bool;
+ default = null;
+ description = ''
+ Whether the node will be put in the center, in floating mode.
+ '';
+ example = true;
+ };
+
+ follow = mkOption {
+ type = types.nullOr types.bool;
+ default = null;
+ description = "Whether focus should follow the node when it is moved.";
+ example = true;
+ };
+
+ manage = mkOption {
+ type = types.nullOr types.bool;
+ default = null;
+ description = ''
+ Whether the window should be managed by bspwm. If false, the window
+ will be ignored by bspwm entirely. This is useful for overlay apps,
+ e.g. screenshot tools.
+ '';
+ example = true;
+ };
+
+ focus = mkOption {
+ type = types.nullOr types.bool;
+ default = null;
+ description = "Whether the node should gain focus on creation.";
+ example = true;
+ };
+
+ border = mkOption {
+ type = types.nullOr types.bool;
+ default = null;
+ description = "Whether the node should have border.";
+ example = true;
+ };
+ };
+ };
+
+in
+
+{
+ xsession.windowManager.bspwm = {
+ enable = mkEnableOption "bspwm window manager.";
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.bspwm;
+ defaultText = literalExample "pkgs.bspwm";
+ description = "bspwm package to use.";
+ example = literalExample "pkgs.bspwm-unstable";
+ };
+
+ settings = mkOption {
+ type = with types; let
+ primitive = either bool (either int (either float str));
+ in
+ attrsOf (either primitive (listOf primitive));
+ default = {};
+ description = "bspwm configuration";
+ example = {
+ "border_width" = 2;
+ "split_ratio" = 0.52;
+ "gapless_monocle" = true;
+ };
+ };
+
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Additional configuration to add.";
+ example = ''
+ bspc subscribe all > ~/bspc-report.log &
+ '';
+ };
+
+ monitors = mkOption {
+ type = types.attrsOf (types.listOf types.str);
+ default = {};
+ description = "bspc monitor configurations";
+ example = {
+ "HDMI-0" = [ "web" "terminal" "III" "IV" ];
+ };
+ };
+
+ rules = mkOption {
+ type = types.attrsOf rule;
+ default = {};
+ description = "bspc rules";
+ example = literalExample ''
+ {
+ "Gimp" = {
+ desktop = "^8";
+ state = "floating";
+ follow = true;
+ };
+ "Kupfer.py" = {
+ focus = true;
+ };
+ "Screenkey" = {
+ manage = false;
+ };
+ }
+ '';
+ };
+
+ startupPrograms = mkOption {
+ type = types.listOf types.str;
+ default = [];
+ description = "Programs to be executed during startup.";
+ example = [
+ "numlockx on"
+ "tilda"
+ ];
+ };
+ };
+}