<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN" [
<!ENTITY doc SYSTEM "code/doc.py" CDATA linespecific>
<!ENTITY parameter SYSTEM "code/parameter.py" CDATA linespecific>
<!ENTITY config SYSTEM "code/config.py" CDATA linespecific>
<!ENTITY i18n SYSTEM "code/i18n.py" CDATA linespecific>
<!ENTITY daemon SYSTEM "code/daemon.py" CDATA linespecific>
<!ENTITY daemonpoll SYSTEM "code/daemon_poll.py" CDATA linespecific>
<!ENTITY daemonaction SYSTEM "code/daemon_action.py" CDATA linespecific>
<!ENTITY skin SYSTEM "code/skin.fxd" CDATA linespecific>
<!ENTITY skin2 SYSTEM "code/skin2.fxd" CDATA linespecific>
<!ENTITY item SYSTEM "code/item.py" CDATA linespecific>
<!ENTITY plugindir SYSTEM "code/dir" CDATA linespecific>
<!ENTITY setup SYSTEM "code/setup.py" CDATA linespecific>
<!ENTITY MANIFEST SYSTEM "code/MANIFEST.in" CDATA linespecific>
<!ENTITY menuex1 SYSTEM "code/menu_Menu.py" CDATA linespecific>
<!ENTITY menuex2 SYSTEM "code/menu_Item.py" CDATA linespecific>
<!ENTITY menuex3 SYSTEM "code/menu_MenuItem.py" CDATA linespecific>
<!ENTITY menuex4 SYSTEM "code/menu_fullexample.py" CDATA linespecific>
<!ENTITY mainmenupluginex SYSTEM "code/MainMenuPlugin.py" CDATA linespecific>
<!ENTITY idlebarpluginex SYSTEM "code/idlebarplugin.py" CDATA linespecific>
]>

