{ perSystem, osConfig, config, pkgs, lib, ... }: let inherit (lib.strings) removeSuffix; inherit (lib) mkIf mkEnableOption mkDefault mkForce getExe; inherit (config.lib.stylix) colors; inherit (config.stylix) cursor; inherit (osConfig.module.stylix) theme; inherit (osConfig.networking) hostName; cfg = config.desktop.niri; keyboard = if hostName == "eldrid" then "chromeos" else "platform"; xdgPics = config.xdg.userDirs.pictures; ifLaptop = mkIf (hostName != "dunamis"); launcher = getExe pkgs.fuzzel; lockscreen = getExe pkgs.gtklock; in { options = { desktop.niri.enable = mkEnableOption "enable niri desktop"; }; config = mkIf cfg.enable { stylix.targets = { waybar.enable = false; wpaperd.enable = true; qt.enable = false; }; xdg.configFile = { niri = { target = "niri/config.kdl"; text = let arrows = { left = "Left"; down = "Down"; up = "Up"; right = "Right"; }; homerow-arrows = { left = "n"; down = "e"; up = "i"; right = "o"; }; letters = { first = "Q"; second = "W"; third = "F"; fourth = "P"; fifth = "B"; sixth = "J"; seventh = "L"; eighth = "U"; ninth = "Y"; }; numbers = { first = "1"; second = "2"; third = "3"; fourth = "4"; fifth = "5"; sixth = "6"; seventh = "7"; eighth = "8"; ninth = "9"; }; genArrowNavigation = keys: '' Mod+${keys.left} { focus-column-left; } Mod+${keys.down} { focus-window-or-workspace-down; } Mod+${keys.up} { focus-window-or-workspace-up; } Mod+${keys.right} { focus-column-right; } Mod+Ctrl+${keys.left} { move-column-left; } Mod+Ctrl+${keys.down} { move-window-down-or-to-workspace-down; } Mod+Ctrl+${keys.up} { move-window-up-or-to-workspace-up; } Mod+Ctrl+${keys.right} { move-column-right; } Mod+Shift+${keys.left} { focus-monitor-left; } Mod+Shift+${keys.down} { focus-monitor-down; } Mod+Shift+${keys.up} { focus-monitor-up; } Mod+Shift+${keys.right} { focus-monitor-right; } Mod+Shift+Ctrl+${keys.left} { move-column-to-monitor-left; } Mod+Shift+Ctrl+${keys.down} { move-column-to-monitor-down; } Mod+Shift+Ctrl+${keys.up} { move-column-to-monitor-up; } Mod+Shift+Ctrl+${keys.right} { move-column-to-monitor-right; } ''; genWorkspacesNavigation = keys: '' Mod+${keys.first} { focus-workspace 1; } Mod+${keys.second} { focus-workspace 2; } Mod+${keys.third} { focus-workspace 3; } Mod+${keys.fourth} { focus-workspace 4; } Mod+${keys.fifth} { focus-workspace 5; } Mod+${keys.sixth} { focus-workspace 6; } Mod+${keys.seventh} { focus-workspace 7; } Mod+${keys.eighth} { focus-workspace 8; } Mod+${keys.ninth} { focus-workspace 9; } Mod+Ctrl+Shift+${keys.first} { move-column-to-workspace 1; } Mod+Ctrl+Shift+${keys.second} { move-column-to-workspace 2; } Mod+Ctrl+Shift+${keys.third} { move-column-to-workspace 3; } Mod+Ctrl+Shift+${keys.fourth} { move-column-to-workspace 4; } Mod+Ctrl+Shift+${keys.fifth} { move-column-to-workspace 5; } Mod+Ctrl+Shift+${keys.sixth} { move-column-to-workspace 6; } Mod+Ctrl+Shift+${keys.seventh} { move-column-to-workspace 7; } Mod+Ctrl+Shift+${keys.eighth} { move-column-to-workspace 8; } Mod+Ctrl+Shift+${keys.ninth} { move-column-to-workspace 9; } ''; in '' environment { DISPLAY ":123" QT_QPA_PLATFORM "wayland" QT_QPA_PLATFORMTHEME "gtk3" GDK_BACKEND "wayland" MOZ_ENABLE_WAYLAND "1" MOZ_WEBRENDER "1" XDG_SESSION_TYPE "wayland" CLUTTER_BACKEND "wayland" SDL_VIDEODRIVER "wayland" } input { keyboard { xkb { layout "us,ua" ${ if (hostName == "dunamis" || hostName == "legion") then "// variant \"colemak_dh_ortho,\"" else "variant \"colemak_dh_ortho,\"" } } repeat-delay 200 repeat-rate 50 track-layout "global" } touchpad { // off tap // dwt // dwtp natural-scroll accel-speed 0.2 accel-profile "flat" // scroll-method "two-finger" // disabled-on-external-mouse } mouse { // off // natural-scroll accel-speed 0.0 accel-profile "flat" // scroll-method "no-scroll" } warp-mouse-to-focus // focus-follows-mouse } output "eDP-1" { // off scale 1.2 transform "normal" // position x=1920 y=0 } output "Samsung Electric Company S24R65x H4TMA03291" { position x=0 y=0 variable-refresh-rate } output "Philips Consumer Electronics Company 27M2N5500 UK02418033254" { mode "2560x1440@${ if hostName == "legion" then "144.001" else "180.000" }" variable-refresh-rate position x=0 y=0 scale 1.33 } output "PNP(AOC) 27V2G5 0x0000026C" { position x=1920 y=0 mode "1920x1080@74.973" variable-refresh-rate } cursor { xcursor-theme "${cursor.name}" xcursor-size ${toString cursor.size} } layout { gaps 6 center-focused-column "never" // "on-overflow" preset-column-widths { proportion 0.33333 proportion 0.66667 } // default-column-width { proportion 0.9; } default-column-width { proportion 0.5; } tab-indicator { active-color "#${colors.base04}" inactive-color "#${colors.base03}" corner-radius 4 gap 6 gaps-between-tabs 3 length total-proportion=0.3 place-within-column position "left" width 6 } focus-ring { // off width 3 active-gradient from="#${colors.base0D}" to="#${colors.base0F}" angle=40 relative-to="workspace-view" inactive-color "#${colors.base03}" } struts { left 24 right 24 } } prefer-no-csd hotkey-overlay { skip-at-startup } window-rule { geometry-corner-radius 12 clip-to-geometry true } window-rule { match is-window-cast-target=true focus-ring { width 3 active-color "#${colors.base0F}" inactive-color "#${colors.base03}" } border { width 3 active-color "#${colors.base0F}" inactive-color "#${colors.base03}" } shadow { on color "#${colors.base0F}" inactive-color "#${colors.base03}" } } screenshot-path "${xdgPics}/screenshots/screenshot-%Y-%m-%d-%H-%M-%S.png" animations { slowdown 0.8 } window-rule { match title="Картинка в картинці" match title="Picture-in-Picture" open-floating true } window-rule { match app-id="org.wezfurlong.wezterm" match app-id="com.mitchellh.ghostty" default-column-width { proportion 1.0; } } window-rule { match app-id="re.sonny.Junction" open-floating true open-focused true } window-rule { match app-id="com.mitchellh.ghostty" draw-border-with-background false } window-rule { match app-id=r#"^org\.keepassxc\.KeePassXC$"# match app-id=r#"^org\.gnome\.World\.Secrets$"# match app-id=r#"^Bitwarden$"# match app-id="org.telegram.desktop" match app-id="org.signal.Signal" match app-id="WebCord" // block-out-from "screen-capture" block-out-from "screencast" } binds { Mod+Shift+Slash { show-hotkey-overlay; } Mod+Tab { focus-workspace-previous; } Mod+C { center-column; } Mod+M { maximize-column; } Mod+H { set-column-width "50%"; } Mod+T { set-column-width "33%"; } Mod+Shift+T { set-column-width "67%"; } Mod+V { toggle-column-tabbed-display; } Mod+G { switch-focus-between-floating-and-tiling; } Mod+Ctrl+Comma { consume-or-expel-window-left; } Mod+Ctrl+Period { consume-or-expel-window-right; } Mod+Ctrl+Q { close-window; } Mod+Ctrl+M { fullscreen-window; } Mod+Ctrl+G { toggle-window-floating; } Mod+Minus { set-column-width "-10%"; } Mod+Equal { set-column-width "+10%"; } Mod+Comma { set-column-width "-10%"; } Mod+Period { set-column-width "+10%"; } Mod+Shift+Comma { set-window-height "-10%"; } Mod+Shift+Period { set-window-height "+10%"; } Mod+Escape { switch-layout "next"; } Mod+Return repeat=false { spawn "sh" "-c" "$TERMINAL"; } Mod+Space repeat=false { spawn "${launcher}"; } Mod+Ctrl+L repeat=false { spawn "${lockscreen}"; } Mod+Ctrl+V repeat=false { spawn "copyq" "toggle"; } Mod+Ctrl+Shift+BracketLeft { quit; } Mod+Ctrl+Shift+BracketRight { power-off-monitors; } Print { screenshot; } Ctrl+Print { screenshot-screen; } Shift+Print { screenshot-window; } // Caps_Lock { spawn "swayosd-client" "--caps-lock"; } // XF86AudioRaiseVolume allow-when-locked=true { spawn "wpctl" "set-volume" "-l" "1.0" "@DEFAULT_AUDIO_SINK@" "5%+"; } // XF86AudioLowerVolume allow-when-locked=true { spawn "wpctl" "set-volume" "-l" "1.0" "@DEFAULT_AUDIO_SINK@" "5%-"; } // XF86AudioMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; } // XF86AudioMicMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; } XF86AudioRaiseVolume allow-when-locked=true { spawn "swayosd-client" "--output-volume" "raise"; } XF86AudioLowerVolume allow-when-locked=true { spawn "swayosd-client" "--output-volume" "lower"; } XF86AudioMute allow-when-locked=true { spawn "swayosd-client" "--output-volume" "mute-toggle"; } XF86AudioMicMute allow-when-locked=true { spawn "swayosd-client" "--input-volume" "mute-toggle"; } XF86AudioPlay allow-when-locked=true { spawn "playerctl" "play-pause"; } XF86AudioPause allow-when-locked=true { spawn "playerctl" "pause"; } XF86AudioPrev allow-when-locked=true { spawn "playerctl" "previous"; } XF86AudioNext allow-when-locked=true { spawn "playerctl" "next"; } XF86ScreenSaver allow-when-locked=true { spawn "${lockscreen}"; } XF86MonBrightnessUp allow-when-locked=true { spawn "brightnessctl" "s" "5%+"; } XF86MonBrightnessDown allow-when-locked=true { spawn "brightnessctl" "s" "5%-"; } Alt+XF86MonBrightnessUp allow-when-locked=true { spawn "brightnessctl" "s" "5%+" "-d" "${keyboard}::kbd_backlight"; } Alt+XF86MonBrightnessDown allow-when-locked=true { spawn "brightnessctl" "s" "5%-" "-d" "${keyboard}::kbd_backlight"; } XF86KbdBrightnessUp allow-when-locked=true { spawn "brightnessctl" "s" "5%+" "-d" "${keyboard}::kbd_backlight"; } XF86KbdBrightnessDown allow-when-locked=true { spawn "brightnessctl" "s" "5%-" "-d" "${keyboard}::kbd_backlight"; } Mod+Home { focus-column-first; } Mod+End { focus-column-last; } Mod+Ctrl+Home { move-column-to-first; } Mod+Ctrl+End { move-column-to-last; } Mod+Next { focus-workspace-down; } Mod+Prior { focus-workspace-up; } Mod+Ctrl+Next { move-column-to-workspace-down; } Mod+Ctrl+Prior { move-column-to-workspace-up; } Mod+Shift+Next { move-workspace-down; } Mod+Shift+Prior { move-workspace-up; } Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; } Mod+WheelScrollUp cooldown-ms=150 { focus-workspace-up; } Mod+Ctrl+WheelScrollDown cooldown-ms=150 { move-column-to-workspace-down; } Mod+Ctrl+WheelScrollUp 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; } Mod+TouchpadScrollDown { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.02+"; } Mod+TouchpadScrollUp { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.02-"; } ${genArrowNavigation arrows} ${genArrowNavigation homerow-arrows} ${genWorkspacesNavigation letters} ${genWorkspacesNavigation numbers} } ''; }; }; services = import ./services {inherit pkgs lockscreen getExe perSystem;}; programs.fuzzel = import ./programs/fuzzel {inherit config mkDefault;}; programs.waybar = import ./programs/waybar {inherit colors config getExe ifLaptop pkgs;}; home.packages = with pkgs; [ # gui libadwaita apps celluloid # mpv gui in libadwaita helvum # pipewire patchbay in rust junction # app chooser loupe # image viewer and editor in rust mission-center # task manager in rust (partly) nautilus # file manager overskride # bluetooth gui in rust papers # pdf reader in rust pika-backup # borg gui in rust pwvucontrol # pipewire gui in rust wdisplays # wlroots display configurator ] ++ [ # misc utils (ifLaptop brightnessctl) dconf libnotify playerctl wl-clipboard-rs # wl-clipboard in rust ]; gtk = { enable = true; iconTheme = { name = if config.stylix.polarity == "dark" then "Papirus-Dark" else "Papirus-Light"; package = if (removeSuffix "-light" theme) == "nord" then pkgs.papirus-nord else pkgs.papirus-icon-theme; }; }; qt = { enable = true; platformTheme.name = "gtk3"; }; dconf.settings = { "org/gnome/desktop/wm/preferences" = { button-layout = "icon:close"; }; "org/gnome/desktop/interface" = { icon-theme = config.gtk.iconTheme.name; }; }; xdg = { mime.enable = true; mimeApps = { enable = true; defaultApplications = let file_manager = ["org.gnome.Nautilus.desktop"]; image_viewer = ["org.gnome.Loupe.desktop"]; pdf_reader = ["org.gnome.Papers.desktop"]; video_player = ["io.github.celluloid_player.Celluloid.desktop"]; web_browser = ["re.sonny.Junction.desktop"]; in { "application/pdf" = pdf_reader; "image/jpeg" = image_viewer; "image/png" = image_viewer; "inode/directory" = file_manager; "text/html" = web_browser; "video/mp4" = video_player; "video/mpeg" = video_player; "video/x-matroska" = video_player; "video/x-mpeg" = video_player; "x-scheme-handler/about" = web_browser; "x-scheme-handler/http" = web_browser; "x-scheme-handler/https" = web_browser; "x-scheme-handler/unknown" = web_browser; }; }; }; systemd.user = { settings.Manager.DefaultEnvironment = { QT_QPA_PLATFORM = "wayland"; DISPLAY = ":123"; }; targets.tray.Unit.Description = "Home Manager System Tray"; # workaround for udiskie services = let mkGraphicalService = config: graphicalService // config; graphicalService = { Install.WantedBy = ["niri.service"]; Unit = { Requisite = ["graphical-session.target"]; PartOf = ["graphical-session.target"]; After = ["graphical-session.target"]; }; Service = { Restart = "on-failure"; TimeoutStopSec = 10; RestartSec = 1; }; }; in { udiskie = mkGraphicalService {}; waybar = mkGraphicalService {}; network-manager-applet = mkGraphicalService {}; copyq = mkGraphicalService { Service = graphicalService.Service // { Environment = mkForce "QT_QPA_PLATFORM=wayland"; }; }; xwayland-satellite = mkGraphicalService { Service = graphicalService.Service // { Type = "simple"; ExecStart = getExe pkgs.xwayland-satellite + " :123"; }; }; wpaperd = mkGraphicalService { Service = mkDefault graphicalService.Service; }; gnome-polkit-agent = mkGraphicalService { Service = graphicalService.Service // { Type = "simple"; ExecStart = pkgs.polkit_gnome + "/libexec/polkit-gnome-authentication-agent-1"; }; }; }; }; }; }