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:
parent
bf4a5a678d
commit
b527b27708
14
HISTORY
14
HISTORY
@ -1,7 +1,9 @@
|
||||
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
|
||||
|
||||
@ -1242,3 +1244,13 @@ Video Disk Recorder Revision History
|
||||
'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
|
||||
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.
|
||||
|
28
Makefile
28
Makefile
@ -4,7 +4,7 @@
|
||||
# See the main source file 'vdr.c' for copyright information and
|
||||
# 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:
|
||||
|
||||
@ -13,15 +13,17 @@ DTVDIR = ./libdtv
|
||||
MANDIR = /usr/local/man
|
||||
BINDIR = /usr/local/bin
|
||||
|
||||
PLUGINDIR= ./PLUGINS
|
||||
|
||||
VIDEODIR = /video
|
||||
|
||||
INCLUDES = -I$(DVBDIR)/ost/include
|
||||
|
||||
DTVLIB = $(DTVDIR)/libdtv.a
|
||||
|
||||
OBJS = config.o dvbapi.o dvbosd.o eit.o font.o i18n.o interface.o menu.o osd.o\
|
||||
recording.o remote.o remux.o ringbuffer.o svdrp.o thread.o tools.o vdr.o\
|
||||
videodir.o
|
||||
OBJS = config.o dvbapi.o dvbosd.o eit.o font.o i18n.o interface.o menu.o\
|
||||
menuitems.o osd.o plugin.o recording.o remote.o remux.o ringbuffer.o\
|
||||
svdrp.o thread.o tools.o vdr.o videodir.o
|
||||
|
||||
OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
|
||||
FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1
|
||||
@ -69,7 +71,7 @@ include $(DEPFILE)
|
||||
# The main program:
|
||||
|
||||
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:
|
||||
|
||||
@ -88,6 +90,21 @@ genfontfile: genfontfile.c
|
||||
$(DTVLIB) $(DTVDIR)/libdtv.h:
|
||||
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:
|
||||
@ -104,6 +121,7 @@ install:
|
||||
clean:
|
||||
make -C $(DTVDIR) clean
|
||||
-rm -f $(OBJS) $(DEPFILE) vdr genfontfile genfontfile.o core* *~
|
||||
-rm -rf include
|
||||
fontclean:
|
||||
-rm -f fontfix.c fontosd.c
|
||||
CLEAN: clean fontclean
|
||||
|
620
PLUGINS.html
Normal file
620
PLUGINS.html
Normal 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 *</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>{ NULL }</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
340
PLUGINS/src/hello/COPYING
Normal 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.
|
6
PLUGINS/src/hello/HISTORY
Normal file
6
PLUGINS/src/hello/HISTORY
Normal file
@ -0,0 +1,6 @@
|
||||
VDR Plugin 'hello' Revision History
|
||||
-----------------------------------
|
||||
|
||||
2002-05-09: Version 0.0.1
|
||||
|
||||
- Initial revision.
|
78
PLUGINS/src/hello/Makefile
Normal file
78
PLUGINS/src/hello/Makefile
Normal 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
11
PLUGINS/src/hello/README
Normal 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
105
PLUGINS/src/hello/hello.c
Normal 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
52
PLUGINS/src/hello/i18n.c
Normal 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
11
PLUGINS/src/hello/i18n.h
Normal 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[];
|
285
config.c
285
config.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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"
|
||||
@ -13,6 +13,7 @@
|
||||
#include "dvbapi.h"
|
||||
#include "i18n.h"
|
||||
#include "interface.h"
|
||||
#include "plugin.h"
|
||||
|
||||
// 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
|
||||
@ -387,11 +388,12 @@ cTimer& cTimer::operator= (const cTimer &Timer)
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool cTimer::operator< (const cTimer &Timer)
|
||||
bool cTimer::operator< (const cListObject &ListObject)
|
||||
{
|
||||
cTimer *ti = (cTimer *)&ListObject;
|
||||
time_t t1 = StartTime();
|
||||
time_t t2 = (*(cTimer *)&Timer).StartTime();
|
||||
return t1 < t2 || (t1 == t2 && priority > Timer.priority);
|
||||
time_t t2 = ti->StartTime();
|
||||
return t1 < t2 || (t1 == t2 && priority > ti->priority);
|
||||
}
|
||||
|
||||
const char *cTimer::ToText(cTimer *Timer)
|
||||
@ -917,12 +919,76 @@ const cCaDefinition *cCaDefinitions::Get(int Number)
|
||||
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 Setup;
|
||||
|
||||
char *cSetup::fileName = NULL;
|
||||
|
||||
cSetup::cSetup(void)
|
||||
{
|
||||
OSDLanguage = 0;
|
||||
@ -966,19 +1032,77 @@ cSetup::cSetup(void)
|
||||
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++) {
|
||||
int written = 0;
|
||||
char buffer[MAXPARSEBUFFER];
|
||||
char *q = buffer;
|
||||
*buffer = 0;
|
||||
for (int i = 0; i < MAXCACAPS; i++) {
|
||||
if (CaCaps[d][i]) {
|
||||
if (!written++)
|
||||
fprintf(f, "CaCaps = %d", d + 1);
|
||||
fprintf(f, " %d", CaCaps[d][i]);
|
||||
if (!*buffer)
|
||||
q += snprintf(buffer, sizeof(buffer), "%d", d + 1);
|
||||
q += snprintf(q, sizeof(buffer) - (q - buffer), " %d", CaCaps[d][i]);
|
||||
}
|
||||
}
|
||||
if (written)
|
||||
fprintf(f, "\n");
|
||||
if (*buffer)
|
||||
Store(Name, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1005,14 +1129,8 @@ bool cSetup::ParseCaCaps(const char *Value)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cSetup::Parse(char *s)
|
||||
bool cSetup::Parse(const char *Name, const char *Value)
|
||||
{
|
||||
char *p = strchr(s, '=');
|
||||
if (p) {
|
||||
*p = 0;
|
||||
char *Name = compactspace(s);
|
||||
char *Value = compactspace(p + 1);
|
||||
if (*Name && *Value) {
|
||||
if (!strcasecmp(Name, "OSDLanguage")) OSDLanguage = atoi(Value);
|
||||
else if (!strcasecmp(Name, "PrimaryDVB")) PrimaryDVB = atoi(Value);
|
||||
else if (!strcasecmp(Name, "ShowInfoOnChSwitch")) ShowInfoOnChSwitch = atoi(Value);
|
||||
@ -1056,93 +1174,54 @@ bool cSetup::Parse(char *s)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cSetup::Load(const char *FileName)
|
||||
bool cSetup::Save(void)
|
||||
{
|
||||
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
|
||||
LOG_ERROR_STR(FileName);
|
||||
return false;
|
||||
}
|
||||
Store("OSDLanguage", OSDLanguage);
|
||||
Store("PrimaryDVB", PrimaryDVB);
|
||||
Store("ShowInfoOnChSwitch", ShowInfoOnChSwitch);
|
||||
Store("MenuScrollPage", MenuScrollPage);
|
||||
Store("MarkInstantRecord", MarkInstantRecord);
|
||||
Store("NameInstantRecord", NameInstantRecord);
|
||||
Store("InstantRecordTime", InstantRecordTime);
|
||||
Store("LnbSLOF", LnbSLOF);
|
||||
Store("LnbFrequLo", LnbFrequLo);
|
||||
Store("LnbFrequHi", LnbFrequHi);
|
||||
Store("DiSEqC", DiSEqC);
|
||||
Store("SetSystemTime", SetSystemTime);
|
||||
Store("TimeTransponder", TimeTransponder);
|
||||
Store("MarginStart", MarginStart);
|
||||
Store("MarginStop", MarginStop);
|
||||
Store("EPGScanTimeout", EPGScanTimeout);
|
||||
Store("EPGBugfixLevel", EPGBugfixLevel);
|
||||
Store("SVDRPTimeout", SVDRPTimeout);
|
||||
Store("SortTimers", SortTimers);
|
||||
Store("PrimaryLimit", PrimaryLimit);
|
||||
Store("DefaultPriority", DefaultPriority);
|
||||
Store("DefaultLifetime", DefaultLifetime);
|
||||
Store("UseSubtitle", UseSubtitle);
|
||||
Store("RecordingDirs", RecordingDirs);
|
||||
Store("VideoFormat", VideoFormat);
|
||||
Store("RecordDolbyDigital", RecordDolbyDigital);
|
||||
Store("ChannelInfoPos", ChannelInfoPos);
|
||||
Store("OSDwidth", OSDwidth);
|
||||
Store("OSDheight", OSDheight);
|
||||
Store("OSDMessageTime", OSDMessageTime);
|
||||
Store("MaxVideoFileSize", MaxVideoFileSize);
|
||||
Store("SplitEditedFiles", SplitEditedFiles);
|
||||
Store("MinEventTimeout", MinEventTimeout);
|
||||
Store("MinUserInactivity", MinUserInactivity);
|
||||
Store("MultiSpeedMode", MultiSpeedMode);
|
||||
Store("ShowReplayMode", ShowReplayMode);
|
||||
StoreCaCaps("CaCaps");
|
||||
Store("CurrentChannel", CurrentChannel);
|
||||
Store("CurrentVolume", CurrentVolume);
|
||||
|
||||
bool cSetup::Save(const char *FileName)
|
||||
{
|
||||
if (!FileName)
|
||||
FileName = fileName;
|
||||
if (FileName) {
|
||||
cSafeFile f(FileName);
|
||||
if (f.Open()) {
|
||||
fprintf(f, "# VDR Setup\n");
|
||||
fprintf(f, "OSDLanguage = %d\n", OSDLanguage);
|
||||
fprintf(f, "PrimaryDVB = %d\n", PrimaryDVB);
|
||||
fprintf(f, "ShowInfoOnChSwitch = %d\n", ShowInfoOnChSwitch);
|
||||
fprintf(f, "MenuScrollPage = %d\n", MenuScrollPage);
|
||||
fprintf(f, "MarkInstantRecord = %d\n", MarkInstantRecord);
|
||||
fprintf(f, "NameInstantRecord = %s\n", NameInstantRecord);
|
||||
fprintf(f, "InstantRecordTime = %d\n", InstantRecordTime);
|
||||
fprintf(f, "LnbSLOF = %d\n", LnbSLOF);
|
||||
fprintf(f, "LnbFrequLo = %d\n", LnbFrequLo);
|
||||
fprintf(f, "LnbFrequHi = %d\n", LnbFrequHi);
|
||||
fprintf(f, "DiSEqC = %d\n", DiSEqC);
|
||||
fprintf(f, "SetSystemTime = %d\n", SetSystemTime);
|
||||
fprintf(f, "TimeTransponder = %d\n", TimeTransponder);
|
||||
fprintf(f, "MarginStart = %d\n", MarginStart);
|
||||
fprintf(f, "MarginStop = %d\n", MarginStop);
|
||||
fprintf(f, "EPGScanTimeout = %d\n", EPGScanTimeout);
|
||||
fprintf(f, "EPGBugfixLevel = %d\n", EPGBugfixLevel);
|
||||
fprintf(f, "SVDRPTimeout = %d\n", SVDRPTimeout);
|
||||
fprintf(f, "SortTimers = %d\n", SortTimers);
|
||||
fprintf(f, "PrimaryLimit = %d\n", PrimaryLimit);
|
||||
fprintf(f, "DefaultPriority = %d\n", DefaultPriority);
|
||||
fprintf(f, "DefaultLifetime = %d\n", DefaultLifetime);
|
||||
fprintf(f, "UseSubtitle = %d\n", UseSubtitle);
|
||||
fprintf(f, "RecordingDirs = %d\n", RecordingDirs);
|
||||
fprintf(f, "VideoFormat = %d\n", VideoFormat);
|
||||
fprintf(f, "RecordDolbyDigital = %d\n", RecordDolbyDigital);
|
||||
fprintf(f, "ChannelInfoPos = %d\n", ChannelInfoPos);
|
||||
fprintf(f, "OSDwidth = %d\n", OSDwidth);
|
||||
fprintf(f, "OSDheight = %d\n", OSDheight);
|
||||
fprintf(f, "OSDMessageTime = %d\n", OSDMessageTime);
|
||||
fprintf(f, "MaxVideoFileSize = %d\n", MaxVideoFileSize);
|
||||
fprintf(f, "SplitEditedFiles = %d\n", SplitEditedFiles);
|
||||
fprintf(f, "MinEventTimeout = %d\n", MinEventTimeout);
|
||||
fprintf(f, "MinUserInactivity = %d\n", MinUserInactivity);
|
||||
fprintf(f, "MultiSpeedMode = %d\n", MultiSpeedMode);
|
||||
fprintf(f, "ShowReplayMode = %d\n", ShowReplayMode);
|
||||
PrintCaCaps(f, "CaCaps");
|
||||
fprintf(f, "CurrentChannel = %d\n", CurrentChannel);
|
||||
fprintf(f, "CurrentVolume = %d\n", CurrentVolume);
|
||||
if (f.Close()) {
|
||||
isyslog(LOG_INFO, "saved setup to %s", FileName);
|
||||
Sort();
|
||||
|
||||
if (cConfig<cSetupLine>::Save()) {
|
||||
isyslog(LOG_INFO, "saved setup to %s", FileName());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
esyslog(LOG_ERR, "attempt to save setup without file name");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
42
config.h
42
config.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -19,7 +19,7 @@
|
||||
#include "eit.h"
|
||||
#include "tools.h"
|
||||
|
||||
#define VDRVERSION "1.0.2"
|
||||
#define VDRVERSION "1.1.0"
|
||||
|
||||
#define MAXPRIORITY 99
|
||||
#define MAXLIFETIME 99
|
||||
@ -149,7 +149,7 @@ public:
|
||||
cTimer(const cEventInfo *EventInfo);
|
||||
virtual ~cTimer();
|
||||
cTimer& operator= (const cTimer &Timer);
|
||||
bool operator< (const cTimer &Timer);
|
||||
virtual bool operator< (const cListObject &ListObject);
|
||||
const char *ToText(void);
|
||||
bool Parse(const char *s);
|
||||
bool Save(FILE *f);
|
||||
@ -220,7 +220,8 @@ private:
|
||||
public:
|
||||
cConfig(void) { fileName = NULL; }
|
||||
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();
|
||||
fileName = strdup(FileName);
|
||||
@ -323,14 +324,35 @@ extern cCommands Commands;
|
||||
extern cSVDRPhosts SVDRPhosts;
|
||||
extern cCaDefinitions CaDefinitions;
|
||||
|
||||
class cSetup {
|
||||
class cSetupLine : public cListObject {
|
||||
private:
|
||||
static char *fileName;
|
||||
void PrintCaCaps(FILE *f, const char *Name);
|
||||
bool ParseCaCaps(const char *Value);
|
||||
char *plugin;
|
||||
char *name;
|
||||
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 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:
|
||||
// Also adjust cMenuSetup (menu.c) when adding parameters here!
|
||||
int __BeginData__;
|
||||
int OSDLanguage;
|
||||
int PrimaryDVB;
|
||||
int ShowInfoOnChSwitch;
|
||||
@ -366,9 +388,11 @@ public:
|
||||
int CaCaps[MAXDVBAPI][MAXCACAPS];
|
||||
int CurrentChannel;
|
||||
int CurrentVolume;
|
||||
int __EndData__;
|
||||
cSetup(void);
|
||||
cSetup& operator= (const cSetup &s);
|
||||
bool Load(const char *FileName);
|
||||
bool Save(const char *FileName = NULL);
|
||||
bool Save(void);
|
||||
};
|
||||
|
||||
extern cSetup Setup;
|
||||
|
133
i18n.c
133
i18n.c
@ -4,27 +4,29 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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>
|
||||
* Italian translations provided by Alberto Carraro <bertocar@tin.it>
|
||||
* Dutch translations provided by Arnold Niessen <niessen@iae.nl> <arnold.niessen@philips.com>
|
||||
* Portuguese translations provided by Paulo Lopes <pmml@netvita.pt>
|
||||
* French translations provided by Jean-Claude Repetto <jc@repetto.org>
|
||||
* Norwegian translations provided by Jørgen Tvedt <pjtvedt@online.no> and Truls Slevigen <truls@slevigen.no>
|
||||
* Finnish translations provided by Hannu Savolainen <hannu@opensound.com>
|
||||
* Polish translations provided by Michael Rakowski <mrak@gmx.de>
|
||||
* Spanish translations provided by Ruben Nunez Francisco <ruben.nunez@tang-it.com>
|
||||
* Greek translations provided by Dimitrios Dimitrakos <mail@dimitrios.de>
|
||||
* Translations provided by:
|
||||
*
|
||||
* Slovenian Miha Setina <mihasetina@softhome.net> and Matjaz Thaler <matjaz.thaler@guest.arnes.si>
|
||||
* Italian Alberto Carraro <bertocar@tin.it>
|
||||
* Dutch Arnold Niessen <niessen@iae.nl> <arnold.niessen@philips.com>
|
||||
* Portuguese Paulo Lopes <pmml@netvita.pt>
|
||||
* French Jean-Claude Repetto <jc@repetto.org>
|
||||
* Norwegian Jørgen Tvedt <pjtvedt@online.no> and Truls Slevigen <truls@slevigen.no>
|
||||
* Finnish Hannu Savolainen <hannu@opensound.com>
|
||||
* 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:
|
||||
*
|
||||
* 1. Announce your translation action on the Linux-DVB mailing
|
||||
* 1. Announce your translation action on the VDR mailing
|
||||
* 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,
|
||||
* containing the translated text for the new 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
|
||||
* be available!
|
||||
* 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
|
||||
* it included in the next version of VDR.
|
||||
*
|
||||
@ -60,15 +62,10 @@
|
||||
*/
|
||||
|
||||
#include "i18n.h"
|
||||
#include <stdio.h>
|
||||
#include "config.h"
|
||||
#include "tools.h"
|
||||
|
||||
const int NumLanguages = 12;
|
||||
|
||||
typedef const char *tPhrase[NumLanguages];
|
||||
|
||||
const tPhrase Phrases[] = {
|
||||
const tI18nPhrase Phrases[] = {
|
||||
// The name of the language (this MUST be the first phrase!):
|
||||
{ "English",
|
||||
"Deutsch",
|
||||
@ -1260,6 +1257,19 @@ const tPhrase Phrases[] = {
|
||||
"Varios",
|
||||
"Diafora",
|
||||
},
|
||||
{ "Plugins",
|
||||
"Plugins",
|
||||
"Plugins",
|
||||
"Plugins",
|
||||
"Plugins",
|
||||
"Plugins",
|
||||
"Plugins",
|
||||
"Plugins",
|
||||
"Plugins",
|
||||
"Plugins",
|
||||
"Plugins",
|
||||
"Plugins",
|
||||
},
|
||||
{ "Restart",
|
||||
"Neustart",
|
||||
"Ponoven zagon",
|
||||
@ -2384,26 +2394,101 @@ const tPhrase Phrases[] = {
|
||||
"buscando grabaciones...",
|
||||
"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 }
|
||||
};
|
||||
|
||||
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) {
|
||||
for (const tPhrase *p = Phrases; **p; p++) {
|
||||
const tI18nPhrase *p = Plugin ? I18nList.GetPhrases(Plugin) : Phrases;
|
||||
if (!p)
|
||||
p = Phrases;
|
||||
for (int i = ((p == Phrases) ? 1 : 2); i--; ) {
|
||||
for (; **p; p++) {
|
||||
if (strcmp(s, **p) == 0) {
|
||||
const char *t = (*p)[Setup.OSDLanguage];
|
||||
if (t && *t)
|
||||
return t;
|
||||
}
|
||||
}
|
||||
esyslog(LOG_ERR, "no translation found for '%s' in language %d (%s)\n", s, Setup.OSDLanguage, Phrases[0][Setup.OSDLanguage]);
|
||||
p = Phrases;
|
||||
}
|
||||
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, '$');
|
||||
return p ? p + 1 : s;
|
||||
}
|
||||
|
||||
const char * const * Languages(void)
|
||||
const char * const * I18nLanguages(void)
|
||||
{
|
||||
return &Phrases[0][0];
|
||||
}
|
||||
|
20
i18n.h
20
i18n.h
@ -4,16 +4,28 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
#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
|
||||
|
690
menu.c
690
menu.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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"
|
||||
@ -16,6 +16,8 @@
|
||||
#include "config.h"
|
||||
#include "eit.h"
|
||||
#include "i18n.h"
|
||||
#include "menuitems.h"
|
||||
#include "plugin.h"
|
||||
#include "videodir.h"
|
||||
|
||||
#define MENUTIMEOUT 120 // seconds
|
||||
@ -28,125 +30,6 @@
|
||||
|
||||
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 -----------------------------------------------------
|
||||
|
||||
class cMenuEditChanItem : public cMenuEditIntItem {
|
||||
@ -451,282 +334,6 @@ eOSState cMenuEditTimeItem::ProcessKey(eKeys Key)
|
||||
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 -------------------------------------------------------
|
||||
|
||||
class cMenuEditCaItem : public cMenuEditIntItem {
|
||||
@ -1018,116 +625,6 @@ eOSState cMenuChannels::ProcessKey(eKeys Key)
|
||||
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 -------------------------------------------------------------
|
||||
|
||||
class cMenuText : public cOsdMenu {
|
||||
@ -2028,60 +1525,6 @@ eOSState cMenuRecordings::ProcessKey(eKeys Key)
|
||||
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 ---------------------------------------------------------
|
||||
|
||||
class cMenuSetupOSD : public cMenuSetupPage {
|
||||
@ -2095,7 +1538,7 @@ void cMenuSetupOSD::Set(void)
|
||||
{
|
||||
Clear();
|
||||
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$Height"), &data.OSDheight, MINOSDHEIGHT, MAXOSDHEIGHT));
|
||||
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));
|
||||
}
|
||||
|
||||
// --- 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 ------------------------------------------------------------
|
||||
|
||||
class cMenuSetup : public cOsdMenu {
|
||||
@ -2265,7 +1777,9 @@ cMenuSetup::cMenuSetup(void)
|
||||
void cMenuSetup::Set(void)
|
||||
{
|
||||
Clear();
|
||||
SetTitle(tr("Setup"));
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), "%s - VDR %s", tr("Setup"), VDRVERSION);
|
||||
SetTitle(buffer);
|
||||
SetHasHotkeys();
|
||||
Add(new cOsdItem(hk(tr("OSD")), osUser1));
|
||||
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("Replay")), osUser7));
|
||||
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)
|
||||
@ -2301,7 +1817,8 @@ eOSState cMenuSetup::ProcessKey(eKeys Key)
|
||||
case osUser6: return AddSubMenu(new cMenuSetupRecord);
|
||||
case osUser7: return AddSubMenu(new cMenuSetupReplay);
|
||||
case osUser8: return AddSubMenu(new cMenuSetupMisc);
|
||||
case osUser9: return Restart();
|
||||
case osUser9: return AddSubMenu(new cMenuSetupPlugins);
|
||||
case osUser10: return Restart();
|
||||
default: ;
|
||||
}
|
||||
if (Setup.OSDLanguage != osdLanguage) {
|
||||
@ -2364,6 +1881,22 @@ eOSState cMenuCommands::ProcessKey(eKeys Key)
|
||||
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 -------------------------------------------------------------
|
||||
|
||||
#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("Timers")), osTimers));
|
||||
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));
|
||||
if (Commands.Count())
|
||||
Add(new cOsdItem(hk(tr("Commands")), osCommands));
|
||||
@ -2475,6 +2024,19 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
|
||||
return osEnd;
|
||||
}
|
||||
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) {
|
||||
case kMenu: state = osEnd; break;
|
||||
case kRed: if (!HasSubMenu())
|
||||
|
476
menuitems.c
Normal file
476
menuitems.c
Normal 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
110
menuitems.h
Normal 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
250
newplugin
Executable 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
11
osd.c
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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"
|
||||
@ -105,12 +105,13 @@ cOsdMenu::~cOsdMenu()
|
||||
|
||||
const char *cOsdMenu::hk(const char *s)
|
||||
{
|
||||
static char buffer[32];
|
||||
static char buffer[64];
|
||||
if (s && hasHotkeys) {
|
||||
if (digit == 0 && '1' <= *s && *s <= '9' && *(s + 1) == ' ')
|
||||
digit = 10; // prevents automatic hotkeys - input already has them
|
||||
if (digit < 9) {
|
||||
snprintf(buffer, sizeof(buffer), " %d %s", ++digit, s);
|
||||
digit = -1; // prevents automatic hotkeys - input already has them
|
||||
if (digit >= 0) {
|
||||
digit++;
|
||||
snprintf(buffer, sizeof(buffer), " %c %s", (digit < 10) ? '0' + digit : ' ' , s);
|
||||
s = buffer;
|
||||
}
|
||||
}
|
||||
|
4
osd.h
4
osd.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -23,6 +23,7 @@ enum eOSState { osUnknown,
|
||||
osChannels,
|
||||
osTimers,
|
||||
osRecordings,
|
||||
osPlugin,
|
||||
osSetup,
|
||||
osCommands,
|
||||
osRecord,
|
||||
@ -43,6 +44,7 @@ enum eOSState { osUnknown,
|
||||
osUser7,
|
||||
osUser8,
|
||||
osUser9,
|
||||
osUser10,
|
||||
};
|
||||
|
||||
class cOsdItem : public cListObject {
|
||||
|
319
plugin.c
Normal file
319
plugin.c
Normal 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
81
plugin.h
Normal 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
|
4
remote.h
4
remote.h
@ -4,7 +4,7 @@
|
||||
* See the main source file 'vdr.c' for copyright information and
|
||||
* 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
|
||||
@ -95,7 +95,7 @@ public:
|
||||
|
||||
#elif !defined REMOTE_NONE
|
||||
|
||||
#error Please define a remote control mode!
|
||||
// #error Please define a remote control mode!
|
||||
|
||||
#endif
|
||||
|
||||
|
24
vdr.1
24
vdr.1
@ -8,9 +8,9 @@
|
||||
.\" License as specified in the file COPYING that comes with the
|
||||
.\" 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
|
||||
vdr - the Video Disk Recorder
|
||||
.SH SYNOPSIS
|
||||
@ -68,6 +68,11 @@ Set logging to \fIlevel\fR.
|
||||
\fB2\fR\ =\ errors and info, \fB3\fR\ =\ errors, info and debug.
|
||||
The default logging level is \fB3\fR.
|
||||
.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
|
||||
Mute audio of the primary DVB device at startup.
|
||||
.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
|
||||
access to the SVDRP port.
|
||||
.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
|
||||
Call \fIcmd\fR before and after a recording.
|
||||
.TP
|
||||
@ -104,6 +121,9 @@ Successful program execution.
|
||||
.B 1
|
||||
An error has been detected which requires the DVB driver and \fBvdr\fR
|
||||
to be re-loaded.
|
||||
.TP
|
||||
.B 2
|
||||
An non-recoverable error has been detected, \fBvdr\fR has given up.
|
||||
.SH FILES
|
||||
.TP
|
||||
.I channels.conf
|
||||
|
131
vdr.c
131
vdr.c
@ -22,7 +22,7 @@
|
||||
*
|
||||
* 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>
|
||||
@ -36,6 +36,7 @@
|
||||
#include "i18n.h"
|
||||
#include "interface.h"
|
||||
#include "menu.h"
|
||||
#include "plugin.h"
|
||||
#include "recording.h"
|
||||
#include "tools.h"
|
||||
#include "videodir.h"
|
||||
@ -79,14 +80,18 @@ int main(int argc, char *argv[])
|
||||
|
||||
#define DEFAULTSVDRPPORT 2001
|
||||
#define DEFAULTWATCHDOG 0 // seconds
|
||||
#define DEFAULTPLUGINDIR "./PLUGINS/lib"
|
||||
|
||||
int SVDRPport = DEFAULTSVDRPPORT;
|
||||
const char *ConfigDirectory = NULL;
|
||||
bool DisplayHelp = false;
|
||||
bool DisplayVersion = false;
|
||||
bool DaemonMode = false;
|
||||
bool MuteAudio = false;
|
||||
int WatchdogTimeout = DEFAULTWATCHDOG;
|
||||
const char *Terminal = NULL;
|
||||
const char *Shutdown = NULL;
|
||||
cPluginManager PluginManager(DEFAULTPLUGINDIR);
|
||||
|
||||
static struct option long_options[] = {
|
||||
{ "audio", required_argument, NULL, 'a' },
|
||||
@ -95,8 +100,10 @@ int main(int argc, char *argv[])
|
||||
{ "device", required_argument, NULL, 'D' },
|
||||
{ "epgfile", required_argument, NULL, 'E' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "lib", required_argument, NULL, 'L' },
|
||||
{ "log", required_argument, NULL, 'l' },
|
||||
{ "mute", no_argument, NULL, 'm' },
|
||||
{ "plugin", required_argument, NULL, 'P' },
|
||||
{ "port", required_argument, NULL, 'p' },
|
||||
{ "record", required_argument, NULL, 'r' },
|
||||
{ "shutdown", required_argument, NULL, 's' },
|
||||
@ -108,8 +115,7 @@ int main(int argc, char *argv[])
|
||||
};
|
||||
|
||||
int c;
|
||||
int option_index = 0;
|
||||
while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:mp:r:s:t:v:Vw:", long_options, &option_index)) != -1) {
|
||||
while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:L:mp:P:r:s:t:v:Vw:", long_options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'a': cDvbApi::SetAudioCommand(optarg);
|
||||
break;
|
||||
@ -128,40 +134,7 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
case 'E': cSIProcessor::SetEpgDataFileName(*optarg != '-' ? optarg : NULL);
|
||||
break;
|
||||
case 'h': printf("Usage: vdr [OPTION]\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"
|
||||
" -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;
|
||||
case 'h': DisplayHelp = true;
|
||||
break;
|
||||
case 'l': if (isnumber(optarg)) {
|
||||
int l = atoi(optarg);
|
||||
@ -173,6 +146,8 @@ int main(int argc, char *argv[])
|
||||
fprintf(stderr, "vdr: invalid log level: %s\n", optarg);
|
||||
return 2;
|
||||
break;
|
||||
case 'L': PluginManager.SetDirectory(optarg);
|
||||
break;
|
||||
case 'm': MuteAudio = true;
|
||||
break;
|
||||
case 'p': if (isnumber(optarg))
|
||||
@ -182,14 +157,15 @@ int main(int argc, char *argv[])
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
case 'P': PluginManager.AddPlugin(optarg);
|
||||
break;
|
||||
case 'r': cRecordingUserCommand::SetCommand(optarg);
|
||||
break;
|
||||
case 's': Shutdown = optarg;
|
||||
break;
|
||||
case 't': Terminal = optarg;
|
||||
break;
|
||||
case 'V': printf("vdr, version %s\n", VDRVERSION);
|
||||
return 0;
|
||||
case 'V': DisplayVersion = true;
|
||||
break;
|
||||
case 'v': VideoDirectory = optarg;
|
||||
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:
|
||||
|
||||
if (SysLogLevel > 0)
|
||||
@ -250,6 +291,11 @@ int main(int argc, char *argv[])
|
||||
|
||||
isyslog(LOG_INFO, "VDR version %s started", VDRVERSION);
|
||||
|
||||
// Load plugins:
|
||||
|
||||
if (!PluginManager.LoadPlugins(true))
|
||||
return 2;
|
||||
|
||||
// Configuration data:
|
||||
|
||||
if (!ConfigDirectory)
|
||||
@ -276,6 +322,12 @@ int main(int argc, char *argv[])
|
||||
|
||||
cSIProcessor::Read();
|
||||
|
||||
// Start plugins:
|
||||
|
||||
PluginManager.StartPlugins();
|
||||
|
||||
// Channel:
|
||||
|
||||
Channels.SwitchTo(Setup.CurrentChannel);
|
||||
if (MuteAudio)
|
||||
cDvbApi::PrimaryDvbApi->ToggleMute();
|
||||
@ -529,6 +581,7 @@ int main(int argc, char *argv[])
|
||||
Setup.CurrentChannel = cDvbApi::CurrentChannel();
|
||||
Setup.CurrentVolume = cDvbApi::CurrentVolume();
|
||||
Setup.Save();
|
||||
PluginManager.Shutdown(true);
|
||||
cVideoCutter::Stop();
|
||||
delete Menu;
|
||||
delete ReplayControl;
|
||||
|
Loading…
Reference in New Issue
Block a user