vdr/interface.c
Klaus Schmidinger f2937af95c Version 0.71
- Fixed 'Transfer Mode' in cases where a non-primary interface was switched to
  a channel that only the primary interface can receive (which could happen in
  the EPG scanner).
- The EPG scanner now starts with the first channel (it used to start with the
  second channel).
- Reacitvated setting the PNR.
- Adapted the frame scanning to the new muxing of the driver.
- The new compile time option REMOTE=NONE can be used to compile VDR without
  any remote control support (for applications where it shall be controlled
  exclusively via SVDRP).
- The new command line option -D can be used to define which DVB interfaces
  a certain instance of VDR shall use.
- The "Left" and "Right" keys are now used to page up and down in lists (thanks
  to Martin Hammerschmid). Since the "Timers" menu already uses these keys to
  (de)activate timers, this functionality is not available there.
- The "Main" and "Commands" menu now support "hotkeys", which means that if the
  first non-blank character of a menu item is a digit in the range 1..9, that
  item can be selected by pressing the respective numeric key on the remote
  control.
- The channel data in 'channels.conf' now contains the teletext PID (thanks to
  Dave Chapman). Existing files will be read normally (and the teletext PID set
  to 0), but once they are written back (due to some channel editing) the file
  will have the new format.
- The EPG scanner now scans each transponder only once per cycle.
- Deleted recordings are now automatically removed from disk after a while (not
  only when disk space is being needed for a new recording).
- Fixed repeat function in LIRC remote control.
- Changed the MAXDVBAPI macro in dvbapi.c to 4 in order to directly support the
  maximum possible number of DVB cards.
- The 'Ca' parameter in the default 'channels.conf' has been changed from '2'
  to '3' because the VDR prototype now has 3 DVB cards (and currently the CAM
  module only works if it is inserted into the last DVB card).
- The "Now", "Next" and "Schedule" menus now remember the current channel and
  restore the list when switching between them.
- The "Green" button in the "Recordings" menu can now be used to rewind a
  recording and play it from the very beginning.
- Fixed handling ':' in timer filenames and '\n' in timer summaries (see FORMATS).
- When removing recordings empty directories are now removed from the video
  directory.
- Added the "schnitt" tools from Matthias Schniedermeyer.
- New SVDRP command MESG to display a short message on the OSD.
- The Perl script 'svdrpsend.pl' can be used to send SVDRP commands to VDR.
- SVDRP can now immediately reuse the same port if VDR is restarted.
- SVDRP now has a timeout after which the connection is automatically closed
  (default is 300 seconds, can be changed in "Setup").
