From b527b2770868bccde05ad47393951fde5d51f79a Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Thu, 9 May 2002 16:26:56 +0200 Subject: Implemented plugin interface --- PLUGINS.html | 620 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 620 insertions(+) create mode 100644 PLUGINS.html (limited to 'PLUGINS.html') diff --git a/PLUGINS.html b/PLUGINS.html new file mode 100644 index 00000000..7481bfaf --- /dev/null +++ b/PLUGINS.html @@ -0,0 +1,620 @@ + + +The VDR Plugin System + + + +

The VDR Plugin System

+ +VDR provides an easy to use plugin interface that allows additional functionality +to be added to the program by implementing a dynamically loadable library file. +This interface allows programmers to develop additional functionality for VDR completely +separate from the core VDR source, without the need of patching the original +VDR code (and all the problems of correlating various patches). +

+This document describes the "outside" interface of the plugin system. +It handles everything necessary for a plugin to get hooked into the core +VDR program and present itself to the user. + + + +


Quick start

+ +
Can't wait, can't wait!

+ +Actually you should read this entire document before starting to work with VDR plugins, +but you probably want to see something happening right away ;-) +

+So, for a quick demonstration of the plugin system, there is a demo plugin called +"hello" that comes with the VDR source. To test drive this one, do the following: +

+If you enjoyed this brief glimpse into VDR plugin handling, read through the rest of +this document and eventually write your own VDR plugin. + +

The name of the plugin

+ +
Give me some I.D.!

+ +One of the first things to consider when writing a VDR plugin is giving the thing +a proper name. This name will be used in the VDR command line in order to load +the plugin, and will also be the name of the plugin's source directory, as well +as part of the final library name. +

+The plugin's name should typically be as short as possible. Three letter +abbreviations like dvd (for a DVD player) or mp3 +(for an MP3 player) would be good choices. It is also recommended that the name +consists of only lowercase letters and digits. +No other characters should be used here. +

+A plugin can access its name through the (non virtual) member function + +


+const char *Name(void); +

+ +The actual name is derived from the plugin's library file name, as defined in the +next chapter. + +


The plugin directory structure

+ +
Where is everybody?

+ +By default plugins are located in a directory named PLUGINS below the +VDR source directory. Inside this directory the following subdirectory structure +is used: + +


+VDR/PLUGINS/src +VDR/PLUGINS/src/demo +VDR/PLUGINS/src/hello +VDR/PLUGINS/lib +VDR/PLUGINS/lib/libvdr-demo.so.1.1.0 +VDR/PLUGINS/lib/libvdr-hello.so.1.1.0 +

+ +The src directory contains one subdirectory for each plugin, which carries +the name of that plugin (in the above example that would be demo and +hello, respectively). What's inside the individual source directory of a +plugin is entirely up to the author of that plugin. The only prerequisites are +that there is a Makefile that provides the targets all and +clean, and that a call to make all actually produces a dynamically +loadable library file for that plugin (we'll get to the details later). +

+The lib directory contains the dynamically loadable libraries of all +available plugins. Note that the names of these files are created by concatenating +

+ + + +
libvdr-demo.so.1.1.0
VDR plugin
library prefix
name of
the plugin
shared object
indicator
VDR version number
this plugin was
compiled for
+

+The plugin library files can be stored in any directory. If the default organization +is not used, the path to the plugin directory has be be given to VDR through the +-L option. +

+The VDR Makefile contains the target plugins, which calls +make all in every directory found under VDR/PLUGINS/src, +plus the target plugins-clean, which calls make clean in +each of these directories. +

+If you download a plugin package +from the web, it will typically have a name like +

+vdr-demo-0.0.1.tgz +

+and will unpack into a directory named +

+vdr-demo-0.0.1 +

+To use the plugins and plugins-clean targets from the VDR Makefile +you need to unpack such an archive into the VDR/PLUGINS/src directory and +create a symbolic link with the basic plugin name, as in + +


+ln -s vdr-demo-0.0.1 demo +

+ +Since the VDR Makefile only searches for directories with names consisting +of only lowercase characters and digits, it will only follow the symbolic links, which +should lead to the current version of the plugin you want to use. This way you can +have several different versions of a plugin source (like vdr-demo-0.0.1 and +vdr-demo-0.0.2) and define which one to actually use through the symbolic link. + +


Initializing a new plugin directory

+ +
A room with a view

