summaryrefslogtreecommitdiff
path: root/display.c
diff options
context:
space:
mode:
Diffstat (limited to 'display.c')
-rw-r--r--display.c1953
1 files changed, 1953 insertions, 0 deletions
diff --git a/display.c b/display.c
new file mode 100644
index 0000000..f82d317
--- /dev/null
+++ b/display.c
@@ -0,0 +1,1953 @@
+/*
+ * GraphLCD plugin for the Video Disk Recorder
+ *
+ * display.c - display class
+ *
+ * This file is released under the GNU General Public License. Refer
+ * to the COPYING file distributed with this package.
+ *
+ * (c) 2001-2004 Carsten Siebholz <c.siebholz AT t-online.de>
+ * (c) 2004 Andreas Regel <andreas.regel AT powarman.de>
+ */
+
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/io.h>
+#include <sys/time.h>
+
+#include <algorithm>
+
+#include <glcddrivers/config.h>
+#include <glcddrivers/drivers.h>
+
+#include "display.h"
+#include "global.h"
+#include "i18n.h"
+#include "setup.h"
+#include "state.h"
+#include "strfct.h"
+
+#include <vdr/tools.h>
+#include <vdr/menu.h>
+
+#include "compat.h"
+
+#define MAXLINES_MSG 4
+#define MAXLINES_TEXT 16
+#define FILENAME_EXTERNAL_TRIGGERED_SYMBOLS "/tmp/graphlcd_symbols"
+
+// tiny: 0..48
+#define MINY_T 0
+#define MAXY_T 48
+
+// small: 49..61
+#define MINY_S (MAXY_T+1)
+#define MAXY_S 61
+
+// medium: 62..127
+#define MINY_M (MAXY_S+1)
+#define MAXY_M 127
+
+// large: 128..
+#define MINY_L (MAXY_M+1)
+#define MAXY_L 9999
+
+
+
+int FRAME_SPACE_X;
+int FRAME_SPACE_XB;
+int FRAME_SPACE_Y;
+int FRAME_SPACE_YB;
+int TEXT_OFFSET_X;
+int TEXT_OFFSET_Y_TIME;
+int TEXT_OFFSET_Y_CHANNEL;
+int TEXT_OFFSET_Y_TITLE;
+int SYMBOL_SPACE;
+int TIMEBAR_HEIGHT;
+
+
+
+cGraphLCDDisplay::cGraphLCDDisplay()
+: cThread("glcd_display"),
+ update(false),
+ active(false),
+ mLcd(NULL),
+ bitmap(NULL),
+ GraphLCDState(NULL)
+{
+ cfgDir = "";
+ fontDir = "";
+ logoDir = "";
+
+ CurrTime = time(NULL);
+ LastTime = CurrTime-58;
+ CurrTimeval.tv_sec = 0;
+ CurrTimeval.tv_usec = 0;
+ timerclear(&UpdateAt);
+ LastTimeCheckSym = CurrTime;
+
+ State = Normal;
+ LastState = Normal;
+
+ menuTop = 0;
+ menuCount = 0;
+ tabCount = 0;
+ for (int i = 0; i < kMaxTabCount; i++)
+ tab[i] = 0;
+ tabMax[0] = 0;
+ tabMax[1] = 66;
+ tabMax[2] = 100;
+ tabMax[3] = 100;
+ tabMax[4] = 100;
+ tabMax[5] = 100;
+ tabMax[6] = 100;
+ tabMax[7] = 100;
+ tabMax[8] = 100;
+ tabMax[9] = 100;
+
+ showVolume = false;
+
+ logo = NULL;
+ logoList = NULL;
+
+ strcpy (szETSymbols, "");
+
+ nCurrentBrightness = -1;
+ LastTimeBrightness = 0;
+ bBrightnessActive = true;
+}
+
+cGraphLCDDisplay::~cGraphLCDDisplay()
+{
+ active = false;
+ Cancel(3);
+
+ delete GraphLCDState;
+ delete bitmap;
+ delete logoList;
+}
+
+int cGraphLCDDisplay::Init(GLCD::cDriver * Lcd, const char * CfgDir)
+{
+ if (!Lcd || !CfgDir)
+ return 2;
+ mLcd = Lcd;
+ cfgDir = CfgDir;
+ fontDir = cfgDir + "/fonts";
+ logoDir = cfgDir + "/logos";
+
+ logoList = new cGraphLCDLogoList(logoDir.c_str(), cfgDir.c_str());
+ if (!logoList)
+ {
+ esyslog("graphlcd plugin: ERROR out of memory\n");
+ return 1;
+ }
+
+ std::string fontListFile = cfgDir + "/fonts.conf";
+ if (fontList.Load(fontListFile) == false)
+ {
+ esyslog("graphlcd plugin: ERROR: Could not load %s!\n", fontListFile.c_str());
+ return 1;
+ }
+
+ Start();
+ return 0;
+}
+
+void cGraphLCDDisplay::Tick(void)
+{
+ if (GraphLCDState)
+ GraphLCDState->Tick();
+}
+
+void cGraphLCDDisplay::Action(void)
+{
+ if (mLcd->Init() != 0)
+ {
+ esyslog("graphlcd plugin: ERROR: Failed initializing display\n");
+ return;
+ }
+
+ bitmap = new GLCD::cBitmap(mLcd->Width(), mLcd->Height());
+ if (!bitmap)
+ {
+ esyslog("graphlcd plugin: ERROR creating drawing bitmap\n");
+ return;
+ }
+
+ largeFont = fontList.GetFont("Large Font");
+ if (largeFont == NULL)
+ {
+ esyslog("graphlcd plugin: ERROR: No \"Large Font\" specified!\n");
+ return;
+ }
+ normalFont = fontList.GetFont("Normal Font");
+ if (normalFont == NULL)
+ {
+ esyslog("graphlcd plugin: ERROR: No \"Normal Font\" specified!\n");
+ return;
+ }
+ smallFont = fontList.GetFont("Small Font");
+ if (smallFont == NULL)
+ {
+ esyslog("graphlcd plugin: ERROR: No \"Small Font\" specified!\n");
+ return;
+ }
+ symbols = fontList.GetFont("Symbol Font");
+ if (symbols == NULL)
+ {
+ esyslog("graphlcd plugin: ERROR: No \"Symbol Font\" specified!\n");
+ return;
+ }
+
+ if (bitmap->Width() < 240)
+ {
+ FRAME_SPACE_X = 0;
+ FRAME_SPACE_XB = 1;
+ TEXT_OFFSET_X = 2;
+ }
+ else
+ {
+ FRAME_SPACE_X = 2;
+ FRAME_SPACE_XB = 2;
+ TEXT_OFFSET_X = 4;
+ }
+
+ if (bitmap->Height() <= MAXY_T)
+ {
+ // very small display
+ FRAME_SPACE_Y = 0;
+ FRAME_SPACE_YB = 1;
+ TEXT_OFFSET_Y_TIME = 1;
+ TEXT_OFFSET_Y_CHANNEL = 1;
+ TEXT_OFFSET_Y_TITLE = 1;
+ SYMBOL_SPACE = 1;
+ TIMEBAR_HEIGHT = 3;
+ }
+ else if (bitmap->Height() <= MAXY_S)
+ {
+ // small display
+ FRAME_SPACE_Y = 0;
+ FRAME_SPACE_YB = 1;
+ TEXT_OFFSET_Y_TIME = 1;
+ TEXT_OFFSET_Y_CHANNEL = 3;
+ TEXT_OFFSET_Y_TITLE = 1;
+ SYMBOL_SPACE = 1;
+ TIMEBAR_HEIGHT = 3;
+ }
+ else if (bitmap->Height() <= MAXY_M)
+ {
+ // medium display
+ FRAME_SPACE_Y = 0;
+ FRAME_SPACE_YB = 1;
+ TEXT_OFFSET_Y_TIME = 1;
+ TEXT_OFFSET_Y_CHANNEL = 3;
+ TEXT_OFFSET_Y_TITLE = 3;
+ SYMBOL_SPACE = 1;
+ TIMEBAR_HEIGHT = 3;
+ }
+ else
+ {
+ // large display
+ FRAME_SPACE_Y = 2;
+ FRAME_SPACE_YB = 2;
+ TEXT_OFFSET_Y_TIME = 2;
+ TEXT_OFFSET_Y_CHANNEL = 5;
+ TEXT_OFFSET_Y_TITLE = 5;
+ SYMBOL_SPACE = 2;
+ TIMEBAR_HEIGHT = 5;
+ }
+
+ GraphLCDState = new cGraphLCDState(this);
+ if (!GraphLCDState)
+ return;
+
+ mLcd->Refresh(true);
+ active = true;
+ update = true;
+ while (active)
+ {
+ if (GraphLCDSetup.PluginActive)
+ {
+ CurrTime = time(NULL);
+
+ if (timerisset(&UpdateAt))
+ {
+ // timed Update enabled
+ if (gettimeofday(&CurrTimeval, NULL) == 0)
+ {
+ // get current time
+ if (CurrTimeval.tv_sec > UpdateAt.tv_sec)
+ {
+ timerclear(&UpdateAt);
+ update = true;
+ }
+ else if (CurrTimeval.tv_sec == UpdateAt.tv_sec &&
+ CurrTimeval.tv_usec > UpdateAt.tv_usec)
+ {
+ timerclear(&UpdateAt);
+ update = true;
+ }
+ }
+ }
+ if (GraphLCDSetup.ShowVolume && !update && showVolume)
+ {
+ if (TimeMs() - GraphLCDState->GetVolumeState().lastChange > 2000)
+ {
+ update = true;
+ showVolume = false;
+ }
+ }
+
+ SetBrightness();
+
+ switch (State)
+ {
+ case Normal:
+ // check and update external triggered symbols
+ if (GraphLCDSetup.ShowETSymbols)
+ {
+ if (CurrTime != LastTimeCheckSym)
+ {
+ update |= CheckAndUpdateSymbols();
+ LastTimeCheckSym = CurrTime;
+ }
+ }
+
+ {
+ std::vector<cScroller>::iterator it;
+ for (it = scroller.begin(); it != scroller.end(); it++)
+ {
+ if (it->NeedsUpdate())
+ update = true;
+ }
+ }
+
+ // update Display if animated Logo is present, and an update is necessary
+ if (!update && IsLogoActive() && logo->Count() > 1 &&
+ (TimeMs() - logo->LastChange() >= logo->Delay()))
+ {
+ update = true;
+ }
+
+ // update Display every minute or due to an update
+ if (CurrTime/60 != LastTime/60 || update)
+ {
+ timerclear(&UpdateAt);
+ update = false;
+
+ bitmap->Clear();
+ DisplayTime();
+ DisplayLogo();
+ DisplayChannel();
+ DisplaySymbols();
+ DisplayProgramme();
+ DisplayVolume();
+ DisplayMessage();
+ mLcd->SetScreen(bitmap->Data(), bitmap->Width(), bitmap->Height(), bitmap->LineSize());
+ mLcd->Refresh(false);
+ LastTime = CurrTime;
+ }
+ else
+ {
+#if VDRVERSNUM < 10314
+ usleep(100000);
+#else
+ cCondWait::SleepMs(100);
+#endif
+ }
+ break;
+
+ case Replay:
+ {
+ tReplayState replay = GraphLCDState->GetReplayState();
+ if (replay.control)
+ {
+ {
+ update = false;
+ std::vector<cScroller>::iterator it;
+ for (it = scroller.begin(); it != scroller.end(); it++)
+ {
+ if (it->NeedsUpdate())
+ update = true;
+ }
+ }
+ // update Display if animated Logo is present, and an update is necessary
+ if (!update && IsLogoActive() && logo->Count() > 1 &&
+ TimeMs() - logo->LastChange() >= logo->Delay())
+ {
+ update = true;
+ }
+
+ // update Display every second or due to an update
+ if (CurrTime != LastTime || update)
+ {
+ // but only, if something has changed
+ if (replay.total / FRAMESPERSEC != replay.totalLast / FRAMESPERSEC ||
+ replay.current / FRAMESPERSEC != replay.currentLast / FRAMESPERSEC ||
+ CurrTime/60 != LastTime/60 ||
+ update)
+ {
+ timerclear(&UpdateAt);
+ update = false;
+ bitmap->Clear();
+ DisplayTime();
+ DisplayLogo();
+ DisplayReplay(replay);
+ //DisplaySymbols();
+ DisplayVolume();
+ DisplayMessage();
+ mLcd->SetScreen(bitmap->Data(), bitmap->Width(), bitmap->Height(), bitmap->LineSize());
+ mLcd->Refresh(false);
+ LastTime = CurrTime;
+ }
+ else
+ {
+#if VDRVERSNUM < 10314
+ usleep(100000);
+#else
+ cCondWait::SleepMs(100);
+#endif
+ }
+ }
+ else
+ {
+#if VDRVERSNUM < 10314
+ usleep(100000);
+#else
+ cCondWait::SleepMs(100);
+#endif
+ }
+ }
+ else
+ {
+ State = Normal;
+ Update();
+ }
+ }
+ break;
+
+ case Menu:
+ if (GraphLCDSetup.ShowMenu)
+ {
+ // update Display every minute or due to an update
+ if (CurrTime/60 != LastTime/60 || update)
+ {
+ timerclear(&UpdateAt);
+ update = false;
+
+ bitmap->Clear();
+ DisplayTime();
+ DisplayMenu();
+ DisplayTextItem();
+ DisplayVolume();
+ DisplayMessage();
+ DisplayColorButtons();
+ mLcd->SetScreen(bitmap->Data(), bitmap->Width(), bitmap->Height(), bitmap->LineSize());
+ mLcd->Refresh(false);
+ LastTime = CurrTime;
+ }
+ else
+ {
+#if VDRVERSNUM < 10314
+ usleep(100000);
+#else
+ cCondWait::SleepMs(100);
+#endif
+ }
+ }
+ else
+ {
+ //GraphLCDState.OsdClear();
+
+ State = LastState;
+ // activate delayed Update
+#if VDRVERSNUM < 10314
+ usleep(100000);
+#else
+ cCondWait::SleepMs(100);
+#endif
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+#if VDRVERSNUM < 10314
+ usleep(100000);
+#else
+ cCondWait::SleepMs(100);
+#endif
+ }
+ }
+}
+
+void cGraphLCDDisplay::SetChannel(int ChannelNumber)
+{
+ if (ChannelNumber == 0)
+ return;
+
+ mutex.Lock();
+ cChannel * ch = Channels.GetByNumber(ChannelNumber);
+ if (GraphLCDSetup.ShowLogo)
+ {
+ ePicType picType;
+
+ switch (GraphLCDSetup.ShowLogo)
+ {
+ case 1: // auto
+ if (bitmap->Height() <= MAXY_M)
+ picType = ptLogoMedium;
+ else
+ picType = ptLogoLarge;
+ break;
+ case 2: // medium
+ picType = ptLogoMedium;
+ break;
+ case 3: // large
+ picType = ptLogoLarge;
+ break;
+ default: // should not happen at the moment !!
+ picType = ptLogoSmall;
+ break;
+ }
+#if VDRVERSNUM >= 10300
+ char strTmp[64];
+ strcpy(strTmp, (const char *) ch->GetChannelID().ToString());
+ char * strId = strstr(strTmp, "-") + 1;
+ logo = logoList->GetLogo(strId, picType);
+#else
+ char strId[16];
+ sprintf(strId, "%d", ch->Sid());
+ logo = logoList->GetLogo(strId, picType);
+#endif
+ if (logo)
+ logo->First(TimeMs());
+ }
+ else
+ {
+ logo = NULL;
+ }
+ bBrightnessActive = true;
+ Update();
+ mutex.Unlock();
+}
+
+void cGraphLCDDisplay::SetClear()
+{
+ mutex.Lock();
+
+ textItemLines.clear();
+ textItemTop = 0;
+ tabCount = 0;
+ for (int i = 0; i < kMaxTabCount; i++)
+ tab[i] = 0;
+
+ mutex.Unlock();
+
+ if (State == Menu)
+ {
+ State = LastState;
+ // activate delayed Update
+ UpdateIn(100000);
+ }
+ else
+ {
+ Update();
+ }
+}
+
+void cGraphLCDDisplay::SetOsdTitle()
+{
+ UpdateIn(0); // stop delayed Update
+ mutex.Lock();
+ if (State != Menu)
+ {
+ menuTop = 0;
+ LastState = State;
+ State = Menu;
+ }
+ mutex.Unlock();
+ // activate delayed Update
+ UpdateIn(100000);
+}
+
+void cGraphLCDDisplay::SetOsdItem(const char * Text)
+{
+ int iAT, t;
+ std::string str;
+ std::string::size_type pos1, pos2;
+
+ mutex.Lock();
+
+ UpdateIn(0); // stop delayed Update
+ str = Text;
+ pos1 = 0;
+ pos2 = str.find('\t');
+ iAT = 0;
+ while (pos1 < str.length() && pos2 != std::string::npos)
+ {
+ iAT++;
+ t = std::min(normalFont->Width(str.substr(pos1), pos2 - pos1), (tabMax[iAT] * bitmap->Width()) / 100);
+ tab[iAT] = std::max(tab[iAT], t);
+ tabCount = std::max(tabCount, iAT);
+ pos1 = pos2 + 1;
+ pos2 = str.find('\t', pos1);
+ }
+ mutex.Unlock();
+}
+
+void cGraphLCDDisplay::SetOsdCurrentItem()
+{
+ UpdateIn(100000); //XXX
+}
+
+void cGraphLCDDisplay::Replaying(bool starting, eReplayMode replayMode)
+{
+ if (starting)
+ {
+ if (State != Menu)
+ {
+ State = Replay;
+ }
+ else
+ {
+ LastState = Replay;
+ }
+ if (GraphLCDSetup.ReplayLogo)
+ {
+ ePicType picType;
+
+ switch (GraphLCDSetup.ReplayLogo)
+ {
+ case 1: // auto
+ if (bitmap->Height() <= MAXY_M)
+ picType = ptLogoMedium;
+ else
+ picType = ptLogoLarge;
+ break;
+ case 2: // medium
+ picType = ptLogoMedium;
+ break;
+ case 3: // large
+ picType = ptLogoLarge;
+ break;
+ default: // should not happen at the moment !!
+ picType = ptLogoSmall;
+ break;
+ }
+ switch (replayMode)
+ {
+ default:
+ case eReplayNormal :
+ logo = logoList->GetLogo("REPLAY-VDR", picType);break;
+ case eReplayMusic :
+ logo = logoList->GetLogo("REPLAY-MUSIC", picType);break;
+ case eReplayDVD :
+ logo = logoList->GetLogo("REPLAY-DVD", picType);break;
+ case eReplayFile :
+ logo = logoList->GetLogo("REPLAY-FILE", picType);break;
+ case eReplayImage :
+ logo = logoList->GetLogo("REPLAY-IMAGE", picType);break;
+ case eReplayAudioCD:
+ logo = logoList->GetLogo("REPLAY-AUDIOCD", picType);break;
+ }
+ if (logo)
+ logo->First(TimeMs());
+ }
+ else
+ {
+ logo = NULL;
+ }
+ }
+ else
+ {
+ if (State != Menu)
+ {
+ State = Normal;
+ }
+ else
+ {
+ LastState = Normal;
+ }
+ }
+ bBrightnessActive = true;
+ Update();
+}
+
+void cGraphLCDDisplay::SetOsdTextItem(const char * Text, bool Scroll)
+{
+ static const char * lastText = NULL;
+ tOsdState osd;
+
+ osd = GraphLCDState->GetOsdState();
+ mutex.Lock();
+ if (Text)
+ {
+ if (osd.textItem.length() == 0)
+ lastText = NULL;
+ int maxTextLen = bitmap->Width() - 2 * FRAME_SPACE_X - 2 * TEXT_OFFSET_X;
+ normalFont->WrapText(maxTextLen, 0, osd.textItem, textItemLines);
+ textItemLines.push_back("");
+ if (lastText != Text)
+ {
+ lastText = Text;
+ textItemTop = 0;
+ }
+ }
+ else
+ {
+ if (Scroll)
+ {
+ if (textItemTop > 0)
+ textItemTop--;
+ }
+ else
+ {
+ if (textItemTop < (int) textItemLines.size() - 2)
+ textItemTop++;
+ }
+ }
+ mutex.Unlock();
+ UpdateIn(100000);
+}
+
+void cGraphLCDDisplay::Update()
+{
+ update = true;
+}
+
+void cGraphLCDDisplay::DisplayTime()
+{
+ static char buffer[32];
+ static char month[5];
+ int FrameWidth, TextLen, yPos;
+ struct tm tm_r;
+
+ if (GraphLCDSetup.ShowDateTime == 1 ||
+ (GraphLCDSetup.ShowDateTime == 2 && State != Menu))
+ {
+ FrameWidth = std::max(bitmap->Width() - 2 * FRAME_SPACE_X, 1);
+ if (State == Normal || State == Replay)
+ {
+ if (IsLogoActive()) // Logo enabled & available
+ {
+ FrameWidth = std::max(FrameWidth - FRAME_SPACE_XB - logo->Width() - 2, (unsigned int) 1);
+ }
+ if (bitmap->Height() <= MAXY_M)
+ {
+ // tiny, small & medium display
+ if (IsSymbolsActive()) // Symbols enabled
+ {
+ FrameWidth = std::max(FrameWidth - FRAME_SPACE_XB - symbols->TotalWidth(), 1);
+ }
+ }
+ }
+ yPos = FRAME_SPACE_Y;
+
+ // draw Rectangle
+ bitmap->DrawRoundRectangle(FRAME_SPACE_X, yPos,
+ FRAME_SPACE_X + FrameWidth - 1,
+ yPos + normalFont->TotalHeight() + 2 * TEXT_OFFSET_Y_TIME - 1,
+ GLCD::clrBlack, true, (TEXT_OFFSET_Y_TIME >= 2) ? 4 : 1);
+
+ if (CurrTime == 0)
+ time(&CurrTime);
+ tm * tm = localtime_r(&CurrTime, &tm_r);
+
+ strncpy(month, (char *)(tr("JanFebMarAprMayJunJulAugSepOctNovDec") + 3 * tm->tm_mon), 3);
+ month[3] = 0;
+ snprintf(buffer, sizeof(buffer), "%s %2d.%s %d:%02d", (const char *) WeekDayName(tm->tm_wday), tm->tm_mday, month, tm->tm_hour, tm->tm_min);
+ TextLen = normalFont->Width(buffer);
+
+ if (TextLen > std::max(FrameWidth - 2 * TEXT_OFFSET_X, 1))
+ {
+ snprintf(buffer, sizeof(buffer), "%d.%s %d:%02d", tm->tm_mday, month, tm->tm_hour, tm->tm_min);
+ TextLen = normalFont->Width(buffer);
+ }
+
+ if (TextLen > std::max(FrameWidth - 2 * TEXT_OFFSET_X, 1))
+ {
+ snprintf(buffer, sizeof(buffer), "%d.%d. %d:%02d", tm->tm_mday, tm->tm_mon+1, tm->tm_hour, tm->tm_min);
+ TextLen = normalFont->Width(buffer);
+ }
+
+ if (TextLen > std::max(FrameWidth - 2 * TEXT_OFFSET_X, 1))
+ {
+ snprintf(buffer, sizeof(buffer), "%d:%02d", tm->tm_hour, tm->tm_min);
+ TextLen = normalFont->Width(buffer);
+ }
+
+ if (TextLen < std::max(FrameWidth - 2 * TEXT_OFFSET_X, 1))
+ {
+ bitmap->DrawText(FRAME_SPACE_X + FrameWidth - TextLen - TEXT_OFFSET_X,
+ yPos + TEXT_OFFSET_Y_TIME,
+ FRAME_SPACE_X + FrameWidth - 1,
+ buffer, normalFont, GLCD::clrWhite);
+ }
+ else
+ {
+ bitmap->DrawText(FRAME_SPACE_X + TEXT_OFFSET_X,
+ yPos + TEXT_OFFSET_Y_TIME,
+ FRAME_SPACE_X + FrameWidth - 1,
+ buffer, normalFont, GLCD::clrWhite);
+ }
+ }
+}
+
+void cGraphLCDDisplay::DisplayChannel()
+{
+ int FrameWidth, yPos;
+ tChannelState channel;
+
+ channel = GraphLCDState->GetChannelState();
+ if (GraphLCDSetup.ShowChannel)
+ {
+ FrameWidth = std::max(bitmap->Width() - 2 * FRAME_SPACE_X, 1);
+ if (State == Normal)
+ {
+ if (IsLogoActive()) // Logo enabled & available
+ {
+ FrameWidth = std::max(FrameWidth - FRAME_SPACE_XB - logo->Width() - 2, (unsigned int) 1);
+ }
+ if (bitmap->Height() <= MAXY_M)
+ {
+ // tiny, small & medium display
+ if (IsSymbolsActive()) // Symbols enabled
+ {
+ FrameWidth = std::max(FrameWidth - FRAME_SPACE_XB - symbols->TotalWidth(), 1);
+ }
+ }
+ }
+
+ if (GraphLCDSetup.ShowDateTime == 1 ||
+ (GraphLCDSetup.ShowDateTime == 2 && State != Menu))
+ {
+ yPos = FRAME_SPACE_Y + normalFont->TotalHeight() + 2 * TEXT_OFFSET_Y_TIME + FRAME_SPACE_YB;
+
+ if (bitmap->Height() >= MINY_L)
+ {
+ // align bottom border with logo
+ if (IsLogoActive()) // Logo enabled & available
+ {
+ yPos += std::max((unsigned int) 0, FRAME_SPACE_Y + logo->Height() + 2 - yPos -
+ (normalFont->TotalHeight() + 2 * TEXT_OFFSET_Y_CHANNEL));
+ }
+ }
+ }
+ else
+ {
+ yPos = FRAME_SPACE_Y;
+ }
+
+ // draw Rectangle
+ bitmap->DrawRoundRectangle(FRAME_SPACE_X, yPos,
+ FRAME_SPACE_X + FrameWidth - 1,
+ yPos + normalFont->TotalHeight() + 2 * TEXT_OFFSET_Y_CHANNEL - 1,
+ GLCD::clrBlack, true, (TEXT_OFFSET_Y_CHANNEL >= 4) ? 4 : 1);
+
+ if (channel.strTmp.length() > 0)
+ {
+ bitmap->DrawText(FRAME_SPACE_X + TEXT_OFFSET_X,
+ yPos + TEXT_OFFSET_Y_CHANNEL,
+ FRAME_SPACE_X + FrameWidth - 1,
+ channel.strTmp, normalFont, GLCD::clrWhite);
+ }
+ else if (channel.str.length() > 0)
+ {
+ bitmap->DrawText(FRAME_SPACE_X + TEXT_OFFSET_X,
+ yPos + TEXT_OFFSET_Y_CHANNEL,
+ FRAME_SPACE_X + FrameWidth - 1,
+ channel.str, normalFont, GLCD::clrWhite);
+ }
+ }
+}
+
+bool cGraphLCDDisplay::IsLogoActive() const
+{
+ if ((State==Normal && GraphLCDSetup.ShowLogo) ||
+ (State==Replay && GraphLCDSetup.IdentifyReplayType && GraphLCDSetup.ReplayLogo))
+ {
+ return logo != NULL;
+ }
+ return false;
+}
+
+void cGraphLCDDisplay::DisplayLogo()
+{
+ int x;
+ int y;
+
+ if (IsLogoActive())
+ {
+ if (logo->Count() > 1)
+ {
+ uint64_t t = TimeMs();
+ if (t - logo->LastChange() >= logo->Delay())
+ {
+ if (!logo->Next(t))
+ logo->First(t);
+ }
+ }
+
+ x = std::max(bitmap->Width() - FRAME_SPACE_X - logo->Width() - 2, (unsigned int) 0);
+ y = FRAME_SPACE_Y;
+
+ bitmap->DrawRoundRectangle(x, y, x + logo->Width() + 1, y + logo->Height() + 1, GLCD::clrBlack, false, 1);
+ bitmap->DrawBitmap(x + 1, y + 1, *logo->GetBitmap(), GLCD::clrBlack);
+ }
+}
+
+bool cGraphLCDDisplay::IsSymbolsActive() const
+{
+ return GraphLCDSetup.ShowSymbols;
+}
+
+void cGraphLCDDisplay::DisplaySymbols()
+{
+ int yPos = 0;
+ int xPos = 0;
+ int i;
+ tChannelState channel;
+ tVolumeState volume;
+ tCardState card[MAXDEVICES];
+
+ channel = GraphLCDState->GetChannelState();
+ for (i = 0; i < MAXDEVICES; i++)
+ card[i] = GraphLCDState->GetCardState(i);
+ volume = GraphLCDState->GetVolumeState();
+
+ if (IsSymbolsActive())
+ {
+ cChannel * ch = Channels.GetByNumber(channel.number);
+ if (ch)
+ {
+ if (bitmap->Height() <= MAXY_M) // medium display
+ {
+ yPos = FRAME_SPACE_Y;
+ xPos = bitmap->Width() - FRAME_SPACE_X - symbols->TotalWidth();
+
+ if (IsLogoActive())
+ {
+ xPos -= FRAME_SPACE_XB + logo->Width() + 2;
+ }
+
+ if (GraphLCDSetup.ShowSymbols == 1) // normal/fixed symbols
+ {
+ // new layout:
+ // displays rec symbols for every card and
+ // 2chan + dolby have their own symbols
+ // user triggered symbols
+
+ int yPos2 = 0;
+
+ // blank or 2chan or mute
+ if (volume.value == 0)
+ {
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 'M', symbols);
+ yPos += symbols->Height('S') + SYMBOL_SPACE;
+ }
+ else if (ch->Apid2())
+ {
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 'A', symbols);
+ yPos += symbols->Height('A') + SYMBOL_SPACE;
+ }
+ else
+ {
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, ' ', symbols);
+ yPos += symbols->Height(' ') + SYMBOL_SPACE;
+ }
+
+ // blank or dolby
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, ch->Dpid1() ? 'D' : ' ', symbols);
+ yPos += symbols->Height(ch->Dpid1() ? 'D' : ' ') + SYMBOL_SPACE;
+
+ // blank or teletext
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, ch->Tpid() ? 'T' : ' ', symbols);
+ yPos += symbols->Height(ch->Tpid() ? 'T' : ' ') + SYMBOL_SPACE;
+
+ // blank or crypt
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, ch->Ca() ? 'C' : ' ', symbols);
+ yPos += symbols->Height(ch->Tpid() ? 'C' : ' ') + SYMBOL_SPACE;
+
+ // show REC symbols at the right border below the logo
+ yPos2 = yPos;
+ yPos = FRAME_SPACE_Y;
+ if (GraphLCDSetup.ShowDateTime == 1 ||
+ (GraphLCDSetup.ShowDateTime == 2 && State != Menu))
+ {
+ yPos += normalFont->TotalAscent() + 2 * TEXT_OFFSET_Y_TIME + FRAME_SPACE_YB;
+ }
+ if (GraphLCDSetup.ShowChannel)
+ {
+ yPos += normalFont->TotalAscent() + 2 * TEXT_OFFSET_Y_CHANNEL + FRAME_SPACE_YB;
+ }
+ if (IsLogoActive())
+ {
+ yPos = std::max((unsigned int) yPos, FRAME_SPACE_Y + logo->Height() + 2 + FRAME_SPACE_YB);
+ }
+
+ yPos = std::max(yPos, yPos2);
+ xPos = bitmap->Width() - FRAME_SPACE_X + SYMBOL_SPACE;
+ for (i = LCDMAXCARDS - 1; i >= 0; i--)
+ {
+ if (card[i].recordingCount > 0)
+ {
+ xPos -= symbols->Width(49 + i) + SYMBOL_SPACE;
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 49 + i, symbols);
+ }
+ }
+
+ // show external triggered symbols
+ if (GraphLCDSetup.ShowETSymbols && strlen(szETSymbols) > 0)
+ {
+ for (i = strlen(szETSymbols) - 1; i >= 0; i--)
+ {
+ xPos -= symbols->Width(szETSymbols[i]) + SYMBOL_SPACE;
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, szETSymbols[i], symbols);
+ }
+ }
+ }
+ else // compressed symbols
+ {
+ // old layout:
+ // displays only 1 rec symbol and
+ // a combined 2chan + dolby symbol
+
+ // blank or teletext
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, ch->Tpid() ? 'T' : ' ', symbols);
+ yPos += symbols->Height(ch->Tpid() ? 'T' : ' ') + SYMBOL_SPACE;
+
+ // blank or crypt
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, ch->Ca() ? 'C' : ' ', symbols);
+ yPos += symbols->Height(ch->Tpid() ? 'C' : ' ') + SYMBOL_SPACE;
+
+ // blank, 2chan, dolby or combined symbol
+ if (volume.value == 0)
+ {
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 'M', symbols);
+ yPos += symbols->Height('S') + SYMBOL_SPACE;
+ }
+ else if (ch->Apid2() && ch->Dpid1())
+ {
+ // if Apid2 and Dpid1 are set then use combined symbol
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 'B', symbols);
+ yPos += symbols->Height('B') + SYMBOL_SPACE;
+ }
+ else if (ch->Apid2())
+ {
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 'A', symbols);
+ yPos += symbols->Height('A') + SYMBOL_SPACE;
+ }
+ else if (ch->Dpid1())
+ {
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 'D', symbols);
+ yPos += symbols->Height('D') + SYMBOL_SPACE;
+ }
+ else
+ {
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, ' ', symbols);
+ yPos += symbols->Height(' ') + SYMBOL_SPACE;
+ }
+
+ // blank or rec
+ if (cRecordControls::Active())
+ {
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, '1', symbols);
+ }
+ else
+ {
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, ' ', symbols);
+ }
+ }
+ }
+ else // large display
+ {
+ yPos = FRAME_SPACE_Y;
+ if (GraphLCDSetup.ShowDateTime == 1 ||
+ (GraphLCDSetup.ShowDateTime == 2 && State != Menu))
+ {
+ yPos += normalFont->TotalHeight() + 2 * TEXT_OFFSET_Y_TIME + FRAME_SPACE_YB;
+ }
+ if (GraphLCDSetup.ShowChannel)
+ {
+ yPos += normalFont->TotalHeight() + 2 * TEXT_OFFSET_Y_CHANNEL + FRAME_SPACE_YB;
+ }
+ if (IsLogoActive())
+ {
+ yPos = std::max((unsigned int) yPos, FRAME_SPACE_Y + logo->Height() + 2 + FRAME_SPACE_YB);
+ }
+
+ xPos = bitmap->Width() - FRAME_SPACE_X - symbols->Width(' ');
+
+ if (GraphLCDSetup.ShowSymbols == 1) // normal/fixed symbols
+ {
+ // blank or teletext
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, ch->Tpid() ? 'T' : ' ', symbols);
+ xPos -= symbols->Width(ch->Tpid() ? 'T' : ' ') + SYMBOL_SPACE;
+
+ // blank or dolby
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, ch->Dpid1() ? 'D' : ' ', symbols);
+ xPos -= symbols->Width(ch->Dpid1() ? 'D' : ' ') + SYMBOL_SPACE;
+
+ if (bitmap->Height() > MAXY_M) // with 128 pixel width only 3 symbols...
+ {
+ // blank or crypt
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, ch->Ca()? 'C' : ' ', symbols);
+ xPos -= symbols->Width(ch->Ca() ? 'C' : ' ') + SYMBOL_SPACE;
+ }
+
+ // blank or 2chan or mute
+ if (volume.value == 0)
+ {
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 'M', symbols);
+ xPos -= symbols->Width('S') + SYMBOL_SPACE;
+ }
+ else if (ch->Apid2())
+ {
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 'A', symbols);
+ xPos -= symbols->Width('A') + SYMBOL_SPACE;
+ }
+ else
+ {
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, ' ', symbols);
+ xPos -= symbols->Width(' ') + SYMBOL_SPACE;
+ }
+ }
+ else // compressed symbols
+ {
+ // crypt
+ if (ch->Ca())
+ {
+ xPos -= symbols->Width('C') + SYMBOL_SPACE;
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 'C', symbols);
+ }
+
+ // teletext
+ if (ch->Tpid())
+ {
+ xPos -= symbols->Width('T') + SYMBOL_SPACE;
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 'T', symbols);
+ }
+
+ // dolby
+ if (ch->Dpid1())
+ {
+ xPos -= symbols->Width('D') + SYMBOL_SPACE;
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 'D', symbols);
+ }
+
+ // 2chan
+ if (ch->Apid2())
+ {
+ xPos -= symbols->Width('A') + SYMBOL_SPACE;
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 'A', symbols);
+ }
+
+ // mute
+ if (volume.value == 0)
+ {
+ xPos -= symbols->Width('S') + SYMBOL_SPACE;
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 'M', symbols);
+ }
+ }
+
+ // show REC symbols at the right border of the 'next line'
+ xPos = bitmap->Width() - FRAME_SPACE_X + SYMBOL_SPACE;
+ yPos += symbols->TotalHeight() + FRAME_SPACE_YB;
+ for (i = cDevice::NumDevices() - 1; i >= 0; i--)
+ {
+ // Just display present devices
+ xPos -= symbols->Width(49 + i) + SYMBOL_SPACE;
+ if (card[i].recordingCount > 0)
+ {
+ // Show a recording Symbol
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, 49 + i, symbols);
+ }
+ else
+ {
+ if (GraphLCDSetup.ShowNotRecording == 1)
+ {
+ // Do we want an empty frame around not recording card's icons?
+ // Show an empty frame instead of the recording symbol
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, ' ', symbols);
+ }
+ }
+ }
+
+ // show external triggered symbols
+ if (GraphLCDSetup.ShowETSymbols && strlen(szETSymbols) > 0)
+ {
+ for (i = strlen(szETSymbols) - 1; i >= 0; i--)
+ {
+ xPos -= symbols->Width(szETSymbols[i]) + SYMBOL_SPACE;
+ bitmap->DrawCharacter(xPos, yPos, bitmap->Width() - 1, szETSymbols[i], symbols);
+ }
+ }
+ }
+ }
+ }
+}
+
+void cGraphLCDDisplay::DisplayProgramme()
+{
+ struct tm tm_r;
+ char buffer[25];
+ std::string str;
+ bool showTimeBar = false;
+ int timeBarWidth = 0;
+ int timeBarValue = 0;
+ tEventState event;
+
+ event = GraphLCDState->GetEventState();
+ if (GraphLCDSetup.ShowProgram)
+ {
+ strftime(buffer, sizeof(buffer), "%R", localtime_r(&event.presentTime, &tm_r));
+ if (event.followingTime && event.followingTime != event.presentTime)
+ {
+ str = buffer;
+ if ((bitmap->Width() >= MINY_L || !IsSymbolsActive()))
+ {
+ str += " - ";
+ }
+ else
+ {
+ str += "-";
+ }
+ strftime(buffer, sizeof(buffer), "%R", localtime_r(&event.followingTime, &tm_r));
+ str += buffer;
+ showTimeBar = true;
+ timeBarWidth = normalFont->Width(str) - 1;
+ timeBarValue = (time(NULL) - event.presentTime) * timeBarWidth / (event.followingTime - event.presentTime);
+ if (timeBarValue > timeBarWidth)
+ timeBarValue = timeBarWidth;
+ if (timeBarValue < 0)
+ timeBarValue = 0;
+ }
+ else
+ {
+ str = buffer;
+ }
+
+ if (!event.presentTime)
+ {
+ std::vector<cScroller>::iterator it;
+ for (it = scroller.begin(); it != scroller.end(); it++)
+ {
+ it->Reset();
+ }
+ }
+
+ if (event.presentTime)
+ {
+ if (scroller.size() < 1 ||
+ event.presentTitle != scroller[0].Text() ||
+ (scroller.size() > 1 && event.presentSubtitle != scroller[1].Text()))
+ {
+ if (bitmap->Height() <= MAXY_S)
+ {
+ scroller.resize(1);
+
+ int nTopY = bitmap->Height() - (TEXT_OFFSET_Y_TITLE - 1) - largeFont->TotalHeight();
+ int nMaxX = std::max(1, bitmap->Width() - 1 - (2 * FRAME_SPACE_X));
+ // Logo enabled & available, and text with Logo is overlapped
+ if (IsLogoActive() && nTopY < (int) logo->Height())
+ {
+ nMaxX -= FRAME_SPACE_XB;
+ nMaxX -= logo->Width();
+ nMaxX = std::max(nMaxX-2,1);//Frame around Logo
+ }
+ // If symbols used, and text with symbols is overlapped
+ if (IsSymbolsActive())
+ {
+ nMaxX -= FRAME_SPACE_XB;
+ nMaxX -= symbols->TotalWidth();
+ nMaxX = std::max(nMaxX,1);
+ }
+
+ scroller[0].Init(FRAME_SPACE_X + TEXT_OFFSET_X,
+ nTopY,
+ nMaxX,
+ largeFont, event.presentTitle);
+ }
+ else
+ {
+ scroller.resize(2);
+
+ scroller[0].Init(FRAME_SPACE_X + TEXT_OFFSET_X,
+ bitmap->Height() - 2 * (TEXT_OFFSET_Y_TITLE - 1) - largeFont->TotalHeight() - normalFont->TotalHeight(),
+ bitmap->Width() - 1,
+ largeFont, event.presentTitle);
+ scroller[1].Init(FRAME_SPACE_X + TEXT_OFFSET_X,
+ bitmap->Height() - (TEXT_OFFSET_Y_TITLE-1) - normalFont->TotalHeight(),
+ bitmap->Width() - 1,
+ normalFont, event.presentSubtitle);
+ }
+ }
+ if (bitmap->Height() <= MAXY_S)
+ {
+ // tiny and small LCDs
+ bitmap->DrawText(FRAME_SPACE_X,
+ bitmap->Height() - 2 * (TEXT_OFFSET_Y_TITLE - 1) - largeFont->TotalHeight() - normalFont->TotalHeight(),
+ bitmap->Width() - 1,
+ str, normalFont);
+ }
+ else
+ {
+ // medium and large LCDs
+ bitmap->DrawText(FRAME_SPACE_X,
+ bitmap->Height() - 3 * (TEXT_OFFSET_Y_TITLE - 1) - largeFont->TotalHeight() - 2 * normalFont->TotalHeight() - (showTimeBar && GraphLCDSetup.ShowTimebar ? TIMEBAR_HEIGHT + 1 : 0),
+ bitmap->Width() - 1,
+ str, normalFont);
+ if (showTimeBar && GraphLCDSetup.ShowTimebar)
+ {
+ bitmap->DrawRectangle(FRAME_SPACE_X,
+ bitmap->Height() - 3 * (TEXT_OFFSET_Y_TITLE - 1) - largeFont->TotalHeight() - normalFont->TotalHeight() - TIMEBAR_HEIGHT - 1,
+ FRAME_SPACE_X + timeBarWidth,
+ bitmap->Height() - 3 * (TEXT_OFFSET_Y_TITLE - 1) - largeFont->TotalHeight() - normalFont->TotalHeight() - 2,
+ GLCD::clrBlack, false);
+ bitmap->DrawRectangle(FRAME_SPACE_X,
+ bitmap->Height() - 3 * (TEXT_OFFSET_Y_TITLE - 1) - largeFont->TotalHeight() - normalFont->TotalHeight() - TIMEBAR_HEIGHT - 1,
+ FRAME_SPACE_X + timeBarValue,
+ bitmap->Height() - 3 * (TEXT_OFFSET_Y_TITLE - 1) - largeFont->TotalHeight() - normalFont->TotalHeight() - 2,
+ GLCD::clrBlack, true);
+ }
+ }
+ // Draw Programmtext
+ {
+ std::vector<cScroller>::iterator it;
+ for (it = scroller.begin(); it != scroller.end(); it++)
+ {
+ it->Draw(bitmap);
+ }
+ }
+ }
+ }
+}
+
+bool cGraphLCDDisplay::IndexIsGreaterAsOneHour(int Index) const
+{
+ int h = (Index / FRAMESPERSEC) / 3600;
+ return h > 0;
+}
+
+const char * cGraphLCDDisplay::IndexToMS(int Index) const
+{
+ static char buffer[16];
+ int s = (Index / FRAMESPERSEC);
+ int m = s / 60;
+ s %= 60;
+ snprintf(buffer, sizeof(buffer), "%02d:%02d", m, s);
+ return buffer;
+}
+
+bool cGraphLCDDisplay::IsScrollerTextChanged(const std::vector<cScroller> & scrollers, const std::vector <std::string> & lines) const
+{
+ if (lines.size() == 0)
+ return true; //Different size found
+ if (scrollers.size() == 0)
+ return true; //Different size found
+
+ std::vector<cScroller>::const_iterator i = scrollers.begin();
+ std::vector<cScroller>::const_iterator e = scrollers.end();
+ std::vector<std::string>::const_iterator li = lines.begin();
+ std::vector<std::string>::const_iterator le = lines.end();
+
+ for (; e != i && le != li; ++li,++i)
+ {
+ if (i->Text() != (*li))
+ return true; //Different text found
+ }
+ return false; //Text seem equal
+}
+
+void cGraphLCDDisplay::DisplayReplay(tReplayState & replay)
+{
+ int nMaxX, nProgressbarHeight, nTopY;
+ int nWidthPreMsg = 0, nWidthCurrent = 0, nWidthTotal = 0,nWidthOffset = 0;
+ std::string szPreMsg,szCurrent,szTotal;
+
+ if (bitmap->Height() >= MINY_L)
+ nProgressbarHeight = 15;
+ else if (bitmap->Height() >= MINY_M)
+ nProgressbarHeight = 9;
+ else if (bitmap->Height() >= MINY_S)
+ nProgressbarHeight = 5;
+ else
+ nProgressbarHeight = 3;
+
+ if (IsLogoActive())
+ nTopY = FRAME_SPACE_Y + logo->Height() + 2;
+ else if (GraphLCDSetup.ShowDateTime)
+ nTopY = FRAME_SPACE_Y + normalFont->TotalAscent() + 2 * TEXT_OFFSET_Y_TIME + FRAME_SPACE_YB;
+ else
+ nTopY = FRAME_SPACE_Y;
+ if (replay.name.length() > 0)
+ {
+ int lineHeight, maxLines;
+ std::vector <std::string> lines;
+
+ nMaxX = std::max(1, bitmap->Width() - (2 * FRAME_SPACE_X) - 2 * TEXT_OFFSET_X);
+ lineHeight = FRAME_SPACE_Y + largeFont->TotalHeight();
+ maxLines = std::max(0, (bitmap->Height() - normalFont->TotalHeight() - FRAME_SPACE_Y - nProgressbarHeight - 2 - nTopY) / lineHeight);
+
+ if (maxLines == 0)
+ {
+ if (IsLogoActive())
+ {
+ // draw replayname next to logo
+ nMaxX = std::max((unsigned int) 1, nMaxX - FRAME_SPACE_X - logo->Width() - 2);
+ if (GraphLCDSetup.ShowDateTime)
+ nTopY = FRAME_SPACE_Y + normalFont->TotalAscent() + 2 * TEXT_OFFSET_Y_TIME + FRAME_SPACE_YB;
+ else
+ nTopY = FRAME_SPACE_Y;
+ maxLines = (bitmap->Height() - normalFont->TotalHeight() - FRAME_SPACE_Y - nProgressbarHeight - 2 - nTopY) / lineHeight;
+ }
+ if (maxLines <= 1)
+ {
+ // use singleline mode
+ lines.push_back(replay.name);
+ }
+ else
+ largeFont->WrapText(nMaxX, maxLines * lineHeight, replay.name, lines);
+ }
+ else if (maxLines == 1) //singleline mode
+ lines.push_back(replay.name);
+ else
+ {
+ largeFont->WrapText(nMaxX, maxLines * lineHeight, replay.name, lines);
+ }
+
+ if (scroller.size() != lines.size() ||
+ IsScrollerTextChanged(scroller,lines)) // if any text is changed
+ {
+ // Same size for Scroller and Textbuffer
+ scroller.resize(lines.size());
+
+ std::vector<cScroller>::iterator i = scroller.begin();
+ std::vector<cScroller>::const_iterator e = scroller.end();
+ std::vector<std::string>::const_iterator li = lines.begin();
+ std::vector<std::string>::const_iterator le = lines.end();
+
+ for (int n = lines.size(); e != i && le != li; ++li,++i,--n)
+ {
+ nTopY = bitmap->Height() - normalFont->TotalHeight() - FRAME_SPACE_Y - nProgressbarHeight - n * lineHeight - 2;
+ i->Init(FRAME_SPACE_X + TEXT_OFFSET_X, nTopY, nMaxX + FRAME_SPACE_X + TEXT_OFFSET_X, largeFont, *li);
+ }
+ }
+ }
+
+ // Draw Replaytext
+ {
+ std::vector<cScroller>::iterator it;
+ for (it = scroller.begin(); it != scroller.end(); it++)
+ {
+ it->Draw(bitmap);
+ }
+ }
+
+ // Draw Progressbar with current and total replay time
+ nTopY = bitmap->Height() - normalFont->TotalHeight() - FRAME_SPACE_Y - nProgressbarHeight - 2;
+ nMaxX = std::max(1, bitmap->Width() - 1 - 2 * FRAME_SPACE_X);
+ // Logo enabled & available, and text with Logo is overlapped
+ if (IsLogoActive() && nTopY < (int) logo->Height())
+ {
+ nMaxX -= max(1,FRAME_SPACE_X); // Free line between Logo and progressbar
+ nMaxX -= logo->Width();
+ nMaxX = std::max(nMaxX - 2, 1); //Frame around Logo
+ }
+
+ bitmap->DrawRectangle(FRAME_SPACE_X,
+ nTopY,
+ FRAME_SPACE_X + nMaxX,
+ nTopY + nProgressbarHeight,
+ GLCD::clrBlack, false);
+
+ if (1 < replay.total && 1 < replay.current) // Don't show full progressbar for endless streams
+ {
+ bitmap->DrawRectangle(FRAME_SPACE_X,
+ nTopY,
+ FRAME_SPACE_X + (std::min(replay.total, replay.current) * nMaxX / replay.total),
+ nTopY + nProgressbarHeight,
+ GLCD::clrBlack, true);
+ }
+
+ // Draw Strings with current and total replay time
+ nTopY = bitmap->Height() - normalFont->TotalHeight() - FRAME_SPACE_Y;
+ // use same width like Progressbar
+// if (!IsLogoActive() || nTopY > logo->Height())
+// nMaxX = max(1, bitmap->Width() - 1 - (2 * FRAME_SPACE_X));
+
+ switch (replay.mode)
+ {
+ case eReplayDVD:
+ szPreMsg = tr("DVD"); break;
+ case eReplayMusic:
+ szPreMsg = tr("Music"); break;
+ case eReplayFile:
+ szPreMsg = tr("File"); break;
+ case eReplayImage:
+ szPreMsg = tr("Image"); break;
+ case eReplayAudioCD:
+ szPreMsg = tr("CD"); break;
+ default:
+ szPreMsg = tr("Replay"); break;
+ }
+
+ if (bitmap->Width() >= MINY_M)
+ {
+ szPreMsg += " : ";
+ szPreMsg += replay.loopmode;
+ }
+ else
+ szPreMsg += ":";
+
+ if (replay.mode == eReplayImage) // Image-Plugin hasn't Frames per Seconds
+ {
+ char buffer[8];
+ snprintf(buffer, sizeof(buffer), "%d", replay.current);
+ szCurrent = buffer;
+ snprintf(buffer, sizeof(buffer), "%d", replay.total);
+ szTotal = buffer;
+ }
+ else
+ {
+ if ((replay.total > 1 && IndexIsGreaterAsOneHour(replay.total)) ||
+ IndexIsGreaterAsOneHour(replay.current)) // Check if any index bigger as one hour
+ {
+ szCurrent = (const char *) IndexToHMSF(replay.current);
+ if (replay.total > 1) // Don't draw totaltime for endless streams
+ szTotal = (const char *) IndexToHMSF(replay.total);
+ }
+ else
+ {
+ // Show only minutes and seconds on short replays
+ szCurrent = (const char *) IndexToMS(replay.current);
+ if (replay.total > 1) // Don't draw totaltime for endless streams
+ szTotal = (const char *) IndexToMS(replay.total);
+ }
+ }
+ // Get width of drawable strings
+ nWidthPreMsg = normalFont->Width(szPreMsg);
+ nWidthCurrent = normalFont->Width(szCurrent);
+ if (szTotal.length()) // Don't draw empty string
+ nWidthTotal = normalFont->Width(szTotal);
+
+ // Draw depends on display width, any placeable informations
+ if (nWidthTotal && nWidthPreMsg && (nWidthPreMsg + nWidthCurrent + nWidthTotal + 5 < nMaxX))
+ {
+ // Show prefix and all position
+ nWidthOffset = bitmap->DrawText(FRAME_SPACE_X, nTopY, nMaxX, szPreMsg, normalFont);
+ bitmap->DrawText(FRAME_SPACE_X + nWidthOffset + 1, nTopY, nMaxX, szCurrent, normalFont);
+ bitmap->DrawText(nMaxX - nWidthTotal, nTopY, nMaxX, szTotal, normalFont);
+ }
+ else if (nWidthTotal && (nWidthCurrent + nWidthTotal + 5 < nMaxX))
+ {
+ // Show current and total position
+ bitmap->DrawText(FRAME_SPACE_X, nTopY, nMaxX, szCurrent, normalFont);
+ bitmap->DrawText(nMaxX - nWidthTotal, nTopY, nMaxX, szTotal, normalFont);
+ }
+ else if (!nWidthTotal && nWidthPreMsg && (nWidthPreMsg + nWidthCurrent + 1 < nMaxX))
+ {
+ // Show prefix and current position
+ nWidthOffset = bitmap->DrawText(FRAME_SPACE_X, nTopY, nMaxX, szPreMsg, normalFont);
+ bitmap->DrawText(FRAME_SPACE_X + nWidthOffset + 1, nTopY, nMaxX, szCurrent, normalFont);
+ }
+ else
+ {
+ // Show only current position
+ bitmap->DrawText(FRAME_SPACE_X, nTopY, nMaxX, szCurrent, normalFont);
+ }
+}
+
+void cGraphLCDDisplay::DisplayMenu(void)
+{
+ char buffer2[255];
+ char * pszTmp1;
+ char * pszTmp2;
+ int iAT, t;
+ int FrameWidth, yPos, iEntryHeight;
+ int extra = 0;
+ tOsdState osd;
+
+ osd = GraphLCDState->GetOsdState();
+
+ mutex.Lock();
+
+ FrameWidth = std::max(bitmap->Width() - 2 * FRAME_SPACE_X, 1);
+
+ if (GraphLCDSetup.ShowDateTime == 1 ||
+ (GraphLCDSetup.ShowDateTime == 2 && State != Menu))
+ {
+ yPos = FRAME_SPACE_Y + normalFont->TotalAscent() + 2 * TEXT_OFFSET_Y_TIME + FRAME_SPACE_YB;
+ }
+ else
+ {
+ yPos = FRAME_SPACE_Y;
+ }
+
+ // draw Menu Title
+ if (osd.title.length() > 0)
+ {
+ bitmap->DrawRoundRectangle(FRAME_SPACE_X,
+ yPos,
+ FRAME_SPACE_X + FrameWidth - 1,
+ yPos + normalFont->TotalHeight() + 2 * TEXT_OFFSET_Y_TIME - 1,
+ GLCD::clrBlack, true, TEXT_OFFSET_Y_CHANNEL >= 4 ? 4 : 1);
+ bitmap->DrawText(FRAME_SPACE_X + TEXT_OFFSET_X,
+ yPos + TEXT_OFFSET_Y_TIME,
+ FRAME_SPACE_X + FrameWidth - 1,
+ osd.title, normalFont, GLCD::clrWhite);
+ }
+
+ if (!(textItemLines.size() > 0))
+ {
+ // draw Menu Entries
+ if (normalFont->TotalHeight() <= normalFont->LineHeight())
+ extra = 1;
+ iEntryHeight = normalFont->TotalHeight() + 2 * extra;
+ yPos = yPos + normalFont->TotalHeight() + 2 * TEXT_OFFSET_Y_TIME + FRAME_SPACE_YB;
+ if (GraphLCDSetup.ShowColorButtons &&
+ (osd.colorButton[0].length() > 0 || osd.colorButton[1].length() > 0 ||
+ osd.colorButton[2].length() > 0 || osd.colorButton[3].length() > 0))
+ {
+ menuCount = (bitmap->Height() - yPos - smallFont->TotalHeight() - 4 - FRAME_SPACE_Y / 3) / iEntryHeight;
+ }
+ else
+ {
+ menuCount = (bitmap->Height() - yPos) / iEntryHeight;
+ }
+
+ if (osd.currentItemIndex < menuTop)
+ menuTop = osd.currentItemIndex;
+ if (osd.currentItemIndex > menuTop + menuCount - 1)
+ menuTop = std::max(0, osd.currentItemIndex + 1 - menuCount);
+
+ bitmap->DrawRectangle(0, yPos, bitmap->Width() - 1, bitmap->Height() - 1, GLCD::clrWhite, true);
+
+ for (int i = menuTop; i < std::min((int) osd.items.size(), menuTop + menuCount); i++)
+ {
+ if (i == osd.currentItemIndex)
+ {
+ bitmap->DrawRoundRectangle(FRAME_SPACE_X, yPos + (i - menuTop) * iEntryHeight,
+ bitmap->Width() - 1 - FRAME_SPACE_X,
+ yPos + (i - menuTop + 1) * iEntryHeight - 1,
+ GLCD::clrBlack, true, TEXT_OFFSET_Y_CHANNEL >= 4 ? 3 : 1);
+ }
+ strncopy(buffer2, osd.items[i].c_str(), sizeof(buffer2));
+ pszTmp1 = buffer2;
+ pszTmp2 = strchr(pszTmp1, '\t');
+ iAT = 0; t = 0;
+
+ while (pszTmp1 && pszTmp2)
+ {
+ *pszTmp2 = '\0';
+ bitmap->DrawText(FRAME_SPACE_X + TEXT_OFFSET_X + t,
+ yPos + (i - menuTop) * iEntryHeight + extra,
+ std::min(FRAME_SPACE_X + TEXT_OFFSET_X + t + tab[iAT + 1], bitmap->Width() - 1 - FRAME_SPACE_X),
+ pszTmp1, normalFont, (i == osd.currentItemIndex) ? GLCD::clrWhite : GLCD::clrBlack);
+ pszTmp1 = pszTmp2+1;
+ pszTmp2 = strchr(pszTmp1, '\t');
+ t = t + tab[iAT + 1] + TEXT_OFFSET_X;
+ iAT++;
+ }
+
+ bitmap->DrawText(FRAME_SPACE_X + TEXT_OFFSET_X + t,
+ yPos + (i - menuTop) * iEntryHeight + extra,
+ bitmap->Width() - 1 - FRAME_SPACE_X,
+ pszTmp1, normalFont, (i == osd.currentItemIndex) ? GLCD::clrWhite : GLCD::clrBlack);
+ }
+ }
+ mutex.Unlock();
+}
+
+void cGraphLCDDisplay::DisplayMessage()
+{
+ std::vector <std::string> lines;
+ int lineCount;
+ int maxTextLen, recW, recH;
+ int entryHeight;
+ tOsdState osd;
+
+ osd = GraphLCDState->GetOsdState();
+ if (GraphLCDSetup.ShowMessages && osd.message.length() > 0)
+ {
+ maxTextLen = bitmap->Width() - 2 * FRAME_SPACE_X - 2 * FRAME_SPACE_XB - 2 * TEXT_OFFSET_X - 10;
+ entryHeight = 2 * (normalFont->TotalHeight() - normalFont->TotalAscent()) + normalFont->TotalAscent();
+ normalFont->WrapText(maxTextLen, MAXLINES_MSG * entryHeight, osd.message, lines, &recW);
+ lineCount = lines.size();
+
+ // display text
+ recH = lineCount * entryHeight + 2 * TEXT_OFFSET_Y_CHANNEL + 2 * FRAME_SPACE_YB;
+ recW = recW + 2 * TEXT_OFFSET_X + 2 * FRAME_SPACE_XB + 2 * FRAME_SPACE_X;
+ recW += (recW % 2);
+
+ bitmap->DrawRectangle((bitmap->Width() - recW) / 2,
+ (bitmap->Height() - recH) / 2,
+ bitmap->Width() - 1 - (bitmap->Width() - recW) / 2,
+ bitmap->Height() - 1 - (bitmap->Height() - recH) / 2,
+ GLCD::clrWhite, true);
+ recH = recH - 2 * FRAME_SPACE_YB;
+ recW = recW - 2 * FRAME_SPACE_XB;
+ bitmap->DrawRectangle((bitmap->Width() - recW) / 2,
+ (bitmap->Height() - recH) / 2,
+ bitmap->Width() - 1 - (bitmap->Width() - recW) / 2,
+ bitmap->Height() - 1 - (bitmap->Height() - recH) / 2,
+ GLCD::clrBlack, false);
+ recH = recH - 2 * TEXT_OFFSET_Y_CHANNEL;
+ recW = recW - 2 * TEXT_OFFSET_X;
+ for (int i = 0; i < lineCount; i++)
+ {
+ bitmap->DrawText((bitmap->Width() - normalFont->Width(lines[i])) / 2,
+ (bitmap->Height() - recH) / 2 + i * entryHeight + (normalFont->TotalHeight() - normalFont->TotalAscent()),
+ bitmap->Width() - (bitmap->Width() - recW) / 2,
+ lines[i], normalFont);
+ }
+ }
+}
+
+void cGraphLCDDisplay::DisplayTextItem()
+{
+ int lineCount;
+ int iEntryHeight, iLineAnz;
+ int yPos;
+ tOsdState osd;
+
+ osd = GraphLCDState->GetOsdState();
+
+ mutex.Lock();
+ if (textItemLines.size() > 0)
+ {
+ lineCount = textItemLines.size();
+
+ if (GraphLCDSetup.ShowDateTime == 1 ||
+ (GraphLCDSetup.ShowDateTime == 2 && State != Menu))
+ {
+ yPos = FRAME_SPACE_Y + normalFont->TotalAscent() + 2 * TEXT_OFFSET_Y_TIME + FRAME_SPACE_YB;
+ }
+ else
+ {
+ yPos = FRAME_SPACE_Y;
+ }
+
+ // draw Text
+ iEntryHeight = normalFont->LineHeight();
+ yPos = yPos + normalFont->TotalAscent() + 2 * TEXT_OFFSET_Y_CHANNEL + FRAME_SPACE_YB;
+ if (GraphLCDSetup.ShowColorButtons &&
+ (osd.colorButton[0].length() > 0 || osd.colorButton[1].length() > 0 ||
+ osd.colorButton[2].length() > 0 || osd.colorButton[3].length() > 0))
+ {
+ iLineAnz = (bitmap->Height() - yPos - smallFont->TotalHeight() - 4 - FRAME_SPACE_Y / 3) / iEntryHeight;
+ }
+ else
+ {
+ iLineAnz = (bitmap->Height() - yPos) / iEntryHeight;
+ }
+
+ int startLine = textItemTop;
+ for (int i = 0; i < std::min(lineCount, iLineAnz); i++)
+ {
+ if (i + startLine < lineCount)
+ bitmap->DrawText(FRAME_SPACE_X + TEXT_OFFSET_X,
+ yPos + i * iEntryHeight,
+ bitmap->Width() - 1 - FRAME_SPACE_X,
+ textItemLines[i + startLine], normalFont);
+ }
+ }
+ mutex.Unlock();
+}
+
+void cGraphLCDDisplay::DisplayColorButtons()
+{
+ int i, buttonWidth, textLen;
+ int extra = 0;
+ tOsdState osd;
+
+ osd = GraphLCDState->GetOsdState();
+
+ if (GraphLCDSetup.ShowColorButtons)
+ {
+ buttonWidth = (bitmap->Width() / 4) - (FRAME_SPACE_X ? 2 * FRAME_SPACE_X : 1);
+ if (smallFont->TotalHeight() == smallFont->TotalAscent())
+ extra = 1;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (osd.colorButton[i].length() > 0)
+ {
+ bitmap->DrawRoundRectangle(i * (bitmap->Width() / 4) + FRAME_SPACE_X,
+ bitmap->Height() - smallFont->TotalHeight() - 2 * extra - FRAME_SPACE_Y / 3,
+ i * (bitmap->Width() / 4) + FRAME_SPACE_X + buttonWidth - 1,
+ bitmap->Height() - 1 - FRAME_SPACE_Y / 3,
+ GLCD::clrBlack, true, std::max(1, (smallFont->TotalHeight() + 4) / 5));
+ textLen = smallFont->Width(osd.colorButton[i]);
+ if (textLen <= buttonWidth - 2)
+ {
+ bitmap->DrawText(i * (bitmap->Width() / 4) + (bitmap->Width() / 8) - (textLen + 1) / 2,
+ bitmap->Height() - smallFont->TotalHeight() - extra - FRAME_SPACE_Y / 3,
+ i * (bitmap->Width() / 4) + FRAME_SPACE_X + buttonWidth - 1,
+ osd.colorButton[i], smallFont, GLCD::clrWhite);
+ }
+ else
+ {
+ bitmap->DrawText(i * (bitmap->Width() / 4) + FRAME_SPACE_X + 1,
+ bitmap->Height() - smallFont->TotalHeight() - extra - FRAME_SPACE_Y / 3,
+ i * (bitmap->Width() / 4) + FRAME_SPACE_X + buttonWidth - 1,
+ osd.colorButton[i], smallFont, GLCD::clrWhite);
+ }
+ }
+ }
+ }
+}
+
+void cGraphLCDDisplay::DisplayVolume()
+{
+ int RecW, RecH;
+ tVolumeState volume;
+
+ volume = GraphLCDState->GetVolumeState();
+
+ if (GraphLCDSetup.ShowVolume)
+ {
+ if (volume.lastChange > 0)
+ {
+ if (TimeMs() - volume.lastChange < 2000)
+ {
+ RecH = (bitmap->Height() / 5) + 2 * FRAME_SPACE_YB + 4 * FRAME_SPACE_YB;
+ RecW = bitmap->Width() / 2;
+ bitmap->DrawRoundRectangle((bitmap->Width() - RecW) / 2, // draw frame
+ (bitmap->Height() - RecH) / 2,
+ bitmap->Width() - (bitmap->Width() - RecW) / 2 - 1,
+ bitmap->Height() - (bitmap->Height() - RecH) / 2 - 1,
+ GLCD::clrWhite, true, 1);
+ RecH = RecH - 2 * FRAME_SPACE_YB;
+ RecW = RecW - 2 * FRAME_SPACE_XB;
+ bitmap->DrawRoundRectangle((bitmap->Width() - RecW) / 2, // draw box
+ (bitmap->Height() - RecH) / 2,
+ bitmap->Width() - 1 - (bitmap->Width() - RecW) / 2,
+ bitmap->Height() - 1 - (bitmap->Height() - RecH) / 2,
+ GLCD::clrBlack, false, 1);
+ RecH = RecH - 2;
+ RecW = RecW - 2;
+ if (volume.value > 0)
+ bitmap->DrawRectangle((bitmap->Width() - RecW) / 2, // draw bar
+ (bitmap->Height() - RecH) / 2,
+ (bitmap->Width() - RecW) / 2 + (volume.value * RecW) / 255,
+ bitmap->Height() - 1 - (bitmap->Height() - RecH) / 2,
+ GLCD::clrBlack, true);
+ if (volume.value == 0)
+ {
+ // display big mute symbol
+ bitmap->DrawCharacter(bitmap->Width() / 2 - symbols->Width('5'),
+ bitmap->Height() / 2 - symbols->Height('5'),
+ bitmap->Width() - 1, '5', symbols);
+ bitmap->DrawCharacter(bitmap->Width() / 2,
+ bitmap->Height() / 2 - symbols->Height('6'),
+ bitmap->Width() - 1, '6', symbols);
+ bitmap->DrawCharacter(bitmap->Width() / 2 - symbols->Width('7'),
+ bitmap->Height() / 2,
+ bitmap->Width() - 1, '7', symbols);
+ bitmap->DrawCharacter(bitmap->Width() / 2,
+ bitmap->Height() / 2,
+ bitmap->Width() - 1, '8', symbols);
+ }
+ showVolume = true;
+ }
+ }
+ }
+}
+
+void cGraphLCDDisplay::UpdateIn(long usec)
+{
+ if (usec == 0)
+ {
+ timerclear(&UpdateAt);
+ }
+ else
+ {
+ if (gettimeofday(&CurrTimeval, NULL) == 0)
+ {
+ // get current time
+ UpdateAt.tv_sec = CurrTimeval.tv_sec;
+ UpdateAt.tv_usec = CurrTimeval.tv_usec + usec;
+ while (UpdateAt.tv_usec >= 1000000)
+ {
+ // take care of an overflow
+ UpdateAt.tv_sec++;
+ UpdateAt.tv_usec -= 1000000;
+ }
+ }
+ }
+}
+
+
+bool cGraphLCDDisplay::CheckAndUpdateSymbols()
+{
+ bool bRet = false;
+ static struct stat filestat;
+ FILE* InFile = NULL;
+ static char szLine[8];
+
+ if (stat(FILENAME_EXTERNAL_TRIGGERED_SYMBOLS, &filestat)==0) {
+ if (LastTimeModSym != filestat.st_mtime) {
+ InFile = fopen(FILENAME_EXTERNAL_TRIGGERED_SYMBOLS, "r");
+ if (InFile) {
+ strcpy(szETSymbols, "");
+ while (!feof(InFile) && (strlen(szETSymbols)+1<sizeof(szLine))) {
+ strcpy(szLine, "");
+ fgets(szLine, sizeof(szLine), InFile);
+ compactspace(szLine);
+ if ((strlen(szLine)==2) && (szLine[1]=='1')) {
+ strcat(szETSymbols, ".");
+ szETSymbols[strlen(szETSymbols)-1] = szLine[0];
+ }
+ }
+ fclose(InFile);
+ LastTimeModSym = filestat.st_mtime;
+ bRet = true;
+ }
+ }
+ } else {
+ if ((errno == ENOENT) && (strlen(szETSymbols)>0)) {
+ strcpy(szETSymbols, "");
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+void cGraphLCDDisplay::SetBrightness()
+{
+ mutex.Lock();
+ bool bActive = bBrightnessActive
+ || (State == Menu)
+ || (GraphLCDSetup.ShowVolume && showVolume)
+ || (GraphLCDSetup.ShowMessages && GraphLCDState->GetOsdState().message.length() > 0)
+ || (GraphLCDSetup.BrightnessDelay == 900);
+ if (bActive)
+ {
+ LastTimeBrightness = TimeMs();
+ bBrightnessActive = false;
+ }
+ if ((bActive ? GraphLCDSetup.BrightnessActive : GraphLCDSetup.BrightnessIdle) != nCurrentBrightness)
+ {
+ if (bActive)
+ {
+ mLcd->SetBrightness(GraphLCDSetup.BrightnessActive);
+ nCurrentBrightness = GraphLCDSetup.BrightnessActive;
+ }
+ else
+ {
+ if (GraphLCDSetup.BrightnessDelay < 1
+ || ((TimeMs() - LastTimeBrightness) > (uint64_t) (GraphLCDSetup.BrightnessDelay*1000)))
+ {
+ mLcd->SetBrightness(GraphLCDSetup.BrightnessIdle);
+ nCurrentBrightness = GraphLCDSetup.BrightnessIdle;
+ }
+ }
+ }
+ mutex.Unlock();
+}