bspwm: add module
PR #362, #981 Co-authored-by: Vincent Breitmoser <look@my.amazin.horse>
This commit is contained in:
parent
244d795325
commit
b270fcef2f
|
@ -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'.
|
||||
'';
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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; })
|
||||
|
|
71
modules/services/window-managers/bspwm/default.nix
Normal file
71
modules/services/window-managers/bspwm/default.nix
Normal 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}";
|
||||
};
|
||||
}
|
221
modules/services/window-managers/bspwm/options.nix
Normal file
221
modules/services/window-managers/bspwm/options.nix
Normal 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"
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue