From fc668608474ed16f015f71e57366f6bd2c5d8c8d Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sun, 29 Sep 2002 13:40:45 +0200 Subject: [PATCH] Made remote controls plugin aware --- HISTORY | 17 +- INSTALL | 14 +- Makefile | 4 +- PLUGINS.html | 140 +++++++++++-- config.c | 172 +-------------- config.h | 80 ++----- device.c | 16 +- device.h | 20 +- dvbdevice.c | 4 +- dvbdevice.h | 4 +- i18n.c | 18 +- interface.c | 248 +++++++++------------- interface.h | 11 +- keys-pc.conf | 27 --- keys.c | 154 ++++++++++++++ keys.h | 87 ++++++++ lirc.c | 84 ++++++++ lirc.h | 26 +++ menu.c | 20 +- menu.h | 4 +- rcu.c | 312 +++++++++++++++++++++++++++ rcu.h | 44 ++++ remote.c | 581 +++++++++++---------------------------------------- remote.h | 108 ++++------ svdrp.c | 11 +- vdr.5 | 22 +- vdr.c | 39 ++-- 27 files changed, 1212 insertions(+), 1055 deletions(-) delete mode 100644 keys-pc.conf create mode 100644 keys.c create mode 100644 keys.h create mode 100644 lirc.c create mode 100644 lirc.h create mode 100644 rcu.c create mode 100644 rcu.h diff --git a/HISTORY b/HISTORY index 59c4e459..15c33967 100644 --- a/HISTORY +++ b/HISTORY @@ -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. diff --git a/INSTALL b/INSTALL index 911285ce..2ceda924 100644 --- a/INSTALL +++ b/INSTALL @@ -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. - diff --git a/Makefile b/Makefile index 4f468c42..9eae7264 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/PLUGINS.html b/PLUGINS.html index 5df0cd99..71f66abc 100644 --- a/PLUGINS.html +++ b/PLUGINS.html @@ -21,18 +21,18 @@ VDR program and present itself to the user. The inside 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.

-
  -Important modifications introduced in version 1.1.6 are marked like this. -
-
  +
  Important modifications introduced in version 1.1.7 are marked like this.
-
  +
  Important modifications introduced in version 1.1.8 are marked like this.
-
  +
  Important modifications introduced in version 1.1.9 are marked like this.
+
  +Important modifications introduced in version 1.1.11 are marked like this. +

Part I - The Outside Interface

@@ -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. -
  +
  To avoid busy loops the player should call its member function


@@ -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... -
 

Receivers

Tapping into the stream...

@@ -1119,7 +1118,6 @@ cDevice::PrimaryDevice()->AttachReceiver(Receiver); If the cReceiver isn't needed any more, it may simply be deleted and will automatically detach itself from the cDevice. -


The On Screen Display

@@ -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). -
 

Devices

Expanding the possibilities

@@ -1187,7 +1184,7 @@ the cDvbDevice, 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: -
  +
 


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); -
  +
  virtual bool GetTSPacket(uchar *&Data);

@@ -1230,7 +1227,7 @@ to indicate this to VDR.

The functions to implement replaying capabilites are -
  +
 


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);

-
  +
 

On Screen Display

@@ -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 new operator! -

+ +
  +

Remote Control

+ +
The joy of zapping!

+ +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 cRemote class to do that. +

+The simplest method for a plugin to issue commands to VDR is to call the +static function cRemote::Put(eKeys Key), as in + +


+cRemote::Put(kUp); +

+ +In this case the plugin must do the mapping of whatever incoming signal or code +it processes to the eKeys values itself. This makes sense if the incoming +codes are well known and won't ever change. +

+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 cRemote, as in + +


+#include <vdr/remote.h> +#include <vdr/thread.h> + +class cMyRemote : public cRemote, private cThread { +private: + virtual void Action(void); +public: + cMyRemote(const char *Name); + virtual bool Initialize(void); + }; +

+ +Note that deriving from cThread 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 cRemote base class. +

+You should create your derived remote control object in the +Start() function of your plugin. +Note that the object has to be created on the heap (using new), +and you shall not delete it at any point (it will be deleted automatically +when the program ends). +

+The constructor of your remote control class should look like this + +


+cMyRemote::cMyRemote(const char *Name) +:cRemote(Name) +{ + Start(); +} +

+ +The Name is important in order for the cRemote base class +to be able to distinguish the codes for the various remote controls. +When creating your cMyRemote object you should use the value returned +by the Name() member function of the plugin class, which returns the +plugin's name. Calling Start() will start the thread that collects +the incoming data (by calling your Action() 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 Start(). +

+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 Initialize(), +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. Initialize(), 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 cRemote class has detected useful incoming data, +Initialize() should return true. If any fatal error occurs, false +should be returned. +

