<!doctype linuxdoc system>
<article>
<title>The eboxy Extension Guide
<author>Paul Eggleton <tt>bluelightning@bluelightning.org</tt>
<date>Version 0.3.7, Sun Jun 15 18:42:33 NZST 2003
<abstract>
This document describes how to extend eboxy version 0.3.7.
</abstract>

<toc>

<sect>Introduction
<p>
<sect1>Purpose

<p>The idea with eboxy plugins is that you can do either of two things:
<itemize>
<item>Provide a pseudo-object that can be manipulated in scripts or by other
  plugins. This is useful for non-visual things, or where you want to be able
  to control some part of the plugin from a script.
<item>Look for a set of GUI widgets by name on the current page, and take control
  of them. This means the user can lay out the widgets in any way they
  wish, and still get the same (possibly enhanced) functionality. All you
  need to do is decide on the names and types of each of the widgets you
  want. You can be as generic or specific as you want.
</itemize>

<p>The plugin framework was designed so that I could add optional features to
eboxy, without adding too many features to the core eboxy codebase that won't
be needed by everyone, and to make it easier for other developers to add
features that they want without having to modify the eboxy codebase itself.

<p>Currently, the plugin framework is in &quot;alpha&quot; stage, which means two
things: (a) please let me know if you think it's been implemented well and if it does
everything you need; and (b) because of this, don't be too surprised if it
changes in future versions of eboxy. That said, after the next one or two
versions I don't plan to change it too much - at the very least I will try
not to change it in any way that breaks backwards compatibility.

<sect1>Plugin Usage

<p>Plugins are loaded by either using the <tt>loadplugin</tt> script command
or alternatively by putting a <tt>&lt;plugin&gt;</tt> element into the XML file. The
latter is the recommended method, because it is simpler and allows you to
attach event scripts to objects that the plugin registers. Here's a simple
example of loading a plugin using a <tt>&lt;plugin&gt;</tt> element:

<tscreen><verb>
...
&lt;page ...&gt;
  &lt;plugin module=&quot;myplugin&quot;/&gt;
  ...
&lt;/page&gt;
...
</verb></tscreen>

<p>eboxy will look for plugins in <tt>LIBDIR/eboxy</tt> only (where LIBDIR is
picked up at compile time, usually /usr/local/lib). Also, the .so extension is always
omitted. So in the above example, when the page is loaded eboxy will attempt to load
the plugin file <tt>/usr/local/lib/eboxy/myplugin.so</tt> into memory (actually,
some loading and pre-initialisation is done when the XML file is read in, and the
plugin is fully initialised when the page is loaded).


<sect>Technical

<sect1>Requirements
<p>eboxy plugins are shared libraries that are linked to libeboxyplugin.a and
implement all of the following C style functions:

<sect2>int ebplugin_init(void)
<p>use this to initialise your plugin. Other than your own initialisation
   code (if any) you should call setPluginInfo somewhere in here with the
   full name of your plugin and a version string. When you're done, return
   an exit status: 0 for success, otherwise failure (which results in the
   plugin not being loaded).

<sect2>int ebplugin_message(int msgcode, void *msgdata1, void *msgdata2)
<p>this function will be called when certain other events occur. Currently
   this exists just to let your plugin know when the page or file is changed.
   When this happens you have to let go of any hooks you have into page
   widgets, since they will no longer exist when the page is changed. Note
   that you don't have to unregister events on page objects - this happens
   automatically.

<p>This function may be extended for other events in future. For now, msgcode
   can be one of the following events (see <tt>pluginconstants.h</tt> for constants):

<table loc="ht">
<tabular ca="ll">
<bf>Message constant</bf>|<bf>Description</bf>@
     <tt>PLMSG_PLUGINSTART</tt>      | issued when your plugin has been loaded@
     <tt>PLMSG_BEFOREPAGECHANGE</tt> | issued before the current page is to be changed@
     <tt>PLMSG_AFTERPAGECHANGE</tt>  | issued after a new page has been displayed@
     <tt>PLMSG_BEFOREFILELOAD</tt>   | issued before a new XML file is to be loaded@
     <tt>PLMSG_AFTERFILELOAD</tt>    | issued after a new XML file has been loaded
</tabular>
</table>

