- Notifications
You must be signed in to change notification settings - Fork1
my GNU Guix/Emacs/exwm configs and stuff
License
lambdanil/emacs-stuff
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
{{{disable-search}}}
Clickhere for a properly formatted HTML version :)
Debian with EXWM
Debian with MATE
openSUSE Aeon with Gnome
All of these are different flavors of this configuration.
This is my highly modular and entirely literate full-system configuration.
Let’s talk about the implementation first - how I handle it and why.
There are multiple important ideas in place here.
I should be able to deploy this configuration quickly and easily, achieving an identical (= reasonably close to identical) result every time. Most importantly all of the relevant dotfiles should be present and should be set up in a way that allows them to work when deployed anywhere. Every file that is relevant to reproducing this configuration should be present. To ensure this is the case I sometimes try to set up my environment in a virtual machine so that I can check if I missed any important files.The user environment should exist almost entirely separately from the system environment.I should be able to easily pick which parts of the configuration I want to deploy.There should be no confusion over how to deploy this system, nor should there be any confusion over where individual files belong, etc. All files should have their own clearly defined place.The configuration should be easy to keep current, it should also be robust and reliable.I’ll start by outlining the structure, then I’ll go over each step and explain its role.
The centerpiece of this implementation is theREADME.org file. This file includes all of the plaintext dotfiles that are part of this system, as well as information about every other file. Usingorg to describe these files allows for the configuration to remain readable and properly organized. Each chapter in this file corresponds to a single “module” (more on these below).Thisorg file is thentangled into theconfigs directory, where the individual files are placed into separate directories, or as I called them above, “modules”.
guix module to ship dependencies. Non-plaintext modules are also present in theconfigs directory. These aren’t tangled but are still described in theorg file. These are mostly things that cannot be defined in plaintext such as fonts, icons, wallpapers and similar.All of these modules are symlinked into the home directory using GNU Stow. Using the modules in this way allows picking out specific parts of the configuration.
The GNU Guix package manager is used for installing dependencies. This means the dependencies for this environment can be installed anywhere where Guix can run, further separating the user environment from the rest of the system and ensuring better reliability.- Make sure to tangle this file with ~org-babel-tangle~ to actually apply changes made to plaintext files.
- Install Guix and run a
guix pullbefore importing Guix package manifests. - The guix manifest providing the bare minimum dependencies is part of the
guixmodule and can be installed withguix package -m $HOME/.guix-manifest-tiny - The configuration itself is placed in ~~/git/emacs-stuff/configs~ and ~~/git/emacs-stuff/configs-root~
- From there individual modules can be installed:
cd ./configsstow$module -t$HOMEcd ..
- Or all at once, overwriting existing:
cd ./configsstow* -t$HOME --adoptcd ..git reset --hard
- The
configs-rootdirectory contains system environment modules. Please ensure the permissions are set correctly.- These files should be linked into the root filesystem (
/) instead of$HOME.
- These files should be linked into the root filesystem (
- From there individual modules can be installed:
Here I store my relevant autostart scripts.
First we need to add a .desktop file for the DE to load.
[Desktop Entry]Type=ApplicationName=loginctlExec=/home/nil/.loginctlComment=Run loginctl script on loginX-GNOME-Autostart-enabled=trueHere’s the actual autostart script.
For some reason Xwayland won’t automatically read my Xresources, so I ensure they are read manually.
xrdb~/git/emacs-stuff/configs/X/.XresourcesI use Syncthing to sync my org agenda between different devices.
#syncthing serve --no-browser &Having an Emacs daemon means instant startup times for emacsclient.
# $HOME/.local/bin/emacs --daemon &emacs --daemon&
These are only necessary when using a standalone window manager.
# nm-applet &# xhost +si:localuser:nil# $HOME/.guix-profile/libexec/xdg-desktop-portal -r &dbus-update-activation-environment --all&# xinput set-prop 'pointer:Logitech G305' 'libinput Accel Profile Enabled' 0 1 &# /usr/bin/gnome-keyring-daemon --start --components=secrets &# playerctld &
These are commands specifically only used when on my laptop.
if ["$HOSTNAME"="lainpad" ];then setxkbmap cz -option'ctrl:swapcaps'& xinput set-prop'pointer:Logitech G305''libinput Accel Profile Enabled' 0 1 0& xinput set-prop'pointer:Logitech USB Receiver Mouse''libinput Accel Profile Enabled' 0 1 0&# echo "nothing to do!"fi
Let’s move on to configuring the shell - bash in my case.
I like to have a function to clear old guix builds by regex, helps keep the amount of garbage down.
clean_build () {# clear guix build by regexif [[$1 ]];then CLEAR_BUILD_PATHS="$(ls --color=never -d /gnu/store/$1| tr'\n''')"elseecho"no regex specified"return 1fiif [[$(echo"$CLEAR_BUILD_PATHS"| wc -c)-ne 1 ]];then guix gc --delete$CLEAR_BUILD_PATHSelseecho"no match for regex found"return 1fi}
A small bit used to remind me when I’m inside a Guix environment.
if [-n"$GUIX_ENVIRONMENT" ];thenif [[$PS1=~ (.*)"\\$" ]];then PS1="${BASH_REMATCH[1]} [env]\\\$"fifi
Set up the prompt with fancy colors and a cute lambda. In casetput (part ofncurses) isn’t available an alternative is provided.
if [[$-==*i* ]]then _GREEN=$(tput setaf 2) _MAGENTA=$(tput setaf 207) _BLUE=$(tput setaf 4) _RED=$(tput setaf 1) _CYAN=$(tput setaf 45) _RESET=$(tput sgr0) _BOLD=$(tput bold)# _GREEN="\e[0;32m"# _MAGENTA="\e[0;35m"# _BLUE="\e[0;34m"# _RED="\e[0;31m"# _CYAN="\e[0;36m"# _RESET="\e[0m"# _BOLD="\e[1m"# export LD_LIBRARY_PATH=$LIBRARY_PATHexport PS1="[${_MAGENTA}\u${_RESET}@${_CYAN}\h${_RESET}] \t\n(\w) λ"fi
Guix needs to be separately sourced in non-container interactive shells.
if [!-f /.dockerenv ]&& [!-f /run/.containerenv ]&& [-d"$HOME/.guix-profile" ]&& [[$-==*i* ]];then GUIX_PROFILE="$HOME/.guix-profile"."$GUIX_PROFILE/etc/profile" GUIX_PROFILE="$HOME/.config/guix/current"."$GUIX_PROFILE/etc/profile"fi
Add some aliases. Theld_libs is useful for exporting Guix’s libraries to run precompiled software.
alias sudo="sudo -p\"[sudo] what's youw p-pa-password, Nil-chan?~ ❤️\""# alias apt="nala"alias ls="ls --color"alias ld_libs="export LD_LIBRARY_PATH=\$LIBRARY_PATH"alias tf="xrandr --output HDMI-A-1 --set TearFree"alias hyfetch="hyfetch --ascii-file ~/git/neofetch-logo"alias deb="xhost +si:localuser:nil && distrobox enter debian --"alias glibc="bwrap --bind /var/chroots/debian / --dev /dev --proc /proc --bind /sys /sys --ro-bind /sys/dev/char /sys/dev/char --ro-bind /sys/devices/pci0000:00 /sys/devices/pci0000:00 --bind /run /run --bind /home /home --ro-bind /dev/dri /dev/dri --ro-bind /etc/resolv.conf /etc/resolv.conf --ro-bind /etc/passwd /etc/passwd --ro-bind /etc/group /etc/group"alias glibc-root="doas chroot /var/chroots/debian /bin/bash"alias reconf="sudo -E guix system reconfigure /etc/guix-systems/$HOSTNAME.scm"
Finally some variable exports - for Guix and Flatpak and a sillysudo prompt.
export XDG_DATA_DIRS="/var/lib/flatpak/exports/share:$HOME/.local/share/flatpak/exports/share:$XDG_DATA_DIRS"export GUIX_PACKAGE_PATH="/etc/guix-modules"export SUDO_PROMPT='[sudo] what'\''s youw p-pa-password, Nil-chan?~ ❤️'
Let’s move onto the bash profile, used in login shells.
First run the bashrc if necessary.
# Honor per-interactive-shell startup fileif [-f~/.bashrc ];then.~/.bashrc;fiexport LANG=cs_CZ.utf-8export PATH=$PATH:$HOME/.local/bin:$HOME/.bin:$HOME/.py/bin:/usr/lib/go-1.22/bin
Then we can go ahead and export Guix, but only when not inside a container. I also verify the Guix profile exists before trying to load it.
if [!-f /.dockerenv ]&& [!-f /run/.containerenv ]&& [-d"$HOME/.guix-profile" ];then# source "$HOME/.guix-profile/etc/profile"# source "$HOME/.config/guix/current/etc/profile"export PATH="$PATH:$HOME/.guix-profile/bin"export XDG_DATA_DIRS="$XDG_DATA_DIRS:$HOME/.guix-profile/share"export GUIX_LOCPATH="$HOME/.guix-profile/lib/locale"# export GUIX_SANDBOX_EXTRA_SHARES="/mnt/media/nil/external/Steam"fi
Laptop-specific environment variables to ensure hardware video acceleration works.
if ["$HOSTNAME"="lainpad" ]&& [!-f /run/.containerenv ]thenexport LIBVA_DRIVER_NAME=iHDexport LD_PRELOAD=/home/nil/.guix-profile/lib/dri/iHD_drv_video.soexport MOZ_USE_XINPUT2=1fi
This makes sway start up automatically when logging intotty2, eliminating the need for a login manager.
if [["$(tty)"=="/dev/tty2" ]]thenexport XDG_CURRENT_DESKTOP=swayexec dbus-run-session -- swayfi
Here I have some custom binaries, mostly scripts and stuff.
Just a screenshot shortcut.
grim -| wl-copy…And the same but for a selected area.
grim -g"$(slurp)" -| wl-copy
And a shortcut for opening chromium in incognito mode.
chromium --incognito
I use distrobox docker/podman (depending on which is available, podman is prefered) on my installation. Here is some necessary configs for the containers.
This isn’t super secure.
{"default": [ {"type":"insecureAcceptAnything" } ],"transports": {"docker-daemon": {"": [{"type":"insecureAcceptAnything"}] } }}Add the necessary registries.
# Copied from https://raw.githubusercontent.com/projectatomic/registries/master/registries.fedora[registries.search]registries = ['docker.io', 'registry.fedoraproject.org', 'registry.access.redhat.com'][registries.insecure]registries = []# Docker only[registries.block]registries = []This module provides the Czech hunspell dictionary since it currently isn’t available in Guix. The files were taken from theLibreOffice dictionaries repository.
My long distrohopping journey has eventually landed me on Debian, which is the system I choose to run nowadays. In order to simplify deploying my configuration on new Debian installations I’ve decided to add a simple list ofapt-installable dependencies.
sudo apt install xinput git stow gcc sbcl guile-3.0 libtool-bin clang cmake make libenchant-2-dev
Emacs is installed separately from the backports.
sudo apt install emacs -t bookworm-backports
This module provides my Firefox configuration. For tree style tab it’s necessary to import$HOME/.mozilla/firefox/default/treestyletab.css.
This module provides the necessary fonts.
This module provides straightforward GTK3 configuration.
[Settings]gtk-cursor-theme-name=Simp1e-Adwgtk-icon-theme-name=Papirus-Darkgtk-theme-name=Draculagtk-font-name=Noto Sans 10gtk-application-prefer-dark-theme=1This adds a bit of padding to the XFCE terminal.
VteTerminal,vte-terminal {padding:8px;}
This module depends on the ~guix-modules~ module for certain package definitions! GNU Guix is a very important part of my configuration.
Just a straightforward (and very messy!) package manifest. I have decided to keep this as a separate file rather than tangle it. This allows me to useguix package --export-manifest to update it.
Due to the above manifest becoming rather bloated a minimal replacement is provided here. This includes only the necessary dependencies to get the system running correctly.
(specifications->manifest (list"glibc-locales""gcc""syncthing""sbcl""guile""cmake""make""emacs""hunspell-dict-en-us""enchant"))
Here is the setup for channels, additions here arenonguix andguix-gaming-channels.
(use-modules (guix ci))(cons* (channel-with-substitutes-available %default-guix-channel"https://ci.guix.gnu.org") (channel (name'nonguix) (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-gaming-games) (url"https://gitlab.com/guix-gaming-channels/games.git");; Enable signature verification: (introduction (make-channel-introduction"c23d64f1b8cc086659f8781b27ab6c7314c5cca5" (openpgp-fingerprint"50F3 3E2E 5B0C 3D90 0424 ABE8 9BDC F497 A4BB CC7F")))) %default-channels)
guix-run is a tiny script I originally found on Github. Unfortunately I do not have the original link anymore.
export LD_LIBRARY_PATH=$LIBRARY_PATHRUNNER=$(find~/.guix-profile/lib/ -name'ld-linux-*.so*')$RUNNER"$@"#!/bin/shexport LD_LIBRARY_PATH=$LIBRARY_PATHRUNNER
Sway is one of the window managers I have a full setup for. While I do usually prefer to use a DE or EXWM, I do have sway here for completeness.
Set some initial variables first.
set $mod Mod4set $HOME /home/nil#set $term emacsclient -c -e "(let ((current-prefix-arg '(4))) (call-interactively 'vterm))"set $term xfce4-terminal# set $files emacsclient -c -e '(dired "~")'set $files thunarset $wallpaper $HOME/.local/share/wallpapers/deijurj-adfa3118-4bd6-4350-b9db-43d4d835536c.jpgset $lock swaylock -i $wallpaperset $scr_off swaymsg "output * dpms off"set $scr_on swaymsg "output * dpms on"font pango:Fira Code 9gaps inner 8pxfor_window [class="^.*"] border pixel 2floating_modifier $modseat seat0 xcursor_theme Breeze_Snow# tiling_drag modifier titlebarConfigure input.
input * {xkb_layout "cz"xkb_options "ctrl:swapcaps"accel_profile flatxkb_numlock enabled}Window rules for transparency, etc.
for_window [app_id="thunar"] opacity 0.96I decrease the time to render a frame as much as possible (12ms in this case), this significantly helps eliminate input lag. This value needs to be increased if stuttering becomes an issue.
# output HDMI-A-2 max_render_time 12# output HDMI-A-1 disable# swaymsg -t get_outputsExecute the startup environment, this is necessary for XDG autostart files.
exec dbus-update-activation-environment DISPLAY WAYLAND_DISPLAY SWAYSOCKexec --no-startup-id $HOME/.loginctlexec --no-startup-id swaybg -i $wallpaperexec --no-startup-id gsettings set org.gnome.desktop.interface gtk-theme Yaru-magenta-darkexec --no-startup-id gsettings set org.gnome.desktop.interface icon-theme Yaru-magenta-dark# exec /usr/libexec/polkit-gnome-authentication-agent-1Configureswaylock to start automatically.
exec --no-startup-id swayidle timeout 480 '$lock' timeout 600 '$scr_off' resume '$scr_on'Now assigning some keybindings; all of these are rather self-explanatory.
set $refresh_i3status killall -SIGUSR1 i3statusbindsym XF86AudioRaiseVolume exec --no-startup-id pamixer -i 10bindsym XF86AudioLowerVolume exec --no-startup-id pamixer -d 10bindsym XF86AudioMute exec --no-startup-id pamixer -tbindsym XF86AudioMicMute exec --no-startup-id pactl set-source-mute @DEFAULT_SOURCE@ togglebindsym Print exec --no-startup-id $HOME/.bin/scrbindsym Shift+Print exec --no-startup-id $HOME/.bin/scr-areabindsym $mod+Return exec $termbindsym $mod+q killbindsym $mod+Shift+Control+l exec --no-startup-id $lockbindsym $mod+d exec --no-startup-id rofi -theme ~/.config/wofi/nord.rasi -show drunbindsym $mod+b exec --no-startup-id rofi -theme ~/.config/wofi/nord.rasi -show windowbindsym $mod+e exec --no-startup-id emacsclient -cbindsym $mod+a exec --no-startup-id $filesWindow focus:
bindsym $mod+h focus leftbindsym $mod+j focus downbindsym $mod+k focus upbindsym $mod+l focus rightbindsym Mod1+Tab focus rightbindsym Mod1+Shift+Tab focus leftbindsym $mod+Left focus leftbindsym $mod+Down focus downbindsym $mod+Up focus upbindsym $mod+Right focus rightMoving and resizing windows:
bindsym $mod+m sticky toggle bindsym $mod+Shift+h move leftbindsym $mod+Shift+j move downbindsym $mod+Shift+k move upbindsym $mod+Shift+l move rightbindsym $mod+Tab move rightbindsym $mod+Shift+Tab move leftbindsym $mod+Shift+Left move leftbindsym $mod+Shift+Down move downbindsym $mod+Shift+Up move upbindsym $mod+Shift+Right move rightbindsym $mod+Control+h resize shrink width 2 px or 2 pptbindsym $mod+Control+j resize grow height 2 px or 2 pptbindsym $mod+Control+k resize shrink height 2 px or 2 pptbindsym $mod+Control+l resize grow width 2 px or 2 pptSplitting and layouts:
bindsym $mod+v split hbindsym $mod+c split vbindsym $mod+s split togglebindsym $mod+f fullscreen togglebindsym $mod+Shift+s layout stackingbindsym $mod+w layout tabbedbindsym $mod+y layout toggle splitbindsym $mod+Shift+space floating togglebindsym $mod+space focus mode_togglebindsym $mod+Shift+x focus parentbindsym $mod+x focus childNext set some workspace names and bindings.
set $ws1 "1:λ"set $ws2 "2:α"set $ws3 "3:β"set $ws4 "4:γ"set $ws5 "5:δ"set $ws6 "6:ε"set $ws7 "7:ζ"set $ws8 "8:η"set $ws9 "9:θ"set $ws10 "10:ι"# switch to workspacebindsym $mod+plus workspace $ws1bindsym $mod+ecaron workspace $ws2bindsym $mod+scaron workspace $ws3bindsym $mod+ccaron workspace $ws4bindsym $mod+rcaron workspace $ws5bindsym $mod+zcaron workspace $ws6bindsym $mod+yacute workspace $ws7bindsym $mod+aacute workspace $ws8bindsym $mod+iacute workspace $ws9bindsym $mod+eacute workspace $ws10# move focused container to workspacebindsym $mod+Shift+plus move container to workspace $ws1bindsym $mod+Shift+ecaron move container to workspace $ws2bindsym $mod+Shift+scaron move container to workspace $ws3bindsym $mod+Shift+ccaron move container to workspace $ws4bindsym $mod+Shift+rcaron move container to workspace $ws5bindsym $mod+Shift+zcaron move container to workspace $ws6bindsym $mod+Shift+yacute move container to workspace $ws7bindsym $mod+Shift+aacute move container to workspace $ws8bindsym $mod+Shift+iacute move container to workspace $ws9bindsym $mod+Shift+eacute move container to workspace $ws10Now for session commands:
bindsym $mod+Shift+r reload# restart sway inplace (preserves your layout/session, can be used to upgrade sway) # leftover from i3, not supported by sway! # bindsym $mod+Control+r restart# exit sway (logs you out of your Wayland session)bindsym $mod+Shift+e exec "swaynag -t warning -m 'You pressed the exit shortcut. Do you really want to exit sway? This will end your Wayland session.' -B 'Yes, exit sway' 'swaymsg exit'"Handle colors next; following the Nord colorscheme here.
set $bg-color #b19cd8set $active-border-color #FFFFFFset $inactive-bg-color #2E3440set $text-color #f5f5f5set $inactive-text-color #d8dee9set $urgent-bg-color #A7C7E7client.focused $bg-color $bg-color $inactive-bg-color $active-border-color# nice transparent borders for unfocused windows :3 client.unfocused $inactive-bg-color $inactive-bg-color $inactive-text-color $inactive-bg-color #00000000client.focused_inactive $inactive-bg-color $inactive-bg-color $inactive-text-color $inactive-bg-colorclient.urgent $urgent-bg-color $urgent-bg-color $text-color $urgent-bg-colorclient.background #2B2C2BConfigure the sway bar.
bar {#strip_workspace_numbers yesposition topmode dockstatus_padding 4tray_padding 4I pipe the status command through a wrapper, which makes extra modifications to the output.
status_command i3status | ~/.config/sway/i3status_wrapper.pyAnd finally set colors.
colors {background $inactive-bg-colorseparator $inactive-bg-color# border background textfocused_workspace $bg-color $bg-color $inactive-bg-colorinactive_workspace $inactive-bg-color $inactive-bg-color $inactive-text-colorurgent_workspace $urgent-bg-color $urgent-bg-color $inactive-bg-color}}This small python wrapper adds the currently playing song as a prefix to the status.
importjsonimportsubprocessimportsysdefget_status():status=subprocess.getoutput('playerctl status')if'Playing'instatus:return"▶️"elif'Paused'instatus:return"⏸️"elif'Stopped'instatus:return"⏹️"else:return""defget_current_music_title():title=subprocess.getoutput('playerctl metadata title')if'No players found'intitle:return""artist=subprocess.getoutput('playerctl metadata artist')status=get_status()returnf"{status}{artist} -{title}"defprint_line(message):""" Non-buffered printing to stdout. """sys.stdout.write(message+'\n')sys.stdout.flush()defread_line():""" Interrupted respecting reader for stdin. """# try reading a line, removing any extra whitespacetry:line=sys.stdin.readline().strip()# i3status sends EOF, or an empty lineifnotline:sys.exit(3)returnline# exit on ctrl-cexceptKeyboardInterrupt:sys.exit()if__name__=='__main__':# Skip the first line which contains the version header.print_line(read_line())# The second line contains the start of the infinite array.print_line(read_line())whileTrue:line,prefix=read_line(),''# ignore comma at start of linesifline.startswith(','):line,prefix=line[1:],','j=json.loads(line)music_title=get_current_music_title()# this is where the magic happens# https://i3wm.org/docs/i3bar-protocol.htmlj.insert(0, {# 'background': '#FFFFFF','full_text':'%s'%music_title,'color':'#b19cd8','name':'music_title','separator_block_width':0})j.insert(1, {# 'background': '#FFFFFF','full_text':' || ','color':'#FFFFFF','name':'separator','separator_block_width':0})print_line(prefix+json.dumps(j))
This is the actual i3status config.
Let’s set some colors first of all.
general {output_format = "i3bar"colors = truecolor_good = "#a3be8c"color_degraded = "#ebcb8b"color_bad = "#bf616a"interval = 1}Now set the order of the items.
order += "ethernet _first_"order += separatororder += "volume master"order += separatororder += "tztime local"And configure the individual items.
volume master {format = "|| 🔊 %volume"format_muted = "|| 🔇 (%volume)"device = "default"mixer = "Master"}wireless _first_ {format_up = "W: (%quality at %essid) %ip"format_down = "W: down"}ethernet _first_ {format_up = "🌏 %ip"format_down = "🚫 net down"}battery all {format = "%status %percentage %remaining"}disk "/" {format = "%avail"}load {format = "%1min"}memory {format = "%used | %available"threshold_degraded = "1G"format_degraded = "MEMORY < %available"}tztime local {format = "|| 🗓️ %H:%M %d.%m.%Y"}This modules provides custom icons.
Wofi is an application launcher, I use it with sway and sometimes even with different desktop environments.
This is my chosen wofi theme, fits with the sway theme! Original sourcehere.
* {font:"Fira Code 10";nord0:#2e3440;nord1:#3b4252;nord2:#434c5e;nord3:#4c566a;nord4:#d8dee9;nord5:#e5e9f0;nord6:#eceff4;nord7:#8fbcbb;nord8:#b19cd8;/* purple-ish thingy */nord9:#81a1c1;nord10:#5e81ac;nord11:#bf616a;nord12:#d08770;nord13:#ebcb8b;nord14:#a3be8c;nord15:#b48ead;background-color: transparent;text-color:@nord4;accent-color:@nord8;margin:0px;padding:0px;spacing:0px;}window {location: north;width:100%;background-color:@nord0;children: [ mainbox,message ];}mainbox {orientation: horizontal;children: [ inputbar,listview ];}inputbar {width:25%;padding:1px8px;spacing:8px;children: [ prompt, entry ];}prompt,entry,element-text,element-icon {vertical-align:0.5;}prompt { text-color:@accent-color;}listview {layout: horizontal;}element {padding:1px8px;spacing:4px;}elementnormalurgent {text-color:@nord13;}elementnormalactive { text-color:@accent-color;}elementselected {text-color:@nord0;}elementselectednormal { background-color:@accent-color;}elementselectedurgent {background-color:@nord13;}elementselectedactive {background-color:@nord8;}element-icon {size:0.75em;}element-text {text-color: inherit;}
This bit is for a customstartx command, made to work with Guix.
DIR=$HOME/.guix-profile$DIR/bin/xinit --$DIR/bin/Xorg :0 vt1 -keeptty \ -configdir$DIR/share/X11/xorg.conf.d \ -modulepath$DIR/lib/xorg/modules
Here I apply some basic Xorg configuration.
Just set the DPI and configure basic font rendering.
Xft.antialias: trueXft.autohint: falseXft.dpi: 96Xft.hinting: trueXft.hintstyle: hintslightXft.lcdfilter: lcddefaultXft.rgba: rgbEmacs.Background: blackThis here is necessary for Debian; makes Guix programs accessible from a desktop session.
export PATH="$PATH:$HOME/.guix-profile/bin:/var/lib/flatpak/exports/bin:$HOME/.bin"export XDG_DATA_DIRS="$XDG_DATA_DIRS:$HOME/.guix-profile/share"
xfce4-terminal is my chosen terminal emulator. It’s very easy to get up and running and supports all the basic features I need.
This module provides a few nice wallpapers I like to use. The path to these wallpapers is$HOME/.local/share/wallpapers.
Here is my Guix system configuration.
Let’s start by adding my custom modules. This isn’t exactly clean but it is functional.
(add-to-load-path"/etc/guix-modules")Now import the actual modules
(use-modules (gnu) (gnu services) (gnu services dbus) (nil services throttled) (gnu services pm) (gnu packages gnome) (gnu packages firmware) (nongnu packages linux) (nongnu system linux-initrd) (gnu services virtualization) (gnu services shepherd) (gnu services avahi) (gnu services admin) (gnu system nss) (guix channels) (gnu services mcron) (gnu services docker) (gnu packages wm) (nil services mount-rshared) (nil services virsh) (nil packages gnome) (gnu services linux) (gnu packages gnome) (gnu packages) (gnu packages fonts) (gnu packages networking) (guix gexp) (guix packages) (srfi srfi-1) (ice-9 match))(use-service-modules linux desktop networking ssh xorg)(use-package-modules linux package-management)
This function is taken from thegnome-desktop service.
(define (extract-propagated-inputs package);; Drop input labels. Attempt to support outputs. (map (match-lambda ((_ (? package? pkg)) pkg) ((_ (? package? pkg) output) (list pkg output))) (package-propagated-inputs package)))
Now redefine some services. Enable nonguix substitutes and blueman for a bluetooth applet.
(define %my-services (modify-services %desktop-services (guix-service-type config => (guix-configuration (inherit config) (substitute-urls (append (list"https://substitutes.nonguix.org") %default-substitute-urls)) (authorized-keys (append (list (local-file"./signing-key.pub")) %default-authorized-guix-keys)))) (gdm-service-type config => (gdm-configuration (inherit config) (wayland?#f) (default-user"nil") (auto-login?#t))) (dbus-root-service-type config => (dbus-configuration (inherit config) (services (list libratbag blueman fwupd))))))
(definemy-username "nil")
Let’s handle basic OS defaults first of all.
(operating-system (locale"cs_CZ.utf8") (timezone"Europe/Prague") (keyboard-layout (keyboard-layout"cz")) (host-name"lainpad")
Now add the necessary firmware from nonguix.
(kernel linux)(initrd microcode-initrd)(firmware (list linux-firmware))User and group setup is next in line:
(groups (cons* (user-group (name"games"));; For libratbagd (user-group (name"realtime")) %base-groups))(users (cons* (user-account (name my-username) (comment"(lambda () nil)") (group"users") (home-directory"/home/nil") (supplementary-groups'("wheel""netdev""audio""video""lp""libvirt""kvm""games""docker""realtime")))%base-user-accounts))
Add system-level packages now; these areonly the most necessary of packages.
(packages (append (map specification->package (list"bluez""openssh""blueman""i915-firmware""vim""mesa""glu""xdg-desktop-portal-gtk""i3lock""libratbag""git")) %base-packages))
Enable resolving hostnames on local network.
(name-service-switch %mdns-host-lookup-nss)
Now for the second part of service configuration - appending my own services.
(services (append (list;; (service gnome-desktop-service-type;; (gnome-desktop-configuration;; (shell (extract-propagated-inputs gnome-meta-core-shell-patched))));; Enable triple buffering support;; I'm sure there's a more proper way to approach this, but this works for now (service xfce-desktop-service-type) (service openssh-service-type);; Not necessary for Gnome (service screen-locker-service-type (screen-locker-configuration (name"i3lock") (program (file-append i3lock"/bin/i3lock")))) (service tlp-service-type (tlp-configuration (tlp-enable?#t) (cpu-scaling-governor-on-ac (list"performance")) (cpu-scaling-governor-on-bat (list"powersave")) (energy-perf-policy-on-ac"") (cpu-boost-on-ac?#t) (cpu-boost-on-bat?#f) (start-charge-thresh-bat070) (stop-charge-thresh-bat075) (start-charge-thresh-bat170) (stop-charge-thresh-bat175))) (service bluetooth-service-type (bluetooth-configuration (auto-enable?#f)))
throttled is a small Python utility, used to fix throttling issues affecting a few certain laptop models with a particular Intel CPU.
(service throttled-service-type)(service kernel-module-loader-service-type'("msr"));; required for throttled
This one is a custom service, essentially distrobox needs the root (/) to be mounted as rshared and this is currently the way to achieve this.
mount-rshared-service
In order for rootlesspodman to work I also have to create the/etc/subuid and/etc/subgid files.
(extra-special-file"/etc/subuid" (plain-file"subuid" (string-append my-username":100000:65536")))(extra-special-file"/etc/subgid" (plain-file"subgid" (string-append my-username":100000:65536")))
Another custom service, this one creates the transientdefault libvirt network.
virsh-net-default-service
PAM limits for Wine Esync and realtime acces forjackd.
(service pam-limits-service-type (list (pam-limits-entry"*"'hard'nofile524288) (pam-limits-entry"@realtime"'both'rtprio99) (pam-limits-entry"@realtime"'both'memlock'unlimited)))
A simple firewall configuration. By default all incoming connections are blocked, except for those onssh port 22.
(service nftables-service-type)
This is a bit of a work-around… Basically we place the linker where regular binaries expect it to be. This allows us to execute standard binaries more easily.
(extra-special-file"/lib64/ld-linux-x86-64.so.2" (file-append glibc"/lib/ld-linux-x86-64.so.2"))
And the final few services.
(set-xorg-configuration (xorg-configuration (keyboard-layout keyboard-layout)))(service virtlog-service-type (virtlog-configuration (max-clients1000)))(service libvirt-service-type (libvirt-configuration (unix-sock-group"libvirt") (tls-port"16555")))(service docker-service-type)(service zram-device-service-type (zram-device-configuration (size"8172M") (compression-algorithm'zstd))))%my-services))
At last we can configure hardware-level stuff - bootloader and drives. Finally we can close theoperating-system section.
(bootloader (bootloader-configuration (bootloader grub-efi-bootloader) (targets (list"/boot/efi")) (keyboard-layout keyboard-layout)))(mapped-devices (list (mapped-device (source (uuid"52ef5bf4-a41c-4d4c-a67c-f4117ab21102")) (target"cryptroot") (type luks-device-mapping))))(file-systems (cons* (file-system(mount-point"/boot/efi")(device (uuid"C417-E6C5"'fat32))(type"vfat")) (file-system(mount-point"/")(device"/dev/mapper/cryptroot")(type"ext4")(dependencies mapped-devices)) %base-file-systems)))
This is the key for nonguix.
(public-key (ecc (curve Ed25519) (q #C1FD53E5D4CE971933EC50C9F307AE2171A2D3B52C804642A7A35F84F3A4EA98#) ) )
I use some of my own custom Guix modules. These provide anything from services to (mainly) packages.
Let’s start with the packages first.
(define-module (nil packages brogue) #:use-module (guix packages) #:use-module (guix download) #:use-module (guix build-system gnu) #:use-module (guix licenses) #:use-module (guix gexp) #:use-module (gnu packages sdl))(define-public brogue-ce (package (name"brogue-ce") (version"1.13") (source (origin (method url-fetch) (uri (string-append"https://github.com/tmewett/BrogueCE/archive/refs/tags/v" version".tar.gz")) (sha256 (base32"0v4hh5c6lgfrm5gmh2r0c3fnq854i4nqbhmkb9b5hbch74bfjqsc")))) (build-system gnu-build-system) (arguments (list #:make-flags #~(list"CC=gcc") #:tests?#f #:phases #~(modify-phases %standard-phases(delete'configure)(add-before'build'change-datadir-path (lambda _ (map (lambda (substitutes) (substitute*"config.mk" (((car substitutes)) (cdr substitutes)))) `(("^DATADIR := ." . ,(string-append"DATADIR :=" #$output"/share")) ("^RELEASE := NO" ."RELEASE := YES")))))(replace'install (lambda _ (mkdir-p (string-append #$output"/bin")) (copy-file"bin/brogue" (string-append #$output"/bin/.brogue_real")) (call-with-output-file (string-append #$output"/bin/brogue"); Wrap around executable and execute in ~/.local (lambda (file) (format file"~A" (string-append"mkdir -p\"$HOME/.local/share/brogue\" && cd\"$HOME/.local/share/brogue\" &&" #$output"/bin/.brogue_real")))) (invoke"chmod""+x" (string-append #$output"/bin/brogue")) (copy-recursively"bin/assets" (string-append #$output"/share/assets")) (make-desktop-entry-file (string-append #$output"/share/applications/brogue.desktop") #:name"Brogue" #:exec"brogue" #:categories'("RolePlaying""Game") #:keywords'("adventure""singleplayer") #:comment'((#f"Brave the Dungeons of Doom!")))))))) (inputs (list (sdl-union (list sdl2 sdl2-image)))) (synopsis"Brogue CE: A dungeon crawler roguelike") (description"Community fork of Brogue") (home-page"https://github.com/tmewett/BrogueCE") (license agpl3)))
Since podman isn’t currently fully functional on Guix we have to use docker instead. Unfortunately the Distrobox definition in Guix’s repos relies on podman, making it practically broken. To solve this issue I modify the package to use docker instead.
(define-module (nil packages distrobox-docker) #:use-module (guix gexp) #:use-module (gnu packages) #:use-module (guix packages) #:use-module (gnu packages docker) #:use-module (guix utils) #:use-module (guix build utils) #:use-module (gnu packages containers))(define-public distrobox-docker (package (inherit distrobox) (name"distrobox-docker") (inputs (modify-inputs (package-inputs distrobox) (append docker) (append docker-cli) (delete"podman"))) (arguments (substitute-keyword-arguments (package-arguments distrobox) ((#:phases phases) #~(modify-phases #$phases (replace'wrap-scripts (lambda _ (let ((path (search-path-as-list (list"bin") (list #$(this-package-input"docker") #$(this-package-input"wget"))))) (for-each (lambda (script) (wrap-script(string-append #$output"/bin/distrobox-" script)`("PATH"":" prefix ,path)))'("assemble""create""enter""ephemeral""generate-entry""list""rm""stop""upgrade")))))))))))
This is a custom retroarch definition, it adds the core downloader functionality which the Guix version doesn’t have for licensing reasons.
(define-module (nil packages emulators) #:use-module (ice-9 match) #:use-module (guix gexp) #:use-module (guix packages) #:use-module (guix utils) #:use-module (gnu packages) #:use-module (gnu packages gl) #:use-module (gnu packages linux) #:use-module (gnu packages emulators))(define-public retroarch-nonfree (package (inherit retroarch) (name"retroarch-nonfree") (arguments (substitute-keyword-arguments (package-arguments retroarch) ((#:phases phases) #~(modify-phases #$phases (replace'configure(lambda* (#:key inputs outputs #:allow-other-keys) (let* ((out (assoc-ref outputs"out")) (etc (string-append out"/etc")) (vulkan (assoc-ref inputs"vulkan-loader")) (wayland-protocols (assoc-ref inputs"wayland-protocols")));; Hard-code some store file names. (substitute*"gfx/common/vulkan_common.c" (("libvulkan.so") (string-append vulkan"/lib/libvulkan.so"))) (substitute*"gfx/common/wayland/generate_wayland_protos.sh" (("/usr/local/share/wayland-protocols") (string-append wayland-protocols"/share/wayland-protocols")));; Without HLSL, we can still enable GLSLANG and Vulkan support. (substitute*"qb/config.libs.sh" (("[$]HAVE_GLSLANG_HLSL")"notcare"));; The configure script does not yet accept the extra arguments;; (like ‘CONFIG_SHELL=’) passed by the default configure phase. (invoke"./configure" #$@(if (string-prefix?"armhf" (or (%current-target-system)(%current-system)))'("--enable-neon""--enable-floathard")'()) (string-append"--prefix=" out)))))))))))
Patches Mutter with the triple buffering patch for improved performance.
(define-module (nil packages gnome) #:use-module (guix packages) #:use-module (guix utils) #:use-module (guix build utils) #:use-module (gnu packages) #:use-module (guix git-download) #:use-module (gnu packages gnome) #:use-module (gnu packages pciutils) #:use-module (guix licenses) #:use-module (gnu packages base)) (define-public mutter-patched (let ((commit"28a6447ff060ae1fbac8f20a13908d6e230eddc2") (revision"1")) (package(inherit mutter)(name"mutter")(version (git-version"44" revision commit))(source (origin (method git-fetch) (uri (git-reference(url"https://gitlab.gnome.org/vanvugt/mutter.git")(commit commit))) (file-name (git-file-name name version)) (sha256 (base32"1ssmflwpmfkn28rmn5glyh96fd5ys9h1j4v70wm4ix2668wk4rr6"))))(arguments (substitute-keyword-arguments (package-arguments mutter) ((#:tests? tests?#f)#f))))));; recommend checking tests on version bumps (define-public gnome-meta-core-shell-patched (package (inherit gnome-meta-core-shell) (name"gnome-meta-core-shell") (propagated-inputs (modify-inputs (package-propagated-inputs gnome-meta-core-shell) (delete"mutter") (append mutter-patched)))))
(define-module (nil packages throttled) #:use-module (guix packages) #:use-module (guix utils) #:use-module (guix build utils) #:use-module (gnu packages) #:use-module (guix git-download) #:use-module (gnu packages commencement) #:use-module (gnu packages pkg-config) #:use-module (gnu packages pciutils) #:use-module (gnu packages gtk) #:use-module (gnu packages glib) #:use-module (gnu packages bash) #:use-module (gnu packages linux) #:use-module (gnu packages python-xyz) #:use-module (guix licenses) #:use-module (gnu packages base) #:use-module (guix build-system python) #:use-module (gnu packages python)) (define-public throttled (let ((commit"596ad496e8c5cff2aae6977de6a9e1546bf51cbf") (revision"1")) (package(name"throttled")(version (git-version"0.10.99" revision commit))(source (origin (method git-fetch) (uri (git-reference(url"https://github.com/erpalma/throttled/")(commit commit))) (file-name (git-file-name name version)) (sha256 (base32"10ki1hjisalsqjknmrj6m6zc7cq3mqkfzi7j5bl9ysh9yh1dlzaq"))))(build-system python-build-system)(inputs (list pciutils python-pygobject python-dbus-python python-configparser gcc-toolchain pkg-config cairo gobject-introspection dbus))(native-inputs (list python))(arguments `(#:phases (modify-phases %standard-phases (delete'build) (delete'check) (replace'install (lambda* (#:key inputs outputs #:allow-other-keys) (use-modules (guix build utils)) (let* ((sitedir (site-packages inputs outputs))(source (assoc-ref %build-inputs"source"))(coreutils (assoc-ref %build-inputs"coreutils"))(pciutils (assoc-ref %build-inputs"pciutils"))(python (assoc-ref %build-inputs"python"))(out (assoc-ref %outputs"out"))(python-sitedir (string-append out"/lib/python"(python-version python)"/site-packages"))) (substitute*"throttled.py" (("setpci") (string-append pciutils"/sbin/setpci"))) (mkdir-p python-sitedir) (mkdir-p (string-append out"/bin/")) (copy-file"throttled.py" (string-append out"/bin/throttled")) (copy-file"mmio.py" (string-append python-sitedir"/mmio.py")) (wrap-program (string-append out"/bin/throttled") `("GUIX_PYTHONPATH"":" suffix ,(list sitedir python-sitedir))) ))))))(synopsis"")(description"")(home-page"https://github.com/erpalma/throttled")(license expat))))
Moving on to services.
Tiny service used to remount the root as recursively shared on boot.
(define-module (nil services mount-rshared) #:use-module (guix gexp) #:use-module (gnu packages) #:use-module (guix packages) #:use-module (gnu packages linux) #:use-module (guix utils) #:use-module (gnu services) #:use-module (gnu services shepherd) #:use-module (guix build utils))(define-public mount-rshared-service (simple-service'mount-rshared shepherd-root-service-type (list (shepherd-service (provision'(mount-rshared)) (requirement'(user-processes)) (start #~(lambda () (invoke #$(file-append util-linux"/bin/mount")"--make-rshared""/"))) (respawn?#f)))))
There doesn’t seem to be a proper Guix way to configure libvirt networks so my lazy workaround is to make a service that creates the default transient network.
(define-module (nil services virsh) #:use-module (guix gexp) #:use-module (gnu packages) #:use-module (guix packages) #:use-module (gnu packages virtualization) #:use-module (guix utils) #:use-module (gnu services) #:use-module (gnu services shepherd) #:use-module (guix build utils)) (definedefault-config " <network> <name>default</name> <bridge name='virbr0'/> <forward/> <ip address='192.168.122.1' netmask='255.255.255.0'> <dhcp><range start='192.168.122.2' end='192.168.122.254'/> </dhcp> </ip> </network> ") (define-public virsh-net-default-service (let ((config (plain-file"default.xml" default-config))) (simple-service'virsh-net-default-service shepherd-root-service-type (list (shepherd-service (provision'(virsh-net-default)) (requirement'(libvirtd NetworkManager)) (start #~(lambda ()(invoke #$(file-append libvirt"/bin/virsh")"net-create" #$config))) (stop #~(lambda () (invoke#$(file-append libvirt"/bin/virsh")"net-destroy""default"))) (respawn?#f))))))
(define-module (nil services throttled) #:use-module (nil packages throttled) #:use-module (guix gexp) #:use-module (guix packages) #:use-module (guix records) #:use-module (guix modules) #:use-module (gnu services) #:use-module (gnu services base) #:use-module (gnu services configuration) #:use-module (gnu services dbus) #:use-module (gnu services shepherd) #:use-module (guix gexp) #:use-module (ice-9 match) #:export (throttled-configuration throttled-configuration? throttled-service-type)) (define (non-negative-integer? val) (and (exact-integer? val) (not (negative? val)))) (define (non-negative-float? val) (not (negative? val))) (define-maybe file-like) (define-maybe/no-serialization string) (define-configuration throttled-configuration (throttled (file-like throttled)"The THROTTLED package.") (throttled-enable? (boolean#t)"Enable or disable the script execution.") (sysfs-power-path (string"/sys/class/power_supply/AC*/online")"SYSFS path for checking if the system is running on AC power.") (bat-update-rate-s (non-negative-integer30)"Update the registers every this many seconds.") (bat-pl1-tdp-w (non-negative-integer29)"Max package power for time window #1.") (bat-pl1-duration-s (non-negative-integer28)"Time window #1 duration.") (bat-pl2-tdp-w (non-negative-integer44)"Max package power for time window #2.") (bat-pl2-duration-s (non-negative-float0.002)"Time window #2 duration.") (bat-trip-temp-c (non-negative-integer85)"Max allowed temperature before throttling.") (ac-update-rate-s (non-negative-integer5)"Update the registers every this many seconds.") (ac-pl1-tdp-w (non-negative-integer44)"Max package power for time window #1.") (ac-pl1-duration-s (non-negative-integer28)"Time window #1 duration.") (ac-pl2-tdp-w (non-negative-integer44)"Max package power for time window #2.") (ac-pl2-duration-s (non-negative-float0.002)"Time window #2 duration.") (ac-trip-temp-c (non-negative-integer90)"Max allowed temperature before throttling.") (no-serialization)) (define (generate-throttled-config config) (string-append"[GENERAL]\n""Enabled:" (if (throttled-configuration-throttled-enable? config)"True""False")"\n""Sysfs_Power_Path:" (throttled-configuration-sysfs-power-path config)"\n""Autoreload: True""\n\n""[BATTERY]\n""Update_Rate_s:" (number->string (throttled-configuration-bat-update-rate-s config))"\n""PL1_Tdp_W:" (number->string (throttled-configuration-bat-pl1-tdp-w config))"\n""PL1_Duration_s:" (number->string (throttled-configuration-bat-pl1-duration-s config))"\n""PL2_Tdp_W:" (number->string (throttled-configuration-ac-pl2-tdp-w config))"\n""PL2_Duration_S:" (number->string (throttled-configuration-bat-pl2-duration-s config))"\n""Trip_Temp_C:" (number->string (throttled-configuration-bat-trip-temp-c config))"\n""cTDP: 0\nDisable_BDPROCHOT: False\n\n""[AC]\n""Update_Rate_s:" (number->string (throttled-configuration-ac-update-rate-s config))"\n""PL1_Tdp_W:" (number->string (throttled-configuration-ac-pl1-tdp-w config))"\n""PL1_Duration_s:" (number->string (throttled-configuration-ac-pl1-duration-s config))"\n""PL2_Tdp_W:" (number->string (throttled-configuration-ac-pl2-tdp-w config))"\n""PL2_Duration_S:" (number->string (throttled-configuration-ac-pl2-duration-s config))"\n""Trip_Temp_C:" (number->string (throttled-configuration-ac-trip-temp-c config))"\n""cTDP: 0\nDisable_BDPROCHOT: False\n\n""[UNDERVOLT.BATTERY]\nCORE: 0\nGPU: 0\nCACHE: 0\nUNCORE: 0\nANALOGIO: 0\n\n""[UNDERVOLT.AC]\nCORE: 0\nGPU: 0\nCACHE: 0\nUNCORE: 0\nANALOGIO: 0\n\n")) (define (throttled-shepherd-service config) (definethrottled-command #~(list (string-append #$(throttled-configuration-throttled config)"/bin/throttled"))) (list (shepherd-service (documentation"Run THROTTLED script.") (provision'(throttled)) (requirement'(user-processes)) (start #~(make-forkexec-constructor #$throttled-command)) (stop #~(make-kill-destructor))))) (define (throttled-activation config) (let* ((config-str (generate-throttled-config config)) (config-file (plain-file"throttled" config-str))) (with-imported-modules'((guix build utils))#~(begin (use-modules (guix build utils)) (copy-file #$config-file"/etc/throttled.conf"))))) (definethrottled-service-type (service-type (name'throttled) (extensions (list (service-extension shepherd-root-service-type throttled-shepherd-service) (service-extension activation-service-type throttled-activation))) (default-value (throttled-configuration)) (description"Run THROTTLED, a power management tool.")))
This is the distribution-agnostic ZRAM implementation for use with systems other than GNU Guix. This module isn’t necessary (or desired) with Guix System.
- Dependent on
systemd. - It is necessary to manually disable regular swap in
/etc/fstab. - It might be necessary to copy the files over rather than symlink them.
First load the kernel module.
zramNow to configure the module a little bit.
options zram num_devices=1Set the ZRAM size - 16GB in this case.
KERNEL=="zram0", ATTR{disksize}="16384M",TAG+="systemd"Finally we need a systemd service to automatically enable zram on boot.
[Unit]Description=Swap with zramAfter=multi-user.target[Service]Type=oneshotRemainAfterExit=trueExecStartPre=/sbin/mkswap /dev/zram0ExecStart=/sbin/swapon /dev/zram0ExecStop=/sbin/swapoff /dev/zram0[Install]WantedBy=multi-user.targetFinally - this is my Emacs config, now fully literate!
It’s pretty full-featured, yet rather short and straightforward. I try to keep this config as readable and maintainable as possible
Let’s start with a quick listing of the relevant system-level dependencies, these are the names in Guix:
clangripgrepfont-fira-monosbclclangcmakemakeenchanthunspellhunspell-dict-en-uslibtoolgcc-toolchainguile
I usually install these usingGNU Guix, at least when running Emacs on Linux.
Set some basic information about the user.
(setq user-mail-address"shadenk30011@gmail.com"user-full-name"(λ () nil)")
EXWM can be toggled here. Its configuration is handled within a separate file.
(defvaruse-exwmnil)(when use-exwm (load"~/.exwm.el"))
Next we need to setup our package archives so that we can ensure all the necessary packages are installed. We’ll also grabel-patch as it needs to have priority over other packages.
(require'package)(add-to-list'package-archives '("melpa"."https://melpa.org/packages/")t);;(add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/") t)(package-initialize)(unless (package-installed-p'el-patch) (package-install'el-patch))(require'el-patch)
Here is the list of packages to be patched withel-patch.
(dolist (package '(elcord nyan-mode)) (unless (package-installed-ppackage) (package-installpackage)))
A small patch needs to be applied before we start setting up packages - theelcord package needs to be patched to work with flatpak Discord.
;; (el-patch-feature elcord);; (el-patch-defvar elcord--discord-ipc-pipe "app/com.discordapp.Discord/discord-ipc-0")
(el-patch-feature nyan-mode)(el-patch-defconst nyan-directory (file-name-directory"~/.emacs.d/nyan-custom/img"))
Finally it’s time to go over the package list and install everything. You’ll notice the use of a very primitive function - nostraight.el oruse-package here!
(dolist (package '(dumb-jump company ivy sudo-edit json-mode sly transmission vterm elfeed which-key magit guix systemd spacious-padding jinx toc-org tempel tempel-collection yaml-mode tree-sitter tree-sitter-langs emms lsp-pyright treemacs doom-themes treemacs-all-the-icons markdown-mode impatient-mode ement counsel lsp-mode rainbow-delimiters ligature flycheck leuven-theme geiser go-mode geiser-guile org-present visual-fill-column org-bullets web-mode)) (unless (package-installed-ppackage) (package-installpackage)) (requirepackage))
I usetempel for inserting snippets. This is the default path for adding new snippets.
;; ~/.config/emacs/templatesorg-mode(file"#+TITLE:" p n"#+AUTHOR:" p n"#+SETUPFILE: ~/.org-html.css.org" n"#+OPTIONS: toc:nil num:nil author:nil date:nil timestamp:nil html-postamble:nil")(blog"#+OPTIONS: toc:nil num:nil" n"#+BEGIN_EXPORT html" n"---" n"layout: post" n"title:" p n"permalink: /:title/" n"tags: [emacs]" n"---" n"#+END_EXPORT");; Local Variables:;; mode: lisp-data;; outline-regexp: "[a-z]";; End:
Now we set some custom variables related mostly to theming. This part here was automatically generated by Emacs.
(custom-set-variables '(column-number-modet) '(ement-save-sessionst) '(custom-enabled-themes '(doom-dracula)) '(custom-safe-themes '("0cf95236abcf59e05b1ea69b4edd53d293a5baec4fe4c3484543fee99bfd2204" "8c7e832be864674c220f9a9361c851917a93f921fedb7717b1b5ece47690c098" "944d52450c57b7cbba08f9b3d08095eb7a5541b0ecfb3a0a9ecd4a18f3c28948" "631c52620e2953e744f2b56d102eae503017047fb43d65ce028e88ef5846ea3b" "1a1ac598737d0fcdc4dfab3af3d6f46ab2d5048b8e72bc22f50271fd6d393a00" "7ea883b13485f175d3075c72fceab701b5bf76b2076f024da50dff4107d0db25" "37768a79b479684b0756dec7c0fc7652082910c37d8863c35b702db3f16000f8" "ae426fc51c58ade49774264c17e666ea7f681d8cae62570630539be3d06fd964" "fee7287586b17efbfda432f05539b58e86e059e78006ce9237b8732fde991b4c" "bfc0b9c3de0382e452a878a1fb4726e1302bf9da20e69d6ec1cd1d5d82f61e3d" "dde643b0efb339c0de5645a2bc2e8b4176976d5298065b8e6ca45bc4ddf188b7" "bffa9739ce0752a37d9b1eee78fc00ba159748f50dc328af4be661484848e476" "12ce0ae7f9f5ba28e7252d9464daea32aa0884646b6576b949edfb2ccf2bf9d4" "da75eceab6bea9298e04ce5b4b07349f8c02da305734f7c0c8c6af7b5eaa9738" "2dd4951e967990396142ec54d376cced3f135810b2b69920e77103e0bcedfba9" "7a424478cb77a96af2c0f50cfb4e2a88647b3ccca225f8c650ed45b7f50d9525" "6945dadc749ac5cbd47012cad836f92aea9ebec9f504d32fe89a956260773ca4" "aec7b55f2a13307a55517fdf08438863d694550565dee23181d2ebd973ebd6b8" default)) '(delete-selection-modet) '(org-safe-remote-resources '("\\`https://fniessen\\.github\\.io/org-html-themes/org/theme-readtheorg\\.setup\\'")) '(package-selected-packages '(leuven-theme org-present org-roam web-mode doom-themes vterm sly sudo-edit pacmacs rainbow-mode eglot nord-theme nyan-mode rainbow-delimiters tree-sitter-langs tree-sitter magit treemacs-icons-dired spacemacs-theme treemacs-all-the-icons treemacs guix emms elfeed impatient-mode company-plisp sly-quicklisp ligature markdown-mode ivy flycheck company dumb-jump)) '(safe-local-variable-values '((eval modify-syntax-entry43"'") (eval modify-syntax-entry36"'") (eval modify-syntax-entry126"'"))) '(warning-suppress-types '((comp) (comp) (emacs))))(custom-set-faces '(ement-room-message-text ((t (:inherit (variable-pitchdefault))))) '(ement-room-name ((t (:inherit (variable-pitchfont-lock-function-name-face))))) '(erc-default-face ((t (:inherit (variable-pitchdefault))))) '(erc-nick-default-face ((t (:inheritvariable-pitch:weightbold)))))
The Fira Code font has ligature support, so we implement it here.
(dolist (char/ligature-re `((?- ,(rx (or (or"-->""-<<""->>""-|""-~""-<""->") (+"-")))) (?/ ,(rx (or (or"/==""/=""/>""/**""/*") (+"/")))) (?* ,(rx (or (or"*>""*/") (+"*")))) (?< ,(rx (or (or"<<=""<<-""<|||""<==>""<!--""<=>""<||""<|>""<-<""<==""<=<""<-|""<~>""<=|""<~~""<$>""<+>""</>""<*>""<->""<=""<|""<:""<>""<$""<-""<~""<+""</""<*") (+"<")))) (?: ,(rx (or (or":?>""::="":>"":<"":?"":=") (+":")))) (?= ,(rx (or (or"=>>""==>""=/=""=!=""=>""=:=") (+"=")))) (?! ,(rx (or (or"!==""!=") (+"!")))) (?> ,(rx (or (or">>-"">>="">=>"">]"">:"">-"">=") (+">")))) (?& ,(rx (+"&"))) (?| ,(rx (or (or"|->""|||>""||>""|=>""||-""||=""|-""|>""|]""|}""|=") (+"|")))) (?. ,(rx (or (or".?"".="".-""..<") (+".")))) (?+ ,(rx (or"+>" (+"+")))) (?\[ ,(rx (or"[<""[|"))) (?\{ ,(rx"{|")) (?\? ,(rx (or (or"?.""?=""?:") (+"?")))) (?# ,(rx (or (or"#_(""#[""#{""#=""#!""#:""#_""#?""#(") (+"#")))) (?\; ,(rx (+";"))) (?_ ,(rx (or"_|_""__"))) (?~ ,(rx (or"~~>""~~""~>""~-""~@"))) (?$ ,(rx"$>")) (?^ ,(rx"^=")) (?\] ,(rx"]#")))) (apply (lambda (charligature-re) (set-char-table-range composition-function-table char `([,ligature-re0 font-shape-gstring]))) char/ligature-re))
It’s finally time to set up some basic variables.
(setq ring-bell-function'ignore)(setq vc-follow-symlinkst)(setq all-the-icons-scale-factor1.2)(setq confirm-kill-processesnil)(setq kill-buffer-query-functionsnil)(treemacs-load-theme"all-the-icons")(menu-bar-mode-1)(tool-bar-mode-1)(scroll-bar-mode-1)(counsel-modet)(setcdr (assoc'counsel-M-x ivy-initial-inputs-alist)"");; Search by substring;;(global-tab-line-mode t)(setq inferior-lisp-program"sbcl")(setf nyan-animate-nyancatt)(setf nyan-animation-frame-interval0.05)(setf nyan-wavy-trailt)(setq-default cursor-type'bar)(setq geiser-active-implementations '(guile))(setq geiser-default-implementation'guile)(defvarmy-org-html-export-theme'leuven)
Background transparency, finally supported since Emacs 29.
(set-frame-parameternil'alpha-background96)(add-to-list'default-frame-alist '(alpha-background.96))
Setup up spacious padding.
(setf spacious-padding-widths '(:internal-border-width8:right-divider-width16:scroll-bar-width0))(define-globalized-minor-mode my-global-spacious-padding-mode spacious-padding-mode (lambda () (spacious-padding-mode1)))(my-global-spacious-padding-mode1)
Emacsclient requires fonts to be loaded on frame creation instead of daemon start.
(defunload-my-fonts (frame) (select-frame frame) (set-face-attribute'defaultnil:font"Fira Code":weight'medium:height120) (set-face-attribute'fixed-pitchnil:font"Fira Code":weight'medium:height120) (set-face-attribute'variable-pitchnil:font"Source Sans Pro":weight'medium:height1.1) (set-face-attribute'company-tooltipnil:font"Fira Code":weight'medium:height120);; Make the document title a bit bigger (set-face-attribute'org-document-titlenil:font"Source Sans Pro":weight'bold:height1.3);; Make sure certain org faces use the fixed-pitch face when variable-pitch-mode is on (set-face-attribute'org-blocknil:foregroundnil:inherit'fixed-pitch) (set-face-attribute'org-tablenil:inherit'fixed-pitch) (set-face-attribute'org-formulanil:inherit'fixed-pitch) (set-face-attribute'org-codenil:inherit '(shadowfixed-pitch)) (set-face-attribute'org-verbatimnil:inherit '(shadowfixed-pitch)) (set-face-attribute'org-special-keywordnil:inherit '(font-lock-comment-facefixed-pitch)) (set-face-attribute'org-meta-linenil:inherit '(font-lock-comment-facefixed-pitch)) (set-face-attribute'org-checkboxnil:inherit'fixed-pitch))(if (daemonp) (add-hook'after-make-frame-functions#'load-my-fonts) (load-my-fonts (selected-frame)))
Some window related stuff; supress opening the welcome screen for buffers opening files.
(when (> (length command-line-args)1) (setq inhibit-splash-screent))(add-to-list'default-frame-alist '(height.44))(add-to-list'default-frame-alist '(width.140))(setq frame-resize-pixelwiset)
Windows has some rather strange encoding issues when UTF-8 isn’t explicitly set as the default.
(prefer-coding-system'utf-8)(set-default-coding-systems'utf-8)(set-language-environment'utf-8)(set-selection-coding-system'utf-8)
Company is used for code completion. Here is a basic setup.
(defuncompany-complete-common-or-cycle ()"Company settings." (interactive) (when (company-manual-begin) (if (eq last-command'company-complete-common-or-cycle) (let ((company-selection-wrap-aroundt)) (call-interactively'company-select-next))(call-interactively'company-complete-common)))) (define-key company-active-map [tab]'company-complete-common-or-cycle) (define-key company-active-map (kbd"TAB")'company-complete-common-or-cycle) (setq company-insertion-on-trigger'company-explicit-action-p) (add-hook'xref-backend-functions#'dumb-jump-xref-activate)
And here we arrive at the global modes.
(global-display-line-numbers-mode-1)(global-prettify-symbols-modet)(global-company-modet)(global-flycheck-modet)(global-jinx-modet)(which-key-modet)(ivy-modet)(global-tree-sitter-mode1)(electric-pair-modet)(global-hl-line-mode1)(desktop-save-mode-1);;(elcord-mode);(nyan-mode t)(add-hook'tree-sitter-after-on-hook#'tree-sitter-hl-mode)(add-hook'scheme-mode-hook'geiser-mode)(add-hook'prog-mode-hook#'(lambda () (display-line-numbers-modet)))
Set up LSP for supported modes.
C and C++ are really straightforward.
(add-hook'c-mode-hook#'lsp)(add-hook'c++-mode-hook#'lsp)
Here I set Python to use a customvenv.
(add-hook'python-mode-hook (lambda ()(require'lsp-pyright)(lsp-register-custom-settings `(("python.pythonPath""/home/nil/.py/bin/python")))(lsp)))
Go requires a bit of simple configuration.
(defunlsp-go-install-save-hooks () (add-hook'before-save-hook#'lsp-format-buffertt) (add-hook'before-save-hook#'lsp-organize-importstt))(add-hook'go-mode-hook (lambda () (lsp-deferred) (lsp-go-install-save-hooks) (setq tab-width4)))
Impatient mode for Markdown, more about this later.
(add-hook'markdown-mode-hook#'(lambda () (markdown-impatient-start))); Impatient mode live preview
Rainbow delimiters are a life-saver for Lisps!
(add-hook'prog-mode-hook#'rainbow-delimiters-mode)
I like to have the selected line clearly highlighted.
(set-face-attribute'hl-linenil:inheritnil:background"gray14"); use lavender for leuven theme instead
Fix unicode invterm.
(add-hook'vterm-mode-hook (lambda () (set-buffer-process-coding-system'utf-8-unix'utf-8-unix)))
Add the Guix development environment for Geiser along with thetempel completions.
;; (with-eval-after-load 'geiser-guile;; (add-to-list 'geiser-guile-load-path "~/guix");; (add-to-list 'geiser-guile-load-path "~/nonguix");; (add-to-list 'geiser-guile-load-path "/etc/guix-modules"));; (with-eval-after-load 'tempel;; ;; Ensure tempel-path is a list -- it may also be a string.;; (unless (listp 'tempel-path);; (setq tempel-path (list tempel-path)));; (add-to-list 'tempel-path "~/src/guix/etc/snippets/tempel/*"))
Enable languages for the Jinx spell checker.
(setq jinx-languages"en_US cs_CZ")
Set up EMMS playback with MPV.
(require'emms-setup)(emms-all)(setq emms-player-list '(emms-player-mpv))
Disable annoying keybind suggestions.
(setq suggest-key-bindingsnil)
Here is a very minimal setup for org-agenda.
(require'org)(setq org-log-donet)(setq calendar-week-start-day1)(add-to-list'org-agenda-files"~/org/agenda.org")
Automatically tangle config on save, currently disabled.
(defunorg-babel-tangle-config () (when (string-equal (buffer-file-name)(expand-file-name"~/git/emacs-stuff/README.org")) (let ((org-config-babel-evaluatenil))(org-babel-tangle))));; (add-hook 'org-mode-hook;; (lambda ();; (add-hook 'after-save-hook #'org-babel-tangle-config)))
Org-mode specific keybindings.
(define-key global-map"\C-ck"'org-store-link)(define-key global-map"\C-ca"'org-agenda)
Ensureorg-html-export-html switches to the correct theme before exporting - this solves code legibility issues. It’s a bit of a flashbang for a couple of seconds though…
(defunmy-with-theme (orig-fun&restargs) (load-theme my-org-html-export-theme) (unwind-protect(apply orig-fun args) (disable-theme my-org-html-export-theme))) (with-eval-after-load"ox-html" (advice-add'org-html-export-to-html:around'my-with-theme))
Jekyll export setup, I use this for my blog.
(require'ox-publish) (with-eval-after-load"ox-publish" (advice-add'org-publish-current-project:around'my-with-theme)) (setq org-publish-project-alist'(("lambdanil.github.io";; Path to org files.:base-directory"~/git/lambdanil.github.io/org":base-extension"org";; Path to Jekyll Posts:publishing-directory"~/git/lambdanil.github.io/_posts/":recursivet:publishing-function org-html-publish-to-html:headline-levels4:html-extension"html":body-onlyt)))
Makeorg-mode a little bit prettier to look at.
(add-hook'org-mode-hook (lambda () (company-mode-1) (toc-org-mode1) (org-bullets-mode1) (setq-local face-remapping-alist '((defaultvariable-pitchdefault))) (set-face-attribute'company-tooltipnil:font"Fira Code":weight'medium:height120) (dolist (face '((org-level-1.1.3) (org-level-2.1.2) (org-level-3.1.1) (org-level-4.1.05) (org-level-5.1.2) (org-level-6.1.2) (org-level-7.1.2) (org-level-8.1.2)))(set-face-attribute (car face)nil:font"Fira Code":weight'medium:height (cdr face)))))(require'org-faces);; Hide emphasis markers on formatted text(setq org-hide-emphasis-markerst)
Now for presentations in Emacs usingorg-present.
(defunmy/org-present-start () (setq visual-fill-column-width160; Set the width visual-fill-column-center-textt) (visual-fill-column-mode1) (org-display-inline-images) (visual-line-mode1); Center text (setq-local face-remapping-alist '((default (:height1.6)variable-pitch); Set font sizes (header-line (:height4.0)variable-pitch) (org-document-title (:height1.75)org-document-title) (org-code (:height1.2)org-code) (org-verbatim (:height1.2)org-verbatim) (org-block (:height1.2)org-block) (org-block-begin-line (:height0.7)org-block))))(defunmy/org-present-end () (visual-fill-column-mode0) (org-remove-inline-images) (visual-line-mode0) (setq-local face-remapping-alist '((defaultvariable-pitchdefault))) (set-face-attribute'company-tooltipnil:font"Fira Code":weight'medium:height120))(add-hook'org-present-mode-hook'my/org-present-start)(add-hook'org-present-mode-quit-hook'my/org-present-end)
This is for keybindings that should never be overwritten by minor modes.
(defvarmy/keys-keymap (make-keymap)"Keymap for my/keys-mode.")(define-minor-mode my/keys-mode"Minor mode for my personal keybindings.":init-valuet:globalt:keymap my/keys-keymap);; The keymaps in`emulation-mode-map-alists' take precedence over;;`minor-mode-map-alist'(add-to-list'emulation-mode-map-alists `((my/keys-mode.,my/keys-keymap)))
Some straightforward functions here, used to make my Emacs life a little easier.
(defunmy-kill-buffer-and-window ()"Kill the current buffer and delete the selected window." (interactive) (let ((window-to-delete (selected-window)) (buffer-to-kill (current-buffer)) (delete-window-hook (lambda () (ignore-errors (delete-window))))) (unwind-protect (progn (add-hook'kill-buffer-hook delete-window-hooktt) (if (kill-buffer (current-buffer));; If`delete-window' failed before, we rerun it to regenerate;; the error so it can be seen in the echo area.(when (eq (selected-window) window-to-delete) (delete-window)))))))(defunnew-vterm () (interactive) (let ((current-prefix-arg '(4)));; emulate C-u (call-interactively'vterm)))(defundired-open-file ()"In Dired, open the file named on this line." (interactive) (let* ((file (dired-get-filenamenilt))) (call-process"xdg-open"nil0nil file)))(defuninsert-above-and-jump ()"Insert line above current line." (interactive) (beginning-of-line) (newline-and-indent) (forward-line-1) (indent-according-to-mode))(defuninsert-line-below-and-jump ()"Insert line below current line." (interactive) (end-of-line) (newline-and-indent))(defunmy-enlarge-window-horizontally ()"Enlarge window horizontally." (interactive) (enlarge-window-horizontally4))(defunmy-shrink-window-horizontally ()"Shrink window horizontally." (interactive) (shrink-window-horizontally4))(defunmy-erc-channel-search () (interactive) (insert"/msg alis LIST **"))
I usetempel for inserting snippets. This is copied right from their docs!
(defuntempel-setup-capf () (setq-local completion-at-point-functions (cons#'tempel-expand completion-at-point-functions)))
Sometimes I like to take quick notes in Markdown with impatient mode used for a preview feature.
(defunmarkdown-html (buffer)"Markdown HTML filter, supply BUFFER." (princ (with-current-buffer buffer (format"<!DOCTYPE html><html><title>Impatient Markdown</title><xmp theme=\"united\" style=\"display:none;\">%s </xmp><script src=\"http://ndossougbe.github.io/strapdown/dist/strapdown.js\"></script></html>" (buffer-substring-no-properties (point-min) (point-max)))) (current-buffer)))(defunmarkdown-impatient-start ()"Start impatient mode with filter." (interactive) (impatient-mode) (httpd-start) (imp-set-user-filter'markdown-html))
Finally we get to my extensive custom keybindings. I try to keep these to as few as possible, to avoid forgetting them over time.
(define-key my/keys-keymap (kbd"C-d")'kill-line)(global-set-key (kbd"C-l")'forward-char)(define-key my/keys-keymap (kbd"C-h")'backward-char)(define-key my/keys-keymap (kbd"C-k")'previous-line)(define-key my/keys-keymap (kbd"C-j")'next-line)(define-key my/keys-keymap (kbd"M-l")'forward-word)(define-key my/keys-keymap (kbd"M-h")'backward-word)(define-key my/keys-keymap (kbd"M-j")'forward-paragraph)(define-key my/keys-keymap (kbd"M-k")'backward-paragraph)(define-key my/keys-keymap (kbd"M-c")'compile)(define-key my/keys-keymap (kbd"C-b")'ivy-switch-buffer)(define-key my/keys-keymap (kbd"C-p")'other-window)(define-key my/keys-keymap (kbd"C-o")'insert-line-above-and-jump)(define-key my/keys-keymap (kbd"C-v")'set-mark-command)(define-key my/keys-keymap (kbd"<backtab>")'indent-rigidly)(define-key my/keys-keymap (kbd"C-=")'indent-region)(define-key my/keys-keymap (kbd"C-§")'scroll-down-line)(define-key my/keys-keymap (kbd"C-ů")'scroll-up-line)(define-key my/keys-keymap (kbd"M-ů")#'(lambda () (interactive) (scroll-up4)))(define-key my/keys-keymap (kbd"M-§")#'(lambda () (interactive) (scroll-down4)))(define-key my/keys-keymap (kbd"C-x c")#'(lambda () (interactive) (load-file"~/.emacs")))(define-key my/keys-keymap (kbd"C-c r")'replace-regexp)(define-key my/keys-keymap (kbd"M-/")'undo-redo)(define-key my/keys-keymap (kbd"M-/")'undo-redo)(define-key my/keys-keymap (kbd"C-c n")'elfeed)(define-key my/keys-keymap (kbd"C-c y")'yank-from-kill-ring)(define-key my/keys-keymap (kbd"C-c p")'treemacs)(define-key my/keys-keymap (kbd"C-c l")'treemacs-select-directory)(define-key my/keys-keymap (kbd"C-c t")'new-vterm)(define-key my/keys-keymap (kbd"M-w")'copy-region-as-kill)(define-key my/keys-keymap (kbd"C-c h")'help)(define-key my/keys-keymap (kbd"C-M-h")'previous-buffer)(define-key my/keys-keymap (kbd"C-M-l")'next-buffer)(define-key my/keys-keymap (kbd"C-c i")#'(lambda () (interactive) (erc-tls)))(define-key my/keys-keymap (kbd"C-c v")#'(lambda () (interactive) (split-window-horizontally) (run-with-idle-timer0.05nil'windmove-right)))(define-key my/keys-keymap (kbd"C-c c")#'(lambda () (interactive) (split-window-vertically) (run-with-idle-timer0.05nil'windmove-down)))(define-key my/keys-keymap (kbd"C-c q")'delete-window)(define-key my/keys-keymap (kbd"C-c C-q")'my-kill-buffer-and-window)(define-key my/keys-keymap (kbd"C-c d")#'(lambda (command) (interactive (list (read-shell-command"$"))) (start-process-shell-command commandnil command)))(global-set-key (kbd"C-c w")'eww-switch-to-buffer)(global-set-key (kbd"C-c C-w")#'(lambda () (interactive) (let ((current-prefix-arg '(4)));; emulate C-u(eww (read-string"Enter URL:""https://google.com")))))(define-key my/keys-keymap (kbd"M-+")'tempel-complete)
These bindings are defined separately in the EXWM config, so they aren’t necessary when using EXWM.
(when (not use-exwm) (define-key my/keys-keymap (kbd"C-<return>")'new-vterm) (define-key my/keys-keymap (kbd"C-)")#'(lambda () (interactive) (enlarge-window-horizontally2))) (define-key my/keys-keymap (kbd"C-ú")#'(lambda () (interactive) (shrink-window-horizontally2))) (define-key my/keys-keymap (kbd"M-ú")#'(lambda () (interactive) (shrink-window2))) (define-key my/keys-keymap (kbd"M-)")#'(lambda () (interactive) (enlarge-window2))))
Some mode-specific bindings are added here.
(add-hook'common-lisp-mode-hook#'(lambda () (local-set-key (kbd"C-c d")'sly-eval-defun) (local-set-key (kbd"C-c r")'sly-eval-region) (local-set-key (kbd"C-c b")'sly-eval-buffer)))(define-key dired-mode-map (kbd"C-c o")'dired-open-file)(define-key dired-mode-map [mouse-2]'dired-mouse-find-file)(add-hook'emacs-lisp-mode-hook#'(lambda () (local-set-key (kbd"C-c e")'eval-region)))(add-hook'erc-mode-hook#'(lambda () (local-set-key (kbd"C-c s")'my-erc-channel-search)))(add-hook'prog-mode-hook'tempel-setup-capf)(add-hook'text-mode-hook'tempel-setup-capf)
Setup elfeed for news.
(setq elfeed-feeds'(("https://www.root.cz/rss/clanky" root.cz) ("https://www.root.cz/rss/zpravicky" root.cz) ("https://forum.root.cz/index.php?action=.xml;type=rss2;limit=30;sa=news" root.cz forum) ("https://protesilaos.com/master.xml" protesilaos.com))) (add-hook'elfeed-show-mode-hook (lambda () (buffer-face-set'variable-pitch)))
And we have reached the end of the main Emacs file!
(provide '.emacs)Let’s move onto EXWM configuration next.
The EXWM configuration is stored in a separate file from the Emacs config, this helps keep it modular and easy to switch on or off.
Eensure theexwm package is installed first.
(unless (package-installed-p'exwm) (package-install'exwm))
Now werequire all the necessary packages and set a few defaults.
(require'exwm)(require'exwm-config)(exwm-config-default)(require'exwm-systemtray)(exwm-systemtray-enable)(setq exwm-systemtray-height16)(setq exwm-layout-show-all-bufferst)(setq exwm-workspace-show-all-bufferst)(setq exwm-workspace-number4)(display-time-mode1)(setq display-time-24hr-formatt)(call-process"/usr/bin/bash""~/.loginctl")
Define some custom functions.
(defunnew-vterm-exwm () (interactive) (let ((current-prefix-arg '(4)));; emulate C-u (call-interactively'vterm)))(defunexwm-move-window-to-workspace (workspace-number) (interactive) (let ((frame (exwm-workspace--workspace-from-frame-or-index workspace-number)) (id (exwm--buffer->id (window-buffer)))) (exwm-workspace-move-window frame id)))(defmacroexwm-set-key-progn (key&restbody) `(exwm-input-set-key (kbd,key) (lambda(),@body)))(defmacroexwm-key-to-workspace (keyworkspace) `(exwm-set-key-progn,key (interactive) (exwm-workspace-switch,workspace)))(defmacroexwm-key-send-to-workspace (keyworkspace) `(exwm-set-key-progn,key (interactive) (exwm-move-window-to-workspace,workspace)))(defmacroexwm-key-to-command (keycommand) `(exwm-set-key-progn,key (interactive) (start-process-shell-command,commandnil,command)))(defunmy-kill-buffer-and-window ()"Kill the current buffer and delete the selected window." (interactive) (let ((window-to-delete (selected-window)) (buffer-to-kill (current-buffer)) (delete-window-hook (lambda () (ignore-errors (delete-window))))) (unwind-protect (progn (add-hook'kill-buffer-hook delete-window-hooktt) (if (kill-buffer (current-buffer));; If`delete-window' failed before, we rerun it to regenerate;; the error so it can be seen in the echo area.(when (eq (selected-window) window-to-delete) (delete-window)))))))
Time to configure the keybindings.
(exwm-key-to-workspace"s-é"0)(exwm-key-to-workspace"s-+"1)(exwm-key-to-workspace"s-ě"2)(exwm-key-to-workspace"s-š"3)(exwm-key-to-workspace"s-č"4)(exwm-key-to-workspace"s-ř"5)(exwm-key-send-to-workspace"s-0"0)(exwm-key-send-to-workspace"s-1"1)(exwm-key-send-to-workspace"s-2"2)(exwm-key-send-to-workspace"s-3"3)(exwm-key-send-to-workspace"s-4"4)(exwm-key-send-to-workspace"s-5"5)(exwm-input-set-key (kbd"s-Q")#'my-kill-buffer-and-window)(exwm-input-set-key (kbd"s-R")#'exwm-reset)(exwm-input-set-key (kbd"s-x")#'exwm-input-toggle-keyboard)(exwm-input-set-key (kbd"s-h")#'windmove-left)(exwm-input-set-key (kbd"s-j")#'windmove-down)(exwm-input-set-key (kbd"s-k")#'windmove-up)(exwm-input-set-key (kbd"s-l")#'windmove-right)(exwm-input-set-key (kbd"s-q")#'delete-window)(exwm-input-set-key (kbd"C-c q")#'kill-current-buffer)(exwm-input-set-key (kbd"s-,")#'exwm-workspace-switch-to-buffer)(exwm-input-set-key (kbd"s-f")#'exwm-layout-toggle-fullscreen)(exwm-input-set-key (kbd"s-<return>")#'new-vterm-exwm)(exwm-input-set-key (kbd"s-c")#'vterm)(exwm-input-set-key (kbd"s-d") (lambda (command) (interactive (list (read-shell-command"$"))) (start-process-shell-command commandnil command)))(exwm-input-set-key (kbd"s-b") (lambda () (interactive) (split-window-vertically) (run-with-idle-timer0.05nil (lambda() (windmove-down)))))(exwm-input-set-key (kbd"s-v") (lambda () (interactive) (split-window-horizontally) (run-with-idle-timer0.05nil (lambda() (windmove-right)))))(exwm-input-set-key (kbd"s-L")#'(lambda () (enlarge-window-horizontally2)))(exwm-input-set-key (kbd"s-H")#'(lambda () (shrink-window-horizontally2)))(exwm-input-set-key (kbd"s-J")#'(lambda () (shrink-window2)))(exwm-input-set-key (kbd"s-K")#'(lambda () (enlarge-window2)))(exwm-input-set-key (kbd"s-F")#'exwm-floating-toggle-floating)(exwm-input-set-key (kbd"s-<tab>")#'exwm-workspace-add)(exwm-input-set-key (kbd"s-<iso-lefttab>")#'exwm-workspace-delete)(exwm-key-to-command"s-M""pavucontrol")(exwm-key-to-command"<XF86AudioRaiseVolume>""pamixer -i 5")(exwm-key-to-command"<XF86AudioLowerVolume>""pamixer -d 5")(exwm-key-to-command"<XF86AudioMute>""pamixer -t")(exwm-key-to-command"s-<f6>""pamixer --default-source -t")
Finally push the super key and escape asexwm-input-prefix-keys.
(push?\s- exwm-input-prefix-keys)(push (kbd"<escape>") exwm-input-prefix-keys)
Some additional services may need to be enabled on non-guix distributions.
sudo systemctl enable zramsudo systemctl enable bluetooth
And that’s all for the configs!
About
my GNU Guix/Emacs/exwm configs and stuff
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Contributors2
Uh oh!
There was an error while loading.Please reload this page.