<book>
  <bookinfo>
    <title>Freevo Plugin Writing HOWTO</title>
    <subtitle>Writing your own plugins for Freevo</subtitle>

    <copyright>
      <year>2003</year>
      <holder>Dirk Meyer</holder>
    </copyright>

    <legalnotice>
      <para>
	Permission is granted to copy, distribute and/or modify this document
	under the terms of the GNU Free Documentation License, Version 1.1 or
	any later version published by the Free Software Foundation; with no
	Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
	Texts. A copy of the license can be obtained at
	http://www.gnu.org/licenses/fdl.html and is included as an appendix in
	this document.
      </para>
    </legalnotice>

    <authorgroup>
      <author>
        <firstname>Dirk</firstname>
        <surname>Meyer</surname>
        <authorblurb>
	  <para>
	    <email>dmeyer@tzi.de</email>
	  </para>
        </authorblurb>
      </author>
      <author>
        <firstname>Mike</firstname>
        <surname>Ruelle</surname>
        <authorblurb>
	  <para>
	    <email>mikeruelle@comcast.net</email>
	  </para>
        </authorblurb>
      </author>
    </authorgroup>

    <abstract>
      <para>
	This document contains some usefull information on how to write a
	plugin for Freevo. This includes some documentation about the interbal
	structure of Freevo. This howto is not complete (and maybe never will
	be). If you have additional questions, please contact the developer
	list (<email>freevo-devel@lists.sourceforge.net</email>).
      </para>
    </abstract>

  </bookinfo>

  <toc>
  </toc>

  <chapter>
    <title>Introduction</title>

    <sect1>
      <title>Introduction</title>
      <para>
	To make it easier to add or remove functions for Freevo, we integrated
	a plugin system into Freevo. By adding a file into the Freevo system,
	you can add new functions without changing anything else in the core of
	Freevo. It's also possible to distribute a plugin with your application
	and not within the Freevo distribution.
      </para>
      <para>
	If you wrote a plugin, please send it to the Freevo developer
	list. Maybe other people also find it usefull.
      </para>
    </sect1>

    <sect1>
      <title>Disclaimer</title>
      <para>
	This document is available for free, and, while I have done the best I
	can to make it accurate and up to date, I take no responsibility for
	any problems you may encounter resulting from the use of this
	document.
      </para>
    </sect1>
    <sect1>
      <title>Contact / Feedback</title>
      <para>
	If you have found this HOWTO to be helpful or you have found errors in
	this HOWTO please feel free to contact me at
	<email>dmeyer@tzi.de</email> or contact the Freevo developer list at
	<email>freevo-devel@lists.sourceforge.net</email>.
      </para>
    </sect1>
    <sect1>
      <title>Revision History</title>
      <para>

	<revhistory>
	  <revision>
	    <revnumber>v0.1</revnumber>
	    <date>2003-10-31</date>
	    <authorinitials>DM</authorinitials>
	    <revremark>Initial Release</revremark>
	  </revision>
	</revhistory>

      </para>
    </sect1>

  </chapter>
  
  <chapter> 
    <title>Internal Structure</title>
    <sect1>
      <title>Plugin Location</title>
      <para>Plugins are intended to be grouped by type and then there is a general location for hard to fit plugins. The main location is in the /usr/local/freevo/src/plugins. The type locations for plugins is in the various plugins subdirectories of the tv, video, audio, and image in /usr/local/freevo/src.</para>
    </sect1>

    <sect1>
      <title>Events</title>
      <para>
	The communication between input devices (remote control or keyboard)
	and the menu or the application is based on events. It's also possible
	that a plugin sends an event to other parts of Freevo. The mapping
	between keys and the events depends on the application type (menu,
	video, audio, etc). See <filename>event.py</filename> for details about
	the default event mapping.
      </para>
      <para>
	A event is an identifier string and optional an argument. The event
	itself can be compared with a string, comparing two event objects only
	compares the identifier. This makes it possible to check for events
	without caring for the argument.
      </para>
      <para>
	An event is send into Freevo with the function
	<function>rc.post_event</function>. The events are stored in a fifo
	queue. If an event is in the queue, Freevo tries to find the correct
	eventhandler for this event. If the menu is active, Freevo will pass
	the event to the current selected item. Each item will send the event
	to it's parent if it doesn't consume the event. If the event wasn't
	consumed by one of the menu items, it will be passed to the
	DaemonPlugins (see <link linkend="daemon">DaemonPlugins section</link>
	for deatils).
      </para>
      <para>
	Instead of the class <classname>Event</classname>, the plugin can use
	the functions helper functions in <filename>plugin.py</filename>
	<function>event</function> to create and <function>isevent</function>
	to get the name of an event. This functions adds the string
	<function>PLUGIN_EVENT</function> to the event name. The media menu has
	some extra redraw checkings if the event has something to do with a
	plugin. 
      </para>
    </sect1>

    <sect1>
      <title>The Menu System</title>
      <para>
        Menu's in freevo are done using the classes described below. But in general they are essential lists of items which have their names displayed in the list. Each item then has actions associated with them. The first action is the one used when selected and you can use enter to see the others. Menu's also have actions that can be associated with them to perform actions or updates.
      </para>
      <sect2>
	<title>Menu</title>
	<para>
          Menu is essentially a class wrapped around an array of choices. It has several methods but the constructor is the most commonly used. It takes a title, then an array of options, and then some optional parameters. The reload_func is the most commonly used. The reload_func is used when you come back from executing an item. It's only used when you want to show something other than the menu you started with when you come back.
        </para>
      <programlisting><inlinegraphic entityref="menuex1"></inlinegraphic>
      </programlisting>
      </sect2>
      <sect2>
	<title>Item</title>
	<para>Item is the base class for anything that appears in the Menu. Generally you create a subclass of Item and then create an actions method to tell the menu what to do when the item is clicked. The name property is what the Menu object uses to display on the screen. You can then create other variables to hold important data points in.</para>
      <programlisting><inlinegraphic entityref="menuex2"></inlinegraphic>
      </programlisting>
      </sect2>
      <sect2>
	<title>MenuItem</title>
	<para>This is a convenience class it is useful in two different situations. The first and most common is for creating Menus to display error conditions. The second use is for when you only need a very simple item with a single easy action.</para>
        <para>To use MenuItem in the error condition case you call the constructor with three parameters. The first parameter is what to display in the menu, the second is the action to take  when the item to select and the third is put the arg to the action function. In this case you typically wrap the constructor call into an append to your list of items to be given to menu.</para>
        <para>To use MenuItem as a simple Item and not bother with creating your own sub item class, you again call the constructor with the set of three parameters.  The first parameter is what to display in the menu, the second is the action to take  when the item to select and the third is put the arg to the action function. But typically you save a reference to this item and set a few additional parameters manually.</para>
      <programlisting><inlinegraphic entityref="menuex3"></inlinegraphic>
      </programlisting>
      </sect2>
      <sect2>
	<title>MenuWidget</title>
	<para>MenuWidget or menuw as it is often labelled in the code. Is a handy utility class where most of the menu magic happens. It has most of the default utility actions for menus as well as the methods to manage the menu stack.</para>
        <para><emphasis>Common Menu Actions:</emphasis></para>
        <para>back_one_menu -- goes to the previous menu. Typically used after deleteing the current menu. see cdbackup.py for an example.</para>
        <para>goto_main_menu -- jumps all the way back to the main menu.</para>

        <para><emphasis>List of menu stack actions:</emphasis></para>
        <para>pushmenu -- used after constructing a menu, then typically a call to refresh to display the menu.</para>
        <para>refresh -- redraw the top item on the menu stack. It is usually called after a pushmenu call.</para>
        <para>delete_menu -- remove the currently displayed menu.</para>
      </sect2>
      <sect2>
	<title>A full example to bring it all together</title>
        <programlisting><inlinegraphic entityref="menuex4"></inlinegraphic>
        </programlisting>
      </sect2>
    </sect1>

    <sect1>
      <title>GUI Objects</title>
      <para><emphasis>not written yet</emphasis></para>
    </sect1>

    <sect1>
      <title>Using the skin</title>
      <para>
	Some plugins may want to draw something on the screen. One example is
	the weather plugin (external plugin, download it from
	<varname>http://freevo.sf.net/addons/</varname>). Since this has
	nothing to do with the normal menu system, such plugins also need an
	eventhandler to react on buttons. 
      </para>
      <para>
	Since Freevo knows nothing about which elements should be displayed
	and were to put them, the plugin needs to define a fxd file in
	<filename>share/skins/plugins</filename> with the needed
	information. 
      </para>
      <para>
	<programlisting><inlinegraphic entityref="skin"></inlinegraphic>
	</programlisting>
      </para>
      <para>
	Now the freevo skin has fxd information about the type
	<varname>foo</varname>, but doesn't know which areas are allowed (ok,
	the skin could guess it). So the plugin needs to call
	<programlisting>
