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

Après avoir crashé le disque dur de mon Macbook pro (1 mois après l'avoir reçu :( ), je me suis dis que ce serait pas mal de faire des sauvegardes pour la prochaine fois où ça m'arrivera (si, si ça m'arrivera encore). Je me suis donc intéressé à Time Machine. Avec un NAS de près de 1 To sur mon réseau, ça doit pouvoir le faire. Seulement voilà, Time Machine ne me permet pas de séléctionner un lecteur réseau (via SMB ou AFP). Un petit coup de Google, et je trouve la réponse, dans un terminal :

$ defaults write com.apple.systempreferences \
    TMShowUnsupportedNetworkVolumes 1

Et voilà, maintenant TM veux bien me laisser choisir un montage réseau (SMB et AFP), seulement quelques secondes après le démarrage du backup voilà ce qu'il me sort :

Time Machine Error - The backup disk image could not be created.

Encore, une fois après quelques recherches sur le web voilà la solution pour forcer la main à notre amis TM :

  • Lancer un backup en surveillant le répertoire du montage réseau. TM créé un répertoire avec l'extension .sparsebundle (de la forme ${HOSTNAME}_${adresse MAC de l'interface en0 sans les :}.sparsebundle). Copier le nom de ce répertoire.

  • Lancer l'utilitaire disque et créer une nouvelle image disque vide au format "image disque de faible densité" de la taille à réserver pour le backup et du nom copier à l'étape précédente (avec l'extension .sparsebundle). Le fichier créé devrait faire ~114 Mo.

  • Déplacer ce fichier à la racine du montage réseau (là où TM a tenté de créer le backup)

  • Relancer le backup de TM et tout devrait aller comme sur des roulettes :)

Depuis quelques temps déjà, le Neuros OSD intègre xmms2 comme player pour la musique. Seulement voilà, gérer une playlist avec une télécommande infrarouge est quelque peu fastidieux. C'est là que l'aspect daemon d'xmms2 (oui, en vrai xmms2 n'est qu'un démon) entre en jeux. Il est possible de le piloter via le réseau.

Voici donc la marche à suivre pour faire marcher le tout avec le seul client potable (qui sache accéder au daemomms2 via le réseau) Esperanza :

  • Il faut commencer par se connecter en telnet sur l'OSD.

    login : root
    mot de passe : pablod
    
  • Une fois connecté, il faut activer l'écoute sur un port réseau avec la commande suivante :

    $ xmms2 config core.ipcsocket \
    'unix:///var/tmp/xmms-ipc-root;tcp://0.0.0.0:4242'
    
    En fait cela active non seulement l'écoute réseau sur le port 4242, mais garde aussi l'ancienne configuration unix:///var/tmp/xmms-ipc-root.
  • Tout les clients xmms2 que j'ai pu tester ne sont capable d'ajouter que des fichiers locaux (au client) et donc illisible depuis l'OSD (ou du moins pas avec le chemin de fichier). Pour résoudre ce problème, il faut donner à xmms2 un (ou plusieurs) répertoire(s) contenant l'ensemble des fichiers musicaux que l'on voudra jouer afin qu'il créé une base de données des fichiers musicaux :

    $ xmms2 config medialib.path \
      /mnt/tmpfs/media/SD-card/medialib.db
    $ xmms2 mlib addpath /mnt/tmpfs/media/nas/Music
    

    La première ligne sert à déplacer la base de données SQLite que va créer xmms2. C'est nécessaire car la taille de ce fichier va grossir et ne pourra pas rester dans le répertoire par défaut (/var/tmp/xmms2/medialib.db). Chez moi, je l'ai donc placé sur une carte SD qui reste constamment branchée sur l'OSD.

    La deuxième ligne ajoute un chemin où chercher les fichiers musicaux à la base de données xmms2. Tout les montages visible depuis l'interface de l'OSD devraient être montés dans /media/tmpfs/media.

  • Il ne reste plus qu'à se connecter au daemon avec Esperanza :

Connection Esperanza
Medialib browser Esperanza
Medialib Esperanza

Voilà, maintenant, plus besoin d'allumer sa TV et de se faire @#@$! avec la télécommande pour écouter de la musique sur son Neuros.

Esperanza permet d'ajouter des flux shoutcast à la playlist xmms2 mais pour une raison encore inconnu, le xmms2 du neuros ne semble pas d'accord. Peut-être manque-t-il un plugin ? La solution peut-être dans un autre post ...

Update: il semble que le support shoutcast ait été ajouté sur la branche trunk (qui est aussi celle qui apportera la nouvelle interface QT).

I have released a first beta of Jabber Mail Component of its third version. Here are the feature introduce by this version:

  • SMTP support for sending emails
  • Ad-hoc commands for admins and users
  • XEP-0033 support to reply emails received through JMC
  • IMAP folder tree browsing

It can be downloaded from here and to install it, follow this documentation (configuration and usage documentations will be added soon).

Before releasing the final 0.3 version, I need your help to track bugs and to update translation (corrections in the English translation are also welcome :) ).

<< Archives | RSS Feed