StumpWM: Lisp Tiling Window Manager

StumpWM: Lisp Tiling Window Manager

Alors, StumpWM, tout d'abord, c'est le descendant de Ratpoison, un gestionnaire de fenêtres en mode pavant, manuel. Atchoum!

Quelques petites définitions

  • Gestionnaire de fenêtre en mode pavant, ou Tiling Window Manager: un gestionnaire de fenêtres où les fenêtres ne se recouvrent jamais partiellement
  • Manual Tiling Window Manager: c'est l'utilisateur qui définit lui même le placement (ou les règles de placement) des fenêtres, en divisant l'écran en zones où elles pourront s'afficher. Notion, Ratpoison, StumpWM, Musca sont des TWM manuels.
  • Dynamic Tiling Window Manager: c'est le gestionnaire de fenêtre qui propose plusieurs "modes" de Tiling déjà prévus, et placera lui même les fenêtres: XMonad, DWM, Awesome par exemple fonctionnent en grande partie sur ce principe.
  • Les "un peu des deux": i3, Wmii par exemple
  • Une des particularités (mais pas que) de ces TWMs est d'être intégralement contrôlables au clavier, via des raccourcis claviers en général dérivés du meilleur éditeur de texte de la planète, ViM, ou de son concurrent tout aussi bon mais un chouilla plus lourd, Emacs (no pun intended, je ne vais pas lancer une flamewar ;)). Et comme ils sont configurables à souhait, un TWM "Emacs-like" comme StumpWM peut très bien se vimifier et vice-versa.

Alors je ne suis pas là pour troller tel ou tel paradigme (ou tel ou tel éditeur), mais j'avoue qu'avec le temps, ma préférence va au TWM manuels: en général, j'ai toujours les mêmes fenêtres sur les mêmes groupes, aux même positions sur l'(es) écran(s) et donc, je dumpe cette configuration dans un fichier qui est chargé au démarrage de mon gestionnaire de fenêtres, et ainsi je retrouve le même environnement à chaque fois.

Ensuite vient la question du/des fichier(s) de configuration: plaintext (ratpoison, spectrwm, musca, i3), header C (dwm), lua (awesome), haskell (XMonad), CommonLisp (StumpWM), bash (herbstluftwm, wmii), etc, etc. Les choix et possibilités sont nombreux, même si pour le débutant je conseillerai plutôt de commencer par un TWM dont la configuration se fait en plaintext.

Mais je diverge, je suis ici pour vous parler de StumpWM.

Pourquoi StumpWM?

  • c'est un TWM manuel
  • il est écrit et se configure en CommonLisp (sbcl et clisp)
  • Il est facilement modulable
  • Le manuel est top
  • Le développement est actif
  • Les possibilités de configuration sont sans limite ou presque
  • On peut avoir une REPL et modifier le code source de Stump en live sans avoir à relancer le gestionnaire de fenêtres
  • Il est capable de se recompiler à la volée

Et ça donne quoi, concrètement?

Eh bien ça donne ça:

stumpwm

Oui, j'ai trois écrans. Si vous faites gaffe, vous verrez sur l'écran de gauche, en bas, la modeline dans laquelle sont affichés:

  • Les groupes
  • Les fenêtres taggées urgent
  • et dans laquelle je pipe conky afin d'avoir quelques infos système (réseau, CPU, RAM, zique, météo, karma reddit, nombres de connectés sur la plateforme principale d'un client, date et heure)

Je ne vous montre pas les autres groupes, vu qu'ils sont réservés à mes usages professionnels et mails.

Mais pourquoi tu utilises ce truc?

C'est très simple:

  • C'est rapide
  • C'est entièrement contrôlé au clavier, et comme je passe beaucoup de temps à taper sur un clavier du fait de mon métier (sysadmin unix/linux), ça m'évite d'avoir à choper la souris à chaque fois que je veux changer de fenêtre
  • Il m'est très facile de créer un nouveau groupe, pour, disons, un nouveau client
  • Lorsque je lance stump, il me charge directement mes groupes et place les fenêtres en fonction
  • Il utilise le principe du prefix-key, ce qui me permet, par exemple si je veux diviser une frame en deux, de presser la touche windows, la relâcher, puis ensuite de presser la touche s. Comme j'ai déjà fait quelques tendinites à causes des raccourcis Windows©® (oui, Ctrl-C,V,X c'est le mal), c'est très confortable
  • Pour toutes ces raisons, je suis plus productif tout en ayant une meilleure ergonomie que si j'utilisais un gestionnaire de fenêtres ou environnement de bureau "classique".

