Friday, September 4, 2026

Waking up a vomiting lion

Back in April 2025 I spent a weekend with Codex making a small Godot game called LeLion. The pitch fits in a sentence: a lion head flies over a grey city, picks up colour dots, and vomits a rainbow to paint the buildings, whilst a flying saucer and a ladybug try to kill it. Nine commits in ten days, most of them titled « On continue » (we keep going), then I put it down. The thing more or less ran, the painting worked through a shader and a pixel mask, and that was that.

Yesterday (yep, 17 months as the crow flies) I picked it up again with Claude Code, and by the end of the day there was a finished web game, and a GBA ROM (for #retrogramming).



Waking up the prototype

The first thing Claude did was read everything and tell me, plainly, what state it was in. Two signal connections pointed at methods that didn't exist (one of them because of a capital letter). Three dated copies of the main script lived next to the real one, because in 2025 I knew git but apparently didn't bother. The victory condition counted the transparent sky as something to paint, so it was unreachable. Seven emitters of 7,500 GPU particles each. One saucer, one ladybug, to debug, and then nothing ever again.

We agreed on phases: clean up, structure, gameplay, polish. Claude installed Godot through Homebrew and drove everything headless. It wrote a smoke test as a SceneTree script that loads the main scene without a display, unlocks a colour, makes the lion vomit on the city, provokes a defeat, then a victory, and asserts on the way. That script grew all day, from 12 assertions in the morning to 136 yesterday evening, and it caught a good half of the regressions before I even saw them.

A few Godot gotchas for anyone trying the same (but I'm sur Claude will reengineer it anyway, for a few tokens). godot --check-only doesn't load autoloads, so every reference to a singleton is flagged as an error; ignore it and trust a real run. A --script harness compiles before the autoloads exist, so you fetch them with root.get_node("GameState") rather than by name. And when you run the game windowed to take screenshots, wait in seconds with create_timer, not in frames, because without vsync the OpenGL window runs at several hundred frames per second and your "60 frames" is a quarter of a second.

Those screenshots helped Claude see the mess the project was in. The colour pickup's collision shape was offset by (195, 54) from its own origin, so it was never where the code put it. The lion's mouth marker sat at (186, 191) on a 128 pixel sprite, well outside the lion, and the vomit was a handful of one pixel particles (I was beginning with Godot and Codex, OK?). Claude located the actual mouth by scanning the sprite for pink pixels, moved the emitter there, and placed the paint zone at the landing point of a projectile fired at 45 degrees with the same velocity and gravity as the particles.

Making it a game

From there it was a long list, and I'll go fast. Three difficulty modes (easy with three hearts and heart pickups, normal with three hearts, hardcore with one). Three levels, two of them procedurally generated skylines, plus a title screen with best times. A rainbow star that doubles the jet for eight seconds. A pause menu. Screen shake, a red flash and a knockback when you're hit, confetti when you win, paint drips running down the buildings, a bit of inertia and lean on the lion. A procedural chiptune loop written by a Python script that outputs WAV files, later split into three synchronised tracks so the arpeggios and the melody come in as the town gets painted, with a minor key theme for the boss level. Touch controls that only appear on touch screens. Settings with volumes, fullscreen, a CRT filter and a language toggle, the whole game translated to English through Godot's CSV translations. A READY? VOMIT! intro, a CONTINUE? countdown when you die, an animated end-of-level summary, an arcade mode chaining the nine level and difficulty combinations, and an attract mode that plays a demo if you leave the title screen alone for fifteen seconds. CI on GitHub Actions builds the web export and deploys it to GitHub Pages on every push.

The boss deserves a paragraph. The Village level was too easy, so we added a giant painter's head, 65 percent of the screen tall, who comes in from one side, walks to the centre, pauses, walks back, and returns from the other side; you paint the half he leaves free. There was no sprite, so Claude drew an SVG silhouette (beret, nose, eye, brush with a red tip) and we iterated on the moustache three times: first it looked like two pieces, then it curled up, then I asked for it to curl down and hold the brush. The collision polygon is generated from the SVG's alpha at runtime, so replacing the file replaces the hitbox. Then a bug I noticed: the sprite flipped when the boss came from the right, but the collision didn't. Fixed together. Progress is now a real coverage measurement (the paint mask downscaled to a grid every 200 ms), which made 85 percent honest and, as a consequence, hard. Normal now asks for 90, hardcore for 95.

And then, a GBA ROM

Late in the day I asked whether the game could exist as a Game Boy Advance ROM, with the constraints of the era. Godot doesn't export to GBA, so the honest answer was a rewrite in C. Claude proposed a scale: 2000 by 648 pixels becomes a 480 by 72 town scrolling on a 240 by 160 screen, the 128 pixel lion becomes 32 by 32, the jet lands 35 pixels ahead and 45 down instead of 136 and 190, the boss becomes a composite of four hardware sprites. We created a second repository and went.

No toolchain got installed on my Mac. The build runs inside the official devkitPro Docker image (make docker), and the same image builds the ROM in CI. For tests, since the Homebrew build of mGBA was linked against an ffmpeg that no longer exists, Claude rebuilt it from source and then wrote a 150 line C harness on top of libmgba: it loads the ROM, runs frames, presses keys, reads memory, pokes memory, and dumps the screen as PPM. The game writes a small debug block at a fixed address in EWRAM (lion position, camera, progress, lives, state), and the test scripts assert on it. Screenshots come out as PPM, a tiny Python script turns them into PNG, and I got to look at them.

The town is a mode 4 bitmap (8 bit paletted), painted directly in an EWRAM buffer and copied into the visible page with one DMA per row. The sky gradient is drawn the same way, with fixed-source DMA fills. Sprites do the rest: the lion, ten drops along the arc, pickups, the saucer, the ladybug, the painter in four parts. Music runs on the actual Game Boy channels (two squares, the wave channel with a triangle for the bass, noise for kick and hat), sequenced from the same note data as the Godot version, and the sound effects are 8 bit samples on the two DirectSound FIFOs. Records and settings go to cartridge SRAM. Seven phases, thirteen commits, four tagged releases, all built and tested by CI, the ROM attached to each release.

The GBA taught us things the Godot version couldn't. libtonc's clamp() excludes its upper bound, so the lion stopped one pixel short of the edge and the camera two. RGB15() is a function, not a constant, so it can't initialise a static table. Non-square hardware sprites use the size code 64 for both 64 by 32 and 32 by 64; I'd used 32 and the painter lost his collar. And two bugs I found by actually playing in mGBA: pressing left on the difficulty row showed an empty label, because key_hit() returns the key's bitmask and not a boolean (right is 16, left is 32, and 16 modulo 3 happens to be 1, which is why right worked). Then the game lagged with four colours or more whilst vomiting: the paint stamp did three software divisions per pixel on a processor with no divide instruction, up to 441 pixels a frame. A lookup table and a multiply fixed it, the hot loops moved to IWRAM as ARM code, and a test now holds A with seven colours for 240 frames and checks that the frame counter advanced by exactly 240.

I asked whether it was worth optimising more, in case we add things. Measured, not guessed: a probe records the scanline reached when the frame's work is done. Worst case in play, boss on screen, seven colours, vomiting and scrolling, is 41 percent of the frame. So no. A test will go red if a future feature pushes that past three quarters.

Last thing, since I thought of it: the released ROM read two test hooks from the debug block, so anyone with an emulator's memory editor could poke one word and win. Those hooks now compile only into a separate lelion-debug.gba; CI tests that one and publishes the clean one, with a final check that the release ROM ignores the pokes. A memory editor can still write your lives directly, of course. That's true of every game ever made.

The web version is at w3cdotorg.github.io/LeLion, the GBA ROM in the releases of the second repo; mGBA runs it, and it should run on a flash cart, though I haven't tried one yet (will try today on a RG35XXSP — not an affiliated link)). If you do, or if the chiptune sounds wrong on real hardware (I haven't heard it on anything but an emulator), tell me in the comments.

Session ledger

Since I'd like to be transparent with costs in tokens, here is the actual count, pulled from the local transcript Claude Code keeps for the session (an 18 MB JSON file, which tells its own story). One session, one model (Claude Fable 5.1), from 07:37 on the 4th to a last exchange the next morning, so roughly fifteen hours of actual work with a night in between.

My messages39
Model calls280
Tool calls319 (224 shell commands, 78 file or image reads, the rest browser automation)
Output tokens561,242
Input tokens, fresh7,700
Input tokens written to the cache2,450,708
Input tokens read from the cache125,947,570

The big number is the one to read carefully. A coding agent re-reads its whole context at every call, so 280 calls over a context that ended up around half a million tokens gives a hundred and twenty-six million tokens of cache reads. Those are billed at a fraction of fresh input, which is the whole point of the cache, and they don't mean the model "read" that much new material; it kept re-reading the same day. The genuinely new material is the two and a half million tokens written to the cache (my messages, every tool result, every screenshot description) and the half a million tokens it produced: the code, the commands, the commit messages, the screenshots' analyses, and the explanations you've just read a summary of. For scale, that output is about the length of four novels, most of it GDScript, C, Python and shell, and a fair amount of it thrown away along the way.

Sunday, August 9, 2026

"It's a Unix system!", or, Porting fsv to macOS with Metal



If you've seen Jurassic Park, you've seen fsn: the 3D file browser Lex uses to lock the raptors out of the control room. It was a real program, written at SGI for IRIX workstations, and its source code never left the building. What the rest of us got, a few years later, was fsv, a GTK+/OpenGL clone by Daniel Richard G. that lays out your directories as geometry you can fly around in.

I wanted it on my Mac. The problem: fsv is a GTK3 + OpenGL application, Apple deprecated OpenGL back in 2019, and I did not fancy an XQuartz-flavoured build of a 3D flythrough. So it got a real port instead. The code lives at github.com/w3cdotorg/fsv (the metal-port branch, which is the default one), with prebuilt binaries on the Releases page.

What changed under the hood

The renderer is now SDL3’s GPU API (Metal on macOS, Vulkan-capable elsewhere), with the shaders ported to modern GLSL and compiled offline to MSL and SPIR-V. The GTK interface was rebuilt in Dear ImGui (menu bar, docked directory tree and file list, colour setup, properties dialogs), and the core of fsv (filesystem scanning, geometry layout, camera math) was extracted into a headless library with its own little CLI and unit tests. Picking is done the modern way, with a colour-ID offscreen readback, so hovering and right-clicking resolve the actual node under the cursor. And while the SDL/Metal frontend is the point of all this, the legacy GTK/OpenGL one still builds and runs on Linux; CI keeps it honest on every push.

Along the way, a surprise: nvstore.c, the settings-persistence backend, turned out to be a complete stub upstream ("ALL THIS HAS YET TO BE IMPLEMENTED!"), on both frontends. fsv has been silently discarding every colour-setup change since the nineties. It now writes ~/.fsvrc for real.

The FSN mode

The original SGI program was never released, but it left traces: screenshots, a 1992 README, and two SGI patents (US5555354 for the flight navigation, US5861885 for the selection spotlight). From those, the port grew a fourth visualisation mode (--fsn, or Vis → FSN) recreating the film’s look: gradient sky over the green ground plane, directory pedestals whose height tracks subtree size, file boxes coloured by age with the classic legend bar at the bottom, and white wires connecting parent to child. Middle-drag flies the camera around, patent-style. Double-clicking a pedestal warps down onto it; double-clicking a file opens it with the default app, which is exactly how Lex locked that door.

Building it

Everything comes from Homebrew:

$ brew install glib cglm meson ninja pkgconf sdl3

$ git clone https://github.com/w3cdotorg/fsv.git && cd fsv

$ meson setup builddir

$ ninja -C builddir

$ ./builddir/src/sdl/fsv ~/some/directory

That is the whole build. packaging/macos/make-bundle.sh wraps the binary into a double-clickable .app, and there is an Xcode project (packaging/xcode) for those who would rather hit Cmd-B. The prebuilt binaries from the Releases page still expect Homebrew’s glib and sdl3 at runtime (the dylibs are not bundled yet; it is in the TODO), so brew install glib sdl3 first, or the loader will complain.

On Linux, the classic GTK frontend builds with the usual libgtk-3-dev / Mesa dev packages (meson setup builddir -Dfrontend=gtk). The new SDL frontend works there too, provided you have SDL3 ≥ 3.2 (most distributions do not package it yet; Ubuntu gets it in 25.10).

How it was made

Full disclosure: the port was done in pair with Claude, Anthropic’s coding agent, task-by-task. Every step, decision, dead end and bug found along the way is written down in docs/PORTING.md, which ended up being a document I enjoy re-reading more than some of the code. The leftover rough edges (including the rewrite of the MapV treemap layout that the original author was already wishing for in his 1999 TODO) are collected in TODO.md.

That’s it! If you take it for a spin, or if you remember fsn from an actual Indigo, let me know in the comments (or on the GitHub issues).

Monday, July 10, 2023

Compiling Python 3.11 in CentOS 7 with OpenSSL

In the beginning, I only wanted to update a Pip package. But I might as well relate that here, just for posterity.

So, the pip package gave me a Python error, because Centos only shipped with Python 2.7. I told myself 'OK, let's build Python 3.11, easy peasy', but it all went down from here.

It compiled mostly OK, but after that trying to use pip3.11 threw an SSL error, the famous:

pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available.

Collecting <package>

  Could not fetch URL https://pypi.python.org/simple/<package>/: There was a problem confirming the ssl certificate: Can't connect to HTTPS URL because the SSL module is not available. - skipping

  Could not find a version that satisfies the requirement <package> (from versions: )

No matching distribution found for <package>

OK, let's update OpenSSL then! I grabbed the openssl-3.1.1.tar.gz package from GitHub, configured and compiled it... And Python still didn't grab the SSL libs. I tried with and without --enable-optimizations, with and without --with-openssl=/usr/, to no avail.

So, what worked? 

Well, first, uninstalling that openssl 3.1.1. Then, installing the EPEL (Extra Packages for Entreprise Linux), and from there install the openssl11 and openssl11-devel (At last I had working SSL libs available).

I don't know if that helped, but I also followed the instructions here to upgrade GCC, working with the more up-to-date devtoolset-11 :

sudo yum install centos-release-scl

sudo yum install devtoolset-11-gcc*

source /opt/rh/devtoolset-11/enable


Now, the BIG thing: you have to tell Python where your SSL libs and packages are. So the full command that worked and finally got all the SSL libs detected was:

./configure --with-openssl='/usr/' --with-ssl-default-suites=openssl CFLAGS="-I/usr/include/openssl11" LDFLAGS="-L/usr/lib64/openssl11 -lssl -lcrypto" --with-openssl-rpath=auto

(You can check for these with pkg-config --cflags openssl11 and pkg-config --libs openssl11.)

The SSL output from the ./configure command:

checking for openssl/ssl.h in /usr/... yes

checking for --with-openssl-rpath... auto

checking whether OpenSSL provides required ssl module APIs... yes

checking for --with-ssl-default-suites... openssl

checking for stdlib extension module _ssl... yes


Finally, in the Python directory:

sudo make clean && sudo make altinstall

In order to keep the original Python installation.


You can then choose to load your new python binary as default by linking /usr/local/bin/python3.11 as /usr/local/bin/python, and adding /usr/local/bin first in your $PATH.

Tuesday, April 26, 2022

macOS et les terminaux, un petit récap'

La base, brew

Pour installer plein de logiciels utiles (préalable : installer XCode, gratuit avec création de compte Apple) : https://brew.sh. Ça s'utilise comme un package manager sous linux (yum, apt), avec brew searchbrew install, brew updatebrew upgrade...

Les terminaux

Pour « la totale », on a le combo iTerm2 (remplacement de Terminal.app) + Oh My Zsh (pour facilement ajouter des thèmes et plugins à l'interpréteur de commande zsh, avec le thème Powerlevel10k et les fontes qui vont bien. Tutoriel complet : https://gist.github.com/kevin-smets/8568070.

On a également le tutoriel du point de vue « développeur frontend » sur le blog de Josh W. Comeau.

Pour un truc plus pas-à-pas :

Divers outils

Le petit plus

De nombreuses applications proposent un remplacement pour Terminal.app : 
  • iTerm2 cité ci-dessus est sans doute le plus facile à installer et celui qui propose le plus d'options ;
  • Si vous utilisez plusieurs systèmes (macOS, Windows, GNU/Linux ou BSD...), peut-être qu'Alacritty, multi-plateformes, conviendra mieux (il conviendra aussi si vous rêviez d'utiliser les raccourcis de vi dans le terminal) ;
  • Enfin, un petit nouveau macOS-only mais qui demande de se logger avec un compte GitHub : Warp, basé sur Rust, qui propose une « réinvention » du terminal pour le 21e siècle. La gestion des « blocs » (une commande correspond à un nouveau bloc de texte, à partir duquel il est facile de copier différentes informations — la commande initiale, le résultat, ou tout à la fois), ainsi que les possibilités d'édition de la commande (multi-lignes...) le rendent assez intéressant !

Monday, June 15, 2020

Slackware64-current with KDE Plasma in Parallels Desktop on macOS

Introduction

I like working from macOS (Catalina, 10.15). I have my habits since 2006 and, now, with iTerm2 + zsh and a few plugins, plus other software (TextMate...), life is good. But there will always be that one moment in your work life where you find yourself wishing you had a GNU/Linux distro at hand, just to try something real quick. Anyway, that's my case.

Unfortunately, I was in a bit of a conundrum: I have the bad habit of getting every distro with a package manager FUBAR in a few months' time. Yes, I'm looking at you, Debian, and at your derivatives [OpenSUSE: you're ok; for everyone else: YMMV]. In macOS, I'm using Parallels Desktop, so at least it's easy to save a working version of the virtual machine before attempting things. I have had some very good luck running OpenSUSE Tumbleweed in Parallels Desktop in the past years, but for some reason when I installed it yesterday it... Didn't ran, and just spent time taking 100% of the processors in the VM (I let it run on 6 cores, 4GB RAM, 1GB video RAM) while trying to load the desktop, after an installation process that went quite well.

I decided to go back to my early teenager-years distro: Slackware, and to what's in my opinion the best desktop environment of this time, KDE Plasma (I've always had a soft spot for Enlightenment, howerver, and I find that the Solus distro gives good options and a nice desktop [with Budgie/Gnome/Mate/Plasma available], for anyone wanting something simple -- but it doesn't work well in Parallels Desktop). Due to a specific Wifi driver in the beginning of the 2000s, I had to use Slackware and recompile the kernel to get Internet to work. I learnt a lot. Would that still work in 2020? Yes, with a few limitations.

Limitations

Let's get right now to the limitations: I haven't been able to install Parallels Tools. As such, I don't have access to my macOS folders from Slackware, neither can I resize the Parallels window to have the X server change the resolution. I tried installing Parallels Tools using the "hack" of deleting the check for requirements (particularly the package manager) but it borked my Xorg install, so use at your own risk.

Installation

Installation itself went smoothly: download the DVD in torrent, select it as boot device in Parallels, and choose your packages. I did make one more partition than needed, for /boot/, because I knew I wanted to go the grub way. I deselected all the KDE packages, as they are part of the KDE 4 version. After installation, I choose to not install Lilo and followed the procedure here to install grub at first install.

After the first boot, I created a user, and followed the mkinitrd procedure here, just to have a cleaner boot. I initially tried adding my configuration in /etc/grub.d/40_custom but after a few updates & upgrades the new kernel was automatically added to grub.

Upgrade

I followed the system upgrade procedure here: blacklist the kernel in /etc/slackpkg/blacklist, manually upgrade the kernel and modules (by downloading them from one of the mirrors), update the initrd, and finally select a mirror for slackware64-current in /etc/slackpkg/mirrors
 
A last grub update :
 
# grub-mkconfig -o /boot/grub/grub.cfg
 
Then: 
# slackpkg update gpg
# slackpkg update
# slackpkg upgrade slackpkg
# slackpkg upgrade glibc-solibs
# slackpkg install-new
# slackpkg upgrade-all
# slackpkg clean-system
# slackpkg new-config
Then a reboot, for luck.

Plasma 5

Installing Plasma 5 can be a bit daunting, if you look at the readme file. But, thanks to AlienBob and Epsi, the procedure is quite simple. First, make sure your system is up-to-date and everything, then manually install slackpkgplus. Enable (add) the ktown repository in /etc/slackpkg/slackpkgplus.conf, and continue the procedure following Epsi's instructions (update GPG, update, etc.).

As root, I deleted (just for luck) /etc/X11/xorg.conf-vesa and didn't run X -configure. With Parallels Tools, I had the "EE No usable screen(s) found" error in X, but by default this seems to work fine! 

Finally, as a user, I launched startx just to make sure, and then in xwmconfig I selected the Plasma Desktop.

When I was sure that everything was setup right, I changed the init to level 4 in /etc/inittab, and rebooted to KDM. I was able with a bit of fiddling to get my MacBook Pro keyboard working with the right layout in Plasma, and to configure zsh as I wanted (in Konsole).

That's it! If you had any luck installing Parallels Tools and getting X to work in Slackware64-current on Parallels Desktop, please let me know. If you have questions about my setup, I'll be happy to answer them in the comments.

Friday, December 1, 2017

OpenSuSE, Fedora et al. : resolving the "os-release file is missing" problem

Yesterday, booting my OpenSuSE-tumbleweed after a relatively small zypper dup the day before, I found myself in front of this :


Of course, a funny thing is that my laptop was docked, and the USB keyboard not recognized. Switching to the laptop keyboard, I found out more (with the systemctl status initrd-switch-root.service command) :

Failed to switch root: Specified switch root path /sysroot does not seem to be an OS tree. os-release file is missing.
initrd-switch-root.service: main process exited, code=exited, status=1/FAILURE
Failed to start Switch Root.
Startup finished in 6.650s (kernel) + 0 (initrd) + 3min 22.924s (userspace) = 3min 29.574s.
Unit initrd-switch-root.service entered failed state.
Triggering OnFailure= dependencies of initrd-switch-root.service.
initrd-switch-root.service failed.
Started Emergency Shell.

Well... that didn't bode well. But my system was mounted, read-only, in /sysroot/, so, what now?
After searching for a bit and finding nothing of interest on the web, I finally managed to fix that by remounting the / filesystem read-write, and copying the file found on the initrd system. So : find your / partition (cat /sysroot/etc/partitions, or blkid /dev/sdXY)
Then :

umount /sysroot && mount /dev/sdXY /sysroot -o rw
cp /usr/lib/os-release /sysroot/etc/ && cp /usr/lib/os-release /sysroot/usr/lib/

... And rebooting. Fixed everything! #TheMoreYouKnow

Tuesday, October 17, 2017

La fin de l'« Uncanny Valley », vraiment ?

Il y a moins d'une semaine, Alan Warburton publiait sur Viméo une vidéo très intéressante, faisant un état de l'art des images de synthèse (Computer-generated imagery, CGI). À travers plusieurs logiciels de création, des exemples précis de films et de création vidéo sont abordés de manière chronologique pour relater une courte histoire des images de synthèse.



Cette vidéo, si elle montre effectivement qu'en ce qui concerne la création de paysages, d'animations d'explosions, de fluides divers, de robots ou de personnages humanoïdes les images de synthèse ont fait leurs preuves et sont très crédibles (l'exemple du montage de Transformers à 7' renforçant cependant le côté spectaculaire au profit d'une véracité de l'image : « abstracted, visceral sequence of spatially and temporally disconnected impacts ») trouve sa limite — il me semble — dans l'impasse qu'elle fait sur les humains (visages, déplacements).

En effet, cette théorie de l' « Uncanny Valley », vallée de l'étrange ou vallée dérangeante en français (dont on peut lire l'article original ici, en français) s'applique assez spécifiquement à la ressemblance entre un robot et un être humain, ce qui a été extrapolé aux humains traités en images de synthèse dans les films. Aujourd'hui, l'exemple de Star Wars: Rogue One présentant le Grand Moff Tarkin ou la princesse Leia, à moins de les regarder en piètre qualité, ne rendent pas crédible la théorie selon laquelle les images de synthèse auraient atteint le point ultime de ressemblance entre les humains et les humains en CGI. Ils sont pourtant censés être les fers de lance de ce qu'il est possible de réaliser en images de synthèses. Une autre discussion pourrait avoir lieu autour de l'identification au personnage par rapport à sa ressemblance à un humain, mais c'est un autre débat (Avatar, c'est toi que je regarde).

Enfin, une phrase m'a fait tiquer : « If you can tell it's CGI, it's bad CGI ; in other words, success equals invisibility. In order to work, CGI must disappear » et, plus tard, « CGI works best undetected » (autour de 9'10). Dans ce cas, un des meilleurs exemples doit clairement être Mad Max: Fury Road. Volontairement, dans icelui, les effets spéciaux et images de synthèse sont utilisés pour simplement rehausser (ok, un peu plus des fois) des scènes spectaculaires tournées pour la plupart avec un minimum d'écrans vert. Et sur lequel les merveilleuses gens du Ciné-club de Monsieur Bobine et de L'Ouvreuse ont déjà beaucoup écrit.