1
0
mirror of https://github.com/VDR4Arch/vdr.git synced 2023-10-10 13:36:52 +02:00

Implemented plugin interface

This commit is contained in:
Klaus Schmidinger 2002-05-09 16:26:56 +02:00
parent bf4a5a678d
commit b527b27708
25 changed files with 3134 additions and 807 deletions

14
HISTORY
View File

@ -1,7 +1,9 @@
Video Disk Recorder Revision History Video Disk Recorder Revision History
------------------------------------ ------------------------------------
2000-02-19: Version 0.01 (Initial revision). 2000-02-19: Version 0.01
- Initial revision.
2000-03-11: Version 0.02 2000-03-11: Version 0.02
@ -1242,3 +1244,13 @@ Video Disk Recorder Revision History
'channels.conf.cable' whenever a new version is sent to me. 'channels.conf.cable' whenever a new version is sent to me.
- Fixed skipping forward in time shift mode near the end of the recording (thanks - Fixed skipping forward in time shift mode near the end of the recording (thanks
to Andreas Böttger for reporting this one). to Andreas Böttger for reporting this one).
2002-05-09: Version 1.1.0
- Begin of the 1.1 development branch. THIS IS NOT A STABLE VERSION!
The current stable version for every day use is still the 1.0 branch.
- First step towards a universal plugin interface. See the file PLUGINS.html
for a detailed description. The man page vdr(1) describes the new options '-L'
and '-P' used to load plugins. This first step implements the complete "outer"
shell for plugins. The "inner" access to VDR data structures will follow.
- The VDR version number is now displayed in the title line of the "Setup" menu.

View File

@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and # See the main source file 'vdr.c' for copyright information and
# how to reach the author. # how to reach the author.
# #
# $Id: Makefile 1.33 2002/04/01 12:50:48 kls Exp $ # $Id: Makefile 1.34 2002/05/09 09:35:05 kls Exp $
.DELETE_ON_ERROR: .DELETE_ON_ERROR:
@ -13,15 +13,17 @@ DTVDIR = ./libdtv
MANDIR = /usr/local/man MANDIR = /usr/local/man
BINDIR = /usr/local/bin BINDIR = /usr/local/bin
PLUGINDIR= ./PLUGINS
VIDEODIR = /video VIDEODIR = /video
INCLUDES = -I$(DVBDIR)/ost/include INCLUDES = -I$(DVBDIR)/ost/include
DTVLIB = $(DTVDIR)/libdtv.a DTVLIB = $(DTVDIR)/libdtv.a
OBJS = config.o dvbapi.o dvbosd.o eit.o font.o i18n.o interface.o menu.o osd.o\ OBJS = config.o dvbapi.o dvbosd.o eit.o font.o i18n.o interface.o menu.o\
recording.o remote.o remux.o ringbuffer.o svdrp.o thread.o tools.o vdr.o\ menuitems.o osd.o plugin.o recording.o remote.o remux.o ringbuffer.o\
videodir.o svdrp.o thread.o tools.o vdr.o videodir.o
OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1 OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1 FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1
@ -69,7 +71,7 @@ include $(DEPFILE)
# The main program: # The main program:
vdr: $(OBJS) $(DTVLIB) vdr: $(OBJS) $(DTVLIB)
g++ -g -O2 $(OBJS) $(NCURSESLIB) -ljpeg -lpthread $(LIBDIRS) $(DTVLIB) -o vdr g++ -g -O2 -rdynamic $(OBJS) $(NCURSESLIB) -ljpeg -lpthread -ldl $(LIBDIRS) $(DTVLIB) -o vdr
# The font files: # The font files:
@ -88,6 +90,21 @@ genfontfile: genfontfile.c
$(DTVLIB) $(DTVDIR)/libdtv.h: $(DTVLIB) $(DTVDIR)/libdtv.h:
make -C $(DTVDIR) all make -C $(DTVDIR) all
# The 'include' directory (for plugins):
include-dir:
@mkdir -p include/vdr
@(cd include/vdr; for i in ../../*.h; do ln -fs $$i .; done)
# Plugins:
plugins: include-dir
@for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do make -C "$(PLUGINDIR)/src/$$i" all; done
plugins-clean:
@for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do make -C "$(PLUGINDIR)/src/$$i" clean; done
@-rm -f $(PLUGINDIR)/lib/*
# Install the files: # Install the files:
install: install:
@ -104,6 +121,7 @@ install:
clean: clean:
make -C $(DTVDIR) clean make -C $(DTVDIR) clean
-rm -f $(OBJS) $(DEPFILE) vdr genfontfile genfontfile.o core* *~ -rm -f $(OBJS) $(DEPFILE) vdr genfontfile genfontfile.o core* *~
-rm -rf include
fontclean: fontclean:
-rm -f fontfix.c fontosd.c -rm -f fontfix.c fontosd.c
CLEAN: clean fontclean CLEAN: clean fontclean

620
PLUGINS.html Normal file
View File

@ -0,0 +1,620 @@
<html>
<head>
<title>The VDR Plugin System</title>
</head>
<body bgcolor="white">
<h1>The VDR Plugin System</h1>
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).
<p>
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.
<!--<p>TODO: Link to the document about VDR base classes to use when implementing actual functionality (yet to be written).-->
<hr><h2>Quick start</h2>
<center><i><b>Can't wait, can't wait!</b></i></center><p>
Actually you should read this entire document before starting to work with VDR plugins,
but you probably want to see something happening right away <tt>;-)</tt>
<p>
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:
<ul>
<li>change into the VDR source directory
<li><b><tt>make</tt></b> the VDR program with your usual <tt>REMOTE=...</tt> (and maybe other) options
<li>do <b><tt>make plugins</tt></b> to build the demo plugin
<li>run VDR with <b><tt>vdr -V</tt></b> to see the version information
<li>run VDR with <b><tt>vdr -h</tt></b> to see the command line options
<li>run VDR with <b><tt>vdr -Phello</tt></b>
<li>open VDR's main menu and select the <i>Hello</i> item
<li>open the <i>Setup</i> menu from VDR's main menu and select <i>Plugins</i>
</ul>
If you enjoyed this brief glimpse into VDR plugin handling, read through the rest of
this document and eventually write your own VDR plugin.
<hr><h2>The name of the plugin</h2>
<center><i><b>Give me some I.D.!</b></i></center><p>
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.
<p>
The plugin's name should typically be as short as possible. Three letter
abbreviations like <b><tt>dvd</tt></b> (for a DVD player) or <b><tt>mp3</tt></b>
(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.
<p>
A plugin can access its name through the (non virtual) member function
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
const char *Name(void);
</pre></td></tr></table><p>
The actual name is derived from the plugin's library file name, as defined in the
next chapter.
<a name="The plugin directory structure"><hr><h2>The plugin directory structure</h2>
<center><i><b>Where is everybody?</b></i></center><p>
By default plugins are located in a directory named <tt>PLUGINS</tt> below the
VDR source directory. Inside this directory the following subdirectory structure
is used:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
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
</pre></td></tr></table><p>
The <tt>src</tt> directory contains one subdirectory for each plugin, which carries
the name of that plugin (in the above example that would be <tt>demo</tt> and
<tt>hello</tt>, 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 <tt>Makefile</tt> that provides the targets <tt>all</tt> and
<tt>clean</tt>, and that a call to <tt>make all</tt> actually produces a dynamically
loadable library file for that plugin (we'll get to the details later).
<p>
The <tt>lib</tt> directory contains the dynamically loadable libraries of all
available plugins. Note that the names of these files are created by concatenating
<p>
<table border=2>
<tr><td align=center><b><tt>libvdr-</tt></b></td><td align=center><b><tt>demo</tt></b></td><td align=center><b><tt>.so.</tt></b></td><td align=center><b><tt>1.1.0</tt></b></td></tr>
<tr><td align=center><font size=-1>VDR plugin<br>library prefix</font></td><td align=center><font size=-1>name of<br>the plugin</font></td><td align=center><font size=-1>shared object<br>indicator</font></td><td align=center><font size=-1>VDR version number<br>this plugin was<br>compiled for</font></td></tr>
</table>
<p>
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
<b><tt>-L</tt></b> option.
<p>
The VDR <tt>Makefile</tt> contains the target <tt>plugins</tt>, which calls
<tt>make all</tt> in every directory found under <tt>VDR/PLUGINS/src</tt>,
plus the target <tt>plugins-clean</tt>, which calls <tt>make clean</tt> in
each of these directories.
<p>
If you download a plugin <a href="#Building the distribution package">package</a>
from the web, it will typically have a name like
<p>
<tt>vdr-demo-0.0.1.tgz</tt>
<p>
and will unpack into a directory named
<p>
<tt>vdr-demo-0.0.1</tt>
<p>
To use the <tt>plugins</tt> and <tt>plugins-clean</tt> targets from the VDR <tt>Makefile</tt>
you need to unpack such an archive into the <tt>VDR/PLUGINS/src</tt> directory and
create a symbolic link with the basic plugin name, as in
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
ln -s vdr-demo-0.0.1 demo
</pre></td></tr></table><p>
Since the VDR <tt>Makefile</tt> 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 <tt>vdr-demo-0.0.1</tt> and
<tt>vdr-demo-0.0.2</tt>) and define which one to actually use through the symbolic link.
<a name="Initializing a new plugin directory"><hr><h2>Initializing a new plugin directory</h2>
<center><i><b>A room with a view</b></i></center><p>
Call the Perl script <tt>newplugin</tt> from the VDR source directory to create
a new plugin directory with a <tt>Makefile</tt> and a main source file implementing
the basic derived plugin class.
You will also find a <tt>README</tt> file there with some inital text, where you
should fill in actual information about your project.
A <tt>HISTORY</tt> file is set up with an "Initial revision" entry. As your project
evolves, you should add the changes here with date and version number.
<p>
<tt>newplugin</tt> also creates a copy of the GPL license file <tt>COPYING</tt>,
assuming that you will release your work under that license. Change this if you
have other plans.
<p>
Add further files and maybe subdirectories to your plugin source directory as
necessary. Don't forget to adapt the <tt>Makefile</tt> appropriately.
<hr><h2>The actual implementation</h2>
<center><i><b>Use the source, Luke!</b></i></center><p>
A newly initialized plugin doesn't really do very much yet.
If you <a href="#Loading plugins into VDR">load it into VDR</a> 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).
<p>
To implement actual functionality into your plugin you need to edit the source file
that was generated as <tt>PLUGINS/src/name.c</tt>. 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.
<p>
Depending on what your plugin shall do, you may or may not need all of the given
member functions. Except for the <tt>MainMenuEntry()</tt> 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 <tt>MainMenuEntry()</tt> function.
<p>
At the end of the plugin's source file you will find a line that looks like this:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
VDRPLUGINCREATOR(cPluginDemo);
</pre></td></tr></table><p>
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.
<p>
If your plugin requires additional source files, simply add them to your plugin's
source directory and adjust the <tt>Makefile</tt> accordingly.
<hr><h2>Construction and Destruction</h2>
<center><i><b>What goes up, must come down...</b></i></center><p>
The constructor and destructor of a plugin are defined as
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
cPlugin(void);
virtual ~cPlugin();
</pre></td></tr></table><p>
The <b>constructor</b> shall initialize any member variables the plugin defines, but
<b>must not access any global structures of VDR</b>.
It also must not create any threads or other large data structures. These things
are done in the <a href="#Getting started"><tt>Start()</tt></a> 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.
<p>
The <b>destructor</b> 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.
<p>
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.
<hr><h2>Version number</h2>
<center><i><b>Which incarnation is this?</b></i></center><p>
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
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual const char *Version(void) = 0;
</pre></td></tr></table><p>
Since this is a "pure" virtual function, any derived plugin class <b>must</b>
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:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
static const char *VERSION = "0.0.1";
...
const char *cPluginDemo::Version(void)
{
return VERSION;
}
</pre></td></tr></table><p>
Note that the definition of the version number is expected to be located in the
main source file, and must be written as
<pre>
static const char *VERSION = ...
</pre>
just like shown in the above example. This is a convention that allows the <tt>Makefile</tt>
to extract the version number when generating the file name for the distribution archive.
<p>
A new plugin project should start with version number <tt>0.0.1</tt> and should reach
version <tt>1.0.0</tt> once it is completely operative and well tested. Following the
Linux kernel version numbering scheme, versions with <i>even</i> release numbers
(like <tt>1.0.x</tt>, <tt>1.2.x</tt>, <tt>1.4.x</tt>...) should be stable releases,
while those with <i>odd</i> release numbers (like <tt>1.1.x</tt>, <tt>1.3.x</tt>,
<tt>1.5.x</tt>...) are usually considered "under development". The three parts of
a version number are not limited to single digits, so a version number of <tt>1.2.15</tt>
would be acceptable.
<hr><h2>Description</h2>
<center><i><b>What is it that you do?</b></i></center><p>
In order to tell the user what exactly a plugin does, it must implement the function
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual const char *Description(void) = 0;
</pre></td></tr></table><p>
which returns a short, one line description of the plugin's purpose.
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual const char *Description(void)
{
return "A simple demo plugin";
}
</pre></td></tr></table><p>
<hr><h2>Command line arguments</h2>
<center><i><b>Taking orders</b></i></center><p>
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
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual bool ProcessArgs(int argc, char *argv[]);
</pre></td></tr></table><p>
The parameters <tt>argc</tt> and <tt>argv</tt> have exactly the same meaning
as in a normal C program's <tt>main()</tt> function.
<tt>argv[0]</tt> contains the name of the plugin (as given in the <b><tt>-P</tt></b>
option of the <tt>vdr</tt> call).
<p>
Each plugin has its own set of command line options, which are totally independent
from those of any other plugin or VDR itself.
<p>
You can use the <tt>getopt()</tt> or <tt>getopt_long()</tt> function to process
these arguments. As with any normal C program, the strings pointed to by <tt>argv</tt>
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:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
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;
}
</pre></td></tr></table><p>
The return value must be <i>true</i> if all options have been processed
correctly, or <i>false</i> in case of an error. The first plugin that returns
<i>false</i> from a call to its <tt>ProcessArgs()</tt> function will cause VDR
to exit.
<hr><h2>Command line help</h2>
<center><i><b>Tell me about it...</b></i></center><p>
If a plugin accepts command line options, it should implement the function
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual const char *CommandLineHelp(void);
</pre></td></tr></table><p>
which will be called if the user enters the <b><tt>-h</tt></b> 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:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
const char *cPluginDemo::CommandLineHelp(void)
{
return " -a ABC, --aaa=ABC do something nice with ABC\n"
" -b, --bbb activate 'plan B'\n";
}
</pre></td></tr></table><p>
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.
<a name="Getting started"><hr><h2>Getting started</h2>
<center><i><b>Let's get ready to rumble!</b></i></center><p>
If a plugin implements a function that runs in the background (presumably in a
thread of its own), or wants to make use of <a href="#Internationalization">internationalization</a>,
it needs to implement the function
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual void Start(void);
</pre></td></tr></table><p>
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.
<p>
If the plugin doesn't implement any background functionality or internationalized
texts, it doesn't need to implement this function.
<hr><h2>Main menu entry</h2>
<center><i><b>Today's special is...</b></i></center><p>
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
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual const char *MainMenuEntry(void);
</pre></td></tr></table><p>
The default implementation returns a <tt>NULL</tt> 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:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
const char *cPluginDemo::MainMenuEntry(void)
{
return "Demo";
}
</pre></td></tr></table><p>
The menu entries of all plugins will be inserted into VDR's main menu right
after the <i>Recordings</i> item, in the same sequence as they were given
in the call to VDR.
<hr><h2>User interaction</h2>
<center><i><b>It's showtime!</b></i></center><p>
If the user selects the main menu entry of a plugin, VDR calls the function
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual cOsdMenu *MainMenuAction(void);
</pre></td></tr></table><p>
which can do one of two things:
<ul>
<li>Return a pointer to a <tt>cOsdMenu</tt> object which will be displayed
as a submenu of the main menu (just like the <i>Recordings</i> menu, for instance).
That menu can then implement further functionality and, for instance, could
eventually start a custom player to replay a file other than a VDR recording.
<li>Perform a specific action and return <tt>NULL</tt>. In that case the main menu
will be closed after calling <tt>MainMenuAction()</tt>.
</ul>
<b>
It is very important that a call to <tt>MainMenuAction()</tt> 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.
</b>
<hr><h2>Setup parameters</h2>
<center><i><b>Remember me...</b></i></center><p>
If a plugin requires its own setup parameters, it needs to implement the following
functions to handle these parameters:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value);
</pre></td></tr></table><p>
The <tt>SetupMenu()</tt> function shall return the plugin's "Setup" menu
page, where the user can adjust all the parameters known to this plugin.
<p>
<tt>SetupParse()</tt> will be called for each parameter the plugin has
previously stored in the global setup data (see below). It shall return
<i>true</i> if the parameter was parsed correctly, <i>false</i> in case of
an error. If <i>false</i> is returned, an error message will be written to
the log file (and program execution will continue).
<p>
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 <tt>Name</tt> of each parameter will be preceeded with the plugin's
name, as in
<p>
<tt>demo.SomeParameter = 123</tt>
<p>
The prefix will be handled by the core VDR setup code, so the individual
plugins need not worry about this.
<p>
To store its values in the global setup, a plugin has to call the function
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
void SetupStore(const char *Name, <i>type</i> Value);
</pre></td></tr></table><p>
where <tt>Name</tt> is the name of the parameter (<tt>"SomeParameter"</tt> in the above
example, without the prefix <tt>"demo."</tt>) and <tt>Value</tt> is a simple data type (like
<tt>char&nbsp;*</tt>, <tt>int</tt> etc).
Note that this is not a function that the individual plugin class needs to implement!
<tt>SetupStore()</tt> is a non-virtual member function of the <tt>cPlugin</tt> class.
<p>
To remove a parameter from the setup data, call <tt>SetupStore()</tt> with the appropriate
name and without any value, as in
<p>
<tt>SetupStore("SomeParameter");</tt>
<p>
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 <tt>SetupMenu()</tt>
function.
<p>
Finally, a plugin doesn't have to implement the <tt>SetupMenu()</tt> if it only
needs setup parameters that are not directly user adjustable. It can use
<tt>SetupStore()</tt> and <tt>SetupParse()</tt> without presenting these
parameters to the user.
<a name="Internationalization"><hr><h2>Internationalization</h2>
<center><i><b>Welcome to Babylon!</b></i></center><p>
If a plugin displays texts to the user, it should implement internationalized
versions of these texts and call the function
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
void RegisterI18n(const tI18nPhrase * const Phrases);
</pre></td></tr></table><p>
to register them with VDR's internationalization mechanism.
<p>
The call to this function must be done in the <a href="#Getting started"><tt>Start()</tt></a> function of the plugin:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
const tI18nPhrase Phrases[] = {
{ "Hello world!",
"Hallo Welt!",
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
},
{ NULL }
};
void cPluginDemo::Start(void)
{
RegisterI18n(Phrases);
}
</pre></td></tr></table><p>
Each entry of type <tt>tI18nPhrase</tt> must have exactly as many members as defined
by the constant <tt>I18nNumLanguages</tt> in the file <tt>VDR/i18n.h</tt>, and the
sequence of the various languages must be the same as defined in <tt>VDR/i18n.c</tt>.<br>
<b>It is very important that the array is terminated with a <tt>{&nbsp;NULL&nbsp;}</tt>
entry!</b>.
<p>
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
<tt>VDR/i18n.c</tt>) and ask them to provide the additional translations.
<p>
The actual runtime selection of the texts corresponding to the selected language
is done by wrapping each internationalized text with the <tt>tr()</tt> macro:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
const char *s = tr("Hello world!");
</pre></td></tr></table><p>
The text given here must be the first one defined in the related <i>Phrases</i>
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 <i>Phrases</i> 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.
<a name="Loading plugins into VDR"><hr><h2>Loading plugins into VDR</h2>
<center><i><b>Saddling up!</b></i></center><p>
Plugins are loaded into VDR using the command line option <b><tt>-P</tt></b>, as in
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
vdr -Pdemo
</pre></td></tr></table><p>
If the plugin accepts command line options, they are given as part of the argument
to the <b><tt>-P</tt></b> option, which then has to be enclosed in quotes:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
vdr -P"demo -a abc -b"
</pre></td></tr></table><p>
Any number of plugins can be loaded this way, each with its own <b><tt>-P</tt></b> option:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
vdr -P"demo -a abc -b" -Pdvd -Pmp3
</pre></td></tr></table><p>
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 <b><tt>-L</tt></b> option:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
vdr -L/usr/lib/vdr -Pdemo
</pre></td></tr></table><p>
There can be any number of <b><tt>-L</tt></b> options, and each of them will apply to the
<b><tt>-P</tt></b> options following it.
<p>
When started with the <b><tt>-h</tt></b> or <b><tt>-V</tt></b> option (for <i>help</i>
or <i>version</i> information, respectively), VDR will automatically load all plugins
in the default or given directory that match the VDR plugin
<a href="#The plugin directory structure">naming convention</a>,
and display their help and/or version information in addition to its own output.
<a name="Building the distribution package"><hr><h2>Building the distribution package</h2>
<center><i><b>Let's get this show on the road!</b></i></center><p>
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 <tt>Makefile</tt> that has been created by the call to
<a href="#Initializing a new plugin directory"><tt>newplugin</tt></a>
provides the target <tt>package</tt>, which does this for you.
<p>
Simply change into your source directory and execute <tt>make package</tt>:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
cd VDR/PLUGINS/src/demo
make package
</pre></td></tr></table><p>
After this you should find a file named like
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
vdr-demo-0.0.1.tgz
</pre></td></tr></table><p>
in your source directory, where <tt>demo</tt> will be replaced with your actual
plugin's name, and <tt>0.0.1</tt> will be your plugin's current version number.
</body>
</html>

340
PLUGINS/src/hello/COPYING Normal file
View File

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

View File

@ -0,0 +1,6 @@
VDR Plugin 'hello' Revision History
-----------------------------------
2002-05-09: Version 0.0.1
- Initial revision.

View File

@ -0,0 +1,78 @@
#
# Makefile for a Video Disk Recorder plugin
#
# $Id: Makefile 1.1 2002/05/09 15:17:44 kls Exp $
# The official name of this plugin.
# This name will be used in the '-P...' option of VDR to load the plugin.
# By default the main source file also carries this name.
#
PLUGIN = hello
### The version number of this plugin (taken from the main source file):
VERSION = `grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g'`
### The directory environment:
DVBDIR = ../../../../DVB/ost/include
VDRDIR = ../../..
VDRINC = $(VDRDIR)/include
LIBDIR = ../../lib
TMPDIR = /tmp
### The version number of VDR (taken from VDR's "config.h"):
VDRVERSION = `grep 'define VDRVERSION ' $(VDRDIR)/config.h | awk '{ print $$3 }' | sed -e 's/"//g'`
### The name of the distribution archive:
ARCHIVE = vdr-$(PLUGIN)-$(VERSION)
### Includes and Defines (add further entries here):
INCLUDES = -I$(VDRINC) -I$(DVBDIR)
DEFINES = -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
### The object files (add further files here):
OBJS = $(PLUGIN).o i18n.o
### The C++ compiler and options:
CXX = g++
CXXFLAGS = -O2 -Wall -Woverloaded-virtual -m486
### Implicit rules:
%.o: %.c
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
# Dependencies:
MAKEDEP = g++ -MM -MG
DEPFILE = .dependencies
$(DEPFILE): Makefile
@$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
include $(DEPFILE)
### Targets:
all: libvdr-$(PLUGIN).so
libvdr-$(PLUGIN).so: $(OBJS)
$(CXX) $(CXXFLAGS) -shared $(OBJS) -o $@
@cp $@ $(LIBDIR)/$@.$(VDRVERSION)
package: clean
@-rm -rf $(TMPDIR)/$(ARCHIVE)
@mkdir $(TMPDIR)/$(ARCHIVE)
@cp -a * $(TMPDIR)/$(ARCHIVE)
@tar czf $(ARCHIVE).tgz -C $(TMPDIR) $(ARCHIVE)
@-rm -rf $(TMPDIR)/$(ARCHIVE)
@echo Distribution archive created as $(ARCHIVE).tgz
clean:
@-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~

11
PLUGINS/src/hello/README Normal file
View File

@ -0,0 +1,11 @@
This is a "plugin" for the Video Disk Recorder (VDR).
Written by: Klaus Schmidinger <kls@cadsoft.de>
Project's homepage: www.cadsoft.de/people/kls/vdr
Latest version available at: www.cadsoft.de/people/kls/vdr/software.htm
See the file COPYING for license information.
Description: This is a small demo of the VDR plugin interface.

105
PLUGINS/src/hello/hello.c Normal file
View File

@ -0,0 +1,105 @@
/*
* hello.c: A plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
* $Id: hello.c 1.1 2002/05/09 15:28:51 kls Exp $
*/
#include <getopt.h>
#include <vdr/interface.h>
#include <vdr/plugin.h>
#include "i18n.h"
static const char *VERSION = "0.0.1";
static const char *DESCRIPTION = "A friendly greeting";
static const char *MAINMENUENTRY = "Hello";
class cPluginHello : public cPlugin {
private:
// Add any member variables or functions you may need here.
const char *option_a;
bool option_b;
public:
cPluginHello(void);
virtual ~cPluginHello();
virtual const char *Version(void) { return VERSION; }
virtual const char *Description(void) { return tr(DESCRIPTION); }
virtual const char *CommandLineHelp(void);
virtual bool ProcessArgs(int argc, char *argv[]);
virtual void Start(void);
virtual const char *MainMenuEntry(void) { return tr(MAINMENUENTRY); }
virtual cOsdMenu *MainMenuAction(void);
virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value);
};
cPluginHello::cPluginHello(void)
{
// Initialize any member varaiables here.
// DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
// VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
option_a = NULL;
option_b = false;
}
cPluginHello::~cPluginHello()
{
// Clean up after yourself!
}
const char *cPluginHello::CommandLineHelp(void)
{
// Return a string that describes all known command line options.
return " -a ABC, --aaa=ABC do something nice with ABC\n"
" -b, --bbb activate 'plan B'\n";
}
bool cPluginHello::ProcessArgs(int argc, char *argv[])
{
// Implement command line argument processing here if applicable.
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': option_a = optarg;
break;
case 'b': option_b = true;
break;
default: return false;
}
}
return true;
}
void cPluginHello::Start(void)
{
// Start any background activities the plugin shall perform.
RegisterI18n(Phrases);
}
cOsdMenu *cPluginHello::MainMenuAction(void)
{
// Perform the action when selected from the main VDR menu.
Interface->Info(tr("Hello world!"));
return NULL;
}
cMenuSetupPage *cPluginHello::SetupMenu(void)
{
// Return a setup menu in case the plugin supports one.
return NULL;
}
bool cPluginHello::SetupParse(const char *Name, const char *Value)
{
// Parse your own setup parameters and store their values.
return false;
}
VDRPLUGINCREATOR(cPluginHello); // Don't touch this!

