diff options
Diffstat (limited to 'texteffects.c')
-rw-r--r-- | texteffects.c | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/texteffects.c b/texteffects.c new file mode 100644 index 0000000..59b4e9c --- /dev/null +++ b/texteffects.c @@ -0,0 +1,447 @@ +#include "common.h" +#include "config.h" +#include "texteffects.h" +#include "tools.h" + +#include <algorithm> + +#ifndef DISABLE_ANIMATED_TEXT +//Redefine macros +#undef TE_LOCK +#define TE_LOCK UpdateLock() +#undef TE_UNLOCK +#define TE_UNLOCK UpdateUnlock() +#endif + +#ifdef HAVE_FREETYPE +// needed for case-insensitive sort of vector (for fonts) +struct NoCase { + bool operator()(const string& x, const string& y) { + string lv(x); + string rv(y); + lcase(lv); + lcase(rv); + return lv < rv; + } + + void lcase(string& s) { + int n = s.size(); + for(int i = 0; i < n; i++) + s[i] = tolower(s[i]); + } +}; +#endif + + +#ifdef DISABLE_ANIMATED_TEXT +cEnigmaTextEffects EnigmaTextEffects; +#else +cEnigmaTextEffects EnigmaTextEffects("EnigmaNG effects"); +#endif + +#ifdef DISABLE_ANIMATED_TEXT +cEnigmaTextEffects::cEnigmaTextEffects(void) : osd(NULL) +#else +cEnigmaTextEffects::cEnigmaTextEffects(const char *Description) : cThread(Description), osd(NULL), condSleep(), mutexSleep(), mutexRunning() +#endif +{ +// SetPriority(19); + +#ifdef HAVE_FREETYPE + availTTFs = NULL; + nMaxTTFs = 0; +#endif +} + +cEnigmaTextEffects::~cEnigmaTextEffects(void) +{ +#ifndef DISABLE_ANIMATED_TEXT +//TODO? Stop(); +#endif + +#ifdef HAVE_FREETYPE + if (availTTFs) { + char **ptr = availTTFs; + while (*ptr) { + delete(*ptr); + ptr++; + } + free(availTTFs); + availTTFs = NULL; + } +#endif +} + +#ifndef DISABLE_ANIMATED_TEXT +void cEnigmaTextEffects::Action(void) +{ + mutexRunning.Lock(); + mutexSleep.Lock(); + + debug("cEnigmaTextEffects::Action() %p\n", pthread_self()); + + while (EnigmaConfig.useTextEffects && osd) { + uint64_t nNow = cTimeMs::Now(); + int nSleepMs = 0; + + TE_LOCK; //This causes an initial wait until thet first Flush() is called (which TE_UNKOCKs) + for (tEffects::iterator effect = vecEffects.begin(); (effect != vecEffects.end()) && osd; effect++) { + tEffect *e = (*effect); + if (e == NULL) + continue; + + if (e->nNextUpdate == 0) { + e->nNextUpdate = nNow + (e->nAction == 0 ? EnigmaConfig.scrollPause : EnigmaConfig.blinkPause); + } else if(nNow >= e->nNextUpdate) { + DoEffect(e, nNow); + } + +// printf("NOW=%llu NEXT=%llu DIFF=%d SLEEP=%d\n", nNow, e->nNextUpdate, (int)(e->nNextUpdate - nNow), nSleepMs); + int nDiff = max(3, (int)(e->nNextUpdate - nNow)); + if (nSleepMs == 0 || nDiff < nSleepMs) + nSleepMs = nDiff; + } + + if (osd) + osd->Flush(); + TE_UNLOCK; + + if (osd) { +// printf("SLEEP1: %d, %p\n", nSleepMs, pthread_self()); + if (nSleepMs) + condSleep.TimedWait(mutexSleep, nSleepMs); + else + condSleep.TimedWait(mutexSleep, EnigmaConfig.scrollPause); //TODO +// printf("SLEEP2: %d, %p\n", nSleepMs, pthread_self()); + } + } + + mutexSleep.Unlock(); + mutexRunning.Unlock(); +} + +void cEnigmaTextEffects::DoEffect(tEffect *e, uint64_t nNow) +{ + bool fDrawItem = ((yMessageTop == 0) || (e->y + e->Height < yMessageTop)); + + switch (e->nAction) { + case 0: // Scroll + DoScroll(e, nNow, fDrawItem); + break; + + case 1: // Blink + DoBlink(e, nNow, fDrawItem); + break; + } +} + +void cEnigmaTextEffects::DoScroll(tEffect *e, uint64_t nNow, bool fDrawItem) +{ +// debug("cEnigmaTextEffects::DoScroll()\n"); + if (e->Font->Width(e->strText.c_str()) <= e->Width) { + if (fDrawItem) { + if (e->Skin) + e->Skin->DrawTitle(e->strText.c_str()); + else + osd->DrawText(e->x, e->y, e->strText.c_str(), e->ColorFg, e->ColorBg, e->Font, e->Width, e->Height, e->Alignment); + } + + if (nNow) + e->nNextUpdate = nNow + EnigmaConfig.scrollPause; + return; + } + + if (nNow) { + int nDelay = EnigmaConfig.scrollDelay; + if (fDrawItem) { + switch (e->nDirection) { + case 0: // Scroll from left to right + if (e->Font->Width(e->strText.c_str() + e->nOffset) <= e->Width) { + if (EnigmaConfig.scrollMode) + e->nDirection = 2; + else + e->nDirection = 1; + nDelay = EnigmaConfig.scrollPause; + } else if (e->nOffset < e->strText.length()) + e->nOffset++; + break; + + case 1: // Scroll from right to left + if (e->nOffset > 0) + e->nOffset--; + if (e->nOffset <= 0) { + e->nDirection = false; + nDelay = EnigmaConfig.scrollPause; + } + break; + + case 2: // Restart scrolling from the left + nDelay = EnigmaConfig.scrollPause; + e->nOffset = 0; + e->nDirection = 0; + break; + } + } + e->nNextUpdate = nNow + nDelay; + } + + if (fDrawItem) { +// printf("SCROLL: %d %d %d/%d (%s) %d %lu %lu\n", e->nOffset, e->nDirection, e->Font->Width(e->strText.c_str() + e->nOffset), e->Width, e->strText.c_str() + e->nOffset, e->strText.length(), nNow, e->nNextUpdate); + if (e->Skin) + e->Skin->DrawTitle(e->strText.c_str() + e->nOffset); + else + osd->DrawText(e->x, e->y, e->strText.c_str() + e->nOffset, e->ColorFg, e->ColorBg, e->Font, e->Width, e->Height); + } +} + +void cEnigmaTextEffects::DoBlink(tEffect *e, uint64_t nNow, bool fDrawItem) +{ +// debug("cEnigmaTextEffects::DoBlink()\n"); + if (fDrawItem) { + if (nNow) { + e->nDirection = (e->nDirection == 0 ? 1 : 0); + e->nNextUpdate = nNow + EnigmaConfig.blinkPause; + } + if (e->nDirection == 1) + osd->DrawText(e->x, e->y, e->strText.c_str() + e->nOffset, e->ColorFg, e->ColorBg, e->Font, e->Width, e->Height, e->Alignment); + else + osd->DrawText(e->x, e->y, e->strText.c_str() + e->nOffset, e->ColorBg, e->ColorBg, e->Font, e->Width, e->Height, e->Alignment); + } else { + e->nNextUpdate = nNow + EnigmaConfig.blinkPause; + } +} + +bool cEnigmaTextEffects::Start(cOsd *o) +{ + osd = o; + + if (!EnigmaConfig.useTextEffects) + return false; + + debug("cEnigmaTextEffects::Start(%p) %p\n", osd, pthread_self()); + + if (osd == NULL) + return false; + + if (Running()) { + error("cEnigmaTextEffects::Start - already running\n"); + return false; //TODO? maybe Cancel() + } + + yMessageTop = 0; + + TE_LOCK; + return cThread::Start(); +} + +void cEnigmaTextEffects::Stop(void) +{ + //Must be TE_LOCKed by caller (calls TE_UNLOCK) + + debug("cEnigmaTextEffects::Stop()\n"); + osd = NULL; + Clear(); + TE_UNLOCK; + Wakeup(); // break sleeping Action() thread + mutexRunning.Lock(); // Wait for Action() to finish + mutexRunning.Unlock(); +} + +void cEnigmaTextEffects::Clear(void) +{ + debug("cEnigmaTextEffects::Clear()\n"); + + //Must be TE_LOCKed by caller + + for (tEffects::iterator effect = vecEffects.begin(); effect != vecEffects.end(); effect++) { + delete(*effect); + } + + vecEffects.clear(); +} + +void cEnigmaTextEffects::PauseEffects(int y) +{ + debug("cEnigmaTextEffects::PauseEffects(%d)\n", y); + + //Must be TE_LOCKed by caller + + yMessageTop = y; +} + +void cEnigmaTextEffects::ResetText(int i, tColor ColorFg, tColor ColorBg, bool fDraw) +{ + debug("cEnigmaTextEffects::ResetText(%d)\n", i); + + //Must be TE_LOCKed by caller + + if (i < 0 || i >= (int)vecEffects.size()) + return; + + tEffect *e = vecEffects[i]; + if (e) { + if (fDraw && osd) + osd->DrawText(e->x, e->y, e->strText.c_str(), + ColorFg ? ColorFg : e->ColorFg, + ColorBg ? ColorBg : e->ColorBg, + e->Font, e->Width, e->Height); + delete(e); + vecEffects[i] = NULL; + } +} + +void cEnigmaTextEffects::UpdateTextWidth(int i, int Width) +{ + debug("cEnigmaTextEffects::UpdateTextWidth(%d)\n", i); + + //Must be TE_LOCKed by caller + + if (i < 0 || i >= (int)vecEffects.size()) + return; + + tEffect *e = vecEffects[i]; + if (e) { + e->Width = Width; + } +} + +int cEnigmaTextEffects::DrawAnimatedTitle(int o_id, int action, const char *s, const cFont *Font, int Width, cSkinEnigmaOsd *skin) +{ + //Must be TE_LOCKed by caller + + if (Font == NULL || osd == NULL || skin == NULL) + return -1; + + debug("cEnigmaTextEffects::DrawAnimatedTitle(%d, %d, %s)\n", o_id, EnigmaConfig.useTextEffects, s); + + if (o_id >= 0) { + // Update animated text + tEffect *effect = vecEffects[o_id]; + if (effect) { + if (s == NULL) + effect->strText = ""; + else if (strcmp(effect->strText.c_str(), s) != 0) { + effect->strText = s; + effect->nOffset = 0; + effect->nDirection = 0; + } + DoEffect(effect); + return o_id; + } else { + return -1; + } + } else { + skin->DrawTitle(s); + if (EnigmaConfig.useTextEffects && ((Font->Width(s ? s : "") > Width) || (action > 0))) { + // New scrolling text + tEffect *effect = new tEffect; + if (effect == NULL) { + return -1; + } + + effect->nAction = action; + effect->strText = std::string(s ? s : ""); + effect->Width = Width; + effect->Font = Font; + effect->Skin = skin; + vecEffects.push_back(effect); + int id = vecEffects.size() - 1; + return id; + } else { + return -1; + } + } +} + +int cEnigmaTextEffects::DrawAnimatedText(int o_id, int action, int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment) +{ + //Must be TE_LOCKed by caller + + if (Font == NULL || osd == NULL) + return -1; + + debug("cEnigmaTextEffects::DrawAnimatedText(%d, %d, %s)\n", o_id, EnigmaConfig.useTextEffects, s); + + if (o_id >= 0) { + // Update animated text + tEffect *effect = vecEffects[o_id]; + if (effect) { + if (s == NULL) + effect->strText = ""; + else if (strcmp(effect->strText.c_str(), s) != 0) { + effect->strText = s; + effect->nOffset = 0; + effect->nDirection = 0; + } + DoEffect(effect); + return o_id; + } else { + return -1; + } + } else { + if (Height == 0) + Height = Font->Height(s); + osd->DrawText(x, y, s ? s : "", ColorFg, ColorBg, Font, Width, Height, Alignment); + // New animated text + tEffect *effect = new tEffect; + if (effect == NULL) { + return -1; + } + + effect->nAction = action; + effect->strText = std::string(s ? s : ""); + effect->x = x; + effect->y = y; + effect->Width = Width; + effect->Height = Height; + effect->ColorFg = ColorFg; + effect->ColorBg = ColorBg; + effect->Font = Font; + effect->Alignment = Alignment; + vecEffects.push_back(effect); + int id = vecEffects.size() - 1; + return id; + } +} +#endif //DISABLE_ANIMATED_TEXT + +#ifdef HAVE_FREETYPE +const char **cEnigmaTextEffects::GetAvailTTFs(void) +{ + if (availTTFs == NULL) { + std::vector<std::string> vecFonts; + cReadDir d(EnigmaConfig.GetFontsDir()); + struct dirent *e; + while ((e = d.Next()) != NULL) { + if ((strcmp(e->d_name, ".") != 0) && (strcmp(e->d_name, "..") != 0)) { + if (strcmp(e->d_name + strlen(e->d_name) - 4, ".ttf") == 0) { + printf("Loading %s\n", e->d_name); + vecFonts.push_back(std::string(e->d_name)); + } else { + printf("Ignoring non-font file: %s\n", e->d_name); + } + } + } + + if (vecFonts.size() > 0) { + sort(vecFonts.begin(), vecFonts.end(), NoCase()); + availTTFs = (char **)calloc(vecFonts.size() + 1, sizeof(char*)); + if (availTTFs) { + char **ptr = availTTFs; + for (vector<std::string>::iterator i = vecFonts.begin(); i != vecFonts.end(); i++) { + if (!(*i).empty()) { + *ptr = strdup((*i).c_str()); + ptr++; + nMaxTTFs++; + } + } + } + } + + vecFonts.clear(); + } + + return (const char**)availTTFs; +} +#endif +// vim:et:sw=2:ts=2: |