diff -ruN vdr-1.3.38/CONTRIBUTORS vdr-1.3.39/CONTRIBUTORS
--- vdr-1.3.38/CONTRIBUTORS	2006-01-08 12:03:44.000000000 +0100
+++ vdr-1.3.39/CONTRIBUTORS	2006-01-15 16:06:19.000000000 +0100
@@ -71,6 +71,8 @@
  and had their start time changed into the future
  for suggesting to give the timer status a bit that is set when that timer
  is currently recording
+ for suggesting to make the SVDRP command LSTT optionally list the channels
+ of the timers with their unique channel ids instead of their numbers
 
 Miha Setina <mihasetina@softhome.net>
  for translating OSD texts to the Slovenian language
@@ -209,6 +211,10 @@
  live PIDs
  for reporting a problem in SetProgress() of the 'skincurses' plugin in case Total
  is 0
+ for fixing canonicalizing the file name in the SVDRP command GRAB to allow full
+ path names
+ for suggesting that the SVDRP command GRAB should allow file names without extension
+ again
 
 Ulrich Röder <roeder@efr-net.de>
  for pointing out that there are channels that have a symbol rate higher than 27500
@@ -425,6 +431,7 @@
  for suggesting to avoid the external 'find' command to scan the video directory
  for reporting a problem with inconsistent channel and timer lists
  for making the "Play" key in live viewing mode resume a previous replay session
+ for suggesting to allow defining key macros for all non-modeless keys
 
 Michael Rakowski <mrak@gmx.de>
  for translating OSD texts to the Polish language
@@ -853,6 +860,9 @@
  for reporting that there are empty info.vdr files created if there is no EPG
  info available
  for implementing the SVDRP command MOVC
+ for fixing the "plugins-clean" and "plugins-install" targets in the Makefile
+ for suggesting to make the log messages regarding lost lock of devices "info"
+ instead of "error"
 
 Manuel Hartl <icecep@gmx.net>
  for suggesting to extend the logging info when starting/stopping timers
@@ -925,6 +935,7 @@
  for changing the title of the recording info menu
  for reporting a bug in handling key macros with keys after @plugin
  for adding compiler options "-fPIC -g" to all plugins
+ for adding a leading '0' to the day in the DayDateTime() function
 
 Ralf Klueber <ralf.klueber@vodafone.com>
  for reporting a bug in cutting a recording if there is only a single editing mark
@@ -940,6 +951,7 @@
  for suggesting to implement the command line option '--vfat'
  for reporting a leftover 'summary.vdr' in vdr.5
  for adding more error messages and line numbers when reading EPG data and info.vdr
+ for fixing handling "more than 3 byte" key sequences in cKbdRemote::ReadKeySequence()
 
 Alexander Damhuis <ad@phonedation.de>
  for reporting problems when deleting a timer that is currently recording
@@ -1459,6 +1471,9 @@
  warnings with g++ 4.1.0
  for reporting warnings with g++ 4.1.0 regarding incrementing the 'state' variables
  in the repacker classes in remux.c
+ for adding a missing #include <linux/unistd.h> to thread.c
+ for adding missing i18n entry for the "Timer" button
+ for removing the obsolete "ca.conf" section from vdr.1
 
 Steffen Beyer <cpunk@reactor.de>
  for fixing setting the colored button help after deleting a recording in case the next
@@ -1625,3 +1640,18 @@
 
 Ingo Schneider <mail@ingo-schneider.de>
  for adding a SleepMs() in cRecorder::Action() to avoid a busy loop
+
+Patrick Cernko <errror@errror.org>
+ for suggesting to add a note about "modprobe capability" to INSTALL
+
+Philippe Gramoullé <philippe@gramoulle.com>
+ for reporting a a missing '-' in the example for viewing a grabbed image on a remote
+ host
+
+André Weidemann <Andre.Weidemann@web.de>
+ for suggesting to only write Dolby Digital tracks into the 'info.vdr' file of a
+ recording if Setup.UseDolbyDigital is true
+
+Jürgen Schilling <juergen_schilling@web.de>
+ for reporting that color buttons were displayed in the recording info menu if it
+ has been invoked from a player
diff -ruN vdr-1.3.38/HISTORY vdr-1.3.39/HISTORY
--- vdr-1.3.38/HISTORY	2006-01-08 12:58:26.000000000 +0100
+++ vdr-1.3.39/HISTORY	2006-01-15 16:05:21.000000000 +0100
@@ -4029,7 +4029,7 @@
   name (based on a suggestion from Darren Salt).
   A simple way of viewing a grabbed image on a remote host is:
 
-  svdrpsend.pl -d <hostname> 'grab -' | sed -n -e 's/^216-//p' -e '1ibegin-base64 644 -' -e '$a====' | uudecode | display
+  svdrpsend.pl -d <hostname> 'grab -' | sed -n -e 's/^216-//p' -e '1ibegin-base64 644 -' -e '$a====' | uudecode | display -
 
 - The new command line option '-g' must be given if the SVDRP command GRAB
   shall be allowed to write image files to disk. The parameter to this option
@@ -4134,3 +4134,58 @@
 - Added a SleepMs() in cRecorder::Action() to avoid a busy loop (thanks to Ingo
   Schneider).
 - Cleaned up some trailing white space.
+
+2006-01-15: Version 1.3.39
+
+- The SVDRP command LSTT now accepts the new option 'id' to have the channels
+  of the timers listed with their unique channel ids instead of their numbers
+  (suggested by Matthias Schniedermeyer).
+- Added a missing #include <linux/unistd.h> to thread.c (thanks to Ville Skyttä).
+- Fixed the "plugins-clean" and "plugins-install" targets in the Makefile (thanks
+  to Andreas Brachold).
+- Fixed handling "more than 3 byte" key sequences in cKbdRemote::ReadKeySequence()
+  (thanks to Peter Bieringer). If you are using the PC keyboard as remote control
+  input you may need to make VDR newly learn the keys by removing the remote.conf
+  file.
+- To avoid problems with access rights when VDR shall run as 'root' it now skips
+  all SetCaps() and SetUser() calls when it is started as 'root' and "-u root"
+  is given.
+- Added missing i18n entry for the "Timer" button (thanks to Ville Skyttä)
+- Updated the Finnish OSD texts (thanks to Rolf Ahrenberg).
+- Making the "Menu" key behave consistently has not been well received by several
+  users, so the new option "Setup/OSD/Menu button closes" can be used to get the
+  old behavior back (which also is the default value of this option).
+- Dropped the default vdr user. The program now always runs under the user id
+  it was started from, unless the '-u' option is given and it was started from
+  the 'root' user. If you want to have a default vdr user, you can activate and
+  adjust the "VDR_USER = vdr" line in your Make.config file (from the original
+  patch by Ludwig Nussel).
+- Key macros can now be defined for all non-modeless keys (suggested by Mirko Dölle).
+- Adjusted the "KEY MACROS" section of vdr.5 to the new plugin calling mechanism
+  introduced in version 1.3.32.
+- Removed the now obsolete "ca.conf" section from vdr.1 (thanks to Ville Skyttä).
+- Added missing description of L and R circular polarization to 'diseqc.conf'.
+- Added a note about "modprobe capability" to INSTALL (suggested by Patrick Cernko).
+- Fixed canonicalizing the file name in the SVDRP command GRAB to allow full path
+  names (thanks to Stefan Huelswitt).
+- Added a missing '-' to the example for viewing a grabbed image on a remote host
+  (reported by Philippe Gramoullé).
+- Made the "What's on now/next?" menus a lot faster by storing a pointer to each
+  channel's schedule in the cChannel data.
+- Made the log messages regarding lost lock of devices "info" instead of "error"
+  (suggested by Andreas Brachold).
+- The SVDRP command GRAB allows file names without extension again (suggested by
+  Stefan Huelswitt).
+- Pressing '0' in the "Schedule" menu now rotates through displaying "This event on
+  this channel", "This event on all channels" and "All events on all channels".
+  This can be used to find reruns of a given show, or the episodes of a series.
+  Note that if there are many channels in your channels.conf, displaying the
+  "All events on all channels" page may take a while.
+- The status markers in the "Schedule" menu are now only updated if a submenu is
+  closed in which a timer has been modified, which speeds up closing submenus.
+- Now only writing Dolby Digital tracks into the 'info.vdr' file of a recording
+  if Setup.UseDolbyDigital is true (suggested by André Weidemann).
+- Added a leading '0' to the day in the DayDateTime() function (thanks to Rolf
+  Ahrenberg).
+- No longer displaying color buttons in the recording info menu if it has been
+  invoked from a player (reported by Jürgen Schilling).
diff -ruN vdr-1.3.38/INSTALL vdr-1.3.39/INSTALL
--- vdr-1.3.38/INSTALL	2005-12-31 14:09:17.000000000 +0100
+++ vdr-1.3.39/INSTALL	2006-01-14 12:27:45.000000000 +0100
@@ -40,6 +40,12 @@
 VDR requires the Linux-DVB driver version dated 2003-08-23 or higher
 to work properly.
 
+You will also need to install the "libjpeg" and "libcap" libraries,
+as well as their "devel" packages to get the necessary header files
+for compiling VDR. If the "capability" module is not compiled into
+your kernel, you may need to do "modprobe capability" before running
+VDR.
+
 After extracting the package, change into the VDR directory
 and type 'make'. This should produce an executable file
 named 'vdr', which can be run after the DVB driver has been
@@ -136,10 +142,10 @@
 ------------------------
 
 If you want VDR to set the system time according to the data received
-from the transponder, you need to start VDR as user 'root'. VDR will
-then only keep the capability to set the system time, and set its
-user id to a lesser privileged one ('vdr' by default, can be set
-to a different value with the '-u' option).
+from the transponder, you need to start VDR as user 'root'. For security
+reasons you should then use the '-u' option to define a lesser privileged
+user id under which VDR should actually run. It will then only keep the
+capability to set the system time, and set its user id to the given one.
 You also need to enable the "EPG/Set system time" option in VDR's
 Setup menu, and select a transponder from which you want to receive
 the time in "Use time from transponder". Make sure you select a transponder
diff -ruN vdr-1.3.38/MANUAL vdr-1.3.39/MANUAL
--- vdr-1.3.38/MANUAL	2006-01-08 12:53:08.000000000 +0100
+++ vdr-1.3.39/MANUAL	2006-01-15 14:09:16.000000000 +0100
@@ -72,6 +72,7 @@
 
   Red       Recordings menu
   Green     Schedule menu
+  Yellow    Info
   Blue      Timers menu
 
   (1) The "On/Off" button in the "Timers" menu only works if sorting the timers
@@ -176,6 +177,12 @@
      whether there is currently a DVB card receiving the transponder this channel
      is on).
 
+  Pressing '0' in the "Schedule" menu rotates through displaying "This event on
+  this channel", "This event on all channels" and "All events on all channels".
+  This can be used to find reruns of a given show, or the episodes of a series.
+  Note that if there are many channels in your channels.conf, displaying the
+  "All events on all channels" page may take a while.
+
 * Selecting a Channel
 
   There are four ways to select a channel:
@@ -525,6 +532,13 @@
                                (first) line of the list directly to the first (last)
                                one.
 
+  Menu button closes = no
+                         If set to "yes", pressing the "Menu" button while there is
+                         anything displayed on the OSD will close the OSD. If set
+                         to "no", the "Menu" button will open the main menu after
+                         closing a temporary display, like, for instance, the channel
+                         display.
+
   Sort timers = yes      Turns sorting the timers in the "Timers" menu on/off.
                          Timers are sorted by ascending start times, with the
                          first one being the next timer that will start.
diff -ruN vdr-1.3.38/Make.config.template vdr-1.3.39/Make.config.template
--- vdr-1.3.38/Make.config.template	2005-09-02 16:24:31.000000000 +0200
+++ vdr-1.3.39/Make.config.template	2006-01-13 17:06:11.000000000 +0100
@@ -6,7 +6,7 @@
 # See the main source file 'vdr.c' for copyright information and
 # how to reach the author.
 #
-# $Id: Make.config.template 1.6 2005/09/02 14:24:31 kls Exp $
+# $Id: Make.config.template 1.7 2006/01/13 16:06:11 kls Exp $
 
 ### The C compiler and options:
 
@@ -30,3 +30,6 @@
 
 LIRC_DEVICE = /dev/lircd
 RCU_DEVICE  = /dev/ttyS1
+
+## Define if you want vdr to not run as root
+#VDR_USER = vdr
diff -ruN vdr-1.3.38/Makefile vdr-1.3.39/Makefile
--- vdr-1.3.38/Makefile	2006-01-01 16:12:05.000000000 +0100
+++ vdr-1.3.39/Makefile	2006-01-13 17:04:56.000000000 +0100
@@ -4,7 +4,7 @@
 # See the main source file 'vdr.c' for copyright information and
 # how to reach the author.
 #
-# $Id: Makefile 1.81 2006/01/01 15:12:05 kls Exp $
+# $Id: Makefile 1.83 2006/01/13 16:04:56 kls Exp $
 
 .DELETE_ON_ERROR:
 
@@ -77,6 +77,9 @@
 ifdef REMOTE
 DEFINES += -DREMOTE_$(REMOTE)
 endif
+ifdef VDR_USER
+DEFINES += -DVDR_USER=\"$(VDR_USER)\"
+endif
 
 LIRC_DEVICE ?= /dev/lircd
 RCU_DEVICE  ?= /dev/ttyS1
@@ -192,7 +195,7 @@
 
 plugins-clean:
 	@for i in `ls $(PLUGINDIR)/src | grep -v '[^a-z0-9]'`; do $(MAKE) -C "$(PLUGINDIR)/src/$$i" clean; done
-	@-rm -f $(PLUGINLIBDIR)/libvdr-*.so.$(VDRVERSION)
+	@-rm -f $(PLUGINDIR)/lib/libvdr-*.so.$(VDRVERSION)
 
 # Install the files:
 
@@ -209,8 +212,8 @@
             fi
 
 plugins-install:
-	@mkdir -p $(BINDIR)/$(PLUGINLIBDIR)
-	@cp $(PLUGINLIBDIR)/* $(BINDIR)/$(PLUGINLIBDIR)
+	@mkdir -p $(PLUGINLIBDIR)
+	@cp $(PLUGINDIR)/lib/libvdr-*.so.$(VDRVERSION) $(PLUGINLIBDIR)
 
 # Source documentation:
 
diff -ruN vdr-1.3.38/README.vps vdr-1.3.39/README.vps
--- vdr-1.3.38/README.vps	2004-03-03 20:20:20.000000000 +0100
+++ vdr-1.3.39/README.vps	2006-01-09 23:06:52.000000000 +0100
@@ -134,3 +134,4 @@
 
 ARD Digital: http://www.ard-digital.de/home/index.php?id=16&languageid=1
 ZDF vision:  http://www.zdf.de/ZDFde/inhalt/1/0,1872,1021601,00.html (select "zdfvision")
+             phone: 06131/706754
diff -ruN vdr-1.3.38/channels.c vdr-1.3.39/channels.c
--- vdr-1.3.38/channels.c	2005-12-30 16:41:24.000000000 +0100
+++ vdr-1.3.39/channels.c	2006-01-14 16:51:02.000000000 +0100
@@ -4,13 +4,14 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: channels.c 1.47 2005/12/30 15:41:24 kls Exp $
+ * $Id: channels.c 1.48 2006/01/14 15:51:02 kls Exp $
  */
 
 #include "channels.h"
 #include <linux/dvb/frontend.h>
 #include <ctype.h>
 #include "device.h"
+#include "epg.h"
 
 // IMPORTANT NOTE: in the 'sscanf()' calls there is a blank after the '%d'
 // format characters in order to allow any number of blanks after a numeric
@@ -174,6 +175,7 @@
   guard        = GUARD_INTERVAL_AUTO;
   hierarchy    = HIERARCHY_AUTO;
   modification = CHANNELMOD_NONE;
+  schedule     = NULL;
   linkChannels = NULL;
   refChannel   = NULL;
 }
@@ -184,6 +186,7 @@
   shortName = NULL;
   provider = NULL;
   portalName = NULL;
+  schedule     = NULL;
   linkChannels = NULL;
   refChannel   = NULL;
   *this = Channel;
@@ -293,6 +296,7 @@
      srate = Srate;
      coderateH = CoderateH;
      modulation = QPSK;
+     schedule = NULL;
      }
   return true;
 }
@@ -310,6 +314,7 @@
      modulation = Modulation;
      srate = Srate;
      coderateH = CoderateH;
+     schedule = NULL;
      }
   return true;
 }
@@ -331,6 +336,7 @@
      coderateL = CoderateL;
      guard = Guard;
      transmission = Transmission;
+     schedule = NULL;
      }
   return true;
 }
@@ -350,6 +356,7 @@
      rid = Rid;
      if (Number())
         Channels.HashChannel(this);
+     schedule = NULL;
      }
 }
 
diff -ruN vdr-1.3.38/channels.conf vdr-1.3.39/channels.conf
--- vdr-1.3.38/channels.conf	2006-01-08 12:08:44.000000000 +0100
+++ vdr-1.3.39/channels.conf	2006-01-15 16:12:04.000000000 +0100
@@ -45,11 +45,11 @@
 rbb Berlin;ARD:12109:hC34:S19.2E:27500:601:602=deu:604:0:28206:1:1073:0
 :Premiere World
 PREMIERE START,START;PREMIERE:11797:hC34:S19.2E:27500:255:256=deu:32:1801,1722,1702:8:133:2:0
-PREMIERE 1,PREM 1;PREMIERE:11797:hC34:S19.2E:27500:511:512=deu,513=deu;515=deu:32:1801,1722,1702:10:133:2:0
+PREMIERE 1,PREM 1;PREMIERE:11797:hC34:S19.2E:27500:511:512=deu;515=deu:32:1801,1722,1702:10:133:2:0
 PREMIERE 2,PREM 2;PREMIERE:11797:hC34:S19.2E:27500:1791:1792=deu,1793=deu;1795=deu:32:1801,1722,1702:11:133:2:0
 PREMIERE 3,PREM 3;PREMIERE:11797:hC34:S19.2E:27500:2303:2304=deu,2305=deu:32:1722,1702,1801:43:133:2:0
 PREMIERE 4,PREM 4;PREMIERE:11797:hC34:S19.2E:27500:767:768=deu,769=deu:32:1801,1722,1702:9:133:2:0
-PREMIERE 5,PREM 5;PREMIERE:11797:hC34:S19.2E:27500:1279:1280=deu,1281=deu:32:1722,1702,1801:29:133:2:0
+PREMIERE 5,PREM 5;PREMIERE:11797:hC34:S19.2E:27500:1279:1280=deu:32:1722,1702,1801:29:133:2:0
 PREMIERE 6,PREM 6;PREMIERE:11797:hC34:S19.2E:27500:1535:1536=deu:32:1702,1722,1801:41:133:2:0
 PREMIERE 7,PREM 7;PREMIERE:11797:hC34:S19.2E:27500:1023:1024=deu:32:1801,1702,1722:20:133:2:0
 DISNEY CHANNEL,DISNEY;PREMIERE:11758:hC34:S19.2E:27500:2559:2560=deu:32:1801,1702,1722:34:133:17:0
@@ -57,7 +57,7 @@
 PREMIERE DIREKT,DIREKT;PREMIERE:12031:hC34:S19.2E:27500:2815:2816=deu,2817=deu;2819=deu:0:0:18:133:4:0
 :PW Erotic
 BEATE-UHSE.TV,B-UHSE;PREMIERE:11758:hC34:S19.2E:27500:1791:1792=deu:32:1722,1702,1801:21:133:17:0
-EROTIK - AB 18!,AB 18!;PREMIERE:12031:hC34:S19.2E:27500:1279:1280=deu:0:1722,1801,1702,1810:513:133:4:0
+EROTIK - AB 18!,AB 18!;PREMIERE:12031:hC34:S19.2E:27500:1279:1280=deu:0:1801,1702,1722,1810:513:133:4:0
 :Sportsworld
 PREMIERE SPORT PORTAL,SPORT PORTAL;PREMIERE:11719:hC34:S19.2E:27500:255:256=deu,257=deu:32:1722,1801,1702:17:133:3:0
 PREMIERE WIN,WIN;PREMIERE:12031:hC34:S19.2E:27500:3839:3840=deu:33:0:27:133:4:0
diff -ruN vdr-1.3.38/channels.h vdr-1.3.39/channels.h
--- vdr-1.3.38/channels.h	2006-01-07 14:00:43.000000000 +0100
+++ vdr-1.3.39/channels.h	2006-01-14 16:51:26.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: channels.h 1.37 2006/01/07 13:00:43 kls Exp $
+ * $Id: channels.h 1.38 2006/01/14 15:51:26 kls Exp $
  */
 
 #ifndef __CHANNELS_H
@@ -103,7 +103,10 @@
 class cLinkChannels : public cList<cLinkChannel> {
   };
 
+class cSchedule;
+
 class cChannel : public cListObject {
+  friend class cSchedules;
   friend class cMenuEditChannel;
 private:
   static cString ToText(const cChannel *Channel);
@@ -142,6 +145,7 @@
   int hierarchy;
   int __EndData__;
   int modification;
+  mutable const cSchedule *schedule;
   cLinkChannels *linkChannels;
   cChannel *refChannel;
   cString ParametersToString(void) const;
diff -ruN vdr-1.3.38/config.c vdr-1.3.39/config.c
--- vdr-1.3.38/config.c	2006-01-07 13:28:49.000000000 +0100
+++ vdr-1.3.39/config.c	2006-01-13 16:19:37.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: config.c 1.140 2006/01/07 12:28:49 kls Exp $
+ * $Id: config.c 1.141 2006/01/13 15:19:37 kls Exp $
  */
 
 #include "config.h"
@@ -220,6 +220,7 @@
   TimeoutRequChInfo = 1;
   MenuScrollPage = 1;
   MenuScrollWrap = 0;
+  MenuButtonCloses = 0;
   MarkInstantRecord = 1;
   strcpy(NameInstantRecord, "TITLE EPISODE");
   InstantRecordTime = 180;
@@ -379,6 +380,7 @@
   else if (!strcasecmp(Name, "TimeoutRequChInfo"))   TimeoutRequChInfo  = atoi(Value);
   else if (!strcasecmp(Name, "MenuScrollPage"))      MenuScrollPage     = atoi(Value);
   else if (!strcasecmp(Name, "MenuScrollWrap"))      MenuScrollWrap     = atoi(Value);
+  else if (!strcasecmp(Name, "MenuButtonCloses"))    MenuButtonCloses   = atoi(Value);
   else if (!strcasecmp(Name, "MarkInstantRecord"))   MarkInstantRecord  = atoi(Value);
   else if (!strcasecmp(Name, "NameInstantRecord"))   strn0cpy(NameInstantRecord, Value, MaxFileName);
   else if (!strcasecmp(Name, "InstantRecordTime"))   InstantRecordTime  = atoi(Value);
@@ -445,6 +447,7 @@
   Store("TimeoutRequChInfo",  TimeoutRequChInfo);
   Store("MenuScrollPage",     MenuScrollPage);
   Store("MenuScrollWrap",     MenuScrollWrap);
+  Store("MenuButtonCloses",   MenuButtonCloses);
   Store("MarkInstantRecord",  MarkInstantRecord);
   Store("NameInstantRecord",  NameInstantRecord);
   Store("InstantRecordTime",  InstantRecordTime);
diff -ruN vdr-1.3.38/config.h vdr-1.3.39/config.h
--- vdr-1.3.38/config.h	2006-01-07 13:57:42.000000000 +0100
+++ vdr-1.3.39/config.h	2006-01-13 16:17:19.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: config.h 1.238 2006/01/07 12:57:42 kls Exp $
+ * $Id: config.h 1.240 2006/01/13 15:17:19 kls Exp $
  */
 
 #ifndef __CONFIG_H
@@ -19,8 +19,8 @@
 #include "i18n.h"
 #include "tools.h"
 
-#define VDRVERSION  "1.3.38"
-#define VDRVERSNUM   10338  // Version * 10000 + Major * 100 + Minor
+#define VDRVERSION  "1.3.39"
+#define VDRVERSNUM   10339  // Version * 10000 + Major * 100 + Minor
 
 #define MAXPRIORITY 99
 #define MAXLIFETIME 99
@@ -193,6 +193,7 @@
   int TimeoutRequChInfo;
   int MenuScrollPage;
   int MenuScrollWrap;
+  int MenuButtonCloses;
   int MarkInstantRecord;
   char NameInstantRecord[MaxFileName];
   int InstantRecordTime;
diff -ruN vdr-1.3.38/diseqc.conf vdr-1.3.39/diseqc.conf
--- vdr-1.3.38/diseqc.conf	2004-10-23 12:06:19.000000000 +0200
+++ vdr-1.3.39/diseqc.conf	2006-01-14 12:14:09.000000000 +0100
@@ -8,7 +8,7 @@
 # slof:           switch frequency of LNB; the first entry with
 #                 an slof greater than the actual transponder
 #                 frequency will be used
-# polarization:   V = vertical, H = horizontal
+# polarization:   V = vertical, H = horizontal, L = Left circular, R = Right circular
 # lof:            the local oscillator frequency to subtract from
 #                 the actual transponder frequency
 # command:
diff -ruN vdr-1.3.38/dvbdevice.c vdr-1.3.39/dvbdevice.c
--- vdr-1.3.38/dvbdevice.c	2006-01-07 16:15:01.000000000 +0100
+++ vdr-1.3.39/dvbdevice.c	2006-01-14 16:57:36.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: dvbdevice.c 1.149 2006/01/07 15:15:01 kls Exp $
+ * $Id: dvbdevice.c 1.150 2006/01/14 15:57:36 kls Exp $
  */
 
 #include "dvbdevice.h"
@@ -314,7 +314,7 @@
                   tunerStatus = tsSet;
                   diseqcCommands = NULL;
                   if (time(NULL) - lastTimeoutReport > 60) { // let's not get too many of these
-                     esyslog("ERROR: frontend %d timed out while tuning to channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder());
+                     isyslog("frontend %d timed out while tuning to channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder());
                      lastTimeoutReport = time(NULL);
                      }
                   continue;
@@ -323,13 +323,13 @@
                if (Status & FE_REINIT) {
                   tunerStatus = tsSet;
                   diseqcCommands = NULL;
-                  esyslog("ERROR: frontend %d was reinitialized", cardIndex);
+                  isyslog("frontend %d was reinitialized", cardIndex);
                   lastTimeoutReport = 0;
                   continue;
                   }
                else if (Status & FE_HAS_LOCK) {
                   if (LostLock) {
-                     esyslog("frontend %d regained lock on channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder());
+                     isyslog("frontend %d regained lock on channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder());
                      LostLock = false;
                      }
                   tunerStatus = tsLocked;
@@ -338,7 +338,7 @@
                   }
                else if (tunerStatus == tsLocked) {
                   LostLock = true;
-                  esyslog("ERROR: frontend %d lost lock on channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder());
+                  isyslog("frontend %d lost lock on channel %d, tp %d", cardIndex, channel.Number(), channel.Transponder());
                   tunerStatus = tsTuned;
                   Timer.Set(lockTimeout);
                   lastTimeoutReport = 0;
diff -ruN vdr-1.3.38/eit.c vdr-1.3.39/eit.c
--- vdr-1.3.38/eit.c	2005-12-26 12:50:09.000000000 +0100
+++ vdr-1.3.39/eit.c	2006-01-14 16:41:21.000000000 +0100
@@ -8,7 +8,7 @@
  * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
  * Adapted to 'libsi' for VDR 1.3.0 by Marcel Wiesweg <marcel.wiesweg@gmx.de>.
  *
- * $Id: eit.c 1.113 2005/12/26 11:50:09 kls Exp $
+ * $Id: eit.c 1.114 2006/01/14 15:41:21 kls Exp $
  */
 
 #include "eit.h"
@@ -35,11 +35,7 @@
   if (!channel)
      return; // only collect data for known channels
 
-  cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channelID);
-  if (!pSchedule) {
-     pSchedule = new cSchedule(channelID);
-     Schedules->Add(pSchedule);
-     }
+  cSchedule *pSchedule = (cSchedule *)Schedules->GetSchedule(channel, true);
 
   bool Empty = true;
   bool Modified = false;
diff -ruN vdr-1.3.38/epg.c vdr-1.3.39/epg.c
--- vdr-1.3.38/epg.c	2005-12-30 16:41:59.000000000 +0100
+++ vdr-1.3.39/epg.c	2006-01-15 14:58:30.000000000 +0100
@@ -7,7 +7,7 @@
  * Original version (as used in VDR before 1.3.0) written by
  * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
  *
- * $Id: epg.c 1.47 2005/12/30 15:41:59 kls Exp $
+ * $Id: epg.c 1.49 2006/01/15 13:58:30 kls Exp $
  */
 
 #include "epg.h"
@@ -247,6 +247,8 @@
      if (components) {
         for (int i = 0; i < components->NumComponents(); i++) {
             tComponent *p = components->Component(i);
+            if (!Setup.UseDolbyDigital && p->stream == 0x02 && p->type == 0x05)
+               continue;
             fprintf(f, "%sX %s\n", Prefix, *p->ToString());
             }
         }
@@ -997,3 +999,19 @@
   return NULL;
 }
 
+const cSchedule *cSchedules::GetSchedule(const cChannel *Channel, bool AddIfMissing) const
+{
+  // This is not very beautiful, but it dramatically speeds up the
+  // "What's on now/next?" menus.
+  static cSchedule DummySchedule(tChannelID::InvalidID);
+  if (!Channel->schedule)
+     Channel->schedule = GetSchedule(Channel->GetChannelID());
+  if (!Channel->schedule)
+     Channel->schedule = &DummySchedule;
+  if (Channel->schedule == &DummySchedule && AddIfMissing) {
+     cSchedule *Schedule = new cSchedule(Channel->GetChannelID());
+     ((cSchedules *)this)->Add(Schedule);
+     Channel->schedule = Schedule;
+     }
+  return Channel->schedule != &DummySchedule? Channel->schedule : NULL;
+}
diff -ruN vdr-1.3.38/epg.h vdr-1.3.39/epg.h
--- vdr-1.3.38/epg.h	2005-12-27 15:31:24.000000000 +0100
+++ vdr-1.3.39/epg.h	2006-01-14 16:45:24.000000000 +0100
@@ -7,7 +7,7 @@
  * Original version (as used in VDR before 1.3.0) written by
  * Robert Schneider <Robert.Schneider@web.de> and Rolf Hakenes <hakenes@hippomi.de>.
  *
- * $Id: epg.h 1.28 2005/12/27 14:31:24 kls Exp $
+ * $Id: epg.h 1.29 2006/01/14 15:45:24 kls Exp $
  */
 
 #ifndef __EPG_H
@@ -179,6 +179,7 @@
   static bool Read(FILE *f = NULL);
   cSchedule *AddSchedule(tChannelID ChannelID);
   const cSchedule *GetSchedule(tChannelID ChannelID) const;
+  const cSchedule *GetSchedule(const cChannel *Channel, bool AddIfMissing = false) const;
   };
 
 void ReportEpgBugFixStats(bool Reset = false);
diff -ruN vdr-1.3.38/i18n.c vdr-1.3.39/i18n.c
--- vdr-1.3.38/i18n.c	2006-01-08 12:56:31.000000000 +0100
+++ vdr-1.3.39/i18n.c	2006-01-15 13:22:15.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: i18n.c 1.230 2006/01/08 11:56:31 kls Exp $
+ * $Id: i18n.c 1.235 2006/01/15 12:22:15 kls Exp $
  *
  * Translations provided by:
  *
@@ -441,6 +441,69 @@
     "Programm - %s",
     "Program - %s",
   },
+  { "This event - %s",
+    "Diese Sendung - %s",
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+  },
+  { "This event - all channels",
+    "Diese Sendung - alle Kanäle",
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+  },
+  { "All events - all channels",
+    "Alle Sendungen - alle Kanäle",
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+    "",//TODO
+  },
   { "What's on now?",
     "Was läuft jetzt?",
     "Kaj je na sporedu?",
@@ -610,6 +673,27 @@
     "Sees/Väljas",
     "Til/Fra",
   },
+  { "Timer",
+    "Timer",
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "Ajastin",
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+  },
   { "Button$Record",
     "Aufnehmen",
     "Posnemi",
@@ -1200,7 +1284,7 @@
     "Optagelse om %d minutter - sluk alligevel?",
   },
   { "Press any key to cancel shutdown",
-    "Taste drücken um Shutdown abzubrechen",
+    "Taste drücken, um Shutdown abzubrechen",
     "Pritisni katerikoli gumb za preklic izklopa",
     "Un tasto per annullare lo spegnimento",
     "Druk een toets om shutdown af te breken",
@@ -2260,7 +2344,7 @@
     "Espaço em disco reduzido!",
     "Disque presque plein!",
     "Lite ledig diskplass!",
-    "Kovalevy lähes täynnä!",
+    "Tallennustila loppumassa!",
     "Dysk wkrotce pelny!",
     "¡Disco casi lleno",
     "Ï óêëçñüò êïíôåýåé íÜ ãåìßóåé!",
@@ -2955,7 +3039,7 @@
     "Mostrar info ao mudar de Canal",
     "Affichage progr. en cours",
     "Info ved kanalskifte",
-    "Näytä kanavatieto",
+    "Näytä kanavatieto vaihdettaessa",
     "Informacja przy zmianie kanalu",
     "Información para cambio de canal",
     "Ðëçñïöïñßåò óôÞí áëëáãÞ êáíáëéïý",
@@ -2976,7 +3060,7 @@
     "", // TODO
     "", // TODO
     "", // TODO
-    "", // TODO
+    "Sulje kanavatieto pyydettäessä",
     "", // TODO
     "", // TODO
     "", // TODO
@@ -3031,6 +3115,27 @@
     "Ridade kerimine",
     "Scroll rundt",
   },
+  { "Setup.OSD$Menu button closes",
+    "Menu-Taste schließt",
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+    "",// TODO
+  },
   { "Setup.OSD$Sort timers",
     "Timer sortieren",
     "Sortiraj termine",
@@ -4155,7 +4260,7 @@
     "",//TODO
     "",//TODO
     "",//TODO
-    "",//TODO
+    " 0\t-.#~,/_@1\tabcäå2\tdef3\tghi4\tjkl5\tmnoö6\tpqrs7\ttuv8\twxyz9",
     "",//TODO
     "",//TODO
     "",//TODO
@@ -5397,7 +5502,7 @@
     "",//TODO
     "",//TODO
     "",//TODO
-    "",//TODO
+    "Tallennus aloitettu",
     "",//TODO
     "",//TODO
     "",//TODO
diff -ruN vdr-1.3.38/menu.c vdr-1.3.39/menu.c
--- vdr-1.3.38/menu.c	2006-01-08 12:39:57.000000000 +0100
+++ vdr-1.3.39/menu.c	2006-01-15 16:02:36.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: menu.c 1.390 2006/01/08 11:39:57 kls Exp $
+ * $Id: menu.c 1.396 2006/01/15 15:02:36 kls Exp $
  */
 
 #include "menu.h"
@@ -323,10 +323,10 @@
   cMenuChannelItem(cChannel *Channel);
   static void SetSortMode(eChannelSortMode SortMode) { sortMode = SortMode; }
   static void IncSortMode(void) { sortMode = eChannelSortMode((sortMode == csmProvider) ? csmNumber : sortMode + 1); }
+  static eChannelSortMode SortMode(void) { return sortMode; }
   virtual int Compare(const cListObject &ListObject) const;
   virtual void Set(void);
   cChannel *Channel(void) { return channel; }
-  static eChannelSortMode SortMode(void) { return sortMode; }
   };
 
 cMenuChannelItem::eChannelSortMode cMenuChannelItem::sortMode = csmNumber;
@@ -916,7 +916,7 @@
         SetTitle(channel->Name());
         int TimerMatch = tmNone;
         Timers.GetMatch(event, &TimerMatch);
-        SetHelp(TimerMatch == tmFull ? tr("Timer") : tr("Button$Record"), NULL, NULL, CanSwitch ? tr("Button$Switch") : NULL);
+        SetHelp(TimerMatch == tmFull ? tr("Button$Timer") : tr("Button$Record"), NULL, NULL, CanSwitch ? tr("Button$Switch") : NULL);
         }
      }
 }
@@ -962,21 +962,44 @@
 
 class cMenuScheduleItem : public cOsdItem {
 public:
+  enum eScheduleSortMode { ssmAllThis, ssmThisThis, ssmThisAll, ssmAllAll }; // "which event(s) on which channel(s)"
+private:
+  static eScheduleSortMode sortMode;
+public:
   const cEvent *event;
   const cChannel *channel;
+  bool withDate;
   int timerMatch;
-  cMenuScheduleItem(const cEvent *Event, cChannel *Channel = NULL);
+  cMenuScheduleItem(const cEvent *Event, cChannel *Channel = NULL, bool WithDate = false);
+  static void SetSortMode(eScheduleSortMode SortMode) { sortMode = SortMode; }
+  static void IncSortMode(void) { sortMode = eScheduleSortMode((sortMode == ssmAllAll) ? ssmAllThis : sortMode + 1); }
+  static eScheduleSortMode SortMode(void) { return sortMode; }
+  virtual int Compare(const cListObject &ListObject) const;
   bool Update(bool Force = false);
-};
+  };
+
+cMenuScheduleItem::eScheduleSortMode cMenuScheduleItem::sortMode = ssmAllThis;
 
-cMenuScheduleItem::cMenuScheduleItem(const cEvent *Event, cChannel *Channel)
+cMenuScheduleItem::cMenuScheduleItem(const cEvent *Event, cChannel *Channel, bool WithDate)
 {
   event = Event;
   channel = Channel;
+  withDate = WithDate;
   timerMatch = tmNone;
   Update(true);
 }
 
+int cMenuScheduleItem::Compare(const cListObject &ListObject) const
+{
+  cMenuScheduleItem *p = (cMenuScheduleItem *)&ListObject;
+  int r = -1;
+  if (sortMode != ssmAllThis)
+     r = strcoll(event->Title(), p->event->Title());
+  if (sortMode == ssmAllThis || r == 0)
+     r = event->StartTime() - p->event->StartTime();
+  return r;
+}
+
 static char *TimerMatchChars = " tT";
 
 bool cMenuScheduleItem::Update(bool Force)
@@ -989,7 +1012,9 @@
      char t = TimerMatchChars[timerMatch];
      char v = event->Vps() && (event->Vps() - event->StartTime()) ? 'V' : ' ';
      char r = event->IsRunning() ? '*' : ' ';
-     if (channel)
+     if (channel && withDate)
+        asprintf(&buffer, "%d\t%.*s\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), 6, channel->ShortName(true), 6, *event->GetDateString(), *event->GetTimeString(), t, v, r, event->Title());
+     else if (channel)
         asprintf(&buffer, "%d\t%.*s\t%s\t%c%c%c\t%s", channel->Number(), 6, channel->ShortName(true), *event->GetTimeString(), t, v, r, event->Title());
      else
         asprintf(&buffer, "%.*s\t%s\t%c%c%c\t%s", 6, *event->GetDateString(), *event->GetTimeString(), t, v, r, event->Title());
@@ -1005,6 +1030,7 @@
 private:
   bool now;
   int helpKeys;
+  int timerState;
   eOSState Record(void);
   eOSState Switch(void);
   static int currentChannel;
@@ -1027,9 +1053,11 @@
 {
   now = Now;
   helpKeys = -1;
+  timerState = 0;
+  Timers.Modified(timerState);
   for (cChannel *Channel = Channels.First(); Channel; Channel = Channels.Next(Channel)) {
       if (!Channel->GroupSep()) {
-         const cSchedule *Schedule = Schedules->GetSchedule(Channel->GetChannelID());
+         const cSchedule *Schedule = Schedules->GetSchedule(Channel);
          if (Schedule) {
             const cEvent *Event = Now ? Schedule->GetPresentEvent() : Schedule->GetFollowingEvent();
             if (Event)
@@ -1044,10 +1072,12 @@
 bool cMenuWhatsOn::Update(void)
 {
   bool result = false;
-  for (cOsdItem *item = First(); item; item = Next(item)) {
-      if (((cMenuScheduleItem *)item)->Update())
-         result = true;
-      }
+  if (Timers.Modified(timerState)) {
+     for (cOsdItem *item = First(); item; item = Next(item)) {
+         if (((cMenuScheduleItem *)item)->Update())
+            result = true;
+         }
+     }
   return result;
 }
 
@@ -1163,9 +1193,14 @@
   bool now, next;
   int otherChannel;
   int helpKeys;
+  int timerState;
+  eOSState Number(void);
   eOSState Record(void);
   eOSState Switch(void);
-  void PrepareSchedule(cChannel *Channel);
+  void PrepareScheduleAllThis(const cEvent *Event, const cChannel *Channel);
+  void PrepareScheduleThisThis(const cEvent *Event, const cChannel *Channel);
+  void PrepareScheduleThisAll(const cEvent *Event, const cChannel *Channel);
+  void PrepareScheduleAllAll(const cEvent *Event, const cChannel *Channel);
   bool Update(void);
   void SetHelpKeys(void);
 public:
@@ -1175,16 +1210,19 @@
   };
 
 cMenuSchedule::cMenuSchedule(void)
-:cOsdMenu("", 7, 6, 4)
+:cOsdMenu("")
 {
   now = next = false;
   otherChannel = 0;
   helpKeys = -1;
+  timerState = 0;
+  Timers.Modified(timerState);
+  cMenuScheduleItem::SetSortMode(cMenuScheduleItem::ssmAllThis);
   cChannel *channel = Channels.GetByNumber(cDevice::CurrentChannel());
   if (channel) {
      cMenuWhatsOn::SetCurrentChannel(channel->Number());
      schedules = cSchedules::Schedules(schedulesLock);
-     PrepareSchedule(channel);
+     PrepareScheduleAllThis(NULL, channel);
      SetHelpKeys();
      }
 }
@@ -1194,33 +1232,94 @@
   cMenuWhatsOn::ScheduleEvent(); // makes sure any posted data is cleared
 }
 
-void cMenuSchedule::PrepareSchedule(cChannel *Channel)
+void cMenuSchedule::PrepareScheduleAllThis(const cEvent *Event, const cChannel *Channel)
 {
   Clear();
+  SetCols(7, 6, 4);
   char *buffer = NULL;
   asprintf(&buffer, tr("Schedule - %s"), Channel->Name());
   SetTitle(buffer);
   free(buffer);
-  if (schedules) {
-     const cSchedule *Schedule = schedules->GetSchedule(Channel->GetChannelID());
+  if (schedules && Channel) {
+     const cSchedule *Schedule = schedules->GetSchedule(Channel);
+     if (Schedule) {
+        const cEvent *PresentEvent = Event ? Event : Schedule->GetPresentEvent(Channel->Number() == cDevice::CurrentChannel());
+        time_t now = time(NULL) - Setup.EPGLinger * 60;
+        for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) {
+            if (ev->EndTime() > now || ev == PresentEvent)
+               Add(new cMenuScheduleItem(ev), ev == PresentEvent);
+            }
+        }
+     }
+}
+
+void cMenuSchedule::PrepareScheduleThisThis(const cEvent *Event, const cChannel *Channel)
+{
+  Clear();
+  SetCols(7, 6, 4);
+  char *buffer = NULL;
+  asprintf(&buffer, tr("This event - %s"), Channel->Name());
+  SetTitle(buffer);
+  free(buffer);
+  if (schedules && Channel && Event) {
+     const cSchedule *Schedule = schedules->GetSchedule(Channel);
      if (Schedule) {
-        const cEvent *PresentEvent = Schedule->GetPresentEvent(Channel->Number() == cDevice::CurrentChannel());
         time_t now = time(NULL) - Setup.EPGLinger * 60;
-        for (const cEvent *Event = Schedule->Events()->First(); Event; Event = Schedule->Events()->Next(Event)) {
-            if (Event->EndTime() > now || Event == PresentEvent)
-               Add(new cMenuScheduleItem(Event), Event == PresentEvent);
+        for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) {
+            if ((ev->EndTime() > now || ev == Event) && !strcmp(ev->Title(), Event->Title()))
+               Add(new cMenuScheduleItem(ev), ev == Event);
             }
         }
      }
 }
 
+void cMenuSchedule::PrepareScheduleThisAll(const cEvent *Event, const cChannel *Channel)
+{
+  Clear();
+  SetCols(CHNUMWIDTH, 7, 7, 6, 4);
+  SetTitle(tr("This event - all channels"));
+  if (schedules && Event) {
+     for (cChannel *ch = Channels.First(); ch; ch = Channels.Next(ch)) {
+         const cSchedule *Schedule = schedules->GetSchedule(ch);
+         if (Schedule) {
+            time_t now = time(NULL) - Setup.EPGLinger * 60;
+            for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) {
+                if ((ev->EndTime() > now || ev == Event) && !strcmp(ev->Title(), Event->Title()))
+                   Add(new cMenuScheduleItem(ev, ch, true), ev == Event && ch == Channel);
+                }
+            }
+         }
+     }
+}
+
+void cMenuSchedule::PrepareScheduleAllAll(const cEvent *Event, const cChannel *Channel)
+{
+  Clear();
+  SetCols(CHNUMWIDTH, 7, 7, 6, 4);
+  SetTitle(tr("All events - all channels"));
+  if (schedules) {
+     for (cChannel *ch = Channels.First(); ch; ch = Channels.Next(ch)) {
+         const cSchedule *Schedule = schedules->GetSchedule(ch);
+         if (Schedule) {
+            time_t now = time(NULL) - Setup.EPGLinger * 60;
+            for (const cEvent *ev = Schedule->Events()->First(); ev; ev = Schedule->Events()->Next(ev)) {
+                if (ev->EndTime() > now || ev == Event)
+                   Add(new cMenuScheduleItem(ev, ch, true), ev == Event && ch == Channel);
+                }
+            }
+         }
+     }
+}
+
 bool cMenuSchedule::Update(void)
 {
   bool result = false;
-  for (cOsdItem *item = First(); item; item = Next(item)) {
-      if (((cMenuScheduleItem *)item)->Update())
-         result = true;
-      }
+  if (Timers.Modified(timerState)) {
+     for (cOsdItem *item = First(); item; item = Next(item)) {
+         if (((cMenuScheduleItem *)item)->Update())
+            result = true;
+         }
+     }
   return result;
 }
 
@@ -1241,6 +1340,29 @@
      }
 }
 
+eOSState cMenuSchedule::Number(void)
+{
+  cMenuScheduleItem::IncSortMode();
+  cMenuScheduleItem *CurrentItem = (cMenuScheduleItem *)Get(Current());
+  const cChannel *Channel = NULL;
+  const cEvent *Event = NULL;
+  if (CurrentItem) {
+     Event = CurrentItem->event;
+     Channel = Channels.GetByChannelID(Event->ChannelID(), true);
+     }
+  switch (cMenuScheduleItem::SortMode()) {
+    case cMenuScheduleItem::ssmAllThis:  PrepareScheduleAllThis(Event, Channel); break;
+    case cMenuScheduleItem::ssmThisThis: PrepareScheduleThisThis(Event, Channel); break;
+    case cMenuScheduleItem::ssmThisAll:  PrepareScheduleThisAll(Event, Channel); break;
+    case cMenuScheduleItem::ssmAllAll:   PrepareScheduleAllAll(Event, Channel); break;
+    }
+  CurrentItem = (cMenuScheduleItem *)Get(Current());
+  Sort();
+  SetCurrent(CurrentItem);
+  Display();
+  return osContinue;
+}
+
 eOSState cMenuSchedule::Record(void)
 {
   cMenuScheduleItem *item = (cMenuScheduleItem *)Get(Current());
@@ -1290,6 +1412,7 @@
 
   if (state == osUnknown) {
      switch (Key) {
+       case k0:      return Number();
        case kRecord:
        case kRed:    return Record();
        case kGreen:  if (schedules) {
@@ -1325,7 +1448,8 @@
      if (ei) {
         cChannel *channel = Channels.GetByChannelID(ei->ChannelID(), true);
         if (channel) {
-           PrepareSchedule(channel);
+           cMenuScheduleItem::SetSortMode(cMenuScheduleItem::ssmAllThis);
+           PrepareScheduleAllThis(NULL, channel);
            if (channel->Number() != cDevice::CurrentChannel()) {
               otherChannel = channel->Number();
               SetHelp(Count() ? tr("Button$Record") : NULL, tr("Button$Now"), tr("Button$Next"), tr("Button$Switch"));
@@ -1578,17 +1702,19 @@
 class cMenuRecording : public cOsdMenu {
 private:
   const cRecording *recording;
+  bool withButtons;
 public:
-  cMenuRecording(const cRecording *Recording);
+  cMenuRecording(const cRecording *Recording, bool WithButtons = false);
   virtual void Display(void);
   virtual eOSState ProcessKey(eKeys Key);
 };
 
-cMenuRecording::cMenuRecording(const cRecording *Recording)
+cMenuRecording::cMenuRecording(const cRecording *Recording, bool WithButtons)
 :cOsdMenu(tr("Recording info"))
 {
   recording = Recording;
-  if (recording)
+  withButtons = WithButtons;
+  if (withButtons)
      SetHelp(tr("Button$Play"), tr("Button$Rewind"));
 }
 
@@ -1620,8 +1746,11 @@
 
   if (state == osUnknown) {
      switch (Key) {
-       case kRed:    Key = kOk; // will play the recording, even if recording commands are defined
-       case kGreen:  cRemote::Put(Key, true);
+       case kRed:    if (withButtons)
+                        Key = kOk; // will play the recording, even if recording commands are defined
+       case kGreen:  if (!withButtons)
+                        break;
+                     cRemote::Put(Key, true);
                      // continue with osBack to close the info menu and process the key
        case kOk:     return osBack;
        default: break;
@@ -1869,7 +1998,7 @@
   if (ri && !ri->IsDirectory()) {
      cRecording *recording = GetRecording(ri);
      if (recording && recording->Info()->Title())
-        return AddSubMenu(new cMenuRecording(recording));
+        return AddSubMenu(new cMenuRecording(recording, true));
      }
   return osContinue;
 }
@@ -2007,6 +2136,7 @@
   Add(new cMenuEditBoolItem(tr("Setup.OSD$Timeout requested channel info"), &data.TimeoutRequChInfo));
   Add(new cMenuEditBoolItem(tr("Setup.OSD$Scroll pages"),           &data.MenuScrollPage));
   Add(new cMenuEditBoolItem(tr("Setup.OSD$Scroll wraps"),           &data.MenuScrollWrap));
+  Add(new cMenuEditBoolItem(tr("Setup.OSD$Menu button closes"),     &data.MenuButtonCloses));
   Add(new cMenuEditBoolItem(tr("Setup.OSD$Sort timers"),            &data.SortTimers));
   Add(new cMenuEditBoolItem(tr("Setup.OSD$Recording directories"),  &data.RecordingDirs));
   SetCurrent(Get(current));
@@ -2832,7 +2962,7 @@
      if (Channel) {
         const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
         if (Schedules) {
-           const cSchedule *Schedule = Schedules->GetSchedule(Channel->GetChannelID());
+           const cSchedule *Schedule = Schedules->GetSchedule(Channel);
            if (Schedule) {
               const cEvent *Present = Schedule->GetPresentEvent(true);
               if (Present)
@@ -2915,7 +3045,7 @@
      cSchedulesLock SchedulesLock;
      const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
      if (Schedules) {
-        const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
+        const cSchedule *Schedule = Schedules->GetSchedule(channel);
         if (Schedule) {
            const cEvent *Present = Schedule->GetPresentEvent(true);
            const cEvent *Following = Schedule->GetFollowingEvent(true);
@@ -3345,7 +3475,7 @@
         cSchedulesLock SchedulesLock;
         const cSchedules *Schedules = cSchedules::Schedules(SchedulesLock);
         if (Schedules) {
-           const cSchedule *Schedule = Schedules->GetSchedule(channel->GetChannelID());
+           const cSchedule *Schedule = Schedules->GetSchedule(channel);
            if (Schedule) {
               event = Schedule->GetEventAround(Time);
               if (event) {
@@ -3868,7 +3998,7 @@
 {
   cRecording *Recording = Recordings.GetByName(cReplayControl::LastReplayed());
   if (Recording)
-     return new cMenuRecording(Recording);
+     return new cMenuRecording(Recording, false);
   return NULL;
 }
 
diff -ruN vdr-1.3.38/remote.c vdr-1.3.39/remote.c
--- vdr-1.3.38/remote.c	2006-01-01 15:21:07.000000000 +0100
+++ vdr-1.3.39/remote.c	2006-01-15 16:17:40.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: remote.c 1.46 2006/01/01 14:21:07 kls Exp $
+ * $Id: remote.c 1.48 2006/01/15 15:17:40 kls Exp $
  */
 
 #include "remote.h"
@@ -301,12 +301,13 @@
                       k |= key1 & 0xFF;
                       switch (key1) {
                         case 0x31 ... 0x3F: // more-byte sequence
-                             while (key1 != 0x7E) {
-                                   k <<= 8;
-                                   k |= key1 & 0xFF;
-                                   if ((key1 = ReadKey()) < 0)
-                                      break; // Sequence ends here
-                                   }
+                        case 0x5B: // strange, may apparently occur
+                             do {
+                                if ((key1 = ReadKey()) < 0)
+                                   break; // Sequence ends here
+                                k <<= 8;
+                                k |= key1 & 0xFF;
+                                } while (key1 != 0x7E);
                              break;
                         }
                       }
diff -ruN vdr-1.3.38/svdrp.c vdr-1.3.39/svdrp.c
--- vdr-1.3.38/svdrp.c	2005-12-30 16:42:29.000000000 +0100
+++ vdr-1.3.39/svdrp.c	2006-01-14 17:08:20.000000000 +0100
@@ -10,7 +10,7 @@
  * and interact with the Video Disk Recorder - or write a full featured
  * graphical interface that sits on top of an SVDRP connection.
  *
- * $Id: svdrp.c 1.89 2005/12/30 15:42:29 kls Exp $
+ * $Id: svdrp.c 1.93 2006/01/14 16:08:20 kls Exp $
  */
 
 #include "svdrp.h"
@@ -229,9 +229,11 @@
   "LSTR [ <number> ]\n"
   "    List recordings. Without option, all recordings are listed. Otherwise\n"
   "    the information for the given recording is listed.",
-  "LSTT [ <number> ]\n"
+  "LSTT [ <number> ] [ id ]\n"
   "    List timers. Without option, all timers are listed. Otherwise\n"
-  "    only the given timer is listed.",
+  "    only the given timer is listed. If the keyword 'id' is given, the\n"
+  "    channels will be listed with their unique channel ids instead of\n"
+  "    their numbers.",
   "MESG <message>\n"
   "    Displays the given message on the OSD. The message will be queued\n"
   "    and displayed whenever this is suitable.\n",
@@ -681,10 +683,6 @@
         }
      else if (strcmp(FileName, "-") == 0)
         FileName = NULL;
-     else {
-        Reply(501, "Missing filename extension in \"%s\"", FileName);
-        return;
-        }
      // image quality (and obsolete type):
      if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
         if (strcasecmp(p, "JPEG") == 0 || strcasecmp(p, "PNM") == 0) {
@@ -729,10 +727,13 @@
      char RealFileName[PATH_MAX];
      if (FileName) {
         if (grabImageDir) {
-           char *s;
-           asprintf(&s, "%s/%s", grabImageDir, FileName);
-           FileName = s;
-           char *slash = strrchr(FileName, '/'); // there definitely is one
+           char *s = 0;
+           char *slash = strrchr(FileName, '/');
+           if (!slash) {
+              asprintf(&s, "%s/%s", grabImageDir, FileName);
+              FileName = s;
+              }
+           slash = strrchr(FileName, '/'); // there definitely is one
            *slash = 0;
            char *r = realpath(FileName, RealFileName);
            *slash = '/';
@@ -931,7 +932,7 @@
                  else
                     Channel = Channels.GetByChannelID(tChannelID::FromString(Option));
                  if (Channel) {
-                    Schedule = Schedules->GetSchedule(Channel->GetChannelID());
+                    Schedule = Schedules->GetSchedule(Channel);
                     if (!Schedule) {
                        Reply(550, "No schedule found");
                        return;
@@ -1009,22 +1010,38 @@
 
 void cSVDRP::CmdLSTT(const char *Option)
 {
+  int Number = 0;
+  bool Id = false;
   if (*Option) {
-     if (isnumber(Option)) {
-        cTimer *timer = Timers.Get(strtol(Option, NULL, 10) - 1);
-        if (timer)
-           Reply(250, "%d %s", timer->Index() + 1, *timer->ToText());
-        else
-           Reply(501, "Timer \"%s\" not defined", Option);
-        }
+     char buf[strlen(Option) + 1];
+     strcpy(buf, Option);
+     const char *delim = " \t";
+     char *strtok_next;
+     char *p = strtok_r(buf, delim, &strtok_next);
+     while (p) {
+           if (isnumber(p))
+              Number = strtol(p, NULL, 10);
+           else if (strcasecmp(p, "ID") == 0)
+              Id = true;
+           else {
+              Reply(501, "Unknown option: \"%s\"", p);
+              return;
+              }
+           p = strtok_r(NULL, delim, &strtok_next);
+           }
+     }
+  if (Number) {
+     cTimer *timer = Timers.Get(Number - 1);
+     if (timer)
+        Reply(250, "%d %s", timer->Index() + 1, *timer->ToText(Id));
      else
-        Reply(501, "Error in timer number \"%s\"", Option);
+        Reply(501, "Timer \"%s\" not defined", Option);
      }
   else if (Timers.Count()) {
      for (int i = 0; i < Timers.Count(); i++) {
          cTimer *timer = Timers.Get(i);
         if (timer)
-           Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, *timer->ToText());
+           Reply(i < Timers.Count() - 1 ? -250 : 250, "%d %s", timer->Index() + 1, *timer->ToText(Id));
         else
            Reply(501, "Timer \"%d\" not found", i + 1);
          }
diff -ruN vdr-1.3.38/thread.c vdr-1.3.39/thread.c
--- vdr-1.3.38/thread.c	2006-01-04 16:01:22.000000000 +0100
+++ vdr-1.3.39/thread.c	2006-01-08 17:03:56.000000000 +0100
@@ -4,11 +4,12 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: thread.c 1.50 2006/01/04 15:01:22 kls Exp $
+ * $Id: thread.c 1.51 2006/01/08 16:03:56 kls Exp $
  */
 
 #include "thread.h"
 #include <errno.h>
+#include <linux/unistd.h>
 #include <malloc.h>
 #include <stdarg.h>
 #include <sys/resource.h>
diff -ruN vdr-1.3.38/timers.c vdr-1.3.39/timers.c
--- vdr-1.3.38/timers.c	2006-01-08 12:40:29.000000000 +0100
+++ vdr-1.3.39/timers.c	2006-01-15 14:31:11.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: timers.c 1.41 2006/01/08 11:40:29 kls Exp $
+ * $Id: timers.c 1.43 2006/01/15 13:31:11 kls Exp $
  */
 
 #include "timers.h"
@@ -515,7 +515,7 @@
 
 cTimers::cTimers(void)
 {
-  modified = false;
+  state = 0;
   beingEdited = 0;;
   lastSetEvents = 0;
 }
@@ -574,13 +574,13 @@
 
 void cTimers::SetModified(void)
 {
-  modified = true;
+  state++;
 }
 
-bool cTimers::Modified(void)
+bool cTimers::Modified(int &State)
 {
-  bool Result = modified;
-  modified = false;
+  bool Result = state != State;
+  State = state;
   return Result;
 }
 
@@ -596,7 +596,7 @@
   if (Schedules) {
      if (!lastSetEvents || Schedules->Modified() >= lastSetEvents) {
         for (cTimer *ti = First(); ti; ti = Next(ti)) {
-            const cSchedule *Schedule = Schedules->GetSchedule(ti->Channel()->GetChannelID());
+            const cSchedule *Schedule = Schedules->GetSchedule(ti->Channel());
             if (Schedule) {
                if (!lastSetEvents || Schedule->Modified() >= lastSetEvents) {
                   const cEvent *Event = NULL;
diff -ruN vdr-1.3.38/timers.h vdr-1.3.39/timers.h
--- vdr-1.3.38/timers.h	2006-01-06 15:13:17.000000000 +0100
+++ vdr-1.3.39/timers.h	2006-01-15 14:29:44.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: timers.h 1.23 2006/01/06 14:13:17 kls Exp $
+ * $Id: timers.h 1.24 2006/01/15 13:29:44 kls Exp $
  */
 
 #ifndef __TIMERS_H
@@ -96,7 +96,7 @@
 
 class cTimers : public cConfig<cTimer> {
 private:
-  bool modified;
+  int state;
   int beingEdited;
   time_t lastSetEvents;
 public:
@@ -109,9 +109,10 @@
   void IncBeingEdited(void) { beingEdited++; }
   void DecBeingEdited(void) { if (!--beingEdited) lastSetEvents = 0; }
   void SetModified(void);
-  bool Modified(void);
-      ///< Returns true if any of the timers have been modified.
-      ///< Calling this function resets the 'modified' flag to false.
+  bool Modified(int &State);
+      ///< Returns true if any of the timers have been modified, which
+      ///< is detected by State being different than the internal state.
+      ///< Upon return the internal state will be stored in State.
   void SetEvents(void);
   void DeleteExpired(void);
   };
diff -ruN vdr-1.3.38/tools.c vdr-1.3.39/tools.c
--- vdr-1.3.38/tools.c	2006-01-08 12:40:35.000000000 +0100
+++ vdr-1.3.39/tools.c	2006-01-15 15:31:45.000000000 +0100
@@ -4,7 +4,7 @@
  * See the main source file 'vdr.c' for copyright information and
  * how to reach the author.
  *
- * $Id: tools.c 1.109 2006/01/08 11:40:35 kls Exp $
+ * $Id: tools.c 1.110 2006/01/15 14:31:45 kls Exp $
  */
 
 #include "tools.h"
@@ -625,7 +625,7 @@
      time(&t);
   struct tm tm_r;
   tm *tm = localtime_r(&t, &tm_r);
-  snprintf(buffer, sizeof(buffer), "%s %2d.%02d %02d:%02d", *WeekDayName(tm->tm_wday), tm->tm_mday, tm->tm_mon + 1, tm->tm_hour, tm->tm_min);
+  snprintf(buffer, sizeof(buffer), "%s %02d.%02d %02d:%02d", *WeekDayName(tm->tm_wday), tm->tm_mday, tm->tm_mon + 1, tm->tm_hour, tm->tm_min);
   return buffer;
 }
 
diff -ruN vdr-1.3.38/vdr.1 vdr-1.3.39/vdr.1
--- vdr-1.3.38/vdr.1	2006-01-08 12:51:36.000000000 +0100
+++ vdr-1.3.39/vdr.1	2006-01-14 12:09:08.000000000 +0100
@@ -8,7 +8,7 @@
 .\" License as specified in the file COPYING that comes with the
 .\" vdr distribution.
 .\"
-.\" $Id: vdr.1 1.20 2006/01/08 11:51:36 kls Exp $
+.\" $Id: vdr.1 1.22 2006/01/14 11:09:08 kls Exp $
 .\"
 .TH vdr 1 "08 Jan 2006" "1.3.38" "Video Disk Recorder"
 .SH NAME
@@ -132,8 +132,8 @@
 Run as user \fIuser\fR in case vdr was started as user 'root'.
 Starting vdr as 'root' is necessary if the system time shall
 be set from the transponder data, but for security reasons
-during normal operation vdr switches to a lesser privileged
-user id. By default the user 'vdr' is used.
+vdr can switch to a lesser privileged user id during normal
+operation.
 .TP
 .BI \-v\  dir ,\ \-\-video= dir
 Use \fIdir\fR as video directory.
@@ -161,9 +161,6 @@
 .I channels.conf
 Channel configuration.
 .TP
-.I ca.conf
-Conditional access configuration.
-.TP
 .I timers.conf
 Timer configuration.
 .TP
diff -ruN vdr-1.3.38/vdr.5 vdr-1.3.39/vdr.5
--- vdr-1.3.38/vdr.5	2006-01-08 12:52:03.000000000 +0100
+++ vdr-1.3.39/vdr.5	2006-01-14 11:57:37.000000000 +0100
@@ -8,7 +8,7 @@
 .\" License as specified in the file COPYING that comes with the
 .\" vdr distribution.
 .\"
-.\" $Id: vdr.5 1.45 2006/01/08 11:52:03 kls Exp $
+.\" $Id: vdr.5 1.47 2006/01/14 10:57:37 kls Exp $
 .\"
 .TH vdr 5 "08 Jan 2006" "1.3.38" "Video Disk Recorder Files"
 .SH NAME
@@ -407,16 +407,15 @@
 \fBmacrokey  [@plugin] key1 key2 key3...\fR
 
 where \fBmacrokey\fR is the key that shall initiate execution of this macro
-and can be one of \fIRed\fR, \fIGreen\fR, \fIYellow\fR, \fIBlue\fR or
-\fIUser1\fR...\fIUser9\fR. The rest of the line consists of a set of
+and can be one of \fIUp\fR, \fIDown\fR, \fIOk\fR, \fIBack\fR, \fILeft\fR,
+\fIRight\fR, \fIRed\fR, \fIGreen\fR, \fIYellow\fR, \fIBlue\fR, \fI0\fR...\fI9\fR
+or \fIUser1\fR...\fIUser9\fR. The rest of the line consists of a set of
 keys, which will be executed just as if they had been pressed in the given
 sequence. The optional \fB@plugin\fR can be used to automatically select
-the given plugin from the main menu (provided that plugin has a main menu
-entry). \fBplugin\fR is the name of the plugin, exactly as given in the \-P
-option when starting VDR. There can be only one \fB@plugin\fR per key macro,
-and it implicitly adds an \fIOk\fR key to the macro definition (in order to
-actually select the plugins main menu entry), which counts against the total
-number of keys in the macro. For instance
+the given plugin.
+\fBplugin\fR is the name of the plugin, exactly as given in the \-P
+option when starting VDR. There can be only one \fB@plugin\fR per key macro.
+For instance
 
 \fBUser1 @abc Down Down Ok\fR
 
diff -ruN vdr-1.3.38/vdr.c vdr-1.3.39/vdr.c
--- vdr-1.3.38/vdr.c	2006-01-08 12:49:03.000000000 +0100
+++ vdr-1.3.39/vdr.c	2006-01-15 14:31:57.000000000 +0100
@@ -22,7 +22,7 @@
  *
  * The project's page is at http://www.cadsoft.de/vdr
  *
- * $Id: vdr.c 1.233 2006/01/08 11:49:03 kls Exp $
+ * $Id: vdr.c 1.238 2006/01/15 13:31:57 kls Exp $
  */
 
 #include <getopt.h>
@@ -157,14 +157,13 @@
 
   // Command line options:
 
-#define DEFAULTVDRUSER   "vdr"
 #define DEFAULTSVDRPPORT 2001
 #define DEFAULTWATCHDOG     0 // seconds
 #define DEFAULTPLUGINDIR PLUGINDIR
 #define DEFAULTEPGDATAFILENAME "epg.data"
 
   bool StartedAsRoot = false;
-  const char *VdrUser = DEFAULTVDRUSER;
+  const char *VdrUser = NULL;
   int SVDRPport = DEFAULTSVDRPPORT;
   const char *AudioCommand = NULL;
   const char *ConfigDirectory = NULL;
@@ -192,6 +191,9 @@
 #if defined(VFAT)
   VfatFileSystem = true;
 #endif
+#if defined(VDR_USER)
+  VdrUser = VDR_USER;
+#endif
 
   cPluginManager PluginManager(DEFAULTPLUGINDIR);
   int ExitCode = 0;
@@ -337,16 +339,18 @@
 
   // Set user id in case we were started as root:
 
-  if (getuid() == 0) {
+  if (VdrUser && getuid() == 0) {
      StartedAsRoot = true;
-     if (!SetKeepCaps(true))
-        return 2;
-     if (!SetUser(VdrUser))
-        return 2;
-     if (!SetKeepCaps(false))
-        return 2;
-     if (!SetCapSysTime())
-        return 2;
+     if (strcmp(VdrUser, "root")) {
+        if (!SetKeepCaps(true))
+           return 2;
+        if (!SetUser(VdrUser))
+           return 2;
+        if (!SetKeepCaps(false))
+           return 2;
+        if (!SetCapSysTime())
+           return 2;
+        }
      }
 
   // Help and version info:
@@ -392,8 +396,8 @@
                "  -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"
-               "  -u USER,  --user=USER    run as user USER (default: %s); only applicable\n"
-               "                           if started as root\n"
+               "  -u USER,  --user=USER    run as user USER; only applicable if started as\n"
+               "                           root\n"
                "  -v DIR,   --video=DIR    use DIR as video directory (default: %s)\n"
                "  -V,       --version      print version information and exit\n"
                "            --vfat         encode special characters in recording names to\n"
@@ -406,7 +410,6 @@
                LIRC_DEVICE,
                DEFAULTSVDRPPORT,
                RCU_DEVICE,
-               DEFAULTVDRUSER,
                VideoDirectory,
                DEFAULTWATCHDOG
                );
@@ -471,7 +474,7 @@
      }
 
   isyslog("VDR version %s started", VDRVERSION);
-  if (StartedAsRoot)
+  if (StartedAsRoot && VdrUser)
      isyslog("switched to user '%s'", VdrUser);
   if (DaemonMode)
      dsyslog("running as daemon (tid=%d)", cThread::ThreadId());
@@ -687,9 +690,10 @@
         if (!Channels.BeingEdited() && !Timers.BeingEdited()) {
            int modified = Channels.Modified();
            static time_t ChannelSaveTimeout = 0;
+           static int TimerState = 0;
            // Channels and timers need to be stored in a consistent manner,
            // therefore if one of them is changed, we save both.
-           if (modified == CHANNELSMOD_USER || Timers.Modified())
+           if (modified == CHANNELSMOD_USER || Timers.Modified(TimerState))
               ChannelSaveTimeout = 1; // triggers an immediate save
            else if (modified && !ChannelSaveTimeout)
               ChannelSaveTimeout = time(NULL) + CHANNELSAVEDELTA;
@@ -785,14 +789,17 @@
         // Keys that must work independent of any interactive mode:
         switch (key) {
           // Menu control:
-          case kMenu:
+          case kMenu: {
                key = kNone; // nobody else needs to see this key
+               bool WasOpen = Interact != NULL;
+               bool WasMenu = Interact && Interact->IsMenu();
                if (Menu)
                   DELETE_MENU;
                else if (cControl::Control() && cOsd::IsOpen())
                   cControl::Control()->Hide();
-               else
+               if (!WasOpen || !WasMenu && !Setup.MenuButtonCloses)
                   Menu = new cMenuMain;
+               }
                break;
           // Info:
           case kInfo: {
@@ -978,6 +985,10 @@
            }
         else {
            // Key functions in "normal" viewing mode:
+           if (KeyMacros.Get(key)) {
+              cRemote::PutMacro(key);
+              key = kNone;
+              }
            switch (key) {
              // Toggle channels:
              case k0: {
@@ -1013,11 +1024,6 @@
                      cControl::Launch(new cReplayControl);
                      }
                   break;
-             // Key macros:
-             case kRed:
-             case kGreen:
-             case kYellow:
-             case kBlue: cRemote::PutMacro(key); break;
              default:    break;
              }
            }