skin.register('foo', ('screen', 'title', 'view', 'plugin'))</programlisting> 
	once. Now the plugin can call
	<programlisting>skin.draw('foo', item)</programlisting> to draw
	<varname>item</varname> with the settings of <varname>foo</varname>. 
      </para>
      <para>So far so good, but it may happen that the plugin needs an area
	which isn't defined right now. The default areas for Freevo are
	<varname>screen</varname>, <varname>title</varname>,
	<varname>subtitle</varname>, <varname>listing</varname>,
	<varname>info</varname>, <varname>view</varname> and
	<varname>plugin</varname>. The plugin area is used for smaller
	plugins to draw on the screen, e.g. the idlebar.
      </para>
      <para>
	To create an area of your own, you first need to define it in the fxd
	file:
      </para>
      <para>
	<programlisting><inlinegraphic entityref="skin2"></inlinegraphic>
	</programlisting>
      </para>
      <para>
	Now the skin has fxd information about an area
	<varname>foo</varname>. The skin knows were it is and can also draw
	the background of the layout. But it needs an object to draw the real
	content. The following example defines a class which inherits from
	<varname>skin.Area</varname>. It defines itself as class to draw
	<varname>foo_area</varname>. The skin now calls the function
	<function>update_content_needed</function> to check if the area needs
	an update (return <varname>True</varname> or
	<varname>False</varname>). When Freevo knows that an update is
	needed, this function may not be called after all. The real work is
	done is <function>update_content</function>. 
      </para>
