diff options
author | Klaus Schmidinger <kls (at) cadsoft (dot) de> | 2000-10-08 18:00:00 +0200 |
---|---|---|
committer | Klaus Schmidinger <kls (at) cadsoft (dot) de> | 2000-10-08 18:00:00 +0200 |
commit | a379eb714f7f5ef9a12efbe7588bb3509faba056 (patch) | |
tree | ea9a0720f305e8ee76ea7c60c996fd3a8bad0ce5 | |
parent | ef8fe3f04c30caedeb17b11ac275581539f039c7 (diff) | |
download | vdr-patch-lnbsharing-a379eb714f7f5ef9a12efbe7588bb3509faba056.tar.gz vdr-patch-lnbsharing-a379eb714f7f5ef9a12efbe7588bb3509faba056.tar.bz2 |
Version 0.66vdr-0.66
- Remote control data is now received in a separate thread, which makes things
a lot smoother.
- Repeat and release of remote control keys is now explicitly distinguished.
- In replay mode the search forward/back and skip functions now have two modes:
Pressing the key shortly and releasing it starts the function, and pressing it
again stops it. Pressing and holding down the key starts the function and
releasing the key stops it.
- The '@' character that marks an "instant recording" can now be turned off
in the "Setup" menu (thanks to Matthias Schniedermeyer).
- Pressing the "Back" button while replaying now stops replaying and brings up
the "Recordings" menu (suggested by Carsten Koch). This can be used to easily
delete a recording after watching it, or to switch to a different recording.
- The "Recordings" menu now places the cursor on the last replayed recording, if
that file still exists.
- The "Blue" button in the "Main" menu can now be used to "Resume" a previously
stopped replay session (suggested by Martin Hammerschmid).
- The low and high LNB frequencies can now be changed in the "Setup" menu.
-rw-r--r-- | CONTRIBUTORS | 5 | ||||
-rw-r--r-- | FORMATS | 8 | ||||
-rw-r--r-- | HISTORY | 22 | ||||
-rw-r--r-- | MANUAL | 29 | ||||
-rw-r--r-- | Makefile | 11 | ||||
-rw-r--r-- | config.c | 17 | ||||
-rw-r--r-- | config.h | 17 | ||||
-rw-r--r-- | dvbapi.c | 8 | ||||
-rw-r--r-- | dvbosd.c | 8 | ||||
-rw-r--r-- | interface.c | 82 | ||||
-rw-r--r-- | interface.h | 15 | ||||
-rw-r--r-- | menu.c | 105 | ||||
-rw-r--r-- | menu.h | 16 | ||||
-rw-r--r-- | osd.c | 26 | ||||
-rw-r--r-- | recording.c | 4 | ||||
-rw-r--r-- | remote.c | 309 | ||||
-rw-r--r-- | remote.h | 44 | ||||
-rw-r--r-- | setup.conf | 3 | ||||
-rw-r--r-- | svdrp.c | 6 | ||||
-rw-r--r-- | thread.c | 105 | ||||
-rw-r--r-- | thread.h | 57 | ||||
-rw-r--r-- | timers.conf | 10 | ||||
-rw-r--r-- | tools.c | 18 | ||||
-rw-r--r-- | tools.h | 3 | ||||
-rw-r--r-- | vdr.c | 36 |
25 files changed, 682 insertions, 282 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 2eefd64..91c9540 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -7,6 +7,7 @@ Carsten Koch <Carsten.Koch@icem.de> for adding the 'epg2timers' tool (see Tools/epg2timers) for his idea of using multiple disks (and for testing this feature) for implementing the 'new recording' indicator + for suggesting that the "Back" button in replay mode should bring up the "Recordings" menu Plamen Ganev <pganev@com-it.net> for fixing the frequency offset for Hotbird channels @@ -31,6 +32,10 @@ Niels de Carpentier <niels@casema.net> Martin Hammerschmid <martin@hammerschmid.com> for suggesting to display the direct channel select input on the OSD + for suggesting to use the "Blue" button in the main menu to resume replay Bastian Guse <bastian@nocopy.de> for writing the FORMATS entry for timers.conf + +Matthias Schniedermeyer <ms@citd.de> + for implementing the 'MarkInstantRecord' setup option. @@ -55,3 +55,11 @@ Video Disk Recorder File Formats - Name of timer (will be used to name the recording) - Summary +* setup.conf + + This file contains the basic configuration options for VDR. + + Each line contains one option in the format "Name = Value". + + See the MANUAL file for a description of the available options. + @@ -212,7 +212,7 @@ Video Disk Recorder Revision History against the version 0.71 DVB driver file 'dvb.c'). - When switching channels the channel is now immediately displayed, and the current/next information is shown as soon as it becomes available. -- No longer displaying the year in the 'Recordings' menu to saves space for the +- No longer displaying the year in the 'Recordings' menu to save space for the title. - The 'Recordings' menu now displays a '*' to indicate new recordings. - Added the description of the timers.conf file to the FORMATS file (thanks to @@ -221,3 +221,23 @@ Video Disk Recorder Revision History that would display only partially). - In normal viewing mode the '0' key now toggles between the current and the previous channel. + +2000-10-08: Version 0.66 + +- Remote control data is now received in a separate thread, which makes things + a lot smoother. +- Repeat and release of remote control keys is now explicitly distinguished. +- In replay mode the search forward/back and skip functions now have two modes: + Pressing the key shortly and releasing it starts the function, and pressing it + again stops it. Pressing and holding down the key starts the function and + releasing the key stops it. +- The '@' character that marks an "instant recording" can now be turned off + in the "Setup" menu (thanks to Matthias Schniedermeyer). +- Pressing the "Back" button while replaying now stops replaying and brings up + the "Recordings" menu (suggested by Carsten Koch). This can be used to easily + delete a recording after watching it, or to switch to a different recording. +- The "Recordings" menu now places the cursor on the last replayed recording, if + that file still exists. +- The "Blue" button in the "Main" menu can now be used to "Resume" a previously + stopped replay session (suggested by Martin Hammerschmid). +- The low and high LNB frequencies can now be changed in the "Setup" menu. @@ -16,11 +16,11 @@ Video Disk Recorder User's Manual Right Next group - - Enable Increment - Search forward Ok Ch display Select Switch Edit Accept Play Progress disp. Menu Menu on Menu off Menu off Menu off Menu off Menu off Menu on - Back - Menu off Main menu Main menu Discard Main menu - + Back - Menu off Main menu Main menu Discard Main menu Recordings menu Red - Record Edit Edit - Play - Green - - New New - - Skip -60s Yellow - - Delete Delete - Delete Skip +60s - Blue - - Mark Mark - - Stop + Blue - Resume Mark Mark - - Stop 0..9 Ch select - - - Numeric inp. - - * Navigating through the On Screen Menus @@ -90,7 +90,7 @@ Video Disk Recorder User's Manual * Instant Recording You can start recording the current channel by pressing the "Red" button - in the Main menu. This will create a timer event named "@channelname" that + in the "Main" menu. This will create a timer event named "@channelname" that starts at the current time and records for two hours. If you want to modify the recording time you need to edit the timer. Stop instant recording by pressing the "Menu" button and selecting @@ -101,8 +101,10 @@ Video Disk Recorder User's Manual All recordings are listed in the "Recordings" menu. Browse through the list with the "Up" and "Down" button and press "Ok" (or the "Red" button) to start playback. New recordings are marked with an '*'. - Playback can be stopped via the Main menu by selecting "Stop replaying", + Playback can be stopped via the "Main" menu by selecting "Stop replaying", or by pressing the "Blue" button outside the menu. + A previously stopped playback session can be resumed by pressing the "Blue" + button in the "Main" menu. * Replay Control @@ -118,12 +120,19 @@ Video Disk Recorder User's Manual Right Runs playback forward or backward at a higher speed; press again to resume normal speed. If in Pause mode, runs forward or backward at a slower speed; press again to return to pause mode. + Pressing and holding down the button performs the function until + the button is released again. - Green Yellow Skips about 60 seconds back or forward. + Pressing and holding down the button performs the function until + the button is released again. - Ok Brings up the replay progress display, which shows the date, time and title of the recording, a progress bar and the current and total time of the recording. Press "Ok" again to turn off the progress display. + - Back Stops replaying and brings up the "Recordings" menu. This can be + used to easily delete a recording after watching it, or to switch + to a different recording. * Programming the Timer @@ -171,7 +180,7 @@ Video Disk Recorder User's Manual * Parameters in the "Setup" menu - Select "Setup" from the main menu to enter the setup menu. From there you can + Select "Setup" from the "Main" menu to enter the setup menu. From there you can modify the following system parameters (note that "boolean" values will be displayed as "no" and "yes" in the "Setup" menu, while in the setup file they are stored as '0' and '1', respectively): @@ -197,3 +206,13 @@ Video Disk Recorder User's Manual 1 = dto., but the cursor remains at the bottom (top) of the page (this mode allows for faster scrolling through long lists). + + MarkInstantRecord = 1 Defines whether an "instant recording" (started by + pressing the "Red" button in the "Main" menu) will be + marked with a '@' character to make it distinguishable + from timer recordings in the "Recordings" menu. + 0 = instant recordings will not be marked + 1 = instant recordings will be marked. + + LnbFrequLo = 9750 The low and high LNB frequencies (in MHz) + LnbFrequHi = 10600 @@ -4,13 +4,13 @@ # See the main source file 'vdr.c' for copyright information and # how to reach the author. # -# $Id: Makefile 1.12 2000/10/01 14:27:12 kls Exp $ +# $Id: Makefile 1.13 2000/10/07 16:24:08 kls Exp $ DVBDIR = ../DVB INCLUDES = -I$(DVBDIR)/driver OBJS = config.o dvbapi.o dvbosd.o eit.o font.o interface.o menu.o osd.o\ - recording.o remote.o svdrp.o tools.o vdr.o videodir.o + recording.o remote.o svdrp.o thread.o tools.o vdr.o videodir.o OSDFONT = -adobe-helvetica-medium-r-normal--23-*-100-100-p-*-iso8859-1 @@ -40,12 +40,13 @@ dvbapi.o : dvbapi.c config.h dvbapi.h dvbosd.h font.h interface.h svdrp.h tool dvbosd.o : dvbosd.c dvbosd.h font.h tools.h eit.o : eit.c eit.h tools.h font.o : font.c font.h fontosd.c tools.h -interface.o: interface.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h tools.h +interface.o: interface.c config.h dvbapi.h dvbosd.h eit.h font.h interface.h remote.h svdrp.h thread.h tools.h menu.o : menu.c config.h dvbapi.h dvbosd.h font.h interface.h menu.h osd.h recording.h svdrp.h tools.h osd.o : osd.c config.h dvbapi.h dvbosd.h font.h interface.h osd.h svdrp.h tools.h recording.o: recording.c config.h dvbapi.h dvbosd.h font.h interface.h recording.h svdrp.h tools.h videodir.h -remote.o : remote.c config.h dvbapi.h dvbosd.h font.h remote.h tools.h +remote.o : remote.c config.h dvbapi.h dvbosd.h font.h remote.h thread.h tools.h svdrp.o : svdrp.c config.h dvbapi.h dvbosd.h font.h interface.h svdrp.h tools.h +thread.o : thread.c thread.h tools.o : tools.c tools.h vdr.o : vdr.c config.h dvbapi.h dvbosd.h font.h interface.h menu.h osd.h recording.h svdrp.h tools.h videodir.h videodir.o : videodir.c tools.h videodir.h @@ -53,7 +54,7 @@ videodir.o : videodir.c tools.h videodir.h # The main program: vdr: $(OBJS) - g++ -g -O2 $(OBJS) -lncurses -ljpeg -o vdr + g++ -g -O2 $(OBJS) -lncurses -ljpeg -lpthread -o vdr # The font 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.23 2000/09/17 09:11:59 kls Exp $ + * $Id: config.c 1.26 2000/10/08 16:10:40 kls Exp $ */ #include "config.h" @@ -275,7 +275,7 @@ bool cChannel::Switch(cDvbApi *DvbApi) } return false; } - Interface.Info(DvbApi->Recording() ? "Channel locked (recording)!" : name); + Interface->Info(DvbApi->Recording() ? "Channel locked (recording)!" : name); return false; } @@ -303,7 +303,7 @@ cTimer::cTimer(bool Instant) *file = 0; summary = NULL; if (Instant && ch) - snprintf(file, sizeof(file), "@%s", ch->name); + snprintf(file, sizeof(file), "%s%s", Setup.MarkInstantRecord ? "@" : "", ch->name); } cTimer::~cTimer() @@ -566,7 +566,7 @@ eKeys cChannels::ShowChannel(int Number, bool Switched, bool Group) { cChannel *channel = Group ? Get(Number) : GetByNumber(Number); if (channel) - return Interface.DisplayChannel(channel->number, channel->name, !Switched || Setup.ShowInfoOnChSwitch); + return Interface->DisplayChannel(channel->number, channel->name, !Switched || Setup.ShowInfoOnChSwitch); return kNone; } @@ -596,6 +596,9 @@ cSetup::cSetup(void) PrimaryDVB = 1; ShowInfoOnChSwitch = 1; MenuScrollPage = 1; + MarkInstantRecord = 1; + LnbFrequLo = 9750; + LnbFrequHi = 10600; } bool cSetup::Parse(char *s) @@ -607,6 +610,9 @@ bool cSetup::Parse(char *s) if (!strcasecmp(Name, "PrimaryDVB")) PrimaryDVB = atoi(Value); else if (!strcasecmp(Name, "ShowInfoOnChSwitch")) ShowInfoOnChSwitch = atoi(Value); else if (!strcasecmp(Name, "MenuScrollPage")) MenuScrollPage = atoi(Value); + else if (!strcasecmp(Name, "MarkInstantRecord")) MarkInstantRecord = atoi(Value); + else if (!strcasecmp(Name, "LnbFrequLo")) LnbFrequLo = atoi(Value); + else if (!strcasecmp(Name, "LnbFrequHi")) LnbFrequHi = atoi(Value); else return false; return true; @@ -651,6 +657,9 @@ bool cSetup::Save(const char *FileName) fprintf(f, "PrimaryDVB = %d\n", PrimaryDVB); fprintf(f, "ShowInfoOnChSwitch = %d\n", ShowInfoOnChSwitch); fprintf(f, "MenuScrollPage = %d\n", MenuScrollPage); + fprintf(f, "MarkInstantRecord = %d\n", MarkInstantRecord); + fprintf(f, "LnbFrequLo = %d\n", LnbFrequLo); + fprintf(f, "LnbFrequHi = %d\n", LnbFrequHi); fclose(f); isyslog(LOG_INFO, "saved setup to %s", FileName); return true; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: config.h 1.22 2000/10/01 14:14:18 kls Exp $ + * $Id: config.h 1.27 2000/10/08 16:33:48 kls Exp $ */ #ifndef __CONFIG_H @@ -17,7 +17,7 @@ #include "dvbapi.h" #include "tools.h" -#define VDRVERSION "0.65" +#define VDRVERSION "0.66" #define MaxBuffer 10000 @@ -34,9 +34,17 @@ enum eKeys { // "Up" and "Down" must be the first two keys! kYellow, kBlue, k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, - kNone + kNone, + // The following flags are OR'd with the above codes: + k_Repeat = 0x8000, + k_Release = 0x4000, + k_Flags = k_Repeat | k_Release, }; +#define RAWKEY(k) ((k) & ~k_Flags) +#define ISRAWKEY(k) ((k) != kNone && ((k) & k_Flags) == 0) +#define NORMALKEY(k) ((k) & ~k_Repeat) + struct tKey { eKeys type; char *name; @@ -223,6 +231,9 @@ public: int PrimaryDVB; int ShowInfoOnChSwitch; int MenuScrollPage; + int MarkInstantRecord; + int LnbFrequLo; + int LnbFrequHi; cSetup(void); bool Load(const char *FileName); bool Save(const char *FileName = NULL); @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbapi.c 1.30 2000/10/03 13:26:16 kls Exp $ + * $Id: dvbapi.c 1.31 2000/10/08 16:14:45 kls Exp $ */ #include "dvbapi.h" @@ -19,7 +19,7 @@ extern "C" { #include <sys/stat.h> #include <sys/time.h> #include <unistd.h> -#include "dvbapi.h" +#include "config.h" #include "interface.h" #include "tools.h" #include "videodir.h" @@ -1657,9 +1657,9 @@ bool cDvbApi::SetChannel(int FrequencyMHz, char Polarization, int Diseqc, int Sr unsigned int freq = FrequencyMHz; front.ttk = (freq < 11700UL) ? 0 : 1; if (freq < 11700UL) - freq -= 9750UL; + freq -= Setup.LnbFrequLo; else - freq -= 10600UL; + freq -= Setup.LnbFrequHi; front.channel_flags = Ca ? DVB_CHANNEL_CA : DVB_CHANNEL_FTA; front.pnr = Pnr; front.freq = freq * 1000000UL; @@ -4,10 +4,11 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: dvbosd.c 1.2 2000/10/03 13:34:13 kls Exp $ + * $Id: dvbosd.c 1.3 2000/10/07 14:42:48 kls Exp $ */ #include "dvbosd.h" +#include <signal.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <sys/unistd.h> @@ -138,11 +139,16 @@ void cDvbOsd::Cmd(OSD_Command cmd, int color, int x0, int y0, int x1, int y1, co dc.x1 = x1; dc.y1 = y1; dc.data = (void *)data; + // must block all signals, otherwise the command might not be fully executed + sigset_t set, oldset; + sigfillset(&set); + sigprocmask(SIG_BLOCK, &set, &oldset); ioctl(videoDev, VIDIOCSOSDCOMMAND, &dc); usleep(10); // XXX Workaround for a driver bug (cInterface::DisplayChannel() displayed texts at wrong places // XXX and sometimes the OSD was no longer displayed). // XXX Increase the value if the problem still persists on your particular system. // TODO Check if this is still necessary with driver versions after 0.7. + sigprocmask(SIG_SETMASK, &oldset, NULL); } } diff --git a/interface.c b/interface.c index a0fca0e..5940195 100644 --- a/interface.c +++ b/interface.c @@ -4,43 +4,39 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: interface.c 1.21 2000/10/03 13:28:02 kls Exp $ + * $Id: interface.c 1.25 2000/10/08 16:34:17 kls Exp $ */ #include "interface.h" #include <unistd.h> #include "eit.h" -#include "remote.h" cEIT EIT; -#if defined(REMOTE_RCU) -cRcIoRCU RcIo("/dev/ttyS1"); -#elif defined(REMOTE_LIRC) -cRcIoLIRC RcIo("/dev/lircd"); -#else -cRcIoKBD RcIo; -#endif - -cInterface Interface; +cInterface *Interface = NULL; -cInterface::cInterface(void) +cInterface::cInterface(int SVDRPport) { open = 0; cols[0] = 0; keyFromWait = kNone; + rcIo = NULL; SVDRP = NULL; -} - -void cInterface::Init(int SVDRPport) -{ - RcIo.SetCode(Keys.code, Keys.address); +#if defined(REMOTE_RCU) + rcIo = new cRcIoRCU("/dev/ttyS1"); +#elif defined(REMOTE_LIRC) + rcIo = new cRcIoLIRC("/dev/lircd"); +#else + rcIo = new cRcIoKBD; +#endif + rcIo->SetCode(Keys.code, Keys.address); if (SVDRPport) SVDRP = new cSVDRP(SVDRPport); } -void cInterface::Cleanup(void) +cInterface::~cInterface() { + delete rcIo; delete SVDRP; } @@ -58,22 +54,29 @@ void cInterface::Close(void) cDvbApi::PrimaryDvbApi->Close(); } -unsigned int cInterface::GetCh(bool Wait) +unsigned int cInterface::GetCh(bool Wait, bool *Repeat, bool *Release) { - if (RcIo.InputAvailable(Wait)) { - unsigned int Command; - return RcIo.GetCommand(&Command, NULL) ? Command : 0; - } - return 0; + if (open) + cDvbApi::PrimaryDvbApi->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) { - if (open) - cDvbApi::PrimaryDvbApi->Flush(); if (SVDRP) SVDRP->Process(); - eKeys Key = keyFromWait != kNone ? keyFromWait : Keys.Get(GetCh(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; } @@ -85,13 +88,16 @@ void cInterface::PutKey(eKeys Key) eKeys cInterface::Wait(int Seconds, bool KeepChar) { - eKeys Key = kNone; if (open) cDvbApi::PrimaryDvbApi->Flush(); - RcIo.Flush(500); - if (cFile::AnyFileReady(-1, Seconds * 1000)) - Key = GetKey(); - if (KeepChar) + eKeys Key = kNone; + time_t timeout = time(NULL) + Seconds; + for (;;) { + Key = GetKey(); + if ((Key != kNone && (RAWKEY(Key) != kOk || RAWKEY(Key) == Key)) || time(NULL) > timeout) + break; + } + if (KeepChar && ISRAWKEY(Key)) keyFromWait = Key; return Key; } @@ -220,9 +226,11 @@ void cInterface::Help(const char *Red, const char *Green, const char *Yellow, co void cInterface::QueryKeys(void) { Keys.Clear(); + Clear(); WriteText(1, 1, "Learning Remote Control Keys"); WriteText(1, 3, "Phase 1: Detecting RC code type"); WriteText(1, 5, "Press any key on the RC unit"); + cDvbApi::PrimaryDvbApi->Flush(); #ifndef REMOTE_KBD unsigned char Code = 0; unsigned short Address; @@ -233,14 +241,16 @@ void cInterface::QueryKeys(void) break; #else //TODO on screen display... - if (RcIo.DetectCode(&Code, &Address)) { + if (rcIo->DetectCode(&Code, &Address)) { Keys.code = Code; Keys.address = Address; WriteText(1, 5, "RC code detected!"); WriteText(1, 6, "Do not press any key..."); - RcIo.Flush(3000); + cDvbApi::PrimaryDvbApi->Flush(); + rcIo->Flush(3000); ClearEol(0, 5); ClearEol(0, 6); + cDvbApi::PrimaryDvbApi->Flush(); break; } #endif @@ -328,7 +338,7 @@ eKeys cInterface::DisplayChannel(int Number, const char *Name, bool WithInfo) { // Number = 0 is used for channel group display and no EIT if (Number) - RcIo.Number(Number); + rcIo->Number(Number); if (Name && !Recording()) { Open(MenuColumns, 5); cDvbApi::PrimaryDvbApi->Fill(0, 0, MenuColumns, 1, clrBackground); @@ -387,7 +397,7 @@ eKeys cInterface::DisplayChannel(int Number, const char *Name, bool WithInfo) void cInterface::DisplayRecording(int Index, bool On) { - RcIo.SetPoints(1 << Index, On); + rcIo->SetPoints(1 << Index, On); } bool cInterface::Recording(void) diff --git a/interface.h b/interface.h index 8f7f1b8..8d0f8f6 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.13 2000/09/18 22:29:31 kls Exp $ + * $Id: interface.h 1.16 2000/10/08 12:15:49 kls Exp $ */ #ifndef __INTERFACE_H @@ -12,6 +12,7 @@ #include "config.h" #include "dvbapi.h" +#include "remote.h" #include "svdrp.h" class cInterface { @@ -22,14 +23,14 @@ private: int cols[MaxCols]; eKeys keyFromWait; cSVDRP *SVDRP; - unsigned int GetCh(bool Wait = true); + cRcIoBase *rcIo; + unsigned int GetCh(bool Wait = true, bool *Repeat = NULL, bool *Release = NULL); void QueryKeys(void); void HelpButton(int Index, const char *Text, eDvbColor FgColor, eDvbColor BgColor); eKeys Wait(int Seconds = 1, bool KeepChar = false); public: - cInterface(void); - void Init(int SVDRPport = 0); - void Cleanup(void); + cInterface(int SVDRPport = 0); + ~cInterface(); void Open(int NumCols = MenuColumns, int NumLines = MenuLines); void Close(void); eKeys GetKey(bool Wait = true); @@ -38,7 +39,7 @@ public: void ClearEol(int x, int y, eDvbColor Color = clrBackground); void SetCols(int *c); void Write(int x, int y, const char *s, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground); - void WriteText(int x, int y, const char *s, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBlack); + void WriteText(int x, int y, const char *s, eDvbColor FgColor = clrWhite, eDvbColor BgColor = clrBackground); void Title(const char *s); void Status(const char *s, eDvbColor FgColor = clrBlack, eDvbColor BgColor = clrCyan); void Info(const char *s); @@ -51,6 +52,6 @@ public: bool Recording(void); }; -extern cInterface Interface; +extern cInterface *Interface; #endif //__INTERFACE_H @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.c 1.30 2000/10/03 14:06:44 kls Exp $ + * $Id: menu.c 1.36 2000/10/08 16:11:22 kls Exp $ */ #include "menu.h" @@ -13,7 +13,6 @@ #include <stdio.h> #include <string.h> #include "config.h" -#include "recording.h" #define MENUTIMEOUT 120 // seconds @@ -94,11 +93,11 @@ eOSState cMenuEditIntItem::ProcessKey(eKeys Key) } newValue = *value * 10 + (Key - k0); } - else if (Key == kLeft) { // TODO might want to increase the delta if repeated quickly? + else if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly? newValue = *value - 1; fresh = true; } - else if (Key == kRight) { + else if (NORMALKEY(Key) == kRight) { newValue = *value + 1; fresh = true; } @@ -211,6 +210,7 @@ void cMenuEditDayItem::Set(void) eOSState cMenuEditDayItem::ProcessKey(eKeys Key) { switch (Key) { + case kLeft|k_Repeat: case kLeft: if (d > 0) *value = days[--d]; else if (d == 0) { @@ -225,6 +225,7 @@ eOSState cMenuEditDayItem::ProcessKey(eKeys Key) return cMenuEditIntItem::ProcessKey(Key); Set(); break; + case kRight|k_Repeat: case kRight: if (d >= 0) { *value = days[++d]; if (*value == 0) { @@ -310,7 +311,7 @@ eOSState cMenuEditTimeItem::ProcessKey(eKeys Key) break; } } - else if (Key == kLeft) { // TODO might want to increase the delta if repeated quickly? + else if (NORMALKEY(Key) == kLeft) { // TODO might want to increase the delta if repeated quickly? if (--mm < 0) { mm = 59; if (--hh < 0) @@ -318,7 +319,7 @@ eOSState cMenuEditTimeItem::ProcessKey(eKeys Key) } fresh = true; } - else if (Key == kRight) { + else if (NORMALKEY(Key) == kRight) { if (++mm > 59) { mm = 0; if (++hh > 23) @@ -377,11 +378,11 @@ eOSState cMenuEditChrItem::ProcessKey(eKeys Key) eOSState state = cMenuEditItem::ProcessKey(Key); if (state == osUnknown) { - if (Key == kLeft) { + if (NORMALKEY(Key) == kLeft) { if (current > allowed) current--; } - else if (Key == kRight) { + else if (NORMALKEY(Key) == kRight) { if (*(current + 1)) current++; } @@ -455,12 +456,14 @@ char cMenuEditStrItem::Inc(char c, bool Up) eOSState cMenuEditStrItem::ProcessKey(eKeys Key) { switch (Key) { + case kLeft|k_Repeat: case kLeft: if (pos > 0) { if (value[pos] == '^') value[pos] = 0; pos--; } break; + case kRight|k_Repeat: case kRight: if (pos < length && value[pos] != '^' && (pos < int(strlen(value) - 1) || value[pos] != ' ')) { if (++pos >= int(strlen(value))) { value[pos] = ' '; @@ -468,9 +471,11 @@ eOSState cMenuEditStrItem::ProcessKey(eKeys Key) } } break; + case kUp|k_Repeat: case kUp: + case kDown|k_Repeat: case kDown: if (pos >= 0) - value[pos] = Inc(value[pos], Key == kUp); + value[pos] = Inc(value[pos], NORMALKEY(Key) == kUp); else return cMenuEditItem::ProcessKey(Key); break; @@ -635,11 +640,11 @@ eOSState cMenuChannels::Del(void) // Check if there is a timer using this channel: for (cTimer *ti = Timers.First(); ti; ti = (cTimer *)ti->Next()) { if (ti->channel == DeletedChannel) { - Interface.Error("Channel is being used by a timer!"); + Interface->Error("Channel is being used by a timer!"); return osContinue; } } - if (Interface.Confirm("Delete Channel?")) { + if (Interface->Confirm("Delete Channel?")) { // Move and renumber the channels: Channels.Del(channel); Channels.ReNumber(); @@ -916,7 +921,7 @@ eOSState cMenuTimers::Del(void) cTimer *ti = Timers.Get(Index); if (ti) { if (!ti->recording) { - if (Interface.Confirm("Delete Timer?")) { + if (Interface->Confirm("Delete Timer?")) { Timers.Del(Timers.Get(Index)); cOsdMenu::Del(Index); Timers.Save(); @@ -925,7 +930,7 @@ eOSState cMenuTimers::Del(void) } } else - Interface.Error("Timer is recording!"); + Interface->Error("Timer is recording!"); } return osContinue; } @@ -990,28 +995,19 @@ void cMenuRecordingItem::Set(void) // --- cMenuRecordings ------------------------------------------------------- -class cMenuRecordings : public cOsdMenu { -private: - cRecordings Recordings; - eOSState Play(void); - eOSState Del(void); - eOSState Summary(void); -public: - cMenuRecordings(void); - virtual eOSState ProcessKey(eKeys Key); - }; - cMenuRecordings::cMenuRecordings(void) :cOsdMenu("Recordings", 6, 6) { if (Recordings.Load()) { + const char *lastReplayed = cReplayControl::LastReplayed(); cRecording *recording = Recordings.First(); while (recording) { - Add(new cMenuRecordingItem(recording)); + Add(new cMenuRecordingItem(recording), lastReplayed && strcmp(lastReplayed, recording->FileName()) == 0); recording = Recordings.Next(recording); } } - SetHelp("Play", NULL/*XXX"Resume"*/, "Delete", "Summary"); + SetHelp("Play", NULL, "Delete", "Summary"); + Display(); } eOSState cMenuRecordings::Play(void) @@ -1030,17 +1026,18 @@ eOSState cMenuRecordings::Del(void) if (ri) { //XXX what if this recording's file is currently in use??? //XXX if (!ti->recording) { - if (Interface.Confirm("Delete Recording?")) { + if (Interface->Confirm("Delete Recording?")) { if (ri->recording->Delete()) { + cReplayControl::ClearLastReplayed(ri->recording->FileName()); cOsdMenu::Del(Current()); Display(); } else - Interface.Error("Error while deleting recording!"); + Interface->Error("Error while deleting recording!"); } //XXX } //XXX else -//XXX Interface.Error("Timer is recording!"); +//XXX Interface->Error("Timer is recording!"); } return osContinue; } @@ -1065,6 +1062,7 @@ eOSState cMenuRecordings::ProcessKey(eKeys Key) case kRed: return Play(); case kYellow: return Del(); case kBlue: return Summary(); + case kMenu: return osEnd; default: break; } } @@ -1088,6 +1086,9 @@ cMenuSetup::cMenuSetup(void) Add(new cMenuEditIntItem( "PrimaryDVB", &data.PrimaryDVB, 1, cDvbApi::NumDvbApis)); Add(new cMenuEditBoolItem("ShowInfoOnChSwitch", &data.ShowInfoOnChSwitch)); Add(new cMenuEditBoolItem("MenuScrollPage", &data.MenuScrollPage)); + Add(new cMenuEditBoolItem("MarkInstantRecord", &data.MarkInstantRecord)); + Add(new cMenuEditIntItem( "LnbFrequLo", &data.LnbFrequLo)); + Add(new cMenuEditIntItem( "LnbFrequHi", &data.LnbFrequHi)); } eOSState cMenuSetup::ProcessKey(eKeys Key) @@ -1126,7 +1127,7 @@ cMenuMain::cMenuMain(bool Replaying) Add(new cOsdItem(buffer, osStopRecord)); delete buffer; } - SetHelp("Record"); + SetHelp("Record", NULL, NULL, cReplayControl::LastReplayed() ? "Resume" : NULL); Display(); lastActivity = time(NULL); } @@ -1140,7 +1141,7 @@ eOSState cMenuMain::ProcessKey(eKeys Key) case osTimer: return AddSubMenu(new cMenuTimers); case osRecordings: return AddSubMenu(new cMenuRecordings); case osSetup: return AddSubMenu(new cMenuSetup); - case osStopRecord: if (Interface.Confirm("Stop Recording?")) { + case osStopRecord: if (Interface->Confirm("Stop Recording?")) { cOsdItem *item = Get(Current()); if (item) { cRecordControls::Stop(item->Text() + strlen(STOP_RECORDING)); @@ -1152,6 +1153,9 @@ eOSState cMenuMain::ProcessKey(eKeys Key) case kRed: if (!HasSubMenu()) state = osRecord; break; + case kBlue: if (!HasSubMenu()) + state = osReplay; + break; default: break; } } @@ -1172,15 +1176,15 @@ cDirectChannelSelect::cDirectChannelSelect(eKeys FirstKey) oldNumber = CurrentChannel; number = 0; lastTime = time_ms(); - Interface.Open(MenuColumns, 1); + Interface->Open(MenuColumns, 1); ProcessKey(FirstKey); } cDirectChannelSelect::~cDirectChannelSelect() { if (number < 0) - Interface.DisplayChannel(oldNumber); - Interface.Close(); + Interface->DisplayChannel(oldNumber); + Interface->Close(); } eOSState cDirectChannelSelect::ProcessKey(eKeys Key) @@ -1194,9 +1198,9 @@ eOSState cDirectChannelSelect::ProcessKey(eKeys Key) int BufSize = MenuColumns + 1; char buffer[BufSize]; snprintf(buffer, BufSize, "%d %s", number, Name); - Interface.DisplayChannel(number); - Interface.Clear(); - Interface.Write(0, 0, buffer); + Interface->DisplayChannel(number); + Interface->Clear(); + Interface->Write(0, 0, buffer); lastTime = time_ms(); if (!channel) { number = -1; @@ -1235,14 +1239,14 @@ cRecordControl::cRecordControl(cDvbApi *DvbApi, cTimer *Timer) cRecording Recording(timer); if (dvbApi->StartRecord(Recording.FileName())) Recording.WriteSummary(); - Interface.DisplayRecording(dvbApi->Index(), true); + Interface->DisplayRecording(dvbApi->Index(), true); } cRecordControl::~cRecordControl() { Stop(true); delete instantId; - Interface.DisplayRecording(dvbApi->Index(), false); + Interface->DisplayRecording(dvbApi->Index(), false); } void cRecordControl::Stop(bool KeepInstant) @@ -1357,10 +1361,23 @@ void cReplayControl::SetRecording(const char *FileName, const char *Title) title = Title ? strdup(Title) : NULL; } +const char *cReplayControl::LastReplayed(void) +{ + return fileName; +} + +void cReplayControl::ClearLastReplayed(const char *FileName) +{ + if (fileName && FileName && strcmp(fileName, FileName) == 0) { + delete fileName; + fileName = NULL; + } +} + void cReplayControl::Show(void) { if (!visible) { - Interface.Open(MenuColumns, -3); + Interface->Open(MenuColumns, -3); needsFastResponse = visible = true; shown = dvbApi->ShowProgress(true); } @@ -1369,7 +1386,7 @@ void cReplayControl::Show(void) void cReplayControl::Hide(void) { if (visible) { - Interface.Close(); + Interface->Close(); needsFastResponse = visible = false; } } @@ -1388,10 +1405,16 @@ eOSState cReplayControl::ProcessKey(eKeys Key) return osEnd; case kLeft: dvbApi->Backward(); break; case kRight: dvbApi->Forward(); break; + case kLeft|k_Release: + case kRight|k_Release: + dvbApi->Play(); break; + case kGreen|k_Repeat: case kGreen: dvbApi->Skip(-60); break; + case kYellow|k_Repeat: case kYellow: dvbApi->Skip(60); break; case kMenu: Hide(); return osMenu; // allow direct switching to menu case kOk: visible ? Hide() : Show(); break; + case kBack: return osRecordings; default: return osUnknown; } return osContinue; @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: menu.h 1.10 2000/09/10 14:42:20 kls Exp $ + * $Id: menu.h 1.12 2000/10/08 15:21:52 kls Exp $ */ #ifndef _MENU_H @@ -14,6 +14,7 @@ #include "dvbapi.h" #include "osd.h" +#include "recording.h" class cMenuMain : public cOsdMenu { private: @@ -34,6 +35,17 @@ public: virtual eOSState ProcessKey(eKeys Key); }; +class cMenuRecordings : public cOsdMenu { +private: + cRecordings Recordings; + eOSState Play(void); + eOSState Del(void); + eOSState Summary(void); +public: + cMenuRecordings(void); + virtual eOSState ProcessKey(eKeys Key); + }; + class cRecordControl { private: cDvbApi *dvbApi; @@ -72,6 +84,8 @@ public: virtual eOSState ProcessKey(eKeys Key); bool Visible(void) { return visible; } static void SetRecording(const char *FileName, const char *Title); + static const char *LastReplayed(void); + static void ClearLastReplayed(const char *FileName); }; #endif //_MENU_H @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: osd.c 1.7 2000/09/10 08:24:50 kls Exp $ + * $Id: osd.c 1.9 2000/10/08 12:20:34 kls Exp $ */ #include "osd.h" @@ -64,7 +64,7 @@ void cOsdItem::Display(int Offset, eDvbColor FgColor, eDvbColor BgColor) if (Offset >= 0) offset = Offset; if (offset >= 0) - Interface.WriteText(0, offset + 2, text, userColor ? fgColor : FgColor, userColor ? bgColor : BgColor); + Interface->WriteText(0, offset + 2, text, userColor ? fgColor : FgColor, userColor ? bgColor : BgColor); } eOSState cOsdItem::ProcessKey(eKeys Key) @@ -88,7 +88,7 @@ cOsdMenu::cOsdMenu(char *Title, int c0, int c1, int c2, int c3, int c4) subMenu = NULL; helpRed = helpGreen = helpYellow = helpBlue = NULL; status = NULL; - Interface.Open(); + Interface->Open(); } cOsdMenu::~cOsdMenu() @@ -96,8 +96,8 @@ cOsdMenu::~cOsdMenu() delete title; delete subMenu; delete status; - Interface.Clear(); - Interface.Close(); + Interface->Clear(); + Interface->Close(); } void cOsdMenu::SetStatus(const char *s) @@ -105,7 +105,7 @@ void cOsdMenu::SetStatus(const char *s) delete status; status = s ? strdup(s) : NULL; if (visible) - Interface.Status(status); + Interface->Status(status); } void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, const char *Blue) @@ -117,7 +117,7 @@ void cOsdMenu::SetHelp(const char *Red, const char *Green, const char *Yellow, c helpBlue = Blue; if (visible) Display(); - //XXX Interface.Help(helpRed, helpGreen, helpYellow, helpBlue); + //XXX Interface->Help(helpRed, helpGreen, helpYellow, helpBlue); //XXX must clear unused button areas! } @@ -140,10 +140,10 @@ void cOsdMenu::Add(cOsdItem *Item, bool Current) void cOsdMenu::Display(void) { visible = true; - Interface.Clear(); - Interface.SetCols(cols); - Interface.Title(title); - Interface.Help(helpRed, helpGreen, helpYellow, helpBlue); + Interface->Clear(); + Interface->SetCols(cols); + Interface->Title(title); + Interface->Help(helpRed, helpGreen, helpYellow, helpBlue); int count = Count(); if (count > 0) { if (current < 0) @@ -164,7 +164,7 @@ void cOsdMenu::Display(void) break; } } - Interface.Status(status); + Interface->Status(status); } void cOsdMenu::RefreshCurrent(void) @@ -274,7 +274,9 @@ eOSState cOsdMenu::ProcessKey(eKeys Key) return state; } switch (Key) { + case kUp|k_Repeat: case kUp: CursorUp(); break; + case kDown|k_Repeat: case kDown: CursorDown(); break; case kBack: return osBack; case kOk: if (marked >= 0) { diff --git a/recording.c b/recording.c index 60ea4a1..f9ab016 100644 --- a/recording.c +++ b/recording.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recording.c 1.18 2000/10/03 12:39:28 kls Exp $ + * $Id: recording.c 1.19 2000/10/08 12:20:53 kls Exp $ */ #define _GNU_SOURCE @@ -254,7 +254,7 @@ bool cRecordings::Load(bool Deleted) result = Count() > 0; } else - Interface.Error("Error while opening pipe!"); + Interface->Error("Error while opening pipe!"); delete cmd; return result; } @@ -6,7 +6,7 @@ * * Ported to LIRC by Carsten Koch <Carsten.Koch@icem.de> 2000-06-16. * - * $Id: remote.c 1.15 2000/10/03 10:49:58 kls Exp $ + * $Id: remote.c 1.18 2000/10/08 16:49:41 kls Exp $ */ #include "remote.h" @@ -27,16 +27,11 @@ #include "config.h" #include "tools.h" -#define REPEATLIMIT 100 // ms -#define REPEATDELAY 250 // ms - // --- cRcIoBase ------------------------------------------------------------- cRcIoBase::cRcIoBase(void) { t = 0; - firstTime = lastTime = 0; - lastCommand = 0; } cRcIoBase::~cRcIoBase() @@ -69,12 +64,12 @@ void cRcIoKBD::Flush(int WaitMs) } } -bool cRcIoKBD::InputAvailable(bool Wait) +bool cRcIoKBD::InputAvailable(void) { - return f.Ready(Wait); + return f.Ready(false); } -bool cRcIoKBD::GetCommand(unsigned int *Command, unsigned short *) +bool cRcIoKBD::GetCommand(unsigned int *Command, bool *Repeat, bool *Release) { if (Command) { *Command = getch(); @@ -87,36 +82,122 @@ bool cRcIoKBD::GetCommand(unsigned int *Command, unsigned short *) #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)) { + 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) + if (tcsetattr(f, TCSAFLUSH, &t) == 0) { + Start(); return; + } } LOG_ERROR_STR(DeviceName); - f.Close(); + close(f); } else LOG_ERROR_STR(DeviceName); + f = -1; } cRcIoRCU::~cRcIoRCU() { + Stop(); } -int cRcIoRCU::ReceiveByte(bool Wait) +void cRcIoRCU::Action(void) +{ +#pragma pack(1) + union { + struct { + unsigned short address; + unsigned int command; + } data; + unsigned char raw[6]; + } buffer; +#pragma pack() + + dsyslog(LOG_INFO, "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; + } +} + +int cRcIoRCU::ReceiveByte(int TimeoutMs) { // Returns the byte if one was received within a timeout, -1 otherwise - if (InputAvailable(Wait)) { + if (cFile::FileReady(f, TimeoutMs)) { unsigned char b; if (read(f, &b, 1) == 1) return b; @@ -128,16 +209,16 @@ int cRcIoRCU::ReceiveByte(bool Wait) bool cRcIoRCU::SendByteHandshake(unsigned char c) { - if (f.IsOpen()) { + if (f >= 0) { int w = write(f, &c, 1); if (w == 1) { - for (int reply = ReceiveByte(); reply >= 0;) { + 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(false) < 0) + if (ReceiveByte() < 0) return false; } } @@ -152,6 +233,8 @@ bool cRcIoRCU::SendByteHandshake(unsigned char c) bool cRcIoRCU::SendByte(unsigned char c) { + LOCK_THREAD; + for (int retry = 5; retry--;) { if (SendByteHandshake(c)) return true; @@ -174,66 +257,34 @@ bool cRcIoRCU::SetMode(unsigned char Mode) void cRcIoRCU::Flush(int WaitMs) { - int t0 = time_ms(); + LOCK_THREAD; + int t0 = time_ms(); for (;;) { - while (ReceiveByte(false) >= 0) + while (ReceiveByte() >= 0) t0 = time_ms(); if (time_ms() - t0 >= WaitMs) break; } + receivedData = receivedRepeat = false; } -bool cRcIoRCU::InputAvailable(bool Wait) +bool cRcIoRCU::GetCommand(unsigned int *Command, bool *Repeat, bool *Release) { - return f.Ready(Wait); -} - -bool cRcIoRCU::GetCommand(unsigned int *Command, unsigned short *Address) -{ -#pragma pack(1) - union { - struct { - unsigned short address; - unsigned int command; - } data; - unsigned char raw[6]; - } buffer; -#pragma pack() - - Flush(); - if (Command && ReceiveByte() == 'X') { - for (int i = 0; i < 6; i++) { - int b = ReceiveByte(false); - if (b >= 0) - buffer.raw[i] = b; - else - return false; - } - if (Address) - *Address = ntohs(buffer.data.address); // the PIC sends bytes in "network order" - else if (address != ntohs(buffer.data.address)) - return false; - *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: - return false; - if (*Command == lastCommand) { - // let's have a timeout to avoid getting overrun by commands - int now = time_ms(); - int delta = now - lastTime; - lastTime = now; - if (delta < REPEATLIMIT) { // if commands come in rapidly... - if (now - firstTime < REPEATDELAY) - return false; // ...repeat function kicks in after a short delay - return true; - } + 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; } - lastTime = firstTime = time_ms(); - lastCommand = *Command; - return true; } if (time(NULL) - t > 60) { SendCommand(code); // in case the PIC listens to the wrong code @@ -254,6 +305,8 @@ bool cRcIoRCU::Digit(int n, int v) bool cRcIoRCU::Number(int n, bool Hex) { + LOCK_THREAD; + if (!Hex) { char buf[8]; sprintf(buf, "%4d", n & 0xFFFF); @@ -275,6 +328,8 @@ bool cRcIoRCU::Number(int n, bool Hex) bool cRcIoRCU::String(char *s) { + LOCK_THREAD; + const char *chars = mode == modeH ? "0123456789ABCDEF" : "0123456789-EHLP "; int n = 0; @@ -318,8 +373,11 @@ bool cRcIoRCU::DetectCode(unsigned char *Code, unsigned short *Address) sprintf(buf, "C0D%c", *Code); String(buf); SetCode(*Code, 0); - unsigned int Command; - if (GetCommand(&Command, Address)) { + delay_ms(REPEATDELAY); + receivedData = receivedRepeat = 0; + delay_ms(REPEATDELAY); + if (GetCommand()) { + *Address = receivedAddress; SetMode(modeB); String("----"); return true; @@ -337,77 +395,94 @@ bool cRcIoRCU::DetectCode(unsigned char *Code, unsigned short *Address) #elif defined REMOTE_LIRC +#define REPEATLIMIT 20 // ms +#define REPEATDELAY 350 // ms + cRcIoLIRC::cRcIoLIRC(char *DeviceName) { - repeat = 1; + *keyName = 0; + receivedData = receivedRepeat = false; struct sockaddr_un addr; addr.sun_family = AF_UNIX; strcpy(addr.sun_path, DeviceName); - int sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock >= 0) { - if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) >= 0) { - f.Open(sock); + 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(sock); + close(f); } else LOG_ERROR_STR(DeviceName); + f = -1; } cRcIoLIRC::~cRcIoLIRC() { + Stop(); } -const char *cRcIoLIRC::ReceiveString(void) +void cRcIoLIRC::Action(void) { - int oldrepeat = 1; - - if (repeat != 0) { - Flush(); - if (repeat != 0) { - oldrepeat = repeat; - Flush(REPEATLIMIT); - } - } - - if (repeat == 0) { - firstTime = time_ms(); - repeat = 1; - return keyName; - } - - if ((repeat > 1) && (repeat != oldrepeat) && (time_ms() > firstTime + REPEATDELAY)) { - repeat = 1; - return keyName; - } + dsyslog(LOG_INFO, "LIRC remote control thread started (pid=%d)", getpid()); - return NULL; -} - -void cRcIoLIRC::Flush(int WaitMs) -{ + int FirstTime = 0; char buf[LIRC_BUFFER_SIZE]; - int t0 = time_ms(); - - do { - if (InputAvailable(false) && (read(f, buf, sizeof(buf)) > 21)) - sscanf(buf, "%*x %x %7s", &repeat, keyName); // '7' in '%7s' is LIRC_KEY_BUF-1! - } while ((repeat != 0) && (time_ms() < t0 + WaitMs)); -} - -bool cRcIoLIRC::InputAvailable(bool Wait) -{ - return f.Ready(Wait); + char LastKeyName[LIRC_KEY_BUF]; + + for (; f >= 0;) { + + LOCK_THREAD; + + if (cFile::FileReady(f, REPEATLIMIT) && read(f, buf, sizeof(buf)) > 21) { + if (!receivedData) { // only accept new data the previous data has been fetched + int count; + sscanf(buf, "%*x %x %7s", &count, LastKeyName); // '7' in '%7s' 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; + } + 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 + 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 + receivedData = receivedRelease = true; + receivedRepeat = false; + WakeUp(); + } + } } -bool cRcIoLIRC::GetCommand(unsigned int *Command, unsigned short *) +bool cRcIoLIRC::GetCommand(unsigned int *Command, bool *Repeat, bool *Release) { - if (Command) { - const char *cmd = ReceiveString(); - if (cmd) { - *Command = Keys.Encode(cmd); + 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; } } @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: remote.h 1.10 2000/10/03 10:45:35 kls Exp $ + * $Id: remote.h 1.13 2000/10/08 12:11:34 kls Exp $ */ #ifndef __REMOTE_H @@ -12,26 +12,25 @@ #include <stdio.h> #include <time.h> +#include "thread.h" #include "tools.h" class cRcIoBase { protected: time_t t; - int firstTime, lastTime; - unsigned int lastCommand; cRcIoBase(void); - virtual ~cRcIoBase(); public: enum { modeH = 'h', modeB = 'b', modeS = 's' }; + 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) = 0; - virtual bool InputAvailable(bool Wait = false) = 0; - virtual bool GetCommand(unsigned int *Command, unsigned short *Address = NULL) = 0; + virtual void Flush(int WaitMs = 0) {} + virtual bool InputAvailable(void) = 0; + virtual bool GetCommand(unsigned int *Command = NULL, bool *Repeat = NULL, bool *Release = NULL) = 0; }; #if defined REMOTE_KBD @@ -43,23 +42,27 @@ public: cRcIoKBD(void); virtual ~cRcIoKBD(); virtual void Flush(int WaitMs = 0); - virtual bool InputAvailable(bool Wait = false); - virtual bool GetCommand(unsigned int *Command, unsigned short *Address = NULL); + 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 { +class cRcIoRCU : public cRcIoBase, private cThread { private: - cFile f; + 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(bool Wait = true); + 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(); @@ -70,25 +73,24 @@ public: virtual bool String(char *s); virtual bool DetectCode(unsigned char *Code, unsigned short *Address); virtual void Flush(int WaitMs = 0); - virtual bool InputAvailable(bool Wait = false); - virtual bool GetCommand(unsigned int *Command, unsigned short *Address = NULL); + virtual bool InputAvailable(void) { return receivedData; } + virtual bool GetCommand(unsigned int *Command = NULL, bool *Repeat = NULL, bool *Release = NULL); }; #elif defined REMOTE_LIRC -class cRcIoLIRC : public cRcIoBase { +class cRcIoLIRC : public cRcIoBase, private cThread { private: enum { LIRC_KEY_BUF = 8, LIRC_BUFFER_SIZE = 128 }; - cFile f; + int f; char keyName[LIRC_KEY_BUF]; - int repeat; - const char *ReceiveString(void); + bool receivedData, receivedRepeat, receivedRelease; + virtual void Action(void); public: cRcIoLIRC(char *DeviceName); virtual ~cRcIoLIRC(); - virtual void Flush(int WaitMs = 0); - virtual bool InputAvailable(bool Wait = false); - virtual bool GetCommand(unsigned int *Command, unsigned short *Address = NULL); + virtual bool InputAvailable(void) { return receivedData; } + virtual bool GetCommand(unsigned int *Command = NULL, bool *Repeat = NULL, bool *Release = NULL); }; #else @@ -2,3 +2,6 @@ PrimaryDVB = 1 ShowInfoOnChSwitch = 1 MenuScrollPage = 1 +MarkInstantRecord = 1 +LnbFrequLo = 9750 +LnbFrequHi = 10600 @@ -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.10 2000/09/17 13:39:37 kls Exp $ + * $Id: svdrp.c 1.11 2000/10/08 12:21:14 kls Exp $ */ #define _GNU_SOURCE @@ -326,7 +326,7 @@ void cSVDRP::CmdCHAN(const char *Option) Reply(501, "Undefined channel \"%s\"", Option); return; } - if (Interface.Recording()) { + if (Interface->Recording()) { Reply(550, "Can't switch channel, interface is recording"); return; } @@ -474,7 +474,7 @@ void cSVDRP::CmdHITK(const char *Option) if (*Option) { eKeys k = Keys.Translate(Option); if (k != kNone) { - Interface.PutKey(k); + Interface->PutKey(k); Reply(250, "Key \"%s\" accepted", Option); } else diff --git a/thread.c b/thread.c new file mode 100644 index 0000000..7304e68 --- /dev/null +++ b/thread.c @@ -0,0 +1,105 @@ +/* + * thread.c: A simple thread base class + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: thread.c 1.2 2000/10/08 16:45:50 kls Exp $ + */ + +#include "thread.h" +#include <signal.h> +#include <unistd.h> + +// --- cThread --------------------------------------------------------------- + +// The signal handler is necessary to be able to use SIGIO to wake up any +// pending 'select()' call. + +bool cThread::signalHandlerInstalled = false; + +cThread::cThread(void) +{ + if (!signalHandlerInstalled) { + signal(SIGIO, SignalHandler); + signalHandlerInstalled = true; + } + pthread_mutex_init(&mutex, NULL); + running = false; + parentPid = lockingPid = 0; + locked = 0; +} + +cThread::~cThread() +{ + pthread_mutex_destroy(&mutex); +} + +void cThread::SignalHandler(int signum) +{ + signal(signum, SignalHandler); +} + +void *cThread::StartThread(cThread *Thread) +{ + Thread->Action(); + return NULL; +} + +bool cThread::Start(void) +{ + if (!running) { + running = true; + parentPid = getpid(); + pthread_create(&thread, NULL, &StartThread, (void *)this); + } + return true; //XXX return value of pthread_create()??? +} + +void cThread::Stop(void) +{ + pthread_cancel(thread); +} + +bool cThread::Lock(void) +{ + if (!lockingPid || lockingPid != getpid()) { + pthread_mutex_lock(&mutex); + lockingPid = getpid(); + } + locked++; + return true; +} + +void cThread::Unlock(void) +{ + if (!--locked) { + lockingPid = 0; + pthread_mutex_unlock(&mutex); + } +} + +void cThread::WakeUp(void) +{ + kill(parentPid, SIGIO); // makes any waiting 'select()' call return immediately +} + +// --- cThreadLock ----------------------------------------------------------- + +cThreadLock::cThreadLock(cThread *Thread) +{ + thread = Thread; + locked = Thread->Lock(); +} + +cThreadLock::~cThreadLock() +{ + if (locked) + thread->Unlock(); +} + +bool cThreadLock::Locked(void) +{ + return locked; +} + diff --git a/thread.h b/thread.h new file mode 100644 index 0000000..86b9e92 --- /dev/null +++ b/thread.h @@ -0,0 +1,57 @@ +/* + * thread.h: A simple thread base class + * + * See the main source file 'vdr.c' for copyright information and + * how to reach the author. + * + * $Id: thread.h 1.1 2000/10/08 08:36:21 kls Exp $ + */ + +#ifndef __THREAD_H +#define __THREAD_H + +#include <pthread.h> +#include <sys/types.h> + +class cThread { + friend class cThreadLock; +private: + pthread_t thread; + pthread_mutex_t mutex; + pid_t parentPid, lockingPid; + int locked; + bool running; + static bool signalHandlerInstalled; + static void SignalHandler(int signum); + static void *StartThread(cThread *Thread); + bool Lock(void); + void Unlock(void); +protected: + void WakeUp(void); + virtual void Action(void) = 0; + void Stop(void); +public: + cThread(void); + virtual ~cThread(); + bool Start(void); + }; + +// cThreadLock can be used to easily set a lock in a thread and make absolutely +// sure that it will be unlocked when the block will be left. Several locks can +// be stacked, so a function that makes many calls to another function which uses +// cThreadLock may itself use a cThreadLock to make one longer lock instead of many +// short ones. + +class cThreadLock { +private: + cThread *thread; + bool locked; +public: + cThreadLock(cThread *Thread); + ~cThreadLock(); + bool Locked(void); + }; + +#define LOCK_THREAD cThreadLock ThreadLock(this) + +#endif //__THREAD_H diff --git a/timers.conf b/timers.conf index 2a58454..fc0e12a 100644 --- a/timers.conf +++ b/timers.conf @@ -1,16 +1,16 @@ 1:15:M------:2128:2205:99:7:Neues: 1:3:-T-----:2013:2125:99:99:SevenDays: 1:10:-T-----:2058:2202:99:10:Quarks: -1:26:-T-----:2235:2345:99:99:UFO: +1:26:-T-----:2320:0040:99:99:UFO: +1:14:--W----:1920:2020:99:99:Rettungsflieger: 1:2:--W----:2110:2325:99:99:BulleVonToelz: 1:3:---T---:2210:2315:99:10:IngoAppelt: -1:2:----F--:2140:2225:10:10:WWW: +0:2:----F--:2140:2225:10:10:WWW: 1:1:----F--:2212:2325:99:99:7Tage7Koepfe: 1:11:-----S-:2058:2135:99:99:Computer: 1:2:-----S-:2211:2340:99:30:Wochenshow: 1:11:------S:2013:2035:99:10:Centauri: 1:14:------S:2158:2235:99:14:MaxUndLisa: -0:15:MTWTF--:1828:1901:10:5:nano: +1:15:MTWTF--:1828:1901:10:5:nano: 1:1:-TWTF--:0955:1040:99:99:Ellen: -0:1:MTWTF--:1553:1710:99:99:Hammerman: -1:1:4:0755:0910:99:99:Hammerman: +1:1:MTWTF--:1553:1710:99:99:Hammerman: @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.c 1.20 2000/09/29 16:19:28 kls Exp $ + * $Id: tools.c 1.21 2000/10/07 18:02:24 kls Exp $ */ #define _GNU_SOURCE @@ -393,6 +393,22 @@ bool cFile::AnyFileReady(int FileDes, int TimeoutMs) return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && (FileDes < 0 || FD_ISSET(FileDes, &set)); } +bool cFile::FileReady(int FileDes, int TimeoutMs) +{ +#ifdef DEBUG_OSD + refresh(); +#endif + fd_set set; + struct timeval timeout; + FD_ZERO(&set); + FD_SET(FileDes, &set); + if (TimeoutMs < 100) + TimeoutMs = 100; + timeout.tv_sec = 0; + timeout.tv_usec = TimeoutMs * 1000; + return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set); +} + // --- cListObject ----------------------------------------------------------- cListObject::cListObject(void) @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: tools.h 1.16 2000/09/29 16:19:31 kls Exp $ + * $Id: tools.h 1.17 2000/10/07 18:00:21 kls Exp $ */ #ifndef __TOOLS_H @@ -68,6 +68,7 @@ public: int ReadString(char *Buffer, int Size); bool Ready(bool Wait = true); static bool AnyFileReady(int FileDes = -1, int TimeoutMs = 1000); + static bool FileReady(int FileDes, int TimeoutMs = 1000); }; class cListObject { @@ -22,7 +22,7 @@ * * The project's page is at http://www.cadsoft.de/people/kls/vdr * - * $Id: vdr.c 1.36 2000/10/03 13:52:26 kls Exp $ + * $Id: vdr.c 1.39 2000/10/08 14:49:25 kls Exp $ */ #include <getopt.h> @@ -161,6 +161,10 @@ int main(int argc, char *argv[]) if (!cDvbApi::Init()) abort(); + // User interface: + + Interface = new cInterface(SVDRPport); + // Configuration data: if (!ConfigDirectory) @@ -173,9 +177,8 @@ int main(int argc, char *argv[]) Keys.SetDummyValues(); #else if (!Keys.Load(AddDirectory(ConfigDirectory, KEYS_CONF))) - Interface.LearnKeys(); + Interface->LearnKeys(); #endif - Interface.Init(SVDRPport); cDvbApi::SetPrimaryDvbApi(Setup.PrimaryDVB); @@ -215,7 +218,7 @@ int main(int argc, char *argv[]) } // User Input: cOsdBase **Interact = Menu ? &Menu : (cOsdBase **)&ReplayControl; - eKeys key = Interface.GetKey(!*Interact || !(*Interact)->NeedsFastResponse()); + eKeys key = Interface->GetKey(!*Interact || !(*Interact)->NeedsFastResponse()); if (*Interact) { switch ((*Interact)->ProcessKey(key)) { case osMenu: DELETENULL(Menu); @@ -223,7 +226,12 @@ int main(int argc, char *argv[]) break; case osRecord: DELETENULL(Menu); if (!cRecordControls::Start()) - Interface.Error("No free DVB device to record!"); + Interface->Error("No free DVB device to record!"); + break; + case osRecordings: + DELETENULL(Menu); + DELETENULL(ReplayControl); + Menu = new cMenuRecordings; break; case osReplay: DELETENULL(Menu); DELETENULL(ReplayControl); @@ -235,7 +243,7 @@ int main(int argc, char *argv[]) break; case osSwitchDvb: DELETENULL(*Interact); - Interface.Info("Switching primary DVB..."); + Interface->Info("Switching primary DVB..."); cDvbApi::SetPrimaryDvbApi(Setup.PrimaryDVB); break; case osBack: @@ -253,14 +261,16 @@ int main(int argc, char *argv[]) break; // Direct Channel Select: case k1 ... k9: - if (!Interface.Recording()) + if (!Interface->Recording()) Menu = new cDirectChannelSelect(key); break; // Left/Right rotates trough channel groups: + case kLeft|k_Repeat: case kLeft: - case kRight: if (!Interface.Recording()) { + case kRight|k_Repeat: + case kRight: if (!Interface->Recording()) { int SaveGroup = CurrentGroup; - if (key == kRight) + if (NORMALKEY(key) == kRight) CurrentGroup = Channels.GetNextGroup(CurrentGroup) ; else CurrentGroup = Channels.GetPrevGroup(CurrentGroup < 1 ? 1 : CurrentGroup); @@ -271,9 +281,11 @@ int main(int argc, char *argv[]) } break; // Up/Down Channel Select: + case kUp|k_Repeat: case kUp: - case kDown: if (!Interface.Recording()) { - int n = CurrentChannel + (key == kUp ? 1 : -1); + case kDown|k_Repeat: + case kDown: if (!Interface->Recording()) { + int n = CurrentChannel + (NORMALKEY(key) == kUp ? 1 : -1); cChannel *channel = Channels.GetByNumber(n); if (channel) channel->Switch(); @@ -290,7 +302,7 @@ int main(int argc, char *argv[]) isyslog(LOG_INFO, "caught signal %d", Interrupted); delete Menu; delete ReplayControl; - Interface.Cleanup(); + delete Interface; cDvbApi::Cleanup(); isyslog(LOG_INFO, "exiting"); if (SysLogLevel > 0) |