From 0c512ec34da8d3f9e69636ce8cff541499b01a18 Mon Sep 17 00:00:00 2001
From: Chris Tallon <chris@vomp.tv>
Date: Sun, 4 Dec 2005 19:44:07 +0000
Subject: [PATCH] Move EPG to the right a bit, timers fixes and improvements

---
 colour.cc      |   1 +
 command.cc     |   4 ++
 message.h      |   1 +
 timers.cc      | 101 ++++++++++++++++++++++++++-----------------------
 timers.h       |  27 +++++++++++--
 vepg.cc        |   2 +-
 viewman.cc     |   1 -
 vlivebanner.cc |  82 +++++++++++++++++++++++++++++++++++----
 vlivebanner.h  |   5 ++-
 vmute.cc       |   7 +++-
 vmute.h        |   1 +
 vvideolive.cc  |  27 +++++++++++--
 vvideolive.h   |   1 -
 vvolume.cc     |   7 +++-
 vvolume.h      |   1 +
 vwelcome.cc    |  10 +++--
 16 files changed, 208 insertions(+), 70 deletions(-)

diff --git a/colour.cc b/colour.cc
index cf1d38a..06c9992 100644
--- a/colour.cc
+++ b/colour.cc
@@ -25,6 +25,7 @@ Real colours
 */
 Colour Colour::BLACK(0, 0, 0);
 Colour Colour::RED(255, 0, 0);
+Colour Colour::GREEN(0, 255, 0);
 Colour Colour::VIDEOBLUE(0, 0, 150);
 Colour Colour::VIEWBACKGROUND(0, 0, 100);
 Colour Colour::TITLEBARBACKGROUND(0, 0, 200);
diff --git a/command.cc b/command.cc
index 60c9f8c..16f98ec 100644
--- a/command.cc
+++ b/command.cc
@@ -215,6 +215,10 @@ void Command::processMessage(Message* m)
       // objects deriving from messagequeues, make them derive from
       // messagereceiver - then one messagequeue can deliver any message to anywhere
 
+      // Try to segfault
+      logger->log("Command", Log::DEBUG, "1: %p", m);
+      logger->log("Command", Log::DEBUG, "2: %p", m->to);
+      logger->log("Command", Log::DEBUG, "3: %lu", m->parameter);
 
       // deliver timer
 
diff --git a/message.h b/message.h
index 873b4bc..b238ec4 100644
--- a/message.h
+++ b/message.h
@@ -55,6 +55,7 @@ class Message
     const static ULONG CHILD_CLOSE = 17;
     const static ULONG REDRAW_LANG = 18;
     const static ULONG TIMER = 19;
+    const static ULONG EPG = 20;
 };
 
 #endif
diff --git a/timers.cc b/timers.cc
index d1ac5e8..280cc73 100755
--- a/timers.cc
+++ b/timers.cc
@@ -48,7 +48,7 @@ int Timers::init()
   logger->log("Timers", Log::DEBUG, "Timers init start");
 
   threadLock(); // lock here, the thread loop will unlock and wait
-logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 1");
+  //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 1");
   if (!threadStart())
   {
     shutdown();
@@ -84,66 +84,71 @@ int Timers::shutdown()
   return 1;
 }
 