<!--       <para> -->
<!-- 	<programlisting><inlinegraphic entityref="skin2"></inlinegraphic> -->
<!-- 	</programlisting> -->
<!--       </para> -->
      <para>
	(add more doc here)
      </para>
    </sect1>
  </chapter>

  <chapter> 
    <title>The Different Types of Plugins</title>

    <sect1>
      <title>Basic Plugin</title>
      <para>
	It's possible to inherit directly from the class Plugin. But by doing
	that, the only thing happens is that the object is created, nothing
	more. You may need this to create a thread in the background an connect
	it to the plugin interface. Other plugins may now use this thread.
      </para>
      <para>
	This is done for the media plugins. The mplayer video plugin only
	starts the mplayer thread and register it to the plugin interface as
	<varname>VIDEO_PLAYER</varname>. The video item asks the plugin
	interface to give him the <varname>VIDEO_PLAYER</varname>.
      </para>
    </sect1>

    <sect1>
      <title>DaemonPlugin</title>
      <anchor id="daemon">
      <para>
	A DaemonPlugin is somthing that works in the background of
	Freevo. Examples for this kind of plugin are the idlebar, the usb
	plugin, the thread watching your rom drives and the LCD plugin. A
	DaemonPlugin can react on events, can be called in a specific time
	intervall and can draw something to the skin.
      </para>
      <programlisting><inlinegraphic entityref="daemon"></inlinegraphic>
      </programlisting>

      <para>
	The <function>shutdown</function> function will be called when Freevo
	shuts down to do some cleanup. Most plugins won't need that function. 
      </para>

      <sect2>
	<title>Polling</title>
	<para>
	  A plugin can be called in a specific time intervall. To do this, it
	  has to set the variable <varname>poll_intervall</varname> and define
	  a function <function>poll</function>. After that, the
	  <function>poll</function> will be called every <varname>0.01 *
	    poll_intervall</varname> seconds. When the menu is not active
	  (e.g. watching a movie or listening to music), the function won't be
	  called. If you want the plugin to be called even than, you can set
	  the variable <varname>poll_menu_only</varname> to True.
	</para>
	<para>
	  Example: a plugin that sends the Event <varname>foo</varname>
	  every second:

	  <programlisting><inlinegraphic entityref="daemonpoll"></inlinegraphic>
	  </programlisting>
	</para>
      </sect2>
      <sect2>
	<title>Action on events</title>
	<para>
	  To act on specific events, the class must define the function
	  <function>eventhandler</function>. This function will be called
	  with the event if nothing before consumed this event. If you create
	  your own kind of event, you can be sure you get it. If the function
	  handles this event, it should return True, if not False.
	</para>
	<para>
	  If the plugin should see all events, the plugin should set the
	  variable <varname>event_listener</varname> to True. After that, the
	  plugin will see all events and it doesn't matter, if the function
	  return True or not.
	</para>
	<para>
	  Example: a plugin that reacts on the Event <varname>foo</varname>
	  and counts the number of the events:
	  
	  <programlisting><inlinegraphic entityref="daemonaction"></inlinegraphic>
	  </programlisting>
	</para>
      </sect2>

      <sect2>
	<title>Drawing on the Screen</title>
	<para><emphasis>not written yet</emphasis></para>
      </sect2>

    </sect1>

    <sect1>
      <title>MainMenuPlugin</title>
      <para>
	A MainMenuPlugin is a plugin that adds items to the main menu. The main
	menu can also be the main menu for the different types of media, like
	the video main menu. E.g. if you put your plugin in
	<filename>video/plugins</filename>, it will be shown in the video main
	menu. The user can also force which kind of menu by setting the
	<varname>type</varname> when calling
	<function>plugin.activate</function>.
      </para>
      <para>
	Examples for this kind of plugin are all items in the main menu and the
	<varname>rom_drives</varname> plugin, adding all possible rom drives to
	the sub main menus.
      </para>
      <para>
	A MainMenuPlugin only needs to define the function
	<programlisting>def items(self, parent):</programlisting>returning a list
	of items for the menu.
      </para>

      <para> A full up example:</para>
      <programlisting><inlinegraphic entityref="mainmenupluginex"></inlinegraphic>
      </programlisting>
    </sect1>


    <sect1>
      <title>ItemPlugin</title>
      <para>
	An ItemPlugin adds something to the submenu of an item. You get this
	menu by pressing <varname>ENTER</varname> on an item. When the submenu
	is generated, all global ItemPlugins and for this media type are
	called. 
      </para>
      <para>
	The function to be called is 
	<programlisting>def actions(self, item):</programlisting> 
	and should return
	a list of actions. The function can check all attributes of the item to
	decide what actions to return. On action can either be a MenuItem (from
	file <filename>menu.py</filename>) or a list. This list conatins a
	callback function, a text for the menu and optional a shortcut which
	can be put on a button for the remote. 
      </para>
      <para>
	Examples for this kind of plugin are the coversearch and the imdb
	plugin. They check if it makes sense to call the function and return
	actions.
      </para>
      <para>
	Here is a simple example for an ItemPlugin. It checks if it's a
	video item and adds two actions to turn the software scaler on and off
	(by changing the global config variable, Ouch):
	<programlisting><inlinegraphic entityref="item"></inlinegraphic>
	</programlisting>
      </para>
    </sect1>

    <sect1>
      <title>IdlebarPlugin</title>
      <para>Idlebar plugins are deceptively simple. Only two methods are reccomedded to be implemented. There is the init method which most often calls the parent init and sets any class variables based on arguments to it. The other method is draw. Draw as its name suggests draws in the idlebar area assigned to the plugin.</para>
      <para>Draw is where most of the action happens. Basically you get a reference to the plugin, the X coordinate to start drawing at, an osd object to use to call all the drawing methods, and a tuple containing a type and object (what do they do? have never seen them used).</para>
      <para>The most complicated part is probably the drawing methods. Basically we can write text and, draw images. The osd variable actually is the Plugin_Area class which is a skin_area subclass and not the real osd class you would expect. You can look in freevo/src/skins/main/area.py and freevo/src/skins/main/main.py for class definitions and possible methods. The osd variable is also how we get and set the font we wish to use.</para>
      <para>Here is a full out example:</para>
      <programlisting><inlinegraphic entityref="idlebarpluginex"></inlinegraphic>
      </programlisting>
    </sect1>

    <sect1>
      <title>MimetypePlugin</title>
      <para><emphasis>not written yet</emphasis></para>
    </sect1>


  </chapter>


  <chapter> 
    <title>Notes for writing a plugin</title>
    <sect1>
      <title>General</title>
      <para>
	It's possible to add or remove a plugin in the
	<filename>local_conf.py</filename>. Freevo will search the plugins the
	main plugin directory, the source directory and (based on the plugin
	name) in the media subdirectories like video or audio.
      </para>
      <para>
	You should place your plugin into the main plugin directory if it
	doesn't depend on the media type. If it only works for e.g. video, you
	should place it in the video plugin directory. By doing that, the
	string <emphasis>video</emphasis> fill be part of the plugin name.
      </para>
      <para>
	Everything inside Freevo is some sort of plugin. Without plugin, the
	main menu will be empty and Freevo couldn't play a file at all. So it's
	possible to change (mostly) everything in Freevo by writing a plugin.
      </para>
    </sect1>
    <sect1>
      <title>Documenting the Plugin</title>
      <para>
	You should not forget to document the plugin. Freevo can scan all
	available plugins by calling the plugins helper:
	<screen>
