summaryrefslogtreecommitdiff
path: root/accounts
diff options
context:
space:
mode:
Diffstat (limited to 'accounts')
-rw-r--r--accounts/gkleen@sif/default.nix27
-rw-r--r--accounts/gkleen@sif/niri/default.nix1111
-rw-r--r--accounts/gkleen@sif/niri/mako.nix8
-rw-r--r--accounts/gkleen@sif/niri/swayosd.nix65
-rw-r--r--accounts/gkleen@sif/niri/waybar.nix51
-rw-r--r--accounts/gkleen@sif/systemd.nix3
6 files changed, 776 insertions, 489 deletions
diff --git a/accounts/gkleen@sif/default.nix b/accounts/gkleen@sif/default.nix
index b47e25a4..58cfb425 100644
--- a/accounts/gkleen@sif/default.nix
+++ b/accounts/gkleen@sif/default.nix
@@ -80,7 +80,7 @@ let
80 ]; 80 ];
81 }; 81 };
82 82
83 lockCommand = "${config.systemd.package}/bin/systemctl --user start gtklock.service"; 83 lockCommand = "${lib.getExe' config.systemd.package "systemctl"} --user start gtklock.service";
84in { 84in {
85 imports = with flake.nixosModules.userProfiles.${userName}; [ 85 imports = with flake.nixosModules.userProfiles.${userName}; [
86 mpv yt-dlp (args: import ./xcompose.nix (inputs // args)) 86 mpv yt-dlp (args: import ./xcompose.nix (inputs // args))
@@ -319,6 +319,7 @@ in {
319 }; 319 };
320 device_config = [ 320 device_config = [
321 { loop_file = "/nix/store/*-etc-metadata.erofs"; is_mounted = false; ignore = true; } 321 { loop_file = "/nix/store/*-etc-metadata.erofs"; is_mounted = false; ignore = true; }
322 { mount_path = "/run/nixos-etc-metadata"; ignore = true; }
322 { mount_path = "/run/nixos-etc-metadata.*"; ignore = true; } 323 { mount_path = "/run/nixos-etc-metadata.*"; ignore = true; }
323 ]; 324 ];
324 icon_names.media = ["drive-removable-media-symbolic"]; 325 icon_names.media = ["drive-removable-media-symbolic"];
@@ -358,31 +359,17 @@ in {
358 enable = true; 359 enable = true;
359 events = [ 360 events = [
360 { event = "before-sleep"; command = lockCommand; } 361 { event = "before-sleep"; command = lockCommand; }
361 # { event = "after-resume"; command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms on"; }
362 { event = "lock"; command = lockCommand; } 362 { event = "lock"; command = lockCommand; }
363 ]; 363 ];
364 timeouts = [ 364 timeouts = [
365 # { timeout = 300;
366 # command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms off";
367 # }
368 { timeout = 330; command = lockCommand; } 365 { timeout = 330; command = lockCommand; }
369 ]; 366 ];
370 extraArgs = [ 367 extraArgs = [
368 "-w"
371 "idlehint" "30" 369 "idlehint" "30"
372 ]; 370 ];
373 }; 371 };
374 poweralertd.enable = true; 372 poweralertd.enable = true;
375 avizo = {
376 enable = true;
377 settings.default = {
378 time = "1.0";
379 background = "rgba(0, 0, 0, 0.8)";
380 border-color = "rgba(0, 0, 0, 1)";
381 bar-fg-color = "rgba(160, 160, 160, 1)";
382 bar-bg-color = "rgba(32, 32, 32, 0.96)";
383 # y-offset = "0.25";
384 };
385 };
386 }; 373 };
387 374
388 home.pointerCursor = { 375 home.pointerCursor = {
@@ -508,6 +495,10 @@ in {
508 [Unit] 495 [Unit]
509 Before=graphical-session-pre.target 496 Before=graphical-session-pre.target
510 ''; 497 '';
498 "pdfpc/pdfpcrc".text = ''
499 mouse 8 prev
500 mouse 9 next
501 '';
511 }; 502 };
512 503
513 xdg.dataFile = { 504 xdg.dataFile = {
@@ -625,7 +616,6 @@ in {
625 --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \ 616 --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \
626 --property 'Environment=DSCP=46' \ 617 --property 'Environment=DSCP=46' \
627 -- ${pkgs.dscp}/bin/dscp ${pkgs.google-chrome}/bin/google-chrome-stable \ 618 -- ${pkgs.dscp}/bin/dscp ${pkgs.google-chrome}/bin/google-chrome-stable \
628 --force-device-scale-factor=1.5 \
629 --class=Rainbow \ 619 --class=Rainbow \
630 --kiosk "https://web.openrainbow.com" \ 620 --kiosk "https://web.openrainbow.com" \
631 --user-data-dir=''${HOME}/.config/google-chrome-rainbow 621 --user-data-dir=''${HOME}/.config/google-chrome-rainbow
@@ -634,6 +624,9 @@ in {
634 url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg"; 624 url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg";
635 hash = "sha256-5fmo8rDqVDpzkGaPjk4Y+SsSZpAsY7VUQSFW6WdHwuU="; 625 hash = "sha256-5fmo8rDqVDpzkGaPjk4Y+SsSZpAsY7VUQSFW6WdHwuU=";
636 }; 626 };
627 settings = {
628 StartupWMClass = "Rainbow";
629 };
637 }; 630 };
638 }; 631 };
639 632
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix
index bf124211..50f8ba44 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,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.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 (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 ))
237 ]; 817 ];
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 }; 818 };
610} 819}
diff --git a/accounts/gkleen@sif/niri/mako.nix b/accounts/gkleen@sif/niri/mako.nix
index 8fbc81c1..0a10555a 100644
--- a/accounts/gkleen@sif/niri/mako.nix
+++ b/accounts/gkleen@sif/niri/mako.nix
@@ -30,6 +30,14 @@
30 [mode=silent] 30 [mode=silent]
31 invisible=1 31 invisible=1
32 ''; 32 '';
33 package = pkgs.symlinkJoin {
34 name = "${pkgs.mako.name}-wrapped";
35 paths = with pkgs; [ mako ];
36 inherit (pkgs.mako) meta;
37 postBuild = ''
38 rm -r $out/share/dbus-1
39 '';
40 };
33 }; 41 };
34 systemd.user.services.mako = { 42 systemd.user.services.mako = {
35 Unit = { 43 Unit = {
diff --git a/accounts/gkleen@sif/niri/swayosd.nix b/accounts/gkleen@sif/niri/swayosd.nix
new file mode 100644
index 00000000..984927c2
--- /dev/null
+++ b/accounts/gkleen@sif/niri/swayosd.nix
@@ -0,0 +1,65 @@
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
index 79c429f8..bae818f6 100644
--- a/accounts/gkleen@sif/niri/waybar.nix
+++ b/accounts/gkleen@sif/niri/waybar.nix
@@ -1,5 +1,7 @@
1{ lib, pkgs, ... }: 1{ lib, config, pkgs, ... }:
2{ 2let
3 swayosd-client = lib.getExe' config.services.swayosd.package "swayosd-client";
4in {
3 config = { 5 config = {
4 programs.waybar = { 6 programs.waybar = {
5 enable = true; 7 enable = true;
@@ -22,7 +24,7 @@
22 output = [ "eDP-1" "DP-2" "DP-3" ]; 24 output = [ "eDP-1" "DP-2" "DP-3" ];
23 modules-left = [ "niri/workspaces" ]; 25 modules-left = [ "niri/workspaces" ];
24 modules-center = [ "niri/window" ]; 26 modules-center = [ "niri/window" ];
25 modules-right = [ # "custom/worktime" "custom/worktime-today" 27 modules-right = [ "custom/worktime" "custom/worktime-today"
26 "custom/weather" 28 "custom/weather"
27 "custom/keymap" 29 "custom/keymap"
28 "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "custom/mako" "clock" ]; 30 "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "custom/mako" "clock" ];
@@ -59,7 +61,7 @@
59 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
60 if is_silent: 62 if is_silent:
61 text = f"<span color=\"#ffffff\">{text}</span>" 63 text = f"<span color=\"#ffffff\">{text}</span>"
62 print(json.dumps({'text': text}, separators=(',', ':')), flush=True) # noqa: E501 64 print(json.dumps({'text': text, 'tooltip': ', '.join(modes)}, separators=(',', ':')), flush=True) # noqa: E501
63 65
64 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
65 if "Modes" not in invalidated_properties: 67 if "Modes" not in invalidated_properties:
@@ -120,16 +122,16 @@
120 }; 122 };
121 "custom/worktime" = { 123 "custom/worktime" = {
122 interval = 60; 124 interval = 60;
123 exec = lib.getExe pkgs.worktime; 125 exec = "${lib.getExe pkgs.worktime} time --waybar";
124 tooltip = false; 126 return-type = "json";
125 }; 127 };
126 "custom/worktime-today" = { 128 "custom/worktime-today" = {
127 interval = 60; 129 interval = 60;
128 exec = "${lib.getExe pkgs.worktime} today"; 130 exec = "${lib.getExe pkgs.worktime} today --waybar";
129 tooltip = false; 131 return-type = "json";
130 }; 132 };
131 "niri/workspaces" = { 133 "niri/workspaces" = {
132 ignore = ["pwctl" "kpxc" "bmgr" "edit" "term"]; 134 ignore = map ({ name, ... }: name) config.programs.niri.scratchspaces;
133 }; 135 };
134 "niri/window" = { 136 "niri/window" = {
135 separate-outputs = true; 137 separate-outputs = true;
@@ -190,8 +192,8 @@
190 icon-size = iconSize; 192 icon-size = iconSize;
191 tooltip-format = "{percent}%"; 193 tooltip-format = "{percent}%";
192 format-icons = ["&#xf00da;" "&#xf00db;" "&#xf00dc;" "&#xf00dd;" "&#xf00de;" "&#xf00df;" "&#xf00e0;"]; 194 format-icons = ["&#xf00da;" "&#xf00db;" "&#xf00dc;" "&#xf00dd;" "&#xf00de;" "&#xf00df;" "&#xf00e0;"];
193 on-scroll-up = "lightctl -d -e4 -n1 up"; 195 on-scroll-up = "${swayosd-client} --brightness raise";
194 on-scroll-down = "lightctl -d -e4 -n1 down"; 196 on-scroll-down = "${swayosd-client} --brightness lower";
195 }; 197 };
196 wireplumber = { 198 wireplumber = {
197 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; 199 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
@@ -200,9 +202,9 @@
200 format-icons = ["&#xf057f;" "&#xf0580;" "&#xf057e;"]; 202 format-icons = ["&#xf057f;" "&#xf0580;" "&#xf057e;"];
201 format-muted = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">&#xf075f;</span>"; 203 format-muted = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">&#xf075f;</span>";
202 # ignored-sinks = ["Easy Effects Sink"]; 204 # ignored-sinks = ["Easy Effects Sink"];
203 on-scroll-up = "volumectl -d -u up"; 205 on-scroll-up = "${swayosd-client} --output-volume raise";
204 on-scroll-down = "volumectl -d -u down"; 206 on-scroll-down = "${swayosd-client} --output-volume lower";
205 on-click = "volumectl -d toggle-mute"; 207 on-click = "${swayosd-client} --output-volume mute-toggle";
206 }; 208 };
207 } 209 }
208 { 210 {
@@ -215,7 +217,7 @@
215 modules-right = [ "clock" ]; 217 modules-right = [ "clock" ];
216 218
217 "niri/workspaces" = { 219 "niri/workspaces" = {
218 ignore = ["pwctl" "kpxc" "bmgr" "edit" "term"]; 220 ignore = map ({ name, ... }: name) config.programs.niri.scratchspaces;
219 }; 221 };
220 "niri/window" = { 222 "niri/window" = {
221 separate-outputs = true; 223 separate-outputs = true;
@@ -241,7 +243,7 @@
241 243
242 * { 244 * {
243 border: none; 245 border: none;
244 font-family: "Fira Sans Nerd Font"; 246 font-family: "Fira Sans";
245 font-size: 10pt; 247 font-size: 10pt;
246 min-height: 0; 248 min-height: 0;
247 } 249 }
@@ -252,10 +254,10 @@
252 } 254 }
253 255
254 .modules-left { 256 .modules-left {
255 margin-left: 8px; 257 margin-left: 12px;
256 } 258 }
257 .modules-right { 259 .modules-right {
258 margin-right: 8px; 260 margin-right: 12px;
259 } 261 }
260 262
261 .module { 263 .module {
@@ -280,11 +282,12 @@
280 color: @grey; 282 color: @grey;
281 margin: 0 5px; 283 margin: 0 5px;
282 } 284 }
283 #custom-weather, #custom-worktime-today { 285 #custom-weather {
284 margin-right: 3px; 286 margin-right: 3px;
285 } 287 }
286 #custom-keymap, #custom-weather { 288 #custom-keymap {
287 margin-left: 3px; 289 margin-left: 3px;
290 margin-right: 3px;
288 } 291 }
289 292
290 #tray { 293 #tray {
@@ -320,6 +323,12 @@
320 #idle_inhibitor.activated { 323 #idle_inhibitor.activated {
321 color: @white; 324 color: @white;
322 } 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 }
323 332
324 #idle_inhibitor { 333 #idle_inhibitor {
325 padding-top: 1px; 334 padding-top: 1px;
@@ -327,7 +336,7 @@
327 336
328 #privacy { 337 #privacy {
329 color: @red; 338 color: @red;
330 margin: -1px 2px 0px 5px; 339 margin: -1px 4px 0px 3px;
331 } 340 }
332 #clock { 341 #clock {
333 /* margin-right: 5px; */ 342 /* margin-right: 5px; */
diff --git a/accounts/gkleen@sif/systemd.nix b/accounts/gkleen@sif/systemd.nix
index d3a1a4c7..a89b46c2 100644
--- a/accounts/gkleen@sif/systemd.nix
+++ b/accounts/gkleen@sif/systemd.nix
@@ -344,6 +344,9 @@ in {
344 Unit = { 344 Unit = {
345 PartOf = lib.mkForce ["tray.target"]; 345 PartOf = lib.mkForce ["tray.target"];
346 }; 346 };
347 Install = {
348 WantedBy = lib.mkForce ["tray.target"];
349 };
347 }; 350 };
348 } // listToAttrs (map ({host, port}: nameValuePair "proxy-to-autossh-socks@${toString port}" { 351 } // listToAttrs (map ({host, port}: nameValuePair "proxy-to-autossh-socks@${toString port}" {
349 Unit = { 352 Unit = {