+
@@ -210,7 +212,7 @@ additional libraries foo and bar, the names would be
-A room with a view
+ 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
@@ -229,7 +231,7 @@ necessary. Don't forget to adapt the Makefile appropriately.
-Use the source, Luke!
+ 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
@@ -252,7 +254,7 @@ If your plugin shall not be accessible through VDR's main menu, simply remove
At the end of the plugin's source file you will find a line that looks like this:
-
+
VDRPLUGINCREATOR(cPluginHello);
|
@@ -265,7 +267,7 @@ source directory and adjust the Makefile accordingly.
Header files usually contain preprocessor statements that prevent the same
file (or rather its contents, to be precise) from being included more than once, like
-
+
#ifndef __I18N_H
#define __I18N_H
@@ -296,11 +298,11 @@ and two replacing the dot).
-What goes up, must come down...
+ What goes up, must come down...
The constructor and destructor of a plugin are defined as
-
+
cPlugin(void);
virtual ~cPlugin();
|
@@ -325,13 +327,13 @@ initialized (and deleted), you don't need to implement either of these functions
-Which incarnation is this?
+ 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;
|
@@ -342,7 +344,7 @@ 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 *cPluginHello::Version(void)
@@ -371,17 +373,17 @@ would be acceptable.
-What is it that you do?
+ 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:
-
+
static const char *DESCRIPTION = "A friendly greeting";
virtual const char *Description(void)
@@ -395,13 +397,13 @@ to be internationalized.
-Taking orders
+ 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[]);
|
@@ -418,7 +420,7 @@ these arguments. As with any normal C program, the strings pointed to by arg
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 cPluginHello::ProcessArgs(int argc, char *argv[])
{
// Implement command line argument processing here if applicable.
@@ -449,11 +451,11 @@ to exit.
-Tell me about it...
+ Tell me about it...
If a plugin accepts command line options, it should implement the function
-
+
virtual const char *CommandLineHelp(void);
|
@@ -461,7 +463,7 @@ which will be called if the user enters the -h option when start
The returned string should contain the command line help for this plugin, formatted
in the same way as done by VDR itself:
-
+
const char *cPluginHello::CommandLineHelp(void)
{
// Return a string that describes all known command line options.
@@ -478,13 +480,13 @@ be shorter than 80 characters.
-Let's get ready to rumble!
+ 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 one of the functions
-
+
virtual bool Initialize(void);
virtual bool Start(void);
|
@@ -516,11 +518,11 @@ texts, it doesn't need to implement either of these functions.
-Stop it, right there!
+ Stop it, right there!
If a plugin performs any background tasks, it shall implement the function
-
+
@@ -533,7 +535,7 @@ as the Start() functions were called.
-Traces in the sand...
+ Traces in the sand...
If the plugin should print log messages, you can use dsyslog(), isyslog() or esyslog().
@@ -545,7 +547,7 @@ If the plugin should print log messages, you can use dsyslog(), isy
The output of this log is the syslog of the system vdr is running on.
The log message can be formatted like printf(), as in
-
+
esyslog("pluginname: error #%d has occurred", ErrorNumber);
|
@@ -579,12 +581,12 @@ of a program, and they may not be able to read them if they are in an exotic lan
-Today's special is...
+ 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);
|
@@ -592,7 +594,7 @@ 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:
-
+
static const char *MAINMENUENTRY = "Hello";
const char *cPluginHello::MainMenuEntry(void)
@@ -607,11 +609,11 @@ in the call to VDR.
-It's showtime!
+ It's showtime!
If the user selects the main menu entry of a plugin, VDR calls the function
-
+
virtual cOsdObject *MainMenuAction(void);
|
@@ -640,12 +642,12 @@ the plugin should launch a separate thread to do this.
-Chores, chores...
+ Chores, chores...
From time to time a plugin may want to do some regular tasks, like cleaning
up some files or other things. In order to do this it can implement the function
-
+
virtual void Housekeeping(void);
|
@@ -665,7 +667,7 @@ the plugin should launch a separate thread to do this.
-Pushing in...
+ Pushing in...
Normally a plugin only reacts on user input if directly called through its
main menu entry, or performs some background
@@ -673,7 +675,7 @@ activity in a separate thread. However, sometimes a plugin may need to do
something in the context of the main program thread, without being explicitly
called up by the user. In such a case it can implement the function
-
+
virtual void MainThreadHook(void);
|
@@ -686,19 +688,19 @@ interface performance will become sluggish!
-Now is not a good time!
+ Now is not a good time!
If a plugin is running a background task that should be finished before shutting
down the system, it can implement the function
-
+
virtual cString Active(void);
|
which shall return an empty string if it is ok to shut down, and a proper message
if not:
-
+
cString cDoSomethingPlugin::Active(void)
{
if (busy)
@@ -722,13 +724,13 @@ further plugins will be queried and no shutdown will be done.
-Wake me up before you go-go
+ Wake me up before you go-go
If a plugin wants to schedule activity for a later time, or wants to perform
periodic activity at a certain time at night, and if VDR shall wake up from
shutdown at that time, the plugin can implement the function
-
+
virtual time_t WakeupTime(void);
|
@@ -737,7 +739,7 @@ is planned. VDR will pass the most recent wakeup time of all plugins, or the nex
timer time, whichever comes first, to the shutdown script. The following sample
will wake up VDR every night at 1:00:
-
+
time_t MyPlugin::WakeupTime(void)
{
time_t Now = time(NULL);
@@ -755,12 +757,12 @@ the future, the time will be ignored.
-Remember me...
+ 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);
|
@@ -775,7 +777,7 @@ an error. If false is returned, an error message will be written to
the log file (and program execution will continue).
A possible implementation of SetupParse() could look like this:
-
+
bool cPluginHello::SetupParse(const char *Name, const char *Value)
{
// Parse your own setup parameters and store their values.
@@ -801,7 +803,7 @@ 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);
|
@@ -828,13 +830,13 @@ parameters to the user.
-Have it your way!
+ Have it your way!
To implement a Setup menu, a plugin needs to derive a class from
cMenuSetupPage and implement its constructor and the pure virtual
Store() member function:
-
+
int GreetingTime = 3;
int UseAlternateGreeting = false;
@@ -852,7 +854,7 @@ cMenuSetupHello::cMenuSetupHello(void)
{
newGreetingTime = GreetingTime;
newUseAlternateGreeting = UseAlternateGreeting;
- Add(new cMenuEdittItem( tr("Greeting time (s)"), &newGreetingTime));
+ Add(new cMenuEditIntItem( tr("Greeting time (s)"), &newGreetingTime));
Add(new cMenuEditBoolItem(tr("Use alternate greeting"), &newUseAlternateGreeting));
}
@@ -886,7 +888,7 @@ your setup parameters and use that one to copy all parameters with one single st
-I want my own stuff!
+ I want my own stuff!
There may be situations where a plugin requires files of its own. While the plugin is
free to store such files anywhere it sees fit, it might be a good idea to put them in a common
@@ -904,7 +906,7 @@ original values that are stored elsewhere.
Therefore VDR provides the functions
-
+
const char *ConfigDirectory(const char *PluginName = NULL);
const char *CacheDirectory(const char *PluginName = NULL);
const char *ResourceDirectory(const char *PluginName = NULL);
@@ -930,7 +932,7 @@ these in a subdirectory of its own, named after the plugin. To easily get such a
the functions can be given an additional string that will be appended to the returned
directory name, as in
-
+
const char *MyConfigDir = ConfigDirectory(Name());
|
@@ -946,19 +948,19 @@ The ConfigDirectory(), CacheDirectory() and ResourceDirect
functions are static member functions of the cPlugin class. This allows them to be
called even from outside any member function of the derived plugin class, by writing
-
+
const char *MyConfigDir = cPlugin::ConfigDirectory();
|
-Welcome to Babylon!
+ Welcome to Babylon!
If a plugin displays texts to the user, it should prepare for internationalization
of these texts. All that is necessary for this is to mark every text that is
presented to the user as translatable, as in
-
+
const char *s = tr("Hello world!");
|
@@ -973,7 +975,7 @@ Sometimes texts are stored in an array, in which case they need to be marked
differently, using the trNOOP() macro. The actual translation is then done
when such a text is used, as in
-
+
const char *Texts = {
trNOOP("First text"),
trNOOP("Second text"),
@@ -998,7 +1000,7 @@ handling individual characters these functions may come in handy.
-What can I do for you?
+ What can I do for you?
In some situations, two plugins may want to communicate directly, talking about things
that VDR doesn't handle itself. For example, a plugin may want to use features
@@ -1006,7 +1008,7 @@ that some other plugin offers, or it may want to inform other plugins about impo
things it does. To receive requests or messages, a plugin can implement the
following function:
-
+
virtual bool Service(const char *Id, void *Data = NULL);
|
@@ -1023,7 +1025,7 @@ otherwise. The plugins have to agree in which situations the service
may be called, for example whether the service may be called from every thread, or
just from the main thread. A possible implementation could look like this:
-
+
struct Hello_SetGreetingTime_v1_0 {
int NewGreetingTime;
};
@@ -1046,7 +1048,7 @@ this as a 'service supported' check without performing any actions.
To send messages to, or request services from a specific plugin, one plugin can directly
call another plugin's service function:
-
+
Hello_SetGreetingTime_v1_0 hellodata;
hellodata.NewGreetingTime = 3;
cPlugin *Plugin = cPluginManager::GetPlugin("hello");
@@ -1066,11 +1068,11 @@ any plugin handled the request, or false if no plugin handled the reque
-Infinite Diversity in Infinite Combinations
+ Infinite Diversity in Infinite Combinations
A plugin can implement its own SVDRP commands through the two functions
-
+
virtual const char **SVDRPHelpPages(void);
virtual cString SVDRPCommand(const char *Cmd, const char *Option, int &ReplyCode);
|
@@ -1078,7 +1080,7 @@ virtual cString SVDRPCommand(const char *Cmd, const char *Option, int &Reply
The SVDRPHelpPages() function must return a pointer to a list of help
strings for all of the plugin's SVDRP commands, like this
-
+
const char **cPluginSvdrpdemo::SVDRPHelpPages(void)
{
static const char *HelpPages[] = {
@@ -1109,7 +1111,7 @@ The actual processing of SVDRP commands for a plugin is done in its
Here's an example of such a function, which implements the commands advertised in
the above help texts:
-
+
cString cPluginSvdrpdemo::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
{
if (strcasecmp(Command, "DATE") == 0) {
@@ -1161,7 +1163,7 @@ global data.
-U can't touch this
+ U can't touch this
When accessing global data structures, proper locking is absolutely necessary.
@@ -1176,7 +1178,7 @@ Using one of these macros sets a lock on the structure, creates a local pointer
respective structure and makes sure the lock is released at the end of the current
block:
-
+
if (some condition) {
LOCK_TIMERS_READ; // creates local const cTimers *Timers
for (const cTimer *Timer = Timers->First(); Timer; Timer = Timers->Next(Timer)) {
@@ -1211,24 +1213,24 @@ to use locking with timeouts, and thread.h, classes cStateLock and
-Saddling up!
+ Saddling up!
Plugins are loaded into VDR using the command line option -P, as in
-
+
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:
-
+
Any number of plugins can be loaded this way, each with its own -P option:
-
+
vdr -P"hello -a abc -b" -Pdvd -Pmp3
|
@@ -1236,7 +1238,7 @@ 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 -Phello
|
@@ -1251,7 +1253,7 @@ and display their help and/or version information in addition to its own output.
-Let's get this show on the road!
+ 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.
@@ -1261,30 +1263,30 @@ provides the target dist, which does this for you.
Simply change into your source directory and execute make dist:
-
+
cd VDR/PLUGINS/src/hello
make dist
|
After this you should find a file named like
-
+
in your source directory, where hello will be replaced with your actual
plugin's name, and 0.0.1 will be your plugin's current version number.
-
+
-A piece of the action
+ A piece of the action
If a plugin wants to get informed on various events in VDR, it can derive a class from
cStatus, as in
-
+
#include <vdr/status.h>
class cMyStatusMonitor : public cStatus {
@@ -1304,7 +1306,7 @@ void cMyStatusMonitor::ChannelSwitch(const cDevice *Device, int ChannelNumber, b
An object of this class will be informed whenever the channel is switched on one of
the DVB devices. It could be used in a plugin like this:
-
+
#include <vdr/plugin.h>
class cPluginStatus : public cPlugin {
@@ -1352,12 +1354,12 @@ the functions you actually want to use.
-Play it again, Sam!
+ Play it again, Sam!
Implementing a player is a two step process.
First you need the actual player class, which is derived from the abstract cPlayer:
-
+
#include <vdr/player.h>
class cMyPlayer : public cPlayer {
@@ -1373,7 +1375,7 @@ What exactly you do in this class is entirely up to you. If you want to run a se
thread which, e.g., reads data from a file, you can additionally derive your class from
cThread and implement the necessary functionality:
-
+
#include <vdr/player.h>
class cMyPlayer : public cPlayer, cThread {
@@ -1391,7 +1393,7 @@ its own player for the VDR recordings.
To play the actual data, the player needs to call its member function
-
+
int PlayPes(const uchar *Data, int Length, bool VideoOnly);
|
@@ -1404,7 +1406,7 @@ desired data stream, and it must be delivered fast enough so that the
DVB device doesn't run out of data.
To avoid busy loops the player should call its member function
-
+
bool DevicePoll(cPoller &Poller, int TimeoutMs = 0);
|
@@ -1416,14 +1418,14 @@ If the player can provide more than a single audio track, and has special
requirements in order to set a given track, it can implement the
following function to allow the device to set a specific track:
-
+
virtual void SetAudioTrack(eTrackType Type, const tTrackId *TrackId)
|
A player that has special requirements about audio tracks should announce its
available audio tracks by calling
-
+
bool DeviceSetAvailableTrack(eTrackType Type, int Index, uint16_t Id, const char *Language = NULL, const char *Description = NULL)
|
@@ -1432,7 +1434,7 @@ See device.h for details about the parameters for track handling.
The second part needed here is a control object that receives user input from the main
program loop and reacts on this by telling the player what to do:
-
+
#include <vdr/player.h>
class cMyControl : public cControl {
@@ -1451,7 +1453,7 @@ public:
hand over a pointer to it to the cControl base class, so that it
can be later attached to the primary DVB device:
-
+
cMyControl::cMyControl(void)
:cControl(player = new cMyPlayer)
{
@@ -1482,7 +1484,7 @@ Finally, to get things going, a plugin that implements a player (and the surroun
infrastructure like displaying a list of playable stuff etc) simply has to call the
static function cControl::Launch() with the player control object, as in
-
+
cControl::Launch(new cMyControl);
|
@@ -1493,7 +1495,7 @@ use the primary DVB device, or the user decides to start a different replay).
The cPlayer class has a member function
- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|