--- a/bin/bootstrap.sh Mon Mar 18 12:49:56 2024 -0400
+++ b/bin/bootstrap.sh Tue Mar 19 13:55:42 2024 -0400
@@ -56,7 +56,7 @@
ensure_link "src/dotfiles/sbclrc" ".sbclrc"
ensure_link "src/dotfiles/shellcheckrc" ".shellcheckrc"
ensure_link "src/dotfiles/sqliterc" ".sqliterc"
-ensure_link "src/dotfiles/stumpwmrc" ".stumpwmrc"
+ensure_link "src/dotfiles/stumpwm/stumpwmrc" ".stumpwmrc"
ensure_link "src/dotfiles/stumpwm/local-share-stumpwm" ".local/share/stumpwm"
ensure_link "src/dotfiles/vim" ".vim"
ensure_link "src/dotfiles/vim/vimrc" ".vimrc"
--- a/fish/config.fish Mon Mar 18 12:49:56 2024 -0400
+++ b/fish/config.fish Tue Mar 19 13:55:42 2024 -0400
@@ -8,7 +8,7 @@
function ei; hg -R ~/src/inventory/ pull -u; and nvim ~/src/inventory/inventory.markdown; and hg -R ~/src/inventory/ ci -m 'Update inventory'; and hg -R ~/src/inventory/ push; end
function el; cd ~/Dropbox/life; nvim .; end
function em; nvim ~/.mutt/muttrc; end
-function es; pushd ~/src/stumpwm; e ~/.stumpwmrc; popd; end
+function es; pushd ~/src/dotfiles/stumpwm; e stumpwmrc; popd; end
function ev; nvim ~/.vimrc; end
function evm; nvim ~/.vimrc-minimal; end
function eff; nvim ~/.config/fish/functions; end
--- a/fish/functions/mutt.fish Mon Mar 18 12:49:56 2024 -0400
+++ b/fish/functions/mutt.fish Tue Mar 19 13:55:42 2024 -0400
@@ -1,5 +1,5 @@
set -g -x MUTT_BIN (which neomutt)
function mutt
- bash --login -c "cd ~/Downloads; $MUTT_BIN \$@" custom_mutt $argv
+ bash --login -c "cd ~/downloads; $MUTT_BIN \$@" custom_mutt $argv
end
--- a/gitignore Mon Mar 18 12:49:56 2024 -0400
+++ b/gitignore Tue Mar 19 13:55:42 2024 -0400
@@ -18,5 +18,7 @@
.sjl-rsync-exclude
sjl-jupyter
sjl-sync-*.sh
+sjl-*-push.sh
+sjl-*-pull.sh
*.waiting
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/applications.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,30 @@
+(in-package :stumpwm-user)
+
+(defcommand spotify () ()
+ (run-or-raise "spotify" '(:class "Spotify")))
+
+(defcommand files () ()
+ (run-shell-command "open $HOME"))
+
+(defcommand browser () ()
+ (run-or-raise "firefox" '(:class "firefox")))
+
+(defcommand vlc () ()
+ (run-or-raise "vlc" '(:class "vlc")))
+
+(defcommand terminal () ()
+ (run-shell-command (format nil "st -f 'Ubuntu Mono:size=~D'" *terminal-font-size*)))
+
+(defcommand terminal-apl () ()
+ (run-shell-command "st -f 'BQN386 Unicode:style=Regular:size=12'"))
+
+(defcommand gcontrol () ()
+ (run-or-raise "gcontrol" '(:class "Gnome-control-center")))
+
+(defcommand zoom () ()
+ (when-let-window (w "^Zoom Meeting.*")
+ (focus-window w t)))
+
+(defcommand papers () ()
+ (run-or-raise "jabref" '(:class "org.jabref.gui.MainApplication")))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/brightness.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,28 @@
+(in-package :stumpwm-user)
+
+(defparameter *brightness-values* #(0 1 5 10 20 30 40 55 70 85 100))
+(defvar *brightness-index* 5)
+
+(defun brightness ()
+ (aref *brightness-values* *brightness-index*))
+
+(defun set-brightness (value)
+ (run-and-echo-shell-command
+ (hostcase
+ ((:gro :juss) (format nil "xrandr --output ~A --brightness ~D"
+ (hostcase ((:gro :juss) "eDP"))
+ (/ value 100.0)))
+ (t (message "Not sure how to set brightness on this machine.")))))
+
+(defun rotate-brightness (delta)
+ (setf *brightness-index*
+ (mod+ *brightness-index* delta (length *brightness-values*)))
+ (set-brightness (brightness)))
+
+
+(defcommand rotate-brightness-up () ()
+ (rotate-brightness 1))
+
+(defcommand rotate-brightness-down () ()
+ (rotate-brightness -1))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/budget.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,51 @@
+(in-package :stumpwm-user)
+
+(defparameter *tz/eastern*
+ (local-time:find-timezone-by-location-name "US/Eastern"))
+
+(defparameter *budget/start*
+ (local-time:encode-timestamp 0 0 0 0 29 8 2023 :timezone *tz/eastern*))
+
+(defun budget/per-day ()
+ (first (losh:read-all-from-file "/home/sjl/Sync/budget/per-day")))
+
+(defun budget/elapsed ()
+ (local-time:timestamp-difference (local-time:now) *budget/start*))
+
+(defun budget/days-elapsed ()
+ (floor (/ (budget/elapsed) (* 60 60 24))))
+
+(defun budget/in ()
+ (* (budget/days-elapsed) (budget/per-day)))
+
+(defun budget/out ()
+ (loop :for path :in (directory "/home/sjl/Sync/budget/hosts/*/total")
+ :summing (print (first (read-all-from-file (print path))))))
+
+(defun budget/current ()
+ (- (budget/in) (budget/out)))
+
+(defcommand budget-dump () ()
+ (message
+ (sh '("sh" "-c" "tail -n 5 /home/sjl/Sync/budget/hosts/*/records")
+ :result-type 'string)))
+
+(defcommand budget () ()
+ (message "$~D" (budget/current)))
+
+(defmacro with-budget-file ((f file &rest open-args) &body body)
+ `(with-open-file
+ (,f (format nil "/home/sjl/Sync/budget/hosts/~(~A~)/~A" *host* ,file)
+ ,@open-args)
+ ,@body))
+
+(defcommand spend (amount what) ((:integer "Amount: $") (:string "For: "))
+ (let ((current (with-budget-file (total "total")
+ (first (read-all-from-file total))))
+ (timestamp (local-time:to-rfc3339-timestring (local-time:now))))
+ (with-budget-file (total "total" :direction :output :if-exists :supersede)
+ (print (+ current amount) total))
+ (with-budget-file (records "records" :direction :output :if-exists :append :if-does-not-exist :create)
+ (print (list timestamp amount what) records))
+ (message "Spent $~D for ~A at ~A" amount what timestamp)))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/clipboard.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,22 @@
+(in-package :stumpwm-user)
+
+(load-module "clipboard-history")
+(clipboard-history:start-clipboard-manager)
+
+(defcommand generate-random-uuid () ()
+ (run-shell-command "uuidgen | tr -d '\\n' | ~/src/dotfiles/bin/pbcopy")
+ (message "Copied random UUID to clipboard."))
+
+(defcommand bee-movie-script () ()
+ (run-shell-command "pbeecopy")
+ (message "Copied the entire Bee Movie script to clipboard."))
+
+(defcommand urlize-jira-issue () ()
+ (let ((issue (str:trim (pbpaste))))
+ (if (ppcre:scan "^[A-Z0-9]+-\\d+$" issue)
+ (let* ((endpoint (str:trim (run-shell-command "grep endpoint .jira.d/config.yml | sed -e 's/.*: //'" t)))
+ (url (format nil "~A/browse/~A" endpoint issue)))
+ (pbcopy url)
+ (message "Copied ~A to the clipboard." url))
+ (message "Clipboard does not look like a JIRA issue."))))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/config.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,32 @@
+(in-package :stumpwm-user)
+
+(set-prefix-key (kbd "C-space"))
+(local-time:reread-timezone-repository)
+
+(set-focus-color "#aaaaaa")
+(set-win-bg-color "#111111")
+(set-unfocus-color "#444444")
+(setf *normal-border-width* 1
+ *default-bg-color* #x222222
+ *window-border-style* :thin
+ (xlib:window-background (screen-root (current-screen))) *default-bg-color*)
+
+(defvar *redirected* (redirect-all-output (data-dir-file "debug" "log")))
+
+(setf *mouse-focus-policy* :click
+ *message-window-gravity* :center
+ *input-window-gravity* :center
+ *debug-level* 0
+ *resize-increment* 75
+ *new-frame-action* :empty
+ *window-format* "(%n%m%20t)"
+ *window-name-source* :title
+ *maximum-completions* 20
+ *shell-program* "/home/sjl/src/dotfiles/bin/bash-dammit"
+ losh:*pbcopy-command* "/home/sjl/src/dotfiles/bin/pbcopy"
+ losh:*pbpaste-command* "/home/sjl/src/dotfiles/bin/pbpaste")
+
+(defun stumpwm::input-insert-hyphen-or-space (input key)
+ ;; Unbreak typing
+ (declare (ignore key))
+ (input-insert-char input #\space))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/external-screens.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,26 @@
+(in-package :stumpwm-user)
+
+(defcommand screen-laptop () ()
+ (only)
+ (hostcase
+ ((:gro :juss) (loop :with laptop = "eDP"
+ :with extern = (hostcase (:gro "DisplayPort-0")
+ (:juss "HDMI-A-0"))
+ :for (output commands) :in `((,laptop ("--auto"))
+ (,laptop ("--primary"))
+ (,extern ("--off")))
+ :do (progn (uiop:run-program `("xrandr" "--output" ,output ,@commands)))))
+ (t (message "Not configured on this system."))))
+
+(defcommand screen-external () ()
+ (only)
+ (hostcase
+ ((:gro :juss) (loop :with laptop = "eDP"
+ :with extern = (hostcase (:gro "DisplayPort-0")
+ (:juss "HDMI-A-0"))
+ :for (output commands) :in `((,extern ("--auto"))
+ (,extern ("--primary"))
+ (,laptop ("--off")))
+ :do (uiop:run-program `("xrandr" "--output" ,output ,@commands))))
+ (t (message "Not configured on this system."))))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/icelandic.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,66 @@
+(in-package :stumpwm-user)
+
+(defcommand send-key (key &optional (win (current-window))) (:key)
+ "Send key press and key release events for KEY to window WIN."
+ ;; from https://github.com/alezost/stumpwm-config/blob/master/utils.lisp
+ (let ((xwin (window-xwin win)))
+ (multiple-value-bind (code state) (stumpwm::key-to-keycode+state key)
+ (flet ((send (event)
+ (xlib:send-event xwin event (xlib:make-event-mask event)
+ :display *display*
+ :root (screen-root (window-screen win))
+ :x 0 :y 0 :root-x 0 :root-y 0
+ :window xwin :event-window xwin
+ :code code
+ :state state)))
+ (send :key-press)
+ (send :key-release)
+ (xlib:display-finish-output *display*)))))
+
+(defun send-keys (keys &key (win (current-window)) (sleep 0))
+ (dolist (k keys)
+ (send-key (kbd k) win)
+ (sleep sleep)))
+
+(defmacro defmultikey (name key compose-keys)
+ ;; Unfortunately we can't reliably autogen the name with something like
+ ;; (symb 'mk- compose-key) here because things like đ (th) and Đ (TH) would
+ ;; case fold to the same name.
+ `(progn
+ (defcommand ,name () ()
+ (send-keys '("Multi_key" ,@(map 'list #'string compose-keys))))
+ (define-key *top-map*
+ (kbd ,key) ,(string name))))
+
+(defmacro defmultikeys (&rest bindings)
+ `(progn ,@(loop for binding :in bindings :collect `(defmultikey ,@binding))))
+
+(defmultikeys
+ (isk-l-á "M-a" "'a")
+ (isk-u-Á "M-A" "'A")
+ (isk-l-é "M-e" "'e")
+ (isk-u-É "M-E" "'E")
+ (isk-l-í "M-i" "'i")
+ (isk-u-Í "M-I" "'I")
+ (isk-l-ó "M-o" "'o")
+ (isk-u-Ó "M-O" "'O")
+ (isk-l-ö "M-m" "\"o")
+ (isk-u-Ö "M-M" "\"O")
+ (isk-l-ú "M-u" "'u")
+ (isk-u-Ú "M-U" "'U")
+ (isk-l-ý "M-y" "'y")
+ (isk-u-Ý "M-Y" "'Y")
+ (isk-l-þ "M-t" "th")
+ (isk-u-Þ "M-T" "TH")
+ (isk-l-đ "M-d" "dh")
+ (isk-u-Đ "M-D" "DH")
+ (isk-l-æ "M-h" "ae")
+ (isk-u-Æ "M-H" "AE"))
+
+
+(defcommand thinkpad-ret () ()
+ (send-key (kbd "RET")))
+
+(defcommand thinkpad-bs () ()
+ (send-key (kbd "BackSpace")))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/key-mapping.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,223 @@
+(in-package :stumpwm-user)
+
+;;; Conventions:
+;;;
+;;; * Hyper-dir: move focus
+;;; * Hyper-Shift-dir: move window
+;;; * Hyper-Shift-Control-dir: swap window
+;;; * Hyper-F*: hardware
+;;; * Shift-F*: timers
+;;; * Hyper-Super-*: layout
+;;; * Hyper-*: miscellaneous
+;;; * Super-GAMERMOUSE: duplicate stuff to avoid swapping back
+
+
+(defmacro define-top-keys (&body keyforms)
+ `(progn ,@(loop :for form :in keyforms
+ :collect `(define-key *top-map*
+ (kbd ,(first form))
+ ,(second form)))))
+
+
+
+(define-top-keys ;; miscellaneous
+ ("H-m" "terminal")
+ ("H-M" "mark")
+ ("H-SunPageUp" "st-font-up")
+ ("H-SunPageDown" "st-font-down")
+ ("H-Home" "st-font-reset")
+ ("H-F4" "switch-yubikeys")
+ ("H-\\" "pass-personal")
+ ("H-|" "generate-password")
+ ("s-1" "pass-um-1")
+ ("s-2" "pass-um-2")
+ ("H-b" "browser")
+ ("H-O" "spotify")
+ ("H-o" "files")
+ ("H-z" "zoom")
+ ("H-Z" "toggle-zoom-mute")
+ ("C-H-Z" "end-zoom")
+ ("F26" "prev")
+ ("S-F26" "next")
+ ("H-q" "exec lock-screen")
+ ("H-y" "screenshot")
+ ("H-g" "gcontrol")
+ ("H-f" "save-fucked-screenshot")
+ ("H-F" "delete-fucked-screenshot")
+ ("H-r" "rain")
+ ("H-V" "vlc")
+ ("H-e" "budget")
+ ("H-E" "spend")
+ ("C-BackSpace" "clear-notifications")
+ )
+
+(define-top-keys ;; clipboard
+ ("H-c" "show-clipboard-history")
+ ("H-C" "clear-clipboard-history")
+ ("H-u" "generate-random-uuid")
+ ("H-B" "bee-movie-script")
+ ("M-H-u" "urlize-jira-issue"))
+
+(define-top-keys ;; movement
+ ("H-h" "move-focus* left")
+ ("H-j" "move-focus down")
+ ("H-k" "move-focus up")
+ ("H-l" "move-focus* right")
+
+ ("H-H" "move-window left")
+ ("H-J" "move-window down")
+ ("H-K" "move-window up")
+ ("H-L" "move-window right")
+
+ ("H-1" "gselect 1")
+ ("H-2" "gselect 2")
+ ("H-3" "gselect 3")
+ ("H-4" "gselect 4")
+ ("H-5" "gselect 5")
+ ("H-6" "gselect 6")
+
+ ("KP_End" "gselect 1")
+ ("KP_Down" "gselect 2")
+ ("KP_Page_Down" "gselect 3")
+ ("KP_Left" "gselect 4")
+ ("KP_Begin" "gselect 5")
+ ("KP_Right" "gselect 6")
+ ("KP_Home" "gselect 7")
+ ("KP_Up" "gselect 8")
+ ("KP_Page_Up" "gselect 9")
+
+ ("H-!" "gmove 1")
+ ("H-@" "gmove 2")
+ ("H-#" "gmove 3")
+ ("H-$" "gmove 4")
+ ("H-%" "gmove 5")
+ ("H-^" "gmove 6")
+
+ ("C-H-H" "exchange-direction left")
+ ("C-H-J" "exchange-direction down")
+ ("C-H-K" "exchange-direction up")
+ ("C-H-L" "exchange-direction right")
+
+ ("H-`" "next")
+ ("S-H-`" "prev")
+ ("H-n" "next-in-frame")
+ ("H-p" "prev-in-frame")
+ ("H-N" "pull-hidden-next")
+ ("H-P" "papers")
+
+ ("H-," "pull-from-windowlist"))
+
+
+(define-top-keys ;; splitting
+ ("H-s" "sane-vsplit")
+ ("H-v" "sane-hsplit")
+ ("H-=" "balance-frames"))
+
+(define-top-keys ;; killing
+ ("H-w" "delete")
+ ("H-W" "kill")
+ ("H-BackSpace" "remove")
+ ("S-H-BackSpace" "kill-and-remove"))
+
+;; (define-top-keys ;; broken thinkpad keys
+;; ("s-m" "thinkpad-ret")
+;; ("s-Delete" "thinkpad-bs"))
+
+
+(define-top-keys ;; naming
+ ("H-'" "title"))
+
+(define-top-keys ;; sound
+ ("H-F1" "mute")
+ ("H-F2" "volume-down")
+ ("H-F3" "volume-up")
+ ("XF86AudioMute" "exec mute")
+ ("XF86AudioRaiseVolume" "volume-down") ; todo unfuck the backwards mapping in qmk
+ ("XF86AudioLowerVolume" "volume-up"))
+
+(define-top-keys ;; screen
+ ("H-F5" "rotate-brightness-down")
+ ("H-F6" "rotate-brightness-up")
+ ("H-F7" "screen-laptop")
+ ("H-F8" "screen-external"))
+
+(define-top-keys ;; layout
+ ("s-H-o" "only")
+ ("s-H-t" "restore-from-file thirds")
+ ("s-H-m" "restore-from-file dev")
+ ("s-H-s" "restore-from-file streaming")
+ ("s-H-w" "restore-from-file work")
+ ("s-H-z" "restore-from-file zoom"))
+
+(define-top-keys ;; timers
+ ("s-F7" "tea-timer")
+ ("s-F9" "run-pop-timer")
+ ("s-F8" "set-pop-timer")
+ ("s-p" "posture-start")
+ ("s-P" "posture-stop")
+ ("s-y" "posture-answer-yes")
+ ("s-h" "posture-answer-meh")
+ ("s-n" "posture-answer-no")
+ ("s-\\" "posture-toggle-pause")
+ ("s-o" "posture-snooze"))
+
+(define-top-keys ;; stump
+ ("Pause" "terminal") ; jesus christ
+ ("H-F9" "sleep-machine")
+ ("H-F10" "toggle-stumptray")
+ ("H-F11" "toggle-current-mode-line")
+ ("H-F12" "refresh-heads"))
+
+
+(define-top-keys ;; GAMER MOUSE
+ ("s-$" "move-focus* left")
+ ("s-*" "move-focus down")
+ ("s-@" "move-focus up")
+ ("s-^" "move-focus* right")
+ ("M-s-Left" "gprev")
+ ("M-s-Right" "gnext")
+ ("s-Home" "next-in-frame")
+ ("s-End" "prev-in-frame")
+ ("s-)" "sane-hsplit")
+ ("s-_" "sane-vsplit")
+ ("s-Delete" "remove"))
+
+
+;; (stumpwm::unbind-remapped-keys)
+(define-remapped-keys
+ '(("st-256color"
+ ("s-c" . "C-C")
+ ("s-v" . "C-V")
+ ("C-=" . "S-C-SunPageUp")
+ ("C--" . "S-C-SunPageDown")
+ ("C-0" . "S-C-Home"))
+ ("(firefox|Google-chrome|Chromium-browser)"
+ ("s-[" . "C-S-Tab")
+ ("s-]" . "C-Tab")
+ ("C-a" . "Home")
+ ("C-e" . "End")
+ ;; I always try to hit ctrl-d to kill a browser window because I'm so used
+ ;; to terminal windows, and it ends up bookmarking the damn page. In the
+ ;; interest of not having a random collection of bookmarks grow over time,
+ ;; I'll just add a mapping to compensate for my stupid brain.
+ ("C-d" . "C-w")
+ ;; todo debug why this breaks a really fast C-a-k roll
+ ;; ("C-a" . "Home")
+ ;; ("C-e" . "End")
+ ("s-a" . "C-a")
+ ("s-d" . "C-d")
+ ("s-l" . "C-l")
+ ("s-t" . "C-t")
+ ("s-w" . "C-w")
+ ("s-r" . "C-r")
+ ("s-f" . "C-f")
+ ("s-z" . "C-z")
+ ("s-x" . "C-x")
+ ("s-c" . "C-c")
+ ("s-v" . "C-v"))
+ (""
+ ("s-z" . "C-z")
+ ("s-x" . "C-x")
+ ("s-c" . "C-c")
+ ("s-v" . "C-v"))))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/local-share-stumpwm/522.dump Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,52 @@
+#S(GDUMP
+ :NUMBER 3
+ :NAME "code"
+ :TREE (((#S(FDUMP
+ :NUMBER 0
+ :X 0
+ :Y 0
+ :WIDTH 1200
+ :HEIGHT 1020
+ :WINDOWS (31457288)
+ :CURRENT 31457288)
+ #S(FDUMP
+ :NUMBER 3
+ :X 0
+ :Y 1020
+ :WIDTH 1200
+ :HEIGHT 420
+ :WINDOWS (29360133)
+ :CURRENT 29360133))
+ ((#S(FDUMP
+ :NUMBER 1
+ :X 1200
+ :Y 0
+ :WIDTH 1735
+ :HEIGHT 1440
+ :WINDOWS (33554437)
+ :CURRENT 33554437)
+ (#S(FDUMP
+ :NUMBER 4
+ :X 2935
+ :Y 0
+ :WIDTH 985
+ :HEIGHT 720
+ :WINDOWS (37748741)
+ :CURRENT 37748741)
+ #S(FDUMP
+ :NUMBER 5
+ :X 2935
+ :Y 720
+ :WIDTH 985
+ :HEIGHT 720
+ :WINDOWS NIL
+ :CURRENT NIL)))
+ #S(FDUMP
+ :NUMBER 2
+ :X 3920
+ :Y 0
+ :WIDTH 1200
+ :HEIGHT 1440
+ :WINDOWS (16977044)
+ :CURRENT 16977044))))
+ :CURRENT 1)
\ No newline at end of file
--- a/stumpwm/local-share-stumpwm/522.lisp.dump Mon Mar 18 12:49:56 2024 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-#S(GDUMP
- :NUMBER 3
- :NAME "code"
- :TREE (((#S(FDUMP
- :NUMBER 0
- :X 0
- :Y 0
- :WIDTH 1200
- :HEIGHT 1020
- :WINDOWS (31457288)
- :CURRENT 31457288)
- #S(FDUMP
- :NUMBER 3
- :X 0
- :Y 1020
- :WIDTH 1200
- :HEIGHT 420
- :WINDOWS (29360133)
- :CURRENT 29360133))
- ((#S(FDUMP
- :NUMBER 1
- :X 1200
- :Y 0
- :WIDTH 1735
- :HEIGHT 1440
- :WINDOWS (33554437)
- :CURRENT 33554437)
- (#S(FDUMP
- :NUMBER 4
- :X 2935
- :Y 0
- :WIDTH 985
- :HEIGHT 720
- :WINDOWS (37748741)
- :CURRENT 37748741)
- #S(FDUMP
- :NUMBER 5
- :X 2935
- :Y 720
- :WIDTH 985
- :HEIGHT 720
- :WINDOWS NIL
- :CURRENT NIL)))
- #S(FDUMP
- :NUMBER 2
- :X 3920
- :Y 0
- :WIDTH 1200
- :HEIGHT 1440
- :WINDOWS (16977044)
- :CURRENT 16977044))))
- :CURRENT 1)
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/miscellaneous.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,97 @@
+(in-package :stumpwm-user)
+
+(defcommand sane-hsplit () ()
+ (hsplit)
+ (move-focus :right))
+
+(defcommand sane-vsplit () ()
+ (vsplit)
+ (move-focus :down))
+
+
+(defcommand move-focus* (direction)
+ ((:direction "Enter a direction: "))
+ (labels ((in-float-p ()
+ (typep (current-group) 'stumpwm::float-group))
+ (focus-first-frame ()
+ (unless (in-float-p)
+ ;; After moving to a new group we don't know which frame is
+ ;; focused, and unfortunately Stump doesn't give us a nice way to
+ ;; say "focus the leftmost frame" so we'll just move the focus
+ ;; a bunch of times and hope it's enough. Sigh.
+ (loop :repeat 15
+ :until (eql (current-frame)
+ (progn (move-focus (ecase direction
+ (:left :right)
+ (:right :left)))
+ (current-frame))))))
+ (next-group ()
+ (ecase direction
+ (:right (gnext))
+ (:left (gprev)))
+ (focus-first-frame)))
+ (unless (in-float-p)
+ (banish))
+ (if (in-float-p)
+ (next-group)
+ (let ((frame (current-frame)))
+ (move-focus direction)
+ (when (eql frame (current-frame))
+ (next-group))))))
+
+(defcommand toggle-current-mode-line () ()
+ (toggle-mode-line (current-screen) (current-head)))
+
+(defcommand toggle-stumptray () ()
+ (run-commands "stumptray"))
+
+(defcommand kill-and-remove () ()
+ (run-commands "kill" "remove"))
+
+(defcommand sleep-machine ()
+ ()
+ (hostcase
+ ((:gro :juss)
+ (run-shell-command "exec lock-screen")
+ (run-shell-command "systemctl suspend"))
+ (t (message "Not sleeping this machine for safety."))))
+
+(defcommand copy-clhs-url (s)
+ ((:string "Symbol: "))
+ (run-shell-command (format nil "clhs --url 'http://www.lispworks.com/documentation/HyperSpec/' --quiet --open echon '~A' | pbcopy" s)))
+
+(defcommand describe-window () ()
+ (show-window-properties))
+
+(defcommand rain () ()
+ (_ '("/home/sjl/src/dotfiles/lisp/bin/weather" "48105" "-H" "36")
+ (losh:sh _ :result-type 'list)
+ (mapcar (lambda (line) (ppcre:regex-replace " 1[0-9]:00 " line "^6\\&^*")) _)
+ (message "~{~A~^~%~}" _)))
+
+(defcommand mark (thing) ((:string "Mark: "))
+ (run-shell-command (format nil "mark ~A" thing)))
+
+(defcommand toggle-zoom-mute () ()
+ (when-let-window (win "^Zoom Meeting.*")
+ ;; Zoom stupidly won't accept the shortcut unless it's in focus
+ (unless (eql (window-group win) (current-group))
+ ;; jesus christ stump just export switch-to-group come on
+ (gselect (princ-to-string (group-number (window-group win)))))
+ (focus-window win t)
+ (meta (kbd "M-a"))))
+
+(defcommand end-zoom () ()
+ (when-let-window (win "^Zoom Meeting.*")
+ (kill-window win))
+ (sleep 2)
+ (when-let-window (win "^Zoom -.*")
+ (kill-window win)))
+
+(defcommand clear-notifications () ()
+ (run-shell-command "dunstctl close-all"))
+
+(defcommand start-vm () ()
+ (echo "Starting VM.")
+ (run-shell-command "/home/sjl/vms/run"))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/modeline.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,52 @@
+(in-package :stumpwm-user)
+
+(defun ensure-mode-line ()
+ (when (not (stumpwm::head-mode-line (current-head)))
+ (toggle-mode-line (current-screen) (current-head))))
+
+
+(defun configure-modeline ()
+ (setf
+ *time-modeline-string*
+ "%a %b %e %H:%M"
+
+ cpu::*cpu-usage-modeline-fmt*
+ "^[~A~3D%^]"
+
+ cpu::*cpu-modeline-fmt*
+ "[%c] [%f]"
+
+ mem::*mem-modeline-fmt*
+ "%b"
+
+ *screen-mode-line-format*
+ (append
+ (list "[^B"
+ '(:eval (princ-to-string (group-number (current-group))))
+ ":%n^b@%h] %W^>")
+
+ #+todo-some-day (list ;; "(V "
+ ;; ;; '(:eval (volume))
+ ;; ")"
+ " ")
+
+ ;; battery and brightness for laptops
+ (hostcase
+ ((:gro :juss)
+ '("(B %B)"
+ " (BR "
+ (:eval (princ-to-string (brightness)))
+ "%)")))
+
+ ;; temp, cpu, mem, time, tray
+ #+no (list "(TEMP %S) (CPU %C) (MEM %M) %d %T")
+ (list "(CPU %C) (MEM %M) %d %T")
+ ))
+
+ (setf *mode-line-timeout* 10)
+ (setf *mode-line-background-color* "#111111")
+
+ (ensure-mode-line))
+
+(configure-modeline)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/modules.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,8 @@
+(in-package :stumpwm-user)
+
+(load-module "pass")
+(load-module "battery-portable")
+(load-module "cpu")
+(load-module "hostname")
+(load-module "mem")
+(load-module "stumptray")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/package.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,5 @@
+(in-package :stumpwm-user)
+
+(shadow :window)
+
+(use-package :losh)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/passwords.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,23 @@
+(in-package :stumpwm-user)
+
+(defcommand pass-personal () ()
+ (let ((pass:*password-store* "/home/sjl/.password-store/")
+ (pass:*pass-notification-message* t))
+ (pass:pass-copy)))
+
+(defcommand pass-um-1 () ()
+ (echo "Copying UM level 1 password, touch key.")
+ (run-shell-command "pass -c umich.edu/slosh"))
+
+(defcommand pass-um-2 () ()
+ (echo "Copying UM level 2 password, touch key.")
+ (run-shell-command "pass -c umich.edu/l2"))
+
+(defcommand switch-yubikeys () ()
+ (echo (run-shell-command "switch-yubikeys" t)))
+
+(defcommand generate-password () ()
+ (run-shell-command "genpass | pbc")
+ (message "Generated a fresh password and copied it to the clipboard."))
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/posture.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,84 @@
+(in-package :stumpwm-user)
+
+(defparameter *posture-thread* nil)
+(defparameter *posture-should-stop* nil)
+(defparameter *posture-paused* nil)
+(defparameter *posture-snooze* nil)
+(defparameter *posture-current* 30)
+(defparameter *posture-min* 5)
+(defparameter *posture-max* (hours->seconds 2))
+
+(defun posture-paused-p ()
+ ;; this is the dumbest shit ever, but I can't figure out how to call into
+ ;; stumpish from the setguid slock process
+ (or *posture-paused* (probe-file "/tmp/.posture-pause")))
+
+(defun posture-snoozed-p ()
+ (and *posture-snooze*
+ (< (get-universal-time) *posture-snooze*)))
+
+(defcommand posture-pause () ()
+ (message "Pausing posture.")
+ (setf *posture-paused* t))
+
+(defcommand posture-unpause () ()
+ (message "Unpausing posture.")
+ (setf *posture-paused* nil))
+
+(defcommand posture-toggle-pause () ()
+ (if (setf *posture-paused* (not *posture-paused*))
+ (message "Posture is now paused.")
+ (message "Posture is now unpaused.")))
+
+(defcommand posture-snooze (hours)
+ ((:real "Snooze for how many hours? "))
+ (setf *posture-snooze* (+ (hours->seconds hours) (get-universal-time))))
+
+(defun posture-update (delta)
+ (setf *posture-current*
+ (clamp *posture-min* *posture-max* (* *posture-current* delta))))
+
+(defun posture-query ()
+ (speak "Is your posture okay?"))
+
+(defcommand posture-answer-yes () ()
+ (message "Good work.")
+ (run-shell-command "echo $(epochseconds) 1.0 >> ~/.posture.log")
+ (posture-update 11/10))
+
+(defcommand posture-answer-meh () ()
+ (message "Better than nothing.")
+ (run-shell-command "echo $(epochseconds) 0.5 >> ~/.posture.log"))
+
+(defcommand posture-answer-no () ()
+ (message "Try harder.")
+ (run-shell-command "echo $(epochseconds) 0.0 >> ~/.posture.log")
+ (posture-update 8/10))
+
+(defun posture% ()
+ (if *posture-should-stop*
+ nil
+ (progn (unless (or (posture-paused-p) (posture-snoozed-p))
+ (posture-query)
+ (sleep 10))
+ *posture-current*)))
+
+(defun posture-running-p ()
+ (and *posture-thread* (sb-thread:thread-alive-p *posture-thread*)))
+
+(defcommand posture-stop () ()
+ (setf *posture-should-stop* t))
+
+(defcommand posture-start () ()
+ (setf *posture-should-stop* nil)
+ (if (posture-running-p)
+ (message "Posture loop was already running.")
+ (setf *posture-thread*
+ (sb-thread:make-thread
+ (lambda ()
+ (loop :for seconds = (posture%)
+ :while seconds
+ :do (sleep seconds))
+ (message "Posture loop exiting."))
+ :name "Posture thread"))))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/screenshots.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,11 @@
+(in-package :stumpwm-user)
+
+(defcommand screenshot () ()
+ (run-shell-command "screenshot"))
+
+(defcommand save-fucked-screenshot () ()
+ (run-shell-command "broken-screenshot"))
+
+(defcommand delete-fucked-screenshot () ()
+ (run-shell-command "delete-broken-screenshot"))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/sensors.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,53 @@
+(in-package :stumpwm-user)
+
+(defun ? (obj &rest keys)
+ (if (null keys)
+ obj
+ (apply #'? (etypecase obj
+ (hash-table (gethash (first keys) obj)))
+ (rest keys))))
+
+(defun parse-sensors ()
+ ;; sensors -j is stupid and will output errors before the actual output on
+ ;; standard out, instead of putting them on standard err like a reasonable
+ ;; program, e.g.:
+ ;;
+ ;; ERROR: Can't get value of subfeature temp1_input: Can't read
+ ;; {
+ ;; "iwlwifi_1-virtual-0":{ … },
+ ;; …
+ ;;
+ ;; So we'll have to drop the `ERROR` lines before we can get to the actual
+ ;; goddamn JSON. UNIX programs are so great.
+ (let ((s (losh:sh '("sensors" "-j") :result-type 'stream)))
+ (loop :while (char= #\E (peek-char nil s)) :do (read-line s))
+ (jarl:read t s)))
+
+(defparameter *sensors-refresh-delay* 5.0 "How long between sensor refreshes (in seconds).")
+(defparameter *sensors-next-refresh* nil)
+(defparameter *sensors-cache* nil)
+
+(defun sensors% (&aux (sensors (parse-sensors)))
+ (hostcase
+ (:ouroboros (format nil "[CPU ~D°C] [GPU ~D°C ~D°C ~D°C]"
+ (round (? sensors "nct6779-isa-0290" "CPUTIN" "temp2_input"))
+ (round (? sensors "amdgpu-pci-4500" "edge" "temp1_input"))
+ (round (? sensors "amdgpu-pci-4500" "junction" "temp2_input"))
+ (round (? sensors "amdgpu-pci-4500" "mem" "temp3_input"))))
+ ((:gro :juss) (format nil "[CPU ~D°C] [GPU ~D°C]"
+ (round (? sensors "thinkpad-isa-0000" "CPU" "temp1_input"))
+ (round (? sensors "amdgpu-pci-0600" "edge" "temp1_input"))))
+ (t "?")))
+
+(defun sensors (&aux (now (get-internal-real-time)))
+ (if (or (null *sensors-next-refresh*)
+ (>= now *sensors-next-refresh*))
+ (setf *sensors-next-refresh* (+ now (* internal-time-units-per-second *sensors-refresh-delay*))
+ *sensors-cache* (sensors%))
+ *sensors-cache*))
+
+(defun sensors-modeline (ml)
+ (declare (ignore ml))
+ (sensors))
+
+(add-screen-mode-line-formatter #\S #'sensors-modeline)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/sound.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,14 @@
+(in-package :stumpwm-user)
+
+(defcommand mute () ()
+ (run-shell-command "mute")
+ (echo "Muted."))
+
+(defcommand volume-up () ()
+ (run-shell-command "amixer -q sset Master 5%+")
+ (message "Volume: ~D%" (volume)))
+
+(defcommand volume-down () ()
+ (run-shell-command "amixer -q sset Master 5%-")
+ (message "Volume: ~D%" (volume)))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/stumpconfig.asd Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,36 @@
+(asdf:defsystem :stumpconfig
+ :description "My StumpWM configuration."
+ :author "Steve Losh <steve@stevelosh.com>"
+
+ :depends-on (:losh
+ :split-sequence
+ :alexandria
+ :parse-number
+ :str
+ :cl-ppcre
+ :bordeaux-threads
+ :jarl
+ :local-time)
+
+ :serial t
+ :components ((:file "package")
+ (:file "modules")
+ (:file "config")
+ (:file "utils")
+ (:file "posture")
+ (:file "budget")
+ (:file "screenshots")
+ (:file "sound")
+ (:file "brightness")
+ (:file "passwords")
+ (:file "terminal-fonts")
+ (:file "clipboard")
+ (:file "applications")
+ (:file "timers")
+ (:file "icelandic")
+ (:file "sensors")
+ (:file "modeline")
+ (:file "vlime")
+ (:file "external-screens")
+ (:file "miscellaneous")
+ (:file "key-mapping")))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/stumpwmrc Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,12 @@
+(in-package :stumpwm-user)
+
+(ql:quickload :stumpconfig)
+
+(defvar *tray-loaded*
+ (run-commands "stumptray"))
+
+(defvar *dunst*
+ (run-shell-command "/usr/bin/dunst -conf ~/.dunstrc"))
+
+(when (probe-file "/home/sjl/.stumpwmrc.local")
+ (load "/home/sjl/.stumpwmrc.local"))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/terminal-fonts.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,27 @@
+(in-package :stumpwm-user)
+
+(defcommand reload-terminal-font-size ()
+ ()
+ (setf *terminal-font-size*
+ (if (probe-file "/home/sjl/.terminal-font")
+ (with-open-file (f "/home/sjl/.terminal-font")
+ (read f))
+ 11)))
+
+(defparameter *terminal-font-size* (if (probe-file "/home/sjl/.terminal-font")
+ (with-open-file (f "/home/sjl/.terminal-font")
+ (read f))
+ 11))
+
+(defcommand st-font-up ()
+ ()
+ (loop :repeat 7 :do (meta (kbd "C-S-SunPageUp"))))
+
+(defcommand st-font-down ()
+ ()
+ (loop :repeat 7 :do (meta (kbd "C-S-SunPageDown"))))
+
+(defcommand st-font-reset ()
+ ()
+ (meta (kbd "C-S-Home")))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/timers.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,38 @@
+(in-package :stumpwm-user)
+
+(defparameter *pop-timer-minutes* nil)
+(defparameter *pop-timer-seconds* nil)
+
+(defun pop-timer ()
+ (if (or (null *pop-timer-minutes*)
+ (null *pop-timer-seconds*))
+ (message "Pop timer is not configured.")
+ (progn
+ (message "Setting pop timer for ~D:~2,'0D."
+ *pop-timer-minutes* *pop-timer-seconds*)
+ (let* ((warning-time 30)
+ (total-time (+ (* *pop-timer-minutes* 60) *pop-timer-seconds*))
+ (initial-time (- total-time warning-time)))
+ (sb-thread:make-thread
+ (lambda ()
+ (if (plusp initial-time)
+ (progn (sleep initial-time)
+ (speak "Pop soon.")
+ (sleep warning-time))
+ (sleep total-time))
+ (speak "Pop!"))
+ :name "Pop Timer")))))
+
+(defcommand run-pop-timer () ()
+ (pop-timer))
+
+(defcommand set-pop-timer (minutes seconds)
+ ((:integer "Minutes: ")
+ (:integer "Seconds: "))
+ (setf *pop-timer-minutes* minutes
+ *pop-timer-seconds* seconds))
+
+(defcommand tea-timer (seconds)
+ ((:integer "Seconds: "))
+ (run-shell-command (format nil "tea ~D" seconds)))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/utils.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,126 @@
+(in-package :stumpwm-user)
+
+(defun string-contains (needle string)
+ (and (search needle string :test #'char=) t))
+
+(defun string-grep (needle text &key first-only)
+ (_ text
+ (split-sequence:split-sequence #\newline _)
+ (if first-only
+ (find needle _ :test #'string-contains)
+ (remove-if-not (alexandria:curry #'string-contains needle) _))))
+
+(defun string-split (delimiters string)
+ (split-sequence:split-sequence delimiters string
+ :test (lambda (bag ch)
+ (find ch bag :test #'char=))))
+
+(defun run-and-echo-shell-command (command &rest args)
+ (message command)
+ (apply #'run-shell-command command args))
+
+
+(defun mod+ (n increment modulo)
+ (mod (+ n increment) modulo))
+
+
+(defun volume ()
+ (_ (run-shell-command "amixer sget Master" t)
+ (string-grep "Front Left:" _ :first-only t)
+ (string-split "[]" _)
+ second
+ (string-trim "%" _)
+ parse-integer))
+
+
+(defun current-frame ()
+ (stumpwm::tile-group-current-frame (current-group)))
+
+
+(defun keywordize (string)
+ (_ string
+ (string-trim (string #\newline) _)
+ string-upcase
+ (intern _ (find-package :keyword))))
+
+(defparameter *host* (keywordize (machine-instance)))
+
+
+(defmacro ehostcase (&body clauses)
+ `(ecase *host* ,@clauses))
+
+(defmacro hostcase (&body clauses)
+ `(case *host* ,@clauses))
+
+
+(defcommand speak (text)
+ ((:string "Text: "))
+ (message text)
+ (run-shell-command (format nil "~~/src/dotfiles/bin/say '~A'" text)))
+
+
+(defun seconds->hours (seconds)
+ (/ seconds 60 60))
+
+(defun hours->seconds (hours)
+ (* hours 60 60))
+
+
+(define-stumpwm-type :integer (input prompt)
+ ;; Annoyingly, StumpWM's built-in :number type isn't actually number, but is
+ ;; actually just integers. Define a better-named type here.
+ (when-let ((n (or (argument-pop input)
+ (read-one-line (current-screen) prompt))))
+ (handler-case
+ (parse-integer n)
+ (parse-error (c)
+ (declare (ignore c))
+ (throw 'error "Integer required.")))))
+
+(define-stumpwm-type :real (input prompt)
+ (when-let ((n (or (argument-pop input)
+ (read-one-line (current-screen) prompt))))
+ (handler-case
+ (let ((result (parse-number:parse-number n)))
+ (assert (typep result 'real))
+ result)
+ (error (c)
+ (declare (ignore c))
+ (throw 'error "Real required.")))))
+
+
+(defun window-match-p (query window)
+ "Return whether `window` matches `query`.
+
+ `query` must be of the form `(query-type query-value)`.
+
+ `query-type` must be one of `:title` or `:class`.
+
+ `query-value` must either be a string (which must be matched exactly) or
+ a PPCRE scanner.
+
+ "
+ (destructuring-bind (query-type query) query
+ (let ((value (ecase query-type
+ (:title (window-title window))
+ (:class (window-class window)))))
+ (etypecase query
+ (string (string= query value))
+ (function (ppcre:scan query value))))))
+
+(defun all-windows ()
+ "Return a fresh list of all windows on all screens. Yes, all of them."
+ (mapcan #'screen-windows *screen-list*))
+
+(defun find-window (query)
+ "Find and return the first window that matches `query` under `window-match-p`."
+ (find-if (lambda (w) (window-match-p query w)) (all-windows)))
+
+(defun find-windows (query)
+ "Find and return a fresh list of all windows that match `query` under `window-match-p`."
+ (remove-if-not (lambda (w) (window-match-p query w)) (all-windows)))
+
+(defmacro when-let-window ((symbol title-query) &body body)
+ `(when-let ((,symbol (find-window `(:title ,(ppcre:create-scanner ,title-query)))))
+ ,@body))
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/stumpwm/vlime.lisp Tue Mar 19 13:55:42 2024 -0400
@@ -0,0 +1,15 @@
+(in-package :stumpwm-user)
+
+(defcommand vlime () ()
+ (load "~/src/dotfiles/vim/bundle/vlime/lisp/start-vlime.lisp")
+ (message "Started VLIME"))
+
+(defcommand vlime-port (port) ((:integer "Port: "))
+ "Start VLIME on the given port.
+
+ Good for bootstrapping a VLIME connection when you accidentally started a
+ VLIME instance on another port that you don't want to mess with.
+
+ "
+ (funcall (read-from-string "vlime-loader::run") port)
+ (message "Started VLIME"))
--- a/stumpwmrc Mon Mar 18 12:49:56 2024 -0400
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1041 +0,0 @@
-(in-package :stumpwm-user)
-(shadow :window)
-
-(ql:quickload '(:losh :split-sequence :alexandria :parse-number :str :cl-ppcre :bordeaux-threads :jarl :local-time)
- :silent t)
-
-(use-package :losh)
-
-;;;; Config -------------------------------------------------------------------
-(set-prefix-key (kbd "C-space"))
-(local-time:reread-timezone-repository)
-
-(set-focus-color "#aaaaaa")
-(set-win-bg-color "#111111")
-(set-unfocus-color "#444444")
-(setf *normal-border-width* 1
- *default-bg-color* #x222222
- *window-border-style* :thin
- (xlib:window-background (screen-root (current-screen))) *default-bg-color*)
-
-(defvar *redirected* (redirect-all-output (data-dir-file "debug" "log")))
-
-(setf *mouse-focus-policy* :click
- *message-window-gravity* :center
- *input-window-gravity* :center
- *debug-level* 0
- *resize-increment* 75
- *new-frame-action* :empty
- *window-format* "(%n%m%20t)"
- *window-name-source* :title
- *maximum-completions* 20
- *shell-program* "/home/sjl/src/dotfiles/bin/bash-dammit"
- losh:*pbcopy-command* "/home/sjl/src/dotfiles/bin/pbcopy"
- losh:*pbpaste-command* "/home/sjl/src/dotfiles/bin/pbpaste")
-
-
-;;;; Utils --------------------------------------------------------------------
-(defun string-contains (needle string)
- (and (search needle string :test #'char=) t))
-
-(defun string-grep (needle text &key first-only)
- (_ text
- (split-sequence:split-sequence #\newline _)
- (if first-only
- (find needle _ :test #'string-contains)
- (remove-if-not (alexandria:curry #'string-contains needle) _))))
-
-(defun string-split (delimiters string)
- (split-sequence:split-sequence delimiters string
- :test (lambda (bag ch)
- (find ch bag :test #'char=))))
-
-(defun run-and-echo-shell-command (command &rest args)
- (message command)
- (apply #'run-shell-command command args))
-
-
-(defun mod+ (n increment modulo)
- (mod (+ n increment) modulo))
-
-
-(defun volume ()
- (_ (run-shell-command "amixer sget Master" t)
- (string-grep "Front Left:" _ :first-only t)
- (string-split "[]" _)
- second
- (string-trim "%" _)
- parse-integer))
-
-
-(defun current-frame ()
- (stumpwm::tile-group-current-frame (current-group)))
-
-
-(defun keywordize (string)
- (_ string
- (string-trim (string #\newline) _)
- string-upcase
- (intern _ (find-package :keyword))))
-
-(defparameter *host* (keywordize (machine-instance)))
-
-
-(defmacro ehostcase (&body clauses)
- `(ecase *host* ,@clauses))
-
-(defmacro hostcase (&body clauses)
- `(case *host* ,@clauses))
-
-
-(defcommand speak (text)
- ((:string "Text: "))
- (message text)
- (run-shell-command (format nil "~~/src/dotfiles/bin/say '~A'" text)))
-
-
-(defun seconds->hours (seconds)
- (/ seconds 60 60))
-
-(defun hours->seconds (hours)
- (* hours 60 60))
-
-
-(define-stumpwm-type :integer (input prompt)
- ;; Annoyingly, StumpWM's built-in :number type isn't actually number, but is
- ;; actually just integers. Define a better-named type here.
- (when-let ((n (or (argument-pop input)
- (read-one-line (current-screen) prompt))))
- (handler-case
- (parse-integer n)
- (parse-error (c)
- (declare (ignore c))
- (throw 'error "Integer required.")))))
-
-(define-stumpwm-type :real (input prompt)
- (when-let ((n (or (argument-pop input)
- (read-one-line (current-screen) prompt))))
- (handler-case
- (let ((result (parse-number:parse-number n)))
- (assert (typep result 'real))
- result)
- (error (c)
- (declare (ignore c))
- (throw 'error "Real required.")))))
-
-
-(defun window-match-p (query window)
- "Return whether `window` matches `query`.
-
- `query` must be of the form `(query-type query-value)`.
-
- `query-type` must be one of `:title` or `:class`.
-
- `query-value` must either be a string (which must be matched exactly) or
- a PPCRE scanner.
-
- "
- (destructuring-bind (query-type query) query
- (let ((value (ecase query-type
- (:title (window-title window))
- (:class (window-class window)))))
- (etypecase query
- (string (string= query value))
- (function (ppcre:scan query value))))))
-
-(defun all-windows ()
- "Return a fresh list of all windows on all screens. Yes, all of them."
- (mapcan #'screen-windows *screen-list*))
-
-(defun find-window (query)
- "Find and return the first window that matches `query` under `window-match-p`."
- (find-if (lambda (w) (window-match-p query w)) (all-windows)))
-
-(defun find-windows (query)
- "Find and return a fresh list of all windows that match `query` under `window-match-p`."
- (remove-if-not (lambda (w) (window-match-p query w)) (all-windows)))
-
-(defmacro when-let-window ((symbol title-query) &body body)
- `(when-let ((,symbol (find-window `(:title ,(ppcre:create-scanner ,title-query)))))
- ,@body))
-
-
-;;;; Posture ------------------------------------------------------------------
-(defparameter *posture-thread* nil)
-(defparameter *posture-should-stop* nil)
-(defparameter *posture-paused* nil)
-(defparameter *posture-snooze* nil)
-(defparameter *posture-current* 30)
-(defparameter *posture-min* 5)
-(defparameter *posture-max* (hours->seconds 2))
-
-(defun posture-paused-p ()
- ;; this is the dumbest shit ever, but I can't figure out how to call into
- ;; stumpish from the setguid slock process
- (or *posture-paused* (probe-file "/tmp/.posture-pause")))
-
-(defun posture-snoozed-p ()
- (and *posture-snooze*
- (< (get-universal-time) *posture-snooze*)))
-
-(defcommand posture-pause () ()
- (message "Pausing posture.")
- (setf *posture-paused* t))
-
-(defcommand posture-unpause () ()
- (message "Unpausing posture.")
- (setf *posture-paused* nil))
-
-(defcommand posture-toggle-pause () ()
- (if (setf *posture-paused* (not *posture-paused*))
- (message "Posture is now paused.")
- (message "Posture is now unpaused.")))
-
-(defcommand posture-snooze (hours)
- ((:real "Snooze for how many hours? "))
- (setf *posture-snooze* (+ (hours->seconds hours) (get-universal-time))))
-
-(defun posture-update (delta)
- (setf *posture-current*
- (clamp *posture-min* *posture-max* (* *posture-current* delta))))
-
-(defun posture-query ()
- (speak "Is your posture okay?"))
-
-(defcommand posture-answer-yes () ()
- (message "Good work.")
- (run-shell-command "echo $(epochseconds) 1.0 >> ~/.posture.log")
- (posture-update 11/10))
-
-(defcommand posture-answer-meh () ()
- (message "Better than nothing.")
- (run-shell-command "echo $(epochseconds) 0.5 >> ~/.posture.log"))
-
-(defcommand posture-answer-no () ()
- (message "Try harder.")
- (run-shell-command "echo $(epochseconds) 0.0 >> ~/.posture.log")
- (posture-update 8/10))
-
-(defun posture% ()
- (if *posture-should-stop*
- nil
- (progn (unless (or (posture-paused-p) (posture-snoozed-p))
- (posture-query)
- (sleep 10))
- *posture-current*)))
-
-(defun posture-running-p ()
- (and *posture-thread* (sb-thread:thread-alive-p *posture-thread*)))
-
-(defcommand posture-stop () ()
- (setf *posture-should-stop* t))
-
-(defcommand posture-start () ()
- (setf *posture-should-stop* nil)
- (if (posture-running-p)
- (message "Posture loop was already running.")
- (setf *posture-thread*
- (sb-thread:make-thread
- (lambda ()
- (loop :for seconds = (posture%)
- :while seconds
- :do (sleep seconds))
- (message "Posture loop exiting."))
- :name "Posture thread"))))
-
-
-;;;; Budget ------------------------------------------------------------------
-(defparameter *tz/eastern*
- (local-time:find-timezone-by-location-name "US/Eastern"))
-
-(defparameter *budget/start*
- (local-time:encode-timestamp 0 0 0 0 29 8 2023 :timezone *tz/eastern*))
-
-(defun budget/per-day ()
- (first (losh:read-all-from-file "/home/sjl/Sync/budget/per-day")))
-
-(defun budget/elapsed ()
- (local-time:timestamp-difference (local-time:now) *budget/start*))
-
-(defun budget/days-elapsed ()
- (floor (/ (budget/elapsed) (* 60 60 24))))
-
-(defun budget/in ()
- (* (budget/days-elapsed) (budget/per-day)))
-
-(defun budget/out ()
- (loop :for path :in (directory "/home/sjl/Sync/budget/hosts/*/total")
- :summing (print (first (read-all-from-file (print path))))))
-
-(defun budget/current ()
- (- (budget/in) (budget/out)))
-
-(defcommand budget-dump () ()
- (message
- (sh '("sh" "-c" "tail -n 5 /home/sjl/Sync/budget/hosts/*/records")
- :result-type 'string)))
-
-(defcommand budget () ()
- (message "$~D" (budget/current)))
-
-(defmacro with-budget-file ((f file &rest open-args) &body body)
- `(with-open-file
- (,f (format nil "/home/sjl/Sync/budget/hosts/~(~A~)/~A" *host* ,file)
- ,@open-args)
- ,@body))
-
-(defcommand spend (amount what) ((:integer "Amount: $") (:string "For: "))
- (let ((current (with-budget-file (total "total")
- (first (read-all-from-file total))))
- (timestamp (local-time:to-rfc3339-timestring (local-time:now))))
- (with-budget-file (total "total" :direction :output :if-exists :supersede)
- (print (+ current amount) total))
- (with-budget-file (records "records" :direction :output :if-exists :append :if-does-not-exist :create)
- (print (list timestamp amount what) records))
- (message "Spent $~D for ~A at ~A" amount what timestamp)))
-
-
-;;;; Load ---------------------------------------------------------------------
-(load-module "pass")
-
-;;;; Screenshotting -----------------------------------------------------------
-(defcommand screenshot () ()
- (run-shell-command "screenshot"))
-
-(defcommand save-fucked-screenshot () ()
- (run-shell-command "broken-screenshot"))
-
-(defcommand delete-fucked-screenshot () ()
- (run-shell-command "delete-broken-screenshot"))
-
-
-;;;; Sound --------------------------------------------------------------------
-(defcommand mute () ()
- (run-shell-command "mute")
- (echo "Muted."))
-
-(defcommand volume-up () ()
- (run-shell-command "amixer -q sset Master 5%+")
- (message "Volume: ~D%" (volume)))
-
-(defcommand volume-down () ()
- (run-shell-command "amixer -q sset Master 5%-")
- (message "Volume: ~D%" (volume)))
-
-
-;;;; Brightness ---------------------------------------------------------------
-(defparameter *brightness-values* #(0 1 5 10 20 30 40 55 70 85 100))
-(defvar *brightness-index* 5)
-
-(defun brightness ()
- (aref *brightness-values* *brightness-index*))
-
-(defun set-brightness (value)
- (run-and-echo-shell-command
- (hostcase
- ((:gro :juss) (format nil "xrandr --output ~A --brightness ~D"
- (hostcase ((:gro :juss) "eDP"))
- (/ value 100.0)))
- (t (message "Not sure how to set brightness on this machine.")))))
-
-(defun rotate-brightness (delta)
- (setf *brightness-index*
- (mod+ *brightness-index* delta (length *brightness-values*)))
- (set-brightness (brightness)))
-
-
-(defcommand rotate-brightness-up () ()
- (rotate-brightness 1))
-
-(defcommand rotate-brightness-down () ()
- (rotate-brightness -1))
-
-
-;;;; Miscellaneous ------------------------------------------------------------
-(defcommand sane-hsplit () ()
- (hsplit)
- (move-focus :right))
-
-(defcommand sane-vsplit () ()
- (vsplit)
- (move-focus :down))
-
-
-(defcommand move-focus* (direction)
- ((:direction "Enter a direction: "))
- (labels ((in-float-p ()
- (typep (current-group) 'stumpwm::float-group))
- (focus-first-frame ()
- (unless (in-float-p)
- ;; After moving to a new group we don't know which frame is
- ;; focused, and unfortunately Stump doesn't give us a nice way to
- ;; say "focus the leftmost frame" so we'll just move the focus
- ;; a bunch of times and hope it's enough. Sigh.
- (loop :repeat 15
- :until (eql (current-frame)
- (progn (move-focus (ecase direction
- (:left :right)
- (:right :left)))
- (current-frame))))))
- (next-group ()
- (ecase direction
- (:right (gnext))
- (:left (gprev)))
- (focus-first-frame)))
- (unless (in-float-p)
- (banish))
- (if (in-float-p)
- (next-group)
- (let ((frame (current-frame)))
- (move-focus direction)
- (when (eql frame (current-frame))
- (next-group))))))
-
-(defcommand screen-laptop () ()
- (only)
- (hostcase
- ((:gro :juss) (loop :with laptop = "eDP"
- :with extern = (hostcase (:gro "DisplayPort-0")
- (:juss "HDMI-A-0"))
- :for (output commands) :in `((,laptop ("--auto"))
- (,laptop ("--primary"))
- (,extern ("--off")))
- :do (progn (uiop:run-program `("xrandr" "--output" ,output ,@commands)))))
- (t (message "Not configured on this system."))))
-
-(defcommand screen-external () ()
- (only)
- (hostcase
- ((:gro :juss) (loop :with laptop = "eDP"
- :with extern = (hostcase (:gro "DisplayPort-0")
- (:juss "HDMI-A-0"))
- :for (output commands) :in `((,extern ("--auto"))
- (,extern ("--primary"))
- (,laptop ("--off")))
- :do (uiop:run-program `("xrandr" "--output" ,output ,@commands))))
- (t (message "Not configured on this system."))))
-
-(defcommand vlime () ()
- (load "~/src/dotfiles/vim/bundle/vlime/lisp/start-vlime.lisp")
- (message "Started VLIME"))
-
-(defcommand vlime-port (port) ((:integer "Port: "))
- "Start VLIME on the given port.
-
- Good for bootstrapping a VLIME connection when you accidentally started a
- VLIME instance on another port that you don't want to mess with.
-
- "
- (funcall (read-from-string "vlime-loader::run") port)
- (message "Started VLIME"))
-
-(defcommand toggle-current-mode-line () ()
- (toggle-mode-line (current-screen) (current-head)))
-
-(defcommand toggle-stumptray () ()
- (run-commands "stumptray"))
-
-(defcommand pass-personal () ()
- (let ((pass:*password-store* "/home/sjl/.password-store/")
- (pass:*pass-notification-message* t))
- (pass:pass-copy)))
-
-(defcommand pass-um-1 () ()
- (echo "Copying UM level 1 password, touch key.")
- (run-shell-command "pass -c umich.edu/slosh"))
-
-(defcommand pass-um-2 () ()
- (echo "Copying UM level 2 password, touch key.")
- (run-shell-command "pass -c umich.edu/l2"))
-
-(defcommand switch-yubikeys () ()
- (echo (run-shell-command "switch-yubikeys" t)))
-
-(defcommand generate-password () ()
- (run-shell-command "genpass | pbc")
- (message "Generated a fresh password and copied it to the clipboard."))
-
-(defcommand kill-and-remove () ()
- (run-commands "kill" "remove"))
-
-(defcommand sleep-machine ()
- ()
- (hostcase
- ((:gro :juss)
- (run-shell-command "exec lock-screen")
- (run-shell-command "systemctl suspend"))
- (t (message "Not sleeping this machine for safety."))))
-
-(defcommand copy-clhs-url (s)
- ((:string "Symbol: "))
- (run-shell-command (format nil "clhs --url 'http://www.lispworks.com/documentation/HyperSpec/' --quiet --open echon '~A' | pbcopy" s)))
-
-(defcommand describe-window () ()
- (show-window-properties))
-
-(defcommand rain () ()
- (_ '("/home/sjl/src/dotfiles/lisp/bin/weather" "48105" "-H" "36")
- (losh:sh _ :result-type 'list)
- (mapcar (lambda (line) (ppcre:regex-replace " 1[0-9]:00 " line "^6\\&^*")) _)
- (message "~{~A~^~%~}" _)))
-
-(defcommand mark (thing) ((:string "Mark: "))
- (run-shell-command (format nil "mark ~A" thing)))
-
-(defcommand toggle-zoom-mute () ()
- (when-let-window (win "^Zoom Meeting.*")
- ;; Zoom stupidly won't accept the shortcut unless it's in focus
- (unless (eql (window-group win) (current-group))
- ;; jesus christ stump just export switch-to-group come on
- (gselect (princ-to-string (group-number (window-group win)))))
- (focus-window win t)
- (meta (kbd "M-a"))))
-
-(defcommand end-zoom () ()
- (when-let-window (win "^Zoom Meeting.*")
- (kill-window win))
- (sleep 2)
- (when-let-window (win "^Zoom -.*")
- (kill-window win)))
-
-(defcommand clear-notifications () ()
- (run-shell-command "dunstctl close-all"))
-
-(defcommand rstudio () ()
- (run-shell-command "rstudio"))
-
-(defcommand start-vm () ()
- (echo "Starting VM.")
- (run-shell-command "/home/sjl/vms/run"))
-
-
-
-;;;; Terminal Fonts -----------------------------------------------------------
-(defcommand reload-terminal-font-size ()
- ()
- (setf *terminal-font-size*
- (if (probe-file "/home/sjl/.terminal-font")
- (with-open-file (f "/home/sjl/.terminal-font")
- (read f))
- 11)))
-
-(defparameter *terminal-font-size* (if (probe-file "/home/sjl/.terminal-font")
- (with-open-file (f "/home/sjl/.terminal-font")
- (read f))
- 11))
-
-(defcommand st-font-up ()
- ()
- (loop :repeat 7 :do (meta (kbd "C-S-SunPageUp"))))
-
-(defcommand st-font-down ()
- ()
- (loop :repeat 7 :do (meta (kbd "C-S-SunPageDown"))))
-
-(defcommand st-font-reset ()
- ()
- (meta (kbd "C-S-Home")))
-
-
-;;;; Clipboard/Data Generation ------------------------------------------------
-(load-module "clipboard-history")
-(clipboard-history:start-clipboard-manager)
-
-(defcommand generate-random-uuid () ()
- (run-shell-command "uuidgen | tr -d '\\n' | ~/src/dotfiles/bin/pbcopy")
- (message "Copied random UUID to clipboard."))
-
-(defcommand bee-movie-script () ()
- (run-shell-command "pbeecopy")
- (message "Copied the entire Bee Movie script to clipboard."))
-
-(defcommand urlize-jira-issue () ()
- (let ((issue (str:trim (pbpaste))))
- (if (ppcre:scan "^[A-Z0-9]+-\\d+$" issue)
- (let* ((endpoint (str:trim (run-shell-command "grep endpoint .jira.d/config.yml | sed -e 's/.*: //'" t)))
- (url (format nil "~A/browse/~A" endpoint issue)))
- (pbcopy url)
- (message "Copied ~A to the clipboard." url))
- (message "Clipboard does not look like a JIRA issue."))))
-
-
-;;;; Applications -------------------------------------------------------------
-(defcommand spotify () ()
- (run-or-raise "spotify" '(:class "Spotify")))
-
-(defcommand files () ()
- (run-shell-command "open $HOME"))
-
-(defcommand browser () ()
- (run-or-raise "firefox" '(:class "firefox")))
-
-(defcommand vlc () ()
- (run-or-raise "vlc" '(:class "vlc")))
-
-(defcommand terminal () ()
- (run-shell-command (format nil "st -f 'Ubuntu Mono:size=~D'" *terminal-font-size*)))
-
-(defcommand terminal-apl () ()
- (run-shell-command "st -f 'BQN386 Unicode:style=Regular:size=12'"))
-
-(defcommand gcontrol () ()
- (run-or-raise "gcontrol" '(:class "Gnome-control-center")))
-
-(defcommand zoom () ()
- (when-let-window (w "^Zoom Meeting.*")
- (focus-window w t)))
-
-(defcommand papers () ()
- (run-or-raise "jabref" '(:class "org.jabref.gui.MainApplication")))
-
-
-;;;; Timers -------------------------------------------------------------------
-(defparameter *pop-timer-minutes* nil)
-(defparameter *pop-timer-seconds* nil)
-
-(defun pop-timer ()
- (if (or (null *pop-timer-minutes*)
- (null *pop-timer-seconds*))
- (message "Pop timer is not configured.")
- (progn
- (message "Setting pop timer for ~D:~2,'0D."
- *pop-timer-minutes* *pop-timer-seconds*)
- (let* ((warning-time 30)
- (total-time (+ (* *pop-timer-minutes* 60) *pop-timer-seconds*))
- (initial-time (- total-time warning-time)))
- (sb-thread:make-thread
- (lambda ()
- (if (plusp initial-time)
- (progn (sleep initial-time)
- (speak "Pop soon.")
- (sleep warning-time))
- (sleep total-time))
- (speak "Pop!"))
- :name "Pop Timer")))))
-
-(defcommand run-pop-timer () ()
- (pop-timer))
-
-(defcommand set-pop-timer (minutes seconds)
- ((:integer "Minutes: ")
- (:integer "Seconds: "))
- (setf *pop-timer-minutes* minutes
- *pop-timer-seconds* seconds))
-
-(defcommand tea-timer (seconds)
- ((:integer "Seconds: "))
- (run-shell-command (format nil "tea ~D" seconds)))
-
-
-;;;; Isk ----------------------------------------------------------------------
-(defcommand send-key (key &optional (win (current-window))) (:key)
- "Send key press and key release events for KEY to window WIN."
- ;; from https://github.com/alezost/stumpwm-config/blob/master/utils.lisp
- (let ((xwin (window-xwin win)))
- (multiple-value-bind (code state) (stumpwm::key-to-keycode+state key)
- (flet ((send (event)
- (xlib:send-event xwin event (xlib:make-event-mask event)
- :display *display*
- :root (screen-root (window-screen win))
- :x 0 :y 0 :root-x 0 :root-y 0
- :window xwin :event-window xwin
- :code code
- :state state)))
- (send :key-press)
- (send :key-release)
- (xlib:display-finish-output *display*)))))
-
-(defun send-keys (keys &key (win (current-window)) (sleep 0))
- (dolist (k keys)
- (send-key (kbd k) win)
- (sleep sleep)))
-
-(defmacro defmultikey (name key compose-keys)
- ;; Unfortunately we can't reliably autogen the name with something like
- ;; (symb 'mk- compose-key) here because things like đ (th) and Đ (TH) would
- ;; case fold to the same name.
- `(progn
- (defcommand ,name () ()
- (send-keys '("Multi_key" ,@(map 'list #'string compose-keys))))
- (define-key *top-map*
- (kbd ,key) ,(string name))))
-
-(defmacro defmultikeys (&rest bindings)
- `(progn ,@(loop for binding :in bindings :collect `(defmultikey ,@binding))))
-
-(defmultikeys
- (isk-l-á "M-a" "'a")
- (isk-u-Á "M-A" "'A")
- (isk-l-é "M-e" "'e")
- (isk-u-É "M-E" "'E")
- (isk-l-í "M-i" "'i")
- (isk-u-Í "M-I" "'I")
- (isk-l-ó "M-o" "'o")
- (isk-u-Ó "M-O" "'O")
- (isk-l-ö "M-m" "\"o")
- (isk-u-Ö "M-M" "\"O")
- (isk-l-ú "M-u" "'u")
- (isk-u-Ú "M-U" "'U")
- (isk-l-ý "M-y" "'y")
- (isk-u-Ý "M-Y" "'Y")
- (isk-l-þ "M-t" "th")
- (isk-u-Þ "M-T" "TH")
- (isk-l-đ "M-d" "dh")
- (isk-u-Đ "M-D" "DH")
- (isk-l-æ "M-h" "ae")
- (isk-u-Æ "M-H" "AE"))
-
-
-(defcommand thinkpad-ret () ()
- (send-key (kbd "RET")))
-
-(defcommand thinkpad-bs () ()
- (send-key (kbd "BackSpace")))
-
-
-;;;; Key Mapping --------------------------------------------------------------
-;;; Conventions:
-;;;
-;;; * Hyper-dir: move focus
-;;; * Hyper-Shift-dir: move window
-;;; * Hyper-Shift-Control-dir: swap window
-;;; * Hyper-F*: hardware
-;;; * Shift-F*: timers
-;;; * Hyper-Super-*: layout
-;;; * Hyper-*: miscellaneous
-
-(defmacro define-top-keys (&body keyforms)
- `(progn ,@(loop :for form :in keyforms
- :collect `(define-key *top-map*
- (kbd ,(first form))
- ,(second form)))))
-
-
-(define-top-keys ;; miscellaneous
- ("H-m" "terminal")
- ("H-M" "mark")
- ("H-SunPageUp" "st-font-up")
- ("H-SunPageDown" "st-font-down")
- ("H-Home" "st-font-reset")
- ("H-F4" "switch-yubikeys")
- ("H-\\" "pass-personal")
- ("H-|" "generate-password")
- ("s-1" "pass-um-1")
- ("s-2" "pass-um-2")
- ("H-b" "browser")
- ("H-O" "spotify")
- ("H-o" "files")
- ("H-z" "zoom")
- ("H-Z" "toggle-zoom-mute")
- ("C-H-Z" "end-zoom")
- ("F26" "prev")
- ("S-F26" "next")
- ("H-q" "exec lock-screen")
- ("H-y" "screenshot")
- ("H-g" "gcontrol")
- ("H-f" "save-fucked-screenshot")
- ("H-F" "delete-fucked-screenshot")
- ("H-R" "rstudio")
- ("H-r" "rain")
- ("H-V" "vlc")
- ("H-e" "budget")
- ("H-E" "spend")
- ("C-BackSpace" "clear-notifications")
- )
-
-(define-top-keys ;; clipboard
- ("H-c" "show-clipboard-history")
- ("H-C" "clear-clipboard-history")
- ("H-u" "generate-random-uuid")
- ("H-B" "bee-movie-script")
- ("M-H-u" "urlize-jira-issue"))
-
-(define-top-keys ;; movement
- ("H-h" "move-focus* left")
- ("H-j" "move-focus down")
- ("H-k" "move-focus up")
- ("H-l" "move-focus* right")
-
- ("H-H" "move-window left")
- ("H-J" "move-window down")
- ("H-K" "move-window up")
- ("H-L" "move-window right")
-
- ("H-1" "gselect 1")
- ("H-2" "gselect 2")
- ("H-3" "gselect 3")
- ("H-4" "gselect 4")
- ("H-5" "gselect 5")
- ("H-6" "gselect 6")
-
- ("KP_End" "gselect 1")
- ("KP_Down" "gselect 2")
- ("KP_Page_Down" "gselect 3")
- ("KP_Left" "gselect 4")
- ("KP_Begin" "gselect 5")
- ("KP_Right" "gselect 6")
- ("KP_Home" "gselect 7")
- ("KP_Up" "gselect 8")
- ("KP_Page_Up" "gselect 9")
-
- ("H-!" "gmove 1")
- ("H-@" "gmove 2")
- ("H-#" "gmove 3")
- ("H-$" "gmove 4")
- ("H-%" "gmove 5")
- ("H-^" "gmove 6")
-
- ("C-H-H" "exchange-direction left")
- ("C-H-J" "exchange-direction down")
- ("C-H-K" "exchange-direction up")
- ("C-H-L" "exchange-direction right")
-
- ("H-`" "next")
- ("S-H-`" "prev")
- ("H-n" "next-in-frame")
- ("H-p" "prev-in-frame")
- ("H-N" "pull-hidden-next")
- ("H-P" "papers")
-
- ("H-," "pull-from-windowlist"))
-
-
-(define-top-keys ;; splitting
- ("H-s" "sane-vsplit")
- ("H-v" "sane-hsplit")
- ("H-=" "balance-frames"))
-
-(define-top-keys ;; killing
- ("H-w" "delete")
- ("H-W" "kill")
- ("H-BackSpace" "remove")
- ("S-H-BackSpace" "kill-and-remove"))
-
-(define-top-keys ;; broken thinkpad keys
- ("s-m" "thinkpad-ret")
- ("s-Delete" "thinkpad-bs"))
-
-
-(define-top-keys ;; naming
- ("H-'" "title"))
-
-(define-top-keys ;; sound
- ("H-F1" "mute")
- ("H-F2" "volume-down")
- ("H-F3" "volume-up")
- ("XF86AudioMute" "exec mute")
- ("XF86AudioRaiseVolume" "volume-down") ; todo unfuck the backwards mapping in qmk
- ("XF86AudioLowerVolume" "volume-up"))
-
-(define-top-keys ;; screen
- ("H-F5" "rotate-brightness-down")
- ("H-F6" "rotate-brightness-up")
- ("H-F7" "screen-laptop")
- ("H-F8" "screen-external"))
-
-(define-top-keys ;; layout
- ("s-H-o" "only")
- ("s-H-t" "restore-from-file thirds")
- ("s-H-m" "restore-from-file dev")
- ("s-H-s" "restore-from-file streaming")
- ("s-H-w" "restore-from-file work")
- ("s-H-z" "restore-from-file zoom"))
-
-(define-top-keys ;; timers
- ("s-F7" "tea-timer")
- ("s-F9" "run-pop-timer")
- ("s-F8" "set-pop-timer")
- ("s-p" "posture-start")
- ("s-P" "posture-stop")
- ("s-y" "posture-answer-yes")
- ("s-h" "posture-answer-meh")
- ("s-n" "posture-answer-no")
- ("s-\\" "posture-toggle-pause")
- ("s-o" "posture-snooze"))
-
-(define-top-keys ;; stump
- ("Pause" "terminal") ; jesus christ
- ("H-F9" "sleep-machine")
- ("H-F10" "toggle-stumptray")
- ("H-F11" "toggle-current-mode-line")
- ("H-F12" "refresh-heads"))
-
-
-;; (stumpwm::unbind-remapped-keys)
-(define-remapped-keys
- '(("st-256color"
- ("s-c" . "C-C")
- ("s-v" . "C-V")
- ("C-=" . "S-C-SunPageUp")
- ("C--" . "S-C-SunPageDown")
- ("C-0" . "S-C-Home"))
- ("(firefox|Google-chrome|Chromium-browser)"
- ("s-[" . "C-S-Tab")
- ("s-]" . "C-Tab")
- ("C-a" . "Home")
- ("C-e" . "End")
- ;; I always try to hit ctrl-d to kill a browser window because I'm so used
- ;; to terminal windows, and it ends up bookmarking the damn page. In the
- ;; interest of not having a random collection of bookmarks grow over time,
- ;; I'll just add a mapping to compensate for my stupid brain.
- ("C-d" . "C-w")
- ;; todo debug why this breaks a really fast C-a-k roll
- ;; ("C-a" . "Home")
- ;; ("C-e" . "End")
- ("s-a" . "C-a")
- ("s-d" . "C-d")
- ("s-l" . "C-l")
- ("s-t" . "C-t")
- ("s-w" . "C-w")
- ("s-r" . "C-r")
- ("s-f" . "C-f")
- ("s-z" . "C-z")
- ("s-x" . "C-x")
- ("s-c" . "C-c")
- ("s-v" . "C-v"))
- (""
- ("s-z" . "C-z")
- ("s-x" . "C-x")
- ("s-c" . "C-c")
- ("s-v" . "C-v"))))
-
-
-;;;; Sensors ------------------------------------------------------------------
-(defun ? (obj &rest keys)
- (if (null keys)
- obj
- (apply #'? (etypecase obj
- (hash-table (gethash (first keys) obj)))
- (rest keys))))
-
-(defun parse-sensors ()
- ;; sensors -j is stupid and will output errors before the actual output on
- ;; standard out, instead of putting them on standard err like a reasonable
- ;; program, e.g.:
- ;;
- ;; ERROR: Can't get value of subfeature temp1_input: Can't read
- ;; {
- ;; "iwlwifi_1-virtual-0":{ … },
- ;; …
- ;;
- ;; So we'll have to drop the `ERROR` lines before we can get to the actual
- ;; goddamn JSON. UNIX programs are so great.
- (let ((s (losh:sh '("sensors" "-j") :result-type 'stream)))
- (loop :while (char= #\E (peek-char nil s)) :do (read-line s))
- (jarl:read t s)))
-
-(defparameter *sensors-refresh-delay* 5.0 "How long between sensor refreshes (in seconds).")
-(defparameter *sensors-next-refresh* nil)
-(defparameter *sensors-cache* nil)
-
-(defun sensors% (&aux (sensors (parse-sensors)))
- (hostcase
- (:ouroboros (format nil "[CPU ~D°C] [GPU ~D°C ~D°C ~D°C]"
- (round (? sensors "nct6779-isa-0290" "CPUTIN" "temp2_input"))
- (round (? sensors "amdgpu-pci-4500" "edge" "temp1_input"))
- (round (? sensors "amdgpu-pci-4500" "junction" "temp2_input"))
- (round (? sensors "amdgpu-pci-4500" "mem" "temp3_input"))))
- ((:gro :juss) (format nil "[CPU ~D°C] [GPU ~D°C]"
- (round (? sensors "thinkpad-isa-0000" "CPU" "temp1_input"))
- (round (? sensors "amdgpu-pci-0600" "edge" "temp1_input"))))
- (t "?")))
-
-(defun sensors (&aux (now (get-internal-real-time)))
- (if (or (null *sensors-next-refresh*)
- (>= now *sensors-next-refresh*))
- (setf *sensors-next-refresh* (+ now (* internal-time-units-per-second *sensors-refresh-delay*))
- *sensors-cache* (sensors%))
- *sensors-cache*))
-
-(defun sensors-modeline (ml)
- (declare (ignore ml))
- (sensors))
-
-(add-screen-mode-line-formatter #\S #'sensors-modeline)
-
-
-;;;; Modeline -----------------------------------------------------------------
-(load-module "battery-portable")
-(load-module "cpu")
-(load-module "hostname")
-(load-module "mem")
-
-(defun ensure-mode-line ()
- (when (not (stumpwm::head-mode-line (current-head)))
- (toggle-mode-line (current-screen) (current-head))))
-
-
-(defun configure-modeline ()
- (setf
- *time-modeline-string*
- "%a %b %e %H:%M"
-
- cpu::*cpu-usage-modeline-fmt*
- "^[~A~3D%^]"
-
- cpu::*cpu-modeline-fmt*
- "[%c] [%f]"
-
- mem::*mem-modeline-fmt*
- "%b"
-
- *screen-mode-line-format*
- (append
- (list "[^B"
- '(:eval (princ-to-string (group-number (current-group))))
- ":%n^b@%h] %W^>")
-
- #+todo-some-day (list ;; "(V "
- ;; ;; '(:eval (volume))
- ;; ")"
- " ")
-
- ;; battery and brightness for laptops
- (hostcase
- ((:gro :juss)
- '("(B %B)"
- " (BR "
- (:eval (princ-to-string (brightness)))
- "%)")))
-
- ;; temp, cpu, mem, time, tray
- #+no (list "(TEMP %S) (CPU %C) (MEM %M) %d %T")
- (list "(CPU %C) (MEM %M) %d %T")
- ))
-
- (setf *mode-line-timeout* 10)
- (setf *mode-line-background-color* "#111111")
-
- (ensure-mode-line)
- )
-
-(configure-modeline)
-
-
-;;;; System Tray --------------------------------------------------------------
-(load-module "stumptray")
-(defvar *tray-loaded* (run-commands "stumptray"))
-
-
-;;;; Unbreak Typing -----------------------------------------------------------
-(defun stumpwm::input-insert-hyphen-or-space (input key)
- (declare (ignore key))
- (input-insert-char input #\space))
-
-
-;;;; Startup ------------------------------------------------------------------
-;; (defvar *dropbox*
- ;; (run-shell-command "~/.dropbox-dist/dropboxd"))
-
-(defvar *dunst*
- (run-shell-command "/usr/bin/dunst -conf ~/.dunstrc"))
-
-(when (probe-file "/home/sjl/.stumpwmrc.local")
- (load "/home/sjl/.stumpwmrc.local"))
-
-
-#;;; Scratch ------------------------------------------------------------------
-
-(group-number (current-group))
-
-(message "~D" *terminal-font-size*)
--- a/vim/custom-dictionary.utf-8.add Mon Mar 18 12:49:56 2024 -0400
+++ b/vim/custom-dictionary.utf-8.add Tue Mar 19 13:55:42 2024 -0400
@@ -429,3 +429,7 @@
trimethylated
BIOINF
astrocyte
+Armis
+Taubman
+H3K27M
+polycomb
--- a/vim/vimrc Mon Mar 18 12:49:56 2024 -0400
+++ b/vim/vimrc Tue Mar 19 13:55:42 2024 -0400
@@ -563,6 +563,7 @@
au BufNewFile,BufRead *.paren set filetype=lisp
au BufNewFile,BufRead .abclrc set filetype=lisp
au BufNewFile,BufRead .lisprc set filetype=lisp
+ au BufNewFile,BufRead stumpwmrc set filetype=lisp
au BufNewFile,BufRead .stumpwmrc set filetype=lisp
au BufNewFile,BufRead .stumpwmrc.local set filetype=lisp