summaryrefslogtreecommitdiff
path: root/accounts/gkleen@sif/niri/default.nix
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/gkleen@sif/niri/default.nix')
-rw-r--r--accounts/gkleen@sif/niri/default.nix439
1 files changed, 247 insertions, 192 deletions
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix
index 924d3843..5ae372c1 100644
--- a/accounts/gkleen@sif/niri/default.nix
+++ b/accounts/gkleen@sif/niri/default.nix
@@ -3,13 +3,10 @@ let
3 cfg = config.programs.niri; 3 cfg = config.programs.niri;
4 4
5 kdl = flakeInputs.niri-flake.lib.kdl; 5 kdl = flakeInputs.niri-flake.lib.kdl;
6 sleaf = name: arg: kdl.node name [arg] [];
6 7
7 niri = cfg.package; 8 niri = cfg.package;
8 terminal = lib.getExe config.programs.kitty.package; 9 terminal = lib.getExe config.programs.kitty.package;
9 makoctl = lib.getExe' config.services.mako.package "makoctl";
10 loginctl = lib.getExe' hostConfig.systemd.package "loginctl";
11 systemctl = lib.getExe' hostConfig.systemd.package "systemctl";
12 swayosd-client = lib.getExe' config.services.swayosd.package "swayosd-client";
13 10
14 focus_or_spawn = pkgs.writeShellApplication { 11 focus_or_spawn = pkgs.writeShellApplication {
15 name = "focus-or-spawn"; 12 name = "focus-or-spawn";
@@ -35,7 +32,11 @@ let
35 if jq -e '.is_focused' <<<"$window_json" >/dev/null; then 32 if jq -e '.is_focused' <<<"$window_json" >/dev/null; then
36 niri msg action focus-workspace-previous 33 niri msg action focus-workspace-previous
37 else 34 else
38 niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")" 35 if [[ $(jq -r --arg workspace_name "$workspace_name" 'map(select(.name == $workspace_name)) | .[0].is_focused' <<<"$workspaces_json") != "true" ]] && [[ $(jq -r --arg workspace_name "$workspace_name" 'map(select(.name == $workspace_name)) | .[0].id' <<<"$workspaces_json") = $(jq -r '.workspace_id' <<<"$window_json") ]]; then
36 niri msg action focus-workspace "$workspace_name"
37 else
38 niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")"
39 fi
39 fi 40 fi
40 exit 0 41 exit 0
41 fi 42 fi
@@ -45,7 +46,6 @@ let
45 ''; 46 '';
46 }; 47 };
47 focus-or-spawn-action = config.lib.niri.actions.spawn (lib.getExe focus_or_spawn); 48 focus-or-spawn-action = config.lib.niri.actions.spawn (lib.getExe focus_or_spawn);
48 focus-or-spawn-action-app_id = app_id: focus-or-spawn-action ''select(.app_id == "${app_id}")'';
49 49
50 with_adjacent_workspace = pkgs.writeShellApplication { 50 with_adjacent_workspace = pkgs.writeShellApplication {
51 name = "with-adjacent-workspace"; 51 name = "with-adjacent-workspace";
@@ -84,7 +84,7 @@ let
84 }; 84 };
85 with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^${lib.concatMapStringsSep "|" ({ name, ...}: name) cfg.scratchspaces}$"; 85 with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^${lib.concatMapStringsSep "|" ({ name, ...}: name) cfg.scratchspaces}$";
86 focus-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; 86 focus-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}'';
87 move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}''; 87 move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}'';
88 88
89 with_unnamed_workspace = pkgs.writeShellApplication { 89 with_unnamed_workspace = pkgs.writeShellApplication {
90 name = "with-unnamed-workspace"; 90 name = "with-unnamed-workspace";
@@ -131,7 +131,7 @@ let
131 131
132 windows_json="$(niri msg -j windows)" 132 windows_json="$(niri msg -j windows)"
133 active_workspace="$(jq -r '.[] | select(.is_focused) | .workspace_id' <<<"$windows_json")" 133 active_workspace="$(jq -r '.[] | select(.is_focused) | .workspace_id' <<<"$windows_json")"
134 window_ix="$(gojq -r --arg active_workspace "$active_workspace" '.[] | select('"$window_select"') | "\(.title)\u0000icon\u001f\(.app_id)"' <<<"$windows_json" | fuzzel --log-level=warning --dmenu --index)" 134 window_ix="$(gojq -r --arg active_workspace "$active_workspace" '.[] | select('"$window_select"') | "\(.title)\u0000icon\u001f\(.app_id)"' <<<"$windows_json" | fuzzel --width=60 --log-level=warning --dmenu --index)"
135 # shellcheck disable=SC2016 135 # shellcheck disable=SC2016
136 window_json="$(gojq -rc --arg active_workspace "$active_workspace" --arg window_ix "$window_ix" 'map(select('"$window_select"')) | .[($window_ix | tonumber)]' <<<"$windows_json")" 136 window_json="$(gojq -rc --arg active_workspace "$active_workspace" --arg window_ix "$window_ix" 'map(select('"$window_select"')) | .[($window_ix | tonumber)]' <<<"$windows_json")"
137 137
@@ -141,13 +141,26 @@ let
141 ''; 141 '';
142 }; 142 };
143 with-select-window-action = config.lib.niri.actions.spawn (lib.getExe with_select_window); 143 with-select-window-action = config.lib.niri.actions.spawn (lib.getExe with_select_window);
144in {
145 imports = [
146 ./waybar.nix
147 ./mako.nix
148 ./swayosd.nix
149 ];
150 144
145 with_predicate_window = pred: pkgs.writeShellApplication {
146 name = "with-predicate-window";
147 runtimeInputs = [ niri pkgs.gojq pkgs.socat ];
148 text = ''
149 action="$1"
150 shift
151
152 windows_json="$(niri msg -j windows)"
153 window_json="$(gojq -rc 'map(select(${pred})) | .[0]' <<<"$windows_json")"
154
155 [[ -z "$window_json" || $window_json = "null" ]] && exit 1
156
157 jq -c "$action" <<<"$window_json" | socat STDIO "$NIRI_SOCKET"
158 '';
159 };
160
161 with-urgent-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_urgent"));
162 with-focused-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_focused"));
163in {
151 options = { 164 options = {
152 programs.niri.scratchspaces = lib.mkOption { 165 programs.niri.scratchspaces = lib.mkOption {
153 type = lib.types.listOf (lib.types.submodule ({ config, ... }: { 166 type = lib.types.listOf (lib.types.submodule ({ config, ... }: {
@@ -171,6 +184,17 @@ in {
171 type = lib.types.nullOr lib.types.str; 184 type = lib.types.nullOr lib.types.str;
172 default = null; 185 default = null;
173 }; 186 };
187 moveKey = lib.mkOption {
188 type = lib.types.nullOr lib.types.str;
189 default = let
190 keys = lib.splitString "+" config.key;
191 defMoveKey = lib.concatStringsSep "+" (lib.flatten [
192 (lib.take (lib.length keys - 1) keys)
193 ["Shift"]
194 (lib.takeEnd 1 keys)
195 ]);
196 in if config.key == null then null else defMoveKey;
197 };
174 spawn = lib.mkOption { 198 spawn = lib.mkOption {
175 type = lib.types.nullOr (lib.types.listOf lib.types.str); 199 type = lib.types.nullOr (lib.types.listOf lib.types.str);
176 default = null; 200 default = null;
@@ -197,36 +221,7 @@ in {
197 }; 221 };
198 222
199 config = { 223 config = {
200 systemd.user.services.xwayland-satellite = { 224 home.packages = [ pkgs.xwayland-satellite-unstable ];
201 Unit = {
202 BindsTo = [ "graphical-session.target" ];
203 PartOf = [ "graphical-session.target" ];
204 After = [ "graphical-session.target" ];
205 Requisite = [ "graphical-session.target" ];
206 };
207 Service = {
208 Type = "notify";
209 NotifyAccess = "all";
210 Environment = [ "DISPLAY=:0" ];
211 ExecStart = ''${lib.getExe pkgs.xwayland-satellite-unstable} ''${DISPLAY}'';
212 ExecStartPre = "${systemctl} --user import-environment DISPLAY";
213 StandardOutput = "journal";
214 };
215 Install = {
216 WantedBy = [ "graphical-session.target" ];
217 };
218 };
219
220 services.swayidle = {
221 events = [
222 { event = "after-resume"; command = "${lib.getExe niri} msg action power-on-monitors"; }
223 ];
224 timeouts = [
225 { timeout = 540;
226 command = "${lib.getExe niri} msg action power-off-monitors";
227 }
228 ];
229 };
230 225
231 systemd.user.sockets.niri-workspace-history = { 226 systemd.user.sockets.niri-workspace-history = {
232 Socket = { 227 Socket = {
@@ -416,8 +411,8 @@ in {
416 { title = "^Access Request.*"; } 411 { title = "^Access Request.*"; }
417 { title = ".*Passkey credentials$"; } 412 { title = ".*Passkey credentials$"; }
418 ]; 413 ];
419 windowRuleExtra = [ 414 windowRuleExtra = with kdl; [
420 (kdl.leaf "open-focused" false) 415 (sleaf "open-focused" false)
421 ]; 416 ];
422 key = "Mod+Control+P"; 417 key = "Mod+Control+P";
423 app-id = "org.keepassxc.KeePassXC"; 418 app-id = "org.keepassxc.KeePassXC";
@@ -444,6 +439,20 @@ in {
444 app-id = "com.github.wwmm.easyeffects"; 439 app-id = "com.github.wwmm.easyeffects";
445 spawn = [ "easyeffects" ]; 440 spawn = [ "easyeffects" ];
446 } 441 }
442 { name = "time";
443 key = "Mod+Control+K";
444 app-id = "chrome-kimai.yggdrasil.li__-Default";
445 spawn = [ (toString (pkgs.resholve.writeScript "kimai" {
446 interpreter = pkgs.runtimeShell;
447 inputs = [ pkgs.dex ];
448 execer = [ "cannot:${lib.getExe pkgs.dex}" ];
449 } ''
450 exec dex $HOME/.local/state/nix/profile/share/applications/kimai.desktop
451 '')) ];
452 windowRuleExtra = with kdl; [
453 (sleaf "block-out-from" "screencast")
454 ];
455 }
447 ]; 456 ];
448 programs.niri.config = 457 programs.niri.config =
449 let 458 let
@@ -453,10 +462,12 @@ in {
453 then v 462 then v
454 else null; 463 else null;
455 opt-props = lib.filterAttrs (lib.const (value: value != null)); 464 opt-props = lib.filterAttrs (lib.const (value: value != null));
465 normalize-nodes = nodes: lib.remove null (lib.flatten nodes);
456 in 466 in
457 [ (flag "prefer-no-csd") 467 normalize-nodes [
468 (flag "prefer-no-csd")
458 469
459 (leaf "screenshot-path" "~/screenshots/%Y-%m-%dT%H:%M:%S.png") 470 (sleaf "screenshot-path" "~/screenshots/%Y-%m-%dT%H:%M:%S.png")
460 471
461 (plain "hotkey-overlay" [ 472 (plain "hotkey-overlay" [
462 (flag "skip-at-startup") 473 (flag "skip-at-startup")
@@ -464,27 +475,27 @@ in {
464 475
465 (plain "input" [ 476 (plain "input" [
466 (plain "keyboard" [ 477 (plain "keyboard" [
467 (leaf "repeat-delay" 300) 478 (sleaf "repeat-delay" 300)
468 (leaf "repeat-rate" 50) 479 (sleaf "repeat-rate" 50)
469 480
470 (plain "xkb" [ 481 (plain "xkb" [
471 (leaf "layout" "us,us") 482 (sleaf "layout" "us,us")
472 (leaf "variant" "dvp,") 483 (sleaf "variant" "dvp,")
473 (leaf "options" "compose:caps,grp:win_space_toggle") 484 (sleaf "options" "compose:caps,grp:win_space_toggle")
474 ]) 485 ])
475 ]) 486 ])
476 487
477 (flag "workspace-auto-back-and-forth") 488 (flag "workspace-auto-back-and-forth")
478 # (leaf "focus-follows-mouse" {}) 489 # (sleaf "focus-follows-mouse" {})
479 # (flag "warp-mouse-to-focus") 490 # (flag "warp-mouse-to-focus")
480 491
481 # (plain "touchpad" [ (flag "off") ]) 492 # (plain "touchpad" [ (flag "off") ])
482 (plain "trackball" [ 493 (plain "trackball" [
483 (leaf "scroll-method" "on-button-down") 494 (sleaf "scroll-method" "on-button-down")
484 (leaf "scroll-button" 278) 495 (sleaf "scroll-button" 278)
485 ]) 496 ])
486 (plain "touch" [ 497 (plain "touch" [
487 (leaf "map-to-output" "eDP-1") 498 (sleaf "map-to-output" "eDP-1")
488 ]) 499 ])
489 ]) 500 ])
490 501
@@ -492,7 +503,7 @@ in {
492 (plain "hot-corners" [(flag "off")]) 503 (plain "hot-corners" [(flag "off")])
493 ]) 504 ])
494 505
495 (plain "environment" (lib.mapAttrsToList leaf { 506 (plain "environment" (lib.mapAttrsToList sleaf {
496 NIXOS_OZONE_WL = "1"; 507 NIXOS_OZONE_WL = "1";
497 QT_QPA_PLATFORM = "wayland"; 508 QT_QPA_PLATFORM = "wayland";
498 QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; 509 QT_WAYLAND_DISABLE_WINDOWDECORATION = "1";
@@ -500,49 +511,52 @@ in {
500 SDL_VIDEODRIVER = "wayland"; 511 SDL_VIDEODRIVER = "wayland";
501 DISPLAY = ":0"; 512 DISPLAY = ":0";
502 ELECTRON_OZONE_PLATFORM_HINT = "auto"; 513 ELECTRON_OZONE_PLATFORM_HINT = "auto";
514 SSH_ASKPASS_REQUIRE = "prefer";
515 SSH_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass;
516 SUDO_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass;
503 })) 517 }))
504 518
505 (node "output" "eDP-1" [ 519 (node "output" ["eDP-1"] [
506 (leaf "scale" 1.5) 520 (sleaf "scale" 1.5)
507 (leaf "position" { x = 0; y = 0; }) 521 (sleaf "position" { x = 0; y = 0; })
508 ]) 522 ])
509 (node "output" "Ancor Communications Inc ASUS PB287Q 0x0000DD9B" [ 523 (node "output" ["Ancor Communications Inc ASUS PB287Q 0x0000DD9B"] [
510 (leaf "scale" 1.5) 524 (sleaf "scale" 1.5)
511 (leaf "position" { x = 2560; y = 0; }) 525 (sleaf "position" { x = 2560; y = 0; })
512 ]) 526 ])
513 (node "output" "HP Inc. HP 727pu CN4417143K" [ 527 (node "output" ["HP Inc. HP 727pu CN4417143K"] [
514 (leaf "mode" "2560x1440@119.998") 528 (sleaf "mode" "2560x1440@119.998")
515 (leaf "scale" 1) 529 (sleaf "scale" 1)
516 (leaf "position" { x = 2560; y = 0; }) 530 (sleaf "position" { x = 2560; y = 0; })
517 (flag "variable-refresh-rate") 531 (flag "variable-refresh-rate")
518 ]) 532 ])
519 533
520 (plain "debug" [ 534 (plain "debug" [
521 (leaf "render-drm-device" "/dev/dri/by-path/pci-0000:00:02.0-render") 535 (sleaf "render-drm-device" "/dev/dri/by-path/pci-0000:00:02.0-render")
522 ]) 536 ])
523 537
524 (plain "animations" [ 538 (plain "animations" [
525 (leaf "slowdown" 0.5) 539 (sleaf "slowdown" 0.5)
526 (plain "workspace-switch" [(flag "off")]) 540 (plain "workspace-switch" [(flag "off")])
527 ]) 541 ])
528 542
529 (plain "layout" [ 543 (plain "layout" [
530 (leaf "gaps" 8) 544 (sleaf "gaps" 8)
531 (plain "struts" [ 545 (plain "struts" [
532 (leaf "left" 26) 546 (sleaf "left" 26)
533 (leaf "right" 26) 547 (sleaf "right" 26)
534 (leaf "top" 0) 548 (sleaf "top" 0)
535 (leaf "bottom" 0) 549 (sleaf "bottom" 0)
536 ]) 550 ])
537 (plain "border" [ 551 (plain "border" [
538 (leaf "width" 2) 552 (sleaf "width" 2)
539 (leaf "active-gradient" { 553 (sleaf "active-gradient" {
540 from = "hsla(195 100% 45% 1)"; 554 from = "hsla(195 100% 45% 1)";
541 to = "hsla(155 100% 37.5% 1)"; 555 to = "hsla(155 100% 37.5% 1)";
542 angle = 29; 556 angle = 29;
543 relative-to = "workspace-view"; 557 relative-to = "workspace-view";
544 }) 558 })
545 (leaf "inactive-gradient" { 559 (sleaf "inactive-gradient" {
546 from = "hsla(0 0% 27.7% 1)"; 560 from = "hsla(0 0% 27.7% 1)";
547 to = "hsla(0 0% 23% 1)"; 561 to = "hsla(0 0% 23% 1)";
548 angle = 29; 562 angle = 29;
@@ -553,29 +567,29 @@ in {
553 (flag "off") 567 (flag "off")
554 ]) 568 ])
555 569
556 (plain "preset-column-widths" (map (prop: leaf "proportion" prop) [ 570 (plain "preset-column-widths" (map (prop: sleaf "proportion" prop) [
557 (1. / 4.) (1. / 3.) (1. / 2.) (2. / 3.) (3. / 4.) (1.) 571 (1. / 4.) (1. / 3.) (1. / 2.) (2. / 3.) (3. / 4.) (1.)
558 ])) 572 ]))
559 (plain "default-column-width" [ (leaf "proportion" (1. / 2.)) ]) 573 (plain "default-column-width" [ (sleaf "proportion" (1. / 2.)) ])
560 (plain "preset-window-heights" (map (prop: leaf "proportion" prop) [ 574 (plain "preset-window-heights" (map (prop: sleaf "proportion" prop) [
561 (1. / 3.) (1. / 2.) (2. / 3.) (1.) 575 (1. / 3.) (1. / 2.) (2. / 3.) (1.)
562 ])) 576 ]))
563 577
564 (flag "always-center-single-column") 578 (flag "always-center-single-column")
565 579
566 (plain "tab-indicator" [ 580 (plain "tab-indicator" [
567 (leaf "gap" 4) 581 (sleaf "gap" 4)
568 (leaf "width" 8) 582 (sleaf "width" 8)
569 (leaf "gaps-between-tabs" 4) 583 (sleaf "gaps-between-tabs" 4)
570 (flag "place-within-column") 584 (flag "place-within-column")
571 (leaf "length" { total-proportion = 1.; }) 585 (sleaf "length" { total-proportion = 1.; })
572 (leaf "active-gradient" { 586 (sleaf "active-gradient" {
573 from = "hsla(195 100% 60% 0.75)"; 587 from = "hsla(195 100% 60% 0.75)";
574 to = "hsla(155 100% 50% 0.75)"; 588 to = "hsla(155 100% 50% 0.75)";
575 angle = 29; 589 angle = 29;
576 relative-to = "workspace-view"; 590 relative-to = "workspace-view";
577 }) 591 })
578 (leaf "inactive-gradient" { 592 (sleaf "inactive-gradient" {
579 from = "hsla(0 0% 42% 0.66)"; 593 from = "hsla(0 0% 42% 0.66)";
580 to = "hsla(0 0% 35% 0.66)"; 594 to = "hsla(0 0% 35% 0.66)";
581 angle = 29; 595 angle = 29;
@@ -589,129 +603,140 @@ in {
589 ]) 603 ])
590 604
591 (map (name: 605 (map (name:
592 (node "workspace" name [ 606 (node "workspace" [name] [
593 (leaf "open-on-output" "eDP-1") 607 (sleaf "open-on-output" "eDP-1")
594 ]) 608 ])
595 ) (map ({name, ...}: name) cfg.scratchspaces)) 609 ) (map ({name, ...}: name) cfg.scratchspaces))
596 (map (name: 610 (map (name:
597 (leaf "workspace" name) 611 (sleaf "workspace" name)
598 ) ["comm" "web" "vid" "bmr"]) 612 ) ["comm" "web" "vid" "bmr"])
599 613
600 (plain "window-rule" [ 614 (plain "window-rule" [
601 (leaf "clip-to-geometry" true) 615 (sleaf "clip-to-geometry" true)
602 ]) 616 ])
603 617
604 (plain "window-rule" [ 618 (plain "window-rule" [
605 (leaf "match" { is-floating = true; }) 619 (sleaf "match" { is-floating = true; })
606 (leaf "geometry-corner-radius" 8) 620 (sleaf "geometry-corner-radius" 8)
607 (plain "shadow" [ (flag "on") ]) 621 (plain "shadow" [ (flag "on") ])
608 ]) 622 ])
609 623
610 (plain "window-rule" [ 624 (plain "window-rule" [
611 (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; }) 625 (sleaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; })
612 (leaf "block-out-from" "screencast") 626 (sleaf "block-out-from" "screencast")
613 ]) 627 ])
614 (plain "window-rule" [ 628 (plain "window-rule" (normalize-nodes [
615 (map (title: 629 (map (title:
616 (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; }) 630 (sleaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; })
617 ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$"]) 631 ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$" "Browser Access Request$"])
618 (leaf "open-focused" true) 632 (sleaf "open-focused" true)
619 (leaf "open-floating" true) 633 (sleaf "open-floating" true)
620 ]) 634 ]))
621 635
622 (map ({ name, match, exclude, windowRuleExtra, ... }: 636 (map ({ name, match, exclude, windowRuleExtra, ... }:
623 (optional-node (match != []) (plain "window-rule" [ 637 (optional-node (match != []) (plain "window-rule" (normalize-nodes [
624 (map (leaf "match") match) 638 (map (sleaf "match") match)
625 (map (leaf "exclude") exclude) 639 (map (sleaf "exclude") exclude)
626 (leaf "open-on-workspace" name) 640 (sleaf "open-on-workspace" name)
627 (leaf "open-maximized" true) 641 (sleaf "open-maximized" true)
628 windowRuleExtra 642 windowRuleExtra
629 ])) 643 ])))
630 ) cfg.scratchspaces) 644 ) cfg.scratchspaces)
631 645
632 (plain "window-rule" [ 646 (plain "window-rule" [
633 (leaf "match" { app-id = "^emacs$"; }) 647 (sleaf "match" { app-id = "^emacs$"; })
634 (leaf "match" { app-id = "^firefox$"; }) 648 (sleaf "match" { app-id = "^firefox$"; })
635 (plain "default-column-width" [(leaf "proportion" (2. / 3.))]) 649 (plain "default-column-width" [(sleaf "proportion" (2. / 3.))])
636 ]) 650 ])
637 (plain "window-rule" [ 651 (plain "window-rule" [
638 (leaf "match" { app-id = "^kitty$"; }) 652 (sleaf "match" { app-id = "^kitty$"; })
639 (leaf "match" { app-id = "^kitty-play$"; }) 653 (sleaf "match" { app-id = "^kitty-play$"; })
640 (plain "default-column-width" [(leaf "proportion" (1. / 3.))]) 654 (plain "default-column-width" [(sleaf "proportion" (1. / 3.))])
641 ]) 655 ])
642 656
643 (plain "window-rule" [ 657 (plain "window-rule" [
644 (leaf "match" { app-id = "^thunderbird$"; }) 658 (sleaf "match" { app-id = "^thunderbird$"; })
645 (leaf "match" { app-id = "^Element$"; }) 659 (sleaf "match" { app-id = "^Element$"; })
646 (leaf "match" { app-id = "^Rainbow$"; }) 660 (sleaf "match" { app-id = "^chrome-web\.openrainbow\.com__-Default$"; })
647 (leaf "open-on-workspace" "comm") 661 (sleaf "open-on-workspace" "comm")
648 ]) 662 ])
649 (plain "window-rule" [ 663 (plain "window-rule" [
650 (leaf "match" { app-id = "^firefox$"; }) 664 (sleaf "match" { app-id = "^firefox$"; })
651 (leaf "open-on-workspace" "web") 665 (sleaf "open-on-workspace" "web")
652 (leaf "open-maximized" true) 666 (sleaf "open-maximized" true)
653 ]) 667 ])
654 (plain "window-rule" [ 668 (plain "window-rule" [
655 (leaf "match" { app-id = "^mpv$"; }) 669 (sleaf "match" { app-id = "^mpv$"; })
656 (leaf "open-on-workspace" "vid") 670 (sleaf "open-on-workspace" "vid")
657 (plain "default-column-width" [(leaf "proportion" 1.)]) 671 (plain "default-column-width" [(sleaf "proportion" 1.)])
658 ]) 672 ])
659 (plain "window-rule" [ 673 (plain "window-rule" [
660 (leaf "match" { app-id = "^kitty-play$"; }) 674 (sleaf "match" { app-id = "^kitty-play$"; })
661 (leaf "open-on-workspace" "vid") 675 (sleaf "open-on-workspace" "vid")
662 (leaf "open-focused" false) 676 (sleaf "open-focused" false)
663 ]) 677 ])
664 (plain "window-rule" [ 678 (plain "window-rule" [
665 (leaf "match" { app-id = "^pdfpc$"; }) 679 (sleaf "match" { app-id = "^chrome-audiobookshelf\.yggdrasil\.li__-Default$"; })
666 (plain "default-column-width" [(leaf "proportion" 1.)]) 680 (sleaf "match" { app-id = "^YouTube Music Desktop App$"; })
681 (sleaf "open-on-workspace" "vid")
667 ]) 682 ])
668 (plain "window-rule" [ 683 (plain "window-rule" [
669 (leaf "match" { app-id = "^pdfpc$"; title = "^.*presentation.*$"; }) 684 (sleaf "match" { app-id = "^pdfpc$"; })
670 (plain "default-column-width" [(leaf "proportion" 1.)]) 685 (plain "default-column-width" [(sleaf "proportion" 1.)])
671 (leaf "open-fullscreen" true)
672 (leaf "open-on-workspace" "bmr")
673 (leaf "open-focused" false)
674 ]) 686 ])
675 (plain "window-rule" [ 687 (plain "window-rule" [
676 (map (leaf "match") [ 688 (sleaf "match" { app-id = "^pdfpc$"; title = "^.*presentation.*$"; })
689 (plain "default-column-width" [(sleaf "proportion" 1.)])
690 (sleaf "open-fullscreen" true)
691 (sleaf "open-on-workspace" "bmr")
692 (sleaf "open-focused" false)
693 ])
694 (plain "window-rule" (normalize-nodes [
695 (map (sleaf "match") [
677 { app-id = "^Gimp-"; title = "^Quit GIMP$"; } 696 { app-id = "^Gimp-"; title = "^Quit GIMP$"; }
678 { app-id = "^org\\.kde\\.polkit-kde-authentication-agent-1$"; } 697 { app-id = "^org\\.kde\\.polkit-kde-authentication-agent-1$"; }
679 { app-id = "^xdg-desktop-portal-gtk$"; } 698 { app-id = "^xdg-desktop-portal-gtk$"; }
680 ]) 699 ])
681 (leaf "open-floating" true) 700 (sleaf "open-floating" true)
682 ]) 701 ]))
683 (plain "window-rule" [ 702 (plain "window-rule" [
684 (leaf "match" { app-id = "^org\\.pwmt\\.zathura$"; }) 703 (sleaf "match" { app-id = "^org\\.pwmt\\.zathura$"; })
685 (leaf "match" { app-id = "^evince$"; }) 704 (sleaf "match" { app-id = "^evince$"; })
686 (leaf "match" { app-id = "^org\\.gnome\\.Papers$"; }) 705 (sleaf "match" { app-id = "^org\\.gnome\\.Papers$"; })
687 (leaf "default-column-display" "tabbed") 706 (sleaf "default-column-display" "tabbed")
688 ]) 707 ])
689 708
690 (plain "layer-rule" [ 709 (plain "layer-rule" [
691 (leaf "match" { namespace = "^notifications$"; }) 710 (sleaf "match" { namespace = "^notifications$"; })
692 (leaf "match" { namespace = "^waybar$"; }) 711 (sleaf "match" { namespace = "^bar$"; })
693 (leaf "match" { namespace = "^launcher$"; }) 712 (sleaf "match" { namespace = "^launcher$"; })
694 (leaf "block-out-from" "screencast") 713 (sleaf "block-out-from" "screencast")
695 ]) 714 ])
696 715
697 (plain "binds" 716 (plain "binds"
698 (let 717 (let
699 bind = name: cfg: node name (opt-props { 718 bind = name: cfg: node name [(lib.removeAttrs cfg ["action"])] (lib.mapAttrsToList leaf (lib.removeAttrs cfg.action ["__functor"]));
700 cooldown-ms = cfg.cooldown-ms or null;
701 }
702 // (lib.optionalAttrs (!(cfg.repeat or true)) {
703 repeat = false;
704 })
705 // (lib.optionalAttrs (cfg.allow-when-locked or false) {
706 allow-when-locked = true;
707 })) (lib.mapAttrsToList leaf (lib.removeAttrs cfg.action ["__functor"]));
708 in 719 in
709 [ 720 normalize-nodes [
710 (lib.mapAttrsToList bind (with config.lib.niri.actions; { 721 (lib.mapAttrsToList bind (with config.lib.niri.actions; {
711 "Mod+Slash".action = show-hotkey-overlay; 722 "Mod+Slash".action = show-hotkey-overlay;
712 723
713 "Mod+Return".action = spawn terminal; 724 "Mod+Return".action = spawn terminal;
714 "Mod+Shift+Return".action = spawn terminal (lib.getExe config.programs.nushell.package); 725 "Mod+Shift+Return".action =
726 let
727 nushellKitty = pkgs.symlinkJoin {
728 name = "nushell-kitty";
729 paths = [ config.programs.kitty.package ];
730 buildInputs = [ pkgs.makeWrapper ];
731 postBuild = ''
732 wrapProgram $out/bin/kitty \
733 --add-flags "--config ${pkgs.writeText "kitty.conf" ''
734 include $HOME/${config.xdg.configFile."kitty/kitty.conf".target}
735 shell ${lib.getExe config.programs.nushell.package}
736 ''}"
737 '';
738 };
739 in spawn (lib.getExe' nushellKitty "kitty");
715 "Mod+Q".action = close-window; 740 "Mod+Q".action = close-window;
716 "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package); 741 "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package);
717 "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path"; 742 "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path";
@@ -747,12 +772,12 @@ in {
747 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) 772 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)
748 $FOUND || echo 773 $FOUND || echo
749 } 774 }
750 FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> ") || exit $? 775 FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> " --width=60) || exit $?
751 if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then 776 if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then
752 QALC_RES="$FUZZEL_RES" 777 QALC_RES="$FUZZEL_RES"
753 QALC_RET=0 778 QALC_RET=0
754 else 779 else
755 QALC_RES=$(qalc "$FUZZEL_RES" 2>&1) 780 QALC_RES=$(qalc -set "autocalc off" "$FUZZEL_RES" 2>&1)
756 QALC_RET=$? 781 QALC_RET=$?
757 fi 782 fi
758 [[ -n "$QALC_RES" ]] || exit 1 783 [[ -n "$QALC_RES" ]] || exit 1
@@ -772,11 +797,26 @@ in {
772 notify-send "$QALC_RES" 797 notify-send "$QALC_RES"
773 ''; 798 '';
774 })); 799 }));
800 "Mod+Shift+U".action =
801 let
802 qalcKitty = pkgs.symlinkJoin {
803 name = "qalc-kitty";
804 paths = [ config.programs.kitty.package ];
805 buildInputs = [ pkgs.makeWrapper ];
806 postBuild = ''
807 wrapProgram $out/bin/kitty \
808 --add-flags "--config ${pkgs.writeText "kitty.conf" ''
809 include $HOME/${config.xdg.configFile."kitty/kitty.conf".target}
810 shell ${lib.getExe pkgs.libqalculate}
811 ''}"
812 '';
813 };
814 in spawn (lib.getExe' qalcKitty "kitty");
775 "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication { 815 "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication {
776 name = "emoji-fuzzel"; 816 name = "emoji-fuzzel";
777 runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ]; 817 runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ];
778 text = '' 818 text = ''
779 FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " <"$HOME"/.local/share/emoji-data/list.txt) || exit $? 819 FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " --cache "$HOME"/.cache/fuzzel-emoji --width=60 <"$HOME"/.local/share/emoji-data/list.txt) || exit $?
780 [[ -n "$FUZZEL_RES" ]] || exit 1 820 [[ -n "$FUZZEL_RES" ]] || exit 1
781 wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste 821 wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste
782 ''; 822 '';
@@ -837,7 +877,7 @@ in {
837 "Mod+Shift+Asterisk".action = kdl.magic-leaf "move-column-to-workspace" "vid"; 877 "Mod+Shift+Asterisk".action = kdl.magic-leaf "move-column-to-workspace" "vid";
838 878
839 "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; 879 "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}'';
840 "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}''; 880 "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}'';
841 881
842 "Mod+M".action = consume-or-expel-window-left; 882 "Mod+M".action = consume-or-expel-window-left;
843 "Mod+W".action = consume-or-expel-window-right; 883 "Mod+W".action = consume-or-expel-window-right;
@@ -860,62 +900,77 @@ in {
860 "Mod+Right".action = set-column-width "+10%"; 900 "Mod+Right".action = set-column-width "+10%";
861 901
862 "Mod+Shift+Z" = { 902 "Mod+Shift+Z" = {
863 action = spawn (lib.getExe niri) "msg" "action" "power-off-monitors"; 903 action = power-off-monitors;
864 allow-when-locked = true; 904 allow-when-locked = true;
865 }; 905 };
866 "Mod+Shift+L".action = spawn loginctl "lock-session";
867 "Mod+Shift+E".action = quit; 906 "Mod+Shift+E".action = quit;
868 "Mod+Shift+Minus" = { 907
869 action = spawn systemctl "suspend"; 908 # "Mod+Semicolon".action = spawn makoctl "dismiss" "--group";
909 # "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all";
910 # "Mod+Period".action = spawn makoctl "menu" "--" (lib.getExe config.programs.fuzzel.package) "--dmenu";
911 # "Mod+Comma".action = spawn makoctl "restore";
912
913 "Mod+Control+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"FocusWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}";
914 "Mod+Control+Shift+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"MoveColumnToWorkspace\":{\"reference\":{\"Id\": $workspace_id}, \"focus\": true}}}";
915
916 "Mod+X".action = set-dynamic-cast-window;
917 "Mod+Shift+X".action = set-dynamic-cast-monitor;
918 "Mod+Control+Shift+X".action = clear-dynamic-cast-target;
919
920 "Mod+D".action = with-urgent-window-action "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
921 "Mod+Shift+D".action = with-focused-window-action "{\"Action\":{\"UnsetUrgent\":{\"id\": .id}}}";
922
923 "Mod+K".action = spawn (lib.getExe' pkgs.worktime "worktime-ui");
924 "Mod+Shift+K".action = spawn (lib.getExe' pkgs.worktime "worktime-stop");
925 }))
926 (lib.mapAttrsToList (name: cfg: node name [(lib.removeAttrs cfg ["action"])] [cfg.action]) (let
927 shell = obj: leaf "send-unix" [
928 { path = ''''${XDG_RUNTIME_DIR}/shell.sock''; }
929 (builtins.toJSON obj + "\n")
930 ];
931 in {
932 "XF86AudioRaiseVolume" = {
870 allow-when-locked = true; 933 allow-when-locked = true;
934 action = shell { Volume.volume = "up"; };
871 }; 935 };
872 "Mod+Shift+Control+Minus" = { 936 "XF86AudioLowerVolume" = {
873 action = spawn systemctl "hibernate";
874 allow-when-locked = true; 937 allow-when-locked = true;
938 action = shell { Volume.volume = "down"; };
875 }; 939 };
876 "Mod+Shift+P" = { 940 "XF86AudioMute" = {
877 action = spawn (lib.getExe pkgs.playerctl) "-a" "pause";
878 allow-when-locked = true; 941 allow-when-locked = true;
942 action = shell { Volume.muted = "toggle"; };
879 }; 943 };
880 944 "XF86AudioMicMute" = {
881 "XF86MonBrightnessUp" = {
882 action = spawn swayosd-client "--brightness" "raise";
883 allow-when-locked = true; 945 allow-when-locked = true;
946 action = shell { Volume."mic-muted" = "toggle"; };
884 }; 947 };
885 "XF86MonBrightnessDown" = { 948 "XF86MonBrightnessUp" = {
886 action = spawn swayosd-client "--brightness" "lower"; 949 action = shell { Brightness = "up"; };
887 allow-when-locked = true; 950 allow-when-locked = true;
888 }; 951 };
889 "XF86AudioRaiseVolume" = { 952 "XF86MonBrightnessDown" = {
890 action = spawn swayosd-client "--output-volume" "raise"; 953 action = shell { Brightness = "down"; };
891 allow-when-locked = true; 954 allow-when-locked = true;
892 }; 955 };
893 "XF86AudioLowerVolume" = { 956 "Mod+Shift+L".action = shell { LockSession = {}; };
894 action = spawn swayosd-client "--output-volume" "lower"; 957 "Mod+Shift+Minus" = {
958 action = shell { Suspend = {}; };
895 allow-when-locked = true; 959 allow-when-locked = true;
896 }; 960 };
897 "XF86AudioMute" = { 961 "Mod+Shift+Control+Minus" = {
898 action = spawn swayosd-client "--output-volume" "mute-toggle"; 962 action = shell { Hibernate = {}; };
899 allow-when-locked = true; 963 allow-when-locked = true;
900 }; 964 };
901 "XF86AudioMicMute" = { 965 "Mod+Shift+P" = {
902 action = spawn swayosd-client "--input-volume" "mute-toggle"; 966 action = shell { Mpris = { PauseAll = {}; }; };
903 allow-when-locked = true; 967 allow-when-locked = true;
904 }; 968 };
905 969 "Mod+Semicolon".action = shell { Notifications = { DismissGroup = {}; }; };
906 "Mod+Semicolon".action = spawn makoctl "dismiss" "--group"; 970 "Mod+Shift+Semicolon".action = shell { Notifications = { DismissAll = {}; }; };
907 "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all";
908 "Mod+Period".action = spawn makoctl "menu" "--" (lib.getExe config.programs.fuzzel.package) "--dmenu";
909 "Mod+Comma".action = spawn makoctl "restore";
910
911 "Mod+Control+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"FocusWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}";
912 "Mod+Control+Shift+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"MoveColumnToWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}";
913
914 "Mod+X".action = set-dynamic-cast-window;
915 "Mod+Shift+X".action = set-dynamic-cast-monitor;
916 "Mod+Control+Shift+X".action = clear-dynamic-cast-target;
917 })) 971 }))
918 (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) 972 (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)
973 (map ({ name, moveKey, ...}: if moveKey != null then bind moveKey { action = kdl.magic-leaf "move-column-to-workspace" name; } else null) cfg.scratchspaces)
919 ] 974 ]
920 )) 975 ))
921 ]; 976 ];