Il est bien évident que cela n'a par contre aucun intérêt pour une personne qui ne passe pas sa vie à taper sur un clavier, ou pour une personne qui préfère de beaux effets graphiques, etc. Ce n'est pas le but de ce genre de gestionnaires de fenêtres, qui s'adresse quand même nettement à un public averti, du power-user au développeur en passant par l'administrateur systèmes et réseaux. Je ne conseillerai pas ce genre de logiciel à Tatie Michette (ou Tonton Raymond), qui veut juste quelque chose de simple pour regarder ses mails, surfer un peu sur les sites qui lui plaisent et écouter un peu de musique, on est bien d'accord! (Et ça n'empêche pas Tatie Michette et Tonton Raymond d'être des gens biens, hein)

Et la conf', elle a quelle tête?

Alors attention les mirettes:

;;; -*- lisp -*-
;;; #Date#: 30 June 2015 18:56

(in-package :stumpwm)

(set-font "-*-terminus-medium-*-*-*-12-*-*-*-*-*-*-*")
(set-normal-gravity :center)
(set-fg-color "#b9b9b9")
(set-bg-color "#101010")
(set-border-color "#525252")
(set-focus-color "#525252")
(set-unfocus-color   "#101010")
(set-win-bg-color    "#101010")
(setf *colors* (list "#101010"          ; 0 black
                     "#7c7c7c"          ; 1 red
                     "#8e8e8e"          ; 2 green
                     "#a0a0a0"          ; 3 yellow
                     "#686868"          ; 4 blue
                     "#747474"          ; 5 magenta
                     "#868686"          ; 6 cyan
                     "#b9b9b9"          ; 7 white
                     "#525252"          ; 8 personal
                     "#f7f7f7"))        ; 9 personal
(update-color-map (current-screen))
(set-msg-border-width 7)
(set-frame-outline-width 3)
(setf *maxsize-border-width* 3)
(setf *transient-border-width* 2)
(setf *normal-border-width* 2)
(setf *window-border-style* :thin)
(setf *message-window-gravity* :center)
(setf *input-window-gravity* :center)
(setf *mouse-focus-policy* :click)
(define-key *root-map* (kbd "c") "exec urxvtc")

;; I need to tag irc window on all groups
;; The Dirty Way :-)

(run-commands "load-module windowtags")

(defcommand my_little_gnext () ()
  (run-commands "gnext" "fselect 0" "pull-tag irc" "fselect 2" "pull-tag fx"))

(defcommand my_little_gprev () ()
  (run-commands "gprev" "fselect 0" "pull-tag irc" "fselect 2" "pull-tag fx"))

(defcommand my_little_gselect () ()
  (run-commands "gselect" "fselect 0" "pull-tag irc" "fselect 2" "pull-tag fx"))

(defcommand my_little_relwarp () ()
  (run-commands "banish" "ratrelwarp -800 -800"))

;; I need my conky friend

(defvar *conky-command*
  "cat /mnt/ramdisk/conkytext")

;; modeline