-int Timers::setTimer(TimerReceiver* client, int clientReference, time_t requestedTime)
+int Timers::setTimer(TimerReceiver* client, int clientReference, long int requestedTime, long int requestedTimeNSEC)
 {
   if (!initted) return 0;
 
   logger->log("Timers", Log::DEBUG, "Starting set timer 1");
 
+  //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 2");
+  threadLock();
+
+  // Check that this timer is not already in the list
+  TimerList::iterator i;
+  Timer* currentTimer = NULL;
+  for(i = timerList.begin(); i != timerList.end(); i++)
+  {
+    currentTimer = *i;
+    if ((currentTimer->client == client) && (currentTimer->clientReference == clientReference))
+    {
+      // Overwrite an existing timer
+      currentTimer->requestedTime.tv_sec = requestedTime;
+      currentTimer->requestedTime.tv_nsec = requestedTimeNSEC;
+      resetThreadFlag = true;
+      threadSignalNoLock();
+
+      //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2 (b)");
+      threadUnlock();
+      return 0;
+    }
+  }
+
   Timer* t = new Timer();
   t->client = client;
   t->clientReference = clientReference;
   t->requestedTime.tv_sec = requestedTime;
-  t->requestedTime.tv_nsec = 0;
+  t->requestedTime.tv_nsec = requestedTimeNSEC;
 
-logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 2");
-  threadLock();
-logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 2");
+  //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");
+  //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 2");
   threadUnlock();
 
-  logger->log("Timers", Log::DEBUG, "1 Have set timer for %p ref %i", client, clientReference);
+  logger->log("Timers", Log::DEBUG, "Timer set for %p ref %i", client, clientReference);
 
   return 1;
 }
 
 int Timers::setTimer(TimerReceiver* client, int clientReference, struct timespec duration)
 {
-  if (!initted) return 0;
-
-  logger->log("Timers", Log::DEBUG, "Starting set timer 2");
-
-  Timer* t = new Timer();
-  t->client = client;
-  t->clientReference = clientReference;
-
   struct timespec currentTime;
   clock_gettime(CLOCK_REALTIME, &currentTime);
 
-  t->requestedTime.tv_sec = currentTime.tv_sec + duration.tv_sec;
-  t->requestedTime.tv_nsec = currentTime.tv_nsec + duration.tv_nsec;
-  if (t->requestedTime.tv_nsec > 999999999)
+  long int requestedTime;
+  long int requestedTimeNSEC;
+
+  requestedTime = currentTime.tv_sec + duration.tv_sec;
+  requestedTimeNSEC = currentTime.tv_nsec + duration.tv_nsec;
+  if (requestedTimeNSEC > 999999999)
   {
-    ++t->requestedTime.tv_sec;
-    t->requestedTime.tv_nsec -= 1000000000;
+    ++requestedTime;
+    requestedTimeNSEC -= 1000000000;
     logger->log("Timers", Log::DEBUG, "Second rollover - CHECK FIXME");
   }
 
-logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 3");
-  threadLock();
-logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 3");
-  timerList.push_back(t);
-  resetThreadFlag = true;
-  threadSignalNoLock();
-logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 3");
-  threadUnlock();
-
-  logger->log("Timers", Log::DEBUG, "2 Have set timer for %p ref %i", client, clientReference);
-
-  return 1;
+  return setTimer(client, clientReference, requestedTime, requestedTimeNSEC);
 }
 
 int Timers::cancelTimer(TimerReceiver* client, int clientReference)
@@ -152,15 +157,15 @@ int Timers::cancelTimer(TimerReceiver* client, int clientReference)
 
   logger->log("Timers", Log::DEBUG, "Starting cancel timer %p %i, list size = %i", client, clientReference, timerList.size());
 
-logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 4");
+  //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 4");
   threadLock();
-logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 4");
+  //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 4");
   TimerList::iterator i;
   Timer* currentTimer = NULL;
   for(i = timerList.begin(); i != timerList.end(); i++)
   {
     currentTimer = *i;
-    logger->log("Timers", Log::DEBUG, "I: %p %i : %p %i", client, clientReference, currentTimer->client, currentTimer->clientReference);
+    //logger->log("Timers", Log::DEBUG, "I: %p %i : %p %i", client, clientReference, currentTimer->client, currentTimer->clientReference);
     if ((currentTimer->client == client) && (currentTimer->clientReference == clientReference))
     {
       timerList.erase(i);
@@ -173,14 +178,14 @@ logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 4");
   {
     // no timer found
     logger->log("Timers", Log::DEBUG, "No timer found in cancelTimer %p ref %i", client, clientReference);
-logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");
+    //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");
     threadUnlock();
     return 0;
   }
 
   resetThreadFlag = true;
   threadSignalNoLock();
-logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");
+  //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX 4");
   threadUnlock();
 
 
@@ -241,20 +246,20 @@ void Timers::threadMethod()
 
     if (nextTimer)
     {
-      logger->log("Timers", Log::DEBUG, "List size: %i. nextTimer: %p. nextTime.tv_sec: %li. nextTime.tv_nsec: %li", timerList.size(), nextTimer, nextTime.tv_sec, nextTime.tv_nsec);
+      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)");
+      //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (1)");
       threadWaitForSignalTimed(&nextTime); // FIXME does this work if the time is in the past?
-logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 5");
+      //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)");
+      //logger->log("Timers", Log::DEBUG, "about to un-LOCK -TIMERS- MUTEX (2)");
       threadWaitForSignal();
-logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");
+      //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");
       // unlocks in the wait
     }
 
@@ -290,12 +295,12 @@ logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 6");
       // now unlock the timers mutex for a fraction of a second
       // in case the gui thread is waiting on the timers mutex
       threadUnlock();
-logger->log("Timers", Log::DEBUG, "un-LOCKED -TIMERS- MUTEX (3)");
-      printf("\n\n\n WOOOOO \n\n\n The anti deadlock code is working!!! \n\n\n");
-      usleep(10000); // 5ms - too long?
-logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 7");
+      //logger->log("Timers", Log::DEBUG, "un-LOCKED -TIMERS- MUTEX (3)");
+      //printf("\n\n\n WOOOOO \n\n\n The anti deadlock code is working!!! \n\n\n");
+      usleep(10000); // 10ms - too long?
+      //logger->log("Timers", Log::DEBUG, "Waiting for LOCK -TIMERS- MUTEX 7");
       threadLock();
-logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 7");
+      //logger->log("Timers", Log::DEBUG, "LOCKED -TIMERS- MUTEX 7");
       resetThreadFlag = true;
     }
     else
diff --git a/timers.h b/timers.h
index 3a88b06..c9e20ec 100755
--- a/timers.h
+++ b/timers.h
@@ -31,6 +31,29 @@
 #include "command.h"
 #include "timerreceiver.h"
 
+// FIXME - ensure all objects that call settimer call cancel timer if they are being deleted
+
+/*
+
+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
+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
+a constant pulse.
+
+clientReference is any int of your choice. It will be supplied back to you in the timercall()
+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.
+
+*/
+
 class Timer
 {
   public:
@@ -53,9 +76,7 @@ class Timers : public Thread
     int init();
     int shutdown();
 
-    // FIXME - ensure all objects that call settimer call cancel timer if they are being deleted
-
-    int setTimer(TimerReceiver* client, int clientReference, time_t requestedTime);
+    int setTimer(TimerReceiver* client, int clientReference, long int requestedTime, long int requestedTimeNSEC=0);
     int setTimer(TimerReceiver* client, int clientReference, timespec duration);
     int cancelTimer(TimerReceiver* client, int clientReference);
 
diff --git a/vepg.cc b/vepg.cc
index 190a401..a28b53b 100644
--- a/vepg.cc
+++ b/vepg.cc
@@ -50,7 +50,7 @@ VEpg::VEpg(VVideoLive* v, UINT currentChannel)
   if (Video::getInstance()->getFormat() == Video::PAL)
   {
     create(632, 520);
-    setScreenPos(44, 30);
+    setScreenPos(60, 30);
   }
   else
   {
diff --git a/viewman.cc b/viewman.cc
index 5c106ef..ab5ab97 100644
--- a/viewman.cc
+++ b/viewman.cc
@@ -130,7 +130,6 @@ void ViewMan::deleteView(int z)
   }
 }
 
