1
0
Fork 0
mirror of https://github.com/nix-community/home-manager.git synced 2025-12-05 16:41:04 +01:00
home-manager/modules/services/window-managers/i3-sway/i3.nix
Austin Horstman 82ee14ff60
treewide: remove with lib (#6871)
Remove from services.
2025-04-21 11:00:59 -05:00

313 lines
11 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkIf mkOption types;
cfg = config.xsession.windowManager.i3;
commonOptions = import ./lib/options.nix {
inherit
config
lib
cfg
pkgs
;
moduleName = "i3";
};
configModule = types.submodule {
options = {
inherit (commonOptions)
fonts
window
floating
focus
assigns
modifier
workspaceLayout
workspaceAutoBackAndForth
keycodebindings
colors
bars
startup
gaps
menu
terminal
defaultWorkspace
workspaceOutputAssign
;
keybindings = mkOption {
type = types.attrsOf (types.nullOr types.str);
default = lib.mapAttrs (n: lib.mkOptionDefault) {
"${cfg.config.modifier}+Return" = "exec ${cfg.config.terminal}";
"${cfg.config.modifier}+Shift+q" = "kill";
"${cfg.config.modifier}+d" = "exec ${cfg.config.menu}";
"${cfg.config.modifier}+Left" = "focus left";
"${cfg.config.modifier}+Down" = "focus down";
"${cfg.config.modifier}+Up" = "focus up";
"${cfg.config.modifier}+Right" = "focus right";
"${cfg.config.modifier}+Shift+Left" = "move left";
"${cfg.config.modifier}+Shift+Down" = "move down";
"${cfg.config.modifier}+Shift+Up" = "move up";
"${cfg.config.modifier}+Shift+Right" = "move right";
"${cfg.config.modifier}+h" = "split h";
"${cfg.config.modifier}+v" = "split v";
"${cfg.config.modifier}+f" = "fullscreen toggle";
"${cfg.config.modifier}+s" = "layout stacking";
"${cfg.config.modifier}+w" = "layout tabbed";
"${cfg.config.modifier}+e" = "layout toggle split";
"${cfg.config.modifier}+Shift+space" = "floating toggle";
"${cfg.config.modifier}+space" = "focus mode_toggle";
"${cfg.config.modifier}+a" = "focus parent";
"${cfg.config.modifier}+Shift+minus" = "move scratchpad";
"${cfg.config.modifier}+minus" = "scratchpad show";
"${cfg.config.modifier}+1" = "workspace number 1";
"${cfg.config.modifier}+2" = "workspace number 2";
"${cfg.config.modifier}+3" = "workspace number 3";
"${cfg.config.modifier}+4" = "workspace number 4";
"${cfg.config.modifier}+5" = "workspace number 5";
"${cfg.config.modifier}+6" = "workspace number 6";
"${cfg.config.modifier}+7" = "workspace number 7";
"${cfg.config.modifier}+8" = "workspace number 8";
"${cfg.config.modifier}+9" = "workspace number 9";
"${cfg.config.modifier}+0" = "workspace number 10";
"${cfg.config.modifier}+Shift+1" = "move container to workspace number 1";
"${cfg.config.modifier}+Shift+2" = "move container to workspace number 2";
"${cfg.config.modifier}+Shift+3" = "move container to workspace number 3";
"${cfg.config.modifier}+Shift+4" = "move container to workspace number 4";
"${cfg.config.modifier}+Shift+5" = "move container to workspace number 5";
"${cfg.config.modifier}+Shift+6" = "move container to workspace number 6";
"${cfg.config.modifier}+Shift+7" = "move container to workspace number 7";
"${cfg.config.modifier}+Shift+8" = "move container to workspace number 8";
"${cfg.config.modifier}+Shift+9" = "move container to workspace number 9";
"${cfg.config.modifier}+Shift+0" = "move container to workspace number 10";
"${cfg.config.modifier}+Shift+c" = "reload";
"${cfg.config.modifier}+Shift+r" = "restart";
"${cfg.config.modifier}+Shift+e" =
"exec i3-nagbar -t warning -m 'Do you want to exit i3?' -b 'Yes' 'i3-msg exit'";
"${cfg.config.modifier}+r" = "mode resize";
};
defaultText = "Default i3 keybindings.";
description = ''
An attribute set that assigns a key press to an action using a key symbol.
See <https://i3wm.org/docs/userguide.html#keybindings>.
Consider to use `lib.mkOptionDefault` function to extend or override
default keybindings instead of specifying all of them from scratch.
'';
example = lib.literalExpression ''
let
modifier = config.xsession.windowManager.i3.config.modifier;
in lib.mkOptionDefault {
"''${modifier}+Return" = "exec i3-sensible-terminal";
"''${modifier}+Shift+q" = "kill";
"''${modifier}+d" = "exec ''${pkgs.dmenu}/bin/dmenu_run";
}
'';
};
modes = mkOption {
type = types.attrsOf (types.attrsOf types.str);
default = {
resize = {
"Left" = "resize shrink width 10 px or 10 ppt";
"Down" = "resize grow height 10 px or 10 ppt";
"Up" = "resize shrink height 10 px or 10 ppt";
"Right" = "resize grow width 10 px or 10 ppt";
"Escape" = "mode default";
"Return" = "mode default";
};
};
description = ''
An attribute set that defines binding modes and keybindings
inside them
Only basic keybinding is supported (bindsym keycomb action),
for more advanced setup use 'i3.extraConfig'.
'';
};
};
};
commonFunctions = import ./lib/functions.nix {
inherit config cfg lib;
moduleName = "i3";
};
inherit (commonFunctions)
keybindingsStr
keycodebindingsStr
modeStr
assignStr
barStr
gapsStr
floatingCriteriaStr
windowCommandsStr
colorSetStr
windowBorderString
fontConfigStr
keybindingDefaultWorkspace
keybindingsRest
workspaceOutputStr
;
startupEntryStr =
{
command,
always,
notification,
workspace,
...
}:
lib.concatStringsSep " " [
(if always then "exec_always" else "exec")
(if (notification && workspace == null) then "" else "--no-startup-id")
(if (workspace == null) then command else "i3-msg 'workspace ${workspace}; exec ${command}'")
];
configFile = pkgs.writeText "i3.conf" (
lib.concatStringsSep "\n" (
(
if cfg.config != null then
with cfg.config;
(
[
(fontConfigStr fonts)
"floating_modifier ${floating.modifier}"
(windowBorderString window floating)
"hide_edge_borders ${window.hideEdgeBorders}"
"focus_wrapping ${focus.wrapping}"
"focus_follows_mouse ${lib.hm.booleans.yesNo focus.followMouse}"
"focus_on_window_activation ${focus.newWindow}"
"mouse_warping ${if focus.mouseWarping then "output" else "none"}"
"workspace_layout ${workspaceLayout}"
"workspace_auto_back_and_forth ${lib.hm.booleans.yesNo workspaceAutoBackAndForth}"
"client.focused ${colorSetStr colors.focused}"
"client.focused_inactive ${colorSetStr colors.focusedInactive}"
"client.unfocused ${colorSetStr colors.unfocused}"
"client.urgent ${colorSetStr colors.urgent}"
"client.placeholder ${colorSetStr colors.placeholder}"
"client.background ${colors.background}"
(keybindingsStr { keybindings = keybindingDefaultWorkspace; })
(keybindingsStr { keybindings = keybindingsRest; })
(keycodebindingsStr keycodebindings)
]
++ lib.mapAttrsToList (modeStr false) modes
++ lib.mapAttrsToList assignStr assigns
++ map barStr bars
++ lib.optional (gaps != null) gapsStr
++ map floatingCriteriaStr floating.criteria
++ map windowCommandsStr window.commands
++ map startupEntryStr startup
++ map workspaceOutputStr workspaceOutputAssign
)
else
[ ]
)
++ [ cfg.extraConfig ]
)
);
# Validates the i3 configuration
checkI3Config = pkgs.runCommandLocal "i3-config" { buildInputs = [ cfg.package ]; } ''
# We have to make sure the wrapper does not start a dbus session
export DBUS_SESSION_BUS_ADDRESS=1
# A zero exit code means i3 successfully validated the configuration
i3 -c ${configFile} -C -d all || {
echo "i3 configuration validation failed"
echo "For a verbose log of the failure, run 'i3 -c ${configFile} -C -d all'"
exit 1
};
cp ${configFile} $out
'';
in
{
meta.maintainers = with lib.maintainers; [ sumnerevans ];
options = {
xsession.windowManager.i3 = {
enable = lib.mkEnableOption "i3 window manager";
package = lib.mkPackageOption pkgs "i3" { nullable = true; };
config = mkOption {
type = types.nullOr configModule;
default = { };
description = "i3 configuration options.";
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = "Extra configuration lines to add to ~/.config/i3/config.";
};
};
};
config = mkIf cfg.enable (
lib.mkMerge [
{
assertions = [
(lib.hm.assertions.assertPlatform "xsession.windowManager.i3" pkgs lib.platforms.linux)
];
home.packages = lib.mkIf (cfg.package != null) [ cfg.package ];
xsession.windowManager.command = "${cfg.package}/bin/i3";
xdg.configFile."i3/config" = {
source = checkI3Config;
onChange = ''
# There may be several sockets after log out/log in, but the old ones
# will fail with "Connection refused".
for i3Socket in ''${XDG_RUNTIME_DIR:-/run/user/$UID}/i3/ipc-socket.*; do
if [[ -S $i3Socket ]]; then
${cfg.package}/bin/i3-msg -s $i3Socket reload >/dev/null |& grep -v "Connection refused" || true
fi
done
'';
};
}
(mkIf (cfg.config != null) {
warnings =
(lib.optional (lib.isList cfg.config.fonts) "Specifying i3.config.fonts as a list is deprecated. Use the attrset version instead.")
++ lib.flatten (
map (
b:
lib.optional (lib.isList b.fonts) "Specifying i3.config.bars[].fonts as a list is deprecated. Use the attrset version instead."
) cfg.config.bars
)
++ [
(mkIf (lib.any (s: s.workspace != null) cfg.config.startup) (
"'xsession.windowManager.i3.config.startup.*.workspace' is deprecated, "
+ "use 'xsession.windowManager.i3.config.assigns' instead."
+ "See https://github.com/nix-community/home-manager/issues/265."
))
(mkIf cfg.config.focus.forceWrapping (
"'xsession.windowManager.i3.config.focus.forceWrapping' is deprecated, "
+ "use 'xsession.windowManager.i3.config.focus.wrapping' instead."
))
];
})
]
);
}