About
My neurosys encompasses configuration for:
- NixOS (config)
- XMonad (config)
- Doom Emacs (config)
- Includes a neurosys module with utilities (e.g. remote deployment)
These define a my computing environment, which strives to be
- Effective: interactions with code/data should occur at the speed of thought.
- Extensible: otherwise, it’s not really mine.
- Long-lived: resist entropy/bitrot/bankruptcy until the end of the keyboard + screen era.
- Minimal: less is more for the base configuration - Nix enables project-level dependency management.
- Reproducible: enabled by the content-based hashes of Nix + Git. No fear of hitting un-recoverable states.
This literate org file, along with another for Doom, are the only two source files. The rest are generated via tangling.
If you are viewing this in a browser, consider opening the original org file in Emacs for the full experience. The HTML version is an imperfect rendering.
The name neurosys is used half-jokingly.
Inspirations
In no particular order, I found the following configurations especially helpful:
- jwiegley/nix-config: My local Nix configuration
- IvanMalison/dotfiles: Configuration files for XMonad, Emacs, NixOS, …
- willbush/system: System configuration
- malloc47/config: personal config files, cannibalized from a number o…
- hlissner/dotfiles: NixOS dotfiles for veteran bike shedders
- bauer: an Emacs+Nix IDE
Helpful Resources
- Nicolas Mattia – Nix: A Reproducible Setup for Linux and macOS
- malloc47/Migrating to NixOS
- My NixOS Desktop Flow - Christine Dodrill
- Choosing Software · Gwern.net
Table of Contents QUOTE TOC_3
Host setup
Install NixOS
Followed: https://www.linode.com/docs/tools-reference/custom-kernels-distros/install-nixos-on-linode/
Add channels
Stable
NIXOS_VERSION=20.03
nix-channel --add "https://nixos.org/channels/nixos-${NIXOS_VERSION}" nixos
nix-channel --add "https://github.com/rycee/home-manager/archive/release-${NIXOS_VERSION}.tar.gz" home-manager
nix-channel --add "https://nixos.org/channels/nixpkgs-${NIXOS_VERSION}" nixpkgs
nix-channel update
Unstable
nix-channel --add https://github.com/rycee/home-manager/archive/master.tar.gz home-manager
nix-channel --add https://nixos.org/channels/nixos-unstable nixos
nix-channel --add "https://nixos.org/channels/nixpkgs-unstable" nixpkgs-unstable
nix-channel update
Deployment
Get the neurosys source
Don’t forget to clone recursively, because of the submodules:
Sync to host
Run neurosys/deploy-to-host, which will tangle this file and rsync
the results to a specified host.
Rsync Script
HOST=$1
HOST_HOME=$2
rsync -Pav --rsync-path="sudo rsync" nixos/ $HOST:/etc/nixos/
rsync -Pav home/ $HOST:$HOST_HOME
Doom Emacs
My Doom Emacs Configuration is tracked in the home folder as a git submodule.
Until projects like nix-doom-emacs are stable, I’m not yet tracking my Emacs packages / config in Nix. For now, I track known-good commits via submodules / straight.el, and tie them to external dependencies (all managed by Nix) in this repo. If you know a better way to do this, please let me know.
Emacs itself is tracked via the emacs-overlay, which is version pinned via niv in sources.json. See the nix delaration.
XMonad
I use XMonad as a window manager and minimal desktop environment. I don’t run any additional desktop environment (e.g. XFCE). Instead, I have the interface to the few things I need configured here in Haskell, or elsewhere (Emacs).
I’ve don’t use any system trays / status bars / panels, since the Emacs modeline is enough for me. This choice reduces the complexity of the XMonad configuration, and avoids depending on xmobar/polybar.
Haskell Configuration
Imports
import XMonad
import XMonad.Hooks.SetWMName
import XMonad.Hooks.DynamicProperty (dynamicTitle)
import XMonad.Hooks.EwmhDesktops
import XMonad.Hooks.ManageHelpers
import XMonad.Hooks.UrgencyHook
import XMonad.Layout.Grid
import XMonad.Layout.Fullscreen
import XMonad.Layout.Minimize
import XMonad.Actions.Minimize
import XMonad.Layout.NoBorders
import XMonad.Layout.NoFrillsDecoration (noFrillsDeco, shrinkText,
inactiveBorderColor, inactiveColor, inactiveTextColor, activeBorderColor,
activeColor, activeTextColor, urgentBorderColor, urgentTextColor, decoHeight)
import XMonad.Layout.Tabbed (simpleTabbed)
import XMonad.Layout.ResizableTile
import XMonad.Layout.MultiColumns
import XMonad.Actions.CycleWS (toggleWS)
import XMonad.Actions.CycleRecentWS (cycleRecentWS)
import qualified XMonad.StackSet as W
import XMonad.Prompt
import XMonad.Prompt.AppLauncher as AL
import XMonad.Util.Run
import Data.Monoid
import Data.Default (def)
import Data.Map as M (fromList,union, Map())
import Data.List (isPrefixOf)
Main
main :: IO ()
main = xmonad $
withUrgencyHook NoUrgencyHook $
ewmh $
fullscreenSupport def {
borderWidth = 1
, focusedBorderColor = blue
, terminal = "alacritty"
, layoutHook = smartBorders $ -- no borders for sole windows
noFrillsDeco shrinkText topBarTheme $ -- visually mark the focused window with a top bar
minimize
(ResizableTall 1 (3/100) (1/2) []
||| Mirror (ResizableTall 1 (3/100) (1/2) [])
||| multiCol [1] 1 0.01 (-0.5)
||| noBorders Full
||| simpleTabbed
||| Grid)
, workspaces = map show $ [1..9] ++ [0 :: Int]
, modMask = mod4Mask -- super key as modifier
, keys = \c -> myKeys c `M.union` keys def c
, manageHook = myManageHook <+> manageZoomHook
, handleEventHook = ewmhDesktopsEventHook <+> myHandleEventHook
, startupHook = do
-- http://hackage.haskell.org/package/xmonad-contrib-0.16/docs/XMonad-Hooks-SetWMName.html
setWMName "LG3D"
windows $ W.greedyView "1"
}
Keybindings
Default
From https://xmonad.org/manpage.html#default-keyboard-bindings
binding command mod-shift-return Launch terminal mod-p Launch dmenurofimod-shift-pLaunch gmrunmod-shift-c Close the focused window mod-space Rotate through the available layout algorithms mod-shift-space Reset the layouts on the current workspace to default mod-nResize viewed windows to the correct sizemod-tab Move focus to the next window mod-shift-tab Move focus to the previous window mod-j Move focus to the next window mod-k Move focus to the previous window mod-mMove focus to the master windowmod-return Swap the focused window and the master window mod-shift-j Swap the focused window with the next window mod-shift-k Swap the focused window with the previous window mod-h Shrink the master area mod-l Expand the master area mod-t Push window back into tiling mod-commaIncrement the number of windows in the master areamod-periodDeincrement the number of windows in the master areamod-shift-q Quit xmonad mod-qRestart xmonadmod-shift-slash Run xmessage with a summary of the default keybindings (useful for beginners) mod-[1..9] Switch to workspace N mod-shift-[1..9] Move client to workspace N mod-{w,e,r} Switch to physical/Xinerama screens 1, 2, or 3 mod-shift-{w,e,r} Move client to screen 1, 2, or 3 mod-button1 Set the window to floating mode and move by dragging mod-button2 Raise the window to the top of the stack mod-button3 Set the window to floating mode and resize by dragging Custom
myKeys :: XConfig t -> M.Map (KeyMask, KeySym) (X ()) myKeys XConfig {modMask = m, terminal = term} = M.fromList $ [
Rebooting / Restarting
Add Workspace 0
Launcher / Window Switcher
I currently use rofi to run programs or switch between open windows. It’s simple, fast, and supports fuzzy search.
, ((m, xK_p), spawn "rofi -show drun -modi drun -show-icons -matching fuzzy -sort") , ((m .|. shiftMask, xK_p), spawn "GDK_SCALE=2 rofi -show drun -modi drun -show-icons -matching fuzzy -sort") , ((m, xK_b), spawn "rofi -show window -show-icons -matching fuzzy -sort") -- Like M-y for helm-show-kill-ring in Emacs , ((m, xK_y), spawn "rofi -modi \"clipboard:greenclip print\" -show clipboard -run-command '{cmd}'") -- Text espander , ((m .|. shiftMask .|. mod1Mask, xK_j), spawn "texpander.sh")
Running Emacs
Lock Screen
Horizontal Resizing
An obvious missing default…
Window Minimization / Restoration
Fullscreen
Workspace Swapping
Using
mod+comma
quickly swap between workspaces is very handy.Changing number of master windows
Some layouts, like
ResizableTall
, have a “master” area, with 1 window initially assigned there. These commands enable incrementing or decrementing that number.They are bound by default to
mod+
, andmod+.
, butmod+,
is much more useful for Workspace Swapping. Here I addShift
to the defaults.Easier Kill Binding
- I find the default
mod+shift+c
binding to be clumbsy for killing windows. mod+q
is easier / more natural.- The default
mod+q
for killing XMonad is something I’ve never needed.
- I find the default
Volume Control
I don’t run a desktop environment, so the volume buttons on my keyboard don’t do anything by default.
Screenshots
Keyboard
Arandr
I have
main.sh
andlaptop.sh
symlinked to whatever the current xrandr scripts are for my desk / laptop.Cycle Recent Workspace
Multiple Monitors
The functionality here is a primary reason for choosing XMonad: a natural, keyboard-driven way for coordinating workspaces across multiple monitors. I’m genuinely curious to know if others have found something on-par/better elsewhere. Please contact me if you do.
- Bind
mod-{w, e, r}
to switch focus between monitors. - Bind
mod-shift-{w, e, r}
to move workspaces between monitors.
] ++ [((m .|. nilOrShift, key), screenWorkspace sc >>= flip whenJust (windows . f)) | (key, sc) <- zip [xK_w, xK_e, xK_r] [0..] , (f, nilOrShift) <- [(W.view, 0), (W.shift, shiftMask)]]
TODO Try XMonad.Actions.PhysicalScreens to order
mod-{w, e, r}
based on physical screen layout
- Bind
Asthetics
red = "#dc322f"
blue = "#268bd2"
yellow = "#b58900"
inactive = "#002b36"
active = blue
topBarTheme = def
{ inactiveBorderColor = inactive
, inactiveColor = inactive
, inactiveTextColor = inactive
, activeBorderColor = active
, activeColor = active
, activeTextColor = active
, urgentBorderColor = red
, urgentTextColor = yellow
, decoHeight = 10
}
myShellPrompt = def
{ font = "xft:Hack:pixelsize=30"
, promptBorderWidth = 1
, position = Top
, height = 42
, defaultText = []
}
Float certain apps
manageZoomHook =
composeAll $
[ (className =? zoomClassName) <&&> shouldFloat <$> title --> doFloat,
(className =? zoomClassName) <&&> shouldSink <$> title --> doSink
]
where
zoomClassName = "zoom"
tileTitles =
[ "Zoom - Free Account", -- main window
"Zoom - Licensed Account", -- main window
"Zoom", -- meeting window on creation
"Zoom Meeting" -- meeting window shortly after creation
]
shouldFloat title = title `notElem` tileTitles
shouldSink title = title `elem` tileTitles
doSink = (ask >>= doF . W.sink) <+> doF W.swapDown
myHandleEventHook =
mconcat
[ dynamicTitle manageZoomHook,
handleEventHook defaultConfig
]
myManageHook = composeAll [ appName =? "Open Files" --> doFloat,
className =? "Zenity" --> doFloat]
Screenshot
myScreenshot = do
-- init takes care of the trailing newline character returned by date
date <- init <$> runProcessWithInput "date" ["+%Y-%m-%d-%H:%M:%S"] []
AL.launchApp myShellPrompt { defaultText = "~/screenshots/" ++ date ++ ".png"} "maim -s"
myScreenshotClipboard :: X ()
myScreenshotClipboard = spawn "maim -s | xclip -selection clipboard -t image/png"
Start Script
This is currently only used by when on non-NixOS systems (e.g. Ubuntu).
#!/bin/bash
# Identify the home of our gtkrc file, important for setting styles of
# gtk-based applications
export GTK2_RC_FILES="$HOME/.gtkrc-2.0"
# enable for hidpi displays
export GDK_SCALE=1
# sudo timedatectl set-timezone Europe/Berlin
#sudo timedatectl set-timezone America/New_York
sudo timedatectl set-timezone America/Los_Angeles
# Ensure Zoom output volume stays constant
/home/dan/scripts/fuck_zoom.sh 100 &
$HOME/scripts/configure_laptop_keeboard.sh
# Clipboard manager (used with rofi)
greenclip daemon &
# set trackball rate
xinput --set-prop "Primax Kensington Eagle Trackball" "libinput Accel Speed" 1 || true
# Wallpaper
feh --bg-fill --no-xinerama --randomize ~/Media/images/wallpaper/*
# xsetroot -solid black
# Config in ~/.config/redshift/redshift.conf
redshift &
picom -b
eval $(ssh-agent)
syncthing serve &
# Now, finally, start xmonad
exec xmonad
Desktop Entry Pointing to Start Script
file:/usr/share/xsessions/xmonad.desktop
[Desktop Entry]
Name=XMonad
Comment=Lightweight tiling window manager
Exec=/home/dan/.xmonad/start-xmonad
Icon=xmonad.png
Type=XSession
Nixos Config
System-level Config
{ config, pkgs, ... }:
let
sources = import ./nix/sources.nix;
# ghcide-nix = import sources."ghcide-nix" { };
in {
imports =
[ ./hardware-configuration.nix
./settings.nix
"${builtins.fetchTarball https://github.com/rycee/home-manager/archive/release-20.03.tar.gz}/nixos"
];
system.stateVersion = "20.03";
nixpkgs.config = {
# Allow unfree, which is required for some drivers.
allowUnfree = true;
};
Nix
nix = {
useSandbox = true;
autoOptimiseStore = true;
maxJobs = 3; # should be 1 per CPU logical core
binaryCaches = [
"https://cache.nixos.org/"
"https://ghcide-nix.cachix.org"
"https://hercules-ci.cachix.org"
"https://iohk.cachix.org"
"https://nix-tools.cachix.org"
];
binaryCachePublicKeys = [
"ghcide-nix.cachix.org-1:ibAY5FD+XWLzbLr8fxK6n8fL9zZe7jS+gYeyxyWYK5c="
"hercules-ci.cachix.org-1:ZZeDl9Va+xe9j+KqdzoBZMFJHVQ42Uu/c/1/KMC5Lw0="
"iohk.cachix.org-1:DpRUyj7h7V830dp/i6Nti+NEO2/nhblbov/8MW7Rqoo="
"nix-tools.cachix.org-1:ebBEBZLogLxcCvipq2MTvuHlP7ZRdkazFSQsbs0Px1A="
];
gc = {
automatic = true;
dates = "23:00";
options = "--delete-older-than 30d";
};
};
Timezone
Boot
boot = {
cleanTmpDir = true;
loader = {
timeout = 1; # Timeout (in seconds) until loader boots the default menu item.
grub = {
enable = true;
version = 2;
device = "nodev";
copyKernels = true;
fsIdentifier = "provided";
extraConfig = "serial; terminal_input serial; terminal_output serial";
};
systemd-boot.enable = false;
efi.canTouchEfiVariables = false;
};
};
Networking
networking.useDHCP = false;
networking.usePredictableInterfaceNames = false;
networking.interfaces.eth0.useDHCP = true;
networking.firewall.enable = false;
networking.firewall.allowPing = true;
# networking.networkmanager.enable = true;
networking.hostName = "nixos-dev";
networking.interfaces.eth0.tempAddress = "disabled";
Services
services = {
xserver = {
enable = true;
layout = "us";
windowManager.xmonad = {
enable = true;
enableContribAndExtras = true;
extraPackages = haskellPackges: [
haskellPackges.xmonad-contrib
haskellPackges.xmonad-extras
haskellPackges.xmonad
];
};
displayManager = {
defaultSession = "none+xmonad";
lightdm.enable = true;
};
desktopManager.xterm.enable = false;
};
Syncthing
# https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/syncthing.nix syncthing = { enable = true; openDefaultPorts = true; user = "${config.settings.username}"; configDir = "/home/${config.settings.username}/.config/syncthing"; dataDir = "/home/${config.settings.username}/.local/share/syncthing"; declarative = { devices = { nixos-dev.id = "EEMRJQE-TBONTUL-UBGJ6FT-AAUS25K-COP3VHE-WERN7IN-PTNZ63Z-GZZX2AY"; x1carbon9.id = "EIZV5LR-F3JILKF-7MD5UMZ-KYRW37L-RVJ2WI4-7LQD7VC-U5BSEBD-YMGQPQ3"; pixel6-pro.id = "DISECZT-3ILXZ2S-B7BTYBI-R6KKLH2-YYIDZGD-4LP2OEF-NAURN57-ZPR6XAD"; }; folders = { sync = rec { id = "at23u-zmxto"; devices = [ "nixos-dev" "x1carbon9" "pixel6-pro"]; path = "/bkp/Sync"; watch = false; rescanInterval = 3600 * 1; type = "receiveonly"; # sendreceive enable = true; versioning.type = "simple"; versioning.params.keep = "5"; }; media = rec { id = "media"; devices = [ "nixos-dev" "x1carbon9" "pixel6-pro"]; path = "/bkp/Media"; watch = false; rescanInterval = 3600 * 6; type = "receiveonly"; # sendreceive enable = true; versioning.type = "simple"; versioning.params.keep = "5"; }; work = rec { id = "d7svv-zjsz2"; devices = [ "nixos-dev" "x1carbon9" "pixel6-pro"]; path = "/bkp/Work"; watch = false; rescanInterval = 3600 * 6; type = "receiveonly"; # sendreceive enable = true; versioning.type = "simple"; versioning.params.keep = "5"; }; }; }; };
Tarsnap
End
Packages
environment.systemPackages = with pkgs; [
coreutils binutils
curl wget
zip unzip
git
killall
syncthing-cli
sshfs
mtr # traceroute
sysstat
htop
];
Fonts
fonts = {
enableFontDir = true;
enableGhostscriptFonts = true;
fonts = with pkgs; [
corefonts
hack-font
];
};
User Definition
security.sudo.wheelNeedsPassword = false;
users.mutableUsers = false;
users.extraUsers.${config.settings.username} = {
isNormalUser = true;
uid = 1000;
createHome = true;
home = "/home/${config.settings.username}";
description = "${config.settings.name}";
extraGroups = [
"audio"
"networkmanager"
"systemd-journal"
"vboxusers"
"video"
"wheel"
];
};
home-manager.users.dan = import ./home.nix ;
SSH
services.openssh = {
enable = true;
forwardX11 = true;
permitRootLogin = "without-password";
passwordAuthentication = false;
};
users.users.${config.settings.username}.openssh.authorizedKeys.keys = [
"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+yJ5sv7iO9PBuozfmitR0JJfqDsJ7w+rlryq5CwdatO3tkRdR5dMYdFTFCeHbmeakPTC/uys08fziEUXh3DL206jDKQEMBoMGXNowZHyYzr25nIogHbveqeNTgP8jsTw5uBaJu8LFzHHey4Sw9WlRrvIqguUT5jB3omZh8yDWcxTrTJlTsN2TM3HILvirfVwBkD2uNTDdd5LplbZhx6x87VCs6ZNYhBjJ4CPcO4zTQuEdyyxUHEgtMkYgrS4Jb/Kl6Tleftlh55E74SZ3XXnw3lWdH9ra8ewH265iqNr/RwysagnalslBZDLl8yJcrMsCVi4tPrZZc4vaeCsIWK4X dan@x1carbon"
];
programs.ssh.startAgent = true;
X2Go Client
End
User-level Config
{ config, pkgs, ... }:
let
homeDir = builtins.getEnv "HOME";
syncDir = builtins.toPath("${homeDir}/Sync");
sources = import ./nix/sources.nix;
nixos20_03 = import sources."nixpkgs-20.03" { };
emacs-overlay = import (import ./nix/sources.nix)."emacs-overlay";
in {
imports = [
./settings.nix
];
home.stateVersion = "20.03";
nixpkgs.config = {
allowUnfree = true;
packageOverrides = pkgs: { stable = nixos20_03; };
};
nixpkgs.overlays = [ emacs-overlay ];
Environment Variables
home.sessionVariables = {
EDITOR = "emacsclient --create-frame --alternate-editor emacs";
PASSWORD_STORE_DIR = "${syncDir}/.password-store";
GNUPGHOME = "${syncDir}/.gnupg/";
# GTK2_RC_FILES="${homeDir}/.gtkrc-2.0";
# https://github.com/xmonad/xmonad/issues/126
_JAVA_AWT_WM_NONREPARENTING = "1";
};
# gtk = {
# enable = true;
# iconTheme = {
# name = "Adwaita";
# package = pkgs.gnome3.adwaita-icon-theme;
# };
# theme = {
# name = "Adwaita-dark";
# package = pkgs.gnome3.gnome_themes_standard;
# };
# };
xdg.enable = true;
Packages
home.packages = with pkgs; [
rofi
gnupg
(pass.withExtensions (exts: [
exts.pass-otp
exts.pass-genphrase
]))
xtrlock-pam # screen locking
maim # screenshots
rofi-pass # interface to password manager
xclip # programmatic access to clipbaord
arandr # gui for xrandr (monitor layout)
# direnv
# Upstream failing :(
# julia_13
## Doom dependencies
(ripgrep.override {withPCRE2 = true;})
gnutls # for TLS connectivity
## Optional dependencies
fd # faster projectile indexing
imagemagick # for image-dired
pinentry_emacs
## Module dependencies
# :tools lookup & :lang org +roam
sqlite
# :lang latex & :lang org (latex previews)
texlive.combined.scheme-tetex
firefox-beta-bin
];
Programs
Home Manager
Emacs
Bash
bash = { enable = true; historyFile = "${syncDir}/.config/bash/.bash_history"; # FIXME: Document and reduce these shellOptions = [ "autocd" "cdspell" "dirspell" "globstar" # bash >= 4 "cmdhist" "nocaseglob" "histappend" "extglob"]; # TODO: Test this # https://github.com/akermu/emacs-libvterm#directory-tracking-and-prompt-tracking initExtra = [ '' vterm_prompt_end(){ vterm_printf \"51;A$(whoami)@$(hostname):$(pwd)\" } PS1=$PS1'\\[$(vterm_prompt_end)\\]' '' ] };
Git
Direnv
SSH
ssh = { enable = true; controlMaster = "auto"; controlPath = "/tmp/ssh-%u-%r@%h:%p"; controlPersist = "1800"; forwardAgent = true; serverAliveInterval = 60; hashKnownHosts = true; userKnownHostsFile = "${homeDir}/.ssh/known_hosts"; matchBlocks = { droplet = { hostname = "45.55.5.197"; identityFile = "${homeDir}/.ssh/id_rsa"; user = "dgirsh"; }; dangirsh = { host = "dangirsh.org"; hostname = "ssh.phx.nearlyfreespeech.net"; identityFile = "${homeDir}/.ssh/id_rsa"; user = "dangirsh_dangirsh"; }; nixos-dev = { hostname = "45.79.58.229"; identityFile = "${homeDir}/.ssh/id_rsa"; user = "dan"; }; }; };
End
Services
services = {
emacs.enable = true;
# redshift = {
# enable = true;
# latitude = "33";
# longitude = "-97";
# temperature.day = 6500;
# temperature.night = 3000;
# };
# https://www.reddit.com/r/emacsporn/comments/euf7m8/doomoutrunelectric_theme_xmonad_nixos/
# https://github.com/willbush/system/blob/371cfa9933f24bca585a3c6c952c41c864d97aa0/nixos/home.nix#L178
# compton = {
# enable = true;
# fade = true;
# backend = "xrender";
# fadeDelta = 1;
# # I only want transparency for a couple of applications.
# opacityRule = [
# "90:class_g ?= 'emacs' && focused"
# "75:class_g ?= 'emacs' && !focused"
# "90:class_g ?= 'alacritty' && focused"
# "75:class_g ?= 'alacritty' && !focused"
# ];
# };
# lorri.enable = true;
};
End
TODO To test [0/5]
- [ ] xtrlock-pam
- [ ] maim
- [ ] rofi-pass (see Floating Emacs popup for quick commands)
- [ ] xclip
- [ ] arandr
TODO To add [0/1]
- keyboard config: currently requires xset & setxbmap
- [ ] Messenging
- unified messenger (if it exists) for dealing with the madness of:
- sms
- signal
- fb
keybase- slack
- irc
- riot
- To try
- [X] pidgin - just bad. no hidpi, things like signal don’t sync with mobile state, clunky ui.
- Otherwise:
- Browser tabs for everything sans signal
- Signal-destop
- maybe slack desktop
- caprine for fb is nice on ubuntu
- unified messenger (if it exists) for dealing with the madness of:
Global Constants
{config, pkgs, lib, ...}:
with lib;
{
options = {
settings = {
name = mkOption {
default = "Dan Girshovich";
type = with types; uniq str;
};
username = mkOption {
default = "dan";
type = with types; uniq str;
};
email = mkOption {
default = "dan.girsh@gmail.com";
type = with types; uniq str;
};
};
};
}
Hardware-specific Config
# Do not modify this file! It was generated by ‘nixos-generate-config’
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, lib, pkgs, ... }:
{
imports =
[ <nixpkgs/nixos/modules/profiles/qemu-guest.nix>
];
boot.initrd.availableKernelModules = [ "virtio_pci" "ahci" "sd_mod" ];
boot.initrd.kernelModules = [ ];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
nix.maxJobs = lib.mkDefault 1;
Disk Mounts
From Chapter 8. File Systems: “Mount points are created automatically if they don’t already exist.”
fileSystems."/" =
{ device = "/dev/disk/by-uuid/bf38bdde-34dd-4d57-9bfe-07de465f0f29";
fsType = "ext4";
};
# Linode Volume "bkp". Targetted by syncthing.
fileSystems."/bkp" =
{ device = "/dev/disk/by-id/scsi-0Linode_Volume_bkp";
fsType = "ext4";
};
swapDevices =
[ { device = "/dev/disk/by-uuid/7596d600-d2c6-4d77-b138-7f595283af00"; }
];
}
Version Pinning
These are generated via niv.
{
"emacs-overlay": {
"branch": "master",
"description": "Bleeding edge emacs overlay [maintainer=@adisbladis] ",
"homepage": "",
"owner": "nix-community",
"repo": "emacs-overlay",
"rev": "0feda8b31b52f3ea008555dfe79dba3989d3e585",
"sha256": "1ijr9pl0czzbgj35vj8kq4xvcana6w24ljcmzriz7cyxln4pgvln",
"type": "tarball",
"url": "https://github.com/nix-community/emacs-overlay/archive/0feda8b31b52f3ea008555dfe79dba3989d3e585.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
},
"ghcide-nix": {
"branch": "master",
"description": "Nix installation for ghcide",
"homepage": "https://github.com/digital-asset/ghcide",
"owner": "cachix",
"repo": "ghcide-nix",
"rev": "f940ec611cc6914693874ee5e024eba921cab19e",
"sha256": "0vri0rivdzjvxrh6lzlwwkh8kzxsn82jp1c2w5rqzhp87y6g2k8z",
"type": "tarball",
"url": "https://github.com/cachix/ghcide-nix/archive/f940ec611cc6914693874ee5e024eba921cab19e.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
},
"nixpkgs-20.03": {
"branch": "release-20.03",
"description": "A read-only mirror of NixOS/nixpkgs tracking the released channels. Send issues and PRs to",
"homepage": "https://github.com/NixOS/nixpkgs",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "7829e5791ba1f6e6dbddbb9b43dda72024dd2bd1",
"sha256": "0hs9swpz0kibjc8l3nx4m10kig1fcjiyy35qy2zgzm0a33pj114w",
"type": "tarball",
"url": "https://github.com/NixOS/nixpkgs/archive/7829e5791ba1f6e6dbddbb9b43dda72024dd2bd1.tar.gz",
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
}
}
# This file has been generated by Niv.
# A record, from name to path, of the third-party packages
with rec
{
pkgs =
if hasNixpkgsPath
then
if hasThisAsNixpkgsPath
then import (builtins_fetchTarball { inherit (sources_nixpkgs) url sha256; }) {}
else import <nixpkgs> {}
else
import (builtins_fetchTarball { inherit (sources_nixpkgs) url sha256; }) {};
sources_nixpkgs =
if builtins.hasAttr "nixpkgs" sources
then sources.nixpkgs
else abort
''
Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
add a package called "nixpkgs" to your sources.json.
'';
# fetchTarball version that is compatible between all the versions of Nix
builtins_fetchTarball =
{ url, sha256 }@attrs:
let
inherit (builtins) lessThan nixVersion fetchTarball;
in
if lessThan nixVersion "1.12" then
fetchTarball { inherit url; }
else
fetchTarball attrs;
# fetchurl version that is compatible between all the versions of Nix
builtins_fetchurl =
{ url, sha256 }@attrs:
let
inherit (builtins) lessThan nixVersion fetchurl;
in
if lessThan nixVersion "1.12" then
fetchurl { inherit url; }
else
fetchurl attrs;
# A wrapper around pkgs.fetchzip that has inspectable arguments,
# annoyingly this means we have to specify them
fetchzip = { url, sha256 }@attrs: pkgs.fetchzip attrs;
# A wrapper around pkgs.fetchurl that has inspectable arguments,
# annoyingly this means we have to specify them
fetchurl = { url, sha256 }@attrs: pkgs.fetchurl attrs;
hasNixpkgsPath = (builtins.tryEval <nixpkgs>).success;
hasThisAsNixpkgsPath =
(builtins.tryEval <nixpkgs>).success && <nixpkgs> == ./.;
sources = builtins.fromJSON (builtins.readFile ./sources.json);
mapAttrs = builtins.mapAttrs or
(f: set: with builtins;
listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)));
# borrowed from nixpkgs
functionArgs = f: f.__functionArgs or (builtins.functionArgs f);
callFunctionWith = autoArgs: f: args:
let auto = builtins.intersectAttrs (functionArgs f) autoArgs;
in f (auto // args);
getFetcher = spec:
let fetcherName =
if builtins.hasAttr "type" spec
then builtins.getAttr "type" spec
else "builtin-tarball";
in builtins.getAttr fetcherName {
"tarball" = fetchzip;
"builtin-tarball" = builtins_fetchTarball;
"file" = fetchurl;
"builtin-url" = builtins_fetchurl;
};
};
# NOTE: spec must _not_ have an "outPath" attribute
mapAttrs (_: spec:
if builtins.hasAttr "outPath" spec
then abort
"The values in sources.json should not have an 'outPath' attribute"
else
if builtins.hasAttr "url" spec && builtins.hasAttr "sha256" spec
then
spec //
{ outPath = callFunctionWith spec (getFetcher spec) { }; }
else spec
) sources
Future Work
TODO [#A] Floating Emacs popup for quick commands
It’s usually most convenient to use/implement functions in Emacs than other tools. This was a huge draw towards EXWM, and I got used to that workflow.
For example, generating / getting passwords stored in pass
has a nice Emacs interface. Currently, this often means switching from Firefox to Emacs to run a command, then back to Firefox. In this situation, a common solution seems to be eschewing Emacs for things like rofi
(e.g. rofi-pass
).
However, if I could popup a fast, floating Emacs window (similar to Rofi), I’d be able to leverage Elisp for most tasks like this. This might fully obviate Rofi
and the XMonad prompt.
The floating window would default to a counsel-M-x
fuzzy search over all incremental commands, and be bound to s-x
in XMonad.
Examples:
- screenshotting
- pass interface
- redshift / brightness controls
- (maybe) finding windows / launching programs
- clipboard management (helm kill ring)
As a small added benefit, I’d have the full Emacs bindings available in the input area.
TODO [#B] HiDPI autotoggle
I’m occasionally lucky enough to plug into a 4k screen, which is not yet a seamless experience in linux.
For now I manually do the following on HiDPI screens:
and change layout.css.devPixelsPerPx
in Firefox about:config from -1 to 2.
tweak font size in Firefox and Emacs.
Keybase Tweak
Per keybase/client#5797 increase font size in client, edit the ExecStart line to have --force-device-scale-factor=2
. Then, reload with:
TODO [#B] Consistent copy/paste bindings
In EXWM, I used the simulation keys to have consistent Emacs copy/paste bindings everywhere. Need a solution for XMonad. Setting the GTK bindings to “emacs” didn’t seem to affect for Firefox, which is (by far) my primary non-Emacs window.
TODO [#B] Consolidate XMonad and Emacs window management
Ideally, XMonad could manage all windows, including Emacs windows.
Possible solutions:
- frames-only-mode
- frame-mode
These seemed to cause more problems than they fixed for me.
TODO [#C] Fix indentation in tangled files
Even with org-src-preserve-indentation
set to t
, files like home.nix aren’t properly indented. This is because they are split across several org blocks, and in each on I use the nix-mode
auto-indent to fixup the indentation within the block. This removes the leading whitespace, so each block is tangled without leading indentation. Thankfully, Nix doesn’t care, so this is purely for the aesthetics of the generated files.
Quickfix is to auto-indent the tangled files, then detangle.
Using noweb references seems to be the best option: https://orgmode.org/manual/Noweb-Reference-Syntax.html. See “This feature can also be used for management of indentation in exported code snippets”.