Le besoin
Cela fait plusieurs années que j’utilise vim slides pour faire mes présentations et feh pour visionner des images.
J’aime beaucoup vim slides. La capacité d’éditer le contenu en direct et
d’exécuter du code en fait l’outil idoine pour présenter des choses à propos de
systèmes Unix. Mais vim slides a une limite évidente. Puisque c’est vim portant
un costume de powerpoint textuel, il n’est pas possible d’intégrer des images.
Qu’à cela ne tienne, vim slides permet d’exécuter automatiquement une commande
arbitraire, présente dans le texte de la présentation mais masquée, au passage
d’une slide. Il suffit donc d’ouvrir feh, ou tout autre visionneur d’image et
hop on a une présentation visuelle.
Cette technique m’a suffit pour tout un tas de conférence mais pour ma
dernière je voulais
quelque chose d’autre. J’avais envie de pouvoir afficher des images au vidéo
projecteur et d’avoir, en simultané sur l’écran de mon portable, le script
correspondant à cette image. Au passage d’une slide dans vim slides il faudrait
que feh passe à l’image suivante. Le tout reste synchronisé. Dit autrement,
l’idée est de s’approcher la fonctionnalité de vue de présentateurice présente
dans la plupart des logiciels de présentation, avec une vision sur la slide en
cours et les notes associées à côté.
Le diaporama
Rien de plus simple que de faire un diaporama avec feh. Il faut lui donner la
liste des images en argument. Si l’on veut que le tout s’affiche en plein écran
on y ajoute l’argument qui va bien :
feh --full-screen img1.jpg img2.jpg img3.jpg
Si l’on a beaucoup d’images ça va être embêtant de maintenir la liste comme ça. On peut à la place écrire la liste des images dans un fichier texte :
img1.jpg
img2.png
img3.webp
...
Et lancer le diaporama avec :
cat liste-images.txt | xargs feh --full-screen
Il est possible de passer d’une image à l’autre avec les flèches. Reste un
problème, si feh est sur une autre fenêtre que celle des notes il faudra
alt-tab à chaque passage de slide pour passer “manuellement” à l’image suivante.
Heureusement, on peut lire dans le manuel :
SIGNALS
In slideshow and multiwindow mode, feh handles the following signals:
SIGUSR1 Slideshow mode: switch to next image; reload current image if the slideshow consists of a single file. Multiwindow mode: reload all images.
SIGUSR2 Slideshow mode: switch to previous image; reload current image if the slideshow consists of a single file. Multiwindow mode: reload all images.
En logiciel connaissant et honorant les traditions unixiennes, feh est
pilotable avec des signaux. Dans le manuel sur les signaux, (man signal.7) on
trouve :
Standard signals
SIGUSR1 P1990 Term User-defined signal 1 SIGUSR2 P1990 Term User-defined signal 2
Ces signaux, standardisés depuis 1990, ne sont pas réservés pour un usage figé
et commun à tous les processus, comme SIGKILL par exemple. Il revient à chaque
logiciel de les utiliser ou non si besoin. En l’occurrence feh les utilise pour
passer à l’image suivante ou précédente. Ainsi, après avoir lancé feh on peut
écrire :
kill -s SIGUSR1 pid
avec pid étant l’identifiant du processus de feh. Il existe pleins de façons
différentes de l’obtenir, plus ou moins robustes, plus ou moins portables. Sans
trop y avoir réfléchi j’utilise pidof :
kill -s SIGUSR1 $(pidof feh)
Si tout fonctionne bien on voit feh passer à l’image suivante comme par magie.
Superbe.
Vim slides comme pilote
On peut piloter feh à l’aide d’une commande. Plus tôt j’écrivais que vim
slides permettait de lancer, au passage d’une slide donnée une commande
arbitraire donnée. Pourtant ce n’est pas cette mécanique que l’on va utiliser.
En effet, il est possible que l’on veuille revenir en arrière dans les slides.
La synchronisation doit être possible dans les deux sens. Écrire une slide comme
ceci :
commande> Titre de la slide
blabla
blabla
Ne satisfait donc pas notre besoin puisque lorsque l’on va de l’avant commande
doit envoyer SIGUSR1 et SIGUSR2 quand on va en arrière. On va donc à la
place modifier, au moins temporairement, le plugin.
Le plugin est assez simple. Le gros se passe dans le fichier ftplugin/slides.vim. On y trouve notamment
nnoremap <buffer> <PageUp> zkzt
nnoremap <buffer> <PageDown> zjzt
Ces lignes permettent le passage d’une slide à une autre. zkzt et zjzt sont
les raccourcis claviers permettant de passer au “fold” précédent ou suivant, les
folds étant ici nos slides. Il nous suffit alors d’y ajouter nos commandes1
:
nnoremap <buffer> <PageUp> zkzt:!kill -s SIGUSR2 $(pidof feh)<cr><cr>
nnoremap <buffer> <PageDown> zjzt:!kill -s SIGUSR1 $(pidof feh)<cr><cr>
Superbe ! Nos notes et le diaporama sont maintenant synchronisées.
Pourquoi c’est cool
Vim slides étant, évidemment, basé sur vim, il profite de son excellente
intégration avec le reste du système. Cette intégration fait dire à certain·e
que Vim est un éditeur de texte créé avec l’idée que le système Unix dans son
ensemble est un IDE, par opposition à d’autres éditeurs qui deviennent des IDE
en étendant un maximum de fonctionnalités en leur sein2. Lorsque l’on utilise
vim slides on est toujours à quelques touches du clavier prêt d’interagir avec
l’interface universelle des systèmes Unix : la ligne de commande. De son côté
feh se rend accessible depuis la ligne de commande via les signaux.
De ces deux idées très simples, celles de pouvoir se rendre au point de
rendez-vous standard d’Unix et celle de s’ouvrir à ce qui en vient, émerge une
nouvelle fonctionnalité à moindre coût. Il aura suffit de modifier deux lignes
dans vim slides. feh intègre les signaux en seulement une dizaine de lignes de
C3. Dans signals.c :
(sigaddset(&feh_ss, SIGUSR1) == -1) ||
(sigaddset(&feh_ss, SIGUSR2) == -1) ||
[...]
(sigaction(SIGUSR1, &feh_sh, NULL) == -1) ||
(sigaction(SIGUSR2, &feh_sh, NULL) == -1) ||
Et dans main.c :
[...]
if (filelist_len > 1) {
if (signo == SIGUSR1)
slideshow_change_image(winwid, SLIDE_NEXT, 1);
else if (signo == SIGUSR2)
slideshow_change_image(winwid, SLIDE_PREV, 1);
} else {
feh_reload_image(winwid, 0, 0);
}
[...]
Pas de code qui soit spécifique aux notions de slides, de notes, de synchronisation entre les deux. Les deux logiciels ne se connaissent pas, ne se comprennent pas mais peuvent coopérer. Aucun des deux ne doit grossir pour faire advenir notre nouvelle fonctionnalité.
J’intuite que cette façon de développer, consistant à créer des interfaces et laisser aux utilisateurices le soin de satisfaire leurs besoins en combinant les logiciels plutôt qu’en étendant leurs fonctionnalités intrinsèques est bon pour la durabilité. Alors que la réutilisation de code, longtemps un objectif à atteindre pour faciliter et fiabiliser l’ingénieurie logicielle, pose aujourd’hui de gros problèmes de robustesse et de complexité4, la combinaison de deux logiciels via un couplage très faible permis par l’OS semble être une méthode de réutilisation de code qui, bien que n’étant pas équivalente, est moins sensible aux chocs que la première.
Je n’invente rien, cet article est simplement un bon exemple de ce que les créateurices d’Unix envisageaient comme étant de l’ingénieurie capable de contrôler la complexité des logiciels. Rob Pike et Brian Kernighan écrivaient dans The Unix Programming Environment :
Even though the UNIX system introduces a number of innovative programs and techniques, no single program or idea makes it work well. Instead, what makes it effective is an approach to programming, a philosophy of using the computer. Although that philosophy can’t be written down in single sentence, at its heart is the idea that the power of a system comes more from the relationships among programs than from the programs themselves. Many UNIX programs do quite trivial tasks in isolation, but, combined with other programs, become general and useful tools.
Cet argument me semble très proche de l’idée populaire qu’il est préférable de spécifier et d’implémenter des protocoles plutôt que de construire des monolithes logiciels enfermés sur eux même. On pourrait dire qu’ici le “protocole” est l’ouverture sur la ligne de commande.
Alors la prochaine fois que vous avez besoin d’une nouvelle fonctionnalité dans un logiciel, plutôt que de la développer5 demandez-vous s’il ne serait pas préférable de créer une interface générique pour l’utiliser en combinaison avec un autre logiciel.
-
On peut forcément faire plus malin, une jolie fonction, exécuter la commande que si l’on trouve effectivement un processus
fehetc, mais ça fait le taf. ↩ -
exemple : emacs ↩
-
Astuce : si vous êtes sur un système utilisant
aptvous pouvez récupérer le code source compilé pour créer le paquet avecapt source paquet. Plutôt que d’aller chercher sur internet les sources puis de fouiller pour trouver la version packagée par debian etc, j’ai faitapt source feh. ↩ -
Cox, Russ. « Surviving Software Dependencies ». Communications of the ACM 62, nᵒ 9 (2019): 36‑43. https://doi.org/10.1145/3347446. ↩
-
ou de demander à la faire développer ↩