{ config, hostConfig, pkgs, lib, ... }: let niri = config.programs.niri.package; terminal = lib.getExe config.programs.kitty.package; lightctl = lib.getExe' config.services.avizo.package "lightctl"; volumectl = lib.getExe' config.services.avizo.package "volumectl"; dunstctl = lib.getExe' config.services.dunst.package "dunstctl"; loginctl = lib.getExe' hostConfig.systemd.package "loginctl"; systemctl = lib.getExe' hostConfig.systemd.package "systemctl"; focus_or_spawn = pkgs.writeShellApplication { name = "focus-or-spawn"; runtimeInputs = [ niri pkgs.gojq pkgs.gnugrep pkgs.socat ]; text = '' window_select="$1" shift workspace_name="$1" shift workspaces_json="$(niri msg -j workspaces)" workspace_output="$(jq -r --arg workspace_name "$workspace_name" '.[] | select(.name == $workspace_name) | .output' <<<"$workspaces_json")" active_workspace="$(jq -r --arg workspace_output "$workspace_output" '.[] | select(.output == $workspace_output and .is_active) | .id' <<<"$workspaces_json")" active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" if [[ $workspace_output != "$active_output" ]]; then niri msg action move-workspace-to-monitor --output "$active_output" "$workspace_name" socat STDIO "$NIRI_SOCKET" <<<'{"Action":{"FocusWorkspace":{"reference":{"Id":'"''${active_workspace}"'}}}}' niri msg action move-workspace-to-index --index 1 "$workspace_name" fi while IFS=$'\n' read -r window_json; do if [[ -n $(jq -c "$window_select" <<<"$window_json") ]]; then niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")" exit 0 fi done < <(niri msg -j windows | jq -c '.[]') exec "$@" ''; }; focus-or-spawn-action = config.lib.niri.actions.spawn (lib.getExe focus_or_spawn); focus-or-spawn-action-app_id = app_id: focus-or-spawn-action ''select(.app_id == "${app_id}")''; with_adjacent_workspace = pkgs.writeShellApplication { name = "with-adjacent-workspace"; runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; text = '' blacklist="$1" shift direction="$1" shift action="$1" shift workspaces_json="$(niri msg -j workspaces)" active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")" workspace_output="$(jq -r --arg active_workspace "$active_workspace" '.[] | select(.id == ($active_workspace | tonumber)) | .output' <<<"$workspaces_json")" workspace_idx="$(jq -r '.[] | select(.is_focused) | .idx' <<<"$workspaces_json")" jq_script='map(select(' case "$direction" in down) # shellcheck disable=SC2016 jq_script=''${jq_script}'.idx > ($workspace_idx | tonumber)';; up) # shellcheck disable=SC2016 jq_script=''${jq_script}'.idx < ($workspace_idx | tonumber)';; esac # shellcheck disable=SC2016 jq_script=''${jq_script}' and .output == $workspace_output and ((.name == null) or (.name | test($blacklist) | not)))) | sort_by(.idx)' [[ $direction == "up" ]] && jq_script=''${jq_script}' | reverse' jq_script=''${jq_script}' | .[0]' workspace_json=$(jq -c --arg blacklist "$blacklist" --arg workspace_output "$workspace_output" --arg workspace_idx "$workspace_idx" "$jq_script" <<<"$workspaces_json") [[ -n $workspace_json && $workspace_json != null ]] || exit 0 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" ''; }; with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^pwctl|kpxc|bmgr|edit|term$"; focus-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}''; with_unnamed_workspace = pkgs.writeShellApplication { name = "with-unnamed-workspace"; runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; text = '' action="$1" shift workspaces_json="$(niri msg -j workspaces)" active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")" workspace_json="$(jq -c --arg active_output "$active_output" 'map(select(.output == $active_output and .name == null)) | sort_by(.idx) | .[0]' <<<"$workspaces_json")" [[ -n $workspace_json && $workspace_json != null && $active_workspace != "$(jq -r '.id' <<<"$workspace_json")" ]] || exit 0 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" ''; }; with-unnamed-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_unnamed_workspace); in { imports = [ ./waybar.nix ]; config = { systemd.user.services.xwayland-satellite = { Unit = { BindsTo = [ "graphical-session.target" ]; PartOf = [ "graphical-session.target" ]; After = [ "graphical-session.target" ]; Requisite = [ "graphical-session.target" ]; }; Service = { Type = "notify"; NotifyAccess = "all"; Environment = [ "DISPLAY=:0" ]; ExecStart = ''${lib.getExe pkgs.xwayland-satellite-unstable} ''${DISPLAY}''; ExecStartPre = "${systemctl} --user import-environment DISPLAY"; StandardOutput = "journal"; }; Install = { WantedBy = [ "graphical-session.target" ]; }; }; services.swayidle = { events = [ { event = "after-resume"; command = "${lib.getExe niri} msg action power-on-monitors"; } ]; timeouts = [ { timeout = 300; command = "${lib.getExe niri} msg action power-off-monitors"; } ]; }; programs.niri.settings = { prefer-no-csd = true; screenshot-path = "${config.home.homeDirectory}/screenshots"; hotkey-overlay.skip-at-startup = true; input = { keyboard.xkb = { layout = "us,us"; variant = "dvp,"; options = "compose:caps,grp:win_space_toggle"; }; workspace-auto-back-and-forth = true; # focus-follows-mouse.enable = true; warp-mouse-to-focus = true; }; outputs = { "Samsung Display Corp. 0x4141 Unknown" = { scale = 1.5; position = { x = 0; y = 0; }; }; "Ancor Communications Inc ASUS PB287Q 0x0000DD9B" = { scale = 1.5; position = { x = 2560; y = 0; }; }; }; environment = { NIXOS_OZONE_WL = "1"; QT_QPA_PLATFORM = "wayland"; GDK_BACKEND = "wayland"; SDL_VIDEODRIVER = "wayland"; }; layout = { gaps = 8; struts = { left = 0; right = 0; top = 0; bottom = 0; }; focus-ring = { width = 2; }; preset-column-widths = [ { proportion = 1. / 4.; } { proportion = 1. / 3.; } { proportion = 1. / 2.; } { proportion = 2. / 3.; } { proportion = 3. / 4.; } ]; default-column-width.proportion = 1. / 2.; preset-window-heights = [ { proportion = 1. / 3.; } { proportion = 1. / 2.; } { proportion = 2. / 3.; } { proportion = 1.; } ]; always-center-single-column = true; }; cursor.hide-when-typing = true; workspaces = { "001".name = "pwctl"; "002".name = "kpxc"; "003".name = "bmgr"; "004".name = "term"; "005".name = "edit"; "101".name = "comm"; "102".name = "web"; "104".name = "read"; "105".name = "mon"; "110".name = "vid"; }; window-rules = [ { geometry-corner-radius = let allCorners = r: { bottom-left = r; bottom-right = r; top-left = r; top-right = r; }; in allCorners 4.; clip-to-geometry = true; open-focused = true; } { matches = [ { app-id = "^com\.saivert\.pwvucontrol$"; } ]; open-on-workspace = "pwctl"; } { matches = [ { app-id = "^\.blueman-manager-wrapped$"; } ]; open-on-workspace = "bmgr"; } { matches = [ { app-id = "^org\.keepassxc\.KeePassXC$"; } ]; excludes = [ { title = "^Unlock Database"; } { title = "^Access Request"; } { title = "^Passkey credentials"; } ]; open-on-workspace = "kpxc"; open-focused = false; } { matches = [ { app-id = "^kitty-scratch$"; } ]; open-on-workspace = "term"; } { matches = [ { title = "^scratch$"; app-id = "^emacs$"; } ]; open-on-workspace = "edit"; } { matches = [ { app-id = "^thunderbird$"; } { app-id = "^Element$"; } ]; open-on-workspace = "comm"; } { matches = [ { app-id = "^firefox$"; } ]; open-on-workspace = "web"; } { matches = [ { app-id = "^evince$"; } { app-id = "^imv$"; } { app-id = "^org\.pwmt\.zathura$"; } ]; open-on-workspace = "read"; } { matches = [ { app-id = "^mpv$"; } ]; open-on-workspace = "vid"; } { matches = [ { app-id = "^qemu$"; } { app-id = "^virt-manager$"; } ]; open-on-workspace = "mon"; } ]; binds = with config.lib.niri.actions; { "Mod+Slash".action = show-hotkey-overlay; "Mod+Return".action = spawn terminal; "Mod+Q".action = close-window; "Mod+D".action = spawn (lib.getExe config.programs.fuzzel.package); "Mod+Shift+D".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path"; "Mod+Alt+E".action = spawn (lib.getExe' config.services.emacs.package "emacsclient") "-c"; "Mod+H".action = focus-column-left; "Mod+T".action = focus-window-down; "Mod+N".action = focus-window-up; "Mod+S".action = focus-column-right; "Mod+Shift+H".action = move-column-left; "Mod+Shift+T".action = move-window-down; "Mod+Shift+N".action = move-window-up; "Mod+Shift+S".action = move-column-right; "Mod+Control+H".action = focus-monitor-left; "Mod+Control+T".action = focus-monitor-down; "Mod+Control+N".action = focus-monitor-up; "Mod+Control+S".action = focus-monitor-right; "Mod+Shift+Control+H".action = move-workspace-to-monitor-left; "Mod+Shift+Control+T".action = move-workspace-to-monitor-down; "Mod+Shift+Control+N".action = move-workspace-to-monitor-up; "Mod+Shift+Control+S".action = move-workspace-to-monitor-right; "Mod+G".action = focus-adjacent-workspace "down"; "Mod+C".action = focus-adjacent-workspace "up"; "Mod+Shift+G".action = move-column-to-adjacent-workspace "down"; "Mod+Shift+C".action = move-column-to-adjacent-workspace "up"; "Mod+Shift+Control+G".action = move-workspace-down; "Mod+Shift+Control+C".action = move-workspace-up; "Mod+ParenLeft".action = focus-workspace "comm"; "Mod+Shift+ParenLeft".action = move-column-to-workspace "comm"; "Mod+ParenRight".action = focus-workspace "web"; "Mod+Shift+ParenRight".action = move-column-to-workspace "web"; "Mod+BraceRight".action = focus-workspace "read"; "Mod+Shift+BraceRight".action = move-column-to-workspace "read"; "Mod+BraceLeft".action = focus-workspace "mon"; "Mod+Shift+BraceLeft".action = move-column-to-workspace "mon"; "Mod+Asterisk".action = focus-workspace "vid"; "Mod+Shift+Asterisk".action = move-column-to-workspace "vid"; "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}''; "Mod+M".action = consume-or-expel-window-left; "Mod+W".action = consume-or-expel-window-right; "Mod+R".action = switch-preset-column-width; "Mod+Shift+R".action = switch-preset-window-height; "Mod+F".action = center-column; "Mod+Shift+F".action = maximize-column; "Mod+Shift+Ctrl+F".action = fullscreen-window; "Mod+B".action = switch-focus-between-floating-and-tiling; "Mod+Shift+B".action = toggle-window-floating; "Mod+Left".action = set-column-width "-10%"; "Mod+Down".action = set-window-height "-10%"; "Mod+Up".action = set-window-height "+10%"; "Mod+Right".action = set-column-width "+10%"; "Mod+Shift+Z" = { action = spawn (lib.getExe niri) "msg" "action" "power-off-monitors"; allow-when-locked = true; }; "Mod+Shift+L" = { action = spawn loginctl "lock-session"; }; "Mod+Shift+E".action = quit; "XF86MonBrightnessUp" = { action = spawn lightctl "-d" "-e4" "-n1" "up"; allow-when-locked = true; }; "XF86MonBrightnessDown" = { action = spawn lightctl "-d" "-e4" "-n1" "down"; allow-when-locked = true; }; "XF86AudioRaiseVolume" = { action = spawn volumectl "-d" "-u" "up"; allow-when-locked = true; }; "XF86AudioLowerVolume" = { action = spawn volumectl "-d" "-u" "down"; allow-when-locked = true; }; "XF86AudioMute" = { action = spawn volumectl "-d" "toggle-mute"; allow-when-locked = true; }; "XF86AudioMicMute" = { action = spawn volumectl "-d" "-m" "toggle-mute"; allow-when-locked = true; }; "Mod+Semicolon".action = spawn dunstctl "close"; "Mod+Shift+Semicolon".action = spawn dunstctl "close-all"; "Mod+Period".action = spawn dunstctl "context"; "Mod+Comma".action = spawn dunstctl "history-pop"; "Mod+Control+A".action = focus-or-spawn-action-app_id "com.saivert.pwvucontrol" "pwctl" "pwvucontrol"; "Mod+Control+P".action = focus-or-spawn-action-app_id "org.keepassxc.KeePassXC" "kpxc" "keepassxc"; "Mod+Control+B".action = focus-or-spawn-action-app_id ".blueman-manager-wrapped" "bmgr" "blueman-manager"; "Mod+Control+Return".action = focus-or-spawn-action-app_id "kitty-scratch" "term" "kitty" "--app-id" "kitty-scratch"; "Mod+Control+E".action = focus-or-spawn-action "select(.app_id == \"emacs\" and .title == \"scratch\")" "edit" "emacsclient" "-c" "--frame-parameters=(quote (name . \"scratch\"))"; }; }; }; }