-// FIXME - make this take a optional region for smaller updates (not whole views)
 void ViewMan::updateView(View* toUpdate, Region* regionToUpdate)
 {
   // Get the z index of the view
diff --git a/vlivebanner.cc b/vlivebanner.cc
index 4af19cd..9b27599 100644
--- a/vlivebanner.cc
+++ b/vlivebanner.cc
@@ -20,12 +20,20 @@
 
 #include "vlivebanner.h"
 
+VLiveBanner* VLiveBanner::instance = NULL;
+
 VLiveBanner::VLiveBanner(View* tparent, Channel* channel, bool bannerTakesCommands)
 {
+  instance = this;
   eventList = NULL;
   parent = tparent;
   takeCommands = bannerTakesCommands;
 
+  clockRegion.x = 440;
+  clockRegion.y = 0;
+  clockRegion.w = 60;
+  clockRegion.h = 30;
+
   create(500, 120);
   if (Video::getInstance()->getFormat() == Video::PAL)
   {
@@ -51,9 +59,17 @@ VLiveBanner::VLiveBanner(View* tparent, Channel* channel, bool bannerTakesComman
 
 VLiveBanner::~VLiveBanner()
 {
+  instance = NULL;
+  Timers::getInstance()->cancelTimer(this, 1);
+  Timers::getInstance()->cancelTimer(this, 2);
   delData();
 }
 
+VLiveBanner* VLiveBanner::getInstance()
+{
+  return instance;
+}
+
 void VLiveBanner::delData()
 {
   if (eventList)
@@ -111,7 +127,6 @@ void VLiveBanner::setChannel(Channel* tChannel)
     }
 
     // Reset the timer as it probably took 1-2 seconds to change the channel
-    Timers::getInstance()->cancelTimer(this, 1); // if it exists
     Timers::getInstance()->setTimer(this, 1, (struct timespec){4, 0});
   }
 }
@@ -124,6 +139,11 @@ void VLiveBanner::draw()
 
   rectangle(7, area.h - 24, 18, 16, Colour::RED);
   drawText(tr("info"), 32, area.h - 25, Colour::LIGHTTEXT);
+
+  rectangle(110, area.h - 24, 18, 16, Colour::GREEN);
+  drawText(tr("EPG"), 135, area.h - 25, Colour::LIGHTTEXT);
+
+  drawClock();
 }
 
 int VLiveBanner::handleCommand(int command)
@@ -146,6 +166,10 @@ int VLiveBanner::handleCommand(int command)
       sl.draw();
 
       show();
+
+      // Arrows pressed, go to an 8s timer
+      Timers::getInstance()->setTimer(this, 1, (struct timespec){8, 0});
+
       return 2;
     }
     case Remote::DF_DOWN:
@@ -158,6 +182,10 @@ int VLiveBanner::handleCommand(int command)
       sl.draw();
 
       show();
+
+      // Arrows pressed, go to an 8s timer
+      Timers::getInstance()->setTimer(this, 1, (struct timespec){8, 0});
+
       return 2;
     }
     case Remote::CHANNELUP:
@@ -225,6 +253,17 @@ int VLiveBanner::handleCommand(int command)
       }
       return 2; // should not get here
     }
+    case Remote::GREEN:
+    {
+      // full epg
+      Timers::getInstance()->cancelTimer(this, 1); // if it exists
+      Message* m = new Message();
+      m->message = Message::EPG;
+      m->to = parent;
+      m->from = this;
+      ViewMan::getInstance()->postMessage(m);
+      return 4;
+    }
   }
 
   return 1;
@@ -232,10 +271,39 @@ int VLiveBanner::handleCommand(int command)
 
 void VLiveBanner::timercall(int clientReference)
 {
-  // delete me!
-  Message* m = new Message();
-  m->message = Message::CLOSE_ME;
-  m->to = ViewMan::getInstance();
-  m->from = this;
-  ViewMan::getInstance()->postMessage(m);
+  if (clientReference == 1)
+  {
+    // delete me!
+    Message* m = new Message();
+    m->message = Message::CLOSE_ME;
+    m->to = ViewMan::getInstance();
+    m->from = this;
+    ViewMan::getInstance()->postMessage(m);
+  }
+  else if (clientReference == 2)
+  {
+    // redraw clock
+    drawClock();
+    ViewMan::getInstance()->updateView(this, &clockRegion);
+  }
+}
+
+void VLiveBanner::drawClock()
+{
+  // Blank the area first
+  rectangle(area.w - 60, 0, 60, 30, titleBarColour);
+
+  char timeString[20];
+  time_t t;
+  time(&t);
+  struct tm* tms = localtime(&t);
+  strftime(timeString, 19, "%H:%M", tms);
+  drawTextRJ(timeString, 490, 5, Colour::LIGHTTEXT);
+
+  time_t dt = 60 - (t % 60);  // seconds to the next minute
+  if (dt == 0) dt = 60; // advance a whole minute if necessary
+  dt += t;  // get a time_t value for it rather than using duration
+  // (so it will occur at the actual second and not second and a half)
+
+  Timers::getInstance()->setTimer(this, 2, dt);
 }
