Made remote controls plugin aware

This commit is contained in:
Klaus Schmidinger 2002-09-29 13:40:45 +02:00
parent f3af8e065a
commit fc66860847
27 changed files with 1212 additions and 1055 deletions

17
HISTORY
View File

@ -1479,7 +1479,7 @@ Video Disk Recorder Revision History
Stefan Huelswitt).
- Added an EPG bugfix for the latest VOX EPG data format.
2002-09-21: Version 1.1.11
2002-09-29: Version 1.1.11
- Fixed an incomplete initialization of the filter parameters in eit.c (thanks
to Jeremy Hall).
@ -1517,3 +1517,18 @@ Video Disk Recorder Revision History
numbers may be different.
- Added a missing 'public' keyword in device.h (thanks to Martin Hammerschmid).
- Fixed a race condition when starting 'Transfer Mode'.
- Rearranged the remote control key handling to allow plugins to implement
additional types of remote controls (see PLUGINS.html, section "Remote Control").
The previously used files 'keys.conf' and 'keys-pc.conf' have been replaced
by the file 'remote.conf', which holds the key definitions of all remote controls.
- The LIRC remote control keys are now handled just like the keyboard and RCU keys.
This means that you can use the lircd.conf file as is for your remote control,
without the need of editing it to make the key names the same as used in VDR.
When first starting VDR it will go into the "Learning keys" mode and ask you
to press the various keys. The resulting key assignment will be stored in
the file 'remote.conf'.
Since I have no way of testing the LIRC support, I hope I didn't break it in
the process...
- While learning the remote control keys it is now possible to press the 'Menu'
key to skip the definition of keys that are not available on your particular
RC unit.

14
INSTALL
View File

@ -330,8 +330,7 @@ of the card that can receive it.
Learning the remote control keys:
---------------------------------
There is no default 'keys.conf' file, so if you compile the program
with 'REMOTE=RCU' you will have to go through a "teach-in"
There is no default 'remote.conf' file, so you will have to go through a "teach-in"
session that allows the program to learn your remote control codes.
It will first attempt to determine the basic data transfer mode and
timing of your remote control unit, and then will ask you to press one
@ -342,11 +341,8 @@ you define, the more you will be able to navigate through the menus and
control recording/replaying. The program uses only a very small number
of keys which have multiple meanings in the various modes (see MANUAL
for a detailed description).
If the program has been built with "REMOTE=KBD", it will use the
key configuration file 'keys-pc.conf', so that you won't loose data
when switching between remote control and keyboard mode.
The default PC key assignments are:
The recommended PC key assignments are:
Up, Down, Left, Right Crsr keys in numeric block
Menu 'Home' in numeric block
@ -359,10 +355,6 @@ The default PC key assignments are:
Mute 'm'
If you prefer different key assignments, or if the default doesn't work for
your keyboard, simply delete the file 'keys-pc.conf' and restart 'vdr' to get
your keyboard, simply delete the file 'remote.conf' and restart 'vdr' to get
into learning mode.
If the program has been compiled with 'REMOTE=LIRC', no 'keys.conf' file
will be used. Instead, the key names as listed in the source file 'config.c'
must be used when setting up LIRC. See http://www.lirc.org for more about LIRC.

View File

@ -4,7 +4,7 @@
# See the main source file 'vdr.c' for copyright information and
# how to reach the author.
#
# $Id: Makefile 1.46 2002/09/08 14:00:48 kls Exp $
# $Id: Makefile 1.47 2002/09/28 14:12:43 kls Exp $
.DELETE_ON_ERROR:
@ -33,7 +33,7 @@ endif
DTVLIB = $(DTVDIR)/libdtv.a
OBJS = audio.o config.o cutter.o device.o dvbdevice.o dvbosd.o dvbplayer.o dvbspu.o eit.o eitscan.o font.o i18n.o\
interface.o menu.o menuitems.o osdbase.o osd.o player.o plugin.o receiver.o\
interface.o keys.o lirc.o menu.o menuitems.o osdbase.o osd.o player.o plugin.o rcu.o receiver.o\
recorder.o recording.o remote.o remux.o ringbuffer.o spu.o status.o svdrp.o thread.o\
tools.o transfer.o vdr.o videodir.o

View File

@ -21,18 +21,18 @@ VDR program and present itself to the user.
The <i>inside</i> interface provides the plugin code access to VDR's internal data
structures and allows it to hook itself into specific areas to perform special actions.
<p>
<!--X1.1.6--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.6 are marked like this.
<!--X1.1.6--></td></tr></table>
<!--X1.1.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.1.7--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.7 are marked like this.
<!--X1.1.7--></td></tr></table>
<!--X1.1.8--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.1.8--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.8 are marked like this.
<!--X1.1.8--></td></tr></table>
<!--X1.1.9--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<!--X1.1.9--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.9 are marked like this.
<!--X1.1.9--></td></tr></table>
<!--X1.1.11--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
Important modifications introduced in version 1.1.11 are marked like this.
<!--X1.1.11--></td></tr></table>
<a name="Part I - The Outside Interface"><hr><center><h1>Part I - The Outside Interface</h1></center>
@ -957,7 +957,7 @@ stream. There are no prerequisites regarding the length or alignment of an
individual block of data. The sum of all blocks must simply result in the
desired video data stream, and it must be delivered fast enough so that the
DVB device doesn't run out of data.
<!--X1.1.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.1.7--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
To avoid busy loops the player should call its member function
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
@ -1065,7 +1065,6 @@ enjoy additional players, since they will be able to control them with actions
that they already know. If you absolutely want to do things differently, just go
ahead - it's your show...
<!--X1.1.6--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<hr><h2>Receivers</h2>
<center><i><b>Tapping into the stream...</b></i></center><p>
@ -1119,7 +1118,6 @@ cDevice::PrimaryDevice()-&gt;AttachReceiver(Receiver);
If the <tt>cReceiver</tt> isn't needed any more, it may simply be <i>deleted</i>
and will automatically detach itself from the <tt>cDevice</tt>.
<!--X1.1.6--></td></tr></table>
<hr><h2>The On Screen Display</h2>
@ -1151,7 +1149,6 @@ to define an actual OSD drawing area (see VDR/osdbase.h for the declarations
of these functions, and VDR/osd.c to see how VDR opens the OSD and sets up
its windows and color depths).
<!--X1.1.6--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<hr><h2>Devices</h2>
<center><i><b>Expanding the possibilities</b></i></center><p>
@ -1187,7 +1184,7 @@ the <tt>cDvbDevice</tt>, which is used to access the DVB PCI cards.
If the new device can receive, it most likely needs to provide a way of
selecting which channel it shall tune to:
<!--X1.1.9--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<!--X1.1.9--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
@ -1206,7 +1203,7 @@ A device that can be used for recording must implement the functions
virtual bool SetPid(cPidHandle *Handle, int Type, bool On);
virtual bool OpenDvr(void);
virtual void CloseDvr(void);
<!--X1.1.9--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<!--X1.1.9--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
virtual bool GetTSPacket(uchar *&amp;Data);
<!--X1.1.9--></td></tr></table>
</pre></td></tr></table><p>
@ -1230,7 +1227,7 @@ to indicate this to VDR.
<p>
The functions to implement replaying capabilites are
<!--X1.1.7--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<!--X1.1.7--><table width=100%><tr><td bgcolor=#0000AA>&nbsp;</td><td width=100%>
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
virtual bool HasDecoder(void) const;
virtual bool SetPlayMode(ePlayMode PlayMode);
@ -1254,7 +1251,7 @@ virtual void SetVideoFormat(bool VideoFormat16_9);
virtual void SetVolumeDevice(int Volume);
</pre></td></tr></table><p>
<!--X1.1.8--><table width=100%><tr><td bgcolor=#AA0000>&nbsp;</td><td width=100%>
<!--X1.1.8--><table width=100%><tr><td bgcolor=#00AA00>&nbsp;</td><td width=100%>
<p>
<b>On Screen Display</b>
<p>
@ -1294,7 +1291,120 @@ Nothing needs to be done to shut down the devices. VDR will automatically
shut down (delete) all devices when the program terminates. It is therefore
important that the devices are created on the heap, using the <tt>new</tt>
operator!
<!--X1.1.6--></td></tr></table>
<!--X1.1.11--><table width=100%><tr><td bgcolor=#FF0000>&nbsp;</td><td width=100%>
<hr><h2>Remote Control</h2>
<center><i><b>The joy of zapping!</b></i></center><p>
There are several ways to control the operation of VDR. The builtin methods
are using the PC keyboard, a homebuilt RCU unit or the LIRC interface.
Of course there may be many more ways you might think of to implement a
remote control, so a plugin can use the <tt>cRemote</tt> class to do that.
<p>
The simplest method for a plugin to issue commands to VDR is to call the
static function <tt>cRemote::Put(eKeys Key)</tt>, as in
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
cRemote::Put(kUp);
</pre></td></tr></table><p>
In this case the plugin must do the mapping of whatever incoming signal or code
it processes to the <tt>eKeys</tt> values itself. This makes sense if the incoming
codes are well known and won't ever change.
<p>
In cases where the incoming codes are not known, or not all available keys may
be supported by the actual remote control in use, you may want to derive your
own remote control class from <tt>cRemote</tt>, as in
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
#include &lt;vdr/remote.h&gt;
#include &lt;vdr/thread.h&gt;
class cMyRemote : public cRemote, private cThread {
private:
virtual void Action(void);
public:
cMyRemote(const char *Name);
virtual bool Initialize(void);
};
</pre></td></tr></table><p>
Note that deriving from <tt>cThread</tt> is not required for a remote control
class to work, but typically you may want to have a separate thread running that
collects the input and delivers it to the <tt>cRemote</tt> base class.
<p>
You should create your derived remote control object in the
<a href="#Getting started"><tt>Start()</tt></a> function of your plugin.
Note that the object has to be created on the heap (using <tt>new</tt>),
and you shall not delete it at any point (it will be deleted automatically
when the program ends).
<p>
The constructor of your remote control class should look like this
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
cMyRemote::cMyRemote(const char *Name)
:cRemote(Name)
{
Start();
}
</pre></td></tr></table><p>
The <tt>Name</tt> is important in order for the <tt>cRemote</tt> base class
to be able to distinguish the codes for the various remote controls.
When creating your <tt>cMyRemote</tt> object you should use the value returned
by the <tt>Name()</tt> member function of the plugin class, which returns the
plugin's name. Calling <tt>Start()</tt> will start the thread that collects
the incoming data (by calling your <tt>Action()</tt> function).
In case you need to do any other setup steps, like opening a file or initializing
member variables, you should do so before calling <tt>Start()</tt>.
<p>
VDR will handle everything necessary to learn the key mappings of your remote
control. In order to do so, it will first call the virtual function <tt>Initialize()</tt>,
in which you should take all necessary steps to make sure your remote control
can be accessed. This may, for instance, include trying various communications
protocols. <tt>Initialize()</tt>, if implemented, shall only return after it has
made sure data can be received from the remote control. Before calling this
function, VDR will prompt the user on the OSD to press any key on the remote control.
As soon as your derived <tt>cRemote</tt> class has detected useful incoming data,
<tt>Initialize()</tt> should return <i>true</i>. If any fatal error occurs, <i>false</i>
should be returned.
<p>
If your remote control class needs some setup data that shall be
readily available next time VDR starts (without having to go through the initialization
procedure again) it can use the <tt>cRemote</tt> member functions
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
void PutSetup(const char *Setup);
const char *GetSetup(void);
</pre></td></tr></table><p>
to store and retrieve a character string containing whatever data is needed.
Note that the <tt>Initialize()</tt> function will only be called if there are
no key mappings known for this remote control. Once the key mappings have been
learned, <tt>Initialize()</tt> will never be called again.
<p>
The <tt>cRemote</tt> class assumes that any incoming remote control code can be
expressed as a character string. So whatever data your remote control provides
needs to be given to the base class by calling
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
Put(const char *Code, bool Repeat = false, bool Release = false);
</pre></td></tr></table><p>
where <tt>Code</tt> is the string representation of the remote control's
incoming data. <tt>Repeat</tt> and <tt>Release</tt> are boolean flags that
indicate whether this is a repeated keypress, or the key has been released.
Since a common case for remote control data is to be given as a numerical
value, there is another <tt>Put()</tt> function available for your convenience,
which takes a 64 bit unsigned integer value instead of a character string:
<p><table><tr><td bgcolor=#F0F0F0><pre><br>
Put(uint64 Code, bool Repeat = false, bool Release = false);
</pre></td></tr></table><p>
The other parameters have the same meaning as in the first version of this function.
<!--X1.1.11--></td></tr></table>
</body>
</html>

