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