Coraline plugins are dynamically linked shared object libraries (.so files). This has three consequences:
- They don’t have to (though they can) be distributed with the apps: one shared installation is good enough for any apps that require it.
- They need some standard functions present to be able to be loaded.
- They need to provide some instance that implements the Coraline Plugin interface.
The skeleton code generator handles this minutiae, and its code creates plugins based on a helper class that handles a lot of the boilerplate and gives some utility function, but it’s all documented here nonetheless.
The plugin lifecycle is essentially:
dlopen library supports_driver() create_object() plugin registration plugin JS injection plugin startup updates/plugin callbacks plugin shutdown destroy_object() dlclose libray
Dynamically loadable libraries containing coraline plugins are first
- checked for all the mandatory loader functions
- checked for a “true” return from the libaries supports_driver(), called with the running version of Coraline
If any of these checks fails, the library is closed and subsequently ignored.
Plugin instances are created, by Coraline at startup, and created using the library’s create_object().
If the plugin is not required by the app, it will then be released/destroyed using its destroy_object(). The end.
If is required by the app, it is given a chance to initialize and register with the system plugin registry. When using Plugin::Base, (as in the code created by the generator) this process is simplified a bit, and you need only override registerAllMethods() in order to make your callbacks visible to the system (e.g. with the PLUGINREGMETH() macro).
As the final step of instantiation, the plugin is given a chance to inject
This can be done a few ways, the simplest being to override Plugin::Base‘s
- clientSideSupportFile() (just return a JS filename to load from
under shortName() directory); or
- clientSideSupport() (return a string of JS to eval in the JS engine)
Once all the required plugins have been instantiated, as described above, the start-up signal is given, and each loaded plugin will get one call to it’s startUp() method.
After all plugins have started up, the device ready signal is given to client code, and you can expect callbacks to be triggered at any time.
While running, your plugin’s update() method will be periodically ticked. If you’re basing your plugin on the Plugin::Base helper, you don’t actually have to manage this–it’ll take care of any actions queued using queueAction().
You will also have callback methods triggered by the client app, as requested by that side.
The simplest approach is to inherit from Plugin::Base and have
your callbacks queue UpdateActions (which are just void(void) lambdas, using queueAction()) to report information/success/failure.
These will actions be executed on the next update().
Your job, while running, is to keep things snappy so the UI remains responsive.
Process any long operations in the background and, ideally, trigger
the JS-side callbacks during a subsequent update() using the UpdateActions queue.
Prior to unloading a plugin, it’s shutdown() method will be called to give you a chance to do any required clean up.
The final step before closing the library will be a call to destroy_object(), passing in the plugin instance.