Mårten Stenius - <mst@sics.se>
Swedish Institute of Computer Science
Stockholm, September 1997
The functionality described here is to be seen as a first step towards enabling a more modular design of Dive, and may in the future be extended to incorporate different classes of plugins, enabling simple pluggable replacements of render modules, distribution mechanisms, object behaviours languages, etc.
A Dive plugin is basically just a dynamically loadable library, providing a set of symbols and functions recognized by Dive. When the plugin is loaded into the Dive process space, which is done runtime, these symbols are looked up by the Dive plugin interface and called, as described below.
The benefits of such a modular redesign includes simpler reconfiguration of the software - even runtime - without recompiling or restarting, greater freedom in experimenting with the integration of new technologies into Dive, still with full access to the "core" C interface of Dive. Furthermore, this is a step towards even further enabling Dive to suit different platforms with different capabilities and approaches by making different parts of the system easy to change and configure.
int dive_plugin_init() - will be called by Dive when the plugin is loaded into memory.
int dive_plugin_exit() - will be called when the plugin will be deactivated (typically when the Dive process exits).
The integer return value informs Dive if the execution of the function was successful: 0 should be returned if no problems occured, -1 otherwise. If dive_plugin_init returns -1, the plugin will not be actively handled by the Dive layer, but still reside in memory (depending on implementation, operating system and so on).
Typical actions performed in dive_plugin_init might be to register callbacks on various events in the virtual environment, to register new Dive/Tcl [4,5] functions that it implements, or to create some Dive objects for later use. Similarly, dive_plugin_exit should clean up by removing objects, deregistering callbacks, etc.
The plugin need not be linked with any the Dive libraries. If it wishes to interface the Dive routines, it needs to include the standard Dive include files (typically "dive/dive.h" and any modules headers as required). On UNIX platforms, it is sufficient to compile the plugin as a shared library, and make sure that the Dive plugin interface can find it (see below).
int plugin_init()
Should be called at startup by any Dive process that wishes to handle plugins (this is done by for instance the diva and vishnu applications). This function sets up the plugin records, and loads any plugins specified in the .dive_plugins file. A default version is found in dive/data/.dive_plugins, and the user may put a personal version in his/her home directory. Also, a specific plugin file can be pointed out by the plugin_file configure option. The format of these files is simple: a text file with one file name per line. Empty lines or lines starting with '#' will be ignored.
dive_plugin_p plugin_load(char *filename)
Will load a plugin from the file specified in filename. This may either be a full path, or just the name of a file. In the latter case, the file will be searched for in the DIVEPATH. A plugin name for further reference in Dive will be derived from this filename/path, by stripping any extensions and directory paths from the string. For example, a plugin loaded by calling dive_plugin_load("/home/foo/example.so") would be named "example". Currently, reloading of plugins with the same (derived) name is not allowed, for instance, any subsequet attempt to load a new module with the name "example" would be ignored. A pointer to a new plugin control record is returned on success, on failure NULL is returned. If there already was a plugin with a matching name, a pointer to that plugin record will be returned.
dive_plugin_p plugin_get(char *name)
Returns a pointer to the plugin control record the currently loaded
plugin matching the name parameter, such as "example" mentioned
above. Returns NULL if no such plugin is loaded.
#include <stdio.h>
#include "dive/dive.h"
void foo_cb(objid_t *id, objid_t *origin, void *arg)
{
printf("foo: New object!\n");
}
int dive_plugin_init()
{
printf("This is plugin foo being inited in DIVE version
%s!\n", DIVE_VERSION);
callback_register(ENTITY_NEW_EVENT, foo_cb, NULL);
return 0;
}
int dive_plugin_exit()
{
printf("This is plugin foo exiting!\n");
callback_unregister(ENTITY_NEW_EVENT, foo_cb);
return 0;
}
This code then needs to be compiled as a dynamically loadable (shared) library. On a UNIX platform this is typically done by using the -shared command-line switch with the compiler, even though this interface is bound to vary with different operating systems and compilers. A compile line may look something like:
gcc -c -o foo.so -shared -I/home/some_user/dive/include/ -I/home/some_user/dive foo.c
Then, Dive must be told to load the plugin (library), by adding a reference to the .dive_plugins file, such as the one found in dive/data, or a personalised version in the home directory of the current user. So .dive_plugins should contain a line similar to:
/home/some_user/foo.so
or just:
foo.so
if the library is placed somewhere in the DIVEPATH.
Once this is done, the plugin should be loaded as soon as some of the default Dive proceses is started (such as vishnu). If it is successfully loaded, vishnu should now be enriched with a piece of code that puts out annoying messages as soon as a new dive object is created.
The implementations of more useful and innovative plugins are left to the reader's imagination and programming skills.