172
config.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.c 1.105 2002/09/04 13:45:56 kls Exp $
* $Id: config.c 1.106 2002/09/28 09:43:41 kls Exp $
*/
#include "config.h"
@ -19,169 +19,6 @@
// format characters in order to allow any number of blanks after a numeric
// value!
// -- cKeys ------------------------------------------------------------------
tKey keyTable[] = { // "Up" and "Down" must be the first two keys!
{ kUp, "Up", 0 },
{ kDown, "Down", 0 },
{ kMenu, "Menu", 0 },
{ kOk, "Ok", 0 },
{ kBack, "Back", 0 },
{ kLeft, "Left", 0 },
{ kRight, "Right", 0 },
{ kRed, "Red", 0 },
{ kGreen, "Green", 0 },
{ kYellow, "Yellow", 0 },
{ kBlue, "Blue", 0 },
{ k0, "0", 0 },
{ k1, "1", 0 },
{ k2, "2", 0 },
{ k3, "3", 0 },
{ k4, "4", 0 },
{ k5, "5", 0 },
{ k6, "6", 0 },
{ k7, "7", 0 },
{ k8, "8", 0 },
{ k9, "9", 0 },
{ kPower, "Power", 0 },
{ kVolUp, "Volume+", 0 },
{ kVolDn, "Volume-", 0 },
{ kMute, "Mute", 0 },
{ kNone, "", 0 },
};
cKeys::cKeys(void)
{
fileName = NULL;
code = 0;
address = 0;
keys = keyTable;
}
void cKeys::Clear(void)
{
for (tKey *k = keys; k->type != kNone; k++)
k->code = 0;
}
void cKeys::SetDummyValues(void)
{
for (tKey *k = keys; k->type != kNone; k++)
k->code = k->type + 1; // '+1' to avoid 0
}
bool cKeys::Load(const char *FileName)
{
isyslog("loading %s", FileName);
bool result = false;
if (FileName)
fileName = strdup(FileName);
if (fileName) {
FILE *f = fopen(fileName, "r");
if (f) {
int line = 0;
char buffer[MAXPARSEBUFFER];
result = true;
while (fgets(buffer, sizeof(buffer), f) > 0) {
line++;
if (!isempty(buffer)) {
char *Name = buffer;
char *p = strpbrk(Name, " \t");
if (p) {
*p = 0; // terminates 'Name'
while (*++p && isspace(*p))
;
if (*p) {
if (strcasecmp(Name, "Code") == 0)
code = *p;
else if (strcasecmp(Name, "Address") == 0)
address = strtol(p, NULL, 16);
else {
for (tKey *k = keys; k->type != kNone; k++) {
if (strcasecmp(Name, k->name) == 0) {
k->code = strtol(p, NULL, 16);
Name = NULL; // to indicate that we found it
break;
}
}
if (Name) {
esyslog("unknown key in %s, line %d\n", fileName, line);
result = false;
break;
}
}
}
continue;
}
esyslog("error in %s, line %d\n", fileName, line);
result = false;
break;
}
}
fclose(f);
}
else
esyslog("can't open '%s'\n", fileName);
}
else
esyslog("no key configuration file name supplied!\n");
return result;
}
bool cKeys::Save(void)
{
cSafeFile f(fileName);
if (f.Open()) {
fprintf(f, "Code\t%c\nAddress\t%04X\n", code, address);
for (tKey *k = keys; k->type != kNone; k++)
fprintf(f, "%s\t%08X\n", k->name, k->code);
return f.Close();
}
return false;
}
eKeys cKeys::Get(unsigned int Code)
{
if (Code != 0) {
tKey *k;
for (k = keys; k->type != kNone; k++) {
if (k->code == Code)
break;
}
return k->type;
}
return kNone;
}
eKeys cKeys::Translate(const char *Command)
{
if (Command) {
const tKey *k = keys;
while ((k->type != kNone) && strcasecmp(k->name, Command) != 0)
k++;
return k->type;
}
return kNone;
}
unsigned int cKeys::Encode(const char *Command)
{
eKeys k = Translate(Command);
if (k != kNone)
return keys[k].code;
return 0;
}
void cKeys::Set(eKeys Key, unsigned int Code)
{
for (tKey *k = keys; k->type != kNone; k++) {
if (k->type == Key) {
k->code = Code;
break;
}
}
}
// -- cChannel ---------------------------------------------------------------
char *cChannel::buffer = NULL;
@ -247,7 +84,6 @@ bool cChannel::Parse(const char *s)
if (*s == ':') {
if (*++s) {
strn0cpy(name, s, MaxChannelName);
name[strlen(name) - 1] = 0; // strip the '\n'
groupSep = true;
number = 0;
}
@ -696,7 +532,7 @@ bool cSVDRPhost::Parse(const char *s)
if (p) {
char *error = NULL;
int m = strtoul(p + 1, &error, 10);
if (error && !isspace(*error) || m > 32)
if (error && *error && !isspace(*error) || m > 32)
return false;
*(char *)p = 0; // yes, we know it's 'const' - will be restored!
if (m == 0)
@ -733,10 +569,6 @@ bool cCaDefinition::Parse(const char *s)
return 2 == sscanf(s, "%d %a[^\n]", &number, &description) && description && *description;
}
// -- cKeys ------------------------------------------------------------------
cKeys Keys;
// -- cCommands --------------------------------------------------------------
cCommands Commands;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: config.h 1.128 2002/09/21 08:34:25 kls Exp $
* $Id: config.h 1.129 2002/09/29 10:42:17 kls Exp $
*/
#ifndef __CONFIG_H
@ -30,69 +30,8 @@
#define MINOSDHEIGHT 12
#define MAXOSDHEIGHT 21
enum eKeys { // "Up" and "Down" must be the first two keys!
kUp,
kDown,
kMenu,
kOk,
kBack,
kLeft,
kRight,
kRed,
kGreen,
kYellow,
kBlue,
k0, k1, k2, k3, k4, k5, k6, k7, k8, k9,
kPower,
kVolUp,
kVolDn,
kMute,
kNone,
// The following flags are OR'd with the above codes:
k_Repeat = 0x8000,
k_Release = 0x4000,
k_Flags = k_Repeat | k_Release,
};
// This is in preparation for having more key codes:
#define kMarkToggle k0
#define kMarkMoveBack k4
#define kMarkMoveForward k6
#define kMarkJumpBack k7
#define kMarkJumpForward k9
#define kEditCut k2
#define kEditTest k8
#define RAWKEY(k) (eKeys((k) & ~k_Flags))
#define ISRAWKEY(k) ((k) != kNone && ((k) & k_Flags) == 0)
#define NORMALKEY(k) (eKeys((k) & ~k_Repeat))
#define MaxFileName 256
struct tKey {
eKeys type;
char *name;
unsigned int code;
};
class cKeys {
private:
char *fileName;
public:
unsigned char code;
unsigned short address;
tKey *keys;
cKeys(void);
void Clear(void);
void SetDummyValues(void);
bool Load(const char *FileName = NULL);
bool Save(void);
eKeys Translate(const char *Command);
unsigned int Encode(const char *Command);
eKeys Get(unsigned int Code);
void Set(eKeys Key, unsigned int Code);
};
#define ISTRANSPONDER(f1, f2) (abs((f1) - (f2)) < 4)
class cChannel : public cListObject {
@ -214,6 +153,7 @@ public:
template<class T> class cConfig : public cList<T> {
private:
char *fileName;
bool allowComments;
void Clear(void)
{
free(fileName);
@ -224,13 +164,17 @@ public:
cConfig(void) { fileName = NULL; }
virtual ~cConfig() { free(fileName); }
const char *FileName(void) { return fileName; }
bool Load(const char *FileName, bool AllowComments = false)
bool Load(const char *FileName = NULL, bool AllowComments = false)
{
Clear();
fileName = strdup(FileName);
if (FileName) {
free(fileName);
fileName = strdup(FileName);
allowComments = AllowComments;
}
bool result = false;
if (access(FileName, F_OK) == 0) {
isyslog("loading %s", FileName);
if (fileName && access(fileName, F_OK) == 0) {
isyslog("loading %s", fileName);
FILE *f = fopen(fileName, "r");
if (f) {
int line = 0;
@ -238,11 +182,12 @@ public:
result = true;
while (fgets(buffer, sizeof(buffer), f) > 0) {
line++;
if (AllowComments) {
if (allowComments) {
char *p = strchr(buffer, '#');
if (p)
*p = 0;
}
stripspace(buffer);
if (!isempty(buffer)) {
T *l = new T;
if (l->Parse(buffer))
@ -322,7 +267,6 @@ public:
extern cChannels Channels;
extern cTimers Timers;
extern cKeys Keys;
extern cCommands Commands;
extern cSVDRPhosts SVDRPhosts;
extern cCaDefinitions CaDefinitions;

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: device.c 1.21 2002/09/15 11:50:19 kls Exp $
* $Id: device.c 1.22 2002/09/28 12:20:22 kls Exp $
*/
#include "device.h"
@ -201,7 +201,7 @@ void cDevice::SetVideoFormat(bool VideoFormat16_9)
//#define PRINTPIDS(s) { char b[500]; char *q = b; q += sprintf(q, "%d %s ", CardIndex(), s); for (int i = 0; i < MAXPIDHANDLES; i++) q += sprintf(q, " %s%4d %d", i == ptOther ? "* " : "", pidHandles[i].pid, pidHandles[i].used); dsyslog(b); }
#define PRINTPIDS(s)
bool cDevice::HasPid(int Pid)
bool cDevice::HasPid(int Pid) const
{
for (int i = 0; i < MAXPIDHANDLES; i++) {
if (pidHandles[i].pid == Pid)
@ -275,7 +275,7 @@ bool cDevice::SetPid(cPidHandle *Handle, int Type, bool On)
return false;
}
bool cDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers)
bool cDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
{
return false;
}
@ -424,7 +424,7 @@ void cDevice::StillPicture(const uchar *Data, int Length)
{
}
bool cDevice::Replaying(void)
bool cDevice::Replaying(void) const
{
return player != NULL;
}
@ -478,7 +478,7 @@ int cDevice::PlayAudio(const uchar *Data, int Length)
return -1;
}
int cDevice::Priority(void)
int cDevice::Priority(void) const
{
int priority = IsPrimaryDevice() ? Setup.PrimaryLimit - 1 : DEFAULTPRIORITY;
for (int i = 0; i < MAXRECEIVERS; i++) {
@ -488,7 +488,7 @@ int cDevice::Priority(void)
return priority;
}
int cDevice::CanShift(int Ca, int Priority, int UsedCards)
int cDevice::CanShift(int Ca, int Priority, int UsedCards) const
{
return -1;//XXX+ too complex with multiple recordings per device
// Test whether a receiver on this device can be shifted to another one
@ -523,7 +523,7 @@ int cDevice::CanShift(int Ca, int Priority, int UsedCards)
return ShiftLevel;
}
int cDevice::ProvidesCa(int Ca)
int cDevice::ProvidesCa(int Ca) const
{
if (Ca == CardIndex() + 1)
return 1; // exactly _this_ card was requested
@ -542,7 +542,7 @@ int cDevice::ProvidesCa(int Ca)
return result ? result + others : 0;
}
bool cDevice::Receiving(void)
bool cDevice::Receiving(void) const
{
for (int i = 0; i < MAXRECEIVERS; i++) {
if (receiver[i] && receiver[i]->priority >= 0) // cReceiver with priority < 0 doesn't count

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: device.h 1.19 2002/09/21 09:14:08 kls Exp $
* $Id: device.h 1.20 2002/09/28 12:20:34 kls Exp $
*/
#ifndef __DEVICE_H
@ -111,7 +111,7 @@ public:
bool IsPrimaryDevice(void) const { return this == primaryDevice; }
int CardIndex(void) const { return cardIndex; }
// Returns the card index of this device (0 ... MAXDEVICES - 1).
int ProvidesCa(int Ca);
int ProvidesCa(int Ca) const;
// Checks whether this device provides the given value in its
// caCaps. Returns 0 if the value is not provided, 1 if only this
// value is provided, and > 1 if this and other values are provided.
@ -139,7 +139,7 @@ public:
protected:
static int currentChannel;
public:
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const;
// Returns true if this device can provide the given channel.
// In case the device has cReceivers attached to it or it is the primary
// device, Priority is used to decide whether the caller's request can
@ -186,7 +186,7 @@ protected:
cPidHandle(void) { pid = used = 0; handle = -1; }
};
cPidHandle pidHandles[MAXPIDHANDLES];
bool HasPid(int Pid);
bool HasPid(int Pid) const;
// Returns true if this device is currently receiving the given PID.
bool AddPid(int Pid, ePidType PidType = ptOther);
// Adds a PID to the set of PIDs this device shall receive.
@ -224,7 +224,7 @@ protected:
virtual void SetVolumeDevice(int Volume);
// Sets the audio volume on this device (Volume = 0...255).
public:
bool IsMute(void) { return mute; }
bool IsMute(void) const { return mute; }
bool ToggleMute(void);
// Turns the volume off or on and returns the new mute state.
void SetVolume(int Volume, bool Absolute = false);
@ -269,7 +269,7 @@ public:
// one video and one audio strem.
virtual int PlayAudio(const uchar *Data, int Length);
// Plays additional audio streams, like Dolby Digital.
bool Replaying(void);
bool Replaying(void) const;
// Returns true if we are currently replaying.
void StopReplay(void);
// Stops the current replay session (if any).
@ -285,9 +285,9 @@ public:
private:
cReceiver *receiver[MAXRECEIVERS];
int ca;
int CanShift(int Ca, int Priority, int UsedCards = 0);
int CanShift(int Ca, int Priority, int UsedCards = 0) const;
protected:
int Priority(void);
int Priority(void) const;
// Returns the priority of the current receiving session (0..MAXPRIORITY),
// or -1 if no receiver is currently active. The primary device will
// always return at least Setup.PrimaryLimit-1.
@ -304,9 +304,9 @@ protected:
// false in case of a non recoverable error, otherwise it returns true,
// even if Data is NULL.
public:
int Ca(void) { return ca; }
int Ca(void) const { return ca; }
// Returns the ca of the current receiving session.
bool Receiving(void);
bool Receiving(void) const;
// Returns true if we are currently receiving.
bool AttachReceiver(cReceiver *Receiver);
// Attaches the given receiver to this device.

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.c 1.19 2002/09/15 13:12:25 kls Exp $
* $Id: dvbdevice.c 1.20 2002/09/28 12:21:42 kls Exp $
*/
#include "dvbdevice.h"
@ -344,7 +344,7 @@ bool cDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
return true;
}
bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers)
bool cDvbDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const
{
bool result = false;
bool hasPriority = Priority < 0 || Priority > this->Priority();

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: dvbdevice.h 1.10 2002/09/08 14:05:29 kls Exp $
* $Id: dvbdevice.h 1.11 2002/09/28 12:21:36 kls Exp $
*/
#ifndef __DVBDEVICE_H
@ -63,7 +63,7 @@ public:
private:
int frequency;
public:
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL);
virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const;
protected:
virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);

18
i18n.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: i18n.c 1.92 2002/06/22 09:26:36 kls Exp $
* $Id: i18n.c 1.93 2002/09/29 12:32:46 kls Exp $
*
* Translations provided by:
*
@ -2401,6 +2401,22 @@ const tI18nPhrase Phrases[] = {
"(Apasati 'Jos' pentru terminare)",
"('Le' megnyomása a befejezéshez)",
},
{ "(press 'Menu' to skip this key)",
"('Menü' drücken zum Überspringen)",
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
"",// TODO
},
{ "Phase 3: Saving key codes",
"Phase 3: Codes abspeichern",
"Faza 3: Shranjujem kodo",

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: interface.c 1.54 2002/08/11 11:46:47 kls Exp $
* $Id: interface.c 1.55 2002/09/29 12:50:22 kls Exp $
*/
#include "interface.h"
@ -22,27 +22,14 @@ cInterface::cInterface(int SVDRPport)
open = 0;
cols[0] = 0;
width = height = 0;
keyFromWait = kNone;
interrupted = false;
rcIo = NULL;
SVDRP = NULL;
#if defined(REMOTE_RCU)
rcIo = new cRcIoRCU("/dev/ttyS1");
#elif defined(REMOTE_LIRC)
rcIo = new cRcIoLIRC("/dev/lircd");
#elif defined(REMOTE_KBD)
rcIo = new cRcIoKBD;
#else
rcIo = new cRcIoBase; // acts as a dummy device
#endif
rcIo->SetCode(Keys.code, Keys.address);
if (SVDRPport)
SVDRP = new cSVDRP(SVDRPport);
}
cInterface::~cInterface()
{
delete rcIo;
delete SVDRP;
}
@ -67,15 +54,6 @@ void cInterface::Close(void)
}
}
unsigned int cInterface::GetCh(bool Wait, bool *Repeat, bool *Release)
{
Flush();
if (!rcIo->InputAvailable())
cFile::AnyFileReady(-1, Wait ? 1000 : 0);
unsigned int Command;
return rcIo->GetCommand(&Command, Repeat, Release) ? Command : 0;
}
eKeys cInterface::GetKey(bool Wait)
{
Flush();
@ -89,22 +67,7 @@ eKeys cInterface::GetKey(bool Wait)
}
}
}
eKeys Key = keyFromWait;
if (Key == kNone) {
bool Repeat = false, Release = false;
Key = Keys.Get(GetCh(Wait, &Repeat, &Release));
if (Repeat)
Key = eKeys(Key | k_Repeat);
if (Release)
Key = eKeys(Key | k_Release);
}
keyFromWait = kNone;
return Key;
}
void cInterface::PutKey(eKeys Key)
{
keyFromWait = Key;
return cRemote::Get(Wait ? 1000 : 10);
}
eKeys cInterface::Wait(int Seconds, bool KeepChar)
@ -120,7 +83,7 @@ eKeys cInterface::Wait(int Seconds, bool KeepChar)
break;
}
if (KeepChar && ISRAWKEY(Key))
keyFromWait = Key;
cRemote::Put(Key);
interrupted = false;
return Key;
}
@ -362,129 +325,118 @@ void cInterface::Help(const char *Red, const char *Green, const char *Yellow, co
cStatus::MsgOsdHelpKeys(Red, Green, Yellow, Blue);
}
void cInterface::QueryKeys(void)
void cInterface::QueryKeys(cRemote *Remote)
{
Keys.Clear();
cRemote::Clear();
Clear();
WriteText(1, 1, tr("Learning Remote Control Keys"));
WriteText(1, 1, tr("Learning Remote Control Keys"));//XXX Remote->name()!!!
WriteText(1, 3, tr("Phase 1: Detecting RC code type"));
WriteText(1, 5, tr("Press any key on the RC unit"));
Flush();
#ifndef REMOTE_KBD
unsigned char Code = '0';
unsigned short Address;
#endif
for (;;) {
#ifdef REMOTE_KBD
if (GetCh())
break;
#else
//TODO on screen display...
if (rcIo->DetectCode(&Code, &Address)) {
Keys.code = Code;
Keys.address = Address;
WriteText(1, 5, tr("RC code detected!"));
WriteText(1, 6, tr("Do not press any key..."));
Flush();
rcIo->Flush(3000);
ClearEol(0, 5);
ClearEol(0, 6);
Flush();
break;
}
#endif
}
WriteText(1, 3, tr("Phase 2: Learning specific key codes"));
tKey *k = Keys.keys;
while (k->type != kNone) {
char *Prompt;
asprintf(&Prompt, tr("Press key for '%s'"), tr(k->name));
WriteText(1, 5, Prompt);
free(Prompt);
for (;;) {
unsigned int ch = GetCh();
if (ch != 0) {
switch (Keys.Get(ch)) {
case kUp: if (k > Keys.keys) {
k--;
break;
}
case kDown: if (k > Keys.keys + 1) {
WriteText(1, 5, tr("Press 'Up' to confirm"));
WriteText(1, 6, tr("Press 'Down' to continue"));
ClearEol(0, 7);
ClearEol(0, 8);
for (;;) {
eKeys key = GetKey();
if (key == kUp) {
Clear();
return;
}
else if (key == kDown) {
ClearEol(0, 6);
break;
}
if (Remote->Initialize()) {
WriteText(1, 5, tr("RC code detected!"));
WriteText(1, 6, tr("Do not press any key..."));
Flush();
sleep(3);
ClearEol(0, 5);
ClearEol(0, 6);
WriteText(1, 3, tr("Phase 2: Learning specific key codes"));
eKeys NewKey = kUp;
while (NewKey != kNone) {
char *Prompt;
asprintf(&Prompt, tr("Press key for '%s'"), tr(cKey::ToString(NewKey)));
WriteText(1, 5, Prompt);
free(Prompt);
cRemote::Clear();
Flush();
for (eKeys k = NewKey; k == NewKey; ) {
char *NewCode = NULL;
eKeys Key = cRemote::Get(100, &NewCode);
switch (Key) {
case kUp: {
NewKey = eKeys(NewKey - 1);
cKey *last = Keys.Last();
if (last && last->Key() == NewKey)
Keys.Del(last);
}
break;
case kDown: WriteText(1, 5, tr("Press 'Up' to confirm"));
WriteText(1, 6, tr("Press 'Down' to continue"));
ClearEol(0, 7);
ClearEol(0, 8);
ClearEol(0, 9);
for (;;) {
Key = cRemote::Get(100);
if (Key == kUp) {
Clear();
return;
}
break;
else if (Key == kDown) {
ClearEol(0, 6);
k = kNone; // breaks the outer for() loop
break;
}
}
break;
case kMenu: NewKey = eKeys(NewKey + 1);
break;
case kNone: if (NewCode) {
dsyslog("new %s code: %s = %s", Remote->Name(), NewCode, cKey::ToString(NewKey));
Keys.Add(new cKey(Remote->Name(), NewCode, NewKey));
NewKey = eKeys(NewKey + 1);
free(NewCode);
}
case kNone: k->code = ch;
k++;
break;
default: break;
}
break;
}
}
if (k > Keys.keys)
WriteText(1, 7, tr("(press 'Up' to go back)"));
else
ClearEol(0, 7);
if (k > Keys.keys + 1)
WriteText(1, 8, tr("(press 'Down' to end key definition)"));
else
ClearEol(0, 8);
}
if (NewKey > kUp)
WriteText(1, 7, tr("(press 'Up' to go back)"));
else
ClearEol(0, 7);
if (NewKey > kDown)
WriteText(1, 8, tr("(press 'Down' to end key definition)"));
else
ClearEol(0, 8);
if (NewKey > kMenu)
WriteText(1, 9, tr("(press 'Menu' to skip this key)"));
else
ClearEol(0, 9);
}
}
}
void cInterface::LearnKeys(void)
{
isyslog("learning keys");
Open();
for (;;) {
Clear();
QueryKeys();
Clear();
WriteText(1, 1, tr("Learning Remote Control Keys"));
WriteText(1, 3, tr("Phase 3: Saving key codes"));
WriteText(1, 5, tr("Press 'Up' to save, 'Down' to cancel"));
for (;;) {
eKeys key = GetKey();
if (key == kUp) {
Keys.Save();
Close();
return;
for (cRemote *Remote = Remotes.First(); Remote; Remote = Remotes.Next(Remote)) {
bool known = Keys.KnowsRemote(Remote->Name());
dsyslog("remote control %s - %s", Remote->Name(), known ? "keys known" : "learning keys");
if (!known) {
Open();
for (;;) {
Clear();
cRemote::SetLearning(true);
QueryKeys(Remote);
cRemote::SetLearning(false);
Clear();
WriteText(1, 1, tr("Learning Remote Control Keys"));//XXX Remote->name()!!!
WriteText(1, 3, tr("Phase 3: Saving key codes"));
WriteText(1, 5, tr("Press 'Up' to save, 'Down' to cancel"));
for (;;) {
eKeys key = GetKey();
if (key == kUp) {
Keys.Save();
Close();
return;
}
else if (key == kDown) {
Keys.Load();
Close();
return;
}
}
}
else if (key == kDown) {
Keys.Load();
Close();
return;
}
}
}
}
}
void cInterface::DisplayChannelNumber(int Number)
{
rcIo->Number(Number);
}
void cInterface::DisplayRecording(int Index, bool On)
{
rcIo->SetPoints(1 << Index, On);
}
bool cInterface::Recording(void)
{
// This is located here because the Interface has to do with the "PrimaryDevice" anyway
return cDevice::PrimaryDevice()->Receiving();
}

View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: interface.h 1.27 2002/06/22 14:39:48 kls Exp $
* $Id: interface.h 1.28 2002/09/29 10:46:50 kls Exp $
*/
#ifndef __INTERFACE_H
@ -22,12 +22,9 @@ private:
int width, height;
int open;
int cols[MaxCols];
eKeys keyFromWait;
bool interrupted;
cSVDRP *SVDRP;
cRcIoBase *rcIo;
unsigned int GetCh(bool Wait = true, bool *Repeat = NULL, bool *Release = NULL);
void QueryKeys(void);
void QueryKeys(cRemote *Remote);
void HelpButton(int Index, const char *Text, eDvbColor FgColor, eDvbColor BgColor);
eKeys Wait(int Seconds = 0, bool KeepChar = false);
public:
@ -40,7 +37,6 @@ public:
int Width(void) { return width; }
int Height(void) { return height; }
eKeys GetKey(bool Wait = true);
void PutKey(eKeys Key);
void Clear(void);
void ClearEol(int x, int y, eDvbColor Color = clrBackground);
void Fill(int x, int y, int w, int h, eDvbColor color = clrBackground);
@ -59,9 +55,6 @@ public:
bool Confirm(const char *s, int Seconds = 10, bool WaitForTimeout = false);
void Help(const char *Red, const char *Green = NULL, const char *Yellow = NULL, const char *Blue = NULL);
void LearnKeys(void);
void DisplayChannelNumber(int Number);
void DisplayRecording(int Index, bool On);
bool Recording(void);
};
extern cInterface *Interface;

View File

@ -1,27 +0,0 @@
Code 0
Address 0000
Up 00000103
Down 00000102
Menu 00000106
Ok 0000000D
Back 00000168
Left 00000104
Right 00000105
Red 00000109
Green 0000010A
Yellow 0000010B
Blue 0000010C
0 00000030
1 00000031
2 00000032
3 00000033
4 00000034
5 00000035
6 00000036
7 00000037
8 00000038
9 00000039
Power 00000050
Volume+ 0000002B
Volume- 0000002D
Mute 0000006D

154
keys.c Normal file
View File

@ -0,0 +1,154 @@
/*
* keys.c: Remote control Key handling
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: keys.c 1.1 2002/09/29 09:56:51 kls Exp $
*/
#include "keys.h"
static tKey keyTable[] = { // "Up" and "Down" must be the first two keys!
{ kUp, "Up" },
{ kDown, "Down" },
{ kMenu, "Menu" },
{ kOk, "Ok" },
{ kBack, "Back" },
{ kLeft, "Left" },
{ kRight, "Right" },
{ kRed, "Red" },
{ kGreen, "Green" },
{ kYellow, "Yellow" },
{ kBlue, "Blue" },
{ k0, "0" },
{ k1, "1" },
{ k2, "2" },
{ k3, "3" },
{ k4, "4" },
{ k5, "5" },
{ k6, "6" },
{ k7, "7" },
{ k8, "8" },
{ k9, "9" },
{ kPower, "Power" },
{ kVolUp, "Volume+" },
{ kVolDn, "Volume-" },
{ kMute, "Mute" },
{ kNone, "" },
{ k_Setup, "_Setup" },
{ kNone, NULL },
};
// -- cKey -------------------------------------------------------------------
cKey::cKey(void)
{
remote = code = NULL;
key = kNone;
}
cKey::cKey(const char *Remote, const char *Code, eKeys Key)
{
remote = strdup(Remote);
code = strdup(Code);
key = Key;
}
cKey::~cKey()
{
free(remote);
free(code);
}
bool cKey::Parse(char *s)
{
char *p = strchr(s, '.');
if (p) {
*p++ = 0;
remote = strdup(s);
char *q = strpbrk(p, " \t");
if (q) {
*q++ = 0;
key = FromString(p);
if (key != kNone) {
q = skipspace(q);
if (*q) {
code = strdup(q);
return true;
}
}
}
}
return false;
}
bool cKey::Save(FILE *f)
{
return fprintf(f, "%s.%-10s %s\n", remote, ToString(key), code) > 0;
}
eKeys cKey::FromString(const char *Name)
{
if (Name) {
for (tKey *k = keyTable; k->name; k++) {
if (strcasecmp(k->name, Name) == 0)
return k->type;
}
}
return kNone;
}
const char *cKey::ToString(eKeys Key)
{
for (tKey *k = keyTable; k->name; k++) {
if (k->type == Key)
return k->name;
}
return NULL;
}
// -- cKeys ------------------------------------------------------------------
cKeys Keys;
bool cKeys::KnowsRemote(const char *Remote)
{
if (Remote) {
for (cKey *k = First(); k; k = Next(k)) {
if (strcmp(Remote, k->Remote()) == 0)
return true;
}
}
return false;
}
eKeys cKeys::Get(const char *Remote, const char *Code)
{
if (Remote && Code) {
for (cKey *k = First(); k; k = Next(k)) {
if (strcmp(Remote, k->Remote()) == 0 && strcmp(Code, k->Code()) == 0)
return k->Key();
}
}
return kNone;
}
const char *cKeys::GetSetup(const char *Remote)
{
if (Remote) {
for (cKey *k = First(); k; k = Next(k)) {
if (strcmp(Remote, k->Remote()) == 0 && k->Key() == k_Setup)
return k->Code();
}
}
return NULL;
}
void cKeys::PutSetup(const char *Remote, const char *Setup)
{
if (!GetSetup(Remote))
Add(new cKey(Remote, Setup, k_Setup));
else
esyslog("ERROR: called PutSetup() for %s, but setup has already been defined!", Remote);
}

87
keys.h Normal file
View File

@ -0,0 +1,87 @@
/*
* keys.h: Remote control Key handling
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: keys.h 1.1 2002/09/29 09:55:52 kls Exp $
*/
#ifndef __KEYS_H
#define __KEYS_H
#include "config.h"
#include "tools.h"
enum eKeys { // "Up" and "Down" must be the first two keys!
kUp,
kDown,
kMenu,
kOk,
kBack,
kLeft,
kRight,
kRed,
kGreen,
kYellow,
kBlue,
k0, k1, k2, k3, k4, k5, k6, k7, k8, k9,
kPower,
kVolUp,
kVolDn,
kMute,
kNone,
k_Setup,
// The following flags are OR'd with the above codes:
k_Repeat = 0x8000,
k_Release = 0x4000,
k_Flags = k_Repeat | k_Release,
};
// This is in preparation for having more key codes:
#define kMarkToggle k0
#define kMarkMoveBack k4
#define kMarkMoveForward k6
#define kMarkJumpBack k7
#define kMarkJumpForward k9
#define kEditCut k2
#define kEditTest k8
#define RAWKEY(k) (eKeys((k) & ~k_Flags))
#define ISRAWKEY(k) ((k) != kNone && ((k) & k_Flags) == 0)
#define NORMALKEY(k) (eKeys((k) & ~k_Repeat))
struct tKey {
eKeys type;
char *name;
};
class cKey : public cListObject {
private:
char *remote;
char *code;
eKeys key;
public:
cKey(void);
cKey(const char *Remote, const char *Code, eKeys Key);
~cKey();
const char *Remote(void) { return remote; }
const char *Code(void) { return code; }
eKeys Key(void) { return key; }
bool Parse(char *s);
bool Save(FILE *f);
static eKeys FromString(const char *Name);
static const char *ToString(eKeys Key);
};
class cKeys : public cConfig<cKey> {
public:
bool KnowsRemote(const char *Remote);
eKeys Get(const char *Remote, const char *Code);
const char *GetSetup(const char *Remote);
void PutSetup(const char *Remote, const char *Setup);
};
extern cKeys Keys;
#endif //__KEYS_H

84
lirc.c Normal file
View File

@ -0,0 +1,84 @@
/*
* lirc.c: LIRC remote control
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* LIRC support added by Carsten Koch <Carsten.Koch@icem.de> 2000-06-16.
*
* $Id: lirc.c 1.1 2002/09/29 13:16:33 kls Exp $
*/
#include "lirc.h"
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
#define REPEATLIMIT 20 // ms
#define REPEATDELAY 350 // ms
cLircRemote::cLircRemote(char *DeviceName)
:cRemote("LIRC")
{
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, DeviceName);
if ((f = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
if (connect(f, (struct sockaddr *)&addr, sizeof(addr)) >= 0) {
Start();
return;
}
LOG_ERROR_STR(DeviceName);
close(f);
}
else
LOG_ERROR_STR(DeviceName);
f = -1;
}
cLircRemote::~cLircRemote()
{
Cancel();
}
void cLircRemote::Action(void)
{
dsyslog("LIRC remote control thread started (pid=%d)", getpid());
int FirstTime = 0;
int LastTime = 0;
char buf[LIRC_BUFFER_SIZE];
char LastKeyName[LIRC_KEY_BUF] = "";
bool repeat = false;
for (; f >= 0;) {
LOCK_THREAD;
if (cFile::FileReady(f, REPEATLIMIT) && safe_read(f, buf, sizeof(buf)) > 21) {
int count;
char KeyName[LIRC_KEY_BUF];
sscanf(buf, "%*x %x %29s", &count, KeyName); // '29' in '%29s' is LIRC_KEY_BUF-1!
int Now = time_ms();
if (count == 0) {
strcpy(LastKeyName, KeyName);
repeat = false;
FirstTime = Now;
}
else {
if (Now - FirstTime < REPEATDELAY)
continue; // repeat function kicks in after a short delay
repeat = true;
}
LastTime = Now;
Put(KeyName, repeat);
}
else if (repeat) { // the last one was a repeat, so let's generate a release
if (time_ms() - LastTime > REPEATDELAY) {
Put(LastKeyName, false, true);
repeat = false;
*LastKeyName = 0;
}
}
}
}

26
lirc.h Normal file
View File

@ -0,0 +1,26 @@
/*
* lirc.h: LIRC remote control
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: lirc.h 1.1 2002/09/28 14:11:04 kls Exp $
*/
#ifndef __LIRC_H
#define __LIRC_H
#include "remote.h"
#include "thread.h"
class cLircRemote : public cRemote, private cThread {
private:
enum { LIRC_KEY_BUF = 30, LIRC_BUFFER_SIZE = 128 };
int f;
virtual void Action(void);
public:
cLircRemote(char *DeviceName);
virtual ~cLircRemote();
};
#endif //__LIRC_H

20
menu.c
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menu.c 1.208 2002/09/06 14:07:58 kls Exp $
* $Id: menu.c 1.209 2002/09/29 12:50:47 kls Exp $
*/
#include "menu.h"
@ -20,6 +20,7 @@
#include "menuitems.h"
#include "plugin.h"
#include "recording.h"
#include "remote.h"
#include "status.h"
#include "videodir.h"
@ -2121,7 +2122,7 @@ cDisplayChannel::cDisplayChannel(int Number, bool Switched)
withInfo = !Switched || Setup.ShowInfoOnChSwitch;
int EpgLines = withInfo ? 5 : 1;
lines = 0;
oldNumber = number = 0;
number = 0;
cChannel *channel = Channels.GetByNumber(Number);
Interface->Open(Setup.OSDwidth, Setup.ChannelInfoPos ? EpgLines : -EpgLines);
if (channel) {
@ -2135,7 +2136,6 @@ cDisplayChannel::cDisplayChannel(eKeys FirstKey)
:cOsdObject(true)
{
group = -1;
oldNumber = cDevice::CurrentChannel();
number = 0;
lastTime = time_ms();
int EpgLines = Setup.ShowInfoOnChSwitch ? 5 : 1;
@ -2145,15 +2145,11 @@ cDisplayChannel::cDisplayChannel(eKeys FirstKey)
cDisplayChannel::~cDisplayChannel()
{
if (number < 0)
Interface->DisplayChannelNumber(oldNumber);
Interface->Close();
}
void cDisplayChannel::DisplayChannel(const cChannel *Channel)
{
if (Channel && Channel->number > 0)
Interface->DisplayChannelNumber(Channel->number);
int BufSize = Width() + 1;
char buffer[BufSize];
if (Channel && Channel->number > 0)
@ -2231,7 +2227,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
case k0:
if (number == 0) {
// keep the "Toggle channels" function working
Interface->PutKey(Key);
cRemote::Put(Key);
return osEnd;
}
case k1 ... k9:
@ -2290,7 +2286,7 @@ eOSState cDisplayChannel::ProcessKey(eKeys Key)
Channels.SwitchTo(Channels.Get(Channels.GetNextNormal(group))->number);
return osEnd;
default: if (NORMALKEY(Key) == kUp || NORMALKEY(Key) == kDown || (Key & (k_Repeat | k_Release)) == 0) {
Interface->PutKey(Key);
cRemote::Put(Key);
return osEnd;
}
};
@ -2397,7 +2393,7 @@ eOSState cDisplayVolume::ProcessKey(eKeys Key)
break;
case kNone: break;
default: if ((Key & k_Release) == 0) {
Interface->PutKey(Key);
cRemote::Put(Key);
return osEnd;
}
}
@ -2441,7 +2437,6 @@ cRecordControl::cRecordControl(cDevice *Device, cTimer *Timer)
if (device->AttachReceiver(recorder)) {
Recording.WriteSummary();
cStatus::MsgRecording(device, Recording.Name());
Interface->DisplayRecording(device->CardIndex(), true);
}
else
DELETENULL(recorder);
@ -2487,7 +2482,6 @@ bool cRecordControl::GetEventInfo(void)
void cRecordControl::Stop(bool KeepInstant)
{
if (timer) {
cStatus::MsgRecording(device, NULL);
DELETENULL(recorder);
timer->SetRecording(false);
if ((IsInstant() && !KeepInstant) || (timer->IsSingleEvent() && timer->StopTime() <= time(NULL))) {
@ -2496,7 +2490,7 @@ void cRecordControl::Stop(bool KeepInstant)
Timers.Save();
}
timer = NULL;
Interface->DisplayRecording(device->CardIndex(), false);
cStatus::MsgRecording(device, NULL);
cRecordingUserCommand::InvokeCommand(RUC_AFTERRECORDING, fileName);
}
}

4
menu.h
View File

@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: menu.h 1.45 2002/06/22 14:49:15 kls Exp $
* $Id: menu.h 1.46 2002/09/29 08:16:31 kls Exp $
*/
#ifndef __MENU_H
@ -32,7 +32,7 @@ private:
bool withInfo;
int lines;
int lastTime;
int oldNumber, number;
int number;
void DisplayChannel(const cChannel *Channel);
void DisplayInfo(void);
public:

312
rcu.c Normal file
View File

@ -0,0 +1,312 @@
/*
* rcu.c: RCU remote control
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: rcu.c 1.1 2002/09/29 13:16:44 kls Exp $
*/
#include "rcu.h"
#include <netinet/in.h>
#include <termios.h>
#include <unistd.h>
#include "tools.h"
#define REPEATLIMIT 20 // ms
#define REPEATDELAY 350 // ms
cRcuRemote::cRcuRemote(char *DeviceName)
:cRemote("RCU")
{
dp = 0;
mode = modeB;
code = 0;
lastNumber = 0;
receivedCommand = false;
if ((f = open(DeviceName, O_RDWR | O_NONBLOCK)) >= 0) {
struct termios t;
if (tcgetattr(f, &t) == 0) {
cfsetspeed(&t, B9600);
cfmakeraw(&t);
if (tcsetattr(f, TCSAFLUSH, &t) == 0) {
Number(0);//XXX 8888???
const char *Setup = GetSetup();
if (Setup) {
code = *Setup;
SetCode(code);
isyslog("connecting to %s remote control using code %c", Name(), code);
}
Start();
return;
}
}
LOG_ERROR_STR(DeviceName);
close(f);
}
else
LOG_ERROR_STR(DeviceName);
f = -1;
}
cRcuRemote::~cRcuRemote()
{
Cancel();
}
bool cRcuRemote::Initialize(void)
{
if (f >= 0) {
unsigned char Code = '0';
isyslog("trying codes for %s remote control...", Name());
for (;;) {
if (DetectCode(&Code)) {
code = Code;
break;
}
}
isyslog("established connection to %s remote control using code %c", Name(), code);
char buffer[16];
snprintf(buffer, sizeof(buffer), "%c", code);
PutSetup(buffer);
return true;
}
return false;
}
void cRcuRemote::Action(void)
{
#pragma pack(1)
union {
struct {
unsigned short address;
unsigned int command;
} data;
unsigned char raw[6];
} buffer;
#pragma pack()
dsyslog("RCU remote control thread started (pid=%d)", getpid());
time_t LastCodeRefresh = 0;
int FirstTime = 0;
uint64 LastCommand = 0;
bool repeat = false;
//XXX
for (; f >= 0;) {
LOCK_THREAD;
if (ReceiveByte(REPEATLIMIT) == 'X') {
for (int i = 0; i < 6; i++) {
int b = ReceiveByte();
if (b >= 0) {
buffer.raw[i] = b;
if (i == 5) {
unsigned short Address = ntohs(buffer.data.address); // the PIC sends bytes in "network order"
uint64 Command = ntohl(buffer.data.command);
if (code == 'B' && Address == 0x0000 && Command == 0x00004000)
// Well, well, if it isn't the "d-box"...
// This remote control sends the above command before and after
// each keypress - let's just drop this:
break;
int Now = time_ms();
Command |= uint64(Address) << 32;
if (Command != LastCommand) {
LastCommand = Command;
repeat = false;
FirstTime = Now;
}
else {
if (Now - FirstTime < REPEATDELAY)
break; // repeat function kicks in after a short delay
repeat = true;
}
Put(Command, repeat);
receivedCommand = true;
}
}
else
break;
}
}
else if (repeat) { // the last one was a repeat, so let's generate a release
Put(LastCommand, false, true);
repeat = false;
LastCommand = 0;
}
else
LastCommand = 0;
if (code && time(NULL) - LastCodeRefresh > 60) {
SendCommand(code); // in case the PIC listens to the wrong code
LastCodeRefresh = time(NULL);
}
}
}
int cRcuRemote::ReceiveByte(int TimeoutMs)
{
// Returns the byte if one was received within a timeout, -1 otherwise
if (cFile::FileReady(f, TimeoutMs)) {
unsigned char b;
if (safe_read(f, &b, 1) == 1)
return b;
else
LOG_ERROR;
}
return -1;
}
bool cRcuRemote::SendByteHandshake(unsigned char c)
{
if (f >= 0) {
int w = write(f, &c, 1);
if (w == 1) {
for (int reply = ReceiveByte(REPEATLIMIT); reply >= 0;) {
if (reply == c)
return true;
else if (reply == 'X') {
// skip any incoming RC code - it will come again
for (int i = 6; i--;) {
if (ReceiveByte() < 0)
return false;
}
}
else
return false;
}
}
LOG_ERROR;
}
return false;
}
bool cRcuRemote::SendByte(unsigned char c)
{
LOCK_THREAD;
for (int retry = 5; retry--;) {
if (SendByteHandshake(c))
return true;
}
return false;
}
bool cRcuRemote::SetCode(unsigned char Code)
{
code = Code;
return SendCommand(code);
}
bool cRcuRemote::SetMode(unsigned char Mode)
{
mode = Mode;
return SendCommand(mode);
}
bool cRcuRemote::SendCommand(unsigned char Cmd)
{
return SendByte(Cmd | 0x80);
}
bool cRcuRemote::Digit(int n, int v)
{
return SendByte(((n & 0x03) << 5) | (v & 0x0F) | (((dp >> n) & 0x01) << 4));
}
bool cRcuRemote::Number(int n, bool Hex)
{
LOCK_THREAD;
if (!Hex) {
char buf[8];
sprintf(buf, "%4d", n & 0xFFFF);
n = 0;
for (char *d = buf; *d; d++) {
if (*d == ' ')
*d = 0xF;
n = (n << 4) | ((*d - '0') & 0x0F);
}
}
lastNumber = n;
for (int i = 0; i < 4; i++) {
if (!Digit(i, n))
return false;
n >>= 4;
}
return SendCommand(mode);
}
bool cRcuRemote::String(char *s)
{
LOCK_THREAD;
const char *chars = mode == modeH ? "0123456789ABCDEF" : "0123456789-EHLP ";
int n = 0;
for (int i = 0; *s && i < 4; s++, i++) {
n <<= 4;
for (const char *c = chars; *c; c++) {
if (*c == *s) {
n |= c - chars;
break;
}
}
}
return Number(n, true);
}
void cRcuRemote::SetPoints(unsigned char Dp, bool On)
{
if (On)
dp |= Dp;
else
dp &= ~Dp;
Number(lastNumber, true);
}
bool cRcuRemote::DetectCode(unsigned char *Code)
{
// Caller should initialize 'Code' to 0 and call DetectCode()
// until it returns true. Whenever DetectCode() returns false
// and 'Code' is not 0, the caller can use 'Code' to display
// a message like "Trying code '%c'". If false is returned and
// 'Code' is 0, all possible codes have been tried and the caller
// can either stop calling DetectCode() (and give some error
// message), or start all over again.
if (*Code < 'A' || *Code > 'D') {
*Code = 'A';
return false;
}
if (*Code <= 'D') {
SetMode(modeH);
char buf[5];
sprintf(buf, "C0D%c", *Code);
String(buf);
SetCode(*Code);
delay_ms(2 * REPEATDELAY);
if (receivedCommand) {
SetMode(modeB);
String("----");
return true;
}
if (*Code < 'D') {
(*Code)++;
return false;
}
}
*Code = 0;
return false;
}
void cRcuRemote::ChannelSwitch(const cDevice *Device, int ChannelNumber)
{
if (ChannelNumber && Device->IsPrimaryDevice())
Number(ChannelNumber);
}
void cRcuRemote::Recording(const cDevice *Device, const char *Name)
{
SetPoints(1 << Device->CardIndex(), Device->Receiving()); //XXX CardNumber()!!!
}

44
rcu.h Normal file
View File

@ -0,0 +1,44 @@
/*
* rcu.h: RCU remote control
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: rcu.h 1.1 2002/09/29 08:56:15 kls Exp $
*/
#ifndef __RCU_H
#define __RCU_H
#include "remote.h"
#include "status.h"
#include "thread.h"
class cRcuRemote : public cRemote, private cThread, private cStatus {
private:
enum { modeH = 'h', modeB = 'b', modeS = 's' };
int f;
unsigned char dp, code, mode;
int lastNumber;
bool receivedCommand;
bool SendCommand(unsigned char Cmd);
int ReceiveByte(int TimeoutMs = 0);
bool SendByteHandshake(unsigned char c);
bool SendByte(unsigned char c);
bool Digit(int n, int v);
bool SetCode(unsigned char Code);
bool SetMode(unsigned char Mode);
bool Number(int n, bool Hex = false);
void SetPoints(unsigned char Dp, bool On);
bool String(char *s);
bool DetectCode(unsigned char *Code);
virtual void Action(void);
virtual void ChannelSwitch(const cDevice *Device, int ChannelNumber);
virtual void Recording(const cDevice *Device, const char *Name);
public:
cRcuRemote(char *DeviceName);
virtual ~cRcuRemote();
virtual bool Initialize(void);
};
#endif //__RCU_H

581
remote.c
View File

@ -1,12 +1,10 @@
/*
* remote.c: Interface to the Remote Control Unit
* remote.c: General Remote Control handling
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* Ported to LIRC by Carsten Koch <Carsten.Koch@icem.de> 2000-06-16.
*
* $Id: remote.c 1.27 2002/05/18 12:55:39 kls Exp $
* $Id: remote.c 1.28 2002/09/29 12:51:26 kls Exp $
*/
#include "remote.h"
@ -18,487 +16,150 @@
#include <termios.h>
#include <unistd.h>
#if defined REMOTE_LIRC
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#endif
#if defined REMOTE_KBD
#include <ncurses.h>
#endif
#include "config.h"
#include "tools.h"
// --- cRcIoBase -------------------------------------------------------------
// --- cRemote ---------------------------------------------------------------
cRcIoBase::cRcIoBase(void)
eKeys cRemote::keys[MaxKeys];
int cRemote::in = 0;
int cRemote::out = 0;
bool cRemote::learning = false;
char *cRemote::unknownCode = NULL;
cMutex cRemote::mutex;
cCondVar cRemote::keyPressed;
cRemote::cRemote(const char *Name)
{
t = 0;
if (Name)
name = strdup(Name);
Remotes.Add(this);
}
cRcIoBase::~cRcIoBase()
cRemote::~cRemote()
{
free(name);
}
// --- cRcIoKBD --------------------------------------------------------------
const char *cRemote::GetSetup(void)
{
return Keys.GetSetup(Name());
}
void cRemote::PutSetup(const char *Setup)
{
Keys.PutSetup(Name(), Setup);
}
void cRemote::Clear(void)
{
cMutexLock MutexLock(&mutex);
in = out = 0;
if (learning) {
free(unknownCode);
unknownCode = NULL;
}
}
bool cRemote::Put(eKeys Key)
{
if (Key != kNone) {
cMutexLock MutexLock(&mutex);
if ((Key & k_Release) != 0)
Clear();
int d = out - in;
if (d <= 0)
d = MaxKeys + d;
if (d - 1 > 0) {
keys[in] = Key;
if (++in >= MaxKeys)
in = 0;
keyPressed.Broadcast();
return true;
}
return false;
}
return true; // only a real key shall report an overflow!
}
bool cRemote::Put(uint64 Code, bool Repeat, bool Release)
{
char buffer[32];
snprintf(buffer, sizeof(buffer), "%016LX", Code);
return Put(buffer, Repeat, Release);
}
bool cRemote::Put(const char *Code, bool Repeat, bool Release)
{
eKeys Key = Keys.Get(Name(), Code);
if (Key != kNone) {
if (Repeat)
Key = eKeys(Key | k_Repeat);
if (Release)
Key = eKeys(Key | k_Release);
return Put(Key);
}
if (learning) {
free(unknownCode);
unknownCode = strdup(Code);
keyPressed.Broadcast();
}
return false;
}
eKeys cRemote::Get(int WaitMs, char **UnknownCode)
{
for (;;) {
cMutexLock MutexLock(&mutex);
if (in != out) {
eKeys k = keys[out];
if (++out >= MaxKeys)
out = 0;
return k;
}
else if (!WaitMs || !keyPressed.TimedWait(mutex, WaitMs)) {
if (learning && UnknownCode) {
*UnknownCode = unknownCode;
unknownCode = NULL;
}
return kNone;
}
}
}
// --- cRemotes --------------------------------------------------------------
cRemotes Remotes;
// --- cKbdRemote ------------------------------------------------------------
#if defined REMOTE_KBD
cRcIoKBD::cRcIoKBD(void)
cKbdRemote::cKbdRemote(void)
:cRemote("KBD")
{
f.Open(0); // stdin
Start();
}
cRcIoKBD::~cRcIoKBD()
{
}
void cRcIoKBD::Flush(int WaitMs)
{
int t0 = time_ms();
timeout(10);
for (;;) {
while (getch() > 0)
t0 = time_ms();
if (time_ms() - t0 >= WaitMs)
break;
}
}
bool cRcIoKBD::InputAvailable(void)
{
return f.Ready(false);
}
bool cRcIoKBD::GetCommand(unsigned int *Command, bool *Repeat, bool *Release)
{
if (Command) {
*Command = getch();
return int(*Command) > 0;
}
return false;
}
// --- cRcIoRCU --------------------------------------------------------------
#elif defined REMOTE_RCU
#define REPEATLIMIT 20 // ms
#define REPEATDELAY 350 // ms
cRcIoRCU::cRcIoRCU(char *DeviceName)
{
dp = 0;
mode = modeB;
code = 0;
address = 0xFFFF;
receivedAddress = 0;
receivedCommand = 0;
receivedData = receivedRepeat = receivedRelease = false;
lastNumber = 0;
if ((f = open(DeviceName, O_RDWR | O_NONBLOCK)) >= 0) {
struct termios t;
if (tcgetattr(f, &t) == 0) {
cfsetspeed(&t, B9600);
cfmakeraw(&t);
if (tcsetattr(f, TCSAFLUSH, &t) == 0) {
Start();
return;
}
}
LOG_ERROR_STR(DeviceName);
close(f);
}
else
LOG_ERROR_STR(DeviceName);
f = -1;
}
cRcIoRCU::~cRcIoRCU()
cKbdRemote::~cKbdRemote()
{
Cancel();
}
void cRcIoRCU::Action(void)
void cKbdRemote::Action(void)
{
#pragma pack(1)
union {
struct {
unsigned short address;
unsigned int command;
} data;
unsigned char raw[6];
} buffer;
#pragma pack()
dsyslog("RCU remote control thread started (pid=%d)", getpid());
int FirstTime = 0;
unsigned int LastCommand = 0;
for (; f >= 0;) {
LOCK_THREAD;
if (ReceiveByte(REPEATLIMIT) == 'X') {
for (int i = 0; i < 6; i++) {
int b = ReceiveByte();
if (b >= 0) {
buffer.raw[i] = b;
if (i == 5) {
unsigned short Address = ntohs(buffer.data.address); // the PIC sends bytes in "network order"
unsigned int Command = ntohl(buffer.data.command);
if (code == 'B' && address == 0x0000 && Command == 0x00004000)
// Well, well, if it isn't the "d-box"...
// This remote control sends the above command before and after
// each keypress - let's just drop this:
break;
if (!receivedData) { // only accept new data the previous data has been fetched
int Now = time_ms();
if (Command != LastCommand) {
receivedAddress = Address;
receivedCommand = Command;
receivedData = true;
receivedRepeat = receivedRelease = false;
FirstTime = Now;
}
else {
if (Now - FirstTime < REPEATDELAY)
break; // repeat function kicks in after a short delay
receivedData = receivedRepeat = true;
}
LastCommand = Command;
WakeUp();
}
}
}
else
break;
}
}
else if (receivedData) { // the last data before releasing the key hasn't been fetched yet
if (receivedRepeat) { // it was a repeat, so let's make it a release
receivedRepeat = false;
receivedRelease = true;
LastCommand = 0;
WakeUp();
}
}
else if (receivedRepeat) { // all data has already been fetched, but the last one was a repeat, so let's generate a release
receivedData = receivedRelease = true;
receivedRepeat = false;
LastCommand = 0;
WakeUp();
}
else
LastCommand = 0;
dsyslog("KBD remote control thread started (pid=%d)", getpid());
cPoller Poller(STDIN_FILENO);
for (;;) {//XXX
int Command = getch();
if (Command != EOF)
Put(Command);
Poller.Poll(100);
}
}
int cRcIoRCU::ReceiveByte(int TimeoutMs)
{
// Returns the byte if one was received within a timeout, -1 otherwise
if (cFile::FileReady(f, TimeoutMs)) {
unsigned char b;
if (safe_read(f, &b, 1) == 1)
return b;
else
LOG_ERROR;
}
return -1;
}
bool cRcIoRCU::SendByteHandshake(unsigned char c)
{
if (f >= 0) {
int w = write(f, &c, 1);
if (w == 1) {
for (int reply = ReceiveByte(REPEATLIMIT); reply >= 0;) {
if (reply == c)
return true;
else if (reply == 'X') {
// skip any incoming RC code - it will come again
for (int i = 6; i--;) {
if (ReceiveByte() < 0)
return false;
}
}
else
return false;
}
}
LOG_ERROR;
}
return false;
}
bool cRcIoRCU::SendByte(unsigned char c)
{
LOCK_THREAD;
for (int retry = 5; retry--;) {
if (SendByteHandshake(c))
return true;
}
return false;
}
bool cRcIoRCU::SetCode(unsigned char Code, unsigned short Address)
{
code = Code;
address = Address;
return SendCommand(code);
}
bool cRcIoRCU::SetMode(unsigned char Mode)
{
mode = Mode;
return SendCommand(mode);
}
void cRcIoRCU::Flush(int WaitMs)
{
LOCK_THREAD;
int t0 = time_ms();
for (;;) {
while (ReceiveByte() >= 0)
t0 = time_ms();
if (time_ms() - t0 >= WaitMs)
break;
}
receivedData = receivedRepeat = false;
}
bool cRcIoRCU::GetCommand(unsigned int *Command, bool *Repeat, bool *Release)
{
if (receivedData) { // first we check the boolean flag without a lock, to avoid delays
LOCK_THREAD;
if (receivedData) { // need to check again, since the status might have changed while waiting for the lock
if (Command)
*Command = receivedCommand;
if (Repeat)
*Repeat = receivedRepeat;
if (Release)
*Release = receivedRelease;
receivedData = false;
return true;
}
}
if (time(NULL) - t > 60) {
SendCommand(code); // in case the PIC listens to the wrong code
t = time(NULL);
}
return false;
}
bool cRcIoRCU::SendCommand(unsigned char Cmd)
{
return SendByte(Cmd | 0x80);
}
bool cRcIoRCU::Digit(int n, int v)
{
return SendByte(((n & 0x03) << 5) | (v & 0x0F) | (((dp >> n) & 0x01) << 4));
}
bool cRcIoRCU::Number(int n, bool Hex)
{
LOCK_THREAD;
if (!Hex) {
char buf[8];
sprintf(buf, "%4d", n & 0xFFFF);
n = 0;
for (char *d = buf; *d; d++) {
if (*d == ' ')
*d = 0xF;
n = (n << 4) | ((*d - '0') & 0x0F);
}
}
lastNumber = n;
for (int i = 0; i < 4; i++) {
if (!Digit(i, n))
return false;
n >>= 4;
}
return SendCommand(mode);
}
bool cRcIoRCU::String(char *s)
{
LOCK_THREAD;
const char *chars = mode == modeH ? "0123456789ABCDEF" : "0123456789-EHLP ";
int n = 0;
for (int i = 0; *s && i < 4; s++, i++) {
n <<= 4;
for (const char *c = chars; *c; c++) {
if (*c == *s) {
n |= c - chars;
break;
}
}
}
return Number(n, true);
}
void cRcIoRCU::SetPoints(unsigned char Dp, bool On)
{
if (On)
dp |= Dp;
else
dp &= ~Dp;
Number(lastNumber, true);
}
bool cRcIoRCU::DetectCode(unsigned char *Code, unsigned short *Address)
{
// Caller should initialize 'Code' to 0 and call DetectCode()
// until it returns true. Whenever DetectCode() returns false
// and 'Code' is not 0, the caller can use 'Code' to display
// a message like "Trying code '%c'". If false is returned and
// 'Code' is 0, all possible codes have been tried and the caller
// can either stop calling DetectCode() (and give some error
// message), or start all over again.
if (*Code < 'A' || *Code > 'D') {
*Code = 'A';
return false;
}
if (*Code <= 'D') {
SetMode(modeH);
char buf[5];
sprintf(buf, "C0D%c", *Code);
String(buf);
SetCode(*Code, 0);
delay_ms(REPEATDELAY);
receivedData = receivedRepeat = 0;
delay_ms(REPEATDELAY);
if (GetCommand()) {
*Address = receivedAddress;
SetMode(modeB);
String("----");
return true;
}
if (*Code < 'D') {
(*Code)++;
return false;
}
}
*Code = 0;
return false;
}
// --- cRcIoLIRC -------------------------------------------------------------
#elif defined REMOTE_LIRC
#define REPEATLIMIT 20 // ms
#define REPEATDELAY 350 // ms
cRcIoLIRC::cRcIoLIRC(char *DeviceName)
{
*keyName = 0;
receivedData = receivedRepeat = false;
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path, DeviceName);
if ((f = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0) {
if (connect(f, (struct sockaddr *)&addr, sizeof(addr)) >= 0) {
Start();
return;
}
LOG_ERROR_STR(DeviceName);
close(f);
}
else
LOG_ERROR_STR(DeviceName);
f = -1;
}
cRcIoLIRC::~cRcIoLIRC()
{
Cancel();
}
void cRcIoLIRC::Action(void)
{
dsyslog("LIRC remote control thread started (pid=%d)", getpid());
int FirstTime = 0;
int LastTime = 0;
char buf[LIRC_BUFFER_SIZE];
char LastKeyName[LIRC_KEY_BUF];
for (; f >= 0;) {
LOCK_THREAD;
if (cFile::FileReady(f, REPEATLIMIT) && safe_read(f, buf, sizeof(buf)) > 21) {
if (!receivedData) { // only accept new data the previous data has been fetched
int count;
sscanf(buf, "%*x %x %29s", &count, LastKeyName); // '29' in '%29s' is LIRC_KEY_BUF-1!
int Now = time_ms();
if (count == 0) {
strcpy(keyName, LastKeyName);
receivedData = true;
receivedRepeat = receivedRelease = false;
FirstTime = Now;
}
else {
if (Now - FirstTime < REPEATDELAY)
continue; // repeat function kicks in after a short delay
receivedData = receivedRepeat = true;
receivedRelease = false;
}
LastTime = Now;
WakeUp();
}
}
else if (receivedData) { // the last data before releasing the key hasn't been fetched yet
if (receivedRepeat) { // it was a repeat, so let's make it a release
if (time_ms() - LastTime > REPEATDELAY) {
receivedRepeat = false;
receivedRelease = true;
WakeUp();
}
}
}
else if (receivedRepeat) { // all data has already been fetched, but the last one was a repeat, so let's generate a release
if (time_ms() - LastTime > REPEATDELAY) {
receivedData = receivedRelease = true;
receivedRepeat = false;
WakeUp();
}
}
}
}
bool cRcIoLIRC::GetCommand(unsigned int *Command, bool *Repeat, bool *Release)
{
if (receivedData) { // first we check the boolean flag without a lock, to avoid delays
LOCK_THREAD;
if (receivedData) { // need to check again, since the status might have changed while waiting for the lock
if (Command)
*Command = Keys.Encode(keyName);
if (Repeat)
*Repeat = receivedRepeat;
if (Release)
*Release = receivedRelease;
receivedData = false;
return true;
}
}
return false;
dsyslog("KBD remote control thread ended (pid=%d)", getpid());
}
#endif

108
remote.h
View File

@ -1,10 +1,10 @@
/*
* remote.h: Interface to the Remote Control Unit
* remote.h: General Remote Control handling
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: remote.h 1.16 2002/05/09 11:43:04 kls Exp $
* $Id: remote.h 1.17 2002/09/29 11:26:45 kls Exp $
*/
#ifndef __REMOTE_H
@ -12,91 +12,53 @@
#include <stdio.h>
#include <time.h>
#include "keys.h"
#include "thread.h"
#include "tools.h"
class cRcIoBase {
typedef unsigned long long int uint64;
class cRemote : public cListObject {
private:
enum { MaxKeys = 16 };
static eKeys keys[MaxKeys];
static int in;
static int out;
static bool learning;
static char *unknownCode;
static cMutex mutex;
static cCondVar keyPressed;
char *name;
protected:
time_t t;
cRemote(const char *Name);
const char *GetSetup(void);
void PutSetup(const char *Setup);
bool Put(uint64 Code, bool Repeat = false, bool Release = false);
bool Put(const char *Code, bool Repeat = false, bool Release = false);
public:
enum { modeH = 'h', modeB = 'b', modeS = 's' };
cRcIoBase(void);
virtual ~cRcIoBase();
virtual bool SetCode(unsigned char Code, unsigned short Address) { return true; }
virtual bool SetMode(unsigned char Mode) { return true; }
virtual bool Number(int n, bool Hex = false) { return true; }
virtual void SetPoints(unsigned char Dp, bool On) {}
virtual bool String(char *s) { return true; }
virtual bool DetectCode(unsigned char *Code, unsigned short *Address) { return true; }
virtual void Flush(int WaitMs = 0) {}
virtual bool InputAvailable(void) { return false; }
virtual bool GetCommand(unsigned int *Command = NULL, bool *Repeat = NULL, bool *Release = NULL) { return false; }
virtual ~cRemote();
virtual bool Initialize(void) { return true; }
const char *Name(void) { return name; }
static void SetLearning(bool On) { learning = On; }
static void Clear(void);
static bool Put(eKeys Key);
static eKeys Get(int WaitMs = 1000, char **UnknownCode = NULL);
};
class cRemotes : public cList<cRemote> {};
extern cRemotes Remotes;
#if defined REMOTE_KBD
class cRcIoKBD : public cRcIoBase {
class cKbdRemote : public cRemote, private cThread {
private:
cFile f;
public:
cRcIoKBD(void);
virtual ~cRcIoKBD();
virtual void Flush(int WaitMs = 0);
virtual bool InputAvailable(void);
virtual bool GetCommand(unsigned int *Command = NULL, bool *Repeat = NULL, bool *Release = NULL);
};
#elif defined REMOTE_RCU
class cRcIoRCU : public cRcIoBase, private cThread {
private:
int f;
unsigned char dp, code, mode;
unsigned short address;
unsigned short receivedAddress;
unsigned int receivedCommand;
bool receivedData, receivedRepeat, receivedRelease;
int lastNumber;
bool SendCommand(unsigned char Cmd);
int ReceiveByte(int TimeoutMs = 0);
bool SendByteHandshake(unsigned char c);
bool SendByte(unsigned char c);
bool Digit(int n, int v);
virtual void Action(void);
public:
cRcIoRCU(char *DeviceName);
virtual ~cRcIoRCU();
virtual bool SetCode(unsigned char Code, unsigned short Address);
virtual bool SetMode(unsigned char Mode);
virtual bool Number(int n, bool Hex = false);
virtual void SetPoints(unsigned char Dp, bool On);
virtual bool String(char *s);
virtual bool DetectCode(unsigned char *Code, unsigned short *Address);
virtual void Flush(int WaitMs = 0);
virtual bool InputAvailable(void) { return receivedData; }
virtual bool GetCommand(unsigned int *Command = NULL, bool *Repeat = NULL, bool *Release = NULL);
cKbdRemote(void);
virtual ~cKbdRemote();
};
#elif defined REMOTE_LIRC
class cRcIoLIRC : public cRcIoBase, private cThread {
private:
enum { LIRC_KEY_BUF = 30, LIRC_BUFFER_SIZE = 128 };
int f;
char keyName[LIRC_KEY_BUF];
bool receivedData, receivedRepeat, receivedRelease;
virtual void Action(void);
public:
cRcIoLIRC(char *DeviceName);
virtual ~cRcIoLIRC();
virtual bool InputAvailable(void) { return receivedData; }
virtual bool GetCommand(unsigned int *Command = NULL, bool *Repeat = NULL, bool *Release = NULL);
};
#elif !defined REMOTE_NONE
// #error Please define a remote control mode!
#endif
#endif //__REMOTE_H

11
svdrp.c
View File

@ -10,7 +10,7 @@
* and interact with the Video Disk Recorder - or write a full featured
* graphical interface that sits on top of an SVDRP connection.
*
* $Id: svdrp.c 1.42 2002/09/08 11:22:57 kls Exp $
* $Id: svdrp.c 1.43 2002/09/28 15:50:19 kls Exp $
*/
#include "svdrp.h"
@ -28,7 +28,8 @@
#include <unistd.h>
#include "config.h"
#include "device.h"
#include "interface.h"
#include "keys.h"
#include "remote.h"
#include "tools.h"
// --- cSocket ---------------------------------------------------------------
@ -606,9 +607,9 @@ void cSVDRP::CmdHELP(const char *Option)
void cSVDRP::CmdHITK(const char *Option)
{
if (*Option) {
eKeys k = Keys.Translate(Option);
eKeys k = cKey::FromString(Option);
if (k != kNone) {
Interface->PutKey(k);
cRemote::Put(k);
Reply(250, "Key \"%s\" accepted", Option);
}
else
@ -617,7 +618,7 @@ void cSVDRP::CmdHITK(const char *Option)
else {
Reply(-214, "Valid <key> names for the HITK command:");
for (int i = 0; i < kNone; i++) {
Reply(-214, " %s", Keys.keys[i].name);
Reply(-214, " %s", cKey::ToString(eKeys(i)));
}
Reply(214, "End of key list");
}

22
vdr.5
View File

@ -8,9 +8,9 @@
.\" License as specified in the file COPYING that comes with the
.\" vdr distribution.
.\"
.\" $Id: vdr.5 1.4 2002/04/07 13:12:04 kls Exp $
.\" $Id: vdr.5 1.5 2002/09/29 13:06:40 kls Exp $
.\"
.TH vdr 5 "7 Apr 2002" "1.0.0" "Video Disk Recorder Files"
.TH vdr 5 "7 Sep 2002" "1.2.0" "Video Disk Recorder Files"
.SH NAME
vdr file formats - the Video Disk Recorder Files
.SH DESCRIPTION
@ -209,12 +209,18 @@ channels that don't require additional decryption hardware.
The values \fB1...4\fR can be used for channels that for some reason explicitly
need a given DVB card (for backward compatibility).
.SS KEYS
The file \fIkeys.conf\fR contains the key assignments for the remote control
unit (RCU). If \fBvdr\fR has been built with REMOTE=KBD, the file \fIkeys-pc.conf\fR
will be used instead. If you are using \fBvdr\fR together with \fBLIRC\fR, no
such file will be used. In that case you need to consult the \fBLIRC\fR
documentation to see how to set up the remote control key assignments there.
.SS REMOTE CONTROL KEYS
The file \fIremote.conf\fR contains the key assignments for all remote control
units. Each line consists of one key assignment in the following format:
\fBname.key code\fR
where \fBname\fR is the name of the remote control (for instance KBD for the
PC keyboard, RCU for the home-built "Remote Control Unit", or LIRC for the
"Linux Infrared Remote Control"), \fBkey\fR is the name of the key that is
defined (like Up, Down, Menu etc.), and \fBcode\fR is a character string that
this remote control delivers when the given key is pressed.
.SS COMMANDS
The file \fIcommands.conf\fR contains the definitions of commands that can
be executed from the \fBvdr\fR main menu's "Commands" option.

39
vdr.c
View File

@ -22,7 +22,7 @@
*
* The project's page is at http://www.cadsoft.de/people/kls/vdr
*
* $Id: vdr.c 1.123 2002/09/15 11:08:35 kls Exp $
* $Id: vdr.c 1.124 2002/09/29 12:55:03 kls Exp $
*/
#include <getopt.h>
@ -37,19 +37,16 @@
#include "eitscan.h"
#include "i18n.h"
#include "interface.h"
#include "keys.h"
#include "lirc.h"
#include "menu.h"
#include "osd.h"
#include "plugin.h"
#include "rcu.h"
#include "recording.h"
#include "tools.h"
#include "videodir.h"
#ifdef REMOTE_KBD
#define KEYS_CONF "keys-pc.conf"
#else
#define KEYS_CONF "keys.conf"
#endif
#define ACTIVITYTIMEOUT 60 // seconds before starting housekeeping
#define SHUTDOWNWAIT 300 // seconds to wait in user prompt before automatic shutdown
#define MANUALSTART 600 // seconds the next timer must be in the future to assume manual start
@ -319,11 +316,7 @@ int main(int argc, char *argv[])
Commands.Load(AddDirectory(ConfigDirectory, "commands.conf"));
SVDRPhosts.Load(AddDirectory(ConfigDirectory, "svdrphosts.conf"), true);
CaDefinitions.Load(AddDirectory(ConfigDirectory, "ca.conf"), true);
#if defined(REMOTE_LIRC)
Keys.SetDummyValues();
#elif !defined(REMOTE_NONE)
bool KeysLoaded = Keys.Load(AddDirectory(ConfigDirectory, KEYS_CONF));
#endif
Keys.Load(AddDirectory(ConfigDirectory, "remote.conf"));
// DVB interfaces:
@ -350,6 +343,20 @@ int main(int argc, char *argv[])
cOsd::Initialize();
// User interface:
Interface = new cInterface(SVDRPport);
// Remote Controls:
#if defined(REMOTE_RCU)
new cRcuRemote("/dev/ttyS1");
#elif defined(REMOTE_LIRC)
new cLircRemote("/dev/lircd");
#elif defined(REMOTE_KBD)
new cKbdRemote;
#endif
Interface->LearnKeys();
// Channel:
Channels.SwitchTo(Setup.CurrentChannel);
@ -360,14 +367,6 @@ int main(int argc, char *argv[])
cEITScanner EITScanner;
// User interface:
Interface = new cInterface(SVDRPport);
#if !defined(REMOTE_LIRC) && !defined(REMOTE_NONE)
if (!KeysLoaded)
Interface->LearnKeys();
#endif
// Signal handlers:
if (signal(SIGHUP, SignalHandler) == SIG_IGN) signal(SIGHUP, SIG_IGN);