Zapping plugin writer's help
(Plugin Protocol 2)

This document is OUTDATED. Please do not use it as a reference. I will write a new one when the API stabilizes.
Until then, use the existing plugins as a reference, and mail me if you have any doubts.



















Brief

This document explains what are Zapping plugins and how to write one, please read it carefully and mail me if you have suggestions or you find anything erroneous.

How does Zapping understand plugins

On startup, Zapping will scan some directories, and it will try to load all the files in the directory ending in the .zapping.so. Thus, Zapping will not try to load plugin.zapping.so.2, to put an example.
After finding the files, it will try to load and validate them. See the plugin structure section to find out what are the requirements for a plugin to be valid.
After that, the plugins will become part of Zapping itself, and you will be able to manage them from inside Zapping.
The following directories are scanned on startup for plugins:
  1. $(prefix)/lib/zapping/plugins, where $(prefix) is the subdirectory were the program will be installed, usually /usr or /usr/local.
  2. $HOME/.zapping/plugins, where $HOME is the content of the enviromental variable with that name.
  3. Anything listed in $HOME/.zapping/plugin_dirs, if that file exists.
If there are two or more plugins with the same canonical name, the one with the highest version is loaded.

Plugin structure in detail

When Zapping finds a possible plugin, it tries to resolve the function plugin_get_protocol in the plugin's symbol table. Thus, this function cannot be declared static. If it is found, it is called, and if the returned version matchs the protocol Zapping uses, then the other compulsory function, plugin_find_public_symbols, is resolved. If it is found, the plugin is considered valid, and the rest of functions, which are optative, are queried through plugin_find_public_symbols.
gint plugin_get_protocol(void);

It must return the protocol number the plugin speaks. 2 in this revision (the Protocol 1 was used in the 0.3 Zapping series, but it had serious limitations). If that doesn't match the protocol Zapping understands, the plugin will not be loaded.

gboolean plugin_find_public_symbols

gboolean zp_init ( PluginBridge bridge, tveng_device_info * info );

 Inits the plugin. It should prepare itself for work, but it shouldn't start working yet. For that, it should wait until zp_start is called.

bridge: The brige is used to let the plugin access Zapping internals, more on this in the communication section.
info: A pointer to the structure attached to the video device. There is no documentation on TVeng yet, see src/tveng.h on the main Zapping distribution to get some help on this.
Returns: The plugin should return FALSE if it cannot init itself, so Zapping doesn't try to use it again. TRUE if it can be used.

void zp_close ( void );

 Close (unload) the plugin. The plugin should free here all the mem it uses, close file descriptors, kill threads, etc.

gboolean zp_start ( void );

 When this function is called, the plugin should start to work. It should return FALSE and stop itself if there is some error, although it shouldn't be. Anything that could potentially cause errors should be done in zp_init.
Returns: TRUE if everything succeeded and FALSE otherwise.

void zp_stop ( void );

 Used to stop the plugin, but it shouldn't unload itself.

void zp_load_config ( gchar * root_key );

 The plugins should store their configuration through the ZConf system (look in the functions that always resolve section to find out how to use ZConf functionalities, but you can always cut'n'paste from the plugins that come with Zapping). When this is called, the plugin should read its values from the zconf config tree, under the given branch.
Note: At least the autostart (Boolean) configuration value should be present for all plugins. If, when reading the configuration, the plugin gets a value of TRUE for the autostart value, it should call zp_start when zp_init is called.
root_key: It will be something like: /zapping/plugins/plugin_name/, g_strconcat () to this the name of the key you want to save to build the path zconf functions will be given.

void zp_save_config ( gchar * root_key );

 This is called when Zapping wants all the plugins to store their config. The autostart (Boolean) value is compulsory for all plugins. Zconf should be used here, too.
root_key: The branch of the config tree were the config should be saved under. It will look like /zapping/plugins/plugin_name/.

void zp_get_info ( gchar ** canonical_name, gchar ** descriptive_name, gchar ** description, gchar ** short_description, gchar ** author, gchar ** version );

 Asks the plugin for some info. One or more of the pointers could be NULL, that will mean that Zapping doesn't need that value. The others should be modified to point to a valid (possibly statically allocated, Zapping will not modify or free it) string with the desired contents.
canonical_name: Short, unique, alphanumeric name for the plugin. Will be used when creating the config key.
descriptive_name: Long name for the plugin. The user will see it called like this.
description: Plugin description. Here you should explain what the plugin does, requirements, and maybe a short faq.
short_description: A line of two giving a general idea of what the plugin does.
author: Your name here.
version: A string describing the version of the plugin.

gboolean zp_running ( void );

 Used by Zapping to know whether the plugin is working or not.
Returns: TRUE if the plugin is working, FALSE otherwise.
 
 

Optative functions

There are some functions that the plugin may or may not have. If they aren't present, Zapping doesn't worry, and ignores it.

GdkImage * zp_process_frame ( GdkImage * image, gpointer data, struct tveng_frame_format * format );

 This is called for all the plugins secuentially. Here the plugin, if it is a image processing plugin, would make any changes to the image.
image: The image that contains the captured frame. data: Pointer to the data contained in image.
format: A pointer to the struct that holds the image format. If the plugin modifies the image in size, the changes should be reflected here, so the following plugins know about the change and behave correctly. Note: The depth of the image should not be modified by the plugins.
Returns: A pointer to the new image. Might be the same as the given pointer.