<p>The return value is currently unused, but you should return 0 for
   compatibility with future usage. Also, any msgcode values your plugin does
   not understand (ie, that do not match any of the current PLMSG_ constants,
   or at least none of them that you're interested in) should be ignored
   silently by your code.

<sect2>void ebplugin_deinit(void)
<p>Use this clean up after your plugin. eboxy will call this before it unloads
   the plugin. You must not call any plugin API functions from this procedure.

<p>Plugins must define all three of these functions, and must be linked to
libeboxyplugin.a (the eboxy plugin client library), or they will not be
loaded. Beyond these, you may define any other functions and use any libraries
you need (however, please read the <ref id="guidelines" name="guidelines section">).

<p>The plugin system has an built-in version checking mechanism. eboxy
can determine what version of libeboxyplugin.a your plugin was linked with,
and so can determine if your plugin is too new or too old to work with it.
Future versions of eboxy will be designed with as much backwards compatibility
as possible.


<sect1>Plugin API

<p>The plugin API documentation has been generated in HTML using Doxygen, and
you can view it <url url="pluginapi/files.html" name="here"> (in eboxy/docs/pluginapi).

<sect1>Guidelines<label id="guidelines">
<p>
<itemize>
<item>Don't take too much time responding to messages in <tt>ebplugin_message</tt>
  - these aren't yet threaded by eboxy so the GUI is effectively &quot;frozen&quot;
  while code in your plugin runs. Use threads if appropriate.
<item>Be careful what values you pass to eboxy functions - there is very little
  checking code at the moment. Particularly, you should never pass NULL
  in place of a <tt>char&nbsp;*</tt> unless it is explicitly allowed.
<item>If you request objects that don't exist, try to set properties that are
  read-only, etc. then an error will be printed on stderr. In this case,
  functions that return a <tt>char&nbsp;*</tt> will return NULL, and functions that return
  an integer result code will return a non-zero value.
<item>If your plugin was loaded from the System OnLoad event rather than the Page
  OnLoad event (which for most applications is recommended) then the current
  page has not been created yet when your plugin recieves the
  PLMSG_PLUGINSTART message, so you should not try to look for any widgets.
<item><em>Do not</em> call any plugin API functions in ebplugin_deinit. Doing so will
  likely result in deadlock. Similarly, you must not do anything that might result
  in a plugin message being sent to your plugin during ebplugin_init (eg.
  changing the page) - this will result in deadlock as well. If this causes you a
  real problem, please let me know.
<item>Problems with external libraries that you use can cause odd things to happen
  in eboxy. Because plugins execute within eboxy itself, there is nothing eboxy can do to prevent
  bad code in a plugin (or libraries a plugin uses) from writing garbage all over its
  memory. If you are having trouble with a plugin, it may be worth checking if there is
  a bug in any libraries you are using in the plugin. Of course, it could just as likely
  be a bug in eboxy. I recommend <url url="http://developer.kde.org/~sewardj/" name="Valgrind">,
  an excellent memory debugger that doesn't even require you to recompile the executable you want
  to debug. Note that as Valgrind does not support the fancy instructions available on
  newer processors (P4, Athlon) make sure you compile eboxy and SDL without optimising
  for a newer architecture if you wish to use Valgrind (use <tt>-march=i386</tt> or <tt>i486</tt>).
<item>Unless you want it to remain in memory until eboxy exits (which may be fine,
  depending on what you want to do) you are responsible for unloading your plugin,
  or allowing the user of your plugin to ask it to unload (through an object
  you have registered). When you're ready to unload, call requestUnload(). Note that
  unloading may not happen immediately, since it is scheduled to avoid deadlock and
  other nasty situations. However, you should avoid doing anything after calling
  requestUnload() (that is, having any statements after it in the same function).
<item>Document your plugin properly, so that users understand how to use it. At least
  provide snippets of XML or script to show how to load and use it, or (even better)
  provide an example XML skin that demonstrates the plugin in action.
<item>DO NOT under any circumstances open up a service on an untrusted network (eg.
  the internet) that allows you to pass script code to runScript() to be executed,
  unless you are willing to accept the possibility you may be hacked by doing so.
  eboxy's security has not been tested, and the trusted flag of runScript
  is only designed to give a small measure of protection. You have been warned!
  (If you wish to test eboxy for security, please do so and report any bugs you find).
<item>Try to make the function names that will be exposed to eboxy unique (those functions
  which will be passed to the *DL functions, eg. property get/set functions). C does
  not allow functions of the same name, so nasty things will happen if there is a clash
  with another plugin. The easiest way to avoid this is by using a unique prefix for all
  of the functions that will be exposed. For non-exposed functions, you can use any name
  you like as normal.
</itemize>

<sect>Dynamic GUI system
<sect1>Introduction
<p>eboxy plugins can now query, modify and even create the GUI at runtime inside eboxy.

<sect1>Adding widgets

<p>To create a new widget, you can either create it from scratch, create one based on a
template, or clone an existing widget. The createWidget or cloneWidget API functions are used
to do this. When a new widget is created or cloned, it goes into a constructed widget
&quot;holding area&quot;. Set any properties you wish to set before using it, and then
add the widget to a page, using the AddWidget method of the page (supplying the name of the
widget to add). You can add it to the current page or to any other page, including pages you
create at runtime (more on this below).

<sect1>Adding pages

<p>You can also create new pages at runtime. Use the createPage API function to do this. To actually
display the page, use the changePage API function to change to it.

<sect1>Tidying up

<p>The deletePage and deleteWidget API functions should be used to remove pages and widgets
from memory when you no longer need them. Note that deletePage may be used to delete pages
that were loaded from an XML file, but deleteWidget may only be used on widgets that you create (the same
applies to the RemoveWidget method of page objects). Also, using deletePage on a page that contains widgets
you have created <em>does not</em> absolve you of the responsibility of deleting the created widgets - these
remain in the holding area, and you must run deleteWidget on each of them to clean them up - so you should
keep track of them. You should not use deleteWidget to delete a widget that is still on a page - use the
page's RemoveWidget method to remove the widget first, then delete it.

<p>Note that unlike custom objects, widgets are not owned by plugins - they will remain if your plugin
is unloaded before eboxy quits. If you do not want them to be persistent, you should explicitly remove
them. However, at this present time, if you change to a different XML file, the created pages will be cleared
out - this is considered to be a bug and will be fixed in subsequent versions of eboxy.

<sect1>Enumerating objects

<p>eboxy now provides ways to enumerate various elements of the GUI at runtime. Enumeration
is provided in the form of a &quot;get count, get item at index&quot; interface.

<sect2>Enumerating object properties, methods and events

<p>To enumerate properties on an object, first retrieve the value of the <tt>propertycount</tt> property,
and then call the <tt>getproperty</tt> method in a loop, passing the index (from 0 to count-1) on each
iteration. In this way you can retrieve the name of each property on the object. You can do likewise
for methods and events by using <tt>methodcount</tt>/<tt>getmethod</tt> and <tt>eventcount</tt>/<tt>getevent</tt>
respectively. These can be used on most types of eboxy object accessible through the plugin API.

<sect2>Enumerating widgets

<p>Page objects have a property <tt>widgetcount</tt> and a method <tt>getwidget</tt> which can be used to
enumerate the widgets on a particular page in the manner described previously.

<sect2>Enumerating pages

<p>To enumerate available pages, use the <tt>pagecount</tt> property and <tt>getpage</tt> method of the system
object.


<sect>Examples
<sect1>Available plugins

<sect2>Simplemusic
<p>Simplemusic, as the name suggests, is a very simple plugin that plays music files.
It exposes a simplemusic object that you can use in scripts, and uses the
<url url="http://www.libsdl.org/projects/SDL_mixer/" name="SDL_mixer">
library to do the playing. It also demonstrates how easy it is to make a plugin. See <tt>simplemusic.txt</tt>
and <tt>simplemusic.c</tt> in <tt>plugins/simplemusic</tt> for more information.

<sect2>Filebrowser
<p>Filebrowser is another simple plugin that operates in conjunction with a listbox
widget in order to display a list of files for the user to select from. Filebrowser
automates the listbox so that selecting a directory browses into that directory,
and selecting a file generates an event. See <tt>filebrowser.txt</tt>
and <tt>filebrowser.c</tt> in <tt>plugins/filebrowser</tt> for more information.

<sect2>TestBench
<p>The testbench plugin is a good example of creating widgets at runtime, and also
serves to explore the properties available for various different types of widget.
This plugin is available for download separately from the eboxy homepage.

<sect2>Others
<p>You can check out the the latest version of all other plugins not supplied with
eboxy itself (at least those distributed by me) from the eboxy CVS repository. The
module you need to check out is <tt>eboxy-plugins</tt>. Once they are of production
quality these will also be available from the eboxy homepage.

<sect1>Plugin skeleton code

<p>Here is a skeleton plugin, containing the bare minimum of code (fill in
where appropriate):

<tscreen><verb>
#include "eboxyplugin.h"
#include "pluginconstants.h"

int ebplugin_init(void) {
  /* Change these two strings to whatever you want for your plugin */
  setPluginInfo("my plugin", "1.0");

  /* Do anything else here that you need to do to initialise your plugin
   * Note: if your own initialisation stuff fails here, you should return
   * a non-zero value.
   */

  return 0;     /* returning 0 means plugin has successfully initialised */
}

int ebplugin_message(int msgcode, void *msgdata) {
  /* If your plugin looks for objects on the current page, you should check
   * here for when the current page/file is changed. This will also be called
   * under other circumstances (see pluginconstants.h for values of msgcode)
   */
  return 0;     /* return 0 here for future compatibility */
}

void ebplugin_deinit(void) {
  /* Any cleanup code you need for your plugin goes here */
}
</verb></tscreen>


</article>
