nix-conf/system/modules/niri.nix
2026-01-10 12:46:40 +08:00

313 lines
10 KiB
Nix
Executable file

{
config,
pkgs,
lib,
...
}:
let
inherit (lib)
getExe
pipe
;
inherit (builtins) fetchurl genList listToAttrs;
inherit (config.systemConf) username;
# nvidia-offload-enabled = config.hardware.nvidia.prime.offload.enableOffloadCmd;
prefix = "nvidia-offload";
terminal = "ghostty";
browser = "zen-twilight";
brightnessStep = toString 10;
volumeStep = toString 4;
execOnceScript = pkgs.writeShellScript "startupExec" ''
# Fix nemo open in terminal
dconf write /org/cinnamon/desktop/applications/terminal/exec "''\'${terminal}''\'" &
dconf write /org/cinnamon/desktop/applications/terminal/exec-arg "''\'''\'" &
# Hint dark theme
dconf write /org/gnome/desktop/interface/color-scheme '"prefer-dark"' &
systemctl --user import-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP QT_QPA_PLATFORMTHEME &
'';
niri_peekaboo = fetchurl {
url = "https://raw.githubusercontent.com/heyoeyo/niri_tweaks/refs/heads/main/niri_peekaboo.py";
sha256 = "sha256:0l1x0bsa9vr089jhzgcz3xbh1hg15sw6njb91q0j9pdbrp2ym3dc";
};
in
{
programs.niri = {
enable = true;
package = pkgs.niri-unstable;
};
home-manager.users."${username}" =
{
osConfig,
config,
...
}:
let
rofiWall = import ../../home/scripts/rofiwall.nix { inherit config pkgs; };
rbwSelector = import ../../home/scripts/rbwSelector.nix { inherit pkgs; };
rNiri = pkgs.writeShellScriptBin "rNiri" ''
NIRI_SOCKET="/run/user/1000/$(ls /run/user/1000 | grep niri | head -n 1)" niri $@
'';
toggleWlogout = pkgs.writeShellScript "toggleWlogout" ''
if ${pkgs.busybox}/bin/pgrep wlogout > /dev/null; then
${pkgs.busybox}/bin/pkill wlogout
else
${config.programs.wlogout.package}/bin/wlogout --protocol layer-shell
fi
'';
in
with config.lib.niri.actions;
{
home.packages = with pkgs; [
nautilus # xdg-desktop-portal-gnome file picker
rNiri
];
xdg.portal = {
extraPortals = with pkgs; [ xdg-desktop-portal-gtk ];
};
services.nfsm.enable = false;
programs.niri.package = osConfig.programs.niri.package;
programs.niri.settings = {
spawn-at-startup = [
{ argv = [ "${execOnceScript}" ]; }
];
screenshot-path = "~/Pictures/Screenshots/%Y-%m-%d_%H-%M-%S.png";
prefer-no-csd = true;
xwayland-satellite = {
enable = true;
path = getExe pkgs.xwayland-satellite-unstable;
};
animations = {
workspace-switch.kind.spring = {
damping-ratio = 1.0;
stiffness = 1000;
epsilon = 0.0001;
};
window-open.kind.easing = {
duration-ms = 150;
curve = "ease-out-expo";
};
window-close.kind.easing = {
duration-ms = 150;
curve = "ease-out-quad";
};
window-resize.kind.spring = {
damping-ratio = 1.0;
stiffness = 800;
epsilon = 0.0001;
};
};
layout.border = {
enable = true;
width = 4;
active.gradient = {
from = "#24273A";
to = "#EBDBB2";
angle = 45;
in' = "oklab";
relative-to = "window";
};
inactive.gradient = {
from = "#24273A";
to = "#24273A";
angle = 45;
in' = "oklab";
relative-to = "window";
};
};
window-rules = [
# Global
{
geometry-corner-radius =
let
round = 12.0;
in
{
bottom-left = round;
bottom-right = round;
top-left = round;
top-right = round;
};
clip-to-geometry = true;
opacity = 1.0;
draw-border-with-background = false;
}
# Float
{
matches = [
{ app-id = "^xdg-desktop-portal-gtk$"; }
{ app-id = "^(org.gnome.Nautilus)$"; }
];
open-floating = true;
}
];
input = {
focus-follows-mouse = {
max-scroll-amount = "90%";
enable = true;
};
mouse.accel-speed = -0.1;
keyboard = {
repeat-delay = 250;
repeat-rate = 35;
};
};
binds =
let
sh = spawn "sh" "-c";
in
{
# ==== Launch ==== #
"Mod+Return".action = sh ''${prefix} ${terminal}'';
"Mod+F".action = sh ''${browser}'';
"Mod+E".action = sh ''${prefix} ${terminal} -e yazi'';
"Mod+Ctrl+P".action = spawn "${rbwSelector}";
"Mod+Ctrl+M".action = spawn "${toggleWlogout}";
# Rofi
"Mod+Ctrl+W".action = spawn "${rofiWall}";
"Alt+Space".action = spawn "rofi" "-config" "~/.config/rofi/apps.rasi" "-show" "drun";
"Mod+Period".action = spawn "rofi" "-modi" "emoji" "-show" "emoji";
"Mod+Ctrl+C".action = spawn "rofi" "-modi" "calc" "-show" "calc" "-no-show-match" "-no-sort";
# ==== Media ==== #
"XF86AudioPrev".action = spawn "playerctl" "previous";
"XF86AudioNext".action = spawn "playerctl" "next";
"Mod+Ctrl+Comma".action = spawn "playerctl" "previous";
"Mod+Ctrl+Period".action = spawn "playerctl" "next";
"XF86AudioPlay".action = spawn "playerctl" "play-pause";
"XF86AudioStop".action = spawn "playerctl" "stop";
"XF86AudioMute".action = spawn "wpctl" "set-mute" "@DEFAULT_SINK@" "toggle";
"XF86AudioRaiseVolume".action =
sh ''wpctl set-mute @DEFAULT_SINK@ 0 && wpctl set-volume @DEFAULT_SINK@ ${volumeStep}%+'';
"XF86AudioLowerVolume".action =
sh ''wpctl set-mute @DEFAULT_SINK@ 0 && wpctl set-volume @DEFAULT_SINK@ ${volumeStep}%-'';
"XF86MonBrightnessDown".action = spawn "brightnessctl set ${brightnessStep}%-";
"XF86MonBrightnessUp".action = spawn "brightnessctl set ${brightnessStep}%+";
# ==== Movement ==== #
# Mouse Scroll
"Mod+WheelScrollDown" = {
cooldown-ms = 150;
action = focus-workspace-down;
};
"Mod+WheelScrollUp" = {
cooldown-ms = 150;
action = focus-workspace-up;
};
"Mod+Shift+WheelScrollDown" = {
cooldown-ms = 150;
action = focus-column-or-monitor-right;
};
"Mod+Shift+WheelScrollUp" = {
cooldown-ms = 150;
action = focus-column-or-monitor-left;
};
"Mod+WheelScrollRight".action = focus-column-right;
"Mod+WheelScrollLeft".action = focus-column-left;
# Touchpad
"Mod+TouchpadScrollDown" = {
cooldown-ms = 150;
action = focus-window-or-workspace-down;
};
"Mod+TouchpadScrollUp" = {
cooldown-ms = 150;
action = focus-window-or-workspace-up;
};
# Monitor Focus
"Mod+Ctrl+H".action = focus-monitor-left;
"Mod+Ctrl+L".action = focus-monitor-right;
# Workspace Focus
"Mod+Ctrl+J".action = focus-workspace-down;
"Mod+Ctrl+K".action = focus-workspace-up;
# General Focus
"Mod+J".action = focus-window-or-workspace-down;
"Mod+K".action = focus-window-or-workspace-up;
"Mod+H".action = focus-column-or-monitor-left;
"Mod+L".action = focus-column-or-monitor-right;
# Workspace Move
"Mod+Ctrl+Shift+J".action = move-workspace-down;
"Mod+Ctrl+Shift+K".action = move-workspace-up;
# Window & Column Move
"Mod+Shift+J".action = move-window-down-or-to-workspace-down;
"Mod+Shift+K".action = move-window-up-or-to-workspace-up;
"Mod+Shift+L".action = move-column-right-or-to-monitor-right;
"Mod+Shift+H".action = move-column-left-or-to-monitor-left;
# Window Comsume
"Mod+Ctrl+Shift+L".action = consume-or-expel-window-right;
"Mod+Ctrl+Shift+H".action = consume-or-expel-window-left;
# ==== Action ==== #
# General
"Mod+C".action = center-window;
"Mod+O".action = toggle-overview;
"Mod+Q".action = close-window;
"F11".action = if config.services.nfsm.enable then (spawn "nfsm-cli") else fullscreen-window;
"Mod+Shift+slash".action = show-hotkey-overlay;
"Mod+Ctrl+Shift+P".action = spawn "${getExe pkgs.python312}" "${niri_peekaboo}";
# Column Scale
"Mod+W".action = switch-preset-column-width;
"Mod+S".action = switch-preset-window-height;
"Mod+P".action = expand-column-to-available-width;
"Mod+M".action = maximize-column;
"Mod+Ctrl+S".action = reset-window-height;
# Float
"Mod+V".action = toggle-window-floating;
"Mod+Ctrl+V".action = switch-focus-between-floating-and-tiling;
# Screenshot
"Mod+Shift+S".action.screenshot = [ { show-pointer = false; } ];
"Ctrl+Shift+S".action.screenshot-window = [
{
write-to-disk = false;
}
];
"Mod+Ctrl+Shift+S".action.screenshot-screen = [
{
write-to-disk = false;
}
];
}
# Map Mod+{1 ~ 9} to workspace{1 ~ 9}
// (pipe 9 [
(x: genList (i: i + 1) x)
(
x:
map (i: {
name = "Mod+${toString i}";
value.action = focus-workspace i;
}) x
)
(x: listToAttrs x)
]);
};
};
}