Reporting of many bugs
Audio channel selection
VDR 1.7 compatibility
+ Raspberry Pi port
Andreas Vogel
Media player
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"
#include "media.h"
#include "mediaplayer.h"
+#include "audioplayer.h"
+
//how often do we retry if there is no data in stream
#define MAXTRY 50
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
#include "messagequeue.h"
#include "thread.h"
#include "afeed.h"
-#include "timerreceiver.h"
+#include "timers.h"
#ifdef WIN32
#include "threadwin.h"
#endif
-
class Boxx;
class DemuxerAudio;
class MediaURI;
#include "colour.h"
#include "osd.h"
#include "i18n.h"
-#include "timerreceiver.h"
#include "timers.h"
#include "wol.h"
#include "vconnect.h"
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);
#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"
#include "thread.h"
#include "afeed.h"
#include "vfeed.h"
-#include "timerreceiver.h"
+#include "timers.h"
#include "video.h"
#ifdef WIN32
/*
- Copyright 2004-2007 Chris Tallon
+ Copyright 2004-2020 Chris Tallon
This file is part of VOMP.
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;
{
if (instance) return;
instance = this;
- initted = false;
}
Timers::~Timers()
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
}
}
}
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(¤tTime);
+ 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)
/* 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
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
}
-
-
-
-
-
-
-
-
-
-/*
-
-
-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.
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
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();
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
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 !
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;
void VChannelSelect::timercall(int /*clientReference*/)
{
- Log::getInstance()->log("VChannelSelect", Log::DEBUG, "Timer call");
if (ignoreTimer) return;
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);
#include <math.h>
#include "boxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
class VChannelSelect : public Boxx, public TimerReceiver
{
}
// 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;
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"
#include "i18n.h"
#include "log.h"
+#include "vepg.h"
VEpg* VEpg::instance = NULL;
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"
#include "tbboxx.h"
#include "wselectlist.h"
#include "timers.h"
-#include "timerreceiver.h"
class DirList;
class Message;
#include "vpicturebanner.h"
#include "vcolourtuner.h"
#include "audioplayer.h"
-#include "timers.h"
#include "boxx.h"
#include "wselectlist.h"
#include "input.h"
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"
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();
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
#include <stdio.h>
#include "boxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
class VMute : public Boxx, public TimerReceiver
{
/*
- Copyright 2004-2019 Chris Tallon
+ Copyright 2004-2020 Chris Tallon
This file is part of VOMP.
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"
#include "message.h"
#include "vdr.h"
#include "video.h"
-#include "timers.h"
#include "playerradio.h"
#include "boxstack.h"
#include "input.h"
#include "log.h"
#include "messagequeue.h"
+#include "vradiorec.h"
+
VRadioRec::VRadioRec(Recording* rec)
{
boxstack = BoxStack::getInstance();
/*
- Copyright 2004-2006 Chris Tallon
+ Copyright 2004-2020 Chris Tallon
This file is part of VOMP.
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"
class VDR;
class Video;
class PlayerRadio;
-class Timers;
class BoxStack;
class VRadioRec : public Boxx, public TimerReceiver
#include "wsymbol.h"
#include "colour.h"
#include "video.h"
-#include "timers.h"
#include "boxstack.h"
#include "command.h"
#include "messagequeue.h"
#include <stdio.h>
#include "boxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
#include "threadsystem.h"
/*
- Copyright 2005-2008 Chris Tallon, Marten Richter
+ Copyright 2005-2020 Chris Tallon, Marten Richter
This file is part of VOMP.
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);
+ }
+}
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
#include "colour.h"
#include "video.h"
#include "i18n.h"
-#include "timers.h"
#include "vquestion.h"
#include "messagequeue.h"
#include "staticartwork.h"
#include "colour.h"
#include "video.h"
#include "i18n.h"
-#include "timers.h"
#include "vtimeredit.h"
#include "command.h"
#include "boxstack.h"
#include "vdr.h"
#include "tbboxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
#include "wselectlist.h"
#include "region.h"
along with VOMP. If not, see <https://www.gnu.org/licenses/>.
*/
-#include "vvideolivetv.h"
+#include <sstream>
#include "vchannellist.h"
#include "video.h"
#include "vaudioselector.h"
#include "colour.h"
#include "event.h"
-#include "timers.h"
#include "vepg.h"
#include "bitmap.h"
#include "log.h"
#include "demuxer.h"
#include "messagequeue.h"
-#include <sstream>
+#include "vvideolivetv.h"
+
VVideoLiveTV::VVideoLiveTV(ChannelList* tchanList, ULONG initialChannelNumber, VChannelList* tvchannelList)
{
#include "vdr.h"
#include "wtextbox.h"
#include "wselectlist.h"
-#include "timerreceiver.h"
+#include "timers.h"
#include "wsymbol.h"
#include "wprogressbar.h"
#include "osdreceiver.h"
#include "wsymbol.h"
#include "audio.h"
#include "video.h"
-#include "timers.h"
#include "playermedia.h"
#include "recording.h"
#include "vaudioselector.h"
#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;
#include "audio.h"
#include "vdr.h"
#include "video.h"
-#include "timers.h"
#include "player.h"
#include "recording.h"
#include "vaudioselector.h"
#include "channel.h"
#include "vteletextview.h"
#include "messagequeue.h"
+
#include "vvideorec.h"
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)
OsdVector* osdv=dynamic_cast<OsdVector*>(Osd::getInstance());
if (osdv)
{
- osdv->updateBackgroundColor(DrawStyle::BLACK);
+ osdv->updateBackgroundColor(DrawStyle::BLACK);
}
barRegion.x = 0;
vdisplay.y=0;
vdisplay.width=0;
vdisplay.height=0;
-
- lastbar = -1;
}
void VVideoRec::preDelete()
#include <stdio.h>
#include "boxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
#include "region.h"
#include "colour.h"
#include "osdreceiver.h"
#include "video.h"
class VDR;
-class Timers;
class Player;
class Recording;
class VAudioSelector;
UCHAR videoMode;
void toggleChopSides();
- bool playing;
+ bool playing{};
bool barShowing;
bool barGenHold;
Region barRegion;
Region clocksRegion;
- UINT startMargin;
- UINT endMargin;
+ UINT startMargin{};
+ UINT endMargin{};
- int lastbar;
+ int lastbar{-1};
VInfo* vsummary;
};
#include "wsymbol.h"
#include "colour.h"
#include "video.h"
-#include "timers.h"
#include "boxstack.h"
#include "messagequeue.h"
#include <stdio.h>
#include "boxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
class VVolume : public Boxx, public TimerReceiver
{
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"
#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()
{
#include <string.h>
#include "tbboxx.h"
-#include "timerreceiver.h"
+#include "timers.h"
#include "wselectlist.h"
#ifndef GRADIENT_DRAWING
#include "wjpeg.h"