From 0c512ec34da8d3f9e69636ce8cff541499b01a18 Mon Sep 17 00:00:00 2001 From: Chris Tallon 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, ¤tTime); - 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