- The compile time switch VFAT can be used to make VDR avoid the ':' character
  in file names (VFAT can't handle them). Do 'make VFAT=1' to enable this.
- Support for DVB-C (thanks to Hans-Peter Raschke and Peter Hofmann).
  See the INSTALL file for more information about the use of VDR with cable.
- Fixed an occasional segfault in the EIT processor.
- A value of '0' for the EPGScanTimeout setup parameter now completely turns off
  scanning for EPG data on both single and multiple card systems.
- New setup parameter "PrimaryLimit" that allows to prevent timers from using the
  primary DVB interface in multi card systems. Default value is 0, which means
  that every timer may use the primary interface.
- The 'active' field of a timer will now be explicitly set to '1' if the user
  modifies an active timer (see FORMATS for details).
- The new command line option -w can be used to activate a watchdog that makes
  VDR exit in case the main program loop does not respond for more than the
  given number of seconds. This is mainly useful in combination with the new
  'runvdr' script that restarts VDR in case is has exited.
2001-02-24 18:00:00 +01:00

467 lines
11 KiB
C

/*
* interface.c: Abstract user interface layer
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: interface.c 1.35 2001/02/18 10:46:13 kls Exp $
*/
#include "interface.h"
#include <ctype.h>
#include <unistd.h>
#include "i18n.h"
cInterface *Interface = NULL;
cInterface::cInterface(int SVDRPport)
{
open = 0;
cols[0] = 0;
width = height = 0;
keyFromWait = kNone;
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;
}
void cInterface::Open(int NumCols, int NumLines)
{
if (!open++)
cDvbApi::PrimaryDvbApi->Open(width = NumCols, height = NumLines);
}
void cInterface::Close(void)
{
if (open == 1)
Clear();
if (!--open) {
cDvbApi::PrimaryDvbApi->Close();
width = height = 0;
}
}
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();
if (SVDRP) {
SVDRP->Process();
if (!open) {
char *message = SVDRP->GetMessage();
if (message) {
Info(message);
delete message;
}
}
}
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;
}
eKeys cInterface::Wait(int Seconds, bool KeepChar)
{
Flush();
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;
}
void cInterface::Clear(void)
{
if (open)
cDvbApi::PrimaryDvbApi->Clear();
}
void cInterface::ClearEol(int x, int y, eDvbColor Color)
{
if (open)
cDvbApi::PrimaryDvbApi->ClrEol(x, y, Color);
}
void cInterface::Fill(int x, int y, int w, int h, eDvbColor Color)
{
if (open)
cDvbApi::PrimaryDvbApi->Fill(x, y, w, h, Color);
}
void cInterface::SetBitmap(int x, int y, const cBitmap &Bitmap)
{
if (open)
cDvbApi::PrimaryDvbApi->SetBitmap(x, y, Bitmap);
}
void cInterface::Flush(void)
{
if (open)
cDvbApi::PrimaryDvbApi->Flush();
}
void cInterface::SetCols(int *c)
{
for (int i = 0; i < MaxCols; i++) {
cols[i] = *c++;
if (cols[i] == 0)
break;
}
}
eDvbFont cInterface::SetFont(eDvbFont Font)
{
return cDvbApi::PrimaryDvbApi->SetFont(Font);
}
char *cInterface::WrapText(const char *Text, int Width, int *Height)
{
// Wraps the Text to make it fit into the area defined by the given Width
// (which is given in character cells).
// The actual number of lines resulting from this operation is returned in
// Height.
// The returned string is newly created on the heap and the caller
// is responsible for deleting it once it is no longer used.
// Wrapping is done by inserting the necessary number of newline
// characters into the string.
int Lines = 1;
char *t = strdup(Text);
char *Blank = NULL;
char *Delim = NULL;
int w = 0;
Width *= cDvbApi::PrimaryDvbApi->CellWidth();
while (*t && t[strlen(t) - 1] == '\n')
t[strlen(t) - 1] = 0; // skips trailing newlines
for (char *p = t; *p; ) {
if (*p == '\n') {
Lines++;
w = 0;
Blank = Delim = NULL;
p++;
continue;
}
else if (isspace(*p))
Blank = p;
int cw = cDvbApi::PrimaryDvbApi->Width(*p);
if (w + cw > Width) {
if (Blank) {
*Blank = '\n';
p = Blank;
continue;
}
else {
// Here's the ugly part, where we don't have any whitespace to
// punch in a newline, so we need to make room for it:
if (Delim)
p = Delim + 1; // let's fall back to the most recent delimiter
char *s = new char[strlen(t) + 2]; // The additional '\n' plus the terminating '\0'
int l = p - t;
strncpy(s, t, l);
s[l] = '\n';
strcpy(s + l + 1, p);
delete t;
t = s;
p = t + l;
continue;
}
}
else
w += cw;
if (strchr("-.,:;!?_", *p)) {
Delim = p;
Blank = NULL;
}
p++;
}
*Height = Lines;
return t;
}
void cInterface::Write(int x, int y, const char *s, eDvbColor FgColor, eDvbColor BgColor)
{
if (open)
cDvbApi::PrimaryDvbApi->Text(x, y, s, FgColor, BgColor);
}
void cInterface::WriteText(int x, int y, const char *s, eDvbColor FgColor, eDvbColor BgColor)
{
if (open) {
ClearEol(x, y, BgColor);
int col = 0;
for (;;) {
const char *t = strchr(s, '\t');
const char *p = s;
char buf[1000];
if (t && col < MaxCols && cols[col] > 0) {
unsigned int n = t - s;
if (n >= sizeof(buf))
n = sizeof(buf) - 1;
strncpy(buf, s, n);
buf[n] = 0;
p = buf;
s = t + 1;
}
Write(x, y, p, FgColor, BgColor);
if (p == s)
break;
x += cols[col++];
}
}
}
void cInterface::Title(const char *s)
{
ClearEol(0, 0, clrCyan);
const char *t = strchr(s, '\t');
if (t) {
char buffer[Width() + 1];
unsigned int n = t - s;
if (n >= sizeof(buffer))
n = sizeof(buffer) - 1;
strn0cpy(buffer, s, n);
Write(1, 0, buffer, clrBlack, clrCyan);
t++;
Write(-(cDvbApi::PrimaryDvbApi->WidthInCells(t) + 1), 0, t, clrBlack, clrCyan);
}
else {
int x = (Width() - strlen(s)) / 2;
if (x < 0)
x = 0;
Write(x, 0, s, clrBlack, clrCyan);
}
}
void cInterface::Status(const char *s, eDvbColor FgColor, eDvbColor BgColor)
{
ClearEol(0, -3, s ? BgColor : clrBackground);
if (s)
Write(0, -3, s, FgColor, BgColor);
}
void cInterface::Info(const char *s)
{
Open();
isyslog(LOG_INFO, "info: %s", s);
Status(s, clrWhite, clrGreen);
Wait();
Status(NULL);
Close();
}
void cInterface::Error(const char *s)
{
Open();
esyslog(LOG_ERR, "ERROR: %s", s);
Status(s, clrWhite, clrRed);
Wait();
Status(NULL);
Close();
}
bool cInterface::Confirm(const char *s)
{
Open();
isyslog(LOG_INFO, "confirm: %s", s);
Status(s, clrBlack, clrGreen);
bool result = Wait(10) == kOk;
Status(NULL);
Close();
isyslog(LOG_INFO, "%sconfirmed", result ? "" : "not ");
return result;
}
void cInterface::HelpButton(int Index, const char *Text, eDvbColor FgColor, eDvbColor BgColor)
{
if (open && Text) {
const int w = Width() / 4;
int l = (w - strlen(Text)) / 2;
if (l < 0)
l = 0;
cDvbApi::PrimaryDvbApi->Fill(Index * w, -1, w, 1, BgColor);
cDvbApi::PrimaryDvbApi->Text(Index * w + l, -1, Text, FgColor, BgColor);
}
}
void cInterface::Help(const char *Red, const char *Green, const char *Yellow, const char *Blue)
{
HelpButton(0, Red, clrBlack, clrRed);
HelpButton(1, Green, clrBlack, clrGreen);
HelpButton(2, Yellow, clrBlack, clrYellow);
HelpButton(3, Blue, clrWhite, clrBlue);
}
void cInterface::QueryKeys(void)
{
Keys.Clear();
Clear();
WriteText(1, 1, tr("Learning Remote Control Keys"));
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);
delete 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;
}
}
break;
}
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);
}
}
void cInterface::LearnKeys(void)
{
isyslog(LOG_INFO, "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;
}
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 "PrimaryDvbApi" anyway
return cDvbApi::PrimaryDvbApi->Recording();
}