diff --git a/org/config/stumpwm.org b/org/config/stumpwm.org new file mode 100644 index 0000000..3cf9139 --- /dev/null +++ b/org/config/stumpwm.org @@ -0,0 +1,972 @@ +#+TITLE: StumpWM config +#+setupfile: headers +#+OPTIONS: auto-id:t +#+HTML_HEAD_EXTRA: +#+HTML_HEAD_EXTRA: +#+HTML_HEAD_EXTRA: + +* Introduction +:PROPERTIES: +:CUSTOM_ID: Introduction-9vda1z81u5j0 +:END: +** What is StumpWM? +:PROPERTIES: +:CUSTOM_ID: Introduction-What-is-StumpWM-oyycyb91u5j0 +:END: +[[https://stumpwm.github.io/][StumpWM]] is a tiling window manager inheriting from [[http://www.nongnu.org/ratpoison/][RatPoison]], written +entirely in [[https://common-lisp.net/][Common Lisp]] and compiled with [[http://www.sbcl.org/][SBCL]]. While it is not an +dynamic tiling window manager like [[file:awesome.org][Awesome]] is, its ability of managing +windows in frames and using keychords with keymaps like Emacs does is +a huge plus for me, not to mention the fact its configuration file is +written in Common Lisp, a general programming language, a bit like +Awesome. This makes it an [[file:Deprecated/i3.org][i3]] on steroids, sort of. It also uses a lot +of Emacs’ concepts, which is great for an Emacs user such as myself. + +** Why not EXWM then? +:PROPERTIES: +:CUSTOM_ID: Introduction-Why-not-EXWM-then-670dyb91u5j0 +:END: +Sometimes, some actions within Emacs are blocking actions, making the +computer not usable while the command runs. It also does not play nice +with video games (pun intended), which is also a negative point for +me. And I also find EXWM more confusing overall than StumpWM. + +** What this file is for +:PROPERTIES: +:CUSTOM_ID: Introduction-What-this-file-is-for-pnyg92a1u5j0 +:END: +This file has two main goals: +- This will be the actual source code of my StumpWM + configuration, thanks to Emacs’ org-mode, and thanks to org-mode’s + literate config capabilities. + + Almost all of the visible source blocks if not all will be included + in my configuration files through tangling, which can be done in + Emacs when this file is opened through ~M-x org-babel-tangle~, which + will write my configuration files based on the source blocks present + in this document. This file is not only my config’s documentation, + it /*is*/ my configuration. +- Be my documentation on my StumpWM configuration. That way, I’ll + never forget which block of code does what. + + And maybe, hopefully, someone could learn a thing or two if they + want to get into StumpWM but don’t know where to begin. You should + be able to read this document as a book, with each chapter dedicated + to a different aspect of StumpWM.a + +** Organization of my files +:PROPERTIES: +:CUSTOM_ID: Introduction-Organization-of-my-files-40vjne91u5j0 +:END: +While I could make this file write everything to the same file (the +actual source will be in a single file after all), I find it easier to +debug StumpWM if everything’s split up. For now, my configuration +follows this architecture: +- ~init.el~ :: My main configuration file, glues everything together. It + loads all of my configuration files as well as some modules I find + useful; + +- ~colors.lisp~ :: In this file are defined colors that will be used in + common in my ~theme.lisp~ and ~modeline.lisp~ files. Let’s make my code + DRY, or as I prefer to say, DRYD (/Don’t Repeat Yourself Dummy/). +- ~commands.lisp~ :: Lisp commands, in case I want to bind some + complicated actions to a keybind that is not just a simple shell + command; +- ~keybindings.lisp~ :: My list of keymaps and keybinds which make + StumpWM actually usable; +- ~modeline.lisp~ :: This defines the modeline, a concept taken from + Emacs which can display various information such as a list of + workspaces, including the current one; +- ~placement.lisp~ :: This file manages my workspaces and the default + placement of various windows; +- ~theme.lisp~ :: manages the color theme of StumpWM, the default + placement of some windows and StumpWM’s gaps. + +* Init file +:PROPERTIES: +:CUSTOM_ID: Init-file-l3q4snd1u5j0 +:header-args:lisp: :mkdirp :tangle ~/.stumpwm.d/init.lisp +:END: +As mentioned in [[https://stumpwm.github.io/git/stumpwm-git_1.html#Init-File][the documentation]], the configuration files can be in +different locations, but I chose an Emacs-like configuration: put +everything in ~~/.stumpwm.d/~. We begin by indicating quicklisp how to +properly initialize: +#+begin_src lisp + #-quicklisp + (let ((quicklisp-init (merge-pathnames "quicklisp/setup.lisp" + (user-homedir-pathname)))) + (when (probe-file quicklisp-init) + (load quicklisp-init))) +#+end_src + +Then, our first StumpWM-related code is declaring we are using the +~stumpwm~ package, and this is also our default package. This will allow +us to avoid using the prefix ~stumpwm:~ each time we are using a +function or a variable from this package. +#+begin_src lisp + (in-package :stumpwm) + (setf *default-package* :stumpwm) +#+end_src + + +Since I install StumpWM with my package manager (I use the AUR’s +~stumpwm-git~ package), StumpWM’s modules are installed to +~/usr/share/stupmwm/contrib/utils/~, let’s indicate that to StumpWM. +#+begin_src lisp + ;; (set-module-dir "/usr/share/stupmwm/contrib/utils/") + (set-module-dir "/usr/share/stupmwm/contrib/") + ;; (dolist (path '("/usr/share/stumpwm/contrib/utils/" + ;; "/usr/share/stumpwm/contrib/minor-mode" + ;; "/usr/share/stumpwm/contrib/modeline")) + ;; (add-to-load-path path)) +#+end_src + +A startup message can be used when initializing StumpWM. For now, +let’s set it to ~nil~. +#+begin_src lisp +(setf *startup-message* nil) +#+end_src + +The first thing I want to do after that is to set some decent cursor +pointer as well as get a bunch of stuff started. To see what’s in the +~autostart~ script, [[file:bin.org::#Autostart-a99e99e7][see here]]. +#+begin_src lisp + (run-shell-command "xsetroot -cursor_name left_ptr") + (run-shell-command "autostart") +#+end_src + +Now, we’ll load a couple of my custom files that will be described below: +#+name: first-loaded-files +| File to be loaded | +|-------------------| +| commands.lisp | +| placement.lisp | +| keybindings.lisp | +| theme.lisp | +| modeline.lisp | + +#+name: gen-load-files +#+headers: :tangle no :exports results :wrap src lisp :cache yes +#+begin_src emacs-lisp :var files=first-loaded-files + (mapconcat (lambda (file) + (format "(load \"~/.stumpwm.d/%s\")" (car file))) + files + "\n") +#+end_src + +This is equivalent to the Common Lisp code: +#+RESULTS[942558619eb0d0a3d694a7808d0b600f0bc4c14c]: gen-load-files +#+begin_src lisp +(load "~/.stumpwm.d/commands.lisp") +(load "~/.stumpwm.d/placement.lisp") +(load "~/.stumpwm.d/keybindings.lisp") +(load "~/.stumpwm.d/theme.lisp") +(load "~/.stumpwm.d/modeline.lisp") +#+end_src + +Once the modeline file is loaded, let’s indicate StumpWM to activate +it: +#+begin_src lisp + (when *initializing* + (mode-line)) +#+end_src + +Another thing I want to set is how focus is linked to my mouse: only +on click. I /HATE/ it when focus follows my mouse like some damn dog +after its ball. Also, the meta key will be used to move floating +windows. +#+begin_src lisp + (setf *mouse-focus-policy* :click + ,*float-window-modifier* :META) +#+end_src + +Next, some modules will be loaded from the ~stumpwm-contrib~ package +(which is included in ~stumpwm-git~). Here is a short list including a +short description of what they are for: +#+name: loaded-modules +| Module Name | Why It Is Loaded | +|------------------+------------------------------------------------------------| +| alert-me | Creates notifications, can also create timed notifications | +| battery-portable | Get information on the battery level of a laptop | +| beckon | Bring the mouse cursor to the current window | +| cpu | Get the CPU usage of the computer | +| end-session | Gracefully end programs when ending user session | +| globalwindows | Navigate between windows from all workspaces | +| mem | Get the memory usage of the computer | +| stump-backlight | Native management of backlight in StumpWM | +| urgentwindows | Get urgent windows | + +#+name: gen-load-modules +#+headers: :tangle no :exports results :wrap src lisp :cache yes +#+begin_src emacs-lisp :var modules=loaded-modules + (mapconcat (lambda (module) + (format "(load-module \"%s\")" (car module))) + modules + "\n") +#+end_src + +#+RESULTS[1978f17a99db4ca68780c378e5e5d9d58f9e08bc]: gen-load-modules +#+begin_src lisp +(load-module "alert-me") +(load-module "battery-portable") +(load-module "beckon") +(load-module "cpu") +(load-module "end-session") +(load-module "globalwindows") +(load-module "mem") +(load-module "stump-backlight") +(load-module "urgentwindows") +#+end_src + +Finally, we can notify the user everything is ready. +#+begin_src lisp + (setf *startup-message* "StumpWM is ready!") +#+end_src + +And it’s done! We can now move on to the creation of the other CLisp files. + +* Commands +:PROPERTIES: +:CUSTOM_ID: Commands-1wagy001v5j0 +:header-args:lisp: :mkdirp :tangle ~/.stumpwm.d/commands.lisp +:END: +This file is going to be short. The only two custom command I have is +for Firefox, in order to either invoke a new Firefox window, or raise +it if it already exists, and for Emacs to invoke the Emacs client or a +new Emacs instance if the server isn’t running. This is done like so: +#+begin_src lisp + (defcommand firefox () () + "Run or raise Firefox." + (run-or-raise "firefox" '(:class "Firefox") t nil)) +#+end_src + +And done, next! + +* Colors +:PROPERTIES: +:CUSTOM_ID: Colors-w5493d01v5j0 +:header-args:lisp: :mkdirp :tangle ~/.stumpwm.d/colors.lisp +:END: +If you’ve taken a look at the rest of my dotfiles, you may have +noticed I really like the [[https://www.nordtheme.com/][Nord theme]]. No wonder we can find it here +again! Here is a small table listing the Nord colors: +#+name: nord-colors +| Name | Value | +|--------+---------| +| nord0 | #2e3440 | +| nord1 | #3b4252 | +| nord2 | #434c5e | +| nord3 | #4c566a | +| nord4 | #d8dee9 | +| nord5 | #e5e9f0 | +| nord6 | #eceff4 | +| nord7 | #8fbcbb | +| nord8 | #88c0d0 | +| nord9 | #81a1c1 | +| nord10 | #5e81ac | +| nord11 | #bf616a | +| nord12 | #d08770 | +| nord13 | #ebcb8b | +| nord14 | #a3be8c | +| nord15 | #b48ead | + +I’ll prefix the variables’ name with ~phundrak-~ just in case it might +conflict with another package I might use in the future, so the CLisp +code looks like so: +#+name: gen-colors +#+headers: :tangle no :exports results :wrap src lisp :cache yes +#+begin_src emacs-lisp :var colors=nord-colors + (mapconcat (lambda (color) + (format "(defvar phundrak-%s \"%s\")" (car color) (cadr color))) + colors + "\n") +#+end_src + +#+RESULTS[08b3db7a2b4f31d641bcd096ff265eae06879244]: gen-colors +#+begin_src lisp +(defvar phundrak-nord0 "#2e3440") +(defvar phundrak-nord1 "#3b4252") +(defvar phundrak-nord2 "#434c5e") +(defvar phundrak-nord3 "#4c566a") +(defvar phundrak-nord4 "#d8dee9") +(defvar phundrak-nord5 "#e5e9f0") +(defvar phundrak-nord6 "#eceff4") +(defvar phundrak-nord7 "#8fbcbb") +(defvar phundrak-nord8 "#88c0d0") +(defvar phundrak-nord9 "#81a1c1") +(defvar phundrak-nord10 "#5e81ac") +(defvar phundrak-nord11 "#bf616a") +(defvar phundrak-nord12 "#d08770") +(defvar phundrak-nord13 "#ebcb8b") +(defvar phundrak-nord14 "#a3be8c") +(defvar phundrak-nord15 "#b48ead") +#+end_src + +And with that we’re done! + +* Modeline +:PROPERTIES: +:CUSTOM_ID: Modeline-g2ofyw01v5j0 +:header-args:lisp: :mkdirp :tangle ~/.stumpwm.d/modeline.lisp +:END: +The modeline is pretty easy. First, let’s load the ~colors.lisp~ file we just created: +#+begin_src lisp + (load "~/.stumpwm.d/colors.lisp") +#+end_src + +Next, we can set some colors for the modeline. Let’s set the +background of the modeline to Nord1 and the foreground to Nord5, I +think this is a pretty good combination. +#+begin_src lisp + (setf *mode-line-background-color* phundrak-nord1 + ,*mode-line-foreground-color* phundrak-nord5) +#+end_src + +We /could/ also use some borders in the modeline. But we won’t. Let’s +still set its color to Nord1, just in case. +#+begin_src lisp + (setf *mode-line-border-color* phundrak-nord1 + ,*mode-line-border-width* 0) +#+end_src + +The timeout of the modeline indicates how often it refreshes in +seconds. I think one second is good. +#+begin_src lisp + (setf *mode-line-timeout* 1) +#+end_src + +Next we get to the content of the modeline. This format follows the +format indicated in the manpage of ~date~. +#+begin_src lisp + (setf *time-modeline-string* "%F %H:%M") +#+end_src + +Let’s also indicate how the groupname is displayed. +#+begin_src lisp + (setf *group-format* " %t ") +#+end_src + +The window format should display first its window number, then its +titled, limited to 30 characters. +#+begin_src lisp + (setf *window-format* "%n: %30t") +#+end_src + +We can indicate what to display in our modeline. Be aware the ~^>~ +string will align the rest of the string to the right of the modeline. +~%g~ will display the group list, while ~%v~ will display the list of +windows that are in the current group, with the active one +highlighted, and ~%u~ will display urgent windows if there are any. ~%d~ +on the other hand will display the date in the format set above, while +~%B~ will display the battery level of the laptop. +#+begin_src lisp + (setf *screen-mode-line-format* (list "%g %v ^> %C | %M | %B | %d")) +#+end_src + +This variable as you can see is a list of elements, although here I am +only using one string. But it is completely possible to insert some +CLisp code in here that returns some string if the user needs some +code to return data that cannot be easily accesible otherwise. I might +add some at some point, but not today yet. + +# Also, let’s enable a system tray. +# #+begin_src lisp +# (load-module "stumptray") +# (stumptray::stumptray) +# #+end_src + +# Don’t forget to run src_lisp[:exports code]{(ql:quickload :xembed)} in +# ~sbcl~ at least once to install its dependencies. + +* Placement +:PROPERTIES: +:CUSTOM_ID: Placement-mhc3sr21v5j0 +:header-args:lisp: :mkdirp :tangle ~/.stumpwm.d/placement.lisp :noweb yes +:END: +I’ve been used to ten groups, or workspaces, or tags, since I began +using tiling window managers. I shall then continue this habit. Here +is the list of groups I will be using: +#+name: list-groups +| Groups | +|----------| +| term | +| emacs | +| www | +| files | +| media | +| graphics | +| VMs | +| games | +| private | +| discord | + +#+name: gen-groups +#+headers: :tangle no :exports none :cache yes +#+begin_src emacs-lisp :var groups=list-groups + (string-join `(,(format "(grename \"%s\")" (car (car groups))) + ,@(mapcar (lambda (group) + (format "(gnewbg \"%s\")" (car group))) + (cdr groups))) + "\n") +#+end_src + +#+RESULTS[ed2b45d9a542233061373da32e830bd27a68d61b]: gen-groups +#+begin_example +(grename "term") +(gnewbg "emacs") +(gnewbg "www") +(gnewbg "files") +(gnewbg "media") +(gnewbg "graphics") +(gnewbg "VMs") +(gnewbg "games") +(gnewbg "private") +(gnewbg "discord") +#+end_example + +Groups are specified this way: +#+begin_src lisp + (when *initializing* + <>) +#+end_src + +Next, let’s make sure no previous window placement rule is in place, +this will avoid unexpected and hard-to-debug behavior. +#+begin_src lisp + (clear-window-placement-rules) +#+end_src + +Now we can define our window placement preferences. For now, all rely +on the window’s class, so it will be pretty straightforward to write. +#+name: frame-preferences +| Window Class | Group | +|--------------+---------| +| Emacs | emacs | +| Firefox | browser | +| Nemo | files | +| Gimp | media | +| Signal | private | +| lightcord | discord | +| Steam | games | +| Virt-manager | VMs | + +#+name: gen-rules +#+headers: :tangle no :exports results :cache yes :wrap src lisp +#+begin_src emacs-lisp :var rules=frame-preferences + (mapconcat (lambda (rule) + (let ((class (car rule)) + (group (cadr rule))) + (format "(define-frame-preference \"%s\" + (nil t t :class \"%s\"))" group class))) + rules + "\n") +#+end_src + +This can be written this way: +#+RESULTS[b493d3cb9cae1fc97cdb4eb5dc56c9440fde0b2b]: gen-rules +#+begin_src lisp +(define-frame-preference "emacs" + (nil t t :class "Emacs")) +(define-frame-preference "browser" + (nil t t :class "Firefox")) +(define-frame-preference "files" + (nil t t :class "Nemo")) +(define-frame-preference "media" + (nil t t :class "Gimp")) +(define-frame-preference "private" + (nil t t :class "Signal")) +(define-frame-preference "discord" + (nil t t :class "lightcord")) +(define-frame-preference "games" + (nil t t :class "Steam")) +(define-frame-preference "VMs" + (nil t t :class "Virt-manager")) +#+end_src + +* Theme +:PROPERTIES: +:CUSTOM_ID: Theme-1x3c2u31v5j0 +:header-args:lisp: :mkdirp :tangle ~/.stumpwm.d/theme.lisp :noweb yes +:END: +As in the modeline file, the first thing we’ll do is to load our colors. +#+begin_src lisp + (load "~/.stumpwm.d/colors.lisp") +#+end_src + +We can now go onto more serious business. + +** Fonts +:PROPERTIES: +:CUSTOM_ID: Theme-Fonts-28pc8141v5j0 +:END: +This gave me quite the headache when I tried to set this up: in order +to use TTF or OTF fonts, we need to use the ~ttf-fonts~ module which +relies on the ~clx-truetype~ library. A few years back, it should have +been possible to get it installed with a call to src_lisp[:exports +code]{(ql:quickload :clx-truetype)}, but it is no longer available! +There’s a quickfix available while we wait for ~clx-truetype~ to be once +again available: clone it in quicklisp’s local projects. You will +obviously need to have quicklisp installed (for that, follow the +[[https://www.quicklisp.org/beta/#installation][official instructions]]), then execute the following shell commands: +#+begin_src sh + cd ~/quicklisp/local-projects/ + git clone https://github.com/lihebi/clx-truetype.git +#+end_src +This will make ~clx-truetype~ available to quicklisp, and you can run +again src_lisp[:exports code]{(ql:quickload :clx-truetype)} without an +issue (running it again is necessary to install its dependencies). + +Now that this is out of the way, let’s add two lines so we can use TTF +and OTF fonts: +#+begin_src lisp + (ql:quickload :clx-truetype) + (load-module "ttf-fonts") +#+end_src + +Something that didn’t click immediately for me (and I think StumpWM’s +documentation on this could be improved) is that ~set-font~ can be used +to set either one main font for StumpWM, as one might guess reading +the documentation --- or you can set a list of them! And this is +great, since my main font does not support some characters I regularly +have in my windows’ title, such as CJK characters, emojis and all! +Here is my list of fonts I want loaded: +#+name: list-fonts +| Family | Subfamily | Size | +|-------------+-----------+------| +| DejaVu Sans | Book | 9 | +| IPAMincho | Regular | 11 | + +#+name: gen-fonts +#+headers: :tangle no :exports results :cache yes :wrap src lisp +#+begin_src emacs-lisp :var fonts=list-fonts + (format "(set-font `(%s))" + (mapconcat (lambda (font) + (let ((family (nth 0 font)) + (subfamily (nth 1 font)) + (size (nth 2 font))) + (format ",%s" `(make-instance 'xft:font + :family ,(format "\"%s\"" family) + :subfamily ,(format "\"%s\"" subfamily) + :size ,size + :antialias t)))) + fonts + "\n ")) +#+end_src + +The code equivalent of this table can be seen below: +#+RESULTS[054585246a49cd88836d4c5ea1aad66c1bc97f8a]: gen-fonts +#+begin_src lisp +(set-font `(,(make-instance 'xft:font :family "DejaVu Sans" :subfamily "Book" :size 11 :antialias t) + ,(make-instance 'xft:font :family "IPAMincho" :subfamily "Regular" :size 11 :antialias t))) +#+end_src + +*** TODO Font error in modeline with Japanese :noexport: +:PROPERTIES: +:CUSTOM_ID: Theme-Fonts-Font-error-in-modeline-with-Japanese-w9xk5161v5j0 +:END: +Apparently having two fonts, including one with Japanese characters, +does not help with window titles containing Japanese characters. + +** Colors +:PROPERTIES: +:CUSTOM_ID: Theme-Colors-ctlclb51v5j0 +:END: +We can now set a couple of colors for StumpWM. Not that we will see +them often since I don’t like borders on my windows, but in case I +want to get them back, they’ll be nice to have. +#+begin_src lisp + (set-border-color phundrak-nord1) + (set-focus-color phundrak-nord1) + (set-unfocus-color phundrak-nord3) + (set-float-focus-color phundrak-nord1) + (set-float-unfocus-color phundrak-nord3) +#+end_src + +Let’s also set the colors of the message and input windows: +#+begin_src lisp + (set-fg-color phundrak-nord4) + (set-bg-color phundrak-nord1) +#+end_src + +As I said, I don’t like borders, so I’ll remove them. I’ll still keep +the window’s title bar available when it’s floating, and this is also +where I can set the format of its title: its number as well as its +name, limited to thirty characters. +#+begin_src lisp + (setf *normal-border-width* 0 + ,*float-window-border* 0 + ,*float-window-title-height* 15 + ,*window-border-style* :none + ,*window-format* "%n:%30t") +#+end_src + +** Message and Input Windows +:PROPERTIES: +:CUSTOM_ID: Theme-Message-and-Input-Windows-jxwhch51v5j0 +:END: +The Input windows as well as the message windows should both be at the +top of my screen. And I believe a padding of five pixels for the +message windows is good. +#+begin_src lisp + (setf *input-window-gravity* :top + ,*message-window-padding* 10 + ,*message-window-y-padding* 10 + ,*message-window-gravity* :top) +#+end_src + +** Gaps Between Frames +:PROPERTIES: +:CUSTOM_ID: Theme-Gaps-Between-Frames-bqngnt51v5j0 +:END: +I love gaps. When I was using i3, I used the ~i3-gaps~ package, not just +plain ~i3~. In Awesome, I still have gaps. And in StumpWM, I shall still +use gaps. In order to use them, let’s load a module dedicated to gaps +in StumpWM: +#+begin_src lisp + (load-module "swm-gaps") +#+end_src + +Now that this is done, I can now set some variables bound to this +package. +#+begin_src lisp + (setf swm-gaps:*head-gaps-size* 0 + swm-gaps:*inner-gaps-size* 5 + swm-gaps:*outer-gaps-size* 15) +#+end_src + +Finally, let’s enable our gaps: +#+begin_src lisp + (when *initializing* + (swm-gaps:toggle-gaps)) +#+end_src + +* Keybinds +:PROPERTIES: +:CUSTOM_ID: Keybinds-c6wgf961v5j0 +:header-args:lisp: :mkdirp :tangle ~/.stumpwm.d/keybindings.lisp :noweb yes +:END: +Buckle up, this chapter is going to be *long*, because me loves LOTS of keybinds. + +First, let’s declare again we are using the default package ~stumpwm~: +#+begin_src lisp + (in-package :stumpwm) +#+end_src + +This will avoid us always repeating ~stumpwm:define-key~ or ~stumpwm:kbd~ +instead of simply ~define-key~ and ~kbd~. + +StumpWM behaves a bit like Emacs in terms of keybinds. You have +keymaps, which are a collection of keybinds, which in turn call CLisp +functions. However, unlike Emacs, you have to declare a lot of +keymaps, because StumpWM cannot (/yet/) understand keybinds such as +src_lisp[:exports code]{(kbd "C-x c l")}, so you end up creating a +keybind to a keymap which contains other keybinds, which might contain +a couple of keybinds to other keymaps. I hope this will get improved +soon. + +There are also two keymaps you need to be aware of: +- ~*top-map*~ :: This is the keymap available litteraly everywhere. With + this keymap, you can emulate most of your keybinds you have in other + window managers. For instance, I cannot live without ~s-RET~ for + creating new shells, so I’ll bind it to ~*top-map*~. But it’s good + practice to avoid polluting ~*top-map*~ with too many keybinds. +- ~*root-map*~ :: This keymap is the default keymap that is already + somewhat populated. It is available after hitting the prefix key set + with ~set-prefix-key~ which we will see just below. + +It is interesting to note that once you entered any keymap, except +~*top-map*~, if you hit ~?~ you will see the list of available keybinds. +I’d like it if something similar to ~general~ in Emacs too could be +implemented: give any arbitrary name to the keybind you just declared +which would be displayed instead of the actual function or keymap +called by keybind. It would be nicer to see ~frames~ rather than +~*my-frames-management-keymap*~. + +Anyways, as mentionned above, ~*root-map*~ is already pre-populated with +some cool stuff for you, and you can access it with a prefix which is +by default ~C-t~. But if this doesn’t suit you, you can always redefine +it with ~set-prefix-key~. I personally like to have my space key as a +leader key, but in order to not have it conflict with Emacs, I also +need to press the super key too. +#+begin_src lisp + (set-prefix-key (kbd "s-SPC")) +#+end_src + +Also, let’s enable ~which-key~: +#+begin_src lisp + (which-key-mode) +#+end_src + +#+name: keybinds-gen +#+headers: :tangle no :exports none :cache yes :noweb yes +#+begin_src emacs-lisp :var map="m" keybinds=frames-float + (mapconcat (lambda (keybind) + (format "%s" (let ((key (string-replace "~" "" (car keybind))) + (function (string-replace "~" "" (cadr keybind)))) + `(define-key ,map + (kbd ,(format "\"%s\"" key)) + ,(if (string-prefix-p "'" function t) + function + (format "\"%s\"" function)))))) + keybinds + "\n") +#+end_src + +#+RESULTS[5938b2a6efd9c8b565416f9a687bc0d6a4a5f77e]: keybinds-gen +: (define-key m (kbd "f") "float-this") +: (define-key m (kbd "F") "unfloat-this") +: (define-key m (kbd "u") "unfloat-this") +: (define-key m (kbd "C-f") "flatten-floats") + +** Frames and Windows management +:PROPERTIES: +:CUSTOM_ID: Keybinds-Frames-and-Windows-management-g4s6j371v5j0 +:END: +As you’ll see, I have loads of keybinds related to frames and windows +management. They are all categorized in a specific keymap, called +~*my-frames-management-keymap*~. But before that, let’s define the +keymap ~*my-frames-float-keymap*~, with keybinds dedicated to actions +related with floating windows and frames. + +#+name: frames-float +#+caption: ~*my-frames-float-keymap*~ +| Keychord | Function | +|----------+----------------| +| ~f~ | ~float-this~ | +| ~F~ | ~unfloat-this~ | +| ~u~ | ~unfloat-this~ | +| ~C-f~ | ~flatten-floats~ | + +We can now pass onto ~*my-frames-management-keymap*~. My keybinds are organized this way: +#+name: frames-and-window-management +#+caption: ~*my-frames-management-keymap*~ +| Keychord | Function | +|----------+---------------------------| +| ~c~ | ~move-focus left~ | +| ~t~ | ~move-focus down~ | +| ~s~ | ~move-focus up~ | +| ~r~ | ~move-focus right~ | +| ~C~ | ~move-window left~ | +| ~T~ | ~move-window down~ | +| ~S~ | ~move-window up~ | +| ~R~ | ~move-window right~ | +| ~C-c~ | ~exchange-direction left~ | +| ~C-t~ | ~exchange-direction down~ | +| ~C-s~ | ~exchange-direction up~ | +| ~C-r~ | ~exchange-direction right~ | +| ~n~ | ~next~ | +| ~p~ | ~prev~ | +| ~/~ | ~hsplit~ | +| ~-~ | ~vsplit~ | +| ~h~ | ~hsplit~ | +| ~v~ | ~vsplit~ | +| ~H~ | ~hsplit-equally~ | +| ~V~ | ~vsplit-equally~ | +| ~.~ | ~iresize~ | +| ~d~ | ~remove-split~ | +| ~D~ | ~only~ | +| ~e~ | ~expose~ | +| ~f~ | ~fullscreen~ | +| ~F~ | ~'*my-frames-float-keymap*~ | +| ~i~ | ~info~ | +| ~I~ | ~show-window-properties~ | +| ~m~ | ~meta~ | +| ~o~ | ~other-window~ | +| ~q~ | ~delete-window~ | +| ~Q~ | ~kill-window~ | +| ~s~ | ~sibling~ | +| ~u~ | ~next-urgent~ | +| ~U~ | ~unmaximize~ | + +As you can see, with the binding to ~F~, we make use of the +~*my-frames-float-keymap*~ keymap declared above, which means if we find +ourselves in ~*my-frames-management-keymap*~, pressing ~F~ will bring us +in ~*my-frames-float-keymap*~. + +#+begin_src lisp + (defvar *my-frames-float-keymap* + (let ((m (make-sparse-keymap))) + <> + m)) + + (defvar *my-frames-management-keymap* + (let ((m (make-sparse-keymap))) + <> + m)) +#+end_src + +Let’s bind ~*my-frames-management-keymap*~ in ~*root-keymap*~: +#+begin_src lisp + (define-key *root-map* (kbd "w") '*my-frames-management-keymap*) +#+end_src + +That way, if we want for instance to split our current frame +vertically, we’ll be able to type ~s-SPC w -~ and ~vsplit~ will be called. + +I also bound a couple of these functions to the top keymap for easier access: +#+name: top-window-map +| Keychord | Function | +|----------+--------------------------| +| ~s-c~ | ~move-focus left~ | +| ~s-t~ | ~move-focus down~ | +| ~s-s~ | ~move-focus up~ | +| ~s-r~ | ~move-focus right~ | +| ~s-C~ | ~move-window left~ | +| ~s-T~ | ~move-window down~ | +| ~s-S~ | ~move-window up~ | +| ~s-R~ | ~move-window right~ | +| ~s-M-c~ | ~exchange-direction left~ | +| ~s-M-t~ | ~exchange-direction down~ | +| ~s-M-s~ | ~exchange-direction up~ | +| ~s-M-r~ | ~exchange-direction right~ | + +This translates to: +#+begin_src lisp + <> +#+end_src + +Being a [[https://bepo.fr/wiki/Accueil][bépo layout]] user, the ~hjkl~ keys don’t exactly fit me, as you +might have noticed with my use of ~ctsr~ which is its equivalent. Due to +this, the interactive keymap for ~iresize~ is not ideal for me, let me +redefine it: +#+begin_src lisp + (define-interactive-keymap (iresize tile-group) (:on-enter #'setup-iresize + :on-exit #'resize-unhide + :abort-if #'abort-resize-p) + ((kbd "c") "resize-direction left") + ((kbd "t") "resize-direction down") + ((kbd "s") "resize-direction up") + ((kbd "r") "resize-direction right")) +#+end_src + +** Applications +:PROPERTIES: +:CUSTOM_ID: Keybinds-Applications-2t512k00w5j0 +:END: +When I speak about applications, I speak about programs and scripts in +general. With these keymaps, I can launch programs I often have use +for, but I can also launch some scripts as well as take screenshots. + +First, let’s create my ~rofi~ scripts keymap. +#+name: rofi-scripts +#+caption: ~*my-rofi-keymap*~ +| Keychord | Function | +|----------+-----------------------------------------------| +| ~a~ | ~exec awiki~ | +| ~r~ | ~exec rofi -combi-modi drun,window -show combi~ | +| ~s~ | ~exec rofi -show ssh~ | +| ~p~ | ~exec rofi-pass -t~ | +| ~P~ | ~exec rofi-pass~ | +| ~e~ | ~exec rofi-emoji~ | +| ~m~ | ~exec rofi-mount~ | +| ~u~ | ~exec rofi-umount~ | +| ~w~ | ~exec wacom-setup~ | +| ~y~ | ~exec ytplay~ | +| ~Y~ | ~exec rofi-ytdl~ | + +Here’s the equivalent in Common Lisp. +#+begin_src lisp + (defvar *my-rofi-keymap* + (let ((m (make-sparse-keymap))) + <> + m)) +#+end_src + +Let’s also create a keymap for screenshots. +#+name: screenshot-keymap +#+caption: ~*my-screenshot-keymap*~ +| Keychord | Function | +|----------+------------------------------------------------------| +| ~d~ | ~exec scrot -d 3 -e 'mv $f ~/Pictures/Screenshots'~ | +| ~s~ | ~exec scrot -e 'mv $f ~/Pictures/Screenshots'~ | +| ~S~ | ~exec scrot -s -e 'mv $f ~/Pictures/Screenshots'~ | +| ~g~ | ~exec scrot -e 'gimp $f; mv $f ~/Pictures/Screenshots'~ | + +Here’s the equivalent in Common Lisp. +#+begin_src lisp + (defvar *my-screenshot-keymap* + (let ((m (make-sparse-keymap))) + <> + m)) +#+end_src + +We can now define our applications keymap which will reference both +the above keymaps. +#+name: application-keymap +#+caption: ~*my-applications-keymap*~ +| Keychord | Function | +|----------+-------------------------| +| ~b~ | ~firefox~ | +| ~d~ | ~exec lightcord~ | +| ~e~ | ~emacs~ | +| ~g~ | ~exec gimp~ | +| ~n~ | ~exec nemo~ | +| ~r~ | ~'*my-rofi-keymap*~ | +| ~s~ | ~'*my-screenshot-keymap*~ | + +This translates to: +#+begin_src lisp + (defvar *my-applications-keymap* + (let ((m (make-sparse-keymap))) + <> + m)) +#+end_src + +The application keymap can now be bound to the top map like so: +#+begin_src lisp + (define-key *top-map* (kbd "s-a") '*my-applications-keymap*) +#+end_src + +I will also bind to the top map ~s-RET~ in order to open a new terminal +window. The screenshot keymap is also bound to the ScreenPrint key. +#+begin_src lisp + (define-key *top-map* (kbd "s-RET") "exec kitty") + (define-key *top-map* (kbd "Print") '*my-screenshot-keymap*) +#+end_src + +** End of Session, Powering Off, and the Likes +:PROPERTIES: +:CUSTOM_ID: Keybinds-End-of-Session-Powering-Off-and-the-Likes-mgz02z40w5j0 +:END: +The module ~end-session~ provides functions for gracefully ending the +user session, powering off, restarting, and suspending the computer. +It also provides a function that interactively asks what the user +whishes to do. +#+name: end-session-keymap +| Keychord | Function | +|----------+-------------------| +| ~q~ | ~end-session~ | +| ~l~ | ~logout~ | +| ~s~ | ~suspend-computer~ | +| ~S~ | ~shutdown-computer~ | +| ~r~ | ~restart-computer~ | + +This translates to: +#+begin_src lisp + (defvar *my-end-session-keymap* + (let ((m (make-sparse-keymap))) + <> + m)) +#+end_src + +Which is bound in the root map to ~q~: +#+begin_src lisp + (define-key *root-map* (kbd "q") '*my-end-session-keymap*) +#+end_src + +** Misc +:PROPERTIES: +:CUSTOM_ID: Keybinds-Misc-455iuh50w5j0 +:END: +Finally, some misc keybinds on the root map which don’t really fit +anywhere else: +#+name: misc-root-map +| Keychord | Function | +|----------+------------| +| ~B~ | ~beckon~ | +| ~l~ | ~exec plock~ | +| ~r~ | ~reload~ | + +#+begin_src lisp + <> +#+end_src