Un petit tour avec XMonad
Posted on 09 Dec 2009 by David in geek, linux
Depuis quelques temps, sous Linux, j'utilisais principalement Enlightenment DR17 comme window manager. Il me permettait de configurer aux petits oignons mon environnement desktop : avec les raccourcis clavier pour manipuler les fenêtres, les fenêtres attachées à un workspace particulier avec la taille, la position, ... sauvegardés. Parmi la configuration d'Enlightenment, un petit module m'intriguait : le module Tiling.
En effet, j'avais déjà entendu parlé des Tiling Window Manager lorsque j'étais encore à l'école mais je n'avais jamais pris le temps de creuser le sujet. Ce coup-ci, avec ce module, je me suis dis qu'il fallait essayer, surtout que je restais avec le window manager que je maitrisais. Seulement voilà, le bousin est pas vraiment stable, et ne propose que peu de fonctionnalités (à ce moment là elles me suffisaient largement).
Qu'est-ce qu'un Tiling Window Manager et pourquoi XMonad ?
Avant de continuer, revenons sur le principe du Tiling Window
Manager :
un window manager, c'est le truc qui dessine un cadre
autour des fenêtres de l'application et qui affiche généralement un
bouton pour fermer, agrandir et réduire la fenêtre, et qui permet de
déplacer et redimensionner cette même fenêtre. Sous Gnome, c'est
Metacity par défaut, sous KDE, c'est KWin. Et pour ceux qui ne le
savait pas, on peut choisir celui qui nous convient le mieux :).
Un Tiling Window Manager a de particulier qu'il est en général
dépourvu de cadre et de bouton et dimensionne les fenêtres
automatiquement selon un layout configuré par nos soins en prenant le
plus d'espace possible (ie. il n'y a pas d'espace non utilisé par une
fenêtre). De plus, il est plus adapté à une utilisation au clavier
qu'à la souris (n'ayez pas peur, avec 3 ou 4 raccourcis, le bénéfice
est déjà accessible).
Après m'être apperçu que le module Tiling d'Enlightenment était pas
vraiment stable, je me suis dit que ce serait pas mal de pousser un
peu plus l'expérimentation de ces fameux Tiling Window Manager (dont
on m'avait d'ailleurs vanté les mérites).
Première étape donc, lequel choisir ? Xmonad, Awesome ou encore Ion ? J'avoue ne pas avoir fait une étude très approfondie pour les comparer et un sentiment très subjectif me poussait vers Xmonad : il y a de la doc, des exemples de fichiers de conf, les avis sur le web sont vraiment bon et puis il est écrit en Haskell, ça faisait un moment aussi que je voulais jeter un oeil à Haskell ;).
La prise en main
Avant de regarder la configuration, je commence par lancer la bête en
utilisant mon fichier ~/.Xsession :
#!/bin/sh
exec xmonad
Une fois la session ouverte, on se retrouve avec un écran vide et on ne sait plus trop quoi faire :).

Le premier raccourci indispensable est celui pour ouvrir un terminal
(la syntaxe que j'utiliserai pour décrir un raccourci clavier est : M
= Alt, C = Ctrl, S = Shift) : M-S-Enter.
Si on lance un terminal, celui-ci prend automatiquement tout l'espace sur l'écran :

Rien d'extraordinaire, mais voyons ce qu'il se passe si l'on ouvre un
second terminal (faut pas s'inquiéter, après on ouvrira autre chose
que des terminaux ;) ) toujours avec M-S-Enter :

XMonad cette fois-ci découpe l'écran en 2 zones égales. Par défaut, la fenêtre active devrait être dans un fin cadre rouge (sur mes screenshot, il sera bleu clair parce que ce n'est plus la config par défaut).
Le second raccourci à connaître est M-TAB pour changer de fenêtre
active, celui-là il est facile et bien connu :).
Si on continue à ouvrir des terminaux et un Emacs, voilà ce que ça donne :

La fenêtre de gauche garde la moitié de l'écran, les autres fenêtres
se partagent l'autre moitié. La fenêtre Emacs qui prend la moitié de
l'écran est la fenêtre principale. Le raccourci M-Enter permet de
sélectionner une autre fenêtre principale (après lui avoir donné le
focus avec M-TAB) qui viendra se placer dans la zone de gauche :

