Lucien Cartier-Tilet
22bbd61ed1
Some modules loaded in init.lisp are instead loaded in modeline.lisp, as it makes more sense. Update modeline’s format. Update groups’ number and name, they now have an actual name and visible purpose. Their definition is also made at the same time as the definition of the windows linked to these groups, generated by different Elisp code blocks but from the same table. Used fonts are also updated, loading more than one doesn’t seem to have an effect. This commit also adds a comment on OTF fonts. More keybinds and keymaps are now defined through tables to make them more readable and easier to maintain. Some keybinds are removed to make more sense between my Emacs keybinds and keychords and StumpWM’s. Some move from window keychords to buffer keychords. Some Elisp functions are hidden in a dedicated noexport heading.
1172 lines
43 KiB
Org Mode
1172 lines
43 KiB
Org Mode
#+TITLE: StumpWM config
|
||
#+setupfile: headers
|
||
#+OPTIONS: auto-id:t
|
||
#+HTML_HEAD_EXTRA: <meta name="description" content="Phundrak's StumpWM config" />
|
||
#+HTML_HEAD_EXTRA: <meta property="og:title" content="Phundrak's StumpWM config" />
|
||
#+HTML_HEAD_EXTRA: <meta property="og:description" content="Description of the Stump Window Manager configuration files of Phundrak" />
|
||
#+property: header-args:emacs-lisp :tangle no :exports results :cache yes :noweb yes
|
||
|
||
* 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/")
|
||
#+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: :wrap src lisp
|
||
#+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~ in the AUR). 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 |
|
||
| beckon | Bring the mouse cursor to the current window |
|
||
| end-session | Gracefully end programs when ending user session |
|
||
| globalwindows | Navigate between windows from all workspaces |
|
||
| stump-backlight | Native management of backlight in StumpWM |
|
||
| urgentwindows | Get urgent windows |
|
||
|
||
#+name: gen-load-modules
|
||
#+headers: :wrap src lisp
|
||
#+begin_src emacs-lisp :var modules=loaded-modules
|
||
(mapconcat (lambda (module)
|
||
(format "(load-module \"%s\")" (car module)))
|
||
modules
|
||
"\n")
|
||
#+end_src
|
||
|
||
#+RESULTS[508e36f9747f1da901bbee63582416a8a6ba2c2f]: gen-load-modules
|
||
#+begin_src lisp
|
||
(load-module "alert-me")
|
||
(load-module "beckon")
|
||
(load-module "end-session")
|
||
(load-module "globalwindows")
|
||
(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:
|
||
|
||
The first command I declare in this file is a command that will avoid
|
||
me invoking too many Firefox instances. Either Firefox is not already
|
||
running and an instance is launched, or one already is and we are
|
||
brought to it. This is done like so:
|
||
#+begin_src lisp
|
||
(defcommand firefox () ()
|
||
"Run or raise Firefox."
|
||
(run-or-raise "firefox" '(:class "Firefox") t nil))
|
||
#+end_src
|
||
|
||
Next, this command will not only close the current window, but it will
|
||
also close the current frame.
|
||
#+begin_src lisp
|
||
(defcommand delete-window-and-frame () ()
|
||
"Delete the current frame with its window."
|
||
(delete-window)
|
||
(remove-split))
|
||
#+end_src
|
||
|
||
The two following commands will create a new frame to the right and
|
||
below the current frame respectively, then focus it.
|
||
#+begin_src lisp
|
||
(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 move focus to it."
|
||
(vsplit)
|
||
(move-focus :down))
|
||
#+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: :wrap src lisp
|
||
#+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
|
||
|
||
Here are some modules that we will load for the modeline:
|
||
#+name: modeline-modules
|
||
| Module Name | Why It Is Loaded |
|
||
|------------------+--------------------------------------------------|
|
||
| battery-portable | Get information on the battery level of a laptop |
|
||
| cpu | Get the CPU usage of the computer |
|
||
| mem | Get the memory usage of the computer |
|
||
|
||
#+name: gen-load-modeline-modules
|
||
#+headers: :wrap src lisp
|
||
#+begin_src emacs-lisp :var modules=modeline-modules
|
||
(mapconcat (lambda (module)
|
||
(format "(load-module \"%s\")" (car module)))
|
||
modules
|
||
"\n")
|
||
#+end_src
|
||
|
||
#+RESULTS[75023085d7c69ae09044826218830e6b678d5959]: gen-load-modeline-modules
|
||
#+begin_src lisp
|
||
(load-module "battery-portable")
|
||
(load-module "cpu")
|
||
(load-module "mem")
|
||
#+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 %u ^> %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.
|
||
|
||
For some reason, the ~stumptray~ module does not work for me, I get an
|
||
error saying a certain value is of type ~NIL~ and not of type
|
||
~STUMPWM:MODELINE~. I should investigate this.
|
||
|
||
** TODO Investigate why ~stumptray~ doesn’t work :noexport:
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Modeline-Investigate-why-stumptray-doesn-t-work-0juh13g0m6j0
|
||
:END:
|
||
|
||
# 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 | Number | Windows |
|
||
|---------+--------+----------------------------|
|
||
| [SYS] | 1 | |
|
||
| [DEV] | 2 | Emacs, Virt-manager |
|
||
| [WWW] | 3 | Firefox |
|
||
| [FILES] | 4 | Nemo |
|
||
| [MEDIA] | 5 | Gimp |
|
||
| [SOC] | 6 | Signal, discord, lightcord |
|
||
| [PRIV] | 7 | |
|
||
|
||
#+name: gen-groups
|
||
#+headers: :tangle no :exports none :cache yes
|
||
#+begin_src emacs-lisp :var groups=list-groups
|
||
(string-trim (string-join `(,(format "(grename \"%s\")" (car (car groups)))
|
||
,@(mapcar (lambda (group)
|
||
(format "(gnewbg \"%s\")" (car group)))
|
||
(cdr groups)))
|
||
"\n")
|
||
"[[:space:]]*"
|
||
"[[:space:]]*")
|
||
#+end_src
|
||
|
||
#+RESULTS[caa45af7c6ee092f88acb86d974cf0c9c93b2a3e]: gen-groups
|
||
: (grename "[SYS]")
|
||
: (gnewbg "[DEV]")
|
||
: (gnewbg "[WWW]")
|
||
: (gnewbg "[FILES]")
|
||
: (gnewbg "[MEDIA]")
|
||
: (gnewbg "[SOC]")
|
||
: (gnewbg "[PRIV]")
|
||
|
||
Groups are specified this way:
|
||
#+begin_src lisp
|
||
(when *initializing*
|
||
<<gen-groups()>>)
|
||
#+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
|
||
|
||
As you can see in the table [[list-groups]] above, I also indicated my
|
||
window placement preferences. For now, they all rely on the window’s
|
||
class, so it will be pretty straightforward to the corresponding code.
|
||
#+name: gen-rules
|
||
#+headers: :tangle no :exports results :cache yes :wrap src lisp
|
||
#+begin_src emacs-lisp :var rules=list-groups
|
||
(require 'seq)
|
||
(let ((output "")
|
||
(rules (seq-filter (lambda (rule) rule)
|
||
(mapcar (lambda (line)
|
||
(let ((classes (caddr line)))
|
||
(unless (string= "" classes)
|
||
(cons
|
||
(split-string classes "," t "[[:space:]]*")
|
||
(car line)))))
|
||
rules))))
|
||
(progn
|
||
(seq-do (lambda (rule)
|
||
(let ((classes (car rule))
|
||
(group (cdr rule)))
|
||
(dolist (class classes)
|
||
(setf output (format "%s\n%s"
|
||
`(define-frame-preference ,(format "\"%s\"" group)
|
||
(nil t t :class ,(format "\"%s\"" class)))
|
||
output)))))
|
||
rules)
|
||
output))
|
||
#+end_src
|
||
|
||
This can be written this way:
|
||
#+RESULTS[1c9490c15e3cc7d2c8ed1c508cab844567232afc]: gen-rules
|
||
#+begin_src lisp
|
||
(define-frame-preference "[SOC]" (nil t t :class "lightcord"))
|
||
(define-frame-preference "[SOC]" (nil t t :class "discord"))
|
||
(define-frame-preference "[SOC]" (nil t t :class "Signal"))
|
||
(define-frame-preference "[MEDIA]" (nil t t :class "Gimp"))
|
||
(define-frame-preference "[FILES]" (nil t t :class "Nemo"))
|
||
(define-frame-preference "[WWW]" (nil t t :class "Firefox"))
|
||
(define-frame-preference "[DEV]" (nil t t :class "Virt-manager"))
|
||
(define-frame-preference "[DEV]" (nil t t :class "Emacs"))
|
||
#+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
|
||
fonts:
|
||
#+begin_src lisp
|
||
(ql:quickload :clx-truetype)
|
||
(load-module "ttf-fonts")
|
||
#+end_src
|
||
The documentation says we should be able to also use OTF fonts, but so
|
||
far I’ve had no luck loading one. Loading more than one font to use
|
||
some fallback fonts also doesn’t seem to work, unlike specified in the
|
||
documentation (I wanted to use a CJK font, but it doesn’t appear to
|
||
work).
|
||
|
||
# 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 Mono | Book | 8 |
|
||
# | 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[ca1ca106ad10eea84a34362acfc543eba559260c]: gen-fonts
|
||
#+begin_src lisp
|
||
(set-font `(,(make-instance 'xft:font :family "DejaVu Sans Mono" :subfamily "Book" :size 8 :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
|
||
|
||
Lastly, before we get more into details, keep in mind that I use the
|
||
[[https://bepo.fr][bépo]] layout, as I often say in my different documents. This means the
|
||
characters found in the numbers’ row when pressing shift are actually
|
||
the numbers themselves. Below are the following characters:
|
||
#+name: number-to-char-table
|
||
| Number | Character |
|
||
|--------+-----------|
|
||
| 1 | ~"~ |
|
||
| 2 | ~«~ |
|
||
| 3 | ~»~ |
|
||
| 4 | ~(~ |
|
||
| 5 | ~)~ |
|
||
| 6 | ~@~ |
|
||
| 7 | ~+~ |
|
||
| 8 | ~-~ |
|
||
| 9 | ~/~ |
|
||
| 0 | ~*~ |
|
||
|
||
So if you see any weird keybind involving these characters, this is
|
||
because of my layout.
|
||
|
||
** 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)))
|
||
<<keybinds-gen(map="m", keybinds=rofi-scripts)>>
|
||
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)))
|
||
<<keybinds-gen(map="m", keybinds=screenshot-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~ | ~exec emacsclient -c~ |
|
||
| ~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)))
|
||
<<keybinds-gen(map="m", keybinds=application-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~ | ~loadrc~ |
|
||
| ~R~ | ~restart-hard~ |
|
||
| ~C-r~ | ~restart-computer~ |
|
||
|
||
This translates to:
|
||
#+begin_src lisp
|
||
(defvar *my-end-session-keymap*
|
||
(let ((m (make-sparse-keymap)))
|
||
<<keybinds-gen(map="m", keybinds=end-session-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
|
||
|
||
** 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~ |
|
||
| ~/~ | ~hsplit-and-focus~ |
|
||
| ~-~ | ~vsplit-and-focus~ |
|
||
| ~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~ |
|
||
| ~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)))
|
||
<<keybinds-gen(map="m", keybinds=frames-float)>>
|
||
m))
|
||
|
||
(defvar *my-frames-management-keymap*
|
||
(let ((m (make-sparse-keymap)))
|
||
<<keybinds-gen(map="m", keybinds=frames-and-window-management)>>
|
||
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
|
||
<<keybinds-gen(map="*top-map*", keybinds=top-window-map)>>
|
||
#+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
|
||
|
||
** Windows management
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Keybinds-Windows-management-ylf903j0x5j0
|
||
:END:
|
||
When it comes to windows management, I will treat them a bit like I do
|
||
with Emacs’ buffers.
|
||
|
||
#+name: window-management
|
||
#+caption: ~*my-buffers-management-keymap*~
|
||
| Keychord | Function |
|
||
|----------+-------------------------|
|
||
| ~b~ | ~windowlist~ |
|
||
| ~d~ | ~delete-window~ |
|
||
| ~D~ | ~window-window-and-frame~ |
|
||
| ~k~ | ~kill-window~ |
|
||
| ~n~ | ~next~ |
|
||
| ~o~ | ~other-window~ |
|
||
| ~p~ | ~prev~ |
|
||
|
||
#+begin_src lisp
|
||
(defvar *my-buffers-management-keymap*
|
||
(let ((m (make-sparse-keymap)))
|
||
<<keybinds-gen(map="m", keybinds=window-management)>>
|
||
m))
|
||
|
||
(define-key *root-map* (kbd "b") '*my-buffers-management-keymap*)
|
||
#+end_src
|
||
|
||
** Media and Media Control
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: Keybinds-Media-and-Media-Control-hbv5uk91z5j0
|
||
:END:
|
||
My music is managed through MPD, and I often use ~mpc~ commands in order
|
||
to interact with it without any GUI application. So, we’ll see a lot
|
||
of its usage here.
|
||
|
||
First, let’s declare an interactive keymap in order to easily change
|
||
several times in a row either the current song playing or the volume
|
||
of MPD.
|
||
#+name: inter-mpc
|
||
#+caption: Interactive keybinds for ~mpc~
|
||
| Keychord | Function |
|
||
|----------+--------------------|
|
||
| ~c~ | ~exec mpc prev~ |
|
||
| ~t~ | ~exec mpc volume -2~ |
|
||
| ~s~ | ~exec mpc volume +2~ |
|
||
| ~r~ | ~exec mpc next~ |
|
||
|
||
Cela donne le code suivant:
|
||
#+begin_src lisp
|
||
<<interactive-gen(name="mpc-interactive", keys=inter-mpc)>>
|
||
#+end_src
|
||
|
||
Another one will be defined for the general audio of my computer. And
|
||
I know it isn’t technically media keybinds, but I’ll add in keybinds
|
||
for my screen’s backlight.
|
||
#+name: inter-media
|
||
#+caption: Interactive keybinds for general media interaction
|
||
| Keys | Function |
|
||
|------+--------------------------------------|
|
||
| ~c~ | ~exec xbacklight -dec 2~ |
|
||
| ~t~ | ~exec amixer -q set Master 2%- unmute~ |
|
||
| ~s~ | ~exec amixer -q set Master 2%+ unmute~ |
|
||
| ~r~ | ~exec xbacklight -inc 2~ |
|
||
| ~m~ | ~exec amixer -q set Master 1+ toggle~ |
|
||
|
||
#+begin_src lisp
|
||
<<interactive-gen(name="media-interactive", keys=inter-media)>>
|
||
#+end_src
|
||
|
||
Then, let’s declare a keymap for our media controls.
|
||
#+name: media-management
|
||
#+caption: ~*my-media-keymap*~
|
||
| Keychord | Function |
|
||
|----------+-------------------|
|
||
| ~.~ | ~media-interactive~ |
|
||
| ~m~ | ~mpc-interactive~ |
|
||
| ~p~ | ~exec mpc prev~ |
|
||
| ~n~ | ~exec mpc next~ |
|
||
| ~t~ | ~exec mpc toggle~ |
|
||
| ~s~ | ~exec mpc stop~ |
|
||
|
||
Let’s translate this table in CommonLisp:
|
||
#+begin_src lisp
|
||
(defvar *my-buffers-management-keymap*
|
||
(let ((m (make-sparse-keymap)))
|
||
<<keybinds-gen(map="m", keybinds=media-management)>>
|
||
m))
|
||
|
||
(define-key *root-map* (kbd "m") '*my-media-keymap*)
|
||
#+end_src
|
||
|
||
I will also define on ~*top-map*~ some basic volume management keybinds
|
||
so that they are immediately accessible. Again, this isn’t technically
|
||
media-related, but I’ll add keybinds for my screen’s backlight.
|
||
#+name: media-top-level
|
||
#+caption: Top-level media keys
|
||
| Keychord | Function |
|
||
|-----------------------+--------------------------------------|
|
||
| ~XF86AudioRaiseVolume~ | ~exec amixer -q set Master 2%+ unmute~ |
|
||
| ~XF86AudioLowerVolume~ | ~exec amixer -q set Master 2%- unmute~ |
|
||
| ~XF86AudioMute~ | ~exec amixer -q set Master 1+ toggle~ |
|
||
| ~XF86MonBrightnessDown~ | ~exec xbacklight -dec 2~ |
|
||
| ~XF86MonBrightnessUp~ | ~exec xbacklight -inc 2~ |
|
||
|
||
#+begin_src lisp
|
||
<<keybinds-gen(map="*top-map*", keybinds=media-top-level)>>
|
||
#+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
|
||
<<keybinds-gen(map="*root-map*", keybinds=misc-root-map)>>
|
||
#+end_src
|
||
|
||
* org functions :noexport:
|
||
:PROPERTIES:
|
||
:CUSTOM_ID: org-functions-syqgzgg0m6j0
|
||
:END:
|
||
|
||
#+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 (let ((s (car keybind)))
|
||
(substring-no-properties s 1 (1- (length s)))))
|
||
(function (let ((s (cadr keybind)))
|
||
(substring-no-properties s 1 (1- (length s))))))
|
||
`(define-key ,map
|
||
(kbd ,(format "\"%s\"" key))
|
||
,(if (string-prefix-p "'" function t)
|
||
function
|
||
(format "\"%s\"" function))))))
|
||
keybinds
|
||
"\n")
|
||
#+end_src
|
||
|
||
#+name: interactive-gen
|
||
#+headers: :tangle no :exports none :noweb yes
|
||
#+begin_src emacs-lisp :var name="inter" keys=inter-mpc
|
||
(format "%s"
|
||
`(define-interactive-keymap ,name ()
|
||
"\n "
|
||
,(mapconcat (lambda (keybind)
|
||
(format "%s"
|
||
(let ((key (let ((s (car keybind)))
|
||
(substring-no-properties s
|
||
1
|
||
(1- (length s)))))
|
||
(command (let ((s (cadr keybind)))
|
||
(substring-no-properties s
|
||
1
|
||
(1- (length s))))))
|
||
`((kbd ,(format "\"%s\"" key)) ,(format "\"%s\"" command)))))
|
||
keys
|
||
"\n ")))
|
||
#+end_src
|
||
|
||
#+name: num-to-char
|
||
#+headers: :tangle no :exports none :noweb yes
|
||
#+begin_src emacs-lisp :var table=number-to-char-table num=0
|
||
(replace-regexp-in-string (regexp-quote "~")
|
||
""
|
||
(cadr (assoc num table)))
|
||
#+end_src
|
||
|
||
#+RESULTS: num-to-char
|
||
: *
|