2000-02-19 13:36:48 +01:00
|
|
|
/*
|
2000-04-24 09:46:05 +02:00
|
|
|
* vdr.c: Video Disk Recorder main program
|
2000-02-19 13:36:48 +01:00
|
|
|
*
|
2006-01-08 11:49:03 +01:00
|
|
|
* Copyright (C) 2000, 2003, 2006 Klaus Schmidinger
|
2001-08-03 14:18:08 +02:00
|
|
|
*
|
2000-02-19 13:36:48 +01:00
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
2001-08-03 14:18:08 +02:00
|
|
|
*
|
2000-02-19 13:36:48 +01:00
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
2001-08-03 14:18:08 +02:00
|
|
|
*
|
2000-02-19 13:36:48 +01:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
2006-04-21 14:53:26 +02:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
2000-02-19 13:36:48 +01:00
|
|
|
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
2001-08-03 14:18:08 +02:00
|
|
|
*
|
2000-02-19 13:36:48 +01:00
|
|
|
* The author can be reached at kls@cadsoft.de
|
|
|
|
*
|
2003-05-29 12:44:06 +02:00
|
|
|
* The project's page is at http://www.cadsoft.de/vdr
|
2000-02-19 13:36:48 +01:00
|
|
|
*
|
2006-09-01 12:59:35 +02:00
|
|
|
* $Id: vdr.c 1.279 2006/09/01 12:57:44 kls Exp $
|
2000-02-19 13:36:48 +01:00
|
|
|
*/
|
|
|
|
|
2000-07-23 15:01:31 +02:00
|
|
|
#include <getopt.h>
|
2005-12-31 13:30:11 +01:00
|
|
|
#include <grp.h>
|
2001-10-07 11:00:35 +02:00
|
|
|
#include <locale.h>
|
2005-12-31 13:30:11 +01:00
|
|
|
#include <pwd.h>
|
2000-04-15 17:38:11 +02:00
|
|
|
#include <signal.h>
|
2000-07-23 15:01:31 +02:00
|
|
|
#include <stdlib.h>
|
2005-12-31 13:30:11 +01:00
|
|
|
#include <sys/capability.h>
|
|
|
|
#include <sys/prctl.h>
|
2002-12-08 14:30:32 +01:00
|
|
|
#include <termios.h>
|
2000-07-23 15:36:43 +02:00
|
|
|
#include <unistd.h>
|
2002-11-03 11:53:58 +01:00
|
|
|
#include "audio.h"
|
2002-10-06 10:25:42 +02:00
|
|
|
#include "channels.h"
|
2000-02-19 13:36:48 +01:00
|
|
|
#include "config.h"
|
2002-06-22 10:11:59 +02:00
|
|
|
#include "cutter.h"
|
2002-06-16 12:57:31 +02:00
|
|
|
#include "device.h"
|
2002-10-06 10:25:42 +02:00
|
|
|
#include "diseqc.h"
|
2002-08-04 14:57:29 +02:00
|
|
|
#include "dvbdevice.h"
|
2002-05-20 11:18:09 +02:00
|
|
|
#include "eitscan.h"
|
2003-12-22 13:29:24 +01:00
|
|
|
#include "epg.h"
|
2000-11-11 10:39:27 +01:00
|
|
|
#include "i18n.h"
|
2000-02-19 13:36:48 +01:00
|
|
|
#include "interface.h"
|
2002-09-29 13:40:45 +02:00
|
|
|
#include "keys.h"
|
2006-06-16 09:22:20 +02:00
|
|
|
#include "libsi/si.h"
|
2002-09-29 13:40:45 +02:00
|
|
|
#include "lirc.h"
|
2000-02-19 13:36:48 +01:00
|
|
|
#include "menu.h"
|
2004-05-16 10:35:36 +02:00
|
|
|
#include "osdbase.h"
|
2002-05-09 16:26:56 +02:00
|
|
|
#include "plugin.h"
|
2002-09-29 13:40:45 +02:00
|
|
|
#include "rcu.h"
|
2000-03-11 11:22:37 +01:00
|
|
|
#include "recording.h"
|
2004-05-16 10:35:36 +02:00
|
|
|
#include "skinclassic.h"
|
|
|
|
#include "skinsttng.h"
|
2002-10-06 10:25:42 +02:00
|
|
|
#include "sources.h"
|
2004-05-16 10:35:36 +02:00
|
|
|
#include "themes.h"
|
2002-10-20 12:28:55 +02:00
|
|
|
#include "timers.h"
|
2000-02-19 13:36:48 +01:00
|
|
|
#include "tools.h"
|
2004-01-17 17:19:34 +01:00
|
|
|
#include "transfer.h"
|
2000-07-29 15:21:42 +02:00
|
|
|
#include "videodir.h"
|
2000-02-19 13:36:48 +01:00
|
|
|
|
2004-10-17 11:50:21 +02:00
|
|
|
#define MINCHANNELWAIT 10 // seconds to wait between failed channel switchings
|
|
|
|
#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
|
|
|
|
#define CHANNELSAVEDELTA 600 // seconds before saving channels.conf after automatic modifications
|
2005-08-20 11:24:42 +02:00
|
|
|
#define LASTCAMMENUTIMEOUT 3 // seconds to run the main loop 'fast' after a CAM menu has been closed
|
|
|
|
// in order to react on a possible new CAM menu as soon as possible
|
2005-08-21 08:56:49 +02:00
|
|
|
#define DEVICEREADYTIMEOUT 30 // seconds to wait until all devices are ready
|
2005-09-03 11:51:54 +02:00
|
|
|
#define MENUTIMEOUT 120 // seconds of user inactivity after which an OSD display is closed
|
2006-01-29 14:37:43 +01:00
|
|
|
#define SHUTDOWNRETRY 300 // seconds before trying again to shut down
|
2006-06-04 09:10:59 +02:00
|
|
|
#define TIMERCHECKDELTA 10 // seconds between checks for timers that need to see their channel
|
|
|
|
#define TIMERDEVICETIMEOUT 8 // seconds before a device used for timer check may be reused
|
|
|
|
#define TIMERLOOKAHEADTIME 60 // seconds before a non-VPS timer starts and the channel is switched if possible
|
2006-05-07 09:13:36 +02:00
|
|
|
#define VPSLOOKAHEADTIME 24 // hours within which VPS timers will make sure their events are up to date
|
|
|
|
#define VPSUPTODATETIME 3600 // seconds before the event or schedule of a VPS timer needs to be refreshed
|
2001-02-11 14:53:44 +01:00
|
|
|
|
2004-10-23 15:17:03 +02:00
|
|
|
#define EXIT(v) { ExitCode = (v); goto Exit; }
|
|
|
|
|
2000-04-15 17:38:11 +02:00
|
|
|
static int Interrupted = 0;
|
|
|
|
|
2005-12-31 13:30:11 +01:00
|
|
|
static bool SetUser(const char *UserName)
|
|
|
|
{
|
|
|
|
if (UserName) {
|
|
|
|
struct passwd *user = getpwnam(UserName);
|
|
|
|
if (!user) {
|
|
|
|
fprintf(stderr, "vdr: unknown user: '%s'\n", UserName);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (setgid(user->pw_gid) < 0) {
|
|
|
|
fprintf(stderr, "vdr: cannot set group id %u: %s\n", (unsigned int)user->pw_gid, strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (initgroups(user->pw_name, user->pw_gid) < 0) {
|
|
|
|
fprintf(stderr, "vdr: cannot set supplemental group ids for user %s: %s\n", user->pw_name, strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (setuid(user->pw_uid) < 0) {
|
|
|
|
fprintf(stderr, "vdr: cannot set user id %u: %s\n", (unsigned int)user->pw_uid, strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
2006-02-05 12:58:59 +01:00
|
|
|
if (prctl(PR_SET_DUMPABLE, 2, 0, 0, 0) < 0) {
|
|
|
|
fprintf(stderr, "vdr: warning - cannot set dumpable: %s\n", strerror(errno));
|
|
|
|
// always non-fatal, and will not work with kernel < 2.6.13
|
|
|
|
}
|
2005-12-31 13:30:11 +01:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool SetCapSysTime(void)
|
|
|
|
{
|
|
|
|
// drop all capabilities except cap_sys_time
|
|
|
|
cap_t caps = cap_from_text("= cap_sys_time=ep");
|
|
|
|
if (!caps) {
|
|
|
|
fprintf(stderr, "vdr: cap_from_text failed: %s\n", strerror(errno));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (cap_set_proc(caps) == -1) {
|
|
|
|
fprintf(stderr, "vdr: cap_set_proc failed: %s\n", strerror(errno));
|
|
|
|
cap_free(caps);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
cap_free(caps);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool SetKeepCaps(bool On)
|
|
|
|
{
|
|
|
|
// set keeping capabilities during setuid() on/off
|
|
|
|
if (prctl(PR_SET_KEEPCAPS, On ? 1 : 0, 0, 0, 0) != 0) {
|
|
|
|
fprintf(stderr, "vdr: prctl failed\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2000-09-15 13:58:36 +02:00
|
|
|
static void SignalHandler(int signum)
|
2000-04-15 17:38:11 +02:00
|
|
|
{
|
2001-09-01 09:04:37 +02:00
|
|
|
if (signum != SIGPIPE) {
|
2000-09-15 13:58:36 +02:00
|
|
|
Interrupted = signum;
|
2001-09-01 09:04:37 +02:00
|
|
|
Interface->Interrupt();
|
|
|
|
}
|
2000-09-15 13:58:36 +02:00
|
|
|
signal(signum, SignalHandler);
|
2000-04-15 17:38:11 +02:00
|
|
|
}
|
|
|
|
|
2001-02-24 16:18:43 +01:00
|
|
|
static void Watchdog(int signum)
|
|
|
|
{
|
|
|
|
// Something terrible must have happened that prevented the 'alarm()' from
|
|
|
|
// being called in time, so let's get out of here:
|
2002-05-13 16:35:49 +02:00
|
|
|
esyslog("PANIC: watchdog timer expired - exiting!");
|
2001-02-24 16:18:43 +01:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2000-02-19 13:36:48 +01:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
2002-12-08 14:30:32 +01:00
|
|
|
// Save terminal settings:
|
|
|
|
|
|
|
|
struct termios savedTm;
|
2003-02-15 15:46:19 +01:00
|
|
|
bool HasStdin = (tcgetpgrp(STDIN_FILENO) == getpid() || getppid() != (pid_t)1) && tcgetattr(STDIN_FILENO, &savedTm) == 0;
|
2002-12-08 14:30:32 +01:00
|
|
|
|
2001-10-07 11:00:35 +02:00
|
|
|
// Initiate locale:
|
|
|
|
|
|
|
|
setlocale(LC_ALL, "");
|
|
|
|
|
2000-07-23 15:01:31 +02:00
|
|
|
// Command line options:
|
|
|
|
|
|
|
|
#define DEFAULTSVDRPPORT 2001
|
2001-02-24 16:18:43 +01:00
|
|
|
#define DEFAULTWATCHDOG 0 // seconds
|
2003-08-02 14:32:53 +02:00
|
|
|
#define DEFAULTPLUGINDIR PLUGINDIR
|
2003-12-22 13:29:24 +01:00
|
|
|
#define DEFAULTEPGDATAFILENAME "epg.data"
|
2000-07-23 15:01:31 +02:00
|
|
|
|
2005-12-31 13:30:11 +01:00
|
|
|
bool StartedAsRoot = false;
|
2006-01-13 16:16:32 +01:00
|
|
|
const char *VdrUser = NULL;
|
2000-07-23 15:01:31 +02:00
|
|
|
int SVDRPport = DEFAULTSVDRPPORT;
|
2002-11-03 11:53:58 +01:00
|
|
|
const char *AudioCommand = NULL;
|
2000-09-15 15:09:15 +02:00
|
|
|
const char *ConfigDirectory = NULL;
|
2003-12-22 13:29:24 +01:00
|
|
|
const char *EpgDataFileName = DEFAULTEPGDATAFILENAME;
|
2002-05-09 16:26:56 +02:00
|
|
|
bool DisplayHelp = false;
|
|
|
|
bool DisplayVersion = false;
|
2000-07-23 15:36:43 +02:00
|
|
|
bool DaemonMode = false;
|
2002-11-24 15:56:24 +01:00
|
|
|
int SysLogTarget = LOG_USER;
|
2002-03-08 16:37:42 +01:00
|
|
|
bool MuteAudio = false;
|
2001-02-24 16:18:43 +01:00
|
|
|
int WatchdogTimeout = DEFAULTWATCHDOG;
|
2001-09-01 09:04:37 +02:00
|
|
|
const char *Terminal = NULL;
|
|
|
|
const char *Shutdown = NULL;
|
2005-07-31 11:38:40 +02:00
|
|
|
|
|
|
|
bool UseKbd = true;
|
|
|
|
const char *LircDevice = NULL;
|
|
|
|
const char *RcuDevice = NULL;
|
|
|
|
#if !defined(REMOTE_KBD)
|
|
|
|
UseKbd = false;
|
|
|
|
#endif
|
|
|
|
#if defined(REMOTE_LIRC)
|
|
|
|
LircDevice = LIRC_DEVICE;
|
|
|
|
#elif defined(REMOTE_RCU)
|
|
|
|
RcuDevice = RCU_DEVICE;
|
|
|
|
#endif
|
2005-09-03 13:35:55 +02:00
|
|
|
#if defined(VFAT)
|
|
|
|
VfatFileSystem = true;
|
|
|
|
#endif
|
2006-01-13 16:16:32 +01:00
|
|
|
#if defined(VDR_USER)
|
|
|
|
VdrUser = VDR_USER;
|
|
|
|
#endif
|
2005-07-31 11:38:40 +02:00
|
|
|
|
2002-05-09 16:26:56 +02:00
|
|
|
cPluginManager PluginManager(DEFAULTPLUGINDIR);
|
2004-10-23 15:17:03 +02:00
|
|
|
int ExitCode = 0;
|
2000-07-23 15:01:31 +02:00
|
|
|
|
|
|
|
static struct option long_options[] = {
|
2001-06-24 17:42:19 +02:00
|
|
|
{ "audio", required_argument, NULL, 'a' },
|
2001-02-24 16:18:43 +01:00
|
|
|
{ "config", required_argument, NULL, 'c' },
|
|
|
|
{ "daemon", no_argument, NULL, 'd' },
|
|
|
|
{ "device", required_argument, NULL, 'D' },
|
2001-08-11 09:38:12 +02:00
|
|
|
{ "epgfile", required_argument, NULL, 'E' },
|
2005-12-30 15:11:16 +01:00
|
|
|
{ "grab", required_argument, NULL, 'g' },
|
2001-02-24 16:18:43 +01:00
|
|
|
{ "help", no_argument, NULL, 'h' },
|
2002-05-09 16:26:56 +02:00
|
|
|
{ "lib", required_argument, NULL, 'L' },
|
2005-07-31 11:38:40 +02:00
|
|
|
{ "lirc", optional_argument, NULL, 'l' | 0x100 },
|
2001-02-24 16:18:43 +01:00
|
|
|
{ "log", required_argument, NULL, 'l' },
|
2002-03-08 16:37:42 +01:00
|
|
|
{ "mute", no_argument, NULL, 'm' },
|
2005-07-31 11:38:40 +02:00
|
|
|
{ "no-kbd", no_argument, NULL, 'n' | 0x100 },
|
2002-05-09 16:26:56 +02:00
|
|
|
{ "plugin", required_argument, NULL, 'P' },
|
2001-02-24 16:18:43 +01:00
|
|
|
{ "port", required_argument, NULL, 'p' },
|
2005-07-31 11:38:40 +02:00
|
|
|
{ "rcu", optional_argument, NULL, 'r' | 0x100 },
|
2001-09-22 14:23:55 +02:00
|
|
|
{ "record", required_argument, NULL, 'r' },
|
2001-09-01 09:04:37 +02:00
|
|
|
{ "shutdown", required_argument, NULL, 's' },
|
|
|
|
{ "terminal", required_argument, NULL, 't' },
|
2005-12-31 13:30:11 +01:00
|
|
|
{ "user", required_argument, NULL, 'u' },
|
2002-03-29 10:10:04 +01:00
|
|
|
{ "version", no_argument, NULL, 'V' },
|
2005-09-03 13:35:55 +02:00
|
|
|
{ "vfat", no_argument, NULL, 'v' | 0x100 },
|
2001-02-24 16:18:43 +01:00
|
|
|
{ "video", required_argument, NULL, 'v' },
|
|
|
|
{ "watchdog", required_argument, NULL, 'w' },
|
2001-08-11 09:38:12 +02:00
|
|
|
{ NULL }
|
2000-07-23 15:01:31 +02:00
|
|
|
};
|
2001-08-03 14:18:08 +02:00
|
|
|
|
2000-07-23 15:01:31 +02:00
|
|
|
int c;
|
2005-12-31 13:30:11 +01:00
|
|
|
while ((c = getopt_long(argc, argv, "a:c:dD:E:g:hl:L:mp:P:r:s:t:u:v:Vw:", long_options, NULL)) != -1) {
|
2000-07-23 15:01:31 +02:00
|
|
|
switch (c) {
|
2002-11-03 11:53:58 +01:00
|
|
|
case 'a': AudioCommand = optarg;
|
2001-06-24 17:42:19 +02:00
|
|
|
break;
|
2000-09-15 15:09:15 +02:00
|
|
|
case 'c': ConfigDirectory = optarg;
|
|
|
|
break;
|
2000-07-23 15:36:43 +02:00
|
|
|
case 'd': DaemonMode = true; break;
|
2001-02-02 15:49:46 +01:00
|
|
|
case 'D': if (isnumber(optarg)) {
|
|
|
|
int n = atoi(optarg);
|
2002-06-16 12:57:31 +02:00
|
|
|
if (0 <= n && n < MAXDEVICES) {
|
|
|
|
cDevice::SetUseDevice(n);
|
2001-02-02 15:49:46 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(stderr, "vdr: invalid DVB device number: %s\n", optarg);
|
2001-04-01 11:18:28 +02:00
|
|
|
return 2;
|
2001-02-02 15:49:46 +01:00
|
|
|
break;
|
2003-12-22 13:29:24 +01:00
|
|
|
case 'E': EpgDataFileName = (*optarg != '-' ? optarg : NULL);
|
2001-08-11 09:38:12 +02:00
|
|
|
break;
|
2005-12-30 15:11:16 +01:00
|
|
|
case 'g': cSVDRP::SetGrabImageDir(*optarg != '-' ? optarg : NULL);
|
|
|
|
break;
|
2002-05-09 16:26:56 +02:00
|
|
|
case 'h': DisplayHelp = true;
|
2000-07-23 15:01:31 +02:00
|
|
|
break;
|
2002-11-24 15:56:24 +01:00
|
|
|
case 'l': {
|
|
|
|
char *p = strchr(optarg, '.');
|
|
|
|
if (p)
|
|
|
|
*p = 0;
|
|
|
|
if (isnumber(optarg)) {
|
|
|
|
int l = atoi(optarg);
|
|
|
|
if (0 <= l && l <= 3) {
|
|
|
|
SysLogLevel = l;
|
|
|
|
if (!p)
|
|
|
|
break;
|
|
|
|
if (isnumber(p + 1)) {
|
2003-01-26 11:57:55 +01:00
|
|
|
int l = atoi(p + 1);
|
2002-11-24 15:56:24 +01:00
|
|
|
if (0 <= l && l <= 7) {
|
|
|
|
int targets[] = { LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2, LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5, LOG_LOCAL6, LOG_LOCAL7 };
|
|
|
|
SysLogTarget = targets[l];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (p)
|
|
|
|
*p = '.';
|
2000-07-29 19:03:09 +02:00
|
|
|
fprintf(stderr, "vdr: invalid log level: %s\n", optarg);
|
2001-04-01 11:18:28 +02:00
|
|
|
return 2;
|
2002-11-24 15:56:24 +01:00
|
|
|
}
|
2000-07-29 19:03:09 +02:00
|
|
|
break;
|
2002-05-18 10:36:35 +02:00
|
|
|
case 'L': if (access(optarg, R_OK | X_OK) == 0)
|
|
|
|
PluginManager.SetDirectory(optarg);
|
|
|
|
else {
|
|
|
|
fprintf(stderr, "vdr: can't access plugin directory: %s\n", optarg);
|
|
|
|
return 2;
|
|
|
|
}
|
2002-05-09 16:26:56 +02:00
|
|
|
break;
|
2005-07-31 11:38:40 +02:00
|
|
|
case 'l' | 0x100:
|
|
|
|
LircDevice = optarg ? : LIRC_DEVICE;
|
|
|
|
break;
|
2002-03-08 16:37:42 +01:00
|
|
|
case 'm': MuteAudio = true;
|
|
|
|
break;
|
2005-07-31 11:38:40 +02:00
|
|
|
case 'n' | 0x100:
|
|
|
|
UseKbd = false;
|
|
|
|
break;
|
2000-07-23 15:01:31 +02:00
|
|
|
case 'p': if (isnumber(optarg))
|
2000-07-29 19:03:09 +02:00
|
|
|
SVDRPport = atoi(optarg);
|
2000-07-23 15:01:31 +02:00
|
|
|
else {
|
|
|
|
fprintf(stderr, "vdr: invalid port number: %s\n", optarg);
|
2001-04-01 11:18:28 +02:00
|
|
|
return 2;
|
2000-07-23 15:01:31 +02:00
|
|
|
}
|
|
|
|
break;
|
2002-05-09 16:26:56 +02:00
|
|
|
case 'P': PluginManager.AddPlugin(optarg);
|
|
|
|
break;
|
2005-07-31 11:38:40 +02:00
|
|
|
case 'r' | 0x100:
|
|
|
|
RcuDevice = optarg ? : RCU_DEVICE;
|
|
|
|
break;
|
2001-09-23 14:02:11 +02:00
|
|
|
case 'r': cRecordingUserCommand::SetCommand(optarg);
|
2001-09-22 14:23:55 +02:00
|
|
|
break;
|
2001-09-01 09:04:37 +02:00
|
|
|
case 's': Shutdown = optarg;
|
|
|
|
break;
|
2001-03-31 10:32:58 +02:00
|
|
|
case 't': Terminal = optarg;
|
2003-09-05 13:02:15 +02:00
|
|
|
if (access(Terminal, R_OK | W_OK) < 0) {
|
|
|
|
fprintf(stderr, "vdr: can't access terminal: %s\n", Terminal);
|
|
|
|
return 2;
|
|
|
|
}
|
2001-03-31 10:32:58 +02:00
|
|
|
break;
|
2005-12-31 13:30:11 +01:00
|
|
|
case 'u': if (*optarg)
|
|
|
|
VdrUser = optarg;
|
|
|
|
break;
|
2002-05-09 16:26:56 +02:00
|
|
|
case 'V': DisplayVersion = true;
|
2002-03-29 10:10:04 +01:00
|
|
|
break;
|
2005-09-03 13:35:55 +02:00
|
|
|
case 'v' | 0x100:
|
|
|
|
VfatFileSystem = true;
|
|
|
|
break;
|
2000-07-28 13:44:31 +02:00
|
|
|
case 'v': VideoDirectory = optarg;
|
2000-09-17 14:18:14 +02:00
|
|
|
while (optarg && *optarg && optarg[strlen(optarg) - 1] == '/')
|
|
|
|
optarg[strlen(optarg) - 1] = 0;
|
2000-07-28 13:44:31 +02:00
|
|
|
break;
|
2002-11-24 15:56:24 +01:00
|
|
|
case 'w': if (isnumber(optarg)) { int t = atoi(optarg);
|
2001-02-24 16:18:43 +01:00
|
|
|
if (t >= 0) {
|
|
|
|
WatchdogTimeout = t;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(stderr, "vdr: invalid watchdog timeout: %s\n", optarg);
|
2001-04-01 11:18:28 +02:00
|
|
|
return 2;
|
2001-02-24 16:18:43 +01:00
|
|
|
break;
|
2001-04-01 11:18:28 +02:00
|
|
|
default: return 2;
|
2000-07-23 15:01:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-12-31 13:30:11 +01:00
|
|
|
// Set user id in case we were started as root:
|
|
|
|
|
2006-04-14 11:04:20 +02:00
|
|
|
if (VdrUser && geteuid() == 0) {
|
2005-12-31 13:30:11 +01:00
|
|
|
StartedAsRoot = true;
|
2006-01-09 16:54:35 +01:00
|
|
|
if (strcmp(VdrUser, "root")) {
|
|
|
|
if (!SetKeepCaps(true))
|
|
|
|
return 2;
|
|
|
|
if (!SetUser(VdrUser))
|
|
|
|
return 2;
|
|
|
|
if (!SetKeepCaps(false))
|
|
|
|
return 2;
|
|
|
|
if (!SetCapSysTime())
|
|
|
|
return 2;
|
|
|
|
}
|
2005-12-31 13:30:11 +01:00
|
|
|
}
|
|
|
|
|
2002-05-09 16:26:56 +02:00
|
|
|
// Help and version info:
|
|
|
|
|
|
|
|
if (DisplayHelp || DisplayVersion) {
|
|
|
|
if (!PluginManager.HasPlugins())
|
|
|
|
PluginManager.AddPlugin("*"); // adds all available plugins
|
|
|
|
PluginManager.LoadPlugins();
|
|
|
|
if (DisplayHelp) {
|
|
|
|
printf("Usage: vdr [OPTIONS]\n\n" // for easier orientation, this is column 80|
|
|
|
|
" -a CMD, --audio=CMD send Dolby Digital audio to stdin of command CMD\n"
|
|
|
|
" -c DIR, --config=DIR read config files from DIR (default is to read them\n"
|
|
|
|
" from the video directory)\n"
|
|
|
|
" -d, --daemon run in daemon mode\n"
|
|
|
|
" -D NUM, --device=NUM use only the given DVB device (NUM = 0, 1, 2...)\n"
|
|
|
|
" there may be several -D options (default: all DVB\n"
|
|
|
|
" devices will be used)\n"
|
2005-12-31 13:30:11 +01:00
|
|
|
" -E FILE, --epgfile=FILE write the EPG data into the given FILE (default is\n"
|
2003-12-22 13:29:24 +01:00
|
|
|
" '%s' in the video directory)\n"
|
|
|
|
" '-E-' disables this\n"
|
2002-05-09 16:26:56 +02:00
|
|
|
" if FILE is a directory, the default EPG file will be\n"
|
|
|
|
" created in that directory\n"
|
2005-12-31 13:30:11 +01:00
|
|
|
" -g DIR, --grab=DIR write images from the SVDRP command GRAB into the\n"
|
2005-12-30 15:11:16 +01:00
|
|
|
" given DIR; DIR must be the full path name of an\n"
|
|
|
|
" existing directory, without any \"..\", double '/'\n"
|
|
|
|
" or symlinks (default: none, same as -g-)\n"
|
2002-05-09 16:26:56 +02:00
|
|
|
" -h, --help print this help and exit\n"
|
|
|
|
" -l LEVEL, --log=LEVEL set log level (default: 3)\n"
|
|
|
|
" 0 = no logging, 1 = errors only,\n"
|
|
|
|
" 2 = errors and info, 3 = errors, info and debug\n"
|
2002-11-24 15:56:24 +01:00
|
|
|
" if logging should be done to LOG_LOCALn instead of\n"
|
|
|
|
" LOG_USER, add '.n' to LEVEL, as in 3.7 (n=0..7)\n"
|
2002-05-09 16:26:56 +02:00
|
|
|
" -L DIR, --lib=DIR search for plugins in DIR (default is %s)\n"
|
2005-07-31 11:38:40 +02:00
|
|
|
" --lirc[=PATH] use a LIRC remote control device, attached to PATH\n"
|
|
|
|
" (default: %s)\n"
|
2002-05-09 16:26:56 +02:00
|
|
|
" -m, --mute mute audio of the primary DVB device at startup\n"
|
2005-07-31 11:38:40 +02:00
|
|
|
" --no-kbd don't use the keyboard as an input device\n"
|
2002-05-09 16:26:56 +02:00
|
|
|
" -p PORT, --port=PORT use PORT for SVDRP (default: %d)\n"
|
|
|
|
" 0 turns off SVDRP\n"
|
|
|
|
" -P OPT, --plugin=OPT load a plugin defined by the given options\n"
|
2005-07-31 11:38:40 +02:00
|
|
|
" --rcu[=PATH] use a remote control device, attached to PATH\n"
|
|
|
|
" (default: %s)\n"
|
2002-05-09 16:26:56 +02:00
|
|
|
" -r CMD, --record=CMD call CMD before and after a recording\n"
|
|
|
|
" -s CMD, --shutdown=CMD call CMD to shutdown the computer\n"
|
|
|
|
" -t TTY, --terminal=TTY controlling tty\n"
|
2006-01-13 16:16:32 +01:00
|
|
|
" -u USER, --user=USER run as user USER; only applicable if started as\n"
|
|
|
|
" root\n"
|
2002-05-09 16:26:56 +02:00
|
|
|
" -v DIR, --video=DIR use DIR as video directory (default: %s)\n"
|
|
|
|
" -V, --version print version information and exit\n"
|
2005-09-03 13:35:55 +02:00
|
|
|
" --vfat encode special characters in recording names to\n"
|
|
|
|
" avoid problems with VFAT file systems\n"
|
2002-05-09 16:26:56 +02:00
|
|
|
" -w SEC, --watchdog=SEC activate the watchdog timer with a timeout of SEC\n"
|
|
|
|
" seconds (default: %d); '0' disables the watchdog\n"
|
|
|
|
"\n",
|
2003-12-22 13:29:24 +01:00
|
|
|
DEFAULTEPGDATAFILENAME,
|
2002-05-09 16:26:56 +02:00
|
|
|
DEFAULTPLUGINDIR,
|
2005-07-31 11:38:40 +02:00
|
|
|
LIRC_DEVICE,
|
2002-05-09 16:26:56 +02:00
|
|
|
DEFAULTSVDRPPORT,
|
2005-07-31 11:38:40 +02:00
|
|
|
RCU_DEVICE,
|
2002-05-09 16:26:56 +02:00
|
|
|
VideoDirectory,
|
|
|
|
DEFAULTWATCHDOG
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (DisplayVersion)
|
2006-04-22 11:28:37 +02:00
|
|
|
printf("vdr (%s/%s) - The Video Disk Recorder\n", VDRVERSION, APIVERSION);
|
2002-05-09 16:26:56 +02:00
|
|
|
if (PluginManager.HasPlugins()) {
|
|
|
|
if (DisplayHelp)
|
|
|
|
printf("Plugins: vdr -P\"name [OPTIONS]\"\n\n");
|
|
|
|
for (int i = 0; ; i++) {
|
|
|
|
cPlugin *p = PluginManager.GetPlugin(i);
|
|
|
|
if (p) {
|
|
|
|
const char *help = p->CommandLineHelp();
|
|
|
|
printf("%s (%s) - %s\n", p->Name(), p->Version(), p->Description());
|
|
|
|
if (DisplayHelp && help) {
|
|
|
|
printf("\n");
|
|
|
|
puts(help);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-05-26 10:47:49 +02:00
|
|
|
// Check for UTF-8 and exit if present - asprintf() will fail if it encounters 8 bit ASCII codes
|
|
|
|
char *LangEnv;
|
|
|
|
if ((LangEnv = getenv("LANG")) != NULL && strcasestr(LangEnv, "utf") ||
|
2006-09-01 12:59:35 +02:00
|
|
|
(LangEnv = getenv("LC_ALL")) != NULL && strcasestr(LangEnv, "utf") ||
|
2005-05-26 10:47:49 +02:00
|
|
|
(LangEnv = getenv("LC_CTYPE")) != NULL && strcasestr(LangEnv, "utf")) {
|
|
|
|
fprintf(stderr, "vdr: please turn off UTF-8 before starting VDR\n");
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
2000-07-23 15:01:31 +02:00
|
|
|
// Log file:
|
2001-08-03 14:18:08 +02:00
|
|
|
|
2000-07-29 19:03:09 +02:00
|
|
|
if (SysLogLevel > 0)
|
2006-01-15 16:42:37 +01:00
|
|
|
openlog("vdr", LOG_CONS, SysLogTarget); // LOG_PID doesn't work as expected under NPTL
|
2000-07-23 15:36:43 +02:00
|
|
|
|
2000-07-28 13:44:31 +02:00
|
|
|
// Check the video directory:
|
|
|
|
|
2000-07-29 15:21:42 +02:00
|
|
|
if (!DirectoryOk(VideoDirectory, true)) {
|
2000-07-28 13:44:31 +02:00
|
|
|
fprintf(stderr, "vdr: can't access video directory %s\n", VideoDirectory);
|
2001-04-01 11:18:28 +02:00
|
|
|
return 2;
|
2000-07-28 13:44:31 +02:00
|
|
|
}
|
|
|
|
|
2000-07-23 15:36:43 +02:00
|
|
|
// Daemon mode:
|
|
|
|
|
|
|
|
if (DaemonMode) {
|
2005-10-09 10:05:21 +02:00
|
|
|
if (daemon(1, 0) == -1) {
|
2005-12-31 13:30:11 +01:00
|
|
|
fprintf(stderr, "vdr: %m\n");
|
2002-05-13 16:35:49 +02:00
|
|
|
esyslog("ERROR: %m");
|
2001-04-01 11:18:28 +02:00
|
|
|
return 2;
|
2000-07-23 15:36:43 +02:00
|
|
|
}
|
|
|
|
}
|
2001-03-31 10:32:58 +02:00
|
|
|
else if (Terminal) {
|
|
|
|
// Claim new controlling terminal
|
|
|
|
stdin = freopen(Terminal, "r", stdin);
|
|
|
|
stdout = freopen(Terminal, "w", stdout);
|
|
|
|
stderr = freopen(Terminal, "w", stderr);
|
2003-05-24 11:08:47 +02:00
|
|
|
HasStdin = true;
|
2001-03-31 10:32:58 +02:00
|
|
|
}
|
|
|
|
|
2002-05-13 16:35:49 +02:00
|
|
|
isyslog("VDR version %s started", VDRVERSION);
|
2006-01-13 16:16:32 +01:00
|
|
|
if (StartedAsRoot && VdrUser)
|
2005-12-31 13:30:11 +01:00
|
|
|
isyslog("switched to user '%s'", VdrUser);
|
2006-01-03 10:20:41 +01:00
|
|
|
if (DaemonMode)
|
|
|
|
dsyslog("running as daemon (tid=%d)", cThread::ThreadId());
|
|
|
|
cThread::SetMainThreadId();
|
2000-02-19 13:36:48 +01:00
|
|
|
|
2004-10-31 09:37:56 +01:00
|
|
|
// Main program loop variables - need to be here to have them initialized before any EXIT():
|
|
|
|
|
|
|
|
cOsdObject *Menu = NULL;
|
2006-01-05 13:54:04 +01:00
|
|
|
int LastChannel = 0;
|
2004-10-31 09:37:56 +01:00
|
|
|
int LastTimerChannel = -1;
|
|
|
|
int PreviousChannel[2] = { 1, 1 };
|
|
|
|
int PreviousChannelIndex = 0;
|
|
|
|
time_t LastChannelChanged = time(NULL);
|
|
|
|
time_t LastActivity = 0;
|
2005-08-20 11:24:42 +02:00
|
|
|
time_t LastCamMenu = 0;
|
2004-10-31 09:37:56 +01:00
|
|
|
int MaxLatencyTime = 0;
|
|
|
|
bool ForceShutdown = false;
|
|
|
|
bool UserShutdown = false;
|
2006-06-04 09:10:59 +02:00
|
|
|
bool InhibitEpgScan = false;
|
2006-01-06 12:53:28 +01:00
|
|
|
bool IsInfoMenu = false;
|
2006-06-03 14:46:36 +02:00
|
|
|
cSkin *CurrentSkin = NULL;
|
2004-10-31 09:37:56 +01:00
|
|
|
|
2002-05-09 16:26:56 +02:00
|
|
|
// Load plugins:
|
|
|
|
|
|
|
|
if (!PluginManager.LoadPlugins(true))
|
2004-10-23 15:17:03 +02:00
|
|
|
EXIT(2);
|
2002-05-09 16:26:56 +02:00
|
|
|
|
2000-07-23 15:01:31 +02:00
|
|
|
// Configuration data:
|
|
|
|
|
2000-09-15 15:09:15 +02:00
|
|
|
if (!ConfigDirectory)
|
|
|
|
ConfigDirectory = VideoDirectory;
|
|
|
|
|
2002-05-13 16:11:19 +02:00
|
|
|
cPlugin::SetConfigDirectory(ConfigDirectory);
|
2004-12-26 12:45:22 +01:00
|
|
|
cThemes::SetThemesDirectory(AddDirectory(ConfigDirectory, "themes"));
|
|
|
|
|
|
|
|
Setup.Load(AddDirectory(ConfigDirectory, "setup.conf"));
|
|
|
|
if (!(Sources.Load(AddDirectory(ConfigDirectory, "sources.conf"), true, true) &&
|
|
|
|
Diseqcs.Load(AddDirectory(ConfigDirectory, "diseqc.conf"), true, Setup.DiSEqC) &&
|
|
|
|
Channels.Load(AddDirectory(ConfigDirectory, "channels.conf"), false, true) &&
|
|
|
|
Timers.Load(AddDirectory(ConfigDirectory, "timers.conf")) &&
|
|
|
|
Commands.Load(AddDirectory(ConfigDirectory, "commands.conf"), true) &&
|
|
|
|
RecordingCommands.Load(AddDirectory(ConfigDirectory, "reccmds.conf"), true) &&
|
|
|
|
SVDRPhosts.Load(AddDirectory(ConfigDirectory, "svdrphosts.conf"), true) &&
|
|
|
|
Keys.Load(AddDirectory(ConfigDirectory, "remote.conf")) &&
|
|
|
|
KeyMacros.Load(AddDirectory(ConfigDirectory, "keymacros.conf"), true)
|
2003-08-16 09:18:52 +02:00
|
|
|
))
|
2004-10-23 15:17:03 +02:00
|
|
|
EXIT(2);
|
2000-02-19 13:36:48 +01:00
|
|
|
|
2003-10-24 12:53:12 +02:00
|
|
|
cFont::SetCode(I18nCharSets()[Setup.OSDLanguage]);
|
|
|
|
|
2005-09-25 11:00:57 +02:00
|
|
|
// Recordings:
|
|
|
|
|
|
|
|
Recordings.Update();
|
2005-12-18 10:41:26 +01:00
|
|
|
DeletedRecordings.Update();
|
2005-09-25 11:00:57 +02:00
|
|
|
|
2003-12-22 13:29:24 +01:00
|
|
|
// EPG data:
|
|
|
|
|
|
|
|
if (EpgDataFileName) {
|
2004-12-19 16:33:34 +01:00
|
|
|
const char *EpgDirectory = NULL;
|
|
|
|
if (DirectoryOk(EpgDataFileName)) {
|
|
|
|
EpgDirectory = EpgDataFileName;
|
|
|
|
EpgDataFileName = DEFAULTEPGDATAFILENAME;
|
|
|
|
}
|
2003-12-22 13:29:24 +01:00
|
|
|
else if (*EpgDataFileName != '/' && *EpgDataFileName != '.')
|
2004-12-19 16:33:34 +01:00
|
|
|
EpgDirectory = VideoDirectory;
|
2005-01-14 16:55:28 +01:00
|
|
|
if (EpgDirectory)
|
|
|
|
cSchedules::SetEpgDataFileName(AddDirectory(EpgDirectory, EpgDataFileName));
|
|
|
|
else
|
|
|
|
cSchedules::SetEpgDataFileName(EpgDataFileName);
|
2004-12-19 16:33:34 +01:00
|
|
|
cSchedules::Read();
|
2003-12-22 13:29:24 +01:00
|
|
|
}
|
|
|
|
|
2000-11-03 15:31:03 +01:00
|
|
|
// DVB interfaces:
|
|
|
|
|
2002-08-16 09:57:10 +02:00
|
|
|
cDvbDevice::Initialize();
|
2000-11-03 15:31:03 +01:00
|
|
|
|
2003-05-09 15:27:46 +02:00
|
|
|
// Initialize plugins:
|
2002-05-09 16:26:56 +02:00
|
|
|
|
2003-05-09 15:27:46 +02:00
|
|
|
if (!PluginManager.InitializePlugins())
|
2004-10-23 15:17:03 +02:00
|
|
|
EXIT(2);
|
2002-05-09 16:26:56 +02:00
|
|
|
|
2002-08-04 14:57:29 +02:00
|
|
|
// Primary device:
|
|
|
|
|
|
|
|
cDevice::SetPrimaryDevice(Setup.PrimaryDVB);
|
2003-04-12 12:20:07 +02:00
|
|
|
if (!cDevice::PrimaryDevice() || !cDevice::PrimaryDevice()->HasDecoder()) {
|
|
|
|
if (cDevice::PrimaryDevice() && !cDevice::PrimaryDevice()->HasDecoder())
|
|
|
|
isyslog("device %d has no MPEG decoder", cDevice::PrimaryDevice()->DeviceNumber() + 1);
|
2003-03-09 14:10:12 +01:00
|
|
|
for (int i = 0; i < cDevice::NumDevices(); i++) {
|
|
|
|
cDevice *d = cDevice::GetDevice(i);
|
|
|
|
if (d && d->HasDecoder()) {
|
|
|
|
isyslog("trying device number %d instead", i + 1);
|
2003-04-12 12:20:07 +02:00
|
|
|
if (cDevice::SetPrimaryDevice(i + 1)) {
|
2003-03-09 14:10:12 +01:00
|
|
|
Setup.PrimaryDVB = i + 1;
|
2003-04-12 12:20:07 +02:00
|
|
|
break;
|
|
|
|
}
|
2003-03-09 14:10:12 +01:00
|
|
|
}
|
|
|
|
}
|
2003-04-12 12:20:07 +02:00
|
|
|
if (!cDevice::PrimaryDevice()) {
|
|
|
|
const char *msg = "no primary device found - using first device!";
|
|
|
|
fprintf(stderr, "vdr: %s\n", msg);
|
|
|
|
esyslog("ERROR: %s", msg);
|
2003-09-05 13:15:51 +02:00
|
|
|
if (!cDevice::SetPrimaryDevice(1))
|
2004-10-23 15:17:03 +02:00
|
|
|
EXIT(2);
|
2003-04-12 12:20:07 +02:00
|
|
|
if (!cDevice::PrimaryDevice()) {
|
|
|
|
const char *msg = "no primary device found - giving up!";
|
|
|
|
fprintf(stderr, "vdr: %s\n", msg);
|
|
|
|
esyslog("ERROR: %s", msg);
|
2004-10-23 15:17:03 +02:00
|
|
|
EXIT(2);
|
2003-04-12 12:20:07 +02:00
|
|
|
}
|
|
|
|
}
|
2002-08-16 09:57:10 +02:00
|
|
|
}
|
2002-08-04 14:57:29 +02:00
|
|
|
|
2002-09-29 13:40:45 +02:00
|
|
|
// User interface:
|
|
|
|
|
|
|
|
Interface = new cInterface(SVDRPport);
|
|
|
|
|
2005-01-14 13:10:32 +01:00
|
|
|
// Default skins:
|
|
|
|
|
|
|
|
new cSkinClassic;
|
|
|
|
new cSkinSTTNG;
|
|
|
|
Skins.SetCurrent(Setup.OSDSkin);
|
|
|
|
cThemes::Load(Skins.Current()->Name(), Setup.OSDTheme, Skins.Current()->Theme());
|
2006-06-03 14:46:36 +02:00
|
|
|
CurrentSkin = Skins.Current();
|
2005-01-14 13:10:32 +01:00
|
|
|
|
2003-05-16 12:27:58 +02:00
|
|
|
// Start plugins:
|
|
|
|
|
|
|
|
if (!PluginManager.StartPlugins())
|
2004-10-23 15:17:03 +02:00
|
|
|
EXIT(2);
|
2003-05-16 12:27:58 +02:00
|
|
|
|
2005-01-14 13:10:32 +01:00
|
|
|
// Set skin and theme in case they're implemented by a plugin:
|
2004-05-16 10:35:36 +02:00
|
|
|
|
2006-06-03 14:46:36 +02:00
|
|
|
if (!CurrentSkin || CurrentSkin == Skins.Current() && strcmp(Skins.Current()->Name(), Setup.OSDSkin) != 0) {
|
|
|
|
Skins.SetCurrent(Setup.OSDSkin);
|
|
|
|
cThemes::Load(Skins.Current()->Name(), Setup.OSDTheme, Skins.Current()->Theme());
|
|
|
|
}
|
2004-05-16 10:35:36 +02:00
|
|
|
|
2002-09-29 13:40:45 +02:00
|
|
|
// Remote Controls:
|
2005-07-31 11:38:40 +02:00
|
|
|
if (RcuDevice)
|
|
|
|
new cRcuRemote(RcuDevice);
|
|
|
|
if (LircDevice)
|
|
|
|
new cLircRemote(LircDevice);
|
|
|
|
if (!DaemonMode && HasStdin && UseKbd)
|
2002-12-08 14:30:32 +01:00
|
|
|
new cKbdRemote;
|
2002-09-29 13:40:45 +02:00
|
|
|
Interface->LearnKeys();
|
|
|
|
|
2002-11-03 11:53:58 +01:00
|
|
|
// External audio:
|
|
|
|
|
|
|
|
if (AudioCommand)
|
|
|
|
new cExternalAudio(AudioCommand);
|
|
|
|
|
2002-05-09 16:26:56 +02:00
|
|
|
// Channel:
|
|
|
|
|
2005-08-21 08:56:49 +02:00
|
|
|
if (!cDevice::WaitForAllDevicesReady(DEVICEREADYTIMEOUT))
|
|
|
|
dsyslog("not all devices ready after %d seconds", DEVICEREADYTIMEOUT);
|
2006-04-09 13:26:56 +02:00
|
|
|
if (Setup.InitialChannel > 0)
|
|
|
|
Setup.CurrentChannel = Setup.InitialChannel;
|
|
|
|
if (Setup.InitialVolume >= 0)
|
|
|
|
Setup.CurrentVolume = Setup.InitialVolume;
|
2001-01-14 15:29:51 +01:00
|
|
|
Channels.SwitchTo(Setup.CurrentChannel);
|
2002-03-08 16:37:42 +01:00
|
|
|
if (MuteAudio)
|
2002-06-16 12:57:31 +02:00
|
|
|
cDevice::PrimaryDevice()->ToggleMute();
|
2002-03-08 16:37:42 +01:00
|
|
|
else
|
2002-06-16 12:57:31 +02:00
|
|
|
cDevice::PrimaryDevice()->SetVolume(Setup.CurrentVolume, true);
|
2000-02-19 13:36:48 +01:00
|
|
|
|
2000-07-23 15:01:31 +02:00
|
|
|
// Signal handlers:
|
|
|
|
|
2000-04-15 17:38:11 +02:00
|
|
|
if (signal(SIGHUP, SignalHandler) == SIG_IGN) signal(SIGHUP, SIG_IGN);
|
|
|
|
if (signal(SIGINT, SignalHandler) == SIG_IGN) signal(SIGINT, SIG_IGN);
|
|
|
|
if (signal(SIGTERM, SignalHandler) == SIG_IGN) signal(SIGTERM, SIG_IGN);
|
2000-09-15 13:58:36 +02:00
|
|
|
if (signal(SIGPIPE, SignalHandler) == SIG_IGN) signal(SIGPIPE, SIG_IGN);
|
2001-02-24 16:18:43 +01:00
|
|
|
if (WatchdogTimeout > 0)
|
|
|
|
if (signal(SIGALRM, Watchdog) == SIG_IGN) signal(SIGALRM, SIG_IGN);
|
2000-04-15 17:38:11 +02:00
|
|
|
|
2003-05-09 15:27:46 +02:00
|
|
|
// Watchdog:
|
|
|
|
|
|
|
|
if (WatchdogTimeout > 0) {
|
|
|
|
dsyslog("setting watchdog timer to %d seconds", WatchdogTimeout);
|
|
|
|
alarm(WatchdogTimeout); // Initial watchdog timer start
|
|
|
|
}
|
|
|
|
|
2000-07-23 15:01:31 +02:00
|
|
|
// Main program loop:
|
|
|
|
|
2006-01-06 12:53:28 +01:00
|
|
|
#define DELETE_MENU ((IsInfoMenu &= (Menu == NULL)), delete Menu, Menu = NULL)
|
|
|
|
|
2000-04-15 17:38:11 +02:00
|
|
|
while (!Interrupted) {
|
2001-06-02 10:47:40 +02:00
|
|
|
// Handle emergency exits:
|
|
|
|
if (cThread::EmergencyExit()) {
|
2002-05-13 16:35:49 +02:00
|
|
|
esyslog("emergency exit requested - shutting down");
|
2001-06-02 10:47:40 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-10-16 09:36:28 +02:00
|
|
|
#ifdef DEBUGRINGBUFFERS
|
|
|
|
cRingBufferLinear::PrintDebugRBL();
|
|
|
|
#endif
|
2002-06-23 11:23:34 +02:00
|
|
|
// Attach launched player control:
|
|
|
|
cControl::Attach();
|
2003-05-03 13:42:37 +02:00
|
|
|
// Make sure we have a visible programme in case device usage has changed:
|
2003-05-24 13:38:28 +02:00
|
|
|
if (!EITScanner.Active() && cDevice::PrimaryDevice()->HasDecoder() && !cDevice::PrimaryDevice()->HasProgramme()) {
|
2003-05-03 13:42:37 +02:00
|
|
|
static time_t lastTime = 0;
|
|
|
|
if (time(NULL) - lastTime > MINCHANNELWAIT) {
|
2004-02-13 13:15:12 +01:00
|
|
|
cChannel *Channel = Channels.GetByNumber(cDevice::CurrentChannel());
|
2004-12-17 14:55:49 +01:00
|
|
|
if (Channel && (Channel->Vpid() || Channel->Apid(0))) {
|
2004-02-13 13:15:12 +01:00
|
|
|
if (!Channels.SwitchTo(cDevice::CurrentChannel()) // try to switch to the original channel...
|
|
|
|
&& !(LastTimerChannel > 0 && Channels.SwitchTo(LastTimerChannel)) // ...or the one used by the last timer...
|
|
|
|
&& !cDevice::SwitchChannel(1) // ...or the next higher available one...
|
|
|
|
&& !cDevice::SwitchChannel(-1)) // ...or the next lower available one
|
2006-01-08 11:44:37 +01:00
|
|
|
;
|
|
|
|
}
|
2004-02-13 13:15:12 +01:00
|
|
|
lastTime = time(NULL); // don't do this too often
|
2003-05-24 15:34:30 +02:00
|
|
|
LastTimerChannel = -1;
|
2003-05-03 13:42:37 +02:00
|
|
|
}
|
|
|
|
}
|
2001-02-24 16:18:43 +01:00
|
|
|
// Restart the Watchdog timer:
|
|
|
|
if (WatchdogTimeout > 0) {
|
|
|
|
int LatencyTime = WatchdogTimeout - alarm(WatchdogTimeout);
|
|
|
|
if (LatencyTime > MaxLatencyTime) {
|
|
|
|
MaxLatencyTime = LatencyTime;
|
2002-05-13 16:35:49 +02:00
|
|
|
dsyslog("max. latency time %d seconds", MaxLatencyTime);
|
2001-02-24 16:18:43 +01:00
|
|
|
}
|
|
|
|
}
|
2004-10-31 10:22:32 +01:00
|
|
|
// Handle channel and timer modifications:
|
|
|
|
if (!Channels.BeingEdited() && !Timers.BeingEdited()) {
|
2004-10-17 11:50:21 +02:00
|
|
|
int modified = Channels.Modified();
|
|
|
|
static time_t ChannelSaveTimeout = 0;
|
2006-01-15 13:44:55 +01:00
|
|
|
static int TimerState = 0;
|
2004-10-31 10:22:32 +01:00
|
|
|
// Channels and timers need to be stored in a consistent manner,
|
|
|
|
// therefore if one of them is changed, we save both.
|
2006-01-15 13:44:55 +01:00
|
|
|
if (modified == CHANNELSMOD_USER || Timers.Modified(TimerState))
|
2004-10-17 11:50:21 +02:00
|
|
|
ChannelSaveTimeout = 1; // triggers an immediate save
|
|
|
|
else if (modified && !ChannelSaveTimeout)
|
|
|
|
ChannelSaveTimeout = time(NULL) + CHANNELSAVEDELTA;
|
|
|
|
bool timeout = ChannelSaveTimeout == 1 || ChannelSaveTimeout && time(NULL) > ChannelSaveTimeout && !cRecordControls::Active();
|
|
|
|
if ((modified || timeout) && Channels.Lock(false, 100)) {
|
|
|
|
if (timeout) {
|
|
|
|
Channels.Save();
|
|
|
|
Timers.Save();
|
|
|
|
ChannelSaveTimeout = 0;
|
|
|
|
}
|
2004-01-04 12:30:00 +01:00
|
|
|
for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
|
2004-02-08 11:23:29 +01:00
|
|
|
if (Channel->Modification(CHANNELMOD_RETUNE)) {
|
2004-01-04 12:30:00 +01:00
|
|
|
cRecordControls::ChannelDataModified(Channel);
|
|
|
|
if (Channel->Number() == cDevice::CurrentChannel()) {
|
2006-02-04 14:58:24 +01:00
|
|
|
if (!cDevice::PrimaryDevice()->Replaying() || cDevice::PrimaryDevice()->Transferring()) {
|
2004-02-15 14:31:53 +01:00
|
|
|
if (cDevice::ActualDevice()->ProvidesTransponder(Channel)) { // avoids retune on devices that don't really access the transponder
|
|
|
|
isyslog("retuning due to modification of channel %d", Channel->Number());
|
|
|
|
Channels.SwitchTo(Channel->Number());
|
|
|
|
}
|
2004-01-04 12:30:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Channels.Unlock();
|
|
|
|
}
|
|
|
|
}
|
2000-04-22 13:51:48 +02:00
|
|
|
// Channel display:
|
2002-06-16 12:57:31 +02:00
|
|
|
if (!EITScanner.Active() && cDevice::CurrentChannel() != LastChannel) {
|
2000-09-09 14:57:43 +02:00
|
|
|
if (!Menu)
|
2006-01-05 13:54:04 +01:00
|
|
|
Menu = new cDisplayChannel(cDevice::CurrentChannel(), LastChannel >= 0);
|
2002-06-16 12:57:31 +02:00
|
|
|
LastChannel = cDevice::CurrentChannel();
|
2003-06-19 10:06:07 +02:00
|
|
|
LastChannelChanged = time(NULL);
|
|
|
|
}
|
2006-01-22 16:10:35 +01:00
|
|
|
if (time(NULL) - LastChannelChanged >= Setup.ZapTimeout && LastChannel != PreviousChannel[PreviousChannelIndex])
|
2003-08-16 15:27:26 +02:00
|
|
|
PreviousChannel[PreviousChannelIndex ^= 1] = LastChannel;
|
2000-05-01 16:29:46 +02:00
|
|
|
// Timers and Recordings:
|
2003-02-09 13:14:44 +01:00
|
|
|
if (!Timers.BeingEdited()) {
|
2004-03-14 14:28:08 +01:00
|
|
|
// Assign events to timers:
|
2004-10-24 15:01:50 +02:00
|
|
|
Timers.SetEvents();
|
|
|
|
// Must do all following calls with the exact same time!
|
|
|
|
time_t Now = time(NULL);
|
2004-02-29 14:21:22 +01:00
|
|
|
// Process ongoing recordings:
|
2001-09-01 15:04:14 +02:00
|
|
|
cRecordControls::Process(Now);
|
2004-02-29 14:21:22 +01:00
|
|
|
// Start new recordings:
|
2001-09-01 15:04:14 +02:00
|
|
|
cTimer *Timer = Timers.GetMatch(Now);
|
2000-04-30 10:22:13 +02:00
|
|
|
if (Timer) {
|
2001-08-11 15:48:54 +02:00
|
|
|
if (!cRecordControls::Start(Timer))
|
|
|
|
Timer->SetPending(true);
|
2003-05-24 15:34:30 +02:00
|
|
|
else
|
|
|
|
LastTimerChannel = Timer->Channel()->Number();
|
2000-04-15 17:38:11 +02:00
|
|
|
}
|
2006-06-04 09:10:59 +02:00
|
|
|
// Make sure timers "see" their channel early enough:
|
|
|
|
static time_t LastTimerCheck = 0;
|
|
|
|
if (Now - LastTimerCheck > TIMERCHECKDELTA) { // don't do this too often
|
|
|
|
InhibitEpgScan = false;
|
2006-04-09 09:12:47 +02:00
|
|
|
static time_t DeviceUsed[MAXDEVICES] = { 0 };
|
|
|
|
for (cTimer *Timer = Timers.First(); Timer; Timer = Timers.Next(Timer)) {
|
2006-05-07 09:13:36 +02:00
|
|
|
bool InVpsMargin = false;
|
|
|
|
bool NeedsTransponder = false;
|
2006-06-04 09:10:59 +02:00
|
|
|
if (Timer->HasFlags(tfActive) && !Timer->Recording()) {
|
|
|
|
if (Timer->HasFlags(tfVps)) {
|
2006-06-16 09:22:20 +02:00
|
|
|
if (Timer->Matches(Now, true, Setup.VpsMargin)) {
|
2006-06-04 09:10:59 +02:00
|
|
|
InVpsMargin = true;
|
2006-06-16 09:22:20 +02:00
|
|
|
Timer->SetInVpsMargin(InVpsMargin);
|
|
|
|
}
|
|
|
|
else if (Timer->Event()) {
|
|
|
|
InVpsMargin = Timer->Event()->StartTime() <= Now && Timer->Event()->RunningStatus() == SI::RunningStatusUndefined;
|
2006-06-04 09:10:59 +02:00
|
|
|
NeedsTransponder = Timer->Event()->StartTime() - Now < VPSLOOKAHEADTIME * 3600 && !Timer->Event()->SeenWithin(VPSUPTODATETIME);
|
2006-06-16 09:22:20 +02:00
|
|
|
}
|
2006-06-04 09:10:59 +02:00
|
|
|
else {
|
|
|
|
cSchedulesLock SchedulesLock;
|
|
|
|
const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
|
|
|
|
if (Schedules) {
|
|
|
|
const cSchedule *Schedule = Schedules->GetSchedule(Timer->Channel());
|
2006-06-18 08:50:04 +02:00
|
|
|
InVpsMargin = !Schedule; // we must make sure we have the schedule
|
2006-06-04 09:10:59 +02:00
|
|
|
NeedsTransponder = Schedule && !Schedule->PresentSeenWithin(VPSUPTODATETIME);
|
|
|
|
}
|
2006-05-07 09:13:36 +02:00
|
|
|
}
|
2006-06-04 09:10:59 +02:00
|
|
|
InhibitEpgScan |= InVpsMargin | NeedsTransponder;
|
2006-05-07 09:13:36 +02:00
|
|
|
}
|
2006-06-04 09:10:59 +02:00
|
|
|
else
|
|
|
|
NeedsTransponder = Timer->Matches(Now, true, TIMERLOOKAHEADTIME);
|
2006-05-07 09:13:36 +02:00
|
|
|
}
|
|
|
|
if (NeedsTransponder || InVpsMargin) {
|
2006-04-09 09:12:47 +02:00
|
|
|
// Find a device that provides the required transponder:
|
|
|
|
cDevice *Device = NULL;
|
2006-06-16 09:22:20 +02:00
|
|
|
bool DeviceAvailable = false;
|
2006-04-09 09:12:47 +02:00
|
|
|
for (int i = 0; i < cDevice::NumDevices(); i++) {
|
|
|
|
cDevice *d = cDevice::GetDevice(i);
|
|
|
|
if (d && d->ProvidesTransponder(Timer->Channel())) {
|
|
|
|
if (d->IsTunedToTransponder(Timer->Channel())) {
|
|
|
|
// if any device is tuned to the transponder, we're done
|
|
|
|
Device = d;
|
|
|
|
break;
|
|
|
|
}
|
2006-06-16 09:22:20 +02:00
|
|
|
bool timeout = Now - DeviceUsed[d->DeviceNumber()] > TIMERDEVICETIMEOUT; // only check other devices if they have been left alone for a while
|
|
|
|
if (d->MaySwitchTransponder()) {
|
|
|
|
DeviceAvailable = true; // avoids using the actual device below
|
|
|
|
if (timeout)
|
|
|
|
Device = d; // only check other devices if they have been left alone for a while
|
2006-04-09 09:12:47 +02:00
|
|
|
}
|
2006-06-16 09:22:20 +02:00
|
|
|
else if (timeout && !Device && InVpsMargin && !d->Receiving() && d->ProvidesTransponderExclusively(Timer->Channel()))
|
|
|
|
Device = d; // use this one only if no other with less impact can be found
|
2006-04-09 09:12:47 +02:00
|
|
|
}
|
|
|
|
}
|
2006-06-16 09:22:20 +02:00
|
|
|
if (!Device && InVpsMargin && !DeviceAvailable) {
|
2006-04-09 09:12:47 +02:00
|
|
|
cDevice *d = cDevice::ActualDevice();
|
2006-06-04 09:10:59 +02:00
|
|
|
if (!d->Receiving() && d->ProvidesTransponder(Timer->Channel()) && Now - DeviceUsed[d->DeviceNumber()] > TIMERDEVICETIMEOUT)
|
2006-04-09 09:12:47 +02:00
|
|
|
Device = d; // use the actual device as a last resort
|
|
|
|
}
|
|
|
|
// Switch the device to the transponder:
|
|
|
|
if (Device) {
|
|
|
|
if (!Device->IsTunedToTransponder(Timer->Channel())) {
|
2006-04-28 13:29:22 +02:00
|
|
|
if (Device == cDevice::ActualDevice() && !Device->IsPrimaryDevice())
|
|
|
|
cDevice::PrimaryDevice()->StopReplay(); // stop transfer mode
|
2006-04-09 09:12:47 +02:00
|
|
|
dsyslog("switching device %d to channel %d", Device->DeviceNumber() + 1, Timer->Channel()->Number());
|
|
|
|
Device->SwitchChannel(Timer->Channel(), false);
|
|
|
|
DeviceUsed[Device->DeviceNumber()] = Now;
|
|
|
|
}
|
|
|
|
if (cDevice::PrimaryDevice()->HasDecoder() && !cDevice::PrimaryDevice()->HasProgramme()) {
|
|
|
|
// the previous SwitchChannel() has switched away the current live channel
|
|
|
|
Channels.SwitchTo(Timer->Channel()->Number()); // avoids toggling between old channel and black screen
|
|
|
|
Skins.Message(mtInfo, tr("Upcoming VPS recording!"));
|
|
|
|
}
|
|
|
|
}
|
2004-02-29 14:21:22 +01:00
|
|
|
}
|
|
|
|
}
|
2006-06-04 09:10:59 +02:00
|
|
|
LastTimerCheck = time(NULL);
|
2006-04-09 09:12:47 +02:00
|
|
|
}
|
2006-04-01 13:27:14 +02:00
|
|
|
// Delete expired timers:
|
|
|
|
Timers.DeleteExpired();
|
2000-04-15 17:38:11 +02:00
|
|
|
}
|
2005-12-18 10:41:26 +01:00
|
|
|
if (!Menu && Recordings.NeedsUpdate()) {
|
2005-09-25 11:00:57 +02:00
|
|
|
Recordings.Update();
|
2005-12-18 10:41:26 +01:00
|
|
|
DeletedRecordings.Update();
|
|
|
|
}
|
2003-01-06 14:44:27 +01:00
|
|
|
// CAM control:
|
2005-08-20 11:24:42 +02:00
|
|
|
if (!Menu && !cOsd::IsOpen()) {
|
2003-01-06 14:44:27 +01:00
|
|
|
Menu = CamControl();
|
2005-08-20 11:24:42 +02:00
|
|
|
if (Menu)
|
|
|
|
LastCamMenu = 0;
|
|
|
|
else if (!LastCamMenu)
|
|
|
|
LastCamMenu = time(NULL);
|
|
|
|
}
|
2005-11-27 15:57:03 +01:00
|
|
|
// Queued messages:
|
|
|
|
if (!Skins.IsOpen())
|
|
|
|
Skins.ProcessQueuedMessages();
|
2000-04-16 15:50:21 +02:00
|
|
|
// User Input:
|
2002-06-23 11:23:34 +02:00
|
|
|
cOsdObject *Interact = Menu ? Menu : cControl::Control();
|
2005-08-20 11:24:42 +02:00
|
|
|
eKeys key = Interface->GetKey((!Interact || !Interact->NeedsFastResponse()) && time(NULL) - LastCamMenu > LASTCAMMENUTIMEOUT);
|
2001-09-01 09:04:37 +02:00
|
|
|
if (NORMALKEY(key) != kNone) {
|
2000-11-18 13:57:32 +01:00
|
|
|
EITScanner.Activity();
|
2001-09-01 09:04:37 +02:00
|
|
|
LastActivity = time(NULL);
|
|
|
|
}
|
2001-09-23 14:36:38 +02:00
|
|
|
// Keys that must work independent of any interactive mode:
|
|
|
|
switch (key) {
|
2002-06-23 09:44:00 +02:00
|
|
|
// Menu control:
|
2006-01-13 15:45:02 +01:00
|
|
|
case kMenu: {
|
2003-05-02 10:59:07 +02:00
|
|
|
key = kNone; // nobody else needs to see this key
|
2006-01-13 15:45:02 +01:00
|
|
|
bool WasOpen = Interact != NULL;
|
|
|
|
bool WasMenu = Interact && Interact->IsMenu();
|
2006-01-04 13:22:53 +01:00
|
|
|
if (Menu)
|
2006-01-06 12:53:28 +01:00
|
|
|
DELETE_MENU;
|
2006-02-04 13:03:03 +01:00
|
|
|
else if (cControl::Control()) {
|
|
|
|
if (cOsd::IsOpen())
|
|
|
|
cControl::Control()->Hide();
|
|
|
|
else
|
|
|
|
WasOpen = false;
|
|
|
|
}
|
2006-01-13 15:45:02 +01:00
|
|
|
if (!WasOpen || !WasMenu && !Setup.MenuButtonCloses)
|
2006-01-04 13:22:53 +01:00
|
|
|
Menu = new cMenuMain;
|
2006-01-13 15:45:02 +01:00
|
|
|
}
|
2002-06-23 09:44:00 +02:00
|
|
|
break;
|
2006-01-06 12:53:28 +01:00
|
|
|
// Info:
|
|
|
|
case kInfo: {
|
|
|
|
bool WasInfoMenu = IsInfoMenu;
|
|
|
|
DELETE_MENU;
|
|
|
|
if (!WasInfoMenu) {
|
|
|
|
IsInfoMenu = true;
|
|
|
|
if (cControl::Control()) {
|
|
|
|
cControl::Control()->Hide();
|
|
|
|
Menu = cControl::Control()->GetInfo();
|
|
|
|
if (Menu)
|
|
|
|
Menu->Show();
|
|
|
|
else
|
|
|
|
IsInfoMenu = false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
cRemote::Put(kOk, true);
|
|
|
|
cRemote::Put(kSchedule, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2006-01-22 14:27:53 +01:00
|
|
|
// Direct main menu functions:
|
2005-09-03 11:33:43 +02:00
|
|
|
#define DirectMainFunction(function)\
|
2006-01-06 12:53:28 +01:00
|
|
|
DELETE_MENU;\
|
2002-10-27 14:32:06 +01:00
|
|
|
if (cControl::Control())\
|
|
|
|
cControl::Control()->Hide();\
|
2005-12-24 15:53:53 +01:00
|
|
|
Menu = new cMenuMain(function);\
|
2003-05-02 10:59:07 +02:00
|
|
|
key = kNone; // nobody else needs to see this key
|
2002-10-27 14:32:06 +01:00
|
|
|
case kSchedule: DirectMainFunction(osSchedule); break;
|
|
|
|
case kChannels: DirectMainFunction(osChannels); break;
|
|
|
|
case kTimers: DirectMainFunction(osTimers); break;
|
|
|
|
case kRecordings: DirectMainFunction(osRecordings); break;
|
|
|
|
case kSetup: DirectMainFunction(osSetup); break;
|
|
|
|
case kCommands: DirectMainFunction(osCommands); break;
|
2003-05-02 10:59:07 +02:00
|
|
|
case kUser1 ... kUser9: cRemote::PutMacro(key); key = kNone; break;
|
2005-09-03 11:33:43 +02:00
|
|
|
case k_Plugin: {
|
2006-01-06 12:53:28 +01:00
|
|
|
DELETE_MENU;
|
2005-09-03 11:33:43 +02:00
|
|
|
if (cControl::Control())
|
|
|
|
cControl::Control()->Hide();
|
|
|
|
cPlugin *plugin = cPluginManager::GetPlugin(cRemote::GetPlugin());
|
|
|
|
if (plugin) {
|
2006-01-04 13:22:53 +01:00
|
|
|
Menu = plugin->MainMenuAction();
|
2006-01-05 15:35:06 +01:00
|
|
|
if (Menu)
|
2005-09-03 11:33:43 +02:00
|
|
|
Menu->Show();
|
|
|
|
}
|
2005-09-03 12:36:51 +02:00
|
|
|
else
|
|
|
|
esyslog("ERROR: unknown plugin '%s'", cRemote::GetPlugin());
|
2005-09-03 11:33:43 +02:00
|
|
|
key = kNone; // nobody else needs to see these keys
|
|
|
|
}
|
|
|
|
break;
|
2002-10-27 14:32:06 +01:00
|
|
|
// Channel up/down:
|
|
|
|
case kChanUp|k_Repeat:
|
|
|
|
case kChanUp:
|
|
|
|
case kChanDn|k_Repeat:
|
|
|
|
case kChanDn:
|
2006-01-22 14:27:53 +01:00
|
|
|
if (!Interact)
|
|
|
|
Menu = new cDisplayChannel(NORMALKEY(key));
|
|
|
|
else if (cDisplayChannel::IsOpen()) {
|
|
|
|
Interact->ProcessKey(key);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cDevice::SwitchChannel(NORMALKEY(key) == kChanUp ? 1 : -1);
|
|
|
|
key = kNone; // nobody else needs to see these keys
|
2002-10-27 14:32:06 +01:00
|
|
|
break;
|
2005-01-02 15:11:44 +01:00
|
|
|
// Volume control:
|
2001-09-23 14:36:38 +02:00
|
|
|
case kVolUp|k_Repeat:
|
|
|
|
case kVolUp:
|
|
|
|
case kVolDn|k_Repeat:
|
|
|
|
case kVolDn:
|
|
|
|
case kMute:
|
2002-03-09 17:11:49 +01:00
|
|
|
if (key == kMute) {
|
2003-03-30 10:53:22 +02:00
|
|
|
if (!cDevice::PrimaryDevice()->ToggleMute() && !Menu) {
|
|
|
|
key = kNone; // nobody else needs to see these keys
|
2002-03-09 17:11:49 +01:00
|
|
|
break; // no need to display "mute off"
|
2003-03-30 10:53:22 +02:00
|
|
|
}
|
2002-03-09 17:11:49 +01:00
|
|
|
}
|
|
|
|
else
|
2002-06-16 12:57:31 +02:00
|
|
|
cDevice::PrimaryDevice()->SetVolume(NORMALKEY(key) == kVolDn ? -VOLUMEDELTA : VOLUMEDELTA);
|
2004-05-16 10:35:36 +02:00
|
|
|
if (!Menu && !cOsd::IsOpen())
|
2006-01-04 13:22:53 +01:00
|
|
|
Menu = cDisplayVolume::Create();
|
2002-03-09 17:11:49 +01:00
|
|
|
cDisplayVolume::Process(key);
|
2003-03-30 10:53:22 +02:00
|
|
|
key = kNone; // nobody else needs to see these keys
|
2001-09-23 14:36:38 +02:00
|
|
|
break;
|
2005-01-02 15:11:44 +01:00
|
|
|
// Audio track control:
|
|
|
|
case kAudio:
|
2005-01-06 14:36:40 +01:00
|
|
|
if (cControl::Control())
|
|
|
|
cControl::Control()->Hide();
|
2006-01-04 13:22:53 +01:00
|
|
|
if (!cDisplayTracks::IsOpen()) {
|
2006-01-06 12:53:28 +01:00
|
|
|
DELETE_MENU;
|
2006-01-04 13:22:53 +01:00
|
|
|
Menu = cDisplayTracks::Create();
|
2005-01-06 14:36:40 +01:00
|
|
|
}
|
2005-01-02 15:11:44 +01:00
|
|
|
else
|
|
|
|
cDisplayTracks::Process(key);
|
|
|
|
key = kNone;
|
|
|
|
break;
|
2003-04-21 14:57:13 +02:00
|
|
|
// Pausing live video:
|
|
|
|
case kPause:
|
|
|
|
if (!cControl::Control()) {
|
2006-01-06 12:53:28 +01:00
|
|
|
DELETE_MENU;
|
2003-04-21 14:57:13 +02:00
|
|
|
if (!cRecordControls::PauseLiveVideo())
|
2004-05-16 10:35:36 +02:00
|
|
|
Skins.Message(mtError, tr("No free DVB device to record!"));
|
2003-04-21 14:57:13 +02:00
|
|
|
key = kNone; // nobody else needs to see this key
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
// Instant recording:
|
|
|
|
case kRecord:
|
|
|
|
if (!cControl::Control()) {
|
|
|
|
if (cRecordControls::Start())
|
2006-01-15 16:04:05 +01:00
|
|
|
Skins.Message(mtInfo, tr("Recording started"));
|
2003-04-21 14:57:13 +02:00
|
|
|
key = kNone; // nobody else needs to see this key
|
|
|
|
}
|
|
|
|
break;
|
2001-09-23 14:36:38 +02:00
|
|
|
// Power off:
|
2006-05-05 13:45:42 +02:00
|
|
|
case kPower: {
|
2006-04-29 09:24:07 +02:00
|
|
|
isyslog("Power button pressed");
|
|
|
|
DELETE_MENU;
|
|
|
|
if (!Shutdown) {
|
|
|
|
Skins.Message(mtError, tr("Can't shutdown - option '-s' not given!"));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
LastActivity = 1; // not 0, see below!
|
|
|
|
UserShutdown = true;
|
|
|
|
if (cRecordControls::Active()) {
|
|
|
|
if (!Interface->Confirm(tr("Recording - shut down anyway?")))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (cPluginManager::Active(tr("shut down anyway?")))
|
|
|
|
break;
|
2006-08-05 10:55:52 +02:00
|
|
|
if (!cRecordControls::Active()) {
|
|
|
|
cTimer *timer = Timers.GetNextActiveTimer();
|
|
|
|
time_t Next = timer ? timer->StartTime() : 0;
|
|
|
|
time_t Delta = timer ? Next - time(NULL) : 0;
|
|
|
|
if (Next && Delta <= Setup.MinEventTimeout * 60) {
|
|
|
|
char *buf;
|
|
|
|
asprintf(&buf, tr("Recording in %ld minutes, shut down anyway?"), Delta / 60);
|
|
|
|
bool confirm = Interface->Confirm(buf);
|
|
|
|
free(buf);
|
|
|
|
if (!confirm)
|
|
|
|
break;
|
|
|
|
}
|
2006-05-05 13:45:42 +02:00
|
|
|
}
|
2006-04-29 09:24:07 +02:00
|
|
|
ForceShutdown = true;
|
|
|
|
break;
|
2006-05-05 13:45:42 +02:00
|
|
|
}
|
2002-10-27 14:32:06 +01:00
|
|
|
default: break;
|
2001-09-23 14:36:38 +02:00
|
|
|
}
|
2002-10-27 14:32:06 +01:00
|
|
|
Interact = Menu ? Menu : cControl::Control(); // might have been closed in the mean time
|
|
|
|
if (Interact) {
|
|
|
|
eOSState state = Interact->ProcessKey(key);
|
2005-09-04 08:57:15 +02:00
|
|
|
if (state == osUnknown && Interact != cControl::Control()) {
|
2005-11-04 13:52:31 +01:00
|
|
|
if (ISMODELESSKEY(key) && cControl::Control()) {
|
2005-09-03 11:51:54 +02:00
|
|
|
state = cControl::Control()->ProcessKey(key);
|
2005-11-04 13:52:31 +01:00
|
|
|
if (state == osEnd) {
|
|
|
|
// let's not close a menu when replay ends:
|
|
|
|
cControl::Shutdown();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2005-09-03 11:51:54 +02:00
|
|
|
else if (time(NULL) - LastActivity > MENUTIMEOUT)
|
|
|
|
state = osEnd;
|
|
|
|
}
|
2006-05-14 11:22:13 +02:00
|
|
|
// TODO make the CAM menu stay open in case of automatic updates and have it return osContinue; then the following two lines can be removed again
|
|
|
|
else if (state == osEnd && LastActivity > 1)
|
|
|
|
LastActivity = time(NULL);
|
2002-10-27 14:32:06 +01:00
|
|
|
switch (state) {
|
2006-01-06 12:53:28 +01:00
|
|
|
case osPause: DELETE_MENU;
|
2003-04-21 14:57:13 +02:00
|
|
|
cControl::Shutdown(); // just in case
|
|
|
|
if (!cRecordControls::PauseLiveVideo())
|
2004-05-16 10:35:36 +02:00
|
|
|
Skins.Message(mtError, tr("No free DVB device to record!"));
|
2003-04-21 14:57:13 +02:00
|
|
|
break;
|
2006-01-06 12:53:28 +01:00
|
|
|
case osRecord: DELETE_MENU;
|
2002-10-27 14:32:06 +01:00
|
|
|
if (cRecordControls::Start())
|
2006-01-04 15:21:55 +01:00
|
|
|
Skins.Message(mtInfo, tr("Recording started"));
|
2002-10-27 14:32:06 +01:00
|
|
|
break;
|
|
|
|
case osRecordings:
|
2006-01-06 12:53:28 +01:00
|
|
|
DELETE_MENU;
|
2002-10-27 14:32:06 +01:00
|
|
|
cControl::Shutdown();
|
2005-12-24 15:53:53 +01:00
|
|
|
Menu = new cMenuMain(osRecordings);
|
2002-10-27 14:32:06 +01:00
|
|
|
break;
|
2006-01-06 12:53:28 +01:00
|
|
|
case osReplay: DELETE_MENU;
|
2002-10-27 14:32:06 +01:00
|
|
|
cControl::Shutdown();
|
|
|
|
cControl::Launch(new cReplayControl);
|
|
|
|
break;
|
|
|
|
case osStopReplay:
|
2006-01-06 12:53:28 +01:00
|
|
|
DELETE_MENU;
|
2002-10-27 14:32:06 +01:00
|
|
|
cControl::Shutdown();
|
|
|
|
break;
|
|
|
|
case osSwitchDvb:
|
2006-01-06 12:53:28 +01:00
|
|
|
DELETE_MENU;
|
2002-10-27 14:32:06 +01:00
|
|
|
cControl::Shutdown();
|
2004-05-16 10:35:36 +02:00
|
|
|
Skins.Message(mtInfo, tr("Switching primary DVB..."));
|
2002-10-27 14:32:06 +01:00
|
|
|
cDevice::SetPrimaryDevice(Setup.PrimaryDVB);
|
|
|
|
break;
|
2006-01-06 12:53:28 +01:00
|
|
|
case osPlugin: DELETE_MENU;
|
2006-01-04 13:22:53 +01:00
|
|
|
Menu = cMenuMain::PluginOsdObject();
|
2002-11-24 10:45:39 +01:00
|
|
|
if (Menu)
|
|
|
|
Menu->Show();
|
|
|
|
break;
|
2002-10-27 14:32:06 +01:00
|
|
|
case osBack:
|
|
|
|
case osEnd: if (Interact == Menu)
|
2006-01-06 12:53:28 +01:00
|
|
|
DELETE_MENU;
|
2002-10-27 14:32:06 +01:00
|
|
|
else
|
|
|
|
cControl::Shutdown();
|
|
|
|
break;
|
|
|
|
default: ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Key functions in "normal" viewing mode:
|
2006-01-20 13:19:35 +01:00
|
|
|
if (key != kNone && KeyMacros.Get(key)) {
|
2006-01-14 10:54:13 +01:00
|
|
|
cRemote::PutMacro(key);
|
|
|
|
key = kNone;
|
|
|
|
}
|
2002-10-27 14:32:06 +01:00
|
|
|
switch (key) {
|
|
|
|
// Toggle channels:
|
2006-04-15 13:56:03 +02:00
|
|
|
case kChanPrev:
|
2002-10-27 14:32:06 +01:00
|
|
|
case k0: {
|
2003-08-16 15:27:26 +02:00
|
|
|
if (PreviousChannel[PreviousChannelIndex ^ 1] == LastChannel || LastChannel != PreviousChannel[0] && LastChannel != PreviousChannel[1])
|
|
|
|
PreviousChannelIndex ^= 1;
|
|
|
|
Channels.SwitchTo(PreviousChannel[PreviousChannelIndex ^= 1]);
|
2002-10-27 14:32:06 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Direct Channel Select:
|
|
|
|
case k1 ... k9:
|
2006-04-15 13:46:55 +02:00
|
|
|
// Left/Right rotates through channel groups:
|
2002-10-27 14:32:06 +01:00
|
|
|
case kLeft|k_Repeat:
|
|
|
|
case kLeft:
|
|
|
|
case kRight|k_Repeat:
|
|
|
|
case kRight:
|
2006-04-15 13:46:55 +02:00
|
|
|
// Previous/Next rotates through channel groups:
|
|
|
|
case kPrev|k_Repeat:
|
|
|
|
case kPrev:
|
|
|
|
case kNext|k_Repeat:
|
|
|
|
case kNext:
|
2002-10-27 14:32:06 +01:00
|
|
|
// Up/Down Channel Select:
|
|
|
|
case kUp|k_Repeat:
|
|
|
|
case kUp:
|
|
|
|
case kDown|k_Repeat:
|
|
|
|
case kDown:
|
2006-01-22 14:27:53 +01:00
|
|
|
Menu = new cDisplayChannel(NORMALKEY(key));
|
2002-10-27 14:32:06 +01:00
|
|
|
break;
|
|
|
|
// Viewing Control:
|
|
|
|
case kOk: LastChannel = -1; break; // forces channel display
|
2005-05-26 10:20:31 +02:00
|
|
|
// Instant resume of the last viewed recording:
|
|
|
|
case kPlay:
|
|
|
|
if (cReplayControl::LastReplayed()) {
|
|
|
|
cControl::Shutdown();
|
|
|
|
cControl::Launch(new cReplayControl);
|
|
|
|
}
|
|
|
|
break;
|
2002-10-27 14:32:06 +01:00
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
}
|
2000-12-28 12:57:16 +01:00
|
|
|
if (!Menu) {
|
2006-06-04 09:10:59 +02:00
|
|
|
if (!InhibitEpgScan)
|
2004-02-29 14:21:22 +01:00
|
|
|
EITScanner.Process();
|
2002-06-22 10:11:59 +02:00
|
|
|
if (!cCutter::Active() && cCutter::Ended()) {
|
|
|
|
if (cCutter::Error())
|
2004-05-16 10:35:36 +02:00
|
|
|
Skins.Message(mtError, tr("Editing process failed!"));
|
2002-01-26 13:42:15 +01:00
|
|
|
else
|
2004-05-16 10:35:36 +02:00
|
|
|
Skins.Message(mtInfo, tr("Editing process finished"));
|
2002-01-26 13:42:15 +01:00
|
|
|
}
|
2000-12-28 12:57:16 +01:00
|
|
|
}
|
2006-04-15 11:18:36 +02:00
|
|
|
if (!Interact && ((!cRecordControls::Active() && !cCutter::Active() && !cPluginManager::Active() && (!Interface->HasSVDRPConnection() || UserShutdown)) || ForceShutdown)) {
|
2001-09-01 09:04:37 +02:00
|
|
|
time_t Now = time(NULL);
|
|
|
|
if (Now - LastActivity > ACTIVITYTIMEOUT) {
|
|
|
|
// Shutdown:
|
2001-11-03 11:49:39 +01:00
|
|
|
if (Shutdown && (Setup.MinUserInactivity || LastActivity == 1) && Now - LastActivity > Setup.MinUserInactivity * 60) {
|
2001-09-01 09:04:37 +02:00
|
|
|
cTimer *timer = Timers.GetNextActiveTimer();
|
2001-09-01 11:44:08 +02:00
|
|
|
time_t Next = timer ? timer->StartTime() : 0;
|
|
|
|
time_t Delta = timer ? Next - Now : 0;
|
2001-09-07 15:37:26 +02:00
|
|
|
if (!LastActivity) {
|
|
|
|
if (!timer || Delta > MANUALSTART) {
|
2001-09-01 11:44:08 +02:00
|
|
|
// Apparently the user started VDR manually
|
2002-05-13 16:35:49 +02:00
|
|
|
dsyslog("assuming manual start of VDR");
|
2001-09-01 11:44:08 +02:00
|
|
|
LastActivity = Now;
|
2001-09-07 15:37:26 +02:00
|
|
|
continue; // don't run into the actual shutdown procedure below
|
2001-09-01 11:44:08 +02:00
|
|
|
}
|
2001-09-07 15:37:26 +02:00
|
|
|
else
|
|
|
|
LastActivity = 1;
|
|
|
|
}
|
2006-08-05 10:42:23 +02:00
|
|
|
if (timer && Delta < Setup.MinEventTimeout * 60 && ForceShutdown) {
|
|
|
|
Delta = Setup.MinEventTimeout * 60;
|
|
|
|
Next = Now + Delta;
|
|
|
|
timer = NULL;
|
|
|
|
dsyslog("reboot at %s", *TimeToString(Next));
|
|
|
|
}
|
2001-09-08 13:02:05 +02:00
|
|
|
if (!Next || Delta > Setup.MinEventTimeout * 60 || ForceShutdown) {
|
|
|
|
ForceShutdown = false;
|
2001-09-07 15:37:26 +02:00
|
|
|
if (timer)
|
2004-12-26 12:45:22 +01:00
|
|
|
dsyslog("next timer event at %s", *TimeToString(Next));
|
2001-09-01 11:44:08 +02:00
|
|
|
if (WatchdogTimeout > 0)
|
|
|
|
signal(SIGALRM, SIG_IGN);
|
2001-11-03 12:23:45 +01:00
|
|
|
if (Interface->Confirm(tr("Press any key to cancel shutdown"), UserShutdown ? 5 : SHUTDOWNWAIT, true)) {
|
2005-05-26 10:33:17 +02:00
|
|
|
cControl::Shutdown();
|
2002-10-20 12:28:55 +02:00
|
|
|
int Channel = timer ? timer->Channel()->Number() : 0;
|
|
|
|
const char *File = timer ? timer->File() : "";
|
2006-04-15 11:33:34 +02:00
|
|
|
if (timer)
|
|
|
|
Delta = Next - time(NULL); // compensates for Confirm() timeout
|
2001-09-01 11:44:08 +02:00
|
|
|
char *cmd;
|
2004-12-26 12:45:22 +01:00
|
|
|
asprintf(&cmd, "%s %ld %ld %d \"%s\" %d", Shutdown, Next, Delta, Channel, *strescape(File, "\"$"), UserShutdown);
|
2002-05-13 16:35:49 +02:00
|
|
|
isyslog("executing '%s'", cmd);
|
2001-10-20 10:39:27 +02:00
|
|
|
SystemExec(cmd);
|
2002-08-11 13:32:23 +02:00
|
|
|
free(cmd);
|
2006-03-19 13:32:27 +01:00
|
|
|
LastActivity = time(NULL) - Setup.MinUserInactivity * 60 + SHUTDOWNRETRY; // try again later
|
2001-09-01 11:44:08 +02:00
|
|
|
}
|
2006-03-19 13:32:27 +01:00
|
|
|
else {
|
|
|
|
LastActivity = Now;
|
|
|
|
if (WatchdogTimeout > 0) {
|
|
|
|
alarm(WatchdogTimeout);
|
|
|
|
if (signal(SIGALRM, Watchdog) == SIG_IGN)
|
|
|
|
signal(SIGALRM, SIG_IGN);
|
|
|
|
}
|
|
|
|
}
|
2002-12-13 14:31:17 +01:00
|
|
|
UserShutdown = false;
|
2001-09-01 11:44:08 +02:00
|
|
|
continue; // skip the rest of the housekeeping for now
|
2001-09-01 09:04:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Disk housekeeping:
|
2001-02-11 14:53:44 +01:00
|
|
|
RemoveDeletedRecordings();
|
2003-12-22 13:29:24 +01:00
|
|
|
cSchedules::Cleanup();
|
2002-05-12 10:20:17 +02:00
|
|
|
// Plugins housekeeping:
|
|
|
|
PluginManager.Housekeeping();
|
2001-02-11 14:53:44 +01:00
|
|
|
}
|
|
|
|
}
|
2006-04-17 10:30:00 +02:00
|
|
|
// Main thread hooks of plugins:
|
|
|
|
PluginManager.MainThreadHook();
|
2000-04-15 17:38:11 +02:00
|
|
|
}
|
2001-06-02 10:47:40 +02:00
|
|
|
if (Interrupted)
|
2002-05-13 16:35:49 +02:00
|
|
|
isyslog("caught signal %d", Interrupted);
|
2004-10-23 15:17:03 +02:00
|
|
|
|
|
|
|
Exit:
|
|
|
|
|
2005-01-30 14:23:01 +01:00
|
|
|
PluginManager.StopPlugins();
|
2002-06-16 12:57:31 +02:00
|
|
|
cRecordControls::Shutdown();
|
2002-06-22 10:11:59 +02:00
|
|
|
cCutter::Stop();
|
2000-04-30 10:22:13 +02:00
|
|
|
delete Menu;
|
2002-06-23 11:23:34 +02:00
|
|
|
cControl::Shutdown();
|
2000-10-08 12:24:30 +02:00
|
|
|
delete Interface;
|
2004-05-16 10:35:36 +02:00
|
|
|
cOsdProvider::Shutdown();
|
2002-10-12 15:22:29 +02:00
|
|
|
Remotes.Clear();
|
2002-11-03 11:53:58 +01:00
|
|
|
Audios.Clear();
|
2004-05-16 10:35:36 +02:00
|
|
|
Skins.Clear();
|
2005-05-22 11:23:22 +02:00
|
|
|
if (ExitCode != 2) {
|
|
|
|
Setup.CurrentChannel = cDevice::CurrentChannel();
|
|
|
|
Setup.CurrentVolume = cDevice::CurrentVolume();
|
|
|
|
Setup.Save();
|
|
|
|
}
|
2002-06-16 12:57:31 +02:00
|
|
|
cDevice::Shutdown();
|
2006-04-14 11:51:13 +02:00
|
|
|
PluginManager.Shutdown(true);
|
2006-01-28 14:44:32 +01:00
|
|
|
cSchedules::Cleanup(true);
|
2003-12-22 13:29:24 +01:00
|
|
|
ReportEpgBugFixStats();
|
2001-02-24 16:18:43 +01:00
|
|
|
if (WatchdogTimeout > 0)
|
2002-05-13 16:35:49 +02:00
|
|
|
dsyslog("max. latency time %d seconds", MaxLatencyTime);
|
|
|
|
isyslog("exiting");
|
2000-07-29 19:03:09 +02:00
|
|
|
if (SysLogLevel > 0)
|
|
|
|
closelog();
|
2002-12-13 13:41:55 +01:00
|
|
|
if (HasStdin)
|
|
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &savedTm);
|
2001-06-02 10:47:40 +02:00
|
|
|
if (cThread::EmergencyExit()) {
|
2002-05-13 16:35:49 +02:00
|
|
|
esyslog("emergency exit!");
|
2001-06-02 10:47:40 +02:00
|
|
|
return 1;
|
|
|
|
}
|
2004-10-23 15:17:03 +02:00
|
|
|
return ExitCode;
|
2000-02-19 13:36:48 +01:00
|
|
|
}
|