From a96f84c4571151396e84628349bde457758d89a0 Mon Sep 17 00:00:00 2001
From: Chris Tallon <chris@vomp.tv>
Date: Thu, 20 Feb 2020 23:29:36 +0000
Subject: [PATCH] Rewrite timers class using std::thread/mutex/cond/chrono

---
 CREDITS           |   1 +
 audioplayer.cc    |   7 +-
 audioplayer.h     |   6 +-
 command.cc        |   1 -
 main.cc           |   2 +-
 playermedia.cc    |   1 -
 playermedia.h     |   2 +-
 timers.cc         | 492 +++++++++++++++++++++-------------------------
 timers.h          | 148 +++++---------
 vchannelselect.cc |   9 +-
 vchannelselect.h  |   2 +-
 vdr.cc            |   6 +-
 vepg.cc           |   4 +-
 vepg.h            |   7 +-
 vmedialist.h      |   1 -
 vmediaview.cc     |   1 -
 vmediaview.h      |   9 +-
 vmute.cc          |   5 +-
 vmute.h           |   5 +-
 vradiorec.cc      |  10 +-
 vradiorec.h       |  10 +-
 vsleeptimer.cc    |   1 -
 vsleeptimer.h     |   2 +-
 vteletextview.cc  | 304 ++++++++++++++--------------
 vteletextview.h   |  43 ++--
 vtimeredit.cc     |   1 -
 vtimerlist.cc     |   1 -
 vtimerlist.h      |   2 +-
 vvideolivetv.cc   |   6 +-
 vvideolivetv.h    |   2 +-
 vvideomedia.cc    |   1 -
 vvideomedia.h     |   3 +-
 vvideorec.cc      |  10 +-
 vvideorec.h       |  11 +-
 vvolume.cc        |   1 -
 vvolume.h         |   2 +-
 vwelcome.cc       |   5 +-
 vwelcome.h        |   2 +-
 38 files changed, 490 insertions(+), 636 deletions(-)

diff --git a/CREDITS b/CREDITS
index 9de0c99..4fc8470 100644
--- a/CREDITS
+++ b/CREDITS
@@ -43,6 +43,7 @@ Marten Richter
   Reporting of many bugs
   Audio channel selection
   VDR 1.7 compatibility
+  Raspberry Pi port
 
 Andreas Vogel
   Media player
diff --git a/audioplayer.cc b/audioplayer.cc
index e6001e7..9f3f9a3 100644
--- a/audioplayer.cc
+++ b/audioplayer.cc
@@ -14,13 +14,10 @@
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with VOMP; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-#include "audioplayer.h"
 #include "demuxeraudio.h"
-#include "timers.h"
 #include "video.h"
 #include "messagequeue.h"
 #include "i18n.h"
@@ -29,6 +26,8 @@
 #include "media.h"
 #include "mediaplayer.h"
 
+#include "audioplayer.h"
+
 //how often do we retry if there is no data in stream
 #define MAXTRY 50 
 
diff --git a/audioplayer.h b/audioplayer.h
index 08af3d9..f39df37 100644
--- a/audioplayer.h
+++ b/audioplayer.h
@@ -14,8 +14,7 @@
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with VOMP; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
 #ifndef AUDIOPLAYER_H
@@ -36,7 +35,7 @@
 #include "messagequeue.h"
 #include "thread.h"
 #include "afeed.h"
-#include "timerreceiver.h"
+#include "timers.h"
 
 #ifdef WIN32
 #include "threadwin.h"
@@ -45,7 +44,6 @@
 #endif
 
 
-
 class Boxx;
 class DemuxerAudio;
 class MediaURI;
diff --git a/command.cc b/command.cc
index e5c767b..b38569d 100644
--- a/command.cc
+++ b/command.cc
@@ -41,7 +41,6 @@
 #include "colour.h"
 #include "osd.h"
 #include "i18n.h"
-#include "timerreceiver.h"
 #include "timers.h"
 #include "wol.h"
 #include "vconnect.h"
diff --git a/main.cc b/main.cc
index 924d9a7..b727db3 100644
--- a/main.cc
+++ b/main.cc
@@ -141,7 +141,7 @@ int main(int argc, char** argv)
   wol        = new Wol();
   sleeptimer = new Sleeptimer();
 