gboolean zp_get_public_info ( gint index, gpointer * ptr, gchar ** symbol, gchar ** description, gchar ** type, gint * hash );

 If present, Zapping will use this routine to get info about the public symbols in the plugin. This public symbols are the routines or variables that can be accessed by other plugins. Zapping will call this from index=0, until gets a return value of FALSE. The plugin must check if any of the given pointers is NULL, and ignore it if that happens. The stored pointers, if any, will not be modified or freed by Zapping.
index: The symbol index we are querying.
ptr: On success, it must contain a pointer to the symbol.
symbol: The symbol name.
description: A short description for this symbol.
type: The type of the symbol, in C sintax. Example: int symbol_name (int param1, int param2);
hash: The hash value for this symbol. More on this in the communications section.
Returns: The plugin should return TRUE if there is an entry for index, and FALSE otherwise.

void zp_add_properties ( GnomePropertyBox * gpb );

 Tells the plugin that a new property box has been created. Any plugin that wants to be user-configurable should add a new page to the property box, and configure the callbacks properly.
gpb:The property box we are building.

gboolean zp_activate_properties ( GnomePropertyBox * gpb, gint page );

 This is called when the Apply or the OK button are pressed.
gpb: A pointer to the GnomePropertyBox.
page: The active page in the property box. If it is not the page of the plugin, it should ignore this and return FALSE.
Returns: The plugin should return TRUE if it handles this page, or FALSE if it doesn't.

gboolean zp_help_properties ( GnomePropertyBox * gpb, int page );

 This is called when the Help button of the property box is pressed.
gpb: A pointer to the GnomePropertyBox.
page: The active page in the property box. If it is not the page of the plugin, it should ignore this and return FALSE.
Returns: The plugin should return TRUE if it has built this page, it doesn't matter whether it does show some help or not. FALSE on error.

Note: Zapping will only load these three functions (zp_[add/activate/help]_properties) if the three of them exist, otherwise, none of them will be loaded, even if one or two of them exist.

void zp_add_gui ( GnomeApp * app);

 Tells the plugin that it can add itself to the main Zapping window.
app: The Gnome Application window belonging to Zapping.

void zp_remove_gui ( GnomeApp * app );

 Asks the plugin to remove all the GUI items it has added to the main Zapping window. The plugin should call this itself when it is being closed.
app: The main Zapping window.

Note: Again, zp_[add/remove]_gui go in a bunch. If the two of them exist, they will be loaded, but if only one exists, it won't be loaded.

gint zp_get_priority ( void );

 This routine lets Zapping know in which order should it run the plugins.
Returns: The greater this value is, the sooner this plugin will be run. Thus, a plugin that wants to receive a fully processed image (such as the screenshot saver) will return a negative value. By convention, 0 means don't care about the order. If this routine doesn't exist, 0 is assumed for the plugin priority.

The way Zapping runs the plugins

The way Zapping runs the plugins is, more or less, the following:
  1. zp_protocol (...)
  2. zp_get_info (...)
  3. zp_load_config (...)
  4. zp_init (...)
  5. zp_add_gui (...)
  6. zp_process_frame (...) a lot of times
  7. zp_remove_gui (...)
  8. zp_save_config (...)
  9. zp_close (....)
Other routines, such as zp_add_properties are called when the user wants to, so there is no predefined order for those.

Communication between Zapping and the plugins

The way Zapping uses to access the plugins has been described, but the other way, how do the plugins access Zapping, has not yet been covered. This section will try to solve that.
The plugins are provided with a "bridge" on startup. This bridge is a pointer to a function with the following prototype:

typedef gboolean (*PluginBridge) ( gpointer * ptr, gchar * plugin, gchar * symbol, gchar * type, gint hash );

 Through this bridge the plugin can ask Zapping for symbols in Zapping itself or in other plugins.
ptr: On success, a pointer to the symbol is stored here. On error its content can be used to get the error (see below).
plugin: The canonical name of the plugin where the symbol is located. Can be NULL, indicating that the requested symbol is in Zapping itself.
symbol: The name of the symbol we want to get.
type: The expected type of the symbol in C sintax. Just for error reporting.
hash: This hash value is used to avoid errors when calling or using the returned symbols. When the plugin writer creates a public symbol (see zp_get_public_info), a random hash value is given to the symbol. This random value is kept while the plugin type is the same as the original, but it is changed if its type changes. This ensures that the real type of the symbol is the same as the type the plugin believes it is.
Returns: FALSE if the symbol could not be loaded or found, and TRUE if the location stored at ptr is valid. In case of error, the meaning of ptr is the following:

Some functions that always resolve

There are some functions that will always resolve for Zapping, you can safely rely on them when writing your plugin, you don't need to use the provided plugin bridge to get them:

Some (probably obvious) notes on designing plugins

Epilog

This is the first version of this document, it may contain errors, unaccuracies, vague references, typos and gramatical errors (I am not a native speaker). If you find any of those, please contact me, so it can get fixed. If you think something is worth adding, if you have any good ideas for this API, comments or criticism, contact me too. Thanks a lot.

(C) Iñaki García Etxebarria 2000. This document is under the GNU Documentation License.