summaryrefslogtreecommitdiff
path: root/accounts/gkleen@sif
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/gkleen@sif')
-rw-r--r--accounts/gkleen@sif/default.nix403
-rw-r--r--accounts/gkleen@sif/emacs.el5
-rw-r--r--accounts/gkleen@sif/firefox-chrome.css30
-rw-r--r--accounts/gkleen@sif/libvirt/default.nix1
-rw-r--r--accounts/gkleen@sif/niri/default.nix1252
-rw-r--r--accounts/gkleen@sif/niri/mako.nix117
-rw-r--r--accounts/gkleen@sif/niri/swayosd.nix66
-rw-r--r--accounts/gkleen@sif/niri/waybar.nix74
-rw-r--r--accounts/gkleen@sif/ssh-hosts.nix85
-rw-r--r--accounts/gkleen@sif/synadm/default.nix9
-rw-r--r--accounts/gkleen@sif/synadm/synadm_yaml15
-rw-r--r--accounts/gkleen@sif/systemd.nix237
-rw-r--r--accounts/gkleen@sif/utils/async-yt-dlp.nix57
-rw-r--r--accounts/gkleen@sif/utils/sieve-edit.nix24
-rw-r--r--accounts/gkleen@sif/zshrc145
15 files changed, 1742 insertions, 778 deletions
diff --git a/accounts/gkleen@sif/default.nix b/accounts/gkleen@sif/default.nix
index 7d5a9c25..706eb241 100644
--- a/accounts/gkleen@sif/default.nix
+++ b/accounts/gkleen@sif/default.nix
@@ -4,33 +4,6 @@ with lib;
4 4
5let 5let
6 cfg = config.home-manager.users.${userName}; 6 cfg = config.home-manager.users.${userName};
7 emacsScratch = pkgs.stdenv.mkDerivation (sources.emacs-scratch_el // rec {
8 phases = [ "installPhase" ];
9
10 installPhase = ''
11 mkdir -p $out/share/emacs/site-lisp
12 cp $src/scratch.el $out/share/emacs/site-lisp/default.el
13 '';
14 });
15 muteScript = pkgs.stdenv.mkDerivation {
16 name = "mute";
17 src = ./scripts/mute.zsh;
18
19 buildInputs = with pkgs; [ makeWrapper ];
20
21 phases = [ "installPhase" ];
22
23 installPhase = ''
24 mkdir -p $out/bin
25 install -m 0755 $src $out/bin/mute
26 wrapProgram $out/bin/mute \
27 --prefix PATH : ${pkgs.zsh}/bin \
28 --prefix PATH : ${pkgs.findutils}/bin \
29 --prefix PATH : ${pkgs.util-linux}/bin \
30 --prefix PATH : ${pkgs.coreutils}/bin \
31 --prefix PATH : ${pkgs.pulseaudio}/bin
32 '';
33 };
34 wrapElectron = { package, bin ? package.meta.mainProgram or package.pname or (pkgs.lib.strings.nameFromURL package.name "-"), outBin ? bin, sandbox ? true }: pkgs.symlinkJoin { 7 wrapElectron = { package, bin ? package.meta.mainProgram or package.pname or (pkgs.lib.strings.nameFromURL package.name "-"), outBin ? bin, sandbox ? true }: pkgs.symlinkJoin {
35 name = "${package.name}-wrapped"; 8 name = "${package.name}-wrapped";
36 buildInputs = with pkgs; [ makeWrapper ]; 9 buildInputs = with pkgs; [ makeWrapper ];
@@ -47,10 +20,6 @@ let
47 ''; 20 '';
48 }; 21 };
49 22
50 wrappedChrome = wrapElectron { package = pkgs.google-chrome; outBin = "google-chrome"; };
51 wrappedZulip = wrapElectron { package = pkgs.zulip; bin = "zulip"; outBin = "zulip"; };
52 wrappedElementDesktop = wrapElectron { package = pkgs.element-desktop; bin = "element-desktop"; };
53 wrappedRocketChatDesktop = wrapElectron { package = pkgs.rocketchat-desktop; bin = "rocketchat-desktop"; outBin = "rocketchat"; };
54 wrappedYTMDesktop = wrapElectron { package = pkgs.ytmdesktop; sandbox = false; }; 23 wrappedYTMDesktop = wrapElectron { package = pkgs.ytmdesktop; sandbox = false; };
55 24
56 wrappedKeepassxc = pkgs.symlinkJoin { 25 wrappedKeepassxc = pkgs.symlinkJoin {
@@ -63,7 +32,7 @@ let
63 text = '' 32 text = ''
64 [D-BUS Service] 33 [D-BUS Service]
65 Name=org.keepassxc.KeePassXC.MainWindow 34 Name=org.keepassxc.KeePassXC.MainWindow
66 Exec=${pkgs.coreutils}/bin/false 35 Exec=${lib.getExe' pkgs.coreutils "false"}
67 SystemdService=keepassxc.service 36 SystemdService=keepassxc.service
68 ''; 37 '';
69 }) 38 })
@@ -73,36 +42,46 @@ let
73 text = '' 42 text = ''
74 [D-BUS Service] 43 [D-BUS Service]
75 Name=org.freedesktop.secrets 44 Name=org.freedesktop.secrets
76 Exec=${pkgs.coreutils}/bin/false 45 Exec=${lib.getExe' pkgs.coreutils "false"}
77 SystemdService=keepassxc.service 46 SystemdService=keepassxc.service
78 ''; 47 '';
79 }) 48 })
80 ]; 49 ];
81 }; 50 };
82 51
83 lockCommand = "${config.systemd.package}/bin/systemctl --user start gtklock.service"; 52 lockCommand = "${lib.getExe' config.systemd.package "systemctl"} --user start gtklock.service";
53
54 editor = pkgs.symlinkJoin {
55 inherit (cfg.services.emacs.package) name;
56 buildInputs = with pkgs; [ makeWrapper ];
57 paths = [ cfg.services.emacs.package ];
58 postBuild = ''
59 wrapProgram $out/bin/emacsclient \
60 --inherit-argv0 \
61 --add-flags ${lib.escapeShellArg (lib.escapeShellArgs cfg.services.emacs.client.arguments)}
62 '';
63 };
84in { 64in {
85 imports = with flake.nixosModules.userProfiles.${userName}; [ 65 imports = with flake.nixosModules.userProfiles.${userName}; [
86 mpv yt-dlp (args: import ./xcompose.nix (inputs // args)) 66 zsh tmux mpv yt-dlp (args: import ./xcompose.nix (inputs // args))
87 ]; 67 ];
88 68
89 config = { 69 config = {
90 services.displayManager.defaultSession = "Hyprland"; # "none+xmonad";
91
92 home-manager.users.${userName} = { 70 home-manager.users.${userName} = {
93 imports = [ 71 imports = [
94 ./libvirt 72 ./libvirt
95 ./niri 73 ./niri
74 ./synadm
96 flakeInputs.nix-index-database.hmModules.nix-index 75 flakeInputs.nix-index-database.hmModules.nix-index
97 flakeInputs.impermanence.nixosModules.home-manager.impermanence 76 flakeInputs.impermanence.nixosModules.home-manager.impermanence
98 ]; 77 ];
99 78
100 home.stateVersion = "20.09"; 79 home.stateVersion = "20.09";
101 80
102 nixpkgs.config = { 81 # nixpkgs.config = {
103 allowUnfree = true; 82 # allowUnfree = true;
104 zathura.useMupdf = false; 83 # zathura.useMupdf = false;
105 }; 84 # };
106 85
107 nix.registry = { 86 nix.registry = {
108 "flk" = { 87 "flk" = {
@@ -112,14 +91,14 @@ in {
112 }; 91 };
113 to = { 92 to = {
114 type = "git"; 93 type = "git";
115 url = "file:///home/gkleen/config/nixos-flakes"; 94 url = "file:///home/gkleen/projects/machines";
116 }; 95 };
117 }; 96 };
118 }; 97 };
119 98
120 programs = { 99 programs = {
121 ssh = { 100 ssh = {
122 matchBlocks = import ./ssh-hosts.nix { inherit pkgs; }; # customUtils.nixImport { dir = ./ssh-hosts; }; 101 matchBlocks = import ./ssh-hosts.nix inputs; # customUtils.nixImport { dir = ./ssh-hosts; };
123 extraConfig = '' 102 extraConfig = ''
124 Match host uniworx3.ifi.lmu.de,uniworx4.ifi.lmu.de,uniworx5.ifi.lmu.de,uni2workgw.ifi.lmu.de,blackbeard.tcs.ifi.lmu.de,gitlab2.rz.ifi.lmu.de,oregon.tcs.ifi.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null" 103 Match host uniworx3.ifi.lmu.de,uniworx4.ifi.lmu.de,uniworx5.ifi.lmu.de,uni2workgw.ifi.lmu.de,blackbeard.tcs.ifi.lmu.de,gitlab2.rz.ifi.lmu.de,oregon.tcs.ifi.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null"
125 ProxyJump remote.cip.ifi.lmu.de 104 ProxyJump remote.cip.ifi.lmu.de
@@ -137,8 +116,8 @@ in {
137 ''} 116 ''}
138 117
139 Match host *.mathinst.loc,*.math.lmu.de !host ssh.math.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null" 118 Match host *.mathinst.loc,*.math.lmu.de !host ssh.math.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null"
140 # ProxyCommand ${pkgs.socat}/bin/socat - SOCKS4A:127.0.0.1:%h:%p,socksport=8118 119 ProxyCommand ${lib.getExe pkgs.socat} - SOCKS4A:127.0.0.1:%h:%p,socksport=8118
141 ProxyJump ssh.math.lmu.de 120 # ProxyJump ssh.math.lmu.de
142 121
143 Match host *.cipmath.loc !host cip04.cipmath.loc,mgmt-cls01.cipmath.loc !exec "nc -z -w 1 %h %p &>/dev/null" 122 Match host *.cipmath.loc !host cip04.cipmath.loc,mgmt-cls01.cipmath.loc !exec "nc -z -w 1 %h %p &>/dev/null"
144 ProxyJump cip04 123 ProxyJump cip04
@@ -155,22 +134,31 @@ in {
155 134
156 emacs = { 135 emacs = {
157 enable = true; 136 enable = true;
158 package = pkgs.emacs29-pgtk; 137 package = pkgs.emacs-pgtk;
159 extraPackages = epkgs: with epkgs; [ 138 extraPackages = epkgs: with epkgs; [
160 evil evil-dvorak undo-tree magit haskell-tng-mode nix-mode 139 evil evil-dvorak undo-tree magit haskell-tng-mode nix-mode
161 yaml-mode json-mode shakespeare-mode smart-mode-line 140 yaml-mode json-mode shakespeare-mode smart-mode-line
162 highlight-parentheses highlight-symbol ag sass-mode lua-mode 141 highlight-parentheses highlight-symbol ag sass-mode
163 fira-code-mode use-package wanderlust # notmuch 142 lua-mode fira-code-mode use-package wanderlust # notmuch
164 git-gutter emacsScratch 143 git-gutter scratch edit-server mediawiki editorconfig
165 edit-server mediawiki editorconfig typescript-mode 144 typescript-mode markdown-mode nftables-mode rustic
166 markdown-mode nftables-mode rustic lsp-mode lsp-ui 145 lsp-mode lsp-ui direnv company projectile
167 direnv company projectile tomorrow-night-paradise-theme 146 tomorrow-night-paradise-theme
168 treesit-grammars.with-all-grammars magit-delta scad-mode 147 treesit-grammars.with-all-grammars magit-delta scad-mode
169 ]; 148 ];
170 overrides = self: super: { 149 overrides = self: super: {
171 tomorrow-night-paradise-theme = super.trivialBuild { 150 tomorrow-night-paradise-theme = super.trivialBuild {
172 inherit (sources.tomorrow-night-paradise-theme) pname version src; 151 inherit (sources.tomorrow-night-paradise-theme) pname version src;
173 }; 152 };
153 scratch = pkgs.stdenv.mkDerivation {
154 inherit (sources.emacs-scratch_el) pname version src;
155
156 phases = [ "unpackPhase" "installPhase" ];
157
158 installPhase = ''
159 install -Dt $out/share/emacs/site-lisp scratch.el
160 '';
161 };
174 }; 162 };
175 }; 163 };
176 firefox = { 164 firefox = {
@@ -185,7 +173,12 @@ in {
185 }; 173 };
186 }; 174 };
187 175
188 zathura.enable = true; 176 zathura = {
177 enable = true;
178 options = {
179 scroll-page-aware = true;
180 };
181 };
189 imv.enable = true; 182 imv.enable = true;
190 183
191 mpv.config = { 184 mpv.config = {
@@ -194,13 +187,91 @@ in {
194 gpu-api = "vulkan"; 187 gpu-api = "vulkan";
195 }; 188 };
196 189
197 zsh.initExtra = '' 190 zsh.initContent = let
198 source ${./zshrc} 191 zshrc = pkgs.resholve.mkDerivation {
192 pname = "zshrc";
193 version = "0.0.0";
194
195 src = ./zshrc;
196
197 dontUnpack = true;
198 dontConfigure = true;
199 dontBuild = true;
200
201 installPhase = ''
202 mkdir -p $out/share
203 install "$src" $out/share/zshrc
204 '';
205
206 solutions = {
207 default = {
208 scripts = [ "share/zshrc" ];
209 interpreter = "none";
210 inputs = with pkgs; [
211 coreutils
212 rpm
213 binutils
214 squashfsTools
215 unzip
216 cfg.programs.git.package
217 magickWrapped
218 curl
219 file
220 gnutar
221 cpio
222 magic-wormhole
223 cfg.programs.zsh.package
224 fuse
225 util-linux
226 findutils
227 qrencode
228 tty-clock
229 cfg.programs.jq.package
230 eza
231 less
232 config.systemd.package
233 config.programs.ssh.package
234 gnused
235 miniserve
236 ];
237 execer = with pkgs; [
238 "cannot:${lib.getExe' rpm "rpm2cpio"}"
239 "cannot:${lib.getExe' squashfsTools "unsquashfs"}"
240 "cannot:${lib.getExe' unzip "unzip"}"
241 "cannot:${lib.getExe cfg.programs.git.package}"
242 "cannot:${lib.getExe cpio}"
243 "cannot:${lib.getExe' magic-wormhole "wormhole"}"
244 "cannot:${lib.getExe' fuse "fusermount"}"
245 "cannot:${lib.getExe less}"
246 "cannot:${lib.getExe' config.systemd.package "systemctl"}"
247 "cannot:${lib.getExe config.programs.ssh.package}"
248 ];
249 wrapper = with pkgs; [
250 "${lib.getExe' magickWrapped "magick"}:${lib.getExe' imagemagick "magick"}"
251 ];
252 fake = {
253 builtin = ["print"];
254 external = ["sudo" "umount"];
255 };
256 };
257 };
258 };
259 magickWrapped = pkgs.symlinkJoin {
260 inherit (pkgs.imagemagick) name;
261 paths = [ pkgs.imagemagick ];
262
263 buildInputs = with pkgs; [ makeWrapper ];
264 postBuild = ''
265 wrapProgram $out/bin/magick \
266 --prefix PATH : ${lib.makeBinPath (with pkgs; [ ghostscript ])}
267 '';
268 };
269 in ''
270 source ${zshrc}/share/zshrc
199 ''; 271 '';
200 zsh.dirHashes = let 272 zsh.dirHashes = let
201 flakeHashes = mapAttrs' (n: v: nameValuePair (inputNames.${n} or n) (toString v)) flakeInputs; 273 flakeHashes = mapAttrs' (n: v: nameValuePair (inputNames.${n} or n) (toString v)) flakeInputs;
202 inputNames = { 274 inputNames = {
203 "nixpkgs" = "nixos";
204 }; 275 };
205 in flakeHashes // { 276 in flakeHashes // {
206 u2w = "$HOME/projects/uni2work"; 277 u2w = "$HOME/projects/uni2work";
@@ -212,6 +283,16 @@ in {
212 pro = "$HOME/projects/pro"; 283 pro = "$HOME/projects/pro";
213 media = "$HOME/media"; 284 media = "$HOME/media";
214 }; 285 };
286 jq.colors = {
287 arrays = "1;37";
288 "false" = "0;37";
289 "null" = "2;37";
290 numbers = "0;37";
291 objectKeys = "1;34";
292 objects = "1;37";
293 strings = "0;32";
294 "true" = "0;37";
295 };
215 296
216 obs-studio = { 297 obs-studio = {
217 enable = true; 298 enable = true;
@@ -221,7 +302,7 @@ in {
221 gh = { 302 gh = {
222 enable = true; 303 enable = true;
223 settings = { 304 settings = {
224 editor = "${config.home-manager.users.${userName}.programs.emacs.package}/bin/emacsclient"; 305 editor = lib.getExe' editor "emacsclient";
225 gitProtocol = "ssh"; 306 gitProtocol = "ssh";
226 }; 307 };
227 }; 308 };
@@ -247,16 +328,10 @@ in {
247 # notify_on_cmd_finish = "invisible 120"; 328 # notify_on_cmd_finish = "invisible 120";
248 }; 329 };
249 keybindings = { 330 keybindings = {
250 "kitty_mod+n" = "detach_window"; 331 "kitty_mod+n" = "new_os_window_with_cwd";
251 "kitty_mod+m" = "detach_window ask"; 332 "kitty_mod+m" = "detach_window ask";
252 }; 333 "kitty_mod+enter" = "new_window_with_cwd";
253 }; 334 "kitty_mod+t" = "new_tab_with_cwd";
254 wpaperd = {
255 enable = true;
256 settings.default = {
257 path = "~/.wallpapers";
258 duration = "8h";
259 mode = "center";
260 }; 335 };
261 }; 336 };
262 fuzzel = { 337 fuzzel = {
@@ -269,7 +344,7 @@ in {
269 font = "Fira Sans"; 344 font = "Fira Sans";
270 }; 345 };
271 colors = { 346 colors = {
272 background = "000000aa"; 347 background = "000000cc";
273 text = "cdd6f4ff"; 348 text = "cdd6f4ff";
274 match = "94e2d5ff"; 349 match = "94e2d5ff";
275 selection = "585b70ff"; 350 selection = "585b70ff";
@@ -282,26 +357,46 @@ in {
282 }; 357 };
283 }; 358 };
284 }; 359 };
360 pandoc = {
361 enable = true;
362 extraAbbreviations = ["i.A." "d.h." "D.h." "gdw."];
363 };
364 nushell = {
365 enable = true;
366 settings.show_banner = false;
367 };
368 fd.enable = true;
285 }; 369 };
286 370
287 services = { 371 services = {
372 wpaperd = {
373 enable = true;
374 settings.default = {
375 path = "~/.wallpapers";
376 duration = "15m";
377 mode = "center";
378 };
379 };
288 emacs = { 380 emacs = {
289 enable = true; 381 enable = true;
290 socketActivation.enable = true; 382 socketActivation.enable = true;
291 client = { 383 client = {
292 enable = true; 384 enable = true;
293 arguments = mkForce ["--reuse-frame" "--alternate-editor" "\"\""]; 385 arguments = mkForce ["--create-frame" "--alternate-editor" (lib.getExe cfg.services.emacs.package)];
294 }; 386 };
295 }; 387 };
296 gpg-agent = { 388 gpg-agent = {
297 enable = true; 389 enable = true;
298 enableSshSupport = true; 390 enableSshSupport = true;
299 extraConfig = '' 391 extraConfig = ''
300 pinentry-program ${pkgs.pinentry-gtk2}/bin/pinentry 392 pinentry-program ${lib.getExe' pkgs.pinentry-gtk2 "pinentry"}
301 grab 393 grab
302 ''; 394 '';
303 }; 395 };
304 xembed-sni-proxy.enable = true; 396 xembed-sni-proxy = {
397 enable = true;
398 package = pkgs.kdePackages.plasma-workspace;
399 };
305 udiskie = { 400 udiskie = {
306 enable = true; 401 enable = true;
307 automount = false; 402 automount = false;
@@ -313,8 +408,11 @@ in {
313 device_mounted = []; 408 device_mounted = [];
314 }; 409 };
315 device_config = [ 410 device_config = [
316 { mount_path = "/run/etc-metadata"; ignore = true; } 411 { loop_file = "/nix/store/*-etc-metadata.erofs"; is_mounted = false; ignore = true; }
412 { mount_path = "/run/nixos-etc-metadata"; ignore = true; }
413 { mount_path = "/run/nixos-etc-metadata.*"; ignore = true; }
317 ]; 414 ];
415 icon_names.media = ["drive-removable-media-symbolic"];
318 }; 416 };
319 }; 417 };
320 network-manager-applet.enable = true; 418 network-manager-applet.enable = true;
@@ -331,7 +429,7 @@ in {
331 batch = "true"; 429 batch = "true";
332 log = "false"; 430 log = "false";
333 repeat = "watch"; 431 repeat = "watch";
334 sshcmd = "${pkgs.openssh}/bin/ssh"; 432 sshcmd = lib.getExe' pkgs.openssh "ssh";
335 ui = "text"; 433 ui = "text";
336 }; 434 };
337 }; 435 };
@@ -351,31 +449,17 @@ in {
351 enable = true; 449 enable = true;
352 events = [ 450 events = [
353 { event = "before-sleep"; command = lockCommand; } 451 { event = "before-sleep"; command = lockCommand; }
354 # { event = "after-resume"; command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms on"; }
355 { event = "lock"; command = lockCommand; } 452 { event = "lock"; command = lockCommand; }
356 ]; 453 ];
357 timeouts = [ 454 timeouts = [
358 # { timeout = 300; 455 { timeout = 600; command = lockCommand; }
359 # command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms off";
360 # }
361 { timeout = 330; command = lockCommand; }
362 ]; 456 ];
363 extraArgs = [ 457 extraArgs = [
458 "-w"
364 "idlehint" "30" 459 "idlehint" "30"
365 ]; 460 ];
366 }; 461 };
367 poweralertd.enable = true; 462 poweralertd.enable = true;
368 avizo = {
369 enable = true;
370 settings.default = {
371 time = "1.0";
372 background = "rgba(0, 0, 0, 0.8)";
373 border-color = "rgba(0, 0, 0, 1)";
374 bar-fg-color = "rgba(160, 160, 160, 1)";
375 bar-bg-color = "rgba(32, 32, 32, 0.96)";
376 # y-offset = "0.25";
377 };
378 };
379 }; 463 };
380 464
381 home.pointerCursor = { 465 home.pointerCursor = {
@@ -407,6 +491,13 @@ in {
407 }; 491 };
408 }; 492 };
409 493
494 qt.kde.settings = {
495 kwalletrc = {
496 KSecretD.Enabled = false;
497 Wallet."Default Wallet" = "store";
498 };
499 };
500
410 xsession.preferStatusNotifierItems = true; 501 xsession.preferStatusNotifierItems = true;
411 502
412 xresources.properties = import ./xresources.nix; 503 xresources.properties = import ./xresources.nix;
@@ -415,18 +506,19 @@ in {
415 packages = with pkgs; [ 506 packages = with pkgs; [
416 fira fira-code pwvucontrol wrappedKeepassxc wl-clipboard-rs 507 fira fira-code pwvucontrol wrappedKeepassxc wl-clipboard-rs
417 mumble pulseaudio-ctl pamixer libnotify screen-message 508 mumble pulseaudio-ctl pamixer libnotify screen-message
418 wrappedYTMDesktop libsForQt5.qt5ct playerctl evince 509 wrappedYTMDesktop libsForQt5.qt5ct playerctl evince papers
419 thunderbird zoom-us steam steam-run wireshark virt-manager 510 thunderbird zoom-us xdg-desktop-portal steam steam-run
420 rclone cached-nix-shell worktime fira-code-symbols 511 wireshark virt-manager rclone cached-nix-shell worktime
421 libreoffice xournalpp google-chrome nixos-shell virt-viewer 512 fira-code-symbols libreoffice xournalpp google-chrome
422 freerdp gnome-icon-theme paper-icon-theme sshpassSecret 513 nixos-shell virt-viewer freerdp gnome-icon-theme
423 weechat element-desktop matrix-synapse-tools.synadm 514 paper-icon-theme sshpassSecret weechat element-desktop
424 flakeInputs.deploy-rs.packages.${config.nixpkgs.system}.deploy-rs 515 sieve-connect gimp3 inkscape udiskie glab nitrokey-app
425 sieve-connect gimp inkscape udiskie glab nitrokey-app
426 pynitrokey gtklock wlrctl remmina openscad spice-record 516 pynitrokey gtklock wlrctl remmina openscad spice-record
427 libguestfs-with-appliance nerd-fonts.fira-mono 517 libguestfs-with-appliance nerd-fonts.fira-mono
428 nerd-fonts.symbols-only nerd-fonts.fira-code powerline-fonts 518 nerd-fonts.symbols-only nerd-fonts.fira-code powerline-fonts
429 ]; 519 swtpm (hunspellWithDicts (with hunspellDicts; [en_GB-large de_DE]))
520 libation libqalculate
521 ] ++ mapAttrsToList (_name: pkg: pkgs.callPackage pkg {}) (customUtils.nixImport { dir = ./utils; });
430 522
431 file = { 523 file = {
432 ".backup-munin".source = ./backup-patterns; 524 ".backup-munin".source = ./backup-patterns;
@@ -446,12 +538,9 @@ in {
446 QT_QPA_PLATFORMTHEME = "qt5ct"; 538 QT_QPA_PLATFORMTHEME = "qt5ct";
447 LIBVIRT_DEFAULT_URI = "qemu:///system"; 539 LIBVIRT_DEFAULT_URI = "qemu:///system";
448 STACK_XDG = 1; 540 STACK_XDG = 1;
449 EDITOR = pkgs.writeShellScript "editor" '' 541 EDITOR = lib.getExe' editor "emacsclient";
450 args=("--reuse-frame" "--alternate-editor" "") 542 RCLONE_PASSWORD_COMMAND = "${lib.getExe' pkgs.libsecret "secret-tool"} lookup service rclone";
451 args+=("$@") 543 SYSTEMD_TINT_BACKGROUND = "false";
452 exec -a emacsclient ${cfg.services.emacs.package}/bin/emacsclient "''${args[@]}"
453 '';
454 RCLONE_PASSWORD_COMMAND = "${pkgs.libsecret}/bin/secret-tool lookup service rclone";
455 }; 544 };
456 545
457 extraProfileCommands = '' 546 extraProfileCommands = ''
@@ -464,7 +553,7 @@ in {
464 source = ./wireplumber; 553 source = ./wireplumber;
465 recursive = true; 554 recursive = true;
466 onChange = '' 555 onChange = ''
467 ${pkgs.systemd}/bin/systemctl --user try-restart wireplumber 556 ${lib.getExe' config.systemd.package "systemctl"} --user try-restart wireplumber
468 ''; 557 '';
469 }; 558 };
470 "stack/config.yaml" = { 559 "stack/config.yaml" = {
@@ -488,37 +577,36 @@ in {
488 General = { 577 General = {
489 dot_as_separator = 0; 578 dot_as_separator = 0;
490 }; 579 };
580 Mode = {
581 calculate_as_you_type = 1;
582 };
491 }; 583 };
492 }; 584 };
493 "emacs/init.el".source = ./emacs.el; 585 "emacs/init.el".source = pkgs.substitute {
586 src = ./emacs.el;
587 substitutions = [
588 "--subst-var-by" "ksshaskpass" (lib.getExe pkgs.kdePackages.ksshaskpass)
589 ];
590 };
591 "systemd/user/xdg-desktop-portal.service.d/after-graphical-session.conf".text = ''
592 [Unit]
593 After=graphical-session.target
594 '';
595 "systemd/user/home-manager.service.d/before-graphical-session.conf".text = ''
596 [Unit]
597 Before=graphical-session-pre.target
598 '';
599 "pdfpc/pdfpcrc".text = ''
600 mouse 8 prev
601 mouse 9 next
602 '';
494 }; 603 };
495 604
496 xdg.dataFile = { 605 xdg.dataFile = {
497 "pandoc/abbreviations" = {
498 source = pkgs.runCommand "pandoc-abbreviations" {
499 buildInputs = [ pkgs.pandoc pkgs.coreutils ];
500 } (let
501 germanAbbrevs = pkgs.fetchFromGitHub {
502 owner = "jfilter";
503 repo = "german-abbreviations";
504 rev = "8eb9dae93b6f05d7c53374cd217ab2dc89558e0c";
505 sha256 = "SaD3tSqzen6Y3SPICe6/9vhe4iMHlArZ3kFQaEk7Hps=";
506 };
507 in ''
508 cat \
509 <(pandoc --print-default-data-file=abbreviations) \
510 <(grep -E '^[^ ]+\.$' ${germanAbbrevs}/german_abbreviations.txt) \
511 ${pkgs.writeText "abbrevs.txt" ''
512 i.A.
513 d.h.
514 D.h.
515 gdw.
516 ''} \
517 | sort | uniq >$out
518 '');
519 };
520 "dbus-1/services/org.keepassxc.KeePassXC.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.keepassxc.KeePassXC.service"; 606 "dbus-1/services/org.keepassxc.KeePassXC.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.keepassxc.KeePassXC.service";
521 "dbus-1/services/org.freedesktop.secrets.service.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.freedesktop.secrets.service.service"; 607 "dbus-1/services/org.freedesktop.secrets.service.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.freedesktop.secrets.service.service";
608 "dbus-1/services/org.kde.kwalletd6.service".source = "${pkgs.kdePackages.kwallet}/share/dbus-1/services/org.kde.kwalletd6.service";
609 "dbus-1/services/org.kde.kwalletd5.service".source = "${pkgs.kdePackages.kwallet}/share/dbus-1/services/org.kde.kwalletd5.service";
522 "emoji-data/list.txt".source = pkgs.stdenv.mkDerivation { 610 "emoji-data/list.txt".source = pkgs.stdenv.mkDerivation {
523 inherit (sources.emoji-data) pname src; 611 inherit (sources.emoji-data) pname src;
524 version = lib.removePrefix "v" sources.emoji-data.version; 612 version = lib.removePrefix "v" sources.emoji-data.version;
@@ -604,19 +692,68 @@ in {
604 name = "Rainbow"; 692 name = "Rainbow";
605 exec = toString (pkgs.writeShellScript "rainbow" '' 693 exec = toString (pkgs.writeShellScript "rainbow" ''
606 exec -- \ 694 exec -- \
607 ${config.systemd.package}/bin/systemd-run --wait --user --slice-inherit \ 695 ${lib.getExe' config.systemd.package "systemd-run"} --wait --user --slice-inherit \
608 --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \ 696 --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \
609 --property 'Environment=DSCP=46' \ 697 -E DSCP=46 -E NIXOS_OZONE_WL \
610 -- ${pkgs.dscp}/bin/dscp ${pkgs.google-chrome}/bin/google-chrome-stable \ 698 -- ${lib.getExe pkgs.dscp} ${lib.getExe' pkgs.google-chrome "google-chrome-stable"} \
611 --force-device-scale-factor=1.5 \
612 --class=Rainbow \ 699 --class=Rainbow \
613 --kiosk "https://web.openrainbow.com" \ 700 --app="https://web.openrainbow.com" \
614 --user-data-dir=''${HOME}/.config/google-chrome-rainbow 701 --user-data-dir=''${HOME}/.config/google-chrome-rainbow
615 ''); 702 '');
616 icon = pkgs.fetchurl { 703 icon = pkgs.fetchurl {
617 url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg"; 704 url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg";
618 hash = "sha256-5fmo8rDqVDpzkGaPjk4Y+SsSZpAsY7VUQSFW6WdHwuU="; 705 hash = "sha256-5fmo8rDqVDpzkGaPjk4Y+SsSZpAsY7VUQSFW6WdHwuU=";
619 }; 706 };
707 settings = {
708 StartupWMClass = "Rainbow";
709 };
710 };
711 kimai = {
712 name = "Kimai";
713 exec = toString (pkgs.writeShellScript "kimai" ''
714 exec -- \
715 ${lib.getExe' pkgs.google-chrome "google-chrome-stable"} \
716 --class=Kimai \
717 --app="https://kimai.yggdrasil.li" \
718 --user-data-dir=''${HOME}/.config/google-chrome-kimai
719 '');
720 icon = pkgs.fetchurl {
721 url = "https://www.kimai.org/images/kimai_logo.png";
722 hash = "sha256-lnlOttzR2SwXA70R+egJUkeKr4U5V0avqTk8uX4bqfs=";
723 };
724 settings = {
725 StartupWMClass = "Kimai";
726 StartupNotify = "true";
727 };
728 };
729 audiobookshelf = {
730 name = "Audiobookshelf";
731 exec = toString (pkgs.writeShellScript "audiobookshelf" ''
732 exec -- \
733 ${lib.getExe' pkgs.google-chrome "google-chrome-stable"} \
734 --class=Audiobookshelf \
735 --app="https://audiobookshelf.yggdrasil.li" \
736 --user-data-dir=''${HOME}/.config/google-chrome-audiobookshelf
737 '');
738 icon = pkgs.fetchurl {
739 url = "https://www.audiobookshelf.org/Logo.png";
740 hash = "sha256-JGPk+WNT1C4DC4lSMb0K0YmAMT5LvmSOeO0QRzkc7Lk=";
741 };
742 settings = {
743 StartupWMClass = "Audiobookshelf";
744 StartupNotify = "true";
745 };
746 };
747 thunderbird-lmu = {
748 name = "Thunderbird (LMU)";
749 exec = "thunderbird --name thunderbird -P lmu %U";
750 icon = "thunderbird";
751 genericName = "Email Client";
752 categories = [ "Network" "Chat" "Email" "Feed" "GTK" "News" ];
753 settings = {
754 StartupWMClass = "thunderbird";
755 StartupNotify = "true";
756 };
620 }; 757 };
621 }; 758 };
622 759
diff --git a/accounts/gkleen@sif/emacs.el b/accounts/gkleen@sif/emacs.el
index 183cb322..3beefba6 100644
--- a/accounts/gkleen@sif/emacs.el
+++ b/accounts/gkleen@sif/emacs.el
@@ -51,7 +51,7 @@
51 51
52;; (require 'scratch) 52;; (require 'scratch)
53(global-set-key (kbd "C-x B") 'scratch-create) 53(global-set-key (kbd "C-x B") 'scratch-create)
54(setq initial-major-mode 'scratch-mode) 54;; (setq initial-major-mode 'scratch-mode)
55(setq initial-scratch-message "") 55(setq initial-scratch-message "")
56 56
57(global-set-key (kbd "C-x K") 'kill-current-buffer) 57(global-set-key (kbd "C-x K") 'kill-current-buffer)
@@ -228,6 +228,7 @@ necessarily running."
228 (global-set-key (kbd "C-x k") 'kill-buffer-with-special-emacsclient-handling)) 228 (global-set-key (kbd "C-x k") 'kill-buffer-with-special-emacsclient-handling))
229 229
230(add-hook 'server-switch-hook 'install-emacsclient-wrapped-kill-buffer) 230(add-hook 'server-switch-hook 'install-emacsclient-wrapped-kill-buffer)
231(add-hook 'server-switch-hook #'raise-frame)
231 232
232(defun move-file (new-location) 233(defun move-file (new-location)
233 "Write this file to NEW-LOCATION, and delete the old one." 234 "Write this file to NEW-LOCATION, and delete the old one."
@@ -253,3 +254,5 @@ necessarily running."
253(bind-key "C-x C-m" #'move-file) 254(bind-key "C-x C-m" #'move-file)
254 255
255(let ((ssh_auth_sock (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))) (setenv "SSH_AUTH_SOCK" ssh_auth_sock)) 256(let ((ssh_auth_sock (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))) (setenv "SSH_AUTH_SOCK" ssh_auth_sock))
257(setenv "SSH_ASKPASS_REQUIRE" "prefer")
258(setenv "SSH_ASKPASS" "@ksshaskpass@")
diff --git a/accounts/gkleen@sif/firefox-chrome.css b/accounts/gkleen@sif/firefox-chrome.css
index 8900e2b9..726f1e4b 100644
--- a/accounts/gkleen@sif/firefox-chrome.css
+++ b/accounts/gkleen@sif/firefox-chrome.css
@@ -4,6 +4,21 @@
4 font-size:12px; 4 font-size:12px;
5} 5}
6 6
7#sidebar-main:has([expanded]) {
8 min-width:20em !important;
9 max-width:20em !important;
10}
11
12#sidebar, #sidebar-box {
13 min-width:35em !important;
14 max-width:35em !important;
15}
16
17#sidebar-box {
18 margin-right: var(--space-small);
19}
20
21/*
7#sidebar { 22#sidebar {
8 min-width:20em !important; 23 min-width:20em !important;
9 max-width:20em !important; 24 max-width:20em !important;
@@ -19,8 +34,21 @@
19} 34}
20 35
21#toolbar-menubar[inactive="true"] + #TabsToolbar { 36#toolbar-menubar[inactive="true"] + #TabsToolbar {
22 visibility: collapse !important; 37 visibility: collapse !important;
23} 38}
24 39
25#sidebar-box[sidebarcommand="tabcenter-reborn_ariasuni-sidebar-action"] #sidebar-header { visibility: collapse !important; } 40#sidebar-box[sidebarcommand="tabcenter-reborn_ariasuni-sidebar-action"] #sidebar-header { visibility: collapse !important; }
26#sidebar-box[sidebarcommand="_3c078156-979c-498b-8990-85f7987dd929_-sidebar-action"] #sidebar-header { visibility: collapse !important; } 41#sidebar-box[sidebarcommand="_3c078156-979c-498b-8990-85f7987dd929_-sidebar-action"] #sidebar-header { visibility: collapse !important; }
42*/
43
44.titlebar-buttonbox-container{ display: none; }
45#vertical-spacer { display: none; }
46#tabbrowser-tabs[orient="vertical"] .tab-background {
47 border-radius: var(--border-radius-small) !important;
48}
49hbox:has(> #tabs-newtab-button) {
50 display: none;
51}
52#sidebar-main .tools-and-extensions {
53 justify-content: space-around !important;
54}
diff --git a/accounts/gkleen@sif/libvirt/default.nix b/accounts/gkleen@sif/libvirt/default.nix
index 70ac22b9..4e5a9b90 100644
--- a/accounts/gkleen@sif/libvirt/default.nix
+++ b/accounts/gkleen@sif/libvirt/default.nix
@@ -7,6 +7,7 @@ with flakeInputs.nixVirt.lib;
7 config = { 7 config = {
8 virtualisation.libvirt = { 8 virtualisation.libvirt = {
9 enable = true; 9 enable = true;
10 swtpm.enable = true;
10 connections."qemu:///session" = { 11 connections."qemu:///session" = {
11 domains = [ 12 domains = [
12 { definition = domain.writeXML (updateManyAttrsByPath [ 13 { definition = domain.writeXML (updateManyAttrsByPath [
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix
index 0c4a58e5..8752f3e3 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";
@@ -19,17 +22,25 @@ let
19 22
20 workspaces_json="$(niri msg -j workspaces)" 23 workspaces_json="$(niri msg -j workspaces)"
21 workspace_output="$(jq -r --arg workspace_name "$workspace_name" '.[] | select(.name == $workspace_name) | .output' <<<"$workspaces_json")" 24 workspace_output="$(jq -r --arg workspace_name "$workspace_name" '.[] | select(.name == $workspace_name) | .output' <<<"$workspaces_json")"
22 active_workspace="$(jq -r --arg workspace_output "$workspace_output" '.[] | select(.output == $workspace_output and .is_active) | .id' <<<"$workspaces_json")" 25 # active_workspace="$(jq -r --arg workspace_output "$workspace_output" '.[] | select(.output == $workspace_output and .is_active) | .id' <<<"$workspaces_json")"
23 active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" 26 active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")"
24 if [[ $workspace_output != "$active_output" ]]; then 27 if [[ $workspace_output != "$active_output" ]]; then
25 niri msg action move-workspace-to-monitor --output "$active_output" "$workspace_name" 28 niri msg action move-workspace-to-monitor --reference "$workspace_name" "$active_output"
26 socat STDIO "$NIRI_SOCKET" <<<'{"Action":{"FocusWorkspace":{"reference":{"Id":'"''${active_workspace}"'}}}}' 29 # socat STDIO "$NIRI_SOCKET" <<<'{"Action":{"FocusWorkspace":{"reference":{"Id":'"''${active_workspace}"'}}}}'
27 niri msg action move-workspace-to-index --index 1 "$workspace_name" 30 # niri msg action move-workspace-to-index --reference "$workspace_name" 1
28 fi 31 fi
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 if [[ $(jq -r --arg workspace_name "$workspace_name" 'map(select(.name == $workspace_name)) | .[0].is_focused' <<<"$workspaces_json") != "true" ]] && [[ $(jq -r --arg workspace_name "$workspace_name" 'map(select(.name == $workspace_name)) | .[0].id' <<<"$workspaces_json") = $(jq -r '.workspace_id' <<<"$window_json") ]]; then
39 niri msg action focus-workspace "$workspace_name"
40 else
41 niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")"
42 fi
43 fi
33 exit 0 44 exit 0
34 fi 45 fi
35 done < <(niri msg -j windows | jq -c '.[]') 46 done < <(niri msg -j windows | jq -c '.[]')
@@ -38,7 +49,6 @@ let
38 ''; 49 '';
39 }; 50 };
40 focus-or-spawn-action = config.lib.niri.actions.spawn (lib.getExe focus_or_spawn); 51 focus-or-spawn-action = config.lib.niri.actions.spawn (lib.getExe focus_or_spawn);
41 focus-or-spawn-action-app_id = app_id: focus-or-spawn-action ''select(.app_id == "${app_id}")'';
42 52
43 with_adjacent_workspace = pkgs.writeShellApplication { 53 with_adjacent_workspace = pkgs.writeShellApplication {
44 name = "with-adjacent-workspace"; 54 name = "with-adjacent-workspace";
@@ -75,9 +85,9 @@ let
75 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET" 85 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET"
76 ''; 86 '';
77 }; 87 };
78 with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^pwctl|kpxc|bmgr|edit|term$"; 88 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}}}}''; 89 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}}}}''; 90 move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}'';
81 91
82 with_unnamed_workspace = pkgs.writeShellApplication { 92 with_unnamed_workspace = pkgs.writeShellApplication {
83 name = "with-unnamed-workspace"; 93 name = "with-unnamed-workspace";
@@ -90,15 +100,31 @@ let
90 active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")" 100 active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")"
91 active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")" 101 active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")"
92 102
93 workspace_json="$(jq -c --arg active_output "$active_output" 'map(select(.output == $active_output and .name == null)) | sort_by(.idx) | .[0]' <<<"$workspaces_json")" 103 history_json="$(socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/niri-workspace-history.sock)"
104 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 105 [[ -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" 106 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET"
96 ''; 107 '';
97 }; 108 };
98 with-unnamed-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_unnamed_workspace); 109 with-unnamed-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_unnamed_workspace);
99 110
111 with_empty_unnamed_workspace = pkgs.writeShellApplication {
112 name = "with-empty-unnamed-workspace";
113 runtimeInputs = [ niri pkgs.gojq pkgs.socat ];
114 text = ''
115 action="$1"
116 shift
117
118 workspaces_json="$(niri msg -j workspaces)"
119 active_output="$(jq '.[] | select(.is_focused) | .output' <<<"$workspaces_json")"
120 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")"
121 jq --argjson workspace_id "$target_workspace_id" -nc "$action" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET"
122 '';
123 };
124 with-empty-unnamed-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_empty_unnamed_workspace);
125
100 with_select_window = pkgs.writeShellApplication { 126 with_select_window = pkgs.writeShellApplication {
101 name = "with-unnamed-workspace"; 127 name = "with-select-window";
102 runtimeInputs = [ niri pkgs.gojq pkgs.socat config.programs.fuzzel.package pkgs.gawk ]; 128 runtimeInputs = [ niri pkgs.gojq pkgs.socat config.programs.fuzzel.package pkgs.gawk ];
103 text = '' 129 text = ''
104 window_select="$1" 130 window_select="$1"
@@ -108,9 +134,9 @@ let
108 134
109 windows_json="$(niri msg -j windows)" 135 windows_json="$(niri msg -j windows)"
110 active_workspace="$(jq -r '.[] | select(.is_focused) | .workspace_id' <<<"$windows_json")" 136 active_workspace="$(jq -r '.[] | select(.is_focused) | .workspace_id' <<<"$windows_json")"
111 window="$(gojq -r --arg active_workspace "$active_workspace" '.[] | select('"$window_select"') | "\(.title)\t\(.id)"' <<<"$windows_json" | fuzzel --log-level=warning --dmenu)" 137 window_ix="$(gojq -r --arg active_workspace "$active_workspace" '.[] | select('"$window_select"') | "\(.title)\u0000icon\u001f\(.app_id)"' <<<"$windows_json" | fuzzel --width=60 --log-level=warning --dmenu --index)"
112 window_id="$(awk -F $'\t' '{print $2}' <<<"$window")" 138 # shellcheck disable=SC2016
113 window_json="$(jq -r --arg window_id "$window_id" '.[] | select(.id == ($window_id | tonumber))' <<<"$windows_json")" 139 window_json="$(gojq -rc --arg active_workspace "$active_workspace" --arg window_ix "$window_ix" 'map(select('"$window_select"')) | .[($window_ix | tonumber)]' <<<"$windows_json")"
114 140
115 [[ -z "$window_json" ]] && exit 1 141 [[ -z "$window_json" ]] && exit 1
116 142
@@ -118,12 +144,91 @@ let
118 ''; 144 '';
119 }; 145 };
120 with-select-window-action = config.lib.niri.actions.spawn (lib.getExe with_select_window); 146 with-select-window-action = config.lib.niri.actions.spawn (lib.getExe with_select_window);
147
148 with_predicate_window = pred: pkgs.writeShellApplication {
149 name = "with-predicate-window";
150 runtimeInputs = [ niri pkgs.gojq pkgs.socat ];
151 text = ''
152 action="$1"
153 shift
154
155 windows_json="$(niri msg -j windows)"
156 window_json="$(gojq -rc 'map(select(${pred})) | .[0]' <<<"$windows_json")"
157
158 [[ -z "$window_json" || $window_json = "null" ]] && exit 1
159
160 jq -c "$action" <<<"$window_json" | socat STDIO "$NIRI_SOCKET"
161 '';
162 };
163
164 with-urgent-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_urgent"));
165 with-focused-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_focused"));
121in { 166in {
122 imports = [ 167 imports = [
123 ./waybar.nix 168 ./waybar.nix
124 ./mako.nix 169 ./mako.nix
170 ./swayosd.nix
125 ]; 171 ];
126 172
173 options = {
174 programs.niri.scratchspaces = lib.mkOption {
175 type = lib.types.listOf (lib.types.submodule ({ config, ... }: {
176 options = {
177 name = lib.mkOption {
178 type = lib.types.str;
179 };
180 match = lib.mkOption {
181 type = lib.types.listOf (lib.types.attrsOf kdl.types.kdl-args);
182 default = [];
183 };
184 exclude = lib.mkOption {
185 type = lib.types.listOf (lib.types.attrsOf kdl.types.kdl-args);
186 default = [];
187 };
188 windowRuleExtra = lib.mkOption {
189 type = kdl.types.kdl-nodes;
190 default = [];
191 };
192 key = lib.mkOption {
193 type = lib.types.nullOr lib.types.str;
194 default = null;
195 };
196 moveKey = lib.mkOption {
197 type = lib.types.nullOr lib.types.str;
198 default = let
199 keys = lib.splitString "+" config.key;
200 defMoveKey = lib.concatStringsSep "+" (lib.flatten [
201 (lib.take (lib.length keys - 1) keys)
202 ["Shift"]
203 (lib.takeEnd 1 keys)
204 ]);
205 in if config.key == null then null else defMoveKey;
206 };
207 spawn = lib.mkOption {
208 type = lib.types.nullOr (lib.types.listOf lib.types.str);
209 default = null;
210 };
211 app-id = lib.mkOption {
212 type = lib.types.nullOr lib.types.str;
213 default = null;
214 };
215 selector = lib.mkOption {
216 type = lib.types.nullOr lib.types.str;
217 default = null;
218 };
219 };
220
221 config = lib.mkMerge [
222 (lib.mkIf (config.app-id != null) {
223 match = lib.mkDefault [ { app-id = "^${lib.escapeRegex config.app-id}$"; } ];
224 selector = lib.mkDefault "select(.app_id == \"${config.app-id}\")";
225 })
226 ];
227 }));
228 default = [];
229 };
230 };
231
127 config = { 232 config = {
128 systemd.user.services.xwayland-satellite = { 233 systemd.user.services.xwayland-satellite = {
129 Unit = { 234 Unit = {
@@ -150,387 +255,760 @@ in {
150 { event = "after-resume"; command = "${lib.getExe niri} msg action power-on-monitors"; } 255 { event = "after-resume"; command = "${lib.getExe niri} msg action power-on-monitors"; }
151 ]; 256 ];
152 timeouts = [ 257 timeouts = [
153 { timeout = 300; 258 { timeout = 540;
154 command = "${lib.getExe niri} msg action power-off-monitors"; 259 command = "${lib.getExe niri} msg action power-off-monitors";
155 } 260 }
156 ]; 261 ];
157 }; 262 };
158 263
159 programs.niri.settings = { 264 systemd.user.sockets.niri-workspace-history = {
160 prefer-no-csd = true; 265 Socket = {
161 screenshot-path = "${config.home.homeDirectory}/screenshots"; 266 ListenStream = "%t/niri-workspace-history.sock";
162 267 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 }; 268 };
176 269 };
177 outputs = { 270 systemd.user.services.niri-workspace-history = {
178 "eDP-1" = { 271 Unit = {
179 scale = 1.5; 272 BindsTo = [ "niri.service" ];
180 position = { x = 0; y = 0; }; 273 After = [ "niri.service" ];
181 };
182 "Ancor Communications Inc ASUS PB287Q 0x0000DD9B" = {
183 scale = 1.5;
184 position = { x = 2560; y = 0; };
185 };
186 }; 274 };
187 275 Install = {
188 environment = { 276 WantedBy = [ "niri.service" ];
189 NIXOS_OZONE_WL = "1";
190 QT_QPA_PLATFORM = "wayland";
191 GDK_BACKEND = "wayland";
192 SDL_VIDEODRIVER = "wayland";
193 }; 277 };
194 278 Service = {
195 layout = { 279 Type = "simple";
196 gaps = 8; 280 Sockets = [ "niri-workspace-history.socket" ];
197 struts = { left = 0; right = 0; top = 0; bottom = 0; }; 281 ExecStart = pkgs.writers.writePython3 "niri-workspace-history" { flakeIgnore = ["E501"]; } ''
198 focus-ring = { 282 import os
199 width = 2; 283 import socket
200 active.gradient = { 284 import json
201 from = "hsla(195 100% 60% 0.75)"; 285 # import sys
202 to = "hsla(155 100% 50% 0.75)"; 286 from collections import defaultdict
203 angle = 29; 287 from threading import Thread, Lock
204 relative-to = "workspace-view"; 288 from socketserver import StreamRequestHandler, ThreadingTCPServer
205 }; 289 from contextlib import contextmanager
206 inactive.gradient = { 290 from io import TextIOWrapper
207 from = "hsla(0 0% 42% 0.66)"; 291
208 to = "hsla(0 0% 35% 0.66)"; 292
209 angle = 29; 293 @contextmanager
210 relative-to = "workspace-view"; 294 def detaching(thing):
211 }; 295 try:
212 }; 296 yield thing
213 297 finally:
214 preset-column-widths = [ 298 thing.detach()
215 { proportion = 1. / 4.; } 299
216 { proportion = 1. / 3.; } 300
217 { proportion = 1. / 2.; } 301 workspace_history = defaultdict(list)
218 { proportion = 2. / 3.; } 302 history_lock = Lock()
219 { proportion = 3. / 4.; } 303
220 ]; 304
221 default-column-width.proportion = 1. / 2.; 305 def monitor_niri():
222 preset-window-heights = [ 306 workspaces = list()
223 { proportion = 1. / 3.; } 307
224 { proportion = 1. / 2.; } 308 def focus_workspace(output, workspace):
225 { proportion = 2. / 3.; } 309 with history_lock:
226 { proportion = 1.; } 310 workspace_history[output] = [workspace] + [ws for ws in workspace_history[output] if ws != workspace]
227 ]; 311 # print(json.dumps(workspace_history), file=sys.stderr)
228 312
229 always-center-single-column = true; 313 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
314 sock.connect(os.environ["NIRI_SOCKET"])
315 sock.send(b"\"EventStream\"\n")
316 for line in sock.makefile(buffering=1, encoding='utf-8'):
317 if line_json := json.loads(line):
318 if "WorkspacesChanged" in line_json:
319 workspaces = line_json["WorkspacesChanged"]["workspaces"]
320 for ws in workspaces:
321 if ws["is_focused"]:
322 focus_workspace(ws["output"], ws["id"])
323 if "WorkspaceActivated" in line_json:
324 for ws in workspaces:
325 if ws["id"] != line_json["WorkspaceActivated"]["id"]:
326 continue
327 focus_workspace(ws["output"], ws["id"])
328 break
329
330
331 class RequestHandler(StreamRequestHandler):
332 def handle(self):
333 with detaching(TextIOWrapper(self.wfile, encoding='utf-8', write_through=True)) as out:
334 with history_lock:
335 json.dump(workspace_history, out)
336
337
338 class Server(ThreadingTCPServer):
339 def __init__(self):
340 ThreadingTCPServer.__init__(self, ("", 8000), RequestHandler, bind_and_activate=False)
341 self.socket = socket.fromfd(3, self.address_family, self.socket_type)
342
343
344 def run_server():
345 with Server() as server:
346 server.serve_forever()
347
348
349 niri = Thread(target=monitor_niri)
350 niri.daemon = True
351 niri.start()
352
353 server_thread = Thread(target=run_server)
354 server_thread.daemon = True
355 server_thread.start()
356
357 while True:
358 server_thread.join(timeout=0.5)
359 niri.join(timeout=0.5)
360
361 if not (niri.is_alive() and server_thread.is_alive()):
362 break
363 '';
230 }; 364 };
231 365 };
232 cursor.hide-when-typing = true; 366 systemd.user.services.niri-workspace-sort = {
233 367 Unit = {
234 workspaces = { 368 BindsTo = [ "niri.service" ];
235 "001" = { name = "pwctl"; open-on-output = "eDP-1"; }; 369 After = [ "niri.service" ];
236 "002" = { name = "kpxc"; open-on-output = "eDP-1"; };
237 "003" = { name = "bmgr"; open-on-output = "eDP-1"; };
238 "004" = { name = "term"; open-on-output = "eDP-1"; };
239 "005" = { name = "edit"; open-on-output = "eDP-1"; };
240 "101".name = "comm";
241 "102".name = "web";
242 # "104".name = "read";
243 # "105".name = "mon";
244 "110".name = "vid";
245 }; 370 };
246 371 Install = {
247 window-rules = [ 372 WantedBy = [ "niri.service" ];
248 # { 373 };
249 # geometry-corner-radius = 374 Service = {
250 # let 375 Type = "simple";
251 # allCorners = r: { bottom-left = r; bottom-right = r; top-left = r; top-right = r; }; 376 ExecStart = pkgs.writers.writePython3 "niri-workspace-sort" { flakeIgnore = ["E501"]; } ''
252 # in allCorners 4.; 377 import os
253 # clip-to-geometry = true; 378 import sys
254 # } 379 import socket
255 { 380 import json
256 matches = [ { app-id = "^com\.saivert\.pwvucontrol$"; } ]; 381
257 open-on-workspace = "pwctl"; 382 outputs = None
258 } 383 only = {'HDMI-A-1': {'bmr'}, 'eDP-1': {'vid'}}
259 { 384
260 matches = [ { app-id = "^\.blueman-manager-wrapped$"; } ]; 385
261 open-on-workspace = "bmgr"; 386 class Niri(socket.socket):
262 } 387 def __init__(self):
263 { 388 super().__init__(socket.AF_UNIX, socket.SOCK_STREAM)
264 matches = [ { app-id = "^org\.keepassxc\.KeePassXC$"; } ]; 389 super().connect(os.environ["NIRI_SOCKET"])
265 excludes = [ 390 self.fh = super().makefile(mode='rw', buffering=1, encoding='utf-8')
266 { title = "^Unlock Database.*"; } 391
267 { title = "^Access Request.*"; } 392 def cmd(self, obj):
268 { title = ".*Passkey credentials$"; } 393 print(json.dumps(obj, separators=(',', ':')), flush=True, file=self.fh)
269 ]; 394
270 open-on-workspace = "kpxc"; 395 def event_stream(self):
271 open-focused = false; 396 self.cmd("EventStream")
272 } 397 return self.fh
273 { 398
274 matches = [ 399
275 { app-id = "^org\.keepassxc\.KeePassXC$"; title = "^Unlock Database.*"; } 400 with Niri() as niri, Niri().event_stream() as niri_stream:
276 { app-id = "^org\.keepassxc\.KeePassXC$"; title = "^Access Request.*"; } 401 for line in niri_stream:
277 { app-id = "^org\.keepassxc\.KeePassXC$"; title = ".*Passkey credentials$"; } 402 workspaces = None
278 ]; 403 if line_json := json.loads(line):
279 open-focused = true; 404 if "WorkspacesChanged" in line_json:
280 } 405 workspaces = line_json["WorkspacesChanged"]["workspaces"]
281 { 406
282 matches = [ { app-id = "^kitty-scratch$"; } ]; 407 if workspaces is None:
283 open-on-workspace = "term"; 408 continue
284 } 409
285 { 410 old_outputs = outputs
286 matches = [ { title = "^scratch$"; app-id = "^emacs$"; } ]; 411 outputs = {ws["output"] for ws in workspaces}
287 open-on-workspace = "edit"; 412 if old_outputs is None:
288 } 413 print("Initial outputs: {}".format(outputs), file=sys.stderr)
289 { 414 continue
290 matches = [ 415
291 { app-id = "^thunderbird$"; } 416 new_outputs = outputs - old_outputs
292 { app-id = "^Element$"; } 417 if not new_outputs:
293 ]; 418 continue
294 open-on-workspace = "comm"; 419 print("New outputs: {}".format(new_outputs), file=sys.stderr)
295 } 420
296 { 421 relevant_workspaces = list(filter(lambda ws: (ws["name"] is not None) or (ws["active_window_id"] is not None), workspaces))
297 matches = [ { app-id = "^firefox$"; } ]; 422 target_output = next(iter(outputs - set(only.keys())))
298 open-on-workspace = "web"; 423 if not target_output:
299 } 424 continue
300 # { 425 for ws in relevant_workspaces:
301 # matches = [ 426 ws_ident = ws["name"] if ws["name"] is not None else (ws["output"], ws["idx"])
302 # { app-id = "^evince$"; } 427 if ws["output"] not in set(only.keys()):
303 # { app-id = "^imv$"; } 428 continue
304 # { app-id = "^org\.pwmt\.zathura$"; } 429 if ws_ident in only[ws["output"]]:
305 # ]; 430 continue
306 # open-on-workspace = "read"; 431
307 # } 432 print("{} -> {}".format(ws_ident, target_output), file=sys.stderr)
308 { 433 niri.cmd({"Action": {"MoveWorkspaceToMonitor": {"reference": {"Id": ws["id"]}, "output": target_output}}})
309 matches = [ { app-id = "^mpv$"; } ]; 434 '';
310 open-on-workspace = "vid"; 435 Restart = "on-failure";
311 default-column-width.proportion = 1.; 436 RestartSec = 10;
312 }
313 {
314 matches = [ { app-id = "^kitty-play$"; } ];
315 open-on-workspace = "vid";
316 default-column-width.proportion = 1. / 3.;
317 open-focused = false;
318 }
319 # {
320 # matches = [
321 # { app-id = "^qemu$"; }
322 # { app-id = "^virt-manager$"; }
323 # ];
324 # open-on-workspace = "mon";
325 # }
326 ];
327
328 binds = with config.lib.niri.actions; {
329 "Mod+Slash".action = show-hotkey-overlay;
330
331 "Mod+Return".action = spawn terminal;
332 "Mod+Q".action = close-window;
333 "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package);
334 "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path";
335
336 "Mod+Alt+E".action = spawn (lib.getExe' config.services.emacs.package "emacsclient") "-c";
337 "Mod+Alt+Y".action = spawn (lib.getExe (pkgs.writeShellApplication {
338 name = "queue-yt-dlp";
339 runtimeInputs = with pkgs; [ wl-clipboard-rs socat ];
340 text = ''
341 socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/yt-dlp.sock <<<$'{ "urls": ["'"$(wl-paste)"$'"] }'
342 '';
343 }));
344 "Mod+Alt+L".action = spawn (lib.getExe (pkgs.writeShellApplication {
345 name = "queue-yt-dlp";
346 runtimeInputs = with pkgs; [ wl-clipboard-rs config.programs.kitty.package ];
347 text = ''
348 exec -- kitty --app-id kitty-play --directory "$HOME"/media mpv "$(wl-paste)"
349 '';
350 }));
351
352 "Mod+U".action = spawn (lib.getExe (pkgs.writeShellApplication {
353 name = "qalc-fuzzel";
354 runtimeInputs = with pkgs; [ wl-clipboard-rs libqalculate config.programs.fuzzel.package coreutils findutils libnotify gnugrep ];
355 text = ''
356 RESULTS_DIR="$HOME/.cache/qalc-fuzzel"
357 prev() {
358 FOUND=false
359 while IFS= read -r line; do
360 [[ -n "$line" ]] || continue
361 FOUND=true
362 echo "$line"
363 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)
364 $FOUND || echo
365 }
366 FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> ") || exit $?
367 if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then
368 QALC_RES="$FUZZEL_RES"
369 QALC_RET=0
370 else
371 QALC_RES=$(qalc "$FUZZEL_RES" 2>&1)
372 QALC_RET=$?
373 fi
374 [[ -n "$QALC_RES" ]] || exit 1
375 EXISTING=false
376 set +e
377 grep -Fxrl "$QALC_RES" "$RESULTS_DIR" | xargs -r touch
378 [[ ''${PIPESTATUS[0]} -eq 0 ]] && EXISTING=true
379 set -e
380 if [[ $QALC_RET -eq 0 ]] && ! $EXISTING; then
381 RES_FILE="$RESULTS_DIR"/$(date -uIs).$(tr -Cd 'a-zA-Z0-9' </dev/random | head -c 10)
382 cat >"$RES_FILE" <<<"$QALC_RES"
383 fi
384 [[ "$QALC_RES" =~ .*\ =\ (.*) ]] && QALC_RES="''${BASH_REMATCH[1]}"
385 [[ $QALC_RET -eq 0 ]] && wl-copy "$QALC_RES"
386 notify-send "$QALC_RES"
387 '';
388 }));
389 "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication {
390 name = "emoji-fuzzel";
391 runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ];
392 text = ''
393 FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " <"$HOME"/.local/share/emoji-data/list.txt) || exit $?
394 [[ -n "$FUZZEL_RES" ]] || exit 1
395 wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste
396 '';
397 }));
398 "Print".action = spawn (lib.getExe (pkgs.writeShellApplication {
399 name = "screenshot";
400 runtimeInputs = with pkgs; [ grim slurp wl-clipboard-rs coreutils ];
401 text = ''
402 grim -g "$(slurp -b 00000080 -c FFFFFFFF -s 00000000 -w 1)" - \
403 | tee "$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" \
404 | wl-copy --type image/png
405 '';
406 }));
407 "Shift+Print".action = spawn (lib.getExe (pkgs.writeShellApplication {
408 name = "screenshot";
409 runtimeInputs = with pkgs; [ grim niri gojq wl-clipboard-rs coreutils ];
410 text = ''
411 grim -o "$(niri msg -j workspaces | jq -r '.[] | select(.is_focused) | .output')" - \
412 | tee "$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" \
413 | wl-copy --type image/png
414 '';
415 }));
416 "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
417 "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
418
419 "Mod+H".action = focus-column-left;
420 "Mod+T".action = focus-window-down;
421 "Mod+N".action = focus-window-up;
422 "Mod+S".action = focus-column-right;
423
424 "Mod+Shift+H".action = move-column-left;
425 "Mod+Shift+T".action = move-window-down;
426 "Mod+Shift+N".action = move-window-up;
427 "Mod+Shift+S".action = move-column-right;
428
429 "Mod+Control+H".action = focus-monitor-left;
430 "Mod+Control+T".action = focus-monitor-down;
431 "Mod+Control+N".action = focus-monitor-up;
432 "Mod+Control+S".action = focus-monitor-right;
433
434 "Mod+Shift+Control+H".action = move-workspace-to-monitor-left;
435 "Mod+Shift+Control+T".action = move-workspace-to-monitor-down;
436 "Mod+Shift+Control+N".action = move-workspace-to-monitor-up;
437 "Mod+Shift+Control+S".action = move-workspace-to-monitor-right;
438
439 "Mod+G".action = focus-adjacent-workspace "down";
440 "Mod+C".action = focus-adjacent-workspace "up";
441
442 "Mod+Shift+G".action = move-column-to-adjacent-workspace "down";
443 "Mod+Shift+C".action = move-column-to-adjacent-workspace "up";
444
445 "Mod+Shift+Control+G".action = move-workspace-down;
446 "Mod+Shift+Control+C".action = move-workspace-up;
447
448 "Mod+ParenLeft".action = focus-workspace "comm";
449 "Mod+Shift+ParenLeft".action = move-column-to-workspace "comm";
450
451 "Mod+ParenRight".action = focus-workspace "web";
452 "Mod+Shift+ParenRight".action = move-column-to-workspace "web";
453
454 "Mod+BraceRight".action = focus-workspace "read";
455 "Mod+Shift+BraceRight".action = move-column-to-workspace "read";
456
457 "Mod+BraceLeft".action = focus-workspace "mon";
458 "Mod+Shift+BraceLeft".action = move-column-to-workspace "mon";
459
460 "Mod+Asterisk".action = focus-workspace "vid";
461 "Mod+Shift+Asterisk".action = move-column-to-workspace "vid";
462
463 "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}'';
464 "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}'';
465
466 "Mod+M".action = consume-or-expel-window-left;
467 "Mod+W".action = consume-or-expel-window-right;
468
469 "Mod+R".action = switch-preset-column-width;
470 "Mod+Shift+R".action = switch-preset-window-height;
471 "Mod+F".action = center-column;
472 "Mod+Shift+F".action = maximize-column;
473 "Mod+Shift+Ctrl+F".action = fullscreen-window;
474
475 "Mod+V".action = switch-focus-between-floating-and-tiling;
476 "Mod+Shift+V".action = toggle-window-floating;
477
478 "Mod+Left".action = set-column-width "-10%";
479 "Mod+Down".action = set-window-height "-10%";
480 "Mod+Up".action = set-window-height "+10%";
481 "Mod+Right".action = set-column-width "+10%";
482
483 "Mod+Shift+Z" = {
484 action = spawn (lib.getExe niri) "msg" "action" "power-off-monitors";
485 allow-when-locked = true;
486 };
487 "Mod+Shift+L".action = spawn loginctl "lock-session";
488 "Mod+Shift+E".action = quit;
489 "Mod+Shift+Minus" = {
490 action = spawn systemctl "suspend";
491 allow-when-locked = true;
492 };
493 "Mod+Shift+Control+Minus" = {
494 action = spawn systemctl "hibernate";
495 allow-when-locked = true;
496 };
497
498 "XF86MonBrightnessUp" = {
499 action = spawn lightctl "-d" "-e4" "-n1" "up";
500 allow-when-locked = true;
501 };
502 "XF86MonBrightnessDown" = {
503 action = spawn lightctl "-d" "-e4" "-n1" "down";
504 allow-when-locked = true;
505 };
506 "XF86AudioRaiseVolume" = {
507 action = spawn volumectl "-d" "-u" "up";
508 allow-when-locked = true;
509 };
510 "XF86AudioLowerVolume" = {
511 action = spawn volumectl "-d" "-u" "down";
512 allow-when-locked = true;
513 };
514 "XF86AudioMute" = {
515 action = spawn volumectl "-d" "toggle-mute";
516 allow-when-locked = true;
517 };
518 "XF86AudioMicMute" = {
519 action = spawn volumectl "-d" "-m" "toggle-mute";
520 allow-when-locked = true;
521 };
522
523 "Mod+Semicolon".action = spawn makoctl "dismiss" "--group";
524 "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all";
525 "Mod+Period".action = spawn makoctl "menu" (lib.getExe config.programs.fuzzel.package) "--dmenu";
526 "Mod+Comma".action = spawn makoctl "restore";
527
528 "Mod+Control+A".action = focus-or-spawn-action-app_id "com.saivert.pwvucontrol" "pwctl" "pwvucontrol";
529 "Mod+Control+P".action = focus-or-spawn-action-app_id "org.keepassxc.KeePassXC" "kpxc" "keepassxc";
530 "Mod+Control+B".action = focus-or-spawn-action-app_id ".blueman-manager-wrapped" "bmgr" "blueman-manager";
531 "Mod+Control+Return".action = focus-or-spawn-action-app_id "kitty-scratch" "term" "kitty" "--app-id" "kitty-scratch";
532 "Mod+Control+E".action = focus-or-spawn-action "select(.app_id == \"emacs\" and .title == \"scratch\")" "edit" "emacsclient" "-c" "--frame-parameters=(quote (name . \"scratch\"))";
533 }; 437 };
534 }; 438 };
439
440 programs.niri.scratchspaces = [
441 { name = "pwctl";
442 key = "Mod+Control+A";
443 spawn = ["pwvucontrol"];
444 app-id = "com.saivert.pwvucontrol";
445 }
446 { name = "kpxc";
447 exclude = [
448 { title = "^Unlock Database.*"; }
449 { title = "^Access Request.*"; }
450 { title = ".*Passkey credentials$"; }
451 ];
452 windowRuleExtra = with kdl; [
453 (kdl.leaf "open-focused" false)
454 ];
455 key = "Mod+Control+P";
456 app-id = "org.keepassxc.KeePassXC";
457 spawn = [ "keepassxc" ];
458 }
459 { name = "bmgr";
460 key = "Mod+Control+B";
461 app-id = ".blueman-manager-wrapped";
462 spawn = [ "blueman-manager" ];
463 }
464 { name = "term";
465 key = "Mod+Control+Return";
466 app-id = "kitty-scratch";
467 spawn = [ "kitty" "--app-id" "kitty-scratch" ];
468 }
469 { name = "edit";
470 match = [ { title = "^scratch$"; app-id = "^emacs$"; } ];
471 key = "Mod+Control+E";
472 selector = "select(.app_id == \"emacs\" and .title == \"scratch\")";
473 spawn = [ "emacsclient" "-c" "--frame-parameters=(quote (name . \"scratch\"))" ];
474 }
475 { name = "eff";
476 key = "Mod+Control+O";
477 app-id = "com.github.wwmm.easyeffects";
478 spawn = [ "easyeffects" ];
479 }
480 { name = "time";
481 key = "Mod+Control+K";
482 app-id = "chrome-kimai.yggdrasil.li__-Default";
483 spawn = [ (toString (pkgs.resholve.writeScript "kimai" {
484 interpreter = pkgs.runtimeShell;
485 inputs = [ pkgs.dex ];
486 execer = [ "cannot:${lib.getExe pkgs.dex}" ];
487 } ''
488 exec dex $HOME/.local/state/nix/profile/share/applications/kimai.desktop
489 '')) ];
490 windowRuleExtra = with kdl; [
491 (leaf "block-out-from" "screencast")
492 ];
493 }
494 ];
495 programs.niri.config =
496 let
497 inherit (kdl) node plain leaf flag;
498 optional-node = cond: v:
499 if cond
500 then v
501 else null;
502 opt-props = lib.filterAttrs (lib.const (value: value != null));
503 in
504 [ (flag "prefer-no-csd")
505
506 (leaf "screenshot-path" "~/screenshots/%Y-%m-%dT%H:%M:%S.png")
507
508 (plain "hotkey-overlay" [
509 (flag "skip-at-startup")
510 ])
511
512 (plain "input" [
513 (plain "keyboard" [
514 (leaf "repeat-delay" 300)
515 (leaf "repeat-rate" 50)
516
517 (plain "xkb" [
518 (leaf "layout" "us,us")
519 (leaf "variant" "dvp,")
520 (leaf "options" "compose:caps,grp:win_space_toggle")
521 ])
522 ])
523
524 (flag "workspace-auto-back-and-forth")
525 # (leaf "focus-follows-mouse" {})
526 # (flag "warp-mouse-to-focus")
527
528 # (plain "touchpad" [ (flag "off") ])
529 (plain "trackball" [
530 (leaf "scroll-method" "on-button-down")
531 (leaf "scroll-button" 278)
532 ])
533 (plain "touch" [
534 (leaf "map-to-output" "eDP-1")
535 ])
536 ])
537
538 (plain "gestures" [
539 (plain "hot-corners" [(flag "off")])
540 ])
541
542 (plain "environment" (lib.mapAttrsToList leaf {
543 NIXOS_OZONE_WL = "1";
544 QT_QPA_PLATFORM = "wayland";
545 QT_WAYLAND_DISABLE_WINDOWDECORATION = "1";
546 GDK_BACKEND = "wayland";
547 SDL_VIDEODRIVER = "wayland";
548 DISPLAY = ":0";
549 ELECTRON_OZONE_PLATFORM_HINT = "auto";
550 SSH_ASKPASS_REQUIRE = "prefer";
551 SSH_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass;
552 SUDO_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass;
553 }))
554
555 (node "output" "eDP-1" [
556 (leaf "scale" 1.5)
557 (leaf "position" { x = 0; y = 0; })
558 ])
559 (node "output" "Ancor Communications Inc ASUS PB287Q 0x0000DD9B" [
560 (leaf "scale" 1.5)
561 (leaf "position" { x = 2560; y = 0; })
562 ])
563 (node "output" "HP Inc. HP 727pu CN4417143K" [
564 (leaf "mode" "2560x1440@119.998")
565 (leaf "scale" 1)
566 (leaf "position" { x = 2560; y = 0; })
567 (flag "variable-refresh-rate")
568 ])
569
570 (plain "debug" [
571 (leaf "render-drm-device" "/dev/dri/by-path/pci-0000:00:02.0-render")
572 ])
573
574 (plain "animations" [
575 (leaf "slowdown" 0.5)
576 (plain "workspace-switch" [(flag "off")])
577 ])
578
579 (plain "layout" [
580 (leaf "gaps" 8)
581 (plain "struts" [
582 (leaf "left" 26)
583 (leaf "right" 26)
584 (leaf "top" 0)
585 (leaf "bottom" 0)
586 ])
587 (plain "border" [
588 (leaf "width" 2)
589 (leaf "active-gradient" {
590 from = "hsla(195 100% 45% 1)";
591 to = "hsla(155 100% 37.5% 1)";
592 angle = 29;
593 relative-to = "workspace-view";
594 })
595 (leaf "inactive-gradient" {
596 from = "hsla(0 0% 27.7% 1)";
597 to = "hsla(0 0% 23% 1)";
598 angle = 29;
599 relative-to = "workspace-view";
600 })
601 ])
602 (plain "focus-ring" [
603 (flag "off")
604 ])
605
606 (plain "preset-column-widths" (map (prop: leaf "proportion" prop) [
607 (1. / 4.) (1. / 3.) (1. / 2.) (2. / 3.) (3. / 4.) (1.)
608 ]))
609 (plain "default-column-width" [ (leaf "proportion" (1. / 2.)) ])
610 (plain "preset-window-heights" (map (prop: leaf "proportion" prop) [
611 (1. / 3.) (1. / 2.) (2. / 3.) (1.)
612 ]))
613
614 (flag "always-center-single-column")
615
616 (plain "tab-indicator" [
617 (leaf "gap" 4)
618 (leaf "width" 8)
619 (leaf "gaps-between-tabs" 4)
620 (flag "place-within-column")
621 (leaf "length" { total-proportion = 1.; })
622 (leaf "active-gradient" {
623 from = "hsla(195 100% 60% 0.75)";
624 to = "hsla(155 100% 50% 0.75)";
625 angle = 29;
626 relative-to = "workspace-view";
627 })
628 (leaf "inactive-gradient" {
629 from = "hsla(0 0% 42% 0.66)";
630 to = "hsla(0 0% 35% 0.66)";
631 angle = 29;
632 relative-to = "workspace-view";
633 })
634 ])
635 ])
636
637 (plain "cursor" [
638 (flag "hide-when-typing")
639 ])
640
641 (map (name:
642 (node "workspace" name [
643 (leaf "open-on-output" "eDP-1")
644 ])
645 ) (map ({name, ...}: name) cfg.scratchspaces))
646 (map (name:
647 (leaf "workspace" name)
648 ) ["comm" "web" "vid" "bmr"])
649
650 (plain "window-rule" [
651 (leaf "clip-to-geometry" true)
652 ])
653
654 (plain "window-rule" [
655 (leaf "match" { is-floating = true; })
656 (leaf "geometry-corner-radius" 8)
657 (plain "shadow" [ (flag "on") ])
658 ])
659
660 (plain "window-rule" [
661 (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; })
662 (leaf "block-out-from" "screencast")
663 ])
664 (plain "window-rule" [
665 (map (title:
666 (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; })
667 ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$" "Browser Access Request$"])
668 (leaf "open-focused" true)
669 (leaf "open-floating" true)
670 ])
671
672 (map ({ name, match, exclude, windowRuleExtra, ... }:
673 (optional-node (match != []) (plain "window-rule" [
674 (map (leaf "match") match)
675 (map (leaf "exclude") exclude)
676 (leaf "open-on-workspace" name)
677 (leaf "open-maximized" true)
678 windowRuleExtra
679 ]))
680 ) cfg.scratchspaces)
681
682 (plain "window-rule" [
683 (leaf "match" { app-id = "^emacs$"; })
684 (leaf "match" { app-id = "^firefox$"; })
685 (plain "default-column-width" [(leaf "proportion" (2. / 3.))])
686 ])
687 (plain "window-rule" [
688 (leaf "match" { app-id = "^kitty$"; })
689 (leaf "match" { app-id = "^kitty-play$"; })
690 (plain "default-column-width" [(leaf "proportion" (1. / 3.))])
691 ])
692
693 (plain "window-rule" [
694 (leaf "match" { app-id = "^thunderbird$"; })
695 (leaf "match" { app-id = "^Element$"; })
696 (leaf "match" { app-id = "^chrome-web\.openrainbow\.com__-Default$"; })
697 (leaf "open-on-workspace" "comm")
698 ])
699 (plain "window-rule" [
700 (leaf "match" { app-id = "^firefox$"; })
701 (leaf "open-on-workspace" "web")
702 (leaf "open-maximized" true)
703 ])
704 (plain "window-rule" [
705 (leaf "match" { app-id = "^mpv$"; })
706 (leaf "open-on-workspace" "vid")
707 (plain "default-column-width" [(leaf "proportion" 1.)])
708 ])
709 (plain "window-rule" [
710 (leaf "match" { app-id = "^kitty-play$"; })
711 (leaf "open-on-workspace" "vid")
712 (leaf "open-focused" false)
713 ])
714 (plain "window-rule" [
715 (leaf "match" { app-id = "^chrome-audiobookshelf\.yggdrasil\.li__-Default$"; })
716 (leaf "match" { app-id = "^YouTube Music Desktop App$"; })
717 (leaf "open-on-workspace" "vid")
718 ])
719 (plain "window-rule" [
720 (leaf "match" { app-id = "^pdfpc$"; })
721 (plain "default-column-width" [(leaf "proportion" 1.)])
722 ])
723 (plain "window-rule" [
724 (leaf "match" { app-id = "^pdfpc$"; title = "^.*presentation.*$"; })
725 (plain "default-column-width" [(leaf "proportion" 1.)])
726 (leaf "open-fullscreen" true)
727 (leaf "open-on-workspace" "bmr")
728 (leaf "open-focused" false)
729 ])
730 (plain "window-rule" [
731 (map (leaf "match") [
732 { app-id = "^Gimp-"; title = "^Quit GIMP$"; }
733 { app-id = "^org\\.kde\\.polkit-kde-authentication-agent-1$"; }
734 { app-id = "^xdg-desktop-portal-gtk$"; }
735 ])
736 (leaf "open-floating" true)
737 ])
738 (plain "window-rule" [
739 (leaf "match" { app-id = "^org\\.pwmt\\.zathura$"; })
740 (leaf "match" { app-id = "^evince$"; })
741 (leaf "match" { app-id = "^org\\.gnome\\.Papers$"; })
742 (leaf "default-column-display" "tabbed")
743 ])
744
745 (plain "layer-rule" [
746 (leaf "match" { namespace = "^notifications$"; })
747 (leaf "match" { namespace = "^waybar$"; })
748 (leaf "match" { namespace = "^launcher$"; })
749 (leaf "block-out-from" "screencast")
750 ])
751
752 (plain "binds"
753 (let
754 bind = name: cfg: node name (opt-props {
755 cooldown-ms = cfg.cooldown-ms or null;
756 }
757 // (lib.optionalAttrs (!(cfg.repeat or true)) {
758 repeat = false;
759 })
760 // (lib.optionalAttrs (cfg.allow-when-locked or false) {
761 allow-when-locked = true;
762 })) (lib.mapAttrsToList leaf (lib.removeAttrs cfg.action ["__functor"]));
763 in
764 [
765 (lib.mapAttrsToList bind (with config.lib.niri.actions; {
766 "Mod+Slash".action = show-hotkey-overlay;
767
768 "Mod+Return".action = spawn terminal;
769 "Mod+Shift+Return".action =
770 let
771 nushellKitty = pkgs.symlinkJoin {
772 name = "nushell-kitty";
773 paths = [ config.programs.kitty.package ];
774 buildInputs = [ pkgs.makeWrapper ];
775 postBuild = ''
776 wrapProgram $out/bin/kitty \
777 --add-flags "--config ${pkgs.writeText "kitty.conf" ''
778 include $HOME/${config.xdg.configFile."kitty/kitty.conf".target}
779 shell ${lib.getExe config.programs.nushell.package}
780 ''}"
781 '';
782 };
783 in spawn (lib.getExe' nushellKitty "kitty");
784 "Mod+Q".action = close-window;
785 "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package);
786 "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path";
787
788 "Mod+Alt+E".action = spawn (lib.getExe' config.services.emacs.package "emacsclient") "-c";
789 "Mod+Alt+Y".action = spawn (lib.getExe (pkgs.writeShellApplication {
790 name = "queue-yt-dlp";
791 runtimeInputs = with pkgs; [ wl-clipboard-rs socat ];
792 text = ''
793 socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/yt-dlp.sock <<<$'{ "urls": ["'"$(wl-paste)"$'"] }'
794 '';
795 }));
796 "Mod+Alt+L".action = spawn (lib.getExe (pkgs.writeShellApplication {
797 name = "queue-yt-dlp";
798 runtimeInputs = with pkgs; [ wl-clipboard-rs config.programs.kitty.package ];
799 text = ''
800 exec -- kitty --app-id kitty-play --directory "$HOME"/media mpv "$(wl-paste)"
801 '';
802 }));
803 "Mod+Alt+M".action = spawn (lib.getExe' pkgs.screen-message "sm") "-n" "Fira Mono" "-a" "1" "-f" "#fff" "-b" "#000";
804
805 "Mod+U".action = spawn (lib.getExe (pkgs.writeShellApplication {
806 name = "qalc-fuzzel";
807 runtimeInputs = with pkgs; [ wl-clipboard-rs libqalculate config.programs.fuzzel.package coreutils findutils libnotify gnugrep ];
808 text = ''
809 RESULTS_DIR="$HOME/.cache/qalc-fuzzel"
810 prev() {
811 FOUND=false
812 while IFS= read -r line; do
813 [[ -n "$line" ]] || continue
814 FOUND=true
815 echo "$line"
816 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)
817 $FOUND || echo
818 }
819 FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> " --width=60) || exit $?
820 if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then
821 QALC_RES="$FUZZEL_RES"
822 QALC_RET=0
823 else
824 QALC_RES=$(qalc -set "autocalc off" "$FUZZEL_RES" 2>&1)
825 QALC_RET=$?
826 fi
827 [[ -n "$QALC_RES" ]] || exit 1
828 EXISTING=false
829 set +o pipefail
830 grep -Fxrl "$QALC_RES" "$RESULTS_DIR" | xargs -r touch
831 [[ ''${PIPESTATUS[0]} -eq 0 ]] && EXISTING=true
832 set -o pipefail
833 if [[ $QALC_RET -eq 0 ]] && ! $EXISTING; then
834 set +o pipefail
835 RES_FILE="$RESULTS_DIR"/$(date -uIs).$(tr -Cd 'a-zA-Z0-9' </dev/random | head -c 10)
836 set -o pipefail
837 cat >"$RES_FILE" <<<"$QALC_RES"
838 fi
839 [[ "$QALC_RES" =~ .*\ =\ (.*) ]] && QALC_RES="''${BASH_REMATCH[1]}"
840 [[ $QALC_RET -eq 0 ]] && wl-copy "$QALC_RES"
841 notify-send "$QALC_RES"
842 '';
843 }));
844 "Mod+Shift+U".action =
845 let
846 qalcKitty = pkgs.symlinkJoin {
847 name = "qalc-kitty";
848 paths = [ config.programs.kitty.package ];
849 buildInputs = [ pkgs.makeWrapper ];
850 postBuild = ''
851 wrapProgram $out/bin/kitty \
852 --add-flags "--config ${pkgs.writeText "kitty.conf" ''
853 include $HOME/${config.xdg.configFile."kitty/kitty.conf".target}
854 shell ${lib.getExe pkgs.libqalculate}
855 ''}"
856 '';
857 };
858 in spawn (lib.getExe' qalcKitty "kitty");
859 "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication {
860 name = "emoji-fuzzel";
861 runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ];
862 text = ''
863 FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " --cache "$HOME"/.cache/fuzzel-emoji --width=60 <"$HOME"/.local/share/emoji-data/list.txt) || exit $?
864 [[ -n "$FUZZEL_RES" ]] || exit 1
865 wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste
866 '';
867 }));
868 "Print".action = screenshot;
869 "Control+Print".action = screenshot-window;
870 "Shift+Print".action = kdl.magic-leaf "screenshot-screen";
871 "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
872 "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
873
874 "Mod+Escape" = {
875 allow-inhibiting = false;
876 action = toggle-keyboard-shortcuts-inhibit;
877 };
878
879 "Mod+H".action = focus-column-left;
880 "Mod+T".action = focus-window-down;
881 "Mod+N".action = focus-window-up;
882 "Mod+S".action = focus-column-right;
883
884 "Mod+Shift+H".action = move-column-left;
885 "Mod+Shift+T".action = move-window-down;
886 "Mod+Shift+N".action = move-window-up;
887 "Mod+Shift+S".action = move-column-right;
888
889 "Mod+Control+H".action = focus-monitor-left;
890 "Mod+Control+T".action = focus-monitor-down;
891 "Mod+Control+N".action = focus-monitor-up;
892 "Mod+Control+S".action = focus-monitor-right;
893
894 "Mod+Shift+Control+H".action = move-workspace-to-monitor-left;
895 "Mod+Shift+Control+T".action = move-workspace-to-monitor-down;
896 "Mod+Shift+Control+N".action = move-workspace-to-monitor-up;
897 "Mod+Shift+Control+S".action = move-workspace-to-monitor-right;
898
899 "Mod+G".action = focus-adjacent-workspace "down";
900 "Mod+C".action = focus-adjacent-workspace "up";
901
902 "Mod+Shift+G".action = move-column-to-adjacent-workspace "down";
903 "Mod+Shift+C".action = move-column-to-adjacent-workspace "up";
904
905 "Mod+Shift+Control+G".action = move-workspace-down;
906 "Mod+Shift+Control+C".action = move-workspace-up;
907
908 "Mod+ParenLeft".action = focus-workspace "comm";
909 "Mod+Shift+ParenLeft".action = kdl.magic-leaf "move-column-to-workspace" "comm";
910
911 "Mod+ParenRight".action = focus-workspace "web";
912 "Mod+Shift+ParenRight".action = kdl.magic-leaf "move-column-to-workspace" "web";
913
914 "Mod+BraceRight".action = focus-workspace "read";
915 "Mod+Shift+BraceRight".action = kdl.magic-leaf "move-column-to-workspace" "read";
916
917 "Mod+BraceLeft".action = focus-workspace "mon";
918 "Mod+Shift+BraceLeft".action = kdl.magic-leaf "move-column-to-workspace" "mon";
919
920 "Mod+Asterisk".action = focus-workspace "vid";
921 "Mod+Shift+Asterisk".action = kdl.magic-leaf "move-column-to-workspace" "vid";
922
923 "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}'';
924 "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}'';
925
926 "Mod+M".action = consume-or-expel-window-left;
927 "Mod+W".action = consume-or-expel-window-right;
928
929 "Mod+Shift+M".action = toggle-column-tabbed-display;
930
931 "Mod+R".action = switch-preset-column-width;
932 "Mod+Shift+R".action = maximize-column;
933 "Mod+Shift+Ctrl+R".action = switch-preset-window-height;
934 "Mod+F".action = center-column;
935 "Mod+Shift+F".action = toggle-windowed-fullscreen;
936 "Mod+Ctrl+Shift+F".action = fullscreen-window;
937
938 "Mod+V".action = switch-focus-between-floating-and-tiling;
939 "Mod+Shift+V".action = toggle-window-floating;
940
941 "Mod+Left".action = set-column-width "-10%";
942 "Mod+Down".action = set-window-height "-10%";
943 "Mod+Up".action = set-window-height "+10%";
944 "Mod+Right".action = set-column-width "+10%";
945
946 "Mod+Shift+Z" = {
947 action = spawn (lib.getExe niri) "msg" "action" "power-off-monitors";
948 allow-when-locked = true;
949 };
950 "Mod+Shift+L".action = spawn loginctl "lock-session";
951 "Mod+Shift+E".action = quit;
952 "Mod+Shift+Minus" = {
953 action = spawn systemctl "suspend";
954 allow-when-locked = true;
955 };
956 "Mod+Shift+Control+Minus" = {
957 action = spawn systemctl "hibernate";
958 allow-when-locked = true;
959 };
960 "Mod+Shift+P" = {
961 action = spawn (lib.getExe pkgs.playerctl) "-a" "pause";
962 allow-when-locked = true;
963 };
964
965 "XF86MonBrightnessUp" = {
966 action = spawn swayosd-client "--brightness" "raise";
967 allow-when-locked = true;
968 };
969 "XF86MonBrightnessDown" = {
970 action = spawn swayosd-client "--brightness" "lower";
971 allow-when-locked = true;
972 };
973 "XF86AudioRaiseVolume" = {
974 action = spawn swayosd-client "--output-volume" "raise";
975 allow-when-locked = true;
976 };
977 "XF86AudioLowerVolume" = {
978 action = spawn swayosd-client "--output-volume" "lower";
979 allow-when-locked = true;
980 };
981 "XF86AudioMute" = {
982 action = spawn swayosd-client "--output-volume" "mute-toggle";
983 allow-when-locked = true;
984 };
985 "XF86AudioMicMute" = {
986 action = spawn swayosd-client "--input-volume" "mute-toggle";
987 allow-when-locked = true;
988 };
989
990 "Mod+Semicolon".action = spawn makoctl "dismiss" "--group";
991 "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all";
992 "Mod+Period".action = spawn makoctl "menu" "--" (lib.getExe config.programs.fuzzel.package) "--dmenu";
993 "Mod+Comma".action = spawn makoctl "restore";
994
995 "Mod+Control+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"FocusWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}";
996 "Mod+Control+Shift+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"MoveColumnToWorkspace\":{\"reference\":{\"Id\": $workspace_id}, \"focus\": true}}}";
997
998 "Mod+X".action = set-dynamic-cast-window;
999 "Mod+Shift+X".action = set-dynamic-cast-monitor;
1000 "Mod+Control+Shift+X".action = clear-dynamic-cast-target;
1001
1002 "Mod+D".action = with-urgent-window-action "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
1003 "Mod+Shift+D".action = with-focused-window-action "{\"Action\":{\"UnsetUrgent\":{\"id\": .id}}}";
1004
1005 "Mod+K".action = spawn (lib.getExe' pkgs.worktime "worktime-ui");
1006 "Mod+Shift+K".action = spawn (lib.getExe' pkgs.worktime "worktime-stop");
1007 }))
1008 (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)
1009 (map ({ name, moveKey, ...}: if moveKey != null then bind moveKey { action = kdl.magic-leaf "move-column-to-workspace" name; } else null) cfg.scratchspaces)
1010 ]
1011 ))
1012 ];
535 }; 1013 };
536} 1014}
diff --git a/accounts/gkleen@sif/niri/mako.nix b/accounts/gkleen@sif/niri/mako.nix
index 8abf14d8..eba26caa 100644
--- a/accounts/gkleen@sif/niri/mako.nix
+++ b/accounts/gkleen@sif/niri/mako.nix
@@ -1,34 +1,41 @@
1{ config, lib, ... }: 1{ config, lib, pkgs, ... }:
2{ 2{
3 config = { 3 config = {
4 services.mako = { 4 services.mako = {
5 enable = true; 5 enable = true;
6 font = "Fira Sans 10"; 6 settings = {
7 format = "<i>%s</i>\\n%b"; 7 font = "Fira Sans 10";
8 margin = "2"; 8 format = "<i>%s</i>\\n%b";
9 maxVisible = -1; 9 margin = "2";
10 backgroundColor = "#000000dd"; 10 max-visible = -1;
11 progressColor = "source #223544ff"; 11 background-color = "#000000dd";
12 width = 384; 12 progress-color = "source #223544ff";
13 extraConfig = '' 13 width = 384;
14 outer-margin=1 14 outer-margin = 1;
15 max-history=100 15 max-history = 100;
16 16 max-icon-size = 48;
17 [grouped] 17 };
18 format=<b>(%g)</b> <i>%s</i>\n%b 18 criteria = {
19 19 grouped.format = "<b>(%g)</b> <i>%s</i>\\n%b";
20 [urgency=low] 20 "urgency=low".text-color = "#999999ff";
21 text-color=#999999ff 21 "urgency=critical".background-color = "#900000dd";
22 22 "app-name=Element".group-by = "summary";
23 [urgency=critical] 23 "app-name=poweralertd" = {
24 background-color=#900000dd 24 history = false;
25 25 ignore-timeout = true;
26 [app-name=Element] 26 default-timeout = 2000;
27 group-by=summary 27 };
28 28 "app-name=worktime".history = false;
29 [mode=silent] 29 "mode=silent".invisible = true;
30 invisible=1 30 };
31 ''; 31 package = pkgs.symlinkJoin {
32 name = "${pkgs.mako.name}-wrapped";
33 paths = with pkgs; [ mako ];
34 inherit (pkgs.mako) meta;
35 postBuild = ''
36 rm -r $out/share/dbus-1
37 '';
38 };
32 }; 39 };
33 systemd.user.services.mako = { 40 systemd.user.services.mako = {
34 Unit = { 41 Unit = {
@@ -46,5 +53,61 @@
46 Restart = "always"; 53 Restart = "always";
47 }; 54 };
48 }; 55 };
56
57 systemd.user.services.mako-follows-focus = {
58 Unit = {
59 BindsTo = [ "niri.service" "mako.service" ];
60 After = [ "niri.service" "mako.service" ];
61 };
62 Service = {
63 Type = "simple";
64 Restart = "always";
65 ExecStart = pkgs.writers.writePython3 "mako-follows-focus" {
66 libraries = with pkgs.python3Packages; [];
67 } ''
68 import os
69 import socket
70 import json
71 import subprocess
72
73
74 current_output = None
75 workspaces = []
76
77
78 def output_changed(new_output):
79 global current_output
80
81 if current_output == new_output:
82 return
83
84 current_output = new_output
85 subprocess.run(["makoctl", "reload"])
86
87
88 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
89 sock.connect(os.environ["NIRI_SOCKET"])
90 sock.send(b"\"EventStream\"\n")
91 for line in sock.makefile(buffering=1, encoding='utf-8'):
92 if line_json := json.loads(line):
93 if "WorkspacesChanged" in line_json:
94 workspaces = line_json["WorkspacesChanged"]["workspaces"]
95 for workspace in workspaces:
96 if not workspace["is_focused"]:
97 continue
98 output_changed(workspace["output"])
99 break
100 if "WorkspaceActivated" in line_json and line_json["WorkspaceActivated"]["focused"]: # noqa: E501
101 for workspace in workspaces:
102 if not workspace["id"] == line_json["WorkspaceActivated"]["id"]: # noqa: E501
103 continue
104 output_changed(workspace["output"])
105 break
106 '';
107 };
108 Install = {
109 WantedBy = [ "mako.service" ];
110 };
111 };
49 }; 112 };
50} 113}
diff --git a/accounts/gkleen@sif/niri/swayosd.nix b/accounts/gkleen@sif/niri/swayosd.nix
new file mode 100644
index 00000000..54ebb302
--- /dev/null
+++ b/accounts/gkleen@sif/niri/swayosd.nix
@@ -0,0 +1,66 @@
1{ pkgs, ... }:
2{
3 config = {
4 services.swayosd = {
5 enable = true;
6 topMargin = 0.4769706078;
7 stylePath = pkgs.runCommand "style.css" {
8 passAsFile = [ "src" ];
9 src = ''
10 window#osd {
11 padding: 12px 20px;
12 border-radius: 999px;
13 border: none;
14 background: rgba(0, 0, 0, 0.87);
15
16 #container {
17 margin: 16px;
18 }
19
20 image,
21 label {
22 color: rgb(255, 255, 255);
23
24 &:disabled {
25 opacity: 1;
26 color: rgb(84, 84, 84);
27 }
28 }
29
30 progressbar {
31 min-height: 6px;
32 border-radius: 999px;
33 background: transparent;
34 border: none;
35
36 trough, progress {
37 min-height: inherit;
38 border-radius: inherit;
39 border: none;
40 }
41
42 trough {
43 background: rgb(127, 127, 127);
44 }
45 progress {
46 background: rgb(255, 255, 255);
47 }
48
49 &:disabled {
50 opacity: 1;
51
52 trough {
53 background: rgb(19, 19, 19);
54 }
55 progress {
56 background: rgb(38, 38, 38);
57 }
58 }
59 }
60 }
61 '';
62 buildInputs = with pkgs; [sass];
63 } "scss -C --sourcemap=none --style=compact $srcPath $out";
64 };
65 };
66}
diff --git a/accounts/gkleen@sif/niri/waybar.nix b/accounts/gkleen@sif/niri/waybar.nix
index 79c429f8..c02a9a76 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;
@@ -18,15 +20,21 @@
18 { 20 {
19 layer = "top"; 21 layer = "top";
20 position = "top"; 22 position = "top";
21 height = 14; 23 height = 21;
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" "custom/lid_inhibitor" "clock" ];
29 31
32 "custom/lid_inhibitor" = {
33 format = "{}";
34 return-type = "json";
35 exec = lib.getExe pkgs.waybar-systemd-inhibit;
36 on-click = lib.getExe' pkgs.waybar-systemd-inhibit "waybar-systemd-inhibit-toggle";
37 };
30 "custom/mako" = { 38 "custom/mako" = {
31 format = "{}"; 39 format = "{}";
32 return-type = "json"; 40 return-type = "json";
@@ -59,7 +67,7 @@
59 text = f"<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>" # noqa: E501 67 text = f"<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>" # noqa: E501
60 if is_silent: 68 if is_silent:
61 text = f"<span color=\"#ffffff\">{text}</span>" 69 text = f"<span color=\"#ffffff\">{text}</span>"
62 print(json.dumps({'text': text}, separators=(',', ':')), flush=True) # noqa: E501 70 print(json.dumps({'text': text, 'tooltip': ', '.join(modes)}, separators=(',', ':')), flush=True) # noqa: E501
63 71
64 async def on_properties_changed(interface_name, changed_properties, invalidated_properties): # noqa: E501 72 async def on_properties_changed(interface_name, changed_properties, invalidated_properties): # noqa: E501
65 if "Modes" not in invalidated_properties: 73 if "Modes" not in invalidated_properties:
@@ -120,16 +128,16 @@
120 }; 128 };
121 "custom/worktime" = { 129 "custom/worktime" = {
122 interval = 60; 130 interval = 60;
123 exec = lib.getExe pkgs.worktime; 131 exec = "${lib.getExe pkgs.worktime} time --waybar";
124 tooltip = false; 132 return-type = "json";
125 }; 133 };
126 "custom/worktime-today" = { 134 "custom/worktime-today" = {
127 interval = 60; 135 interval = 60;
128 exec = "${lib.getExe pkgs.worktime} today"; 136 exec = "${lib.getExe pkgs.worktime} today --waybar";
129 tooltip = false; 137 return-type = "json";
130 }; 138 };
131 "niri/workspaces" = { 139 "niri/workspaces" = {
132 ignore = ["pwctl" "kpxc" "bmgr" "edit" "term"]; 140 ignore = map ({ name, ... }: name) config.programs.niri.scratchspaces;
133 }; 141 };
134 "niri/window" = { 142 "niri/window" = {
135 separate-outputs = true; 143 separate-outputs = true;
@@ -190,8 +198,8 @@
190 icon-size = iconSize; 198 icon-size = iconSize;
191 tooltip-format = "{percent}%"; 199 tooltip-format = "{percent}%";
192 format-icons = ["&#xf00da;" "&#xf00db;" "&#xf00dc;" "&#xf00dd;" "&#xf00de;" "&#xf00df;" "&#xf00e0;"]; 200 format-icons = ["&#xf00da;" "&#xf00db;" "&#xf00dc;" "&#xf00dd;" "&#xf00de;" "&#xf00df;" "&#xf00e0;"];
193 on-scroll-up = "lightctl -d -e4 -n1 up"; 201 on-scroll-up = "${swayosd-client} --brightness raise";
194 on-scroll-down = "lightctl -d -e4 -n1 down"; 202 on-scroll-down = "${swayosd-client} --brightness lower";
195 }; 203 };
196 wireplumber = { 204 wireplumber = {
197 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; 205 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
@@ -200,22 +208,22 @@
200 format-icons = ["&#xf057f;" "&#xf0580;" "&#xf057e;"]; 208 format-icons = ["&#xf057f;" "&#xf0580;" "&#xf057e;"];
201 format-muted = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">&#xf075f;</span>"; 209 format-muted = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">&#xf075f;</span>";
202 # ignored-sinks = ["Easy Effects Sink"]; 210 # ignored-sinks = ["Easy Effects Sink"];
203 on-scroll-up = "volumectl -d -u up"; 211 on-scroll-up = "${swayosd-client} --output-volume raise";
204 on-scroll-down = "volumectl -d -u down"; 212 on-scroll-down = "${swayosd-client} --output-volume lower";
205 on-click = "volumectl -d toggle-mute"; 213 on-click = "${swayosd-client} --output-volume mute-toggle";
206 }; 214 };
207 } 215 }
208 { 216 {
209 layer = "top"; 217 layer = "top";
210 position = "top"; 218 position = "top";
211 height = 14; 219 height = 14;
212 output = [ "!eDP-1" "!DP-2" "!DP-3" ]; 220 output = [ "!eDP-1" "!DP-2" "!DP-3" "*" ];
213 modules-left = [ "niri/workspaces" ]; 221 modules-left = [ "niri/workspaces" ];
214 modules-center = [ "niri/window" ]; 222 modules-center = [ "niri/window" ];
215 modules-right = [ "clock" ]; 223 modules-right = [ "clock" ];
216 224
217 "niri/workspaces" = { 225 "niri/workspaces" = {
218 ignore = ["pwctl" "kpxc" "bmgr" "edit" "term"]; 226 ignore = map ({ name, ... }: name) config.programs.niri.scratchspaces;
219 }; 227 };
220 "niri/window" = { 228 "niri/window" = {
221 separate-outputs = true; 229 separate-outputs = true;
@@ -241,7 +249,7 @@
241 249
242 * { 250 * {
243 border: none; 251 border: none;
244 font-family: "Fira Sans Nerd Font"; 252 font-family: "Fira Sans";
245 font-size: 10pt; 253 font-size: 10pt;
246 min-height: 0; 254 min-height: 0;
247 } 255 }
@@ -252,10 +260,10 @@
252 } 260 }
253 261
254 .modules-left { 262 .modules-left {
255 margin-left: 8px; 263 margin-left: 38px;
256 } 264 }
257 .modules-right { 265 .modules-right {
258 margin-right: 8px; 266 margin-right: 38px;
259 } 267 }
260 268
261 .module { 269 .module {
@@ -280,17 +288,18 @@
280 color: @grey; 288 color: @grey;
281 margin: 0 5px; 289 margin: 0 5px;
282 } 290 }
283 #custom-weather, #custom-worktime-today { 291 #custom-weather {
284 margin-right: 3px; 292 margin-right: 3px;
285 } 293 }
286 #custom-keymap, #custom-weather { 294 #custom-keymap {
287 margin-left: 3px; 295 margin-left: 3px;
296 margin-right: 3px;
288 } 297 }
289 298
290 #tray { 299 #tray {
291 margin: 0; 300 margin: 0;
292 } 301 }
293 #battery, #idle_inhibitor, #backlight, #wireplumber, #custom-mako { 302 #battery, #idle_inhibitor, #backlight, #wireplumber, #custom-mako, #custom-lid_inhibitor {
294 color: @grey; 303 color: @grey;
295 margin: 0 5px 0 2px; 304 margin: 0 5px 0 2px;
296 } 305 }
@@ -299,7 +308,11 @@
299 margin-left: 6px; 308 margin-left: 6px;
300 } 309 }
301 #custom-mako { 310 #custom-mako {
302 margin-right: 2px; 311 margin-right: 4px;
312 margin-left: 3px;
313 }
314 #custom-lid_inhibitor {
315 margin-right: 3px;
303 margin-left: 3px; 316 margin-left: 3px;
304 } 317 }
305 #battery { 318 #battery {
@@ -320,17 +333,24 @@
320 #idle_inhibitor.activated { 333 #idle_inhibitor.activated {
321 color: @white; 334 color: @white;
322 } 335 }
336 #custom-worktime.running, #custom-worktime-today.running {
337 color: @white;
338 }
339 #custom-worktime.over, #custom-worktime-today.over {
340 color: @orange;
341 }
323 342
324 #idle_inhibitor { 343 #idle_inhibitor, #custom-lid_inhibitor {
325 padding-top: 1px; 344 padding-top: 1px;
326 } 345 }
327 346
328 #privacy { 347 #privacy {
329 color: @red; 348 color: @red;
330 margin: -1px 2px 0px 5px; 349 margin: -1px 4px 0px 3px;
331 } 350 }
332 #clock { 351 #clock {
333 /* margin-right: 5px; */ 352 /* margin-right: 5px; */
353 font-feature-settings: "tnum";
334 } 354 }
335 ''; 355 '';
336 }; 356 };
diff --git a/accounts/gkleen@sif/ssh-hosts.nix b/accounts/gkleen@sif/ssh-hosts.nix
index 107f1e76..a250509b 100644
--- a/accounts/gkleen@sif/ssh-hosts.nix
+++ b/accounts/gkleen@sif/ssh-hosts.nix
@@ -1,5 +1,12 @@
1{ pkgs, ... }: 1{ lib, pkgs, ... }:
2{ 2let
3 autosshProxyPorts = {
4 "ssh.math.lmu.de" = 8118;
5 "mathw0h" = 8122;
6 "mathw0e" = 8124;
7 };
8 autosshProxy = host: "${lib.getExe pkgs.socat} - SOCKS4A:127.0.0.1:%h:%p,socksport=${toString autosshProxyPorts.${host}}";
9in {
3 "git.ymir" = 10 "git.ymir" =
4 { hostname = "ymir.yggdrasil.li"; 11 { hostname = "ymir.yggdrasil.li";
5 user = "gitolite"; 12 user = "gitolite";
@@ -290,15 +297,15 @@
290 }; 297 };
291 "mathw0d" = 298 "mathw0d" =
292 { hostname = "mathw0d.mathinst.loc"; 299 { hostname = "mathw0d.mathinst.loc";
293 proxyJump = "mathw0h"; 300 proxyCommand = autosshProxy "mathw0h";
294 }; 301 };
295 "mathw0e" = 302 "mathw0e" =
296 { hostname = "mathw0e.mathinst.loc"; 303 { hostname = "mathw0e.mathinst.loc";
297 proxyJump = "mathw0h"; 304 proxyCommand = autosshProxy "mathw0h";
298 }; 305 };
299 "mathw0f" = 306 "mathw0f" =
300 { hostname = "mathw0f.mathinst.loc"; 307 { hostname = "mathw0f.mathinst.loc";
301 proxyJump = "mathw0h"; 308 proxyCommand = autosshProxy "mathw0h";
302 }; 309 };
303 "mathw0g" = 310 "mathw0g" =
304 { hostname = "mathw0g.mathinst.loc"; 311 { hostname = "mathw0g.mathinst.loc";
@@ -306,8 +313,8 @@
306 "mathw0h" = 313 "mathw0h" =
307 { hostname = "mathw0h.mathinst.loc"; 314 { hostname = "mathw0h.mathinst.loc";
308 }; 315 };
309 "proxy.mathw0g" = 316 "proxy.ssh.math.lmu.de" =
310 { hostname = "mathw0g.mathinst.loc"; 317 { hostname = "ssh.math.lmu.de";
311 extraOptions = { 318 extraOptions = {
312 ControlPath = "none"; 319 ControlPath = "none";
313 ExitOnForwardFailure = "yes"; 320 ExitOnForwardFailure = "yes";
@@ -317,7 +324,17 @@
317 }; 324 };
318 "proxy.mathw0h" = 325 "proxy.mathw0h" =
319 { hostname = "mathw0h.mathinst.loc"; 326 { hostname = "mathw0h.mathinst.loc";
320 proxyJump = "proxy.mathw0g"; 327 proxyCommand = autosshProxy "ssh.math.lmu.de";
328 extraOptions = {
329 ControlPath = "none";
330 ExitOnForwardFailure = "yes";
331 ServerAliveCountMax = "15";
332 ServerAliveInterval = "2";
333 };
334 };
335 "proxy.mathw0e" =
336 { hostname = "mathw0e.mathinst.loc";
337 proxyCommand = autosshProxy "mathw0h";
321 extraOptions = { 338 extraOptions = {
322 ControlPath = "none"; 339 ControlPath = "none";
323 ExitOnForwardFailure = "yes"; 340 ExitOnForwardFailure = "yes";
@@ -327,7 +344,7 @@
327 }; 344 };
328 "vrt-kvm06" = 345 "vrt-kvm06" =
329 { hostname = "vrt-kvm06"; 346 { hostname = "vrt-kvm06";
330 proxyJump = "mathw0e"; 347 proxyCommand = autosshProxy "mathw0e";
331 user = "root"; 348 user = "root";
332 extraOptions = { 349 extraOptions = {
333 PasswordAuthentication = "yes"; 350 PasswordAuthentication = "yes";
@@ -336,7 +353,7 @@
336 }; 353 };
337 "vrt-kvm05" = 354 "vrt-kvm05" =
338 { hostname = "vrt-kvm05"; 355 { hostname = "vrt-kvm05";
339 proxyJump = "mathw0e"; 356 proxyCommand = autosshProxy "mathw0e";
340 user = "root"; 357 user = "root";
341 extraOptions = { 358 extraOptions = {
342 PasswordAuthentication = "yes"; 359 PasswordAuthentication = "yes";
@@ -345,7 +362,7 @@
345 }; 362 };
346 "vrt-kvm04" = 363 "vrt-kvm04" =
347 { hostname = "vrt-kvm04"; 364 { hostname = "vrt-kvm04";
348 proxyJump = "mathw0e"; 365 proxyCommand = autosshProxy "mathw0e";
349 user = "root"; 366 user = "root";
350 extraOptions = { 367 extraOptions = {
351 PasswordAuthentication = "yes"; 368 PasswordAuthentication = "yes";
@@ -354,7 +371,7 @@
354 }; 371 };
355 "vrt-kvm02" = 372 "vrt-kvm02" =
356 { hostname = "vrt-kvm02"; 373 { hostname = "vrt-kvm02";
357 proxyJump = "mathw0e"; 374 proxyCommand = autosshProxy "mathw0e";
358 user = "root"; 375 user = "root";
359 extraOptions = { 376 extraOptions = {
360 PasswordAuthentication = "yes"; 377 PasswordAuthentication = "yes";
@@ -363,7 +380,7 @@
363 }; 380 };
364 "vrt-kvm03" = 381 "vrt-kvm03" =
365 { hostname = "vrt-kvm03"; 382 { hostname = "vrt-kvm03";
366 proxyJump = "mathw0e"; 383 proxyCommand = autosshProxy "mathw0e";
367 user = "root"; 384 user = "root";
368 extraOptions = { 385 extraOptions = {
369 PasswordAuthentication = "yes"; 386 PasswordAuthentication = "yes";
@@ -372,7 +389,7 @@
372 }; 389 };
373 "vrt-kvm01" = 390 "vrt-kvm01" =
374 { hostname = "vrt-kvm01"; 391 { hostname = "vrt-kvm01";
375 proxyJump = "mathw0e"; 392 proxyCommand = autosshProxy "mathw0e";
376 user = "root"; 393 user = "root";
377 extraOptions = { 394 extraOptions = {
378 PasswordAuthentication = "yes"; 395 PasswordAuthentication = "yes";
@@ -381,39 +398,44 @@
381 }; 398 };
382 "tts-www01" = 399 "tts-www01" =
383 { hostname = "tts-www01.mathinst.loc"; 400 { hostname = "tts-www01.mathinst.loc";
384 proxyJump = "mathw0h"; 401 proxyCommand = autosshProxy "mathw0h";
385 user = "root"; 402 user = "root";
386 }; 403 };
387 "vpn-wg01" = 404 "vpn-wg01" =
388 { hostname = "vpn-wg01.mathinst.loc"; 405 { hostname = "vpn-wg01.mathinst.loc";
389 proxyJump = "mathw0h"; 406 proxyCommand = autosshProxy "mathw0h";
390 user = "root"; 407 user = "root";
391 }; 408 };
392 "repo-apt01" = 409 "repo-apt01" =
393 { hostname = "repo-apt01.mathinst.loc"; 410 { hostname = "repo-apt01.mathinst.loc";
394 proxyJump = "mathw0h"; 411 proxyCommand = autosshProxy "mathw0h";
395 user = "root"; 412 user = "root";
396 }; 413 };
397 "ldap-lmumr01" = 414 "ldap-lmumr01" =
398 { hostname = "ldap-lmumr01.mathinst.loc"; 415 { hostname = "ldap-lmumr01.mathinst.loc";
399 proxyJump = "mathw0h"; 416 proxyCommand = autosshProxy "mathw0h";
400 user = "root"; 417 user = "root";
401 }; 418 };
402 "mail-mi01" = 419 "mail-mi01" =
403 { hostname = "mail-mi01.mathinst.loc"; 420 { hostname = "mail-mi01.mathinst.loc";
404 proxyJump = "mathw0h"; 421 proxyCommand = autosshProxy "mathw0h";
405 }; 422 };
406 "mail-www02" = 423 "mail-www02" =
407 { hostname = "mail-www02.mathinst.loc"; 424 { hostname = "mail-www02.mathinst.loc";
408 proxyJump = "mathw0h"; 425 proxyCommand = autosshProxy "mathw0h";
409 }; 426 };
410 "dpl-fai01" = 427 "dpl-fai01" =
411 { hostname = "dpl-fai01.mathinst.loc"; 428 { hostname = "dpl-fai01.mathinst.loc";
412 user = "root"; 429 user = "root";
413 }; 430 };
431 "dpl-fai02" =
432 { hostname = "dpl-fai02.mathinst.loc";
433 user = "root";
434 proxyJump = "mgmt01";
435 };
414 "math05" = 436 "math05" =
415 { hostname = "math05.mathinst.loc"; 437 { hostname = "math05.mathinst.loc";
416 proxyJump = "mathw0h"; 438 proxyCommand = autosshProxy "mathw0h";
417 extraOptions.KexAlgorithms = "+diffie-hellman-group1-sha1"; 439 extraOptions.KexAlgorithms = "+diffie-hellman-group1-sha1";
418 }; 440 };
419 "switch01" = 441 "switch01" =
@@ -439,20 +461,20 @@
439 }; 461 };
440 "www-mi01" = 462 "www-mi01" =
441 { hostname = "www-mi01.mathinst.loc"; 463 { hostname = "www-mi01.mathinst.loc";
442 proxyJump = "mathw0h"; 464 proxyCommand = autosshProxy "mathw0h";
443 }; 465 };
444 "cip04" = 466 "cip04" =
445 { hostname = "cip04.cipmath.loc"; 467 { hostname = "cip04.cipmath.loc";
446 proxyJump = "mathw0h"; 468 proxyCommand = autosshProxy "mathw0h";
447 }; 469 };
448 "mgmt-cls01" = 470 "mgmt-cls01" =
449 { user = "root"; 471 { user = "root";
450 hostname = "mgmt-cls01.cipmath.loc"; 472 hostname = "mgmt-cls01.cipmath.loc";
451 proxyJump = "ssh.math.lmu.de"; 473 proxyCommand = autosshProxy "ssh.math.lmu.de";
452 }; 474 };
453 "mgmt01" = 475 "mgmt01" =
454 { hostname = "mgmt01.mathinst.loc"; 476 { hostname = "mgmt01.mathinst.loc";
455 proxyJump = "mathw0h"; 477 proxyCommand = autosshProxy "mathw0h";
456 user = "root"; 478 user = "root";
457 }; 479 };
458 "ssh-lb01" = 480 "ssh-lb01" =
@@ -471,17 +493,17 @@
471 "rdlx02" = { hostname = "rdlx02.mathinst.loc"; proxyJump = "mgmt01"; }; 493 "rdlx02" = { hostname = "rdlx02.mathinst.loc"; proxyJump = "mgmt01"; };
472 "math0d" = 494 "math0d" =
473 { hostname = "math0d.mathinst.loc"; 495 { hostname = "math0d.mathinst.loc";
474 proxyJump = "mathw0h"; 496 proxyCommand = autosshProxy "mathw0h";
475 }; 497 };
476 "dhcp01" = 498 "dhcp01" =
477 { hostname = "dhcp01.mathinst.loc"; 499 { hostname = "dhcp01.mathinst.loc";
478 user = "root"; 500 user = "root";
479 proxyJump = "mathw0h"; 501 proxyCommand = autosshProxy "mathw0h";
480 }; 502 };
481 "dhcp02" = 503 "dhcp02" =
482 { hostname = "dhcp02.mathinst.loc"; 504 { hostname = "dhcp02.mathinst.loc";
483 user = "root"; 505 user = "root";
484 proxyJump = "mathw0h"; 506 proxyCommand = autosshProxy "mathw0h";
485 }; 507 };
486 "cc-gpu-l01" = 508 "cc-gpu-l01" =
487 { hostname = "cc-gpu-l01.mathinst.loc"; 509 { hostname = "cc-gpu-l01.mathinst.loc";
@@ -546,7 +568,7 @@
546 user = "root"; 568 user = "root";
547 }; 569 };
548 "nas*" = 570 "nas*" =
549 { proxyJump = "mathw0e"; 571 { proxyCommand = autosshProxy "mathw0e";
550 user = "admin"; 572 user = "admin";
551 extraOptions = { 573 extraOptions = {
552 PasswordAuthentication = "yes"; 574 PasswordAuthentication = "yes";
@@ -554,9 +576,4 @@
554 HostKeyAlgorithms = "+ecdsa-sha2-nistp256"; 576 HostKeyAlgorithms = "+ecdsa-sha2-nistp256";
555 }; 577 };
556 }; 578 };
557 "game01" =
558 { hostname = "game01.yggdrasil.li";
559 user = "factorio";
560 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
561 };
562} 579}
diff --git a/accounts/gkleen@sif/synadm/default.nix b/accounts/gkleen@sif/synadm/default.nix
new file mode 100644
index 00000000..0a8e0d4c
--- /dev/null
+++ b/accounts/gkleen@sif/synadm/default.nix
@@ -0,0 +1,9 @@
1{ config, pkgs, ... }:
2{
3 home.packages = with pkgs; [ synadm ];
4 sops.secrets."synadm.yaml" = {
5 format = "binary";
6 sopsFile = ./synadm_yaml;
7 path = config.xdg.configHome + "/synadm.yaml";
8 };
9}
diff --git a/accounts/gkleen@sif/synadm/synadm_yaml b/accounts/gkleen@sif/synadm/synadm_yaml
new file mode 100644
index 00000000..8d951ccc
--- /dev/null
+++ b/accounts/gkleen@sif/synadm/synadm_yaml
@@ -0,0 +1,15 @@
1{
2 "data": "ENC[AES256_GCM,data:qJy4Pmbbxja4jmW7OaHsD0mQZ7anZwLhiVmAgkavb+CqwWGDnUBXdz22/MHCbxng5NshcFSpBoCBhgY6B9V2bUiES6bH9AtMlDcs9ebKGMArBTUTnQ2MjWQGfQTqraWdNgy+n327uj9swwCH8EZXdYH/Hlv0t/re470W+VOHeXhGghQ3Y9IGz2sgfvMGr8QxaJNydZz85rgs5QUP/PglCwWIOw2mY1EX2vYwnmiAo49LmIEaxWvRi++KHaeBveDt0nlkJwzUlipL2VOKWxkgpK3yGucQn2mz+FRe1btp+4KGm8H17eUI9FO9sBwq,iv:kgM921ovwCgDYHQj3c5Rupy/8JxHehxUD2jb1k9Ik2Y=,tag:3TLQkJbv679VWy8V2TMugw==,type:str]",
3 "sops": {
4 "age": [
5 {
6 "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866",
7 "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6bzVHUGNxZTF2WC9MYmZr\neGdVVzJXN3lGdEk3cTBER3J6UTFtcUJna2d3CjdNQmRXd2haZW1MYlJzNkk1dWVD\nVTFQc2gvS0JrejJ6SFh2MXpPWDZpRE0KLS0tIE0wTC85bEpvSnlGdGFkZVFhNjFZ\nbzRiZkxMWUg2ODNVUlBmNFlPNGRrZlkK1VXLJWcssv3ETyZSSM/Hhn5VIaI9iov9\nzShZA9Zx/FX6PYTuUMC29pJ57gKourcIxa/7HwSv/xYn1A6WcYfgSg==\n-----END AGE ENCRYPTED FILE-----\n"
8 }
9 ],
10 "lastmodified": "2025-05-18T11:03:42Z",
11 "mac": "ENC[AES256_GCM,data:yonJC68PhilAgEHNNJQ8nO53Qo3rx/LnfiOWfuMm24bOUIH9QM3WZZxpigd7bHI4eC4TqRb4LvcSi0nEURTRAhwiTqGNrWbpw2Iv3n5dhLEN9aTcetG5ZuhaXqfVUoML45/ovdBZG/0l8+XIHqxN2M/g/h4JwKoR/6lqzcrVhgo=,iv:xvxBJwy+E5zUdjhGPdZPdy7tnBIEj50hfiDJFsS3wNg=,tag:L4Fas36ZOg4h0QQwC4gjNA==,type:str]",
12 "unencrypted_suffix": "_unencrypted",
13 "version": "3.10.2"
14 }
15}
diff --git a/accounts/gkleen@sif/systemd.nix b/accounts/gkleen@sif/systemd.nix
index cefcf4ea..90cccc58 100644
--- a/accounts/gkleen@sif/systemd.nix
+++ b/accounts/gkleen@sif/systemd.nix
@@ -6,7 +6,7 @@ let
6 cfg = config.home-manager.users.${userName}; 6 cfg = config.home-manager.users.${userName};
7 7
8 autossh-socks-script = pkgs.writeScript "autossh" '' 8 autossh-socks-script = pkgs.writeScript "autossh" ''
9 #!${pkgs.zsh}/bin/zsh -xe 9 #!${lib.getExe pkgs.zsh} -xe
10 10
11 host="''${1%:*}" 11 host="''${1%:*}"
12 port="''${1#*:}" 12 port="''${1#*:}"
@@ -15,31 +15,29 @@ let
15 cmd=() 15 cmd=()
16 16
17 if [[ -n "''${SSHPASS_SECRET}" ]]; then 17 if [[ -n "''${SSHPASS_SECRET}" ]]; then
18 cmd+=(${pkgs.sshpassSecret}/bin/sshpass-secret) 18 cmd+=(${lib.getExe' pkgs.sshpassSecret "sshpass-secret"})
19 cmd+=("''${(@s/:/)SSHPASS_SECRET}") 19 cmd+=("''${(@s/:/)SSHPASS_SECRET}")
20 cmd+=(--) 20 cmd+=(--)
21 fi 21 fi
22 22
23 cmd+=(${pkgs.openssh}/bin/ssh -vN -D localhost:''${port} "''${host}") 23 cmd+=(${lib.getExe' pkgs.openssh "ssh"} -vN -D 127.0.0.1:''${port} "''${host}")
24 24
25 ( exec -a "''${cmd[1]}" -- ''${cmd} ) & 25 ( exec -a "''${cmd[1]}" -- ''${cmd} ) &
26 pid=$! 26 pid=$!
27 27
28 newpid="" 28 newpid=""
29 i=200 29 i=200
30 while ! newpid=$(${pkgs.lsof}/bin/lsof -Pi @localhost:"''${port}" -sTCP:LISTEN -t); do 30 while ! newpid=$(${lib.getExe pkgs.lsof} -Pi @localhost:"''${port}" -sTCP:LISTEN -t); do
31 if ! kill -0 "''${pid}"; then 31 if ! kill -0 "''${pid}"; then
32 wait "''${pid}" 32 wait "''${pid}"
33 exit $? 33 exit $?
34 fi 34 fi
35 [[ "''${i}" -gt 0 ]] || exit 1 35 [[ "''${i}" -gt 0 ]] || exit 1
36 i=$((''${i} - 1)) 36 i=$((''${i} - 1))
37 ${pkgs.coreutils}/bin/sleep 0.1 37 ${lib.getExe' pkgs.coreutils "sleep"} 0.1
38 done 38 done
39 39
40 ${config.systemd.package}/bin/systemd-notify --ready 40 ${lib.getExe' config.systemd.package "systemd-notify"} --pid=''${newpid} --ready
41
42 wait "''${pid}" "''${newpid}"
43 ''; 41 '';
44in { 42in {
45 tmpfiles.rules = [ 43 tmpfiles.rules = [
@@ -48,11 +46,11 @@ in {
48 ]; 46 ];
49 47
50 services = { 48 services = {
51 sync-keepass = { 49 "sync-keepass@" = {
52 Service = { 50 Service = {
53 Type = "oneshot"; 51 Type = "oneshot";
54 WorkingDirectory = "~"; 52 WorkingDirectory = "~";
55 ExecStart = toString (pkgs.writers.writePython3 "sync-keepass" { 53 ExecStart = "${pkgs.writers.writePython3 "sync-keepass" {
56 libraries = with pkgs.python3Packages; [ python-dateutil ]; 54 libraries = with pkgs.python3Packages; [ python-dateutil ];
57 } '' 55 } ''
58 import json 56 import json
@@ -61,13 +59,13 @@ in {
61 from datetime import datetime 59 from datetime import datetime
62 from dateutil.tz import tzlocal 60 from dateutil.tz import tzlocal
63 from dateutil.parser import isoparse 61 from dateutil.parser import isoparse
64 from sys import stderr 62 from sys import stderr, argv
65 63
66 64
67 remote_fs = 'surtr' 65 remote_fs = 'surtr' if argv[1] == 'store.kdbx' else 'mathcloud'
68 remote_file = 'store.kdbx' 66 remote_file = argv[1]
69 target_file = expanduser('~/store.kdbx') 67 target_file = expanduser(f'~/{argv[1]}')
70 meta_file = expanduser('~/.store.kdbx.json') 68 meta_file = expanduser(f'~/.{argv[1]}.json')
71 69
72 upload_time = None 70 upload_time = None
73 our_last_upload_time = None 71 our_last_upload_time = None
@@ -117,13 +115,14 @@ in {
117 do_upload() 115 do_upload()
118 elif upload_time is not None and (mod_time is None or upload_time > mod_time) and (our_last_upload_time is None or upload_time > our_last_upload_time): # noqa: E501 116 elif upload_time is not None and (mod_time is None or upload_time > mod_time) and (our_last_upload_time is None or upload_time > our_last_upload_time): # noqa: E501
119 do_download() 117 do_download()
120 ''); 118 ''} \"%I\"";
121 Environment = [ "RCLONE_PASSWORD_COMMAND=\"${pkgs.coreutils}/bin/cat ${config.sops.secrets.gkleen-rclone.path}\"" "PATH=${pkgs.rclone}/bin" ]; 119 Environment = [ "RCLONE_PASSWORD_COMMAND=\"${pkgs.coreutils}/bin/cat ${config.sops.secrets.gkleen-rclone.path}\"" "PATH=${pkgs.rclone}/bin" ];
122 }; 120 };
123 }; 121 };
124 emacs = { 122 emacs = {
125 Unit = { 123 Unit = {
126 After = ["graphical-session-pre.target"]; 124 After = [ "graphical-session.target" ];
125 BindsTo = [ "graphical-session.target" ];
127 }; 126 };
128 }; 127 };
129 keepassxc = { 128 keepassxc = {
@@ -135,8 +134,8 @@ in {
135 Environment = [ "QT_QPA_PLATFORM=wayland" ]; 134 Environment = [ "QT_QPA_PLATFORM=wayland" ];
136 }; 135 };
137 Unit = { 136 Unit = {
138 Requires = ["graphical-session-pre.target"]; 137 After = [ "graphical-session.target" ];
139 After = ["graphical-session-pre.target"]; 138 BindsTo = [ "graphical-session.target" ];
140 }; 139 };
141 }; 140 };
142 mpris-proxy = { 141 mpris-proxy = {
@@ -145,7 +144,7 @@ in {
145 Service.ExecStart = "${pkgs.bluez}/bin/mpris-proxy"; 144 Service.ExecStart = "${pkgs.bluez}/bin/mpris-proxy";
146 Install.WantedBy = [ "default.target" ]; 145 Install.WantedBy = [ "default.target" ];
147 }; 146 };
148 "autossh-socks@proxy.mathw0h:8119" = { 147 "autossh-socks@proxy.ssh.math.lmu.de:8119" = {
149 Service = { 148 Service = {
150 Type = "notify"; 149 Type = "notify";
151 NotifyAccess = "all"; 150 NotifyAccess = "all";
@@ -153,7 +152,7 @@ in {
153 Restart = "always"; 152 Restart = "always";
154 RestartSec = "23s"; 153 RestartSec = "23s";
155 ExecStart = "${autossh-socks-script} \"%I\""; 154 ExecStart = "${autossh-socks-script} \"%I\"";
156 Environment = [ "SSHPASS_SECRET=gkleen@mathw0g.math.lmu.de" ]; 155 Environment = [ "SSHPASS_SECRET=gkleen@ssh.math.lmu.de" ];
157 }; 156 };
158 Unit = { 157 Unit = {
159 StopWhenUnneeded = true; 158 StopWhenUnneeded = true;
@@ -174,6 +173,38 @@ in {
174 StopWhenUnneeded = true; 173 StopWhenUnneeded = true;
175 }; 174 };
176 }; 175 };
176 "autossh-socks@proxy.mathw0h:8123" = {
177 Service = {
178 Type = "notify";
179 NotifyAccess = "all";
180 WorkingDirectory = "~";
181 Restart = "always";
182 RestartSec = "23s";
183 ExecStart = "${autossh-socks-script} \"%I\"";
184 Environment = [ "SSHPASS_SECRET=gkleen@mathw0h.mathinst.loc" ];
185 };
186 Unit = {
187 StopWhenUnneeded = true;
188 StartLimitInterval = "180s";
189 StartLimitBurst = 7;
190 };
191 };
192 "autossh-socks@proxy.mathw0e:8125" = {
193 Service = {
194 Type = "notify";
195 NotifyAccess = "all";
196 WorkingDirectory = "~";
197 Restart = "always";
198 RestartSec = "23s";
199 ExecStart = "${autossh-socks-script} \"%I\"";
200 Environment = [ "SSHPASS_SECRET=gkleen@mathw0e.mathinst.loc" ];
201 };
202 Unit = {
203 StopWhenUnneeded = true;
204 StartLimitInterval = "180s";
205 StartLimitBurst = 7;
206 };
207 };
177 swayidle = { 208 swayidle = {
178 Service = { 209 Service = {
179 RuntimeDirectory = "swayidle"; 210 RuntimeDirectory = "swayidle";
@@ -184,8 +215,8 @@ in {
184 WantedBy = ["graphical-session.target"]; 215 WantedBy = ["graphical-session.target"];
185 }; 216 };
186 Unit = { 217 Unit = {
187 Requires = ["graphical-session-pre.target"]; 218 After = [ "graphical-session.target" ];
188 After = ["graphical-session-pre.target"]; 219 PartOf = [ "graphical-session.target" ];
189 }; 220 };
190 Service = { 221 Service = {
191 ExecStart = lib.getExe pkgs.psi-notify; 222 ExecStart = lib.getExe pkgs.psi-notify;
@@ -198,6 +229,7 @@ in {
198 gtklock = { 229 gtklock = {
199 Unit = { 230 Unit = {
200 Requisite = ["graphical-session.target"]; 231 Requisite = ["graphical-session.target"];
232 After = [ "graphical-session.target" ];
201 PartOf = ["graphical-session.target"]; 233 PartOf = ["graphical-session.target"];
202 }; 234 };
203 Service = { 235 Service = {
@@ -205,53 +237,55 @@ in {
205 RuntimeDirectory = "gtklock"; 237 RuntimeDirectory = "gtklock";
206 CacheDirectory = "gtklock"; 238 CacheDirectory = "gtklock";
207 ExecStartPre = [ 239 ExecStartPre = [
208 "${pkgs.libsForQt5.qt5.qttools.bin}/bin/qdbus org.keepassxc.KeePassXC.MainWindow /keepassxc org.keepassxc.KeePassXC.MainWindow.lockAllDatabases" 240 "-${lib.getExe' pkgs.libsForQt5.qt5.qttools.bin "qdbus"} org.keepassxc.KeePassXC.MainWindow /keepassxc org.keepassxc.KeePassXC.MainWindow.lockAllDatabases"
209 "${config.systemd.package}/bin/systemctl --user stop gpg-agent.service" 241 "-${lib.getExe' config.systemd.package "systemctl"} --user stop gpg-agent.service"
210 (pkgs.writeShellScript "generate-css" '' 242 "-${lib.getExe pkgs.playerctl} -a pause"
211 set -x 243 "-${lib.getExe (pkgs.writeShellApplication {
212 export PATH="${lib.makeBinPath [cfg.programs.wpaperd.package pkgs.jq pkgs.coreutils pkgs.imagemagick pkgs.findutils]}:$PATH" 244 name = "generate-css";
245 runtimeInputs = with pkgs; [cfg.services.wpaperd.package jq coreutils imagemagick findutils];
246 text = ''
247 declare -A monitors
248 monitors=()
249 while IFS= read -r entry; do
250 path=$(jq -r ".path" <<<"$entry")
251 [[ -z "$path" || ! -f "$path" ]] && continue
252 blurred_path="$CACHE_DIRECTORY"/"$(b2sum -l 128 <<<"$path" | cut -d' ' -f1)"."''${path##*.}"
253 monitor=$(jq -r ".display" <<<"$entry")
254 if [[ ! -f "$blurred_path" ]]; then
255 mkdir -p "$(dirname "$blurred_path")"
256 magick "$path" -filter Gaussian -resize 6.25% -define filter:sigma=2.5 -resize 1600% "$blurred_path" &
257 fi
258 monitors+=([$monitor]="$blurred_path")
259 done < <(wpaperctl all-wallpapers -j | jq -c ".[]")
260 # wait
213 261
214 declare -A monitors 262 cp --no-preserve=mode ${pkgs.writeText "gtklock.css" ''
215 monitors=() 263 #window-box {
216 while IFS= read -r entry; do 264 padding: 64px;
217 path=$(jq -r ".path" <<<"$entry") 265 /* border: 1px solid black; */
218 [[ -z "$path" || ! -f "$path" ]] && continue 266 border-radius: 4px;
219 blurred_path="$CACHE_DIRECTORY"/"$(b2sum -l 128 <<<"$path" | cut -d' ' -f1)"."''${path##*.}" 267 box-shadow: rgba(0, 0, 0, 0.8) 0px 4px 12px;
220 monitor=$(jq -r ".display" <<<"$entry") 268 /* background-color: white; */
221 if [[ ! -f "$blurred_path" ]]; then 269 background-color: rgba(0, 0, 0, 0.5);
222 mkdir -p "$(dirname "$blurred_path")" 270 }
223 magick "$path" -filter Gaussian -resize 6.25% -define filter:sigma=2.5 -resize 1600% "$blurred_path" & 271 ''} "$RUNTIME_DIRECTORY"/style.css
224 fi 272 for monitor in "''${!monitors[@]}"; do
225 monitors+=([$monitor]="$blurred_path") 273 cat >>"$RUNTIME_DIRECTORY"/style.css <<EOF
226 done < <(wpaperctl all-wallpapers -j | jq -c ".[]") 274 window#''${monitor} {
227 wait 275 background-image: url("''${monitors[$monitor]}");
228 276 background-repeat: no-repeat;
229 cp --no-preserve=mode ${pkgs.writeText "gtklock.css" '' 277 background-size: 100% 100%;
230 #window-box { 278 background-origin: content-box;
231 padding: 64px;
232 /* border: 1px solid black; */
233 border-radius: 4px;
234 box-shadow: rgba(0, 0, 0, 0.8) 0px 4px 12px;
235 /* background-color: white; */
236 background-color: rgba(0, 0, 0, 0.5);
237 } 279 }
238 ''} "$RUNTIME_DIRECTORY"/style.css 280 EOF
239 for monitor in "''${!monitors[@]}"; do 281 done
240 cat >>"$RUNTIME_DIRECTORY"/style.css <<EOF 282 '';
241 window#''${monitor} { 283 })}"
242 background-image: url("''${monitors[$monitor]}");
243 background-repeat: no-repeat;
244 background-size: 100% 100%;
245 background-origin: content-box;
246 }
247 EOF
248 done
249 '')
250 ]; 284 ];
251 NotifyAccess = "all"; 285 NotifyAccess = "all";
252 ExecStart = ''${lib.getExe pkgs.gtklock} -s "''${RUNTIME_DIRECTORY}/style.css" -L ${pkgs.writeShellScript "after-lock" '' 286 ExecStart = ''${lib.getExe pkgs.gtklock} -s "''${RUNTIME_DIRECTORY}/style.css" -L ${pkgs.writeShellScript "after-lock" ''
253 ${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms off 287 ${lib.getExe cfg.programs.niri.package} msg action power-off-monitors
254 ${config.systemd.package}/bin/systemd-notify --ready 288 ${lib.getExe' config.systemd.package "systemd-notify"} --ready
255 ''}''; 289 ''}'';
256 }; 290 };
257 }; 291 };
@@ -299,30 +333,60 @@ in {
299 ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\""; 333 ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\"";
300 }; 334 };
301 }; 335 };
302 wpaperd = { 336 # wpaperd = {
303 Install = { 337 # Install = {
304 WantedBy = ["graphical-session.target"]; 338 # WantedBy = ["graphical-session.target"];
339 # };
340 # Unit = {
341 # After = [ "graphical-session.target" ];
342 # PartOf = [ "graphical-session.target" ];
343 # };
344 # Service = {
345 # ExecStart = lib.getExe cfg.services.wpaperd.package;
346 # Type = "simple";
347 # Restart = "always";
348 # RestartSec = "2s";
349 # };
350 # };
351 xembed-sni-proxy = {
352 Unit = {
353 PartOf = lib.mkForce ["tray.target"];
354 BindsTo = ["xwayland-satellite.service"];
355 After = ["xwayland-satellite.service"];
305 }; 356 };
357 };
358 poweralertd = {
306 Unit = { 359 Unit = {
307 BindsTo = ["graphical-session-pre.target"]; 360 After = ["graphical-session.target"];
308 After = ["graphical-session-pre.target"];
309 }; 361 };
310 Service = { 362 };
311 ExecStart = lib.getExe cfg.programs.wpaperd.package; 363 network-manager-applet = {
312 Type = "simple"; 364 Unit = {
313 Restart = "always"; 365 PartOf = lib.mkForce ["tray.target"];
314 RestartSec = "2s"; 366 };
367 };
368 udiskie = {
369 Unit = {
370 PartOf = lib.mkForce ["tray.target"];
371 };
372 };
373 blueman-applet = {
374 Unit = {
375 PartOf = lib.mkForce ["tray.target"];
376 };
377 Install = {
378 WantedBy = lib.mkForce ["tray.target"];
315 }; 379 };
316 }; 380 };
317 } // listToAttrs (map ({host, port}: nameValuePair "proxy-to-autossh-socks@${toString port}" { 381 } // listToAttrs (map ({host, port}: nameValuePair "proxy-to-autossh-socks@${toString port}" {
318 Unit = { 382 Unit = {
319 Requires = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; 383 BindsTo = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"];
320 After = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; 384 After = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"];
321 }; 385 };
322 Service = { 386 Service = {
323 ExecStart = "${config.systemd.package}/lib/systemd/systemd-socket-proxyd --exit-idle-time=10s localhost:${toString (port + 1)}"; 387 ExecStart = "${config.systemd.package}/lib/systemd/systemd-socket-proxyd --exit-idle-time=60s 127.0.0.1:${toString (port + 1)}";
324 }; 388 };
325 }) [{ host = "proxy.mathw0h"; port = 8118; } { host = "proxy.vidhar"; port = 8120; }]); 389 }) [{ host = "proxy.ssh.math.lmu.de"; port = 8118; } { host = "proxy.vidhar"; port = 8120; } { host = "proxy.mathw0h"; port = 8122; } { host = "proxy.mathw0e"; port = 8124; }]);
326 sockets = listToAttrs (map (port: nameValuePair "proxy-to-autossh-socks@${toString port}" { 390 sockets = listToAttrs (map (port: nameValuePair "proxy-to-autossh-socks@${toString port}" {
327 Socket = { 391 Socket = {
328 ListenStream = "%I"; 392 ListenStream = "%I";
@@ -330,7 +394,7 @@ in {
330 Install = { 394 Install = {
331 WantedBy = ["default.target"]; 395 WantedBy = ["default.target"];
332 }; 396 };
333 }) [8118 8120]) // { 397 }) [8118 8120 8122 8124]) // {
334 "yt-dlp" = { 398 "yt-dlp" = {
335 Socket = { 399 Socket = {
336 SocketMode = "0600"; 400 SocketMode = "0600";
@@ -344,7 +408,7 @@ in {
344 }; 408 };
345 }; 409 };
346 timers = { 410 timers = {
347 sync-keepass = { 411 "sync-keepass@store.kdbx" = {
348 Timer = { 412 Timer = {
349 OnActiveSec = "1m"; 413 OnActiveSec = "1m";
350 OnUnitActiveSec = "1m"; 414 OnUnitActiveSec = "1m";
@@ -354,6 +418,16 @@ in {
354 WantedBy = ["default.target"]; 418 WantedBy = ["default.target"];
355 }; 419 };
356 }; 420 };
421 "sync-keepass@rz.kdbx" = {
422 Timer = {
423 OnActiveSec = "1d";
424 OnUnitActiveSec = "1d";
425 };
426
427 Install = {
428 WantedBy = ["default.target"];
429 };
430 };
357 }; 431 };
358 targets = { 432 targets = {
359 graphical-session = { 433 graphical-session = {
@@ -364,6 +438,9 @@ in {
364 }; 438 };
365 tray = { 439 tray = {
366 Unit = { 440 Unit = {
441 PartOf = [ "graphical-session.target" ];
442 Requires = [ "waybar.service" ];
443 After = [ "graphical-session.target" "waybar.service" ];
367 Wants = ["blueman-applet.service" "udiskie.service" "network-manager-applet.service"]; 444 Wants = ["blueman-applet.service" "udiskie.service" "network-manager-applet.service"];
368 }; 445 };
369 }; 446 };
diff --git a/accounts/gkleen@sif/utils/async-yt-dlp.nix b/accounts/gkleen@sif/utils/async-yt-dlp.nix
new file mode 100644
index 00000000..c3b82ec5
--- /dev/null
+++ b/accounts/gkleen@sif/utils/async-yt-dlp.nix
@@ -0,0 +1,57 @@
1{ writers, python3Packages, ... }:
2
3writers.writePython3Bin "async-yt-dlp" {
4 libraries = with python3Packages; [ yt-dlp ];
5 flakeIgnore = ["E501"];
6} ''
7import sys
8import os
9
10import yt_dlp
11import yt_dlp.options
12from collections import namedtuple
13import socket
14from pathlib import Path
15import json
16
17create_parser = yt_dlp.options.create_parser
18
19
20def parse_patched_options(opts):
21 patched_parser = create_parser()
22 patched_parser.defaults.update({
23 'ignoreerrors': False,
24 'retries': 0,
25 'fragment_retries': 0,
26 'extract_flat': False,
27 'concat_playlist': 'never',
28 })
29 yt_dlp.options.create_parser = lambda: patched_parser
30 try:
31 return yt_dlp.parse_options(opts)
32 finally:
33 yt_dlp.options.create_parser = create_parser
34
35
36default_opts = parse_patched_options([]).ydl_opts
37
38
39def cli_to_api(opts):
40 opts = parse_patched_options(opts)
41 urls = opts.urls
42 opts = opts.ydl_opts
43
44 diff = {k: v for k, v in opts.items() if default_opts[k] != v}
45 if 'postprocessors' in diff:
46 diff['postprocessors'] = [pp for pp in diff['postprocessors']
47 if pp not in default_opts['postprocessors']]
48 return namedtuple('Options', ('params', 'urls'))(diff, urls)
49
50
51if __name__ == '__main__':
52 opts = cli_to_api(sys.argv[1:])
53 with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock:
54 sock.connect(str(Path(os.environ["XDG_RUNTIME_DIR"]) / "yt-dlp.sock").encode('utf-8'))
55 with sock.makefile(mode='w', buffering=1, encoding='utf-8') as fh:
56 json.dump(opts._asdict(), fh)
57''
diff --git a/accounts/gkleen@sif/utils/sieve-edit.nix b/accounts/gkleen@sif/utils/sieve-edit.nix
new file mode 100644
index 00000000..f985a3f6
--- /dev/null
+++ b/accounts/gkleen@sif/utils/sieve-edit.nix
@@ -0,0 +1,24 @@
1pkgs@{ lib, resholve, zsh, sieve-connect, sops, ... }:
2
3resholve.writeScriptBin "sieve-edit" {
4 inputs = with pkgs; [sieve-connect sops];
5 interpreter = lib.getExe zsh;
6 execer = with pkgs; [
7 "cannot:${lib.getExe sieve-connect}"
8 "cannot:${lib.getExe sops}"
9 ];
10} ''
11 host=$1; shift
12 case "$host" in
13 surtr)
14 sieve-connect -s surtr.yggdrasil.li -m EXTERNAL --clientkey <(sops decrypt $HOME/projects/machines/hosts/surtr/email/ca/gkleen@sif.key) --clientcert $HOME/projects/machines/hosts/surtr/email/ca/gkleen@sif.crt --edit --remotesieve sieve
15 ;;
16 ymir)
17 sieve-connect -s ymir.yggdrasil.li -u gkleen --edit --remotesieve sieve
18 ;;
19 *)
20 echo "Unknown host: ‘$host’" >&2
21 return 2
22 ;;
23 esac
24''
diff --git a/accounts/gkleen@sif/zshrc b/accounts/gkleen@sif/zshrc
index e3f675a1..abc200c6 100644
--- a/accounts/gkleen@sif/zshrc
+++ b/accounts/gkleen@sif/zshrc
@@ -2,17 +2,14 @@ dir() {
2 curlArchive=false 2 curlArchive=false
3 templateArchive="" 3 templateArchive=""
4 repoUrl="" 4 repoUrl=""
5 nixShell=""
6 findNix=false
7 dir="" 5 dir=""
8 forceShell=false 6 forceShell=false
9 wormhole=false 7 wormhole=false
10 gitWorktree="" 8 gitWorktree=""
11 # notmuchMsg=""
12 quickserve=false
13 modifyPDF="" 9 modifyPDF=""
10 miniserve=false
14 11
15 while getopts ':t:a:s:Sd:ir:wqg:p:' arg; do 12 while getopts ':t:a:d:ir:wg:p:m' arg; do
16 case $arg in 13 case $arg in
17 "t") ;; 14 "t") ;;
18 "a") 15 "a")
@@ -23,16 +20,13 @@ dir() {
23 templateArchive=${OPTARG:a} 20 templateArchive=${OPTARG:a}
24 fi 21 fi
25 ;; 22 ;;
26 "s") nixShell=${OPTARG:a} ;;
27 "S") findNix=true ;;
28 "d") dir=${OPTARG} ;; 23 "d") dir=${OPTARG} ;;
29 "i") forceShell=true ;; 24 "i") forceShell=true ;;
30 "r") repoUrl=${OPTARG} ;; 25 "r") repoUrl=${OPTARG} ;;
31 "w") wormhole=true ;; 26 "w") wormhole=true ;;
32 "g") gitWorktree=${OPTARG} ;; 27 "g") gitWorktree=${OPTARG} ;;
33 # "n") notmuchMsg=${OPTARG} ;;
34 "q") quickserve=true ;;
35 "p") modifyPDF=${OPTARG:a} ;; 28 "p") modifyPDF=${OPTARG:a} ;;
29 "m") miniserve=true ;;
36 *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;; 30 *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;;
37 esac 31 esac
38 done 32 done
@@ -56,20 +50,34 @@ dir() {
56 gitWorktree="" 50 gitWorktree=""
57 fi 51 fi
58 52
53 miniservePIDFile=""
54 if [[ ${miniserve} = "true" ]]; then
55 miniservePIDFile=$(mktemp --tmpdir --suffix=.pid)
56 fi
57
59 cleanup() 58 cleanup()
60 { 59 {
61 cd ${modifyPDF:h} 60 if [[ -n ${modifyPDF} ]]; then
62 [[ -n ${modifyPDF} ]] && nix shell nixos#imagemagick -c convert -verbose ${dir}/${modifyPDF:t:r}_*.png(on) ${modifyPDF} 61 cd ${modifyPDF:h}
62 typeset -a pages
63 eval 'pages=(${dir}/${modifyPDF:t:r}_*.png(on))'
64 magick -verbose "$pages" ${modifyPDF}
65 modifyPDF=""
66 fi
67 if [[ -n ${miniservePIDFile} ]]; then
68 command kill --verbose -- $(cat ${miniservePIDFile}) && wait $(cat ${miniservePIDFile})
69 miniservePIDFile=""
70 fi
63 } 71 }
64 72
65 ( 73 (
74 set -o localoptions -o localtraps
75 trap 'return 1' INT TERM
66 trap cleanup EXIT 76 trap cleanup EXIT
67 77
68 cd ${dir} 78 cd ${dir}
69 export dir; 79 export dir;
70 80
71 ${findNix} && { nixShell=$(findNix) || return $? }
72
73 [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} . 81 [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} .
74 82
75 [[ -n ${modifyPDF} ]] && templateArchive=${modifyPDF} 83 [[ -n ${modifyPDF} ]] && templateArchive=${modifyPDF}
@@ -82,7 +90,7 @@ dir() {
82 } 90 }
83 trap cleanup EXIT 91 trap cleanup EXIT
84 92
85 if ${curlArchive}; then 93 if [[ $curlArchive = "true" ]]; then
86 archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}") 94 archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}")
87 95
88 curl -L -o ${archiveFile} ${templateArchive} 96 curl -L -o ${archiveFile} ${templateArchive}
@@ -91,14 +99,14 @@ dir() {
91 fi 99 fi
92 100
93 unpack=true 101 unpack=true
94 while ${unpack}; do 102 while [[ $unpack = "true" ]]; do
95 case $(file --brief --mime-type --dereference ${templateArchive}) in 103 case $(file --brief --mime-type --dereference ${templateArchive}) in
96 application/zip) 104 application/zip)
97 unzip ${templateArchive} 105 unzip ${templateArchive}
98 unpack=false 106 unpack=false
99 ;; 107 ;;
100 application/vnd.debian.binary-package) 108 application/vnd.debian.binary-package)
101 nix shell nixos#binutils --command ar x ${templateArchive} 109 ar x ${templateArchive}
102 mkdir control data 110 mkdir control data
103 tar -C control -xvaf control.* 111 tar -C control -xvaf control.*
104 tar -C data -xvaf data.* 112 tar -C data -xvaf data.*
@@ -106,7 +114,7 @@ dir() {
106 ;; 114 ;;
107 application/x-rpm) 115 application/x-rpm)
108 cpioArchive=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t:r}.cpio") 116 cpioArchive=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t:r}.cpio")
109 nix shell nixos#busybox --command rpm2cpio ${templateArchive} > ${cpioArchive} 117 rpm2cpio ${templateArchive} > ${cpioArchive}
110 templateArchive=${cpioArchive} 118 templateArchive=${cpioArchive}
111 unpack=true 119 unpack=true
112 ;; 120 ;;
@@ -115,12 +123,12 @@ dir() {
115 unpack=false 123 unpack=false
116 ;; 124 ;;
117 application/pdf) 125 application/pdf)
118 nix shell nixos#ghostscript nixos#imagemagick -c convert -verbose -density 400 ${templateArchive} ${modifyPDF:t:r}_%0d.png 126 magick -verbose -density 400 ${templateArchive} ${modifyPDF:t:r}_%0d.png
119 unpack=false 127 unpack=false
120 ;; 128 ;;
121 application/octet-stream) 129 application/octet-stream)
122 if [[ $(file --brief --dereferenc ${templateArchive}) =~ Squashfs ]]; then 130 if [[ $(file --brief --dereference ${templateArchive}) =~ Squashfs ]]; then
123 nix shell nixos#squashfsTools -c unsquashfs -d . ${templateArchive} 131 unsquashfs -d . ${templateArchive}
124 unpack=false 132 unpack=false
125 fi 133 fi
126 ;; 134 ;;
@@ -134,25 +142,21 @@ dir() {
134 fi 142 fi
135 143
136 144
137 ${wormhole} && wormhole receive --accept-file 145 [[ $wormhole = "true" ]] && wormhole receive --accept-file
138 146
139 147
140 if ${quickserve}; then 148 if [[ ${#@} -gt 0 ]]; then
141 quickserve --root . --upload . --show-hidden --tar gz 149 ${@}
142 fi 150 fi
143 151
152 cd $(pwd) # Needed for mounting to work
144 153
145 if [[ ${#@} -eq 0 ]] || ${forceShell}; then 154 if [[ ${miniserve} = "true" ]]; then
146 if [[ ${#@} -gt 0 ]]; then 155 miniserve --random-route --hidden --enable-tar-gz --enable-zip . &
147 if [[ -z ${nixShell} ]]; then 156 echo $! > "${miniservePIDFile}"
148 ${@} 157 fi
149 else
150 nix-shell ${nixShell} --run "${@}"
151 fi
152 fi
153
154 cd $(pwd) # Needed for mounting to work
155 158
159 if [[ ${#@} -eq 0 ]] && [[ ${miniserve} != "true" ]] || [[ $forceShell = "true" ]]; then
156 isSingleDir() { 160 isSingleDir() {
157 typeset -a contents 161 typeset -a contents
158 contents=(*(N) .*(N)) 162 contents=(*(N) .*(N))
@@ -166,18 +170,9 @@ dir() {
166 } 170 }
167 while d=$(isSingleDir); do cd ${d}; done 171 while d=$(isSingleDir); do cd ${d}; done
168 172
169 173 zsh
170 if [[ -z ${nixShell} ]]; then 174 elif [[ ${miniserve} == "true" ]]; then
171 zsh 175 wait $(cat "${miniservePIDFile}")
172 else
173 nix-shell ${nixShell} --run zsh
174 fi
175 else
176 if [[ -z ${nixShell} ]]; then
177 ${@}
178 else
179 nix-shell ${nixShell} --run "${@}"
180 fi
181 fi 176 fi
182 ) 177 )
183} 178}
@@ -185,27 +180,30 @@ dir() {
185tmpdir() { 180tmpdir() {
186 cleanup() 181 cleanup()
187 { 182 {
188 cd / 183 cd /
189 unmount() { 184 unmount() {
190 printf "Unmounting %s\n" ${1} >&2 185 printf "Unmounting %s\n" ${1} >&2
191 fusermount -u ${1} || umount ${1} || sudo umount ${1} 186 fusermount -u ${1} || umount ${1} || sudo umount ${1}
192 } 187 }
193 188
194 if mountpoint -q -- ${dir}; then 189 if [[ -n ${dir} ]]; then
195 unmount ${dir} || return $? 190 if mountpoint -q -- ${dir}; then
196 else 191 unmount ${dir} || return $?
197 while read -d $'\0' subDir; do 192 else
198 mountpoint -q -- ${subDir} || continue 193 while read -d $'\0' subDir; do
199 unmount ${subDir} || return $? 194 mountpoint -q -- ${subDir} || continue
200 done <<<$(find ${dir} -xdev -type d -print0 | sort -zr) 195 unmount ${subDir} || return $?
201 fi 196 done <<<$(find ${dir} -xdev -type d -print0 | sort -zr)
202 197 fi
203 rm -rfv --one-file-system -- ${dir} 198
199 rm -rfv --one-file-system -- ${dir}
200 dir=""
201 fi
204 } 202 }
205 203
206 local tmpdir="" 204 local tmpdir=""
207 205
208 while getopts ':t:a:s:Sd:ir:wqg:p:' arg; do 206 while getopts ':t:a:d:ir:wg:p:m' arg; do
209 case $arg in 207 case $arg in
210 "t") tmpdir="=${OPTARG}" ;; 208 "t") tmpdir="=${OPTARG}" ;;
211 "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;; 209 "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;;
@@ -213,6 +211,8 @@ tmpdir() {
213 done 211 done
214 212
215 ( 213 (
214 set -o localoptions -o localtraps
215 trap 'return 1' INT TERM
216 trap cleanup EXIT 216 trap cleanup EXIT
217 217
218 218
@@ -234,16 +234,6 @@ public-ip() {
234 curl -s -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip' 234 curl -s -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip'
235} 235}
236 236
237nix-ghci() {
238 pkgExpr=""
239 if [[ ${#@} -gt 0 ]]; then
240 pkgExpr="${1}"
241 shift
242 fi
243
244 nix-shell -p "with (import <nixpkgs> {}); pkgs.haskellPackages.ghcWithPackages (p: with p; [${pkgExpr}])" --run "ghci ${@}"
245}
246
247swap() { 237swap() {
248 f1=${1} 238 f1=${1}
249 f2=${2} 239 f2=${2}
@@ -271,14 +261,6 @@ l() {
271 ls --long --binary --git --time-style=iso --header $@ 261 ls --long --binary --git --time-style=iso --header $@
272} 262}
273 263
274re() {
275 systemctl --restart $@
276}
277
278ure() {
279 systemctl --user --restart $@
280}
281
282ssh-installer() { 264ssh-installer() {
283 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentityFile=~/.ssh/gkleen@sif.midgard.yggdrasil $@ 265 ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentityFile=~/.ssh/gkleen@sif.midgard.yggdrasil $@
284} 266}
@@ -306,20 +288,7 @@ done < <(find ~/projects ~/uni -regextype posix-extended -maxdepth 2 -type d -re
306 sed -zr 's|(.*/([0-9]{2}[ws])/(.+))|\1 \2 \3|' | \ 288 sed -zr 's|(.*/([0-9]{2}[ws])/(.+))|\1 \2 \3|' | \
307 sort -z -r -k2 | sort -z -s -k3 | uniq -z -f 2) 289 sort -z -r -k2 | sort -z -s -k3 | uniq -z -f 2)
308 290
309alias '..'='cd ..'
310alias rzadm=$'tmpdir -i sh -c \'mkdir adm; sshfs gkleen@mgmt01:/adm adm\'' 291alias rzadm=$'tmpdir -i sh -c \'mkdir adm; sshfs gkleen@mgmt01:/adm adm\''
311alias mathcloud=$'tmpdir -i rclone mount --daemon mathcloud:// .' 292alias mathcloud=$'tmpdir -i rclone mount --daemon mathcloud:// .'
312alias -g L='| less'
313alias -g S='&> /dev/null'
314alias -g G='| grep'
315alias -g B='&> /dev/null &'
316alias -g BB='&> /dev/null &!'
317 293
318export DEFAULT_USER=gkleen 294export DEFAULT_USER=gkleen
319
320bindkey -e
321bindkey ';5C' emacs-forward-word
322bindkey ';5D' emacs-backward-word
323bindkey '^[[1;5C' emacs-forward-word
324bindkey '^[[1;5D' emacs-backward-word
325bindkey '^H' backward-kill-word