-  if (!logger || !inputMan || !led || !osd || !video || !audio || !boxstack || !command || !wol || !sleeptimer)
+  if (!logger || !timers || !inputMan || !led || !osd || !video || !audio || !boxstack || !command || !wol || !sleeptimer)
   {
     printf("Could not create objects. Memory problems?\n");
     shutdown(1);
diff --git a/playermedia.cc b/playermedia.cc
index 692dd20..ecaad25 100644
--- a/playermedia.cc
+++ b/playermedia.cc
@@ -20,7 +20,6 @@
 #include "playermedia.h"
 //we most probably need a new demux...
 #include "demuxermedia.h"
-#include "timers.h"
 #include "video.h"
 #include "messagequeue.h"
 #include "i18n.h"
diff --git a/playermedia.h b/playermedia.h
index d97e17b..6012c66 100644
--- a/playermedia.h
+++ b/playermedia.h
@@ -37,7 +37,7 @@
 #include "thread.h"
 #include "afeed.h"
 #include "vfeed.h"
-#include "timerreceiver.h"
+#include "timers.h"
 #include "video.h"
 
 #ifdef WIN32
diff --git a/timers.cc b/timers.cc
index 115cacb..f1f73da 100644
--- a/timers.cc
+++ b/timers.cc
@@ -1,5 +1,5 @@
 /*
-    Copyright 2004-2007 Chris Tallon
+    Copyright 2004-2020 Chris Tallon
 
     This file is part of VOMP.
 
@@ -14,15 +14,12 @@
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with VOMP; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-#include "timers.h"
-
 #include "log.h"
-#include "command.h"
-#include "timerreceiver.h"
+
+#include "timers.h"
 
 Timers* Timers::instance = NULL;
 
@@ -30,7 +27,6 @@ Timers::Timers()
 {
   if (instance) return;
   instance = this;
-  initted = false;
 }
 
 Timers::~Timers()
@@ -45,87 +41,106 @@ Timers* Timers::getInstance()
 
 int Timers::init()
 {
-  if (initted) return 0;
-  initted = true;
+  if (initted) return false;
   logger = Log::getInstance();
 
-  if (!threadStart())
-  {
-    shutdown();
-    return 0;
-  }
-
-  return 1;
+  timersMutex.lock(); // Start thread with mutex locked
+  recalc = true;
+  timersThread = std::thread([this] { masterLoop(); });
+  logger->log("Timers", Log::DEBUG, "Timers started");
+  initted = true;
+  return true;
 }
 
-int Timers::shutdown()
+void Timers::shutdown()
 {
-  if (!initted) return 0;
-  initted = false;
+  if (!initted) return;
+
+  std::unique_lock<std::mutex> lockWrapper(timersMutex); // lock the mutex
+  // main loop is in cond.wait/_until
 
-  logger->log("Timers", Log::DEBUG, "Timers shutdown start");
+  // stop the main thread
+  quitThread = true;  // main loop effectively dead, but join it later
 
-  threadStop();
+  // Remove any future timer events from the list
+  for(auto i = timerList.begin(); i != timerList.end(); )
+  {
+    TimerEvent* te = *i;
 
-  TimerEvent* timerEvent = NULL;
-  TimerReceiver* client = NULL;
-  int clientReference = 0;
+    if (!te->running)
+    {
+      delete te;
+      i = timerList.erase(i);
+    }
+    else
+      ++i;
+  }
 
+  // Now wait for all timer event threads to end
   while(timerList.size())
   {
-    threadLock();
-    timerEvent = timerList.front();
-    client = timerEvent->client;
-    clientReference = timerEvent->clientReference;
-    threadUnlock();
-
-    cancelTimer(client, clientReference);
+    timersCond.wait(lockWrapper); // unlocks in wait. waiting for reap signals
+    // locked
+    // Assume there has been a reap signal. Could be spurious but no harm done
+    reap();
+    timersCond.notify_all(); // In case of waiting cancelTimers
   }
 
-  logger->log("Timers", Log::DEBUG, "Timers shutdown end");
+  timersCond.notify_one(); // In case there were no TimerEvents to reap, wake the masterLoop thread
+  lockWrapper.unlock(); // main loop exits if it hasn't already
+  timersThread.join();
+}
 
-  return 1;
+bool Timers::setTimerD(TimerReceiver* client, int clientReference, long int requestedSecs, long int requestedNSecs)
+{
+  std::chrono::system_clock::time_point fireTime = std::chrono::system_clock::now();
+  fireTime += std::chrono::seconds(requestedSecs);
+  fireTime += std::chrono::nanoseconds(requestedNSecs);
+
+  return setTimerC(client, clientReference, fireTime);
+}
+
+bool Timers::setTimerT(TimerReceiver* client, int clientReference, long int requestedTime, long int requestedTimeNSecs)
+{
+  std::chrono::system_clock::time_point fireTime = std::chrono::system_clock::from_time_t(requestedTime);
+  fireTime += std::chrono::nanoseconds(requestedTimeNSecs);
+
+  return setTimerC(client, clientReference, fireTime);
 }
 
-bool Timers::setTimerT(TimerReceiver* client, int clientReference, long int requestedTime, long int requestedTimeNSEC)
+bool Timers::setTimerC(TimerReceiver* client, int clientReference, std::chrono::system_clock::time_point& fireTime)
 {
   if (!initted) return 0;
 
-  logger->log("Timers", Log::DEBUG, "Starting set timer 1");
+  logger->log("Timers", Log::DEBUG, "Starting set timer chrono");
 
-  //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 2");
-  threadLock();
+  std::lock_guard<std::mutex> lock(timersMutex);
 
   // Check that this timer is not already in the list
-  TimerList::iterator i;
-  TimerEvent* currentTimerEvent = NULL;
-  for(i = timerList.begin(); i != timerList.end(); i++)
+  for(auto foundTimerEvent : timerList)
   {
-    currentTimerEvent = *i;
-
-    if ((currentTimerEvent->client == client) && (currentTimerEvent->clientReference == clientReference))
+    if ((foundTimerEvent->client == client) && (foundTimerEvent->clientReference == clientReference))
     {
       // Timer exists already, either waiting or running
-      // Update the clocks
-      currentTimerEvent->requestedTime.tv_sec = requestedTime;
-      currentTimerEvent->requestedTime.tv_nsec = requestedTimeNSEC;
+      // Update requestedTime
+      foundTimerEvent->requestedTime = fireTime;
 
-      if (currentTimerEvent->running)
+      if (foundTimerEvent->running)
       {
         // If this timerEvent is currently running, update the clocks and set the restart flag.
         // Instead of being deleted in timerEventFinished it will be restarted
-        currentTimerEvent->restartAfterFinish = true;
+        foundTimerEvent->restartAfterFinish = true;
         // Don't need to resetThreadFlag because this timer isn't re-live yet
-        threadUnlock();
-        return true;
+        return true; // unlock
       }
       else
       {
+        logger->log("Timers", Log::DEBUG, "Editing existing timer");
+
         // A waiting timer has been edited
-        resetThreadFlag = true;
-        threadSignalNoLock();
-        threadUnlock();
-        return true;
+        recalc = true;
+        timersCond.notify_all();
+        return true; // unlock
       }
     }
   }
@@ -135,40 +150,132 @@ bool Timers::setTimerT(TimerReceiver* client, int clientReference, long int requ
   TimerEvent* t = new TimerEvent();
   t->client = client;
   t->clientReference = clientReference;
-  t->requestedTime.tv_sec = requestedTime;
-  t->requestedTime.tv_nsec = requestedTimeNSEC;
+  t->requestedTime = fireTime;
 
-  //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 2");
   timerList.push_back(t);
-  resetThreadFlag = true;
-  threadSignalNoLock();
-  //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2");
-  threadUnlock();
-
+  recalc = true;
+  timersCond.notify_all();
   logger->log("Timers", Log::DEBUG, "Timer set for %p ref %i", client, clientReference);
-
-  return true;
+  return true; // unlock
 }
 
-bool Timers::setTimerD(TimerReceiver* client, int clientReference, long int requestedSecs, long int requestedNSecs)
+void Timers::masterLoop()
 {
-  struct timespec currentTime;
+  // mutex is locked already
+  std::unique_lock<std::mutex> lockWrapper(timersMutex, std::defer_lock);
 
-  getClockRealTime(&currentTime);
+  TimerEvent* nextTimer = NULL;
+
+  while(1)
+  {
+    if (recalc)
+    {
+      // work out the next fire time
+      nextTimer = NULL;
 
-  long int requestedTime;
-  long int requestedTimeNSEC;
+      for(TimerEvent* thisTimer : timerList)
+      {
+        if (thisTimer->running) continue; // has already been timercall'd
 
-  requestedTime = currentTime.tv_sec + requestedSecs;
-  requestedTimeNSEC = currentTime.tv_nsec + requestedNSecs;
-  if (requestedTimeNSEC > 999999999)
+        if (!nextTimer)
+        {
+          nextTimer = thisTimer;
+        }
+        else
+        {
+          if (thisTimer->requestedTime < nextTimer->requestedTime)
+          {
+            nextTimer = thisTimer;
+          }
+        }
+      }
+
+      recalc = false;
+    }
+
+    std::cv_status cvs;
+    if (nextTimer)
+    {
+      // wait for signal timed
+      cvs = timersCond.wait_until(lockWrapper, nextTimer->requestedTime); //unlocks in wait
+    }
+    else
+    {
+      // wait for signal
+      timersCond.wait(lockWrapper); //unlocks in wait
+    }
+
+    // and we're back - we've been signalled (quit, recalc, reap) or the time ran out, or spurious. mutex locked.
+
+    // quit? -> quit
+    if (quitThread) return; // unlocks motex
+
+    if (doReap)
+    {
+      reap();
+      timersCond.notify_all(); // in case of waiting cancelTimers
+    }
+
+    // recalc? -> restart loop
+    if (recalc) continue;
+
+    // time ran out? -> fire a timer
+    if (nextTimer && (cvs == std::cv_status::timeout))
+    {
+      nextTimer->run();
+      recalc = true;
+    }
+//  else
+//  {
+      // not quit, not recalc, and either:
+      // 1. there is no next timer
+      // 2. there is a next timer but the timeout didn't expire
+      // therefore, this is a spurious wakeup. Leave recalc == false, go around, and do wait/wait_until again.
+//    continue;
+//  }
+  }
+
+  // If you want to do any cleanup after the main loop, change the quitthread return to break and put cleanup here. mutex will be locked
+}
+
+void Timers::reap() // Master timers thread, mutex locked (or shutdown, mutex locked)
+{
+  for(auto i = timerList.begin(); i != timerList.end(); )
   {
-    ++requestedTime;
-    requestedTimeNSEC -= 1000000000;
-    logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");
+    TimerEvent* te = *i;
+
+    if (te->completed)
+    {
+      te->timerThread.join();
+
+      if (te->restartAfterFinish)
+      {
+        logger->log("Timers", Log::DEBUG, "timerEventFinished RESTART for %p %i", te->client, te->clientReference);
+        te->restartAfterFinish = false;
+        te->running = false;
+        te->completed = false;
+        recalc = true;
+        ++i;
+      }
+      else
+      {
+        delete te;
+        i = timerList.erase(i);
+      }
+    }
+    else
+      ++i;
   }
+}
 
-  return setTimerT(client, clientReference, requestedTime, requestedTimeNSEC);
+void Timers::reapTimerEvent(TimerEvent* te) // Called by a TimerEvent thread
+{
+  std::lock_guard<std::mutex> lock(timersMutex);
+  te->completed = true;
+  doReap = true;
+  timersCond.notify_all(); // Would be notify_one, but in the case of shutdown the thread within shutdown() will be
+  // handling the reaping. The notify that goes to the masterLoop will cause it to return from wait and sit waiting
+  // for the lock so it can examine quitThread and return. Doesn't matter which thread is unblocked first
 }
 
 bool Timers::cancelTimer(TimerReceiver* client, int clientReference)
@@ -176,7 +283,7 @@ bool Timers::cancelTimer(TimerReceiver* client, int clientReference)
   /* This method locks the timers mutex
      Then one of three things can happen:
      1. The TimerEvent is found, running = false. This means it hasn't started yet.
-        Delete the timer normally, set resetFlag
+        Delete the timer normally, set recalc
      2. The TimerEvent is found, running = true. This means the timer is currently firing,
         timercall on the client is being called.
         a. Thread calling cancelTimer is an external thread: In this case, this thread
@@ -199,225 +306,80 @@ bool Timers::cancelTimer(TimerReceiver* client, int clientReference)
 
   logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());
 
+  std::unique_lock<std::mutex> lockWrapper(timersMutex); // lock
+
   while(1)
   {
-    threadLock();
-
+    TimerEvent* foundTimerEvent = NULL;
     TimerList::iterator i;
-    TimerEvent* currentTimerEvent = NULL;
     for(i = timerList.begin(); i != timerList.end(); i++)
     {
-      currentTimerEvent = *i;
-      if ((currentTimerEvent->client == client) && (currentTimerEvent->clientReference == clientReference))
+      if (((*i)->client == client) && ((*i)->clientReference == clientReference))
       {
+        foundTimerEvent = *i;
         break;
       }
     }
 
-    if (i == timerList.end())
+    if (!foundTimerEvent)
     {
       // Case 3, no timer found
-      threadUnlock();
-      return true;
+      return true; // unlock
     }
-    else
-    {
-      // Timer found, Case 1 or 2
 
-      if (currentTimerEvent->running == false)
-      {
-        // Case 1. Just delete the timer and reset the thread.
-        timerList.erase(i);
-        delete currentTimerEvent;
-        logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);
-        resetThreadFlag = true;
-        threadSignalNoLock();
-        threadUnlock();
-        return true;
-      }
-      else
-      {
-        if (Thread_TYPE::thisThreadID() == currentTimerEvent->getThreadID())
-        {
-          // Case 2 b.
-          // The thread requesting cancelTimer is the timer thread itself, the timer has already fired.
-          logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer itself calling - ignore", client, clientReference);
-          currentTimerEvent->restartAfterFinish = false; // in case a restart had already been set.
-          threadUnlock();
-          return true;
-        }
-
-        // Case 2 a. For now, use polling with a 50ms delay.
-        // Don't delete a running timer.
-        // FIXME upgrade me to signalling
-
-        logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer WAITING", client, clientReference);
-
-        threadUnlock();
-        MILLISLEEP(50);
-      }
-    }
-  } // end of the big while loop
-}
+    // Timer found, case 1 or 2
 
-void Timers::timerEventFinished(TimerEvent* timerEvent)
-{
-  // This function takes out the already timercall'd TimerEvent from the list
-  // Or resets it if restart flag is true
-
-
-  if (!initted) return;
-  threadLock();
-
-  logger->log("Timers", Log::DEBUG, "timerEventFinished for %p", timerEvent->client);
-
-  for(TimerList::iterator i = timerList.begin(); i != timerList.end(); i++)
-  {
-    if (timerEvent != *i) continue;
-
-    if (timerEvent->restartAfterFinish)
+    if (!foundTimerEvent->running)
     {
-      logger->log("Timers", Log::DEBUG, "timerEventFinished RESTART for %p", timerEvent->client);
+      // Case 1. Timer was just waiting. Delete and set recalc.
 
-      timerEvent->restartAfterFinish = false;
-      timerEvent->running = false;
-      resetThreadFlag = true;
-      threadSignalNoLock();
-    }
-    else
-    {
-      // The removal of a called and non-restart TimerEvent doesn't need the threadMethod to be reset
       timerList.erase(i);
-      logger->log("Timers", Log::DEBUG, "timerEventFinished for %p %i - remove done", timerEvent->client, timerEvent->clientReference);
-      delete timerEvent;
+      delete foundTimerEvent;
+      logger->log("Timers", Log::DEBUG, "Removed timer for %p ref %i", client, clientReference);
+      recalc = true;
+      timersCond.notify_all(); // shutdown could be being called? notify_all guarantees we wake masterLoop
+      return true; // unlock
     }
 
-    break;
-  }
-  // FIXME At this point, this should signal all threads waiting on cancelTimer
-  threadUnlock();
-
-  // Kill this thread, as it's the one started for the timer event
-  Thread_TYPE::threadSuicide();
-}
-
-void Timers::threadMethod()
-{
-  struct timespec nextTime;
-  TimerEvent* nextTimer = NULL;
-  resetThreadFlag = true;
-
-  threadLock();
-
-  while(1)
-  {
-    if (resetThreadFlag)
+    if (std::this_thread::get_id() == foundTimerEvent->timerThread.get_id())
     {
-      resetThreadFlag = false;
-
-      // Work out the next Timer
-
-      nextTime.tv_sec = 0;
-      nextTime.tv_nsec = 0;
-      nextTimer = NULL;
-
-      TimerList::iterator i;
-      TimerEvent* currentTimer = NULL;
-      for(i = timerList.begin(); i != timerList.end(); i++)
-      {
-        currentTimer = *i;
-        if (currentTimer->running) continue; // has already been timercall'd
-
-        if (!nextTimer)
-        {
-          nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
-          nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
-          nextTimer = currentTimer;
-        }
-        else
-        {
-          if (currentTimer->requestedTime.tv_sec < nextTime.tv_sec)
-          {
-            nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
-            nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
-            nextTimer = currentTimer;
-          }
-          else if (currentTimer->requestedTime.tv_sec == nextTime.tv_sec)
-          {
-            if (currentTimer->requestedTime.tv_nsec < nextTime.tv_nsec)
-            {
-              nextTime.tv_sec = currentTimer->requestedTime.tv_sec;
-              nextTime.tv_nsec = currentTimer->requestedTime.tv_nsec;
-              nextTimer = currentTimer;
-            }
-          }
-        }
-      }
-    }
-
-    if (nextTimer)
-    {
-//##      logger->log("Timers", Log::DEBUG, "List size: %i. nextTimerClient: %p/%i. nextTime.tv_sec: %li. nextTime.tv_nsec: %li", timerList.size(), nextTimer->client, nextTimer->clientReference, nextTime.tv_sec, nextTime.tv_nsec);
-
-
-      //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (1)");
-      threadWaitForSignalTimed(&nextTime);
-      //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 5");
-
-      // unlocks in the wait
-    }
-    else
-    {
-      //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (2)");
-      threadWaitForSignal();
-      //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");
-      // unlocks in the wait
+      // Case 2 b.
+      // The thread requesting cancelTimer is the timer thread itself, the timer has already fired.
+      logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer itself calling - ignore", client, clientReference);
+      foundTimerEvent->restartAfterFinish = false; // in case a restart had already been set.
+      return true; // unlock
     }
 
-    // Mutex locked again here by exit of wait or timedwait above
-
-    // ok. we have been signalled or the time has run out
-    // This only gets signalled if it is to reset or die
-
-    // First check for die..
-    threadCheckExit(); // exiting thread with mutex locked
-
-    // Check for reset..
-    // This can be caused by an addition or deletion to the list
-    if (resetThreadFlag || (nextTimer == NULL)) continue;
+    // Case 2 a. An external thread is calling cancelTimer for a timer which has already fired and is still running.
+    // Want to block here until we know the thread has completed.
 
-    // timer ran out
+    // A broadcast notify goes out after each reap (whether in main loop or shutdown)
+    // So, wait on the cond and go around each time we wake. One of them will have been after the timerThread finished,
+    // which turns it into a case 3.
 
-    Log::getInstance()->log("Timers", Log::DEBUG, "Timer firing for client %p ref %i", nextTimer->client, nextTimer->clientReference);
+    logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer WAITING", client, clientReference);
+    timersCond.wait(lockWrapper); //unlocks in wait
+    // locked
+    logger->log("Timers", Log::DEBUG, "%p ref %i cancelTimer go-around", client, clientReference);
 
-    nextTimer->run(); // sets timerevent to running and starts it
-    resetThreadFlag = true; // find a new timer to wait on
-  }
+  } // end of the big while loop
 }
 
 
-
 // Class TimerEvent
 
-TimerEvent::TimerEvent()
-{
-  running = false;
-  restartAfterFinish = false;
-  client = NULL;
-  clientReference = 0;
-  requestedTime.tv_sec = 0;
-  requestedTime.tv_nsec = 0;
-}
-
-void TimerEvent::threadMethod()
-{
-  Log::getInstance()->log("Timers", Log::DEBUG, "sending timer to %p with parameter %u", client, clientReference);
-  client->timercall(clientReference);
-  Timers::getInstance()->timerEventFinished(this); // does not return
-}
-
 void TimerEvent::run()
 {
   running = true;
-  threadStart();
+  threadStartProtect.lock();
+  timerThread = std::thread([this]
+  {
+    threadStartProtect.lock();
+    threadStartProtect.unlock();
+
+    Log::getInstance()->log("Timers", Log::DEBUG, "sending timer to %p with parameter %u", client, clientReference);
+    client->timercall(clientReference);
+    Timers::getInstance()->reapTimerEvent(this);
+  });
+  threadStartProtect.unlock(); // Ensures timerThread is valid before run() returns
 }
diff --git a/timers.h b/timers.h
index d15ef30..bf746b9 100644
--- a/timers.h
+++ b/timers.h
@@ -1,60 +1,5 @@
-
-
-
-
-
-
-
-
-
-/*
-
-
-FYI
-
-Fixed:
-
-The main problem was that timers only seemed to launch messages which went into The Big Mutex.
-This has been fixed a different way by implementing locking in BoxStack - meaning timercalls
-can now draw and update their displays without locking The Big Mutex. Problem solved. I think
-the whole program might move more towards classes keeping more mutexes and rely less on The
-Big Mutex.
-
-
-
->      Timers ... deprecated. Maybe.
-
->      This whole thing will be replaced with a timed-message idea. It will be possible
->      to send yourself (or whoever) a message with a delay, or delivery time. The message
->      will be handed to Command as usual, but command will do the right thing. The messages
->      will be delivered to the recipient _with the gui mutex locked_, meaning updates can
->      be done there and then in the message handler.
-
->      Good points:
->      * Cuts down on code lines
->      * Most (all?) timercall()s eventually send a message to command in order to
->        do something within The Big Mutex. This makes it easier and simpler code wise
->      * Gets rid of Timers.
-
->      Bad points:
->      * Timers become gui only features. Solve this with a MessageReceiver interface and
->        have command deliver messages straight to the recipients rather than through BoxStack.
->      * Timer delivery accuracy becomes dependant on everything that uses The Big Mutex.
->        It will become more important to not block The Big Mutex.
->      * Cancelling timers... hmm
-
->      If you have any comments about the new design, like, "It's just as flawed as the old one",
->      then I'd appreciate hearing it before I start writing it all :)
-
-
-*/
-
-
-
-
-
 /*
-    Copyright 2004-2005 Chris Tallon
+    Copyright 2004-2020 Chris Tallon
 
     This file is part of VOMP.
 
@@ -69,30 +14,15 @@ Big Mutex.
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with VOMP; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-#ifndef TIMERS_H
-#define TIMERS_H
-
-#include <stdio.h>
-#include <list>
-
-#include "threadsystem.h"
-
-#include "defines.h"
-
-
-class Log;
-class TimerReceiver;
-
 /*
 
 Timers documentation
 
 Call setTimer to set a timer.... cancelTimer to delete a running timer.
-Derive your object from TimerReceiver (timerreceiver.h), implement timercall() in your class
+Derive your object from TimerReceiver, implement timercall() in your class
 and supply your 'this' pointer to setTimer.
 
 Once a timer has fired it does not exist anymore, you have to keep creating them if you want
@@ -103,36 +33,52 @@ so you can identify which timer has fired if you have more than one.
 
 You can reset a timer by calling setTimer again. This will not create 2 timers, it will overwrite the first one.
 
-You must not allow a timer to fire on an object that has been deleted already, unless you want
-segfaulty hell.
-
-??
+You must not allow a timer to fire on an object that has been deleted already.
 
 You must call cancelTimer before deleting object. cancelTimer guarantees that timercall
 will not be called again.
 
 */
 
-class TimerEvent : public Thread_TYPE
-{
-  public:
-    TimerEvent();
+#ifndef TIMERS_H
+#define TIMERS_H
 
-    virtual void run();
-    virtual void threadMethod();
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include <list>
+#include <chrono>
 
-    TimerReceiver* client;
-    int clientReference;
-    struct timespec requestedTime;
+#include "defines.h"
 
-    bool running;
-    bool restartAfterFinish;
+class TimerReceiver
+{
+  public:
+    virtual ~TimerReceiver() {}
+    virtual void timercall(int clientReference)=0;
 };
 
+class TimerEvent
+{
+  friend class Timers;
+  public:
+  private:
+    TimerReceiver* client{};
+    int clientReference{};
+    std::chrono::system_clock::time_point requestedTime;
+    bool running{};
+    bool completed{};
+    bool restartAfterFinish{};
+    std::thread timerThread;
+    std::mutex threadStartProtect;
+
+    void run();
+};
 
+class Log;
 typedef std::list<TimerEvent*> TimerList;
 
-class Timers : public Thread_TYPE
+class Timers
 {
   public:
     Timers();
@@ -140,23 +86,29 @@ class Timers : public Thread_TYPE
     static Timers* getInstance();
 
     int init();
-    int shutdown();
+    void shutdown();
 
     bool setTimerT(TimerReceiver* client, int clientReference, long int requestedTime, long int requestedTimeNSEC=0);
     bool setTimerD(TimerReceiver* client, int clientReference, long int requestedSecs, long int requestedNSecs=0);
+    bool setTimerC(TimerReceiver* client, int clientReference, std::chrono::system_clock::time_point& requestedTime);
     bool cancelTimer(TimerReceiver* client, int clientReference);
 
-    // Thread stuff
-    virtual void threadMethod();
-
-    void timerEventFinished(TimerEvent* timerEvent); // internal use only, does not return
-
+    void reapTimerEvent(TimerEvent*); // Internal only
   private:
     static Timers* instance;
-    Log* logger;
-    bool initted;
+    Log* logger{};
+    bool initted{};
+    bool quitThread{};
+    bool recalc{};
+    bool doReap{};
     TimerList timerList;
-    bool resetThreadFlag;
+
+    std::thread timersThread;
+    std::mutex timersMutex;
+    std::condition_variable timersCond;
+
+    void masterLoop();
+    void reap();
 };
 
 #endif
diff --git a/vchannelselect.cc b/vchannelselect.cc
index bc6c492..d53d4aa 100644
--- a/vchannelselect.cc
+++ b/vchannelselect.cc
@@ -17,17 +17,17 @@
     along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-#include "vchannelselect.h"
 
 #include "input.h"
 #include "message.h"
 #include "boxstack.h"
 #include "colour.h"
 #include "log.h"
-#include "timers.h"
 #include "vdr.h"
 #include "messagequeue.h"
 
+#include "vchannelselect.h"
+
 // this class only works as it does because the remote command
 // values for the numbers are the numbers themselves !
 
@@ -37,7 +37,7 @@ VChannelSelect::VChannelSelect(Boxx* v)
   numGot = 0;
   ignoreTimer = false;
 
-  numWidth = (int)VDR::getInstance()->getChannelNumberWidth();
+  numWidth = static_cast<int>(VDR::getInstance()->getChannelNumberWidth());
   if (numWidth > 10) numWidth = 10;
   for (int i = 0; i < numWidth; i++) input[i] = -1;
 
@@ -54,7 +54,6 @@ VChannelSelect::~VChannelSelect()
 
 void VChannelSelect::timercall(int /*clientReference*/)
 {
-  Log::getInstance()->log("VChannelSelect", Log::DEBUG, "Timer call");
   if (ignoreTimer) return;
   changeChannel();
 }
@@ -112,7 +111,7 @@ void VChannelSelect::changeChannel()
 
     for(i = numGot - 1; i >= 0; i--)
     {
-      m->parameter += input[i] * (ULONG)pow(10., i);
+      m->parameter += input[i] * static_cast<ULONG>(pow(10., i));
     }
 
     MessageQueue::getInstance()->postMessage(m);
diff --git a/vchannelselect.h b/vchannelselect.h
index ab22182..00b84a6 100644
--- a/vchannelselect.h
+++ b/vchannelselect.h
@@ -24,7 +24,7 @@
 #include <math.h>
 
 #include "boxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
 
 class VChannelSelect : public Boxx, public TimerReceiver
 {
diff --git a/vdr.cc b/vdr.cc
index 3f218b7..c54e8f4 100644
--- a/vdr.cc
+++ b/vdr.cc
@@ -598,12 +598,12 @@ VDR_ResponsePacket* VDR::RequestResponse(VDR_RequestPacket* vrp)
   }
 
   // Sleep and block this thread. The sleep unlocks the mutex
-  logger->log("VDR", Log::DEBUG, "RR sleep - opcode %lu", vrp->getOpcode());
+//  logger->log("VDR", Log::DEBUG, "RR sleep - opcode %lu", vrp->getOpcode());
   edSleepThisReceiver(&vdrpr);
-  logger->log("VDR", Log::DEBUG, "RR unsleep");
+//  logger->log("VDR", Log::DEBUG, "RR unsleep");
     
   // Woken because a response packet has arrived, mutex will be locked
-  logger->log("VDR", Log::DEBUG, "Packet delivered to me, requestID: %lu", vdrpr.save_vresp->getRequestID());
+//  logger->log("VDR", Log::DEBUG, "Packet delivered to me, requestID: %lu", vdrpr.save_vresp->getRequestID());
   
   edUnlock();
   return vdrpr.save_vresp;
diff --git a/vepg.cc b/vepg.cc
index 4069109..62705b3 100644
--- a/vepg.cc
+++ b/vepg.cc
@@ -30,14 +30,11 @@
     The video stream currently being viewed is shown as quarter screen in the top right.
 */
 
-#include "vepg.h"
-
 #include "input.h"
 #include "vchannellist.h"
 #include "messagequeue.h"
 #include "video.h"
 #include "vepgsettimer.h"
-#include "timers.h"
 #include "wsymbol.h"
 #include "message.h"
 #include "colour.h"
@@ -46,6 +43,7 @@
 #include "i18n.h"
 #include "log.h"
 
+#include "vepg.h"
 
 VEpg* VEpg::instance = NULL;
 
diff --git a/vepg.h b/vepg.h
index adc6f7e..098e5d5 100644
--- a/vepg.h
+++ b/vepg.h
@@ -14,20 +14,17 @@
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with VOMP; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
 #ifndef VEPG_H
 #define VEPG_H
 
-#include <stdio.h>
-#include <string.h>
 #include <vector>
 
 #include "boxx.h"
 #include "defines.h"
-#include "timerreceiver.h"
+#include "timers.h"
 #include "wtextbox.h"
 #include "vdr.h"
 #include "wselectlist.h"
diff --git a/vmedialist.h b/vmedialist.h
index 4cda3cd..0707547 100644
--- a/vmedialist.h
+++ b/vmedialist.h
@@ -28,7 +28,6 @@
 #include "tbboxx.h"
 #include "wselectlist.h"
 #include "timers.h"
-#include "timerreceiver.h"
 
 class DirList;
 class Message;
diff --git a/vmediaview.cc b/vmediaview.cc
index 403c68d..87e0f10 100644
--- a/vmediaview.cc
+++ b/vmediaview.cc
@@ -24,7 +24,6 @@
 #include "vpicturebanner.h"
 #include "vcolourtuner.h"
 #include "audioplayer.h"
-#include "timers.h"
 #include "boxx.h"
 #include "wselectlist.h"
 #include "input.h"
diff --git a/vmediaview.h b/vmediaview.h
index 7e6d10a..7d0bf63 100644
--- a/vmediaview.h
+++ b/vmediaview.h
@@ -14,19 +14,14 @@
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with VOMP; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
 #ifndef VMEDIAVIEW_H
 #define VMEDIAVIEW_H
 
-#include <stdio.h>
-#include <string.h>
-#include <vector>
-
 #include "boxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
 #include "vmedialist.h"
 #include "colour.h"
 #include "wjpeg.h"
diff --git a/vmute.cc b/vmute.cc
index b2fd4df..9a8ccdc 100644
--- a/vmute.cc
+++ b/vmute.cc
@@ -17,17 +17,16 @@
     along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-#include "vmute.h"
-
 #include "input.h"
 #include "audio.h"
 #include "video.h"
 #include "wsymbol.h"
 #include "colour.h"
-#include "timers.h"
 #include "boxstack.h"
 #include "messagequeue.h"
 
+#include "vmute.h"
+
 VMute::VMute()
 {
   isMuted = Audio::getInstance()->toggleUserMute();
diff --git a/vmute.h b/vmute.h
index 850dbb9..ebeb242 100644
--- a/vmute.h
+++ b/vmute.h
@@ -14,8 +14,7 @@
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with VOMP; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
 #ifndef VMUTE_H
@@ -24,7 +23,7 @@
 #include <stdio.h>
 
 #include "boxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
 
 class VMute : public Boxx, public TimerReceiver
 {
diff --git a/vradiorec.cc b/vradiorec.cc
index 416a1e3..d53b7fa 100644
--- a/vradiorec.cc
+++ b/vradiorec.cc
@@ -1,5 +1,5 @@
 /*
-    Copyright 2004-2019 Chris Tallon
+    Copyright 2004-2020 Chris Tallon
 
     This file is part of VOMP.
 
@@ -14,12 +14,9 @@
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with VOMP; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-#include "vradiorec.h"
-
 #include "command.h"
 #include "osd.h"
 #include "player.h"
@@ -29,7 +26,6 @@
 #include "message.h"
 #include "vdr.h"
 #include "video.h"
-#include "timers.h"
 #include "playerradio.h"
 #include "boxstack.h"
 #include "input.h"
@@ -38,6 +34,8 @@
 #include "log.h"
 #include "messagequeue.h"
 
+#include "vradiorec.h"
+
 VRadioRec::VRadioRec(Recording* rec)
 {
   boxstack = BoxStack::getInstance();
diff --git a/vradiorec.h b/vradiorec.h
index a552901..dfff16d 100644
--- a/vradiorec.h
+++ b/vradiorec.h
@@ -1,5 +1,5 @@
 /*
-    Copyright 2004-2006 Chris Tallon
+    Copyright 2004-2020 Chris Tallon
 
     This file is part of VOMP.
 
@@ -14,17 +14,14 @@
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with VOMP; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
 #ifndef VRADIOREC_H
 #define VRADIOREC_H
 
-#include <stdio.h>
-
 #include "boxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
 #include "defines.h"
 #include "colour.h"
 #include "region.h"
@@ -34,7 +31,6 @@ class Message;
 class VDR;
 class Video;
 class PlayerRadio;
-class Timers;
 class BoxStack;
 
 class VRadioRec : public Boxx, public TimerReceiver
diff --git a/vsleeptimer.cc b/vsleeptimer.cc
index 45136a8..9ba949f 100644
--- a/vsleeptimer.cc
+++ b/vsleeptimer.cc
@@ -23,7 +23,6 @@
 #include "wsymbol.h"
 #include "colour.h"
 #include "video.h"
-#include "timers.h"
 #include "boxstack.h"
 #include "command.h"
 #include "messagequeue.h"
diff --git a/vsleeptimer.h b/vsleeptimer.h
index 3ca76f0..bf0785e 100644
--- a/vsleeptimer.h
+++ b/vsleeptimer.h
@@ -23,7 +23,7 @@
 #include <stdio.h>
 
 #include "boxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
 
 #include "threadsystem.h"
 
diff --git a/vteletextview.cc b/vteletextview.cc
index 5e47bc4..15a1ce6 100644
--- a/vteletextview.cc
+++ b/vteletextview.cc
@@ -1,5 +1,5 @@
 /*
-    Copyright 2005-2008 Chris Tallon, Marten Richter
+    Copyright 2005-2020 Chris Tallon, Marten Richter
 
     This file is part of VOMP.
 
@@ -14,200 +14,192 @@
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with VOMP; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
+
 #include <math.h>
-#include "vteletextview.h"
+
 #include "video.h"
-#include "timers.h"
 #include "boxstack.h"
 #include "input.h"
 #include "playerlivetv.h"
 
+#include "vteletextview.h"
 
-VTeletextView::VTeletextView(TeletextDecoderVBIEBU* TTdecoder,Boxx* playerview, PlayerLiveTV* playerObj)
+VTeletextView::VTeletextView(TeletextDecoderVBIEBU* TTdecoder, Boxx* playerview, PlayerLiveTV* playerObj)
 {   
-    ttdecoder=TTdecoder;
-    pv=playerview;
-    player=playerObj;
-    subtitlemode=false;
-    
-    
-
-    if (Video::getInstance()->getFormat() == Video::PAL)
-    { 
-        //setSize(680, 550);
-        setSize(680,22); //Only first line
-        setPosition(40, 26);
-    }
-    else
-    {
-        setPosition(40, 30);
-        //setSize(680, 450);
-        setSize(680,18);//only first line
-    }
-    createBuffer();
-    keyindigit=1;
-    page=0x100;
-    
+  ttdecoder = TTdecoder;
+  pv = playerview;
+  player = playerObj;
+
+  if (Video::getInstance()->getFormat() == Video::PAL)
+  {
+    //setSize(680, 550);
+    setSize(680,22); //Only first line
+    setPosition(40, 26);
+  }
+  else
+  {
+    setPosition(40, 30);
+    //setSize(680, 450);
+    setSize(680,18);//only first line
+  }
+
+  createBuffer();
+  keyindigit = 1;
+  page = 0x100;
 }
 
-VTeletextView::~VTeletextView ()
+VTeletextView::~VTeletextView()
 {
-    // Make sure the timer is deleted
-	Log::getInstance()->log("VTeletextView", Log::DEBUG, "VTeletextView destruct");
+  Log::getInstance()->log("VTeletextView", Log::DEBUG, "VTeletextView destruct");
   pv->draw();
   BoxStack::getInstance()->update(pv);
-  Timers::getInstance()->cancelTimer(this, 1);
   ttdecoder->unRegisterTeletextView(this);
-    
 }
 
 void VTeletextView::draw(bool completedraw, bool onlyfirstline)
 {
-	//Log::getInstance()->log("VTeletextView", Log::ERR, "Start draw");
-    Boxx::draw();
-    int x,y;
+  //Log::getInstance()->log("VTeletextView", Log::ERR, "Start draw");
+  Boxx::draw();
+  int x, y;
     
-    Boxx *drawtarget=NULL;
-    int ox,oy;
-    for (y=0;y<25;y++) {
-    	if (y==0) {
-    		drawtarget=this;
-    		ox=0;
-    		oy=0;
-    	} else {
-    		drawtarget=pv;
-    		ox=this->getScreenX();
-    		oy=this->getScreenY();
-    	}
-
-        for (x=0;x<40;x++) {
-            if (ttdecoder->isDirty(x,y) || completedraw) {
-                cTeletextChar c=ttdecoder->getChar(x,y);
-                c.SetDirty(false);
-                //Skip Blinking and conceal
-                drawtarget->drawTTChar(ox,oy,x,y,c);
-                ttdecoder->setChar(x,y,c);
-            }
-        }
-    //    Log::getInstance()->log("VTeletextView", Log::ERR, "Line %d",y);
-        if (onlyfirstline) break;
+  Boxx* drawtarget = NULL;
+  int ox, oy;
+  for (y = 0; y < 25; y++)
+  {
+    if (y == 0)
+    {
+      drawtarget = this;
+      ox = 0;
+      oy = 0;
+    }
+    else
+    {
+      drawtarget = pv;
+      ox = this->getScreenX();
+      oy = this->getScreenY();
+    }
+
+    for (x = 0; x < 40; x++)
+    {
+      if (ttdecoder->isDirty(x, y) || completedraw)
+      {
+        cTeletextChar c = ttdecoder->getChar(x, y);
+        c.SetDirty(false);
+        //Skip Blinking and conceal
+        drawtarget->drawTTChar(ox, oy, x, y, c);
+        ttdecoder->setChar(x, y, c);
+      }
     }
+//    Log::getInstance()->log("VTeletextView", Log::ERR, "Line %d",y);
+    if (onlyfirstline) break;
+  }
   //  Log::getInstance()->log("VTeletextView", Log::ERR, "Start end");
-   
-    
 }
 
-
-
-
-
-int VTeletextView::handleCommand(int command) {
-    if (subtitlemode) return 0; //Ok we are in  subtitle mode, we are a slave of the player
-    switch (command) {
-    case Input::OK:
+int VTeletextView::handleCommand(int command)
+{
+    if (subtitlemode) return 0; //Ok we are in subtitle mode, we are a slave of the player
+    switch (command)
+    {
+      case Input::OK:
         return 2;
-    case Input::BACK:
+      case Input::BACK:
         if(player) player->tellSubtitlesOSDVisible(false); // Only on liveTV
         return 4;
-    case Input::ZERO:
-    case Input::ONE:
-    case Input::TWO:
-    case Input::THREE:
-    case Input::FOUR:
-    case Input::FIVE:
-    case Input::SIX:
-    case Input::SEVEN:
-    case Input::EIGHT:
-    case Input::NINE:
-    {
-      // key in teletext page
-      doKey(command);
-      return 2;
+      case Input::ZERO:
+      case Input::ONE:
+      case Input::TWO:
+      case Input::THREE:
+      case Input::FOUR:
+      case Input::FIVE:
+      case Input::SIX:
+      case Input::SEVEN:
+      case Input::EIGHT:
+      case Input::NINE:
+      {
+        // key in teletext page
+        doKey(command);
+        return 2;
+      }
     }
-    };
 
     return 0;
-    
 }
 
 void VTeletextView::doKey(int command)
 {
-    char pagenums[3];
-    if (keyindigit==1){
-        if (command==9) return; //not allowed
-        page=command<<8;
-        pagenums[0]=command+ 48;
-        pagenums[1]='-';
-        pagenums[2]='-';
-        keyindigit++;
-    } else if (keyindigit==2) {
-        page|=command<<4;
-        pagenums[0]=48+((page &0xF00)>>8);
-        pagenums[1]=command+ 48;
-        pagenums[2]='-';
-        keyindigit++;
-    } else if (keyindigit==3) {
-        page|=command;
-        pagenums[0]=48+((page &0xF00)>>8);
-        pagenums[1]=48+((page &0x0F0)>>4);
-        pagenums[2]=48+command;
-        keyindigit=1;
-        ttdecoder->setPage(page);
-    }
-    ttdecoder->setKeyinDigits(pagenums,true);
-    Region toupdate;
-    toupdate.w=16*40;
-    if (Video::getInstance()->getFormat() == Video::PAL) {
-        toupdate.h=22;
-        
-     } else {
-        toupdate.h=18;
-        
-      }
-     toupdate.x=0;
-     toupdate.y=0;
-            
-     draw(false,true);
-     BoxStack::getInstance()->update(this,&toupdate);
-
-}
-
-void VTeletextView::timercall(int /* clientReference */)
-{
-    
+  char pagenums[3];
+  if (keyindigit == 1)
+  {
+    if (command == 9) return; //not allowed
+    page = command << 8;
+    pagenums[0] = static_cast<char>(command + 48);
+    pagenums[1] = '-';
+    pagenums[2] = '-';
+    keyindigit++;
+  }
+  else if (keyindigit == 2)
+  {
+    page |= command << 4;
+    pagenums[0] = static_cast<char>(48 + ((page & 0xF00) >> 8));
+    pagenums[1] = static_cast<char>(command + 48);
+    pagenums[2] = '-';
+    keyindigit++;
+  }
+  else if (keyindigit == 3)
+  {
+    page |= command;
+    pagenums[0] = static_cast<char>(48 + ((page & 0xF00) >> 8));
+    pagenums[1] = static_cast<char>(48 + ((page & 0x0F0) >> 4));
+    pagenums[2] = static_cast<char>(48 + command);
+    keyindigit = 1;
+    ttdecoder->setPage(page);
+  }
+
+  ttdecoder->setKeyinDigits(pagenums, true);
+  Region toupdate;
+  toupdate.w = 16 * 40;
+  if (Video::getInstance()->getFormat() == Video::PAL)
+  {
+    toupdate.h = 22;
+  }
+  else
+  {
+    toupdate.h = 18;
+  }
+  toupdate.x = 0;
+  toupdate.y = 0;
+
+  draw(false, true);
+  BoxStack::getInstance()->update(this, &toupdate);
 }
 
 void VTeletextView::processMessage(Message* m)
 {
-    if (m->message == Message::TELETEXTUPDATE)
+  if (m->message == Message::TELETEXTUPDATE)
+  {
+    draw(false, false);
+    BoxStack::getInstance()->update(this);
+    BoxStack::getInstance()->update(pv);
+  }
+  else if (m->message == Message::TELETEXTUPDATEFIRSTLINE)
+  {
+    Region toupdate;
+    toupdate.w = 16 * 40;
+    if (Video::getInstance()->getFormat() == Video::PAL)
     {
-        draw(false,false);
-        BoxStack::getInstance()->update(this);
-        BoxStack::getInstance()->update(pv);
-
-    } else if (m->message == Message::TELETEXTUPDATEFIRSTLINE) 
+      toupdate.h = 22;
+    }
+    else
     {
-        Region toupdate;
-        toupdate.w=16*40;
-        if (Video::getInstance()->getFormat() == Video::PAL) {
-            toupdate.h=22;
-            
-        } else {
-            toupdate.h=18;
-            
-        }
-        toupdate.x=0;
-        toupdate.y=0;
-
-
-            
-        draw(false,true);
-        BoxStack::getInstance()->update(this,&toupdate);
-
+      toupdate.h = 18;
     }
-}
-
+    toupdate.x = 0;
+    toupdate.y = 0;
 
+    draw(false, true);
+    BoxStack::getInstance()->update(this, &toupdate);
+  }
+}
diff --git a/vteletextview.h b/vteletextview.h
index d3d1f35..5e7ac45 100644
--- a/vteletextview.h
+++ b/vteletextview.h
@@ -14,55 +14,42 @@
     GNU General Public License for more details.
 
     You should have received a copy of the GNU General Public License
-    along with VOMP; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+    along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
 #ifndef VTELETEXTVIEW_H
 #define VTELETEXTVIEW_H
 
-#include <stdio.h>
-
 #include "boxx.h"
-#include "timerreceiver.h"
+
 #include "teletextdecodervbiebu.h"
 
 class PlayerLiveTV;
 
-class VTeletextView : public Boxx, public TimerReceiver
+class VTeletextView : public Boxx
 {
   public:
-    VTeletextView (TeletextDecoderVBIEBU* TTdecoder, Boxx* playerview,
-       PlayerLiveTV* palyerObj);
-    ~VTeletextView ();
+    VTeletextView(TeletextDecoderVBIEBU* TTdecoder, Boxx* playerview, PlayerLiveTV* palyerObj);
+    ~VTeletextView();
     void draw(bool completedraw, bool onlyfirstline);
-    void draw() {draw(true,false);};
-
-
-
-
+    void draw() { draw(true, false); }
     void processMessage(Message* m);
 
-    void setSubtitleMode(bool mode) {subtitlemode=mode;};
-    bool isInSubtitleMode() {return subtitlemode;};
+    void setSubtitleMode(bool mode) { subtitlemode = mode; }
+    bool isInSubtitleMode() { return subtitlemode; }
 
     int handleCommand(int command);
-    void timercall(int clientReference);
-   
 
   private:
-      void doKey(int command);
-    
+    void doKey(int command);
 
   protected:
-      TeletextDecoderVBIEBU* ttdecoder;
-      int keyindigit;
-      int page;
-      bool subtitlemode;
-      Boxx* pv;
-      PlayerLiveTV* player;
-    
-
+    TeletextDecoderVBIEBU* ttdecoder;
+    int keyindigit;
+    int page;
+    bool subtitlemode{};
+    Boxx* pv;
+    PlayerLiveTV* player;
 };
 
 #endif
diff --git a/vtimeredit.cc b/vtimeredit.cc
index 0d2f97b..a66aca0 100644
--- a/vtimeredit.cc
+++ b/vtimeredit.cc
@@ -27,7 +27,6 @@
 #include "colour.h"
 #include "video.h"
 #include "i18n.h"
-#include "timers.h"
 #include "vquestion.h"
 #include "messagequeue.h"
 #include "staticartwork.h"
diff --git a/vtimerlist.cc b/vtimerlist.cc
index c96c8b0..59bdd21 100644
--- a/vtimerlist.cc
+++ b/vtimerlist.cc
@@ -28,7 +28,6 @@
 #include "colour.h"
 #include "video.h"
 #include "i18n.h"
-#include "timers.h"
 #include "vtimeredit.h"
 #include "command.h"
 #include "boxstack.h"
diff --git a/vtimerlist.h b/vtimerlist.h
index 21219bf..966ac37 100644
--- a/vtimerlist.h
+++ b/vtimerlist.h
@@ -22,7 +22,7 @@
 
 #include "vdr.h"
 #include "tbboxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
 #include "wselectlist.h"
 #include "region.h"
 
diff --git a/vvideolivetv.cc b/vvideolivetv.cc
index bce617f..c09a0ba 100644
--- a/vvideolivetv.cc
+++ b/vvideolivetv.cc
@@ -17,7 +17,7 @@
     along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-#include "vvideolivetv.h"
+#include <sstream>
 
 #include "vchannellist.h"
 #include "video.h"
@@ -37,7 +37,6 @@
 #include "vaudioselector.h"
 #include "colour.h"
 #include "event.h"
-#include "timers.h"
 #include "vepg.h"
 #include "bitmap.h"
 #include "log.h"
@@ -48,7 +47,8 @@
 #include "demuxer.h"
 #include "messagequeue.h"
 
-#include <sstream>
+#include "vvideolivetv.h"
+
 
 VVideoLiveTV::VVideoLiveTV(ChannelList* tchanList, ULONG initialChannelNumber, VChannelList* tvchannelList)
 {
diff --git a/vvideolivetv.h b/vvideolivetv.h
index 22077df..7d083c2 100644
--- a/vvideolivetv.h
+++ b/vvideolivetv.h
@@ -30,7 +30,7 @@
 #include "vdr.h"
 #include "wtextbox.h"
 #include "wselectlist.h"
-#include "timerreceiver.h"
+#include "timers.h"
 #include "wsymbol.h"
 #include "wprogressbar.h"
 #include "osdreceiver.h"
diff --git a/vvideomedia.cc b/vvideomedia.cc
index efb2bdc..b2f26c8 100644
--- a/vvideomedia.cc
+++ b/vvideomedia.cc
@@ -27,7 +27,6 @@
 #include "wsymbol.h"
 #include "audio.h"
 #include "video.h"
-#include "timers.h"
 #include "playermedia.h"
 #include "recording.h"
 #include "vaudioselector.h"
diff --git a/vvideomedia.h b/vvideomedia.h
index f45c222..da51337 100644
--- a/vvideomedia.h
+++ b/vvideomedia.h
@@ -24,13 +24,12 @@
 #include <stdio.h>
 
 #include "boxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
 #include "wwss.h"
 #include "region.h"
 #include "colour.h"
 #include "recinfo.h"
 
-class Timers;
 class PlayerMedia;
 class Recording;
 class VAudioSelector;
diff --git a/vvideorec.cc b/vvideorec.cc
index 478f8a0..f212b94 100644
--- a/vvideorec.cc
+++ b/vvideorec.cc
@@ -25,7 +25,6 @@
 #include "audio.h"
 #include "vdr.h"
 #include "video.h"
-#include "timers.h"
 #include "player.h"
 #include "recording.h"
 #include "vaudioselector.h"
@@ -40,6 +39,7 @@
 #include "channel.h"
 #include "vteletextview.h"
 #include "messagequeue.h"
+
 #include "vvideorec.h"
 
 VVideoRec::VVideoRec(Recording* rec, bool ish264)
@@ -59,10 +59,6 @@ VVideoRec::VVideoRec(Recording* rec, bool ish264)
   player = new Player(Command::getInstance(), this, this);
   player->init(myRec->IsPesRecording,myRec->recInfo->fps);
 
-  playing = false;
-
-  startMargin = 0;
-  endMargin = 0;
   char* cstartMargin = vdr->configLoad("Timers", "Start margin");
   char* cendMargin = vdr->configLoad("Timers", "End margin");
   if (!cstartMargin)
@@ -95,7 +91,7 @@ VVideoRec::VVideoRec(Recording* rec, bool ish264)
   OsdVector* osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
   if (osdv)
   {
-	  osdv->updateBackgroundColor(DrawStyle::BLACK);
+    osdv->updateBackgroundColor(DrawStyle::BLACK);
   }
 
   barRegion.x = 0;
@@ -121,8 +117,6 @@ VVideoRec::VVideoRec(Recording* rec, bool ish264)
   vdisplay.y=0;
   vdisplay.width=0;
   vdisplay.height=0;
-
-  lastbar = -1;
 }
 
 void VVideoRec::preDelete()
diff --git a/vvideorec.h b/vvideorec.h
index 6c2823c..9dfa4fd 100644
--- a/vvideorec.h
+++ b/vvideorec.h
@@ -23,7 +23,7 @@
 #include <stdio.h>
 
 #include "boxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
 #include "region.h"
 #include "colour.h"
 #include "osdreceiver.h"
@@ -31,7 +31,6 @@
 #include "video.h"
 
 class VDR;
-class Timers;
 class Player;
 class Recording;
 class VAudioSelector;
@@ -77,7 +76,7 @@ class VVideoRec : public Boxx, public TimerReceiver, public OSDReceiver
     UCHAR videoMode;
     void toggleChopSides();
 
-    bool playing;
+    bool playing{};
 
     bool barShowing;
     bool barGenHold;
@@ -94,10 +93,10 @@ class VVideoRec : public Boxx, public TimerReceiver, public OSDReceiver
     Region barRegion;
     Region clocksRegion;
 
-    UINT startMargin;
-    UINT endMargin;
+    UINT startMargin{};
+    UINT endMargin{};
 
-    int lastbar;
+    int lastbar{-1};
     
     VInfo* vsummary;
 };