+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 cRemote member functions + +


+void PutSetup(const char *Setup); +const char *GetSetup(void); +

+ +to store and retrieve a character string containing whatever data is needed. +Note that the Initialize() function will only be called if there are +no key mappings known for this remote control. Once the key mappings have been +learned, Initialize() will never be called again. +

+The cRemote 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 + +


+Put(const char *Code, bool Repeat = false, bool Release = false); +

+ +where Code is the string representation of the remote control's +incoming data. Repeat and Release 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 Put() function available for your convenience, +which takes a 64 bit unsigned integer value instead of a character string: + +


+Put(uint64 Code, bool Repeat = false, bool Release = false); +

+ +The other parameters have the same meaning as in the first version of this function. +

diff --git a/config.c b/config.c index 15d645d0..e5a56090 100644 --- a/config.c +++ b/config.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.c 1.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; diff --git a/config.h b/config.h index 011148f2..dc043054 100644 --- a/config.h +++ b/config.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.h 1.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 cConfig : public cList { 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; diff --git a/device.c b/device.c index 830c3ef3..12166a8a 100644 --- a/device.c +++ b/device.c @@ -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 diff --git a/device.h b/device.h index 29f88a63..b6f44273 100644 --- a/device.h +++ b/device.h @@ -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. diff --git a/dvbdevice.c b/dvbdevice.c index 0ba7c25d..e10a376a 100644 --- a/dvbdevice.c +++ b/dvbdevice.c @@ -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(); diff --git a/dvbdevice.h b/dvbdevice.h index b135ff66..acf569e0 100644 --- a/dvbdevice.h +++ b/dvbdevice.h @@ -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); diff --git a/i18n.c b/i18n.c index 39948c2a..81b77178 100644 --- a/i18n.c +++ b/i18n.c @@ -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", diff --git a/interface.c b/interface.c index 8d4da216..3f1fba8f 100644 --- a/interface.c +++ b/interface.c @@ -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(); -} diff --git a/interface.h b/interface.h index 137c7d48..6344c5bd 100644 --- a/interface.h +++ b/interface.h @@ -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; diff --git a/keys-pc.conf b/keys-pc.conf deleted file mode 100644 index d9a390f7..00000000 --- a/keys-pc.conf +++ /dev/null @@ -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 diff --git a/keys.c b/keys.c new file mode 100644 index 00000000..0d91d673 --- /dev/null +++ b/keys.c @@ -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); +} diff --git a/keys.h b/keys.h new file mode 100644 index 00000000..d5ecbbc4 --- /dev/null +++ b/keys.h @@ -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 { +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 diff --git a/lirc.c b/lirc.c new file mode 100644 index 00000000..f120743b --- /dev/null +++ b/lirc.c @@ -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 2000-06-16. + * + * $Id: lirc.c 1.1 2002/09/29 13:16:33 kls Exp $ + */ + +#include "lirc.h" +#include +#include +#include + +#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; + } + } + } +} diff --git a/lirc.h b/lirc.h new file mode 100644 index 00000000..ff58f315 --- /dev/null +++ b/lirc.h @@ -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 diff --git a/menu.c b/menu.c index 7d13055d..eaf260f4 100644 --- a/menu.c +++ b/menu.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 1.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); } } diff --git a/menu.h b/menu.h index c03e627e..247057b8 100644 --- a/menu.h +++ b/menu.h @@ -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: diff --git a/rcu.c b/rcu.c new file mode 100644 index 00000000..6b7c4f97 --- /dev/null +++ b/rcu.c @@ -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 +#include +#include +#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()!!! +} diff --git a/rcu.h b/rcu.h new file mode 100644 index 00000000..1c54999c --- /dev/null +++ b/rcu.h @@ -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 diff --git a/remote.c b/remote.c index 0ee42a79..ffdfa18a 100644 --- a/remote.c +++ b/remote.c @@ -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 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 #include -#if defined REMOTE_LIRC -#include -#include -#include -#endif - #if defined REMOTE_KBD #include #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 - diff --git a/remote.h b/remote.h index 184a42e9..f715ade7 100644 --- a/remote.h +++ b/remote.h @@ -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 #include +#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 {}; + +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 diff --git a/svdrp.c b/svdrp.c index 3115349c..325b7dab 100644 --- a/svdrp.c +++ b/svdrp.c @@ -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 #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 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"); } diff --git a/vdr.5 b/vdr.5 index b440f12b..00c58edb 100644 --- a/vdr.5 +++ b/vdr.5 @@ -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. diff --git a/vdr.c b/vdr.c index 1f38af41..fd23ae1e 100644 --- a/vdr.c +++ b/vdr.c @@ -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 @@ -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);