thejournalofsierraleonestudies.com

Journal de bord 7 — Vieux projet autour du Befunge, articles imprimables, et carnet de dev sur Rivalcfg

Bienvenu dans ce septième journal de bord ! Aujourd'hui, je vais vous parler de jsFunge IDE, un vieux projet à moi que j'ai déterré il y a quelques mois maintenant. À vrai dire, je l'avais complètement oublié et j'ai été assez surprit quand quelqu'un est venu m'en reparler ! On va poursuivre …

Bienvenu dans ce septième journal de bord !

Aujourd'hui, je vais vous parler de jsFunge IDE, un vieux projet à moi que j'ai déterré il y a quelques mois maintenant. À vrai dire, je l'avais complètement oublié et j'ai été assez surprit quand quelqu'un est venu m'en reparler !

On va poursuivre ensuite avec la série d'améliorations du blog que j'ai entrepris cet été. Cette fois-ci, je me suis attaqué au CSS dédié à l'impression. Vous obtiendrez à présent un résultat tout-beau-tout-propre si vous souhaitez imprimer — ou transformer en PDF — un article du blog. N'hésitez pas à essayer ! 😉️

Et on finira ce JDB par un carnet de dev sur Rivalcfg. Il sera question du nouveau système de test que j'ai développé afin d'éviter toute régression dans le support des différentes souris !

Bonne lecture ! 😁️

Déterrage d'un vieux projet : jsFunge IDE, un éditeur et interpréteur Befunge 93

Il y a quelque temps, on m'a contacté à propos d'un vieux vieux projet que j'avais complètement oublié : jsFunge IDE. Le code source n'était pas trouvable en ligne et quelqu'un en avait besoin pour compléter le projet pour son propre usage. C'est donc l'occasion de se replonger un peu un peu là-dedans !

Pour ceux qui ne connaissent pas le Befunge, il s'agit d'un langage de programmation exotique, tout comme le Brainfuck, mais contrairement à ce dernier, le Befunge s'écrit en 2 dimensions. Il est important de comprendre à ce stade qu'il s'agit d'un langage de programmation qui existe pour le fun, pas pour faire des projets sérieux ! 😅

Voici un exemple de « hello world » en Befunge :

>25*"!dlrow ,olleH":v
v:,_@
>  ^

jsFunge IDE est donc un éditeur et un débogueur pour Befunge 93, écrit en JavaScript. Je l'avais codé pendant mes études, un jour où je m'ennuyais en cours... Le code n'est pas très beau, pas optimisé du tout et fait des appels à JQuery dans tous les sens... mais il fonctionne.

Exécution d'un petit « Hello World » écrit en Befunge 93 dans jsFunge IDE

Exécution d'un petit « Hello World » écrit en Befunge 93 dans jsFunge IDE

Vous pouvez trouver une version en ligne de l'IDE à l'adresse suivante :

Le problème avec ce projet, c'est que je ne l'ai jamais totalement terminé. Je n'ai notamment pas implémenté certaines instructions du langage, comme & et ~ (respectivement, demander à l'utilisateur d'entrer un nombre ou un caractère ASCII).