52
PLUGINS/src/hello/i18n.c Normal file
View File

@ -0,0 +1,52 @@
/*
* i18n.c: Internationalization
*
* See the README file for copyright information and how to reach the author.
*
* $Id: i18n.c 1.1 2002/05/09 15:13:31 kls Exp $
*/
#include "i18n.h"
const tI18nPhrase Phrases[] = {
{ "Hello",
"Hallo",
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
},
{ "Hello world!",
"Hallo Welt!",
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
},
{ "A friendly greeting",
"Ein freundlicher Gruß",
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
},
{ NULL }
};

11
PLUGINS/src/hello/i18n.h Normal file
View File

@ -0,0 +1,11 @@
/*
* i18n.h: Internationalization
*
* See the README file for copyright information and how to reach the author.
*
* $Id: i18n.h 1.1 2002/05/09 15:15:49 kls Exp $
*/
#include <vdr/i18n.h>
extern const tI18nPhrase Phrases[];

371
config.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: config.c 1.98 2002/04/26 12:30:00 kls Exp $ * $Id: config.c 1.99 2002/05/05 12:00:00 kls Exp $
*/ */
#include "config.h" #include "config.h"
@ -13,6 +13,7 @@
#include "dvbapi.h" #include "dvbapi.h"
#include "i18n.h" #include "i18n.h"
#include "interface.h" #include "interface.h"
#include "plugin.h"
// IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d' // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
// format characters in order to allow any number of blanks after a numeric // format characters in order to allow any number of blanks after a numeric
@ -387,11 +388,12 @@ cTimer& cTimer::operator= (const cTimer &Timer)
return *this; return *this;
} }
bool cTimer::operator< (const cTimer &Timer) bool cTimer::operator< (const cListObject &ListObject)
{ {
cTimer *ti = (cTimer *)&ListObject;
time_t t1 = StartTime(); time_t t1 = StartTime();
time_t t2 = (*(cTimer *)&Timer).StartTime(); time_t t2 = ti->StartTime();
return t1 < t2 || (t1 == t2 && priority > Timer.priority); return t1 < t2 || (t1 == t2 && priority > ti->priority);
} }
const char *cTimer::ToText(cTimer *Timer) const char *cTimer::ToText(cTimer *Timer)
@ -917,12 +919,76 @@ const cCaDefinition *cCaDefinitions::Get(int Number)
return NULL; return NULL;
} }
// -- cSetupLine -------------------------------------------------------------
cSetupLine::cSetupLine(void)
{
plugin = name = value = NULL;
}
cSetupLine::cSetupLine(const char *Name, const char *Value, const char *Plugin)
{
name = strdup(Name);
value = strdup(Value);
plugin = Plugin ? strdup(Plugin) : NULL;
}
cSetupLine::~cSetupLine()
{
delete plugin;
delete name;
delete value;
}
bool cSetupLine::operator< (const cListObject &ListObject)
{
const cSetupLine *sl = (cSetupLine *)&ListObject;
if (!plugin && !sl->plugin)
return strcasecmp(name, sl->name) < 0;
if (!plugin)
return true;
if (!sl->plugin)
return false;
int result = strcasecmp(plugin, sl->plugin);
if (result == 0)
result = strcasecmp(name, sl->name);
return result < 0;
}
bool cSetupLine::Parse(char *s)
{
char *p = strchr(s, '=');
if (p) {
*p = 0;
char *Name = compactspace(s);
char *Value = compactspace(p + 1);
if (*Name && *Value) {
p = strchr(Name, '.');
if (p) {
*p = 0;
char *Plugin = compactspace(Name);
Name = compactspace(p + 1);
if (!(*Plugin && *Name))
return false;
plugin = strdup(Plugin);
}
name = strdup(Name);
value = strdup(Value);
return true;
}
}
return false;
}
bool cSetupLine::Save(FILE *f)
{
return fprintf(f, "%s%s%s = %s\n", plugin ? plugin : "", plugin ? "." : "", name, value) > 0;
}
// -- cSetup ----------------------------------------------------------------- // -- cSetup -----------------------------------------------------------------
cSetup Setup; cSetup Setup;
char *cSetup::fileName = NULL;
cSetup::cSetup(void) cSetup::cSetup(void)
{ {
OSDLanguage = 0; OSDLanguage = 0;
@ -966,19 +1032,77 @@ cSetup::cSetup(void)
CurrentVolume = MAXVOLUME; CurrentVolume = MAXVOLUME;
} }
void cSetup::PrintCaCaps(FILE *f, const char *Name) cSetup& cSetup::operator= (const cSetup &s)
{
memcpy(&__BeginData__, &s.__BeginData__, (char *)&s.__EndData__ - (char *)&s.__BeginData__);
return *this;
}
cSetupLine *cSetup::Get(const char *Name, const char *Plugin)
{
for (cSetupLine *l = First(); l; l = Next(l)) {
if ((l->Plugin() == NULL) == (Plugin == NULL)) {
if ((!Plugin || strcasecmp(l->Plugin(), Plugin) == 0) && strcasecmp(l->Name(), Name) == 0)
return l;
}
}
return NULL;
}
void cSetup::Store(const char *Name, const char *Value, const char *Plugin)
{
if (Name && *Name) {
cSetupLine *l = Get(Name, Plugin);
if (l)
Del(l);
if (Value)
Add(new cSetupLine(Name, Value, Plugin));
}
}
void cSetup::Store(const char *Name, int Value, const char *Plugin)
{
char *buffer = NULL;
asprintf(&buffer, "%d", Value);
Store(Name, buffer, Plugin);
delete buffer;
}
bool cSetup::Load(const char *FileName)
{
if (cConfig<cSetupLine>::Load(FileName, true)) {
bool result = true;
for (cSetupLine *l = First(); l; l = Next(l)) {
if (l->Plugin()) {
cPlugin *p = cPluginManager::GetPlugin(l->Plugin());
if (p && !p->SetupParse(l->Name(), l->Value()))
result = false;
}
else {
if (!Parse(l->Name(), l->Value()))
result = false;
}
}
return result;
}
return false;
}
void cSetup::StoreCaCaps(const char *Name)
{ {
for (int d = 0; d < MAXDVBAPI; d++) { for (int d = 0; d < MAXDVBAPI; d++) {
int written = 0; char buffer[MAXPARSEBUFFER];
char *q = buffer;
*buffer = 0;
for (int i = 0; i < MAXCACAPS; i++) { for (int i = 0; i < MAXCACAPS; i++) {
if (CaCaps[d][i]) { if (CaCaps[d][i]) {
if (!written++) if (!*buffer)
fprintf(f, "CaCaps = %d", d + 1); q += snprintf(buffer, sizeof(buffer), "%d", d + 1);
fprintf(f, " %d", CaCaps[d][i]); q += snprintf(q, sizeof(buffer) - (q - buffer), " %d", CaCaps[d][i]);
} }
} }
if (written) if (*buffer)
fprintf(f, "\n"); Store(Name, buffer);
} }
} }
@ -1005,144 +1129,99 @@ bool cSetup::ParseCaCaps(const char *Value)
return false; return false;
} }
bool cSetup::Parse(char *s) bool cSetup::Parse(const char *Name, const char *Value)
{ {
char *p = strchr(s, '='); if (!strcasecmp(Name, "OSDLanguage")) OSDLanguage = atoi(Value);
if (p) { else if (!strcasecmp(Name, "PrimaryDVB")) PrimaryDVB = atoi(Value);
*p = 0; else if (!strcasecmp(Name, "ShowInfoOnChSwitch")) ShowInfoOnChSwitch = atoi(Value);
char *Name = compactspace(s); else if (!strcasecmp(Name, "MenuScrollPage")) MenuScrollPage = atoi(Value);
char *Value = compactspace(p + 1); else if (!strcasecmp(Name, "MarkInstantRecord")) MarkInstantRecord = atoi(Value);
if (*Name && *Value) { else if (!strcasecmp(Name, "NameInstantRecord")) strn0cpy(NameInstantRecord, Value, MaxFileName);
if (!strcasecmp(Name, "OSDLanguage")) OSDLanguage = atoi(Value); else if (!strcasecmp(Name, "InstantRecordTime")) InstantRecordTime = atoi(Value);
else if (!strcasecmp(Name, "PrimaryDVB")) PrimaryDVB = atoi(Value); else if (!strcasecmp(Name, "LnbSLOF")) LnbSLOF = atoi(Value);
else if (!strcasecmp(Name, "ShowInfoOnChSwitch")) ShowInfoOnChSwitch = atoi(Value); else if (!strcasecmp(Name, "LnbFrequLo")) LnbFrequLo = atoi(Value);
else if (!strcasecmp(Name, "MenuScrollPage")) MenuScrollPage = atoi(Value); else if (!strcasecmp(Name, "LnbFrequHi")) LnbFrequHi = atoi(Value);
else if (!strcasecmp(Name, "MarkInstantRecord")) MarkInstantRecord = atoi(Value); else if (!strcasecmp(Name, "DiSEqC")) DiSEqC = atoi(Value);
else if (!strcasecmp(Name, "NameInstantRecord")) strn0cpy(NameInstantRecord, Value, MaxFileName); else if (!strcasecmp(Name, "SetSystemTime")) SetSystemTime = atoi(Value);
else if (!strcasecmp(Name, "InstantRecordTime")) InstantRecordTime = atoi(Value); else if (!strcasecmp(Name, "TimeTransponder")) TimeTransponder = atoi(Value);
else if (!strcasecmp(Name, "LnbSLOF")) LnbSLOF = atoi(Value); else if (!strcasecmp(Name, "MarginStart")) MarginStart = atoi(Value);
else if (!strcasecmp(Name, "LnbFrequLo")) LnbFrequLo = atoi(Value); else if (!strcasecmp(Name, "MarginStop")) MarginStop = atoi(Value);
else if (!strcasecmp(Name, "LnbFrequHi")) LnbFrequHi = atoi(Value); else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = atoi(Value);
else if (!strcasecmp(Name, "DiSEqC")) DiSEqC = atoi(Value); else if (!strcasecmp(Name, "EPGBugfixLevel")) EPGBugfixLevel = atoi(Value);
else if (!strcasecmp(Name, "SetSystemTime")) SetSystemTime = atoi(Value); else if (!strcasecmp(Name, "SVDRPTimeout")) SVDRPTimeout = atoi(Value);
else if (!strcasecmp(Name, "TimeTransponder")) TimeTransponder = atoi(Value); else if (!strcasecmp(Name, "SortTimers")) SortTimers = atoi(Value);
else if (!strcasecmp(Name, "MarginStart")) MarginStart = atoi(Value); else if (!strcasecmp(Name, "PrimaryLimit")) PrimaryLimit = atoi(Value);
else if (!strcasecmp(Name, "MarginStop")) MarginStop = atoi(Value); else if (!strcasecmp(Name, "DefaultPriority")) DefaultPriority = atoi(Value);
else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = atoi(Value); else if (!strcasecmp(Name, "DefaultLifetime")) DefaultLifetime = atoi(Value);
else if (!strcasecmp(Name, "EPGBugfixLevel")) EPGBugfixLevel = atoi(Value); else if (!strcasecmp(Name, "UseSubtitle")) UseSubtitle = atoi(Value);
else if (!strcasecmp(Name, "SVDRPTimeout")) SVDRPTimeout = atoi(Value); else if (!strcasecmp(Name, "RecordingDirs")) RecordingDirs = atoi(Value);
else if (!strcasecmp(Name, "SortTimers")) SortTimers = atoi(Value); else if (!strcasecmp(Name, "VideoFormat")) VideoFormat = atoi(Value);
else if (!strcasecmp(Name, "PrimaryLimit")) PrimaryLimit = atoi(Value); else if (!strcasecmp(Name, "RecordDolbyDigital")) RecordDolbyDigital = atoi(Value);
else if (!strcasecmp(Name, "DefaultPriority")) DefaultPriority = atoi(Value); else if (!strcasecmp(Name, "ChannelInfoPos")) ChannelInfoPos = atoi(Value);
else if (!strcasecmp(Name, "DefaultLifetime")) DefaultLifetime = atoi(Value); else if (!strcasecmp(Name, "OSDwidth")) OSDwidth = atoi(Value);
else if (!strcasecmp(Name, "UseSubtitle")) UseSubtitle = atoi(Value); else if (!strcasecmp(Name, "OSDheight")) OSDheight = atoi(Value);
else if (!strcasecmp(Name, "RecordingDirs")) RecordingDirs = atoi(Value); else if (!strcasecmp(Name, "OSDMessageTime")) OSDMessageTime = atoi(Value);
else if (!strcasecmp(Name, "VideoFormat")) VideoFormat = atoi(Value); else if (!strcasecmp(Name, "MaxVideoFileSize")) MaxVideoFileSize = atoi(Value);
else if (!strcasecmp(Name, "RecordDolbyDigital")) RecordDolbyDigital = atoi(Value); else if (!strcasecmp(Name, "SplitEditedFiles")) SplitEditedFiles = atoi(Value);
else if (!strcasecmp(Name, "ChannelInfoPos")) ChannelInfoPos = atoi(Value); else if (!strcasecmp(Name, "MinEventTimeout")) MinEventTimeout = atoi(Value);
else if (!strcasecmp(Name, "OSDwidth")) OSDwidth = atoi(Value); else if (!strcasecmp(Name, "MinUserInactivity")) MinUserInactivity = atoi(Value);
else if (!strcasecmp(Name, "OSDheight")) OSDheight = atoi(Value); else if (!strcasecmp(Name, "MultiSpeedMode")) MultiSpeedMode = atoi(Value);
else if (!strcasecmp(Name, "OSDMessageTime")) OSDMessageTime = atoi(Value); else if (!strcasecmp(Name, "ShowReplayMode")) ShowReplayMode = atoi(Value);
else if (!strcasecmp(Name, "MaxVideoFileSize")) MaxVideoFileSize = atoi(Value); else if (!strcasecmp(Name, "CaCaps")) return ParseCaCaps(Value);
else if (!strcasecmp(Name, "SplitEditedFiles")) SplitEditedFiles = atoi(Value); else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value);
else if (!strcasecmp(Name, "MinEventTimeout")) MinEventTimeout = atoi(Value); else if (!strcasecmp(Name, "CurrentVolume")) CurrentVolume = atoi(Value);
else if (!strcasecmp(Name, "MinUserInactivity")) MinUserInactivity = atoi(Value);
else if (!strcasecmp(Name, "MultiSpeedMode")) MultiSpeedMode = atoi(Value);
else if (!strcasecmp(Name, "ShowReplayMode")) ShowReplayMode = atoi(Value);
else if (!strcasecmp(Name, "CaCaps")) return ParseCaCaps(Value);
else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value);
else if (!strcasecmp(Name, "CurrentVolume")) CurrentVolume = atoi(Value);
else
return false;
return true;
}
}
return false;
}
bool cSetup::Load(const char *FileName)
{
isyslog(LOG_INFO, "loading %s", FileName);
delete fileName;
fileName = strdup(FileName);
FILE *f = fopen(fileName, "r");
if (f) {
int line = 0;
char buffer[MAXPARSEBUFFER];
bool result = true;
while (fgets(buffer, sizeof(buffer), f) > 0) {
line++;
stripspace(buffer);
if (!isempty(buffer)) {
if (*buffer != '#' && !Parse(buffer)) {
esyslog(LOG_ERR, "error in %s, line %d\n", fileName, line);
result = false;
}
}
}
fclose(f);
return result;
}
else else
LOG_ERROR_STR(FileName); return false;
return false; return true;
} }
bool cSetup::Save(const char *FileName) bool cSetup::Save(void)
{ {
if (!FileName) Store("OSDLanguage", OSDLanguage);
FileName = fileName; Store("PrimaryDVB", PrimaryDVB);
if (FileName) { Store("ShowInfoOnChSwitch", ShowInfoOnChSwitch);
cSafeFile f(FileName); Store("MenuScrollPage", MenuScrollPage);
if (f.Open()) { Store("MarkInstantRecord", MarkInstantRecord);
fprintf(f, "# VDR Setup\n"); Store("NameInstantRecord", NameInstantRecord);
fprintf(f, "OSDLanguage = %d\n", OSDLanguage); Store("InstantRecordTime", InstantRecordTime);
fprintf(f, "PrimaryDVB = %d\n", PrimaryDVB); Store("LnbSLOF", LnbSLOF);
fprintf(f, "ShowInfoOnChSwitch = %d\n", ShowInfoOnChSwitch); Store("LnbFrequLo", LnbFrequLo);
fprintf(f, "MenuScrollPage = %d\n", MenuScrollPage); Store("LnbFrequHi", LnbFrequHi);
fprintf(f, "MarkInstantRecord = %d\n", MarkInstantRecord); Store("DiSEqC", DiSEqC);
fprintf(f, "NameInstantRecord = %s\n", NameInstantRecord); Store("SetSystemTime", SetSystemTime);
fprintf(f, "InstantRecordTime = %d\n", InstantRecordTime); Store("TimeTransponder", TimeTransponder);
fprintf(f, "LnbSLOF = %d\n", LnbSLOF); Store("MarginStart", MarginStart);
fprintf(f, "LnbFrequLo = %d\n", LnbFrequLo); Store("MarginStop", MarginStop);
fprintf(f, "LnbFrequHi = %d\n", LnbFrequHi); Store("EPGScanTimeout", EPGScanTimeout);
fprintf(f, "DiSEqC = %d\n", DiSEqC); Store("EPGBugfixLevel", EPGBugfixLevel);
fprintf(f, "SetSystemTime = %d\n", SetSystemTime); Store("SVDRPTimeout", SVDRPTimeout);
fprintf(f, "TimeTransponder = %d\n", TimeTransponder); Store("SortTimers", SortTimers);
fprintf(f, "MarginStart = %d\n", MarginStart); Store("PrimaryLimit", PrimaryLimit);
fprintf(f, "MarginStop = %d\n", MarginStop); Store("DefaultPriority", DefaultPriority);
fprintf(f, "EPGScanTimeout = %d\n", EPGScanTimeout); Store("DefaultLifetime", DefaultLifetime);
fprintf(f, "EPGBugfixLevel = %d\n", EPGBugfixLevel); Store("UseSubtitle", UseSubtitle);
fprintf(f, "SVDRPTimeout = %d\n", SVDRPTimeout); Store("RecordingDirs", RecordingDirs);
fprintf(f, "SortTimers = %d\n", SortTimers); Store("VideoFormat", VideoFormat);
fprintf(f, "PrimaryLimit = %d\n", PrimaryLimit); Store("RecordDolbyDigital", RecordDolbyDigital);
fprintf(f, "DefaultPriority = %d\n", DefaultPriority); Store("ChannelInfoPos", ChannelInfoPos);
fprintf(f, "DefaultLifetime = %d\n", DefaultLifetime); Store("OSDwidth", OSDwidth);
fprintf(f, "UseSubtitle = %d\n", UseSubtitle); Store("OSDheight", OSDheight);
fprintf(f, "RecordingDirs = %d\n", RecordingDirs); Store("OSDMessageTime", OSDMessageTime);
fprintf(f, "VideoFormat = %d\n", VideoFormat); Store("MaxVideoFileSize", MaxVideoFileSize);
fprintf(f, "RecordDolbyDigital = %d\n", RecordDolbyDigital); Store("SplitEditedFiles", SplitEditedFiles);
fprintf(f, "ChannelInfoPos = %d\n", ChannelInfoPos); Store("MinEventTimeout", MinEventTimeout);
fprintf(f, "OSDwidth = %d\n", OSDwidth); Store("MinUserInactivity", MinUserInactivity);
fprintf(f, "OSDheight = %d\n", OSDheight); Store("MultiSpeedMode", MultiSpeedMode);
fprintf(f, "OSDMessageTime = %d\n", OSDMessageTime); Store("ShowReplayMode", ShowReplayMode);
fprintf(f, "MaxVideoFileSize = %d\n", MaxVideoFileSize); StoreCaCaps("CaCaps");
fprintf(f, "SplitEditedFiles = %d\n", SplitEditedFiles); Store("CurrentChannel", CurrentChannel);
fprintf(f, "MinEventTimeout = %d\n", MinEventTimeout); Store("CurrentVolume", CurrentVolume);
fprintf(f, "MinUserInactivity = %d\n", MinUserInactivity);
fprintf(f, "MultiSpeedMode = %d\n", MultiSpeedMode); Sort();
fprintf(f, "ShowReplayMode = %d\n", ShowReplayMode);
PrintCaCaps(f, "CaCaps"); if (cConfig<cSetupLine>::Save()) {
fprintf(f, "CurrentChannel = %d\n", CurrentChannel); isyslog(LOG_INFO, "saved setup to %s", FileName());
fprintf(f, "CurrentVolume = %d\n", CurrentVolume); return true;
if (f.Close()) {
isyslog(LOG_INFO, "saved setup to %s", FileName);
return true;
}
}
} }
else
esyslog(LOG_ERR, "attempt to save setup without file name");
return false; return false;
} }

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: config.h 1.112 2002/04/26 13:56:30 kls Exp $ * $Id: config.h 1.113 2002/05/05 12:00:00 kls Exp $
*/ */
#ifndef __CONFIG_H #ifndef __CONFIG_H
@ -19,7 +19,7 @@
#include "eit.h" #include "eit.h"
#include "tools.h" #include "tools.h"
#define VDRVERSION "1.0.2" #define VDRVERSION "1.1.0"
#define MAXPRIORITY 99 #define MAXPRIORITY 99
#define MAXLIFETIME 99 #define MAXLIFETIME 99
@ -149,7 +149,7 @@ public:
cTimer(const cEventInfo *EventInfo); cTimer(const cEventInfo *EventInfo);
virtual ~cTimer(); virtual ~cTimer();
cTimer& operator= (const cTimer &Timer); cTimer& operator= (const cTimer &Timer);
bool operator< (const cTimer &Timer); virtual bool operator< (const cListObject &ListObject);
const char *ToText(void); const char *ToText(void);
bool Parse(const char *s); bool Parse(const char *s);
bool Save(FILE *f); bool Save(FILE *f);
@ -220,7 +220,8 @@ private:
public: public:
cConfig(void) { fileName = NULL; } cConfig(void) { fileName = NULL; }
virtual ~cConfig() { delete fileName; } virtual ~cConfig() { delete fileName; }
virtual bool Load(const char *FileName, bool AllowComments = false) const char *FileName(void) { return fileName; }
bool Load(const char *FileName, bool AllowComments = false)
{ {
Clear(); Clear();
fileName = strdup(FileName); fileName = strdup(FileName);
@ -323,14 +324,35 @@ extern cCommands Commands;
extern cSVDRPhosts SVDRPhosts; extern cSVDRPhosts SVDRPhosts;
extern cCaDefinitions CaDefinitions; extern cCaDefinitions CaDefinitions;
class cSetup { class cSetupLine : public cListObject {
private: private:
static char *fileName; char *plugin;
void PrintCaCaps(FILE *f, const char *Name); char *name;
bool ParseCaCaps(const char *Value); char *value;
public:
cSetupLine(void);
cSetupLine(const char *Name, const char *Value, const char *Plugin = NULL);
virtual ~cSetupLine();
virtual bool operator< (const cListObject &ListObject);
const char *Plugin(void) { return plugin; }
const char *Name(void) { return name; }
const char *Value(void) { return value; }
bool Parse(char *s); bool Parse(char *s);
bool Save(FILE *f);
};
class cSetup : public cConfig<cSetupLine> {
friend class cPlugin; // needs to be able to call Store()
private:
void StoreCaCaps(const char *Name);
bool ParseCaCaps(const char *Value);
bool Parse(const char *Name, const char *Value);
cSetupLine *Get(const char *Name, const char *Plugin = NULL);
void Store(const char *Name, const char *Value, const char *Plugin = NULL);
void Store(const char *Name, int Value, const char *Plugin = NULL);
public: public:
// Also adjust cMenuSetup (menu.c) when adding parameters here! // Also adjust cMenuSetup (menu.c) when adding parameters here!
int __BeginData__;
int OSDLanguage; int OSDLanguage;
int PrimaryDVB; int PrimaryDVB;
int ShowInfoOnChSwitch; int ShowInfoOnChSwitch;
@ -366,9 +388,11 @@ public:
int CaCaps[MAXDVBAPI][MAXCACAPS]; int CaCaps[MAXDVBAPI][MAXCACAPS];
int CurrentChannel; int CurrentChannel;
int CurrentVolume; int CurrentVolume;
int __EndData__;
cSetup(void); cSetup(void);
cSetup& operator= (const cSetup &s);
bool Load(const char *FileName); bool Load(const char *FileName);
bool Save(const char *FileName = NULL); bool Save(void);
}; };
extern cSetup Setup; extern cSetup Setup;

143
i18n.c
View File

@ -4,27 +4,29 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: i18n.c 1.86 2002/05/04 14:44:32 kls Exp $ * $Id: i18n.c 1.87 2002/05/09 13:40:51 kls Exp $
* *
* Slovenian translations provided by Miha Setina <mihasetina@softhome.net> and Matjaz Thaler <matjaz.thaler@guest.arnes.si> * Translations provided by:
* Italian translations provided by Alberto Carraro <bertocar@tin.it> *
* Dutch translations provided by Arnold Niessen <niessen@iae.nl> <arnold.niessen@philips.com> * Slovenian Miha Setina <mihasetina@softhome.net> and Matjaz Thaler <matjaz.thaler@guest.arnes.si>
* Portuguese translations provided by Paulo Lopes <pmml@netvita.pt> * Italian Alberto Carraro <bertocar@tin.it>
* French translations provided by Jean-Claude Repetto <jc@repetto.org> * Dutch Arnold Niessen <niessen@iae.nl> <arnold.niessen@philips.com>
* Norwegian translations provided by Jørgen Tvedt <pjtvedt@online.no> and Truls Slevigen <truls@slevigen.no> * Portuguese Paulo Lopes <pmml@netvita.pt>
* Finnish translations provided by Hannu Savolainen <hannu@opensound.com> * French Jean-Claude Repetto <jc@repetto.org>
* Polish translations provided by Michael Rakowski <mrak@gmx.de> * Norwegian Jørgen Tvedt <pjtvedt@online.no> and Truls Slevigen <truls@slevigen.no>
* Spanish translations provided by Ruben Nunez Francisco <ruben.nunez@tang-it.com> * Finnish Hannu Savolainen <hannu@opensound.com>
* Greek translations provided by Dimitrios Dimitrakos <mail@dimitrios.de> * Polish Michael Rakowski <mrak@gmx.de>
* Spanish Ruben Nunez Francisco <ruben.nunez@tang-it.com>
* Greek Dimitrios Dimitrakos <mail@dimitrios.de>
* *
*/ */
/* /*
* How to add a new language: * How to add a new language:
* *
* 1. Announce your translation action on the Linux-DVB mailing * 1. Announce your translation action on the VDR mailing
* list to avoid duplicate work. * list to avoid duplicate work.
* 2. Increase the value of 'NumLanguages'. * 2. Increase the value of 'I18nNumLanguages' in 'i18n.h'.
* 3. Insert a new line in every member of the 'Phrases[]' array, * 3. Insert a new line in every member of the 'Phrases[]' array,
* containing the translated text for the new language. * containing the translated text for the new language.
* For example, assuming you want to add the Italian language, * For example, assuming you want to add the Italian language,
@ -46,7 +48,7 @@
* Note that only the characters defined in 'fontosd.c' will * Note that only the characters defined in 'fontosd.c' will
* be available! * be available!
* 4. Compile VDR and test the new language by switching to it * 4. Compile VDR and test the new language by switching to it
* in the "Setup" menu. * in the "Setup/OSD" menu.
* 5. Send the modified 'i18n.c' file to <kls@cadsoft.de> to have * 5. Send the modified 'i18n.c' file to <kls@cadsoft.de> to have
* it included in the next version of VDR. * it included in the next version of VDR.
* *
@ -60,15 +62,10 @@
*/ */
#include "i18n.h" #include "i18n.h"
#include <stdio.h>
#include "config.h" #include "config.h"
#include "tools.h" #include "tools.h"
const int NumLanguages = 12; const tI18nPhrase Phrases[] = {
typedef const char *tPhrase[NumLanguages];
const tPhrase Phrases[] = {
// The name of the language (this MUST be the first phrase!): // The name of the language (this MUST be the first phrase!):
{ "English", { "English",
"Deutsch", "Deutsch",
@ -1260,6 +1257,19 @@ const tPhrase Phrases[] = {
"Varios", "Varios",
"Diafora", "Diafora",
}, },
{ "Plugins",
"Plugins",
"Plugins",
"Plugins",
"Plugins",
"Plugins",
"Plugins",
"Plugins",
"Plugins",
"Plugins",
"Plugins",
"Plugins",
},
{ "Restart", { "Restart",
"Neustart", "Neustart",
"Ponoven zagon", "Ponoven zagon",
@ -2384,26 +2394,101 @@ const tPhrase Phrases[] = {
"buscando grabaciones...", "buscando grabaciones...",
"Ginete sarosi egrafon...", "Ginete sarosi egrafon...",
}, },
{ "This plugin has no setup parameters!",
"Dieses Plugin hat keine Setup-Parameter!",
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
},
{ NULL } { NULL }
}; };
const char *tr(const char *s) // --- cI18nEntry ------------------------------------------------------------
class cI18nEntry : public cListObject {
private:
const tI18nPhrase *phrases;
const char *plugin;
public:
cI18nEntry(const tI18nPhrase * const Phrases, const char *Plugin);
const tI18nPhrase *Phrases(void) { return phrases; }
const char *Plugin(void) { return plugin; }
};
cI18nEntry::cI18nEntry(const tI18nPhrase * const Phrases, const char *Plugin)
{
phrases = Phrases;
plugin = Plugin;
}
// --- cI18nList -------------------------------------------------------------
class cI18nList : public cList<cI18nEntry> {
public:
cI18nEntry *Get(const char *Plugin);
const tI18nPhrase *GetPhrases(const char *Plugin);
};
cI18nEntry *cI18nList::Get(const char *Plugin)
{
if (Plugin) {
for (cI18nEntry *p = First(); p; p = Next(p)) {
if (strcmp(p->Plugin(), Plugin) == 0)
return p;
}
}
return NULL;
}
const tI18nPhrase *cI18nList::GetPhrases(const char *Plugin)
{
cI18nEntry *p = Get(Plugin);
return p ? p->Phrases() : NULL;
}
cI18nList I18nList;
// ---
void I18nRegister(const tI18nPhrase * const Phrases, const char *Plugin)
{
cI18nEntry *p = I18nList.Get(Plugin);
if (p)
I18nList.Del(p);
if (Phrases)
I18nList.Add(new cI18nEntry(Phrases, Plugin));
}
const char *I18nTranslate(const char *s, const char *Plugin)
{ {
if (Setup.OSDLanguage) { if (Setup.OSDLanguage) {
for (const tPhrase *p = Phrases; **p; p++) { const tI18nPhrase *p = Plugin ? I18nList.GetPhrases(Plugin) : Phrases;
if (strcmp(s, **p) == 0) { if (!p)
const char *t = (*p)[Setup.OSDLanguage]; p = Phrases;
if (t && *t) for (int i = ((p == Phrases) ? 1 : 2); i--; ) {
return t; for (; **p; p++) {
} if (strcmp(s, **p) == 0) {
const char *t = (*p)[Setup.OSDLanguage];
if (t && *t)
return t;
}
}
p = Phrases;
} }
esyslog(LOG_ERR, "no translation found for '%s' in language %d (%s)\n", s, Setup.OSDLanguage, Phrases[0][Setup.OSDLanguage]); esyslog(LOG_ERR, "%s%sno translation found for '%s' in language %d (%s)\n", Plugin ? Plugin : "", Plugin ? ": " : "", s, Setup.OSDLanguage, Phrases[0][Setup.OSDLanguage]);
} }
const char *p = strchr(s, '$'); const char *p = strchr(s, '$');
return p ? p + 1 : s; return p ? p + 1 : s;
} }
const char * const * Languages(void) const char * const * I18nLanguages(void)
{ {
return &Phrases[0][0]; return &Phrases[0][0];
} }

20
i18n.h
View File

@ -4,16 +4,28 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: i18n.h 1.1 2000/11/11 09:27:25 kls Exp $ * $Id: i18n.h 1.2 2002/05/05 13:42:31 kls Exp $
*/ */
#ifndef __I18N_H #ifndef __I18N_H
#define __I18N_H #define __I18N_H
extern const int NumLanguages; #include <stdio.h>
const char *tr(const char *s); const int I18nNumLanguages = 12;
const char * const * Languages(void); typedef const char *tI18nPhrase[I18nNumLanguages];
void I18nRegister(const tI18nPhrase * const Phrases, const char *Plugin);
const char *I18nTranslate(const char *s, const char *Plugin = NULL);
const char * const * I18nLanguages(void);
#ifdef PLUGIN_NAME_I18N
#define tr(s) I18nTranslate(s, PLUGIN_NAME_I18N)
#else
#define tr(s) I18nTranslate(s)
#endif
#endif //__I18N_H #endif //__I18N_H

690
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: menu.c 1.189 2002/05/01 14:54:10 kls Exp $ * $Id: menu.c 1.190 2002/05/09 10:13:47 kls Exp $
*/ */
#include "menu.h" #include "menu.h"
@ -16,6 +16,8 @@
#include "config.h" #include "config.h"
#include "eit.h" #include "eit.h"
#include "i18n.h" #include "i18n.h"
#include "menuitems.h"
#include "plugin.h"
#include "videodir.h" #include "videodir.h"
#define MENUTIMEOUT 120 // seconds #define MENUTIMEOUT 120 // seconds
@ -28,125 +30,6 @@
const char *FileNameChars = " abcdefghijklmnopqrstuvwxyz0123456789-.#~"; const char *FileNameChars = " abcdefghijklmnopqrstuvwxyz0123456789-.#~";
// --- cMenuEditItem ---------------------------------------------------------
class cMenuEditItem : public cOsdItem {
private:
const char *name;
const char *value;
public:
cMenuEditItem(const char *Name);
~cMenuEditItem();
void SetValue(const char *Value);
};
cMenuEditItem::cMenuEditItem(const char *Name)
{
name = strdup(Name);
value = NULL;
}
cMenuEditItem::~cMenuEditItem()
{
delete name;
delete value;
}
void cMenuEditItem::SetValue(const char *Value)
{
delete value;
value = strdup(Value);
char *buffer = NULL;
asprintf(&buffer, "%s:\t%s", name, value);
SetText(buffer, false);
Display();
}
// --- cMenuEditIntItem ------------------------------------------------------
class cMenuEditIntItem : public cMenuEditItem {
protected:
int *value;
int min, max;
virtual void Set(void);
public:
cMenuEditIntItem(const char *Name, int *Value, int Min = 0, int Max = INT_MAX);
virtual eOSState ProcessKey(eKeys Key);
};
cMenuEditIntItem::cMenuEditIntItem(const char *Name, int *Value, int Min, int Max)
:cMenuEditItem(Name)
{
value = Value;
min = Min;
max = Max;
Set();
}
void cMenuEditIntItem::Set(void)
{
char buf[16];
snprintf(buf, sizeof(buf), "%d", *value);
SetValue(buf);
}
eOSState cMenuEditIntItem::ProcessKey(eKeys Key)
{
eOSState state = cMenuEditItem::ProcessKey(Key);
if (state == osUnknown) {
int newValue;
if (k0 <= Key && Key <= k9) {
if (fresh) {
*value = 0;
fresh = false;
}
newValue = *value * 10 + (Key - k0);
}
else if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
newValue = *value - 1;
fresh = true;
}
else if (NORMALKEY(Key) == kRight) {
newValue = *value + 1;
fresh = true;
}
else
return state;
if ((!fresh || min <= newValue) && newValue <= max) {
*value = newValue;
Set();
}
state = osContinue;
}
return state;
}
// --- cMenuEditBoolItem -----------------------------------------------------
class cMenuEditBoolItem : public cMenuEditIntItem {
protected:
const char *falseString, *trueString;
virtual void Set(void);
public:
cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString = NULL, const char *TrueString = NULL);
};
cMenuEditBoolItem::cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString, const char *TrueString)
:cMenuEditIntItem(Name, Value, 0, 1)
{
falseString = FalseString ? FalseString : tr("no");
trueString = TrueString ? TrueString : tr("yes");
Set();
}
void cMenuEditBoolItem::Set(void)
{
char buf[16];
snprintf(buf, sizeof(buf), "%s", *value ? trueString : falseString);
SetValue(buf);
}
// --- cMenuEditChanItem ----------------------------------------------------- // --- cMenuEditChanItem -----------------------------------------------------
class cMenuEditChanItem : public cMenuEditIntItem { class cMenuEditChanItem : public cMenuEditIntItem {
@ -451,282 +334,6 @@ eOSState cMenuEditTimeItem::ProcessKey(eKeys Key)
return state; return state;
} }
// --- cMenuEditChrItem ------------------------------------------------------
class cMenuEditChrItem : public cMenuEditItem {
private:
char *value;
const char *allowed;
const char *current;
virtual void Set(void);
public:
cMenuEditChrItem(const char *Name, char *Value, const char *Allowed);
~cMenuEditChrItem();
virtual eOSState ProcessKey(eKeys Key);
};
cMenuEditChrItem::cMenuEditChrItem(const char *Name, char *Value, const char *Allowed)
:cMenuEditItem(Name)
{
value = Value;
allowed = strdup(Allowed);
current = strchr(allowed, *Value);
if (!current)
current = allowed;
Set();
}
cMenuEditChrItem::~cMenuEditChrItem()
{
delete allowed;
}
void cMenuEditChrItem::Set(void)
{
char buf[2];
snprintf(buf, sizeof(buf), "%c", *value);
SetValue(buf);
}
eOSState cMenuEditChrItem::ProcessKey(eKeys Key)
{
eOSState state = cMenuEditItem::ProcessKey(Key);
if (state == osUnknown) {
if (NORMALKEY(Key) == kLeft) {
if (current > allowed)
current--;
}
else if (NORMALKEY(Key) == kRight) {
if (*(current + 1))
current++;
}
else
return state;
*value = *current;
Set();
state = osContinue;
}
return state;
}
// --- cMenuEditStrItem ------------------------------------------------------
class cMenuEditStrItem : public cMenuEditItem {
private:
char *value;
int length;
const char *allowed;
int pos;
bool insert, newchar, uppercase;
void SetHelpKeys(void);
virtual void Set(void);
char Inc(char c, bool Up);
public:
cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed);
~cMenuEditStrItem();
virtual eOSState ProcessKey(eKeys Key);
};
cMenuEditStrItem::cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed)
:cMenuEditItem(Name)
{
value = Value;
length = Length;
allowed = strdup(Allowed);
pos = -1;
insert = uppercase = false;
newchar = true;
Set();
}
cMenuEditStrItem::~cMenuEditStrItem()
{
delete allowed;
}
void cMenuEditStrItem::SetHelpKeys(void)
{
if (pos >= 0)
Interface->Help(tr("ABC/abc"), tr(insert ? "Overwrite" : "Insert"), tr("Delete"));
else
Interface->Help(NULL);
}
void cMenuEditStrItem::Set(void)
{
char buf[1000];
const char *fmt = insert && newchar ? "[]%c%s" : "[%c]%s";
if (pos >= 0) {
strncpy(buf, value, pos);
snprintf(buf + pos, sizeof(buf) - pos - 2, fmt, *(value + pos), value + pos + 1);
int width = Interface->Width() - Interface->GetCols()[0];
if (cDvbApi::PrimaryDvbApi->WidthInCells(buf) <= width) {
// the whole buffer fits on the screen
SetValue(buf);
return;
}
width *= cDvbApi::PrimaryDvbApi->CellWidth();
width -= cDvbApi::PrimaryDvbApi->Width('>'); // assuming '<' and '>' have the same with
int w = 0;
int i = 0;
int l = strlen(buf);
while (i < l && w <= width)
w += cDvbApi::PrimaryDvbApi->Width(buf[i++]);
if (i >= pos + 4) {
// the cursor fits on the screen
buf[i - 1] = '>';
buf[i] = 0;
SetValue(buf);
return;
}
// the cursor doesn't fit on the screen
w = 0;
if (buf[i = pos + 3]) {
buf[i] = '>';
buf[i + 1] = 0;
}
else
i--;
while (i >= 0 && w <= width)
w += cDvbApi::PrimaryDvbApi->Width(buf[i--]);
buf[++i] = '<';
SetValue(buf + i);
}
else
SetValue(value);
}
char cMenuEditStrItem::Inc(char c, bool Up)
{
const char *p = strchr(allowed, c);
if (!p)
p = allowed;
if (Up) {
if (!*++p)
p = allowed;
}
else if (--p < allowed)
p = allowed + strlen(allowed) - 1;
return *p;
}
eOSState cMenuEditStrItem::ProcessKey(eKeys Key)
{
switch (Key) {
case kRed: // Switch between upper- and lowercase characters
if (pos >= 0 && (!insert || !newchar)) {
uppercase = !uppercase;
value[pos] = uppercase ? toupper(value[pos]) : tolower(value[pos]);
}
break;
case kGreen: // Toggle insert/overwrite modes
if (pos >= 0) {
insert = !insert;
newchar = true;
}
SetHelpKeys();
break;
case kYellow|k_Repeat:
case kYellow: // Remove the character at current position; in insert mode it is the character to the right of cursor
if (pos >= 0) {
if (strlen(value) > 1) {
memmove(value + pos, value + pos + 1, strlen(value) - pos);
// reduce position, if we removed the last character
if (pos == int(strlen(value)))
pos--;
}
else if (strlen(value) == 1)
value[0] = ' '; // This is the last character in the string, replace it with a blank
if (isalpha(value[pos]))
uppercase = isupper(value[pos]);
newchar = true;
}
break;
case kLeft|k_Repeat:
case kLeft: if (pos > 0) {
if (!insert || newchar)
pos--;
newchar = true;
}
if (!insert && isalpha(value[pos]))
uppercase = isupper(value[pos]);
break;
case kRight|k_Repeat:
case kRight: if (pos < length && pos < int(strlen(value)) ) {
if (++pos >= int(strlen(value))) {
if (pos >= 2 && value[pos - 1] == ' ' && value[pos - 2] == ' ')
pos--; // allow only two blanks at the end
else {
value[pos] = ' ';
value[pos + 1] = 0;
}
}
}
newchar = true;
if (!insert && isalpha(value[pos]))
uppercase = isupper(value[pos]);
if (pos == 0)
SetHelpKeys();
break;
case kUp|k_Repeat:
case kUp:
case kDown|k_Repeat:
case kDown: if (pos >= 0) {
if (insert && newchar) {
// create a new character in insert mode
if (int(strlen(value)) < length) {
memmove(value + pos + 1, value + pos, strlen(value) - pos + 1);
value[pos] = ' ';
}
}
if (uppercase)
value[pos] = toupper(Inc(tolower(value[pos]), NORMALKEY(Key) == kUp));
else
value[pos] = Inc( value[pos], NORMALKEY(Key) == kUp);
newchar = false;
}
else
return cMenuEditItem::ProcessKey(Key);
break;
case kOk: if (pos >= 0) {
pos = -1;
newchar = true;
stripspace(value);
SetHelpKeys();
break;
}
// run into default
default: return cMenuEditItem::ProcessKey(Key);
}
Set();
return osContinue;
}
// --- cMenuEditStraItem -----------------------------------------------------
class cMenuEditStraItem : public cMenuEditIntItem {
private:
const char * const *strings;
protected:
virtual void Set(void);
public:
cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings);
};
cMenuEditStraItem::cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings)
:cMenuEditIntItem(Name, Value, 0, NumStrings - 1)
{
strings = Strings;
Set();
}
void cMenuEditStraItem::Set(void)
{
SetValue(strings[*value]);
}
// --- cMenuEditCaItem ------------------------------------------------------- // --- cMenuEditCaItem -------------------------------------------------------
class cMenuEditCaItem : public cMenuEditIntItem { class cMenuEditCaItem : public cMenuEditIntItem {
@ -1018,116 +625,6 @@ eOSState cMenuChannels::ProcessKey(eKeys Key)
return state; return state;
} }
// --- cMenuTextItem ---------------------------------------------------------
class cMenuTextItem : public cOsdItem {
private:
char *text;
int x, y, w, h, lines, offset;
eDvbColor fgColor, bgColor;
eDvbFont font;
public:
cMenuTextItem(const char *Text, int X, int Y, int W, int H = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground, eDvbFont Font = fontOsd);
~cMenuTextItem();
int Height(void) { return h; }
void Clear(void);
virtual void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground);
bool CanScrollUp(void) { return offset > 0; }
bool CanScrollDown(void) { return h + offset < lines; }
void ScrollUp(bool Page);
void ScrollDown(bool Page);
virtual eOSState ProcessKey(eKeys Key);
};
cMenuTextItem::cMenuTextItem(const char *Text, int X, int Y, int W, int H, eDvbColor FgColor, eDvbColor BgColor, eDvbFont Font)
{
x = X;
y = Y;
w = W;
h = H;
fgColor = FgColor;
bgColor = BgColor;
font = Font;
offset = 0;
eDvbFont oldFont = Interface->SetFont(font);
text = Interface->WrapText(Text, w - 1, &lines);
Interface->SetFont(oldFont);
if (h < 0)
h = lines;
}
cMenuTextItem::~cMenuTextItem()
{
delete text;
}
void cMenuTextItem::Clear(void)
{
Interface->Fill(x, y, w, h, bgColor);
}
void cMenuTextItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor)
{
int l = 0;
char *t = text;
eDvbFont oldFont = Interface->SetFont(font);
while (*t) {
char *n = strchr(t, '\n');
if (l >= offset) {
if (n)
*n = 0;
Interface->Write(x, y + l - offset, t, fgColor, bgColor);
if (n)
*n = '\n';
else
break;
}
if (!n)
break;
t = n + 1;
if (++l >= h + offset)
break;
}
Interface->SetFont(oldFont);
// scroll indicators use inverted color scheme!
if (CanScrollUp()) Interface->Write(x + w - 1, y, "^", bgColor, fgColor);
if (CanScrollDown()) Interface->Write(x + w - 1, y + h - 1, "v", bgColor, fgColor);
}
void cMenuTextItem::ScrollUp(bool Page)
{
if (CanScrollUp()) {
Clear();
offset = max(offset - (Page ? h : 1), 0);
Display();
}
}
void cMenuTextItem::ScrollDown(bool Page)
{
if (CanScrollDown()) {
Clear();
offset = min(offset + (Page ? h : 1), lines - h);
Display();
}
}
eOSState cMenuTextItem::ProcessKey(eKeys Key)
{
switch (Key) {
case kLeft|k_Repeat:
case kLeft:
case kUp|k_Repeat:
case kUp: ScrollUp(NORMALKEY(Key) == kLeft); break;
case kRight|k_Repeat:
case kRight:
case kDown|k_Repeat:
case kDown: ScrollDown(NORMALKEY(Key) == kRight); break;
default: return osUnknown;
}
return osContinue;
}
// --- cMenuText ------------------------------------------------------------- // --- cMenuText -------------------------------------------------------------
class cMenuText : public cOsdMenu { class cMenuText : public cOsdMenu {
@ -2028,60 +1525,6 @@ eOSState cMenuRecordings::ProcessKey(eKeys Key)
return state; return state;
} }
// --- cMenuSetupPage --------------------------------------------------------
class cMenuSetupPage : public cOsdMenu {
protected:
cSetup data;
int osdLanguage;
void SetupTitle(const char *s);
virtual void Set(void) = 0;
public:
cMenuSetupPage(void);
virtual eOSState ProcessKey(eKeys Key);
};
cMenuSetupPage::cMenuSetupPage(void)
:cOsdMenu("", 33)
{
data = Setup;
osdLanguage = Setup.OSDLanguage;
}
void cMenuSetupPage::SetupTitle(const char *s)
{
char buf[40]; // can't call tr() for more than one string at a time!
char *q = buf + snprintf(buf, sizeof(buf), "%s - ", tr("Setup"));
snprintf(q, sizeof(buf) - strlen(buf), "%s", tr(s));
SetTitle(buf);
}
eOSState cMenuSetupPage::ProcessKey(eKeys Key)
{
eOSState state = cOsdMenu::ProcessKey(Key);
if (state == osUnknown) {
switch (Key) {
case kOk: state = (Setup.PrimaryDVB != data.PrimaryDVB) ? osSwitchDvb : osBack;
cDvbApi::PrimaryDvbApi->SetVideoFormat(data.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3);
Setup = data;
Setup.Save();
cDvbApi::SetCaCaps();
break;
default: break;
}
}
if (data.OSDLanguage != osdLanguage) {
int OriginalOSDLanguage = Setup.OSDLanguage;
Setup.OSDLanguage = data.OSDLanguage;
Set();
Display();
osdLanguage = data.OSDLanguage;
Setup.OSDLanguage = OriginalOSDLanguage;
}
return state;
}
// --- cMenuSetupOSD --------------------------------------------------------- // --- cMenuSetupOSD ---------------------------------------------------------
class cMenuSetupOSD : public cMenuSetupPage { class cMenuSetupOSD : public cMenuSetupPage {
@ -2095,7 +1538,7 @@ void cMenuSetupOSD::Set(void)
{ {
Clear(); Clear();
SetupTitle("OSD"); SetupTitle("OSD");
Add(new cMenuEditStraItem(tr("Setup.OSD$Language"), &data.OSDLanguage, NumLanguages, Languages())); Add(new cMenuEditStraItem(tr("Setup.OSD$Language"), &data.OSDLanguage, I18nNumLanguages, I18nLanguages()));
Add(new cMenuEditIntItem( tr("Setup.OSD$Width"), &data.OSDwidth, MINOSDWIDTH, MAXOSDWIDTH)); Add(new cMenuEditIntItem( tr("Setup.OSD$Width"), &data.OSDwidth, MINOSDWIDTH, MAXOSDWIDTH));
Add(new cMenuEditIntItem( tr("Setup.OSD$Height"), &data.OSDheight, MINOSDHEIGHT, MAXOSDHEIGHT)); Add(new cMenuEditIntItem( tr("Setup.OSD$Height"), &data.OSDheight, MINOSDHEIGHT, MAXOSDHEIGHT));
Add(new cMenuEditIntItem( tr("Setup.OSD$Message time (s)"), &data.OSDMessageTime, 1, 60)); Add(new cMenuEditIntItem( tr("Setup.OSD$Message time (s)"), &data.OSDMessageTime, 1, 60));
@ -2245,6 +1688,75 @@ void cMenuSetupMisc::Set(void)
Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$SVDRP timeout (s)"), &data.SVDRPTimeout)); Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$SVDRP timeout (s)"), &data.SVDRPTimeout));
} }
// --- cMenuSetupPluginItem --------------------------------------------------
class cMenuSetupPluginItem : public cOsdItem {
private:
int pluginIndex;
public:
cMenuSetupPluginItem(const char *Name, int Index);
int PluginIndex(void) { return pluginIndex; }
};
cMenuSetupPluginItem::cMenuSetupPluginItem(const char *Name, int Index)
:cOsdItem(Name)
{
pluginIndex = Index;
}
// --- cMenuSetupPlugins -----------------------------------------------------
class cMenuSetupPlugins : public cMenuSetupPage {
private:
virtual void Set(void);
public:
cMenuSetupPlugins(void) { Set(); }
virtual eOSState ProcessKey(eKeys Key);
};
void cMenuSetupPlugins::Set(void)
{
Clear();
SetupTitle("Plugins");
SetHasHotkeys();
for (int i = 0; ; i++) {
cPlugin *p = cPluginManager::GetPlugin(i);
if (p) {
char *buffer = NULL;
asprintf(&buffer, "%s (%s) - %s", p->Name(), p->Version(), p->Description());
Add(new cMenuSetupPluginItem(hk(buffer), i));
delete buffer;
}
else
break;
}
}
eOSState cMenuSetupPlugins::ProcessKey(eKeys Key)
{
eOSState state = cOsdMenu::ProcessKey(Key); // not cMenuSetupPage::ProcessKey()!
if (state == osUnknown) {
switch (Key) {
case kOk: {
cMenuSetupPluginItem *item = (cMenuSetupPluginItem *)Get(Current());
if (item) {
cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
if (p) {
cOsdMenu *menu = p->SetupMenu();
if (menu)
return AddSubMenu(menu);
Interface->Info(tr("This plugin has no setup parameters!"));
}
}
}
break;
default: break;
}
}
return state;
}
// --- cMenuSetup ------------------------------------------------------------ // --- cMenuSetup ------------------------------------------------------------
class cMenuSetup : public cOsdMenu { class cMenuSetup : public cOsdMenu {
@ -2265,7 +1777,9 @@ cMenuSetup::cMenuSetup(void)
void cMenuSetup::Set(void) void cMenuSetup::Set(void)
{ {
Clear(); Clear();
SetTitle(tr("Setup")); char buffer[64];
snprintf(buffer, sizeof(buffer), "%s - VDR %s", tr("Setup"), VDRVERSION);
SetTitle(buffer);
SetHasHotkeys(); SetHasHotkeys();
Add(new cOsdItem(hk(tr("OSD")), osUser1)); Add(new cOsdItem(hk(tr("OSD")), osUser1));
Add(new cOsdItem(hk(tr("EPG")), osUser2)); Add(new cOsdItem(hk(tr("EPG")), osUser2));
@ -2275,7 +1789,9 @@ void cMenuSetup::Set(void)
Add(new cOsdItem(hk(tr("Recording")), osUser6)); Add(new cOsdItem(hk(tr("Recording")), osUser6));
Add(new cOsdItem(hk(tr("Replay")), osUser7)); Add(new cOsdItem(hk(tr("Replay")), osUser7));
Add(new cOsdItem(hk(tr("Miscellaneous")), osUser8)); Add(new cOsdItem(hk(tr("Miscellaneous")), osUser8));
Add(new cOsdItem(hk(tr("Restart")), osUser9)); if (cPluginManager::HasPlugins())
Add(new cOsdItem(hk(tr("Plugins")), osUser9));
Add(new cOsdItem(hk(tr("Restart")), osUser10));
} }
eOSState cMenuSetup::Restart(void) eOSState cMenuSetup::Restart(void)
@ -2301,7 +1817,8 @@ eOSState cMenuSetup::ProcessKey(eKeys Key)
case osUser6: return AddSubMenu(new cMenuSetupRecord); case osUser6: return AddSubMenu(new cMenuSetupRecord);
case osUser7: return AddSubMenu(new cMenuSetupReplay); case osUser7: return AddSubMenu(new cMenuSetupReplay);
case osUser8: return AddSubMenu(new cMenuSetupMisc); case osUser8: return AddSubMenu(new cMenuSetupMisc);
case osUser9: return Restart(); case osUser9: return AddSubMenu(new cMenuSetupPlugins);
case osUser10: return Restart();
default: ; default: ;
} }
if (Setup.OSDLanguage != osdLanguage) { if (Setup.OSDLanguage != osdLanguage) {
@ -2364,6 +1881,22 @@ eOSState cMenuCommands::ProcessKey(eKeys Key)
return state; return state;
} }
// --- cMenuPluginItem -------------------------------------------------------
class cMenuPluginItem : public cOsdItem {
private:
int pluginIndex;
public:
cMenuPluginItem(const char *Name, int Index);
int PluginIndex(void) { return pluginIndex; }
};
cMenuPluginItem::cMenuPluginItem(const char *Name, int Index)
:cOsdItem(Name, osPlugin)
{
pluginIndex = Index;
}
// --- cMenuMain ------------------------------------------------------------- // --- cMenuMain -------------------------------------------------------------
#define STOP_RECORDING tr(" Stop recording ") #define STOP_RECORDING tr(" Stop recording ")
@ -2408,6 +1941,22 @@ void cMenuMain::Set(void)
Add(new cOsdItem(hk(tr("Channels")), osChannels)); Add(new cOsdItem(hk(tr("Channels")), osChannels));
Add(new cOsdItem(hk(tr("Timers")), osTimers)); Add(new cOsdItem(hk(tr("Timers")), osTimers));
Add(new cOsdItem(hk(tr("Recordings")), osRecordings)); Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
// Plugins:
for (int i = 0; ; i++) {
cPlugin *p = cPluginManager::GetPlugin(i);
if (p) {
const char *item = p->MainMenuEntry();
if (item)
Add(new cMenuPluginItem(hk(item), i));
}
else
break;
}
// More basic menu items:
Add(new cOsdItem(hk(tr("Setup")), osSetup)); Add(new cOsdItem(hk(tr("Setup")), osSetup));
if (Commands.Count()) if (Commands.Count())
Add(new cOsdItem(hk(tr("Commands")), osCommands)); Add(new cOsdItem(hk(tr("Commands")), osCommands));
@ -2475,6 +2024,19 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
return osEnd; return osEnd;
} }
break; break;
case osPlugin: {
cMenuPluginItem *item = (cMenuPluginItem *)Get(Current());
if (item) {
cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
if (p) {
cOsdMenu *menu = p->MainMenuAction();
if (menu)
return AddSubMenu(menu);
}
}
state = osEnd;
}
break;
default: switch (Key) { default: switch (Key) {
case kMenu: state = osEnd; break; case kMenu: state = osEnd; break;
case kRed: if (!HasSubMenu()) case kRed: if (!HasSubMenu())

476
menuitems.c Normal file
View File

@ -0,0 +1,476 @@
/*
* menuitems.c: General purpose menu items
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menuitems.c 1.1 2002/05/09 10:10:12 kls Exp $
*/
#include "menuitems.h"
#include <ctype.h>
#include "i18n.h"
// --- cMenuEditItem ---------------------------------------------------------
cMenuEditItem::cMenuEditItem(const char *Name)
{
name = strdup(Name);
value = NULL;
}
cMenuEditItem::~cMenuEditItem()
{
delete name;
delete value;
}
void cMenuEditItem::SetValue(const char *Value)
{
delete value;
value = strdup(Value);
char *buffer = NULL;
asprintf(&buffer, "%s:\t%s", name, value);
SetText(buffer, false);
Display();
}
// --- cMenuEditIntItem ------------------------------------------------------
cMenuEditIntItem::cMenuEditIntItem(const char *Name, int *Value, int Min, int Max)
:cMenuEditItem(Name)
{
value = Value;
min = Min;
max = Max;
Set();
}
void cMenuEditIntItem::Set(void)
{
char buf[16];
snprintf(buf, sizeof(buf), "%d", *value);
SetValue(buf);
}
eOSState cMenuEditIntItem::ProcessKey(eKeys Key)
{
eOSState state = cMenuEditItem::ProcessKey(Key);
if (state == osUnknown) {
int newValue;
if (k0 <= Key && Key <= k9) {
if (fresh) {
*value = 0;
fresh = false;
}
newValue = *value * 10 + (Key - k0);
}
else if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
newValue = *value - 1;
fresh = true;
}
else if (NORMALKEY(Key) == kRight) {
newValue = *value + 1;
fresh = true;
}
else
return state;
if ((!fresh || min <= newValue) && newValue <= max) {
*value = newValue;
Set();
}
state = osContinue;
}
return state;
}
// --- cMenuEditBoolItem -----------------------------------------------------
cMenuEditBoolItem::cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString, const char *TrueString)
:cMenuEditIntItem(Name, Value, 0, 1)
{
falseString = FalseString ? FalseString : tr("no");
trueString = TrueString ? TrueString : tr("yes");
Set();
}
void cMenuEditBoolItem::Set(void)
{
char buf[16];
snprintf(buf, sizeof(buf), "%s", *value ? trueString : falseString);
SetValue(buf);
}
// --- cMenuEditChrItem ------------------------------------------------------
cMenuEditChrItem::cMenuEditChrItem(const char *Name, char *Value, const char *Allowed)
:cMenuEditItem(Name)
{
value = Value;
allowed = strdup(Allowed);
current = strchr(allowed, *Value);
if (!current)
current = allowed;
Set();
}
cMenuEditChrItem::~cMenuEditChrItem()
{
delete allowed;
}
void cMenuEditChrItem::Set(void)
{
char buf[2];
snprintf(buf, sizeof(buf), "%c", *value);
SetValue(buf);
}
eOSState cMenuEditChrItem::ProcessKey(eKeys Key)
{
eOSState state = cMenuEditItem::ProcessKey(Key);
if (state == osUnknown) {
if (NORMALKEY(Key) == kLeft) {
if (current > allowed)
current--;
}
else if (NORMALKEY(Key) == kRight) {
if (*(current + 1))
current++;
}
else
return state;
*value = *current;
Set();
state = osContinue;
}
return state;
}
// --- cMenuEditStrItem ------------------------------------------------------
cMenuEditStrItem::cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed)
:cMenuEditItem(Name)
{
value = Value;
length = Length;
allowed = strdup(Allowed);
pos = -1;
insert = uppercase = false;
newchar = true;
Set();
}
cMenuEditStrItem::~cMenuEditStrItem()
{
delete allowed;
}
void cMenuEditStrItem::SetHelpKeys(void)
{
if (pos >= 0)
Interface->Help(tr("ABC/abc"), tr(insert ? "Overwrite" : "Insert"), tr("Delete"));
else
Interface->Help(NULL);
}
void cMenuEditStrItem::Set(void)
{
char buf[1000];
const char *fmt = insert && newchar ? "[]%c%s" : "[%c]%s";
if (pos >= 0) {
strncpy(buf, value, pos);
snprintf(buf + pos, sizeof(buf) - pos - 2, fmt, *(value + pos), value + pos + 1);
int width = Interface->Width() - Interface->GetCols()[0];
if (cDvbApi::PrimaryDvbApi->WidthInCells(buf) <= width) {
// the whole buffer fits on the screen
SetValue(buf);
return;
}
width *= cDvbApi::PrimaryDvbApi->CellWidth();
width -= cDvbApi::PrimaryDvbApi->Width('>'); // assuming '<' and '>' have the same with
int w = 0;
int i = 0;
int l = strlen(buf);
while (i < l && w <= width)
w += cDvbApi::PrimaryDvbApi->Width(buf[i++]);
if (i >= pos + 4) {
// the cursor fits on the screen
buf[i - 1] = '>';
buf[i] = 0;
SetValue(buf);
return;
}
// the cursor doesn't fit on the screen
w = 0;
if (buf[i = pos + 3]) {
buf[i] = '>';
buf[i + 1] = 0;
}
else
i--;
while (i >= 0 && w <= width)
w += cDvbApi::PrimaryDvbApi->Width(buf[i--]);
buf[++i] = '<';
SetValue(buf + i);
}
else
SetValue(value);
}
char cMenuEditStrItem::Inc(char c, bool Up)
{
const char *p = strchr(allowed, c);
if (!p)
p = allowed;
if (Up) {
if (!*++p)
p = allowed;
}
else if (--p < allowed)
p = allowed + strlen(allowed) - 1;
return *p;
}
eOSState cMenuEditStrItem::ProcessKey(eKeys Key)
{
switch (Key) {
case kRed: // Switch between upper- and lowercase characters
if (pos >= 0 && (!insert || !newchar)) {
uppercase = !uppercase;
value[pos] = uppercase ? toupper(value[pos]) : tolower(value[pos]);
}
break;
case kGreen: // Toggle insert/overwrite modes
if (pos >= 0) {
insert = !insert;
newchar = true;
}
SetHelpKeys();
break;
case kYellow|k_Repeat:
case kYellow: // Remove the character at current position; in insert mode it is the character to the right of cursor
if (pos >= 0) {
if (strlen(value) > 1) {
memmove(value + pos, value + pos + 1, strlen(value) - pos);
// reduce position, if we removed the last character
if (pos == int(strlen(value)))
pos--;
}
else if (strlen(value) == 1)
value[0] = ' '; // This is the last character in the string, replace it with a blank
if (isalpha(value[pos]))
uppercase = isupper(value[pos]);
newchar = true;
}
break;
case kLeft|k_Repeat:
case kLeft: if (pos > 0) {
if (!insert || newchar)
pos--;
newchar = true;
}
if (!insert && isalpha(value[pos]))
uppercase = isupper(value[pos]);
break;
case kRight|k_Repeat:
case kRight: if (pos < length && pos < int(strlen(value)) ) {
if (++pos >= int(strlen(value))) {
if (pos >= 2 && value[pos - 1] == ' ' && value[pos - 2] == ' ')
pos--; // allow only two blanks at the end
else {
value[pos] = ' ';
value[pos + 1] = 0;
}
}
}
newchar = true;
if (!insert && isalpha(value[pos]))
uppercase = isupper(value[pos]);
if (pos == 0)
SetHelpKeys();
break;
case kUp|k_Repeat:
case kUp:
case kDown|k_Repeat:
case kDown: if (pos >= 0) {
if (insert && newchar) {
// create a new character in insert mode
if (int(strlen(value)) < length) {
memmove(value + pos + 1, value + pos, strlen(value) - pos + 1);
value[pos] = ' ';
}
}
if (uppercase)
value[pos] = toupper(Inc(tolower(value[pos]), NORMALKEY(Key) == kUp));
else
value[pos] = Inc( value[pos], NORMALKEY(Key) == kUp);
newchar = false;
}
else
return cMenuEditItem::ProcessKey(Key);
break;
case kOk: if (pos >= 0) {
pos = -1;
newchar = true;
stripspace(value);
SetHelpKeys();
break;
}
// run into default
default: return cMenuEditItem::ProcessKey(Key);
}
Set();
return osContinue;
}
// --- cMenuEditStraItem -----------------------------------------------------
cMenuEditStraItem::cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings)
:cMenuEditIntItem(Name, Value, 0, NumStrings - 1)
{
strings = Strings;
Set();
}
void cMenuEditStraItem::Set(void)
{
SetValue(strings[*value]);
}
// --- cMenuTextItem ---------------------------------------------------------
cMenuTextItem::cMenuTextItem(const char *Text, int X, int Y, int W, int H, eDvbColor FgColor, eDvbColor BgColor, eDvbFont Font)
{
x = X;
y = Y;
w = W;
h = H;
fgColor = FgColor;
bgColor = BgColor;
font = Font;
offset = 0;
eDvbFont oldFont = Interface->SetFont(font);
text = Interface->WrapText(Text, w - 1, &lines);
Interface->SetFont(oldFont);
if (h < 0)
h = lines;
}
cMenuTextItem::~cMenuTextItem()
{
delete text;
}
void cMenuTextItem::Clear(void)
{
Interface->Fill(x, y, w, h, bgColor);
}
void cMenuTextItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor)
{
int l = 0;
char *t = text;
eDvbFont oldFont = Interface->SetFont(font);
while (*t) {
char *n = strchr(t, '\n');
if (l >= offset) {
if (n)
*n = 0;
Interface->Write(x, y + l - offset, t, fgColor, bgColor);
if (n)
*n = '\n';
else
break;
}
if (!n)
break;
t = n + 1;
if (++l >= h + offset)
break;
}
Interface->SetFont(oldFont);
// scroll indicators use inverted color scheme!
if (CanScrollUp()) Interface->Write(x + w - 1, y, "^", bgColor, fgColor);
if (CanScrollDown()) Interface->Write(x + w - 1, y + h - 1, "v", bgColor, fgColor);
}
void cMenuTextItem::ScrollUp(bool Page)
{
if (CanScrollUp()) {
Clear();
offset = max(offset - (Page ? h : 1), 0);
Display();
}
}
void cMenuTextItem::ScrollDown(bool Page)
{
if (CanScrollDown()) {
Clear();
offset = min(offset + (Page ? h : 1), lines - h);
Display();
}
}
eOSState cMenuTextItem::ProcessKey(eKeys Key)
{
switch (Key) {
case kLeft|k_Repeat:
case kLeft:
case kUp|k_Repeat:
case kUp: ScrollUp(NORMALKEY(Key) == kLeft); break;
case kRight|k_Repeat:
case kRight:
case kDown|k_Repeat:
case kDown: ScrollDown(NORMALKEY(Key) == kRight); break;
default: return osUnknown;
}
return osContinue;
}
// --- cMenuSetupPage --------------------------------------------------------
cMenuSetupPage::cMenuSetupPage(void)
:cOsdMenu("", 33)
{
data = Setup;
osdLanguage = Setup.OSDLanguage;
}
void cMenuSetupPage::SetupTitle(const char *s)
{
char buf[40]; // can't call tr() for more than one string at a time!
char *q = buf + snprintf(buf, sizeof(buf), "%s - ", tr("Setup"));
snprintf(q, sizeof(buf) - strlen(buf), "%s", tr(s));
SetTitle(buf);
}
eOSState cMenuSetupPage::ProcessKey(eKeys Key)
{
eOSState state = cOsdMenu::ProcessKey(Key);
if (state == osUnknown) {
switch (Key) {
case kOk: state = (Setup.PrimaryDVB != data.PrimaryDVB) ? osSwitchDvb : osBack;
cDvbApi::PrimaryDvbApi->SetVideoFormat(data.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3);
Setup = data;
Setup.Save();
cDvbApi::SetCaCaps();
break;
default: break;
}
}
if (data.OSDLanguage != osdLanguage) {
int OriginalOSDLanguage = Setup.OSDLanguage;
Setup.OSDLanguage = data.OSDLanguage;
Set();
Display();
osdLanguage = data.OSDLanguage;
Setup.OSDLanguage = OriginalOSDLanguage;
}
return state;
}

