From 0684dd8af47013de3b9bdccf72455fe1ec3b1ca9 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Wed, 5 Feb 2025 21:24:52 +0100 Subject: ... --- accounts/gkleen@sif/niri/default.nix | 1015 ++++++++++++++++++---------------- 1 file changed, 543 insertions(+), 472 deletions(-) (limited to 'accounts/gkleen@sif/niri/default.nix') diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix index 7e187c84..abcb80fc 100644 --- a/accounts/gkleen@sif/niri/default.nix +++ b/accounts/gkleen@sif/niri/default.nix @@ -1,6 +1,10 @@ -{ config, hostConfig, pkgs, lib, ... }: +{ config, hostConfig, pkgs, lib, flakeInputs, ... }: let - niri = config.programs.niri.package; + cfg = config.programs.niri; + + kdl = flakeInputs.niri-flake.lib.kdl; + + niri = cfg.package; terminal = lib.getExe config.programs.kitty.package; makoctl = lib.getExe' config.services.mako.package "makoctl"; loginctl = lib.getExe' hostConfig.systemd.package "loginctl"; @@ -78,7 +82,7 @@ let 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|eff|kpxc|bmgr|edit|term$"; + with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^${lib.concatMapStringsSep "|" ({ name, ...}: name) cfg.scratchspaces}$"; 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}}}}''; @@ -129,6 +133,54 @@ in { ./swayosd.nix ]; + options = { + programs.niri.scratchspaces = lib.mkOption { + type = lib.types.listOf (lib.types.submodule ({ config, ... }: { + options = { + name = lib.mkOption { + type = lib.types.str; + }; + match = lib.mkOption { + type = lib.types.listOf (lib.types.attrsOf kdl.types.kdl-args); + default = []; + }; + exclude = lib.mkOption { + type = lib.types.listOf (lib.types.attrsOf kdl.types.kdl-args); + default = []; + }; + windowRuleExtra = lib.mkOption { + type = kdl.types.kdl-nodes; + default = []; + }; + key = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + }; + spawn = lib.mkOption { + type = lib.types.nullOr (lib.types.listOf lib.types.str); + default = null; + }; + app-id = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + }; + selector = lib.mkOption { + type = lib.types.nullOr lib.types.str; + default = null; + }; + }; + + config = lib.mkMerge [ + (lib.mkIf (config.app-id != null) { + match = lib.mkDefault [ { app-id = "^${lib.escapeRegex config.app-id}$"; } ]; + selector = lib.mkDefault "select(.app_id == \"${config.app-id}\")"; + }) + ]; + })); + default = []; + }; + }; + config = { systemd.user.services.xwayland-satellite = { Unit = { @@ -268,476 +320,495 @@ in { }; }; - programs.niri.settings = { - prefer-no-csd = true; - screenshot-path = "${config.home.homeDirectory}/screenshots"; - - hotkey-overlay.skip-at-startup = true; - - input = { - keyboard = { - repeat-delay = 300; - repeat-rate = 50; - - 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 = { - "eDP-1" = { - scale = 1.5; - position = { x = 0; y = 0; }; - }; - "Ancor Communications Inc ASUS PB287Q 0x0000DD9B" = { - scale = 1.5; - position = { x = 2560; y = 0; }; - }; - "HP Inc. HP 727pu CN4417143K" = { - mode = { width = 2560; height = 1440; refresh = 119.998; }; - scale = 1; - position = { x = 2560; y = 0; }; - variable-refresh-rate = "on-demand"; - }; - }; - - environment = { - NIXOS_OZONE_WL = "1"; - QT_QPA_PLATFORM = "wayland"; - QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; - GDK_BACKEND = "wayland"; - SDL_VIDEODRIVER = "wayland"; - DISPLAY = ":0"; - }; - - debug.render-drm-device = "/dev/dri/by-path/pci-0000:00:02.0-render"; - - animations = { - slowdown = 0.5; - workspace-switch = null; - }; - - layout = { - gaps = 8; - struts = { left = 0; right = 0; top = 0; bottom = 0; }; - focus-ring = { - width = 2; - active.gradient = { - from = "hsla(195 100% 60% 0.75)"; - to = "hsla(155 100% 50% 0.75)"; - angle = 29; - relative-to = "workspace-view"; - }; - inactive.gradient = { - from = "hsla(0 0% 42% 0.66)"; - to = "hsla(0 0% 35% 0.66)"; - angle = 29; - relative-to = "workspace-view"; - }; - }; - - preset-column-widths = [ - { proportion = 1. / 4.; } - { proportion = 1. / 3.; } - { proportion = 1. / 2.; } - { proportion = 2. / 3.; } - { proportion = 3. / 4.; } + programs.niri.scratchspaces = [ + { name = "pwctl"; + key = "Mod+Control+A"; + spawn = ["pwvucontrol"]; + app-id = "com.saivert.pwvucontrol"; + } + { name = "kpxc"; + exclude = [ + { title = "^Unlock Database.*"; } + { title = "^Access Request.*"; } + { title = ".*Passkey credentials$"; } ]; - default-column-width.proportion = 1. / 2.; - preset-window-heights = [ - { proportion = 1. / 3.; } - { proportion = 1. / 2.; } - { proportion = 2. / 3.; } - { proportion = 1.; } + windowRuleExtra = [ + (kdl.leaf "open-focused" false) + ]; + key = "Mod+Control+P"; + app-id = "org.keepassxc.KeePassXC"; + spawn = [ "keepassxc" ]; + } + { name = "bmgr"; + key = "Mod+Control+B"; + app-id = ".blueman-manager-wrapped"; + spawn = [ "blueman-manager" ]; + } + { name = "term"; + key = "Mod+Control+Return"; + app-id = "kitty-scratch"; + spawn = [ "kitty" "--app-id" "kitty-scratch" ]; + } + { name = "edit"; + match = [ { title = "^scratch$"; app-id = "^emacs$"; } ]; + key = "Mod+Control+E"; + selector = "select(.app_id == \"emacs\" and .title == \"scratch\")"; + spawn = [ "emacsclient" "-c" "--frame-parameters=(quote (name . \"scratch\"))" ]; + } + { name = "eff"; + key = "Mod+Control+O"; + app-id = "com.github.wwmm.easyeffects"; + spawn = [ "easyeffects" ]; + } + ]; + programs.niri.config = + let + inherit (kdl) node plain leaf flag; + optional-node = cond: v: + if cond + then v + else null; + opt-props = lib.filterAttrs (lib.const (value: value != null)); + in + [ (flag "prefer-no-csd") + + (plain "hotkey-overlay" [ + (flag "skip-at-startup") + ]) + + (plain "input" [ + (plain "keyboard" [ + (leaf "repeat-delay" 300) + (leaf "repeat-rate" 50) + + (plain "xkb" [ + (leaf "layout" "us,us") + (leaf "variant" "dvp,") + (leaf "options" "compose:caps,grp:win_space_toggle") + ]) + ]) + + (flag "workspace-auto-back-and-forth") + # (leaf "focus-follows-mouse" {}) + # (flag "warp-mouse-to-focus") + + (plain "touchpad" [ (flag "off") ]) + (plain "trackball" [ + (leaf "scroll-method" "on-button-down") + (leaf "scroll-button" 278) + ]) + ]) + + (plain "environment" (lib.mapAttrsToList leaf { + NIXOS_OZONE_WL = "1"; + QT_QPA_PLATFORM = "wayland"; + QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; + GDK_BACKEND = "wayland"; + SDL_VIDEODRIVER = "wayland"; + DISPLAY = ":0"; + })) + + (node "output" "eDP-1" [ + (leaf "scale" 1.5) + (leaf "position" { x = 0; y = 0; }) + ]) + (node "output" "Ancor Communications Inc ASUS PB287Q 0x0000DD9B" [ + (leaf "scale" 1.5) + (leaf "position" { x = 2560; y = 0; }) + ]) + (node "output" "HP Inc. HP 727pu CN4417143K" [ + (leaf "mode" "2560x1440@120") # 119.998 + (leaf "scale" 1) + (leaf "position" { x = 2560; y = 0; }) + (flag "variable-refresh-rate") + ]) + + (plain "debug" [ + (leaf "render-drm-device" "/dev/dri/by-path/pci-0000:00:02.0-render") + ]) + + (plain "animations" [ + (leaf "slowdown" 0.5) + (plain "workspace-switch" [(flag "off")]) + ]) + + (plain "layout" [ + (leaf "gaps" 8) + (plain "struts" [ + (leaf "left" 0) + (leaf "right" 0) + (leaf "top" 0) + (leaf "bottom" 0) + ]) + (plain "focus-ring" [ + (leaf "width" 2) + (leaf "active-gradient" { + from = "hsla(195 100% 45% 1)"; + to = "hsla(155 100% 37.5% 1)"; + angle = 29; + relative-to = "workspace-view"; + }) + (leaf "inactive-gradient" { + from = "hsla(0 0% 27.7% 1)"; + to = "hsla(0 0% 23% 1)"; + angle = 29; + relative-to = "workspace-view"; + }) + ]) + + (plain "preset-column-widths" (map (prop: leaf "proportion" prop) [ + (1. / 4.) (1. / 3.) (1. / 2.) (2. / 3.) (3. / 4.) + ])) + (plain "default-column-width" [ (leaf "proportion" (1. / 2.)) ]) + (plain "preset-window-heights" (map (prop: leaf "proportion" prop) [ + (1. / 3.) (1. / 2.) (2. / 3.) (1.) + ])) + + (flag "always-center-single-column") + + (plain "tab-indicator" [ + (leaf "gap" (-6)) + (leaf "width" 6) + (leaf "length" { total-proportion = 1.; }) + (leaf "active-gradient" { + from = "hsla(195 100% 60% 0.75)"; + to = "hsla(155 100% 50% 0.75)"; + angle = 29; + relative-to = "workspace-view"; + }) + (leaf "inactive-gradient" { + from = "hsla(0 0% 42% 0.66)"; + to = "hsla(0 0% 35% 0.66)"; + angle = 29; + relative-to = "workspace-view"; + }) + ]) + ]) + + (plain "cursor" [ + (flag "hide-when-typing") + ]) + + (map (name: + (node "workspace" name [ + (leaf "open-on-output" "eDP-1") + ]) + ) (map ({name, ...}: name) cfg.scratchspaces)) + (map (name: + (leaf "workspace" name) + ) ["comm" "web" "vid" "bmr"]) + + (plain "window-rule" [ + (leaf "match" { is-floating = true; }) + (leaf "geometry-corner-radius" 8) + (leaf "clip-to-geometry" true) + ]) + + (plain "window-rule" [ + (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; }) + (leaf "block-out-from" "screencast") + ]) + (plain "window-rule" [ + (map (title: + (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; }) + ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$"]) + (leaf "open-focused" true) + (leaf "open-floating" true) + ]) + + (map ({ name, match, exclude, windowRuleExtra, ... }: + (optional-node (match != []) (plain "window-rule" [ + (map (leaf "match") match) + (map (leaf "exclude") exclude) + (leaf "open-on-workspace" name) + (leaf "open-maximized" true) + windowRuleExtra + ])) + ) cfg.scratchspaces) + + (plain "window-rule" [ + (leaf "match" { app-id = "^emacs$"; }) + (leaf "match" { app-id = "^firefox$"; }) + (plain "default-column-width" [(leaf "proportion" (2. / 3.))]) + ]) + (plain "window-rule" [ + (leaf "match" { app-id = "^kitty$"; }) + (leaf "match" { app-id = "^kitty-play$"; }) + (plain "default-column-width" [(leaf "proportion" (1. / 3.))]) + ]) + + (plain "window-rule" [ + (leaf "match" { app-id = "^thunderbird$"; }) + (leaf "match" { app-id = "^Element$"; }) + (leaf "match" { app-id = "^Rainbow$"; }) + (leaf "open-on-workspace" "comm") + ]) + (plain "window-rule" [ + (leaf "match" { app-id = "^firefox$"; }) + (leaf "open-on-workspace" "web") + (leaf "open-maximized" true) + ]) + (plain "window-rule" [ + (leaf "match" { app-id = "^mpv$"; }) + (leaf "open-on-workspace" "vid") + (plain "default-column-width" [(leaf "proportion" 1.)]) + ]) + (plain "window-rule" [ + (leaf "match" { app-id = "^kitty-play$"; }) + (leaf "open-on-workspace" "vid") + (leaf "open-focused" false) + ]) + (plain "window-rule" [ + (leaf "match" { app-id = "^pdfpc$"; }) + (plain "default-column-width" [(leaf "proportion" 1.)]) + ]) + (plain "window-rule" [ + (leaf "match" { app-id = "^pdfpc$"; title = "^pdfpc - presentation$"; }) + (plain "default-column-width" [(leaf "proportion" 1.)]) + (leaf "open-fullscreen" true) + (leaf "open-on-workspace" "bmr") + (leaf "open-focused" false) + ]) + (plain "window-rule" [ + (map (leaf "match") [ + { app-id = "^Gimp-"; title = "^Quit GIMP$"; } + { app-id = "^org\\.kde\\.polkit-kde-authentication-agent-1$"; } + { app-id = "^xdg-desktop-portal-gtk$"; } + ]) + (leaf "open-floating" true) + ]) + + (plain "layer-rule" [ + (leaf "match" { namespace = "^notifications$"; }) + (leaf "match" { namespace = "^waybar$"; }) + (leaf "match" { namespace = "^launcher$"; }) + (leaf "block-out-from" "screencast") + ]) + + (plain "binds" + (let + bind = name: cfg: node name (opt-props { + cooldown-ms = cfg.cooldown-ms or null; + } + // (lib.optionalAttrs (!(cfg.repeat or true)) { + repeat = false; + }) + // (lib.optionalAttrs (cfg.allow-when-locked or false) { + allow-when-locked = true; + })) (lib.mapAttrsToList leaf (lib.removeAttrs cfg.action ["__functor"])); + in + [ + (lib.mapAttrsToList bind (with config.lib.niri.actions; { + "Mod+Slash".action = show-hotkey-overlay; + + "Mod+Return".action = spawn terminal; + "Mod+Q".action = close-window; + "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package); + "Mod+Shift+O".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+Alt+Y".action = spawn (lib.getExe (pkgs.writeShellApplication { + name = "queue-yt-dlp"; + runtimeInputs = with pkgs; [ wl-clipboard-rs socat ]; + text = '' + socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/yt-dlp.sock <<<$'{ "urls": ["'"$(wl-paste)"$'"] }' + ''; + })); + "Mod+Alt+L".action = spawn (lib.getExe (pkgs.writeShellApplication { + name = "queue-yt-dlp"; + runtimeInputs = with pkgs; [ wl-clipboard-rs config.programs.kitty.package ]; + text = '' + exec -- kitty --app-id kitty-play --directory "$HOME"/media mpv "$(wl-paste)" + ''; + })); + + "Mod+U".action = spawn (lib.getExe (pkgs.writeShellApplication { + name = "qalc-fuzzel"; + runtimeInputs = with pkgs; [ wl-clipboard-rs libqalculate config.programs.fuzzel.package coreutils findutils libnotify gnugrep ]; + text = '' + RESULTS_DIR="$HOME/.cache/qalc-fuzzel" + prev() { + FOUND=false + while IFS= read -r line; do + [[ -n "$line" ]] || continue + FOUND=true + echo "$line" + done < <(export LC_ALL=C.UTF-8; echo; find "$RESULTS_DIR" -type f -printf $'%T@ %p\n' | sort -n | cut -d' ' -f2- | xargs -r cat) + $FOUND || echo + } + FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> ") || exit $? + if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then + QALC_RES="$FUZZEL_RES" + QALC_RET=0 + else + QALC_RES=$(qalc "$FUZZEL_RES" 2>&1) + QALC_RET=$? + fi + [[ -n "$QALC_RES" ]] || exit 1 + EXISTING=false + set +o pipefail + grep -Fxrl "$QALC_RES" "$RESULTS_DIR" | xargs -r touch + [[ ''${PIPESTATUS[0]} -eq 0 ]] && EXISTING=true + set -o pipefail + if [[ $QALC_RET -eq 0 ]] && ! $EXISTING; then + set +o pipefail + RES_FILE="$RESULTS_DIR"/$(date -uIs).$(tr -Cd 'a-zA-Z0-9' "$RES_FILE" <<<"$QALC_RES" + fi + [[ "$QALC_RES" =~ .*\ =\ (.*) ]] && QALC_RES="''${BASH_REMATCH[1]}" + [[ $QALC_RET -eq 0 ]] && wl-copy "$QALC_RES" + notify-send "$QALC_RES" + ''; + })); + "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication { + name = "emoji-fuzzel"; + runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ]; + text = '' + FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " <"$HOME"/.local/share/emoji-data/list.txt) || exit $? + [[ -n "$FUZZEL_RES" ]] || exit 1 + wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste + ''; + })); + "Print".action = spawn (lib.getExe (pkgs.writeShellApplication { + name = "screenshot"; + runtimeInputs = with pkgs; [ grim slurp wl-clipboard-rs coreutils ]; + text = '' + grim -g "$(slurp -b 00000080 -c FFFFFFFF -s 00000000 -w 1)" - \ + | tee "$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" \ + | wl-copy --type image/png + ''; + })); + "Shift+Print".action = spawn (lib.getExe (pkgs.writeShellApplication { + name = "screenshot"; + runtimeInputs = with pkgs; [ grim niri gojq wl-clipboard-rs coreutils ]; + text = '' + grim -o "$(niri msg -j workspaces | jq -r '.[] | select(.is_focused) | .output')" - \ + | tee "$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" \ + | wl-copy --type image/png + ''; + })); + "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; + "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; + + "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+Shift+M".action = toggle-column-tabbed-display; + + "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+V".action = switch-focus-between-floating-and-tiling; + "Mod+Shift+V".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; + "Mod+Shift+Minus" = { + action = spawn systemctl "suspend"; + allow-when-locked = true; + }; + "Mod+Shift+Control+Minus" = { + action = spawn systemctl "hibernate"; + allow-when-locked = true; + }; + "Mod+Shift+P" = { + action = spawn (lib.getExe pkgs.playerctl) "-a" "pause"; + allow-when-locked = true; + }; + + "XF86MonBrightnessUp" = { + action = spawn swayosd-client "--brightness" "raise"; + allow-when-locked = true; + }; + "XF86MonBrightnessDown" = { + action = spawn swayosd-client "--brightness" "lower"; + allow-when-locked = true; + }; + "XF86AudioRaiseVolume" = { + action = spawn swayosd-client "--output-volume" "raise"; + allow-when-locked = true; + }; + "XF86AudioLowerVolume" = { + action = spawn swayosd-client "--output-volume" "lower"; + allow-when-locked = true; + }; + "XF86AudioMute" = { + action = spawn swayosd-client "--output-volume" "mute-toggle"; + allow-when-locked = true; + }; + "XF86AudioMicMute" = { + action = spawn swayosd-client "--input-volume" "mute-toggle"; + allow-when-locked = true; + }; + + "Mod+Semicolon".action = spawn makoctl "dismiss" "--group"; + "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all"; + "Mod+Period".action = spawn makoctl "menu" (lib.getExe config.programs.fuzzel.package) "--dmenu"; + "Mod+Comma".action = spawn makoctl "restore"; + })) + (map ({ name, selector, spawn, key, ...}: if key != null && selector != null && spawn != null then bind key { action = focus-or-spawn-action selector name spawn; } else null) cfg.scratchspaces) + ] + )) ]; - - always-center-single-column = true; - }; - - cursor.hide-when-typing = true; - - input = { - touchpad.enable = false; - trackball = { - scroll-method = "on-button-down"; - scroll-button = 278; - }; - }; - - workspaces = { - "001" = { name = "pwctl"; open-on-output = "eDP-1"; }; - "002" = { name = "kpxc"; open-on-output = "eDP-1"; }; - "003" = { name = "bmgr"; open-on-output = "eDP-1"; }; - "004" = { name = "term"; open-on-output = "eDP-1"; }; - "005" = { name = "edit"; open-on-output = "eDP-1"; }; - "006" = { name = "eff"; open-on-output = "eDP-1"; }; - "101".name = "comm"; - "102".name = "web"; - # "104".name = "read"; - # "105".name = "mon"; - "110".name = "vid"; - "120".name = "bmr"; - }; - - window-rules = [ - { - matches = [ { is-floating = true; } ]; - geometry-corner-radius = - let - allCorners = r: { bottom-left = r; bottom-right = r; top-left = r; top-right = r; }; - in allCorners 8.; - clip-to-geometry = true; - } - { - matches = [ { app-id = "^com\.saivert\.pwvucontrol$"; } ]; - open-on-workspace = "pwctl"; - open-maximized = true; - } - { - matches = [ { app-id = "^com\.github\.wwmm\.easyeffects$"; } ]; - open-on-workspace = "eff"; - open-maximized = true; - } - { - matches = [ { app-id = "^\.blueman-manager-wrapped$"; } ]; - open-on-workspace = "bmgr"; - open-maximized = true; - } - { - matches = [ { app-id = "^org\.keepassxc\.KeePassXC$"; } ]; - block-out-from = "screencast"; - } - { - matches = [ { app-id = "^org\.keepassxc\.KeePassXC$"; } ]; - excludes = [ - { title = "^Unlock Database.*"; } - { title = "^Access Request.*"; } - { title = ".*Passkey credentials$"; } - ]; - open-on-workspace = "kpxc"; - open-maximized = true; - open-focused = false; - } - { - matches = [ - { app-id = "^org\.keepassxc\.KeePassXC$"; title = "^Unlock Database.*"; } - { app-id = "^org\.keepassxc\.KeePassXC$"; title = "^Access Request.*"; } - { app-id = "^org\.keepassxc\.KeePassXC$"; title = ".*Passkey credentials$"; } - ]; - open-focused = true; - open-floating = true; - } - { - matches = [ { app-id = "^kitty-scratch$"; } ]; - open-on-workspace = "term"; - open-maximized = true; - } - { - matches = [ { title = "^scratch$"; app-id = "^emacs$"; } ]; - open-on-workspace = "edit"; - open-maximized = true; - } - { - matches = [ - { app-id = "^emacs$"; } - { app-id = "^firefox$"; } - ]; - default-column-width.proportion = 2. / 3.; - } - { - matches = [ - { app-id = "^kitty$"; } - { app-id = "^kitty-play$"; } - ]; - default-column-width.proportion = 1. / 3.; - } - { - matches = [ - { app-id = "^thunderbird$"; } - { app-id = "^Element$"; } - { app-id = "^Rainbow$"; } - ]; - open-on-workspace = "comm"; - } - { - matches = [ { app-id = "^firefox$"; } ]; - open-on-workspace = "web"; - open-maximized = true; - variable-refresh-rate = true; - } - # { - # matches = [ - # { app-id = "^evince$"; } - # { app-id = "^imv$"; } - # { app-id = "^org\.pwmt\.zathura$"; } - # ]; - # open-on-workspace = "read"; - # } - { - matches = [ { app-id = "^mpv$"; } ]; - open-on-workspace = "vid"; - default-column-width.proportion = 1.; - variable-refresh-rate = true; - } - { - matches = [ { app-id = "^kitty-play$"; } ]; - open-on-workspace = "vid"; - open-focused = false; - } - # { - # matches = [ - # { app-id = "^qemu$"; } - # { app-id = "^virt-manager$"; } - # ]; - # open-on-workspace = "mon"; - # } - { - matches = [ { app-id = "^pdfpc$"; } ]; - default-column-width.proportion = 1.; - } - { - matches = [ { app-id = "^pdfpc$"; title = "^pdfpc - presentation"; } ]; - open-on-workspace = "bmr"; - open-fullscreen = true; - } - { - matches = [ - { app-id = "^Gimp-"; title = "^Quit GIMP$"; } - { app-id = "^org\.kde\.polkit-kde-authentication-agent-1$"; } - { app-id = "^xdg-desktop-portal-gtk$"; } - ]; - open-floating = true; - } - ]; - layer-rules = [ - { matches = [ - { namespace = "^notifications$"; } - { namespace = "^waybar$"; } - ]; - block-out-from = "screencast"; - } - ]; - - binds = with config.lib.niri.actions; { - "Mod+Slash".action = show-hotkey-overlay; - - "Mod+Return".action = spawn terminal; - "Mod+Q".action = close-window; - "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package); - "Mod+Shift+O".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+Alt+Y".action = spawn (lib.getExe (pkgs.writeShellApplication { - name = "queue-yt-dlp"; - runtimeInputs = with pkgs; [ wl-clipboard-rs socat ]; - text = '' - socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/yt-dlp.sock <<<$'{ "urls": ["'"$(wl-paste)"$'"] }' - ''; - })); - "Mod+Alt+L".action = spawn (lib.getExe (pkgs.writeShellApplication { - name = "queue-yt-dlp"; - runtimeInputs = with pkgs; [ wl-clipboard-rs config.programs.kitty.package ]; - text = '' - exec -- kitty --app-id kitty-play --directory "$HOME"/media mpv "$(wl-paste)" - ''; - })); - - "Mod+U".action = spawn (lib.getExe (pkgs.writeShellApplication { - name = "qalc-fuzzel"; - runtimeInputs = with pkgs; [ wl-clipboard-rs libqalculate config.programs.fuzzel.package coreutils findutils libnotify gnugrep ]; - text = '' - RESULTS_DIR="$HOME/.cache/qalc-fuzzel" - prev() { - FOUND=false - while IFS= read -r line; do - [[ -n "$line" ]] || continue - FOUND=true - echo "$line" - done < <(export LC_ALL=C.UTF-8; echo; find "$RESULTS_DIR" -type f -printf $'%T@ %p\n' | sort -n | cut -d' ' -f2- | xargs -r cat) - $FOUND || echo - } - FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> ") || exit $? - if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then - QALC_RES="$FUZZEL_RES" - QALC_RET=0 - else - QALC_RES=$(qalc "$FUZZEL_RES" 2>&1) - QALC_RET=$? - fi - [[ -n "$QALC_RES" ]] || exit 1 - EXISTING=false - set +o pipefail - grep -Fxrl "$QALC_RES" "$RESULTS_DIR" | xargs -r touch - [[ ''${PIPESTATUS[0]} -eq 0 ]] && EXISTING=true - set -o pipefail - if [[ $QALC_RET -eq 0 ]] && ! $EXISTING; then - set +o pipefail - RES_FILE="$RESULTS_DIR"/$(date -uIs).$(tr -Cd 'a-zA-Z0-9' "$RES_FILE" <<<"$QALC_RES" - fi - [[ "$QALC_RES" =~ .*\ =\ (.*) ]] && QALC_RES="''${BASH_REMATCH[1]}" - [[ $QALC_RET -eq 0 ]] && wl-copy "$QALC_RES" - notify-send "$QALC_RES" - ''; - })); - "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication { - name = "emoji-fuzzel"; - runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ]; - text = '' - FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " <"$HOME"/.local/share/emoji-data/list.txt) || exit $? - [[ -n "$FUZZEL_RES" ]] || exit 1 - wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste - ''; - })); - "Print".action = spawn (lib.getExe (pkgs.writeShellApplication { - name = "screenshot"; - runtimeInputs = with pkgs; [ grim slurp wl-clipboard-rs coreutils ]; - text = '' - grim -g "$(slurp -b 00000080 -c FFFFFFFF -s 00000000 -w 1)" - \ - | tee "$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" \ - | wl-copy --type image/png - ''; - })); - "Shift+Print".action = spawn (lib.getExe (pkgs.writeShellApplication { - name = "screenshot"; - runtimeInputs = with pkgs; [ grim niri gojq wl-clipboard-rs coreutils ]; - text = '' - grim -o "$(niri msg -j workspaces | jq -r '.[] | select(.is_focused) | .output')" - \ - | tee "$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" \ - | wl-copy --type image/png - ''; - })); - "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; - "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; - - "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+V".action = switch-focus-between-floating-and-tiling; - "Mod+Shift+V".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; - "Mod+Shift+Minus" = { - action = spawn systemctl "suspend"; - allow-when-locked = true; - }; - "Mod+Shift+Control+Minus" = { - action = spawn systemctl "hibernate"; - allow-when-locked = true; - }; - "Mod+Shift+P" = { - action = spawn (lib.getExe pkgs.playerctl) "-a" "pause"; - allow-when-locked = true; - }; - - "XF86MonBrightnessUp" = { - action = spawn swayosd-client "--brightness" "raise"; - allow-when-locked = true; - }; - "XF86MonBrightnessDown" = { - action = spawn swayosd-client "--brightness" "lower"; - allow-when-locked = true; - }; - "XF86AudioRaiseVolume" = { - action = spawn swayosd-client "--output-volume" "raise"; - allow-when-locked = true; - }; - "XF86AudioLowerVolume" = { - action = spawn swayosd-client "--output-volume" "lower"; - allow-when-locked = true; - }; - "XF86AudioMute" = { - action = spawn swayosd-client "--output-volume" "mute-toggle"; - allow-when-locked = true; - }; - "XF86AudioMicMute" = { - action = spawn swayosd-client "--input-volume" "mute-toggle"; - allow-when-locked = true; - }; - - "Mod+Semicolon".action = spawn makoctl "dismiss" "--group"; - "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all"; - "Mod+Period".action = spawn makoctl "menu" (lib.getExe config.programs.fuzzel.package) "--dmenu"; - "Mod+Comma".action = spawn makoctl "restore"; - - "Mod+Control+A".action = focus-or-spawn-action-app_id "com.saivert.pwvucontrol" "pwctl" "pwvucontrol"; - "Mod+Control+O".action = focus-or-spawn-action-app_id "com.github.wwmm.easyeffects" "eff" "easyeffects"; - "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\"))"; - }; - }; }; } -- cgit v1.2.3