(define-module (tw services desktop) #:use-module (gnu) #:use-module (gnu home services) #:use-module (gnu home services mail) #:use-module (gnu home services mcron) #:use-module (gnu home services shepherd) #:use-module ((gnu packages admin) #:select (ansible)) #:use-module ((gnu packages aidc) #:select (qrencode)) #:use-module ((gnu packages bittorrent) #:select (transmission-remote-gtk)) #:use-module ((gnu packages calendar) #:select (khal)) #:use-module ((gnu packages check) #:select (actionlint)) #:use-module ((gnu packages chromium) #:select (ungoogled-chromium)) #:use-module ((gnu packages compton) #:select (picom)) #:use-module ((gnu packages dav) #:select (vdirsyncer)) #:use-module ((gnu packages fonts) #:select (font-fira-code font-fira-sans font-google-noto font-google-noto-emoji font-google-noto-sans-cjk font-google-noto-serif-cjk font-hermit font-inconsolata font-libertinus font-openmoji)) #:use-module ((gnu packages freedesktop) #:select (xdg-utils)) #:use-module ((gnu packages games) #:select (bsd-games)) #:use-module ((gnu packages gimp) #:select (gimp)) #:use-module ((gnu packages gl) #:select (mesa)) #:use-module ((gnu packages gnome) #:select (dconf dconf-editor network-manager-applet vinagre)) #:use-module ((gnu packages gnome-xyz) #:select (papirus-icon-theme)) #:use-module ((gnu packages gnuzilla) #:select (icecat)) #:use-module ((gnu packages graphviz) #:select (xdot)) #:use-module ((gnu packages image-viewers) #:select (imv)) #:use-module ((gnu packages inkscape) #:select (inkscape)) #:use-module ((gnu packages libreoffice) #:select (libreoffice)) #:use-module ((gnu packages linux) #:select (acpilight powertop radeontop)) #:use-module ((gnu packages mail) #:select (aerc khard)) #:use-module ((gnu packages messaging) #:select (nheko)) #:use-module ((gnu packages networking) #:select (blueman)) #:use-module ((gnu packages package-management) #:select (modules)) #:use-module ((gnu packages pdf) #:select (zathura zathura-pdf-poppler zathura-ps)) #:use-module ((gnu packages pulseaudio) #:select (pulsemixer)) #:use-module ((gnu packages python-build) #:select (python-setuptools-scm)) #:use-module ((gnu packages python-check) #:select (python-mypy python-tox)) #:use-module ((gnu packages python-xyz) #:select (s3cmd)) #:use-module ((gnu packages ssh) #:select (pdsh)) #:use-module ((gnu packages syndication) #:select (newsboat)) #:use-module ((gnu packages tcl) #:select (tk)) #:use-module ((gnu packages terminals) #:select (kitty)) #:use-module ((gnu packages tex) #:select (texlive-scheme-small texlive-latexmk texlive-moderncv texlive-fontawesome5 texlive-multirow texlive-arydshln texlive-libertine texlive-inconsolata texlive-newtx texlive-babel texlive-csquotes texlive-siunitx 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-txfonts)) #:use-module ((gnu packages version-control) #:select (git)) #:use-module ((gnu packages video) #:select (mpv)) #:use-module ((gnu packages web-browsers) #:select (lynx)) #:use-module ((gnu packages wm) #:select (dunst polybar)) #:use-module ((gnu packages xdisorg) #:select (arandr numlockx hsetroot rofi rofi-calc xclip xdotool xsel)) #:use-module ((gnu packages xfce) #:select (xfce4-screenshooter)) #:use-module ((gnu packages xorg) #:select (xdpyinfo xev xfd xfontsel xinput xkill xprop xrandr xset xrdb xwininfo setxkbmap)) #:use-module ((games packages minecraft) #:select (prismlauncher)) #:use-module ((nongnu packages nvidia) #:select (nvda)) #:use-module ((nongnu packages messaging) #:select (zoom)) #:use-module ((nongnu packages nvidia) #:select (nvidia-system-monitor)) #:use-module (gnu services configuration) #:use-module (guix gexp) #:use-module (guix packages) #:use-module ((guix records) #:select (match-record)) #: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 (srfi srfi-1) #:use-module (srfi srfi-26) #:use-module (tw gexp) #:use-module ((tw packages alice) #:select (python-alibuild python-alidistlint)) #:use-module (tw packages catppuccin) #:use-module ((tw packages ci) #:select (hashicorp-consul-bin hashicorp-levant-bin hashicorp-nomad-bin hashicorp-packer-bin hashicorp-vault-bin)) #:use-module ((tw packages games) #:select (szio-solitaire)) #:use-module ((tw packages mail) #:select (mutt_oauth2.py)) #:use-module ((tw packages scanner) #:select (simple-scan/airscan)) #:use-module ((tw packages xorg) #:select (xcwd)) #:use-module (tw theme) #:export (home-desktop-configuration home-monitor-configuration home-desktop-service-type home-pim-configuration home-pim-service-type)) ;; Desktop services, including monitor layout (define-maybe/no-serialization string) (define (list-of-strings? thing) (and (list? thing) (every string? thing))) (define-configuration/no-serialization home-monitor-configuration (name string "The monitor's name in X11.") (xrandr-options (list-of-strings '()) "Options to pass to xrandr to configure this monitor.")) (define (list-of-monitors? thing) (and (list? thing) (every home-monitor-configuration? thing))) (define-configuration/no-serialization home-desktop-configuration (gaming? (boolean #f) "Install games and other packages useful for gaming?") (nvidia-driver? (boolean #f) "Adapt the X11 desktop for the proprietary NVIDIA driver?") (desktop-background (file-like (file-append tw-background "/share/backgrounds/tw/blobs.png")) "An image that will be set as the desktop background.") (bluetooth? (boolean #f) "Whether the device has Bluetooth hardware. If true, configure the user interface to use it.") (battery-name maybe-string "The device name of the system's battery, if any. See @code{/sys/class/power_supply}.") (ac-adapter-name maybe-string "The device name of the system's mains power supply, if any. See @code{/sys/class/power_supply}.") (monitors list-of-monitors "List of monitor declarations to apply.")) (define polybar-wrapper (program-file "polybar-wrapper" #~(begin ;; This wrapper program checks that the monitor we want to start ;; polybar on is actually connected. (use-modules (ice-9 popen) (ice-9 rdelim)) (let* ((connected-str (string-append (getenv "POLYBAR_MONITOR") " connected")) (xrandr (open-pipe* OPEN_READ #$(file-append xrandr "/bin/xrandr") "-q")) (monitor-connected? (let loop ((line (read-line xrandr))) (cond ((eof-object? line) #f) ; we didn't find our monitor connected ((string-prefix? connected-str line) #t) ; the monitor we want is connected (else (loop (read-line xrandr))))))) ; keep looking (close-pipe xrandr) (when monitor-connected? (execl #$(file-append polybar "/bin/polybar") "polybar")))))) (define (polybar-service monitor battery-name ac-adapter-name) (shepherd-service (documentation (string-append "Polybar desktop bar for monitor " monitor ".")) (provision (list (symbol-append 'polybar- (string->symbol monitor)))) (requirement '(xorg-setup)) (start #~(make-forkexec-constructor (list #$polybar-wrapper) #:environment-variables (cons* #$(string-append "POLYBAR_MONITOR=" monitor) #$(string-append "POLYBAR_BATTERY=" (maybe-value battery-name "")) #$(string-append "POLYBAR_AC_ADAPTER=" (maybe-value ac-adapter-name "")) (default-environment-variables)))) (stop #~(make-kill-destructor)))) (define (home-packages config) "Install packages I use frequently." (cons* ;; 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 prismlauncher ;; 0ad flightgear freeciv simutrans/pak128 warzone2100 widelands pioneer (match-record config (gaming? nvidia-driver?) (if (not gaming?) '() (if nvidia-driver? ;; nvidia-system-monitor: Qt; installs "qnvsm" binary, but no .desktop file (list steam-nvidia nvidia-system-monitor) (list steam radeontop)))))) (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 (home-xdg-configs config) "Configuration files that follow the XDG basedir spec." `(("alibuild/disable-analytics" ; All alibuild needs is an empty file. ,(plain-file "alibuild-disable-analytics" "")) ;; X11 environment configuration. ("X11/XCompose" ,(local-file "files/XCompose")) ; see also: $XCOMPOSEFILE variable ;; 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 "files/dunstrc")) ("dunst/dunstrc.d/50-catppuccin.conf" ,catppuccin-dunstrc) ("gtk-2.0/gtkrc" ,(local-file "files/gtk2.ini")) ("gtk-3.0/settings.ini" ,(local-file "files/gtk3.ini")) ("i3/config" ,(local-file "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 "files/kitty.conf") catppuccin-kitty)) ("mimeapps.list" ,(local-file "files/mimeapps.list")) ("polybar/config.ini" ,(local-file "files/polybar.ini")) ("polybar/catppuccin.ini" ,catppuccin-polybar) ("rofi/config.rasi" ,(local-file "files/rofi.rasi")) ("rofi/themes/catppuccin.rasi" ,catppuccin-rofi) ("xfce4/xfce4-screenshooter" ,xfce4-screenshooter.conf) ("zathura/zathurarc" ,(local-file "files/zathurarc")) ("zathura/catppuccin" ,catppuccin-zathura) ,@(match-record config (gaming?) (if gaming? `(("guix-gaming-channels/games.scm" ,(scheme-file "guix-gaming-credentials.scm" #~(begin (use-modules (ice-9 rdelim) (ice-9 popen)) (define gog-credentials (let* ((pass (open-pipe* OPEN_READ "pass" "show" "software/games/gog")) (password (read-line pass)) (username (let loop ((line (read-line pass))) (cond ((eof-object? line) (error "Could not find username in pass output")) ((string-prefix? "username: " line) (string-trim (substring line (string-length "username:")))) (else (loop (read-line pass))))))) (close-pipe pass) `((email ,username) (password ,password)))) (make-gaming-config `((gog ,gog-credentials))))))) '())))) (define (home-files config) "Extra configuration files and binaries that don't follow the XDG spec." `((".icons/default/index.theme" ,(local-file "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 "files/sessionmenu" #:recursive? #t)) (".local/bin/passmenu" ,(local-file "files/passmenu" #:recursive? #t)) (".local/bin/volume" ,(local-file "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)))))))) (define (home-environment config) `(("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") ("_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")))) (define (home-shepherd-services config) "Run various daemons in my user profile." (match-record config (nvidia-driver? bluetooth? desktop-background battery-name ac-adapter-name monitors) `(,(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 "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" #$(local-file "files/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)))))))))) ,(shepherd-service (documentation "Set up X displays on login.") (provision '(xorg-setup)) (one-shot? #t) (start #~(lambda _ (define (replace-home path) (if (string-prefix? "~/" path) (string-replace path (getenv "HOME") 0 1) path)) (system* #$(file-append numlockx "/bin/numlockx") "on") ;; Turn off the monitors if there is no input for 10 minutes. (system* #$(file-append xset "/bin/xset") "dpms" "600" "600" "600") (system* #$(file-append xrandr "/bin/xrandr") #$@(append-map (lambda (monitor) (match-record monitor (name xrandr-options) `("--output" ,name ,@xrandr-options))) monitors)) ;; Set the desktop background picture. Hopefully doing this just after ;; xrandr works and sets it for both screens. (system* #$(file-append hsetroot "/bin/hsetroot") "-cover" (replace-home #$desktop-background))))) ,@(if bluetooth? (list (shepherd-service (documentation "Blueman applet; provides a GUI for connection to bluetooth devices.") (provision '(blueman-applet)) (start #~(make-forkexec-constructor (list #$(file-append blueman "/bin/blueman-applet")))) (stop #~(make-kill-destructor)))) '()) ,@(map (compose (cut polybar-service <> battery-name ac-adapter-name) home-monitor-configuration-name) monitors)))) (define home-desktop-service-type (service-type (name 'desktop) (description "Configure the desktop environment.") (extensions (list (service-extension home-profile-service-type home-packages) (service-extension home-xdg-configuration-files-service-type home-xdg-configs) (service-extension home-files-service-type home-files) (service-extension home-environment-variables-service-type home-environment) (service-extension home-shepherd-service-type home-shepherd-services))))) ;; Personal Information Management (mail, calendar, contacts, etc) (define-configuration/no-serialization home-pim-configuration (work? (boolean #f) "Configure only programs relevant for work?")) (define (pim-packages config) "PIM programs and utilities." (match-record config (work?) (cons* vdirsyncer khal khard aerc lynx mutt_oauth2.py ; lynx for HTML mail (if work? '() (list nheko signal-desktop newsboat))))) (define (pim-xdg-configs config) "Configuration files for PIM programs." (match-record config (work?) `(("khal/config" ,(local-file "files/khal.conf")) ("khard/khard.conf" ,(local-file "files/khard.conf")) ("aerc/accounts.conf" ,(if work? (local-file "files/aerc/accounts.work.conf") (local-file "files/aerc/accounts.conf"))) ("aerc/aerc.conf" ,(local-file "files/aerc/aerc.conf")) ("aerc/binds.conf" ,(local-file "files/aerc/binds.conf")) ("aerc/filters" ,(local-file "files/aerc/filters" #:recursive? #t)) ("aerc/stylesets/catppuccin" ,(local-file "files/aerc/catppuccin.conf")) ("vdirsyncer/config" ,(local-file "files/vdirsyncer.conf")) ,@(if work? '() `(("newsboat/config" ,(local-file "files/newsboat.conf")) ("newsboat/config.catppuccin" ,catppuccin-newsboat)))))) (define (pim-cron-jobs config) "Periodic jobs to sync calendars and contacts." ;; To avoid popping up a password prompt every time these run, ;; gpg-agent needs a long-enough default-cache-ttl. (list #~(job "15 */4 * * *" #$(file-append vdirsyncer "/bin/vdirsyncer metasync")) #~(job "0,30 * * * *" #$(file-append vdirsyncer "/bin/vdirsyncer sync")))) (define-public home-pim-service-type (service-type (name 'pim) (description "Install and configure Personal Information Management software.") (default-value (home-pim-configuration)) (extensions (list (service-extension home-profile-service-type pim-packages) (service-extension home-xdg-configuration-files-service-type pim-xdg-configs) (service-extension home-mcron-service-type pim-cron-jobs)))))