Ce placement des fenêtres est défini par un layout. Le
raccourci M-SPACE permet de switcher parmis les layouts. Celui qu'on
vient de voir s'appelle Tall. Par défaut, il y en a 2 autres qui
sont configurés lorsque l'on switch :
Mirror Tallqui est identique àTallmais en horizontal :

- et
FullScreenqui comme son nom l'indique place la fenêtre principale en plein écran.
Il en existe plein d'autres qui sont customizable simplement.
Il reste deux raccourcis qui peuvent aider (bon OK, on doit avoir
dépasser les 4 raccourcis à connaitre maintenant :) ), qui sont :
M-[1-9] qui permet de switcher d'un workspace à un autre
(ie. au 1er avec M-1, au second avec M-2, ...),
M-S-[1-9] qui permet d'envoyer une fenêtre sur un autre workspace.
Voilà pour la prise en main de XMonad, reste maintenant à le configurer pour qu'il réponde exactement à notre besoin.
La configuration
Pour configurer XMonad, il va falloir faire un petit peu d'Haskell et là, ça fait mal quand on connait pas Haskell. Ce n'est pas aussi évident que je l'imaginais ! Heureusement pour nous, il y a beaucoup d'exemples et pas mal de doc qui m'ont permi de faire tout ce que je voulais.
Voici juste un petit panorama des fonctionnalités que j'utilise :
Placer les fenêtres sur un workspace suivant l'application :
myManageHook = composeAll [ className =? "Gimp" --> doShift "9:gimp" , className =? "Vncviewer" --> doFloat , className =? "MPlayer" --> doShift "6:video" , className =? "xine" --> doShift "6:video" , className =? "Gajim.py" --> doShift "1:im" , className =? "psi" --> doShift "1:im" , className =? "Pidgin" --> doShift "1:im" , className =? "Empathy" --> doShift "1:im" , className =? "Firefox" --> doShift "2:web" , (className =? "Firefox" <&&> resource =? "Dialog") --> doFloat , className =? "Emacs" --> doShift "3:dev" , className =? "Rhythmbox" --> doShift "5:music" , className =? "Amarok" --> doShift "5:music" , className =? "Banshee" --> doShift "5:music" , className =? "Transmission" --> doShift "7:download" , className =? "stalonetray" --> doIgnore , isFullscreen --> doFullFloat , isSplash --> doIgnore ] where copyToWss ids win = map (copyWindow win) ids isSplash = isInProperty "_NET_WM_WINDOW_TYPE" "_NET_WM_WINDOW_TYPE_SPLASH"
Avoir un thème personnalisé :
myTheme = defaultTheme { fontName = myFont, activeColor = "#343434", activeTextColor = "#2B7598", activeBorderColor = "#2B7598", inactiveColor = "#343434", inactiveTextColor = "#FFFFFF", inactiveBorderColor = "#343434" }
Avoir un layout personnalisé suivant le workspace (et donc l'application) :
... , layoutHook = onWorkspace "9:gimp" gimpLayout $ onWorkspace "1:im" imLayout $ onWorkspace "2:web" webLayout $ onWorkspace "6:video" videoLayout $ onWorkspace "8:log" logLayout $ avoidStruts $ layoutHook gnomeConfig ... where gimpLayout = avoidStruts $ withIM (0.11) (Role "gimp-toolbox") $ reflectHoriz $ withIM (0.15) (Role "gimp-dock") $ tabbed shrinkText myTheme videoLayout = smartBorders (tabbed shrinkText defaultTheme) logLayout = simpleDeco shrinkText myTheme $ avoidStruts $ Grid ||| Full ||| Accordion imLayout = avoidStruts $ withIM (1%6) (Or (Or (Or (Role "psimain") (Role "roster")) (Role "buddy_list")) (Role "contact_list")) (tabbed shrinkText myTheme) webLayout = avoidStruts $ tabbed shrinkText myTheme ||| Accordion ||| Grid
Dans l'exemple précédent, l'
imLayoutplace la liste de contacts sur la gauche avec une taille fixée (withIM (1%6)) et les autres fenêtres dans l'espace restant avec des onglets (tabbed shrinkText myTheme).
Autre exemple, le
webLayout(où Firefox est envoyé) utilise un layout avec des onglets, en accordéon ou en grille.


Et des raccourcis clavier personnalisés :
[ ("M-l", spawn "gnome-screensaver-command -l") , ("M-S-q", spawn "gnome-session-save --gui --logout-dialog") -- moving workspaces , ("M-<Left>", prevWS ) , ("M-<Right>", nextWS ) , ("M-S-<Left>", shiftToPrev ) , ("M-S-<Right>", shiftToNext ) , ("M-s", sshPrompt myPromptConfig ) , ("M-m", manPrompt myPromptConfig ) , ("M-S-t", themePrompt myPromptConfig ) , ("M-S-g", windowPromptGoto myPromptConfig ) , ("M-S-b", windowPromptBring myPromptConfig ) , ("M-S-x", xmonadPrompt myPromptConfig ) , ("M-x", runOrRaisePrompt myPromptConfig ) , ("M-C-f", spawn "firefox" ) , ("M-C-e", spawn "e" ) , ("M-C-S-e", spawn "emacs" ) , ("M-C-r", spawn "emacsclient -n -e '(make-remember-frame)'" ) , ("M-C-m", spawn "emacs" ) ]
Dans les screenshots précédent, on voit que j'utilise la barre Gnome comme zone de notification et qui affiche le nom de mes workspaces Xmonad ainsi que le titre de la fenêtre qui a le focus. Pour cela, j'ai développé une petite applet Gnome en Python (forcément ;) ) car je n'arrivais à faire marcher celle-ci. Il reste à envoyer ces infos par XMonad via DBus :
Les sources de l'applet sont dispos par ici. Ce n'est pas encore du tout propre ! Il me reste encore à faire une vraie procédure d'install et un code un peu plus propre :).xmonadAppletLogHook = myLogHook $ defaultPP { ppOutput = \ str -> do let str' = "<span>" ++ str ++ "</span>" spawn("dbus-send --session --type=signal /org/xmonad/Log org.xmonad.Log.Update string:'" ++ str' ++ "'") return () , ppTitle = pangoColor "#2B7598" . shorten 50 , ppCurrent = pangoColor "#2B7598" . wrap "[" "]" , ppVisible = pangoColor "#2B7598" . wrap "(" ")" , ppHidden = wrap " " " " , ppUrgent = pangoColor "red" } pangoColor :: String -> String -> String pangoColor fg = wrap left right where left = "<span foreground=\"" ++ fg ++ "\">" right = "</span>"
- dernière feature, qui pour moi est super importante : une bonne
gestion du multi-écran. Je m'explique, avec beaucoup de window
manager (y compris ceux de Windows et MacOSX), je trouve la gestion
du multi-écran complètement pourrie. Elle ne consiste qu'à agrandir
l'espace de travail du premier écran avec juste une gestion du
maximize des fenêtres pour que celles-ci restent sur un seul
écran. Au-delà, si les écrans n'ont pas la même taille, il existe
une zone non visible où les fenêtres peuvent se ballader, il n'est
pas possible de garder un écran fixe lorsque l'on switch de de
workspace, ...
XMonad associe simplement un workspace à un écran. Ainsi, quand je
switch de workspace, je ne switch que le workspace de l'écran qui a
le focus (
M-Wpour donner le focus au 1er écran,M-Epour le donner au second), plus de zone invisible, les fenêtres sont agrandis/reduites automatiquement suivant le layout et la taille de l'écran. Exactement ce que je voulais en fait :).
Conclusion
Finalement, c'est un aller simple pour les tiling window managers. Je suis vraiment efficace pour jouer avec mes fenêtre, plus d'aller retour entre le clavier et la souris, coupler avec un launcher du type Gnome Do, je peux tout faire avec mon clavier :). Malgré la courbe d'apprentissage d'Haskell et de la configuration, je suis arrivé à faire tout ce que je voulais même si je ne sais toujours pas codé en Haskell :).
Ma config complète d'XMonad est dispo par ici.
Maintenant, le plus dur c'est que j'ai du mal à m'en passer et quand je reviens sous Windows ou MacOS, je pleure !!!