(setf *screen-mode-line-format*
      (list "^b%g :: %W ::^B^8 %u^>"
            '(:eval (run-shell-command *conky-command* t))))

(setf *mode-line-timeout* 2)
(setf *mode-line-background-color* "#101010")
(setf *mode-line-foreground-color* "#b9b9b9")
(setf *mode-line-border-color* "#101010")
(setf *mode-line-position* :bottom)

;; top map for convenience
(define-key *top-map* (kbd "Pause") "exec mpc toggle")
(define-key *top-map* (kbd "Print") "exec mpc prev")
(define-key *top-map* (kbd "Scroll_Lock") "exec mpc next")
(define-key *top-map* (kbd "M-Pause") "exec echo pause > $HOME/.mplayer_fifo")
(define-key *top-map* (kbd "M-Print") "exec echo seek -30 > $HOME/.mplayer_fifo")
(define-key *top-map* (kbd "M-Scroll_Lock") "exec echo seek +30 > $HOME/.mplayer_fifo")
(define-key *top-map* (kbd "M-p") "exec rofi -rnow -font 'Terminus 12' -fg '#b9b9b9' -bg '#101010' -lines '20' -columns '3' -hlfg '#101010' -hlbg '#b9b9b9' -bc '#525252' -bw '3'")
(define-key *top-map* (kbd "M-;") "eval")
(define-key *top-map* (kbd "M-:") "colon")
(define-key *top-map* (kbd "M-Return") "exec urxvtc")

;; root map
(define-key *root-map* (kbd "h") "move-focus left")
(define-key *root-map* (kbd "l") "move-focus right")
(define-key *root-map* (kbd "j") "move-focus down")
(define-key *root-map* (kbd "k") "move-focus up")
(define-key *root-map* (kbd "H") "move-window left")
(define-key *root-map* (kbd "L") "move-window right")
(define-key *root-map* (kbd "J") "move-window down")
(define-key *root-map* (kbd "K") "move-window up")
(define-key *root-map* (kbd "s") "vsplit")
(define-key *root-map* (kbd "f") "hsplit")
(define-key *root-map* (kbd "i") "next-in-frame")
(define-key *root-map* (kbd "F20") "other-in-frame")
(define-key *root-map* (kbd "I") "prev-in-frame")
(define-key *root-map* (kbd "E") "fselect")
(define-key *root-map* (kbd "e") "pull-tag vim")
(define-key *root-map* (kbd "C-h") "exchange-direction left")
(define-key *root-map* (kbd "C-l") "exchange-direction right")
(define-key *root-map* (kbd "C-j") "exchange-direction down")
(define-key *root-map* (kbd "C-k") "exchange-direction up")
(define-key *root-map* (kbd "v") "show-window-properties")
(define-key *root-map* (kbd "V") "info")
(define-key *root-map* (kbd "!") "exec")
(define-key *root-map* (kbd "p") "exec rofi -rnow -font 'Terminus 12' -fg '#b9b9b9' -bg '#101010' -lines '20' -columns '3' -hlfg '#101010' -hlbg '#b9b9b9' -bc '#525252' -bw '3'")
(define-key *root-map* (kbd ";") "eval")
(define-key *root-map* (kbd ":") "colon")
(define-key *root-map* (kbd "y") "windowlist")
(define-key *root-map* (kbd "u") "next-urgent")
(define-key *root-map* (kbd "w") "frame-windowlist")
(define-key *root-map* (kbd "A") "time")
(define-key *root-map* (kbd "U") "title")
(define-key *root-map* (kbd "n") "my_little_gnext")
(define-key *root-map* (kbd "C-n") "gnext-with-window")
(define-key *root-map* (kbd "N") "my_little_gprev")
(define-key *root-map* (kbd "C-N") "gprev-with-window")
(define-key *root-map* (kbd "t") "mode-line")
(define-key *root-map* (kbd "r") "iresize")
(define-key *root-map* (kbd "b") "banish")
(define-key *root-map* (kbd "B") "my_little_relwarp")
(define-key *root-map* (kbd "z") "remove")
(define-key *root-map* (kbd "Return") "exec urxvtc")
(define-key *root-map* (kbd "m") "my_little_gselect")
(define-key *root-map* (kbd "M") "tag-window")
(define-key *root-map* (kbd "C-m") "pull-tag")
(define-key *root-map* (kbd "o") "fullscreen")
(define-key *root-map* (kbd "d") "delete-window")
(define-key *root-map* (kbd "Y") "exec /home/lidstah/bin/mlock.sh")

(setf *resize-increment* 40)
(defun update-resize-map ()
"on refait le mapping du mode iresize avec un incrément supérieur"
  (let ((m (setf *resize-map* (make-sparse-keymap))))
    (let ((i *resize-increment*))
    (labels ((dk (m k c) (define-key m k (format nil c i))))
      (dk m (kbd "k") "resize 0 -~D")
      (dk m (kbd "j") "resize 0 ~D")
      (dk m (kbd "h") "resize -~D 0")
      (dk m (kbd "l") "resize ~D 0")
      (dk m (kbd "RET") "exit-iresize")
      (dk m (kbd "ESC") "abort-iresize")
    M)))) (update-resize-map)

(setf (group-name (first (screen-groups (current-screen)))) "pcp")
(run-commands "gnewbg ekl" "gnewbg mail" "gnewbg stm" "gnewbg oth" "restore-from-file /home/lidstah/stumpdump")
(run-shell-command "xsetroot -cursor_name left_ptr")
(run-shell-command "feh --bg-tile lcyanide.png")
(run-shell-command "xmodmap -e 'clear mod4'")
(run-shell-command "xmodmap -e 'keycode 133 = F20'")
(set-prefix-key (kbd "F20"))

;;; vim:filetype=lisp: 

Oui, c'est long! Sachez cependant que la configuration de base est déjà tout à fait utilisable, il suffit de taper Ctrl-t ? (control-t, puis ?) pour avoir une liste des commandes disponibles. Tout le reste, ce n'est que de la customisation et de l'adaptation (par exemple je remappe Ctrl-t sur F20 (que j'ai mappé sur la touche Super (aka Windows), vui, je sais)

De la Doc', encore de la Doc'

Alors comme faire un cours complet sur StumpWM serait un peu long, autant aller voir la doc', à commencer par celle de StumpWM:

Show Comments