bspwm: add module

PR #362, #981

Co-authored-by: Vincent Breitmoser <look@my.amazin.horse>
This commit is contained in:
Néfix Estrada 2018-08-28 15:46:24 +02:00 committed by Robert Helgesson
parent 244d795325
commit b270fcef2f
No known key found for this signature in database
GPG key ID: 36BDAA14C2797E89
4 changed files with 301 additions and 0 deletions

View file

@ -1295,6 +1295,14 @@ in
A new module is available: 'services.cbatticon'.
'';
}
{
time = "2020-01-26T12:42:33+00:00";
condition = hostPlatform.isLinux;
message = ''
A new module is available: 'xsession.windowManager.bspwm'.
'';
}
];
};
}

View file

@ -149,6 +149,7 @@ let
(loadModule ./services/unclutter.nix { })
(loadModule ./services/unison.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/window-managers/awesome.nix { })
(loadModule ./services/window-managers/bspwm/default.nix { condition = hostPlatform.isLinux; })
(loadModule ./services/window-managers/i3.nix { })
(loadModule ./services/window-managers/xmonad.nix { })
(loadModule ./services/xcape.nix { condition = hostPlatform.isLinux; })

View file

@ -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}";
};
}

View file

@ -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"
];
};
};
}