summaryrefslogtreecommitdiff
path: root/accounts/gkleen@sif/niri
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/gkleen@sif/niri')
-rw-r--r--accounts/gkleen@sif/niri/default.nix842
-rw-r--r--accounts/gkleen@sif/niri/mako.nix119
-rw-r--r--accounts/gkleen@sif/niri/swayosd.nix65
-rw-r--r--accounts/gkleen@sif/niri/waybar.nix347
4 files changed, 0 insertions, 1373 deletions
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix
deleted file mode 100644
index 216a98ea..00000000
--- a/accounts/gkleen@sif/niri/default.nix
+++ /dev/null
@@ -1,842 +0,0 @@
1{ config, hostConfig, pkgs, lib, flakeInputs, ... }:
2let
3 cfg = config.programs.niri;
4
5 kdl = flakeInputs.niri-flake.lib.kdl;
6
7 niri = cfg.package;
8 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
14 focus_or_spawn = pkgs.writeShellApplication {
15 name = "focus-or-spawn";
16 runtimeInputs = [ niri pkgs.gojq pkgs.gnugrep pkgs.socat ];
17 text = ''
18 window_select="$1"
19 shift
20 workspace_name="$1"
21 shift
22
23 workspaces_json="$(niri msg -j workspaces)"
24 workspace_output="$(jq -r --arg workspace_name "$workspace_name" '.[] | select(.name == $workspace_name) | .output' <<<"$workspaces_json")"
25 # active_workspace="$(jq -r --arg workspace_output "$workspace_output" '.[] | select(.output == $workspace_output and .is_active) | .id' <<<"$workspaces_json")"
26 active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")"
27 if [[ $workspace_output != "$active_output" ]]; then
28 niri msg action move-workspace-to-monitor --reference "$workspace_name" "$active_output"
29 # socat STDIO "$NIRI_SOCKET" <<<'{"Action":{"FocusWorkspace":{"reference":{"Id":'"''${active_workspace}"'}}}}'
30 # niri msg action move-workspace-to-index --reference "$workspace_name" 1
31 fi
32
33 while IFS=$'\n' read -r window_json; do
34 if [[ -n $(jq -c "$window_select" <<<"$window_json") ]]; then
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
40 exit 0
41 fi
42 done < <(niri msg -j windows | jq -c '.[]')
43
44 exec "$@"
45 '';
46 };
47 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
50 with_adjacent_workspace = pkgs.writeShellApplication {
51 name = "with-adjacent-workspace";
52 runtimeInputs = [ niri pkgs.gojq pkgs.socat ];
53 text = ''
54 blacklist="$1"
55 shift
56 direction="$1"
57 shift
58 action="$1"
59 shift
60
61 workspaces_json="$(niri msg -j workspaces)"
62 active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")"
63 workspace_output="$(jq -r --arg active_workspace "$active_workspace" '.[] | select(.id == ($active_workspace | tonumber)) | .output' <<<"$workspaces_json")"
64 workspace_idx="$(jq -r '.[] | select(.is_focused) | .idx' <<<"$workspaces_json")"
65
66 jq_script='map(select('
67 case "$direction" in
68 down)
69 # shellcheck disable=SC2016
70 jq_script=''${jq_script}'.idx > ($workspace_idx | tonumber)';;
71 up)
72 # shellcheck disable=SC2016
73 jq_script=''${jq_script}'.idx < ($workspace_idx | tonumber)';;
74 esac
75 # shellcheck disable=SC2016
76 jq_script=''${jq_script}' and .output == $workspace_output and ((.name == null) or (.name | test($blacklist) | not)))) | sort_by(.idx)'
77 [[ $direction == "up" ]] && jq_script=''${jq_script}' | reverse'
78 jq_script=''${jq_script}' | .[0]'
79
80 workspace_json=$(jq -c --arg blacklist "$blacklist" --arg workspace_output "$workspace_output" --arg workspace_idx "$workspace_idx" "$jq_script" <<<"$workspaces_json")
81 [[ -n $workspace_json && $workspace_json != null ]] || exit 0
82 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET"
83 '';
84 };
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}}}}'';
87 move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}'';
88
89 with_unnamed_workspace = pkgs.writeShellApplication {
90 name = "with-unnamed-workspace";
91 runtimeInputs = [ niri pkgs.gojq pkgs.socat ];
92 text = ''
93 action="$1"
94 shift
95
96 workspaces_json="$(niri msg -j workspaces)"
97 active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")"
98 active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")"
99
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")"
102 [[ -n $workspace_json && $workspace_json != null ]] || exit 0
103 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET"
104 '';
105 };
106 with-unnamed-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_unnamed_workspace);
107
108 with_empty_unnamed_workspace = pkgs.writeShellApplication {
109 name = "with-empty-unnamed-workspace";
110 runtimeInputs = [ niri pkgs.gojq pkgs.socat ];
111 text = ''
112 action="$1"
113 shift
114
115 workspaces_json="$(niri msg -j workspaces)"
116 active_output="$(jq '.[] | select(.is_focused) | .output' <<<"$workspaces_json")"
117 target_workspace_id="$(jq --argjson active_output "$active_output" 'map(select(.active_window_id == null and .name == null and .output == $active_output)) | sort_by(.idx) | .[0].id' <<<"$workspaces_json")"
118 jq --argjson workspace_id "$target_workspace_id" -nc "$action" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET"
119 '';
120 };
121 with-empty-unnamed-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_empty_unnamed_workspace);
122
123 with_select_window = pkgs.writeShellApplication {
124 name = "with-select-window";
125 runtimeInputs = [ niri pkgs.gojq pkgs.socat config.programs.fuzzel.package pkgs.gawk ];
126 text = ''
127 window_select="$1"
128 shift
129 action="$1"
130 shift
131
132 windows_json="$(niri msg -j windows)"
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)"
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")"
137
138 [[ -z "$window_json" ]] && exit 1
139
140 jq -c "$action" <<<"$window_json" | socat STDIO "$NIRI_SOCKET"
141 '';
142 };
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
151 options = {
152 programs.niri.scratchspaces = lib.mkOption {
153 type = lib.types.listOf (lib.types.submodule ({ config, ... }: {
154 options = {
155 name = lib.mkOption {
156 type = lib.types.str;
157 };
158 match = lib.mkOption {
159 type = lib.types.listOf (lib.types.attrsOf kdl.types.kdl-args);
160 default = [];
161 };
162 exclude = lib.mkOption {
163 type = lib.types.listOf (lib.types.attrsOf kdl.types.kdl-args);
164 default = [];
165 };
166 windowRuleExtra = lib.mkOption {
167 type = kdl.types.kdl-nodes;
168 default = [];
169 };
170 key = lib.mkOption {
171 type = lib.types.nullOr lib.types.str;
172 default = null;
173 };
174 spawn = lib.mkOption {
175 type = lib.types.nullOr (lib.types.listOf lib.types.str);
176 default = null;
177 };
178 app-id = lib.mkOption {
179 type = lib.types.nullOr lib.types.str;
180 default = null;
181 };
182 selector = lib.mkOption {
183 type = lib.types.nullOr lib.types.str;
184 default = null;
185 };
186 };
187
188 config = lib.mkMerge [
189 (lib.mkIf (config.app-id != null) {
190 match = lib.mkDefault [ { app-id = "^${lib.escapeRegex config.app-id}$"; } ];
191 selector = lib.mkDefault "select(.app_id == \"${config.app-id}\")";
192 })
193 ];
194 }));
195 default = [];
196 };
197 };
198
199 config = {
200 systemd.user.services.xwayland-satellite = {
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
231 systemd.user.sockets.niri-workspace-history = {
232 Socket = {
233 ListenStream = "%t/niri-workspace-history.sock";
234 SocketMode = "0600";
235 };
236 };
237 systemd.user.services.niri-workspace-history = {
238 Unit = {
239 BindsTo = [ "niri.service" ];
240 After = [ "niri.service" ];
241 };
242 Install = {
243 WantedBy = [ "niri.service" ];
244 };
245 Service = {
246 Type = "simple";
247 Sockets = [ "niri-workspace-history.socket" ];
248 ExecStart = pkgs.writers.writePython3 "niri-workspace-history" {} ''
249 import os
250 import socket
251 import json
252 import sys
253 from collections import defaultdict
254 from threading import Thread, Lock
255 from socketserver import StreamRequestHandler, ThreadingTCPServer
256 from contextlib import contextmanager
257 from io import TextIOWrapper
258
259
260 @contextmanager
261 def detaching(thing):
262 try:
263 yield thing
264 finally:
265 thing.detach()
266
267
268 workspace_history = defaultdict(list)
269 history_lock = Lock()
270
271
272 def monitor_niri():
273 workspaces = list()
274
275 def focus_workspace(output, workspace):
276 global workspace_history, history_lock
277
278 with history_lock:
279 workspace_history[output] = [workspace] + [ws for ws in workspace_history[output] if ws != workspace] # noqa: E501
280 print(json.dumps(workspace_history), file=sys.stderr)
281
282 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
283 sock.connect(os.environ["NIRI_SOCKET"])
284 sock.send(b"\"EventStream\"\n")
285 for line in sock.makefile(buffering=1, encoding='utf-8'):
286 if line_json := json.loads(line):
287 if "WorkspacesChanged" in line_json:
288 workspaces = line_json["WorkspacesChanged"]["workspaces"]
289 for ws in workspaces:
290 if ws["is_focused"]:
291 focus_workspace(ws["output"], ws["id"])
292 if "WorkspaceActivated" in line_json:
293 for ws in workspaces:
294 if ws["id"] != line_json["WorkspaceActivated"]["id"]:
295 continue
296 focus_workspace(ws["output"], ws["id"])
297 break
298
299
300 class RequestHandler(StreamRequestHandler):
301 def handle(self):
302 global workspace_history, history_lock
303
304 with detaching(TextIOWrapper(self.wfile, encoding='utf-8', write_through=True)) as out: # noqa: E501
305 with history_lock:
306 json.dump(workspace_history, out)
307
308
309 class Server(ThreadingTCPServer):
310 def __init__(self):
311 ThreadingTCPServer.__init__(self, ("", 8000), RequestHandler, bind_and_activate=False) # noqa: E501
312 self.socket = socket.fromfd(3, self.address_family, self.socket_type)
313
314
315 def run_server():
316 with Server() as server:
317 server.serve_forever()
318
319
320 niri = Thread(target=monitor_niri)
321 niri.daemon = True
322 niri.start()
323
324 server_thread = Thread(target=run_server)
325 server_thread.daemon = True
326 server_thread.start()
327
328 while True:
329 server_thread.join(timeout=0.5)
330 niri.join(timeout=0.5)
331
332 if not (niri.is_alive() and server_thread.is_alive()):
333 break
334 '';
335 };
336 };
337
338 programs.niri.scratchspaces = [
339 { name = "pwctl";
340 key = "Mod+Control+A";
341 spawn = ["pwvucontrol"];
342 app-id = "com.saivert.pwvucontrol";
343 }
344 { name = "kpxc";
345 exclude = [
346 { title = "^Unlock Database.*"; }
347 { title = "^Access Request.*"; }
348 { title = ".*Passkey credentials$"; }
349 ];
350 windowRuleExtra = [
351 (kdl.leaf "open-focused" false)
352 ];
353 key = "Mod+Control+P";
354 app-id = "org.keepassxc.KeePassXC";
355 spawn = [ "keepassxc" ];
356 }
357 { name = "bmgr";
358 key = "Mod+Control+B";
359 app-id = ".blueman-manager-wrapped";
360 spawn = [ "blueman-manager" ];
361 }
362 { name = "term";
363 key = "Mod+Control+Return";
364 app-id = "kitty-scratch";
365 spawn = [ "kitty" "--app-id" "kitty-scratch" ];
366 }
367 { name = "edit";
368 match = [ { title = "^scratch$"; app-id = "^emacs$"; } ];
369 key = "Mod+Control+E";
370 selector = "select(.app_id == \"emacs\" and .title == \"scratch\")";
371 spawn = [ "emacsclient" "-c" "--frame-parameters=(quote (name . \"scratch\"))" ];
372 }
373 { name = "eff";
374 key = "Mod+Control+O";
375 app-id = "com.github.wwmm.easyeffects";
376 spawn = [ "easyeffects" ];
377 }
378 ];
379 programs.niri.config =
380 let
381 inherit (kdl) node plain leaf flag;
382 optional-node = cond: v:
383 if cond
384 then v
385 else null;
386 opt-props = lib.filterAttrs (lib.const (value: value != null));
387 in
388 [ (flag "prefer-no-csd")
389
390 (leaf "screenshot-path" "~/screenshots/%Y-%m-%dT%H:%M:%S.png")
391
392 (plain "hotkey-overlay" [
393 (flag "skip-at-startup")
394 ])
395
396 (plain "input" [
397 (plain "keyboard" [
398 (leaf "repeat-delay" 300)
399 (leaf "repeat-rate" 50)
400
401 (plain "xkb" [
402 (leaf "layout" "us,us")
403 (leaf "variant" "dvp,")
404 (leaf "options" "compose:caps,grp:win_space_toggle")
405 ])
406 ])
407
408 (flag "workspace-auto-back-and-forth")
409 # (leaf "focus-follows-mouse" {})
410 # (flag "warp-mouse-to-focus")
411
412 # (plain "touchpad" [ (flag "off") ])
413 (plain "trackball" [
414 (leaf "scroll-method" "on-button-down")
415 (leaf "scroll-button" 278)
416 ])
417 (plain "touch" [
418 (leaf "map-to-output" "eDP-1")
419 ])
420 ])
421
422 (plain "environment" (lib.mapAttrsToList leaf {
423 NIXOS_OZONE_WL = "1";
424 QT_QPA_PLATFORM = "wayland";
425 QT_WAYLAND_DISABLE_WINDOWDECORATION = "1";
426 GDK_BACKEND = "wayland";
427 SDL_VIDEODRIVER = "wayland";
428 DISPLAY = ":0";
429 }))
430
431 (node "output" "eDP-1" [
432 (leaf "scale" 1.5)
433 (leaf "position" { x = 0; y = 0; })
434 ])
435 (node "output" "Ancor Communications Inc ASUS PB287Q 0x0000DD9B" [
436 (leaf "scale" 1.5)
437 (leaf "position" { x = 2560; y = 0; })
438 ])
439 (node "output" "HP Inc. HP 727pu CN4417143K" [
440 (leaf "mode" "2560x1440@119.998")
441 (leaf "scale" 1)
442 (leaf "position" { x = 2560; y = 0; })
443 (flag "variable-refresh-rate")
444 ])
445
446 (plain "debug" [
447 (leaf "render-drm-device" "/dev/dri/by-path/pci-0000:00:02.0-render")
448 ])
449
450 (plain "animations" [
451 (leaf "slowdown" 0.5)
452 (plain "workspace-switch" [(flag "off")])
453 ])
454
455 (plain "layout" [
456 (leaf "gaps" 8)
457 (plain "struts" [
458 (leaf "left" 0)
459 (leaf "right" 0)
460 (leaf "top" 0)
461 (leaf "bottom" 0)
462 ])
463 (plain "border" [
464 (leaf "width" 2)
465 (leaf "active-gradient" {
466 from = "hsla(195 100% 45% 1)";
467 to = "hsla(155 100% 37.5% 1)";
468 angle = 29;
469 relative-to = "workspace-view";
470 })
471 (leaf "inactive-gradient" {
472 from = "hsla(0 0% 27.7% 1)";
473 to = "hsla(0 0% 23% 1)";
474 angle = 29;
475 relative-to = "workspace-view";
476 })
477 ])
478 (plain "focus-ring" [
479 (flag "off")
480 ])
481
482 (plain "preset-column-widths" (map (prop: leaf "proportion" prop) [
483 (1. / 4.) (1. / 3.) (1. / 2.) (2. / 3.) (3. / 4.) (1.)
484 ]))
485 (plain "default-column-width" [ (leaf "proportion" (1. / 2.)) ])
486 (plain "preset-window-heights" (map (prop: leaf "proportion" prop) [
487 (1. / 3.) (1. / 2.) (2. / 3.) (1.)
488 ]))
489
490 (flag "always-center-single-column")
491
492 (plain "tab-indicator" [
493 (leaf "gap" 4)
494 (leaf "width" 8)
495 (leaf "gaps-between-tabs" 4)
496 (flag "place-within-column")
497 (leaf "length" { total-proportion = 1.; })
498 (leaf "active-gradient" {
499 from = "hsla(195 100% 60% 0.75)";
500 to = "hsla(155 100% 50% 0.75)";
501 angle = 29;
502 relative-to = "workspace-view";
503 })
504 (leaf "inactive-gradient" {
505 from = "hsla(0 0% 42% 0.66)";
506 to = "hsla(0 0% 35% 0.66)";
507 angle = 29;
508 relative-to = "workspace-view";
509 })
510 ])
511 ])
512
513 (plain "cursor" [
514 (flag "hide-when-typing")
515 ])
516
517 (map (name:
518 (node "workspace" name [
519 (leaf "open-on-output" "eDP-1")
520 ])
521 ) (map ({name, ...}: name) cfg.scratchspaces))
522 (map (name:
523 (leaf "workspace" name)
524 ) ["comm" "web" "vid" "bmr"])
525
526 (plain "window-rule" [
527 (leaf "clip-to-geometry" true)
528 ])
529
530 (plain "window-rule" [
531 (leaf "match" { is-floating = true; })
532 (leaf "geometry-corner-radius" 8)
533 (plain "shadow" [ (flag "on") ])
534 ])
535
536 (plain "window-rule" [
537 (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; })
538 (leaf "block-out-from" "screencast")
539 ])
540 (plain "window-rule" [
541 (map (title:
542 (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; })
543 ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$"])
544 (leaf "open-focused" true)
545 (leaf "open-floating" true)
546 ])
547
548 (map ({ name, match, exclude, windowRuleExtra, ... }:
549 (optional-node (match != []) (plain "window-rule" [
550 (map (leaf "match") match)
551 (map (leaf "exclude") exclude)
552 (leaf "open-on-workspace" name)
553 (leaf "open-maximized" true)
554 windowRuleExtra
555 ]))
556 ) cfg.scratchspaces)
557
558 (plain "window-rule" [
559 (leaf "match" { app-id = "^emacs$"; })
560 (leaf "match" { app-id = "^firefox$"; })
561 (plain "default-column-width" [(leaf "proportion" (2. / 3.))])
562 ])
563 (plain "window-rule" [
564 (leaf "match" { app-id = "^kitty$"; })
565 (leaf "match" { app-id = "^kitty-play$"; })
566 (plain "default-column-width" [(leaf "proportion" (1. / 3.))])
567 ])
568
569 (plain "window-rule" [
570 (leaf "match" { app-id = "^thunderbird$"; })
571 (leaf "match" { app-id = "^Element$"; })
572 (leaf "match" { app-id = "^Rainbow$"; })
573 (leaf "open-on-workspace" "comm")
574 ])
575 (plain "window-rule" [
576 (leaf "match" { app-id = "^firefox$"; })
577 (leaf "open-on-workspace" "web")
578 (leaf "open-maximized" true)
579 ])
580 (plain "window-rule" [
581 (leaf "match" { app-id = "^mpv$"; })
582 (leaf "open-on-workspace" "vid")
583 (plain "default-column-width" [(leaf "proportion" 1.)])
584 ])
585 (plain "window-rule" [
586 (leaf "match" { app-id = "^kitty-play$"; })
587 (leaf "open-on-workspace" "vid")
588 (leaf "open-focused" false)
589 ])
590 (plain "window-rule" [
591 (leaf "match" { app-id = "^pdfpc$"; })
592 (plain "default-column-width" [(leaf "proportion" 1.)])
593 ])
594 (plain "window-rule" [
595 (leaf "match" { app-id = "^pdfpc$"; title = "^pdfpc - presentation$"; })
596 (plain "default-column-width" [(leaf "proportion" 1.)])
597 (leaf "open-fullscreen" true)
598 (leaf "open-on-workspace" "bmr")
599 (leaf "open-focused" false)
600 ])
601 (plain "window-rule" [
602 (map (leaf "match") [
603 { app-id = "^Gimp-"; title = "^Quit GIMP$"; }
604 { app-id = "^org\\.kde\\.polkit-kde-authentication-agent-1$"; }
605 { app-id = "^xdg-desktop-portal-gtk$"; }
606 ])
607 (leaf "open-floating" true)
608 ])
609 (plain "window-rule" [
610 (leaf "match" { app-id = "^org\\.pwmt\\.zathura$"; })
611 (leaf "match" { app-id = "^evince$"; })
612 (leaf "default-column-display" "tabbed")
613 ])
614
615 (plain "layer-rule" [
616 (leaf "match" { namespace = "^notifications$"; })
617 (leaf "match" { namespace = "^waybar$"; })
618 (leaf "match" { namespace = "^launcher$"; })
619 (leaf "block-out-from" "screencast")
620 ])
621
622 (plain "binds"
623 (let
624 bind = name: cfg: node name (opt-props {
625 cooldown-ms = cfg.cooldown-ms or null;
626 }
627 // (lib.optionalAttrs (!(cfg.repeat or true)) {
628 repeat = false;
629 })
630 // (lib.optionalAttrs (cfg.allow-when-locked or false) {
631 allow-when-locked = true;
632 })) (lib.mapAttrsToList leaf (lib.removeAttrs cfg.action ["__functor"]));
633 in
634 [
635 (lib.mapAttrsToList bind (with config.lib.niri.actions; {
636 "Mod+Slash".action = show-hotkey-overlay;
637
638 "Mod+Return".action = spawn terminal;
639 "Mod+Q".action = close-window;
640 "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package);
641 "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path";
642
643 "Mod+Alt+E".action = spawn (lib.getExe' config.services.emacs.package "emacsclient") "-c";
644 "Mod+Alt+Y".action = spawn (lib.getExe (pkgs.writeShellApplication {
645 name = "queue-yt-dlp";
646 runtimeInputs = with pkgs; [ wl-clipboard-rs socat ];
647 text = ''
648 socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/yt-dlp.sock <<<$'{ "urls": ["'"$(wl-paste)"$'"] }'
649 '';
650 }));
651 "Mod+Alt+L".action = spawn (lib.getExe (pkgs.writeShellApplication {
652 name = "queue-yt-dlp";
653 runtimeInputs = with pkgs; [ wl-clipboard-rs config.programs.kitty.package ];
654 text = ''
655 exec -- kitty --app-id kitty-play --directory "$HOME"/media mpv "$(wl-paste)"
656 '';
657 }));
658 "Mod+Alt+M".action = spawn (lib.getExe' pkgs.screen-message "sm") "-n" "Fira Mono" "-a" "1" "-f" "#fff" "-b" "#000";
659
660 "Mod+U".action = spawn (lib.getExe (pkgs.writeShellApplication {
661 name = "qalc-fuzzel";
662 runtimeInputs = with pkgs; [ wl-clipboard-rs libqalculate config.programs.fuzzel.package coreutils findutils libnotify gnugrep ];
663 text = ''
664 RESULTS_DIR="$HOME/.cache/qalc-fuzzel"
665 prev() {
666 FOUND=false
667 while IFS= read -r line; do
668 [[ -n "$line" ]] || continue
669 FOUND=true
670 echo "$line"
671 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)
672 $FOUND || echo
673 }
674 FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> ") || exit $?
675 if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then
676 QALC_RES="$FUZZEL_RES"
677 QALC_RET=0
678 else
679 QALC_RES=$(qalc "$FUZZEL_RES" 2>&1)
680 QALC_RET=$?
681 fi
682 [[ -n "$QALC_RES" ]] || exit 1
683 EXISTING=false
684 set +o pipefail
685 grep -Fxrl "$QALC_RES" "$RESULTS_DIR" | xargs -r touch
686 [[ ''${PIPESTATUS[0]} -eq 0 ]] && EXISTING=true
687 set -o pipefail
688 if [[ $QALC_RET -eq 0 ]] && ! $EXISTING; then
689 set +o pipefail
690 RES_FILE="$RESULTS_DIR"/$(date -uIs).$(tr -Cd 'a-zA-Z0-9' </dev/random | head -c 10)
691 set -o pipefail
692 cat >"$RES_FILE" <<<"$QALC_RES"
693 fi
694 [[ "$QALC_RES" =~ .*\ =\ (.*) ]] && QALC_RES="''${BASH_REMATCH[1]}"
695 [[ $QALC_RET -eq 0 ]] && wl-copy "$QALC_RES"
696 notify-send "$QALC_RES"
697 '';
698 }));
699 "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication {
700 name = "emoji-fuzzel";
701 runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ];
702 text = ''
703 FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " <"$HOME"/.local/share/emoji-data/list.txt) || exit $?
704 [[ -n "$FUZZEL_RES" ]] || exit 1
705 wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste
706 '';
707 }));
708 "Print".action = screenshot;
709 "Control+Print".action = screenshot-window;
710 # "Shift+Print".action = screenshot-screen;
711 "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
712 "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
713
714 "Mod+Escape" = {
715 allow-inhibiting = false;
716 action = toggle-keyboard-shortcuts-inhibit;
717 };
718
719 "Mod+H".action = focus-column-left;
720 "Mod+T".action = focus-window-down;
721 "Mod+N".action = focus-window-up;
722 "Mod+S".action = focus-column-right;
723
724 "Mod+Shift+H".action = move-column-left;
725 "Mod+Shift+T".action = move-window-down;
726 "Mod+Shift+N".action = move-window-up;
727 "Mod+Shift+S".action = move-column-right;
728
729 "Mod+Control+H".action = focus-monitor-left;
730 "Mod+Control+T".action = focus-monitor-down;
731 "Mod+Control+N".action = focus-monitor-up;
732 "Mod+Control+S".action = focus-monitor-right;
733
734 "Mod+Shift+Control+H".action = move-workspace-to-monitor-left;
735 "Mod+Shift+Control+T".action = move-workspace-to-monitor-down;
736 "Mod+Shift+Control+N".action = move-workspace-to-monitor-up;
737 "Mod+Shift+Control+S".action = move-workspace-to-monitor-right;
738
739 "Mod+G".action = focus-adjacent-workspace "down";
740 "Mod+C".action = focus-adjacent-workspace "up";
741
742 "Mod+Shift+G".action = move-column-to-adjacent-workspace "down";
743 "Mod+Shift+C".action = move-column-to-adjacent-workspace "up";
744
745 "Mod+Shift+Control+G".action = move-workspace-down;
746 "Mod+Shift+Control+C".action = move-workspace-up;
747
748 "Mod+ParenLeft".action = focus-workspace "comm";
749 "Mod+Shift+ParenLeft".action = move-column-to-workspace "comm";
750
751 "Mod+ParenRight".action = focus-workspace "web";
752 "Mod+Shift+ParenRight".action = move-column-to-workspace "web";
753
754 "Mod+BraceRight".action = focus-workspace "read";
755 "Mod+Shift+BraceRight".action = move-column-to-workspace "read";
756
757 "Mod+BraceLeft".action = focus-workspace "mon";
758 "Mod+Shift+BraceLeft".action = move-column-to-workspace "mon";
759
760 "Mod+Asterisk".action = focus-workspace "vid";
761 "Mod+Shift+Asterisk".action = move-column-to-workspace "vid";
762
763 "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}'';
764 "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}'';
765
766 "Mod+M".action = consume-or-expel-window-left;
767 "Mod+W".action = consume-or-expel-window-right;
768
769 "Mod+Shift+M".action = toggle-column-tabbed-display;
770
771 "Mod+R".action = switch-preset-column-width;
772 "Mod+Shift+R".action = switch-preset-window-height;
773 "Mod+F".action = center-column;
774 "Mod+Shift+F".action = maximize-column;
775 "Mod+Shift+Ctrl+F".action = fullscreen-window;
776
777 "Mod+V".action = switch-focus-between-floating-and-tiling;
778 "Mod+Shift+V".action = toggle-window-floating;
779
780 "Mod+Left".action = set-column-width "-10%";
781 "Mod+Down".action = set-window-height "-10%";
782 "Mod+Up".action = set-window-height "+10%";
783 "Mod+Right".action = set-column-width "+10%";
784
785 "Mod+Shift+Z" = {
786 action = spawn (lib.getExe niri) "msg" "action" "power-off-monitors";
787 allow-when-locked = true;
788 };
789 "Mod+Shift+L".action = spawn loginctl "lock-session";
790 "Mod+Shift+E".action = quit;
791 "Mod+Shift+Minus" = {
792 action = spawn systemctl "suspend";
793 allow-when-locked = true;
794 };
795 "Mod+Shift+Control+Minus" = {
796 action = spawn systemctl "hibernate";
797 allow-when-locked = true;
798 };
799 "Mod+Shift+P" = {
800 action = spawn (lib.getExe pkgs.playerctl) "-a" "pause";
801 allow-when-locked = true;
802 };
803
804 "XF86MonBrightnessUp" = {
805 action = spawn swayosd-client "--brightness" "raise";
806 allow-when-locked = true;
807 };
808 "XF86MonBrightnessDown" = {
809 action = spawn swayosd-client "--brightness" "lower";
810 allow-when-locked = true;
811 };
812 "XF86AudioRaiseVolume" = {
813 action = spawn swayosd-client "--output-volume" "raise";
814 allow-when-locked = true;
815 };
816 "XF86AudioLowerVolume" = {
817 action = spawn swayosd-client "--output-volume" "lower";
818 allow-when-locked = true;
819 };
820 "XF86AudioMute" = {
821 action = spawn swayosd-client "--output-volume" "mute-toggle";
822 allow-when-locked = true;
823 };
824 "XF86AudioMicMute" = {
825 action = spawn swayosd-client "--input-volume" "mute-toggle";
826 allow-when-locked = true;
827 };
828
829 "Mod+Semicolon".action = spawn makoctl "dismiss" "--group";
830 "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all";
831 "Mod+Period".action = spawn makoctl "menu" (lib.getExe config.programs.fuzzel.package) "--dmenu";
832 "Mod+Comma".action = spawn makoctl "restore";
833
834 "Mod+Control+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"FocusWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}";
835 "Mod+Control+Shift+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"MoveColumnToWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}";
836 }))
837 (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)
838 ]
839 ))
840 ];
841 };
842}
diff --git a/accounts/gkleen@sif/niri/mako.nix b/accounts/gkleen@sif/niri/mako.nix
deleted file mode 100644
index 2788fb82..00000000
--- a/accounts/gkleen@sif/niri/mako.nix
+++ /dev/null
@@ -1,119 +0,0 @@
1{ config, lib, pkgs, ... }:
2{
3 config = {
4 services.mako = {
5 enable = true;
6 font = "Fira Sans 10";
7 format = "<i>%s</i>\\n%b";
8 margin = "2";
9 maxVisible = -1;
10 backgroundColor = "#000000dd";
11 progressColor = "source #223544ff";
12 width = 384;
13 extraConfig = ''
14 outer-margin=1
15 max-history=100
16 max-icon-size=48
17
18 [grouped]
19 format=<b>(%g)</b> <i>%s</i>\n%b
20
21 [urgency=low]
22 text-color=#999999ff
23
24 [urgency=critical]
25 background-color=#900000dd
26
27 [app-name=Element]
28 group-by=summary
29
30 [app-name=poweralertd]
31 ignore-timeout=1
32 default-timeout=2000
33
34 [mode=silent]
35 invisible=1
36 '';
37 package = pkgs.symlinkJoin {
38 name = "${pkgs.mako.name}-wrapped";
39 paths = with pkgs; [ mako ];
40 inherit (pkgs.mako) meta;
41 postBuild = ''
42 rm -r $out/share/dbus-1
43 '';
44 };
45 };
46 systemd.user.services.mako = {
47 Unit = {
48 Description = "Mako notification daemon";
49 PartOf = [ "graphical-session.target" ];
50 };
51 Install = {
52 WantedBy = [ "graphical-session.target" ];
53 };
54 Service = {
55 Type = "dbus";
56 BusName = "org.freedesktop.Notifications";
57 ExecStart = lib.getExe config.services.mako.package;
58 RestartSec = 5;
59 Restart = "always";
60 };
61 };
62
63 systemd.user.services.mako-follows-focus = {
64 Unit = {
65 BindsTo = [ "niri.service" "mako.service" ];
66 After = [ "niri.service" "mako.service" ];
67 };
68 Service = {
69 Type = "simple";
70 Restart = "always";
71 ExecStart = pkgs.writers.writePython3 "mako-follows-focus" {
72 libraries = with pkgs.python3Packages; [];
73 } ''
74 import os
75 import socket
76 import json
77 import subprocess
78
79
80 current_output = None
81 workspaces = []
82
83
84 def output_changed(new_output):
85 global current_output
86
87 if current_output == new_output:
88 return
89
90 current_output = new_output
91 subprocess.run(["makoctl", "reload"])
92
93
94 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
95 sock.connect(os.environ["NIRI_SOCKET"])
96 sock.send(b"\"EventStream\"\n")
97 for line in sock.makefile(buffering=1, encoding='utf-8'):
98 if line_json := json.loads(line):
99 if "WorkspacesChanged" in line_json:
100 workspaces = line_json["WorkspacesChanged"]["workspaces"]
101 for workspace in workspaces:
102 if not workspace["is_focused"]:
103 continue
104 output_changed(workspace["output"])
105 break
106 if "WorkspaceActivated" in line_json and line_json["WorkspaceActivated"]["focused"]: # noqa: E501
107 for workspace in workspaces:
108 if not workspace["id"] == line_json["WorkspaceActivated"]["id"]: # noqa: E501
109 continue
110 output_changed(workspace["output"])
111 break
112 '';
113 };
114 Install = {
115 WantedBy = [ "mako.service" ];
116 };
117 };
118 };
119}
diff --git a/accounts/gkleen@sif/niri/swayosd.nix b/accounts/gkleen@sif/niri/swayosd.nix
deleted file mode 100644
index 984927c2..00000000
--- a/accounts/gkleen@sif/niri/swayosd.nix
+++ /dev/null
@@ -1,65 +0,0 @@
1{ pkgs, ... }:
2{
3 config = {
4 services.swayosd = {
5 enable = true;
6 topMargin = 0.946154;
7 stylePath = pkgs.runCommand "style.css" {
8 src = pkgs.writeText "style.scss" ''
9 window#osd {
10 padding: 12px 20px;
11 border-radius: 999px;
12 border: none;
13 background: rgba(0, 0, 0, 0.87);
14
15 #container {
16 margin: 16px;
17 }
18
19 image,
20 label {
21 color: rgb(255, 255, 255);
22
23 &:disabled {
24 opacity: 1;
25 color: rgb(84, 84, 84);
26 }
27 }
28
29 progressbar {
30 min-height: 6px;
31 border-radius: 999px;
32 background: transparent;
33 border: none;
34
35 trough, progress {
36 min-height: inherit;
37 border-radius: inherit;
38 border: none;
39 }
40
41 trough {
42 background: rgb(127, 127, 127);
43 }
44 progress {
45 background: rgb(255, 255, 255);
46 }
47
48 &:disabled {
49 opacity: 1;
50
51 trough {
52 background: rgb(19, 19, 19);
53 }
54 progress {
55 background: rgb(38, 38, 38);
56 }
57 }
58 }
59 }
60 '';
61 buildInputs = with pkgs; [sass];
62 } "scss -C --sourcemap=none --style=compact $src $out";
63 };
64 };
65}
diff --git a/accounts/gkleen@sif/niri/waybar.nix b/accounts/gkleen@sif/niri/waybar.nix
deleted file mode 100644
index bae818f6..00000000
--- a/accounts/gkleen@sif/niri/waybar.nix
+++ /dev/null
@@ -1,347 +0,0 @@
1{ lib, config, pkgs, ... }:
2let
3 swayosd-client = lib.getExe' config.services.swayosd.package "swayosd-client";
4in {
5 config = {
6 programs.waybar = {
7 enable = true;
8 systemd = {
9 enable = true;
10 target = "graphical-session.target";
11 };
12 settings = let
13 windowRewrites = {
14 "(.*) — Mozilla Firefox" = "$1";
15 "(.*) - Mozilla Thunderbird" = "$1";
16 "(.*) - mpv" = "$1";
17 };
18 iconSize = 11;
19 in [
20 {
21 layer = "top";
22 position = "top";
23 height = 14;
24 output = [ "eDP-1" "DP-2" "DP-3" ];
25 modules-left = [ "niri/workspaces" ];
26 modules-center = [ "niri/window" ];
27 modules-right = [ "custom/worktime" "custom/worktime-today"
28 "custom/weather"
29 "custom/keymap"
30 "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "custom/mako" "clock" ];
31
32 "custom/mako" = {
33 format = "{}";
34 return-type = "json";
35 exec = pkgs.writers.writePython3 "mako-silent" { libraries = [ pkgs.python3Packages.dbus-next ]; } ''
36 from dbus_next.aio import MessageBus
37
38 import asyncio
39
40 import json
41
42
43 loop = asyncio.new_event_loop()
44 asyncio.set_event_loop(loop)
45
46
47 async def main():
48 bus = await MessageBus().connect()
49 # the introspection xml would normally be included in your project, but
50 # this is convenient for development
51 introspection = await bus.introspect('org.freedesktop.Notifications', '/fr/emersion/Mako') # noqa: E501
52
53 obj = bus.get_proxy_object('org.freedesktop.Notifications', '/fr/emersion/Mako', introspection) # noqa: E501
54 mako = obj.get_interface('fr.emersion.Mako')
55 properties = obj.get_interface('org.freedesktop.DBus.Properties')
56
57 async def print_mode():
58 modes = await mako.get_modes()
59 is_silent = "silent" in modes
60 icon = "&#xf009b;" if is_silent else "&#xf009a;"
61 text = f"<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>" # noqa: E501
62 if is_silent:
63 text = f"<span color=\"#ffffff\">{text}</span>"
64 print(json.dumps({'text': text, 'tooltip': ', '.join(modes)}, separators=(',', ':')), flush=True) # noqa: E501
65
66 async def on_properties_changed(interface_name, changed_properties, invalidated_properties): # noqa: E501
67 if "Modes" not in invalidated_properties:
68 return
69
70 await print_mode()
71
72 properties.on_properties_changed(on_properties_changed)
73 await print_mode()
74
75 await loop.create_future()
76
77
78 loop.run_until_complete(main())
79 '';
80 on-click = "makoctl mode -t silent";
81 };
82 "custom/weather" = {
83 format = "{}";
84 tooltip = true;
85 interval = 3600;
86 exec = "${lib.getExe pkgs.wttrbar} --hide-conditions --nerd --custom-indicator \"<span font=\\\"Symbols Nerd Font Mono\\\" size=\\\"100%\\\">{ICON}</span> {FeelsLikeC}°\"";
87 return-type = "json";
88 };
89 "custom/keymap" = {
90 format = "{}";
91 tooltip = true;
92 return-type = "json";
93 exec = pkgs.writers.writePython3 "keymap" {} ''
94 import os
95 import socket
96 import json
97
98
99 def output(keymap):
100 short = keymap
101 if keymap == "English (programmer Dvorak)":
102 short = "dvp"
103 elif keymap == "English (US)":
104 short = "<span color=\"#ffffff\">us</span>"
105 print(json.dumps({'text': short, 'tooltip': keymap}, separators=(',', ':')), flush=True) # noqa: E501
106
107
108 keyboard_layouts = []
109
110 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
111 sock.connect(os.environ["NIRI_SOCKET"])
112 sock.send(b"\"EventStream\"\n")
113 for line in sock.makefile(buffering=1, encoding='utf-8'):
114 if line_json := json.loads(line):
115 if "KeyboardLayoutsChanged" in line_json:
116 keyboard_layouts = line_json["KeyboardLayoutsChanged"]["keyboard_layouts"]["names"] # noqa: E501
117 output(keyboard_layouts[line_json["KeyboardLayoutsChanged"]["keyboard_layouts"]["current_idx"]]) # noqa: E501
118 if "KeyboardLayoutSwitched" in line_json:
119 output(keyboard_layouts[line_json["KeyboardLayoutSwitched"]["idx"]]) # noqa: E501
120 '';
121 on-click = "niri msg action switch-layout next";
122 };
123 "custom/worktime" = {
124 interval = 60;
125 exec = "${lib.getExe pkgs.worktime} time --waybar";
126 return-type = "json";
127 };
128 "custom/worktime-today" = {
129 interval = 60;
130 exec = "${lib.getExe pkgs.worktime} today --waybar";
131 return-type = "json";
132 };
133 "niri/workspaces" = {
134 ignore = map ({ name, ... }: name) config.programs.niri.scratchspaces;
135 };
136 "niri/window" = {
137 separate-outputs = true;
138 icon = true;
139 icon-size = 14;
140 rewrite = windowRewrites;
141 };
142 clock = {
143 interval = 1;
144 # timezone = "Europe/Berlin";
145 format = "W{:%V-%u %F %H:%M:%S%Ez}";
146 tooltip-format = "<tt><small>{calendar}</small></tt>";
147 calendar = {
148 mode = "year";
149 mode-mon-col = 3;
150 weeks-pos = "left";
151 on-scroll = 1;
152 format = {
153 months = "<span color='#ffead3'><b>{}</b></span>";
154 days = "{}";
155 weeks = "<span color='#99ffdd'><b>{}</b></span>";
156 weekdays = "<span color='#ffcc66'><b>{}</b></span>";
157 today = "<span color='#ff6699'><b>{}</b></span>";
158 };
159 };
160 };
161 battery = {
162 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
163 icon-size = iconSize - 2;
164 states = { warning = 30; critical = 15; };
165 format-icons = ["&#xf008e;" "&#xf007a;" "&#xf007b;" "&#xf007c;" "&#xf007d;" "&#xf007e;" "&#xf007f;" "&#xf0080;" "&#xf0081;" "&#xf0082;" "&#xf0079;" ];
166 format-charging = "&#xf0084;";
167 format-plugged = "&#xf06a5;";
168 tooltip-format = "{capacity}% {timeTo}";
169 interval = 20;
170 };
171 tray = {
172 icon-size = 16;
173 # show-passive-items = true;
174 spacing = 1;
175 };
176 privacy = {
177 icon-spacing = 7;
178 icon-size = iconSize;
179 modules = [
180 { type = "screenshare"; }
181 { type = "audio-in"; }
182 ];
183 };
184 idle_inhibitor = {
185 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
186 icon-size = iconSize;
187 format-icons = { activated = "&#xf0208;"; deactivated = "&#xf0209;"; };
188 timeout = 120;
189 };
190 backlight = {
191 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
192 icon-size = iconSize;
193 tooltip-format = "{percent}%";
194 format-icons = ["&#xf00da;" "&#xf00db;" "&#xf00dc;" "&#xf00dd;" "&#xf00de;" "&#xf00df;" "&#xf00e0;"];
195 on-scroll-up = "${swayosd-client} --brightness raise";
196 on-scroll-down = "${swayosd-client} --brightness lower";
197 };
198 wireplumber = {
199 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
200 icon-size = iconSize;
201 tooltip-format = "{volume}% {node_name}";
202 format-icons = ["&#xf057f;" "&#xf0580;" "&#xf057e;"];
203 format-muted = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">&#xf075f;</span>";
204 # ignored-sinks = ["Easy Effects Sink"];
205 on-scroll-up = "${swayosd-client} --output-volume raise";
206 on-scroll-down = "${swayosd-client} --output-volume lower";
207 on-click = "${swayosd-client} --output-volume mute-toggle";
208 };
209 }
210 {
211 layer = "top";
212 position = "top";
213 height = 14;
214 output = [ "!eDP-1" "!DP-2" "!DP-3" ];
215 modules-left = [ "niri/workspaces" ];
216 modules-center = [ "niri/window" ];
217 modules-right = [ "clock" ];
218
219 "niri/workspaces" = {
220 ignore = map ({ name, ... }: name) config.programs.niri.scratchspaces;
221 };
222 "niri/window" = {
223 separate-outputs = true;
224 icon = true;
225 icon-size = 14;
226 rewrite = windowRewrites;
227 };
228 clock = {
229 interval = 1;
230 # timezone = "Europe/Berlin";
231 format = "{:%H:%M}";
232 tooltip-format = "W{:%V-%u %F %H:%M:%S%Ez}";
233 };
234 }
235 ];
236 style = ''
237 @define-color white #ffffff;
238 @define-color grey #555555;
239 @define-color blue #1a8fff;
240 @define-color green #23fd00;
241 @define-color orange #f28a21;
242 @define-color red #f2201f;
243
244 * {
245 border: none;
246 font-family: "Fira Sans";
247 font-size: 10pt;
248 min-height: 0;
249 }
250
251 window#waybar {
252 background-color: rgba(0, 0, 0, 0.66);
253 color: @white;
254 }
255
256 .modules-left {
257 margin-left: 12px;
258 }
259 .modules-right {
260 margin-right: 12px;
261 }
262
263 .module {
264 margin: 0 5px;
265 }
266
267 #workspaces button {
268 color: @white;
269 padding: 2px 5px;
270 }
271 #workspaces button.empty {
272 color: @grey;
273 }
274 #workspaces button.active {
275 color: @green;
276 }
277 #workspaces button.urgent {
278 color: @red;
279 }
280
281 #custom-weather, #custom-keymap, #custom-worktime, #custom-worktime-today {
282 color: @grey;
283 margin: 0 5px;
284 }
285 #custom-weather {
286 margin-right: 3px;
287 }
288 #custom-keymap {
289 margin-left: 3px;
290 margin-right: 3px;
291 }
292
293 #tray {
294 margin: 0;
295 }
296 #battery, #idle_inhibitor, #backlight, #wireplumber, #custom-mako {
297 color: @grey;
298 margin: 0 5px 0 2px;
299 }
300 #idle_inhibitor {
301 margin-right: 4px;
302 margin-left: 6px;
303 }
304 #custom-mako {
305 margin-right: 2px;
306 margin-left: 3px;
307 }
308 #battery {
309 margin-right: 3px;
310 }
311 #battery.discharging {
312 color: @white;
313 }
314 #battery.warning {
315 color: @orange;
316 }
317 #battery.critical {
318 color: @red;
319 }
320 #battery.charging {
321 color: @white;
322 }
323 #idle_inhibitor.activated {
324 color: @white;
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 }
332
333 #idle_inhibitor {
334 padding-top: 1px;
335 }
336
337 #privacy {
338 color: @red;
339 margin: -1px 4px 0px 3px;
340 }
341 #clock {
342 /* margin-right: 5px; */
343 }
344 '';
345 };
346 };
347}