Visiblement, j'avais prévu à l'époque de le publier sur Launchpad, mais il semblerait que je n'ai jamais poussé le code dans le projet... et à vrai dire, je n'ai même pas retrouvé le moindre dépôt de code bzr sur ma machine (à l'époque, j'utilisais Bazaar comme gestionnaire de version, je ne me suis mis à Git qu'un peu plus tard).

Du coup, j'ai récupéré le code que j'avais en ligne, je l'ai gitté en l'état (il n'est ni compilé ni minifié, il est donc parfaitement lisible) et je l'ai publié sur GitHub pour les rares personnes que ça pourrait intéresser. Vous pouvez donc le retrouver à l'adresse suivante si vous souhaitez y jeter un œil :

Petit truc rigolo au passage : en faisant quelques recherches, je suis tombé sur une version retravaillée de jsFunge IDE. Quelqu'un a dumpé le code source depuis mon site et a poursuivi le développement de son côté, ajoutant le support des instructions manquantes du langage, mais également le support des instructions de Befunge 97. Il a également ajouté la coloration du flow du programme, un réglage de la vitesse d'exécution, rendu les fenêtres redimensionnables, et même amélioré le terminal ! 🤩️

Visiblement cette version date de 2021 et vous la trouverez sur le site de son auteur à l'adresse suivante :

Je lui ai envoyé un email pour lui dire tout le bien que je pense de ses améliorations et que ça m'a vraiment fait plaisir de voir que ce projet avait intéressé quelqu'un au point qu'il le termine et l'améliore. On verra bien s'il me répondra. 😄️

Les articles du blog sont maintenant imprimables !

Décidément, je profite de cet été pour réaliser les petites améliorations que je souhaitais apporter au blog (et à mes sites) depuis longtemps ! Après avoir refait ma home page, après avoir amélioré la version mobile du blog et rendu la mise à jour des articles plus visibles, je m'attaque à rendre les articles du blog imprimables !

Jusqu'à présent, si vous essayiez d'imprimer ou de générer un PDF depuis un article du blog, le résultat n'était clairement pas folichon... 😅️

Version imprimée d'un article du blog avant l'ajout de CSS dédié à l'impression

Version imprimée d'un article du blog avant l'ajout de CSS dédié à l'impression

Comme vous pouvez le constater sur les pages ci-dessus, le premier problème, c'est qu'il y a plein d'éléments inutiles autour de l'article. On se retrouve avec la barre supérieure sur toutes les pages, et en plus elle passe par-dessus le contenu, donc on perd une partie du texte. On a également la barre de menu et de recherche du blog qui n'ont aucun intérêt ici, et à la fin on a les commentaires (avec le formulaire et tout) ainsi que tout le bloc footer avec le « À propos », la liste des catégories, etc. qui ne sont pas vraiment utiles non plus...

La première étape est donc de virer tout ça pour l'impression. Rien de bien compliqué, il suffit de rajouter la propriété "display: none;" sur tout ce qu'on ne veut plus voir. Par exemple, pour masquer la barre supérieure, il suffit d'ajouter le code suivant à la feuille de style CSS :

@media print {
.page-header {
display: none;
}
}

Second problème, les marges de la page. Vu que les blocs d'articles ont été designés comme des feuilles de papier avec des marges assez larges, on se retrouve avec une double marge : celles autours du texte de l'article, et celles de la page imprimée.

Pour changer ça, on va supprimer les marges horizontales de la page pour faire en sorte que le contenu paraisse « border-less ». Ainsi, l'image de couverture et les blocs (code, note, warning,...) toucheront les bords de la page. Mais pas de panique, les marges internes des blocs feront en sorte que le texte reste suffisamment éloigné du bord pour ne pas se retrouver tronqué par une imprimante qui ne saurait pas imprimer sans marge. En gros j'avais déjà prévu les fond perdu à l'intérieur des blocs quoi. 😄️

On va également en profiter pour afficher quelques informations comme le numéro des pages dans les marges. Voici le code CSS — dûment commenté — permettant de faire tout ça :

@page {
/* 40px de marge en haut pour que le texte ne soit pas collé en haut de la
* page, et 80px en bas pour la même raison, mais on laisse plus de place
* pour afficher les infos et les numéros de page.
*/
margin: 40px 0 80px 0;
/* Dans le coin en bas à gauche, on affiche le nom de l'auteur, la licence
* et l'URL du blog, en tout petit.
*/
@bottom-left {
content: "Fabien LOISON • CC BY-SA 3.0 • blog.flozz.fr";
padding-left: 50px;
color: #87969d;
font-size: 7pt;
}
/* Dans le coin en bas à droite, on affiche le numéro de la page courante.
* On utilise pour cela un compteur CSS.
*/
@bottom-right {
content: counter(page);
padding-right: 50px;
color: #87969d;
font-size: 12pt;
}
}
/* Pour la première page, on supprime la marge du haut pour que la couverture
* de l'article soit collée au bord supérieur de la page.
*/
@page:first {
margin-top: 0;
}

À tout ça j'ai rajouté quelques ajustements supplémentaires :

Voilà ce que ça donne à présent avec les modifications décrites ci-dessus :

Version imprimée d'un article du blog après l'ajout du CSS dédié à l'impression

Version imprimée d'un article du blog après l'ajout du CSS dédié à l'impression

À noter que tous les navigateurs ne sont pas égaux dans leur gestion de l'impression.

Niveau rendu, j'ai dans mon cas obtenu les meilleurs résultats avec Chromium (snif), suivi de près par WeasyPrint dont le seul tord est de ne pas encore supporter la propriété CSS "clip-path" que j'utilise pour biseauter le fond du titre des articles [mais bon ça va aller, je devrais pouvoir vivre sans 😛️]. Firefox arrive quant à lui bon dernier, car il ne tient pas compte des textes ajoutés dans les marges de la page et qu'il m'ajoute un espace que j'arrive pas à supprimer entre le titre et le premier paragraphe des articles... 🤷️

Comparaison du rendu imprimé d'un article : Chromium vs WeasyPrint vs Firefox

Comparaison du rendu imprimé d'un article : Chromium vs WeasyPrint vs Firefox

Tant qu'à lister les différences entre les moteurs de rendu, j'accorde un point bonus à WeasyPrint, car il est le seul à intégrer le sommaire dans les métadonnées du document !

Lecteur de PDF affichant le sommaire de l'article

Lecteur de PDF affichant le sommaire de l'article

Un dernier élément qui change énormément d'un navigateur à l'autre, c'est le poids du document de sortie. Ici, c'est WeasyPrint qui s'en sort le mieux, talonné par Chromium. Firefox est quant à lui loiiiiiiiin dernière... 🙁️

Navigateur Poids du PDF
WeasyPrint (future v70) 1,6 Mio
Chromium (v151) 1,7 Mio
Firefox (v153) 3,4 Mio

Voilà, avec tout ça, les articles sont enfin présentables si vous souhaitez les imprimer ou les transformer en PDF pour les conserver sur la durée ! 😁️

Carnet de dev : Amélioration des tests de Rivalcfg

Rivalcfg et un outil en ligne de commande et une bibliothèque Python permettant de configurer les souris gaming de la marque SteelSeries sous Linux, macOS et Windows. Si vous ne connaissez pas le projet, vous pouvez lire l'un des articles que je lui ai dédiés ou visiter son site Web officiel.

Après un gros coup de motivation sur le projet en début d'année, j'avais enchaîné sur un truc que je voulais faire depuis longtemps : améliorer les tests automatisés des différents modèles de souris supportés. J'ai donc travaillé là-dessus de fin mars à début mai... et puis j'ai un peu tout laissé en plan au moment où je me suis retrouvé en congé. J'avais besoin d'une pause, pendant laquelle j'ai préféré jouer et écrire plutôt que coder.

Je suis en train de me remettre tranquillement sur le projet et je reprends donc le sujet. Mais avant de remettre les mains dans le code, je voulais d'abord faire un petit point sur la manière dont sont testées les souris actuellement et ce qui va changer avec ce que je suis en train de développer.

Jusqu'à présent, les différentes options des souris étaient testées individuellement, de manière isolée, à l'aide de Pytest. Par exemple pour tester le changement de la couleur d'une LED, on va fabriquer le paquet permettant de changer cette configuration, on simule son envoi, puis on l'intercepte et on le compare avec un paquet de référence.

Le souci, c'est que quand vous configurez votre souris en utilisant la ligne de commande, et bah en réalité Rivalcfg ne va pas forcément envoyer uniquement le paquet pour modifier la couleur de la LED. Il va aussi envoyer un second paquet pour persister les paramètres dans la mémoire interne de la souris. Il serait donc pertinent de prendre en compte TOUS les paquets envoyés à la souris pour être sûr qu'on ne change pas le comportement du logiciel par accident.

Et puis il y a un autre problème, qui ne se pose pas encore, mais qui se posera à l'avenir : certaines souris ont besoin que plusieurs paquets de données soient envoyés pour configurer une unique option. Par exemple l'Aerox 9 a besoin de plusieurs paquets pour configurer ses boutons et c'est pour cette raison que cette option n'est pas encore supportée... À l'avenir, il va falloir implémenter le nécessaire pour ces configurations multi-paquets, et il faudra donc être capable de tester l'envoi d'une séquence de plusieurs paquets à la suite.

Il serait bien sûr possible de faire tout ça directement avec Pytest, mais les tests seraient en pratique assez lourds à écrire. C'est d'ailleurs déjà plutôt verbeux pour tester un seul paquet, comme vous pouvez le constater avec cet extrait permettant de tester la configuration de la couleur d'une LED sur une Rival 100 :

import pytest
from rivalcfg import usbhid
from rivalcfg import mouse
from rivalcfg.devices import rival100
from rivalcfg import mouse_settings
class TestDevice(object):
@pytest.fixture
def mouse(self):
settings = mouse_settings.FakeMouseSettings(
0x1038,
0xBAAD,
rival100.profile,
)
return mouse.Mouse(
usbhid.FakeDevice(),
rival110.profile,
settings,
)
@pytest.mark.parametrize(
"value,expected_hid_report",
[
("#ABCDEF", b"\x02\x00\x05\x00\xab\xcd\xef"),
("red", b"\x02\x00\x05\x00\xff\x00\x00"),
],
)
def test_set_color(self, mouse, value, expected_hid_report):
mouse.set_color(value)
mouse._hid_device.bytes.seek(0)
hid_report = mouse._hid_device.bytes.read()
assert len(hid_report) == 1 + 1 + 32  # report_type + report_id + data
assert hid_report.startswith(expected_hid_report)

J'ai donc décidé de développer une solution de test sur mesure, permettant de valider le comportement global de bout en bout, avec une syntaxe spécifique, plus concise et plus pratique à écrire. Voici à quoi ça ressemble pour tester la même chose que le code Python / Pytest ci-dessus :

:1038:1702 SteelSeries Rival 100
--color ABCDEF
< 02 00 | 05 00 AB CD EF
< 02 00 | 09 00
--color red
< 02 00 | 05 00 FF 00 00
< 02 00 | 09 00

C'est tout de suite plus lisible et plus court, même si j'imagine que ça reste un peu obscur si on ne connaît pas le fonctionnement de Rivalcfg et des bidules USB HID. Mais globalement on peut reconnaître les options de la ligne de commande de Rivalcfg, et juste en dessous, chaque ligne qui commence par un chevron (<) correspond un paquet qui devrait être envoyé à la souris.

Je vous en dis pas plus ici sur le format de ce fichier, mais si vous voulez creuser davantage, sachez qu'il est documenté dans la doc de Rivalcfg :

Je pensais au départ convertir tous les tests Pytest des devices dans ce nouveau format, mais j'ai pas le courage de faire tout ça d'un coup. Donc je repasserai sur les tests des différentes souris au fur et à mesure, quand j'aurai des modifs à faire dessus. Et j'utiliserai bien sûr directement ce nouveau format pour tester les nouveaux modèles de souris.

Et voilà !

Et voilà, c'est tout pour aujourd'hui. Vous constaterez que cette fois-ci il n'y a pas eu de recommandation d'articles... C'est pas que j'ai rien lu d'intéressant ces derniers temps, mais j'ai juste pas eu le temps de rédiger de notes sur mes lectures. Je vais essayer d'y remédier pour le prochain JDB. 😄️

À bientôt o/