Skip to content

Latest commit

 

History

History
1726 lines (1395 loc) · 50.3 KB

systems.org

File metadata and controls

1726 lines (1395 loc) · 50.3 KB

System Configuration with Guix

Table of Contents

Update strategy

Remove all the sections currently not in use

Move the target filenames to one directory tree

Build guix home configuration and copy the files from the target directory tree

Once working - reconfigure with idiomatic guix home approach

Emacs

  • The config for emacs is common to all systems
  • A list of modules is generated as a manifest for guix installation

Emacs mail

Review the approach by Andrew Tropin - either adopt or standardise with mu4e across all linux systems.

Introduction

This file was originally created by David Wilson, and can be seen here: https://github.com/daviwil/dotfiles My modifications are made to configure the contents for my systems.

Channels

Guix supports the concept of channels which basically amount to Git repositories which contain Guix package definitions that can be installed on your machine. Aside from the %default-channels list, I also use the Nonguix channel to install packages that aren’t included with Guix by default like the non-free Linux kernel.

~/dotfiles/.config/guix/channels.scm:*

;; -*- buffer-read-only: t; -*-
;; NOTE: This file is generated from ~/dotfiles/System.org.  Please see commentary there.

(list (channel
       (name 'pj-suckless)
       (url "https://github.com/paul-jewell/pj-suckless.git"))
      (channel
       (name 'nonguix)
       ;;(commit "c34fa8bfacdce5fa45b2a684c2b27309c09a9056")
       (url "https://gitlab.com/nonguix/nonguix")
       ;; enable signature verification
       (introduction
        (make-channel-introduction
         "897c1a470da759236cc11798f4e0a5f7d4d59fbc"
         (openpgp-fingerprint
          "2A39 3FFF 68F4 EF7A 3D29  12AF 6F51 20A0 22FB B2D5"))))
      (channel
       (name 'guix)
       ;;(commit "190187326ad7516dd6728eed7bb6ef2d4f92897a")
       (url "https://git.savannah.gnu.org/git/guix.git")
       (introduction
        (make-channel-introduction
         "9edb3f66fd807b096b48283debdcddccfea34bad"
         (openpgp-fingerprint
          "BBB0 2DDF 2CEA F6A8 0D1D  E643 A2A0 6DF2 A33A 54FA")))))

The following channel list can be used when testing patches to packages and services from a local clone of the Guix repo. You’ll have to create a branch and commit changes to it before guix pull can pick them up, though. You can change the target branch using the branch field of the channel.

;; (list (channel
;;         (name 'nonguix)
;;         (url "https://gitlab.com/nonguix/nonguix"))
;;       (channel
;;         (name 'guix)
;;         (branch "fix-glu-pkg-config")
;;         (url "file:///home/daviwil/Projects/Code/guix")
;;         (introduction
;;           (make-channel-introduction
;;             "d06d5db885e4b8399e878708862fbe3a67f0592c"
;;             (openpgp-fingerprint
;;               "53C4 1E6E 41AA FE55 335A  CA5E 446A 2ED4 D940 BF14")))))

Systems

Base Configuration

This base configuration is shared between all of the machines I manage with Guix. Since all of my machines are Lenovo ThinkPad laptops, the same basic configuration applies pretty cleanly across all of them. This may change in the future.

Any configuration that derives from base-operating-system must invoke guix system in a specific way to ensure it gets loaded correctly:

sudo guix system -L ~/.config/guix/systems reconfigure ~/.config/guix/systems/davinci.scm

This is also executed by running Make $(system)-system in the dotfiles directory

.config/guix/system/base-system.scm:*

;; -*- buffer-read-only: t; -*-
;; NOTE: This file is generated from ~/.dotfiles/System.org.  Please see commentary there.

(define-module (base-system)
  #:use-module (gnu)
  #:use-module (srfi srfi-1)
  #:use-module (gnu system nss)
  #:use-module (gnu system setuid)
  #:use-module (gnu services pm)
  #:use-module (gnu services cups)
  #:use-module (gnu services desktop)
  #:use-module (gnu services networking)
  #:use-module (gnu packages wm)
  #:use-module (gnu packages cups)
  #:use-module (gnu packages vim)
  #:use-module (gnu packages gtk)
  #:use-module (gnu packages xorg)
  #:use-module (gnu packages emacs)
  #:use-module (gnu packages file-systems)
  #:use-module (gnu packages gnome)
  #:use-module (gnu packages mtools)
  #:use-module (gnu packages linux)
  #:use-module (gnu packages audio)
  #:use-module (gnu packages gnuzilla)
  #:use-module (gnu packages pulseaudio)
  #:use-module (gnu packages web-browsers)
  #:use-module (gnu packages version-control)
  #:use-module (gnu packages package-management)
  #:use-module (nongnu packages linux)
  #:use-module (nongnu system linux-initrd)
  #:use-module (paulj packages paulj-dwm)
  #:use-module (paulj packages paulj-st)
  #:use-module (paulj packages paulj-dmenu)
  #:use-module (paulj packages paulj-slock))

(use-service-modules nix)
(use-service-modules desktop xorg)
(use-service-modules ssh)
(use-package-modules certs)
(use-package-modules shells)

Add a udev rule to enable members of the video group to control screen brightness.

;; Allow members of the "video" group to change the screen brightness.
(define %backlight-udev-rule
  (udev-rule
   "90-backlight.rules"
   (string-append "ACTION==\"add\", SUBSYSTEM==\"backlight\", "
                  "KERNEL==\"intel_backlight\", "
                  "RUN+=\"/run/current-system/profile/bin/chgrp video /sys/class/backlight/%k/brightness\""
                  "\n"
                  "ACTION==\"add\", SUBSYSTEM==\"backlight\", "
                  "KERNEL==\"intel_backlight\", "
                  "RUN+=\"/run/current-system/profile/bin/chmod g+w /sys/class/backlight/%k/brightness\"")))

(define %touchpad-udev-rule
  (udev-rule
   "10-trackpoint.rules"
   (string-append "ACTION==\"add\", SUBSYSTEM==\"input\", "
                  "ATTR{name}==\"TPPS/2 IBM Trackpoint\", "
                  "ATTR{device/sensitivity}=\"240\", "
                  "ATTR{device/press_to_select}=\"1\"")))

Override the default %desktop-services to add the udev backlight configuration and include OpenVPN in the list of NetworkManager plugins.

(define %my-desktop-services
  (modify-services
   %desktop-services
   (elogind-service-type config =>
                         (elogind-configuration
                          (inherit config)
                          (handle-lid-switch-external-power 'suspend)))
   (udev-service-type config =>
                      (udev-configuration
                       (inherit config)
                       (rules (cons* %backlight-udev-rule
                                     %touchpad-udev-rule
                                     (udev-configuration-rules config)))))))

Use the libinput driver for all input devices since it’s a bit more modern than the default.

(define %xorg-libinput-config
  "Section \"InputClass\"
     Identifier \"Touchpads\"
     Driver \"libinput\"
     MatchDevicePath \"/dev/input/event*\"
     MatchIsTouchpad \"on\"

     Option \"Tapping\" \"on\"
     Option \"TappingDrag\" \"on\"
     Option \"DisableWhileTyping\" \"on\"
     Option \"MiddleEmulation\" \"on\"
     Option \"ScrollMethod\" \"twofinger\"
   EndSection
   Section \"InputClass\"
     Identifier \"Keyboards\"
     Driver \"libinput\"
     MatchDevicePath \"/dev/input/event*\"
     MatchIsKeyboard \"on\"
   EndSection")

Define the base-operating-system which will be inherited by all machine configurations.

(define-public base-operating-system
  (operating-system
    (host-name "base")
    (timezone "Europe/London")
    (locale "en_GB.utf8")
    
    ;; Use non-free Linux and firmware
    (kernel linux)
    (firmware (list linux-firmware))
    (initrd microcode-initrd)
    
    ;; Choose UK English keyboard layout, with the extd layout.
    (keyboard-layout (keyboard-layout "gb" "extd"
                                      #:model "thinkpad"
                                      #:options '("ctrl:nocaps")))
    
    ;; Use the UEFI variant of GRUB with the EFI System
    ;; Partition mounted on /boot/efi.
    (bootloader (bootloader-configuration
                 (bootloader grub-efi-bootloader)
                 (targets '("/boot/efi"))
                 (keyboard-layout keyboard-layout)))
    
    ;; Guix doesn't like it when there isn't a file-systems
    ;; entry, so add one that is meant to be overridden
    (file-systems (cons*
                   (file-system
                     (mount-point "/tmp")
                     (device "none")
                     (type "tmpfs")
                     (check? #f))
                   %base-file-systems))
    
    (users (cons (user-account
                  (name "paul")
                  (comment "Paul Jewell")
                  (group "users")
                  (home-directory "/home/paul")
                  (supplementary-groups '(
                                          "wheel"     ;; sudo
                                          "netdev"    ;; network devices
                                          "kvm"
                                          "tty"
                                          "input"
                                          "realtime"  ;; Enable realtime scheduling
                                          "lp"        ;; control bluetooth devices
                                          "audio"     ;; control audio devices
                                          "video")))  ;; control video devices
                 %base-user-accounts))
    
    ;; Add the 'realtime' group
    (groups (cons (user-group (system? #t) (name "realtime"))
                  %base-groups))
    
    ;; Install bare-minimum system packages
    (packages (append (list
                       git
                       ntfs-3g
                       exfat-utils
                       fuse-exfat
                       stow
                       neovim
                       bluez
                       bluez-alsa
                       pulseaudio
                       tlp
                       xf86-input-libinput
                       nss-certs     ;; for HTTPS access
                       gvfs)         ;; for user mounts
                      %base-packages))
    
    ;; Use the "desktop" services, which include the X11 log-in service,
    ;; networking with NetworkManager, and more
    (services (cons* (service slim-service-type
                              (slim-configuration
                               (xorg-configuration
                                (xorg-configuration
                                 (keyboard-layout keyboard-layout)
                                 (extra-config (list %xorg-libinput-config))))))

                     (service openssh-service-type)
                     (service tlp-service-type
                              (tlp-configuration
                               (cpu-boost-on-ac? #t)
                               (wifi-pwr-on-bat? #t)))
                     (pam-limits-service ;; This enables JACK to enter realtime mode
                      (list
                       (pam-limits-entry "@realtime" 'both 'rtprio 99)
                       (pam-limits-entry "@realtime" 'both 'memlock 'unlimited)))

                     (service thermald-service-type)

                     (service cups-service-type
                              (cups-configuration
                               (web-interface? #t)
                               (extensions
                                (list cups-filters))))
                     (bluetooth-service #:auto-enable? #t)
                     (remove (lambda (service)
                               (eq? (service-kind service) gdm-service-type))
                             %my-desktop-services)))

    ;; Add nmtui to the setuid program list
    (setuid-programs
     (append (list (setuid-program
                    (program (file-append network-manager "/bin/nmtui"))))
             %setuid-programs))))

Machines

Machines are named after opera stars and greek gods (during initial setup!).

Per-System Settings

Some settings need to be customized on a per-system basis without tweaking individual configuration files. Thanks to org-mode’s noweb functionality, I can define a set of variables that can be tweaked for each system and applied across these configuration files when they get generated.

(require 'map) ;; Needed for map-merge

(setq pj/system-settings
  (map-merge
    'list
    '()
    <<system-settings>>))

zeus

zeus is a Lenovo X270 being used as a test bed for guix configuration.

./config/guix/systems/zeus.scm:

;; -*- buffer-read-only: t; -*-
;; NOTE: This file is generated from ~/.dotfiles/System.org.  Please see commentary there.
;; Note - if you re-install, you need to review the uuid entries below.

(define-module (zeus)
  #:use-module (base-system)
  #:use-module (gnu))

(operating-system
  (inherit base-operating-system)
  (host-name "zeus")

;; TODO: Move from uuid to drive labels - makes it easier to install the system afresh
  (swap-devices
   (list (swap-space
          (target (uuid "a0a103a5-cef2-446b-a2ff-8ffbee6890de")))))
  (file-systems
   (cons* (file-system
            (mount-point "/boot/efi")
            (device (uuid "F21D-F4AF" 'fat32))
            (type "vfat"))
          (file-system
            (mount-point "/")
            (device
             (uuid "dbac54d4-4507-4205-bc72-8b1e7abc3c8f"
                   'btrfs))
            (type "btrfs"))
          %base-file-systems)))

tristan

tristan is my main desktop AMD Ryzen 9 system, running gentoo linux, and dual booting into windows.

Currently, there is no guix installation on this system, but there is emacs on both operating systems.

Rodolfo

rodolfo is a lenovo x270 laptop with gentoo linux installed.

Shingo

shingo is a small computer in the shed. Currently has gentoo, but will be the next candidate for guix.

mpd Configuration

music_directory   "~/music"
playlist_directory "~/.config/mpd/playlists"

auto_update "yes"
bind_to_address "127.0.0.1"
restore_paused "yes"
max_output_buffer_size "16384"

audio_output {
         type "pulse"
         name "pulse"
}               

audio_output {
         type   "fifo"
         name   "visualiser feed"
         path   "/tmp/mpd.fifo"
         format "44100:16:2"
}

Home Configuration

Configuration using guix home

Basic Version - get it working, then develop it later

(use-modules
 (gnu home)
 (gnu home services)
 (gnu home services shells)
 (gnu home services shepherd)
 (gnu services)
 (guix gexp)
 (gnu packages)
 (gnu packages emacs)
 (gnu packages syncthing)
 (gnu packages gnupg)
 (gnu packages mpd)
 (gnu packages pulseaudio)
 (gnu packages admin))

(define %inputrc
  (plain-file
   "inputrc"
   (string-append
    "set input-meta on\n"
    "set convert-meta off\n"
    "set output-meta on\n\n"
    "set editing-mode emacs\n\n"
    "set enable-keypad on\n\n"

    "$if mode=emacs\n"
    "# Allow the use of the home/end keys\n"
    "\"\\e[1~\": beginning-of-line\n"
    "\"\\e[4~\": end-of-line\n\n"
    "# Map 'page up' and 'page down' to search history based on current cmdline\n"
    "\"\\e[5~\": history-search-backward\n"
    "\"\\e[6~\": history-search-forward\n"
    "$endif\n")))

;; emacs-packages
;; - emacs and packages required for the fully functioning emacs editor with my
;;   configuration.
;; TODO: Link the configuration snippets with the package names and tangle the
;;       result, either in emacs, or with guile

(define %emacs-packages
  ;;  (map specification->package
  (list
   "emacs"
   "emacs-use-package"
   "emacs-no-littering"
   "emacs-popup"
   "emacs-ivy"
   "emacs-ivy-hydra"
   "emacs-ivy-rich"
   "emacs-swiper"
   "emacs-counsel-bbdb"
   "emacs-counsel"
   "emacs-which-key"
   "ispell"
   "hunspell"
   "hunspell-dict-en-gb"
   "emacs-ledger-mode"
    "emacs-go-mode"

    "emacs-multiple-cursors"
    "emacs-org-bullets"
    "emacs-org-roam"
    ;; "emacs-org-roam-dailies" ;; package not available under guix
    "emacs-auctex"
    ;;"reftex" ;; included in emacs
    "emacs-hydra"
    ;;"emacs-js2-mode"
    ;;"emacs-js2-reflector-el"
    "emacs-company"
    "emacs-company-irony"
    "emacs-irony-mode"
    "emacs-irony-eldoc"
    "emacs-jedi"
    "emacs-epc"
    "emacs-company-jedi"
    "emacs-magit"
    "emacs-git-gutter"
    "emacs-git-timemachine"
    "emacs-flycheck"
    "emacs-all-the-icons"
    "emacs-all-the-icons-dired"
    "emacs-gruvbox-theme"
    "emacs-projectile"
    "emacs-counsel-projectile"
    "emacs-powerline"
    "emacs-diminish"
    "emacs-paredit"
    ;; "emacs-paredit-everywhere"
    "emacs-rainbow-delimiters"

    "emacs-clojure-mode"
    "emacs-cider"
    ;; "emacs-cider-hydra"
    "emacs-slime"
    "emacs-macrostep"
    "emacs-elisp-slime-nav"
    "mu"
    "emacs-mu4e-alert"
    "isync"
    "emacs-org-caldav"
    "emacs-helpful"
    "pinentry"
    "emacs-pinentry"
    ;;"emacs-erc"
    ;;"emacs-erc-log"
    ;;"emacs-erc-notify"
    ;;"emacs-erc-spelling"
    ;;"emacs-erc-autoaway"
    "emacs-toc-org"
    "emacs-ox-hugo"
    "emacs-flycheck-guile"
    "emacs-guix"
    "emacs-geiser"))

(define %programming-apps
  (list
   "gcc-toolchain"
   "make"
   "pkg-config"
   "texinfo"
   "sbcl"
   "curl"))

;; Home-apps
;; - The applications I want to have installed even for the base case

(define %home-apps
  (append
   (list
    "git"
    "ntfs-3g"
    "exfat-utils"
    "fuse-exfat"
    "bluez"
    "bluez-alsa"
    "pulseaudio"
    "tlp"
    "xf86-input-libinput"
    "nss-certs"
    "gvfs"
    "htop"
    "syncthing"
    "nyxt"
    "mpd")
   %emacs-packages))

(home-environment
 (packages (map specification->package+output
                %home-apps))
 (services
  (list
   (service home-bash-service-type
            (home-bash-configuration
             (guix-defaults? #t)
             (environment-variables
              '(("XDG_CACHE_HOME" . "~/.cache")
                ("INPUTRC" . "~/.inputrc")
                ("HISTFILE" . "$XDG_CACHE_HOME/.bash_history")
                ("EDITOR" . "emacs")
                ("PATH" . "~/.bin:~/.bin/statusbar:$PATH")
                ("BROWSER" . "nyxt")))))

   (service home-shepherd-service-type
            (home-shepherd-configuration
             (shepherd shepherd)
             (services
              (list
               (shepherd-service
                (provision '(syncthing))
                (documentation "Run 'syncthing' without calling the browser")
                (start #~(make-forkexec-constructor
                          (list #$(file-append syncthing "/bin/syncthing")
                                "-no-browser"
                                "-logflags=3" ; prefix with date and time
                                "-logfile=/home/paul/log/syncthing.log")))
                (stop #~(make-kill-destructor)))
               (shepherd-service
                (provision '(gpg-agent))
                (documentation "Run 'gpg-agent'")
                (start #~(make-system-constructor
                          #$(file-append gnupg "/bin/gpg-connect-agent /bye")))
                (stop #~(make-system-destructor
                         #$(file-append gnupg "/bin/gpgconf --kill gpg-agent"))))
               (shepherd-service
                (provision '(pulseaudio))
                (documentation "Run pulseaudio")
                (start #~(make-forkexec-constructor
                          (list #$(file-append pulseaudio "/bin/pulseaudio"))))
                (stop #~(make-kill-destructor)))
               (shepherd-service
                (provision '(mpd))
                (documentation "Run 'mpd' server")
                (start #~(make-forkexec-constructor
                          (list #$(file-append mpd "/bin/mpd ~/.config/mpd/mpd.conf"))))
                (stop #~(make-kill-destructor)))))))


   (simple-service 'inputrc-config
                   home-files-service-type
                   (list `("inputrc"
                           ,%inputrc)))

   (simple-service 'mpd-config
                   home-files-service-type
                   (list `("config/mpd/mpd.conf"
                           ,(local-file "/home/paul/dotfiles/home/files/mpd/mpd.conf"))))

   (simple-service 'emacs-config
                   home-files-service-type
                   (list `("emacs.d/early-init.el"
                           ,(local-file "/home/paul/dotfiles/home/files/emacs/early-init.el"))
                         `("emacs.d/init.el"
                           ,(local-file "/home/paul/dotfiles/home/files/emacs/init.el"))
                         `("emacs.d/lisp/my-org-mode.el"
                           ,(local-file "/home/paul/dotfiles/home/files/emacs/lisp/my-org-mode.el")))))))

Zeus

(define-module (home zeus core)
  #:use-module (gnu home)
  #:use-module ((home zeus gnupg) #:prefix gnupg:)
  #:use-module ((home zeus version-control) #:prefix vc:)
  #:use-module ((home zeus wm) #:prefix wm:)
  #:use-module ((home zeus emacs) #:prefix emacs:)
  #:use-module ((home zeus shell) #:prefix shell:)
  #:use-module ((home zeus password-utils) #:prefix pass:)
  #:use-module ((home zeus xdg) #:prefix xdg:)
  #:use-module ((home zeus ssh) #:prefix ssh:)
  #:use-module ((home zeus pipewire) #:prefix pw:)
  #:use-module ((home zeus terminals) #:prefix term:)
  #:use-module ((home zeus packages) #:select (packages)))

(home-environment
 (packages packages)
 (services
  (append
   wm:services
   vc:services
   gnupg:services
   emacs:services
   pass:services
   shell:services
   xdg:services
   ssh:services
   pw:services
   term:services)))

Profiles

Packages are installed into separate manifests that get installed as profiles which can be updated independently. These profiles get installed under the ~/.guix-extra-profiles path and sourced by ~/.profile when I log in.

Profile Manifests:

Base installation

Base installation includes the packages required for console based computer use, without xorg. This includes system admin tools etc, but not additional packages for specific tasks. Those are in the additional manifests below.

Printing

Guix packages

"system-config-printer"

System tools

"openssh"
"zip"
"unzip"
"htop"
"lf"
"gnupg"
"screen"
"stress"
"glances"

Syncthing

"syncthing"
"syncthing-gtk"

Document Readers

# Automatically adjust the document to full width
set adjust-open width

# Set the title to the filename
set window-title-basename true

# Larger scroll steps with j/k
set scroll-step 150

# Adjusting the document
map [normal] E adjust_window best-fit
map [fullscreen] E adjust_window best-fit
map [normal] e adjust_window width
map [fullscreen] e adjust_window width

# Toggling the inverted colours
map <C-i> recolor
map <C-g> abort

Guix packages

"zathura"
"zathura-pdf-mupdf"

Audio controls

Guix Packages

"alsa-utils"
"pavucontrol"

Password Management

Guix Packages

"password-store"
"keepassxc"
Syncing Passwords
pass git pull
pass git push

notify-send -i "emblem-synchronizing" "Passwords synced!"

I use GNU mcron for scheduling tasks to run periodically in the background.

Syncing Passwords

(job
   '(next-hour (range 0 24 4))
   "~/.bin/sync-passwords")

Guix Packages

"mcron"

Window

Manager I am using dwm, installed in the user account. These are the packages required for that, and the other support packages for the Xorg desktop use. By not having window managers installed at system level, the .xsession code is run by the login manager, so configuration of the window manager and associated programs is done there.

(specifications->manifest
 '("paulj-dwm"
   "paulj-dmenu"
   "dwmblocks"
   "paulj-st"
   "paulj-slock"
   "xev"
   "xset"
   "xrdb"
   "xhost"
   "xmodmap"
   "setxkbmap"
   "xrandr"
   "arandr"
   "xss-lock"
   "libinput"
   "xinput"
   "compton"
   "redshift"
   "gucharmap"
   "fontmanager"
   "brightnessctl"
   "xdg-utils"      ;; For xdg-open, etc
   "shared-mime-info"
   "dunst"
   "libnotify"
   "unclutter"
   ;; Settings Manager
   "xsettingsd"

   ;; GTK Themes
   "arc-icon-theme"
   "matcha-theme"
   "hicolor-icon-theme"
   "gnome-icon-theme"
   "gnome-backgrounds"
   "papirus-icon-theme"
   
   ;; Fonts
   "font-iosevka"
   "font-fira-mono"
   "font-fira-code"
   "font-abattis-cantarell"
   "font-dejavu"
   "font-google-noto"
   "font-gnu-freefont"
   "font-liberation"
   "font-awesome"
   "font-google-material-design-icons"
   "gs-fonts"

   ;; Packages for dwmblocks status bar
   "bmon"
   "xbacklight"
   "pamixer"
   "lm-sensors"
   "mpd-mpc"

   ;; Packages for dwm
   "bc"
   "maim"
   "calcurse"
   ))

.xsession

#if [ -z "$(pgrep -u paul shepherd)" ]; then
#  shepherd &
#fi

#xsettingsd &

#compton &
# TODO: Make the background changeable, and saved between sessions
#feh --bg-fill ~/backgrounds/mountains-1412683.jpg &
#unclutter &
#exec dwm

Fonts and Themes

I use xsettingsd as a minimal settings daemon for Xorg applications. It replaces similar daemons from desktop environments like GNOME and XFCE and enables me to use a simple configuration file like the following:

.config/xsettingsd/xsettingsd.conf:

Net/ThemeName "Matcha-dark-azul"
Net/IconThemeName "Papirus-Dark"
Gtk/DecorationLayout "menu:minimize,maximize,close"
Gtk/FontName "Cantarell 11"
Gtk/MonospaceFontName "Fira Mono 10"
Gtk/CursorThemeName "Adwaita"
Xft/Antialias 1
Xft/Hinting 0
Xft/HintStyle "hintnone"
Xft/DPI 96 # 1024 * DPI

I also have to do an extra step to make sure Emacs can find the font path from the “desktop” profile.

.config/fontconfig/fonts.conf:

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
  <dir>~/.dotfiles/fonts</dir>
  <dir>~/.guix-extra-profiles/window-manager/window-manager/share/fonts</dir>
  <alias>
    <family>Apple Color Emoji</family>
    <prefer>
      <family>Noto Color Emoji</family>
    </prefer>
  </alias>
</fontconfig>

Browsers

nyxt is the future!

(specifications->manifest
 '("ungoogled-chromium"
   "nyxt"
   "lagrange"
   "gsettings-desktop-schemas"
   "gnome-icon-theme"
   ))

Code

Languages and tools for programming.

;; Various developer tools that I use.  These might be split out into
;; platform-specific manifests at some point.

(specifications->manifest
 '(;; C/C++
   "gcc-toolchain"
   "make"
   "pkg-config"
   "texinfo"
   "llvm"
   "lld"
   "clang"

   ;; Python (3 by default)
   "python"
   "python2" ;; needed by gimp tools?

   ;; Docker
   ;;"docker-cli"

   ;; Java
   "icedtea"

   ;;lisp
   "sbcl"

   ;;clojure
   "clojure"
   "leiningen"
   
   ;; SDL
   "glu"
   "glfw"
   "sdl2"
   "sdl2-image"
   "sdl2-mixer"
   "sdl2-gfx"
   "sdl2-ttf"

   "curl"
   "virt-manager"))
   ;; "glibc" ;; For ldd

Image viewers and editors

(specifications->manifest
 '("feh"
   "gimp"
   "scrot"))

Music Player

music_directory   "~/music"
playlist_directory "~/.config/mpd/playlists"

auto_update "yes"
bind_to_address "127.0.0.1"
restore_paused "yes"
max_output_buffer_size "16384"

audio_output {
         type "pulse"
         name "pulse"
}               

audio_output {
         type   "fifo"
         name   "visualiser feed"
         path   "/tmp/mpd.fifo"
         format "44100:16:2"
}

Guix packages

"mpd"
"ncmpcpp"

Games

(specifications->manifest
 '("aisleriot"
   "gnome-mahjongg"))

Codecs and drivers

These packages are needed to enable many video formats to be played in browsers and video players. VAAPI drivers are also used to enable hardware-accelerated video decoding.

Guix Packages

"gstreamer"
"gst-plugins-base"
"gst-plugins-good"
"gst-plugins-bad"
"gst-plugins-ugly"
"gst-libav"
"intel-vaapi-driver"
"libva-utils"

Music Creation

An area of future investigation. This is currently as specified by David Wilson in his configuration.

;; Music creation tools

(specifications->manifest
 '(;; JACK tools
   "jack"
   "jack2"
   "jack-keyboard"
   "qjackctl"
   "patchage"

   ;; DAWs
   "ardour"
   "zrythm"

   ;; Guitar
   ;; "guitarix"
   ;; "guitarix-lv2"

   ;; Effects
   "calf"
   "g2reverb"
   "dragonfly-reverb"
   "wolf-shaper"

   ;; Synths
   "helm"
   "amsynth"
   "avldrums-lv2"
   "geonkick"
   "fluidsynth"
   "zynaddsubfx"

   ;; Mixing Tools
   "wolf-spectrum"))

Video Creation Tools

Also something for future exploration.

;; Video creation tools

(specifications->manifest
 '(;; Screen Capture and Streaming
   "obs"
   "ffmpeg"    ;; ffmpeg and ffplay
   "v4l-utils" ;; Get details about webcams: v4l2-ctl --list-devices

   ;; Screen recording with pulseaudio source 0 (-i 0)
   ;; ffmpeg -y -f x11grab -video_size 2560x1440 -i :0.0+0,0 -f pulse -ac 2 -i 0 -c:v libx264 -pix_fmt yuv420p -crf 0 -preset ultrafast ~/output.mp4 -v 0

   ;; Scaling video down to 1080p
   ;; ffmpeg -i output2.mp4 -s 1920x1080 ~/output2-scaled.mp4

   ;; Show webcam with specific resolution
   ;; ffplay -f v4l2 -framerate 60 -video_size hd480 /dev/video2 -v 0

   ;; Video Editing
   "blender"))

Applications

Desktop Notifications via Dunst

Dunst is a minimal interface for displaying desktop notifications. It is quite hackable but I’m not currently taking much advantage of its power. One useful feature is the ability to recall notification history; the keybinding is C-` in my configuration (though I’d prefer if I could invoke it from an Emacs keybinding somehow).

.config/dunst/dunstrc:

[global]
    ### Display ###
    monitor = 0

    # The geometry of the window:
    #   [{width}]x{height}[+/-{x}+/-{y}]
    geometry = "500x10-10+50"

    # Show how many messages are currently hidden (because of geometry).
    indicate_hidden = yes

    # Shrink window if it's smaller than the width.  Will be ignored if
    # width is 0.
    shrink = no

    # The transparency of the window.  Range: [0; 100].
    transparency = 10

    # The height of the entire notification.  If the height is smaller
    # than the font height and padding combined, it will be raised
    # to the font height and padding.
    notification_height = 0

    # Draw a line of "separator_height" pixel height between two
    # notifications.
    # Set to 0 to disable.
    separator_height = 1
    separator_color = frame

    # Padding between text and separator.
    padding = 8

    # Horizontal padding.
    horizontal_padding = 8

    # Defines width in pixels of frame around the notification window.
    # Set to 0 to disable.
    frame_width = 2

    # Defines color of the frame around the notification window.
    frame_color = "#89AAEB"

    # Sort messages by urgency.
    sort = yes

    # Don't remove messages, if the user is idle (no mouse or keyboard input)
    # for longer than idle_threshold seconds.
    idle_threshold = 120

    ### Text ###

    font = Cantarell 20

    # The spacing between lines.  If the height is smaller than the
    # font height, it will get raised to the font height.
    line_height = 0
    markup = full

    # The format of the message.  Possible variables are:
    #   %a  appname
    #   %s  summary
    #   %b  body
    #   %i  iconname (including its path)
    #   %I  iconname (without its path)
    #   %p  progress value if set ([  0%] to [100%]) or nothing
    #   %n  progress value if set without any extra characters
    #   %%  Literal %
    # Markup is allowed
    format = "<b>%s</b>\n%b"

    # Alignment of message text.
    # Possible values are "left", "center" and "right".
    alignment = left

    # Show age of message if message is older than show_age_threshold
    # seconds.
    # Set to -1 to disable.
    show_age_threshold = 60

    # Split notifications into multiple lines if they don't fit into
    # geometry.
    word_wrap = yes

    # When word_wrap is set to no, specify where to make an ellipsis in long lines.
    # Possible values are "start", "middle" and "end".
    ellipsize = middle

    # Ignore newlines '\n' in notifications.
    ignore_newline = no

    # Stack together notifications with the same content
    stack_duplicates = true

    # Hide the count of stacked notifications with the same content
    hide_duplicate_count = false

    # Display indicators for URLs (U) and actions (A).
    show_indicators = yes

    ### Icons ###

    # Align icons left/right/off
    icon_position = left

    # Scale larger icons down to this size, set to 0 to disable
    max_icon_size = 88

    # Paths to default icons.
    # TODO: Check the icon path...
    icon_path = /home/paul/.guix-extra-profiles/desktop/desktop/share/icons/gnome/256x256/status/:/home/daviwil/.guix-extra-profiles/desktop/desktop/share/icons/gnome/256x256/devices/:/home/daviwil/.guix-extra-profiles/desktop/desktop/share/icons/gnome/256x256/emblems/

    ### History ###

    # Should a notification popped up from history be sticky or timeout
    # as if it would normally do.
    sticky_history = no

    # Maximum amount of notifications kept in history
    history_length = 20

    ### Misc/Advanced ###

    # Browser for opening urls in context menu.
    browser = nyxt

    # Always run rule-defined scripts, even if the notification is suppressed
    always_run_script = true

    # Define the title of the windows spawned by dunst
    title = Dunst

    # Define the class of the windows spawned by dunst
    class = Dunst

    startup_notification = false
    verbosity = mesg

    # Define the corner radius of the notification window
    # in pixel size. If the radius is 0, you have no rounded
    # corners.
    # The radius will be automatically lowered if it exceeds half of the
    # notification height to avoid clipping text and/or icons.
    corner_radius = 4

    mouse_left_click = close_current
    mouse_middle_click = do_action
    mouse_right_click = close_all

# Experimental features that may or may not work correctly. Do not expect them
# to have a consistent behaviour across releases.
[experimental]
    # Calculate the dpi to use on a per-monitor basis.
    # If this setting is enabled the Xft.dpi value will be ignored and instead
    # dunst will attempt to calculate an appropriate dpi value for each monitor
    # using the resolution and physical size. This might be useful in setups
    # where there are multiple screens with very different dpi values.
    per_monitor_dpi = false

[shortcuts]

    # Shortcuts are specified as [modifier+][modifier+]...key
    # Available modifiers are "ctrl", "mod1" (the alt-key), "mod2",
    # "mod3" and "mod4" (windows-key).
    # Xev might be helpful to find names for keys.

    # Close notification.
    #close = ctrl+space

    # Close all notifications.
    #close_all = ctrl+shift+space

    # Redisplay last message(s).
    # On the US keyboard layout "grave" is normally above TAB and left
    # of "1". Make sure this key actually exists on your keyboard layout,
    # e.g. check output of 'xmodmap -pke'
    history = ctrl+grave

    # Context menu.
    context = ctrl+shift+period

[urgency_low]
    # IMPORTANT: colors have to be defined in quotation marks.
    # Otherwise the "#" and following would be interpreted as a comment.
    background = "#222222"
    foreground = "#888888"
    timeout = 10
    # Icon for notifications with low urgency, uncomment to enable
    #icon = /path/to/icon

[urgency_normal]
    background = "#1c1f26"
    foreground = "#ffffff"
    timeout = 10
    # Icon for notifications with normal urgency, uncomment to enable
    #icon = /path/to/icon

[urgency_critical]
    background = "#900000"
    foreground = "#ffffff"
    frame_color = "#ff0000"
    timeout = 0
    # Icon for notifications with critical urgency, uncomment to enable
    #icon = /path/to/icon

Desktop

The desktop.scm manifest holds the list of packages that I use to configure my desktop environment. The package names are pulled from the relevant sections titled Guix Packages in this file (system.org).

.config/guix/manifests/desktop.scm:

(specifications->manifest
 '(
   <<packages>>
))

Media Players

mpv

mpv is a simple yet powerful video player. Paired with youtube-dl it can even stream YouTube videos. mpv-mpris allows playback control via playerctl.

.config/mpv/mpv.conf

# Configure playback quality
vo=gpu
hwdec=vaapi
profile=gpu-hq
scale=ewa_lanczossharp
cscale=ewa_lanczossharp

# Start the window in the upper right screen corner
geometry=22%-30+20

# Save video position on quit
save-position-on-quit

# Enable control by MPRIS
script=~/.guix-extra-profiles/desktop/desktop/lib/mpris.so

# Limit the resolution of YouTube videos
ytdl=yes
ytdl-format=bestvideo[height<=?720]+bestaudio/best

# When playing audio files, display the album art
audio-display=attachment

# Keep the player open after the file finishes
keep-open

Guix Packages

"mpv"
"mpv-mpris"
"youtube-dl"
"playerctl"

User services

GNU Shepherd is used to manage services that run in the background after I log in. Documentation: https://guix.gnu.org/en/blog/2020/gnu-shepherd-user-services/

(use-modules (shepherd service)
             ((ice-9 ftw) #:select (scandir)))

;; Load all the files in the directory 'init.d' with a suffix '.scm'

(for-each
 (lambda (file)
   (load (string-append "init.d/" file)))
 (scandir (string-append (dirname (current-filename)) "/init.d")
          (lambda (file)
            (string-suffix? ".scm" file))))

(action 'shepherd 'daemonize)
(define gpg-agent
  (make <service>
    #:provides '(gpg-agent)
    #:respawn? #t
    #:start (make-system-constructor "gpg-connect-agent /bye")
    #:stop (make-system-destructor "gpgconf --kill gpg-agent")))

(register-services gpg-agent)
(start gpg-agent)
(define mcron
  (make <service>
    #:provides '(mcron)
    #:respawn? #t
    #:start (make-forkexec-constructor '("mcron"))
    #:stop  (make-kill-destructor)))

(register-services mcron)
(start mcron)

Currently this isn’t working as expected, so set to not tangle.

(define emacs
  (make <service>
    #:provides '(emacs)
    #:requires '()
    #:docstring "Start emacs daemon"
    #:start (make-system-constructor "emacs --daemon")
    #:stop (make-system-destructor "emacsclient --eval \"(kill-emacs)\"")))

(register-services emacs)
(start emacs)
(define syncthing
  (make <service>
    #:provides '(syncthing)
    #:docstring "Run `syncthing' without calling the browser"
    #:start (make-forkexec-constructor
             '("syncthing" "-no-browser"
               "-logflags=3" ; prefix with date and time
               "-logfile=/home/paul/log/syncthing.log"))
    #:stop (make-kill-destructor)
    #:respawn #t))

(register-services syncthing)
(start syncthing)
(define pulseaudio
  (make <service>
    #:provides '(pulseaudio)
    #:respawn? #t
    #:start (make-forkexec-constructor '("pulseaudio"))
    #:stop  (make-kill-destructor)))

(register-services pulseaudio)

(start pulseaudio)
(define mpd
  (make <service>
    #:provides '(mpd)
    #:respawn? #t
    #:start (make-forkexec-constructor '("mpd"))
    #:stop (make-kill-destructor)))

(register-services mpd)
(start mpd)

Profile Management

To make the management of multiple profiles easier, I’ve created a couple of shell scripts:

Activating Profiles

This script accepts a space-separated list of manifest file names (without extension) under the ~/.config/guix/manifests folder and then installs those profiles for the first time. For example:

activate-profiles desktop emacs music

.bin/activate-profiles:

# -*- buffer-read-only: t; -*-
# NOTE: This file is generated from ~/.dotfiles/System.org.  Please see commentary there.

if [ $HOSTNAME = "zeus" ] # This will need modification when expanding number of guix systems.
                          # Currently zeus is the only one.
then
  GREEN='\033[1;32m'
  RED='\033[1;30m'
  NC='\033[0m'
  GUIX_EXTRA_PROFILES=$HOME/.guix-extra-profiles

  profiles=$*
  if [[ $# -eq 0 ]]; then
    profiles="$HOME/.config/guix/manifests/*.scm";
  fi
  
  for profile in $profiles; do
    # Remove the path and file extension, if any
    profileName=$(basename $profile)
    profileName="${profileName%.*}"
    profilePath="$GUIX_EXTRA_PROFILES/$profileName"
    manifestPath=$HOME/.config/guix/manifests/$profileName.scm
    
    if [ -f $manifestPath ]; then
      echo
      echo -e "${GREEN}Activating profile:" $manifestPath "${NC}"
      echo
      
      mkdir -p $profilePath
      guix package --manifest=$manifestPath --profile="$profilePath/$profileName"
      
      # Source the new profile
      GUIX_PROFILE="$profilePath/$profileName"
      if [ -f $GUIX_PROFILE/etc/profile ]; then
        . "$GUIX_PROFILE"/etc/profile
      else
        echo -e "${RED}Couldn't find profile:" $GUIX_PROFILE/etc/profile "${NC}"
      fi
    else
      echo "No profile found at path" $profilePath
    fi
  done
fi

Updating Profiles

This script accepts a space-separated list of manifest file names (without extension) under the ~/.config/guix/manifests folder and then installs any updates to the packages contained within them. If no profile names are provided, it walks the list of profile directories under ~/.guix-extra-profiles and updates each one of them.

update-profiles emacs

.bin/update-profiles:

# -*- buffer-read-only: t; -*-
# NOTE: This file is generated from ~/.dotfiles/System.org.  Please see commentary there.

GREEN='\033[1;32m'
NC='\033[0m'
GUIX_EXTRA_PROFILES=$HOME/.guix-extra-profiles

profiles=$*
if [[ $# -eq 0 ]]; then
    profiles="$GUIX_EXTRA_PROFILES/*";
fi

for profile in $profiles; do
  profileName=$(basename $profile)
  profilePath=$GUIX_EXTRA_PROFILES/$profileName

  echo
  echo -e "${GREEN}Updating profile:" $profilePath "${NC}"
  echo

  guix package --profile="$profilePath/$profileName" --manifest="$HOME/.config/guix/manifests/$profileName.scm"
done

Dotfiles Management

Since I keep all of my important configuration files in Org Mode code blocks, I have to ensure that the real configuration files are kept up to date when I sync the latest changes to my dotfiles repo. I’ve written a couple of scripts to simplify that process:

Syncing

When I want to sync my dotfiles repo into my local clone which likely has uncommitted changes, I run sync-dotfiles. This script first makes sure that all Org files are saved in a running Emacs instance and then stashes everything before pulling the latest changes from origin. After pulling, the stash is popped and then the script verifies there are no merge conflicts from the stash before proceeding. If there are no conflicts, update-dotfiles is run, otherwise I’ll fix the merge conflicts manually and run update-dotfiles myself.

.bin/sync-dotfiles

# -*- buffer-read-only: t; -*-
# Sync dotfiles repo and ensure that dotfiles are tangled correctly afterward

GREEN='\033[1;32m'
BLUE='\033[1;34m'
RED='\033[1;30m'
NC='\033[0m'

# Navigate to the directory of this script (generally ~/.dotfiles/.bin)
cd $(dirname $(readlink -f $0))
cd ..

echo
echo -e "${BLUE}Saving Org buffers if Emacs is running...${NC}"
emacsclient -u -e "(org-save-all-org-buffers)" -a "echo 'Emacs is not currently running'"

echo -e "${BLUE}Stashing existing changes...${NC}"
stash_result=$(git stash push -m "sync-dotfiles: Before syncing dotfiles")
needs_pop=1
if [ "$stash_result" = "No local changes to save" ]; then
    needs_pop=0
fi

echo -e "${BLUE}Pulling updates from dotfiles repo...${NC}"
echo
git pull origin master
echo

if [[ $needs_pop -eq 1 ]]; then
    echo -e "${BLUE}Popping stashed changes...${NC}"
    echo
    git stash pop
fi

unmerged_files=$(git diff --name-only --diff-filter=U)
if [[ ! -z $unmerged_files ]]; then
   echo -e "${RED}The following files have merge conflicts after popping the stash:${NC}"
   echo
   printf %"s\n" $unmerged_files  # Ensure newlines are printed
else
    update-dotfiles
fi

Updating

Updating my dotfiles requires running a script in Emacs to loop over all of my literate configuration .org files and run org-babel-tangle-file to make sure all of my configuration files are up to date.

.bin/update-dotfiles .emacs.d/tangle-dotfiles.el

These two files is already in the .bin and .emacs.d directory respectively. Otherwise it’s not straight forward to tangle all of the .org files.

System Installation

Until I migrate its Markdown contents into Org syntax, consult .config/guix/systems/README.md for installation instructions.

Notes

Bluetooth Setup

If you need to manually connect to Bluetooth audio devices using bluetoothctl, as I currently do in Guix, you’ll need to enter these commands at the bluetoothctl prompt:

system-alias "my-hostname" # To configure your laptop's device name
default-agent
power on
scan on
# Wait for your device to appear
pair 04:52:C7:5E:5C:A8
trust 04:52:C7:5E:5C:A8 # To enable auto-connect
connect 04:52:C7:5E:5C:A8