What happens at startup
When domin starts a script it first reads the plugin lines and loads each plugin. Right after loading one — before the script is bound, before main() — it calls the plugin’s Ready hook. The plugin answers with a constant, READY or NOT_READY, and may add a message.
Figure 1 — the five steps; a NOT_READY answer ends the run before step 3.
A NOT_READY answer is final: the host prints plugin 'Sdl2ImagePlugin' is not ready: followed by the plugin’s own message, and exits with status 1. No statement of the script has run, no file has been touched, nothing is half done. This is the point of the mechanism: a problem that can be found before running is found before running. The old behaviour — a plugin that loaded fine and failed on its first real call, possibly minutes into a run — is gone.
A plugin that has no Ready hook at all (one built against an older plugin header) is treated as not ready too; the message says to rebuild it.
Adapters: the library opens in Ready
Most plugins have nothing to check and simply answer READY. The ones that matter are the adapters — plugins that wrap a third-party library at run time: SDL2, SDL2_image, FFmpeg, OpenSSL, SQLite, the MariaDB and PostgreSQL clients, cJSON. Their Ready opens the library, keeps the handle, and answers accordingly.
Figure 2 — an adapter’s Ready: the runtime folder first, then the system; the message names the file and the folder.
The search order is the same for every adapter and is described on the Optional libraries page. What matters here: the message the person reads is written by the plugin at the moment it knows exactly what is missing, so it can say which file and which folder.
Constants and messages are independent
The constant decides; the text informs. A plugin may answer READY and still leave a note — the ffmpeg plugin does, when it was built without decoding support: the script runs, and the host shows plugin 'FfmpegPlugin': decoding is disabled in this build … as a notice. An empty message means nothing to say, and the host stays silent.
| Answer | Message | What the host does |
|---|---|---|
READY | empty | nothing; the script runs |
READY | text | shows it as a notice; the script runs |
NOT_READY | text | refuses to start, quoting the text; exit 1 |
NOT_READY | empty | refuses to start: “the plugin gave no reason” |
domin --validate runs the same check but reports a NOT_READY plugin as a warning, anchored at the plugin line, and still says Validation OK. A script can therefore be checked — and edited in VS Code with live diagnostics — on a machine that lacks the library it will run with elsewhere.
Shutdown and Deinit
At the end of a run — a normal return from main(), a runtime error, or a Stop from the debugger — the host calls each plugin’s Deinit hook, in load order, before unloading the library. The plugin closes what Ready opened and frees what it allocated. Deinit answers with a constant and an optional message in the same way: DEINIT_OK with an empty message is silence; a message is shown as a notice; anything other than OK is reported as a warning on stderr — the script’s exit code is not changed by it, because the script itself did not fail.
How plugins hand data to each other
One plugin per library is a rule with a consequence: a PNG decoded by sdl2image has to reach a texture owned by sdl2 or gl, and the two plugins must not know each other’s internals. DominScript’s answer is the one it gives everywhere: the blob is the interface. The decoder returns a pixel packet — a small fixed header (width, height, stride, format) followed by raw RGBA bytes — and anything that draws pixels accepts that packet.
Figure 3 — producers on the left, consumers on the right, one packet format in the middle. No handles cross a plugin boundary.
The same packet can be written to a file, sent over a socket, inspected by the script with a struct over the header, or produced by your own code. A plugin you write joins either side of the picture by reading or writing the same bytes.
If you write a plugin
Two functions with fixed names, PluginReady(Message, Cap) and PluginDeinit(Message, Cap), defined before the export line; the export macro wires them, and a plugin without them does not compile. A plain plugin writes one line, DOMIN_PLUGIN_ALWAYS_READY(). An adapter opens its library in Ready with the shared loader and, when that fails, lets DominRtl_NotFound write the message; if the library has a purely software init of its own (IMG_Init, mysql_library_init), that runs in Ready too, and its counterpart in Deinit. Device- or session-level initialisation (an SDL subsystem, a GL context) is not Ready’s business: only the script knows whether it needs video, audio or both, so those come up at the first verb that asks for them. The Writing an adapter plugin page shows the whole file; plugins/adapter_skeleton in the tree is the pattern with every obligation numbered.