diff --git a/vlivebanner.h b/vlivebanner.h
index fe36207..05f2f78 100644
--- a/vlivebanner.h
+++ b/vlivebanner.h
@@ -44,6 +44,7 @@ class VLiveBanner : public View, public TimerReceiver
   public:
     VLiveBanner(View* parent, Channel* channel, bool bannerTakesCommands);
     ~VLiveBanner();
+    static VLiveBanner* getInstance();
     void delData();
 
     void setChannel(Channel* channel);
@@ -53,12 +54,14 @@ class VLiveBanner : public View, public TimerReceiver
     void timercall(int clientReference);
 
   private:
+    static VLiveBanner* instance;
     View* parent;
     WSelectList sl;
     Channel* currentChannel;
     EventList* eventList;
     bool takeCommands;
-
+    void drawClock();
+    Region clockRegion;
 };
 
 #endif
diff --git a/vmute.cc b/vmute.cc
index c7fee0c..46d6b1f 100644
--- a/vmute.cc
+++ b/vmute.cc
@@ -37,6 +37,12 @@ VMute::VMute()
   setBackgroundColour(Colour::VIEWBACKGROUND);
 }
 
+VMute::~VMute()
+{
+  // Make sure the timer is deleted
+  Timers::getInstance()->cancelTimer(this, 1);
+}
+
 void VMute::draw()
 {
   View::draw();
@@ -48,7 +54,6 @@ void VMute::draw()
   w.setSurfaceOffset(5, 5);
   w.draw();
 
-  Timers::getInstance()->cancelTimer(this, 1); // if it exists
   Timers::getInstance()->setTimer(this, 1, (struct timespec){2, 0});
 }
 
diff --git a/vmute.h b/vmute.h
index 263729d..3709115 100644
--- a/vmute.h
+++ b/vmute.h
@@ -37,6 +37,7 @@ class VMute : public View, public TimerReceiver
 {
   public:
     VMute();
+    ~VMute();
     void draw();
     int handleCommand(int command);
     void timercall(int clientReference);
diff --git a/vvideolive.cc b/vvideolive.cc
index f96b5bc..8dfd9c6 100644
--- a/vvideolive.cc
+++ b/vvideolive.cc
@@ -33,7 +33,6 @@ VVideoLive::VVideoLive(ChannelList* tchanList, ULONG tstreamType)
   unavailable = 0;
   unavailableView = NULL;
   streamType = tstreamType;
-  vlb = (VLiveBanner*)1; // Can't be NULL because then that is sent to ViewMan::remove and it takes the top view off .. FIXME!
   epgmode=false;
   if (streamType == VDR::RADIO) player = new PlayerVideo(Command::getInstance(), 0, 1);
   else                          player = new PlayerVideo(Command::getInstance(), 0, 0);
@@ -97,6 +96,13 @@ int VVideoLive::handleCommand(int command)
       channelChange(OFFSET, DOWN);
       return 2;
     }