110
menuitems.h Normal file
View File

@ -0,0 +1,110 @@
/*
* menuitems.h: General purpose menu items
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menuitems.h 1.1 2002/05/09 09:41:06 kls Exp $
*/
#ifndef __MENUITEMS_H
#define __MENUITEMS_H
#include "osd.h"
class cMenuEditItem : public cOsdItem {
private:
const char *name;
const char *value;
public:
cMenuEditItem(const char *Name);
~cMenuEditItem();
void SetValue(const char *Value);
};
class cMenuEditIntItem : public cMenuEditItem {
protected:
int *value;
int min, max;
virtual void Set(void);
public:
cMenuEditIntItem(const char *Name, int *Value, int Min = 0, int Max = INT_MAX);
virtual eOSState ProcessKey(eKeys Key);
};
class cMenuEditBoolItem : public cMenuEditIntItem {
protected:
const char *falseString, *trueString;
virtual void Set(void);
public:
cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString = NULL, const char *TrueString = NULL);
};
class cMenuEditChrItem : public cMenuEditItem {
private:
char *value;
const char *allowed;
const char *current;
virtual void Set(void);
public:
cMenuEditChrItem(const char *Name, char *Value, const char *Allowed);
~cMenuEditChrItem();
virtual eOSState ProcessKey(eKeys Key);
};
class cMenuEditStrItem : public cMenuEditItem {
private:
char *value;
int length;
const char *allowed;
int pos;
bool insert, newchar, uppercase;
void SetHelpKeys(void);
virtual void Set(void);
char Inc(char c, bool Up);
public:
cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed);
~cMenuEditStrItem();
virtual eOSState ProcessKey(eKeys Key);
};
class cMenuEditStraItem : public cMenuEditIntItem {
private:
const char * const *strings;
protected:
virtual void Set(void);
public:
cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings);
};
class cMenuTextItem : public cOsdItem {
private:
char *text;
int x, y, w, h, lines, offset;
eDvbColor fgColor, bgColor;
eDvbFont font;
public:
cMenuTextItem(const char *Text, int X, int Y, int W, int H = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground, eDvbFont Font = fontOsd);
~cMenuTextItem();
int Height(void) { return h; }
void Clear(void);
virtual void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground);
bool CanScrollUp(void) { return offset > 0; }
bool CanScrollDown(void) { return h + offset < lines; }
void ScrollUp(bool Page);
void ScrollDown(bool Page);
virtual eOSState ProcessKey(eKeys Key);
};
class cMenuSetupPage : public cOsdMenu {
protected:
cSetup data;
int osdLanguage;
void SetupTitle(const char *s);
virtual void Set(void) = 0;
public:
cMenuSetupPage(void);
virtual eOSState ProcessKey(eKeys Key);
};
#endif //__MENUITEMS_H

