dotfiles/home/niri/default.nix

284 lines
10 KiB
Nix

{
pkgs,
scale ? null,
...
}: let
nautilus = "${pkgs.nautilus}/bin/nautilus";
playerctl = "${pkgs.playerctl}/bin/playerctl";
wpctl = "${pkgs.wireplumber}/bin/wpctl";
brightnessctl = "${pkgs.brightnessctl}/bin/brightnessctl";
# Spawn a foot terminal that the "duplicate window" keybind can later trace back to
# its shell. foot's --server mode makes every window report the *server's* PID to the
# compositor, so niri can't distinguish windows by PID. Instead we give each window a
# unique app-id ("footclient-<pid>") and export the same value as FOOT_APPID into its
# shell; -E hands footclient's environment (incl. FOOT_APPID) to the server-spawned
# shell. launch_terminal then finds the focused window's shell by matching FOOT_APPID.
spawn_terminal = pkgs.writeShellScriptBin "spawn_terminal" ''
ID="footclient-$$"
export FOOT_APPID="$ID"
exec ${pkgs.foot}/bin/footclient -E --app-id="$ID" "$@"
'';
launch_terminal = pkgs.writeShellScriptBin "launch_terminal" ''
function find_leaf_pid(){
local PID
local RET
local NAME
PID=$1
for CHILD_PID in $(pgrep -P "$PID"); do
RET=$(find_leaf_pid "$CHILD_PID")
if [ "$RET" != "" ]; then
PID="$RET"
fi
done
NAME=$(ps -p "$PID" -o comm=)
if [ "$NAME" == "zsh" ]; then
echo "$PID"
elif [ "$NAME" == "ssh" ]; then
echo "$PID"
fi
}
# foot runs in --server mode, so niri reports the shared server PID for every foot
# window and cannot tell them apart. Resolve the focused window to its own shell by
# matching the FOOT_APPID marker that spawn_terminal exports into each window: scan
# the server's direct children for the one whose environment carries our app-id.
FOCUSED=$(niri msg --json focused-window)
SERVER_PID=$(echo "$FOCUSED" | ${pkgs.jq}/bin/jq -r '.pid // empty')
APP_ID=$(echo "$FOCUSED" | ${pkgs.jq}/bin/jq -r '.app_id // empty')
ROOT_PID=""
if [ -n "$APP_ID" ] && [ -n "$SERVER_PID" ]; then
for CHILD in $(pgrep -P "$SERVER_PID"); do
if grep -qzF "FOOT_APPID=$APP_ID" "/proc/$CHILD/environ" 2>/dev/null; then
ROOT_PID="$CHILD"
break
fi
done
fi
# Focused window isn't one of our tagged foot terminals: just open a plain terminal.
if [ -z "$ROOT_PID" ]; then
exec ${spawn_terminal}/bin/spawn_terminal
fi
LEAF_PID=$(find_leaf_pid "$ROOT_PID")
if [ "$(ps -p "$LEAF_PID" -o comm=)" == "ssh" ]; then
SSH_COMMAND=$(ps -p "$LEAF_PID" -o args --no-headers)
if [[ "$SSH_COMMAND" == *"waypipe"* ]]; then
CLIENT=$(echo "$SSH_COMMAND" | awk '{ print $5 }')
PREVIOUS_SESSION_ID=$(grep -z "SSH_SESSION_ID" "/proc/$LEAF_PID/environ" | xargs -0 -n 1 | grep -oP "[0-9]*")
SSH_SESSION_ID=$RANDOM
${spawn_terminal}/bin/spawn_terminal zsh -c "SSH_SESSION_ID=$SSH_SESSION_ID waypipe ssh -t \"$CLIENT\" env SSH_SESSION_ID=\"$SSH_SESSION_ID\" PREVIOUS_SESSION_ID=\"$PREVIOUS_SESSION_ID\" \"zsh --login\""
else
${spawn_terminal}/bin/spawn_terminal zsh -c "ssh -t \"$(echo "$SSH_COMMAND" | awk '{ print $2 }')\""
fi
else # Not an ssh session: open a local terminal in the same directory.
CWD=$(readlink -e /proc/"$LEAF_PID"/cwd)
if [ "$CWD" != "" ]; then
${spawn_terminal}/bin/spawn_terminal --working-directory "$CWD"
else
${spawn_terminal}/bin/spawn_terminal
fi
fi
'';
in {
# Since native Home Manager doesn't have a programs.niri module yet,
# we generate the KDL configuration file directly.
home.packages = with pkgs; [
wl-clipboard
];
programs.hyprlock = {
enable = true;
settings = {
input-field = [
{
size = "200, 50";
position = "0, -80";
monitor = "";
dots_center = true;
fade_on_empty = true;
font_color = "rgb(202, 211, 245)";
inner_color = "rgb(91, 96, 120)";
outer_color = "rgb(24, 25, 38)";
outline_thickness = 3;
placeholder_text = "<span foreground=\"##cad3f5\">Password...</span>";
shadow_passes = 2;
}
];
general = {
no_fade_in = false;
};
background = [
{
path = "screenshot";
blur_passes = 3;
blur_size = 8;
}
];
};
};
xdg.configFile."niri/config.kdl".text = ''
spawn-at-startup "${pkgs.swaybg}/bin/swaybg" "-i" "${../hyprland/files/wallpaper.png}" "-m" "fill"
spawn-at-startup "foot" "--server"
spawn-at-startup "ags" "run"
spawn-at-startup "${pkgs.mate-polkit}/bin/polkit-mate"
prefer-no-csd
hotkey-overlay {
skip-at-startup
}
gestures {
hot-corners {
off
}
}
layout {
gaps 5
border {
off
}
focus-ring {
width 4
active-color "rgba(0, 47, 95, 238)"
inactive-color "rgba(255, 255, 255, 0)"
}
}
input {
mouse {
accel-speed 0.5
}
touchpad {
tap
}
keyboard {
xkb {
options "compose:rctrl"
}
}
focus-follows-mouse max-scroll-amount="10%"
warp-mouse-to-focus
}
output "HDMI-A-1" {
scale 1.0
position x=0 y=-2160
}
${
if scale != null
then ''
output "eDP-1" {
scale ${toString scale}
position x=0 y=0
}
''
else ""
} window-rule {
match app-id="vicinae"
open-floating true
}
binds {
// General Binds
Mod+Return { spawn "${launch_terminal}/bin/launch_terminal"; }
Mod+Shift+Return { spawn "${spawn_terminal}/bin/spawn_terminal"; }
Mod+Q { close-window; }
Mod+A { spawn "${nautilus}"; }
Mod+B { spawn "Helium"; }
Mod+M { maximize-column; }
Mod+F {maximize-window-to-edges;}
Mod+Tab { toggle-overview; }
// Vicinae Binds
Mod+C { spawn "vicinae" "vicinae://launch/clipboard/history"; }
Mod+N { spawn "vicinae" "vicinae://launch/wm/switch-windows"; }
Mod+Escape { spawn "vicinae" "open" "-q" "Power Management "; }
Mod+Space { spawn "vicinae" "toggle"; }
Mod+Comma { consume-or-expel-window-left; }
Mod+Period { consume-or-expel-window-right; }
Mod+Alt+H { set-column-width "-5%"; }
Mod+Alt+L { set-column-width "+5%"; }
Mod+Alt+K { set-window-height "-5%"; }
Mod+Alt+J { set-window-height "+5%"; }
// Movement
Mod+H { focus-column-or-monitor-left; }
Mod+J { focus-window-or-monitor-down; }
Mod+K { focus-window-or-monitor-up; }
Mod+L { focus-column-or-monitor-right; }
Mod+Shift+H { move-column-left-or-to-monitor-left; }
Mod+Shift+L { move-column-right-or-to-monitor-right; }
Mod+Shift+J { move-window-down-or-to-workspace-down; }
Mod+Shift+K { move-window-up-or-to-workspace-up; }
// Move window to the other monitor
Mod+Ctrl+K { move-window-to-monitor-up; }
Mod+Ctrl+J { move-window-to-monitor-down; }
// Workspaces
Mod+Shift+U { move-column-to-workspace-down; }
Mod+Shift+I { move-column-to-workspace-up; }
Mod+I { focus-workspace-up; }
Mod+U { focus-workspace-down; }
Mod+1 { focus-workspace 1; }
Mod+2 { focus-workspace 2; }
Mod+3 { focus-workspace 3; }
Mod+4 { focus-workspace 4; }
Mod+5 { focus-workspace 5; }
Mod+6 { focus-workspace 6; }
Mod+7 { focus-workspace 7; }
Mod+8 { focus-workspace 8; }
Mod+9 { focus-workspace 9; }
// Move to workspace
Mod+Shift+1 { move-column-to-workspace 1; }
Mod+Shift+2 { move-column-to-workspace 2; }
Mod+Shift+3 { move-column-to-workspace 3; }
Mod+Shift+4 { move-column-to-workspace 4; }
Mod+Shift+5 { move-column-to-workspace 5; }
Mod+Shift+6 { move-column-to-workspace 6; }
Mod+Shift+7 { move-column-to-workspace 7; }
Mod+Shift+8 { move-column-to-workspace 8; }
Mod+Shift+9 { move-column-to-workspace 9; }
Print { screenshot; }
Ctrl+Alt+L {spawn "${pkgs.hyprlock}/bin/hyprlock"; }
XF86MonBrightnessUp { spawn "${brightnessctl}" "set" "+10%"; }
XF86MonBrightnessDown { spawn "${brightnessctl}" "set" "10%-"; }
// Volume
XF86AudioRaiseVolume { spawn "${wpctl}" "set-volume" "@DEFAULT_AUDIO_SINK@" "10%+" "--limit" "1.0"; }
XF86AudioLowerVolume { spawn "${wpctl}" "set-volume" "@DEFAULT_AUDIO_SINK@" "10%-"; }
XF86AudioMute { spawn "${wpctl}" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; }
XF86AudioMicMute { spawn "${wpctl}" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; }
// Horizontal mouse scroll controls volume
WheelScrollRight cooldown-ms=10 { spawn "${wpctl}" "set-volume" "@DEFAULT_AUDIO_SINK@" "1%+" "--limit" "1.0"; }
WheelScrollLeft cooldown-ms=10 { spawn "${wpctl}" "set-volume" "@DEFAULT_AUDIO_SINK@" "1%-"; }
// Mod + horizontal scroll moves between windows (columns) left/right
Mod+WheelScrollRight cooldown-ms=200 { focus-column-right; }
Mod+WheelScrollLeft cooldown-ms=200 { focus-column-left; }
// Mod + vertical scroll moves between workspaces up/down
Mod+WheelScrollDown cooldown-ms=100 { focus-workspace-down; }
Mod+WheelScrollUp cooldown-ms=100 { focus-workspace-up; }
// Media
XF86AudioNext { spawn "${playerctl}" "next"; }
XF86AudioPrev { spawn "${playerctl}" "previous"; }
Mod+XF86AudioMute { spawn "${playerctl}" "play-pause"; }
Mod+XF86AudioRaiseVolume { spawn "${playerctl}" "next"; }
Mod+XF86AudioLowerVolume { spawn "${playerctl}" "previous"; }
}
'';
}