diff --git a/vvolume.cc b/vvolume.cc
index 3728a54..c0fc58a 100644
--- a/vvolume.cc
+++ b/vvolume.cc
@@ -24,7 +24,6 @@
 #include "wsymbol.h"
 #include "colour.h"
 #include "video.h"
-#include "timers.h"
 #include "boxstack.h"
 #include "messagequeue.h"
 
diff --git a/vvolume.h b/vvolume.h
index a1a670b..400cd8c 100644
--- a/vvolume.h
+++ b/vvolume.h
@@ -24,7 +24,7 @@
 #include <stdio.h>
 
 #include "boxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
 
 class VVolume : public Boxx, public TimerReceiver
 {
diff --git a/vwelcome.cc b/vwelcome.cc
index bb8b070..efc1d2b 100644
--- a/vwelcome.cc
+++ b/vwelcome.cc
@@ -17,8 +17,8 @@
     along with VOMP.  If not, see <https://www.gnu.org/licenses/>.
 */
 
-#include "vwelcome.h"
 
+#include "log.h"
 #include "input.h"
 #include "vdr.h"
 #include "vchannellist.h"
@@ -30,14 +30,13 @@
 #include "colour.h"
 #include "video.h"
 #include "i18n.h"
-#include "timers.h"
 #include "vscreensaver.h"
 #include "vmedialist.h"
 #include "boxstack.h"
 #include "vopts.h"
 #include "staticartwork.h"
 
-#include "log.h"
+#include "vwelcome.h"
 
 VWelcome::VWelcome()
 {
diff --git a/vwelcome.h b/vwelcome.h
index fd70295..a0dfecc 100644
--- a/vwelcome.h
+++ b/vwelcome.h
@@ -25,7 +25,7 @@
 #include <string.h>
 
 #include "tbboxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
 #include "wselectlist.h"
 #ifndef GRADIENT_DRAWING
 #include "wjpeg.h"
-- 
2.39.5