250
newplugin Executable file
View File

@ -0,0 +1,250 @@
#!/usr/bin/perl -w
# newplugin: Initializing a new plugin source directory
#
# Creates a new plugin source directory from which to start implementing
# a plugin for VDR.
# See the file PLUGINS.html for detailed instructions on how to
# write a plugin.
#
# Usage: newplugin <name>
#
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
# $Id: newplugin 1.1 2002/05/09 15:12:26 kls Exp $
$PLUGIN_NAME = $ARGV[0] || die "Usage: newplugin <name>\n";
die "Please use only lowercase letters and digits in the plugin name\n" if ($PLUGIN_NAME =~ tr/a-z0-9//c);
$PLUGIN_CLASS = ucfirst($PLUGIN_NAME);
$PLUGIN_VERSION = "0.0.1";
$PLUGIN_DESCRIPTION = "Enter description for '$PLUGIN_NAME' plugin";
$PLUGIN_MAINENTRY = $PLUGIN_CLASS;
$PLUGINS_SRC = "PLUGINS/src";
$README = qq
{This is a "plugin" for the Video Disk Recorder (VDR).
Written by: Your Name <email\@host.dom>
Project's homepage: URL
Latest version available at: URL
See the file COPYING for license information.
Description:
};
$HISTORY_TITLE = "VDR Plugin '$PLUGIN_NAME' Revision History";
$HISTORY_LINE = '-' x length($HISTORY_TITLE);
$HISTORY_DATE = sprintf("%4d-%02d-%02d", (localtime)[5] + 1900, (localtime)[4] + 1, (localtime)[3]);
$HISTORY = qq
{$HISTORY_TITLE
$HISTORY_LINE
$HISTORY_DATE: Version $PLUGIN_VERSION
- Initial revision.
};
$MAKEFILE = qq
{#
# Makefile for a Video Disk Recorder plugin
#
# \$Id\$
# The official name of this plugin.
# This name will be used in the '-P...' option of VDR to load the plugin.
# By default the main source file also carries this name.
#
PLUGIN = $PLUGIN_NAME
### The version number of this plugin (taken from the main source file):
VERSION = `grep 'static const char \\*VERSION *=' \$(PLUGIN).c | awk '{ print \$\$6 }' | sed -e 's/[";]//g'`
### The directory environment:
DVBDIR = ../../../../DVB/ost/include
VDRDIR = ../../..
VDRINC = \$(VDRDIR)/include
LIBDIR = ../../lib
TMPDIR = /tmp
### The version number of VDR (taken from VDR's "config.h"):
VDRVERSION = `grep 'define VDRVERSION ' \$(VDRDIR)/config.h | awk '{ print \$\$3 }' | sed -e 's/"//g'`
### The name of the distribution archive:
ARCHIVE = vdr-\$(PLUGIN)-\$(VERSION)
### Includes and Defines (add further entries here):
INCLUDES = -I\$(VDRINC) -I\$(DVBDIR)
DEFINES = -DPLUGIN_NAME_I18N='"\$(PLUGIN)"'
### The object files (add further files here):
OBJS = \$(PLUGIN).o
### The C++ compiler and options:
CXX = g++
CXXFLAGS = -O2 -Wall -Woverloaded-virtual -m486
### Implicit rules:
%.o: %.c
\$(CXX) \$(CXXFLAGS) -c \$(DEFINES) \$(INCLUDES) \$<
# Dependencies:
MAKEDEP = g++ -MM -MG
DEPFILE = .dependencies
\$(DEPFILE): Makefile
\@\$(MAKEDEP) \$(DEFINES) \$(INCLUDES) \$(OBJS:%.o=%.c) > \$\@
include \$(DEPFILE)
### Targets:
all: libvdr-\$(PLUGIN).so
libvdr-\$(PLUGIN).so: \$(OBJS)
\$(CXX) \$(CXXFLAGS) -shared \$(OBJS) -o \$\@
\@cp \$\@ \$(LIBDIR)/\$\@.\$(VDRVERSION)
package: clean
\@-rm -rf \$(TMPDIR)/\$(ARCHIVE)
\@mkdir \$(TMPDIR)/\$(ARCHIVE)
\@cp -a * \$(TMPDIR)/\$(ARCHIVE)
\@tar czf \$(ARCHIVE).tgz -C \$(TMPDIR) \$(ARCHIVE)
\@-rm -rf \$(TMPDIR)/\$(ARCHIVE)
\@echo Distribution archive created as \$(ARCHIVE).tgz
clean:
\@-rm -f \$(OBJS) \$(DEPFILE) *.so *.tgz core* *~
};
$MAIN = qq
{/*
* $PLUGIN_NAME.c: A plugin for the Video Disk Recorder
*
* See the README file for copyright information and how to reach the author.
*
* \$Id\$
*/
#include <vdr/plugin.h>
static const char *VERSION = "$PLUGIN_VERSION";
static const char *DESCRIPTION = "$PLUGIN_DESCRIPTION";
static const char *MAINMENUENTRY = "$PLUGIN_MAINENTRY";
class cPlugin$PLUGIN_CLASS : public cPlugin {
private:
// Add any member variables or functions you may need here.
public:
cPlugin$PLUGIN_CLASS(void);
virtual ~cPlugin$PLUGIN_CLASS();
virtual const char *Version(void) { return VERSION; }
virtual const char *Description(void) { return DESCRIPTION; }
virtual const char *CommandLineHelp(void);
virtual bool ProcessArgs(int argc, char *argv[]);
virtual void Start(void);
virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; }
virtual cOsdMenu *MainMenuAction(void);
virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value);
};
cPlugin${PLUGIN_CLASS}::cPlugin$PLUGIN_CLASS(void)
{
// Initialize any member varaiables here.
// DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
// VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
}
cPlugin${PLUGIN_CLASS}::~cPlugin$PLUGIN_CLASS()
{
// Clean up after yourself!
}
const char *cPlugin${PLUGIN_CLASS}::CommandLineHelp(void)
{
// Return a string that describes all known command line options.
return NULL;
}
bool cPlugin${PLUGIN_CLASS}::ProcessArgs(int argc, char *argv[])
{
// Implement command line argument processing here if applicable.
return true;
}
void cPlugin${PLUGIN_CLASS}::Start(void)
{
// Start any background activities the plugin shall perform.
}
cOsdMenu *cPlugin${PLUGIN_CLASS}::MainMenuAction(void)
{
// Perform the action when selected from the main VDR menu.
return NULL;
}
cMenuSetupPage *cPlugin${PLUGIN_CLASS}::SetupMenu(void)
{
// Return a setup menu in case the plugin supports one.
return NULL;
}
bool cPlugin${PLUGIN_CLASS}::SetupParse(const char *Name, const char *Value)
{
// Parse your own setup parameters and store their values.
return false;
}
VDRPLUGINCREATOR(cPlugin$PLUGIN_CLASS); // Don't touch this!
};
$PLUGINDIR = "$PLUGINS_SRC/$PLUGIN_NAME";
die "The directory $PLUGINS_SRC doesn't exist!\n" unless (-d "$PLUGINS_SRC");
die "A plugin named '$PLUGIN_NAME' already exists in $PLUGINS_SRC!\n" if (-e "$PLUGINDIR");
mkdir("$PLUGINDIR") || die "$!";
CreateFile("README", $README);
CreateFile("HISTORY", $HISTORY);
CreateFile("Makefile", $MAKEFILE);
CreateFile("$PLUGIN_NAME.c", $MAIN);
`cp COPYING "$PLUGINDIR"` if (-e "COPYING");
print qq{
The new plugin source directory has been created in "$PLUGINDIR".
The next steps you should perform now are:
* edit the file "README" to adjust it to your specific implementation
* fill in the code skeleton in "$PLUGIN_NAME.c" to implement your plugin function
* add further source files if necessary
* adapt the "Makefile" if necessary
* do "make plugins" from the VDR source directory to build your plugin
};
sub CreateFile
{
my ($Name, $Content) = @_;
open(FILE, ">$PLUGINDIR/$Name") || die "$Name: $!\n";
print FILE $Content;
close(FILE);
}