+ +Call the Perl script newplugin from the VDR source directory to create +a new plugin directory with a Makefile and a main source file implementing +the basic derived plugin class. +You will also find a README file there with some inital text, where you +should fill in actual information about your project. +A HISTORY file is set up with an "Initial revision" entry. As your project +evolves, you should add the changes here with date and version number. +

+newplugin also creates a copy of the GPL license file COPYING, +assuming that you will release your work under that license. Change this if you +have other plans. +

+Add further files and maybe subdirectories to your plugin source directory as +necessary. Don't forget to adapt the Makefile appropriately. + +


The actual implementation

+ +
Use the source, Luke!

+ +A newly initialized plugin doesn't really do very much yet. +If you load it into VDR you will find a new +entry in the main menu, with the same name as your plugin (where the first character +has been converted to uppercase). There will also be a new entry named "Plugins" in +the "Setup" menu, which will bring up a list of all loaded plugins, through which you +can access each plugin's own setup parameters (if it provides any). +

+To implement actual functionality into your plugin you need to edit the source file +that was generated as PLUGINS/src/name.c. Read the comments in that file +to see where you can bring in your own code. The following sections of this document +will walk you through the individual member functions of the plugin class. +

+Depending on what your plugin shall do, you may or may not need all of the given +member functions. Except for the MainMenuEntry() function they all by default +return values that will result in no actual functionality. You can either completely +delete unused functions from your source file, or just leave them as they are. +If your plugin shall not be accessible through VDR's main menu, simply remove +(or comment out) the line implementing the MainMenuEntry() function. +

+At the end of the plugin's source file you will find a line that looks like this: + +


+VDRPLUGINCREATOR(cPluginDemo); +

+ +This is the "magic" hook that allows VDR to actually load the plugin into +its memory. You don't need to worry about the details behind all this. +

+If your plugin requires additional source files, simply add them to your plugin's +source directory and adjust the Makefile accordingly. + +


Construction and Destruction

+ +
What goes up, must come down...

+ +The constructor and destructor of a plugin are defined as + +


+cPlugin(void); +virtual ~cPlugin(); +

+ +The constructor shall initialize any member variables the plugin defines, but +must not access any global structures of VDR. +It also must not create any threads or other large data structures. These things +are done in the Start() function later. +Constructing a plugin object shall not have any side effects or produce any output, +since VDR, for instance, has to create the plugin objects in order to get their +command line help - and after that immediately destroys them again. +

+The destructor has to clean up any data created by the plugin, and has to +take care that any threads the plugin may have created will be stopped. +

+Of course, if your plugin doesn't define any member variables that need to be +initialized (and deleted), you don't need to implement either of these functions. + +


Version number

+ +
Which incarnation is this?

+ +Every plugin must have a version number of its own, which does not necessarily +have to be in any way related to the VDR version number. +VDR requests a plugin's version number through a call to the function + +


+virtual const char *Version(void) = 0; +

+ +Since this is a "pure" virtual function, any derived plugin class must +implement it. The returned string should identify this version of the plugin. +Typically this would be something like "0.0.1", but it may also contain other +information, like for instance "0.0.1pre2" or the like. The string should only +be as long as really necessary, and shall not contain the plugin's name itself. +Here's an example: + +


+static const char *VERSION = "0.0.1"; + +... + +const char *cPluginDemo::Version(void) +{ + return VERSION; +} +

+ +Note that the definition of the version number is expected to be located in the +main source file, and must be written as +

+static const char *VERSION = ...
+
+just like shown in the above example. This is a convention that allows the Makefile +to extract the version number when generating the file name for the distribution archive. +

+A new plugin project should start with version number 0.0.1 and should reach +version 1.0.0 once it is completely operative and well tested. Following the +Linux kernel version numbering scheme, versions with even release numbers +(like 1.0.x, 1.2.x, 1.4.x...) should be stable releases, +while those with odd release numbers (like 1.1.x, 1.3.x, +1.5.x...) are usually considered "under development". The three parts of +a version number are not limited to single digits, so a version number of 1.2.15 +would be acceptable. + +


Description

+ +
What is it that you do?

+ +In order to tell the user what exactly a plugin does, it must implement the function + +


+virtual const char *Description(void) = 0; +

+ +which returns a short, one line description of the plugin's purpose. + +


+virtual const char *Description(void) +{ + return "A simple demo plugin"; +} +

+ +


Command line arguments

