diff options
Diffstat (limited to 'display.c')
-rw-r--r-- | display.c | 1953 |
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(); +} |