summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--_sources/generated.json62
-rw-r--r--_sources/generated.nix56
-rw-r--r--accounts/gkleen@sif/default.nix339
-rw-r--r--accounts/gkleen@sif/dunst-settings.nix45
-rw-r--r--accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf4
-rw-r--r--accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf4
-rw-r--r--accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf4
-rw-r--r--accounts/gkleen@sif/dunstrc.d/10-brightness.conf5
-rw-r--r--accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf5
-rw-r--r--accounts/gkleen@sif/dunstrc.d/20-element.conf3
-rw-r--r--accounts/gkleen@sif/dunstrc.d/20-kitty.conf3
-rw-r--r--accounts/gkleen@sif/dunstrc.d/20-mail.conf3
-rw-r--r--accounts/gkleen@sif/dunstrc.d/20-zulip.conf3
-rw-r--r--accounts/gkleen@sif/emacs.el2
-rw-r--r--accounts/gkleen@sif/hyprland.nix424
-rw-r--r--accounts/gkleen@sif/libvirt/default.nix20
-rw-r--r--accounts/gkleen@sif/niri/default.nix577
-rw-r--r--accounts/gkleen@sif/niri/mako.nix50
-rw-r--r--accounts/gkleen@sif/niri/waybar.nix338
-rw-r--r--accounts/gkleen@sif/systemd.nix42
-rw-r--r--accounts/gkleen@sif/taffybar/default.nix2
-rw-r--r--accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal32
-rw-r--r--accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs111
-rw-r--r--accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs101
-rw-r--r--accounts/gkleen@sif/taffybar/src/taffybar.hs89
-rw-r--r--accounts/gkleen@sif/taffybar/taffybar.css146
-rw-r--r--accounts/gkleen@sif/xmonad/.gitignore4
-rw-r--r--accounts/gkleen@sif/xmonad/default.nix7
-rw-r--r--accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs127
-rw-r--r--accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs94
-rw-r--r--accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs105
-rw-r--r--accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs246
-rw-r--r--accounts/gkleen@sif/xmonad/package.yaml31
-rw-r--r--accounts/gkleen@sif/xmonad/stack.nix17
-rw-r--r--accounts/gkleen@sif/xmonad/stack.yaml10
-rw-r--r--accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix21
-rw-r--r--accounts/gkleen@sif/xmonad/xmonad.hs939
-rw-r--r--flake.lock202
-rw-r--r--flake.nix21
-rw-r--r--hosts/sif/default.nix52
-rw-r--r--hosts/sif/hw.nix19
-rw-r--r--hosts/surtr/default.nix2
-rw-r--r--hosts/surtr/dns/default.nix2
-rw-r--r--hosts/surtr/dns/keys/immich.yggdrasil.li_acme24
-rw-r--r--hosts/surtr/dns/zones/li.yggdrasil.soa10
-rw-r--r--hosts/surtr/immich.nix66
-rw-r--r--hosts/surtr/tls/tsig_key.gup4
-rw-r--r--hosts/surtr/tls/tsig_keys/immich.yggdrasil.li24
-rw-r--r--hosts/vidhar/default.nix4
-rw-r--r--hosts/vidhar/immich.nix10
-rw-r--r--hosts/vidhar/network/ruleset.nft7
-rw-r--r--hosts/vidhar/pgbackrest/default.nix9
-rw-r--r--hosts/vidhar/postgresql.nix36
-rw-r--r--hosts/vidhar/zfs.nix2
-rw-r--r--modules/niri.nix6
-rw-r--r--modules/uucp.nix27
-rw-r--r--nvfetcher.toml4
-rw-r--r--overlays/mako.nix5
-rw-r--r--overlays/prometheus-lvm-exporter.nix2
-rw-r--r--overlays/uucp/default.nix9
-rw-r--r--overlays/uucp/mailprogram.patch16
-rw-r--r--overlays/wttrbar/default.nix7
-rw-r--r--overlays/wttrbar/icons.patch154
-rw-r--r--system-profiles/core/default.nix15
-rw-r--r--system-profiles/niri-flake.nix4
-rw-r--r--system-profiles/niri-unstable.nix11
-rw-r--r--user-profiles/mpv/default.nix5
-rw-r--r--user-profiles/yt-dlp.nix2
68 files changed, 1575 insertions, 3257 deletions
diff --git a/_sources/generated.json b/_sources/generated.json
index 06346969..f73b8190 100644
--- a/_sources/generated.json
+++ b/_sources/generated.json
@@ -36,7 +36,7 @@
36 }, 36 },
37 "bpf-examples": { 37 "bpf-examples": {
38 "cargoLocks": null, 38 "cargoLocks": null,
39 "date": "2024-01-31", 39 "date": "2025-01-03",
40 "extract": null, 40 "extract": null,
41 "name": "bpf-examples", 41 "name": "bpf-examples",
42 "passthru": null, 42 "passthru": null,
@@ -48,12 +48,12 @@
48 "name": null, 48 "name": null,
49 "owner": "xdp-project", 49 "owner": "xdp-project",
50 "repo": "bpf-examples", 50 "repo": "bpf-examples",
51 "rev": "5343ed3377471c7b7ef2237526c8bdc0f00a0cef", 51 "rev": "8d53e6fc46ae625bd16b38eb1007ece99460eada",
52 "sha256": "sha256-vKVI8pQ17BNWLKm8wwpyNkLslnB9E2CAZTS6EP5lDT0=", 52 "sha256": "sha256-BUncjyaywmtSMVhbWZDy9XiNlGJet8Z0lzmUqm3f+HU=",
53 "sparseCheckout": [], 53 "sparseCheckout": [],
54 "type": "github" 54 "type": "github"
55 }, 55 },
56 "version": "5343ed3377471c7b7ef2237526c8bdc0f00a0cef" 56 "version": "8d53e6fc46ae625bd16b38eb1007ece99460eada"
57 }, 57 },
58 "emacs-scratch_el": { 58 "emacs-scratch_el": {
59 "cargoLocks": null, 59 "cargoLocks": null,
@@ -105,11 +105,31 @@
105 "passthru": null, 105 "passthru": null,
106 "pinned": false, 106 "pinned": false,
107 "src": { 107 "src": {
108 "sha256": "sha256-s6oV77sOPYAd8CA51KZK6nydWIh8oK2+SUpPfm7yehg=", 108 "sha256": "sha256-afJuTByGUMU6kFqGGa3pbPaFVdYGcJYiR0RfDNYNgDk=",
109 "type": "tarball", 109 "type": "tarball",
110 "url": "https://github.com/wofr06/lesspipe/archive/refs/tags/v2.16.tar.gz" 110 "url": "https://github.com/wofr06/lesspipe/archive/refs/tags/v2.17.tar.gz"
111 }, 111 },
112 "version": "2.16" 112 "version": "2.17"
113 },
114 "mako": {
115 "cargoLocks": null,
116 "date": "2025-01-19",
117 "extract": null,
118 "name": "mako",
119 "passthru": null,
120 "pinned": false,
121 "src": {
122 "deepClone": false,
123 "fetchSubmodules": false,
124 "leaveDotGit": false,
125 "name": null,
126 "rev": "57a258c1f8861200e0623153f1b79065d4ddabd8",
127 "sha256": "sha256-9PcZLpIfGR8SmZf5e2rDZhF+y3kfSaFw5DneDXHMGTc=",
128 "sparseCheckout": [],
129 "type": "git",
130 "url": "https://github.com/emersion/mako"
131 },
132 "version": "57a258c1f8861200e0623153f1b79065d4ddabd8"
113 }, 133 },
114 "mpv-autosave": { 134 "mpv-autosave": {
115 "cargoLocks": null, 135 "cargoLocks": null,
@@ -217,7 +237,7 @@
217 }, 237 },
218 "mpv-subselect": { 238 "mpv-subselect": {
219 "cargoLocks": null, 239 "cargoLocks": null,
220 "date": "2024-08-31", 240 "date": "2024-12-22",
221 "extract": null, 241 "extract": null,
222 "name": "mpv-subselect", 242 "name": "mpv-subselect",
223 "passthru": null, 243 "passthru": null,
@@ -227,13 +247,13 @@
227 "fetchSubmodules": false, 247 "fetchSubmodules": false,
228 "leaveDotGit": false, 248 "leaveDotGit": false,
229 "name": null, 249 "name": null,
230 "rev": "9b0043ba6042ba01fdd2533281313b70cbc98be4", 250 "rev": "77d0148aa6aa952f07f06212cabe32d54dfdf49e",
231 "sha256": "sha256-ZLWwkwrFaOg7PnuC23VaZ0P3zMhm1JmVf0eH9lqO0BY=", 251 "sha256": "sha256-VxwwTxE8c8rRQt/m2NA7cRC7+7O1ItYFFGv81nxqIxg=",
232 "sparseCheckout": [], 252 "sparseCheckout": [],
233 "type": "git", 253 "type": "git",
234 "url": "https://github.com/CogentRedTester/mpv-sub-select" 254 "url": "https://github.com/CogentRedTester/mpv-sub-select"
235 }, 255 },
236 "version": "9b0043ba6042ba01fdd2533281313b70cbc98be4" 256 "version": "77d0148aa6aa952f07f06212cabe32d54dfdf49e"
237 }, 257 },
238 "mpv-youtube-quality": { 258 "mpv-youtube-quality": {
239 "cargoLocks": null, 259 "cargoLocks": null,
@@ -291,11 +311,11 @@
291 "passthru": null, 311 "passthru": null,
292 "pinned": false, 312 "pinned": false,
293 "src": { 313 "src": {
294 "sha256": "sha256-mA84Bnq5JF0BGfqHhcCzTef5nDotLgQuiyg3/zOPqTE=", 314 "sha256": "sha256-Nz/lBhQbzWSnOKN4n0OUdJzDTpf3mfY0+FXoCqF03TU=",
295 "type": "tarball", 315 "type": "tarball",
296 "url": "https://github.com/hansmi/prometheus-lvm-exporter/archive/refs/tags/v0.3.3.tar.gz" 316 "url": "https://github.com/hansmi/prometheus-lvm-exporter/archive/refs/tags/v0.4.0.tar.gz"
297 }, 317 },
298 "version": "0.3.3" 318 "version": "0.4.0"
299 }, 319 },
300 "psql-versioning": { 320 "psql-versioning": {
301 "cargoLocks": null, 321 "cargoLocks": null,
@@ -381,7 +401,7 @@
381 }, 401 },
382 "v4l2loopback": { 402 "v4l2loopback": {
383 "cargoLocks": null, 403 "cargoLocks": null,
384 "date": "2024-11-26", 404 "date": "2025-01-18",
385 "extract": null, 405 "extract": null,
386 "name": "v4l2loopback", 406 "name": "v4l2loopback",
387 "passthru": null, 407 "passthru": null,
@@ -393,12 +413,12 @@
393 "name": null, 413 "name": null,
394 "owner": "umlaeute", 414 "owner": "umlaeute",
395 "repo": "v4l2loopback", 415 "repo": "v4l2loopback",
396 "rev": "e750af9eb17d729b8c5257a4bcd2faba2b28029c", 416 "rev": "39ad8a43522c18b5e4f4363ce053f604312fc413",
397 "sha256": "sha256-ePA1LcxQInrLLpbZ7Wljv75lWl6V6s9KkdMp0tF1vhk=", 417 "sha256": "sha256-A1p5ZfoMlw6/J3vBdQcXMvERdyBnqs9Ca+0LcLnu7b8=",
398 "sparseCheckout": [], 418 "sparseCheckout": [],
399 "type": "github" 419 "type": "github"
400 }, 420 },
401 "version": "e750af9eb17d729b8c5257a4bcd2faba2b28029c" 421 "version": "39ad8a43522c18b5e4f4363ce053f604312fc413"
402 }, 422 },
403 "xcompose": { 423 "xcompose": {
404 "cargoLocks": null, 424 "cargoLocks": null,
@@ -430,10 +450,10 @@
430 "pinned": false, 450 "pinned": false,
431 "src": { 451 "src": {
432 "name": null, 452 "name": null,
433 "sha256": "sha256-dD2+CB6ocb4/X/CD4s2V2oZt6nc/xwrmsQmDjPv3KsQ=", 453 "sha256": "sha256-6OxRXUm7YnBJFdE6Iu5v4DpWWNZR5OZFdOOhfuAfbjs=",
434 "type": "url", 454 "type": "url",
435 "url": "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2024.12.6.tar.gz" 455 "url": "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2025.1.15.tar.gz"
436 }, 456 },
437 "version": "2024.12.6" 457 "version": "2025.1.15"
438 } 458 }
439} \ No newline at end of file 459} \ No newline at end of file
diff --git a/_sources/generated.nix b/_sources/generated.nix
index b07979a7..fb57de83 100644
--- a/_sources/generated.nix
+++ b/_sources/generated.nix
@@ -26,15 +26,15 @@
26 }; 26 };
27 bpf-examples = { 27 bpf-examples = {
28 pname = "bpf-examples"; 28 pname = "bpf-examples";
29 version = "5343ed3377471c7b7ef2237526c8bdc0f00a0cef"; 29 version = "8d53e6fc46ae625bd16b38eb1007ece99460eada";
30 src = fetchFromGitHub { 30 src = fetchFromGitHub {
31 owner = "xdp-project"; 31 owner = "xdp-project";
32 repo = "bpf-examples"; 32 repo = "bpf-examples";
33 rev = "5343ed3377471c7b7ef2237526c8bdc0f00a0cef"; 33 rev = "8d53e6fc46ae625bd16b38eb1007ece99460eada";
34 fetchSubmodules = true; 34 fetchSubmodules = true;
35 sha256 = "sha256-vKVI8pQ17BNWLKm8wwpyNkLslnB9E2CAZTS6EP5lDT0="; 35 sha256 = "sha256-BUncjyaywmtSMVhbWZDy9XiNlGJet8Z0lzmUqm3f+HU=";
36 }; 36 };
37 date = "2024-01-31"; 37 date = "2025-01-03";
38 }; 38 };
39 emacs-scratch_el = { 39 emacs-scratch_el = {
40 pname = "emacs-scratch_el"; 40 pname = "emacs-scratch_el";
@@ -61,12 +61,26 @@
61 }; 61 };
62 lesspipe = { 62 lesspipe = {
63 pname = "lesspipe"; 63 pname = "lesspipe";
64 version = "2.16"; 64 version = "2.17";
65 src = fetchTarball { 65 src = fetchTarball {
66 url = "https://github.com/wofr06/lesspipe/archive/refs/tags/v2.16.tar.gz"; 66 url = "https://github.com/wofr06/lesspipe/archive/refs/tags/v2.17.tar.gz";
67 sha256 = "sha256-s6oV77sOPYAd8CA51KZK6nydWIh8oK2+SUpPfm7yehg="; 67 sha256 = "sha256-afJuTByGUMU6kFqGGa3pbPaFVdYGcJYiR0RfDNYNgDk=";
68 }; 68 };
69 }; 69 };
70 mako = {
71 pname = "mako";
72 version = "57a258c1f8861200e0623153f1b79065d4ddabd8";
73 src = fetchgit {
74 url = "https://github.com/emersion/mako";
75 rev = "57a258c1f8861200e0623153f1b79065d4ddabd8";
76 fetchSubmodules = false;
77 deepClone = false;
78 leaveDotGit = false;
79 sparseCheckout = [ ];
80 sha256 = "sha256-9PcZLpIfGR8SmZf5e2rDZhF+y3kfSaFw5DneDXHMGTc=";
81 };
82 date = "2025-01-19";
83 };
70 mpv-autosave = { 84 mpv-autosave = {
71 pname = "mpv-autosave"; 85 pname = "mpv-autosave";
72 version = "744c3ee61d2f0a8e9bb4e308dec6897215ae4704"; 86 version = "744c3ee61d2f0a8e9bb4e308dec6897215ae4704";
@@ -130,17 +144,17 @@
130 }; 144 };
131 mpv-subselect = { 145 mpv-subselect = {
132 pname = "mpv-subselect"; 146 pname = "mpv-subselect";
133 version = "9b0043ba6042ba01fdd2533281313b70cbc98be4"; 147 version = "77d0148aa6aa952f07f06212cabe32d54dfdf49e";
134 src = fetchgit { 148 src = fetchgit {
135 url = "https://github.com/CogentRedTester/mpv-sub-select"; 149 url = "https://github.com/CogentRedTester/mpv-sub-select";
136 rev = "9b0043ba6042ba01fdd2533281313b70cbc98be4"; 150 rev = "77d0148aa6aa952f07f06212cabe32d54dfdf49e";
137 fetchSubmodules = false; 151 fetchSubmodules = false;
138 deepClone = false; 152 deepClone = false;
139 leaveDotGit = false; 153 leaveDotGit = false;
140 sparseCheckout = [ ]; 154 sparseCheckout = [ ];
141 sha256 = "sha256-ZLWwkwrFaOg7PnuC23VaZ0P3zMhm1JmVf0eH9lqO0BY="; 155 sha256 = "sha256-VxwwTxE8c8rRQt/m2NA7cRC7+7O1ItYFFGv81nxqIxg=";
142 }; 156 };
143 date = "2024-08-31"; 157 date = "2024-12-22";
144 }; 158 };
145 mpv-youtube-quality = { 159 mpv-youtube-quality = {
146 pname = "mpv-youtube-quality"; 160 pname = "mpv-youtube-quality";
@@ -174,10 +188,10 @@
174 }; 188 };
175 prometheus-lvm-exporter = { 189 prometheus-lvm-exporter = {
176 pname = "prometheus-lvm-exporter"; 190 pname = "prometheus-lvm-exporter";
177 version = "0.3.3"; 191 version = "0.4.0";
178 src = fetchTarball { 192 src = fetchTarball {
179 url = "https://github.com/hansmi/prometheus-lvm-exporter/archive/refs/tags/v0.3.3.tar.gz"; 193 url = "https://github.com/hansmi/prometheus-lvm-exporter/archive/refs/tags/v0.4.0.tar.gz";
180 sha256 = "sha256-mA84Bnq5JF0BGfqHhcCzTef5nDotLgQuiyg3/zOPqTE="; 194 sha256 = "sha256-Nz/lBhQbzWSnOKN4n0OUdJzDTpf3mfY0+FXoCqF03TU=";
181 }; 195 };
182 }; 196 };
183 psql-versioning = { 197 psql-versioning = {
@@ -234,15 +248,15 @@
234 }; 248 };
235 v4l2loopback = { 249 v4l2loopback = {
236 pname = "v4l2loopback"; 250 pname = "v4l2loopback";
237 version = "e750af9eb17d729b8c5257a4bcd2faba2b28029c"; 251 version = "39ad8a43522c18b5e4f4363ce053f604312fc413";
238 src = fetchFromGitHub { 252 src = fetchFromGitHub {
239 owner = "umlaeute"; 253 owner = "umlaeute";
240 repo = "v4l2loopback"; 254 repo = "v4l2loopback";
241 rev = "e750af9eb17d729b8c5257a4bcd2faba2b28029c"; 255 rev = "39ad8a43522c18b5e4f4363ce053f604312fc413";
242 fetchSubmodules = true; 256 fetchSubmodules = true;
243 sha256 = "sha256-ePA1LcxQInrLLpbZ7Wljv75lWl6V6s9KkdMp0tF1vhk="; 257 sha256 = "sha256-A1p5ZfoMlw6/J3vBdQcXMvERdyBnqs9Ca+0LcLnu7b8=";
244 }; 258 };
245 date = "2024-11-26"; 259 date = "2025-01-18";
246 }; 260 };
247 xcompose = { 261 xcompose = {
248 pname = "xcompose"; 262 pname = "xcompose";
@@ -258,10 +272,10 @@
258 }; 272 };
259 yt-dlp = { 273 yt-dlp = {
260 pname = "yt-dlp"; 274 pname = "yt-dlp";
261 version = "2024.12.6"; 275 version = "2025.1.15";
262 src = fetchurl { 276 src = fetchurl {
263 url = "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2024.12.6.tar.gz"; 277 url = "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2025.1.15.tar.gz";
264 sha256 = "sha256-dD2+CB6ocb4/X/CD4s2V2oZt6nc/xwrmsQmDjPv3KsQ="; 278 sha256 = "sha256-6OxRXUm7YnBJFdE6Iu5v4DpWWNZR5OZFdOOhfuAfbjs=";
265 }; 279 };
266 }; 280 };
267} 281}
diff --git a/accounts/gkleen@sif/default.nix b/accounts/gkleen@sif/default.nix
index 00707e87..10180eca 100644
--- a/accounts/gkleen@sif/default.nix
+++ b/accounts/gkleen@sif/default.nix
@@ -92,6 +92,7 @@ in {
92 home-manager.users.${userName} = { 92 home-manager.users.${userName} = {
93 imports = [ 93 imports = [
94 ./libvirt 94 ./libvirt
95 ./niri
95 flakeInputs.nix-index-database.hmModules.nix-index 96 flakeInputs.nix-index-database.hmModules.nix-index
96 flakeInputs.impermanence.nixosModules.home-manager.impermanence 97 flakeInputs.impermanence.nixosModules.home-manager.impermanence
97 ]; 98 ];
@@ -160,7 +161,7 @@ in {
160 yaml-mode json-mode shakespeare-mode smart-mode-line 161 yaml-mode json-mode shakespeare-mode smart-mode-line
161 highlight-parentheses highlight-symbol ag sass-mode lua-mode 162 highlight-parentheses highlight-symbol ag sass-mode lua-mode
162 fira-code-mode use-package wanderlust # notmuch 163 fira-code-mode use-package wanderlust # notmuch
163 use-package-ensure-system-package git-gutter emacsScratch 164 git-gutter emacsScratch
164 edit-server mediawiki editorconfig typescript-mode 165 edit-server mediawiki editorconfig typescript-mode
165 markdown-mode nftables-mode rustic lsp-mode lsp-ui 166 markdown-mode nftables-mode rustic lsp-mode lsp-ui
166 direnv company projectile tomorrow-night-paradise-theme 167 direnv company projectile tomorrow-night-paradise-theme
@@ -250,286 +251,6 @@ in {
250 "kitty_mod+m" = "detach_window ask"; 251 "kitty_mod+m" = "detach_window ask";
251 }; 252 };
252 }; 253 };
253 waybar = {
254 enable = true;
255 systemd = {
256 enable = true;
257 target = "hyprland-session.target";
258 };
259 settings = let
260 windowRewrites = {
261 "(.*) — Mozilla Firefox" = "$1";
262 "(.*) - Mozilla Thunderbird" = "$1";
263 "(.*) - mpv" = "$1";
264 };
265 iconSize = 11;
266 in [
267 {
268 layer = "top";
269 position = "top";
270 height = 14;
271 output = [ "eDP-1" "DP-2" "DP-3" ];
272 modules-left = [ "hyprland/workspaces" ];
273 modules-center = [ "hyprland/window" ];
274 modules-right = [ "custom/worktime" "custom/worktime-today" "custom/weather" "custom/keymap" "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "clock" ];
275
276 "custom/weather" = {
277 format = "{}";
278 tooltip = true;
279 interval = 3600;
280 exec = "${lib.getExe pkgs.wttrbar} --hide-conditions --custom-indicator \"<span font=\\\"Symbols Nerd Font Mono\\\" size=\\\"120%\\\">{ICON}</span> {FeelsLikeC}°\"";
281 return-type = "json";
282 };
283 "custom/keymap" = {
284 format = "{}";
285 tooltip = true;
286 return-type = "json";
287 exec = pkgs.writers.writePython3 "keymap" {} ''
288 import os
289 import socket
290 import re
291 import subprocess
292 import json
293
294
295 def output(keymap):
296 short = keymap
297 if keymap == "English (programmer Dvorak)":
298 short = "dvp"
299 elif keymap == "English (US)":
300 short = "<span color=\"#ffffff\">us</span>"
301 print(json.dumps({'text': short, 'tooltip': keymap}, separators=(',', ':')), flush=True) # noqa: E501
302
303
304 r = subprocess.run(["hyprctl", "devices", "-j"], check=True, stdout=subprocess.PIPE, text=True) # noqa: E501
305 for keyboard in json.loads(r.stdout)['keyboards']:
306 if keyboard['name'] != "at-translated-set-2-keyboard":
307 continue
308 output(keyboard['active_keymap'])
309
310 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
311 sock.connect(os.environ["XDG_RUNTIME_DIR"] + "/hypr/" + os.environ["HYPRLAND_INSTANCE_SIGNATURE"] + "/.socket2.sock") # noqa: E501
312 expected = re.compile(r'^activelayout>>at-translated-set-2-keyboard,(?P<keymap>.+)$') # noqa: E501
313 for line in sock.makefile(buffering=1, encoding='utf-8'):
314 if match := expected.match(line):
315 output(match.group("keymap"))
316 '';
317 on-click = "hyprctl switchxkblayout at-translated-set-2-keyboard next";
318 };
319 "custom/worktime" = {
320 interval = 60;
321 exec = getExe pkgs.worktime;
322 tooltip = false;
323 };
324 "custom/worktime-today" = {
325 interval = 60;
326 exec = "${getExe pkgs.worktime} today";
327 tooltip = false;
328 };
329 "hyprland/workspaces" = {
330 all-outputs = true;
331 };
332 "hyprland/window" = {
333 separate-outputs = true;
334 icon = true;
335 icon-size = 14;
336 rewrite = windowRewrites;
337 };
338 clock = {
339 interval = 1;
340 # timezone = "Europe/Berlin";
341 format = "W{:%V-%u %F %H:%M:%S%Ez}";
342 tooltip-format = "<tt><small>{calendar}</small></tt>";
343 calendar = {
344 mode = "year";
345 mode-mon-col = 3;
346 weeks-pos = "left";
347 on-scroll = 1;
348 format = {
349 months = "<span color='#ffead3'><b>{}</b></span>";
350 days = "{}";
351 weeks = "<span color='#99ffdd'><b>{}</b></span>";
352 weekdays = "<span color='#ffcc66'><b>{}</b></span>";
353 today = "<span color='#ff6699'><b>{}</b></span>";
354 };
355 };
356 };
357 battery = {
358 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
359 icon-size = iconSize - 2;
360 states = { warning = 30; critical = 15; };
361 format-icons = ["&#xf008e;" "&#xf007a;" "&#xf007b;" "&#xf007c;" "&#xf007d;" "&#xf007e;" "&#xf007f;" "&#xf0080;" "&#xf0081;" "&#xf0082;" "&#xf0079;" ];
362 format-charging = "&#xf0084;";
363 format-plugged = "&#xf06a5;";
364 tooltip-format = "{capacity}% {timeTo}";
365 interval = 20;
366 };
367 tray = {
368 icon-size = 16;
369 # show-passive-items = true;
370 spacing = 1;
371 };
372 privacy = {
373 icon-spacing = 7;
374 icon-size = iconSize;
375 modules = [
376 { type = "screenshare"; }
377 { type = "audio-in"; }
378 ];
379 };
380 idle_inhibitor = {
381 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
382 icon-size = iconSize;
383 format-icons = { activated = "&#xf0208;"; deactivated = "&#xf0209;"; };
384 timeout = 120;
385 };
386 backlight = {
387 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
388 icon-size = iconSize;
389 tooltip-format = "{percent}%";
390 format-icons = ["&#xf00da;" "&#xf00db;" "&#xf00dc;" "&#xf00dd;" "&#xf00de;" "&#xf00df;" "&#xf00e0;"];
391 on-scroll-up = "lightctl -d -e4 -n1 up";
392 on-scroll-down = "lightctl -d -e4 -n1 down";
393 };
394 wireplumber = {
395 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
396 icon-size = iconSize;
397 tooltip-format = "{volume}% {node_name}";
398 format-icons = ["&#xf057f;" "&#xf0580;" "&#xf057e;"];
399 format-muted = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">&#xf075f;</span>";
400 # ignored-sinks = ["Easy Effects Sink"];
401 on-scroll-up = "volumectl -d -u up";
402 on-scroll-down = "volumectl -d -u down";
403 on-click = "volumectl -d toggle-mute";
404 };
405 }
406 {
407 layer = "top";
408 position = "top";
409 height = 14;
410 output = [ "!eDP-1" "!DP-2" "!DP-3" ];
411 modules-left = [ "hyprland/workspaces" ];
412 modules-center = [ "hyprland/window" ];
413 modules-right = [ "clock" ];
414
415 "hyprland/workspaces" = {
416 all-outputs = false;
417 };
418 "hyprland/window" = {
419 separate-outputs = true;
420 icon = true;
421 icon-size = 14;
422 rewrite = windowRewrites;
423 };
424 clock = {
425 interval = 1;
426 # timezone = "Europe/Berlin";
427 format = "{:%H:%M}";
428 tooltip-format = "W{:%V-%u %F %H:%M:%S%Ez}";
429 };
430 }
431 ];
432 style = ''
433 @define-color white #ffffff;
434 @define-color grey #555555;
435 @define-color blue #1a8fff;
436 @define-color green #23fd00;
437 @define-color orange #f28a21;
438 @define-color red #f2201f;
439
440 * {
441 border: none;
442 font-family: "Fira Sans Nerd Font";
443 font-size: 10pt;
444 min-height: 0;
445 }
446
447 window#waybar {
448 background-color: rgba(0, 0, 0, 0.66);
449 color: @white;
450 }
451
452 .modules-left {
453 margin-left: 9px;
454 }
455 .modules-right {
456 margin-right: 9px;
457 }
458
459 .module {
460 margin: 0 5px;
461 }
462
463 #workspaces button {
464 color: @grey;
465 }
466 #workspaces button.hosting-monitor {
467 color: @white;
468 }
469 #workspaces button.visible {
470 color: @blue;
471 }
472 #workspaces button.active {
473 color: @green;
474 }
475 #workspaces button.urgent {
476 color: @red;
477 }
478
479 #custom-weather, #custom-keymap, #custom-worktime, #custom-worktime-today {
480 color: @grey;
481 margin: 0 5px;
482 }
483 #custom-weather, #custom-worktime-today {
484 margin-right: 3px;
485 }
486 #custom-keymap, #custom-weather {
487 margin-left: 3px;
488 }
489
490 #tray {
491 margin: 0;
492 }
493 #battery, #idle_inhibitor, #backlight, #wireplumber {
494 color: @grey;
495 margin: 0 5px 0 2px;
496 }
497 #idle_inhibitor {
498 margin-right: 2px;
499 margin-left: 3px;
500 }
501 #battery {
502 margin-right: 3px;
503 }
504 #battery.discharging {
505 color: @white;
506 }
507 #battery.warning {
508 color: @orange;
509 }
510 #battery.critical {
511 color: @red;
512 }
513 #battery.charging {
514 color: @white;
515 }
516 #idle_inhibitor.activated {
517 color: @white;
518 }
519
520 #idle_inhibitor {
521 padding-top: 1px;
522 }
523
524 #privacy {
525 color: @red;
526 margin: -1px 2px 0px 5px;
527 }
528 #clock {
529 /* margin-right: 5px; */
530 }
531 '';
532 };
533 wpaperd = { 254 wpaperd = {
534 enable = true; 255 enable = true;
535 settings.default = { 256 settings.default = {
@@ -542,7 +263,7 @@ in {
542 enable = true; 263 enable = true;
543 settings = { 264 settings = {
544 main = { 265 main = {
545 terminal = lib.getExe pkgs.kitty; 266 terminal = lib.getExe cfg.programs.kitty.package;
546 layer = "overlay"; 267 layer = "overlay";
547 icon-theme = "Paper"; 268 icon-theme = "Paper";
548 font = "Fira Sans"; 269 font = "Fira Sans";
@@ -564,14 +285,6 @@ in {
564 }; 285 };
565 286
566 services = { 287 services = {
567 dunst = {
568 settings = import ./dunst-settings.nix inputs;
569 iconTheme = {
570 package = pkgs.paper-icon-theme;
571 name = "Paper";
572 };
573 enable = true;
574 };
575 emacs = { 288 emacs = {
576 enable = true; 289 enable = true;
577 socketActivation.enable = true; 290 socketActivation.enable = true;
@@ -599,6 +312,9 @@ in {
599 notification_actions = { 312 notification_actions = {
600 device_mounted = []; 313 device_mounted = [];
601 }; 314 };
315 device_config = [
316 { mount_path = "/run/etc-metadata"; ignore = true; }
317 ];
602 }; 318 };
603 }; 319 };
604 network-manager-applet.enable = true; 320 network-manager-applet.enable = true;
@@ -635,13 +351,13 @@ in {
635 enable = true; 351 enable = true;
636 events = [ 352 events = [
637 { event = "before-sleep"; command = lockCommand; } 353 { event = "before-sleep"; command = lockCommand; }
638 { event = "after-resume"; command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms on"; } 354 # { event = "after-resume"; command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms on"; }
639 { event = "lock"; command = lockCommand; } 355 { event = "lock"; command = lockCommand; }
640 ]; 356 ];
641 timeouts = [ 357 timeouts = [
642 { timeout = 300; 358 # { timeout = 300;
643 command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms off"; 359 # command = "${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms off";
644 } 360 # }
645 { timeout = 330; command = lockCommand; } 361 { timeout = 330; command = lockCommand; }
646 ]; 362 ];
647 extraArgs = [ 363 extraArgs = [
@@ -700,11 +416,12 @@ in {
700 fira fira-code pwvucontrol wrappedKeepassxc wl-clipboard-rs 416 fira fira-code pwvucontrol wrappedKeepassxc wl-clipboard-rs
701 mumble pulseaudio-ctl pamixer libnotify screen-message 417 mumble pulseaudio-ctl pamixer libnotify screen-message
702 wrappedYTMDesktop libsForQt5.qt5ct playerctl evince 418 wrappedYTMDesktop libsForQt5.qt5ct playerctl evince
703 thunderbird zoom-us steam steam-run wireshark virt-manager 419 thunderbird zoom-us xdg-desktop-portal steam steam-run
704 rclone cached-nix-shell worktime fira-code-symbols 420 wireshark virt-manager rclone cached-nix-shell worktime
705 libreoffice xournalpp google-chrome nixos-shell virt-viewer 421 fira-code-symbols libreoffice xournalpp google-chrome
706 freerdp gnome-icon-theme paper-icon-theme sshpassSecret 422 nixos-shell virt-viewer freerdp gnome-icon-theme
707 weechat element-desktop matrix-synapse-tools.synadm 423 paper-icon-theme sshpassSecret weechat element-desktop
424 matrix-synapse-tools.synadm
708 flakeInputs.deploy-rs.packages.${config.nixpkgs.system}.deploy-rs 425 flakeInputs.deploy-rs.packages.${config.nixpkgs.system}.deploy-rs
709 sieve-connect gimp inkscape udiskie glab nitrokey-app 426 sieve-connect gimp inkscape udiskie glab nitrokey-app
710 pynitrokey gtklock wlrctl remmina openscad spice-record 427 pynitrokey gtklock wlrctl remmina openscad spice-record
@@ -744,13 +461,6 @@ in {
744 }; 461 };
745 462
746 xdg.configFile = { 463 xdg.configFile = {
747 "dunst/dunstrc.d" = {
748 source = ./dunstrc.d;
749 recursive = true;
750 onChange = ''
751 ${pkgs.systemd}/bin/systemctl --user try-restart dunst
752 '';
753 };
754 "wireplumber" = { 464 "wireplumber" = {
755 source = ./wireplumber; 465 source = ./wireplumber;
756 recursive = true; 466 recursive = true;
@@ -923,23 +633,6 @@ in {
923 color-scheme = "prefer-dark"; 633 color-scheme = "prefer-dark";
924 }; 634 };
925 }; 635 };
926
927 wayland.windowManager.hyprland = {
928 enable = true;
929 settings = import ./hyprland.nix inputs;
930 };
931
932 xdg.portal = {
933 enable = true;
934 xdgOpenUsePortal = true;
935 config = {
936 common.default = [ "gtk" ];
937 hyprland.default = [ "gtk" "kde" "hyprland" ];
938 };
939 extraPortals = with pkgs; [
940 xdg-desktop-portal-kde xdg-desktop-portal-gtk xdg-desktop-portal-wlr xdg-desktop-portal-hyprland
941 ];
942 };
943 }; 636 };
944 }; 637 };
945} 638}
diff --git a/accounts/gkleen@sif/dunst-settings.nix b/accounts/gkleen@sif/dunst-settings.nix
deleted file mode 100644
index 72687aea..00000000
--- a/accounts/gkleen@sif/dunst-settings.nix
+++ /dev/null
@@ -1,45 +0,0 @@
1{ pkgs, ... }:
2{
3 global = {
4 font = "Fira Sans 12";
5 markup = "full";
6 format = "<i>%s</i> %p\\n%b";
7 alignment = "left";
8 # geometry = "1216x10-32+64";
9 width = 500;
10 height = 100;
11 offset = "4x4";
12 origin = "top-right";
13 shrink = true;
14 monitor = 0;
15 follow = "none";
16 padding = 6;
17 horizontal_padding = 6;
18 separator_height = 1;
19 separator_color = "frame";
20 idle_threshold = 0;
21
22 transparency = 10;
23
24 frame_width = 1;
25 frame_color = "#999999";
26
27 word_wrap = true;
28 show_age_threshold = 15;
29 show_indicators = false;
30 icon_position = "right";
31 min_icon_size = 25;
32 max_icon_size = 25;
33 sort = false;
34 sticky_history = false;
35
36 dmenu = "fuzzel --dmenu";
37 browser = "${pkgs.xdg-utils}/bin/xdg-open";
38 };
39 # shortcuts = {
40 # close = "ctrl+space";
41 # close_all = "ctrl+shift+space";
42 # history = "ctrl+comma";
43 # context = "ctrl+period";
44 # };
45}
diff --git a/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf b/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf
deleted file mode 100644
index 98c94b64..00000000
--- a/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf
+++ /dev/null
@@ -1,4 +0,0 @@
1[urgency_low]
2background="#000000aa"
3foreground="#999999"
4timeout=5 \ No newline at end of file
diff --git a/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf b/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf
deleted file mode 100644
index f8fa8e2d..00000000
--- a/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf
+++ /dev/null
@@ -1,4 +0,0 @@
1[urgency_normal]
2background="#000000aa"
3foreground="#ffffff"
4timeout=15 \ No newline at end of file
diff --git a/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf b/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf
deleted file mode 100644
index a08bf4b1..00000000
--- a/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf
+++ /dev/null
@@ -1,4 +0,0 @@
1[urgency_critical]
2background="#900000aa"
3foreground="#ffffff"
4timeout=0
diff --git a/accounts/gkleen@sif/dunstrc.d/10-brightness.conf b/accounts/gkleen@sif/dunstrc.d/10-brightness.conf
deleted file mode 100644
index c54595ab..00000000
--- a/accounts/gkleen@sif/dunstrc.d/10-brightness.conf
+++ /dev/null
@@ -1,5 +0,0 @@
1[brightness]
2appname="brightness"
3set_stack_tag="brightness"
4set_transient=yes
5history_ignore=yes
diff --git a/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf b/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf
deleted file mode 100644
index 074f4535..00000000
--- a/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf
+++ /dev/null
@@ -1,5 +0,0 @@
1[pulseaudio-ctl]
2body="Current is *"
3history_ignore=yes
4set_stack_tag="volume"
5summary="Volume *"
diff --git a/accounts/gkleen@sif/dunstrc.d/20-element.conf b/accounts/gkleen@sif/dunstrc.d/20-element.conf
deleted file mode 100644
index 5ff6031e..00000000
--- a/accounts/gkleen@sif/dunstrc.d/20-element.conf
+++ /dev/null
@@ -1,3 +0,0 @@
1[element-im]
2appname=Element
3timeout=0 \ No newline at end of file
diff --git a/accounts/gkleen@sif/dunstrc.d/20-kitty.conf b/accounts/gkleen@sif/dunstrc.d/20-kitty.conf
deleted file mode 100644
index b27ee27e..00000000
--- a/accounts/gkleen@sif/dunstrc.d/20-kitty.conf
+++ /dev/null
@@ -1,3 +0,0 @@
1[kitty]
2appname=kitty
3urgency=low
diff --git a/accounts/gkleen@sif/dunstrc.d/20-mail.conf b/accounts/gkleen@sif/dunstrc.d/20-mail.conf
deleted file mode 100644
index cb568e01..00000000
--- a/accounts/gkleen@sif/dunstrc.d/20-mail.conf
+++ /dev/null
@@ -1,3 +0,0 @@
1[element]
2appname="notmuch"
3timeout=0 \ No newline at end of file
diff --git a/accounts/gkleen@sif/dunstrc.d/20-zulip.conf b/accounts/gkleen@sif/dunstrc.d/20-zulip.conf
deleted file mode 100644
index d7fbd32c..00000000
--- a/accounts/gkleen@sif/dunstrc.d/20-zulip.conf
+++ /dev/null
@@ -1,3 +0,0 @@
1[zulip]
2appname="Zulip"
3timeout=0 \ No newline at end of file
diff --git a/accounts/gkleen@sif/emacs.el b/accounts/gkleen@sif/emacs.el
index b1b1b198..183cb322 100644
--- a/accounts/gkleen@sif/emacs.el
+++ b/accounts/gkleen@sif/emacs.el
@@ -14,7 +14,7 @@
14(setq package-archives nil) 14(setq package-archives nil)
15(package-initialize) 15(package-initialize)
16(require 'use-package) 16(require 'use-package)
17(use-package use-package-ensure-system-package :ensure t) 17;; (use-package use-package-ensure-system-package :ensure t)
18 18
19(require 'evil) 19(require 'evil)
20(evil-mode 1) 20(evil-mode 1)
diff --git a/accounts/gkleen@sif/hyprland.nix b/accounts/gkleen@sif/hyprland.nix
deleted file mode 100644
index d3061c61..00000000
--- a/accounts/gkleen@sif/hyprland.nix
+++ /dev/null
@@ -1,424 +0,0 @@
1{ pkgs, lib, config, userName, ... }:
2let
3 cfg = config.home-manager.users.${userName};
4in {
5 monitor = [
6 ",preferred,auto,auto,bitdepth,8"
7 "eDP-1,3840x2160@60,auto,1.5,bitdepth,8"
8 ];
9
10 "$terminal" = "kitty";
11 "$menu" = "fuzzel";
12
13 exec-once = [
14 "wpaperd"
15 ];
16
17 env = [
18 "NIXOS_OZONE_WL,1"
19 "QT_QPA_PLATFORM,wayland"
20 "QT_WAYLAND_DISABLE_WINDOWDECORATION,1"
21 "GDK_BACKEND,wayland"
22 "GDK_SCALE,0.66"
23 "QT_AUTO_SCREEN_SCALE_FACTOR,1"
24 "SDL_VIDEODRIVER,wayland"
25 # "AQ_DRM_DEVICES,/dev/dri/by-path/pci-0000:01:00.0-card"
26 "__NV_PRIME_RENDER_OFFLOAD,1"
27 "__NV_PRIME_RENDER_OFFLOAD_PROVIDER,NVIDIA-G0"
28 "__GLX_VENDOR_LIBRARY_NAME,nvidia"
29 "__VK_LAYER_NV_optimus,NVIDIA_only"
30 ];
31
32 xwayland.force_zero_scaling = true;
33
34 general = {
35 gaps_in = 3;
36 gaps_out = 9;
37 "col.active_border" = "rgba(33ccffee) rgba(00ff95ee) 45deg";
38 "col.inactive_border" = "rgba(595959aa)";
39
40 resize_on_border = false;
41
42 allow_tearing = false;
43
44 layout = "dwindle";
45 };
46
47 decoration = {
48 rounding = 5;
49 dim_special = 0.0;
50 };
51
52 animations = {
53 enabled = true;
54 bezier = "myBezier, 0.05, 0.9, 0.1, 1.05";
55 animation = [
56 "windows, 1, 1, default, popin 80%"
57 "windowsMove, 0"
58 # "windows, 1, 7, myBezier"
59 # "windowsOut, 1, 7, myBezier, popin 80%"
60 "border, 1, 10, default"
61 "borderangle, 1, 8, default"
62 "fade, 1, 1, default"
63 "workspaces, 1, 1, default, fade"
64 # "workspaces, 1, 6, default"
65 ];
66 };
67
68 dwindle = {
69 pseudotile = false;
70 preserve_split = true;
71 };
72
73 master = {
74 new_status = "master";
75 };
76
77 misc = {
78 disable_hyprland_logo = true;
79 disable_splash_rendering = true;
80 # focus_on_activate = true;
81 mouse_move_enables_dpms = true;
82 key_press_enables_dpms = true;
83 new_window_takes_over_fullscreen = 1;
84 exit_window_retains_fullscreen = true;
85 };
86
87 cursor = {
88 hide_on_key_press = true;
89 };
90
91 input = {
92 kb_layout = "us,us";
93 kb_variant = "dvp,";
94 kb_model = "";
95 kb_options = "compose:caps,grp:win_space_toggle";
96 kb_rules = "";
97
98 follow_mouse = 1;
99
100 sensitivity = 0;
101
102 touchpad = {
103 natural_scroll = false;
104 };
105 };
106
107 device = [
108 { name = "synaptics-tm3512-010";
109 sensitivity = 0.4;
110 }
111 { name = "tpps/2-elan-trackpoint";
112 sensitivity = 0.2;
113 }
114 { name = "logitech-ergo-m575";
115 sensitivity = 1.333;
116 }
117 ];
118
119 gestures = {
120 workspace_swipe = false;
121 };
122
123 dwindle = {
124 # no_gaps_when_only = 1;
125 };
126
127 "$mainMod" = "SUPER";
128
129 bind = [
130 "$mainMod, return, exec, $terminal"
131 "$mainMod, Q, killactive"
132 "$mainMod SHIFT, Q, exec, hyprctl kill"
133 "$mainMod, V, togglefloating"
134 "$mainMod, D, exec, $menu"
135 "$mainMod SHIFT, D, exec, $menu --list-executables-in-path"
136 # "$mainMod, J, togglesplit,"
137
138 "$mainMod SHIFT, L, exec, loginctl lock-session"
139 "$mainMod SHIFT, E, exit"
140
141 "$mainMod, left, movefocus, l"
142 "$mainMod, right, movefocus, r"
143 "$mainMod, up, movefocus, u"
144 "$mainMod, down, movefocus, d"
145 "$mainMod SHIFT, left, swapwindow, l"
146 "$mainMod SHIFT, right, swapwindow, r"
147 "$mainMod SHIFT, up, swapwindow, u"
148 "$mainMod SHIFT, down, swapwindow, d"
149
150 "$mainMod, T, cyclenext"
151
152 "$mainMod, G, focusmonitor, 0"
153 "$mainMod, C, focusmonitor, 1"
154 "$mainMod, R, focusmonitor, 2"
155 "$mainMod, L, focusmonitor, 3"
156
157 "$mainMod CTRL, G, movecurrentworkspacetomonitor, 0"
158 "$mainMod CTRL, C, movecurrentworkspacetomonitor, 1"
159 "$mainMod CTRL, R, movecurrentworkspacetomonitor, 2"
160 "$mainMod CTRL, L, movecurrentworkspacetomonitor, 3"
161
162 "$mainMod, F, fullscreen, 1"
163 "$mainMod SHIFT, F, fullscreen, 0"
164 "$mainMod CTRL SHIFT, F, fullscreenstate, 1, 2"
165
166 "$mainMod, code:14, workspace, 1"
167 "$mainMod, code:17, workspace, 2"
168 "$mainMod, code:13, workspace, 3"
169 "$mainMod, code:18, workspace, 4"
170 "$mainMod, code:12, workspace, 5"
171 "$mainMod, code:19, workspace, 6"
172 "$mainMod, code:11, workspace, 7"
173 "$mainMod, code:20, workspace, 8"
174 "$mainMod, code:15, workspace, 9"
175 "$mainMod, code:16, workspace, 10"
176
177 "$mainMod SHIFT, code:14, movetoworkspacesilent, 1"
178 "$mainMod SHIFT, code:17, movetoworkspacesilent, 2"
179 "$mainMod SHIFT, code:13, movetoworkspacesilent, 3"
180 "$mainMod SHIFT, code:18, movetoworkspacesilent, 4"
181 "$mainMod SHIFT, code:12, movetoworkspacesilent, 5"
182 "$mainMod SHIFT, code:19, movetoworkspacesilent, 6"
183 "$mainMod SHIFT, code:11, movetoworkspacesilent, 7"
184 "$mainMod SHIFT, code:20, movetoworkspacesilent, 8"
185 "$mainMod SHIFT, code:15, movetoworkspacesilent, 9"
186 "$mainMod SHIFT, code:16, movetoworkspacesilent, 10"
187
188 "$mainMod, semicolon, exec, dunstctl close"
189 "$mainMod SHIFT, semicolon, exec, dunstctl close-all"
190 "$mainMod, period, exec, dunstctl context"
191 "$mainMod, comma, exec, dunstctl history-pop"
192
193 "$mainMod ALT, E, exec, emacsclient -c"
194 "$mainMod ALT, Y, exec, ${pkgs.writeShellScript "yt-dlp" ''
195 export PATH="${lib.makeBinPath (with pkgs; [ wl-clipboard-rs socat ])}:$PATH"
196 socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/yt-dlp.sock <<<$'{ "urls": ["'"$(wl-paste)"$'"] }'
197 ''}"
198 "$mainMod ALT, L, exec, ${pkgs.writeShellScript "mpv" ''
199 export PATH="${lib.makeBinPath (with pkgs; [ wl-clipboard-rs ])}:$PATH"
200 exec mpv "$(wl-paste)"
201 ''}"
202
203 ", Print, exec, ${pkgs.writeShellScript "screenshot" ''
204 export PATH="${lib.makeBinPath (with pkgs; [ grim slurp wl-clipboard-rs coreutils ])}:$PATH"
205
206 outFile="$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png"
207 grim -g "$(slurp -b 00000080 -c FFFFFFFF -s 00000000 -w 1)" "$outFile"
208 wl-copy --type image/png <"$outFile"
209 ''}"
210 "SHIFT, Print, exec, ${pkgs.writeShellScript "screenshot" ''
211 export PATH="${lib.makeBinPath (with pkgs; [ grim jq wl-clipboard-rs coreutils ])}:$PATH"
212
213 outFile="$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png"
214 grim -o "$(hyprctl monitors -j | jq -r '.[] | select(.focused) | .name')" "$outFile"
215 wl-copy --type image/png <"$outFile"
216 ''}"
217 "CTRL SHIFT, Print, exec, ${pkgs.runCommand "picker" {
218 buildInputs = [ pkgs.makeWrapper ];
219 } ''
220 makeWrapper ${lib.getExe pkgs.hyprpicker} $out \
221 --prefix PATH : ${lib.makeBinPath [pkgs.wl-clipboard-rs]}
222 ''} -a"
223 "$mainMod, M, exec, ${pkgs.writeShellScript "qalc-fuzzel" ''
224 export PATH="${lib.makeBinPath (with pkgs; [ wl-clipboard-rs libqalculate cfg.programs.fuzzel.package coreutils findutils libnotify gnugrep ])}:$PATH"
225
226 RESULTS_DIR="$HOME/.cache/qalc-fuzzel"
227 prev() {
228 FOUND=false
229 while IFS= read -r line; do
230 [[ -n "$line" ]] || continue
231 FOUND=true
232 echo $line
233 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)
234 $FOUND || echo
235 }
236 FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> ") || exit $?
237 if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then
238 QALC_RES="$FUZZEL_RES"
239 QALC_RET=0
240 else
241 QALC_RES=$(qalc "$FUZZEL_RES" 2>&1)
242 QALC_RET=$?
243 fi
244 [[ -n "$QALC_RES" ]] || exit 1
245 EXISTING=false
246 fgrep -xrl "$QALC_RES" "$RESULTS_DIR" | xargs -r touch
247 [[ ''${PIPESTATUS[0]} -eq 0 ]] && EXISTING=true
248 if [[ $QALC_RET -eq 0 ]] && ! $EXISTING; then
249 RES_FILE="$RESULTS_DIR"/$(date -uIs).$(tr -Cd 'a-zA-Z0-9' </dev/random | head -c 10)
250 cat >"$RES_FILE" <<<"$QALC_RES"
251 fi
252 [[ "$QALC_RES" =~ .*\ =\ (.*) ]] && QALC_RES="''${BASH_REMATCH[1]}"
253 [[ $QALC_RET -eq 0 ]] && wl-copy "$QALC_RES"
254 notify-send "$QALC_RES"
255 ''}"
256 "$mainMod, E, exec, ${pkgs.writeShellScript "emoji-fuzzel" ''
257 export PATH="${lib.makeBinPath (with pkgs; [ cfg.programs.fuzzel.package wtype wl-clipboard-rs ])}:$PATH"
258
259 FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " <$HOME/.local/share/emoji-data/list.txt) || exit $?
260 [[ -n "$FUZZEL_RES" ]] || exit 1
261 wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste
262 ''}"
263 "$mainMod, B, exec, ${pkgs.writeShellScript "bring" ''
264 export PATH="${lib.makeBinPath (with pkgs; [ cfg.programs.fuzzel.package gawk gojq cfg.wayland.windowManager.hyprland.package ])}:$PATH"
265
266 state="$(hyprctl -j clients)"
267 active_window="$(hyprctl -j activewindow)"
268 active_workspace="$(hyprctl -j activeworkspace | gojq -r '.id')"
269
270 current_addr="$(echo "$active_window" | gojq -r '.address')"
271
272 window="$(echo "$state" |
273 gojq -r '.[] | select(.workspace.id == '"$active_workspace"') | select(.monitor != -1 ) | "\(.title)\t\(.address)"' |
274 fuzzel --log-level=warning --dmenu)"
275
276 addr="$(echo "$window" | awk -F $'\t' '{print $2}')"
277
278 if [[ "$addr" = "$current_addr" ]]; then
279 exit 0
280 fi
281
282 fullscreen_on_same_ws="$(echo "$state" | gojq -r '.[] | select(.fullscreen == true) | select(.workspace.id == '"$active_workspace"') | .address')"
283
284 if [[ "$window" != "" ]]; then
285 if [[ "$fullscreen_on_same_ws" == "" ]]; then
286 hyprctl dispatch focuswindow address:"''${addr}"
287 else
288 # If we want to focus app_A and app_B is fullscreen on the same workspace,
289 # app_A will get focus, but app_B will remain on top.
290 # This monstrosity is to make sure app_A will end up on top instead.
291 # XXX: doesn't handle fullscreen 0, but I don't care.
292 hyprctl --batch "dispatch focuswindow address:''${fullscreen_on_same_ws}; dispatch fullscreen 1; dispatch focuswindow address:''${addr}; dispatch fullscreen 1"
293 fi
294 fi
295 ''}"
296 "$mainMod SHIFT, B, exec, ${pkgs.writeShellScript "bring" ''
297 export PATH="${lib.makeBinPath (with pkgs; [ cfg.programs.fuzzel.package gawk gojq cfg.wayland.windowManager.hyprland.package ])}:$PATH"
298
299 state="$(hyprctl -j clients)"
300 active_window="$(hyprctl -j activewindow)"
301
302 current_addr="$(echo "$active_window" | gojq -r '.address')"
303
304 window="$(echo "$state" |
305 gojq -r '.[] | select(.monitor != -1 ) | "\(.title)\t\(.workspace.name)\t\(.address)"' |
306 fuzzel --log-level=warning --dmenu)"
307
308 addr="$(echo "$window" | awk -F $'\t' '{print $3}')"
309 ws="$(echo "$window" | awk -F $'\t' '{print $2}')"
310
311 if [[ "$addr" = "$current_addr" ]]; then
312 exit 0
313 fi
314
315 fullscreen_on_same_ws="$(echo "$state" | gojq -r ".[] | select(.fullscreen == true) | select(.workspace.name == \"$ws\") | .address")"
316
317 if [[ "$window" != "" ]]; then
318 if [[ "$fullscreen_on_same_ws" == "" ]]; then
319 hyprctl dispatch focuswindow address:"''${addr}"
320 else
321 # If we want to focus app_A and app_B is fullscreen on the same workspace,
322 # app_A will get focus, but app_B will remain on top.
323 # This monstrosity is to make sure app_A will end up on top instead.
324 # XXX: doesn't handle fullscreen 0, but I don't care.
325 hyprctl --batch "dispatch focuswindow address:''${fullscreen_on_same_ws}; dispatch fullscreen 1; dispatch focuswindow address:''${addr}; dispatch fullscreen 1"
326 fi
327 fi
328 ''}"
329
330 "$mainMod CTRL, return, togglespecialworkspace, term"
331 "$mainMod CTRL, e, togglespecialworkspace, edit"
332 "$mainMod CTRL, a, togglespecialworkspace, pwvucontrol"
333 "$mainMod CTRL, o, togglespecialworkspace, easyeffects"
334 "$mainMod CTRL, b, togglespecialworkspace, blueman"
335 "$mainMod CTRL, p, togglespecialworkspace, keepass"
336 ];
337 bindm = [
338 "$mainMod, mouse:272, movewindow"
339 "$mainMod, mouse:273, resizewindow"
340 ];
341 bindel = [
342 ", XF86MonBrightnessUp, exec, lightctl -d -e4 -n1 up"
343 ", XF86MonBrightnessDown, exec, lightctl -d -e4 -n1 down"
344 ", XF86AudioRaiseVolume, exec, volumectl -d -u up"
345 ", XF86AudioLowerVolume, exec, volumectl -d -u down"
346 ];
347 bindl = [
348 ", XF86AudioMute, exec, volumectl -d toggle-mute"
349 ", XF86AudioMicMute, exec, volumectl -d -m toggle-mute"
350 "$mainMod SHIFT, S, exec, systemctl suspend"
351
352 ", switch:off:Lid Switch,exec,hyprctl dispatch dpms on eDP-1"
353 ", switch:on:Lid Switch,exec,hyprctl dispatch dpms off eDP-1"
354
355 ", switch:off:Lid Switch,exec,${pkgs.writeShellScript "clamshell-off" ''
356 export PATH="${lib.makeBinPath (with pkgs; [ jq ])}:$PATH"
357 [[ $(hyprctl monitors -j | jq '.[] | select(.name == "eDP-1") | .disabled') = "true" ]] || exit 0
358
359 hyprctl keyword monitor "eDP-1,3840x2160@60,auto,1.5"
360 ''}"
361 ", switch:on:Lid Switch,exec,${pkgs.writeShellScript "clamshell-on" ''
362 export PATH="${lib.makeBinPath (with pkgs; [ jq ])}:$PATH"
363
364 [[ $(hyprctl monitors -j | jq 'reduce (.[] | select(.disabled == false)) as $_ (0; .+1)') -gt 1 ]] || exit 0
365
366 hyprctl keyword monitor "eDP-1,disable"
367 ''}"
368 ];
369
370 windowrulev2 = [
371 "suppressevent maximize fullscreen, class:.*"
372
373 # "maximize, class:^(Element|thunderbird)$"
374 "workspace special:pwvucontrol, class:^com\.saivert\.pwvucontrol$"
375 "workspace special:easyeffects, class:^com\.github\.wwmm\.easyeffects$"
376 "workspace special:blueman, class:^\.blueman-manager-wrapped$"
377 "workspace special:keepass silent, class:^org\.keepassxc\.KeePassXC$, title:^(?!Unlock Database.*)(?!.*(Access Request|Passkey credentials))"
378 # "group set always lock invade, class:^Element$"
379 "workspace 2, class:^firefox$"
380 "workspace 4, class:^evince$"
381 "workspace 4, class:^imv$"
382 "workspace 4, class:^org\.pwmt\.zathura$"
383 "workspace 10, class:^mpv$"
384 "workspace 1, class:^Element$"
385 "workspace 1, class:^thunderbird$"
386 "workspace 5, class:^virt-manager$"
387 "workspace 5, class:^qemu$"
388 "float, class:^org\.keepassxc\.KeePassXC$, title:Access Request$"
389 "center, class:^org\.keepassxc\.KeePassXC$, title:Access Request$"
390 "float, class:^org\.keepassxc\.KeePassXC$, title:Passkey credentials$"
391 "center, class:^org\.keepassxc\.KeePassXC$, title:Passkey credentials$"
392 "float, class:^org\.keepassxc\.KeePassXC$, title:^Unlock Database"
393 "center, class:^org\.keepassxc\.KeePassXC$, title:^Unlock Database"
394 "float, class:^xdg-desktop-portal-gtk$"
395 "center, class:^xdg-desktop-portal-gtk$"
396
397 "bordercolor rgba(ffaa33ee) rgba(bfff00ee) 45deg, fullscreen:1"
398 "bordercolor rgba(3366ffee) rgba(6a00ffee) 45deg, xwayland:1"
399 "bordercolor rgba(6633ffee) rgba(ea00ffee) 45deg, xwayland:1, fullscreen:1"
400 ];
401
402 workspace = [
403 "s[true], gapsout:100"
404
405 "special:term, on-created-empty:kitty"
406 "special:edit, on-created-empty:emacsclient -c"
407 "special:pwvucontrol, on-created-empty:pwvucontrol"
408 "special:easyeffects, on-created-empty:easyeffects"
409 "special:blueman, on-created-empty:blueman-manager"
410 "special:keepass, on-created-empty:keepassxc"
411
412 "1, defaultName:comm"
413 "2, defaultName:web"
414 "3, defaultName:work"
415 "4, defaultName:read"
416 ];
417
418 layerrule = [
419 "blur, waybar"
420 "blur, launcher"
421 "noanim, notifications"
422 "blur, notifications"
423 ];
424}
diff --git a/accounts/gkleen@sif/libvirt/default.nix b/accounts/gkleen@sif/libvirt/default.nix
index f86a68a2..70ac22b9 100644
--- a/accounts/gkleen@sif/libvirt/default.nix
+++ b/accounts/gkleen@sif/libvirt/default.nix
@@ -16,8 +16,8 @@ with flakeInputs.nixVirt.lib;
16 memory = { count = 16; unit = "GiB"; }; 16 memory = { count = 16; unit = "GiB"; };
17 storage_vol = "/home/gkleen/.local/share/libvirt/images/lmmirzm-vmrz01.qcow2"; 17 storage_vol = "/home/gkleen/.local/share/libvirt/images/lmmirzm-vmrz01.qcow2";
18 nvram_path = "/home/gkleen/.local/share/libvirt/lmmirzm-vmrz01.nvram"; 18 nvram_path = "/home/gkleen/.local/share/libvirt/lmmirzm-vmrz01.nvram";
19 virtio_drive = false; 19 virtio_drive = true;
20 virtio_video = false; 20 virtio_video = true;
21 install_virtio = false; 21 install_virtio = false;
22 }) { 22 }) {
23 qemu-commandline.env = [ 23 qemu-commandline.env = [
@@ -33,11 +33,12 @@ with flakeInputs.nixVirt.lib;
33 os.bootmenu.enable = true; 33 os.bootmenu.enable = true;
34 devices.graphics = { 34 devices.graphics = {
35 listen.type = "address"; 35 listen.type = "address";
36 # gl.enable = true; 36 gl.enable = false;
37 }; 37 };
38 devices.video.model.acceleration.accel3d = false;
38 devices.interface = { 39 devices.interface = {
39 # model.type = "virtio"; 40 model.type = "virtio";
40 model.type = "e1000e"; 41 # model.type = "e1000e";
41 type = "bridge"; 42 type = "bridge";
42 mac.address = "52:54:00:b9:f3:ed"; 43 mac.address = "52:54:00:b9:f3:ed";
43 source.bridge = "rz-0971"; 44 source.bridge = "rz-0971";
@@ -47,6 +48,15 @@ with flakeInputs.nixVirt.lib;
47 type = "unix"; 48 type = "unix";
48 target = { type = "virtio"; name = "org.qemu.guest_agent.0"; }; 49 target = { type = "virtio"; name = "org.qemu.guest_agent.0"; };
49 } 50 }
51 {
52 type = "spicevmc";
53 target = { type = "virtio"; name = "com.redhat.spice.0"; };
54 }
55 {
56 type = "spiceport";
57 target = { type = "virtio"; name = "org.spice-space.webdav.0"; };
58 source.channel = "org.spice-space.webdav.0";
59 }
50 ]; 60 ];
51 devices.tpm.model = "tpm-tis"; 61 devices.tpm.model = "tpm-tis";
52 })); 62 }));
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix
new file mode 100644
index 00000000..165eb5fa
--- /dev/null
+++ b/accounts/gkleen@sif/niri/default.nix
@@ -0,0 +1,577 @@
1{ config, hostConfig, pkgs, lib, ... }:
2let
3 niri = config.programs.niri.package;
4 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";
8 loginctl = lib.getExe' hostConfig.systemd.package "loginctl";
9 systemctl = lib.getExe' hostConfig.systemd.package "systemctl";
10
11 focus_or_spawn = pkgs.writeShellApplication {
12 name = "focus-or-spawn";
13 runtimeInputs = [ niri pkgs.gojq pkgs.gnugrep pkgs.socat ];
14 text = ''
15 window_select="$1"
16 shift
17 workspace_name="$1"
18 shift
19
20 workspaces_json="$(niri msg -j workspaces)"
21 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")"
23 active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")"
24 if [[ $workspace_output != "$active_output" ]]; then
25 niri msg action move-workspace-to-monitor --output "$active_output" "$workspace_name"
26 socat STDIO "$NIRI_SOCKET" <<<'{"Action":{"FocusWorkspace":{"reference":{"Id":'"''${active_workspace}"'}}}}'
27 niri msg action move-workspace-to-index --index 1 "$workspace_name"
28 fi
29
30 while IFS=$'\n' read -r window_json; do
31 if [[ -n $(jq -c "$window_select" <<<"$window_json") ]]; then
32 niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")"
33 exit 0
34 fi
35 done < <(niri msg -j windows | jq -c '.[]')
36
37 exec "$@"
38 '';
39 };
40 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
43 with_adjacent_workspace = pkgs.writeShellApplication {
44 name = "with-adjacent-workspace";
45 runtimeInputs = [ niri pkgs.gojq pkgs.socat ];
46 text = ''
47 blacklist="$1"
48 shift
49 direction="$1"
50 shift
51 action="$1"
52 shift
53
54 workspaces_json="$(niri msg -j workspaces)"
55 active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")"
56 workspace_output="$(jq -r --arg active_workspace "$active_workspace" '.[] | select(.id == ($active_workspace | tonumber)) | .output' <<<"$workspaces_json")"
57 workspace_idx="$(jq -r '.[] | select(.is_focused) | .idx' <<<"$workspaces_json")"
58
59 jq_script='map(select('
60 case "$direction" in
61 down)
62 # shellcheck disable=SC2016
63 jq_script=''${jq_script}'.idx > ($workspace_idx | tonumber)';;
64 up)
65 # shellcheck disable=SC2016
66 jq_script=''${jq_script}'.idx < ($workspace_idx | tonumber)';;
67 esac
68 # shellcheck disable=SC2016
69 jq_script=''${jq_script}' and .output == $workspace_output and ((.name == null) or (.name | test($blacklist) | not)))) | sort_by(.idx)'
70 [[ $direction == "up" ]] && jq_script=''${jq_script}' | reverse'
71 jq_script=''${jq_script}' | .[0]'
72
73 workspace_json=$(jq -c --arg blacklist "$blacklist" --arg workspace_output "$workspace_output" --arg workspace_idx "$workspace_idx" "$jq_script" <<<"$workspaces_json")
74 [[ -n $workspace_json && $workspace_json != null ]] || exit 0
75 jq --arg active_workspace "$active_workspace" -c "$action" <<<"$workspace_json" | tee /dev/stderr | socat STDIO "$NIRI_SOCKET"
76 '';
77 };
78 with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^pwctl|kpxc|bmgr|edit|term$";
79 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}}}}'';
81
82 with_unnamed_workspace = pkgs.writeShellApplication {
83 name = "with-unnamed-workspace";
84 runtimeInputs = [ niri pkgs.gojq pkgs.socat ];
85 text = ''
86 action="$1"
87 shift
88
89 workspaces_json="$(niri msg -j workspaces)"
90 active_output="$(jq -r '.[] | select(.is_focused) | .output' <<<"$workspaces_json")"
91 active_workspace="$(jq -r '.[] | select(.is_focused) | .id' <<<"$workspaces_json")"
92
93 workspace_json="$(jq -c --arg active_output "$active_output" 'map(select(.output == $active_output and .name == null)) | sort_by(.idx) | .[0]' <<<"$workspaces_json")"
94 [[ -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"
96 '';
97 };
98 with-unnamed-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_unnamed_workspace);
99
100 with_select_window = pkgs.writeShellApplication {
101 name = "with-unnamed-workspace";
102 runtimeInputs = [ niri pkgs.gojq pkgs.socat config.programs.fuzzel.package pkgs.gawk ];
103 text = ''
104 window_select="$1"
105 shift
106 action="$1"
107 shift
108
109 windows_json="$(niri msg -j windows)"
110 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)"
112 window_id="$(awk -F $'\t' '{print $2}' <<<"$window")"
113 window_json="$(jq -r --arg window_id "$window_id" '.[] | select(.id == ($window_id | tonumber))' <<<"$windows_json")"
114
115 [[ -z "$window_json" ]] && exit 1
116
117 jq -c "$action" <<<"$window_json" | socat STDIO "$NIRI_SOCKET"
118 '';
119 };
120 with-select-window-action = config.lib.niri.actions.spawn (lib.getExe with_select_window);
121in {
122 imports = [
123 ./waybar.nix
124 ./mako.nix
125 ];
126
127 config = {
128 systemd.user.services.xwayland-satellite = {
129 Unit = {
130 BindsTo = [ "graphical-session.target" ];
131 PartOf = [ "graphical-session.target" ];
132 After = [ "graphical-session.target" ];
133 Requisite = [ "graphical-session.target" ];
134 };
135 Service = {
136 Type = "notify";
137 NotifyAccess = "all";
138 Environment = [ "DISPLAY=:0" ];
139 ExecStart = ''${lib.getExe pkgs.xwayland-satellite-unstable} ''${DISPLAY}'';
140 ExecStartPre = "${systemctl} --user import-environment DISPLAY";
141 StandardOutput = "journal";
142 };
143 Install = {
144 WantedBy = [ "graphical-session.target" ];
145 };
146 };
147
148 services.swayidle = {
149 events = [
150 { event = "after-resume"; command = "${lib.getExe niri} msg action power-on-monitors"; }
151 ];
152 timeouts = [
153 { timeout = 300;
154 command = "${lib.getExe niri} msg action power-off-monitors";
155 }
156 ];
157 };
158
159 programs.niri.settings = {
160 prefer-no-csd = true;
161 screenshot-path = "${config.home.homeDirectory}/screenshots";
162
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 };
176
177 outputs = {
178 "eDP-1" = {
179 scale = 1.5;
180 position = { x = 0; y = 0; };
181 };
182 "Ancor Communications Inc ASUS PB287Q 0x0000DD9B" = {
183 scale = 1.5;
184 position = { x = 2560; y = 0; };
185 };
186 "HP Inc. HP 727pu CN4417143K" = {
187 mode = { width = 2560; height = 1440; refresh = 119.998; };
188 scale = 1;
189 position = { x = 2560; y = 0; };
190 variable-refresh-rate = "on-demand";
191 };
192 };
193
194 environment = {
195 NIXOS_OZONE_WL = "1";
196 QT_QPA_PLATFORM = "wayland";
197 QT_WAYLAND_DISABLE_WINDOWDECORATION = "1";
198 GDK_BACKEND = "wayland";
199 SDL_VIDEODRIVER = "wayland";
200 DISPLAY = ":0";
201 };
202
203 layout = {
204 gaps = 8;
205 struts = { left = 0; right = 0; top = 0; bottom = 0; };
206 focus-ring = {
207 width = 2;
208 active.gradient = {
209 from = "hsla(195 100% 60% 0.75)";
210 to = "hsla(155 100% 50% 0.75)";
211 angle = 29;
212 relative-to = "workspace-view";
213 };
214 inactive.gradient = {
215 from = "hsla(0 0% 42% 0.66)";
216 to = "hsla(0 0% 35% 0.66)";
217 angle = 29;
218 relative-to = "workspace-view";
219 };
220 };
221
222 preset-column-widths = [
223 { proportion = 1. / 4.; }
224 { proportion = 1. / 3.; }
225 { proportion = 1. / 2.; }
226 { proportion = 2. / 3.; }
227 { proportion = 3. / 4.; }
228 ];
229 default-column-width.proportion = 1. / 2.;
230 preset-window-heights = [
231 { proportion = 1. / 3.; }
232 { proportion = 1. / 2.; }
233 { proportion = 2. / 3.; }
234 { proportion = 1.; }
235 ];
236
237 always-center-single-column = true;
238 };
239
240 cursor.hide-when-typing = true;
241
242 workspaces = {
243 "001" = { name = "pwctl"; open-on-output = "eDP-1"; };
244 "002" = { name = "kpxc"; open-on-output = "eDP-1"; };
245 "003" = { name = "bmgr"; open-on-output = "eDP-1"; };
246 "004" = { name = "term"; open-on-output = "eDP-1"; };
247 "005" = { name = "edit"; open-on-output = "eDP-1"; };
248 "101".name = "comm";
249 "102".name = "web";
250 # "104".name = "read";
251 # "105".name = "mon";
252 "110".name = "vid";
253 };
254
255 window-rules = [
256 # {
257 # geometry-corner-radius =
258 # let
259 # allCorners = r: { bottom-left = r; bottom-right = r; top-left = r; top-right = r; };
260 # in allCorners 4.;
261 # clip-to-geometry = true;
262 # }
263 {
264 matches = [ { app-id = "^com\.saivert\.pwvucontrol$"; } ];
265 open-on-workspace = "pwctl";
266 open-maximized = true;
267 }
268 {
269 matches = [ { app-id = "^\.blueman-manager-wrapped$"; } ];
270 open-on-workspace = "bmgr";
271 open-maximized = true;
272 }
273 {
274 matches = [ { app-id = "^org\.keepassxc\.KeePassXC$"; } ];
275 block-out-from = "screencast";
276 }
277 {
278 matches = [ { app-id = "^org\.keepassxc\.KeePassXC$"; } ];
279 excludes = [
280 { title = "^Unlock Database.*"; }
281 { title = "^Access Request.*"; }
282 { title = ".*Passkey credentials$"; }
283 ];
284 open-on-workspace = "kpxc";
285 open-maximized = true;
286 open-focused = false;
287 }
288 {
289 matches = [
290 { app-id = "^org\.keepassxc\.KeePassXC$"; title = "^Unlock Database.*"; }
291 { app-id = "^org\.keepassxc\.KeePassXC$"; title = "^Access Request.*"; }
292 { app-id = "^org\.keepassxc\.KeePassXC$"; title = ".*Passkey credentials$"; }
293 ];
294 open-focused = true;
295 }
296 {
297 matches = [ { app-id = "^kitty-scratch$"; } ];
298 open-on-workspace = "term";
299 open-maximized = true;
300 }
301 {
302 matches = [ { title = "^scratch$"; app-id = "^emacs$"; } ];
303 open-on-workspace = "edit";
304 open-maximized = true;
305 }
306 {
307 matches = [
308 { app-id = "^emacs$"; }
309 { app-id = "^firefox$"; }
310 ];
311 default-column-width.proportion = 2. / 3.;
312 }
313 {
314 matches = [
315 { app-id = "^kitty$"; }
316 { app-id = "^kitty-play$"; }
317 ];
318 default-column-width.proportion = 1. / 3.;
319 }
320 {
321 matches = [
322 { app-id = "^thunderbird$"; }
323 { app-id = "^Element$"; }
324 ];
325 open-on-workspace = "comm";
326 }
327 {
328 matches = [ { app-id = "^firefox$"; } ];
329 open-on-workspace = "web";
330 open-maximized = true;
331 variable-refresh-rate = true;
332 }
333 # {
334 # matches = [
335 # { app-id = "^evince$"; }
336 # { app-id = "^imv$"; }
337 # { app-id = "^org\.pwmt\.zathura$"; }
338 # ];
339 # open-on-workspace = "read";
340 # }
341 {
342 matches = [ { app-id = "^mpv$"; } ];
343 open-on-workspace = "vid";
344 default-column-width.proportion = 1.;
345 variable-refresh-rate = true;
346 }
347 {
348 matches = [ { app-id = "^kitty-play$"; } ];
349 open-on-workspace = "vid";
350 open-focused = false;
351 }
352 # {
353 # matches = [
354 # { app-id = "^qemu$"; }
355 # { app-id = "^virt-manager$"; }
356 # ];
357 # open-on-workspace = "mon";
358 # }
359 ];
360 layer-rules = [
361 { matches = [
362 { namespace = "^notifications$"; }
363 { namespace = "^waybar$"; }
364 ];
365 block-out-from = "screencast";
366 }
367 ];
368
369 binds = with config.lib.niri.actions; {
370 "Mod+Slash".action = show-hotkey-overlay;
371
372 "Mod+Return".action = spawn terminal;
373 "Mod+Q".action = close-window;
374 "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package);
375 "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path";
376
377 "Mod+Alt+E".action = spawn (lib.getExe' config.services.emacs.package "emacsclient") "-c";
378 "Mod+Alt+Y".action = spawn (lib.getExe (pkgs.writeShellApplication {
379 name = "queue-yt-dlp";
380 runtimeInputs = with pkgs; [ wl-clipboard-rs socat ];
381 text = ''
382 socat STDIO UNIX-CONNECT:"$XDG_RUNTIME_DIR"/yt-dlp.sock <<<$'{ "urls": ["'"$(wl-paste)"$'"] }'
383 '';
384 }));
385 "Mod+Alt+L".action = spawn (lib.getExe (pkgs.writeShellApplication {
386 name = "queue-yt-dlp";
387 runtimeInputs = with pkgs; [ wl-clipboard-rs config.programs.kitty.package ];
388 text = ''
389 exec -- kitty --app-id kitty-play --directory "$HOME"/media mpv "$(wl-paste)"
390 '';
391 }));
392
393 "Mod+U".action = spawn (lib.getExe (pkgs.writeShellApplication {
394 name = "qalc-fuzzel";
395 runtimeInputs = with pkgs; [ wl-clipboard-rs libqalculate config.programs.fuzzel.package coreutils findutils libnotify gnugrep ];
396 text = ''
397 RESULTS_DIR="$HOME/.cache/qalc-fuzzel"
398 prev() {
399 FOUND=false
400 while IFS= read -r line; do
401 [[ -n "$line" ]] || continue
402 FOUND=true
403 echo "$line"
404 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)
405 $FOUND || echo
406 }
407 FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> ") || exit $?
408 if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then
409 QALC_RES="$FUZZEL_RES"
410 QALC_RET=0
411 else
412 QALC_RES=$(qalc "$FUZZEL_RES" 2>&1)
413 QALC_RET=$?
414 fi
415 [[ -n "$QALC_RES" ]] || exit 1
416 EXISTING=false
417 set +e
418 grep -Fxrl "$QALC_RES" "$RESULTS_DIR" | xargs -r touch
419 [[ ''${PIPESTATUS[0]} -eq 0 ]] && EXISTING=true
420 set -e
421 if [[ $QALC_RET -eq 0 ]] && ! $EXISTING; then
422 RES_FILE="$RESULTS_DIR"/$(date -uIs).$(tr -Cd 'a-zA-Z0-9' </dev/random | head -c 10)
423 cat >"$RES_FILE" <<<"$QALC_RES"
424 fi
425 [[ "$QALC_RES" =~ .*\ =\ (.*) ]] && QALC_RES="''${BASH_REMATCH[1]}"
426 [[ $QALC_RET -eq 0 ]] && wl-copy "$QALC_RES"
427 notify-send "$QALC_RES"
428 '';
429 }));
430 "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication {
431 name = "emoji-fuzzel";
432 runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ];
433 text = ''
434 FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " <"$HOME"/.local/share/emoji-data/list.txt) || exit $?
435 [[ -n "$FUZZEL_RES" ]] || exit 1
436 wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste
437 '';
438 }));
439 "Print".action = spawn (lib.getExe (pkgs.writeShellApplication {
440 name = "screenshot";
441 runtimeInputs = with pkgs; [ grim slurp wl-clipboard-rs coreutils ];
442 text = ''
443 grim -g "$(slurp -b 00000080 -c FFFFFFFF -s 00000000 -w 1)" - \
444 | tee "$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" \
445 | wl-copy --type image/png
446 '';
447 }));
448 "Shift+Print".action = spawn (lib.getExe (pkgs.writeShellApplication {
449 name = "screenshot";
450 runtimeInputs = with pkgs; [ grim niri gojq wl-clipboard-rs coreutils ];
451 text = ''
452 grim -o "$(niri msg -j workspaces | jq -r '.[] | select(.is_focused) | .output')" - \
453 | tee "$HOME/screenshots/$(date +"%Y-%m-%dT%H:%M:%S").png" \
454 | wl-copy --type image/png
455 '';
456 }));
457 "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
458 "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}";
459
460 "Mod+H".action = focus-column-left;
461 "Mod+T".action = focus-window-down;
462 "Mod+N".action = focus-window-up;
463 "Mod+S".action = focus-column-right;
464
465 "Mod+Shift+H".action = move-column-left;
466 "Mod+Shift+T".action = move-window-down;
467 "Mod+Shift+N".action = move-window-up;
468 "Mod+Shift+S".action = move-column-right;
469
470 "Mod+Control+H".action = focus-monitor-left;
471 "Mod+Control+T".action = focus-monitor-down;
472 "Mod+Control+N".action = focus-monitor-up;
473 "Mod+Control+S".action = focus-monitor-right;
474
475 "Mod+Shift+Control+H".action = move-workspace-to-monitor-left;
476 "Mod+Shift+Control+T".action = move-workspace-to-monitor-down;
477 "Mod+Shift+Control+N".action = move-workspace-to-monitor-up;
478 "Mod+Shift+Control+S".action = move-workspace-to-monitor-right;
479
480 "Mod+G".action = focus-adjacent-workspace "down";
481 "Mod+C".action = focus-adjacent-workspace "up";
482
483 "Mod+Shift+G".action = move-column-to-adjacent-workspace "down";
484 "Mod+Shift+C".action = move-column-to-adjacent-workspace "up";
485
486 "Mod+Shift+Control+G".action = move-workspace-down;
487 "Mod+Shift+Control+C".action = move-workspace-up;
488
489 "Mod+ParenLeft".action = focus-workspace "comm";
490 "Mod+Shift+ParenLeft".action = move-column-to-workspace "comm";
491
492 "Mod+ParenRight".action = focus-workspace "web";
493 "Mod+Shift+ParenRight".action = move-column-to-workspace "web";
494
495 "Mod+BraceRight".action = focus-workspace "read";
496 "Mod+Shift+BraceRight".action = move-column-to-workspace "read";
497
498 "Mod+BraceLeft".action = focus-workspace "mon";
499 "Mod+Shift+BraceLeft".action = move-column-to-workspace "mon";
500
501 "Mod+Asterisk".action = focus-workspace "vid";
502 "Mod+Shift+Asterisk".action = move-column-to-workspace "vid";
503
504 "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}'';
505 "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}'';
506
507 "Mod+M".action = consume-or-expel-window-left;
508 "Mod+W".action = consume-or-expel-window-right;
509
510 "Mod+R".action = switch-preset-column-width;
511 "Mod+Shift+R".action = switch-preset-window-height;
512 "Mod+F".action = center-column;
513 "Mod+Shift+F".action = maximize-column;
514 "Mod+Shift+Ctrl+F".action = fullscreen-window;
515
516 "Mod+V".action = switch-focus-between-floating-and-tiling;
517 "Mod+Shift+V".action = toggle-window-floating;
518
519 "Mod+Left".action = set-column-width "-10%";
520 "Mod+Down".action = set-window-height "-10%";
521 "Mod+Up".action = set-window-height "+10%";
522 "Mod+Right".action = set-column-width "+10%";
523
524 "Mod+Shift+Z" = {
525 action = spawn (lib.getExe niri) "msg" "action" "power-off-monitors";
526 allow-when-locked = true;
527 };
528 "Mod+Shift+L".action = spawn loginctl "lock-session";
529 "Mod+Shift+E".action = quit;
530 "Mod+Shift+Minus" = {
531 action = spawn systemctl "suspend";
532 allow-when-locked = true;
533 };
534 "Mod+Shift+Control+Minus" = {
535 action = spawn systemctl "hibernate";
536 allow-when-locked = true;
537 };
538
539 "XF86MonBrightnessUp" = {
540 action = spawn lightctl "-d" "-e4" "-n1" "up";
541 allow-when-locked = true;
542 };
543 "XF86MonBrightnessDown" = {
544 action = spawn lightctl "-d" "-e4" "-n1" "down";
545 allow-when-locked = true;
546 };
547 "XF86AudioRaiseVolume" = {
548 action = spawn volumectl "-d" "-u" "up";
549 allow-when-locked = true;
550 };
551 "XF86AudioLowerVolume" = {
552 action = spawn volumectl "-d" "-u" "down";
553 allow-when-locked = true;
554 };
555 "XF86AudioMute" = {
556 action = spawn volumectl "-d" "toggle-mute";
557 allow-when-locked = true;
558 };
559 "XF86AudioMicMute" = {
560 action = spawn volumectl "-d" "-m" "toggle-mute";
561 allow-when-locked = true;
562 };
563
564 "Mod+Semicolon".action = spawn makoctl "dismiss" "--group";
565 "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all";
566 "Mod+Period".action = spawn makoctl "menu" (lib.getExe config.programs.fuzzel.package) "--dmenu";
567 "Mod+Comma".action = spawn makoctl "restore";
568
569 "Mod+Control+A".action = focus-or-spawn-action-app_id "com.saivert.pwvucontrol" "pwctl" "pwvucontrol";
570 "Mod+Control+P".action = focus-or-spawn-action-app_id "org.keepassxc.KeePassXC" "kpxc" "keepassxc";
571 "Mod+Control+B".action = focus-or-spawn-action-app_id ".blueman-manager-wrapped" "bmgr" "blueman-manager";
572 "Mod+Control+Return".action = focus-or-spawn-action-app_id "kitty-scratch" "term" "kitty" "--app-id" "kitty-scratch";
573 "Mod+Control+E".action = focus-or-spawn-action "select(.app_id == \"emacs\" and .title == \"scratch\")" "edit" "emacsclient" "-c" "--frame-parameters=(quote (name . \"scratch\"))";
574 };
575 };
576 };
577}
diff --git a/accounts/gkleen@sif/niri/mako.nix b/accounts/gkleen@sif/niri/mako.nix
new file mode 100644
index 00000000..8abf14d8
--- /dev/null
+++ b/accounts/gkleen@sif/niri/mako.nix
@@ -0,0 +1,50 @@
1{ config, lib, ... }:
2{
3 config = {
4 services.mako = {
5 enable = true;
6 font = "Fira Sans 10";
7 format = "<i>%s</i>\\n%b";
8 margin = "2";
9 maxVisible = -1;
10 backgroundColor = "#000000dd";
11 progressColor = "source #223544ff";
12 width = 384;
13 extraConfig = ''
14 outer-margin=1
15 max-history=100
16
17 [grouped]
18 format=<b>(%g)</b> <i>%s</i>\n%b
19
20 [urgency=low]
21 text-color=#999999ff
22
23 [urgency=critical]
24 background-color=#900000dd
25
26 [app-name=Element]
27 group-by=summary
28
29 [mode=silent]
30 invisible=1
31 '';
32 };
33 systemd.user.services.mako = {
34 Unit = {
35 Description = "Mako notification daemon";
36 PartOf = [ "graphical-session.target" ];
37 };
38 Install = {
39 WantedBy = [ "graphical-session.target" ];
40 };
41 Service = {
42 Type = "dbus";
43 BusName = "org.freedesktop.Notifications";
44 ExecStart = lib.getExe config.services.mako.package;
45 RestartSec = 5;
46 Restart = "always";
47 };
48 };
49 };
50}
diff --git a/accounts/gkleen@sif/niri/waybar.nix b/accounts/gkleen@sif/niri/waybar.nix
new file mode 100644
index 00000000..79c429f8
--- /dev/null
+++ b/accounts/gkleen@sif/niri/waybar.nix
@@ -0,0 +1,338 @@
1{ lib, pkgs, ... }:
2{
3 config = {
4 programs.waybar = {
5 enable = true;
6 systemd = {
7 enable = true;
8 target = "graphical-session.target";
9 };
10 settings = let
11 windowRewrites = {
12 "(.*) — Mozilla Firefox" = "$1";
13 "(.*) - Mozilla Thunderbird" = "$1";
14 "(.*) - mpv" = "$1";
15 };
16 iconSize = 11;
17 in [
18 {
19 layer = "top";
20 position = "top";
21 height = 14;
22 output = [ "eDP-1" "DP-2" "DP-3" ];
23 modules-left = [ "niri/workspaces" ];
24 modules-center = [ "niri/window" ];
25 modules-right = [ # "custom/worktime" "custom/worktime-today"
26 "custom/weather"
27 "custom/keymap"
28 "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "custom/mako" "clock" ];
29
30 "custom/mako" = {
31 format = "{}";
32 return-type = "json";
33 exec = pkgs.writers.writePython3 "mako-silent" { libraries = [ pkgs.python3Packages.dbus-next ]; } ''
34 from dbus_next.aio import MessageBus
35
36 import asyncio
37
38 import json
39
40
41 loop = asyncio.new_event_loop()
42 asyncio.set_event_loop(loop)
43
44
45 async def main():
46 bus = await MessageBus().connect()
47 # the introspection xml would normally be included in your project, but
48 # this is convenient for development
49 introspection = await bus.introspect('org.freedesktop.Notifications', '/fr/emersion/Mako') # noqa: E501
50
51 obj = bus.get_proxy_object('org.freedesktop.Notifications', '/fr/emersion/Mako', introspection) # noqa: E501
52 mako = obj.get_interface('fr.emersion.Mako')
53 properties = obj.get_interface('org.freedesktop.DBus.Properties')
54
55 async def print_mode():
56 modes = await mako.get_modes()
57 is_silent = "silent" in modes
58 icon = "&#xf009b;" if is_silent else "&#xf009a;"
59 text = f"<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>" # noqa: E501
60 if is_silent:
61 text = f"<span color=\"#ffffff\">{text}</span>"
62 print(json.dumps({'text': text}, separators=(',', ':')), flush=True) # noqa: E501
63
64 async def on_properties_changed(interface_name, changed_properties, invalidated_properties): # noqa: E501
65 if "Modes" not in invalidated_properties:
66 return
67
68 await print_mode()
69
70 properties.on_properties_changed(on_properties_changed)
71 await print_mode()
72
73 await loop.create_future()
74
75
76 loop.run_until_complete(main())
77 '';
78 on-click = "makoctl mode -t silent";
79 };
80 "custom/weather" = {
81 format = "{}";
82 tooltip = true;
83 interval = 3600;
84 exec = "${lib.getExe pkgs.wttrbar} --hide-conditions --nerd --custom-indicator \"<span font=\\\"Symbols Nerd Font Mono\\\" size=\\\"100%\\\">{ICON}</span> {FeelsLikeC}°\"";
85 return-type = "json";
86 };
87 "custom/keymap" = {
88 format = "{}";
89 tooltip = true;
90 return-type = "json";
91 exec = pkgs.writers.writePython3 "keymap" {} ''
92 import os
93 import socket
94 import json
95
96
97 def output(keymap):
98 short = keymap
99 if keymap == "English (programmer Dvorak)":
100 short = "dvp"
101 elif keymap == "English (US)":
102 short = "<span color=\"#ffffff\">us</span>"
103 print(json.dumps({'text': short, 'tooltip': keymap}, separators=(',', ':')), flush=True) # noqa: E501
104
105
106 keyboard_layouts = []
107
108 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
109 sock.connect(os.environ["NIRI_SOCKET"])
110 sock.send(b"\"EventStream\"\n")
111 for line in sock.makefile(buffering=1, encoding='utf-8'):
112 if line_json := json.loads(line):
113 if "KeyboardLayoutsChanged" in line_json:
114 keyboard_layouts = line_json["KeyboardLayoutsChanged"]["keyboard_layouts"]["names"] # noqa: E501
115 output(keyboard_layouts[line_json["KeyboardLayoutsChanged"]["keyboard_layouts"]["current_idx"]]) # noqa: E501
116 if "KeyboardLayoutSwitched" in line_json:
117 output(keyboard_layouts[line_json["KeyboardLayoutSwitched"]["idx"]]) # noqa: E501
118 '';
119 on-click = "niri msg action switch-layout next";
120 };
121 "custom/worktime" = {
122 interval = 60;
123 exec = lib.getExe pkgs.worktime;
124 tooltip = false;
125 };
126 "custom/worktime-today" = {
127 interval = 60;
128 exec = "${lib.getExe pkgs.worktime} today";
129 tooltip = false;
130 };
131 "niri/workspaces" = {
132 ignore = ["pwctl" "kpxc" "bmgr" "edit" "term"];
133 };
134 "niri/window" = {
135 separate-outputs = true;
136 icon = true;
137 icon-size = 14;
138 rewrite = windowRewrites;
139 };
140 clock = {
141 interval = 1;
142 # timezone = "Europe/Berlin";
143 format = "W{:%V-%u %F %H:%M:%S%Ez}";
144 tooltip-format = "<tt><small>{calendar}</small></tt>";
145 calendar = {
146 mode = "year";
147 mode-mon-col = 3;
148 weeks-pos = "left";
149 on-scroll = 1;
150 format = {
151 months = "<span color='#ffead3'><b>{}</b></span>";
152 days = "{}";
153 weeks = "<span color='#99ffdd'><b>{}</b></span>";
154 weekdays = "<span color='#ffcc66'><b>{}</b></span>";
155 today = "<span color='#ff6699'><b>{}</b></span>";
156 };
157 };
158 };
159 battery = {
160 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
161 icon-size = iconSize - 2;
162 states = { warning = 30; critical = 15; };
163 format-icons = ["&#xf008e;" "&#xf007a;" "&#xf007b;" "&#xf007c;" "&#xf007d;" "&#xf007e;" "&#xf007f;" "&#xf0080;" "&#xf0081;" "&#xf0082;" "&#xf0079;" ];
164 format-charging = "&#xf0084;";
165 format-plugged = "&#xf06a5;";
166 tooltip-format = "{capacity}% {timeTo}";
167 interval = 20;
168 };
169 tray = {
170 icon-size = 16;
171 # show-passive-items = true;
172 spacing = 1;
173 };
174 privacy = {
175 icon-spacing = 7;
176 icon-size = iconSize;
177 modules = [
178 { type = "screenshare"; }
179 { type = "audio-in"; }
180 ];
181 };
182 idle_inhibitor = {
183 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
184 icon-size = iconSize;
185 format-icons = { activated = "&#xf0208;"; deactivated = "&#xf0209;"; };
186 timeout = 120;
187 };
188 backlight = {
189 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
190 icon-size = iconSize;
191 tooltip-format = "{percent}%";
192 format-icons = ["&#xf00da;" "&#xf00db;" "&#xf00dc;" "&#xf00dd;" "&#xf00de;" "&#xf00df;" "&#xf00e0;"];
193 on-scroll-up = "lightctl -d -e4 -n1 up";
194 on-scroll-down = "lightctl -d -e4 -n1 down";
195 };
196 wireplumber = {
197 format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>";
198 icon-size = iconSize;
199 tooltip-format = "{volume}% {node_name}";
200 format-icons = ["&#xf057f;" "&#xf0580;" "&#xf057e;"];
201 format-muted = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">&#xf075f;</span>";
202 # ignored-sinks = ["Easy Effects Sink"];
203 on-scroll-up = "volumectl -d -u up";
204 on-scroll-down = "volumectl -d -u down";
205 on-click = "volumectl -d toggle-mute";
206 };
207 }
208 {
209 layer = "top";
210 position = "top";
211 height = 14;
212 output = [ "!eDP-1" "!DP-2" "!DP-3" ];
213 modules-left = [ "niri/workspaces" ];
214 modules-center = [ "niri/window" ];
215 modules-right = [ "clock" ];
216
217 "niri/workspaces" = {
218 ignore = ["pwctl" "kpxc" "bmgr" "edit" "term"];
219 };
220 "niri/window" = {
221 separate-outputs = true;
222 icon = true;
223 icon-size = 14;
224 rewrite = windowRewrites;
225 };
226 clock = {
227 interval = 1;
228 # timezone = "Europe/Berlin";
229 format = "{:%H:%M}";
230 tooltip-format = "W{:%V-%u %F %H:%M:%S%Ez}";
231 };
232 }
233 ];
234 style = ''
235 @define-color white #ffffff;
236 @define-color grey #555555;
237 @define-color blue #1a8fff;
238 @define-color green #23fd00;
239 @define-color orange #f28a21;
240 @define-color red #f2201f;
241
242 * {
243 border: none;
244 font-family: "Fira Sans Nerd Font";
245 font-size: 10pt;
246 min-height: 0;
247 }
248
249 window#waybar {
250 background-color: rgba(0, 0, 0, 0.66);
251 color: @white;
252 }
253
254 .modules-left {
255 margin-left: 8px;
256 }
257 .modules-right {
258 margin-right: 8px;
259 }
260
261 .module {
262 margin: 0 5px;
263 }
264
265 #workspaces button {
266 color: @white;
267 padding: 2px 5px;
268 }
269 #workspaces button.empty {
270 color: @grey;
271 }
272 #workspaces button.active {
273 color: @green;
274 }
275 #workspaces button.urgent {
276 color: @red;
277 }
278
279 #custom-weather, #custom-keymap, #custom-worktime, #custom-worktime-today {
280 color: @grey;
281 margin: 0 5px;
282 }
283 #custom-weather, #custom-worktime-today {
284 margin-right: 3px;
285 }
286 #custom-keymap, #custom-weather {
287 margin-left: 3px;
288 }
289
290 #tray {
291 margin: 0;
292 }
293 #battery, #idle_inhibitor, #backlight, #wireplumber, #custom-mako {
294 color: @grey;
295 margin: 0 5px 0 2px;
296 }
297 #idle_inhibitor {
298 margin-right: 4px;
299 margin-left: 6px;
300 }
301 #custom-mako {
302 margin-right: 2px;
303 margin-left: 3px;
304 }
305 #battery {
306 margin-right: 3px;
307 }
308 #battery.discharging {
309 color: @white;
310 }
311 #battery.warning {
312 color: @orange;
313 }
314 #battery.critical {
315 color: @red;
316 }
317 #battery.charging {
318 color: @white;
319 }
320 #idle_inhibitor.activated {
321 color: @white;
322 }
323
324 #idle_inhibitor {
325 padding-top: 1px;
326 }
327
328 #privacy {
329 color: @red;
330 margin: -1px 2px 0px 5px;
331 }
332 #clock {
333 /* margin-right: 5px; */
334 }
335 '';
336 };
337 };
338}
diff --git a/accounts/gkleen@sif/systemd.nix b/accounts/gkleen@sif/systemd.nix
index 33bf7ef2..34f52172 100644
--- a/accounts/gkleen@sif/systemd.nix
+++ b/accounts/gkleen@sif/systemd.nix
@@ -126,15 +126,6 @@ in {
126 After = ["graphical-session-pre.target"]; 126 After = ["graphical-session-pre.target"];
127 }; 127 };
128 }; 128 };
129 dunst = {
130 Service = {
131 ExecStart = lib.mkForce "${cfg.services.dunst.package}/bin/dunst";
132 Restart = "always";
133 };
134 Install = {
135 WantedBy = ["graphical-session.target"];
136 };
137 };
138 keepassxc = { 129 keepassxc = {
139 Service = { 130 Service = {
140 Type = "dbus"; 131 Type = "dbus";
@@ -204,20 +195,6 @@ in {
204 WatchdogSec = "2s"; 195 WatchdogSec = "2s";
205 }; 196 };
206 }; 197 };
207 polkit-gnome-authentication-agent-1 = {
208 Install = {
209 WantedBy = ["graphical-session.target"];
210 };
211 Unit = {
212 PartOf = ["graphical-session.target"];
213 Requires = ["graphical-session-pre.target"];
214 After = ["graphical-session-pre.target"];
215 };
216 Service = {
217 ExecStart = "${pkgs.polkit_gnome}/libexec/polkit-gnome-authentication-agent-1";
218 Restart = "on-failure";
219 };
220 };
221 gtklock = { 198 gtklock = {
222 Unit = { 199 Unit = {
223 Requisite = ["graphical-session.target"]; 200 Requisite = ["graphical-session.target"];
@@ -247,7 +224,7 @@ in {
247 fi 224 fi
248 monitors+=([$monitor]="$blurred_path") 225 monitors+=([$monitor]="$blurred_path")
249 done < <(wpaperctl all-wallpapers -j | jq -c ".[]") 226 done < <(wpaperctl all-wallpapers -j | jq -c ".[]")
250 wait 227 # wait
251 228
252 cp --no-preserve=mode ${pkgs.writeText "gtklock.css" '' 229 cp --no-preserve=mode ${pkgs.writeText "gtklock.css" ''
253 #window-box { 230 #window-box {
@@ -273,7 +250,7 @@ in {
273 ]; 250 ];
274 NotifyAccess = "all"; 251 NotifyAccess = "all";
275 ExecStart = ''${lib.getExe pkgs.gtklock} -s "''${RUNTIME_DIRECTORY}/style.css" -L ${pkgs.writeShellScript "after-lock" '' 252 ExecStart = ''${lib.getExe pkgs.gtklock} -s "''${RUNTIME_DIRECTORY}/style.css" -L ${pkgs.writeShellScript "after-lock" ''
276 ${cfg.wayland.windowManager.hyprland.package}/bin/hyprctl dispatch dpms off 253 ${lib.getExe cfg.programs.niri.package} msg action power-off-monitors
277 ${config.systemd.package}/bin/systemd-notify --ready 254 ${config.systemd.package}/bin/systemd-notify --ready
278 ''}''; 255 ''}'';
279 }; 256 };
@@ -322,6 +299,21 @@ in {
322 ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\""; 299 ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\"";
323 }; 300 };
324 }; 301 };
302 wpaperd = {
303 Install = {
304 WantedBy = ["graphical-session.target"];
305 };
306 Unit = {
307 BindsTo = ["graphical-session-pre.target"];
308 After = ["graphical-session-pre.target"];
309 };
310 Service = {
311 ExecStart = lib.getExe cfg.programs.wpaperd.package;
312 Type = "simple";
313 Restart = "always";
314 RestartSec = "2s";
315 };
316 };
325 } // listToAttrs (map ({host, port}: nameValuePair "proxy-to-autossh-socks@${toString port}" { 317 } // listToAttrs (map ({host, port}: nameValuePair "proxy-to-autossh-socks@${toString port}" {
326 Unit = { 318 Unit = {
327 Requires = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"]; 319 Requires = ["autossh-socks@${host}:${toString (port + 1)}.service" "proxy-to-autossh-socks@${toString port}.socket"];
diff --git a/accounts/gkleen@sif/taffybar/default.nix b/accounts/gkleen@sif/taffybar/default.nix
deleted file mode 100644
index 98366d8f..00000000
--- a/accounts/gkleen@sif/taffybar/default.nix
+++ /dev/null
@@ -1,2 +0,0 @@
1{ haskellPackages ? (import <nixpkgs> {}).haskellPackages }:
2haskellPackages.callCabal2nix "gkleen-sif-taffybar" ./. {}
diff --git a/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal b/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal
deleted file mode 100644
index e32cb473..00000000
--- a/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal
+++ /dev/null
@@ -1,32 +0,0 @@
1name: gkleen-sif-taffybar
2version: 0.0.0
3build-type: Simple
4cabal-version: >=1.10
5
6data-files: taffybar.css
7
8executable taffybar
9 hs-source-dirs: src
10 main-is: taffybar.hs
11 ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2 -Wall
12 build-depends: base
13 , containers
14 , directory
15 , filepath
16 , gtk3
17 , taffybar
18 , X11>=1.8
19 , transformers
20 , gi-gtk
21 , time, time-locale-compat
22 , text
23 , HStringTemplate
24 , gtk-sni-tray
25 , hslogger
26 other-modules: Paths_gkleen_sif_taffybar
27 , System.Taffybar.Widget.Clock
28 , System.Taffybar.Widget.TooltipBattery
29 default-language: Haskell2010
30 default-extensions: ScopedTypeVariables
31 , LambdaCase
32 , NamedFieldPuns \ No newline at end of file
diff --git a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs b/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs
deleted file mode 100644
index e8dc480f..00000000
--- a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs
+++ /dev/null
@@ -1,111 +0,0 @@
1{-# LANGUAGE OverloadedStrings #-}
2module System.Taffybar.Widget.Clock
3 ( textClockNew
4 , textClockNewWith
5 , defaultClockConfig
6 , ClockConfig(..)
7 , ClockUpdateStrategy(..)
8 ) where
9
10import Control.Monad.IO.Class
11import Data.Maybe
12import qualified Data.Text as T
13import qualified Data.Time.Clock as Clock
14import Data.Time.Format
15import Data.Time.LocalTime
16import qualified Data.Time.Locale.Compat as L
17import GI.Gtk
18import System.Taffybar.Widget.Generic.PollingLabel
19
20type ClockFormat = L.TimeLocale -> ZonedTime -> T.Text
21
22-- | Create the widget. I recommend passing @Nothing@ for the TimeLocale
23-- parameter. The format string can include Pango markup
24-- (<http://developer.gnome.org/pango/stable/PangoMarkupFormat.html>).
25textClockNew ::
26 MonadIO m => Maybe L.TimeLocale -> ClockFormat -> Double -> m GI.Gtk.Widget
27textClockNew userLocale format interval =
28 textClockNewWith cfg
29 where
30 cfg = defaultClockConfig { clockTimeLocale = userLocale
31 , clockFormat = format
32 , clockUpdateStrategy = ConstantInterval interval
33 }
34
35data ClockUpdateStrategy
36 = ConstantInterval Double
37 | RoundedTargetInterval Int Double
38 deriving (Eq, Ord, Show)
39
40data ClockConfig = ClockConfig
41 { clockTimeZone :: Maybe TimeZone
42 , clockTimeLocale :: Maybe L.TimeLocale
43 , clockFormat :: ClockFormat
44 , clockUpdateStrategy :: ClockUpdateStrategy
45 }
46
47-- | A clock configuration that defaults to the current locale
48defaultClockConfig :: ClockConfig
49defaultClockConfig = ClockConfig
50 { clockTimeZone = Nothing
51 , clockTimeLocale = Nothing
52 , clockFormat = \locale zonedTime -> T.pack $ formatTime locale "%a %b %_d %r" zonedTime
53 , clockUpdateStrategy = RoundedTargetInterval 5 0.0
54 }
55
56systemGetTZ :: IO TimeZone
57systemGetTZ = getCurrentTimeZone
58
59-- | A configurable text-based clock widget. It currently allows for
60-- a configurable time zone through the 'ClockConfig'.
61--
62-- See also 'textClockNew'.
63textClockNewWith :: MonadIO m => ClockConfig -> m Widget
64textClockNewWith ClockConfig
65 { clockTimeZone = userZone
66 , clockTimeLocale = userLocale
67 , clockFormat = format
68 , clockUpdateStrategy = updateStrategy
69 } = liftIO $ do
70 let getTZ = maybe systemGetTZ return userZone
71 locale = fromMaybe L.defaultTimeLocale userLocale
72
73 let getUserZonedTime =
74 utcToZonedTime <$> getTZ <*> Clock.getCurrentTime
75
76 doTimeFormat = format locale
77
78 getRoundedTimeAndNextTarget = do
79 zonedTime <- getUserZonedTime
80 return $ case updateStrategy of
81 ConstantInterval interval ->
82 (doTimeFormat zonedTime, Nothing, interval)
83 RoundedTargetInterval roundSeconds offset ->
84 let roundSecondsDiffTime = fromIntegral roundSeconds
85 addTheRound = addLocalTime roundSecondsDiffTime
86 localTime = zonedTimeToLocalTime zonedTime
87 ourLocalTimeOfDay = localTimeOfDay localTime
88 seconds = round $ todSec ourLocalTimeOfDay
89 secondsFactor = seconds `div` roundSeconds
90 displaySeconds = secondsFactor * roundSeconds
91 baseLocalTimeOfDay =
92 ourLocalTimeOfDay { todSec = fromIntegral displaySeconds }
93 ourLocalTime =
94 localTime { localTimeOfDay = baseLocalTimeOfDay }
95 roundedLocalTime =
96 if seconds `mod` roundSeconds > roundSeconds `div` 2
97 then addTheRound ourLocalTime
98 else ourLocalTime
99 roundedZonedTime =
100 zonedTime { zonedTimeToLocalTime = roundedLocalTime }
101 nextTarget = addTheRound ourLocalTime
102 amountToWait = realToFrac $ diffLocalTime nextTarget localTime
103 in (doTimeFormat roundedZonedTime, Nothing, amountToWait - offset)
104
105 label <- pollingLabelWithVariableDelay getRoundedTimeAndNextTarget
106 ebox <- eventBoxNew
107 containerAdd ebox label
108 eventBoxSetVisibleWindow ebox False
109 widgetShowAll ebox
110 toWidget ebox
111
diff --git a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs b/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs
deleted file mode 100644
index 9dc52774..00000000
--- a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs
+++ /dev/null
@@ -1,101 +0,0 @@
1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE ScopedTypeVariables #-}
3module System.Taffybar.Widget.TooltipBattery ( batteryIconTooltipNew ) where
4
5import Control.Applicative
6import Control.Monad
7import Control.Monad.IO.Class
8import Control.Monad.Trans.Reader
9import Data.Int (Int64)
10import qualified Data.Text as T
11import GI.Gtk
12import Prelude
13import StatusNotifier.Tray (scalePixbufToSize)
14import System.Taffybar.Context
15import System.Taffybar.Information.Battery
16import System.Taffybar.Util
17import System.Taffybar.Widget.Generic.AutoSizeImage
18import System.Taffybar.Widget.Generic.ChannelWidget
19import Text.Printf
20import Text.StringTemplate
21import Data.Function ((&))
22
23-- | Just the battery info that will be used for display (this makes combining
24-- several easier).
25data BatteryWidgetInfo = BWI
26 { seconds :: Maybe Int64
27 , percent :: Double
28 , status :: String
29 , rate :: Maybe Double
30 } deriving (Eq, Show)
31
32-- | Format a duration expressed as seconds to hours and minutes
33formatDuration :: Int64 -> String
34formatDuration secs = let minutes, hours, minutes' :: Int64
35 minutes = secs `div` 60
36 (hours, minutes') = minutes `divMod` 60
37 in printf "%02d:%02d" hours minutes'
38
39getBatteryWidgetInfo :: BatteryInfo -> BatteryWidgetInfo
40getBatteryWidgetInfo info =
41 let battPctNum :: Double
42 battPctNum = batteryPercentage info
43 battTime :: Maybe Int64
44 battTime =
45 case batteryState info of
46 BatteryStateCharging -> Just $ batteryTimeToFull info
47 BatteryStateDischarging -> Just $ batteryTimeToEmpty info
48 _ -> Nothing
49 battStatus :: String
50 battStatus =
51 case batteryState info of
52 BatteryStateCharging -> "↑"
53 BatteryStateDischarging -> "↓"
54 BatteryStateEmpty -> "⤓"
55 BatteryStateFullyCharged -> "⤒"
56 _ -> "?"
57 battRate :: Maybe Double
58 battRate | rawRate < 0.1 = Nothing
59 | otherwise = Just rawRate
60 where rawRate = batteryEnergyRate info
61 in BWI{ seconds = battTime, percent = battPctNum, status = battStatus, rate = battRate }
62
63-- | Given (maybe summarized) battery info and format: provides the string to display
64formatBattInfo :: BatteryWidgetInfo -> String -> T.Text
65formatBattInfo info fmt =
66 let tpl = newSTMP fmt
67 tpl' = tpl
68 & setManyAttrib [ ("percentage", printf "%.0f" $ percent info)
69 , ("status", status info)
70 ]
71 & setManyAttrib [ ("time", formatDuration <$> seconds info)
72 , ("rate", printf "%.0f" <$> rate info)
73 ]
74 in render tpl'
75
76themeLoadFlags :: [IconLookupFlags]
77themeLoadFlags = [IconLookupFlagsGenericFallback, IconLookupFlagsUseBuiltin]
78
79batteryIconTooltipNew :: String -> TaffyIO Widget
80batteryIconTooltipNew format = do
81 DisplayBatteryChanVar (chan, _) <- setupDisplayBatteryChanVar ["IconName", "State", "Percentage", "TimeToFull", "TimeToEmpty", "EnergyRate"]
82 ctx <- ask
83 liftIO $ do
84 image <- imageNew
85 styleCtx <- widgetGetStyleContext =<< toWidget image
86 defaultTheme <- iconThemeGetDefault
87 let getCurrentBatteryIconNameStringTooltip = do
88 info <- runReaderT getDisplayBatteryInfo ctx
89 let iconNameString = T.pack $ batteryIconName info
90 tooltip = formatBattInfo (getBatteryWidgetInfo info) format
91 return (iconNameString, tooltip)
92 extractPixbuf info =
93 fst <$> iconInfoLoadSymbolicForContext info styleCtx
94 setIconForSize size = do
95 (name, tooltip) <- getCurrentBatteryIconNameStringTooltip
96 widgetSetTooltipMarkup image $ Just tooltip
97 iconThemeLookupIcon defaultTheme name size themeLoadFlags >>=
98 traverse extractPixbuf >>=
99 traverse (scalePixbufToSize size OrientationHorizontal)
100 updateImage <- autoSizeImage image setIconForSize OrientationHorizontal
101 toWidget =<< channelWidgetNew image chan (const $ postGUIASync updateImage)
diff --git a/accounts/gkleen@sif/taffybar/src/taffybar.hs b/accounts/gkleen@sif/taffybar/src/taffybar.hs
deleted file mode 100644
index 67ee942d..00000000
--- a/accounts/gkleen@sif/taffybar/src/taffybar.hs
+++ /dev/null
@@ -1,89 +0,0 @@
1{-# LANGUAGE OverloadedStrings #-}
2
3module Main where
4
5import System.Taffybar (startTaffybar)
6import System.Taffybar.Context (TaffybarConfig(..))
7import System.Taffybar.Hooks
8import System.Taffybar.SimpleConfig hiding (SimpleTaffyConfig(cssPaths))
9import System.Taffybar.Widget
10import qualified System.Taffybar.Widget.Clock as MyClock
11import System.Taffybar.Widget.TooltipBattery
12
13import Data.Time.Format
14import Data.Time.LocalTime
15import Data.Time.Calendar.WeekDate
16
17import qualified Data.Text as T
18
19import Control.Exception (SomeException, try)
20import Control.Monad.Trans.Reader (mapReaderT)
21
22import Paths_gkleen_sif_taffybar
23
24import System.Log.Logger
25
26
27main :: IO ()
28main = do
29 logger <- getLogger "System.Taffybar"
30 saveGlobalLogger $ setLevel INFO logger
31
32 myCssPath <- getDataFileName "taffybar.css"
33 startTaffybar taffybarConfig{ cssPaths = pure myCssPath }
34
35
36taffybarConfig :: TaffybarConfig
37taffybarConfig =
38 let myWorkspacesConfig =
39 defaultWorkspacesConfig
40 { maxIcons = Just 0
41 , widgetGap = 7
42 , showWorkspaceFn = \case
43 -- Workspace{ workspaceState = Empty } -> False
44 Workspace{ workspaceName } | workspaceName == "NSP" -> False
45 _other -> True
46 , getWindowIconPixbuf = \i d -> either (\(_ :: SomeException) -> Nothing) id <$> mapReaderT try (defaultGetWindowIconPixbuf i d)
47 , urgentWorkspaceState = True
48 }
49 workspaces = workspacesNew myWorkspacesConfig
50 clock = MyClock.textClockNewWith MyClock.defaultClockConfig
51 { MyClock.clockUpdateStrategy = MyClock.RoundedTargetInterval 1 0.0
52 , MyClock.clockFormat = \tl zt@ZonedTime{ zonedTimeToLocalTime = LocalTime{ localDay } }
53 -> let date = formatTime tl "%Y-%m-%d" localDay
54 weekdate = "W" <> show2 woy <> "-" <> show dow
55 where (_, woy, dow) = toWeekDate localDay
56 show2 :: Int -> String
57 show2 x = replicate (2 - length s) '0' ++ s
58 where s = show x
59 time = formatTime tl "%H:%M:%S%Ez" zt
60 in T.intercalate " " $ map T.pack [weekdate, date, time]
61 }
62 layout = layoutNew defaultLayoutConfig
63 windowsW = windowsNew defaultWindowsConfig
64 { getMenuLabel = truncatedGetMenuLabel 80
65 , getActiveLabel = truncatedGetActiveLabel 80
66 }
67 worktime = commandRunnerNew 60 "worktime" [] "worktime"
68 worktimeToday = commandRunnerNew 60 "worktime" ["today"] "worktime today"
69 -- See https://github.com/taffybar/gtk-sni-tray#statusnotifierwatcher
70 -- for a better way to set up the sni tray
71 -- tray = sniTrayThatStartsWatcherEvenThoughThisIsABadWayToDoIt
72 tray = sniTrayNew
73 myConfig = defaultSimpleTaffyConfig
74 { startWidgets =
75 workspaces : map (>>= buildContentsBox) [ layout, windowsW ]
76 , endWidgets = map (>>= buildContentsBox) $ reverse
77 -- , mpris2New
78 [ worktime, worktimeToday
79 , clock
80 , tray
81 , batteryIconTooltipNew "$status$ $percentage$%$if(time)$$if(rate)$ ($rate$W $time$)$else$ ($time$)$endif$$elseif(rate)$ ($rate$W)$endif$"
82 ]
83 , barPosition = Top
84 , barPadding = 2
85 , barHeight = ExactSize 28
86 , widgetSpacing = 10
87 }
88 in withBatteryRefresh $ withLogServer $
89 withToggleServer $ toTaffyConfig myConfig
diff --git a/accounts/gkleen@sif/taffybar/taffybar.css b/accounts/gkleen@sif/taffybar/taffybar.css
deleted file mode 100644
index 7a297465..00000000
--- a/accounts/gkleen@sif/taffybar/taffybar.css
+++ /dev/null
@@ -1,146 +0,0 @@
1@define-color transparent rgba(0.0, 0.0, 0.0, 0.0);
2@define-color white #808080;
3@define-color gray #202020;
4@define-color green #008000;
5@define-color yellow #808000;
6@define-color blue #000080;
7@define-color red #800000;
8@define-color black #000000;
9/* @define-color taffy-blue #0c7cd5; */
10@define-color taffy-blue @blue;
11
12@define-color active-window-color @white;
13@define-color urgent-window-color @taffy-blue;
14@define-color font-color @white;
15@define-color menu-background-color @black;
16@define-color menu-font-color @white;
17
18/* Top level styling */
19
20.taffy-window * {
21 /*
22 This removes any existing styling from UI elements. Taffybar will not
23 cohere with your gtk theme.
24 */
25 all: unset;
26
27 font-family: "Fira Sans", sans-serif;
28 font-size: 21px;
29 color: @font-color;
30}
31
32.taffy-box {
33 /* border-radius: 10px; */
34 background-color: @black;
35}
36
37.inner-pad {
38 /* padding-bottom: 5px; */
39 /* padding-top: 5px; */
40 padding-left: 2px;
41 padding-right: 2px;
42}
43
44.contents {
45 /* padding-bottom: 4px; */
46 /* padding-top: 4px; */
47 padding-right: 2px;
48 padding-left: 2px;
49 transition: background-color .5s;
50 border-radius: 5px;
51}
52
53/* Workspaces styling */
54
55.workspace-label {
56 padding-right: 3px;
57 padding-left: 2px;
58 font-size: 21px;
59}
60
61.workspace-label.active {
62 color: @green;
63}
64.workspace-label.visible {
65 color: @yellow;
66}
67.workspace-label.empty {
68 color: @gray;
69}
70.workspace-label.urgent {
71 color: @red;
72}
73
74.active .contents {
75 background-color: rgba(0.0, 0.0, 0.0, 0.5);
76}
77
78.visible .contents {
79 background-color: rgba(0.0, 0.0, 0.0, 0.2);
80}
81
82.window-icon-container {
83 transition: opacity .5s, box-shadow .5s;
84 opacity: 1;
85}
86
87/* This gives space for the box-shadow (they look like underlines) that follow.
88 This will actually affect all widgets, (not just the workspace icons), but
89 that is what we want since we want the icons to look the same. */
90.auto-size-image, .sni-tray {
91 padding-top: 3px;
92 padding-bottom: 3px;
93}
94
95.window-icon-container.active {
96 box-shadow: inset 0 -3px @white;
97}
98
99.window-icon-container.urgent {
100 box-shadow: inset 0 -3px @urgent-window-color;
101}
102
103.window-icon-container.inactive .window-icon {
104 padding: 0px;
105}
106
107.window-icon-container.minimized .window-icon {
108 opacity: .3;
109}
110
111.window-icon {
112 opacity: 1;
113 transition: opacity .5s;
114}
115
116/* Button styling */
117
118button {
119 background-color: @transparent;
120 border-width: 0px;
121 border-radius: 0px;
122}
123
124button:checked, button:hover .Contents:hover {
125 box-shadow: inset 0 -3px @taffy-blue;
126}
127
128/* Menu styling */
129
130/* The ".taffy-window" prefixed selectors are needed because if they aren't present,
131 the top level .Taffybar selector takes precedence */
132.taffy-window menuitem *, menuitem * {
133 color: @menu-font-color;
134}
135
136.taffy-window menuitem, menuitem {
137 background-color: @menu-background-color;
138}
139
140.taffy-window menuitem:hover, menuitem:hover {
141 background-color: @taffy-blue;
142}
143
144.taffy-window menuitem:hover > label, menuitem:hover > label {
145 color: @white;
146}
diff --git a/accounts/gkleen@sif/xmonad/.gitignore b/accounts/gkleen@sif/xmonad/.gitignore
deleted file mode 100644
index c11891cd..00000000
--- a/accounts/gkleen@sif/xmonad/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
1**/#*#
2**/.stack-work/
3/stack.yaml.lock
4/*.cabal
diff --git a/accounts/gkleen@sif/xmonad/default.nix b/accounts/gkleen@sif/xmonad/default.nix
deleted file mode 100644
index 8790c12f..00000000
--- a/accounts/gkleen@sif/xmonad/default.nix
+++ /dev/null
@@ -1,7 +0,0 @@
1argumentPackages@{ ... }:
2
3let
4 # defaultPackages = (import ./stackage.nix {});
5 # haskellPackages = defaultPackages // argumentPackages;
6 haskellPackages = argumentPackages;
7in haskellPackages.callPackage ./xmonad-yggdrasil.nix {}
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs
deleted file mode 100644
index e6accdcc..00000000
--- a/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs
+++ /dev/null
@@ -1,127 +0,0 @@
1{-# LANGUAGE DeriveGeneric, OverloadedLists, OverloadedStrings, ViewPatterns, ExistentialQuantification, MultiWayIf #-}
2
3module XMonad.Mpv
4 ( MpvCommand(..), MpvResponse(..), MpvException(..)
5 , mpv
6 , mpvDir
7 , mpvAll, mpvOne
8 , mpvResponse
9 ) where
10
11import Data.Aeson
12
13import Data.Monoid
14
15import Network.Socket hiding (recv)
16import Network.Socket.ByteString
17
18import qualified Data.ByteString as BS
19import qualified Data.ByteString.Char8 as CBS
20import qualified Data.ByteString.Lazy as LBS
21
22import GHC.Generics (Generic)
23import Data.Typeable (Typeable)
24import Data.String (IsString(..))
25
26import Control.Exception
27
28import System.IO.Temp (getCanonicalTemporaryDirectory)
29
30import Control.Monad
31import Control.Exception (bracket)
32import Control.Monad.IO.Class (MonadIO(..))
33
34import System.FilePath
35import System.Directory (getDirectoryContents)
36
37import Data.List
38import Data.Either
39import Data.Maybe
40
41import Debug.Trace
42
43
44data MpvCommand
45 = forall a. ToJSON a => MpvSetProperty String a
46 | MpvGetProperty String
47data MpvResponse
48 = MpvError String
49 | MpvSuccess (Maybe Value)
50 deriving (Read, Show, Generic, Eq)
51data MpvException = MpvException String
52 | MpvNoValue
53 | MpvNoParse String
54 deriving (Generic, Typeable, Read, Show)
55instance Exception MpvException
56
57
58instance ToJSON MpvCommand where
59 toJSON (MpvSetProperty name val) = Array ["set_property", fromString name, toJSON val]
60 toJSON (MpvGetProperty name) = Array ["get_property", fromString name]
61
62instance FromJSON MpvResponse where
63 parseJSON = withObject "response object" $ \obj -> do
64 mval <- obj .:? "data"
65 err <- obj .: "error"
66
67 let ret
68 | err == "success" = MpvSuccess mval
69 | otherwise = MpvError err
70
71 return ret
72
73mpvSocket :: FilePath -> (Socket -> IO a) -> IO a
74mpvSocket sockPath = withSocketsDo . bracket mkSock close
75 where
76 mkSock = do
77 sock <- socket AF_UNIX Stream defaultProtocol
78 connect sock $ SockAddrUnix (traceId sockPath)
79 return sock
80
81mpvResponse :: FromJSON v => MpvResponse -> IO v
82mpvResponse (MpvError str) = throwIO $ MpvException str
83mpvResponse (MpvSuccess Nothing) = throwIO MpvNoValue
84mpvResponse (MpvSuccess (Just v)) = case fromJSON v of
85 Success v' -> return v'
86 Error str -> throwIO $ MpvNoParse str
87
88mpv :: FilePath -> MpvCommand -> IO MpvResponse
89mpv sockPath cmd = mpvSocket sockPath $ \sock -> do
90 let message = (`BS.append` "\n") . LBS.toStrict . encode $ Object [("command", toJSON cmd)]
91 traceIO $ show message
92 sendAll sock message
93 let recvAll = do
94 prefix <- recv sock 4096
95 if
96 | (prefix', rest) <- CBS.break (== '\n') prefix
97 , not (BS.null rest) -> return prefix'
98 | BS.null prefix -> return prefix
99 | otherwise -> BS.append prefix <$> recvAll
100 response <- recvAll
101 traceIO $ show response
102 either (ioError . userError) return . traceShowId $ eitherDecodeStrict' response
103
104mpvDir :: Exception e => FilePath -> (FilePath -> [(FilePath, Either e MpvResponse)] -> Maybe MpvCommand) -> IO [(FilePath, Either e MpvResponse)]
105mpvDir dir step = do
106 socks <- filter (".sock" `isSuffixOf`) <$> getDirectoryContents dir
107 go [] socks
108 where
109 go acc [] = return acc
110 go acc (sock:socks)
111 | Just cmd <- step sock acc = do
112 res <- try $ mpv (dir </> sock) cmd
113 go ((sock, res) : acc) socks
114 | otherwise =
115 go acc socks
116
117mpvAll :: FilePath -> MpvCommand -> IO [MpvResponse]
118mpvAll dir cmd = do
119 results <- map snd <$> (mpvDir dir (\_ _ -> Just cmd) :: IO [(FilePath, Either SomeException MpvResponse)])
120 mapM (either throwIO return) results
121
122mpvOne :: FilePath -> MpvCommand -> IO (Maybe MpvResponse)
123mpvOne dir cmd = listToMaybe . snd . partitionEithers . map snd <$> (mpvDir dir step :: IO [(FilePath, Either SomeException MpvResponse)])
124 where
125 step _ results
126 | any (isRight . snd) results = Nothing
127 | otherwise = Just cmd
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs
deleted file mode 100644
index 1caefae5..00000000
--- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs
+++ /dev/null
@@ -1,94 +0,0 @@
1module XMonad.Prompt.MyPass
2 (
3 -- * Usages
4 -- $usages
5 mkPassPrompt
6 ) where
7
8import Control.Monad (liftM)
9import XMonad.Core
10import XMonad.Prompt ( XPrompt
11 , showXPrompt
12 , commandToComplete
13 , nextCompletion
14 , getNextCompletion
15 , XPConfig
16 , mkXPrompt
17 , searchPredicate)
18import System.Directory (getHomeDirectory)
19import System.FilePath (takeExtension, dropExtension, combine)
20import System.Posix.Env (getEnv)
21import XMonad.Util.Run (runProcessWithInput)
22
23-- $usages
24-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
25--
26-- > import XMonad.Prompt.Pass
27--
28-- Then add a keybinding for 'passPrompt', 'passGeneratePrompt' or 'passRemovePrompt':
29--
30-- > , ((modMask x , xK_p) , passPrompt xpconfig)
31-- > , ((modMask x .|. controlMask, xK_p) , passGeneratePrompt xpconfig)
32-- > , ((modMask x .|. controlMask .|. shiftMask, xK_p), passRemovePrompt xpconfig)
33--
34-- For detailed instructions on:
35--
36-- - editing your key bindings, see "XMonad.Doc.Extending#Editing_key_bindings".
37--
38-- - how to setup the password storage, see <http://git.zx2c4.com/password-store/about/>
39--
40
41type Predicate = String -> String -> Bool
42
43getPassCompl :: [String] -> Predicate -> String -> IO [String]
44getPassCompl compls p s
45 | length s <= minL
46 , all ((> minL) . length) compls = return []
47 | otherwise = do return $ filter (p s) compls
48 where
49 minL = 3
50
51type PromptLabel = String
52
53data Pass = Pass PromptLabel
54
55instance XPrompt Pass where
56 showXPrompt (Pass prompt) = prompt ++ ": "
57 commandToComplete _ c = c
58 nextCompletion _ = getNextCompletion
59
60-- | Default password store folder in $HOME/.password-store
61--
62passwordStoreFolderDefault :: String -> String
63passwordStoreFolderDefault home = combine home ".password-store"
64
65-- | Compute the password store's location.
66-- Use the PASSWORD_STORE_DIR environment variable to set the password store.
67-- If empty, return the password store located in user's home.
68--
69passwordStoreFolder :: IO String
70passwordStoreFolder =
71 getEnv "PASSWORD_STORE_DIR" >>= computePasswordStoreDir
72 where computePasswordStoreDir Nothing = liftM passwordStoreFolderDefault getHomeDirectory
73 computePasswordStoreDir (Just storeDir) = return storeDir
74
75-- | A pass prompt factory
76--
77mkPassPrompt :: PromptLabel -> (String -> X ()) -> XPConfig -> X ()
78mkPassPrompt promptLabel passwordFunction xpconfig = do
79 passwords <- io (passwordStoreFolder >>= getPasswords)
80 mkXPrompt (Pass promptLabel) xpconfig (getPassCompl passwords $ searchPredicate xpconfig) passwordFunction
81
82-- | Retrieve the list of passwords from the password storage 'passwordStoreDir
83getPasswords :: FilePath -> IO [String]
84getPasswords passwordStoreDir = do
85 files <- runProcessWithInput "find" [
86 passwordStoreDir,
87 "-type", "f",
88 "-name", "*.gpg",
89 "-printf", "%P\n"] []
90 return $ map removeGpgExtension $ lines files
91
92removeGpgExtension :: String -> String
93removeGpgExtension file | takeExtension file == ".gpg" = dropExtension file
94 | otherwise = file
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs
deleted file mode 100644
index c268f87d..00000000
--- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs
+++ /dev/null
@@ -1,105 +0,0 @@
1module XMonad.Prompt.MyShell
2 ( Shell (..)
3 , shellPrompt
4 , prompt
5 , safePrompt
6 , unsafePrompt
7 , getCommands
8 , getShellCompl
9 , split
10 ) where
11
12import Codec.Binary.UTF8.String (encodeString)
13import Control.Exception as E
14import Control.Monad (forM)
15import Data.List (isPrefixOf)
16import System.Directory (doesDirectoryExist, getDirectoryContents)
17import System.Environment (getEnv)
18import System.Posix.Files (getFileStatus, isDirectory)
19
20import XMonad hiding (config)
21import XMonad.Prompt
22import XMonad.Util.Run
23
24econst :: Monad m => a -> IOException -> m a
25econst = const . return
26
27data Shell = Shell String
28
29instance XPrompt Shell where
30 showXPrompt (Shell q) = q
31 completionToCommand _ = escape
32
33shellPrompt :: String -> XPConfig -> X ()
34shellPrompt q c = do
35 cmds <- io getCommands
36 mkXPrompt (Shell q) c (getShellCompl cmds) spawn
37
38{- $spawns
39 See safe and unsafeSpawn in "XMonad.Util.Run".
40 prompt is an alias for safePrompt;
41 safePrompt and unsafePrompt work on the same principles, but will use
42 XPrompt to interactively query the user for input; the appearance is
43 set by passing an XPConfig as the second argument. The first argument
44 is the program to be run with the interactive input.
45 You would use these like this:
46
47 > , ((modm, xK_b), safePrompt "firefox" greenXPConfig)
48 > , ((modm .|. shiftMask, xK_c), prompt ("xterm" ++ " -e") greenXPConfig)
49
50 Note that you want to use safePrompt for Firefox input, as Firefox
51 wants URLs, and unsafePrompt for the XTerm example because this allows
52 you to easily start a terminal executing an arbitrary command, like
53 'top'. -}
54
55prompt, unsafePrompt, safePrompt :: String -> FilePath -> XPConfig -> X ()
56prompt = unsafePrompt
57safePrompt q c config = mkXPrompt (Shell q) config (getShellCompl [c]) run
58 where run = safeSpawn c . return
59unsafePrompt q c config = mkXPrompt (Shell q) config (getShellCompl [c]) run
60 where run a = unsafeSpawn $ c ++ " " ++ a
61
62getShellCompl :: [String] -> String -> IO [String]
63getShellCompl cmds s | s == "" || last s == ' ' = return []
64 | otherwise = do
65 f <- fmap lines $ runProcessWithInput "bash" [] ("compgen -A file -- "
66 ++ s ++ "\n")
67 files <- case f of
68 [x] -> do fs <- getFileStatus (encodeString x)
69 if isDirectory fs then return [x ++ "/"]
70 else return [x]
71 _ -> return f
72 return . uniqSort $ files ++ commandCompletionFunction cmds s
73
74commandCompletionFunction :: [String] -> String -> [String]
75commandCompletionFunction cmds str | '/' `elem` str = []
76 | otherwise = filter (isPrefixOf str) cmds
77
78getCommands :: IO [String]
79getCommands = do
80 p <- getEnv "PATH" `E.catch` econst []
81 let ds = filter (/= "") $ split ':' p
82 es <- forM ds $ \d -> do
83 exists <- doesDirectoryExist d
84 if exists
85 then getDirectoryContents d
86 else return []
87 return . uniqSort . filter ((/= '.') . head) . concat $ es
88
89split :: Eq a => a -> [a] -> [[a]]
90split _ [] = []
91split e l =
92 f : split e (rest ls)
93 where
94 (f,ls) = span (/=e) l
95 rest s | s == [] = []
96 | otherwise = tail s
97
98escape :: String -> String
99escape [] = ""
100escape (x:xs)
101 | isSpecialChar x = '\\' : x : escape xs
102 | otherwise = x : escape xs
103
104isSpecialChar :: Char -> Bool
105isSpecialChar = flip elem " &\\@\"'#?$*()[]{};"
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs
deleted file mode 100644
index 998c533e..00000000
--- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs
+++ /dev/null
@@ -1,246 +0,0 @@
1module XMonad.Prompt.MySsh
2 ( -- * Usage
3 -- $usage
4 sshPrompt,
5 Ssh,
6 Override (..),
7 mkOverride,
8 Conn (..),
9 moshCmd,
10 moshCmd',
11 sshCmd,
12 inTmux,
13 withEnv
14 ) where
15
16import XMonad
17import XMonad.Util.Run
18import XMonad.Prompt
19
20import System.Directory
21import System.Environment
22import qualified Control.Exception as E
23
24import Control.Monad
25import Data.Maybe
26
27import Text.Parsec.String
28import Text.Parsec
29import Data.Char (isSpace)
30
31econst :: Monad m => a -> E.IOException -> m a
32econst = const . return
33
34-- $usage
35-- 1. In your @~\/.xmonad\/xmonad.hs@:
36--
37-- > import XMonad.Prompt
38-- > import XMonad.Prompt.Ssh
39--
40-- 2. In your keybindings add something like:
41--
42-- > , ((modm .|. controlMask, xK_s), sshPrompt defaultXPConfig)
43--
44-- Keep in mind, that if you want to use the completion you have to
45-- disable the "HashKnownHosts" option in your ssh_config
46--
47-- For detailed instruction on editing the key binding see
48-- "XMonad.Doc.Extending#Editing_key_bindings".
49
50data Override = Override
51 { oUser :: Maybe String
52 , oHost :: String
53 , oPort :: Maybe Int
54 , oCommand :: Conn -> String
55 }
56
57mkOverride = Override { oUser = Nothing, oHost = "", oPort = Nothing, oCommand = sshCmd }
58sshCmd c = concat
59 [ "ssh -t "
60 , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else ""
61 , cHost c
62 , if isJust $ cPort c then " -p " ++ (show $ fromJust $ cPort c) else ""
63 , " -- "
64 , cCommand c
65 ]
66moshCmd c = concat
67 [ "mosh "
68 , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else ""
69 , cHost c
70 , if isJust $ cPort c then " --ssh=\"ssh -p " ++ (show $ fromJust $ cPort c) ++ "\"" else ""
71 , " -- "
72 , cCommand c
73 ]
74moshCmd' p c = concat
75 [ "mosh "
76 , "--server=" ++ p ++ " "
77 , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else ""
78 , cHost c
79 , if isJust $ cPort c then " --ssh=\"ssh -p " ++ (show $ fromJust $ cPort c) ++ "\"" else ""
80 , " -- "
81 , cCommand c
82 ]
83inTmux Nothing c
84 | null $ cCommand c = c { cCommand = "tmux new-session" }
85 | otherwise = c { cCommand = "tmux new-session \"" ++ (cCommand c) ++ "\"" }
86inTmux (Just h) c
87 | null $ cCommand c = c { cCommand = "tmux new-session -As " <> h }
88 | otherwise = c { cCommand = "tmux new-session \"" ++ (cCommand c) ++ "\"" }
89withEnv :: [(String, String)] -> Conn -> Conn
90withEnv envs c = c { cCommand = "env" ++ (concat $ map (\(n, v) -> ' ' : (n ++ "=" ++ v)) envs) ++ " " ++ (cCommand c) }
91
92data Conn = Conn
93 { cUser :: Maybe String
94 , cHost :: String
95 , cPort :: Maybe Int
96 , cCommand :: String
97 } deriving (Eq, Show, Read)
98
99data Ssh = Ssh
100
101instance XPrompt Ssh where
102 showXPrompt Ssh = "SSH to: "
103 commandToComplete _ c = c
104 nextCompletion _ = getNextCompletion
105
106toConn :: String -> Maybe Conn
107toConn = toConn' . parse connParser "(unknown)"
108toConn' :: Either ParseError Conn -> Maybe Conn
109toConn' (Left _) = Nothing
110toConn' (Right a) = Just a
111
112connParser :: Parser Conn
113connParser = do
114 spaces
115 user' <- optionMaybe $ try $ do
116 str <- many1 $ satisfy (\c -> (not $ isSpace c) && (c /= '@'))
117 char '@'
118 return str
119 host' <- many1 $ satisfy (not . isSpace)
120 port' <- optionMaybe $ try $ do
121 space
122 string "-p"
123 spaces
124 int <- many1 digit
125 (space >> return ()) <|> eof
126 return $ (read int :: Int)
127 spaces
128 command' <- many anyChar
129 eof
130 return $ Conn
131 { cHost = host'
132 , cUser = user'
133 , cPort = port'
134 , cCommand = command'
135 }
136
137sshPrompt :: [Override] -> XPConfig -> X ()
138sshPrompt o c = do
139 sc <- io sshComplList
140 mkXPrompt Ssh c (mkComplFunFromList c sc) $ ssh o
141
142ssh :: [Override] -> String -> X ()
143ssh overrides str = do
144 let cmd = applyOverrides overrides str
145 liftIO $ putStr "SSH Command: "
146 liftIO $ putStrLn cmd
147 runInTerm "" cmd
148
149applyOverrides :: [Override] -> String -> String
150applyOverrides [] str = "ssh " ++ str
151applyOverrides (o:os) str = case (applyOverride o str) of
152 Just str -> str
153 Nothing -> applyOverrides os str
154
155applyOverride :: Override -> String -> Maybe String
156applyOverride o str = let
157 conn = toConn str
158 in
159 if isNothing conn then Nothing else
160 case (fromJust conn) `matches` o of
161 True -> Just $ (oCommand o) (fromJust conn)
162 False -> Nothing
163
164matches :: Conn -> Override -> Bool
165a `matches` b = and
166 [ justBool (cUser a) (oUser b) (==)
167 , (cHost a) == (oHost b)
168 , justBool (cPort a) (oPort b) (==)
169 ]
170
171justBool :: Eq a => Maybe a -> Maybe a -> (a -> a -> Bool) -> Bool
172justBool Nothing _ _ = True
173justBool _ Nothing _ = True
174justBool (Just a) (Just b) match = a `match` b
175
176sshComplList :: IO [String]
177sshComplList = uniqSort `fmap` liftM2 (++) sshComplListLocal sshComplListGlobal
178
179sshComplListLocal :: IO [String]
180sshComplListLocal = do
181 h <- getEnv "HOME"
182 s1 <- sshComplListFile $ h ++ "/.ssh/known_hosts"
183 s2 <- sshComplListConf $ h ++ "/.ssh/config"
184 return $ s1 ++ s2
185
186sshComplListGlobal :: IO [String]
187sshComplListGlobal = do
188 env <- getEnv "SSH_KNOWN_HOSTS" `E.catch` econst "/nonexistent"
189 fs <- mapM fileExists [ env
190 , "/usr/local/etc/ssh/ssh_known_hosts"
191 , "/usr/local/etc/ssh_known_hosts"
192 , "/etc/ssh/ssh_known_hosts"
193 , "/etc/ssh_known_hosts"
194 ]
195 case catMaybes fs of
196 [] -> return []
197 (f:_) -> sshComplListFile' f
198
199sshComplListFile :: String -> IO [String]
200sshComplListFile kh = do
201 f <- doesFileExist kh
202 if f then sshComplListFile' kh
203 else return []
204
205sshComplListFile' :: String -> IO [String]
206sshComplListFile' kh = do
207 l <- readFile kh
208 return $ map (getWithPort . takeWhile (/= ',') . concat . take 1 . words)
209 $ filter nonComment
210 $ lines l
211
212sshComplListConf :: String -> IO [String]
213sshComplListConf kh = do
214 f <- doesFileExist kh
215 if f then sshComplListConf' kh
216 else return []
217
218sshComplListConf' :: String -> IO [String]
219sshComplListConf' kh = do
220 l <- readFile kh
221 return $ map (!!1)
222 $ filter isHost
223 $ map words
224 $ lines l
225 where
226 isHost ws = take 1 ws == ["Host"] && length ws > 1
227
228fileExists :: String -> IO (Maybe String)
229fileExists kh = do
230 f <- doesFileExist kh
231 if f then return $ Just kh
232 else return Nothing
233
234nonComment :: String -> Bool
235nonComment [] = False
236nonComment ('#':_) = False
237nonComment ('|':_) = False -- hashed, undecodeable
238nonComment _ = True
239
240getWithPort :: String -> String
241getWithPort ('[':str) = host ++ " -p " ++ port
242 where (host,p) = break (==']') str
243 port = case p of
244 ']':':':x -> x
245 _ -> "22"
246getWithPort str = str
diff --git a/accounts/gkleen@sif/xmonad/package.yaml b/accounts/gkleen@sif/xmonad/package.yaml
deleted file mode 100644
index f65137af..00000000
--- a/accounts/gkleen@sif/xmonad/package.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
1name: xmonad-yggdrasil
2
3executables:
4 xmonad:
5 dependencies:
6 - base
7 - xmonad
8 - xmonad-contrib
9 - aeson
10 - bytestring
11 - text
12 - temporary
13 - filepath
14 - directory
15 - network
16 - unix
17 - utf8-string
18 - parsec
19 - process
20 - mtl
21 - X11
22 - transformers
23 - containers
24 - hostname
25 - libnotify
26 - taffybar
27
28 main: xmonad.hs
29 source-dirs:
30 - .
31 - lib
diff --git a/accounts/gkleen@sif/xmonad/stack.nix b/accounts/gkleen@sif/xmonad/stack.nix
deleted file mode 100644
index 17a49e04..00000000
--- a/accounts/gkleen@sif/xmonad/stack.nix
+++ /dev/null
@@ -1,17 +0,0 @@
1{ ghc, nixpkgs ? import ./nixpkgs.nix {} }:
2
3let
4 haskellPackages = import ./stackage.nix { inherit nixpkgs; };
5 inherit (nixpkgs {}) pkgs;
6in pkgs.haskell.lib.buildStackProject {
7 inherit ghc;
8 inherit (haskellPackages) stack;
9 name = "stackenv";
10 buildInputs = (with pkgs;
11 [ xorg.libX11 xorg.libXrandr xorg.libXinerama xorg.libXScrnSaver xorg.libXext xorg.libXft
12 cairo
13 glib
14 ]) ++ (with haskellPackages;
15 [
16 ]);
17}
diff --git a/accounts/gkleen@sif/xmonad/stack.yaml b/accounts/gkleen@sif/xmonad/stack.yaml
deleted file mode 100644
index b8ed1147..00000000
--- a/accounts/gkleen@sif/xmonad/stack.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
1nix:
2 enable: true
3 shell-file: stack.nix
4
5resolver: lts-13.21
6
7packages:
8 - .
9
10extra-deps: []
diff --git a/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix b/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix
deleted file mode 100644
index 7c853619..00000000
--- a/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix
+++ /dev/null
@@ -1,21 +0,0 @@
1{ mkDerivation, aeson, base, bytestring, containers, directory
2, filepath, hostname, hpack, mtl, network, parsec, process, lib
3, temporary, transformers, unix, utf8-string, X11, xmonad
4, xmonad-contrib, libnotify, taffybar
5}:
6mkDerivation {
7 pname = "xmonad-yggdrasil";
8 version = "0.0.0";
9 src = ./.;
10 isLibrary = false;
11 isExecutable = true;
12 libraryToolDepends = [ hpack ];
13 executableHaskellDepends = [
14 aeson base bytestring containers directory filepath hostname mtl
15 network parsec process temporary transformers unix utf8-string X11
16 xmonad xmonad-contrib libnotify taffybar
17 ];
18 preConfigure = "hpack";
19 license = "unknown";
20 hydraPlatforms = lib.platforms.none;
21}
diff --git a/accounts/gkleen@sif/xmonad/xmonad.hs b/accounts/gkleen@sif/xmonad/xmonad.hs
deleted file mode 100644
index a44d3bb7..00000000
--- a/accounts/gkleen@sif/xmonad/xmonad.hs
+++ /dev/null
@@ -1,939 +0,0 @@
1{-# LANGUAGE TupleSections, ViewPatterns, OverloadedStrings, FlexibleInstances, UndecidableInstances, MultiWayIf, NumDecimals #-}
2
3import XMonad
4import XMonad.Hooks.DynamicLog
5import XMonad.Hooks.ManageDocks
6import XMonad.Util.Run hiding (proc)
7import XMonad.Util.Loggers
8import XMonad.Util.EZConfig(additionalKeys)
9import System.IO
10import System.IO.Error
11import System.Environment
12import Data.Map (Map)
13import qualified Data.Map as Map
14import qualified XMonad.StackSet as W
15import System.Exit
16import Control.Monad.State (get)
17-- import XMonad.Layout.Spiral
18import Data.Ratio
19import Data.List
20import Data.Char
21import Data.Maybe (fromMaybe, listToMaybe, maybeToList, catMaybes, isJust)
22import XMonad.Layout.Tabbed
23import XMonad.Prompt
24import XMonad.Prompt.Input
25import XMonad.Util.Scratchpad
26import XMonad.Util.NamedScratchpad
27import XMonad.Util.Ungrab
28import Control.Monad (sequence, liftM, liftM2, join, void)
29import XMonad.Util.WorkspaceCompare
30import XMonad.Layout.NoBorders
31import XMonad.Layout.PerWorkspace
32import XMonad.Layout.SimplestFloat
33import XMonad.Layout.Renamed
34import XMonad.Layout.Reflect
35import XMonad.Layout.OnHost
36import XMonad.Layout.Combo
37import XMonad.Layout.ComboP
38import XMonad.Layout.Column
39import XMonad.Layout.TwoPane
40import XMonad.Layout.IfMax
41import XMonad.Layout.LayoutBuilder
42import XMonad.Layout.WindowNavigation
43import XMonad.Layout.Dwindle
44import XMonad.Layout.TrackFloating
45import System.Process
46import System.Directory (removeFile)
47import System.Posix.Files
48import System.FilePath ((</>))
49import Control.Concurrent
50import System.Posix.Process (getProcessID)
51import System.IO.Error
52import System.IO
53import XMonad.Hooks.ManageHelpers hiding (CW)
54import XMonad.Hooks.UrgencyHook as U
55import XMonad.Hooks.EwmhDesktops
56import XMonad.StackSet (RationalRect (..))
57import Control.Monad (when, filterM, (<=<))
58import Graphics.X11.ExtraTypes.XF86
59import XMonad.Util.Cursor
60import XMonad.Actions.Warp
61import XMonad.Actions.FloatKeys
62import XMonad.Util.SpawnOnce
63import System.Directory
64import System.FilePath
65import XMonad.Actions.CopyWindow
66import XMonad.Hooks.ServerMode
67import XMonad.Actions.Commands
68import XMonad.Actions.CycleWS
69import XMonad.Actions.RotSlaves
70import XMonad.Actions.UpdatePointer
71import XMonad.Prompt.Window
72import Data.IORef
73import Data.Monoid
74import Data.String
75import qualified XMonad.Actions.PhysicalScreens as P
76
77import XMonad.Layout.IM
78
79import System.Taffybar.Support.PagerHints (pagerHints)
80
81import XMonad.Prompt.MyShell
82import XMonad.Prompt.MyPass
83import XMonad.Prompt.MySsh
84
85import XMonad.Mpv
86
87import Network.HostName
88
89import Control.Applicative ((<$>))
90
91import Libnotify as Notify hiding (appName)
92import qualified Libnotify as Notify (appName)
93import Libnotify (Notification)
94-- import System.Information.Battery
95
96import Data.Int (Int32)
97
98import System.Posix.Process
99import System.Posix.Signals
100import System.Posix.IO as Posix
101import Control.Exception
102
103import System.IO.Unsafe
104
105import Control.Monad.Trans.Class
106import Control.Monad.Trans.Maybe
107
108import Data.Fixed (Micro)
109
110import qualified Data.Text as Text
111import Data.Ord (comparing)
112import Debug.Trace
113
114instance MonadIO m => IsString (m ()) where
115 fromString = spawn
116
117type KeyMap = Map (ButtonMask, KeySym) (X ())
118
119data Host = Host
120 { hName :: HostName
121 , hManageHook :: ManageHook
122 , hWsp :: Integer -> WorkspaceId
123 , hCoWsp :: String -> Maybe WorkspaceId
124 , hKeysMod :: XConfig Layout -> (KeyMap -> KeyMap)
125 , hScreens :: [P.PhysicalScreen]
126 , hKbLayouts :: [(String, Maybe String)]
127 , hCmds :: X [(String, X ())]
128 , hKeyUpKeys :: XConfig Layout -> KeyMap
129 }
130
131defaultHost = Host { hName = "unkown"
132 , hManageHook = composeOne [manageScratchTerm]
133 , hWsp = show
134 , hCoWsp = const Nothing
135 , hKeysMod = const id
136 , hScreens = [0,1..]
137 , hKbLayouts = [ ("us", Just "dvp")
138 , ("us", Nothing)
139 , ("de", Nothing)
140 ]
141 , hCmds = return []
142 , hKeyUpKeys = const Map.empty
143 }
144
145browser :: String
146browser = "env MOZ_USE_XINPUT2=1 firefox"
147
148gray, darkGray, red, green :: String
149gray = "#808080"
150darkGray = "#202020"
151red = "#800000"
152green = "#008000"
153
154hostFromName :: HostName -> Host
155hostFromName h@("vali") = defaultHost { hName = h
156 , hManageHook = composeOne $ catMaybes [ Just manageScratchTerm
157 , assign "web" $ className =? ".dwb-wrapped"
158 , assign "web" $ className =? "Chromium"
159 , assign "work" $ className =? "Emacs"
160 , assign "media" $ className =? "mpv"
161 ]
162 , hWsp = hWsp
163 , hCoWsp = hCoWsp
164 , hKeysMod = \conf -> Map.union $ (Map.fromList $ join $ map (spawnBindings conf) [ (xK_d, ["chromium", "chromium $(xclip -o)"])
165 , (xK_e, ["emacsclient -c"])
166 ])
167 `Map.union`
168 ( Map.fromList [ ((XMonad.modMask conf .|. controlMask, xK_Return), scratchpadSpawnActionCustom $ (XMonad.terminal conf) ++ " -name scratchpad -title scratchpad -e tmux new-session -D -s scratch")
169 ] )
170 , hScreens = hScreens defaultHost
171 }
172 where
173 workspaceNames = Map.fromList [ (2, "web")
174 , (3, "work")
175 , (10, "media")
176 ]
177 hWsp = wspFromMap workspaceNames
178 hCoWsp = coWspFromMap workspaceNames
179 assign wsp test = (\wsp -> test -?> doShift wsp) <$> hCoWsp wsp
180hostFromName h
181 | h `elem` ["hel", "sif"] = defaultHost { hName = h
182 , hManageHook = namedScratchpadManageHook scratchpads <+> composeOne (catMaybes
183 [ assign "mpv" $ className =? "mpv"
184 , assign "mpv" $ stringProperty "WM_WINDOW_ROLE" =? "presentation"
185 , assign "read" $ stringProperty "WM_WINDOW_ROLE" =? "presenter"
186 , assign "mpv" $ className =? "factorio"
187 , assign "mpv" $ resource =? "twitch"
188 , assign "web" $ className =? "chromium-browser"
189 , assign "web" $ className =? "Google-chrome"
190 , assign "work" $ (appName =? "Devtools" <&&> className =? "firefox")
191 , assign "work" $ className =? "Postman"
192 , assign "web" $ (appName =? "Navigator" <&&> className =? "firefox")
193 , assign "comm" $ (className =? "Emacs" <&&> title =? "Mail")
194 , assign "comm" $ className =? "Zulip"
195 , assign "comm" $ className =? "Element"
196 , assign "comm" $ className =? "Rocket.Chat"
197 , assign "comm" $ className =? "Discord"
198 , assign "comm" $ className =? "Rainbow"
199 , assign "media" $ resource =? "media"
200 , assign "monitor" $ className =? "Grafana"
201 , assign "monitor" $ className =? "Virt-viewer"
202 , assign "monitor" $ resource =? "htop"
203 , assign "monitor" $ resource =? "monitor"
204 , assign "monitor" $ className =? "xfreerdp"
205 , assign "monitor" $ className =? "org.remmina.Remmina"
206 , Just $ resource =? "htop" -?> centerFloat
207 , Just $ (className =? "Scp-dbus-service.py") -?> centerFloat
208 , Just $ resource =? "log" -?> centerFloat
209 , assign "work" $ className =? "Alacritty"
210 , Just $ (appName =? "Edit with Emacs FRAME") -?> centerFloat
211 , assign' ["work", "uni"] $ (className =? "Emacs" <&&> appName /=? "Edit with Emacs FRAME")
212 , assign' ["work", "uni"] $ className =? "jetbrains-idea-ce"
213 , assign "read" $ className =? "llpp"
214 , assign "read" $ className =? "Evince"
215 , assign "read" $ className =? "Zathura"
216 , assign "read" $ className =? "MuPDF"
217 , assign "read" $ className =? "Xournal"
218 , assign "read" $ appName =? "libreoffice"
219 , assign "read" $ appName =? "com-trollworks-gcs-app-GCS"
220 , assign "read" $ appName =? "Tux.py"
221 , assign "read" $ className =? "Gnucash"
222 , assign "comm" $ className =? "Skype"
223 , assign "comm" $ className =? "Daily"
224 , assign "comm" $ className =? "Pidgin"
225 , assign "comm" $ className =? "Thunderbird"
226 , assign "comm" $ className =? "Slack"
227 , Just $ (resource =? "xvkbd") -?> doRectFloat $ RationalRect (1 % 8) (3 % 8) (6 % 8) (4 % 8)
228 , Just $ (stringProperty "_NET_WM_WINDOW_TYPE" =? "_NET_WM_WINDOW_TYPE_DIALOG") -?> doFloat
229 , Just $ (className =? "Dunst") -?> doFloat
230 , Just $ (className =? "Xmessage") -?> doCenterFloat
231 , Just $ (className =? "Nm-openconnect-auth-dialog") -?> centerFloat
232 , Just $ (className =? "Pinentry") -?> doCenterFloat
233 , Just $ (className =? "pinentry") -?> doCenterFloat
234 , Just $ (stringProperty "WM_WINDOW_ROLE" =? "GtkFileChooseDialog") -?> centerFloatSmall
235 , Just $ (className =? "Nvidia-settings") -?> doCenterFloat
236 , Just $ fmap ("Minetest" `isInfixOf`) title -?> doIgnore
237 , Just $ fmap ("Automachef" `isInfixOf`) title -?> doIgnore
238 , assign "call" $ className =? "zoom"
239 ])
240 , hWsp = hWsp
241 , hCoWsp = hCoWsp
242 , hKeysMod = \conf -> Map.union $ (Map.fromList $ join $ map (spawnBindings conf) [ (xK_e, ["emacsclient -c"])
243 , (xK_d, [fromString browser, "google-chrome" {- , "notmuch-links" -}])
244 , (xK_c, [ inputPrompt xPConfigMonospace "dc" ?+ dc ])
245 , (xK_g, ["pidgin"])
246 , (xK_s, ["skype"])
247 -- , (xK_p, [mkPassPrompt "Type password" pwType xPConfig, mkPassPrompt "Show password" pwShow xPConfig, mkPassPrompt "Copy password" pwClip xPConfig])
248 , (xK_w, ["sudo rewacom"])
249 , (xK_y, [ "tmux new-window -dt media /var/media/link.hs $(xclip -o)"
250 , "tmux new-window -dt media /var/media/download.hs $(xclip -o)"
251 , "tmux new-window -dt media /var/media/download.hs $(xclip -o -selection clipboard)"
252 ])
253 , (xK_l, [ "tmux new-window -dt media mpv $(xclip -o)"
254 , "tmux new-window -dt media mpv $(xclip -o -selection clipboard)"
255 , "alacritty --class media -e tmuxp load /var/media"
256 ])
257 {- , (xK_m, [ "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e '(notmuch)'"
258 , "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e '(notmuch-mua-new-mail)'"
259 , "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e \"(browse-url-mail \"$(xclip -o)\")\""
260 ]) -}
261 , (xK_Return, ["keynav start,windowzoom", "keynav start"])
262 , (xK_t, [inputPrompt xPConfigMonospace "fuzzytime timer" ?+ fuzzytime, fuzzytime "unset", work_fuzzytime])
263 , (xK_a, [inputPrompt xPConfigMonospace "adjmix" ?+ adjmix])
264 , (xK_s, [ inputPromptWithCompl xPConfigMonospace "start synergy" synergyCompl ?+ synergyStart
265 , inputPromptWithCompl xPConfigMonospace "stop synergy" synergyCompl ?+ synergyStop
266 ])
267 , (xK_h, [ "alacritty --class htop -e htop"
268 , "alacritty --class log -e journalctl -xef"
269 ])
270 , (xK_x, [ "autorandr -c"
271 , "autorandr -fl def"
272 ])
273 , (xK_z, [ "zulip -- --force-device-scale-factor=2"
274 ])
275 ])
276 `Map.union`
277 ( Map.fromList [ ((XMonad.modMask conf .|. controlMask, xK_Return), namedScratchpadAction scratchpads "term")
278 , ((XMonad.modMask conf .|. controlMask, xK_a), namedScratchpadAction scratchpads "pavucontrol")
279 , ((XMonad.modMask conf .|. controlMask, xK_o), namedScratchpadAction scratchpads "easyeffects")
280 , ((XMonad.modMask conf .|. controlMask .|. shiftMask, xK_o), namedScratchpadAction scratchpads "helvum")
281 , ((XMonad.modMask conf .|. controlMask, xK_w), namedScratchpadAction scratchpads "alarms")
282 , ((XMonad.modMask conf .|. controlMask, xK_b), namedScratchpadAction scratchpads "blueman")
283 , ((XMonad.modMask conf .|. controlMask, xK_p), namedScratchpadAction scratchpads "keepassxc")
284 , ((XMonad.modMask conf .|. controlMask, xK_t), namedScratchpadAction scratchpads "toggl")
285 , ((XMonad.modMask conf .|. controlMask, xK_e), namedScratchpadAction scratchpads "emacs")
286 , ((XMonad.modMask conf .|. controlMask, xK_m), namedScratchpadAction scratchpads "calendar")
287 , ((XMonad.modMask conf .|. controlMask, xK_f), namedScratchpadAction scratchpads "music")
288 , ((XMonad.modMask conf .|. mod1Mask, xK_Up), rotate U)
289 , ((XMonad.modMask conf .|. mod1Mask, xK_Down), rotate D)
290 , ((XMonad.modMask conf .|. mod1Mask, xK_Left), rotate L)
291 , ((XMonad.modMask conf .|. mod1Mask, xK_Right), rotate R)
292 , ((controlMask, xK_space ), "dunstctl close" )
293 , ((controlMask .|. shiftMask, xK_space ), "dunstctl close-all" )
294 , ((controlMask, xK_period), "dunstctl context" )
295 , ((controlMask, xK_comma ), "dunstctl history-pop")
296 -- , ((XMonad.modMask conf .|. shiftMask, xK_a), startMute "hel")
297 ] )
298 , hKeyUpKeys = \conf -> Map.fromList [ -- ((XMonad.modMask conf .|. shiftMask, xK_a), stopMute "hel")
299 ]
300 , hScreens = hScreens defaultHost
301 , hCmds = return [ ("prev-workspace", prevWS)
302 , ("next-workspace", nextWS)
303 , ("prev-window", rotAllDown)
304 , ("next-window", rotAllUp)
305 , ("banish", banishScreen LowerRight)
306 , ("update-gpg-tty", safeSpawn "gpg-connect-agent" ["UPDATESTARTUPTTY", "/bye"])
307 , ("rescreen", rescreen)
308 , ("repanel", do
309 spawn "nm-applet"
310 spawn "blueman-applet"
311 spawn "pasystray"
312 spawn "kdeconnect-indicator"
313 spawn "dunst -print"
314 spawn "udiskie"
315 spawn "autocutsel -s PRIMARY"
316 spawn "autocutsel -s CLIPBOARD"
317 )
318 , ("pause", mediaMpv $ MpvSetProperty "pause" True)
319 , ("unpause", mediaMpv $ MpvSetProperty "pause" False)
320 , ("exit", io $ exitWith ExitSuccess)
321 ]
322 }
323 where
324 withGdkScale act = void . xfork $ setEnv "GDK_SCALE" "2" >> act
325 workspaceNames = Map.fromList [ (1, "comm")
326 , (2, "web")
327 , (3, "work")
328 , (4, "read")
329 , (5, "monitor")
330 , (6, "uni")
331 , (8, "call")
332 , (9, "media")
333 , (10, "mpv")
334 ]
335 scratchpads = [ NS "term" "alacritty --class scratchpad --title scratchpad -e tmux new-session -AD -s scratch" (resource =? "scratchpad") centerFloat
336 , NS "pavucontrol" "pavucontrol" (resource =? "pavucontrol") centerFloat
337 , NS "helvum" "helvum" (resource =? "helvum") centerFloat
338 , NS "easyeffects" "easyeffects" (resource =? "easyeffects") centerFloat
339 , NS "alarms" "alarm-clock-applet" (className =? "Alarm-clock-applet" <&&> title =? "Alarms") centerFloat
340 , NS "blueman" "blueman-manager" (className =? ".blueman-manager-wrapped") centerFloat
341 , NS "keepassxc" "keepassxc" (className =? "KeePassXC") centerFloat
342 , NS "toggl" "toggldesktop" (className =? "Toggl Desktop") centerFloat
343 , NS "calendar" "minetime -- --force-device-scale-factor=1.6" (className =? "MineTime") centerFloat
344 , NS "emacs" "emacsclient -c -F \"'(title . \\\"Scratchpad\\\")\"" (className =? "Emacs" <&&> title =? "Scratchpad") centerFloat
345 , NS "music" "ytmdesktop" (className =? "youtube-music-desktop-app") centerFloat
346 ]
347 centerFloat = customFloating $ RationalRect (1 % 16) (1 % 16) (7 % 8) (7 % 8)
348 centerFloatSmall = customFloating $ RationalRect (1 % 4) (1 % 4) (1 % 2) (1 % 2)
349 hWsp = wspFromMap workspaceNames
350 hCoWsp = coWspFromMap workspaceNames
351 assign wsp test = (\wsp -> test -?> doShift wsp) <$> hCoWsp wsp
352 assign' :: [String] -> Query Bool -> Maybe MaybeManageHook
353 assign' wsps test = do
354 wsIds <- mapM hCoWsp wsps
355 return $ test -?> go wsIds
356 where
357 go :: [WorkspaceId] -> ManageHook
358 go wsps = do
359 visWsps <- liftX $ (\wset -> W.tag . W.workspace <$> W.current wset : W.visible wset) <$> gets windowset
360 case (filter (`elem` visWsps) wsps, wsps) of
361 (wsp : _, _) -> doShift wsp
362 (_, wsp : _) -> doShift wsp
363 ([], []) -> return mempty
364 rotate rot = do
365 safeSpawn "xrandr" ["--output", "eDP-1", "--rotate", xrandrDir]
366 mapM_ rotTouch touchscreens
367 where
368 xrandrDir = case rot of
369 U -> "normal"
370 L -> "left"
371 R -> "right"
372 D -> "inverted"
373 matrix = case rot of
374 U -> [ [ 1, 0, 0]
375 , [ 0, 1, 0]
376 , [ 0, 0, 1]
377 ]
378 L -> [ [ 0, -1, 1]
379 , [ 1, 0, 0]
380 , [ 0, 0, 1]
381 ]
382 R -> [ [ 0, 1, 0]
383 , [-1, 0, 1]
384 , [ 0, 0, 1]
385 ]
386 D -> [ [-1, 0, 1]
387 , [ 0, -1, 1]
388 , [ 0, 0, 1]
389 ]
390 touchscreens = [ "Wacom Co.,Ltd. Pen and multitouch sensor Finger touch"
391 , "Wacom Co.,Ltd. Pen and multitouch sensor Pen stylus"
392 , "Wacom Co.,Ltd. Pen and multitouch sensor Pen eraser"
393 ]
394 rotTouch screen = do
395 safeSpawn "xinput" $ ["set-prop", screen, "Coordinate Transformation Matrix"] ++ map (\n -> show n ++ ",") (concat matrix)
396 safeSpawn "xinput" ["map-to-output", screen, "eDP-1"]
397 withPw f label = io . void . forkProcess $ do
398 uninstallSignalHandlers
399 void $ createSession
400 (dropWhileEnd isSpace -> pw) <- readCreateProcess (proc "pass" ["show", label]) ""
401 void $ f pw
402 pwType :: String -> X ()
403 pwType = withPw $ readCreateProcess (proc "xdotool" ["type", "--clearmodifiers", "--file", "-"])
404 pwClip label = safeSpawn "pass" ["show", "--clip", label]
405 pwShow :: String -> X ()
406 pwShow = withPw $ \pw -> do
407 xmessage <- fromMaybe "xmessage" <$> liftIO (lookupEnv "XMONAD_XMESSAGE")
408 readCreateProcess (proc xmessage ["-file", "-"]) pw
409 fuzzytime str = safeSpawn "fuzzytime" $ "timer" : words str
410 work_fuzzytime = io . void . forkProcess $ do
411 readCreateProcess (proc "worktime" []) "" >>= safeSpawn "fuzzytime" . ("timer" : ) . pure
412 adjmix str = safeSpawn "adjmix" $ words str
413 dc expr = void . xfork $ do
414 result <- readProcess "dc" [] $ expr ++ "f"
415 let
416 (first : rest) = filter (not . null) $ lines result
417 notification = Notify.summary first <> Notify.body (unlines rest) <> Notify.timeout Infinite <> Notify.urgency Normal <> Notify.appName "dc"
418 void $ Notify.display notification
419 synergyCompl = mkComplFunFromList' xPConfigMonospace ["mathw86"]
420 synergyStart host = safeSpawn "systemctl" ["--user", "start", "synergy-rtunnel@" ++ host ++ ".service"]
421 synergyStop host = safeSpawn "systemctl" ["--user", "stop", "synergy-rtunnel@" ++ host ++ ".service"]
422
423hostFromName _ = defaultHost
424
425-- muteRef :: IORef (Maybe (String, Notification))
426-- {-# NOINLINE muteRef #-}
427-- muteRef = unsafePerformIO $ newIORef Nothing
428
429-- startMute, stopMute :: String -> X ()
430-- startMute sink = liftIO $ do
431-- muted <- isJust <$> readIORef muteRef
432-- when (not muted) $ do
433-- let
434-- notification = Notify.summary "Muted" <> Notify.timeout Infinite <> Notify.urgency Normal
435-- level = "0.0dB"
436-- -- level <- runProcessWithInput "ssh" ["bragi", "cat", "/dev/shm/mix/" ++ sink ++ "/level"] ""
437-- -- callProcess "ssh" ["bragi", "adjmix", "-t", sink, "-o", "0"]
438-- hPutStrLn stderr "Mute"
439-- writeIORef muteRef . Just . (level, ) =<< Notify.display notification
440-- stopMute sink = liftIO $ do
441-- let
442-- unmute (Just (level, notification)) = do
443-- hPutStrLn stderr "Unmute"
444-- -- callProcess "ssh" ["bragi", "adjmix", "-t", sink, "-o", level]
445-- Notify.close notification
446-- unmute Nothing = return ()
447-- muted <- isJust <$> readIORef muteRef
448-- when muted . join . atomicModifyIORef muteRef $ (Nothing, ) . unmute
449
450wspFromMap workspaceNames = \i -> case Map.lookup i workspaceNames of
451 Just str -> show i ++ " " ++ str
452 Nothing -> show i
453
454coWspFromMap workspaceNames = \str -> case filter ((== str) . snd) $ Map.toList workspaceNames of
455 [] -> Nothing
456 [(i, _)] -> Just $ wspFromMap workspaceNames i
457 _ -> Nothing
458
459spawnModifiers = [0, controlMask, shiftMask .|. controlMask]
460spawnBindings :: XConfig layout -> (KeySym, [X ()]) -> [((KeyMask, KeySym), X ())]
461spawnBindings conf (k, cmds) = zipWith (\m cmd -> ((modm .|. mod1Mask .|. m, k), cmd)) spawnModifiers cmds
462 where
463 modm = XMonad.modMask conf
464
465manageScratchTerm = (resource =? "scratchpad" <||> resource =? "keysetup") -?> doRectFloat $ RationalRect (1 % 16) (1 % 16) (7 % 8) (7 % 8)
466
467tabbedLayout t = renamed [Replace "Tabbed"] $ reflectHoriz $ t CustomShrink $ tabbedTheme
468tabbedLayoutHoriz t = renamed [Replace "Tabbed Horiz"] $ reflectVert $ t CustomShrink $ tabbedTheme
469tabbedTheme = def
470 { activeColor = "black"
471 , inactiveColor = "black"
472 , urgentColor = "black"
473 , activeBorderColor = gray
474 , inactiveBorderColor = darkGray
475 , urgentBorderColor = red
476 , activeTextColor = gray
477 , inactiveTextColor = gray
478 , urgentTextColor = gray
479 , decoHeight = 32
480 , fontName = "xft:Fira Sans:pixelsize=21"
481 }
482
483main :: IO ()
484main = do
485 arguments <- either (const []) id <$> tryIOError getArgs
486 case arguments of
487 ["--command", s] -> do
488 d <- openDisplay ""
489 rw <- rootWindow d $ defaultScreen d
490 a <- internAtom d "XMONAD_COMMAND" False
491 m <- internAtom d s False
492 allocaXEvent $ \e -> do
493 setEventType e clientMessage
494 setClientMessageEvent e rw a 32 m currentTime
495 sendEvent d rw False structureNotifyMask e
496 sync d False
497 _ -> do
498 -- batteryMon <- xfork $ monitorBattery Nothing Nothing
499 hostname <- getHostName
500 let
501 host = hostFromName hostname
502 setEnv "HOST" hostname
503 let myConfig = withHostUrgency . ewmhFullscreen . ewmh . pagerHints $ docks def
504 { manageHook = hManageHook host
505 , terminal = "alacritty"
506 , layoutHook = smartBorders . avoidStruts $ windowNavigation layout'
507 , logHook = do
508 dynamicLogString xmobarPP' >>= writeProps
509 updatePointer (99 % 100, 98 % 100) (0, 0)
510 , modMask = mod4Mask
511 , keys = \conf -> hKeysMod host conf $ myKeys' conf host
512 , workspaces = take (length numKeys) $ map wsp [1..]
513 , startupHook = setDefaultCursor xC_left_ptr
514 , normalBorderColor = darkGray
515 , focusedBorderColor = gray
516 , handleEventHook = serverModeEventHookCmd' (hCmds host) <+> keyUpEventHook
517 }
518 writeProps str = do
519 let encodeCChar = map $ fromIntegral . fromEnum
520 atoms = [ "_XMONAD_WORKSPACES"
521 , "_XMONAD_LAYOUT"
522 , "_XMONAD_TITLE"
523 ]
524 (flip mapM_) (zip atoms (lines str)) $ \(atom', content) -> do
525 ustring <- getAtom "UTF8_STRING"
526 atom <- getAtom atom'
527 withDisplay $ \dpy -> io $ do
528 root <- rootWindow dpy $ defaultScreen dpy
529 changeProperty8 dpy root atom ustring propModeReplace $ encodeCChar content
530 sync dpy True
531 wsp = hWsp host
532 -- We can´t define per-host layout modifiers because we lack dependent types
533 layout' = onHost "skadhi" ( onWorkspace (wsp 1) (Full ||| withIM (1%5) (Title "Buddy List") tabbedLayout') $
534 onWorkspace (wsp 10) Full $
535 onWorkspace (wsp 2) (Full ||| tabbedLayout') $
536 onWorkspace (wsp 5) tabbedLayout' $
537 onWorkspace (wsp 8) (withIM (1%5) (Title "Friends") tabbedLayout') $
538 defaultLayouts
539 ) $
540 onHost "vali" ( onWorkspace (wsp 2) (Full ||| tabbedLayout' ||| combineTwo (TwoPane 0.01 0.57) Full tabbedLayout') $
541 onWorkspace (wsp 3) workLayouts $
542 defaultLayouts
543 ) $
544 onHost "hel" ( onWorkspace (wsp 1) (withIM (1 % 8) (Title "Buddy List") $ trackFloating tabbedLayout') $
545 onWorkspace (wsp 2) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
546 onWorkspace (wsp 3) workLayouts $
547 onWorkspace (wsp 6) workLayouts $
548 onWorkspace (wsp 4) (tabbedLayout' ||| tabbedLayoutHoriz' ||| Dwindle R CW 1 (5 % 100)) $
549 onWorkspace (wsp 5) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
550 onWorkspace (wsp 10) (tabbedLayout''' ||| combineTwoP (TwoPane (1 % 100) (3 % 4)) tabbedLayout''' tabbedLayout''' (ClassName "mpv") ||| Dwindle R CW 1 (5 % 100)) $
551 defaultLayouts
552 ) $
553 onHost "sif" ( onWorkspace (wsp 1) (withIM (1 % 8) (Title "Buddy List") $ trackFloating tabbedLayout') $
554 onWorkspace (wsp 2) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
555 onWorkspace (wsp 3) workLayouts $
556 onWorkspace (wsp 6) workLayouts $
557 onWorkspace (wsp 4) (tabbedLayout' ||| tabbedLayoutHoriz' ||| Dwindle R CW 1 (5 % 100)) $
558 onWorkspace (wsp 5) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
559 onWorkspace (wsp 8) tabbedLayout''' $
560 onWorkspace (wsp 10) (tabbedLayout''' ||| combineTwoP (TwoPane (1 % 100) (3 % 4)) tabbedLayout''' tabbedLayout''' (ClassName "mpv") ||| Dwindle R CW 1 (5 % 100)) $
561 defaultLayouts
562 ) $
563 defaultLayouts
564 -- tabbedLayout''' = renamed [Replace "Tabbed'"] $ IfMax 1 (noBorders Full) (tabbedLayout tabbedBottomAlways)
565 tabbedLayout''' = tabbedLayout tabbedBottom
566 tabbedLayout' = tabbedLayout tabbedBottomAlways
567 tabbedLayoutHoriz' = tabbedLayoutHoriz tabbedLeftAlways
568 defaultLayouts = {- spiralWithDir East CW (1 % 2) -} Dwindle R CW 1 (5 % 100) ||| tabbedLayout' ||| Full
569 -- workLayouts = {- spiralWithDir East CW (1 % 2) -} Dwindle R CW (2 % 1) (5 % 100) ||| tabbedLayout' ||| Full
570 workLayouts = tabbedLayout' ||| (renamed [Replace "Combined"] $ combineTwoP (TwoPane (1 % 100) (1891 % 2560)) tabbedLayout''' (Column 1.6) (ClassName "Postman" `Or` ClassName "Emacs" `Or` ClassName "jetbrains-idea-ce" `Or` (Resource "Devtools" `And` ClassName "Firefox"))) ||| Full ||| Dwindle R CW 1 (5 % 100)
571 sqrtTwo = approxRational (sqrt 2) (1 / 2560)
572 xmobarPP' = xmobarPP { ppTitle = shorten 80
573 , ppSort = (liftM2 (.)) getSortByIndex $ return scratchpadFilterOutWorkspace
574 , ppUrgent = wrap "(" ")" . xmobarColor "#800000" ""
575 , ppHiddenNoWindows = xmobarColor "#202020" "" . wrap "(" ")"
576 , ppVisible = wrap "(" ")" . xmobarColor "#808000" ""
577 , ppCurrent = wrap "(" ")" . xmobarColor "#008000" ""
578 , ppHidden = wrap "(" ")"
579 , ppWsSep = " "
580 , ppSep = "\n"
581 }
582 withHostUrgency = case hostname of
583 "sif" -> withUrgencyHookC urgencyHook' $ def { suppressWhen = U.Never, remindWhen = Every 2 }
584 _ -> id
585 urgencyHook' window = do
586 let blinkLight = (lightHigh >> threadDelay 0.5e6) `finally` lightLow
587 where
588 lightHigh =
589 writeFile "/sys/class/leds/input0::capslock/brightness" =<< readFile "/sys/class/leds/input0::capslock/max_brightness"
590 lightLow = writeFile "/sys/class/leds/input0::capslock/brightness" "0"
591 runQuery ((resource =? "comm" <||> resource =? "Pidgin" <||> className =? "Gajim" <||> className =? "Skype" <||> className =? "Thunderbird") --> void (xfork blinkLight)) window
592 urgencyHook (BorderUrgencyHook { urgencyBorderColor = red }) window
593 shutdown :: SomeException -> IO a
594 shutdown e = do
595 let pids = [ -- batteryMon
596 ]
597 mapM_ (signalProcess sigTERM) pids
598 mapM_ (getProcessStatus False False) pids
599 throw e
600 keyUpEventHook :: Event -> X All
601 keyUpEventHook event = handle event >> return (All True)
602 where
603 handle (KeyEvent { ev_event_type = t, ev_state = m, ev_keycode = code })
604 | t == keyRelease = withDisplay $ \dpy -> do
605 s <- io $ keycodeToKeysym dpy code 0
606 mClean <- cleanMask m
607 ks <- asks $ hKeyUpKeys host . config
608 userCodeDef () $ whenJust (Map.lookup (mClean, s) ks) id
609 | otherwise = return ()
610 handle _ = return ()
611 handle shutdown $ launch myConfig =<< getDirectories
612
613secs :: Int -> Int
614secs = (* 1000000)
615
616-- monitorBattery :: Maybe BatteryContext -> Maybe Notification -> IO ()
617-- monitorBattery Nothing n = do
618-- ctx <- batteryContextNew
619-- case ctx of
620-- Nothing -> threadDelay (secs 10) >> monitorBattery Nothing n
621-- Just _ -> monitorBattery ctx n
622-- monitorBattery ctx@(Just ctx') n = do
623-- batInfo <- getBatteryInfo ctx'
624-- case batInfo of
625-- Nothing -> threadDelay (secs 1) >> monitorBattery ctx n
626-- Just batInfo -> do
627-- let n'
628-- | batteryState batInfo == BatteryStateDischarging
629-- , timeLeft <= 1200
630-- , timeLeft > 0 = Just $ summary "Discharging" <> hint "value" percentage <> urgency u <> body (duz timeLeft ++ "left")
631-- | otherwise = Nothing
632-- u
633-- | timeLeft <= 600 = Critical
634-- | timeLeft <= 1800 = Normal
635-- | otherwise = Low
636-- timeLeft = batteryTimeToEmpty batInfo
637-- percentage :: Int32
638-- percentage = round $ batteryPercentage batInfo
639-- ts = [("s", 60), ("m", 60), ("h", 24), ("d", 365), ("y", 1)]
640-- duz ms = ss
641-- where (ss, _) = foldl (\(ss, x) (s, y) -> ((if rem x y > 0 then show (rem x y) ++ s ++ " " else "") ++ ss , quot x y)) ("", ms) ts
642-- case n' of
643-- Just n' -> Notify.display (maybe mempty reuse n <> Notify.appName "monitorBattery" <> n') >>= (\n -> threadDelay (secs 2) >> monitorBattery ctx (Just n))
644-- Nothing -> threadDelay (secs 30) >> monitorBattery ctx n
645
646disableTouchpad, disableTrackpoint, enableTrackpoint, enableTouchpad :: X ()
647enableTouchpad = safeSpawn "xinput" ["enable", "SynPS/2 Synaptics TouchPad"]
648disableTouchpad = safeSpawn "xinput" ["disable", "SynPS/2 Synaptics TouchPad"]
649enableTrackpoint = safeSpawn "xinput" ["enable", "TPPS/2 IBM TrackPoint"]
650disableTrackpoint = safeSpawn "xinput" ["disable", "TPPS/2 IBM TrackPoint"]
651
652isDisabled :: String -> X Bool
653isDisabled str = do
654 out <- runProcessWithInput "xinput" ["list", str] ""
655 return $ "disabled" `isInfixOf` out
656
657
658spawnKeychain :: X ()
659spawnKeychain = do
660 home <- liftIO getHomeDirectory
661 let keys = (map ((home </>) . (".ssh/" ++)) ["id", "id-rsa"]) ++ ["6B13AA67"]
662 liftIO (maybe (return ()) (setEnv "SSH_ASKPASS") =<< findAskpass)
663 safeSpawn "keychain" . (["--agents", "gpg,ssh"] ++)=<< liftIO (filterM doesFileExist keys)
664 where
665 findAskpass = filter `liftM` readFile "/etc/zshrc"
666 filter = listToMaybe . catMaybes . map (stripPrefix "export SSH_ASKPASS=") . lines
667
668assimilateKeychain :: X ()
669assimilateKeychain = liftIO $ assimilateKeychain' >> return ()
670assimilateKeychain' = tryIOError $ do
671 -- pid <- getProcessID
672 -- tmpDir <- lookupEnv "TMPDIR"
673 -- let tmpDir' = fromMaybe "/tmp" tmpDir
674 -- tmpFile = tmpDir' </> "xmonad-keychain" ++ (show pid) ++ ".env"
675 env <- runProcessWithInput "sh" ["-c", "eval $(keychain --eval --noask --agents gpg,ssh); env"] "" -- > " ++ tmpFile] ""
676 -- env <- readFile tmpFile
677 let envVars = Map.fromList $ map (\(k, v) -> (k, tail' v)) $ map (span (/= '=')) $ envLines
678 envVars' = Map.filterWithKey (\k _ -> k `elem` transfer) envVars
679 transfer = ["SSH_AUTH_SOCK", "SSH_AGENT_PID", "GPG_AGENT_INFO"]
680 envLines = filter (elem '=') $ lines env :: [String]
681 sequence $ map (\(k, c) -> setEnv k c) $ Map.toList envVars'
682 -- removeFile tmpFile
683 where
684 tail' [] = []
685 tail' (x:xs) = xs
686
687
688numKeys = [xK_parenleft, xK_parenright, xK_braceright, xK_plus, xK_braceleft, xK_bracketright, xK_bracketleft, xK_exclam, xK_equal, xK_asterisk]
689
690instance Shrinker CustomShrink where
691 shrinkIt _ "" = [""]
692 shrinkIt s cs
693 | length cs >= 4 = cs : shrinkIt s ((reverse . drop 4 . reverse $ cs) ++ "...")
694 | otherwise = cs : shrinkIt s (init cs)
695
696xPConfig, xPConfigMonospace :: XPConfig
697xPConfig = def
698 { font = "xft:Fira Sans:pixelsize=21"
699 , height = 32
700 , bgColor = "black"
701 , fgColor = gray
702 , fgHLight = green
703 , bgHLight = "black"
704 , borderColor = gray
705 , searchPredicate = (\needle haystack -> all (`isInfixOf` map toLower haystack) . map (map toLower) $ words needle)
706 , position = Top
707 }
708xPConfigMonospace = xPConfig { font = "xft:Fira Code:pixelsize=21" }
709
710sshOverrides host = map (\h -> mkOverride { oHost = h, oCommand = moshCmd . inTmux host} )
711 [ "odin"
712 , "ymir"
713 , "surtr"
714 , "vidhar"
715 , "srv02.uniworx.de"
716 ]
717 ++
718 map (\h -> mkOverride { oHost = h, oCommand = moshCmd' "/run/current-system/sw/bin/mosh-server" . withEnv [("TERM", "xterm")] . inTmux host} )
719 [ "bragi", "bragi.asgard.yggdrasil"
720 ]
721 ++
722 map (\h -> mkOverride { oHost = h, oCommand = sshCmd . inTmux host } )
723 [ "uni2work-dev1", "srv01.uniworx.de"
724 ]
725 ++
726 map (\h -> mkOverride { oHost = h, oCommand = sshCmd . withEnv [("TERM", "xterm")] . inTmux host } )
727 [ "remote.cip.ifi.lmu.de"
728 , "uniworx3", "uniworx4", "uniworx5", "uniworxdb2"
729 , "testworx"
730 ]
731
732backlight :: (Rational -> Rational) -> X ()
733backlight f = void . xfork . liftIO $ do
734 [ _device
735 , _class
736 , read . Text.unpack -> currentBright
737 , _currentPercentage
738 , read . Text.unpack -> maximumBright
739 ] <- Text.splitOn "," . Text.pack <$> readProcess "brightnessctl" ["-m"] ""
740 let current = currentBright % maximumBright
741 new' = f current * fromIntegral maximumBright
742 new :: Integer
743 new | floor new' < 0 = 0
744 | ceiling new' > maximumBright = maximumBright
745 | new' >= maximumBright % 2 = ceiling new'
746 | otherwise = floor new'
747 callProcess "brightnessctl" ["-m", "s", show new]
748
749cycleThrough :: [Rational] -> (Rational -> Rational)
750cycleThrough opts current = fromMaybe currentOpt $ listToMaybe next'
751 where currentOpt = minimumBy (comparing $ abs . subtract current) opts
752 (_, _ : next') = break (== currentOpt) opts
753
754cycleKbLayout :: [(String, Maybe String)] -> X ()
755cycleKbLayout [] = return ()
756cycleKbLayout layouts = liftIO $ do
757 next <- (getNext . extract) `liftM` runProcessWithInput "setxkbmap" ["-query"] ""
758 let
759 args = case next of
760 (l, Just v) -> [l, v]
761 (l, Nothing) -> [l]
762 safeSpawn "setxkbmap" args
763 where
764 extract :: String -> Maybe (String, Maybe String)
765 extract str = listToMaybe $ do
766 ["layout:", l] <- str'
767 [(l, Just v) | ["variant:", v] <- str'] ++ pure (l, Nothing)
768 where
769 str' = map words $ lines str
770 getNext :: Maybe (String, Maybe String) -> (String, Maybe String)
771 getNext = maybe (head layouts) getNext'
772 getNext' x = case elemIndex x layouts of
773 Nothing -> getNext Nothing
774 Just i -> layouts !! ((i + 1) `mod` length layouts)
775
776mpvAll' :: MpvCommand -> IO [MpvResponse]
777mpvAll' = mpvAll "/var/media/.mpv-ipc"
778
779mpvOne' :: MpvCommand -> IO (Maybe MpvResponse)
780mpvOne' = mpvOne "/var/media/.mpv-ipc"
781
782mediaMpv :: MpvCommand -> X ()
783mediaMpv cmd = void . xfork $ print =<< mpvAll' cmd
784
785mediaMpvTogglePause :: X ()
786mediaMpvTogglePause = void . xfork $ do
787 paused <- mapM mpvResponse <=< mpvAll' $ MpvGetProperty "pause"
788 if
789 | and paused -> print <=< mpvAll' $ MpvSetProperty "pause" False
790 | otherwise -> print <=< mpvOne' $ MpvSetProperty "pause" True
791
792myKeys' conf host = Map.fromList $
793 -- launch a terminal
794 [ ((modm, xK_Return), spawn $ (XMonad.terminal conf) ++ " -e tmux")
795 , ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
796
797 -- launch dmenu
798 --, ((modm, xK_d ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"")
799 , ((modm, xK_d ), shellPrompt "Run: " xPConfigMonospace)
800 , ((modm .|. shiftMask, xK_d ), prompt "Run in Terminal: " ("alacritty" ++ " -e") xPConfigMonospace)
801 , ((modm, xK_at ), sshPrompt (sshOverrides . Just $ hName host) xPConfigMonospace)
802
803 -- close focused window
804 , ((modm .|. shiftMask, xK_q ), kill)
805 , ((modm .|. controlMask .|. shiftMask, xK_q ), spawn "xkill")
806
807 -- Rotate through the available layout algorithms
808 , ((modm, xK_space ), sendMessage NextLayout)
809
810 -- Reset the layouts on the current workspace to default
811 , ((modm .|. controlMask, xK_r ), (setLayout $ XMonad.layoutHook conf) >> refresh)
812
813 -- Resize viewed windows to the correct size
814 , ((modm, xK_r ), refresh)
815
816 -- Move focus to the next window
817 , ((modm, xK_t ), windows W.focusDown)
818
819 -- Move focus to the previous window
820 , ((modm, xK_n ), windows W.focusUp )
821
822 -- Move focus to the master window
823 , ((modm, xK_m ), windows W.focusMaster )
824
825 -- Swap the focused window and the master window
826 , ((modm .|. shiftMask, xK_m ), windows W.swapMaster)
827
828 -- Swap the focused window with the next window
829 , ((modm .|. shiftMask, xK_t ), windows W.swapDown )
830
831 -- Swap the focused window with the previous window
832 , ((modm .|. shiftMask, xK_n ), windows W.swapUp )
833
834 -- Swap the focused window with the previous window
835 , ((modm .|. shiftMask .|. controlMask, xK_m), sendMessage SwapWindow)
836
837 , ((modm, xK_Right), sendMessage $ Go R)
838 , ((modm, xK_Left ), sendMessage $ Go L)
839 , ((modm, xK_Up ), sendMessage $ Go U)
840 , ((modm, xK_Down ), sendMessage $ Go D)
841 , ((modm .|. shiftMask , xK_Right), sendMessage $ Move R)
842 , ((modm .|. shiftMask , xK_Left ), sendMessage $ Move L)
843 , ((modm .|. shiftMask , xK_Up ), sendMessage $ Move U)
844 , ((modm .|. shiftMask , xK_Down ), sendMessage $ Move D)
845 -- , ((modm .|. controlMask, xK_Right), withFocused $ keysMoveWindow (10, 0))
846 -- , ((modm .|. controlMask, xK_Left ), withFocused $ keysMoveWindow (-10, 0))
847 -- , ((modm .|. controlMask, xK_Up ), withFocused $ keysMoveWindow (0, -10))
848 -- , ((modm .|. controlMask, xK_Down ), withFocused $ keysMoveWindow (0, 10))
849 -- Shrink the master area
850 , ((modm, xK_h ), sendMessage Shrink)
851
852 -- Expand the master area
853 , ((modm, xK_s ), sendMessage Expand)
854
855 -- Push window back into tiling
856 , ((modm .|. shiftMask, xK_space ), withFocused $ windows . W.sink)
857 , ((modm, xK_BackSpace), focusUrgent)
858 , ((modm .|. shiftMask, xK_BackSpace), clearUrgents)
859
860 -- Increment the number of windows in the master area
861 , ((modm , xK_comma ), sendMessage (IncMasterN 1))
862
863 -- Deincrement the number of windows in the master area
864 , ((modm , xK_period), sendMessage (IncMasterN (-1)))
865
866 , ((0, xF86XK_AudioRaiseVolume), safeSpawn "pamixer" ["-i", "2"])
867 , ((0, xF86XK_AudioLowerVolume), safeSpawn "pamixer" ["-d", "2"])
868 , ((0, xF86XK_AudioMute), safeSpawn "pamixer" ["-t"])
869 , ((0, xF86XK_AudioPause), mediaMpv $ MpvSetProperty "pause" False)
870 , ((0, {-xF86XK_AudioMicMute-} 269025202), safeSpawn "pulseaudio-ctl" ["mute-input"])
871 , ((0, xF86XK_AudioPlay), mediaMpvTogglePause)
872 , ((0, xK_Print), do
873 home <- liftIO getHomeDirectory
874 unGrab
875 safeSpawn "scrot" ["-s", "-F", home </> "screenshots" </> "%Y-%m-%dT%H:%M:%S.png", "-e", "xclip -selection clipboard -t image/png -i $f"]
876 )
877 , ((modm .|. mod1Mask, xK_space), mediaMpvTogglePause)
878
879 -- , ((0, xF86XK_MonBrightnessDown), backlight . cycleThrough $ reverse brCycle)
880 -- , ((0, xF86XK_MonBrightnessUp ), backlight $ cycleThrough brCycle)
881 , ((modm .|. shiftMask , xK_b), backlight . cycleThrough $ reverse brCycle)
882 , ((modm .|. shiftMask .|. controlMask, xK_b), backlight $ cycleThrough brCycle)
883
884 , ((modm , xK_Escape), cycleKbLayout (hKbLayouts host))
885 , ((modm .|. controlMask, xK_Escape), safeSpawn "setxkbmap" $ fst (head $ hKbLayouts host) : maybeToList (snd . head $ hKbLayouts host))
886
887 -- Toggle the status bar gap
888 -- Use this binding with avoidStruts from Hooks.ManageDocks.
889 -- See also the statusBar function from Hooks.DynamicLog.
890 --
891 , ((modm , xK_b ), sendMessage ToggleStruts)
892
893 , ((modm .|. shiftMask, xK_p ), safeSpawn "playerctl" ["-a", "pause"])
894
895 -- Quit xmonad
896 , ((modm .|. shiftMask, xK_e ), io (exitWith ExitSuccess))
897
898 -- Restart xmonad
899 -- , ((modm .|. shiftMask .|. controlMask, xK_r ), void . xfork $ recompile False >>= flip when (safeSpawn "xmonad" ["--restart"]))
900 , ((modm .|. shiftMask, xK_r ), void . liftIO $ executeFile "xmonad" True [] Nothing)
901 , ((modm .|. shiftMask, xK_l ), void . xfork $ do
902 sessId <- getEnv "XDG_SESSION_ID"
903 safeSpawn "loginctl" ["lock-session", sessId]
904 )
905 , ((modm .|. shiftMask, xK_s ), safeSpawn "systemctl" ["suspend"])
906 , ((modm .|. shiftMask, xK_h ), inputPromptWithCompl xPConfigMonospace "systemctl" powerActCompl ?+ powerAct)
907 , ((modm, xK_v ), windows copyToAll) -- @@ Make focused window always visible
908 , ((modm .|. shiftMask, xK_v ), killAllOtherCopies) -- @@ Toggle window state back
909 , ((modm .|. shiftMask, xK_g ), windowPrompt xPConfig Goto wsWindows)
910 , ((modm , xK_g ), windowPrompt xPConfig Bring allWindows)
911 ]
912 ++
913
914 --
915 -- mod-[1..9], Switch to workspace N
916 --
917 -- mod-[1..9], Switch to workspace N
918 -- mod-shift-[1..9], Move client to workspace N
919 --
920 [((m .|. modm, k), windows $ f i)
921 | (i, k) <- zip (XMonad.workspaces conf) $ numKeys
922 , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]
923 ]
924 ++
925 [((m .|. modm .|. controlMask, k), void . runMaybeT $
926 MaybeT (P.getScreen def i) >>= MaybeT . screenWorkspace >>= lift . windows . f
927 )
928 | (i, k) <- zip (hScreens host) [xK_g, xK_c, xK_r, xK_l]
929 , (f, m) <- [(W.view, 0), (W.shift, shiftMask)]
930 ]
931 where
932 modm = XMonad.modMask conf
933
934 brCycle = [0, 1 % 500, 1 % 250, 1 % 100, 1 % 10, 1 % 4, 1 % 2, 3 % 4, 1]
935
936 powerActWords = ["poweroff", "reboot", "hibernate", "suspend"]
937 powerActCompl = mkComplFunFromList' xPConfigMonospace powerActWords
938 powerAct act | act `elem` powerActWords = safeSpawn "systemctl" $ pure act
939 | otherwise = return ()
diff --git a/flake.lock b/flake.lock
index 56b571ca..ce7e59e4 100644
--- a/flake.lock
+++ b/flake.lock
@@ -168,11 +168,11 @@
168 "nixpkgs-lib": "nixpkgs-lib_2" 168 "nixpkgs-lib": "nixpkgs-lib_2"
169 }, 169 },
170 "locked": { 170 "locked": {
171 "lastModified": 1726153070, 171 "lastModified": 1733312601,
172 "narHash": "sha256-HO4zgY0ekfwO5bX0QH/3kJ/h4KvUDFZg8YpkNwIbg1U=", 172 "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
173 "owner": "hercules-ci", 173 "owner": "hercules-ci",
174 "repo": "flake-parts", 174 "repo": "flake-parts",
175 "rev": "bcef6817a8b2aa20a5a6dbb19b43e63c5bf8619a", 175 "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
176 "type": "github" 176 "type": "github"
177 }, 177 },
178 "original": { 178 "original": {
@@ -202,11 +202,11 @@
202 "flake-registry": { 202 "flake-registry": {
203 "flake": false, 203 "flake": false,
204 "locked": { 204 "locked": {
205 "lastModified": 1717415742, 205 "lastModified": 1734450202,
206 "narHash": "sha256-HKvoLGZUsBpjkxWkdtctGYj6RH0bl6vcw0OjTOqyzJk=", 206 "narHash": "sha256-/3gigrEBFORQs6a8LL5twoHs7biu08y/8Xc5aQmk3b0=",
207 "owner": "NixOS", 207 "owner": "NixOS",
208 "repo": "flake-registry", 208 "repo": "flake-registry",
209 "rev": "895a65f8d5acf848136ee8fe8e8f736f0d27df96", 209 "rev": "02fe640c9e117dd9d6a34efc7bcb8bd09c08111d",
210 "type": "github" 210 "type": "github"
211 }, 211 },
212 "original": { 212 "original": {
@@ -322,11 +322,11 @@
322 ] 322 ]
323 }, 323 },
324 "locked": { 324 "locked": {
325 "lastModified": 1722322032, 325 "lastModified": 1736014120,
326 "narHash": "sha256-pnO44gA8GcJj3oCVeGmypSGLr10+usMbJXochJWdugw=", 326 "narHash": "sha256-ZrI+mcuQfal5IfT4HsxVEiiFNAgV4qYh+B4/NyXxpAs=",
327 "owner": "gkleen", 327 "owner": "gkleen",
328 "repo": "home-manager", 328 "repo": "home-manager",
329 "rev": "55c1d61f06fd331f874178a6028f22be22ee7878", 329 "rev": "99e8412a18eb7e0731aa2b77abeed00d6d1863ad",
330 "type": "github" 330 "type": "github"
331 }, 331 },
332 "original": { 332 "original": {
@@ -359,11 +359,11 @@
359 }, 359 },
360 "impermanence": { 360 "impermanence": {
361 "locked": { 361 "locked": {
362 "lastModified": 1731242966, 362 "lastModified": 1736688610,
363 "narHash": "sha256-B3C3JLbGw0FtLSWCjBxU961gLNv+BOOBC6WvstKLYMw=", 363 "narHash": "sha256-1Zl9xahw399UiZSJ9Vxs1W4WRFjO1SsNdVZQD4nghz0=",
364 "owner": "nix-community", 364 "owner": "nix-community",
365 "repo": "impermanence", 365 "repo": "impermanence",
366 "rev": "3ed3f0eaae9fcc0a8331e77e9319c8a4abd8a71a", 366 "rev": "c64bed13b562fc3bb454b48773d4155023ac31b7",
367 "type": "github" 367 "type": "github"
368 }, 368 },
369 "original": { 369 "original": {
@@ -385,6 +385,65 @@
385 "url": "https://data.iana.org/time-zones/tzdb/leap-seconds.list" 385 "url": "https://data.iana.org/time-zones/tzdb/leap-seconds.list"
386 } 386 }
387 }, 387 },
388 "niri-flake": {
389 "inputs": {
390 "niri-stable": "niri-stable",
391 "niri-unstable": "niri-unstable",
392 "nixpkgs": [
393 "nixpkgs"
394 ],
395 "nixpkgs-stable": "nixpkgs-stable_2",
396 "xwayland-satellite-stable": "xwayland-satellite-stable",
397 "xwayland-satellite-unstable": "xwayland-satellite-unstable"
398 },
399 "locked": {
400 "lastModified": 1736855225,
401 "narHash": "sha256-2+ayH/0B37BLPJy4thO1titHIrVCoDdCtdnl0CyV8kc=",
402 "owner": "sodiboo",
403 "repo": "niri-flake",
404 "rev": "b013bedcff63b5cdbb9cd9841ac339361fc5cfcc",
405 "type": "github"
406 },
407 "original": {
408 "owner": "sodiboo",
409 "ref": "main",
410 "repo": "niri-flake",
411 "type": "github"
412 }
413 },
414 "niri-stable": {
415 "flake": false,
416 "locked": {
417 "lastModified": 1736614405,
418 "narHash": "sha256-AJ1rlgNOPb3/+DbS5hkhm21t6Oz8IgqLllwmZt0lyzk=",
419 "owner": "YaLTeR",
420 "repo": "niri",
421 "rev": "e05bc269e678ecf828b96ae79c991c13b00b38a5",
422 "type": "github"
423 },
424 "original": {
425 "owner": "YaLTeR",
426 "ref": "v25.01",
427 "repo": "niri",
428 "type": "github"
429 }
430 },
431 "niri-unstable": {
432 "flake": false,
433 "locked": {
434 "lastModified": 1736861309,
435 "narHash": "sha256-RSCoXyngYF+7apD5pRq6lZfRbl8vHIUVI57bbihA5Ew=",
436 "owner": "gkleen",
437 "repo": "niri",
438 "rev": "80a7ee2971b2d43622f68dcdc3233ae8365338f6",
439 "type": "github"
440 },
441 "original": {
442 "owner": "gkleen",
443 "repo": "niri",
444 "type": "github"
445 }
446 },
388 "nix-github-actions": { 447 "nix-github-actions": {
389 "inputs": { 448 "inputs": {
390 "nixpkgs": [ 449 "nixpkgs": [
@@ -413,11 +472,11 @@
413 ] 472 ]
414 }, 473 },
415 "locked": { 474 "locked": {
416 "lastModified": 1733629314, 475 "lastModified": 1736652904,
417 "narHash": "sha256-U0vivjQFAwjNDYt49Krevs1murX9hKBFe2Ye0cHpgbU=", 476 "narHash": "sha256-8uolHABgroXqzs03QdulHp8H9e5kWQZnnhcda1MKbBM=",
418 "owner": "Mic92", 477 "owner": "Mic92",
419 "repo": "nix-index-database", 478 "repo": "nix-index-database",
420 "rev": "f1e477a7dd11e27e7f98b646349cd66bbabf2fb8", 479 "rev": "271e5bd7c57e1f001693799518b10a02d1123b12",
421 "type": "github" 480 "type": "github"
422 }, 481 },
423 "original": { 482 "original": {
@@ -434,11 +493,11 @@
434 ] 493 ]
435 }, 494 },
436 "locked": { 495 "locked": {
437 "lastModified": 1732406038, 496 "lastModified": 1736736253,
438 "narHash": "sha256-BYNBN+Rtc/SX6qI7m3nmryufRPn0ZYd40yHDo9VQaNE=", 497 "narHash": "sha256-GrktftEfXmmdKOU0yz3QXckDz1ncZ+f4KLU8XnYKYuA=",
439 "owner": "AshleyYakeley", 498 "owner": "AshleyYakeley",
440 "repo": "NixVirt", 499 "repo": "NixVirt",
441 "rev": "fe3aaa86d4458e4f84348941297f7ba82e2a9f67", 500 "rev": "9063243af5e6674359a0ff7cec57f02eeacf0cea",
442 "type": "github" 501 "type": "github"
443 }, 502 },
444 "original": { 503 "original": {
@@ -449,11 +508,11 @@
449 }, 508 },
450 "nixos-hardware": { 509 "nixos-hardware": {
451 "locked": { 510 "locked": {
452 "lastModified": 1733861262, 511 "lastModified": 1736441705,
453 "narHash": "sha256-+jjPup/ByS0LEVIrBbt7FnGugJgLeG9oc+ivFASYn2U=", 512 "narHash": "sha256-OL7leZ6KBhcDF3nEKe4aZVfIm6xQpb1Kb+mxySIP93o=",
454 "owner": "NixOS", 513 "owner": "NixOS",
455 "repo": "nixos-hardware", 514 "repo": "nixos-hardware",
456 "rev": "cf737e2eba82b603f54f71b10cb8fd09d22ce3f5", 515 "rev": "8870dcaff63dfc6647fb10648b827e9d40b0a337",
457 "type": "github" 516 "type": "github"
458 }, 517 },
459 "original": { 518 "original": {
@@ -509,14 +568,14 @@
509 }, 568 },
510 "nixpkgs-lib_2": { 569 "nixpkgs-lib_2": {
511 "locked": { 570 "locked": {
512 "lastModified": 1725233747, 571 "lastModified": 1733096140,
513 "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=", 572 "narHash": "sha256-1qRH7uAUsyQI7R1Uwl4T+XvdNv778H0Nb5njNrqvylY=",
514 "type": "tarball", 573 "type": "tarball",
515 "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" 574 "url": "https://github.com/NixOS/nixpkgs/archive/5487e69da40cbd611ab2cadee0b4637225f7cfae.tar.gz"
516 }, 575 },
517 "original": { 576 "original": {
518 "type": "tarball", 577 "type": "tarball",
519 "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" 578 "url": "https://github.com/NixOS/nixpkgs/archive/5487e69da40cbd611ab2cadee0b4637225f7cfae.tar.gz"
520 } 579 }
521 }, 580 },
522 "nixpkgs-lib_3": { 581 "nixpkgs-lib_3": {
@@ -571,6 +630,22 @@
571 }, 630 },
572 "nixpkgs-stable_2": { 631 "nixpkgs-stable_2": {
573 "locked": { 632 "locked": {
633 "lastModified": 1736684107,
634 "narHash": "sha256-vH5mXxEvZeoGNkqKoCluhTGfoeXCZ1seYhC2pbMN0sg=",
635 "owner": "NixOS",
636 "repo": "nixpkgs",
637 "rev": "635e887b48521e912a516625eee7df6cf0eba9c1",
638 "type": "github"
639 },
640 "original": {
641 "owner": "NixOS",
642 "ref": "nixos-24.11",
643 "repo": "nixpkgs",
644 "type": "github"
645 }
646 },
647 "nixpkgs-stable_3": {
648 "locked": {
574 "lastModified": 1717179513, 649 "lastModified": 1717179513,
575 "narHash": "sha256-vboIEwIQojofItm2xGCdZCzW96U85l9nDW3ifMuAIdM=", 650 "narHash": "sha256-vboIEwIQojofItm2xGCdZCzW96U85l9nDW3ifMuAIdM=",
576 "owner": "NixOS", 651 "owner": "NixOS",
@@ -585,7 +660,7 @@
585 "type": "github" 660 "type": "github"
586 } 661 }
587 }, 662 },
588 "nixpkgs-stable_3": { 663 "nixpkgs-stable_4": {
589 "locked": { 664 "locked": {
590 "lastModified": 1678872516, 665 "lastModified": 1678872516,
591 "narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=", 666 "narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=",
@@ -603,11 +678,11 @@
603 }, 678 },
604 "nixpkgs_2": { 679 "nixpkgs_2": {
605 "locked": { 680 "locked": {
606 "lastModified": 1733759999, 681 "lastModified": 1736798957,
607 "narHash": "sha256-463SNPWmz46iLzJKRzO3Q2b0Aurff3U1n0nYItxq7jU=", 682 "narHash": "sha256-qwpCtZhSsSNQtK4xYGzMiyEDhkNzOCz/Vfu4oL2ETsQ=",
608 "owner": "NixOS", 683 "owner": "NixOS",
609 "repo": "nixpkgs", 684 "repo": "nixpkgs",
610 "rev": "a73246e2eef4c6ed172979932bc80e1404ba2d56", 685 "rev": "9abb87b552b7f55ac8916b6fc9e5cb486656a2f3",
611 "type": "github" 686 "type": "github"
612 }, 687 },
613 "original": { 688 "original": {
@@ -673,11 +748,11 @@
673 "treefmt-nix": "treefmt-nix" 748 "treefmt-nix": "treefmt-nix"
674 }, 749 },
675 "locked": { 750 "locked": {
676 "lastModified": 1731205797, 751 "lastModified": 1736774291,
677 "narHash": "sha256-F7N1mxH1VrkVNHR3JGNMRvp9+98KYO4b832KS8Gl2xI=", 752 "narHash": "sha256-1rEUm7R93L8rltgyBzon2/lzIN2udC/Kd8nyvuDN6ps=",
678 "owner": "nix-community", 753 "owner": "nix-community",
679 "repo": "poetry2nix", 754 "repo": "poetry2nix",
680 "rev": "f554d27c1544d9c56e5f1f8e2b8aff399803674e", 755 "rev": "499221030113adc5dea05886a1d7aa1cc3a315d1",
681 "type": "github" 756 "type": "github"
682 }, 757 },
683 "original": { 758 "original": {
@@ -722,11 +797,11 @@
722 ] 797 ]
723 }, 798 },
724 "locked": { 799 "locked": {
725 "lastModified": 1726745158, 800 "lastModified": 1734261738,
726 "narHash": "sha256-D5AegvGoEjt4rkKedmxlSEmC+nNLMBPWFxvmYnVLhjk=", 801 "narHash": "sha256-3Lzk+7QyX8v60+km26D3dln7NMSA13vW+KYTkMkds6Q=",
727 "owner": "cachix", 802 "owner": "cachix",
728 "repo": "pre-commit-hooks.nix", 803 "repo": "pre-commit-hooks.nix",
729 "rev": "4e743a6920eab45e8ba0fbe49dc459f1423a4b74", 804 "rev": "4c8e75efbbdcc6f9203f64b1f21f8a55d2285264",
730 "type": "github" 805 "type": "github"
731 }, 806 },
732 "original": { 807 "original": {
@@ -741,7 +816,7 @@
741 "flake-utils": "flake-utils_2", 816 "flake-utils": "flake-utils_2",
742 "gitignore": "gitignore_3", 817 "gitignore": "gitignore_3",
743 "nixpkgs": "nixpkgs_3", 818 "nixpkgs": "nixpkgs_3",
744 "nixpkgs-stable": "nixpkgs-stable_3" 819 "nixpkgs-stable": "nixpkgs-stable_4"
745 }, 820 },
746 "locked": { 821 "locked": {
747 "lastModified": 1685361114, 822 "lastModified": 1685361114,
@@ -794,13 +869,14 @@
794 "home-manager": "home-manager", 869 "home-manager": "home-manager",
795 "home-manager-eostre": "home-manager-eostre", 870 "home-manager-eostre": "home-manager-eostre",
796 "impermanence": "impermanence", 871 "impermanence": "impermanence",
872 "niri-flake": "niri-flake",
797 "nix-index-database": "nix-index-database", 873 "nix-index-database": "nix-index-database",
798 "nixVirt": "nixVirt", 874 "nixVirt": "nixVirt",
799 "nixos-hardware": "nixos-hardware", 875 "nixos-hardware": "nixos-hardware",
800 "nixpkgs": "nixpkgs_2", 876 "nixpkgs": "nixpkgs_2",
801 "nixpkgs-eostre": "nixpkgs-eostre", 877 "nixpkgs-eostre": "nixpkgs-eostre",
802 "nixpkgs-pgbackrest": "nixpkgs-pgbackrest", 878 "nixpkgs-pgbackrest": "nixpkgs-pgbackrest",
803 "nixpkgs-stable": "nixpkgs-stable_2", 879 "nixpkgs-stable": "nixpkgs-stable_3",
804 "nvfetcher": "nvfetcher", 880 "nvfetcher": "nvfetcher",
805 "poetry2nix": "poetry2nix", 881 "poetry2nix": "poetry2nix",
806 "prometheus-borg-exporter": "prometheus-borg-exporter", 882 "prometheus-borg-exporter": "prometheus-borg-exporter",
@@ -815,11 +891,11 @@
815 ] 891 ]
816 }, 892 },
817 "locked": { 893 "locked": {
818 "lastModified": 1733965552, 894 "lastModified": 1736808430,
819 "narHash": "sha256-GZ4YtqkfyTjJFVCub5yAFWsHknG1nS/zfk7MuHht4Fs=", 895 "narHash": "sha256-wlgdf/n7bJMLBheqt1jmPoxJFrUP6FByKQFXuM9YvIk=",
820 "owner": "Mic92", 896 "owner": "Mic92",
821 "repo": "sops-nix", 897 "repo": "sops-nix",
822 "rev": "2d73fc6ac4eba4b9a83d3cb8275096fbb7ab4004", 898 "rev": "553c7cb22fed19fd60eb310423fdc93045c51ba8",
823 "type": "github" 899 "type": "github"
824 }, 900 },
825 "original": { 901 "original": {
@@ -854,8 +930,9 @@
854 "type": "github" 930 "type": "github"
855 }, 931 },
856 "original": { 932 "original": {
857 "id": "systems", 933 "owner": "nix-systems",
858 "type": "indirect" 934 "repo": "default",
935 "type": "github"
859 } 936 }
860 }, 937 },
861 "treefmt-nix": { 938 "treefmt-nix": {
@@ -889,19 +966,52 @@
889 ] 966 ]
890 }, 967 },
891 "locked": { 968 "locked": {
892 "lastModified": 1734278650, 969 "lastModified": 1737014022,
893 "narHash": "sha256-z9FiyHDbKC2nwfd/qsHCxLBEogzQj73zo85lW3zIlzY=", 970 "narHash": "sha256-5cG3lbjvrqvotI3oEPham3jGq8Fd96NfrqCGvC1e6Qw=",
894 "owner": "gkleen", 971 "owner": "gkleen",
895 "repo": "Waybar", 972 "repo": "Waybar",
896 "rev": "5432f9c1697a8d2d3e1264a5ce820d7eac26e2c6", 973 "rev": "83765e0f8e99a7d344eae511a4090a76a27e5791",
897 "type": "github" 974 "type": "github"
898 }, 975 },
899 "original": { 976 "original": {
900 "owner": "gkleen", 977 "owner": "gkleen",
901 "ref": "feat/privacy-ignore", 978 "ref": "feat/niri-workspaces-hide",
902 "repo": "Waybar", 979 "repo": "Waybar",
903 "type": "github" 980 "type": "github"
904 } 981 }
982 },
983 "xwayland-satellite-stable": {
984 "flake": false,
985 "locked": {
986 "lastModified": 1730166465,
987 "narHash": "sha256-nq7bouXQXaaPPo/E+Jbq+wNHnatD4dY8OxSrRqzvy6s=",
988 "owner": "Supreeeme",
989 "repo": "xwayland-satellite",
990 "rev": "a713cf46cb7db84a0d1b57c3a397c610cad3cf98",
991 "type": "github"
992 },
993 "original": {
994 "owner": "Supreeeme",
995 "ref": "v0.5",
996 "repo": "xwayland-satellite",
997 "type": "github"
998 }
999 },
1000 "xwayland-satellite-unstable": {
1001 "flake": false,
1002 "locked": {
1003 "lastModified": 1736487362,
1004 "narHash": "sha256-4kGoOA7FgK9N2mzS+TFEn41kUUNY6KwdiA/0rqlr868=",
1005 "owner": "Supreeeme",
1006 "repo": "xwayland-satellite",
1007 "rev": "8f55e27f63a749881c4bbfbb6b1da028342a91d1",
1008 "type": "github"
1009 },
1010 "original": {
1011 "owner": "Supreeeme",
1012 "repo": "xwayland-satellite",
1013 "type": "github"
1014 }
905 } 1015 }
906 }, 1016 },
907 "root": "root", 1017 "root": "root",
diff --git a/flake.nix b/flake.nix
index cd50543e..b5ced0a0 100644
--- a/flake.nix
+++ b/flake.nix
@@ -4,9 +4,11 @@
4 nixConfig = { 4 nixConfig = {
5 extra-substituters = [ 5 extra-substituters = [
6 "https://nix-community.cachix.org" 6 "https://nix-community.cachix.org"
7 "https://niri.cachix.org"
7 ]; 8 ];
8 extra-trusted-public-keys = [ 9 extra-trusted-public-keys = [
9 "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=" 10 "nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs="
11 "niri.cachix.org-1:Wv0OmO7PsuocRKzfDoJ3mulSl7Z6oezYhGhR+3W2964="
10 ]; 12 ];
11 }; 13 };
12 14
@@ -170,7 +172,7 @@
170 type = "github"; 172 type = "github";
171 owner = "gkleen"; 173 owner = "gkleen";
172 repo = "Waybar"; 174 repo = "Waybar";
173 ref = "feat/privacy-ignore"; 175 ref = "feat/niri-workspaces-hide";
174 inputs = { 176 inputs = {
175 nixpkgs.follows = "nixpkgs"; 177 nixpkgs.follows = "nixpkgs";
176 flake-compat.follows = "flake-compat"; 178 flake-compat.follows = "flake-compat";
@@ -182,9 +184,19 @@
182 repo = "NixVirt"; 184 repo = "NixVirt";
183 inputs.nixpkgs.follows = "nixpkgs"; 185 inputs.nixpkgs.follows = "nixpkgs";
184 }; 186 };
187 niri-flake = {
188 type = "github";
189 owner = "sodiboo";
190 repo = "niri-flake";
191 ref = "main";
192 inputs = {
193 nixpkgs.follows = "nixpkgs";
194 niri-unstable.url = "github:gkleen/niri";
195 };
196 };
185 }; 197 };
186 198
187 outputs = { self, nixpkgs, home-manager, sops-nix, deploy-rs, nvfetcher, ... }@inputs: 199 outputs = { self, nixpkgs, home-manager, sops-nix, deploy-rs, nvfetcher, niri-flake, ... }@inputs:
188 let 200 let
189 inherit (builtins) attrNames attrValues elemAt toJSON isNull pathExists; 201 inherit (builtins) attrNames attrValues elemAt toJSON isNull pathExists;
190 inherit (nixpkgs) lib; 202 inherit (nixpkgs) lib;
@@ -267,9 +279,10 @@
267 mkAccountModule = dir: path: accountName: 279 mkAccountModule = dir: path: accountName:
268 let 280 let
269 userName = accountUserName accountName; 281 userName = accountUserName accountName;
282 hostName = accountHostName accountName;
270 in overrideModule 283 in overrideModule
271 (import (dir + "/${path}")) 284 (import (dir + "/${path}"))
272 (inputs: inputs // { inherit userName; }) 285 (inputs: inputs // { inherit userName hostName; })
273 (outputs: { _file = dir + "/${path}"; } 286 (outputs: { _file = dir + "/${path}"; }
274 // outputs 287 // outputs
275 // { imports = [self.nixosModules.users.${userName} or ({...}: { imports = defaultUserProfiles userName; })] ++ (outputs.imports or []); }); 288 // { imports = [self.nixosModules.users.${userName} or ({...}: { imports = defaultUserProfiles userName; })] ++ (outputs.imports or []); });
@@ -322,7 +335,7 @@
322 nixosConfigurations = installerNixosConfigurations // nixImport rec { dir = ./hosts; _import = mkNixosConfiguration [] dir; }; 335 nixosConfigurations = installerNixosConfigurations // nixImport rec { dir = ./hosts; _import = mkNixosConfiguration [] dir; };
323 336
324 homeModules = nixImport rec { dir = ./home-modules; }; 337 homeModules = nixImport rec { dir = ./home-modules; };
325 homeConfigurations = listToAttrs (concatLists (mapAttrsToList (hostname: nixosConfig: mapAttrsToList (username: configuration: nameValuePair "${username}@${hostname}" { inherit (configuration.home) activationPackage; }) nixosConfig.config.home-manager.users) self.nixosConfigurations)); 338 homeConfigurations = listToAttrs (concatLists (mapAttrsToList (hostname: nixosConfig: mapAttrsToList (username: configuration: nameValuePair "${username}@${hostname}" { inherit (configuration.home) activationPackage; inherit (configuration) home-files; }) nixosConfig.config.home-manager.users) self.nixosConfigurations));
326 339
327 overlays = mapAttrs (_name: path: mkOverlay path) overlayPaths; 340 overlays = mapAttrs (_name: path: mkOverlay path) overlayPaths;
328 341
diff --git a/hosts/sif/default.nix b/hosts/sif/default.nix
index 7c8da63a..655b2e9c 100644
--- a/hosts/sif/default.nix
+++ b/hosts/sif/default.nix
@@ -13,8 +13,7 @@ in {
13 imports = with flake.nixosModules.systemProfiles; [ 13 imports = with flake.nixosModules.systemProfiles; [
14 ./hw.nix 14 ./hw.nix
15 ./mail ./libvirt 15 ./mail ./libvirt
16 tmpfs-root bcachefs initrd-all-crypto-modules default-locale openssh rebuild-machines 16 tmpfs-root bcachefs initrd-all-crypto-modules default-locale openssh rebuild-machines niri-unstable networkmanager
17 networkmanager
18 flakeInputs.nixos-hardware.nixosModules.lenovo-thinkpad-p1 17 flakeInputs.nixos-hardware.nixosModules.lenovo-thinkpad-p1
19 flakeInputs.impermanence.nixosModules.impermanence 18 flakeInputs.impermanence.nixosModules.impermanence
20 flakeInputs.nixVirt.nixosModules.default 19 flakeInputs.nixVirt.nixosModules.default
@@ -27,6 +26,9 @@ in {
27 allowUnfree = true; 26 allowUnfree = true;
28 pulseaudio = true; 27 pulseaudio = true;
29 }; 28 };
29 extraOverlays = [
30 flakeInputs.niri-flake.overlays.niri
31 ];
30 }; 32 };
31 33
32 time.timeZone = null; 34 time.timeZone = null;
@@ -34,7 +36,6 @@ in {
34 boot = { 36 boot = {
35 initrd = { 37 initrd = {
36 systemd = { 38 systemd = {
37 enable = false;
38 emergencyAccess = config.users.users.root.hashedPassword; 39 emergencyAccess = config.users.users.root.hashedPassword;
39 }; 40 };
40 luks.devices = { 41 luks.devices = {
@@ -62,15 +63,20 @@ in {
62 plymouth.enable = true; 63 plymouth.enable = true;
63 64
64 kernelPackages = pkgs.linuxPackages_latest; 65 kernelPackages = pkgs.linuxPackages_latest;
65 extraModulePackages = with config.boot.kernelPackages; [ v4l2loopback ];
66 kernelModules = ["v4l2loopback"];
67 kernelPatches = [ 66 kernelPatches = [
68 { name = "edac-config"; 67 { name = "edac-config";
69 patch = null; 68 patch = null;
70 extraConfig = '' 69 extraStructuredConfig = with lib.kernel; {
71 EDAC y 70 EDAC = yes;
72 EDAC_IE31200 y 71 EDAC_IE31200 = yes;
73 ''; 72 };
73 }
74 { name = "zswap-default";
75 patch = null;
76 extraStructuredConfig = with lib.kernel; {
77 ZSWAP_DEFAULT_ON = yes;
78 ZSWAP_SHRINKER_DEFAULT_ON = yes;
79 };
74 } 80 }
75 ]; 81 ];
76 82
@@ -439,7 +445,7 @@ in {
439 }; 445 };
440 446
441 xserver = { 447 xserver = {
442 enable = true; 448 enable = false;
443 449
444 xkb = { 450 xkb = {
445 layout = "us"; 451 layout = "us";
@@ -497,14 +503,14 @@ in {
497 GTK.application_prefer_dark_theme = true; 503 GTK.application_prefer_dark_theme = true;
498 }; 504 };
499 }; 505 };
500 programs.hyprland.enable = true; 506 programs.niri.enable = true;
501 507
502 systemd.tmpfiles.settings = { 508 systemd.tmpfiles.settings = {
503 "10-localtime"."/etc/localtime".L.argument = "/.bcachefs/etc/localtime"; 509 "10-localtime"."/etc/localtime".L.argument = "/.bcachefs/etc/localtime";
504 510
505 "10-regreet"."/var/cache/regreet/cache.toml".C.argument = toString ((pkgs.formats.toml {}).generate "cache.toml" { 511 "10-regreet"."/var/cache/regreet/cache.toml".C.argument = toString ((pkgs.formats.toml {}).generate "cache.toml" {
506 last_user = "gkleen"; 512 last_user = "gkleen";
507 user_to_last_sess.gkleen = "Hyprland"; 513 user_to_last_sess.gkleen = "Niri";
508 }); 514 });
509 }; 515 };
510 516
@@ -616,7 +622,7 @@ in {
616 nvidia = { 622 nvidia = {
617 open = true; 623 open = true;
618 modesetting.enable = true; 624 modesetting.enable = true;
619 powerManagement.enable = true; 625 powerManagement.enable = false;
620 prime = { 626 prime = {
621 nvidiaBusId = "PCI:1:0:0"; 627 nvidiaBusId = "PCI:1:0:0";
622 intelBusId = "PCI:0:2:0"; 628 intelBusId = "PCI:0:2:0";
@@ -706,12 +712,6 @@ in {
706 dconf.enable = true; 712 dconf.enable = true;
707 }; 713 };
708 714
709 zramSwap = {
710 enable = true;
711 algorithm = "zstd";
712 writebackDevice = "/dev/disk/by-label/swap";
713 };
714
715 services.pcscd.enable = true; 715 services.pcscd.enable = true;
716 716
717 sops.secrets.gkleen-rclone = { 717 sops.secrets.gkleen-rclone = {
@@ -729,6 +729,16 @@ in {
729 environment.sessionVariables."GTK_USE_PORTAL" = "1"; 729 environment.sessionVariables."GTK_USE_PORTAL" = "1";
730 xdg.portal = { 730 xdg.portal = {
731 enable = true; 731 enable = true;
732 extraPortals = with pkgs; [ xdg-desktop-portal-gtk ];
733 config.niri = {
734 default = ["gnome" "gtk"];
735 "org.freedesktop.impl.portal.FileChooser" = ["gtk"];
736 "org.freedesktop.impl.portal.OpenFile" = ["gtk"];
737 "org.freedesktop.impl.portal.Access" = ["gtk"];
738 "org.freedesktop.impl.portal.Notification" = ["gtk"];
739 "org.freedesktop.impl.portal.Secret" = ["gnome-keyring"];
740 "org.freedesktop.impl.portal.Inhibit" = ["none"];
741 };
732 }; 742 };
733 743
734 environment.persistence."/.bcachefs" = { 744 environment.persistence."/.bcachefs" = {
@@ -769,6 +779,10 @@ in {
769 779
770 home-manager.sharedModules = [ flakeInputs.nixVirt.homeModules.default ]; 780 home-manager.sharedModules = [ flakeInputs.nixVirt.homeModules.default ];
771 781
782 environment.pathsToLink = [
783 "share/zsh"
784 ];
785
772 system.stateVersion = "24.11"; 786 system.stateVersion = "24.11";
773 }; 787 };
774} 788}
diff --git a/hosts/sif/hw.nix b/hosts/sif/hw.nix
index fc20ef7c..1bcf0261 100644
--- a/hosts/sif/hw.nix
+++ b/hosts/sif/hw.nix
@@ -8,15 +8,28 @@
8 options = [ "fmask=0033" "dmask=0022" ]; 8 options = [ "fmask=0033" "dmask=0022" ];
9 }; 9 };
10 "/.bcachefs" = 10 "/.bcachefs" =
11 { device = "/dev/mapper/sif-nvm0:/dev/mapper/sif-nvm1"; 11 { options = [
12 "x-systemd.requires=/dev/disk/by-id/dm-name-sif-nvm0"
13 "x-systemd.requires=/dev/disk/by-id/dm-name-sif-nvm1"
14 ];
15 device = "/dev/disk/by-uuid/fe7bdaac-d2f3-4535-a635-e2fb97ef3802";
12 fsType = "bcachefs"; 16 fsType = "bcachefs";
13 neededForBoot = true; 17 neededForBoot = true;
14 }; 18 };
15 "/var/lib/sops-nix".neededForBoot = true; 19 "/var/lib/sops-nix".neededForBoot = true;
16 "/var/lib/systemd".neededForBoot = true; 20 "/var/lib/systemd".neededForBoot = true;
17 }; 21 };
18 system.etc.overlay.enable = false; 22 swapDevices = [
19 systemd.sysusers.enable = false; 23 { label = "swap"; }
24 ];
25 # system.etc.overlay.enable = false;
26
27 boot.initrd.systemd.packages = [
28 (pkgs.writeTextDir "/etc/systemd/system/\\x2ebcachefs.mount.d/block_scan.conf" ''
29 [Mount]
30 Environment=BCACHEFS_BLOCK_SCAN=1
31 '')
32 ];
20 33
21 # boot.initrd.supportedFilesystems.bcachefs = true; 34 # boot.initrd.supportedFilesystems.bcachefs = true;
22 # boot.initrd.systemd.units."dev-sif-nvm0:-dev-sif-nvm1.device".enable = false; 35 # boot.initrd.systemd.units."dev-sif-nvm0:-dev-sif-nvm1.device".enable = false;
diff --git a/hosts/surtr/default.nix b/hosts/surtr/default.nix
index 223e1f10..b8a639d5 100644
--- a/hosts/surtr/default.nix
+++ b/hosts/surtr/default.nix
@@ -6,7 +6,7 @@ with lib;
6 imports = with flake.nixosModules.systemProfiles; [ 6 imports = with flake.nixosModules.systemProfiles; [
7 tmpfs-root qemu-guest openssh rebuild-machines zfs 7 tmpfs-root qemu-guest openssh rebuild-machines zfs
8 ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql 8 ./zfs.nix ./dns ./tls ./http ./bifrost ./matrix ./postgresql
9 ./prometheus ./email ./vpn ./borg.nix ./etebase 9 ./prometheus ./email ./vpn ./borg.nix ./etebase ./immich.nix
10 ]; 10 ];
11 11
12 config = { 12 config = {
diff --git a/hosts/surtr/dns/default.nix b/hosts/surtr/dns/default.nix
index 53df798e..ee1d089d 100644
--- a/hosts/surtr/dns/default.nix
+++ b/hosts/surtr/dns/default.nix
@@ -157,7 +157,7 @@ in {
157 ${concatMapStringsSep "\n" mkZone [ 157 ${concatMapStringsSep "\n" mkZone [
158 { domain = "yggdrasil.li"; 158 { domain = "yggdrasil.li";
159 addACLs = { "yggdrasil.li" = ["ymir_acme_acl"]; }; 159 addACLs = { "yggdrasil.li" = ["ymir_acme_acl"]; };
160 acmeDomains = ["surtr.yggdrasil.li" "yggdrasil.li" "etesync.yggdrasil.li" "app.etesync.yggdrasil.li"]; 160 acmeDomains = ["surtr.yggdrasil.li" "yggdrasil.li" "etesync.yggdrasil.li" "immich.yggdrasil.li" "app.etesync.yggdrasil.li"];
161 } 161 }
162 { domain = "nights.email"; 162 { domain = "nights.email";
163 addACLs = { "nights.email" = ["ymir_acme_acl"]; }; 163 addACLs = { "nights.email" = ["ymir_acme_acl"]; };
diff --git a/hosts/surtr/dns/keys/immich.yggdrasil.li_acme b/hosts/surtr/dns/keys/immich.yggdrasil.li_acme
new file mode 100644
index 00000000..c31234bd
--- /dev/null
+++ b/hosts/surtr/dns/keys/immich.yggdrasil.li_acme
@@ -0,0 +1,24 @@
1{
2 "data": "ENC[AES256_GCM,data:1i27jx1E4nn8/iXEN90tnQve0MX0HcXyYZoQfga1djcESDARd7kX78jncqnSdEMJPIQCyq2zmvOwEiAwyofIjSBSW2teoxD1PybSZdyvKwOnwLqpVWxgw6LORUoN5c1y4+WmnQa1SJ0a1WJwZ3cFRa3LP5JPbZzmNCZWEg+yGwVHNMsmrBSrjLRFC1NPIfX69lWGZl5VIMw2/SoSMDcOsSURWIpVSEYe9LNrc4/cKQQC/rmpXBg9ekIa8xd7NQTcDZA42bDIBQxJzqkUNV3JeIrl0C+eQw==,iv:MEDhvUvy0PfcLGim06VXkiIGgkNgaQcYqGhJraaGC6M=,tag:43CdGFe5JXOsMCHrvgl+BA==,type:str]",
3 "sops": {
4 "kms": null,
5 "gcp_kms": null,
6 "azure_kv": null,
7 "hc_vault": null,
8 "age": [
9 {
10 "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866",
11 "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBLOU5OL0xxVVE2dzVOYnhP\nMUlhYlpyZGZSamU4QkpObHJLS1NJdjdwcmtjCks0OElnaHZvb3BSNnYySnVPcEUx\nazdNVUpZZGRNOVNBVTVUNUdnaC9DM00KLS0tIHUxU2dHMCt4d3hJbXlSNzBKUk1W\na05uZVRwVlMrbEZ2WEkvUy9PUVhmWXcKemRLnCC2mAkCEbZ3bC+iZmIWQCQsI+ew\ni4mmc/mUkGx8/61SR571NXIKmxSx2U5L2IK1pIKy6G/xMkPq+wDgUQ==\n-----END AGE ENCRYPTED FILE-----\n"
12 },
13 {
14 "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq",
15 "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB5VnNUV2lRR292ZmpNeU51\nQ2YxcnVnd21ybGdpd0I1QlJuSEQwNGZYTDJrCml0YUdxaWJHTEFNYWVIY01jWGk2\nZEJSQ0VZcVV1bU9VZ3dBbUh3a0N1ekUKLS0tIFhvNnRqcnRUZFNYOVhuWkFrZ3Vk\nMjU0YnNDODJRYk9lVENLUU9KU2dkWm8KHqtuNtC39S4oiQFRhNT2OOUOY9KQvDYW\nvtcdR8MSZDE7jsqgLgGS/8lIc0GBbIwYWghgFsLmn2Bkdh2q/VuO9w==\n-----END AGE ENCRYPTED FILE-----\n"
16 }
17 ],
18 "lastmodified": "2025-01-03T15:31:39Z",
19 "mac": "ENC[AES256_GCM,data:NKUbtcQf2DfALfm9kwiirmwD3slfTh4HNIg8BT/xbySHfwsaFtmlZTkhavBNr+b5snR8opATVXnJPAoykxXq8q4G1yDeitnTw6x9KfwgyZKpbJANMTBZEwK+CnZbqYRas1bFC88+D0yWI1yUnle+NPQ8VUj+KxCiUNuWO80mWhE=,iv:2a7CawUQujcZuR1pmsm9L2KGKcrBRAOxiIWEKCkTCEM=,tag:j8caxyN2fvx2FnWXHkWKcw==,type:str]",
20 "pgp": null,
21 "unencrypted_suffix": "_unencrypted",
22 "version": "3.9.2"
23 }
24} \ No newline at end of file
diff --git a/hosts/surtr/dns/zones/li.yggdrasil.soa b/hosts/surtr/dns/zones/li.yggdrasil.soa
index 092d23ec..9af6232f 100644
--- a/hosts/surtr/dns/zones/li.yggdrasil.soa
+++ b/hosts/surtr/dns/zones/li.yggdrasil.soa
@@ -1,7 +1,7 @@
1$ORIGIN yggdrasil.li. 1$ORIGIN yggdrasil.li.
2$TTL 3600 2$TTL 3600
3@ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( 3@ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li (
4 2024102100 ; serial 4 2025010300 ; serial
5 10800 ; refresh 5 10800 ; refresh
6 3600 ; retry 6 3600 ; retry
7 604800 ; expire 7 604800 ; expire
@@ -69,6 +69,14 @@ _acme-challenge.app.etesync IN NS ns.yggdrasil.li.
69 69
70app.etesync IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" 70app.etesync IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::"
71 71
72immich IN A 202.61.241.61
73immich IN AAAA 2a03:4000:52:ada::
74immich IN MX 0 surtr.yggdrasil.li
75immich IN TXT "v=spf1 redirect=surtr.yggdrasil.li"
76_acme-challenge.immich IN NS ns.yggdrasil.li.
77
78immich IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::"
79
72vidhar IN AAAA 2a03:4000:52:ada:4:1:: 80vidhar IN AAAA 2a03:4000:52:ada:4:1::
73vidhar IN MX 0 ymir.yggdrasil.li 81vidhar IN MX 0 ymir.yggdrasil.li
74vidhar IN TXT "v=spf1 redirect=yggdrasil.li" 82vidhar IN TXT "v=spf1 redirect=yggdrasil.li"
diff --git a/hosts/surtr/immich.nix b/hosts/surtr/immich.nix
new file mode 100644
index 00000000..61a55e77
--- /dev/null
+++ b/hosts/surtr/immich.nix
@@ -0,0 +1,66 @@
1{ config, ... }:
2
3{
4 config = {
5 security.acme.rfc2136Domains = {
6 "immich.yggdrasil.li" = {
7 restartUnits = ["nginx.service"];
8 };
9 };
10
11 services.nginx = {
12 upstreams."immich" = {
13 servers = {
14 "[2a03:4000:52:ada:4:1::]:2283" = {};
15 };
16 extraConfig = ''
17 keepalive 8;
18 '';
19 };
20 virtualHosts = {
21 "immich.yggdrasil.li" = {
22 kTLS = true;
23 http3 = true;
24 forceSSL = true;
25 sslCertificate = "/run/credentials/nginx.service/immich.yggdrasil.li.pem";
26 sslCertificateKey = "/run/credentials/nginx.service/immich.yggdrasil.li.key.pem";
27 sslTrustedCertificate = "/run/credentials/nginx.service/immich.yggdrasil.li.chain.pem";
28 extraConfig = ''
29 charset utf-8;
30 '';
31
32 locations = {
33 "/".extraConfig = ''
34 proxy_pass http://immich;
35
36 proxy_http_version 1.1;
37 proxy_set_header Upgrade $http_upgrade;
38 proxy_set_header Connection "upgrade";
39
40 proxy_redirect off;
41 proxy_set_header Host $host;
42 proxy_set_header X-Real-IP $remote_addr;
43 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
44 proxy_set_header X-Forwarded-Host $server_name;
45 proxy_set_header X-Forwarded-Proto $scheme;
46
47 client_max_body_size 0;
48 proxy_request_buffering off;
49 proxy_buffering off;
50 '';
51 };
52 };
53 };
54 };
55
56 systemd.services.nginx = {
57 serviceConfig = {
58 LoadCredential = [
59 "immich.yggdrasil.li.key.pem:${config.security.acme.certs."immich.yggdrasil.li".directory}/key.pem"
60 "immich.yggdrasil.li.pem:${config.security.acme.certs."immich.yggdrasil.li".directory}/fullchain.pem"
61 "immich.yggdrasil.li.chain.pem:${config.security.acme.certs."immich.yggdrasil.li".directory}/chain.pem"
62 ];
63 };
64 };
65 };
66}
diff --git a/hosts/surtr/tls/tsig_key.gup b/hosts/surtr/tls/tsig_key.gup
index 3d81b603..825479e5 100644
--- a/hosts/surtr/tls/tsig_key.gup
+++ b/hosts/surtr/tls/tsig_key.gup
@@ -1,6 +1,6 @@
1#!/usr/bin/env zsh 1#!/usr/bin/env zsh
2 2
3keyFile=../dns/keys/${2:t}_acme.yaml 3keyFile=../dns/keys/${2:t}_acme
4gup -u $keyFile 4gup -u $keyFile
5sops -d --input-type=binary --output-type=binary ${keyFile} | yq -r '.key[0].secret' > $1 5sops -d --input-type=binary --output-type=binary ${keyFile} | yq -r '.key[0].secret' > $1
6sops -p '7ED22F4AA7BB55728B643DC5471B7D88E4EF66F8,30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51' --input-type=binary -e -i $1 \ No newline at end of file 6sops -p '7ED22F4AA7BB55728B643DC5471B7D88E4EF66F8,30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51' --input-type=binary -e -i $1
diff --git a/hosts/surtr/tls/tsig_keys/immich.yggdrasil.li b/hosts/surtr/tls/tsig_keys/immich.yggdrasil.li
new file mode 100644
index 00000000..73104cc1
--- /dev/null
+++ b/hosts/surtr/tls/tsig_keys/immich.yggdrasil.li
@@ -0,0 +1,24 @@
1{
2 "data": "ENC[AES256_GCM,data:COfmT91I4+yiPhN3Hi7BTqMHyKhdKtwlzT9vNgTZc7FWTHhfuTtCHQo/rhX0,iv:RDs//AT8peUhKwIRdchCScUr/PlEzyMzQPB90S4k3g4=,tag:Lh4BULmQ6+hC+Ed8s9k0Hw==,type:str]",
3 "sops": {
4 "kms": null,
5 "gcp_kms": null,
6 "azure_kv": null,
7 "hc_vault": null,
8 "age": [
9 {
10 "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866",
11 "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBGVEQvU29uWnVjMS8xcGp1\nam5hZVN3ejAxaGdpVWNia0VxT0I3dzR4YlV3ClczOGd0ZDJmUDhqb2dEdm5VeUdX\nRlR1WDNYUU9qaTYrRzhYMXBTV1JjV1UKLS0tIHBONU55RWtRSkR6K2NTNFZrUEZj\nbktqY0xBdGtiZWFFV3JUUVZTOC9YV2MKI4Ytz1NZ9+Og0GzIt/bh6L3aJUeR476g\nyRNifW4eOHf4Ne02ElpEoq6woInkxk8Ou/SJVIRmEOhjwm+qbV17gQ==\n-----END AGE ENCRYPTED FILE-----\n"
12 },
13 {
14 "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq",
15 "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBVOVNydWcyYjFrZDR2WStu\nWkJQd3VTTGtqVzdLVFR1eFdPZXBtVFBzVXc4ClREQVpKeXlhWlBFQzVFL0VGME5K\nUUhoa3A1YWdvSkZVV1FQcUh5L3RoUmMKLS0tIHE4c3A1OTNXVk9xZHJPZTlSMlQ4\nZnZUTXdjUGZuN3NoSEFSSko1aU5aQUEKHcuI2+9q7DsDwRn7mfwcSyC7AixzCC0e\nhqnGaW0HxmtLeOFuSPLdFMhhockCYGEV/907i/X6EImepWC4cf3bqA==\n-----END AGE ENCRYPTED FILE-----\n"
16 }
17 ],
18 "lastmodified": "2025-01-03T15:31:40Z",
19 "mac": "ENC[AES256_GCM,data:soDFDk35A1ULzOosZNrbhvtG3NPJDpAtLP3xrDtCBxgSGQ0lWrQ0o3MaKaJoDXQv7g/vYghmSwjH+0In0Ib3OWg0WLAlhwTEsiAn1o4JNRu/wF5aqvazOiDzFu7cyWil4Lsphy5eZgtc4IUp75SlCQc71xlNLoxudPpdcSxNLWg=,iv:jBYUZbiWM5gxFA+ZdpxpZIkz3WfgFi59tXFp242/qr8=,tag:H7ihAryZ8be+BbaqXFhRRg==,type:str]",
20 "pgp": null,
21 "unencrypted_suffix": "_unencrypted",
22 "version": "3.9.2"
23 }
24} \ No newline at end of file
diff --git a/hosts/vidhar/default.nix b/hosts/vidhar/default.nix
index 42a9e80d..b0797d8a 100644
--- a/hosts/vidhar/default.nix
+++ b/hosts/vidhar/default.nix
@@ -4,7 +4,7 @@ with lib;
4 4
5{ 5{
6 imports = with flake.nixosModules.systemProfiles; [ 6 imports = with flake.nixosModules.systemProfiles; [
7 ./zfs.nix ./network ./samba.nix ./dns ./prometheus ./borg ./pgbackrest 7 ./zfs.nix ./network ./samba.nix ./dns ./prometheus ./borg ./pgbackrest ./postgresql.nix ./immich.nix
8 tmpfs-root zfs 8 tmpfs-root zfs
9 initrd-all-crypto-modules default-locale openssh rebuild-machines 9 initrd-all-crypto-modules default-locale openssh rebuild-machines
10 build-server 10 build-server
@@ -136,7 +136,7 @@ with lib;
136 wantedBy = ["basic.target"]; 136 wantedBy = ["basic.target"];
137 serviceConfig = { 137 serviceConfig = {
138 ExecStart = pkgs.writeShellScript "limit-pstate-start" '' 138 ExecStart = pkgs.writeShellScript "limit-pstate-start" ''
139 echo 60 > /sys/devices/system/cpu/intel_pstate/max_perf_pct 139 echo 50 > /sys/devices/system/cpu/intel_pstate/max_perf_pct
140 ''; 140 '';
141 RemainAfterExit = true; 141 RemainAfterExit = true;
142 ExecStop = pkgs.writeShellScript "limit-pstate-stop" '' 142 ExecStop = pkgs.writeShellScript "limit-pstate-stop" ''
diff --git a/hosts/vidhar/immich.nix b/hosts/vidhar/immich.nix
new file mode 100644
index 00000000..a1f145a8
--- /dev/null
+++ b/hosts/vidhar/immich.nix
@@ -0,0 +1,10 @@
1{ ... }:
2
3{
4 config = {
5 services.immich = {
6 enable = true;
7 host = "2a03:4000:52:ada:4:1::";
8 };
9 };
10}
diff --git a/hosts/vidhar/network/ruleset.nft b/hosts/vidhar/network/ruleset.nft
index 9f519302..10fd4c51 100644
--- a/hosts/vidhar/network/ruleset.nft
+++ b/hosts/vidhar/network/ruleset.nft
@@ -1,4 +1,5 @@
1define icmp_protos = { ipv6-icmp, icmp, igmp } 1define icmp_protos = { ipv6-icmp, icmp, igmp }
2define bifrost_surtr = 2a03:4000:52:ada:4::/128
2 3
3table arp filter { 4table arp filter {
4 limit lim_arp_local { 5 limit lim_arp_local {
@@ -90,6 +91,7 @@ table inet filter {
90 counter http-rx {} 91 counter http-rx {}
91 counter tftp-rx {} 92 counter tftp-rx {}
92 counter pgbackrest-rx {} 93 counter pgbackrest-rx {}
94 counter immich-rx {}
93 95
94 counter established-rx {} 96 counter established-rx {}
95 97
@@ -118,6 +120,7 @@ table inet filter {
118 counter http-tx {} 120 counter http-tx {}
119 counter tftp-tx {} 121 counter tftp-tx {}
120 counter pgbackrest-tx {} 122 counter pgbackrest-tx {}
123 counter immich-tx {}
121 124
122 counter tx {} 125 counter tx {}
123 126
@@ -193,6 +196,8 @@ table inet filter {
193 196
194 tcp dport 8432 counter name pgbackrest-rx accept 197 tcp dport 8432 counter name pgbackrest-rx accept
195 198
199 iifname bifrost tcp dport 2283 ip6 saddr $bifrost_surtr counter name immich-rx accept
200
196 ct state { established, related } counter name established-rx accept 201 ct state { established, related } counter name established-rx accept
197 202
198 203
@@ -240,6 +245,8 @@ table inet filter {
240 245
241 tcp sport 8432 counter name pgbackrest-tx accept 246 tcp sport 8432 counter name pgbackrest-tx accept
242 247
248 iifname bifrost tcp sport 2283 ip6 daddr $bifrost_surtr counter name immich-tx accept
249
243 250
244 counter name tx 251 counter name tx
245 } 252 }
diff --git a/hosts/vidhar/pgbackrest/default.nix b/hosts/vidhar/pgbackrest/default.nix
index ffb149f5..1e0828ce 100644
--- a/hosts/vidhar/pgbackrest/default.nix
+++ b/hosts/vidhar/pgbackrest/default.nix
@@ -130,8 +130,9 @@ in {
130 }; 130 };
131 131
132 systemd.tmpfiles.rules = [ 132 systemd.tmpfiles.rules = [
133 "d /var/lib/pgbackrest 0750 pgbackrest pgbackrest - -" 133 "d /var/lib/pgbackrest 0770 pgbackrest pgbackrest - -"
134 "d /var/spool/pgbackrest 0750 pgbackrest pgbackrest - -" 134 "d /var/spool/pgbackrest 0770 pgbackrest pgbackrest - -"
135 "d /tmp/pgbackrest 0770 pgbackrest pgbackrest - -"
135 ]; 136 ];
136 137
137 users = { 138 users = {
@@ -141,7 +142,9 @@ in {
141 isSystemUser = true; 142 isSystemUser = true;
142 home = "/var/lib/pgbackrest"; 143 home = "/var/lib/pgbackrest";
143 }; 144 };
144 groups.pgbackrest = {}; 145 groups.pgbackrest = {
146 members = [ "postgres" ];
147 };
145 }; 148 };
146 149
147 systemd.services."pgbackrest-tls-server".serviceConfig = { 150 systemd.services."pgbackrest-tls-server".serviceConfig = {
diff --git a/hosts/vidhar/postgresql.nix b/hosts/vidhar/postgresql.nix
new file mode 100644
index 00000000..7e44e69f
--- /dev/null
+++ b/hosts/vidhar/postgresql.nix
@@ -0,0 +1,36 @@
1{ pkgs, config, flake, flakeInputs, ... }:
2
3let
4 nixpkgs-pgbackrest = import (flakeInputs.nixpkgs-pgbackrest.outPath + "/pkgs/top-level") {
5 overlays = [ flake.overlays.libdscp ];
6 localSystem = config.nixpkgs.system;
7 };
8in {
9 config = {
10 services.postgresql = {
11 enable = true;
12 package = pkgs.postgresql_15;
13 };
14
15 services.pgbackrest = {
16 settings."vidhar" = {
17 pg1-path = config.services.postgresql.dataDir;
18
19 repo1-path = "/var/lib/pgbackrest";
20 repo1-retention-full-type = "time";
21 repo1-retention-full = 14;
22 repo1-retention-archive = 7;
23 };
24
25 backups."vidhar-daily" = {
26 stanza = "vidhar";
27 repo = "1";
28 timerConfig.OnCalendar = "daily";
29 };
30 };
31
32 systemd.services.postgresql.serviceConfig = {
33 ReadWritePaths = [ "/var/spool/pgbackrest" "/var/lib/pgbackrest/archive/vidhar" ];
34 };
35 };
36}
diff --git a/hosts/vidhar/zfs.nix b/hosts/vidhar/zfs.nix
index 518c3287..9d667fd6 100644
--- a/hosts/vidhar/zfs.nix
+++ b/hosts/vidhar/zfs.nix
@@ -34,7 +34,7 @@ with lib;
34 }; 34 };
35 35
36 "/etc/zfs/zfs-list.cache" = 36 "/etc/zfs/zfs-list.cache" =
37 { device = "ssd-raid1/local/zfs-zfs--list.cache"; 37 { device = "ssd-raid1/local/etc-zfs-zfs--list.cache";
38 fsType = "zfs"; 38 fsType = "zfs";
39 neededForBoot = true; 39 neededForBoot = true;
40 }; 40 };
diff --git a/modules/niri.nix b/modules/niri.nix
new file mode 100644
index 00000000..4e2ddf8b
--- /dev/null
+++ b/modules/niri.nix
@@ -0,0 +1,6 @@
1{ flakeInputs, ... }:
2{
3 imports = [
4 flakeInputs.niri-flake.nixosModules.niri
5 ];
6}
diff --git a/modules/uucp.nix b/modules/uucp.nix
index abca2acb..10f7297b 100644
--- a/modules/uucp.nix
+++ b/modules/uucp.nix
@@ -314,32 +314,7 @@ in {
314 in listToAttrs (map wrapper ["uucico" "cu" "uucp" "uuname" "uustat" "uux" "uuxqt"]); 314 in listToAttrs (map wrapper ["uucico" "cu" "uucp" "uuname" "uustat" "uux" "uuxqt"]);
315 315
316 nixpkgs.overlays = [(self: super: { 316 nixpkgs.overlays = [(self: super: {
317 uucp = super.lib.overrideDerivation super.uucp (oldAttrs: { 317 rmail = super.writeShellScriptBin "rmail" ''
318 configureFlags = "--with-newconfigdir=/etc/uucp";
319 patches = [
320 (super.writeText "mailprogram" ''
321 policy.h | 2 +-
322 1 file changed, 1 insertion(+), 1 deletion(-)
323
324 diff --git a/policy.h b/policy.h
325 index 5afe34b..8e92c8b 100644
326 --- a/policy.h
327 +++ b/policy.h
328 @@ -240,7 +240,7 @@
329 the sendmail choice below. Otherwise, select one of the other
330 choices as appropriate. */
331 #if 1
332 -#define MAIL_PROGRAM "/usr/lib/sendmail -t"
333 +#define MAIL_PROGRAM "${config.security.wrapperDir}/sendmail -t"
334 /* #define MAIL_PROGRAM "/usr/sbin/sendmail -t" */
335 #define MAIL_PROGRAM_TO_BODY 1
336 #define MAIL_PROGRAM_SUBJECT_BODY 1
337 '')
338 ];
339 });
340 rmail = super.writeScriptBin "rmail" ''
341 #!${super.stdenv.shell}
342
343 # Dummy UUCP rmail command for postfix/qmail systems 318 # Dummy UUCP rmail command for postfix/qmail systems
344 319
345 IFS=" " read junk from junk junk junk junk junk junk junk relay 320 IFS=" " read junk from junk junk junk junk junk junk junk relay
diff --git a/nvfetcher.toml b/nvfetcher.toml
index c0566373..d33040c5 100644
--- a/nvfetcher.toml
+++ b/nvfetcher.toml
@@ -107,3 +107,7 @@ fetch.tarball = "https://github.com/JonathonReinhart/spice-record/archive/refs/t
107[yt-dlp] 107[yt-dlp]
108src.pypi = "yt_dlp" 108src.pypi = "yt_dlp"
109fetch.pypi = "yt_dlp" 109fetch.pypi = "yt_dlp"
110
111[mako]
112src.git = "https://github.com/emersion/mako"
113fetch.git = "https://github.com/emersion/mako" \ No newline at end of file
diff --git a/overlays/mako.nix b/overlays/mako.nix
new file mode 100644
index 00000000..1c1464fb
--- /dev/null
+++ b/overlays/mako.nix
@@ -0,0 +1,5 @@
1{ final, prev, sources, ... }: {
2 mako = prev.mako.overrideAttrs (oldAttrs: {
3 inherit (sources.mako) version src;
4 });
5}
diff --git a/overlays/prometheus-lvm-exporter.nix b/overlays/prometheus-lvm-exporter.nix
index 6b671cab..240f8d85 100644
--- a/overlays/prometheus-lvm-exporter.nix
+++ b/overlays/prometheus-lvm-exporter.nix
@@ -3,7 +3,7 @@
3 pname = "prometheus-lvm-exporter"; 3 pname = "prometheus-lvm-exporter";
4 inherit (sources.prometheus-lvm-exporter) version src; 4 inherit (sources.prometheus-lvm-exporter) version src;
5 5
6 vendorHash = "sha256-vqxsg70ShMo4OVdzhqYDj/HT3RTpCUBGHze/EkbBJig="; 6 vendorHash = "sha256-z/fV0PzoWSDTJ44En19o7zJPPPox5ymFw7sw0Ab9t00=";
7 7
8 doCheck = false; 8 doCheck = false;
9 9
diff --git a/overlays/uucp/default.nix b/overlays/uucp/default.nix
new file mode 100644
index 00000000..4189dbcc
--- /dev/null
+++ b/overlays/uucp/default.nix
@@ -0,0 +1,9 @@
1{ final, prev, ... }: {
2 uucp = prev.uucp.overrideAttrs (oldAttrs: {
3 configureFlags = (oldAttrs.configureFlags or []) ++ ["--with-newconfigdir=/etc/uucp"];
4 patches = (oldAttrs.patches or []) ++ [
5 ./mailprogram.patch
6 ];
7 NIX_CFLAGS_COMPILE = "${oldAttrs.NIX_CFLAGS_COMPILE or ""} -Wno-error=incompatible-pointer-types";
8 });
9}
diff --git a/overlays/uucp/mailprogram.patch b/overlays/uucp/mailprogram.patch
new file mode 100644
index 00000000..89ac8f31
--- /dev/null
+++ b/overlays/uucp/mailprogram.patch
@@ -0,0 +1,16 @@
1 policy.h | 2 +-
2 1 file changed, 1 insertion(+), 1 deletion(-)
3
4diff --git a/policy.h b/policy.h
5index 5afe34b..8e92c8b 100644
6--- a/policy.h
7+++ b/policy.h
8@@ -240,7 +240,7 @@
9 the sendmail choice below. Otherwise, select one of the other
10 choices as appropriate. */
11 #if 1
12-#define MAIL_PROGRAM "/usr/lib/sendmail -t"
13+#define MAIL_PROGRAM "${config.security.wrapperDir}/sendmail -t"
14 /* #define MAIL_PROGRAM "/usr/sbin/sendmail -t" */
15 #define MAIL_PROGRAM_TO_BODY 1
16 #define MAIL_PROGRAM_SUBJECT_BODY 1
diff --git a/overlays/wttrbar/default.nix b/overlays/wttrbar/default.nix
deleted file mode 100644
index 876fa699..00000000
--- a/overlays/wttrbar/default.nix
+++ /dev/null
@@ -1,7 +0,0 @@
1{ prev, ... }: {
2 wttrbar = prev.wttrbar.overrideAttrs (oldAttrs: {
3 patches = (oldAttrs.patches or []) ++ [
4 ./icons.patch
5 ];
6 });
7}
diff --git a/overlays/wttrbar/icons.patch b/overlays/wttrbar/icons.patch
deleted file mode 100644
index e7e721c8..00000000
--- a/overlays/wttrbar/icons.patch
+++ /dev/null
@@ -1,154 +0,0 @@
1diff --git a/src/constants.rs b/src/constants.rs
2index 81b1926..3619d8f 100644
3--- a/src/constants.rs
4+++ b/src/constants.rs
5@@ -1,64 +1,52 @@
6 pub const WEATHER_CODES: &[(i32, &str)] = &[
7- (113, "☀ī¸"),
8- (116, "🌤ī¸"),
9- (119, "☁ī¸"),
10- (122, "đŸŒĨī¸"),
11- (143, "đŸŒĢī¸"),
12- (176, "đŸŒĻī¸"),
13- (179, "🌧ī¸"),
14- (182, "🌨ī¸"),
15- (185, "🌨ī¸"),
16- (200, "🌩ī¸"),
17- (227, "❄ī¸"),
18- (230, "❄ī¸"),
19- (248, "đŸŒĢī¸"),
20- (260, "đŸŒĢī¸"),
21- (263, "🌧ī¸"),
22- (266, "🌧ī¸"),
23- (281, "đŸŒĻī¸"),
24- (284, "đŸŒĻī¸"),
25- (293, "🌧ī¸"),
26- (296, "🌧ī¸"),
27- (299, "🌧ī¸"),
28- (302, "🌧ī¸"),
29- (305, "🌧ī¸"),
30- (308, "🌧ī¸"),
31- (311, "🌧ī¸"),
32- (314, "🌧ī¸"),
33- (317, "🌧ī¸"),
34- (320, "🌨ī¸"),
35- (323, "🌨ī¸"),
36- (326, "🌨ī¸"),
37- (329, "🌨ī¸"),
38- (332, "🌨ī¸"),
39- (335, "🌨ī¸"),
40- (338, "🌨ī¸"),
41- (350, "🌨ī¸"),
42- (353, "🌧ī¸"),
43- (356, "🌧ī¸"),
44- (359, "🌧ī¸"),
45- (362, "🌨ī¸"),
46- (365, "🌨ī¸"),
47- (368, "🌨ī¸"),
48- (371, "🌨ī¸"),
49- (374, "🌨ī¸"),
50- (377, "🌨ī¸"),
51- (386, "🌩ī¸"),
52- (389, "🌨ī¸"),
53- (392, "🌨ī¸"),
54- (395, "🌨ī¸"),
55- (398, "🌨ī¸"),
56- (401, "🌨ī¸"),
57- (404, "🌨ī¸"),
58- (407, "🌨ī¸"),
59- (410, "🌨ī¸"),
60- (413, "🌨ī¸"),
61- (416, "🌨ī¸"),
62- (419, "🌨ī¸"),
63- (422, "🌨ī¸"),
64- (425, "🌨ī¸"),
65- (428, "🌨ī¸"),
66- (431, "🌨ī¸"),
67+ (113, "<span font=\"Symbols Nerd Font Mono\"></span>"),
68+ (116, "<span font=\"Symbols Nerd Font Mono\"></span>"),
69+ (119, "<span font=\"Symbols Nerd Font Mono\">îŒŊ</span>"),
70+ (122, "<span font=\"Symbols Nerd Font Mono\"></span>"),
71+ (143, "<span font=\"Symbols Nerd Font Mono\"></span>"),
72+ (176, "<span font=\"Symbols Nerd Font Mono\"></span>"),
73+ (179, "<span font=\"Symbols Nerd Font Mono\"></span>"),
74+ (182, "<span font=\"Symbols Nerd Font Mono\">îŽĒ</span>"),
75+ (185, "<span font=\"Symbols Nerd Font Mono\">îŽĒ</span>"),
76+ (200, "<span font=\"Symbols Nerd Font Mono\"></span>"),
77+ (227, "<span font=\"Symbols Nerd Font Mono\"></span>"),
78+ (230, "<span font=\"Symbols Nerd Font Mono\"></span>"),
79+ (248, "<span font=\"Symbols Nerd Font Mono\"></span>"),
80+ (260, "<span font=\"Symbols Nerd Font Mono\"></span>"),
81+ (263, "<span font=\"Symbols Nerd Font Mono\"></span>"),
82+ (266, "<span font=\"Symbols Nerd Font Mono\"></span>"),
83+ (281, "<span font=\"Symbols Nerd Font Mono\">îŽĒ</span>"),
84+ (284, "<span font=\"Symbols Nerd Font Mono\">îŽĒ</span>"),
85+ (293, "<span font=\"Symbols Nerd Font Mono\"></span>"),
86+ (296, "<span font=\"Symbols Nerd Font Mono\"></span>"),
87+ (299, "<span font=\"Symbols Nerd Font Mono\"></span>"),
88+ (302, "<span font=\"Symbols Nerd Font Mono\"></span>"),
89+ (305, "<span font=\"Symbols Nerd Font Mono\"></span>"),
90+ (308, "<span font=\"Symbols Nerd Font Mono\"></span>"),
91+ (311, "<span font=\"Symbols Nerd Font Mono\">îŽĒ</span>"),
92+ (314, "<span font=\"Symbols Nerd Font Mono\">îŽĒ</span>"),
93+ (317, "<span font=\"Symbols Nerd Font Mono\">îŽĒ</span>"),
94+ (320, "<span font=\"Symbols Nerd Font Mono\"></span>"),
95+ (323, "<span font=\"Symbols Nerd Font Mono\"></span>"),
96+ (326, "<span font=\"Symbols Nerd Font Mono\"></span>"),
97+ (329, "<span font=\"Symbols Nerd Font Mono\"></span>"),
98+ (332, "<span font=\"Symbols Nerd Font Mono\"></span>"),
99+ (335, "<span font=\"Symbols Nerd Font Mono\"></span>"),
100+ (338, "<span font=\"Symbols Nerd Font Mono\"></span>"),
101+ (350, "<span font=\"Symbols Nerd Font Mono\">îŽĒ</span>"),
102+ (353, "<span font=\"Symbols Nerd Font Mono\"></span>"),
103+ (356, "<span font=\"Symbols Nerd Font Mono\"></span>"),
104+ (359, "<span font=\"Symbols Nerd Font Mono\"></span>"),
105+ (362, "<span font=\"Symbols Nerd Font Mono\"></span>"),
106+ (365, "<span font=\"Symbols Nerd Font Mono\"></span>"),
107+ (368, "<span font=\"Symbols Nerd Font Mono\"></span>"),
108+ (371, "<span font=\"Symbols Nerd Font Mono\"></span>"),
109+ (374, "<span font=\"Symbols Nerd Font Mono\"></span>"),
110+ (377, "<span font=\"Symbols Nerd Font Mono\">îŽĒ</span>"),
111+ (386, "<span font=\"Symbols Nerd Font Mono\"></span>"),
112+ (389, "<span font=\"Symbols Nerd Font Mono\"></span>"),
113+ (392, "<span font=\"Symbols Nerd Font Mono\">îĨ</span>"),
114+ (395, "<span font=\"Symbols Nerd Font Mono\"></span>"),
115 ];
116
117 pub const ICON_PLACEHOLDER: &str = "{ICON}";
118diff --git a/src/main.rs b/src/main.rs
119index 6ac4654..1b84207 100644
120--- a/src/main.rs
121+++ b/src/main.rs
122@@ -175,20 +175,20 @@ fn main() {
123
124 if args.fahrenheit {
125 tooltip += &format!(
126- "âŦ†ī¸ {}° âŦ‡ī¸ {}° ",
127+ "<span font=\"Symbols Nerd Font Mono\">ķ°¸ƒ</span> {}° <span font=\"Symbols Nerd Font Mono\">ķ°¸‚</span> {}° ",
128 day["maxtempF"].as_str().unwrap(),
129 day["mintempF"].as_str().unwrap(),
130 );
131 } else {
132 tooltip += &format!(
133- "âŦ†ī¸ {}° âŦ‡ī¸ {}° ",
134+ "<span font=\"Symbols Nerd Font Mono\">ķ°¸ƒ</span> {}° <span font=\"Symbols Nerd Font Mono\">ķ°¸‚</span> {}° ",
135 day["maxtempC"].as_str().unwrap(),
136 day["mintempC"].as_str().unwrap(),
137 );
138 };
139
140 tooltip += &format!(
141- "🌅 {} 🌇 {}\n",
142+ "<span font=\"Symbols Nerd Font Mono\"></span> {} <span font=\"Symbols Nerd Font Mono\"></span> {}\n",
143 format_ampm_time(day, "sunrise", args.ampm),
144 format_ampm_time(day, "sunset", args.ampm),
145 );
146@@ -207,7 +207,7 @@ fn main() {
147 }
148
149 let mut tooltip_line = format!(
150- "{} {} {} {}",
151+ "{} {}{} {}",
152 format_time(hour["time"].as_str().unwrap(), args.ampm),
153 WEATHER_CODES
154 .iter()
diff --git a/system-profiles/core/default.nix b/system-profiles/core/default.nix
index 71d0619a..b85aea4e 100644
--- a/system-profiles/core/default.nix
+++ b/system-profiles/core/default.nix
@@ -181,7 +181,7 @@ in {
181 programs.ssh.internallyManaged = mkForce true; 181 programs.ssh.internallyManaged = mkForce true;
182 } 182 }
183 ]; 183 ];
184 extraSpecialArgs = { inherit flake flakeInputs path; }; 184 extraSpecialArgs = { inherit flake flakeInputs path; hostConfig = config; };
185 }; 185 };
186 186
187 sops = mkIf hasSops { 187 sops = mkIf hasSops {
@@ -208,11 +208,22 @@ in {
208 enableNg = true; 208 enableNg = true;
209 }; 209 };
210 }) 210 })
211 ++ (optional (options ? system.rebuild.enableNg) {
212 system.rebuild.enableNg = lib.mkDefault true;
213 })
214 ++ (optional (options ? services.userborn) {
215 services.userborn = {
216 enable = lib.mkDefault true;
217 passwordFilesLocation = lib.mkDefault "/var/lib/nixos";
218 };
219 })
220 ++ (optional (!(options ? services.userborn) && (options ? system.etc)) {
221 systemd.sysusers.enable = lib.mkDefault true;
222 })
211 ++ (optional (options ? system.etc) { 223 ++ (optional (options ? system.etc) {
212 boot.initrd.systemd.enable = lib.mkDefault true; 224 boot.initrd.systemd.enable = lib.mkDefault true;
213 system.etc.overlay.enable = lib.mkDefault true; 225 system.etc.overlay.enable = lib.mkDefault true;
214 system.etc.overlay.mutable = lib.mkDefault (!config.systemd.sysusers.enable); 226 system.etc.overlay.mutable = lib.mkDefault (!config.systemd.sysusers.enable);
215 systemd.sysusers.enable = lib.mkDefault true;
216 227
217 # Random perl remnants 228 # Random perl remnants
218 system.disableInstallerTools = lib.mkDefault true; 229 system.disableInstallerTools = lib.mkDefault true;
diff --git a/system-profiles/niri-flake.nix b/system-profiles/niri-flake.nix
new file mode 100644
index 00000000..b28d51ff
--- /dev/null
+++ b/system-profiles/niri-flake.nix
@@ -0,0 +1,4 @@
1{ ... }:
2{
3 config.niri-flake.cache.enable = false;
4}
diff --git a/system-profiles/niri-unstable.nix b/system-profiles/niri-unstable.nix
new file mode 100644
index 00000000..3a8b393d
--- /dev/null
+++ b/system-profiles/niri-unstable.nix
@@ -0,0 +1,11 @@
1{ config, pkgs, lib, ... }:
2{
3 config = {
4 programs.niri.package = lib.mkDefault pkgs.niri-unstable;
5 home-manager.sharedModules = [
6 {
7 programs.niri.package = lib.mkDefault config.programs.niri.package;
8 }
9 ];
10 };
11}
diff --git a/user-profiles/mpv/default.nix b/user-profiles/mpv/default.nix
index 2df87994..83eba2a9 100644
--- a/user-profiles/mpv/default.nix
+++ b/user-profiles/mpv/default.nix
@@ -1,6 +1,6 @@
1{ config, lib, userName, pkgs, sources, ... }: 1{ lib, userName, pkgs, sources, ... }:
2{ 2{
3 home-manager.users.${userName} = { 3 home-manager.users.${userName} = { config, ... }: {
4 programs.mpv = { 4 programs.mpv = {
5 enable = true; 5 enable = true;
6 package = pkgs.symlinkJoin { 6 package = pkgs.symlinkJoin {
@@ -105,6 +105,7 @@
105 }; 105 };
106 config = { 106 config = {
107 ytdl = true; 107 ytdl = true;
108 ytdl-raw-options = "sub-langs=\"${config.programs.yt-dlp.settings.sub-langs}\"";
108 subs-with-matching-audio = false; 109 subs-with-matching-audio = false;
109 audio-display = false; 110 audio-display = false;
110 osd-font = "Fira Sans"; 111 osd-font = "Fira Sans";
diff --git a/user-profiles/yt-dlp.nix b/user-profiles/yt-dlp.nix
index 0f0b2204..9e77f734 100644
--- a/user-profiles/yt-dlp.nix
+++ b/user-profiles/yt-dlp.nix
@@ -14,7 +14,7 @@
14 ]; 14 ];
15 embed-subs = true; 15 embed-subs = true;
16 # write-subs = true; 16 # write-subs = true;
17 # write-auto-subs = true; 17 write-auto-subs = true;
18 sub-langs = "en(-(gb|us|orig))?,de(-(de|orig))?,-live_chat,-rechat"; 18 sub-langs = "en(-(gb|us|orig))?,de(-(de|orig))?,-live_chat,-rechat";
19 prefer-free-formats = true; 19 prefer-free-formats = true;
20 embed-metadata = true; 20 embed-metadata = true;