Index: plugin.c
===================================================================
--- plugin.c	(.../tags/1.5.0)	(Revision 838)
+++ plugin.c	(.../branches/shutdown)	(Revision 838)
@@ -80,6 +80,11 @@
   return NULL;
 }
 
+time_t cPlugin::NextWakeupEvent(void)
+{
+  return 0;
+}
+
 const char *cPlugin::MainMenuEntry(void)
 {
   return NULL;
@@ -403,6 +408,23 @@
   return false;
 }
 
+time_t cPluginManager::NextWakeupEvent(void)
+{
+  time_t Next = 0;
+  time_t Now = time(NULL);
+  if (pluginManager) {
+     for (cDll *dll = pluginManager->dlls.First(); dll; dll = pluginManager->dlls.Next(dll)) {
+         cPlugin *p = dll->Plugin();
+         if (p) {
+            time_t t = p->NextWakeupEvent();
+            if (t > Now && (!Next || t < Next))
+               Next = t;
+            }
+         }
+     }
+  return Next;
+}
+
 bool cPluginManager::HasPlugins(void)
 {
   return pluginManager && pluginManager->dlls.Count();
Index: plugin.h
===================================================================
--- plugin.h	(.../tags/1.5.0)	(Revision 838)
+++ plugin.h	(.../branches/shutdown)	(Revision 838)
@@ -41,6 +41,7 @@
   virtual void Housekeeping(void);
   virtual void MainThreadHook(void);
   virtual cString Active(void);
+  virtual time_t NextWakeupEvent(void);
 
   virtual const char *MainMenuEntry(void);
   virtual cOsdObject *MainMenuAction(void);
@@ -93,6 +94,7 @@
   void Housekeeping(void);
   void MainThreadHook(void);
   static bool Active(const char *Prompt = NULL);
+  static time_t NextWakeupEvent(void);
   static bool HasPlugins(void);
   static cPlugin *GetPlugin(int Index);
   static cPlugin *GetPlugin(const char *Name);
Index: config.c
===================================================================
--- config.c	(.../tags/1.5.0)	(Revision 838)
+++ config.c	(.../branches/shutdown)	(Revision 838)
@@ -267,6 +267,7 @@
   SplitEditedFiles = 0;
   MinEventTimeout = 30;
   MinUserInactivity = 300;
+  NextWakeupEvent = 0;
   MultiSpeedMode = 0;
   ShowReplayMode = 0;
   ResumeID = 0;
@@ -428,6 +429,7 @@
   else if (!strcasecmp(Name, "SplitEditedFiles"))    SplitEditedFiles   = atoi(Value);
   else if (!strcasecmp(Name, "MinEventTimeout"))     MinEventTimeout    = atoi(Value);
   else if (!strcasecmp(Name, "MinUserInactivity"))   MinUserInactivity  = atoi(Value);
+  else if (!strcasecmp(Name, "NextWakeupEvent"))     NextWakeupEvent    = atoi(Value);
   else if (!strcasecmp(Name, "MultiSpeedMode"))      MultiSpeedMode     = atoi(Value);
   else if (!strcasecmp(Name, "ShowReplayMode"))      ShowReplayMode     = atoi(Value);
   else if (!strcasecmp(Name, "ResumeID"))            ResumeID           = atoi(Value);
@@ -496,6 +498,7 @@
   Store("SplitEditedFiles",   SplitEditedFiles);
   Store("MinEventTimeout",    MinEventTimeout);
   Store("MinUserInactivity",  MinUserInactivity);
+  Store("NextWakeupEvent",    NextWakeupEvent);
   Store("MultiSpeedMode",     MultiSpeedMode);
   Store("ShowReplayMode",     ShowReplayMode);
   Store("ResumeID",           ResumeID);
Index: config.h
===================================================================
--- config.h	(.../tags/1.5.0)	(Revision 838)
+++ config.h	(.../branches/shutdown)	(Revision 838)
@@ -244,6 +244,7 @@
   int MaxVideoFileSize;
   int SplitEditedFiles;
   int MinEventTimeout, MinUserInactivity;
+  time_t NextWakeupEvent;
   int MultiSpeedMode;
   int ShowReplayMode;
   int ResumeID;
Index: vdr.c
===================================================================
--- vdr.c	(.../tags/1.5.0)	(Revision 838)
+++ vdr.c	(.../branches/shutdown)	(Revision 838)
@@ -54,6 +54,7 @@
 #include "plugin.h"
 #include "rcu.h"
 #include "recording.h"
+#include "shutdown.h"
 #include "skinclassic.h"
 #include "skinsttng.h"
 #include "sources.h"
@@ -70,7 +71,7 @@
 #define CHANNELSAVEDELTA  600 // seconds before saving channels.conf after automatic modifications
 #define DEVICEREADYTIMEOUT 30 // seconds to wait until all devices are ready
 #define MENUTIMEOUT       120 // seconds of user inactivity after which an OSD display is closed
-#define SHUTDOWNRETRY     300 // seconds before trying again to shut down
+#define SHUTDOWNRETRY     360 // seconds before trying again to shut down
 #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
@@ -184,7 +185,6 @@
   bool MuteAudio = false;
   int WatchdogTimeout = DEFAULTWATCHDOG;
   const char *Terminal = NULL;
-  const char *Shutdown = NULL;
 
   bool UseKbd = true;
   const char *LircDevice = NULL;
@@ -313,7 +313,7 @@
                     break;
           case 'r': cRecordingUserCommand::SetCommand(optarg);
                     break;
-          case 's': Shutdown = optarg;
+          case 's': Shutdown.SetShutdownCommand(optarg);
                     break;
           case 't': Terminal = optarg;
                     if (access(Terminal, R_OK | W_OK) < 0) {
@@ -498,10 +498,7 @@
   int PreviousChannel[2] = { 1, 1 };
   int PreviousChannelIndex = 0;
   time_t LastChannelChanged = time(NULL);
-  time_t LastActivity = 0;
   int MaxLatencyTime = 0;
-  bool ForceShutdown = false;
-  bool UserShutdown = false;
   bool InhibitEpgScan = false;
   bool IsInfoMenu = false;
   cSkin *CurrentSkin = NULL;
@@ -596,6 +593,11 @@
         }
      }
 
+
+  // Check for timers in automatic start time window:
+  Shutdown.CheckManualStart(MANUALSTART);
+
+
   // User interface:
 
   Interface = new cInterface(SVDRPport);
@@ -856,9 +858,14 @@
         // User Input:
         cOsdObject *Interact = Menu ? Menu : cControl::Control();
         eKeys key = Interface->GetKey(!Interact || !Interact->NeedsFastResponse());
-        if (NORMALKEY(key) != kNone) {
+        if (NORMALKEY(key) != kNone && NORMALKEY(key) != k_Plugin) {
            EITScanner.Activity();
-           LastActivity = time(NULL);
+
+           // Cancel 5 minute shutdown countdown:
+           if (Shutdown.countdown) Shutdown.countdown.Cancel();
+
+           // Set user active for MinUserInactivity time in the future
+           Shutdown.SetUserInactiveTimeout();
            }
         // Keys that must work independent of any interactive mode:
         switch (key) {
@@ -996,37 +1003,39 @@
                   }
                break;
           // Power off:
-          case kPower: {
+          case kPower:
                isyslog("Power button pressed");
                DELETE_MENU;
-               if (!Shutdown) {
-                  Skins.Message(mtError, tr("Can't shutdown - option '-s' not given!"));
+               
+               // Check for activity, request power button again if active
+               if (!Shutdown.ConfirmShutdown(false)
+                   && Skins.Message(mtWarning, tr("VDR will shut down later. Press power to force."), 5) != kPower) {
+                  // Not pressed power. Set VDR to be non-interactive and power down later.
+                  Shutdown.SetUserInactive();
                   break;
                   }
-               LastActivity = 1; // not 0, see below!
-               UserShutdown = true;
-               if (cRecordControls::Active()) {
-                  if (!Interface->Confirm(tr("Recording - shut down anyway?")))
-                     break;
+               
+               // No activity or 2x power button pressed. Ask for confirmations.
+               if (!Shutdown.ConfirmShutdown(true)) {
+                  // Non-confirmed background activity, set VDR to be non-interactive and power down later.
+                  Shutdown.SetUserInactive();
+                  break;
                   }
-               if (cPluginManager::Active(tr("shut down anyway?")))
+                
+               // Ask the final question
+               if (!Interface->Confirm(tr("Press any key to cancel shutdown"), 5, true))
+                  // If final question was canceled, continue to be active
                   break;
-               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;
-                     }
-                  }
-               ForceShutdown = true;
+                
+               // Ok, now call the shutdown scripts
+               Shutdown.DoShutdown(true);
+               // Set VDR to be non-interactive and power down again later.
+               Shutdown.SetUserInactive();
+               // Do not attempt to automatically shut down for 6 minutes
+               Shutdown.SetRetry(SHUTDOWNRETRY);
+
+               // will retry shutdown in 6 minutes
                break;
-               }
           default: break;
           }
         Interact = Menu ? Menu : cControl::Control(); // might have been closed in the mean time
@@ -1041,7 +1050,7 @@
                     continue;
                     }
                  }
-              else if (time(NULL) - LastActivity > MENUTIMEOUT)
+              else if (cRemote::LastActivity() > MENUTIMEOUT)
                  state = osEnd;
               }
            switch (state) {
@@ -1143,74 +1152,82 @@
                  Skins.Message(mtInfo, tr("Editing process finished"));
               }
            }
-        if (!Interact && ((!cRecordControls::Active() && !cCutter::Active() && (!Interface->HasSVDRPConnection() || UserShutdown)) || ForceShutdown)) {
-           time_t Now = time(NULL);
-           if (Now - LastActivity > ACTIVITYTIMEOUT) {
-              // Shutdown:
-              if (Shutdown && (Setup.MinUserInactivity || LastActivity == 1) && Now - LastActivity > Setup.MinUserInactivity * 60) {
-                 cTimer *timer = Timers.GetNextActiveTimer();
-                 time_t Next  = timer ? timer->StartTime() : 0;
-                 time_t Delta = timer ? Next - Now : 0;
-                 if (!LastActivity) {
-                    if (!timer || Delta > MANUALSTART) {
-                       // Apparently the user started VDR manually
-                       dsyslog("assuming manual start of VDR");
-                       LastActivity = Now;
-                       continue; // don't run into the actual shutdown procedure below
-                       }
-                    else
-                       LastActivity = 1;
-                    }
-                 if (timer && Delta < Setup.MinEventTimeout * 60 && ForceShutdown) {
-                    Delta = Setup.MinEventTimeout * 60;
-                    Next = Now + Delta;
-                    timer = NULL;
-                    dsyslog("reboot at %s", *TimeToString(Next));
-                    }
-                 if (!ForceShutdown && cPluginManager::Active()) {
-                    LastActivity = Now - Setup.MinUserInactivity * 60 + SHUTDOWNRETRY; // try again later
-                    continue;
-                    }
-                 if (!Next || Delta > Setup.MinEventTimeout * 60 || ForceShutdown) {
-                    ForceShutdown = false;
-                    if (timer)
-                       dsyslog("next timer event at %s", *TimeToString(Next));
-                    if (WatchdogTimeout > 0)
-                       signal(SIGALRM, SIG_IGN);
-                    if (Interface->Confirm(tr("Press any key to cancel shutdown"), UserShutdown ? 5 : SHUTDOWNWAIT, true)) {
-                       cControl::Shutdown();
-                       int Channel = timer ? timer->Channel()->Number() : 0;
-                       const char *File = timer ? timer->File() : "";
-                       if (timer)
-                          Delta = Next - time(NULL); // compensates for Confirm() timeout
-                       char *cmd;
-                       asprintf(&cmd, "%s %ld %ld %d \"%s\" %d", Shutdown, Next, Delta, Channel, *strescape(File, "\"$"), UserShutdown);
-                       isyslog("executing '%s'", cmd);
-                       SystemExec(cmd);
-                       free(cmd);
-                       LastActivity = time(NULL) - Setup.MinUserInactivity * 60 + SHUTDOWNRETRY; // try again later
-                       }
-                    else {
-                      LastActivity = Now;
-                      if (WatchdogTimeout > 0) {
-                         alarm(WatchdogTimeout);
-                         if (signal(SIGALRM, Watchdog) == SIG_IGN)
-                            signal(SIGALRM, SIG_IGN);
-                         }
-                      }
-                    UserShutdown = false;
-                    continue; // skip the rest of the housekeeping for now
-                    }
-                 }
-              // Disk housekeeping:
-              RemoveDeletedRecordings();
-              cSchedules::Cleanup();
-              // Plugins housekeeping:
-              PluginManager.Housekeeping();
+        // Update the shutdown countdown:
+        if (Shutdown.countdown && Shutdown.countdown.Update()) {
+           // Also on countdown, every 10 seconds:
+           
+           // Any action that would cancel shutdown?
+           if (!Shutdown.ConfirmShutdown(false)) 
+              Shutdown.countdown.Cancel();
+           }
+
+        if (!Interact && !cRecordControls::Active() && !cCutter::Active() && !Interface->HasSVDRPConnection() && cRemote::LastActivity() > ACTIVITYTIMEOUT) {
+           // Handle housekeeping tasks
+
+           // Shutdown:
+           // Check whether VDR will be ready for shutdown in 5 minutes:
+           time_t Soon = time(NULL) + SHUTDOWNWAIT;
+           if (Shutdown.IsUserInactive(Soon) && Shutdown.Retry(Soon) && !Shutdown.countdown) {
+              if (Shutdown.ConfirmShutdown(false))
+                 // Time to shut down. Start final 5 minute countdown
+                 Shutdown.countdown.Start(tr("VDR will shut down in %s minutes"),SHUTDOWNWAIT);
+              
+              // Dont try to shut down again for next 6 minutes
+              Shutdown.SetRetry(SHUTDOWNRETRY);
               }
+
+           // Countdown run down to 0?              
+           if (Shutdown.countdown.Done()) {
+              // Timed out, now do a final check
+              if (Shutdown.IsUserInactive() && Shutdown.ConfirmShutdown(false)) 
+                 // and call the shutdown command
+                 Shutdown.DoShutdown(false);
+  
+              // Do this again a bit later
+              Shutdown.SetRetry(SHUTDOWNRETRY);
+              }
+ 
+           // Disk housekeeping:
+           RemoveDeletedRecordings();
+           cSchedules::Cleanup();
+           // Plugins housekeeping:
+           PluginManager.Housekeeping();
            }
+           
         // Main thread hooks of plugins:
         PluginManager.MainThreadHook();
+        
+        
+        #define DebugTimeouts
+        #ifdef DebugTimeouts
+        
+        static time_t DebugTime = time(NULL);
+        time_t Now = time(NULL);
+        if (Now - DebugTime >= 1) {
+           if (!cRemote::LastActivityTime())
+              fprintf(stderr,"LastActivity: Never ");
+           else
+              fprintf(stderr,"LastActivity: %5i ",cRemote::LastActivity());
+
+           time_t ActiveTimeout = Shutdown.GetUserInactiveTime();
+           if (!ActiveTimeout)
+              fprintf(stderr,"ActiveTimeout:  Never ");
+           else
+              fprintf(stderr,"ActiveTimeout: %6i ",(int)(Now - ActiveTimeout));
+           
+           time_t Retry = Shutdown.GetRetry();
+           if (!Retry)
+              fprintf(stderr,"Retry:  Never ");
+           else
+              fprintf(stderr,"Retry: %6i ",(int)(Now - Retry));
+           
+           fprintf(stderr,"Counter: %s\r",Shutdown.countdown ? "Yes" : "No ");
+           
+           fflush(stderr);
+           DebugTime = Now;
+           }
+
+        #endif
         }
   if (Interrupted)
      isyslog("caught signal %d", Interrupted);
Index: Makefile
===================================================================
--- Makefile	(.../tags/1.5.0)	(Revision 838)
+++ Makefile	(.../branches/shutdown)	(Revision 838)
@@ -35,7 +35,7 @@
 OBJS = audio.o channels.o ci.o config.o cutter.o device.o diseqc.o dvbdevice.o dvbci.o dvbosd.o\
        dvbplayer.o dvbspu.o eit.o eitscan.o epg.o filter.o font.o i18n.o interface.o keys.o\
        lirc.o menu.o menuitems.o nit.o osdbase.o osd.o pat.o player.o plugin.o rcu.o\
-       receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o\
+       receiver.o recorder.o recording.o remote.o remux.o ringbuffer.o sdt.o sections.o shutdown.o\
        skinclassic.o skins.o skinsttng.o sources.o spu.o status.o svdrp.o themes.o thread.o\
        timers.o tools.o transfer.o vdr.o videodir.o
 
Index: shutdown.c
===================================================================
--- shutdown.c	(.../tags/1.5.0)	(Revision 0)
+++ shutdown.c	(.../branches/shutdown)	(Revision 838)
@@ -0,0 +1,243 @@
+/*
+ * shutdown.c: Handling for shutdown and inactivity
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: $
+ */
+
+#include "shutdown.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "channels.h"
+#include "config.h"
+#include "cutter.h"
+#include "i18n.h"
+#include "interface.h"
+#include "menu.h"
+#include "plugin.h"
+#include "timers.h"
+#include "tools.h"
+
+cShutdown Shutdown;
+
+cCountdown::cCountdown(void) 
+{
+  timeout = 0;
+  counter = 0;
+  timedOut = false;
+  format = NULL;
+}
+
+void cCountdown::Start(const char *Format, int Seconds)
+{
+  timeout = time(NULL) + Seconds;
+  counter = -1;
+  timedOut = false;
+  format = Format;
+  Update();
+}  
+
+void cCountdown::Cancel(void)
+{ 
+  if (timeout) { 
+     timeout = 0;
+     timedOut = false;
+     Skins.Message(mtStatus, NULL); 
+     } 
+}
+
+bool cCountdown::Update(void)
+{
+  if (!timeout) return false;
+
+  int NewCounter = (timeout - time(NULL) + 9) / 10;
+  if (NewCounter <= 0) 
+     timedOut=true;
+     
+  if (counter != NewCounter) {
+     counter = NewCounter;
+     
+     char time[10];
+     char *Message;
+     snprintf(time, sizeof time, "%i:%i0", counter>0 ? counter/6 : 0, counter>0 ? counter%6 : 0);
+     asprintf(&Message,format,time);
+     
+     Skins.Message(mtStatus, Message);
+     free(Message);
+     return true;
+     }
+  return false;
+}  
+
+cShutdown::cShutdown(void)
+{
+  activeTimeout = 0;
+  retry = 0;
+  shutdownCommand = NULL;
+}
+
+void cShutdown::CheckManualStart(int ManualStart)
+{
+  time_t Delta = Setup.NextWakeupEvent ? Setup.NextWakeupEvent - time(NULL) : 0;
+  
+  if (!Setup.NextWakeupEvent || abs(Delta) > ManualStart) {
+     // Apparently the user started VDR manually
+     dsyslog("assuming manual start of VDR");
+     // Set inactive after MinUserInactivity
+     SetUserInactiveTimeout();
+     }
+  else
+     // Set inactive from now on
+     SetUserInactive();
+}
+
+void cShutdown::SetShutdownCommand(const char *ShutdownCommand)
+{
+  shutdownCommand = ShutdownCommand;
+}
+
+bool SystemExecSession(const char *Command)
+{
+  pid_t pid;
+
+  if ((pid = fork()) < 0) { // fork failed
+     LOG_ERROR;
+     return false;
+     }
+
+  if (pid > 0) // parent process
+     return true;
+     
+  // child process
+  pid_t sid=setsid();
+  if (sid < 0) LOG_ERROR;
+     
+  int MaxPossibleFileDescriptors = getdtablesize();
+  for (int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
+      close(i); //close all dup'ed filedescriptors
+  if (execl("/bin/sh", "sh", "-c", Command, NULL) == -1) {
+     LOG_ERROR_STR(Command);
+     _exit(-1);
+     }
+  _exit(0);  
+}
+
+void cShutdown::CallShutdownCommand(time_t NextEvent, int Channel, const char *File, bool UserShutdown)
+{
+  time_t Delta = NextEvent ? NextEvent - time(NULL) : 0;
+  char *cmd;
+
+  asprintf(&cmd, "%s %ld %ld %d \"%s\" %d", shutdownCommand, NextEvent, Delta, Channel, *strescape(File, "\"$"), UserShutdown);
+  isyslog("executing '%s'", cmd);
+  if (SystemExecSession(cmd))
+     // Remember this wakeup time for comparison on reboot
+     Setup.NextWakeupEvent = NextEvent;
+  free(cmd);
+}
+
+void cShutdown::SetUserInactiveTimeout(int Seconds, bool Force)
+{
+  if (!Setup.MinUserInactivity && !Force) {
+    activeTimeout = 0;
+    return;
+  }
+  if (Seconds < 0) Seconds = Setup.MinUserInactivity * 60;
+  activeTimeout = time(NULL) + Seconds;
+}
+
+bool cShutdown::ConfirmShutdown(bool Interactive)
+{
+  if (!shutdownCommand) {
+     if (Interactive) Skins.Message(mtError, tr("Can't shutdown - option '-s' not given!"));
+     return false;
+     }
+  if (cReplayControl::NowReplaying()) {
+     if (!Interactive || !Interface->Confirm(tr("Replaying - shut down anyway?")))
+        return false;
+     }
+  if (cCutter::Active()) {
+     if (!Interactive || !Interface->Confirm(tr("Cutting - shut down anyway?")))
+        return false;
+     }
+
+  cTimer *timer = Timers.GetNextActiveTimer();
+  time_t Next  = timer ? timer->StartTime() : 0;
+  time_t Delta = timer ? Next - time(NULL) : 0;
+
+  if (cRecordControls::Active() || (Next && Delta <= 0)) {
+     // VPS recordings in timer end margin may cause Delta <= 0
+     
+     if (!Interactive || !Interface->Confirm(tr("Recording - shut down anyway?")))
+        return false;
+     }
+  else if (Next && Delta <= Setup.MinEventTimeout * 60) {
+     // Timer within Min Event Timeout
+     if (!Interactive) return false;
+     
+     char *buf;
+     asprintf(&buf, tr("Recording in %ld minutes, shut down anyway?"), Delta / 60);
+     bool confirm = Interface->Confirm(buf);
+     free(buf);
+     if (!confirm)
+        return false;
+     }
+
+  if (cPluginManager::Active(Interactive ? tr("shut down anyway?") : NULL))
+     return false;
+  
+  Next = cPluginManager::NextWakeupEvent();
+  Delta = Next ? Next - time(NULL) : 0;
+  if (Next && Delta <= Setup.MinEventTimeout * 60) {
+     // Plugin event within Min Event Timeout
+     if (!Interactive) return false;
+     
+     char *buf;
+     asprintf(&buf, tr("Plugin activity in %ld minutes, shut down anyway?"), Delta / 60);
+     bool confirm = Interface->Confirm(buf);
+     free(buf);
+     if (!confirm)
+        return false;
+     }
+  
+  return true;  
+}
+
+bool cShutdown::DoShutdown(bool Force)
+{
+  time_t Now = time(NULL);
+  cTimer *timer = Timers.GetNextActiveTimer();
+  time_t Next  = timer ? timer->StartTime() : 0;
+  time_t NextPlugin = cPluginManager::NextWakeupEvent();
+  if (NextPlugin && (!Next || Next > NextPlugin)) {
+     Next = NextPlugin;
+     timer = NULL;
+     }
+  time_t Delta = Next ? Next - Now : 0;
+
+  if (Next && Delta < Setup.MinEventTimeout * 60) {
+     if (!Force)
+        return false;
+     Delta = Setup.MinEventTimeout * 60;
+     Next = Now + Delta;
+     timer = NULL;
+     dsyslog("reboot at %s", *TimeToString(Next));
+     }
+
+  if (Next)
+     dsyslog("next timer event at %s", *TimeToString(Next));
+
+  if (Next && timer)
+     CallShutdownCommand(Next, timer->Channel()->Number(), timer->File(), Force);
+  else if (Next)
+     CallShutdownCommand(Next, 0, "", Force);
+  else
+     CallShutdownCommand(0, 0, "", Force);
+
+  return true;
+}
Index: shutdown.h
===================================================================
--- shutdown.h	(.../tags/1.5.0)	(Revision 0)
+++ shutdown.h	(.../branches/shutdown)	(Revision 838)
@@ -0,0 +1,110 @@
+/*
+ * shutdown.h: Handling for shutdown and inactivity
+ *
+ * See the main source file 'vdr.c' for copyright information and
+ * how to reach the author.
+ *
+ * $Id: $
+ */
+
+#ifndef __SHUTDOWN_H
+#define __SHUTDOWN_H
+
+#include <time.h>
+
+class cCountdown {
+private:
+  time_t timeout;     ///< 5-minute countdown timer
+  int counter;        ///< last shown time in 10s units
+  bool timedOut;      ///< Countdown did run down to 0 and was not cancelled
+  const char *format; ///< Format string for message display, %s is placeholder
+  
+public:
+  cCountdown(void);
+
+  void Start(const char *Message, int Seconds); 
+      ///< Start the 5 minute shutdown warning countdown
+
+  void Cancel(void); 
+      ///< Cancel the 5 minute shutdown warning countdown
+
+  bool Done(void) { if (!timedOut) return false; Cancel(); return true; }
+      ///< Check if countdown timer has run out without cancelling
+
+  operator bool(void) const { return timeout != 0; }
+      ///< Check if countdown is running
+
+  bool Update(void);
+      ///< Update status display of the countdown
+      ///< Returns true on actual update
+};
+
+class cShutdown {
+private:
+  time_t activeTimeout;
+      ///< Time when VDR will become non-interactive. 0 means never.
+  time_t retry;
+      ///< Time for retrying the shutdown. 
+  
+  const char *shutdownCommand;
+      ///< Command for shutting down VDR, set with -s option
+
+public:
+  cShutdown(void);
+
+  void CheckManualStart(int ManualStart);
+      ///< Check whether next timer is in ManualStart time window.
+      ///< If yes, assume non-interactive use
+  
+  void SetShutdownCommand(const char *ShutdownCommand);
+      ///< Set the command string for shutdown command
+  
+  void CallShutdownCommand(time_t NextEvent, int Channel, const char *File, bool UserShutdown);
+      ///< Call the shutdown command, giving it these parameters
+  
+  bool IsUserInactive(time_t AtTime = 0) { return activeTimeout && activeTimeout <= (AtTime ? AtTime : time(NULL)); }
+      ///< Check whether VDR is in interactive mode or non-interactive mode (waiting for shutdown).
+      ///< AtTime checks whether VDR will probably be inactive at that time.
+
+  time_t GetUserInactiveTime(void) { return activeTimeout; }
+      ///< Time when user will become non-inactive, or 0 if never
+  
+  void SetUserInactiveTimeout(int Seconds = -1, bool Force = false);
+      ///< Set the time when VDR will switch into non-interactive mode or power down.
+      ///< -1 means Setup.MinUserInactivity in the future.
+      ///< Otherwise, seconds in the future.
+      ///< If MinUserInactivity = 0 and Force = false, Seconds is ignored and VDR will 
+      ///< stay interactive forever.
+      
+  void SetUserInactive() { SetUserInactiveTimeout(0, true); }
+      ///< Set VDR manually into non-interactive mode.
+  
+  bool Retry(time_t AtTime = 0) { return retry <= (AtTime ? AtTime : time(NULL)); }
+      ///< Check whether its time to re-try the shutdown.
+      ///< AtTime checks whether VDR will probably be inactive at that time.
+  
+  time_t GetRetry(void) { return retry; }
+      ///< Time when shutdown retry block ends.
+
+  void SetRetry(int Seconds) { retry = time(NULL) + Seconds; }
+      ///< Set shutdown retry so that VDR will not try to automatically shut down
+      ///< within Seconds.
+  
+  bool ConfirmShutdown(bool Ask);
+      ///< Check for background activity that blocks shutdown.
+      ///< Returns immediately and without user interaction if Ask=false
+      ///< Asks for confirmation if ask=true
+      ///< Returns true if ready for shutdown.
+  
+  bool DoShutdown(bool Force);
+      ///< Call the shutdown script with data of the next pending timer.
+      ///< Fails if force = false and a timer is running or within MinEventTimeout.
+      ///< Always calls shutdown on force = true.
+      ///< Returns true on success.
+    
+  cCountdown countdown;
+};
+
+extern cShutdown Shutdown;
+
+#endif
Index: remote.c
===================================================================
--- remote.c	(.../tags/1.5.0)	(Revision 838)
+++ remote.c	(.../branches/shutdown)	(Revision 838)
@@ -31,6 +31,7 @@
 cCondVar cRemote::keyPressed;
 const char *cRemote::keyMacroPlugin = NULL;
 const char *cRemote::callPlugin = NULL;
+time_t cRemote::lastActivity = 0;
 
 cRemote::cRemote(const char *Name)
 {
@@ -183,6 +184,7 @@
             out = 0;
          if ((k & k_Repeat) != 0)
             repeatTimeout.Set(REPEATTIMEOUT);
+         lastActivity = time(NULL);
          return k;
          }
       else if (!WaitMs || !keyPressed.TimedWait(mutex, WaitMs) && repeatTimeout.TimedOut())
Index: remote.h
===================================================================
--- remote.h	(.../tags/1.5.0)	(Revision 838)
+++ remote.h	(.../branches/shutdown)	(Revision 838)
@@ -28,6 +28,7 @@
   static char *unknownCode;
   static cMutex mutex;
   static cCondVar keyPressed;
+  static time_t lastActivity;
   static const char *keyMacroPlugin;
   static const char *callPlugin;
   char *name;
@@ -61,6 +62,10 @@
       ///< plugin name will be reset to NULL by this call.
   static bool HasKeys(void);
   static eKeys Get(int WaitMs = 1000, char **UnknownCode = NULL);
+  static int LastActivity() { return time(NULL) - lastActivity; }
+      ///< Seconds since last key was delivered by Get().
+  static time_t LastActivityTime() { return lastActivity; }
+      ///< Absolute time when last key was delivered by Get().
   };
 
 class cRemotes : public cList<cRemote> {};

Eigenschaftsänderungen: 
___________________________________________________________________
Name: svnmerge-integrated
   + /vdr/trunk:1-833

