nix-config/modules/home/wm/i3/default.nix
Bruno BELANYI b7b6705391
All checks were successful
ci/woodpecker/push/check Pipeline was successful
home: wm: i3: make 'arandr' float
Another work-around due to a wrapper in nixpkgs.
2024-12-18 20:48:09 -05:00

391 lines
14 KiB
Nix

{ config, lib, pkgs, ... }:
let
isEnabled = config.my.home.wm.windowManager == "i3";
terminal =
if config.my.home.terminal.program != null
then config.my.home.terminal.program
else "i3-sensible-terminal";
alt = "Mod1"; # `Alt` key
modifier = "Mod4"; # `Super` key
movementKeys = [ "Left" "Down" "Up" "Right" ];
vimMovementKeys = [ "h" "j" "k" "l" ];
shutdownMode =
"(l)ock, (e)xit, switch_(u)ser, (h)ibernate, (r)eboot, (Shift+s)hutdown";
# Takes an attrset of bindings for movement keys, transforms it to Vim keys
toVimKeyBindings =
let
toVimKeys = builtins.replaceStrings movementKeys vimMovementKeys;
in
lib.my.renameAttrs toVimKeys;
# Takes an attrset of bindings for movement keys, add equivalent Vim keys
addVimKeyBindings = bindings: bindings // (toVimKeyBindings bindings);
# Generate an attrset of movement bindings, using the mapper function
genMovementBindings = f: addVimKeyBindings (lib.my.genAttrs' movementKeys f);
# Used in multiple scripts to show messages through keybindings
notify-send = lib.getExe pkgs.libnotify;
# Screen backlight management
changeBacklight = lib.getExe pkgs.ambroisie.change-backlight;
# Audio and volume management
changeAudio = lib.getExe pkgs.ambroisie.change-audio;
# Lock management
toggleXautolock =
let
systemctlUser = "${pkgs.systemd}/bin/systemctl --user";
notify = "${notify-send} -u low"
+ " -h string:x-canonical-private-synchronous:xautolock-toggle";
in
pkgs.writeScript "toggle-xautolock" ''
#!/bin/sh
if ${systemctlUser} is-active xautolock-session.service; then
${systemctlUser} stop --user xautolock-session.service
xset s off
${notify} "Disabled Xautolock"
else
${systemctlUser} start xautolock-session.service
xset s on
${notify} "Enabled Xautolock"
fi
'';
in
{
config = lib.mkIf isEnabled {
home.packages = with pkgs; [
ambroisie.dragger # drag-and-drop from the CLI
ambroisie.i3-get-window-criteria # little helper for i3 configuration
arandr # Used by a mapping
playerctl # Used by a mapping
xdotool # Used by 'rofi-rbw', in a mapping
];
xsession.windowManager.i3 = {
enable = true;
config = {
inherit modifier;
bars =
let
i3status-rs = lib.getExe config.programs.i3status-rust.package;
in
assert [ "top" ] == lib.attrNames config.programs.i3status-rust.bars;
[
{
statusCommand = "${i3status-rs} config-top.toml";
trayOutput = "primary";
position = "top";
colors = {
background = "#021215";
statusline = "#93a1a1";
separator = "#2aa198";
focusedWorkspace = {
border = "#2aa198";
background = "#073642";
text = "#eee895";
};
activeWorkspace = {
border = "#073642";
background = "#002b36";
text = "#839496";
};
inactiveWorkspace = {
border = "#002b36";
background = "#021215";
text = "#586e75";
};
urgentWorkspace = {
border = "#cb4b16";
background = "#dc322f";
text = "#fdf6e3";
};
};
fonts = {
names = [ "DejaVu Sans Mono" "FontAwesome5Free" ];
size = 8.0;
};
}
];
floating = {
inherit modifier;
criteria = [
{ class = "^tridactyl_editor$"; }
{ class = "^Blueman-.*$"; }
{ title = "^htop$"; }
{ class = "^Thunderbird$"; instance = "Mailnews"; window_role = "filterlist"; }
{ class = "^pavucontrol.*$"; }
{ class = "^Arandr$"; }
{ class = "^\\.blueman-manager-wrapped$"; }
{ class = "^\\.arandr-wrapped$"; }
];
};
focus = {
followMouse = true; # It is annoying sometimes, but useful enough to use
mouseWarping = true; # Let's moving around when switching screens
};
fonts = {
names = [ "DejaVu Sans Mono" ];
size = 8.0;
};
# I don't care for i3's default values, I specify them all explicitly
keybindings = lib.my.recursiveMerge [
{
# The basics
"${modifier}+Return" = "exec ${terminal} ${
lib.optionalString config.my.home.tmux.enable "-e tmux new-session"
}";
"${modifier}+Shift+Return" = "exec env TMUX=nil ${terminal}";
"${modifier}+Shift+q" = "kill";
"${modifier}+f" = "fullscreen toggle";
"${modifier}+Shift+c" = "reload";
"${modifier}+Shift+r" = "restart";
"${modifier}+Shift+e" =
"exec i3-nagbar -t warning -m 'Do you want to exit i3?' -b 'Yes' 'i3-msg exit'";
}
{
# Splits
"${modifier}+g" = "split h"; # Horizontally
"${modifier}+v" = "split v"; # Vertically
}
{
# Layouts
"${modifier}+s" = "layout stacking";
"${modifier}+w" = "layout tabbed";
"${modifier}+e" = "layout toggle split";
}
{
# Toggle tiling/floating
"${modifier}+Control+space" = "floating toggle";
# Change focus between tiling/floating
"${modifier}+space" = "focus mode_toggle";
# Center floating window
"${modifier}+c" = "move absolute position center";
# Center floating window
"${modifier}+Shift+s" = "sticky toggle";
}
{
# Focus parent container
"${modifier}+q" = "focus parent";
# Focus child container
"${modifier}+a" = "focus child";
}
(lib.optionalAttrs config.my.home.wm.rofi.enable {
# Rofi tools
"${modifier}+d" = "exec rofi -show drun -disable-history";
"${modifier}+Shift+d" = "exec rofi -show run -disable-history";
"${modifier}+p" = "exec --no-startup-id flameshot gui";
"${modifier}+Ctrl+p" = "exec ${lib.getExe pkgs.rofi-rbw}";
"${modifier}+Shift+p" = "exec rofi -show emoji";
"${modifier}+b" =
let
inherit (config.my.home.bluetooth) enable;
prog = lib.getExe pkgs.rofi-bluetooth;
in
lib.mkIf enable "exec ${prog} -i";
})
(
# Changing container focus
genMovementBindings (
key: lib.nameValuePair
"${modifier}+${key}"
"focus ${lib.toLower key}"
)
)
(
# Changing screen focus
genMovementBindings (
key: lib.nameValuePair
"${modifier}+${alt}+${key}"
"focus output ${lib.toLower key}"
)
)
(
# Moving workspace to another screen
genMovementBindings (
key: lib.nameValuePair
"${modifier}+${alt}+Control+${key}"
"move workspace to output ${lib.toLower key}"
)
)
(
# Moving container to another screen
genMovementBindings (
key: lib.nameValuePair
"${modifier}+${alt}+Shift+${key}"
"move container to output ${lib.toLower key}"
)
)
(addVimKeyBindings {
# Scroll through workspaces on given screen
"${modifier}+Control+Left" = "workspace prev_on_output";
"${modifier}+Control+Right" = "workspace next_on_output";
# Use scratchpad
"${modifier}+Control+Up" = "move to scratchpad";
"${modifier}+Control+Down" = "scratchpad show";
})
(
# Moving floating window
genMovementBindings (
key: lib.nameValuePair
"${modifier}+Shift+${key}"
"move ${lib.toLower key} 10 px"
)
)
{
# Media keys
"XF86AudioRaiseVolume" = "exec --no-startup-id ${changeAudio} up 5";
"XF86AudioLowerVolume" = "exec --no-startup-id ${changeAudio} down 5";
"Control+XF86AudioRaiseVolume" = "exec --no-startup-id ${changeAudio} up 1";
"Control+XF86AudioLowerVolume" = "exec --no-startup-id ${changeAudio} down 1";
"Shift+XF86AudioRaiseVolume" = "exec --no-startup-id ${changeAudio} up --force 5";
"Shift+XF86AudioLowerVolume" = "exec --no-startup-id ${changeAudio} down --force 5";
"Control+Shift+XF86AudioRaiseVolume" = "exec --no-startup-id ${changeAudio} up --force 1";
"Control+Shift+XF86AudioLowerVolume" = "exec --no-startup-id ${changeAudio} down --force 1";
"XF86AudioMute" = "exec --no-startup-id ${changeAudio} toggle";
"XF86AudioMicMute" = "exec --no-startup-id ${changeAudio} toggle mic";
"XF86AudioPlay" = "exec playerctl play-pause";
"XF86AudioNext" = "exec playerctl next";
"XF86AudioPrev" = "exec playerctl previous";
}
{
# Screen management
"XF86Display" = "exec arandr";
"XF86MonBrightnessUp" = "exec --no-startup-id ${changeBacklight} up 10";
"XF86MonBrightnessDown" = "exec --no-startup-id ${changeBacklight} down 10";
"Control+XF86MonBrightnessUp" = "exec --no-startup-id ${changeBacklight} up 1";
"Control+XF86MonBrightnessDown" = "exec --no-startup-id ${changeBacklight} down 1";
}
{
# Sub-modes
"${modifier}+r" = "mode resize";
"${modifier}+Shift+space" = "mode floating";
}
(lib.optionalAttrs config.my.home.wm.screen-lock.enable {
"${modifier}+x" = "exec ${toggleXautolock}";
})
(
let
execDunstctl = "exec ${pkgs.dunst}/bin/dunstctl";
in
lib.optionalAttrs config.my.home.wm.dunst.enable {
"${modifier}+minus" = "${execDunstctl} close";
"${modifier}+Shift+minus" = "${execDunstctl} close-all";
"${modifier}+equal" = "${execDunstctl} history-pop";
}
)
];
keycodebindings =
let
toKeycode = n: if n == 0 then 19 else n + 9;
createWorkspaceBindings = mapping: command:
let
createWorkspaceBinding = num:
lib.nameValuePair
"${mapping}+${toString (toKeycode num)}"
"${command} ${toString num}";
oneToNine = builtins.genList (x: x + 1) 9;
in
lib.my.genAttrs' oneToNine createWorkspaceBinding;
in
lib.my.recursiveMerge [
(createWorkspaceBindings modifier "workspace number")
(createWorkspaceBindings "${modifier}+Shift" "move container to workspace number")
{
"${modifier}+${toString (toKeycode 0)}" = ''mode "${shutdownMode}"'';
}
];
modes =
let
makeModeBindings = attrs: (addVimKeyBindings attrs) // {
"Escape" = "mode default";
"Return" = "mode default";
};
in
{
resize = makeModeBindings {
# Normal movements
"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";
# Small movements
"Control+Left" = "resize shrink width 1 px or 1 ppt";
"Control+Down" = "resize grow height 1 px or 1 ppt";
"Control+Up" = "resize shrink height 1 px or 1 ppt";
"Control+Right" = "resize grow width 1 px or 1 ppt";
# Big movements
"Shift+Left" = "resize shrink width 100 px or 100 ppt";
"Shift+Down" = "resize grow height 100 px or 100 ppt";
"Shift+Up" = "resize shrink height 100 px or 100 ppt";
"Shift+Right" = "resize grow width 100 px or 100 ppt";
};
floating = makeModeBindings {
# Normal movements
"Left" = "move left 10 px";
"Down" = "move down 10 px";
"Up" = "move up 10 px";
"Right" = "move right 10 px";
# Small movements
"Control+Left" = "move left 1 px";
"Control+Down" = "move down 1 px";
"Control+Up" = "move up 1 px";
"Control+Right" = "move right 1 px";
# Big movements
"Shift+Left" = "move left 100 px";
"Shift+Down" = "move down 100 px";
"Shift+Up" = "move up 100 px";
"Shift+Right" = "move right 100 px";
};
${shutdownMode} = makeModeBindings {
"l" = "exec --no-startup-id loginctl lock-session, mode default";
"s" = "exec --no-startup-id systemctl suspend, mode default";
"u" = "exec --no-startup-id dm-tool switch-to-greeter, mode default";
"e" = "exec --no-startup-id i3-msg exit, mode default";
"h" = "exec --no-startup-id systemctl hibernate, mode default";
"r" = "exec --no-startup-id systemctl reboot, mode default";
"Shift+s" = "exec --no-startup-id systemctl poweroff, mode default";
};
};
startup = [
# NOTE: rely on systemd user services instead...
];
window = {
commands = [
# Make htop window bigger
{
criteria = { title = "^htop$"; };
command = "resize set 80 ppt 80 ppt, move position center";
}
];
};
};
};
};
}