+ +
Taking orders

+ +A VDR plugin can have command line arguments just like any normal program. +If a plugin wants to react on command line arguments, it needs to implement +the function + +


+virtual bool ProcessArgs(int argc, char *argv[]); +

+ +The parameters argc and argv have exactly the same meaning +as in a normal C program's main() function. +argv[0] contains the name of the plugin (as given in the -P +option of the vdr call). +

+Each plugin has its own set of command line options, which are totally independent +from those of any other plugin or VDR itself. +

+You can use the getopt() or getopt_long() function to process +these arguments. As with any normal C program, the strings pointed to by argv +will survive the entire lifetime of the plugin, so it is safe to store pointers to +these values inside the plugin. Here's an example: + +


+bool cPluginDemo::ProcessArgs(int argc, char *argv[]) +{ + static struct option long_options[] = { + { "aaa", required_argument, NULL, 'a' }, + { "bbb", no_argument, NULL, 'b' }, + { NULL } + }; + + int c; + while ((c = getopt_long(argc, argv, "a:b", long_options, NULL)) != -1) { + switch (c) { + case 'a': fprintf(stderr, "option -a = %s\n", optarg); + break; + case 'b': fprintf(stderr, "option -b\n"); + break; + default: return false; + } + } + return true; +} +

+ +The return value must be true if all options have been processed +correctly, or false in case of an error. The first plugin that returns +false from a call to its ProcessArgs() function will cause VDR +to exit. + +


Command line help

+ +
Tell me about it...

+ +If a plugin accepts command line options, it should implement the function + +


+virtual const char *CommandLineHelp(void); +

+ +which will be called if the user enters the -h option when starting VDR. +The returned string should contain the command line help for this plugin, formatted +in the same way as done by VDR itself: + +


+const char *cPluginDemo::CommandLineHelp(void) +{ + return " -a ABC, --aaa=ABC do something nice with ABC\n" + " -b, --bbb activate 'plan B'\n"; +} +

+ +This command line help will be printed directly below VDR's help texts (separated +by a line indicating the plugin's name, version and description), so if you use the +same formatting as shown here it will line up nicely. +Note that all lines should be terminated with a newline character, and should +be shorter than 80 characters. + +


Getting started

+ +
Let's get ready to rumble!

+ +If a plugin implements a function that runs in the background (presumably in a +thread of its own), or wants to make use of internationalization, +it needs to implement the function + +


+virtual void Start(void); +

+ +which is called once for each plugin at program startup. +Inside this function the plugin must set up everything necessary to perform +its task. This may, for instance, be a thread that collects data from the DVB +stream, which is later presented to the user via a function that is available +from the main menu. +

+If the plugin doesn't implement any background functionality or internationalized +texts, it doesn't need to implement this function. + +


Main menu entry

+ +
Today's special is...

+ +If the plugin implements a feature that the user shall be able to access +from VDR's main menu, it needs to implement the function + +


+virtual const char *MainMenuEntry(void); +

+ +The default implementation returns a NULL pointer, which means that +this plugin will not have an item in the main menu. Here's an example of a +plugin that will have a main menu item: + +


+const char *cPluginDemo::MainMenuEntry(void) +{ + return "Demo"; +} +

+ +The menu entries of all plugins will be inserted into VDR's main menu right +after the Recordings item, in the same sequence as they were given +in the call to VDR. + +


User interaction

+ +
It's showtime!

+ +If the user selects the main menu entry of a plugin, VDR calls the function + +


+virtual cOsdMenu *MainMenuAction(void); +

+ +which can do one of two things: +

+ +It is very important that a call to MainMenuAction() returns as soon +as possible! As long as the program stays inside this function, no other user +interaction is possible. If a specific action takes longer than a few seconds, +the plugin should launch a separate thread to do this. + + +

Setup parameters

+ +
Remember me...

+ +If a plugin requires its own setup parameters, it needs to implement the following +functions to handle these parameters: + +


+virtual cMenuSetupPage *SetupMenu(void); +virtual bool SetupParse(const char *Name, const char *Value); +

+ +The SetupMenu() function shall return the plugin's "Setup" menu +page, where the user can adjust all the parameters known to this plugin. +

+SetupParse() will be called for each parameter the plugin has +previously stored in the global setup data (see below). It shall return +true if the parameter was parsed correctly, false in case of +an error. If false is returned, an error message will be written to +the log file (and program execution will continue). +

