summaryrefslogtreecommitdiff
path: root/texteffects.c
diff options
context:
space:
mode:
Diffstat (limited to 'texteffects.c')
-rw-r--r--texteffects.c447
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: