diff options
227 files changed, 12877 insertions, 3379 deletions
| @@ -8,6 +8,12 @@ creation_rules: | |||
| 8 | - path_regex: ^hosts/surtr/email/ca | 8 | - path_regex: ^hosts/surtr/email/ca |
| 9 | key_groups: | 9 | key_groups: |
| 10 | - age: [ *admin_gkleen ] | 10 | - age: [ *admin_gkleen ] |
| 11 | - path_regex: ^home-modules/lmu-hausschrift/ | ||
| 12 | key_groups: | ||
| 13 | - age: [ *admin_gkleen ] | ||
| 14 | - path_regex: ^accounts/gkleen@sif/ | ||
| 15 | key_groups: | ||
| 16 | - age: [ *admin_gkleen ] | ||
| 11 | - path_regex: surtr\/?[^\/]*$ | 17 | - path_regex: surtr\/?[^\/]*$ |
| 12 | key_groups: | 18 | key_groups: |
| 13 | - age: [ *admin_gkleen, *machine_surtr ] | 19 | - age: [ *admin_gkleen, *machine_surtr ] |
diff --git a/_sources/generated.json b/_sources/generated.json index b85cb274..dd73e455 100644 --- a/_sources/generated.json +++ b/_sources/generated.json | |||
| @@ -22,7 +22,7 @@ | |||
| 22 | }, | 22 | }, |
| 23 | "bpf-examples": { | 23 | "bpf-examples": { |
| 24 | "cargoLocks": null, | 24 | "cargoLocks": null, |
| 25 | "date": "2025-03-06", | 25 | "date": "2025-09-19", |
| 26 | "extract": null, | 26 | "extract": null, |
| 27 | "name": "bpf-examples", | 27 | "name": "bpf-examples", |
| 28 | "passthru": null, | 28 | "passthru": null, |
| @@ -34,12 +34,12 @@ | |||
| 34 | "name": null, | 34 | "name": null, |
| 35 | "owner": "xdp-project", | 35 | "owner": "xdp-project", |
| 36 | "repo": "bpf-examples", | 36 | "repo": "bpf-examples", |
| 37 | "rev": "64e7da048b14822bef06f3971189c4c0985422e7", | 37 | "rev": "d621b4fb25c4877415a563887606ab0fe47ad59a", |
| 38 | "sha256": "sha256-cyyRNvU35ujxkLraOqw2oiZwUblBpJaEncPl2++VHL4=", | 38 | "sha256": "sha256-IQBTYtqHsghbb/Mpx29Hjr9AsLVG6w3BqfJYSKoMotU=", |
| 39 | "sparseCheckout": [], | 39 | "sparseCheckout": [], |
| 40 | "type": "github" | 40 | "type": "github" |
| 41 | }, | 41 | }, |
| 42 | "version": "64e7da048b14822bef06f3971189c4c0985422e7" | 42 | "version": "d621b4fb25c4877415a563887606ab0fe47ad59a" |
| 43 | }, | 43 | }, |
| 44 | "emacs-scratch_el": { | 44 | "emacs-scratch_el": { |
| 45 | "cargoLocks": null, | 45 | "cargoLocks": null, |
| @@ -76,12 +76,12 @@ | |||
| 76 | "name": null, | 76 | "name": null, |
| 77 | "owner": "Mange", | 77 | "owner": "Mange", |
| 78 | "repo": "emoji-data", | 78 | "repo": "emoji-data", |
| 79 | "rev": "v2.6", | 79 | "rev": "v2.7", |
| 80 | "sha256": "sha256-6nBiT9q139P1pXLqkV1JejE0s2rZn1gUbNsejXJR6RU=", | 80 | "sha256": "sha256-bUFh0Q7xcnKTBgVBUJU8BH6zzq1Y3krLfJJAgx5TqKs=", |
| 81 | "sparseCheckout": [], | 81 | "sparseCheckout": [], |
| 82 | "type": "github" | 82 | "type": "github" |
| 83 | }, | 83 | }, |
| 84 | "version": "v2.6" | 84 | "version": "v2.7" |
| 85 | }, | 85 | }, |
| 86 | "lesspipe": { | 86 | "lesspipe": { |
| 87 | "cargoLocks": null, | 87 | "cargoLocks": null, |
| @@ -91,15 +91,15 @@ | |||
| 91 | "passthru": null, | 91 | "passthru": null, |
| 92 | "pinned": false, | 92 | "pinned": false, |
| 93 | "src": { | 93 | "src": { |
| 94 | "sha256": "sha256-afJuTByGUMU6kFqGGa3pbPaFVdYGcJYiR0RfDNYNgDk=", | 94 | "sha256": "sha256-yb3IzdaMiv1PwqHOfSyHvmWXyStvK/XXC49saXVAJFU=", |
| 95 | "type": "tarball", | 95 | "type": "tarball", |
| 96 | "url": "https://github.com/wofr06/lesspipe/archive/refs/tags/v2.17.tar.gz" | 96 | "url": "https://github.com/wofr06/lesspipe/archive/refs/tags/v2.20.tar.gz" |
| 97 | }, | 97 | }, |
| 98 | "version": "2.17" | 98 | "version": "2.20" |
| 99 | }, | 99 | }, |
| 100 | "mako": { | 100 | "mako": { |
| 101 | "cargoLocks": null, | 101 | "cargoLocks": null, |
| 102 | "date": "2025-03-21", | 102 | "date": "2025-09-11", |
| 103 | "extract": null, | 103 | "extract": null, |
| 104 | "name": "mako", | 104 | "name": "mako", |
| 105 | "passthru": null, | 105 | "passthru": null, |
| @@ -109,13 +109,13 @@ | |||
| 109 | "fetchSubmodules": false, | 109 | "fetchSubmodules": false, |
| 110 | "leaveDotGit": false, | 110 | "leaveDotGit": false, |
| 111 | "name": null, | 111 | "name": null, |
| 112 | "rev": "2a06a341efae601431a6944f94cfe4965a46cb44", | 112 | "rev": "8318972590420c042c0177af16e26a1768550fab", |
| 113 | "sha256": "sha256-QH2rxJcNiurHFEtkmq6Ki15k11b6ft6WYiZKbGs7SS0=", | 113 | "sha256": "sha256-Y/exF/Pv60E31Zl+M1zboWkmkZgOUCA3l93OKbtvZ+g=", |
| 114 | "sparseCheckout": [], | 114 | "sparseCheckout": [], |
| 115 | "type": "git", | 115 | "type": "git", |
| 116 | "url": "https://github.com/emersion/mako" | 116 | "url": "https://github.com/emersion/mako" |
| 117 | }, | 117 | }, |
| 118 | "version": "2a06a341efae601431a6944f94cfe4965a46cb44" | 118 | "version": "8318972590420c042c0177af16e26a1768550fab" |
| 119 | }, | 119 | }, |
| 120 | "mpv-autosave": { | 120 | "mpv-autosave": { |
| 121 | "cargoLocks": null, | 121 | "cargoLocks": null, |
| @@ -223,7 +223,7 @@ | |||
| 223 | }, | 223 | }, |
| 224 | "mpv-subselect": { | 224 | "mpv-subselect": { |
| 225 | "cargoLocks": null, | 225 | "cargoLocks": null, |
| 226 | "date": "2024-12-22", | 226 | "date": "2025-04-04", |
| 227 | "extract": null, | 227 | "extract": null, |
| 228 | "name": "mpv-subselect", | 228 | "name": "mpv-subselect", |
| 229 | "passthru": null, | 229 | "passthru": null, |
| @@ -233,13 +233,13 @@ | |||
| 233 | "fetchSubmodules": false, | 233 | "fetchSubmodules": false, |
| 234 | "leaveDotGit": false, | 234 | "leaveDotGit": false, |
| 235 | "name": null, | 235 | "name": null, |
| 236 | "rev": "77d0148aa6aa952f07f06212cabe32d54dfdf49e", | 236 | "rev": "26d24a0fd1d69988eaedda6056a2c87d0a55b6cb", |
| 237 | "sha256": "sha256-VxwwTxE8c8rRQt/m2NA7cRC7+7O1ItYFFGv81nxqIxg=", | 237 | "sha256": "sha256-+eVga4b7KIBnfrtmlgq/0HNjQVS3SK6YWVXCPvOeOOc=", |
| 238 | "sparseCheckout": [], | 238 | "sparseCheckout": [], |
| 239 | "type": "git", | 239 | "type": "git", |
| 240 | "url": "https://github.com/CogentRedTester/mpv-sub-select" | 240 | "url": "https://github.com/CogentRedTester/mpv-sub-select" |
| 241 | }, | 241 | }, |
| 242 | "version": "77d0148aa6aa952f07f06212cabe32d54dfdf49e" | 242 | "version": "26d24a0fd1d69988eaedda6056a2c87d0a55b6cb" |
| 243 | }, | 243 | }, |
| 244 | "mpv-youtube-quality": { | 244 | "mpv-youtube-quality": { |
| 245 | "cargoLocks": null, | 245 | "cargoLocks": null, |
| @@ -261,6 +261,36 @@ | |||
| 261 | }, | 261 | }, |
| 262 | "version": "1f8c31457459ffc28cd1c3f3c2235a53efad7148" | 262 | "version": "1f8c31457459ffc28cd1c3f3c2235a53efad7148" |
| 263 | }, | 263 | }, |
| 264 | "netbootxyz-efi": { | ||
| 265 | "cargoLocks": null, | ||
| 266 | "date": null, | ||
| 267 | "extract": null, | ||
| 268 | "name": "netbootxyz-efi", | ||
| 269 | "passthru": null, | ||
| 270 | "pinned": false, | ||
| 271 | "src": { | ||
| 272 | "name": null, | ||
| 273 | "sha256": "sha256-ipbZJ0mPCuwzb/TDtXXUBTuWOcSsKGAJ1GEGIgB2G7E=", | ||
| 274 | "type": "url", | ||
| 275 | "url": "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.88/netboot.xyz.efi" | ||
| 276 | }, | ||
| 277 | "version": "2.0.88" | ||
| 278 | }, | ||
| 279 | "netbootxyz-lkrn": { | ||
| 280 | "cargoLocks": null, | ||
| 281 | "date": null, | ||
| 282 | "extract": null, | ||
| 283 | "name": "netbootxyz-lkrn", | ||
| 284 | "passthru": null, | ||
| 285 | "pinned": false, | ||
| 286 | "src": { | ||
| 287 | "name": null, | ||
| 288 | "sha256": "sha256-igy3O30noS25dU7ZnHuKrWqLLkjjd/L46IdCTd038dI=", | ||
| 289 | "type": "url", | ||
| 290 | "url": "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.88/netboot.xyz.lkrn" | ||
| 291 | }, | ||
| 292 | "version": "2.0.88" | ||
| 293 | }, | ||
| 264 | "postfix-mta-sts-resolver": { | 294 | "postfix-mta-sts-resolver": { |
| 265 | "cargoLocks": null, | 295 | "cargoLocks": null, |
| 266 | "date": null, | 296 | "date": null, |
| @@ -297,11 +327,11 @@ | |||
| 297 | "passthru": null, | 327 | "passthru": null, |
| 298 | "pinned": false, | 328 | "pinned": false, |
| 299 | "src": { | 329 | "src": { |
| 300 | "sha256": "sha256-Nz/lBhQbzWSnOKN4n0OUdJzDTpf3mfY0+FXoCqF03TU=", | 330 | "sha256": "sha256-mg4iyp/heYzSoK+pGSMYfZb5UauoBMrEL1QPH6EoJ8o=", |
| 301 | "type": "tarball", | 331 | "type": "tarball", |
| 302 | "url": "https://github.com/hansmi/prometheus-lvm-exporter/archive/refs/tags/v0.4.0.tar.gz" | 332 | "url": "https://github.com/hansmi/prometheus-lvm-exporter/archive/refs/tags/v0.6.1.tar.gz" |
| 303 | }, | 333 | }, |
| 304 | "version": "0.4.0" | 334 | "version": "0.6.1" |
| 305 | }, | 335 | }, |
| 306 | "psql-versioning": { | 336 | "psql-versioning": { |
| 307 | "cargoLocks": null, | 337 | "cargoLocks": null, |
| @@ -323,6 +353,26 @@ | |||
| 323 | }, | 353 | }, |
| 324 | "version": "330cb9da36651b701085ad53ae75ff296d02202a" | 354 | "version": "330cb9da36651b701085ad53ae75ff296d02202a" |
| 325 | }, | 355 | }, |
| 356 | "quickshell": { | ||
| 357 | "cargoLocks": null, | ||
| 358 | "date": "2025-09-19", | ||
| 359 | "extract": null, | ||
| 360 | "name": "quickshell", | ||
| 361 | "passthru": null, | ||
| 362 | "pinned": false, | ||
| 363 | "src": { | ||
| 364 | "deepClone": false, | ||
| 365 | "fetchSubmodules": false, | ||
| 366 | "leaveDotGit": false, | ||
| 367 | "name": null, | ||
| 368 | "rev": "e9a574d919a89602d2868621576b2ccae54a5cb0", | ||
| 369 | "sha256": "sha256-wOv1guIi9THD1NjOtBU2Xh/Avg9xv7nIjsfFSkr1NeQ=", | ||
| 370 | "sparseCheckout": [], | ||
| 371 | "type": "git", | ||
| 372 | "url": "https://git.outfoxxed.me/quickshell/quickshell.git" | ||
| 373 | }, | ||
| 374 | "version": "e9a574d919a89602d2868621576b2ccae54a5cb0" | ||
| 375 | }, | ||
| 326 | "scutiger": { | 376 | "scutiger": { |
| 327 | "cargoLocks": null, | 377 | "cargoLocks": null, |
| 328 | "date": null, | 378 | "date": null, |
| @@ -367,7 +417,7 @@ | |||
| 367 | }, | 417 | }, |
| 368 | "swayosd": { | 418 | "swayosd": { |
| 369 | "cargoLocks": null, | 419 | "cargoLocks": null, |
| 370 | "date": "2025-03-03", | 420 | "date": "2025-07-07", |
| 371 | "extract": null, | 421 | "extract": null, |
| 372 | "name": "swayosd", | 422 | "name": "swayosd", |
| 373 | "passthru": null, | 423 | "passthru": null, |
| @@ -377,13 +427,13 @@ | |||
| 377 | "fetchSubmodules": false, | 427 | "fetchSubmodules": false, |
| 378 | "leaveDotGit": false, | 428 | "leaveDotGit": false, |
| 379 | "name": null, | 429 | "name": null, |
| 380 | "rev": "b3c78fce3d90be2ce6a6ffee0e22a50379952e2b", | 430 | "rev": "73aed75146b81aaf67c4301353790ff5a17aed1f", |
| 381 | "sha256": "sha256-V3V18BoBRJU8mtvwWXvdYPbKBDIHdu5LzVSkDkGJjFU=", | 431 | "sha256": "sha256-p31HNelptAw7Sk0NmYP4FkoUCdA5uAsrXC20JJp24Vw=", |
| 382 | "sparseCheckout": [], | 432 | "sparseCheckout": [], |
| 383 | "type": "git", | 433 | "type": "git", |
| 384 | "url": "https://github.com/ErikReider/SwayOSD" | 434 | "url": "https://github.com/ErikReider/SwayOSD" |
| 385 | }, | 435 | }, |
| 386 | "version": "b3c78fce3d90be2ce6a6ffee0e22a50379952e2b" | 436 | "version": "73aed75146b81aaf67c4301353790ff5a17aed1f" |
| 387 | }, | 437 | }, |
| 388 | "tomorrow-night-paradise-theme": { | 438 | "tomorrow-night-paradise-theme": { |
| 389 | "cargoLocks": null, | 439 | "cargoLocks": null, |
| @@ -407,7 +457,7 @@ | |||
| 407 | }, | 457 | }, |
| 408 | "v4l2loopback": { | 458 | "v4l2loopback": { |
| 409 | "cargoLocks": null, | 459 | "cargoLocks": null, |
| 410 | "date": "2025-03-24", | 460 | "date": "2025-08-18", |
| 411 | "extract": null, | 461 | "extract": null, |
| 412 | "name": "v4l2loopback", | 462 | "name": "v4l2loopback", |
| 413 | "passthru": null, | 463 | "passthru": null, |
| @@ -419,16 +469,16 @@ | |||
| 419 | "name": null, | 469 | "name": null, |
| 420 | "owner": "umlaeute", | 470 | "owner": "umlaeute", |
| 421 | "repo": "v4l2loopback", | 471 | "repo": "v4l2loopback", |
| 422 | "rev": "84cccedd9d3979f0a8ec5478b100ea57adf29696", | 472 | "rev": "5eaa59e7c41d0e6f35a6c14c3b756d94d25f58ed", |
| 423 | "sha256": "sha256-o31+j5OdMV0e5GvyoGSR1RK6GjTwz9sfhfUK+vbW+b4=", | 473 | "sha256": "sha256-YcSpNfItvUdPVirlDyGdYuCnVvxHhh780x+OI5VNZmE=", |
| 424 | "sparseCheckout": [], | 474 | "sparseCheckout": [], |
| 425 | "type": "github" | 475 | "type": "github" |
| 426 | }, | 476 | }, |
| 427 | "version": "84cccedd9d3979f0a8ec5478b100ea57adf29696" | 477 | "version": "5eaa59e7c41d0e6f35a6c14c3b756d94d25f58ed" |
| 428 | }, | 478 | }, |
| 429 | "xcompose": { | 479 | "xcompose": { |
| 430 | "cargoLocks": null, | 480 | "cargoLocks": null, |
| 431 | "date": "2025-03-11", | 481 | "date": "2025-06-05", |
| 432 | "extract": null, | 482 | "extract": null, |
| 433 | "name": "xcompose", | 483 | "name": "xcompose", |
| 434 | "passthru": null, | 484 | "passthru": null, |
| @@ -440,12 +490,12 @@ | |||
| 440 | "name": null, | 490 | "name": null, |
| 441 | "owner": "kragen", | 491 | "owner": "kragen", |
| 442 | "repo": "xcompose", | 492 | "repo": "xcompose", |
| 443 | "rev": "8b5a6a0c788fd0a4b921d9d3737174defb863873", | 493 | "rev": "4d8eab4d05a19537ce79294ae0459fdae78ffb20", |
| 444 | "sha256": "sha256-6EjQErdBOd5hqcrdaf88E1UZVYIc3FOfv34hvUwOWdA=", | 494 | "sha256": "sha256-vKY4u5Z2IL111orLLkkF4AoVzqluKG/VQhNUUCqO/k8=", |
| 445 | "sparseCheckout": [], | 495 | "sparseCheckout": [], |
| 446 | "type": "github" | 496 | "type": "github" |
| 447 | }, | 497 | }, |
| 448 | "version": "8b5a6a0c788fd0a4b921d9d3737174defb863873" | 498 | "version": "4d8eab4d05a19537ce79294ae0459fdae78ffb20" |
| 449 | }, | 499 | }, |
| 450 | "yt-dlp": { | 500 | "yt-dlp": { |
| 451 | "cargoLocks": null, | 501 | "cargoLocks": null, |
| @@ -456,10 +506,10 @@ | |||
| 456 | "pinned": false, | 506 | "pinned": false, |
| 457 | "src": { | 507 | "src": { |
| 458 | "name": null, | 508 | "name": null, |
| 459 | "sha256": "sha256-G/4OZg0acKCeJ7LVj5LjCx4uNizEh4KfL4JDRq5J+5E=", | 509 | "sha256": "sha256-koKtHerbTJCy5tO8+fNgq/iMXy5LqDba17UTh7CG11c=", |
| 460 | "type": "url", | 510 | "type": "url", |
| 461 | "url": "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2025.3.31.tar.gz" | 511 | "url": "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2025.9.23.tar.gz" |
| 462 | }, | 512 | }, |
| 463 | "version": "2025.3.31" | 513 | "version": "2025.9.23" |
| 464 | } | 514 | } |
| 465 | } \ No newline at end of file | 515 | } \ No newline at end of file |
diff --git a/_sources/generated.nix b/_sources/generated.nix index ddcbbf10..8eac064b 100644 --- a/_sources/generated.nix +++ b/_sources/generated.nix | |||
| @@ -18,15 +18,15 @@ | |||
| 18 | }; | 18 | }; |
| 19 | bpf-examples = { | 19 | bpf-examples = { |
| 20 | pname = "bpf-examples"; | 20 | pname = "bpf-examples"; |
| 21 | version = "64e7da048b14822bef06f3971189c4c0985422e7"; | 21 | version = "d621b4fb25c4877415a563887606ab0fe47ad59a"; |
| 22 | src = fetchFromGitHub { | 22 | src = fetchFromGitHub { |
| 23 | owner = "xdp-project"; | 23 | owner = "xdp-project"; |
| 24 | repo = "bpf-examples"; | 24 | repo = "bpf-examples"; |
| 25 | rev = "64e7da048b14822bef06f3971189c4c0985422e7"; | 25 | rev = "d621b4fb25c4877415a563887606ab0fe47ad59a"; |
| 26 | fetchSubmodules = true; | 26 | fetchSubmodules = true; |
| 27 | sha256 = "sha256-cyyRNvU35ujxkLraOqw2oiZwUblBpJaEncPl2++VHL4="; | 27 | sha256 = "sha256-IQBTYtqHsghbb/Mpx29Hjr9AsLVG6w3BqfJYSKoMotU="; |
| 28 | }; | 28 | }; |
| 29 | date = "2025-03-06"; | 29 | date = "2025-09-19"; |
| 30 | }; | 30 | }; |
| 31 | emacs-scratch_el = { | 31 | emacs-scratch_el = { |
| 32 | pname = "emacs-scratch_el"; | 32 | pname = "emacs-scratch_el"; |
| @@ -42,36 +42,36 @@ | |||
| 42 | }; | 42 | }; |
| 43 | emoji-data = { | 43 | emoji-data = { |
| 44 | pname = "emoji-data"; | 44 | pname = "emoji-data"; |
| 45 | version = "v2.6"; | 45 | version = "v2.7"; |
| 46 | src = fetchFromGitHub { | 46 | src = fetchFromGitHub { |
| 47 | owner = "Mange"; | 47 | owner = "Mange"; |
| 48 | repo = "emoji-data"; | 48 | repo = "emoji-data"; |
| 49 | rev = "v2.6"; | 49 | rev = "v2.7"; |
| 50 | fetchSubmodules = true; | 50 | fetchSubmodules = true; |
| 51 | sha256 = "sha256-6nBiT9q139P1pXLqkV1JejE0s2rZn1gUbNsejXJR6RU="; | 51 | sha256 = "sha256-bUFh0Q7xcnKTBgVBUJU8BH6zzq1Y3krLfJJAgx5TqKs="; |
| 52 | }; | 52 | }; |
| 53 | }; | 53 | }; |
| 54 | lesspipe = { | 54 | lesspipe = { |
| 55 | pname = "lesspipe"; | 55 | pname = "lesspipe"; |
| 56 | version = "2.17"; | 56 | version = "2.20"; |
| 57 | src = fetchTarball { | 57 | src = fetchTarball { |
| 58 | url = "https://github.com/wofr06/lesspipe/archive/refs/tags/v2.17.tar.gz"; | 58 | url = "https://github.com/wofr06/lesspipe/archive/refs/tags/v2.20.tar.gz"; |
| 59 | sha256 = "sha256-afJuTByGUMU6kFqGGa3pbPaFVdYGcJYiR0RfDNYNgDk="; | 59 | sha256 = "sha256-yb3IzdaMiv1PwqHOfSyHvmWXyStvK/XXC49saXVAJFU="; |
| 60 | }; | 60 | }; |
| 61 | }; | 61 | }; |
| 62 | mako = { | 62 | mako = { |
| 63 | pname = "mako"; | 63 | pname = "mako"; |
| 64 | version = "2a06a341efae601431a6944f94cfe4965a46cb44"; | 64 | version = "8318972590420c042c0177af16e26a1768550fab"; |
| 65 | src = fetchgit { | 65 | src = fetchgit { |
| 66 | url = "https://github.com/emersion/mako"; | 66 | url = "https://github.com/emersion/mako"; |
| 67 | rev = "2a06a341efae601431a6944f94cfe4965a46cb44"; | 67 | rev = "8318972590420c042c0177af16e26a1768550fab"; |
| 68 | fetchSubmodules = false; | 68 | fetchSubmodules = false; |
| 69 | deepClone = false; | 69 | deepClone = false; |
| 70 | leaveDotGit = false; | 70 | leaveDotGit = false; |
| 71 | sparseCheckout = [ ]; | 71 | sparseCheckout = [ ]; |
| 72 | sha256 = "sha256-QH2rxJcNiurHFEtkmq6Ki15k11b6ft6WYiZKbGs7SS0="; | 72 | sha256 = "sha256-Y/exF/Pv60E31Zl+M1zboWkmkZgOUCA3l93OKbtvZ+g="; |
| 73 | }; | 73 | }; |
| 74 | date = "2025-03-21"; | 74 | date = "2025-09-11"; |
| 75 | }; | 75 | }; |
| 76 | mpv-autosave = { | 76 | mpv-autosave = { |
| 77 | pname = "mpv-autosave"; | 77 | pname = "mpv-autosave"; |
| @@ -136,17 +136,17 @@ | |||
| 136 | }; | 136 | }; |
| 137 | mpv-subselect = { | 137 | mpv-subselect = { |
| 138 | pname = "mpv-subselect"; | 138 | pname = "mpv-subselect"; |
| 139 | version = "77d0148aa6aa952f07f06212cabe32d54dfdf49e"; | 139 | version = "26d24a0fd1d69988eaedda6056a2c87d0a55b6cb"; |
| 140 | src = fetchgit { | 140 | src = fetchgit { |
| 141 | url = "https://github.com/CogentRedTester/mpv-sub-select"; | 141 | url = "https://github.com/CogentRedTester/mpv-sub-select"; |
| 142 | rev = "77d0148aa6aa952f07f06212cabe32d54dfdf49e"; | 142 | rev = "26d24a0fd1d69988eaedda6056a2c87d0a55b6cb"; |
| 143 | fetchSubmodules = false; | 143 | fetchSubmodules = false; |
| 144 | deepClone = false; | 144 | deepClone = false; |
| 145 | leaveDotGit = false; | 145 | leaveDotGit = false; |
| 146 | sparseCheckout = [ ]; | 146 | sparseCheckout = [ ]; |
| 147 | sha256 = "sha256-VxwwTxE8c8rRQt/m2NA7cRC7+7O1ItYFFGv81nxqIxg="; | 147 | sha256 = "sha256-+eVga4b7KIBnfrtmlgq/0HNjQVS3SK6YWVXCPvOeOOc="; |
| 148 | }; | 148 | }; |
| 149 | date = "2024-12-22"; | 149 | date = "2025-04-04"; |
| 150 | }; | 150 | }; |
| 151 | mpv-youtube-quality = { | 151 | mpv-youtube-quality = { |
| 152 | pname = "mpv-youtube-quality"; | 152 | pname = "mpv-youtube-quality"; |
| @@ -162,6 +162,22 @@ | |||
| 162 | }; | 162 | }; |
| 163 | date = "2020-02-10"; | 163 | date = "2020-02-10"; |
| 164 | }; | 164 | }; |
| 165 | netbootxyz-efi = { | ||
| 166 | pname = "netbootxyz-efi"; | ||
| 167 | version = "2.0.88"; | ||
| 168 | src = fetchurl { | ||
| 169 | url = "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.88/netboot.xyz.efi"; | ||
| 170 | sha256 = "sha256-ipbZJ0mPCuwzb/TDtXXUBTuWOcSsKGAJ1GEGIgB2G7E="; | ||
| 171 | }; | ||
| 172 | }; | ||
| 173 | netbootxyz-lkrn = { | ||
| 174 | pname = "netbootxyz-lkrn"; | ||
| 175 | version = "2.0.88"; | ||
| 176 | src = fetchurl { | ||
| 177 | url = "https://github.com/netbootxyz/netboot.xyz/releases/download/2.0.88/netboot.xyz.lkrn"; | ||
| 178 | sha256 = "sha256-igy3O30noS25dU7ZnHuKrWqLLkjjd/L46IdCTd038dI="; | ||
| 179 | }; | ||
| 180 | }; | ||
| 165 | postfix-mta-sts-resolver = { | 181 | postfix-mta-sts-resolver = { |
| 166 | pname = "postfix-mta-sts-resolver"; | 182 | pname = "postfix-mta-sts-resolver"; |
| 167 | version = "1.5.0"; | 183 | version = "1.5.0"; |
| @@ -180,10 +196,10 @@ | |||
| 180 | }; | 196 | }; |
| 181 | prometheus-lvm-exporter = { | 197 | prometheus-lvm-exporter = { |
| 182 | pname = "prometheus-lvm-exporter"; | 198 | pname = "prometheus-lvm-exporter"; |
| 183 | version = "0.4.0"; | 199 | version = "0.6.1"; |
| 184 | src = fetchTarball { | 200 | src = fetchTarball { |
| 185 | url = "https://github.com/hansmi/prometheus-lvm-exporter/archive/refs/tags/v0.4.0.tar.gz"; | 201 | url = "https://github.com/hansmi/prometheus-lvm-exporter/archive/refs/tags/v0.6.1.tar.gz"; |
| 186 | sha256 = "sha256-Nz/lBhQbzWSnOKN4n0OUdJzDTpf3mfY0+FXoCqF03TU="; | 202 | sha256 = "sha256-mg4iyp/heYzSoK+pGSMYfZb5UauoBMrEL1QPH6EoJ8o="; |
| 187 | }; | 203 | }; |
| 188 | }; | 204 | }; |
| 189 | psql-versioning = { | 205 | psql-versioning = { |
| @@ -200,6 +216,20 @@ | |||
| 200 | }; | 216 | }; |
| 201 | date = "2023-11-23"; | 217 | date = "2023-11-23"; |
| 202 | }; | 218 | }; |
| 219 | quickshell = { | ||
| 220 | pname = "quickshell"; | ||
| 221 | version = "e9a574d919a89602d2868621576b2ccae54a5cb0"; | ||
| 222 | src = fetchgit { | ||
| 223 | url = "https://git.outfoxxed.me/quickshell/quickshell.git"; | ||
| 224 | rev = "e9a574d919a89602d2868621576b2ccae54a5cb0"; | ||
| 225 | fetchSubmodules = false; | ||
| 226 | deepClone = false; | ||
| 227 | leaveDotGit = false; | ||
| 228 | sparseCheckout = [ ]; | ||
| 229 | sha256 = "sha256-wOv1guIi9THD1NjOtBU2Xh/Avg9xv7nIjsfFSkr1NeQ="; | ||
| 230 | }; | ||
| 231 | date = "2025-09-19"; | ||
| 232 | }; | ||
| 203 | scutiger = { | 233 | scutiger = { |
| 204 | pname = "scutiger"; | 234 | pname = "scutiger"; |
| 205 | version = "0.2.0"; | 235 | version = "0.2.0"; |
| @@ -226,17 +256,17 @@ | |||
| 226 | }; | 256 | }; |
| 227 | swayosd = { | 257 | swayosd = { |
| 228 | pname = "swayosd"; | 258 | pname = "swayosd"; |
| 229 | version = "b3c78fce3d90be2ce6a6ffee0e22a50379952e2b"; | 259 | version = "73aed75146b81aaf67c4301353790ff5a17aed1f"; |
| 230 | src = fetchgit { | 260 | src = fetchgit { |
| 231 | url = "https://github.com/ErikReider/SwayOSD"; | 261 | url = "https://github.com/ErikReider/SwayOSD"; |
| 232 | rev = "b3c78fce3d90be2ce6a6ffee0e22a50379952e2b"; | 262 | rev = "73aed75146b81aaf67c4301353790ff5a17aed1f"; |
| 233 | fetchSubmodules = false; | 263 | fetchSubmodules = false; |
| 234 | deepClone = false; | 264 | deepClone = false; |
| 235 | leaveDotGit = false; | 265 | leaveDotGit = false; |
| 236 | sparseCheckout = [ ]; | 266 | sparseCheckout = [ ]; |
| 237 | sha256 = "sha256-V3V18BoBRJU8mtvwWXvdYPbKBDIHdu5LzVSkDkGJjFU="; | 267 | sha256 = "sha256-p31HNelptAw7Sk0NmYP4FkoUCdA5uAsrXC20JJp24Vw="; |
| 238 | }; | 268 | }; |
| 239 | date = "2025-03-03"; | 269 | date = "2025-07-07"; |
| 240 | }; | 270 | }; |
| 241 | tomorrow-night-paradise-theme = { | 271 | tomorrow-night-paradise-theme = { |
| 242 | pname = "tomorrow-night-paradise-theme"; | 272 | pname = "tomorrow-night-paradise-theme"; |
| @@ -254,34 +284,34 @@ | |||
| 254 | }; | 284 | }; |
| 255 | v4l2loopback = { | 285 | v4l2loopback = { |
| 256 | pname = "v4l2loopback"; | 286 | pname = "v4l2loopback"; |
| 257 | version = "84cccedd9d3979f0a8ec5478b100ea57adf29696"; | 287 | version = "5eaa59e7c41d0e6f35a6c14c3b756d94d25f58ed"; |
| 258 | src = fetchFromGitHub { | 288 | src = fetchFromGitHub { |
| 259 | owner = "umlaeute"; | 289 | owner = "umlaeute"; |
| 260 | repo = "v4l2loopback"; | 290 | repo = "v4l2loopback"; |
| 261 | rev = "84cccedd9d3979f0a8ec5478b100ea57adf29696"; | 291 | rev = "5eaa59e7c41d0e6f35a6c14c3b756d94d25f58ed"; |
| 262 | fetchSubmodules = true; | 292 | fetchSubmodules = true; |
| 263 | sha256 = "sha256-o31+j5OdMV0e5GvyoGSR1RK6GjTwz9sfhfUK+vbW+b4="; | 293 | sha256 = "sha256-YcSpNfItvUdPVirlDyGdYuCnVvxHhh780x+OI5VNZmE="; |
| 264 | }; | 294 | }; |
| 265 | date = "2025-03-24"; | 295 | date = "2025-08-18"; |
| 266 | }; | 296 | }; |
| 267 | xcompose = { | 297 | xcompose = { |
| 268 | pname = "xcompose"; | 298 | pname = "xcompose"; |
| 269 | version = "8b5a6a0c788fd0a4b921d9d3737174defb863873"; | 299 | version = "4d8eab4d05a19537ce79294ae0459fdae78ffb20"; |
| 270 | src = fetchFromGitHub { | 300 | src = fetchFromGitHub { |
| 271 | owner = "kragen"; | 301 | owner = "kragen"; |
| 272 | repo = "xcompose"; | 302 | repo = "xcompose"; |
| 273 | rev = "8b5a6a0c788fd0a4b921d9d3737174defb863873"; | 303 | rev = "4d8eab4d05a19537ce79294ae0459fdae78ffb20"; |
| 274 | fetchSubmodules = false; | 304 | fetchSubmodules = false; |
| 275 | sha256 = "sha256-6EjQErdBOd5hqcrdaf88E1UZVYIc3FOfv34hvUwOWdA="; | 305 | sha256 = "sha256-vKY4u5Z2IL111orLLkkF4AoVzqluKG/VQhNUUCqO/k8="; |
| 276 | }; | 306 | }; |
| 277 | date = "2025-03-11"; | 307 | date = "2025-06-05"; |
| 278 | }; | 308 | }; |
| 279 | yt-dlp = { | 309 | yt-dlp = { |
| 280 | pname = "yt-dlp"; | 310 | pname = "yt-dlp"; |
| 281 | version = "2025.3.31"; | 311 | version = "2025.9.23"; |
| 282 | src = fetchurl { | 312 | src = fetchurl { |
| 283 | url = "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2025.3.31.tar.gz"; | 313 | url = "https://pypi.org/packages/source/y/yt_dlp/yt_dlp-2025.9.23.tar.gz"; |
| 284 | sha256 = "sha256-G/4OZg0acKCeJ7LVj5LjCx4uNizEh4KfL4JDRq5J+5E="; | 314 | sha256 = "sha256-koKtHerbTJCy5tO8+fNgq/iMXy5LqDba17UTh7CG11c="; |
| 285 | }; | 315 | }; |
| 286 | }; | 316 | }; |
| 287 | } | 317 | } |
diff --git a/accounts/gkleen@eostre.nix b/accounts/gkleen@eostre.nix index 72818d44..28daf3fd 100644 --- a/accounts/gkleen@eostre.nix +++ b/accounts/gkleen@eostre.nix | |||
| @@ -1,16 +1,16 @@ | |||
| 1 | { flake, userName, pkgs, ... }: | 1 | { flake, userName, pkgs, ... }: |
| 2 | { | 2 | { |
| 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 4 | zsh utils tmux | 4 | utils |
| 5 | ]; | 5 | ]; |
| 6 | 6 | ||
| 7 | config = { | 7 | config = { |
| 8 | home-manager.users.${userName} = { | 8 | home-manager.users.${userName} = { |
| 9 | home.stateVersion = "20.09"; | 9 | home.stateVersion = "20.09"; |
| 10 | 10 | ||
| 11 | nixpkgs.config = { | 11 | # nixpkgs.config = { |
| 12 | allowUnfree = true; | 12 | # allowUnfree = true; |
| 13 | }; | 13 | # }; |
| 14 | 14 | ||
| 15 | home.packages = with pkgs; [ | 15 | home.packages = with pkgs; [ |
| 16 | thunderbird libreoffice element-desktop keepassxc vlc | 16 | thunderbird libreoffice element-desktop keepassxc vlc |
diff --git a/accounts/gkleen@installer.nix b/accounts/gkleen@installer.nix index c7a418f8..5fe1db38 100644 --- a/accounts/gkleen@installer.nix +++ b/accounts/gkleen@installer.nix | |||
| @@ -1,7 +1,11 @@ | |||
| 1 | { userName, ... }: | 1 | { flake, userName, ... }: |
| 2 | 2 | ||
| 3 | { | 3 | { |
| 4 | home-manager.users.${userName} = { config, ... } : { | 4 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 5 | zsh tmux | ||
| 6 | ]; | ||
| 7 | |||
| 8 | config.home-manager.users.${userName} = { config, ... } : { | ||
| 5 | home.stateVersion = config.home.version.release; | 9 | home.stateVersion = config.home.version.release; |
| 6 | }; | 10 | }; |
| 7 | } | 11 | } |
diff --git a/accounts/gkleen@sif/default.nix b/accounts/gkleen@sif/default.nix index 11f24f31..36b722e4 100644 --- a/accounts/gkleen@sif/default.nix +++ b/accounts/gkleen@sif/default.nix | |||
| @@ -49,10 +49,22 @@ let | |||
| 49 | ]; | 49 | ]; |
| 50 | }; | 50 | }; |
| 51 | 51 | ||
| 52 | lockCommand = "${lib.getExe' config.systemd.package "systemctl"} --user start gtklock.service"; | 52 | # lockCommand = "${lib.getExe' config.systemd.package "systemctl"} --user start gtklock.service"; |
| 53 | lockCommand = "${lib.getExe' cfg.programs.quickshell.package "qs"} ipc call Lockscreen setLocked true"; | ||
| 54 | |||
| 55 | editor = pkgs.symlinkJoin { | ||
| 56 | inherit (cfg.services.emacs.package) name; | ||
| 57 | buildInputs = with pkgs; [ makeWrapper ]; | ||
| 58 | paths = [ cfg.services.emacs.package ]; | ||
| 59 | postBuild = '' | ||
| 60 | wrapProgram $out/bin/emacsclient \ | ||
| 61 | --inherit-argv0 \ | ||
| 62 | --add-flags ${lib.escapeShellArg (lib.escapeShellArgs cfg.services.emacs.client.arguments)} | ||
| 63 | ''; | ||
| 64 | }; | ||
| 53 | in { | 65 | in { |
| 54 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 66 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 55 | mpv yt-dlp (args: import ./xcompose.nix (inputs // args)) | 67 | zsh tmux mpv yt-dlp (args: import ./xcompose.nix (inputs // args)) |
| 56 | ]; | 68 | ]; |
| 57 | 69 | ||
| 58 | config = { | 70 | config = { |
| @@ -60,16 +72,18 @@ in { | |||
| 60 | imports = [ | 72 | imports = [ |
| 61 | ./libvirt | 73 | ./libvirt |
| 62 | ./niri | 74 | ./niri |
| 63 | flakeInputs.nix-index-database.hmModules.nix-index | 75 | ./shell |
| 76 | ./synadm | ||
| 77 | flakeInputs.nix-index-database.homeModules.nix-index | ||
| 64 | flakeInputs.impermanence.nixosModules.home-manager.impermanence | 78 | flakeInputs.impermanence.nixosModules.home-manager.impermanence |
| 65 | ]; | 79 | ]; |
| 66 | 80 | ||
| 67 | home.stateVersion = "20.09"; | 81 | home.stateVersion = "20.09"; |
| 68 | 82 | ||
| 69 | nixpkgs.config = { | 83 | # nixpkgs.config = { |
| 70 | allowUnfree = true; | 84 | # allowUnfree = true; |
| 71 | zathura.useMupdf = false; | 85 | # zathura.useMupdf = false; |
| 72 | }; | 86 | # }; |
| 73 | 87 | ||
| 74 | nix.registry = { | 88 | nix.registry = { |
| 75 | "flk" = { | 89 | "flk" = { |
| @@ -160,6 +174,7 @@ in { | |||
| 160 | }; | 174 | }; |
| 161 | }; | 175 | }; |
| 162 | }; | 176 | }; |
| 177 | chromium.enable = true; | ||
| 163 | 178 | ||
| 164 | zathura = { | 179 | zathura = { |
| 165 | enable = true; | 180 | enable = true; |
| @@ -175,13 +190,93 @@ in { | |||
| 175 | gpu-api = "vulkan"; | 190 | gpu-api = "vulkan"; |
| 176 | }; | 191 | }; |
| 177 | 192 | ||
| 178 | zsh.initExtra = '' | 193 | zsh.initContent = let |
| 179 | source ${./zshrc} | 194 | zshrc = pkgs.resholve.mkDerivation { |
| 195 | pname = "zshrc"; | ||
| 196 | version = "0.0.0"; | ||
| 197 | |||
| 198 | src = ./zshrc; | ||
| 199 | |||
| 200 | dontUnpack = true; | ||
| 201 | dontConfigure = true; | ||
| 202 | dontBuild = true; | ||
| 203 | |||
| 204 | installPhase = '' | ||
| 205 | mkdir -p $out/share | ||
| 206 | install "$src" $out/share/zshrc | ||
| 207 | ''; | ||
| 208 | |||
| 209 | solutions = { | ||
| 210 | default = { | ||
| 211 | scripts = [ "share/zshrc" ]; | ||
| 212 | interpreter = "none"; | ||
| 213 | inputs = with pkgs; [ | ||
| 214 | coreutils | ||
| 215 | rpm | ||
| 216 | binutils | ||
| 217 | squashfsTools | ||
| 218 | unzip | ||
| 219 | cfg.programs.git.package | ||
| 220 | magickWrapped | ||
| 221 | curl | ||
| 222 | file | ||
| 223 | gnutar | ||
| 224 | cpio | ||
| 225 | magic-wormhole | ||
| 226 | cfg.programs.zsh.package | ||
| 227 | fuse | ||
| 228 | util-linux | ||
| 229 | findutils | ||
| 230 | qrencode | ||
| 231 | tty-clock | ||
| 232 | cfg.programs.jq.package | ||
| 233 | eza | ||
| 234 | less | ||
| 235 | config.systemd.package | ||
| 236 | config.programs.ssh.package | ||
| 237 | gnused | ||
| 238 | miniserve | ||
| 239 | p7zip | ||
| 240 | ]; | ||
| 241 | execer = with pkgs; [ | ||
| 242 | "cannot:${lib.getExe' rpm "rpm2cpio"}" | ||
| 243 | "cannot:${lib.getExe' squashfsTools "unsquashfs"}" | ||
| 244 | "cannot:${lib.getExe' unzip "unzip"}" | ||
| 245 | "cannot:${lib.getExe cfg.programs.git.package}" | ||
| 246 | "cannot:${lib.getExe cpio}" | ||
| 247 | "cannot:${lib.getExe' magic-wormhole "wormhole"}" | ||
| 248 | "cannot:${lib.getExe' fuse "fusermount"}" | ||
| 249 | "cannot:${lib.getExe less}" | ||
| 250 | "cannot:${lib.getExe' config.systemd.package "systemctl"}" | ||
| 251 | "cannot:${lib.getExe config.programs.ssh.package}" | ||
| 252 | "cannot:${lib.getExe' p7zip "7z"}" | ||
| 253 | ]; | ||
| 254 | wrapper = with pkgs; [ | ||
| 255 | "${lib.getExe' magickWrapped "magick"}:${lib.getExe' imagemagick "magick"}" | ||
| 256 | ]; | ||
| 257 | fake = { | ||
| 258 | builtin = ["print"]; | ||
| 259 | external = ["sudo" "umount"]; | ||
| 260 | }; | ||
| 261 | }; | ||
| 262 | }; | ||
| 263 | }; | ||
| 264 | magickWrapped = pkgs.symlinkJoin { | ||
| 265 | inherit (pkgs.imagemagick) name; | ||
| 266 | paths = [ pkgs.imagemagick ]; | ||
| 267 | |||
| 268 | buildInputs = with pkgs; [ makeWrapper ]; | ||
| 269 | postBuild = '' | ||
| 270 | wrapProgram $out/bin/magick \ | ||
| 271 | --prefix PATH : ${lib.makeBinPath (with pkgs; [ ghostscript ])} | ||
| 272 | ''; | ||
| 273 | }; | ||
| 274 | in '' | ||
| 275 | source ${zshrc}/share/zshrc | ||
| 180 | ''; | 276 | ''; |
| 181 | zsh.dirHashes = let | 277 | zsh.dirHashes = let |
| 182 | flakeHashes = mapAttrs' (n: v: nameValuePair (inputNames.${n} or n) (toString v)) flakeInputs; | 278 | flakeHashes = mapAttrs' (n: v: nameValuePair (inputNames.${n} or n) (toString v)) flakeInputs; |
| 183 | inputNames = { | 279 | inputNames = { |
| 184 | "nixpkgs" = "nixos"; | ||
| 185 | }; | 280 | }; |
| 186 | in flakeHashes // { | 281 | in flakeHashes // { |
| 187 | u2w = "$HOME/projects/uni2work"; | 282 | u2w = "$HOME/projects/uni2work"; |
| @@ -193,6 +288,16 @@ in { | |||
| 193 | pro = "$HOME/projects/pro"; | 288 | pro = "$HOME/projects/pro"; |
| 194 | media = "$HOME/media"; | 289 | media = "$HOME/media"; |
| 195 | }; | 290 | }; |
| 291 | jq.colors = { | ||
| 292 | arrays = "1;37"; | ||
| 293 | "false" = "0;37"; | ||
| 294 | "null" = "2;37"; | ||
| 295 | numbers = "0;37"; | ||
| 296 | objectKeys = "1;34"; | ||
| 297 | objects = "1;37"; | ||
| 298 | strings = "0;32"; | ||
| 299 | "true" = "0;37"; | ||
| 300 | }; | ||
| 196 | 301 | ||
| 197 | obs-studio = { | 302 | obs-studio = { |
| 198 | enable = true; | 303 | enable = true; |
| @@ -202,7 +307,7 @@ in { | |||
| 202 | gh = { | 307 | gh = { |
| 203 | enable = true; | 308 | enable = true; |
| 204 | settings = { | 309 | settings = { |
| 205 | editor = lib.getExe' config.home-manager.users.${userName}.programs.emacs.package "emacsclient"; | 310 | editor = lib.getExe' editor "emacsclient"; |
| 206 | gitProtocol = "ssh"; | 311 | gitProtocol = "ssh"; |
| 207 | }; | 312 | }; |
| 208 | }; | 313 | }; |
| @@ -228,16 +333,10 @@ in { | |||
| 228 | # notify_on_cmd_finish = "invisible 120"; | 333 | # notify_on_cmd_finish = "invisible 120"; |
| 229 | }; | 334 | }; |
| 230 | keybindings = { | 335 | keybindings = { |
| 231 | "kitty_mod+n" = "detach_window"; | 336 | "kitty_mod+n" = "new_os_window_with_cwd"; |
| 232 | "kitty_mod+m" = "detach_window ask"; | 337 | "kitty_mod+m" = "detach_window ask"; |
| 233 | }; | 338 | "kitty_mod+enter" = "new_window_with_cwd"; |
| 234 | }; | 339 | "kitty_mod+t" = "new_tab_with_cwd"; |
| 235 | wpaperd = { | ||
| 236 | enable = true; | ||
| 237 | settings.default = { | ||
| 238 | path = "~/.wallpapers"; | ||
| 239 | duration = "15m"; | ||
| 240 | mode = "center"; | ||
| 241 | }; | 340 | }; |
| 242 | }; | 341 | }; |
| 243 | fuzzel = { | 342 | fuzzel = { |
| @@ -250,7 +349,7 @@ in { | |||
| 250 | font = "Fira Sans"; | 349 | font = "Fira Sans"; |
| 251 | }; | 350 | }; |
| 252 | colors = { | 351 | colors = { |
| 253 | background = "000000aa"; | 352 | background = "000000cc"; |
| 254 | text = "cdd6f4ff"; | 353 | text = "cdd6f4ff"; |
| 255 | match = "94e2d5ff"; | 354 | match = "94e2d5ff"; |
| 256 | selection = "585b70ff"; | 355 | selection = "585b70ff"; |
| @@ -267,15 +366,28 @@ in { | |||
| 267 | enable = true; | 366 | enable = true; |
| 268 | extraAbbreviations = ["i.A." "d.h." "D.h." "gdw."]; | 367 | extraAbbreviations = ["i.A." "d.h." "D.h." "gdw."]; |
| 269 | }; | 368 | }; |
| 369 | nushell = { | ||
| 370 | enable = true; | ||
| 371 | settings.show_banner = false; | ||
| 372 | }; | ||
| 373 | fd.enable = true; | ||
| 270 | }; | 374 | }; |
| 271 | 375 | ||
| 272 | services = { | 376 | services = { |
| 377 | wpaperd = { | ||
| 378 | enable = false; | ||
| 379 | settings.default = { | ||
| 380 | path = "~/.wallpapers"; | ||
| 381 | duration = "15m"; | ||
| 382 | mode = "center"; | ||
| 383 | }; | ||
| 384 | }; | ||
| 273 | emacs = { | 385 | emacs = { |
| 274 | enable = true; | 386 | enable = true; |
| 275 | socketActivation.enable = true; | 387 | socketActivation.enable = true; |
| 276 | client = { | 388 | client = { |
| 277 | enable = true; | 389 | enable = true; |
| 278 | arguments = mkForce ["--reuse-frame" "--alternate-editor" "\"\""]; | 390 | arguments = mkForce ["--create-frame" "--alternate-editor" (lib.getExe cfg.services.emacs.package)]; |
| 279 | }; | 391 | }; |
| 280 | }; | 392 | }; |
| 281 | gpg-agent = { | 393 | gpg-agent = { |
| @@ -338,20 +450,6 @@ in { | |||
| 338 | serverUrl = "https://etesync.yggdrasil.li"; | 450 | serverUrl = "https://etesync.yggdrasil.li"; |
| 339 | }; | 451 | }; |
| 340 | 452 | ||
| 341 | swayidle = { | ||
| 342 | enable = true; | ||
| 343 | events = [ | ||
| 344 | { event = "before-sleep"; command = lockCommand; } | ||
| 345 | { event = "lock"; command = lockCommand; } | ||
| 346 | ]; | ||
| 347 | timeouts = [ | ||
| 348 | { timeout = 600; command = lockCommand; } | ||
| 349 | ]; | ||
| 350 | extraArgs = [ | ||
| 351 | "-w" | ||
| 352 | "idlehint" "30" | ||
| 353 | ]; | ||
| 354 | }; | ||
| 355 | poweralertd.enable = true; | 453 | poweralertd.enable = true; |
| 356 | }; | 454 | }; |
| 357 | 455 | ||
| @@ -383,6 +481,15 @@ in { | |||
| 383 | name = "Paper-Mono-Dark"; | 481 | name = "Paper-Mono-Dark"; |
| 384 | }; | 482 | }; |
| 385 | }; | 483 | }; |
| 484 | qt.enable = true; | ||
| 485 | qt.platformTheme.name = "gtk"; | ||
| 486 | |||
| 487 | qt.kde.settings = { | ||
| 488 | kwalletrc = { | ||
| 489 | KSecretD.Enabled = false; | ||
| 490 | Wallet."Default Wallet" = "store"; | ||
| 491 | }; | ||
| 492 | }; | ||
| 386 | 493 | ||
| 387 | xsession.preferStatusNotifierItems = true; | 494 | xsession.preferStatusNotifierItems = true; |
| 388 | 495 | ||
| @@ -392,20 +499,19 @@ in { | |||
| 392 | packages = with pkgs; [ | 499 | packages = with pkgs; [ |
| 393 | fira fira-code pwvucontrol wrappedKeepassxc wl-clipboard-rs | 500 | fira fira-code pwvucontrol wrappedKeepassxc wl-clipboard-rs |
| 394 | mumble pulseaudio-ctl pamixer libnotify screen-message | 501 | mumble pulseaudio-ctl pamixer libnotify screen-message |
| 395 | wrappedYTMDesktop libsForQt5.qt5ct playerctl evince | 502 | wrappedYTMDesktop libsForQt5.qt5ct playerctl evince papers |
| 396 | thunderbird zoom-us xdg-desktop-portal steam steam-run | 503 | thunderbird zoom-us xdg-desktop-portal steam steam-run |
| 397 | wireshark virt-manager rclone cached-nix-shell worktime | 504 | wireshark virt-manager rclone cached-nix-shell worktime |
| 398 | fira-code-symbols libreoffice xournalpp google-chrome | 505 | fira-code-symbols libreoffice xournalpp |
| 399 | nixos-shell virt-viewer freerdp gnome-icon-theme | 506 | nixos-shell virt-viewer freerdp gnome-icon-theme |
| 400 | paper-icon-theme sshpassSecret weechat element-desktop | 507 | paper-icon-theme sshpassSecret weechat element-desktop |
| 401 | flakeInputs.deploy-rs.packages.${config.nixpkgs.system}.deploy-rs | 508 | sieve-connect gimp3 inkscape udiskie glab nitrokey-app |
| 402 | sieve-connect gimp inkscape udiskie glab nitrokey-app | ||
| 403 | pynitrokey gtklock wlrctl remmina openscad spice-record | 509 | pynitrokey gtklock wlrctl remmina openscad spice-record |
| 404 | libguestfs-with-appliance nerd-fonts.fira-mono | 510 | nerd-fonts.fira-mono |
| 405 | nerd-fonts.symbols-only nerd-fonts.fira-code powerline-fonts | 511 | nerd-fonts.symbols-only nerd-fonts.fira-code powerline-fonts |
| 406 | swtpm (hunspellWithDicts (with hunspellDicts; [en_GB-large de_DE])) | 512 | swtpm (hunspell.withDicts (dicts: with dicts; [en_GB-large de_DE])) |
| 407 | # synadm | 513 | libation libqalculate |
| 408 | ]; | 514 | ] ++ mapAttrsToList (_name: pkg: pkgs.callPackage pkg {}) (customUtils.nixImport { dir = ./utils; }); |
| 409 | 515 | ||
| 410 | file = { | 516 | file = { |
| 411 | ".backup-munin".source = ./backup-patterns; | 517 | ".backup-munin".source = ./backup-patterns; |
| @@ -422,15 +528,12 @@ in { | |||
| 422 | sessionVariables = { | 528 | sessionVariables = { |
| 423 | # GDK_SCALE = 96.0 / 282.0; | 529 | # GDK_SCALE = 96.0 / 282.0; |
| 424 | # QT_AUTO_SCREEN_SCALE_FACTOR = 1; | 530 | # QT_AUTO_SCREEN_SCALE_FACTOR = 1; |
| 425 | QT_QPA_PLATFORMTHEME = "qt5ct"; | 531 | QT_QPA_PLATFORMTHEME = lib.mkForce "gtk3"; |
| 426 | LIBVIRT_DEFAULT_URI = "qemu:///system"; | 532 | LIBVIRT_DEFAULT_URI = "qemu:///system"; |
| 427 | STACK_XDG = 1; | 533 | STACK_XDG = 1; |
| 428 | EDITOR = pkgs.writeShellScript "editor" '' | 534 | EDITOR = lib.getExe' editor "emacsclient"; |
| 429 | args=("--reuse-frame" "--alternate-editor" "") | ||
| 430 | args+=("$@") | ||
| 431 | exec -a emacsclient ${lib.getExe' cfg.services.emacs.package "emacsclient"} "''${args[@]}" | ||
| 432 | ''; | ||
| 433 | RCLONE_PASSWORD_COMMAND = "${lib.getExe' pkgs.libsecret "secret-tool"} lookup service rclone"; | 535 | RCLONE_PASSWORD_COMMAND = "${lib.getExe' pkgs.libsecret "secret-tool"} lookup service rclone"; |
| 536 | SYSTEMD_TINT_BACKGROUND = "false"; | ||
| 434 | }; | 537 | }; |
| 435 | 538 | ||
| 436 | extraProfileCommands = '' | 539 | extraProfileCommands = '' |
| @@ -467,13 +570,21 @@ in { | |||
| 467 | General = { | 570 | General = { |
| 468 | dot_as_separator = 0; | 571 | dot_as_separator = 0; |
| 469 | }; | 572 | }; |
| 573 | Mode = { | ||
| 574 | calculate_as_you_type = 1; | ||
| 575 | }; | ||
| 470 | }; | 576 | }; |
| 471 | }; | 577 | }; |
| 472 | "emacs/init.el".source = ./emacs.el; | 578 | "emacs/init.el".source = pkgs.substitute { |
| 473 | "systemd/user/xdg-desktop-portal.service.d/after-graphical-session.conf".text = '' | 579 | src = ./emacs.el; |
| 474 | [Unit] | 580 | substitutions = [ |
| 475 | After=graphical-session.target | 581 | "--subst-var-by" "ksshaskpass" (lib.getExe pkgs.kdePackages.ksshaskpass) |
| 476 | ''; | 582 | ]; |
| 583 | }; | ||
| 584 | # "systemd/user/xdg-desktop-portal.service.d/after-graphical-session.conf".text = '' | ||
| 585 | # [Unit] | ||
| 586 | # After=graphical-session.target | ||
| 587 | # ''; | ||
| 477 | "systemd/user/home-manager.service.d/before-graphical-session.conf".text = '' | 588 | "systemd/user/home-manager.service.d/before-graphical-session.conf".text = '' |
| 478 | [Unit] | 589 | [Unit] |
| 479 | Before=graphical-session-pre.target | 590 | Before=graphical-session-pre.target |
| @@ -487,6 +598,8 @@ in { | |||
| 487 | xdg.dataFile = { | 598 | xdg.dataFile = { |
| 488 | "dbus-1/services/org.keepassxc.KeePassXC.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.keepassxc.KeePassXC.service"; | 599 | "dbus-1/services/org.keepassxc.KeePassXC.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.keepassxc.KeePassXC.service"; |
| 489 | "dbus-1/services/org.freedesktop.secrets.service.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.freedesktop.secrets.service.service"; | 600 | "dbus-1/services/org.freedesktop.secrets.service.service".source = "${wrappedKeepassxc}/share/dbus-1/services/org.freedesktop.secrets.service.service"; |
| 601 | "dbus-1/services/org.kde.kwalletd6.service".source = "${pkgs.kdePackages.kwallet}/share/dbus-1/services/org.kde.kwalletd6.service"; | ||
| 602 | "dbus-1/services/org.kde.kwalletd5.service".source = "${pkgs.kdePackages.kwallet}/share/dbus-1/services/org.kde.kwalletd5.service"; | ||
| 490 | "emoji-data/list.txt".source = pkgs.stdenv.mkDerivation { | 603 | "emoji-data/list.txt".source = pkgs.stdenv.mkDerivation { |
| 491 | inherit (sources.emoji-data) pname src; | 604 | inherit (sources.emoji-data) pname src; |
| 492 | version = lib.removePrefix "v" sources.emoji-data.version; | 605 | version = lib.removePrefix "v" sources.emoji-data.version; |
| @@ -574,11 +687,11 @@ in { | |||
| 574 | exec -- \ | 687 | exec -- \ |
| 575 | ${lib.getExe' config.systemd.package "systemd-run"} --wait --user --slice-inherit \ | 688 | ${lib.getExe' config.systemd.package "systemd-run"} --wait --user --slice-inherit \ |
| 576 | --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \ | 689 | --property 'CPUAccounting=yes' --property 'CPUQuotaPeriodSec=50ms' \ |
| 577 | --property 'Environment=DSCP=46' \ | 690 | -E DSCP=46 -E NIXOS_OZONE_WL \ |
| 578 | -- ${lib.getExe pkgs.dscp} ${lib.getExe' pkgs.google-chrome "google-chrome-stable"} \ | 691 | -- ${lib.getExe pkgs.dscp} ${lib.getExe cfg.programs.chromium.package} \ |
| 579 | --class=Rainbow \ | 692 | --class=Rainbow \ |
| 580 | --kiosk "https://web.openrainbow.com" \ | 693 | --app="https://web.openrainbow.com" \ |
| 581 | --user-data-dir=''${HOME}/.config/google-chrome-rainbow | 694 | --user-data-dir=''${HOME}/.config/chromium-rainbow |
| 582 | ''); | 695 | ''); |
| 583 | icon = pkgs.fetchurl { | 696 | icon = pkgs.fetchurl { |
| 584 | url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg"; | 697 | url = "https://web.openrainbow.com/rb/2.139.17/assets/skins/rainbow/images/homepage/logo__rainbow.svg"; |
| @@ -588,6 +701,53 @@ in { | |||
| 588 | StartupWMClass = "Rainbow"; | 701 | StartupWMClass = "Rainbow"; |
| 589 | }; | 702 | }; |
| 590 | }; | 703 | }; |
| 704 | kimai = { | ||
| 705 | name = "Kimai"; | ||
| 706 | exec = toString (pkgs.writeShellScript "kimai" '' | ||
| 707 | exec -- \ | ||
| 708 | ${lib.getExe cfg.programs.chromium.package} \ | ||
| 709 | --class=Kimai \ | ||
| 710 | --app="https://kimai.yggdrasil.li" \ | ||
| 711 | --user-data-dir=''${HOME}/.config/chromium-kimai | ||
| 712 | ''); | ||
| 713 | icon = pkgs.fetchurl { | ||
| 714 | url = "https://www.kimai.org/images/kimai_logo.png"; | ||
| 715 | hash = "sha256-lnlOttzR2SwXA70R+egJUkeKr4U5V0avqTk8uX4bqfs="; | ||
| 716 | }; | ||
| 717 | settings = { | ||
| 718 | StartupWMClass = "Kimai"; | ||
| 719 | StartupNotify = "true"; | ||
| 720 | }; | ||
| 721 | }; | ||
| 722 | audiobookshelf = { | ||
| 723 | name = "Audiobookshelf"; | ||
| 724 | exec = toString (pkgs.writeShellScript "audiobookshelf" '' | ||
| 725 | exec -- \ | ||
| 726 | ${lib.getExe cfg.programs.chromium.package} \ | ||
| 727 | --class=Audiobookshelf \ | ||
| 728 | --app="https://audiobookshelf.yggdrasil.li" \ | ||
| 729 | --user-data-dir=''${HOME}/.config/chromium-audiobookshelf | ||
| 730 | ''); | ||
| 731 | icon = pkgs.fetchurl { | ||
| 732 | url = "https://www.audiobookshelf.org/Logo.png"; | ||
| 733 | hash = "sha256-JGPk+WNT1C4DC4lSMb0K0YmAMT5LvmSOeO0QRzkc7Lk="; | ||
| 734 | }; | ||
| 735 | settings = { | ||
| 736 | StartupWMClass = "Audiobookshelf"; | ||
| 737 | StartupNotify = "true"; | ||
| 738 | }; | ||
| 739 | }; | ||
| 740 | thunderbird-lmu = { | ||
| 741 | name = "Thunderbird (LMU)"; | ||
| 742 | exec = "thunderbird --name thunderbird -P lmu %U"; | ||
| 743 | icon = "thunderbird"; | ||
| 744 | genericName = "Email Client"; | ||
| 745 | categories = [ "Network" "Chat" "Email" "Feed" "GTK" "News" ]; | ||
| 746 | settings = { | ||
| 747 | StartupWMClass = "thunderbird"; | ||
| 748 | StartupNotify = "true"; | ||
| 749 | }; | ||
| 750 | }; | ||
| 591 | }; | 751 | }; |
| 592 | 752 | ||
| 593 | fonts = { | 753 | fonts = { |
diff --git a/accounts/gkleen@sif/emacs.el b/accounts/gkleen@sif/emacs.el index 5cee16b0..3beefba6 100644 --- a/accounts/gkleen@sif/emacs.el +++ b/accounts/gkleen@sif/emacs.el | |||
| @@ -51,7 +51,7 @@ | |||
| 51 | 51 | ||
| 52 | ;; (require 'scratch) | 52 | ;; (require 'scratch) |
| 53 | (global-set-key (kbd "C-x B") 'scratch-create) | 53 | (global-set-key (kbd "C-x B") 'scratch-create) |
| 54 | (setq initial-major-mode 'scratch-mode) | 54 | ;; (setq initial-major-mode 'scratch-mode) |
| 55 | (setq initial-scratch-message "") | 55 | (setq initial-scratch-message "") |
| 56 | 56 | ||
| 57 | (global-set-key (kbd "C-x K") 'kill-current-buffer) | 57 | (global-set-key (kbd "C-x K") 'kill-current-buffer) |
| @@ -254,3 +254,5 @@ necessarily running." | |||
| 254 | (bind-key "C-x C-m" #'move-file) | 254 | (bind-key "C-x C-m" #'move-file) |
| 255 | 255 | ||
| 256 | (let ((ssh_auth_sock (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))) (setenv "SSH_AUTH_SOCK" ssh_auth_sock)) | 256 | (let ((ssh_auth_sock (string-chop-newline (shell-command-to-string "gpgconf --list-dirs agent-ssh-socket")))) (setenv "SSH_AUTH_SOCK" ssh_auth_sock)) |
| 257 | (setenv "SSH_ASKPASS_REQUIRE" "prefer") | ||
| 258 | (setenv "SSH_ASKPASS" "@ksshaskpass@") | ||
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix index 216a98ea..5ae372c1 100644 --- a/accounts/gkleen@sif/niri/default.nix +++ b/accounts/gkleen@sif/niri/default.nix | |||
| @@ -3,13 +3,10 @@ let | |||
| 3 | cfg = config.programs.niri; | 3 | cfg = config.programs.niri; |
| 4 | 4 | ||
| 5 | kdl = flakeInputs.niri-flake.lib.kdl; | 5 | kdl = flakeInputs.niri-flake.lib.kdl; |
| 6 | sleaf = name: arg: kdl.node name [arg] []; | ||
| 6 | 7 | ||
| 7 | niri = cfg.package; | 8 | niri = cfg.package; |
| 8 | terminal = lib.getExe config.programs.kitty.package; | 9 | terminal = lib.getExe config.programs.kitty.package; |
| 9 | makoctl = lib.getExe' config.services.mako.package "makoctl"; | ||
| 10 | loginctl = lib.getExe' hostConfig.systemd.package "loginctl"; | ||
| 11 | systemctl = lib.getExe' hostConfig.systemd.package "systemctl"; | ||
| 12 | swayosd-client = lib.getExe' config.services.swayosd.package "swayosd-client"; | ||
| 13 | 10 | ||
| 14 | focus_or_spawn = pkgs.writeShellApplication { | 11 | focus_or_spawn = pkgs.writeShellApplication { |
| 15 | name = "focus-or-spawn"; | 12 | name = "focus-or-spawn"; |
| @@ -35,7 +32,11 @@ let | |||
| 35 | if jq -e '.is_focused' <<<"$window_json" >/dev/null; then | 32 | if jq -e '.is_focused' <<<"$window_json" >/dev/null; then |
| 36 | niri msg action focus-workspace-previous | 33 | niri msg action focus-workspace-previous |
| 37 | else | 34 | else |
| 38 | niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")" | 35 | if [[ $(jq -r --arg workspace_name "$workspace_name" 'map(select(.name == $workspace_name)) | .[0].is_focused' <<<"$workspaces_json") != "true" ]] && [[ $(jq -r --arg workspace_name "$workspace_name" 'map(select(.name == $workspace_name)) | .[0].id' <<<"$workspaces_json") = $(jq -r '.workspace_id' <<<"$window_json") ]]; then |
| 36 | niri msg action focus-workspace "$workspace_name" | ||
| 37 | else | ||
| 38 | niri msg action focus-window --id "$(jq -r '.id' <<<"$window_json")" | ||
| 39 | fi | ||
| 39 | fi | 40 | fi |
| 40 | exit 0 | 41 | exit 0 |
| 41 | fi | 42 | fi |
| @@ -45,7 +46,6 @@ let | |||
| 45 | ''; | 46 | ''; |
| 46 | }; | 47 | }; |
| 47 | focus-or-spawn-action = config.lib.niri.actions.spawn (lib.getExe focus_or_spawn); | 48 | focus-or-spawn-action = config.lib.niri.actions.spawn (lib.getExe focus_or_spawn); |
| 48 | focus-or-spawn-action-app_id = app_id: focus-or-spawn-action ''select(.app_id == "${app_id}")''; | ||
| 49 | 49 | ||
| 50 | with_adjacent_workspace = pkgs.writeShellApplication { | 50 | with_adjacent_workspace = pkgs.writeShellApplication { |
| 51 | name = "with-adjacent-workspace"; | 51 | name = "with-adjacent-workspace"; |
| @@ -84,7 +84,7 @@ let | |||
| 84 | }; | 84 | }; |
| 85 | with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^${lib.concatMapStringsSep "|" ({ name, ...}: name) cfg.scratchspaces}$"; | 85 | with-adjacent-workspace-action = config.lib.niri.actions.spawn (lib.getExe with_adjacent_workspace) "^${lib.concatMapStringsSep "|" ({ name, ...}: name) cfg.scratchspaces}$"; |
| 86 | focus-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; | 86 | focus-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; |
| 87 | move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}''; | 87 | move-column-to-adjacent-workspace = direction: with-adjacent-workspace-action direction ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}''; |
| 88 | 88 | ||
| 89 | with_unnamed_workspace = pkgs.writeShellApplication { | 89 | with_unnamed_workspace = pkgs.writeShellApplication { |
| 90 | name = "with-unnamed-workspace"; | 90 | name = "with-unnamed-workspace"; |
| @@ -131,7 +131,7 @@ let | |||
| 131 | 131 | ||
| 132 | windows_json="$(niri msg -j windows)" | 132 | windows_json="$(niri msg -j windows)" |
| 133 | active_workspace="$(jq -r '.[] | select(.is_focused) | .workspace_id' <<<"$windows_json")" | 133 | active_workspace="$(jq -r '.[] | select(.is_focused) | .workspace_id' <<<"$windows_json")" |
| 134 | window_ix="$(gojq -r --arg active_workspace "$active_workspace" '.[] | select('"$window_select"') | "\(.title)\u0000icon\u001f\(.app_id)"' <<<"$windows_json" | fuzzel --log-level=warning --dmenu --index)" | 134 | window_ix="$(gojq -r --arg active_workspace "$active_workspace" '.[] | select('"$window_select"') | "\(.title)\u0000icon\u001f\(.app_id)"' <<<"$windows_json" | fuzzel --width=60 --log-level=warning --dmenu --index)" |
| 135 | # shellcheck disable=SC2016 | 135 | # shellcheck disable=SC2016 |
| 136 | window_json="$(gojq -rc --arg active_workspace "$active_workspace" --arg window_ix "$window_ix" 'map(select('"$window_select"')) | .[($window_ix | tonumber)]' <<<"$windows_json")" | 136 | window_json="$(gojq -rc --arg active_workspace "$active_workspace" --arg window_ix "$window_ix" 'map(select('"$window_select"')) | .[($window_ix | tonumber)]' <<<"$windows_json")" |
| 137 | 137 | ||
| @@ -141,13 +141,26 @@ let | |||
| 141 | ''; | 141 | ''; |
| 142 | }; | 142 | }; |
| 143 | with-select-window-action = config.lib.niri.actions.spawn (lib.getExe with_select_window); | 143 | with-select-window-action = config.lib.niri.actions.spawn (lib.getExe with_select_window); |
| 144 | in { | ||
| 145 | imports = [ | ||
| 146 | ./waybar.nix | ||
| 147 | ./mako.nix | ||
| 148 | ./swayosd.nix | ||
| 149 | ]; | ||
| 150 | 144 | ||
| 145 | with_predicate_window = pred: pkgs.writeShellApplication { | ||
| 146 | name = "with-predicate-window"; | ||
| 147 | runtimeInputs = [ niri pkgs.gojq pkgs.socat ]; | ||
| 148 | text = '' | ||
| 149 | action="$1" | ||
| 150 | shift | ||
| 151 | |||
| 152 | windows_json="$(niri msg -j windows)" | ||
| 153 | window_json="$(gojq -rc 'map(select(${pred})) | .[0]' <<<"$windows_json")" | ||
| 154 | |||
| 155 | [[ -z "$window_json" || $window_json = "null" ]] && exit 1 | ||
| 156 | |||
| 157 | jq -c "$action" <<<"$window_json" | socat STDIO "$NIRI_SOCKET" | ||
| 158 | ''; | ||
| 159 | }; | ||
| 160 | |||
| 161 | with-urgent-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_urgent")); | ||
| 162 | with-focused-window-action = config.lib.niri.actions.spawn (lib.getExe (with_predicate_window ".is_focused")); | ||
| 163 | in { | ||
| 151 | options = { | 164 | options = { |
| 152 | programs.niri.scratchspaces = lib.mkOption { | 165 | programs.niri.scratchspaces = lib.mkOption { |
| 153 | type = lib.types.listOf (lib.types.submodule ({ config, ... }: { | 166 | type = lib.types.listOf (lib.types.submodule ({ config, ... }: { |
| @@ -171,6 +184,17 @@ in { | |||
| 171 | type = lib.types.nullOr lib.types.str; | 184 | type = lib.types.nullOr lib.types.str; |
| 172 | default = null; | 185 | default = null; |
| 173 | }; | 186 | }; |
| 187 | moveKey = lib.mkOption { | ||
| 188 | type = lib.types.nullOr lib.types.str; | ||
| 189 | default = let | ||
| 190 | keys = lib.splitString "+" config.key; | ||
| 191 | defMoveKey = lib.concatStringsSep "+" (lib.flatten [ | ||
| 192 | (lib.take (lib.length keys - 1) keys) | ||
| 193 | ["Shift"] | ||
| 194 | (lib.takeEnd 1 keys) | ||
| 195 | ]); | ||
| 196 | in if config.key == null then null else defMoveKey; | ||
| 197 | }; | ||
| 174 | spawn = lib.mkOption { | 198 | spawn = lib.mkOption { |
| 175 | type = lib.types.nullOr (lib.types.listOf lib.types.str); | 199 | type = lib.types.nullOr (lib.types.listOf lib.types.str); |
| 176 | default = null; | 200 | default = null; |
| @@ -197,36 +221,7 @@ in { | |||
| 197 | }; | 221 | }; |
| 198 | 222 | ||
| 199 | config = { | 223 | config = { |
| 200 | systemd.user.services.xwayland-satellite = { | 224 | home.packages = [ pkgs.xwayland-satellite-unstable ]; |
| 201 | Unit = { | ||
| 202 | BindsTo = [ "graphical-session.target" ]; | ||
| 203 | PartOf = [ "graphical-session.target" ]; | ||
| 204 | After = [ "graphical-session.target" ]; | ||
| 205 | Requisite = [ "graphical-session.target" ]; | ||
| 206 | }; | ||
| 207 | Service = { | ||
| 208 | Type = "notify"; | ||
| 209 | NotifyAccess = "all"; | ||
| 210 | Environment = [ "DISPLAY=:0" ]; | ||
| 211 | ExecStart = ''${lib.getExe pkgs.xwayland-satellite-unstable} ''${DISPLAY}''; | ||
| 212 | ExecStartPre = "${systemctl} --user import-environment DISPLAY"; | ||
| 213 | StandardOutput = "journal"; | ||
| 214 | }; | ||
| 215 | Install = { | ||
| 216 | WantedBy = [ "graphical-session.target" ]; | ||
| 217 | }; | ||
| 218 | }; | ||
| 219 | |||
| 220 | services.swayidle = { | ||
| 221 | events = [ | ||
| 222 | { event = "after-resume"; command = "${lib.getExe niri} msg action power-on-monitors"; } | ||
| 223 | ]; | ||
| 224 | timeouts = [ | ||
| 225 | { timeout = 540; | ||
| 226 | command = "${lib.getExe niri} msg action power-off-monitors"; | ||
| 227 | } | ||
| 228 | ]; | ||
| 229 | }; | ||
| 230 | 225 | ||
| 231 | systemd.user.sockets.niri-workspace-history = { | 226 | systemd.user.sockets.niri-workspace-history = { |
| 232 | Socket = { | 227 | Socket = { |
| @@ -245,11 +240,11 @@ in { | |||
| 245 | Service = { | 240 | Service = { |
| 246 | Type = "simple"; | 241 | Type = "simple"; |
| 247 | Sockets = [ "niri-workspace-history.socket" ]; | 242 | Sockets = [ "niri-workspace-history.socket" ]; |
| 248 | ExecStart = pkgs.writers.writePython3 "niri-workspace-history" {} '' | 243 | ExecStart = pkgs.writers.writePython3 "niri-workspace-history" { flakeIgnore = ["E501"]; } '' |
| 249 | import os | 244 | import os |
| 250 | import socket | 245 | import socket |
| 251 | import json | 246 | import json |
| 252 | import sys | 247 | # import sys |
| 253 | from collections import defaultdict | 248 | from collections import defaultdict |
| 254 | from threading import Thread, Lock | 249 | from threading import Thread, Lock |
| 255 | from socketserver import StreamRequestHandler, ThreadingTCPServer | 250 | from socketserver import StreamRequestHandler, ThreadingTCPServer |
| @@ -273,11 +268,9 @@ in { | |||
| 273 | workspaces = list() | 268 | workspaces = list() |
| 274 | 269 | ||
| 275 | def focus_workspace(output, workspace): | 270 | def focus_workspace(output, workspace): |
| 276 | global workspace_history, history_lock | ||
| 277 | |||
| 278 | with history_lock: | 271 | with history_lock: |
| 279 | workspace_history[output] = [workspace] + [ws for ws in workspace_history[output] if ws != workspace] # noqa: E501 | 272 | workspace_history[output] = [workspace] + [ws for ws in workspace_history[output] if ws != workspace] |
| 280 | print(json.dumps(workspace_history), file=sys.stderr) | 273 | # print(json.dumps(workspace_history), file=sys.stderr) |
| 281 | 274 | ||
| 282 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | 275 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
| 283 | sock.connect(os.environ["NIRI_SOCKET"]) | 276 | sock.connect(os.environ["NIRI_SOCKET"]) |
| @@ -299,16 +292,14 @@ in { | |||
| 299 | 292 | ||
| 300 | class RequestHandler(StreamRequestHandler): | 293 | class RequestHandler(StreamRequestHandler): |
| 301 | def handle(self): | 294 | def handle(self): |
| 302 | global workspace_history, history_lock | 295 | with detaching(TextIOWrapper(self.wfile, encoding='utf-8', write_through=True)) as out: |
| 303 | |||
| 304 | with detaching(TextIOWrapper(self.wfile, encoding='utf-8', write_through=True)) as out: # noqa: E501 | ||
| 305 | with history_lock: | 296 | with history_lock: |
| 306 | json.dump(workspace_history, out) | 297 | json.dump(workspace_history, out) |
| 307 | 298 | ||
| 308 | 299 | ||
| 309 | class Server(ThreadingTCPServer): | 300 | class Server(ThreadingTCPServer): |
| 310 | def __init__(self): | 301 | def __init__(self): |
| 311 | ThreadingTCPServer.__init__(self, ("", 8000), RequestHandler, bind_and_activate=False) # noqa: E501 | 302 | ThreadingTCPServer.__init__(self, ("", 8000), RequestHandler, bind_and_activate=False) |
| 312 | self.socket = socket.fromfd(3, self.address_family, self.socket_type) | 303 | self.socket = socket.fromfd(3, self.address_family, self.socket_type) |
| 313 | 304 | ||
| 314 | 305 | ||
| @@ -334,6 +325,79 @@ in { | |||
| 334 | ''; | 325 | ''; |
| 335 | }; | 326 | }; |
| 336 | }; | 327 | }; |
| 328 | systemd.user.services.niri-workspace-sort = { | ||
| 329 | Unit = { | ||
| 330 | BindsTo = [ "niri.service" ]; | ||
| 331 | After = [ "niri.service" ]; | ||
| 332 | }; | ||
| 333 | Install = { | ||
| 334 | WantedBy = [ "niri.service" ]; | ||
| 335 | }; | ||
| 336 | Service = { | ||
| 337 | Type = "simple"; | ||
| 338 | ExecStart = pkgs.writers.writePython3 "niri-workspace-sort" { flakeIgnore = ["E501"]; } '' | ||
| 339 | import os | ||
| 340 | import sys | ||
| 341 | import socket | ||
| 342 | import json | ||
| 343 | |||
| 344 | outputs = None | ||
| 345 | only = {'HDMI-A-1': {'bmr'}, 'eDP-1': {'vid'}} | ||
| 346 | |||
| 347 | |||
| 348 | class Niri(socket.socket): | ||
| 349 | def __init__(self): | ||
| 350 | super().__init__(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 351 | super().connect(os.environ["NIRI_SOCKET"]) | ||
| 352 | self.fh = super().makefile(mode='rw', buffering=1, encoding='utf-8') | ||
| 353 | |||
| 354 | def cmd(self, obj): | ||
| 355 | print(json.dumps(obj, separators=(',', ':')), flush=True, file=self.fh) | ||
| 356 | |||
| 357 | def event_stream(self): | ||
| 358 | self.cmd("EventStream") | ||
| 359 | return self.fh | ||
| 360 | |||
| 361 | |||
| 362 | with Niri() as niri, Niri().event_stream() as niri_stream: | ||
| 363 | for line in niri_stream: | ||
| 364 | workspaces = None | ||
| 365 | if line_json := json.loads(line): | ||
| 366 | if "WorkspacesChanged" in line_json: | ||
| 367 | workspaces = line_json["WorkspacesChanged"]["workspaces"] | ||
| 368 | |||
| 369 | if workspaces is None: | ||
| 370 | continue | ||
| 371 | |||
| 372 | old_outputs = outputs | ||
| 373 | outputs = {ws["output"] for ws in workspaces} | ||
| 374 | if old_outputs is None: | ||
| 375 | print("Initial outputs: {}".format(outputs), file=sys.stderr) | ||
| 376 | continue | ||
| 377 | |||
| 378 | new_outputs = outputs - old_outputs | ||
| 379 | if not new_outputs: | ||
| 380 | continue | ||
| 381 | print("New outputs: {}".format(new_outputs), file=sys.stderr) | ||
| 382 | |||
| 383 | relevant_workspaces = list(filter(lambda ws: (ws["name"] is not None) or (ws["active_window_id"] is not None), workspaces)) | ||
| 384 | target_output = next(iter(outputs - set(only.keys()))) | ||
| 385 | if not target_output: | ||
| 386 | continue | ||
| 387 | for ws in relevant_workspaces: | ||
| 388 | ws_ident = ws["name"] if ws["name"] is not None else (ws["output"], ws["idx"]) | ||
| 389 | if ws["output"] not in set(only.keys()): | ||
| 390 | continue | ||
| 391 | if ws_ident in only[ws["output"]]: | ||
| 392 | continue | ||
| 393 | |||
| 394 | print("{} -> {}".format(ws_ident, target_output), file=sys.stderr) | ||
| 395 | niri.cmd({"Action": {"MoveWorkspaceToMonitor": {"reference": {"Id": ws["id"]}, "output": target_output}}}) | ||
| 396 | ''; | ||
| 397 | Restart = "on-failure"; | ||
| 398 | RestartSec = 10; | ||
| 399 | }; | ||
| 400 | }; | ||
| 337 | 401 | ||
| 338 | programs.niri.scratchspaces = [ | 402 | programs.niri.scratchspaces = [ |
| 339 | { name = "pwctl"; | 403 | { name = "pwctl"; |
| @@ -347,8 +411,8 @@ in { | |||
| 347 | { title = "^Access Request.*"; } | 411 | { title = "^Access Request.*"; } |
| 348 | { title = ".*Passkey credentials$"; } | 412 | { title = ".*Passkey credentials$"; } |
| 349 | ]; | 413 | ]; |
| 350 | windowRuleExtra = [ | 414 | windowRuleExtra = with kdl; [ |
| 351 | (kdl.leaf "open-focused" false) | 415 | (sleaf "open-focused" false) |
| 352 | ]; | 416 | ]; |
| 353 | key = "Mod+Control+P"; | 417 | key = "Mod+Control+P"; |
| 354 | app-id = "org.keepassxc.KeePassXC"; | 418 | app-id = "org.keepassxc.KeePassXC"; |
| @@ -375,6 +439,20 @@ in { | |||
| 375 | app-id = "com.github.wwmm.easyeffects"; | 439 | app-id = "com.github.wwmm.easyeffects"; |
| 376 | spawn = [ "easyeffects" ]; | 440 | spawn = [ "easyeffects" ]; |
| 377 | } | 441 | } |
| 442 | { name = "time"; | ||
| 443 | key = "Mod+Control+K"; | ||
| 444 | app-id = "chrome-kimai.yggdrasil.li__-Default"; | ||
| 445 | spawn = [ (toString (pkgs.resholve.writeScript "kimai" { | ||
| 446 | interpreter = pkgs.runtimeShell; | ||
| 447 | inputs = [ pkgs.dex ]; | ||
| 448 | execer = [ "cannot:${lib.getExe pkgs.dex}" ]; | ||
| 449 | } '' | ||
| 450 | exec dex $HOME/.local/state/nix/profile/share/applications/kimai.desktop | ||
| 451 | '')) ]; | ||
| 452 | windowRuleExtra = with kdl; [ | ||
| 453 | (sleaf "block-out-from" "screencast") | ||
| 454 | ]; | ||
| 455 | } | ||
| 378 | ]; | 456 | ]; |
| 379 | programs.niri.config = | 457 | programs.niri.config = |
| 380 | let | 458 | let |
| @@ -384,10 +462,12 @@ in { | |||
| 384 | then v | 462 | then v |
| 385 | else null; | 463 | else null; |
| 386 | opt-props = lib.filterAttrs (lib.const (value: value != null)); | 464 | opt-props = lib.filterAttrs (lib.const (value: value != null)); |
| 465 | normalize-nodes = nodes: lib.remove null (lib.flatten nodes); | ||
| 387 | in | 466 | in |
| 388 | [ (flag "prefer-no-csd") | 467 | normalize-nodes [ |
| 468 | (flag "prefer-no-csd") | ||
| 389 | 469 | ||
| 390 | (leaf "screenshot-path" "~/screenshots/%Y-%m-%dT%H:%M:%S.png") | 470 | (sleaf "screenshot-path" "~/screenshots/%Y-%m-%dT%H:%M:%S.png") |
| 391 | 471 | ||
| 392 | (plain "hotkey-overlay" [ | 472 | (plain "hotkey-overlay" [ |
| 393 | (flag "skip-at-startup") | 473 | (flag "skip-at-startup") |
| @@ -395,80 +475,88 @@ in { | |||
| 395 | 475 | ||
| 396 | (plain "input" [ | 476 | (plain "input" [ |
| 397 | (plain "keyboard" [ | 477 | (plain "keyboard" [ |
| 398 | (leaf "repeat-delay" 300) | 478 | (sleaf "repeat-delay" 300) |
| 399 | (leaf "repeat-rate" 50) | 479 | (sleaf "repeat-rate" 50) |
| 400 | 480 | ||
| 401 | (plain "xkb" [ | 481 | (plain "xkb" [ |
| 402 | (leaf "layout" "us,us") | 482 | (sleaf "layout" "us,us") |
| 403 | (leaf "variant" "dvp,") | 483 | (sleaf "variant" "dvp,") |
| 404 | (leaf "options" "compose:caps,grp:win_space_toggle") | 484 | (sleaf "options" "compose:caps,grp:win_space_toggle") |
| 405 | ]) | 485 | ]) |
| 406 | ]) | 486 | ]) |
| 407 | 487 | ||
| 408 | (flag "workspace-auto-back-and-forth") | 488 | (flag "workspace-auto-back-and-forth") |
| 409 | # (leaf "focus-follows-mouse" {}) | 489 | # (sleaf "focus-follows-mouse" {}) |
| 410 | # (flag "warp-mouse-to-focus") | 490 | # (flag "warp-mouse-to-focus") |
| 411 | 491 | ||
| 412 | # (plain "touchpad" [ (flag "off") ]) | 492 | # (plain "touchpad" [ (flag "off") ]) |
| 413 | (plain "trackball" [ | 493 | (plain "trackball" [ |
| 414 | (leaf "scroll-method" "on-button-down") | 494 | (sleaf "scroll-method" "on-button-down") |
| 415 | (leaf "scroll-button" 278) | 495 | (sleaf "scroll-button" 278) |
| 416 | ]) | 496 | ]) |
| 417 | (plain "touch" [ | 497 | (plain "touch" [ |
| 418 | (leaf "map-to-output" "eDP-1") | 498 | (sleaf "map-to-output" "eDP-1") |
| 419 | ]) | 499 | ]) |
| 420 | ]) | 500 | ]) |
| 421 | 501 | ||
| 422 | (plain "environment" (lib.mapAttrsToList leaf { | 502 | (plain "gestures" [ |
| 503 | (plain "hot-corners" [(flag "off")]) | ||
| 504 | ]) | ||
| 505 | |||
| 506 | (plain "environment" (lib.mapAttrsToList sleaf { | ||
| 423 | NIXOS_OZONE_WL = "1"; | 507 | NIXOS_OZONE_WL = "1"; |
| 424 | QT_QPA_PLATFORM = "wayland"; | 508 | QT_QPA_PLATFORM = "wayland"; |
| 425 | QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; | 509 | QT_WAYLAND_DISABLE_WINDOWDECORATION = "1"; |
| 426 | GDK_BACKEND = "wayland"; | 510 | GDK_BACKEND = "wayland"; |
| 427 | SDL_VIDEODRIVER = "wayland"; | 511 | SDL_VIDEODRIVER = "wayland"; |
| 428 | DISPLAY = ":0"; | 512 | DISPLAY = ":0"; |
| 513 | ELECTRON_OZONE_PLATFORM_HINT = "auto"; | ||
| 514 | SSH_ASKPASS_REQUIRE = "prefer"; | ||
| 515 | SSH_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass; | ||
| 516 | SUDO_ASKPASS = lib.getExe pkgs.kdePackages.ksshaskpass; | ||
| 429 | })) | 517 | })) |
| 430 | 518 | ||
| 431 | (node "output" "eDP-1" [ | 519 | (node "output" ["eDP-1"] [ |
| 432 | (leaf "scale" 1.5) | 520 | (sleaf "scale" 1.5) |
| 433 | (leaf "position" { x = 0; y = 0; }) | 521 | (sleaf "position" { x = 0; y = 0; }) |
| 434 | ]) | 522 | ]) |
| 435 | (node "output" "Ancor Communications Inc ASUS PB287Q 0x0000DD9B" [ | 523 | (node "output" ["Ancor Communications Inc ASUS PB287Q 0x0000DD9B"] [ |
| 436 | (leaf "scale" 1.5) | 524 | (sleaf "scale" 1.5) |
| 437 | (leaf "position" { x = 2560; y = 0; }) | 525 | (sleaf "position" { x = 2560; y = 0; }) |
| 438 | ]) | 526 | ]) |
| 439 | (node "output" "HP Inc. HP 727pu CN4417143K" [ | 527 | (node "output" ["HP Inc. HP 727pu CN4417143K"] [ |
| 440 | (leaf "mode" "2560x1440@119.998") | 528 | (sleaf "mode" "2560x1440@119.998") |
| 441 | (leaf "scale" 1) | 529 | (sleaf "scale" 1) |
| 442 | (leaf "position" { x = 2560; y = 0; }) | 530 | (sleaf "position" { x = 2560; y = 0; }) |
| 443 | (flag "variable-refresh-rate") | 531 | (flag "variable-refresh-rate") |
| 444 | ]) | 532 | ]) |
| 445 | 533 | ||
| 446 | (plain "debug" [ | 534 | (plain "debug" [ |
| 447 | (leaf "render-drm-device" "/dev/dri/by-path/pci-0000:00:02.0-render") | 535 | (sleaf "render-drm-device" "/dev/dri/by-path/pci-0000:00:02.0-render") |
| 448 | ]) | 536 | ]) |
| 449 | 537 | ||
| 450 | (plain "animations" [ | 538 | (plain "animations" [ |
| 451 | (leaf "slowdown" 0.5) | 539 | (sleaf "slowdown" 0.5) |
| 452 | (plain "workspace-switch" [(flag "off")]) | 540 | (plain "workspace-switch" [(flag "off")]) |
| 453 | ]) | 541 | ]) |
| 454 | 542 | ||
| 455 | (plain "layout" [ | 543 | (plain "layout" [ |
| 456 | (leaf "gaps" 8) | 544 | (sleaf "gaps" 8) |
| 457 | (plain "struts" [ | 545 | (plain "struts" [ |
| 458 | (leaf "left" 0) | 546 | (sleaf "left" 26) |
| 459 | (leaf "right" 0) | 547 | (sleaf "right" 26) |
| 460 | (leaf "top" 0) | 548 | (sleaf "top" 0) |
| 461 | (leaf "bottom" 0) | 549 | (sleaf "bottom" 0) |
| 462 | ]) | 550 | ]) |
| 463 | (plain "border" [ | 551 | (plain "border" [ |
| 464 | (leaf "width" 2) | 552 | (sleaf "width" 2) |
| 465 | (leaf "active-gradient" { | 553 | (sleaf "active-gradient" { |
| 466 | from = "hsla(195 100% 45% 1)"; | 554 | from = "hsla(195 100% 45% 1)"; |
| 467 | to = "hsla(155 100% 37.5% 1)"; | 555 | to = "hsla(155 100% 37.5% 1)"; |
| 468 | angle = 29; | 556 | angle = 29; |
| 469 | relative-to = "workspace-view"; | 557 | relative-to = "workspace-view"; |
| 470 | }) | 558 | }) |
| 471 | (leaf "inactive-gradient" { | 559 | (sleaf "inactive-gradient" { |
| 472 | from = "hsla(0 0% 27.7% 1)"; | 560 | from = "hsla(0 0% 27.7% 1)"; |
| 473 | to = "hsla(0 0% 23% 1)"; | 561 | to = "hsla(0 0% 23% 1)"; |
| 474 | angle = 29; | 562 | angle = 29; |
| @@ -479,29 +567,29 @@ in { | |||
| 479 | (flag "off") | 567 | (flag "off") |
| 480 | ]) | 568 | ]) |
| 481 | 569 | ||
| 482 | (plain "preset-column-widths" (map (prop: leaf "proportion" prop) [ | 570 | (plain "preset-column-widths" (map (prop: sleaf "proportion" prop) [ |
| 483 | (1. / 4.) (1. / 3.) (1. / 2.) (2. / 3.) (3. / 4.) (1.) | 571 | (1. / 4.) (1. / 3.) (1. / 2.) (2. / 3.) (3. / 4.) (1.) |
| 484 | ])) | 572 | ])) |
| 485 | (plain "default-column-width" [ (leaf "proportion" (1. / 2.)) ]) | 573 | (plain "default-column-width" [ (sleaf "proportion" (1. / 2.)) ]) |
| 486 | (plain "preset-window-heights" (map (prop: leaf "proportion" prop) [ | 574 | (plain "preset-window-heights" (map (prop: sleaf "proportion" prop) [ |
| 487 | (1. / 3.) (1. / 2.) (2. / 3.) (1.) | 575 | (1. / 3.) (1. / 2.) (2. / 3.) (1.) |
| 488 | ])) | 576 | ])) |
| 489 | 577 | ||
| 490 | (flag "always-center-single-column") | 578 | (flag "always-center-single-column") |
| 491 | 579 | ||
| 492 | (plain "tab-indicator" [ | 580 | (plain "tab-indicator" [ |
| 493 | (leaf "gap" 4) | 581 | (sleaf "gap" 4) |
| 494 | (leaf "width" 8) | 582 | (sleaf "width" 8) |
| 495 | (leaf "gaps-between-tabs" 4) | 583 | (sleaf "gaps-between-tabs" 4) |
| 496 | (flag "place-within-column") | 584 | (flag "place-within-column") |
| 497 | (leaf "length" { total-proportion = 1.; }) | 585 | (sleaf "length" { total-proportion = 1.; }) |
| 498 | (leaf "active-gradient" { | 586 | (sleaf "active-gradient" { |
| 499 | from = "hsla(195 100% 60% 0.75)"; | 587 | from = "hsla(195 100% 60% 0.75)"; |
| 500 | to = "hsla(155 100% 50% 0.75)"; | 588 | to = "hsla(155 100% 50% 0.75)"; |
| 501 | angle = 29; | 589 | angle = 29; |
| 502 | relative-to = "workspace-view"; | 590 | relative-to = "workspace-view"; |
| 503 | }) | 591 | }) |
| 504 | (leaf "inactive-gradient" { | 592 | (sleaf "inactive-gradient" { |
| 505 | from = "hsla(0 0% 42% 0.66)"; | 593 | from = "hsla(0 0% 42% 0.66)"; |
| 506 | to = "hsla(0 0% 35% 0.66)"; | 594 | to = "hsla(0 0% 35% 0.66)"; |
| 507 | angle = 29; | 595 | angle = 29; |
| @@ -515,127 +603,140 @@ in { | |||
| 515 | ]) | 603 | ]) |
| 516 | 604 | ||
| 517 | (map (name: | 605 | (map (name: |
| 518 | (node "workspace" name [ | 606 | (node "workspace" [name] [ |
| 519 | (leaf "open-on-output" "eDP-1") | 607 | (sleaf "open-on-output" "eDP-1") |
| 520 | ]) | 608 | ]) |
| 521 | ) (map ({name, ...}: name) cfg.scratchspaces)) | 609 | ) (map ({name, ...}: name) cfg.scratchspaces)) |
| 522 | (map (name: | 610 | (map (name: |
| 523 | (leaf "workspace" name) | 611 | (sleaf "workspace" name) |
| 524 | ) ["comm" "web" "vid" "bmr"]) | 612 | ) ["comm" "web" "vid" "bmr"]) |
| 525 | 613 | ||
| 526 | (plain "window-rule" [ | 614 | (plain "window-rule" [ |
| 527 | (leaf "clip-to-geometry" true) | 615 | (sleaf "clip-to-geometry" true) |
| 528 | ]) | 616 | ]) |
| 529 | 617 | ||
| 530 | (plain "window-rule" [ | 618 | (plain "window-rule" [ |
| 531 | (leaf "match" { is-floating = true; }) | 619 | (sleaf "match" { is-floating = true; }) |
| 532 | (leaf "geometry-corner-radius" 8) | 620 | (sleaf "geometry-corner-radius" 8) |
| 533 | (plain "shadow" [ (flag "on") ]) | 621 | (plain "shadow" [ (flag "on") ]) |
| 534 | ]) | 622 | ]) |
| 535 | 623 | ||
| 536 | (plain "window-rule" [ | 624 | (plain "window-rule" [ |
| 537 | (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; }) | 625 | (sleaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; }) |
| 538 | (leaf "block-out-from" "screencast") | 626 | (sleaf "block-out-from" "screencast") |
| 539 | ]) | 627 | ]) |
| 540 | (plain "window-rule" [ | 628 | (plain "window-rule" (normalize-nodes [ |
| 541 | (map (title: | 629 | (map (title: |
| 542 | (leaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; }) | 630 | (sleaf "match" { app-id = "^org\\.keepassxc\\.KeePassXC$"; inherit title; }) |
| 543 | ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$"]) | 631 | ) ["^Unlock Database.*" "^Access Request.*" ".*Passkey credentials$" "Browser Access Request$"]) |
| 544 | (leaf "open-focused" true) | 632 | (sleaf "open-focused" true) |
| 545 | (leaf "open-floating" true) | 633 | (sleaf "open-floating" true) |
| 546 | ]) | 634 | ])) |
| 547 | 635 | ||
| 548 | (map ({ name, match, exclude, windowRuleExtra, ... }: | 636 | (map ({ name, match, exclude, windowRuleExtra, ... }: |
| 549 | (optional-node (match != []) (plain "window-rule" [ | 637 | (optional-node (match != []) (plain "window-rule" (normalize-nodes [ |
| 550 | (map (leaf "match") match) | 638 | (map (sleaf "match") match) |
| 551 | (map (leaf "exclude") exclude) | 639 | (map (sleaf "exclude") exclude) |
| 552 | (leaf "open-on-workspace" name) | 640 | (sleaf "open-on-workspace" name) |
| 553 | (leaf "open-maximized" true) | 641 | (sleaf "open-maximized" true) |
| 554 | windowRuleExtra | 642 | windowRuleExtra |
| 555 | ])) | 643 | ]))) |
| 556 | ) cfg.scratchspaces) | 644 | ) cfg.scratchspaces) |
| 557 | 645 | ||
| 558 | (plain "window-rule" [ | 646 | (plain "window-rule" [ |
| 559 | (leaf "match" { app-id = "^emacs$"; }) | 647 | (sleaf "match" { app-id = "^emacs$"; }) |
| 560 | (leaf "match" { app-id = "^firefox$"; }) | 648 | (sleaf "match" { app-id = "^firefox$"; }) |
| 561 | (plain "default-column-width" [(leaf "proportion" (2. / 3.))]) | 649 | (plain "default-column-width" [(sleaf "proportion" (2. / 3.))]) |
| 562 | ]) | 650 | ]) |
| 563 | (plain "window-rule" [ | 651 | (plain "window-rule" [ |
| 564 | (leaf "match" { app-id = "^kitty$"; }) | 652 | (sleaf "match" { app-id = "^kitty$"; }) |
| 565 | (leaf "match" { app-id = "^kitty-play$"; }) | 653 | (sleaf "match" { app-id = "^kitty-play$"; }) |
| 566 | (plain "default-column-width" [(leaf "proportion" (1. / 3.))]) | 654 | (plain "default-column-width" [(sleaf "proportion" (1. / 3.))]) |
| 567 | ]) | 655 | ]) |
| 568 | 656 | ||
| 569 | (plain "window-rule" [ | 657 | (plain "window-rule" [ |
| 570 | (leaf "match" { app-id = "^thunderbird$"; }) | 658 | (sleaf "match" { app-id = "^thunderbird$"; }) |
| 571 | (leaf "match" { app-id = "^Element$"; }) | 659 | (sleaf "match" { app-id = "^Element$"; }) |
| 572 | (leaf "match" { app-id = "^Rainbow$"; }) | 660 | (sleaf "match" { app-id = "^chrome-web\.openrainbow\.com__-Default$"; }) |
| 573 | (leaf "open-on-workspace" "comm") | 661 | (sleaf "open-on-workspace" "comm") |
| 574 | ]) | 662 | ]) |
| 575 | (plain "window-rule" [ | 663 | (plain "window-rule" [ |
| 576 | (leaf "match" { app-id = "^firefox$"; }) | 664 | (sleaf "match" { app-id = "^firefox$"; }) |
| 577 | (leaf "open-on-workspace" "web") | 665 | (sleaf "open-on-workspace" "web") |
| 578 | (leaf "open-maximized" true) | 666 | (sleaf "open-maximized" true) |
| 579 | ]) | 667 | ]) |
| 580 | (plain "window-rule" [ | 668 | (plain "window-rule" [ |
| 581 | (leaf "match" { app-id = "^mpv$"; }) | 669 | (sleaf "match" { app-id = "^mpv$"; }) |
| 582 | (leaf "open-on-workspace" "vid") | 670 | (sleaf "open-on-workspace" "vid") |
| 583 | (plain "default-column-width" [(leaf "proportion" 1.)]) | 671 | (plain "default-column-width" [(sleaf "proportion" 1.)]) |
| 584 | ]) | 672 | ]) |
| 585 | (plain "window-rule" [ | 673 | (plain "window-rule" [ |
| 586 | (leaf "match" { app-id = "^kitty-play$"; }) | 674 | (sleaf "match" { app-id = "^kitty-play$"; }) |
| 587 | (leaf "open-on-workspace" "vid") | 675 | (sleaf "open-on-workspace" "vid") |
| 588 | (leaf "open-focused" false) | 676 | (sleaf "open-focused" false) |
| 589 | ]) | 677 | ]) |
| 590 | (plain "window-rule" [ | 678 | (plain "window-rule" [ |
| 591 | (leaf "match" { app-id = "^pdfpc$"; }) | 679 | (sleaf "match" { app-id = "^chrome-audiobookshelf\.yggdrasil\.li__-Default$"; }) |
| 592 | (plain "default-column-width" [(leaf "proportion" 1.)]) | 680 | (sleaf "match" { app-id = "^YouTube Music Desktop App$"; }) |
| 681 | (sleaf "open-on-workspace" "vid") | ||
| 593 | ]) | 682 | ]) |
| 594 | (plain "window-rule" [ | 683 | (plain "window-rule" [ |
| 595 | (leaf "match" { app-id = "^pdfpc$"; title = "^pdfpc - presentation$"; }) | 684 | (sleaf "match" { app-id = "^pdfpc$"; }) |
| 596 | (plain "default-column-width" [(leaf "proportion" 1.)]) | 685 | (plain "default-column-width" [(sleaf "proportion" 1.)]) |
| 597 | (leaf "open-fullscreen" true) | ||
| 598 | (leaf "open-on-workspace" "bmr") | ||
| 599 | (leaf "open-focused" false) | ||
| 600 | ]) | 686 | ]) |
| 601 | (plain "window-rule" [ | 687 | (plain "window-rule" [ |
| 602 | (map (leaf "match") [ | 688 | (sleaf "match" { app-id = "^pdfpc$"; title = "^.*presentation.*$"; }) |
| 689 | (plain "default-column-width" [(sleaf "proportion" 1.)]) | ||
| 690 | (sleaf "open-fullscreen" true) | ||
| 691 | (sleaf "open-on-workspace" "bmr") | ||
| 692 | (sleaf "open-focused" false) | ||
| 693 | ]) | ||
| 694 | (plain "window-rule" (normalize-nodes [ | ||
| 695 | (map (sleaf "match") [ | ||
| 603 | { app-id = "^Gimp-"; title = "^Quit GIMP$"; } | 696 | { app-id = "^Gimp-"; title = "^Quit GIMP$"; } |
| 604 | { app-id = "^org\\.kde\\.polkit-kde-authentication-agent-1$"; } | 697 | { app-id = "^org\\.kde\\.polkit-kde-authentication-agent-1$"; } |
| 605 | { app-id = "^xdg-desktop-portal-gtk$"; } | 698 | { app-id = "^xdg-desktop-portal-gtk$"; } |
| 606 | ]) | 699 | ]) |
| 607 | (leaf "open-floating" true) | 700 | (sleaf "open-floating" true) |
| 608 | ]) | 701 | ])) |
| 609 | (plain "window-rule" [ | 702 | (plain "window-rule" [ |
| 610 | (leaf "match" { app-id = "^org\\.pwmt\\.zathura$"; }) | 703 | (sleaf "match" { app-id = "^org\\.pwmt\\.zathura$"; }) |
| 611 | (leaf "match" { app-id = "^evince$"; }) | 704 | (sleaf "match" { app-id = "^evince$"; }) |
| 612 | (leaf "default-column-display" "tabbed") | 705 | (sleaf "match" { app-id = "^org\\.gnome\\.Papers$"; }) |
| 706 | (sleaf "default-column-display" "tabbed") | ||
| 613 | ]) | 707 | ]) |
| 614 | 708 | ||
| 615 | (plain "layer-rule" [ | 709 | (plain "layer-rule" [ |
| 616 | (leaf "match" { namespace = "^notifications$"; }) | 710 | (sleaf "match" { namespace = "^notifications$"; }) |
| 617 | (leaf "match" { namespace = "^waybar$"; }) | 711 | (sleaf "match" { namespace = "^bar$"; }) |
| 618 | (leaf "match" { namespace = "^launcher$"; }) | 712 | (sleaf "match" { namespace = "^launcher$"; }) |
| 619 | (leaf "block-out-from" "screencast") | 713 | (sleaf "block-out-from" "screencast") |
| 620 | ]) | 714 | ]) |
| 621 | 715 | ||
| 622 | (plain "binds" | 716 | (plain "binds" |
| 623 | (let | 717 | (let |
| 624 | bind = name: cfg: node name (opt-props { | 718 | bind = name: cfg: node name [(lib.removeAttrs cfg ["action"])] (lib.mapAttrsToList leaf (lib.removeAttrs cfg.action ["__functor"])); |
| 625 | cooldown-ms = cfg.cooldown-ms or null; | ||
| 626 | } | ||
| 627 | // (lib.optionalAttrs (!(cfg.repeat or true)) { | ||
| 628 | repeat = false; | ||
| 629 | }) | ||
| 630 | // (lib.optionalAttrs (cfg.allow-when-locked or false) { | ||
| 631 | allow-when-locked = true; | ||
| 632 | })) (lib.mapAttrsToList leaf (lib.removeAttrs cfg.action ["__functor"])); | ||
| 633 | in | 719 | in |
| 634 | [ | 720 | normalize-nodes [ |
| 635 | (lib.mapAttrsToList bind (with config.lib.niri.actions; { | 721 | (lib.mapAttrsToList bind (with config.lib.niri.actions; { |
| 636 | "Mod+Slash".action = show-hotkey-overlay; | 722 | "Mod+Slash".action = show-hotkey-overlay; |
| 637 | 723 | ||
| 638 | "Mod+Return".action = spawn terminal; | 724 | "Mod+Return".action = spawn terminal; |
| 725 | "Mod+Shift+Return".action = | ||
| 726 | let | ||
| 727 | nushellKitty = pkgs.symlinkJoin { | ||
| 728 | name = "nushell-kitty"; | ||
| 729 | paths = [ config.programs.kitty.package ]; | ||
| 730 | buildInputs = [ pkgs.makeWrapper ]; | ||
| 731 | postBuild = '' | ||
| 732 | wrapProgram $out/bin/kitty \ | ||
| 733 | --add-flags "--config ${pkgs.writeText "kitty.conf" '' | ||
| 734 | include $HOME/${config.xdg.configFile."kitty/kitty.conf".target} | ||
| 735 | shell ${lib.getExe config.programs.nushell.package} | ||
| 736 | ''}" | ||
| 737 | ''; | ||
| 738 | }; | ||
| 739 | in spawn (lib.getExe' nushellKitty "kitty"); | ||
| 639 | "Mod+Q".action = close-window; | 740 | "Mod+Q".action = close-window; |
| 640 | "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package); | 741 | "Mod+O".action = spawn (lib.getExe config.programs.fuzzel.package); |
| 641 | "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path"; | 742 | "Mod+Shift+O".action = spawn (lib.getExe config.programs.fuzzel.package) "--list-executables-in-path"; |
| @@ -671,12 +772,12 @@ in { | |||
| 671 | done < <(export LC_ALL=C.UTF-8; echo; find "$RESULTS_DIR" -type f -printf $'%T@ %p\n' | sort -n | cut -d' ' -f2- | xargs -r cat) | 772 | done < <(export LC_ALL=C.UTF-8; echo; find "$RESULTS_DIR" -type f -printf $'%T@ %p\n' | sort -n | cut -d' ' -f2- | xargs -r cat) |
| 672 | $FOUND || echo | 773 | $FOUND || echo |
| 673 | } | 774 | } |
| 674 | FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> ") || exit $? | 775 | FUZZEL_RES=$(prev | fuzzel --dmenu --prompt "qalc> " --width=60) || exit $? |
| 675 | if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then | 776 | if [[ "$FUZZEL_RES" =~ .*\ =\ .* ]]; then |
| 676 | QALC_RES="$FUZZEL_RES" | 777 | QALC_RES="$FUZZEL_RES" |
| 677 | QALC_RET=0 | 778 | QALC_RET=0 |
| 678 | else | 779 | else |
| 679 | QALC_RES=$(qalc "$FUZZEL_RES" 2>&1) | 780 | QALC_RES=$(qalc -set "autocalc off" "$FUZZEL_RES" 2>&1) |
| 680 | QALC_RET=$? | 781 | QALC_RET=$? |
| 681 | fi | 782 | fi |
| 682 | [[ -n "$QALC_RES" ]] || exit 1 | 783 | [[ -n "$QALC_RES" ]] || exit 1 |
| @@ -696,18 +797,33 @@ in { | |||
| 696 | notify-send "$QALC_RES" | 797 | notify-send "$QALC_RES" |
| 697 | ''; | 798 | ''; |
| 698 | })); | 799 | })); |
| 800 | "Mod+Shift+U".action = | ||
| 801 | let | ||
| 802 | qalcKitty = pkgs.symlinkJoin { | ||
| 803 | name = "qalc-kitty"; | ||
| 804 | paths = [ config.programs.kitty.package ]; | ||
| 805 | buildInputs = [ pkgs.makeWrapper ]; | ||
| 806 | postBuild = '' | ||
| 807 | wrapProgram $out/bin/kitty \ | ||
| 808 | --add-flags "--config ${pkgs.writeText "kitty.conf" '' | ||
| 809 | include $HOME/${config.xdg.configFile."kitty/kitty.conf".target} | ||
| 810 | shell ${lib.getExe pkgs.libqalculate} | ||
| 811 | ''}" | ||
| 812 | ''; | ||
| 813 | }; | ||
| 814 | in spawn (lib.getExe' qalcKitty "kitty"); | ||
| 699 | "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication { | 815 | "Mod+E".action = spawn (lib.getExe (pkgs.writeShellApplication { |
| 700 | name = "emoji-fuzzel"; | 816 | name = "emoji-fuzzel"; |
| 701 | runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ]; | 817 | runtimeInputs = with pkgs; [ config.programs.fuzzel.package wtype wl-clipboard-rs ]; |
| 702 | text = '' | 818 | text = '' |
| 703 | FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " <"$HOME"/.local/share/emoji-data/list.txt) || exit $? | 819 | FUZZEL_RES=$(fuzzel --dmenu --prompt "emoji> " --cache "$HOME"/.cache/fuzzel-emoji --width=60 <"$HOME"/.local/share/emoji-data/list.txt) || exit $? |
| 704 | [[ -n "$FUZZEL_RES" ]] || exit 1 | 820 | [[ -n "$FUZZEL_RES" ]] || exit 1 |
| 705 | wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste | 821 | wl-copy "$(cut -d ':' -f 1 <<<"$FUZZEL_RES" | tr -d '\n')" && wtype -k XF86Paste |
| 706 | ''; | 822 | ''; |
| 707 | })); | 823 | })); |
| 708 | "Print".action = screenshot; | 824 | "Print".action = screenshot; |
| 709 | "Control+Print".action = screenshot-window; | 825 | "Control+Print".action = screenshot-window; |
| 710 | # "Shift+Print".action = screenshot-screen; | 826 | "Shift+Print".action = kdl.magic-leaf "screenshot-screen"; |
| 711 | "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; | 827 | "Mod+B".action = with-select-window-action ".workspace_id == ($active_workspace | tonumber)" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; |
| 712 | "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; | 828 | "Mod+Shift+B".action = with-select-window-action "true" "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; |
| 713 | 829 | ||
| @@ -746,22 +862,22 @@ in { | |||
| 746 | "Mod+Shift+Control+C".action = move-workspace-up; | 862 | "Mod+Shift+Control+C".action = move-workspace-up; |
| 747 | 863 | ||
| 748 | "Mod+ParenLeft".action = focus-workspace "comm"; | 864 | "Mod+ParenLeft".action = focus-workspace "comm"; |
| 749 | "Mod+Shift+ParenLeft".action = move-column-to-workspace "comm"; | 865 | "Mod+Shift+ParenLeft".action = kdl.magic-leaf "move-column-to-workspace" "comm"; |
| 750 | 866 | ||
| 751 | "Mod+ParenRight".action = focus-workspace "web"; | 867 | "Mod+ParenRight".action = focus-workspace "web"; |
| 752 | "Mod+Shift+ParenRight".action = move-column-to-workspace "web"; | 868 | "Mod+Shift+ParenRight".action = kdl.magic-leaf "move-column-to-workspace" "web"; |
| 753 | 869 | ||
| 754 | "Mod+BraceRight".action = focus-workspace "read"; | 870 | "Mod+BraceRight".action = focus-workspace "read"; |
| 755 | "Mod+Shift+BraceRight".action = move-column-to-workspace "read"; | 871 | "Mod+Shift+BraceRight".action = kdl.magic-leaf "move-column-to-workspace" "read"; |
| 756 | 872 | ||
| 757 | "Mod+BraceLeft".action = focus-workspace "mon"; | 873 | "Mod+BraceLeft".action = focus-workspace "mon"; |
| 758 | "Mod+Shift+BraceLeft".action = move-column-to-workspace "mon"; | 874 | "Mod+Shift+BraceLeft".action = kdl.magic-leaf "move-column-to-workspace" "mon"; |
| 759 | 875 | ||
| 760 | "Mod+Asterisk".action = focus-workspace "vid"; | 876 | "Mod+Asterisk".action = focus-workspace "vid"; |
| 761 | "Mod+Shift+Asterisk".action = move-column-to-workspace "vid"; | 877 | "Mod+Shift+Asterisk".action = kdl.magic-leaf "move-column-to-workspace" "vid"; |
| 762 | 878 | ||
| 763 | "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; | 879 | "Mod+Plus".action = with-unnamed-workspace-action ''{"Action":{"FocusWorkspace":{"reference":{"Id": .id}}}}''; |
| 764 | "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}}}}''; | 880 | "Mod+Shift+Plus".action = with-unnamed-workspace-action ''{"Action":{"MoveColumnToWorkspace":{"reference":{"Id": .id}, "focus": true}}}''; |
| 765 | 881 | ||
| 766 | "Mod+M".action = consume-or-expel-window-left; | 882 | "Mod+M".action = consume-or-expel-window-left; |
| 767 | "Mod+W".action = consume-or-expel-window-right; | 883 | "Mod+W".action = consume-or-expel-window-right; |
| @@ -769,10 +885,11 @@ in { | |||
| 769 | "Mod+Shift+M".action = toggle-column-tabbed-display; | 885 | "Mod+Shift+M".action = toggle-column-tabbed-display; |
| 770 | 886 | ||
| 771 | "Mod+R".action = switch-preset-column-width; | 887 | "Mod+R".action = switch-preset-column-width; |
| 772 | "Mod+Shift+R".action = switch-preset-window-height; | 888 | "Mod+Shift+R".action = maximize-column; |
| 889 | "Mod+Shift+Ctrl+R".action = switch-preset-window-height; | ||
| 773 | "Mod+F".action = center-column; | 890 | "Mod+F".action = center-column; |
| 774 | "Mod+Shift+F".action = maximize-column; | 891 | "Mod+Shift+F".action = toggle-windowed-fullscreen; |
| 775 | "Mod+Shift+Ctrl+F".action = fullscreen-window; | 892 | "Mod+Ctrl+Shift+F".action = fullscreen-window; |
| 776 | 893 | ||
| 777 | "Mod+V".action = switch-focus-between-floating-and-tiling; | 894 | "Mod+V".action = switch-focus-between-floating-and-tiling; |
| 778 | "Mod+Shift+V".action = toggle-window-floating; | 895 | "Mod+Shift+V".action = toggle-window-floating; |
| @@ -783,58 +900,77 @@ in { | |||
| 783 | "Mod+Right".action = set-column-width "+10%"; | 900 | "Mod+Right".action = set-column-width "+10%"; |
| 784 | 901 | ||
| 785 | "Mod+Shift+Z" = { | 902 | "Mod+Shift+Z" = { |
| 786 | action = spawn (lib.getExe niri) "msg" "action" "power-off-monitors"; | 903 | action = power-off-monitors; |
| 787 | allow-when-locked = true; | 904 | allow-when-locked = true; |
| 788 | }; | 905 | }; |
| 789 | "Mod+Shift+L".action = spawn loginctl "lock-session"; | ||
| 790 | "Mod+Shift+E".action = quit; | 906 | "Mod+Shift+E".action = quit; |
| 791 | "Mod+Shift+Minus" = { | 907 | |
| 792 | action = spawn systemctl "suspend"; | 908 | # "Mod+Semicolon".action = spawn makoctl "dismiss" "--group"; |
| 909 | # "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all"; | ||
| 910 | # "Mod+Period".action = spawn makoctl "menu" "--" (lib.getExe config.programs.fuzzel.package) "--dmenu"; | ||
| 911 | # "Mod+Comma".action = spawn makoctl "restore"; | ||
| 912 | |||
| 913 | "Mod+Control+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"FocusWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}"; | ||
| 914 | "Mod+Control+Shift+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"MoveColumnToWorkspace\":{\"reference\":{\"Id\": $workspace_id}, \"focus\": true}}}"; | ||
| 915 | |||
| 916 | "Mod+X".action = set-dynamic-cast-window; | ||
| 917 | "Mod+Shift+X".action = set-dynamic-cast-monitor; | ||
| 918 | "Mod+Control+Shift+X".action = clear-dynamic-cast-target; | ||
| 919 | |||
| 920 | "Mod+D".action = with-urgent-window-action "{\"Action\":{\"FocusWindow\":{\"id\": .id}}}"; | ||
| 921 | "Mod+Shift+D".action = with-focused-window-action "{\"Action\":{\"UnsetUrgent\":{\"id\": .id}}}"; | ||
| 922 | |||
| 923 | "Mod+K".action = spawn (lib.getExe' pkgs.worktime "worktime-ui"); | ||
| 924 | "Mod+Shift+K".action = spawn (lib.getExe' pkgs.worktime "worktime-stop"); | ||
| 925 | })) | ||
| 926 | (lib.mapAttrsToList (name: cfg: node name [(lib.removeAttrs cfg ["action"])] [cfg.action]) (let | ||
| 927 | shell = obj: leaf "send-unix" [ | ||
| 928 | { path = ''''${XDG_RUNTIME_DIR}/shell.sock''; } | ||
| 929 | (builtins.toJSON obj + "\n") | ||
| 930 | ]; | ||
| 931 | in { | ||
| 932 | "XF86AudioRaiseVolume" = { | ||
| 793 | allow-when-locked = true; | 933 | allow-when-locked = true; |
| 934 | action = shell { Volume.volume = "up"; }; | ||
| 794 | }; | 935 | }; |
| 795 | "Mod+Shift+Control+Minus" = { | 936 | "XF86AudioLowerVolume" = { |
| 796 | action = spawn systemctl "hibernate"; | ||
| 797 | allow-when-locked = true; | 937 | allow-when-locked = true; |
| 938 | action = shell { Volume.volume = "down"; }; | ||
| 798 | }; | 939 | }; |
| 799 | "Mod+Shift+P" = { | 940 | "XF86AudioMute" = { |
| 800 | action = spawn (lib.getExe pkgs.playerctl) "-a" "pause"; | ||
| 801 | allow-when-locked = true; | 941 | allow-when-locked = true; |
| 942 | action = shell { Volume.muted = "toggle"; }; | ||
| 802 | }; | 943 | }; |
| 803 | 944 | "XF86AudioMicMute" = { | |
| 804 | "XF86MonBrightnessUp" = { | ||
| 805 | action = spawn swayosd-client "--brightness" "raise"; | ||
| 806 | allow-when-locked = true; | 945 | allow-when-locked = true; |
| 946 | action = shell { Volume."mic-muted" = "toggle"; }; | ||
| 807 | }; | 947 | }; |
| 808 | "XF86MonBrightnessDown" = { | 948 | "XF86MonBrightnessUp" = { |
| 809 | action = spawn swayosd-client "--brightness" "lower"; | 949 | action = shell { Brightness = "up"; }; |
| 810 | allow-when-locked = true; | 950 | allow-when-locked = true; |
| 811 | }; | 951 | }; |
| 812 | "XF86AudioRaiseVolume" = { | 952 | "XF86MonBrightnessDown" = { |
| 813 | action = spawn swayosd-client "--output-volume" "raise"; | 953 | action = shell { Brightness = "down"; }; |
| 814 | allow-when-locked = true; | 954 | allow-when-locked = true; |
| 815 | }; | 955 | }; |
| 816 | "XF86AudioLowerVolume" = { | 956 | "Mod+Shift+L".action = shell { LockSession = {}; }; |
| 817 | action = spawn swayosd-client "--output-volume" "lower"; | 957 | "Mod+Shift+Minus" = { |
| 958 | action = shell { Suspend = {}; }; | ||
| 818 | allow-when-locked = true; | 959 | allow-when-locked = true; |
| 819 | }; | 960 | }; |
| 820 | "XF86AudioMute" = { | 961 | "Mod+Shift+Control+Minus" = { |
| 821 | action = spawn swayosd-client "--output-volume" "mute-toggle"; | 962 | action = shell { Hibernate = {}; }; |
| 822 | allow-when-locked = true; | 963 | allow-when-locked = true; |
| 823 | }; | 964 | }; |
| 824 | "XF86AudioMicMute" = { | 965 | "Mod+Shift+P" = { |
| 825 | action = spawn swayosd-client "--input-volume" "mute-toggle"; | 966 | action = shell { Mpris = { PauseAll = {}; }; }; |
| 826 | allow-when-locked = true; | 967 | allow-when-locked = true; |
| 827 | }; | 968 | }; |
| 828 | 969 | "Mod+Semicolon".action = shell { Notifications = { DismissGroup = {}; }; }; | |
| 829 | "Mod+Semicolon".action = spawn makoctl "dismiss" "--group"; | 970 | "Mod+Shift+Semicolon".action = shell { Notifications = { DismissAll = {}; }; }; |
| 830 | "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all"; | ||
| 831 | "Mod+Period".action = spawn makoctl "menu" (lib.getExe config.programs.fuzzel.package) "--dmenu"; | ||
| 832 | "Mod+Comma".action = spawn makoctl "restore"; | ||
| 833 | |||
| 834 | "Mod+Control+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"FocusWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}"; | ||
| 835 | "Mod+Control+Shift+W".action = with-empty-unnamed-workspace-action "{\"Action\":{\"MoveColumnToWorkspace\":{\"reference\":{\"Id\": $workspace_id}}}}"; | ||
| 836 | })) | 971 | })) |
| 837 | (map ({ name, selector, spawn, key, ...}: if key != null && selector != null && spawn != null then bind key { action = focus-or-spawn-action selector name spawn; } else null) cfg.scratchspaces) | 972 | (map ({ name, selector, spawn, key, ...}: if key != null && selector != null && spawn != null then bind key { action = focus-or-spawn-action selector name spawn; } else null) cfg.scratchspaces) |
| 973 | (map ({ name, moveKey, ...}: if moveKey != null then bind moveKey { action = kdl.magic-leaf "move-column-to-workspace" name; } else null) cfg.scratchspaces) | ||
| 838 | ] | 974 | ] |
| 839 | )) | 975 | )) |
| 840 | ]; | 976 | ]; |
diff --git a/accounts/gkleen@sif/niri/mako.nix b/accounts/gkleen@sif/niri/mako.nix index 2788fb82..3d246d96 100644 --- a/accounts/gkleen@sif/niri/mako.nix +++ b/accounts/gkleen@sif/niri/mako.nix | |||
| @@ -1,39 +1,32 @@ | |||
| 1 | { config, lib, pkgs, ... }: | 1 | { config, lib, pkgs, ... }: |
| 2 | { | 2 | { |
| 3 | config = { | 3 | config = lib.mkIf false { |
| 4 | services.mako = { | 4 | services.mako = { |
| 5 | enable = true; | 5 | enable = true; |
| 6 | font = "Fira Sans 10"; | 6 | settings = { |
| 7 | format = "<i>%s</i>\\n%b"; | 7 | font = "Fira Sans 10"; |
| 8 | margin = "2"; | 8 | format = "<i>%s</i>\\n%b"; |
| 9 | maxVisible = -1; | 9 | margin = "2"; |
| 10 | backgroundColor = "#000000dd"; | 10 | max-visible = -1; |
| 11 | progressColor = "source #223544ff"; | 11 | background-color = "#000000dd"; |
| 12 | width = 384; | 12 | progress-color = "source #223544ff"; |
| 13 | extraConfig = '' | 13 | width = 384; |
| 14 | outer-margin=1 | 14 | outer-margin = 1; |
| 15 | max-history=100 | 15 | max-history = 100; |
| 16 | max-icon-size=48 | 16 | max-icon-size = 48; |
| 17 | 17 | ||
| 18 | [grouped] | 18 | grouped.format = "<b>(%g)</b> <i>%s</i>\\n%b"; |
| 19 | format=<b>(%g)</b> <i>%s</i>\n%b | 19 | "urgency=low".text-color = "#999999ff"; |
| 20 | 20 | "urgency=critical".background-color = "#900000dd"; | |
| 21 | [urgency=low] | 21 | "app-name=Element".group-by = "summary"; |
| 22 | text-color=#999999ff | 22 | "app-name=poweralertd" = { |
| 23 | 23 | history = false; | |
| 24 | [urgency=critical] | 24 | ignore-timeout = true; |
| 25 | background-color=#900000dd | 25 | default-timeout = 2000; |
| 26 | 26 | }; | |
| 27 | [app-name=Element] | 27 | "app-name=worktime".history = false; |
| 28 | group-by=summary | 28 | "mode=silent".invisible = true; |
| 29 | 29 | }; | |
| 30 | [app-name=poweralertd] | ||
| 31 | ignore-timeout=1 | ||
| 32 | default-timeout=2000 | ||
| 33 | |||
| 34 | [mode=silent] | ||
| 35 | invisible=1 | ||
| 36 | ''; | ||
| 37 | package = pkgs.symlinkJoin { | 30 | package = pkgs.symlinkJoin { |
| 38 | name = "${pkgs.mako.name}-wrapped"; | 31 | name = "${pkgs.mako.name}-wrapped"; |
| 39 | paths = with pkgs; [ mako ]; | 32 | paths = with pkgs; [ mako ]; |
diff --git a/accounts/gkleen@sif/niri/swayosd.nix b/accounts/gkleen@sif/niri/swayosd.nix deleted file mode 100644 index 984927c2..00000000 --- a/accounts/gkleen@sif/niri/swayosd.nix +++ /dev/null | |||
| @@ -1,65 +0,0 @@ | |||
| 1 | { pkgs, ... }: | ||
| 2 | { | ||
| 3 | config = { | ||
| 4 | services.swayosd = { | ||
| 5 | enable = true; | ||
| 6 | topMargin = 0.946154; | ||
| 7 | stylePath = pkgs.runCommand "style.css" { | ||
| 8 | src = pkgs.writeText "style.scss" '' | ||
| 9 | window#osd { | ||
| 10 | padding: 12px 20px; | ||
| 11 | border-radius: 999px; | ||
| 12 | border: none; | ||
| 13 | background: rgba(0, 0, 0, 0.87); | ||
| 14 | |||
| 15 | #container { | ||
| 16 | margin: 16px; | ||
| 17 | } | ||
| 18 | |||
| 19 | image, | ||
| 20 | label { | ||
| 21 | color: rgb(255, 255, 255); | ||
| 22 | |||
| 23 | &:disabled { | ||
| 24 | opacity: 1; | ||
| 25 | color: rgb(84, 84, 84); | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | progressbar { | ||
| 30 | min-height: 6px; | ||
| 31 | border-radius: 999px; | ||
| 32 | background: transparent; | ||
| 33 | border: none; | ||
| 34 | |||
| 35 | trough, progress { | ||
| 36 | min-height: inherit; | ||
| 37 | border-radius: inherit; | ||
| 38 | border: none; | ||
| 39 | } | ||
| 40 | |||
| 41 | trough { | ||
| 42 | background: rgb(127, 127, 127); | ||
| 43 | } | ||
| 44 | progress { | ||
| 45 | background: rgb(255, 255, 255); | ||
| 46 | } | ||
| 47 | |||
| 48 | &:disabled { | ||
| 49 | opacity: 1; | ||
| 50 | |||
| 51 | trough { | ||
| 52 | background: rgb(19, 19, 19); | ||
| 53 | } | ||
| 54 | progress { | ||
| 55 | background: rgb(38, 38, 38); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | ''; | ||
| 61 | buildInputs = with pkgs; [sass]; | ||
| 62 | } "scss -C --sourcemap=none --style=compact $src $out"; | ||
| 63 | }; | ||
| 64 | }; | ||
| 65 | } | ||
diff --git a/accounts/gkleen@sif/niri/waybar.nix b/accounts/gkleen@sif/niri/waybar.nix deleted file mode 100644 index bae818f6..00000000 --- a/accounts/gkleen@sif/niri/waybar.nix +++ /dev/null | |||
| @@ -1,347 +0,0 @@ | |||
| 1 | { lib, config, pkgs, ... }: | ||
| 2 | let | ||
| 3 | swayosd-client = lib.getExe' config.services.swayosd.package "swayosd-client"; | ||
| 4 | in { | ||
| 5 | config = { | ||
| 6 | programs.waybar = { | ||
| 7 | enable = true; | ||
| 8 | systemd = { | ||
| 9 | enable = true; | ||
| 10 | target = "graphical-session.target"; | ||
| 11 | }; | ||
| 12 | settings = let | ||
| 13 | windowRewrites = { | ||
| 14 | "(.*) — Mozilla Firefox" = "$1"; | ||
| 15 | "(.*) - Mozilla Thunderbird" = "$1"; | ||
| 16 | "(.*) - mpv" = "$1"; | ||
| 17 | }; | ||
| 18 | iconSize = 11; | ||
| 19 | in [ | ||
| 20 | { | ||
| 21 | layer = "top"; | ||
| 22 | position = "top"; | ||
| 23 | height = 14; | ||
| 24 | output = [ "eDP-1" "DP-2" "DP-3" ]; | ||
| 25 | modules-left = [ "niri/workspaces" ]; | ||
| 26 | modules-center = [ "niri/window" ]; | ||
| 27 | modules-right = [ "custom/worktime" "custom/worktime-today" | ||
| 28 | "custom/weather" | ||
| 29 | "custom/keymap" | ||
| 30 | "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "custom/mako" "clock" ]; | ||
| 31 | |||
| 32 | "custom/mako" = { | ||
| 33 | format = "{}"; | ||
| 34 | return-type = "json"; | ||
| 35 | exec = pkgs.writers.writePython3 "mako-silent" { libraries = [ pkgs.python3Packages.dbus-next ]; } '' | ||
| 36 | from dbus_next.aio import MessageBus | ||
| 37 | |||
| 38 | import asyncio | ||
| 39 | |||
| 40 | import json | ||
| 41 | |||
| 42 | |||
| 43 | loop = asyncio.new_event_loop() | ||
| 44 | asyncio.set_event_loop(loop) | ||
| 45 | |||
| 46 | |||
| 47 | async def main(): | ||
| 48 | bus = await MessageBus().connect() | ||
| 49 | # the introspection xml would normally be included in your project, but | ||
| 50 | # this is convenient for development | ||
| 51 | introspection = await bus.introspect('org.freedesktop.Notifications', '/fr/emersion/Mako') # noqa: E501 | ||
| 52 | |||
| 53 | obj = bus.get_proxy_object('org.freedesktop.Notifications', '/fr/emersion/Mako', introspection) # noqa: E501 | ||
| 54 | mako = obj.get_interface('fr.emersion.Mako') | ||
| 55 | properties = obj.get_interface('org.freedesktop.DBus.Properties') | ||
| 56 | |||
| 57 | async def print_mode(): | ||
| 58 | modes = await mako.get_modes() | ||
| 59 | is_silent = "silent" in modes | ||
| 60 | icon = "󰂛" if is_silent else "󰂚" | ||
| 61 | text = f"<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>" # noqa: E501 | ||
| 62 | if is_silent: | ||
| 63 | text = f"<span color=\"#ffffff\">{text}</span>" | ||
| 64 | print(json.dumps({'text': text, 'tooltip': ', '.join(modes)}, separators=(',', ':')), flush=True) # noqa: E501 | ||
| 65 | |||
| 66 | async def on_properties_changed(interface_name, changed_properties, invalidated_properties): # noqa: E501 | ||
| 67 | if "Modes" not in invalidated_properties: | ||
| 68 | return | ||
| 69 | |||
| 70 | await print_mode() | ||
| 71 | |||
| 72 | properties.on_properties_changed(on_properties_changed) | ||
| 73 | await print_mode() | ||
| 74 | |||
| 75 | await loop.create_future() | ||
| 76 | |||
| 77 | |||
| 78 | loop.run_until_complete(main()) | ||
| 79 | ''; | ||
| 80 | on-click = "makoctl mode -t silent"; | ||
| 81 | }; | ||
| 82 | "custom/weather" = { | ||
| 83 | format = "{}"; | ||
| 84 | tooltip = true; | ||
| 85 | interval = 3600; | ||
| 86 | exec = "${lib.getExe pkgs.wttrbar} --hide-conditions --nerd --custom-indicator \"<span font=\\\"Symbols Nerd Font Mono\\\" size=\\\"100%\\\">{ICON}</span> {FeelsLikeC}°\""; | ||
| 87 | return-type = "json"; | ||
| 88 | }; | ||
| 89 | "custom/keymap" = { | ||
| 90 | format = "{}"; | ||
| 91 | tooltip = true; | ||
| 92 | return-type = "json"; | ||
| 93 | exec = pkgs.writers.writePython3 "keymap" {} '' | ||
| 94 | import os | ||
| 95 | import socket | ||
| 96 | import json | ||
| 97 | |||
| 98 | |||
| 99 | def output(keymap): | ||
| 100 | short = keymap | ||
| 101 | if keymap == "English (programmer Dvorak)": | ||
| 102 | short = "dvp" | ||
| 103 | elif keymap == "English (US)": | ||
| 104 | short = "<span color=\"#ffffff\">us</span>" | ||
| 105 | print(json.dumps({'text': short, 'tooltip': keymap}, separators=(',', ':')), flush=True) # noqa: E501 | ||
| 106 | |||
| 107 | |||
| 108 | keyboard_layouts = [] | ||
| 109 | |||
| 110 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 111 | sock.connect(os.environ["NIRI_SOCKET"]) | ||
| 112 | sock.send(b"\"EventStream\"\n") | ||
| 113 | for line in sock.makefile(buffering=1, encoding='utf-8'): | ||
| 114 | if line_json := json.loads(line): | ||
| 115 | if "KeyboardLayoutsChanged" in line_json: | ||
| 116 | keyboard_layouts = line_json["KeyboardLayoutsChanged"]["keyboard_layouts"]["names"] # noqa: E501 | ||
| 117 | output(keyboard_layouts[line_json["KeyboardLayoutsChanged"]["keyboard_layouts"]["current_idx"]]) # noqa: E501 | ||
| 118 | if "KeyboardLayoutSwitched" in line_json: | ||
| 119 | output(keyboard_layouts[line_json["KeyboardLayoutSwitched"]["idx"]]) # noqa: E501 | ||
| 120 | ''; | ||
| 121 | on-click = "niri msg action switch-layout next"; | ||
| 122 | }; | ||
| 123 | "custom/worktime" = { | ||
| 124 | interval = 60; | ||
| 125 | exec = "${lib.getExe pkgs.worktime} time --waybar"; | ||
| 126 | return-type = "json"; | ||
| 127 | }; | ||
| 128 | "custom/worktime-today" = { | ||
| 129 | interval = 60; | ||
| 130 | exec = "${lib.getExe pkgs.worktime} today --waybar"; | ||
| 131 | return-type = "json"; | ||
| 132 | }; | ||
| 133 | "niri/workspaces" = { | ||
| 134 | ignore = map ({ name, ... }: name) config.programs.niri.scratchspaces; | ||
| 135 | }; | ||
| 136 | "niri/window" = { | ||
| 137 | separate-outputs = true; | ||
| 138 | icon = true; | ||
| 139 | icon-size = 14; | ||
| 140 | rewrite = windowRewrites; | ||
| 141 | }; | ||
| 142 | clock = { | ||
| 143 | interval = 1; | ||
| 144 | # timezone = "Europe/Berlin"; | ||
| 145 | format = "W{:%V-%u %F %H:%M:%S%Ez}"; | ||
| 146 | tooltip-format = "<tt><small>{calendar}</small></tt>"; | ||
| 147 | calendar = { | ||
| 148 | mode = "year"; | ||
| 149 | mode-mon-col = 3; | ||
| 150 | weeks-pos = "left"; | ||
| 151 | on-scroll = 1; | ||
| 152 | format = { | ||
| 153 | months = "<span color='#ffead3'><b>{}</b></span>"; | ||
| 154 | days = "{}"; | ||
| 155 | weeks = "<span color='#99ffdd'><b>{}</b></span>"; | ||
| 156 | weekdays = "<span color='#ffcc66'><b>{}</b></span>"; | ||
| 157 | today = "<span color='#ff6699'><b>{}</b></span>"; | ||
| 158 | }; | ||
| 159 | }; | ||
| 160 | }; | ||
| 161 | battery = { | ||
| 162 | format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; | ||
| 163 | icon-size = iconSize - 2; | ||
| 164 | states = { warning = 30; critical = 15; }; | ||
| 165 | format-icons = ["󰂎" "󰁺" "󰁻" "󰁼" "󰁽" "󰁾" "󰁿" "󰂀" "󰂁" "󰂂" "󰁹" ]; | ||
| 166 | format-charging = "󰂄"; | ||
| 167 | format-plugged = "󰚥"; | ||
| 168 | tooltip-format = "{capacity}% {timeTo}"; | ||
| 169 | interval = 20; | ||
| 170 | }; | ||
| 171 | tray = { | ||
| 172 | icon-size = 16; | ||
| 173 | # show-passive-items = true; | ||
| 174 | spacing = 1; | ||
| 175 | }; | ||
| 176 | privacy = { | ||
| 177 | icon-spacing = 7; | ||
| 178 | icon-size = iconSize; | ||
| 179 | modules = [ | ||
| 180 | { type = "screenshare"; } | ||
| 181 | { type = "audio-in"; } | ||
| 182 | ]; | ||
| 183 | }; | ||
| 184 | idle_inhibitor = { | ||
| 185 | format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; | ||
| 186 | icon-size = iconSize; | ||
| 187 | format-icons = { activated = "󰈈"; deactivated = "󰈉"; }; | ||
| 188 | timeout = 120; | ||
| 189 | }; | ||
| 190 | backlight = { | ||
| 191 | format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; | ||
| 192 | icon-size = iconSize; | ||
| 193 | tooltip-format = "{percent}%"; | ||
| 194 | format-icons = ["󰃚" "󰃛" "󰃜" "󰃝" "󰃞" "󰃟" "󰃠"]; | ||
| 195 | on-scroll-up = "${swayosd-client} --brightness raise"; | ||
| 196 | on-scroll-down = "${swayosd-client} --brightness lower"; | ||
| 197 | }; | ||
| 198 | wireplumber = { | ||
| 199 | format = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>"; | ||
| 200 | icon-size = iconSize; | ||
| 201 | tooltip-format = "{volume}% {node_name}"; | ||
| 202 | format-icons = ["󰕿" "󰖀" "󰕾"]; | ||
| 203 | format-muted = "<span font=\"Symbols Nerd Font Mono\" size=\"90%\">󰝟</span>"; | ||
| 204 | # ignored-sinks = ["Easy Effects Sink"]; | ||
| 205 | on-scroll-up = "${swayosd-client} --output-volume raise"; | ||
| 206 | on-scroll-down = "${swayosd-client} --output-volume lower"; | ||
| 207 | on-click = "${swayosd-client} --output-volume mute-toggle"; | ||
| 208 | }; | ||
| 209 | } | ||
| 210 | { | ||
| 211 | layer = "top"; | ||
| 212 | position = "top"; | ||
| 213 | height = 14; | ||
| 214 | output = [ "!eDP-1" "!DP-2" "!DP-3" ]; | ||
| 215 | modules-left = [ "niri/workspaces" ]; | ||
| 216 | modules-center = [ "niri/window" ]; | ||
| 217 | modules-right = [ "clock" ]; | ||
| 218 | |||
| 219 | "niri/workspaces" = { | ||
| 220 | ignore = map ({ name, ... }: name) config.programs.niri.scratchspaces; | ||
| 221 | }; | ||
| 222 | "niri/window" = { | ||
| 223 | separate-outputs = true; | ||
| 224 | icon = true; | ||
| 225 | icon-size = 14; | ||
| 226 | rewrite = windowRewrites; | ||
| 227 | }; | ||
| 228 | clock = { | ||
| 229 | interval = 1; | ||
| 230 | # timezone = "Europe/Berlin"; | ||
| 231 | format = "{:%H:%M}"; | ||
| 232 | tooltip-format = "W{:%V-%u %F %H:%M:%S%Ez}"; | ||
| 233 | }; | ||
| 234 | } | ||
| 235 | ]; | ||
| 236 | style = '' | ||
| 237 | @define-color white #ffffff; | ||
| 238 | @define-color grey #555555; | ||
| 239 | @define-color blue #1a8fff; | ||
| 240 | @define-color green #23fd00; | ||
| 241 | @define-color orange #f28a21; | ||
| 242 | @define-color red #f2201f; | ||
| 243 | |||
| 244 | * { | ||
| 245 | border: none; | ||
| 246 | font-family: "Fira Sans"; | ||
| 247 | font-size: 10pt; | ||
| 248 | min-height: 0; | ||
| 249 | } | ||
| 250 | |||
| 251 | window#waybar { | ||
| 252 | background-color: rgba(0, 0, 0, 0.66); | ||
| 253 | color: @white; | ||
| 254 | } | ||
| 255 | |||
| 256 | .modules-left { | ||
| 257 | margin-left: 12px; | ||
| 258 | } | ||
| 259 | .modules-right { | ||
| 260 | margin-right: 12px; | ||
| 261 | } | ||
| 262 | |||
| 263 | .module { | ||
| 264 | margin: 0 5px; | ||
| 265 | } | ||
| 266 | |||
| 267 | #workspaces button { | ||
| 268 | color: @white; | ||
| 269 | padding: 2px 5px; | ||
| 270 | } | ||
| 271 | #workspaces button.empty { | ||
| 272 | color: @grey; | ||
| 273 | } | ||
| 274 | #workspaces button.active { | ||
| 275 | color: @green; | ||
| 276 | } | ||
| 277 | #workspaces button.urgent { | ||
| 278 | color: @red; | ||
| 279 | } | ||
| 280 | |||
| 281 | #custom-weather, #custom-keymap, #custom-worktime, #custom-worktime-today { | ||
| 282 | color: @grey; | ||
| 283 | margin: 0 5px; | ||
| 284 | } | ||
| 285 | #custom-weather { | ||
| 286 | margin-right: 3px; | ||
| 287 | } | ||
| 288 | #custom-keymap { | ||
| 289 | margin-left: 3px; | ||
| 290 | margin-right: 3px; | ||
| 291 | } | ||
| 292 | |||
| 293 | #tray { | ||
| 294 | margin: 0; | ||
| 295 | } | ||
| 296 | #battery, #idle_inhibitor, #backlight, #wireplumber, #custom-mako { | ||
| 297 | color: @grey; | ||
| 298 | margin: 0 5px 0 2px; | ||
| 299 | } | ||
| 300 | #idle_inhibitor { | ||
| 301 | margin-right: 4px; | ||
| 302 | margin-left: 6px; | ||
| 303 | } | ||
| 304 | #custom-mako { | ||
| 305 | margin-right: 2px; | ||
| 306 | margin-left: 3px; | ||
| 307 | } | ||
| 308 | #battery { | ||
| 309 | margin-right: 3px; | ||
| 310 | } | ||
| 311 | #battery.discharging { | ||
| 312 | color: @white; | ||
| 313 | } | ||
| 314 | #battery.warning { | ||
| 315 | color: @orange; | ||
| 316 | } | ||
| 317 | #battery.critical { | ||
| 318 | color: @red; | ||
| 319 | } | ||
| 320 | #battery.charging { | ||
| 321 | color: @white; | ||
| 322 | } | ||
| 323 | #idle_inhibitor.activated { | ||
| 324 | color: @white; | ||
| 325 | } | ||
| 326 | #custom-worktime.running, #custom-worktime-today.running { | ||
| 327 | color: @white; | ||
| 328 | } | ||
| 329 | #custom-worktime.over, #custom-worktime-today.over { | ||
| 330 | color: @orange; | ||
| 331 | } | ||
| 332 | |||
| 333 | #idle_inhibitor { | ||
| 334 | padding-top: 1px; | ||
| 335 | } | ||
| 336 | |||
| 337 | #privacy { | ||
| 338 | color: @red; | ||
| 339 | margin: -1px 4px 0px 3px; | ||
| 340 | } | ||
| 341 | #clock { | ||
| 342 | /* margin-right: 5px; */ | ||
| 343 | } | ||
| 344 | ''; | ||
| 345 | }; | ||
| 346 | }; | ||
| 347 | } | ||
diff --git a/accounts/gkleen@sif/shell/default.nix b/accounts/gkleen@sif/shell/default.nix new file mode 100644 index 00000000..44462865 --- /dev/null +++ b/accounts/gkleen@sif/shell/default.nix | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | { config, pkgs, lib, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | programs.quickshell = { | ||
| 6 | enable = true; | ||
| 7 | package = pkgs.symlinkJoin { | ||
| 8 | pname = pkgs.quickshell.pname + "-wrapped"; | ||
| 9 | inherit (pkgs.quickshell) version meta; | ||
| 10 | paths = [ pkgs.quickshell ]; | ||
| 11 | buildInputs = [ pkgs.makeWrapper ]; | ||
| 12 | postBuild = '' | ||
| 13 | for binary in quickshell qs; do | ||
| 14 | wrapProgram $out/bin/$binary \ | ||
| 15 | --prefix QML_IMPORT_PATH : ${pkgs.qt6Packages.callPackage ./quickshell-plugins {}}/${pkgs.qt6.qtbase.qtQmlPrefix} | ||
| 16 | done | ||
| 17 | ''; | ||
| 18 | }; | ||
| 19 | config = { | ||
| 20 | src = ./quickshell; | ||
| 21 | replacements = { | ||
| 22 | ignore_workspaces = builtins.toJSON (map ({ name, ... }: name) config.programs.niri.scratchspaces); | ||
| 23 | wallpapers = builtins.toJSON (pkgs.stdenvNoCC.mkDerivation { | ||
| 24 | name = "wallpapers"; | ||
| 25 | srcs = [ | ||
| 26 | (pkgs.fetchurl { | ||
| 27 | url = "https://esawebb.org/media/archives/images/publicationtiff10k/carinanebula3.tif"; | ||
| 28 | hash = "sha256-YxZEweDKJfvfrdxb/QFmgJhcZDEJYxotoHrG+RRn1tw="; | ||
| 29 | }) | ||
| 30 | (pkgs.fetchurl { | ||
| 31 | url = "https://esawebb.org/media/archives/images/original/pillarsofcreation_composite.tif"; | ||
| 32 | hash = "sha256-qRiODxR0lZWdxgYXna0fNRFFDErpBJDwOJuQl6sNjRc="; | ||
| 33 | }) | ||
| 34 | (pkgs.fetchurl { | ||
| 35 | url = "https://esawebb.org/media/archives/images/publicationtiff10k/weic2212a.tif"; | ||
| 36 | hash = "sha256-l2fqE/z//C1a0xkvZwsnwPbTSb+WuA11h+SUl3E1dhw="; | ||
| 37 | }) | ||
| 38 | (pkgs.fetchurl { | ||
| 39 | url = "https://esawebb.org/media/archives/images/publicationtiff10k/weic2415a.tif"; | ||
| 40 | hash = "sha256-onBy7cPoUpDuzQStbY2E+qmlGgSLXPwFCLX53ukAb4c="; | ||
| 41 | }) | ||
| 42 | (pkgs.fetchurl { | ||
| 43 | url = "https://esawebb.org/media/archives/images/publicationtiff10k/weic2330a.tif"; | ||
| 44 | hash = "sha256-nn0ZtjZIrPcpj3YcLTsrL7XiXvyh3QYgCSmdDMD+3OM="; | ||
| 45 | }) | ||
| 46 | (pkgs.fetchurl { | ||
| 47 | url = "https://esawebb.org/media/archives/images/original/weic2426a.tif"; | ||
| 48 | hash = "sha256-EDnfPn3GE9jt6XPqiGInP7E2F3Az7d25NqATSWltDv0="; | ||
| 49 | }) | ||
| 50 | (pkgs.fetchurl { | ||
| 51 | url = "https://esawebb.org/media/archives/images/original/weic2503a.tif"; | ||
| 52 | hash = "sha256-3/RX6RQp8naELcgReHPd5/zhJkoCjnA10w5BEnNo+qI="; | ||
| 53 | }) | ||
| 54 | (pkgs.fetchurl { | ||
| 55 | url = "https://esawebb.org/media/archives/images/original/weic2506a.tif"; | ||
| 56 | hash = "sha256-aDld0aoY1owRxDVf7Jcyw71TH42M1foYotxn2thyFYw="; | ||
| 57 | }) | ||
| 58 | (pkgs.fetchurl { | ||
| 59 | url = "https://esawebb.org/media/archives/images/original/weic2514a.tif"; | ||
| 60 | hash = "sha256-jTi1G1Ofo5xsF6ggrbtYJHxqLaCQ7edM5B3uORiVQtg="; | ||
| 61 | }) | ||
| 62 | (pkgs.fetchurl { | ||
| 63 | url = "https://esawebb.org/media/archives/images/original/weic2425c.tif"; | ||
| 64 | hash = "sha256-oaEOexSJHEGj090dJF3ct5HAoR+Y5gRiPrUlxdvnTRo="; | ||
| 65 | }) | ||
| 66 | ]; | ||
| 67 | |||
| 68 | dontUnpack = true; | ||
| 69 | |||
| 70 | buildInputs = [ pkgs.imagemagick ]; | ||
| 71 | buildPhase = '' | ||
| 72 | runHook preBuild | ||
| 73 | |||
| 74 | typeset sources=($srcs) | ||
| 75 | |||
| 76 | mkdir -p $out | ||
| 77 | magick ''${sources[0]} -crop 10000x5625+0+79 +repage -define jpeg:extent=10MB $out/carinanebula3.jpeg | ||
| 78 | magick ''${sources[1]} -crop 6716x3778+329+80 +repage -define jpeg:extent=10MB $out/pillarsofcreation_composite.jpeg | ||
| 79 | magick ''${sources[2]} -crop 10000x5625+0+79 +repage -define jpeg:extent=10MB $out/weic2212a.jpeg | ||
| 80 | magick ''${sources[3]} -crop 7650x4302+1166+389 +repage -define jpeg:extent=10MB $out/weic2415a.jpeg | ||
| 81 | magick ''${sources[4]} -crop 8732x4912+0+434 +repage -define jpeg:extent=10MB $out/weic2330a.jpeg | ||
| 82 | magick ''${sources[5]} -crop 5302x2982+636+0 +repage -define jpeg:extent=10MB $out/weic2426a.jpeg | ||
| 83 | magick ''${sources[6]} -crop 4328x2434+0+906 +repage -define jpeg:extent=10MB $out/weic2503a.jpeg | ||
| 84 | magick ''${sources[7]} -crop 4152x2335+0+666 +repage -define jpeg:extent=10MB $out/weic2506a.jpeg | ||
| 85 | magick ''${sources[8]} -crop 4320x2430+0+0 +repage -define jpeg:extent=10MB $out/weic2514a.jpeg | ||
| 86 | magick ''${sources[9]} -crop 5863x3298+0+477 +repage -define jpeg:extent=10MB $out/weic2425c.jpeg | ||
| 87 | |||
| 88 | runHook postBuild | ||
| 89 | ''; | ||
| 90 | }); | ||
| 91 | niri_session = builtins.toJSON [ | ||
| 92 | (pkgs.writeShellScript "niri-session" '' | ||
| 93 | exec ${lib.getExe pkgs.dex} -w ${config.programs.niri.package}/share/wayland-sessions/niri.desktop &>/tmp/niri-session-$$.log | ||
| 94 | '') | ||
| 95 | # (lib.getExe pkgs.dex) | ||
| 96 | # "${config.programs.niri.package}/share/wayland-sessions/niri.desktop" | ||
| 97 | ]; | ||
| 98 | username = builtins.toJSON config.home.username; | ||
| 99 | mdi = builtins.toJSON (pkgs.fetchFromGitHub { | ||
| 100 | owner = "Templarian"; | ||
| 101 | repo = "MaterialDesign"; | ||
| 102 | rev = "2424e748e0cc63ab7b9c095a099b9fe239b737c0"; | ||
| 103 | hash = "sha256-QMGl7soAhErrrnY3aKOZpt49yebkSNzy10p/v5OaqQ0="; | ||
| 104 | }); | ||
| 105 | worktime = builtins.toJSON (lib.getExe pkgs.worktime); | ||
| 106 | }; | ||
| 107 | }; | ||
| 108 | }; | ||
| 109 | systemd.user.services.quickshell = { | ||
| 110 | Service = { | ||
| 111 | RuntimeDirectory = "quickshell"; | ||
| 112 | }; | ||
| 113 | }; | ||
| 114 | }; | ||
| 115 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt b/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt new file mode 100644 index 00000000..020c0515 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/CMakeLists.txt | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | set(INSTALL_QMLDIR "" CACHE STRING "QML install dir") | ||
| 2 | set(INSTALL_QML_PREFIX "" CACHE STRING "QML install prefix") | ||
| 3 | |||
| 4 | # There doesn't seem to be a standard cross-distro qml install path. | ||
| 5 | if ("${INSTALL_QMLDIR}" STREQUAL "" AND "${INSTALL_QML_PREFIX}" STREQUAL "") | ||
| 6 | message(WARNING "Neither INSTALL_QMLDIR nor INSTALL_QML_PREFIX is set. QML modules will not be installed.") | ||
| 7 | else() | ||
| 8 | if ("${INSTALL_QMLDIR}" STREQUAL "") | ||
| 9 | set(QML_FULL_INSTALLDIR "${CMAKE_INSTALL_PREFIX}/${INSTALL_QML_PREFIX}") | ||
| 10 | else() | ||
| 11 | set(QML_FULL_INSTALLDIR "${INSTALL_QMLDIR}") | ||
| 12 | endif() | ||
| 13 | |||
| 14 | message(STATUS "QML install dir: ${QML_FULL_INSTALLDIR}") | ||
| 15 | endif() | ||
| 16 | |||
| 17 | # Install a given target as a QML module. This is mostly pulled from ECM, as there does not seem | ||
| 18 | # to be an official way to do it. | ||
| 19 | # see https://github.com/KDE/extra-cmake-modules/blob/fe0f606bf7f222e36f7560fd7a2c33ef993e23bb/modules/ECMQmlModule6.cmake#L160 | ||
| 20 | function(install_qml_module arg_TARGET) | ||
| 21 | if (NOT DEFINED QML_FULL_INSTALLDIR) | ||
| 22 | return() | ||
| 23 | endif() | ||
| 24 | |||
| 25 | qt_query_qml_module(${arg_TARGET} | ||
| 26 | URI module_uri | ||
| 27 | VERSION module_version | ||
| 28 | PLUGIN_TARGET module_plugin_target | ||
| 29 | TARGET_PATH module_target_path | ||
| 30 | QMLDIR module_qmldir | ||
| 31 | TYPEINFO module_typeinfo | ||
| 32 | QML_FILES module_qml_files | ||
| 33 | RESOURCES module_resources | ||
| 34 | ) | ||
| 35 | |||
| 36 | set(module_dir "${QML_FULL_INSTALLDIR}/${module_target_path}") | ||
| 37 | |||
| 38 | if (NOT TARGET "${module_plugin_target}") | ||
| 39 | message(FATAL_ERROR "install_qml_modules called for a target without a plugin") | ||
| 40 | endif() | ||
| 41 | |||
| 42 | get_target_property(target_type "${arg_TARGET}" TYPE) | ||
| 43 | if (NOT "${target_type}" STREQUAL "STATIC_LIBRARY") | ||
| 44 | install( | ||
| 45 | TARGETS "${arg_TARGET}" | ||
| 46 | LIBRARY DESTINATION "${module_dir}" | ||
| 47 | RUNTIME DESTINATION "${module_dir}" | ||
| 48 | ) | ||
| 49 | |||
| 50 | install( | ||
| 51 | TARGETS "${module_plugin_target}" | ||
| 52 | LIBRARY DESTINATION "${module_dir}" | ||
| 53 | RUNTIME DESTINATION "${module_dir}" | ||
| 54 | ) | ||
| 55 | endif() | ||
| 56 | |||
| 57 | install(FILES "${module_qmldir}" DESTINATION "${module_dir}") | ||
| 58 | install(FILES "${module_typeinfo}" DESTINATION "${module_dir}") | ||
| 59 | |||
| 60 | # Install QML files | ||
| 61 | list(LENGTH module_qml_files num_files) | ||
| 62 | if (NOT "${module_qml_files}" MATCHES "NOTFOUND" AND ${num_files} GREATER 0) | ||
| 63 | qt_query_qml_module(${arg_TARGET} QML_FILES_DEPLOY_PATHS qml_files_deploy_paths) | ||
| 64 | |||
| 65 | math(EXPR last_index "${num_files} - 1") | ||
| 66 | foreach(i RANGE 0 ${last_index}) | ||
| 67 | list(GET module_qml_files ${i} src_file) | ||
| 68 | list(GET qml_files_deploy_paths ${i} deploy_path) | ||
| 69 | get_filename_component(dst_name "${deploy_path}" NAME) | ||
| 70 | get_filename_component(dest_dir "${deploy_path}" DIRECTORY) | ||
| 71 | install(FILES "${src_file}" DESTINATION "${module_dir}/${dest_dir}" RENAME "${dst_name}") | ||
| 72 | endforeach() | ||
| 73 | endif() | ||
| 74 | |||
| 75 | # Install resources | ||
| 76 | list(LENGTH module_resources num_files) | ||
| 77 | if (NOT "${module_resources}" MATCHES "NOTFOUND" AND ${num_files} GREATER 0) | ||
| 78 | qt_query_qml_module(${arg_TARGET} RESOURCES_DEPLOY_PATHS resources_deploy_paths) | ||
| 79 | |||
| 80 | math(EXPR last_index "${num_files} - 1") | ||
| 81 | foreach(i RANGE 0 ${last_index}) | ||
| 82 | list(GET module_resources ${i} src_file) | ||
| 83 | list(GET resources_deploy_paths ${i} deploy_path) | ||
| 84 | get_filename_component(dst_name "${deploy_path}" NAME) | ||
| 85 | get_filename_component(dest_dir "${deploy_path}" DIRECTORY) | ||
| 86 | install(FILES "${src_file}" DESTINATION "${module_dir}/${dest_dir}" RENAME "${dst_name}") | ||
| 87 | endforeach() | ||
| 88 | endif() | ||
| 89 | endfunction() | ||
| 90 | |||
| 91 | |||
| 92 | cmake_minimum_required(VERSION 3.20) | ||
| 93 | project(custom LANGUAGES CXX) | ||
| 94 | |||
| 95 | find_package(Qt6 REQUIRED COMPONENTS Core Qml DBus) | ||
| 96 | |||
| 97 | qt_standard_project_setup(REQUIRES 6.6) | ||
| 98 | |||
| 99 | qt6_policy(SET QTP0001 NEW) | ||
| 100 | qt6_add_qml_module(customplugin | ||
| 101 | URI "Custom" | ||
| 102 | PLUGIN_TARGET customplugin | ||
| 103 | ) | ||
| 104 | |||
| 105 | set_source_files_properties(org.keepassxc.KeePassXC.MainWindow.xml PROPERTIES | ||
| 106 | CLASSNAME DBusKeePassXC | ||
| 107 | NO_NAMESPACE TRUE | ||
| 108 | ) | ||
| 109 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 110 | org.keepassxc.KeePassXC.MainWindow.xml | ||
| 111 | dbus_keepassxc | ||
| 112 | ) | ||
| 113 | |||
| 114 | set_source_files_properties(org.freedesktop.systemd1.Manager.xml PROPERTIES | ||
| 115 | CLASSNAME DBusSystemdManager | ||
| 116 | NO_NAMESPACE TRUE | ||
| 117 | ) | ||
| 118 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 119 | org.freedesktop.systemd1.Manager.xml | ||
| 120 | dbus_systemd_manager | ||
| 121 | ) | ||
| 122 | |||
| 123 | set_source_files_properties(org.freedesktop.login1.Manager.xml PROPERTIES | ||
| 124 | CLASSNAME DBusLogindManager | ||
| 125 | NO_NAMESPACE TRUE | ||
| 126 | ) | ||
| 127 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 128 | org.freedesktop.login1.Manager.xml | ||
| 129 | dbus_logind_manager | ||
| 130 | ) | ||
| 131 | |||
| 132 | set_source_files_properties(org.freedesktop.login1.Session.xml PROPERTIES | ||
| 133 | CLASSNAME DBusLogindSession | ||
| 134 | NO_NAMESPACE TRUE | ||
| 135 | ) | ||
| 136 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 137 | org.freedesktop.login1.Session.xml | ||
| 138 | dbus_logind_session | ||
| 139 | ) | ||
| 140 | |||
| 141 | set_source_files_properties(org.freedesktop.DBus.Properties.xml PROPERTIES | ||
| 142 | CLASSNAME DBusProperties | ||
| 143 | NO_NAMESPACE TRUE | ||
| 144 | ) | ||
| 145 | qt_add_dbus_interface(DBUS_INTERFACES | ||
| 146 | org.freedesktop.DBus.Properties.xml | ||
| 147 | dbus_properties | ||
| 148 | ) | ||
| 149 | |||
| 150 | include_directories(${CMAKE_SOURCE_DIR}/build) | ||
| 151 | |||
| 152 | target_compile_features(customplugin PUBLIC cxx_std_26) | ||
| 153 | |||
| 154 | target_link_libraries(customplugin PRIVATE | ||
| 155 | Qt6::Core | ||
| 156 | Qt6::Qml | ||
| 157 | Qt6::DBus | ||
| 158 | ) | ||
| 159 | |||
| 160 | target_sources(customplugin PRIVATE | ||
| 161 | Chrono.cpp Chrono.hpp | ||
| 162 | FileSelector.cpp FileSelector.hpp | ||
| 163 | KeePassXC.cpp KeePassXC.hpp | ||
| 164 | Systemd.cpp Systemd.hpp | ||
| 165 | ${DBUS_INTERFACES} | ||
| 166 | ) | ||
| 167 | |||
| 168 | install_qml_module(customplugin) | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp new file mode 100644 index 00000000..22b3469b --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.cpp | |||
| @@ -0,0 +1,83 @@ | |||
| 1 | #include "Chrono.hpp" | ||
| 2 | |||
| 3 | #include <format> | ||
| 4 | #include <iostream> | ||
| 5 | #include <cmath> | ||
| 6 | |||
| 7 | Chrono::Chrono(QObject* parent): QObject(parent) { | ||
| 8 | QObject::connect(&this->timer, &QTimer::timeout, this, &Chrono::onTimeout); | ||
| 9 | this->update(); | ||
| 10 | } | ||
| 11 | |||
| 12 | bool Chrono::enabled() const { return this->mEnabled; } | ||
| 13 | |||
| 14 | void Chrono::setEnabled(bool enabled) { | ||
| 15 | if (enabled == this->mEnabled) return; | ||
| 16 | this->mEnabled = enabled; | ||
| 17 | emit this->enabledChanged(); | ||
| 18 | this->update(); | ||
| 19 | } | ||
| 20 | |||
| 21 | Chrono::Precision Chrono::precision() const { return this->mPrecision; } | ||
| 22 | |||
| 23 | void Chrono::setPrecision(Chrono::Precision precision) { | ||
| 24 | if (precision == this->mPrecision) return; | ||
| 25 | this->mPrecision = precision; | ||
| 26 | emit this->precisionChanged(); | ||
| 27 | this->update(); | ||
| 28 | } | ||
| 29 | |||
| 30 | void Chrono::onTimeout() { | ||
| 31 | this->setTime(this->targetTime); | ||
| 32 | this->schedule(this->targetTime); | ||
| 33 | } | ||
| 34 | |||
| 35 | void Chrono::update() { | ||
| 36 | if (this->mEnabled) { | ||
| 37 | this->setTime(std::chrono::time_point<std::chrono::system_clock>()); | ||
| 38 | this->schedule(std::chrono::time_point<std::chrono::system_clock>()); | ||
| 39 | } else { | ||
| 40 | this->timer.stop(); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | void Chrono::setTime(const std::chrono::time_point<std::chrono::system_clock>& targetTime) { | ||
| 45 | using namespace std::chrono_literals; | ||
| 46 | |||
| 47 | auto currentTime = std::chrono::system_clock::now(); | ||
| 48 | auto offset = std::chrono::duration_cast<std::chrono::milliseconds>(targetTime - currentTime); | ||
| 49 | this->currentTime = abs(offset) < 500ms ? targetTime : currentTime; | ||
| 50 | |||
| 51 | switch (this->mPrecision) { | ||
| 52 | case Chrono::Hours: this->currentTime = std::chrono::time_point_cast<std::chrono::hours>(this->currentTime); | ||
| 53 | case Chrono::Minutes: this->currentTime = std::chrono::time_point_cast<std::chrono::minutes>(this->currentTime); | ||
| 54 | case Chrono::Seconds: this->currentTime = std::chrono::time_point_cast<std::chrono::seconds>(this->currentTime); | ||
| 55 | } | ||
| 56 | |||
| 57 | emit this->dateChanged(); | ||
| 58 | } | ||
| 59 | |||
| 60 | void Chrono::schedule(const std::chrono::time_point<std::chrono::system_clock>& targetTime) { | ||
| 61 | using namespace std::chrono_literals; | ||
| 62 | |||
| 63 | auto currentTime = std::chrono::system_clock::now(); | ||
| 64 | auto offset = std::chrono::duration_cast<std::chrono::milliseconds>(targetTime - currentTime); | ||
| 65 | auto nextTime = abs(offset) < 500ms ? targetTime : currentTime; | ||
| 66 | |||
| 67 | switch (this->mPrecision) { | ||
| 68 | case Chrono::Hours: nextTime = std::chrono::time_point_cast<std::chrono::hours>(nextTime) + 1h; | ||
| 69 | case Chrono::Minutes: nextTime = std::chrono::time_point_cast<std::chrono::minutes>(nextTime) + 1min; | ||
| 70 | case Chrono::Seconds: nextTime = std::chrono::time_point_cast<std::chrono::seconds>(nextTime) + 1s; | ||
| 71 | } | ||
| 72 | |||
| 73 | this->targetTime = nextTime; | ||
| 74 | this->timer.start(std::chrono::duration_cast<std::chrono::milliseconds>(nextTime - currentTime)); | ||
| 75 | } | ||
| 76 | |||
| 77 | QString Chrono::format(const QString& fmt) const { | ||
| 78 | return QString::fromStdString(std::format(std::runtime_format(fmt.toStdString()), std::chrono::zoned_time(std::chrono::current_zone(), std::chrono::time_point_cast<std::chrono::seconds>(this->currentTime)))); | ||
| 79 | } | ||
| 80 | |||
| 81 | QDateTime Chrono::date() const { | ||
| 82 | return QDateTime::fromStdTimePoint(std::chrono::time_point_cast<std::chrono::milliseconds>(this->currentTime)); | ||
| 83 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp new file mode 100644 index 00000000..04080187 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Chrono.hpp | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <chrono> | ||
| 4 | |||
| 5 | #include <QDateTime> | ||
| 6 | #include <QObject> | ||
| 7 | #include <QTimer> | ||
| 8 | |||
| 9 | #include <qqmlintegration.h> | ||
| 10 | |||
| 11 | class Chrono : public QObject { | ||
| 12 | Q_OBJECT; | ||
| 13 | Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged); | ||
| 14 | Q_PROPERTY(Chrono::Precision precision READ precision WRITE setPrecision NOTIFY precisionChanged); | ||
| 15 | Q_PROPERTY(QDateTime date READ date NOTIFY dateChanged) | ||
| 16 | QML_ELEMENT; | ||
| 17 | |||
| 18 | public: | ||
| 19 | enum Precision : quint8 { | ||
| 20 | Hours = 1, | ||
| 21 | Minutes = 2, | ||
| 22 | Seconds = 3, | ||
| 23 | }; | ||
| 24 | Q_ENUM(Precision); | ||
| 25 | |||
| 26 | explicit Chrono(QObject* parent = nullptr); | ||
| 27 | |||
| 28 | bool enabled() const; | ||
| 29 | void setEnabled(bool enabled); | ||
| 30 | |||
| 31 | Chrono::Precision precision() const; | ||
| 32 | void setPrecision(Chrono::Precision precision); | ||
| 33 | |||
| 34 | Q_INVOKABLE QString format(const QString& fmt) const; | ||
| 35 | |||
| 36 | QDateTime date() const; | ||
| 37 | |||
| 38 | signals: | ||
| 39 | void enabledChanged(); | ||
| 40 | void precisionChanged(); | ||
| 41 | void dateChanged(); | ||
| 42 | |||
| 43 | private slots: | ||
| 44 | void onTimeout(); | ||
| 45 | |||
| 46 | private: | ||
| 47 | bool mEnabled = true; | ||
| 48 | Chrono::Precision mPrecision = Chrono::Seconds; | ||
| 49 | QTimer timer; | ||
| 50 | std::chrono::time_point<std::chrono::system_clock> currentTime, targetTime; | ||
| 51 | |||
| 52 | void update(); | ||
| 53 | void setTime(const std::chrono::time_point<std::chrono::system_clock>& targetTime); | ||
| 54 | void schedule(const std::chrono::time_point<std::chrono::system_clock>& targetTime); | ||
| 55 | }; | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.cpp new file mode 100644 index 00000000..d7051d2a --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.cpp | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | #include "FileSelector.hpp" | ||
| 2 | |||
| 3 | #include <sstream> | ||
| 4 | #include <vector> | ||
| 5 | #include <random> | ||
| 6 | #include <algorithm> | ||
| 7 | |||
| 8 | #include <iostream> | ||
| 9 | #include <format> | ||
| 10 | |||
| 11 | namespace fs = std::filesystem; | ||
| 12 | |||
| 13 | FileSelector::FileSelector(QObject* parent): QObject(parent) { | ||
| 14 | QObject::connect(&this->timer, &QTimer::timeout, this, &FileSelector::onTimeout); | ||
| 15 | this->timer.setTimerType(Qt::PreciseTimer); | ||
| 16 | } | ||
| 17 | |||
| 18 | QString FileSelector::directory() const { | ||
| 19 | return QString::fromStdString(this->mDirectory->string()); | ||
| 20 | } | ||
| 21 | void FileSelector::setDirectory(QString directory) { | ||
| 22 | this->mDirectory = directory.toStdString(); | ||
| 23 | if (this->mDirectory && this->mEpoch) | ||
| 24 | this->update(); | ||
| 25 | emit this->directoryChanged(); | ||
| 26 | } | ||
| 27 | |||
| 28 | unsigned int FileSelector::epoch() const { | ||
| 29 | return std::chrono::duration_cast<std::chrono::milliseconds>(*this->mEpoch).count(); | ||
| 30 | } | ||
| 31 | void FileSelector::setEpoch(unsigned int epoch) { | ||
| 32 | this->mEpoch = std::chrono::milliseconds{epoch}; | ||
| 33 | if (this->mDirectory && this->mEpoch) | ||
| 34 | this->update(); | ||
| 35 | emit this->epochChanged(); | ||
| 36 | } | ||
| 37 | |||
| 38 | QString FileSelector::seed() const { | ||
| 39 | return this->mSeed; | ||
| 40 | } | ||
| 41 | void FileSelector::setSeed(QString seed) { | ||
| 42 | this->mSeed = seed; | ||
| 43 | emit this->seedChanged(); | ||
| 44 | if (this->mDirectory && this->mEpoch) | ||
| 45 | emit this->selectedChanged(); | ||
| 46 | } | ||
| 47 | |||
| 48 | QString FileSelector::selected() const { | ||
| 49 | if (!this->mDirectory || !this->mEpoch) | ||
| 50 | return QString(); | ||
| 51 | |||
| 52 | std::vector<fs::path> shuffled(this->mFiles.begin(), this->mFiles.end()); | ||
| 53 | std::sort(shuffled.begin(), shuffled.end()); | ||
| 54 | |||
| 55 | auto currentTime = std::chrono::system_clock::now(); | ||
| 56 | uint64_t currentEpoch = currentTime.time_since_epoch() / *this->mEpoch; | ||
| 57 | std::chrono::milliseconds timeInEpoch = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime.time_since_epoch()) % *this->mEpoch; | ||
| 58 | |||
| 59 | std::ostringstream seed; | ||
| 60 | seed << this->mSeed.size() << ";" << this->mSeed.toStdString() << ";"; | ||
| 61 | seed << *this->mEpoch << ";"; | ||
| 62 | seed << currentEpoch << ";"; | ||
| 63 | seed << this->mDirectory->string().size() << ";" << *this->mDirectory << ";"; | ||
| 64 | seed << this->mFiles.size() << ";"; | ||
| 65 | for (const fs::path& p: this->mFiles) | ||
| 66 | seed << p.string().size() << ";" << p << ";"; | ||
| 67 | |||
| 68 | std::vector<std::seed_seq::result_type> v; | ||
| 69 | v.reserve(seed.str().size()); | ||
| 70 | for (const char& c: seed.str()) | ||
| 71 | v.push_back(c); | ||
| 72 | |||
| 73 | std::seed_seq engine_seed(v.begin(), v.end()); | ||
| 74 | std::mt19937 g(engine_seed); | ||
| 75 | std::shuffle(shuffled.begin(), shuffled.end(), g); | ||
| 76 | |||
| 77 | std::vector<fs::path>::size_type ix = shuffled.size() * timeInEpoch / *this->mEpoch; | ||
| 78 | return QString::fromStdString((*this->mDirectory / shuffled[ix]).string()); | ||
| 79 | } | ||
| 80 | |||
| 81 | void FileSelector::onTimeout() { | ||
| 82 | if (!this->mFiles.size()) | ||
| 83 | return; | ||
| 84 | |||
| 85 | auto currentTime = std::chrono::system_clock::now(); | ||
| 86 | uint64_t currentMinorEpoch = currentTime.time_since_epoch() / (*this->mEpoch / this->mFiles.size()); | ||
| 87 | auto nextTime = std::chrono::time_point<std::chrono::system_clock>((currentMinorEpoch + 1) * (*this->mEpoch / this->mFiles.size())); | ||
| 88 | this->timer.start(std::chrono::duration_cast<std::chrono::milliseconds>(nextTime - currentTime)); | ||
| 89 | |||
| 90 | emit this->selectedChanged(); | ||
| 91 | } | ||
| 92 | |||
| 93 | void FileSelector::update() { | ||
| 94 | this->mFiles = std::set<fs::path>{}; | ||
| 95 | for (const fs::directory_entry& entry: | ||
| 96 | fs::recursive_directory_iterator(*this->mDirectory, fs::directory_options::follow_directory_symlink)) | ||
| 97 | { | ||
| 98 | this->mFiles.insert(fs::relative(entry, *this->mDirectory)); | ||
| 99 | } | ||
| 100 | |||
| 101 | this->onTimeout(); | ||
| 102 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.hpp new file mode 100644 index 00000000..72c4f2a7 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/FileSelector.hpp | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <filesystem> | ||
| 4 | #include <chrono> | ||
| 5 | #include <set> | ||
| 6 | #include <optional> | ||
| 7 | |||
| 8 | #include <QObject> | ||
| 9 | #include <QTimer> | ||
| 10 | |||
| 11 | #include <qqmlintegration.h> | ||
| 12 | |||
| 13 | class FileSelector : public QObject { | ||
| 14 | Q_OBJECT; | ||
| 15 | Q_PROPERTY(QString directory READ directory WRITE setDirectory NOTIFY directoryChanged REQUIRED); | ||
| 16 | Q_PROPERTY(unsigned int epoch READ epoch WRITE setEpoch NOTIFY epochChanged REQUIRED); | ||
| 17 | Q_PROPERTY(QString seed READ seed WRITE setSeed NOTIFY seedChanged); | ||
| 18 | Q_PROPERTY(QString selected READ selected NOTIFY selectedChanged); | ||
| 19 | QML_ELEMENT; | ||
| 20 | |||
| 21 | public: | ||
| 22 | explicit FileSelector(QObject* parent = nullptr); | ||
| 23 | |||
| 24 | QString directory() const; | ||
| 25 | void setDirectory(QString directory); | ||
| 26 | |||
| 27 | unsigned int epoch() const; | ||
| 28 | void setEpoch(unsigned int epoch); | ||
| 29 | |||
| 30 | QString seed() const; | ||
| 31 | void setSeed(QString seed); | ||
| 32 | |||
| 33 | QString selected() const; | ||
| 34 | |||
| 35 | signals: | ||
| 36 | void directoryChanged(); | ||
| 37 | void epochChanged(); | ||
| 38 | void seedChanged(); | ||
| 39 | void selectedChanged(); | ||
| 40 | |||
| 41 | private slots: | ||
| 42 | void onTimeout(); | ||
| 43 | |||
| 44 | private: | ||
| 45 | std::optional<std::filesystem::path> mDirectory; | ||
| 46 | std::optional<std::chrono::milliseconds> mEpoch; | ||
| 47 | std::set<std::filesystem::path> mFiles; | ||
| 48 | QString mSeed; | ||
| 49 | QTimer timer; | ||
| 50 | |||
| 51 | void update(); | ||
| 52 | }; | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.cpp new file mode 100644 index 00000000..f6e4dd6e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.cpp | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | #include "KeePassXC.hpp" | ||
| 2 | |||
| 3 | #include <QDBusConnection> | ||
| 4 | |||
| 5 | KeePassXC::KeePassXC() { | ||
| 6 | this->service = new DBusKeePassXC(DBusKeePassXC::staticInterfaceName(), "/keepassxc", QDBusConnection::sessionBus(), this); | ||
| 7 | } | ||
| 8 | KeePassXC::~KeePassXC() { | ||
| 9 | if (this->service) | ||
| 10 | delete this->service; | ||
| 11 | } | ||
| 12 | |||
| 13 | void KeePassXC::lockAllDatabases() { | ||
| 14 | if (!this->service) | ||
| 15 | return; | ||
| 16 | |||
| 17 | this->service->lockAllDatabases(); | ||
| 18 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.hpp new file mode 100644 index 00000000..c4cd71e0 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/KeePassXC.hpp | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include "dbus_keepassxc.h" | ||
| 4 | |||
| 5 | #include <QObject> | ||
| 6 | #include <QtQmlIntegration/qqmlintegration.h> | ||
| 7 | |||
| 8 | class KeePassXC : public QObject { | ||
| 9 | Q_OBJECT; | ||
| 10 | QML_SINGLETON; | ||
| 11 | QML_ELEMENT; | ||
| 12 | |||
| 13 | public: | ||
| 14 | explicit KeePassXC(); | ||
| 15 | ~KeePassXC(); | ||
| 16 | |||
| 17 | Q_INVOKABLE void lockAllDatabases(); | ||
| 18 | |||
| 19 | private: | ||
| 20 | DBusKeePassXC* service = nullptr; | ||
| 21 | }; | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp new file mode 100644 index 00000000..308659e9 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.cpp | |||
| @@ -0,0 +1,198 @@ | |||
| 1 | #include "Systemd.hpp" | ||
| 2 | |||
| 3 | #include <sstream> | ||
| 4 | |||
| 5 | #include <QDBusConnection> | ||
| 6 | #include <QDBusReply> | ||
| 7 | #include <QDebug> | ||
| 8 | #include <QDBusUnixFileDescriptor> | ||
| 9 | #include <QDBusObjectPath> | ||
| 10 | |||
| 11 | Systemd::Systemd(QObject* parent) : QObject(parent) { | ||
| 12 | this->systemdManager = new DBusSystemdManager( | ||
| 13 | "org.freedesktop.systemd1", | ||
| 14 | "/org/freedesktop/systemd1", | ||
| 15 | QDBusConnection::sessionBus(), | ||
| 16 | this | ||
| 17 | ); | ||
| 18 | this->logindManager = new DBusLogindManager( | ||
| 19 | "org.freedesktop.login1", | ||
| 20 | "/org/freedesktop/login1", | ||
| 21 | QDBusConnection::systemBus(), | ||
| 22 | this | ||
| 23 | ); | ||
| 24 | |||
| 25 | QDBusReply<QDBusObjectPath> sessionPath = this->logindManager->GetSession("auto"); | ||
| 26 | qDebug() << sessionPath; | ||
| 27 | this->logindSession = new DBusLogindSession( | ||
| 28 | "org.freedesktop.login1", | ||
| 29 | sessionPath.value().path(), | ||
| 30 | QDBusConnection::systemBus(), | ||
| 31 | this | ||
| 32 | ); | ||
| 33 | this->logindSessionProperties = new DBusProperties( | ||
| 34 | "org.freedesktop.login1", | ||
| 35 | sessionPath.value().path(), | ||
| 36 | QDBusConnection::systemBus(), | ||
| 37 | this | ||
| 38 | ); | ||
| 39 | |||
| 40 | QObject::connect(this->logindManager, &DBusLogindManager::PrepareForShutdown, this, &Systemd::shutdown); | ||
| 41 | QObject::connect(this->logindManager, &DBusLogindManager::PrepareForSleep, this, &Systemd::sleep); | ||
| 42 | QObject::connect(this->logindSession, &DBusLogindSession::Lock, this, &Systemd::lock); | ||
| 43 | QObject::connect(this->logindSession, &DBusLogindSession::Unlock, this, &Systemd::unlock); | ||
| 44 | QObject::connect(this->logindSessionProperties, &DBusProperties::PropertiesChanged, this, &Systemd::onLogindSessionPropertiesChanged); | ||
| 45 | } | ||
| 46 | |||
| 47 | void Systemd::onLogindSessionPropertiesChanged(const QString& interface_name, const QVariantMap& changed_properties, const QStringList& invalidated_properties) { | ||
| 48 | if (changed_properties.contains("IdleHint") || invalidated_properties.contains("IdleHint")) | ||
| 49 | emit this->idleHintChanged(); | ||
| 50 | if (changed_properties.contains("LockedHint") || invalidated_properties.contains("LockedHint")) | ||
| 51 | emit this->lockedHintChanged(); | ||
| 52 | } | ||
| 53 | |||
| 54 | void Systemd::stopUserUnit(const QString& unit, const QString& mode) { this->systemdManager->StopUnit(unit, mode); } | ||
| 55 | |||
| 56 | void Systemd::setBrightness(const QString& subsystem, const QString& name, quint32 brightness) { this->logindSession->SetBrightness(subsystem, name, brightness); } | ||
| 57 | |||
| 58 | bool Systemd::idleHint() { return this->logindSession->idleHint(); } | ||
| 59 | void Systemd::setIdleHint(bool idle) { this->logindSession->SetIdleHint(idle); } | ||
| 60 | bool Systemd::lockedHint() { return this->logindSession->lockedHint(); } | ||
| 61 | void Systemd::setLockedHint(bool locked) { this->logindSession->SetLockedHint(locked); } | ||
| 62 | void Systemd::lockSession() { this->logindSession->call("Lock"); } | ||
| 63 | void Systemd::suspend() { this->logindManager->Suspend(true); } | ||
| 64 | void Systemd::hibernate() { this->logindManager->Hibernate(true); } | ||
| 65 | |||
| 66 | std::string SystemdInhibitorParams::toString(SystemdInhibitorParams::WhatItem what) { | ||
| 67 | if (what == SystemdInhibitorParams::Shutdown) | ||
| 68 | return "shutdown"; | ||
| 69 | else if (what == SystemdInhibitorParams::Sleep) | ||
| 70 | return "sleep"; | ||
| 71 | else if (what == SystemdInhibitorParams::Idle) | ||
| 72 | return "idle"; | ||
| 73 | else if (what == SystemdInhibitorParams::HandlePowerKey) | ||
| 74 | return "handle-power-key"; | ||
| 75 | else if (what == SystemdInhibitorParams::HandleSuspendKey) | ||
| 76 | return "handle-suspend-key"; | ||
| 77 | else if (what == SystemdInhibitorParams::HandleHibernateKey) | ||
| 78 | return "handle-hibernate-key"; | ||
| 79 | else if (what == SystemdInhibitorParams::HandleLidSwitch) | ||
| 80 | return "handle-lid-switch"; | ||
| 81 | return ""; | ||
| 82 | } | ||
| 83 | |||
| 84 | std::string SystemdInhibitorParams::toString(SystemdInhibitorParams::What what) { | ||
| 85 | std::ostringstream res; | ||
| 86 | bool first = true; | ||
| 87 | for (const WhatItem& item: SystemdInhibitorParams::allWhatItems) { | ||
| 88 | if (!(what & item)) | ||
| 89 | continue; | ||
| 90 | |||
| 91 | if (!first) | ||
| 92 | res << ":"; | ||
| 93 | else | ||
| 94 | first = false; | ||
| 95 | res << SystemdInhibitorParams::toString(item); | ||
| 96 | } | ||
| 97 | return res.str(); | ||
| 98 | } | ||
| 99 | |||
| 100 | std::string SystemdInhibitorParams::toString(SystemdInhibitorParams::Mode mode) { | ||
| 101 | if (mode == SystemdInhibitorParams::Block) | ||
| 102 | return "block"; | ||
| 103 | else if (mode == SystemdInhibitorParams::BlockWeak) | ||
| 104 | return "block-weak"; | ||
| 105 | else if (mode == SystemdInhibitorParams::Delay) | ||
| 106 | return "delay"; | ||
| 107 | return ""; | ||
| 108 | } | ||
| 109 | |||
| 110 | bool SystemdInhibitor::enabled() const { return static_cast<bool>(this->activeInhibitor); } | ||
| 111 | void SystemdInhibitor::setEnabled(bool enabled) { | ||
| 112 | this->mEnabled = enabled; | ||
| 113 | |||
| 114 | if (enabled) | ||
| 115 | this->update(); | ||
| 116 | else | ||
| 117 | this->release(); | ||
| 118 | } | ||
| 119 | |||
| 120 | SystemdInhibitorParams::What SystemdInhibitor::what() const { return this->mWhat; } | ||
| 121 | void SystemdInhibitor::setWhat(SystemdInhibitorParams::What what) { | ||
| 122 | this->mWhat = what; | ||
| 123 | this->update(); | ||
| 124 | } | ||
| 125 | |||
| 126 | QString SystemdInhibitor::who() const { return this->mWho; } | ||
| 127 | void SystemdInhibitor::setWho(QString who) { | ||
| 128 | this->mWho = who; | ||
| 129 | this->update(); | ||
| 130 | } | ||
| 131 | |||
| 132 | QString SystemdInhibitor::why() const { return this->mWhy; } | ||
| 133 | void SystemdInhibitor::setWhy(QString why) { | ||
| 134 | this->mWhy = why; | ||
| 135 | this->update(); | ||
| 136 | } | ||
| 137 | |||
| 138 | SystemdInhibitorParams::Mode SystemdInhibitor::mode() const { return this->mMode; } | ||
| 139 | void SystemdInhibitor::setMode(SystemdInhibitorParams::Mode mode) { | ||
| 140 | this->mMode = mode; | ||
| 141 | this->update(); | ||
| 142 | } | ||
| 143 | |||
| 144 | SystemdInhibitor::ActiveSystemdInhibitor::ActiveSystemdInhibitor(SystemdInhibitorParams::What what_, QString who_, QString why_, SystemdInhibitorParams::Mode mode_): what(what_), who(who_), why(why_), mode(mode_) { | ||
| 145 | DBusLogindManager logindManager( | ||
| 146 | "org.freedesktop.login1", | ||
| 147 | "/org/freedesktop/login1", | ||
| 148 | QDBusConnection::systemBus() | ||
| 149 | ); | ||
| 150 | QDBusReply<QDBusUnixFileDescriptor> fd_ = logindManager.Inhibit(QString::fromStdString(SystemdInhibitorParams::toString(what)), who, why, QString::fromStdString(SystemdInhibitorParams::toString(mode))); | ||
| 151 | if (fd_.error().isValid()) | ||
| 152 | throw fd_.error(); | ||
| 153 | this->fd = ::dup(fd_.value().fileDescriptor()); | ||
| 154 | } | ||
| 155 | SystemdInhibitor::ActiveSystemdInhibitor::~ActiveSystemdInhibitor() { | ||
| 156 | if (this->fd != -1) | ||
| 157 | ::close(this->fd); | ||
| 158 | } | ||
| 159 | |||
| 160 | void SystemdInhibitor::release() { | ||
| 161 | if (!this->activeInhibitor) | ||
| 162 | return; | ||
| 163 | |||
| 164 | this->activeInhibitor.reset(); | ||
| 165 | emit this->enabledChanged(); | ||
| 166 | } | ||
| 167 | |||
| 168 | void SystemdInhibitor::update() { | ||
| 169 | if (!this->mWhat || this->mWho.isEmpty() || this->mWhy.isEmpty() || !this->mMode || !this->mEnabled) | ||
| 170 | if (this->activeInhibitor) | ||
| 171 | this->release(); | ||
| 172 | else | ||
| 173 | return; | ||
| 174 | |||
| 175 | if (this->activeInhibitor && this->mWhat == this->activeInhibitor->what && this->mWho == this->activeInhibitor->who && this->mWhy == this->activeInhibitor->why && this->mMode == this->activeInhibitor->mode) | ||
| 176 | return; | ||
| 177 | |||
| 178 | std::unique_ptr<ActiveSystemdInhibitor> otherInhibitor; | ||
| 179 | try { | ||
| 180 | otherInhibitor.reset(new SystemdInhibitor::ActiveSystemdInhibitor(this->mWhat, this->mWho, this->mWhy, this->mMode)); | ||
| 181 | } catch (const QDBusError& err) { | ||
| 182 | qCritical().noquote() | ||
| 183 | << err.name().toStdString() << " " << err.message().toStdString(); | ||
| 184 | return; | ||
| 185 | } | ||
| 186 | this->activeInhibitor.swap(otherInhibitor); | ||
| 187 | |||
| 188 | if (!otherInhibitor && this->activeInhibitor) | ||
| 189 | emit this->enabledChanged(); | ||
| 190 | if (otherInhibitor && otherInhibitor->what != this->activeInhibitor->what) | ||
| 191 | emit this->whatChanged(); | ||
| 192 | if (otherInhibitor && otherInhibitor->who != this->activeInhibitor->who) | ||
| 193 | emit this->whoChanged(); | ||
| 194 | if (otherInhibitor && otherInhibitor->why != this->activeInhibitor->why) | ||
| 195 | emit this->whyChanged(); | ||
| 196 | if (otherInhibitor && otherInhibitor->mode != this->activeInhibitor->mode) | ||
| 197 | emit this->modeChanged(); | ||
| 198 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp new file mode 100644 index 00000000..615024d2 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/Systemd.hpp | |||
| @@ -0,0 +1,147 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <cstdint> | ||
| 4 | #include <memory> | ||
| 5 | #include <string> | ||
| 6 | |||
| 7 | #include <QObject> | ||
| 8 | #include <QDBusInterface> | ||
| 9 | #include <QtQmlIntegration/qqmlintegration.h> | ||
| 10 | |||
| 11 | #include "dbus_systemd_manager.h" | ||
| 12 | #include "dbus_logind_manager.h" | ||
| 13 | #include "dbus_logind_session.h" | ||
| 14 | #include "dbus_properties.h" | ||
| 15 | |||
| 16 | class Systemd : public QObject { | ||
| 17 | Q_OBJECT; | ||
| 18 | QML_SINGLETON; | ||
| 19 | QML_ELEMENT; | ||
| 20 | |||
| 21 | public: | ||
| 22 | explicit Systemd(QObject* parent = nullptr); | ||
| 23 | |||
| 24 | Q_PROPERTY(bool idleHint READ idleHint WRITE setIdleHint NOTIFY idleHintChanged) | ||
| 25 | Q_PROPERTY(bool lockedHint READ lockedHint WRITE setLockedHint NOTIFY lockedHintChanged) | ||
| 26 | |||
| 27 | Q_INVOKABLE void stopUserUnit(const QString& unit, const QString& mode); | ||
| 28 | Q_INVOKABLE void setBrightness(const QString& subsystem, const QString& name, quint32 brightness); | ||
| 29 | Q_INVOKABLE void setIdleHint(bool idle); | ||
| 30 | Q_INVOKABLE void setLockedHint(bool locked); | ||
| 31 | Q_INVOKABLE void lockSession(); | ||
| 32 | Q_INVOKABLE void suspend(); | ||
| 33 | Q_INVOKABLE void hibernate(); | ||
| 34 | |||
| 35 | bool idleHint(); | ||
| 36 | bool lockedHint(); | ||
| 37 | |||
| 38 | signals: | ||
| 39 | void shutdown(bool before); | ||
| 40 | void sleep(bool before); | ||
| 41 | void lock(); | ||
| 42 | void unlock(); | ||
| 43 | void idleHintChanged(); | ||
| 44 | void lockedHintChanged(); | ||
| 45 | |||
| 46 | private slots: | ||
| 47 | void onLogindSessionPropertiesChanged(const QString& interface_name, const QVariantMap& changed_properties, const QStringList& invalidated_properties); | ||
| 48 | |||
| 49 | private: | ||
| 50 | DBusSystemdManager* systemdManager; | ||
| 51 | DBusLogindManager* logindManager; | ||
| 52 | DBusLogindSession* logindSession; | ||
| 53 | DBusProperties* logindSessionProperties; | ||
| 54 | }; | ||
| 55 | |||
| 56 | class SystemdInhibitorParams : public QObject { | ||
| 57 | Q_OBJECT; | ||
| 58 | QML_ELEMENT; | ||
| 59 | QML_SINGLETON; | ||
| 60 | |||
| 61 | public: | ||
| 62 | enum WhatItem : uint8_t { | ||
| 63 | Shutdown = 0b1, | ||
| 64 | Sleep = 0b10, | ||
| 65 | Idle = 0b100, | ||
| 66 | HandlePowerKey = 0b1000, | ||
| 67 | HandleSuspendKey = 0b10000, | ||
| 68 | HandleHibernateKey = 0b100000, | ||
| 69 | HandleLidSwitch = 0b1000000, | ||
| 70 | }; | ||
| 71 | Q_ENUM(WhatItem); | ||
| 72 | Q_DECLARE_FLAGS(What, WhatItem); | ||
| 73 | |||
| 74 | enum Mode : uint8_t { | ||
| 75 | Block = 1, | ||
| 76 | BlockWeak = 2, | ||
| 77 | Delay = 3, | ||
| 78 | }; | ||
| 79 | Q_ENUM(Mode); | ||
| 80 | |||
| 81 | Q_INVOKABLE static std::string toString(WhatItem what); | ||
| 82 | Q_INVOKABLE static std::string toString(What what); | ||
| 83 | Q_INVOKABLE static std::string toString(Mode mode); | ||
| 84 | |||
| 85 | static constexpr WhatItem allWhatItems[] = { Shutdown, Sleep, Idle, HandlePowerKey, HandleSuspendKey, HandleHibernateKey, HandleLidSwitch }; | ||
| 86 | }; | ||
| 87 | Q_DECLARE_OPERATORS_FOR_FLAGS(SystemdInhibitorParams::What) | ||
| 88 | |||
| 89 | class SystemdInhibitor : public QObject { | ||
| 90 | Q_OBJECT; | ||
| 91 | Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged); | ||
| 92 | Q_PROPERTY(SystemdInhibitorParams::What what READ what WRITE setWhat NOTIFY whatChanged); | ||
| 93 | Q_PROPERTY(QString who READ who WRITE setWho NOTIFY whoChanged); | ||
| 94 | Q_PROPERTY(QString why READ why WRITE setWhy NOTIFY whyChanged); | ||
| 95 | Q_PROPERTY(SystemdInhibitorParams::Mode mode READ mode WRITE setMode NOTIFY modeChanged); | ||
| 96 | QML_ELEMENT; | ||
| 97 | |||
| 98 | public: | ||
| 99 | explicit SystemdInhibitor(QObject* parent = nullptr): QObject(parent) {} | ||
| 100 | |||
| 101 | bool enabled() const; | ||
| 102 | void setEnabled(bool enabled); | ||
| 103 | |||
| 104 | SystemdInhibitorParams::What what() const; | ||
| 105 | void setWhat(SystemdInhibitorParams::What what); | ||
| 106 | |||
| 107 | QString who() const; | ||
| 108 | void setWho(QString who); | ||
| 109 | |||
| 110 | QString why() const; | ||
| 111 | void setWhy(QString why); | ||
| 112 | |||
| 113 | SystemdInhibitorParams::Mode mode() const; | ||
| 114 | void setMode(SystemdInhibitorParams::Mode mode); | ||
| 115 | |||
| 116 | Q_INVOKABLE void release(); | ||
| 117 | |||
| 118 | signals: | ||
| 119 | void enabledChanged(); | ||
| 120 | void whatChanged(); | ||
| 121 | void whoChanged(); | ||
| 122 | void whyChanged(); | ||
| 123 | void modeChanged(); | ||
| 124 | |||
| 125 | private: | ||
| 126 | class ActiveSystemdInhibitor { | ||
| 127 | public: | ||
| 128 | uint32_t fd = -1; | ||
| 129 | SystemdInhibitorParams::What what; | ||
| 130 | QString who; | ||
| 131 | QString why; | ||
| 132 | SystemdInhibitorParams::Mode mode; | ||
| 133 | |||
| 134 | ActiveSystemdInhibitor(SystemdInhibitorParams::What what_, QString who_, QString why_, SystemdInhibitorParams::Mode mode_); | ||
| 135 | ~ActiveSystemdInhibitor(); | ||
| 136 | }; | ||
| 137 | |||
| 138 | void update(); | ||
| 139 | |||
| 140 | bool mEnabled = true; | ||
| 141 | std::unique_ptr<ActiveSystemdInhibitor> activeInhibitor; | ||
| 142 | SystemdInhibitorParams::What mWhat = static_cast<SystemdInhibitorParams::What>(0); | ||
| 143 | QString mWho; | ||
| 144 | QString mWhy; | ||
| 145 | SystemdInhibitorParams::Mode mMode = static_cast<SystemdInhibitorParams::Mode>(0); | ||
| 146 | }; | ||
| 147 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h b/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h new file mode 100644 index 00000000..e66ba9e3 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/customplugin.h | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | #include <QQmlEngineExtensionPlugin> | ||
| 2 | |||
| 3 | class CustomPlugin : public QQmlEngineExtensionPlugin | ||
| 4 | { | ||
| 5 | Q_OBJECT | ||
| 6 | Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid) | ||
| 7 | }; | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/default.nix b/accounts/gkleen@sif/shell/quickshell-plugins/default.nix new file mode 100644 index 00000000..20a195eb --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/default.nix | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | { lib | ||
| 2 | , stdenv | ||
| 3 | , cmake | ||
| 4 | , qt6 | ||
| 5 | , fmt | ||
| 6 | , keepassxc | ||
| 7 | , systemd | ||
| 8 | }: | ||
| 9 | |||
| 10 | stdenv.mkDerivation rec { | ||
| 11 | name = "quickshell-custom"; | ||
| 12 | |||
| 13 | src = ./.; | ||
| 14 | |||
| 15 | prePatch = '' | ||
| 16 | cp ${keepassxc.src}/src/gui/org.keepassxc.KeePassXC.MainWindow.xml . | ||
| 17 | ''; | ||
| 18 | |||
| 19 | nativeBuildInputs = [ cmake qt6.wrapQtAppsHook ]; | ||
| 20 | buildInputs = [ | ||
| 21 | qt6.qtbase | ||
| 22 | qt6.qtdeclarative | ||
| 23 | ]; | ||
| 24 | |||
| 25 | cmakeFlags = [ | ||
| 26 | (lib.cmakeFeature "INSTALL_QML_PREFIX" qt6.qtbase.qtQmlPrefix) | ||
| 27 | ]; | ||
| 28 | |||
| 29 | LC_ALL = "C.UTF-8"; | ||
| 30 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.DBus.Properties.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.DBus.Properties.xml new file mode 100644 index 00000000..7588e7a5 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.DBus.Properties.xml | |||
| @@ -0,0 +1,28 @@ | |||
| 1 | <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||
| 2 | "https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||
| 3 | <node> | ||
| 4 | <interface name="org.freedesktop.DBus.Properties"> | ||
| 5 | <method name="Get"> | ||
| 6 | <arg name="interface_name" direction="in" type="s"/> | ||
| 7 | <arg name="property_name" direction="in" type="s"/> | ||
| 8 | <arg name="value" direction="out" type="v"/> | ||
| 9 | </method> | ||
| 10 | <method name="GetAll"> | ||
| 11 | <arg name="interface_name" direction="in" type="s"/> | ||
| 12 | <arg name="props" direction="out" type="a{sv}"/> | ||
| 13 | <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/> | ||
| 14 | </method> | ||
| 15 | <method name="Set"> | ||
| 16 | <arg name="interface_name" direction="in" type="s"/> | ||
| 17 | <arg name="property_name" direction="in" type="s"/> | ||
| 18 | <arg name="value" direction="in" type="v"/> | ||
| 19 | </method> | ||
| 20 | <signal name="PropertiesChanged"> | ||
| 21 | <arg type="s" name="interface_name"/> | ||
| 22 | <arg type="a{sv}" name="changed_properties"/> | ||
| 23 | <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/> | ||
| 24 | <arg type="as" name="invalidated_properties"/> | ||
| 25 | </signal> | ||
| 26 | </interface> | ||
| 27 | </node> | ||
| 28 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Manager.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Manager.xml new file mode 100644 index 00000000..120a06d9 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Manager.xml | |||
| @@ -0,0 +1,445 @@ | |||
| 1 | <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||
| 2 | "https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||
| 3 | <node> | ||
| 4 | <interface name="org.freedesktop.login1.Manager"> | ||
| 5 | <property name="EnableWallMessages" type="b" access="readwrite"> | ||
| 6 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 7 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 8 | </property> | ||
| 9 | <property name="WallMessage" type="s" access="readwrite"> | ||
| 10 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 11 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 12 | </property> | ||
| 13 | <property name="NAutoVTs" type="u" access="read"> | ||
| 14 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 15 | </property> | ||
| 16 | <property name="KillOnlyUsers" type="as" access="read"> | ||
| 17 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 18 | </property> | ||
| 19 | <property name="KillExcludeUsers" type="as" access="read"> | ||
| 20 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 21 | </property> | ||
| 22 | <property name="KillUserProcesses" type="b" access="read"> | ||
| 23 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 24 | </property> | ||
| 25 | <property name="RebootParameter" type="s" access="read"> | ||
| 26 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 27 | </property> | ||
| 28 | <property name="RebootToFirmwareSetup" type="b" access="read"> | ||
| 29 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 30 | </property> | ||
| 31 | <property name="RebootToBootLoaderMenu" type="t" access="read"> | ||
| 32 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 33 | </property> | ||
| 34 | <property name="RebootToBootLoaderEntry" type="s" access="read"> | ||
| 35 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 36 | </property> | ||
| 37 | <property name="BootLoaderEntries" type="as" access="read"> | ||
| 38 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 39 | </property> | ||
| 40 | <property name="IdleHint" type="b" access="read"> | ||
| 41 | </property> | ||
| 42 | <property name="IdleSinceHint" type="t" access="read"> | ||
| 43 | </property> | ||
| 44 | <property name="IdleSinceHintMonotonic" type="t" access="read"> | ||
| 45 | </property> | ||
| 46 | <property name="BlockInhibited" type="s" access="read"> | ||
| 47 | </property> | ||
| 48 | <property name="BlockWeakInhibited" type="s" access="read"> | ||
| 49 | </property> | ||
| 50 | <property name="DelayInhibited" type="s" access="read"> | ||
| 51 | </property> | ||
| 52 | <property name="InhibitDelayMaxUSec" type="t" access="read"> | ||
| 53 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 54 | </property> | ||
| 55 | <property name="UserStopDelayUSec" type="t" access="read"> | ||
| 56 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 57 | </property> | ||
| 58 | <property name="SleepOperation" type="as" access="read"> | ||
| 59 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 60 | </property> | ||
| 61 | <property name="HandlePowerKey" type="s" access="read"> | ||
| 62 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 63 | </property> | ||
| 64 | <property name="HandlePowerKeyLongPress" type="s" access="read"> | ||
| 65 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 66 | </property> | ||
| 67 | <property name="HandleRebootKey" type="s" access="read"> | ||
| 68 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 69 | </property> | ||
| 70 | <property name="HandleRebootKeyLongPress" type="s" access="read"> | ||
| 71 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 72 | </property> | ||
| 73 | <property name="HandleSuspendKey" type="s" access="read"> | ||
| 74 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 75 | </property> | ||
| 76 | <property name="HandleSuspendKeyLongPress" type="s" access="read"> | ||
| 77 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 78 | </property> | ||
| 79 | <property name="HandleHibernateKey" type="s" access="read"> | ||
| 80 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 81 | </property> | ||
| 82 | <property name="HandleHibernateKeyLongPress" type="s" access="read"> | ||
| 83 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 84 | </property> | ||
| 85 | <property name="HandleLidSwitch" type="s" access="read"> | ||
| 86 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 87 | </property> | ||
| 88 | <property name="HandleLidSwitchExternalPower" type="s" access="read"> | ||
| 89 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 90 | </property> | ||
| 91 | <property name="HandleLidSwitchDocked" type="s" access="read"> | ||
| 92 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 93 | </property> | ||
| 94 | <property name="HandleSecureAttentionKey" type="s" access="read"> | ||
| 95 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 96 | </property> | ||
| 97 | <property name="HoldoffTimeoutUSec" type="t" access="read"> | ||
| 98 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 99 | </property> | ||
| 100 | <property name="IdleAction" type="s" access="read"> | ||
| 101 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 102 | </property> | ||
| 103 | <property name="IdleActionUSec" type="t" access="read"> | ||
| 104 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 105 | </property> | ||
| 106 | <property name="PreparingForShutdown" type="b" access="read"> | ||
| 107 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 108 | </property> | ||
| 109 | <property name="PreparingForShutdownWithMetadata" type="a{sv}" access="read"> | ||
| 110 | <annotation name="org.qtproject.QtDBus.QtTypeName" value="QVariantMap"/> | ||
| 111 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 112 | </property> | ||
| 113 | <property name="PreparingForSleep" type="b" access="read"> | ||
| 114 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 115 | </property> | ||
| 116 | <!-- <property name="ScheduledShutdown" type="(st)" access="read"> --> | ||
| 117 | <!-- </property> --> | ||
| 118 | <property name="DesignatedMaintenanceTime" type="s" access="read"> | ||
| 119 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 120 | </property> | ||
| 121 | <property name="Docked" type="b" access="read"> | ||
| 122 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 123 | </property> | ||
| 124 | <property name="LidClosed" type="b" access="read"> | ||
| 125 | </property> | ||
| 126 | <property name="OnExternalPower" type="b" access="read"> | ||
| 127 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 128 | </property> | ||
| 129 | <property name="RemoveIPC" type="b" access="read"> | ||
| 130 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 131 | </property> | ||
| 132 | <property name="RuntimeDirectorySize" type="t" access="read"> | ||
| 133 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 134 | </property> | ||
| 135 | <property name="RuntimeDirectoryInodesMax" type="t" access="read"> | ||
| 136 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 137 | </property> | ||
| 138 | <property name="InhibitorsMax" type="t" access="read"> | ||
| 139 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 140 | </property> | ||
| 141 | <property name="NCurrentInhibitors" type="t" access="read"> | ||
| 142 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 143 | </property> | ||
| 144 | <property name="SessionsMax" type="t" access="read"> | ||
| 145 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 146 | </property> | ||
| 147 | <property name="NCurrentSessions" type="t" access="read"> | ||
| 148 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 149 | </property> | ||
| 150 | <property name="StopIdleSessionUSec" type="t" access="read"> | ||
| 151 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 152 | </property> | ||
| 153 | <method name="GetSession"> | ||
| 154 | <arg type="s" name="session_id" direction="in"/> | ||
| 155 | <arg type="o" name="object_path" direction="out"/> | ||
| 156 | </method> | ||
| 157 | <method name="GetSessionByPID"> | ||
| 158 | <arg type="u" name="pid" direction="in"/> | ||
| 159 | <arg type="o" name="object_path" direction="out"/> | ||
| 160 | </method> | ||
| 161 | <method name="GetUser"> | ||
| 162 | <arg type="u" name="uid" direction="in"/> | ||
| 163 | <arg type="o" name="object_path" direction="out"/> | ||
| 164 | </method> | ||
| 165 | <method name="GetUserByPID"> | ||
| 166 | <arg type="u" name="pid" direction="in"/> | ||
| 167 | <arg type="o" name="object_path" direction="out"/> | ||
| 168 | </method> | ||
| 169 | <method name="GetSeat"> | ||
| 170 | <arg type="s" name="seat_id" direction="in"/> | ||
| 171 | <arg type="o" name="object_path" direction="out"/> | ||
| 172 | </method> | ||
| 173 | <!-- <method name="ListSessions"> --> | ||
| 174 | <!-- <arg type="a(susso)" name="sessions" direction="out"/> --> | ||
| 175 | <!-- </method> --> | ||
| 176 | <!-- <method name="ListSessionsEx"> --> | ||
| 177 | <!-- <arg type="a(sussussbto)" name="sessions" direction="out"/> --> | ||
| 178 | <!-- </method> --> | ||
| 179 | <!-- <method name="ListUsers"> --> | ||
| 180 | <!-- <arg type="a(uso)" name="users" direction="out"/> --> | ||
| 181 | <!-- </method> --> | ||
| 182 | <!-- <method name="ListSeats"> --> | ||
| 183 | <!-- <arg type="a(so)" name="seats" direction="out"/> --> | ||
| 184 | <!-- </method> --> | ||
| 185 | <!-- <method name="ListInhibitors"> --> | ||
| 186 | <!-- <arg type="a(ssssuu)" name="inhibitors" direction="out"/> --> | ||
| 187 | <!-- </method> --> | ||
| 188 | <!-- <method name="CreateSession"> --> | ||
| 189 | <!-- <arg type="u" name="uid" direction="in"/> --> | ||
| 190 | <!-- <arg type="u" name="pid" direction="in"/> --> | ||
| 191 | <!-- <arg type="s" name="service" direction="in"/> --> | ||
| 192 | <!-- <arg type="s" name="type" direction="in"/> --> | ||
| 193 | <!-- <arg type="s" name="class" direction="in"/> --> | ||
| 194 | <!-- <arg type="s" name="desktop" direction="in"/> --> | ||
| 195 | <!-- <arg type="s" name="seat_id" direction="in"/> --> | ||
| 196 | <!-- <arg type="u" name="vtnr" direction="in"/> --> | ||
| 197 | <!-- <arg type="s" name="tty" direction="in"/> --> | ||
| 198 | <!-- <arg type="s" name="display" direction="in"/> --> | ||
| 199 | <!-- <arg type="b" name="remote" direction="in"/> --> | ||
| 200 | <!-- <arg type="s" name="remote_user" direction="in"/> --> | ||
| 201 | <!-- <arg type="s" name="remote_host" direction="in"/> --> | ||
| 202 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 203 | <!-- <arg type="s" name="session_id" direction="out"/> --> | ||
| 204 | <!-- <arg type="o" name="object_path" direction="out"/> --> | ||
| 205 | <!-- <arg type="s" name="runtime_path" direction="out"/> --> | ||
| 206 | <!-- <arg type="h" name="fifo_fd" direction="out"/> --> | ||
| 207 | <!-- <arg type="u" name="uid" direction="out"/> --> | ||
| 208 | <!-- <arg type="s" name="seat_id" direction="out"/> --> | ||
| 209 | <!-- <arg type="u" name="vtnr" direction="out"/> --> | ||
| 210 | <!-- <arg type="b" name="existing" direction="out"/> --> | ||
| 211 | <!-- <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> --> | ||
| 212 | <!-- </method> --> | ||
| 213 | <!-- <method name="CreateSessionWithPIDFD"> --> | ||
| 214 | <!-- <arg type="u" name="uid" direction="in"/> --> | ||
| 215 | <!-- <arg type="h" name="pidfd" direction="in"/> --> | ||
| 216 | <!-- <arg type="s" name="service" direction="in"/> --> | ||
| 217 | <!-- <arg type="s" name="type" direction="in"/> --> | ||
| 218 | <!-- <arg type="s" name="class" direction="in"/> --> | ||
| 219 | <!-- <arg type="s" name="desktop" direction="in"/> --> | ||
| 220 | <!-- <arg type="s" name="seat_id" direction="in"/> --> | ||
| 221 | <!-- <arg type="u" name="vtnr" direction="in"/> --> | ||
| 222 | <!-- <arg type="s" name="tty" direction="in"/> --> | ||
| 223 | <!-- <arg type="s" name="display" direction="in"/> --> | ||
| 224 | <!-- <arg type="b" name="remote" direction="in"/> --> | ||
| 225 | <!-- <arg type="s" name="remote_user" direction="in"/> --> | ||
| 226 | <!-- <arg type="s" name="remote_host" direction="in"/> --> | ||
| 227 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 228 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 229 | <!-- <arg type="s" name="session_id" direction="out"/> --> | ||
| 230 | <!-- <arg type="o" name="object_path" direction="out"/> --> | ||
| 231 | <!-- <arg type="s" name="runtime_path" direction="out"/> --> | ||
| 232 | <!-- <arg type="h" name="fifo_fd" direction="out"/> --> | ||
| 233 | <!-- <arg type="u" name="uid" direction="out"/> --> | ||
| 234 | <!-- <arg type="s" name="seat_id" direction="out"/> --> | ||
| 235 | <!-- <arg type="u" name="vtnr" direction="out"/> --> | ||
| 236 | <!-- <arg type="b" name="existing" direction="out"/> --> | ||
| 237 | <!-- <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> --> | ||
| 238 | <!-- </method> --> | ||
| 239 | <method name="ReleaseSession"> | ||
| 240 | <arg type="s" name="session_id" direction="in"/> | ||
| 241 | </method> | ||
| 242 | <method name="ActivateSession"> | ||
| 243 | <arg type="s" name="session_id" direction="in"/> | ||
| 244 | </method> | ||
| 245 | <method name="ActivateSessionOnSeat"> | ||
| 246 | <arg type="s" name="session_id" direction="in"/> | ||
| 247 | <arg type="s" name="seat_id" direction="in"/> | ||
| 248 | </method> | ||
| 249 | <method name="LockSession"> | ||
| 250 | <arg type="s" name="session_id" direction="in"/> | ||
| 251 | </method> | ||
| 252 | <method name="UnlockSession"> | ||
| 253 | <arg type="s" name="session_id" direction="in"/> | ||
| 254 | </method> | ||
| 255 | <method name="LockSessions"> | ||
| 256 | </method> | ||
| 257 | <method name="UnlockSessions"> | ||
| 258 | </method> | ||
| 259 | <method name="KillSession"> | ||
| 260 | <arg type="s" name="session_id" direction="in"/> | ||
| 261 | <arg type="s" name="whom" direction="in"/> | ||
| 262 | <arg type="i" name="signal_number" direction="in"/> | ||
| 263 | </method> | ||
| 264 | <method name="KillUser"> | ||
| 265 | <arg type="u" name="uid" direction="in"/> | ||
| 266 | <arg type="i" name="signal_number" direction="in"/> | ||
| 267 | </method> | ||
| 268 | <method name="TerminateSession"> | ||
| 269 | <arg type="s" name="session_id" direction="in"/> | ||
| 270 | </method> | ||
| 271 | <method name="TerminateUser"> | ||
| 272 | <arg type="u" name="uid" direction="in"/> | ||
| 273 | </method> | ||
| 274 | <method name="TerminateSeat"> | ||
| 275 | <arg type="s" name="seat_id" direction="in"/> | ||
| 276 | </method> | ||
| 277 | <method name="SetUserLinger"> | ||
| 278 | <arg type="u" name="uid" direction="in"/> | ||
| 279 | <arg type="b" name="enable" direction="in"/> | ||
| 280 | <arg type="b" name="interactive" direction="in"/> | ||
| 281 | </method> | ||
| 282 | <method name="AttachDevice"> | ||
| 283 | <arg type="s" name="seat_id" direction="in"/> | ||
| 284 | <arg type="s" name="sysfs_path" direction="in"/> | ||
| 285 | <arg type="b" name="interactive" direction="in"/> | ||
| 286 | </method> | ||
| 287 | <method name="FlushDevices"> | ||
| 288 | <arg type="b" name="interactive" direction="in"/> | ||
| 289 | </method> | ||
| 290 | <method name="PowerOff"> | ||
| 291 | <arg type="b" name="interactive" direction="in"/> | ||
| 292 | </method> | ||
| 293 | <method name="PowerOffWithFlags"> | ||
| 294 | <arg type="t" name="flags" direction="in"/> | ||
| 295 | </method> | ||
| 296 | <method name="Reboot"> | ||
| 297 | <arg type="b" name="interactive" direction="in"/> | ||
| 298 | </method> | ||
| 299 | <method name="RebootWithFlags"> | ||
| 300 | <arg type="t" name="flags" direction="in"/> | ||
| 301 | </method> | ||
| 302 | <method name="Halt"> | ||
| 303 | <arg type="b" name="interactive" direction="in"/> | ||
| 304 | </method> | ||
| 305 | <method name="HaltWithFlags"> | ||
| 306 | <arg type="t" name="flags" direction="in"/> | ||
| 307 | </method> | ||
| 308 | <method name="Suspend"> | ||
| 309 | <arg type="b" name="interactive" direction="in"/> | ||
| 310 | </method> | ||
| 311 | <method name="SuspendWithFlags"> | ||
| 312 | <arg type="t" name="flags" direction="in"/> | ||
| 313 | </method> | ||
| 314 | <method name="Hibernate"> | ||
| 315 | <arg type="b" name="interactive" direction="in"/> | ||
| 316 | </method> | ||
| 317 | <method name="HibernateWithFlags"> | ||
| 318 | <arg type="t" name="flags" direction="in"/> | ||
| 319 | </method> | ||
| 320 | <method name="HybridSleep"> | ||
| 321 | <arg type="b" name="interactive" direction="in"/> | ||
| 322 | </method> | ||
| 323 | <method name="HybridSleepWithFlags"> | ||
| 324 | <arg type="t" name="flags" direction="in"/> | ||
| 325 | </method> | ||
| 326 | <method name="SuspendThenHibernate"> | ||
| 327 | <arg type="b" name="interactive" direction="in"/> | ||
| 328 | </method> | ||
| 329 | <method name="SuspendThenHibernateWithFlags"> | ||
| 330 | <arg type="t" name="flags" direction="in"/> | ||
| 331 | </method> | ||
| 332 | <method name="Sleep"> | ||
| 333 | <arg type="t" name="flags" direction="in"/> | ||
| 334 | </method> | ||
| 335 | <method name="CanPowerOff"> | ||
| 336 | <arg type="s" name="result" direction="out"/> | ||
| 337 | </method> | ||
| 338 | <method name="CanReboot"> | ||
| 339 | <arg type="s" name="result" direction="out"/> | ||
| 340 | </method> | ||
| 341 | <method name="CanHalt"> | ||
| 342 | <arg type="s" name="result" direction="out"/> | ||
| 343 | </method> | ||
| 344 | <method name="CanSuspend"> | ||
| 345 | <arg type="s" name="result" direction="out"/> | ||
| 346 | </method> | ||
| 347 | <method name="CanHibernate"> | ||
| 348 | <arg type="s" name="result" direction="out"/> | ||
| 349 | </method> | ||
| 350 | <method name="CanHybridSleep"> | ||
| 351 | <arg type="s" name="result" direction="out"/> | ||
| 352 | </method> | ||
| 353 | <method name="CanSuspendThenHibernate"> | ||
| 354 | <arg type="s" name="result" direction="out"/> | ||
| 355 | </method> | ||
| 356 | <method name="CanSleep"> | ||
| 357 | <arg type="s" name="result" direction="out"/> | ||
| 358 | </method> | ||
| 359 | <method name="ScheduleShutdown"> | ||
| 360 | <arg type="s" name="type" direction="in"/> | ||
| 361 | <arg type="t" name="usec" direction="in"/> | ||
| 362 | </method> | ||
| 363 | <method name="CancelScheduledShutdown"> | ||
| 364 | <arg type="b" name="cancelled" direction="out"/> | ||
| 365 | </method> | ||
| 366 | <method name="Inhibit"> | ||
| 367 | <arg type="s" name="what" direction="in"/> | ||
| 368 | <arg type="s" name="who" direction="in"/> | ||
| 369 | <arg type="s" name="why" direction="in"/> | ||
| 370 | <arg type="s" name="mode" direction="in"/> | ||
| 371 | <arg type="h" name="pipe_fd" direction="out"/> | ||
| 372 | </method> | ||
| 373 | <method name="CanRebootParameter"> | ||
| 374 | <arg type="s" name="result" direction="out"/> | ||
| 375 | </method> | ||
| 376 | <method name="SetRebootParameter"> | ||
| 377 | <arg type="s" name="parameter" direction="in"/> | ||
| 378 | </method> | ||
| 379 | <method name="CanRebootToFirmwareSetup"> | ||
| 380 | <arg type="s" name="result" direction="out"/> | ||
| 381 | </method> | ||
| 382 | <method name="SetRebootToFirmwareSetup"> | ||
| 383 | <arg type="b" name="enable" direction="in"/> | ||
| 384 | </method> | ||
| 385 | <method name="CanRebootToBootLoaderMenu"> | ||
| 386 | <arg type="s" name="result" direction="out"/> | ||
| 387 | </method> | ||
| 388 | <method name="SetRebootToBootLoaderMenu"> | ||
| 389 | <arg type="t" name="timeout" direction="in"/> | ||
| 390 | </method> | ||
| 391 | <method name="CanRebootToBootLoaderEntry"> | ||
| 392 | <arg type="s" name="result" direction="out"/> | ||
| 393 | </method> | ||
| 394 | <method name="SetRebootToBootLoaderEntry"> | ||
| 395 | <arg type="s" name="boot_loader_entry" direction="in"/> | ||
| 396 | </method> | ||
| 397 | <method name="SetWallMessage"> | ||
| 398 | <arg type="s" name="wall_message" direction="in"/> | ||
| 399 | <arg type="b" name="enable" direction="in"/> | ||
| 400 | </method> | ||
| 401 | <signal name="SecureAttentionKey"> | ||
| 402 | <arg type="s" name="seat_id"/> | ||
| 403 | <arg type="o" name="object_path"/> | ||
| 404 | </signal> | ||
| 405 | <signal name="SessionNew"> | ||
| 406 | <arg type="s" name="session_id"/> | ||
| 407 | <arg type="o" name="object_path"/> | ||
| 408 | </signal> | ||
| 409 | <signal name="SessionRemoved"> | ||
| 410 | <arg type="s" name="session_id"/> | ||
| 411 | <arg type="o" name="object_path"/> | ||
| 412 | </signal> | ||
| 413 | <signal name="UserNew"> | ||
| 414 | <arg type="u" name="uid"/> | ||
| 415 | <arg type="o" name="object_path"/> | ||
| 416 | </signal> | ||
| 417 | <signal name="UserRemoved"> | ||
| 418 | <arg type="u" name="uid"/> | ||
| 419 | <arg type="o" name="object_path"/> | ||
| 420 | </signal> | ||
| 421 | <signal name="SeatNew"> | ||
| 422 | <arg type="s" name="seat_id"/> | ||
| 423 | <arg type="o" name="object_path"/> | ||
| 424 | </signal> | ||
| 425 | <signal name="SeatRemoved"> | ||
| 426 | <arg type="s" name="seat_id"/> | ||
| 427 | <arg type="o" name="object_path"/> | ||
| 428 | </signal> | ||
| 429 | <signal name="PrepareForShutdown"> | ||
| 430 | <arg type="b" name="start"/> | ||
| 431 | </signal> | ||
| 432 | <signal name="PrepareForShutdownWithMetadata"> | ||
| 433 | <arg type="b" name="start"/> | ||
| 434 | <arg type="a{sv}" name="metadata"/> | ||
| 435 | <annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/> | ||
| 436 | </signal> | ||
| 437 | <signal name="PrepareForSleep"> | ||
| 438 | <arg type="b" name="start"/> | ||
| 439 | </signal> | ||
| 440 | </interface> | ||
| 441 | <node name="user"/> | ||
| 442 | <node name="session"/> | ||
| 443 | <node name="seat"/> | ||
| 444 | </node> | ||
| 445 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Session.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Session.xml new file mode 100644 index 00000000..7d6fc8ee --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.login1.Session.xml | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||
| 2 | "https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||
| 3 | <node> | ||
| 4 | <interface name="org.freedesktop.login1.Session"> | ||
| 5 | <property name="Id" type="s" access="read"> | ||
| 6 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 7 | </property> | ||
| 8 | <property name="User" type="o" access="read"> | ||
| 9 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 10 | </property> | ||
| 11 | <property name="Name" type="s" access="read"> | ||
| 12 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 13 | </property> | ||
| 14 | <property name="Timestamp" type="t" access="read"> | ||
| 15 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 16 | </property> | ||
| 17 | <property name="TimestampMonotonic" type="t" access="read"> | ||
| 18 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 19 | </property> | ||
| 20 | <property name="VTNr" type="u" access="read"> | ||
| 21 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 22 | </property> | ||
| 23 | <property name="Seat" type="o" access="read"> | ||
| 24 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 25 | </property> | ||
| 26 | <property name="TTY" type="s" access="read"> | ||
| 27 | </property> | ||
| 28 | <property name="Display" type="s" access="read"> | ||
| 29 | </property> | ||
| 30 | <property name="Remote" type="b" access="read"> | ||
| 31 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 32 | </property> | ||
| 33 | <property name="RemoteHost" type="s" access="read"> | ||
| 34 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 35 | </property> | ||
| 36 | <property name="RemoteUser" type="s" access="read"> | ||
| 37 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 38 | </property> | ||
| 39 | <property name="Service" type="s" access="read"> | ||
| 40 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 41 | </property> | ||
| 42 | <property name="Desktop" type="s" access="read"> | ||
| 43 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 44 | </property> | ||
| 45 | <property name="Scope" type="s" access="read"> | ||
| 46 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 47 | </property> | ||
| 48 | <property name="Leader" type="u" access="read"> | ||
| 49 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 50 | </property> | ||
| 51 | <property name="Audit" type="u" access="read"> | ||
| 52 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 53 | </property> | ||
| 54 | <property name="Type" type="s" access="read"> | ||
| 55 | </property> | ||
| 56 | <!-- <property name="Class" type="s" access="read"> --> | ||
| 57 | <!-- </property> --> | ||
| 58 | <property name="Active" type="b" access="read"> | ||
| 59 | </property> | ||
| 60 | <property name="State" type="s" access="read"> | ||
| 61 | </property> | ||
| 62 | <property name="IdleHint" type="b" access="read"> | ||
| 63 | </property> | ||
| 64 | <property name="IdleSinceHint" type="t" access="read"> | ||
| 65 | </property> | ||
| 66 | <property name="IdleSinceHintMonotonic" type="t" access="read"> | ||
| 67 | </property> | ||
| 68 | <property name="CanIdle" type="b" access="read"> | ||
| 69 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 70 | </property> | ||
| 71 | <property name="CanLock" type="b" access="read"> | ||
| 72 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 73 | </property> | ||
| 74 | <property name="LockedHint" type="b" access="read"> | ||
| 75 | </property> | ||
| 76 | <method name="Terminate"> | ||
| 77 | </method> | ||
| 78 | <method name="Activate"> | ||
| 79 | </method> | ||
| 80 | <!-- <method name="Lock"> --> | ||
| 81 | <!-- </method> --> | ||
| 82 | <!-- <method name="Unlock"> --> | ||
| 83 | <!-- </method> --> | ||
| 84 | <method name="SetIdleHint"> | ||
| 85 | <arg type="b" name="idle" direction="in"/> | ||
| 86 | </method> | ||
| 87 | <method name="SetLockedHint"> | ||
| 88 | <arg type="b" name="locked" direction="in"/> | ||
| 89 | </method> | ||
| 90 | <method name="Kill"> | ||
| 91 | <arg type="s" name="whom" direction="in"/> | ||
| 92 | <arg type="i" name="signal_number" direction="in"/> | ||
| 93 | </method> | ||
| 94 | <method name="TakeControl"> | ||
| 95 | <arg type="b" name="force" direction="in"/> | ||
| 96 | </method> | ||
| 97 | <method name="ReleaseControl"> | ||
| 98 | </method> | ||
| 99 | <method name="SetType"> | ||
| 100 | <arg type="s" name="type" direction="in"/> | ||
| 101 | </method> | ||
| 102 | <!-- <method name="SetClass"> --> | ||
| 103 | <!-- <arg type="s" name="class" direction="in"/> --> | ||
| 104 | <!-- </method> --> | ||
| 105 | <method name="SetDisplay"> | ||
| 106 | <arg type="s" name="display" direction="in"/> | ||
| 107 | </method> | ||
| 108 | <method name="SetTTY"> | ||
| 109 | <arg type="h" name="tty_fd" direction="in"/> | ||
| 110 | </method> | ||
| 111 | <method name="TakeDevice"> | ||
| 112 | <arg type="u" name="major" direction="in"/> | ||
| 113 | <arg type="u" name="minor" direction="in"/> | ||
| 114 | <arg type="h" name="fd" direction="out"/> | ||
| 115 | <arg type="b" name="inactive" direction="out"/> | ||
| 116 | </method> | ||
| 117 | <method name="ReleaseDevice"> | ||
| 118 | <arg type="u" name="major" direction="in"/> | ||
| 119 | <arg type="u" name="minor" direction="in"/> | ||
| 120 | </method> | ||
| 121 | <method name="PauseDeviceComplete"> | ||
| 122 | <arg type="u" name="major" direction="in"/> | ||
| 123 | <arg type="u" name="minor" direction="in"/> | ||
| 124 | </method> | ||
| 125 | <method name="SetBrightness"> | ||
| 126 | <arg type="s" name="subsystem" direction="in"/> | ||
| 127 | <arg type="s" name="name" direction="in"/> | ||
| 128 | <arg type="u" name="brightness" direction="in"/> | ||
| 129 | </method> | ||
| 130 | <signal name="PauseDevice"> | ||
| 131 | <arg type="u" name="major"/> | ||
| 132 | <arg type="u" name="minor"/> | ||
| 133 | <arg type="s" name="type"/> | ||
| 134 | </signal> | ||
| 135 | <signal name="ResumeDevice"> | ||
| 136 | <arg type="u" name="major"/> | ||
| 137 | <arg type="u" name="minor"/> | ||
| 138 | <arg type="h" name="fd"/> | ||
| 139 | </signal> | ||
| 140 | <signal name="Lock"> | ||
| 141 | </signal> | ||
| 142 | <signal name="Unlock"> | ||
| 143 | </signal> | ||
| 144 | </interface> | ||
| 145 | </node> | ||
| 146 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.systemd1.Manager.xml b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.systemd1.Manager.xml new file mode 100644 index 00000000..b4f84a13 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell-plugins/org.freedesktop.systemd1.Manager.xml | |||
| @@ -0,0 +1,817 @@ | |||
| 1 | <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" | ||
| 2 | "https://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> | ||
| 3 | <node> | ||
| 4 | <interface name="org.freedesktop.systemd1.Manager"> | ||
| 5 | <property name="Version" type="s" access="read"> | ||
| 6 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 7 | </property> | ||
| 8 | <property name="Features" type="s" access="read"> | ||
| 9 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 10 | </property> | ||
| 11 | <property name="Virtualization" type="s" access="read"> | ||
| 12 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 13 | </property> | ||
| 14 | <property name="ConfidentialVirtualization" type="s" access="read"> | ||
| 15 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 16 | </property> | ||
| 17 | <property name="Architecture" type="s" access="read"> | ||
| 18 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 19 | </property> | ||
| 20 | <property name="Tainted" type="s" access="read"> | ||
| 21 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 22 | </property> | ||
| 23 | <property name="FirmwareTimestamp" type="t" access="read"> | ||
| 24 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 25 | </property> | ||
| 26 | <property name="FirmwareTimestampMonotonic" type="t" access="read"> | ||
| 27 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 28 | </property> | ||
| 29 | <property name="LoaderTimestamp" type="t" access="read"> | ||
| 30 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 31 | </property> | ||
| 32 | <property name="LoaderTimestampMonotonic" type="t" access="read"> | ||
| 33 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 34 | </property> | ||
| 35 | <property name="KernelTimestamp" type="t" access="read"> | ||
| 36 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 37 | </property> | ||
| 38 | <property name="KernelTimestampMonotonic" type="t" access="read"> | ||
| 39 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 40 | </property> | ||
| 41 | <property name="InitRDTimestamp" type="t" access="read"> | ||
| 42 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 43 | </property> | ||
| 44 | <property name="InitRDTimestampMonotonic" type="t" access="read"> | ||
| 45 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 46 | </property> | ||
| 47 | <property name="UserspaceTimestamp" type="t" access="read"> | ||
| 48 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 49 | </property> | ||
| 50 | <property name="UserspaceTimestampMonotonic" type="t" access="read"> | ||
| 51 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 52 | </property> | ||
| 53 | <property name="FinishTimestamp" type="t" access="read"> | ||
| 54 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 55 | </property> | ||
| 56 | <property name="FinishTimestampMonotonic" type="t" access="read"> | ||
| 57 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 58 | </property> | ||
| 59 | <property name="ShutdownStartTimestamp" type="t" access="read"> | ||
| 60 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 61 | </property> | ||
| 62 | <property name="ShutdownStartTimestampMonotonic" type="t" access="read"> | ||
| 63 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 64 | </property> | ||
| 65 | <property name="SecurityStartTimestamp" type="t" access="read"> | ||
| 66 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 67 | </property> | ||
| 68 | <property name="SecurityStartTimestampMonotonic" type="t" access="read"> | ||
| 69 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 70 | </property> | ||
| 71 | <property name="SecurityFinishTimestamp" type="t" access="read"> | ||
| 72 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 73 | </property> | ||
| 74 | <property name="SecurityFinishTimestampMonotonic" type="t" access="read"> | ||
| 75 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 76 | </property> | ||
| 77 | <property name="GeneratorsStartTimestamp" type="t" access="read"> | ||
| 78 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 79 | </property> | ||
| 80 | <property name="GeneratorsStartTimestampMonotonic" type="t" access="read"> | ||
| 81 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 82 | </property> | ||
| 83 | <property name="GeneratorsFinishTimestamp" type="t" access="read"> | ||
| 84 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 85 | </property> | ||
| 86 | <property name="GeneratorsFinishTimestampMonotonic" type="t" access="read"> | ||
| 87 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 88 | </property> | ||
| 89 | <property name="UnitsLoadStartTimestamp" type="t" access="read"> | ||
| 90 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 91 | </property> | ||
| 92 | <property name="UnitsLoadStartTimestampMonotonic" type="t" access="read"> | ||
| 93 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 94 | </property> | ||
| 95 | <property name="UnitsLoadFinishTimestamp" type="t" access="read"> | ||
| 96 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 97 | </property> | ||
| 98 | <property name="UnitsLoadFinishTimestampMonotonic" type="t" access="read"> | ||
| 99 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 100 | </property> | ||
| 101 | <property name="UnitsLoadTimestamp" type="t" access="read"> | ||
| 102 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 103 | </property> | ||
| 104 | <property name="UnitsLoadTimestampMonotonic" type="t" access="read"> | ||
| 105 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 106 | </property> | ||
| 107 | <property name="InitRDSecurityStartTimestamp" type="t" access="read"> | ||
| 108 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 109 | </property> | ||
| 110 | <property name="InitRDSecurityStartTimestampMonotonic" type="t" access="read"> | ||
| 111 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 112 | </property> | ||
| 113 | <property name="InitRDSecurityFinishTimestamp" type="t" access="read"> | ||
| 114 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 115 | </property> | ||
| 116 | <property name="InitRDSecurityFinishTimestampMonotonic" type="t" access="read"> | ||
| 117 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 118 | </property> | ||
| 119 | <property name="InitRDGeneratorsStartTimestamp" type="t" access="read"> | ||
| 120 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 121 | </property> | ||
| 122 | <property name="InitRDGeneratorsStartTimestampMonotonic" type="t" access="read"> | ||
| 123 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 124 | </property> | ||
| 125 | <property name="InitRDGeneratorsFinishTimestamp" type="t" access="read"> | ||
| 126 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 127 | </property> | ||
| 128 | <property name="InitRDGeneratorsFinishTimestampMonotonic" type="t" access="read"> | ||
| 129 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 130 | </property> | ||
| 131 | <property name="InitRDUnitsLoadStartTimestamp" type="t" access="read"> | ||
| 132 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 133 | </property> | ||
| 134 | <property name="InitRDUnitsLoadStartTimestampMonotonic" type="t" access="read"> | ||
| 135 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 136 | </property> | ||
| 137 | <property name="InitRDUnitsLoadFinishTimestamp" type="t" access="read"> | ||
| 138 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 139 | </property> | ||
| 140 | <property name="InitRDUnitsLoadFinishTimestampMonotonic" type="t" access="read"> | ||
| 141 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 142 | </property> | ||
| 143 | <property name="LogLevel" type="s" access="readwrite"> | ||
| 144 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 145 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 146 | </property> | ||
| 147 | <property name="LogTarget" type="s" access="readwrite"> | ||
| 148 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 149 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 150 | </property> | ||
| 151 | <property name="NNames" type="u" access="read"> | ||
| 152 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 153 | </property> | ||
| 154 | <property name="NFailedUnits" type="u" access="read"> | ||
| 155 | </property> | ||
| 156 | <property name="NJobs" type="u" access="read"> | ||
| 157 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 158 | </property> | ||
| 159 | <property name="NInstalledJobs" type="u" access="read"> | ||
| 160 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 161 | </property> | ||
| 162 | <property name="NFailedJobs" type="u" access="read"> | ||
| 163 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 164 | </property> | ||
| 165 | <property name="Progress" type="d" access="read"> | ||
| 166 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 167 | </property> | ||
| 168 | <property name="Environment" type="as" access="read"> | ||
| 169 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 170 | </property> | ||
| 171 | <property name="ConfirmSpawn" type="b" access="read"> | ||
| 172 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 173 | </property> | ||
| 174 | <property name="ShowStatus" type="b" access="read"> | ||
| 175 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 176 | </property> | ||
| 177 | <property name="UnitPath" type="as" access="read"> | ||
| 178 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 179 | </property> | ||
| 180 | <property name="DefaultStandardOutput" type="s" access="read"> | ||
| 181 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 182 | </property> | ||
| 183 | <property name="DefaultStandardError" type="s" access="read"> | ||
| 184 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 185 | </property> | ||
| 186 | <property name="WatchdogDevice" type="s" access="read"> | ||
| 187 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 188 | </property> | ||
| 189 | <property name="WatchdogLastPingTimestamp" type="t" access="read"> | ||
| 190 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 191 | </property> | ||
| 192 | <property name="WatchdogLastPingTimestampMonotonic" type="t" access="read"> | ||
| 193 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 194 | </property> | ||
| 195 | <property name="RuntimeWatchdogUSec" type="t" access="readwrite"> | ||
| 196 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 197 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 198 | </property> | ||
| 199 | <property name="RuntimeWatchdogPreUSec" type="t" access="readwrite"> | ||
| 200 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 201 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 202 | </property> | ||
| 203 | <property name="RuntimeWatchdogPreGovernor" type="s" access="readwrite"> | ||
| 204 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 205 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 206 | </property> | ||
| 207 | <property name="RebootWatchdogUSec" type="t" access="readwrite"> | ||
| 208 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 209 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 210 | </property> | ||
| 211 | <property name="KExecWatchdogUSec" type="t" access="readwrite"> | ||
| 212 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 213 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 214 | </property> | ||
| 215 | <property name="ServiceWatchdogs" type="b" access="readwrite"> | ||
| 216 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 217 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 218 | </property> | ||
| 219 | <property name="ControlGroup" type="s" access="read"> | ||
| 220 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 221 | </property> | ||
| 222 | <property name="SystemState" type="s" access="read"> | ||
| 223 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 224 | </property> | ||
| 225 | <property name="ExitCode" type="y" access="read"> | ||
| 226 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 227 | </property> | ||
| 228 | <property name="DefaultTimerAccuracyUSec" type="t" access="read"> | ||
| 229 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 230 | </property> | ||
| 231 | <property name="DefaultTimeoutStartUSec" type="t" access="read"> | ||
| 232 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 233 | </property> | ||
| 234 | <property name="DefaultTimeoutStopUSec" type="t" access="read"> | ||
| 235 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 236 | </property> | ||
| 237 | <property name="DefaultTimeoutAbortUSec" type="t" access="read"> | ||
| 238 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 239 | </property> | ||
| 240 | <property name="DefaultDeviceTimeoutUSec" type="t" access="read"> | ||
| 241 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 242 | </property> | ||
| 243 | <property name="DefaultRestartUSec" type="t" access="read"> | ||
| 244 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 245 | </property> | ||
| 246 | <property name="DefaultStartLimitIntervalUSec" type="t" access="read"> | ||
| 247 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 248 | </property> | ||
| 249 | <property name="DefaultStartLimitBurst" type="u" access="read"> | ||
| 250 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 251 | </property> | ||
| 252 | <property name="DefaultCPUAccounting" type="b" access="read"> | ||
| 253 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 254 | </property> | ||
| 255 | <property name="DefaultBlockIOAccounting" type="b" access="read"> | ||
| 256 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 257 | </property> | ||
| 258 | <property name="DefaultIOAccounting" type="b" access="read"> | ||
| 259 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 260 | </property> | ||
| 261 | <property name="DefaultIPAccounting" type="b" access="read"> | ||
| 262 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 263 | </property> | ||
| 264 | <property name="DefaultMemoryAccounting" type="b" access="read"> | ||
| 265 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 266 | </property> | ||
| 267 | <property name="DefaultTasksAccounting" type="b" access="read"> | ||
| 268 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 269 | </property> | ||
| 270 | <property name="DefaultLimitCPU" type="t" access="read"> | ||
| 271 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 272 | </property> | ||
| 273 | <property name="DefaultLimitCPUSoft" type="t" access="read"> | ||
| 274 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 275 | </property> | ||
| 276 | <property name="DefaultLimitFSIZE" type="t" access="read"> | ||
| 277 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 278 | </property> | ||
| 279 | <property name="DefaultLimitFSIZESoft" type="t" access="read"> | ||
| 280 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 281 | </property> | ||
| 282 | <property name="DefaultLimitDATA" type="t" access="read"> | ||
| 283 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 284 | </property> | ||
| 285 | <property name="DefaultLimitDATASoft" type="t" access="read"> | ||
| 286 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 287 | </property> | ||
| 288 | <property name="DefaultLimitSTACK" type="t" access="read"> | ||
| 289 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 290 | </property> | ||
| 291 | <property name="DefaultLimitSTACKSoft" type="t" access="read"> | ||
| 292 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 293 | </property> | ||
| 294 | <property name="DefaultLimitCORE" type="t" access="read"> | ||
| 295 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 296 | </property> | ||
| 297 | <property name="DefaultLimitCORESoft" type="t" access="read"> | ||
| 298 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 299 | </property> | ||
| 300 | <property name="DefaultLimitRSS" type="t" access="read"> | ||
| 301 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 302 | </property> | ||
| 303 | <property name="DefaultLimitRSSSoft" type="t" access="read"> | ||
| 304 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 305 | </property> | ||
| 306 | <property name="DefaultLimitNOFILE" type="t" access="read"> | ||
| 307 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 308 | </property> | ||
| 309 | <property name="DefaultLimitNOFILESoft" type="t" access="read"> | ||
| 310 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 311 | </property> | ||
| 312 | <property name="DefaultLimitAS" type="t" access="read"> | ||
| 313 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 314 | </property> | ||
| 315 | <property name="DefaultLimitASSoft" type="t" access="read"> | ||
| 316 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 317 | </property> | ||
| 318 | <property name="DefaultLimitNPROC" type="t" access="read"> | ||
| 319 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 320 | </property> | ||
| 321 | <property name="DefaultLimitNPROCSoft" type="t" access="read"> | ||
| 322 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 323 | </property> | ||
| 324 | <property name="DefaultLimitMEMLOCK" type="t" access="read"> | ||
| 325 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 326 | </property> | ||
| 327 | <property name="DefaultLimitMEMLOCKSoft" type="t" access="read"> | ||
| 328 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 329 | </property> | ||
| 330 | <property name="DefaultLimitLOCKS" type="t" access="read"> | ||
| 331 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 332 | </property> | ||
| 333 | <property name="DefaultLimitLOCKSSoft" type="t" access="read"> | ||
| 334 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 335 | </property> | ||
| 336 | <property name="DefaultLimitSIGPENDING" type="t" access="read"> | ||
| 337 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 338 | </property> | ||
| 339 | <property name="DefaultLimitSIGPENDINGSoft" type="t" access="read"> | ||
| 340 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 341 | </property> | ||
| 342 | <property name="DefaultLimitMSGQUEUE" type="t" access="read"> | ||
| 343 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 344 | </property> | ||
| 345 | <property name="DefaultLimitMSGQUEUESoft" type="t" access="read"> | ||
| 346 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 347 | </property> | ||
| 348 | <property name="DefaultLimitNICE" type="t" access="read"> | ||
| 349 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 350 | </property> | ||
| 351 | <property name="DefaultLimitNICESoft" type="t" access="read"> | ||
| 352 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 353 | </property> | ||
| 354 | <property name="DefaultLimitRTPRIO" type="t" access="read"> | ||
| 355 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 356 | </property> | ||
| 357 | <property name="DefaultLimitRTPRIOSoft" type="t" access="read"> | ||
| 358 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 359 | </property> | ||
| 360 | <property name="DefaultLimitRTTIME" type="t" access="read"> | ||
| 361 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 362 | </property> | ||
| 363 | <property name="DefaultLimitRTTIMESoft" type="t" access="read"> | ||
| 364 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 365 | </property> | ||
| 366 | <property name="DefaultTasksMax" type="t" access="read"> | ||
| 367 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 368 | </property> | ||
| 369 | <property name="DefaultMemoryPressureThresholdUSec" type="t" access="read"> | ||
| 370 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 371 | </property> | ||
| 372 | <property name="DefaultMemoryPressureWatch" type="s" access="read"> | ||
| 373 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/> | ||
| 374 | </property> | ||
| 375 | <property name="TimerSlackNSec" type="t" access="read"> | ||
| 376 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 377 | </property> | ||
| 378 | <property name="DefaultOOMPolicy" type="s" access="read"> | ||
| 379 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 380 | </property> | ||
| 381 | <property name="DefaultOOMScoreAdjust" type="i" access="read"> | ||
| 382 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 383 | </property> | ||
| 384 | <property name="CtrlAltDelBurstAction" type="s" access="read"> | ||
| 385 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 386 | </property> | ||
| 387 | <property name="SoftRebootsCount" type="u" access="read"> | ||
| 388 | <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> | ||
| 389 | </property> | ||
| 390 | <method name="GetUnit"> | ||
| 391 | <arg type="s" name="name" direction="in"/> | ||
| 392 | <arg type="o" name="unit" direction="out"/> | ||
| 393 | </method> | ||
| 394 | <method name="GetUnitByPID"> | ||
| 395 | <arg type="u" name="pid" direction="in"/> | ||
| 396 | <arg type="o" name="unit" direction="out"/> | ||
| 397 | </method> | ||
| 398 | <method name="GetUnitByInvocationID"> | ||
| 399 | <arg type="ay" name="invocation_id" direction="in"/> | ||
| 400 | <arg type="o" name="unit" direction="out"/> | ||
| 401 | </method> | ||
| 402 | <method name="GetUnitByControlGroup"> | ||
| 403 | <arg type="s" name="cgroup" direction="in"/> | ||
| 404 | <arg type="o" name="unit" direction="out"/> | ||
| 405 | </method> | ||
| 406 | <method name="GetUnitByPIDFD"> | ||
| 407 | <arg type="h" name="pidfd" direction="in"/> | ||
| 408 | <arg type="o" name="unit" direction="out"/> | ||
| 409 | <arg type="s" name="unit_id" direction="out"/> | ||
| 410 | <arg type="ay" name="invocation_id" direction="out"/> | ||
| 411 | </method> | ||
| 412 | <method name="LoadUnit"> | ||
| 413 | <arg type="s" name="name" direction="in"/> | ||
| 414 | <arg type="o" name="unit" direction="out"/> | ||
| 415 | </method> | ||
| 416 | <method name="StartUnit"> | ||
| 417 | <arg type="s" name="name" direction="in"/> | ||
| 418 | <arg type="s" name="mode" direction="in"/> | ||
| 419 | <arg type="o" name="job" direction="out"/> | ||
| 420 | </method> | ||
| 421 | <method name="StartUnitWithFlags"> | ||
| 422 | <arg type="s" name="name" direction="in"/> | ||
| 423 | <arg type="s" name="mode" direction="in"/> | ||
| 424 | <arg type="t" name="flags" direction="in"/> | ||
| 425 | <arg type="o" name="job" direction="out"/> | ||
| 426 | </method> | ||
| 427 | <method name="StartUnitReplace"> | ||
| 428 | <arg type="s" name="old_unit" direction="in"/> | ||
| 429 | <arg type="s" name="new_unit" direction="in"/> | ||
| 430 | <arg type="s" name="mode" direction="in"/> | ||
| 431 | <arg type="o" name="job" direction="out"/> | ||
| 432 | </method> | ||
| 433 | <method name="StopUnit"> | ||
| 434 | <arg type="s" name="name" direction="in"/> | ||
| 435 | <arg type="s" name="mode" direction="in"/> | ||
| 436 | <arg type="o" name="job" direction="out"/> | ||
| 437 | </method> | ||
| 438 | <method name="ReloadUnit"> | ||
| 439 | <arg type="s" name="name" direction="in"/> | ||
| 440 | <arg type="s" name="mode" direction="in"/> | ||
| 441 | <arg type="o" name="job" direction="out"/> | ||
| 442 | </method> | ||
| 443 | <method name="RestartUnit"> | ||
| 444 | <arg type="s" name="name" direction="in"/> | ||
| 445 | <arg type="s" name="mode" direction="in"/> | ||
| 446 | <arg type="o" name="job" direction="out"/> | ||
| 447 | </method> | ||
| 448 | <method name="TryRestartUnit"> | ||
| 449 | <arg type="s" name="name" direction="in"/> | ||
| 450 | <arg type="s" name="mode" direction="in"/> | ||
| 451 | <arg type="o" name="job" direction="out"/> | ||
| 452 | </method> | ||
| 453 | <method name="ReloadOrRestartUnit"> | ||
| 454 | <arg type="s" name="name" direction="in"/> | ||
| 455 | <arg type="s" name="mode" direction="in"/> | ||
| 456 | <arg type="o" name="job" direction="out"/> | ||
| 457 | </method> | ||
| 458 | <method name="ReloadOrTryRestartUnit"> | ||
| 459 | <arg type="s" name="name" direction="in"/> | ||
| 460 | <arg type="s" name="mode" direction="in"/> | ||
| 461 | <arg type="o" name="job" direction="out"/> | ||
| 462 | </method> | ||
| 463 | <!-- <method name="EnqueueUnitJob"> --> | ||
| 464 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 465 | <!-- <arg type="s" name="job_type" direction="in"/> --> | ||
| 466 | <!-- <arg type="s" name="job_mode" direction="in"/> --> | ||
| 467 | <!-- <arg type="u" name="job_id" direction="out"/> --> | ||
| 468 | <!-- <arg type="o" name="job_path" direction="out"/> --> | ||
| 469 | <!-- <arg type="s" name="unit_id" direction="out"/> --> | ||
| 470 | <!-- <arg type="o" name="unit_path" direction="out"/> --> | ||
| 471 | <!-- <arg type="s" name="job_type" direction="out"/> --> | ||
| 472 | <!-- <arg type="a(uosos)" name="affected_jobs" direction="out"/> --> | ||
| 473 | <!-- </method> --> | ||
| 474 | <method name="KillUnit"> | ||
| 475 | <arg type="s" name="name" direction="in"/> | ||
| 476 | <arg type="s" name="whom" direction="in"/> | ||
| 477 | <arg type="i" name="signal" direction="in"/> | ||
| 478 | </method> | ||
| 479 | <method name="QueueSignalUnit"> | ||
| 480 | <arg type="s" name="name" direction="in"/> | ||
| 481 | <arg type="s" name="whom" direction="in"/> | ||
| 482 | <arg type="i" name="signal" direction="in"/> | ||
| 483 | <arg type="i" name="value" direction="in"/> | ||
| 484 | </method> | ||
| 485 | <method name="CleanUnit"> | ||
| 486 | <arg type="s" name="name" direction="in"/> | ||
| 487 | <arg type="as" name="mask" direction="in"/> | ||
| 488 | </method> | ||
| 489 | <method name="FreezeUnit"> | ||
| 490 | <arg type="s" name="name" direction="in"/> | ||
| 491 | </method> | ||
| 492 | <method name="ThawUnit"> | ||
| 493 | <arg type="s" name="name" direction="in"/> | ||
| 494 | </method> | ||
| 495 | <method name="ResetFailedUnit"> | ||
| 496 | <arg type="s" name="name" direction="in"/> | ||
| 497 | </method> | ||
| 498 | <!-- <method name="SetUnitProperties"> --> | ||
| 499 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 500 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 501 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 502 | <!-- </method> --> | ||
| 503 | <method name="BindMountUnit"> | ||
| 504 | <arg type="s" name="name" direction="in"/> | ||
| 505 | <arg type="s" name="source" direction="in"/> | ||
| 506 | <arg type="s" name="destination" direction="in"/> | ||
| 507 | <arg type="b" name="read_only" direction="in"/> | ||
| 508 | <arg type="b" name="mkdir" direction="in"/> | ||
| 509 | </method> | ||
| 510 | <!-- <method name="MountImageUnit"> --> | ||
| 511 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 512 | <!-- <arg type="s" name="source" direction="in"/> --> | ||
| 513 | <!-- <arg type="s" name="destination" direction="in"/> --> | ||
| 514 | <!-- <arg type="b" name="read_only" direction="in"/> --> | ||
| 515 | <!-- <arg type="b" name="mkdir" direction="in"/> --> | ||
| 516 | <!-- <arg type="a(ss)" name="options" direction="in"/> --> | ||
| 517 | <!-- </method> --> | ||
| 518 | <method name="RefUnit"> | ||
| 519 | <arg type="s" name="name" direction="in"/> | ||
| 520 | </method> | ||
| 521 | <method name="UnrefUnit"> | ||
| 522 | <arg type="s" name="name" direction="in"/> | ||
| 523 | </method> | ||
| 524 | <!-- <method name="StartTransientUnit"> --> | ||
| 525 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 526 | <!-- <arg type="s" name="mode" direction="in"/> --> | ||
| 527 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 528 | <!-- <arg type="a(sa(sv))" name="aux" direction="in"/> --> | ||
| 529 | <!-- <arg type="o" name="job" direction="out"/> --> | ||
| 530 | <!-- </method> --> | ||
| 531 | <!-- <method name="GetUnitProcesses"> --> | ||
| 532 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 533 | <!-- <arg type="a(sus)" name="processes" direction="out"/> --> | ||
| 534 | <!-- </method> --> | ||
| 535 | <!-- <method name="AttachProcessesToUnit"> --> | ||
| 536 | <!-- <arg type="s" name="unit_name" direction="in"/> --> | ||
| 537 | <!-- <arg type="s" name="subcgroup" direction="in"/> --> | ||
| 538 | <!-- <arg type="au" name="pids" direction="in"/> --> | ||
| 539 | <!-- </method> --> | ||
| 540 | <method name="AbandonScope"> | ||
| 541 | <arg type="s" name="name" direction="in"/> | ||
| 542 | </method> | ||
| 543 | <method name="GetJob"> | ||
| 544 | <arg type="u" name="id" direction="in"/> | ||
| 545 | <arg type="o" name="job" direction="out"/> | ||
| 546 | </method> | ||
| 547 | <!-- <method name="GetJobAfter"> --> | ||
| 548 | <!-- <arg type="u" name="id" direction="in"/> --> | ||
| 549 | <!-- <arg type="a(usssoo)" name="jobs" direction="out"/> --> | ||
| 550 | <!-- </method> --> | ||
| 551 | <!-- <method name="GetJobBefore"> --> | ||
| 552 | <!-- <arg type="u" name="id" direction="in"/> --> | ||
| 553 | <!-- <arg type="a(usssoo)" name="jobs" direction="out"/> --> | ||
| 554 | <!-- </method> --> | ||
| 555 | <method name="CancelJob"> | ||
| 556 | <arg type="u" name="id" direction="in"/> | ||
| 557 | </method> | ||
| 558 | <method name="ClearJobs"> | ||
| 559 | </method> | ||
| 560 | <method name="ResetFailed"> | ||
| 561 | </method> | ||
| 562 | <method name="SetShowStatus"> | ||
| 563 | <arg type="s" name="mode" direction="in"/> | ||
| 564 | </method> | ||
| 565 | <!-- <method name="ListUnits"> --> | ||
| 566 | <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> --> | ||
| 567 | <!-- </method> --> | ||
| 568 | <!-- <method name="ListUnitsFiltered"> --> | ||
| 569 | <!-- <arg type="as" name="states" direction="in"/> --> | ||
| 570 | <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> --> | ||
| 571 | <!-- </method> --> | ||
| 572 | <!-- <method name="ListUnitsByPatterns"> --> | ||
| 573 | <!-- <arg type="as" name="states" direction="in"/> --> | ||
| 574 | <!-- <arg type="as" name="patterns" direction="in"/> --> | ||
| 575 | <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> --> | ||
| 576 | <!-- </method> --> | ||
| 577 | <!-- <method name="ListUnitsByNames"> --> | ||
| 578 | <!-- <arg type="as" name="names" direction="in"/> --> | ||
| 579 | <!-- <arg type="a(ssssssouso)" name="units" direction="out"/> --> | ||
| 580 | <!-- </method> --> | ||
| 581 | <!-- <method name="ListJobs"> --> | ||
| 582 | <!-- <arg type="a(usssoo)" name="jobs" direction="out"/> --> | ||
| 583 | <!-- </method> --> | ||
| 584 | <method name="Subscribe"> | ||
| 585 | </method> | ||
| 586 | <method name="Unsubscribe"> | ||
| 587 | </method> | ||
| 588 | <method name="Dump"> | ||
| 589 | <arg type="s" name="output" direction="out"/> | ||
| 590 | </method> | ||
| 591 | <method name="DumpUnitsMatchingPatterns"> | ||
| 592 | <arg type="as" name="patterns" direction="in"/> | ||
| 593 | <arg type="s" name="output" direction="out"/> | ||
| 594 | </method> | ||
| 595 | <method name="DumpByFileDescriptor"> | ||
| 596 | <arg type="h" name="fd" direction="out"/> | ||
| 597 | </method> | ||
| 598 | <method name="DumpUnitsMatchingPatternsByFileDescriptor"> | ||
| 599 | <arg type="as" name="patterns" direction="in"/> | ||
| 600 | <arg type="h" name="fd" direction="out"/> | ||
| 601 | </method> | ||
| 602 | <method name="Reload"> | ||
| 603 | </method> | ||
| 604 | <method name="Reexecute"> | ||
| 605 | <annotation name="org.freedesktop.DBus.Method.NoReply" value="true"/> | ||
| 606 | </method> | ||
| 607 | <method name="Exit"> | ||
| 608 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 609 | </method> | ||
| 610 | <method name="Reboot"> | ||
| 611 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 612 | </method> | ||
| 613 | <method name="SoftReboot"> | ||
| 614 | <arg type="s" name="new_root" direction="in"/> | ||
| 615 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 616 | </method> | ||
| 617 | <method name="PowerOff"> | ||
| 618 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 619 | </method> | ||
| 620 | <method name="Halt"> | ||
| 621 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 622 | </method> | ||
| 623 | <method name="KExec"> | ||
| 624 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 625 | </method> | ||
| 626 | <method name="SwitchRoot"> | ||
| 627 | <arg type="s" name="new_root" direction="in"/> | ||
| 628 | <arg type="s" name="init" direction="in"/> | ||
| 629 | <annotation name="org.freedesktop.systemd1.Privileged" value="true"/> | ||
| 630 | </method> | ||
| 631 | <method name="SetEnvironment"> | ||
| 632 | <arg type="as" name="assignments" direction="in"/> | ||
| 633 | </method> | ||
| 634 | <method name="UnsetEnvironment"> | ||
| 635 | <arg type="as" name="names" direction="in"/> | ||
| 636 | </method> | ||
| 637 | <method name="UnsetAndSetEnvironment"> | ||
| 638 | <arg type="as" name="names" direction="in"/> | ||
| 639 | <arg type="as" name="assignments" direction="in"/> | ||
| 640 | </method> | ||
| 641 | <method name="EnqueueMarkedJobs"> | ||
| 642 | <arg type="ao" name="jobs" direction="out"/> | ||
| 643 | </method> | ||
| 644 | <!-- <method name="ListUnitFiles"> --> | ||
| 645 | <!-- <arg type="a(ss)" name="unit_files" direction="out"/> --> | ||
| 646 | <!-- </method> --> | ||
| 647 | <!-- <method name="ListUnitFilesByPatterns"> --> | ||
| 648 | <!-- <arg type="as" name="states" direction="in"/> --> | ||
| 649 | <!-- <arg type="as" name="patterns" direction="in"/> --> | ||
| 650 | <!-- <arg type="a(ss)" name="unit_files" direction="out"/> --> | ||
| 651 | <!-- </method> --> | ||
| 652 | <method name="GetUnitFileState"> | ||
| 653 | <arg type="s" name="file" direction="in"/> | ||
| 654 | <arg type="s" name="state" direction="out"/> | ||
| 655 | </method> | ||
| 656 | <!-- <method name="EnableUnitFiles"> --> | ||
| 657 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 658 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 659 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 660 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 661 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 662 | <!-- </method> --> | ||
| 663 | <!-- <method name="DisableUnitFiles"> --> | ||
| 664 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 665 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 666 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 667 | <!-- </method> --> | ||
| 668 | <!-- <method name="EnableUnitFilesWithFlags"> --> | ||
| 669 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 670 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 671 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 672 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 673 | <!-- </method> --> | ||
| 674 | <!-- <method name="DisableUnitFilesWithFlags"> --> | ||
| 675 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 676 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 677 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 678 | <!-- </method> --> | ||
| 679 | <!-- <method name="DisableUnitFilesWithFlagsAndInstallInfo"> --> | ||
| 680 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 681 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 682 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 683 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 684 | <!-- </method> --> | ||
| 685 | <!-- <method name="ReenableUnitFiles"> --> | ||
| 686 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 687 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 688 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 689 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 690 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 691 | <!-- </method> --> | ||
| 692 | <!-- <method name="LinkUnitFiles"> --> | ||
| 693 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 694 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 695 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 696 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 697 | <!-- </method> --> | ||
| 698 | <!-- <method name="PresetUnitFiles"> --> | ||
| 699 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 700 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 701 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 702 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 703 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 704 | <!-- </method> --> | ||
| 705 | <!-- <method name="PresetUnitFilesWithMode"> --> | ||
| 706 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 707 | <!-- <arg type="s" name="mode" direction="in"/> --> | ||
| 708 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 709 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 710 | <!-- <arg type="b" name="carries_install_info" direction="out"/> --> | ||
| 711 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 712 | <!-- </method> --> | ||
| 713 | <!-- <method name="MaskUnitFiles"> --> | ||
| 714 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 715 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 716 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 717 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 718 | <!-- </method> --> | ||
| 719 | <!-- <method name="UnmaskUnitFiles"> --> | ||
| 720 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 721 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 722 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 723 | <!-- </method> --> | ||
| 724 | <!-- <method name="RevertUnitFiles"> --> | ||
| 725 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 726 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 727 | <!-- </method> --> | ||
| 728 | <!-- <method name="SetDefaultTarget"> --> | ||
| 729 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 730 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 731 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 732 | <!-- </method> --> | ||
| 733 | <method name="GetDefaultTarget"> | ||
| 734 | <arg type="s" name="name" direction="out"/> | ||
| 735 | </method> | ||
| 736 | <!-- <method name="PresetAllUnitFiles"> --> | ||
| 737 | <!-- <arg type="s" name="mode" direction="in"/> --> | ||
| 738 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 739 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 740 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 741 | <!-- </method> --> | ||
| 742 | <!-- <method name="AddDependencyUnitFiles"> --> | ||
| 743 | <!-- <arg type="as" name="files" direction="in"/> --> | ||
| 744 | <!-- <arg type="s" name="target" direction="in"/> --> | ||
| 745 | <!-- <arg type="s" name="type" direction="in"/> --> | ||
| 746 | <!-- <arg type="b" name="runtime" direction="in"/> --> | ||
| 747 | <!-- <arg type="b" name="force" direction="in"/> --> | ||
| 748 | <!-- <arg type="a(sss)" name="changes" direction="out"/> --> | ||
| 749 | <!-- </method> --> | ||
| 750 | <method name="GetUnitFileLinks"> | ||
| 751 | <arg type="s" name="name" direction="in"/> | ||
| 752 | <arg type="b" name="runtime" direction="in"/> | ||
| 753 | <arg type="as" name="links" direction="out"/> | ||
| 754 | </method> | ||
| 755 | <method name="SetExitCode"> | ||
| 756 | <arg type="y" name="number" direction="in"/> | ||
| 757 | </method> | ||
| 758 | <method name="LookupDynamicUserByName"> | ||
| 759 | <arg type="s" name="name" direction="in"/> | ||
| 760 | <arg type="u" name="uid" direction="out"/> | ||
| 761 | </method> | ||
| 762 | <method name="LookupDynamicUserByUID"> | ||
| 763 | <arg type="u" name="uid" direction="in"/> | ||
| 764 | <arg type="s" name="name" direction="out"/> | ||
| 765 | </method> | ||
| 766 | <!-- <method name="GetDynamicUsers"> --> | ||
| 767 | <!-- <arg type="a(us)" name="users" direction="out"/> --> | ||
| 768 | <!-- </method> --> | ||
| 769 | <!-- <method name="DumpUnitFileDescriptorStore"> --> | ||
| 770 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 771 | <!-- <arg type="a(suuutuusu)" name="entries" direction="out"/> --> | ||
| 772 | <!-- </method> --> | ||
| 773 | <!-- <method name="StartAuxiliaryScope"> --> | ||
| 774 | <!-- <arg type="s" name="name" direction="in"/> --> | ||
| 775 | <!-- <arg type="ah" name="pidfds" direction="in"/> --> | ||
| 776 | <!-- <arg type="t" name="flags" direction="in"/> --> | ||
| 777 | <!-- <arg type="a(sv)" name="properties" direction="in"/> --> | ||
| 778 | <!-- <arg type="o" name="job" direction="out"/> --> | ||
| 779 | <!-- <annotation name="org.freedesktop.DBus.Deprecated" value="true"/> --> | ||
| 780 | <!-- </method> --> | ||
| 781 | <signal name="UnitNew"> | ||
| 782 | <arg type="s" name="id"/> | ||
| 783 | <arg type="o" name="unit"/> | ||
| 784 | </signal> | ||
| 785 | <signal name="UnitRemoved"> | ||
| 786 | <arg type="s" name="id"/> | ||
| 787 | <arg type="o" name="unit"/> | ||
| 788 | </signal> | ||
| 789 | <signal name="JobNew"> | ||
| 790 | <arg type="u" name="id"/> | ||
| 791 | <arg type="o" name="job"/> | ||
| 792 | <arg type="s" name="unit"/> | ||
| 793 | </signal> | ||
| 794 | <signal name="JobRemoved"> | ||
| 795 | <arg type="u" name="id"/> | ||
| 796 | <arg type="o" name="job"/> | ||
| 797 | <arg type="s" name="unit"/> | ||
| 798 | <arg type="s" name="result"/> | ||
| 799 | </signal> | ||
| 800 | <signal name="StartupFinished"> | ||
| 801 | <arg type="t" name="firmware"/> | ||
| 802 | <arg type="t" name="loader"/> | ||
| 803 | <arg type="t" name="kernel"/> | ||
| 804 | <arg type="t" name="initrd"/> | ||
| 805 | <arg type="t" name="userspace"/> | ||
| 806 | <arg type="t" name="total"/> | ||
| 807 | </signal> | ||
| 808 | <signal name="UnitFilesChanged"> | ||
| 809 | </signal> | ||
| 810 | <signal name="Reloading"> | ||
| 811 | <arg type="b" name="active"/> | ||
| 812 | </signal> | ||
| 813 | </interface> | ||
| 814 | <node name="unit"/> | ||
| 815 | <node name="job"/> | ||
| 816 | </node> | ||
| 817 | |||
diff --git a/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml b/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml new file mode 100644 index 00000000..dcc23279 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/ActiveWindowDisplay.qml | |||
| @@ -0,0 +1,172 @@ | |||
| 1 | import QtQuick | ||
| 2 | import qs.Services | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Widgets | ||
| 5 | |||
| 6 | Item { | ||
| 7 | id: activeWindowDisplay | ||
| 8 | |||
| 9 | required property int maxWidth | ||
| 10 | required property var screen | ||
| 11 | |||
| 12 | property var activeWindow: { | ||
| 13 | let currWindowId = Array.from(NiriService.workspaces).find(ws => { | ||
| 14 | return ws.output === screen.name && ws.is_active; | ||
| 15 | })?.active_window_id; | ||
| 16 | |||
| 17 | return currWindowId ? Array.from(NiriService.windows).find(win => win.id == currWindowId) : null; | ||
| 18 | } | ||
| 19 | property var windowEntry: activeWindow ? DesktopEntries.heuristicLookup(activeWindow.app_id) : null | ||
| 20 | |||
| 21 | anchors.verticalCenter: parent.verticalCenter | ||
| 22 | width: activeWindowDisplayContent.width | ||
| 23 | height: parent.height | ||
| 24 | |||
| 25 | WrapperMouseArea { | ||
| 26 | id: widgetMouseArea | ||
| 27 | |||
| 28 | anchors.fill: parent | ||
| 29 | |||
| 30 | hoverEnabled: true | ||
| 31 | |||
| 32 | Item { | ||
| 33 | anchors.fill: parent | ||
| 34 | |||
| 35 | Row { | ||
| 36 | id: activeWindowDisplayContent | ||
| 37 | |||
| 38 | width: childrenRect.width | ||
| 39 | height: parent.height | ||
| 40 | anchors.verticalCenter: parent.verticalCenter | ||
| 41 | spacing: 8 | ||
| 42 | |||
| 43 | IconImage { | ||
| 44 | id: activeWindowIcon | ||
| 45 | |||
| 46 | implicitSize: 14 | ||
| 47 | |||
| 48 | anchors.verticalCenter: parent.verticalCenter | ||
| 49 | |||
| 50 | source: { | ||
| 51 | let icon = activeWindowDisplay.windowEntry?.icon | ||
| 52 | if (typeof icon === 'string' || icon instanceof String) { | ||
| 53 | if (icon.includes("?path=")) { | ||
| 54 | const split = icon.split("?path=") | ||
| 55 | if (split.length !== 2) | ||
| 56 | return icon | ||
| 57 | const name = split[0] | ||
| 58 | const path = split[1] | ||
| 59 | const fileName = name.substring( | ||
| 60 | name.lastIndexOf("/") + 1) | ||
| 61 | return `file://${path}/${fileName}` | ||
| 62 | } else | ||
| 63 | icon = Quickshell.iconPath(icon); | ||
| 64 | return icon | ||
| 65 | } | ||
| 66 | return "" | ||
| 67 | } | ||
| 68 | asynchronous: true | ||
| 69 | smooth: true | ||
| 70 | mipmap: true | ||
| 71 | } | ||
| 72 | |||
| 73 | Text { | ||
| 74 | id: windowTitle | ||
| 75 | |||
| 76 | width: Math.min(implicitWidth, activeWindowDisplay.maxWidth - activeWindowIcon.width - activeWindowDisplayContent.spacing) | ||
| 77 | |||
| 78 | property var appAliases: { "Firefox": "Mozilla Firefox", "mpv Media Player": "mpv", "Thunderbird": "Mozilla Thunderbird", "Thunderbird (LMU)": "Mozilla Thunderbird" } | ||
| 79 | |||
| 80 | elide: Text.ElideRight | ||
| 81 | maximumLineCount: 1 | ||
| 82 | color: "white" | ||
| 83 | anchors.verticalCenter: parent.verticalCenter | ||
| 84 | text: { | ||
| 85 | if (!activeWindowDisplay.activeWindow) | ||
| 86 | return ""; | ||
| 87 | |||
| 88 | var title = activeWindowDisplay.activeWindow.title; | ||
| 89 | var appName = activeWindowDisplay.windowEntry?.name; | ||
| 90 | if (appAliases[appName]) | ||
| 91 | appName = appAliases[appName]; | ||
| 92 | if (appName && title.endsWith(appName)) { | ||
| 93 | const oldTitle = title; | ||
| 94 | title = title.substring(0, title.length - appName.length); | ||
| 95 | title = title.replace(/\s*(—|-)\s*$/, ""); | ||
| 96 | if (!title) | ||
| 97 | title = oldTitle; | ||
| 98 | } | ||
| 99 | return title; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | Loader { | ||
| 107 | id: tooltipLoader | ||
| 108 | |||
| 109 | active: false | ||
| 110 | |||
| 111 | Connections { | ||
| 112 | target: widgetMouseArea | ||
| 113 | function onContainsMouseChanged() { | ||
| 114 | if (widgetMouseArea.containsMouse) | ||
| 115 | tooltipLoader.active = true; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | PopupWindow { | ||
| 120 | id: tooltip | ||
| 121 | |||
| 122 | property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 123 | |||
| 124 | anchor { | ||
| 125 | item: widgetMouseArea | ||
| 126 | edges: Edges.Bottom | Edges.Left | ||
| 127 | } | ||
| 128 | visible: false | ||
| 129 | |||
| 130 | onNextVisibleChanged: hangTimer.restart() | ||
| 131 | |||
| 132 | Timer { | ||
| 133 | id: hangTimer | ||
| 134 | interval: tooltip.visible ? 100 : 500 | ||
| 135 | onTriggered: { | ||
| 136 | tooltip.visible = tooltip.nextVisible; | ||
| 137 | if (!tooltip.visible) | ||
| 138 | tooltipLoader.active = false; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | implicitWidth: widgetTooltipText.contentWidth + 16 | ||
| 143 | implicitHeight: widgetTooltipText.contentHeight + 16 | ||
| 144 | color: "black" | ||
| 145 | |||
| 146 | WrapperMouseArea { | ||
| 147 | id: tooltipMouseArea | ||
| 148 | |||
| 149 | hoverEnabled: true | ||
| 150 | enabled: true | ||
| 151 | |||
| 152 | anchors.fill: parent | ||
| 153 | |||
| 154 | Item { | ||
| 155 | anchors.fill: parent | ||
| 156 | |||
| 157 | Text { | ||
| 158 | id: widgetTooltipText | ||
| 159 | |||
| 160 | anchors.centerIn: parent | ||
| 161 | |||
| 162 | font.pointSize: 10 | ||
| 163 | font.family: "Fira Mono" | ||
| 164 | color: "white" | ||
| 165 | |||
| 166 | text: JSON.stringify(Object.assign({}, activeWindowDisplay.activeWindow), null, 2) | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | } | ||
| 172 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Bar.qml b/accounts/gkleen@sif/shell/quickshell/Bar.qml new file mode 100644 index 00000000..9210066c --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Bar.qml | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | import Quickshell | ||
| 2 | import Quickshell.Wayland | ||
| 3 | import QtQuick | ||
| 4 | |||
| 5 | PanelWindow { | ||
| 6 | id: bar | ||
| 7 | |||
| 8 | required property var modelData | ||
| 9 | screen: modelData | ||
| 10 | |||
| 11 | WlrLayershell.namespace: "bar" | ||
| 12 | |||
| 13 | anchors { | ||
| 14 | top: true | ||
| 15 | left: true | ||
| 16 | right: true | ||
| 17 | } | ||
| 18 | margins { | ||
| 19 | left: 26 + 8 | ||
| 20 | right: 26 + 8 | ||
| 21 | } | ||
| 22 | |||
| 23 | implicitHeight: 21 | ||
| 24 | color: "transparent" | ||
| 25 | |||
| 26 | Rectangle { | ||
| 27 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 28 | anchors.fill: parent | ||
| 29 | // bottomLeftRadius: 8 | ||
| 30 | // bottomRightRadius: 8 | ||
| 31 | } | ||
| 32 | |||
| 33 | Row { | ||
| 34 | id: left | ||
| 35 | |||
| 36 | height: parent.height | ||
| 37 | width: childrenRect.width | ||
| 38 | anchors.left: parent.left | ||
| 39 | anchors.leftMargin: 8 | ||
| 40 | anchors.verticalCenter: parent.verticalCenter | ||
| 41 | spacing: 8 | ||
| 42 | |||
| 43 | WorkspaceSwitcher { | ||
| 44 | screen: bar.screen | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | Row { | ||
| 49 | id: center | ||
| 50 | |||
| 51 | height: parent.height | ||
| 52 | width: childrenRect.width | ||
| 53 | anchors.centerIn: parent | ||
| 54 | spacing: 5 | ||
| 55 | |||
| 56 | ActiveWindowDisplay { | ||
| 57 | screen: bar.screen | ||
| 58 | maxWidth: bar.width - 2*Math.max(left.width, right.width) - 2*8 | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | Row { | ||
| 63 | id: right | ||
| 64 | |||
| 65 | height: parent.height | ||
| 66 | width: childrenRect.width | ||
| 67 | anchors.right: parent.right | ||
| 68 | anchors.rightMargin: 8 | ||
| 69 | anchors.verticalCenter: parent.verticalCenter | ||
| 70 | spacing: 0 | ||
| 71 | |||
| 72 | // WorktimeWidget { command: "time"; } | ||
| 73 | |||
| 74 | // WorktimeWidget { command: "today"; } | ||
| 75 | |||
| 76 | KeyboardLayout {} | ||
| 77 | |||
| 78 | Item { | ||
| 79 | visible: privacy.visible | ||
| 80 | height: parent.height | ||
| 81 | width: 8 - 4 | ||
| 82 | } | ||
| 83 | |||
| 84 | PrivacyWidget { | ||
| 85 | id: privacy | ||
| 86 | } | ||
| 87 | |||
| 88 | Item { | ||
| 89 | visible: privacy.visible | ||
| 90 | height: parent.height | ||
| 91 | width: 8 - 4 | ||
| 92 | } | ||
| 93 | |||
| 94 | SystemTray {} | ||
| 95 | |||
| 96 | PipewireWidget {} | ||
| 97 | |||
| 98 | BrightnessWidget {} | ||
| 99 | |||
| 100 | BatteryWidget {} | ||
| 101 | |||
| 102 | WaylandInhibitorWidget { | ||
| 103 | window: bar | ||
| 104 | } | ||
| 105 | |||
| 106 | NotificationInhibitorWidget {} | ||
| 107 | |||
| 108 | LidSwitchInhibitorWidget {} | ||
| 109 | |||
| 110 | Item { | ||
| 111 | height: parent.height | ||
| 112 | width: 8 - 4 | ||
| 113 | } | ||
| 114 | |||
| 115 | Clock {} | ||
| 116 | } | ||
| 117 | } \ No newline at end of file | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml b/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml new file mode 100644 index 00000000..da17df2a --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/BatteryWidget.qml | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | import QtQuick | ||
| 2 | import Quickshell | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import Quickshell.Services.UPower | ||
| 5 | |||
| 6 | Item { | ||
| 7 | id: root | ||
| 8 | |||
| 9 | height: parent.height | ||
| 10 | width: batteryIcon.width + 8 | ||
| 11 | anchors.verticalCenter: parent.verticalCenter | ||
| 12 | |||
| 13 | property var batteryDevice: Array.from(UPower.devices.values).find(dev => dev.isLaptopBattery) | ||
| 14 | |||
| 15 | WrapperMouseArea { | ||
| 16 | id: widgetMouseArea | ||
| 17 | |||
| 18 | anchors.fill: parent | ||
| 19 | |||
| 20 | hoverEnabled: true | ||
| 21 | |||
| 22 | Item { | ||
| 23 | anchors.fill: parent | ||
| 24 | |||
| 25 | MaterialDesignIcon { | ||
| 26 | id: batteryIcon | ||
| 27 | |||
| 28 | implicitSize: 14 | ||
| 29 | anchors.centerIn: parent | ||
| 30 | |||
| 31 | icon: { | ||
| 32 | if (!root.batteryDevice?.ready) | ||
| 33 | return "battery-unknown"; | ||
| 34 | |||
| 35 | if (root.batteryDevice.state == UPowerDeviceState.FullyCharged) | ||
| 36 | return "power-plug-battery"; | ||
| 37 | |||
| 38 | const perdec = 10 * Math.max(1, Math.ceil(root.batteryDevice.percentage * 10)); | ||
| 39 | if (root.batteryDevice.state == UPowerDeviceState.Charging) | ||
| 40 | return `battery-charging-${perdec}`; | ||
| 41 | if (perdec == 100) | ||
| 42 | return "battery"; | ||
| 43 | return `battery-${perdec}`; | ||
| 44 | } | ||
| 45 | color: { | ||
| 46 | if (!root.batteryDevice?.ready) | ||
| 47 | return "#555"; | ||
| 48 | |||
| 49 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged && root.batteryDevice.state != UPowerDeviceState.Charging && root.batteryDevice.timeToEmpty < 20 * 60) | ||
| 50 | return "#f2201f"; | ||
| 51 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged && root.batteryDevice.state != UPowerDeviceState.Charging && root.batteryDevice.timeToEmpty < 40 * 60) | ||
| 52 | return "#f28a21"; | ||
| 53 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged) | ||
| 54 | return "#fff"; | ||
| 55 | return "#555"; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | PopupWindow { | ||
| 62 | id: tooltip | ||
| 63 | |||
| 64 | property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 65 | |||
| 66 | anchor { | ||
| 67 | item: widgetMouseArea | ||
| 68 | edges: Edges.Bottom | Edges.Left | ||
| 69 | } | ||
| 70 | visible: false | ||
| 71 | |||
| 72 | onNextVisibleChanged: hangTimer.restart() | ||
| 73 | |||
| 74 | Timer { | ||
| 75 | id: hangTimer | ||
| 76 | interval: 100 | ||
| 77 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 78 | } | ||
| 79 | |||
| 80 | implicitWidth: widgetTooltipText.contentWidth + 16 | ||
| 81 | implicitHeight: widgetTooltipText.contentHeight + 16 | ||
| 82 | color: "black" | ||
| 83 | |||
| 84 | WrapperMouseArea { | ||
| 85 | id: tooltipMouseArea | ||
| 86 | |||
| 87 | hoverEnabled: true | ||
| 88 | enabled: true | ||
| 89 | |||
| 90 | anchors.fill: parent | ||
| 91 | |||
| 92 | Item { | ||
| 93 | anchors.fill: parent | ||
| 94 | |||
| 95 | Text { | ||
| 96 | id: widgetTooltipText | ||
| 97 | |||
| 98 | anchors.centerIn: parent | ||
| 99 | |||
| 100 | font.pointSize: 10 | ||
| 101 | font.family: "Fira Sans" | ||
| 102 | color: "white" | ||
| 103 | |||
| 104 | text: { | ||
| 105 | const stateStr = UPowerDeviceState.toString(root.batteryDevice.state); | ||
| 106 | var outStr = stateStr; | ||
| 107 | if (root.batteryDevice.state != UPowerDeviceState.FullyCharged) | ||
| 108 | outStr += ` ${Math.round(root.batteryDevice.percentage * 100)}%`; | ||
| 109 | |||
| 110 | function formatTime(t) { | ||
| 111 | var res = ""; | ||
| 112 | for (const unit of [{ "s": "h", "v": 3600 }, { "s": "m", "v": 60 }, { "s": "s", "v": 1 }]) { | ||
| 113 | if (t < unit.v) | ||
| 114 | continue; | ||
| 115 | res += Math.floor(t / unit.v) + unit.s; | ||
| 116 | t %= unit.v; | ||
| 117 | } | ||
| 118 | return res; | ||
| 119 | } | ||
| 120 | if (root.batteryDevice.timeToEmpty != 0) { | ||
| 121 | const tStr = formatTime(Math.floor(root.batteryDevice.timeToEmpty / 60) * 60); | ||
| 122 | if (tStr) | ||
| 123 | outStr += " " + tStr; | ||
| 124 | } else if (root.batteryDevice.timeToFull != 0) { | ||
| 125 | const tStr = formatTime(Math.ceil(root.batteryDevice.timeToFull / 60) * 60); | ||
| 126 | if (tStr) | ||
| 127 | outStr += " " + tStr; | ||
| 128 | } | ||
| 129 | |||
| 130 | return outStr; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | } | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml b/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml new file mode 100644 index 00000000..a432179e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/BrightnessOSD.qml | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Layouts | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Widgets | ||
| 5 | import qs.Services | ||
| 6 | |||
| 7 | Scope { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | property bool show: false | ||
| 11 | property bool inhibited: true | ||
| 12 | |||
| 13 | Connections { | ||
| 14 | target: Brightness | ||
| 15 | |||
| 16 | function onCurrBrightnessChanged() { | ||
| 17 | root.show = true; | ||
| 18 | hideTimer.restart(); | ||
| 19 | } | ||
| 20 | } | ||
| 21 | |||
| 22 | onShowChanged: { | ||
| 23 | if (show) | ||
| 24 | hideTimer.restart(); | ||
| 25 | } | ||
| 26 | |||
| 27 | Timer { | ||
| 28 | id: hideTimer | ||
| 29 | interval: 1000 | ||
| 30 | onTriggered: root.show = false | ||
| 31 | } | ||
| 32 | |||
| 33 | Timer { | ||
| 34 | id: startInhibit | ||
| 35 | interval: 100 | ||
| 36 | running: true | ||
| 37 | onTriggered: { | ||
| 38 | root.show = false; | ||
| 39 | root.inhibited = false; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | LazyLoader { | ||
| 44 | active: root.show && !root.inhibited | ||
| 45 | |||
| 46 | Variants { | ||
| 47 | model: Quickshell.screens | ||
| 48 | |||
| 49 | delegate: Scope { | ||
| 50 | id: screenScope | ||
| 51 | |||
| 52 | required property var modelData | ||
| 53 | |||
| 54 | PanelWindow { | ||
| 55 | id: window | ||
| 56 | |||
| 57 | screen: screenScope.modelData | ||
| 58 | |||
| 59 | anchors.top: true | ||
| 60 | margins.top: screen.height / 2 - 50 + 3.5 | ||
| 61 | exclusiveZone: 0 | ||
| 62 | exclusionMode: ExclusionMode.Ignore | ||
| 63 | |||
| 64 | implicitWidth: 400 | ||
| 65 | implicitHeight: 50 | ||
| 66 | |||
| 67 | mask: Region {} | ||
| 68 | |||
| 69 | color: "transparent" | ||
| 70 | |||
| 71 | Rectangle { | ||
| 72 | anchors.fill: parent | ||
| 73 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 74 | } | ||
| 75 | |||
| 76 | RowLayout { | ||
| 77 | id: layout | ||
| 78 | |||
| 79 | anchors.centerIn: parent | ||
| 80 | |||
| 81 | height: 50 - 8*2 | ||
| 82 | width: 400 - 8*2 | ||
| 83 | |||
| 84 | MaterialDesignIcon { | ||
| 85 | id: volumeIcon | ||
| 86 | |||
| 87 | implicitWidth: parent.height | ||
| 88 | implicitHeight: parent.height | ||
| 89 | |||
| 90 | icon: `brightness-${Math.min(7, Math.floor(Brightness.currBrightness * 7) + 1)}` | ||
| 91 | } | ||
| 92 | |||
| 93 | Rectangle { | ||
| 94 | Layout.fillWidth: true | ||
| 95 | |||
| 96 | implicitHeight: 10 | ||
| 97 | |||
| 98 | color: "#50ffffff" | ||
| 99 | |||
| 100 | Rectangle { | ||
| 101 | anchors { | ||
| 102 | left: parent.left | ||
| 103 | top: parent.top | ||
| 104 | bottom: parent.bottom | ||
| 105 | } | ||
| 106 | |||
| 107 | color: "white" | ||
| 108 | |||
| 109 | implicitWidth: parent.width * Brightness.currBrightness | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml b/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml new file mode 100644 index 00000000..3bb5a80e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/BrightnessWidget.qml | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | import QtQuick | ||
| 2 | import Quickshell | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import qs.Services | ||
| 5 | |||
| 6 | Item { | ||
| 7 | height: parent.height | ||
| 8 | width: brightnessIcon.width + 8 | ||
| 9 | anchors.verticalCenter: parent.verticalCenter | ||
| 10 | |||
| 11 | WrapperMouseArea { | ||
| 12 | id: widgetMouseArea | ||
| 13 | |||
| 14 | anchors.fill: parent | ||
| 15 | |||
| 16 | hoverEnabled: true | ||
| 17 | |||
| 18 | property real sensitivity: (1 / 50) / 120 | ||
| 19 | onWheel: event => Brightness.currBrightness += event.angleDelta.y * sensitivity | ||
| 20 | |||
| 21 | Item { | ||
| 22 | anchors.fill: parent | ||
| 23 | |||
| 24 | MaterialDesignIcon { | ||
| 25 | id: brightnessIcon | ||
| 26 | |||
| 27 | implicitSize: 14 | ||
| 28 | anchors.centerIn: parent | ||
| 29 | |||
| 30 | icon: `brightness-${Math.min(7, Math.floor(Brightness.currBrightness * 7) + 1)}` | ||
| 31 | color: "#555" | ||
| 32 | } | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | PopupWindow { | ||
| 37 | id: tooltip | ||
| 38 | |||
| 39 | property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 40 | |||
| 41 | anchor { | ||
| 42 | item: widgetMouseArea | ||
| 43 | edges: Edges.Bottom | Edges.Left | ||
| 44 | } | ||
| 45 | visible: false | ||
| 46 | |||
| 47 | onNextVisibleChanged: hangTimer.restart() | ||
| 48 | |||
| 49 | Timer { | ||
| 50 | id: hangTimer | ||
| 51 | interval: 100 | ||
| 52 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 53 | } | ||
| 54 | |||
| 55 | implicitWidth: widgetTooltipText.contentWidth + 16 | ||
| 56 | implicitHeight: widgetTooltipText.contentHeight + 16 | ||
| 57 | color: "black" | ||
| 58 | |||
| 59 | WrapperMouseArea { | ||
| 60 | id: tooltipMouseArea | ||
| 61 | |||
| 62 | hoverEnabled: true | ||
| 63 | enabled: true | ||
| 64 | |||
| 65 | anchors.fill: parent | ||
| 66 | |||
| 67 | Item { | ||
| 68 | anchors.fill: parent | ||
| 69 | |||
| 70 | Text { | ||
| 71 | id: widgetTooltipText | ||
| 72 | |||
| 73 | anchors.centerIn: parent | ||
| 74 | |||
| 75 | font.pointSize: 10 | ||
| 76 | font.family: "Fira Sans" | ||
| 77 | color: "white" | ||
| 78 | |||
| 79 | text: `${Math.round(Brightness.currBrightness * 100)}%` | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Clock.qml b/accounts/gkleen@sif/shell/quickshell/Clock.qml new file mode 100644 index 00000000..b7004528 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Clock.qml | |||
| @@ -0,0 +1,295 @@ | |||
| 1 | import QtQml | ||
| 2 | import QtQuick | ||
| 3 | import Quickshell | ||
| 4 | import Custom as Custom | ||
| 5 | import QtQuick.Controls | ||
| 6 | import QtQuick.Layouts | ||
| 7 | import Quickshell.Widgets | ||
| 8 | |||
| 9 | Item { | ||
| 10 | id: clockItem | ||
| 11 | |||
| 12 | property bool calendarPopup: true | ||
| 13 | |||
| 14 | width: clock.contentWidth | ||
| 15 | height: parent.height | ||
| 16 | anchors.verticalCenter: parent.verticalCenter | ||
| 17 | |||
| 18 | WrapperMouseArea { | ||
| 19 | id: clockMouseArea | ||
| 20 | |||
| 21 | anchors.fill: parent | ||
| 22 | hoverEnabled: true | ||
| 23 | enabled: clockItem.calendarPopup | ||
| 24 | |||
| 25 | Item { | ||
| 26 | anchors.fill: parent | ||
| 27 | |||
| 28 | Text { | ||
| 29 | id: clock | ||
| 30 | color: "white" | ||
| 31 | |||
| 32 | anchors.verticalCenter: parent.verticalCenter | ||
| 33 | |||
| 34 | Custom.Chrono { | ||
| 35 | id: chrono | ||
| 36 | |||
| 37 | onDateChanged: clock.text = format("W{0:%V-%u} {0:%F} {0:%H:%M:%S%Ez}") | ||
| 38 | } | ||
| 39 | |||
| 40 | font.pointSize: 10 | ||
| 41 | font.family: "Fira Sans" | ||
| 42 | font.features: { "tnum": 1 } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | Loader { | ||
| 48 | id: tooltipLoader | ||
| 49 | |||
| 50 | active: false | ||
| 51 | |||
| 52 | Connections { | ||
| 53 | target: clockMouseArea | ||
| 54 | function onContainsMouseChanged() { | ||
| 55 | if (clockMouseArea.containsMouse) | ||
| 56 | tooltipLoader.active = true; | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | sourceComponent: PopupWindow { | ||
| 61 | id: tooltip | ||
| 62 | |||
| 63 | property bool nextVisible: clockMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 64 | |||
| 65 | anchor { | ||
| 66 | item: clockMouseArea | ||
| 67 | edges: Edges.Bottom | Edges.Left | ||
| 68 | } | ||
| 69 | visible: false | ||
| 70 | |||
| 71 | onNextVisibleChanged: hangTimer.restart() | ||
| 72 | |||
| 73 | Timer { | ||
| 74 | id: hangTimer | ||
| 75 | interval: 100 | ||
| 76 | onTriggered: { | ||
| 77 | tooltip.visible = tooltip.nextVisible; | ||
| 78 | if (!tooltip.visible) | ||
| 79 | tooltipLoader.active = false; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | implicitWidth: tooltipLayout.childrenRect.width + 16 | ||
| 84 | implicitHeight: tooltipLayout.childrenRect.height + 16 | ||
| 85 | color: "black" | ||
| 86 | |||
| 87 | onVisibleChanged: { | ||
| 88 | yearCalendar.year = chrono.date.getFullYear(); | ||
| 89 | yearCalendar.angleRem = 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | WrapperMouseArea { | ||
| 93 | id: tooltipMouseArea | ||
| 94 | |||
| 95 | hoverEnabled: true | ||
| 96 | enabled: true | ||
| 97 | |||
| 98 | onWheel: event => yearCalendar.scrollYear(event) | ||
| 99 | |||
| 100 | anchors.fill: parent | ||
| 101 | |||
| 102 | Item { | ||
| 103 | id: clockTooltipContent | ||
| 104 | |||
| 105 | anchors.fill: parent | ||
| 106 | |||
| 107 | ColumnLayout { | ||
| 108 | id: tooltipLayout | ||
| 109 | |||
| 110 | anchors { | ||
| 111 | left: parent.left | ||
| 112 | top: parent.top | ||
| 113 | leftMargin: 8 | ||
| 114 | topMargin: 8 | ||
| 115 | } | ||
| 116 | |||
| 117 | Text { | ||
| 118 | id: yearLabel | ||
| 119 | |||
| 120 | horizontalAlignment: Text.AlignHCenter | ||
| 121 | |||
| 122 | font.pointSize: 14 | ||
| 123 | font.family: "Fira Sans" | ||
| 124 | font.features: { "tnum": 1 } | ||
| 125 | color: "white" | ||
| 126 | |||
| 127 | text: yearCalendar.year | ||
| 128 | |||
| 129 | Layout.fillWidth: true | ||
| 130 | Layout.bottomMargin: 8 | ||
| 131 | } | ||
| 132 | |||
| 133 | GridLayout { | ||
| 134 | property int year: chrono.date.getFullYear() | ||
| 135 | |||
| 136 | id: yearCalendar | ||
| 137 | |||
| 138 | columns: 3 | ||
| 139 | columnSpacing: 16 | ||
| 140 | rowSpacing: 16 | ||
| 141 | |||
| 142 | Layout.alignment: Qt.AlignHCenter | ||
| 143 | Layout.fillWidth: false | ||
| 144 | |||
| 145 | property real angleRem: 0 | ||
| 146 | property real sensitivity: 1 / 120 | ||
| 147 | |||
| 148 | function scrollYear(event) { | ||
| 149 | angleRem += event.angleDelta.y; | ||
| 150 | const d = Math.round(angleRem * sensitivity); | ||
| 151 | yearCalendar.year += d; | ||
| 152 | angleRem -= d / sensitivity; | ||
| 153 | } | ||
| 154 | |||
| 155 | Connections { | ||
| 156 | target: clockMouseArea | ||
| 157 | function onWheel(event) { yearCalendar.scrollYear(event); } | ||
| 158 | } | ||
| 159 | |||
| 160 | Repeater { | ||
| 161 | model: 12 | ||
| 162 | |||
| 163 | GridLayout { | ||
| 164 | columns: 2 | ||
| 165 | |||
| 166 | required property int index | ||
| 167 | property int month: index | ||
| 168 | |||
| 169 | id: monthCalendar | ||
| 170 | |||
| 171 | Layout.alignment: Qt.AlignTop | Qt.AlignRight | ||
| 172 | Layout.fillWidth: false | ||
| 173 | |||
| 174 | Text { | ||
| 175 | Layout.column: 1 | ||
| 176 | Layout.fillWidth: true | ||
| 177 | |||
| 178 | horizontalAlignment: Text.AlignHCenter | ||
| 179 | |||
| 180 | font.pointSize: 10 | ||
| 181 | font.family: "Fira Sans" | ||
| 182 | |||
| 183 | text: new Date(yearCalendar.year, monthCalendar.month, 1).toLocaleString(Qt.locale("en_DK"), "MMMM") | ||
| 184 | |||
| 185 | color: "#ffead3" | ||
| 186 | } | ||
| 187 | |||
| 188 | DayOfWeekRow { | ||
| 189 | locale: grid.locale | ||
| 190 | |||
| 191 | Layout.row: 1 | ||
| 192 | Layout.column: 1 | ||
| 193 | Layout.fillWidth: true | ||
| 194 | |||
| 195 | delegate: WrapperItem { | ||
| 196 | required property string shortName | ||
| 197 | |||
| 198 | width: dowLabel.contentWidth + 6 | ||
| 199 | |||
| 200 | Text { | ||
| 201 | id: dowLabel | ||
| 202 | |||
| 203 | anchors.fill: parent | ||
| 204 | |||
| 205 | font.pointSize: 10 | ||
| 206 | font.family: "Fira Sans" | ||
| 207 | |||
| 208 | text: parent.shortName | ||
| 209 | color: "#ffcc66" | ||
| 210 | |||
| 211 | horizontalAlignment: Text.AlignHCenter | ||
| 212 | verticalAlignment: Text.AlignVCenter | ||
| 213 | } | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | WeekNumberColumn { | ||
| 218 | month: grid.month | ||
| 219 | year: grid.year | ||
| 220 | locale: grid.locale | ||
| 221 | |||
| 222 | Layout.fillHeight: true | ||
| 223 | |||
| 224 | delegate: Text { | ||
| 225 | required property int weekNumber | ||
| 226 | |||
| 227 | opacity: { | ||
| 228 | const simple = new Date(weekNumber == 1 && monthCalendar.month == 12 ? yearCalendar.year + 1 : yearCalendar.year, 0, 1 + (weekNumber - 1) * 7); | ||
| 229 | const dayOfWeek = simple.getDay(); | ||
| 230 | const isoWeekStart = simple; | ||
| 231 | |||
| 232 | isoWeekStart.setDate(simple.getDate() - dayOfWeek + 1); | ||
| 233 | if (dayOfWeek > 4) { | ||
| 234 | isoWeekStart.setDate(isoWeekStart.getDate() + 7); | ||
| 235 | } | ||
| 236 | |||
| 237 | for (let i = 0; i < 7; i++) { | ||
| 238 | const dayInWeek = new Date(isoWeekStart); | ||
| 239 | dayInWeek.setDate(dayInWeek.getDate() + i); | ||
| 240 | if (dayInWeek.getMonth() == monthCalendar.month) | ||
| 241 | return 1; | ||
| 242 | } | ||
| 243 | |||
| 244 | return 0; | ||
| 245 | } | ||
| 246 | |||
| 247 | font.pointSize: 10 | ||
| 248 | font.family: "Fira Sans" | ||
| 249 | font.features: { "tnum": 1 } | ||
| 250 | |||
| 251 | text: weekNumber | ||
| 252 | color: "#99ffdd" | ||
| 253 | |||
| 254 | horizontalAlignment: Text.AlignRight | ||
| 255 | verticalAlignment: Text.AlignVCenter | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | MonthGrid { | ||
| 260 | id: grid | ||
| 261 | |||
| 262 | year: yearCalendar.year | ||
| 263 | month: monthCalendar.month | ||
| 264 | locale: Qt.locale("en_DK") | ||
| 265 | |||
| 266 | Layout.fillWidth: true | ||
| 267 | Layout.fillHeight: true | ||
| 268 | |||
| 269 | delegate: Text { | ||
| 270 | required property var model | ||
| 271 | |||
| 272 | opacity: model.month === monthCalendar.month ? 1 : 0 | ||
| 273 | |||
| 274 | font.pointSize: 10 | ||
| 275 | font.family: "Fira Sans" | ||
| 276 | font.features: { "tnum": 1 } | ||
| 277 | |||
| 278 | property bool today: chrono.date.getFullYear() == model.year && chrono.date.getMonth() == model.month && chrono.date.getDate() == model.day | ||
| 279 | |||
| 280 | text: model.day | ||
| 281 | color: today ? "#ff6699" : "white" | ||
| 282 | |||
| 283 | horizontalAlignment: Text.AlignRight | ||
| 284 | verticalAlignment: Text.AlignVCenter | ||
| 285 | } | ||
| 286 | } | ||
| 287 | } | ||
| 288 | } | ||
| 289 | } | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml b/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml new file mode 100644 index 00000000..46302e54 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/KeyboardLayout.qml | |||
| @@ -0,0 +1,112 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import qs.Services | ||
| 4 | import Quickshell.Widgets | ||
| 5 | |||
| 6 | Item { | ||
| 7 | width: kbdLabel.contentWidth + 8 | ||
| 8 | height: parent.height | ||
| 9 | anchors.verticalCenter: parent.verticalCenter | ||
| 10 | |||
| 11 | WrapperMouseArea { | ||
| 12 | id: kbdMouseArea | ||
| 13 | |||
| 14 | anchors.fill: parent | ||
| 15 | |||
| 16 | hoverEnabled: true | ||
| 17 | cursorShape: Qt.PointingHandCursor | ||
| 18 | enabled: true | ||
| 19 | onClicked: { | ||
| 20 | NiriService.sendCommand({ "Action": { "SwitchLayout": { "layout": "Next" } } }, _ => {}) | ||
| 21 | } | ||
| 22 | onWheel: event => { | ||
| 23 | NiriService.sendCommand({ "Action": { "SwitchLayout": { "layout": event.angleDelta > 0 ? "Next" : "Prev" } } }, _ => {}) | ||
| 24 | } | ||
| 25 | |||
| 26 | Rectangle { | ||
| 27 | id: kbdWidget | ||
| 28 | |||
| 29 | property var keyboardAbbrev: { "English (programmer Dvorak)": "dvp", "English (US)": "us" } | ||
| 30 | |||
| 31 | anchors.fill: parent | ||
| 32 | color: { | ||
| 33 | if (kbdMouseArea.containsMouse) { | ||
| 34 | return "#33808080"; | ||
| 35 | } | ||
| 36 | return "transparent"; | ||
| 37 | } | ||
| 38 | |||
| 39 | Text { | ||
| 40 | id: kbdLabel | ||
| 41 | |||
| 42 | font.pointSize: 10 | ||
| 43 | font.family: "Fira Sans" | ||
| 44 | color: { | ||
| 45 | if (NiriService.keyboardLayouts?.current_idx === 0) | ||
| 46 | return "#555"; | ||
| 47 | return "white"; | ||
| 48 | } | ||
| 49 | anchors.centerIn: parent | ||
| 50 | |||
| 51 | text: { | ||
| 52 | const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx]; | ||
| 53 | if (!currentLayout) | ||
| 54 | return ""; | ||
| 55 | return kbdWidget.keyboardAbbrev[currentLayout] ? kbdWidget.keyboardAbbrev[currentLayout] : currentLayout; | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | PopupWindow { | ||
| 62 | id: tooltip | ||
| 63 | |||
| 64 | property bool nextVisible: kbdMouseArea.containsMouse || tooltipMouseArea.containsMouse | ||
| 65 | |||
| 66 | anchor { | ||
| 67 | item: kbdMouseArea | ||
| 68 | edges: Edges.Bottom | Edges.Left | ||
| 69 | } | ||
| 70 | visible: false | ||
| 71 | |||
| 72 | onNextVisibleChanged: hangTimer.restart() | ||
| 73 | |||
| 74 | Timer { | ||
| 75 | id: hangTimer | ||
| 76 | interval: 100 | ||
| 77 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 78 | } | ||
| 79 | |||
| 80 | implicitWidth: kbdTooltipText.contentWidth + 16 | ||
| 81 | implicitHeight: kbdTooltipText.contentHeight + 16 | ||
| 82 | color: "black" | ||
| 83 | |||
| 84 | WrapperMouseArea { | ||
| 85 | id: tooltipMouseArea | ||
| 86 | |||
| 87 | hoverEnabled: true | ||
| 88 | enabled: true | ||
| 89 | |||
| 90 | anchors.fill: parent | ||
| 91 | |||
| 92 | Item { | ||
| 93 | anchors.fill: parent | ||
| 94 | |||
| 95 | Text { | ||
| 96 | id: kbdTooltipText | ||
| 97 | |||
| 98 | anchors.centerIn: parent | ||
| 99 | |||
| 100 | font.pointSize: 10 | ||
| 101 | font.family: "Fira Sans" | ||
| 102 | color: "white" | ||
| 103 | |||
| 104 | text: { | ||
| 105 | const currentLayout = NiriService.keyboardLayouts?.names?.[NiriService.keyboardLayouts.current_idx]; | ||
| 106 | return currentLayout || ""; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml new file mode 100644 index 00000000..8410dcda --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/LidSwitchInhibitorWidget.qml | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import qs.Services | ||
| 5 | |||
| 6 | Item { | ||
| 7 | id: root | ||
| 8 | |||
| 9 | width: icon.width + 8 | ||
| 10 | height: parent.height | ||
| 11 | anchors.verticalCenter: parent.verticalCenter | ||
| 12 | |||
| 13 | WrapperMouseArea { | ||
| 14 | id: widgetMouseArea | ||
| 15 | |||
| 16 | anchors.fill: parent | ||
| 17 | |||
| 18 | hoverEnabled: true | ||
| 19 | cursorShape: Qt.PointingHandCursor | ||
| 20 | |||
| 21 | onClicked: InhibitorState.lidSwitchInhibited = !InhibitorState.lidSwitchInhibited | ||
| 22 | |||
| 23 | Rectangle { | ||
| 24 | anchors.fill: parent | ||
| 25 | color: { | ||
| 26 | if (widgetMouseArea.containsMouse) { | ||
| 27 | return "#33808080"; | ||
| 28 | } | ||
| 29 | return "transparent"; | ||
| 30 | } | ||
| 31 | |||
| 32 | Item { | ||
| 33 | anchors.fill: parent | ||
| 34 | |||
| 35 | MaterialDesignIcon { | ||
| 36 | id: icon | ||
| 37 | |||
| 38 | implicitSize: 14 | ||
| 39 | anchors.centerIn: parent | ||
| 40 | |||
| 41 | icon: InhibitorState.lidSwitchInhibited ? "laptop-off" : "laptop" | ||
| 42 | color: InhibitorState.lidSwitchInhibited ? "#f28a21" : "#555" | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/LockSurface.qml b/accounts/gkleen@sif/shell/quickshell/LockSurface.qml new file mode 100644 index 00000000..f4f8f0cd --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/LockSurface.qml | |||
| @@ -0,0 +1,227 @@ | |||
| 1 | import Quickshell.Widgets | ||
| 2 | import QtQuick.Effects | ||
| 3 | import QtQuick.Layouts | ||
| 4 | import QtQuick | ||
| 5 | import QtQuick.Controls | ||
| 6 | import QtQuick.Controls.Fusion | ||
| 7 | import qs.Services | ||
| 8 | import QtQml | ||
| 9 | |||
| 10 | Item { | ||
| 11 | id: lockSurface | ||
| 12 | |||
| 13 | property var screen | ||
| 14 | property list<var> messages: [] | ||
| 15 | property bool responseRequired: false | ||
| 16 | property bool responseVisible: false | ||
| 17 | property string currentText: "" | ||
| 18 | property bool authRunning: false | ||
| 19 | |||
| 20 | signal response(string responseText) | ||
| 21 | |||
| 22 | anchors.fill: parent | ||
| 23 | |||
| 24 | Item { | ||
| 25 | id: background | ||
| 26 | |||
| 27 | anchors.fill: parent | ||
| 28 | |||
| 29 | property Img current: one | ||
| 30 | property string source: selector.selected | ||
| 31 | |||
| 32 | WallpaperSelector { | ||
| 33 | id: selector | ||
| 34 | seed: lockSurface.screen?.name || "" | ||
| 35 | } | ||
| 36 | |||
| 37 | onSourceChanged: { | ||
| 38 | if (!source) | ||
| 39 | current = null; | ||
| 40 | else if (current === one) | ||
| 41 | two.update() | ||
| 42 | else | ||
| 43 | one.update() | ||
| 44 | } | ||
| 45 | |||
| 46 | Img { id: one } | ||
| 47 | Img { id: two } | ||
| 48 | |||
| 49 | component Img: Item { | ||
| 50 | id: img | ||
| 51 | |||
| 52 | property string source | ||
| 53 | |||
| 54 | function update() { | ||
| 55 | source = background.source || "" | ||
| 56 | } | ||
| 57 | |||
| 58 | anchors.fill: parent | ||
| 59 | |||
| 60 | Image { | ||
| 61 | id: imageSource | ||
| 62 | |||
| 63 | source: img.source | ||
| 64 | sourceSize: Qt.size(parent.width, parent.height) | ||
| 65 | fillMode: Image.PreserveAspectCrop | ||
| 66 | smooth: true | ||
| 67 | visible: false | ||
| 68 | asynchronous: true | ||
| 69 | cache: false | ||
| 70 | |||
| 71 | onStatusChanged: { | ||
| 72 | if (status === Image.Ready) { | ||
| 73 | background.current = img | ||
| 74 | } | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | MultiEffect { | ||
| 79 | id: imageEffect | ||
| 80 | |||
| 81 | source: imageSource | ||
| 82 | anchors.fill: parent | ||
| 83 | blurEnabled: true | ||
| 84 | blur: 1 | ||
| 85 | blurMax: 64 | ||
| 86 | blurMultiplier: 2 | ||
| 87 | |||
| 88 | opacity: 0 | ||
| 89 | |||
| 90 | states: State { | ||
| 91 | name: "visible" | ||
| 92 | when: background.current === img | ||
| 93 | |||
| 94 | PropertyChanges { | ||
| 95 | imageEffect.opacity: 1 | ||
| 96 | } | ||
| 97 | StateChangeScript { | ||
| 98 | name: "unloadOther" | ||
| 99 | script: { | ||
| 100 | if (img === one) | ||
| 101 | two.source = "" | ||
| 102 | if (img === two) | ||
| 103 | one.source = "" | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | transitions: Transition { | ||
| 109 | SequentialAnimation { | ||
| 110 | NumberAnimation { | ||
| 111 | target: imageEffect | ||
| 112 | properties: "opacity" | ||
| 113 | duration: { | ||
| 114 | if (img === one && two.source == "" || img === two && one.source == "") | ||
| 115 | return 0; | ||
| 116 | return 5000; | ||
| 117 | } | ||
| 118 | easing.type: Easing.OutCubic | ||
| 119 | } | ||
| 120 | ScriptAction { | ||
| 121 | scriptName: "unloadOther" | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | Item { | ||
| 130 | anchors { | ||
| 131 | top: lockSurface.top | ||
| 132 | left: lockSurface.left | ||
| 133 | right: lockSurface.right | ||
| 134 | } | ||
| 135 | |||
| 136 | implicitWidth: lockSurface.width | ||
| 137 | implicitHeight: 21 | ||
| 138 | |||
| 139 | Rectangle { | ||
| 140 | anchors.fill: parent | ||
| 141 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 142 | } | ||
| 143 | |||
| 144 | Clock { | ||
| 145 | anchors.centerIn: parent | ||
| 146 | calendarPopup: false | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | WrapperRectangle { | ||
| 151 | id: unlockUi | ||
| 152 | |||
| 153 | Keys.onPressed: event => { | ||
| 154 | if (!lockSurface.authRunning) { | ||
| 155 | event.accepted = true; | ||
| 156 | lockSurface.authRunning = true; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | focus: !passwordBox.visible | ||
| 160 | |||
| 161 | visible: lockSurface.authRunning | ||
| 162 | |||
| 163 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 164 | margin: 8 | ||
| 165 | |||
| 166 | anchors.centerIn: parent | ||
| 167 | |||
| 168 | ColumnLayout { | ||
| 169 | spacing: 4 | ||
| 170 | |||
| 171 | BusyIndicator { | ||
| 172 | visible: running | ||
| 173 | running: !Array.from(lockSurface.messages).length && !lockSurface.responseRequired | ||
| 174 | } | ||
| 175 | |||
| 176 | Repeater { | ||
| 177 | model: lockSurface.messages | ||
| 178 | |||
| 179 | Text { | ||
| 180 | required property var modelData | ||
| 181 | |||
| 182 | font.pointSize: 10 | ||
| 183 | font.family: "Fira Sans" | ||
| 184 | color: modelData.error ? "#f28a21" : "#ffffff" | ||
| 185 | |||
| 186 | text: String(modelData.text).trim() | ||
| 187 | |||
| 188 | Layout.fillWidth: true | ||
| 189 | horizontalAlignment: Text.AlignHCenter | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | TextField { | ||
| 194 | id: passwordBox | ||
| 195 | |||
| 196 | visible: lockSurface.responseRequired | ||
| 197 | echoMode: lockSurface.responseVisible ? TextInput.Normal : TextInput.Password | ||
| 198 | inputMethodHints: Qt.ImhSensitiveData | ||
| 199 | |||
| 200 | onTextChanged: lockSurface.currentText = passwordBox.text | ||
| 201 | onAccepted: { | ||
| 202 | passwordBox.readOnly = true; | ||
| 203 | lockSurface.response(lockSurface.currentText); | ||
| 204 | } | ||
| 205 | |||
| 206 | Connections { | ||
| 207 | target: lockSurface | ||
| 208 | function onCurrentTextChanged() { | ||
| 209 | passwordBox.text = lockSurface.currentText | ||
| 210 | } | ||
| 211 | } | ||
| 212 | Connections { | ||
| 213 | target: lockSurface | ||
| 214 | function onResponseRequiredChanged() { | ||
| 215 | if (lockSurface.responseRequired) | ||
| 216 | passwordBox.readOnly = false; | ||
| 217 | passwordBox.focus = true; | ||
| 218 | passwordBox.selectAll(); | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 222 | Layout.topMargin: 4 | ||
| 223 | Layout.fillWidth: true | ||
| 224 | } | ||
| 225 | } | ||
| 226 | } | ||
| 227 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml b/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml new file mode 100644 index 00000000..996fd41b --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Lockscreen.qml | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | import Quickshell | ||
| 2 | import Quickshell.Wayland | ||
| 3 | import Quickshell.Io | ||
| 4 | import Quickshell.Services.Pam | ||
| 5 | import Quickshell.Services.Mpris | ||
| 6 | import Custom as Custom | ||
| 7 | import qs.Services | ||
| 8 | import QtQml | ||
| 9 | |||
| 10 | Scope { | ||
| 11 | id: lockscreen | ||
| 12 | |||
| 13 | property string currentText: "" | ||
| 14 | |||
| 15 | PamContext { | ||
| 16 | id: pam | ||
| 17 | |||
| 18 | property list<var> messages: [] | ||
| 19 | |||
| 20 | config: "quickshell" | ||
| 21 | onCompleted: result => { | ||
| 22 | if (result === PamResult.Success) { | ||
| 23 | lock.locked = false; | ||
| 24 | } | ||
| 25 | } | ||
| 26 | onPamMessage: { | ||
| 27 | messages = Array.from(messages).concat([{ "text": pam.message, "error": pam.messageIsError }]) | ||
| 28 | } | ||
| 29 | onActiveChanged: { | ||
| 30 | messages = []; | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | IpcHandler { | ||
| 35 | target: "Lockscreen" | ||
| 36 | |||
| 37 | function setLocked(locked: bool): void { lock.locked = locked; } | ||
| 38 | function getLocked(): bool { return lock.locked; } | ||
| 39 | } | ||
| 40 | |||
| 41 | Connections { | ||
| 42 | target: Custom.Systemd | ||
| 43 | function onSleep(before: bool) { | ||
| 44 | console.log(`received prepare for sleep ${before}`); | ||
| 45 | if (before) | ||
| 46 | lock.locked = true; | ||
| 47 | } | ||
| 48 | function onLock() { lock.locked = true; } | ||
| 49 | function onUnlock() { lock.locked = false; } | ||
| 50 | } | ||
| 51 | |||
| 52 | IdleMonitor { | ||
| 53 | id: idleMonitor | ||
| 54 | enabled: !lock.secure | ||
| 55 | timeout: 600 | ||
| 56 | respectInhibitors: true | ||
| 57 | |||
| 58 | onIsIdleChanged: { | ||
| 59 | if (idleMonitor.isIdle) | ||
| 60 | lock.locked = true; | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | Custom.SystemdInhibitor { | ||
| 65 | enabled: !lock.secure | ||
| 66 | |||
| 67 | what: Custom.SystemdInhibitorParams.Sleep | ||
| 68 | who: "quickshell" | ||
| 69 | why: "Lock session" | ||
| 70 | mode: Custom.SystemdInhibitorParams.Delay | ||
| 71 | } | ||
| 72 | |||
| 73 | Binding { | ||
| 74 | target: NotificationManager | ||
| 75 | property: "lockscreenActive" | ||
| 76 | value: lock.locked | ||
| 77 | } | ||
| 78 | |||
| 79 | WlSessionLock { | ||
| 80 | id: lock | ||
| 81 | |||
| 82 | onLockStateChanged: { | ||
| 83 | if (!locked && pam.active) | ||
| 84 | pam.abort(); | ||
| 85 | |||
| 86 | if (locked) { | ||
| 87 | NiriService.sendCommand({ "Action": { "PowerOffMonitors": {} } }, _ => {}); | ||
| 88 | Custom.KeePassXC.lockAllDatabases(); | ||
| 89 | Array.from(MprisProxy.players).forEach(player => { | ||
| 90 | if (player.canPause && player.isPlaying) | ||
| 91 | player.pause(); | ||
| 92 | }); | ||
| 93 | // Custom.Systemd.stopUserUnit("gpg-agent.service", "replace"); | ||
| 94 | GpgAgent.reloadAgent(); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | Component.onCompleted: { (_ => {})(MprisProxy.players); } | ||
| 98 | |||
| 99 | onSecureStateChanged: Custom.Systemd.lockedHint = lock.secure | ||
| 100 | |||
| 101 | WlSessionLockSurface { | ||
| 102 | id: lockSurface | ||
| 103 | |||
| 104 | color: "black" | ||
| 105 | |||
| 106 | LockSurface { | ||
| 107 | id: surfaceContent | ||
| 108 | |||
| 109 | onResponse: responseText => pam.respond(responseText) | ||
| 110 | onAuthRunningChanged: { | ||
| 111 | if (authRunning) | ||
| 112 | pam.start(); | ||
| 113 | } | ||
| 114 | Connections { | ||
| 115 | target: pam | ||
| 116 | function onMessagesChanged() { surfaceContent.messages = pam.messages; } | ||
| 117 | function onResponseRequiredChanged() { surfaceContent.responseRequired = pam.responseRequired; } | ||
| 118 | function onActiveChanged() { surfaceContent.authRunning = pam.active; } | ||
| 119 | } | ||
| 120 | onCurrentTextChanged: lockscreen.currentText = currentText | ||
| 121 | Connections { | ||
| 122 | target: lockscreen | ||
| 123 | function onCurrentTextChanged() { surfaceContent.currentText = lockscreen.currentText; } | ||
| 124 | } | ||
| 125 | Connections { | ||
| 126 | target: lockSurface | ||
| 127 | function onScreenChanged() { surfaceContent.screen = lockSurface.screen; } | ||
| 128 | } | ||
| 129 | } | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml b/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml new file mode 100644 index 00000000..155a009e --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/MaterialDesignIcon.qml | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Effects | ||
| 3 | |||
| 4 | Item { | ||
| 5 | id: root | ||
| 6 | |||
| 7 | required property string icon | ||
| 8 | property color color: "white" | ||
| 9 | |||
| 10 | property real implicitSize: 0 | ||
| 11 | |||
| 12 | readonly property real actualSize: Math.min(root.width, root.height) | ||
| 13 | |||
| 14 | implicitWidth: root.implicitSize | ||
| 15 | implicitHeight: root.implicitSize | ||
| 16 | |||
| 17 | Image { | ||
| 18 | id: sourceImage | ||
| 19 | source: "file://" + @mdi@ + "/svg/" + root.icon + ".svg" | ||
| 20 | anchors.fill: parent | ||
| 21 | fillMode: Image.PreserveAspectFit | ||
| 22 | |||
| 23 | sourceSize.width: root.actualSize | ||
| 24 | sourceSize.height: root.actualSize | ||
| 25 | |||
| 26 | layer.enabled: true | ||
| 27 | layer.effect: MultiEffect { | ||
| 28 | id: effect | ||
| 29 | |||
| 30 | brightness: 1 | ||
| 31 | colorization: 1 | ||
| 32 | colorizationColor: root.color | ||
| 33 | } | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/NiriIdle.qml b/accounts/gkleen@sif/shell/quickshell/NiriIdle.qml new file mode 100644 index 00000000..beff205c --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/NiriIdle.qml | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | import QtQml | ||
| 2 | import Quickshell | ||
| 3 | import Quickshell.Wayland | ||
| 4 | import qs.Services | ||
| 5 | import Custom as Custom | ||
| 6 | |||
| 7 | Scope { | ||
| 8 | IdleMonitor { | ||
| 9 | id: idleMonitor30 | ||
| 10 | timeout: 30 | ||
| 11 | |||
| 12 | onIsIdleChanged: Custom.Systemd.setIdleHint(idleMonitor30.isIdle) | ||
| 13 | } | ||
| 14 | IdleMonitor { | ||
| 15 | id: idleMonitor540 | ||
| 16 | timeout: 540 | ||
| 17 | |||
| 18 | onIsIdleChanged: { | ||
| 19 | if (idleMonitor540.isIdle) | ||
| 20 | NiriService.sendCommand({ "Action": { "PowerOffMonitors": {} } }, _ => {}); | ||
| 21 | } | ||
| 22 | } | ||
| 23 | Connections { | ||
| 24 | target: Custom.Systemd | ||
| 25 | function onSleep(before: bool) { | ||
| 26 | if (!before) | ||
| 27 | NiriService.sendCommand({ "Action": { "PowerOnMonitors": {} } }, _ => {}); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml b/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml new file mode 100644 index 00000000..cc0e49b1 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/NotificationDisplay.qml | |||
| @@ -0,0 +1,340 @@ | |||
| 1 | import QtQml | ||
| 2 | import QtQml.Models | ||
| 3 | import QtQuick | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Widgets | ||
| 6 | import Quickshell.Wayland | ||
| 7 | import qs.Services | ||
| 8 | import QtQuick.Layouts | ||
| 9 | import Quickshell.Services.Notifications | ||
| 10 | |||
| 11 | Scope { | ||
| 12 | id: root | ||
| 13 | |||
| 14 | property var activeScreen: Array.from(Quickshell.screens).find(screen => screen.name === Array.from(NiriService.workspaces).find(ws => ws.is_focused)?.output) ?? null | ||
| 15 | |||
| 16 | Instantiator { | ||
| 17 | id: notifsRepeater | ||
| 18 | |||
| 19 | model: ScriptModel { | ||
| 20 | values: NotificationManager.groups | ||
| 21 | } | ||
| 22 | |||
| 23 | delegate: PanelWindow { | ||
| 24 | id: notifWindow | ||
| 25 | |||
| 26 | visible: NotificationManager.active | ||
| 27 | |||
| 28 | screen: root.activeScreen | ||
| 29 | |||
| 30 | WlrLayershell.namespace: "notifications" | ||
| 31 | |||
| 32 | required property var modelData | ||
| 33 | required property var index | ||
| 34 | |||
| 35 | property int activeIx: modelData.length - 1 | ||
| 36 | onModelDataChanged: { | ||
| 37 | notifWindow.activeIx = modelData.length - 1; | ||
| 38 | } | ||
| 39 | |||
| 40 | property color textColor: { | ||
| 41 | if (notifWindow.modelData?.[notifWindow.activeIx]?.urgency == NotificationUrgency.Low) | ||
| 42 | return "#ff999999"; | ||
| 43 | return "white"; | ||
| 44 | } | ||
| 45 | property color backgroundColor: { | ||
| 46 | if (notifWindow.modelData?.[notifWindow.activeIx]?.urgency == NotificationUrgency.Critical) | ||
| 47 | return "#dd900000"; | ||
| 48 | return "black"; | ||
| 49 | } | ||
| 50 | |||
| 51 | anchors { | ||
| 52 | right: true | ||
| 53 | top: true | ||
| 54 | } | ||
| 55 | |||
| 56 | readonly property real spaceAbove: { | ||
| 57 | var res = 0; | ||
| 58 | for (let i = 0; i < notifWindow.index; i++) { | ||
| 59 | (_ => {})(notifsRepeater.objectAt(i).modelData); | ||
| 60 | res += notifsRepeater.objectAt(i).height + 8; | ||
| 61 | } | ||
| 62 | return res; | ||
| 63 | } | ||
| 64 | |||
| 65 | margins { | ||
| 66 | right: 26 + 8 | ||
| 67 | top: 8 + spaceAbove | ||
| 68 | } | ||
| 69 | |||
| 70 | color: "transparent" | ||
| 71 | |||
| 72 | implicitHeight: Math.max(notifCount.visible ? notifCount.contentHeight : 0, notifSummary.contentHeight) + (notifBody.visible ? 8 + notifBody.contentHeight : 0) + (notifActions.visible ? 8 + notifActions.height : 0) + (notifTime.visible ? 8 + notifTime.contentHeight : 0) + 16 | ||
| 73 | implicitWidth: 400 | ||
| 74 | |||
| 75 | WrapperMouseArea { | ||
| 76 | enabled: true | ||
| 77 | |||
| 78 | anchors.fill: parent | ||
| 79 | |||
| 80 | cursorShape: Qt.PointingHandCursor | ||
| 81 | |||
| 82 | onClicked: { | ||
| 83 | for (const notif of notifWindow.modelData) | ||
| 84 | notif.dismiss(); | ||
| 85 | } | ||
| 86 | |||
| 87 | property real angleRem: 0 | ||
| 88 | property real sensitivity: 1 / 120 | ||
| 89 | onWheel: event => { | ||
| 90 | angleRem += event.angleDelta.y; | ||
| 91 | const d = Math.round(angleRem * sensitivity); | ||
| 92 | angleRem -= d / sensitivity; | ||
| 93 | notifWindow.activeIx = ((notifWindow.modelData?.length ?? 1) + notifWindow.activeIx - d) % (notifWindow.modelData?.length ?? 1); | ||
| 94 | } | ||
| 95 | |||
| 96 | Rectangle { | ||
| 97 | color: notifWindow.backgroundColor | ||
| 98 | anchors.fill: parent | ||
| 99 | border { | ||
| 100 | color: Qt.hsla(195/360, 1, 0.45, 1) | ||
| 101 | width: 2 | ||
| 102 | } | ||
| 103 | |||
| 104 | GridLayout { | ||
| 105 | id: notifLayout | ||
| 106 | |||
| 107 | width: 400 - 16 | ||
| 108 | anchors.fill: parent | ||
| 109 | anchors.margins: 8 | ||
| 110 | columnSpacing: 8 | ||
| 111 | rowSpacing: 8 | ||
| 112 | |||
| 113 | columns: notifImage.visible ? 3 : 2 | ||
| 114 | rows: { | ||
| 115 | var res = 1; | ||
| 116 | if (notifBody.visible) | ||
| 117 | res += 1; | ||
| 118 | if (notifActions.visible) | ||
| 119 | res += 1; | ||
| 120 | if (notifTime.visible) | ||
| 121 | res += 1; | ||
| 122 | return res; | ||
| 123 | } | ||
| 124 | |||
| 125 | Text { | ||
| 126 | id: notifCount | ||
| 127 | |||
| 128 | visible: notifWindow.modelData?.length > 1 ?? false | ||
| 129 | text: `${notifWindow.activeIx + 1}/${notifWindow.modelData?.length ?? ""}` | ||
| 130 | |||
| 131 | font.pointSize: 10 | ||
| 132 | font.family: "Fira Sans" | ||
| 133 | font.bold: true | ||
| 134 | font.features: { "tnum": 1 } | ||
| 135 | color: notifWindow.textColor | ||
| 136 | maximumLineCount: 1 | ||
| 137 | |||
| 138 | Layout.fillWidth: false | ||
| 139 | Layout.row: 0 | ||
| 140 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 141 | } | ||
| 142 | |||
| 143 | Text { | ||
| 144 | id: notifSummary | ||
| 145 | |||
| 146 | text: notifWindow.modelData?.[notifWindow.activeIx]?.summary ?? "" | ||
| 147 | |||
| 148 | font.pointSize: 10 | ||
| 149 | font.family: "Fira Sans" | ||
| 150 | font.italic: true | ||
| 151 | color: notifWindow.textColor | ||
| 152 | maximumLineCount: 1 | ||
| 153 | elide: Text.ElideRight | ||
| 154 | |||
| 155 | Layout.fillWidth: true | ||
| 156 | Layout.row: 0 | ||
| 157 | Layout.column: (notifCount.visible ? 1 : 0) + (notifImage.visible ? 1 : 0) | ||
| 158 | Layout.columnSpan: notifCount.visible ? 1 : 2 | ||
| 159 | } | ||
| 160 | |||
| 161 | Image { | ||
| 162 | id: notifImage | ||
| 163 | |||
| 164 | visible: (notifWindow.modelData?.[notifWindow.activeIx]?.image || notifWindow.modelData?.[notifWindow.activeIx]?.appIcon) ?? false | ||
| 165 | |||
| 166 | onStatusChanged: { | ||
| 167 | if (notifImage.status == Image.Error) | ||
| 168 | notifImage.visible = false; | ||
| 169 | } | ||
| 170 | |||
| 171 | source: (notifWindow.modelData?.[notifWindow.activeIx]?.image || notifWindow.modelData?.[notifWindow.activeIx]?.appIcon) ?? "" | ||
| 172 | fillMode: Image.PreserveAspectFit | ||
| 173 | asynchronous: true | ||
| 174 | smooth: true | ||
| 175 | mipmap: true | ||
| 176 | |||
| 177 | Layout.maximumWidth: 50 | ||
| 178 | Layout.column: 0 | ||
| 179 | Layout.row: 0 | ||
| 180 | Layout.fillHeight: true | ||
| 181 | Layout.rowSpan: 1 + (notifBody.visible ? 1 : 0) + (notifTime.visible ? 1 : 0) | ||
| 182 | } | ||
| 183 | |||
| 184 | Text { | ||
| 185 | id: notifBody | ||
| 186 | |||
| 187 | visible: notifWindow.modelData?.[notifWindow.activeIx]?.body ?? false | ||
| 188 | text: notifWindow.modelData?.[notifWindow.activeIx]?.body ?? "" | ||
| 189 | textFormat: Text.RichText | ||
| 190 | wrapMode: Text.Wrap | ||
| 191 | |||
| 192 | font.pointSize: 10 | ||
| 193 | font.family: "Fira Sans" | ||
| 194 | color: notifWindow.textColor | ||
| 195 | |||
| 196 | Layout.fillWidth: true | ||
| 197 | Layout.row: 1 | ||
| 198 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 199 | Layout.columnSpan: notifCount.visible ? 2 : 1 | ||
| 200 | } | ||
| 201 | |||
| 202 | Text { | ||
| 203 | id: notifTime | ||
| 204 | |||
| 205 | Connections { | ||
| 206 | target: NotificationManager.clock | ||
| 207 | function onDateChanged() { | ||
| 208 | notifTime.text = NotificationManager.formatTime(notifWindow.modelData?.[notifWindow.activeIx]?.receivedTime); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | visible: notifTime.text && notifTime.text !== "now" | ||
| 213 | text: NotificationManager.formatTime(notifWindow.modelData?.[notifWindow.activeIx]?.receivedTime) | ||
| 214 | |||
| 215 | font.pointSize: 8 | ||
| 216 | font.family: "Fira Sans" | ||
| 217 | font.italic: true | ||
| 218 | color: "#555" | ||
| 219 | maximumLineCount: 1 | ||
| 220 | horizontalAlignment: Text.AlignRight | ||
| 221 | |||
| 222 | Layout.fillWidth: true | ||
| 223 | Layout.row: notifBody.visible ? 2 : 1 | ||
| 224 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 225 | Layout.columnSpan: 2 | ||
| 226 | } | ||
| 227 | |||
| 228 | RowLayout { | ||
| 229 | id: notifActions | ||
| 230 | |||
| 231 | visible: notifWindow.modelData?.[notifWindow.activeIx]?.actions.length > 0 ?? false | ||
| 232 | |||
| 233 | spacing: 8 | ||
| 234 | uniformCellSizes: true | ||
| 235 | |||
| 236 | width: 400 - 16 | ||
| 237 | Layout.row: 1 + (notifBody.visible ? 1 : 0) + (notifTime.visible ? 1 : 0) | ||
| 238 | Layout.column: 0 | ||
| 239 | Layout.columnSpan: 2 + (notifImage.visible ? 1 : 0) | ||
| 240 | |||
| 241 | Repeater { | ||
| 242 | model: ScriptModel { | ||
| 243 | values: notifWindow.modelData?.[notifWindow.activeIx]?.actions | ||
| 244 | } | ||
| 245 | |||
| 246 | delegate: WrapperMouseArea { | ||
| 247 | id: actionMouseArea | ||
| 248 | |||
| 249 | required property var modelData | ||
| 250 | |||
| 251 | height: actionLabelWrapper.implicitHeight | ||
| 252 | Layout.fillWidth: true | ||
| 253 | Layout.horizontalStretchFactor: 1 | ||
| 254 | |||
| 255 | hoverEnabled: true | ||
| 256 | cursorShape: Qt.PointingHandCursor | ||
| 257 | |||
| 258 | onClicked: actionMouseArea.modelData?.invoke() | ||
| 259 | |||
| 260 | Rectangle { | ||
| 261 | anchors.fill: parent | ||
| 262 | |||
| 263 | color: actionMouseArea.containsMouse ? "#20ffffff" : "transparent" | ||
| 264 | |||
| 265 | border { | ||
| 266 | width: 2 | ||
| 267 | color: "#20ffffff" | ||
| 268 | } | ||
| 269 | |||
| 270 | WrapperItem { | ||
| 271 | id: actionLabelWrapper | ||
| 272 | |||
| 273 | margin: 8 | ||
| 274 | anchors.centerIn: parent | ||
| 275 | |||
| 276 | RowLayout { | ||
| 277 | id: actionLabelLayout | ||
| 278 | |||
| 279 | spacing: 8 | ||
| 280 | |||
| 281 | IconImage { | ||
| 282 | id: actionIcon | ||
| 283 | |||
| 284 | visible: notifWindow.modelData?.[notifWindow.activeIx]?.hasActionIcons | ||
| 285 | |||
| 286 | onStatusChanged: { | ||
| 287 | if (actionIcon.status == Image.Error) | ||
| 288 | actionIcon.visible = false; | ||
| 289 | } | ||
| 290 | |||
| 291 | implicitSize: 16 | ||
| 292 | source: { | ||
| 293 | if (!actionIcon.visible) | ||
| 294 | return ""; | ||
| 295 | |||
| 296 | let icon = actionMouseArea.modelData?.identifier ?? "" | ||
| 297 | if (icon.includes("?path=")) { | ||
| 298 | const split = icon.split("?path=") | ||
| 299 | if (split.length !== 2) | ||
| 300 | return icon | ||
| 301 | const name = split[0] | ||
| 302 | const path = split[1] | ||
| 303 | const fileName = name.substring( | ||
| 304 | name.lastIndexOf("/") + 1) | ||
| 305 | return `file://${path}/${fileName}` | ||
| 306 | } | ||
| 307 | return icon | ||
| 308 | } | ||
| 309 | asynchronous: true | ||
| 310 | smooth: true | ||
| 311 | mipmap: true | ||
| 312 | |||
| 313 | Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter | ||
| 314 | } | ||
| 315 | |||
| 316 | Text { | ||
| 317 | id: actionLabel | ||
| 318 | |||
| 319 | visible: actionMouseArea.modelData?.text ?? false | ||
| 320 | |||
| 321 | text: actionMouseArea.modelData?.text ?? "" | ||
| 322 | |||
| 323 | font.pointSize: 10 | ||
| 324 | font.family: "Fira Sans" | ||
| 325 | color: notifWindow.textColor | ||
| 326 | maximumLineCount: 1 | ||
| 327 | elide: Text.ElideRight | ||
| 328 | } | ||
| 329 | } | ||
| 330 | } | ||
| 331 | } | ||
| 332 | } | ||
| 333 | } | ||
| 334 | } | ||
| 335 | } | ||
| 336 | } | ||
| 337 | } | ||
| 338 | } | ||
| 339 | } | ||
| 340 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml new file mode 100644 index 00000000..b58467b3 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/NotificationInhibitorWidget.qml | |||
| @@ -0,0 +1,266 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import qs.Services | ||
| 5 | import QtQuick.Controls | ||
| 6 | import QtQuick.Layouts | ||
| 7 | import QtQuick.Shapes | ||
| 8 | |||
| 9 | Item { | ||
| 10 | id: root | ||
| 11 | |||
| 12 | width: icon.width + 8 | ||
| 13 | height: parent.height | ||
| 14 | anchors.verticalCenter: parent.verticalCenter | ||
| 15 | |||
| 16 | WrapperMouseArea { | ||
| 17 | id: widgetMouseArea | ||
| 18 | |||
| 19 | anchors.fill: parent | ||
| 20 | |||
| 21 | hoverEnabled: true | ||
| 22 | cursorShape: Qt.PointingHandCursor | ||
| 23 | |||
| 24 | onClicked: NotificationManager.displayInhibited = !NotificationManager.displayInhibited | ||
| 25 | |||
| 26 | Rectangle { | ||
| 27 | anchors.fill: parent | ||
| 28 | color: { | ||
| 29 | if (widgetMouseArea.containsMouse) { | ||
| 30 | return "#33808080"; | ||
| 31 | } | ||
| 32 | return "transparent"; | ||
| 33 | } | ||
| 34 | |||
| 35 | Item { | ||
| 36 | anchors.fill: parent | ||
| 37 | |||
| 38 | MaterialDesignIcon { | ||
| 39 | id: icon | ||
| 40 | |||
| 41 | implicitSize: 14 | ||
| 42 | anchors.centerIn: parent | ||
| 43 | |||
| 44 | icon: NotificationManager.active ? "message" : "message-off" | ||
| 45 | color: { | ||
| 46 | if (!NotificationManager.active && !NotificationManager.displayInhibited) | ||
| 47 | return "#f28a21"; | ||
| 48 | if (NotificationManager.displayInhibited) | ||
| 49 | return "white"; | ||
| 50 | return "#555"; | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | Loader { | ||
| 58 | id: tooltipLoader | ||
| 59 | |||
| 60 | active: false | ||
| 61 | |||
| 62 | Connections { | ||
| 63 | target: widgetMouseArea | ||
| 64 | function onContainsMouseChanged() { | ||
| 65 | if (widgetMouseArea.containsMouse) | ||
| 66 | tooltipLoader.active = true; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | sourceComponent: PopupWindow { | ||
| 71 | id: tooltip | ||
| 72 | |||
| 73 | property bool nextVisible: NotificationManager.active && (widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse) | ||
| 74 | |||
| 75 | anchor { | ||
| 76 | item: widgetMouseArea | ||
| 77 | edges: Edges.Bottom | Edges.Left | ||
| 78 | } | ||
| 79 | visible: false | ||
| 80 | |||
| 81 | onNextVisibleChanged: hangTimer.restart() | ||
| 82 | |||
| 83 | Timer { | ||
| 84 | id: hangTimer | ||
| 85 | interval: tooltip.visible ? 100 : 500 | ||
| 86 | onTriggered: { | ||
| 87 | tooltip.visible = tooltip.nextVisible; | ||
| 88 | if (!tooltip.visible) | ||
| 89 | tooltipLoader.active = false; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | implicitWidth: 400 | ||
| 94 | implicitHeight: Math.min(tooltip.screen.height * 0.66, Math.max(100, scroll.contentHeight + 16)) | ||
| 95 | color: "black" | ||
| 96 | |||
| 97 | WrapperMouseArea { | ||
| 98 | id: tooltipMouseArea | ||
| 99 | |||
| 100 | hoverEnabled: true | ||
| 101 | enabled: true | ||
| 102 | |||
| 103 | anchors.fill: parent | ||
| 104 | |||
| 105 | WrapperItem { | ||
| 106 | margin: 8 | ||
| 107 | |||
| 108 | ScrollView { | ||
| 109 | id: scroll | ||
| 110 | |||
| 111 | contentWidth: availableWidth | ||
| 112 | // ScrollBar.vertical.policy: ScrollBar.AlwaysOn | ||
| 113 | |||
| 114 | ColumnLayout { | ||
| 115 | id: historyLayout | ||
| 116 | anchors { | ||
| 117 | left: parent.left | ||
| 118 | right: parent.right | ||
| 119 | } | ||
| 120 | |||
| 121 | spacing: 8 | ||
| 122 | |||
| 123 | Repeater { | ||
| 124 | model: ScriptModel { | ||
| 125 | values: [...NotificationManager.history].reverse().map(o => o.notification) | ||
| 126 | } | ||
| 127 | |||
| 128 | delegate: GridLayout { | ||
| 129 | id: notif | ||
| 130 | |||
| 131 | Layout.fillWidth: true | ||
| 132 | Layout.preferredHeight: notifSummary.contentHeight + (notifBody.visible ? notifBody.contentHeight + 8 : 0) + (notifSep.visible ? notifSep.height + 8 : 0) + notifTime.contentHeight + 8 | ||
| 133 | |||
| 134 | required property var modelData | ||
| 135 | required property int index | ||
| 136 | |||
| 137 | columnSpacing: 8 | ||
| 138 | rowSpacing: 8 | ||
| 139 | |||
| 140 | columns: notifImage.visible ? 2 : 1 | ||
| 141 | rows: { | ||
| 142 | var res = 2; | ||
| 143 | if (notifBody.visible) | ||
| 144 | res += 1; | ||
| 145 | if (notifSep.visible) | ||
| 146 | res += 1; | ||
| 147 | return res; | ||
| 148 | } | ||
| 149 | |||
| 150 | Shape { | ||
| 151 | id: notifSep | ||
| 152 | |||
| 153 | visible: notif.index != 0 | ||
| 154 | |||
| 155 | height: 2 | ||
| 156 | width: 400 - 32 | ||
| 157 | |||
| 158 | ShapePath { | ||
| 159 | strokeWidth: 2 | ||
| 160 | strokeColor: "#20ffffff" | ||
| 161 | startX: 0; startY: 0; | ||
| 162 | PathLine { x: 400 - 32; y: 0; } | ||
| 163 | } | ||
| 164 | |||
| 165 | Layout.row: 0 | ||
| 166 | Layout.column: 0 | ||
| 167 | Layout.columnSpan: notifImage.visible ? 2 : 1 | ||
| 168 | Layout.alignment: Qt.AlignHCenter | ||
| 169 | } | ||
| 170 | |||
| 171 | Text { | ||
| 172 | id: notifSummary | ||
| 173 | |||
| 174 | text: notif.modelData?.summary ?? "" | ||
| 175 | |||
| 176 | font.pointSize: 10 | ||
| 177 | font.family: "Fira Sans" | ||
| 178 | font.italic: true | ||
| 179 | color: "white" | ||
| 180 | maximumLineCount: 1 | ||
| 181 | elide: Text.ElideRight | ||
| 182 | |||
| 183 | Layout.fillWidth: true | ||
| 184 | Layout.row: notifSep.visible ? 1 : 0 | ||
| 185 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 186 | } | ||
| 187 | |||
| 188 | Image { | ||
| 189 | id: notifImage | ||
| 190 | |||
| 191 | visible: (notif.modelData?.image || notif.modelData?.appIcon) ?? false | ||
| 192 | |||
| 193 | onStatusChanged: { | ||
| 194 | if (notifImage.status == Image.Error) | ||
| 195 | notifImage.visible = false; | ||
| 196 | } | ||
| 197 | |||
| 198 | source: (notif.modelData?.image || notif.modelData?.appIcon) ?? "" | ||
| 199 | fillMode: Image.PreserveAspectFit | ||
| 200 | asynchronous: true | ||
| 201 | smooth: true | ||
| 202 | mipmap: true | ||
| 203 | |||
| 204 | Layout.maximumWidth: 50 | ||
| 205 | Layout.column: 0 | ||
| 206 | Layout.row: notifSep.visible ? 1 : 0 | ||
| 207 | Layout.fillHeight: true | ||
| 208 | Layout.rowSpan: notifBody.visible ? 3 : 2 | ||
| 209 | } | ||
| 210 | |||
| 211 | Text { | ||
| 212 | id: notifBody | ||
| 213 | |||
| 214 | visible: notif.modelData?.body ?? false | ||
| 215 | text: notif.modelData?.body ?? "" | ||
| 216 | textFormat: Text.RichText | ||
| 217 | wrapMode: Text.Wrap | ||
| 218 | |||
| 219 | font.pointSize: 10 | ||
| 220 | font.family: "Fira Sans" | ||
| 221 | color: "white" | ||
| 222 | |||
| 223 | Layout.fillWidth: true | ||
| 224 | Layout.row: notifSep.visible ? 2 : 1 | ||
| 225 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 226 | } | ||
| 227 | |||
| 228 | Text { | ||
| 229 | id: notifTime | ||
| 230 | |||
| 231 | Connections { | ||
| 232 | target: NotificationManager.clock | ||
| 233 | function onDateChanged() { | ||
| 234 | notifTime.text = NotificationManager.formatTime(notif.modelData?.receivedTime); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | text: NotificationManager.formatTime(notif.modelData?.receivedTime) | ||
| 239 | |||
| 240 | font.pointSize: 8 | ||
| 241 | font.family: "Fira Sans" | ||
| 242 | font.italic: true | ||
| 243 | color: "#555" | ||
| 244 | maximumLineCount: 1 | ||
| 245 | horizontalAlignment: Text.AlignRight | ||
| 246 | |||
| 247 | Layout.fillWidth: true | ||
| 248 | Layout.row: { | ||
| 249 | var res = 1; | ||
| 250 | if (notifSep.visible) | ||
| 251 | res += 1; | ||
| 252 | if (notifBody.visible) | ||
| 253 | res += 1; | ||
| 254 | return res; | ||
| 255 | } | ||
| 256 | Layout.column: notifImage.visible ? 1 : 0 | ||
| 257 | } | ||
| 258 | } | ||
| 259 | } | ||
| 260 | } | ||
| 261 | } | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml b/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml new file mode 100644 index 00000000..9c6b65a4 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/PipewireWidget.qml | |||
| @@ -0,0 +1,483 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Layouts | ||
| 3 | import QtQuick.Controls.Fusion | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Services.Pipewire | ||
| 6 | import Quickshell.Widgets | ||
| 7 | |||
| 8 | Item { | ||
| 9 | height: parent.height | ||
| 10 | width: volumeIcon.width + 8 | ||
| 11 | anchors.verticalCenter: parent.verticalCenter | ||
| 12 | |||
| 13 | PwObjectTracker { | ||
| 14 | objects: [Pipewire.defaultAudioSink] | ||
| 15 | } | ||
| 16 | |||
| 17 | WrapperMouseArea { | ||
| 18 | id: widgetMouseArea | ||
| 19 | |||
| 20 | anchors.fill: parent | ||
| 21 | hoverEnabled: true | ||
| 22 | cursorShape: Qt.PointingHandCursor | ||
| 23 | |||
| 24 | onClicked: { | ||
| 25 | if (!Pipewire.defaultAudioSink) | ||
| 26 | return; | ||
| 27 | Pipewire.defaultAudioSink.audio.muted = !Pipewire.defaultAudioSink.audio.muted; | ||
| 28 | } | ||
| 29 | |||
| 30 | property real sensitivity: (1 / 40) / 120 | ||
| 31 | onWheel: event => { | ||
| 32 | if (!Pipewire.defaultAudioSink) | ||
| 33 | return; | ||
| 34 | Pipewire.defaultAudioSink.audio.volume += event.angleDelta.y * sensitivity; | ||
| 35 | } | ||
| 36 | |||
| 37 | Rectangle { | ||
| 38 | id: volumeWidget | ||
| 39 | |||
| 40 | anchors.fill: parent | ||
| 41 | color: { | ||
| 42 | if (widgetMouseArea.containsMouse) | ||
| 43 | return "#33808080"; | ||
| 44 | return "transparent"; | ||
| 45 | } | ||
| 46 | |||
| 47 | Item { | ||
| 48 | anchors.fill: parent | ||
| 49 | |||
| 50 | MaterialDesignIcon { | ||
| 51 | id: volumeIcon | ||
| 52 | |||
| 53 | implicitSize: 14 | ||
| 54 | anchors.centerIn: parent | ||
| 55 | |||
| 56 | icon: { | ||
| 57 | if (!Pipewire.defaultAudioSink || Pipewire.defaultAudioSink.audio.muted) | ||
| 58 | return "volume-off"; | ||
| 59 | if (Pipewire.defaultAudioSink.audio.volume <= 0.33) | ||
| 60 | return "volume-low"; | ||
| 61 | if (Pipewire.defaultAudioSink.audio.volume <= 0.67) | ||
| 62 | return "volume-medium"; | ||
| 63 | return "volume-high"; | ||
| 64 | } | ||
| 65 | color: "#555" | ||
| 66 | } | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | Loader { | ||
| 72 | id: tooltipLoader | ||
| 73 | |||
| 74 | active: false | ||
| 75 | |||
| 76 | Connections { | ||
| 77 | target: widgetMouseArea | ||
| 78 | function onContainsMouseChanged() { | ||
| 79 | if (widgetMouseArea.containsMouse) | ||
| 80 | tooltipLoader.active = true; | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | PwObjectTracker { | ||
| 85 | objects: Pipewire.devices | ||
| 86 | } | ||
| 87 | PwObjectTracker { | ||
| 88 | objects: Pipewire.nodes | ||
| 89 | } | ||
| 90 | |||
| 91 | sourceComponent: PopupWindow { | ||
| 92 | id: tooltip | ||
| 93 | |||
| 94 | property bool openPopup: false | ||
| 95 | property bool nextVisible: widgetMouseArea.containsMouse || tooltipMouseArea.containsMouse || openPopup | ||
| 96 | |||
| 97 | anchor { | ||
| 98 | item: widgetMouseArea | ||
| 99 | edges: Edges.Bottom | Edges.Left | ||
| 100 | } | ||
| 101 | visible: false | ||
| 102 | |||
| 103 | onNextVisibleChanged: hangTimer.restart() | ||
| 104 | |||
| 105 | Timer { | ||
| 106 | id: hangTimer | ||
| 107 | interval: 100 | ||
| 108 | onTriggered: { | ||
| 109 | tooltip.visible = tooltip.nextVisible; | ||
| 110 | if (!tooltip.visible) | ||
| 111 | tooltipLoader.active = false; | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | implicitWidth: tooltipContent.width | ||
| 116 | implicitHeight: tooltipContent.height | ||
| 117 | color: "transparent" | ||
| 118 | |||
| 119 | Rectangle { | ||
| 120 | width: tooltip.width | ||
| 121 | height: tooltipLayout.childrenRect.height + 16 | ||
| 122 | color: "black" | ||
| 123 | } | ||
| 124 | |||
| 125 | WrapperItem { | ||
| 126 | id: tooltipContent | ||
| 127 | |||
| 128 | bottomMargin: Math.max(0, 200 - tooltipLayout.implicitHeight) | ||
| 129 | |||
| 130 | WrapperMouseArea { | ||
| 131 | id: tooltipMouseArea | ||
| 132 | |||
| 133 | hoverEnabled: true | ||
| 134 | enabled: true | ||
| 135 | |||
| 136 | WrapperItem { | ||
| 137 | margin: 8 | ||
| 138 | bottomMargin: 8 | ||
| 139 | |||
| 140 | GridLayout { | ||
| 141 | id: tooltipLayout | ||
| 142 | |||
| 143 | columns: 4 | ||
| 144 | |||
| 145 | Repeater { | ||
| 146 | model: Array.from(Pipewire.devices.values).filter(dev => dev.type == "Audio/Device") | ||
| 147 | |||
| 148 | Item { | ||
| 149 | id: descItem | ||
| 150 | |||
| 151 | required property var modelData | ||
| 152 | required property int index | ||
| 153 | |||
| 154 | Layout.column: 0 | ||
| 155 | Layout.row: index | ||
| 156 | |||
| 157 | implicitWidth: descText.contentWidth | ||
| 158 | implicitHeight: descText.contentHeight | ||
| 159 | |||
| 160 | Text { | ||
| 161 | id: descText | ||
| 162 | |||
| 163 | color: "white" | ||
| 164 | font.pointSize: 10 | ||
| 165 | font.family: "Fira Sans" | ||
| 166 | |||
| 167 | text: descItem.modelData.description | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | Repeater { | ||
| 173 | id: defaultSinkRepeater | ||
| 174 | |||
| 175 | model: { | ||
| 176 | Array.from(Pipewire.devices.values) | ||
| 177 | .filter(dev => dev.type == "Audio/Device") | ||
| 178 | .map(device => Array.from(Pipewire.nodes.values).find(node => node.type == PwNodeType.AudioSink && node.device?.id == device.id )); | ||
| 179 | } | ||
| 180 | |||
| 181 | Item { | ||
| 182 | id: defaultSinkItem | ||
| 183 | |||
| 184 | required property var modelData | ||
| 185 | required property int index | ||
| 186 | |||
| 187 | visible: Boolean(modelData) | ||
| 188 | |||
| 189 | PwObjectTracker { | ||
| 190 | objects: [defaultSinkItem.modelData] | ||
| 191 | } | ||
| 192 | |||
| 193 | Layout.column: 1 | ||
| 194 | Layout.row: index | ||
| 195 | |||
| 196 | Layout.fillHeight: true | ||
| 197 | |||
| 198 | implicitWidth: 16 + 8 | ||
| 199 | |||
| 200 | WrapperMouseArea { | ||
| 201 | id: defaultSinkMouseArea | ||
| 202 | |||
| 203 | anchors.fill: parent | ||
| 204 | hoverEnabled: true | ||
| 205 | cursorShape: Qt.PointingHandCursor | ||
| 206 | |||
| 207 | onClicked: { | ||
| 208 | Pipewire.preferredDefaultAudioSink = defaultSinkItem.modelData | ||
| 209 | } | ||
| 210 | |||
| 211 | onWheel: event => scrollVolume(event); | ||
| 212 | property real sensitivity: (1 / 40) / 120 | ||
| 213 | function scrollVolume(event) { | ||
| 214 | defaultSinkItem.modelData.audio.volume += event.angleDelta.y * sensitivity; | ||
| 215 | } | ||
| 216 | |||
| 217 | Rectangle { | ||
| 218 | id: defaultSinkWidget | ||
| 219 | |||
| 220 | anchors.fill: parent | ||
| 221 | color: { | ||
| 222 | if (defaultSinkMouseArea.containsMouse) | ||
| 223 | return "#33808080"; | ||
| 224 | return "transparent"; | ||
| 225 | } | ||
| 226 | |||
| 227 | MaterialDesignIcon { | ||
| 228 | width: 16 | ||
| 229 | height: 16 | ||
| 230 | anchors.centerIn: parent | ||
| 231 | |||
| 232 | icon: { | ||
| 233 | if (defaultSinkItem.modelData?.id == Pipewire.defaultAudioSink?.id) | ||
| 234 | return "speaker"; | ||
| 235 | return "speaker-off"; | ||
| 236 | } | ||
| 237 | color: icon == "speaker" ? "white" : "#555" | ||
| 238 | } | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 242 | PopupWindow { | ||
| 243 | id: volumeTooltip | ||
| 244 | |||
| 245 | property bool nextVisible: defaultSinkMouseArea.containsMouse || volumeTooltipMouseArea.containsMouse | ||
| 246 | |||
| 247 | anchor { | ||
| 248 | item: defaultSinkMouseArea | ||
| 249 | edges: Edges.Bottom | Edges.Left | ||
| 250 | } | ||
| 251 | visible: false | ||
| 252 | |||
| 253 | onNextVisibleChanged: volumeHangTimer.restart() | ||
| 254 | |||
| 255 | onVisibleChanged: tooltip.openPopup = volumeTooltip.visible | ||
| 256 | |||
| 257 | Timer { | ||
| 258 | id: volumeHangTimer | ||
| 259 | interval: 100 | ||
| 260 | onTriggered: volumeTooltip.visible = volumeTooltip.nextVisible | ||
| 261 | } | ||
| 262 | |||
| 263 | implicitWidth: volumeTooltipText.contentWidth + 16 | ||
| 264 | implicitHeight: volumeTooltipText.contentHeight + 16 | ||
| 265 | color: "black" | ||
| 266 | |||
| 267 | WrapperMouseArea { | ||
| 268 | id: volumeTooltipMouseArea | ||
| 269 | |||
| 270 | hoverEnabled: true | ||
| 271 | enabled: true | ||
| 272 | |||
| 273 | onWheel: event => defaultSinkMouseArea.scrollVolume(event); | ||
| 274 | |||
| 275 | anchors.fill: parent | ||
| 276 | |||
| 277 | Item { | ||
| 278 | anchors.fill: parent | ||
| 279 | |||
| 280 | Text { | ||
| 281 | id: volumeTooltipText | ||
| 282 | |||
| 283 | anchors.centerIn: parent | ||
| 284 | |||
| 285 | font.pointSize: 10 | ||
| 286 | font.family: "Fira Sans" | ||
| 287 | color: "white" | ||
| 288 | |||
| 289 | text: `${Math.round(defaultSinkItem.modelData?.audio?.volume * 100)}%` | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | } | ||
| 294 | } | ||
| 295 | } | ||
| 296 | |||
| 297 | Repeater { | ||
| 298 | id: defaultSourceRepeater | ||
| 299 | |||
| 300 | model: { | ||
| 301 | Array.from(Pipewire.devices.values) | ||
| 302 | .filter(dev => dev.type == "Audio/Device") | ||
| 303 | .map(device => Array.from(Pipewire.nodes.values).find(node => node.type == PwNodeType.AudioSource && node.device?.id == device.id )); | ||
| 304 | } | ||
| 305 | |||
| 306 | Item { | ||
| 307 | id: defaultSourceItem | ||
| 308 | |||
| 309 | required property var modelData | ||
| 310 | required property int index | ||
| 311 | |||
| 312 | visible: Boolean(modelData) | ||
| 313 | |||
| 314 | PwObjectTracker { | ||
| 315 | objects: [defaultSourceItem.modelData] | ||
| 316 | } | ||
| 317 | |||
| 318 | Layout.column: 2 | ||
| 319 | Layout.row: index | ||
| 320 | |||
| 321 | Layout.fillHeight: true | ||
| 322 | |||
| 323 | implicitWidth: 16 + 8 | ||
| 324 | |||
| 325 | WrapperMouseArea { | ||
| 326 | id: defaultSourceMouseArea | ||
| 327 | |||
| 328 | anchors.fill: parent | ||
| 329 | hoverEnabled: true | ||
| 330 | cursorShape: Qt.PointingHandCursor | ||
| 331 | |||
| 332 | onClicked: { | ||
| 333 | Pipewire.preferredDefaultAudioSource = defaultSourceItem.modelData | ||
| 334 | } | ||
| 335 | |||
| 336 | onWheel: event => scrollVolume(event); | ||
| 337 | property real sensitivity: (1 / 40) / 120 | ||
| 338 | function scrollVolume(event) { | ||
| 339 | defaultSourceItem.modelData.audio.volume += event.angleDelta.y * sensitivity; | ||
| 340 | } | ||
| 341 | |||
| 342 | Rectangle { | ||
| 343 | id: defaultSourceWidget | ||
| 344 | |||
| 345 | anchors.fill: parent | ||
| 346 | color: { | ||
| 347 | if (defaultSourceMouseArea.containsMouse) | ||
| 348 | return "#33808080"; | ||
| 349 | return "transparent"; | ||
| 350 | } | ||
| 351 | |||
| 352 | MaterialDesignIcon { | ||
| 353 | width: 16 | ||
| 354 | height: 16 | ||
| 355 | anchors.centerIn: parent | ||
| 356 | |||
| 357 | icon: { | ||
| 358 | if (defaultSourceItem.modelData?.id == Pipewire.defaultAudioSource?.id) | ||
| 359 | return "microphone"; | ||
| 360 | return "microphone-off"; | ||
| 361 | } | ||
| 362 | color: icon == "microphone" ? "white" : "#555" | ||
| 363 | } | ||
| 364 | } | ||
| 365 | } | ||
| 366 | |||
| 367 | PopupWindow { | ||
| 368 | id: volumeTooltip | ||
| 369 | |||
| 370 | property bool nextVisible: defaultSourceMouseArea.containsMouse || volumeTooltipMouseArea.containsMouse | ||
| 371 | |||
| 372 | anchor { | ||
| 373 | item: defaultSourceMouseArea | ||
| 374 | edges: Edges.Bottom | Edges.Left | ||
| 375 | } | ||
| 376 | visible: false | ||
| 377 | |||
| 378 | onNextVisibleChanged: volumeHangTimer.restart() | ||
| 379 | |||
| 380 | onVisibleChanged: tooltip.openPopup = volumeTooltip.visible | ||
| 381 | |||
| 382 | Timer { | ||
| 383 | id: volumeHangTimer | ||
| 384 | interval: 100 | ||
| 385 | onTriggered: volumeTooltip.visible = volumeTooltip.nextVisible | ||
| 386 | } | ||
| 387 | |||
| 388 | implicitWidth: volumeTooltipText.contentWidth + 16 | ||
| 389 | implicitHeight: volumeTooltipText.contentHeight + 16 | ||
| 390 | color: "black" | ||
| 391 | |||
| 392 | WrapperMouseArea { | ||
| 393 | id: volumeTooltipMouseArea | ||
| 394 | |||
| 395 | hoverEnabled: true | ||
| 396 | enabled: true | ||
| 397 | |||
| 398 | onWheel: event => defaultSourceMouseArea.scrollVolume(event); | ||
| 399 | |||
| 400 | anchors.fill: parent | ||
| 401 | |||
| 402 | Item { | ||
| 403 | anchors.fill: parent | ||
| 404 | |||
| 405 | Text { | ||
| 406 | id: volumeTooltipText | ||
| 407 | |||
| 408 | anchors.centerIn: parent | ||
| 409 | |||
| 410 | font.pointSize: 10 | ||
| 411 | font.family: "Fira Sans" | ||
| 412 | color: "white" | ||
| 413 | |||
| 414 | text: `${Math.round(defaultSourceItem.modelData?.audio?.volume * 100)}%` | ||
| 415 | } | ||
| 416 | } | ||
| 417 | } | ||
| 418 | } | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | Repeater { | ||
| 423 | id: profileRepeater | ||
| 424 | |||
| 425 | model: Array.from(Pipewire.devices.values).filter(dev => dev.type == "Audio/Device") | ||
| 426 | |||
| 427 | Item { | ||
| 428 | id: profileItem | ||
| 429 | |||
| 430 | required property var modelData | ||
| 431 | required property int index | ||
| 432 | |||
| 433 | PwObjectTracker { | ||
| 434 | objects: [profileItem.modelData] | ||
| 435 | } | ||
| 436 | |||
| 437 | Layout.column: 3 | ||
| 438 | Layout.row: index | ||
| 439 | |||
| 440 | Layout.fillWidth: true | ||
| 441 | |||
| 442 | implicitWidth: Math.max(profileBox.implicitWidth, 300) | ||
| 443 | implicitHeight: profileBox.height | ||
| 444 | |||
| 445 | ComboBox { | ||
| 446 | id: profileBox | ||
| 447 | |||
| 448 | model: profileItem.modelData.profiles | ||
| 449 | |||
| 450 | textRole: "description" | ||
| 451 | valueRole: "index" | ||
| 452 | onActivated: profileItem.modelData.setProfile(currentValue) | ||
| 453 | |||
| 454 | anchors.fill: parent | ||
| 455 | |||
| 456 | implicitContentWidthPolicy: ComboBox.WidestText | ||
| 457 | |||
| 458 | Connections { | ||
| 459 | target: profileItem.modelData | ||
| 460 | function onCurrentProfileChanged() { | ||
| 461 | profileBox.currentIndex = Array.from(profileItem.modelData.profiles).findIndex(profile => profile.index == profileItem.modelData.currentProfile); | ||
| 462 | } | ||
| 463 | } | ||
| 464 | Component.onCompleted: { | ||
| 465 | profileBox.currentIndex = Array.from(profileItem.modelData.profiles).findIndex(profile => profile.index == profileItem.modelData.currentProfile); | ||
| 466 | } | ||
| 467 | |||
| 468 | Connections { | ||
| 469 | target: profileBox.popup | ||
| 470 | function onVisibleChanged() { | ||
| 471 | tooltip.openPopup = profileBox.popup.visible | ||
| 472 | } | ||
| 473 | } | ||
| 474 | } | ||
| 475 | } | ||
| 476 | } | ||
| 477 | } | ||
| 478 | } | ||
| 479 | } | ||
| 480 | } | ||
| 481 | } | ||
| 482 | } | ||
| 483 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml b/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml new file mode 100644 index 00000000..d7ffadfe --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/PrivacyWidget.qml | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Layouts | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Widgets | ||
| 5 | import qs.Services | ||
| 6 | |||
| 7 | Item { | ||
| 8 | height: parent.height | ||
| 9 | width: layout.childrenRect.width | ||
| 10 | anchors.verticalCenter: parent.verticalCenter | ||
| 11 | |||
| 12 | visible: Array.from(Privacy.activeItems).length > 0 | ||
| 13 | |||
| 14 | RowLayout { | ||
| 15 | id: layout | ||
| 16 | |||
| 17 | anchors.fill: parent | ||
| 18 | |||
| 19 | spacing: 8 | ||
| 20 | |||
| 21 | Repeater { | ||
| 22 | model: Privacy.activeItems | ||
| 23 | |||
| 24 | Item { | ||
| 25 | id: privacyItem | ||
| 26 | |||
| 27 | required property var modelData; | ||
| 28 | |||
| 29 | height: parent.height | ||
| 30 | width: icon.width | ||
| 31 | |||
| 32 | MaterialDesignIcon { | ||
| 33 | id: icon | ||
| 34 | |||
| 35 | implicitSize: 14 | ||
| 36 | anchors.centerIn: parent | ||
| 37 | |||
| 38 | icon: { | ||
| 39 | if (privacyItem.modelData == Privacy.Item.Microphone) | ||
| 40 | return "microphone"; | ||
| 41 | if (privacyItem.modelData == Privacy.Item.Screensharing) | ||
| 42 | return "monitor-share"; | ||
| 43 | } | ||
| 44 | color: "#f2201f" | ||
| 45 | } | ||
| 46 | } | ||
| 47 | } | ||
| 48 | } | ||
| 49 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml b/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml new file mode 100644 index 00000000..8318df50 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/Brightness.qml | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import QtQml | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Io | ||
| 6 | import Custom as Custom | ||
| 7 | |||
| 8 | Singleton { | ||
| 9 | id: root | ||
| 10 | |||
| 11 | property string subsystem: "backlight" | ||
| 12 | property string device: "intel_backlight" | ||
| 13 | |||
| 14 | property real currBrightness | ||
| 15 | property real exponent: 4 | ||
| 16 | |||
| 17 | function calcCurrBrightness() { | ||
| 18 | if (!currFile.loaded || !maxFile.loaded) | ||
| 19 | return undefined; | ||
| 20 | const curr = Number(currFile.text()); | ||
| 21 | const max = Number(maxFile.text()); | ||
| 22 | const val = Math.pow(curr / max, 1 / root.exponent); | ||
| 23 | return val; | ||
| 24 | } | ||
| 25 | |||
| 26 | Connections { | ||
| 27 | target: currFile | ||
| 28 | function onLoaded() { | ||
| 29 | const b = root.calcCurrBrightness(); | ||
| 30 | if (typeof b !== 'undefined') | ||
| 31 | root.currBrightness = b; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | Connections { | ||
| 35 | target: maxFile | ||
| 36 | function onLoaded() { | ||
| 37 | const b = root.calcCurrBrightness(); | ||
| 38 | if (typeof b !== 'undefined') | ||
| 39 | root.currBrightness = b; | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | onCurrBrightnessChanged: { | ||
| 44 | root.currBrightness = Math.max(0, Math.min(1, root.currBrightness)); | ||
| 45 | |||
| 46 | const prev = root.calcCurrBrightness(); | ||
| 47 | if (typeof prev === 'undefined' || Math.abs(root.currBrightness - prev) < 0.01) | ||
| 48 | return; | ||
| 49 | |||
| 50 | const max = Number(maxFile.text()); | ||
| 51 | const actual = Number(currFile.text()); | ||
| 52 | let curr = Math.max(0, Math.min(max, Math.pow(root.currBrightness, root.exponent) * max)); | ||
| 53 | if (Math.round(curr) == actual && curr < actual) | ||
| 54 | curr = Math.max(0, actual - 1); | ||
| 55 | else if (Math.round(curr) == actual && curr > actual) | ||
| 56 | curr = Math.min(max, actual + 1); | ||
| 57 | // root.currBrightness = Math.pow(curr / max, 1 / root.exponent); | ||
| 58 | Custom.Systemd.setBrightness(root.subsystem, root.device, Math.round(curr)); | ||
| 59 | } | ||
| 60 | |||
| 61 | FileView { | ||
| 62 | id: currFile | ||
| 63 | path: `/sys/class/${root.subsystem}/${root.device}/brightness` | ||
| 64 | blockAllReads: true | ||
| 65 | watchChanges: true | ||
| 66 | onFileChanged: reload() | ||
| 67 | } | ||
| 68 | FileView { | ||
| 69 | id: maxFile | ||
| 70 | path: `/sys/class/${root.subsystem}/${root.device}/max_brightness` | ||
| 71 | blockAllReads: true | ||
| 72 | watchChanges: true | ||
| 73 | onFileChanged: reload() | ||
| 74 | } | ||
| 75 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/GpgAgent.qml b/accounts/gkleen@sif/shell/quickshell/Services/GpgAgent.qml new file mode 100644 index 00000000..3de69535 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/GpgAgent.qml | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Io | ||
| 5 | |||
| 6 | Singleton { | ||
| 7 | id: root | ||
| 8 | |||
| 9 | Socket { | ||
| 10 | id: agentSocket | ||
| 11 | connected: true | ||
| 12 | path: `${Quickshell.env("XDG_RUNTIME_DIR")}/gnupg/S.gpg-agent` | ||
| 13 | } | ||
| 14 | |||
| 15 | function reloadAgent() { | ||
| 16 | agentSocket.write("RELOADAGENT\n") | ||
| 17 | } | ||
| 18 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/InhibitorState.qml b/accounts/gkleen@sif/shell/quickshell/Services/InhibitorState.qml new file mode 100644 index 00000000..fe48fd7f --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/InhibitorState.qml | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Custom as Custom | ||
| 5 | |||
| 6 | Singleton { | ||
| 7 | id: inhibitorState | ||
| 8 | |||
| 9 | property bool waylandIdleInhibited: false | ||
| 10 | property alias lidSwitchInhibited: lidSwitchInhibitor.enabled | ||
| 11 | |||
| 12 | Custom.SystemdInhibitor { | ||
| 13 | id: lidSwitchInhibitor | ||
| 14 | |||
| 15 | enabled: false | ||
| 16 | |||
| 17 | what: Custom.SystemdInhibitorParams.HandleLidSwitch | ||
| 18 | who: "quickshell" | ||
| 19 | why: "User request" | ||
| 20 | mode: Custom.SystemdInhibitorParams.BlockWeak | ||
| 21 | } | ||
| 22 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml b/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml new file mode 100644 index 00000000..e3ab9755 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/MprisProxy.qml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Services.Mpris | ||
| 5 | |||
| 6 | Scope { | ||
| 7 | property list<var> players: Mpris.players.values | ||
| 8 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml b/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml new file mode 100644 index 00000000..cce614eb --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/NiriService.qml | |||
| @@ -0,0 +1,194 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Io | ||
| 5 | import QtQuick | ||
| 6 | |||
| 7 | Singleton { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | property var workspaces: [] | ||
| 11 | property var outputs: {} | ||
| 12 | property var keyboardLayouts: {} | ||
| 13 | property var windows: [] | ||
| 14 | readonly property string socketPath: Quickshell.env("NIRI_SOCKET") | ||
| 15 | |||
| 16 | function refreshOutputs() { | ||
| 17 | commandSocket.sendCommand("Outputs", data => { | ||
| 18 | outputs = data.Ok.Outputs; | ||
| 19 | }); | ||
| 20 | } | ||
| 21 | |||
| 22 | function sendCommand(command, callback) { | ||
| 23 | commandSocket.sendCommand(command, callback); | ||
| 24 | } | ||
| 25 | |||
| 26 | Socket { | ||
| 27 | id: eventStreamSocket | ||
| 28 | path: root.socketPath | ||
| 29 | connected: true | ||
| 30 | |||
| 31 | property bool acked: false | ||
| 32 | |||
| 33 | onConnectionStateChanged: { | ||
| 34 | if (connected) { | ||
| 35 | acked = false; | ||
| 36 | write('"EventStream"\n'); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | parser: SplitParser { | ||
| 41 | onRead: line => { | ||
| 42 | try { | ||
| 43 | const event = JSON.parse(line) | ||
| 44 | |||
| 45 | // console.log(JSON.stringify(event)) | ||
| 46 | |||
| 47 | if (event.WorkspacesChanged) { | ||
| 48 | root.workspaces = event.WorkspacesChanged.workspaces | ||
| 49 | root.refreshOutputs(); | ||
| 50 | } else if (event.WorkspaceActivated) | ||
| 51 | eventWorkspaceActivated(event.WorkspaceActivated); | ||
| 52 | else if (event.WorkspaceUrgencyChanged) | ||
| 53 | eventWorkspaceUrgencyChanged(event.WorkspaceUrgencyChanged); | ||
| 54 | else if (event.WorkspaceActiveWindowChanged) | ||
| 55 | eventWorkspaceActiveWindowChanged(event.WorkspaceActiveWindowChanged); | ||
| 56 | else if (event.KeyboardLayoutsChanged) | ||
| 57 | root.keyboardLayouts = event.KeyboardLayoutsChanged.keyboard_layouts; | ||
| 58 | else if (event.KeyboardLayoutSwitched) | ||
| 59 | root.keyboardLayouts = Object.assign({}, root.keyboardLayouts, {"current_idx": event.KeyboardLayoutSwitched.idx }); | ||
| 60 | else if (event.WindowsChanged) | ||
| 61 | root.windows = event.WindowsChanged.windows | ||
| 62 | else if (event.WindowOpenedOrChanged) | ||
| 63 | eventWindowOpenedOrChanged(event.WindowOpenedOrChanged); | ||
| 64 | else if (event.WindowClosed) | ||
| 65 | eventWindowClosed(event.WindowClosed); | ||
| 66 | else if (event.WindowFocusChanged) | ||
| 67 | eventWindowFocusChanged(event.WindowFocusChanged); | ||
| 68 | else if (event.WindowUrgencyChanged) | ||
| 69 | eventWindowUrgencyChanged(event.WindowUrgencyChanged); | ||
| 70 | else if (event.WindowLayoutsChanged) | ||
| 71 | eventWindowLayoutsChanged(event.WindowLayoutsChanged); | ||
| 72 | else if (event.Ok && !eventStreamSocket.acked) { eventStreamSocket.acked = true; } | ||
| 73 | else if (event.OverviewOpenedOrClosed) {} | ||
| 74 | else if (event.ConfigLoaded) {} | ||
| 75 | else | ||
| 76 | console.log(JSON.stringify(event)); | ||
| 77 | } catch (e) { | ||
| 78 | console.warn("NiriService: Failed to parse event:", line, e) | ||
| 79 | } | ||
| 80 | } | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | Socket { | ||
| 85 | id: commandSocket | ||
| 86 | path: root.socketPath | ||
| 87 | connected: true | ||
| 88 | |||
| 89 | property var awaitingAnswer: null | ||
| 90 | property var cmdQueue: [] | ||
| 91 | |||
| 92 | parser: SplitParser { | ||
| 93 | onRead: line => { | ||
| 94 | if (commandSocket.awaitingAnswer === null) | ||
| 95 | return; | ||
| 96 | |||
| 97 | try { | ||
| 98 | const response = JSON.parse(line); | ||
| 99 | commandSocket.awaitingAnswer.callback(response); | ||
| 100 | commandSocket.awaitingAnswer = null; | ||
| 101 | } catch (e) { | ||
| 102 | console.warn("NiriService: Failed to parse response:", line, e) | ||
| 103 | } | ||
| 104 | commandSocket._handleQueue(); | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | onCmdQueueChanged: { | ||
| 109 | _handleQueue(); | ||
| 110 | } | ||
| 111 | onAwaitingAnswerChanged: { | ||
| 112 | _handleQueue(); | ||
| 113 | } | ||
| 114 | |||
| 115 | function _handleQueue() { | ||
| 116 | if (cmdQueue.length <= 0 || awaitingAnswer !== null) | ||
| 117 | return; | ||
| 118 | |||
| 119 | let localQueue = Array.from(cmdQueue); | ||
| 120 | awaitingAnswer = localQueue.shift(); | ||
| 121 | cmdQueue = localQueue; | ||
| 122 | write(JSON.stringify(awaitingAnswer.command) + '\n'); | ||
| 123 | } | ||
| 124 | |||
| 125 | function sendCommand(command, callback) { | ||
| 126 | cmdQueue = Array.from(cmdQueue).concat([{ "command": command, "callback": callback }]) | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | function eventWorkspaceActivated(data) { | ||
| 131 | let relevant_output = null; | ||
| 132 | Array.from(root.workspaces).forEach(ws => { | ||
| 133 | if (data.id === ws.id) | ||
| 134 | relevant_output = ws.output; | ||
| 135 | }); | ||
| 136 | root.workspaces = Array.from(root.workspaces).map(ws => { | ||
| 137 | if (data.focused) | ||
| 138 | ws.is_focused = false; | ||
| 139 | if (ws.output === relevant_output) | ||
| 140 | ws.is_active = false; | ||
| 141 | if (data.id === ws.id) { | ||
| 142 | ws.is_active = true; | ||
| 143 | ws.is_focused = data.focused; | ||
| 144 | } | ||
| 145 | return ws; | ||
| 146 | }); | ||
| 147 | } | ||
| 148 | function eventWorkspaceUrgencyChanged(data) { | ||
| 149 | root.workspaces = Array.from(root.workspaces).map(ws => { | ||
| 150 | if (data.id == ws.id) | ||
| 151 | ws.is_urgent = data.urgent; | ||
| 152 | return ws; | ||
| 153 | }); | ||
| 154 | } | ||
| 155 | function eventWorkspaceActiveWindowChanged(data) { | ||
| 156 | root.workspaces = Array.from(root.workspaces).map(ws => { | ||
| 157 | if (data.workspace_id === ws.id) | ||
| 158 | ws.active_window_id = data.active_window_id; | ||
| 159 | return ws; | ||
| 160 | }); | ||
| 161 | } | ||
| 162 | function eventWindowOpenedOrChanged(data) { | ||
| 163 | root.windows = Array.from(root.windows).map(win => { | ||
| 164 | if (data.window.is_focused) | ||
| 165 | win.is_focused = false; | ||
| 166 | return win; | ||
| 167 | }).filter(win => win.id !== data.window.id).concat([data.window]); | ||
| 168 | } | ||
| 169 | function eventWindowClosed(data) { | ||
| 170 | root.windows = Array.from(root.windows).filter(win => win.id !== data.id); | ||
| 171 | } | ||
| 172 | function eventWindowFocusChanged(data) { | ||
| 173 | root.windows = Array.from(root.windows).map(win => { | ||
| 174 | win.is_focused = win.id === data.id; | ||
| 175 | return win; | ||
| 176 | }); | ||
| 177 | } | ||
| 178 | function eventWindowUrgencyChanged(data) { | ||
| 179 | root.windows = Array.from(root.windows).map(win => { | ||
| 180 | if (win.id === data.id) | ||
| 181 | win.is_urgent = data.urgent; | ||
| 182 | return win; | ||
| 183 | }); | ||
| 184 | } | ||
| 185 | function eventWindowLayoutsChanged(data) { | ||
| 186 | root.windows = Array.from(root.windows).map(win => { | ||
| 187 | Array.from(data.changes).forEach(change => { | ||
| 188 | if (win.id === change[0]) | ||
| 189 | win.layout = change[1]; | ||
| 190 | }); | ||
| 191 | return win; | ||
| 192 | }); | ||
| 193 | } | ||
| 194 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml b/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml new file mode 100644 index 00000000..f02d1695 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/NotificationManager.qml | |||
| @@ -0,0 +1,162 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import QtQml | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Services.Notifications | ||
| 6 | |||
| 7 | Singleton { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | readonly property bool active: !root.lockscreenActive && !root.displayInhibited | ||
| 11 | property bool lockscreenActive: false | ||
| 12 | property bool displayInhibited: false | ||
| 13 | property alias trackedNotifications: server.trackedNotifications | ||
| 14 | readonly property var groups: { | ||
| 15 | function matchesGroupKey(notif, groupKey) { | ||
| 16 | var matches = true; | ||
| 17 | for (const prop in groupKey.test) { | ||
| 18 | if (notif[prop] !== groupKey.test[prop]) { | ||
| 19 | matches = false; | ||
| 20 | break; | ||
| 21 | } | ||
| 22 | } | ||
| 23 | return matches; | ||
| 24 | } | ||
| 25 | |||
| 26 | var groups = new Map(); | ||
| 27 | var notifs = new Array(); | ||
| 28 | for (const [ix, notif] of server.trackedNotifications.values.entries()) { | ||
| 29 | var didGroup = false; | ||
| 30 | for (const groupKey of root.groupKeys) { | ||
| 31 | if (!matchesGroupKey(notif, groupKey)) | ||
| 32 | continue; | ||
| 33 | |||
| 34 | const key = JSON.stringify({ | ||
| 35 | "key": groupKey, | ||
| 36 | "values": Object.assign({}, ...(Array.from(groupKey["group-by"]).map(prop => { | ||
| 37 | var res = {}; | ||
| 38 | res[prop] = notif[prop]; | ||
| 39 | return res; | ||
| 40 | }))) | ||
| 41 | }); | ||
| 42 | if (!groups.has(key)) | ||
| 43 | groups.set(key, new Array()); | ||
| 44 | groups.get(key).push({ "ix": ix, "notif": notif }); | ||
| 45 | didGroup = true; | ||
| 46 | break; | ||
| 47 | } | ||
| 48 | |||
| 49 | if (!didGroup) | ||
| 50 | notifs.push([{ "ix": ix, "notif": notif }]); | ||
| 51 | } | ||
| 52 | notifs.push(...groups.values()); | ||
| 53 | notifs.sort((as, bs) => Math.min(...(as.map(o => o.ix))) - Math.min(...(bs.map(o => o.ix)))); | ||
| 54 | return notifs.map(ns => ns.map(n => n.notif)); | ||
| 55 | } | ||
| 56 | |||
| 57 | property var groupKeys: [ | ||
| 58 | { "test": { "appName": "Element" }, "group-by": [ "summary" ] } | ||
| 59 | ]; | ||
| 60 | |||
| 61 | property int historyLimit: 100 | ||
| 62 | property var history: [] | ||
| 63 | |||
| 64 | Component { | ||
| 65 | id: expirationTimer | ||
| 66 | |||
| 67 | QtObject { | ||
| 68 | id: timer | ||
| 69 | |||
| 70 | required property QtObject parent | ||
| 71 | required property int expirationTime | ||
| 72 | |||
| 73 | property list<QtObject> data: [ | ||
| 74 | Timer { | ||
| 75 | running: root.active && !timer.expired | ||
| 76 | interval: timer.expirationTime | ||
| 77 | onTriggered: { | ||
| 78 | timer.parent.expirationTimer.destroy(); | ||
| 79 | timer.parent.expirationTimer = null; | ||
| 80 | timer.parent.expire(); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | ] | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | Component { | ||
| 88 | id: notificationLock | ||
| 89 | |||
| 90 | RetainableLock {} | ||
| 91 | } | ||
| 92 | |||
| 93 | readonly property SystemClock clock: SystemClock { | ||
| 94 | precision: SystemClock.Minutes | ||
| 95 | } | ||
| 96 | |||
| 97 | function formatTime(time) { | ||
| 98 | const now = root.clock.date; | ||
| 99 | const diff = now - time; | ||
| 100 | const minutes = Math.ceil(diff / 60000); | ||
| 101 | const hours = Math.floor(minutes / 60); | ||
| 102 | |||
| 103 | if (hours < 1) { | ||
| 104 | if (minutes < 1) | ||
| 105 | return "now"; | ||
| 106 | if (minutes == 1) | ||
| 107 | return "1 minute"; | ||
| 108 | return `${minutes} minutes`; | ||
| 109 | } | ||
| 110 | |||
| 111 | const nowDate = new Date(now.getFullYear(), now.getMonth(), now.getDate()) | ||
| 112 | const timeDate = new Date(time.getFullYear(), time.getMonth(), time.getDate()) | ||
| 113 | const days = Math.floor((nowDate - timeDate) / (1000 * 86400)) | ||
| 114 | |||
| 115 | const timeStr = time.toLocaleTimeString(Qt.locale(), "HH:mm"); | ||
| 116 | |||
| 117 | if (days === 0) | ||
| 118 | return timeStr; | ||
| 119 | if (days === 1) | ||
| 120 | return `yesterday ${timeStr}`; | ||
| 121 | |||
| 122 | const dateStr = time.toLocaleTimeString(Qt.locale(), "YYYY-MM-DD"); | ||
| 123 | return `${dateStr} ${timeStr}`; | ||
| 124 | } | ||
| 125 | |||
| 126 | NotificationServer { | ||
| 127 | id: server | ||
| 128 | |||
| 129 | bodySupported: true | ||
| 130 | actionsSupported: true | ||
| 131 | actionIconsSupported: true | ||
| 132 | imageSupported: true | ||
| 133 | bodyMarkupSupported: true | ||
| 134 | bodyImagesSupported: true | ||
| 135 | |||
| 136 | onNotification: notification => { | ||
| 137 | var timeout = notification.expireTimeout * 1000; | ||
| 138 | if (notification.appName == "poweralertd") | ||
| 139 | timeout = 2000; | ||
| 140 | if (timeout > 0) { | ||
| 141 | Object.defineProperty(notification, "expirationTimer", { configurable: true, enumerable: true, writable: true }); | ||
| 142 | notification.expirationTimer = expirationTimer.createObject(notification, { parent: notification, expirationTime: timeout }); | ||
| 143 | } | ||
| 144 | Object.defineProperty(notification, "receivedTime", { configurable: true, enumerable: true, writable: true }); | ||
| 145 | notification.receivedTime = root.clock.date; | ||
| 146 | notification.closed.connect((reason) => server.onNotificationClosed(notification, reason)); | ||
| 147 | notification.tracked = true; | ||
| 148 | } | ||
| 149 | |||
| 150 | function onNotificationClosed(notification, reason) { | ||
| 151 | while (root.history.length >= root.historyLimit) { | ||
| 152 | root.history[0].lock.locked = false; | ||
| 153 | root.history.shift(); | ||
| 154 | } | ||
| 155 | |||
| 156 | root.history.push({ | ||
| 157 | lock: notificationLock.createObject(root, { locked: true, object: notification }), | ||
| 158 | notification: notification | ||
| 159 | }); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml b/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml new file mode 100644 index 00000000..9c813e49 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/Privacy.qml | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | pragma Singleton | ||
| 2 | |||
| 3 | import QtQml | ||
| 4 | import Quickshell | ||
| 5 | import Quickshell.Services.Pipewire | ||
| 6 | |||
| 7 | Singleton { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | PwObjectTracker { | ||
| 11 | objects: Pipewire.nodes.values | ||
| 12 | } | ||
| 13 | |||
| 14 | enum Item { | ||
| 15 | Microphone, | ||
| 16 | Screensharing | ||
| 17 | } | ||
| 18 | |||
| 19 | readonly property list<var> activeItems: { | ||
| 20 | var items = []; | ||
| 21 | if (microphoneActive) | ||
| 22 | items.push(Privacy.Item.Microphone); | ||
| 23 | if (screensharingActive) | ||
| 24 | items.push(Privacy.Item.Screensharing); | ||
| 25 | return items; | ||
| 26 | } | ||
| 27 | |||
| 28 | readonly property bool microphoneActive: { | ||
| 29 | if (!Pipewire.ready || !Pipewire.nodes?.values) { | ||
| 30 | return false | ||
| 31 | } | ||
| 32 | |||
| 33 | for (const node of Pipewire.nodes.values) { | ||
| 34 | if (!node || (node.type & PwNodeType.AudioInStream) != PwNodeType.AudioInStream) | ||
| 35 | continue; | ||
| 36 | |||
| 37 | if (node.properties?.["stream.monitor"] === "true") | ||
| 38 | continue; | ||
| 39 | |||
| 40 | if (node.audio?.muted) | ||
| 41 | continue; | ||
| 42 | |||
| 43 | return true; | ||
| 44 | } | ||
| 45 | |||
| 46 | return false; | ||
| 47 | } | ||
| 48 | |||
| 49 | readonly property bool screensharingActive: { | ||
| 50 | if (!Pipewire.ready || !Pipewire.nodes?.values) { | ||
| 51 | return false | ||
| 52 | } | ||
| 53 | |||
| 54 | for (const node of Pipewire.nodes.values) { | ||
| 55 | if (!node || (node.type & PwNodeType.VideoInStream) != PwNodeType.VideoInStream) | ||
| 56 | continue; | ||
| 57 | |||
| 58 | return true; | ||
| 59 | } | ||
| 60 | |||
| 61 | return false; | ||
| 62 | } | ||
| 63 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/Services/WallpaperSelector.qml b/accounts/gkleen@sif/shell/quickshell/Services/WallpaperSelector.qml new file mode 100644 index 00000000..3c524955 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/Services/WallpaperSelector.qml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | import Custom as Custom | ||
| 2 | |||
| 3 | Custom.FileSelector { | ||
| 4 | id: root | ||
| 5 | |||
| 6 | directory: @wallpapers@ | ||
| 7 | epoch: 72000000 | ||
| 8 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/SystemTray.qml b/accounts/gkleen@sif/shell/quickshell/SystemTray.qml new file mode 100644 index 00000000..f7b4ed96 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/SystemTray.qml | |||
| @@ -0,0 +1,201 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Effects | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Widgets | ||
| 5 | import Quickshell.Services.SystemTray | ||
| 6 | |||
| 7 | Item { | ||
| 8 | anchors.verticalCenter: parent.verticalCenter | ||
| 9 | width: systemTrayRow.childrenRect.width | ||
| 10 | height: parent.height | ||
| 11 | clip: true | ||
| 12 | |||
| 13 | Row { | ||
| 14 | id: systemTrayRow | ||
| 15 | anchors.centerIn: parent | ||
| 16 | width: childrenRect.width | ||
| 17 | height: parent.height | ||
| 18 | spacing: 0 | ||
| 19 | |||
| 20 | Repeater { | ||
| 21 | model: ScriptModel { | ||
| 22 | values: { | ||
| 23 | var trayItems = Array.from(SystemTray.items.values).filter(item => item.status !== Status.Passive); | ||
| 24 | trayItems.sort((a, b) => a.category !== b.category ? b.category - a.category : a.id.localeCompare(b.id)) | ||
| 25 | return trayItems; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | delegate: Item { | ||
| 30 | id: trayItemWrapper | ||
| 31 | |||
| 32 | required property var modelData | ||
| 33 | required property int index | ||
| 34 | |||
| 35 | property var trayItem: modelData | ||
| 36 | property string iconSource: { | ||
| 37 | let icon = trayItem && trayItem.icon | ||
| 38 | if (typeof icon === 'string' || icon instanceof String) { | ||
| 39 | if (icon.includes("?path=")) { | ||
| 40 | const split = icon.split("?path=") | ||
| 41 | if (split.length !== 2) | ||
| 42 | return icon | ||
| 43 | const name = split[0] | ||
| 44 | const path = split[1] | ||
| 45 | const fileName = name.substring( | ||
| 46 | name.lastIndexOf("/") + 1) | ||
| 47 | return `file://${path}/${fileName}` | ||
| 48 | } | ||
| 49 | return icon | ||
| 50 | } | ||
| 51 | return "" | ||
| 52 | } | ||
| 53 | |||
| 54 | width: icon.width + 6 | ||
| 55 | height: parent.height | ||
| 56 | anchors.verticalCenter: parent.verticalCenter | ||
| 57 | |||
| 58 | WrapperMouseArea { | ||
| 59 | id: trayItemArea | ||
| 60 | |||
| 61 | anchors.fill: parent | ||
| 62 | acceptedButtons: Qt.LeftButton | Qt.RightButton | ||
| 63 | hoverEnabled: true | ||
| 64 | cursorShape: trayItem.onlyMenu ? Qt.ArrowCursor : Qt.PointingHandCursor | ||
| 65 | onClicked: mouse => { | ||
| 66 | if (!trayItem) | ||
| 67 | return | ||
| 68 | |||
| 69 | if (mouse.button === Qt.LeftButton | ||
| 70 | && !trayItem.onlyMenu) { | ||
| 71 | trayItem.activate() | ||
| 72 | return | ||
| 73 | } | ||
| 74 | |||
| 75 | if (trayItem.hasMenu) { | ||
| 76 | var globalPos = mapToGlobal(0, 0) | ||
| 77 | var currentScreen = screen || Screen | ||
| 78 | var screenX = currentScreen.x || 0 | ||
| 79 | var relativeX = globalPos.x - screenX | ||
| 80 | menuAnchor.menu = trayItem.menu | ||
| 81 | menuAnchor.anchor.window = bar | ||
| 82 | menuAnchor.anchor.rect = Qt.rect( | ||
| 83 | relativeX, | ||
| 84 | 21, | ||
| 85 | parent.width, 1) | ||
| 86 | menuAnchor.open() | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | Rectangle { | ||
| 91 | anchors.fill: parent | ||
| 92 | color: { | ||
| 93 | if (trayItemArea.containsMouse) | ||
| 94 | return "#33808080"; | ||
| 95 | return "transparent"; | ||
| 96 | } | ||
| 97 | |||
| 98 | Item { | ||
| 99 | anchors.fill: parent | ||
| 100 | |||
| 101 | layer.enabled: true | ||
| 102 | layer.effect: MultiEffect { | ||
| 103 | colorization: 1 | ||
| 104 | colorizationColor: "#555" | ||
| 105 | } | ||
| 106 | |||
| 107 | IconImage { | ||
| 108 | id: icon | ||
| 109 | |||
| 110 | anchors.centerIn: parent | ||
| 111 | implicitSize: 16 | ||
| 112 | source: trayItemWrapper.iconSource | ||
| 113 | asynchronous: true | ||
| 114 | smooth: true | ||
| 115 | mipmap: true | ||
| 116 | |||
| 117 | layer.enabled: true | ||
| 118 | layer.effect: MultiEffect { | ||
| 119 | id: effect | ||
| 120 | |||
| 121 | brightness: 1 | ||
| 122 | } | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | PopupWindow { | ||
| 129 | id: tooltip | ||
| 130 | |||
| 131 | property bool nextVisible: (trayItem.tooltipTitle || trayItem.tooltipDescription) && (trayItemArea.containsMouse || tooltipMouseArea.containsMouse) && !menuAnchor.visible | ||
| 132 | |||
| 133 | anchor { | ||
| 134 | item: trayItemArea | ||
| 135 | edges: Edges.Bottom | ||
| 136 | } | ||
| 137 | |||
| 138 | visible: false | ||
| 139 | onNextVisibleChanged: hangTimer.restart() | ||
| 140 | |||
| 141 | Timer { | ||
| 142 | id: hangTimer | ||
| 143 | interval: 100 | ||
| 144 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 145 | } | ||
| 146 | |||
| 147 | color: "black" | ||
| 148 | |||
| 149 | implicitWidth: Math.max(tooltipTitle.contentWidth, tooltipDescription.contentWidth) + 16 | ||
| 150 | implicitHeight: (trayItem.tooltipTitle ? tooltipTitle.contentHeight : 0) + (trayItem.tooltipDescription ? tooltipDescription.contentHeight : 0) + 16 | ||
| 151 | |||
| 152 | WrapperMouseArea { | ||
| 153 | id: tooltipMouseArea | ||
| 154 | |||
| 155 | hoverEnabled: true | ||
| 156 | enabled: true | ||
| 157 | |||
| 158 | margin: 4 | ||
| 159 | |||
| 160 | anchors.fill: parent | ||
| 161 | |||
| 162 | Item { | ||
| 163 | anchors.fill: parent | ||
| 164 | |||
| 165 | Column { | ||
| 166 | anchors.centerIn: parent | ||
| 167 | Text { | ||
| 168 | id: tooltipTitle | ||
| 169 | |||
| 170 | enabled: trayItem.tooltipTitle | ||
| 171 | |||
| 172 | font.pointSize: 10 | ||
| 173 | font.family: "Fira Sans" | ||
| 174 | font.bold: true | ||
| 175 | color: "white" | ||
| 176 | |||
| 177 | text: trayItem.tooltipTitle | ||
| 178 | } | ||
| 179 | |||
| 180 | Text { | ||
| 181 | id: tooltipDescription | ||
| 182 | |||
| 183 | enabled: trayItem.tooltipDescription | ||
| 184 | |||
| 185 | font.pointSize: 10 | ||
| 186 | font.family: "Fira Sans" | ||
| 187 | color: "white" | ||
| 188 | |||
| 189 | text: trayItem.tooltipDescription | ||
| 190 | } | ||
| 191 | } | ||
| 192 | } | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 198 | QsMenuAnchor { | ||
| 199 | id: menuAnchor | ||
| 200 | } | ||
| 201 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml b/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml new file mode 100644 index 00000000..05a40dbc --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/UnixIPC.qml | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | import Quickshell | ||
| 2 | import Quickshell.Io | ||
| 3 | import Quickshell.Services.Pipewire | ||
| 4 | import Quickshell.Services.Mpris | ||
| 5 | import qs.Services | ||
| 6 | import Custom as Custom | ||
| 7 | import QtQml | ||
| 8 | |||
| 9 | Scope { | ||
| 10 | id: root | ||
| 11 | |||
| 12 | SocketServer { | ||
| 13 | active: true | ||
| 14 | path: `${Quickshell.env("XDG_RUNTIME_DIR")}/shell.sock` | ||
| 15 | handler: Socket { | ||
| 16 | parser: SplitParser { | ||
| 17 | onRead: line => { | ||
| 18 | const command = (() => { | ||
| 19 | try { | ||
| 20 | return JSON.parse(line); | ||
| 21 | } catch (e) { | ||
| 22 | console.warn("UnixIPC: Failed to parse command:", line, e); | ||
| 23 | } | ||
| 24 | })(); | ||
| 25 | if (!command) | ||
| 26 | return; | ||
| 27 | |||
| 28 | if (command.Volume) | ||
| 29 | root.onCommandVolume(command.Volume); | ||
| 30 | else if (command.Brightness) | ||
| 31 | root.onCommandBrightness(command.Brightness); | ||
| 32 | else if (command.LockSession) | ||
| 33 | Custom.Systemd.lockSession(); | ||
| 34 | else if (command.Suspend) | ||
| 35 | Custom.Systemd.suspend(); | ||
| 36 | else if (command.Hibernate) | ||
| 37 | Custom.Systemd.hibernate(); | ||
| 38 | else if (command.Mpris) | ||
| 39 | root.onCommandMpris(command.Mpris); | ||
| 40 | else if (command.Notifications) | ||
| 41 | root.onCommandNotifications(command.Notifications); | ||
| 42 | else | ||
| 43 | console.warn("UnixIPC: Command not handled:", JSON.stringify(command)); | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | onError: e => { | ||
| 48 | if (e == 1) | ||
| 49 | return; | ||
| 50 | console.warn("QLocalSocket::LocalSocketError", e); | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | PwObjectTracker { | ||
| 56 | objects: [ Pipewire.defaultAudioSink, Pipewire.defaultAudioSource ] | ||
| 57 | } | ||
| 58 | function onCommandVolume(command) { | ||
| 59 | if (command.muted === "toggle") | ||
| 60 | Pipewire.defaultAudioSink.audio.muted = !Pipewire.defaultAudioSink.audio.muted; | ||
| 61 | if (command.volume === "up") | ||
| 62 | Pipewire.defaultAudioSink.audio.volume += 0.02; | ||
| 63 | if (command.volume === "down") | ||
| 64 | Pipewire.defaultAudioSink.audio.volume -= 0.02; | ||
| 65 | |||
| 66 | if (command["mic-muted"] === "toggle") | ||
| 67 | Pipewire.defaultAudioSource.audio.muted = !Pipewire.defaultAudioSource.audio.muted; | ||
| 68 | } | ||
| 69 | |||
| 70 | function onCommandBrightness(command) { | ||
| 71 | if (command === "up") | ||
| 72 | Brightness.currBrightness += 0.02 | ||
| 73 | if (command === "down") | ||
| 74 | Brightness.currBrightness -= 0.02 | ||
| 75 | } | ||
| 76 | |||
| 77 | function onCommandMpris(command) { | ||
| 78 | if (command.PauseAll) | ||
| 79 | Array.from(MprisProxy.players).forEach(player => { | ||
| 80 | if (player.canPause && player.isPlaying) | ||
| 81 | player.pause(); | ||
| 82 | }); | ||
| 83 | } | ||
| 84 | Component.onCompleted: { (_ => {})(MprisProxy.players); } | ||
| 85 | |||
| 86 | function onCommandNotifications(command) { | ||
| 87 | if (command.DismissGroup && NotificationManager.active) { | ||
| 88 | if (NotificationManager.groups.length > 0) | ||
| 89 | for (const notif of [...NotificationManager.groups[0]]) | ||
| 90 | notif.dismiss(); | ||
| 91 | } | ||
| 92 | if (command.DismissAll && NotificationManager.active) { | ||
| 93 | for (const notif of [...NotificationManager.trackedNotifications.values]) | ||
| 94 | notif.dismiss(); | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml b/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml new file mode 100644 index 00000000..653f4763 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/VolumeOSD.qml | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | import QtQuick | ||
| 2 | import QtQuick.Layouts | ||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Services.Pipewire | ||
| 5 | import Quickshell.Widgets | ||
| 6 | |||
| 7 | Scope { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | property string show: "" | ||
| 11 | property bool inhibited: true | ||
| 12 | |||
| 13 | PwObjectTracker { | ||
| 14 | objects: [ Pipewire.defaultAudioSink, Pipewire.defaultAudioSource ] | ||
| 15 | } | ||
| 16 | |||
| 17 | Connections { | ||
| 18 | enabled: Pipewire.defaultAudioSink | ||
| 19 | target: Pipewire.defaultAudioSink?.audio | ||
| 20 | |||
| 21 | function onVolumeChanged() { | ||
| 22 | root.show = "sink"; | ||
| 23 | hideTimer.restart(); | ||
| 24 | } | ||
| 25 | function onMutedChanged() { | ||
| 26 | root.show = "sink"; | ||
| 27 | hideTimer.restart(); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | Connections { | ||
| 32 | enabled: Pipewire.defaultAudioSource | ||
| 33 | target: Pipewire.defaultAudioSource?.audio | ||
| 34 | |||
| 35 | function onVolumeChanged() { | ||
| 36 | root.show = "source"; | ||
| 37 | hideTimer.restart(); | ||
| 38 | } | ||
| 39 | function onMutedChanged() { | ||
| 40 | root.show = "source"; | ||
| 41 | hideTimer.restart(); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | onShowChanged: { | ||
| 46 | if (show) | ||
| 47 | hideTimer.restart(); | ||
| 48 | } | ||
| 49 | |||
| 50 | Timer { | ||
| 51 | id: hideTimer | ||
| 52 | interval: 1000 | ||
| 53 | onTriggered: root.show = "" | ||
| 54 | } | ||
| 55 | |||
| 56 | Timer { | ||
| 57 | id: startInhibit | ||
| 58 | interval: 100 | ||
| 59 | running: true | ||
| 60 | onTriggered: { | ||
| 61 | root.show = ""; | ||
| 62 | root.inhibited = false; | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | LazyLoader { | ||
| 67 | active: root.show && !root.inhibited | ||
| 68 | |||
| 69 | Variants { | ||
| 70 | model: Quickshell.screens | ||
| 71 | |||
| 72 | delegate: Scope { | ||
| 73 | id: screenScope | ||
| 74 | |||
| 75 | required property var modelData | ||
| 76 | |||
| 77 | PanelWindow { | ||
| 78 | id: window | ||
| 79 | |||
| 80 | screen: screenScope.modelData | ||
| 81 | |||
| 82 | anchors.top: true | ||
| 83 | margins.top: screen.height / 2 - 50 + 3.5 | ||
| 84 | exclusiveZone: 0 | ||
| 85 | exclusionMode: ExclusionMode.Ignore | ||
| 86 | |||
| 87 | implicitWidth: 400 | ||
| 88 | implicitHeight: 50 | ||
| 89 | |||
| 90 | mask: Region {} | ||
| 91 | |||
| 92 | color: "transparent" | ||
| 93 | |||
| 94 | Rectangle { | ||
| 95 | anchors.fill: parent | ||
| 96 | color: Qt.rgba(0, 0, 0, 0.75) | ||
| 97 | } | ||
| 98 | |||
| 99 | RowLayout { | ||
| 100 | id: layout | ||
| 101 | |||
| 102 | anchors.centerIn: parent | ||
| 103 | |||
| 104 | height: 50 - 8*2 | ||
| 105 | width: 400 - 8*2 | ||
| 106 | |||
| 107 | MaterialDesignIcon { | ||
| 108 | id: volumeIcon | ||
| 109 | |||
| 110 | implicitWidth: parent.height | ||
| 111 | implicitHeight: parent.height | ||
| 112 | |||
| 113 | icon: { | ||
| 114 | if (root.show == "sink") { | ||
| 115 | if (!Pipewire.defaultAudioSink || Pipewire.defaultAudioSink.audio.muted) | ||
| 116 | return "volume-off"; | ||
| 117 | if (Pipewire.defaultAudioSink.audio.volume <= 0.33) | ||
| 118 | return "volume-low"; | ||
| 119 | if (Pipewire.defaultAudioSink.audio.volume <= 0.67) | ||
| 120 | return "volume-medium"; | ||
| 121 | return "volume-high"; | ||
| 122 | } else if (root.show == "source") { | ||
| 123 | if (!Pipewire.defaultAudioSource || Pipewire.defaultAudioSource.audio.muted) | ||
| 124 | return "microphone-off"; | ||
| 125 | if (Pipewire.defaultAudioSource.audio.volume > 1) | ||
| 126 | return "microphone-plus"; | ||
| 127 | return "microphone"; | ||
| 128 | } | ||
| 129 | return "volume-high"; | ||
| 130 | } | ||
| 131 | } | ||
| 132 | |||
| 133 | Rectangle { | ||
| 134 | Layout.fillWidth: true | ||
| 135 | |||
| 136 | implicitHeight: 10 | ||
| 137 | |||
| 138 | color: "#50ffffff" | ||
| 139 | |||
| 140 | Rectangle { | ||
| 141 | anchors { | ||
| 142 | left: parent.left | ||
| 143 | top: parent.top | ||
| 144 | bottom: parent.bottom | ||
| 145 | } | ||
| 146 | |||
| 147 | color: Pipewire.defaultAudioSink?.audio.muted ? "#70ffffff" : "white" | ||
| 148 | |||
| 149 | implicitWidth: { | ||
| 150 | if (root.show == "sink") | ||
| 151 | return parent.width * (Pipewire.defaultAudioSink?.audio.volume ?? 0); | ||
| 152 | else if (root.show == "source") | ||
| 153 | return parent.width * Math.min(1, (Pipewire.defaultAudioSource?.audio.volume ?? 0)); | ||
| 154 | return 0; | ||
| 155 | } | ||
| 156 | } | ||
| 157 | } | ||
| 158 | } | ||
| 159 | } | ||
| 160 | } | ||
| 161 | } | ||
| 162 | } | ||
| 163 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/WallpaperBackground.qml b/accounts/gkleen@sif/shell/quickshell/WallpaperBackground.qml new file mode 100644 index 00000000..4f85a900 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/WallpaperBackground.qml | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | import QtQuick | ||
| 2 | import Quickshell | ||
| 3 | import qs.Services | ||
| 4 | |||
| 5 | Item { | ||
| 6 | id: root | ||
| 7 | |||
| 8 | anchors.fill: parent | ||
| 9 | |||
| 10 | required property string screen | ||
| 11 | |||
| 12 | property Img current: one | ||
| 13 | property string source: selector.selected | ||
| 14 | |||
| 15 | WallpaperSelector { | ||
| 16 | id: selector | ||
| 17 | seed: screen | ||
| 18 | } | ||
| 19 | |||
| 20 | onSourceChanged: { | ||
| 21 | if (!source) | ||
| 22 | current = null; | ||
| 23 | else if (current === one) | ||
| 24 | two.update() | ||
| 25 | else | ||
| 26 | one.update() | ||
| 27 | } | ||
| 28 | |||
| 29 | Img { id: one } | ||
| 30 | Img { id: two } | ||
| 31 | |||
| 32 | component Img: Image { | ||
| 33 | id: img | ||
| 34 | |||
| 35 | function update() { | ||
| 36 | source = root.source || "" | ||
| 37 | } | ||
| 38 | |||
| 39 | anchors.fill: parent | ||
| 40 | fillMode: Image.PreserveAspectCrop | ||
| 41 | smooth: true | ||
| 42 | asynchronous: true | ||
| 43 | cache: false | ||
| 44 | |||
| 45 | opacity: 0 | ||
| 46 | |||
| 47 | onStatusChanged: { | ||
| 48 | if (status === Image.Ready) { | ||
| 49 | root.current = this | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | states: State { | ||
| 54 | name: "visible" | ||
| 55 | when: root.current === img | ||
| 56 | |||
| 57 | PropertyChanges { | ||
| 58 | img.opacity: 1 | ||
| 59 | } | ||
| 60 | StateChangeScript { | ||
| 61 | name: "unloadOther" | ||
| 62 | script: { | ||
| 63 | if (img === one) | ||
| 64 | two.source = "" | ||
| 65 | if (img === two) | ||
| 66 | one.source = "" | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | transitions: Transition { | ||
| 72 | SequentialAnimation { | ||
| 73 | NumberAnimation { | ||
| 74 | target: img | ||
| 75 | properties: "opacity" | ||
| 76 | duration: { | ||
| 77 | if (img === one && two.source == "" || img === two && one.source == "") | ||
| 78 | return 0; | ||
| 79 | return 5000; | ||
| 80 | } | ||
| 81 | easing.type: Easing.OutCubic | ||
| 82 | } | ||
| 83 | ScriptAction { | ||
| 84 | scriptName: "unloadOther" | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml b/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml new file mode 100644 index 00000000..0512ff51 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/WaylandInhibitorWidget.qml | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import Quickshell.Widgets | ||
| 4 | import Quickshell.Wayland | ||
| 5 | import qs.Services | ||
| 6 | |||
| 7 | Item { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | required property var window | ||
| 11 | |||
| 12 | width: icon.width + 8 | ||
| 13 | height: parent.height | ||
| 14 | anchors.verticalCenter: parent.verticalCenter | ||
| 15 | |||
| 16 | IdleInhibitor { | ||
| 17 | id: inhibitor | ||
| 18 | enabled: InhibitorState.waylandIdleInhibited | ||
| 19 | window: root.window | ||
| 20 | } | ||
| 21 | |||
| 22 | WrapperMouseArea { | ||
| 23 | id: widgetMouseArea | ||
| 24 | |||
| 25 | anchors.fill: parent | ||
| 26 | |||
| 27 | hoverEnabled: true | ||
| 28 | cursorShape: Qt.PointingHandCursor | ||
| 29 | |||
| 30 | onClicked: InhibitorState.waylandIdleInhibited = !InhibitorState.waylandIdleInhibited | ||
| 31 | |||
| 32 | Rectangle { | ||
| 33 | anchors.fill: parent | ||
| 34 | color: { | ||
| 35 | if (widgetMouseArea.containsMouse) { | ||
| 36 | return "#33808080"; | ||
| 37 | } | ||
| 38 | return "transparent"; | ||
| 39 | } | ||
| 40 | |||
| 41 | Item { | ||
| 42 | anchors.fill: parent | ||
| 43 | |||
| 44 | MaterialDesignIcon { | ||
| 45 | id: icon | ||
| 46 | |||
| 47 | implicitSize: 14 | ||
| 48 | anchors.centerIn: parent | ||
| 49 | |||
| 50 | icon: inhibitor.enabled ? "eye" : "eye-off" | ||
| 51 | color: inhibitor.enabled ? "white" : "#555" | ||
| 52 | } | ||
| 53 | } | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml b/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml new file mode 100644 index 00000000..3ae94346 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/WorkspaceSwitcher.qml | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | import Quickshell | ||
| 2 | import QtQuick | ||
| 3 | import qs.Services | ||
| 4 | import Quickshell.Widgets | ||
| 5 | import QtQuick.Layouts | ||
| 6 | |||
| 7 | Row { | ||
| 8 | id: workspaces | ||
| 9 | |||
| 10 | required property var screen | ||
| 11 | |||
| 12 | property var ignoreWorkspaces: @ignore_workspaces@ | ||
| 13 | |||
| 14 | height: parent.height | ||
| 15 | anchors.verticalCenter: parent.verticalCenter | ||
| 16 | spacing: 0 | ||
| 17 | |||
| 18 | Repeater { | ||
| 19 | model: ScriptModel { | ||
| 20 | values: { | ||
| 21 | let currWorkspaces = NiriService.workspaces; | ||
| 22 | const ignoreWorkspaces = Array.from(workspaces.ignoreWorkspaces); | ||
| 23 | currWorkspaces = currWorkspaces.filter(ws => ws.output == workspaces.screen.name).filter(ws => ws.is_active || ignoreWorkspaces.every(iws => iws !== ws.name)); | ||
| 24 | currWorkspaces.sort((a, b) => { | ||
| 25 | if (NiriService.outputs?.[a.output]?.logical?.x !== NiriService.outputs?.[b.output]?.logical?.x) | ||
| 26 | return NiriService.outputs?.[a.output]?.logical?.x - NiriService.outputs?.[b.output]?.logical?.x | ||
| 27 | if (NiriService.outputs?.[a.output]?.logical?.y !== NiriService.outputs?.[b.output]?.logical?.y) | ||
| 28 | return NiriService.outputs?.[a.output]?.logical?.y - NiriService.outputs?.[b.output]?.logical?.y | ||
| 29 | return a.idx - b.idx; | ||
| 30 | }); | ||
| 31 | return currWorkspaces; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | Item { | ||
| 36 | id: wsItem | ||
| 37 | |||
| 38 | property var workspaceData: modelData | ||
| 39 | |||
| 40 | width: wsLabel.contentWidth + 8 | ||
| 41 | height: parent.height | ||
| 42 | anchors.verticalCenter: parent.verticalCenter | ||
| 43 | |||
| 44 | WrapperMouseArea { | ||
| 45 | id: mouseArea | ||
| 46 | |||
| 47 | anchors.fill: parent | ||
| 48 | |||
| 49 | hoverEnabled: true | ||
| 50 | cursorShape: Qt.PointingHandCursor | ||
| 51 | enabled: true | ||
| 52 | onClicked: { | ||
| 53 | NiriService.sendCommand({ "Action": { "FocusWorkspace": { "reference": { "Id": workspaceData.id } } } }, _ => {}); | ||
| 54 | } | ||
| 55 | |||
| 56 | Rectangle { | ||
| 57 | anchors.fill: parent | ||
| 58 | |||
| 59 | color: { | ||
| 60 | if (mouseArea.containsMouse) { | ||
| 61 | return "#33808080"; | ||
| 62 | } | ||
| 63 | return "transparent"; | ||
| 64 | } | ||
| 65 | |||
| 66 | Text { | ||
| 67 | id: wsLabel | ||
| 68 | |||
| 69 | anchors.centerIn: parent | ||
| 70 | |||
| 71 | font.pointSize: 10 | ||
| 72 | font.family: "Fira Sans" | ||
| 73 | color: { | ||
| 74 | if (workspaceData.is_active) | ||
| 75 | return "#23fd00"; | ||
| 76 | if (workspaceData.active_window_id === null) | ||
| 77 | return "#555"; | ||
| 78 | return "white"; | ||
| 79 | } | ||
| 80 | |||
| 81 | text: workspaceData.name ? workspaceData.name : workspaceData.idx | ||
| 82 | } | ||
| 83 | } | ||
| 84 | } | ||
| 85 | |||
| 86 | PopupWindow { | ||
| 87 | id: tooltip | ||
| 88 | |||
| 89 | property bool nextVisible: (mouseArea.containsMouse || tooltipMouseArea.containsMouse) && [...windowsModel.values].length > 0 | ||
| 90 | |||
| 91 | anchor { | ||
| 92 | item: mouseArea | ||
| 93 | edges: Edges.Bottom | Edges.Left | ||
| 94 | } | ||
| 95 | visible: false | ||
| 96 | |||
| 97 | onNextVisibleChanged: hangTimer.restart() | ||
| 98 | |||
| 99 | Timer { | ||
| 100 | id: hangTimer | ||
| 101 | interval: 100 | ||
| 102 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 103 | } | ||
| 104 | |||
| 105 | implicitWidth: tooltipContent.implicitWidth | ||
| 106 | implicitHeight: tooltipContent.implicitHeight | ||
| 107 | color: "black" | ||
| 108 | |||
| 109 | WrapperMouseArea { | ||
| 110 | id: tooltipMouseArea | ||
| 111 | |||
| 112 | hoverEnabled: true | ||
| 113 | enabled: true | ||
| 114 | |||
| 115 | anchors.fill: parent | ||
| 116 | |||
| 117 | WrapperItem { | ||
| 118 | id: tooltipContent | ||
| 119 | |||
| 120 | margin: 0 | ||
| 121 | |||
| 122 | ColumnLayout { | ||
| 123 | spacing: 0 | ||
| 124 | |||
| 125 | Repeater { | ||
| 126 | model: ScriptModel { | ||
| 127 | id: windowsModel | ||
| 128 | |||
| 129 | values: { | ||
| 130 | let currWindows = Array.from(NiriService.windows).filter(win => win.workspace_id == wsItem.workspaceData.id); | ||
| 131 | currWindows.sort((a, b) => { | ||
| 132 | if (a.is_floating !== b.is_floating) | ||
| 133 | return b.is_floating - a.is_floating; | ||
| 134 | if (a.layout.tile_pos_in_workspace_view?.[0] !== b.layout.tile_pos_in_workspace_view?.[0]) | ||
| 135 | return a.layout.tile_pos_in_workspace_view?.[0] - b.layout.tile_pos_in_workspace_view?.[0] | ||
| 136 | if (a.layout.tile_pos_in_workspace_view?.[1] !== b.layout.tile_pos_in_workspace_view?.[1]) | ||
| 137 | return a.layout.tile_pos_in_workspace_view?.[1] - b.layout.tile_pos_in_workspace_view?.[1] | ||
| 138 | if (a.layout.pos_in_scrolling_layout?.[0] !== b.layout.pos_in_scrolling_layout?.[0]) | ||
| 139 | return a.layout.pos_in_scrolling_layout?.[0] - b.layout.pos_in_scrolling_layout?.[0] | ||
| 140 | if (a.layout.pos_in_scrolling_layout?.[1] !== b.layout.pos_in_scrolling_layout?.[1]) | ||
| 141 | return a.layout.pos_in_scrolling_layout?.[1] - b.layout.pos_in_scrolling_layout?.[1] | ||
| 142 | if (a.app_id !== b.app_id) | ||
| 143 | return a.app_id.localeCompare(b.app_id); | ||
| 144 | |||
| 145 | return a.title.localeCompare(b.title); | ||
| 146 | }); | ||
| 147 | return currWindows; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | WrapperMouseArea { | ||
| 152 | id: windowMouseArea | ||
| 153 | |||
| 154 | required property int index | ||
| 155 | required property var modelData | ||
| 156 | property var windowData: modelData | ||
| 157 | |||
| 158 | hoverEnabled: true | ||
| 159 | cursorShape: Qt.PointingHandCursor | ||
| 160 | enabled: true | ||
| 161 | |||
| 162 | Layout.fillWidth: true | ||
| 163 | |||
| 164 | onClicked: { | ||
| 165 | NiriService.sendCommand({ "Action": { "FocusWindow": { "id": windowData.id } } }, _ => {}) | ||
| 166 | } | ||
| 167 | |||
| 168 | WrapperRectangle { | ||
| 169 | color: windowMouseArea.containsMouse ? "#33808080" : "transparent"; | ||
| 170 | |||
| 171 | WrapperItem { | ||
| 172 | rightMargin: 8 | ||
| 173 | leftMargin: 8 | ||
| 174 | topMargin: windowMouseArea.index == 0 ? 8 : 4 | ||
| 175 | bottomMargin: windowMouseArea.index == windowsModel.values.length - 1 ? 8 : 4 | ||
| 176 | |||
| 177 | Text { | ||
| 178 | id: windowLabel | ||
| 179 | |||
| 180 | font.pointSize: 10 | ||
| 181 | font.family: "Fira Sans" | ||
| 182 | color: { | ||
| 183 | if (windowData.is_focused) | ||
| 184 | return "#23fd00"; | ||
| 185 | if (NiriService.workspaces.find(ws => ws.id == windowData.workspace_id)?.active_window_id == windowData.id) | ||
| 186 | return "white"; | ||
| 187 | return "#555"; | ||
| 188 | } | ||
| 189 | |||
| 190 | text: windowData.title | ||
| 191 | |||
| 192 | horizontalAlignment: Text.AlignLeft | ||
| 193 | } | ||
| 194 | } | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 198 | } | ||
| 199 | } | ||
| 200 | } | ||
| 201 | } | ||
| 202 | } | ||
| 203 | } | ||
| 204 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml b/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml new file mode 100644 index 00000000..04bcc581 --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/WorktimeWidget.qml | |||
| @@ -0,0 +1,120 @@ | |||
| 1 | import QtQml | ||
| 2 | import Quickshell | ||
| 3 | import Quickshell.Io | ||
| 4 | import QtQuick | ||
| 5 | import Quickshell.Widgets | ||
| 6 | |||
| 7 | Item { | ||
| 8 | id: root | ||
| 9 | |||
| 10 | required property string command | ||
| 11 | property var state: null | ||
| 12 | |||
| 13 | height: parent.height | ||
| 14 | width: label.contentWidth + 8 | ||
| 15 | anchors.verticalCenter: parent.verticalCenter | ||
| 16 | |||
| 17 | Process { | ||
| 18 | id: process | ||
| 19 | running: true | ||
| 20 | command: [ @worktime@, root.command, "--waybar" ] | ||
| 21 | stdout: StdioCollector { | ||
| 22 | id: processCollector | ||
| 23 | onStreamFinished: { | ||
| 24 | try { | ||
| 25 | root.state = JSON.parse(processCollector.text); | ||
| 26 | } catch (e) { | ||
| 27 | console.warn("Worktime: Failed to parse output:", processCollector.text, e); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | Timer { | ||
| 34 | running: true | ||
| 35 | interval: 60 | ||
| 36 | repeat: true | ||
| 37 | onTriggered: process.running = true | ||
| 38 | } | ||
| 39 | |||
| 40 | WrapperMouseArea { | ||
| 41 | id: mouseArea | ||
| 42 | |||
| 43 | anchors.fill: parent | ||
| 44 | |||
| 45 | enabled: true | ||
| 46 | hoverEnabled: true | ||
| 47 | |||
| 48 | Item { | ||
| 49 | anchors.fill: parent | ||
| 50 | |||
| 51 | Text { | ||
| 52 | id: label | ||
| 53 | |||
| 54 | anchors.centerIn: parent | ||
| 55 | |||
| 56 | visible: root.state?.text ?? false | ||
| 57 | text: root.state?.text ?? "" | ||
| 58 | |||
| 59 | font.pointSize: 10 | ||
| 60 | font.family: "Fira Sans" | ||
| 61 | color: { | ||
| 62 | if (root.state?.class == "running") | ||
| 63 | return "white"; | ||
| 64 | if (root.state?.class == "over") | ||
| 65 | return "#f28a21"; | ||
| 66 | return "#555"; | ||
| 67 | } | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | PopupWindow { | ||
| 73 | id: tooltip | ||
| 74 | |||
| 75 | property bool nextVisible: Boolean(root.state?.tooltip ?? false) && (mouseArea.containsMouse || tooltipMouseArea.containsMouse) | ||
| 76 | |||
| 77 | anchor { | ||
| 78 | item: mouseArea | ||
| 79 | edges: Edges.Bottom | Edges.Left | ||
| 80 | } | ||
| 81 | visible: false | ||
| 82 | |||
| 83 | onNextVisibleChanged: hangTimer.restart() | ||
| 84 | |||
| 85 | Timer { | ||
| 86 | id: hangTimer | ||
| 87 | interval: 100 | ||
| 88 | onTriggered: tooltip.visible = tooltip.nextVisible | ||
| 89 | } | ||
| 90 | |||
| 91 | implicitWidth: tooltipText.contentWidth + 16 | ||
| 92 | implicitHeight: tooltipText.contentHeight + 16 | ||
| 93 | color: "black" | ||
| 94 | |||
| 95 | WrapperMouseArea { | ||
| 96 | id: tooltipMouseArea | ||
| 97 | |||
| 98 | enabled: true | ||
| 99 | hoverEnabled: true | ||
| 100 | |||
| 101 | anchors.fill: parent | ||
| 102 | |||
| 103 | Item { | ||
| 104 | anchors.fill: parent | ||
| 105 | |||
| 106 | Text { | ||
| 107 | id: tooltipText | ||
| 108 | |||
| 109 | anchors.centerIn: parent | ||
| 110 | |||
| 111 | font.pointSize: 10 | ||
| 112 | font.family: "Fira Sans" | ||
| 113 | color: "white" | ||
| 114 | |||
| 115 | text: root.state?.tooltip ?? "" | ||
| 116 | } | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/displaymanager.qml b/accounts/gkleen@sif/shell/quickshell/displaymanager.qml new file mode 100644 index 00000000..b452c03d --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/displaymanager.qml | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | //@ pragma UseQApplication | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Wayland | ||
| 5 | import Quickshell.Io | ||
| 6 | import Quickshell.Services.Greetd | ||
| 7 | import QtQml | ||
| 8 | |||
| 9 | |||
| 10 | ShellRoot { | ||
| 11 | id: displaymanager | ||
| 12 | |||
| 13 | settings.watchFiles: false | ||
| 14 | |||
| 15 | property string currentText: "" | ||
| 16 | property string username: @username@ | ||
| 17 | property list<string> command: @niri_session@ | ||
| 18 | property list<var> messages: [] | ||
| 19 | property bool responseRequired: false | ||
| 20 | property bool responseVisible: false | ||
| 21 | |||
| 22 | signal startAuth() | ||
| 23 | |||
| 24 | onStartAuth: { | ||
| 25 | if (Greetd.state !== GreetdState.Inactive) | ||
| 26 | Greetd.cancelSession(); | ||
| 27 | displaymanager.messages = []; | ||
| 28 | Greetd.createSession(displaymanager.username); | ||
| 29 | } | ||
| 30 | |||
| 31 | Connections { | ||
| 32 | target: Greetd | ||
| 33 | function onStateChanged() { | ||
| 34 | console.log("greetd state: ", GreetdState.toString(Greetd.state)); | ||
| 35 | if (Greetd.state === GreetdState.ReadyToLaunch) | ||
| 36 | Greetd.launch(displaymanager.command); | ||
| 37 | } | ||
| 38 | function onAuthMessage(message: string, error: bool, responseRequired: bool, echoResponse: bool) { | ||
| 39 | displaymanager.responseVisible = echoResponse; | ||
| 40 | displaymanager.responseRequired = responseRequired; | ||
| 41 | displaymanager.messages = Array.from(displaymanager.messages).concat([{ "text": message, "error": error }]); | ||
| 42 | } | ||
| 43 | function onAuthFailure(message: string) { | ||
| 44 | displaymanager.responseRequired = false; | ||
| 45 | displaymanager.messages = Array.from(displaymanager.messages).concat([{ "text": message, "error": true }]); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | Component.onCompleted: { | ||
| 50 | if (Greetd.state !== GreetdState.Inactive) | ||
| 51 | Greetd.cancelSession(); | ||
| 52 | } | ||
| 53 | |||
| 54 | Variants { | ||
| 55 | model: Quickshell.screens | ||
| 56 | |||
| 57 | delegate: Scope { | ||
| 58 | id: screenScope | ||
| 59 | |||
| 60 | required property var modelData | ||
| 61 | |||
| 62 | PanelWindow { | ||
| 63 | color: "black" | ||
| 64 | |||
| 65 | screen: screenScope.modelData | ||
| 66 | |||
| 67 | WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive | ||
| 68 | |||
| 69 | anchors.top: true | ||
| 70 | anchors.bottom: true | ||
| 71 | anchors.left: true | ||
| 72 | anchors.right: true | ||
| 73 | |||
| 74 | LockSurface { | ||
| 75 | id: surfaceContent | ||
| 76 | |||
| 77 | screen: screenScope.modelData | ||
| 78 | |||
| 79 | onCurrentTextChanged: displaymanager.currentText = currentText | ||
| 80 | Connections { | ||
| 81 | target: displaymanager | ||
| 82 | function onCurrentTextChanged() { surfaceContent.currentText = displaymanager.currentText; } | ||
| 83 | function onMessagesChanged() { surfaceContent.messages = Array.from(displaymanager.messages); } | ||
| 84 | function onResponseRequiredChanged() { surfaceContent.responseRequired = displaymanager.responseRequired; } | ||
| 85 | function onResponseVisibleChanged() { surfaceContent.responseVisible = displaymanager.responseVisible; } | ||
| 86 | } | ||
| 87 | |||
| 88 | onResponse: responseText => Greetd.respond(responseText); | ||
| 89 | Connections { | ||
| 90 | target: Greetd | ||
| 91 | function onStateChanged() { | ||
| 92 | if (Greetd.state === GreetdState.Authenticating) { | ||
| 93 | surfaceContent.authRunning = true; | ||
| 94 | } else { | ||
| 95 | surfaceContent.authRunning = false; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | onAuthRunningChanged: { | ||
| 101 | if (surfaceContent.authRunning && Greetd.state !== GreetdState.Authenticating) | ||
| 102 | displaymanager.startAuth(); | ||
| 103 | } | ||
| 104 | Component.onCompleted: { | ||
| 105 | surfaceContent.authRunning = Greetd.state === GreetdState.Authenticating | ||
| 106 | surfaceContent.messages = Array.from(displaymanager.messages); | ||
| 107 | surfaceContent.responseVisible = displaymanager.responseVisible; | ||
| 108 | surfaceContent.responseRequired = displaymanager.responseRequired; | ||
| 109 | surfaceContent.currentText = displaymanager.currentText; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
diff --git a/accounts/gkleen@sif/shell/quickshell/shell.qml b/accounts/gkleen@sif/shell/quickshell/shell.qml new file mode 100644 index 00000000..fb8b16dc --- /dev/null +++ b/accounts/gkleen@sif/shell/quickshell/shell.qml | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | //@ pragma UseQApplication | ||
| 2 | |||
| 3 | import Quickshell | ||
| 4 | import Quickshell.Wayland | ||
| 5 | |||
| 6 | ShellRoot { | ||
| 7 | settings.watchFiles: false | ||
| 8 | |||
| 9 | Variants { | ||
| 10 | model: Quickshell.screens | ||
| 11 | |||
| 12 | delegate: Scope { | ||
| 13 | id: screenScope | ||
| 14 | |||
| 15 | required property var modelData | ||
| 16 | |||
| 17 | PanelWindow { | ||
| 18 | id: bgWindow | ||
| 19 | |||
| 20 | screen: screenScope.modelData | ||
| 21 | |||
| 22 | WlrLayershell.layer: WlrLayer.Background | ||
| 23 | WlrLayershell.namespace: "background" | ||
| 24 | exclusionMode: ExclusionMode.Ignore | ||
| 25 | |||
| 26 | anchors.top: true | ||
| 27 | anchors.bottom: true | ||
| 28 | anchors.left: true | ||
| 29 | anchors.right: true | ||
| 30 | |||
| 31 | color: "black" | ||
| 32 | |||
| 33 | WallpaperBackground { | ||
| 34 | screen: bgWindow.screen.name | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | Bar { | ||
| 39 | modelData: screenScope.modelData | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | Lockscreen {} | ||
| 45 | NiriIdle {} | ||
| 46 | |||
| 47 | VolumeOSD {} | ||
| 48 | BrightnessOSD {} | ||
| 49 | |||
| 50 | NotificationDisplay {} | ||
| 51 | |||
| 52 | UnixIPC {} | ||
| 53 | } | ||
diff --git a/accounts/gkleen@sif/synadm/default.nix b/accounts/gkleen@sif/synadm/default.nix new file mode 100644 index 00000000..0a8e0d4c --- /dev/null +++ b/accounts/gkleen@sif/synadm/default.nix | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | { config, pkgs, ... }: | ||
| 2 | { | ||
| 3 | home.packages = with pkgs; [ synadm ]; | ||
| 4 | sops.secrets."synadm.yaml" = { | ||
| 5 | format = "binary"; | ||
| 6 | sopsFile = ./synadm_yaml; | ||
| 7 | path = config.xdg.configHome + "/synadm.yaml"; | ||
| 8 | }; | ||
| 9 | } | ||
diff --git a/accounts/gkleen@sif/synadm/synadm_yaml b/accounts/gkleen@sif/synadm/synadm_yaml new file mode 100644 index 00000000..8d951ccc --- /dev/null +++ b/accounts/gkleen@sif/synadm/synadm_yaml | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:qJy4Pmbbxja4jmW7OaHsD0mQZ7anZwLhiVmAgkavb+CqwWGDnUBXdz22/MHCbxng5NshcFSpBoCBhgY6B9V2bUiES6bH9AtMlDcs9ebKGMArBTUTnQ2MjWQGfQTqraWdNgy+n327uj9swwCH8EZXdYH/Hlv0t/re470W+VOHeXhGghQ3Y9IGz2sgfvMGr8QxaJNydZz85rgs5QUP/PglCwWIOw2mY1EX2vYwnmiAo49LmIEaxWvRi++KHaeBveDt0nlkJwzUlipL2VOKWxkgpK3yGucQn2mz+FRe1btp+4KGm8H17eUI9FO9sBwq,iv:kgM921ovwCgDYHQj3c5Rupy/8JxHehxUD2jb1k9Ik2Y=,tag:3TLQkJbv679VWy8V2TMugw==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6bzVHUGNxZTF2WC9MYmZr\neGdVVzJXN3lGdEk3cTBER3J6UTFtcUJna2d3CjdNQmRXd2haZW1MYlJzNkk1dWVD\nVTFQc2gvS0JrejJ6SFh2MXpPWDZpRE0KLS0tIE0wTC85bEpvSnlGdGFkZVFhNjFZ\nbzRiZkxMWUg2ODNVUlBmNFlPNGRrZlkK1VXLJWcssv3ETyZSSM/Hhn5VIaI9iov9\nzShZA9Zx/FX6PYTuUMC29pJ57gKourcIxa/7HwSv/xYn1A6WcYfgSg==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | } | ||
| 9 | ], | ||
| 10 | "lastmodified": "2025-05-18T11:03:42Z", | ||
| 11 | "mac": "ENC[AES256_GCM,data:yonJC68PhilAgEHNNJQ8nO53Qo3rx/LnfiOWfuMm24bOUIH9QM3WZZxpigd7bHI4eC4TqRb4LvcSi0nEURTRAhwiTqGNrWbpw2Iv3n5dhLEN9aTcetG5ZuhaXqfVUoML45/ovdBZG/0l8+XIHqxN2M/g/h4JwKoR/6lqzcrVhgo=,iv:xvxBJwy+E5zUdjhGPdZPdy7tnBIEj50hfiDJFsS3wNg=,tag:L4Fas36ZOg4h0QQwC4gjNA==,type:str]", | ||
| 12 | "unencrypted_suffix": "_unencrypted", | ||
| 13 | "version": "3.10.2" | ||
| 14 | } | ||
| 15 | } | ||
diff --git a/accounts/gkleen@sif/systemd.nix b/accounts/gkleen@sif/systemd.nix index 2237b708..e601b49c 100644 --- a/accounts/gkleen@sif/systemd.nix +++ b/accounts/gkleen@sif/systemd.nix | |||
| @@ -205,11 +205,6 @@ in { | |||
| 205 | StartLimitBurst = 7; | 205 | StartLimitBurst = 7; |
| 206 | }; | 206 | }; |
| 207 | }; | 207 | }; |
| 208 | swayidle = { | ||
| 209 | Service = { | ||
| 210 | RuntimeDirectory = "swayidle"; | ||
| 211 | }; | ||
| 212 | }; | ||
| 213 | psi-notify = { | 208 | psi-notify = { |
| 214 | Install = { | 209 | Install = { |
| 215 | WantedBy = ["graphical-session.target"]; | 210 | WantedBy = ["graphical-session.target"]; |
| @@ -242,7 +237,7 @@ in { | |||
| 242 | "-${lib.getExe pkgs.playerctl} -a pause" | 237 | "-${lib.getExe pkgs.playerctl} -a pause" |
| 243 | "-${lib.getExe (pkgs.writeShellApplication { | 238 | "-${lib.getExe (pkgs.writeShellApplication { |
| 244 | name = "generate-css"; | 239 | name = "generate-css"; |
| 245 | runtimeInputs = with pkgs; [cfg.programs.wpaperd.package jq coreutils imagemagick findutils]; | 240 | runtimeInputs = with pkgs; [cfg.services.wpaperd.package jq coreutils imagemagick findutils]; |
| 246 | text = '' | 241 | text = '' |
| 247 | declare -A monitors | 242 | declare -A monitors |
| 248 | monitors=() | 243 | monitors=() |
| @@ -333,26 +328,24 @@ in { | |||
| 333 | ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\""; | 328 | ExecStopPost = "${pkgs.coreutils}/bin/rm -rfv \"$CACHE_DIRECTORY\""; |
| 334 | }; | 329 | }; |
| 335 | }; | 330 | }; |
| 336 | wpaperd = { | 331 | # wpaperd = { |
| 337 | Install = { | 332 | # Install = { |
| 338 | WantedBy = ["graphical-session.target"]; | 333 | # WantedBy = ["graphical-session.target"]; |
| 339 | }; | 334 | # }; |
| 340 | Unit = { | 335 | # Unit = { |
| 341 | After = [ "graphical-session.target" ]; | 336 | # After = [ "graphical-session.target" ]; |
| 342 | PartOf = [ "graphical-session.target" ]; | 337 | # PartOf = [ "graphical-session.target" ]; |
| 343 | }; | 338 | # }; |
| 344 | Service = { | 339 | # Service = { |
| 345 | ExecStart = lib.getExe cfg.programs.wpaperd.package; | 340 | # ExecStart = lib.getExe cfg.services.wpaperd.package; |
| 346 | Type = "simple"; | 341 | # Type = "simple"; |
| 347 | Restart = "always"; | 342 | # Restart = "always"; |
| 348 | RestartSec = "2s"; | 343 | # RestartSec = "2s"; |
| 349 | }; | 344 | # }; |
| 350 | }; | 345 | # }; |
| 351 | xembed-sni-proxy = { | 346 | xembed-sni-proxy = { |
| 352 | Unit = { | 347 | Unit = { |
| 353 | PartOf = lib.mkForce ["tray.target"]; | 348 | PartOf = lib.mkForce ["tray.target"]; |
| 354 | BindsTo = ["xwayland-satellite.service"]; | ||
| 355 | After = ["xwayland-satellite.service"]; | ||
| 356 | }; | 349 | }; |
| 357 | }; | 350 | }; |
| 358 | poweralertd = { | 351 | poweralertd = { |
| @@ -385,6 +378,8 @@ in { | |||
| 385 | }; | 378 | }; |
| 386 | Service = { | 379 | Service = { |
| 387 | ExecStart = "${config.systemd.package}/lib/systemd/systemd-socket-proxyd --exit-idle-time=60s 127.0.0.1:${toString (port + 1)}"; | 380 | ExecStart = "${config.systemd.package}/lib/systemd/systemd-socket-proxyd --exit-idle-time=60s 127.0.0.1:${toString (port + 1)}"; |
| 381 | Restart = "always"; | ||
| 382 | RestartSec = "23s"; | ||
| 388 | }; | 383 | }; |
| 389 | }) [{ host = "proxy.ssh.math.lmu.de"; port = 8118; } { host = "proxy.vidhar"; port = 8120; } { host = "proxy.mathw0h"; port = 8122; } { host = "proxy.mathw0e"; port = 8124; }]); | 384 | }) [{ host = "proxy.ssh.math.lmu.de"; port = 8118; } { host = "proxy.vidhar"; port = 8120; } { host = "proxy.mathw0h"; port = 8122; } { host = "proxy.mathw0e"; port = 8124; }]); |
| 390 | sockets = listToAttrs (map (port: nameValuePair "proxy-to-autossh-socks@${toString port}" { | 385 | sockets = listToAttrs (map (port: nameValuePair "proxy-to-autossh-socks@${toString port}" { |
| @@ -439,8 +434,8 @@ in { | |||
| 439 | tray = { | 434 | tray = { |
| 440 | Unit = { | 435 | Unit = { |
| 441 | PartOf = [ "graphical-session.target" ]; | 436 | PartOf = [ "graphical-session.target" ]; |
| 442 | Requires = [ "waybar.service" ]; | 437 | # Requires = [ "waybar.service" ]; |
| 443 | After = [ "graphical-session.target" "waybar.service" ]; | 438 | After = [ "graphical-session.target" ]; # "waybar.service" ]; |
| 444 | Wants = ["blueman-applet.service" "udiskie.service" "network-manager-applet.service"]; | 439 | Wants = ["blueman-applet.service" "udiskie.service" "network-manager-applet.service"]; |
| 445 | }; | 440 | }; |
| 446 | }; | 441 | }; |
diff --git a/accounts/gkleen@sif/utils/async-yt-dlp.nix b/accounts/gkleen@sif/utils/async-yt-dlp.nix new file mode 100644 index 00000000..c3b82ec5 --- /dev/null +++ b/accounts/gkleen@sif/utils/async-yt-dlp.nix | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | { writers, python3Packages, ... }: | ||
| 2 | |||
| 3 | writers.writePython3Bin "async-yt-dlp" { | ||
| 4 | libraries = with python3Packages; [ yt-dlp ]; | ||
| 5 | flakeIgnore = ["E501"]; | ||
| 6 | } '' | ||
| 7 | import sys | ||
| 8 | import os | ||
| 9 | |||
| 10 | import yt_dlp | ||
| 11 | import yt_dlp.options | ||
| 12 | from collections import namedtuple | ||
| 13 | import socket | ||
| 14 | from pathlib import Path | ||
| 15 | import json | ||
| 16 | |||
| 17 | create_parser = yt_dlp.options.create_parser | ||
| 18 | |||
| 19 | |||
| 20 | def parse_patched_options(opts): | ||
| 21 | patched_parser = create_parser() | ||
| 22 | patched_parser.defaults.update({ | ||
| 23 | 'ignoreerrors': False, | ||
| 24 | 'retries': 0, | ||
| 25 | 'fragment_retries': 0, | ||
| 26 | 'extract_flat': False, | ||
| 27 | 'concat_playlist': 'never', | ||
| 28 | }) | ||
| 29 | yt_dlp.options.create_parser = lambda: patched_parser | ||
| 30 | try: | ||
| 31 | return yt_dlp.parse_options(opts) | ||
| 32 | finally: | ||
| 33 | yt_dlp.options.create_parser = create_parser | ||
| 34 | |||
| 35 | |||
| 36 | default_opts = parse_patched_options([]).ydl_opts | ||
| 37 | |||
| 38 | |||
| 39 | def cli_to_api(opts): | ||
| 40 | opts = parse_patched_options(opts) | ||
| 41 | urls = opts.urls | ||
| 42 | opts = opts.ydl_opts | ||
| 43 | |||
| 44 | diff = {k: v for k, v in opts.items() if default_opts[k] != v} | ||
| 45 | if 'postprocessors' in diff: | ||
| 46 | diff['postprocessors'] = [pp for pp in diff['postprocessors'] | ||
| 47 | if pp not in default_opts['postprocessors']] | ||
| 48 | return namedtuple('Options', ('params', 'urls'))(diff, urls) | ||
| 49 | |||
| 50 | |||
| 51 | if __name__ == '__main__': | ||
| 52 | opts = cli_to_api(sys.argv[1:]) | ||
| 53 | with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as sock: | ||
| 54 | sock.connect(str(Path(os.environ["XDG_RUNTIME_DIR"]) / "yt-dlp.sock").encode('utf-8')) | ||
| 55 | with sock.makefile(mode='w', buffering=1, encoding='utf-8') as fh: | ||
| 56 | json.dump(opts._asdict(), fh) | ||
| 57 | '' | ||
diff --git a/accounts/gkleen@sif/utils/pdf2pdf.nix b/accounts/gkleen@sif/utils/pdf2pdf.nix new file mode 100644 index 00000000..9f4cbc3e --- /dev/null +++ b/accounts/gkleen@sif/utils/pdf2pdf.nix | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | pkgs@{ lib, resholve, zsh, ghostscript_headless, ... }: | ||
| 2 | |||
| 3 | resholve.writeScriptBin "pdf2pdf" { | ||
| 4 | inputs = with pkgs; [ghostscript_headless]; | ||
| 5 | interpreter = lib.getExe zsh; | ||
| 6 | } '' | ||
| 7 | exec gs -dPDFSETTINGS=/prepress -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -dSAFER -dPreserveAnnots=false "-sOutputFile=''${2}" "''${1}" | ||
| 8 | '' | ||
diff --git a/accounts/gkleen@sif/utils/sieve-edit.nix b/accounts/gkleen@sif/utils/sieve-edit.nix new file mode 100644 index 00000000..f985a3f6 --- /dev/null +++ b/accounts/gkleen@sif/utils/sieve-edit.nix | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | pkgs@{ lib, resholve, zsh, sieve-connect, sops, ... }: | ||
| 2 | |||
| 3 | resholve.writeScriptBin "sieve-edit" { | ||
| 4 | inputs = with pkgs; [sieve-connect sops]; | ||
| 5 | interpreter = lib.getExe zsh; | ||
| 6 | execer = with pkgs; [ | ||
| 7 | "cannot:${lib.getExe sieve-connect}" | ||
| 8 | "cannot:${lib.getExe sops}" | ||
| 9 | ]; | ||
| 10 | } '' | ||
| 11 | host=$1; shift | ||
| 12 | case "$host" in | ||
| 13 | surtr) | ||
| 14 | sieve-connect -s surtr.yggdrasil.li -m EXTERNAL --clientkey <(sops decrypt $HOME/projects/machines/hosts/surtr/email/ca/gkleen@sif.key) --clientcert $HOME/projects/machines/hosts/surtr/email/ca/gkleen@sif.crt --edit --remotesieve sieve | ||
| 15 | ;; | ||
| 16 | ymir) | ||
| 17 | sieve-connect -s ymir.yggdrasil.li -u gkleen --edit --remotesieve sieve | ||
| 18 | ;; | ||
| 19 | *) | ||
| 20 | echo "Unknown host: ‘$host’" >&2 | ||
| 21 | return 2 | ||
| 22 | ;; | ||
| 23 | esac | ||
| 24 | '' | ||
diff --git a/accounts/gkleen@sif/zshrc b/accounts/gkleen@sif/zshrc index c628e2e9..702990c3 100644 --- a/accounts/gkleen@sif/zshrc +++ b/accounts/gkleen@sif/zshrc | |||
| @@ -2,17 +2,14 @@ dir() { | |||
| 2 | curlArchive=false | 2 | curlArchive=false |
| 3 | templateArchive="" | 3 | templateArchive="" |
| 4 | repoUrl="" | 4 | repoUrl="" |
| 5 | nixShell="" | ||
| 6 | findNix=false | ||
| 7 | dir="" | 5 | dir="" |
| 8 | forceShell=false | 6 | forceShell=false |
| 9 | wormhole=false | 7 | wormhole=false |
| 10 | gitWorktree="" | 8 | gitWorktree="" |
| 11 | # notmuchMsg="" | ||
| 12 | quickserve=false | ||
| 13 | modifyPDF="" | 9 | modifyPDF="" |
| 10 | miniserve=false | ||
| 14 | 11 | ||
| 15 | while getopts ':t:a:s:Sd:ir:wqg:p:' arg; do | 12 | while getopts ':t:a:d:ir:wg:p:m' arg; do |
| 16 | case $arg in | 13 | case $arg in |
| 17 | "t") ;; | 14 | "t") ;; |
| 18 | "a") | 15 | "a") |
| @@ -23,16 +20,13 @@ dir() { | |||
| 23 | templateArchive=${OPTARG:a} | 20 | templateArchive=${OPTARG:a} |
| 24 | fi | 21 | fi |
| 25 | ;; | 22 | ;; |
| 26 | "s") nixShell=${OPTARG:a} ;; | ||
| 27 | "S") findNix=true ;; | ||
| 28 | "d") dir=${OPTARG} ;; | 23 | "d") dir=${OPTARG} ;; |
| 29 | "i") forceShell=true ;; | 24 | "i") forceShell=true ;; |
| 30 | "r") repoUrl=${OPTARG} ;; | 25 | "r") repoUrl=${OPTARG} ;; |
| 31 | "w") wormhole=true ;; | 26 | "w") wormhole=true ;; |
| 32 | "g") gitWorktree=${OPTARG} ;; | 27 | "g") gitWorktree=${OPTARG} ;; |
| 33 | # "n") notmuchMsg=${OPTARG} ;; | ||
| 34 | "q") quickserve=true ;; | ||
| 35 | "p") modifyPDF=${OPTARG:a} ;; | 28 | "p") modifyPDF=${OPTARG:a} ;; |
| 29 | "m") miniserve=true ;; | ||
| 36 | *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;; | 30 | *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;; |
| 37 | esac | 31 | esac |
| 38 | done | 32 | done |
| @@ -56,20 +50,34 @@ dir() { | |||
| 56 | gitWorktree="" | 50 | gitWorktree="" |
| 57 | fi | 51 | fi |
| 58 | 52 | ||
| 53 | miniservePIDFile="" | ||
| 54 | if [[ ${miniserve} = "true" ]]; then | ||
| 55 | miniservePIDFile=$(mktemp --tmpdir --suffix=.pid) | ||
| 56 | fi | ||
| 57 | |||
| 59 | cleanup() | 58 | cleanup() |
| 60 | { | 59 | { |
| 61 | cd ${modifyPDF:h} | 60 | if [[ -n ${modifyPDF} ]]; then |
| 62 | [[ -n ${modifyPDF} ]] && nix shell 'nixos#imagemagick' -c convert -verbose ${dir}/${modifyPDF:t:r}_*.png(on) ${modifyPDF} | 61 | cd ${modifyPDF:h} |
| 62 | typeset -a pages | ||
| 63 | eval 'pages=(${dir}/${modifyPDF:t:r}_*.png(on))' | ||
| 64 | magick -verbose "$pages" ${modifyPDF} | ||
| 65 | modifyPDF="" | ||
| 66 | fi | ||
| 67 | if [[ -n ${miniservePIDFile} ]]; then | ||
| 68 | command kill --verbose -- $(cat ${miniservePIDFile}) && wait $(cat ${miniservePIDFile}) | ||
| 69 | miniservePIDFile="" | ||
| 70 | fi | ||
| 63 | } | 71 | } |
| 64 | 72 | ||
| 65 | ( | 73 | ( |
| 74 | set -o localoptions -o localtraps | ||
| 75 | trap 'return 1' INT TERM | ||
| 66 | trap cleanup EXIT | 76 | trap cleanup EXIT |
| 67 | 77 | ||
| 68 | cd ${dir} | 78 | cd ${dir} |
| 69 | export dir; | 79 | export dir; |
| 70 | 80 | ||
| 71 | ${findNix} && { nixShell=$(findNix) || return $? } | ||
| 72 | |||
| 73 | [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} . | 81 | [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} . |
| 74 | 82 | ||
| 75 | [[ -n ${modifyPDF} ]] && templateArchive=${modifyPDF} | 83 | [[ -n ${modifyPDF} ]] && templateArchive=${modifyPDF} |
| @@ -82,23 +90,23 @@ dir() { | |||
| 82 | } | 90 | } |
| 83 | trap cleanup EXIT | 91 | trap cleanup EXIT |
| 84 | 92 | ||
| 85 | if ${curlArchive}; then | 93 | if [[ $curlArchive = "true" ]]; then |
| 86 | archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}") | 94 | archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}") |
| 87 | 95 | ||
| 88 | curl -L -o ${archiveFile} ${templateArchive} | 96 | curl -SfL -o ${archiveFile} ${templateArchive} |
| 89 | 97 | ||
| 90 | templateArchive=${archiveFile} | 98 | templateArchive=${archiveFile} |
| 91 | fi | 99 | fi |
| 92 | 100 | ||
| 93 | unpack=true | 101 | unpack=true |
| 94 | while ${unpack}; do | 102 | while [[ $unpack = "true" ]]; do |
| 95 | case $(file --brief --mime-type --dereference ${templateArchive}) in | 103 | case $(file --brief --mime-type --dereference ${templateArchive}) in |
| 96 | application/zip) | 104 | application/zip) |
| 97 | unzip ${templateArchive} | 105 | unzip ${templateArchive} |
| 98 | unpack=false | 106 | unpack=false |
| 99 | ;; | 107 | ;; |
| 100 | application/vnd.debian.binary-package) | 108 | application/vnd.debian.binary-package) |
| 101 | nix shell 'nixos#binutils' --command ar x ${templateArchive} | 109 | ar x ${templateArchive} |
| 102 | mkdir control data | 110 | mkdir control data |
| 103 | tar -C control -xvaf control.* | 111 | tar -C control -xvaf control.* |
| 104 | tar -C data -xvaf data.* | 112 | tar -C data -xvaf data.* |
| @@ -106,7 +114,7 @@ dir() { | |||
| 106 | ;; | 114 | ;; |
| 107 | application/x-rpm) | 115 | application/x-rpm) |
| 108 | cpioArchive=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t:r}.cpio") | 116 | cpioArchive=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t:r}.cpio") |
| 109 | nix shell 'nixos#busybox' --command rpm2cpio ${templateArchive} > ${cpioArchive} | 117 | rpm2cpio ${templateArchive} > ${cpioArchive} |
| 110 | templateArchive=${cpioArchive} | 118 | templateArchive=${cpioArchive} |
| 111 | unpack=true | 119 | unpack=true |
| 112 | ;; | 120 | ;; |
| @@ -115,15 +123,19 @@ dir() { | |||
| 115 | unpack=false | 123 | unpack=false |
| 116 | ;; | 124 | ;; |
| 117 | application/pdf) | 125 | application/pdf) |
| 118 | nix shell 'nixos#ghostscript' 'nixos#imagemagick' -c convert -verbose -density 400 ${templateArchive} ${modifyPDF:t:r}_%0d.png | 126 | magick -verbose -density 400 ${templateArchive} ${modifyPDF:t:r}_%0d.png |
| 119 | unpack=false | 127 | unpack=false |
| 120 | ;; | 128 | ;; |
| 121 | application/octet-stream) | 129 | application/octet-stream) |
| 122 | if [[ $(file --brief --dereferenc ${templateArchive}) =~ Squashfs ]]; then | 130 | if [[ $(file --brief --dereference ${templateArchive}) =~ Squashfs ]]; then |
| 123 | nix shell 'nixos#squashfsTools' -c unsquashfs -d . ${templateArchive} | 131 | unsquashfs -d . ${templateArchive} |
| 124 | unpack=false | 132 | unpack=false |
| 125 | fi | 133 | fi |
| 126 | ;; | 134 | ;; |
| 135 | application/x-iso9660-image) | ||
| 136 | 7z x ${templateArchive} | ||
| 137 | unpack=false | ||
| 138 | ;; | ||
| 127 | *) | 139 | *) |
| 128 | tar -xvaf ${templateArchive} | 140 | tar -xvaf ${templateArchive} |
| 129 | unpack=false | 141 | unpack=false |
| @@ -134,25 +146,21 @@ dir() { | |||
| 134 | fi | 146 | fi |
| 135 | 147 | ||
| 136 | 148 | ||
| 137 | ${wormhole} && wormhole receive --accept-file | 149 | [[ $wormhole = "true" ]] && wormhole receive --accept-file |
| 138 | 150 | ||
| 139 | 151 | ||
| 140 | if ${quickserve}; then | 152 | if [[ ${#@} -gt 0 ]]; then |
| 141 | quickserve --root . --upload . --show-hidden --tar gz | 153 | ${@} |
| 142 | fi | 154 | fi |
| 143 | 155 | ||
| 156 | cd $(pwd) # Needed for mounting to work | ||
| 144 | 157 | ||
| 145 | if [[ ${#@} -eq 0 ]] || ${forceShell}; then | 158 | if [[ ${miniserve} = "true" ]]; then |
| 146 | if [[ ${#@} -gt 0 ]]; then | 159 | miniserve --random-route --hidden --enable-tar-gz --enable-zip . & |
| 147 | if [[ -z ${nixShell} ]]; then | 160 | echo $! > "${miniservePIDFile}" |
| 148 | ${@} | 161 | fi |
| 149 | else | ||
| 150 | nix-shell ${nixShell} --run "${@}" | ||
| 151 | fi | ||
| 152 | fi | ||
| 153 | |||
| 154 | cd $(pwd) # Needed for mounting to work | ||
| 155 | 162 | ||
| 163 | if [[ ${#@} -eq 0 ]] && [[ ${miniserve} != "true" ]] || [[ $forceShell = "true" ]]; then | ||
| 156 | isSingleDir() { | 164 | isSingleDir() { |
| 157 | typeset -a contents | 165 | typeset -a contents |
| 158 | contents=(*(N) .*(N)) | 166 | contents=(*(N) .*(N)) |
| @@ -166,18 +174,9 @@ dir() { | |||
| 166 | } | 174 | } |
| 167 | while d=$(isSingleDir); do cd ${d}; done | 175 | while d=$(isSingleDir); do cd ${d}; done |
| 168 | 176 | ||
| 169 | 177 | zsh | |
| 170 | if [[ -z ${nixShell} ]]; then | 178 | elif [[ ${miniserve} == "true" ]]; then |
| 171 | zsh | 179 | wait $(cat "${miniservePIDFile}") |
| 172 | else | ||
| 173 | nix-shell ${nixShell} --run zsh | ||
| 174 | fi | ||
| 175 | else | ||
| 176 | if [[ -z ${nixShell} ]]; then | ||
| 177 | ${@} | ||
| 178 | else | ||
| 179 | nix-shell ${nixShell} --run "${@}" | ||
| 180 | fi | ||
| 181 | fi | 180 | fi |
| 182 | ) | 181 | ) |
| 183 | } | 182 | } |
| @@ -185,27 +184,30 @@ dir() { | |||
| 185 | tmpdir() { | 184 | tmpdir() { |
| 186 | cleanup() | 185 | cleanup() |
| 187 | { | 186 | { |
| 188 | cd / | 187 | cd / |
| 189 | unmount() { | 188 | unmount() { |
| 190 | printf "Unmounting %s\n" ${1} >&2 | 189 | printf "Unmounting %s\n" ${1} >&2 |
| 191 | fusermount -u ${1} || umount ${1} || sudo umount ${1} | 190 | fusermount -u ${1} || umount ${1} || sudo umount ${1} |
| 192 | } | 191 | } |
| 193 | 192 | ||
| 194 | if mountpoint -q -- ${dir}; then | 193 | if [[ -n ${dir} ]]; then |
| 195 | unmount ${dir} || return $? | 194 | if mountpoint -q -- ${dir}; then |
| 196 | else | 195 | unmount ${dir} || return $? |
| 197 | while read -d $'\0' subDir; do | 196 | else |
| 198 | mountpoint -q -- ${subDir} || continue | 197 | while read -d $'\0' subDir; do |
| 199 | unmount ${subDir} || return $? | 198 | mountpoint -q -- ${subDir} || continue |
| 200 | done <<<$(find ${dir} -xdev -type d -print0 | sort -zr) | 199 | unmount ${subDir} || return $? |
| 201 | fi | 200 | done <<<$(find ${dir} -xdev -type d -print0 | sort -zr) |
| 202 | 201 | fi | |
| 203 | rm -rfv --one-file-system -- ${dir} | 202 | |
| 203 | rm -rfv --one-file-system -- ${dir} | ||
| 204 | dir="" | ||
| 205 | fi | ||
| 204 | } | 206 | } |
| 205 | 207 | ||
| 206 | local tmpdir="" | 208 | local tmpdir="" |
| 207 | 209 | ||
| 208 | while getopts ':t:a:s:Sd:ir:wqg:p:' arg; do | 210 | while getopts ':t:a:d:ir:wg:p:m' arg; do |
| 209 | case $arg in | 211 | case $arg in |
| 210 | "t") tmpdir="=${OPTARG}" ;; | 212 | "t") tmpdir="=${OPTARG}" ;; |
| 211 | "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;; | 213 | "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;; |
| @@ -213,6 +215,8 @@ tmpdir() { | |||
| 213 | done | 215 | done |
| 214 | 216 | ||
| 215 | ( | 217 | ( |
| 218 | set -o localoptions -o localtraps | ||
| 219 | trap 'return 1' INT TERM | ||
| 216 | trap cleanup EXIT | 220 | trap cleanup EXIT |
| 217 | 221 | ||
| 218 | 222 | ||
| @@ -231,17 +235,7 @@ clock() { | |||
| 231 | } | 235 | } |
| 232 | 236 | ||
| 233 | public-ip() { | 237 | public-ip() { |
| 234 | curl -s -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip' | 238 | curl -sSf -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip' |
| 235 | } | ||
| 236 | |||
| 237 | nix-ghci() { | ||
| 238 | pkgExpr="" | ||
| 239 | if [[ ${#@} -gt 0 ]]; then | ||
| 240 | pkgExpr="${1}" | ||
| 241 | shift | ||
| 242 | fi | ||
| 243 | |||
| 244 | nix-shell -p "with (import <nixpkgs> {}); pkgs.haskellPackages.ghcWithPackages (p: with p; [${pkgExpr}])" --run "ghci ${@}" | ||
| 245 | } | 239 | } |
| 246 | 240 | ||
| 247 | swap() { | 241 | swap() { |
| @@ -271,14 +265,6 @@ l() { | |||
| 271 | ls --long --binary --git --time-style=iso --header $@ | 265 | ls --long --binary --git --time-style=iso --header $@ |
| 272 | } | 266 | } |
| 273 | 267 | ||
| 274 | re() { | ||
| 275 | systemctl restart $@ | ||
| 276 | } | ||
| 277 | |||
| 278 | ure() { | ||
| 279 | systemctl --user restart $@ | ||
| 280 | } | ||
| 281 | |||
| 282 | ssh-installer() { | 268 | ssh-installer() { |
| 283 | ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentityFile=~/.ssh/gkleen@sif.midgard.yggdrasil $@ | 269 | ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentityFile=~/.ssh/gkleen@sif.midgard.yggdrasil $@ |
| 284 | } | 270 | } |
diff --git a/accounts/gkleen@surtr.nix b/accounts/gkleen@surtr.nix index 58c4f21d..8f678ac9 100644 --- a/accounts/gkleen@surtr.nix +++ b/accounts/gkleen@surtr.nix | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | { userName, ... }: { | 1 | { flake, userName, ... }: { |
| 2 | home-manager.users.${userName}.home.stateVersion = "20.09"; | 2 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 3 | zsh tmux | ||
| 4 | ]; | ||
| 5 | |||
| 6 | config.home-manager.users.${userName}.home.stateVersion = "20.09"; | ||
| 3 | } | 7 | } |
diff --git a/accounts/gkleen@vidhar.nix b/accounts/gkleen@vidhar.nix index 8509c2f4..3a37c4bd 100644 --- a/accounts/gkleen@vidhar.nix +++ b/accounts/gkleen@vidhar.nix | |||
| @@ -1,4 +1,8 @@ | |||
| 1 | { flake, pkgs, userName, config, ... }: { | 1 | { flake, pkgs, userName, config, ... }: { |
| 2 | imports = with flake.nixosModules.userProfiles.${userName}; [ | ||
| 3 | zsh tmux | ||
| 4 | ]; | ||
| 5 | |||
| 2 | config = { | 6 | config = { |
| 3 | users.users.${userName} = { | 7 | users.users.${userName} = { |
| 4 | uid = 1000; | 8 | uid = 1000; |
diff --git a/accounts/mherold@eostre.nix b/accounts/mherold@eostre.nix index 51e4529a..0e2f37aa 100644 --- a/accounts/mherold@eostre.nix +++ b/accounts/mherold@eostre.nix | |||
| @@ -7,9 +7,9 @@ | |||
| 7 | home-manager.users.${userName} = { | 7 | home-manager.users.${userName} = { |
| 8 | home.stateVersion = "20.09"; | 8 | home.stateVersion = "20.09"; |
| 9 | 9 | ||
| 10 | nixpkgs.config = { | 10 | # nixpkgs.config = { |
| 11 | allowUnfree = true; | 11 | # allowUnfree = true; |
| 12 | }; | 12 | # }; |
| 13 | 13 | ||
| 14 | home.packages = with pkgs; [ | 14 | home.packages = with pkgs; [ |
| 15 | thunderbird libreoffice element-desktop keepassxc vlc | 15 | thunderbird libreoffice element-desktop keepassxc vlc |
diff --git a/accounts/root@installer.nix b/accounts/root@installer.nix index c7a418f8..5fe1db38 100644 --- a/accounts/root@installer.nix +++ b/accounts/root@installer.nix | |||
| @@ -1,7 +1,11 @@ | |||
| 1 | { userName, ... }: | 1 | { flake, userName, ... }: |
| 2 | 2 | ||
| 3 | { | 3 | { |
| 4 | home-manager.users.${userName} = { config, ... } : { | 4 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 5 | zsh tmux | ||
| 6 | ]; | ||
| 7 | |||
| 8 | config.home-manager.users.${userName} = { config, ... } : { | ||
| 5 | home.stateVersion = config.home.version.release; | 9 | home.stateVersion = config.home.version.release; |
| 6 | }; | 10 | }; |
| 7 | } | 11 | } |
diff --git a/accounts/root@sif.nix b/accounts/root@sif.nix index c9e129a0..bb816230 100644 --- a/accounts/root@sif.nix +++ b/accounts/root@sif.nix | |||
| @@ -1,6 +1,10 @@ | |||
| 1 | { userName, ... }: | 1 | { flake, userName, ... }: |
| 2 | { | 2 | { |
| 3 | home-manager.users.${userName} = { | 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 4 | zsh tmux | ||
| 5 | ]; | ||
| 6 | |||
| 7 | config.home-manager.users.${userName} = { | ||
| 4 | home.stateVersion = "20.09"; | 8 | home.stateVersion = "20.09"; |
| 5 | 9 | ||
| 6 | programs.ssh.matchBlocks = { | 10 | programs.ssh.matchBlocks = { |
diff --git a/accounts/root@surtr.nix b/accounts/root@surtr.nix index 58c4f21d..8f678ac9 100644 --- a/accounts/root@surtr.nix +++ b/accounts/root@surtr.nix | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | { userName, ... }: { | 1 | { flake, userName, ... }: { |
| 2 | home-manager.users.${userName}.home.stateVersion = "20.09"; | 2 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 3 | zsh tmux | ||
| 4 | ]; | ||
| 5 | |||
| 6 | config.home-manager.users.${userName}.home.stateVersion = "20.09"; | ||
| 3 | } | 7 | } |
diff --git a/accounts/root@vidhar.nix b/accounts/root@vidhar.nix index e82414a8..0fc56633 100644 --- a/accounts/root@vidhar.nix +++ b/accounts/root@vidhar.nix | |||
| @@ -1,6 +1,11 @@ | |||
| 1 | { config, userName, ... }: | 1 | { flake, config, userName, ... }: |
| 2 | |||
| 2 | { | 3 | { |
| 3 | home-manager.users.${userName} = { | 4 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 5 | zsh tmux | ||
| 6 | ]; | ||
| 7 | |||
| 8 | config.home-manager.users.${userName} = { | ||
| 4 | home.stateVersion = "20.09"; | 9 | home.stateVersion = "20.09"; |
| 5 | 10 | ||
| 6 | programs.ssh.matchBlocks = { | 11 | programs.ssh.matchBlocks = { |
| @@ -6,22 +6,28 @@ | |||
| 6 | "nixpkgs": [ | 6 | "nixpkgs": [ |
| 7 | "nixpkgs" | 7 | "nixpkgs" |
| 8 | ], | 8 | ], |
| 9 | "poetry2nix": [ | 9 | "pre-commit-hooks-nix": "pre-commit-hooks-nix", |
| 10 | "poetry2nix" | 10 | "pyproject-build-systems": [ |
| 11 | "pyproject-build-systems" | ||
| 11 | ], | 12 | ], |
| 12 | "pre-commit-hooks-nix": "pre-commit-hooks-nix" | 13 | "pyproject-nix": [ |
| 14 | "pyproject-nix" | ||
| 15 | ], | ||
| 16 | "uv2nix": [ | ||
| 17 | "uv2nix" | ||
| 18 | ] | ||
| 13 | }, | 19 | }, |
| 14 | "locked": { | 20 | "locked": { |
| 15 | "lastModified": 1723124245, | 21 | "lastModified": 1749560907, |
| 16 | "narHash": "sha256-ThDq7vOXo6G4+C5FHqUc64CeX2c5n36tln4ZlDao6s4=", | 22 | "narHash": "sha256-zvAxxnJ5dcnjbuog0W6UvlthD1dLm5t4ZQI25jzNDW4=", |
| 17 | "owner": "gkleen", | 23 | "owner": "gkleen", |
| 18 | "repo": "backup-utils", | 24 | "repo": "backup-utils", |
| 19 | "rev": "74e65090de63fc99f056098677cc490754e2708f", | 25 | "rev": "8ed6a1d7c2e337cb2e35ed68f6d852f0d1049908", |
| 20 | "type": "gitlab" | 26 | "type": "gitlab" |
| 21 | }, | 27 | }, |
| 22 | "original": { | 28 | "original": { |
| 23 | "owner": "gkleen", | 29 | "owner": "gkleen", |
| 24 | "ref": "v0.1.6", | 30 | "ref": "v0.1.7", |
| 25 | "repo": "backup-utils", | 31 | "repo": "backup-utils", |
| 26 | "type": "gitlab" | 32 | "type": "gitlab" |
| 27 | } | 33 | } |
| @@ -33,26 +39,45 @@ | |||
| 33 | "nixpkgs": [ | 39 | "nixpkgs": [ |
| 34 | "nixpkgs" | 40 | "nixpkgs" |
| 35 | ], | 41 | ], |
| 36 | "poetry2nix": [ | 42 | "pre-commit-hooks-nix": "pre-commit-hooks-nix_2", |
| 37 | "poetry2nix" | 43 | "pyproject-build-systems": "pyproject-build-systems", |
| 44 | "pyproject-nix": [ | ||
| 45 | "pyproject-nix" | ||
| 38 | ], | 46 | ], |
| 39 | "pre-commit-hooks-nix": "pre-commit-hooks-nix_2" | 47 | "uv2nix": [ |
| 48 | "uv2nix" | ||
| 49 | ] | ||
| 40 | }, | 50 | }, |
| 41 | "locked": { | 51 | "locked": { |
| 42 | "lastModified": 1734281899, | 52 | "lastModified": 1750599403, |
| 43 | "narHash": "sha256-9QdIl3sjHY4Xij9KrBUkW1KpLB+jyxlI12UHPitlawI=", | 53 | "narHash": "sha256-MLQ7CISl00w1xq88TL2wukNq3ukzID4u7BVT4okbUik=", |
| 44 | "owner": "gkleen", | 54 | "owner": "gkleen", |
| 45 | "repo": "ca", | 55 | "repo": "ca", |
| 46 | "rev": "1e4ee9d25a5282ef7bc6774072229784fa0036f3", | 56 | "rev": "505a29233ada969b2eca76d616b0d7a8767dfb71", |
| 47 | "type": "gitlab" | 57 | "type": "gitlab" |
| 48 | }, | 58 | }, |
| 49 | "original": { | 59 | "original": { |
| 50 | "owner": "gkleen", | 60 | "owner": "gkleen", |
| 51 | "ref": "v3.1.3", | 61 | "ref": "v3.1.5", |
| 52 | "repo": "ca", | 62 | "repo": "ca", |
| 53 | "type": "gitlab" | 63 | "type": "gitlab" |
| 54 | } | 64 | } |
| 55 | }, | 65 | }, |
| 66 | "crane": { | ||
| 67 | "locked": { | ||
| 68 | "lastModified": 1731098351, | ||
| 69 | "narHash": "sha256-HQkYvKvaLQqNa10KEFGgWHfMAbWBfFp+4cAgkut+NNE=", | ||
| 70 | "owner": "ipetkov", | ||
| 71 | "repo": "crane", | ||
| 72 | "rev": "ef80ead953c1b28316cc3f8613904edc2eb90c28", | ||
| 73 | "type": "github" | ||
| 74 | }, | ||
| 75 | "original": { | ||
| 76 | "owner": "ipetkov", | ||
| 77 | "repo": "crane", | ||
| 78 | "type": "github" | ||
| 79 | } | ||
| 80 | }, | ||
| 56 | "deploy-rs": { | 81 | "deploy-rs": { |
| 57 | "inputs": { | 82 | "inputs": { |
| 58 | "flake-compat": [ | 83 | "flake-compat": [ |
| @@ -66,11 +91,11 @@ | |||
| 66 | ] | 91 | ] |
| 67 | }, | 92 | }, |
| 68 | "locked": { | 93 | "locked": { |
| 69 | "lastModified": 1727447169, | 94 | "lastModified": 1749105467, |
| 70 | "narHash": "sha256-3KyjMPUKHkiWhwR91J1YchF6zb6gvckCAY1jOE+ne0U=", | 95 | "narHash": "sha256-hXh76y/wDl15almBcqvjryB50B0BaiXJKk20f314RoE=", |
| 71 | "owner": "serokell", | 96 | "owner": "serokell", |
| 72 | "repo": "deploy-rs", | 97 | "repo": "deploy-rs", |
| 73 | "rev": "aa07eb05537d4cd025e2310397a6adcedfe72c76", | 98 | "rev": "6bc76b872374845ba9d645a2f012b764fecd765f", |
| 74 | "type": "github" | 99 | "type": "github" |
| 75 | }, | 100 | }, |
| 76 | "original": { | 101 | "original": { |
| @@ -115,11 +140,11 @@ | |||
| 115 | "flake-compat_3": { | 140 | "flake-compat_3": { |
| 116 | "flake": false, | 141 | "flake": false, |
| 117 | "locked": { | 142 | "locked": { |
| 118 | "lastModified": 1733328505, | 143 | "lastModified": 1747046372, |
| 119 | "narHash": "sha256-NeCCThCEP3eCl2l/+27kNNK7QrwZB1IJCrXfrbv5oqU=", | 144 | "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", |
| 120 | "owner": "edolstra", | 145 | "owner": "edolstra", |
| 121 | "repo": "flake-compat", | 146 | "repo": "flake-compat", |
| 122 | "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", | 147 | "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", |
| 123 | "type": "github" | 148 | "type": "github" |
| 124 | }, | 149 | }, |
| 125 | "original": { | 150 | "original": { |
| @@ -132,6 +157,22 @@ | |||
| 132 | "flake-compat_4": { | 157 | "flake-compat_4": { |
| 133 | "flake": false, | 158 | "flake": false, |
| 134 | "locked": { | 159 | "locked": { |
| 160 | "lastModified": 1696426674, | ||
| 161 | "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", | ||
| 162 | "owner": "edolstra", | ||
| 163 | "repo": "flake-compat", | ||
| 164 | "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", | ||
| 165 | "type": "github" | ||
| 166 | }, | ||
| 167 | "original": { | ||
| 168 | "owner": "edolstra", | ||
| 169 | "repo": "flake-compat", | ||
| 170 | "type": "github" | ||
| 171 | } | ||
| 172 | }, | ||
| 173 | "flake-compat_5": { | ||
| 174 | "flake": false, | ||
| 175 | "locked": { | ||
| 135 | "lastModified": 1673956053, | 176 | "lastModified": 1673956053, |
| 136 | "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", | 177 | "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", |
| 137 | "owner": "edolstra", | 178 | "owner": "edolstra", |
| @@ -168,11 +209,11 @@ | |||
| 168 | "nixpkgs-lib": "nixpkgs-lib_2" | 209 | "nixpkgs-lib": "nixpkgs-lib_2" |
| 169 | }, | 210 | }, |
| 170 | "locked": { | 211 | "locked": { |
| 171 | "lastModified": 1733312601, | 212 | "lastModified": 1749398372, |
| 172 | "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", | 213 | "narHash": "sha256-tYBdgS56eXYaWVW3fsnPQ/nFlgWi/Z2Ymhyu21zVM98=", |
| 173 | "owner": "hercules-ci", | 214 | "owner": "hercules-ci", |
| 174 | "repo": "flake-parts", | 215 | "repo": "flake-parts", |
| 175 | "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", | 216 | "rev": "9305fe4e5c2a6fcf5ba6a3ff155720fbe4076569", |
| 176 | "type": "github" | 217 | "type": "github" |
| 177 | }, | 218 | }, |
| 178 | "original": { | 219 | "original": { |
| @@ -183,6 +224,27 @@ | |||
| 183 | }, | 224 | }, |
| 184 | "flake-parts_3": { | 225 | "flake-parts_3": { |
| 185 | "inputs": { | 226 | "inputs": { |
| 227 | "nixpkgs-lib": [ | ||
| 228 | "lanzaboote", | ||
| 229 | "nixpkgs" | ||
| 230 | ] | ||
| 231 | }, | ||
| 232 | "locked": { | ||
| 233 | "lastModified": 1730504689, | ||
| 234 | "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", | ||
| 235 | "owner": "hercules-ci", | ||
| 236 | "repo": "flake-parts", | ||
| 237 | "rev": "506278e768c2a08bec68eb62932193e341f55c90", | ||
| 238 | "type": "github" | ||
| 239 | }, | ||
| 240 | "original": { | ||
| 241 | "owner": "hercules-ci", | ||
| 242 | "repo": "flake-parts", | ||
| 243 | "type": "github" | ||
| 244 | } | ||
| 245 | }, | ||
| 246 | "flake-parts_4": { | ||
| 247 | "inputs": { | ||
| 186 | "nixpkgs-lib": "nixpkgs-lib_3" | 248 | "nixpkgs-lib": "nixpkgs-lib_3" |
| 187 | }, | 249 | }, |
| 188 | "locked": { | 250 | "locked": { |
| @@ -202,11 +264,11 @@ | |||
| 202 | "flake-registry": { | 264 | "flake-registry": { |
| 203 | "flake": false, | 265 | "flake": false, |
| 204 | "locked": { | 266 | "locked": { |
| 205 | "lastModified": 1734450202, | 267 | "lastModified": 1744623129, |
| 206 | "narHash": "sha256-/3gigrEBFORQs6a8LL5twoHs7biu08y/8Xc5aQmk3b0=", | 268 | "narHash": "sha256-nlQTQrHqM+ywXN0evDXnYEV6z6WWZB5BFQ2TkXsduKw=", |
| 207 | "owner": "NixOS", | 269 | "owner": "NixOS", |
| 208 | "repo": "flake-registry", | 270 | "repo": "flake-registry", |
| 209 | "rev": "02fe640c9e117dd9d6a34efc7bcb8bd09c08111d", | 271 | "rev": "1322f33d5836ae757d2e6190239252cf8402acf6", |
| 210 | "type": "github" | 272 | "type": "github" |
| 211 | }, | 273 | }, |
| 212 | "original": { | 274 | "original": { |
| @@ -296,6 +358,28 @@ | |||
| 296 | "gitignore_3": { | 358 | "gitignore_3": { |
| 297 | "inputs": { | 359 | "inputs": { |
| 298 | "nixpkgs": [ | 360 | "nixpkgs": [ |
| 361 | "lanzaboote", | ||
| 362 | "pre-commit-hooks-nix", | ||
| 363 | "nixpkgs" | ||
| 364 | ] | ||
| 365 | }, | ||
| 366 | "locked": { | ||
| 367 | "lastModified": 1709087332, | ||
| 368 | "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", | ||
| 369 | "owner": "hercules-ci", | ||
| 370 | "repo": "gitignore.nix", | ||
| 371 | "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", | ||
| 372 | "type": "github" | ||
| 373 | }, | ||
| 374 | "original": { | ||
| 375 | "owner": "hercules-ci", | ||
| 376 | "repo": "gitignore.nix", | ||
| 377 | "type": "github" | ||
| 378 | } | ||
| 379 | }, | ||
| 380 | "gitignore_4": { | ||
| 381 | "inputs": { | ||
| 382 | "nixpkgs": [ | ||
| 299 | "prometheus-borg-exporter", | 383 | "prometheus-borg-exporter", |
| 300 | "pre-commit-hooks-nix", | 384 | "pre-commit-hooks-nix", |
| 301 | "nixpkgs" | 385 | "nixpkgs" |
| @@ -322,11 +406,11 @@ | |||
| 322 | ] | 406 | ] |
| 323 | }, | 407 | }, |
| 324 | "locked": { | 408 | "locked": { |
| 325 | "lastModified": 1738691953, | 409 | "lastModified": 1753177987, |
| 326 | "narHash": "sha256-JY/w2Xyrj3mhUhLJkSgk8t7MSf3LGZjewPTQ7QtCbHE=", | 410 | "narHash": "sha256-PkCc+YTrl0A/H6EV09DCr5yZpvQZ9DkuFXj/NNaEvHs=", |
| 327 | "owner": "gkleen", | 411 | "owner": "gkleen", |
| 328 | "repo": "home-manager", | 412 | "repo": "home-manager", |
| 329 | "rev": "c077fc8684289ab1b1c2231bab1566879e099c97", | 413 | "rev": "b493410fc6e427129a1caee8f50970d152a27daa", |
| 330 | "type": "github" | 414 | "type": "github" |
| 331 | }, | 415 | }, |
| 332 | "original": { | 416 | "original": { |
| @@ -343,16 +427,16 @@ | |||
| 343 | ] | 427 | ] |
| 344 | }, | 428 | }, |
| 345 | "locked": { | 429 | "locked": { |
| 346 | "lastModified": 1710245356, | 430 | "lastModified": 1749562430, |
| 347 | "narHash": "sha256-8cQGUn+N1dTgklMWMejSLN2q8Oz+7Rnqsfaw2rt3bU4=", | 431 | "narHash": "sha256-M5MqsIsf+o7yngakVUW4poBGZaghB6sUpw7SsWA55kU=", |
| 348 | "owner": "gkleen", | 432 | "owner": "gkleen", |
| 349 | "repo": "home-manager", | 433 | "repo": "home-manager", |
| 350 | "rev": "a14fe0c27d04dfa3d80abe2db743e9a7f4f2a33d", | 434 | "rev": "dca5a2df9f8a00cc34bea6ead249a8446f5f069e", |
| 351 | "type": "github" | 435 | "type": "github" |
| 352 | }, | 436 | }, |
| 353 | "original": { | 437 | "original": { |
| 354 | "owner": "gkleen", | 438 | "owner": "gkleen", |
| 355 | "ref": "nixos-late-start-23.11", | 439 | "ref": "nixos-late-start-25.05", |
| 356 | "repo": "home-manager", | 440 | "repo": "home-manager", |
| 357 | "type": "github" | 441 | "type": "github" |
| 358 | } | 442 | } |
| @@ -373,10 +457,36 @@ | |||
| 373 | "type": "github" | 457 | "type": "github" |
| 374 | } | 458 | } |
| 375 | }, | 459 | }, |
| 460 | "lanzaboote": { | ||
| 461 | "inputs": { | ||
| 462 | "crane": "crane", | ||
| 463 | "flake-compat": "flake-compat_4", | ||
| 464 | "flake-parts": "flake-parts_3", | ||
| 465 | "nixpkgs": [ | ||
| 466 | "nixpkgs" | ||
| 467 | ], | ||
| 468 | "pre-commit-hooks-nix": "pre-commit-hooks-nix_3", | ||
| 469 | "rust-overlay": "rust-overlay" | ||
| 470 | }, | ||
| 471 | "locked": { | ||
| 472 | "lastModified": 1737639419, | ||
| 473 | "narHash": "sha256-AEEDktApTEZ5PZXNDkry2YV2k6t0dTgLPEmAZbnigXU=", | ||
| 474 | "owner": "nix-community", | ||
| 475 | "repo": "lanzaboote", | ||
| 476 | "rev": "a65905a09e2c43ff63be8c0e86a93712361f871e", | ||
| 477 | "type": "github" | ||
| 478 | }, | ||
| 479 | "original": { | ||
| 480 | "owner": "nix-community", | ||
| 481 | "ref": "v0.4.2", | ||
| 482 | "repo": "lanzaboote", | ||
| 483 | "type": "github" | ||
| 484 | } | ||
| 485 | }, | ||
| 376 | "leapseconds": { | 486 | "leapseconds": { |
| 377 | "flake": false, | 487 | "flake": false, |
| 378 | "locked": { | 488 | "locked": { |
| 379 | "narHash": "sha256-5ZaoY/bScQS7EGJRHu6vj9XWhbObmxNEaGugaGU7+lg=", | 489 | "narHash": "sha256-FJgbafPB48+5sT+7ZB8pajSsfJoISEOoaJ0d/2Ya7o8=", |
| 380 | "type": "file", | 490 | "type": "file", |
| 381 | "url": "https://data.iana.org/time-zones/tzdb/leap-seconds.list" | 491 | "url": "https://data.iana.org/time-zones/tzdb/leap-seconds.list" |
| 382 | }, | 492 | }, |
| @@ -392,16 +502,16 @@ | |||
| 392 | "nixpkgs": [ | 502 | "nixpkgs": [ |
| 393 | "nixpkgs" | 503 | "nixpkgs" |
| 394 | ], | 504 | ], |
| 395 | "nixpkgs-stable": "nixpkgs-stable_2", | 505 | "nixpkgs-stable": "nixpkgs-stable_3", |
| 396 | "xwayland-satellite-stable": "xwayland-satellite-stable", | 506 | "xwayland-satellite-stable": "xwayland-satellite-stable", |
| 397 | "xwayland-satellite-unstable": "xwayland-satellite-unstable" | 507 | "xwayland-satellite-unstable": "xwayland-satellite-unstable" |
| 398 | }, | 508 | }, |
| 399 | "locked": { | 509 | "locked": { |
| 400 | "lastModified": 1743186084, | 510 | "lastModified": 1757437545, |
| 401 | "narHash": "sha256-zEPpazywKQLIRWxeTBFnSnacSnGIqM2Qr1iJ5qB94IQ=", | 511 | "narHash": "sha256-7ssbrFnmSrqtCtOySiu5ncyOBxPrR6p2nhNHrg6D+fo=", |
| 402 | "owner": "sodiboo", | 512 | "owner": "sodiboo", |
| 403 | "repo": "niri-flake", | 513 | "repo": "niri-flake", |
| 404 | "rev": "111a3afc23fdf64fd76115df48c2e6571fee51b7", | 514 | "rev": "ef694b996daeeb8684c0adfaa9b7067a6e709054", |
| 405 | "type": "github" | 515 | "type": "github" |
| 406 | }, | 516 | }, |
| 407 | "original": { | 517 | "original": { |
| @@ -414,16 +524,16 @@ | |||
| 414 | "niri-stable": { | 524 | "niri-stable": { |
| 415 | "flake": false, | 525 | "flake": false, |
| 416 | "locked": { | 526 | "locked": { |
| 417 | "lastModified": 1740117926, | 527 | "lastModified": 1756556321, |
| 418 | "narHash": "sha256-mTTHA0RAaQcdYe+9A3Jx77cmmyLFHmRoZdd8RpWa+m8=", | 528 | "narHash": "sha256-RLD89dfjN0RVO86C/Mot0T7aduCygPGaYbog566F0Qo=", |
| 419 | "owner": "YaLTeR", | 529 | "owner": "YaLTeR", |
| 420 | "repo": "niri", | 530 | "repo": "niri", |
| 421 | "rev": "b94a5db8790339cf9134873d8b490be69e02ac71", | 531 | "rev": "01be0e65f4eb91a9cd624ac0b76aaeab765c7294", |
| 422 | "type": "github" | 532 | "type": "github" |
| 423 | }, | 533 | }, |
| 424 | "original": { | 534 | "original": { |
| 425 | "owner": "YaLTeR", | 535 | "owner": "YaLTeR", |
| 426 | "ref": "v25.02", | 536 | "ref": "v25.08", |
| 427 | "repo": "niri", | 537 | "repo": "niri", |
| 428 | "type": "github" | 538 | "type": "github" |
| 429 | } | 539 | } |
| @@ -431,15 +541,16 @@ | |||
| 431 | "niri-unstable": { | 541 | "niri-unstable": { |
| 432 | "flake": false, | 542 | "flake": false, |
| 433 | "locked": { | 543 | "locked": { |
| 434 | "lastModified": 1743017820, | 544 | "lastModified": 1757671534, |
| 435 | "narHash": "sha256-EYq2NfWSSO87O7hLrJLPPt0VbgvSjgPzru+58LQ29WI=", | 545 | "narHash": "sha256-7tfypHWNtR+wZS9K9XrvcUwyvZ3h8CxInQ2mVsjUU9A=", |
| 436 | "owner": "YaLTeR", | 546 | "owner": "gkleen", |
| 437 | "repo": "niri", | 547 | "repo": "niri", |
| 438 | "rev": "7cfecf4b1b9b8c11c80061fb31926f888228499d", | 548 | "rev": "5e3611a3c5f8c819e5517d0b3f795f161579a0db", |
| 439 | "type": "github" | 549 | "type": "github" |
| 440 | }, | 550 | }, |
| 441 | "original": { | 551 | "original": { |
| 442 | "owner": "YaLTeR", | 552 | "owner": "gkleen", |
| 553 | "ref": "fix/locked-monitor-control", | ||
| 443 | "repo": "niri", | 554 | "repo": "niri", |
| 444 | "type": "github" | 555 | "type": "github" |
| 445 | } | 556 | } |
| @@ -472,11 +583,11 @@ | |||
| 472 | ] | 583 | ] |
| 473 | }, | 584 | }, |
| 474 | "locked": { | 585 | "locked": { |
| 475 | "lastModified": 1743306489, | 586 | "lastModified": 1755404379, |
| 476 | "narHash": "sha256-LROaIjSLo347cwcHRfSpqzEOa2FoLSeJwU4dOrGm55E=", | 587 | "narHash": "sha256-Q6ZxZDBmD/B988Jjbx7/NchxOKIpOKBBrx9Yb0zMzpQ=", |
| 477 | "owner": "Mic92", | 588 | "owner": "Mic92", |
| 478 | "repo": "nix-index-database", | 589 | "repo": "nix-index-database", |
| 479 | "rev": "b3696bfb6c24aa61428839a99e8b40c53ac3a82d", | 590 | "rev": "ebbc1c05f786ae39bb5e04e57bf2c10c44a649e3", |
| 480 | "type": "github" | 591 | "type": "github" |
| 481 | }, | 592 | }, |
| 482 | "original": { | 593 | "original": { |
| @@ -486,6 +597,27 @@ | |||
| 486 | "type": "github" | 597 | "type": "github" |
| 487 | } | 598 | } |
| 488 | }, | 599 | }, |
| 600 | "nix-monitored": { | ||
| 601 | "inputs": { | ||
| 602 | "nixpkgs": [ | ||
| 603 | "nixpkgs" | ||
| 604 | ] | ||
| 605 | }, | ||
| 606 | "locked": { | ||
| 607 | "lastModified": 1745680380, | ||
| 608 | "narHash": "sha256-Z8PknjkmIr/8ZCH+dmc2Pc+UltiOr7/oKg37PXuVvuU=", | ||
| 609 | "owner": "ners", | ||
| 610 | "repo": "nix-monitored", | ||
| 611 | "rev": "60f3baa4701d58eab86c2d1d9c3d7e820074d461", | ||
| 612 | "type": "github" | ||
| 613 | }, | ||
| 614 | "original": { | ||
| 615 | "owner": "ners", | ||
| 616 | "ref": "master", | ||
| 617 | "repo": "nix-monitored", | ||
| 618 | "type": "github" | ||
| 619 | } | ||
| 620 | }, | ||
| 489 | "nixVirt": { | 621 | "nixVirt": { |
| 490 | "inputs": { | 622 | "inputs": { |
| 491 | "nixpkgs": [ | 623 | "nixpkgs": [ |
| @@ -493,11 +625,11 @@ | |||
| 493 | ] | 625 | ] |
| 494 | }, | 626 | }, |
| 495 | "locked": { | 627 | "locked": { |
| 496 | "lastModified": 1741549407, | 628 | "lastModified": 1748140003, |
| 497 | "narHash": "sha256-f9SXK+/rvlryDNlc++Eva/hYjbkf7OCalWwmwifRhtI=", | 629 | "narHash": "sha256-DNBZmuk1YRM2PmwbHzVdXumRjCUzQkMarg4iI/37rOQ=", |
| 498 | "owner": "AshleyYakeley", | 630 | "owner": "AshleyYakeley", |
| 499 | "repo": "NixVirt", | 631 | "repo": "NixVirt", |
| 500 | "rev": "9950b932dce4ae6b9bda7c83d41705c1a14e10f0", | 632 | "rev": "5dfe108fd859b122f9a96981cb6bc12297653d6c", |
| 501 | "type": "github" | 633 | "type": "github" |
| 502 | }, | 634 | }, |
| 503 | "original": { | 635 | "original": { |
| @@ -508,11 +640,11 @@ | |||
| 508 | }, | 640 | }, |
| 509 | "nixos-hardware": { | 641 | "nixos-hardware": { |
| 510 | "locked": { | 642 | "locked": { |
| 511 | "lastModified": 1743167577, | 643 | "lastModified": 1755330281, |
| 512 | "narHash": "sha256-I09SrXIO0UdyBFfh0fxDq5WnCDg8XKmZ1HQbaXzMA1k=", | 644 | "narHash": "sha256-aJHFJWP9AuI8jUGzI77LYcSlkA9wJnOIg4ZqftwNGXA=", |
| 513 | "owner": "NixOS", | 645 | "owner": "NixOS", |
| 514 | "repo": "nixos-hardware", | 646 | "repo": "nixos-hardware", |
| 515 | "rev": "0ed819e708af17bfc4bbc63ee080ef308a24aa42", | 647 | "rev": "3dac8a872557e0ca8c083cdcfc2f218d18e113b0", |
| 516 | "type": "github" | 648 | "type": "github" |
| 517 | }, | 649 | }, |
| 518 | "original": { | 650 | "original": { |
| @@ -540,16 +672,16 @@ | |||
| 540 | }, | 672 | }, |
| 541 | "nixpkgs-eostre": { | 673 | "nixpkgs-eostre": { |
| 542 | "locked": { | 674 | "locked": { |
| 543 | "lastModified": 1701282334, | 675 | "lastModified": 1748026580, |
| 544 | "narHash": "sha256-MxCVrXY6v4QmfTwIysjjaX0XUhqBbxTWWB4HXtDYsdk=", | 676 | "narHash": "sha256-rWtXrcIzU5wm/C8F9LWvUfBGu5U5E7cFzPYT1pHIJaQ=", |
| 545 | "owner": "NixOS", | 677 | "owner": "NixOS", |
| 546 | "repo": "nixpkgs", | 678 | "repo": "nixpkgs", |
| 547 | "rev": "057f9aecfb71c4437d2b27d3323df7f93c010b7e", | 679 | "rev": "11cb3517b3af6af300dd6c055aeda73c9bf52c48", |
| 548 | "type": "github" | 680 | "type": "github" |
| 549 | }, | 681 | }, |
| 550 | "original": { | 682 | "original": { |
| 551 | "owner": "NixOS", | 683 | "owner": "NixOS", |
| 552 | "ref": "23.11", | 684 | "ref": "25.05", |
| 553 | "repo": "nixpkgs", | 685 | "repo": "nixpkgs", |
| 554 | "type": "github" | 686 | "type": "github" |
| 555 | } | 687 | } |
| @@ -568,14 +700,17 @@ | |||
| 568 | }, | 700 | }, |
| 569 | "nixpkgs-lib_2": { | 701 | "nixpkgs-lib_2": { |
| 570 | "locked": { | 702 | "locked": { |
| 571 | "lastModified": 1733096140, | 703 | "lastModified": 1748740939, |
| 572 | "narHash": "sha256-1qRH7uAUsyQI7R1Uwl4T+XvdNv778H0Nb5njNrqvylY=", | 704 | "narHash": "sha256-rQaysilft1aVMwF14xIdGS3sj1yHlI6oKQNBRTF40cc=", |
| 573 | "type": "tarball", | 705 | "owner": "nix-community", |
| 574 | "url": "https://github.com/NixOS/nixpkgs/archive/5487e69da40cbd611ab2cadee0b4637225f7cfae.tar.gz" | 706 | "repo": "nixpkgs.lib", |
| 707 | "rev": "656a64127e9d791a334452c6b6606d17539476e2", | ||
| 708 | "type": "github" | ||
| 575 | }, | 709 | }, |
| 576 | "original": { | 710 | "original": { |
| 577 | "type": "tarball", | 711 | "owner": "nix-community", |
| 578 | "url": "https://github.com/NixOS/nixpkgs/archive/5487e69da40cbd611ab2cadee0b4637225f7cfae.tar.gz" | 712 | "repo": "nixpkgs.lib", |
| 713 | "type": "github" | ||
| 579 | } | 714 | } |
| 580 | }, | 715 | }, |
| 581 | "nixpkgs-lib_3": { | 716 | "nixpkgs-lib_3": { |
| @@ -630,38 +765,54 @@ | |||
| 630 | }, | 765 | }, |
| 631 | "nixpkgs-stable_2": { | 766 | "nixpkgs-stable_2": { |
| 632 | "locked": { | 767 | "locked": { |
| 633 | "lastModified": 1742937945, | 768 | "lastModified": 1730741070, |
| 634 | "narHash": "sha256-lWc+79eZRyvHp/SqMhHTMzZVhpxkRvthsP1Qx6UCq0E=", | 769 | "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=", |
| 635 | "owner": "NixOS", | 770 | "owner": "NixOS", |
| 636 | "repo": "nixpkgs", | 771 | "repo": "nixpkgs", |
| 637 | "rev": "d02d88f8de5b882ccdde0465d8fa2db3aa1169f7", | 772 | "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3", |
| 638 | "type": "github" | 773 | "type": "github" |
| 639 | }, | 774 | }, |
| 640 | "original": { | 775 | "original": { |
| 641 | "owner": "NixOS", | 776 | "owner": "NixOS", |
| 642 | "ref": "nixos-24.11", | 777 | "ref": "nixos-24.05", |
| 643 | "repo": "nixpkgs", | 778 | "repo": "nixpkgs", |
| 644 | "type": "github" | 779 | "type": "github" |
| 645 | } | 780 | } |
| 646 | }, | 781 | }, |
| 647 | "nixpkgs-stable_3": { | 782 | "nixpkgs-stable_3": { |
| 648 | "locked": { | 783 | "locked": { |
| 649 | "lastModified": 1717179513, | 784 | "lastModified": 1757408970, |
| 650 | "narHash": "sha256-vboIEwIQojofItm2xGCdZCzW96U85l9nDW3ifMuAIdM=", | 785 | "narHash": "sha256-aSgK4BLNFFGvDTNKPeB28lVXYqVn8RdyXDNAvgGq+k0=", |
| 651 | "owner": "NixOS", | 786 | "owner": "NixOS", |
| 652 | "repo": "nixpkgs", | 787 | "repo": "nixpkgs", |
| 653 | "rev": "63dacb46bf939521bdc93981b4cbb7ecb58427a0", | 788 | "rev": "d179d77c139e0a3f5c416477f7747e9d6b7ec315", |
| 654 | "type": "github" | 789 | "type": "github" |
| 655 | }, | 790 | }, |
| 656 | "original": { | 791 | "original": { |
| 657 | "owner": "NixOS", | 792 | "owner": "NixOS", |
| 658 | "ref": "24.05", | 793 | "ref": "nixos-25.05", |
| 659 | "repo": "nixpkgs", | 794 | "repo": "nixpkgs", |
| 660 | "type": "github" | 795 | "type": "github" |
| 661 | } | 796 | } |
| 662 | }, | 797 | }, |
| 663 | "nixpkgs-stable_4": { | 798 | "nixpkgs-stable_4": { |
| 664 | "locked": { | 799 | "locked": { |
| 800 | "lastModified": 1748026580, | ||
| 801 | "narHash": "sha256-rWtXrcIzU5wm/C8F9LWvUfBGu5U5E7cFzPYT1pHIJaQ=", | ||
| 802 | "owner": "NixOS", | ||
| 803 | "repo": "nixpkgs", | ||
| 804 | "rev": "11cb3517b3af6af300dd6c055aeda73c9bf52c48", | ||
| 805 | "type": "github" | ||
| 806 | }, | ||
| 807 | "original": { | ||
| 808 | "owner": "NixOS", | ||
| 809 | "ref": "25.05", | ||
| 810 | "repo": "nixpkgs", | ||
| 811 | "type": "github" | ||
| 812 | } | ||
| 813 | }, | ||
| 814 | "nixpkgs-stable_5": { | ||
| 815 | "locked": { | ||
| 665 | "lastModified": 1678872516, | 816 | "lastModified": 1678872516, |
| 666 | "narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=", | 817 | "narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=", |
| 667 | "owner": "NixOS", | 818 | "owner": "NixOS", |
| @@ -678,11 +829,11 @@ | |||
| 678 | }, | 829 | }, |
| 679 | "nixpkgs_2": { | 830 | "nixpkgs_2": { |
| 680 | "locked": { | 831 | "locked": { |
| 681 | "lastModified": 1743095683, | 832 | "lastModified": 1755615617, |
| 682 | "narHash": "sha256-gWd4urRoLRe8GLVC/3rYRae1h+xfQzt09xOfb0PaHSk=", | 833 | "narHash": "sha256-HMwfAJBdrr8wXAkbGhtcby1zGFvs+StOp19xNsbqdOg=", |
| 683 | "owner": "NixOS", | 834 | "owner": "NixOS", |
| 684 | "repo": "nixpkgs", | 835 | "repo": "nixpkgs", |
| 685 | "rev": "5e5402ecbcb27af32284d4a62553c019a3a49ea6", | 836 | "rev": "20075955deac2583bb12f07151c2df830ef346b4", |
| 686 | "type": "github" | 837 | "type": "github" |
| 687 | }, | 838 | }, |
| 688 | "original": { | 839 | "original": { |
| @@ -748,11 +899,11 @@ | |||
| 748 | "treefmt-nix": "treefmt-nix" | 899 | "treefmt-nix": "treefmt-nix" |
| 749 | }, | 900 | }, |
| 750 | "locked": { | 901 | "locked": { |
| 751 | "lastModified": 1742397518, | 902 | "lastModified": 1743690424, |
| 752 | "narHash": "sha256-nzgO/ZCSBzWjbMkYDxG+yl9Z2eGbCgQu06Oku3ir5D4=", | 903 | "narHash": "sha256-cX98bUuKuihOaRp8dNV1Mq7u6/CQZWTPth2IJPATBXc=", |
| 753 | "owner": "nix-community", | 904 | "owner": "nix-community", |
| 754 | "repo": "poetry2nix", | 905 | "repo": "poetry2nix", |
| 755 | "rev": "b9a98080beff0903a5e5fe431f42cde1e3e50d6b", | 906 | "rev": "ce2369db77f45688172384bbeb962bc6c2ea6f94", |
| 756 | "type": "github" | 907 | "type": "github" |
| 757 | }, | 908 | }, |
| 758 | "original": { | 909 | "original": { |
| @@ -790,18 +941,14 @@ | |||
| 790 | "nixpkgs": [ | 941 | "nixpkgs": [ |
| 791 | "ca-util", | 942 | "ca-util", |
| 792 | "nixpkgs" | 943 | "nixpkgs" |
| 793 | ], | ||
| 794 | "nixpkgs-stable": [ | ||
| 795 | "ca-util", | ||
| 796 | "nixpkgs" | ||
| 797 | ] | 944 | ] |
| 798 | }, | 945 | }, |
| 799 | "locked": { | 946 | "locked": { |
| 800 | "lastModified": 1734261738, | 947 | "lastModified": 1749636823, |
| 801 | "narHash": "sha256-3Lzk+7QyX8v60+km26D3dln7NMSA13vW+KYTkMkds6Q=", | 948 | "narHash": "sha256-WUaIlOlPLyPgz9be7fqWJA5iG6rHcGRtLERSCfUDne4=", |
| 802 | "owner": "cachix", | 949 | "owner": "cachix", |
| 803 | "repo": "pre-commit-hooks.nix", | 950 | "repo": "pre-commit-hooks.nix", |
| 804 | "rev": "4c8e75efbbdcc6f9203f64b1f21f8a55d2285264", | 951 | "rev": "623c56286de5a3193aa38891a6991b28f9bab056", |
| 805 | "type": "github" | 952 | "type": "github" |
| 806 | }, | 953 | }, |
| 807 | "original": { | 954 | "original": { |
| @@ -812,11 +959,38 @@ | |||
| 812 | }, | 959 | }, |
| 813 | "pre-commit-hooks-nix_3": { | 960 | "pre-commit-hooks-nix_3": { |
| 814 | "inputs": { | 961 | "inputs": { |
| 815 | "flake-compat": "flake-compat_4", | 962 | "flake-compat": [ |
| 816 | "flake-utils": "flake-utils_2", | 963 | "lanzaboote", |
| 964 | "flake-compat" | ||
| 965 | ], | ||
| 817 | "gitignore": "gitignore_3", | 966 | "gitignore": "gitignore_3", |
| 967 | "nixpkgs": [ | ||
| 968 | "lanzaboote", | ||
| 969 | "nixpkgs" | ||
| 970 | ], | ||
| 971 | "nixpkgs-stable": "nixpkgs-stable_2" | ||
| 972 | }, | ||
| 973 | "locked": { | ||
| 974 | "lastModified": 1731363552, | ||
| 975 | "narHash": "sha256-vFta1uHnD29VUY4HJOO/D6p6rxyObnf+InnSMT4jlMU=", | ||
| 976 | "owner": "cachix", | ||
| 977 | "repo": "pre-commit-hooks.nix", | ||
| 978 | "rev": "cd1af27aa85026ac759d5d3fccf650abe7e1bbf0", | ||
| 979 | "type": "github" | ||
| 980 | }, | ||
| 981 | "original": { | ||
| 982 | "owner": "cachix", | ||
| 983 | "repo": "pre-commit-hooks.nix", | ||
| 984 | "type": "github" | ||
| 985 | } | ||
| 986 | }, | ||
| 987 | "pre-commit-hooks-nix_4": { | ||
| 988 | "inputs": { | ||
| 989 | "flake-compat": "flake-compat_5", | ||
| 990 | "flake-utils": "flake-utils_2", | ||
| 991 | "gitignore": "gitignore_4", | ||
| 818 | "nixpkgs": "nixpkgs_3", | 992 | "nixpkgs": "nixpkgs_3", |
| 819 | "nixpkgs-stable": "nixpkgs-stable_4" | 993 | "nixpkgs-stable": "nixpkgs-stable_5" |
| 820 | }, | 994 | }, |
| 821 | "locked": { | 995 | "locked": { |
| 822 | "lastModified": 1685361114, | 996 | "lastModified": 1685361114, |
| @@ -834,14 +1008,14 @@ | |||
| 834 | }, | 1008 | }, |
| 835 | "prometheus-borg-exporter": { | 1009 | "prometheus-borg-exporter": { |
| 836 | "inputs": { | 1010 | "inputs": { |
| 837 | "flake-parts": "flake-parts_3", | 1011 | "flake-parts": "flake-parts_4", |
| 838 | "nixpkgs": [ | 1012 | "nixpkgs": [ |
| 839 | "nixpkgs" | 1013 | "nixpkgs" |
| 840 | ], | 1014 | ], |
| 841 | "poetry2nix": [ | 1015 | "poetry2nix": [ |
| 842 | "poetry2nix" | 1016 | "poetry2nix" |
| 843 | ], | 1017 | ], |
| 844 | "pre-commit-hooks-nix": "pre-commit-hooks-nix_3" | 1018 | "pre-commit-hooks-nix": "pre-commit-hooks-nix_4" |
| 845 | }, | 1019 | }, |
| 846 | "locked": { | 1020 | "locked": { |
| 847 | "lastModified": 1722088088, | 1021 | "lastModified": 1722088088, |
| @@ -858,6 +1032,81 @@ | |||
| 858 | "type": "gitlab" | 1032 | "type": "gitlab" |
| 859 | } | 1033 | } |
| 860 | }, | 1034 | }, |
| 1035 | "pyproject-build-systems": { | ||
| 1036 | "inputs": { | ||
| 1037 | "nixpkgs": [ | ||
| 1038 | "ca-util", | ||
| 1039 | "nixpkgs" | ||
| 1040 | ], | ||
| 1041 | "pyproject-nix": [ | ||
| 1042 | "ca-util", | ||
| 1043 | "pyproject-nix" | ||
| 1044 | ], | ||
| 1045 | "uv2nix": [ | ||
| 1046 | "ca-util", | ||
| 1047 | "uv2nix" | ||
| 1048 | ] | ||
| 1049 | }, | ||
| 1050 | "locked": { | ||
| 1051 | "lastModified": 1749519371, | ||
| 1052 | "narHash": "sha256-UJONN7mA2stweZCoRcry2aa1XTTBL0AfUOY84Lmqhos=", | ||
| 1053 | "owner": "pyproject-nix", | ||
| 1054 | "repo": "build-system-pkgs", | ||
| 1055 | "rev": "7c06967eca687f3482624250428cc12f43c92523", | ||
| 1056 | "type": "github" | ||
| 1057 | }, | ||
| 1058 | "original": { | ||
| 1059 | "owner": "pyproject-nix", | ||
| 1060 | "repo": "build-system-pkgs", | ||
| 1061 | "type": "github" | ||
| 1062 | } | ||
| 1063 | }, | ||
| 1064 | "pyproject-build-systems_2": { | ||
| 1065 | "inputs": { | ||
| 1066 | "nixpkgs": [ | ||
| 1067 | "nixpkgs" | ||
| 1068 | ], | ||
| 1069 | "pyproject-nix": [ | ||
| 1070 | "pyproject-nix" | ||
| 1071 | ], | ||
| 1072 | "uv2nix": [ | ||
| 1073 | "uv2nix" | ||
| 1074 | ] | ||
| 1075 | }, | ||
| 1076 | "locked": { | ||
| 1077 | "lastModified": 1755484659, | ||
| 1078 | "narHash": "sha256-2FfbqsaHVQd12XFFUAinIMAuGO3853LONmva1gT3vKw=", | ||
| 1079 | "owner": "pyproject-nix", | ||
| 1080 | "repo": "build-system-pkgs", | ||
| 1081 | "rev": "9778e87c2361810ff15e287ca5895c9da4a0e900", | ||
| 1082 | "type": "github" | ||
| 1083 | }, | ||
| 1084 | "original": { | ||
| 1085 | "owner": "pyproject-nix", | ||
| 1086 | "repo": "build-system-pkgs", | ||
| 1087 | "type": "github" | ||
| 1088 | } | ||
| 1089 | }, | ||
| 1090 | "pyproject-nix": { | ||
| 1091 | "inputs": { | ||
| 1092 | "nixpkgs": [ | ||
| 1093 | "nixpkgs" | ||
| 1094 | ] | ||
| 1095 | }, | ||
| 1096 | "locked": { | ||
| 1097 | "lastModified": 1754923840, | ||
| 1098 | "narHash": "sha256-QSKpYg+Ts9HYF155ltlj40iBex39c05cpOF8gjoE2EM=", | ||
| 1099 | "owner": "pyproject-nix", | ||
| 1100 | "repo": "pyproject.nix", | ||
| 1101 | "rev": "023cd4be230eacae52635be09eef100c37ef78da", | ||
| 1102 | "type": "github" | ||
| 1103 | }, | ||
| 1104 | "original": { | ||
| 1105 | "owner": "pyproject-nix", | ||
| 1106 | "repo": "pyproject.nix", | ||
| 1107 | "type": "github" | ||
| 1108 | } | ||
| 1109 | }, | ||
| 861 | "root": { | 1110 | "root": { |
| 862 | "inputs": { | 1111 | "inputs": { |
| 863 | "backup-utils": "backup-utils", | 1112 | "backup-utils": "backup-utils", |
| @@ -869,21 +1118,47 @@ | |||
| 869 | "home-manager": "home-manager", | 1118 | "home-manager": "home-manager", |
| 870 | "home-manager-eostre": "home-manager-eostre", | 1119 | "home-manager-eostre": "home-manager-eostre", |
| 871 | "impermanence": "impermanence", | 1120 | "impermanence": "impermanence", |
| 1121 | "lanzaboote": "lanzaboote", | ||
| 872 | "niri-flake": "niri-flake", | 1122 | "niri-flake": "niri-flake", |
| 873 | "nix-index-database": "nix-index-database", | 1123 | "nix-index-database": "nix-index-database", |
| 1124 | "nix-monitored": "nix-monitored", | ||
| 874 | "nixVirt": "nixVirt", | 1125 | "nixVirt": "nixVirt", |
| 875 | "nixos-hardware": "nixos-hardware", | 1126 | "nixos-hardware": "nixos-hardware", |
| 876 | "nixpkgs": "nixpkgs_2", | 1127 | "nixpkgs": "nixpkgs_2", |
| 877 | "nixpkgs-eostre": "nixpkgs-eostre", | 1128 | "nixpkgs-eostre": "nixpkgs-eostre", |
| 878 | "nixpkgs-pgbackrest": "nixpkgs-pgbackrest", | 1129 | "nixpkgs-pgbackrest": "nixpkgs-pgbackrest", |
| 879 | "nixpkgs-stable": "nixpkgs-stable_3", | 1130 | "nixpkgs-stable": "nixpkgs-stable_4", |
| 880 | "nvfetcher": "nvfetcher", | 1131 | "nvfetcher": "nvfetcher", |
| 881 | "poetry2nix": "poetry2nix", | 1132 | "poetry2nix": "poetry2nix", |
| 882 | "prometheus-borg-exporter": "prometheus-borg-exporter", | 1133 | "prometheus-borg-exporter": "prometheus-borg-exporter", |
| 1134 | "pyproject-build-systems": "pyproject-build-systems_2", | ||
| 1135 | "pyproject-nix": "pyproject-nix", | ||
| 883 | "sops-nix": "sops-nix", | 1136 | "sops-nix": "sops-nix", |
| 1137 | "uv2nix": "uv2nix", | ||
| 884 | "waybar": "waybar" | 1138 | "waybar": "waybar" |
| 885 | } | 1139 | } |
| 886 | }, | 1140 | }, |
| 1141 | "rust-overlay": { | ||
| 1142 | "inputs": { | ||
| 1143 | "nixpkgs": [ | ||
| 1144 | "lanzaboote", | ||
| 1145 | "nixpkgs" | ||
| 1146 | ] | ||
| 1147 | }, | ||
| 1148 | "locked": { | ||
| 1149 | "lastModified": 1731897198, | ||
| 1150 | "narHash": "sha256-Ou7vLETSKwmE/HRQz4cImXXJBr/k9gp4J4z/PF8LzTE=", | ||
| 1151 | "owner": "oxalica", | ||
| 1152 | "repo": "rust-overlay", | ||
| 1153 | "rev": "0be641045af6d8666c11c2c40e45ffc9667839b5", | ||
| 1154 | "type": "github" | ||
| 1155 | }, | ||
| 1156 | "original": { | ||
| 1157 | "owner": "oxalica", | ||
| 1158 | "repo": "rust-overlay", | ||
| 1159 | "type": "github" | ||
| 1160 | } | ||
| 1161 | }, | ||
| 887 | "sops-nix": { | 1162 | "sops-nix": { |
| 888 | "inputs": { | 1163 | "inputs": { |
| 889 | "nixpkgs": [ | 1164 | "nixpkgs": [ |
| @@ -891,11 +1166,11 @@ | |||
| 891 | ] | 1166 | ] |
| 892 | }, | 1167 | }, |
| 893 | "locked": { | 1168 | "locked": { |
| 894 | "lastModified": 1743305778, | 1169 | "lastModified": 1754988908, |
| 895 | "narHash": "sha256-Ux/UohNtnM5mn9SFjaHp6IZe2aAnUCzklMluNtV6zFo=", | 1170 | "narHash": "sha256-t+voe2961vCgrzPFtZxha0/kmFSHFobzF00sT8p9h0U=", |
| 896 | "owner": "Mic92", | 1171 | "owner": "Mic92", |
| 897 | "repo": "sops-nix", | 1172 | "repo": "sops-nix", |
| 898 | "rev": "8e873886bbfc32163fe027b8676c75637b7da114", | 1173 | "rev": "3223c7a92724b5d804e9988c6b447a0d09017d48", |
| 899 | "type": "github" | 1174 | "type": "github" |
| 900 | }, | 1175 | }, |
| 901 | "original": { | 1176 | "original": { |
| @@ -956,6 +1231,29 @@ | |||
| 956 | "type": "github" | 1231 | "type": "github" |
| 957 | } | 1232 | } |
| 958 | }, | 1233 | }, |
| 1234 | "uv2nix": { | ||
| 1235 | "inputs": { | ||
| 1236 | "nixpkgs": [ | ||
| 1237 | "nixpkgs" | ||
| 1238 | ], | ||
| 1239 | "pyproject-nix": [ | ||
| 1240 | "pyproject-nix" | ||
| 1241 | ] | ||
| 1242 | }, | ||
| 1243 | "locked": { | ||
| 1244 | "lastModified": 1755485731, | ||
| 1245 | "narHash": "sha256-k8kxwVs8Oze6q/jAaRa3RvZbb50I/K0b5uptlsh0HXI=", | ||
| 1246 | "owner": "pyproject-nix", | ||
| 1247 | "repo": "uv2nix", | ||
| 1248 | "rev": "bebbd80bf56110fcd20b425589814af28f1939eb", | ||
| 1249 | "type": "github" | ||
| 1250 | }, | ||
| 1251 | "original": { | ||
| 1252 | "owner": "pyproject-nix", | ||
| 1253 | "repo": "uv2nix", | ||
| 1254 | "type": "github" | ||
| 1255 | } | ||
| 1256 | }, | ||
| 959 | "waybar": { | 1257 | "waybar": { |
| 960 | "inputs": { | 1258 | "inputs": { |
| 961 | "flake-compat": [ | 1259 | "flake-compat": [ |
| @@ -966,16 +1264,16 @@ | |||
| 966 | ] | 1264 | ] |
| 967 | }, | 1265 | }, |
| 968 | "locked": { | 1266 | "locked": { |
| 969 | "lastModified": 1742140394, | 1267 | "lastModified": 1752562190, |
| 970 | "narHash": "sha256-U1Lp5HZbpnWQRetOLzQl3dURplY2BRfAZYkjBawYrVM=", | 1268 | "narHash": "sha256-zWOMCNe56H2PHUd3rJZ6tklZUZBLgRo85jd9IlK1g9o=", |
| 971 | "owner": "gkleen", | 1269 | "owner": "gkleen", |
| 972 | "repo": "Waybar", | 1270 | "repo": "Waybar", |
| 973 | "rev": "f310667db199c570b599a08152d49b7f80db93f2", | 1271 | "rev": "d008cd998369c40f2344a856caf39cdbbd7bd068", |
| 974 | "type": "github" | 1272 | "type": "github" |
| 975 | }, | 1273 | }, |
| 976 | "original": { | 1274 | "original": { |
| 977 | "owner": "gkleen", | 1275 | "owner": "gkleen", |
| 978 | "ref": "feat/niri-workspaces-hide", | 1276 | "ref": "feat/niri-urgency", |
| 979 | "repo": "Waybar", | 1277 | "repo": "Waybar", |
| 980 | "type": "github" | 1278 | "type": "github" |
| 981 | } | 1279 | } |
| @@ -983,16 +1281,16 @@ | |||
| 983 | "xwayland-satellite-stable": { | 1281 | "xwayland-satellite-stable": { |
| 984 | "flake": false, | 1282 | "flake": false, |
| 985 | "locked": { | 1283 | "locked": { |
| 986 | "lastModified": 1739246919, | 1284 | "lastModified": 1755491097, |
| 987 | "narHash": "sha256-/hBM43/Gd0/tW+egrhlWgOIISeJxEs2uAOIYVpfDKeU=", | 1285 | "narHash": "sha256-m+9tUfsmBeF2Gn4HWa6vSITZ4Gz1eA1F5Kh62B0N4oE=", |
| 988 | "owner": "Supreeeme", | 1286 | "owner": "Supreeeme", |
| 989 | "repo": "xwayland-satellite", | 1287 | "repo": "xwayland-satellite", |
| 990 | "rev": "44590a416d4a3e8220e19e29e0b6efe64a80315d", | 1288 | "rev": "388d291e82ffbc73be18169d39470f340707edaa", |
| 991 | "type": "github" | 1289 | "type": "github" |
| 992 | }, | 1290 | }, |
| 993 | "original": { | 1291 | "original": { |
| 994 | "owner": "Supreeeme", | 1292 | "owner": "Supreeeme", |
| 995 | "ref": "v0.5.1", | 1293 | "ref": "v0.7", |
| 996 | "repo": "xwayland-satellite", | 1294 | "repo": "xwayland-satellite", |
| 997 | "type": "github" | 1295 | "type": "github" |
| 998 | } | 1296 | } |
| @@ -1000,11 +1298,11 @@ | |||
| 1000 | "xwayland-satellite-unstable": { | 1298 | "xwayland-satellite-unstable": { |
| 1001 | "flake": false, | 1299 | "flake": false, |
| 1002 | "locked": { | 1300 | "locked": { |
| 1003 | "lastModified": 1742773235, | 1301 | "lastModified": 1757179758, |
| 1004 | "narHash": "sha256-YhJex62HHVF6EfdGLIC01uM6jH8XJu5ryZ+LlhG7wMs=", | 1302 | "narHash": "sha256-TIvyWzRt1miQj6Cf5Wy8Qz43XIZX7c4vTVwRLAT5S4Y=", |
| 1005 | "owner": "Supreeeme", | 1303 | "owner": "Supreeeme", |
| 1006 | "repo": "xwayland-satellite", | 1304 | "repo": "xwayland-satellite", |
| 1007 | "rev": "b2613aec05f9e3f8488ef924203d62cafb712642", | 1305 | "rev": "970728d0d9d1eada342bb8860af214b601139e58", |
| 1008 | "type": "github" | 1306 | "type": "github" |
| 1009 | }, | 1307 | }, |
| 1010 | "original": { | 1308 | "original": { |
| @@ -29,13 +29,13 @@ | |||
| 29 | type = "github"; | 29 | type = "github"; |
| 30 | owner = "NixOS"; | 30 | owner = "NixOS"; |
| 31 | repo = "nixpkgs"; | 31 | repo = "nixpkgs"; |
| 32 | ref = "24.05"; | 32 | ref = "25.05"; |
| 33 | }; | 33 | }; |
| 34 | nixpkgs-eostre = { | 34 | nixpkgs-eostre = { |
| 35 | type = "github"; | 35 | type = "github"; |
| 36 | owner = "NixOS"; | 36 | owner = "NixOS"; |
| 37 | repo = "nixpkgs"; | 37 | repo = "nixpkgs"; |
| 38 | ref = "23.11"; | 38 | ref = "25.05"; |
| 39 | }; | 39 | }; |
| 40 | home-manager = { | 40 | home-manager = { |
| 41 | type = "github"; | 41 | type = "github"; |
| @@ -53,7 +53,7 @@ | |||
| 53 | type = "github"; | 53 | type = "github"; |
| 54 | owner = "gkleen"; | 54 | owner = "gkleen"; |
| 55 | repo = "home-manager"; | 55 | repo = "home-manager"; |
| 56 | ref = "nixos-late-start-23.11"; | 56 | ref = "nixos-late-start-25.05"; |
| 57 | inputs = { | 57 | inputs = { |
| 58 | nixpkgs.follows = "nixpkgs-eostre"; | 58 | nixpkgs.follows = "nixpkgs-eostre"; |
| 59 | }; | 59 | }; |
| @@ -125,25 +125,43 @@ | |||
| 125 | nixpkgs.follows = "nixpkgs"; | 125 | nixpkgs.follows = "nixpkgs"; |
| 126 | }; | 126 | }; |
| 127 | }; | 127 | }; |
| 128 | pyproject-nix = { | ||
| 129 | url = "github:pyproject-nix/pyproject.nix"; | ||
| 130 | inputs.nixpkgs.follows = "nixpkgs"; | ||
| 131 | }; | ||
| 132 | uv2nix = { | ||
| 133 | url = "github:pyproject-nix/uv2nix"; | ||
| 134 | inputs.pyproject-nix.follows = "pyproject-nix"; | ||
| 135 | inputs.nixpkgs.follows = "nixpkgs"; | ||
| 136 | }; | ||
| 137 | pyproject-build-systems = { | ||
| 138 | url = "github:pyproject-nix/build-system-pkgs"; | ||
| 139 | inputs.pyproject-nix.follows = "pyproject-nix"; | ||
| 140 | inputs.uv2nix.follows = "uv2nix"; | ||
| 141 | inputs.nixpkgs.follows = "nixpkgs"; | ||
| 142 | }; | ||
| 128 | 143 | ||
| 129 | ca-util = { | 144 | ca-util = { |
| 130 | type = "gitlab"; | 145 | type = "gitlab"; |
| 131 | owner = "gkleen"; | 146 | owner = "gkleen"; |
| 132 | repo = "ca"; | 147 | repo = "ca"; |
| 133 | ref = "v3.1.3"; | 148 | ref = "v3.1.5"; |
| 134 | inputs = { | 149 | inputs = { |
| 150 | pyproject-nix.follows = "pyproject-nix"; | ||
| 151 | uv2nix.follows = "uv2nix"; | ||
| 135 | nixpkgs.follows = "nixpkgs"; | 152 | nixpkgs.follows = "nixpkgs"; |
| 136 | poetry2nix.follows = "poetry2nix"; | ||
| 137 | }; | 153 | }; |
| 138 | }; | 154 | }; |
| 139 | backup-utils = { | 155 | backup-utils = { |
| 140 | type = "gitlab"; | 156 | type = "gitlab"; |
| 141 | owner = "gkleen"; | 157 | owner = "gkleen"; |
| 142 | repo = "backup-utils"; | 158 | repo = "backup-utils"; |
| 143 | ref = "v0.1.6"; | 159 | ref = "v0.1.7"; |
| 144 | inputs = { | 160 | inputs = { |
| 145 | nixpkgs.follows = "nixpkgs"; | 161 | nixpkgs.follows = "nixpkgs"; |
| 146 | poetry2nix.follows = "poetry2nix"; | 162 | pyproject-nix.follows = "pyproject-nix"; |
| 163 | uv2nix.follows = "uv2nix"; | ||
| 164 | pyproject-build-systems.follows = "pyproject-build-systems"; | ||
| 147 | }; | 165 | }; |
| 148 | }; | 166 | }; |
| 149 | prometheus-borg-exporter = { | 167 | prometheus-borg-exporter = { |
| @@ -172,7 +190,7 @@ | |||
| 172 | type = "github"; | 190 | type = "github"; |
| 173 | owner = "gkleen"; | 191 | owner = "gkleen"; |
| 174 | repo = "Waybar"; | 192 | repo = "Waybar"; |
| 175 | ref = "feat/niri-workspaces-hide"; | 193 | ref = "feat/niri-urgency"; |
| 176 | inputs = { | 194 | inputs = { |
| 177 | nixpkgs.follows = "nixpkgs"; | 195 | nixpkgs.follows = "nixpkgs"; |
| 178 | flake-compat.follows = "flake-compat"; | 196 | flake-compat.follows = "flake-compat"; |
| @@ -191,9 +209,31 @@ | |||
| 191 | ref = "main"; | 209 | ref = "main"; |
| 192 | inputs = { | 210 | inputs = { |
| 193 | nixpkgs.follows = "nixpkgs"; | 211 | nixpkgs.follows = "nixpkgs"; |
| 194 | # niri-unstable.url = "github:gkleen/niri"; | 212 | niri-unstable = { |
| 213 | type = "github"; | ||
| 214 | owner = "gkleen"; | ||
| 215 | repo = "niri"; | ||
| 216 | ref = "fix/locked-monitor-control"; | ||
| 217 | }; | ||
| 195 | }; | 218 | }; |
| 196 | }; | 219 | }; |
| 220 | nix-monitored = { | ||
| 221 | type = "github"; | ||
| 222 | owner = "ners"; | ||
| 223 | repo = "nix-monitored"; | ||
| 224 | ref = "master"; | ||
| 225 | inputs = { | ||
| 226 | nixpkgs.follows = "nixpkgs"; | ||
| 227 | }; | ||
| 228 | }; | ||
| 229 | lanzaboote = { | ||
| 230 | type = "github"; | ||
| 231 | owner = "nix-community"; | ||
| 232 | repo = "lanzaboote"; | ||
| 233 | ref = "v0.4.2"; | ||
| 234 | |||
| 235 | inputs.nixpkgs.follows = "nixpkgs"; | ||
| 236 | }; | ||
| 197 | }; | 237 | }; |
| 198 | 238 | ||
| 199 | outputs = { self, nixpkgs, home-manager, sops-nix, deploy-rs, nvfetcher, niri-flake, ... }@inputs: | 239 | outputs = { self, nixpkgs, home-manager, sops-nix, deploy-rs, nvfetcher, niri-flake, ... }@inputs: |
| @@ -339,7 +379,7 @@ | |||
| 339 | 379 | ||
| 340 | overlays = mapAttrs (_name: path: mkOverlay path) overlayPaths; | 380 | overlays = mapAttrs (_name: path: mkOverlay path) overlayPaths; |
| 341 | 381 | ||
| 342 | packages = forAllSystems (system: systemPkgs: nixImport rec { dir = ./tools; _import = _path: name: import "${toString dir}/${name}" ({ inherit system; } // inputs); }); | 382 | packages = forAllSystems (system: systemPkgs: nixImport rec { dir = ./tools; _import = name: _base: import (dir + "/${name}") ({ inherit system; } // inputs); }); |
| 343 | 383 | ||
| 344 | # packages = mapAttrs (_name: filterAttrs (_name: isDerivation)) packages; | 384 | # packages = mapAttrs (_name: filterAttrs (_name: isDerivation)) packages; |
| 345 | # packages' = mapAttrs (_name: filterAttrs (_name: value: !(isDerivation value))) packages; | 385 | # packages' = mapAttrs (_name: filterAttrs (_name: value: !(isDerivation value))) packages; |
| @@ -351,6 +391,8 @@ | |||
| 351 | activateNixosConfigurations activateHomeManagerConfigurations | 391 | activateNixosConfigurations activateHomeManagerConfigurations |
| 352 | ]; | 392 | ]; |
| 353 | 393 | ||
| 394 | lib = nixImport rec { dir = ./lib; _import = name: _base: import (dir + "/${name}") inputs; }; | ||
| 395 | |||
| 354 | devShells = forAllSystems (system: systemPkgs: { default = import ./shell.nix ({ inherit system; } // inputs); } // installerShells system systemPkgs); | 396 | devShells = forAllSystems (system: systemPkgs: { default = import ./shell.nix ({ inherit system; } // inputs); } // installerShells system systemPkgs); |
| 355 | 397 | ||
| 356 | templates.default = { | 398 | templates.default = { |
| @@ -374,10 +416,10 @@ | |||
| 374 | # path = activateHomeManager (self.nixosConfigurations.${hostname}.config.nixpkgs.system) usercfg.home; | 416 | # path = activateHomeManager (self.nixosConfigurations.${hostname}.config.nixpkgs.system) usercfg.home; |
| 375 | # }) self.nixosConfigurations.${hostname}.config.home-manager.users); | 417 | # }) self.nixosConfigurations.${hostname}.config.home-manager.users); |
| 376 | }) (nixImport { dir = ./hosts; _import = (_path: name: name); }); | 418 | }) (nixImport { dir = ./hosts; _import = (_path: name: name); }); |
| 377 | overrides = if pathExists ./deploy then nixImport { dir = ./deploy; _import = path: _name: import (./deploy + "/${path}") inputs; } else {}; | 419 | overrides = if pathExists ./deploy then nixImport rec { dir = ./deploy; _import = path: _name: import (dir + "/${path}") inputs; } else {}; |
| 378 | filterEnabled = attrs: mapAttrs (_n: v: filterAttrs (n: _v: n != "enabled") v) (filterAttrs (_n: v: v.enabled or true) attrs); | 420 | filterEnabled = attrs: mapAttrs (_n: v: filterAttrs (n: _v: n != "enabled") v) (filterAttrs (_n: v: v.enabled or true) attrs); |
| 379 | in mapAttrs (_n: v: if v ? "profiles" then v // { profiles = filterEnabled v.profiles; } else v) (filterEnabled (recursiveUpdate defaults overrides)); | 421 | in mapAttrs (_n: v: if v ? "profiles" then v // { profiles = filterEnabled v.profiles; } else v) (filterEnabled (recursiveUpdate defaults overrides)); |
| 380 | 422 | ||
| 381 | checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib; | 423 | # checks = builtins.mapAttrs (system: deployLib: deployLib.deployChecks self.deploy) deploy-rs.lib; |
| 382 | }; | 424 | }; |
| 383 | } | 425 | } |
diff --git a/home-modules/nixpkgs-release-check.nix b/home-modules/nixpkgs-release-check.nix new file mode 100644 index 00000000..baf2713a --- /dev/null +++ b/home-modules/nixpkgs-release-check.nix | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | { ... }: | ||
| 2 | { | ||
| 3 | config.home.enableNixpkgsReleaseCheck = false; | ||
| 4 | } | ||
diff --git a/home-modules/quickshell.nix b/home-modules/quickshell.nix new file mode 100644 index 00000000..dac7089f --- /dev/null +++ b/home-modules/quickshell.nix | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | { config, pkgs, lib, ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | cfg = config.programs.quickshell; | ||
| 5 | in { | ||
| 6 | disabledModules = ["programs/quickshell.nix"]; | ||
| 7 | |||
| 8 | options = { | ||
| 9 | programs.quickshell = { | ||
| 10 | enable = lib.mkEnableOption "quickshell"; | ||
| 11 | package = lib.mkPackageOption pkgs "quickshell" { nullable = true; }; | ||
| 12 | config.src = lib.mkOption { | ||
| 13 | type = lib.types.path; | ||
| 14 | }; | ||
| 15 | config.replacements = lib.mkOption { | ||
| 16 | type = lib.types.attrsOf lib.types.str; | ||
| 17 | default = {}; | ||
| 18 | }; | ||
| 19 | }; | ||
| 20 | }; | ||
| 21 | |||
| 22 | config = lib.mkIf cfg.enable { | ||
| 23 | home.packages = [ cfg.package ]; | ||
| 24 | |||
| 25 | xdg.configFile."quickshell".source = pkgs.stdenvNoCC.mkDerivation { | ||
| 26 | name = "quickshell"; | ||
| 27 | preferLocalBuild = true; | ||
| 28 | allowSubstitutes = false; | ||
| 29 | dontUnpack = true; | ||
| 30 | inherit (cfg.config) src; | ||
| 31 | buildPhase = '' | ||
| 32 | runHook preBuild | ||
| 33 | |||
| 34 | while IFS= read -r -d $'\0' file <&3; do | ||
| 35 | [[ -z $file ]] && continue | ||
| 36 | |||
| 37 | mkdir -p "$out"/"$(dirname "$file")" | ||
| 38 | substitute "$src"/"$file" "$out"/"$file" \ | ||
| 39 | ${lib.concatStringsSep " " ( | ||
| 40 | lib.concatLists (lib.mapAttrsToList (name: value: [ | ||
| 41 | "--replace-quiet" (lib.escapeShellArg "@${name}@") (lib.escapeShellArg value) | ||
| 42 | ]) cfg.config.replacements) | ||
| 43 | )} | ||
| 44 | done 3< <(find "$src" -type f -printf '%P\0') | ||
| 45 | |||
| 46 | runHook postBuild | ||
| 47 | ''; | ||
| 48 | }; | ||
| 49 | |||
| 50 | systemd.user.services.quickshell = { | ||
| 51 | Unit = { | ||
| 52 | Description = "quickshell"; | ||
| 53 | Documentation = "https://quickshell.org/docs/v${cfg.package.version}"; | ||
| 54 | PartOf = [ "graphical-session.target" ]; | ||
| 55 | After = [ "graphical-session-pre.target" ]; | ||
| 56 | }; | ||
| 57 | |||
| 58 | Service = { | ||
| 59 | ExecStart = lib.getExe cfg.package; | ||
| 60 | Restart = "always"; | ||
| 61 | }; | ||
| 62 | |||
| 63 | Install = { | ||
| 64 | WantedBy = [ "graphical-session.target" ]; | ||
| 65 | }; | ||
| 66 | }; | ||
| 67 | }; | ||
| 68 | } | ||
diff --git a/hosts/eostre/default.nix b/hosts/eostre/default.nix index fd4b15f2..d4113024 100644 --- a/hosts/eostre/default.nix +++ b/hosts/eostre/default.nix | |||
| @@ -37,14 +37,10 @@ with lib; | |||
| 37 | powerManagement.enable = true; | 37 | powerManagement.enable = true; |
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| 40 | opengl.enable = true; | 40 | graphics.enable = true; |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | environment.etc."machine-id".text = "f457b21333f1491e916521151ff5d468"; | ||
| 44 | |||
| 45 | networking = { | 43 | networking = { |
| 46 | hostId = "f457b213"; | ||
| 47 | |||
| 48 | domain = "lan.yggdrasil"; | 44 | domain = "lan.yggdrasil"; |
| 49 | search = [ "lan.yggdrasil" "yggdrasil" ]; | 45 | search = [ "lan.yggdrasil" "yggdrasil" ]; |
| 50 | 46 | ||
| @@ -83,19 +79,14 @@ with lib; | |||
| 83 | ]; | 79 | ]; |
| 84 | }; | 80 | }; |
| 85 | 81 | ||
| 86 | 82 | services.displayManager.sddm = { | |
| 87 | services.xserver = { | ||
| 88 | enable = true; | 83 | enable = true; |
| 89 | displayManager.sddm = { | 84 | wayland.enable = true; |
| 90 | enable = true; | 85 | settings = { |
| 91 | settings = { | 86 | Users.HideUsers = "gkleen"; |
| 92 | Users.HideUsers = "gkleen"; | ||
| 93 | }; | ||
| 94 | }; | 87 | }; |
| 95 | desktopManager.plasma5.enable = true; | ||
| 96 | |||
| 97 | videoDrivers = [ "nvidia" ]; | ||
| 98 | }; | 88 | }; |
| 89 | services.desktopManager.plasma6.enable = true; | ||
| 99 | 90 | ||
| 100 | 91 | ||
| 101 | services.openssh = { | 92 | services.openssh = { |
diff --git a/hosts/sif/default.nix b/hosts/sif/default.nix index 9208e391..fb2dddc6 100644 --- a/hosts/sif/default.nix +++ b/hosts/sif/default.nix | |||
| @@ -12,10 +12,9 @@ let | |||
| 12 | in { | 12 | in { |
| 13 | imports = with flake.nixosModules.systemProfiles; [ | 13 | imports = with flake.nixosModules.systemProfiles; [ |
| 14 | ./hw.nix | 14 | ./hw.nix |
| 15 | ./mail ./libvirt ./greetd | 15 | ./email ./libvirt ./greetd |
| 16 | tmpfs-root bcachefs initrd-all-crypto-modules default-locale openssh rebuild-machines niri-unstable networkmanager | 16 | tmpfs-root bcachefs initrd-all-crypto-modules default-locale openssh rebuild-machines niri-unstable networkmanager lanzaboote |
| 17 | flakeInputs.nixos-hardware.nixosModules.lenovo-thinkpad-p1 | 17 | flakeInputs.nixos-hardware.nixosModules.lenovo-thinkpad-p1 |
| 18 | flakeInputs.impermanence.nixosModules.impermanence | ||
| 19 | flakeInputs.nixVirt.nixosModules.default | 18 | flakeInputs.nixVirt.nixosModules.default |
| 20 | ]; | 19 | ]; |
| 21 | 20 | ||
| @@ -34,6 +33,10 @@ in { | |||
| 34 | initrd = { | 33 | initrd = { |
| 35 | systemd = { | 34 | systemd = { |
| 36 | emergencyAccess = config.users.users.root.hashedPassword; | 35 | emergencyAccess = config.users.users.root.hashedPassword; |
| 36 | extraBin = { | ||
| 37 | "vim" = lib.getExe pkgs.vim; | ||
| 38 | "grep" = lib.getExe pkgs.gnugrep; | ||
| 39 | }; | ||
| 37 | }; | 40 | }; |
| 38 | luks.devices = { | 41 | luks.devices = { |
| 39 | nvm0 = { device = "/dev/disk/by-uuid/bef17e86-d929-4a60-97cb-6bfa133face7"; bypassWorkqueues = true; }; | 42 | nvm0 = { device = "/dev/disk/by-uuid/bef17e86-d929-4a60-97cb-6bfa133face7"; bypassWorkqueues = true; }; |
| @@ -47,12 +50,8 @@ in { | |||
| 47 | 50 | ||
| 48 | blacklistedKernelModules = [ "nouveau" ]; | 51 | blacklistedKernelModules = [ "nouveau" ]; |
| 49 | 52 | ||
| 50 | # Use the systemd-boot EFI boot loader. | 53 | lanzaboote.configurationLimit = 15; |
| 51 | loader = { | 54 | loader = { |
| 52 | systemd-boot = { | ||
| 53 | enable = true; | ||
| 54 | configurationLimit = 15; | ||
| 55 | }; | ||
| 56 | efi.canTouchEfiVariables = true; | 55 | efi.canTouchEfiVariables = true; |
| 57 | timeout = null; | 56 | timeout = null; |
| 58 | }; | 57 | }; |
| @@ -63,19 +62,27 @@ in { | |||
| 63 | kernelPatches = [ | 62 | kernelPatches = [ |
| 64 | { name = "edac-config"; | 63 | { name = "edac-config"; |
| 65 | patch = null; | 64 | patch = null; |
| 66 | extraStructuredConfig = with lib.kernel; { | 65 | structuredExtraConfig = with lib.kernel; { |
| 67 | EDAC = yes; | 66 | EDAC = yes; |
| 68 | EDAC_IE31200 = yes; | 67 | EDAC_IE31200 = yes; |
| 69 | }; | 68 | }; |
| 70 | } | 69 | } |
| 71 | { name = "zswap-default"; | 70 | { name = "zswap-default"; |
| 72 | patch = null; | 71 | patch = null; |
| 73 | extraStructuredConfig = with lib.kernel; { | 72 | structuredExtraConfig = with lib.kernel; { |
| 74 | ZSWAP_DEFAULT_ON = yes; | 73 | ZSWAP_DEFAULT_ON = yes; |
| 75 | ZSWAP_SHRINKER_DEFAULT_ON = yes; | 74 | ZSWAP_SHRINKER_DEFAULT_ON = yes; |
| 76 | }; | 75 | }; |
| 77 | } | 76 | } |
| 78 | ]; | 77 | ]; |
| 78 | consoleLogLevel = 3; | ||
| 79 | kernelParams = [ | ||
| 80 | "quiet" | ||
| 81 | "boot.shell_on_fail" | ||
| 82 | "udev.log_priority=3" | ||
| 83 | "rd.systemd.show_status=auto" | ||
| 84 | "plymouth.use-simpledrm" | ||
| 85 | ]; | ||
| 79 | 86 | ||
| 80 | tmp.useTmpfs = true; | 87 | tmp.useTmpfs = true; |
| 81 | 88 | ||
| @@ -97,6 +104,8 @@ in { | |||
| 97 | server ptbtime2.ptb.de prefer iburst nts | 104 | server ptbtime2.ptb.de prefer iburst nts |
| 98 | server ptbtime3.ptb.de prefer iburst nts | 105 | server ptbtime3.ptb.de prefer iburst nts |
| 99 | server ptbtime4.ptb.de prefer iburst nts | 106 | server ptbtime4.ptb.de prefer iburst nts |
| 107 | pool ntppool1.time.nl prefer iburst nts | ||
| 108 | pool ntppool2.time.nl prefer iburst nts | ||
| 100 | 109 | ||
| 101 | authselectmode require | 110 | authselectmode require |
| 102 | minsources 3 | 111 | minsources 3 |
| @@ -125,40 +134,16 @@ in { | |||
| 125 | rulesetFile = ./ruleset.nft; | 134 | rulesetFile = ./ruleset.nft; |
| 126 | }; | 135 | }; |
| 127 | 136 | ||
| 128 | # firewall = { | ||
| 129 | # enable = true; | ||
| 130 | # allowedTCPPorts = [ 22 # ssh | ||
| 131 | # 8000 # quickserve | ||
| 132 | # ]; | ||
| 133 | # }; | ||
| 134 | |||
| 135 | # wlanInterfaces = { | ||
| 136 | # wlan0 = { | ||
| 137 | # device = "wlp82s0"; | ||
| 138 | # }; | ||
| 139 | # }; | ||
| 140 | |||
| 141 | # bonds = { | ||
| 142 | # "lan" = { | ||
| 143 | # interfaces = [ "wlan0" "enp0s31f6" "dock0" ]; | ||
| 144 | # driverOptions = { | ||
| 145 | # miimon = "1000"; | ||
| 146 | # mode = "active-backup"; | ||
| 147 | # primary_reselect = "always"; | ||
| 148 | # }; | ||
| 149 | # }; | ||
| 150 | # }; | ||
| 151 | |||
| 152 | useDHCP = false; | 137 | useDHCP = false; |
| 153 | useNetworkd = true; | 138 | useNetworkd = true; |
| 154 | |||
| 155 | # interfaces."tinc.yggdrasil" = { | ||
| 156 | # virtual = true; | ||
| 157 | # virtualType = config.services.tinc.networks.yggdrasil.interfaceType; | ||
| 158 | # macAddress = "5c:93:21:c3:61:39"; | ||
| 159 | # }; | ||
| 160 | }; | 139 | }; |
| 161 | 140 | ||
| 141 | environment.etc."NetworkManager/dnsmasq.d/dnssec.conf" = { | ||
| 142 | text = '' | ||
| 143 | conf-file=${pkgs.dnsmasq}/share/dnsmasq/trust-anchors.conf | ||
| 144 | dnssec | ||
| 145 | ''; | ||
| 146 | }; | ||
| 162 | environment.etc."NetworkManager/dnsmasq.d/libvirt_dnsmasq.conf" = { | 147 | environment.etc."NetworkManager/dnsmasq.d/libvirt_dnsmasq.conf" = { |
| 163 | text = '' | 148 | text = '' |
| 164 | except-interface=virbr0 | 149 | except-interface=virbr0 |
| @@ -401,19 +386,6 @@ in { | |||
| 401 | ]; | 386 | ]; |
| 402 | 387 | ||
| 403 | services = { | 388 | services = { |
| 404 | uucp = { | ||
| 405 | enable = true; | ||
| 406 | nodeName = "sif"; | ||
| 407 | remoteNodes = { | ||
| 408 | "ymir" = { | ||
| 409 | publicKeys = ["ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIG6KNtsCOl5fsZ4rV7udTulGMphJweLBoKapzerWNoLY root@ymir"]; | ||
| 410 | hostnames = ["ymir.yggdrasil.li" "ymir.niflheim.yggdrasil"]; | ||
| 411 | }; | ||
| 412 | }; | ||
| 413 | |||
| 414 | defaultCommands = lib.mkForce []; | ||
| 415 | }; | ||
| 416 | |||
| 417 | avahi.enable = true; | 389 | avahi.enable = true; |
| 418 | 390 | ||
| 419 | fwupd.enable = true; | 391 | fwupd.enable = true; |
| @@ -432,8 +404,8 @@ in { | |||
| 432 | 404 | ||
| 433 | logind = { | 405 | logind = { |
| 434 | lidSwitch = "suspend"; | 406 | lidSwitch = "suspend"; |
| 435 | lidSwitchDocked = "lock"; | 407 | lidSwitchDocked = "ignore"; |
| 436 | lidSwitchExternalPower = "lock"; | 408 | lidSwitchExternalPower = "ignore"; |
| 437 | }; | 409 | }; |
| 438 | 410 | ||
| 439 | atd = { | 411 | atd = { |
| @@ -475,11 +447,6 @@ in { | |||
| 475 | 447 | ||
| 476 | systemd.tmpfiles.settings = { | 448 | systemd.tmpfiles.settings = { |
| 477 | "10-localtime"."/etc/localtime".L.argument = "/.bcachefs/etc/localtime"; | 449 | "10-localtime"."/etc/localtime".L.argument = "/.bcachefs/etc/localtime"; |
| 478 | |||
| 479 | # "10-regreet"."/var/cache/regreet/cache.toml".C.argument = toString ((pkgs.formats.toml {}).generate "cache.toml" { | ||
| 480 | # last_user = "gkleen"; | ||
| 481 | # user_to_last_sess.gkleen = "Niri"; | ||
| 482 | # }); | ||
| 483 | }; | 450 | }; |
| 484 | 451 | ||
| 485 | users = { | 452 | users = { |
| @@ -639,25 +606,6 @@ in { | |||
| 639 | 606 | ||
| 640 | environment.etc."X11/xorg.conf.d/50-wacom.conf".source = lib.mkForce ./wacom.conf; | 607 | environment.etc."X11/xorg.conf.d/50-wacom.conf".source = lib.mkForce ./wacom.conf; |
| 641 | 608 | ||
| 642 | systemd.services."ac-plugged" = { | ||
| 643 | description = "Inhibit handling of lid-switch and sleep"; | ||
| 644 | |||
| 645 | path = with pkgs; [ systemd coreutils ]; | ||
| 646 | |||
| 647 | script = '' | ||
| 648 | exec systemd-inhibit --what=handle-lid-switch --why="AC is connected" --mode=block sleep infinity | ||
| 649 | ''; | ||
| 650 | |||
| 651 | serviceConfig = { | ||
| 652 | Type = "simple"; | ||
| 653 | }; | ||
| 654 | }; | ||
| 655 | |||
| 656 | services.udev.extraRules = with pkgs; lib.mkAfter '' | ||
| 657 | SUBSYSTEM=="power_supply", ENV{POWER_SUPPLY_ONLINE}=="0", RUN+="${systemd}/bin/systemctl --no-block stop ac-plugged.service" | ||
| 658 | SUBSYSTEM=="power_supply", ENV{POWER_SUPPLY_ONLINE}=="1", RUN+="${systemd}/bin/systemctl --no-block start ac-plugged.service" | ||
| 659 | ''; | ||
| 660 | |||
| 661 | systemd.services."nix-daemon".serviceConfig = { | 609 | systemd.services."nix-daemon".serviceConfig = { |
| 662 | MemoryAccounting = true; | 610 | MemoryAccounting = true; |
| 663 | MemoryHigh = "50%"; | 611 | MemoryHigh = "50%"; |
| @@ -681,6 +629,10 @@ in { | |||
| 681 | dconf.enable = true; | 629 | dconf.enable = true; |
| 682 | niri.enable = true; | 630 | niri.enable = true; |
| 683 | fuse.userAllowOther = true; | 631 | fuse.userAllowOther = true; |
| 632 | captive-browser = { | ||
| 633 | enable = true; | ||
| 634 | interface = "wlp82s0"; | ||
| 635 | }; | ||
| 684 | }; | 636 | }; |
| 685 | 637 | ||
| 686 | services.pcscd.enable = true; | 638 | services.pcscd.enable = true; |
| @@ -707,7 +659,7 @@ in { | |||
| 707 | "org.freedesktop.impl.portal.OpenFile" = ["gtk"]; | 659 | "org.freedesktop.impl.portal.OpenFile" = ["gtk"]; |
| 708 | "org.freedesktop.impl.portal.Access" = ["gtk"]; | 660 | "org.freedesktop.impl.portal.Access" = ["gtk"]; |
| 709 | "org.freedesktop.impl.portal.Notification" = ["gtk"]; | 661 | "org.freedesktop.impl.portal.Notification" = ["gtk"]; |
| 710 | "org.freedesktop.impl.portal.Secret" = ["gnome-keyring"]; | 662 | "org.freedesktop.impl.portal.Secret" = ["none"]; |
| 711 | "org.freedesktop.impl.portal.Inhibit" = ["none"]; | 663 | "org.freedesktop.impl.portal.Inhibit" = ["none"]; |
| 712 | }; | 664 | }; |
| 713 | }; | 665 | }; |
| @@ -717,7 +669,7 @@ in { | |||
| 717 | directories = [ | 669 | directories = [ |
| 718 | "/nix" | 670 | "/nix" |
| 719 | "/root" | 671 | "/root" |
| 720 | "/home" | 672 | "/home" |
| 721 | "/var/log" | 673 | "/var/log" |
| 722 | "/var/lib/sops-nix" | 674 | "/var/lib/sops-nix" |
| 723 | "/var/lib/nixos" | 675 | "/var/lib/nixos" |
| @@ -727,33 +679,19 @@ in { | |||
| 727 | "/var/lib/bluetooth" | 679 | "/var/lib/bluetooth" |
| 728 | "/var/lib/upower" | 680 | "/var/lib/upower" |
| 729 | "/var/lib/postfix" | 681 | "/var/lib/postfix" |
| 682 | "/var/lib/regreet" | ||
| 730 | "/etc/NetworkManager/system-connections" | 683 | "/etc/NetworkManager/system-connections" |
| 731 | { directory = "/var/uucp"; user = "uucp"; group = "uucp"; mode = "0700"; } | 684 | config.boot.lanzaboote.pkiBundle |
| 732 | { directory = "/var/spool/uucp"; user = "uucp"; group = "uucp"; mode = "0750"; } | ||
| 733 | ]; | 685 | ]; |
| 734 | files = [ | 686 | files = [ |
| 735 | ]; | 687 | ]; |
| 688 | timezone = true; | ||
| 736 | }; | 689 | }; |
| 737 | 690 | ||
| 738 | systemd.services.timezone = { | 691 | security.pam.services.quickshell = {}; |
| 739 | wantedBy = [ "multi-user.target" ]; | ||
| 740 | serviceConfig = { | ||
| 741 | Type = "oneshot"; | ||
| 742 | RemainAfterExit = true; | ||
| 743 | ExecStart = "${pkgs.coreutils}/bin/cp -vP /.bcachefs/etc/localtime /etc/localtime"; | ||
| 744 | ExecStop = "${pkgs.coreutils}/bin/cp -vP /etc/localtime /.bcachefs/etc/localtime"; | ||
| 745 | }; | ||
| 746 | }; | ||
| 747 | services.tzupdate.enable = true; | ||
| 748 | |||
| 749 | security.pam.services.gtklock = {}; | ||
| 750 | 692 | ||
| 751 | home-manager.sharedModules = [ flakeInputs.nixVirt.homeModules.default ]; | 693 | home-manager.sharedModules = [ flakeInputs.nixVirt.homeModules.default ]; |
| 752 | 694 | ||
| 753 | environment.pathsToLink = [ | ||
| 754 | "share/zsh" | ||
| 755 | ]; | ||
| 756 | |||
| 757 | system.stateVersion = "24.11"; | 695 | system.stateVersion = "24.11"; |
| 758 | }; | 696 | }; |
| 759 | } | 697 | } |
diff --git a/hosts/sif/email/default.nix b/hosts/sif/email/default.nix new file mode 100644 index 00000000..bebf7980 --- /dev/null +++ b/hosts/sif/email/default.nix | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | { config, lib, pkgs, ... }: | ||
| 2 | { | ||
| 3 | services.postfix = { | ||
| 4 | enable = true; | ||
| 5 | enableSmtp = false; | ||
| 6 | enableSubmission = false; | ||
| 7 | setSendmail = true; | ||
| 8 | # networksStyle = "host"; | ||
| 9 | settings.main = { | ||
| 10 | recpipient_delimiter = "+"; | ||
| 11 | mydestination = []; | ||
| 12 | myhostname = "sif.midgard.yggdrasil"; | ||
| 13 | |||
| 14 | mydomain = "yggdrasil.li"; | ||
| 15 | |||
| 16 | local_transport = "error:5.1.1 No local delivery"; | ||
| 17 | alias_database = []; | ||
| 18 | alias_maps = []; | ||
| 19 | local_recipient_maps = []; | ||
| 20 | |||
| 21 | inet_interfaces = "loopback-only"; | ||
| 22 | |||
| 23 | message_size_limit = 0; | ||
| 24 | |||
| 25 | authorized_submit_users = "inline:{ gkleen= }"; | ||
| 26 | authorized_flush_users = "inline:{ gkleen= }"; | ||
| 27 | authorized_mailq_users = "inline:{ gkleen= }"; | ||
| 28 | |||
| 29 | smtp_generic_maps = "inline:{ root=root+sif }"; | ||
| 30 | |||
| 31 | mynetworks = ["127.0.0.0/8" "[::1]/128"]; | ||
| 32 | smtpd_client_restrictions = ["permit_mynetworks" "reject"]; | ||
| 33 | smtpd_relay_restrictions = ["permit_mynetworks" "reject"]; | ||
| 34 | |||
| 35 | sender_dependent_default_transport_maps = ''regexp:${pkgs.writeText "sender_relay" '' | ||
| 36 | /@(cip|stud)\.ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtp.ifi.lmu.de | ||
| 37 | /@ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtpin1.ifi.lmu.de:587 | ||
| 38 | /@math(ematik)?\.(lmu|uni-muenchen)\.de$/ smtps:smtp.math.lmu.de:465 | ||
| 39 | /@(campus\.)?lmu\.de$/ smtp:postout.lrz.de | ||
| 40 | ''}''; | ||
| 41 | sender_bcc_maps = ''regexp:${pkgs.writeText "sender_bcc" '' | ||
| 42 | /^uni2work(-[^@]*)?@ifi\.lmu\.de$/ uni2work@ifi.lmu.de | ||
| 43 | /@ifi\.lmu\.de$/ gregor.kleen@ifi.lmu.de | ||
| 44 | ''}''; | ||
| 45 | relayhost = ["[surtr.yggdrasil.li]:465"]; | ||
| 46 | default_transport = "relay"; | ||
| 47 | |||
| 48 | smtp_sasl_auth_enable = true; | ||
| 49 | smtp_sender_dependent_authentication = true; | ||
| 50 | smtp_sasl_tls_security_options = "noanonymous"; | ||
| 51 | smtp_sasl_mechanism_filter = ["plain"]; | ||
| 52 | smtp_sasl_password_maps = "regexp:/run/credentials/postfix.service/sasl_passwd"; | ||
| 53 | smtp_cname_overrides_servername = false; | ||
| 54 | smtp_always_send_ehlo = true; | ||
| 55 | smtp_tls_security_level = "dane"; | ||
| 56 | |||
| 57 | smtp_tls_loglevel = "1"; | ||
| 58 | smtp_dns_support_level = "dnssec"; | ||
| 59 | }; | ||
| 60 | settings.master = { | ||
| 61 | submission = { | ||
| 62 | type = "inet"; | ||
| 63 | private = false; | ||
| 64 | command = "smtpd"; | ||
| 65 | args = [ | ||
| 66 | "-o" "syslog_name=postfix/$service_name" | ||
| 67 | ]; | ||
| 68 | }; | ||
| 69 | smtp = { }; | ||
| 70 | smtps = { | ||
| 71 | type = "unix"; | ||
| 72 | private = true; | ||
| 73 | privileged = true; | ||
| 74 | chroot = false; | ||
| 75 | command = "smtp"; | ||
| 76 | args = [ | ||
| 77 | "-o" "smtp_tls_wrappermode=yes" | ||
| 78 | "-o" "smtp_tls_security_level=encrypt" | ||
| 79 | ]; | ||
| 80 | }; | ||
| 81 | relay = { | ||
| 82 | command = "smtp"; | ||
| 83 | args = [ | ||
| 84 | "-o" "smtp_fallback_relay=" | ||
| 85 | "-o" "smtp_tls_security_level=verify" | ||
| 86 | "-o" "smtp_tls_wrappermode=yes" | ||
| 87 | "-o" "smtp_tls_cert_file=${./relay.crt}" | ||
| 88 | "-o" "smtp_tls_key_file=/run/credentials/postfix.service/relay.key" | ||
| 89 | ]; | ||
| 90 | }; | ||
| 91 | }; | ||
| 92 | }; | ||
| 93 | |||
| 94 | systemd.services.postfix = { | ||
| 95 | serviceConfig.LoadCredential = [ | ||
| 96 | "sasl_passwd:${config.sops.secrets."postfix-sasl-passwd".path}" | ||
| 97 | "relay.key:${config.sops.secrets."relay-key".path}" | ||
| 98 | ]; | ||
| 99 | }; | ||
| 100 | |||
| 101 | sops.secrets = { | ||
| 102 | postfix-sasl-passwd = { | ||
| 103 | key = "sasl-passwd"; | ||
| 104 | sopsFile = ./secrets.yaml; | ||
| 105 | }; | ||
| 106 | relay-key = { | ||
| 107 | format = "binary"; | ||
| 108 | sopsFile = ./relay.key; | ||
| 109 | }; | ||
| 110 | }; | ||
| 111 | } | ||
diff --git a/hosts/sif/email/relay.crt b/hosts/sif/email/relay.crt new file mode 100644 index 00000000..ac13e7cb --- /dev/null +++ b/hosts/sif/email/relay.crt | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | -----BEGIN CERTIFICATE----- | ||
| 2 | MIIBjDCCAQygAwIBAgIPQAAAAGgLfNoL/PSMAsutMAUGAytlcTAXMRUwEwYDVQQD | ||
| 3 | DAx5Z2dkcmFzaWwubGkwHhcNMjUwNDI1MTIwOTQ1WhcNMzUwNDI2MTIxNDQ1WjAR | ||
| 4 | MQ8wDQYDVQQDDAZna2xlZW4wKjAFBgMrZXADIQB3outi3/3F4YO7Q97WAAaMHW0a | ||
| 5 | m+Blldrgee+EZnWnD6N1MHMwHwYDVR0jBBgwFoAUTtn+VjMw6Ge1f68KD8dT1CWn | ||
| 6 | l3YwHQYDVR0OBBYEFFOa4rYZYMbXUVdKv98NB504GUhjMA4GA1UdDwEB/wQEAwID | ||
| 7 | 6DAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAUGAytlcQNzABC0 | ||
| 8 | 0UgIt7gLZrU1TmzGoqPBris8R1DbKOJacicF5CU0MIIjHcX7mPFW8KtB4qm6KcPq | ||
| 9 | kF6IaEPmgKpX3Nubk8HJik9vhIy9ysfINcVTvzXx8pO1bxbvREJRyA/apj10nzav | ||
| 10 | yauId0cXHvN6g5RLAMsMAA== | ||
| 11 | -----END CERTIFICATE----- | ||
diff --git a/hosts/sif/email/relay.key b/hosts/sif/email/relay.key new file mode 100644 index 00000000..412a44e0 --- /dev/null +++ b/hosts/sif/email/relay.key | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:lBlTuzOS75pvRmcTKT4KhHMH44RlE2SvCFAUP+GfsXws1Uai7DZ1MmbhvxxCa+pcLW19+sQYxrXLRNZWby1yOeKBJ2UQeYV5LOk9LSL/WIE3FZkCo5Dv0O0gSFKjjb61WN22a4JnHbLWADf/mLT3GZv91XfvFDo=,iv:ho8wQH3UNzX9JPW5gVcUGtxZzdVwsMFus0Z4KYe5t48=,tag:dAgZyHOva2xVVhE1nTl+lg==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB6eTVRSUdFNUZGZmcxSUlT\nWmlsOGNyWXIzMGNTZjlKbXlhcEdZUXFRVkR3Cll0T0RMd0h2UW16QkR3SHlhYmNZ\nNDFrYXh3Rkp5NWsvcWc3UFJJaHVwT1UKLS0tIHhXVEI0VHBZVkpDQ1FzWENjMmJH\nb1FQWXVUUTBiZ1pKWG00MTNqVEo2SjAKK3VOU+QgRuxWYWEcrJiVMRFCprBICz4F\ngD+9zuPUzPezyJkYwTs+M+wX5GYkXppqm5W58yQLS2UDD38sr+SRjg==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | }, | ||
| 9 | { | ||
| 10 | "recipient": "age1fj65apkhfkrwyv5tx6zcs9nkjg8267fy733qph30sc7zfn7vapjqkd5kne", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBzWmJmZDVFazN2bDY1TkNG\nNXpJN2twMFFjZUxMTVdSNzJwQTFiYktrcGdrCjk4eFVHTko0bFVMSlFFWm9tbjMr\nbWNHMEQ1Rm1qUVhodlB1RGw2aDc4TUEKLS0tIERBK0J5NkN4OXJEZ1ZOZXhNc1Jm\naWNnUmZGbTIxdmNkYi9TZ2h2bGs3MVEKPQGaEf7M/5/xvSOfawpIp50fB3QfFSuz\nPgkrPMneaBeUx+uBYMyEFX4rpzLIBR3pnYMjAfoc+bjWaOtGQuEqyQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2025-04-25T12:14:44Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:pObl2bJA93az9E3Ya+hA3ekI8TKKZ9NNTi0KzmWZBOiQwi9FuQYtpnmmT80L1KXWyOKJV6wGdAri3mNe/ue2S0TziSbQ/4+Dj4ubFKgkH7thb5q2dFyxw5FzhYzRQiXFqD/pxcNN9uL0lQI2Al0Eci0zX8Kcd1rAQ6RzLEoSmco=,iv:zo/3QFKTUEDxLy1k5yyU7Z1JMZ7cKdYUc6GHjaTTZKQ=,tag:f63Eja3lBfwJCYAOyEt56g==,type:str]", | ||
| 16 | "unencrypted_suffix": "_unencrypted", | ||
| 17 | "version": "3.10.2" | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/hosts/sif/mail/secrets.yaml b/hosts/sif/email/secrets.yaml index 3c74b710..3c74b710 100644 --- a/hosts/sif/mail/secrets.yaml +++ b/hosts/sif/email/secrets.yaml | |||
diff --git a/hosts/sif/greetd/default.nix b/hosts/sif/greetd/default.nix index 37ca13c5..081b6346 100644 --- a/hosts/sif/greetd/default.nix +++ b/hosts/sif/greetd/default.nix | |||
| @@ -1,49 +1,92 @@ | |||
| 1 | { pkgs, ... }: | 1 | { config, pkgs, lib, flakeInputs, ... }: |
| 2 | { | 2 | |
| 3 | let | ||
| 4 | gkleenConfig = config.home-manager.users."gkleen"; | ||
| 5 | toIni = lib.generators.toINI { | ||
| 6 | mkKeyValue = | ||
| 7 | key: value: | ||
| 8 | let | ||
| 9 | value' = if lib.isBool value then lib.boolToString value else toString value; | ||
| 10 | in | ||
| 11 | "${lib.escape [ "=" ] key}=${value'}"; | ||
| 12 | }; | ||
| 13 | toDconfIni = let | ||
| 14 | gvariant = import (flakeInputs.home-manager + "/modules/lib/gvariant.nix") { inherit lib; }; | ||
| 15 | mkIniKeyValue = key: value: "${key}=${toString (gvariant.mkValue value)}"; | ||
| 16 | in lib.generators.toINI { mkKeyValue = mkIniKeyValue; }; | ||
| 17 | in { | ||
| 3 | config = { | 18 | config = { |
| 4 | services.greetd = { | 19 | services.greetd = { |
| 5 | enable = true; | 20 | enable = true; |
| 6 | # settings.default_session.command = let | 21 | settings.default_session.command = lib.getExe (pkgs.writeShellApplication { |
| 7 | # cfg = config.programs.regreet; | 22 | name = "sway"; |
| 8 | # in pkgs.writeShellScript "greeter" '' | 23 | runtimeInputs = [ pkgs.sway pkgs.fontconfig ]; |
| 9 | # modprobe -r nvidia_drm | 24 | runtimeEnv = { |
| 25 | XDG_DATA_DIRS = lib.makeSearchPath "share" [ | ||
| 26 | pkgs.equilux-theme pkgs.paper-icon-theme pkgs.fira | ||
| 27 | ]; | ||
| 28 | QT_PLUGIN_PATH = lib.makeSearchPath (pkgs.qt6.qtbase.qtPluginPrefix) [ | ||
| 29 | pkgs.qt6Packages.qtbase | ||
| 30 | ]; | ||
| 31 | QML2_IMPORT_PATH = lib.makeSearchPath (pkgs.qt6.qtbase.qtQmlPrefix) [ | ||
| 32 | pkgs.qt6Packages.qtbase | ||
| 33 | ]; | ||
| 34 | QT_QPA_PLATFORMTHEME = "gtk3"; | ||
| 35 | XDG_CONFIG_DIR = pkgs.symlinkJoin { | ||
| 36 | name = "config"; | ||
| 37 | paths = [ | ||
| 38 | (pkgs.writeTextDir "gtk-3.0/settings.ini" (toIni { | ||
| 39 | Settings = { | ||
| 40 | gtk-font-name = "Fira Sans 10"; | ||
| 41 | gtk-theme-name = "Equilux-compact"; | ||
| 42 | gtk-icon-theme-name = "Paper-Mono-Dark"; | ||
| 43 | }; | ||
| 44 | })) | ||
| 45 | ]; | ||
| 46 | }; | ||
| 47 | # XDG_CACHE_HOME = "/var/cache/greetd/greeter"; | ||
| 48 | # XDG_CONFIG_HOME = "/var/cache/greetd/greeter/config"; | ||
| 49 | }; | ||
| 50 | text = '' | ||
| 51 | exec &>/tmp/sway-$$.log | ||
| 52 | |||
| 53 | unset MANAGERPID SYSTEMD_EXEC_PID | ||
| 54 | |||
| 55 | # ${lib.getExe' pkgs.coreutils "mkdir"} -p ''${XDG_CONFIG_HOME}/dconf | ||
| 56 | ${lib.getExe pkgs.dconf} load / < ${pkgs.writeText "dconf.ini" (toDconfIni { | ||
| 57 | "org/gnome/desktop/interface" = { | ||
| 58 | "color-scheme" = "prefer-dark"; | ||
| 59 | "font-name" = "Fira Sans 10"; | ||
| 60 | "gtk-theme" = "Equilux-compact"; | ||
| 61 | "icon-theme" = "Paper-Mono-Dark"; | ||
| 62 | }; | ||
| 63 | })} | ||
| 64 | |||
| 65 | exec sway --unsupported-gpu --config ${pkgs.writeText "sway-config" '' | ||
| 66 | exec "${lib.getExe' config.systemd.package "systemctl"} --user import-environment {,WAYLAND_}DISPLAY SWAYSOCK; ${lib.getExe gkleenConfig.programs.quickshell.package} --path ${gkleenConfig.xdg.configFile."quickshell".source}/displaymanager.qml; swaymsg exit" | ||
| 10 | 67 | ||
| 11 | # exec ${pkgs.dbus}/bin/dbus-run-session ${lib.getExe pkgs.cage} ${lib.escapeShellArgs cfg.cageArgs} -- ${lib.getExe cfg.package} | 68 | input type:keyboard { |
| 12 | # ''; | 69 | xkb_layout "us,us" |
| 70 | xkb_variant "dvp," | ||
| 71 | xkb_options "compose:caps,grp:win_space_toggle" | ||
| 72 | } | ||
| 73 | |||
| 74 | output eDP-1 scale 1.5 | ||
| 75 | ''} | ||
| 76 | ''; | ||
| 77 | }); | ||
| 13 | }; | 78 | }; |
| 14 | systemd.services.greetd.environment = { | 79 | |
| 15 | XKB_DEFAULT_LAYOUT = "us,us"; | 80 | # security.pam.services.greetd.fprintAuth = false; |
| 16 | XKB_DEFAULT_VARIANT = "dvp,"; | 81 | |
| 17 | XKB_DEFAULT_OPTIONS = "compose:caps,grp:win_space_toggle"; | 82 | systemd.services.greetd.serviceConfig = { |
| 83 | ExecStartPre = ''${lib.getExe' pkgs.coreutils "install"} -d -o greeter -g greeter -m 0700 ''${CACHE_DIRECTORY}/greeter''; | ||
| 84 | # CacheDirectory = "greetd"; | ||
| 18 | }; | 85 | }; |
| 19 | programs.regreet = { | 86 | |
| 20 | enable = true; | 87 | users.users.greeter = { |
| 21 | theme = { | 88 | home = "/var/lib/greeter"; |
| 22 | package = pkgs.equilux-theme; | 89 | createHome = true; |
| 23 | name = "Equilux-compact"; | ||
| 24 | }; | ||
| 25 | iconTheme = { | ||
| 26 | package = pkgs.paper-icon-theme; | ||
| 27 | name = "Paper-Mono-Dark"; | ||
| 28 | }; | ||
| 29 | font = { | ||
| 30 | package = pkgs.fira; | ||
| 31 | name = "Fira Sans"; | ||
| 32 | # size = 6; | ||
| 33 | }; | ||
| 34 | cageArgs = [ "-s" "-m" "last" ]; | ||
| 35 | settings = { | ||
| 36 | GTK.application_prefer_dark_theme = true; | ||
| 37 | widget.clock.format = "%F %H:%M:%S%:z"; | ||
| 38 | background = { | ||
| 39 | path = pkgs.runCommand "wallpaper.png" { | ||
| 40 | buildInputs = with pkgs; [ imagemagick ]; | ||
| 41 | } '' | ||
| 42 | magick ${./wallpaper.png} -filter Gaussian -resize 6.25% -define filter:sigma=2.5 -resize 1600% "$out" | ||
| 43 | ''; | ||
| 44 | fit = "Cover"; | ||
| 45 | }; | ||
| 46 | }; | ||
| 47 | }; | 90 | }; |
| 48 | }; | 91 | }; |
| 49 | } | 92 | } |
diff --git a/hosts/sif/greetd/wallpaper.png b/hosts/sif/greetd/wallpaper.png deleted file mode 100644 index 20fc761a..00000000 --- a/hosts/sif/greetd/wallpaper.png +++ /dev/null | |||
| Binary files differ | |||
diff --git a/hosts/sif/hw.nix b/hosts/sif/hw.nix index 1bcf0261..e567c37d 100644 --- a/hosts/sif/hw.nix +++ b/hosts/sif/hw.nix | |||
| @@ -25,7 +25,7 @@ | |||
| 25 | # system.etc.overlay.enable = false; | 25 | # system.etc.overlay.enable = false; |
| 26 | 26 | ||
| 27 | boot.initrd.systemd.packages = [ | 27 | boot.initrd.systemd.packages = [ |
| 28 | (pkgs.writeTextDir "/etc/systemd/system/\\x2ebcachefs.mount.d/block_scan.conf" '' | 28 | (pkgs.writeTextDir "/etc/systemd/system/sysroot-.bcachefs.mount.d/block_scan.conf" '' |
| 29 | [Mount] | 29 | [Mount] |
| 30 | Environment=BCACHEFS_BLOCK_SCAN=1 | 30 | Environment=BCACHEFS_BLOCK_SCAN=1 |
| 31 | '') | 31 | '') |
diff --git a/hosts/sif/mail/default.nix b/hosts/sif/mail/default.nix deleted file mode 100644 index 8d6cd705..00000000 --- a/hosts/sif/mail/default.nix +++ /dev/null | |||
| @@ -1,70 +0,0 @@ | |||
| 1 | { config, lib, pkgs, ... }: | ||
| 2 | { | ||
| 3 | services.postfix = { | ||
| 4 | enable = true; | ||
| 5 | enableSmtp = true; | ||
| 6 | enableSubmission = false; | ||
| 7 | setSendmail = true; | ||
| 8 | networksStyle = "host"; | ||
| 9 | hostname = "sif.midgard.yggdrasil"; | ||
| 10 | destination = []; | ||
| 11 | relayHost = "uucp:ymir"; | ||
| 12 | recipientDelimiter = "+"; | ||
| 13 | masterConfig = { | ||
| 14 | uucp = { | ||
| 15 | type = "unix"; | ||
| 16 | private = true; | ||
| 17 | privileged = true; | ||
| 18 | chroot = false; | ||
| 19 | command = "pipe"; | ||
| 20 | args = [ "flags=Fqhu" "user=uucp" ''argv=${config.security.wrapperDir}/uux -z -a $sender - $nexthop!rmail ($recipient)'' ]; | ||
| 21 | }; | ||
| 22 | smtps = { | ||
| 23 | type = "unix"; | ||
| 24 | private = true; | ||
| 25 | privileged = true; | ||
| 26 | chroot = false; | ||
| 27 | command = "smtp"; | ||
| 28 | args = [ "-o" "smtp_tls_wrappermode=yes" "-o" "smtp_tls_security_level=encrypt" ]; | ||
| 29 | }; | ||
| 30 | }; | ||
| 31 | config = { | ||
| 32 | default_transport = "uucp:ymir"; | ||
| 33 | |||
| 34 | inet_interfaces = "loopback-only"; | ||
| 35 | |||
| 36 | authorized_submit_users = ["!uucp" "static:anyone"]; | ||
| 37 | message_size_limit = "0"; | ||
| 38 | |||
| 39 | sender_dependent_default_transport_maps = ''regexp:${pkgs.writeText "sender_relay" '' | ||
| 40 | /@(cip|stud)\.ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtp.ifi.lmu.de | ||
| 41 | /@ifi\.(lmu|uni-muenchen)\.de$/ smtp:smtpin1.ifi.lmu.de:587 | ||
| 42 | /@math(ematik)?\.(lmu|uni-muenchen)\.de$/ smtps:smtp.math.lmu.de:465 | ||
| 43 | /@(campus\.)?lmu\.de$/ smtp:postout.lrz.de | ||
| 44 | ''}''; | ||
| 45 | sender_bcc_maps = ''regexp:${pkgs.writeText "sender_bcc" '' | ||
| 46 | /^uni2work(-[^@]*)?@ifi\.lmu\.de$/ uni2work@ifi.lmu.de | ||
| 47 | /@ifi\.lmu\.de$/ gregor.kleen@ifi.lmu.de | ||
| 48 | ''}''; | ||
| 49 | |||
| 50 | smtp_sasl_auth_enable = true; | ||
| 51 | smtp_sender_dependent_authentication = true; | ||
| 52 | smtp_sasl_tls_security_options = "noanonymous"; | ||
| 53 | smtp_sasl_mechanism_filter = ["plain"]; | ||
| 54 | smtp_sasl_password_maps = "regexp:/var/db/postfix/sasl_passwd"; | ||
| 55 | smtp_cname_overrides_servername = false; | ||
| 56 | smtp_always_send_ehlo = true; | ||
| 57 | smtp_tls_security_level = "dane"; | ||
| 58 | |||
| 59 | smtp_tls_loglevel = "1"; | ||
| 60 | smtp_dns_support_level = "dnssec"; | ||
| 61 | }; | ||
| 62 | }; | ||
| 63 | |||
| 64 | sops.secrets.postfix-sasl-passwd = { | ||
| 65 | key = "sasl-passwd"; | ||
| 66 | path = "/var/db/postfix/sasl_passwd"; | ||
| 67 | owner = "postfix"; | ||
| 68 | sopsFile = ./secrets.yaml; | ||
| 69 | }; | ||
| 70 | } | ||
diff --git a/hosts/sif/ruleset.nft b/hosts/sif/ruleset.nft index 2af8b2ee..62339f69 100644 --- a/hosts/sif/ruleset.nft +++ b/hosts/sif/ruleset.nft | |||
| @@ -61,7 +61,7 @@ table inet filter { | |||
| 61 | counter mosh-rx {} | 61 | counter mosh-rx {} |
| 62 | counter wg-rx {} | 62 | counter wg-rx {} |
| 63 | counter yggdrasil-gre-rx {} | 63 | counter yggdrasil-gre-rx {} |
| 64 | counter quickserve-rx {} | 64 | counter miniserve-rx {} |
| 65 | counter ausweisapp2-rx {} | 65 | counter ausweisapp2-rx {} |
| 66 | 66 | ||
| 67 | counter established-rx {} | 67 | counter established-rx {} |
| @@ -81,7 +81,7 @@ table inet filter { | |||
| 81 | counter mosh-tx {} | 81 | counter mosh-tx {} |
| 82 | counter wg-tx {} | 82 | counter wg-tx {} |
| 83 | counter yggdrasil-gre-tx {} | 83 | counter yggdrasil-gre-tx {} |
| 84 | counter quickserve-tx {} | 84 | counter miniserve-tx {} |
| 85 | 85 | ||
| 86 | counter tx {} | 86 | counter tx {} |
| 87 | 87 | ||
| @@ -134,7 +134,7 @@ table inet filter { | |||
| 134 | tcp dport 22 counter name ssh-rx accept | 134 | tcp dport 22 counter name ssh-rx accept |
| 135 | udp dport 60000-61000 counter name mosh-rx accept | 135 | udp dport 60000-61000 counter name mosh-rx accept |
| 136 | 136 | ||
| 137 | tcp dport 8000 counter name quickserve-rx accept | 137 | tcp dport 8080 counter name miniserve-rx accept |
| 138 | udp dport 24727 counter name ausweisapp2-rx accept | 138 | udp dport 24727 counter name ausweisapp2-rx accept |
| 139 | 139 | ||
| 140 | udp dport 51820-51822 counter name wg-rx accept | 140 | udp dport 51820-51822 counter name wg-rx accept |
| @@ -173,7 +173,7 @@ table inet filter { | |||
| 173 | udp sport 51820-51822 counter name wg-tx | 173 | udp sport 51820-51822 counter name wg-tx |
| 174 | iifname "yggdrasil-wg-*" meta l4proto gre counter name yggdrasil-gre-tx | 174 | iifname "yggdrasil-wg-*" meta l4proto gre counter name yggdrasil-gre-tx |
| 175 | 175 | ||
| 176 | tcp sport 8000 counter name quickserve-tx accept | 176 | tcp sport 8080 counter name miniserve-tx accept |
| 177 | 177 | ||
| 178 | oifname virbr0 udp sport 67 counter name libvirt-dhcp accept | 178 | oifname virbr0 udp sport 67 counter name libvirt-dhcp accept |
| 179 | oifname virbr0 udp sport 547 counter name libvirt-dhcp accept | 179 | oifname virbr0 udp sport 547 counter name libvirt-dhcp accept |
diff --git a/hosts/surtr/audiobookshelf.nix b/hosts/surtr/audiobookshelf.nix new file mode 100644 index 00000000..728851a4 --- /dev/null +++ b/hosts/surtr/audiobookshelf.nix | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | { config, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | security.acme.rfc2136Domains = { | ||
| 6 | "audiobookshelf.yggdrasil.li" = { | ||
| 7 | restartUnits = ["nginx.service"]; | ||
| 8 | }; | ||
| 9 | }; | ||
| 10 | |||
| 11 | services.nginx = { | ||
| 12 | upstreams."audiobookshelf" = { | ||
| 13 | servers = { | ||
| 14 | "[2a03:4000:52:ada:4:1::]:28982" = {}; | ||
| 15 | }; | ||
| 16 | extraConfig = '' | ||
| 17 | keepalive 8; | ||
| 18 | ''; | ||
| 19 | }; | ||
| 20 | virtualHosts = { | ||
| 21 | "audiobookshelf.yggdrasil.li" = { | ||
| 22 | kTLS = true; | ||
| 23 | http3 = true; | ||
| 24 | forceSSL = true; | ||
| 25 | sslCertificate = "/run/credentials/nginx.service/audiobookshelf.yggdrasil.li.pem"; | ||
| 26 | sslCertificateKey = "/run/credentials/nginx.service/audiobookshelf.yggdrasil.li.key.pem"; | ||
| 27 | sslTrustedCertificate = "/run/credentials/nginx.service/audiobookshelf.yggdrasil.li.chain.pem"; | ||
| 28 | extraConfig = '' | ||
| 29 | charset utf-8; | ||
| 30 | ''; | ||
| 31 | |||
| 32 | locations = { | ||
| 33 | "/".extraConfig = '' | ||
| 34 | proxy_pass http://audiobookshelf; | ||
| 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 | "audiobookshelf.yggdrasil.li.key.pem:${config.security.acme.certs."audiobookshelf.yggdrasil.li".directory}/key.pem" | ||
| 60 | "audiobookshelf.yggdrasil.li.pem:${config.security.acme.certs."audiobookshelf.yggdrasil.li".directory}/fullchain.pem" | ||
| 61 | "audiobookshelf.yggdrasil.li.chain.pem:${config.security.acme.certs."audiobookshelf.yggdrasil.li".directory}/chain.pem" | ||
| 62 | ]; | ||
| 63 | }; | ||
| 64 | }; | ||
| 65 | }; | ||
| 66 | } | ||
diff --git a/hosts/surtr/bifrost/default.nix b/hosts/surtr/bifrost/default.nix index fbfde757..52ab43f5 100644 --- a/hosts/surtr/bifrost/default.nix +++ b/hosts/surtr/bifrost/default.nix | |||
| @@ -18,7 +18,7 @@ in { | |||
| 18 | ListenPort = 51822; | 18 | ListenPort = 51822; |
| 19 | }; | 19 | }; |
| 20 | wireguardPeers = [ | 20 | wireguardPeers = [ |
| 21 | { AllowedIPs = [ "2a03:4000:52:ada:4:1::/96" ]; | 21 | { AllowedIPs = [ "2a03:4000:52:ada:4:1::/96" "2a03:4000:52:ada:6::/80" ]; |
| 22 | PublicKey = trim (readFile ../../vidhar/network/bifrost/vidhar.pub); | 22 | PublicKey = trim (readFile ../../vidhar/network/bifrost/vidhar.pub); |
| 23 | } | 23 | } |
| 24 | ]; | 24 | ]; |
| @@ -34,6 +34,8 @@ in { | |||
| 34 | routes = [ | 34 | routes = [ |
| 35 | { Destination = "2a03:4000:52:ada:4::/80"; | 35 | { Destination = "2a03:4000:52:ada:4::/80"; |
| 36 | } | 36 | } |
| 37 | { Destination = "2a03:4000:52:ada:6::/80"; | ||
| 38 | } | ||
| 37 | ]; | 39 | ]; |
| 38 | linkConfig = { | 40 | linkConfig = { |
| 39 | RequiredForOnline = false; | 41 | RequiredForOnline = false; |
diff --git a/hosts/surtr/default.nix b/hosts/surtr/default.nix index 0a79d047..1c66df2b 100644 --- a/hosts/surtr/default.nix +++ b/hosts/surtr/default.nix | |||
| @@ -7,7 +7,7 @@ with lib; | |||
| 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 ./immich.nix | 9 | ./prometheus ./email ./vpn ./borg.nix ./etebase ./immich.nix |
| 10 | ./paperless.nix ./hledger.nix | 10 | ./paperless.nix ./hledger.nix ./audiobookshelf.nix ./kimai.nix |
| 11 | ]; | 11 | ]; |
| 12 | 12 | ||
| 13 | config = { | 13 | config = { |
| @@ -22,7 +22,6 @@ with lib; | |||
| 22 | device = "/dev/vda"; | 22 | device = "/dev/vda"; |
| 23 | }; | 23 | }; |
| 24 | 24 | ||
| 25 | |||
| 26 | tmp.useTmpfs = true; | 25 | tmp.useTmpfs = true; |
| 27 | 26 | ||
| 28 | zfs.devNodes = "/dev"; # /dev/vda2 does not show up in /dev/disk/by-id | 27 | zfs.devNodes = "/dev"; # /dev/vda2 does not show up in /dev/disk/by-id |
| @@ -31,7 +30,7 @@ with lib; | |||
| 31 | kernelPatches = [ | 30 | kernelPatches = [ |
| 32 | { name = "zswap-default"; | 31 | { name = "zswap-default"; |
| 33 | patch = null; | 32 | patch = null; |
| 34 | extraStructuredConfig = with lib.kernel; { | 33 | structuredExtraConfig = with lib.kernel; { |
| 35 | ZSWAP_DEFAULT_ON = yes; | 34 | ZSWAP_DEFAULT_ON = yes; |
| 36 | ZSWAP_SHRINKER_DEFAULT_ON = yes; | 35 | ZSWAP_SHRINKER_DEFAULT_ON = yes; |
| 37 | }; | 36 | }; |
diff --git a/hosts/surtr/dns/default.nix b/hosts/surtr/dns/default.nix index 5dd60190..8aca2b97 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" "immich.yggdrasil.li" "app.etesync.yggdrasil.li" "paperless.yggdrasil.li" "hledger.yggdrasil.li"]; | 160 | acmeDomains = ["surtr.yggdrasil.li" "yggdrasil.li" "etesync.yggdrasil.li" "immich.yggdrasil.li" "app.etesync.yggdrasil.li" "paperless.yggdrasil.li" "hledger.yggdrasil.li" "audiobookshelf.yggdrasil.li" "kimai.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"]; }; |
| @@ -169,15 +169,9 @@ in { | |||
| 169 | { domain = "kleen.li"; | 169 | { domain = "kleen.li"; |
| 170 | addACLs = { "kleen.li" = ["ymir_acme_acl"]; }; | 170 | addACLs = { "kleen.li" = ["ymir_acme_acl"]; }; |
| 171 | } | 171 | } |
| 172 | { domain = "xmpp.li"; | ||
| 173 | addACLs = { "xmpp.li" = ["ymir_acme_acl"]; }; | ||
| 174 | } | ||
| 175 | { domain = "synapse.li"; | 172 | { domain = "synapse.li"; |
| 176 | acmeDomains = ["element.synapse.li" "turn.synapse.li" "synapse.li"]; | 173 | acmeDomains = ["element.synapse.li" "turn.synapse.li" "synapse.li"]; |
| 177 | } | 174 | } |
| 178 | { domain = "dirty-haskell.org"; | ||
| 179 | addACLs = { "dirty-haskell.org" = ["ymir_acme_acl"]; }; | ||
| 180 | } | ||
| 181 | { domain = "praseodym.org"; | 175 | { domain = "praseodym.org"; |
| 182 | addACLs = { "praseodym.org" = ["ymir_acme_acl"]; }; | 176 | addACLs = { "praseodym.org" = ["ymir_acme_acl"]; }; |
| 183 | } | 177 | } |
diff --git a/hosts/surtr/dns/keys/audiobookshelf.yggdrasil.li_acme b/hosts/surtr/dns/keys/audiobookshelf.yggdrasil.li_acme new file mode 100644 index 00000000..e3af9966 --- /dev/null +++ b/hosts/surtr/dns/keys/audiobookshelf.yggdrasil.li_acme | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:5BLk/MZtQsLjpdKGiZjtOH1soIXB05MkEoPWyr5m27ho7H5udDjRZE0/OjvSlMzQNjFNJc+OwbeHwaJ8sPLCnXqoFHJXikAmi0gpdFC0uN0JGYCBT1EaK7j6yVHyiqFXo6xwVSCO/zP/fbjItiuqU+B4llGx5N2I4HQqRLFoW/35PZazkR15xI1zo8LeC8T+jm16apFQw2Ih/RqsJK7XlHqnXq9SzeA2qhgkCbJf6aJ6zDS7eQVFt7qHh2mVtV7Al++bZiIkJiNJ5SJO1ck18w2t9HwXBhfMFKXvfFW0I6kKur1qSJk=,iv:rxm5PwOzXDaK+nj2k3bUqlYbIFFA49ispyfamtQqU/A=,tag:7dOua39f+0JsLldLRLr1NQ==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwV1hIcWNNRVJXOG1xYlFK\nb1VxWG03dGVmS3dmQTltUTR0VmEzYjRFanlBCnE1M1BTZjNCQnpsNkU5ZlR5T3BR\nOHliWUV2bXMrK0w1K3JXbUE0dEhKdzgKLS0tIDdkUEZ4QUE0NUN2TXFYcklybjBS\nRVpxV2J6U1BnUng1dytWTGRkd0FrRGcKVaMCzcrX+BQqbmh95JEk3lRdfnv9uO8n\nwotKxp/+xX7GEF42BOzJ/mWcgyVjABlnWeVyaRxfpbCUrmNuFYO6XA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | }, | ||
| 9 | { | ||
| 10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkZmRFR0gwc1pjVVorK0t6\nVHU4U0xob09Dd2J5QUJXbndZdWp3cytpYWhRCnFnMlZ0cjNGdXN1RWEvQlVHSE1M\nMEdzSjlEb2NkaE1zMEJJOTlCMC9FVFEKLS0tIGJiTEJSRVZZbVUzZVFGeXQwYm1w\ndWVYQVdoVlJnRENTYzhnQm1BcWVRdW8K0ECfLVQBQuZWFFFjdHLcJBz+CDzsOOwh\nOA38qxHD4EWfXR1c3G8RDbow7nB2MGc1Zohc7qhtuTj2wL0qhVKWqA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2025-05-09T17:07:16Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:K5I6YXQXCUPHFBNVlXIdLLKqiNPVZh95KoHni2m16SdAvTyBab79SZ5xNvotrtKXp0iISCogEgdMm+OWbxYywEiZ+sUsxgx6RE5nAXruZOiAwzuyUr88qgCHTBZnKzgaDZlbYOgWB+LCzr8s5JbcTD4G6/RaXYyqmx7igygubHA=,iv:Zbij4eoDqoP5XYhAsDGBGqlcP5ACQAY/QngTmrJYRzs=,tag:WYxhUEZwmXkQmnpyOuP2Bg==,type:str]", | ||
| 16 | "unencrypted_suffix": "_unencrypted", | ||
| 17 | "version": "3.10.2" | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/hosts/surtr/dns/keys/kimai.yggdrasil.li_acme b/hosts/surtr/dns/keys/kimai.yggdrasil.li_acme new file mode 100644 index 00000000..bdfb135a --- /dev/null +++ b/hosts/surtr/dns/keys/kimai.yggdrasil.li_acme | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:sKFt4pH0Xn7Qm6JFMg/2N7Ht7jtMJukfN+U3dQaoYXPbhRJ+heEtDpXV/WP4AlfbfpIOgTPW3mcmQCwKFNhS00vEsQA4728FfXZzDDmZCa3hwg51wDbL7XUOr0OePgzi86lt0Q193K6CkGqEAa1vFIb//ElEfBYIwdATbmcoAsM3mHhz58X7c1qf8LNuB93o/1N2xXXZI3NWOhOjlviTc2DAhffXDwlMJSYUhldnwtDKmLM1mooJzLgm2p9w7gRD7WPqEqZFq9uFDK69P9uX5T9hFHg=,iv:rAE4sYxxLou4tyD4RWTp3LjQP0cya95coy1MvwfEK/U=,tag:u4SSk8SZFlj0ks7d6tDocw==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2KzdNUWhEcDB6QmtUTnVh\nNS9Nc2I4UjAzekxhRXo1UmY3SklPejV1TURJCm9NY2lVOERoMDFKTU56Mmh1NHEr\naGV4M1RoVldHV0xyc3Z0MnVqakpjMFUKLS0tIEYxSk9OUm9kMkdtcG5POWRGQVkx\nY1FEaXYwMGo0L0Z0aTVTZDA5aUFDWEUKJ+e/7lR/rNPNVnIy+wkiKiAYMxWp4L7q\nwnSTx451vSnxv9j3JWB43Y7XQC08cisWDj06ULw8FnEbKYOvTYj9mQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | }, | ||
| 9 | { | ||
| 10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBwOTU3dUEzaXM5T1VkbDRO\nMm14OG1mUkk2bDRhdnBsMHBkc3kvUzlyNlQwCktFSHJhMnhoQ2J6bC9vUHNLWTRC\nRFpYeHo3N2xjWUhjQnRwQ2Nrc1pRUmsKLS0tIDdPeFBVdkxDd1JWSmcxQ0tLMTBD\ncHU3VExZOUhYUlJvbGNoK3FMK2VIbGMKFk94P9aBY04CPIi983f3Aalgh4fnU+/K\n2mxawSMf9jz8704N5XJfmr2hwNy8hqLIn8bjsEMAPTfE1YBGga4w0g==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2025-05-24T09:42:23Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:diCeJGvBmM0Ng722eKoFwDe7pqZrdLPSLn5j9LfdaFI64BAbSbA5bAq4NFXqdJ1vttarD2A5rEafYoXUxP8228x2GhNyWUGW5AWgBjVPUc59gjs4wYKR5HlkVMIadhTwNheEyoEjrxX40GNBgCG7X3ocOtOYKbKECp433gdAPDg=,iv:d+yJMWj2RyFnveo2ZNrpNeV+amXM+H7vdC0A2F7mwjA=,tag:yjibG2iusdprp0ORghYWhw==,type:str]", | ||
| 16 | "unencrypted_suffix": "_unencrypted", | ||
| 17 | "version": "3.10.2" | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/hosts/surtr/dns/zones/email.nights.soa b/hosts/surtr/dns/zones/email.nights.soa index 913a88d4..34209a99 100644 --- a/hosts/surtr/dns/zones/email.nights.soa +++ b/hosts/surtr/dns/zones/email.nights.soa | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | $ORIGIN nights.email. | 1 | $ORIGIN nights.email. |
| 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 | 2023013000 ; serial | 4 | 2025060700 ; serial |
| 5 | 10800 ; refresh | 5 | 10800 ; refresh |
| 6 | 3600 ; retry | 6 | 3600 ; retry |
| 7 | 604800 ; expire | 7 | 604800 ; expire |
| @@ -27,11 +27,7 @@ $TTL 3600 | |||
| 27 | 27 | ||
| 28 | _acme-challenge IN NS ns.yggdrasil.li. | 28 | _acme-challenge IN NS ns.yggdrasil.li. |
| 29 | 29 | ||
| 30 | ymir._domainkey IN TXT ( | 30 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
| 31 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | ||
| 32 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
| 33 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
| 34 | ) | ||
| 35 | 31 | ||
| 36 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 32 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
| 37 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 33 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/dns/zones/li.141.soa b/hosts/surtr/dns/zones/li.141.soa index ab117f09..78d137bb 100644 --- a/hosts/surtr/dns/zones/li.141.soa +++ b/hosts/surtr/dns/zones/li.141.soa | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | $ORIGIN 141.li. | 1 | $ORIGIN 141.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 | 2025020900 ; serial | 4 | 2025060701 ; serial |
| 5 | 10800 ; refresh | 5 | 10800 ; refresh |
| 6 | 3600 ; retry | 6 | 3600 ; retry |
| 7 | 604800 ; expire | 7 | 604800 ; expire |
| @@ -45,11 +45,8 @@ ymir IN AAAA 2a03:4000:6:d004:: | |||
| 45 | ymir IN MX 0 ymir.yggdrasil.li | 45 | ymir IN MX 0 ymir.yggdrasil.li |
| 46 | ymir IN TXT "v=spf1 redirect=ymir.yggdrasil.li" | 46 | ymir IN TXT "v=spf1 redirect=ymir.yggdrasil.li" |
| 47 | 47 | ||
| 48 | ymir._domainkey IN TXT ( | 48 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
| 49 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 49 | surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li. |
| 50 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
| 51 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
| 52 | ) | ||
| 53 | 50 | ||
| 54 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 51 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
| 55 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 52 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/dns/zones/li.kleen.soa b/hosts/surtr/dns/zones/li.kleen.soa index a1c7d35a..5dd3e697 100644 --- a/hosts/surtr/dns/zones/li.kleen.soa +++ b/hosts/surtr/dns/zones/li.kleen.soa | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | $ORIGIN kleen.li. | 1 | $ORIGIN kleen.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 | 2023013000 ; serial | 4 | 2025060701 ; serial |
| 5 | 10800 ; refresh | 5 | 10800 ; refresh |
| 6 | 3600 ; retry | 6 | 3600 ; retry |
| 7 | 604800 ; expire | 7 | 604800 ; expire |
| @@ -27,11 +27,8 @@ $TTL 3600 | |||
| 27 | 27 | ||
| 28 | _acme-challenge IN NS ns.yggdrasil.li. | 28 | _acme-challenge IN NS ns.yggdrasil.li. |
| 29 | 29 | ||
| 30 | ymir._domainkey IN TXT ( | 30 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
| 31 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 31 | surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li. |
| 32 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
| 33 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
| 34 | ) | ||
| 35 | 32 | ||
| 36 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 33 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
| 37 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 34 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/dns/zones/li.synapse.soa b/hosts/surtr/dns/zones/li.synapse.soa index 086d4a85..247cf025 100644 --- a/hosts/surtr/dns/zones/li.synapse.soa +++ b/hosts/surtr/dns/zones/li.synapse.soa | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | $ORIGIN synapse.li. | 1 | $ORIGIN synapse.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 | 2023092100 ; serial | 4 | 2025060701 ; serial |
| 5 | 10800 ; refresh | 5 | 10800 ; refresh |
| 6 | 3600 ; retry | 6 | 3600 ; retry |
| 7 | 604800 ; expire | 7 | 604800 ; expire |
diff --git a/hosts/surtr/dns/zones/li.xmpp.soa b/hosts/surtr/dns/zones/li.xmpp.soa deleted file mode 100644 index a9e98fb4..00000000 --- a/hosts/surtr/dns/zones/li.xmpp.soa +++ /dev/null | |||
| @@ -1,43 +0,0 @@ | |||
| 1 | $ORIGIN xmpp.li. | ||
| 2 | $TTL 3600 | ||
| 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | ||
| 4 | 2023013000 ; serial | ||
| 5 | 10800 ; refresh | ||
| 6 | 3600 ; retry | ||
| 7 | 604800 ; expire | ||
| 8 | 3600 ; min TTL | ||
| 9 | ) | ||
| 10 | IN NS ns.yggdrasil.li. | ||
| 11 | IN NS ns.inwx.de. | ||
| 12 | IN NS ns2.inwx.de. | ||
| 13 | IN NS ns3.inwx.eu. | ||
| 14 | |||
| 15 | @ IN CAA 128 issue "letsencrypt.org; validationmethods=dns-01" | ||
| 16 | @ IN CAA 128 iodef "mailto:caa@yggdrasil.li" | ||
| 17 | |||
| 18 | @ IN A 188.68.51.254 | ||
| 19 | @ IN AAAA 2a03:4000:6:d004:: | ||
| 20 | @ IN MX 0 ymir.yggdrasil.li. | ||
| 21 | @ IN TXT "v=spf1 redirect=yggdrasil.li" | ||
| 22 | |||
| 23 | * IN A 188.68.51.254 | ||
| 24 | * IN AAAA 2a03:4000:6:d004:: | ||
| 25 | * IN MX 0 ymir.yggdrasil.li. | ||
| 26 | * IN TXT "v=spf1 redirect=yggdrasil.li" | ||
| 27 | |||
| 28 | _acme-challenge IN NS ns.yggdrasil.li. | ||
| 29 | |||
| 30 | ymir._domainkey IN TXT ( | ||
| 31 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | ||
| 32 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
| 33 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
| 34 | ) | ||
| 35 | |||
| 36 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | ||
| 37 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | ||
| 38 | |||
| 39 | _infinoted._tcp IN SRV 5 0 6523 ymir.yggdrasil.li. | ||
| 40 | |||
| 41 | _submission._tcp IN SRV 5 0 25 ymir.yggdrasil.li. | ||
| 42 | _imap._tcp IN SRV 5 0 143 ymir.yggdrasil.li. | ||
| 43 | _imaps._tcp IN SRV 5 0 993 ymir.yggdrasil.li. | ||
diff --git a/hosts/surtr/dns/zones/li.yggdrasil.soa b/hosts/surtr/dns/zones/li.yggdrasil.soa index 2ef120e6..500194ae 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 | 2025021901 ; serial | 4 | 2025060700 ; serial |
| 5 | 10800 ; refresh | 5 | 10800 ; refresh |
| 6 | 3600 ; retry | 6 | 3600 ; retry |
| 7 | 604800 ; expire | 7 | 604800 ; expire |
| @@ -93,12 +93,30 @@ _acme-challenge.hledger IN NS ns.yggdrasil.li. | |||
| 93 | 93 | ||
| 94 | hledger IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | 94 | hledger IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" |
| 95 | 95 | ||
| 96 | audiobookshelf IN A 202.61.241.61 | ||
| 97 | audiobookshelf IN AAAA 2a03:4000:52:ada:: | ||
| 98 | audiobookshelf IN MX 0 surtr.yggdrasil.li | ||
| 99 | audiobookshelf IN TXT "v=spf1 redirect=surtr.yggdrasil.li" | ||
| 100 | _acme-challenge.audiobookshelf IN NS ns.yggdrasil.li. | ||
| 101 | |||
| 102 | audiobookshelf IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | ||
| 103 | |||
| 104 | kimai IN A 202.61.241.61 | ||
| 105 | kimai IN AAAA 2a03:4000:52:ada:: | ||
| 106 | kimai IN MX 0 surtr.yggdrasil.li | ||
| 107 | kimai IN TXT "v=spf1 redirect=surtr.yggdrasil.li" | ||
| 108 | _acme-challenge.kimai IN NS ns.yggdrasil.li. | ||
| 109 | |||
| 110 | kimai IN HTTPS 1 . alpn="h2,h3" ipv4hint="202.61.241.61" ipv6hint="2a03:4000:52:ada::" | ||
| 111 | |||
| 96 | vidhar IN AAAA 2a03:4000:52:ada:4:1:: | 112 | vidhar IN AAAA 2a03:4000:52:ada:4:1:: |
| 97 | vidhar IN MX 0 ymir.yggdrasil.li | 113 | vidhar IN MX 0 ymir.yggdrasil.li |
| 98 | vidhar IN TXT "v=spf1 redirect=yggdrasil.li" | 114 | vidhar IN TXT "v=spf1 redirect=yggdrasil.li" |
| 99 | 115 | ||
| 100 | mailout IN A 188.68.51.254 | 116 | mailout IN A 188.68.51.254 |
| 101 | mailout IN AAAA 2a03:4000:6:d004:: | 117 | mailout IN AAAA 2a03:4000:6:d004:: |
| 118 | mailout IN A 202.61.241.61 | ||
| 119 | mailout IN AAAA 2a03:4000:52:ada:: | ||
| 102 | mailout IN MX 0 ymir.yggdrasil.li | 120 | mailout IN MX 0 ymir.yggdrasil.li |
| 103 | mailout IN TXT "v=spf1 redirect=yggdrasil.li" | 121 | mailout IN TXT "v=spf1 redirect=yggdrasil.li" |
| 104 | 122 | ||
diff --git a/hosts/surtr/dns/zones/org.dirty-haskell.soa b/hosts/surtr/dns/zones/org.dirty-haskell.soa deleted file mode 100644 index 27f0d7f9..00000000 --- a/hosts/surtr/dns/zones/org.dirty-haskell.soa +++ /dev/null | |||
| @@ -1,34 +0,0 @@ | |||
| 1 | $ORIGIN dirty-haskell.org. | ||
| 2 | $TTL 3600 | ||
| 3 | @ IN SOA ns.yggdrasil.li. hostmaster.yggdrasil.li ( | ||
| 4 | 2023013000 ; serial | ||
| 5 | 10800 ; refresh | ||
| 6 | 3600 ; retry | ||
| 7 | 604800 ; expire | ||
| 8 | 3600 ; min TTL | ||
| 9 | ) | ||
| 10 | IN NS ns.yggdrasil.li. | ||
| 11 | IN NS ns.inwx.de. | ||
| 12 | IN NS ns2.inwx.de. | ||
| 13 | IN NS ns3.inwx.eu. | ||
| 14 | |||
| 15 | @ IN CAA 128 issue "letsencrypt.org; validationmethods=dns-01" | ||
| 16 | @ IN CAA 128 iodef "mailto:caa@yggdrasil.li" | ||
| 17 | |||
| 18 | @ IN A 188.68.51.254 | ||
| 19 | @ IN AAAA 2a03:4000:6:d004:: | ||
| 20 | @ IN MX 10 ymir.yggdrasil.li. | ||
| 21 | @ IN TXT "v=spf1 redirect=yggdrasil.li" | ||
| 22 | |||
| 23 | * IN A 188.68.51.254 | ||
| 24 | * IN AAAA 2a03:4000:6:d004:: | ||
| 25 | * IN MX 0 ymir.yggdrasil.li. | ||
| 26 | * IN TXT "v=spf1 redirect=yggdrasil.li" | ||
| 27 | |||
| 28 | _acme-challenge IN NS ns.yggdrasil.li. | ||
| 29 | |||
| 30 | ymir._domainkey IN TXT ( | ||
| 31 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | ||
| 32 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
| 33 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
| 34 | ) | ||
diff --git a/hosts/surtr/dns/zones/org.praseodym.soa b/hosts/surtr/dns/zones/org.praseodym.soa index df505b4c..2b97ca19 100644 --- a/hosts/surtr/dns/zones/org.praseodym.soa +++ b/hosts/surtr/dns/zones/org.praseodym.soa | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | $ORIGIN praseodym.org. | 1 | $ORIGIN praseodym.org. |
| 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 | 2023013000 ; serial | 4 | 2025060701 ; serial |
| 5 | 10800 ; refresh | 5 | 10800 ; refresh |
| 6 | 3600 ; retry | 6 | 3600 ; retry |
| 7 | 604800 ; expire | 7 | 604800 ; expire |
| @@ -32,11 +32,8 @@ surtr IN AAAA 2a03:4000:52:ada:: | |||
| 32 | surtr IN MX 0 ymir.yggdrasil.li | 32 | surtr IN MX 0 ymir.yggdrasil.li |
| 33 | surtr IN TXT "v=spf1 redirect=yggdrasil.li" | 33 | surtr IN TXT "v=spf1 redirect=yggdrasil.li" |
| 34 | 34 | ||
| 35 | ymir._domainkey IN TXT ( | 35 | ymir._domainkey IN CNAME ymir._domainkey.yggdrasil.li. |
| 36 | "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" | 36 | surtr._domainkey IN CNAME surtr._domainkey.yggdrasil.li. |
| 37 | "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" | ||
| 38 | "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" | ||
| 39 | ) | ||
| 40 | 37 | ||
| 41 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. | 38 | _xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li. |
| 42 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. | 39 | _xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li. |
diff --git a/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py b/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py index 00182523..45619fb0 100644 --- a/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py +++ b/hosts/surtr/email/ccert-policy-server/ccert_policy_server/__main__.py | |||
| @@ -28,12 +28,14 @@ class PolicyHandler(StreamRequestHandler): | |||
| 28 | 28 | ||
| 29 | allowed = False | 29 | allowed = False |
| 30 | user = None | 30 | user = None |
| 31 | relay_eligible = False | ||
| 31 | if self.args['sasl_username']: | 32 | if self.args['sasl_username']: |
| 32 | user = self.args['sasl_username'] | 33 | user = self.args['sasl_username'] |
| 33 | if self.args['ccert_subject']: | 34 | if self.args['ccert_subject']: |
| 34 | user = self.args['ccert_subject'] | 35 | user = self.args['ccert_subject'] |
| 36 | relay_eligible = True | ||
| 35 | 37 | ||
| 36 | if user: | 38 | if user and '@' in self.args['sender']: |
| 37 | with self.server.db_pool.connection() as conn: | 39 | with self.server.db_pool.connection() as conn: |
| 38 | local, domain = self.args['sender'].split(sep='@', maxsplit=1) | 40 | local, domain = self.args['sender'].split(sep='@', maxsplit=1) |
| 39 | extension = None | 41 | extension = None |
| @@ -44,10 +46,16 @@ class PolicyHandler(StreamRequestHandler): | |||
| 44 | 46 | ||
| 45 | with conn.cursor() as cur: | 47 | with conn.cursor() as cur: |
| 46 | cur.row_factory = namedtuple_row | 48 | cur.row_factory = namedtuple_row |
| 47 | cur.execute('SELECT "mailbox"."mailbox" as "user", "local", "extension", "domain" FROM "mailbox" INNER JOIN "mailbox_mapping" ON "mailbox".id = "mailbox_mapping"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("local" = %(local)s OR "local" IS NULL) AND ("extension" = %(extension)s OR "extension" IS NULL) AND "domain" = %(domain)s', params = {'user': user, 'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare=True) | 49 | |
| 48 | for record in cur: | 50 | if relay_eligible: |
| 49 | logger.debug('Received result: %s', record) | 51 | cur.execute('SELECT EXISTS(SELECT true FROM "mailbox" INNER JOIN "relay_access" ON "mailbox".id = "relay_access"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("domain" = %(domain)s OR %(domain)s ilike CONCAT(\'%%_.\', "domain"))) as "exists"', params = {'user': user, 'domain': domain}) |
| 50 | allowed = True | 52 | if (row := cur.fetchone()) is not None: |
| 53 | allowed = row.exists | ||
| 54 | |||
| 55 | if not allowed: | ||
| 56 | cur.execute('SELECT EXISTS(SELECT true FROM "mailbox" INNER JOIN "mailbox_mapping" ON "mailbox".id = "mailbox_mapping"."mailbox" WHERE "mailbox"."mailbox" = %(user)s AND ("local" = %(local)s OR "local" IS NULL) AND ("extension" = %(extension)s OR "extension" IS NULL) AND "domain" = %(domain)s) as "exists"', params = {'user': user, 'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare=True) | ||
| 57 | if (row := cur.fetchone()) is not None: | ||
| 58 | allowed = row.exists | ||
| 51 | 59 | ||
| 52 | action = '550 5.7.0 Sender address not authorized for current user' | 60 | action = '550 5.7.0 Sender address not authorized for current user' |
| 53 | if allowed: | 61 | if allowed: |
diff --git a/hosts/surtr/email/default.nix b/hosts/surtr/email/default.nix index a85b76aa..b4b2b5c8 100644 --- a/hosts/surtr/email/default.nix +++ b/hosts/surtr/email/default.nix | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | { config, pkgs, lib, flakeInputs, ... }: | 1 | { config, pkgs, lib, flake, flakeInputs, ... }: |
| 2 | 2 | ||
| 3 | with lib; | 3 | with lib; |
| 4 | 4 | ||
| @@ -15,7 +15,7 @@ let | |||
| 15 | 15 | ||
| 16 | for file in $out/pipe/bin/*; do | 16 | for file in $out/pipe/bin/*; do |
| 17 | wrapProgram $file \ | 17 | wrapProgram $file \ |
| 18 | --set PATH "${pkgs.coreutils}/bin:${pkgs.rspamd}/bin" | 18 | --set PATH "${makeBinPath (with pkgs; [coreutils rspamd])}" |
| 19 | done | 19 | done |
| 20 | ''; | 20 | ''; |
| 21 | }; | 21 | }; |
| @@ -33,12 +33,28 @@ let | |||
| 33 | }); | 33 | }); |
| 34 | }); | 34 | }); |
| 35 | }; | 35 | }; |
| 36 | internal-policy-server = | ||
| 37 | let | ||
| 38 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./internal-policy-server; }; | ||
| 39 | pythonSet = flake.lib.pythonSet { | ||
| 40 | inherit pkgs; | ||
| 41 | python = pkgs.python312; | ||
| 42 | overlay = workspace.mkPyprojectOverlay { | ||
| 43 | sourcePreference = "wheel"; | ||
| 44 | }; | ||
| 45 | }; | ||
| 46 | virtualEnv = pythonSet.mkVirtualEnv "internal-policy-server-env" workspace.deps.default; | ||
| 47 | in virtualEnv.overrideAttrs (oldAttrs: { | ||
| 48 | meta = (oldAttrs.meta or {}) // { | ||
| 49 | mainProgram = "internal-policy-server"; | ||
| 50 | }; | ||
| 51 | }); | ||
| 36 | 52 | ||
| 37 | nftables-nologin-script = pkgs.writeScript "nftables-mail-nologin" '' | 53 | nftables-nologin-script = pkgs.resholve.writeScript "nftables-mail-nologin" { |
| 38 | #!${pkgs.zsh}/bin/zsh | 54 | inputs = with pkgs; [inetutils nftables gnugrep findutils]; |
| 39 | 55 | interpreter = lib.getExe pkgs.zsh; | |
| 56 | } '' | ||
| 40 | set -e | 57 | set -e |
| 41 | export PATH="${lib.makeBinPath (with pkgs; [inetutils nftables])}:$PATH" | ||
| 42 | 58 | ||
| 43 | typeset -a as_sets mnt_bys route route6 | 59 | typeset -a as_sets mnt_bys route route6 |
| 44 | as_sets=(${lib.escapeShellArgs config.services.email.nologin.ASSets}) | 60 | as_sets=(${lib.escapeShellArgs config.services.email.nologin.ASSets}) |
| @@ -51,7 +67,7 @@ let | |||
| 51 | elif [[ "''${line}" =~ "^route6:\s+(.+)$" ]]; then | 67 | elif [[ "''${line}" =~ "^route6:\s+(.+)$" ]]; then |
| 52 | route6+=($match[1]) | 68 | route6+=($match[1]) |
| 53 | fi | 69 | fi |
| 54 | done < <(whois -h whois.radb.net "!i''${as_set},1" | egrep -o 'AS[0-9]+' | xargs -- whois -h whois.radb.net -- -i origin) | 70 | done < <(whois -h whois.radb.net "!i''${as_set},1" | grep -Eo 'AS[0-9]+' | xargs whois -h whois.radb.net -- -i origin) |
| 55 | done | 71 | done |
| 56 | for mnt_by in $mnt_bys; do | 72 | for mnt_by in $mnt_bys; do |
| 57 | while IFS=$'\n' read line; do | 73 | while IFS=$'\n' read line; do |
| @@ -108,19 +124,20 @@ in { | |||
| 108 | services.postfix = { | 124 | services.postfix = { |
| 109 | enable = true; | 125 | enable = true; |
| 110 | enableSmtp = false; | 126 | enableSmtp = false; |
| 111 | hostname = "surtr.yggdrasil.li"; | ||
| 112 | recipientDelimiter = ""; | ||
| 113 | setSendmail = true; | 127 | setSendmail = true; |
| 114 | postmasterAlias = ""; rootAlias = ""; extraAliases = ""; | 128 | postmasterAlias = ""; rootAlias = ""; extraAliases = ""; |
| 115 | destination = []; | 129 | settings.main = { |
| 116 | sslCert = "/run/credentials/postfix.service/surtr.yggdrasil.li.pem"; | 130 | recpipient_delimiter = ""; |
| 117 | sslKey = "/run/credentials/postfix.service/surtr.yggdrasil.li.key.pem"; | 131 | mydestination = []; |
| 118 | networks = []; | 132 | mynetworks = []; |
| 119 | config = let | 133 | myhostname = "surtr.yggdrasil.li"; |
| 120 | relay_ccert = "texthash:${pkgs.writeText "relay_ccert" ""}"; | 134 | |
| 121 | in { | ||
| 122 | smtpd_tls_security_level = "may"; | 135 | smtpd_tls_security_level = "may"; |
| 123 | 136 | ||
| 137 | smtpd_tls_chain_files = [ | ||
| 138 | "/run/credentials/postfix.service/surtr.yggdrasil.li.full.pem" | ||
| 139 | ]; | ||
| 140 | |||
| 124 | #the dh params | 141 | #the dh params |
| 125 | smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path; | 142 | smtpd_tls_dh1024_param_file = toString config.security.dhparams.params."postfix-1024".path; |
| 126 | smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path; | 143 | smtpd_tls_dh512_param_file = toString config.security.dhparams.params."postfix-512".path; |
| @@ -155,21 +172,14 @@ in { | |||
| 155 | 172 | ||
| 156 | smtp_tls_connection_reuse = true; | 173 | smtp_tls_connection_reuse = true; |
| 157 | 174 | ||
| 158 | tls_server_sni_maps = ''texthash:${pkgs.writeText "sni" ( | 175 | tls_server_sni_maps = "inline:{${concatMapStringsSep ", " (domain: "{ ${domain} = /run/credentials/postfix.service/${removePrefix "." domain}.full.pem }") (concatMap (domain: [domain "mailin.${domain}" "mailsub.${domain}" ".${domain}"]) emailDomains)}}"; |
| 159 | concatMapStringsSep "\n\n" (domain: | ||
| 160 | concatMapStringsSep "\n" (subdomain: "${subdomain} /run/credentials/postfix.service/${removePrefix "." subdomain}.full.pem") | ||
| 161 | [domain "mailin.${domain}" "mailsub.${domain}" ".${domain}"] | ||
| 162 | ) emailDomains | ||
| 163 | )}''; | ||
| 164 | 176 | ||
| 165 | smtp_tls_policy_maps = "socketmap:unix:${config.services.postfix-mta-sts-resolver.settings.path}:postfix"; | 177 | smtp_tls_policy_maps = "socketmap:unix:${config.services.postfix-mta-sts-resolver.settings.path}:postfix"; |
| 166 | 178 | ||
| 167 | local_recipient_maps = ""; | 179 | local_recipient_maps = ""; |
| 168 | 180 | ||
| 169 | # 10 GiB | 181 | message_size_limit = 10 * 1024 * 1024 * 1024; |
| 170 | message_size_limit = "10737418240"; | 182 | mailbox_size_limit = 10 * 1024 * 1024 * 1024; |
| 171 | # 10 GiB | ||
| 172 | mailbox_size_limit = "10737418240"; | ||
| 173 | 183 | ||
| 174 | smtpd_delay_reject = true; | 184 | smtpd_delay_reject = true; |
| 175 | smtpd_helo_required = true; | 185 | smtpd_helo_required = true; |
| @@ -184,25 +194,26 @@ in { | |||
| 184 | dbname = email | 194 | dbname = email |
| 185 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' | 195 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' |
| 186 | ''}" | 196 | ''}" |
| 187 | "check_ccert_access ${relay_ccert}" | ||
| 188 | "reject_non_fqdn_helo_hostname" | 197 | "reject_non_fqdn_helo_hostname" |
| 189 | "reject_invalid_helo_hostname" | 198 | "reject_invalid_helo_hostname" |
| 190 | "reject_unauth_destination" | 199 | "reject_unauth_destination" |
| 191 | "reject_unknown_recipient_domain" | 200 | "reject_unknown_recipient_domain" |
| 192 | "reject_unverified_recipient" | 201 | "reject_unverified_recipient" |
| 202 | "check_policy_service unix:/run/postfix-internal-policy.sock" | ||
| 193 | ]; | 203 | ]; |
| 194 | unverified_recipient_reject_code = "550"; | 204 | unverified_recipient_reject_code = "550"; |
| 195 | unverified_recipient_reject_reason = "Recipient address lookup failed"; | 205 | unverified_recipient_reject_reason = "Recipient address lookup failed"; |
| 196 | address_verify_map = "internal:address_verify_map"; | 206 | address_verify_map = "internal:address_verify_map"; |
| 197 | address_verify_positive_expire_time = "1h"; | 207 | address_verify_positive_expire_time = "1h"; |
| 198 | address_verify_positive_refresh_time = "15m"; | 208 | address_verify_positive_refresh_time = "15m"; |
| 199 | address_verify_negative_expire_time = "15s"; | 209 | address_verify_negative_expire_time = "5m"; |
| 200 | address_verify_negative_refresh_time = "5s"; | 210 | address_verify_negative_refresh_time = "1m"; |
| 201 | address_verify_cache_cleanup_interval = "5s"; | 211 | address_verify_cache_cleanup_interval = "12h"; |
| 212 | address_verify_poll_count = "\${stress?15}\${stress:30}"; | ||
| 202 | address_verify_poll_delay = "1s"; | 213 | address_verify_poll_delay = "1s"; |
| 214 | address_verify_sender_ttl = "30045s"; | ||
| 203 | 215 | ||
| 204 | smtpd_relay_restrictions = [ | 216 | smtpd_relay_restrictions = [ |
| 205 | "check_ccert_access ${relay_ccert}" | ||
| 206 | "reject_unauth_destination" | 217 | "reject_unauth_destination" |
| 207 | ]; | 218 | ]; |
| 208 | 219 | ||
| @@ -213,7 +224,7 @@ in { | |||
| 213 | smtpd_client_event_limit_exceptions = ""; | 224 | smtpd_client_event_limit_exceptions = ""; |
| 214 | 225 | ||
| 215 | milter_default_action = "accept"; | 226 | milter_default_action = "accept"; |
| 216 | smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"]; | 227 | smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock" "local:/run/postsrsd/postsrsd-milter.sock"]; |
| 217 | non_smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"]; | 228 | non_smtpd_milters = [config.services.opendkim.socket "local:/run/rspamd/rspamd-milter.sock"]; |
| 218 | 229 | ||
| 219 | alias_maps = ""; | 230 | alias_maps = ""; |
| @@ -225,6 +236,37 @@ in { | |||
| 225 | bounce_queue_lifetime = "20m"; | 236 | bounce_queue_lifetime = "20m"; |
| 226 | delay_warning_time = "10m"; | 237 | delay_warning_time = "10m"; |
| 227 | 238 | ||
| 239 | failure_template_file = toString (pkgs.writeText "failure.cf" '' | ||
| 240 | Charset: us-ascii | ||
| 241 | From: Mail Delivery System <MAILER-DAEMON> | ||
| 242 | Subject: Undelivered Mail Returned to Sender | ||
| 243 | Postmaster-Subject: Postmaster Copy: Undelivered Mail | ||
| 244 | |||
| 245 | This is the mail system at host $myhostname. | ||
| 246 | |||
| 247 | I'm sorry to have to inform you that your message could not | ||
| 248 | be delivered to one or more recipients. It's attached below. | ||
| 249 | |||
| 250 | The mail system | ||
| 251 | ''); | ||
| 252 | delay_template_file = toString (pkgs.writeText "delay.cf" '' | ||
| 253 | Charset: us-ascii | ||
| 254 | From: Mail Delivery System <MAILER-DAEMON> | ||
| 255 | Subject: Delayed Mail (still being retried) | ||
| 256 | Postmaster-Subject: Postmaster Warning: Delayed Mail | ||
| 257 | |||
| 258 | This is the mail system at host $myhostname. | ||
| 259 | |||
| 260 | #################################################################### | ||
| 261 | # THIS IS A WARNING ONLY. YOU DO NOT NEED TO RESEND YOUR MESSAGE. # | ||
| 262 | #################################################################### | ||
| 263 | |||
| 264 | Your message could not be delivered for more than $delay_warning_time_minutes minute(s). | ||
| 265 | It will be retried until it is $maximal_queue_lifetime_minutes minute(s) old. | ||
| 266 | |||
| 267 | The mail system | ||
| 268 | ''); | ||
| 269 | |||
| 228 | smtpd_discard_ehlo_keyword_address_maps = "cidr:${pkgs.writeText "esmtp_access" '' | 270 | smtpd_discard_ehlo_keyword_address_maps = "cidr:${pkgs.writeText "esmtp_access" '' |
| 229 | # Allow DSN requests from local subnet only | 271 | # Allow DSN requests from local subnet only |
| 230 | 192.168.0.0/16 silent-discard | 272 | 192.168.0.0/16 silent-discard |
| @@ -235,11 +277,6 @@ in { | |||
| 235 | ::/0 silent-discard, dsn | 277 | ::/0 silent-discard, dsn |
| 236 | ''}"; | 278 | ''}"; |
| 237 | 279 | ||
| 238 | sender_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.forwardPort}"; | ||
| 239 | sender_canonical_classes = "envelope_sender"; | ||
| 240 | recipient_canonical_maps = "tcp:localhost:${toString config.services.postsrsd.reversePort}"; | ||
| 241 | recipient_canonical_classes = ["envelope_recipient" "header_recipient"]; | ||
| 242 | |||
| 243 | virtual_mailbox_domains = ''pgsql:${pkgs.writeText "virtual_mailbox_domains.cf" '' | 280 | virtual_mailbox_domains = ''pgsql:${pkgs.writeText "virtual_mailbox_domains.cf" '' |
| 244 | hosts = postgresql:///email | 281 | hosts = postgresql:///email |
| 245 | dbname = email | 282 | dbname = email |
| @@ -254,13 +291,26 @@ in { | |||
| 254 | virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp"; | 291 | virtual_transport = "dvlmtp:unix:/run/dovecot-lmtp"; |
| 255 | smtputf8_enable = false; | 292 | smtputf8_enable = false; |
| 256 | 293 | ||
| 257 | authorized_submit_users = "inline:{ root= postfwd= }"; | 294 | authorized_submit_users = "inline:{ root= postfwd= ${config.services.dovecot2.user}= }"; |
| 295 | authorized_flush_users = "inline:{ root= }"; | ||
| 296 | authorized_mailq_users = "inline:{ root= }"; | ||
| 258 | 297 | ||
| 259 | postscreen_access_list = ""; | 298 | postscreen_access_list = ""; |
| 260 | postscreen_denylist_action = "drop"; | 299 | postscreen_denylist_action = "drop"; |
| 261 | postscreen_greet_action = "enforce"; | 300 | postscreen_greet_action = "enforce"; |
| 301 | |||
| 302 | sender_bcc_maps = ''pgsql:${pkgs.writeText "sender_bcc_maps.cf" '' | ||
| 303 | hosts = postgresql:///email | ||
| 304 | dbname = email | ||
| 305 | query = SELECT value FROM sender_bcc_maps WHERE key = '%s' | ||
| 306 | ''}''; | ||
| 307 | recipient_bcc_maps = ''pgsql:${pkgs.writeText "recipient_bcc_maps.cf" '' | ||
| 308 | hosts = postgresql:///email | ||
| 309 | dbname = email | ||
| 310 | query = SELECT value FROM recipient_bcc_maps WHERE key = '%s' | ||
| 311 | ''}''; | ||
| 262 | }; | 312 | }; |
| 263 | masterConfig = { | 313 | settings.master = { |
| 264 | "465" = { | 314 | "465" = { |
| 265 | type = "inet"; | 315 | type = "inet"; |
| 266 | private = false; | 316 | private = false; |
| @@ -283,7 +333,7 @@ in { | |||
| 283 | hosts = postgresql:///email | 333 | hosts = postgresql:///email |
| 284 | dbname = email | 334 | dbname = email |
| 285 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) | 335 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) |
| 286 | ''},permit_tls_all_clientcerts,reject}'' | 336 | ''},check_policy_service unix:/run/postfix-internal-policy.sock,permit_tls_all_clientcerts,reject}'' |
| 287 | "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" | 337 | "-o" "smtpd_relay_restrictions=permit_tls_all_clientcerts,reject" |
| 288 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" | 338 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" |
| 289 | "-o" "unverified_sender_reject_code=550" | 339 | "-o" "unverified_sender_reject_code=550" |
| @@ -313,7 +363,7 @@ in { | |||
| 313 | hosts = postgresql:///email | 363 | hosts = postgresql:///email |
| 314 | dbname = email | 364 | dbname = email |
| 315 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) | 365 | query = SELECT action FROM virtual_mailbox_access WHERE lookup = '%s' OR (lookup = regexp_replace('%s', '\+[^@]*@', '@') AND NOT EXISTS (SELECT 1 FROM virtual_mailbox_access WHERE lookup = '%s')) |
| 316 | ''},permit_sasl_authenticated,reject}'' | 366 | ''},check_policy_service unix:/run/postfix-internal-policy.sock,permit_sasl_authenticated,reject}'' |
| 317 | "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject" | 367 | "-o" "smtpd_relay_restrictions=permit_sasl_authenticated,reject" |
| 318 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" | 368 | "-o" "{smtpd_data_restrictions = check_policy_service unix:/run/postfwd3/postfwd3.sock}" |
| 319 | "-o" "unverified_sender_reject_code=550" | 369 | "-o" "unverified_sender_reject_code=550" |
| @@ -328,7 +378,10 @@ in { | |||
| 328 | maxproc = 0; | 378 | maxproc = 0; |
| 329 | args = [ | 379 | args = [ |
| 330 | "-o" "header_checks=pcre:${pkgs.writeText "header_checks_submission" '' | 380 | "-o" "header_checks=pcre:${pkgs.writeText "header_checks_submission" '' |
| 381 | if /^Received: / | ||
| 382 | !/by surtr\.yggdrasil\.li/ STRIP | ||
| 331 | /^Received: from [^ ]+ \([^ ]+ [^ ]+\)\s+(.*)$/ REPLACE Received: $1 | 383 | /^Received: from [^ ]+ \([^ ]+ [^ ]+\)\s+(.*)$/ REPLACE Received: $1 |
| 384 | endif | ||
| 332 | ''}" | 385 | ''}" |
| 333 | ]; | 386 | ]; |
| 334 | }; | 387 | }; |
| @@ -364,17 +417,19 @@ in { | |||
| 364 | 417 | ||
| 365 | services.postsrsd = { | 418 | services.postsrsd = { |
| 366 | enable = true; | 419 | enable = true; |
| 367 | domain = "surtr.yggdrasil.li"; | 420 | domains = [ "surtr.yggdrasil.li" ] ++ concatMap (domain: [".${domain}" domain]) emailDomains; |
| 368 | separator = "+"; | 421 | separator = "+"; |
| 369 | excludeDomains = [ "surtr.yggdrasil.li" | 422 | extraConfig = '' |
| 370 | ] ++ concatMap (domain: [".${domain}" domain]) emailDomains; | 423 | socketmap = unix:/run/postsrsd/postsrsd-socketmap.sock |
| 424 | milter = unix:/run/postsrsd/postsrsd-milter.sock | ||
| 425 | ''; | ||
| 371 | }; | 426 | }; |
| 372 | 427 | ||
| 373 | services.opendkim = { | 428 | services.opendkim = { |
| 374 | enable = true; | 429 | enable = true; |
| 375 | user = "postfix"; group = "postfix"; | 430 | user = "postfix"; group = "postfix"; |
| 376 | socket = "local:/run/opendkim/opendkim.sock"; | 431 | socket = "local:/run/opendkim/opendkim.sock"; |
| 377 | domains = ''csl:${concatStringsSep "," (["surtr.yggdrasil.li"] ++ emailDomains)}''; | 432 | domains = ''csl:${concatStringsSep "," (["surtr.yggdrasil.li" "yggdrasil.li" "141.li" "kleen.li" "synapse.li" "praseodym.org"] ++ emailDomains)}''; |
| 378 | selector = "surtr"; | 433 | selector = "surtr"; |
| 379 | configFile = builtins.toFile "opendkim.conf" '' | 434 | configFile = builtins.toFile "opendkim.conf" '' |
| 380 | Syslog true | 435 | Syslog true |
| @@ -478,7 +533,7 @@ in { | |||
| 478 | }; | 533 | }; |
| 479 | }; | 534 | }; |
| 480 | 535 | ||
| 481 | users.groups.${config.services.rspamd.group}.members = [ config.services.postfix.user "dovecot2" ]; | 536 | users.groups.${config.services.rspamd.group}.members = [ config.services.postfix.user config.services.dovecot2.user ]; |
| 482 | 537 | ||
| 483 | services.redis.servers.rspamd.enable = true; | 538 | services.redis.servers.rspamd.enable = true; |
| 484 | 539 | ||
| @@ -488,22 +543,22 @@ in { | |||
| 488 | services.dovecot2 = { | 543 | services.dovecot2 = { |
| 489 | enable = true; | 544 | enable = true; |
| 490 | enablePAM = false; | 545 | enablePAM = false; |
| 491 | sslServerCert = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.pem"; | 546 | sslServerCert = "/run/credentials/dovecot.service/surtr.yggdrasil.li.pem"; |
| 492 | sslServerKey = "/run/credentials/dovecot2.service/surtr.yggdrasil.li.key.pem"; | 547 | sslServerKey = "/run/credentials/dovecot.service/surtr.yggdrasil.li.key.pem"; |
| 493 | sslCACert = toString ./ca/ca.crt; | 548 | sslCACert = toString ./ca/ca.crt; |
| 494 | mailLocation = "maildir:/var/lib/mail/%u/maildir:UTF-8:INDEX=/var/lib/dovecot/indices/%u"; | 549 | mailLocation = "maildir:/var/lib/mail/%u/maildir:UTF-8:INDEX=/var/lib/dovecot/indices/%u"; |
| 495 | mailPlugins.globally.enable = [ "fts" "fts_xapian" ]; | 550 | mailPlugins.globally.enable = [ "fts" "fts_xapian" ]; |
| 496 | protocols = [ "lmtp" "sieve" ]; | 551 | protocols = [ "lmtp" "sieve" ]; |
| 497 | sieve = { | 552 | sieve = { |
| 498 | extensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation"]; | 553 | extensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation" "vacation-seconds" "vnd.dovecot.debug"]; |
| 499 | globalExtensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation"]; | 554 | globalExtensions = ["copy" "imapsieve" "variables" "imap4flags" "vacation" "vacation-seconds" "vnd.dovecot.debug"]; |
| 500 | }; | 555 | }; |
| 501 | extraConfig = let | 556 | extraConfig = let |
| 502 | dovecotSqlConf = pkgs.writeText "dovecot-sql.conf" '' | 557 | dovecotSqlConf = pkgs.writeText "dovecot-sql.conf" '' |
| 503 | driver = pgsql | 558 | driver = pgsql |
| 504 | connect = dbname=email | 559 | connect = dbname=email |
| 505 | password_query = SELECT (CASE WHEN '%k' = 'valid' AND '%m' = 'EXTERNAL' THEN NULL ELSE "password" END) as password, (CASE WHEN '%k' = 'valid' AND '%m' = 'EXTERNAL' THEN true WHEN password IS NULL THEN true ELSE NULL END) as nopassword, "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' | 560 | password_query = SELECT (CASE WHEN '%k' = 'valid' AND '%m' = 'EXTERNAL' THEN NULL ELSE "password" END) as password, (CASE WHEN '%k' = 'valid' AND '%m' = 'EXTERNAL' THEN true WHEN password IS NULL THEN true ELSE NULL END) as nopassword, "user", quota_rule, '${config.services.dovecot2.user}' as uid, '${config.services.dovecot2.group}' as gid FROM imap_user WHERE "user" = '%n' |
| 506 | user_query = SELECT "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' | 561 | user_query = SELECT "user", quota_rule, '${config.services.dovecot2.user}' as uid, 'dovecot2' as gid FROM imap_user WHERE "user" = '%n' |
| 507 | iterate_query = SELECT "user" FROM imap_user | 562 | iterate_query = SELECT "user" FROM imap_user |
| 508 | ''; | 563 | ''; |
| 509 | in '' | 564 | in '' |
| @@ -511,16 +566,16 @@ in { | |||
| 511 | 566 | ||
| 512 | mail_plugins = $mail_plugins quota | 567 | mail_plugins = $mail_plugins quota |
| 513 | 568 | ||
| 514 | first_valid_uid = ${toString config.users.users.dovecot2.uid} | 569 | first_valid_uid = ${toString config.users.users.${config.services.dovecot2.user}.uid} |
| 515 | last_valid_uid = ${toString config.users.users.dovecot2.uid} | 570 | last_valid_uid = ${toString config.users.users.${config.services.dovecot2.user}.uid} |
| 516 | first_valid_gid = ${toString config.users.groups.dovecot2.gid} | 571 | first_valid_gid = ${toString config.users.groups.${config.services.dovecot2.group}.gid} |
| 517 | last_valid_gid = ${toString config.users.groups.dovecot2.gid} | 572 | last_valid_gid = ${toString config.users.groups.${config.services.dovecot2.group}.gid} |
| 518 | 573 | ||
| 519 | ${concatMapStringsSep "\n\n" (domain: | 574 | ${concatMapStringsSep "\n\n" (domain: |
| 520 | concatMapStringsSep "\n" (subdomain: '' | 575 | concatMapStringsSep "\n" (subdomain: '' |
| 521 | local_name ${subdomain} { | 576 | local_name ${subdomain} { |
| 522 | ssl_cert = </run/credentials/dovecot2.service/${subdomain}.pem | 577 | ssl_cert = </run/credentials/dovecot.service/${subdomain}.pem |
| 523 | ssl_key = </run/credentials/dovecot2.service/${subdomain}.key.pem | 578 | ssl_key = </run/credentials/dovecot.service/${subdomain}.key.pem |
| 524 | } | 579 | } |
| 525 | '') ["imap.${domain}" domain] | 580 | '') ["imap.${domain}" domain] |
| 526 | ) emailDomains} | 581 | ) emailDomains} |
| @@ -541,10 +596,10 @@ in { | |||
| 541 | auth_debug = yes | 596 | auth_debug = yes |
| 542 | 597 | ||
| 543 | service auth { | 598 | service auth { |
| 544 | user = dovecot2 | 599 | user = ${config.services.dovecot2.user} |
| 545 | } | 600 | } |
| 546 | service auth-worker { | 601 | service auth-worker { |
| 547 | user = dovecot2 | 602 | user = ${config.services.dovecot2.user} |
| 548 | } | 603 | } |
| 549 | 604 | ||
| 550 | userdb { | 605 | userdb { |
| @@ -565,7 +620,7 @@ in { | |||
| 565 | args = ${pkgs.writeText "dovecot-sql.conf" '' | 620 | args = ${pkgs.writeText "dovecot-sql.conf" '' |
| 566 | driver = pgsql | 621 | driver = pgsql |
| 567 | connect = dbname=email | 622 | connect = dbname=email |
| 568 | user_query = SELECT DISTINCT ON (extension IS NULL, local IS NULL) "user", quota_rule, 'dovecot2' as uid, 'dovecot2' as gid FROM lmtp_mapping WHERE CASE WHEN extension IS NOT NULL AND local IS NOT NULL THEN ('%n' :: citext) = local || '+' || extension AND domain = ('%d' :: citext) WHEN local IS NOT NULL THEN (local = ('%n' :: citext) OR ('%n' :: citext) ILIKE local || '+%%') AND domain = ('%d' :: citext) WHEN extension IS NOT NULL THEN ('%n' :: citext) ILIKE '%%+' || extension AND domain = ('%d' :: citext) ELSE domain = ('%d' :: citext) END ORDER BY (extension IS NULL) ASC, (local IS NULL) ASC | 623 | user_query = SELECT DISTINCT ON (extension IS NULL, local IS NULL) "user", quota_rule, '${config.services.dovecot2.user}' as uid, '${config.services.dovecot2.group}' as gid FROM lmtp_mapping WHERE CASE WHEN extension IS NOT NULL AND local IS NOT NULL THEN ('%n' :: citext) = local || '+' || extension AND domain = ('%d' :: citext) WHEN local IS NOT NULL THEN (local = ('%n' :: citext) OR ('%n' :: citext) ILIKE local || '+%%') AND domain = ('%d' :: citext) WHEN extension IS NOT NULL THEN ('%n' :: citext) ILIKE '%%+' || extension AND domain = ('%d' :: citext) ELSE domain = ('%d' :: citext) END ORDER BY (extension IS NULL) ASC, (local IS NULL) ASC |
| 569 | ''} | 624 | ''} |
| 570 | 625 | ||
| 571 | skip = never | 626 | skip = never |
| @@ -635,7 +690,7 @@ in { | |||
| 635 | quota_status_success = DUNNO | 690 | quota_status_success = DUNNO |
| 636 | quota_status_nouser = DUNNO | 691 | quota_status_nouser = DUNNO |
| 637 | quota_grace = 10%% | 692 | quota_grace = 10%% |
| 638 | quota_max_mail_size = ${config.services.postfix.config.message_size_limit} | 693 | quota_max_mail_size = ${toString config.services.postfix.settings.main.message_size_limit} |
| 639 | quota_vsizes = yes | 694 | quota_vsizes = yes |
| 640 | } | 695 | } |
| 641 | 696 | ||
| @@ -673,7 +728,7 @@ in { | |||
| 673 | plugin { | 728 | plugin { |
| 674 | plugin = fts fts_xapian | 729 | plugin = fts fts_xapian |
| 675 | fts = xapian | 730 | fts = xapian |
| 676 | fts_xapian = partial=2 full=20 attachments=1 verbose=1 | 731 | fts_xapian = partial=3 full=20 attachments=1 verbose=1 |
| 677 | 732 | ||
| 678 | fts_autoindex = yes | 733 | fts_autoindex = yes |
| 679 | 734 | ||
| @@ -688,12 +743,12 @@ in { | |||
| 688 | 743 | ||
| 689 | systemd.services.dovecot-fts-xapian-optimize = { | 744 | systemd.services.dovecot-fts-xapian-optimize = { |
| 690 | description = "Optimize dovecot indices for fts_xapian"; | 745 | description = "Optimize dovecot indices for fts_xapian"; |
| 691 | requisite = [ "dovecot2.service" ]; | 746 | requisite = [ "dovecot.service" ]; |
| 692 | after = [ "dovecot2.service" ]; | 747 | after = [ "dovecot.service" ]; |
| 693 | startAt = "*-*-* 22:00:00 Europe/Berlin"; | 748 | startAt = "*-*-* 22:00:00 Europe/Berlin"; |
| 694 | serviceConfig = { | 749 | serviceConfig = { |
| 695 | Type = "oneshot"; | 750 | Type = "oneshot"; |
| 696 | ExecStart = "${pkgs.dovecot}/bin/doveadm fts optimize -A"; | 751 | ExecStart = "${getExe' pkgs.dovecot "doveadm"} fts optimize -A"; |
| 697 | PrivateDevices = true; | 752 | PrivateDevices = true; |
| 698 | PrivateNetwork = true; | 753 | PrivateNetwork = true; |
| 699 | ProtectKernelTunables = true; | 754 | ProtectKernelTunables = true; |
| @@ -754,31 +809,29 @@ in { | |||
| 754 | 809 | ||
| 755 | security.acme.rfc2136Domains = { | 810 | security.acme.rfc2136Domains = { |
| 756 | "surtr.yggdrasil.li" = { | 811 | "surtr.yggdrasil.li" = { |
| 757 | restartUnits = [ "postfix.service" "dovecot2.service" ]; | 812 | restartUnits = [ "postfix.service" "dovecot.service" ]; |
| 758 | }; | 813 | }; |
| 759 | } // listToAttrs (map (domain: nameValuePair "spm.${domain}" { restartUnits = ["nginx.service"]; }) spmDomains) | 814 | } // listToAttrs (map (domain: nameValuePair "spm.${domain}" { restartUnits = ["nginx.service"]; }) spmDomains) |
| 760 | // listToAttrs (concatMap (domain: [ | 815 | // listToAttrs (concatMap (domain: [ |
| 761 | (nameValuePair domain { restartUnits = ["postfix.service" "dovecot2.service"]; }) | 816 | (nameValuePair domain { restartUnits = ["postfix.service" "dovecot.service"]; }) |
| 762 | (nameValuePair "mailin.${domain}" { restartUnits = ["postfix.service"]; }) | 817 | (nameValuePair "mailin.${domain}" { restartUnits = ["postfix.service"]; }) |
| 763 | (nameValuePair "mailsub.${domain}" { restartUnits = ["postfix.service"]; }) | 818 | (nameValuePair "mailsub.${domain}" { restartUnits = ["postfix.service"]; }) |
| 764 | (nameValuePair "imap.${domain}" { restartUnits = ["dovecot2.service"]; }) | 819 | (nameValuePair "imap.${domain}" { restartUnits = ["dovecot.service"]; }) |
| 765 | (nameValuePair "mta-sts.${domain}" { restartUnits = ["nginx.service"]; }) | 820 | (nameValuePair "mta-sts.${domain}" { restartUnits = ["nginx.service"]; }) |
| 766 | ]) emailDomains); | 821 | ]) emailDomains); |
| 767 | 822 | ||
| 768 | systemd.services.postfix = { | 823 | systemd.services.postfix = { |
| 769 | serviceConfig.LoadCredential = [ | 824 | serviceConfig.LoadCredential = let |
| 770 | "surtr.yggdrasil.li.key.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/key.pem" | 825 | tlsCredential = domain: "${domain}.full.pem:${config.security.acme.certs.${domain}.directory}/full.pem"; |
| 771 | "surtr.yggdrasil.li.pem:${config.security.acme.certs."surtr.yggdrasil.li".directory}/fullchain.pem" | 826 | in [ |
| 772 | ] ++ concatMap (domain: | 827 | (tlsCredential "surtr.yggdrasil.li") |
| 773 | map (subdomain: "${subdomain}.full.pem:${config.security.acme.certs.${subdomain}.directory}/full.pem") | 828 | ] ++ concatMap (domain: map tlsCredential [domain "mailin.${domain}" "mailsub.${domain}"]) emailDomains; |
| 774 | [domain "mailin.${domain}" "mailsub.${domain}"] | ||
| 775 | ) emailDomains; | ||
| 776 | }; | 829 | }; |
| 777 | 830 | ||
| 778 | systemd.services.dovecot2 = { | 831 | systemd.services.dovecot = { |
| 779 | preStart = '' | 832 | preStart = '' |
| 780 | for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do | 833 | for f in /etc/dovecot/sieve_flag.d/*.sieve /etc/dovecot/sieve_before.d/*.sieve; do |
| 781 | ${pkgs.dovecot_pigeonhole}/bin/sievec $f | 834 | ${getExe' pkgs.dovecot_pigeonhole "sievec"} $f |
| 782 | done | 835 | done |
| 783 | ''; | 836 | ''; |
| 784 | 837 | ||
| @@ -845,15 +898,16 @@ in { | |||
| 845 | charset utf-8; | 898 | charset utf-8; |
| 846 | source_charset utf-8; | 899 | source_charset utf-8; |
| 847 | ''; | 900 | ''; |
| 848 | root = pkgs.runCommand "mta-sts.${domain}" {} '' | 901 | root = pkgs.writeTextFile { |
| 849 | mkdir -p $out/.well-known | 902 | name = "mta-sts.${domain}"; |
| 850 | cp ${pkgs.writeText "mta-sts.${domain}.txt" '' | 903 | destination = "/.well-known/mta-sts.txt"; |
| 904 | text = '' | ||
| 851 | version: STSv1 | 905 | version: STSv1 |
| 852 | mode: enforce | 906 | mode: enforce |
| 853 | max_age: 2419200 | 907 | max_age: 2419200 |
| 854 | mx: mailin.${domain} | 908 | mx: mailin.${domain} |
| 855 | ''} $out/.well-known/mta-sts.txt | 909 | ''; |
| 856 | ''; | 910 | }; |
| 857 | }; | 911 | }; |
| 858 | }) emailDomains); | 912 | }) emailDomains); |
| 859 | }; | 913 | }; |
| @@ -870,7 +924,7 @@ in { | |||
| 870 | systemd.services.spm = { | 924 | systemd.services.spm = { |
| 871 | serviceConfig = { | 925 | serviceConfig = { |
| 872 | Type = "notify"; | 926 | Type = "notify"; |
| 873 | ExecStart = "${pkgs.spm}/bin/spm-server"; | 927 | ExecStart = getExe' pkgs.spm "spm-server"; |
| 874 | User = "spm"; | 928 | User = "spm"; |
| 875 | Group = "spm"; | 929 | Group = "spm"; |
| 876 | 930 | ||
| @@ -928,7 +982,7 @@ in { | |||
| 928 | serviceConfig = { | 982 | serviceConfig = { |
| 929 | Type = "notify"; | 983 | Type = "notify"; |
| 930 | 984 | ||
| 931 | ExecStart = "${ccert-policy-server}/bin/ccert-policy-server"; | 985 | ExecStart = getExe' ccert-policy-server "ccert-policy-server"; |
| 932 | 986 | ||
| 933 | Environment = [ | 987 | Environment = [ |
| 934 | "PGDATABASE=email" | 988 | "PGDATABASE=email" |
| @@ -961,6 +1015,53 @@ in { | |||
| 961 | }; | 1015 | }; |
| 962 | users.groups."postfix-ccert-sender-policy" = {}; | 1016 | users.groups."postfix-ccert-sender-policy" = {}; |
| 963 | 1017 | ||
| 1018 | systemd.sockets."postfix-internal-policy" = { | ||
| 1019 | requiredBy = ["postfix.service"]; | ||
| 1020 | wants = ["postfix-internal-policy.service"]; | ||
| 1021 | socketConfig = { | ||
| 1022 | ListenStream = "/run/postfix-internal-policy.sock"; | ||
| 1023 | }; | ||
| 1024 | }; | ||
| 1025 | systemd.services."postfix-internal-policy" = { | ||
| 1026 | after = [ "postgresql.service" ]; | ||
| 1027 | bindsTo = [ "postgresql.service" ]; | ||
| 1028 | |||
| 1029 | serviceConfig = { | ||
| 1030 | Type = "notify"; | ||
| 1031 | |||
| 1032 | ExecStart = lib.getExe internal-policy-server; | ||
| 1033 | |||
| 1034 | Environment = [ | ||
| 1035 | "PGDATABASE=email" | ||
| 1036 | ]; | ||
| 1037 | |||
| 1038 | DynamicUser = false; | ||
| 1039 | User = "postfix-internal-policy"; | ||
| 1040 | Group = "postfix-internal-policy"; | ||
| 1041 | ProtectSystem = "strict"; | ||
| 1042 | SystemCallFilter = "@system-service"; | ||
| 1043 | NoNewPrivileges = true; | ||
| 1044 | ProtectKernelTunables = true; | ||
| 1045 | ProtectKernelModules = true; | ||
| 1046 | ProtectKernelLogs = true; | ||
| 1047 | ProtectControlGroups = true; | ||
| 1048 | MemoryDenyWriteExecute = true; | ||
| 1049 | RestrictSUIDSGID = true; | ||
| 1050 | KeyringMode = "private"; | ||
| 1051 | ProtectClock = true; | ||
| 1052 | RestrictRealtime = true; | ||
| 1053 | PrivateDevices = true; | ||
| 1054 | PrivateTmp = true; | ||
| 1055 | ProtectHostname = true; | ||
| 1056 | ReadWritePaths = ["/run/postgresql"]; | ||
| 1057 | }; | ||
| 1058 | }; | ||
| 1059 | users.users."postfix-internal-policy" = { | ||
| 1060 | isSystemUser = true; | ||
| 1061 | group = "postfix-internal-policy"; | ||
| 1062 | }; | ||
| 1063 | users.groups."postfix-internal-policy" = {}; | ||
| 1064 | |||
| 964 | services.postfwd = { | 1065 | services.postfwd = { |
| 965 | enable = true; | 1066 | enable = true; |
| 966 | cache = false; | 1067 | cache = false; |
diff --git a/hosts/surtr/email/internal-policy-server/.envrc b/hosts/surtr/email/internal-policy-server/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/.envrc | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | use flake | ||
| 2 | |||
| 3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
| 4 | . .venv/bin/activate | ||
diff --git a/hosts/surtr/email/internal-policy-server/.gitignore b/hosts/surtr/email/internal-policy-server/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | .venv | ||
| 2 | **/__pycache__ | ||
diff --git a/hosts/surtr/email/internal-policy-server/internal_policy_server/__init__.py b/hosts/surtr/email/internal-policy-server/internal_policy_server/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/internal_policy_server/__init__.py | |||
diff --git a/hosts/surtr/email/internal-policy-server/internal_policy_server/__main__.py b/hosts/surtr/email/internal-policy-server/internal_policy_server/__main__.py new file mode 100644 index 00000000..04f1a59a --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/internal_policy_server/__main__.py | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | from systemd.daemon import listen_fds | ||
| 2 | from sdnotify import SystemdNotifier | ||
| 3 | from socketserver import StreamRequestHandler, ThreadingMixIn | ||
| 4 | from systemd_socketserver import SystemdSocketServer | ||
| 5 | import sys | ||
| 6 | from threading import Thread | ||
| 7 | from psycopg_pool import ConnectionPool | ||
| 8 | from psycopg.rows import namedtuple_row | ||
| 9 | |||
| 10 | import logging | ||
| 11 | |||
| 12 | |||
| 13 | class PolicyHandler(StreamRequestHandler): | ||
| 14 | def handle(self): | ||
| 15 | logger.debug('Handling new connection...') | ||
| 16 | |||
| 17 | self.args = dict() | ||
| 18 | |||
| 19 | line = None | ||
| 20 | while line := self.rfile.readline().removesuffix(b'\n'): | ||
| 21 | if b'=' not in line: | ||
| 22 | break | ||
| 23 | |||
| 24 | key, val = line.split(sep=b'=', maxsplit=1) | ||
| 25 | self.args[key.decode()] = val.decode() | ||
| 26 | |||
| 27 | logger.info('Connection parameters: %s', self.args) | ||
| 28 | |||
| 29 | allowed = False | ||
| 30 | user = None | ||
| 31 | if self.args['sasl_username']: | ||
| 32 | user = self.args['sasl_username'] | ||
| 33 | if self.args['ccert_subject']: | ||
| 34 | user = self.args['ccert_subject'] | ||
| 35 | |||
| 36 | with self.server.db_pool.connection() as conn: | ||
| 37 | local, domain = self.args['recipient'].split(sep='@', maxsplit=1) | ||
| 38 | extension = None | ||
| 39 | if '+' in local: | ||
| 40 | local, extension = local.split(sep='+', maxsplit=1) | ||
| 41 | |||
| 42 | logger.debug('Parsed recipient address: %s', {'local': local, 'extension': extension, 'domain': domain}) | ||
| 43 | |||
| 44 | with conn.cursor() as cur: | ||
| 45 | cur.row_factory = namedtuple_row | ||
| 46 | cur.execute('SELECT id, internal FROM "mailbox_mapping" WHERE ("local" = %(local)s OR "local" IS NULL) AND ("extension" = %(extension)s OR "extension" IS NULL) AND "domain" = %(domain)s', params = {'local': local, 'extension': extension if extension is not None else '', 'domain': domain}, prepare = True) | ||
| 47 | if (row := cur.fetchone()) is not None: | ||
| 48 | if not row.internal: | ||
| 49 | logger.debug('Recipient mailbox is not internal') | ||
| 50 | allowed = True | ||
| 51 | elif user: | ||
| 52 | cur.execute('SELECT EXISTS(SELECT true FROM "mailbox_mapping_access" INNER JOIN "mailbox" ON "mailbox".id = "mailbox_mapping_access"."mailbox" WHERE mailbox_mapping = %(mailbox_mapping)s AND "mailbox"."mailbox" = %(user)s) as "exists"', params = { 'mailbox_mapping': row.id, 'user': user }, prepare = True) | ||
| 53 | if (row := cur.fetchone()) is not None: | ||
| 54 | allowed = row.exists | ||
| 55 | else: | ||
| 56 | logger.debug('Recipient is not local') | ||
| 57 | allowed = True | ||
| 58 | |||
| 59 | action = '550 5.7.0 Recipient mailbox mapping not authorized for current user' | ||
| 60 | if allowed: | ||
| 61 | action = 'DUNNO' | ||
| 62 | |||
| 63 | logger.info('Reached verdict: %s', {'allowed': allowed, 'action': action}) | ||
| 64 | self.wfile.write(f'action={action}\n\n'.encode()) | ||
| 65 | |||
| 66 | class ThreadedSystemdSocketServer(ThreadingMixIn, SystemdSocketServer): | ||
| 67 | def __init__(self, fd, RequestHandlerClass): | ||
| 68 | super().__init__(fd, RequestHandlerClass) | ||
| 69 | |||
| 70 | self.db_pool = ConnectionPool(min_size=1) | ||
| 71 | self.db_pool.wait() | ||
| 72 | |||
| 73 | def main(): | ||
| 74 | global logger | ||
| 75 | logger = logging.getLogger(__name__) | ||
| 76 | console_handler = logging.StreamHandler() | ||
| 77 | console_handler.setFormatter( logging.Formatter('[%(levelname)s](%(name)s): %(message)s') ) | ||
| 78 | if sys.stderr.isatty(): | ||
| 79 | console_handler.setFormatter( logging.Formatter('%(asctime)s [%(levelname)s](%(name)s): %(message)s') ) | ||
| 80 | logger.addHandler(console_handler) | ||
| 81 | logger.setLevel(logging.DEBUG) | ||
| 82 | |||
| 83 | # log uncaught exceptions | ||
| 84 | def log_exceptions(type, value, tb): | ||
| 85 | global logger | ||
| 86 | |||
| 87 | logger.error(value) | ||
| 88 | sys.__excepthook__(type, value, tb) # calls default excepthook | ||
| 89 | |||
| 90 | sys.excepthook = log_exceptions | ||
| 91 | |||
| 92 | fds = listen_fds() | ||
| 93 | servers = [ThreadedSystemdSocketServer(fd, PolicyHandler) for fd in fds] | ||
| 94 | |||
| 95 | if servers: | ||
| 96 | for server in servers: | ||
| 97 | Thread(name=f'Server for fd{server.fileno()}', target=server.serve_forever).start() | ||
| 98 | else: | ||
| 99 | return 2 | ||
| 100 | |||
| 101 | SystemdNotifier().notify('READY=1') | ||
| 102 | |||
| 103 | return 0 | ||
| 104 | |||
| 105 | if __name__ == '__main__': | ||
| 106 | sys.exit(main()) | ||
diff --git a/hosts/surtr/email/internal-policy-server/pyproject.toml b/hosts/surtr/email/internal-policy-server/pyproject.toml new file mode 100644 index 00000000..c697cd01 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/pyproject.toml | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | [project] | ||
| 2 | name = "internal-policy-server" | ||
| 3 | version = "0.1.0" | ||
| 4 | requires-python = ">=3.12" | ||
| 5 | dependencies = [ | ||
| 6 | "psycopg>=3.2.9", | ||
| 7 | "psycopg-binary>=3.2.9", | ||
| 8 | "psycopg-pool>=3.2.6", | ||
| 9 | "sdnotify>=0.3.2", | ||
| 10 | "systemd-socketserver>=1.0", | ||
| 11 | ] | ||
| 12 | |||
| 13 | [project.scripts] | ||
| 14 | internal-policy-server = "internal_policy_server.__main__:main" | ||
| 15 | |||
| 16 | [build-system] | ||
| 17 | requires = ["hatchling"] | ||
| 18 | build-backend = "hatchling.build" | ||
diff --git a/hosts/surtr/email/internal-policy-server/uv.lock b/hosts/surtr/email/internal-policy-server/uv.lock new file mode 100644 index 00000000..f7a4e729 --- /dev/null +++ b/hosts/surtr/email/internal-policy-server/uv.lock | |||
| @@ -0,0 +1,119 @@ | |||
| 1 | version = 1 | ||
| 2 | revision = 2 | ||
| 3 | requires-python = ">=3.12" | ||
| 4 | |||
| 5 | [[package]] | ||
| 6 | name = "internal-policy-server" | ||
| 7 | version = "0.1.0" | ||
| 8 | source = { editable = "." } | ||
| 9 | dependencies = [ | ||
| 10 | { name = "psycopg" }, | ||
| 11 | { name = "psycopg-binary" }, | ||
| 12 | { name = "psycopg-pool" }, | ||
| 13 | { name = "sdnotify" }, | ||
| 14 | { name = "systemd-socketserver" }, | ||
| 15 | ] | ||
| 16 | |||
| 17 | [package.metadata] | ||
| 18 | requires-dist = [ | ||
| 19 | { name = "psycopg", specifier = ">=3.2.9" }, | ||
| 20 | { name = "psycopg-binary", specifier = ">=3.2.9" }, | ||
| 21 | { name = "psycopg-pool", specifier = ">=3.2.6" }, | ||
| 22 | { name = "sdnotify", specifier = ">=0.3.2" }, | ||
| 23 | { name = "systemd-socketserver", specifier = ">=1.0" }, | ||
| 24 | ] | ||
| 25 | |||
| 26 | [[package]] | ||
| 27 | name = "psycopg" | ||
| 28 | version = "3.2.9" | ||
| 29 | source = { registry = "https://pypi.org/simple" } | ||
| 30 | dependencies = [ | ||
| 31 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, | ||
| 32 | { name = "tzdata", marker = "sys_platform == 'win32'" }, | ||
| 33 | ] | ||
| 34 | sdist = { url = "https://files.pythonhosted.org/packages/27/4a/93a6ab570a8d1a4ad171a1f4256e205ce48d828781312c0bbaff36380ecb/psycopg-3.2.9.tar.gz", hash = "sha256:2fbb46fcd17bc81f993f28c47f1ebea38d66ae97cc2dbc3cad73b37cefbff700", size = 158122, upload-time = "2025-05-13T16:11:15.533Z" } | ||
| 35 | wheels = [ | ||
| 36 | { url = "https://files.pythonhosted.org/packages/44/b0/a73c195a56eb6b92e937a5ca58521a5c3346fb233345adc80fd3e2f542e2/psycopg-3.2.9-py3-none-any.whl", hash = "sha256:01a8dadccdaac2123c916208c96e06631641c0566b22005493f09663c7a8d3b6", size = 202705, upload-time = "2025-05-13T16:06:26.584Z" }, | ||
| 37 | ] | ||
| 38 | |||
| 39 | [[package]] | ||
| 40 | name = "psycopg-binary" | ||
| 41 | version = "3.2.9" | ||
| 42 | source = { registry = "https://pypi.org/simple" } | ||
| 43 | wheels = [ | ||
| 44 | { url = "https://files.pythonhosted.org/packages/29/6f/ec9957e37a606cd7564412e03f41f1b3c3637a5be018d0849914cb06e674/psycopg_binary-3.2.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:be7d650a434921a6b1ebe3fff324dbc2364393eb29d7672e638ce3e21076974e", size = 4022205, upload-time = "2025-05-13T16:07:48.195Z" }, | ||
| 45 | { url = "https://files.pythonhosted.org/packages/6b/ba/497b8bea72b20a862ac95a94386967b745a472d9ddc88bc3f32d5d5f0d43/psycopg_binary-3.2.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a76b4722a529390683c0304501f238b365a46b1e5fb6b7249dbc0ad6fea51a0", size = 4083795, upload-time = "2025-05-13T16:07:50.917Z" }, | ||
| 46 | { url = "https://files.pythonhosted.org/packages/42/07/af9503e8e8bdad3911fd88e10e6a29240f9feaa99f57d6fac4a18b16f5a0/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96a551e4683f1c307cfc3d9a05fec62c00a7264f320c9962a67a543e3ce0d8ff", size = 4655043, upload-time = "2025-05-13T16:07:54.857Z" }, | ||
| 47 | { url = "https://files.pythonhosted.org/packages/28/ed/aff8c9850df1648cc6a5cc7a381f11ee78d98a6b807edd4a5ae276ad60ad/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:61d0a6ceed8f08c75a395bc28cb648a81cf8dee75ba4650093ad1a24a51c8724", size = 4477972, upload-time = "2025-05-13T16:07:57.925Z" }, | ||
| 48 | { url = "https://files.pythonhosted.org/packages/5c/bd/8e9d1b77ec1a632818fe2f457c3a65af83c68710c4c162d6866947d08cc5/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad280bbd409bf598683dda82232f5215cfc5f2b1bf0854e409b4d0c44a113b1d", size = 4737516, upload-time = "2025-05-13T16:08:01.616Z" }, | ||
| 49 | { url = "https://files.pythonhosted.org/packages/46/ec/222238f774cd5a0881f3f3b18fb86daceae89cc410f91ef6a9fb4556f236/psycopg_binary-3.2.9-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76eddaf7fef1d0994e3d536ad48aa75034663d3a07f6f7e3e601105ae73aeff6", size = 4436160, upload-time = "2025-05-13T16:08:04.278Z" }, | ||
| 50 | { url = "https://files.pythonhosted.org/packages/37/78/af5af2a1b296eeca54ea7592cd19284739a844974c9747e516707e7b3b39/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:52e239cd66c4158e412318fbe028cd94b0ef21b0707f56dcb4bdc250ee58fd40", size = 3753518, upload-time = "2025-05-13T16:08:07.567Z" }, | ||
| 51 | { url = "https://files.pythonhosted.org/packages/ec/ac/8a3ed39ea069402e9e6e6a2f79d81a71879708b31cc3454283314994b1ae/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:08bf9d5eabba160dd4f6ad247cf12f229cc19d2458511cab2eb9647f42fa6795", size = 3313598, upload-time = "2025-05-13T16:08:09.999Z" }, | ||
| 52 | { url = "https://files.pythonhosted.org/packages/da/43/26549af068347c808fbfe5f07d2fa8cef747cfff7c695136172991d2378b/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1b2cf018168cad87580e67bdde38ff5e51511112f1ce6ce9a8336871f465c19a", size = 3407289, upload-time = "2025-05-13T16:08:12.66Z" }, | ||
| 53 | { url = "https://files.pythonhosted.org/packages/67/55/ea8d227c77df8e8aec880ded398316735add8fda5eb4ff5cc96fac11e964/psycopg_binary-3.2.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:14f64d1ac6942ff089fc7e926440f7a5ced062e2ed0949d7d2d680dc5c00e2d4", size = 3472493, upload-time = "2025-05-13T16:08:15.672Z" }, | ||
| 54 | { url = "https://files.pythonhosted.org/packages/3c/02/6ff2a5bc53c3cd653d281666728e29121149179c73fddefb1e437024c192/psycopg_binary-3.2.9-cp312-cp312-win_amd64.whl", hash = "sha256:7a838852e5afb6b4126f93eb409516a8c02a49b788f4df8b6469a40c2157fa21", size = 2927400, upload-time = "2025-05-13T16:08:18.652Z" }, | ||
| 55 | { url = "https://files.pythonhosted.org/packages/28/0b/f61ff4e9f23396aca674ed4d5c9a5b7323738021d5d72d36d8b865b3deaf/psycopg_binary-3.2.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:98bbe35b5ad24a782c7bf267596638d78aa0e87abc7837bdac5b2a2ab954179e", size = 4017127, upload-time = "2025-05-13T16:08:21.391Z" }, | ||
| 56 | { url = "https://files.pythonhosted.org/packages/bc/00/7e181fb1179fbfc24493738b61efd0453d4b70a0c4b12728e2b82db355fd/psycopg_binary-3.2.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:72691a1615ebb42da8b636c5ca9f2b71f266be9e172f66209a361c175b7842c5", size = 4080322, upload-time = "2025-05-13T16:08:24.049Z" }, | ||
| 57 | { url = "https://files.pythonhosted.org/packages/58/fd/94fc267c1d1392c4211e54ccb943be96ea4032e761573cf1047951887494/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ab464bfba8c401f5536d5aa95f0ca1dd8257b5202eede04019b4415f491351", size = 4655097, upload-time = "2025-05-13T16:08:27.376Z" }, | ||
| 58 | { url = "https://files.pythonhosted.org/packages/41/17/31b3acf43de0b2ba83eac5878ff0dea5a608ca2a5c5dd48067999503a9de/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e8aeefebe752f46e3c4b769e53f1d4ad71208fe1150975ef7662c22cca80fab", size = 4482114, upload-time = "2025-05-13T16:08:30.781Z" }, | ||
| 59 | { url = "https://files.pythonhosted.org/packages/85/78/b4d75e5fd5a85e17f2beb977abbba3389d11a4536b116205846b0e1cf744/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7e4e4dd177a8665c9ce86bc9caae2ab3aa9360b7ce7ec01827ea1baea9ff748", size = 4737693, upload-time = "2025-05-13T16:08:34.625Z" }, | ||
| 60 | { url = "https://files.pythonhosted.org/packages/3b/95/7325a8550e3388b00b5e54f4ced5e7346b531eb4573bf054c3dbbfdc14fe/psycopg_binary-3.2.9-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fc2915949e5c1ea27a851f7a472a7da7d0a40d679f0a31e42f1022f3c562e87", size = 4437423, upload-time = "2025-05-13T16:08:37.444Z" }, | ||
| 61 | { url = "https://files.pythonhosted.org/packages/1a/db/cef77d08e59910d483df4ee6da8af51c03bb597f500f1fe818f0f3b925d3/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a1fa38a4687b14f517f049477178093c39c2a10fdcced21116f47c017516498f", size = 3758667, upload-time = "2025-05-13T16:08:40.116Z" }, | ||
| 62 | { url = "https://files.pythonhosted.org/packages/95/3e/252fcbffb47189aa84d723b54682e1bb6d05c8875fa50ce1ada914ae6e28/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5be8292d07a3ab828dc95b5ee6b69ca0a5b2e579a577b39671f4f5b47116dfd2", size = 3320576, upload-time = "2025-05-13T16:08:43.243Z" }, | ||
| 63 | { url = "https://files.pythonhosted.org/packages/1c/cd/9b5583936515d085a1bec32b45289ceb53b80d9ce1cea0fef4c782dc41a7/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:778588ca9897b6c6bab39b0d3034efff4c5438f5e3bd52fda3914175498202f9", size = 3411439, upload-time = "2025-05-13T16:08:47.321Z" }, | ||
| 64 | { url = "https://files.pythonhosted.org/packages/45/6b/6f1164ea1634c87956cdb6db759e0b8c5827f989ee3cdff0f5c70e8331f2/psycopg_binary-3.2.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f0d5b3af045a187aedbd7ed5fc513bd933a97aaff78e61c3745b330792c4345b", size = 3477477, upload-time = "2025-05-13T16:08:51.166Z" }, | ||
| 65 | { url = "https://files.pythonhosted.org/packages/7b/1d/bf54cfec79377929da600c16114f0da77a5f1670f45e0c3af9fcd36879bc/psycopg_binary-3.2.9-cp313-cp313-win_amd64.whl", hash = "sha256:2290bc146a1b6a9730350f695e8b670e1d1feb8446597bed0bbe7c3c30e0abcb", size = 2928009, upload-time = "2025-05-13T16:08:53.67Z" }, | ||
| 66 | ] | ||
| 67 | |||
| 68 | [[package]] | ||
| 69 | name = "psycopg-pool" | ||
| 70 | version = "3.2.6" | ||
| 71 | source = { registry = "https://pypi.org/simple" } | ||
| 72 | dependencies = [ | ||
| 73 | { name = "typing-extensions" }, | ||
| 74 | ] | ||
| 75 | sdist = { url = "https://files.pythonhosted.org/packages/cf/13/1e7850bb2c69a63267c3dbf37387d3f71a00fd0e2fa55c5db14d64ba1af4/psycopg_pool-3.2.6.tar.gz", hash = "sha256:0f92a7817719517212fbfe2fd58b8c35c1850cdd2a80d36b581ba2085d9148e5", size = 29770, upload-time = "2025-02-26T12:03:47.129Z" } | ||
| 76 | wheels = [ | ||
| 77 | { url = "https://files.pythonhosted.org/packages/47/fd/4feb52a55c1a4bd748f2acaed1903ab54a723c47f6d0242780f4d97104d4/psycopg_pool-3.2.6-py3-none-any.whl", hash = "sha256:5887318a9f6af906d041a0b1dc1c60f8f0dda8340c2572b74e10907b51ed5da7", size = 38252, upload-time = "2025-02-26T12:03:45.073Z" }, | ||
| 78 | ] | ||
| 79 | |||
| 80 | [[package]] | ||
| 81 | name = "sdnotify" | ||
| 82 | version = "0.3.2" | ||
| 83 | source = { registry = "https://pypi.org/simple" } | ||
| 84 | sdist = { url = "https://files.pythonhosted.org/packages/ce/d8/9fdc36b2a912bf78106de4b3f0de3891ff8f369e7a6f80be842b8b0b6bd5/sdnotify-0.3.2.tar.gz", hash = "sha256:73977fc746b36cc41184dd43c3fe81323e7b8b06c2bb0826c4f59a20c56bb9f1", size = 2459, upload-time = "2017-08-02T20:03:44.395Z" } | ||
| 85 | |||
| 86 | [[package]] | ||
| 87 | name = "systemd-python" | ||
| 88 | version = "235" | ||
| 89 | source = { registry = "https://pypi.org/simple" } | ||
| 90 | sdist = { url = "https://files.pythonhosted.org/packages/10/9e/ab4458e00367223bda2dd7ccf0849a72235ee3e29b36dce732685d9b7ad9/systemd-python-235.tar.gz", hash = "sha256:4e57f39797fd5d9e2d22b8806a252d7c0106c936039d1e71c8c6b8008e695c0a", size = 61677, upload-time = "2023-02-11T13:42:16.588Z" } | ||
| 91 | |||
| 92 | [[package]] | ||
| 93 | name = "systemd-socketserver" | ||
| 94 | version = "1.0" | ||
| 95 | source = { registry = "https://pypi.org/simple" } | ||
| 96 | dependencies = [ | ||
| 97 | { name = "systemd-python" }, | ||
| 98 | ] | ||
| 99 | wheels = [ | ||
| 100 | { url = "https://files.pythonhosted.org/packages/d8/4f/b28b7f08880120a26669b080ca74487c8c67e8b54dcb0467a8f0c9f38ed6/systemd_socketserver-1.0-py3-none-any.whl", hash = "sha256:987a8bfbf28d959e7c2966c742ad7bad482f05e121077defcf95bb38267db9a8", size = 3248, upload-time = "2020-04-26T05:26:40.661Z" }, | ||
| 101 | ] | ||
| 102 | |||
| 103 | [[package]] | ||
| 104 | name = "typing-extensions" | ||
| 105 | version = "4.13.2" | ||
| 106 | source = { registry = "https://pypi.org/simple" } | ||
| 107 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } | ||
| 108 | wheels = [ | ||
| 109 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, | ||
| 110 | ] | ||
| 111 | |||
| 112 | [[package]] | ||
| 113 | name = "tzdata" | ||
| 114 | version = "2025.2" | ||
| 115 | source = { registry = "https://pypi.org/simple" } | ||
| 116 | sdist = { url = "https://files.pythonhosted.org/packages/95/32/1a225d6164441be760d75c2c42e2780dc0873fe382da3e98a2e1e48361e5/tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9", size = 196380, upload-time = "2025-03-23T13:54:43.652Z" } | ||
| 117 | wheels = [ | ||
| 118 | { url = "https://files.pythonhosted.org/packages/5c/23/c7abc0ca0a1526a0774eca151daeb8de62ec457e77262b66b359c3c7679e/tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", size = 347839, upload-time = "2025-03-23T13:54:41.845Z" }, | ||
| 119 | ] | ||
diff --git a/hosts/surtr/http/default.nix b/hosts/surtr/http/default.nix index f3a7154e..ea527cb5 100644 --- a/hosts/surtr/http/default.nix +++ b/hosts/surtr/http/default.nix | |||
| @@ -13,8 +13,6 @@ | |||
| 13 | recommendedTlsSettings = true; | 13 | recommendedTlsSettings = true; |
| 14 | sslDhparam = config.security.dhparams.params.nginx.path; | 14 | sslDhparam = config.security.dhparams.params.nginx.path; |
| 15 | commonHttpConfig = '' | 15 | commonHttpConfig = '' |
| 16 | ssl_ecdh_curve X448:X25519:prime256v1:secp521r1:secp384r1; | ||
| 17 | |||
| 18 | log_format main | 16 | log_format main |
| 19 | '$remote_addr "$remote_user" ' | 17 | '$remote_addr "$remote_user" ' |
| 20 | '"$host" "$request" $status $bytes_sent ' | 18 | '"$host" "$request" $status $bytes_sent ' |
diff --git a/hosts/surtr/kimai.nix b/hosts/surtr/kimai.nix new file mode 100644 index 00000000..454b3d80 --- /dev/null +++ b/hosts/surtr/kimai.nix | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | { config, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | security.acme.rfc2136Domains = { | ||
| 6 | "kimai.yggdrasil.li" = { | ||
| 7 | restartUnits = ["nginx.service"]; | ||
| 8 | }; | ||
| 9 | }; | ||
| 10 | |||
| 11 | services.nginx = { | ||
| 12 | upstreams."kimai" = { | ||
| 13 | servers = { | ||
| 14 | "[2a03:4000:52:ada:6::2]:80" = {}; | ||
| 15 | }; | ||
| 16 | extraConfig = '' | ||
| 17 | keepalive 8; | ||
| 18 | ''; | ||
| 19 | }; | ||
| 20 | virtualHosts = { | ||
| 21 | "kimai.yggdrasil.li" = { | ||
| 22 | kTLS = true; | ||
| 23 | http3 = true; | ||
| 24 | forceSSL = true; | ||
| 25 | sslCertificate = "/run/credentials/nginx.service/kimai.yggdrasil.li.pem"; | ||
| 26 | sslCertificateKey = "/run/credentials/nginx.service/kimai.yggdrasil.li.key.pem"; | ||
| 27 | sslTrustedCertificate = "/run/credentials/nginx.service/kimai.yggdrasil.li.chain.pem"; | ||
| 28 | extraConfig = '' | ||
| 29 | charset utf-8; | ||
| 30 | ''; | ||
| 31 | |||
| 32 | locations = { | ||
| 33 | "/".extraConfig = '' | ||
| 34 | proxy_pass http://kimai; | ||
| 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 | proxy_read_timeout 300; | ||
| 52 | ''; | ||
| 53 | }; | ||
| 54 | }; | ||
| 55 | }; | ||
| 56 | }; | ||
| 57 | |||
| 58 | systemd.services.nginx = { | ||
| 59 | serviceConfig = { | ||
| 60 | LoadCredential = [ | ||
| 61 | "kimai.yggdrasil.li.key.pem:${config.security.acme.certs."kimai.yggdrasil.li".directory}/key.pem" | ||
| 62 | "kimai.yggdrasil.li.pem:${config.security.acme.certs."kimai.yggdrasil.li".directory}/fullchain.pem" | ||
| 63 | "kimai.yggdrasil.li.chain.pem:${config.security.acme.certs."kimai.yggdrasil.li".directory}/chain.pem" | ||
| 64 | ]; | ||
| 65 | }; | ||
| 66 | }; | ||
| 67 | }; | ||
| 68 | } | ||
diff --git a/hosts/surtr/postgresql/default.nix b/hosts/surtr/postgresql/default.nix index 059f4088..3786ea7c 100644 --- a/hosts/surtr/postgresql/default.nix +++ b/hosts/surtr/postgresql/default.nix | |||
| @@ -280,6 +280,64 @@ in { | |||
| 280 | CREATE VIEW imap_user ("user", "password", quota_rule) AS SELECT mailbox.mailbox AS "user", "password", quota_rule FROM mailbox_quota_rule INNER JOIN mailbox ON mailbox_quota_rule.mailbox = mailbox.mailbox; | 280 | CREATE VIEW imap_user ("user", "password", quota_rule) AS SELECT mailbox.mailbox AS "user", "password", quota_rule FROM mailbox_quota_rule INNER JOIN mailbox ON mailbox_quota_rule.mailbox = mailbox.mailbox; |
| 281 | 281 | ||
| 282 | COMMIT; | 282 | COMMIT; |
| 283 | |||
| 284 | BEGIN; | ||
| 285 | SELECT _v.register_patch('013-internal', ARRAY['000-base'], null); | ||
| 286 | |||
| 287 | ALTER TABLE mailbox_mapping ADD COLUMN internal bool NOT NULL DEFAULT false; | ||
| 288 | CREATE TABLE mailbox_mapping_access ( | ||
| 289 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
| 290 | mailbox_mapping uuid REFERENCES mailbox_mapping(id), | ||
| 291 | mailbox uuid REFERENCES mailbox(id) | ||
| 292 | ); | ||
| 293 | CREATE USER "postfix-internal-policy"; | ||
| 294 | GRANT CONNECT ON DATABASE "email" TO "postfix-internal-policy"; | ||
| 295 | ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO "postfix-internal-policy"; | ||
| 296 | GRANT SELECT ON ALL TABLES IN SCHEMA public TO "postfix-internal-policy"; | ||
| 297 | |||
| 298 | COMMIT; | ||
| 299 | |||
| 300 | BEGIN; | ||
| 301 | SELECT _v.register_patch('014-relay', ARRAY['000-base'], null); | ||
| 302 | |||
| 303 | CREATE TABLE relay_access ( | ||
| 304 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
| 305 | mailbox uuid REFERENCES mailbox(id), | ||
| 306 | domain citext NOT NULL CONSTRAINT domain_non_empty CHECK (domain <> ''') | ||
| 307 | ); | ||
| 308 | |||
| 309 | COMMIT; | ||
| 310 | |||
| 311 | BEGIN; | ||
| 312 | SELECT _v.register_patch('015-relay-unique', ARRAY['000-base', '014-relay'], null); | ||
| 313 | |||
| 314 | CREATE UNIQUE INDEX relay_unique ON relay_access (mailbox, domain); | ||
| 315 | |||
| 316 | COMMIT; | ||
| 317 | |||
| 318 | BEGIN; | ||
| 319 | SELECT _v.register_patch('015-sender_bcc', null, null); | ||
| 320 | |||
| 321 | CREATE TABLE sender_bcc_maps ( | ||
| 322 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
| 323 | key text NOT NULL CONSTRAINT key_not_empty CHECK (key <> '''), | ||
| 324 | value text NOT NULL CONSTRAINT value_not_empty CHECK (value <> '''), | ||
| 325 | CONSTRAINT key_unique UNIQUE (key) | ||
| 326 | ); | ||
| 327 | |||
| 328 | COMMIT; | ||
| 329 | |||
| 330 | BEGIN; | ||
| 331 | SELECT _v.register_patch('016-recipient_bcc', null, null); | ||
| 332 | |||
| 333 | CREATE TABLE recipient_bcc_maps ( | ||
| 334 | id uuid PRIMARY KEY NOT NULL DEFAULT gen_random_uuid(), | ||
| 335 | key text NOT NULL CONSTRAINT key_not_empty CHECK (key <> '''), | ||
| 336 | value text NOT NULL CONSTRAINT value_not_empty CHECK (value <> '''), | ||
| 337 | CONSTRAINT recipient_bcc_maps_key_unique UNIQUE (key) | ||
| 338 | ); | ||
| 339 | |||
| 340 | COMMIT; | ||
| 283 | ''} | 341 | ''} |
| 284 | 342 | ||
| 285 | psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" '' | 343 | psql etebase postgres -eXf ${pkgs.writeText "etebase.sql" '' |
diff --git a/hosts/surtr/tls/default.nix b/hosts/surtr/tls/default.nix index b1c05888..b25bd2ea 100644 --- a/hosts/surtr/tls/default.nix +++ b/hosts/surtr/tls/default.nix | |||
| @@ -41,7 +41,7 @@ in { | |||
| 41 | 41 | ||
| 42 | acceptTerms = true; | 42 | acceptTerms = true; |
| 43 | # DNS challenge is slow | 43 | # DNS challenge is slow |
| 44 | preliminarySelfsigned = true; | 44 | # preliminarySelfsigned = true; |
| 45 | defaults = { | 45 | defaults = { |
| 46 | email = "phikeebaogobaegh@141.li"; | 46 | email = "phikeebaogobaegh@141.li"; |
| 47 | # We don't like NIST curves and Let's Encrypt doesn't support | 47 | # We don't like NIST curves and Let's Encrypt doesn't support |
diff --git a/hosts/surtr/tls/tsig_keys/audiobookshelf.yggdrasil.li b/hosts/surtr/tls/tsig_keys/audiobookshelf.yggdrasil.li new file mode 100644 index 00000000..8dd610dd --- /dev/null +++ b/hosts/surtr/tls/tsig_keys/audiobookshelf.yggdrasil.li | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:r9jhdTlbDnCMq1QLJutn76uz1Ml8MFs7fXYRSiVYh1gafcXXsUZBq5+qqoQI,iv:un/luttuKpCiMf53fa2SRY0ffttGiYwT8DuHCKEnnEI=,tag:SkNULZSulQmP99aB/Ec+Fw==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBkZGJzaEsrSU4raHlTVDVB\nczRnWVlSTTRuNXU0T3F1RTkxKytXeVJRdGpFCk9WMzNBR1NaTTMzN3BGQ2JmTjVt\nRU4rSWxCYjJPYVRzLzR0OVRYQm45TUkKLS0tIDNyMnpPN2VKUFFadTkveXRYeWps\nYUNaTjRJLzdWUnREaUVIWkpFV0FTZ2MKJS0K49SdkLW4p67FlgboHy/OVvCiUA7g\nuv5b+yotkQmh5xJwr7CUvwRewqJh56mg1yhWmE8wzpgLZMIjRXcQCQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | }, | ||
| 9 | { | ||
| 10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQQ1h3M1lXTXVNd0d6cmtU\nU2JtUzFFblJudmEycnJONkkwME9wWm5jWVFzCnRYVEFWaVNvSW9GZ05TRWF4L2ho\nanltVytEU3ZOdHk1VHY5aGJDUkdDdmcKLS0tIEtzOFVkbmpjbWN5d0c1VEpxc1Rr\nSzJwclYxeC9TVWNaK2gwUmJSY0x1ZVUKTNivp5iS+1tzVMjMn17/ncvHcELhjQ/B\n0OVz4VpKM2wv6CjEcIMxmchqT8p8GFYVRrKUdqO2GEKOoe8ANtidWA==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2025-05-09T17:07:16Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:SwS+8UQnPgHORobKLu+u2pNaMdKIvR+etUed8btbbne/IX/Wpxt0qyPYXNNGGRkN3KAxTHWjRRdrKU1bkuTU3ER1c94T935ExDESKJLVjzaEF5VSWCqLyUNCMsY2ANw84UES2swK4YI4zF1CP7rD8tKFFld78IWZoeQ7XNGDMRA=,iv:neLvamISgQ5+aqW1iRj9xJoXq1weNNyy7KCFG2+WRQE=,tag:66SDO61WnKU6DVElo9CImg==,type:str]", | ||
| 16 | "unencrypted_suffix": "_unencrypted", | ||
| 17 | "version": "3.10.2" | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/hosts/surtr/tls/tsig_keys/kimai.yggdrasil.li b/hosts/surtr/tls/tsig_keys/kimai.yggdrasil.li new file mode 100644 index 00000000..b9199975 --- /dev/null +++ b/hosts/surtr/tls/tsig_keys/kimai.yggdrasil.li | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:ATcU3Ix7o5d/49rD5H8je1ozTjoghrloMh5DIZ5WE3oYauUAknpGfr9xq92V,iv:vy9YK5Ot7CCjMtgAGVeAUQuaSw4F5kmmZ0GJYV9kCdQ=,tag:F/MXTUM2AI1fGXa9Ewn8yQ==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBDMEF0cUdydERYVzJCa3pW\nTlo0NUFON0d5RGJFVnVTNVg3cjNEUERQMEdFClEvQW5odlNEd2F1VTFmMWQrL2RB\ncllFZVpIVVJrNTJsSGF4UEdZMnVmQzAKLS0tIFUrQkkzRVZiOFNiTnFCT1pEYVRM\nQm8wV1JkQ3RrR1dkL0FsNkhsY2kxa1kKGnAo/6oibgXexUU31THdLu6X+pRtrkjD\nZnXGPZ2xaESDVUVEYQPVpNrjt9brZGJBI1BasrkEwHAXMbJC236yYQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | }, | ||
| 9 | { | ||
| 10 | "recipient": "age19a7j77w267z04zls7m28a8hj4a0g5af6ltye2d5wypg33c3l89csd4r9zq", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB3MGs1Z2ZqK2pqWHdVYTJH\naTlncHdPa3Zld0JhQW5Ccmc1SStWSnlDR0JrCmpML2d4TGdldUdoZCtaWVpPZVl0\nVm4waWVBS1orRS90ZS96N0Y2M29LY0UKLS0tIEI1Z2VVbVVxRUpOZEN4NnBRRklC\nQXloelZCb04xbmduTlVuL005TlRGMHMKfLB6zA3sj3HgDBC7VGfGVB6I1zJpt0PV\nkCV2yADgvAA2pT9HPg9IWAEpTPysOBiuE2jPNtFvylZYwTDHoumFnQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2025-05-24T09:42:23Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:0pk1LpWPmX9td/TwJFxwWp5pTDyW78UtHXMDah+V9Tmgi8hH7ONdysgjwpDwS/c4zGnMA3qtobEL286U3//CTXt2qVsiUGLsnngzs2E6yBg8oGMYlGrch4M355Fl5ZxYsc8QLA6qWcuZ4H3QW8PnoqdJixcHoYLoxG01dzh4Bc0=,iv:zchk4enI1D80BkJLji5RLm7OTk3GeF8nYHuwqBxCXIM=,tag:bgkknPMqkSidi6bDFfv6UQ==,type:str]", | ||
| 16 | "unencrypted_suffix": "_unencrypted", | ||
| 17 | "version": "3.10.2" | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/hosts/surtr/vpn/default.nix b/hosts/surtr/vpn/default.nix index 1bdcf74e..92223144 100644 --- a/hosts/surtr/vpn/default.nix +++ b/hosts/surtr/vpn/default.nix | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | { pkgs, config, lib, ... }: | 1 | { flake, pkgs, config, lib, ... }: |
| 2 | 2 | ||
| 3 | with lib; | 3 | with lib; |
| 4 | 4 | ||
| @@ -22,7 +22,11 @@ in { | |||
| 22 | "--load-credential=surtr.priv:/run/credentials/container@vpn.service/surtr.priv" | 22 | "--load-credential=surtr.priv:/run/credentials/container@vpn.service/surtr.priv" |
| 23 | "--network-ipvlan=ens3:upstream" | 23 | "--network-ipvlan=ens3:upstream" |
| 24 | ]; | 24 | ]; |
| 25 | config = { | 25 | config = let hostConfig = config; in { config, pkgs, ... }: { |
| 26 | system.stateVersion = lib.mkIf hostConfig.containers."vpn".ephemeral config.system.nixos.release; | ||
| 27 | system.configurationRevision = mkIf (flake ? rev) flake.rev; | ||
| 28 | nixpkgs.pkgs = hostConfig.nixpkgs.pkgs; | ||
| 29 | |||
| 26 | boot.kernel.sysctl = { | 30 | boot.kernel.sysctl = { |
| 27 | "net.core.rmem_max" = 4194304; | 31 | "net.core.rmem_max" = 4194304; |
| 28 | "net.core.wmem_max" = 4194304; | 32 | "net.core.wmem_max" = 4194304; |
diff --git a/hosts/surtr/vpn/geri.pub b/hosts/surtr/vpn/geri.pub index ed5de2b2..2cd9b24e 100644 --- a/hosts/surtr/vpn/geri.pub +++ b/hosts/surtr/vpn/geri.pub | |||
| @@ -1 +1 @@ | |||
| sYuQSNZHzfegv8HRz71jnZm2nFLGeRnaGwVonhKUj2k= | hhER05bvstOTGfiAG3IJsFkBNWCUZHokBXwaiC5d534= | ||
diff --git a/hosts/vidhar/audiobookshelf/abs-podcast-autoplaylist-gkleen.toml b/hosts/vidhar/audiobookshelf/abs-podcast-autoplaylist-gkleen.toml new file mode 100644 index 00000000..42920069 --- /dev/null +++ b/hosts/vidhar/audiobookshelf/abs-podcast-autoplaylist-gkleen.toml | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:7STcG1J3zLHzlHi2LZgSa+pmKlYU6X1eLvjXcx7gvCuHFkXwa/ldrHdzaBXAMrQ+C+DWM4+cyhdal3ypxXueBuBEvH3P7/KofPP2A519sdZo1vDkXCzYRD3Ow74M+hX5ej6hL1mv21HayrRMPnYfH8HUWk1kd/y69EjpjvDHyCpQuw1WG5M4FDUaTd4Vh51QRCSG/Is29dEkvQowhIvNqnAR4KW9PpfjlbUPHauZ6PQLnlJmI9QCcAGJ8hHtp5T+xctTym82GAlXugTJpHnkqcxnGteu9H7UuiDMQ3aKKGYzZCpWkNHrbD1IRhzPzSjVlN2nIX3Ydp8ENNf61EGOrp5hzmV0MhwRHKfz5rE1FRVuyHcwhwBdJzfhzrL9zXZYzn9Zuf9KWwsF+1+58i9u9hPym63r4c1+RbMjNKzHc1/yhhsRDCrTwvMd/Hc6KfUi3H1wW/r02Va2bOCkYrOkWIQpBdx4ThMgkTaHr94DBMqT8n3Fzhc7+uaUsl6I9gMwTn8QCV3g4U7Mp3+/gnQLOdsCTgKr69x1iPfp7jzzbC29MWhIk+2aig+iWRMVT0n4AWIxooc3cYnfOJNtCdJwKuPUl4xNlZ22NK3XQfxBURSw0pi5KyDO1wgTVRoRJMnXZyin3bZ10OU8QKBqLp22FXpG/+mHrm3Y6yLkqfIP5ry+b86Rb7nWJlxDTwTGSoGi8sJzwNb+H9ioG7LjaSmvr7V+2aSq0+72WopAkIFSBq/yIWX3J/rNZuia6jKMY/8bSLDJz3V/YuzeUJ5feUkOS4KW1J1UkaYd6XxZ9Y8szCS/B7Ocz0YiV8fHmOzZKRn8BTmmXUIoyxO07MmwQ1shYfiVCwXjdjq2f3/CZbXNVOPvhGBYOcutUztftu3H9DRDTKrVae1TnztCVSkBSk1o52uSj0r8t6f4l1r7UG1TviOnVLVh1/6iiJVQey0m8G6ugw22zxfhI0Uea/rB70i+2UuoxrmsOfg+TgnvKBuakEBifp4rx0S5wyz05RYXkBLJTIwi2fVGmulqjZuCQNQnI3cfPI+oGcykob9IQqdo/Dwd118hNPVAwdDHyaiGXGh8eu8N2OCLQxlGZDtkVUveaEasJaZ0WWWaK97FUeoNJfUnB7Y3lNjv5u2rzviZyk4=,iv:jT21FNnHod6btDlBa3UflK3au5VmcsABs5OTMXF6oFA=,tag:Oh8cOL+edT5Wp0I1L5+vwg==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "age": [ | ||
| 5 | { | ||
| 6 | "recipient": "age1rmmhetcmllq0ahl5qznlr0eya2zdxwl9h6y5wnl97d2wtyx5t99sm2u866", | ||
| 7 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA0aE1XNUNCM1Q5V0d3R2JG\nbjJZTmdvQ21JbmtyR0ZmODFMdVBGejRoam1vCjMzMGdTb3BReDVCa2JJU0JrSHFP\ndTdicU5TRjIrTWpteDMzeGtDT0xaelkKLS0tIFhaSlFrbzFDUjRZV0lGR0cydVdZ\nY2xma0VSVXlTM1JucFJUSys4dlRvdEUK9gQNQEdKDDf1ikWzd6uTlE50WsfO/EB0\nGH2Ono6oNWbKWTyl/wRO8NzXx0nudwqq66s0oBLIdTMQOpIBBNI0XQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 8 | }, | ||
| 9 | { | ||
| 10 | "recipient": "age1qffdqvy9arld9zd5a5cylt0n98xhcns5shxhrhwjq5g4qa844ejselaa4l", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBiRWFqSHNlY1IvMkkwaEto\ncHZHa2p1Y25SakFkS2JYMlRFcFhnZGY1dVRFCkxSWmxvcHZMampQKzdKRHI0ZVMx\nUTFtR0pHbzFaQ0xQUFA2ZERDSWpwS0UKLS0tIFBaSGczY3VWdy9TKzRDZWZ2SElY\nbVQ4dDNhQllmVmViWGs5c3V4TmNscjQKeugevQJFAN/8JrzeAm4hm2JsQGb26BCb\n3dKYnN1kJU7oVHr1aVfXwMpELNYt9poX6WTY2h9lsdHuRlqoFXAA5Q==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2025-08-11T07:08:36Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:ZL/dOz+NC8sr8vPBsux+gFOWxUhQqMSmG1az7udhB0ckmOXtnrPBzMM1gs+5pwXLvfLux0m4xzT87+o87axIECnCq35FSuMjtEBK24OUJXsLG/q/tDv5dfRBy/976dM5W7YkBVX/uc03p8CLKf5w4XYNeRKnSwjLvWGd9runDOU=,iv:9ZIeJ5aDVVPHi3/oHqWkWtEfeivV/nFFyQ1lJWJwMu8=,tag:TfkHaopMa+Z0zk38A6/NTA==,type:str]", | ||
| 16 | "unencrypted_suffix": "_unencrypted", | ||
| 17 | "version": "3.10.2" | ||
| 18 | } | ||
| 19 | } | ||
diff --git a/hosts/vidhar/audiobookshelf/default.nix b/hosts/vidhar/audiobookshelf/default.nix new file mode 100644 index 00000000..136bcaff --- /dev/null +++ b/hosts/vidhar/audiobookshelf/default.nix | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | { config, pkgs, lib, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | services.audiobookshelf = { | ||
| 6 | enable = true; | ||
| 7 | host = "2a03:4000:52:ada:4:1::"; | ||
| 8 | port = 28982; | ||
| 9 | }; | ||
| 10 | |||
| 11 | users.groups.audiobookshelf.members = [ "gkleen" ]; | ||
| 12 | |||
| 13 | services.abs-podcast-autoplaylist = { | ||
| 14 | gkleen = {}; | ||
| 15 | }; | ||
| 16 | sops.secrets.${config.services.abs-podcast-autoplaylist.gkleen.configSecret} = { | ||
| 17 | format = "binary"; | ||
| 18 | sopsFile = ./abs-podcast-autoplaylist-gkleen.toml; | ||
| 19 | }; | ||
| 20 | }; | ||
| 21 | } | ||
diff --git a/hosts/vidhar/default.nix b/hosts/vidhar/default.nix index 90ab40dd..1c60ed22 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 ./postgresql.nix ./immich.nix ./paperless ./hledger | 7 | ./zfs.nix ./network ./samba.nix ./dns ./prometheus ./borg ./pgbackrest ./postgresql.nix ./immich.nix ./paperless ./hledger ./audiobookshelf ./kimai |
| 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 50 > /sys/devices/system/cpu/intel_pstate/max_perf_pct | 139 | echo 40 > /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" '' |
| @@ -157,8 +157,6 @@ with lib; | |||
| 157 | recommendedProxySettings = true; | 157 | recommendedProxySettings = true; |
| 158 | recommendedTlsSettings = true; | 158 | recommendedTlsSettings = true; |
| 159 | commonHttpConfig = '' | 159 | commonHttpConfig = '' |
| 160 | ssl_ecdh_curve X25519:prime256v1:secp521r1:secp384r1; | ||
| 161 | |||
| 162 | log_format main | 160 | log_format main |
| 163 | '$remote_addr "$remote_user" ' | 161 | '$remote_addr "$remote_user" ' |
| 164 | '"$host" "$request" $status $bytes_sent ' | 162 | '"$host" "$request" $status $bytes_sent ' |
diff --git a/hosts/vidhar/kimai/default.nix b/hosts/vidhar/kimai/default.nix new file mode 100644 index 00000000..0258697b --- /dev/null +++ b/hosts/vidhar/kimai/default.nix | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | { flake, config, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | config = { | ||
| 5 | boot.enableContainers = true; | ||
| 6 | boot.kernel.sysctl = { | ||
| 7 | "net.netfilter.nf_log_all_netns" = true; | ||
| 8 | }; | ||
| 9 | |||
| 10 | containers."kimai" = { | ||
| 11 | autoStart = true; | ||
| 12 | ephemeral = true; | ||
| 13 | bindMounts = { | ||
| 14 | "/var/lib/kimai" = { | ||
| 15 | hostPath = "/var/lib/kimai/state"; | ||
| 16 | isReadOnly = false; | ||
| 17 | }; | ||
| 18 | "/var/lib/mysql" = { | ||
| 19 | hostPath = "/var/lib/kimai/mysql"; | ||
| 20 | isReadOnly = false; | ||
| 21 | }; | ||
| 22 | }; | ||
| 23 | privateNetwork = true; | ||
| 24 | # forwardPorts = [ | ||
| 25 | # { containerPort = 80; | ||
| 26 | # hostPort = 28983; | ||
| 27 | # } | ||
| 28 | # ]; | ||
| 29 | hostAddress = "192.168.52.113"; | ||
| 30 | localAddress = "192.168.52.114"; | ||
| 31 | hostAddress6 = "2a03:4000:52:ada:6::1"; | ||
| 32 | localAddress6 = "2a03:4000:52:ada:6::2"; | ||
| 33 | config = let hostConfig = config; in { config, pkgs, lib, ... }: { | ||
| 34 | system.stateVersion = lib.mkIf hostConfig.containers."kimai".ephemeral config.system.nixos.release; | ||
| 35 | system.configurationRevision = lib.mkIf (flake ? rev) flake.rev; | ||
| 36 | nixpkgs.pkgs = hostConfig.nixpkgs.pkgs; | ||
| 37 | |||
| 38 | services.kimai.sites."kimai.yggdrasil.li" = { | ||
| 39 | database.socket = "/run/mysqld/mysqld.sock"; | ||
| 40 | }; | ||
| 41 | |||
| 42 | networking = { | ||
| 43 | useDHCP = false; | ||
| 44 | useNetworkd = true; | ||
| 45 | useHostResolvConf = false; | ||
| 46 | firewall.enable = false; | ||
| 47 | nftables = { | ||
| 48 | enable = true; | ||
| 49 | rulesetFile = ./ruleset.nft; | ||
| 50 | }; | ||
| 51 | }; | ||
| 52 | |||
| 53 | services.resolved.fallbackDns = [ | ||
| 54 | "9.9.9.10#dns10.quad9.net" | ||
| 55 | "149.112.112.10#dns10.quad9.net" | ||
| 56 | "2620:fe::10#dns10.quad9.net" | ||
| 57 | "2620:fe::fe:10#dns10.quad9.net" | ||
| 58 | ]; | ||
| 59 | |||
| 60 | systemd.network = { | ||
| 61 | networks.upstream = { | ||
| 62 | name = "eth0"; | ||
| 63 | matchConfig = { | ||
| 64 | Name = "eth0"; | ||
| 65 | }; | ||
| 66 | linkConfig = { | ||
| 67 | RequiredForOnline = true; | ||
| 68 | }; | ||
| 69 | networkConfig = { | ||
| 70 | Address = [ "192.168.52.114/32" "2a03:4000:52:ada:6::2/128" ]; | ||
| 71 | LLMNR = false; | ||
| 72 | MulticastDNS = false; | ||
| 73 | }; | ||
| 74 | routes = [ | ||
| 75 | { Destination = "192.168.52.113/32"; } | ||
| 76 | { Destination = "2a03:4000:52:ada:6::1/128"; } | ||
| 77 | { Destination = "0.0.0.0/0"; | ||
| 78 | Gateway = "192.168.52.113"; | ||
| 79 | } | ||
| 80 | { Destination = "::/0"; | ||
| 81 | Gateway = "2a03:4000:52:ada:6::1"; | ||
| 82 | } | ||
| 83 | ]; | ||
| 84 | }; | ||
| 85 | }; | ||
| 86 | }; | ||
| 87 | }; | ||
| 88 | }; | ||
| 89 | } | ||
diff --git a/hosts/vidhar/kimai/ruleset.nft b/hosts/vidhar/kimai/ruleset.nft new file mode 100644 index 00000000..ad4db6d5 --- /dev/null +++ b/hosts/vidhar/kimai/ruleset.nft | |||
| @@ -0,0 +1,149 @@ | |||
| 1 | define icmp_protos = {ipv6-icmp, icmp, igmp} | ||
| 2 | |||
| 3 | table arp filter { | ||
| 4 | limit lim_arp { | ||
| 5 | rate over 50 mbytes/second burst 50 mbytes | ||
| 6 | } | ||
| 7 | |||
| 8 | counter arp-rx {} | ||
| 9 | counter arp-tx {} | ||
| 10 | |||
| 11 | counter arp-ratelimit-rx {} | ||
| 12 | counter arp-ratelimit-tx {} | ||
| 13 | |||
| 14 | chain input { | ||
| 15 | type filter hook input priority filter | ||
| 16 | policy accept | ||
| 17 | |||
| 18 | limit name lim_arp counter name arp-ratelimit-rx drop | ||
| 19 | |||
| 20 | counter name arp-rx | ||
| 21 | } | ||
| 22 | |||
| 23 | chain output { | ||
| 24 | type filter hook output priority filter | ||
| 25 | policy accept | ||
| 26 | |||
| 27 | limit name lim_arp counter name arp-ratelimit-tx drop | ||
| 28 | |||
| 29 | counter name arp-tx | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | table inet filter { | ||
| 34 | limit lim_reject { | ||
| 35 | rate over 1000/second burst 1000 packets | ||
| 36 | } | ||
| 37 | |||
| 38 | limit lim_icmp { | ||
| 39 | rate over 50 mbytes/second burst 50 mbytes | ||
| 40 | } | ||
| 41 | |||
| 42 | counter invalid-fw {} | ||
| 43 | counter fw-lo {} | ||
| 44 | |||
| 45 | counter reject-ratelimit-fw {} | ||
| 46 | counter reject-fw {} | ||
| 47 | counter reject-tcp-fw {} | ||
| 48 | counter reject-icmp-fw {} | ||
| 49 | |||
| 50 | counter drop-fw {} | ||
| 51 | |||
| 52 | counter invalid-rx {} | ||
| 53 | |||
| 54 | counter rx-lo {} | ||
| 55 | counter invalid-local4-rx {} | ||
| 56 | counter invalid-local6-rx {} | ||
| 57 | |||
| 58 | counter icmp-ratelimit-rx {} | ||
| 59 | counter icmp-rx {} | ||
| 60 | |||
| 61 | counter kimai-rx {} | ||
| 62 | |||
| 63 | counter established-rx {} | ||
| 64 | |||
| 65 | counter reject-ratelimit-rx {} | ||
| 66 | counter reject-rx {} | ||
| 67 | counter reject-tcp-rx {} | ||
| 68 | counter reject-icmp-rx {} | ||
| 69 | |||
| 70 | counter drop-rx {} | ||
| 71 | |||
| 72 | counter tx-lo {} | ||
| 73 | |||
| 74 | counter icmp-ratelimit-tx {} | ||
| 75 | counter icmp-tx {} | ||
| 76 | |||
| 77 | counter kimai-tx {} | ||
| 78 | |||
| 79 | counter tx {} | ||
| 80 | |||
| 81 | chain forward { | ||
| 82 | type filter hook forward priority filter | ||
| 83 | policy drop | ||
| 84 | |||
| 85 | |||
| 86 | ct state invalid log level debug prefix "kimai: drop invalid forward: " counter name invalid-fw drop | ||
| 87 | |||
| 88 | |||
| 89 | iifname lo counter name fw-lo accept | ||
| 90 | |||
| 91 | |||
| 92 | limit name lim_reject log level debug prefix "kimai: drop forward: " counter name reject-ratelimit-fw drop | ||
| 93 | log level debug prefix "kimai: reject forward: " counter name reject-fw | ||
| 94 | meta l4proto tcp ct state new counter name reject-tcp-fw reject with tcp reset | ||
| 95 | ct state new counter name reject-icmp-fw reject | ||
| 96 | |||
| 97 | |||
| 98 | counter name drop-fw | ||
| 99 | } | ||
| 100 | |||
| 101 | chain input { | ||
| 102 | type filter hook input priority filter | ||
| 103 | policy drop | ||
| 104 | |||
| 105 | |||
| 106 | ct state invalid log level debug prefix "kimai: drop invalid input: " counter name invalid-rx drop | ||
| 107 | |||
| 108 | |||
| 109 | iifname lo counter name rx-lo accept | ||
| 110 | iif != lo ip daddr 127.0.0.1/8 counter name invalid-local4-rx reject | ||
| 111 | iif != lo ip6 daddr ::1/128 counter name invalid-local6-rx reject | ||
| 112 | |||
| 113 | |||
| 114 | meta l4proto $icmp_protos limit name lim_icmp counter name icmp-ratelimit-rx drop | ||
| 115 | meta l4proto $icmp_protos counter name icmp-rx accept | ||
| 116 | |||
| 117 | |||
| 118 | tcp dport 80 counter name kimai-rx accept | ||
| 119 | |||
| 120 | |||
| 121 | ct state { established, related } counter name established-rx accept | ||
| 122 | |||
| 123 | |||
| 124 | limit name lim_reject log level debug prefix "kimai: drop input: " counter name reject-ratelimit-rx drop | ||
| 125 | log level debug prefix "kimai: reject input: " counter name reject-rx | ||
| 126 | meta l4proto tcp ct state new counter name reject-tcp-rx reject with tcp reset | ||
| 127 | ct state new counter name reject-icmp-rx reject | ||
| 128 | |||
| 129 | |||
| 130 | counter name drop-rx | ||
| 131 | } | ||
| 132 | |||
| 133 | chain output { | ||
| 134 | type filter hook output priority filter | ||
| 135 | policy accept | ||
| 136 | |||
| 137 | |||
| 138 | oifname lo counter name tx-lo accept | ||
| 139 | |||
| 140 | meta l4proto $icmp_protos limit name lim_icmp counter name icmp-ratelimit-tx drop | ||
| 141 | meta l4proto $icmp_protos counter name icmp-tx accept | ||
| 142 | |||
| 143 | |||
| 144 | tcp sport 80 counter name kimai-tx | ||
| 145 | |||
| 146 | |||
| 147 | counter name tx | ||
| 148 | } | ||
| 149 | } | ||
diff --git a/hosts/vidhar/network/default.nix b/hosts/vidhar/network/default.nix index 0643f0bb..6fcef9d8 100644 --- a/hosts/vidhar/network/default.nix +++ b/hosts/vidhar/network/default.nix | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | { pkgs, lib, ... }: | 1 | { pkgs, lib, config, ... }: |
| 2 | 2 | ||
| 3 | with lib; | 3 | with lib; |
| 4 | 4 | ||
| 5 | { | 5 | { |
| 6 | imports = [ ./gpon.nix ./bifrost ./dhcp ]; | 6 | imports = [ ./pppoe.nix ./bifrost ./dhcp ]; |
| 7 | 7 | ||
| 8 | config = { | 8 | config = { |
| 9 | networking = { | 9 | networking = { |
| @@ -61,7 +61,9 @@ with lib; | |||
| 61 | firewall.enable = false; | 61 | firewall.enable = false; |
| 62 | nftables = { | 62 | nftables = { |
| 63 | enable = true; | 63 | enable = true; |
| 64 | rulesetFile = ./ruleset.nft; | 64 | rulesetFile = pkgs.replaceVars ./ruleset.nft { |
| 65 | inherit (config.networking) pppInterface; | ||
| 66 | }; | ||
| 65 | }; | 67 | }; |
| 66 | 68 | ||
| 67 | resolvconf = { | 69 | resolvconf = { |
| @@ -103,7 +105,14 @@ with lib; | |||
| 103 | /srv/nfs/nix-store 10.141.0.0/24(ro,async,root_squash) 2a03:4000:52:ada:1::/80(ro,async,root_squash) | 105 | /srv/nfs/nix-store 10.141.0.0/24(ro,async,root_squash) 2a03:4000:52:ada:1::/80(ro,async,root_squash) |
| 104 | ''; | 106 | ''; |
| 105 | }; | 107 | }; |
| 106 | settings.nfsd.vers3 = false; | 108 | settings.nfsd = { |
| 109 | rdma = true; | ||
| 110 | vers3 = false; | ||
| 111 | vers4 = true; | ||
| 112 | "vers4.0" = false; | ||
| 113 | "vers4.1" = false; | ||
| 114 | "vers4.2" = true; | ||
| 115 | }; | ||
| 107 | }; | 116 | }; |
| 108 | 117 | ||
| 109 | fileSystems = { | 118 | fileSystems = { |
diff --git a/hosts/vidhar/network/dhcp/default.nix b/hosts/vidhar/network/dhcp/default.nix index 07a83351..11460393 100644 --- a/hosts/vidhar/network/dhcp/default.nix +++ b/hosts/vidhar/network/dhcp/default.nix | |||
| @@ -1,8 +1,33 @@ | |||
| 1 | { flake, config, pkgs, lib, ... }: | 1 | { flake, config, pkgs, lib, sources, ... }: |
| 2 | 2 | ||
| 3 | with lib; | 3 | with lib; |
| 4 | 4 | ||
| 5 | { | 5 | let |
| 6 | nfsrootBaseUrl = "http://nfsroot.vidhar.yggdrasil"; | ||
| 7 | tftpIp = "10.141.0.1"; | ||
| 8 | nfsIp = tftpIp; | ||
| 9 | ipxe = pkgs.ipxe.override { | ||
| 10 | additionalTargets = { | ||
| 11 | "bin-i386-efi/ipxe.efi" = "i386-ipxe.efi"; | ||
| 12 | }; | ||
| 13 | additionalOptions = [ | ||
| 14 | "NSLOOKUP_CMD" | ||
| 15 | "PING_CMD" | ||
| 16 | "CONSOLE_CMD" | ||
| 17 | ]; | ||
| 18 | embedScript = pkgs.writeText "yggdrasil.ipxe" '' | ||
| 19 | #!ipxe | ||
| 20 | |||
| 21 | cpair --background 9 1 | ||
| 22 | cpair --background 9 3 | ||
| 23 | cpair --background 9 6 | ||
| 24 | |||
| 25 | set user-class iPXE-yggdrasil | ||
| 26 | |||
| 27 | autoboot | ||
| 28 | ''; | ||
| 29 | }; | ||
| 30 | in { | ||
| 6 | config = { | 31 | config = { |
| 7 | services.kea = { | 32 | services.kea = { |
| 8 | dhcp4 = { | 33 | dhcp4 = { |
| @@ -23,41 +48,67 @@ with lib; | |||
| 23 | }; | 48 | }; |
| 24 | 49 | ||
| 25 | client-classes = [ | 50 | client-classes = [ |
| 26 | { name = "eostre-ipxe"; | 51 | { name = "ipxe-eostre"; |
| 27 | test = "hexstring(pkt4.mac, ':') == '00:d8:61:79:c5:40' and option[77].hex == 'iPXE'"; | 52 | test = "hexstring(pkt4.mac, ':') == '00:d8:61:79:c5:40' and option[77].hex == 'iPXE-yggdrasil'"; |
| 28 | next-server = "10.141.0.1"; | 53 | next-server = tftpIp; |
| 29 | boot-file-name = "http://nfsroot.vidhar.yggdrasil/eostre/netboot.ipxe"; | 54 | boot-file-name = "${nfsrootBaseUrl}/eostre.menu.ipxe"; |
| 55 | only-if-required = true; | ||
| 56 | } | ||
| 57 | { name = "ipxe-yggdrasil"; | ||
| 58 | test = "option[77].hex == 'iPXE-yggdrasil'"; | ||
| 59 | next-server = tftpIp; | ||
| 60 | boot-file-name = "${nfsrootBaseUrl}/installer-x86_64-linux.menu.ipxe"; | ||
| 61 | only-if-required = true; | ||
| 62 | } | ||
| 63 | |||
| 64 | { name = "uefi-http"; | ||
| 65 | test = "option[client-system].hex == 0x0010"; | ||
| 66 | option-data = [ | ||
| 67 | { name = "vendor-class-identifier"; data = "HTTPClient"; } | ||
| 68 | ]; | ||
| 69 | boot-file-name = "${nfsrootBaseUrl}/ipxe.efi"; | ||
| 70 | only-if-required = true; | ||
| 71 | } | ||
| 72 | |||
| 73 | { name = "ipxe-uefi-64"; | ||
| 74 | test = "option[77].hex == 'iPXE' and (substring(option[60].hex,0,20) == 'PXEClient:Arch:00007' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00008' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00009')"; | ||
| 75 | boot-file-name = "${nfsrootBaseUrl}/ipxe.efi"; | ||
| 76 | only-if-required = true; | ||
| 77 | } | ||
| 78 | { name = "ipxe-uefi-32"; | ||
| 79 | test = "option[77].hex == 'iPXE' and (substring(option[60].hex,0,20) == 'PXEClient:Arch:00002' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00006')"; | ||
| 80 | boot-file-name = "${nfsrootBaseUrl}/i386-ipxe.efi"; | ||
| 30 | only-if-required = true; | 81 | only-if-required = true; |
| 31 | } | 82 | } |
| 32 | { name = "ipxe"; | 83 | { name = "ipxe-legacy"; |
| 33 | test = "option[77].hex == 'iPXE'"; | 84 | test = "option[77].hex == 'iPXE' and substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'"; |
| 34 | next-server = "10.141.0.1"; | 85 | boot-file-name = "${nfsrootBaseUrl}/ipxe.lkrn"; |
| 35 | boot-file-name = "http://nfsroot.vidhar.yggdrasil/installer-x86_64-linux/netboot.ipxe"; | ||
| 36 | only-if-required = true; | 86 | only-if-required = true; |
| 37 | } | 87 | } |
| 88 | |||
| 38 | { name = "uefi-64"; | 89 | { name = "uefi-64"; |
| 39 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00007' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00008' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00009'"; | 90 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00007' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00008' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00009'"; |
| 40 | only-if-required = true; | ||
| 41 | option-data = [ | 91 | option-data = [ |
| 42 | { name = "tftp-server-name"; data = "10.141.0.1"; } | 92 | { name = "tftp-server-name"; data = tftpIp; } |
| 43 | ]; | 93 | ]; |
| 44 | boot-file-name = "ipxe.efi"; | 94 | boot-file-name = "ipxe.efi"; |
| 95 | only-if-required = true; | ||
| 45 | } | 96 | } |
| 46 | { name = "uefi-32"; | 97 | { name = "uefi-32"; |
| 47 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00002' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00006'"; | 98 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00002' or substring(option[60].hex,0,20) == 'PXEClient:Arch:00006'"; |
| 48 | only-if-required = true; | ||
| 49 | option-data = [ | 99 | option-data = [ |
| 50 | { name = "tftp-server-name"; data = "10.141.0.1"; } | 100 | { name = "tftp-server-name"; data = tftpIp; } |
| 51 | ]; | 101 | ]; |
| 52 | boot-file-name = "i386-ipxe.efi"; | 102 | boot-file-name = "i386-ipxe.efi"; |
| 103 | only-if-required = true; | ||
| 53 | } | 104 | } |
| 54 | { name = "legacy"; | 105 | { name = "legacy"; |
| 55 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'"; | 106 | test = "substring(option[60].hex,0,20) == 'PXEClient:Arch:00000'"; |
| 56 | only-if-required = true; | ||
| 57 | option-data = [ | 107 | option-data = [ |
| 58 | { name = "tftp-server-name"; data = "10.141.0.1"; } | 108 | { name = "tftp-server-name"; data = tftpIp; } |
| 59 | ]; | 109 | ]; |
| 60 | boot-file-name = "undionly.kpxe"; | 110 | boot-file-name = "ipxe.lkrn"; |
| 111 | only-if-required = true; | ||
| 61 | } | 112 | } |
| 62 | ]; | 113 | ]; |
| 63 | 114 | ||
| @@ -252,34 +303,78 @@ with lib; | |||
| 252 | name = "nfsroot.vidhar.yggdrasil"; | 303 | name = "nfsroot.vidhar.yggdrasil"; |
| 253 | paths = | 304 | paths = |
| 254 | (map (system: | 305 | (map (system: |
| 255 | let | 306 | pkgs.symlinkJoin { |
| 256 | installerBuild = (flake.nixosConfigurations.${"installer-${system}-nfsroot"}.extendModules { | 307 | name = "installer-${system}"; |
| 257 | modules = [ | 308 | paths = [ |
| 258 | ({ ... }: { | 309 | (builtins.addErrorContext "while evaluating installer-${system}-nfsroot" (let |
| 259 | config.nfsroot.storeDevice = "10.141.0.1:nix-store"; | 310 | installerBuild' = (flake.nixosConfigurations.${"installer-${system}-nfsroot"}.extendModules { |
| 260 | config.nfsroot.registrationUrl = "http://nfsroot.vidhar.yggdrasil/installer-${system}/registration"; | 311 | modules = [ |
| 261 | }) | 312 | ({ ... }: { |
| 262 | ]; | 313 | config.nfsroot.storeDevice = "${nfsIp}:nix-store"; |
| 263 | }).config.system.build; | 314 | config.nfsroot.registrationUrl = "${nfsrootBaseUrl}/installer-${system}/registration"; |
| 264 | in builtins.toPath (pkgs.runCommandLocal "install-${system}" {} '' | 315 | config.system.nixos.label = "installer-${system}"; |
| 265 | mkdir -p $out/installer-${system} | 316 | }) |
| 266 | install -m 0444 -t $out/installer-${system} \ | 317 | ]; |
| 267 | ${installerBuild.initialRamdisk}/initrd \ | 318 | }); |
| 268 | ${installerBuild.kernel}/bzImage \ | 319 | installerBuild = installerBuild'.config.system.build; |
| 269 | ${installerBuild.netbootIpxeScript}/netboot.ipxe \ | 320 | in builtins.toPath (pkgs.runCommandLocal "installer-${system}" {} '' |
| 270 | ${pkgs.closureInfo { rootPaths = installerBuild.storeContents; }}/registration | 321 | mkdir -p $out/installer-${system} |
| 271 | '') | 322 | install -m 0444 -t $out/installer-${system} \ |
| 272 | ) ["x86_64-linux"] | 323 | ${installerBuild.initialRamdisk}/initrd \ |
| 324 | ${installerBuild.kernel}/bzImage \ | ||
| 325 | ${installerBuild.netbootIpxeScript}/netboot.ipxe \ | ||
| 326 | ${pkgs.closureInfo { rootPaths = installerBuild.storeContents; }}/registration | ||
| 327 | install -m 0444 ${pkgs.writeText "installer-${system}.menu.ipxe" '' | ||
| 328 | #!ipxe | ||
| 329 | |||
| 330 | :start | ||
| 331 | menu iPXE boot menu for installer-${system} | ||
| 332 | item installer ${with installerBuild'; "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})"} | ||
| 333 | item memtest memtest86plus | ||
| 334 | item netboot netboot.xyz | ||
| 335 | item shell iPXE shell | ||
| 336 | choose --timeout 0 --default installer selected || goto shell | ||
| 337 | goto ''${selected} | ||
| 338 | |||
| 339 | :shell | ||
| 340 | shell | ||
| 341 | goto start | ||
| 342 | |||
| 343 | :installer | ||
| 344 | chain installer-${system}/netboot.ipxe | ||
| 345 | goto start | ||
| 346 | |||
| 347 | :netboot | ||
| 348 | iseq ''${platform} efi && chain --autofree netboot.xyz.efi || chain --autofree netboot.xyz.lkrn | ||
| 349 | goto start | ||
| 350 | |||
| 351 | :memtest | ||
| 352 | iseq ''${platform} efi && chain --autofree memtest.efi || chain --autofree memtest.bin | ||
| 353 | goto start | ||
| 354 | ''} $out/installer-${system}.menu.ipxe | ||
| 355 | ''))) | ||
| 356 | ]; | ||
| 357 | }) ["x86_64-linux"] | ||
| 273 | ) ++ [ | 358 | ) ++ [ |
| 274 | (let | 359 | (pkgs.runCommandLocal "utils" {} '' |
| 275 | eostreBuild = (flake.nixosConfigurations.eostre.extendModules { | 360 | mkdir $out |
| 361 | install -m 0444 -t $out \ | ||
| 362 | ${ipxe}/{ipxe.efi,i386-ipxe.efi,ipxe.lkrn} \ | ||
| 363 | ${pkgs.memtest86plus}/{memtest.efi,memtest.bin} | ||
| 364 | install -m 0444 ${sources.netbootxyz-efi.src} $out/netboot.xyz.efi | ||
| 365 | install -m 0444 ${sources.netbootxyz-lkrn.src} $out/netboot.xyz.lkrn | ||
| 366 | '') | ||
| 367 | (builtins.addErrorContext "while evaluating eostre" (let | ||
| 368 | eostreBuild' = (flake.nixosConfigurations.eostre.extendModules { | ||
| 276 | modules = [ | 369 | modules = [ |
| 277 | ({ ... }: { | 370 | ({ ... }: { |
| 278 | config.nfsroot.storeDevice = "10.141.0.1:nix-store"; | 371 | config.nfsroot.storeDevice = "${nfsIp}:nix-store"; |
| 279 | config.nfsroot.registrationUrl = "http://nfsroot.vidhar.yggdrasil/eostre/registration"; | 372 | config.nfsroot.registrationUrl = "${nfsrootBaseUrl}/eostre/registration"; |
| 373 | config.system.nixos.label = "eostre"; | ||
| 280 | }) | 374 | }) |
| 281 | ]; | 375 | ]; |
| 282 | }).config.system.build; | 376 | }); |
| 377 | eostreBuild = eostreBuild'.config.system.build; | ||
| 283 | in builtins.toPath (pkgs.runCommandLocal "eostre" {} '' | 378 | in builtins.toPath (pkgs.runCommandLocal "eostre" {} '' |
| 284 | mkdir -p $out/eostre | 379 | mkdir -p $out/eostre |
| 285 | install -m 0444 -t $out/eostre \ | 380 | install -m 0444 -t $out/eostre \ |
| @@ -287,7 +382,39 @@ with lib; | |||
| 287 | ${eostreBuild.kernel}/bzImage \ | 382 | ${eostreBuild.kernel}/bzImage \ |
| 288 | ${eostreBuild.netbootIpxeScript}/netboot.ipxe \ | 383 | ${eostreBuild.netbootIpxeScript}/netboot.ipxe \ |
| 289 | ${pkgs.closureInfo { rootPaths = eostreBuild.storeContents; }}/registration | 384 | ${pkgs.closureInfo { rootPaths = eostreBuild.storeContents; }}/registration |
| 290 | '')) | 385 | install -m 0444 ${pkgs.writeText "eostre.menu.ipxe" '' |
| 386 | #!ipxe | ||
| 387 | |||
| 388 | set menu-timeout 5000 | ||
| 389 | |||
| 390 | :start | ||
| 391 | menu iPXE boot menu for eostre | ||
| 392 | item eostre ${with eostreBuild'; "${config.system.nixos.distroName} ${config.system.nixos.codeName} ${config.system.nixos.label} (Linux ${config.boot.kernelPackages.kernel.modDirVersion})"} | ||
| 393 | item memtest memtest86plus | ||
| 394 | item netboot netboot.xyz | ||
| 395 | item shell iPXE shell | ||
| 396 | choose --timeout ''${menu-timeout} --default eostre selected || goto shell | ||
| 397 | set menu-timeout 0 | ||
| 398 | goto ''${selected} | ||
| 399 | |||
| 400 | :shell | ||
| 401 | set menu-timeout 0 | ||
| 402 | shell | ||
| 403 | goto start | ||
| 404 | |||
| 405 | :eostre | ||
| 406 | chain eostre/netboot.ipxe | ||
| 407 | goto start | ||
| 408 | |||
| 409 | :netboot | ||
| 410 | iseq ''${platform} efi && chain --autofree netboot.xyz.efi || chain --autofree netboot.xyz.lkrn | ||
| 411 | goto start | ||
| 412 | |||
| 413 | :memtest | ||
| 414 | iseq ''${platform} efi && chain --autofree memtest.efi || chain --autofree memtest.bin | ||
| 415 | goto start | ||
| 416 | ''} $out/eostre.menu.ipxe | ||
| 417 | ''))) | ||
| 291 | ]; | 418 | ]; |
| 292 | }; | 419 | }; |
| 293 | }; | 420 | }; |
| @@ -298,20 +425,12 @@ with lib; | |||
| 298 | after = [ "network.target" ]; | 425 | after = [ "network.target" ]; |
| 299 | wantedBy = [ "multi-user.target" ]; | 426 | wantedBy = [ "multi-user.target" ]; |
| 300 | serviceConfig.ExecStart = let | 427 | serviceConfig.ExecStart = let |
| 301 | ipxe = pkgs.ipxe.override { | ||
| 302 | additionalTargets = { | ||
| 303 | "bin-i386-efi/ipxe.efi" = "i386-ipxe.efi"; | ||
| 304 | }; | ||
| 305 | additionalOptions = [ | ||
| 306 | "NSLOOKUP_CMD" | ||
| 307 | ]; | ||
| 308 | }; | ||
| 309 | tftpRoot = pkgs.runCommandLocal "netboot" {} '' | 428 | tftpRoot = pkgs.runCommandLocal "netboot" {} '' |
| 310 | mkdir -p $out | 429 | mkdir -p $out |
| 311 | install -m 0444 -t $out \ | 430 | install -m 0444 -t $out \ |
| 312 | ${ipxe}/ipxe.efi ${ipxe}/i386-ipxe.efi ${ipxe}/undionly.kpxe | 431 | ${ipxe}/{ipxe.efi,i386-ipxe.efi,ipxe.lkrn} |
| 313 | ''; | 432 | ''; |
| 314 | in "${pkgs.atftp}/sbin/atftpd --daemon --no-fork --bind-address=10.141.0.1 ${tftpRoot}"; | 433 | in "${pkgs.atftp}/sbin/atftpd --daemon --no-fork --bind-address=${tftpIp} ${tftpRoot}"; |
| 315 | }; | 434 | }; |
| 316 | }; | 435 | }; |
| 317 | } | 436 | } |
diff --git a/hosts/vidhar/network/pap-secrets b/hosts/vidhar/network/pap-secrets deleted file mode 100644 index 3516de6c..00000000 --- a/hosts/vidhar/network/pap-secrets +++ /dev/null | |||
| @@ -1,26 +0,0 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:BOyWdys7Oja54Ijv5j+kqufdokQe0onnqw/gVxpNOMf+YI/LlzJscaEtGqPh3ehVtYoSWGumCBPrjdq/zhYq4VV/PCtdeu3MnX1CH2B5bH0mFs0eXcqeGK6NErz/b5nEglv4Z19ig2CUDlbvi8h1zZAEjxTNKhT16ItCtJnBCsIoiMl2QcTTWMyh4a02v3wA1UrOQZvFuCgCHmRoBE6vpREyB23gQdrdKLk7D1LLw5C1aZnzQOhFsgs7bVjOcBnmwTao8ntXxw==,iv:X5FgYkl3DGA/lkRsoc+5XrK3Nlp/ldnFigpXpYNfSJE=,tag:qUH7m9NzuVNnOfr3rRgaOg==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "kms": null, | ||
| 5 | "gcp_kms": null, | ||
| 6 | "azure_kv": null, | ||
| 7 | "hc_vault": null, | ||
| 8 | "age": [ | ||
| 9 | { | ||
| 10 | "recipient": "age1qffdqvy9arld9zd5a5cylt0n98xhcns5shxhrhwjq5g4qa844ejselaa4l", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAwYTFaTDZRbDd6cFBRSTNN\nVk1kcFJXRG9TT21IMDZsVmtoZjBTNDNjeDFzCkhxNEI2Ujd6SW1STG43eE5EdzZa\nS3phenJZN0RxajBXQ1BnbUhTa3htdFEKLS0tIGVlT3lReHJSQ2UvQ1FST0M0RzVP\nNmxWNzJmNlFPclJTeDUycDJiUzA4Yk0K4JHtkEPY49TGnKPZzEoEZ131RxeQEWkR\nK1ftH2ilr2tUhiErhpqxoTqfAm33xvruqTsePxh1uC7svzKtKBlS2g==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2021-11-15T08:30:09Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:TAgZ4ktdN9sZPMo1UtwjKdTM2QBjLorcm84HYXTGYNNEorPoqrXAWOvyWRLjx+zxzpRuDLBPQHCkjwkVO2CctxnTaWPMwITbYtQqj/5ZxACuAeX8MaSximB8s5MJK2faCuVXEnFehbnnPr5Fs8ZsgHwu2iH6DU8ScLEkgckzGV0=,iv:keUbKwWfoIIBsp5Rsm2lEba1ZHAozQY2YpA6p5qDBiU=,tag:1llGytMGvOjSVYKJXGUmXg==,type:str]", | ||
| 16 | "pgp": [ | ||
| 17 | { | ||
| 18 | "created_at": "2023-01-30T10:58:50Z", | ||
| 19 | "enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdA+cwEt6Gv5oKvym4ceJek+J/5guNpmsLLXWIY5CCCSXUw\npXyQpqxm7LQnasIqYNNsNCVbB1mAu6WU6MKn0BG03YWjr8buLB+7PpwZcxeZzRfD\n0l4BAsl+vKwa2YSMCR+EWYSfeEzEVHqoGBJ60dYXuiFiNZInCik+g69PdhsGygNH\nRtIcRiCB8t94GkvdWySTq5ohi1wKOe224l9evbt4zXntVngCHxixuufLrr3Cj+EE\n=3lw4\n-----END PGP MESSAGE-----\n", | ||
| 20 | "fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51" | ||
| 21 | } | ||
| 22 | ], | ||
| 23 | "unencrypted_suffix": "_unencrypted", | ||
| 24 | "version": "3.7.1" | ||
| 25 | } | ||
| 26 | } \ No newline at end of file | ||
diff --git a/hosts/vidhar/network/gpon.nix b/hosts/vidhar/network/pppoe.nix index 1628159c..5cc84862 100644 --- a/hosts/vidhar/network/gpon.nix +++ b/hosts/vidhar/network/pppoe.nix | |||
| @@ -8,7 +8,7 @@ in { | |||
| 8 | options = { | 8 | options = { |
| 9 | networking.pppInterface = mkOption { | 9 | networking.pppInterface = mkOption { |
| 10 | type = types.str; | 10 | type = types.str; |
| 11 | default = "gpon"; | 11 | default = "ppp"; |
| 12 | }; | 12 | }; |
| 13 | }; | 13 | }; |
| 14 | 14 | ||
| @@ -26,14 +26,14 @@ in { | |||
| 26 | nodefaultroute | 26 | nodefaultroute |
| 27 | ifname ${pppInterface} | 27 | ifname ${pppInterface} |
| 28 | lcp-echo-adaptive | 28 | lcp-echo-adaptive |
| 29 | lcp-echo-failure 5 | 29 | lcp-echo-failure 10 |
| 30 | lcp-echo-interval 1 | 30 | lcp-echo-interval 1 |
| 31 | maxfail 0 | 31 | maxfail 0 |
| 32 | mtu 1492 | 32 | mtu 1492 |
| 33 | mru 1492 | 33 | mru 1492 |
| 34 | plugin pppoe.so | 34 | plugin pppoe.so |
| 35 | name telekom | 35 | user congstar |
| 36 | user 002576900250551137425220#0001@t-online.de | 36 | password congstar |
| 37 | nic-telekom | 37 | nic-telekom |
| 38 | debug | 38 | debug |
| 39 | +ipv6 | 39 | +ipv6 |
| @@ -43,62 +43,55 @@ in { | |||
| 43 | stopIfChanged = true; | 43 | stopIfChanged = true; |
| 44 | 44 | ||
| 45 | serviceConfig = { | 45 | serviceConfig = { |
| 46 | Type = lib.mkForce "notify"; | ||
| 47 | ExecStart = lib.mkForce "${getBin config.services.pppd.package}/sbin/pppd call telekom up_sdnotify nolog"; | ||
| 46 | PIDFile = "/run/pppd/${pppInterface}.pid"; | 48 | PIDFile = "/run/pppd/${pppInterface}.pid"; |
| 47 | }; | 49 | }; |
| 48 | restartTriggers = with config; [ | 50 | restartTriggers = with config; [ |
| 49 | environment.etc."ppp/ip-pre-up".source | 51 | environment.etc."ppp/ip-pre-up".source |
| 50 | environment.etc."ppp/ip-up".source | 52 | environment.etc."ppp/ip-up".source |
| 51 | environment.etc."ppp/ip-down".source | 53 | environment.etc."ppp/ip-down".source |
| 52 | # sops.secrets."pap-secrets".sopsFile | ||
| 53 | ]; | 54 | ]; |
| 54 | }; | 55 | }; |
| 55 | sops.secrets."pap-secrets" = { | ||
| 56 | format = "binary"; | ||
| 57 | sopsFile = ./pap-secrets; | ||
| 58 | path = "/etc/ppp/pap-secrets"; | ||
| 59 | }; | ||
| 60 | 56 | ||
| 61 | environment.etc = { | 57 | environment.etc = { |
| 62 | "ppp/ip-pre-up".source = let | 58 | "ppp/ip-pre-up".source = pkgs.resholve.writeScript "ip-pre-up" { |
| 63 | app = pkgs.writeShellApplication { | 59 | interpreter = pkgs.runtimeShell; |
| 64 | name = "ip-pre-up"; | 60 | inputs = [ pkgs.iproute2 pkgs.ethtool ]; |
| 65 | runtimeInputs = with pkgs; [ iproute2 ethtool ]; | 61 | execer = [ |
| 66 | text = '' | 62 | "cannot:${lib.getExe' pkgs.iproute2 "ip"}" |
| 67 | ethtool -K telekom tso off gso off gro off | 63 | "cannot:${lib.getExe' pkgs.iproute2 "tc"}" |
| 64 | ]; | ||
| 65 | } '' | ||
| 66 | ethtool -K telekom tso off gso off gro off | ||
| 68 | 67 | ||
| 69 | ip link del "ifb4${pppInterface}" || true | 68 | ip link del "ifb4${pppInterface}" || true |
| 70 | ip link add name "ifb4${pppInterface}" type ifb | 69 | ip link add name "ifb4${pppInterface}" type ifb |
| 71 | ip link set "ifb4${pppInterface}" up | 70 | ip link set "ifb4${pppInterface}" up |
| 72 | 71 | ||
| 73 | tc qdisc del dev "ifb4${pppInterface}" root || true | 72 | tc qdisc del dev "ifb4${pppInterface}" root || true |
| 74 | tc qdisc del dev "${pppInterface}" ingress || true | 73 | tc qdisc del dev "${pppInterface}" ingress || true |
| 75 | tc qdisc del dev "${pppInterface}" root || true | 74 | tc qdisc del dev "${pppInterface}" root || true |
| 76 | 75 | ||
| 77 | tc qdisc add dev "${pppInterface}" handle ffff: ingress | 76 | tc qdisc add dev "${pppInterface}" handle ffff: ingress |
| 78 | tc filter add dev "${pppInterface}" parent ffff: basic action ctinfo dscp 0x0000003f 0x00000040 action mirred egress redirect dev "ifb4${pppInterface}" | 77 | tc filter add dev "${pppInterface}" parent ffff: basic action ctinfo dscp 0x0000003f 0x00000040 action mirred egress redirect dev "ifb4${pppInterface}" |
| 79 | tc qdisc replace dev "ifb4${pppInterface}" root cake memlimit 128Mb overhead 35 mpu 74 regional diffserv4 bandwidth 285mbit | 78 | tc qdisc replace dev "ifb4${pppInterface}" root cake memlimit 128Mb overhead 35 mpu 74 regional diffserv4 bandwidth ${toString (builtins.floor (177968 * 0.95))}kbit |
| 80 | tc qdisc replace dev "${pppInterface}" root cake memlimit 128Mb overhead 35 mpu 74 regional nat diffserv4 wash bandwidth 143mbit | 79 | tc qdisc replace dev "${pppInterface}" root cake memlimit 128Mb overhead 35 mpu 74 regional nat diffserv4 wash bandwidth ${toString (builtins.floor (41216 * 0.95))}kbit |
| 81 | ''; | 80 | ''; |
| 82 | }; | 81 | "ppp/ip-up".source = pkgs.resholve.writeScript "ip-up" { |
| 83 | in "${app}/bin/${app.meta.mainProgram}"; | 82 | interpreter = pkgs.runtimeShell; |
| 84 | "ppp/ip-up".source = let | 83 | inputs = [ pkgs.iproute2 ]; |
| 85 | app = pkgs.writeShellApplication { | 84 | execer = [ "cannot:${lib.getExe' pkgs.iproute2 "ip"}" ]; |
| 86 | name = "ip-up"; | 85 | } '' |
| 87 | runtimeInputs = with pkgs; [ iproute2 ]; | 86 | ip route add default via "$5" dev "${pppInterface}" metric 512 |
| 88 | text = '' | 87 | ''; |
| 89 | ip route add default via "$5" dev "${pppInterface}" metric 512 | 88 | "ppp/ip-down".source = pkgs.resholve.writeScript "ip-down" { |
| 90 | ''; | 89 | interpreter = pkgs.runtimeShell; |
| 91 | }; | 90 | inputs = [ pkgs.iproute2 ]; |
| 92 | in "${app}/bin/${app.meta.mainProgram}"; | 91 | execer = [ "cannot:${lib.getExe' pkgs.iproute2 "ip"}" ]; |
| 93 | "ppp/ip-down".source = let | 92 | } '' |
| 94 | app = pkgs.writeShellApplication { | 93 | ip link del "ifb4${pppInterface}" |
| 95 | name = "ip-down"; | 94 | ''; |
| 96 | runtimeInputs = with pkgs; [ iproute2 ]; | ||
| 97 | text = '' | ||
| 98 | ip link del "ifb4${pppInterface}" | ||
| 99 | ''; | ||
| 100 | }; | ||
| 101 | in "${app}/bin/${app.meta.mainProgram}"; | ||
| 102 | }; | 95 | }; |
| 103 | 96 | ||
| 104 | systemd.network.networks.${pppInterface} = { | 97 | systemd.network.networks.${pppInterface} = { |
diff --git a/hosts/vidhar/network/ruleset.nft b/hosts/vidhar/network/ruleset.nft index 1edae167..dd750394 100644 --- a/hosts/vidhar/network/ruleset.nft +++ b/hosts/vidhar/network/ruleset.nft | |||
| @@ -5,15 +5,15 @@ table arp filter { | |||
| 5 | limit lim_arp_local { | 5 | limit lim_arp_local { |
| 6 | rate over 50 mbytes/second burst 50 mbytes | 6 | rate over 50 mbytes/second burst 50 mbytes |
| 7 | } | 7 | } |
| 8 | limit lim_arp_gpon { | 8 | limit lim_arp_ppp { |
| 9 | rate over 7500 kbytes/second burst 7500 kbytes | 9 | rate over 7500 kbytes/second burst 7500 kbytes |
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | counter arp-rx {} | 12 | counter arp-rx {} |
| 13 | counter arp-tx {} | 13 | counter arp-tx {} |
| 14 | 14 | ||
| 15 | counter arp-ratelimit-gpon-rx {} | 15 | counter arp-ratelimit-ppp-rx {} |
| 16 | counter arp-ratelimit-gpon-tx {} | 16 | counter arp-ratelimit-ppp-tx {} |
| 17 | 17 | ||
| 18 | counter arp-ratelimit-local-rx {} | 18 | counter arp-ratelimit-local-rx {} |
| 19 | counter arp-ratelimit-local-tx {} | 19 | counter arp-ratelimit-local-tx {} |
| @@ -22,8 +22,8 @@ table arp filter { | |||
| 22 | type filter hook input priority filter | 22 | type filter hook input priority filter |
| 23 | policy accept | 23 | policy accept |
| 24 | 24 | ||
| 25 | iifname != gpon limit name lim_arp_local counter name arp-ratelimit-local-rx drop | 25 | iifname != @pppInterface@ limit name lim_arp_local counter name arp-ratelimit-local-rx drop |
| 26 | iifname gpon limit name lim_arp_gpon counter name arp-ratelimit-gpon-rx drop | 26 | iifname @pppInterface@ limit name lim_arp_ppp counter name arp-ratelimit-ppp-rx drop |
| 27 | 27 | ||
| 28 | counter name arp-rx | 28 | counter name arp-rx |
| 29 | } | 29 | } |
| @@ -32,8 +32,8 @@ table arp filter { | |||
| 32 | type filter hook output priority filter | 32 | type filter hook output priority filter |
| 33 | policy accept | 33 | policy accept |
| 34 | 34 | ||
| 35 | oifname != gpon limit name lim_arp_local counter name arp-ratelimit-local-tx drop | 35 | oifname != @pppInterface@ limit name lim_arp_local counter name arp-ratelimit-local-tx drop |
| 36 | oifname gpon limit name lim_arp_gpon counter name arp-ratelimit-gpon-tx drop | 36 | oifname @pppInterface@ limit name lim_arp_ppp counter name arp-ratelimit-ppp-tx drop |
| 37 | 37 | ||
| 38 | counter name arp-tx | 38 | counter name arp-tx |
| 39 | } | 39 | } |
| @@ -47,11 +47,11 @@ table inet filter { | |||
| 47 | limit lim_icmp_local { | 47 | limit lim_icmp_local { |
| 48 | rate over 50 mbytes/second burst 50 mbytes | 48 | rate over 50 mbytes/second burst 50 mbytes |
| 49 | } | 49 | } |
| 50 | limit lim_icmp_gpon { | 50 | limit lim_icmp_ppp { |
| 51 | rate over 7500 kbytes/second burst 7500 kbytes | 51 | rate over 7500 kbytes/second burst 7500 kbytes |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | counter icmp-ratelimit-gpon-fw {} | 54 | counter icmp-ratelimit-ppp-fw {} |
| 55 | counter icmp-ratelimit-local-fw {} | 55 | counter icmp-ratelimit-local-fw {} |
| 56 | 56 | ||
| 57 | counter icmp-fw {} | 57 | counter icmp-fw {} |
| @@ -59,7 +59,8 @@ table inet filter { | |||
| 59 | counter invalid-fw {} | 59 | counter invalid-fw {} |
| 60 | counter fw-lo {} | 60 | counter fw-lo {} |
| 61 | counter fw-lan {} | 61 | counter fw-lan {} |
| 62 | counter fw-gpon {} | 62 | counter fw-ppp {} |
| 63 | counter fw-kimai {} | ||
| 63 | 64 | ||
| 64 | counter fw-cups {} | 65 | counter fw-cups {} |
| 65 | 66 | ||
| @@ -74,7 +75,7 @@ table inet filter { | |||
| 74 | counter invalid-local4-rx {} | 75 | counter invalid-local4-rx {} |
| 75 | counter invalid-local6-rx {} | 76 | counter invalid-local6-rx {} |
| 76 | 77 | ||
| 77 | counter icmp-ratelimit-gpon-rx {} | 78 | counter icmp-ratelimit-ppp-rx {} |
| 78 | counter icmp-ratelimit-local-rx {} | 79 | counter icmp-ratelimit-local-rx {} |
| 79 | counter icmp-rx {} | 80 | counter icmp-rx {} |
| 80 | 81 | ||
| @@ -94,6 +95,8 @@ table inet filter { | |||
| 94 | counter immich-rx {} | 95 | counter immich-rx {} |
| 95 | counter paperless-rx {} | 96 | counter paperless-rx {} |
| 96 | counter hledger-rx {} | 97 | counter hledger-rx {} |
| 98 | counter audiobookshelf-rx {} | ||
| 99 | counter kimai-rx {} | ||
| 97 | 100 | ||
| 98 | counter established-rx {} | 101 | counter established-rx {} |
| 99 | 102 | ||
| @@ -105,7 +108,7 @@ table inet filter { | |||
| 105 | 108 | ||
| 106 | counter tx-lo {} | 109 | counter tx-lo {} |
| 107 | 110 | ||
| 108 | counter icmp-ratelimit-gpon-tx {} | 111 | counter icmp-ratelimit-ppp-tx {} |
| 109 | counter icmp-ratelimit-local-tx {} | 112 | counter icmp-ratelimit-local-tx {} |
| 110 | counter icmp-tx {} | 113 | counter icmp-tx {} |
| 111 | 114 | ||
| @@ -125,15 +128,17 @@ table inet filter { | |||
| 125 | counter immich-tx {} | 128 | counter immich-tx {} |
| 126 | counter paperless-tx {} | 129 | counter paperless-tx {} |
| 127 | counter hledger-tx {} | 130 | counter hledger-tx {} |
| 131 | counter audiobookshelf-tx {} | ||
| 132 | counter kimai-tx {} | ||
| 128 | 133 | ||
| 129 | counter tx {} | 134 | counter tx {} |
| 130 | 135 | ||
| 131 | 136 | ||
| 132 | chain forward_icmp_accept { | 137 | chain forward_icmp_accept { |
| 133 | oifname { gpon, bifrost } limit name lim_icmp_gpon counter name icmp-ratelimit-gpon-fw drop | 138 | oifname { @pppInterface@, bifrost } limit name lim_icmp_ppp counter name icmp-ratelimit-ppp-fw drop |
| 134 | iifname { gpon, bifrost } limit name lim_icmp_gpon counter name icmp-ratelimit-gpon-fw drop | 139 | iifname { @pppInterface@, bifrost } limit name lim_icmp_ppp counter name icmp-ratelimit-ppp-fw drop |
| 135 | oifname != { gpon, bifrost } limit name lim_icmp_local counter name icmp-ratelimit-local-fw drop | 140 | oifname != { @pppInterface@, bifrost } limit name lim_icmp_local counter name icmp-ratelimit-local-fw drop |
| 136 | iifname != { gpon, bifrost } limit name lim_icmp_local counter name icmp-ratelimit-local-fw drop | 141 | iifname != { @pppInterface@, bifrost } limit name lim_icmp_local counter name icmp-ratelimit-local-fw drop |
| 137 | counter name icmp-fw accept | 142 | counter name icmp-fw accept |
| 138 | } | 143 | } |
| 139 | chain forward { | 144 | chain forward { |
| @@ -146,10 +151,15 @@ table inet filter { | |||
| 146 | 151 | ||
| 147 | iifname lo counter name fw-lo accept | 152 | iifname lo counter name fw-lo accept |
| 148 | 153 | ||
| 149 | oifname { lan, gpon, bifrost } meta l4proto $icmp_protos jump forward_icmp_accept | 154 | oifname { lan, @pppInterface@, bifrost } meta l4proto $icmp_protos jump forward_icmp_accept |
| 150 | iifname lan oifname { gpon, bifrost } counter name fw-lan accept | 155 | iifname lan oifname { @pppInterface@, bifrost } counter name fw-lan accept |
| 156 | iifname ve-kimai oifname @pppInterface@ counter name fw-kimai accept | ||
| 151 | 157 | ||
| 152 | iifname gpon oifname lan ct state { established, related } counter name fw-gpon accept | 158 | iifname @pppInterface@ oifname lan ct state { established, related } counter name fw-ppp accept |
| 159 | iifname @pppInterface@ oifname ve-kimai ct state { established, related } counter name fw-kimai accept | ||
| 160 | |||
| 161 | iifname bifrost oifname ve-kimai tcp dport 80 ip6 saddr $bifrost_surtr ip6 daddr 2a03:4000:52:ada:6::2 counter name kimai-rx accept | ||
| 162 | iifname ve-kimai oifname bifrost tcp sport 80 ip6 saddr 2a03:4000:52:ada:6::2 ip6 daddr $bifrost_surtr counter name kimai-tx accept | ||
| 153 | 163 | ||
| 154 | 164 | ||
| 155 | limit name lim_reject log level debug prefix "drop forward: " counter name reject-ratelimit-fw drop | 165 | limit name lim_reject log level debug prefix "drop forward: " counter name reject-ratelimit-fw drop |
| @@ -170,22 +180,22 @@ table inet filter { | |||
| 170 | iif != lo ip daddr 127.0.0.1/8 counter name invalid-local4-rx reject | 180 | iif != lo ip daddr 127.0.0.1/8 counter name invalid-local4-rx reject |
| 171 | iif != lo ip6 daddr ::1/128 counter name invalid-local6-rx reject | 181 | iif != lo ip6 daddr ::1/128 counter name invalid-local6-rx reject |
| 172 | 182 | ||
| 173 | iifname { bifrost, gpon } meta l4proto $icmp_protos limit name lim_icmp_gpon counter name icmp-ratelimit-gpon-rx drop | 183 | iifname { bifrost, @pppInterface@ } meta l4proto $icmp_protos limit name lim_icmp_ppp counter name icmp-ratelimit-ppp-rx drop |
| 174 | iifname != { bifrost, gpon } meta l4proto $icmp_protos limit name lim_icmp_local counter name icmp-ratelimit-local-rx drop | 184 | iifname != { bifrost, @pppInterface@ } meta l4proto $icmp_protos limit name lim_icmp_local counter name icmp-ratelimit-local-rx drop |
| 175 | meta l4proto $icmp_protos counter name icmp-rx accept | 185 | meta l4proto $icmp_protos counter name icmp-rx accept |
| 176 | 186 | ||
| 177 | iifname { lan, mgmt, gpon, yggdrasil, bifrost } tcp dport 22 counter name ssh-rx accept | 187 | iifname { lan, mgmt, @pppInterface@, yggdrasil, bifrost } tcp dport 22 counter name ssh-rx accept |
| 178 | iifname { lan, mgmt, gpon, yggdrasil, bifrost } udp dport 60000-61000 counter name mosh-rx accept | 188 | iifname { lan, mgmt, @pppInterface@, yggdrasil, bifrost } udp dport 60000-61000 counter name mosh-rx accept |
| 179 | 189 | ||
| 180 | iifname { lan, mgmt, wifibh, yggdrasil } meta l4proto { tcp, udp } th dport 53 counter name dns-rx accept | 190 | iifname { lan, mgmt, wifibh, yggdrasil } meta l4proto { tcp, udp } th dport 53 counter name dns-rx accept |
| 181 | 191 | ||
| 182 | iifname { lan, yggdrasil } tcp dport 2049 counter name nfs-rx accept | 192 | iifname { lan, yggdrasil } tcp dport 2049 counter name nfs-rx accept |
| 183 | 193 | ||
| 184 | iifname { lan, mgmt, gpon } meta protocol ip udp dport 51820 counter name wg-rx accept | 194 | iifname { lan, mgmt, @pppInterface@ } meta protocol ip udp dport 51820 counter name wg-rx accept |
| 185 | iifname { lan, mgmt, gpon } meta protocol ip6 udp dport 51821 counter name wg-rx accept | 195 | iifname { lan, mgmt, @pppInterface@ } meta protocol ip6 udp dport 51821 counter name wg-rx accept |
| 186 | iifname "yggdrasil-wg-*" meta l4proto gre counter name yggdrasil-gre-rx accept | 196 | iifname "yggdrasil-wg-*" meta l4proto gre counter name yggdrasil-gre-rx accept |
| 187 | 197 | ||
| 188 | iifname gpon meta protocol ip6 udp dport 546 udp sport 547 counter name ipv6-pd-rx accept | 198 | iifname @pppInterface@ meta protocol ip6 udp dport 546 udp sport 547 counter name ipv6-pd-rx accept |
| 189 | 199 | ||
| 190 | iifname mgmt udp dport 123 counter name ntp-rx accept | 200 | iifname mgmt udp dport 123 counter name ntp-rx accept |
| 191 | 201 | ||
| @@ -203,6 +213,7 @@ table inet filter { | |||
| 203 | iifname bifrost tcp dport 2283 ip6 saddr $bifrost_surtr counter name immich-rx accept | 213 | iifname bifrost tcp dport 2283 ip6 saddr $bifrost_surtr counter name immich-rx accept |
| 204 | iifname bifrost tcp dport 28981 ip6 saddr $bifrost_surtr counter name paperless-rx accept | 214 | iifname bifrost tcp dport 28981 ip6 saddr $bifrost_surtr counter name paperless-rx accept |
| 205 | iifname bifrost tcp dport 5000 ip6 saddr $bifrost_surtr counter name hledger-rx accept | 215 | iifname bifrost tcp dport 5000 ip6 saddr $bifrost_surtr counter name hledger-rx accept |
| 216 | iifname bifrost tcp dport 28982 ip6 saddr $bifrost_surtr counter name audiobookshelf-rx accept | ||
| 206 | 217 | ||
| 207 | ct state { established, related } counter name established-rx accept | 218 | ct state { established, related } counter name established-rx accept |
| 208 | 219 | ||
| @@ -220,8 +231,8 @@ table inet filter { | |||
| 220 | 231 | ||
| 221 | oifname lo counter name tx-lo accept | 232 | oifname lo counter name tx-lo accept |
| 222 | 233 | ||
| 223 | oifname { bifrost, gpon } meta l4proto $icmp_protos limit name lim_icmp_gpon counter name icmp-ratelimit-gpon-tx drop | 234 | oifname { bifrost, @pppInterface@ } meta l4proto $icmp_protos limit name lim_icmp_ppp counter name icmp-ratelimit-ppp-tx drop |
| 224 | oifname != { bifrost, gpon } meta l4proto $icmp_protos limit name lim_icmp_local counter name icmp-ratelimit-local-tx drop | 235 | oifname != { bifrost, @pppInterface@ } meta l4proto $icmp_protos limit name lim_icmp_local counter name icmp-ratelimit-local-tx drop |
| 225 | meta l4proto $icmp_protos counter name icmp-tx accept | 236 | meta l4proto $icmp_protos counter name icmp-tx accept |
| 226 | 237 | ||
| 227 | 238 | ||
| @@ -254,6 +265,7 @@ table inet filter { | |||
| 254 | iifname bifrost tcp sport 2283 ip6 daddr $bifrost_surtr counter name immich-tx accept | 265 | iifname bifrost tcp sport 2283 ip6 daddr $bifrost_surtr counter name immich-tx accept |
| 255 | iifname bifrost tcp sport 28981 ip6 daddr $bifrost_surtr counter name paperless-tx accept | 266 | iifname bifrost tcp sport 28981 ip6 daddr $bifrost_surtr counter name paperless-tx accept |
| 256 | iifname bifrost tcp sport 5000 ip6 daddr $bifrost_surtr counter name hledger-tx accept | 267 | iifname bifrost tcp sport 5000 ip6 daddr $bifrost_surtr counter name hledger-tx accept |
| 268 | iifname bifrost tcp sport 28982 ip6 daddr $bifrost_surtr counter name audiobookshelf-tx accept | ||
| 257 | 269 | ||
| 258 | 270 | ||
| 259 | counter name tx | 271 | counter name tx |
| @@ -261,28 +273,28 @@ table inet filter { | |||
| 261 | } | 273 | } |
| 262 | 274 | ||
| 263 | table inet nat { | 275 | table inet nat { |
| 264 | counter gpon-nat {} | 276 | counter ppp-nat {} |
| 265 | # counter container-nat {} | 277 | counter kimai-nat {} |
| 266 | 278 | ||
| 267 | chain postrouting { | 279 | chain postrouting { |
| 268 | type nat hook postrouting priority srcnat | 280 | type nat hook postrouting priority srcnat |
| 269 | policy accept | 281 | policy accept |
| 270 | 282 | ||
| 271 | 283 | ||
| 272 | meta nfproto ipv4 oifname gpon counter name gpon-nat masquerade | 284 | meta nfproto ipv4 oifname @pppInterface@ counter name ppp-nat masquerade |
| 273 | # iifname ve-* oifname gpon counter name container-nat masquerade | 285 | iifname ve-kimai oifname @pppInterface@ counter name kimai-nat masquerade |
| 274 | } | 286 | } |
| 275 | } | 287 | } |
| 276 | 288 | ||
| 277 | table inet mss_clamp { | 289 | table inet mss_clamp { |
| 278 | counter gpon-mss-clamp {} | 290 | counter ppp-mss-clamp {} |
| 279 | 291 | ||
| 280 | chain postrouting { | 292 | chain postrouting { |
| 281 | type filter hook postrouting priority mangle | 293 | type filter hook postrouting priority mangle |
| 282 | policy accept | 294 | policy accept |
| 283 | 295 | ||
| 284 | 296 | ||
| 285 | oifname gpon tcp flags & (syn|rst) == syn counter name gpon-mss-clamp tcp option maxseg size set rt mtu | 297 | oifname @pppInterface@ tcp flags & (syn|rst) == syn counter name ppp-mss-clamp tcp option maxseg size set rt mtu |
| 286 | } | 298 | } |
| 287 | } | 299 | } |
| 288 | 300 | ||
| @@ -417,7 +429,7 @@ table inet dscpclassify { | |||
| 417 | chain postrouting { | 429 | chain postrouting { |
| 418 | type filter hook postrouting priority filter + 1; policy accept | 430 | type filter hook postrouting priority filter + 1; policy accept |
| 419 | 431 | ||
| 420 | oifname != gpon return | 432 | oifname != @pppInterface@ return |
| 421 | 433 | ||
| 422 | ip dscp cs0 goto ct_set_cs0 | 434 | ip dscp cs0 goto ct_set_cs0 |
| 423 | ip dscp lephb goto ct_set_lephb | 435 | ip dscp lephb goto ct_set_lephb |
diff --git a/hosts/vidhar/paperless/default.nix b/hosts/vidhar/paperless/default.nix index 34cd18c4..dd02da38 100644 --- a/hosts/vidhar/paperless/default.nix +++ b/hosts/vidhar/paperless/default.nix | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | config = { | 4 | config = { |
| 5 | services.paperless = { | 5 | services.paperless = { |
| 6 | enable = true; | 6 | enable = true; |
| 7 | address = "[2a03:4000:52:ada:4:1::]"; | 7 | address = "2a03:4000:52:ada:4:1::"; |
| 8 | passwordFile = config.sops.secrets."paperless-rootpw".path; | 8 | passwordFile = config.sops.secrets."paperless-rootpw".path; |
| 9 | settings = { | 9 | settings = { |
| 10 | PAPERLESS_OCR_LANGUAGE = "deu+eng"; | 10 | PAPERLESS_OCR_LANGUAGE = "deu+eng"; |
diff --git a/hosts/vidhar/prometheus/default.nix b/hosts/vidhar/prometheus/default.nix index d368ad52..df135b58 100644 --- a/hosts/vidhar/prometheus/default.nix +++ b/hosts/vidhar/prometheus/default.nix | |||
| @@ -26,7 +26,8 @@ in { | |||
| 26 | enable = true; | 26 | enable = true; |
| 27 | 27 | ||
| 28 | extraFlags = [ | 28 | extraFlags = [ |
| 29 | "--enable-feature=remote-write-receiver" | 29 | "--web.enable-remote-write-receiver" |
| 30 | "--storage.tsdb.retention.size=35GB" | ||
| 30 | ]; | 31 | ]; |
| 31 | 32 | ||
| 32 | exporters = { | 33 | exporters = { |
| @@ -144,6 +145,17 @@ in { | |||
| 144 | ]; | 145 | ]; |
| 145 | scrape_interval = "15s"; | 146 | scrape_interval = "15s"; |
| 146 | } | 147 | } |
| 148 | { job_name = "zte"; | ||
| 149 | static_configs = [ | ||
| 150 | { targets = ["localhost:9900"]; } | ||
| 151 | ]; | ||
| 152 | relabel_configs = [ | ||
| 153 | { replacement = "dsl01"; | ||
| 154 | target_label = "instance"; | ||
| 155 | } | ||
| 156 | ]; | ||
| 157 | scrape_interval = "15s"; | ||
| 158 | } | ||
| 147 | { job_name = "unbound"; | 159 | { job_name = "unbound"; |
| 148 | static_configs = [ | 160 | static_configs = [ |
| 149 | { targets = ["localhost:${toString config.services.prometheus.exporters.unbound.port}"]; } | 161 | { targets = ["localhost:${toString config.services.prometheus.exporters.unbound.port}"]; } |
| @@ -287,6 +299,22 @@ in { | |||
| 287 | } | 299 | } |
| 288 | ]; | 300 | ]; |
| 289 | } | 301 | } |
| 302 | { name = "dsl-disconnects"; | ||
| 303 | rules = [ | ||
| 304 | { record = "dsl_uptime_seconds:resets_per_hour"; | ||
| 305 | expr = "resets(dsl_uptime_seconds[1h])"; | ||
| 306 | } | ||
| 307 | { record = "dsl_uptime_seconds:resets_per_day"; | ||
| 308 | expr = "resets(dsl_uptime_seconds[1d])"; | ||
| 309 | } | ||
| 310 | { record = "dsl_uptime_seconds:resets_per_week"; | ||
| 311 | expr = "resets(dsl_uptime_seconds[1w])"; | ||
| 312 | } | ||
| 313 | { record = "dsl_uptime_seconds:avg_resets_per_day"; | ||
| 314 | expr = "avg_over_time(dsl_uptime_seconds:resets_per_day[1w])"; | ||
| 315 | } | ||
| 316 | ]; | ||
| 317 | } | ||
| 290 | ]; | 318 | ]; |
| 291 | }) | 319 | }) |
| 292 | ]; | 320 | ]; |
| @@ -424,6 +452,47 @@ in { | |||
| 424 | }; | 452 | }; |
| 425 | }; | 453 | }; |
| 426 | 454 | ||
| 455 | systemd.services."prometheus-zte-exporter@dsl01.mgmt.yggdrasil" = { | ||
| 456 | wantedBy = [ "multi-user.target" ]; | ||
| 457 | after = [ "network.target" ]; | ||
| 458 | serviceConfig = { | ||
| 459 | Restart = "always"; | ||
| 460 | PrivateTmp = true; | ||
| 461 | WorkingDirectory = "/tmp"; | ||
| 462 | DynamicUser = true; | ||
| 463 | CapabilityBoundingSet = [""]; | ||
| 464 | DeviceAllow = [""]; | ||
| 465 | LockPersonality = true; | ||
| 466 | MemoryDenyWriteExecute = true; | ||
| 467 | NoNewPrivileges = true; | ||
| 468 | PrivateDevices = true; | ||
| 469 | ProtectClock = true; | ||
| 470 | ProtectControlGroups = true; | ||
| 471 | ProtectHome = true; | ||
| 472 | ProtectHostname = true; | ||
| 473 | ProtectKernelLogs = true; | ||
| 474 | ProtectKernelModules = true; | ||
| 475 | ProtectKernelTunables = true; | ||
| 476 | ProtectSystem = "strict"; | ||
| 477 | RemoveIPC = true; | ||
| 478 | RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; | ||
| 479 | RestrictNamespaces = true; | ||
| 480 | RestrictRealtime = true; | ||
| 481 | RestrictSUIDSGID = true; | ||
| 482 | SystemCallArchitectures = "native"; | ||
| 483 | UMask = "0077"; | ||
| 484 | |||
| 485 | Type = "simple"; | ||
| 486 | ExecStart = "${pkgs.zte-prometheus-exporter}/bin/zte-prometheus-exporter"; | ||
| 487 | Environment = "ZTE_BASEURL=http://10.141.1.3 ZTE_HOSTNAME=localhost ZTE_PORT=9900"; | ||
| 488 | EnvironmentFile = config.sops.secrets."zte_dsl01.mgmt.yggdrasil".path; | ||
| 489 | }; | ||
| 490 | }; | ||
| 491 | sops.secrets."zte_dsl01.mgmt.yggdrasil" = { | ||
| 492 | format = "binary"; | ||
| 493 | sopsFile = ./zte_dsl01.mgmt.yggdrasil; | ||
| 494 | }; | ||
| 495 | |||
| 427 | services.nginx = { | 496 | services.nginx = { |
| 428 | upstreams.prometheus = { | 497 | upstreams.prometheus = { |
| 429 | servers = { "localhost:${toString config.services.prometheus.port}" = {}; }; | 498 | servers = { "localhost:${toString config.services.prometheus.port}" = {}; }; |
diff --git a/hosts/vidhar/prometheus/zte_dsl01.mgmt.yggdrasil b/hosts/vidhar/prometheus/zte_dsl01.mgmt.yggdrasil new file mode 100644 index 00000000..1c9c1fe0 --- /dev/null +++ b/hosts/vidhar/prometheus/zte_dsl01.mgmt.yggdrasil | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | { | ||
| 2 | "data": "ENC[AES256_GCM,data:nAsn7dhfDr0+V1cJjpqWn/kJQt2zGjlfQKi3n5speroJkL3IvMG/9fsTaXJQZSi2gPlrN8GbxKQ=,iv:9g0V3xRBC+sa/JPP2bUZMfg//VuKT5qI7ua9iU4QRCg=,tag:fzwih9OHUBLmx8dxL4BjGg==,type:str]", | ||
| 3 | "sops": { | ||
| 4 | "kms": null, | ||
| 5 | "gcp_kms": null, | ||
| 6 | "azure_kv": null, | ||
| 7 | "hc_vault": null, | ||
| 8 | "age": [ | ||
| 9 | { | ||
| 10 | "recipient": "age1qffdqvy9arld9zd5a5cylt0n98xhcns5shxhrhwjq5g4qa844ejselaa4l", | ||
| 11 | "enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBIaEE3bUFBY0xKSDUrVnc2\nbFpjSkNOSm56amJTNjdXcTljdDNRREhITm1NCjZrOUEwNFpxN2FmTVV5T2xCbENk\nMEFmVzlPZ29CTlJ4dVNCRUsyRFFseXcKLS0tIEhscVZ4VUVsaG9OUnBIRFE4WXA2\ncGFnbWpNMlNIQzFLc1Ryc1Z3NUl1bVUKi9zYBlF2vslGKu4GP368ApbvuxjZnQpF\nuOujXSNoEps21wY6xUENm+CbYbgaJjSgmb5c1IjAmnubVI4JVY9OyQ==\n-----END AGE ENCRYPTED FILE-----\n" | ||
| 12 | } | ||
| 13 | ], | ||
| 14 | "lastmodified": "2021-12-31T15:00:33Z", | ||
| 15 | "mac": "ENC[AES256_GCM,data:sw2NVXHLibbuOChgScLhSTjGZBjSoHpzIuRqfCW0eL3DwhL5CekG6T/oYu06KjNmxVjxwb3OmqECSU0TUvPn9ySOWwMSoBfyJpDoTHnZ+YOjOH351IOAMBNcBDJse7aLGRWW5YXKLDfmp8Dhg2hlMhCmkVwAquQjPhfmAdJfj64=,iv:wgM/BlRU2XJSGj7KvAo1WRamecffUDnFvv2+4twtsQY=,tag:0mXblJtTGMTvxndedws94A==,type:str]", | ||
| 16 | "pgp": [ | ||
| 17 | { | ||
| 18 | "created_at": "2023-01-30T10:58:49Z", | ||
| 19 | "enc": "-----BEGIN PGP MESSAGE-----\n\nhF4DXxoViZlp6dISAQdAcwl1Blp3J5wgpRJKbYI1G1yEZrRYeYuoDtYUh3ToMAQw\nd92/bIJJR5Ml91eDym9uBN0fFRRy72r6FOx4qZT7S4DhmuA84qCbASjF8bKSclc0\n0l4BBXvDS5Dz1Q7iYc+LxZjHASV1v73A+MaeCFvG/pjmHzF0z0EzBiAJD4ZWGcP0\nX2dDbjl+n9VFrvmeLRxQNh4XZW43iTXdRjwHDgm16zhd9X6VOVhr5UkC4Nyjq2Ar\n=4ZEa\n-----END PGP MESSAGE-----\n", | ||
| 20 | "fp": "30D3453B8CD02FE2A3E7C78C0FB536FB87AE8F51" | ||
| 21 | } | ||
| 22 | ], | ||
| 23 | "unencrypted_suffix": "_unencrypted", | ||
| 24 | "version": "3.7.1" | ||
| 25 | } | ||
| 26 | } \ No newline at end of file | ||
diff --git a/installer-profiles/cd-dvd.nix b/installer-profiles/cd-dvd.nix index 45291bad..ac12d885 100644 --- a/installer-profiles/cd-dvd.nix +++ b/installer-profiles/cd-dvd.nix | |||
| @@ -1,7 +1,13 @@ | |||
| 1 | { flakeInputs, ... }: | 1 | { flakeInputs, lib, ... }: |
| 2 | 2 | ||
| 3 | { | 3 | { |
| 4 | imports = [ | 4 | imports = [ |
| 5 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix" | 5 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix" |
| 6 | ]; | 6 | ]; |
| 7 | |||
| 8 | config = { | ||
| 9 | isoImage.squashfsCompression = "zstd -Xcompression-level 9"; | ||
| 10 | system.installer.channel.enable = false; | ||
| 11 | boot.loader.grub.memtest86.enable = lib.mkForce false; | ||
| 12 | }; | ||
| 7 | } | 13 | } |
diff --git a/installer-profiles/netboot.nix b/installer-profiles/netboot.nix index 28e8084d..6e39ebfb 100644 --- a/installer-profiles/netboot.nix +++ b/installer-profiles/netboot.nix | |||
| @@ -4,4 +4,9 @@ | |||
| 4 | imports = [ | 4 | imports = [ |
| 5 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/installer/netboot/netboot-minimal.nix" | 5 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/installer/netboot/netboot-minimal.nix" |
| 6 | ]; | 6 | ]; |
| 7 | |||
| 8 | config = { | ||
| 9 | netboot.squashfsCompression = "zstd -Xcompression-level 9"; | ||
| 10 | system.installer.channel.enable = false; | ||
| 11 | }; | ||
| 7 | } | 12 | } |
diff --git a/installer-profiles/nfsroot.nix b/installer-profiles/nfsroot.nix index 6bd875b4..a8f6def6 100644 --- a/installer-profiles/nfsroot.nix +++ b/installer-profiles/nfsroot.nix | |||
| @@ -8,4 +8,6 @@ | |||
| 8 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/profiles/base.nix" | 8 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/profiles/base.nix" |
| 9 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/profiles/installation-device.nix" | 9 | "${flakeInputs.nixpkgs.outPath}/nixos/modules/profiles/installation-device.nix" |
| 10 | ]; | 10 | ]; |
| 11 | |||
| 12 | config.system.installer.channel.enable = false; | ||
| 11 | } | 13 | } |
diff --git a/installer/default.nix b/installer/default.nix index ec47832a..26f38572 100644 --- a/installer/default.nix +++ b/installer/default.nix | |||
| @@ -8,7 +8,7 @@ with lib; | |||
| 8 | ]; | 8 | ]; |
| 9 | 9 | ||
| 10 | config = { | 10 | config = { |
| 11 | boot.initrd.availableKernelModules = [ "e1000e" ]; | 11 | boot.initrd.kernelModules = [ "e1000e" "virtio_net" ]; |
| 12 | 12 | ||
| 13 | hardware.cpu.amd.updateMicrocode = config.hardware.enableRedistributableFirmware; | 13 | hardware.cpu.amd.updateMicrocode = config.hardware.enableRedistributableFirmware; |
| 14 | 14 | ||
| @@ -47,7 +47,7 @@ with lib; | |||
| 47 | services.xserver.videoDrivers = [ "nvidia" ]; | 47 | services.xserver.videoDrivers = [ "nvidia" ]; |
| 48 | systemd.services.nvidia-control-devices = { | 48 | systemd.services.nvidia-control-devices = { |
| 49 | wantedBy = [ "multi-user.target" ]; | 49 | wantedBy = [ "multi-user.target" ]; |
| 50 | serviceConfig.ExecStart = "${pkgs.linuxPackages.nvidia_x11.bin}/bin/nvidia-smi"; | 50 | serviceConfig.ExecStart = lib.getExe' pkgs.linuxPackages.nvidia_x11.bin "nvidia-smi"; |
| 51 | }; | 51 | }; |
| 52 | nixpkgs.externalConfig.allowUnfree = true; | 52 | nixpkgs.externalConfig.allowUnfree = true; |
| 53 | 53 | ||
diff --git a/lib/pythonSet.nix b/lib/pythonSet.nix new file mode 100644 index 00000000..c69216cc --- /dev/null +++ b/lib/pythonSet.nix | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | { uv2nix, pyproject-nix, pyproject-build-systems, ... }: | ||
| 2 | { pkgs, python, overlay, lib ? pkgs.lib }: | ||
| 3 | (pkgs.callPackage pyproject-nix.build.packages { | ||
| 4 | inherit python; | ||
| 5 | }).overrideScope | ||
| 6 | ( | ||
| 7 | lib.composeManyExtensions [ | ||
| 8 | pyproject-build-systems.overlays.default | ||
| 9 | overlay | ||
| 10 | (final: prev: { | ||
| 11 | sdnotify = (prev.sdnotify.override { | ||
| 12 | sourcePreference = "sdist"; | ||
| 13 | }).overrideAttrs (oldAttrs: { | ||
| 14 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ | ||
| 15 | (final.resolveBuildSystem { setuptools = []; }) | ||
| 16 | ]; | ||
| 17 | }); | ||
| 18 | systemd-python = (prev.systemd-python.override { | ||
| 19 | sourcePreference = "sdist"; | ||
| 20 | }).overrideAttrs (oldAttrs: { | ||
| 21 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ | ||
| 22 | pkgs.pkg-config pkgs.systemd.dev | ||
| 23 | (final.resolveBuildSystem { setuptools = []; }) | ||
| 24 | ]; | ||
| 25 | }); | ||
| 26 | halo = (prev.halo.override { | ||
| 27 | sourcePreference = "sdist"; | ||
| 28 | }).overrideAttrs (oldAttrs: { | ||
| 29 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ | ||
| 30 | (final.resolveBuildSystem { setuptools = []; }) | ||
| 31 | ]; | ||
| 32 | }); | ||
| 33 | unshare = (prev.unshare.override { | ||
| 34 | sourcePreference = "sdist"; | ||
| 35 | }).overrideAttrs (oldAttrs: { | ||
| 36 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ | ||
| 37 | (final.resolveBuildSystem { setuptools = []; }) | ||
| 38 | ]; | ||
| 39 | }); | ||
| 40 | }) | ||
| 41 | ] | ||
| 42 | ) | ||
diff --git a/modules/abs-podcast-autoplaylist.nix b/modules/abs-podcast-autoplaylist.nix new file mode 100644 index 00000000..f526a434 --- /dev/null +++ b/modules/abs-podcast-autoplaylist.nix | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | { config, pkgs, lib, utils, ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | cfg = config.services.abs-podcast-autoplaylist; | ||
| 5 | |||
| 6 | enabledAttrs = lib.filterAttrs (_name: { enable, ... }: enable) cfg; | ||
| 7 | in { | ||
| 8 | options = { | ||
| 9 | services.abs-podcast-autoplaylist = lib.mkOption { | ||
| 10 | default = {}; | ||
| 11 | type = lib.types.attrsOf (lib.types.submodule ({ name, ... }: { | ||
| 12 | options = { | ||
| 13 | enable = lib.mkEnableOption "this instance of abs-podcast-autoplaylist" // { | ||
| 14 | default = true; | ||
| 15 | }; | ||
| 16 | cron = lib.mkOption { | ||
| 17 | type = lib.types.str; | ||
| 18 | default = "*-*-* *:00/30:00"; | ||
| 19 | }; | ||
| 20 | configSecret = lib.mkOption { | ||
| 21 | type = lib.types.str; | ||
| 22 | default = "abs-podcast-autoplaylist-${name}.toml"; | ||
| 23 | }; | ||
| 24 | }; | ||
| 25 | })); | ||
| 26 | }; | ||
| 27 | }; | ||
| 28 | |||
| 29 | config = lib.mkIf (enabledAttrs != {}) { | ||
| 30 | systemd.services = { | ||
| 31 | "abs-podcast-autoplaylist@" = { | ||
| 32 | serviceConfig = { | ||
| 33 | WorkingDirectory = "%d"; | ||
| 34 | DynamicUser = true; | ||
| 35 | ProtectHome = true; | ||
| 36 | PrivateTmp = true; | ||
| 37 | PrivateDevices = true; | ||
| 38 | Type = "oneshot"; | ||
| 39 | ExecStart = "${lib.getExe pkgs.abs-podcast-autoplaylist} %I.toml"; | ||
| 40 | TimeoutSec = "5min"; | ||
| 41 | }; | ||
| 42 | }; | ||
| 43 | } // lib.mapAttrs' (name: { configSecret, ... }: lib.nameValuePair "abs-podcast-autoplaylist@${utils.escapeSystemdPath name}" { | ||
| 44 | overrideStrategy = "asDropin"; | ||
| 45 | serviceConfig = { | ||
| 46 | LoadCredential = "${name}.toml:${config.sops.secrets.${configSecret}.path}"; | ||
| 47 | }; | ||
| 48 | }) enabledAttrs; | ||
| 49 | |||
| 50 | systemd.timers = lib.mapAttrs' (name: { cron, ... }: lib.nameValuePair "abs-podcast-autoplaylist@${utils.escapeSystemdPath name}" { | ||
| 51 | wantedBy = [ "timers.target" ]; | ||
| 52 | timerConfig.OnCalendar = cron; | ||
| 53 | }) enabledAttrs; | ||
| 54 | }; | ||
| 55 | } | ||
diff --git a/modules/borgcopy/.envrc b/modules/borgcopy/.envrc new file mode 100644 index 00000000..01e755c1 --- /dev/null +++ b/modules/borgcopy/.envrc | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | use flake | ||
| 2 | |||
| 3 | uv venv && uv sync | ||
| 4 | . .venv/bin/activate | ||
diff --git a/modules/borgcopy/.gitignore b/modules/borgcopy/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/modules/borgcopy/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | .venv | ||
| 2 | **/__pycache__ | ||
diff --git a/modules/borgcopy/default.nix b/modules/borgcopy/default.nix index 8e1afc27..af021777 100644 --- a/modules/borgcopy/default.nix +++ b/modules/borgcopy/default.nix | |||
| @@ -1,25 +1,32 @@ | |||
| 1 | { config, pkgs, lib, utils, flakeInputs, ... }: | 1 | { config, pkgs, lib, utils, flake, flakeInputs, ... }: |
| 2 | 2 | ||
| 3 | with lib; | 3 | with lib; |
| 4 | 4 | ||
| 5 | let | 5 | let |
| 6 | copyBorg = | 6 | copyBorg = let |
| 7 | with pkgs.poetry2nix; | 7 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; |
| 8 | mkPoetryApplication { | 8 | pythonSet = flake.lib.pythonSet { |
| 9 | projectDir = cleanPythonSources { src = ./.; }; | 9 | inherit pkgs; |
| 10 | python = pkgs.python312; | ||
| 11 | overlay = workspace.mkPyprojectOverlay { | ||
| 12 | sourcePreference = "wheel"; | ||
| 13 | }; | ||
| 14 | }; | ||
| 15 | virtualEnv = pythonSet.mkVirtualEnv "copy_borg" workspace.deps.default; | ||
| 16 | in virtualEnv.overrideAttrs (oldAttrs: { | ||
| 17 | meta = (oldAttrs.meta or {}) // { | ||
| 18 | mainProgram = "copy_borg"; | ||
| 19 | }; | ||
| 10 | 20 | ||
| 11 | overrides = overrides.withDefaults (self: super: { | 21 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ pkgs.makeWrapper ]; |
| 12 | pyprctl = super.pyprctl.overridePythonAttrs (oldAttrs: { | ||
| 13 | buildInputs = (oldAttrs.buildInputs or []) ++ [super.setuptools]; | ||
| 14 | }); | ||
| 15 | inherit (pkgs.python3Packages) python-unshare; | ||
| 16 | }); | ||
| 17 | 22 | ||
| 18 | postInstall = '' | 23 | postInstall = '' |
| 19 | wrapProgram $out/bin/copy_borg \ | 24 | ${oldAttrs.postInstall or ""} |
| 20 | --prefix PATH : ${makeBinPath (with pkgs; [util-linux borgbackup])}:${config.security.wrapperDir} | 25 | |
| 21 | ''; | 26 | wrapProgram $out/bin/copy_borg \ |
| 22 | }; | 27 | --prefix PATH : ${makeBinPath (with pkgs; [util-linux borgbackup])}:${config.security.wrapperDir} |
| 28 | ''; | ||
| 29 | }); | ||
| 23 | 30 | ||
| 24 | copyService = name: opts: nameValuePair "copy-borg@${utils.escapeSystemdPath name}" { | 31 | copyService = name: opts: nameValuePair "copy-borg@${utils.escapeSystemdPath name}" { |
| 25 | restartIfChanged = false; | 32 | restartIfChanged = false; |
diff --git a/modules/borgcopy/poetry.lock b/modules/borgcopy/poetry.lock deleted file mode 100644 index 759ecfe9..00000000 --- a/modules/borgcopy/poetry.lock +++ /dev/null | |||
| @@ -1,180 +0,0 @@ | |||
| 1 | # This file is automatically @generated by Poetry 1.4.2 and should not be changed by hand. | ||
| 2 | |||
| 3 | [[package]] | ||
| 4 | name = "colorama" | ||
| 5 | version = "0.4.6" | ||
| 6 | description = "Cross-platform colored terminal text." | ||
| 7 | category = "main" | ||
| 8 | optional = false | ||
| 9 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" | ||
| 10 | files = [ | ||
| 11 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, | ||
| 12 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, | ||
| 13 | ] | ||
| 14 | |||
| 15 | [[package]] | ||
| 16 | name = "halo" | ||
| 17 | version = "0.0.31" | ||
| 18 | description = "Beautiful terminal spinners in Python" | ||
| 19 | category = "main" | ||
| 20 | optional = false | ||
| 21 | python-versions = ">=3.4" | ||
| 22 | files = [ | ||
| 23 | {file = "halo-0.0.31-py2-none-any.whl", hash = "sha256:5350488fb7d2aa7c31a1344120cee67a872901ce8858f60da7946cef96c208ab"}, | ||
| 24 | {file = "halo-0.0.31.tar.gz", hash = "sha256:7b67a3521ee91d53b7152d4ee3452811e1d2a6321975137762eb3d70063cc9d6"}, | ||
| 25 | ] | ||
| 26 | |||
| 27 | [package.dependencies] | ||
| 28 | colorama = ">=0.3.9" | ||
| 29 | log-symbols = ">=0.0.14" | ||
| 30 | six = ">=1.12.0" | ||
| 31 | spinners = ">=0.0.24" | ||
| 32 | termcolor = ">=1.1.0" | ||
| 33 | |||
| 34 | [package.extras] | ||
| 35 | ipython = ["IPython (==5.7.0)", "ipywidgets (==7.1.0)"] | ||
| 36 | |||
| 37 | [[package]] | ||
| 38 | name = "humanize" | ||
| 39 | version = "4.6.0" | ||
| 40 | description = "Python humanize utilities" | ||
| 41 | category = "main" | ||
| 42 | optional = false | ||
| 43 | python-versions = ">=3.7" | ||
| 44 | files = [ | ||
| 45 | {file = "humanize-4.6.0-py3-none-any.whl", hash = "sha256:401201aca462749773f02920139f302450cb548b70489b9b4b92be39fe3c3c50"}, | ||
| 46 | {file = "humanize-4.6.0.tar.gz", hash = "sha256:5f1f22bc65911eb1a6ffe7659bd6598e33dcfeeb904eb16ee1e705a09bf75916"}, | ||
| 47 | ] | ||
| 48 | |||
| 49 | [package.extras] | ||
| 50 | tests = ["freezegun", "pytest", "pytest-cov"] | ||
| 51 | |||
| 52 | [[package]] | ||
| 53 | name = "log-symbols" | ||
| 54 | version = "0.0.14" | ||
| 55 | description = "Colored symbols for various log levels for Python" | ||
| 56 | category = "main" | ||
| 57 | optional = false | ||
| 58 | python-versions = "*" | ||
| 59 | files = [ | ||
| 60 | {file = "log_symbols-0.0.14-py3-none-any.whl", hash = "sha256:4952106ff8b605ab7d5081dd2c7e6ca7374584eff7086f499c06edd1ce56dcca"}, | ||
| 61 | {file = "log_symbols-0.0.14.tar.gz", hash = "sha256:cf0bbc6fe1a8e53f0d174a716bc625c4f87043cc21eb55dd8a740cfe22680556"}, | ||
| 62 | ] | ||
| 63 | |||
| 64 | [package.dependencies] | ||
| 65 | colorama = ">=0.3.9" | ||
| 66 | |||
| 67 | [[package]] | ||
| 68 | name = "pyprctl" | ||
| 69 | version = "0.1.3" | ||
| 70 | description = "An interface to Linux's prctl() syscall written in pure Python using ctypes." | ||
| 71 | category = "main" | ||
| 72 | optional = false | ||
| 73 | python-versions = ">=3.6" | ||
| 74 | files = [ | ||
| 75 | {file = "pyprctl-0.1.3-py3-none-any.whl", hash = "sha256:6302e5114f078fb33e5799835d0a69e2fc180bb6b28ad073515fa40c5272f1dd"}, | ||
| 76 | {file = "pyprctl-0.1.3.tar.gz", hash = "sha256:1fb54d3ab030ec02e4afc38fb9662d6634c12834e91ae7959de56a9c09f69c26"}, | ||
| 77 | ] | ||
| 78 | |||
| 79 | [[package]] | ||
| 80 | name = "python-dateutil" | ||
| 81 | version = "2.8.2" | ||
| 82 | description = "Extensions to the standard Python datetime module" | ||
| 83 | category = "main" | ||
| 84 | optional = false | ||
| 85 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" | ||
| 86 | files = [ | ||
| 87 | {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, | ||
| 88 | {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, | ||
| 89 | ] | ||
| 90 | |||
| 91 | [package.dependencies] | ||
| 92 | six = ">=1.5" | ||
| 93 | |||
| 94 | [[package]] | ||
| 95 | name = "python-unshare" | ||
| 96 | version = "0.2" | ||
| 97 | description = "Python bindings for the Linux unshare() syscall" | ||
| 98 | category = "main" | ||
| 99 | optional = false | ||
| 100 | python-versions = "*" | ||
| 101 | files = [ | ||
| 102 | {file = "python-unshare-0.2.tar.gz", hash = "sha256:f79b7de441b6c27930b775085a6a4fd2f378b628737aaaebc2a6c519023fd47a"}, | ||
| 103 | ] | ||
| 104 | |||
| 105 | [[package]] | ||
| 106 | name = "six" | ||
| 107 | version = "1.16.0" | ||
| 108 | description = "Python 2 and 3 compatibility utilities" | ||
| 109 | category = "main" | ||
| 110 | optional = false | ||
| 111 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" | ||
| 112 | files = [ | ||
| 113 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, | ||
| 114 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, | ||
| 115 | ] | ||
| 116 | |||
| 117 | [[package]] | ||
| 118 | name = "spinners" | ||
| 119 | version = "0.0.24" | ||
| 120 | description = "Spinners for terminals" | ||
| 121 | category = "main" | ||
| 122 | optional = false | ||
| 123 | python-versions = "*" | ||
| 124 | files = [ | ||
| 125 | {file = "spinners-0.0.24-py3-none-any.whl", hash = "sha256:2fa30d0b72c9650ad12bbe031c9943b8d441e41b4f5602b0ec977a19f3290e98"}, | ||
| 126 | {file = "spinners-0.0.24.tar.gz", hash = "sha256:1eb6aeb4781d72ab42ed8a01dcf20f3002bf50740d7154d12fb8c9769bf9e27f"}, | ||
| 127 | ] | ||
| 128 | |||
| 129 | [[package]] | ||
| 130 | name = "termcolor" | ||
| 131 | version = "2.2.0" | ||
| 132 | description = "ANSI color formatting for output in terminal" | ||
| 133 | category = "main" | ||
| 134 | optional = false | ||
| 135 | python-versions = ">=3.7" | ||
| 136 | files = [ | ||
| 137 | {file = "termcolor-2.2.0-py3-none-any.whl", hash = "sha256:91ddd848e7251200eac969846cbae2dacd7d71c2871e92733289e7e3666f48e7"}, | ||
| 138 | {file = "termcolor-2.2.0.tar.gz", hash = "sha256:dfc8ac3f350788f23b2947b3e6cfa5a53b630b612e6cd8965a015a776020b99a"}, | ||
| 139 | ] | ||
| 140 | |||
| 141 | [package.extras] | ||
| 142 | tests = ["pytest", "pytest-cov"] | ||
| 143 | |||
| 144 | [[package]] | ||
| 145 | name = "tqdm" | ||
| 146 | version = "4.65.0" | ||
| 147 | description = "Fast, Extensible Progress Meter" | ||
| 148 | category = "main" | ||
| 149 | optional = false | ||
| 150 | python-versions = ">=3.7" | ||
| 151 | files = [ | ||
| 152 | {file = "tqdm-4.65.0-py3-none-any.whl", hash = "sha256:c4f53a17fe37e132815abceec022631be8ffe1b9381c2e6e30aa70edc99e9671"}, | ||
| 153 | {file = "tqdm-4.65.0.tar.gz", hash = "sha256:1871fb68a86b8fb3b59ca4cdd3dcccbc7e6d613eeed31f4c332531977b89beb5"}, | ||
| 154 | ] | ||
| 155 | |||
| 156 | [package.dependencies] | ||
| 157 | colorama = {version = "*", markers = "platform_system == \"Windows\""} | ||
| 158 | |||
| 159 | [package.extras] | ||
| 160 | dev = ["py-make (>=0.1.0)", "twine", "wheel"] | ||
| 161 | notebook = ["ipywidgets (>=6)"] | ||
| 162 | slack = ["slack-sdk"] | ||
| 163 | telegram = ["requests"] | ||
| 164 | |||
| 165 | [[package]] | ||
| 166 | name = "xdg" | ||
| 167 | version = "6.0.0" | ||
| 168 | description = "Variables defined by the XDG Base Directory Specification" | ||
| 169 | category = "main" | ||
| 170 | optional = false | ||
| 171 | python-versions = ">=3.7,<4.0" | ||
| 172 | files = [ | ||
| 173 | {file = "xdg-6.0.0-py3-none-any.whl", hash = "sha256:df3510755b4395157fc04fc3b02467c777f3b3ca383257397f09ab0d4c16f936"}, | ||
| 174 | {file = "xdg-6.0.0.tar.gz", hash = "sha256:24278094f2d45e846d1eb28a2ebb92d7b67fc0cab5249ee3ce88c95f649a1c92"}, | ||
| 175 | ] | ||
| 176 | |||
| 177 | [metadata] | ||
| 178 | lock-version = "2.0" | ||
| 179 | python-versions = ">=3.10.0,<3.12" | ||
| 180 | content-hash = "3c6b538852447a8f3ae34e1be122716d47e669a2b44f7c5d3d850e5d877353c7" | ||
diff --git a/modules/borgcopy/pyproject.toml b/modules/borgcopy/pyproject.toml index f3401ed2..d76d73c6 100644 --- a/modules/borgcopy/pyproject.toml +++ b/modules/borgcopy/pyproject.toml | |||
| @@ -1,22 +1,25 @@ | |||
| 1 | [tool.poetry] | 1 | [project] |
| 2 | name = "copy_borg" | 2 | name = "copy_borg" |
| 3 | version = "0.0.0" | 3 | version = "0.0.0" |
| 4 | authors = ["Gregor Kleen <gkleen@yggdrasil.li>"] | ||
| 5 | description = "" | 4 | description = "" |
| 5 | authors = [{ name = "Gregor Kleen", email = "gkleen@yggdrasil.li" }] | ||
| 6 | requires-python = "~=3.12" | ||
| 7 | dependencies = [ | ||
| 8 | "humanize>=4.6.0,<5", | ||
| 9 | "tqdm>=4.65.0,<5", | ||
| 10 | "python-dateutil>=2.8.2,<3", | ||
| 11 | "xdg>=6.0.0,<7", | ||
| 12 | "pyprctl>=0.1.3,<0.2", | ||
| 13 | "halo>=0.0.31,<0.0.32", | ||
| 14 | "unshare>=0.22", | ||
| 15 | ] | ||
| 6 | 16 | ||
| 7 | [tool.poetry.scripts] | 17 | [project.scripts] |
| 8 | copy_borg = "copy_borg.__main__:main" | 18 | copy_borg = "copy_borg.__main__:main" |
| 9 | 19 | ||
| 10 | [tool.poetry.dependencies] | ||
| 11 | python = ">=3.10.0,<3.12" | ||
| 12 | humanize = "^4.6.0" | ||
| 13 | tqdm = "^4.65.0" | ||
| 14 | python-dateutil = "^2.8.2" | ||
| 15 | xdg = "^6.0.0" | ||
| 16 | python-unshare = "^0.2" | ||
| 17 | pyprctl = "^0.1.3" | ||
| 18 | halo = "^0.0.31" | ||
| 19 | |||
| 20 | [build-system] | 20 | [build-system] |
| 21 | requires = ["poetry-core>=1.0.0"] | 21 | requires = ["hatchling"] |
| 22 | build-backend = "poetry.core.masonry.api" \ No newline at end of file | 22 | build-backend = "hatchling.build" |
| 23 | |||
| 24 | [tool.hatch.build.targets.wheel] | ||
| 25 | packages = ["copy_borg"] | ||
diff --git a/modules/borgcopy/uv.lock b/modules/borgcopy/uv.lock new file mode 100644 index 00000000..1a282598 --- /dev/null +++ b/modules/borgcopy/uv.lock | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | version = 1 | ||
| 2 | revision = 2 | ||
| 3 | requires-python = ">=3.12, <4" | ||
| 4 | |||
| 5 | [[package]] | ||
| 6 | name = "colorama" | ||
| 7 | version = "0.4.6" | ||
| 8 | source = { registry = "https://pypi.org/simple" } | ||
| 9 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } | ||
| 10 | wheels = [ | ||
| 11 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, | ||
| 12 | ] | ||
| 13 | |||
| 14 | [[package]] | ||
| 15 | name = "copy-borg" | ||
| 16 | version = "0.0.0" | ||
| 17 | source = { editable = "." } | ||
| 18 | dependencies = [ | ||
| 19 | { name = "halo" }, | ||
| 20 | { name = "humanize" }, | ||
| 21 | { name = "pyprctl" }, | ||
| 22 | { name = "python-dateutil" }, | ||
| 23 | { name = "tqdm" }, | ||
| 24 | { name = "unshare" }, | ||
| 25 | { name = "xdg" }, | ||
| 26 | ] | ||
| 27 | |||
| 28 | [package.metadata] | ||
| 29 | requires-dist = [ | ||
| 30 | { name = "halo", specifier = ">=0.0.31,<0.0.32" }, | ||
| 31 | { name = "humanize", specifier = ">=4.6.0,<5" }, | ||
| 32 | { name = "pyprctl", specifier = ">=0.1.3,<0.2" }, | ||
| 33 | { name = "python-dateutil", specifier = ">=2.8.2,<3" }, | ||
| 34 | { name = "tqdm", specifier = ">=4.65.0,<5" }, | ||
| 35 | { name = "unshare", specifier = ">=0.22" }, | ||
| 36 | { name = "xdg", specifier = ">=6.0.0,<7" }, | ||
| 37 | ] | ||
| 38 | |||
| 39 | [[package]] | ||
| 40 | name = "halo" | ||
| 41 | version = "0.0.31" | ||
| 42 | source = { registry = "https://pypi.org/simple" } | ||
| 43 | dependencies = [ | ||
| 44 | { name = "colorama" }, | ||
| 45 | { name = "log-symbols" }, | ||
| 46 | { name = "six" }, | ||
| 47 | { name = "spinners" }, | ||
| 48 | { name = "termcolor" }, | ||
| 49 | ] | ||
| 50 | sdist = { url = "https://files.pythonhosted.org/packages/ee/48/d53580d30b1fabf25d0d1fcc3f5b26d08d2ac75a1890ff6d262f9f027436/halo-0.0.31.tar.gz", hash = "sha256:7b67a3521ee91d53b7152d4ee3452811e1d2a6321975137762eb3d70063cc9d6", size = 11666, upload-time = "2020-11-10T02:36:48.335Z" } | ||
| 51 | |||
| 52 | [[package]] | ||
| 53 | name = "humanize" | ||
| 54 | version = "4.12.3" | ||
| 55 | source = { registry = "https://pypi.org/simple" } | ||
| 56 | sdist = { url = "https://files.pythonhosted.org/packages/22/d1/bbc4d251187a43f69844f7fd8941426549bbe4723e8ff0a7441796b0789f/humanize-4.12.3.tar.gz", hash = "sha256:8430be3a615106fdfceb0b2c1b41c4c98c6b0fc5cc59663a5539b111dd325fb0", size = 80514, upload-time = "2025-04-30T11:51:07.98Z" } | ||
| 57 | wheels = [ | ||
| 58 | { url = "https://files.pythonhosted.org/packages/a0/1e/62a2ec3104394a2975a2629eec89276ede9dbe717092f6966fcf963e1bf0/humanize-4.12.3-py3-none-any.whl", hash = "sha256:2cbf6370af06568fa6d2da77c86edb7886f3160ecd19ee1ffef07979efc597f6", size = 128487, upload-time = "2025-04-30T11:51:06.468Z" }, | ||
| 59 | ] | ||
| 60 | |||
| 61 | [[package]] | ||
| 62 | name = "log-symbols" | ||
| 63 | version = "0.0.14" | ||
| 64 | source = { registry = "https://pypi.org/simple" } | ||
| 65 | dependencies = [ | ||
| 66 | { name = "colorama" }, | ||
| 67 | ] | ||
| 68 | sdist = { url = "https://files.pythonhosted.org/packages/45/87/e86645d758a4401c8c81914b6a88470634d1785c9ad09823fa4a1bd89250/log_symbols-0.0.14.tar.gz", hash = "sha256:cf0bbc6fe1a8e53f0d174a716bc625c4f87043cc21eb55dd8a740cfe22680556", size = 3211, upload-time = "2019-08-08T06:32:22.538Z" } | ||
| 69 | wheels = [ | ||
| 70 | { url = "https://files.pythonhosted.org/packages/28/5d/d710c38be68b0fb54e645048fe359c3904cc3cb64b2de9d40e1712bf110c/log_symbols-0.0.14-py3-none-any.whl", hash = "sha256:4952106ff8b605ab7d5081dd2c7e6ca7374584eff7086f499c06edd1ce56dcca", size = 3081, upload-time = "2019-08-08T06:32:20.604Z" }, | ||
| 71 | ] | ||
| 72 | |||
| 73 | [[package]] | ||
| 74 | name = "pyprctl" | ||
| 75 | version = "0.1.3" | ||
| 76 | source = { registry = "https://pypi.org/simple" } | ||
| 77 | sdist = { url = "https://files.pythonhosted.org/packages/c9/16/6ed71ebcad76c1cd5f22185bcc6b31c0ee62fc5e693b626febea8fedeba3/pyprctl-0.1.3.tar.gz", hash = "sha256:1fb54d3ab030ec02e4afc38fb9662d6634c12834e91ae7959de56a9c09f69c26", size = 18739, upload-time = "2021-10-26T23:52:03.87Z" } | ||
| 78 | wheels = [ | ||
| 79 | { url = "https://files.pythonhosted.org/packages/bf/5e/62765de39bbce8111fb1f4453a4a804913bf49179fa265fb713ed66c9d15/pyprctl-0.1.3-py3-none-any.whl", hash = "sha256:6302e5114f078fb33e5799835d0a69e2fc180bb6b28ad073515fa40c5272f1dd", size = 20016, upload-time = "2021-10-26T23:52:02.986Z" }, | ||
| 80 | ] | ||
| 81 | |||
| 82 | [[package]] | ||
| 83 | name = "python-dateutil" | ||
| 84 | version = "2.9.0.post0" | ||
| 85 | source = { registry = "https://pypi.org/simple" } | ||
| 86 | dependencies = [ | ||
| 87 | { name = "six" }, | ||
| 88 | ] | ||
| 89 | sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } | ||
| 90 | wheels = [ | ||
| 91 | { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, | ||
| 92 | ] | ||
| 93 | |||
| 94 | [[package]] | ||
| 95 | name = "six" | ||
| 96 | version = "1.17.0" | ||
| 97 | source = { registry = "https://pypi.org/simple" } | ||
| 98 | sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } | ||
| 99 | wheels = [ | ||
| 100 | { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, | ||
| 101 | ] | ||
| 102 | |||
| 103 | [[package]] | ||
| 104 | name = "spinners" | ||
| 105 | version = "0.0.24" | ||
| 106 | source = { registry = "https://pypi.org/simple" } | ||
| 107 | sdist = { url = "https://files.pythonhosted.org/packages/d3/91/bb331f0a43e04d950a710f402a0986a54147a35818df0e1658551c8d12e1/spinners-0.0.24.tar.gz", hash = "sha256:1eb6aeb4781d72ab42ed8a01dcf20f3002bf50740d7154d12fb8c9769bf9e27f", size = 5308, upload-time = "2020-02-19T21:42:32.326Z" } | ||
| 108 | wheels = [ | ||
| 109 | { url = "https://files.pythonhosted.org/packages/9f/8e/3310207a68118000ca27ac878b8386123628b335ecb3d4bec4743357f0d1/spinners-0.0.24-py3-none-any.whl", hash = "sha256:2fa30d0b72c9650ad12bbe031c9943b8d441e41b4f5602b0ec977a19f3290e98", size = 5499, upload-time = "2020-02-19T21:42:30.876Z" }, | ||
| 110 | ] | ||
| 111 | |||
| 112 | [[package]] | ||
| 113 | name = "termcolor" | ||
| 114 | version = "3.1.0" | ||
| 115 | source = { registry = "https://pypi.org/simple" } | ||
| 116 | sdist = { url = "https://files.pythonhosted.org/packages/ca/6c/3d75c196ac07ac8749600b60b03f4f6094d54e132c4d94ebac6ee0e0add0/termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970", size = 14324, upload-time = "2025-04-30T11:37:53.791Z" } | ||
| 117 | wheels = [ | ||
| 118 | { url = "https://files.pythonhosted.org/packages/4f/bd/de8d508070629b6d84a30d01d57e4a65c69aa7f5abe7560b8fad3b50ea59/termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa", size = 7684, upload-time = "2025-04-30T11:37:52.382Z" }, | ||
| 119 | ] | ||
| 120 | |||
| 121 | [[package]] | ||
| 122 | name = "tqdm" | ||
| 123 | version = "4.67.1" | ||
| 124 | source = { registry = "https://pypi.org/simple" } | ||
| 125 | dependencies = [ | ||
| 126 | { name = "colorama", marker = "sys_platform == 'win32'" }, | ||
| 127 | ] | ||
| 128 | sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737, upload-time = "2024-11-24T20:12:22.481Z" } | ||
| 129 | wheels = [ | ||
| 130 | { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540, upload-time = "2024-11-24T20:12:19.698Z" }, | ||
| 131 | ] | ||
| 132 | |||
| 133 | [[package]] | ||
| 134 | name = "unshare" | ||
| 135 | version = "0.22" | ||
| 136 | source = { registry = "https://pypi.org/simple" } | ||
| 137 | sdist = { url = "https://files.pythonhosted.org/packages/15/85/2ba218129c95b894efe87506489b525f859c40f6e21cb0521ff3cec754f4/unshare-0.22.tar.gz", hash = "sha256:d521d72cca6e876f22cbd5ff5eb51f1beef75e8f9c53b599b55fa05fba1dd3a6", size = 2041, upload-time = "2019-10-17T12:58:31.498Z" } | ||
| 138 | |||
| 139 | [[package]] | ||
| 140 | name = "xdg" | ||
| 141 | version = "6.0.0" | ||
| 142 | source = { registry = "https://pypi.org/simple" } | ||
| 143 | sdist = { url = "https://files.pythonhosted.org/packages/2a/b9/0e6e6f19fb75cf5e1758f4f33c1256738f718966700cffc0fde2f966218b/xdg-6.0.0.tar.gz", hash = "sha256:24278094f2d45e846d1eb28a2ebb92d7b67fc0cab5249ee3ce88c95f649a1c92", size = 3453, upload-time = "2023-02-27T19:27:44.309Z" } | ||
| 144 | wheels = [ | ||
| 145 | { url = "https://files.pythonhosted.org/packages/dd/54/3516c1cf349060fc3578686d271eba242f10ec00b4530c2985af9faac49b/xdg-6.0.0-py3-none-any.whl", hash = "sha256:df3510755b4395157fc04fc3b02467c777f3b3ca383257397f09ab0d4c16f936", size = 3855, upload-time = "2023-02-27T19:27:42.151Z" }, | ||
| 146 | ] | ||
diff --git a/modules/i18n.nix b/modules/i18n.nix new file mode 100644 index 00000000..f84e8b64 --- /dev/null +++ b/modules/i18n.nix | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | { | ||
| 2 | config, | ||
| 3 | lib, | ||
| 4 | pkgs, | ||
| 5 | ... | ||
| 6 | }: | ||
| 7 | let | ||
| 8 | aggregatedLocales = | ||
| 9 | (builtins.map | ||
| 10 | (l: (lib.replaceStrings [ "utf8" "utf-8" "UTF8" ] [ "UTF-8" "UTF-8" "UTF-8" ] l) + "/UTF-8") | ||
| 11 | ( | ||
| 12 | [ config.i18n.defaultLocale ] | ||
| 13 | ++ (lib.optionals (builtins.isList config.i18n.extraLocales) config.i18n.extraLocales) | ||
| 14 | ++ (lib.attrValues (lib.filterAttrs (n: _v: lib.hasPrefix "LC_" n) config.i18n.extraLocaleSettings)) | ||
| 15 | ) | ||
| 16 | ) | ||
| 17 | ++ (lib.optional (builtins.isString config.i18n.extraLocales) config.i18n.extraLocales); | ||
| 18 | in | ||
| 19 | { | ||
| 20 | disabledModules = [ "config/i18n.nix" ]; | ||
| 21 | |||
| 22 | ###### interface | ||
| 23 | |||
| 24 | options = { | ||
| 25 | |||
| 26 | i18n = { | ||
| 27 | glibcLocales = lib.mkOption { | ||
| 28 | type = lib.types.path; | ||
| 29 | default = pkgs.glibcLocales.override { | ||
| 30 | allLocales = lib.any (x: x == "all") config.i18n.supportedLocales; | ||
| 31 | locales = config.i18n.supportedLocales; | ||
| 32 | }; | ||
| 33 | defaultText = lib.literalExpression '' | ||
| 34 | pkgs.glibcLocales.override { | ||
| 35 | allLocales = lib.any (x: x == "all") config.i18n.supportedLocales; | ||
| 36 | locales = config.i18n.supportedLocales; | ||
| 37 | } | ||
| 38 | ''; | ||
| 39 | example = lib.literalExpression "pkgs.glibcLocales"; | ||
| 40 | description = '' | ||
| 41 | Customized pkg.glibcLocales package. | ||
| 42 | |||
| 43 | Changing this option can disable handling of i18n.defaultLocale | ||
| 44 | and supportedLocale. | ||
| 45 | ''; | ||
| 46 | }; | ||
| 47 | |||
| 48 | defaultLocale = lib.mkOption { | ||
| 49 | type = lib.types.str; | ||
| 50 | default = "en_US.UTF-8"; | ||
| 51 | example = "nl_NL.UTF-8"; | ||
| 52 | description = '' | ||
| 53 | The default locale. It determines the language for program | ||
| 54 | messages, the format for dates and times, sort order, and so on. | ||
| 55 | It also determines the character set, such as UTF-8. | ||
| 56 | ''; | ||
| 57 | }; | ||
| 58 | |||
| 59 | extraLocales = lib.mkOption { | ||
| 60 | type = lib.types.either (lib.types.listOf lib.types.str) (lib.types.enum [ "all" ]); | ||
| 61 | default = [ ]; | ||
| 62 | example = [ "nl_NL.UTF-8" ]; | ||
| 63 | description = '' | ||
| 64 | Additional locales that the system should support, besides the ones | ||
| 65 | configured with {option}`i18n.defaultLocale` and | ||
| 66 | {option}`i18n.extraLocaleSettings`. | ||
| 67 | Set this to `"all"` to install all available locales. | ||
| 68 | ''; | ||
| 69 | }; | ||
| 70 | |||
| 71 | extraLocaleSettings = lib.mkOption { | ||
| 72 | type = lib.types.attrsOf lib.types.str; | ||
| 73 | default = { }; | ||
| 74 | example = { | ||
| 75 | LC_MESSAGES = "en_US.UTF-8"; | ||
| 76 | LC_TIME = "de_DE.UTF-8"; | ||
| 77 | }; | ||
| 78 | description = '' | ||
| 79 | A set of additional system-wide locale settings other than | ||
| 80 | `LANG` which can be configured with | ||
| 81 | {option}`i18n.defaultLocale`. | ||
| 82 | ''; | ||
| 83 | }; | ||
| 84 | |||
| 85 | supportedLocales = lib.mkOption { | ||
| 86 | type = lib.types.listOf lib.types.str; | ||
| 87 | visible = false; | ||
| 88 | default = lib.unique ( | ||
| 89 | [ | ||
| 90 | "C.UTF-8/UTF-8" | ||
| 91 | "en_US.UTF-8/UTF-8" | ||
| 92 | ] | ||
| 93 | ++ aggregatedLocales | ||
| 94 | ); | ||
| 95 | example = [ | ||
| 96 | "en_US.UTF-8/UTF-8" | ||
| 97 | "nl_NL.UTF-8/UTF-8" | ||
| 98 | "nl_NL/ISO-8859-1" | ||
| 99 | ]; | ||
| 100 | description = '' | ||
| 101 | List of locales that the system should support. The value | ||
| 102 | `"all"` means that all locales supported by | ||
| 103 | Glibc will be installed. A full list of supported locales | ||
| 104 | can be found at <https://sourceware.org/git/?p=glibc.git;a=blob;f=localedata/SUPPORTED>. | ||
| 105 | ''; | ||
| 106 | }; | ||
| 107 | |||
| 108 | }; | ||
| 109 | |||
| 110 | }; | ||
| 111 | |||
| 112 | ###### implementation | ||
| 113 | |||
| 114 | config = { | ||
| 115 | warnings = | ||
| 116 | lib.optional | ||
| 117 | ( | ||
| 118 | !( | ||
| 119 | (lib.subtractLists config.i18n.supportedLocales aggregatedLocales) == [ ] | ||
| 120 | || lib.any (x: x == "all") config.i18n.supportedLocales | ||
| 121 | ) | ||
| 122 | ) | ||
| 123 | '' | ||
| 124 | `i18n.supportedLocales` is deprecated in favor of `i18n.extraLocales`, | ||
| 125 | and it seems you are using `i18n.supportedLocales` and forgot to | ||
| 126 | include some locales specified in `i18n.defaultLocale`, | ||
| 127 | `i18n.extraLocales` or `i18n.extraLocaleSettings`. | ||
| 128 | |||
| 129 | If you're trying to install additional locales not specified in | ||
| 130 | `i18n.defaultLocale` or `i18n.extraLocaleSettings`, consider adding | ||
| 131 | only those locales to `i18n.extraLocales`. | ||
| 132 | ''; | ||
| 133 | |||
| 134 | environment.systemPackages = | ||
| 135 | # We increase the priority a little, so that plain glibc in systemPackages can't win. | ||
| 136 | lib.optional (config.i18n.supportedLocales != [ ]) (lib.setPrio (-1) config.i18n.glibcLocales); | ||
| 137 | |||
| 138 | environment.sessionVariables = { | ||
| 139 | LANG = config.i18n.defaultLocale; | ||
| 140 | LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive"; | ||
| 141 | } // config.i18n.extraLocaleSettings; | ||
| 142 | |||
| 143 | systemd.globalEnvironment = lib.mkIf (config.i18n.supportedLocales != [ ]) { | ||
| 144 | LOCALE_ARCHIVE = "${config.i18n.glibcLocales}/lib/locale/locale-archive"; | ||
| 145 | }; | ||
| 146 | |||
| 147 | # ‘/etc/locale.conf’ is used by systemd. | ||
| 148 | environment.etc."locale.conf".source = pkgs.writeText "locale.conf" '' | ||
| 149 | LANG=${config.i18n.defaultLocale} | ||
| 150 | ${lib.concatStringsSep "\n" ( | ||
| 151 | lib.mapAttrsToList (n: v: "${n}=${v}") config.i18n.extraLocaleSettings | ||
| 152 | )} | ||
| 153 | ''; | ||
| 154 | |||
| 155 | }; | ||
| 156 | } | ||
diff --git a/modules/impermanence-timezone.nix b/modules/impermanence-timezone.nix new file mode 100644 index 00000000..a1edbfa5 --- /dev/null +++ b/modules/impermanence-timezone.nix | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | { config, lib, utils, pkgs, ... }: | ||
| 2 | |||
| 3 | { | ||
| 4 | options = { | ||
| 5 | environment.persistence = lib.mkOption { | ||
| 6 | type = lib.types.attrsOf (lib.types.submodule { | ||
| 7 | options = { | ||
| 8 | timezone = lib.mkEnableOption "storing system timezone"; | ||
| 9 | }; | ||
| 10 | }); | ||
| 11 | }; | ||
| 12 | }; | ||
| 13 | |||
| 14 | config = { | ||
| 15 | systemd = lib.mkMerge (lib.mapAttrsToList (name: cfg: lib.mkIf cfg.timezone { | ||
| 16 | services = { | ||
| 17 | "timezone@${utils.escapeSystemdPath name}" = { | ||
| 18 | wantedBy = [ "multi-user.target" ]; | ||
| 19 | serviceConfig = { | ||
| 20 | Type = "oneshot"; | ||
| 21 | RemainAfterExit = true; | ||
| 22 | ExecStart = "${pkgs.coreutils}/bin/cp -vP ${utils.escapeSystemdExecArg "${name}/etc/localtime"} /etc/localtime"; | ||
| 23 | ExecStop = "${pkgs.coreutils}/bin/cp -vP /etc/localtime ${utils.escapeSystemdExecArg "${name}/etc/localtime"}"; | ||
| 24 | }; | ||
| 25 | }; | ||
| 26 | "etc-localtime@${utils.escapeSystemdPath name}" = { | ||
| 27 | serviceConfig = { | ||
| 28 | Type = "oneshot"; | ||
| 29 | ExecStart = "${pkgs.coreutils}/bin/cp -vP /etc/localtime ${utils.escapeSystemdExecArg "${name}/etc/localtime"}"; | ||
| 30 | }; | ||
| 31 | }; | ||
| 32 | }; | ||
| 33 | paths."etc-localtime@${utils.escapeSystemdPath name}" = { | ||
| 34 | wantedBy = [ "timezone@${utils.escapeSystemdPath name}.service" ]; | ||
| 35 | after = [ "timezone@${utils.escapeSystemdPath name}.service" ]; | ||
| 36 | |||
| 37 | pathConfig.PathChanged = "/etc/localtime"; | ||
| 38 | }; | ||
| 39 | }) config.environment.persistence); | ||
| 40 | }; | ||
| 41 | } | ||
diff --git a/modules/impermanence.nix b/modules/impermanence.nix new file mode 100644 index 00000000..621576a3 --- /dev/null +++ b/modules/impermanence.nix | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | { flakeInputs, ... }: | ||
| 2 | { | ||
| 3 | imports = [ | ||
| 4 | flakeInputs.impermanence.nixosModules.impermanence | ||
| 5 | ]; | ||
| 6 | } | ||
diff --git a/modules/installer.nix b/modules/installer.nix new file mode 100644 index 00000000..3e5c6d5b --- /dev/null +++ b/modules/installer.nix | |||
| @@ -0,0 +1,56 @@ | |||
| 1 | { flake, config, lib, pkgs, ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | cfg = config.installer.links; | ||
| 5 | |||
| 6 | installerOutPath = { | ||
| 7 | "cd-dvd" = _: installerBuild: "${installerBuild.config.system.build.isoImage}/iso"; | ||
| 8 | "netboot" = {system, variant}: installerBuild: pkgs.runCommandLocal "${system}-${variant}" {} '' | ||
| 9 | mkdir $out | ||
| 10 | install -m 0444 -t $out \ | ||
| 11 | ${installerBuild.config.system.build.netbootRamdisk}/initrd \ | ||
| 12 | ${installerBuild.config.system.build.kernel}/${config.system.boot.loader.kernelFile} \ | ||
| 13 | ${installerBuild.config.system.build.netbootIpxeScript}/netboot.ipxe \ | ||
| 14 | ${pkgs.ipxe.override { | ||
| 15 | additionalTargets = { | ||
| 16 | "bin-i386-efi/ipxe.efi" = "i386-ipxe.efi"; | ||
| 17 | }; | ||
| 18 | additionalOptions = [ | ||
| 19 | "NSLOOKUP_CMD" | ||
| 20 | "PING_CMD" | ||
| 21 | "CONSOLE_CMD" | ||
| 22 | ]; | ||
| 23 | embedScript = pkgs.writeText "netboot.ipxe" '' | ||
| 24 | #!ipxe | ||
| 25 | |||
| 26 | chain netboot.ipxe | ||
| 27 | ''; | ||
| 28 | }}/{ipxe.efi,i386-ipxe.efi,ipxe.lkrn} | ||
| 29 | ''; | ||
| 30 | }; | ||
| 31 | in { | ||
| 32 | options = { | ||
| 33 | installer.links = lib.mkOption { | ||
| 34 | type = lib.types.listOf (lib.types.submodule { | ||
| 35 | options = { | ||
| 36 | system = lib.mkOption { | ||
| 37 | type = lib.types.str; | ||
| 38 | }; | ||
| 39 | variant = lib.mkOption { | ||
| 40 | type = lib.types.str; | ||
| 41 | }; | ||
| 42 | }; | ||
| 43 | }); | ||
| 44 | default = []; | ||
| 45 | }; | ||
| 46 | }; | ||
| 47 | |||
| 48 | config = lib.mkIf (cfg != []) { | ||
| 49 | systemd.tmpfiles.rules = map (installer'@{system, variant}: | ||
| 50 | let | ||
| 51 | installer = "${system}-${variant}"; | ||
| 52 | installerBuild = builtins.addErrorContext "while evaluating installer-${installer}" flake.nixosConfigurations.${"installer-${installer}"}; | ||
| 53 | in "L+ /run/installer-${installer} - - - - ${installerOutPath.${variant} installer' installerBuild}" | ||
| 54 | ) cfg; | ||
| 55 | }; | ||
| 56 | } | ||
diff --git a/modules/pgbackrest.nix b/modules/pgbackrest.nix index 81c74a8e..550e970b 100644 --- a/modules/pgbackrest.nix +++ b/modules/pgbackrest.nix | |||
| @@ -43,6 +43,8 @@ let | |||
| 43 | loglevelType = types.enum ["off" "error" "warn" "info" "detail" "debug" "trace"]; | 43 | loglevelType = types.enum ["off" "error" "warn" "info" "detail" "debug" "trace"]; |
| 44 | inherit (utils.systemdUtils.unitOptions) unitOption; | 44 | inherit (utils.systemdUtils.unitOptions) unitOption; |
| 45 | in { | 45 | in { |
| 46 | disabledModules = ["services/backup/pgbackrest.nix"]; | ||
| 47 | |||
| 46 | options = { | 48 | options = { |
| 47 | services.pgbackrest = { | 49 | services.pgbackrest = { |
| 48 | enable = mkEnableOption "pgBackRest"; | 50 | enable = mkEnableOption "pgBackRest"; |
diff --git a/modules/postsrsd.nix b/modules/postsrsd.nix new file mode 100644 index 00000000..bc941e3e --- /dev/null +++ b/modules/postsrsd.nix | |||
| @@ -0,0 +1,163 @@ | |||
| 1 | { | ||
| 2 | config, | ||
| 3 | lib, | ||
| 4 | pkgs, | ||
| 5 | ... | ||
| 6 | }: | ||
| 7 | let | ||
| 8 | |||
| 9 | cfg = config.services.postsrsd; | ||
| 10 | runtimeDirectoryName = "postsrsd"; | ||
| 11 | runtimeDirectory = "/run/${runtimeDirectoryName}"; | ||
| 12 | # TODO: follow RFC 42, but we need a libconfuse format first: | ||
| 13 | # https://github.com/NixOS/nixpkgs/issues/401565 | ||
| 14 | # Arrays in `libconfuse` look like this: {"Life", "Universe", "Everything"} | ||
| 15 | # See https://www.nongnu.org/confuse/tutorial-html/ar01s03.html. | ||
| 16 | # | ||
| 17 | # Note: We're using `builtins.toJSON` to escape strings, but JSON strings | ||
| 18 | # don't have exactly the same semantics as libconfuse strings. For example, | ||
| 19 | # "${F}" gets treated as an env var reference, see above issue for details. | ||
| 20 | libconfuseDomains = "{ " + lib.concatMapStringsSep ", " builtins.toJSON cfg.domains + " }"; | ||
| 21 | configFile = pkgs.writeText "postsrsd.conf" '' | ||
| 22 | secrets-file = "''${CREDENTIALS_DIRECTORY}/secrets-file" | ||
| 23 | domains = ${libconfuseDomains} | ||
| 24 | separator = "${cfg.separator}" | ||
| 25 | |||
| 26 | # Disable postsrsd's jailing in favor of confinement with systemd. | ||
| 27 | unprivileged-user = "" | ||
| 28 | chroot-dir = "" | ||
| 29 | |||
| 30 | ${cfg.extraConfig} | ||
| 31 | ''; | ||
| 32 | |||
| 33 | in | ||
| 34 | { | ||
| 35 | imports = | ||
| 36 | map | ||
| 37 | ( | ||
| 38 | name: | ||
| 39 | lib.mkRemovedOptionModule [ "services" "postsrsd" name ] '' | ||
| 40 | `postsrsd` was upgraded to `>= 2.0.0`, with some different behaviors and configuration settings: | ||
| 41 | - NixOS Release Notes: https://nixos.org/manual/nixos/unstable/release-notes#sec-nixpkgs-release-25.05-incompatibilities | ||
| 42 | - NixOS Options Reference: https://nixos.org/manual/nixos/unstable/options#opt-services.postsrsd.enable | ||
| 43 | - Migration instructions: https://github.com/roehling/postsrsd/blob/2.0.10/README.rst#migrating-from-version-1x | ||
| 44 | - Postfix Setup: https://github.com/roehling/postsrsd/blob/2.0.10/README.rst#postfix-setup | ||
| 45 | '' | ||
| 46 | ) | ||
| 47 | [ | ||
| 48 | "domain" | ||
| 49 | "forwardPort" | ||
| 50 | "reversePort" | ||
| 51 | "timeout" | ||
| 52 | "excludeDomains" | ||
| 53 | ]; | ||
| 54 | |||
| 55 | disabledModules = [ "services/mail/postsrsd.nix" ]; | ||
| 56 | |||
| 57 | options = { | ||
| 58 | services.postsrsd = { | ||
| 59 | enable = lib.mkOption { | ||
| 60 | type = lib.types.bool; | ||
| 61 | default = false; | ||
| 62 | description = "Whether to enable the postsrsd SRS server for Postfix."; | ||
| 63 | }; | ||
| 64 | |||
| 65 | secretsFile = lib.mkOption { | ||
| 66 | type = lib.types.path; | ||
| 67 | default = "/var/lib/postsrsd/postsrsd.secret"; | ||
| 68 | description = "Secret keys used for signing and verification"; | ||
| 69 | }; | ||
| 70 | |||
| 71 | domains = lib.mkOption { | ||
| 72 | type = lib.types.listOf lib.types.str; | ||
| 73 | description = "Domain names for rewrite"; | ||
| 74 | default = [ config.networking.hostName ]; | ||
| 75 | defaultText = lib.literalExpression "[ config.networking.hostName ]"; | ||
| 76 | }; | ||
| 77 | |||
| 78 | separator = lib.mkOption { | ||
| 79 | type = lib.types.enum [ | ||
| 80 | "-" | ||
| 81 | "=" | ||
| 82 | "+" | ||
| 83 | ]; | ||
| 84 | default = "="; | ||
| 85 | description = "First separator character in generated addresses"; | ||
| 86 | }; | ||
| 87 | |||
| 88 | user = lib.mkOption { | ||
| 89 | type = lib.types.str; | ||
| 90 | default = "postsrsd"; | ||
| 91 | description = "User for the daemon"; | ||
| 92 | }; | ||
| 93 | |||
| 94 | group = lib.mkOption { | ||
| 95 | type = lib.types.str; | ||
| 96 | default = "postsrsd"; | ||
| 97 | description = "Group for the daemon"; | ||
| 98 | }; | ||
| 99 | |||
| 100 | extraConfig = lib.mkOption { | ||
| 101 | type = lib.types.lines; | ||
| 102 | default = ""; | ||
| 103 | }; | ||
| 104 | |||
| 105 | configurePostfix = lib.mkOption { | ||
| 106 | type = lib.types.bool; | ||
| 107 | default = false; | ||
| 108 | description = "noop"; | ||
| 109 | }; | ||
| 110 | }; | ||
| 111 | }; | ||
| 112 | |||
| 113 | config = lib.mkIf cfg.enable { | ||
| 114 | users.users = lib.optionalAttrs (cfg.user == "postsrsd") { | ||
| 115 | postsrsd = { | ||
| 116 | group = cfg.group; | ||
| 117 | uid = config.ids.uids.postsrsd; | ||
| 118 | }; | ||
| 119 | }; | ||
| 120 | |||
| 121 | users.groups = lib.optionalAttrs (cfg.group == "postsrsd") { | ||
| 122 | postsrsd.gid = config.ids.gids.postsrsd; | ||
| 123 | }; | ||
| 124 | |||
| 125 | systemd.services.postsrsd-generate-secrets = { | ||
| 126 | path = [ pkgs.coreutils ]; | ||
| 127 | script = '' | ||
| 128 | if [ -e "${cfg.secretsFile}" ]; then | ||
| 129 | echo "Secrets file exists. Nothing to do!" | ||
| 130 | else | ||
| 131 | echo "WARNING: secrets file not found, autogenerating!" | ||
| 132 | DIR="$(dirname "${cfg.secretsFile}")" | ||
| 133 | install -m 750 -o ${cfg.user} -g ${cfg.group} -d "$DIR" | ||
| 134 | install -m 600 -o ${cfg.user} -g ${cfg.group} <(dd if=/dev/random bs=18 count=1 | base64) "${cfg.secretsFile}" | ||
| 135 | fi | ||
| 136 | ''; | ||
| 137 | serviceConfig = { | ||
| 138 | Type = "oneshot"; | ||
| 139 | }; | ||
| 140 | }; | ||
| 141 | |||
| 142 | systemd.services.postsrsd = { | ||
| 143 | description = "PostSRSd SRS rewriting server"; | ||
| 144 | after = [ | ||
| 145 | "network.target" | ||
| 146 | "postsrsd-generate-secrets.service" | ||
| 147 | ]; | ||
| 148 | before = [ "postfix.service" ]; | ||
| 149 | wantedBy = [ "multi-user.target" ]; | ||
| 150 | requires = [ "postsrsd-generate-secrets.service" ]; | ||
| 151 | confinement.enable = true; | ||
| 152 | |||
| 153 | serviceConfig = { | ||
| 154 | ExecStart = "${lib.getExe pkgs.postsrsd} -C ${configFile}"; | ||
| 155 | User = cfg.user; | ||
| 156 | Group = cfg.group; | ||
| 157 | PermissionsStartOnly = true; | ||
| 158 | RuntimeDirectory = runtimeDirectoryName; | ||
| 159 | LoadCredential = "secrets-file:${cfg.secretsFile}"; | ||
| 160 | }; | ||
| 161 | }; | ||
| 162 | }; | ||
| 163 | } | ||
diff --git a/modules/systemd-run0.nix b/modules/systemd-run0.nix new file mode 100644 index 00000000..8575ec7c --- /dev/null +++ b/modules/systemd-run0.nix | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | { config, lib, ... }: | ||
| 2 | { | ||
| 3 | config.security.pam.services.systemd-run0 = lib.mkIf (lib.versionAtLeast config.systemd.package.version "256") {}; | ||
| 4 | } | ||
diff --git a/modules/tzupdate.nix b/modules/tzupdate.nix new file mode 100644 index 00000000..6465d33f --- /dev/null +++ b/modules/tzupdate.nix | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | { | ||
| 2 | config, | ||
| 3 | lib, | ||
| 4 | pkgs, | ||
| 5 | ... | ||
| 6 | }: | ||
| 7 | let | ||
| 8 | cfg = config.services.tzupdate; | ||
| 9 | in | ||
| 10 | { | ||
| 11 | disabledModules = [ "services/misc/tzupdate.nix" ]; | ||
| 12 | |||
| 13 | options.services.tzupdate = { | ||
| 14 | enable = lib.mkOption { | ||
| 15 | type = lib.types.bool; | ||
| 16 | default = false; | ||
| 17 | description = '' | ||
| 18 | Enable the tzupdate timezone updating service. This provides | ||
| 19 | a one-shot service which can be activated with systemctl to | ||
| 20 | update the timezone. | ||
| 21 | ''; | ||
| 22 | }; | ||
| 23 | |||
| 24 | package = lib.mkPackageOption pkgs "tzupdate" { }; | ||
| 25 | |||
| 26 | timer.enable = lib.mkOption { | ||
| 27 | type = lib.types.bool; | ||
| 28 | default = true; | ||
| 29 | description = '' | ||
| 30 | Enable the tzupdate timer to update the timezone automatically. | ||
| 31 | ''; | ||
| 32 | }; | ||
| 33 | |||
| 34 | timer.interval = lib.mkOption { | ||
| 35 | type = lib.types.str; | ||
| 36 | default = "hourly"; | ||
| 37 | description = '' | ||
| 38 | The interval at which the tzupdate timer should run. See | ||
| 39 | {manpage}`systemd.time(7)` to understand the format. | ||
| 40 | ''; | ||
| 41 | }; | ||
| 42 | }; | ||
| 43 | |||
| 44 | config = lib.mkIf cfg.enable { | ||
| 45 | # We need to have imperative time zone management for this to work. | ||
| 46 | # This will give users an error if they have set an explicit time | ||
| 47 | # zone, which is better than silently overriding it. | ||
| 48 | time.timeZone = null; | ||
| 49 | |||
| 50 | # We provide a one-shot service that runs at startup once network | ||
| 51 | # interfaces are up, but we can’t ensure we actually have Internet access | ||
| 52 | # at that point. It can also be run manually with `systemctl start tzupdate`. | ||
| 53 | systemd.services.tzupdate = { | ||
| 54 | description = "tzupdate timezone update service"; | ||
| 55 | wantedBy = [ "multi-user.target" ]; | ||
| 56 | wants = [ "network-online.target" ]; | ||
| 57 | after = [ "network-online.target" ]; | ||
| 58 | script = '' | ||
| 59 | timezone="$(${lib.getExe cfg.package} --print-only)" | ||
| 60 | if [[ -n "$timezone" ]]; then | ||
| 61 | echo "Setting timezone to '$timezone'" | ||
| 62 | ${lib.getExe' config.systemd.package "timedatectl"} set-timezone "$timezone" | ||
| 63 | fi | ||
| 64 | ''; | ||
| 65 | |||
| 66 | serviceConfig = { | ||
| 67 | Type = "oneshot"; | ||
| 68 | }; | ||
| 69 | }; | ||
| 70 | |||
| 71 | systemd.timers.tzupdate = { | ||
| 72 | enable = cfg.timer.enable; | ||
| 73 | timerConfig = { | ||
| 74 | OnStartupSec = "30s"; | ||
| 75 | OnCalendar = cfg.timer.interval; | ||
| 76 | Persistent = true; | ||
| 77 | }; | ||
| 78 | wantedBy = [ "timers.target" ]; | ||
| 79 | }; | ||
| 80 | }; | ||
| 81 | } | ||
diff --git a/modules/uucp.nix b/modules/uucp.nix deleted file mode 100644 index 10f7297b..00000000 --- a/modules/uucp.nix +++ /dev/null | |||
| @@ -1,373 +0,0 @@ | |||
| 1 | { flake, config, lib, pkgs, ... }: | ||
| 2 | |||
| 3 | with lib; | ||
| 4 | |||
| 5 | let | ||
| 6 | portSpec = name: node: concatStringsSep "\n" (map (port: '' | ||
| 7 | port ${name}.${port} | ||
| 8 | type pipe | ||
| 9 | protocol ${node.protocols} | ||
| 10 | reliable true | ||
| 11 | command ${pkgs.openssh}/bin/ssh -x -o batchmode=yes ${name}.${port} | ||
| 12 | '') node.hostnames); | ||
| 13 | sysSpec = name: node: '' | ||
| 14 | system ${name} | ||
| 15 | time any | ||
| 16 | chat-seven-bit false | ||
| 17 | chat . "" | ||
| 18 | protocol ${node.protocols} | ||
| 19 | command-path ${concatStringsSep " " cfg.commandPath} | ||
| 20 | commands ${concatStringsSep " " node.commands} | ||
| 21 | ${concatStringsSep "\nalternate\n" (map (port: '' | ||
| 22 | port ${name}.${port} | ||
| 23 | '') node.hostnames)} | ||
| 24 | ''; | ||
| 25 | sshConfig = name: node: concatStringsSep "\n" (map (port: '' | ||
| 26 | Host ${name}.${port} | ||
| 27 | Hostname ${port} | ||
| 28 | IdentitiesOnly Yes | ||
| 29 | IdentityFile ${cfg.sshKeyDir}/${name} | ||
| 30 | '') node.hostnames); | ||
| 31 | sshKeyGen = name: node: '' | ||
| 32 | if [[ ! -e ${cfg.sshKeyDir}/${name} ]]; then | ||
| 33 | ${pkgs.openssh}/bin/ssh-keygen ${escapeShellArgs node.generateKey} -f ${cfg.sshKeyDir}/${name} | ||
| 34 | fi | ||
| 35 | ''; | ||
| 36 | restrictKey = key: '' | ||
| 37 | restrict,command="${chat}" ${key} | ||
| 38 | ''; | ||
| 39 | chat = pkgs.writeScript "chat" '' | ||
| 40 | #!${pkgs.stdenv.shell} | ||
| 41 | |||
| 42 | echo . | ||
| 43 | exec ${config.security.wrapperDir}/uucico | ||
| 44 | ''; | ||
| 45 | |||
| 46 | nodeCfg = { | ||
| 47 | options = { | ||
| 48 | commands = mkOption { | ||
| 49 | type = types.listOf types.str; | ||
| 50 | default = cfg.defaultCommands; | ||
| 51 | defaultText = literalExpression "config.services.uucp.defaultCommands"; | ||
| 52 | description = "Commands to allow for this remote"; | ||
| 53 | }; | ||
| 54 | |||
| 55 | protocols = mkOption { | ||
| 56 | type = types.separatedString ""; | ||
| 57 | default = cfg.defaultProtocols; | ||
| 58 | defaultText = literalExpression "config.services.uucp.defaultProtocols"; | ||
| 59 | description = "UUCP protocols to use for this remote"; | ||
| 60 | }; | ||
| 61 | |||
| 62 | publicKeys = mkOption { | ||
| 63 | type = types.listOf types.str; | ||
| 64 | default = []; | ||
| 65 | description = "SSH client public keys for this node"; | ||
| 66 | }; | ||
| 67 | |||
| 68 | generateKey = mkOption { | ||
| 69 | type = types.listOf types.str; | ||
| 70 | default = [ "-t" "ed25519" "-N" "" ]; | ||
| 71 | description = "Arguments to pass to `ssh-keygen` to generate a keypair for communication with this host"; | ||
| 72 | }; | ||
| 73 | |||
| 74 | hostnames = mkOption { | ||
| 75 | type = types.listOf types.str; | ||
| 76 | default = []; | ||
| 77 | description = "Hostnames to try in order when connecting"; | ||
| 78 | }; | ||
| 79 | }; | ||
| 80 | }; | ||
| 81 | |||
| 82 | cfg = config.services.uucp; | ||
| 83 | in { | ||
| 84 | options = { | ||
| 85 | services.uucp = { | ||
| 86 | enable = mkOption { | ||
| 87 | type = types.bool; | ||
| 88 | default = false; | ||
| 89 | description = '' | ||
| 90 | If enabled we set up an account accesible via uucp over ssh | ||
| 91 | ''; | ||
| 92 | }; | ||
| 93 | |||
| 94 | nodeName = mkOption { | ||
| 95 | type = types.str; | ||
| 96 | default = "nixos"; | ||
| 97 | description = "uucp node name"; | ||
| 98 | }; | ||
| 99 | |||
| 100 | sshUser = mkOption { | ||
| 101 | type = types.attrs; | ||
| 102 | default = {}; | ||
| 103 | description = "Overrides for the local uucp linux-user"; | ||
| 104 | }; | ||
| 105 | |||
| 106 | extraSSHConfig = mkOption { | ||
| 107 | type = types.str; | ||
| 108 | default = ""; | ||
| 109 | description = "Extra SSH config"; | ||
| 110 | }; | ||
| 111 | |||
| 112 | remoteNodes = mkOption { | ||
| 113 | type = types.attrsOf (types.submodule nodeCfg); | ||
| 114 | default = {}; | ||
| 115 | description = '' | ||
| 116 | Ports to set up | ||
| 117 | Names will probably need to be configured in sshConfig | ||
| 118 | ''; | ||
| 119 | }; | ||
| 120 | |||
| 121 | commandPath = mkOption { | ||
| 122 | type = types.listOf types.path; | ||
| 123 | default = [ "${pkgs.rmail}/bin" ]; | ||
| 124 | defaultText = literalExpression ''[ "''${pkgs.rmail}/bin" ]''; | ||
| 125 | description = '' | ||
| 126 | Command search path for all systems | ||
| 127 | ''; | ||
| 128 | }; | ||
| 129 | |||
| 130 | defaultCommands = mkOption { | ||
| 131 | type = types.listOf types.str; | ||
| 132 | default = ["rmail"]; | ||
| 133 | description = "Commands allowed for remotes without explicit override"; | ||
| 134 | }; | ||
| 135 | |||
| 136 | defaultProtocols = mkOption { | ||
| 137 | type = types.separatedString ""; | ||
| 138 | default = "te"; | ||
| 139 | description = "UUCP protocol to use within ssh unless overriden"; | ||
| 140 | }; | ||
| 141 | |||
| 142 | incomingProtocols = mkOption { | ||
| 143 | type = types.separatedString ""; | ||
| 144 | default = "te"; | ||
| 145 | description = "UUCP protocols to use when called"; | ||
| 146 | }; | ||
| 147 | |||
| 148 | homeDir = mkOption { | ||
| 149 | type = types.path; | ||
| 150 | default = "/var/uucp"; | ||
| 151 | description = "Home of the uucp user"; | ||
| 152 | }; | ||
| 153 | |||
| 154 | sshKeyDir = mkOption { | ||
| 155 | type = types.path; | ||
| 156 | default = "${cfg.homeDir}/.ssh/"; | ||
| 157 | defaultText = literalExpression ''''${config.services.uucp.homeDir}/.ssh/''; | ||
| 158 | description = "Directory to store ssh keypairs"; | ||
| 159 | }; | ||
| 160 | |||
| 161 | spoolDir = mkOption { | ||
| 162 | type = types.path; | ||
| 163 | default = "/var/spool/uucp"; | ||
| 164 | description = "Spool directory"; | ||
| 165 | }; | ||
| 166 | |||
| 167 | lockDir = mkOption { | ||
| 168 | type = types.path; | ||
| 169 | default = "/var/spool/uucp"; | ||
| 170 | description = "Lock directory"; | ||
| 171 | }; | ||
| 172 | |||
| 173 | pubDir = mkOption { | ||
| 174 | type = types.path; | ||
| 175 | default = "/var/spool/uucppublic"; | ||
| 176 | description = "Public directory"; | ||
| 177 | }; | ||
| 178 | |||
| 179 | logFile = mkOption { | ||
| 180 | type = types.path; | ||
| 181 | default = "/var/log/uucp"; | ||
| 182 | description = "Log file"; | ||
| 183 | }; | ||
| 184 | |||
| 185 | statFile = mkOption { | ||
| 186 | type = types.path; | ||
| 187 | default = "/var/log/uucp.stat"; | ||
| 188 | description = "Statistics file"; | ||
| 189 | }; | ||
| 190 | |||
| 191 | debugFile = mkOption { | ||
| 192 | type = types.path; | ||
| 193 | default = "/var/log/uucp.debug"; | ||
| 194 | description = "Debug file"; | ||
| 195 | }; | ||
| 196 | |||
| 197 | interval = mkOption { | ||
| 198 | type = types.nullOr types.str; | ||
| 199 | default = "1h"; | ||
| 200 | description = '' | ||
| 201 | Specification of when to run `uucico' in format used by systemd timers | ||
| 202 | The default is to do so every hour | ||
| 203 | ''; | ||
| 204 | }; | ||
| 205 | |||
| 206 | nmDispatch = mkOption { | ||
| 207 | type = types.bool; | ||
| 208 | default = config.networking.networkmanager.enable; | ||
| 209 | defaultText = literalExpression "config.networking.networkmanager.enable"; | ||
| 210 | description = '' | ||
| 211 | Install a network-manager dispatcher script to automatically | ||
| 212 | call all remotes when networking is available | ||
| 213 | ''; | ||
| 214 | }; | ||
| 215 | |||
| 216 | extraConfig = mkOption { | ||
| 217 | type = types.lines; | ||
| 218 | default = '' | ||
| 219 | run-uuxqt 1 | ||
| 220 | ''; | ||
| 221 | description = "Extra configuration to append verbatim to `/etc/uucp/config'"; | ||
| 222 | }; | ||
| 223 | |||
| 224 | extraSys = mkOption { | ||
| 225 | type = types.lines; | ||
| 226 | default = '' | ||
| 227 | protocol-parameter g packet-size 4096 | ||
| 228 | ''; | ||
| 229 | description = "Extra configuration to prepend verbatim to `/etc/uucp/sys`"; | ||
| 230 | }; | ||
| 231 | }; | ||
| 232 | }; | ||
| 233 | |||
| 234 | config = mkIf cfg.enable { | ||
| 235 | environment.etc."uucp/config" = { | ||
| 236 | text = '' | ||
| 237 | hostname ${cfg.nodeName} | ||
| 238 | |||
| 239 | spool ${cfg.spoolDir} | ||
| 240 | lockdir ${cfg.lockDir} | ||
| 241 | pubdir ${cfg.pubDir} | ||
| 242 | logfile ${cfg.logFile} | ||
| 243 | statfile ${cfg.statFile} | ||
| 244 | debugfile ${cfg.debugFile} | ||
| 245 | |||
| 246 | ${cfg.extraConfig} | ||
| 247 | ''; | ||
| 248 | }; | ||
| 249 | |||
| 250 | users.groups."uucp" = {}; | ||
| 251 | users.users."uucp" = { | ||
| 252 | name = "uucp"; | ||
| 253 | group = "uucp"; | ||
| 254 | isSystemUser = true; | ||
| 255 | isNormalUser = false; | ||
| 256 | createHome = true; | ||
| 257 | home = cfg.homeDir; | ||
| 258 | description = "User for uucp over ssh"; | ||
| 259 | useDefaultShell = true; | ||
| 260 | openssh.authorizedKeys.keys = map restrictKey (concatLists (mapAttrsToList (name: node: node.publicKeys) cfg.remoteNodes)); | ||
| 261 | } // cfg.sshUser; | ||
| 262 | |||
| 263 | system.activationScripts."uucp-sshconfig" = '' | ||
| 264 | mkdir -p ${config.users.users."uucp".home}/.ssh | ||
| 265 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${config.users.users."uucp".home}/.ssh | ||
| 266 | chmod 700 ${config.users.users."uucp".home}/.ssh | ||
| 267 | ln -fs ${builtins.toFile "ssh-config" '' | ||
| 268 | ${concatStringsSep "\n" (mapAttrsToList sshConfig cfg.remoteNodes)} | ||
| 269 | |||
| 270 | ${cfg.extraSSHConfig} | ||
| 271 | ''} ${config.users.users."uucp".home}/.ssh/config | ||
| 272 | |||
| 273 | mkdir -p ${cfg.sshKeyDir} | ||
| 274 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.sshKeyDir} | ||
| 275 | chmod 700 ${cfg.sshKeyDir} | ||
| 276 | |||
| 277 | ${concatStringsSep "\n" (mapAttrsToList sshKeyGen cfg.remoteNodes)} | ||
| 278 | ''; | ||
| 279 | |||
| 280 | system.activationScripts."uucp-logs" = '' | ||
| 281 | touch ${cfg.logFile} | ||
| 282 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.logFile} | ||
| 283 | chmod 644 ${cfg.logFile} | ||
| 284 | touch ${cfg.statFile} | ||
| 285 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.statFile} | ||
| 286 | chmod 644 ${cfg.statFile} | ||
| 287 | touch ${cfg.debugFile} | ||
| 288 | chown ${config.users.users."uucp".name}:${config.users.users."uucp".group} ${cfg.debugFile} | ||
| 289 | chmod 644 ${cfg.debugFile} | ||
| 290 | ''; | ||
| 291 | |||
| 292 | environment.etc."uucp/port" = { | ||
| 293 | text = '' | ||
| 294 | port ssh | ||
| 295 | type stdin | ||
| 296 | protocol ${cfg.incomingProtocols} | ||
| 297 | '' + concatStringsSep "\n" (mapAttrsToList portSpec cfg.remoteNodes); | ||
| 298 | }; | ||
| 299 | environment.etc."uucp/sys" = { | ||
| 300 | text = cfg.extraSys + "\n" + concatStringsSep "\n" (mapAttrsToList sysSpec cfg.remoteNodes); | ||
| 301 | }; | ||
| 302 | |||
| 303 | security.wrappers = let | ||
| 304 | wrapper = p: { | ||
| 305 | name = p; | ||
| 306 | value = { | ||
| 307 | source = "${pkgs.uucp}/bin/${p}"; | ||
| 308 | owner = "root"; | ||
| 309 | group = "root"; | ||
| 310 | setuid = true; | ||
| 311 | setgid = false; | ||
| 312 | }; | ||
| 313 | }; | ||
| 314 | in listToAttrs (map wrapper ["uucico" "cu" "uucp" "uuname" "uustat" "uux" "uuxqt"]); | ||
| 315 | |||
| 316 | nixpkgs.overlays = [(self: super: { | ||
| 317 | rmail = super.writeShellScriptBin "rmail" '' | ||
| 318 | # Dummy UUCP rmail command for postfix/qmail systems | ||
| 319 | |||
| 320 | IFS=" " read junk from junk junk junk junk junk junk junk relay | ||
| 321 | |||
| 322 | case "$from" in | ||
| 323 | *[@!]*) ;; | ||
| 324 | *) from="$from@$relay";; | ||
| 325 | esac | ||
| 326 | |||
| 327 | exec ${config.security.wrapperDir}/sendmail -G -i -f "$from" -- "$@" | ||
| 328 | ''; | ||
| 329 | })]; | ||
| 330 | |||
| 331 | environment.systemPackages = with pkgs; [ | ||
| 332 | uucp | ||
| 333 | ]; | ||
| 334 | |||
| 335 | systemd.services."uucico@" = { | ||
| 336 | serviceConfig = { | ||
| 337 | User = "uucp"; | ||
| 338 | Type = "oneshot"; | ||
| 339 | ExecStart = "${config.security.wrapperDir}/uucico -D -S %i"; | ||
| 340 | }; | ||
| 341 | }; | ||
| 342 | |||
| 343 | systemd.timers."uucico@" = { | ||
| 344 | timerConfig.OnActiveSec = cfg.interval; | ||
| 345 | timerConfig.OnUnitActiveSec = cfg.interval; | ||
| 346 | }; | ||
| 347 | |||
| 348 | systemd.targets."multi-user" = { | ||
| 349 | wants = mapAttrsToList (name: node: "uucico@${name}.timer") cfg.remoteNodes; | ||
| 350 | }; | ||
| 351 | |||
| 352 | systemd.kill-user.enable = true; | ||
| 353 | systemd.targets."sleep" = { | ||
| 354 | after = [ "kill-user@uucp.service" ]; | ||
| 355 | wants = [ "kill-user@uucp.service" ]; | ||
| 356 | }; | ||
| 357 | |||
| 358 | networking.networkmanager.dispatcherScripts = optional cfg.nmDispatch { | ||
| 359 | type = "basic"; | ||
| 360 | source = pkgs.writeScript "callRemotes.sh" '' | ||
| 361 | #!${pkgs.stdenv.shell} | ||
| 362 | |||
| 363 | shopt -s extglob | ||
| 364 | |||
| 365 | case "''${2}" in | ||
| 366 | (?(vpn-)up) | ||
| 367 | ${concatStringsSep "\n " (mapAttrsToList (name: node: "${pkgs.systemd}/bin/systemctl start uucico@${name}.service") cfg.remoteNodes)} | ||
| 368 | ;; | ||
| 369 | esac | ||
| 370 | ''; | ||
| 371 | }; | ||
| 372 | }; | ||
| 373 | } | ||
diff --git a/nvfetcher.toml b/nvfetcher.toml index ecaebba0..8e3ba905 100644 --- a/nvfetcher.toml +++ b/nvfetcher.toml | |||
| @@ -115,3 +115,15 @@ fetch.git = "https://github.com/emersion/mako" | |||
| 115 | [swayosd] | 115 | [swayosd] |
| 116 | src.git = "https://github.com/ErikReider/SwayOSD" | 116 | src.git = "https://github.com/ErikReider/SwayOSD" |
| 117 | fetch.git = "https://github.com/ErikReider/SwayOSD" | 117 | fetch.git = "https://github.com/ErikReider/SwayOSD" |
| 118 | |||
| 119 | [netbootxyz-efi] | ||
| 120 | src.github = "netbootxyz/netboot.xyz" | ||
| 121 | fetch.url = "https://github.com/netbootxyz/netboot.xyz/releases/download/$ver/netboot.xyz.efi" | ||
| 122 | |||
| 123 | [netbootxyz-lkrn] | ||
| 124 | src.github = "netbootxyz/netboot.xyz" | ||
| 125 | fetch.url = "https://github.com/netbootxyz/netboot.xyz/releases/download/$ver/netboot.xyz.lkrn" | ||
| 126 | |||
| 127 | [quickshell] | ||
| 128 | src.git = "https://git.outfoxxed.me/quickshell/quickshell.git" | ||
| 129 | fetch.git = "https://git.outfoxxed.me/quickshell/quickshell.git" | ||
diff --git a/overlays/abs-podcast-autoplaylist/.envrc b/overlays/abs-podcast-autoplaylist/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/.envrc | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | use flake | ||
| 2 | |||
| 3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
| 4 | . .venv/bin/activate | ||
diff --git a/overlays/abs-podcast-autoplaylist/.gitignore b/overlays/abs-podcast-autoplaylist/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | .venv | ||
| 2 | **/__pycache__ | ||
diff --git a/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__init__.py b/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__init__.py | |||
diff --git a/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__main__.py b/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__main__.py new file mode 100644 index 00000000..fd739805 --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/abs_podcast_autoplaylist/__main__.py | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | import click | ||
| 2 | from pathlib import Path | ||
| 3 | import tomllib | ||
| 4 | import requests | ||
| 5 | from urllib.parse import urljoin | ||
| 6 | from operator import itemgetter | ||
| 7 | import re | ||
| 8 | from frozendict import frozendict | ||
| 9 | |||
| 10 | class BearerAuth(requests.auth.AuthBase): | ||
| 11 | def __init__(self, token): | ||
| 12 | self.token = token | ||
| 13 | def __call__(self, r): | ||
| 14 | r.headers["authorization"] = "Bearer " + self.token | ||
| 15 | return r | ||
| 16 | |||
| 17 | class ABSSession(requests.Session): | ||
| 18 | def __init__(self, config): | ||
| 19 | super().__init__() | ||
| 20 | self.base_url = config['instance'] | ||
| 21 | self.auth = BearerAuth(config['api_token']) | ||
| 22 | |||
| 23 | def request(self, method, url, *args, **kwargs): | ||
| 24 | joined_url = urljoin(self.base_url, url) | ||
| 25 | return super().request(method, joined_url, *args, **kwargs) | ||
| 26 | |||
| 27 | @click.command() | ||
| 28 | @click.argument('config_file', type=click.Path(dir_okay=False, path_type=Path)) | ||
| 29 | def main(config_file: Path): | ||
| 30 | with config_file.open('rb') as fh: | ||
| 31 | config = tomllib.load(fh) | ||
| 32 | |||
| 33 | with ABSSession(config) as s: | ||
| 34 | libraries = s.get('/api/libraries').json()['libraries'] | ||
| 35 | playlists = s.get('/api/playlists').json()['playlists'] | ||
| 36 | |||
| 37 | for library_config in config['libraries']: | ||
| 38 | [library] = filter(lambda l: l['name'] == library_config['name'], libraries) | ||
| 39 | filtered_playlists = list(filter(lambda p: p['name'] == library_config['playlist'] and p['libraryId'] == library['id'], playlists)) | ||
| 40 | def get_playlist(): | ||
| 41 | playlist = None | ||
| 42 | if filtered_playlists: | ||
| 43 | [playlist] = filtered_playlists | ||
| 44 | if not playlist: | ||
| 45 | playlist = s.post('/api/playlists', json={ | ||
| 46 | 'libraryId': library['id'], | ||
| 47 | 'name': library_config['playlist'], | ||
| 48 | }).json() | ||
| 49 | return playlist | ||
| 50 | |||
| 51 | podcasts = dict() | ||
| 52 | items = s.get('/api/libraries/{}/items'.format(library['id'])).json()['results'] | ||
| 53 | for item in items: | ||
| 54 | item = s.get('/api/items/{}'.format(item['id']), json={'expanded': True}).json() | ||
| 55 | episodes = list() | ||
| 56 | for episode in sorted(item['media']['episodes'], key = itemgetter('publishedAt')): | ||
| 57 | progress = s.get('/api/me/progress/{}/{}'.format(episode['libraryItemId'], episode['id'])) | ||
| 58 | if progress.ok and progress.json()["isFinished"]: | ||
| 59 | continue | ||
| 60 | episodes.append(episode) | ||
| 61 | podcasts[item['media']['metadata']['title']] = list(map(lambda x: frozendict({ 'libraryItemId': x['libraryItemId'], 'episodeId': x['id']}), episodes)) | ||
| 62 | def lookup_podcast(expr): | ||
| 63 | expr = re.compile(expr, flags=re.I) | ||
| 64 | matches = filter(lambda t: expr.search(t), podcasts.keys()) | ||
| 65 | match list(matches): | ||
| 66 | case [x]: | ||
| 67 | return (x,) | ||
| 68 | case _: | ||
| 69 | raise RuntimeError("No unique match for ‘{}’".format(expr)) | ||
| 70 | |||
| 71 | priorities = [ | ||
| 72 | [ | ||
| 73 | k | ||
| 74 | for item in (section if type(section) is list else [section]) | ||
| 75 | for k in lookup_podcast(item) | ||
| 76 | ] | ||
| 77 | for section in library_config['priorities'] | ||
| 78 | ] | ||
| 79 | |||
| 80 | playlist_items = list() | ||
| 81 | for section in priorities: | ||
| 82 | while any(map(lambda item: item in podcasts, section)): | ||
| 83 | for item in section: | ||
| 84 | if not item in podcasts: | ||
| 85 | continue | ||
| 86 | |||
| 87 | if not podcasts[item]: | ||
| 88 | del podcasts[item] | ||
| 89 | continue | ||
| 90 | |||
| 91 | playlist_items.append(podcasts[item].pop(0)) | ||
| 92 | |||
| 93 | playlist = get_playlist() | ||
| 94 | current_playlist_items = map(lambda item: frozendict({ k: v for k, v in item.items() if k in {'libraryItemId', 'episodeId'}}), playlist['items']) | ||
| 95 | |||
| 96 | if current_playlist_items == playlist_items: | ||
| 97 | continue | ||
| 98 | |||
| 99 | to_remove = set(current_playlist_items) - set(playlist_items) | ||
| 100 | if to_remove: | ||
| 101 | s.post('/api/playlists/{}/batch/remove'.format(playlist['id']), json={'items': list(to_remove)}).raise_for_status() | ||
| 102 | playlist = get_playlist() | ||
| 103 | to_add = set(playlist_items) - set(current_playlist_items) | ||
| 104 | if to_add: | ||
| 105 | s.post('/api/playlists/{}/batch/add'.format(playlist['id']), json={'items': list(to_add)}).raise_for_status() | ||
| 106 | |||
| 107 | r = s.patch('/api/playlists/{}'.format(playlist['id']), json={'items': playlist_items}).raise_for_status() | ||
diff --git a/overlays/abs-podcast-autoplaylist/default.nix b/overlays/abs-podcast-autoplaylist/default.nix new file mode 100644 index 00000000..843f1b65 --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/default.nix | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | { prev, final, flake, flakeInputs, ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; | ||
| 5 | pythonSet = flake.lib.pythonSet { | ||
| 6 | pkgs = final; | ||
| 7 | python = final.python312; | ||
| 8 | overlay = workspace.mkPyprojectOverlay { | ||
| 9 | sourcePreference = "wheel"; | ||
| 10 | }; | ||
| 11 | }; | ||
| 12 | virtualEnv = pythonSet.mkVirtualEnv "abs-podcast-autoplaylist-env" workspace.deps.default; | ||
| 13 | in { | ||
| 14 | abs-podcast-autoplaylist = virtualEnv.overrideAttrs (oldAttrs: { | ||
| 15 | meta = (oldAttrs.meta or {}) // { | ||
| 16 | mainProgram = "abs-podcast-autoplaylist"; | ||
| 17 | }; | ||
| 18 | }); | ||
| 19 | } | ||
diff --git a/overlays/abs-podcast-autoplaylist/pyproject.toml b/overlays/abs-podcast-autoplaylist/pyproject.toml new file mode 100644 index 00000000..f52a84bc --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/pyproject.toml | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | [project] | ||
| 2 | name = "abs-podcast-autoplaylist" | ||
| 3 | version = "0.1.0" | ||
| 4 | requires-python = ">=3.12" | ||
| 5 | dependencies = [ | ||
| 6 | "click>=8.1.8", | ||
| 7 | "frozendict>=2.4.6", | ||
| 8 | "requests>=2.32.3", | ||
| 9 | ] | ||
| 10 | |||
| 11 | [project.scripts] | ||
| 12 | abs-podcast-autoplaylist = "abs_podcast_autoplaylist.__main__:main" | ||
| 13 | |||
| 14 | [build-system] | ||
| 15 | requires = ["hatchling"] | ||
| 16 | build-backend = "hatchling.build" | ||
diff --git a/overlays/abs-podcast-autoplaylist/uv.lock b/overlays/abs-podcast-autoplaylist/uv.lock new file mode 100644 index 00000000..17de5f0e --- /dev/null +++ b/overlays/abs-podcast-autoplaylist/uv.lock | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | version = 1 | ||
| 2 | revision = 2 | ||
| 3 | requires-python = ">=3.12" | ||
| 4 | |||
| 5 | [[package]] | ||
| 6 | name = "abs-podcast-autoplaylist" | ||
| 7 | version = "0.1.0" | ||
| 8 | source = { editable = "." } | ||
| 9 | dependencies = [ | ||
| 10 | { name = "click" }, | ||
| 11 | { name = "frozendict" }, | ||
| 12 | { name = "requests" }, | ||
| 13 | ] | ||
| 14 | |||
| 15 | [package.metadata] | ||
| 16 | requires-dist = [ | ||
| 17 | { name = "click", specifier = ">=8.1.8" }, | ||
| 18 | { name = "frozendict", specifier = ">=2.4.6" }, | ||
| 19 | { name = "requests", specifier = ">=2.32.3" }, | ||
| 20 | ] | ||
| 21 | |||
| 22 | [[package]] | ||
| 23 | name = "certifi" | ||
| 24 | version = "2025.4.26" | ||
| 25 | source = { registry = "https://pypi.org/simple" } | ||
| 26 | sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload_time = "2025-04-26T02:12:29.51Z" } | ||
| 27 | wheels = [ | ||
| 28 | { url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload_time = "2025-04-26T02:12:27.662Z" }, | ||
| 29 | ] | ||
| 30 | |||
| 31 | [[package]] | ||
| 32 | name = "charset-normalizer" | ||
| 33 | version = "3.4.2" | ||
| 34 | source = { registry = "https://pypi.org/simple" } | ||
| 35 | sdist = { url = "https://files.pythonhosted.org/packages/e4/33/89c2ced2b67d1c2a61c19c6751aa8902d46ce3dacb23600a283619f5a12d/charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", size = 126367, upload_time = "2025-05-02T08:34:42.01Z" } | ||
| 36 | wheels = [ | ||
| 37 | { url = "https://files.pythonhosted.org/packages/d7/a4/37f4d6035c89cac7930395a35cc0f1b872e652eaafb76a6075943754f095/charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", size = 199936, upload_time = "2025-05-02T08:32:33.712Z" }, | ||
| 38 | { url = "https://files.pythonhosted.org/packages/ee/8a/1a5e33b73e0d9287274f899d967907cd0bf9c343e651755d9307e0dbf2b3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", size = 143790, upload_time = "2025-05-02T08:32:35.768Z" }, | ||
| 39 | { url = "https://files.pythonhosted.org/packages/66/52/59521f1d8e6ab1482164fa21409c5ef44da3e9f653c13ba71becdd98dec3/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", size = 153924, upload_time = "2025-05-02T08:32:37.284Z" }, | ||
| 40 | { url = "https://files.pythonhosted.org/packages/86/2d/fb55fdf41964ec782febbf33cb64be480a6b8f16ded2dbe8db27a405c09f/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", size = 146626, upload_time = "2025-05-02T08:32:38.803Z" }, | ||
| 41 | { url = "https://files.pythonhosted.org/packages/8c/73/6ede2ec59bce19b3edf4209d70004253ec5f4e319f9a2e3f2f15601ed5f7/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", size = 148567, upload_time = "2025-05-02T08:32:40.251Z" }, | ||
| 42 | { url = "https://files.pythonhosted.org/packages/09/14/957d03c6dc343c04904530b6bef4e5efae5ec7d7990a7cbb868e4595ee30/charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", size = 150957, upload_time = "2025-05-02T08:32:41.705Z" }, | ||
| 43 | { url = "https://files.pythonhosted.org/packages/0d/c8/8174d0e5c10ccebdcb1b53cc959591c4c722a3ad92461a273e86b9f5a302/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", size = 145408, upload_time = "2025-05-02T08:32:43.709Z" }, | ||
| 44 | { url = "https://files.pythonhosted.org/packages/58/aa/8904b84bc8084ac19dc52feb4f5952c6df03ffb460a887b42615ee1382e8/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", size = 153399, upload_time = "2025-05-02T08:32:46.197Z" }, | ||
| 45 | { url = "https://files.pythonhosted.org/packages/c2/26/89ee1f0e264d201cb65cf054aca6038c03b1a0c6b4ae998070392a3ce605/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", size = 156815, upload_time = "2025-05-02T08:32:48.105Z" }, | ||
| 46 | { url = "https://files.pythonhosted.org/packages/fd/07/68e95b4b345bad3dbbd3a8681737b4338ff2c9df29856a6d6d23ac4c73cb/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", size = 154537, upload_time = "2025-05-02T08:32:49.719Z" }, | ||
| 47 | { url = "https://files.pythonhosted.org/packages/77/1a/5eefc0ce04affb98af07bc05f3bac9094513c0e23b0562d64af46a06aae4/charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", size = 149565, upload_time = "2025-05-02T08:32:51.404Z" }, | ||
| 48 | { url = "https://files.pythonhosted.org/packages/37/a0/2410e5e6032a174c95e0806b1a6585eb21e12f445ebe239fac441995226a/charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", size = 98357, upload_time = "2025-05-02T08:32:53.079Z" }, | ||
| 49 | { url = "https://files.pythonhosted.org/packages/6c/4f/c02d5c493967af3eda9c771ad4d2bbc8df6f99ddbeb37ceea6e8716a32bc/charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", size = 105776, upload_time = "2025-05-02T08:32:54.573Z" }, | ||
| 50 | { url = "https://files.pythonhosted.org/packages/ea/12/a93df3366ed32db1d907d7593a94f1fe6293903e3e92967bebd6950ed12c/charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", size = 199622, upload_time = "2025-05-02T08:32:56.363Z" }, | ||
| 51 | { url = "https://files.pythonhosted.org/packages/04/93/bf204e6f344c39d9937d3c13c8cd5bbfc266472e51fc8c07cb7f64fcd2de/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", size = 143435, upload_time = "2025-05-02T08:32:58.551Z" }, | ||
| 52 | { url = "https://files.pythonhosted.org/packages/22/2a/ea8a2095b0bafa6c5b5a55ffdc2f924455233ee7b91c69b7edfcc9e02284/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", size = 153653, upload_time = "2025-05-02T08:33:00.342Z" }, | ||
| 53 | { url = "https://files.pythonhosted.org/packages/b6/57/1b090ff183d13cef485dfbe272e2fe57622a76694061353c59da52c9a659/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", size = 146231, upload_time = "2025-05-02T08:33:02.081Z" }, | ||
| 54 | { url = "https://files.pythonhosted.org/packages/e2/28/ffc026b26f441fc67bd21ab7f03b313ab3fe46714a14b516f931abe1a2d8/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", size = 148243, upload_time = "2025-05-02T08:33:04.063Z" }, | ||
| 55 | { url = "https://files.pythonhosted.org/packages/c0/0f/9abe9bd191629c33e69e47c6ef45ef99773320e9ad8e9cb08b8ab4a8d4cb/charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", size = 150442, upload_time = "2025-05-02T08:33:06.418Z" }, | ||
| 56 | { url = "https://files.pythonhosted.org/packages/67/7c/a123bbcedca91d5916c056407f89a7f5e8fdfce12ba825d7d6b9954a1a3c/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", size = 145147, upload_time = "2025-05-02T08:33:08.183Z" }, | ||
| 57 | { url = "https://files.pythonhosted.org/packages/ec/fe/1ac556fa4899d967b83e9893788e86b6af4d83e4726511eaaad035e36595/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", size = 153057, upload_time = "2025-05-02T08:33:09.986Z" }, | ||
| 58 | { url = "https://files.pythonhosted.org/packages/2b/ff/acfc0b0a70b19e3e54febdd5301a98b72fa07635e56f24f60502e954c461/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", size = 156454, upload_time = "2025-05-02T08:33:11.814Z" }, | ||
| 59 | { url = "https://files.pythonhosted.org/packages/92/08/95b458ce9c740d0645feb0e96cea1f5ec946ea9c580a94adfe0b617f3573/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", size = 154174, upload_time = "2025-05-02T08:33:13.707Z" }, | ||
| 60 | { url = "https://files.pythonhosted.org/packages/78/be/8392efc43487ac051eee6c36d5fbd63032d78f7728cb37aebcc98191f1ff/charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", size = 149166, upload_time = "2025-05-02T08:33:15.458Z" }, | ||
| 61 | { url = "https://files.pythonhosted.org/packages/44/96/392abd49b094d30b91d9fbda6a69519e95802250b777841cf3bda8fe136c/charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", size = 98064, upload_time = "2025-05-02T08:33:17.06Z" }, | ||
| 62 | { url = "https://files.pythonhosted.org/packages/e9/b0/0200da600134e001d91851ddc797809e2fe0ea72de90e09bec5a2fbdaccb/charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", size = 105641, upload_time = "2025-05-02T08:33:18.753Z" }, | ||
| 63 | { url = "https://files.pythonhosted.org/packages/20/94/c5790835a017658cbfabd07f3bfb549140c3ac458cfc196323996b10095a/charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", size = 52626, upload_time = "2025-05-02T08:34:40.053Z" }, | ||
| 64 | ] | ||
| 65 | |||
| 66 | [[package]] | ||
| 67 | name = "click" | ||
| 68 | version = "8.1.8" | ||
| 69 | source = { registry = "https://pypi.org/simple" } | ||
| 70 | dependencies = [ | ||
| 71 | { name = "colorama", marker = "sys_platform == 'win32'" }, | ||
| 72 | ] | ||
| 73 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593, upload_time = "2024-12-21T18:38:44.339Z" } | ||
| 74 | wheels = [ | ||
| 75 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload_time = "2024-12-21T18:38:41.666Z" }, | ||
| 76 | ] | ||
| 77 | |||
| 78 | [[package]] | ||
| 79 | name = "colorama" | ||
| 80 | version = "0.4.6" | ||
| 81 | source = { registry = "https://pypi.org/simple" } | ||
| 82 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload_time = "2022-10-25T02:36:22.414Z" } | ||
| 83 | wheels = [ | ||
| 84 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload_time = "2022-10-25T02:36:20.889Z" }, | ||
| 85 | ] | ||
| 86 | |||
| 87 | [[package]] | ||
| 88 | name = "frozendict" | ||
| 89 | version = "2.4.6" | ||
| 90 | source = { registry = "https://pypi.org/simple" } | ||
| 91 | sdist = { url = "https://files.pythonhosted.org/packages/bb/59/19eb300ba28e7547538bdf603f1c6c34793240a90e1a7b61b65d8517e35e/frozendict-2.4.6.tar.gz", hash = "sha256:df7cd16470fbd26fc4969a208efadc46319334eb97def1ddf48919b351192b8e", size = 316416, upload_time = "2024-10-13T12:15:32.449Z" } | ||
| 92 | wheels = [ | ||
| 93 | { url = "https://files.pythonhosted.org/packages/04/13/d9839089b900fa7b479cce495d62110cddc4bd5630a04d8469916c0e79c5/frozendict-2.4.6-py311-none-any.whl", hash = "sha256:d065db6a44db2e2375c23eac816f1a022feb2fa98cbb50df44a9e83700accbea", size = 16148, upload_time = "2024-10-13T12:15:26.839Z" }, | ||
| 94 | { url = "https://files.pythonhosted.org/packages/ba/d0/d482c39cee2ab2978a892558cf130681d4574ea208e162da8958b31e9250/frozendict-2.4.6-py312-none-any.whl", hash = "sha256:49344abe90fb75f0f9fdefe6d4ef6d4894e640fadab71f11009d52ad97f370b9", size = 16146, upload_time = "2024-10-13T12:15:28.16Z" }, | ||
| 95 | { url = "https://files.pythonhosted.org/packages/a5/8e/b6bf6a0de482d7d7d7a2aaac8fdc4a4d0bb24a809f5ddd422aa7060eb3d2/frozendict-2.4.6-py313-none-any.whl", hash = "sha256:7134a2bb95d4a16556bb5f2b9736dceb6ea848fa5b6f3f6c2d6dba93b44b4757", size = 16146, upload_time = "2024-10-13T12:15:29.495Z" }, | ||
| 96 | ] | ||
| 97 | |||
| 98 | [[package]] | ||
| 99 | name = "idna" | ||
| 100 | version = "3.10" | ||
| 101 | source = { registry = "https://pypi.org/simple" } | ||
| 102 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload_time = "2024-09-15T18:07:39.745Z" } | ||
| 103 | wheels = [ | ||
| 104 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload_time = "2024-09-15T18:07:37.964Z" }, | ||
| 105 | ] | ||
| 106 | |||
| 107 | [[package]] | ||
| 108 | name = "requests" | ||
| 109 | version = "2.32.3" | ||
| 110 | source = { registry = "https://pypi.org/simple" } | ||
| 111 | dependencies = [ | ||
| 112 | { name = "certifi" }, | ||
| 113 | { name = "charset-normalizer" }, | ||
| 114 | { name = "idna" }, | ||
| 115 | { name = "urllib3" }, | ||
| 116 | ] | ||
| 117 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload_time = "2024-05-29T15:37:49.536Z" } | ||
| 118 | wheels = [ | ||
| 119 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload_time = "2024-05-29T15:37:47.027Z" }, | ||
| 120 | ] | ||
| 121 | |||
| 122 | [[package]] | ||
| 123 | name = "urllib3" | ||
| 124 | version = "2.4.0" | ||
| 125 | source = { registry = "https://pypi.org/simple" } | ||
| 126 | sdist = { url = "https://files.pythonhosted.org/packages/8a/78/16493d9c386d8e60e442a35feac5e00f0913c0f4b7c217c11e8ec2ff53e0/urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", size = 390672, upload_time = "2025-04-10T15:23:39.232Z" } | ||
| 127 | wheels = [ | ||
| 128 | { url = "https://files.pythonhosted.org/packages/6b/11/cc635220681e93a0183390e26485430ca2c7b5f9d33b15c74c2861cb8091/urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813", size = 128680, upload_time = "2025-04-10T15:23:37.377Z" }, | ||
| 129 | ] | ||
diff --git a/overlays/cake-prometheus-exporter/default.nix b/overlays/cake-prometheus-exporter/default.nix index 3d0acc2d..69a5008c 100644 --- a/overlays/cake-prometheus-exporter/default.nix +++ b/overlays/cake-prometheus-exporter/default.nix | |||
| @@ -1,19 +1,18 @@ | |||
| 1 | { final, prev, ... }: | 1 | { final, prev, ... }: |
| 2 | let | 2 | let |
| 3 | inpPython = final.python310.override {}; | 3 | inpPython = final.python310.override {}; |
| 4 | python = inpPython.withPackages (ps: with ps; []); | ||
| 4 | in { | 5 | in { |
| 5 | cake-prometheus-exporter = prev.stdenv.mkDerivation rec { | 6 | cake-prometheus-exporter = prev.stdenv.mkDerivation rec { |
| 6 | pname = "cake-prometheus-exporter"; | 7 | pname = "cake-prometheus-exporter"; |
| 7 | version = "0.0.0"; | 8 | version = "0.0.0"; |
| 8 | 9 | ||
| 9 | src = ./cake-prometheus-exporter.py; | 10 | src = prev.replaceVars ./cake-prometheus-exporter.py { inherit python; }; |
| 10 | 11 | ||
| 11 | phases = [ "buildPhase" "checkPhase" "installPhase" ]; | 12 | phases = [ "unpackPhase" "checkPhase" "installPhase" ]; |
| 12 | 13 | ||
| 13 | python = inpPython.withPackages (ps: with ps; []); | 14 | unpackPhase = '' |
| 14 | 15 | cp $src cake-prometheus-exporter | |
| 15 | buildPhase = '' | ||
| 16 | substituteAll $src cake-prometheus-exporter | ||
| 17 | ''; | 16 | ''; |
| 18 | 17 | ||
| 19 | doCheck = true; | 18 | doCheck = true; |
diff --git a/overlays/deploy-rs.nix b/overlays/deploy-rs.nix new file mode 100644 index 00000000..678c6f5f --- /dev/null +++ b/overlays/deploy-rs.nix | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | { final, prev, flakeInputs, ... }: prev.lib.composeExtensions | ||
| 2 | flakeInputs.deploy-rs.overlays.default | ||
| 3 | (final: prev: { | ||
| 4 | deploy-rs = prev.deploy-rs // { | ||
| 5 | deploy-rs = prev.symlinkJoin { | ||
| 6 | name = "${prev.deploy-rs.deploy-rs.name}-wrapped"; | ||
| 7 | paths = [ prev.deploy-rs.deploy-rs ]; | ||
| 8 | buildInputs = [ prev.makeWrapper ]; | ||
| 9 | postBuild = '' | ||
| 10 | wrapProgram $out/bin/deploy \ | ||
| 11 | --prefix PATH : ${prev.lib.makeBinPath (with final; [ nix-monitored ])} | ||
| 12 | ''; | ||
| 13 | }; | ||
| 14 | }; | ||
| 15 | }) | ||
| 16 | final prev | ||
diff --git a/overlays/etesync-dav.nix b/overlays/etesync-dav.nix index cec216e2..e0ced1e3 100644 --- a/overlays/etesync-dav.nix +++ b/overlays/etesync-dav.nix | |||
| @@ -1,55 +1,58 @@ | |||
| 1 | { final, prev, ... }: { | 1 | { final, prev, ... }: { |
| 2 | etesync-dav = prev.python3Packages.buildPythonApplication rec { | 2 | etesync-dav = final.python3Packages.buildPythonApplication rec { |
| 3 | pname = "etesync-dav"; | 3 | pname = "etesync-dav"; |
| 4 | version = "0.33.4"; | 4 | version = "0.35.1"; |
| 5 | pyproject = true; | ||
| 5 | 6 | ||
| 6 | src = prev.fetchFromGitHub { | 7 | src = prev.fetchFromGitHub { |
| 7 | owner = "etesync"; | 8 | owner = "etesync"; |
| 8 | repo = "etesync-dav"; | 9 | repo = "etesync-dav"; |
| 9 | rev = "v${version}"; | 10 | tag = "v${version}"; |
| 10 | hash = "sha256-g+rK762tSWPDaBsaTwpTzfK/lqVs+Z/Qrpq2HCpipQE="; | 11 | hash = "sha256-y4BhU2kSn+RWqc5+pJQFhbwfat9cMWD0ED0EXJp25cY="; |
| 11 | }; | 12 | }; |
| 12 | 13 | ||
| 13 | dependencies = with prev.python3Packages; [ | 14 | build-system = with final.python3Packages; [ setuptools ]; |
| 15 | |||
| 16 | dependencies = with final.python3Packages; [ | ||
| 14 | appdirs | 17 | appdirs |
| 15 | etebase | 18 | etebase |
| 16 | etesync | 19 | etesync |
| 17 | flask | 20 | flask |
| 18 | flask-wtf | 21 | flask-wtf |
| 19 | msgpack | 22 | msgpack |
| 20 | setuptools | 23 | requests |
| 21 | (toPythonModule (buildPythonApplication rec { | 24 | requests.optional-dependencies.socks |
| 25 | (buildPythonApplication rec { | ||
| 22 | pname = "radicale"; | 26 | pname = "radicale"; |
| 23 | version = "3.2.3"; | 27 | version = "3.2.0"; |
| 24 | pyproject = true; | 28 | pyproject = true; |
| 25 | 29 | ||
| 26 | src = prev.fetchFromGitHub { | 30 | src = prev.fetchFromGitHub { |
| 27 | owner = "Kozea"; | 31 | owner = "Kozea"; |
| 28 | repo = "Radicale"; | 32 | repo = "Radicale"; |
| 29 | rev = "refs/tags/v${version}"; | 33 | rev = "refs/tags/v${version}"; |
| 30 | hash = "sha256-1IlnXVetQQuKBt6+QVKNeMM6qBQAiUhqc+4x3xOnSdE="; | 34 | hash = "sha256-RxC8VOfdTXJZiAroDHTKjJqGWu65Z5uyb4WK1LOqubQ="; |
| 31 | }; | 35 | }; |
| 32 | 36 | ||
| 37 | postPatch = '' | ||
| 38 | sed -i '/addopts/d' setup.cfg | ||
| 39 | ''; | ||
| 40 | |||
| 33 | build-system = [ | 41 | build-system = [ |
| 34 | setuptools | 42 | setuptools |
| 35 | ]; | 43 | ]; |
| 36 | 44 | ||
| 37 | dependencies = | 45 | dependencies = [ |
| 38 | [ | 46 | defusedxml |
| 39 | defusedxml | 47 | passlib |
| 40 | passlib | 48 | vobject |
| 41 | vobject | 49 | pika |
| 42 | pika | 50 | python-dateutil |
| 43 | python-dateutil | 51 | pytz # https://github.com/Kozea/Radicale/issues/816 |
| 44 | pytz # https://github.com/Kozea/Radicale/issues/816 | 52 | ] ++ passlib.optional-dependencies.bcrypt; |
| 45 | ] | ||
| 46 | ++ passlib.optional-dependencies.bcrypt; | ||
| 47 | 53 | ||
| 48 | doCheck = false; | 54 | doCheck = false; |
| 49 | })) | 55 | }) |
| 50 | requests | ||
| 51 | types-setuptools | ||
| 52 | requests.optional-dependencies.socks | ||
| 53 | ]; | 56 | ]; |
| 54 | 57 | ||
| 55 | doCheck = false; | 58 | doCheck = false; |
diff --git a/overlays/inwx-cdnskey/default.nix b/overlays/inwx-cdnskey/default.nix index cd564f24..e1bee0f2 100644 --- a/overlays/inwx-cdnskey/default.nix +++ b/overlays/inwx-cdnskey/default.nix | |||
| @@ -2,17 +2,16 @@ | |||
| 2 | let | 2 | let |
| 3 | packageOverrides = final.callPackage ./python-packages.nix {}; | 3 | packageOverrides = final.callPackage ./python-packages.nix {}; |
| 4 | inpPython = final.python39.override { inherit packageOverrides; }; | 4 | inpPython = final.python39.override { inherit packageOverrides; }; |
| 5 | python = inpPython.withPackages (ps: with ps; [pyxdg inwx-domrobot configparser dnspython]); | ||
| 5 | in { | 6 | in { |
| 6 | inwx-cdnskey = prev.stdenv.mkDerivation rec { | 7 | inwx-cdnskey = prev.stdenv.mkDerivation rec { |
| 7 | name = "inwx-cdnskey"; | 8 | name = "inwx-cdnskey"; |
| 8 | src = ./inwx-cdnskey.py; | 9 | src = prev.replaceVars ./inwx-cdnskey.py { inherit python; }; |
| 9 | 10 | ||
| 10 | phases = [ "buildPhase" "checkPhase" "installPhase" ]; | 11 | phases = [ "unpackPhase" "checkPhase" "installPhase" ]; |
| 11 | 12 | ||
| 12 | python = inpPython.withPackages (ps: with ps; [pyxdg inwx-domrobot configparser dnspython]); | 13 | unpackPhase = '' |
| 13 | 14 | cp $src inwx-cdnskey | |
| 14 | buildPhase = '' | ||
| 15 | substituteAll $src inwx-cdnskey | ||
| 16 | ''; | 15 | ''; |
| 17 | 16 | ||
| 18 | doCheck = true; | 17 | doCheck = true; |
diff --git a/overlays/lesspipe.nix b/overlays/lesspipe.nix index 3258eb70..b791f6e5 100644 --- a/overlays/lesspipe.nix +++ b/overlays/lesspipe.nix | |||
| @@ -17,7 +17,7 @@ | |||
| 17 | 17 | ||
| 18 | preFixup = '' | 18 | preFixup = '' |
| 19 | wrapProgram $out/bin/lesspipe.sh \ | 19 | wrapProgram $out/bin/lesspipe.sh \ |
| 20 | --prefix PATH : ${final.python3.pkgs.pygments}/bin:${final.file}/bin:${final.ncurses}/bin | 20 | --prefix PATH : ${prev.lib.makeBinPath (with final; [ file ncurses binutils ])} |
| 21 | ''; | 21 | ''; |
| 22 | }; | 22 | }; |
| 23 | } | 23 | } |
diff --git a/overlays/nftables-prometheus-exporter/default.nix b/overlays/nftables-prometheus-exporter/default.nix index aab0c8e9..48f668c4 100644 --- a/overlays/nftables-prometheus-exporter/default.nix +++ b/overlays/nftables-prometheus-exporter/default.nix | |||
| @@ -1,17 +1,16 @@ | |||
| 1 | { final, prev, ... }: | 1 | { final, prev, ... }: |
| 2 | let | 2 | let |
| 3 | inpPython = final.python310; | 3 | inpPython = final.python310; |
| 4 | python = inpPython.withPackages (ps: with ps; []); | ||
| 4 | in { | 5 | in { |
| 5 | nftables-prometheus-exporter = prev.stdenv.mkDerivation rec { | 6 | nftables-prometheus-exporter = prev.stdenv.mkDerivation rec { |
| 6 | name = "nftables-prometheus-exporter"; | 7 | name = "nftables-prometheus-exporter"; |
| 7 | src = ./nftables-prometheus-exporter.py; | 8 | src = prev.replaceVars ./nftables-prometheus-exporter.py { inherit python; }; |
| 8 | 9 | ||
| 9 | phases = [ "buildPhase" "checkPhase" "installPhase" ]; | 10 | phases = [ "unpackPhase" "checkPhase" "installPhase" ]; |
| 10 | 11 | ||
| 11 | python = inpPython.withPackages (ps: with ps; []); | 12 | unpackPhase = '' |
| 12 | 13 | cp $src nftables-prometheus-exporter | |
| 13 | buildPhase = '' | ||
| 14 | substituteAll $src nftables-prometheus-exporter | ||
| 15 | ''; | 14 | ''; |
| 16 | 15 | ||
| 17 | doCheck = true; | 16 | doCheck = true; |
diff --git a/overlays/nftables-prometheus-exporter/nftables-prometheus-exporter.py b/overlays/nftables-prometheus-exporter/nftables-prometheus-exporter.py index 484228c8..60ef4670 100644 --- a/overlays/nftables-prometheus-exporter/nftables-prometheus-exporter.py +++ b/overlays/nftables-prometheus-exporter/nftables-prometheus-exporter.py | |||
| @@ -42,7 +42,7 @@ class NFTMetrics: | |||
| 42 | cls._instance = cls.__new__(cls) | 42 | cls._instance = cls.__new__(cls) |
| 43 | cls._instance.attrs = None | 43 | cls._instance.attrs = None |
| 44 | return cls._instance | 44 | return cls._instance |
| 45 | 45 | ||
| 46 | 46 | ||
| 47 | def __init__(self): | 47 | def __init__(self): |
| 48 | raise RuntimeError('Call instance() instead') | 48 | raise RuntimeError('Call instance() instead') |
| @@ -62,7 +62,7 @@ class NFTMetrics: | |||
| 62 | raise RuntimeError(f'nftables json schema v{version} is not supported') | 62 | raise RuntimeError(f'nftables json schema v{version} is not supported') |
| 63 | queries[query_name] = data['nftables'][1:] | 63 | queries[query_name] = data['nftables'][1:] |
| 64 | 64 | ||
| 65 | 65 | ||
| 66 | def extract_query(query_name, type_name): | 66 | def extract_query(query_name, type_name): |
| 67 | return [ | 67 | return [ |
| 68 | item[type_name] | 68 | item[type_name] |
| @@ -98,21 +98,21 @@ class NFTMetrics: | |||
| 98 | metrics += _format_prom_metrics('nftables_counter_packets_count', 'counter', counter_packets) | 98 | metrics += _format_prom_metrics('nftables_counter_packets_count', 'counter', counter_packets) |
| 99 | 99 | ||
| 100 | map_counts = [] | 100 | map_counts = [] |
| 101 | for meter in self.attrs['maps']: | 101 | for item in self.attrs['maps']: |
| 102 | labels = { k: v for k, v in counter.items() if k not in set(['elem']) } | 102 | labels = { k: v for k, v in counter.items() if k not in set(['elem']) } |
| 103 | map_counts += [(labels, len(meter['elem']))] | 103 | map_counts += [(labels, len(item['elem']) if 'elem' in item else 0)] |
| 104 | metrics += _format_prom_metrics('nftables_map_elem_count', 'gauge', map_counts) | 104 | metrics += _format_prom_metrics('nftables_map_elem_count', 'gauge', map_counts) |
| 105 | 105 | ||
| 106 | meter_counts = [] | 106 | meter_counts = [] |
| 107 | for meter in self.attrs['meters']: | 107 | for item in self.attrs['meters']: |
| 108 | labels = { k: v for k, v in counter.items() if k not in set(['elem']) } | 108 | labels = { k: v for k, v in counter.items() if k not in set(['elem']) } |
| 109 | meter_counts += [(labels, len(meter['elem']))] | 109 | item_counts += [(labels, len(item['elem']) if 'elem' in item else 0)] |
| 110 | metrics += _format_prom_metrics('nftables_meter_elem_count', 'gauge', meter_counts) | 110 | metrics += _format_prom_metrics('nftables_meter_elem_count', 'gauge', meter_counts) |
| 111 | 111 | ||
| 112 | set_counts = [] | 112 | set_counts = [] |
| 113 | for meter in self.attrs['sets']: | 113 | for item in self.attrs['sets']: |
| 114 | labels = { k: v for k, v in counter.items() if k not in set(['elem']) } | 114 | labels = { k: v for k, v in counter.items() if k not in set(['elem']) } |
| 115 | set_counts += [(labels, len(meter['elem']))] | 115 | set_counts += [(labels, len(item['elem']) if 'elem' in item else 0)] |
| 116 | metrics += _format_prom_metrics('nftables_set_elem_count', 'gauge', set_counts) | 116 | metrics += _format_prom_metrics('nftables_set_elem_count', 'gauge', set_counts) |
| 117 | 117 | ||
| 118 | return metrics.encode('utf-8') | 118 | return metrics.encode('utf-8') |
| @@ -120,7 +120,7 @@ class NFTMetrics: | |||
| 120 | class NFTMetricsServer(BaseHTTPRequestHandler): | 120 | class NFTMetricsServer(BaseHTTPRequestHandler): |
| 121 | def log_message(self, format, *args): | 121 | def log_message(self, format, *args): |
| 122 | pass | 122 | pass |
| 123 | 123 | ||
| 124 | def do_GET(self): | 124 | def do_GET(self): |
| 125 | nft_metrics = NFTMetrics.instance() | 125 | nft_metrics = NFTMetrics.instance() |
| 126 | nft_metrics.update() | 126 | nft_metrics.update() |
| @@ -138,7 +138,7 @@ class NFTMetricsServer(BaseHTTPRequestHandler): | |||
| 138 | self.send_response(200) | 138 | self.send_response(200) |
| 139 | self.send_header("Content-type", "text/plain") | 139 | self.send_header("Content-type", "text/plain") |
| 140 | self.end_headers() | 140 | self.end_headers() |
| 141 | 141 | ||
| 142 | self.wfile.write(nft_metrics.prometheus()) | 142 | self.wfile.write(nft_metrics.prometheus()) |
| 143 | case _: | 143 | case _: |
| 144 | self.send_response(404) | 144 | self.send_response(404) |
diff --git a/overlays/niri.nix b/overlays/niri.nix index 9188ed7d..95a918b0 100644 --- a/overlays/niri.nix +++ b/overlays/niri.nix | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | (final: prev: { | 3 | (final: prev: { |
| 4 | niri-unstable = prev.niri-unstable.overrideAttrs (oldAttrs: { | 4 | niri-unstable = prev.niri-unstable.overrideAttrs (oldAttrs: { |
| 5 | buildInputs = (oldAttrs.buildInputs or []) ++ [ final.libgbm ]; | 5 | buildInputs = (oldAttrs.buildInputs or []) ++ [ final.libgbm ]; |
| 6 | doCheck = false; | ||
| 6 | }); | 7 | }); |
| 7 | }) | 8 | }) |
| 8 | final prev | 9 | final prev |
diff --git a/overlays/nix-direnv/default.nix b/overlays/nix-direnv/default.nix new file mode 100644 index 00000000..7c488e4e --- /dev/null +++ b/overlays/nix-direnv/default.nix | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | { final, prev, ... }: { | ||
| 2 | nix-direnv = prev.resholve.mkDerivation rec { | ||
| 3 | pname = "nix-direnv"; | ||
| 4 | version = "3.0.6"; | ||
| 5 | |||
| 6 | patches = [ | ||
| 7 | ./static-nix.patch | ||
| 8 | ]; | ||
| 9 | |||
| 10 | src = prev.fetchFromGitHub { | ||
| 11 | owner = "nix-community"; | ||
| 12 | repo = "nix-direnv"; | ||
| 13 | rev = version; | ||
| 14 | hash = "sha256-oNqhPqgQT92yxbKmcgX4F3e2yTUPyXYG7b2xQm3TvQw="; | ||
| 15 | }; | ||
| 16 | |||
| 17 | installPhase = '' | ||
| 18 | runHook preInstall | ||
| 19 | install -m400 -D direnvrc $out/share/nix-direnv/direnvrc | ||
| 20 | runHook postInstall | ||
| 21 | ''; | ||
| 22 | |||
| 23 | solutions = { | ||
| 24 | default = { | ||
| 25 | scripts = [ "share/nix-direnv/direnvrc" ]; | ||
| 26 | interpreter = "none"; | ||
| 27 | inputs = with final; [ coreutils nix-monitored ]; | ||
| 28 | fake = { | ||
| 29 | builtin = [ | ||
| 30 | "PATH_add" | ||
| 31 | "direnv_layout_dir" | ||
| 32 | "has" | ||
| 33 | "log_error" | ||
| 34 | "log_status" | ||
| 35 | "watch_file" | ||
| 36 | ]; | ||
| 37 | function = [ | ||
| 38 | # not really a function - this is in an else branch for macOS/homebrew that | ||
| 39 | # cannot be reached when built with nix | ||
| 40 | "shasum" | ||
| 41 | ]; | ||
| 42 | }; | ||
| 43 | keep = { | ||
| 44 | "$cmd" = true; | ||
| 45 | "$direnv" = true; | ||
| 46 | }; | ||
| 47 | execer = [ | ||
| 48 | "cannot:${prev.lib.getExe' final.nix-monitored "nix"}" | ||
| 49 | ]; | ||
| 50 | }; | ||
| 51 | }; | ||
| 52 | }; | ||
| 53 | } | ||
diff --git a/overlays/nix-direnv/static-nix.patch b/overlays/nix-direnv/static-nix.patch new file mode 100644 index 00000000..5de8193f --- /dev/null +++ b/overlays/nix-direnv/static-nix.patch | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | diff --git i/direnvrc w/direnvrc | ||
| 2 | index ddac0f5..fbcade6 100644 | ||
| 3 | --- i/direnvrc | ||
| 4 | +++ w/direnvrc | ||
| 5 | @@ -29,10 +29,8 @@ _nix_direnv_warning() { | ||
| 6 | |||
| 7 | _nix_direnv_error() { log_error "${_NIX_DIRENV_LOG_PREFIX}$*"; } | ||
| 8 | |||
| 9 | -_nix_direnv_nix="" | ||
| 10 | - | ||
| 11 | _nix() { | ||
| 12 | - ${_nix_direnv_nix} --extra-experimental-features "nix-command flakes" "$@" | ||
| 13 | + nix --extra-experimental-features "nix-command flakes" "$@" | ||
| 14 | } | ||
| 15 | |||
| 16 | _require_version() { | ||
| 17 | @@ -55,34 +53,6 @@ _require_cmd_version() { | ||
| 18 | _require_version "$cmd" "${BASH_REMATCH[1]}" "$required" | ||
| 19 | } | ||
| 20 | |||
| 21 | -_nix_direnv_resolve_nix() { | ||
| 22 | - local ambient_nix | ||
| 23 | - | ||
| 24 | - if ambient_nix=$(command -v nix); then | ||
| 25 | - if _require_cmd_version "${ambient_nix}" "${NIX_MIN_VERSION}"; then | ||
| 26 | - echo "${ambient_nix}" | ||
| 27 | - return 0 | ||
| 28 | - else | ||
| 29 | - _nix_direnv_warning "Nix version in PATH is too old, wanted ${NIX_MIN_VERSION}+, got $(${ambient_nix} --version), will attempt fallback" | ||
| 30 | - fi | ||
| 31 | - else | ||
| 32 | - _nix_direnv_warning "Could not find Nix in PATH, will attempt fallback" | ||
| 33 | - fi | ||
| 34 | - | ||
| 35 | - if [ -n "${NIX_DIRENV_FALLBACK_NIX}" ]; then | ||
| 36 | - if _require_cmd_version "${NIX_DIRENV_FALLBACK_NIX}" "${NIX_MIN_VERSION}"; then | ||
| 37 | - echo "${NIX_DIRENV_FALLBACK_NIX}" | ||
| 38 | - return 0 | ||
| 39 | - else | ||
| 40 | - _nix_direnv_error "Fallback Nix version is too old, wanted ${NIX_MIN_VERSION}+, got $(${NIX_DIRENV_FALLBACK_NIX} --version)" | ||
| 41 | - return 1 | ||
| 42 | - fi | ||
| 43 | - else | ||
| 44 | - _nix_direnv_error "Could not find fallback Nix binary, please add Nix to PATH or set NIX_DIRENV_FALLBACK_NIX" | ||
| 45 | - return 1 | ||
| 46 | - fi | ||
| 47 | -} | ||
| 48 | - | ||
| 49 | _nix_direnv_preflight() { | ||
| 50 | if [[ -z $direnv ]]; then | ||
| 51 | # shellcheck disable=2016 | ||
| 52 | @@ -102,10 +72,6 @@ _nix_direnv_preflight() { | ||
| 53 | fi | ||
| 54 | fi | ||
| 55 | |||
| 56 | - if ! _nix_direnv_nix=$(_nix_direnv_resolve_nix); then | ||
| 57 | - return 1 | ||
| 58 | - fi | ||
| 59 | - | ||
| 60 | local layout_dir | ||
| 61 | layout_dir=$(direnv_layout_dir) | ||
| 62 | |||
diff --git a/overlays/nix-monitored.nix b/overlays/nix-monitored.nix new file mode 100644 index 00000000..9f6caff1 --- /dev/null +++ b/overlays/nix-monitored.nix | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | { final, prev, flakeInputs, ... }: prev.lib.composeExtensions | ||
| 2 | flakeInputs.nix-monitored.overlays.default | ||
| 3 | (final: prev: { | ||
| 4 | nix-monitored = prev.nix-monitored.override { | ||
| 5 | withNotify = false; | ||
| 6 | }; | ||
| 7 | }) | ||
| 8 | final prev | ||
diff --git a/overlays/persistent-nix-shell/default.nix b/overlays/persistent-nix-shell/default.nix index c36b9e86..6067cade 100644 --- a/overlays/persistent-nix-shell/default.nix +++ b/overlays/persistent-nix-shell/default.nix | |||
| @@ -5,10 +5,9 @@ | |||
| 5 | 5 | ||
| 6 | phases = [ "buildPhase" "installPhase" ]; | 6 | phases = [ "buildPhase" "installPhase" ]; |
| 7 | 7 | ||
| 8 | inherit (final) zsh; | ||
| 9 | |||
| 10 | buildPhase = '' | 8 | buildPhase = '' |
| 11 | substituteAll $src persistent-nix-shell | 9 | substitute $src persistent-nix-shell \ |
| 10 | --subst-var-by zsh ${final.zsh} | ||
| 12 | ''; | 11 | ''; |
| 13 | 12 | ||
| 14 | installPhase = '' | 13 | installPhase = '' |
diff --git a/overlays/postsrsd.nix b/overlays/postsrsd.nix new file mode 100644 index 00000000..cb1ccf30 --- /dev/null +++ b/overlays/postsrsd.nix | |||
| @@ -0,0 +1,11 @@ | |||
| 1 | { final, prev, ... }: | ||
| 2 | { | ||
| 3 | postsrsd = prev.postsrsd.overrideAttrs (oldAttrs: { | ||
| 4 | cmakeFlags = (oldAttrs.cmakeFlags or []) ++ [ | ||
| 5 | "-DWITH_MILTER=ON" | ||
| 6 | ]; | ||
| 7 | buildInputs = (oldAttrs.buildInputs or []) ++ [ | ||
| 8 | final.libmilter | ||
| 9 | ]; | ||
| 10 | }); | ||
| 11 | } | ||
diff --git a/overlays/prometheus-lvm-exporter.nix b/overlays/prometheus-lvm-exporter.nix index 240f8d85..72d9c7ca 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-z/fV0PzoWSDTJ44En19o7zJPPPox5ymFw7sw0Ab9t00="; | 6 | vendorHash = "sha256-CoTNTCBBugbHWDsOuZY1t8HrpdmEbbSMyVb3+1u0q+g="; |
| 7 | 7 | ||
| 8 | doCheck = false; | 8 | doCheck = false; |
| 9 | 9 | ||
diff --git a/overlays/quickshell/default.nix b/overlays/quickshell/default.nix new file mode 100644 index 00000000..c01fac20 --- /dev/null +++ b/overlays/quickshell/default.nix | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | { final, prev, sources, ... }: | ||
| 2 | { | ||
| 3 | quickshell = prev.quickshell.overrideAttrs (oldAttrs: { | ||
| 4 | inherit (sources.quickshell) version src; | ||
| 5 | |||
| 6 | patches = (oldAttrs.patches or []) ++ [ | ||
| 7 | ./greetd-response.patch | ||
| 8 | ./lock-state-changed.patch | ||
| 9 | ./pipewire.patch | ||
| 10 | ./io.patch | ||
| 11 | ]; | ||
| 12 | }); | ||
| 13 | } | ||
diff --git a/overlays/quickshell/greetd-response.patch b/overlays/quickshell/greetd-response.patch new file mode 100644 index 00000000..a0efb562 --- /dev/null +++ b/overlays/quickshell/greetd-response.patch | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | diff --git c/src/services/greetd/connection.cpp w/src/services/greetd/connection.cpp | ||
| 2 | index bf0d1fd..a790ab7 100644 | ||
| 3 | --- c/src/services/greetd/connection.cpp | ||
| 4 | +++ w/src/services/greetd/connection.cpp | ||
| 5 | @@ -225,6 +225,11 @@ void GreetdConnection::onSocketReady() { | ||
| 6 | |||
| 7 | this->mResponseRequired = responseRequired; | ||
| 8 | emit this->authMessage(message, error, responseRequired, echoResponse); | ||
| 9 | + | ||
| 10 | + if (!responseRequired) | ||
| 11 | + this->sendRequest({ | ||
| 12 | + {"type", "post_auth_message_response"} | ||
| 13 | + }); | ||
| 14 | } else goto unexpected; | ||
| 15 | |||
| 16 | return; | ||
diff --git a/overlays/quickshell/io.patch b/overlays/quickshell/io.patch new file mode 100644 index 00000000..961bdcaf --- /dev/null +++ b/overlays/quickshell/io.patch | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | diff --git i/src/io/socket.cpp w/src/io/socket.cpp | ||
| 2 | index 371f687..d12eaeb 100644 | ||
| 3 | --- i/src/io/socket.cpp | ||
| 4 | +++ w/src/io/socket.cpp | ||
| 5 | @@ -66,7 +66,7 @@ void Socket::onSocketDisconnected() { | ||
| 6 | } | ||
| 7 | |||
| 8 | void Socket::onSocketError(QLocalSocket::LocalSocketError error) { | ||
| 9 | - qCWarning(logSocket) << "Socket error for" << this << error; | ||
| 10 | + // qCWarning(logSocket) << "Socket error for" << this << error; | ||
| 11 | emit this->error(error); | ||
| 12 | } | ||
| 13 | |||
diff --git a/overlays/quickshell/lock-state-changed.patch b/overlays/quickshell/lock-state-changed.patch new file mode 100644 index 00000000..4be273fa --- /dev/null +++ b/overlays/quickshell/lock-state-changed.patch | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | diff --git i/src/wayland/session_lock.cpp w/src/wayland/session_lock.cpp | ||
| 2 | index 0ecf9ec..3dbd19b 100644 | ||
| 3 | --- i/src/wayland/session_lock.cpp | ||
| 4 | +++ w/src/wayland/session_lock.cpp | ||
| 5 | @@ -127,6 +127,7 @@ void WlSessionLock::realizeLockTarget(WlSessionLock* old) { | ||
| 6 | this->updateSurfaces(false); | ||
| 7 | |||
| 8 | if (!this->manager->lock()) this->lockTarget = false; | ||
| 9 | + emit this->lockStateChanged(); | ||
| 10 | |||
| 11 | this->updateSurfaces(true, old); | ||
| 12 | } else { | ||
diff --git a/overlays/quickshell/pipewire.patch b/overlays/quickshell/pipewire.patch new file mode 100644 index 00000000..2d98eefc --- /dev/null +++ b/overlays/quickshell/pipewire.patch | |||
| @@ -0,0 +1,488 @@ | |||
| 1 | diff --git i/src/services/pipewire/device.cpp w/src/services/pipewire/device.cpp | ||
| 2 | index 616e7d0..0c55008 100644 | ||
| 3 | --- i/src/services/pipewire/device.cpp | ||
| 4 | +++ w/src/services/pipewire/device.cpp | ||
| 5 | @@ -3,6 +3,7 @@ | ||
| 6 | #include <cstdint> | ||
| 7 | #include <functional> | ||
| 8 | #include <utility> | ||
| 9 | +#include <algorithm> | ||
| 10 | |||
| 11 | #include <pipewire/device.h> | ||
| 12 | #include <qcontainerfwd.h> | ||
| 13 | @@ -19,6 +20,8 @@ | ||
| 14 | #include <spa/pod/pod.h> | ||
| 15 | #include <spa/pod/vararg.h> | ||
| 16 | #include <spa/utils/type.h> | ||
| 17 | +#include <spa/monitor/device.h> | ||
| 18 | +#include <spa/utils/keys.h> | ||
| 19 | |||
| 20 | #include "../../core/logcat.hpp" | ||
| 21 | #include "core.hpp" | ||
| 22 | @@ -46,6 +49,25 @@ void PwDevice::unbindHooks() { | ||
| 23 | this->mWaitingForDevice = false; | ||
| 24 | } | ||
| 25 | |||
| 26 | +void PwDevice::initProps(const spa_dict* props) { | ||
| 27 | + if (const auto* deviceName = spa_dict_lookup(props, SPA_KEY_DEVICE_NAME)) { | ||
| 28 | + this->name = deviceName; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + if (const auto* deviceDesc = spa_dict_lookup(props, SPA_KEY_DEVICE_DESCRIPTION)) { | ||
| 32 | + this->description = deviceDesc; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + if (const auto* deviceNick = spa_dict_lookup(props, SPA_KEY_DEVICE_NICK)) { | ||
| 36 | + this->nick = deviceNick; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + if (const auto* mediaClass = spa_dict_lookup(props, SPA_KEY_MEDIA_CLASS)) { | ||
| 40 | + this->type = mediaClass; | ||
| 41 | + } | ||
| 42 | +} | ||
| 43 | + | ||
| 44 | + | ||
| 45 | const pw_device_events PwDevice::EVENTS = { | ||
| 46 | .version = PW_VERSION_DEVICE_EVENTS, | ||
| 47 | .info = &PwDevice::onInfo, | ||
| 48 | @@ -71,6 +93,11 @@ void PwDevice::onInfo(void* data, const pw_device_info* info) { | ||
| 49 | } | ||
| 50 | |||
| 51 | break; | ||
| 52 | + } else if (param.id == SPA_PARAM_EnumProfile && param.flags & SPA_PARAM_INFO_READ) { | ||
| 53 | + self->validProfiles.clear(); | ||
| 54 | + pw_device_enum_params(self->proxy(), 0, param.id, 0, UINT32_MAX, nullptr); | ||
| 55 | + } else if (param.id == SPA_PARAM_Profile && param.flags & SPA_PARAM_INFO_READ) { | ||
| 56 | + pw_device_enum_params(self->proxy(), 0, param.id, 0, UINT32_MAX, nullptr); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | @@ -97,6 +124,15 @@ void PwDevice::onParam( | ||
| 61 | } | ||
| 62 | |||
| 63 | self->addDeviceIndexPairs(param); | ||
| 64 | + } else if (id == SPA_PARAM_EnumProfile) { | ||
| 65 | + PwProfile profile = PwProfile::parseSpaPod(param); | ||
| 66 | + self->profilesUpdated = true; | ||
| 67 | + self->profiles.insertOrAssign(profile.index, profile); | ||
| 68 | + self->validProfiles.insert(profile.index); | ||
| 69 | + } else if (id == SPA_PARAM_Profile) { | ||
| 70 | + PwProfile profile = PwProfile::parseSpaPod(param); | ||
| 71 | + self->currentProfileUpdated = true; | ||
| 72 | + self->currentProfile = profile; | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | @@ -145,6 +181,21 @@ void PwDevice::polled() { | ||
| 77 | return false; | ||
| 78 | }); | ||
| 79 | } | ||
| 80 | + if (this->profilesUpdated) { | ||
| 81 | + this->profiles.removeIf([&](const std::pair<qint32, PwProfile>& entry) { | ||
| 82 | + return !this->validProfiles.contains(entry.first); | ||
| 83 | + }); | ||
| 84 | + this->profilesUpdated = false; | ||
| 85 | + QList<PwProfile> profiles = this->profiles.values(); | ||
| 86 | + std::sort(profiles.begin(), profiles.end(), [](const PwProfile& a, const PwProfile& b) { return a.index < b.index; }); | ||
| 87 | + emit this->profilesChanged(profiles); | ||
| 88 | + } | ||
| 89 | + if (this->currentProfileUpdated) { | ||
| 90 | + this->currentProfileUpdated = false; | ||
| 91 | + if (this->currentProfile) { | ||
| 92 | + emit this->currentProfileChanged(*this->currentProfile); | ||
| 93 | + } | ||
| 94 | + } | ||
| 95 | } | ||
| 96 | |||
| 97 | bool PwDevice::setVolumes(qint32 routeDevice, const QVector<float>& volumes) { | ||
| 98 | @@ -182,6 +233,15 @@ bool PwDevice::setMuted(qint32 routeDevice, bool muted) { | ||
| 99 | }); | ||
| 100 | } | ||
| 101 | |||
| 102 | +void PwDevice::setProfile(qint32 profileIndex) { | ||
| 103 | + auto buffer = std::array<uint8_t, 1024>(); | ||
| 104 | + auto builder = SPA_POD_BUILDER_INIT(buffer.data(), buffer.size()); | ||
| 105 | + auto* pod = spa_pod_builder_add_object(&builder, | ||
| 106 | + SPA_TYPE_OBJECT_ParamProfile, SPA_PARAM_Profile, | ||
| 107 | + SPA_PARAM_PROFILE_index, SPA_POD_Int(profileIndex)); | ||
| 108 | + pw_device_set_param(this->proxy(), SPA_PARAM_Profile, 0, static_cast<spa_pod*>(pod)); | ||
| 109 | +} | ||
| 110 | + | ||
| 111 | void PwDevice::waitForDevice() { this->mWaitingForDevice = true; } | ||
| 112 | bool PwDevice::waitingForDevice() const { return this->mWaitingForDevice; } | ||
| 113 | |||
| 114 | @@ -222,4 +282,24 @@ bool PwDevice::setRouteProps( | ||
| 115 | return true; | ||
| 116 | } | ||
| 117 | |||
| 118 | +PwProfile PwProfile::parseSpaPod(const spa_pod* param) { | ||
| 119 | + PwProfile profile; | ||
| 120 | + | ||
| 121 | + const auto* indexProp = spa_pod_find_prop(param, nullptr, SPA_PARAM_PROFILE_index); | ||
| 122 | + const auto* descProp = spa_pod_find_prop(param, nullptr, SPA_PARAM_PROFILE_description); | ||
| 123 | + const auto* nameProp = spa_pod_find_prop(param, nullptr, SPA_PARAM_PROFILE_name); | ||
| 124 | + | ||
| 125 | + spa_pod_get_int(&indexProp->value, &profile.index); | ||
| 126 | + | ||
| 127 | + const char* desc_cstr = nullptr; | ||
| 128 | + spa_pod_get_string(&descProp->value, &desc_cstr); | ||
| 129 | + profile.description = QString(desc_cstr); | ||
| 130 | + | ||
| 131 | + const char* name_cstr = nullptr; | ||
| 132 | + spa_pod_get_string(&nameProp->value, &name_cstr); | ||
| 133 | + profile.name = QString(name_cstr); | ||
| 134 | + | ||
| 135 | + return profile; | ||
| 136 | +} | ||
| 137 | + | ||
| 138 | } // namespace qs::service::pipewire | ||
| 139 | diff --git i/src/services/pipewire/device.hpp w/src/services/pipewire/device.hpp | ||
| 140 | index 1a1f705..ee64858 100644 | ||
| 141 | --- i/src/services/pipewire/device.hpp | ||
| 142 | +++ w/src/services/pipewire/device.hpp | ||
| 143 | @@ -1,6 +1,7 @@ | ||
| 144 | #pragma once | ||
| 145 | |||
| 146 | #include <functional> | ||
| 147 | +#include <optional> | ||
| 148 | |||
| 149 | #include <pipewire/core.h> | ||
| 150 | #include <pipewire/device.h> | ||
| 151 | @@ -17,6 +18,20 @@ | ||
| 152 | |||
| 153 | namespace qs::service::pipewire { | ||
| 154 | |||
| 155 | +struct PwProfile { | ||
| 156 | + Q_GADGET; | ||
| 157 | + Q_PROPERTY(qint32 index MEMBER index) | ||
| 158 | + Q_PROPERTY(QString description MEMBER description) | ||
| 159 | + Q_PROPERTY(QString name MEMBER name) | ||
| 160 | + | ||
| 161 | +public: | ||
| 162 | + qint32 index; | ||
| 163 | + QString description; | ||
| 164 | + QString name; | ||
| 165 | + | ||
| 166 | + static PwProfile parseSpaPod(const spa_pod* param); | ||
| 167 | +}; | ||
| 168 | + | ||
| 169 | class PwDevice; | ||
| 170 | |||
| 171 | class PwDevice: public PwBindable<pw_device, PW_TYPE_INTERFACE_Device, PW_VERSION_DEVICE> { | ||
| 172 | @@ -25,6 +40,12 @@ class PwDevice: public PwBindable<pw_device, PW_TYPE_INTERFACE_Device, PW_VERSIO | ||
| 173 | public: | ||
| 174 | void bindHooks() override; | ||
| 175 | void unbindHooks() override; | ||
| 176 | + void initProps(const spa_dict* props) override; | ||
| 177 | + | ||
| 178 | + QString name; | ||
| 179 | + QString description; | ||
| 180 | + QString nick; | ||
| 181 | + QString type; | ||
| 182 | |||
| 183 | bool setVolumes(qint32 routeDevice, const QVector<float>& volumes); | ||
| 184 | bool setMuted(qint32 routeDevice, bool muted); | ||
| 185 | @@ -32,9 +53,16 @@ public: | ||
| 186 | void waitForDevice(); | ||
| 187 | [[nodiscard]] bool waitingForDevice() const; | ||
| 188 | |||
| 189 | + void setProfile(qint32 profileIndex); | ||
| 190 | + | ||
| 191 | + QHash<qint32, PwProfile> profiles; | ||
| 192 | + std::optional<PwProfile> currentProfile; | ||
| 193 | + | ||
| 194 | signals: | ||
| 195 | void deviceReady(); | ||
| 196 | void routeVolumesChanged(qint32 routeDevice, const PwVolumeProps& volumeProps); | ||
| 197 | + void profilesChanged(QList<PwProfile> profiles); | ||
| 198 | + void currentProfileChanged(PwProfile profile); | ||
| 199 | |||
| 200 | private slots: | ||
| 201 | void polled(); | ||
| 202 | @@ -49,6 +77,11 @@ private: | ||
| 203 | QList<qint32> stagingIndexes; | ||
| 204 | void addDeviceIndexPairs(const spa_pod* param); | ||
| 205 | |||
| 206 | + bool profilesUpdated = false; | ||
| 207 | + QSet<qint32> validProfiles; | ||
| 208 | + | ||
| 209 | + bool currentProfileUpdated = false; | ||
| 210 | + | ||
| 211 | bool | ||
| 212 | setRouteProps(qint32 routeDevice, const std::function<void*(spa_pod_builder*)>& propsCallback); | ||
| 213 | |||
| 214 | diff --git i/src/services/pipewire/node.cpp w/src/services/pipewire/node.cpp | ||
| 215 | index 3e68149..4721a58 100644 | ||
| 216 | --- i/src/services/pipewire/node.cpp | ||
| 217 | +++ w/src/services/pipewire/node.cpp | ||
| 218 | @@ -145,6 +145,10 @@ void PwNode::initProps(const spa_dict* props) { | ||
| 219 | this->type = PwNodeType::VideoSink; | ||
| 220 | } else if (strcmp(mediaClass, "Video/Source") == 0) { | ||
| 221 | this->type = PwNodeType::VideoSource; | ||
| 222 | + } else if (strcmp(mediaClass, "Stream/Output/Video") == 0) { | ||
| 223 | + this->type = PwNodeType::VideoOutStream; | ||
| 224 | + } else if (strcmp(mediaClass, "Stream/Input/Video") == 0) { | ||
| 225 | + this->type = PwNodeType::VideoInStream; | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | diff --git i/src/services/pipewire/node.hpp w/src/services/pipewire/node.hpp | ||
| 230 | index 0d4c92e..ee6f223 100644 | ||
| 231 | --- i/src/services/pipewire/node.hpp | ||
| 232 | +++ w/src/services/pipewire/node.hpp | ||
| 233 | @@ -144,6 +144,8 @@ public: | ||
| 234 | // This is equivalent to the media class `Video/Sink` and is composed of the | ||
| 235 | // @@PwNodeType.Video and @@PwNodeType.Sink flags. | ||
| 236 | VideoSink = Video | Sink, | ||
| 237 | + VideoOutStream = Video | Sink | Stream, | ||
| 238 | + VideoInStream = Video | Source | Stream, | ||
| 239 | }; | ||
| 240 | Q_ENUM(Flag); | ||
| 241 | Q_DECLARE_FLAGS(Flags, Flag); | ||
| 242 | diff --git i/src/services/pipewire/qml.cpp w/src/services/pipewire/qml.cpp | ||
| 243 | index 9efb17e..921d12a 100644 | ||
| 244 | --- i/src/services/pipewire/qml.cpp | ||
| 245 | +++ w/src/services/pipewire/qml.cpp | ||
| 246 | @@ -9,6 +9,9 @@ | ||
| 247 | #include <qtypes.h> | ||
| 248 | #include <qvariant.h> | ||
| 249 | |||
| 250 | +#include <cstdint> | ||
| 251 | +#include <algorithm> | ||
| 252 | + | ||
| 253 | #include "../../core/model.hpp" | ||
| 254 | #include "connection.hpp" | ||
| 255 | #include "defaults.hpp" | ||
| 256 | @@ -54,6 +57,12 @@ Pipewire::Pipewire(QObject* parent): QObject(parent) { | ||
| 257 | |||
| 258 | QObject::connect(&connection->registry, &PwRegistry::nodeAdded, this, &Pipewire::onNodeAdded); | ||
| 259 | |||
| 260 | + for (auto* device: connection->registry.devices.values()) { | ||
| 261 | + this->onDeviceAdded(device); | ||
| 262 | + } | ||
| 263 | + | ||
| 264 | + QObject::connect(&connection->registry, &PwRegistry::deviceAdded, this, &Pipewire::onDeviceAdded); | ||
| 265 | + | ||
| 266 | for (auto* link: connection->registry.links.values()) { | ||
| 267 | this->onLinkAdded(link); | ||
| 268 | } | ||
| 269 | @@ -123,6 +132,19 @@ void Pipewire::onNodeRemoved(QObject* object) { | ||
| 270 | this->mNodes.removeObject(iface); | ||
| 271 | } | ||
| 272 | |||
| 273 | +ObjectModel<PwDeviceIface>* Pipewire::devices() { return &this->mDevices; } | ||
| 274 | + | ||
| 275 | +void Pipewire::onDeviceAdded(PwDevice* device) { | ||
| 276 | + auto* iface = PwDeviceIface::instance(device); | ||
| 277 | + QObject::connect(iface, &QObject::destroyed, this, &Pipewire::onDeviceRemoved); | ||
| 278 | + this->mDevices.insertObject(iface); | ||
| 279 | +} | ||
| 280 | + | ||
| 281 | +void Pipewire::onDeviceRemoved(QObject* object) { | ||
| 282 | + auto* iface = static_cast<PwDeviceIface*>(object); // NOLINT | ||
| 283 | + this->mDevices.removeObject(iface); | ||
| 284 | +} | ||
| 285 | + | ||
| 286 | ObjectModel<PwLinkIface>* Pipewire::links() { return &this->mLinks; } | ||
| 287 | |||
| 288 | void Pipewire::onLinkAdded(PwLink* link) { | ||
| 289 | @@ -357,6 +379,8 @@ QVariantMap PwNodeIface::properties() const { | ||
| 290 | |||
| 291 | PwNodeAudioIface* PwNodeIface::audio() const { return this->audioIface; } | ||
| 292 | |||
| 293 | +PwDeviceIface* PwNodeIface::device() const { return PwDeviceIface::instance(this->mNode->device); } | ||
| 294 | + | ||
| 295 | PwNodeIface* PwNodeIface::instance(PwNode* node) { | ||
| 296 | if (node == nullptr) return nullptr; | ||
| 297 | |||
| 298 | @@ -481,4 +505,42 @@ void PwObjectTracker::objectDestroyed(QObject* object) { | ||
| 299 | emit this->objectsChanged(); | ||
| 300 | } | ||
| 301 | |||
| 302 | +PwDeviceIface::PwDeviceIface(PwDevice* device): PwObjectIface(device), mDevice(device) { | ||
| 303 | + QObject::connect(device, &PwDevice::profilesChanged, this, &PwDeviceIface::deviceProfilesChanged); | ||
| 304 | + QObject::connect(device, &PwDevice::currentProfileChanged, this, &PwDeviceIface::deviceCurrentProfileChanged); | ||
| 305 | +} | ||
| 306 | + | ||
| 307 | +void PwDeviceIface::deviceProfilesChanged(QList<PwProfile>) { emit this->profilesChanged(); } | ||
| 308 | +void PwDeviceIface::deviceCurrentProfileChanged(PwProfile) { emit this->currentProfileChanged(); } | ||
| 309 | + | ||
| 310 | +quint32 PwDeviceIface::id() const { return this->mDevice->id; } | ||
| 311 | +QString PwDeviceIface::name() const { return this->mDevice->name; } | ||
| 312 | +QString PwDeviceIface::description() const { return this->mDevice->description; } | ||
| 313 | +QString PwDeviceIface::nickname() const { return this->mDevice->nick; } | ||
| 314 | +QString PwDeviceIface::type() const { return this->mDevice->type; } | ||
| 315 | +QList<PwProfile> PwDeviceIface::profiles() const { | ||
| 316 | + QList<PwProfile> profiles = this->mDevice->profiles.values(); | ||
| 317 | + std::sort(profiles.begin(), profiles.end(), [](const PwProfile& a, const PwProfile& b) { return a.index < b.index; }); | ||
| 318 | + return profiles; | ||
| 319 | +} | ||
| 320 | +qint32 PwDeviceIface::currentProfile() const { return this->mDevice->currentProfile->index; } | ||
| 321 | + | ||
| 322 | +PwDeviceIface* PwDeviceIface::instance(PwDevice* device) { | ||
| 323 | + if (device == nullptr) return nullptr; | ||
| 324 | + | ||
| 325 | + auto v = device->property("iface"); | ||
| 326 | + if (v.canConvert<PwDeviceIface*>()) { | ||
| 327 | + return v.value<PwDeviceIface*>(); | ||
| 328 | + } | ||
| 329 | + | ||
| 330 | + auto* instance = new PwDeviceIface(device); | ||
| 331 | + device->setProperty("iface", QVariant::fromValue(instance)); | ||
| 332 | + | ||
| 333 | + return instance; | ||
| 334 | +} | ||
| 335 | + | ||
| 336 | +void PwDeviceIface::setProfile(qint32 profileIndex) { | ||
| 337 | + this->mDevice->setProfile(profileIndex); | ||
| 338 | +} | ||
| 339 | + | ||
| 340 | } // namespace qs::service::pipewire | ||
| 341 | diff --git i/src/services/pipewire/qml.hpp w/src/services/pipewire/qml.hpp | ||
| 342 | index e3489a1..e5e1891 100644 | ||
| 343 | --- i/src/services/pipewire/qml.hpp | ||
| 344 | +++ w/src/services/pipewire/qml.hpp | ||
| 345 | @@ -12,11 +12,13 @@ | ||
| 346 | #include "../../core/model.hpp" | ||
| 347 | #include "link.hpp" | ||
| 348 | #include "node.hpp" | ||
| 349 | +#include "device.hpp" | ||
| 350 | #include "registry.hpp" | ||
| 351 | |||
| 352 | namespace qs::service::pipewire { | ||
| 353 | |||
| 354 | class PwNodeIface; | ||
| 355 | +class PwDeviceIface; | ||
| 356 | class PwLinkIface; | ||
| 357 | class PwLinkGroupIface; | ||
| 358 | |||
| 359 | @@ -65,6 +67,8 @@ class Pipewire: public QObject { | ||
| 360 | /// - @@PwNode.audio - if non null the node is an audio node. | ||
| 361 | QSDOC_TYPE_OVERRIDE(ObjectModel<qs::service::pipewire::PwNodeIface>*); | ||
| 362 | Q_PROPERTY(UntypedObjectModel* nodes READ nodes CONSTANT); | ||
| 363 | + QSDOC_TYPE_OVERRIDE(ObjectModel<qs::service::pipewire::PwDeviceIface>*); | ||
| 364 | + Q_PROPERTY(UntypedObjectModel* devices READ devices CONSTANT); | ||
| 365 | /// All links present in pipewire. | ||
| 366 | /// | ||
| 367 | /// Links connect pipewire nodes to each other, and can be used to determine | ||
| 368 | @@ -134,6 +138,7 @@ public: | ||
| 369 | explicit Pipewire(QObject* parent = nullptr); | ||
| 370 | |||
| 371 | [[nodiscard]] ObjectModel<PwNodeIface>* nodes(); | ||
| 372 | + [[nodiscard]] ObjectModel<PwDeviceIface>* devices(); | ||
| 373 | [[nodiscard]] ObjectModel<PwLinkIface>* links(); | ||
| 374 | [[nodiscard]] ObjectModel<PwLinkGroupIface>* linkGroups(); | ||
| 375 | |||
| 376 | @@ -159,7 +164,9 @@ signals: | ||
| 377 | |||
| 378 | private slots: | ||
| 379 | void onNodeAdded(PwNode* node); | ||
| 380 | + void onDeviceAdded(PwDevice* node); | ||
| 381 | void onNodeRemoved(QObject* object); | ||
| 382 | + void onDeviceRemoved(QObject* object); | ||
| 383 | void onLinkAdded(PwLink* link); | ||
| 384 | void onLinkRemoved(QObject* object); | ||
| 385 | void onLinkGroupAdded(PwLinkGroup* group); | ||
| 386 | @@ -167,6 +174,7 @@ private slots: | ||
| 387 | |||
| 388 | private: | ||
| 389 | ObjectModel<PwNodeIface> mNodes {this}; | ||
| 390 | + ObjectModel<PwDeviceIface> mDevices {this}; | ||
| 391 | ObjectModel<PwLinkIface> mLinks {this}; | ||
| 392 | ObjectModel<PwLinkGroupIface> mLinkGroups {this}; | ||
| 393 | }; | ||
| 394 | @@ -315,6 +323,7 @@ class PwNodeIface: public PwObjectIface { | ||
| 395 | /// > [!NOTE] The node may be used before it is fully bound, but some data | ||
| 396 | /// > may be missing or incorrect. | ||
| 397 | Q_PROPERTY(bool ready READ isReady NOTIFY readyChanged); | ||
| 398 | + Q_PROPERTY(qs::service::pipewire::PwDeviceIface* device READ device CONSTANT); | ||
| 399 | QML_NAMED_ELEMENT(PwNode); | ||
| 400 | QML_UNCREATABLE("PwNodes cannot be created directly"); | ||
| 401 | |||
| 402 | @@ -332,6 +341,7 @@ public: | ||
| 403 | [[nodiscard]] PwNodeType::Flags type() const; | ||
| 404 | [[nodiscard]] QVariantMap properties() const; | ||
| 405 | [[nodiscard]] PwNodeAudioIface* audio() const; | ||
| 406 | + [[nodiscard]] PwDeviceIface* device() const; | ||
| 407 | |||
| 408 | static PwNodeIface* instance(PwNode* node); | ||
| 409 | |||
| 410 | @@ -344,6 +354,44 @@ private: | ||
| 411 | PwNodeAudioIface* audioIface = nullptr; | ||
| 412 | }; | ||
| 413 | |||
| 414 | +class PwDeviceIface: public PwObjectIface { | ||
| 415 | + Q_OBJECT; | ||
| 416 | + Q_PROPERTY(quint32 id READ id CONSTANT); | ||
| 417 | + Q_PROPERTY(QString name READ name CONSTANT); | ||
| 418 | + Q_PROPERTY(QString description READ description CONSTANT); | ||
| 419 | + Q_PROPERTY(QString nickname READ nickname CONSTANT); | ||
| 420 | + Q_PROPERTY(QString type READ type CONSTANT); | ||
| 421 | + Q_PROPERTY(QList<PwProfile> profiles READ profiles NOTIFY profilesChanged); | ||
| 422 | + Q_PROPERTY(qint32 currentProfile READ currentProfile NOTIFY currentProfileChanged); | ||
| 423 | + | ||
| 424 | + QML_NAMED_ELEMENT(PwDevice); | ||
| 425 | + QML_UNCREATABLE("PwDevices cannot be created directly"); | ||
| 426 | + | ||
| 427 | +signals: | ||
| 428 | + void profilesChanged(); | ||
| 429 | + void currentProfileChanged(); | ||
| 430 | + | ||
| 431 | +public: | ||
| 432 | + explicit PwDeviceIface(PwDevice* node); | ||
| 433 | + | ||
| 434 | + [[nodiscard]] quint32 id() const; | ||
| 435 | + [[nodiscard]] QString name() const; | ||
| 436 | + [[nodiscard]] QString description() const; | ||
| 437 | + [[nodiscard]] QString nickname() const; | ||
| 438 | + [[nodiscard]] QString type() const; | ||
| 439 | + QList<PwProfile> profiles() const; | ||
| 440 | + qint32 currentProfile() const; | ||
| 441 | + | ||
| 442 | + Q_INVOKABLE void setProfile(qint32 profileIndex); | ||
| 443 | + | ||
| 444 | + static PwDeviceIface* instance(PwDevice* node); | ||
| 445 | +private: | ||
| 446 | + PwDevice* mDevice; | ||
| 447 | + | ||
| 448 | + void deviceProfilesChanged(QList<PwProfile> profiles); | ||
| 449 | + void deviceCurrentProfileChanged(PwProfile profile); | ||
| 450 | +}; | ||
| 451 | + | ||
| 452 | ///! A connection between pipewire nodes. | ||
| 453 | /// Note that there is one link per *channel* of a connection between nodes. | ||
| 454 | /// You usually want @@PwLinkGroup. | ||
| 455 | diff --git i/src/services/pipewire/registry.cpp w/src/services/pipewire/registry.cpp | ||
| 456 | index c08fc1d..50c6d7a 100644 | ||
| 457 | --- i/src/services/pipewire/registry.cpp | ||
| 458 | +++ w/src/services/pipewire/registry.cpp | ||
| 459 | @@ -196,6 +196,7 @@ void PwRegistry::onGlobal( | ||
| 460 | device->initProps(props); | ||
| 461 | |||
| 462 | self->devices.emplace(id, device); | ||
| 463 | + emit self->deviceAdded(device); | ||
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | @@ -211,6 +212,9 @@ void PwRegistry::onGlobalRemoved(void* data, quint32 id) { | ||
| 468 | } else if (auto* node = self->nodes.value(id)) { | ||
| 469 | self->nodes.remove(id); | ||
| 470 | node->safeDestroy(); | ||
| 471 | + } else if (auto* device = self->devices.value(id)) { | ||
| 472 | + self->devices.remove(id); | ||
| 473 | + device->safeDestroy(); | ||
| 474 | } | ||
| 475 | } | ||
| 476 | |||
| 477 | diff --git i/src/services/pipewire/registry.hpp w/src/services/pipewire/registry.hpp | ||
| 478 | index 8473f04..87e0766 100644 | ||
| 479 | --- i/src/services/pipewire/registry.hpp | ||
| 480 | +++ w/src/services/pipewire/registry.hpp | ||
| 481 | @@ -132,6 +132,7 @@ public: | ||
| 482 | |||
| 483 | signals: | ||
| 484 | void nodeAdded(PwNode* node); | ||
| 485 | + void deviceAdded(PwDevice* node); | ||
| 486 | void linkAdded(PwLink* link); | ||
| 487 | void linkGroupAdded(PwLinkGroup* group); | ||
| 488 | void metadataAdded(PwMetadata* metadata); | ||
diff --git a/overlays/scutiger.nix b/overlays/scutiger.nix index 7a56adaf..fea15c5f 100644 --- a/overlays/scutiger.nix +++ b/overlays/scutiger.nix | |||
| @@ -2,7 +2,7 @@ | |||
| 2 | scutiger = final.rustPlatform.buildRustPackage { | 2 | scutiger = final.rustPlatform.buildRustPackage { |
| 3 | inherit (sources.scutiger) pname version src; | 3 | inherit (sources.scutiger) pname version src; |
| 4 | 4 | ||
| 5 | cargoHash = "sha256-d+wJ3trrldCVATsudsbglElU6q4LaS6feRocRyHal2k="; | 5 | cargoHash = if prev.lib.versionOlder prev.lib.version "24.05" then "sha256-d+wJ3trrldCVATsudsbglElU6q4LaS6feRocRyHal2k=" else "sha256-FTAEmRuO95ii84uwaALVuImiymnSAQkB2UwZ5yX0WPs="; |
| 6 | 6 | ||
| 7 | nativeBuildInputs = with final; [ pkg-config pcre2.dev zlib.dev git ]; | 7 | nativeBuildInputs = with final; [ pkg-config pcre2.dev zlib.dev git ]; |
| 8 | }; | 8 | }; |
diff --git a/overlays/spice-record.nix b/overlays/spice-record.nix index 06a114da..2d37079b 100644 --- a/overlays/spice-record.nix +++ b/overlays/spice-record.nix | |||
| @@ -8,5 +8,7 @@ | |||
| 8 | wrapProgram $out/bin/spice-record \ | 8 | wrapProgram $out/bin/spice-record \ |
| 9 | --prefix PATH : ${prev.lib.makeBinPath (with prev; [ ffmpeg-full ])} | 9 | --prefix PATH : ${prev.lib.makeBinPath (with prev; [ ffmpeg-full ])} |
| 10 | ''; | 10 | ''; |
| 11 | pyproject = true; | ||
| 12 | build-system = [ prev.python3Packages.setuptools ]; | ||
| 11 | }; | 13 | }; |
| 12 | } | 14 | } |
diff --git a/overlays/swayosd/default.nix b/overlays/swayosd/default.nix deleted file mode 100644 index 2a3a0f2b..00000000 --- a/overlays/swayosd/default.nix +++ /dev/null | |||
| @@ -1,13 +0,0 @@ | |||
| 1 | { final, prev, sources, ... }: { | ||
| 2 | swayosd = prev.swayosd.overrideAttrs (oldAttrs: rec { | ||
| 3 | inherit (sources.swayosd) version src; | ||
| 4 | cargoDeps = prev.rustPlatform.fetchCargoTarball { | ||
| 5 | inherit (oldAttrs) pname; | ||
| 6 | inherit version src; | ||
| 7 | hash = "sha256-s2vgyQP6J3i0q2o/Tt8dYoamBH3vrI/FNJYteHO8v5Q="; | ||
| 8 | }; | ||
| 9 | patches = (oldAttrs.patches or []) ++ [ | ||
| 10 | ./exponential.patch | ||
| 11 | ]; | ||
| 12 | }); | ||
| 13 | } | ||
diff --git a/overlays/swayosd/exponential.patch b/overlays/swayosd/exponential.patch deleted file mode 100644 index eb90d739..00000000 --- a/overlays/swayosd/exponential.patch +++ /dev/null | |||
| @@ -1,57 +0,0 @@ | |||
| 1 | diff --git a/src/brightness_backend/brightnessctl.rs b/src/brightness_backend/brightnessctl.rs | ||
| 2 | index ccb0e11..740fdb6 100644 | ||
| 3 | --- a/src/brightness_backend/brightnessctl.rs | ||
| 4 | +++ b/src/brightness_backend/brightnessctl.rs | ||
| 5 | @@ -107,10 +107,21 @@ impl VirtualDevice { | ||
| 6 | } | ||
| 7 | } | ||
| 8 | |||
| 9 | - fn set_percent(&mut self, mut val: u32) -> anyhow::Result<()> { | ||
| 10 | - val = val.clamp(0, 100); | ||
| 11 | - self.current = self.max.map(|max| val * max / 100); | ||
| 12 | - let _: String = self.run(("set", &*format!("{val}%")))?; | ||
| 13 | + fn val_to_percent(&mut self, val: u32) -> u32 { | ||
| 14 | + return ((val as f64 / self.get_max() as f64).powf(0.25) * 100_f64).round() as u32; | ||
| 15 | + } | ||
| 16 | + fn percent_to_val(&mut self, perc: u32) -> u32 { | ||
| 17 | + return ((perc as f64 / 100_f64).powf(4_f64) * self.get_max() as f64).round() as u32; | ||
| 18 | + } | ||
| 19 | + | ||
| 20 | + fn set_percent(&mut self, val: u32) -> anyhow::Result<()> { | ||
| 21 | + let new = self.percent_to_val(val); | ||
| 22 | + self.set_val(new) | ||
| 23 | + } | ||
| 24 | + fn set_val(&mut self, val: u32) -> anyhow::Result<()> { | ||
| 25 | + let curr = val.clamp(0, self.get_max()); | ||
| 26 | + self.current = Some(curr); | ||
| 27 | + let _: String = self.run(("set", &*format!("{curr}")))?; | ||
| 28 | Ok(()) | ||
| 29 | } | ||
| 30 | } | ||
| 31 | @@ -134,20 +145,18 @@ impl BrightnessBackend for BrightnessCtl { | ||
| 32 | |||
| 33 | fn lower(&mut self, by: u32) -> anyhow::Result<()> { | ||
| 34 | let curr = self.get_current(); | ||
| 35 | - let max = self.get_max(); | ||
| 36 | - | ||
| 37 | - let curr = curr * 100 / max; | ||
| 38 | + let mut new = self.device.val_to_percent(curr).saturating_sub(by); | ||
| 39 | + new = self.device.percent_to_val(new).min(curr.saturating_sub(1)); | ||
| 40 | |||
| 41 | - self.device.set_percent(curr.saturating_sub(by)) | ||
| 42 | + self.device.set_val(new) | ||
| 43 | } | ||
| 44 | |||
| 45 | fn raise(&mut self, by: u32) -> anyhow::Result<()> { | ||
| 46 | let curr = self.get_current(); | ||
| 47 | - let max = self.get_max(); | ||
| 48 | - | ||
| 49 | - let curr = curr * 100 / max; | ||
| 50 | + let mut new = self.device.val_to_percent(curr) + by; | ||
| 51 | + new = self.device.percent_to_val(new).max(curr + 1); | ||
| 52 | |||
| 53 | - self.device.set_percent(curr + by) | ||
| 54 | + self.device.set_val(new) | ||
| 55 | } | ||
| 56 | |||
| 57 | fn set(&mut self, val: u32) -> anyhow::Result<()> { | ||
diff --git a/overlays/thunderbird.nix b/overlays/thunderbird.nix deleted file mode 100644 index 48ae2ccb..00000000 --- a/overlays/thunderbird.nix +++ /dev/null | |||
| @@ -1,11 +0,0 @@ | |||
| 1 | { final, prev, ... }: { | ||
| 2 | thunderbird-unwrapped = prev.thunderbird-unwrapped.overrideAttrs (oldAttrs: { | ||
| 3 | patches = (oldAttrs.patches or []) | ||
| 4 | ++ prev.lib.optional (prev.lib.versionAtLeast oldAttrs.version "136") (prev.fetchpatch { | ||
| 5 | url = "https://hg.mozilla.org/comm-central/raw-rev/a82bd8fc0bc0"; | ||
| 6 | hash = "sha256-7t8IqxwcZJqXmOevpGof1mcrFKZvXtCcY2EOIsP47EY="; | ||
| 7 | stripLen = 1; | ||
| 8 | extraPrefix = "comm/"; | ||
| 9 | }); | ||
| 10 | }); | ||
| 11 | } | ||
diff --git a/overlays/uucp/default.nix b/overlays/uucp/default.nix deleted file mode 100644 index 4189dbcc..00000000 --- a/overlays/uucp/default.nix +++ /dev/null | |||
| @@ -1,9 +0,0 @@ | |||
| 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 deleted file mode 100644 index 89ac8f31..00000000 --- a/overlays/uucp/mailprogram.patch +++ /dev/null | |||
| @@ -1,16 +0,0 @@ | |||
| 1 | policy.h | 2 +- | ||
| 2 | 1 file changed, 1 insertion(+), 1 deletion(-) | ||
| 3 | |||
| 4 | diff --git a/policy.h b/policy.h | ||
| 5 | index 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/waybar-systemd-inhibit/.envrc b/overlays/waybar-systemd-inhibit/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/.envrc | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | use flake | ||
| 2 | |||
| 3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
| 4 | . .venv/bin/activate | ||
diff --git a/overlays/waybar-systemd-inhibit/.gitignore b/overlays/waybar-systemd-inhibit/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | .venv | ||
| 2 | **/__pycache__ | ||
diff --git a/overlays/waybar-systemd-inhibit/default.nix b/overlays/waybar-systemd-inhibit/default.nix new file mode 100644 index 00000000..ae6b8c75 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/default.nix | |||
| @@ -0,0 +1,20 @@ | |||
| 1 | { prev, final, flake, flakeInputs, ... }: | ||
| 2 | |||
| 3 | let | ||
| 4 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; | ||
| 5 | pythonSet = flake.lib.pythonSet { | ||
| 6 | pkgs = final; | ||
| 7 | python = final.python312; | ||
| 8 | overlay = workspace.mkPyprojectOverlay { | ||
| 9 | sourcePreference = "wheel"; | ||
| 10 | }; | ||
| 11 | }; | ||
| 12 | virtualEnv = pythonSet.mkVirtualEnv "waybar-systemd-inhibit-env" workspace.deps.default; | ||
| 13 | in { | ||
| 14 | waybar-systemd-inhibit = virtualEnv.overrideAttrs (oldAttrs: { | ||
| 15 | meta = (oldAttrs.meta or {}) // { | ||
| 16 | mainProgram = "waybar-systemd-inhibit"; | ||
| 17 | }; | ||
| 18 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ [ final.gobject-introspection final.wrapGAppsHook ]; | ||
| 19 | }); | ||
| 20 | } | ||
diff --git a/overlays/waybar-systemd-inhibit/pyproject.toml b/overlays/waybar-systemd-inhibit/pyproject.toml new file mode 100644 index 00000000..6c6240b8 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/pyproject.toml | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | [project] | ||
| 2 | name = "waybar-systemd-inhibit" | ||
| 3 | version = "0.1.0" | ||
| 4 | requires-python = ">=3.12" | ||
| 5 | dependencies = [ | ||
| 6 | "asyncclick>=8.1.8", | ||
| 7 | "asyncio>=3.4.3", | ||
| 8 | "dbus-next>=0.2.3", | ||
| 9 | ] | ||
| 10 | |||
| 11 | [project.scripts] | ||
| 12 | waybar-systemd-inhibit = "waybar_systemd_inhibit.__main__:main" | ||
| 13 | waybar-systemd-inhibit-toggle = "waybar_systemd_inhibit.__main__:toggle" | ||
| 14 | |||
| 15 | [build-system] | ||
| 16 | requires = ["hatchling"] | ||
| 17 | build-backend = "hatchling.build" | ||
diff --git a/overlays/waybar-systemd-inhibit/uv.lock b/overlays/waybar-systemd-inhibit/uv.lock new file mode 100644 index 00000000..4e10d145 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/uv.lock | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | version = 1 | ||
| 2 | revision = 2 | ||
| 3 | requires-python = ">=3.12" | ||
| 4 | |||
| 5 | [[package]] | ||
| 6 | name = "anyio" | ||
| 7 | version = "4.9.0" | ||
| 8 | source = { registry = "https://pypi.org/simple" } | ||
| 9 | dependencies = [ | ||
| 10 | { name = "idna" }, | ||
| 11 | { name = "sniffio" }, | ||
| 12 | { name = "typing-extensions", marker = "python_full_version < '3.13'" }, | ||
| 13 | ] | ||
| 14 | sdist = { url = "https://files.pythonhosted.org/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949, upload-time = "2025-03-17T00:02:54.77Z" } | ||
| 15 | wheels = [ | ||
| 16 | { url = "https://files.pythonhosted.org/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916, upload-time = "2025-03-17T00:02:52.713Z" }, | ||
| 17 | ] | ||
| 18 | |||
| 19 | [[package]] | ||
| 20 | name = "asyncclick" | ||
| 21 | version = "8.1.8" | ||
| 22 | source = { registry = "https://pypi.org/simple" } | ||
| 23 | dependencies = [ | ||
| 24 | { name = "anyio" }, | ||
| 25 | { name = "colorama", marker = "sys_platform == 'win32'" }, | ||
| 26 | ] | ||
| 27 | sdist = { url = "https://files.pythonhosted.org/packages/cb/b5/e1e5fdf1c1bb7e6e614987c120a98d9324bf8edfaa5f5cd16a6235c9d91b/asyncclick-8.1.8.tar.gz", hash = "sha256:0f0eb0f280e04919d67cf71b9fcdfb4db2d9ff7203669c40284485c149578e4c", size = 232900, upload-time = "2025-01-06T09:46:52.694Z" } | ||
| 28 | wheels = [ | ||
| 29 | { url = "https://files.pythonhosted.org/packages/14/cc/a436f0fc2d04e57a0697e0f87a03b9eaed03ad043d2d5f887f8eebcec95f/asyncclick-8.1.8-py3-none-any.whl", hash = "sha256:eb1ccb44bc767f8f0695d592c7806fdf5bd575605b4ee246ffd5fadbcfdbd7c6", size = 99093, upload-time = "2025-01-06T09:46:51.046Z" }, | ||
| 30 | { url = "https://files.pythonhosted.org/packages/92/c4/ae9e9d25522c6dc96ff167903880a0fe94d7bd31ed999198ee5017d977ed/asyncclick-8.1.8.0-py3-none-any.whl", hash = "sha256:be146a2d8075d4fe372ff4e877f23c8b5af269d16705c1948123b9415f6fd678", size = 99115, upload-time = "2025-01-06T09:50:52.72Z" }, | ||
| 31 | ] | ||
| 32 | |||
| 33 | [[package]] | ||
| 34 | name = "asyncio" | ||
| 35 | version = "3.4.3" | ||
| 36 | source = { registry = "https://pypi.org/simple" } | ||
| 37 | sdist = { url = "https://files.pythonhosted.org/packages/da/54/054bafaf2c0fb8473d423743e191fcdf49b2c1fd5e9af3524efbe097bafd/asyncio-3.4.3.tar.gz", hash = "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41", size = 204411, upload-time = "2015-03-10T14:11:26.494Z" } | ||
| 38 | wheels = [ | ||
| 39 | { url = "https://files.pythonhosted.org/packages/22/74/07679c5b9f98a7cb0fc147b1ef1cc1853bc07a4eb9cb5731e24732c5f773/asyncio-3.4.3-py3-none-any.whl", hash = "sha256:c4d18b22701821de07bd6aea8b53d21449ec0ec5680645e5317062ea21817d2d", size = 101767, upload-time = "2015-03-10T14:05:10.959Z" }, | ||
| 40 | ] | ||
| 41 | |||
| 42 | [[package]] | ||
| 43 | name = "colorama" | ||
| 44 | version = "0.4.6" | ||
| 45 | source = { registry = "https://pypi.org/simple" } | ||
| 46 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } | ||
| 47 | wheels = [ | ||
| 48 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, | ||
| 49 | ] | ||
| 50 | |||
| 51 | [[package]] | ||
| 52 | name = "dbus-next" | ||
| 53 | version = "0.2.3" | ||
| 54 | source = { registry = "https://pypi.org/simple" } | ||
| 55 | sdist = { url = "https://files.pythonhosted.org/packages/ce/45/6a40fbe886d60a8c26f480e7d12535502b5ba123814b3b9a0b002ebca198/dbus_next-0.2.3.tar.gz", hash = "sha256:f4eae26909332ada528c0a3549dda8d4f088f9b365153952a408e28023a626a5", size = 71112, upload-time = "2021-07-25T22:11:28.398Z" } | ||
| 56 | wheels = [ | ||
| 57 | { url = "https://files.pythonhosted.org/packages/d2/fc/c0a3f4c4eaa5a22fbef91713474666e13d0ea2a69c84532579490a9f2cc8/dbus_next-0.2.3-py3-none-any.whl", hash = "sha256:58948f9aff9db08316734c0be2a120f6dc502124d9642f55e90ac82ffb16a18b", size = 57885, upload-time = "2021-07-25T22:11:25.466Z" }, | ||
| 58 | ] | ||
| 59 | |||
| 60 | [[package]] | ||
| 61 | name = "idna" | ||
| 62 | version = "3.10" | ||
| 63 | source = { registry = "https://pypi.org/simple" } | ||
| 64 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } | ||
| 65 | wheels = [ | ||
| 66 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, | ||
| 67 | ] | ||
| 68 | |||
| 69 | [[package]] | ||
| 70 | name = "sniffio" | ||
| 71 | version = "1.3.1" | ||
| 72 | source = { registry = "https://pypi.org/simple" } | ||
| 73 | sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } | ||
| 74 | wheels = [ | ||
| 75 | { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, | ||
| 76 | ] | ||
| 77 | |||
| 78 | [[package]] | ||
| 79 | name = "typing-extensions" | ||
| 80 | version = "4.13.2" | ||
| 81 | source = { registry = "https://pypi.org/simple" } | ||
| 82 | sdist = { url = "https://files.pythonhosted.org/packages/f6/37/23083fcd6e35492953e8d2aaaa68b860eb422b34627b13f2ce3eb6106061/typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef", size = 106967, upload-time = "2025-04-10T14:19:05.416Z" } | ||
| 83 | wheels = [ | ||
| 84 | { url = "https://files.pythonhosted.org/packages/8b/54/b1ae86c0973cc6f0210b53d508ca3641fb6d0c56823f288d108bc7ab3cc8/typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", size = 45806, upload-time = "2025-04-10T14:19:03.967Z" }, | ||
| 85 | ] | ||
| 86 | |||
| 87 | [[package]] | ||
| 88 | name = "waybar-systemd-inhibit" | ||
| 89 | version = "0.1.0" | ||
| 90 | source = { editable = "." } | ||
| 91 | dependencies = [ | ||
| 92 | { name = "asyncclick" }, | ||
| 93 | { name = "asyncio" }, | ||
| 94 | { name = "dbus-next" }, | ||
| 95 | ] | ||
| 96 | |||
| 97 | [package.metadata] | ||
| 98 | requires-dist = [ | ||
| 99 | { name = "asyncclick", specifier = ">=8.1.8" }, | ||
| 100 | { name = "asyncio", specifier = ">=3.4.3" }, | ||
| 101 | { name = "dbus-next", specifier = ">=0.2.3" }, | ||
| 102 | ] | ||
diff --git a/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__init__.py b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__init__.py | |||
diff --git a/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__main__.py b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__main__.py new file mode 100644 index 00000000..35cc7fd1 --- /dev/null +++ b/overlays/waybar-systemd-inhibit/waybar_systemd_inhibit/__main__.py | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | import asyncclick as click | ||
| 2 | from dbus_next.aio import MessageBus | ||
| 3 | from dbus_next import BusType, Message, PropertyAccess | ||
| 4 | import asyncio | ||
| 5 | from functools import update_wrapper | ||
| 6 | from dbus_next.service import ServiceInterface, method, dbus_property | ||
| 7 | from dbus_next import Variant, DBusError | ||
| 8 | import os | ||
| 9 | import json | ||
| 10 | |||
| 11 | class BlockInterface(ServiceInterface): | ||
| 12 | def __init__(self, system_bus, logind): | ||
| 13 | super().__init__('li.yggdrasil.WaybarSystemdInhibit') | ||
| 14 | self.system_bus = system_bus | ||
| 15 | self.logind = logind | ||
| 16 | self.fd = None | ||
| 17 | |||
| 18 | def Release(self): | ||
| 19 | if not self.fd: | ||
| 20 | return | ||
| 21 | |||
| 22 | os.close(self.fd) | ||
| 23 | self.fd = None | ||
| 24 | self.emit_properties_changed({'IsAcquired': False}) | ||
| 25 | |||
| 26 | async def Acquire(self): | ||
| 27 | if self.fd: | ||
| 28 | return | ||
| 29 | |||
| 30 | res = await self.system_bus.call(Message( | ||
| 31 | destination='org.freedesktop.login1', | ||
| 32 | path='/org/freedesktop/login1', | ||
| 33 | interface='org.freedesktop.login1.Manager', | ||
| 34 | member='Inhibit', | ||
| 35 | signature='ssss', | ||
| 36 | body=[ | ||
| 37 | "handle-lid-switch", | ||
| 38 | "waybar-systemd-inhibit", | ||
| 39 | "User request", | ||
| 40 | "block", | ||
| 41 | ], | ||
| 42 | )) | ||
| 43 | self.fd = res.unix_fds[res.body[0]] | ||
| 44 | self.emit_properties_changed({'IsAcquired': True}) | ||
| 45 | |||
| 46 | @method() | ||
| 47 | async def ToggleBlock(self): | ||
| 48 | if self.fd: | ||
| 49 | self.Release() | ||
| 50 | else: | ||
| 51 | await self.Acquire() | ||
| 52 | |||
| 53 | @dbus_property(access=PropertyAccess.READ) | ||
| 54 | def IsAcquired(self) -> 'b': | ||
| 55 | return self.fd is not None | ||
| 56 | |||
| 57 | |||
| 58 | @click.command() | ||
| 59 | async def main(): | ||
| 60 | system_bus = await MessageBus(bus_type=BusType.SYSTEM, negotiate_unix_fd=True).connect() | ||
| 61 | session_bus = await MessageBus(bus_type=BusType.SESSION).connect() | ||
| 62 | |||
| 63 | introspection = await system_bus.introspect('org.freedesktop.login1', '/org/freedesktop/login1') | ||
| 64 | obj = system_bus.get_proxy_object('org.freedesktop.login1', '/org/freedesktop/login1', introspection) | ||
| 65 | logind = obj.get_interface('org.freedesktop.login1.Manager') | ||
| 66 | properties = obj.get_interface('org.freedesktop.DBus.Properties') | ||
| 67 | |||
| 68 | def is_blocked_logind(what: str): | ||
| 69 | return "handle-lid-switch" in what.split(':') | ||
| 70 | |||
| 71 | def print_state(is_blocked: bool, is_acquired: bool = False): | ||
| 72 | icon = "󰌢" if is_blocked else "󰛧" | ||
| 73 | text = f"<span font=\"Symbols Nerd Font Mono\">{icon}</span>" | ||
| 74 | if is_acquired: | ||
| 75 | text = f"<span color=\"#f28a21\">{text}</span>" | ||
| 76 | elif is_blocked: | ||
| 77 | text = f"<span color=\"#ffffff\">{text}</span>" | ||
| 78 | print(json.dumps({'text': text, 'tooltip': ("Manually inhibited" if is_acquired else None)}, separators=(',', ':')), flush=True) | ||
| 79 | |||
| 80 | print_state(is_blocked_logind(await logind.get_block_inhibited())) | ||
| 81 | |||
| 82 | async def get_inhibit(): | ||
| 83 | introspection = await session_bus.introspect('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit') | ||
| 84 | return session_bus.get_proxy_object('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit', introspection) | ||
| 85 | |||
| 86 | async def on_logind_properties_changed(interface_name, changed_properties, invalidated_properties): | ||
| 87 | if 'BlockInhibited' not in changed_properties: | ||
| 88 | return | ||
| 89 | |||
| 90 | properties = (await get_inhibit()).get_interface('li.yggdrasil.WaybarSystemdInhibit') | ||
| 91 | |||
| 92 | print_state(is_blocked_logind(changed_properties['BlockInhibited'].value), await properties.get_is_acquired()) | ||
| 93 | |||
| 94 | properties.on_properties_changed(on_logind_properties_changed) | ||
| 95 | |||
| 96 | session_bus.export('/li/yggdrasil/WaybarSystemdInhibit', BlockInterface(system_bus, logind)) | ||
| 97 | await session_bus.request_name('li.yggdrasil.WaybarSystemdInhibit') | ||
| 98 | |||
| 99 | properties = (await get_inhibit()).get_interface('org.freedesktop.DBus.Properties') | ||
| 100 | |||
| 101 | async def on_inhibit_properties_changed(interface_name, changed_properties, invalidated_properties): | ||
| 102 | if 'IsAcquired' not in changed_properties: | ||
| 103 | return | ||
| 104 | |||
| 105 | print_state(is_blocked_logind(await logind.get_block_inhibited()), changed_properties['IsAcquired'].value) | ||
| 106 | |||
| 107 | properties.on_properties_changed(on_inhibit_properties_changed) | ||
| 108 | |||
| 109 | await session_bus.wait_for_disconnect() | ||
| 110 | |||
| 111 | @click.command() | ||
| 112 | async def toggle(): | ||
| 113 | session_bus = await MessageBus(bus_type=BusType.SESSION).connect() | ||
| 114 | introspection = await session_bus.introspect('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit') | ||
| 115 | obj = session_bus.get_proxy_object('li.yggdrasil.WaybarSystemdInhibit', '/li/yggdrasil/WaybarSystemdInhibit', introspection) | ||
| 116 | interface = obj.get_interface('li.yggdrasil.WaybarSystemdInhibit') | ||
| 117 | await interface.call_toggle_block() | ||
diff --git a/overlays/worktime/.envrc b/overlays/worktime/.envrc new file mode 100644 index 00000000..2c909235 --- /dev/null +++ b/overlays/worktime/.envrc | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | use flake | ||
| 2 | |||
| 3 | [[ -d ".venv" ]] || ( uv venv && uv sync ) | ||
| 4 | . .venv/bin/activate | ||
diff --git a/overlays/worktime/.gitignore b/overlays/worktime/.gitignore new file mode 100644 index 00000000..4ccfae70 --- /dev/null +++ b/overlays/worktime/.gitignore | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | .venv | ||
| 2 | **/__pycache__ | ||
diff --git a/overlays/worktime/default.nix b/overlays/worktime/default.nix index 1d8433af..579cf7ad 100644 --- a/overlays/worktime/default.nix +++ b/overlays/worktime/default.nix | |||
| @@ -1,13 +1,19 @@ | |||
| 1 | { prev, ... }: | 1 | { prev, final, flake, flakeInputs, ... }: |
| 2 | 2 | ||
| 3 | with prev.poetry2nix; | 3 | let |
| 4 | 4 | workspace = flakeInputs.uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; }; | |
| 5 | { | 5 | pythonSet = flake.lib.pythonSet { |
| 6 | worktime = mkPoetryApplication { | 6 | pkgs = final; |
| 7 | python = prev.python312; | 7 | python = final.python312; |
| 8 | 8 | overlay = workspace.mkPyprojectOverlay { | |
| 9 | projectDir = cleanPythonSources { src = ./.; }; | 9 | sourcePreference = "wheel"; |
| 10 | 10 | }; | |
| 11 | meta.mainProgram = "worktime"; | ||
| 12 | }; | 11 | }; |
| 12 | virtualEnv = pythonSet.mkVirtualEnv "worktime" workspace.deps.default; | ||
| 13 | in { | ||
| 14 | worktime = virtualEnv.overrideAttrs (oldAttrs: { | ||
| 15 | meta = (oldAttrs.meta or {}) // { | ||
| 16 | mainProgram = "worktime"; | ||
| 17 | }; | ||
| 18 | }); | ||
| 13 | } | 19 | } |
diff --git a/overlays/worktime/poetry.lock b/overlays/worktime/poetry.lock deleted file mode 100644 index 7c1ca91d..00000000 --- a/overlays/worktime/poetry.lock +++ /dev/null | |||
| @@ -1,284 +0,0 @@ | |||
| 1 | # This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. | ||
| 2 | |||
| 3 | [[package]] | ||
| 4 | name = "backoff" | ||
| 5 | version = "2.2.1" | ||
| 6 | description = "Function decoration for backoff and retry" | ||
| 7 | optional = false | ||
| 8 | python-versions = ">=3.7,<4.0" | ||
| 9 | groups = ["main"] | ||
| 10 | files = [ | ||
| 11 | {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, | ||
| 12 | {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, | ||
| 13 | ] | ||
| 14 | |||
| 15 | [[package]] | ||
| 16 | name = "certifi" | ||
| 17 | version = "2025.1.31" | ||
| 18 | description = "Python package for providing Mozilla's CA Bundle." | ||
| 19 | optional = false | ||
| 20 | python-versions = ">=3.6" | ||
| 21 | groups = ["main"] | ||
| 22 | files = [ | ||
| 23 | {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, | ||
| 24 | {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, | ||
| 25 | ] | ||
| 26 | |||
| 27 | [[package]] | ||
| 28 | name = "charset-normalizer" | ||
| 29 | version = "3.4.1" | ||
| 30 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." | ||
| 31 | optional = false | ||
| 32 | python-versions = ">=3.7" | ||
| 33 | groups = ["main"] | ||
| 34 | files = [ | ||
| 35 | {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, | ||
| 36 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, | ||
| 37 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, | ||
| 38 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, | ||
| 39 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, | ||
| 40 | {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, | ||
| 41 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, | ||
| 42 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, | ||
| 43 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, | ||
| 44 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, | ||
| 45 | {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, | ||
| 46 | {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, | ||
| 47 | {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, | ||
| 48 | {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, | ||
| 49 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, | ||
| 50 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, | ||
| 51 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, | ||
| 52 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, | ||
| 53 | {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, | ||
| 54 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, | ||
| 55 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, | ||
| 56 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, | ||
| 57 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, | ||
| 58 | {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, | ||
| 59 | {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, | ||
| 60 | {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, | ||
| 61 | {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, | ||
| 62 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, | ||
| 63 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, | ||
| 64 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, | ||
| 65 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, | ||
| 66 | {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, | ||
| 67 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, | ||
| 68 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, | ||
| 69 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, | ||
| 70 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, | ||
| 71 | {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, | ||
| 72 | {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, | ||
| 73 | {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, | ||
| 74 | {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, | ||
| 75 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, | ||
| 76 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, | ||
| 77 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, | ||
| 78 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, | ||
| 79 | {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, | ||
| 80 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, | ||
| 81 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, | ||
| 82 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, | ||
| 83 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, | ||
| 84 | {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, | ||
| 85 | {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, | ||
| 86 | {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, | ||
| 87 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, | ||
| 88 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, | ||
| 89 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, | ||
| 90 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, | ||
| 91 | {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, | ||
| 92 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, | ||
| 93 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, | ||
| 94 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, | ||
| 95 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, | ||
| 96 | {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, | ||
| 97 | {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, | ||
| 98 | {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, | ||
| 99 | {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, | ||
| 100 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, | ||
| 101 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, | ||
| 102 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, | ||
| 103 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, | ||
| 104 | {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, | ||
| 105 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, | ||
| 106 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, | ||
| 107 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, | ||
| 108 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, | ||
| 109 | {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, | ||
| 110 | {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, | ||
| 111 | {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, | ||
| 112 | {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, | ||
| 113 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, | ||
| 114 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, | ||
| 115 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, | ||
| 116 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, | ||
| 117 | {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, | ||
| 118 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, | ||
| 119 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, | ||
| 120 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, | ||
| 121 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, | ||
| 122 | {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, | ||
| 123 | {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, | ||
| 124 | {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, | ||
| 125 | {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, | ||
| 126 | {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, | ||
| 127 | ] | ||
| 128 | |||
| 129 | [[package]] | ||
| 130 | name = "idna" | ||
| 131 | version = "3.10" | ||
| 132 | description = "Internationalized Domain Names in Applications (IDNA)" | ||
| 133 | optional = false | ||
| 134 | python-versions = ">=3.6" | ||
| 135 | groups = ["main"] | ||
| 136 | files = [ | ||
| 137 | {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, | ||
| 138 | {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, | ||
| 139 | ] | ||
| 140 | |||
| 141 | [package.extras] | ||
| 142 | all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] | ||
| 143 | |||
| 144 | [[package]] | ||
| 145 | name = "jsonpickle" | ||
| 146 | version = "4.0.5" | ||
| 147 | description = "jsonpickle encodes/decodes any Python object to/from JSON" | ||
| 148 | optional = false | ||
| 149 | python-versions = ">=3.8" | ||
| 150 | groups = ["main"] | ||
| 151 | files = [ | ||
| 152 | {file = "jsonpickle-4.0.5-py3-none-any.whl", hash = "sha256:b4ac7d0a75ddcdfd93445737f1d36ff28768690d43e54bf5d0ddb1d915e580df"}, | ||
| 153 | {file = "jsonpickle-4.0.5.tar.gz", hash = "sha256:f299818b39367c361b3f26bdba827d4249ab5d383cd93144d0f94b5417aacb35"}, | ||
| 154 | ] | ||
| 155 | |||
| 156 | [package.extras] | ||
| 157 | cov = ["pytest-cov"] | ||
| 158 | dev = ["black", "pyupgrade"] | ||
| 159 | docs = ["furo", "rst.linker (>=1.9)", "sphinx (>=3.5)"] | ||
| 160 | packaging = ["build", "setuptools (>=61.2)", "setuptools-scm[toml] (>=6.0)", "twine"] | ||
| 161 | testing = ["PyYAML", "atheris (>=2.3.0,<2.4.0) ; python_version < \"3.12\"", "bson", "ecdsa", "feedparser", "gmpy2", "numpy", "pandas", "pymongo", "pytest (>=6.0,!=8.1.*)", "pytest-benchmark", "pytest-benchmark[histogram]", "pytest-checkdocs (>=1.2.3)", "pytest-enabler (>=1.0.1)", "pytest-ruff (>=0.2.1)", "scikit-learn", "scipy (>=1.9.3) ; python_version > \"3.10\"", "scipy ; python_version <= \"3.10\"", "simplejson", "sqlalchemy", "ujson"] | ||
| 162 | |||
| 163 | [[package]] | ||
| 164 | name = "python-dateutil" | ||
| 165 | version = "2.9.0.post0" | ||
| 166 | description = "Extensions to the standard Python datetime module" | ||
| 167 | optional = false | ||
| 168 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" | ||
| 169 | groups = ["main"] | ||
| 170 | files = [ | ||
| 171 | {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, | ||
| 172 | {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, | ||
| 173 | ] | ||
| 174 | |||
| 175 | [package.dependencies] | ||
| 176 | six = ">=1.5" | ||
| 177 | |||
| 178 | [[package]] | ||
| 179 | name = "pyxdg" | ||
| 180 | version = "0.28" | ||
| 181 | description = "PyXDG contains implementations of freedesktop.org standards in python." | ||
| 182 | optional = false | ||
| 183 | python-versions = "*" | ||
| 184 | groups = ["main"] | ||
| 185 | files = [ | ||
| 186 | {file = "pyxdg-0.28-py2.py3-none-any.whl", hash = "sha256:bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab"}, | ||
| 187 | {file = "pyxdg-0.28.tar.gz", hash = "sha256:3267bb3074e934df202af2ee0868575484108581e6f3cb006af1da35395e88b4"}, | ||
| 188 | ] | ||
| 189 | |||
| 190 | [[package]] | ||
| 191 | name = "requests" | ||
| 192 | version = "2.32.3" | ||
| 193 | description = "Python HTTP for Humans." | ||
| 194 | optional = false | ||
| 195 | python-versions = ">=3.8" | ||
| 196 | groups = ["main"] | ||
| 197 | files = [ | ||
| 198 | {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, | ||
| 199 | {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, | ||
| 200 | ] | ||
| 201 | |||
| 202 | [package.dependencies] | ||
| 203 | certifi = ">=2017.4.17" | ||
| 204 | charset-normalizer = ">=2,<4" | ||
| 205 | idna = ">=2.5,<4" | ||
| 206 | urllib3 = ">=1.21.1,<3" | ||
| 207 | |||
| 208 | [package.extras] | ||
| 209 | socks = ["PySocks (>=1.5.6,!=1.5.7)"] | ||
| 210 | use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] | ||
| 211 | |||
| 212 | [[package]] | ||
| 213 | name = "six" | ||
| 214 | version = "1.17.0" | ||
| 215 | description = "Python 2 and 3 compatibility utilities" | ||
| 216 | optional = false | ||
| 217 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" | ||
| 218 | groups = ["main"] | ||
| 219 | files = [ | ||
| 220 | {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, | ||
| 221 | {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, | ||
| 222 | ] | ||
| 223 | |||
| 224 | [[package]] | ||
| 225 | name = "tabulate" | ||
| 226 | version = "0.9.0" | ||
| 227 | description = "Pretty-print tabular data" | ||
| 228 | optional = false | ||
| 229 | python-versions = ">=3.7" | ||
| 230 | groups = ["main"] | ||
| 231 | files = [ | ||
| 232 | {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, | ||
| 233 | {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, | ||
| 234 | ] | ||
| 235 | |||
| 236 | [package.extras] | ||
| 237 | widechars = ["wcwidth"] | ||
| 238 | |||
| 239 | [[package]] | ||
| 240 | name = "toml" | ||
| 241 | version = "0.10.2" | ||
| 242 | description = "Python Library for Tom's Obvious, Minimal Language" | ||
| 243 | optional = false | ||
| 244 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" | ||
| 245 | groups = ["main"] | ||
| 246 | files = [ | ||
| 247 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, | ||
| 248 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, | ||
| 249 | ] | ||
| 250 | |||
| 251 | [[package]] | ||
| 252 | name = "uritools" | ||
| 253 | version = "4.0.3" | ||
| 254 | description = "URI parsing, classification and composition" | ||
| 255 | optional = false | ||
| 256 | python-versions = ">=3.7" | ||
| 257 | groups = ["main"] | ||
| 258 | files = [ | ||
| 259 | {file = "uritools-4.0.3-py3-none-any.whl", hash = "sha256:bae297d090e69a0451130ffba6f2f1c9477244aa0a5543d66aed2d9f77d0dd9c"}, | ||
| 260 | {file = "uritools-4.0.3.tar.gz", hash = "sha256:ee06a182a9c849464ce9d5fa917539aacc8edd2a4924d1b7aabeeecabcae3bc2"}, | ||
| 261 | ] | ||
| 262 | |||
| 263 | [[package]] | ||
| 264 | name = "urllib3" | ||
| 265 | version = "2.3.0" | ||
| 266 | description = "HTTP library with thread-safe connection pooling, file post, and more." | ||
| 267 | optional = false | ||
| 268 | python-versions = ">=3.9" | ||
| 269 | groups = ["main"] | ||
| 270 | files = [ | ||
| 271 | {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, | ||
| 272 | {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, | ||
| 273 | ] | ||
| 274 | |||
| 275 | [package.extras] | ||
| 276 | brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] | ||
| 277 | h2 = ["h2 (>=4,<5)"] | ||
| 278 | socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] | ||
| 279 | zstd = ["zstandard (>=0.18.0)"] | ||
| 280 | |||
| 281 | [metadata] | ||
| 282 | lock-version = "2.1" | ||
| 283 | python-versions = "^3.12" | ||
| 284 | content-hash = "2b335da94bf3e2d2bee7d8ca6e84cdb56e97ac29d1224d8c8dca98d93bbdcea2" | ||
diff --git a/overlays/worktime/pyproject.toml b/overlays/worktime/pyproject.toml index de4b9fd4..42da51f5 100644 --- a/overlays/worktime/pyproject.toml +++ b/overlays/worktime/pyproject.toml | |||
| @@ -1,23 +1,28 @@ | |||
| 1 | [tool.poetry] | 1 | [project] |
| 2 | name = "worktime" | 2 | name = "worktime" |
| 3 | version = "0.1.0" | 3 | version = "1.0.0" |
| 4 | description = "" | 4 | requires-python = "~=3.12" |
| 5 | authors = ["Gregor Kleen <gkleen@yggdrasil.li>"] | 5 | dependencies = [ |
| 6 | "pyxdg>=0.28,<0.29", | ||
| 7 | "python-dateutil>=2.9.0.post0,<3", | ||
| 8 | "uritools>=4.0.3,<5", | ||
| 9 | "requests>=2.32.3,<3", | ||
| 10 | "tabulate>=0.9.0,<0.10", | ||
| 11 | "toml>=0.10.2,<0.11", | ||
| 12 | "jsonpickle>=4.0.5,<5", | ||
| 13 | "frozendict>=2.4.6", | ||
| 14 | "atomicwriter>=0.2.5", | ||
| 15 | "desktop-notify>=1.3.3", | ||
| 16 | ] | ||
| 6 | 17 | ||
| 7 | [tool.poetry.dependencies] | 18 | [project.scripts] |
| 8 | python = "^3.12" | ||
| 9 | pyxdg = "^0.28" | ||
| 10 | python-dateutil = "^2.9.0.post0" | ||
| 11 | uritools = "^4.0.3" | ||
| 12 | requests = "^2.32.3" | ||
| 13 | tabulate = "^0.9.0" | ||
| 14 | backoff = "^2.2.1" | ||
| 15 | toml = "^0.10.2" | ||
| 16 | jsonpickle = "^4.0.5" | ||
| 17 | |||
| 18 | [tool.poetry.scripts] | ||
| 19 | worktime = "worktime.__main__:main" | 19 | worktime = "worktime.__main__:main" |
| 20 | worktime-ui = "worktime.__main__:ui" | ||
| 21 | worktime-stop = "worktime.__main__:stop" | ||
| 20 | 22 | ||
| 21 | [build-system] | 23 | [build-system] |
| 22 | requires = ["poetry-core"] | 24 | requires = ["hatchling"] |
| 23 | build-backend = "poetry.core.masonry.api" \ No newline at end of file | 25 | build-backend = "hatchling.build" |
| 26 | |||
| 27 | [dependency-groups] | ||
| 28 | dev = [] | ||
diff --git a/overlays/worktime/uv.lock b/overlays/worktime/uv.lock new file mode 100644 index 00000000..39de4ccf --- /dev/null +++ b/overlays/worktime/uv.lock | |||
| @@ -0,0 +1,248 @@ | |||
| 1 | version = 1 | ||
| 2 | revision = 2 | ||
| 3 | requires-python = ">=3.12, <4" | ||
| 4 | |||
| 5 | [[package]] | ||
| 6 | name = "atomicwriter" | ||
| 7 | version = "0.2.5" | ||
| 8 | source = { registry = "https://pypi.org/simple" } | ||
| 9 | sdist = { url = "https://files.pythonhosted.org/packages/50/b4/dd04e186eb244d1ed84b1d0ebfba19ddc7f8886b98e345aaca4208b031d2/atomicwriter-0.2.5.tar.gz", hash = "sha256:5ced6afb0579377a13e191b17a16115e14c30ec00e6c38b60403f58235a867af", size = 64990, upload-time = "2025-05-24T20:35:42.538Z" } | ||
| 10 | wheels = [ | ||
| 11 | { url = "https://files.pythonhosted.org/packages/99/7c/672a0de09b0b355a2ffa521ef25cf106f1984823379dee37f7305fdc1774/atomicwriter-0.2.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5c1fab874e62ebe96f1af0e965dc1e92c4c1ef2e2e9612a444371b8fc751ec43", size = 234141, upload-time = "2025-05-24T20:34:32.74Z" }, | ||
| 12 | { url = "https://files.pythonhosted.org/packages/b9/0c/e1c5bad033284c212c0a77121b48dd4147f80e9a7cd82a9d2ce0a2160901/atomicwriter-0.2.5-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:8dbb67cc730be7d6bdfd5e991271bc17052be8fb2e4fa27854b47d8a76d36349", size = 245788, upload-time = "2025-05-24T20:34:33.897Z" }, | ||
| 13 | { url = "https://files.pythonhosted.org/packages/f4/d3/7036e203cc5fc4c49bf916b4ba158e0d2779de127afad5963edd7e3b9400/atomicwriter-0.2.5-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:a4e7f81932839c738425dc96ad98e4a7511b740cd3d75f480bfabbcf8e6f7eae", size = 260428, upload-time = "2025-05-24T20:34:35.533Z" }, | ||
| 14 | { url = "https://files.pythonhosted.org/packages/e5/b9/9a4d235a8d67fb442302dc0f3ea2394b7bd994bfc99b1dc0f744c7852418/atomicwriter-0.2.5-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:de37a3a5d1b57b719cfb0b81a11cab2114acfdc2c36051bf0af72d05eb644411", size = 263648, upload-time = "2025-05-24T20:34:36.72Z" }, | ||
| 15 | { url = "https://files.pythonhosted.org/packages/71/7c/32d4ddad53375de42f3e972bb0633ec76f2c31772f2e508479d4788651d9/atomicwriter-0.2.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b925e55750092fd482565b6068b8c8366fd79de526681af9e58eb209f0deeca", size = 323775, upload-time = "2025-05-24T20:34:37.968Z" }, | ||
| 16 | { url = "https://files.pythonhosted.org/packages/06/fe/6a226368a3f7ea30001fbd165f6a97f28c8f1a884896357b3d694983f5d2/atomicwriter-0.2.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:538f78f25e01584535782397211c66b8b3c9de90c2d1fc01a668ddce73dd0cb2", size = 340819, upload-time = "2025-05-24T20:34:39.63Z" }, | ||
| 17 | { url = "https://files.pythonhosted.org/packages/92/95/b035b2296c483fde5392c629e0b6e3844eba6e54ea965c4b8827379b0893/atomicwriter-0.2.5-cp312-cp312-win_amd64.whl", hash = "sha256:1d2d49a1b94ea7b289be9f7134d756bfb0bbf53eb0e58411334ed1b9958abe5e", size = 152789, upload-time = "2025-05-24T20:34:40.905Z" }, | ||
| 18 | { url = "https://files.pythonhosted.org/packages/da/25/caa0959ae8ce24763e24e1f45be6cb897414545d224a155f929d496d6812/atomicwriter-0.2.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f5490fd5bec378509521f7c2a19a64031a0de07d368d76733c3f76a0b9f026b", size = 233830, upload-time = "2025-05-24T20:34:42.532Z" }, | ||
| 19 | { url = "https://files.pythonhosted.org/packages/d2/76/3c41bfd4fd74bc63bec29f05a806a767258eea7cf151496b4ab015cb5323/atomicwriter-0.2.5-cp313-cp313-macosx_11_0_x86_64.whl", hash = "sha256:a4dada83ff1255c7e640363cc2a4399ab9a822d4dbc9c18f55bbf0c8b12ce056", size = 245461, upload-time = "2025-05-24T20:34:44.454Z" }, | ||
| 20 | { url = "https://files.pythonhosted.org/packages/c3/1e/5512dbdfdc3f4ab12f5923c50ae4765cc2fc65a9f112bb9dccbcbe60b395/atomicwriter-0.2.5-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:ef2cf15e67513f05ad37d4cec48e403982c6b3c07f491472effd76d2157de7e2", size = 259892, upload-time = "2025-05-24T20:34:45.688Z" }, | ||
| 21 | { url = "https://files.pythonhosted.org/packages/e5/1d/2382b6cacb119115828eb519697a555900bcfdb062efeb0f82603295402d/atomicwriter-0.2.5-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:73618f74c3c5f5401d3da0a3cd3043f23de5b6bb4a3d85bc580940a441355d25", size = 263125, upload-time = "2025-05-24T20:34:47.205Z" }, | ||
| 22 | { url = "https://files.pythonhosted.org/packages/07/d7/c4d68386161870db4a8d0452f0655a19902fa435b749c12e6ef800e89b19/atomicwriter-0.2.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbd5eda80710ddac7aefb421c79cef6b905852a827e764f0f12fcbaa88919f7a", size = 323503, upload-time = "2025-05-24T20:34:48.417Z" }, | ||
| 23 | { url = "https://files.pythonhosted.org/packages/b7/08/0fc03c0736ab8466e1b47a3ee17a528da18019cff93b7c4c2b33df82c19e/atomicwriter-0.2.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4776aaca40bc3040c3716c2adad74625c42285083ff31e8bf24a95315225c7b", size = 340156, upload-time = "2025-05-24T20:34:50.389Z" }, | ||
| 24 | { url = "https://files.pythonhosted.org/packages/fa/09/7ba888cf4d90bcabd9e82db3bdb9de50e4ef072e0ea0d375cd1931b79349/atomicwriter-0.2.5-cp313-cp313-win_amd64.whl", hash = "sha256:225ed1fbfa1996d9b0b2252f8a5d81263e51cbc797086d830f488c35b1d2ab42", size = 152274, upload-time = "2025-05-24T20:34:51.785Z" }, | ||
| 25 | { url = "https://files.pythonhosted.org/packages/2a/70/07d2ba2e0a126cfecfbfed46baf599c9e2155f4c8338fed4d3ae0041b133/atomicwriter-0.2.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:63b55982cfa47232f179689933bf003eefb2bd33464235883ed3ce7322cf38f3", size = 232879, upload-time = "2025-05-24T20:34:53.195Z" }, | ||
| 26 | { url = "https://files.pythonhosted.org/packages/f6/4d/397eb5435917135df93b339d849884bb1125896b1e15163c5244aa590336/atomicwriter-0.2.5-cp313-cp313t-macosx_11_0_x86_64.whl", hash = "sha256:e33f40b2a27f8831beeabb485923acb6dd067cc70bba1a63096749b3dc4747ff", size = 244386, upload-time = "2025-05-24T20:34:54.852Z" }, | ||
| 27 | { url = "https://files.pythonhosted.org/packages/8b/01/73f0b683fa55e61dd29d30e48e9a75ddb049e6dad0ac4ae1a29dbc05f21e/atomicwriter-0.2.5-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:c646e115e88147d71f845a005fc53910f22c4dc65bd634768cb90b7f34259359", size = 258255, upload-time = "2025-05-24T20:34:56.046Z" }, | ||
| 28 | { url = "https://files.pythonhosted.org/packages/4b/19/692387c1fb1b8714a9b2fab99a58850fd4136bed988814c8ff74d0c8de02/atomicwriter-0.2.5-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:47f974e986ff6514351c3ea75041009a514be0c34c225c062b0ad8a28ec9c0a3", size = 261768, upload-time = "2025-05-24T20:34:57.795Z" }, | ||
| 29 | { url = "https://files.pythonhosted.org/packages/3e/f2/4d466f52ee635cc54011713272f302584c6d1ce612c331d9989fa6fa672f/atomicwriter-0.2.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e1db8b9004cd3f628166e83b25eb814b82345f9d6bc15e99b6d201c355455b45", size = 321975, upload-time = "2025-05-24T20:34:59.45Z" }, | ||
| 30 | { url = "https://files.pythonhosted.org/packages/84/ad/0189ad9783ca6609df47e06cc0cd22866a8073d46478f59c6ab3ec13e0fb/atomicwriter-0.2.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a7da4a114121ab865663578b801a0520b2b518d4591af0bd294f6aac0dad243b", size = 338946, upload-time = "2025-05-24T20:35:01.501Z" }, | ||
| 31 | { url = "https://files.pythonhosted.org/packages/94/79/2c4d8f75eeb09192cf572957f031271998f3c985fabd79d513fff66ac715/atomicwriter-0.2.5-cp313-cp313t-win_amd64.whl", hash = "sha256:7aab4b3956cc17219e7e4da76e8a1bceb3d3aeaf03234f89b90e234a2adcf27b", size = 151571, upload-time = "2025-05-24T20:35:02.747Z" }, | ||
| 32 | { url = "https://files.pythonhosted.org/packages/32/19/d6a686d189c3577e7f08b33df398b959c24bf74b3cec34359104db1a24ff/atomicwriter-0.2.5-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:8d0fccac2dfe5d884d97edbda28be9c16d55faee9bdf66f53a99384ac387cc43", size = 239320, upload-time = "2025-05-24T20:35:04.028Z" }, | ||
| 33 | { url = "https://files.pythonhosted.org/packages/8e/35/35571a4eed57816c3b5fdbefcb15f38563fbe4f3a4a7d1588c8ef899afaf/atomicwriter-0.2.5-cp39-abi3-macosx_11_0_x86_64.whl", hash = "sha256:6583c24333508839db2156d895cbbb5cd3ff20d4f9c698e341435e5b35990eaa", size = 250818, upload-time = "2025-05-24T20:35:05.21Z" }, | ||
| 34 | { url = "https://files.pythonhosted.org/packages/81/d9/145093630bc25f115a49d32d9ef66745f5cdef787492d77fd27e74d20389/atomicwriter-0.2.5-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:136a9902ae3f1c0cb262a07dd3ac85069d71f8b11347cd740030567e67d611aa", size = 265796, upload-time = "2025-05-24T20:35:06.388Z" }, | ||
| 35 | { url = "https://files.pythonhosted.org/packages/58/32/d1881adade2ebc70aa9dbb61cadabc2c00cfa99a7a5d6ba48f44e279056f/atomicwriter-0.2.5-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:0b6830434b6a49c19473c3f3975dfa0a87dec95bee81297f7393e378f9a0b82f", size = 269378, upload-time = "2025-05-24T20:35:07.578Z" }, | ||
| 36 | { url = "https://files.pythonhosted.org/packages/93/f5/2661ea763784a4991c4c7be5c932a468937bd1d4618b833a63ec638a3b76/atomicwriter-0.2.5-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:53095a01891a2901aa04c10c8de52c0ba41e0d8a4a1893318cf34ccbdbde00b7", size = 328167, upload-time = "2025-05-24T20:35:08.764Z" }, | ||
| 37 | { url = "https://files.pythonhosted.org/packages/ec/bc/e3aa521671a589bee9662d3e2108e4835a5d80e6da76e4d05d98d1c78005/atomicwriter-0.2.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ecf4dc3983bb1f28b21cb09c2d96b6936d8864c559dcf151b57813cb1eae998b", size = 347153, upload-time = "2025-05-24T20:35:10.507Z" }, | ||
| 38 | { url = "https://files.pythonhosted.org/packages/59/b7/e190383e7240b1f247c6df9bc6667db8df10190cd0bb2dba8ea6bd704ea4/atomicwriter-0.2.5-cp39-abi3-win_amd64.whl", hash = "sha256:92cff264a20364301ab341b332fd0112866870b8cb35caf99a3f3fee0e6c19e8", size = 156374, upload-time = "2025-05-24T20:35:11.716Z" }, | ||
| 39 | ] | ||
| 40 | |||
| 41 | [[package]] | ||
| 42 | name = "certifi" | ||
| 43 | version = "2025.1.31" | ||
| 44 | source = { registry = "https://pypi.org/simple" } | ||
| 45 | sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577, upload-time = "2025-01-31T02:16:47.166Z" } | ||
| 46 | wheels = [ | ||
| 47 | { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393, upload-time = "2025-01-31T02:16:45.015Z" }, | ||
| 48 | ] | ||
| 49 | |||
| 50 | [[package]] | ||
| 51 | name = "charset-normalizer" | ||
| 52 | version = "3.4.1" | ||
| 53 | source = { registry = "https://pypi.org/simple" } | ||
| 54 | sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188, upload-time = "2024-12-24T18:12:35.43Z" } | ||
| 55 | wheels = [ | ||
| 56 | { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105, upload-time = "2024-12-24T18:10:38.83Z" }, | ||
| 57 | { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404, upload-time = "2024-12-24T18:10:44.272Z" }, | ||
| 58 | { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423, upload-time = "2024-12-24T18:10:45.492Z" }, | ||
| 59 | { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184, upload-time = "2024-12-24T18:10:47.898Z" }, | ||
| 60 | { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268, upload-time = "2024-12-24T18:10:50.589Z" }, | ||
| 61 | { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601, upload-time = "2024-12-24T18:10:52.541Z" }, | ||
| 62 | { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098, upload-time = "2024-12-24T18:10:53.789Z" }, | ||
| 63 | { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520, upload-time = "2024-12-24T18:10:55.048Z" }, | ||
| 64 | { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852, upload-time = "2024-12-24T18:10:57.647Z" }, | ||
| 65 | { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488, upload-time = "2024-12-24T18:10:59.43Z" }, | ||
| 66 | { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192, upload-time = "2024-12-24T18:11:00.676Z" }, | ||
| 67 | { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550, upload-time = "2024-12-24T18:11:01.952Z" }, | ||
| 68 | { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785, upload-time = "2024-12-24T18:11:03.142Z" }, | ||
| 69 | { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698, upload-time = "2024-12-24T18:11:05.834Z" }, | ||
| 70 | { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162, upload-time = "2024-12-24T18:11:07.064Z" }, | ||
| 71 | { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263, upload-time = "2024-12-24T18:11:08.374Z" }, | ||
| 72 | { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966, upload-time = "2024-12-24T18:11:09.831Z" }, | ||
| 73 | { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992, upload-time = "2024-12-24T18:11:12.03Z" }, | ||
| 74 | { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162, upload-time = "2024-12-24T18:11:13.372Z" }, | ||
| 75 | { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972, upload-time = "2024-12-24T18:11:14.628Z" }, | ||
| 76 | { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095, upload-time = "2024-12-24T18:11:17.672Z" }, | ||
| 77 | { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668, upload-time = "2024-12-24T18:11:18.989Z" }, | ||
| 78 | { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073, upload-time = "2024-12-24T18:11:21.507Z" }, | ||
| 79 | { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732, upload-time = "2024-12-24T18:11:22.774Z" }, | ||
| 80 | { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391, upload-time = "2024-12-24T18:11:24.139Z" }, | ||
| 81 | { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702, upload-time = "2024-12-24T18:11:26.535Z" }, | ||
| 82 | { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767, upload-time = "2024-12-24T18:12:32.852Z" }, | ||
| 83 | ] | ||
| 84 | |||
| 85 | [[package]] | ||
| 86 | name = "dbus-next" | ||
| 87 | version = "0.2.3" | ||
| 88 | source = { registry = "https://pypi.org/simple" } | ||
| 89 | sdist = { url = "https://files.pythonhosted.org/packages/ce/45/6a40fbe886d60a8c26f480e7d12535502b5ba123814b3b9a0b002ebca198/dbus_next-0.2.3.tar.gz", hash = "sha256:f4eae26909332ada528c0a3549dda8d4f088f9b365153952a408e28023a626a5", size = 71112, upload-time = "2021-07-25T22:11:28.398Z" } | ||
| 90 | wheels = [ | ||
| 91 | { url = "https://files.pythonhosted.org/packages/d2/fc/c0a3f4c4eaa5a22fbef91713474666e13d0ea2a69c84532579490a9f2cc8/dbus_next-0.2.3-py3-none-any.whl", hash = "sha256:58948f9aff9db08316734c0be2a120f6dc502124d9642f55e90ac82ffb16a18b", size = 57885, upload-time = "2021-07-25T22:11:25.466Z" }, | ||
| 92 | ] | ||
| 93 | |||
| 94 | [[package]] | ||
| 95 | name = "desktop-notify" | ||
| 96 | version = "1.3.3" | ||
| 97 | source = { registry = "https://pypi.org/simple" } | ||
| 98 | dependencies = [ | ||
| 99 | { name = "dbus-next" }, | ||
| 100 | ] | ||
| 101 | sdist = { url = "https://files.pythonhosted.org/packages/7a/d8/7ae5779257f5f1aa0a2d50c02d70b29522bd414692f3d3bd18ef119fe82d/desktop-notify-1.3.3.tar.gz", hash = "sha256:62934ad1f72f292f9a3af5ffe45af32814af18c396c00369385540c72bf08077", size = 7828, upload-time = "2021-01-03T16:46:36.483Z" } | ||
| 102 | wheels = [ | ||
| 103 | { url = "https://files.pythonhosted.org/packages/0a/cd/a7e3bd0262f3e8a9272fd24d0193e24dad7cb4e4edd27da48e74b5523e59/desktop_notify-1.3.3-py3-none-any.whl", hash = "sha256:8ad7ecc3a9a603dd5fa3cdc11cc6265cfbc7f6df9d8ed240f4663f43ef0de37a", size = 9937, upload-time = "2021-01-03T16:46:35.157Z" }, | ||
| 104 | ] | ||
| 105 | |||
| 106 | [[package]] | ||
| 107 | name = "frozendict" | ||
| 108 | version = "2.4.6" | ||
| 109 | source = { registry = "https://pypi.org/simple" } | ||
| 110 | sdist = { url = "https://files.pythonhosted.org/packages/bb/59/19eb300ba28e7547538bdf603f1c6c34793240a90e1a7b61b65d8517e35e/frozendict-2.4.6.tar.gz", hash = "sha256:df7cd16470fbd26fc4969a208efadc46319334eb97def1ddf48919b351192b8e", size = 316416, upload-time = "2024-10-13T12:15:32.449Z" } | ||
| 111 | wheels = [ | ||
| 112 | { url = "https://files.pythonhosted.org/packages/04/13/d9839089b900fa7b479cce495d62110cddc4bd5630a04d8469916c0e79c5/frozendict-2.4.6-py311-none-any.whl", hash = "sha256:d065db6a44db2e2375c23eac816f1a022feb2fa98cbb50df44a9e83700accbea", size = 16148, upload-time = "2024-10-13T12:15:26.839Z" }, | ||
| 113 | { url = "https://files.pythonhosted.org/packages/ba/d0/d482c39cee2ab2978a892558cf130681d4574ea208e162da8958b31e9250/frozendict-2.4.6-py312-none-any.whl", hash = "sha256:49344abe90fb75f0f9fdefe6d4ef6d4894e640fadab71f11009d52ad97f370b9", size = 16146, upload-time = "2024-10-13T12:15:28.16Z" }, | ||
| 114 | { url = "https://files.pythonhosted.org/packages/a5/8e/b6bf6a0de482d7d7d7a2aaac8fdc4a4d0bb24a809f5ddd422aa7060eb3d2/frozendict-2.4.6-py313-none-any.whl", hash = "sha256:7134a2bb95d4a16556bb5f2b9736dceb6ea848fa5b6f3f6c2d6dba93b44b4757", size = 16146, upload-time = "2024-10-13T12:15:29.495Z" }, | ||
| 115 | ] | ||
| 116 | |||
| 117 | [[package]] | ||
| 118 | name = "idna" | ||
| 119 | version = "3.10" | ||
| 120 | source = { registry = "https://pypi.org/simple" } | ||
| 121 | sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490, upload-time = "2024-09-15T18:07:39.745Z" } | ||
| 122 | wheels = [ | ||
| 123 | { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442, upload-time = "2024-09-15T18:07:37.964Z" }, | ||
| 124 | ] | ||
| 125 | |||
| 126 | [[package]] | ||
| 127 | name = "jsonpickle" | ||
| 128 | version = "4.0.5" | ||
| 129 | source = { registry = "https://pypi.org/simple" } | ||
| 130 | sdist = { url = "https://files.pythonhosted.org/packages/d6/33/4bda317ab294722fcdfff8f63aab74af9fda3675a4652d984a101aa7587e/jsonpickle-4.0.5.tar.gz", hash = "sha256:f299818b39367c361b3f26bdba827d4249ab5d383cd93144d0f94b5417aacb35", size = 315661, upload-time = "2025-03-29T19:22:56.92Z" } | ||
| 131 | wheels = [ | ||
| 132 | { url = "https://files.pythonhosted.org/packages/dc/1b/0e79cf115e0f54f1e8f56effb6ffd2ef8f92e9c324d692ede660067f1bfe/jsonpickle-4.0.5-py3-none-any.whl", hash = "sha256:b4ac7d0a75ddcdfd93445737f1d36ff28768690d43e54bf5d0ddb1d915e580df", size = 46382, upload-time = "2025-03-29T19:22:54.252Z" }, | ||
| 133 | ] | ||
| 134 | |||
| 135 | [[package]] | ||
| 136 | name = "python-dateutil" | ||
| 137 | version = "2.9.0.post0" | ||
| 138 | source = { registry = "https://pypi.org/simple" } | ||
| 139 | dependencies = [ | ||
| 140 | { name = "six" }, | ||
| 141 | ] | ||
| 142 | sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } | ||
| 143 | wheels = [ | ||
| 144 | { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, | ||
| 145 | ] | ||
| 146 | |||
| 147 | [[package]] | ||
| 148 | name = "pyxdg" | ||
| 149 | version = "0.28" | ||
| 150 | source = { registry = "https://pypi.org/simple" } | ||
| 151 | sdist = { url = "https://files.pythonhosted.org/packages/b0/25/7998cd2dec731acbd438fbf91bc619603fc5188de0a9a17699a781840452/pyxdg-0.28.tar.gz", hash = "sha256:3267bb3074e934df202af2ee0868575484108581e6f3cb006af1da35395e88b4", size = 77776, upload-time = "2022-06-05T11:35:01Z" } | ||
| 152 | wheels = [ | ||
| 153 | { url = "https://files.pythonhosted.org/packages/e5/8d/cf41b66a8110670e3ad03dab9b759704eeed07fa96e90fdc0357b2ba70e2/pyxdg-0.28-py2.py3-none-any.whl", hash = "sha256:bdaf595999a0178ecea4052b7f4195569c1ff4d344567bccdc12dfdf02d545ab", size = 49520, upload-time = "2022-06-05T11:34:58.832Z" }, | ||
| 154 | ] | ||
| 155 | |||
| 156 | [[package]] | ||
| 157 | name = "requests" | ||
| 158 | version = "2.32.3" | ||
| 159 | source = { registry = "https://pypi.org/simple" } | ||
| 160 | dependencies = [ | ||
| 161 | { name = "certifi" }, | ||
| 162 | { name = "charset-normalizer" }, | ||
| 163 | { name = "idna" }, | ||
| 164 | { name = "urllib3" }, | ||
| 165 | ] | ||
| 166 | sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218, upload-time = "2024-05-29T15:37:49.536Z" } | ||
| 167 | wheels = [ | ||
| 168 | { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928, upload-time = "2024-05-29T15:37:47.027Z" }, | ||
| 169 | ] | ||
| 170 | |||
| 171 | [[package]] | ||
| 172 | name = "six" | ||
| 173 | version = "1.17.0" | ||
| 174 | source = { registry = "https://pypi.org/simple" } | ||
| 175 | sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } | ||
| 176 | wheels = [ | ||
| 177 | { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, | ||
| 178 | ] | ||
| 179 | |||
| 180 | [[package]] | ||
| 181 | name = "tabulate" | ||
| 182 | version = "0.9.0" | ||
| 183 | source = { registry = "https://pypi.org/simple" } | ||
| 184 | sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090, upload-time = "2022-10-06T17:21:48.54Z" } | ||
| 185 | wheels = [ | ||
| 186 | { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252, upload-time = "2022-10-06T17:21:44.262Z" }, | ||
| 187 | ] | ||
| 188 | |||
| 189 | [[package]] | ||
| 190 | name = "toml" | ||
| 191 | version = "0.10.2" | ||
| 192 | source = { registry = "https://pypi.org/simple" } | ||
| 193 | sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253, upload-time = "2020-11-01T01:40:22.204Z" } | ||
| 194 | wheels = [ | ||
| 195 | { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588, upload-time = "2020-11-01T01:40:20.672Z" }, | ||
| 196 | ] | ||
| 197 | |||
| 198 | [[package]] | ||
| 199 | name = "uritools" | ||
| 200 | version = "4.0.3" | ||
| 201 | source = { registry = "https://pypi.org/simple" } | ||
| 202 | sdist = { url = "https://files.pythonhosted.org/packages/d3/43/4182fb2a03145e6d38698e38b49114ce59bc8c79063452eb585a58f8ce78/uritools-4.0.3.tar.gz", hash = "sha256:ee06a182a9c849464ce9d5fa917539aacc8edd2a4924d1b7aabeeecabcae3bc2", size = 24184, upload-time = "2024-05-28T18:07:45.194Z" } | ||
| 203 | wheels = [ | ||
| 204 | { url = "https://files.pythonhosted.org/packages/e6/17/5a4510d9ca9cc8be217ce359eb54e693dca81cf4d442308b282d5131b17d/uritools-4.0.3-py3-none-any.whl", hash = "sha256:bae297d090e69a0451130ffba6f2f1c9477244aa0a5543d66aed2d9f77d0dd9c", size = 10304, upload-time = "2024-05-28T18:07:42.731Z" }, | ||
| 205 | ] | ||
| 206 | |||
| 207 | [[package]] | ||
| 208 | name = "urllib3" | ||
| 209 | version = "2.3.0" | ||
| 210 | source = { registry = "https://pypi.org/simple" } | ||
| 211 | sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268, upload-time = "2024-12-22T07:47:30.032Z" } | ||
| 212 | wheels = [ | ||
| 213 | { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369, upload-time = "2024-12-22T07:47:28.074Z" }, | ||
| 214 | ] | ||
| 215 | |||
| 216 | [[package]] | ||
| 217 | name = "worktime" | ||
| 218 | version = "1.0.0" | ||
| 219 | source = { editable = "." } | ||
| 220 | dependencies = [ | ||
| 221 | { name = "atomicwriter" }, | ||
| 222 | { name = "desktop-notify" }, | ||
| 223 | { name = "frozendict" }, | ||
| 224 | { name = "jsonpickle" }, | ||
| 225 | { name = "python-dateutil" }, | ||
| 226 | { name = "pyxdg" }, | ||
| 227 | { name = "requests" }, | ||
| 228 | { name = "tabulate" }, | ||
| 229 | { name = "toml" }, | ||
| 230 | { name = "uritools" }, | ||
| 231 | ] | ||
| 232 | |||
| 233 | [package.metadata] | ||
| 234 | requires-dist = [ | ||
| 235 | { name = "atomicwriter", specifier = ">=0.2.5" }, | ||
| 236 | { name = "desktop-notify", specifier = ">=1.3.3" }, | ||
| 237 | { name = "frozendict", specifier = ">=2.4.6" }, | ||
| 238 | { name = "jsonpickle", specifier = ">=4.0.5,<5" }, | ||
| 239 | { name = "python-dateutil", specifier = ">=2.9.0.post0,<3" }, | ||
| 240 | { name = "pyxdg", specifier = ">=0.28,<0.29" }, | ||
| 241 | { name = "requests", specifier = ">=2.32.3,<3" }, | ||
| 242 | { name = "tabulate", specifier = ">=0.9.0,<0.10" }, | ||
| 243 | { name = "toml", specifier = ">=0.10.2,<0.11" }, | ||
| 244 | { name = "uritools", specifier = ">=4.0.3,<5" }, | ||
| 245 | ] | ||
| 246 | |||
| 247 | [package.metadata.requires-dev] | ||
| 248 | dev = [] | ||
diff --git a/overlays/worktime/worktime/__main__.py b/overlays/worktime/worktime/__main__.py index 4eee5dc2..ffeb1b84 100755 --- a/overlays/worktime/worktime/__main__.py +++ b/overlays/worktime/worktime/__main__.py | |||
| @@ -1,10 +1,12 @@ | |||
| 1 | import requests | 1 | import requests |
| 2 | from requests.exceptions import HTTPError | 2 | from requests.exceptions import HTTPError |
| 3 | from requests.auth import HTTPBasicAuth | 3 | from requests.auth import HTTPBasicAuth |
| 4 | from requests.adapters import HTTPAdapter, Retry | ||
| 4 | from datetime import * | 5 | from datetime import * |
| 5 | from xdg import BaseDirectory | 6 | from xdg import BaseDirectory |
| 6 | import toml | 7 | import toml |
| 7 | from uritools import (uricompose) | 8 | from uritools import uricompose |
| 9 | from urllib.parse import urljoin | ||
| 8 | 10 | ||
| 9 | from inspect import signature | 11 | from inspect import signature |
| 10 | 12 | ||
| @@ -27,77 +29,76 @@ from sys import stderr, stdout | |||
| 27 | 29 | ||
| 28 | from tabulate import tabulate | 30 | from tabulate import tabulate |
| 29 | 31 | ||
| 30 | from itertools import groupby, count | 32 | from itertools import groupby, count, islice |
| 31 | from functools import cache, partial | 33 | from functools import cache, partial |
| 32 | 34 | ||
| 33 | import backoff | ||
| 34 | |||
| 35 | from pathlib import Path | 35 | from pathlib import Path |
| 36 | 36 | ||
| 37 | from collections import defaultdict | 37 | from collections import defaultdict |
| 38 | from collections.abc import Iterable, Generator | ||
| 39 | from typing import Any | ||
| 38 | 40 | ||
| 39 | import jsonpickle | 41 | import jsonpickle |
| 40 | from hashlib import blake2s | 42 | from hashlib import blake2s |
| 41 | import json | 43 | import json |
| 42 | 44 | ||
| 43 | class TogglAPISection(Enum): | 45 | import asyncio |
| 44 | TOGGL = '/api/v9' | 46 | |
| 45 | REPORTS = '/reports/api/v2' | 47 | from frozendict import frozendict |
| 46 | 48 | from contextlib import closing | |
| 47 | class TogglAPIError(Exception): | 49 | import os |
| 48 | def __init__(self, response, *, http_error=None): | 50 | from time import clock_gettime_ns, CLOCK_MONOTONIC |
| 49 | self.http_error = http_error | 51 | from atomicwriter import AtomicWriter |
| 50 | self.response = response | 52 | import desktop_notify.aio as notify |
| 51 | 53 | ||
| 52 | def __str__(self): | 54 | class BearerAuth(requests.auth.AuthBase): |
| 53 | if not self.http_error is None: | 55 | def __init__(self, token): |
| 54 | return str(self.http_error) | 56 | self.token = token |
| 55 | else: | 57 | def __call__(self, r): |
| 56 | return self.response.text | 58 | r.headers["authorization"] = "Bearer " + self.token |
| 57 | 59 | return r | |
| 58 | class TogglAPI(object): | 60 | |
| 59 | def __init__(self, api_token, workspace_id, client_ids): | 61 | class KimaiSession(requests.Session): |
| 60 | self._api_token = api_token | 62 | def __init__(self, base_url: str, api_token: str): |
| 61 | self._workspace_id = workspace_id | 63 | super().__init__() |
| 62 | self._client_ids = set(map(int, client_ids.split(','))) if client_ids else None | 64 | self.base_url = base_url |
| 63 | 65 | self.auth = BearerAuth(api_token) | |
| 64 | def _make_url(self, api=TogglAPISection.TOGGL, section=['me', 'time_entries', 'current'], params={}): | 66 | retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504]) |
| 65 | if api is TogglAPISection.REPORTS: | 67 | super().mount(base_url, HTTPAdapter(max_retries=retries)) |
| 66 | params.update({'user_agent': 'worktime', 'workspace_id': self._workspace_id}) | 68 | |
| 67 | 69 | def request(self, method, url, *args, **kwargs): | |
| 68 | api_path = api.value | 70 | joined_url = urljoin(self.base_url, url) |
| 69 | section_path = '/'.join(section) | 71 | return super().request(method, joined_url, *args, headers = {'Accept': 'application/json'} | (kwargs['headers'] if 'headers' in kwargs else {}), **{k: v for k, v in kwargs.items() if k not in ['headers']}) |
| 70 | uri = uricompose(scheme='https', host='api.track.toggl.com', path=f"{api_path}/{section_path}", query=params) | 72 | |
| 71 | 73 | class KimaiAPI(object): | |
| 72 | return uri | 74 | def __init__(self, base_url: str, api_token: str, clients: Iterable[str]): |
| 73 | 75 | self._session = KimaiSession(base_url, api_token) | |
| 74 | def _query(self, url, method): | 76 | self._kimai_clients = self._session.get('/api/customers').json() |
| 75 | response = self._raw_query(url, method) | 77 | self._client_ids = self.resolve_clients(clients) |
| 76 | response.raise_for_status() | 78 | kimai_user = self._session.get('/api/users/me').json() |
| 77 | return response | 79 | self._tz = gettz(kimai_user['timezone']) |
| 78 | 80 | ||
| 79 | @backoff.on_predicate( | 81 | def resolve_clients(self, clients: Iterable[str]) -> frozenset[int]: |
| 80 | backoff.expo, | 82 | return frozenset({ client['id'] for client in self._kimai_clients if client['name'] in clients }) |
| 81 | factor=0.1, max_value=2, | 83 | |
| 82 | predicate=lambda r: r.status_code == 429, | 84 | def render_datetime(self, datetime: datetime) -> str: |
| 83 | max_time=10, | 85 | return datetime.astimezone(self._tz).strftime('%Y-%m-%dT%H:%M:%S') |
| 84 | ) | 86 | |
| 85 | def _raw_query(self, url, method): | 87 | def get_timesheets(self, params: dict[str, Any] = {}) -> Generator[Any]: |
| 86 | headers = {'content-type': 'application/json', 'accept': 'application/json'} | 88 | for page in count(start=1): |
| 87 | response = None | 89 | resp = self._session.get('/api/timesheets', params=params | {'size': 100, 'page': page}) |
| 88 | 90 | if resp.status_code == 404: | |
| 89 | if method == 'GET': | 91 | break |
| 90 | response = requests.get(url, headers=headers, auth=HTTPBasicAuth(self._api_token, 'api_token')) | 92 | yield from resp.json() |
| 91 | elif method == 'POST': | ||
| 92 | response = requests.post(url, headers=headers, auth=HTTPBasicAuth(self._api_token, 'api_token')) | ||
| 93 | else: | ||
| 94 | raise ValueError(f"Undefined HTTP method “{method}”") | ||
| 95 | |||
| 96 | return response | ||
| 97 | 93 | ||
| 98 | def entry_durations(self, start_date, *, end_date, rounding=False, client_ids): | 94 | def entry_durations(self, start_date: datetime, *, end_date: datetime, clients: Iterable[str] | None = None) -> Generator[timedelta]: |
| 99 | if client_ids is not None and not client_ids: | 95 | client_ids = None |
| 96 | if clients is not None and not clients: | ||
| 100 | return | 97 | return |
| 98 | elif clients is None: | ||
| 99 | client_ids = self._client_ids | ||
| 100 | else: | ||
| 101 | client_ids = self.resolve_clients(clients) | ||
| 101 | 102 | ||
| 102 | cache_dir = Path(BaseDirectory.save_cache_path('worktime')) / 'entry_durations' | 103 | cache_dir = Path(BaseDirectory.save_cache_path('worktime')) / 'entry_durations' |
| 103 | step = timedelta(days = 120) | 104 | step = timedelta(days = 120) |
| @@ -116,11 +117,8 @@ class TogglAPI(object): | |||
| 116 | cache_key = blake2s(jsonpickle.encode({ | 117 | cache_key = blake2s(jsonpickle.encode({ |
| 117 | 'start': req_start, | 118 | 'start': req_start, |
| 118 | 'end': req_end, | 119 | 'end': req_end, |
| 119 | 'rounding': rounding, | 120 | 'client_ids': client_ids, |
| 120 | 'clients': client_ids, | 121 | }).encode('utf-8'), key = self._session.auth.token.encode('utf-8')).hexdigest() |
| 121 | 'workspace': self._workspace_id, | ||
| 122 | 'workspace_clients': self._client_ids | ||
| 123 | }).encode('utf-8'), key = self._api_token.encode('utf-8')).hexdigest() | ||
| 124 | cache_path = cache_dir / cache_key[:2] / cache_key[2:4] / f'{cache_key[4:]}.json' | 122 | cache_path = cache_dir / cache_key[:2] / cache_key[2:4] / f'{cache_key[4:]}.json' |
| 125 | try: | 123 | try: |
| 126 | with cache_path.open('r', encoding='utf-8') as ch: | 124 | with cache_path.open('r', encoding='utf-8') as ch: |
| @@ -130,85 +128,83 @@ class TogglAPI(object): | |||
| 130 | pass | 128 | pass |
| 131 | 129 | ||
| 132 | entries = list() | 130 | entries = list() |
| 133 | params = { 'since': (req_start - timedelta(days=1)).date().isoformat(), | 131 | params = { |
| 134 | 'until': (req_end + timedelta(days=1)).date().isoformat(), | 132 | 'begin': self.render_datetime(req_start), |
| 135 | 'rounding': 'yes' if rounding else 'no', | 133 | 'end': self.render_datetime(req_end), |
| 136 | 'billable': 'yes' | 134 | 'customers[]': list(client_ids), |
| 137 | } | 135 | 'billable': 1, |
| 138 | if client_ids is not None: | 136 | } |
| 139 | params |= { 'client_ids': ','.join(map(str, client_ids)) } | 137 | |
| 140 | for page in count(start = 1): | 138 | for entry in self.get_timesheets(params): |
| 141 | url = self._make_url(api = TogglAPISection.REPORTS, section = ['details'], params = params | { 'page': page }) | 139 | if entry['end'] is None: |
| 142 | r = self._query(url = url, method='GET') | 140 | continue |
| 143 | if not r or not r.json(): | ||
| 144 | raise TogglAPIError(r) | ||
| 145 | report = r.json() | ||
| 146 | for entry in report['data']: | ||
| 147 | start = isoparse(entry['start']) | ||
| 148 | end = isoparse(entry['end']) | ||
| 149 | |||
| 150 | if start > req_end or end < req_start: | ||
| 151 | continue | ||
| 152 | 141 | ||
| 153 | x = min(end, req_end) - max(start, req_start) | 142 | start = isoparse(entry['begin']) |
| 154 | if cache_key: | 143 | end = isoparse(entry['end']) |
| 155 | entries.append(x) | 144 | |
| 156 | yield x | 145 | if start > req_end or end < req_start: |
| 157 | if not report['data']: | 146 | continue |
| 158 | break | 147 | |
| 148 | x = min(end, req_end) - max(start, req_start) | ||
| 149 | if cache_key: | ||
| 150 | entries.append(x) | ||
| 151 | yield x | ||
| 159 | 152 | ||
| 160 | if cache_path: | 153 | if cache_path: |
| 161 | cache_path.parent.mkdir(parents=True, exist_ok=True) | 154 | cache_path.parent.mkdir(parents=True, exist_ok=True) |
| 162 | with cache_path.open('w', encoding='utf-8') as ch: | 155 | with cache_path.open('w', encoding='utf-8') as ch: |
| 163 | ch.write(jsonpickle.encode(entries)) | 156 | ch.write(jsonpickle.encode(entries)) |
| 164 | # res = timedelta(milliseconds=report['total_billable']) if report['total_billable'] else timedelta(milliseconds=0) | ||
| 165 | # return res | ||
| 166 | |||
| 167 | def get_billable_hours(self, start_date, end_date=datetime.now(timezone.utc), rounding=False): | ||
| 168 | billable_acc = timedelta(milliseconds = 0) | ||
| 169 | if 0 in self._client_ids: | ||
| 170 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['workspaces', self._workspace_id, 'clients']) | ||
| 171 | r = self._query(url = url, method = 'GET') | ||
| 172 | if not r or not r.json(): | ||
| 173 | raise TogglAPIError(r) | ||
| 174 | |||
| 175 | billable_acc += sum(self.entry_durations(start_date, end_date=end_date, rounding=rounding, client_ids=None), start=timedelta(milliseconds=0)) - sum(self.entry_durations(start_date, end_date=end_date, rounding=rounding, client_ids=frozenset(map(lambda c: c['id'], r.json()))), start=timedelta(milliseconds=0)) | ||
| 176 | 157 | ||
| 177 | billable_acc += sum(self.entry_durations(start_date, end_date=end_date, rounding=rounding, client_ids=frozenset(*(self._client_ids - {0}))), start=timedelta(milliseconds=0)) | 158 | def get_billable_hours(self, start_date: datetime, end_date: datetime = datetime.now(timezone.utc)) -> timedelta: |
| 159 | return sum(self.entry_durations(start_date, end_date=end_date), start=timedelta(milliseconds=0)) | ||
| 178 | 160 | ||
| 179 | return billable_acc | 161 | def get_running_entry(self) -> Any | None: |
| 162 | kimai_entries = self._session.get('/api/timesheets/active').json() | ||
| 163 | if not kimai_entries: | ||
| 164 | return None | ||
| 165 | entry = kimai_entries[0] | ||
| 180 | 166 | ||
| 181 | def get_running_clock(self, now=datetime.now(timezone.utc)): | 167 | if entry['project']['customer']['id'] not in self._client_ids: |
| 182 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['me', 'time_entries', 'current']) | 168 | return None |
| 183 | r = self._query(url = url, method='GET') | ||
| 184 | 169 | ||
| 185 | if not r or (not r.json() and r.json() is not None): | 170 | return entry |
| 186 | raise TogglAPIError(r) | ||
| 187 | 171 | ||
| 188 | if not r.json() or not r.json()['billable']: | 172 | def get_running_clock(self, now: datetime = datetime.now(timezone.utc)) -> timedelta | None: |
| 173 | entry = self.get_running_entry() | ||
| 174 | if not entry: | ||
| 189 | return None | 175 | return None |
| 176 | start = isoparse(entry['begin']) | ||
| 177 | return now - start if start <= now else None | ||
| 190 | 178 | ||
| 191 | if self._client_ids is not None: | 179 | def get_recent_entries(self) -> Generator[Any]: |
| 192 | if 'pid' in r.json() and r.json()['pid']: | 180 | step = timedelta(days = 7) |
| 193 | url = self._make_url(api = TogglAPISection.TOGGL, section = ['projects', str(r.json()['pid'])]) | 181 | now = datetime.now().astimezone(timezone.utc) |
| 194 | pr = self._query(url = url, method = 'GET') | 182 | ids = set() |
| 195 | if not pr or not pr.json(): | 183 | for req_end in (now - step * i for i in count()): |
| 196 | raise TogglAPIError(pr) | 184 | params = { |
| 185 | 'begin': self.render_datetime(req_end - step), | ||
| 186 | 'end': self.render_datetime(req_end), | ||
| 187 | 'full': 'true', | ||
| 188 | } | ||
| 189 | for entry in self.get_timesheets(params): | ||
| 190 | if entry['id'] in ids: | ||
| 191 | continue | ||
| 192 | ids.add(entry['id']) | ||
| 193 | yield entry | ||
| 197 | 194 | ||
| 198 | if not pr.json(): | 195 | def start_clock(self, project_id: int, activity_id: int, description: str | None = None, tags: Iterable[str] | None = None, billable: bool = True): |
| 199 | return None | 196 | self._session.post('/api/timesheets', json={ |
| 197 | 'begin': self.render_datetime(datetime.now()), | ||
| 198 | 'project': project_id, | ||
| 199 | 'activity': activity_id, | ||
| 200 | 'description': description if description else '', | ||
| 201 | 'tags': (','.join(tags)) if tags else '', | ||
| 202 | 'billable': billable, | ||
| 203 | }).raise_for_status() | ||
| 200 | 204 | ||
| 201 | if 'cid' in pr.json() and pr.json()['cid']: | 205 | def stop_clock(self, running_id: int): |
| 202 | if pr.json()['cid'] not in self._client_ids: | 206 | self._session.patch(f'/api/timesheets/{running_id}/stop').raise_for_status() |
| 203 | return None | ||
| 204 | elif 0 not in self._client_ids: | ||
| 205 | return None | ||
| 206 | elif 0 not in self._client_ids: | ||
| 207 | return None | ||
| 208 | 207 | ||
| 209 | start = isoparse(r.json()['start']) | ||
| 210 | |||
| 211 | return now - start if start <= now else None | ||
| 212 | 208 | ||
| 213 | class Worktime(object): | 209 | class Worktime(object): |
| 214 | time_worked = timedelta() | 210 | time_worked = timedelta() |
| @@ -281,10 +277,10 @@ class Worktime(object): | |||
| 281 | 277 | ||
| 282 | config = Worktime.config() | 278 | config = Worktime.config() |
| 283 | config_dir = BaseDirectory.load_first_config('worktime') | 279 | config_dir = BaseDirectory.load_first_config('worktime') |
| 284 | api = TogglAPI( | 280 | api = KimaiAPI( |
| 285 | api_token=config.get("TOGGL", {}).get("ApiToken", None), | 281 | base_url=config.get("KIMAI", {}).get("BaseUrl", None), |
| 286 | workspace_id=config.get("TOGGL", {}).get("Workspace", None), | 282 | api_token=config.get("KIMAI", {}).get("ApiToken", None), |
| 287 | client_ids=config.get("TOGGL", {}).get("ClientIds", None) | 283 | clients=config.get("KIMAI", {}).get("Clients", None) |
| 288 | ) | 284 | ) |
| 289 | date_format = config.get("WORKTIME", {}).get("DateFormat", '%Y-%m-%d') | 285 | date_format = config.get("WORKTIME", {}).get("DateFormat", '%Y-%m-%d') |
| 290 | 286 | ||
| @@ -379,10 +375,7 @@ class Worktime(object): | |||
| 379 | parse_datestr(stripped_line) | 375 | parse_datestr(stripped_line) |
| 380 | 376 | ||
| 381 | for day in [fromDay + timedelta(days = x) for x in range(0, (toDay - fromDay).days + 1)]: | 377 | for day in [fromDay + timedelta(days = x) for x in range(0, (toDay - fromDay).days + 1)]: |
| 382 | if self.end_date.date() < day or day < self.start_date.date(): | 378 | if self.would_be_workday(day) and self.start_date.date() <= day and day <= self.end_date.date(): |
| 383 | continue | ||
| 384 | |||
| 385 | if self.would_be_workday(day): | ||
| 386 | if excused_kind == 'leave': | 379 | if excused_kind == 'leave': |
| 387 | self.leave_days.add(day) | 380 | self.leave_days.add(day) |
| 388 | elif time is not None and time >= self.time_per_day(day): | 381 | elif time is not None and time >= self.time_per_day(day): |
| @@ -397,9 +390,29 @@ class Worktime(object): | |||
| 397 | start_day = self.start_date.date() | 390 | start_day = self.start_date.date() |
| 398 | end_day = self.end_date.date() | 391 | end_day = self.end_date.date() |
| 399 | 392 | ||
| 393 | self.extra_days_to_work = dict() | ||
| 394 | |||
| 400 | try: | 395 | try: |
| 401 | with open(Path(config_dir) / "pull-forward", 'r') as excused: | 396 | with open(Path(config_dir) / "days-to-work", 'r') as extra_days_to_work_file: |
| 402 | for line in excused: | 397 | for line in extra_days_to_work_file: |
| 398 | stripped_line = line.strip() | ||
| 399 | if stripped_line: | ||
| 400 | splitLine = stripped_line.split(' ') | ||
| 401 | if len(splitLine) == 2: | ||
| 402 | [hours, datestr] = splitLine | ||
| 403 | day = datetime.strptime(datestr, date_format).replace(tzinfo=tzlocal()).date() | ||
| 404 | self.extra_days_to_work[day] = timedelta(hours = float(hours)) | ||
| 405 | else: | ||
| 406 | day = datetime.strptime(stripped_line, date_format).replace(tzinfo=tzlocal()).date() | ||
| 407 | self.extra_days_to_work[day] = self.time_per_day(day) | ||
| 408 | except IOError as e: | ||
| 409 | if e.errno != 2: | ||
| 410 | raise e | ||
| 411 | |||
| 412 | |||
| 413 | try: | ||
| 414 | with open(Path(config_dir) / "pull-forward", 'r') as pull_forward: | ||
| 415 | for line in pull_forward: | ||
| 403 | stripped_line = line.strip() | 416 | stripped_line = line.strip() |
| 404 | if stripped_line: | 417 | if stripped_line: |
| 405 | [hours, datestr] = stripped_line.split(' ') | 418 | [hours, datestr] = stripped_line.split(' ') |
| @@ -420,15 +433,22 @@ class Worktime(object): | |||
| 420 | if not d == datetime.strptime(c, date_format).replace(tzinfo=tzlocal()).date(): break | 433 | if not d == datetime.strptime(c, date_format).replace(tzinfo=tzlocal()).date(): break |
| 421 | else: | 434 | else: |
| 422 | if d >= self.end_date.date(): | 435 | if d >= self.end_date.date(): |
| 423 | self.pull_forward[d] = min(timedelta(hours = float(hours)), self.time_per_day(d) - (holidays[d] if d in holidays else timedelta())) | 436 | time_for_day = self.time_per_day(d) if d.isoweekday() in self.workdays else timedelta() |
| 437 | if d in self.extra_days_to_work: | ||
| 438 | time_for_day += self.extra_days_to_work[d] | ||
| 439 | self.pull_forward[d] = min(timedelta(hours = float(hours)), time_for_day) | ||
| 424 | except IOError as e: | 440 | except IOError as e: |
| 425 | if e.errno != 2: | 441 | if e.errno != 2: |
| 426 | raise e | 442 | raise e |
| 427 | 443 | ||
| 444 | if self.pull_forward: | ||
| 445 | for year in range(self.end_date.year + 1, max(self.pull_forward.keys()).year + 1): | ||
| 446 | holidays |= {k: v * timedelta(hours = hours_per_week(k)) / len(self.workdays) for k, v in Worktime.holidays(year).items()} | ||
| 447 | |||
| 428 | self.days_to_work = dict() | 448 | self.days_to_work = dict() |
| 429 | 449 | ||
| 430 | if self.pull_forward: | 450 | # if self.pull_forward: |
| 431 | end_day = max(end_day, max(list(self.pull_forward))) | 451 | # end_day = max(end_day, max(self.pull_forward.keys())) |
| 432 | 452 | ||
| 433 | for day in [start_day + timedelta(days = x) for x in range(0, (end_day - start_day).days + 1)]: | 453 | for day in [start_day + timedelta(days = x) for x in range(0, (end_day - start_day).days + 1)]: |
| 434 | if day.isoweekday() in self.workdays: | 454 | if day.isoweekday() in self.workdays: |
| @@ -436,26 +456,6 @@ class Worktime(object): | |||
| 436 | if time_to_work > timedelta(): | 456 | if time_to_work > timedelta(): |
| 437 | self.days_to_work[day] = time_to_work | 457 | self.days_to_work[day] = time_to_work |
| 438 | 458 | ||
| 439 | self.extra_days_to_work = dict() | ||
| 440 | |||
| 441 | try: | ||
| 442 | with open(Path(config_dir) / "days-to-work", 'r') as extra_days_to_work_file: | ||
| 443 | for line in extra_days_to_work_file: | ||
| 444 | stripped_line = line.strip() | ||
| 445 | if stripped_line: | ||
| 446 | splitLine = stripped_line.split(' ') | ||
| 447 | if len(splitLine) == 2: | ||
| 448 | [hours, datestr] = splitLine | ||
| 449 | day = datetime.strptime(datestr, date_format).replace(tzinfo=tzlocal()).date() | ||
| 450 | self.extra_days_to_work[day] = timedelta(hours = float(hours)) | ||
| 451 | else: | ||
| 452 | day = datetime.strptime(stripped_line, date_format).replace(tzinfo=tzlocal()).date() | ||
| 453 | self.extra_days_to_work[day] = self.time_per_day(day) | ||
| 454 | except IOError as e: | ||
| 455 | if e.errno != 2: | ||
| 456 | raise e | ||
| 457 | |||
| 458 | |||
| 459 | self.now_is_workday = self.is_workday(self.now.date()) | 459 | self.now_is_workday = self.is_workday(self.now.date()) |
| 460 | 460 | ||
| 461 | self.time_worked = timedelta() | 461 | self.time_worked = timedelta() |
| @@ -471,9 +471,9 @@ class Worktime(object): | |||
| 471 | 471 | ||
| 472 | self.time_to_work = sum([self.days_to_work[day] for day in self.days_to_work.keys() if day <= self.end_date.date()], timedelta()) | 472 | self.time_to_work = sum([self.days_to_work[day] for day in self.days_to_work.keys() if day <= self.end_date.date()], timedelta()) |
| 473 | for day in [d for d in list(self.pull_forward) if d > self.end_date.date()]: | 473 | for day in [d for d in list(self.pull_forward) if d > self.end_date.date()]: |
| 474 | days_forward = set([d for d in self.days_to_work.keys() if d >= self.end_date.date() and d < day and (not d in self.pull_forward or d == self.end_date.date())]) | 474 | days_forward = set([d for d in [start_day + timedelta(days = x) for x in range(0, (day - start_day).days + 1)] if d >= self.end_date.date() and d < day and (d not in self.pull_forward or d == self.end_date.date())]) |
| 475 | extra_days_forward = set([d for d in self.extra_days_to_work.keys() if d >= self.end_date.date() and d < day and (not d in self.pull_forward or d == self.end_date.date())]) | 475 | extra_days_forward = set([d for d in self.extra_days_to_work.keys() if d >= self.end_date.date() and d < day and (d not in self.pull_forward or d == self.end_date.date())]) |
| 476 | days_forward = days_forward.union(extra_days_forward) | 476 | days_forward |= extra_days_forward |
| 477 | 477 | ||
| 478 | extra_day_time_left = timedelta() | 478 | extra_day_time_left = timedelta() |
| 479 | for extra_day in extra_days_forward: | 479 | for extra_day in extra_days_forward: |
| @@ -486,17 +486,30 @@ class Worktime(object): | |||
| 486 | day_time = max(timedelta(), self.time_per_day(extra_day) - self.extra_days_to_work[extra_day]) | 486 | day_time = max(timedelta(), self.time_per_day(extra_day) - self.extra_days_to_work[extra_day]) |
| 487 | self.extra_days_to_work[extra_day] += extra_day_time * (day_time / extra_day_time_left) | 487 | self.extra_days_to_work[extra_day] += extra_day_time * (day_time / extra_day_time_left) |
| 488 | 488 | ||
| 489 | hours_per_day_forward = time_forward / len(days_forward) if len(days_forward) > 0 else timedelta() | 489 | def days_count(days_forward): |
| 490 | r = 0 | ||
| 491 | for day in sorted(days_forward): | ||
| 492 | day_time = timedelta() | ||
| 493 | if day in self.extra_days_to_work: | ||
| 494 | day_time += self.extra_days_to_work[day] | ||
| 495 | if day in holidays and not day in self.extra_days_to_work: | ||
| 496 | day_time -= holidays[day] | ||
| 497 | if day.isoweekday() in self.workdays: | ||
| 498 | day_time += timedelta(hours = hours_per_week(day)) / len(self.workdays) | ||
| 499 | r += max(timedelta(), day_time) / (timedelta(hours = hours_per_week(day)) / len(self.workdays)) | ||
| 500 | return r | ||
| 501 | |||
| 502 | hours_per_day_forward = time_forward / days_count(days_forward) if days_count(days_forward) > 0 else timedelta() | ||
| 490 | days_forward.discard(self.end_date.date()) | 503 | days_forward.discard(self.end_date.date()) |
| 491 | 504 | ||
| 492 | self.time_pulled_forward += time_forward - hours_per_day_forward * len(days_forward) | 505 | self.time_pulled_forward += time_forward - hours_per_day_forward * days_count(days_forward) |
| 493 | 506 | ||
| 494 | if self.end_date.date() in self.extra_days_to_work: | 507 | if self.end_date.date() in self.extra_days_to_work: |
| 495 | self.time_pulled_forward += self.extra_days_to_work[self.end_date.date()] | 508 | self.time_pulled_forward += self.extra_days_to_work[self.end_date.date()] |
| 496 | 509 | ||
| 497 | self.time_to_work += self.time_pulled_forward | 510 | # self.time_to_work += self.time_pulled_forward |
| 498 | 511 | ||
| 499 | self.time_worked += api.get_billable_hours(self.start_date, self.now, rounding = config.get("WORKTIME", {}).get("rounding", True)) | 512 | self.time_worked += api.get_billable_hours(self.start_date, self.now) |
| 500 | 513 | ||
| 501 | def format_days(worktime, days, date_format=None): | 514 | def format_days(worktime, days, date_format=None): |
| 502 | if not date_format: | 515 | if not date_format: |
| @@ -573,7 +586,7 @@ def worktime(pull_forward_cutoff, waybar, **args): | |||
| 573 | pull_forward_sum = sum(worktime.pull_forward.values(), start=timedelta(milliseconds=0)) | 586 | pull_forward_sum = sum(worktime.pull_forward.values(), start=timedelta(milliseconds=0)) |
| 574 | if pull_forward_sum >= min(pull_forward_cutoff, timedelta(seconds = 1)): | 587 | if pull_forward_sum >= min(pull_forward_cutoff, timedelta(seconds = 1)): |
| 575 | worktime_no_pulled_forward = deepcopy(worktime) | 588 | worktime_no_pulled_forward = deepcopy(worktime) |
| 576 | worktime_no_pulled_forward.time_to_work -= worktime_no_pulled_forward.time_pulled_forward | 589 | # worktime_no_pulled_forward.time_to_work -= worktime_no_pulled_forward.time_pulled_forward |
| 577 | worktime_no_pulled_forward.time_pulled_forward = timedelta() | 590 | worktime_no_pulled_forward.time_pulled_forward = timedelta() |
| 578 | worktime_no_pulled_forward.pull_forward = dict() | 591 | worktime_no_pulled_forward.pull_forward = dict() |
| 579 | worktime.time_to_work += pull_forward_sum | 592 | worktime.time_to_work += pull_forward_sum |
| @@ -611,7 +624,7 @@ def time_worked(now, waybar, **args): | |||
| 611 | out_text = None | 624 | out_text = None |
| 612 | out_class = "running" if now.running_entry else "stopped" | 625 | out_class = "running" if now.running_entry else "stopped" |
| 613 | tooltip = tooltip_timedelta(worked) | 626 | tooltip = tooltip_timedelta(worked) |
| 614 | target_time = max(then.time_per_day(then.now.date()), now.time_per_day(now.now.date())) if then.time_per_day(then.now.date()) and now.time_per_day(now.now.date()) else (then.time_per_day(then.now.date()) if then.time_per_day(then.now.date()) else now.time_per_day(now.now.date())); | 627 | target_time = max(then.time_per_day(then.now.date()), now.time_per_day(now.now.date())) if then.time_per_day(then.now.date()) and now.time_per_day(now.now.date()) else (then.time_per_day(then.now.date()) if then.time_per_day(then.now.date()) else now.time_per_day(now.now.date())) |
| 615 | difference = target_time - worked | 628 | difference = target_time - worked |
| 616 | difference_pull_forward = difference + now.time_pulled_forward | 629 | difference_pull_forward = difference + now.time_pulled_forward |
| 617 | if now.running_entry and difference_pull_forward < timedelta(seconds=0): | 630 | if now.running_entry and difference_pull_forward < timedelta(seconds=0): |
| @@ -886,7 +899,7 @@ def main(): | |||
| 886 | 899 | ||
| 887 | config = Worktime.config() | 900 | config = Worktime.config() |
| 888 | 901 | ||
| 889 | parser = argparse.ArgumentParser(prog = "worktime", description = 'Track worktime using toggl API') | 902 | parser = argparse.ArgumentParser(prog = "worktime", description = 'Track worktime using Kimai API') |
| 890 | parser.add_argument('--time', dest = 'now', metavar = 'TIME', type = isotime, help = 'Time to calculate status for (default: current time)', default = datetime.now(tzlocal())) | 903 | parser.add_argument('--time', dest = 'now', metavar = 'TIME', type = isotime, help = 'Time to calculate status for (default: current time)', default = datetime.now(tzlocal())) |
| 891 | parser.add_argument('--start', dest = 'start_datetime', metavar = 'TIME', type = isotime, help = 'Time to calculate status from (default: None)', default = None) | 904 | parser.add_argument('--start', dest = 'start_datetime', metavar = 'TIME', type = isotime, help = 'Time to calculate status from (default: None)', default = None) |
| 892 | parser.add_argument('--no-running', dest = 'include_running', action = 'store_false') | 905 | parser.add_argument('--no-running', dest = 'include_running', action = 'store_false') |
| @@ -923,5 +936,139 @@ def main(): | |||
| 923 | 936 | ||
| 924 | args.cmd(**vars(args)) | 937 | args.cmd(**vars(args)) |
| 925 | 938 | ||
| 939 | async def ui_update_options(api, cache_path): | ||
| 940 | options = set() | ||
| 941 | sort_order = dict() | ||
| 942 | entry_iter = enumerate(api.get_recent_entries()) | ||
| 943 | loop = asyncio.get_event_loop() | ||
| 944 | start = clock_gettime_ns(CLOCK_MONOTONIC) | ||
| 945 | while item := await loop.run_in_executor(None, next, entry_iter): | ||
| 946 | ix, entry = item | ||
| 947 | if len(options) >= 20 or ix >= 1000: | ||
| 948 | break | ||
| 949 | elif len(options) >= 3: | ||
| 950 | now = clock_gettime_ns(CLOCK_MONOTONIC) | ||
| 951 | if now - start >= 4000000000: | ||
| 952 | break | ||
| 953 | |||
| 954 | option = frozendict({ | ||
| 955 | 'tags': frozenset(entry['tags']), | ||
| 956 | 'activity': frozendict({'id': entry['activity']['id'], 'name': entry['activity']['name']}), | ||
| 957 | 'project': frozendict({'id': entry['project']['id'], 'customer': entry['project']['customer']['name'], 'name': entry['project']['name']}), | ||
| 958 | 'description': entry['description'] if entry['description'] else None, | ||
| 959 | 'billable': entry['billable'], | ||
| 960 | }) | ||
| 961 | sort_value = isoparse(entry['begin']) | ||
| 962 | if option in sort_order: | ||
| 963 | sort_value = max(sort_value, sort_order[option]) | ||
| 964 | sort_order[option] = sort_value | ||
| 965 | options.add(option) | ||
| 966 | |||
| 967 | options = list(sorted(options, key = lambda o: sort_order[o], reverse = True)) | ||
| 968 | |||
| 969 | with AtomicWriter(cache_path, overwrite=True) as ch: | ||
| 970 | ch.write_text(jsonpickle.encode(options)) | ||
| 971 | |||
| 972 | return options | ||
| 973 | |||
| 974 | def ui_render_option(option): | ||
| 975 | res = '' | ||
| 976 | if option['description']: | ||
| 977 | res += '„{}“, '.format(option['description']) | ||
| 978 | res += option['activity']['name'] + ', ' | ||
| 979 | res += option['project']['name'] | ||
| 980 | if option['project']['customer'] not in option['project']['name']: | ||
| 981 | res += ' ({})'.format(option['project']['customer']) | ||
| 982 | if option['tags']: | ||
| 983 | res += ', {}'.format(' '.join(map(lambda t: '#{}'.format(t), option['tags']))) | ||
| 984 | if not option['billable']: | ||
| 985 | res += ', not billable' | ||
| 986 | return res | ||
| 987 | |||
| 988 | async def ui_main(): | ||
| 989 | cache_path = Path(BaseDirectory.save_cache_path('worktime-ui')) / 'options.json' | ||
| 990 | options = None | ||
| 991 | try: | ||
| 992 | with cache_path.open('r', encoding='utf-8') as ch: | ||
| 993 | options = jsonpickle.decode(ch.read()) | ||
| 994 | except FileNotFoundError: | ||
| 995 | pass | ||
| 996 | |||
| 997 | config = Worktime.config() | ||
| 998 | api = KimaiAPI( | ||
| 999 | base_url=config.get("KIMAI", {}).get("BaseUrl", None), | ||
| 1000 | api_token=config.get("KIMAI", {}).get("ApiToken", None), | ||
| 1001 | clients=config.get("KIMAI", {}).get("Clients", None) | ||
| 1002 | ) | ||
| 1003 | running_entry = api.get_running_entry() | ||
| 1004 | |||
| 1005 | async with asyncio.TaskGroup() as tg: | ||
| 1006 | update_options = tg.create_task(ui_update_options(api, cache_path)) | ||
| 1007 | if not options: | ||
| 1008 | options = await update_options | ||
| 1009 | |||
| 1010 | read_fd, write_fd = os.pipe() | ||
| 1011 | w_pipe = open(write_fd, 'wb', 0) | ||
| 1012 | loop = asyncio.get_event_loop() | ||
| 1013 | w_transport, _ = await loop.connect_write_pipe( | ||
| 1014 | asyncio.Protocol, | ||
| 1015 | w_pipe, | ||
| 1016 | ) | ||
| 1017 | r_pipe = open(read_fd, 'rb', 0) | ||
| 1018 | |||
| 1019 | proc = await asyncio.create_subprocess_exec( | ||
| 1020 | "fuzzel", "--dmenu", "--index", "--width=60", | ||
| 1021 | stdout = asyncio.subprocess.PIPE, | ||
| 1022 | stdin = r_pipe, | ||
| 1023 | ) | ||
| 1024 | |||
| 1025 | with closing(w_transport) as t: | ||
| 1026 | if running_entry: | ||
| 1027 | t.write(b'Stop running timesheet\n') | ||
| 1028 | for option in options: | ||
| 1029 | t.write(ui_render_option(option).encode('utf-8') + b'\n') | ||
| 1030 | |||
| 1031 | stdout, _ = await proc.communicate() | ||
| 1032 | if proc.returncode != 0: | ||
| 1033 | return | ||
| 1034 | fuzzel_out = int(stdout.decode('utf-8')) | ||
| 1035 | if fuzzel_out < 0 or fuzzel_out >= len(options): | ||
| 1036 | return | ||
| 1037 | elif running_entry and fuzzel_out == 0: | ||
| 1038 | api.stop_clock(running_entry['id']) | ||
| 1039 | await notify.Server('worktime').Notify("Stopped running timesheet").set_timeout(65000).show() | ||
| 1040 | else: | ||
| 1041 | if running_entry: | ||
| 1042 | fuzzel_out -= 1 | ||
| 1043 | option = options[fuzzel_out] | ||
| 1044 | api.start_clock( | ||
| 1045 | project_id = option['project']['id'], | ||
| 1046 | activity_id = option['activity']['id'], | ||
| 1047 | description = option['description'], | ||
| 1048 | tags = option['tags'], | ||
| 1049 | billable = option['billable'], | ||
| 1050 | ) | ||
| 1051 | await notify.Server('worktime').Notify("Timesheet started…").set_timeout(65000).show() | ||
| 1052 | |||
| 1053 | |||
| 1054 | def ui(): | ||
| 1055 | asyncio.run(ui_main()) | ||
| 1056 | |||
| 1057 | async def stop_main(): | ||
| 1058 | config = Worktime.config() | ||
| 1059 | api = KimaiAPI( | ||
| 1060 | base_url=config.get("KIMAI", {}).get("BaseUrl", None), | ||
| 1061 | api_token=config.get("KIMAI", {}).get("ApiToken", None), | ||
| 1062 | clients=config.get("KIMAI", {}).get("Clients", None) | ||
| 1063 | ) | ||
| 1064 | if running_entry := api.get_running_entry(): | ||
| 1065 | api.stop_clock(running_entry['id']) | ||
| 1066 | await notify.Server('worktime').Notify("Stopped running timesheet").set_timeout(65000).show() | ||
| 1067 | else: | ||
| 1068 | await notify.Server('worktime').Notify("No timesheet currently running").set_timeout(65000).show() | ||
| 1069 | |||
| 1070 | def stop(): | ||
| 1071 | asyncio.run(stop_main()) | ||
| 1072 | |||
| 926 | if __name__ == "__main__": | 1073 | if __name__ == "__main__": |
| 927 | sys.exit(main()) | 1074 | sys.exit(main()) |
diff --git a/overlays/yt-dlp.nix b/overlays/yt-dlp.nix index 94ab1fdd..9a54a32b 100644 --- a/overlays/yt-dlp.nix +++ b/overlays/yt-dlp.nix | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | { prev, sources, ... }: { | 1 | { prev, sources, ... }: { |
| 2 | yt-dlp = prev.yt-dlp.overrideAttrs (oldAttrs: { | 2 | yt-dlp = prev.yt-dlp.overrideAttrs (oldAttrs: { |
| 3 | inherit (sources.yt-dlp) pname version src; | 3 | inherit (sources.yt-dlp) pname version src; |
| 4 | |||
| 5 | postPatch = ""; | ||
| 4 | }); | 6 | }); |
| 5 | } | 7 | } |
diff --git a/overlays/zte-prometheus-exporter/default.nix b/overlays/zte-prometheus-exporter/default.nix index 2188e7b3..6295567d 100644 --- a/overlays/zte-prometheus-exporter/default.nix +++ b/overlays/zte-prometheus-exporter/default.nix | |||
| @@ -1,18 +1,17 @@ | |||
| 1 | { final, prev, ... }: | 1 | { final, prev, ... }: |
| 2 | let | 2 | let |
| 3 | packageOverrides = final.callPackage ./python-packages.nix {}; | 3 | packageOverrides = final.callPackage ./python-packages.nix {}; |
| 4 | inpPython = final.python310.override { inherit packageOverrides; }; | 4 | inpPython = final.python3.override { inherit packageOverrides; }; |
| 5 | python = inpPython.withPackages (ps: with ps; [pytimeparse requests]); | ||
| 5 | in { | 6 | in { |
| 6 | zte-prometheus-exporter = prev.stdenv.mkDerivation rec { | 7 | zte-prometheus-exporter = prev.stdenv.mkDerivation rec { |
| 7 | name = "zte-prometheus-exporter"; | 8 | name = "zte-prometheus-exporter"; |
| 8 | src = ./zte-prometheus-exporter.py; | 9 | src = prev.replaceVars ./zte-prometheus-exporter.py { inherit python; }; |
| 9 | 10 | ||
| 10 | phases = [ "buildPhase" "checkPhase" "installPhase" ]; | 11 | phases = [ "unpackPhase" "checkPhase" "installPhase" ]; |
| 11 | 12 | ||
| 12 | python = inpPython.withPackages (ps: with ps; [pytimeparse requests]); | 13 | unpackPhase = '' |
| 13 | 14 | cp $src zte-prometheus-exporter | |
| 14 | buildPhase = '' | ||
| 15 | substituteAll $src zte-prometheus-exporter | ||
| 16 | ''; | 15 | ''; |
| 17 | 16 | ||
| 18 | doCheck = true; | 17 | doCheck = true; |
diff --git a/overlays/zte-prometheus-exporter/zte-prometheus-exporter.py b/overlays/zte-prometheus-exporter/zte-prometheus-exporter.py index fc719a96..bc8326ff 100644 --- a/overlays/zte-prometheus-exporter/zte-prometheus-exporter.py +++ b/overlays/zte-prometheus-exporter/zte-prometheus-exporter.py | |||
| @@ -54,13 +54,13 @@ class ZTEMetrics: | |||
| 54 | cls._instance.password = environ.get('ZTE_PASSWORD') | 54 | cls._instance.password = environ.get('ZTE_PASSWORD') |
| 55 | cls._instance.attrs = None | 55 | cls._instance.attrs = None |
| 56 | return cls._instance | 56 | return cls._instance |
| 57 | 57 | ||
| 58 | 58 | ||
| 59 | def __init__(self): | 59 | def __init__(self): |
| 60 | raise RuntimeError('Call instance() instead') | 60 | raise RuntimeError('Call instance() instead') |
| 61 | 61 | ||
| 62 | _error_pattern = re.compile('^IF_ERROR(PARAM|TYPE|STR|ID)$') | 62 | _error_pattern = re.compile(r'^IF_ERROR(PARAM|TYPE|STR|ID)$') |
| 63 | _obj_pattern = re.compile('^(?:OBJ_(.+)_ID)|(?:ID_(WAN_COMFIG))$') | 63 | _obj_pattern = re.compile(r'^(?:OBJ_(.+)_ID)|(?:ID_(WAN_COMFIG))$') |
| 64 | def update(self): | 64 | def update(self): |
| 65 | attrs = dict() | 65 | attrs = dict() |
| 66 | 66 | ||
| @@ -106,6 +106,8 @@ class ZTEMetrics: | |||
| 106 | value = child.text | 106 | value = child.text |
| 107 | case _: | 107 | case _: |
| 108 | pass | 108 | pass |
| 109 | if value == '0,0': | ||
| 110 | value = '0' | ||
| 109 | if not name is None and not value is None: | 111 | if not name is None and not value is None: |
| 110 | instance_dict[name] = value | 112 | instance_dict[name] = value |
| 111 | name = None | 113 | name = None |
| @@ -120,8 +122,8 @@ class ZTEMetrics: | |||
| 120 | def json_text(self): | 122 | def json_text(self): |
| 121 | return json.dumps(self.attrs) | 123 | return json.dumps(self.attrs) |
| 122 | 124 | ||
| 123 | _link_pattern = re.compile('^IGD\.WD1\.LINE([0-9]+)$') | 125 | _link_pattern = re.compile(r'^IGD\.WD1\.LINE([0-9]+)$') |
| 124 | _eth_pattern = re.compile('^IGD\.LD1\.ETH([0-9]+)$') | 126 | _eth_pattern = re.compile(r'^IGD\.LD1\.ETH([0-9]+)$') |
| 125 | def prometheus(self): | 127 | def prometheus(self): |
| 126 | metrics = '' | 128 | metrics = '' |
| 127 | 129 | ||
| @@ -133,34 +135,41 @@ class ZTEMetrics: | |||
| 133 | link_match = self._link_pattern.match(link) | 135 | link_match = self._link_pattern.match(link) |
| 134 | link_number = link_match.group(1) | 136 | link_number = link_match.group(1) |
| 135 | 137 | ||
| 136 | if 'crc_errors_count' not in link_metrics: | 138 | link_is_up = self.attrs['DSLINTERFACE'][link]['Status'] == 'Up' |
| 137 | link_metrics['crc_errors_count'] = {'type': 'counter', 'metrics': []} | 139 | |
| 138 | link_metrics['crc_errors_count']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['UpCrc_errors']))] | 140 | if link_is_up: |
| 139 | link_metrics['crc_errors_count']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['DownCrc_errors']))] | 141 | if 'crc_errors_count' not in link_metrics: |
| 142 | link_metrics['crc_errors_count'] = {'type': 'counter', 'metrics': []} | ||
| 143 | link_metrics['crc_errors_count']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['UpCrc_errors']))] | ||
| 144 | link_metrics['crc_errors_count']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['DownCrc_errors']))] | ||
| 140 | 145 | ||
| 141 | if 'noise_margin_db' not in link_metrics: | 146 | if 'noise_margin_db' not in link_metrics: |
| 142 | link_metrics['noise_margin_db'] = {'type': 'gauge', 'metrics': []} | 147 | link_metrics['noise_margin_db'] = {'type': 'gauge', 'metrics': []} |
| 143 | link_metrics['noise_margin_db']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_noise_margin']))] | 148 | link_metrics['noise_margin_db']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_noise_margin']) / 10)] |
| 144 | link_metrics['noise_margin_db']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_noise_margin']))] | 149 | link_metrics['noise_margin_db']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_noise_margin']) / 10)] |
| 145 | 150 | ||
| 146 | if 'attenuation_db' not in link_metrics: | 151 | if 'attenuation_db' not in link_metrics: |
| 147 | link_metrics['attenuation_db'] = {'type': 'gauge', 'metrics': []} | 152 | link_metrics['attenuation_db'] = {'type': 'gauge', 'metrics': []} |
| 148 | link_metrics['attenuation_db']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_attenuation']))] | 153 | link_metrics['attenuation_db']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_attenuation']) / 10)] |
| 149 | link_metrics['attenuation_db']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_attenuation']))] | 154 | link_metrics['attenuation_db']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_attenuation']) / 10)] |
| 150 | 155 | ||
| 151 | if 'max_rate_kbps' not in link_metrics: | 156 | if 'max_rate_kbps' not in link_metrics: |
| 152 | link_metrics['max_rate_kbps'] = {'type': 'gauge', 'metrics': []} | 157 | link_metrics['max_rate_kbps'] = {'type': 'gauge', 'metrics': []} |
| 153 | link_metrics['max_rate_kbps']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_max_rate']))] | 158 | link_metrics['max_rate_kbps']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_max_rate']))] |
| 154 | link_metrics['max_rate_kbps']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_max_rate']))] | 159 | link_metrics['max_rate_kbps']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_max_rate']))] |
| 155 | 160 | ||
| 156 | if 'current_rate_kbps' not in link_metrics: | 161 | if 'current_rate_kbps' not in link_metrics: |
| 157 | link_metrics['current_rate_kbps'] = {'type': 'gauge', 'metrics': []} | 162 | link_metrics['current_rate_kbps'] = {'type': 'gauge', 'metrics': []} |
| 158 | link_metrics['current_rate_kbps']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_current_rate']))] | 163 | link_metrics['current_rate_kbps']['metrics'] += [({"direction": "up", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Upstream_current_rate']))] |
| 159 | link_metrics['current_rate_kbps']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_current_rate']))] | 164 | link_metrics['current_rate_kbps']['metrics'] += [({"direction": "down", "link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Downstream_current_rate']))] |
| 160 | 165 | ||
| 161 | if 'dsl_uptime_seconds' not in link_metrics: | 166 | if 'dsl_uptime_seconds' not in link_metrics: |
| 162 | link_metrics['dsl_uptime_seconds'] = {'type': 'gauge', 'metrics': []} | 167 | link_metrics['uptime_seconds'] = {'type': 'gauge', 'metrics': []} |
| 163 | link_metrics['dsl_uptime_seconds']['metrics'] += [({"link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Showtime_start']))] | 168 | link_metrics['uptime_seconds']['metrics'] += [({"link": link_number}, int(self.attrs['DSLINTERFACE'][link]['Showtime_start']) if link_is_up else 0)] |
| 169 | |||
| 170 | if 'status' not in link_metrics: | ||
| 171 | link_metrics['status'] = {'type': 'gauge', 'metrics': []} | ||
| 172 | link_metrics['status']['metrics'] += [({"link": link_number, "status": self.attrs['DSLINTERFACE'][link]['Status']}, 1)] | ||
| 164 | if link_metrics: | 173 | if link_metrics: |
| 165 | for metric_name in link_metrics: | 174 | for metric_name in link_metrics: |
| 166 | metrics += _format_prom_metrics(f'dsl_{metric_name}', link_metrics[metric_name]['type'], link_metrics[metric_name]['metrics']) | 175 | metrics += _format_prom_metrics(f'dsl_{metric_name}', link_metrics[metric_name]['type'], link_metrics[metric_name]['metrics']) |
| @@ -203,7 +212,7 @@ class ZTEMetricsServer(BaseHTTPRequestHandler): | |||
| 203 | self.send_response(200) | 212 | self.send_response(200) |
| 204 | self.send_header("Content-type", "text/plain") | 213 | self.send_header("Content-type", "text/plain") |
| 205 | self.end_headers() | 214 | self.end_headers() |
| 206 | 215 | ||
| 207 | self.wfile.write(zte_metrics.prometheus()) | 216 | self.wfile.write(zte_metrics.prometheus()) |
| 208 | case _: | 217 | case _: |
| 209 | self.send_response(404) | 218 | self.send_response(404) |
| @@ -1,19 +1,29 @@ | |||
| 1 | inputs@{ system, self, deploy-rs, nvfetcher, nixpkgs, ca-util, ... }: | 1 | inputs@{ system, self, nvfetcher, nixpkgs, ca-util, ... }: |
| 2 | let | 2 | let |
| 3 | pkgs = self.legacyPackages.${system}; | 3 | pkgs = self.legacyPackages.${system}; |
| 4 | utils = import ./utils { inherit (nixpkgs) lib; }; | 4 | utils = import ./utils { inherit (nixpkgs) lib; }; |
| 5 | inherit (utils) nixImport; | 5 | inherit (utils) nixImport; |
| 6 | uv-links = pkgs.symlinkJoin { | ||
| 7 | name = "uv-links"; | ||
| 8 | paths = [ | ||
| 9 | pkgs.python312.pkgs.pygobject3 | ||
| 10 | ]; | ||
| 11 | }; | ||
| 6 | in pkgs.mkShell { | 12 | in pkgs.mkShell { |
| 7 | nativeBuildInputs = builtins.attrValues self.packages.${system} ++ (with pkgs; [ | 13 | nativeBuildInputs = builtins.attrValues self.packages.${system} ++ (with pkgs; [ |
| 8 | sops | 14 | sops |
| 9 | wireguard-tools | 15 | wireguard-tools |
| 10 | gup | 16 | gup |
| 11 | nftables | 17 | nftables |
| 12 | deploy-rs.packages.${system}.deploy-rs | 18 | deploy-rs.deploy-rs |
| 13 | knot-dns | 19 | knot-dns |
| 14 | yq | 20 | yq |
| 15 | nvfetcher.packages.${system}.default | 21 | nvfetcher.packages.${system}.default |
| 16 | ca-util.packages.${system}.ca | 22 | ca-util.packages.${system}.ca |
| 17 | poetry | 23 | poetry uv |
| 24 | ninja pkg-config cairo.dev systemd.dev | ||
| 18 | ]); | 25 | ]); |
| 26 | shellHook = '' | ||
| 27 | export UV_FIND_LINKS=${uv-links}/lib/python3.12/site-packages | ||
| 28 | ''; | ||
| 19 | } | 29 | } |
diff --git a/system-profiles/bcachefs.nix b/system-profiles/bcachefs.nix index f9f048b9..be12bf20 100644 --- a/system-profiles/bcachefs.nix +++ b/system-profiles/bcachefs.nix | |||
| @@ -1,6 +1,16 @@ | |||
| 1 | { pkgs, ... } : { | 1 | { pkgs, lib, ... } : { |
| 2 | config = { | 2 | config = { |
| 3 | boot.supportedFilesystems.bcachefs = true; | 3 | boot.supportedFilesystems.bcachefs = true; |
| 4 | environment.systemPackages = with pkgs; [ bcachefs-tools ]; | 4 | environment.systemPackages = with pkgs; [ bcachefs-tools ]; |
| 5 | |||
| 6 | boot.kernelPatches = [ | ||
| 7 | { | ||
| 8 | name = "bcachefs-casefold-fix"; | ||
| 9 | patch = null; | ||
| 10 | structuredExtraConfig = with lib.kernel; { | ||
| 11 | UNICODE = lib.mkOverride 90 no; | ||
| 12 | }; | ||
| 13 | } | ||
| 14 | ]; | ||
| 5 | }; | 15 | }; |
| 6 | } | 16 | } |
diff --git a/system-profiles/core/default.nix b/system-profiles/core/default.nix index b85aea4e..e5f9dc16 100644 --- a/system-profiles/core/default.nix +++ b/system-profiles/core/default.nix | |||
| @@ -127,36 +127,16 @@ in { | |||
| 127 | 127 | ||
| 128 | flake-registry = "${flakeInputs.flake-registry}/flake-registry.json"; | 128 | flake-registry = "${flakeInputs.flake-registry}/flake-registry.json"; |
| 129 | }; | 129 | }; |
| 130 | nixPath = [ | 130 | nixPath = map (flake: "${flake}=flake:${flake}") (attrNames config.nix.registry); |
| 131 | "nixpkgs=${pkgs.runCommand "nixpkgs" {} '' | ||
| 132 | mkdir $out | ||
| 133 | ln -s ${./nixpkgs.nix} $out/default.nix | ||
| 134 | ln -s /run/nixpkgs/lib $out/lib | ||
| 135 | ''}" | ||
| 136 | ]; | ||
| 137 | registry = | 131 | registry = |
| 138 | let override = { self = "nixos"; }; | 132 | let override = { self = "nixos"; }; |
| 139 | in mapAttrs' (inpName: inpFlake: nameValuePair | 133 | in mapAttrs' (inpName: inpFlake: nameValuePair |
| 140 | (override.${inpName} or inpName) | 134 | (override.${inpName} or inpName) |
| 141 | { flake = inpFlake; } ) flakeInputs; | 135 | { to = { type = "path"; path = inpFlake; }; } ) flakeInputs; |
| 142 | }; | 136 | }; |
| 143 | 137 | ||
| 144 | systemd.tmpfiles.rules = [ | 138 | systemd.tmpfiles.rules = [ |
| 145 | "L+ /run/nixpkgs - - - - ${flakeInputs.${config.nixpkgs.flakeInput}.outPath}" | 139 | "L+ /run/nixpkgs - - - - ${flakeInputs.${config.nixpkgs.flakeInput}.outPath}" |
| 146 | "L+ /run/nixpkgs-overlays.nix - - - - ${pkgs.writeText "overlays.nix" '' | ||
| 147 | with builtins; | ||
| 148 | |||
| 149 | attrValues (import | ||
| 150 | ( | ||
| 151 | let lock = fromJSON (readFile ${flake + "/flake.lock"}); in | ||
| 152 | fetchTarball { | ||
| 153 | url = "https://github.com/edolstra/flake-compat/archive/''${lock.nodes.flake-compat.locked.rev}.tar.gz"; | ||
| 154 | sha256 = lock.nodes.flake-compat.locked.narHash; | ||
| 155 | } | ||
| 156 | ) | ||
| 157 | { src = ${flake}; } | ||
| 158 | ).defaultNix.overlays | ||
| 159 | ''}" | ||
| 160 | "L+ /etc/nixos - - - - ${flake}" | 140 | "L+ /etc/nixos - - - - ${flake}" |
| 161 | ] ++ map (input: "L+ /run/flake-inputs/${input} - - - - ${flakeInputs.${input}.outPath}") (attrNames flakeInputs); | 141 | ] ++ map (input: "L+ /run/flake-inputs/${input} - - - - ${flakeInputs.${input}.outPath}") (attrNames flakeInputs); |
| 162 | 142 | ||
| @@ -177,8 +157,6 @@ in { | |||
| 177 | { | 157 | { |
| 178 | manual.manpages.enable = true; | 158 | manual.manpages.enable = true; |
| 179 | systemd.user.startServices = "sd-switch"; | 159 | systemd.user.startServices = "sd-switch"; |
| 180 | |||
| 181 | programs.ssh.internallyManaged = mkForce true; | ||
| 182 | } | 160 | } |
| 183 | ]; | 161 | ]; |
| 184 | extraSpecialArgs = { inherit flake flakeInputs path; hostConfig = config; }; | 162 | extraSpecialArgs = { inherit flake flakeInputs path; hostConfig = config; }; |
| @@ -202,13 +180,7 @@ in { | |||
| 202 | }; | 180 | }; |
| 203 | environment.systemPackages = with pkgs; [ git-annex scutiger ]; | 181 | environment.systemPackages = with pkgs; [ git-annex scutiger ]; |
| 204 | } | 182 | } |
| 205 | ] ++ (optional (options ? system.switch.enableNg) { | 183 | ] ++ (optional (options ? system.rebuild.enableNg) { |
| 206 | system.switch = lib.mkDefault { | ||
| 207 | enable = false; | ||
| 208 | enableNg = true; | ||
| 209 | }; | ||
| 210 | }) | ||
| 211 | ++ (optional (options ? system.rebuild.enableNg) { | ||
| 212 | system.rebuild.enableNg = lib.mkDefault true; | 184 | system.rebuild.enableNg = lib.mkDefault true; |
| 213 | }) | 185 | }) |
| 214 | ++ (optional (options ? services.userborn) { | 186 | ++ (optional (options ? services.userborn) { |
diff --git a/system-profiles/default-locale.nix b/system-profiles/default-locale.nix index 2d483f04..60d338cb 100644 --- a/system-profiles/default-locale.nix +++ b/system-profiles/default-locale.nix | |||
| @@ -1,16 +1,23 @@ | |||
| 1 | { lib, ... }: | 1 | { lib, options, ... }: |
| 2 | 2 | ||
| 3 | with lib; | 3 | with lib; |
| 4 | 4 | ||
| 5 | { | 5 | { |
| 6 | i18n = { | 6 | config = foldr recursiveUpdate {} ([ |
| 7 | defaultLocale = "en_DK.UTF-8"; | 7 | { |
| 8 | extraLocaleSettings = { | 8 | i18n = { |
| 9 | "TIME_STYLE" = "long-iso"; | 9 | defaultLocale = "en_DK.UTF-8"; |
| 10 | }; | 10 | extraLocaleSettings = { |
| 11 | supportedLocales = [ "C.UTF-8/UTF-8" "en_US.UTF-8/UTF-8" "en_DK.UTF-8/UTF-8" ]; | 11 | "TIME_STYLE" = "long-iso"; |
| 12 | }; | 12 | }; |
| 13 | console.keyMap = mkDefault "dvorak-programmer"; | 13 | }; |
| 14 | console.keyMap = mkDefault "dvorak-programmer"; | ||
| 14 | 15 | ||
| 15 | time.timeZone = mkDefault "Europe/Berlin"; | 16 | time.timeZone = mkDefault "Europe/Berlin"; |
| 17 | } | ||
| 18 | ] ++ (optional (options ? i18n.extraLocales) { | ||
| 19 | i18n.extraLocales = [ "C.UTF-8" "en_US.UTF-8" "en_DK.UTF-8" ]; | ||
| 20 | }) ++ (optional (!(options ? i18n.extraLocales)) { | ||
| 21 | i18n.supportedLocales = [ "C.UTF-8/UTF-8" "en_US.UTF-8/UTF-8" "en_DK.UTF-8/UTF-8" ]; | ||
| 22 | })); | ||
| 16 | } | 23 | } |
diff --git a/system-profiles/initrd-all-crypto-modules.nix b/system-profiles/initrd-all-crypto-modules.nix index 45cd4b74..da6c781e 100644 --- a/system-profiles/initrd-all-crypto-modules.nix +++ b/system-profiles/initrd-all-crypto-modules.nix | |||
| @@ -18,7 +18,7 @@ in { | |||
| 18 | { | 18 | { |
| 19 | name = "encrypted_key"; | 19 | name = "encrypted_key"; |
| 20 | patch = null; | 20 | patch = null; |
| 21 | extraStructuredConfig.ENCRYPTED_KEYS = lib.kernel.yes; | 21 | structuredExtraConfig.ENCRYPTED_KEYS = lib.kernel.yes; |
| 22 | } | 22 | } |
| 23 | ]; | 23 | ]; |
| 24 | } | 24 | } |
diff --git a/system-profiles/lanzaboote.nix b/system-profiles/lanzaboote.nix new file mode 100644 index 00000000..f1e179cf --- /dev/null +++ b/system-profiles/lanzaboote.nix | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | { flakeInputs, pkgs, ... }: | ||
| 2 | { | ||
| 3 | imports = [ | ||
| 4 | flakeInputs.lanzaboote.nixosModules.lanzaboote | ||
| 5 | ]; | ||
| 6 | |||
| 7 | config = { | ||
| 8 | environment.systemPackages = [ pkgs.sbctl ]; | ||
| 9 | boot.lanzaboote = { | ||
| 10 | enable = true; | ||
| 11 | pkiBundle = "/var/lib/sbctl"; | ||
| 12 | }; | ||
| 13 | }; | ||
| 14 | } | ||
diff --git a/system-profiles/nfsroot.nix b/system-profiles/nfsroot.nix index 1cd930d9..e3dc2d2e 100644 --- a/system-profiles/nfsroot.nix +++ b/system-profiles/nfsroot.nix | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | { config, options, pkgs, lib, flake, flakeInputs, ... }: | 1 | { config, options, pkgs, lib, flake, ... }: |
| 2 | 2 | ||
| 3 | with lib; | 3 | with lib; |
| 4 | 4 | ||
| @@ -48,7 +48,7 @@ in { | |||
| 48 | fileSystems."/nix/.ro-store" = mkImageMediaOverride | 48 | fileSystems."/nix/.ro-store" = mkImageMediaOverride |
| 49 | { fsType = "nfs4"; | 49 | { fsType = "nfs4"; |
| 50 | device = cfg.storeDevice; | 50 | device = cfg.storeDevice; |
| 51 | options = [ "ro" ]; | 51 | options = [ "ro" "nfsvers=4.2" ]; |
| 52 | neededForBoot = true; | 52 | neededForBoot = true; |
| 53 | }; | 53 | }; |
| 54 | 54 | ||
| @@ -86,7 +86,7 @@ in { | |||
| 86 | mkdir -p /mnt-root/etc/ | 86 | mkdir -p /mnt-root/etc/ |
| 87 | cp /etc/resolv.conf /mnt-root/etc/resolv.conf | 87 | cp /etc/resolv.conf /mnt-root/etc/resolv.conf |
| 88 | ''; | 88 | ''; |
| 89 | networking.useDHCP = true; | 89 | networking.useDHCP = mkImageMediaOverride true; |
| 90 | networking.resolvconf.enable = false; | 90 | networking.resolvconf.enable = false; |
| 91 | networking.dhcpcd.persistent = true; | 91 | networking.dhcpcd.persistent = true; |
| 92 | 92 | ||
diff --git a/system-profiles/rebuild-machines/default.nix b/system-profiles/rebuild-machines/default.nix index 544f47e1..de86cd74 100644 --- a/system-profiles/rebuild-machines/default.nix +++ b/system-profiles/rebuild-machines/default.nix | |||
| @@ -25,16 +25,18 @@ let | |||
| 25 | 25 | ||
| 26 | phases = [ "buildPhase" "installPhase" ]; | 26 | phases = [ "buildPhase" "installPhase" ]; |
| 27 | 27 | ||
| 28 | inherit (pkgs) zsh coreutils openssh; | ||
| 29 | inherit (cfg) scriptName; | ||
| 30 | inherit (cfg.flake) flakeOutput; | ||
| 31 | flake = cfg.flake.name; | ||
| 32 | nixosRebuild = config.system.build.nixos-rebuild; | ||
| 33 | inherit (config.security) wrapperDir; | ||
| 34 | inherit sshConfig; | ||
| 35 | |||
| 36 | buildPhase = '' | 28 | buildPhase = '' |
| 37 | substituteAll $src rebuild-machine.zsh | 29 | substitute $src rebuild-machine.zsh \ |
| 30 | --subst-var-by zsh ${pkgs.zsh} \ | ||
| 31 | --subst-var-by coreutils ${pkgs.coreutils} \ | ||
| 32 | --subst-var-by openssh ${pkgs.openssh} \ | ||
| 33 | --subst-var-by wrapperDir ${config.security.wrapperDir} \ | ||
| 34 | --subst-var-by sshConfig ${sshConfig} \ | ||
| 35 | --subst-var-by out "$out" \ | ||
| 36 | --subst-var-by nixosRebuild ${config.system.build.nixos-rebuild} \ | ||
| 37 | --subst-var-by flake ${cfg.flake.name} \ | ||
| 38 | --subst-var-by scriptName ${cfg.scriptName} \ | ||
| 39 | --subst-var-by flakeOutput ${cfg.flake.flakeOutput} | ||
| 38 | ''; | 40 | ''; |
| 39 | 41 | ||
| 40 | installPhase = '' | 42 | installPhase = '' |
diff --git a/system-profiles/zfs.nix b/system-profiles/zfs.nix index a93dddd2..af9f1c17 100644 --- a/system-profiles/zfs.nix +++ b/system-profiles/zfs.nix | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | { pkgs, lib, ... } : { | 1 | { config, pkgs, lib, ... } : { |
| 2 | config = { | 2 | config = { |
| 3 | boot = { | 3 | boot = { |
| 4 | kernelPackages = pkgs.linuxPackages_6_12; | 4 | kernelPackages = pkgs.linuxPackages_6_12; |
diff --git a/user-profiles/core.nix b/user-profiles/core.nix index a8af48b3..57fb7628 100644 --- a/user-profiles/core.nix +++ b/user-profiles/core.nix | |||
| @@ -5,7 +5,11 @@ with lib; | |||
| 5 | { | 5 | { |
| 6 | config = { | 6 | config = { |
| 7 | users.users.${userName} = {}; # Just make sure the user is created | 7 | users.users.${userName} = {}; # Just make sure the user is created |
| 8 | home-manager.users.${userName} = {}; | 8 | home-manager.users.${userName} = let sysConfig = config; in { config, ... }: { |
| 9 | config.nix.settings = { | ||
| 10 | inherit (sysConfig.nix.settings) use-xdg-base-directories; | ||
| 11 | }; | ||
| 12 | }; | ||
| 9 | 13 | ||
| 10 | systemd.services."home-manager-${utils.escapeSystemdPath userName}" = lib.mkIf (!config.home-manager.enableSystemd) { | 14 | systemd.services."home-manager-${utils.escapeSystemdPath userName}" = lib.mkIf (!config.home-manager.enableSystemd) { |
| 11 | restartIfChanged = false; # only run once on startup, deploy to running system with deploy-rs | 15 | restartIfChanged = false; # only run once on startup, deploy to running system with deploy-rs |
diff --git a/user-profiles/feeds/alot.config b/user-profiles/feeds/alot.config deleted file mode 100644 index a14d4539..00000000 --- a/user-profiles/feeds/alot.config +++ /dev/null | |||
| @@ -1,50 +0,0 @@ | |||
| 1 | attachment_prefix="~/Downloads" | ||
| 2 | bug_on_exit=true | ||
| 3 | editor_cmd="false" | ||
| 4 | tabwidth=2 | ||
| 5 | timestamp_format="%a %d %b %H:%M:%S %Y UTC%z" | ||
| 6 | auto_remove_unread=True | ||
| 7 | #initial_command="search ( tag:inbox ) AND NOT ( tag:killed )" | ||
| 8 | initial_command="search ( tag:inbox ) AND NOT ( is:link OR is:media OR is:killed )" | ||
| 9 | |||
| 10 | [accounts] | ||
| 11 | [[private]] | ||
| 12 | realname = @realname@ | ||
| 13 | address = @address@ | ||
| 14 | |||
| 15 | [bindings] | ||
| 16 | j = | ||
| 17 | k = | ||
| 18 | 'g g' = | ||
| 19 | G = | ||
| 20 | I = search ( tag:inbox ) AND NOT ( is:killed ) | ||
| 21 | U = search ( tag:inbox ) AND NOT ( is:link OR is:media OR is:killed ) | ||
| 22 | V = search ( tag:inbox AND is:media OR ( is:live AND date:12h.. AND NOT is:unread ) ) AND NOT ( is:killed ) | ||
| 23 | W = search ( is:media ) AND NOT ( tag:inbox OR is:killed OR is:highlight ) | ||
| 24 | L = search ( tag:inbox AND is:link ) AND NOT ( is:killed ) | ||
| 25 | |||
| 26 | h = move first | ||
| 27 | t = move up | ||
| 28 | n = move down | ||
| 29 | s = move last | ||
| 30 | [[search]] | ||
| 31 | a = | ||
| 32 | s = | ||
| 33 | |||
| 34 | u = toggletags unread | ||
| 35 | i = toggletags inbox | ||
| 36 | j = untag unread,inbox | ||
| 37 | r = toggletags later | ||
| 38 | [[thread]] | ||
| 39 | s = | ||
| 40 | S = | ||
| 41 | n = | ||
| 42 | 'g j' = | ||
| 43 | 'g k' = | ||
| 44 | 'g l' = | ||
| 45 | w = save | ||
| 46 | W = save --all | ||
| 47 | 'g h' = move parent | ||
| 48 | 'g t' = move next sibling | ||
| 49 | 'g n' = move previous sibling | ||
| 50 | 'g s' = move first reply \ No newline at end of file | ||
diff --git a/user-profiles/feeds/default.nix b/user-profiles/feeds/default.nix deleted file mode 100644 index 82be90c7..00000000 --- a/user-profiles/feeds/default.nix +++ /dev/null | |||
| @@ -1,11 +0,0 @@ | |||
| 1 | { config, flakeInputs, pkgs, lib, userName, customUtils, ... }: | ||
| 2 | { | ||
| 3 | home-manager.users.${userName} = {...}: { | ||
| 4 | imports = [ | ||
| 5 | (customUtils.overrideModuleArgs | ||
| 6 | (import ./module.nix) | ||
| 7 | (inputs: inputs // { inherit flakeInputs; inherit (config.nixpkgs) system; }) | ||
| 8 | ) | ||
| 9 | ]; | ||
| 10 | }; | ||
| 11 | } | ||
diff --git a/user-profiles/feeds/imm-notmuch-insert.py b/user-profiles/feeds/imm-notmuch-insert.py deleted file mode 100644 index b7eed292..00000000 --- a/user-profiles/feeds/imm-notmuch-insert.py +++ /dev/null | |||
| @@ -1,52 +0,0 @@ | |||
| 1 | #!@python@/bin/python | ||
| 2 | |||
| 3 | import json | ||
| 4 | import sys | ||
| 5 | import subprocess | ||
| 6 | from io import BytesIO | ||
| 7 | from email.message import EmailMessage | ||
| 8 | import configparser | ||
| 9 | from os import environ | ||
| 10 | from datetime import * | ||
| 11 | from dateutil.tz import * | ||
| 12 | from dateutil.parser import isoparse | ||
| 13 | from html2text import html2text | ||
| 14 | |||
| 15 | def main(): | ||
| 16 | notmuchConfig = configparser.ConfigParser() | ||
| 17 | notmuchConfig.read(environ.get('NOTMUCH_CONFIG')) | ||
| 18 | |||
| 19 | callbackMessage = json.load(sys.stdin) | ||
| 20 | |||
| 21 | msg = EmailMessage() | ||
| 22 | authors = ', '.join(map(lambda author: author['name'], callbackMessage['feed_item']['authors'])) | ||
| 23 | if authors: | ||
| 24 | msg['From'] = f"{callbackMessage['feed_definition']['title']} ({authors}) <imm@imm.invalid>" | ||
| 25 | else: | ||
| 26 | msg['From'] = f"{callbackMessage['feed_definition']['title']} <imm@imm.invalid>" | ||
| 27 | msg['To'] = f"{notmuchConfig['user']['name']} <{notmuchConfig['user']['primary_email']}>" | ||
| 28 | if 'title' in callbackMessage['feed_item'] and callbackMessage['feed_item']['title']: | ||
| 29 | msg['Subject'] = callbackMessage['feed_item']['title'] | ||
| 30 | msg['Item-Identifier'] = f"{callbackMessage['feed_item']['identifier']}" | ||
| 31 | for link in callbackMessage['feed_item']['links']: | ||
| 32 | msg.add_header('Link', link['uri']) | ||
| 33 | date = None | ||
| 34 | if 'date' in callbackMessage['feed_item']: | ||
| 35 | date = isoparse(callbackMessage['feed_item']['date']) | ||
| 36 | else: | ||
| 37 | date = datetime.now(tzlocal()) | ||
| 38 | msg['Date'] = date.strftime('%a, %e %b %Y %T %z') | ||
| 39 | |||
| 40 | if 'content' in callbackMessage['feed_item'] and callbackMessage['feed_item']['content']: | ||
| 41 | msg.set_content(html2text(callbackMessage['feed_item']['content'])) | ||
| 42 | msg.add_alternative(callbackMessage['feed_item']['content'], subtype='html') | ||
| 43 | |||
| 44 | |||
| 45 | subprocess.run( | ||
| 46 | args=['notmuch', 'insert'], | ||
| 47 | check=True, | ||
| 48 | input=bytes(msg) | ||
| 49 | ) | ||
| 50 | |||
| 51 | if __name__ == '__main__': | ||
| 52 | sys.exit(main()) | ||
diff --git a/user-profiles/feeds/module.nix b/user-profiles/feeds/module.nix deleted file mode 100644 index 63e827eb..00000000 --- a/user-profiles/feeds/module.nix +++ /dev/null | |||
| @@ -1,236 +0,0 @@ | |||
| 1 | { config, flakeInputs, pkgs, lib, system, ... }: | ||
| 2 | |||
| 3 | with lib; | ||
| 4 | |||
| 5 | let | ||
| 6 | inherit (flakeInputs.home-manager.lib) hm; | ||
| 7 | |||
| 8 | databasePath = "${config.xdg.dataHome}/feeds"; | ||
| 9 | |||
| 10 | imm = | ||
| 11 | let | ||
| 12 | hlib = pkgs.haskell.lib; | ||
| 13 | haskellPackages = pkgs.haskellPackages.override { | ||
| 14 | overrides = finalHaskell: prevHaskell: { | ||
| 15 | uri-bytestring = finalHaskell.callCabal2nix "uri-bytestring" (pkgs.fetchFromGitHub { | ||
| 16 | owner = "gkleen"; | ||
| 17 | repo = "uri-bytestring"; | ||
| 18 | rev = "5f7f32c8274bc4d1b81d99582f5148fe3e8b637e"; | ||
| 19 | sha256 = "XLanwyCDIlMuOkpE5LbTNOBfL+1kZX+URfj9Bhs1Nsc="; | ||
| 20 | fetchSubmodules = true; | ||
| 21 | }) {}; | ||
| 22 | atom-conduit = finalHaskell.callCabal2nix "atom-conduit" (pkgs.fetchFromGitHub { | ||
| 23 | owner = "gkleen"; | ||
| 24 | repo = "atom-conduit"; | ||
| 25 | rev = "022f0182a02373f87c06a0a09817c8c41efe2425"; | ||
| 26 | sha256 = "8yEyh3ymqkoM/YP+eBqPq1I5ofzj0Qn7ojL7IWx1DPo="; | ||
| 27 | fetchSubmodules = true; | ||
| 28 | }) {}; | ||
| 29 | rss-conduit = finalHaskell.callCabal2nix "rss-condit" (pkgs.fetchFromGitHub { | ||
| 30 | owner = "gkleen"; | ||
| 31 | repo = "rss-conduit"; | ||
| 32 | rev = "dbb0960a8d3dc519f1607aa0223b3a25a49282ef"; | ||
| 33 | sha256 = "Md1XApZWkdv4JvNoaVnjz0S85LbEC6w9U3PUcwXfu94="; | ||
| 34 | fetchSubmodules = true; | ||
| 35 | }) {}; | ||
| 36 | beam-core = hlib.doJailbreak (finalHaskell.callCabal2nix "beam-core" "${beamSrc}/beam-core" {}); | ||
| 37 | beam-migrate = hlib.doJailbreak (finalHaskell.callCabal2nix "beam-migrate" "${beamSrc}/beam-migrate" {}); | ||
| 38 | beam-sqlite = hlib.doJailbreak (finalHaskell.callCabal2nix "beam-sqlite" "${beamSrc}/beam-sqlite" {}); | ||
| 39 | |||
| 40 | imm = finalHaskell.callCabal2nix "imm" (pkgs.fetchFromGitHub { | ||
| 41 | owner = "k0ral"; | ||
| 42 | repo = "imm"; | ||
| 43 | rev = "5033879667264cb44cee65671a66f6aa43f249e7"; | ||
| 44 | sha256 = "PG22caLQmAGhLZP49HsazuNd8IFKKaTuhXIQBD8v4Fs="; | ||
| 45 | fetchSubmodules = true; | ||
| 46 | }) {}; | ||
| 47 | }; | ||
| 48 | }; | ||
| 49 | beamSrc = pkgs.fetchFromGitHub { | ||
| 50 | owner = "haskell-beam"; | ||
| 51 | repo = "beam"; | ||
| 52 | rev = "efd464b079755a781c2bb7a2fc030d6c141bbb8a"; | ||
| 53 | sha256 = "8nTuBP/vD0L/qMo4h3XNrGZvpIwXuMVdj40j5gvHU6w="; | ||
| 54 | fetchSubmodules = true; | ||
| 55 | }; | ||
| 56 | in haskellPackages.imm; | ||
| 57 | immWrapped = pkgs.runCommand "${imm.name}-wrapped-${config.home.username}" | ||
| 58 | { nativeBuildInputs = with pkgs; [ makeWrapper ]; | ||
| 59 | } '' | ||
| 60 | mkdir -p $out/bin | ||
| 61 | makeWrapper ${imm}/bin/imm $out/bin/imm \ | ||
| 62 | --add-flags --callbacks=${notmuchCallbacks} | ||
| 63 | ''; | ||
| 64 | |||
| 65 | notmuchCallbacks = pkgs.writeText "imm-callbacks-${config.home.username}.dhall" '' | ||
| 66 | [ { _executable = "${immNotmuchInsert}/bin/imm-notmuch-insert" | ||
| 67 | , _arguments = [] : List Text | ||
| 68 | } | ||
| 69 | ] | ||
| 70 | ''; | ||
| 71 | |||
| 72 | immNotmuchInsert = pkgs.stdenv.mkDerivation rec { | ||
| 73 | name = "imm-notmuch-insert-${config.home.username}"; | ||
| 74 | src = ./imm-notmuch-insert.py; | ||
| 75 | |||
| 76 | phases = [ "buildPhase" "checkPhase" "installPhase" "fixupPhase" ]; | ||
| 77 | |||
| 78 | python = pkgs.python39.withPackages (ps: with ps; [ configparser python-dateutil html2text ]); | ||
| 79 | |||
| 80 | nativeBuildInputs = with pkgs; [ makeWrapper ]; | ||
| 81 | |||
| 82 | buildPhase = '' | ||
| 83 | substituteAll $src imm-notmuch-insert | ||
| 84 | ''; | ||
| 85 | |||
| 86 | doCheck = true; | ||
| 87 | checkPhase = '' | ||
| 88 | ${python}/bin/python -m py_compile imm-notmuch-insert | ||
| 89 | ''; | ||
| 90 | |||
| 91 | installPhase = '' | ||
| 92 | install -m 0755 -D -t $out/bin \ | ||
| 93 | imm-notmuch-insert | ||
| 94 | ''; | ||
| 95 | |||
| 96 | fixupPhase = '' | ||
| 97 | wrapProgram $out/bin/imm-notmuch-insert \ | ||
| 98 | --prefix PATH : ${pkgs.notmuch}/bin \ | ||
| 99 | --set NOTMUCH_CONFIG ${configPath} | ||
| 100 | ''; | ||
| 101 | }; | ||
| 102 | |||
| 103 | mkIniKeyValue = key: value: | ||
| 104 | let | ||
| 105 | tweakVal = v: | ||
| 106 | if isString v then | ||
| 107 | v | ||
| 108 | else if isList v then | ||
| 109 | concatMapStringsSep ";" tweakVal v | ||
| 110 | else if isBool v then | ||
| 111 | (if v then "true" else "false") | ||
| 112 | else | ||
| 113 | toString v; | ||
| 114 | in "${key}=${tweakVal value}"; | ||
| 115 | |||
| 116 | notmuchIni = { | ||
| 117 | database = { path = databasePath; }; | ||
| 118 | |||
| 119 | maildir = { synchronize_flags = false; }; | ||
| 120 | |||
| 121 | new = { | ||
| 122 | ignore = []; | ||
| 123 | tags = ["new"]; | ||
| 124 | }; | ||
| 125 | |||
| 126 | user = { | ||
| 127 | name = config.home.username; | ||
| 128 | primary_email = "${config.home.username}@imm.invalid"; | ||
| 129 | }; | ||
| 130 | |||
| 131 | search = { exclude_tags = ["deleted"]; }; | ||
| 132 | }; | ||
| 133 | configPath = pkgs.writeText "notmuchrc" (generators.toINI { mkKeyValue = mkIniKeyValue; } notmuchIni); | ||
| 134 | |||
| 135 | afewConfigDir = pkgs.symlinkJoin { | ||
| 136 | name = "afew-config"; | ||
| 137 | paths = [ | ||
| 138 | (pkgs.writeTextDir "config" '' | ||
| 139 | [InboxFilter] | ||
| 140 | '') | ||
| 141 | ]; | ||
| 142 | }; | ||
| 143 | |||
| 144 | notmuchHooksDir = | ||
| 145 | let | ||
| 146 | afewHook = pkgs.writeShellScript "afew" '' | ||
| 147 | exec -- ${pkgs.afew}/bin/afew -c ${afewConfigDir} -C ${configPath} --tag --new -vv | ||
| 148 | ''; | ||
| 149 | in pkgs.linkFarm "notmuch-hooks" [ | ||
| 150 | { name = "post-new"; | ||
| 151 | path = afewHook; | ||
| 152 | } | ||
| 153 | { name = "post-insert"; | ||
| 154 | path = afewHook; | ||
| 155 | } | ||
| 156 | ]; | ||
| 157 | |||
| 158 | notmuchWrapped = pkgs.runCommand "${pkgs.notmuch.name}-wrapped-${config.home.username}" | ||
| 159 | { nativeBuildInputs = with pkgs; [ makeWrapper ]; | ||
| 160 | } '' | ||
| 161 | mkdir -p $out/bin | ||
| 162 | makeWrapper ${pkgs.notmuch}/bin/notmuch $out/bin/notmuch-feeds \ | ||
| 163 | --set NOTMUCH_CONFIG ${configPath} | ||
| 164 | ''; | ||
| 165 | alotWrapped = pkgs.runCommand "${pkgs.alot.name}-wrapped-${config.home.username}" | ||
| 166 | { nativeBuildInputs = with pkgs; [ makeWrapper gnused ]; | ||
| 167 | } '' | ||
| 168 | mkdir -p $out/bin | ||
| 169 | makeWrapper ${pkgs.alot}/bin/alot $out/bin/alot-feeds \ | ||
| 170 | --prefix MAILCAPS : ${alotMailcaps} \ | ||
| 171 | --add-flags --config=${alotConfig} \ | ||
| 172 | --add-flags --notmuch-config=${configPath} | ||
| 173 | |||
| 174 | mkdir $out/share | ||
| 175 | ln -s ${pkgs.alot}/share/alot $out/share | ||
| 176 | mkdir -p $out/share/applications | ||
| 177 | sed -r 's/alot/alot-feeds/g' ${pkgs.alot}/share/applications/alot.desktop > $out/share/applications/alot-feeds.desktop | ||
| 178 | mkdir -p $out/share/zsh/site-functions | ||
| 179 | sed -r 's/alot/alot-feeds/g' ${pkgs.alot}/share/zsh/site-functions/_alot > $out/share/zsh/site-functions/_alot-feeds | ||
| 180 | ''; | ||
| 181 | |||
| 182 | alotConfig = pkgs.runCommand "alot" { | ||
| 183 | realname = notmuchIni.user.name; | ||
| 184 | address = notmuchIni.user.primary_email; | ||
| 185 | } "substituteAll ${./alot.config} $out"; | ||
| 186 | alotMailcaps = pkgs.writeText "mailcaps" '' | ||
| 187 | text/html; ${pkgs.lynx}/bin/lynx -dump -dont_wrap_pre -assume_charset=utf-8 -display_charset=utf-8 "%s"; nametemplate=%s.html; copiousoutput | ||
| 188 | ''; | ||
| 189 | in { | ||
| 190 | config = { | ||
| 191 | home.packages = [ immWrapped notmuchWrapped pkgs.notmuch.man alotWrapped ]; | ||
| 192 | |||
| 193 | home.activation.createImm = hm.dag.entryAfter ["writeBoundary"] '' | ||
| 194 | $DRY_RUN_CMD mkdir -p $VERBOSE_ARG ${config.xdg.configHome}/imm | ||
| 195 | ''; | ||
| 196 | |||
| 197 | home.activation.createFeedsDatabase = hm.dag.entryAfter ["linkGeneration" "writeBoundary"] '' | ||
| 198 | $DRY_RUN_CMD mkdir -p -m 0750 $VERBOSE_ARG ${databasePath} | ||
| 199 | $DRY_RUN_CMD mkdir -p $VERBOSE_ARG ${databasePath}/new ${databasePath}/cur ${databasePath}/tmp | ||
| 200 | if ! [[ -d ${databasePath}/.notmuch ]]; then | ||
| 201 | NOTMUCH_VERBOSE_ARG="--quiet" | ||
| 202 | if [[ -v VERBOSE ]]; then | ||
| 203 | NOTMUCH_VERBOSE_ARG="--verbose" | ||
| 204 | fi | ||
| 205 | NOTMUCH_CONFIG=${configPath} $DRY_RUN_CMD ${pkgs.notmuch}/bin/notmuch new $NOTMUCH_VERBOSE_ARG | ||
| 206 | fi | ||
| 207 | $DRY_RUN_CMD ln -Tsf $VERBOSE_ARG ${notmuchHooksDir} ${databasePath}/.notmuch/hooks | ||
| 208 | ''; | ||
| 209 | |||
| 210 | systemd.user.services."logrotate-imm" = { | ||
| 211 | Unit = { | ||
| 212 | Description = "Rotate imm logfile"; | ||
| 213 | }; | ||
| 214 | Service = { | ||
| 215 | Type = "oneshot"; | ||
| 216 | ExecStart = '' | ||
| 217 | ${pkgs.logrotate}/bin/logrotate --state ${config.xdg.configHome}/imm/imm.logrotate ${pkgs.writeText "logrotate.conf" '' | ||
| 218 | ${config.xdg.configHome}/imm/imm.log { | ||
| 219 | rotate 5 | ||
| 220 | size 1024k | ||
| 221 | } | ||
| 222 | ''} | ||
| 223 | ''; | ||
| 224 | }; | ||
| 225 | }; | ||
| 226 | systemd.user.timers."logrotate-imm" = { | ||
| 227 | Timer = { | ||
| 228 | OnActiveSec = "6h"; | ||
| 229 | OnUnitActiveSec = "6h"; | ||
| 230 | }; | ||
| 231 | Install = { | ||
| 232 | WantedBy = ["default.target"]; | ||
| 233 | }; | ||
| 234 | }; | ||
| 235 | }; | ||
| 236 | } | ||
diff --git a/user-profiles/mpv/default.nix b/user-profiles/mpv/default.nix index 94f241c8..8cf330e8 100644 --- a/user-profiles/mpv/default.nix +++ b/user-profiles/mpv/default.nix | |||
| @@ -105,7 +105,8 @@ | |||
| 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 | ytdl-format = "ytdl"; |
| 109 | # ytdl-raw-options = "sub-langs=\"${config.programs.yt-dlp.settings.sub-langs}\""; | ||
| 109 | subs-with-matching-audio = false; | 110 | subs-with-matching-audio = false; |
| 110 | audio-display = false; | 111 | audio-display = false; |
| 111 | osd-font = "Fira Sans"; | 112 | osd-font = "Fira Sans"; |
diff --git a/user-profiles/tmux/default.nix b/user-profiles/tmux/default.nix index 11c53788..7ea0c0d5 100644 --- a/user-profiles/tmux/default.nix +++ b/user-profiles/tmux/default.nix | |||
| @@ -1,10 +1,11 @@ | |||
| 1 | { userName, pkgs, lib, ... }: | 1 | { userName, pkgs, lib, ... }: |
| 2 | { | 2 | { |
| 3 | home-manager.users.${userName} = { | 3 | home-manager.users.${userName} = { config, ... }: { |
| 4 | programs.tmux = { | 4 | programs.tmux = { |
| 5 | enable = true; | 5 | enable = true; |
| 6 | clock24 = true; | 6 | clock24 = true; |
| 7 | historyLimit = 50000; | 7 | historyLimit = 50000; |
| 8 | mouse = true; | ||
| 8 | extraConfig = lib.readFile (pkgs.stdenv.mkDerivation { | 9 | extraConfig = lib.readFile (pkgs.stdenv.mkDerivation { |
| 9 | name = "tmux.conf"; | 10 | name = "tmux.conf"; |
| 10 | src = ./tmux.conf; | 11 | src = ./tmux.conf; |
| @@ -13,11 +14,10 @@ | |||
| 13 | 14 | ||
| 14 | phases = [ "installPhase" ]; | 15 | phases = [ "installPhase" ]; |
| 15 | 16 | ||
| 16 | inherit (pkgs) zsh; | ||
| 17 | mandb = pkgs.man-db; | ||
| 18 | |||
| 19 | installPhase = '' | 17 | installPhase = '' |
| 20 | substituteAll $src $out | 18 | substitute $src $out \ |
| 19 | --subst-var-by zsh ${lib.getExe config.programs.zsh.package} \ | ||
| 20 | --subst-var-by man ${lib.getExe config.programs.man.package} | ||
| 21 | ''; | 21 | ''; |
| 22 | }); | 22 | }); |
| 23 | }; | 23 | }; |
diff --git a/user-profiles/tmux/tmux.conf b/user-profiles/tmux/tmux.conf index 415d13e7..9e658800 100644 --- a/user-profiles/tmux/tmux.conf +++ b/user-profiles/tmux/tmux.conf | |||
| @@ -1,23 +1,20 @@ | |||
| 1 | set-option -g history-limit 50000 | ||
| 2 | set-option -g status-bg black | 1 | set-option -g status-bg black |
| 3 | set-option -g status-fg white | 2 | set-option -g status-fg white |
| 4 | set-option -g clock-mode-colour white | 3 | set-option -g clock-mode-colour white |
| 5 | set-option -g clock-mode-style 24 | ||
| 6 | set-option -g bell-action any | 4 | set-option -g bell-action any |
| 7 | set-option -g default-shell @zsh@/bin/zsh | 5 | set-option -g default-shell @zsh@ |
| 8 | set-option -g update-environment 'DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY PROMPT_INFO PATH PGHOST PGLOG' | 6 | set-option -g update-environment 'DISPLAY SSH_ASKPASS SSH_AUTH_SOCK SSH_AGENT_PID SSH_CONNECTION WINDOWID XAUTHORITY PROMPT_INFO PATH PGHOST PGLOG' |
| 9 | set-option -g mouse on | ||
| 10 | set-option -g set-clipboard on | 7 | set-option -g set-clipboard on |
| 11 | set-option -g terminal-overrides 'rxvt-uni*:XT:Ms=\E]52;%p1%s;%p2%s\007' | 8 | set-option -g terminal-overrides 'rxvt-uni*:XT:Ms=\E]52;%p1%s;%p2%s\007' |
| 12 | 9 | ||
| 13 | set-environment -g LESS " -R " | 10 | set-environment -g LESS " -R " |
| 14 | 11 | ||
| 15 | ## determine if we should enable 256-colour support | 12 | ## determine if we should enable 256-colour support |
| 16 | if "[[ ''${TERM} =~ 256color || ''${TERM} == fbterm || ''${TERM} =~ alacritty ]]" 'set -g default-terminal tmux-256color' | 13 | if "[[ ''${TERM} =~ 256color || ''${TERM} == fbterm || ''${TERM} =~ alacritty || ''${TERM} =~ kitty ]]" 'set -g default-terminal tmux-256color' |
| 17 | 14 | ||
| 18 | set-option -g status-right "" | 15 | set-option -g status-right "" |
| 19 | 16 | ||
| 20 | bind / command-prompt "split-window -h 'exec @mandb@/bin/man %%'" | 17 | bind / command-prompt "split-window -h 'exec @man@ %%'" |
| 21 | bind C clock-mode | 18 | bind C clock-mode |
| 22 | bind r switch-client -r | 19 | bind r switch-client -r |
| 23 | 20 | ||
diff --git a/user-profiles/utils.nix b/user-profiles/utils.nix index 13eb6033..edf6da11 100644 --- a/user-profiles/utils.nix +++ b/user-profiles/utils.nix | |||
| @@ -1,19 +1,6 @@ | |||
| 1 | { userName, lib, pkgs, config, ... }: | 1 | { userName, lib, pkgs, config, ... }: |
| 2 | let | 2 | let |
| 3 | cfg = config.home-manager.users.${userName}; | 3 | cfg = config.home-manager.users.${userName}; |
| 4 | |||
| 5 | wrappedLess = pkgs.less.overrideAttrs (oldAttrs: { | ||
| 6 | pname = "${oldAttrs.pname or "less"}-wrapper"; | ||
| 7 | |||
| 8 | nativeBuildInputs = (oldAttrs.nativeBuildInputs or []) ++ (with pkgs; [makeWrapper]); | ||
| 9 | |||
| 10 | postInstall = '' | ||
| 11 | ${oldAttrs.postInstall or ""} | ||
| 12 | |||
| 13 | wrapProgram $out/bin/less \ | ||
| 14 | --prefix PATH : ${lib.makeBinPath (with pkgs; [binutils])} | ||
| 15 | ''; | ||
| 16 | }); | ||
| 17 | in { | 4 | in { |
| 18 | home-manager.users.${userName} = { | 5 | home-manager.users.${userName} = { |
| 19 | programs = { | 6 | programs = { |
| @@ -55,19 +42,25 @@ in { | |||
| 55 | }; | 42 | }; |
| 56 | 43 | ||
| 57 | jq.enable = true; | 44 | jq.enable = true; |
| 45 | |||
| 46 | lesspipe.enable = true; | ||
| 47 | |||
| 48 | man.enable = true; | ||
| 49 | |||
| 50 | vim.enable = true; | ||
| 58 | }; | 51 | }; |
| 59 | 52 | ||
| 60 | home.sessionVariables = { | 53 | home.sessionVariables = { |
| 61 | LESSCOLORIZER = "pygmentize -O style=rrt"; | 54 | LESSCOLORIZER = "${lib.getExe' pkgs.python3Packages.pygments "pygmentize"} -O style=rrt"; |
| 62 | }; | 55 | }; |
| 63 | 56 | ||
| 64 | home.packages = with pkgs; [ | 57 | home.packages = with pkgs; [ |
| 65 | autossh usbutils pciutils eza silver-searcher pwgen xkcdpass | 58 | autossh usbutils pciutils eza silver-searcher pwgen xkcdpass |
| 66 | unzip magic-wormhole qrencode tty-clock dnsutils openssl sshfs | 59 | unzip magic-wormhole dnsutils openssl sshfs |
| 67 | psmisc mosh tree vnstat file pv bc zip nmap aspell | 60 | psmisc mosh tree vnstat file pv bc zip nmap aspell |
| 68 | aspellDicts.de aspellDicts.en borgbackup man-pages rsync socat | 61 | aspellDicts.de aspellDicts.en borgbackup man-pages rsync socat |
| 69 | inetutils yq cached-nix-shell persistent-nix-shell rage | 62 | inetutils yq cached-nix-shell persistent-nix-shell rage |
| 70 | smartmontools hdparm nix-output-monitor wrappedLess dscp | 63 | smartmontools hdparm nix-output-monitor less dscp |
| 71 | iputils | 64 | iputils |
| 72 | ]; | 65 | ]; |
| 73 | }; | 66 | }; |
diff --git a/user-profiles/yt-dlp.nix b/user-profiles/yt-dlp.nix index 9e77f734..eefa673f 100644 --- a/user-profiles/yt-dlp.nix +++ b/user-profiles/yt-dlp.nix | |||
| @@ -7,17 +7,26 @@ | |||
| 7 | cookies-from-browser = "firefox::none"; | 7 | cookies-from-browser = "firefox::none"; |
| 8 | mark-watched = true; | 8 | mark-watched = true; |
| 9 | format = lib.concatStringsSep "/" [ | 9 | format = lib.concatStringsSep "/" [ |
| 10 | "bestvideo*[width<=2560][height<=1440][fps<=60][vcodec!*=av01][width>=1920]+bestaudio" | ||
| 11 | "best[width<=2560][height<=1440][fps<=60][vcodec!*=av01][width>=1920]" | ||
| 12 | "bestvideo*[vcodec!*=av01][width>=1920]+bestaudio" | ||
| 13 | "best[vcodec!*=av01][width>=1920]" | ||
| 14 | "bestvideo*[width<=2560][height<=1440][fps<=60][vcodec!*=av01][height>=1080]+bestaudio" | ||
| 15 | "best[width<=2560][height<=1440][fps<=60][vcodec!*=av01][height>=1080]" | ||
| 16 | "bestvideo*[vcodec!*=av01][height>=1080]+bestaudio" | ||
| 17 | "best[vcodec!*=av01][height>=1080]" | ||
| 10 | "bestvideo*[width<=2560][height<=1440][fps<=60]+bestaudio" | 18 | "bestvideo*[width<=2560][height<=1440][fps<=60]+bestaudio" |
| 11 | "best[width<=2560][height<=1440][fps<=60]" | 19 | "best[width<=2560][height<=1440][fps<=60]" |
| 12 | "bestvideo*+bestaudio" | 20 | "bestvideo*+bestaudio" |
| 13 | "best" | 21 | "best" |
| 14 | ]; | 22 | ]; |
| 15 | embed-subs = true; | 23 | # embed-subs = true; |
| 24 | embed-thumbnail = true; | ||
| 25 | embed-metadata = true; | ||
| 16 | # write-subs = true; | 26 | # write-subs = true; |
| 17 | write-auto-subs = true; | 27 | # write-auto-subs = true; |
| 18 | sub-langs = "en(-(gb|us|orig))?,de(-(de|orig))?,-live_chat,-rechat"; | 28 | # sub-langs = "en(-(gb|us|orig))?,de(-(de|orig))?,-live_chat,-rechat"; |
| 19 | prefer-free-formats = true; | 29 | prefer-free-formats = true; |
| 20 | embed-metadata = true; | ||
| 21 | # downloader = "${pkgs.axel}/bin/axel"; | 30 | # downloader = "${pkgs.axel}/bin/axel"; |
| 22 | concurrent-fragments = 12; | 31 | concurrent-fragments = 12; |
| 23 | buffer-size = "16K"; | 32 | buffer-size = "16K"; |
| @@ -28,7 +37,8 @@ | |||
| 28 | # "youtube:formats=dashy" | 37 | # "youtube:formats=dashy" |
| 29 | # ]; | 38 | # ]; |
| 30 | remux-video = "mp4>mkv"; | 39 | remux-video = "mp4>mkv"; |
| 31 | output = "\"%(title)s [%(uploader)s %(webpage_url)s].%(ext)s\""; | 40 | output = lib.mkDefault "\"%(modified_date>%Y%m%d,release_date>%Y%m%d,upload_date>%Y%m%d)s %(title)s [%(uploader)s %(webpage_url)s].%(ext)s\""; |
| 41 | audio-multistreams = true; | ||
| 32 | }; | 42 | }; |
| 33 | }; | 43 | }; |
| 34 | }; | 44 | }; |
diff --git a/user-profiles/zsh/default.nix b/user-profiles/zsh/default.nix index 428e2459..ab523a52 100644 --- a/user-profiles/zsh/default.nix +++ b/user-profiles/zsh/default.nix | |||
| @@ -1,38 +1,72 @@ | |||
| 1 | { userName, pkgs, customUtils, lib, config, ... }: | 1 | { userName, pkgs, customUtils, lib, config, ... }: |
| 2 | let | 2 | { |
| 3 | dotDir = ".config/zsh"; | 3 | config = { |
| 4 | p10kZsh = "${dotDir}/.p10k.zsh"; | 4 | home-manager.users.${userName} = let sysConfig = config; in { config, ... }: { |
| 5 | cfg = config.home-manager.users.${userName}; | 5 | config = { |
| 6 | in { | 6 | programs.zsh = { |
| 7 | home-manager.users.${userName} = { | 7 | dotDir = ".config/zsh"; |
| 8 | programs.zsh = { | 8 | enable = true; |
| 9 | inherit dotDir; | 9 | autocd = true; |
| 10 | enable = true; | 10 | enableCompletion = true; |
| 11 | autocd = true; | 11 | enableVteIntegration = true; |
| 12 | enableCompletion = true; | 12 | history = { |
| 13 | append = true; | ||
| 14 | expireDuplicatesFirst = true; | ||
| 15 | extended = true; | ||
| 16 | findNoDups = true; | ||
| 17 | }; | ||
| 18 | syntaxHighlighting.enable = true; | ||
| 19 | zsh-abbr = { | ||
| 20 | enable = true; | ||
| 21 | abbreviations = { | ||
| 22 | re = "systemctl restart"; | ||
| 23 | ure = "systemctl --user restart"; | ||
| 24 | st = "systemctl status"; | ||
| 25 | ust = "systemctl --user status"; | ||
| 26 | }; | ||
| 27 | globalAbbreviations = { | ||
| 28 | "L" = "| less"; | ||
| 29 | "S" = "&> /dev/null"; | ||
| 30 | "G" = "| grep"; | ||
| 31 | "B" = "&> /dev/null &"; | ||
| 32 | "BB" = "&> /dev/null &!"; | ||
| 33 | "J" = lib.mkIf config.programs.jq.enable "| jq '.'"; | ||
| 34 | }; | ||
| 35 | }; | ||
| 13 | 36 | ||
| 14 | plugins = [ | 37 | plugins = [ |
| 15 | { name = "powerlevel10k"; | 38 | { name = "powerlevel10k"; |
| 16 | file = "share/zsh-powerlevel10k/powerlevel10k.zsh-theme"; | 39 | file = "share/zsh-powerlevel10k/powerlevel10k.zsh-theme"; |
| 17 | src = pkgs.zsh-powerlevel10k; | 40 | src = pkgs.zsh-powerlevel10k; |
| 18 | } | 41 | } |
| 19 | ]; | 42 | ]; |
| 20 | initExtraFirst = '' | 43 | initContent = lib.mkMerge [ |
| 21 | if [[ $TERM == "dumb" ]]; then | 44 | (lib.mkBefore '' |
| 22 | unsetopt zle | 45 | if [[ $TERM == "dumb" ]]; then |
| 23 | PS1='$ ' | 46 | unsetopt zle |
| 24 | return | 47 | PS1='$ ' |
| 25 | fi | 48 | return |
| 26 | ''; | 49 | fi |
| 27 | initExtraBeforeCompInit = '' | 50 | '') |
| 28 | source "${cfg.home.homeDirectory}/${p10kZsh}" | 51 | (lib.mkOrder 550 '' |
| 29 | ''; | 52 | source "$HOME/${config.xdg.configFile."zsh/.p10k.zsh".target}" |
| 30 | initExtra = lib.mkAfter '' | 53 | '') |
| 31 | source ${./zshrc} | 54 | (lib.mkAfter '' |
| 32 | source "${pkgs.zsh-syntax-highlighting}/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" | 55 | source ${./zshrc} |
| 33 | ''; | 56 | '') |
| 57 | ]; | ||
| 58 | }; | ||
| 59 | |||
| 60 | xdg.configFile."zsh/.p10k.zsh".source = ./p10k.zsh; | ||
| 61 | }; | ||
| 34 | }; | 62 | }; |
| 35 | 63 | ||
| 36 | home.file.${p10kZsh}.source = ./p10k.zsh; | 64 | programs.zsh.enable = true; |
| 65 | environment.pathsToLink = [ "/share/zsh" ]; | ||
| 66 | environment.shellAliases = lib.mkOverride 90 {}; | ||
| 67 | |||
| 68 | nixpkgs.externalConfig.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [ | ||
| 69 | "zsh-abbr" | ||
| 70 | ]; | ||
| 37 | }; | 71 | }; |
| 38 | } | 72 | } |
diff --git a/user-profiles/zsh/zshrc b/user-profiles/zsh/zshrc index ed614182..af3aca64 100644 --- a/user-profiles/zsh/zshrc +++ b/user-profiles/zsh/zshrc | |||
| @@ -33,9 +33,3 @@ zle -N self-insert url-quote-magic | |||
| 33 | zle -N bracketed-paste bracketed-paste-magic | 33 | zle -N bracketed-paste bracketed-paste-magic |
| 34 | 34 | ||
| 35 | setopt extended_glob | 35 | setopt extended_glob |
| 36 | |||
| 37 | alias -g L='| less' | ||
| 38 | alias -g S='&> /dev/null' | ||
| 39 | alias -g G='| grep' | ||
| 40 | alias -g B='&> /dev/null &' | ||
| 41 | alias -g BB='&> /dev/null &!' | ||
diff --git a/users/gkleen/default.nix b/users/gkleen/default.nix index 5cc32521..5ce93de7 100644 --- a/users/gkleen/default.nix +++ b/users/gkleen/default.nix | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | { flake, userName, pkgs, customUtils, lib, ... }: | 1 | { flake, userName, pkgs, customUtils, lib, ... }: |
| 2 | { | 2 | { |
| 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 3 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 4 | zsh tmux utils direnv | 4 | utils direnv |
| 5 | ]; | 5 | ]; |
| 6 | 6 | ||
| 7 | users.users.${userName} = { | 7 | users.users.${userName} = { |
diff --git a/users/root.nix b/users/root.nix index b61f9cfd..ed1acd50 100644 --- a/users/root.nix +++ b/users/root.nix | |||
| @@ -3,7 +3,7 @@ let | |||
| 3 | haveGKleen = flake.nixosModules.accounts ? "gkleen@${hostName}"; | 3 | haveGKleen = flake.nixosModules.accounts ? "gkleen@${hostName}"; |
| 4 | in { | 4 | in { |
| 5 | imports = with flake.nixosModules.userProfiles.${userName}; [ | 5 | imports = with flake.nixosModules.userProfiles.${userName}; [ |
| 6 | zsh tmux direnv utils | 6 | direnv utils |
| 7 | ]; | 7 | ]; |
| 8 | 8 | ||
| 9 | users.users.${userName} = lib.mkIf haveGKleen { | 9 | users.users.${userName} = lib.mkIf haveGKleen { |
