summaryrefslogtreecommitdiff
path: root/watch.c
diff options
context:
space:
mode:
Diffstat (limited to 'watch.c')
-rw-r--r--watch.c874
1 files changed, 874 insertions, 0 deletions
diff --git a/watch.c b/watch.c
new file mode 100644
index 0000000..179ae1d
--- /dev/null
+++ b/watch.c
@@ -0,0 +1,874 @@
+/*
+ * targavfd plugin for VDR (C++)
+ *
+ * (C) 2010 Andreas Brachold <vdr07 AT deltab de>
+ *
+ * This targavfd plugin is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation, version 3 of the License.
+ *
+ * See the files README and COPYING for details.
+ *
+ */
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <time.h>
+
+#include "watch.h"
+#include "setup.h"
+
+#include <vdr/tools.h>
+
+struct cMutexLooker {
+ cMutex& mutex;
+ cMutexLooker(cMutex& m):
+ mutex(m){
+ mutex.Lock();
+ }
+ virtual ~cMutexLooker() {
+ mutex.Unlock();
+ }
+};
+
+
+cVFDWatch::cVFDWatch()
+: cThread("targaVFD: watch thread")
+, m_bShutdown(false)
+{
+ m_nIconsForceOn = 0;
+ m_nIconsForceOff = 0;
+ m_nIconsForceMask = 0;
+
+ unsigned int n;
+ for(n=0;n<memberof(m_nCardIsRecording);++n) {
+ m_nCardIsRecording[n] = 0;
+ }
+ chPresentTime = 0;
+ chFollowingTime = 0;
+ chName = NULL;
+ chPresentTitle = NULL;
+ chPresentShortTitle = NULL;
+
+ m_nLastVolume = cDevice::CurrentVolume();
+ m_bVolumeMute = false;
+
+ osdTitle = NULL;
+ osdItem = NULL;
+ osdMessage = NULL;
+
+ m_pControl = NULL;
+ replayTitle = NULL;
+ replayTitleLast = NULL;
+ replayTime = NULL;
+
+ currentTime = NULL;
+
+ m_eWatchMode = eLiveTV;
+
+ m_nScrollOffset = -1;
+ m_bScrollBackward = false;
+ m_bScrollNeeded = false;
+}
+
+cVFDWatch::~cVFDWatch()
+{
+ close();
+ if(chName) {
+ delete chName;
+ chName = NULL;
+ }
+ if(chPresentTitle) {
+ delete chPresentTitle;
+ chPresentTitle = NULL;
+ }
+ if(chPresentShortTitle) {
+ delete chPresentShortTitle;
+ chPresentShortTitle = NULL;
+ }
+ if(osdMessage) {
+ delete osdMessage;
+ osdMessage = NULL;
+ }
+ if(osdTitle) {
+ delete osdTitle;
+ osdTitle = NULL;
+ }
+ if(osdItem) {
+ delete osdItem;
+ osdItem = NULL;
+ }
+ if(replayTitle) {
+ delete replayTitle;
+ replayTitle = NULL;
+ }
+ if(replayTime) {
+ delete replayTime;
+ replayTime = NULL;
+ }
+ if(currentTime) {
+ delete currentTime;
+ currentTime = NULL;
+ }
+}
+
+bool cVFDWatch::open() {
+ if(cVFD::open()) {
+ m_bShutdown = false;
+ m_bUpdateScreen = true;
+ Start();
+ return true;
+ }
+ return false;
+}
+
+void cVFDWatch::close() {
+
+ if(Running()) {
+ m_bShutdown = true;
+ usleep(500000);
+ Cancel();
+ }
+
+ if(this->isopen()) {
+ cTimer* t = Timers.GetNextActiveTimer();
+
+ switch(theSetup.m_nOnExit) {
+ case eOnExitMode_NEXTTIMER: {
+ isyslog("targaVFD: closing, show only next timer.");
+
+ this->clear();
+ if(t) {
+ struct tm l;
+ cString topic;
+ time_t tn = time(NULL);
+ time_t tt = t->StartTime();
+ localtime_r(&tt, &l);
+ if((tt - tn) > 86400) {
+ // next timer more then 24h
+ topic = cString::sprintf("%d. %02d:%02d %s", l.tm_mday, l.tm_hour, l.tm_min, t->File());
+ } else {
+ // next timer (today)
+ topic = cString::sprintf("%02d:%02d %s", l.tm_hour, l.tm_min, t->File());
+ }
+ this->DrawText(0,0,topic);
+ this->icons(eIconRECORD);
+ } else {
+ this->DrawText(0,0,tr("None active timer"));
+ this->icons(0);
+ }
+ this->flush();
+ break;
+ }
+ case eOnExitMode_SHOWMSG: {
+ isyslog("targaVFD: closing, leaving \"last\" message.");
+ break;
+ }
+ default:
+ case eOnExitMode_BLANKSCREEN: {
+ isyslog("targaVFD: closing, turning backlight off.");
+ SendCmdShutdown();
+ break;
+ }
+ case eOnExitMode_SHOWCLOCK: {
+ isyslog("targaVFD: closing, showing clock.");
+ SendCmdClock();
+ break;
+ }
+ }
+ }
+ cVFD::close();
+}
+
+void cVFDWatch::Action(void)
+{
+ unsigned int nLastIcons = -1;
+ int nBrightness = -1;
+
+ unsigned int n;
+ unsigned int nCnt = 0;
+
+ cTimeMs runTime;
+
+ for (;!m_bShutdown;++nCnt) {
+
+ LOCK_THREAD;
+
+ unsigned int nIcons = 0;
+ bool bFlush = false;
+ bool bReDraw = false;
+ if(m_bShutdown)
+ break;
+ else {
+ cMutexLooker m(mutex);
+ runTime.Set();
+
+ // every second the clock need updates.
+ if (theSetup.m_bTwoLineMode) {
+ if((0 == (nCnt % 5))) {
+ if(m_eWatchMode == eLiveTV) {
+ bReDraw = CurrentTime();
+ } else {
+ bReDraw = ReplayTime();
+ }
+ }
+ }
+
+ bFlush = RenderScreen(bReDraw);
+ if(m_eWatchMode == eLiveTV) {
+ //if((chFollowingTime - chPresentTime) > 0) {
+ // nBottomProgressBar = (time(NULL) - chPresentTime) * 32 / (chFollowingTime - chPresentTime);
+ // if(nBottomProgressBar > 32) nBottomProgressBar = 32;
+ // if(nBottomProgressBar < 0) nBottomProgressBar = 0;
+ //} else {
+ // nBottomProgressBar = 0;
+ //}
+ } else {
+ switch(ReplayMode()) {
+ case eReplayNone:
+ case eReplayPaused:
+ nIcons |= eIconPAUSE;
+ break;
+ default:
+ case eReplayPlay:
+ nIcons |= eIconPLAY;
+ break;
+ case eReplayBackward1:
+ case eReplayBackward2:
+ case eReplayBackward3:
+ break;
+ case eReplayForward1:
+ case eReplayForward2:
+ case eReplayForward3:
+ break;
+ }
+ }
+
+ for(n=0;n<memberof(m_nCardIsRecording);++n) {
+ if(0 != m_nCardIsRecording[n]) {
+ nIcons |= eIconRECORD;
+ break;
+ }
+ }
+
+ if(m_bVolumeMute) {
+ nIcons |= eIconMUTE;
+ } else {
+ nIcons |= eIconVOLUME;
+ const int nVolSteps = (MAXVOLUME/14);
+ nIcons |= (((1 << (m_nLastVolume / nVolSteps)) - 1) << 0x0B);
+ }
+
+ if(theSetup.m_nBrightness != nBrightness) {
+ nBrightness = theSetup.m_nBrightness;
+ Brightness(nBrightness);
+ }
+
+ //Force icon state (defined by svdrp)
+ nIcons &= ~(m_nIconsForceMask);
+ nIcons |= (m_nIconsForceOn);
+ nIcons &= ~(m_nIconsForceOff);
+
+ if(nIcons != nLastIcons) {
+ icons(nIcons);
+ nLastIcons = nIcons;
+ bFlush = true;
+ }
+
+ if(bFlush) {
+ flush();
+ }
+ }
+ int nDelay = 100 - runTime.Elapsed();
+ if(nDelay <= 10) {
+ nDelay = 10;
+ }
+ cCondWait::SleepMs(nDelay);
+ }
+ dsyslog("targaVFD: watch thread closed (pid=%d)", getpid());
+}
+
+bool cVFDWatch::RenderScreen(bool bReDraw) {
+ cString* scRender;
+ cString* scHeader = NULL;
+ bool bForce = m_bUpdateScreen;
+ if(osdMessage) {
+ scRender = osdMessage;
+ } else if(osdItem) {
+ scHeader = osdTitle;
+ scRender = osdItem;
+ } else if(m_eWatchMode == eLiveTV) {
+ scHeader = chName;
+ if(Program()) {
+ bForce = true;
+ }
+ if(chPresentTitle)
+ scRender = chPresentTitle;
+ else {
+ scHeader = currentTime;
+ scRender = chName;
+ }
+ } else {
+ if(Replay()) {
+ bForce = true;
+ }
+ scHeader = replayTime;
+ scRender = replayTitle;
+ }
+
+
+ if(bForce) {
+ m_nScrollOffset = 0;
+ m_bScrollBackward = false;
+ m_bScrollNeeded = true;
+ }
+ if(bForce || bReDraw || m_nScrollOffset > 0 || m_bScrollBackward) {
+ this->clear();
+ if(scRender) {
+
+ int iRet = -1;
+ if(theSetup.m_bTwoLineMode) {
+ iRet = this->DrawText(0 - m_nScrollOffset,8, *scRender);
+ } else {
+ iRet = this->DrawText(0 - m_nScrollOffset,0, *scRender);
+ }
+ if(m_bScrollNeeded) {
+ switch(iRet) {
+ case 0:
+ if(m_nScrollOffset <= 0) {
+ m_nScrollOffset = 0;
+ m_bScrollBackward = false;
+ m_bScrollNeeded = false;
+ break; //Fit to screen
+ }
+ m_bScrollBackward = true;
+ case 2:
+ case 1:
+ if(m_bScrollBackward) m_nScrollOffset -= 2;
+ else m_nScrollOffset += 2;
+ if(m_nScrollOffset >= 0) {
+ break;
+ }
+ case -1:
+ m_nScrollOffset = 0;
+ m_bScrollBackward = false;
+ m_bScrollNeeded = false;
+ break;
+ }
+ }
+ }
+
+ if(scHeader && theSetup.m_bTwoLineMode) {
+ this->DrawText(0, 0, *scHeader);
+ }
+
+ m_bUpdateScreen = false;
+ return true;
+ }
+ return false;
+}
+
+bool cVFDWatch::CurrentTime() {
+ time_t ts = time(NULL);
+
+ if((ts / 60) != (tsCurrentLast / 60)) {
+
+ if(currentTime)
+ delete currentTime;
+
+ tsCurrentLast = ts;
+ currentTime = new cString(TimeString(ts));
+ return currentTime != NULL;
+ }
+ return false;
+}
+
+bool cVFDWatch::Replay() {
+
+ if(!replayTitleLast
+ || !replayTitle
+ || strcmp(*replayTitleLast,*replayTitle)) {
+ replayTitleLast = replayTitle;
+ return true;
+ }
+ return false;
+}
+
+void cVFDWatch::Replaying(const cControl * Control, const char * Name, const char *FileName, bool On)
+{
+ cMutexLooker m(mutex);
+ m_bUpdateScreen = true;
+ if (On)
+ {
+ m_pControl = (cControl *) Control;
+ m_eWatchMode = eReplayNormal;
+ if(replayTitle) {
+ delete replayTitle;
+ replayTitle = NULL;
+ }
+ if (Name && !isempty(Name))
+ {
+ int slen = strlen(Name);
+ bool bFound = false;
+ ///////////////////////////////////////////////////////////////////////
+ //Looking for mp3/muggle-plugin replay : [LS] (444/666) title
+ //
+ if (slen > 6 &&
+ *(Name+0)=='[' &&
+ *(Name+3)==']' &&
+ *(Name+5)=='(') {
+ unsigned int i;
+ for (i=6; *(Name + i) != '\0'; ++i) { //search for [xx] (xxxx) title
+ if (*(Name+i)==' ' && *(Name+i-1)==')') {
+ bFound = true;
+ break;
+ }
+ }
+ if (bFound) { //found mp3/muggle-plugin
+ if (strlen(Name+i) > 0) { //if name isn't empty, then copy
+ replayTitle = new cString(skipspace(Name + i));
+ }
+ m_eWatchMode = eReplayMusic;
+ }
+ }
+ ///////////////////////////////////////////////////////////////////////
+ //Looking for DVD-Plugin replay : 1/8 4/28, de 2/5 ac3, no 0/7, 16:9, VOLUMENAME
+ // cDvdPlayerControl::GetDisplayHeaderLine
+ // titleinfo, audiolang, spulang, aspect, title
+ if (!bFound && slen>7)
+ {
+ unsigned int i,n;
+ for (n=0,i=0;*(Name+i) != '\0';++i)
+ { //search volumelabel after 4*", " => xxx, xxx, xxx, xxx, title
+ if (*(Name+i)==' ' && *(Name+i-1)==',') {
+ if (++n == 4) {
+ bFound = true;
+ break;
+ }
+ }
+ }
+ if (bFound) //found DVD replay
+ {
+ if (strlen(Name+i) > 0)
+ { // if name isn't empty, then copy
+ replayTitle = new cString(skipspace(Name + i));
+ }
+ m_eWatchMode = eReplayDVD;
+ }
+ }
+ if (!bFound) {
+ int i;
+ for (i=slen-1;i>0;--i)
+ { //search reverse last Subtitle
+ // - filename contains '~' => subdirectory
+ // or filename contains '/' => subdirectory
+ switch (*(Name+i)) {
+ case '/': {
+ // look for file extentsion like .xxx or .xxxx
+ if (slen>5 && ((*(Name+slen-4) == '.') || (*(Name+slen-5) == '.')))
+ {
+ m_eWatchMode = eReplayFile;
+ }
+ else
+ {
+ break;
+ }
+ }
+ case '~': {
+ replayTitle = new cString(Name + i + 1);
+ bFound = true;
+ i = 0;
+ }
+ default:
+ break;
+ }
+ }
+ }
+
+ if (0 == strncmp(Name,"[image] ",8)) {
+ if (m_eWatchMode != eReplayFile) //if'nt already Name stripped-down as filename
+ replayTitle = new cString(Name + 8);
+ m_eWatchMode = eReplayImage;
+ bFound = true;
+ }
+ else if (0 == strncmp(Name,"[audiocd] ",10)) {
+ replayTitle = new cString(Name + 10);
+ m_eWatchMode = eReplayAudioCD;
+ bFound = true;
+ }
+ if (!bFound) {
+ replayTitle = new cString(Name);
+ }
+ }
+ if (!replayTitle) {
+ replayTitle = new cString(tr("Unknown title"));
+ }
+ }
+ else
+ {
+ m_eWatchMode = eLiveTV;
+ m_pControl = NULL;
+ }
+}
+
+
+eReplayState cVFDWatch::ReplayMode() const
+{
+ bool Play = false, Forward = false;
+ int Speed = -1;
+ if (m_pControl
+ && ((cControl *)m_pControl)->GetReplayMode(Play,Forward,Speed))
+ {
+ // 'Play' tells whether we are playing or pausing, 'Forward' tells whether
+ // we are going forward or backward and 'Speed' is -1 if this is normal
+ // play/pause mode, 0 if it is single speed fast/slow forward/back mode
+ // and >0 if this is multi speed mode.
+ switch(Speed) {
+ default:
+ case -1:
+ return Play ? eReplayPlay : eReplayPaused;
+ case 0:
+ case 1:
+ return Forward ? eReplayForward1 : eReplayBackward1;
+ case 2:
+ return Forward ? eReplayForward2 : eReplayBackward2;
+ case 3:
+ return Forward ? eReplayForward3 : eReplayBackward3;
+ }
+ }
+ return eReplayNone;
+}
+
+bool cVFDWatch::ReplayPosition(int &current, int &total) const
+{
+ if (m_pControl && ((cControl *)m_pControl)->GetIndex(current, total, false)) {
+ total = (total == 0) ? 1 : total;
+ return true;
+ }
+ return false;
+}
+
+const char * cVFDWatch::FormatReplayTime(int current, int total) const
+{
+ static char s[32];
+#if VDRVERSNUM >= 10701
+ int cs = (current / DEFAULTFRAMESPERSECOND);
+ int ts = (total / DEFAULTFRAMESPERSECOND);
+ bool g = (((current / DEFAULTFRAMESPERSECOND) / 3600) > 0)
+ || (((total / DEFAULTFRAMESPERSECOND) / 3600) > 0);
+#else
+ int cs = (current / FRAMESPERSEC);
+ int ts = (total / FRAMESPERSEC);
+ bool g = (((current / FRAMESPERSEC) / 3600) > 0)
+ || (((total / FRAMESPERSEC) / 3600) > 0);
+#endif
+ int cm = cs / 60;
+ cs %= 60;
+ int tm = ts / 60;
+ ts %= 60;
+
+ if (total > 1) {
+ if(g) {
+ snprintf(s, sizeof(s), "%s (%s)", (const char*)IndexToHMSF(current), (const char*)IndexToHMSF(total));
+ } else {
+ snprintf(s, sizeof(s), "%02d:%02d (%02d:%02d)", cm, cs, tm, ts);
+ }
+ }
+ else {
+ if(g) {
+ snprintf(s, sizeof(s), "%s", (const char*)IndexToHMSF(current));
+ } else {
+ snprintf(s, sizeof(s), "%02d:%02d", cm, cs);
+ }
+ }
+ return s;
+}
+
+bool cVFDWatch::ReplayTime() {
+ int current = 0,total = 0;
+ if(ReplayPosition(current,total)) {
+ const char * sz = FormatReplayTime(current,total);
+ if(!replayTime || strcmp(sz,*replayTime)) {
+ if(replayTime)
+ delete replayTime;
+ replayTime = new cString(sz);
+ return replayTime != NULL;
+ }
+ }
+ return false;
+}
+
+void cVFDWatch::Recording(const cDevice *pDevice, const char *szName, const char *szFileName, bool bOn)
+{
+ cMutexLooker m(mutex);
+
+ unsigned int nCardIndex = pDevice->CardIndex();
+ if (nCardIndex > memberof(m_nCardIsRecording) - 1 )
+ nCardIndex = memberof(m_nCardIsRecording)-1;
+
+ if (nCardIndex < memberof(m_nCardIsRecording)) {
+ if (bOn) {
+ ++m_nCardIsRecording[nCardIndex];
+ }
+ else {
+ if (m_nCardIsRecording[nCardIndex] > 0)
+ --m_nCardIsRecording[nCardIndex];
+ }
+ }
+ else {
+ esyslog("targaVFD: Recording: only up to %d devices are supported by this plugin", memberof(m_nCardIsRecording));
+ }
+}
+
+void cVFDWatch::Channel(int ChannelNumber)
+{
+ cMutexLooker m(mutex);
+ if(chPresentTitle) {
+ delete chPresentTitle;
+ chPresentTitle = NULL;
+ }
+ if(chPresentShortTitle) {
+ delete chPresentShortTitle;
+ chPresentShortTitle = NULL;
+ }
+ if(chName) {
+ delete chName;
+ chName = NULL;
+ }
+
+ cChannel * ch = Channels.GetByNumber(ChannelNumber);
+ if(ch) {
+ chID = ch->GetChannelID();
+ chPresentTime = 0;
+ chFollowingTime = 0;
+ if (!isempty(ch->Name())) {
+ chName = new cString(ch->Name());
+ }
+ }
+ m_eWatchMode = eLiveTV;
+ m_bUpdateScreen = true;
+ m_nScrollOffset = 0;
+ m_bScrollBackward = false;
+}
+
+bool cVFDWatch::Program() {
+
+ bool bChanged = false;
+ const cEvent * p = NULL;
+ cSchedulesLock schedulesLock;
+ const cSchedules * schedules = cSchedules::Schedules(schedulesLock);
+ if (chID.Valid() && schedules)
+ {
+ const cSchedule * schedule = schedules->GetSchedule(chID);
+ if (schedule)
+ {
+ if ((p = schedule->GetPresentEvent()) != NULL)
+ {
+ if(chPresentTime && chEventID == p->EventID()) {
+ return false;
+ }
+
+ bChanged = true;
+ chEventID = p->EventID();
+ chPresentTime = p->StartTime();
+ chFollowingTime = p->EndTime();
+
+ if(chPresentTitle) {
+ delete chPresentTitle;
+ chPresentTitle = NULL;
+ }
+ if (!isempty(p->Title())) {
+ chPresentTitle = new cString(p->Title());
+ }
+
+ if(chPresentShortTitle) {
+ delete chPresentShortTitle;
+ chPresentShortTitle = NULL;
+ }
+ if (!isempty(p->ShortText())) {
+ chPresentShortTitle = new cString(p->ShortText());
+ }
+ }
+ }
+ }
+ return bChanged;
+}
+
+
+void cVFDWatch::Volume(int nVolume, bool bAbsolute)
+{
+ cMutexLooker m(mutex);
+
+ int nAbsVolume;
+
+ nAbsVolume = m_nLastVolume;
+ if (bAbsolute) {
+ nAbsVolume = nVolume;
+ } else {
+ nAbsVolume += nVolume;
+ }
+ if(nAbsVolume > MAXVOLUME) {
+ nAbsVolume = MAXVOLUME;
+ }
+ else if(nAbsVolume < 0) {
+ nAbsVolume = 0;
+ }
+
+ if(m_nLastVolume > 0 && 0 == nAbsVolume) {
+ m_bVolumeMute = true;
+ }
+ else if(0 == m_nLastVolume && nAbsVolume > 0) {
+ m_bVolumeMute = false;
+ }
+ m_nLastVolume = nAbsVolume;
+}
+
+
+void cVFDWatch::OsdClear() {
+ cMutexLooker m(mutex);
+ if(osdMessage) {
+ delete osdMessage;
+ osdMessage = NULL;
+ m_bUpdateScreen = true;
+ }
+ if(osdTitle) {
+ delete osdTitle;
+ osdTitle = NULL;
+ m_bUpdateScreen = true;
+ }
+ if(osdItem) {
+ delete osdItem;
+ osdItem = NULL;
+ m_bUpdateScreen = true;
+ }
+}
+
+void cVFDWatch::OsdTitle(const char *sz) {
+ char *s = NULL;
+ char *sc = NULL;
+ if(sz && !isempty(sz)) {
+ s = strdup(sz);
+ sc = compactspace(strreplace(s,'\t',' '));
+ }
+ if(sc
+ && osdTitle
+ && 0 == strcmp(sc, *osdTitle)) {
+ if(s) {
+ free(s);
+ }
+ return;
+ }
+ cMutexLooker m(mutex);
+ if(osdTitle) {
+ delete osdTitle;
+ osdTitle = NULL;
+ m_bUpdateScreen = true;
+ }
+ if(sc) {
+ osdTitle = new cString(sc);
+ m_bUpdateScreen = true;
+ }
+ if(s) {
+ free(s);
+ }
+}
+
+void cVFDWatch::OsdCurrentItem(const char *sz)
+{
+ char *s = NULL;
+ char *sc = NULL;
+ if(sz && !isempty(sz)) {
+ s = strdup(sz);
+ sc = compactspace(strreplace(s,'\t',' '));
+ }
+ if(sc
+ && osdItem
+ && 0 == strcmp(sc, *osdItem)) {
+ if(s) {
+ free(s);
+ }
+ return;
+ }
+ cMutexLooker m(mutex);
+ if(osdItem) {
+ delete osdItem;
+ osdItem = NULL;
+ m_bUpdateScreen = true;
+ }
+ if(sc) {
+ osdItem = new cString(sc);
+ m_bUpdateScreen = true;
+ }
+ if(s) {
+ free(s);
+ }
+}
+
+void cVFDWatch::OsdStatusMessage(const char *sz)
+{
+ char *s = NULL;
+ char *sc = NULL;
+ if(sz && !isempty(sz)) {
+ s = strdup(sz);
+ sc = compactspace(strreplace(s,'\t',' '));
+ }
+ if(sc
+ && osdMessage
+ && 0 == strcmp(sc, *osdMessage)) {
+ if(s) {
+ free(s);
+ }
+ return;
+ }
+ cMutexLooker m(mutex);
+ if(osdMessage) {
+ delete osdMessage;
+ osdMessage = NULL;
+ m_bUpdateScreen = true;
+ }
+ if(sc) {
+ osdMessage = new cString(sc);
+ m_bUpdateScreen = true;
+ }
+ if(s) {
+ free(s);
+ }
+}
+
+bool cVFDWatch::SetFont(const char *szFont, int bTwoLineMode) {
+ cMutexLooker m(mutex);
+ if(cVFD::SetFont(szFont, bTwoLineMode)) {
+ m_bUpdateScreen = true;
+ return true;
+ }
+ return false;
+}
+
+eIconState cVFDWatch::ForceIcon(unsigned int nIcon, eIconState nState) {
+
+ unsigned int nIconOff = nIcon;
+
+ switch(nState) {
+ case eIconStateAuto:
+ m_nIconsForceOn &= ~(nIconOff);
+ m_nIconsForceOff &= ~(nIconOff);
+ m_nIconsForceMask &= ~(nIconOff);
+ break;
+ case eIconStateOn:
+ m_nIconsForceOn |= nIcon;
+ m_nIconsForceOff &= ~(nIconOff);
+ m_nIconsForceMask |= nIconOff;
+ break;
+ case eIconStateOff:
+ m_nIconsForceOff |= nIcon;
+ m_nIconsForceOn &= ~(nIconOff);
+ m_nIconsForceMask |= nIconOff;
+ break;
+ default:
+ break;
+ }
+ if(m_nIconsForceOn & nIcon) return eIconStateOn;
+ if(m_nIconsForceOff & nIcon) return eIconStateOff;
+ return eIconStateAuto;
+}
+