summaryrefslogtreecommitdiff
path: root/tw/home.scm
blob: 2f0c417815d13da82424ba9c1d2429132ebf77cb (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
(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 shells)
  #:use-module (gnu home services shepherd)
  #:use-module (gnu home services ssh)
  #:use-module (guix gexp)
  #:use-module (guix packages)
  ;; Don't load nongnu on hosts where it's not available, i.e. servers.
  #:autoload (nongnu packages messaging) (zoom)
  #:autoload (nongnu packages nvidia) (nvda)
  #: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 xorg)
  #:use-module (tw system)
  #:use-module (tw theme))

(use-package-modules
 admin aidc backup base calendar check chromium compression cmake compton curl
 databases dav disk dns elf emacs emacs-xyz file finance fonts
 freedesktop games gcc gimp gl gnome gnome-xyz gnupg gnuzilla graphviz
 haskell-apps image-viewers imagemagick inkscape less libreoffice linux lisp
 llvm mail maths ncdu package-management password-utils pdf pretty-print
 pulseaudio pv python python-build python-check python-xyz rsync shells
 shellutils sqlite ssh syndication terminals tcl tex textutils tmux tls
 tree-sitter version-control video vim web web-browsers wm xfce xdisorg xorg)

(define-public %common-packages
  (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
   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))

(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 "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 "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")))))))

(define-public %common-services
  (list
   (simple-service 'zshrc home-zsh-service-type
     ;; `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")))))

   (simple-service 'common-config home-xdg-configuration-files-service-type
     `(("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"))))

   (simple-service 'common-files home-files-service-type
     `((".infokey" ,(local-file "home/files/infokey"))
       ;; 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

   (simple-service 'cern-ci-environment home-environment-variables-service-type
     ;; 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")))

   (simple-service 'common-environment home-environment-variables-service-type
     `(;; 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")

       ;; ("NVIM_TUI_ENABLE_CURSOR_SHAPE" . "1")
       ("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.
   (simple-service 'xdg-spec-compliance home-environment-variables-service-type
     '(("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" .
        "$_JAVA_OPTIONS${_JAVA_OPTIONS:+ }-Djava.util.prefs.userRoot=$XDG_CONFIG_HOME/java")))))

(define-public %interactive-packages
  (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 xfce4-screenshooter

   ;; 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 tk xdg-utils xdot xclip xcwd xdotool
   xdpyinfo xev xfd xfontsel xinput xkill xprop xrandr xrdb xsel xset xwininfo
   zoom zathura zathura-ps zathura-pdf-poppler
   ungoogled-chromium  ; needed e.g. for UBS 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
   font-google-noto font-google-noto-emoji font-openmoji  ; for polybar
   ;; Theming
   papirus-icon-theme catppuccin-gtk-theme catppuccin-mocha-dark-cursors

   ;; 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
   emacs-use-package
   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))

(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
"))

(export interactive-services)
(define* (interactive-services #:key (nvidia-driver? #f))
  (list
   ;; Configuration files for terminal-only programs in $XDG_CONFIG_HOME.
   (simple-service 'interactive-terminal-config home-xdg-configuration-files-service-type
     `(;; All alibuild needs is an empty file.
       ("alibuild/disable-analytics"
        ,(plain-file "alibuild-disable-analytics" ""))
       ("X11/XCompose" ,(local-file "home/files/XCompose"))   ; see also: $XCOMPOSEFILE variable
       ("X11/Xresources" ,(local-file "home/files/Xresources"))))

   (simple-service 'emacs-config home-xdg-configuration-files-service-type
     `(("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)))

   (simple-service 'emacsclient-is-editor home-environment-variables-service-type
     `(("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")))

   (simple-service 'emacs-server home-shepherd-service-type
     (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)))))

   ;; The dbus service doesn't seem to be added automatically.
   (simple-service 'force-activate-dbus home-dbus-service-type #t)

   (simple-service 'common-gui-services home-shepherd-service-type
     (list
      (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))))))))))))

   ;; Configuration files for GUI programs in $XDG_CONFIG_HOME.
   (simple-service 'gui-config home-xdg-configuration-files-service-type
     `(("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)))

   (simple-service 'cursor-theme home-files-service-type
     `((".icons/default/index.theme" ,(local-file "home/files/cursors.ini"))))

   (simple-service 'gui-environment home-environment-variables-service-type
     `(("TERMINAL" . "kitty")
       ("QT_X11_NO_MITSHM" . "1")  ; fixes a Steam issue: https://gitlab.com/nonguix/nonguix/-/issues/267
       ("_JAVA_OPTIONS" .
        ,(string-append
          "$_JAVA_OPTIONS${_JAVA_OPTIONS:+ }-Dawt.useSystemAAFontSettings=on -Dswing.aatext=true "
          "-Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel "
          "-Dswing.crossplatformlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel"))
       ;; Smooth trackpad scrolling in Firefox/Icecat.
       ;; https://wiki.archlinux.org/index.php/Firefox/Tweaks#Pixel-perfect_trackpad_scrolling
       ("MOZ_USE_XINPUT2" . "1")))

   (simple-service 'gui-scripts home-files-service-type
     `(;; 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/share/applications/emacsclient.desktop"
        ,(local-file "home/files/emacsclient.desktop"))))))