{ lib, osConfig, ... }: let # Mod+1..9 focus a workspace, Mod+Ctrl+1..9 move the column there. workspaceBinds = lib.mergeAttrsList ( map (n: { "Mod+${toString n}".focus-workspace = n; "Mod+Ctrl+${toString n}".move-column-to-workspace = n; }) (lib.range 1 9) ); # Shorthand for binds that only run spawn-sh while allowed on the lock screen. lockedSh = cmd: { _props.allow-when-locked = true; spawn-sh = cmd; }; in { # Niri config, rendered to ~/.config/niri/config.kdl by home-manager's built-in # wayland.windowManager.niri module. The compositor itself, its session, systemd # units and portals are provided by the NixOS module (modules/nixos/niri.nix), so # this module only writes the config. Host-specific outputs and input devices # live in hosts//niri.nix. wayland.windowManager.niri = { # only on hosts that run niri (see modules/nixos/niri.nix) enable = osConfig.programs.niri.enable; # Use the system's niri so the config is validated against the running version. package = osConfig.programs.niri.package; systemd.enable = false; portalPackage = null; xwaylandSatellitePackage = null; # installed system-wide settings = { input = { keyboard = { xkb.layout = "ch"; numlock = { }; }; mouse = { natural-scroll = { }; accel-speed = 0.5; scroll-method = "no-scroll"; }; }; environment.GTK_IM_MODULE = "simple"; layout = { gaps = 16; center-focused-column = "never"; preset-column-widths._children = [ { proportion = 0.33333; } { proportion = 0.5; } { proportion = 0.66667; } { fixed = 2560; } ]; default-column-width.proportion = 0.5; focus-ring = { on = { }; width = 2; active-color = "#4c566a"; inactive-color = "#2e3440"; urgent-color = "#a3be8c"; }; border.off = { }; shadow = { on = { }; draw-behind-window = true; softness = 30; spread = 10; offset._props = { x = 0; y = 5; }; color = "#0007"; }; }; spawn-at-startup = "noctalia"; hotkey-overlay.skip-at-startup = { }; screenshot-path = "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png"; _children = [ # Open the Firefox picture-in-picture player as floating by default. { window-rule._children = [ { match._props = { app-id = "firefox$"; title = "^Picture-in-Picture$"; }; } { open-floating = true; } ]; } # Block out the password manager from screen capture. { window-rule._children = [ { match._props.app-id = "^org\\.keepassxc\\.KeePassXC$"; } { block-out-from = "screen-capture"; } ]; } { window-rule = { geometry-corner-radius = 12; clip-to-geometry = true; }; } ]; binds = workspaceBinds // { # programs: terminal, app launcher, screen locker "Mod+Return" = { _props.hotkey-overlay-title = "Open a Terminal"; spawn = "ghostty"; }; "Mod+D" = { _props.hotkey-overlay-title = "Run an Application"; spawn = [ "noctalia" "msg" "panel-toggle" "launcher" ]; }; "Super+Alt+L" = { _props.hotkey-overlay-title = "Lock the Screen"; spawn = [ "noctalia" "msg" "session" "lock" ]; }; # media keys (PipeWire / WirePlumber) "XF86AudioRaiseVolume" = lockedSh "wpctl set-volume @DEFAULT_SINK@ 0.01+ -l 1.0"; "XF86AudioLowerVolume" = lockedSh "wpctl set-volume @DEFAULT_SINK@ 0.01-"; "XF86AudioMute" = lockedSh "wpctl set-mute @DEFAULT_SINK@ toggle"; "XF86AudioMicMute" = lockedSh "wpctl set-mute @DEFAULT_SOURCE@ toggle"; "XF86AudioPlay" = lockedSh "playerctl play-pause"; "XF86AudioStop" = lockedSh "playerctl stop"; "XF86AudioPrev" = lockedSh "playerctl previous"; "XF86AudioNext" = lockedSh "playerctl next"; "XF86MonBrightnessUp" = { _props.allow-when-locked = true; spawn = [ "brightnessctl" "--class=backlight" "set" "+10%" ]; }; "XF86MonBrightnessDown" = { _props.allow-when-locked = true; spawn = [ "brightnessctl" "--class=backlight" "set" "10%-" ]; }; "Mod+O" = { _props.repeat = false; toggle-overview = { }; }; "Mod+Q" = { _props.repeat = false; close-window = { }; }; # focus / move columns and windows "Mod+Left".focus-column-left = { }; "Mod+Down".focus-window-down = { }; "Mod+Up".focus-window-up = { }; "Mod+Right".focus-column-right = { }; "Mod+H".focus-column-left = { }; "Mod+J".focus-window-down = { }; "Mod+K".focus-window-up = { }; "Mod+L".focus-column-right = { }; "Mod+Shift+Left".move-column-left = { }; "Mod+Shift+Down".move-window-down = { }; "Mod+Shift+Up".move-window-up = { }; "Mod+Shift+Right".move-column-right = { }; "Mod+Shift+H".move-column-left = { }; "Mod+Shift+J".move-window-down = { }; "Mod+Shift+K".move-window-up = { }; "Mod+Shift+L".move-column-right = { }; "Mod+Home".focus-column-first = { }; "Mod+End".focus-column-last = { }; "Mod+Ctrl+Home".move-column-to-first = { }; "Mod+Ctrl+End".move-column-to-last = { }; # move columns between monitors "Mod+Ctrl+Left".move-column-to-monitor-left = { }; "Mod+Ctrl+Down".move-column-to-monitor-down = { }; "Mod+Ctrl+Up".move-column-to-monitor-up = { }; "Mod+Ctrl+Right".move-column-to-monitor-right = { }; "Mod+Ctrl+H".move-column-to-monitor-left = { }; "Mod+Ctrl+J".move-column-to-monitor-down = { }; "Mod+Ctrl+K".move-column-to-monitor-up = { }; "Mod+Ctrl+L".move-column-to-monitor-right = { }; # workspaces "Mod+Page_Down".focus-workspace-down = { }; "Mod+Page_Up".focus-workspace-up = { }; "Mod+U".focus-workspace-down = { }; "Mod+I".focus-workspace-up = { }; "Mod+Ctrl+Page_Down".move-column-to-workspace-down = { }; "Mod+Ctrl+Page_Up".move-column-to-workspace-up = { }; "Mod+Ctrl+U".move-column-to-workspace-down = { }; "Mod+Ctrl+I".move-column-to-workspace-up = { }; "Mod+Shift+Page_Down".move-workspace-down = { }; "Mod+Shift+Page_Up".move-workspace-up = { }; "Mod+Shift+U".move-workspace-down = { }; "Mod+Shift+I".move-workspace-up = { }; # mouse wheel "Mod+WheelScrollDown" = { _props.cooldown-ms = 150; focus-workspace-down = { }; }; "Mod+WheelScrollUp" = { _props.cooldown-ms = 150; focus-workspace-up = { }; }; "Mod+Ctrl+WheelScrollDown" = { _props.cooldown-ms = 150; move-column-to-workspace-down = { }; }; "Mod+Ctrl+WheelScrollUp" = { _props.cooldown-ms = 150; move-column-to-workspace-up = { }; }; "Mod+WheelScrollRight".focus-column-right = { }; "Mod+WheelScrollLeft".focus-column-left = { }; "Mod+Ctrl+WheelScrollRight".move-column-right = { }; "Mod+Ctrl+WheelScrollLeft".move-column-left = { }; "Mod+Shift+WheelScrollDown".focus-column-right = { }; "Mod+Shift+WheelScrollUp".focus-column-left = { }; "Mod+Ctrl+Shift+WheelScrollDown".move-column-right = { }; "Mod+Ctrl+Shift+WheelScrollUp".move-column-left = { }; # columns "Mod+BracketLeft".consume-or-expel-window-left = { }; "Mod+BracketRight".consume-or-expel-window-right = { }; "Mod+Comma".consume-window-into-column = { }; "Mod+Period".expel-window-from-column = { }; "Mod+R".switch-preset-column-width = { }; "Mod+Shift+R".switch-preset-window-height = { }; "Mod+Ctrl+R".reset-window-height = { }; "Mod+F".maximize-column = { }; "Mod+Shift+F".fullscreen-window = { }; "Mod+Ctrl+F".expand-column-to-available-width = { }; "Mod+C".center-column = { }; "Mod+Ctrl+C".center-visible-columns = { }; "Mod+Minus".set-column-width = "-10%"; "Mod+Equal".set-column-width = "+10%"; "Mod+Shift+Minus".set-window-height = "-10%"; "Mod+Shift+Equal".set-window-height = "+10%"; "Mod+Space".toggle-window-floating = { }; "Mod+Shift+Space".switch-focus-between-floating-and-tiling = { }; "Mod+W".toggle-column-tabbed-display = { }; # screenshots "Mod+P".screenshot = { }; "Alt+Print".screenshot-window = { }; # escape hatch for apps that inhibit keyboard shortcuts "Mod+Escape" = { _props.allow-inhibiting = false; toggle-keyboard-shortcuts-inhibit = { }; }; "Mod+Shift+E".quit = { }; }; }; }; }