11
osd.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: osd.c 1.23 2002/03/29 16:34:03 kls Exp $ * $Id: osd.c 1.24 2002/05/05 12:00:00 kls Exp $
*/ */
#include "osd.h" #include "osd.h"
@ -105,12 +105,13 @@ cOsdMenu::~cOsdMenu()
const char *cOsdMenu::hk(const char *s) const char *cOsdMenu::hk(const char *s)
{ {
static char buffer[32]; static char buffer[64];
if (s && hasHotkeys) { if (s && hasHotkeys) {
if (digit == 0 && '1' <= *s && *s <= '9' && *(s + 1) == ' ') if (digit == 0 && '1' <= *s && *s <= '9' && *(s + 1) == ' ')
digit = 10; // prevents automatic hotkeys - input already has them digit = -1; // prevents automatic hotkeys - input already has them
if (digit < 9) { if (digit >= 0) {
snprintf(buffer, sizeof(buffer), " %d %s", ++digit, s); digit++;
snprintf(buffer, sizeof(buffer), " %c %s", (digit < 10) ? '0' + digit : ' ' , s);
s = buffer; s = buffer;
} }
} }

4
osd.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: osd.h 1.27 2002/03/10 16:18:11 kls Exp $ * $Id: osd.h 1.28 2002/05/05 12:00:00 kls Exp $
*/ */
#ifndef __OSD_H #ifndef __OSD_H
@ -23,6 +23,7 @@ enum eOSState { osUnknown,
osChannels, osChannels,
osTimers, osTimers,
osRecordings, osRecordings,
osPlugin,
osSetup, osSetup,
osCommands, osCommands,
osRecord, osRecord,
@ -43,6 +44,7 @@ enum eOSState { osUnknown,
osUser7, osUser7,
osUser8, osUser8,
osUser9, osUser9,
osUser10,
}; };
class cOsdItem : public cListObject { class cOsdItem : public cListObject {

319
plugin.c Normal file
View File

@ -0,0 +1,319 @@
/*
* plugin.c: The VDR plugin interface
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: plugin.c 1.1 2002/05/09 16:26:56 kls Exp $
*/
#include "plugin.h"
#include <ctype.h>
#include <dirent.h>
#include <dlfcn.h>
#include "config.h"
#define LIBVDR_PREFIX "libvdr-"
#define SO_INDICATOR ".so."
#define MAXPLUGINARGS 1024
// --- cPlugin ---------------------------------------------------------------
cPlugin::cPlugin(void)
{
name = NULL;
}
cPlugin::~cPlugin()
{
I18nRegister(NULL, Name());
}
void cPlugin::SetName(const char *s)
{
name = s;
}
const char *cPlugin::CommandLineHelp(void)
{
return NULL;
}
bool cPlugin::ProcessArgs(int argc, char *argv[])
{
return true;
}
void cPlugin::Start(void)
{
}
const char *cPlugin::MainMenuEntry(void)
{
return NULL;
}
cOsdMenu *cPlugin::MainMenuAction(void)
{
return NULL;
}
cMenuSetupPage *cPlugin::SetupMenu(void)
{
return NULL;
}
bool cPlugin::SetupParse(const char *Name, const char *Value)
{
return false;
}
void cPlugin::SetupStore(const char *Name, const char *Value)
{
Setup.Store(Name, Value, this->Name());
}
void cPlugin::SetupStore(const char *Name, int Value)
{
Setup.Store(Name, Value, this->Name());
}
void cPlugin::RegisterI18n(const tI18nPhrase * const Phrases)
{
I18nRegister(Phrases, Name());
}
// --- cDll ------------------------------------------------------------------
cDll::cDll(const char *FileName, const char *Args)
{
fileName = strdup(FileName);
args = Args ? strdup(Args) : NULL;
handle = NULL;
plugin = NULL;
}
cDll::~cDll()
{
delete plugin;
if (handle)
dlclose(handle);
delete args;
delete fileName;
}
static char *SkipQuote(char *s)
{
char c = *s;
strcpy(s, s + 1);
while (*s && *s != c) {
if (*s == '\\')
strcpy(s, s + 1);
if (*s)
s++;
}
if (*s) {
strcpy(s, s + 1);
return s;
}
esyslog(LOG_ERR, "ERROR: missing closing %c", c);
fprintf(stderr, "vdr: missing closing %c\n", c);
return NULL;
}
bool cDll::Load(bool Log)
{
if (Log)
isyslog(LOG_INFO, "loading plugin: %s", fileName);
if (handle) {
esyslog(LOG_ERR, "attempt to load plugin '%s' twice!", fileName);
return false;
}
handle = dlopen(fileName, RTLD_NOW);
const char *error = dlerror();
if (!error) {
void *(*creator)(void);
(void *)creator = dlsym(handle, "VDRPluginCreator");
if (!(error = dlerror()))
plugin = (cPlugin *)creator();
}
if (!error) {
if (plugin && args) {
int argc = 0;
char *argv[MAXPLUGINARGS];
char *p = args;
char *q = NULL;
bool done = false;
while (!done) {
if (!q)
q = p;
switch (*p) {
case '\\': strcpy(p, p + 1);
if (*p)
p++;
else {
esyslog(LOG_ERR, "ERROR: missing character after \\");
fprintf(stderr, "vdr: missing character after \\\n");
return false;
}
break;
case '"':
case '\'': if ((p = SkipQuote(p)) == NULL)
return false;
break;
default: if (!*p || isspace(*p)) {
done = !*p;
*p = 0;
if (q) {
if (argc < MAXPLUGINARGS - 1)
argv[argc++] = q;
else {
esyslog(LOG_ERR, "ERROR: plugin argument list too long");
fprintf(stderr, "vdr: plugin argument list too long\n");
return false;
}
q = NULL;
}
}
if (!done)
p++;
}
}
argv[argc] = NULL;
if (argc)
plugin->SetName(argv[0]);
optind = 0; // to reset the getopt() data
return !argc || plugin->ProcessArgs(argc, argv);
}
}
else {
esyslog(LOG_ERR, "ERROR: %s", error);
fprintf(stderr, "vdr: %s\n", error);
}
return !error && plugin;
}
// --- cPluginManager --------------------------------------------------------
cPluginManager *cPluginManager::pluginManager = NULL;
cPluginManager::cPluginManager(const char *Directory)
{
directory = NULL;
if (pluginManager) {
fprintf(stderr, "vdr: attempt to create more than one plugin manager - exiting!\n");
exit(2);
}
SetDirectory(Directory);
pluginManager = this;
}
cPluginManager::~cPluginManager()
{
Shutdown();
delete directory;
if (pluginManager == this)
pluginManager = NULL;
}
void cPluginManager::SetDirectory(const char *Directory)
{
delete directory;
directory = Directory ? strdup(Directory) : NULL;
}
void cPluginManager::AddPlugin(const char *Args)
{
if (strcmp(Args, "*") == 0) {
DIR *d = opendir(directory);
if (d) {
struct dirent *e;
while ((e = readdir(d)) != NULL) {
if (strstr(e->d_name, LIBVDR_PREFIX) == e->d_name) {
char *p = strstr(e->d_name, SO_INDICATOR);
if (p) {
*p = 0;
p += strlen(SO_INDICATOR);
if (strcmp(p, VDRVERSION) == 0) {
char *name = e->d_name + strlen(LIBVDR_PREFIX);
if (strcmp(name, "*") != 0) { // let's not get into a loop!
AddPlugin(e->d_name + strlen(LIBVDR_PREFIX));
}
}
}
}
}
closedir(d);
}
return;
}
char *s = strdup(Args);
char *p = strchr(s, ' ');
if (p)
*p = 0;
char *buffer = NULL;
asprintf(&buffer, "%s/%s%s%s%s", directory, LIBVDR_PREFIX, s, SO_INDICATOR, VDRVERSION);
dlls.Add(new cDll(buffer, Args));
delete buffer;
delete s;
}
bool cPluginManager::LoadPlugins(bool Log)
{
for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
if (!dll->Load(Log))
return false;
}
return true;
}
void cPluginManager::StartPlugins(void)
{
for (cDll *dll = dlls.First(); dll; dll = dlls.Next(dll)) {
cPlugin *p = dll->Plugin();
if (p) {
int Language = Setup.OSDLanguage;
Setup.OSDLanguage = 0; // the i18n texts are only available _after_ Start()
isyslog(LOG_INFO, "starting plugin: %s (%s): %s", p->Name(), p->Version(), p->Description());
Setup.OSDLanguage = Language;
dll->Plugin()->Start();
}
}
}
bool cPluginManager::HasPlugins(void)
{
return pluginManager && pluginManager->dlls.Count();
}
cPlugin *cPluginManager::GetPlugin(int Index)
{
cDll *dll = pluginManager ? pluginManager->dlls.Get(Index) : NULL;
return dll ? dll->Plugin() : NULL;
}
cPlugin *cPluginManager::GetPlugin(const char *Name)
{
if (pluginManager) {
for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
cPlugin *p = dll->Plugin();
if (p && strcmp(p->Name(), Name) == 0)
return p;
}
}
return NULL;
}
void cPluginManager::Shutdown(bool Log)
{
cDll *dll;
while ((dll = dlls.Last()) != NULL) {
if (Log) {
cPlugin *p = dll->Plugin();
if (p)
isyslog(LOG_INFO, "stopping plugin: %s", p->Name());
}
dlls.Del(dll);
}
}

81
plugin.h Normal file
View File

@ -0,0 +1,81 @@
/*
* plugin.h: The VDR plugin interface
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: plugin.h 1.1 2002/05/09 10:16:09 kls Exp $
*/
#ifndef __PLUGIN_H
#define __PLUGIN_H
#include "i18n.h"
#include "menuitems.h"
#include "osd.h"
#include "tools.h"
#define VDRPLUGINCREATOR(PluginClass) extern "C" void *VDRPluginCreator(void) { return new PluginClass; }
class cPlugin {
friend class cDll;
private:
const char *name;
void SetName(const char *s);
public:
cPlugin(void);
virtual ~cPlugin();
const char *Name(void) { return name; }
virtual const char *Version(void) = 0;
virtual const char *Description(void) = 0;
virtual const char *CommandLineHelp(void);
virtual bool ProcessArgs(int argc, char *argv[]);
virtual void Start(void);
virtual const char *MainMenuEntry(void);
virtual cOsdMenu *MainMenuAction(void);
virtual cMenuSetupPage *SetupMenu(void);
virtual bool SetupParse(const char *Name, const char *Value);
void SetupStore(const char *Name, const char *Value = NULL);
void SetupStore(const char *Name, int Value);
void RegisterI18n(const tI18nPhrase * const Phrases);
};
class cDll : public cListObject {
private:
char *fileName;
char *args;
void *handle;
cPlugin *plugin;
public:
cDll(const char *FileName, const char *Args);
virtual ~cDll();
bool Load(bool Log = false);
cPlugin *Plugin(void) { return plugin; }
};
class cDlls : public cList<cDll> {};
class cPluginManager {
private:
static cPluginManager *pluginManager;
char *directory;
cDlls dlls;
public:
cPluginManager(const char *Directory);
virtual ~cPluginManager();
void SetDirectory(const char *Directory);
void AddPlugin(const char *Args);
bool LoadPlugins(bool Log = false);
void StartPlugins(void);
static bool HasPlugins(void);
static cPlugin *GetPlugin(int Index);
static cPlugin *GetPlugin(const char *Name);
void Shutdown(bool Log = false);
};
#endif //__PLUGIN_H

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and * See the main source file 'vdr.c' for copyright information and
* how to reach the author. * how to reach the author.
* *
* $Id: remote.h 1.15 2001/07/22 14:42:59 kls Exp $ * $Id: remote.h 1.16 2002/05/09 11:43:04 kls Exp $
*/ */
#ifndef __REMOTE_H #ifndef __REMOTE_H
@ -95,7 +95,7 @@ public:
#elif !defined REMOTE_NONE #elif !defined REMOTE_NONE
#error Please define a remote control mode! // #error Please define a remote control mode!
#endif #endif

24
vdr.1
View File

@ -8,9 +8,9 @@
.\" License as specified in the file COPYING that comes with the .\" License as specified in the file COPYING that comes with the
.\" vdr distribution. .\" vdr distribution.
.\" .\"
.\" $Id: vdr.1 1.3 2002/04/07 13:11:43 kls Exp $ .\" $Id: vdr.1 1.4 2002/05/09 16:04:17 kls Exp $
.\" .\"
.TH vdr 1 "7 Apr 2002" "1.0.0" "Video Disk Recorder" .TH vdr 1 "9 May 2002" "1.1.0" "Video Disk Recorder"
.SH NAME .SH NAME
vdr - the Video Disk Recorder vdr - the Video Disk Recorder
.SH SYNOPSIS .SH SYNOPSIS
@ -68,6 +68,11 @@ Set logging to \fIlevel\fR.
\fB2\fR\ =\ errors and info, \fB3\fR\ =\ errors, info and debug. \fB2\fR\ =\ errors and info, \fB3\fR\ =\ errors, info and debug.
The default logging level is \fB3\fR. The default logging level is \fB3\fR.
.TP .TP
.BI -L\ dir ,\ --lib= dir
Search for plugins in directory \fIdir\fR (default is ./PLUGINS/lib).
There can be several \fB-L\fR options with different \fIdir\fR values.
Each of them will apply to the \fB-P\fR options following it.
.TP
.B -m, --mute .B -m, --mute
Mute audio of the primary DVB device at startup. Mute audio of the primary DVB device at startup.
.TP .TP
@ -77,6 +82,18 @@ The default SVDRP port is \fB2001\fR.
You need to edit the file \fIsvdrphosts.conf\fR in order to enable You need to edit the file \fIsvdrphosts.conf\fR in order to enable
access to the SVDRP port. access to the SVDRP port.
.TP .TP
.BI -P\ options ,\ --plugin= options
Load a plugin, defined by the given \fIoptions\fR.
The first word in \fIoptions\fR must be the name of an existing \fBvdr\fR
plugin, optionally followed by a blank separated list of command line options
for that plugin. If \fIoptions\fR contains any blanks, you need to enclose it
in quotes, like for example
\fBvdr -P "abc -a -b xyz"\fR
which would load a plugin named \fBabc\fR, giving it the command line options
\fB-a\ -b\ xyz\fR.
.TP
.BI -r\ cmd ,\ --record= cmd .BI -r\ cmd ,\ --record= cmd
Call \fIcmd\fR before and after a recording. Call \fIcmd\fR before and after a recording.
.TP .TP
@ -104,6 +121,9 @@ Successful program execution.
.B 1 .B 1
An error has been detected which requires the DVB driver and \fBvdr\fR An error has been detected which requires the DVB driver and \fBvdr\fR
to be re-loaded. to be re-loaded.
.TP
.B 2
An non-recoverable error has been detected, \fBvdr\fR has given up.
.SH FILES .SH FILES
.TP .TP
.I channels.conf .I channels.conf

131
vdr.c
View File

@ -22,7 +22,7 @@
* *
* The project's page is at http://www.cadsoft.de/people/kls/vdr * The project's page is at http://www.cadsoft.de/people/kls/vdr
* *
* $Id: vdr.c 1.103 2002/04/26 12:15:30 kls Exp $ * $Id: vdr.c 1.104 2002/05/05 12:00:00 kls Exp $
*/ */
#include <getopt.h> #include <getopt.h>
@ -36,6 +36,7 @@
#include "i18n.h" #include "i18n.h"
#include "interface.h" #include "interface.h"
#include "menu.h" #include "menu.h"
#include "plugin.h"
#include "recording.h" #include "recording.h"
#include "tools.h" #include "tools.h"
#include "videodir.h" #include "videodir.h"
@ -79,14 +80,18 @@ int main(int argc, char *argv[])
#define DEFAULTSVDRPPORT 2001 #define DEFAULTSVDRPPORT 2001
#define DEFAULTWATCHDOG 0 // seconds #define DEFAULTWATCHDOG 0 // seconds
#define DEFAULTPLUGINDIR "./PLUGINS/lib"
int SVDRPport = DEFAULTSVDRPPORT; int SVDRPport = DEFAULTSVDRPPORT;
const char *ConfigDirectory = NULL; const char *ConfigDirectory = NULL;
bool DisplayHelp = false;
bool DisplayVersion = false;
bool DaemonMode = false; bool DaemonMode = false;
bool MuteAudio = false; bool MuteAudio = false;
int WatchdogTimeout = DEFAULTWATCHDOG; int WatchdogTimeout = DEFAULTWATCHDOG;
const char *Terminal = NULL; const char *Terminal = NULL;
const char *Shutdown = NULL; const char *Shutdown = NULL;
cPluginManager PluginManager(DEFAULTPLUGINDIR);
static struct option long_options[] = { static struct option long_options[] = {
{ "audio", required_argument, NULL, 'a' }, { "audio", required_argument, NULL, 'a' },
@ -95,8 +100,10 @@ int main(int argc, char *argv[])
{ "device", required_argument, NULL, 'D' }, { "device", required_argument, NULL, 'D' },
{ "epgfile", required_argument, NULL, 'E' }, { "epgfile", required_argument, NULL, 'E' },
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "lib", required_argument, NULL, 'L' },
{ "log", required_argument, NULL, 'l' }, { "log", required_argument, NULL, 'l' },
{ "mute", no_argument, NULL, 'm' }, { "mute", no_argument, NULL, 'm' },
{ "plugin", required_argument, NULL, 'P' },
{ "port", required_argument, NULL, 'p' }, { "port", required_argument, NULL, 'p' },
{ "record", required_argument, NULL, 'r' }, { "record", required_argument, NULL, 'r' },
{ "shutdown", required_argument, NULL, 's' }, { "shutdown", required_argument, NULL, 's' },
@ -108,8 +115,7 @@ int main(int argc, char *argv[])
}; };
int c; int c;
int option_index = 0; while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:L:mp:P:r:s:t:v:Vw:", long_options, NULL)) != -1) {
while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:mp:r:s:t:v:Vw:", long_options, &option_index)) != -1) {
switch (c) { switch (c) {
case 'a': cDvbApi::SetAudioCommand(optarg); case 'a': cDvbApi::SetAudioCommand(optarg);
break; break;
@ -128,40 +134,7 @@ int main(int argc, char *argv[])
break; break;
case 'E': cSIProcessor::SetEpgDataFileName(*optarg != '-' ? optarg : NULL); case 'E': cSIProcessor::SetEpgDataFileName(*optarg != '-' ? optarg : NULL);
break; break;
case 'h': printf("Usage: vdr [OPTION]\n\n" // for easier orientation, this is column 80| case 'h': DisplayHelp = true;
" -a CMD, --audio=CMD send Dolby Digital audio to stdin of command CMD\n"
" -c DIR, --config=DIR read config files from DIR (default is to read them\n"
" from the video directory)\n"
" -d, --daemon run in daemon mode\n"
" -D NUM, --device=NUM use only the given DVB device (NUM = 0, 1, 2...)\n"
" there may be several -D options (default: all DVB\n"
" devices will be used)\n"
" -E FILE --epgfile=FILE write the EPG data into the given FILE (default is\n"
" %s); use '-E-' to disable this\n"
" if FILE is a directory, the default EPG file will be\n"
" created in that directory\n"
" -h, --help print this help and exit\n"
" -l LEVEL, --log=LEVEL set log level (default: 3)\n"
" 0 = no logging, 1 = errors only,\n"
" 2 = errors and info, 3 = errors, info and debug\n"
" -m, --mute mute audio of the primary DVB device at startup\n"
" -p PORT, --port=PORT use PORT for SVDRP (default: %d)\n"
" 0 turns off SVDRP\n"
" -r CMD, --record=CMD call CMD before and after a recording\n"
" -s CMD, --shutdown=CMD call CMD to shutdown the computer\n"
" -t TTY, --terminal=TTY controlling tty\n"
" -V, --version print version information and exit\n"
" -v DIR, --video=DIR use DIR as video directory (default: %s)\n"
" -w SEC, --watchdog=SEC activate the watchdog timer with a timeout of SEC\n"
" seconds (default: %d); '0' disables the watchdog\n"
"\n"
"Report bugs to <vdr-bugs@cadsoft.de>\n",
cSIProcessor::GetEpgDataFileName() ? cSIProcessor::GetEpgDataFileName() : "'-'",
DEFAULTSVDRPPORT,
VideoDirectory,
DEFAULTWATCHDOG
);
return 0;
break; break;
case 'l': if (isnumber(optarg)) { case 'l': if (isnumber(optarg)) {
int l = atoi(optarg); int l = atoi(optarg);
@ -173,6 +146,8 @@ int main(int argc, char *argv[])
fprintf(stderr, "vdr: invalid log level: %s\n", optarg); fprintf(stderr, "vdr: invalid log level: %s\n", optarg);
return 2; return 2;
break; break;
case 'L': PluginManager.SetDirectory(optarg);
break;
case 'm': MuteAudio = true; case 'm': MuteAudio = true;
break; break;
case 'p': if (isnumber(optarg)) case 'p': if (isnumber(optarg))
@ -182,14 +157,15 @@ int main(int argc, char *argv[])
return 2; return 2;
} }
break; break;
case 'P': PluginManager.AddPlugin(optarg);
break;
case 'r': cRecordingUserCommand::SetCommand(optarg); case 'r': cRecordingUserCommand::SetCommand(optarg);
break; break;
case 's': Shutdown = optarg; case 's': Shutdown = optarg;
break; break;
case 't': Terminal = optarg; case 't': Terminal = optarg;
break; break;
case 'V': printf("vdr, version %s\n", VDRVERSION); case 'V': DisplayVersion = true;
return 0;
break; break;
case 'v': VideoDirectory = optarg; case 'v': VideoDirectory = optarg;
while (optarg && *optarg && optarg[strlen(optarg) - 1] == '/') while (optarg && *optarg && optarg[strlen(optarg) - 1] == '/')
@ -209,6 +185,71 @@ int main(int argc, char *argv[])
} }
} }
// Help and version info:
if (DisplayHelp || DisplayVersion) {
if (!PluginManager.HasPlugins())
PluginManager.AddPlugin("*"); // adds all available plugins
PluginManager.LoadPlugins();
if (DisplayHelp) {
printf("Usage: vdr [OPTIONS]\n\n" // for easier orientation, this is column 80|
" -a CMD, --audio=CMD send Dolby Digital audio to stdin of command CMD\n"
" -c DIR, --config=DIR read config files from DIR (default is to read them\n"
" from the video directory)\n"
" -d, --daemon run in daemon mode\n"
" -D NUM, --device=NUM use only the given DVB device (NUM = 0, 1, 2...)\n"
" there may be several -D options (default: all DVB\n"
" devices will be used)\n"
" -E FILE --epgfile=FILE write the EPG data into the given FILE (default is\n"
" %s); use '-E-' to disable this\n"
" if FILE is a directory, the default EPG file will be\n"
" created in that directory\n"
" -h, --help print this help and exit\n"
" -l LEVEL, --log=LEVEL set log level (default: 3)\n"
" 0 = no logging, 1 = errors only,\n"
" 2 = errors and info, 3 = errors, info and debug\n"
" -L DIR, --lib=DIR search for plugins in DIR (default is %s)\n"
" -m, --mute mute audio of the primary DVB device at startup\n"
" -p PORT, --port=PORT use PORT for SVDRP (default: %d)\n"
" 0 turns off SVDRP\n"
" -P OPT, --plugin=OPT load a plugin defined by the given options\n"
" -r CMD, --record=CMD call CMD before and after a recording\n"
" -s CMD, --shutdown=CMD call CMD to shutdown the computer\n"
" -t TTY, --terminal=TTY controlling tty\n"
" -v DIR, --video=DIR use DIR as video directory (default: %s)\n"
" -V, --version print version information and exit\n"
" -w SEC, --watchdog=SEC activate the watchdog timer with a timeout of SEC\n"
" seconds (default: %d); '0' disables the watchdog\n"
"\n",
cSIProcessor::GetEpgDataFileName() ? cSIProcessor::GetEpgDataFileName() : "'-'",
DEFAULTPLUGINDIR,
DEFAULTSVDRPPORT,
VideoDirectory,
DEFAULTWATCHDOG
);
}
if (DisplayVersion)
printf("vdr (%s) - The Video Disk Recorder\n", VDRVERSION);
if (PluginManager.HasPlugins()) {
if (DisplayHelp)
printf("Plugins: vdr -P\"name [OPTIONS]\"\n\n");
for (int i = 0; ; i++) {
cPlugin *p = PluginManager.GetPlugin(i);
if (p) {
const char *help = p->CommandLineHelp();
printf("%s (%s) - %s\n", p->Name(), p->Version(), p->Description());
if (DisplayHelp && help) {
printf("\n");
puts(help);
}
}
else
break;
}
}
return 0;
}
// Log file: // Log file:
if (SysLogLevel > 0) if (SysLogLevel > 0)
@ -250,6 +291,11 @@ int main(int argc, char *argv[])
isyslog(LOG_INFO, "VDR version %s started", VDRVERSION); isyslog(LOG_INFO, "VDR version %s started", VDRVERSION);
// Load plugins:
if (!PluginManager.LoadPlugins(true))
return 2;
// Configuration data: // Configuration data:
if (!ConfigDirectory) if (!ConfigDirectory)
@ -276,6 +322,12 @@ int main(int argc, char *argv[])
cSIProcessor::Read(); cSIProcessor::Read();
// Start plugins:
PluginManager.StartPlugins();
// Channel:
Channels.SwitchTo(Setup.CurrentChannel); Channels.SwitchTo(Setup.CurrentChannel);
if (MuteAudio) if (MuteAudio)
cDvbApi::PrimaryDvbApi->ToggleMute(); cDvbApi::PrimaryDvbApi->ToggleMute();
@ -529,6 +581,7 @@ int main(int argc, char *argv[])
Setup.CurrentChannel = cDvbApi::CurrentChannel(); Setup.CurrentChannel = cDvbApi::CurrentChannel();
Setup.CurrentVolume = cDvbApi::CurrentVolume(); Setup.CurrentVolume = cDvbApi::CurrentVolume();
Setup.Save(); Setup.Save();
PluginManager.Shutdown(true);
cVideoCutter::Stop(); cVideoCutter::Stop();
delete Menu; delete Menu;
delete ReplayControl; delete ReplayControl;