<prompt>-> </prompt><command>freevo plugins -l</command>
&lt;list of plugins&gt;

<prompt>-> </prompt><command>freevo plugins -i audio.playlist</command>
Name: audio.playlist
Type: ItemPlugin
File: src/audio/plugins/playlist.py

Description:
&lt;...&gt;
	</screen>
	This documentation comes directly from the python source code. Use
	the Python docstring to document the plugin class. The first line
	will be used to show the short information for <command>-l</command>,
	the hole text will be used for <command>-i</command>.
      </para>
      <para>
	Example:
	<programlisting><inlinegraphic entityref="doc"></inlinegraphic>
	</programlisting>
      </para>
    </sect1>

    <sect1>
      <title>Which files to Import</title>
      <para>
	You will need to import other parts of Freevo do make your plugin
	work. Most common are the following modules:
      </para>
      <para>
	<table>
	  <title>Module list</title>
	  <tgroup cols="2">
	    <tbody>
	      <row>
		<entry>config</entry>
		<entry>Basic configuration of Freevo. This also contains the
		  settings from <filename>local_conf.py</filename>.</entry>
	      </row>
	      <row>
		<entry>plugin</entry>
		<entry>This contains all basic classes and functions for the
		  plugin interface.</entry>
	      </row>
	      <row>
		<entry>event</entry>
		<entry>Some pre-defined events and the class
		  <varname>Event</varname>to build your own events.</entry>
	      </row>
	      <row>
		<entry>menu</entry>
		<entry>Contains the class <function>Menu</function> to create
		  menus for the plugin. This module also conatins
		  <function>MenuItem</function>, a pre-defined item to put in
		  a menu</entry>
	      </row>
	      <row>
		<entry>item</entry>
		<entry>This module contains the basic
		  <function>Item</function> class to put into a menu.</entry>
	      </row>
	      <row>
		<entry>rc</entry>
		<entry>Functions for putting an event into the event queue</entry>
	      </row>
	    </tbody>
	  </tgroup>
	</table>
      </para>
    </sect1>

    <sect1>
      <title>User Configuartion</title>
      <para>
	A plugin may require some special configuration. There are two
	possible ways:
      </para>
      <sect2>
	<title>Adding parameter to the constructor</title>
	<para>
	  A plugin may add more than the <varname>self</varname> to the
	  <function>__init__</function> function. The user has to add the
	  values when the plugin is loaded. This makes it possible to load a
	  plugin more than once with different settings.
	</para>
	<para>
	  The following example adds two parameter. The first one has no
	  default value and has to be added when activating the plugin. The
	  second is optional.
	  <programlisting><inlinegraphic entityref="parameter"></inlinegraphic>
	  </programlisting>
	</para>
      </sect2>
      <sect2>
	<title>Using local_conf.py</title>
	<para>
	  The second way is to use the <filename>local_conf.py</filename> to
	  let the user set the variables. Since a plugin should be self
	  contained, it should not add something to
	  <filename>freevo_config.py</filename>. A better way is to use the
	  member function <function>config</function> to return a list of
	  variables for this plugin. If the user doesn't define the variable
	  the default values will be added to the <function>config</function>
	  module. Warning: the <function>config</function> function will be
	  called in the <function>__init__</function> function of the base
	  plugin class, do not try to use the variables before that. 
	</para>
	<para>Example: the plugin needs <varname>FOO_NAME</varname> and
	  <varname>FOO_FUNCTION</varname> to be set. It defines the default
	  values and a small description (not used at the moment)
	  <programlisting><inlinegraphic entityref="config"></inlinegraphic>
	  </programlisting>
	</para>
      </sect2>
    </sect1>

    <sect1>
      <title>i18n support</title>
      <para>
	Please keep in mind that Freevo has i18n support, meaning that the
	plugin can be translated into different languages. To support that,
	please add <function>_()</function> to each visible text string. 
      </para>
      <para>
	If your plugin is an external plugin and not distributed with Freevo,
	please use <function>self._()</function> for the translation and set
	the module for the translation with the function
	<function>translation</function>. This function exists for items and
	plugins. A item will inherit the translation settings from it's
	parent, so you only need to load it once.
	<programlisting><inlinegraphic entityref="i18n"></inlinegraphic>
	</programlisting>
      </para>
      <para>
	To update the translation you need to call the script
	<filename>update.py</filename> in the i18n directory of Freevo or
	when you have an external plugin, call <filename>setup.py</filename>
	(see <link linkend="external">Plugin Distribution</link> for details).
      </para>
    </sect1>
  </chapter>


  <chapter> 
    <title>Plugin Distribution</title>
    <anchor id="external">
    <para>
      If you wrote a plugin, it would be nice to send it to the Freevo mailing
      list. If you like (and we), we could integrate the plugin into the Freevo
      distribution. If you don't want that, or your plugin is very special and
      we won't include it, there is a way to build an external plugin with the
      Freevo distutils as installer. We will add a plugin (or a link to a
      plugin) on the Freevo homepage if it's build for the Freevo distutils. 
    </para>
    <para>
      The Freevo distutils are an enhancement for Freevo of the normal Python
      distutils. To install a plugin, the user only needs to call
      <command>freevo install tarball.tgz</command>. To make this work, the
      plugin needs to use the same directory structure as the Freevo
      distribution and a <filename>setup.py</filename> and
      <filename>MANIFEST.in</filename> file in the root directory.
    </para>
    <para>
      The directory structure should only conatin the needed directories and an
      empty <filename>__init__.py</filename> in the plugin directory. The
      <filename>__init__.py</filename> is needed by Python. Since the
      <filename>__init__.py</filename> isn't empty for
      <filename>plugins/idlebar/</filename>, it's not possible to write external
      idlebar plugins at the moment (you could place it in the normal plugin
      dir, that works). We are working on a solution to fix that.
      E.g if your plugin is a video plugin (video.foo) and contains one image
      <filename>foo.png</filename>, the directory structure may look like this:
      <programlisting><inlinegraphic entityref="plugindir"></inlinegraphic>
      </programlisting>
    </para>
    <para>
      The MANIFEST.in file describes a list of files to be included in the
      distribution (you can build a source distribution by calling
      <command>python setup.py sdist</command>).
      <programlisting><inlinegraphic entityref="MANIFEST"></inlinegraphic>
      </programlisting>
    </para>
    <para>
      The <filename>setup.py</filename> script is a Python distutils script
      with some default attributes for Freevo. If the plugin uses the Freevo
      file structure, a setup script could look like this:
      <programlisting><inlinegraphic entityref="setup"></inlinegraphic>
      </programlisting>
      For more details about the parameter for the setup function, read the
      Python manual. 
    </para>
  </chapter>

</book>
