aboutsummaryrefslogtreecommitdiff
path: root/tw/home.scm
blob: 990c4c24b2b6ec5436566e751a0b7ad2c175003a (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
(define-module (tw home)
  #:use-module (ice-9 match)
  #:use-module (ice-9 string-fun)
  #:use-module (gnu)
  #:use-module (gnu home services)
  #:use-module (gnu home services desktop)
  #:use-module (gnu home services mail)
  #:use-module (gnu home services mcron)
  #:use-module (gnu home services shells)
  #:use-module (gnu home services shepherd)
  #:use-module (gnu home services ssh)
  #:use-module (gnu packages admin)
  #:use-module (gnu packages aidc)
  #:use-module (gnu packages android)
  #:use-module (gnu packages backup)
  #:use-module (gnu packages base)
  #:use-module (gnu packages bittorrent)
  #:use-module (gnu packages calendar)
  #:use-module (gnu packages check)
  #:use-module (gnu packages chromium)
  #:use-module (gnu packages cmake)
  #:use-module (gnu packages compression)
  #:use-module (gnu packages compton)
  #:use-module (gnu packages curl)
  #:use-module (gnu packages databases)
  #:use-module (gnu packages dav)
  #:use-module (gnu packages disk)
  #:use-module (gnu packages dns)
  #:use-module (gnu packages elf)
  #:use-module (gnu packages emacs)
  #:use-module (gnu packages emacs-xyz)
  #:use-module (gnu packages file)
  #:use-module (gnu packages finance)
  #:use-module (gnu packages fonts)
  #:use-module (gnu packages freedesktop)
  #:use-module (gnu packages games)
  #:use-module (gnu packages gcc)
  #:use-module (gnu packages gimp)
  #:use-module (gnu packages gl)
  #:use-module (gnu packages gnome)
  #:use-module (gnu packages gnome-xyz)
  #:use-module (gnu packages gnupg)
  #:use-module (gnu packages gnuzilla)
  #:use-module (gnu packages graphviz)
  #:use-module (gnu packages haskell-apps)
  #:use-module (gnu packages image-viewers)
  #:use-module (gnu packages imagemagick)
  #:use-module (gnu packages inkscape)
  #:use-module (gnu packages less)
  #:use-module (gnu packages libreoffice)
  #:use-module (gnu packages linux)
  #:use-module (gnu packages lisp)
  #:use-module (gnu packages llvm)
  #:use-module (gnu packages mail)
  #:use-module (gnu packages maths)
  #:use-module (gnu packages messaging)
  #:use-module (gnu packages music)
  #:use-module (gnu packages ncdu)
  #:use-module (gnu packages package-management)
  #:use-module (gnu packages password-utils)
  #:use-module (gnu packages pdf)
  #:use-module (gnu packages pretty-print)
  #:use-module (gnu packages pulseaudio)
  #:use-module (gnu packages pv)
  #:use-module (gnu packages python)
  #:use-module (gnu packages python-build)
  #:use-module (gnu packages python-check)
  #:use-module (gnu packages python-xyz)
  #:use-module (gnu packages rsync)
  #:use-module (gnu packages shells)
  #:use-module (gnu packages shellutils)
  #:use-module (gnu packages sqlite)
  #:use-module (gnu packages ssh)
  #:use-module (gnu packages syndication)
  #:use-module (gnu packages tcl)
  #:use-module (gnu packages terminals)
  #:use-module (gnu packages tex)
  #:use-module (gnu packages textutils)
  #:use-module (gnu packages tls)
  #:use-module (gnu packages tmux)
  #:use-module (gnu packages tree-sitter)
  #:use-module (gnu packages version-control)
  #:use-module (gnu packages video)
  #:use-module (gnu packages vim)
  #:use-module (gnu packages web)
  #:use-module (gnu packages web-browsers)
  #:use-module (gnu packages wm)
  #:use-module (gnu packages xdisorg)
  #:use-module (gnu packages xfce)
  #:use-module (gnu packages xorg)
  #:use-module (gnu services configuration)
  #:use-module (guix gexp)
  #:use-module (guix packages)
  #:use-module (guix records)
  #:use-module ((nongnu packages game-client)
                #:select (steam steam-nvidia))
  #:use-module ((nongnu packages messaging)
                #:select (zoom signal-desktop))
  #:use-module ((nongnu packages nvidia)
                #:select (nvda nvidia-system-monitor))
  #:use-module (tw gexp)
  #:use-module (tw packages alice)
  #:use-module (tw packages catppuccin)
  #:use-module (tw packages ci)
  #:use-module (tw packages games)
  #:use-module (tw packages mail)
  #:use-module (tw packages scanner)
  #:use-module (tw packages xorg)
  #:use-module (tw services restic)
  #:use-module (tw system)
  #:use-module (tw theme))

(export tw-home-configuration)
(define-configuration/no-serialization tw-home-configuration
  (x11-desktop? (boolean #t) "Install a full desktop and development
environment.  This is based on X11; a Wayland environment may be provided in
future.")
  (gaming? (boolean #f) "Install games and other packages useful for gaming?")
  (nvidia-driver? (boolean #f) "Adapt the X11 desktop for the proprietary
NVIDIA driver?")
  (pim? (boolean #t) "Install Personal Information Management (PIM) software
like a calendar and mail reader?"))

(define (home-packages config)
  "Install packages I use frequently."
  (match-record config <tw-home-configuration> (x11-desktop? gaming? nvidia-driver? pim?)
    (append
     ;; Basic packages to install everywhere, including servers.
     (list curl diffstat dos2unix fdupes file git gnupg gnuplot graphviz hledger
           imagemagick jq less lesspipe ncdu net-tools openssh openssl password-store
           pass-otp pv python python-ipython recutils restic rsync sbcl smartmontools
           source-highlight tmux tree xxd zip unzip
           adb fastboot beets cmus get-iplayer ffmpeg atomicparsley yt-dlp

           ;; Install only bind-utils like dig, not the full suite.
           (list isc-bind "utils")

           ;; Ranger can do code highlighting using python-pygments and
           ;; image previews in kitty using python-pillow.
           ranger python-pygments python-pillow mediainfo python-pdftotext

           ;; Shell
           zsh zsh-autosuggestions zsh-history-substring-search
           zsh-syntax-highlighting zsh-completions)

     (cond
      ;; Games are installed in ~/.guix-profile instead, to make updates of the
      ;; home environment faster.
      ;; 0ad flightgear freeciv simutrans/pak128 warzone2100 widelands pioneer
      ((and gaming? (not nvidia-driver?))
       (list steam radeontop))
      ((and gaming? nvidia-driver?)
       ;; nvidia-system-monitor: Qt; installs "qnvsm" binary, but no .desktop file
       (list steam-nvidia nvidia-system-monitor))
      (else '()))

     (if x11-desktop?
         (list
          ;; CLI tools
          bsd-games powertop (list git "send-email") pdsh qrencode
          texlive-scheme-small texlive-latexmk
          ;; For CV:
          texlive-moderncv texlive-fontawesome5 texlive-multirow texlive-arydshln
          texlive-libertine texlive-inconsolata texlive-newtx texlive-babel
          texlive-csquotes texlive-siunitx
          ;; For org-mode export:
          texlive-minted texlive-svg texlive-hyperref texlive-capt-of texlive-ulem
          texlive-trimspaces texlive-transparent texlive-graphics texlive-tools
          texlive-wrapfig texlive-amsmath texlive-amsfonts texlive-libertine
          texlive-newtx texlive-txfonts texlive-inconsolata

          ;; Work
          s3cmd python-alibuild python-alidistlint actionlint modules
          python-tox python-setuptools-scm python-mypy
          hashicorp-levant-bin hashicorp-nomad-bin
          hashicorp-consul-bin hashicorp-vault-bin hashicorp-packer-bin
          ansible vinagre

          ;; i3 and Xorg. i3 itself must be installed system-wide for gdm to pick it up.
          ;; acpilight is a drop-in xbacklight replacement, as xbacklight doesn't work on my system.
          acpilight arandr dunst gimp hsetroot inkscape icecat imv kitty libreoffice
          mpv polybar pulsemixer rofi rofi-calc simple-scan/airscan
          transmission-remote-gtk tk xdg-utils xdot xclip xcwd xdotool
          xdpyinfo xev xfd xfontsel xinput xkill xprop xrandr xrdb xsel xset xwininfo
          xfce4-screenshooter zoom zathura zathura-ps zathura-pdf-poppler
          ungoogled-chromium  ; needed e.g. for Interrail site & DRM video
          dconf dconf-editor  ; required for config by blueman, cozy, ...
          ;; gnome-keyring  ; installed system-wide; see system-configuration.scm
          ;; geoclue  ; for redshift -- installed system-wide
          ;; Fonts
          font-hermit font-inconsolata font-fira-code font-fira-sans font-libertinus
          ;; Base Noto doesn't include CJK, so install those separately.
          font-google-noto font-google-noto-sans-cjk font-google-noto-serif-cjk
          font-google-noto-emoji font-openmoji  ; for polybar
          ;; Theming
          papirus-icon-theme catppuccin-gtk-theme catppuccin-mocha-dark-cursors
          ;; Games (larger games installed in ~/.guix-profile to avoid frequent huge downloads).
          szio-solitaire

          ;; Development & language servers
          gnu-make python-lsp-server python-yamllint shellcheck gcc binutils patchelf elfutils
          clang  ; for clangd
          glibc  ; for ldd
          ;; Supported OotB by eglot, but not packaged by guix:
          ;; https://github.com/mads-hartmann/bash-language-server
          ;; https://github.com/regen100/cmake-language-server
          ;; https://github.com/hrsh7th/vscode-langservers-extracted   ; {html,css,json}-languageserver
          ;; https://github.com/golang/tools/tree/master/gopls         ; maybe?
          ;; https://github.com/artempyanykh/marksman                  ; Markdown
          ;; https://github.com/astoff/digestif                        ; (La)TeX
          ;; https://github.com/redhat-developer/yaml-language-server
          ;; Needs eglot config + not packaged (from lsp-mode):
          ;; https://github.com/graphql/graphiql/tree/main/packages/graphql-language-service-cli#readme
          ;; https://github.com/haskell/haskell-language-server / https://github.com/haskell/ghcide
          ;; https://github.com/eclipse/lemminx

          ;; Emacs general
          emacs   ; for Wayland, switch to emacs-pgtk for better fractional scaling support
          emacs-use-package
          emacs-gcmh
          emacs-eglot
          emacs-counsel
          emacs-counsel-dash sqlite  ; emacs-counsel-dash requires the sqlite3 binary
          emacs-ivy
          ;; emacs-company emacs-company-quickhelp emacs-company-posframe
          emacs-corfu emacs-corfu-doc
          emacs-undo-tree
          emacs-aggressive-indent
          emacs-which-key
          emacs-smart-mode-line
          emacs-diminish
          emacs-rainbow-mode
          emacs-form-feed
          emacs-guix
          ;; TODO: emacs-editorconfig emacs-sly emacs-sly-macrostep
          emacs-org   ; emacs-org-modern theme?

          ;; Emacs Evil
          emacs-evil
          emacs-evil-collection
          emacs-evil-expat  ; for :reverse, :remove, :rename, :colo, :g*, ... ex commands
          emacs-evil-surround
          ;; emacs-evil-owl  ; tests failing
          emacs-evil-args
          emacs-evil-numbers
          emacs-evil-multiedit
          emacs-evil-goggles
          emacs-evil-traces
          emacs-evil-commentary
          emacs-evil-replace-with-register
          emacs-evil-cleverparens
          emacs-evil-org
          emacs-evil-markdown
          emacs-evil-tex
          emacs-evil-text-object-python

          ;; Emacs language modes
          emacs-flymake-collection
          emacs-geiser emacs-geiser-guile
          emacs-sly
          emacs-gnuplot
          emacs-graphviz-dot-mode
          emacs-haskell-mode
          emacs-hcl-mode
          emacs-ledger-mode
          emacs-mmm-mode
          emacs-puppet-mode
          emacs-rec-mode
          emacs-web-mode
          emacs-yaml-mode

          ;; Tree sitter libraries, for Emacs' built-in X-ts-modes.
          tree-sitter-bash
          tree-sitter-c
          tree-sitter-cmake
          tree-sitter-cpp
          tree-sitter-css
          tree-sitter-dockerfile
          tree-sitter-javascript
          tree-sitter-json
          tree-sitter-python
          tree-sitter-ruby)

         ;; In non-graphical environments, install vim as an editor.  Neovim
         ;; might be better, but doesn't have an equivalent to `vim-surround'
         ;; packaged.
         (list vim vim-surround))

     (if pim?
         (list nheko signal-desktop
               newsboat vdirsyncer khal khard aerc lynx mutt_oauth2.py)   ; lynx for HTML mail
         '()))))

(define (zshrc config)
  "Extend the home ZSH service to install my custom zshrc."
  ;; `home-zsh-configuration's are merged, so we can extend `home-zsh-service-type'.
  (home-zsh-extension
   (zshrc (list (local-file "home/files/zshrc")
                (local-file "home/files/prompt.zsh")))))

(define xfce4-screenshooter.conf
  (mixed-text-file "xfce4-screenshooter.conf" "\
app=" imv "/bin/imv
custom_action_command=none
last_user=
last_extension=png
screenshot_dir=file:/home/timo/pictures
enable_imgur_upload=false
show_in_folder=false
action=1
delay=0
region=3
show_mouse=0
show_border=1
"))

(define (xdg-configs config)
  "Configuration files that follow the XDG basedir spec."
  (match-record config <tw-home-configuration> (x11-desktop? pim?)
    `(;; Basic configuration files.
      ("htop/htoprc" ,(local-file "home/files/htoprc"))
      ("lesskey" ,(local-file "home/files/lesskey"))
      ("ranger/rc.conf" ,(local-file "home/files/ranger.conf"))
      ("tmux/tmux.conf" ,(local-file "home/files/tmux.conf"))
      ("user-dirs.locale" ,(plain-file "user-dirs.locale" "C"))  ; Not sure if this is needed. Arch has it.
      ("user-dirs.dirs" ,(local-file "home/files/user-dirs.dirs"))

      ,@(if x11-desktop?
            `(;; Configuration files for terminal-only programs in $XDG_CONFIG_HOME.
              ("alibuild/disable-analytics"    ; All alibuild needs is an empty file.
               ,(plain-file "alibuild-disable-analytics" ""))
              ;; Emacs is only used in graphical sessions.
              ("emacs/include" ,(local-file "home/files/emacs-packages" #:recursive? #t))
              ("emacs/init.el" ,(local-file "home/files/emacs-init.el"))
              ("emacs/catppuccin-theme.el" ,catppuccin-emacs)
              ;; X11 environment configuration.
              ("X11/XCompose" ,(local-file "home/files/XCompose"))   ; see also: $XCOMPOSEFILE variable
              ("X11/Xresources" ,(local-file "home/files/Xresources"))
              ;; Configuration files for GUI programs in $XDG_CONFIG_HOME.  Some
              ;; of these may also work under Wayland, but some are X11-specific.
              ("dunst/dunstrc" ,(local-file "home/files/dunstrc"))
              ("dunst/dunstrc.d/50-catppuccin.conf" ,catppuccin-dunstrc)
              ("gtk-2.0/gtkrc" ,(local-file "home/files/gtk2.ini"))
              ("gtk-3.0/settings.ini" ,(local-file "home/files/gtk3.ini"))
              ("i3/config" ,(local-file "home/files/i3.conf"))
              ;; TODO: "kdeglobals" works for some programs (e.g. kdeconnect-app),
              ;; but not for others (e.g. nheko, kdeconnect-settings)...
              ("kdeglobals" ,catppuccin-kdeglobals)
              ("kitty/diff.conf"
               ,(combined-text-file "kitty-diff.conf"
                  (plain-file "kitty-diff-custom.conf"
                    "pygments_style bw\n")
                  catppuccin-kitty-diff))
              ("kitty/kitty.conf"
               ,(combined-text-file "kitty.conf"
                  (local-file "home/files/kitty.conf")
                  catppuccin-kitty))
              ("mimeapps.list" ,(local-file "home/files/mimeapps.list"))
              ("rofi/config.rasi" ,(local-file "home/files/rofi.rasi"))
              ("rofi/themes/catppuccin.rasi" ,catppuccin-rofi)
              ("xfce4/xfce4-screenshooter" ,xfce4-screenshooter.conf)
              ("zathura/zathurarc" ,(local-file "home/files/zathurarc"))
              ("zathura/catppuccin" ,catppuccin-zathura))
            '())

      ,@(if pim?
            `(("khal/config" ,(local-file "home/files/khal.conf"))
              ("khard/khard.conf" ,(local-file "home/files/khard.conf"))
              ("aerc/accounts.conf" ,(local-file "home/files/aerc/accounts.conf"))
              ("aerc/aerc.conf" ,(local-file "home/files/aerc/aerc.conf"))
              ("aerc/binds.conf" ,(local-file "home/files/aerc/binds.conf"))
              ("aerc/filters" ,(local-file "home/files/aerc/filters" #:recursive? #t))
              ("aerc/stylesets" ,(local-file "home/files/aerc/stylesets" #:recursive? #t))
              ("newsboat/config" ,(local-file "home/files/newsboat.conf"))
              ("newsboat/config.catppuccin" ,catppuccin-newsboat)
              ("vdirsyncer/config" ,(local-file "home/files/vdirsyncer.conf")))
            '()))))

(define (non-xdg-configs config)
  "Extra configuration files and binaries that don't follow the XDG spec."
  (match-record config <tw-home-configuration> (x11-desktop?)
    `((".infokey" ,(local-file "home/files/infokey"))
      (".local/bin/python" ,(file-append python "/bin/python3"))
      ;; With #:recursive? #t, Guix keeps the files' permission bits, i.e. makes them executable.
      (".local/bin/ppscm" ,(local-file "home/files/ppscm" #:recursive? #t))  ; pretty-print scheme files

      ,@(if x11-desktop?
            `((".icons/default/index.theme" ,(local-file "home/files/cursors.ini"))
              ;; https://sw.kovidgoyal.net/kitty/kittens/diff/
              (".local/bin/kdiff"  ; show a diff
               ,(program-file "kdiff"
                  #~(apply execl #$(file-append kitty "/bin/kitty") "kitty"
                           "+kitten" "diff" (cdr (command-line)))))
              (".local/bin/icat"   ; kitty's "catimg" equivalent
               ,(program-file "icat"
                  #~(apply execl #$(file-append kitty "/bin/kitty") "kitty"
                           "+kitten" "icat" (cdr (command-line)))))
              (".local/bin/screenlock"
               ,(program-file "screenlock"
                  #~(begin  ; Wrapper around i3lock to turn off the screen and pause notifications.
                      (system* #$(file-append dunst "/bin/dunstctl") "set-paused" "true")
                      (system* #$(file-append xset "/bin/xset") "dpms" "0" "0" "5")
                      ;; We mustn't use `file-append' here, as we have to pick up the
                      ;; setuid binary for i3lock installed by the system config.
                      (system* "i3lock" "-nc" #$catppuccin-background-color)
                      (system* #$(file-append xset "/bin/xset") "dpms" "600" "600" "600")
                      (system* #$(file-append dunst "/bin/dunstctl") "set-paused" "false"))))
              ;; With #:recursive? #t, Guix keeps the files' permission bits, i.e. makes them executable.
              (".local/bin/sessionmenu" ,(local-file "home/files/sessionmenu" #:recursive? #t))
              (".local/bin/passmenu" ,(local-file "home/files/passmenu" #:recursive? #t))
              (".local/bin/volume" ,(local-file "home/files/volume" #:recursive? #t))
              (".local/bin/alienv.guix"
               ,(program-file "alienv.guix"
                  #~(begin
                      (setenv "TERM" "xterm-256color")  ; "modules" gets confused if this is unset
                      (apply execlp "guix" "guix" "shell" "--pure" "--container" "--emulate-fhs" "--preserve=^TERM$"
                             "bash" "util-linux" "coreutils"   ; basic shell utilities (also needed by alienv)
                             "which" "sed" "grep" "findutils" "procps" "gawk" "modules"  ; alienv requirements
                             "openssl@3"  ; some packages need openssl
                             "xz"   ; XRootD needs liblzma
                             "python-alibuild" "--" "alienv" (cdr (command-line))))))
              (".local/share/applications/emacsclient.desktop"
               ,(local-file "home/files/emacsclient.desktop")))

            ;; In non-graphical environments, set up vim as my preferred editor.
            `((".vim/vimrc" ,(local-file "home/files/vimrc"))
              (".vim/catppuccin.vim" ,catppuccin-vim))))))

(define (environment-variables config)
  "Configure my shell environment."
  (match-record config <tw-home-configuration> (x11-desktop?)
    `(;; Work at ALICE: Nomad, Consul, Vault.
      ;; Tokens are not defined here as they are stored in pass(1).
      ;; Shell aliases to use tokens are defined in zshrc.
      ("NOMAD_ADDR" . "https://alinomad.cern.ch:443")
      ("NOMAD_CACERT" . "${XDG_CONFIG_HOME}/cern-ca-bundle.crt")
      ("NOMAD_CLIENT_CERT" . "${XDG_CONFIG_HOME}/grid-personal-cert.pem")
      ("NOMAD_CLIENT_KEY" . "${XDG_CONFIG_HOME}/grid-personal-key.pem")
      ("CONSUL_HTTP_ADDR" . "https://aliconsul.cern.ch:443")
      ("CONSUL_CACERT" . "${XDG_CONFIG_HOME}/cern-ca-bundle.crt")
      ("CONSUL_CLIENT_CERT" . "${XDG_CONFIG_HOME}/grid-personal-cert.pem")
      ("CONSUL_CLIENT_KEY" . "${XDG_CONFIG_HOME}/grid-personal-key.pem")
      ("VAULT_ADDR" . "https://alivault.cern.ch:443")
      ("VAULT_CACERT" . "${XDG_CONFIG_HOME}/cern-ca-bundle.crt")
      ("VAULT_CLIENT_CERT" . "${XDG_CONFIG_HOME}/grid-personal-cert.pem")
      ("VAULT_CLIENT_KEY" . "${XDG_CONFIG_HOME}/grid-personal-key.pem")

      ;; Common environment everywhere.
      ;; Prepend my own binaries to $PATH. These should probably all
      ;; be managed through `home-files-service-type'.
      ("PATH" . "$HOME/.local/bin${PATH:+:}$PATH")
      ;; Default terminal-related applications (except Emacs, which is separate).
      ("PAGER" . "less")
      ;; Guix force-overrides $LESS by default, so force-force it to do what I
      ;; want instead.  `less' reads the `lesskey' file configured above.
      ("GUIX_PAGER" . "env -u LESS less")
      ;; To make LESS_TERMCAP_* variables (set in lesskey) apply to man pages in kitty.
      ("GROFF_NO_SGR" . "1")
      ;; Shell history -- primarily for zsh, but Emacs' eshell uses this too.
      ("HISTSIZE" . "10000000")
      ("LEDGER_FILE" . "$HOME/sync/ledger/ledger.journal")
      ("GTAGSLABEL" . "pygments")
      ;; Disable at-spi-dbus-launcher accessibility service.
      ("NO_AT_BRIDGE" . "1")
      ;; Auto-compilation is annoying and creates a bunch of files that are never cleaned up.
      ("GUILE_AUTO_COMPILE" . "0")

      ;; XDG basedir spec compliance for various programs.
      ;; See: https://wiki.archlinux.org/index.php/XDG_Base_Directory for a list of programs.
      ;; The `home-xdg-base-directories' service (enabled by default) sets $XDG_* variables for us.
      ("ANDROID_EMULATOR_HOME" . "$XDG_DATA_HOME/android-emulator")
      ("ASPELL_CONF" . "per-conf $XDG_CONFIG_HOME/aspell/aspell.conf; home-dir $XDG_DATA_HOME/aspell")
      ("BUP_DIR" . "$XDG_DATA_HOME/bup")
      ("CARGO_HOME" . "$XDG_DATA_HOME/cargo")
      ("ELECTRUMDIR" . "$XDG_DATA_HOME/electrum")
      ("FG_HOME" . "$XDG_DATA_HOME/fgfs")
      ("GETIPLAYERUSERPREFS" . "$XDG_DATA_HOME/get_iplayer")
      ("GTK2_RC_FILES" . "$XDG_CONFIG_HOME/gtk-2.0/gtkrc")
      ("ICEAUTHORITY" . "$XDG_CACHE_HOME/ICEauthority")
      ("INPUTRC" . "$XDG_CONFIG_HOME/readline/inputrc")
      ("IPYTHONDIR" . "$XDG_CONFIG_HOME/ipython")
      ("JUPYTER_CONFIG_DIR" . "$XDG_CONFIG_HOME/jupyter")
      ;; KONAN_DATA_DIR=~/.konan by default; grows to multiple GiB.
      ;; https://discuss.kotlinlang.org/t/change-konan-folder-location/18309
      ("KONAN_DATA_DIR" . "$XDG_CACHE_HOME/konan")
      ("NPM_CONFIG_USERCONFIG" . "$XDG_CONFIG_HOME/npm/npmrc")
      ("PASSWORD_STORE_DIR" . "$XDG_DATA_HOME/password-store")
      ("PYLINTHOME" . "$XDG_CACHE_HOME/pylint")
      ("PYLINTRC" . "$XDG_CONFIG_HOME/pylint/pylintrc")
      ("RECOLL_CONFDIR" . "$XDG_CONFIG_HOME/recoll")
      ("RLWRAP_HOME" . "$XDG_DATA_HOME/rlwrap")
      ("STACK_ROOT" . "$XDG_DATA_HOME/stack")
      ("TMUX_TMPDIR" . "$XDG_RUNTIME_DIR")
      ("WEECHAT_HOME" . "$XDG_CONFIG_HOME/weechat")
      ("XCOMPOSECACHE" . "$XDG_CACHE_HOME/X11/XCompose")
      ("XCOMPOSEFILE" . "$XDG_CONFIG_HOME/X11/XCompose")
      ("ZDOTDIR" . "$XDG_CONFIG_HOME/zsh")
      ("_JAVA_OPTIONS" .
       ;; Merge _JAVA_OPTIONS declarations since Guix warns if a variable is
       ;; defined twice (even if the definitions would complement each other).
       ,(string-append
         "$_JAVA_OPTIONS${_JAVA_OPTIONS:+ }-Djava.util.prefs.userRoot=$XDG_CONFIG_HOME/java"
         (if x11-desktop?
             (string-append
              "-Dawt.useSystemAAFontSettings=on -Dswing.aatext=true "
              "-Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel "
              "-Dswing.crossplatformlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel")
             "")))

      ,@(if x11-desktop?
            `(("TERMINAL" . "kitty")
              ("QT_X11_NO_MITSHM" . "1")  ; fixes a Steam issue: https://gitlab.com/nonguix/nonguix/-/issues/267
              ("GUIX_SANDBOX_EXTRA_SHARES" .   ; mount savegame locations inside Steam container
               ,(string-join
                 '("$HOME/savegames/banished=$HOME/.local/share/Steam/steamapps/compatdata/242920/pfx/drive_c/users/steamuser/Documents/Banished/Save"
                   "$HOME/savegames/planetbase=$HOME/.local/share/Steam/steamapps/compatdata/403190/pfx/drive_c/users/steamuser/Documents/Planetbase"
                   "$HOME/savegames/cities-skylines=$HOME/.local/share/Colossal Order/Cities_Skylines/Saves"
                   "$HOME/savegames/surviving-mars=$HOME/.local/share/Surviving Mars/76561198130982912"
                   "$HOME/savegames/colony-survival=$HOME/.local/share/Steam/steamapps/common/Colony Survival/gamedata/savegames"
                   "$HOME/savegames/portal=$HOME/.local/share/Steam/steamapps/common/Portal/portal/save"
                   "$HOME/savegames/spacechem=$HOME/.local/share/Zachtronics Industries/SpaceChem/save"
                   "$HOME/savegames/tis-100=$HOME/.local/share/TIS-100/76561198130982912")
                 ":"))
              ;; Smooth trackpad scrolling in Firefox/Icecat.
              ;; https://wiki.archlinux.org/index.php/Firefox/Tweaks#Pixel-perfect_trackpad_scrolling
              ("MOZ_USE_XINPUT2" . "1")

              ;; Set Emacs up as my preferred editor.
              ("EDITOR" . "emacsclient -qc")
              ;; Tell emacsclient to return immediately after opening the file.  I
              ;; can't put this in $EDITOR as many programs expect $EDITOR to exit
              ;; only when the user is done editing.
              ("ASYNC_EDITOR" . "emacsclient -qcn"))

            ;; In non-graphical environments, set up vim as my preferred editor.
            '(("EDITOR" . "vim"))))))   ; we define no ASYNC_EDITOR

(define (shepherd-services config)
  "Run various daemons in my user profile."
  (match-record config <tw-home-configuration> (x11-desktop? nvidia-driver?)
    (if x11-desktop?
        (list
         (shepherd-service
          (documentation "Emacs server; connect using emacsclient.")
          (provision '(emacs))
          (requirement '(x11-display))
          (start #~(make-forkexec-constructor
                    (list #$(file-append emacs "/bin/emacs") "--fg-daemon")))
          (stop #~(make-kill-destructor)))

         (shepherd-service
          (documentation "NetworkManager applet; provides a GUI for network connections.")
          (provision '(nm-applet))
          (requirement '(x11-display))
          (start #~(make-forkexec-constructor
                    (list #$(file-append network-manager-applet "/bin/nm-applet"))))
          (stop #~(make-kill-destructor)))

         (shepherd-service
          (documentation "Dunst notification daemon; displays desktop notifications.")
          (provision '(dunst))
          (requirement '(x11-display))
          (start #~(make-forkexec-constructor
                    (list #$(file-append dunst "/bin/dunst"))))
          (stop #~(make-kill-destructor)))

         ;; Picom needs to use the proprietary nvidia driver's libgl if that
         ;; driver is used for Xorg; plain Mesa won't work then.
         (let ((grafter
                (if nvidia-driver?
                    (package-input-rewriting
                     `((,mesa . ,(package (inherit mesa) (replacement nvda)))))
                    identity)))
           (shepherd-service
            (documentation "Picom compositor; enables transparent windows in X.")
            (provision '(picom))
            (requirement '(x11-display))
            (start #~(make-forkexec-constructor
                      (list #$(file-append (grafter picom) "/bin/picom")
                            "--config" #$(local-file "home/files/picom.conf"))))
            (stop #~(make-kill-destructor))))

         (shepherd-service
          (documentation "Source Xresources on login.")
          (provision '(xrdb))
          (requirement '(x11-display))
          (one-shot? #t)
          (start #~(lambda _
                     (invoke #$(file-append xrdb "/bin/xrdb") "-merge"
                             (string-append (getenv "XDG_CONFIG_HOME") "/X11/Xresources")))))

         ;; By default, xdotool gets most of "#@\|~()<>[]{} wrong.  Make
         ;; it use the correct keymap by re-setting the same one again.
         (shepherd-service
          (documentation "Fix X keyboard map on login; passmenu needs this.")
          (provision '(fix-xdotool))
          (requirement '(x11-display))
          (one-shot? #t)
          (start #~(lambda _
                     (use-modules (ice-9 rdelim)
                                  (ice-9 regex)
                                  (ice-9 popen))
                     (let ((port (open-pipe* OPEN_READ #$(file-append setxkbmap "/bin/setxkbmap")
                                             "-query")))
                       (let loop ((line (read-line port)))
                         (unless (eof-object? line)
                           (let ((mtch (string-match "^layout:[[:space:]]*" line)))
                             (if mtch
                                 (system* #$(file-append setxkbmap "/bin/setxkbmap")
                                          (match:suffix mtch))
                                 (loop (read-line port)))))))))))

        '())))

(define (cron-jobs config)
  "Periodic jobs to keep my home directory in sync."
  (match-record config <tw-home-configuration> (pim?)
    (if pim?
        ;; To avoid popping up a password prompt every time these run,
        ;; gpg-agent needs a long-enough default-cache-ttl.
        (list #~(job "15 */4 * * *"   ; every four hours at HH:15
                     (string-append #$(file-append vdirsyncer "/bin/vdirsyncer") " metasync"))
              #~(job "0,30 * * * *"   ; every half hour
                     (string-append #$(file-append vdirsyncer "/bin/vdirsyncer") " sync")))
        '())))

(define-public tw-home-service-type
  (service-type
   (name 'tw-home)
   (description "Set up a basic, uniform home environment for my machines.")
   (default-value (tw-home-configuration))
   (extensions
    (list (service-extension home-profile-service-type home-packages)
          (service-extension home-zsh-service-type zshrc)
          (service-extension home-xdg-configuration-files-service-type xdg-configs)
          (service-extension home-files-service-type non-xdg-configs)
          (service-extension home-environment-variables-service-type environment-variables)
          (service-extension home-mcron-service-type cron-jobs)
          (service-extension home-shepherd-service-type shepherd-services)
          ;; The dbus service doesn't seem to be added automatically.
          ;; Always activate it, even in non-graphical environments, because the
          ;; `service-extension' itself can't depend on the `tw-home-configuration'.
          ;; It shouldn't be too much overhead, though.
          (service-extension home-dbus-service-type (const #t))))))


;;; Restic backup helpers

(define-public (restic-pass-key key)
  (restic-password-source (type 'pass) (name key)))

(define-public (restic-vin.wg-repo path)
  (restic-rest-repository
   (username "timo")
   (password (restic-pass-key "computers/vin/restic-server/timo"))
   (hostname "vin.wg")
   (port 8181)
   (path path)))


;;; OpenSSH configuration
;; Since we specify the entire `home-openssh-configuration', we cannot make
;; this a service extension.

(define (wireguardify host)
  (string-replace-substring host ".twilken.net" ".wg"))

(define (make-own-ssh-host host port)
  (openssh-host (name host) (port port) (user "timo")))

(define-public (openssh-service proxy-to-cern?)
  (define no-proxy (list (proxy-jump (host-name "none"))))
  (service home-openssh-service-type
    (home-openssh-configuration
     (hosts
      ;; Earlier rules take precedence over later ones.
      `(,(openssh-host (name "*.srcf.net") (user "tw466"))
        ;; My own machines
        ,@(map make-own-ssh-host (map car %ssh-ports) (map cdr %ssh-ports))
        ,@(map make-own-ssh-host (map (compose wireguardify car) %ssh-ports) (map cdr %ssh-ports))
        ,(openssh-host (name "*.fritz.box") (proxy (list (proxy-jump (host-name "lud.twilken.net")))))
        ;; Git hosts
        ,(openssh-host
          (name "git.twilken.net")
          (user "git")
          ;; git.twilken.net is a CNAME to lud.twilken.net.
          (port (assoc-ref %ssh-ports "lud.twilken.net")))
        ,(openssh-host (name "github.com") (user "git"))
        ,(openssh-host (name "ssh.github.com") (user "git"))
        ,(openssh-host
          (name "gitlab.cern.ch")
          (user "git")
          (port 7999)
          (proxy no-proxy))
        ;; BitBucket apparently only supports ssh-rsa.
        ,(openssh-host
          (name "bitbucket.org")
          (user "git")
          (host-key-algorithms '("+ssh-rsa"))
          (accepted-key-types '("+ssh-rsa")))
        ;; CERN stuff
        ,(openssh-host
          (name "lxtunnel.cern.ch")
          (proxy no-proxy)    ; avoid ProxyJump loops
          ;; Share a single connection to lxtunnel, to speed up subsequent
          ;; connections to the GPN.  Keep it open for a few minutes after
          ;; the last user connection exits, in case we need it again.
          (extra-content "\
  ControlMaster auto
  ControlPath ${XDG_RUNTIME_DIR}/ssh_mux_%h_%p_%r
  ControlPersist 5m
"))
        ,(openssh-host
          (name "lxplus*.cern.ch")   ; also catch e.g. lxplus9
          (proxy no-proxy)     ; no jump needed
          (extra-content "GSSAPIDelegateCredentials yes"))   ; needed for EOS home mount
        ,(openssh-host
          (name "aiadm.cern.ch")
          (proxy no-proxy)     ; aiadm refuses connections via lxtunnel
          (extra-content "GSSAPIDelegateCredentials yes"))   ; needed for EOS home mount
        ,(openssh-host
          (name "cvmfs-alice.cern.ch")
          (extra-content "GSSAPIDelegateCredentials yes"))   ; needed for EOS home mount
        ,(openssh-host
          (name "alihlt-gw-prod.cern.ch")
          (port 2020))
        ,(openssh-host
          (name "alieevee-wn-*.cern.ch")
          (user "root")
          (identity-file "~/.local/share/ssh-keys/cern_id_rsa"))
        ,(openssh-host
          (name "alieevee-wn-*.cern.ch !alieevee-wn-1.cern.ch")   ; avoid proxy loops
          (proxy (list (proxy-jump (host-name "alieevee-wn-1.cern.ch")))))
        ,(openssh-host
          (name "epn???")
          (user "pdp")
          (proxy (list (proxy-jump (host-name "alihlt-gw-prod.cern.ch") (port 2020))))
          (identity-file "~/.local/share/ssh-keys/epn_id_rsa"))
        ,(openssh-host
          (name "twilkendesktop.cern.ch")
          (port 22022)
          (forward-x11? #t)
          (extra-content "GSSAPIDelegateCredentials yes"))
        ,@(map (lambda (spec)
                 (openssh-host (name (car spec)) (user (cdr spec))
                               (identity-file "~/.local/share/ssh-keys/alicern_id_rsa")))
               '(("alimonitor.cern.ch" . "monalisa")
                 ("alinsure.cern.ch" . "alibuild")
                 ("alibuildmac*.cern.ch" . "alibuild")
                 ("aido*osx*.cern.ch" . "alibuild")
                 ("alibuild*.cern.ch" . "root")
                 ("alimetal*.cern.ch" . "root")
                 ("alissandra*.cern.ch" . "root")
                 ("alimesos*.cern.ch" . "root")
                 ("alientest*.cern.ch" . "root")
                 ("aliflow*.cern.ch" . "root")
                 ("alijenkins*.cern.ch" . "root")
                 ("arm-builder-*" . "centos")))
        ,(openssh-host
          (name "*.cern.ch")
          (user "twilken")
          (identity-file "~/.local/share/ssh-keys/cern_id_rsa")
          (proxy (if proxy-to-cern?
                     (list (proxy-jump (host-name "lxtunnel.cern.ch")))
                     no-proxy))
          (extra-content  "\
  # Kerberos authentication
  GSSAPIAuthentication yes
  GSSAPIDelegateCredentials no
  PreferredAuthentications gssapi-keyex,gssapi-with-mic,publickey,password,keyboard-interactive
"))
        ;; Default SSH key. This isn't in ~/.ssh as `home-openssh-service-type'
        ;; manages that and might delete keys there.
        ,(openssh-host
          (name "*")
          (identity-file "~/.local/share/ssh-keys/id_ed25519")
          ;; Remote servers probably don't know about xterm-kitty.
          (extra-content "SetEnv TERM=xterm-256color")))))))