nix/modules/stuff/i3.nix

335 lines
13 KiB
Nix

{ lib, pkgs, config, ...}: let
inherit (lib) mkIf mkEnableOption mkOption types optionals optionalAttrs;
cfg = config.chuj.stuff.i3;
user = config.chuj.system.user;
in {
options.chuj.stuff.i3 = {
enable = mkEnableOption "i3";
workspaceOutput = mkOption {
type = types.attrsOf types.str;
default = {};
};
communicators = mkOption {
type = types.submodule {
options = {
autostart = mkOption {
type = types.bool;
default = false;
};
workspace = mkOption {
type = types.str;
default = "";
};
};
};
default = {};
};
battery = mkEnableOption "battery";
mediaKeys = mkOption {
type = types.submodule {
options = let
mkKeyOpt = default: mkOption { inherit default; type = types.str; };
in {
musicMenu = mkKeyOpt "F5";
musicPlay = mkKeyOpt "F7";
musicPrev = mkKeyOpt "F6";
musicNext = mkKeyOpt "F8";
audioMute = mkKeyOpt "F9";
audioDown = mkKeyOpt "F10";
audioUp = mkKeyOpt "F11";
};
};
default = {};
};
};
config = let
hmCfg = config.home-manager.users.${user};
exec = what: "exec --no-startup-id ${what}";
i3-msg = lib.getExe' hmCfg.xsession.windowManager.i3.package "i3-msg";
terminal = if (config.chuj.stuff.alacritty.enable)
then (lib.getExe hmCfg.programs.alacritty.package)
else (lib.getExe' hmCfg.xsession.windowManager.i3.package "i3-sensible-terminal");
firefox = lib.getExe hmCfg.programs.firefox.package;
schildichat = lib.getExe pkgs.schildichat-desktop;
flameshot = lib.getExe hmCfg.services.flameshot.package;
dunst = lib.getExe' hmCfg.services.dunst.package "dunst";
dunstify = lib.getExe' hmCfg.services.dunst.package "dunstify";
dmenu_run = lib.getExe' pkgs.dmenu "dmenu_run";
dmenu = lib.getExe pkgs.dmenu;
yad = lib.getExe pkgs.yad;
mpc = lib.getExe pkgs.mpc-cli;
mpdstatus = lib.getExe config.chuj.stuff.mpd.mpdstatusPackage;
pamixer = lib.getExe pkgs.pamixer;
xdotool = lib.getExe pkgs.xdotool;
xkill = lib.getExe pkgs.xorg.xkill;
killall = lib.getExe pkgs.killall;
sync = lib.getExe' pkgs.coreutils "sync";
tail = lib.getExe' pkgs.coreutils "tail";
find = lib.getExe' pkgs.findutils "find";
sed = lib.getExe pkgs.gnused;
i3custom-window-actions = lib.getExe (pkgs.writeShellScriptBin "i3custom-window-actions" ''
${yad} --splash --mouse \
--title="i3 window actions" \
--gtkrc="${../../files/i3gtk.css}" \
--text="i3 window actions" \
--button float:0 --button kill:1
case $? in
0) ${i3-msg} "floating toggle";;
1) ${i3-msg} "kill";;
-1) ;;
esac
'');
musicsel = lib.getExe (pkgs.writeShellScriptBin "musicsel" ''
cd ~/Music
path=$(${find} . -maxdepth 1 -type d | ${tail} -n +2 | ${sed} 's|^./||g' | ${dmenu})
${dunstify} "playing from \"$path\""
${mpc} update
${mpc} rescan
${mpc} add "$path"
${mpc} random on
${mpc} repeat on
${mpc} play
'');
i3custom-center-mouse-on-focus = lib.getExe (pkgs.writeShellScriptBin "i3custom-center-mouse-on-focus" ''
eval $(${xdotool} getwindowfocus getwindowgeometry --shell)
MX=$(($X + $WIDTH / 2))
MY=$(($Y + $HEIGHT / 2))
${xdotool} mousemove $MX $MY
'');
in mkIf cfg.enable {
services.xserver.windowManager.i3.enable = true;
home-manager.users.${user} = {
xsession.windowManager.i3 = {
enable = true;
config = rec {
assigns = mkIf (cfg.communicators.workspace != "") {
${cfg.communicators.workspace} = [
{ class = "discord"; }
{ class = "SchildiChat"; }
{ class = "element"; }
];
};
window = {
border = 1;
titlebar = true;
};
floating = {
border = 1;
titlebar = true;
criteria = [
{ class = "zenity"; }
{ class = "fceux"; }
{ title = "Friends List"; } # steam
];
};
gaps = {
inner = 0;
outer = 0;
};
fonts = {
names = [ "monospace" ];
size = 10.0;
};
defaultWorkspace = "workspace number 1";
# transforms
# { "<workspace>" = "<output>"; }
# into
# [{ workspace = "<workspace>"; output = "<output>"; }]
workspaceOutputAssign = lib.mapAttrsToList
(workspace: output: { inherit workspace output; })
cfg.workspaceOutput;
focus = {
followMouse = true;
mouseWarping = true;
newWindow = "urgent";
};
startup = []
++ optionals config.chuj.stuff.dunst.enable [{ command = "${dunst} &"; notification = false; }]
++ optionals config.chuj.stuff.flameshot.enable [{ command = "${flameshot} &"; notification = false; }]
++ optionals cfg.communicators.autostart [{ command = "${schildichat} &"; notification = false; }]
++ optionals config.chuj.stuff.mpd.enable [{ command = "${mpdstatus} &"; notification = false; }];
modes = {
resize = {
"Left" = "resize shrink width 10 px or 10 ppt; ${exec i3custom-center-mouse-on-focus}";
"Right" = "resize grow width 10 px or 10 ppt; ${exec i3custom-center-mouse-on-focus}";
"Up" = "resize shrink height 10 px or 10 ppt; ${exec i3custom-center-mouse-on-focus}";
"Down" = "resize grow height 10 px or 10 ppt; ${exec i3custom-center-mouse-on-focus}";
"Escape" = "mode default";
};
};
modifier = "Mod4";
floating.modifier = "Mod4";
keybindings = with config.chuj.stuff.i3.mediaKeys; let
mod = modifier;
update_mpdstatus = "${killall} -USR1 mpdstatus && ${sync} ${config.chuj.stuff.mpd.statusFile}";
update_i3status = "${killall} -USR1 i3status";
center_mouse = exec i3custom-center-mouse-on-focus;
in {
# Program keybinds
"${mod}+Return" = exec terminal;
"${mod}+d" = exec dmenu_run;
"${mod}+Shift+s" = mkIf config.chuj.stuff.flameshot.enable (exec "${flameshot} gui");
"${mod}+b" = mkIf config.chuj.stuff.firefox.enable (exec firefox);
"--release ${mod}+k" = exec xkill;
# Volume control
"Ctrl+${audioMute}" = exec "${pamixer} --toggle-mute && ${update_i3status}";
"Ctrl+${audioDown}" = exec "${pamixer} --decrease 5 && ${update_i3status}";
"Ctrl+${audioUp}" = exec "${pamixer} --increase 5 && ${update_i3status}";
# General i3 keybindings
"${mod}+Shift+q" = "kill; ${center_mouse}";
"${mod}+f" = "fullscreen toggle; ${center_mouse}";
"${mod}+r" = "mode resize";
"${mod}+Shift+r" = "restart";
"--border button3" = exec i3custom-window-actions;
"${mod}+Left" = "focus left; ${center_mouse}";
"${mod}+Right" = "focus right; ${center_mouse}";
"${mod}+Up" = "focus up; ${center_mouse}";
"${mod}+Down" = "focus down; ${center_mouse}";
"${mod}+Shift+Left" = "move left; ${center_mouse}";
"${mod}+Shift+Right" = "move right; ${center_mouse}";
"${mod}+Shift+Up" = "move up; ${center_mouse}";
"${mod}+Shift+Down" = "move down; ${center_mouse}";
"${mod}+q" = "layout stacking; ${center_mouse}";
"${mod}+w" = "layout tabbed; ${center_mouse}";
"${mod}+e" = "layout toggle split; ${center_mouse}";
"${mod}+Shift+space" = "floating toggle; ${center_mouse}";
"${mod}+space" = "focus mode_toggle; ${center_mouse}";
"${mod}+1" = "workspace number 1; ${center_mouse}";
"${mod}+2" = "workspace number 2; ${center_mouse}";
"${mod}+3" = "workspace number 3; ${center_mouse}";
"${mod}+4" = "workspace number 4; ${center_mouse}";
"${mod}+5" = "workspace number 5; ${center_mouse}";
"${mod}+6" = "workspace number 6; ${center_mouse}";
"${mod}+7" = "workspace number 7; ${center_mouse}";
"${mod}+8" = "workspace number 8; ${center_mouse}";
"${mod}+9" = "workspace number 9; ${center_mouse}";
"${mod}+0" = "workspace number 10; ${center_mouse}";
"${mod}+Shift+1" = "move container to workspace number 1; ${center_mouse}";
"${mod}+Shift+2" = "move container to workspace number 2; ${center_mouse}";
"${mod}+Shift+3" = "move container to workspace number 3; ${center_mouse}";
"${mod}+Shift+4" = "move container to workspace number 4; ${center_mouse}";
"${mod}+Shift+5" = "move container to workspace number 5; ${center_mouse}";
"${mod}+Shift+6" = "move container to workspace number 6; ${center_mouse}";
"${mod}+Shift+7" = "move container to workspace number 7; ${center_mouse}";
"${mod}+Shift+8" = "move container to workspace number 8; ${center_mouse}";
"${mod}+Shift+9" = "move container to workspace number 9; ${center_mouse}";
"${mod}+Shift+0" = "move container to workspace number 10; ${center_mouse}";
} // (optionalAttrs config.chuj.stuff.mpd.enable {
# MPD controls
"Ctrl+${musicMenu}" = exec "${musicsel} && ${update_mpdstatus} && ${update_i3status}";
"Ctrl+${musicPrev}" = exec "${mpc} prev && ${update_mpdstatus} && ${update_i3status}";
"Ctrl+${musicNext}" = exec "${mpc} next && ${update_mpdstatus} && ${update_i3status}";
"Ctrl+${musicPlay}" = exec "${mpc} toggle && ${update_mpdstatus} && ${update_i3status}";
"Ctrl+Shift+${musicPlay}" = exec "${mpc} clear && ${update_mpdstatus} && ${update_i3status}";
"Ctrl+Shift+${musicPrev}" = exec "${mpc} seek -3";
"Ctrl+Shift+${musicNext}" = exec "${mpc} seek +3";
"Ctrl+Shift+${audioDown}" = exec ''${mpc} volume -5 && ${dunstify} "mpc $(${mpc} volume)" -r 420'';
"Ctrl+Shift+${audioUp}" = exec ''${mpc} volume +5 && ${dunstify} "mpc $(${mpc} volume)" -r 420'';
});
bars = [{
inherit fonts;
trayOutput = "primary";
statusCommand = "${lib.getExe hmCfg.programs.i3status.package}";
extraConfig = ''
height 24
'';
}];
};
};
programs.i3status = {
enable = true;
enableDefault = false;
general = {
interval = 1;
colors = true;
color_good = "#91d956";
color_bad = "#f43841";
color_degraded = "#ffdd33";
output_format = "i3bar";
};
modules = {
"cpu_usage" = {
position = 0;
settings = {
format = "cpu: %usage";
};
};
"memory" = {
position = 1;
settings = {
format = "mem: %used";
threshold_degraded = "1G";
format_degraded = "MEMORY < %available";
};
};
"volume master" = {
position = 2;
settings = {
color_degraded = "#333333";
format = "vol: %volume";
format_muted = "vol: ---";
};
};
"tztime local" = {
position = 4;
settings = {
format = "%Y-%m-%d %H:%M:%S (%A)";
};
};
} // (optionalAttrs cfg.battery {
"battery 0" = {
position = 3;
settings = {
format = "bat%status: %percentage";
status_chr = "(+)";
status_bat = "";
status_unk = "(?)";
status_full = "(+)";
status_idle = "";
low_threshold = 30;
};
};
}) // (optionalAttrs config.chuj.stuff.mpd.enable {
"read_file mpd" = {
position = -1;
settings = {
format = "%content";
format_bad = "mpdstatus file not found";
path = config.chuj.stuff.mpd.statusFile;
color_good = "#ffffff";
};
};
});
};
};
};
}