mirror of
https://github.com/Gavinok/stump-conf.git
synced 2026-09-10 07:26:30 -04:00
442 lines
14 KiB
Common Lisp
442 lines
14 KiB
Common Lisp
;;; -*- mode: lisp; -*-
|
|
(in-package :stumpwm)
|
|
|
|
(defun tr-define-key (key command)
|
|
(define-key *top-map* (kbd (concat "s-" key )) command)
|
|
(define-key *root-map* (kbd key) command))
|
|
|
|
;; path to modules
|
|
;; git clone git@github.com:stumpwm/stumpwm-contrib.git ~/.config/stumpwm/modules
|
|
(init-load-path #p"~/.config/stumpwm/modules/")
|
|
|
|
;;; Helper Functions
|
|
(defun file-readable-p (file)
|
|
"Return t, if FILE is available for reading."
|
|
(handler-case
|
|
(with-open-file (f file)
|
|
(read-line f))
|
|
(stream-error () nil)))
|
|
|
|
(defun executable-p (name)
|
|
"Tell if given executable is present in PATH."
|
|
(let ((which-out (string-trim '(#\ #\linefeed) (run-shell-command (concat "which " name) t))))
|
|
(unless (string-equal "" which-out) which-out)))
|
|
|
|
;;; Theme
|
|
(setf *colors*
|
|
'("#000000" ;black
|
|
"#BF6262" ;red
|
|
"#a1bf78" ;green
|
|
"#dbb774" ;yellow
|
|
"#7D8FA3" ;blue
|
|
"#ff99ff" ;magenta
|
|
"#53cdbd" ;cyan
|
|
"#b7bec9")) ;white
|
|
|
|
(setf *default-bg-color* "#e699cc")
|
|
|
|
(update-color-map (current-screen))
|
|
|
|
(setf *window-format* "%m%s%50t")
|
|
|
|
;;; Basic Settings
|
|
(setf *mode-line-background-color* (car *colors*)
|
|
*mode-line-foreground-color* (car (last *colors*))
|
|
*mode-line-timeout* 1)
|
|
|
|
(setf *message-window-gravity* :center
|
|
*input-window-gravity* :center
|
|
*window-border-style* :thin
|
|
*message-window-padding* 3
|
|
*maxsize-border-width* 2
|
|
*normal-border-width* 2
|
|
*transient-border-width* 2
|
|
stumpwm::*float-window-border* 1
|
|
stumpwm::*float-window-title-height* 1)
|
|
|
|
;; Focus Follow Mouse
|
|
(setf *mouse-focus-policy* :sloppy)
|
|
|
|
;;; Completion
|
|
;; ;; Show all completions from start
|
|
;; (setf *input-completion-show-empty* nil)
|
|
;; ;; keep completions open even when one is selected
|
|
;; (setf *input-completion-style* (make-input-completion-style-unambiguous))
|
|
(setf *input-window-gravity* :center
|
|
;; TODO determin why this appears above
|
|
*message-window-input-gravity* :left)
|
|
|
|
(setf *input-completion-show-empty* t)
|
|
|
|
;; Remember commands and offers orderless completion
|
|
;; https://github.com/landakram/stumpwm-prescient
|
|
(ql:quickload :stumpwm-prescient)
|
|
(setf *input-refine-candidates-fn* 'stumpwm-prescient:refine-input)
|
|
|
|
;;; Startup Commands
|
|
(run-shell-command "xsetroot -cursor_name left_ptr")
|
|
|
|
;;; Bindings
|
|
(set-prefix-key (kbd "XF86Tools"))
|
|
|
|
;; General Top Level Bindings
|
|
(define-key *top-map* (kbd "s-n") "pull-hidden-next")
|
|
(define-key *top-map* (kbd "s-p") "pull-hidden-previous")
|
|
(define-key *top-map* (kbd "s-TAB") "fnext")
|
|
(define-key *top-map* (kbd "s-ISO_Left_Tab") "fprev")
|
|
|
|
(setf *resize-increment* 25)
|
|
(tr-define-key "f" "fullscreen")
|
|
(tr-define-key "q" "only")
|
|
(tr-define-key "=" "exec menu_connection_manager.sh")
|
|
(tr-define-key "X" "exec power_menu.sh")
|
|
(tr-define-key "P" "exec clipmenu")
|
|
(tr-define-key "d" "exec dmenu_run")
|
|
(tr-define-key "RET" "exec emacsclient -c -a 'emacs'")
|
|
(tr-define-key "D" "exec discord")
|
|
|
|
;; Window Movement
|
|
(dyn-blacklist-command "move-window")
|
|
(define-key *top-map* (kbd "s-H") "move-window left")
|
|
(define-key *top-map* (kbd "s-J") "move-window down")
|
|
(define-key *top-map* (kbd "s-K") "move-window up")
|
|
(define-key *top-map* (kbd "s-L") "move-window right")
|
|
|
|
;;; Volume Stuff
|
|
(let ((vdown "exec cm down 5")
|
|
(vup "exec cm up 5")
|
|
(m *top-map*))
|
|
(define-key m (kbd "s-C-a") vdown)
|
|
(define-key m (kbd "XF86AudioLowerVolume") vdown)
|
|
(define-key m (kbd "s-C-f") vup)
|
|
(define-key m (kbd "XF86AudioRaiseVolume") vup))
|
|
|
|
;;; Brightness
|
|
(when *initializing*
|
|
(defconstant backlightfile "/sys/class/backlight/intel_backlight/brightness"))
|
|
|
|
;; Xbacklight broak so I made this
|
|
(defcommand brighten (val) ((:number "Change brightness by: "))
|
|
(with-open-file (fp backlightfile
|
|
:if-exists :overwrite
|
|
:direction :io)
|
|
(write-sequence (write-to-string (+ (parse-integer (read-line fp nil)) val))
|
|
fp)))
|
|
|
|
(let (;; If xbacklight doesn't work use this (requires special file permissions)
|
|
;; (bdown "brighten -1000")
|
|
;; (bup "brighten 1000")
|
|
(bdown "exec xbacklight -dec 10")
|
|
(bup "exec xbacklight -inc 10")
|
|
(m *top-map*))
|
|
(define-key m (kbd "s-C-s") bdown)
|
|
(define-key m (kbd "XF86MonBrightnessDown") bdown)
|
|
(define-key m (kbd "s-C-d") bup)
|
|
(define-key m (kbd "XF86MonBrightnessUp") bup))
|
|
|
|
;;; General Root Level Bindings
|
|
(defcommand term (&optional prg) ()
|
|
(run-shell-command (if prg
|
|
(format nil "st -e ~A" prg)
|
|
"st")))
|
|
(define-key *root-map* (kbd "c") "term")
|
|
(define-key *root-map* (kbd "C-c") "term")
|
|
(define-key *root-map* (kbd "y") "eval (term \"cm\")")
|
|
(define-key *root-map* (kbd "w") "exec ducksearch")
|
|
|
|
(define-key *root-map* (kbd "b") "pull-from-windowlist")
|
|
(define-key *root-map* (kbd "r") "remove")
|
|
(define-key *root-map* (kbd "R") "iresize")
|
|
(define-key *root-map* (kbd "f") "fullscreen")
|
|
(define-key *root-map* (kbd "Q") "quit-confirm")
|
|
|
|
(define-key *root-map* (kbd "M-o") "fnext")
|
|
(define-key *root-map* (kbd "SPC") "exec cabl -c")
|
|
;; more usful alternatives to i and I
|
|
(define-key *root-map* (kbd "i") "show-window-properties")
|
|
(define-key *root-map* (kbd "I") "list-window-properties")
|
|
|
|
;;; Groups
|
|
(grename "main")
|
|
(gnewbg ".trash") ;hidden group
|
|
|
|
;; Don't jump between groups when switching apps
|
|
(setf *run-or-raise-all-groups* nil)
|
|
(define-key *groups-map* (kbd "d") "gnew-dynamic")
|
|
(define-key *groups-map* (kbd "s") "gselect")
|
|
|
|
(load-module "globalwindows")
|
|
(define-key *groups-map* (kbd "b") "global-pull-windowlist")
|
|
|
|
|
|
;;;; Hide and Show Windows
|
|
(defun window-menu-format (w)
|
|
(list (format-expand *window-formatters* *window-format* w) w))
|
|
|
|
(defun window-from-menu (windows)
|
|
(when windows
|
|
(second (select-from-menu
|
|
(group-screen (window-group (car windows)))
|
|
(mapcar 'window-menu-format windows)
|
|
"Select Window: "))))
|
|
|
|
(defun windows-in-group (group)
|
|
(group-windows (find group (screen-groups (current-screen))
|
|
:key 'group-name :test 'equal)))
|
|
|
|
(defcommand pull-from-trash () ()
|
|
(let* ((windows (windows-in-group ".trash"))
|
|
(window (window-from-menu windows)))
|
|
(when window
|
|
(move-window-to-group window (current-group))
|
|
(stumpwm::pull-window window))))
|
|
|
|
(defcommand move-to-trash () ()
|
|
(stumpwm:run-commands "gmove .trash"))
|
|
|
|
(tr-define-key "]" "move-to-trash")
|
|
(tr-define-key "[" "pull-from-trash")
|
|
|
|
|
|
;;; Floating Windows
|
|
;; slop taken from https://github.com/lepisma/cfg
|
|
(defun floatingp (window)
|
|
"Return T if WINDOW is floating and NIL otherwise"
|
|
(typep window 'stumpwm::float-window))
|
|
|
|
(defun slop-get-pos ()
|
|
(mapcar #'parse-integer (ppcre:split "[^0-9]" (run-shell-command "slop -f \"%x %y %w %h\"" t))))
|
|
|
|
(defun slop ()
|
|
"Slop the current window or just float if slop cli not present."
|
|
(if (executable-p "slop")
|
|
(let ((win (current-window))
|
|
(group (current-group))
|
|
(screen (current-screen))
|
|
(pos (slop-get-pos)))
|
|
(stumpwm::float-window win group)
|
|
(stumpwm::float-window-move-resize win
|
|
:x (nth 0 pos)
|
|
:y (nth 1 pos)
|
|
:width (nth 2 pos)
|
|
:height (nth 3 pos))
|
|
;; TODO use method from toggle-always-on-top instead
|
|
(stumpwm::always-show-window win screen))))
|
|
|
|
(defcommand toggle-slop-this () ()
|
|
(let ((win (current-window))
|
|
(group (current-group))
|
|
(screen (current-screen)))
|
|
(if (floatingp win)
|
|
(progn (stumpwm::disable-always-show-window win screen)
|
|
(stumpwm::unfloat-window win group))
|
|
(slop))))
|
|
|
|
(tr-define-key "z" "toggle-slop-this")
|
|
|
|
;;; Splits
|
|
(defcommand hsplit-and-focus () ()
|
|
"create a new frame on the right and focus it."
|
|
(hsplit)
|
|
(move-focus :right))
|
|
|
|
(defcommand vsplit-and-focus () ()
|
|
"create a new frame below and focus it."
|
|
(vsplit)
|
|
(move-focus :down))
|
|
(define-key *root-map* (kbd "v") "hsplit-and-focus")
|
|
(define-key *root-map* (kbd "s") "vsplit-and-focus")
|
|
(loop :for i :in '("hsplit-and-focus"
|
|
"vsplit-and-focus")
|
|
:do (dyn-blacklist-command i))
|
|
|
|
;;; Mode-Line
|
|
(load-module "battery-portable")
|
|
|
|
;; Get Fit
|
|
(defvar *reps* 0
|
|
"Variable for keeping track of reps")
|
|
(defcommand add-reps (reps) ((:number "Enter reps: "))
|
|
(when reps
|
|
(setq *reps* (+ *reps* reps))))
|
|
(defcommand reset-reps () ()
|
|
(setq *reps* 0))
|
|
|
|
(defvar *gym-map*
|
|
(let ((m (make-sparse-keymap)))
|
|
(define-key m (kbd "a") "add-reps")
|
|
(define-key m (kbd "r") "reset-reps")
|
|
m))
|
|
(define-key *root-map* (kbd "ESC") '*gym-map*)
|
|
|
|
(setf *time-modeline-string* "%a, %b %d %I:%M%p")
|
|
(setf *screen-mode-line-format*
|
|
(list
|
|
;; Groups
|
|
" ^7[^B^4%n^7^b] "
|
|
;; Pad to right
|
|
"^>"
|
|
'(:eval (when (> *reps* 0)
|
|
(format nil "^1^B(Reps ~A)^n " *reps*)))
|
|
;; Date
|
|
"^7"
|
|
"%d"
|
|
;; Battery
|
|
" ^7[^n%B^7]^n "))
|
|
|
|
(defun enable-mode-line-everywhere ()
|
|
(loop for screen in *screen-list* do
|
|
(loop for head in (screen-heads screen) do
|
|
(enable-mode-line screen head t))))
|
|
(enable-mode-line-everywhere)
|
|
;; turn on/off the mode line for the current head only.
|
|
(define-key *top-map* (kbd "s-B") "mode-line")
|
|
|
|
;;; Gaps
|
|
(load-module "swm-gaps")
|
|
(setf swm-gaps:*inner-gaps-size* 13
|
|
swm-gaps:*outer-gaps-size* 7
|
|
swm-gaps:*head-gaps-size* 7)
|
|
(when *initializing*
|
|
(swm-gaps:toggle-gaps))
|
|
(define-key *groups-map* (kbd "g") "toggle-gaps")
|
|
|
|
;;; Remaps
|
|
(define-remapped-keys
|
|
'(("(discord|Element|Google-chrome)"
|
|
("C-a" . "Home")
|
|
("C-e" . "End")
|
|
("C-n" . "Down")
|
|
("C-p" . "Up")
|
|
("C-f" . "Right")
|
|
("C-b" . "Left")
|
|
("C-v" . "Next")
|
|
("M-v" . "Prior")
|
|
("M-w" . "C-c")
|
|
("C-w" . "C-x")
|
|
("C-y" . "C-v")
|
|
("M-<" . "Home")
|
|
("M->" . "End")
|
|
("C-M-b" . "M-Left")
|
|
("C-M-f" . "M-Right")
|
|
("M-f" . "C-Right")
|
|
("M-b" . "C-Left")
|
|
("C-s" . "C-f")
|
|
("C-j" . "C-k")
|
|
("C-/" . "C-z")
|
|
("C-k" . ("C-S-End" "C-x"))
|
|
("C-d" . "Delete"))))
|
|
|
|
;;; Undo And Redo Functionality
|
|
(load-module "winner-mode")
|
|
(define-key *root-map* (kbd "u") "winner-undo")
|
|
(define-key *root-map* (kbd "C-r") "winner-redo")
|
|
(add-hook *post-command-hook* (lambda (command)
|
|
(when (member command winner-mode:*default-commands*)
|
|
(winner-mode:dump-group-to-file))))
|
|
|
|
;;; Emacs integration
|
|
(defcommand emacs () () ; override default emacs command
|
|
"Start emacs if emacsclient is not running and focus emacs if it is
|
|
running in the current group"
|
|
(run-or-raise "emacsclient -c -a 'emacs'" '(:class "Emacs")))
|
|
;; Treat emacs splits like Xorg windows
|
|
(defun is-emacs-p (win)
|
|
"nil if the WIN"
|
|
(when win
|
|
(string-equal (window-class win) "Emacs")))
|
|
|
|
(defun exec-el (expression)
|
|
"execute emacs lisp do not collect it's output"
|
|
(run-shell-command (concat "emacsclient -e '"
|
|
(write-to-string expression) "'")
|
|
nil))
|
|
|
|
(defun eval-string-as-el (expression)
|
|
"evaluate a string as emacs lisp"
|
|
(run-shell-command (concat "emacsclient -e '" expression "'") t))
|
|
|
|
(defmacro eval-el (expression)
|
|
"evaluate emacs lisp and collect it's output"
|
|
`(eval-string-as-el (write-to-string (quote ,expression))))
|
|
|
|
(defun emacs-winmove (direction)
|
|
"executes the emacs function winmove-DIRECTION where DIRECTION is a string"
|
|
(eval-string-as-el (concat "(windmove-" direction ")")))
|
|
|
|
(defun better-move-focus (ogdir)
|
|
"Similar to move-focus but also treats emacs windows as Xorg windows"
|
|
(declare (type (member :up :down :left :right) ogdir))
|
|
(flet ((mv () (move-focus ogdir)))
|
|
(if (is-emacs-p (current-window))
|
|
(when
|
|
;; There is not emacs window in that direction
|
|
(= (length (emacs-winmove
|
|
(string-downcase (string ogdir))))
|
|
1)
|
|
(mv))
|
|
(mv))))
|
|
|
|
(defcommand my-mv (dir) ((:direction "Enter direction: "))
|
|
(when dir (better-move-focus dir)))
|
|
|
|
(define-key *top-map* (kbd "s-h") "my-mv left")
|
|
(define-key *top-map* (kbd "s-j") "my-mv down")
|
|
(define-key *top-map* (kbd "s-k") "my-mv up")
|
|
(define-key *top-map* (kbd "s-l") "my-mv right")
|
|
|
|
;;; Plumber Concept
|
|
(defun xdg-open (x)
|
|
(run-shell-command (concat "xdg-open " x) nil))
|
|
|
|
(defvar regex-cmds '((".*\.(png|jpeg|mp4|mkv)" xdg-open)
|
|
("testing" (print "never hello"))
|
|
("never" echo)
|
|
("hello" "notify-send")))
|
|
|
|
(defvar fallback-cmds '("tts.sh"
|
|
"xdg-open"))
|
|
|
|
(defun plumber-select (item)
|
|
(let ((selected (select-from-menu (current-screen) fallback-cmds "Select CMD: ")))
|
|
(plumber-handle-tools (car selected) item)))
|
|
|
|
(defgeneric plumber-handle-tools (tool arg)
|
|
(:method ((tool symbol) (arg string))
|
|
(funcall (symbol-function tool) arg))
|
|
(:method ((tool string) (arg string))
|
|
(run-shell-command (concat tool " " arg)))
|
|
(:method ((tool cons) (arg string))
|
|
(eval tool))
|
|
(:method ((tool t) (arg string))
|
|
(plumber-select arg)))
|
|
|
|
(defun plumber (&optional replacement-clip)
|
|
"An attempt at emulating plan9's plumber but in common lisp
|
|
using REGEX-CMDS it will match the first argument against the clipboard using regex
|
|
if the result matches then the 2nd argument is ran as a command with the clipboard as an argument,
|
|
if the match is a lisp symbol it will call the function with the clipboard as an argument
|
|
if the match is a string the the string will be concatenated with a space and the clipboard and ran as a shell command
|
|
if the match is a cons cell it is assumed to be a lambda and will execute call the lambda with the clipboard as an argument"
|
|
(let* ((clip (if replacement-clip
|
|
replacement-clip
|
|
(uiop:run-program '("xsel") :output :string)))
|
|
(found (find clip
|
|
regex-cmds
|
|
:key 'car
|
|
:test (lambda (clp cur) (ppcre:scan cur clp)))))
|
|
|
|
(if found
|
|
(plumber-handle-tools (second found) clip)
|
|
(plumber-select clip))))
|
|
|
|
(defun plumber-test ()
|
|
(plumber)
|
|
(plumber "/home/gavinok/Pictures/city.png"))
|
|
|
|
(defcommand connect-to-sly () ()
|
|
(unless *stumpwm-slynk-session*
|
|
(start-slynk))
|
|
(exec-el '(sly-connect "localhost" "4005"))
|
|
(emacs))
|