{ 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 node --close 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" ]; }; }; }