vdr/remote.c
Klaus Schmidinger 8465398c6d Version 0.97
- Implemented a lock file to prevent more than one instance of VDR from removing
  files from the video directory at the same time.
- The new setup parameter SplitEditedFiles can be used to control whether or
  not the result of an editing process shall be written into separate files.
- Fixed handling repeat function when using LIRC (thanks to Matthias Weingart).
- The shutdown program (defined with the '-s' option) now also gets the channel
  number and recording title of the timer (see INSTALL).
- New channel data for 'Premiere One', 'Premiere X-Action', 'Fox Kids Türkce'
  and 'N24' (thanks to Andreas Share, Ulrich Röder, Uwe Scheffler and Simon
  Bauschulte). Note that if you are using the default 'channels.conf' or
  'channels.conf.cable' files you may need to check any exiting timers to see
  whether they still use the correct channel number.
- Fixed the "EPG bugfix" (sometimes had duplicate information in Subtitle and
  Extended Description).
- Fixed checking for valid video device when setting the video mode.
- The external command 'sort' is no longer required. VDR now sorts the list of
  recordings itself, making sure that recordings that stem from repeating timers
  are sorted chronologically. Sorting is done according to the setting of the
  current locale, so you may want to make sure LC_COLLATE is set to the desired
  value (see INSTALL).
- Fixed handling 'newline' characters in EPG texts (thanks to Rolf Hakenes for
  an improved version of his 'libdtv').
  Newline characters are always mapped to a single "blank" in VDR, because they
  would otherwise disturb the Title and Subtitle layout in the channel display
  (where these are assumed to be single line texts) and would have to be
  specially handled in the 'epg.data' file and the LSTE command in SVDRP.
- Mapping ` ("backtick") characters in EPG texts to ' (single quote).
- Fixed timers starting and ending at unexpected times. 'localtime()' was not
  thread safe, now using localtime_r().
- Removed the "system time seen..." message.
- Fixed a bug in the replay mode display when pressing the Green or Yellow
  button while in trick mode (thanks to Stefan Huelswitt)
- Closing all open file descriptors when calling external programs.
- The menu timeout now also works when pressing the "Back" button during replay
  to enter the "Recordings" menu.
- Updated 'channels.conf' for the "Bundesliga" channels of Premiere World
  (thanks to Helmut Schächner).
- Fixed reading timers.conf and channels.conf that contain blanks after numeric
  values.
- Fixed handling trick modes near the beginning and end of a recording.
- Pressing the "Back" button while replaying a DVD now leads to the DVD menu.
2001-10-21 18:00:00 +02:00

501 lines
12 KiB
C

/*
* remote.c: Interface to the Remote Control Unit
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* Ported to LIRC by Carsten Koch <Carsten.Koch@icem.de> 2000-06-16.
*
* $Id: remote.c 1.25 2001/09/30 11:39:49 kls Exp $
*/
#include "remote.h"
#include <fcntl.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <termios.h>
#include <unistd.h>
#if defined REMOTE_LIRC
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#endif
#include "config.h"
#include "tools.h"
// --- cRcIoBase -------------------------------------------------------------
cRcIoBase::cRcIoBase(void)
{
t = 0;
}
cRcIoBase::~cRcIoBase()
{
}
// --- cRcIoKBD --------------------------------------------------------------
#if defined REMOTE_KBD
cRcIoKBD::cRcIoKBD(void)
{
f.Open(0); // stdin
}
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()
{
Cancel();
}
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 (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(LOG_INFO, "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;
}
#endif