+The plugin's setup parameters are stored in the same file as VDR's parameters. +In order to allow each plugin (and VDR itself) to have its own set of parameters, +the Name of each parameter will be preceeded with the plugin's +name, as in +

+demo.SomeParameter = 123 +

+The prefix will be handled by the core VDR setup code, so the individual +plugins need not worry about this. +

+To store its values in the global setup, a plugin has to call the function + +


+void SetupStore(const char *Name, type Value); +

+ +where Name is the name of the parameter ("SomeParameter" in the above +example, without the prefix "demo.") and Value is a simple data type (like +char *, int etc). +Note that this is not a function that the individual plugin class needs to implement! +SetupStore() is a non-virtual member function of the cPlugin class. +

+To remove a parameter from the setup data, call SetupStore() with the appropriate +name and without any value, as in +

+SetupStore("SomeParameter"); +

+The VDR menu "Setup/Plugins" will list all loaded plugins with their name, +version number and description. Selecting an item in this list will bring up +the plugin's "Setup" menu if that plugin has implemented the SetupMenu() +function. +

+Finally, a plugin doesn't have to implement the SetupMenu() if it only +needs setup parameters that are not directly user adjustable. It can use +SetupStore() and SetupParse() without presenting these +parameters to the user. + +


Internationalization

+ +
Welcome to Babylon!

+ +If a plugin displays texts to the user, it should implement internationalized +versions of these texts and call the function + +


+void RegisterI18n(const tI18nPhrase * const Phrases); +

+ +to register them with VDR's internationalization mechanism. +

+The call to this function must be done in the Start() function of the plugin: + +


+const tI18nPhrase Phrases[] = { + { "Hello world!", + "Hallo Welt!", + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + "",// TODO + }, + { NULL } + }; + +void cPluginDemo::Start(void) +{ + RegisterI18n(Phrases); +} +

+ +Each entry of type tI18nPhrase must have exactly as many members as defined +by the constant I18nNumLanguages in the file VDR/i18n.h, and the +sequence of the various languages must be the same as defined in VDR/i18n.c.
+It is very important that the array is terminated with a { NULL } +entry!. +

+Usually you won't be able to fill in all the different translations by yourself, so +you may want to contact the maintainers of these languages (listed in the file +VDR/i18n.c) and ask them to provide the additional translations. +

+The actual runtime selection of the texts corresponding to the selected language +is done by wrapping each internationalized text with the tr() macro: + +


+const char *s = tr("Hello world!"); +

+ +The text given here must be the first one defined in the related Phrases +entry (which is the English version), and the returned pointer is either a translated +version (if available) or the original string. In the latter case a message will be +written to the log file, indicating that a translation is missing. +Texts are first searched for in the Phrases registered for this plugin (if any) +and then in the global VDR texts. So a plugin can make use of texts defined by the +core VDR code. + +


Loading plugins into VDR

+ +
Saddling up!

+ +Plugins are loaded into VDR using the command line option -P, as in + +


+vdr -Pdemo +

+ +If the plugin accepts command line options, they are given as part of the argument +to the -P option, which then has to be enclosed in quotes: + +


+vdr -P"demo -a abc -b" +

+ +Any number of plugins can be loaded this way, each with its own -P option: + +


+vdr -P"demo -a abc -b" -Pdvd -Pmp3 +

+ +If you are not starting VDR from the VDR source directory (and thus your plugins +cannot be found at their default location) you need to tell VDR the location of +the plugins through the -L option: + +


+vdr -L/usr/lib/vdr -Pdemo +

+ +There can be any number of -L options, and each of them will apply to the +-P options following it. +

+When started with the -h or -V option (for help +or version information, respectively), VDR will automatically load all plugins +in the default or given directory that match the VDR plugin +naming convention, +and display their help and/or version information in addition to its own output. + +


Building the distribution package

+ +
Let's get this show on the road!

+ +If you want to make your plugin available to other VDR users, you'll need to +make a package that can be easily distributed. +The Makefile that has been created by the call to +newplugin +provides the target package, which does this for you. +

+Simply change into your source directory and execute make package: + +


+cd VDR/PLUGINS/src/demo +make package +

+ +After this you should find a file named like + +


+vdr-demo-0.0.1.tgz +

+ +in your source directory, where demo will be replaced with your actual +plugin's name, and 0.0.1 will be your plugin's current version number. + + + -- cgit v1.2.3