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.nix1106
1 files changed, 655 insertions, 451 deletions
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix
index bf124211..abcb80fc 100644
--- a/accounts/gkleen@sif/niri/default.nix
+++ b/accounts/gkleen@sif/niri/default.nix
@@ -1,12 +1,15 @@
1{ config, hostConfig, pkgs, lib, ... }: 1{ config, hostConfig, pkgs, lib, flakeInputs, ... }:
2let 2let
3 niri = config.programs.niri.package; 3 cfg = config.programs.niri;
4
5 kdl = flakeInputs.niri-flake.lib.kdl;
6
7 niri = cfg.package;
4 terminal = lib.getExe config.programs.kitty.package; 8 terminal = lib.getExe config.programs.kitty.package;
5 lightctl = lib.getExe' config.services.avizo.package "lightctl";
6 volumectl = lib.getExe' config.services.avizo.package "volumectl";
7 makoctl = lib.getExe' config.services.mako.package "makoctl"; 9 makoctl = lib.getExe' config.services.mako.package "makoctl";
8 loginctl = lib.getExe' hostConfig.systemd.package "loginctl"; 10 loginctl = lib.getExe' hostConfig.systemd.package "loginctl";
9 systemctl = lib.getExe' hostConfig.systemd.package "systemctl"; 11 systemctl = lib.getExe' hostConfig.systemd.package "systemctl";
12 swayosd-client = lib.getExe' config.services.swayosd.package "swayosd-client";
10 13
11 focus_or_spawn = pkgs.writeShellApplication { 14 focus_or_spawn = pkgs.writeShellApplication {
12 name = "focus-or-spawn"; 15 name = "focus-or-spawn";
@@ -29,7 +32,11 @@ let
29 32
30 while IFS=$'\n' read -r window_json; do 33 while IFS=$'\n' read -r window_json; do
31 if [[ -n $(jq -c "$window_select" <<<"$window_json") ]]; then 34 if [[ -n $(jq -c "$window_select" <<<"$window_json") ]]; then
32 niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")" 35 if jq -e '.is_focused' <<<"$window_json" >/dev/null; then
36 niri msg action focus-workspace-previous
37 else
38 niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")"
39 fi
33 exit 0 40 exit 0
34 fi 41 fi
35 done < <(niri msg -j windows | jq -c '.[]') 42 done < <(niri msg -j windows | jq -c '.[]')
@@ -75,7 +82,7 @@ let
75 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" 82 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET"
76 ''; 83 '';
77 }; 84 };
78 with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^pwctl|kpxc|bmgr|edit|term$"; 85 with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^${lib.concatMapStringsSep "|" ({ name, ...}: name) cfg.scratchspaces}$";
79 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}}}}'';
80 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}}}}'';
81 88
@@ -90,7 +97,8 @@ let
90 active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" 97 active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")"
91 active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")" 98 active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")"
92 99
93 workspace_json="$(jq -c --arg active_output "$active_output" 'map(select(.output == $active_output and .name == null)) | sort_by(.idx) | .[0]' <<<"$workspaces_json")" 100 history_json="$(socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/niri-workspace-history.sock)"
101 workspace_json="$(jq -c --arg active_output "$active_output" --argjson history "$history_json" 'map(select(.output == $active_output and .name == null)) | map({"value": ., "history_idx": ((. as $workspace | ($history[$active_output] | index($workspace | .id))) as $active_idx | if $active_idx then $active_idx else ($history[$active_output] | length) + 1 end)}) | sort_by(.history_idx, .value.idx) | map(.value) | .[0]' <<<"$workspaces_json")"
94 [[ -n $workspace_json && $workspace_json != null ]] || exit 0 102 [[ -n $workspace_json && $workspace_json != null ]] || exit 0
95 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" 103 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET"
96 ''; 104 '';
@@ -122,8 +130,57 @@ in {
122 imports = [ 130 imports = [
123 ./waybar.nix 131 ./waybar.nix
124 ./mako.nix 132 ./mako.nix
133 ./swayosd.nix
125 ]; 134 ];
126 135
136 options = {
137 programs.niri.scratchspaces = lib.mkOption {
138 type = lib.types.listOf (lib.types.submodule ({ config, ... }: {
139 options = {
140 name = lib.mkOption {
141 type = lib.types.str;
142 };
143 match = lib.mkOption {
144 type = lib.types.listOf (lib.types.attrsOf kdl.types.kdl-args);
145 default = [];
146 };
147 exclude = lib.mkOption {
148 type = lib.types.listOf (lib.types.attrsOf kdl.types.kdl-args);
149 default = [];
150 };
151 windowRuleExtra = lib.mkOption {
152 type = kdl.types.kdl-nodes;
153 default = [];
154 };
155 key = lib.mkOption {
156 type = lib.types.nullOr lib.types.str;
157 default = null;
158 };
159 spawn = lib.mkOption {
160 type = lib.types.nullOr (lib.types.listOf lib.types.str);
161 default = null;
162 };
163 app-id = lib.mkOption {
164 type = lib.types.nullOr lib.types.str;
165 default = null;
166 };
167 selector = lib.mkOption {
168 type = lib.types.nullOr lib.types.str;
169 default = null;
170 };
171 };
172
173 config = lib.mkMerge [
174 (lib.mkIf (config.app-id != null) {
175 match = lib.mkDefault [ { app-id = "^${lib.escapeRegex config.app-id}$"; } ];
176 selector = lib.mkDefault "select(.app_id == \"${config.app-id}\")";
177 })
178 ];
179 }));
180 default = [];
181 };
182 };
183
127 config = { 184 config = {
128 systemd.user.services.xwayland-satellite = { 185 systemd.user.services.xwayland-satellite = {
129 Unit = { 186 Unit = {
@@ -156,455 +213,602 @@ in {
156 ]; 213 ];
157 }; 214 };
158 215
159 programs.niri.settings = { 216 systemd.user.sockets.niri-workspace-history = {
160 prefer-no-csd = true; 217 Socket = {
161 screenshot-path = "${config.home.homeDirectory}/screenshots"; 218 ListenStream = "%t/niri-workspace-history.sock";
162 219 SocketMode = "0600";
163 hotkey-overlay.skip-at-startup = true;
164
165 input = {
166 keyboard.xkb = {
167 layout = "us,us";
168 variant = "dvp,";
169 options = "compose:caps,grp:win_space_toggle";
170 };
171
172 workspace-auto-back-and-forth = true;
173 # focus-follows-mouse.enable = true;
174 warp-mouse-to-focus = true;
175 }; 220 };
176 221 };
177 outputs = { 222 systemd.user.services.niri-workspace-history = {
178 "eDP-1" = { 223 Unit = {
179 scale = 1.5; 224 BindsTo = [ "niri.service" ];
180 position = { x = 0; y = 0; }; 225 After = [ "niri.service" ];
181 };
182 "Ancor Communications Inc ASUS PB287Q 0x0000DD9B" = {
183 scale = 1.5;
184 position = { x = 2560; y = 0; };
185 };
186 "HP Inc. HP 727pu CN4417143K" = {
187 mode = { width = 2560; height = 1440; refresh = 119.998; };
188 scale = 1;
189 position = { x = 2560; y = 0; };
190 variable-refresh-rate = "on-demand";
191 };
192 }; 226 };
193 227 Install = {
194 environment = { 228 WantedBy = [ "niri.service" ];
195 NIXOS_OZONE_WL = "1";
196 QT_QPA_PLATFORM = "wayland";
197 QT_WAYLAND_DISABLE_WINDOWDECORATION = "1";
198 GDK_BACKEND = "wayland";
199 SDL_VIDEODRIVER = "wayland";
200 DISPLAY = ":0";
201 }; 229 };
230 Service = {
231 Type = "simple";
232 Sockets = [ "niri-workspace-history.socket" ];
233 ExecStart = pkgs.writers.writePython3 "niri-workspace-history" {} ''
234 import os
235 import socket
236 import json
237 import sys
238 from collections import defaultdict
239 from threading import Thread, Lock
240 from socketserver import StreamRequestHandler, ThreadingTCPServer
241 from contextlib import contextmanager
242 from io import TextIOWrapper
243
244
245 @contextmanager
246 def detaching(thing):
247 try:
248 yield thing
249 finally:
250 thing.detach()
251
252
253 workspace_history = defaultdict(list)
254 history_lock = Lock()
255
256
257 def monitor_niri():
258 workspaces = list()
259
260 def focus_workspace(output, workspace):
261 global workspace_history, history_lock
262
263 with history_lock:
264 workspace_history[output] = [workspace] + [ws for ws in workspace_history[output] if ws != workspace] # noqa: E501
265 print(json.dumps(workspace_history), file=sys.stderr)
266
267 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
268 sock.connect(os.environ["NIRI_SOCKET"])
269 sock.send(b"\"EventStream\"\n")
270 for line in sock.makefile(buffering=1, encoding='utf-8'):
271 if line_json := json.loads(line):
272 if "WorkspacesChanged" in line_json:
273 workspaces = line_json["WorkspacesChanged"]["workspaces"]
274 for ws in workspaces:
275 if ws["is_focused"]:
276 focus_workspace(ws["output"], ws["id"])
277 if "WorkspaceActivated" in line_json:
278 for ws in workspaces:
279 if ws["id"] != line_json["WorkspaceActivated"]["id"]:
280 continue
281 focus_workspace(ws["output"], ws["id"])
282 break
283
284
285 class RequestHandler(StreamRequestHandler):
286 def handle(self):
287 global workspace_history, history_lock
288
289 with detaching(TextIOWrapper(self.wfile, encoding='utf-8', write_through=True)) as out: # noqa: E501
290 with history_lock:
291 json.dump(workspace_history, out)
292
293
294 class Server(ThreadingTCPServer):
295 def __init__(self):
296 ThreadingTCPServer.__init__(self, ("", 8000), RequestHandler, bind_and_activate=False) # noqa: E501
297 self.socket = socket.fromfd(3, self.address_family, self.socket_type)
298
299
300 def run_server():
301 with Server() as server:
302 server.serve_forever()
303
304
305 niri = Thread(target=monitor_niri)
306 niri.daemon = True
307 niri.start()
308
309 server_thread = Thread(target=run_server)
310 server_thread.daemon = True
311 server_thread.start()
312
313 while True:
314 server_thread.join(timeout=0.5)
315 niri.join(timeout=0.5)
316
317 if not (niri.is_alive() and server_thread.is_alive()):
318 break
319 '';
320 };
321 };
202 322
203 debug.render-drm-device = "/dev/dri/by-path/pci-0000:00:02.0-render"; 323 programs.niri.scratchspaces = [
204 324 { name = "pwctl";
205 layout = { 325 key = "Mod+Control+A";
206 gaps = 8; 326 spawn = ["pwvucontrol"];
207 struts = { left = 0; right = 0; top = 0; bottom = 0; }; 327 app-id = "com.saivert.pwvucontrol";
208 focus-ring = { 328 }
209 width = 2; 329 { name = "kpxc";
210 active.gradient = { 330 exclude = [
211 from = "hsla(195 100% 60% 0.75)"; 331 { title = "^Unlock Database.*"; }
212 to = "hsla(155 100% 50% 0.75)"; 332 { title = "^Access Request.*"; }
213 angle = 29; 333 { title = ".*Passkey credentials$"; }
214 relative-to = "workspace-view";
215 };
216 inactive.gradient = {
217 from = "hsla(0 0% 42% 0.66)";
218 to = "hsla(0 0% 35% 0.66)";
219 angle = 29;
220 relative-to = "workspace-view";
221 };
222 };
223
224 preset-column-widths = [
225 { proportion = 1. / 4.; }
226 { proportion = 1. / 3.; }
227 { proportion = 1. / 2.; }
228 { proportion = 2. / 3.; }
229 { proportion = 3. / 4.; }
230 ]; 334 ];
231 default-column-width.proportion = 1. / 2.; 335 windowRuleExtra = [
232 preset-window-heights = [ 336 (kdl.leaf "open-focused" false)
233 { proportion = 1. / 3.; } 337 ];
234 { proportion = 1. / 2.; } 338 key = "Mod+Control+P";
235 { proportion = 2. / 3.; } 339 app-id = "org.keepassxc.KeePassXC";
236 { proportion = 1.; } 340 spawn = [ "keepassxc" ];
341 }
342 { name = "bmgr";
343 key = "Mod+Control+B";
344 app-id = ".blueman-manager-wrapped";
345 spawn = [ "blueman-manager" ];
346 }
347 { name = "term";
348 key = "Mod+Control+Return";
349 app-id = "kitty-scratch";
350 spawn = [ "kitty" "--app-id" "kitty-scratch" ];
351 }
352 { name = "edit";
353 match = [ { title = "^scratch$"; app-id = "^emacs$"; } ];
354 key = "Mod+Control+E";
355 selector = "select(.app_id == \"emacs\" and .title == \"scratch\")";
356 spawn = [ "emacsclient" "-c" "--frame-parameters=(quote (name . \"scratch\"))" ];
357 }
358 { name = "eff";
359 key = "Mod+Control+O";
360 app-id = "com.github.wwmm.easyeffects";
361 spawn = [ "easyeffects" ];
362 }
363 ];
364 programs.niri.config =
365 let
366 inherit (kdl) node plain leaf flag;
367 optional-node = cond: v:
368 if cond
369 then v
370 else null;
371 opt-props = lib.filterAttrs (lib.const (value: value != null));
372 in
373 [ (flag "prefer-no-csd")
374
375 (plain "hotkey-overlay" [
376 (flag "skip-at-startup")
377 ])
378
379 (plain "input" [
380 (plain "keyboard" [
381 (leaf "repeat-delay" 300)
382 (leaf "repeat-rate" 50)
383
384 (plain "xkb" [
385 (leaf "layout" "us,us")
386 (leaf "variant" "dvp,")
387 (leaf "options" "compose:caps,grp:win_space_toggle")
388 ])
389 ])
390
391 (flag "workspace-auto-back-and-forth")
392 # (leaf "focus-follows-mouse" {})
393 # (flag "warp-mouse-to-focus")
394
395 (plain "touchpad" [ (flag "off") ])
396 (plain "trackball" [
397 (leaf "scroll-method" "on-button-down")
398 (leaf "scroll-button" 278)
399 ])
400 ])
401
402 (plain "environment" (lib.mapAttrsToList leaf {
403 NIXOS_OZONE_WL = "1";
404 QT_QPA_PLATFORM = "wayland";
405 QT_WAYLAND_DISABLE_WINDOWDECORATION = "1";
406 GDK_BACKEND = "wayland";
407 SDL_VIDEODRIVER = "wayland";
408 DISPLAY = ":0";
409 }))
410
411 (node "output" "eDP-1" [
412 (leaf "scale" 1.5)
413 (leaf "position" { x = 0; y = 0; })
414 ])
415 (node "output" "Ancor Communications Inc ASUS PB287Q 0x0000DD9B" [
416 (leaf "scale" 1.5)
417 (leaf "position" { x = 2560; y = 0; })
418 ])
419 (node "output" "HP Inc. HP 727pu CN4417143K" [
420 (leaf "mode" "2560x1440@120") # 119.998
421 (leaf "scale" 1)
422 (leaf "position" { x = 2560; y = 0; })
423 (flag "variable-refresh-rate")
424 ])
425
426 (plain "debug" [
427 (leaf "render-drm-device" "/dev/dri/by-path/pci-0000:00:02.0-render")
428 ])
429
430 (plain "animations" [
431 (leaf "slowdown" 0.5)
432 (plain "workspace-switch" [(flag "off")])
433 ])
434
435 (plain "layout" [
436 (leaf "gaps" 8)
437 (plain "struts" [
438 (leaf "left" 0)
439 (leaf "right" 0)
440 (leaf "top" 0)
441 (leaf "bottom" 0)
442 ])
443 (plain "focus-ring" [
444 (leaf "width" 2)
445 (leaf "active-gradient" {
446 from = "hsla(195 100% 45% 1)";
447 to = "hsla(155 100% 37.5% 1)";
448 angle = 29;
449 relative-to = "workspace-view";
450 })
451 (leaf "inactive-gradient" {
452 from = "hsla(0 0% 27.7% 1)";
453 to = "hsla(0 0% 23% 1)";
454 angle = 29;
455 relative-to = "workspace-view";
456 })
457 ])
458
459 (plain "preset-column-widths" (map (prop: leaf "proportion" prop) [
460 (1. / 4.) (1. / 3.) (1. / 2.) (2. / 3.) (3. / 4.)
461 ]))
462 (plain "default-column-width" [ (leaf "proportion" (1. / 2.)) ])
463 (plain "preset-window-heights" (map (prop: leaf "proportion" prop) [
464 (1. / 3.) (1. / 2.) (2. / 3.) (1.)
465 ]))
466
467 (flag "always-center-single-column")
468
469 (plain "tab-indicator" [
470 (leaf "gap" (-6))
471 (leaf "width" 6)
472 (leaf "length" { total-proportion = 1.; })
473 (leaf "active-gradient" {
474 from = "hsla(195 100% 60% 0.75)";
475 to = "hsla(155 100% 50% 0.75)";
476 angle = 29;
477 relative-to = "workspace-view";
478 })
479 (leaf "inactive-gradient" {
480 from = "hsla(0 0% 42% 0.66)";
481 to = "hsla(0 0% 35% 0.66)";
482 angle = 29;
483 relative-to = "workspace-view";
484 })
485 ])
486 ])
487
488 (plain "cursor" [
489 (flag "hide-when-typing")
490 ])
491
492 (map (name:
493 (node "workspace" name [
494 (leaf "open-on-output" "eDP-1")
495 ])
496 ) (map ({name, ...}: name) cfg.scratchspaces))
497 (map (name:
498 (leaf "workspace" name)
499 ) ["comm" "web" "vid" "bmr"])
500
501 (plain "window-rule" [
502 (leaf "match" { is-floating = true; })
503 (leaf "geometry-corner-radius" 8)
504 (leaf "clip-to-geometry" true)
505 ])
506
507 (plain "window-rule" [
508 (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; })
509 (leaf "block-out-from" "screencast")
510 ])
511 (plain "window-rule" [
512 (map (title:
513 (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; })
514 ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$"])
515 (leaf "open-focused" true)
516 (leaf "open-floating" true)
517 ])
518
519 (map ({ name, match, exclude, windowRuleExtra, ... }:
520 (optional-node (match != []) (plain "window-rule" [
521 (map (leaf "match") match)
522 (map (leaf "exclude") exclude)
523 (leaf "open-on-workspace" name)
524 (leaf "open-maximized" true)
525 windowRuleExtra
526 ]))
527 ) cfg.scratchspaces)
528
529 (plain "window-rule" [
530 (leaf "match" { app-id = "^emacs$"; })
531 (leaf "match" { app-id = "^firefox$"; })
532 (plain "default-column-width" [(leaf "proportion" (2. / 3.))])
533 ])
534 (plain "window-rule" [
535 (leaf "match" { app-id = "^kitty$"; })
536 (leaf "match" { app-id = "^kitty-play$"; })
537 (plain "default-column-width" [(leaf "proportion" (1. / 3.))])
538 ])
539
540 (plain "window-rule" [
541 (leaf "match" { app-id = "^thunderbird$"; })
542 (leaf "match" { app-id = "^Element$"; })
543 (leaf "match" { app-id = "^Rainbow$"; })
544 (leaf "open-on-workspace" "comm")
545 ])
546 (plain "window-rule" [
547 (leaf "match" { app-id = "^firefox$"; })
548 (leaf "open-on-workspace" "web")
549 (leaf "open-maximized" true)
550 ])
551 (plain "window-rule" [
552 (leaf "match" { app-id = "^mpv$"; })
553 (leaf "open-on-workspace" "vid")
554 (plain "default-column-width" [(leaf "proportion" 1.)])
555 ])
556 (plain "window-rule" [
557 (leaf "match" { app-id = "^kitty-play$"; })
558 (leaf "open-on-workspace" "vid")
559 (leaf "open-focused" false)
560 ])
561 (plain "window-rule" [
562 (leaf "match" { app-id = "^pdfpc$"; })
563 (plain "default-column-width" [(leaf "proportion" 1.)])
564 ])
565 (plain "window-rule" [
566 (leaf "match" { app-id = "^pdfpc$"; title = "^pdfpc - presentation$"; })
567 (plain "default-column-width" [(leaf "proportion" 1.)])
568 (leaf "open-fullscreen" true)
569 (leaf "open-on-workspace" "bmr")
570 (leaf "open-focused" false)
571 ])
572 (plain "window-rule" [
573 (map (leaf "match") [
574 { app-id = "^Gimp-"; title = "^Quit GIMP$"; }
575 { app-id = "^org\\.kde\\.polkit-kde-authentication-agent-1$"; }
576 { app-id = "^xdg-desktop-portal-gtk$"; }
577 ])
578 (leaf "open-floating" true)
579 ])
580
581 (plain "layer-rule" [
582 (leaf "match" { namespace = "^notifications$"; })
583 (leaf "match" { namespace = "^waybar$"; })
584 (leaf "match" { namespace = "^launcher$"; })
585 (leaf "block-out-from" "screencast")
586 ])
587
588 (plain "binds"
589 (let
590 bind = name: cfg: node name (opt-props {
591 cooldown-ms = cfg.cooldown-ms or null;
592 }
593 // (lib.optionalAttrs (!(cfg.repeat or true)) {
594 repeat = false;
595 })
596 // (lib.optionalAttrs (cfg.allow-when-locked or false) {
597 allow-when-locked = true;
598 })) (lib.mapAttrsToList leaf (lib.removeAttrs cfg.action ["__functor"]));
599 in
600 [
601 (lib.mapAttrsToList bind (with config.lib.niri.actions; {
602 "Mod+Slash".action = show-hotkey-overlay;
603
604 "Mod+Return".action = spawn terminal;
605 "Mod+Q".action = close-window;
606 "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package);
607 "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path";
608
609 "Mod+Alt+E".action = spawn (lib.getExe' config.services.emacs.package "emacsclient") "-c";
610 "Mod+Alt+Y".action = spawn (lib.getExe (pkgs.writeShellApplication {
611 name = "queue-yt-dlp";
612 runtimeInputs = with pkgs; [ wl-clipboard-rs socat ];
613 text = ''
614 socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/yt-dlp.sock <<<$'{ "urls": ["'"$(wl-paste)"$'"] }'
615 '';
616 }));
617 "Mod+Alt+L".action = spawn (lib.getExe (pkgs.writeShellApplication {
618 name = "queue-yt-dlp";
619 runtimeInputs = with pkgs; [ wl-clipboard-rs config.programs.kitty.package ];
620 text = ''
621 exec -- kitty --app-id kitty-play --directory "$HOME"/media mpv "$(wl-paste)"
622 '';
623 }));
624
625 "Mod+U".action = spawn (lib.getExe (pkgs.writeShellApplication {
626 name = "qalc-fuzzel";
627 runtimeInputs = with pkgs; [ wl-clipboard-rs libqalculate config.programs.fuzzel.package coreutils findutils libnotify gnugrep ];
628 text = ''
629 RESULTS_DIR="$HOME/.cache/qalc-fuzzel"
630 prev() {
631 FOUND=false
632 while IFS= read -r line; do
633 [[ -n "$line" ]] || continue
634 FOUND=true
635 echo "$line"
636 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)
637 $FOUND || echo
638 }
639 FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> ") || exit $?
640 if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then
641 QALC_RES="$FUZZEL_RES"
642 QALC_RET=0
643 else
644 QALC_RES=$(qalc "$FUZZEL_RES" 2>&1)
645 QALC_RET=$?
646 fi
647 [[ -n "$QALC_RES" ]] || exit 1
648 EXISTING=false
649 set +o pipefail
650 grep -Fxrl "$QALC_RES" "$RESULTS_DIR" | xargs -r touch
651 [[ ''${PIPESTATUS[0]} -eq 0 ]] && EXISTING=true
652 set -o pipefail
653 if [[ $QALC_RET -eq 0 ]] && ! $EXISTING; then
654 set +o pipefail
655 RES_FILE="$RESULTS_DIR"/$(date -uIs).$(tr -Cd 'a-zA-Z0-9' </dev/random | head -c 10)
656 set -o pipefail
657 cat >"$RES_FILE" <<<"$QALC_RES"
658 fi
659 [[ "$QALC_RES" =~ .*\ =\ (.*) ]] && QALC_RES="''${BASH_REMATCH[1]}"
660 [[ $QALC_RET -eq 0 ]] && wl-copy "$QALC_RES"
661 notify-send "$QALC_RES"
662 '';
663 }));
664 "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication {
665 name = "emoji-fuzzel";
666 runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ];
667 text = ''
668 FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " <"$HOME"/.local/share/emoji-data/list.txt) || exit $?
669 [[ -n "$FUZZEL_RES" ]] || exit 1
670 wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste
671 '';
672 }));
673 "Print".action = spawn (lib.getExe (pkgs.writeShellApplication {
674 name = "screenshot";
675 runtimeInputs = with pkgs; [ grim slurp wl-clipboard-rs coreutils ];
676 text = ''
677 grim -g "$(slurp -b 00000080 -c FFFFFFFF -s 00000000 -w 1)" - \
678 | tee "$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" \
679 | wl-copy --type image/png
680 '';
681 }));
682 "Shift+Print".action = spawn (lib.getExe (pkgs.writeShellApplication {
683 name = "screenshot";
684 runtimeInputs = with pkgs; [ grim niri gojq wl-clipboard-rs coreutils ];
685 text = ''
686 grim -o "$(niri msg -j workspaces | jq -r '.[] | select(.is_focused) | .output')" - \
687 | tee "$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" \
688 | wl-copy --type image/png
689 '';
690 }));
691 "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
692 "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
693
694 "Mod+H".action = focus-column-left;
695 "Mod+T".action = focus-window-down;
696 "Mod+N".action = focus-window-up;
697 "Mod+S".action = focus-column-right;
698
699 "Mod+Shift+H".action = move-column-left;
700 "Mod+Shift+T".action = move-window-down;
701 "Mod+Shift+N".action = move-window-up;
702 "Mod+Shift+S".action = move-column-right;
703
704 "Mod+Control+H".action = focus-monitor-left;
705 "Mod+Control+T".action = focus-monitor-down;
706 "Mod+Control+N".action = focus-monitor-up;
707 "Mod+Control+S".action = focus-monitor-right;
708
709 "Mod+Shift+Control+H".action = move-workspace-to-monitor-left;
710 "Mod+Shift+Control+T".action = move-workspace-to-monitor-down;
711 "Mod+Shift+Control+N".action = move-workspace-to-monitor-up;
712 "Mod+Shift+Control+S".action = move-workspace-to-monitor-right;
713
714 "Mod+G".action = focus-adjacent-workspace "down";
715 "Mod+C".action = focus-adjacent-workspace "up";
716
717 "Mod+Shift+G".action = move-column-to-adjacent-workspace "down";
718 "Mod+Shift+C".action = move-column-to-adjacent-workspace "up";
719
720 "Mod+Shift+Control+G".action = move-workspace-down;
721 "Mod+Shift+Control+C".action = move-workspace-up;
722
723 "Mod+ParenLeft".action = focus-workspace "comm";
724 "Mod+Shift+ParenLeft".action = move-column-to-workspace "comm";
725
726 "Mod+ParenRight".action = focus-workspace "web";
727 "Mod+Shift+ParenRight".action = move-column-to-workspace "web";
728
729 "Mod+BraceRight".action = focus-workspace "read";
730 "Mod+Shift+BraceRight".action = move-column-to-workspace "read";
731
732 "Mod+BraceLeft".action = focus-workspace "mon";
733 "Mod+Shift+BraceLeft".action = move-column-to-workspace "mon";
734
735 "Mod+Asterisk".action = focus-workspace "vid";
736 "Mod+Shift+Asterisk".action = move-column-to-workspace "vid";
737
738 "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}'';
739 "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}'';
740
741 "Mod+M".action = consume-or-expel-window-left;
742 "Mod+W".action = consume-or-expel-window-right;
743
744 "Mod+Shift+M".action = toggle-column-tabbed-display;
745
746 "Mod+R".action = switch-preset-column-width;
747 "Mod+Shift+R".action = switch-preset-window-height;
748 "Mod+F".action = center-column;
749 "Mod+Shift+F".action = maximize-column;
750 "Mod+Shift+Ctrl+F".action = fullscreen-window;
751
752 "Mod+V".action = switch-focus-between-floating-and-tiling;
753 "Mod+Shift+V".action = toggle-window-floating;
754
755 "Mod+Left".action = set-column-width "-10%";
756 "Mod+Down".action = set-window-height "-10%";
757 "Mod+Up".action = set-window-height "+10%";
758 "Mod+Right".action = set-column-width "+10%";
759
760 "Mod+Shift+Z" = {
761 action = spawn (lib.getExe niri) "msg" "action" "power-off-monitors";
762 allow-when-locked = true;
763 };
764 "Mod+Shift+L".action = spawn loginctl "lock-session";
765 "Mod+Shift+E".action = quit;
766 "Mod+Shift+Minus" = {
767 action = spawn systemctl "suspend";
768 allow-when-locked = true;
769 };
770 "Mod+Shift+Control+Minus" = {
771 action = spawn systemctl "hibernate";
772 allow-when-locked = true;
773 };
774 "Mod+Shift+P" = {
775 action = spawn (lib.getExe pkgs.playerctl) "-a" "pause";
776 allow-when-locked = true;
777 };
778
779 "XF86MonBrightnessUp" = {
780 action = spawn swayosd-client "--brightness" "raise";
781 allow-when-locked = true;
782 };
783 "XF86MonBrightnessDown" = {
784 action = spawn swayosd-client "--brightness" "lower";
785 allow-when-locked = true;
786 };
787 "XF86AudioRaiseVolume" = {
788 action = spawn swayosd-client "--output-volume" "raise";
789 allow-when-locked = true;
790 };
791 "XF86AudioLowerVolume" = {
792 action = spawn swayosd-client "--output-volume" "lower";
793 allow-when-locked = true;
794 };
795 "XF86AudioMute" = {
796 action = spawn swayosd-client "--output-volume" "mute-toggle";
797 allow-when-locked = true;
798 };
799 "XF86AudioMicMute" = {
800 action = spawn swayosd-client "--input-volume" "mute-toggle";
801 allow-when-locked = true;
802 };
803
804 "Mod+Semicolon".action = spawn makoctl "dismiss" "--group";
805 "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all";
806 "Mod+Period".action = spawn makoctl "menu" (lib.getExe config.programs.fuzzel.package) "--dmenu";
807 "Mod+Comma".action = spawn makoctl "restore";
808 }))
809 (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)
810 ]
811 ))
237 ]; 812 ];
238
239 always-center-single-column = true;
240 };
241
242 cursor.hide-when-typing = true;
243
244 input = {
245 touchpad.enable = false;
246 trackball = {
247 scroll-method = "on-button-down";
248 scroll-button = 278;
249 };
250 };
251
252 workspaces = {
253 "001" = { name = "pwctl"; open-on-output = "eDP-1"; };
254 "002" = { name = "kpxc"; open-on-output = "eDP-1"; };
255 "003" = { name = "bmgr"; open-on-output = "eDP-1"; };
256 "004" = { name = "term"; open-on-output = "eDP-1"; };
257 "005" = { name = "edit"; open-on-output = "eDP-1"; };
258 "101".name = "comm";
259 "102".name = "web";
260 # "104".name = "read";
261 # "105".name = "mon";
262 "110".name = "vid";
263 "120".name = "bmr";
264 };
265
266 window-rules = [
267 # {
268 # geometry-corner-radius =
269 # let
270 # allCorners = r: { bottom-left = r; bottom-right = r; top-left = r; top-right = r; };
271 # in allCorners 4.;
272 # clip-to-geometry = true;
273 # }
274 {
275 matches = [ { app-id = "^com\.saivert\.pwvucontrol$"; } ];
276 open-on-workspace = "pwctl";
277 open-maximized = true;
278 }
279 {
280 matches = [ { app-id = "^\.blueman-manager-wrapped$"; } ];
281 open-on-workspace = "bmgr";
282 open-maximized = true;
283 }
284 {
285 matches = [ { app-id = "^org\.keepassxc\.KeePassXC$"; } ];
286 block-out-from = "screencast";
287 }
288 {
289 matches = [ { app-id = "^org\.keepassxc\.KeePassXC$"; } ];
290 excludes = [
291 { title = "^Unlock Database.*"; }
292 { title = "^Access Request.*"; }
293 { title = ".*Passkey credentials$"; }
294 ];
295 open-on-workspace = "kpxc";
296 open-maximized = true;
297 open-focused = false;
298 }
299 {
300 matches = [
301 { app-id = "^org\.keepassxc\.KeePassXC$"; title = "^Unlock Database.*"; }
302 { app-id = "^org\.keepassxc\.KeePassXC$"; title = "^Access Request.*"; }
303 { app-id = "^org\.keepassxc\.KeePassXC$"; title = ".*Passkey credentials$"; }
304 ];
305 open-focused = true;
306 }
307 {
308 matches = [ { app-id = "^kitty-scratch$"; } ];
309 open-on-workspace = "term";
310 open-maximized = true;
311 }
312 {
313 matches = [ { title = "^scratch$"; app-id = "^emacs$"; } ];
314 open-on-workspace = "edit";
315 open-maximized = true;
316 }
317 {
318 matches = [
319 { app-id = "^emacs$"; }
320 { app-id = "^firefox$"; }
321 ];
322 default-column-width.proportion = 2. / 3.;
323 }
324 {
325 matches = [
326 { app-id = "^kitty$"; }
327 { app-id = "^kitty-play$"; }
328 ];
329 default-column-width.proportion = 1. / 3.;
330 }
331 {
332 matches = [
333 { app-id = "^thunderbird$"; }
334 { app-id = "^Element$"; }
335 ];
336 open-on-workspace = "comm";
337 }
338 {
339 matches = [ { app-id = "^firefox$"; } ];
340 open-on-workspace = "web";
341 open-maximized = true;
342 variable-refresh-rate = true;
343 }
344 # {
345 # matches = [
346 # { app-id = "^evince$"; }
347 # { app-id = "^imv$"; }
348 # { app-id = "^org\.pwmt\.zathura$"; }
349 # ];
350 # open-on-workspace = "read";
351 # }
352 {
353 matches = [ { app-id = "^mpv$"; } ];
354 open-on-workspace = "vid";
355 default-column-width.proportion = 1.;
356 variable-refresh-rate = true;
357 }
358 {
359 matches = [ { app-id = "^kitty-play$"; } ];
360 open-on-workspace = "vid";
361 open-focused = false;
362 }
363 # {
364 # matches = [
365 # { app-id = "^qemu$"; }
366 # { app-id = "^virt-manager$"; }
367 # ];
368 # open-on-workspace = "mon";
369 # }
370 {
371 matches = [ { app-id = "^pdfpc$"; } ];
372 default-column-width.proportion = 1.;
373 }
374 {
375 matches = [ { app-id = "^pdfpc$"; title = "^pdfpc - presentation"; } ];
376 open-on-workspace = "bmr";
377 open-fullscreen = true;
378 }
379 {
380 matches = [
381 { app-id = "^Gimp-"; title = "^Quit GIMP$"; }
382 { app-id = "^org\.kde\.polkit-kde-authentication-agent-1$"; }
383 ];
384 open-floating = true;
385 }
386 ];
387 layer-rules = [
388 { matches = [
389 { namespace = "^notifications$"; }
390 { namespace = "^waybar$"; }
391 ];
392 block-out-from = "screencast";
393 }
394 ];
395
396 binds = with config.lib.niri.actions; {
397 "Mod+Slash".action = show-hotkey-overlay;
398
399 "Mod+Return".action = spawn terminal;
400 "Mod+Q".action = close-window;
401 "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package);
402 "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path";
403
404 "Mod+Alt+E".action = spawn (lib.getExe' config.services.emacs.package "emacsclient") "-c";
405 "Mod+Alt+Y".action = spawn (lib.getExe (pkgs.writeShellApplication {
406 name = "queue-yt-dlp";
407 runtimeInputs = with pkgs; [ wl-clipboard-rs socat ];
408 text = ''
409 socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/yt-dlp.sock <<<$'{ "urls": ["'"$(wl-paste)"$'"] }'
410 '';
411 }));
412 "Mod+Alt+L".action = spawn (lib.getExe (pkgs.writeShellApplication {
413 name = "queue-yt-dlp";
414 runtimeInputs = with pkgs; [ wl-clipboard-rs config.programs.kitty.package ];
415 text = ''
416 exec -- kitty --app-id kitty-play --directory "$HOME"/media mpv "$(wl-paste)"
417 '';
418 }));
419
420 "Mod+U".action = spawn (lib.getExe (pkgs.writeShellApplication {
421 name = "qalc-fuzzel";
422 runtimeInputs = with pkgs; [ wl-clipboard-rs libqalculate config.programs.fuzzel.package coreutils findutils libnotify gnugrep ];
423 text = ''
424 RESULTS_DIR="$HOME/.cache/qalc-fuzzel"
425 prev() {
426 FOUND=false
427 while IFS= read -r line; do
428 [[ -n "$line" ]] || continue
429 FOUND=true
430 echo "$line"
431 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)
432 $FOUND || echo
433 }
434 FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> ") || exit $?
435 if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then
436 QALC_RES="$FUZZEL_RES"
437 QALC_RET=0
438 else
439 QALC_RES=$(qalc "$FUZZEL_RES" 2>&1)
440 QALC_RET=$?
441 fi
442 [[ -n "$QALC_RES" ]] || exit 1
443 EXISTING=false
444 set +o pipefail
445 grep -Fxrl "$QALC_RES" "$RESULTS_DIR" | xargs -r touch
446 [[ ''${PIPESTATUS[0]} -eq 0 ]] && EXISTING=true
447 set -o pipefail
448 if [[ $QALC_RET -eq 0 ]] && ! $EXISTING; then
449 set +o pipefail
450 RES_FILE="$RESULTS_DIR"/$(date -uIs).$(tr -Cd 'a-zA-Z0-9' </dev/random | head -c 10)
451 set -o pipefail
452 cat >"$RES_FILE" <<<"$QALC_RES"
453 fi
454 [[ "$QALC_RES" =~ .*\ =\ (.*) ]] && QALC_RES="''${BASH_REMATCH[1]}"
455 [[ $QALC_RET -eq 0 ]] && wl-copy "$QALC_RES"
456 notify-send "$QALC_RES"
457 '';
458 }));
459 "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication {
460 name = "emoji-fuzzel";
461 runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ];
462 text = ''
463 FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " <"$HOME"/.local/share/emoji-data/list.txt) || exit $?
464 [[ -n "$FUZZEL_RES" ]] || exit 1
465 wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste
466 '';
467 }));
468 "Print".action = spawn (lib.getExe (pkgs.writeShellApplication {
469 name = "screenshot";
470 runtimeInputs = with pkgs; [ grim slurp wl-clipboard-rs coreutils ];
471 text = ''
472 grim -g "$(slurp -b 00000080 -c FFFFFFFF -s 00000000 -w 1)" - \
473 | tee "$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" \
474 | wl-copy --type image/png
475 '';
476 }));
477 "Shift+Print".action = spawn (lib.getExe (pkgs.writeShellApplication {
478 name = "screenshot";
479 runtimeInputs = with pkgs; [ grim niri gojq wl-clipboard-rs coreutils ];
480 text = ''
481 grim -o "$(niri msg -j workspaces | jq -r '.[] | select(.is_focused) | .output')" - \
482 | tee "$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" \
483 | wl-copy --type image/png
484 '';
485 }));
486 "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
487 "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
488
489 "Mod+H".action = focus-column-left;
490 "Mod+T".action = focus-window-down;
491 "Mod+N".action = focus-window-up;
492 "Mod+S".action = focus-column-right;
493
494 "Mod+Shift+H".action = move-column-left;
495 "Mod+Shift+T".action = move-window-down;
496 "Mod+Shift+N".action = move-window-up;
497 "Mod+Shift+S".action = move-column-right;
498
499 "Mod+Control+H".action = focus-monitor-left;
500 "Mod+Control+T".action = focus-monitor-down;
501 "Mod+Control+N".action = focus-monitor-up;
502 "Mod+Control+S".action = focus-monitor-right;
503
504 "Mod+Shift+Control+H".action = move-workspace-to-monitor-left;
505 "Mod+Shift+Control+T".action = move-workspace-to-monitor-down;
506 "Mod+Shift+Control+N".action = move-workspace-to-monitor-up;
507 "Mod+Shift+Control+S".action = move-workspace-to-monitor-right;
508
509 "Mod+G".action = focus-adjacent-workspace "down";
510 "Mod+C".action = focus-adjacent-workspace "up";
511
512 "Mod+Shift+G".action = move-column-to-adjacent-workspace "down";
513 "Mod+Shift+C".action = move-column-to-adjacent-workspace "up";
514
515 "Mod+Shift+Control+G".action = move-workspace-down;
516 "Mod+Shift+Control+C".action = move-workspace-up;
517
518 "Mod+ParenLeft".action = focus-workspace "comm";
519 "Mod+Shift+ParenLeft".action = move-column-to-workspace "comm";
520
521 "Mod+ParenRight".action = focus-workspace "web";
522 "Mod+Shift+ParenRight".action = move-column-to-workspace "web";
523
524 "Mod+BraceRight".action = focus-workspace "read";
525 "Mod+Shift+BraceRight".action = move-column-to-workspace "read";
526
527 "Mod+BraceLeft".action = focus-workspace "mon";
528 "Mod+Shift+BraceLeft".action = move-column-to-workspace "mon";
529
530 "Mod+Asterisk".action = focus-workspace "vid";
531 "Mod+Shift+Asterisk".action = move-column-to-workspace "vid";
532
533 "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}'';
534 "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}'';
535
536 "Mod+M".action = consume-or-expel-window-left;
537 "Mod+W".action = consume-or-expel-window-right;
538
539 "Mod+R".action = switch-preset-column-width;
540 "Mod+Shift+R".action = switch-preset-window-height;
541 "Mod+F".action = center-column;
542 "Mod+Shift+F".action = maximize-column;
543 "Mod+Shift+Ctrl+F".action = fullscreen-window;
544
545 "Mod+V".action = switch-focus-between-floating-and-tiling;
546 "Mod+Shift+V".action = toggle-window-floating;
547
548 "Mod+Left".action = set-column-width "-10%";
549 "Mod+Down".action = set-window-height "-10%";
550 "Mod+Up".action = set-window-height "+10%";
551 "Mod+Right".action = set-column-width "+10%";
552
553 "Mod+Shift+Z" = {
554 action = spawn (lib.getExe niri) "msg" "action" "power-off-monitors";
555 allow-when-locked = true;
556 };
557 "Mod+Shift+L".action = spawn loginctl "lock-session";
558 "Mod+Shift+E".action = quit;
559 "Mod+Shift+Minus" = {
560 action = spawn systemctl "suspend";
561 allow-when-locked = true;
562 };
563 "Mod+Shift+Control+Minus" = {
564 action = spawn systemctl "hibernate";
565 allow-when-locked = true;
566 };
567 "Mod+Shift+P" = {
568 action = spawn (lib.getExe pkgs.playerctl) "-a" "pause";
569 allow-when-locked = true;
570 };
571
572 "XF86MonBrightnessUp" = {
573 action = spawn lightctl "-d" "-e4" "-n1" "up";
574 allow-when-locked = true;
575 };
576 "XF86MonBrightnessDown" = {
577 action = spawn lightctl "-d" "-e4" "-n1" "down";
578 allow-when-locked = true;
579 };
580 "XF86AudioRaiseVolume" = {
581 action = spawn volumectl "-d" "-u" "up";
582 allow-when-locked = true;
583 };
584 "XF86AudioLowerVolume" = {
585 action = spawn volumectl "-d" "-u" "down";
586 allow-when-locked = true;
587 };
588 "XF86AudioMute" = {
589 action = spawn volumectl "-d" "toggle-mute";
590 allow-when-locked = true;
591 };
592 "XF86AudioMicMute" = {
593 action = spawn volumectl "-d" "-m" "toggle-mute";
594 allow-when-locked = true;
595 };
596
597 "Mod+Semicolon".action = spawn makoctl "dismiss" "--group";
598 "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all";
599 "Mod+Period".action = spawn makoctl "menu" (lib.getExe config.programs.fuzzel.package) "--dmenu";
600 "Mod+Comma".action = spawn makoctl "restore";
601
602 "Mod+Control+A".action = focus-or-spawn-action-app_id "com.saivert.pwvucontrol" "pwctl" "pwvucontrol";
603 "Mod+Control+P".action = focus-or-spawn-action-app_id "org.keepassxc.KeePassXC" "kpxc" "keepassxc";
604 "Mod+Control+B".action = focus-or-spawn-action-app_id ".blueman-manager-wrapped" "bmgr" "blueman-manager";
605 "Mod+Control+Return".action = focus-or-spawn-action-app_id "kitty-scratch" "term" "kitty" "--app-id" "kitty-scratch";
606 "Mod+Control+E".action = focus-or-spawn-action "select(.app_id == \"emacs\" and .title == \"scratch\")" "edit" "emacsclient" "-c" "--frame-parameters=(quote (name . \"scratch\"))";
607 };
608 };
609 }; 813 };
610} 814}