+    case Remote::PREVCHANNEL:
+    {
+      if (unavailable) showUnavailable(0);
+      else stop();
+      channelChange(PREVIOUS, 0);
+      return 2;
+    }
     case Remote::OK:
     {
       doBanner(true);
@@ -174,6 +180,7 @@ void VVideoLive::processMessage(Message* m)
     channelChange(OFFSET, UP);
     if(!epgmode)
     {
+      VLiveBanner* vlb = VLiveBanner::getInstance(); // guaranteed to be one
       vlb->setChannel((*chanList)[currentChannel]);
       vlb->draw();
       vlb->show();
@@ -185,6 +192,7 @@ void VVideoLive::processMessage(Message* m)
     channelChange(OFFSET, DOWN);
     if(!epgmode)
     {
+      VLiveBanner* vlb = VLiveBanner::getInstance(); // guaranteed to be one
       vlb->setChannel((*chanList)[currentChannel]);
       vlb->draw();
       vlb->show();
@@ -196,13 +204,26 @@ void VVideoLive::processMessage(Message* m)
     stop();
     play(1);
   }
+  else if (m->message == Message::EPG)
+  {
+    Log::getInstance()->log("VVideoLive", Log::DEBUG, "EPG requested from live banner");
+
+    if (!epgmode)
+    {
+      showEPG();
+      epgmode=!epgmode; // shouldn't this be within the braces? // same for above in handleCommand, ask Brian FIXME
+    }
+  }
 }
 
 void VVideoLive::doBanner(bool bannerTakesCommands)
 {
   if(epgmode)
     return;
-  vlb = new VLiveBanner(this, (*chanList)[currentChannel], bannerTakesCommands);
+
+  if (VLiveBanner::getInstance()) return; // there already is one
+
+  VLiveBanner* vlb = new VLiveBanner(this, (*chanList)[currentChannel], bannerTakesCommands);
 
   Message* m = new Message();
   m->from = this;
@@ -274,7 +295,7 @@ void VVideoLive::stop(int noRemoveVLB)
 printf("1\n");
   if (unavailable) return;
 printf("2\n");
-  if (!noRemoveVLB) viewman->removeView(vlb); // if live banner is present, remove it. won't cause damage if its not present
+  if (!noRemoveVLB && VLiveBanner::getInstance()) viewman->removeView(VLiveBanner::getInstance()); // if live banner is present, remove it. won't cause damage if its not present
 printf("3\n");
 
   player->stop();
diff --git a/vvideolive.h b/vvideolive.h
index ae6e51e..0f496b4 100644
--- a/vvideolive.h
+++ b/vvideolive.h
@@ -82,7 +82,6 @@ class VVideoLive : public View
     int unavailable;
     VInfo* unavailableView;
     ULONG streamType;
-    VLiveBanner* vlb;
 
     UINT upChannel();
     UINT downChannel();
diff --git a/vvolume.cc b/vvolume.cc
index b9de8cb..c6adf7d 100644
--- a/vvolume.cc
+++ b/vvolume.cc
@@ -37,6 +37,12 @@ VVolume::VVolume()
   setBackgroundColour(Colour::VIEWBACKGROUND);
 }
 
+VVolume::~VVolume()
+{
+  // Make sure the timer is deleted
+  Timers::getInstance()->cancelTimer(this, 1);
+}
+
 void VVolume::draw()
 {
   View::draw();
@@ -63,7 +69,6 @@ void VVolume::draw()
     w.draw();
   }
 
-  Timers::getInstance()->cancelTimer(this, 1); // if it exists
   Timers::getInstance()->setTimer(this, 1, (struct timespec){2, 0});
 }
 
diff --git a/vvolume.h b/vvolume.h
index a7915b9..ce24717 100644
--- a/vvolume.h
+++ b/vvolume.h
@@ -37,6 +37,7 @@ class VVolume : public View, public TimerReceiver
 {
   public:
     VVolume();
+    ~VVolume();
     void draw();
     int handleCommand(int command);
     void timercall(int clientReference);
diff --git a/vwelcome.cc b/vwelcome.cc
index 7ee0c6f..cd2ebca 100644
--- a/vwelcome.cc
+++ b/vwelcome.cc
@@ -98,9 +98,13 @@ void VWelcome::drawClock()
   struct tm* tms = localtime(&t);
   strftime(timeString, 19, "%H:%M", tms);
   drawTextRJ(timeString, 450, 5, Colour::LIGHTTEXT);
-  time_t dt = 60 - (t % 60);
-  if (dt == 0) dt = 60;
-  Timers::getInstance()->setTimer(this, 1, (struct timespec){dt, 0});
+
+  time_t dt = 60 - (t % 60);  // seconds to the next minute
+  if (dt == 0) dt = 60; // advance a whole minute if necessary
+  dt += t;  // get a time_t value for it rather than using duration
+  // (so it will occur at the actual second and not second and a half)
+
+  Timers::getInstance()->setTimer(this, 1, dt);
 }
 
 void VWelcome::timercall(int clientReference)
-- 
2.39.5