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
|
Video Disk Recorder Revision History
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
2000-02-19: Version 0.01 (Initial revision).
|
2000-02-19: Version 0.01
|
||||||
|
|
||||||
|
- Initial revision.
|
||||||
|
|
||||||
2000-03-11: Version 0.02
|
2000-03-11: Version 0.02
|
||||||
|
|
||||||
@ -1242,3 +1244,13 @@ Video Disk Recorder Revision History
|
|||||||
'channels.conf.cable' whenever a new version is sent to me.
|
'channels.conf.cable' whenever a new version is sent to me.
|
||||||
- Fixed skipping forward in time shift mode near the end of the recording (thanks
|
- Fixed skipping forward in time shift mode near the end of the recording (thanks
|
||||||
to Andreas Böttger for reporting this one).
|
to Andreas Böttger for reporting this one).
|
||||||
|
|
||||||
|
2002-05-09: Version 1.1.0
|
||||||
|
|
||||||
|
- Begin of the 1.1 development branch. THIS IS NOT A STABLE VERSION!
|
||||||
|
The current stable version for every day use is still the 1.0 branch.
|
||||||
|
- First step towards a universal plugin interface. See the file PLUGINS.html
|
||||||
|
for a detailed description. The man page vdr(1) describes the new options '-L'
|
||||||
|
and '-P' used to load plugins. This first step implements the complete "outer"
|
||||||
|
shell for plugins. The "inner" access to VDR data structures will follow.
|
||||||
|
- The VDR version number is now displayed in the title line of the "Setup" menu.
|
||||||
|
28
Makefile
28
Makefile
@ -4,7 +4,7 @@
|
|||||||
# See the main source file 'vdr.c' for copyright information and
|
# See the main source file 'vdr.c' for copyright information and
|
||||||
# how to reach the author.
|
# how to reach the author.
|
||||||
#
|
#
|
||||||
# $Id: Makefile 1.33 2002/04/01 12:50:48 kls Exp $
|
# $Id: Makefile 1.34 2002/05/09 09:35:05 kls Exp $
|
||||||
|
|
||||||
.DELETE_ON_ERROR:
|
.DELETE_ON_ERROR:
|
||||||
|
|
||||||
@ -13,15 +13,17 @@ DTVDIR = ./libdtv
|
|||||||
MANDIR = /usr/local/man
|
MANDIR = /usr/local/man
|
||||||
BINDIR = /usr/local/bin
|
BINDIR = /usr/local/bin
|
||||||
|
|
||||||
|
PLUGINDIR= ./PLUGINS
|
||||||
|
|
||||||
VIDEODIR = /video
|
VIDEODIR = /video
|
||||||
|
|
||||||
INCLUDES = -I$(DVBDIR)/ost/include
|
INCLUDES = -I$(DVBDIR)/ost/include
|
||||||
|
|
||||||
DTVLIB = $(DTVDIR)/libdtv.a
|
DTVLIB = $(DTVDIR)/libdtv.a
|
||||||
|
|
||||||
OBJS = config.o dvbapi.o dvbosd.o eit.o font.o i18n.o interface.o menu.o osd.o\
|
OBJS = config.o dvbapi.o dvbosd.o eit.o font.o i18n.o interface.o menu.o\
|
||||||
recording.o remote.o remux.o ringbuffer.o svdrp.o thread.o tools.o vdr.o\
|
menuitems.o osd.o plugin.o recording.o remote.o remux.o ringbuffer.o\
|
||||||
videodir.o
|
svdrp.o thread.o tools.o vdr.o videodir.o
|
||||||
|
|
||||||
OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
|
OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1
|
||||||
FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1
|
FIXFONT = -adobe-courier-bold-r-normal--25-*-100-100-m-*-iso8859-1
|
||||||
@ -69,7 +71,7 @@ include $(DEPFILE)
|
|||||||
# The main program:
|
# The main program:
|
||||||
|
|
||||||
vdr: $(OBJS) $(DTVLIB)
|
vdr: $(OBJS) $(DTVLIB)
|
||||||
g++ -g -O2 $(OBJS) $(NCURSESLIB) -ljpeg -lpthread $(LIBDIRS) $(DTVLIB) -o vdr
|
g++ -g -O2 -rdynamic $(OBJS) $(NCURSESLIB) -ljpeg -lpthread -ldl $(LIBDIRS) $(DTVLIB) -o vdr
|
||||||
|
|
||||||
# The font files:
|
# The font files:
|
||||||
|
|
||||||
@ -88,6 +90,21 @@ genfontfile: genfontfile.c
|
|||||||
$(DTVLIB) $(DTVDIR)/libdtv.h:
|
$(DTVLIB) $(DTVDIR)/libdtv.h:
|
||||||
make -C $(DTVDIR) all
|
make -C $(DTVDIR) all
|
||||||
|
|
||||||
|
# The 'include' directory (for plugins):
|
||||||
|
|
||||||
|
include-dir:
|
||||||
|
@mkdir -p include/vdr
|
||||||
|
@(cd include/vdr; for i in ../../*.h; do ln -fs $$i .; done)
|
||||||
|
|
||||||
|
# Plugins:
|
||||||
|
|
||||||
|
plugins: include-dir
|
||||||
|
@for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do make -C "$(PLUGINDIR)/src/$$i" all; done
|
||||||
|
|
||||||
|
plugins-clean:
|
||||||
|
@for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do make -C "$(PLUGINDIR)/src/$$i" clean; done
|
||||||
|
@-rm -f $(PLUGINDIR)/lib/*
|
||||||
|
|
||||||
# Install the files:
|
# Install the files:
|
||||||
|
|
||||||
install:
|
install:
|
||||||
@ -104,6 +121,7 @@ install:
|
|||||||
clean:
|
clean:
|
||||||
make -C $(DTVDIR) clean
|
make -C $(DTVDIR) clean
|
||||||
-rm -f $(OBJS) $(DEPFILE) vdr genfontfile genfontfile.o core* *~
|
-rm -f $(OBJS) $(DEPFILE) vdr genfontfile genfontfile.o core* *~
|
||||||
|
-rm -rf include
|
||||||
fontclean:
|
fontclean:
|
||||||
-rm -f fontfix.c fontosd.c
|
-rm -f fontfix.c fontosd.c
|
||||||
CLEAN: clean fontclean
|
CLEAN: clean fontclean
|
||||||
|
620
PLUGINS.html
Normal file
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[];
|
371
config.c
371
config.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: config.c 1.98 2002/04/26 12:30:00 kls Exp $
|
* $Id: config.c 1.99 2002/05/05 12:00:00 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@ -13,6 +13,7 @@
|
|||||||
#include "dvbapi.h"
|
#include "dvbapi.h"
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
#include "interface.h"
|
#include "interface.h"
|
||||||
|
#include "plugin.h"
|
||||||
|
|
||||||
// IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
|
// IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
|
||||||
// format characters in order to allow any number of blanks after a numeric
|
// format characters in order to allow any number of blanks after a numeric
|
||||||
@ -387,11 +388,12 @@ cTimer& cTimer::operator= (const cTimer &Timer)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cTimer::operator< (const cTimer &Timer)
|
bool cTimer::operator< (const cListObject &ListObject)
|
||||||
{
|
{
|
||||||
|
cTimer *ti = (cTimer *)&ListObject;
|
||||||
time_t t1 = StartTime();
|
time_t t1 = StartTime();
|
||||||
time_t t2 = (*(cTimer *)&Timer).StartTime();
|
time_t t2 = ti->StartTime();
|
||||||
return t1 < t2 || (t1 == t2 && priority > Timer.priority);
|
return t1 < t2 || (t1 == t2 && priority > ti->priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *cTimer::ToText(cTimer *Timer)
|
const char *cTimer::ToText(cTimer *Timer)
|
||||||
@ -917,12 +919,76 @@ const cCaDefinition *cCaDefinitions::Get(int Number)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -- cSetupLine -------------------------------------------------------------
|
||||||
|
|
||||||
|
cSetupLine::cSetupLine(void)
|
||||||
|
{
|
||||||
|
plugin = name = value = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cSetupLine::cSetupLine(const char *Name, const char *Value, const char *Plugin)
|
||||||
|
{
|
||||||
|
name = strdup(Name);
|
||||||
|
value = strdup(Value);
|
||||||
|
plugin = Plugin ? strdup(Plugin) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cSetupLine::~cSetupLine()
|
||||||
|
{
|
||||||
|
delete plugin;
|
||||||
|
delete name;
|
||||||
|
delete value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSetupLine::operator< (const cListObject &ListObject)
|
||||||
|
{
|
||||||
|
const cSetupLine *sl = (cSetupLine *)&ListObject;
|
||||||
|
if (!plugin && !sl->plugin)
|
||||||
|
return strcasecmp(name, sl->name) < 0;
|
||||||
|
if (!plugin)
|
||||||
|
return true;
|
||||||
|
if (!sl->plugin)
|
||||||
|
return false;
|
||||||
|
int result = strcasecmp(plugin, sl->plugin);
|
||||||
|
if (result == 0)
|
||||||
|
result = strcasecmp(name, sl->name);
|
||||||
|
return result < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSetupLine::Parse(char *s)
|
||||||
|
{
|
||||||
|
char *p = strchr(s, '=');
|
||||||
|
if (p) {
|
||||||
|
*p = 0;
|
||||||
|
char *Name = compactspace(s);
|
||||||
|
char *Value = compactspace(p + 1);
|
||||||
|
if (*Name && *Value) {
|
||||||
|
p = strchr(Name, '.');
|
||||||
|
if (p) {
|
||||||
|
*p = 0;
|
||||||
|
char *Plugin = compactspace(Name);
|
||||||
|
Name = compactspace(p + 1);
|
||||||
|
if (!(*Plugin && *Name))
|
||||||
|
return false;
|
||||||
|
plugin = strdup(Plugin);
|
||||||
|
}
|
||||||
|
name = strdup(Name);
|
||||||
|
value = strdup(Value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSetupLine::Save(FILE *f)
|
||||||
|
{
|
||||||
|
return fprintf(f, "%s%s%s = %s\n", plugin ? plugin : "", plugin ? "." : "", name, value) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
// -- cSetup -----------------------------------------------------------------
|
// -- cSetup -----------------------------------------------------------------
|
||||||
|
|
||||||
cSetup Setup;
|
cSetup Setup;
|
||||||
|
|
||||||
char *cSetup::fileName = NULL;
|
|
||||||
|
|
||||||
cSetup::cSetup(void)
|
cSetup::cSetup(void)
|
||||||
{
|
{
|
||||||
OSDLanguage = 0;
|
OSDLanguage = 0;
|
||||||
@ -966,19 +1032,77 @@ cSetup::cSetup(void)
|
|||||||
CurrentVolume = MAXVOLUME;
|
CurrentVolume = MAXVOLUME;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cSetup::PrintCaCaps(FILE *f, const char *Name)
|
cSetup& cSetup::operator= (const cSetup &s)
|
||||||
|
{
|
||||||
|
memcpy(&__BeginData__, &s.__BeginData__, (char *)&s.__EndData__ - (char *)&s.__BeginData__);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
cSetupLine *cSetup::Get(const char *Name, const char *Plugin)
|
||||||
|
{
|
||||||
|
for (cSetupLine *l = First(); l; l = Next(l)) {
|
||||||
|
if ((l->Plugin() == NULL) == (Plugin == NULL)) {
|
||||||
|
if ((!Plugin || strcasecmp(l->Plugin(), Plugin) == 0) && strcasecmp(l->Name(), Name) == 0)
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSetup::Store(const char *Name, const char *Value, const char *Plugin)
|
||||||
|
{
|
||||||
|
if (Name && *Name) {
|
||||||
|
cSetupLine *l = Get(Name, Plugin);
|
||||||
|
if (l)
|
||||||
|
Del(l);
|
||||||
|
if (Value)
|
||||||
|
Add(new cSetupLine(Name, Value, Plugin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSetup::Store(const char *Name, int Value, const char *Plugin)
|
||||||
|
{
|
||||||
|
char *buffer = NULL;
|
||||||
|
asprintf(&buffer, "%d", Value);
|
||||||
|
Store(Name, buffer, Plugin);
|
||||||
|
delete buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cSetup::Load(const char *FileName)
|
||||||
|
{
|
||||||
|
if (cConfig<cSetupLine>::Load(FileName, true)) {
|
||||||
|
bool result = true;
|
||||||
|
for (cSetupLine *l = First(); l; l = Next(l)) {
|
||||||
|
if (l->Plugin()) {
|
||||||
|
cPlugin *p = cPluginManager::GetPlugin(l->Plugin());
|
||||||
|
if (p && !p->SetupParse(l->Name(), l->Value()))
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!Parse(l->Name(), l->Value()))
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cSetup::StoreCaCaps(const char *Name)
|
||||||
{
|
{
|
||||||
for (int d = 0; d < MAXDVBAPI; d++) {
|
for (int d = 0; d < MAXDVBAPI; d++) {
|
||||||
int written = 0;
|
char buffer[MAXPARSEBUFFER];
|
||||||
|
char *q = buffer;
|
||||||
|
*buffer = 0;
|
||||||
for (int i = 0; i < MAXCACAPS; i++) {
|
for (int i = 0; i < MAXCACAPS; i++) {
|
||||||
if (CaCaps[d][i]) {
|
if (CaCaps[d][i]) {
|
||||||
if (!written++)
|
if (!*buffer)
|
||||||
fprintf(f, "CaCaps = %d", d + 1);
|
q += snprintf(buffer, sizeof(buffer), "%d", d + 1);
|
||||||
fprintf(f, " %d", CaCaps[d][i]);
|
q += snprintf(q, sizeof(buffer) - (q - buffer), " %d", CaCaps[d][i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (written)
|
if (*buffer)
|
||||||
fprintf(f, "\n");
|
Store(Name, buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1005,144 +1129,99 @@ bool cSetup::ParseCaCaps(const char *Value)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cSetup::Parse(char *s)
|
bool cSetup::Parse(const char *Name, const char *Value)
|
||||||
{
|
{
|
||||||
char *p = strchr(s, '=');
|
if (!strcasecmp(Name, "OSDLanguage")) OSDLanguage = atoi(Value);
|
||||||
if (p) {
|
else if (!strcasecmp(Name, "PrimaryDVB")) PrimaryDVB = atoi(Value);
|
||||||
*p = 0;
|
else if (!strcasecmp(Name, "ShowInfoOnChSwitch")) ShowInfoOnChSwitch = atoi(Value);
|
||||||
char *Name = compactspace(s);
|
else if (!strcasecmp(Name, "MenuScrollPage")) MenuScrollPage = atoi(Value);
|
||||||
char *Value = compactspace(p + 1);
|
else if (!strcasecmp(Name, "MarkInstantRecord")) MarkInstantRecord = atoi(Value);
|
||||||
if (*Name && *Value) {
|
else if (!strcasecmp(Name, "NameInstantRecord")) strn0cpy(NameInstantRecord, Value, MaxFileName);
|
||||||
if (!strcasecmp(Name, "OSDLanguage")) OSDLanguage = atoi(Value);
|
else if (!strcasecmp(Name, "InstantRecordTime")) InstantRecordTime = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "PrimaryDVB")) PrimaryDVB = atoi(Value);
|
else if (!strcasecmp(Name, "LnbSLOF")) LnbSLOF = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "ShowInfoOnChSwitch")) ShowInfoOnChSwitch = atoi(Value);
|
else if (!strcasecmp(Name, "LnbFrequLo")) LnbFrequLo = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "MenuScrollPage")) MenuScrollPage = atoi(Value);
|
else if (!strcasecmp(Name, "LnbFrequHi")) LnbFrequHi = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "MarkInstantRecord")) MarkInstantRecord = atoi(Value);
|
else if (!strcasecmp(Name, "DiSEqC")) DiSEqC = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "NameInstantRecord")) strn0cpy(NameInstantRecord, Value, MaxFileName);
|
else if (!strcasecmp(Name, "SetSystemTime")) SetSystemTime = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "InstantRecordTime")) InstantRecordTime = atoi(Value);
|
else if (!strcasecmp(Name, "TimeTransponder")) TimeTransponder = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "LnbSLOF")) LnbSLOF = atoi(Value);
|
else if (!strcasecmp(Name, "MarginStart")) MarginStart = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "LnbFrequLo")) LnbFrequLo = atoi(Value);
|
else if (!strcasecmp(Name, "MarginStop")) MarginStop = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "LnbFrequHi")) LnbFrequHi = atoi(Value);
|
else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "DiSEqC")) DiSEqC = atoi(Value);
|
else if (!strcasecmp(Name, "EPGBugfixLevel")) EPGBugfixLevel = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "SetSystemTime")) SetSystemTime = atoi(Value);
|
else if (!strcasecmp(Name, "SVDRPTimeout")) SVDRPTimeout = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "TimeTransponder")) TimeTransponder = atoi(Value);
|
else if (!strcasecmp(Name, "SortTimers")) SortTimers = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "MarginStart")) MarginStart = atoi(Value);
|
else if (!strcasecmp(Name, "PrimaryLimit")) PrimaryLimit = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "MarginStop")) MarginStop = atoi(Value);
|
else if (!strcasecmp(Name, "DefaultPriority")) DefaultPriority = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "EPGScanTimeout")) EPGScanTimeout = atoi(Value);
|
else if (!strcasecmp(Name, "DefaultLifetime")) DefaultLifetime = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "EPGBugfixLevel")) EPGBugfixLevel = atoi(Value);
|
else if (!strcasecmp(Name, "UseSubtitle")) UseSubtitle = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "SVDRPTimeout")) SVDRPTimeout = atoi(Value);
|
else if (!strcasecmp(Name, "RecordingDirs")) RecordingDirs = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "SortTimers")) SortTimers = atoi(Value);
|
else if (!strcasecmp(Name, "VideoFormat")) VideoFormat = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "PrimaryLimit")) PrimaryLimit = atoi(Value);
|
else if (!strcasecmp(Name, "RecordDolbyDigital")) RecordDolbyDigital = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "DefaultPriority")) DefaultPriority = atoi(Value);
|
else if (!strcasecmp(Name, "ChannelInfoPos")) ChannelInfoPos = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "DefaultLifetime")) DefaultLifetime = atoi(Value);
|
else if (!strcasecmp(Name, "OSDwidth")) OSDwidth = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "UseSubtitle")) UseSubtitle = atoi(Value);
|
else if (!strcasecmp(Name, "OSDheight")) OSDheight = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "RecordingDirs")) RecordingDirs = atoi(Value);
|
else if (!strcasecmp(Name, "OSDMessageTime")) OSDMessageTime = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "VideoFormat")) VideoFormat = atoi(Value);
|
else if (!strcasecmp(Name, "MaxVideoFileSize")) MaxVideoFileSize = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "RecordDolbyDigital")) RecordDolbyDigital = atoi(Value);
|
else if (!strcasecmp(Name, "SplitEditedFiles")) SplitEditedFiles = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "ChannelInfoPos")) ChannelInfoPos = atoi(Value);
|
else if (!strcasecmp(Name, "MinEventTimeout")) MinEventTimeout = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "OSDwidth")) OSDwidth = atoi(Value);
|
else if (!strcasecmp(Name, "MinUserInactivity")) MinUserInactivity = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "OSDheight")) OSDheight = atoi(Value);
|
else if (!strcasecmp(Name, "MultiSpeedMode")) MultiSpeedMode = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "OSDMessageTime")) OSDMessageTime = atoi(Value);
|
else if (!strcasecmp(Name, "ShowReplayMode")) ShowReplayMode = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "MaxVideoFileSize")) MaxVideoFileSize = atoi(Value);
|
else if (!strcasecmp(Name, "CaCaps")) return ParseCaCaps(Value);
|
||||||
else if (!strcasecmp(Name, "SplitEditedFiles")) SplitEditedFiles = atoi(Value);
|
else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "MinEventTimeout")) MinEventTimeout = atoi(Value);
|
else if (!strcasecmp(Name, "CurrentVolume")) CurrentVolume = atoi(Value);
|
||||||
else if (!strcasecmp(Name, "MinUserInactivity")) MinUserInactivity = atoi(Value);
|
|
||||||
else if (!strcasecmp(Name, "MultiSpeedMode")) MultiSpeedMode = atoi(Value);
|
|
||||||
else if (!strcasecmp(Name, "ShowReplayMode")) ShowReplayMode = atoi(Value);
|
|
||||||
else if (!strcasecmp(Name, "CaCaps")) return ParseCaCaps(Value);
|
|
||||||
else if (!strcasecmp(Name, "CurrentChannel")) CurrentChannel = atoi(Value);
|
|
||||||
else if (!strcasecmp(Name, "CurrentVolume")) CurrentVolume = atoi(Value);
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cSetup::Load(const char *FileName)
|
|
||||||
{
|
|
||||||
isyslog(LOG_INFO, "loading %s", FileName);
|
|
||||||
delete fileName;
|
|
||||||
fileName = strdup(FileName);
|
|
||||||
FILE *f = fopen(fileName, "r");
|
|
||||||
if (f) {
|
|
||||||
int line = 0;
|
|
||||||
char buffer[MAXPARSEBUFFER];
|
|
||||||
bool result = true;
|
|
||||||
while (fgets(buffer, sizeof(buffer), f) > 0) {
|
|
||||||
line++;
|
|
||||||
stripspace(buffer);
|
|
||||||
if (!isempty(buffer)) {
|
|
||||||
if (*buffer != '#' && !Parse(buffer)) {
|
|
||||||
esyslog(LOG_ERR, "error in %s, line %d\n", fileName, line);
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(f);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
LOG_ERROR_STR(FileName);
|
return false;
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cSetup::Save(const char *FileName)
|
bool cSetup::Save(void)
|
||||||
{
|
{
|
||||||
if (!FileName)
|
Store("OSDLanguage", OSDLanguage);
|
||||||
FileName = fileName;
|
Store("PrimaryDVB", PrimaryDVB);
|
||||||
if (FileName) {
|
Store("ShowInfoOnChSwitch", ShowInfoOnChSwitch);
|
||||||
cSafeFile f(FileName);
|
Store("MenuScrollPage", MenuScrollPage);
|
||||||
if (f.Open()) {
|
Store("MarkInstantRecord", MarkInstantRecord);
|
||||||
fprintf(f, "# VDR Setup\n");
|
Store("NameInstantRecord", NameInstantRecord);
|
||||||
fprintf(f, "OSDLanguage = %d\n", OSDLanguage);
|
Store("InstantRecordTime", InstantRecordTime);
|
||||||
fprintf(f, "PrimaryDVB = %d\n", PrimaryDVB);
|
Store("LnbSLOF", LnbSLOF);
|
||||||
fprintf(f, "ShowInfoOnChSwitch = %d\n", ShowInfoOnChSwitch);
|
Store("LnbFrequLo", LnbFrequLo);
|
||||||
fprintf(f, "MenuScrollPage = %d\n", MenuScrollPage);
|
Store("LnbFrequHi", LnbFrequHi);
|
||||||
fprintf(f, "MarkInstantRecord = %d\n", MarkInstantRecord);
|
Store("DiSEqC", DiSEqC);
|
||||||
fprintf(f, "NameInstantRecord = %s\n", NameInstantRecord);
|
Store("SetSystemTime", SetSystemTime);
|
||||||
fprintf(f, "InstantRecordTime = %d\n", InstantRecordTime);
|
Store("TimeTransponder", TimeTransponder);
|
||||||
fprintf(f, "LnbSLOF = %d\n", LnbSLOF);
|
Store("MarginStart", MarginStart);
|
||||||
fprintf(f, "LnbFrequLo = %d\n", LnbFrequLo);
|
Store("MarginStop", MarginStop);
|
||||||
fprintf(f, "LnbFrequHi = %d\n", LnbFrequHi);
|
Store("EPGScanTimeout", EPGScanTimeout);
|
||||||
fprintf(f, "DiSEqC = %d\n", DiSEqC);
|
Store("EPGBugfixLevel", EPGBugfixLevel);
|
||||||
fprintf(f, "SetSystemTime = %d\n", SetSystemTime);
|
Store("SVDRPTimeout", SVDRPTimeout);
|
||||||
fprintf(f, "TimeTransponder = %d\n", TimeTransponder);
|
Store("SortTimers", SortTimers);
|
||||||
fprintf(f, "MarginStart = %d\n", MarginStart);
|
Store("PrimaryLimit", PrimaryLimit);
|
||||||
fprintf(f, "MarginStop = %d\n", MarginStop);
|
Store("DefaultPriority", DefaultPriority);
|
||||||
fprintf(f, "EPGScanTimeout = %d\n", EPGScanTimeout);
|
Store("DefaultLifetime", DefaultLifetime);
|
||||||
fprintf(f, "EPGBugfixLevel = %d\n", EPGBugfixLevel);
|
Store("UseSubtitle", UseSubtitle);
|
||||||
fprintf(f, "SVDRPTimeout = %d\n", SVDRPTimeout);
|
Store("RecordingDirs", RecordingDirs);
|
||||||
fprintf(f, "SortTimers = %d\n", SortTimers);
|
Store("VideoFormat", VideoFormat);
|
||||||
fprintf(f, "PrimaryLimit = %d\n", PrimaryLimit);
|
Store("RecordDolbyDigital", RecordDolbyDigital);
|
||||||
fprintf(f, "DefaultPriority = %d\n", DefaultPriority);
|
Store("ChannelInfoPos", ChannelInfoPos);
|
||||||
fprintf(f, "DefaultLifetime = %d\n", DefaultLifetime);
|
Store("OSDwidth", OSDwidth);
|
||||||
fprintf(f, "UseSubtitle = %d\n", UseSubtitle);
|
Store("OSDheight", OSDheight);
|
||||||
fprintf(f, "RecordingDirs = %d\n", RecordingDirs);
|
Store("OSDMessageTime", OSDMessageTime);
|
||||||
fprintf(f, "VideoFormat = %d\n", VideoFormat);
|
Store("MaxVideoFileSize", MaxVideoFileSize);
|
||||||
fprintf(f, "RecordDolbyDigital = %d\n", RecordDolbyDigital);
|
Store("SplitEditedFiles", SplitEditedFiles);
|
||||||
fprintf(f, "ChannelInfoPos = %d\n", ChannelInfoPos);
|
Store("MinEventTimeout", MinEventTimeout);
|
||||||
fprintf(f, "OSDwidth = %d\n", OSDwidth);
|
Store("MinUserInactivity", MinUserInactivity);
|
||||||
fprintf(f, "OSDheight = %d\n", OSDheight);
|
Store("MultiSpeedMode", MultiSpeedMode);
|
||||||
fprintf(f, "OSDMessageTime = %d\n", OSDMessageTime);
|
Store("ShowReplayMode", ShowReplayMode);
|
||||||
fprintf(f, "MaxVideoFileSize = %d\n", MaxVideoFileSize);
|
StoreCaCaps("CaCaps");
|
||||||
fprintf(f, "SplitEditedFiles = %d\n", SplitEditedFiles);
|
Store("CurrentChannel", CurrentChannel);
|
||||||
fprintf(f, "MinEventTimeout = %d\n", MinEventTimeout);
|
Store("CurrentVolume", CurrentVolume);
|
||||||
fprintf(f, "MinUserInactivity = %d\n", MinUserInactivity);
|
|
||||||
fprintf(f, "MultiSpeedMode = %d\n", MultiSpeedMode);
|
Sort();
|
||||||
fprintf(f, "ShowReplayMode = %d\n", ShowReplayMode);
|
|
||||||
PrintCaCaps(f, "CaCaps");
|
if (cConfig<cSetupLine>::Save()) {
|
||||||
fprintf(f, "CurrentChannel = %d\n", CurrentChannel);
|
isyslog(LOG_INFO, "saved setup to %s", FileName());
|
||||||
fprintf(f, "CurrentVolume = %d\n", CurrentVolume);
|
return true;
|
||||||
if (f.Close()) {
|
|
||||||
isyslog(LOG_INFO, "saved setup to %s", FileName);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
esyslog(LOG_ERR, "attempt to save setup without file name");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
42
config.h
42
config.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: config.h 1.112 2002/04/26 13:56:30 kls Exp $
|
* $Id: config.h 1.113 2002/05/05 12:00:00 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __CONFIG_H
|
#ifndef __CONFIG_H
|
||||||
@ -19,7 +19,7 @@
|
|||||||
#include "eit.h"
|
#include "eit.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
#define VDRVERSION "1.0.2"
|
#define VDRVERSION "1.1.0"
|
||||||
|
|
||||||
#define MAXPRIORITY 99
|
#define MAXPRIORITY 99
|
||||||
#define MAXLIFETIME 99
|
#define MAXLIFETIME 99
|
||||||
@ -149,7 +149,7 @@ public:
|
|||||||
cTimer(const cEventInfo *EventInfo);
|
cTimer(const cEventInfo *EventInfo);
|
||||||
virtual ~cTimer();
|
virtual ~cTimer();
|
||||||
cTimer& operator= (const cTimer &Timer);
|
cTimer& operator= (const cTimer &Timer);
|
||||||
bool operator< (const cTimer &Timer);
|
virtual bool operator< (const cListObject &ListObject);
|
||||||
const char *ToText(void);
|
const char *ToText(void);
|
||||||
bool Parse(const char *s);
|
bool Parse(const char *s);
|
||||||
bool Save(FILE *f);
|
bool Save(FILE *f);
|
||||||
@ -220,7 +220,8 @@ private:
|
|||||||
public:
|
public:
|
||||||
cConfig(void) { fileName = NULL; }
|
cConfig(void) { fileName = NULL; }
|
||||||
virtual ~cConfig() { delete fileName; }
|
virtual ~cConfig() { delete fileName; }
|
||||||
virtual bool Load(const char *FileName, bool AllowComments = false)
|
const char *FileName(void) { return fileName; }
|
||||||
|
bool Load(const char *FileName, bool AllowComments = false)
|
||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
fileName = strdup(FileName);
|
fileName = strdup(FileName);
|
||||||
@ -323,14 +324,35 @@ extern cCommands Commands;
|
|||||||
extern cSVDRPhosts SVDRPhosts;
|
extern cSVDRPhosts SVDRPhosts;
|
||||||
extern cCaDefinitions CaDefinitions;
|
extern cCaDefinitions CaDefinitions;
|
||||||
|
|
||||||
class cSetup {
|
class cSetupLine : public cListObject {
|
||||||
private:
|
private:
|
||||||
static char *fileName;
|
char *plugin;
|
||||||
void PrintCaCaps(FILE *f, const char *Name);
|
char *name;
|
||||||
bool ParseCaCaps(const char *Value);
|
char *value;
|
||||||
|
public:
|
||||||
|
cSetupLine(void);
|
||||||
|
cSetupLine(const char *Name, const char *Value, const char *Plugin = NULL);
|
||||||
|
virtual ~cSetupLine();
|
||||||
|
virtual bool operator< (const cListObject &ListObject);
|
||||||
|
const char *Plugin(void) { return plugin; }
|
||||||
|
const char *Name(void) { return name; }
|
||||||
|
const char *Value(void) { return value; }
|
||||||
bool Parse(char *s);
|
bool Parse(char *s);
|
||||||
|
bool Save(FILE *f);
|
||||||
|
};
|
||||||
|
|
||||||
|
class cSetup : public cConfig<cSetupLine> {
|
||||||
|
friend class cPlugin; // needs to be able to call Store()
|
||||||
|
private:
|
||||||
|
void StoreCaCaps(const char *Name);
|
||||||
|
bool ParseCaCaps(const char *Value);
|
||||||
|
bool Parse(const char *Name, const char *Value);
|
||||||
|
cSetupLine *Get(const char *Name, const char *Plugin = NULL);
|
||||||
|
void Store(const char *Name, const char *Value, const char *Plugin = NULL);
|
||||||
|
void Store(const char *Name, int Value, const char *Plugin = NULL);
|
||||||
public:
|
public:
|
||||||
// Also adjust cMenuSetup (menu.c) when adding parameters here!
|
// Also adjust cMenuSetup (menu.c) when adding parameters here!
|
||||||
|
int __BeginData__;
|
||||||
int OSDLanguage;
|
int OSDLanguage;
|
||||||
int PrimaryDVB;
|
int PrimaryDVB;
|
||||||
int ShowInfoOnChSwitch;
|
int ShowInfoOnChSwitch;
|
||||||
@ -366,9 +388,11 @@ public:
|
|||||||
int CaCaps[MAXDVBAPI][MAXCACAPS];
|
int CaCaps[MAXDVBAPI][MAXCACAPS];
|
||||||
int CurrentChannel;
|
int CurrentChannel;
|
||||||
int CurrentVolume;
|
int CurrentVolume;
|
||||||
|
int __EndData__;
|
||||||
cSetup(void);
|
cSetup(void);
|
||||||
|
cSetup& operator= (const cSetup &s);
|
||||||
bool Load(const char *FileName);
|
bool Load(const char *FileName);
|
||||||
bool Save(const char *FileName = NULL);
|
bool Save(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern cSetup Setup;
|
extern cSetup Setup;
|
||||||
|
143
i18n.c
143
i18n.c
@ -4,27 +4,29 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: i18n.c 1.86 2002/05/04 14:44:32 kls Exp $
|
* $Id: i18n.c 1.87 2002/05/09 13:40:51 kls Exp $
|
||||||
*
|
*
|
||||||
* Slovenian translations provided by Miha Setina <mihasetina@softhome.net> and Matjaz Thaler <matjaz.thaler@guest.arnes.si>
|
* Translations provided by:
|
||||||
* Italian translations provided by Alberto Carraro <bertocar@tin.it>
|
*
|
||||||
* Dutch translations provided by Arnold Niessen <niessen@iae.nl> <arnold.niessen@philips.com>
|
* Slovenian Miha Setina <mihasetina@softhome.net> and Matjaz Thaler <matjaz.thaler@guest.arnes.si>
|
||||||
* Portuguese translations provided by Paulo Lopes <pmml@netvita.pt>
|
* Italian Alberto Carraro <bertocar@tin.it>
|
||||||
* French translations provided by Jean-Claude Repetto <jc@repetto.org>
|
* Dutch Arnold Niessen <niessen@iae.nl> <arnold.niessen@philips.com>
|
||||||
* Norwegian translations provided by Jørgen Tvedt <pjtvedt@online.no> and Truls Slevigen <truls@slevigen.no>
|
* Portuguese Paulo Lopes <pmml@netvita.pt>
|
||||||
* Finnish translations provided by Hannu Savolainen <hannu@opensound.com>
|
* French Jean-Claude Repetto <jc@repetto.org>
|
||||||
* Polish translations provided by Michael Rakowski <mrak@gmx.de>
|
* Norwegian Jørgen Tvedt <pjtvedt@online.no> and Truls Slevigen <truls@slevigen.no>
|
||||||
* Spanish translations provided by Ruben Nunez Francisco <ruben.nunez@tang-it.com>
|
* Finnish Hannu Savolainen <hannu@opensound.com>
|
||||||
* Greek translations provided by Dimitrios Dimitrakos <mail@dimitrios.de>
|
* Polish Michael Rakowski <mrak@gmx.de>
|
||||||
|
* Spanish Ruben Nunez Francisco <ruben.nunez@tang-it.com>
|
||||||
|
* Greek Dimitrios Dimitrakos <mail@dimitrios.de>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* How to add a new language:
|
* How to add a new language:
|
||||||
*
|
*
|
||||||
* 1. Announce your translation action on the Linux-DVB mailing
|
* 1. Announce your translation action on the VDR mailing
|
||||||
* list to avoid duplicate work.
|
* list to avoid duplicate work.
|
||||||
* 2. Increase the value of 'NumLanguages'.
|
* 2. Increase the value of 'I18nNumLanguages' in 'i18n.h'.
|
||||||
* 3. Insert a new line in every member of the 'Phrases[]' array,
|
* 3. Insert a new line in every member of the 'Phrases[]' array,
|
||||||
* containing the translated text for the new language.
|
* containing the translated text for the new language.
|
||||||
* For example, assuming you want to add the Italian language,
|
* For example, assuming you want to add the Italian language,
|
||||||
@ -46,7 +48,7 @@
|
|||||||
* Note that only the characters defined in 'fontosd.c' will
|
* Note that only the characters defined in 'fontosd.c' will
|
||||||
* be available!
|
* be available!
|
||||||
* 4. Compile VDR and test the new language by switching to it
|
* 4. Compile VDR and test the new language by switching to it
|
||||||
* in the "Setup" menu.
|
* in the "Setup/OSD" menu.
|
||||||
* 5. Send the modified 'i18n.c' file to <kls@cadsoft.de> to have
|
* 5. Send the modified 'i18n.c' file to <kls@cadsoft.de> to have
|
||||||
* it included in the next version of VDR.
|
* it included in the next version of VDR.
|
||||||
*
|
*
|
||||||
@ -60,15 +62,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
#include <stdio.h>
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
const int NumLanguages = 12;
|
const tI18nPhrase Phrases[] = {
|
||||||
|
|
||||||
typedef const char *tPhrase[NumLanguages];
|
|
||||||
|
|
||||||
const tPhrase Phrases[] = {
|
|
||||||
// The name of the language (this MUST be the first phrase!):
|
// The name of the language (this MUST be the first phrase!):
|
||||||
{ "English",
|
{ "English",
|
||||||
"Deutsch",
|
"Deutsch",
|
||||||
@ -1260,6 +1257,19 @@ const tPhrase Phrases[] = {
|
|||||||
"Varios",
|
"Varios",
|
||||||
"Diafora",
|
"Diafora",
|
||||||
},
|
},
|
||||||
|
{ "Plugins",
|
||||||
|
"Plugins",
|
||||||
|
"Plugins",
|
||||||
|
"Plugins",
|
||||||
|
"Plugins",
|
||||||
|
"Plugins",
|
||||||
|
"Plugins",
|
||||||
|
"Plugins",
|
||||||
|
"Plugins",
|
||||||
|
"Plugins",
|
||||||
|
"Plugins",
|
||||||
|
"Plugins",
|
||||||
|
},
|
||||||
{ "Restart",
|
{ "Restart",
|
||||||
"Neustart",
|
"Neustart",
|
||||||
"Ponoven zagon",
|
"Ponoven zagon",
|
||||||
@ -2384,26 +2394,101 @@ const tPhrase Phrases[] = {
|
|||||||
"buscando grabaciones...",
|
"buscando grabaciones...",
|
||||||
"Ginete sarosi egrafon...",
|
"Ginete sarosi egrafon...",
|
||||||
},
|
},
|
||||||
|
{ "This plugin has no setup parameters!",
|
||||||
|
"Dieses Plugin hat keine Setup-Parameter!",
|
||||||
|
"",// TODO
|
||||||
|
"",// TODO
|
||||||
|
"",// TODO
|
||||||
|
"",// TODO
|
||||||
|
"",// TODO
|
||||||
|
"",// TODO
|
||||||
|
"",// TODO
|
||||||
|
"",// TODO
|
||||||
|
"",// TODO
|
||||||
|
"",// TODO
|
||||||
|
},
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *tr(const char *s)
|
// --- cI18nEntry ------------------------------------------------------------
|
||||||
|
|
||||||
|
class cI18nEntry : public cListObject {
|
||||||
|
private:
|
||||||
|
const tI18nPhrase *phrases;
|
||||||
|
const char *plugin;
|
||||||
|
public:
|
||||||
|
cI18nEntry(const tI18nPhrase * const Phrases, const char *Plugin);
|
||||||
|
const tI18nPhrase *Phrases(void) { return phrases; }
|
||||||
|
const char *Plugin(void) { return plugin; }
|
||||||
|
};
|
||||||
|
|
||||||
|
cI18nEntry::cI18nEntry(const tI18nPhrase * const Phrases, const char *Plugin)
|
||||||
|
{
|
||||||
|
phrases = Phrases;
|
||||||
|
plugin = Plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cI18nList -------------------------------------------------------------
|
||||||
|
|
||||||
|
class cI18nList : public cList<cI18nEntry> {
|
||||||
|
public:
|
||||||
|
cI18nEntry *Get(const char *Plugin);
|
||||||
|
const tI18nPhrase *GetPhrases(const char *Plugin);
|
||||||
|
};
|
||||||
|
|
||||||
|
cI18nEntry *cI18nList::Get(const char *Plugin)
|
||||||
|
{
|
||||||
|
if (Plugin) {
|
||||||
|
for (cI18nEntry *p = First(); p; p = Next(p)) {
|
||||||
|
if (strcmp(p->Plugin(), Plugin) == 0)
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tI18nPhrase *cI18nList::GetPhrases(const char *Plugin)
|
||||||
|
{
|
||||||
|
cI18nEntry *p = Get(Plugin);
|
||||||
|
return p ? p->Phrases() : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cI18nList I18nList;
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
|
void I18nRegister(const tI18nPhrase * const Phrases, const char *Plugin)
|
||||||
|
{
|
||||||
|
cI18nEntry *p = I18nList.Get(Plugin);
|
||||||
|
if (p)
|
||||||
|
I18nList.Del(p);
|
||||||
|
if (Phrases)
|
||||||
|
I18nList.Add(new cI18nEntry(Phrases, Plugin));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *I18nTranslate(const char *s, const char *Plugin)
|
||||||
{
|
{
|
||||||
if (Setup.OSDLanguage) {
|
if (Setup.OSDLanguage) {
|
||||||
for (const tPhrase *p = Phrases; **p; p++) {
|
const tI18nPhrase *p = Plugin ? I18nList.GetPhrases(Plugin) : Phrases;
|
||||||
if (strcmp(s, **p) == 0) {
|
if (!p)
|
||||||
const char *t = (*p)[Setup.OSDLanguage];
|
p = Phrases;
|
||||||
if (t && *t)
|
for (int i = ((p == Phrases) ? 1 : 2); i--; ) {
|
||||||
return t;
|
for (; **p; p++) {
|
||||||
}
|
if (strcmp(s, **p) == 0) {
|
||||||
|
const char *t = (*p)[Setup.OSDLanguage];
|
||||||
|
if (t && *t)
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = Phrases;
|
||||||
}
|
}
|
||||||
esyslog(LOG_ERR, "no translation found for '%s' in language %d (%s)\n", s, Setup.OSDLanguage, Phrases[0][Setup.OSDLanguage]);
|
esyslog(LOG_ERR, "%s%sno translation found for '%s' in language %d (%s)\n", Plugin ? Plugin : "", Plugin ? ": " : "", s, Setup.OSDLanguage, Phrases[0][Setup.OSDLanguage]);
|
||||||
}
|
}
|
||||||
const char *p = strchr(s, '$');
|
const char *p = strchr(s, '$');
|
||||||
return p ? p + 1 : s;
|
return p ? p + 1 : s;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char * const * Languages(void)
|
const char * const * I18nLanguages(void)
|
||||||
{
|
{
|
||||||
return &Phrases[0][0];
|
return &Phrases[0][0];
|
||||||
}
|
}
|
||||||
|
20
i18n.h
20
i18n.h
@ -4,16 +4,28 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: i18n.h 1.1 2000/11/11 09:27:25 kls Exp $
|
* $Id: i18n.h 1.2 2002/05/05 13:42:31 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __I18N_H
|
#ifndef __I18N_H
|
||||||
#define __I18N_H
|
#define __I18N_H
|
||||||
|
|
||||||
extern const int NumLanguages;
|
#include <stdio.h>
|
||||||
|
|
||||||
const char *tr(const char *s);
|
const int I18nNumLanguages = 12;
|
||||||
|
|
||||||
const char * const * Languages(void);
|
typedef const char *tI18nPhrase[I18nNumLanguages];
|
||||||
|
|
||||||
|
void I18nRegister(const tI18nPhrase * const Phrases, const char *Plugin);
|
||||||
|
|
||||||
|
const char *I18nTranslate(const char *s, const char *Plugin = NULL);
|
||||||
|
|
||||||
|
const char * const * I18nLanguages(void);
|
||||||
|
|
||||||
|
#ifdef PLUGIN_NAME_I18N
|
||||||
|
#define tr(s) I18nTranslate(s, PLUGIN_NAME_I18N)
|
||||||
|
#else
|
||||||
|
#define tr(s) I18nTranslate(s)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif //__I18N_H
|
#endif //__I18N_H
|
||||||
|
690
menu.c
690
menu.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: menu.c 1.189 2002/05/01 14:54:10 kls Exp $
|
* $Id: menu.c 1.190 2002/05/09 10:13:47 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
@ -16,6 +16,8 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "eit.h"
|
#include "eit.h"
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
|
#include "menuitems.h"
|
||||||
|
#include "plugin.h"
|
||||||
#include "videodir.h"
|
#include "videodir.h"
|
||||||
|
|
||||||
#define MENUTIMEOUT 120 // seconds
|
#define MENUTIMEOUT 120 // seconds
|
||||||
@ -28,125 +30,6 @@
|
|||||||
|
|
||||||
const char *FileNameChars = " abcdefghijklmnopqrstuvwxyz0123456789-.#~";
|
const char *FileNameChars = " abcdefghijklmnopqrstuvwxyz0123456789-.#~";
|
||||||
|
|
||||||
// --- cMenuEditItem ---------------------------------------------------------
|
|
||||||
|
|
||||||
class cMenuEditItem : public cOsdItem {
|
|
||||||
private:
|
|
||||||
const char *name;
|
|
||||||
const char *value;
|
|
||||||
public:
|
|
||||||
cMenuEditItem(const char *Name);
|
|
||||||
~cMenuEditItem();
|
|
||||||
void SetValue(const char *Value);
|
|
||||||
};
|
|
||||||
|
|
||||||
cMenuEditItem::cMenuEditItem(const char *Name)
|
|
||||||
{
|
|
||||||
name = strdup(Name);
|
|
||||||
value = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
cMenuEditItem::~cMenuEditItem()
|
|
||||||
{
|
|
||||||
delete name;
|
|
||||||
delete value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cMenuEditItem::SetValue(const char *Value)
|
|
||||||
{
|
|
||||||
delete value;
|
|
||||||
value = strdup(Value);
|
|
||||||
char *buffer = NULL;
|
|
||||||
asprintf(&buffer, "%s:\t%s", name, value);
|
|
||||||
SetText(buffer, false);
|
|
||||||
Display();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- cMenuEditIntItem ------------------------------------------------------
|
|
||||||
|
|
||||||
class cMenuEditIntItem : public cMenuEditItem {
|
|
||||||
protected:
|
|
||||||
int *value;
|
|
||||||
int min, max;
|
|
||||||
virtual void Set(void);
|
|
||||||
public:
|
|
||||||
cMenuEditIntItem(const char *Name, int *Value, int Min = 0, int Max = INT_MAX);
|
|
||||||
virtual eOSState ProcessKey(eKeys Key);
|
|
||||||
};
|
|
||||||
|
|
||||||
cMenuEditIntItem::cMenuEditIntItem(const char *Name, int *Value, int Min, int Max)
|
|
||||||
:cMenuEditItem(Name)
|
|
||||||
{
|
|
||||||
value = Value;
|
|
||||||
min = Min;
|
|
||||||
max = Max;
|
|
||||||
Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cMenuEditIntItem::Set(void)
|
|
||||||
{
|
|
||||||
char buf[16];
|
|
||||||
snprintf(buf, sizeof(buf), "%d", *value);
|
|
||||||
SetValue(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
eOSState cMenuEditIntItem::ProcessKey(eKeys Key)
|
|
||||||
{
|
|
||||||
eOSState state = cMenuEditItem::ProcessKey(Key);
|
|
||||||
|
|
||||||
if (state == osUnknown) {
|
|
||||||
int newValue;
|
|
||||||
if (k0 <= Key && Key <= k9) {
|
|
||||||
if (fresh) {
|
|
||||||
*value = 0;
|
|
||||||
fresh = false;
|
|
||||||
}
|
|
||||||
newValue = *value * 10 + (Key - k0);
|
|
||||||
}
|
|
||||||
else if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly?
|
|
||||||
newValue = *value - 1;
|
|
||||||
fresh = true;
|
|
||||||
}
|
|
||||||
else if (NORMALKEY(Key) == kRight) {
|
|
||||||
newValue = *value + 1;
|
|
||||||
fresh = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return state;
|
|
||||||
if ((!fresh || min <= newValue) && newValue <= max) {
|
|
||||||
*value = newValue;
|
|
||||||
Set();
|
|
||||||
}
|
|
||||||
state = osContinue;
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- cMenuEditBoolItem -----------------------------------------------------
|
|
||||||
|
|
||||||
class cMenuEditBoolItem : public cMenuEditIntItem {
|
|
||||||
protected:
|
|
||||||
const char *falseString, *trueString;
|
|
||||||
virtual void Set(void);
|
|
||||||
public:
|
|
||||||
cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString = NULL, const char *TrueString = NULL);
|
|
||||||
};
|
|
||||||
|
|
||||||
cMenuEditBoolItem::cMenuEditBoolItem(const char *Name, int *Value, const char *FalseString, const char *TrueString)
|
|
||||||
:cMenuEditIntItem(Name, Value, 0, 1)
|
|
||||||
{
|
|
||||||
falseString = FalseString ? FalseString : tr("no");
|
|
||||||
trueString = TrueString ? TrueString : tr("yes");
|
|
||||||
Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cMenuEditBoolItem::Set(void)
|
|
||||||
{
|
|
||||||
char buf[16];
|
|
||||||
snprintf(buf, sizeof(buf), "%s", *value ? trueString : falseString);
|
|
||||||
SetValue(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- cMenuEditChanItem -----------------------------------------------------
|
// --- cMenuEditChanItem -----------------------------------------------------
|
||||||
|
|
||||||
class cMenuEditChanItem : public cMenuEditIntItem {
|
class cMenuEditChanItem : public cMenuEditIntItem {
|
||||||
@ -451,282 +334,6 @@ eOSState cMenuEditTimeItem::ProcessKey(eKeys Key)
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cMenuEditChrItem ------------------------------------------------------
|
|
||||||
|
|
||||||
class cMenuEditChrItem : public cMenuEditItem {
|
|
||||||
private:
|
|
||||||
char *value;
|
|
||||||
const char *allowed;
|
|
||||||
const char *current;
|
|
||||||
virtual void Set(void);
|
|
||||||
public:
|
|
||||||
cMenuEditChrItem(const char *Name, char *Value, const char *Allowed);
|
|
||||||
~cMenuEditChrItem();
|
|
||||||
virtual eOSState ProcessKey(eKeys Key);
|
|
||||||
};
|
|
||||||
|
|
||||||
cMenuEditChrItem::cMenuEditChrItem(const char *Name, char *Value, const char *Allowed)
|
|
||||||
:cMenuEditItem(Name)
|
|
||||||
{
|
|
||||||
value = Value;
|
|
||||||
allowed = strdup(Allowed);
|
|
||||||
current = strchr(allowed, *Value);
|
|
||||||
if (!current)
|
|
||||||
current = allowed;
|
|
||||||
Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
cMenuEditChrItem::~cMenuEditChrItem()
|
|
||||||
{
|
|
||||||
delete allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cMenuEditChrItem::Set(void)
|
|
||||||
{
|
|
||||||
char buf[2];
|
|
||||||
snprintf(buf, sizeof(buf), "%c", *value);
|
|
||||||
SetValue(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
eOSState cMenuEditChrItem::ProcessKey(eKeys Key)
|
|
||||||
{
|
|
||||||
eOSState state = cMenuEditItem::ProcessKey(Key);
|
|
||||||
|
|
||||||
if (state == osUnknown) {
|
|
||||||
if (NORMALKEY(Key) == kLeft) {
|
|
||||||
if (current > allowed)
|
|
||||||
current--;
|
|
||||||
}
|
|
||||||
else if (NORMALKEY(Key) == kRight) {
|
|
||||||
if (*(current + 1))
|
|
||||||
current++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return state;
|
|
||||||
*value = *current;
|
|
||||||
Set();
|
|
||||||
state = osContinue;
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- cMenuEditStrItem ------------------------------------------------------
|
|
||||||
|
|
||||||
class cMenuEditStrItem : public cMenuEditItem {
|
|
||||||
private:
|
|
||||||
char *value;
|
|
||||||
int length;
|
|
||||||
const char *allowed;
|
|
||||||
int pos;
|
|
||||||
bool insert, newchar, uppercase;
|
|
||||||
void SetHelpKeys(void);
|
|
||||||
virtual void Set(void);
|
|
||||||
char Inc(char c, bool Up);
|
|
||||||
public:
|
|
||||||
cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed);
|
|
||||||
~cMenuEditStrItem();
|
|
||||||
virtual eOSState ProcessKey(eKeys Key);
|
|
||||||
};
|
|
||||||
|
|
||||||
cMenuEditStrItem::cMenuEditStrItem(const char *Name, char *Value, int Length, const char *Allowed)
|
|
||||||
:cMenuEditItem(Name)
|
|
||||||
{
|
|
||||||
value = Value;
|
|
||||||
length = Length;
|
|
||||||
allowed = strdup(Allowed);
|
|
||||||
pos = -1;
|
|
||||||
insert = uppercase = false;
|
|
||||||
newchar = true;
|
|
||||||
Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
cMenuEditStrItem::~cMenuEditStrItem()
|
|
||||||
{
|
|
||||||
delete allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cMenuEditStrItem::SetHelpKeys(void)
|
|
||||||
{
|
|
||||||
if (pos >= 0)
|
|
||||||
Interface->Help(tr("ABC/abc"), tr(insert ? "Overwrite" : "Insert"), tr("Delete"));
|
|
||||||
else
|
|
||||||
Interface->Help(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cMenuEditStrItem::Set(void)
|
|
||||||
{
|
|
||||||
char buf[1000];
|
|
||||||
const char *fmt = insert && newchar ? "[]%c%s" : "[%c]%s";
|
|
||||||
|
|
||||||
if (pos >= 0) {
|
|
||||||
strncpy(buf, value, pos);
|
|
||||||
snprintf(buf + pos, sizeof(buf) - pos - 2, fmt, *(value + pos), value + pos + 1);
|
|
||||||
int width = Interface->Width() - Interface->GetCols()[0];
|
|
||||||
if (cDvbApi::PrimaryDvbApi->WidthInCells(buf) <= width) {
|
|
||||||
// the whole buffer fits on the screen
|
|
||||||
SetValue(buf);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
width *= cDvbApi::PrimaryDvbApi->CellWidth();
|
|
||||||
width -= cDvbApi::PrimaryDvbApi->Width('>'); // assuming '<' and '>' have the same with
|
|
||||||
int w = 0;
|
|
||||||
int i = 0;
|
|
||||||
int l = strlen(buf);
|
|
||||||
while (i < l && w <= width)
|
|
||||||
w += cDvbApi::PrimaryDvbApi->Width(buf[i++]);
|
|
||||||
if (i >= pos + 4) {
|
|
||||||
// the cursor fits on the screen
|
|
||||||
buf[i - 1] = '>';
|
|
||||||
buf[i] = 0;
|
|
||||||
SetValue(buf);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// the cursor doesn't fit on the screen
|
|
||||||
w = 0;
|
|
||||||
if (buf[i = pos + 3]) {
|
|
||||||
buf[i] = '>';
|
|
||||||
buf[i + 1] = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
i--;
|
|
||||||
while (i >= 0 && w <= width)
|
|
||||||
w += cDvbApi::PrimaryDvbApi->Width(buf[i--]);
|
|
||||||
buf[++i] = '<';
|
|
||||||
SetValue(buf + i);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SetValue(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
char cMenuEditStrItem::Inc(char c, bool Up)
|
|
||||||
{
|
|
||||||
const char *p = strchr(allowed, c);
|
|
||||||
if (!p)
|
|
||||||
p = allowed;
|
|
||||||
if (Up) {
|
|
||||||
if (!*++p)
|
|
||||||
p = allowed;
|
|
||||||
}
|
|
||||||
else if (--p < allowed)
|
|
||||||
p = allowed + strlen(allowed) - 1;
|
|
||||||
return *p;
|
|
||||||
}
|
|
||||||
|
|
||||||
eOSState cMenuEditStrItem::ProcessKey(eKeys Key)
|
|
||||||
{
|
|
||||||
switch (Key) {
|
|
||||||
case kRed: // Switch between upper- and lowercase characters
|
|
||||||
if (pos >= 0 && (!insert || !newchar)) {
|
|
||||||
uppercase = !uppercase;
|
|
||||||
value[pos] = uppercase ? toupper(value[pos]) : tolower(value[pos]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case kGreen: // Toggle insert/overwrite modes
|
|
||||||
if (pos >= 0) {
|
|
||||||
insert = !insert;
|
|
||||||
newchar = true;
|
|
||||||
}
|
|
||||||
SetHelpKeys();
|
|
||||||
break;
|
|
||||||
case kYellow|k_Repeat:
|
|
||||||
case kYellow: // Remove the character at current position; in insert mode it is the character to the right of cursor
|
|
||||||
if (pos >= 0) {
|
|
||||||
if (strlen(value) > 1) {
|
|
||||||
memmove(value + pos, value + pos + 1, strlen(value) - pos);
|
|
||||||
// reduce position, if we removed the last character
|
|
||||||
if (pos == int(strlen(value)))
|
|
||||||
pos--;
|
|
||||||
}
|
|
||||||
else if (strlen(value) == 1)
|
|
||||||
value[0] = ' '; // This is the last character in the string, replace it with a blank
|
|
||||||
if (isalpha(value[pos]))
|
|
||||||
uppercase = isupper(value[pos]);
|
|
||||||
newchar = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case kLeft|k_Repeat:
|
|
||||||
case kLeft: if (pos > 0) {
|
|
||||||
if (!insert || newchar)
|
|
||||||
pos--;
|
|
||||||
newchar = true;
|
|
||||||
}
|
|
||||||
if (!insert && isalpha(value[pos]))
|
|
||||||
uppercase = isupper(value[pos]);
|
|
||||||
break;
|
|
||||||
case kRight|k_Repeat:
|
|
||||||
case kRight: if (pos < length && pos < int(strlen(value)) ) {
|
|
||||||
if (++pos >= int(strlen(value))) {
|
|
||||||
if (pos >= 2 && value[pos - 1] == ' ' && value[pos - 2] == ' ')
|
|
||||||
pos--; // allow only two blanks at the end
|
|
||||||
else {
|
|
||||||
value[pos] = ' ';
|
|
||||||
value[pos + 1] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newchar = true;
|
|
||||||
if (!insert && isalpha(value[pos]))
|
|
||||||
uppercase = isupper(value[pos]);
|
|
||||||
if (pos == 0)
|
|
||||||
SetHelpKeys();
|
|
||||||
break;
|
|
||||||
case kUp|k_Repeat:
|
|
||||||
case kUp:
|
|
||||||
case kDown|k_Repeat:
|
|
||||||
case kDown: if (pos >= 0) {
|
|
||||||
if (insert && newchar) {
|
|
||||||
// create a new character in insert mode
|
|
||||||
if (int(strlen(value)) < length) {
|
|
||||||
memmove(value + pos + 1, value + pos, strlen(value) - pos + 1);
|
|
||||||
value[pos] = ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (uppercase)
|
|
||||||
value[pos] = toupper(Inc(tolower(value[pos]), NORMALKEY(Key) == kUp));
|
|
||||||
else
|
|
||||||
value[pos] = Inc( value[pos], NORMALKEY(Key) == kUp);
|
|
||||||
newchar = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return cMenuEditItem::ProcessKey(Key);
|
|
||||||
break;
|
|
||||||
case kOk: if (pos >= 0) {
|
|
||||||
pos = -1;
|
|
||||||
newchar = true;
|
|
||||||
stripspace(value);
|
|
||||||
SetHelpKeys();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// run into default
|
|
||||||
default: return cMenuEditItem::ProcessKey(Key);
|
|
||||||
}
|
|
||||||
Set();
|
|
||||||
return osContinue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- cMenuEditStraItem -----------------------------------------------------
|
|
||||||
|
|
||||||
class cMenuEditStraItem : public cMenuEditIntItem {
|
|
||||||
private:
|
|
||||||
const char * const *strings;
|
|
||||||
protected:
|
|
||||||
virtual void Set(void);
|
|
||||||
public:
|
|
||||||
cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings);
|
|
||||||
};
|
|
||||||
|
|
||||||
cMenuEditStraItem::cMenuEditStraItem(const char *Name, int *Value, int NumStrings, const char * const *Strings)
|
|
||||||
:cMenuEditIntItem(Name, Value, 0, NumStrings - 1)
|
|
||||||
{
|
|
||||||
strings = Strings;
|
|
||||||
Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
void cMenuEditStraItem::Set(void)
|
|
||||||
{
|
|
||||||
SetValue(strings[*value]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- cMenuEditCaItem -------------------------------------------------------
|
// --- cMenuEditCaItem -------------------------------------------------------
|
||||||
|
|
||||||
class cMenuEditCaItem : public cMenuEditIntItem {
|
class cMenuEditCaItem : public cMenuEditIntItem {
|
||||||
@ -1018,116 +625,6 @@ eOSState cMenuChannels::ProcessKey(eKeys Key)
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cMenuTextItem ---------------------------------------------------------
|
|
||||||
|
|
||||||
class cMenuTextItem : public cOsdItem {
|
|
||||||
private:
|
|
||||||
char *text;
|
|
||||||
int x, y, w, h, lines, offset;
|
|
||||||
eDvbColor fgColor, bgColor;
|
|
||||||
eDvbFont font;
|
|
||||||
public:
|
|
||||||
cMenuTextItem(const char *Text, int X, int Y, int W, int H = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground, eDvbFont Font = fontOsd);
|
|
||||||
~cMenuTextItem();
|
|
||||||
int Height(void) { return h; }
|
|
||||||
void Clear(void);
|
|
||||||
virtual void Display(int Offset = -1, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground);
|
|
||||||
bool CanScrollUp(void) { return offset > 0; }
|
|
||||||
bool CanScrollDown(void) { return h + offset < lines; }
|
|
||||||
void ScrollUp(bool Page);
|
|
||||||
void ScrollDown(bool Page);
|
|
||||||
virtual eOSState ProcessKey(eKeys Key);
|
|
||||||
};
|
|
||||||
|
|
||||||
cMenuTextItem::cMenuTextItem(const char *Text, int X, int Y, int W, int H, eDvbColor FgColor, eDvbColor BgColor, eDvbFont Font)
|
|
||||||
{
|
|
||||||
x = X;
|
|
||||||
y = Y;
|
|
||||||
w = W;
|
|
||||||
h = H;
|
|
||||||
fgColor = FgColor;
|
|
||||||
bgColor = BgColor;
|
|
||||||
font = Font;
|
|
||||||
offset = 0;
|
|
||||||
eDvbFont oldFont = Interface->SetFont(font);
|
|
||||||
text = Interface->WrapText(Text, w - 1, &lines);
|
|
||||||
Interface->SetFont(oldFont);
|
|
||||||
if (h < 0)
|
|
||||||
h = lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
cMenuTextItem::~cMenuTextItem()
|
|
||||||
{
|
|
||||||
delete text;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cMenuTextItem::Clear(void)
|
|
||||||
{
|
|
||||||
Interface->Fill(x, y, w, h, bgColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cMenuTextItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor)
|
|
||||||
{
|
|
||||||
int l = 0;
|
|
||||||
char *t = text;
|
|
||||||
eDvbFont oldFont = Interface->SetFont(font);
|
|
||||||
while (*t) {
|
|
||||||
char *n = strchr(t, '\n');
|
|
||||||
if (l >= offset) {
|
|
||||||
if (n)
|
|
||||||
*n = 0;
|
|
||||||
Interface->Write(x, y + l - offset, t, fgColor, bgColor);
|
|
||||||
if (n)
|
|
||||||
*n = '\n';
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!n)
|
|
||||||
break;
|
|
||||||
t = n + 1;
|
|
||||||
if (++l >= h + offset)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Interface->SetFont(oldFont);
|
|
||||||
// scroll indicators use inverted color scheme!
|
|
||||||
if (CanScrollUp()) Interface->Write(x + w - 1, y, "^", bgColor, fgColor);
|
|
||||||
if (CanScrollDown()) Interface->Write(x + w - 1, y + h - 1, "v", bgColor, fgColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cMenuTextItem::ScrollUp(bool Page)
|
|
||||||
{
|
|
||||||
if (CanScrollUp()) {
|
|
||||||
Clear();
|
|
||||||
offset = max(offset - (Page ? h : 1), 0);
|
|
||||||
Display();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void cMenuTextItem::ScrollDown(bool Page)
|
|
||||||
{
|
|
||||||
if (CanScrollDown()) {
|
|
||||||
Clear();
|
|
||||||
offset = min(offset + (Page ? h : 1), lines - h);
|
|
||||||
Display();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eOSState cMenuTextItem::ProcessKey(eKeys Key)
|
|
||||||
{
|
|
||||||
switch (Key) {
|
|
||||||
case kLeft|k_Repeat:
|
|
||||||
case kLeft:
|
|
||||||
case kUp|k_Repeat:
|
|
||||||
case kUp: ScrollUp(NORMALKEY(Key) == kLeft); break;
|
|
||||||
case kRight|k_Repeat:
|
|
||||||
case kRight:
|
|
||||||
case kDown|k_Repeat:
|
|
||||||
case kDown: ScrollDown(NORMALKEY(Key) == kRight); break;
|
|
||||||
default: return osUnknown;
|
|
||||||
}
|
|
||||||
return osContinue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- cMenuText -------------------------------------------------------------
|
// --- cMenuText -------------------------------------------------------------
|
||||||
|
|
||||||
class cMenuText : public cOsdMenu {
|
class cMenuText : public cOsdMenu {
|
||||||
@ -2028,60 +1525,6 @@ eOSState cMenuRecordings::ProcessKey(eKeys Key)
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cMenuSetupPage --------------------------------------------------------
|
|
||||||
|
|
||||||
class cMenuSetupPage : public cOsdMenu {
|
|
||||||
protected:
|
|
||||||
cSetup data;
|
|
||||||
int osdLanguage;
|
|
||||||
void SetupTitle(const char *s);
|
|
||||||
virtual void Set(void) = 0;
|
|
||||||
public:
|
|
||||||
cMenuSetupPage(void);
|
|
||||||
virtual eOSState ProcessKey(eKeys Key);
|
|
||||||
};
|
|
||||||
|
|
||||||
cMenuSetupPage::cMenuSetupPage(void)
|
|
||||||
:cOsdMenu("", 33)
|
|
||||||
{
|
|
||||||
data = Setup;
|
|
||||||
osdLanguage = Setup.OSDLanguage;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cMenuSetupPage::SetupTitle(const char *s)
|
|
||||||
{
|
|
||||||
char buf[40]; // can't call tr() for more than one string at a time!
|
|
||||||
char *q = buf + snprintf(buf, sizeof(buf), "%s - ", tr("Setup"));
|
|
||||||
snprintf(q, sizeof(buf) - strlen(buf), "%s", tr(s));
|
|
||||||
SetTitle(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
eOSState cMenuSetupPage::ProcessKey(eKeys Key)
|
|
||||||
{
|
|
||||||
eOSState state = cOsdMenu::ProcessKey(Key);
|
|
||||||
|
|
||||||
if (state == osUnknown) {
|
|
||||||
switch (Key) {
|
|
||||||
case kOk: state = (Setup.PrimaryDVB != data.PrimaryDVB) ? osSwitchDvb : osBack;
|
|
||||||
cDvbApi::PrimaryDvbApi->SetVideoFormat(data.VideoFormat ? VIDEO_FORMAT_16_9 : VIDEO_FORMAT_4_3);
|
|
||||||
Setup = data;
|
|
||||||
Setup.Save();
|
|
||||||
cDvbApi::SetCaCaps();
|
|
||||||
break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (data.OSDLanguage != osdLanguage) {
|
|
||||||
int OriginalOSDLanguage = Setup.OSDLanguage;
|
|
||||||
Setup.OSDLanguage = data.OSDLanguage;
|
|
||||||
Set();
|
|
||||||
Display();
|
|
||||||
osdLanguage = data.OSDLanguage;
|
|
||||||
Setup.OSDLanguage = OriginalOSDLanguage;
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- cMenuSetupOSD ---------------------------------------------------------
|
// --- cMenuSetupOSD ---------------------------------------------------------
|
||||||
|
|
||||||
class cMenuSetupOSD : public cMenuSetupPage {
|
class cMenuSetupOSD : public cMenuSetupPage {
|
||||||
@ -2095,7 +1538,7 @@ void cMenuSetupOSD::Set(void)
|
|||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
SetupTitle("OSD");
|
SetupTitle("OSD");
|
||||||
Add(new cMenuEditStraItem(tr("Setup.OSD$Language"), &data.OSDLanguage, NumLanguages, Languages()));
|
Add(new cMenuEditStraItem(tr("Setup.OSD$Language"), &data.OSDLanguage, I18nNumLanguages, I18nLanguages()));
|
||||||
Add(new cMenuEditIntItem( tr("Setup.OSD$Width"), &data.OSDwidth, MINOSDWIDTH, MAXOSDWIDTH));
|
Add(new cMenuEditIntItem( tr("Setup.OSD$Width"), &data.OSDwidth, MINOSDWIDTH, MAXOSDWIDTH));
|
||||||
Add(new cMenuEditIntItem( tr("Setup.OSD$Height"), &data.OSDheight, MINOSDHEIGHT, MAXOSDHEIGHT));
|
Add(new cMenuEditIntItem( tr("Setup.OSD$Height"), &data.OSDheight, MINOSDHEIGHT, MAXOSDHEIGHT));
|
||||||
Add(new cMenuEditIntItem( tr("Setup.OSD$Message time (s)"), &data.OSDMessageTime, 1, 60));
|
Add(new cMenuEditIntItem( tr("Setup.OSD$Message time (s)"), &data.OSDMessageTime, 1, 60));
|
||||||
@ -2245,6 +1688,75 @@ void cMenuSetupMisc::Set(void)
|
|||||||
Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$SVDRP timeout (s)"), &data.SVDRPTimeout));
|
Add(new cMenuEditIntItem( tr("Setup.Miscellaneous$SVDRP timeout (s)"), &data.SVDRPTimeout));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- cMenuSetupPluginItem --------------------------------------------------
|
||||||
|
|
||||||
|
class cMenuSetupPluginItem : public cOsdItem {
|
||||||
|
private:
|
||||||
|
int pluginIndex;
|
||||||
|
public:
|
||||||
|
cMenuSetupPluginItem(const char *Name, int Index);
|
||||||
|
int PluginIndex(void) { return pluginIndex; }
|
||||||
|
};
|
||||||
|
|
||||||
|
cMenuSetupPluginItem::cMenuSetupPluginItem(const char *Name, int Index)
|
||||||
|
:cOsdItem(Name)
|
||||||
|
{
|
||||||
|
pluginIndex = Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- cMenuSetupPlugins -----------------------------------------------------
|
||||||
|
|
||||||
|
class cMenuSetupPlugins : public cMenuSetupPage {
|
||||||
|
private:
|
||||||
|
virtual void Set(void);
|
||||||
|
public:
|
||||||
|
cMenuSetupPlugins(void) { Set(); }
|
||||||
|
virtual eOSState ProcessKey(eKeys Key);
|
||||||
|
};
|
||||||
|
|
||||||
|
void cMenuSetupPlugins::Set(void)
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
SetupTitle("Plugins");
|
||||||
|
SetHasHotkeys();
|
||||||
|
for (int i = 0; ; i++) {
|
||||||
|
cPlugin *p = cPluginManager::GetPlugin(i);
|
||||||
|
if (p) {
|
||||||
|
char *buffer = NULL;
|
||||||
|
asprintf(&buffer, "%s (%s) - %s", p->Name(), p->Version(), p->Description());
|
||||||
|
Add(new cMenuSetupPluginItem(hk(buffer), i));
|
||||||
|
delete buffer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eOSState cMenuSetupPlugins::ProcessKey(eKeys Key)
|
||||||
|
{
|
||||||
|
eOSState state = cOsdMenu::ProcessKey(Key); // not cMenuSetupPage::ProcessKey()!
|
||||||
|
|
||||||
|
if (state == osUnknown) {
|
||||||
|
switch (Key) {
|
||||||
|
case kOk: {
|
||||||
|
cMenuSetupPluginItem *item = (cMenuSetupPluginItem *)Get(Current());
|
||||||
|
if (item) {
|
||||||
|
cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
|
||||||
|
if (p) {
|
||||||
|
cOsdMenu *menu = p->SetupMenu();
|
||||||
|
if (menu)
|
||||||
|
return AddSubMenu(menu);
|
||||||
|
Interface->Info(tr("This plugin has no setup parameters!"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
// --- cMenuSetup ------------------------------------------------------------
|
// --- cMenuSetup ------------------------------------------------------------
|
||||||
|
|
||||||
class cMenuSetup : public cOsdMenu {
|
class cMenuSetup : public cOsdMenu {
|
||||||
@ -2265,7 +1777,9 @@ cMenuSetup::cMenuSetup(void)
|
|||||||
void cMenuSetup::Set(void)
|
void cMenuSetup::Set(void)
|
||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
SetTitle(tr("Setup"));
|
char buffer[64];
|
||||||
|
snprintf(buffer, sizeof(buffer), "%s - VDR %s", tr("Setup"), VDRVERSION);
|
||||||
|
SetTitle(buffer);
|
||||||
SetHasHotkeys();
|
SetHasHotkeys();
|
||||||
Add(new cOsdItem(hk(tr("OSD")), osUser1));
|
Add(new cOsdItem(hk(tr("OSD")), osUser1));
|
||||||
Add(new cOsdItem(hk(tr("EPG")), osUser2));
|
Add(new cOsdItem(hk(tr("EPG")), osUser2));
|
||||||
@ -2275,7 +1789,9 @@ void cMenuSetup::Set(void)
|
|||||||
Add(new cOsdItem(hk(tr("Recording")), osUser6));
|
Add(new cOsdItem(hk(tr("Recording")), osUser6));
|
||||||
Add(new cOsdItem(hk(tr("Replay")), osUser7));
|
Add(new cOsdItem(hk(tr("Replay")), osUser7));
|
||||||
Add(new cOsdItem(hk(tr("Miscellaneous")), osUser8));
|
Add(new cOsdItem(hk(tr("Miscellaneous")), osUser8));
|
||||||
Add(new cOsdItem(hk(tr("Restart")), osUser9));
|
if (cPluginManager::HasPlugins())
|
||||||
|
Add(new cOsdItem(hk(tr("Plugins")), osUser9));
|
||||||
|
Add(new cOsdItem(hk(tr("Restart")), osUser10));
|
||||||
}
|
}
|
||||||
|
|
||||||
eOSState cMenuSetup::Restart(void)
|
eOSState cMenuSetup::Restart(void)
|
||||||
@ -2301,7 +1817,8 @@ eOSState cMenuSetup::ProcessKey(eKeys Key)
|
|||||||
case osUser6: return AddSubMenu(new cMenuSetupRecord);
|
case osUser6: return AddSubMenu(new cMenuSetupRecord);
|
||||||
case osUser7: return AddSubMenu(new cMenuSetupReplay);
|
case osUser7: return AddSubMenu(new cMenuSetupReplay);
|
||||||
case osUser8: return AddSubMenu(new cMenuSetupMisc);
|
case osUser8: return AddSubMenu(new cMenuSetupMisc);
|
||||||
case osUser9: return Restart();
|
case osUser9: return AddSubMenu(new cMenuSetupPlugins);
|
||||||
|
case osUser10: return Restart();
|
||||||
default: ;
|
default: ;
|
||||||
}
|
}
|
||||||
if (Setup.OSDLanguage != osdLanguage) {
|
if (Setup.OSDLanguage != osdLanguage) {
|
||||||
@ -2364,6 +1881,22 @@ eOSState cMenuCommands::ProcessKey(eKeys Key)
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- cMenuPluginItem -------------------------------------------------------
|
||||||
|
|
||||||
|
class cMenuPluginItem : public cOsdItem {
|
||||||
|
private:
|
||||||
|
int pluginIndex;
|
||||||
|
public:
|
||||||
|
cMenuPluginItem(const char *Name, int Index);
|
||||||
|
int PluginIndex(void) { return pluginIndex; }
|
||||||
|
};
|
||||||
|
|
||||||
|
cMenuPluginItem::cMenuPluginItem(const char *Name, int Index)
|
||||||
|
:cOsdItem(Name, osPlugin)
|
||||||
|
{
|
||||||
|
pluginIndex = Index;
|
||||||
|
}
|
||||||
|
|
||||||
// --- cMenuMain -------------------------------------------------------------
|
// --- cMenuMain -------------------------------------------------------------
|
||||||
|
|
||||||
#define STOP_RECORDING tr(" Stop recording ")
|
#define STOP_RECORDING tr(" Stop recording ")
|
||||||
@ -2408,6 +1941,22 @@ void cMenuMain::Set(void)
|
|||||||
Add(new cOsdItem(hk(tr("Channels")), osChannels));
|
Add(new cOsdItem(hk(tr("Channels")), osChannels));
|
||||||
Add(new cOsdItem(hk(tr("Timers")), osTimers));
|
Add(new cOsdItem(hk(tr("Timers")), osTimers));
|
||||||
Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
|
Add(new cOsdItem(hk(tr("Recordings")), osRecordings));
|
||||||
|
|
||||||
|
// Plugins:
|
||||||
|
|
||||||
|
for (int i = 0; ; i++) {
|
||||||
|
cPlugin *p = cPluginManager::GetPlugin(i);
|
||||||
|
if (p) {
|
||||||
|
const char *item = p->MainMenuEntry();
|
||||||
|
if (item)
|
||||||
|
Add(new cMenuPluginItem(hk(item), i));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// More basic menu items:
|
||||||
|
|
||||||
Add(new cOsdItem(hk(tr("Setup")), osSetup));
|
Add(new cOsdItem(hk(tr("Setup")), osSetup));
|
||||||
if (Commands.Count())
|
if (Commands.Count())
|
||||||
Add(new cOsdItem(hk(tr("Commands")), osCommands));
|
Add(new cOsdItem(hk(tr("Commands")), osCommands));
|
||||||
@ -2475,6 +2024,19 @@ eOSState cMenuMain::ProcessKey(eKeys Key)
|
|||||||
return osEnd;
|
return osEnd;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case osPlugin: {
|
||||||
|
cMenuPluginItem *item = (cMenuPluginItem *)Get(Current());
|
||||||
|
if (item) {
|
||||||
|
cPlugin *p = cPluginManager::GetPlugin(item->PluginIndex());
|
||||||
|
if (p) {
|
||||||
|
cOsdMenu *menu = p->MainMenuAction();
|
||||||
|
if (menu)
|
||||||
|
return AddSubMenu(menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state = osEnd;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default: switch (Key) {
|
default: switch (Key) {
|
||||||
case kMenu: state = osEnd; break;
|
case kMenu: state = osEnd; break;
|
||||||
case kRed: if (!HasSubMenu())
|
case kRed: if (!HasSubMenu())
|
||||||
|
476
menuitems.c
Normal file
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
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: osd.c 1.23 2002/03/29 16:34:03 kls Exp $
|
* $Id: osd.c 1.24 2002/05/05 12:00:00 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "osd.h"
|
#include "osd.h"
|
||||||
@ -105,12 +105,13 @@ cOsdMenu::~cOsdMenu()
|
|||||||
|
|
||||||
const char *cOsdMenu::hk(const char *s)
|
const char *cOsdMenu::hk(const char *s)
|
||||||
{
|
{
|
||||||
static char buffer[32];
|
static char buffer[64];
|
||||||
if (s && hasHotkeys) {
|
if (s && hasHotkeys) {
|
||||||
if (digit == 0 && '1' <= *s && *s <= '9' && *(s + 1) == ' ')
|
if (digit == 0 && '1' <= *s && *s <= '9' && *(s + 1) == ' ')
|
||||||
digit = 10; // prevents automatic hotkeys - input already has them
|
digit = -1; // prevents automatic hotkeys - input already has them
|
||||||
if (digit < 9) {
|
if (digit >= 0) {
|
||||||
snprintf(buffer, sizeof(buffer), " %d %s", ++digit, s);
|
digit++;
|
||||||
|
snprintf(buffer, sizeof(buffer), " %c %s", (digit < 10) ? '0' + digit : ' ' , s);
|
||||||
s = buffer;
|
s = buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
osd.h
4
osd.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: osd.h 1.27 2002/03/10 16:18:11 kls Exp $
|
* $Id: osd.h 1.28 2002/05/05 12:00:00 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __OSD_H
|
#ifndef __OSD_H
|
||||||
@ -23,6 +23,7 @@ enum eOSState { osUnknown,
|
|||||||
osChannels,
|
osChannels,
|
||||||
osTimers,
|
osTimers,
|
||||||
osRecordings,
|
osRecordings,
|
||||||
|
osPlugin,
|
||||||
osSetup,
|
osSetup,
|
||||||
osCommands,
|
osCommands,
|
||||||
osRecord,
|
osRecord,
|
||||||
@ -43,6 +44,7 @@ enum eOSState { osUnknown,
|
|||||||
osUser7,
|
osUser7,
|
||||||
osUser8,
|
osUser8,
|
||||||
osUser9,
|
osUser9,
|
||||||
|
osUser10,
|
||||||
};
|
};
|
||||||
|
|
||||||
class cOsdItem : public cListObject {
|
class cOsdItem : public cListObject {
|
||||||
|
319
plugin.c
Normal file
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
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: remote.h 1.15 2001/07/22 14:42:59 kls Exp $
|
* $Id: remote.h 1.16 2002/05/09 11:43:04 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __REMOTE_H
|
#ifndef __REMOTE_H
|
||||||
@ -95,7 +95,7 @@ public:
|
|||||||
|
|
||||||
#elif !defined REMOTE_NONE
|
#elif !defined REMOTE_NONE
|
||||||
|
|
||||||
#error Please define a remote control mode!
|
// #error Please define a remote control mode!
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
24
vdr.1
24
vdr.1
@ -8,9 +8,9 @@
|
|||||||
.\" License as specified in the file COPYING that comes with the
|
.\" License as specified in the file COPYING that comes with the
|
||||||
.\" vdr distribution.
|
.\" vdr distribution.
|
||||||
.\"
|
.\"
|
||||||
.\" $Id: vdr.1 1.3 2002/04/07 13:11:43 kls Exp $
|
.\" $Id: vdr.1 1.4 2002/05/09 16:04:17 kls Exp $
|
||||||
.\"
|
.\"
|
||||||
.TH vdr 1 "7 Apr 2002" "1.0.0" "Video Disk Recorder"
|
.TH vdr 1 "9 May 2002" "1.1.0" "Video Disk Recorder"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
vdr - the Video Disk Recorder
|
vdr - the Video Disk Recorder
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@ -68,6 +68,11 @@ Set logging to \fIlevel\fR.
|
|||||||
\fB2\fR\ =\ errors and info, \fB3\fR\ =\ errors, info and debug.
|
\fB2\fR\ =\ errors and info, \fB3\fR\ =\ errors, info and debug.
|
||||||
The default logging level is \fB3\fR.
|
The default logging level is \fB3\fR.
|
||||||
.TP
|
.TP
|
||||||
|
.BI -L\ dir ,\ --lib= dir
|
||||||
|
Search for plugins in directory \fIdir\fR (default is ./PLUGINS/lib).
|
||||||
|
There can be several \fB-L\fR options with different \fIdir\fR values.
|
||||||
|
Each of them will apply to the \fB-P\fR options following it.
|
||||||
|
.TP
|
||||||
.B -m, --mute
|
.B -m, --mute
|
||||||
Mute audio of the primary DVB device at startup.
|
Mute audio of the primary DVB device at startup.
|
||||||
.TP
|
.TP
|
||||||
@ -77,6 +82,18 @@ The default SVDRP port is \fB2001\fR.
|
|||||||
You need to edit the file \fIsvdrphosts.conf\fR in order to enable
|
You need to edit the file \fIsvdrphosts.conf\fR in order to enable
|
||||||
access to the SVDRP port.
|
access to the SVDRP port.
|
||||||
.TP
|
.TP
|
||||||
|
.BI -P\ options ,\ --plugin= options
|
||||||
|
Load a plugin, defined by the given \fIoptions\fR.
|
||||||
|
The first word in \fIoptions\fR must be the name of an existing \fBvdr\fR
|
||||||
|
plugin, optionally followed by a blank separated list of command line options
|
||||||
|
for that plugin. If \fIoptions\fR contains any blanks, you need to enclose it
|
||||||
|
in quotes, like for example
|
||||||
|
|
||||||
|
\fBvdr -P "abc -a -b xyz"\fR
|
||||||
|
|
||||||
|
which would load a plugin named \fBabc\fR, giving it the command line options
|
||||||
|
\fB-a\ -b\ xyz\fR.
|
||||||
|
.TP
|
||||||
.BI -r\ cmd ,\ --record= cmd
|
.BI -r\ cmd ,\ --record= cmd
|
||||||
Call \fIcmd\fR before and after a recording.
|
Call \fIcmd\fR before and after a recording.
|
||||||
.TP
|
.TP
|
||||||
@ -104,6 +121,9 @@ Successful program execution.
|
|||||||
.B 1
|
.B 1
|
||||||
An error has been detected which requires the DVB driver and \fBvdr\fR
|
An error has been detected which requires the DVB driver and \fBvdr\fR
|
||||||
to be re-loaded.
|
to be re-loaded.
|
||||||
|
.TP
|
||||||
|
.B 2
|
||||||
|
An non-recoverable error has been detected, \fBvdr\fR has given up.
|
||||||
.SH FILES
|
.SH FILES
|
||||||
.TP
|
.TP
|
||||||
.I channels.conf
|
.I channels.conf
|
||||||
|
131
vdr.c
131
vdr.c
@ -22,7 +22,7 @@
|
|||||||
*
|
*
|
||||||
* The project's page is at http://www.cadsoft.de/people/kls/vdr
|
* The project's page is at http://www.cadsoft.de/people/kls/vdr
|
||||||
*
|
*
|
||||||
* $Id: vdr.c 1.103 2002/04/26 12:15:30 kls Exp $
|
* $Id: vdr.c 1.104 2002/05/05 12:00:00 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
@ -36,6 +36,7 @@
|
|||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
#include "interface.h"
|
#include "interface.h"
|
||||||
#include "menu.h"
|
#include "menu.h"
|
||||||
|
#include "plugin.h"
|
||||||
#include "recording.h"
|
#include "recording.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
#include "videodir.h"
|
#include "videodir.h"
|
||||||
@ -79,14 +80,18 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
#define DEFAULTSVDRPPORT 2001
|
#define DEFAULTSVDRPPORT 2001
|
||||||
#define DEFAULTWATCHDOG 0 // seconds
|
#define DEFAULTWATCHDOG 0 // seconds
|
||||||
|
#define DEFAULTPLUGINDIR "./PLUGINS/lib"
|
||||||
|
|
||||||
int SVDRPport = DEFAULTSVDRPPORT;
|
int SVDRPport = DEFAULTSVDRPPORT;
|
||||||
const char *ConfigDirectory = NULL;
|
const char *ConfigDirectory = NULL;
|
||||||
|
bool DisplayHelp = false;
|
||||||
|
bool DisplayVersion = false;
|
||||||
bool DaemonMode = false;
|
bool DaemonMode = false;
|
||||||
bool MuteAudio = false;
|
bool MuteAudio = false;
|
||||||
int WatchdogTimeout = DEFAULTWATCHDOG;
|
int WatchdogTimeout = DEFAULTWATCHDOG;
|
||||||
const char *Terminal = NULL;
|
const char *Terminal = NULL;
|
||||||
const char *Shutdown = NULL;
|
const char *Shutdown = NULL;
|
||||||
|
cPluginManager PluginManager(DEFAULTPLUGINDIR);
|
||||||
|
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{ "audio", required_argument, NULL, 'a' },
|
{ "audio", required_argument, NULL, 'a' },
|
||||||
@ -95,8 +100,10 @@ int main(int argc, char *argv[])
|
|||||||
{ "device", required_argument, NULL, 'D' },
|
{ "device", required_argument, NULL, 'D' },
|
||||||
{ "epgfile", required_argument, NULL, 'E' },
|
{ "epgfile", required_argument, NULL, 'E' },
|
||||||
{ "help", no_argument, NULL, 'h' },
|
{ "help", no_argument, NULL, 'h' },
|
||||||
|
{ "lib", required_argument, NULL, 'L' },
|
||||||
{ "log", required_argument, NULL, 'l' },
|
{ "log", required_argument, NULL, 'l' },
|
||||||
{ "mute", no_argument, NULL, 'm' },
|
{ "mute", no_argument, NULL, 'm' },
|
||||||
|
{ "plugin", required_argument, NULL, 'P' },
|
||||||
{ "port", required_argument, NULL, 'p' },
|
{ "port", required_argument, NULL, 'p' },
|
||||||
{ "record", required_argument, NULL, 'r' },
|
{ "record", required_argument, NULL, 'r' },
|
||||||
{ "shutdown", required_argument, NULL, 's' },
|
{ "shutdown", required_argument, NULL, 's' },
|
||||||
@ -108,8 +115,7 @@ int main(int argc, char *argv[])
|
|||||||
};
|
};
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
int option_index = 0;
|
while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:L:mp:P:r:s:t:v:Vw:", long_options, NULL)) != -1) {
|
||||||
while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:mp:r:s:t:v:Vw:", long_options, &option_index)) != -1) {
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'a': cDvbApi::SetAudioCommand(optarg);
|
case 'a': cDvbApi::SetAudioCommand(optarg);
|
||||||
break;
|
break;
|
||||||
@ -128,40 +134,7 @@ int main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
case 'E': cSIProcessor::SetEpgDataFileName(*optarg != '-' ? optarg : NULL);
|
case 'E': cSIProcessor::SetEpgDataFileName(*optarg != '-' ? optarg : NULL);
|
||||||
break;
|
break;
|
||||||
case 'h': printf("Usage: vdr [OPTION]\n\n" // for easier orientation, this is column 80|
|
case 'h': DisplayHelp = true;
|
||||||
" -a CMD, --audio=CMD send Dolby Digital audio to stdin of command CMD\n"
|
|
||||||
" -c DIR, --config=DIR read config files from DIR (default is to read them\n"
|
|
||||||
" from the video directory)\n"
|
|
||||||
" -d, --daemon run in daemon mode\n"
|
|
||||||
" -D NUM, --device=NUM use only the given DVB device (NUM = 0, 1, 2...)\n"
|
|
||||||
" there may be several -D options (default: all DVB\n"
|
|
||||||
" devices will be used)\n"
|
|
||||||
" -E FILE --epgfile=FILE write the EPG data into the given FILE (default is\n"
|
|
||||||
" %s); use '-E-' to disable this\n"
|
|
||||||
" if FILE is a directory, the default EPG file will be\n"
|
|
||||||
" created in that directory\n"
|
|
||||||
" -h, --help print this help and exit\n"
|
|
||||||
" -l LEVEL, --log=LEVEL set log level (default: 3)\n"
|
|
||||||
" 0 = no logging, 1 = errors only,\n"
|
|
||||||
" 2 = errors and info, 3 = errors, info and debug\n"
|
|
||||||
" -m, --mute mute audio of the primary DVB device at startup\n"
|
|
||||||
" -p PORT, --port=PORT use PORT for SVDRP (default: %d)\n"
|
|
||||||
" 0 turns off SVDRP\n"
|
|
||||||
" -r CMD, --record=CMD call CMD before and after a recording\n"
|
|
||||||
" -s CMD, --shutdown=CMD call CMD to shutdown the computer\n"
|
|
||||||
" -t TTY, --terminal=TTY controlling tty\n"
|
|
||||||
" -V, --version print version information and exit\n"
|
|
||||||
" -v DIR, --video=DIR use DIR as video directory (default: %s)\n"
|
|
||||||
" -w SEC, --watchdog=SEC activate the watchdog timer with a timeout of SEC\n"
|
|
||||||
" seconds (default: %d); '0' disables the watchdog\n"
|
|
||||||
"\n"
|
|
||||||
"Report bugs to <vdr-bugs@cadsoft.de>\n",
|
|
||||||
cSIProcessor::GetEpgDataFileName() ? cSIProcessor::GetEpgDataFileName() : "'-'",
|
|
||||||
DEFAULTSVDRPPORT,
|
|
||||||
VideoDirectory,
|
|
||||||
DEFAULTWATCHDOG
|
|
||||||
);
|
|
||||||
return 0;
|
|
||||||
break;
|
break;
|
||||||
case 'l': if (isnumber(optarg)) {
|
case 'l': if (isnumber(optarg)) {
|
||||||
int l = atoi(optarg);
|
int l = atoi(optarg);
|
||||||
@ -173,6 +146,8 @@ int main(int argc, char *argv[])
|
|||||||
fprintf(stderr, "vdr: invalid log level: %s\n", optarg);
|
fprintf(stderr, "vdr: invalid log level: %s\n", optarg);
|
||||||
return 2;
|
return 2;
|
||||||
break;
|
break;
|
||||||
|
case 'L': PluginManager.SetDirectory(optarg);
|
||||||
|
break;
|
||||||
case 'm': MuteAudio = true;
|
case 'm': MuteAudio = true;
|
||||||
break;
|
break;
|
||||||
case 'p': if (isnumber(optarg))
|
case 'p': if (isnumber(optarg))
|
||||||
@ -182,14 +157,15 @@ int main(int argc, char *argv[])
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'P': PluginManager.AddPlugin(optarg);
|
||||||
|
break;
|
||||||
case 'r': cRecordingUserCommand::SetCommand(optarg);
|
case 'r': cRecordingUserCommand::SetCommand(optarg);
|
||||||
break;
|
break;
|
||||||
case 's': Shutdown = optarg;
|
case 's': Shutdown = optarg;
|
||||||
break;
|
break;
|
||||||
case 't': Terminal = optarg;
|
case 't': Terminal = optarg;
|
||||||
break;
|
break;
|
||||||
case 'V': printf("vdr, version %s\n", VDRVERSION);
|
case 'V': DisplayVersion = true;
|
||||||
return 0;
|
|
||||||
break;
|
break;
|
||||||
case 'v': VideoDirectory = optarg;
|
case 'v': VideoDirectory = optarg;
|
||||||
while (optarg && *optarg && optarg[strlen(optarg) - 1] == '/')
|
while (optarg && *optarg && optarg[strlen(optarg) - 1] == '/')
|
||||||
@ -209,6 +185,71 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Help and version info:
|
||||||
|
|
||||||
|
if (DisplayHelp || DisplayVersion) {
|
||||||
|
if (!PluginManager.HasPlugins())
|
||||||
|
PluginManager.AddPlugin("*"); // adds all available plugins
|
||||||
|
PluginManager.LoadPlugins();
|
||||||
|
if (DisplayHelp) {
|
||||||
|
printf("Usage: vdr [OPTIONS]\n\n" // for easier orientation, this is column 80|
|
||||||
|
" -a CMD, --audio=CMD send Dolby Digital audio to stdin of command CMD\n"
|
||||||
|
" -c DIR, --config=DIR read config files from DIR (default is to read them\n"
|
||||||
|
" from the video directory)\n"
|
||||||
|
" -d, --daemon run in daemon mode\n"
|
||||||
|
" -D NUM, --device=NUM use only the given DVB device (NUM = 0, 1, 2...)\n"
|
||||||
|
" there may be several -D options (default: all DVB\n"
|
||||||
|
" devices will be used)\n"
|
||||||
|
" -E FILE --epgfile=FILE write the EPG data into the given FILE (default is\n"
|
||||||
|
" %s); use '-E-' to disable this\n"
|
||||||
|
" if FILE is a directory, the default EPG file will be\n"
|
||||||
|
" created in that directory\n"
|
||||||
|
" -h, --help print this help and exit\n"
|
||||||
|
" -l LEVEL, --log=LEVEL set log level (default: 3)\n"
|
||||||
|
" 0 = no logging, 1 = errors only,\n"
|
||||||
|
" 2 = errors and info, 3 = errors, info and debug\n"
|
||||||
|
" -L DIR, --lib=DIR search for plugins in DIR (default is %s)\n"
|
||||||
|
" -m, --mute mute audio of the primary DVB device at startup\n"
|
||||||
|
" -p PORT, --port=PORT use PORT for SVDRP (default: %d)\n"
|
||||||
|
" 0 turns off SVDRP\n"
|
||||||
|
" -P OPT, --plugin=OPT load a plugin defined by the given options\n"
|
||||||
|
" -r CMD, --record=CMD call CMD before and after a recording\n"
|
||||||
|
" -s CMD, --shutdown=CMD call CMD to shutdown the computer\n"
|
||||||
|
" -t TTY, --terminal=TTY controlling tty\n"
|
||||||
|
" -v DIR, --video=DIR use DIR as video directory (default: %s)\n"
|
||||||
|
" -V, --version print version information and exit\n"
|
||||||
|
" -w SEC, --watchdog=SEC activate the watchdog timer with a timeout of SEC\n"
|
||||||
|
" seconds (default: %d); '0' disables the watchdog\n"
|
||||||
|
"\n",
|
||||||
|
cSIProcessor::GetEpgDataFileName() ? cSIProcessor::GetEpgDataFileName() : "'-'",
|
||||||
|
DEFAULTPLUGINDIR,
|
||||||
|
DEFAULTSVDRPPORT,
|
||||||
|
VideoDirectory,
|
||||||
|
DEFAULTWATCHDOG
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (DisplayVersion)
|
||||||
|
printf("vdr (%s) - The Video Disk Recorder\n", VDRVERSION);
|
||||||
|
if (PluginManager.HasPlugins()) {
|
||||||
|
if (DisplayHelp)
|
||||||
|
printf("Plugins: vdr -P\"name [OPTIONS]\"\n\n");
|
||||||
|
for (int i = 0; ; i++) {
|
||||||
|
cPlugin *p = PluginManager.GetPlugin(i);
|
||||||
|
if (p) {
|
||||||
|
const char *help = p->CommandLineHelp();
|
||||||
|
printf("%s (%s) - %s\n", p->Name(), p->Version(), p->Description());
|
||||||
|
if (DisplayHelp && help) {
|
||||||
|
printf("\n");
|
||||||
|
puts(help);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Log file:
|
// Log file:
|
||||||
|
|
||||||
if (SysLogLevel > 0)
|
if (SysLogLevel > 0)
|
||||||
@ -250,6 +291,11 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
isyslog(LOG_INFO, "VDR version %s started", VDRVERSION);
|
isyslog(LOG_INFO, "VDR version %s started", VDRVERSION);
|
||||||
|
|
||||||
|
// Load plugins:
|
||||||
|
|
||||||
|
if (!PluginManager.LoadPlugins(true))
|
||||||
|
return 2;
|
||||||
|
|
||||||
// Configuration data:
|
// Configuration data:
|
||||||
|
|
||||||
if (!ConfigDirectory)
|
if (!ConfigDirectory)
|
||||||
@ -276,6 +322,12 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
cSIProcessor::Read();
|
cSIProcessor::Read();
|
||||||
|
|
||||||
|
// Start plugins:
|
||||||
|
|
||||||
|
PluginManager.StartPlugins();
|
||||||
|
|
||||||
|
// Channel:
|
||||||
|
|
||||||
Channels.SwitchTo(Setup.CurrentChannel);
|
Channels.SwitchTo(Setup.CurrentChannel);
|
||||||
if (MuteAudio)
|
if (MuteAudio)
|
||||||
cDvbApi::PrimaryDvbApi->ToggleMute();
|
cDvbApi::PrimaryDvbApi->ToggleMute();
|
||||||
@ -529,6 +581,7 @@ int main(int argc, char *argv[])
|
|||||||
Setup.CurrentChannel = cDvbApi::CurrentChannel();
|
Setup.CurrentChannel = cDvbApi::CurrentChannel();
|
||||||
Setup.CurrentVolume = cDvbApi::CurrentVolume();
|
Setup.CurrentVolume = cDvbApi::CurrentVolume();
|
||||||
Setup.Save();
|
Setup.Save();
|
||||||
|
PluginManager.Shutdown(true);
|
||||||
cVideoCutter::Stop();
|
cVideoCutter::Stop();
|
||||||
delete Menu;
|
delete Menu;
|
||||||
delete ReplayControl;
|
delete ReplayControl;
|
||||||
|
Loading…
Reference in New Issue
Block a user