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 :).

Écran par défaut d'XMonad

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 :

XMonad avec une seule fenêtre

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 avec deux fenêtres

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 :

XMonad avec cinq fenêtres

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 :

XMonad avec cinq fenêtres et une nouvelle fenêtre principale

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 Tall qui est identique à Tall mais en horizontal :
XMonad avec le layout MirrorTall
  • et FullScreen qui 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'imLayout place 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).

    Mon imLayout

    Autre exemple, le webLayout (où Firefox est envoyé) utilise un layout avec des onglets, en accordéon ou en grille.

    WebLayout en onglets

    WebLayout en accordéon

    WebLayout 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 :

    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>"
    
    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 :).
  • 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-W pour donner le focus au 1er écran, M-E pour 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 !!!

Les rares abonnés au flux RSS de mon blog l'auront remarqué, je ne poste pas vraiment régulièrement (j'espère m'améliorer, j'ai plein de sujets de geek à partager :) ). En fait, jusqu'à maintenant, je passais plus de temps à mettre à jour Wordpress pour éviter d'être à la traine niveau correction de failles de sécu. Si seulement c'était juste Wordpress, les plugins que j'utilisais devaient eux aussi subir une mise à jour pour suivre l'évolution des APIs de Wordpress. Et tout ne se passait pas toujours très bien :(.

Comme je suis dans ma période "revenons aux sources, pourquoi ai-je besoin de m'encombrer avec ce #$%@!!. de XXX ?" (remplacer XXX par Wordpress, Eclipse, OpenOffice ou tout autre soft qui finit immanquablement en usine à gaz), et que je suis un peu un geek sur les bords, je me suis dis que j'allais essayer Jekyll.

Qu'est ce que Jekyll ?

C'est un petit outil écrit en Ruby qui à partir de templates Liquid et de fichiers Markdown (ça ressemble à une syntaxe de Wiki : voyez le source de ce post) génère un blog en statique que je dépose ensuite derrière un Apache.

Par où commencer ?

Je n'ai toutefois pas fait table rase, j'ai repris l'existant de mon Wordpress en commençant par migrer le système de commentaire sur le service Disqus à l'aide du plugin Wordpress qui va bien. Oui, un site avec des pages HTML en statique va avoir du mal à conserver les commentaires des posts! Je délègue donc, tout en me permettant une migration des commentaires tout en douceur :

  • Le plugin Disqus pour Wordpress a une fonction de migration,
  • Le nouveau site inclus ensuite Disqus avec les quelques lignes de Javascript qui vont bien.

Ensuite, un petit script permet d'extraire les posts de la base de données Wordpress pour les transformer en fichier Markdown. Le résultat n'est pas parfait, mais ça permet de débuter.

Le templating

Une fois les posts migrés, il reste encore à écrire les templates dans lesquels les articles (et autres pages) s'insèreront.

Rien de magique, du HTML, du CSS, une pincée de Javascript et un peu de Liquid pour faire quelques boucles, quelques conditions :

<div id="recent_posts" class="sidebar_section">
  <h4 class="sidebar_title">Recent posts</h4>
  <div class="sidebar_content">
    <ul>
    
    <li><a href="/2009/12/09/un-petit-tour-avec-xmonad">
          Un petit tour avec XMonad
    </a></li>
    
    <li><a href="/2009/11/09/migration-wordpress-jekyll">
          Migration de Wordpress vers Jekyll
    </a></li>
    
    <li><a href="/2008/05/24/utiliser-time-machine-avec-un-lecteur-reseau">
          Utiliser Time Machine avec un lecteur réseau
    </a></li>
    
    <li><a href="/2008/03/12/gerer-la-diffusion-de-la-musique-de-son-neuros-osd-depuis-son-pc">
          Gérer la diffusion de la musique de son Neuros OSD depuis son PC
    </a></li>
    
    <li><a href="/2008/03/10/first-beta-of-jabber-mail-component-v03">
          First beta of Jabber Mail Component v0.3
    </a></li>
    
    </ul>
  </div>
</div>

Et donc ?

Oui, et donc ? Et bien je dirais que je me suis bien fait plaisir ;), j'ai passé un peu plus de temps que prévu quand même vu le niveau HTML+CSS que j'avais.

Le blog est versionné dans un repo Git, donc plus de problème de backup de base MySQL, de migration, de restauration approximative, un git clone suivi de rake suffit (j'utilise Rake pour générer quelques pages supplémentaires, et lancer la génération du site par Jekyll).

<< Archives | RSS Feed