MythTV  master
mythuihelper.cpp
Go to the documentation of this file.
1 #include "mythuihelper.h"
2 
3 #include <cmath>
4 #include <unistd.h>
5 
6 #include <QImage>
7 #include <QPixmap>
8 #include <QMutex>
9 #include <QPalette>
10 #include <QMap>
11 #include <QDir>
12 #include <QFileInfo>
13 #include <QApplication>
14 #include <QPainter>
15 #include <QDesktopWidget>
16 #include <QStyleFactory>
17 #include <QSize>
18 #include <QFile>
19 #include <QAtomicInt>
20 #include <QEventLoop>
21 #include <QTimer>
22 
23 // mythbase headers
24 #include "mythdirs.h"
25 #include "mythlogging.h"
26 #include "mythdownloadmanager.h"
27 #include "mythdb.h"
28 #include "remotefile.h"
29 #include "mythcorecontext.h"
30 #include "storagegroup.h"
31 #include "mythdate.h"
32 #include "mthreadpool.h"
33 
34 // mythui headers
35 #include "mythprogressdialog.h"
36 #include "mythimage.h"
37 #include "screensaver.h"
38 #include "mythmainwindow.h"
39 #include "themeinfo.h"
40 #include "x11colors.h"
41 #include "mythdisplay.h"
42 #include "DisplayRes.h"
43 
44 #define LOC QString("MythUIHelper: ")
45 
46 static MythUIHelper *mythui = nullptr;
47 static QMutex uiLock;
49 
51 {
52  if (mythui)
53  return mythui;
54 
55  uiLock.lock();
56 
57  if (!mythui)
58  mythui = new MythUIHelper();
59 
60  uiLock.unlock();
61 
62  // These directories should always exist. Don't test first as
63  // there's no harm in trying to create an existing directory.
64  QDir dir;
65  dir.mkdir(GetThemeBaseCacheDir());
66  dir.mkdir(GetRemoteCacheDir());
67  dir.mkdir(GetThumbnailDir());
68 
69  return mythui;
70 }
71 
73 {
76  uiLock.lock();
77  delete mythui;
78  mythui = nullptr;
79  uiLock.unlock();
80 }
81 
83 {
84  return MythUIHelper::getMythUI();
85 }
86 
88 {
90 }
91 
93 {
94 public:
95  explicit MythUIHelperPrivate(MythUIHelper *p);
97 
98  void Init();
99 
100  void GetScreenBounds(void);
101  void StoreGUIsettings(void);
102 
103  double GetPixelAspectRatio(void);
104  void WaitForScreenChange(void) const;
105 
109  QString m_themename;
110  QPalette m_palette;
111 
112  float m_wmult, m_hmult;
114 
115  // Drawable area of the full screen. May cover several screens,
116  // or exclude windowing system fixtures (like Mac menu bar)
119 
120  // Dimensions of the theme
122  bool m_isWide;
123 
124  QMap<QString, MythImage *> imageCache;
125 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
126  QMap<QString, uint> CacheTrack;
127 #else
128  QMap<QString, qint64> CacheTrack;
129 #endif
130  QMutex *m_cacheLock;
131 
132 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
133  QAtomicInt m_cacheSize;
134  QAtomicInt m_maxCacheSize;
135 #else
136  QAtomicInteger<qint64> m_cacheSize;
137  QAtomicInteger<qint64> m_maxCacheSize;
138 #endif
139 
140  // The part of the screen(s) allocated for the GUI. Unless
141  // overridden by the user, defaults to drawable area above.
143 
144  // The part of the screen(s) allocated for the GUI. Unless
145  // overridden by the user, defaults to drawable area above.
147 
148  // Command-line GUI size, which overrides both the above sets of sizes
150 
151  QString themecachedir;
152  QString m_userThemeDir;
153 
156 
159 
161 
163 
165 
167 
168  QStringList m_searchPaths;
169 };
170 
175 
177  : m_themeloaded(false),
178  m_wmult(1.0), m_hmult(1.0), m_pixelAspectRatio(-1.0),
179  m_xbase(0), m_ybase(0), m_height(0), m_width(0),
180  m_baseWidth(800), m_baseHeight(600), m_isWide(false),
181  m_cacheLock(new QMutex(QMutex::Recursive)),
182  m_cacheSize(0), m_maxCacheSize(30 * 1024 * 1024),
183  m_screenxbase(0), m_screenybase(0), m_screenwidth(0), m_screenheight(0),
184  screensaver(nullptr), screensaverEnabled(false), display_res(nullptr),
185  screenSetup(false), m_imageThreadPool(new MThreadPool("MythUIHelper")),
186  parent(p), m_fontStretch(100)
187 {
188  callbacks.exec_program = nullptr;
189  callbacks.exec_program_tv = nullptr;
190  callbacks.configplugin = nullptr;
191  callbacks.plugin = nullptr;
192  callbacks.eject = nullptr;
193 }
194 
196 {
197  QMutableMapIterator<QString, MythImage *> i(imageCache);
198 
199  while (i.hasNext())
200  {
201  i.next();
202  i.value()->SetIsInCache(false);
203  i.value()->DecrRef();
204  i.remove();
205  }
206 
207  CacheTrack.clear();
208 
209  delete m_cacheLock;
210  delete m_imageThreadPool;
211  delete screensaver;
212 
213  if (display_res)
215 }
216 
218 {
220  GetScreenBounds();
222  screenSetup = true;
223 
224  StorageGroup sgroup("Themes", gCoreContext->GetHostName());
225  m_userThemeDir = sgroup.GetFirstDir(true);
226 }
227 
236 {
237  QDesktopWidget *desktop = QApplication::desktop();
238  bool hasXinerama = MythDisplay::GetNumberXineramaScreens() > 1;
239  int numScreens = desktop->numScreens();
240  int screen;
241 
242  if (hasXinerama)
243  {
244  LOG(VB_GUI, LOG_INFO, LOC +
245  QString("Total desktop dim: %1x%2, over %3 screen[s].")
246  .arg(desktop->width()).arg(desktop->height()).arg(numScreens));
247  }
248 
249  if (numScreens > 1)
250  {
251  for (screen = 0; screen < numScreens; ++screen)
252  {
253  QRect dim = desktop->screenGeometry(screen);
254  LOG(VB_GUI, LOG_INFO, LOC + QString("Screen %1 dim: %2x%3.")
255  .arg(screen).arg(dim.width()).arg(dim.height()));
256  }
257  }
258 
259  screen = desktop->primaryScreen();
260  LOG(VB_GUI, LOG_INFO, LOC + QString("Primary screen: %1.").arg(screen));
261 
262  if (hasXinerama)
263  screen = GetMythDB()->GetNumSetting("XineramaScreen", screen);
264 
265  if (screen == -1) // Special case - span all screens
266  {
267  m_xbase = 0;
268  m_ybase = 0;
269  m_width = desktop->width();
270  m_height = desktop->height();
271 
272  LOG(VB_GUI, LOG_INFO, LOC +
273  QString("Using all %1 screens. ").arg(numScreens) +
274  QString("Dimensions: %1x%2").arg(m_width).arg(m_height));
275 
276  return;
277  }
278 
279  if (hasXinerama) // User specified a single screen
280  {
281  if (screen < 0 || screen >= numScreens)
282  {
283  LOG(VB_GENERAL, LOG_WARNING, LOC +
284  QString("Xinerama screen %1 was specified,"
285  " but only %2 available, so using screen 0.")
286  .arg(screen).arg(numScreens));
287  screen = 0;
288  }
289  }
290 
291 
292  {
293  QRect bounds;
294 
295  bool inWindow = GetMythDB()->GetBoolSetting("RunFrontendInWindow", false);
296 
297  if (inWindow)
298  LOG(VB_GUI, LOG_INFO, LOC + "Running in a window");
299 
300  if (inWindow)
301  // This doesn't include the area occupied by the
302  // Windows taskbar, or the Mac OS X menu bar and Dock
303  bounds = desktop->availableGeometry(screen);
304  else
305  bounds = desktop->screenGeometry(screen);
306 
307  m_xbase = bounds.x();
308  m_ybase = bounds.y();
309  m_width = bounds.width();
310  m_height = bounds.height();
311 
312  LOG(VB_GUI, LOG_INFO, LOC + QString("Using screen %1, %2x%3 at %4,%5")
313  .arg(screen).arg(m_width).arg(m_height)
314  .arg(m_xbase).arg(m_ybase));
315  }
316 }
317 
322 {
323  if (x_override >= 0 && y_override >= 0)
324  {
325  GetMythDB()->OverrideSettingForSession("GuiOffsetX", QString::number(x_override));
326  GetMythDB()->OverrideSettingForSession("GuiOffsetY", QString::number(y_override));
327  }
328 
329  if (w_override > 0 && h_override > 0)
330  {
331  GetMythDB()->OverrideSettingForSession("GuiWidth", QString::number(w_override));
332  GetMythDB()->OverrideSettingForSession("GuiHeight", QString::number(h_override));
333  }
334 
335  m_screenxbase = GetMythDB()->GetNumSetting("GuiOffsetX");
336  m_screenybase = GetMythDB()->GetNumSetting("GuiOffsetY");
337 
340 
341  // If any of these was _not_ set by the user,
342  // (i.e. they are 0) use the whole-screen defaults
343 
344  if (!m_screenxbase)
346 
347  if (!m_screenybase)
349 
350  if (!m_screenwidth)
352 
353  if (!m_screenheight)
355 
356  if (m_screenheight < 160 || m_screenwidth < 160)
357  {
358  LOG(VB_GENERAL, LOG_ERR, LOC +
359  "Somehow, your screen size settings are bad.\n\t\t\t" +
360  QString("GuiResolution: %1\n\t\t\t")
361  .arg(GetMythDB()->GetSetting("GuiResolution")) +
362  QString(" old GuiWidth: %1\n\t\t\t")
363  .arg(GetMythDB()->GetNumSetting("GuiWidth")) +
364  QString(" old GuiHeight: %1\n\t\t\t")
365  .arg(GetMythDB()->GetNumSetting("GuiHeight")) +
366  QString("m_width: %1").arg(m_width) +
367  QString("m_height: %1\n\t\t\t").arg(m_height) +
368  "Falling back to 640x480");
369 
370  m_screenwidth = 640;
371  m_screenheight = 480;
372  }
373 
374  m_wmult = m_screenwidth / (float)m_baseWidth;
376 
377  // Default font, _ALL_ fonts inherit from this!
378  // e.g All fonts will be 19 pixels unless a new size is explicitly defined.
379  QFont font = QFont("Arial");
380 
381  if (!font.exactMatch())
382  font = QFont();
383 
384  font.setStyleHint(QFont::SansSerif, QFont::PreferAntialias);
385  font.setPixelSize(lroundf(19.0f * m_hmult));
386  int stretch = (int)(100 / GetPixelAspectRatio());
387  font.setStretch(stretch); // QT
388  m_fontStretch = stretch; // MythUI
389 
390  QApplication::setFont(font);
391 }
392 
394 {
395  if (m_pixelAspectRatio < 0)
396  {
397  if (!display_res)
398  {
399  DisplayRes *dispRes = DisplayRes::GetDisplayRes(); // create singleton
400 
401  if (dispRes)
403  else
404  m_pixelAspectRatio = 1.0;
405  }
406  else
408  }
409 
410  return m_pixelAspectRatio;
411 }
412 
414 {
415  // Wait for screen signal change, so we later get updated screen resolution
416  QEventLoop loop;
417  QTimer timer;
418  QDesktopWidget *desktop = QApplication::desktop();
419 
420  timer.setSingleShot(true);
421  QObject::connect(&timer, SIGNAL(timeout()),
422  &loop, SLOT(quit()));
423  QObject::connect(desktop, SIGNAL(resized(int)),
424  &loop, SLOT(quit()));
425  QObject::connect(desktop, SIGNAL(workAreaResized(int)),
426  &loop, SLOT(quit()));
427  timer.start(300); //300ms maximum wait
428  loop.exec();
429 }
430 
432 {
433  d = new MythUIHelperPrivate(this);
434 }
435 
437 {
438  delete d;
439 }
440 
442 {
443  d->Init();
444  d->callbacks = cbs;
445 
446  d->m_maxCacheSize.fetchAndStoreRelease(
447  GetMythDB()->GetNumSetting("UIImageCacheSize", 30) * 1024 * 1024);
448 
449  LOG(VB_GUI, LOG_INFO, LOC +
450  QString("MythUI Image Cache size set to %1 bytes")
451  .arg(d->m_maxCacheSize.fetchAndAddRelease(0)));
452 }
453 
454 // This init is used for showing the startup UI that is shown
455 // before the database is initialized. The above init is called later,
456 // after the DB is available.
457 // This class does not mind being Initialized twice.
459 {
460  d->Init();
461 }
462 
464 {
465  return &(d->callbacks);
466 }
467 
469 {
470  return d->screenSetup;
471 }
472 
474 {
476 }
477 
479 {
481  d->themecachedir.clear();
482 
483  if (GetMythDB()->GetBoolSetting("UseVideoModes", false))
484  {
485  DisplayRes *dispRes = DisplayRes::GetDisplayRes(); // create singleton
486 
487  if (dispRes)
488  {
489  d->display_res = dispRes;
490  // Make sure DisplayRes has current context info
492  // Switch to desired GUI resolution
493  if (d->display_res->SwitchToGUI())
494  {
496  }
497  }
498  }
499 
500  // Note the possibly changed screen settings
501  d->GetScreenBounds();
502 
503  qApp->setStyle("Windows");
504 
505  QString themename = GetMythDB()->GetSetting("Theme", DEFAULT_UI_THEME);
506  QString themedir = FindThemeDir(themename);
507 
508  ThemeInfo *themeinfo = new ThemeInfo(themedir);
509 
510  if (themeinfo)
511  {
512  d->m_isWide = themeinfo->IsWide();
513  d->m_baseWidth = themeinfo->GetBaseRes()->width();
514  d->m_baseHeight = themeinfo->GetBaseRes()->height();
515  d->m_themename = themeinfo->GetName();
516  LOG(VB_GUI, LOG_INFO, LOC +
517  QString("Using theme base resolution of %1x%2")
518  .arg(d->m_baseWidth).arg(d->m_baseHeight));
519  delete themeinfo;
520  }
521 
522  // Recalculate GUI dimensions
523  d->StoreGUIsettings();
524 
525  d->m_themepathname = themedir + '/';
526  d->m_searchPaths.clear();
527 
528  d->m_themeloaded = false;
529 
530  themename = GetMythDB()->GetSetting("MenuTheme", "defaultmenu");
531 
532  if (themename == "default")
533  themename = "defaultmenu";
534 
535  d->m_menuthemepathname = FindMenuThemeDir(themename);
536 }
537 
539 {
540  QMutexLocker locker(d->m_cacheLock);
541 
542  QMutableMapIterator<QString, MythImage *> i(d->imageCache);
543 
544  while (i.hasNext())
545  {
546  i.next();
547  i.value()->SetIsInCache(false);
548  i.value()->DecrRef();
549  i.remove();
550  }
551 
552  d->CacheTrack.clear();
553 
554  d->m_cacheSize.fetchAndStoreOrdered(0);
555 
559 }
560 
562 {
563  QMutexLocker locker(d->m_cacheLock);
564 
565  if (d->imageCache.contains(url))
566  {
567 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
568  d->CacheTrack[url] = MythDate::current().toTime_t();
569 #else
570  d->CacheTrack[url] = MythDate::current().toSecsSinceEpoch();
571 #endif
572  d->imageCache[url]->IncrRef();
573  return d->imageCache[url];
574  }
575 
576  /*
577  if (QFileInfo(url).exists())
578  {
579  MythImage *im = GetMythPainter()->GetFormatImage();
580  im->Load(url,false);
581  return im;
582  }
583  */
584 
585  return nullptr;
586 }
587 
589 {
590  if (im)
591 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
592  d->m_cacheSize.fetchAndAddOrdered(im->byteCount());
593 #else
594  d->m_cacheSize.fetchAndAddOrdered(im->sizeInBytes());
595 #endif
596 }
597 
599 {
600  if (im)
601 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
602  d->m_cacheSize.fetchAndAddOrdered(-im->byteCount());
603 #else
604  d->m_cacheSize.fetchAndAddOrdered(-im->sizeInBytes());
605 #endif
606 }
607 
609  bool nodisk)
610 {
611  if (!im)
612  return nullptr;
613 
614  if (!nodisk)
615  {
616  QString dstfile = GetCacheDirByUrl(url) + '/' + url;
617 
618  LOG(VB_GUI | VB_FILE, LOG_INFO, LOC +
619  QString("Saved to Cache (%1)").arg(dstfile));
620 
621  // Save to disk cache
622  im->save(dstfile, "PNG");
623  }
624 
625  // delete the oldest cached images until we fall below threshold.
626  QMutexLocker locker(d->m_cacheLock);
627 
628  while ((d->m_cacheSize.fetchAndAddOrdered(0) +
629 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
630  im->byteCount()
631 #else
632  im->sizeInBytes()
633 #endif
634  ) >=
635  d->m_maxCacheSize.fetchAndAddOrdered(0) &&
636  d->imageCache.size())
637  {
638  QMap<QString, MythImage *>::iterator it = d->imageCache.begin();
639 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
640  uint oldestTime = MythDate::current().toTime_t();
641 #else
642  qint64 oldestTime = MythDate::current().toSecsSinceEpoch();
643 #endif
644  QString oldestKey = it.key();
645 
646  int count = 0;
647 
648  for (; it != d->imageCache.end(); ++it)
649  {
650  if (d->CacheTrack[it.key()] < oldestTime)
651  {
652  if ((2 == it.value()->IncrRef()) && (it.value() != im))
653  {
654  oldestTime = d->CacheTrack[it.key()];
655  oldestKey = it.key();
656  count++;
657  }
658  it.value()->DecrRef();
659  }
660  }
661 
662  LOG(VB_GUI | VB_FILE, LOG_INFO, LOC +
663  QString("%1 images are eligible for expiry").arg(count));
664 
665  if (count > 0)
666  {
667  LOG(VB_GUI | VB_FILE, LOG_INFO, LOC +
668  QString("Cache too big (%1), removing :%2:")
669  .arg(d->m_cacheSize.fetchAndAddOrdered(0) +
670 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
671  im->byteCount()
672 #else
673  im->sizeInBytes()
674 #endif
675  )
676  .arg(oldestKey));
677 
678  d->imageCache[oldestKey]->SetIsInCache(false);
679  d->imageCache[oldestKey]->DecrRef();
680  d->imageCache.remove(oldestKey);
681  d->CacheTrack.remove(oldestKey);
682  }
683  else
684  {
685  break;
686  }
687  }
688 
689  QMap<QString, MythImage *>::iterator it = d->imageCache.find(url);
690 
691  if (it == d->imageCache.end())
692  {
693  im->IncrRef();
694  d->imageCache[url] = im;
695 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
696  d->CacheTrack[url] = MythDate::current().toTime_t();
697 #else
698  d->CacheTrack[url] = MythDate::current().toSecsSinceEpoch();
699 #endif
700 
701  im->SetIsInCache(true);
702  LOG(VB_GUI | VB_FILE, LOG_INFO, LOC +
703  QString("NOT IN RAM CACHE, Adding, and adding to size :%1: :%2:")
704  .arg(url)
705 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
706  .arg(im->byteCount())
707 #else
708  .arg(im->sizeInBytes())
709 #endif
710  );
711  }
712 
713  LOG(VB_GUI | VB_FILE, LOG_INFO, LOC +
714  QString("MythUIHelper::CacheImage : Cache Count = :%1: size :%2:")
715  .arg(d->imageCache.count())
716  .arg(d->m_cacheSize.fetchAndAddRelaxed(0)));
717 
718  return d->imageCache[url];
719 }
720 
721 void MythUIHelper::RemoveFromCacheByURL(const QString &url)
722 {
723  QMutexLocker locker(d->m_cacheLock);
724  QMap<QString, MythImage *>::iterator it = d->imageCache.find(url);
725 
726  if (it != d->imageCache.end())
727  {
728  d->imageCache[url]->SetIsInCache(false);
729  d->imageCache[url]->DecrRef();
730  d->imageCache.remove(url);
731  d->CacheTrack.remove(url);
732  }
733 
734  QString dstfile;
735 
736  dstfile = GetCacheDirByUrl(url) + '/' + url;
737  LOG(VB_GUI | VB_FILE, LOG_INFO, LOC +
738  QString("RemoveFromCacheByURL removed :%1: from cache").arg(dstfile));
739  QFile::remove(dstfile);
740 }
741 
742 void MythUIHelper::RemoveFromCacheByFile(const QString &fname)
743 {
744  QList<QString>::iterator it;
745 
746  QString partialKey = fname;
747  partialKey.replace('/', '-');
748 
749  d->m_cacheLock->lock();
750  QList<QString> imageCacheKeys = d->imageCache.keys();
751  d->m_cacheLock->unlock();
752 
753  for (it = imageCacheKeys.begin(); it != imageCacheKeys.end(); ++it)
754  {
755  if ((*it).contains(partialKey))
757  }
758 
759  // Loop through files to cache any that were not caught by
760  // RemoveFromCacheByURL
761  QDir dir(GetThemeCacheDir());
762  QFileInfoList list = dir.entryInfoList();
763 
764  for (int i = 0; i < list.size(); ++i)
765  {
766  QFileInfo fileInfo = list.at(i);
767 
768  if (fileInfo.fileName().contains(partialKey))
769  {
770  LOG(VB_GUI | VB_FILE, LOG_INFO, LOC +
771  QString("RemoveFromCacheByFile removed: %1: from cache")
772  .arg(fileInfo.fileName()));
773 
774  if (!dir.remove(fileInfo.fileName()))
775  LOG(VB_GENERAL, LOG_ERR, LOC +
776  QString("Failed to delete %1 from the theme cache")
777  .arg(fileInfo.fileName()));
778  }
779  }
780 }
781 
782 bool MythUIHelper::IsImageInCache(const QString &url)
783 {
784  QMutexLocker locker(d->m_cacheLock);
785 
786  if (d->imageCache.contains(url))
787  return true;
788 
789  if (QFileInfo(url).exists())
790  return true;
791 
792  return false;
793 }
794 
796 {
797  static QString oldcachedir;
798  QString tmpcachedir = GetThemeBaseCacheDir() + "/" +
799  GetMythDB()->GetSetting("Theme", DEFAULT_UI_THEME) +
800  "." + QString::number(d->m_screenwidth) +
801  "." + QString::number(d->m_screenheight);
802 
803  if (tmpcachedir != oldcachedir)
804  {
805  LOG(VB_GUI | VB_FILE, LOG_INFO, LOC +
806  QString("Creating cache dir: %1").arg(tmpcachedir));
807  QDir dir;
808  dir.mkdir(tmpcachedir);
809  oldcachedir = tmpcachedir;
810  }
811  return tmpcachedir;
812 }
813 
821 QString MythUIHelper::GetCacheDirByUrl(QString url)
822 {
823  if (url.startsWith("myth:") || url.startsWith("-"))
824  return GetThumbnailDir();
825  return GetThemeCacheDir();
826 }
827 
829 {
831 
832  QString themecachedir = d->themecachedir;
833 
834  d->themecachedir += '/';
835 
836  QDir dir(GetThemeBaseCacheDir());
837  dir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
838  QFileInfoList list = dir.entryInfoList();
839 
840  QFileInfoList::const_iterator it = list.begin();
841  QMap<QDateTime, QString> dirtimes;
842  const QFileInfo *fi;
843 
844  while (it != list.end())
845  {
846  fi = &(*it++);
847 
848  if (fi->isDir() && !fi->isSymLink())
849  {
850  if (fi->absoluteFilePath() == themecachedir)
851  continue;
852 
853  dirtimes[fi->lastModified()] = fi->absoluteFilePath();
854  }
855  }
856 
857  // Cache two themes/resolutions to allow sampling other themes without
858  // incurring a penalty. Especially for those writing new themes or testing
859  // changes of an existing theme. The space used is neglible when compared
860  // against the average video
861  while ((size_t)dirtimes.size() >= 2)
862  {
863  LOG(VB_GUI | VB_FILE, LOG_INFO, LOC + QString("Removing cache dir: %1")
864  .arg(dirtimes.begin().value()));
865 
866  RemoveCacheDir(dirtimes.begin().value());
867  dirtimes.erase(dirtimes.begin());
868  }
869 
870  QMap<QDateTime, QString>::const_iterator dit = dirtimes.begin();
871 
872  for (; dit != dirtimes.end(); ++dit)
873  {
874  LOG(VB_GUI | VB_FILE, LOG_INFO, LOC +
875  QString("Keeping cache dir: %1").arg(*dit));
876  }
877 }
878 
879 void MythUIHelper::RemoveCacheDir(const QString &dirname)
880 {
881  QString cachedirname = GetThemeBaseCacheDir();
882 
883  if (!dirname.startsWith(cachedirname))
884  return;
885 
886  LOG(VB_GENERAL, LOG_ERR, LOC +
887  QString("Removing stale cache dir: %1").arg(dirname));
888 
889  QDir dir(dirname);
890 
891  if (!dir.exists())
892  return;
893 
894  dir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
895  QFileInfoList list = dir.entryInfoList();
896  QFileInfoList::const_iterator it = list.begin();
897  const QFileInfo *fi;
898 
899  while (it != list.end())
900  {
901  fi = &(*it++);
902 
903  if (fi->isFile() && !fi->isSymLink())
904  {
905  QFile file(fi->absoluteFilePath());
906  file.remove();
907  }
908  else if (fi->isDir() && !fi->isSymLink())
909  {
910  RemoveCacheDir(fi->absoluteFilePath());
911  }
912  }
913 
914  dir.rmdir(dirname);
915 }
916 
923 void MythUIHelper::PruneCacheDir(QString dirname)
924 {
925  int days = GetMythDB()->GetNumSetting("UIDiskCacheDays", 7);
926  if (days == -1) {
927  LOG(VB_GENERAL, LOG_INFO, LOC +
928  QString("Pruning cache directory: %1 is disabled").arg(dirname));
929  return;
930  }
931 
932  LOG(VB_GENERAL, LOG_INFO, LOC +
933  QString("Pruning cache directory: %1").arg(dirname));
934  QDateTime cutoff = MythDate::current().addDays(-days);
935 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
936  qint64 cutoffsecs = cutoff.toMSecsSinceEpoch()/1000;
937 #else
938  qint64 cutoffsecs = cutoff.toSecsSinceEpoch();
939 #endif
940 
941  LOG(VB_GUI | VB_FILE, LOG_INFO, LOC +
942  QString("Removing files not accessed since %1")
943  .arg(cutoff.toLocalTime().toString(Qt::ISODate)));
944 
945  int kept = 0, deleted = 0, errcnt = 0;
946  QDir dir(dirname);
947  dir.setFilter(QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot);
948  dir.setSorting(QDir::NoSort);
949 
950  // Trying to save every cycle possible within this loop. The
951  // stat() call seems significantly faster than the fi.fileRead()
952  // method. The documentation for QFileInfo says that the
953  // fi.absoluteFilePath() method has to query the file system, so
954  // use fi.filePath() method here and then add the directory if
955  // needed. Using dir.entryList() and adding the dirname each time
956  // is also slower just using dir.entryInfoList().
957  foreach (const QFileInfo &fi, dir.entryInfoList())
958  {
959  struct stat buf;
960  QString fullname = fi.filePath();
961  if (not fullname.startsWith('/'))
962  fullname = dirname + "/" + fullname;
963  QByteArray fullname8 = fullname.toLocal8Bit();
964  int rc = stat(fullname8, &buf);
965  if (rc == -1)
966  {
967  errcnt += 1;
968  continue;
969  }
970  if (buf.st_atime >= cutoffsecs)
971  {
972  kept += 1;
973  LOG(VB_GUI | VB_FILE, LOG_DEBUG, LOC +
974  QString("%1 Keep %2")
975  .arg(fi.lastRead().toLocalTime().toString(Qt::ISODate))
976  .arg(fi.fileName()));
977  continue;
978  }
979  deleted += 1;
980  LOG(VB_GUI | VB_FILE, LOG_DEBUG, LOC +
981  QString("%1 Delete %2")
982  .arg(fi.lastRead().toLocalTime().toString(Qt::ISODate))
983  .arg(fi.fileName()));
984  unlink(fullname8);
985  }
986 
987  LOG(VB_GENERAL, LOG_INFO, LOC +
988  QString("Kept %1 files, deleted %2 files, stat error on %3 files")
989  .arg(kept).arg(deleted).arg(errcnt));
990 }
991 
992 void MythUIHelper::GetScreenBounds(int &xbase, int &ybase,
993  int &width, int &height)
994 {
995  xbase = d->m_xbase;
996  ybase = d->m_ybase;
997 
998  width = d->m_width;
999  height = d->m_height;
1000 }
1001 
1002 void MythUIHelper::GetScreenSettings(float &wmult, float &hmult)
1003 {
1004  wmult = d->m_wmult;
1005  hmult = d->m_hmult;
1006 }
1007 
1008 void MythUIHelper::GetScreenSettings(int &width, float &wmult,
1009  int &height, float &hmult)
1010 {
1011  height = d->m_screenheight;
1012  width = d->m_screenwidth;
1013 
1014  wmult = d->m_wmult;
1015  hmult = d->m_hmult;
1016 }
1017 
1018 void MythUIHelper::GetScreenSettings(int &xbase, int &width, float &wmult,
1019  int &ybase, int &height, float &hmult)
1020 {
1021  xbase = d->m_screenxbase;
1022  ybase = d->m_screenybase;
1023 
1024  height = d->m_screenheight;
1025  width = d->m_screenwidth;
1026 
1027  wmult = d->m_wmult;
1028  hmult = d->m_hmult;
1029 }
1030 
1040 void MythUIHelper::ParseGeometryOverride(const QString &geometry)
1041 {
1042  QRegExp sre("^(\\d+)x(\\d+)$");
1043  QRegExp lre("^(\\d+)x(\\d+)([+-]\\d+)([+-]\\d+)$");
1044  QStringList geo;
1045  bool longForm = false;
1046 
1047  if (sre.exactMatch(geometry))
1048  {
1049  geo = sre.capturedTexts();
1050  }
1051  else if (lre.exactMatch(geometry))
1052  {
1053  geo = lre.capturedTexts();
1054  longForm = true;
1055  }
1056  else
1057  {
1058  LOG(VB_GENERAL, LOG_ERR, LOC +
1059  "Geometry does not match either form -\n\t\t\t"
1060  "WIDTHxHEIGHT or WIDTHxHEIGHT+XOFF+YOFF");
1061  return;
1062  }
1063 
1064  bool parsed;
1065  int tmp_w, tmp_h;
1066 
1067  tmp_w = geo[1].toInt(&parsed);
1068 
1069  if (!parsed)
1070  {
1071  LOG(VB_GENERAL, LOG_ERR, LOC +
1072  "Could not parse width of geometry override");
1073  }
1074 
1075  if (parsed)
1076  {
1077  tmp_h = geo[2].toInt(&parsed);
1078 
1079  if (!parsed)
1080  {
1081  LOG(VB_GENERAL, LOG_ERR, LOC +
1082  "Could not parse height of geometry override");
1083  }
1084  }
1085 
1086  if (parsed)
1087  {
1090  LOG(VB_GENERAL, LOG_INFO, LOC +
1091  QString("Overriding GUI size: width=%1 height=%2")
1092  .arg(tmp_w).arg(tmp_h));
1093  }
1094  else
1095  {
1096  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to override GUI size.");
1097  }
1098 
1099  if (longForm)
1100  {
1101  int tmp_x, tmp_y;
1102  tmp_x = geo[3].toInt(&parsed);
1103 
1104  if (!parsed)
1105  {
1106  LOG(VB_GENERAL, LOG_ERR, LOC +
1107  "Could not parse horizontal offset of geometry override");
1108  }
1109 
1110  if (parsed)
1111  {
1112  tmp_y = geo[4].toInt(&parsed);
1113 
1114  if (!parsed)
1115  {
1116  LOG(VB_GENERAL, LOG_ERR, LOC +
1117  "Could not parse vertical offset of geometry override");
1118  }
1119  }
1120 
1121  if (parsed)
1122  {
1125  LOG(VB_GENERAL, LOG_INFO, LOC +
1126  QString("Overriding GUI offset: x=%1 y=%2")
1127  .arg(tmp_x).arg(tmp_y));
1128  }
1129  else
1130  {
1131  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to override GUI offset.");
1132  }
1133  }
1134 }
1135 
1137 {
1138  return (MythUIHelperPrivate::x_override >= 0 ||
1142 }
1143 
1155 QString MythUIHelper::FindThemeDir(const QString &themename, bool doFallback)
1156 {
1157  QString testdir;
1158  QDir dir;
1159 
1160  if (!themename.isEmpty())
1161  {
1162  testdir = d->m_userThemeDir + themename;
1163 
1164  dir.setPath(testdir);
1165 
1166  if (dir.exists())
1167  return testdir;
1168 
1169  testdir = GetThemesParentDir() + themename;
1170  dir.setPath(testdir);
1171 
1172  if (dir.exists())
1173  return testdir;
1174 
1175  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("No theme dir: '%1'")
1176  .arg(dir.absolutePath()));
1177  }
1178 
1179  if (!doFallback)
1180  return QString();
1181 
1182  testdir = GetThemesParentDir() + DEFAULT_UI_THEME;
1183  dir.setPath(testdir);
1184 
1185  if (dir.exists())
1186  {
1187  LOG(VB_GENERAL, LOG_ERR, LOC +
1188  QString("Could not find theme: %1 - Switching to %2")
1189  .arg(themename).arg(DEFAULT_UI_THEME));
1191  return testdir;
1192  }
1193 
1194  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("No default theme dir: '%1'")
1195  .arg(dir.absolutePath()));
1196 
1197  testdir = GetThemesParentDir() + FALLBACK_UI_THEME;
1198  dir.setPath(testdir);
1199 
1200  if (dir.exists())
1201  {
1202  LOG(VB_GENERAL, LOG_ERR, LOC +
1203  QString("Could not find theme: %1 - Switching to %2")
1204  .arg(themename).arg(FALLBACK_UI_THEME));
1206  return testdir;
1207  }
1208 
1209  LOG(VB_GENERAL, LOG_ERR, LOC + QString("No fallback GUI theme dir: '%1'")
1210  .arg(dir.absolutePath()));
1211 
1212  return QString();
1213 }
1214 
1223 QString MythUIHelper::FindMenuThemeDir(const QString &menuname)
1224 {
1225  QString testdir;
1226  QDir dir;
1227 
1228  testdir = d->m_userThemeDir + menuname;
1229 
1230  dir.setPath(testdir);
1231 
1232  if (dir.exists())
1233  return testdir;
1234 
1235  testdir = GetThemesParentDir() + menuname;
1236  dir.setPath(testdir);
1237 
1238  if (dir.exists())
1239  return testdir;
1240 
1241  testdir = GetShareDir();
1242  dir.setPath(testdir);
1243 
1244  if (dir.exists())
1245  {
1246  LOG(VB_GENERAL, LOG_ERR, LOC +
1247  QString("Could not find menu theme: %1 - Switching to default")
1248  .arg(menuname));
1249 
1250  GetMythDB()->SaveSetting("MenuTheme", "default");
1251  return testdir;
1252  }
1253 
1254  LOG(VB_GENERAL, LOG_ERR, LOC +
1255  QString("Could not find menu theme: %1 - Fallback to default failed.")
1256  .arg(menuname));
1257 
1258  return QString();
1259 }
1260 
1262 {
1263  return d->m_menuthemepathname;
1264 }
1265 
1267 {
1268  return d->m_themepathname;
1269 }
1270 
1272 {
1273  return d->m_themename;
1274 }
1275 
1277 {
1278  if (!d->m_searchPaths.isEmpty())
1279  return d->m_searchPaths;
1280 
1281  // traverse up the theme inheritance list adding their location to the search path
1282  QList<ThemeInfo> themeList = GetThemes(THEME_UI);
1283  bool found = true;
1284  QString themeName = d->m_themename;
1285  QString baseName;
1286  QString dirName;
1287 
1288  while (found && !themeName.isEmpty())
1289  {
1290  // find the ThemeInfo for this theme
1291  found = false;
1292  baseName = "";
1293  dirName = "";
1294 
1295  for (int x = 0; x < themeList.count(); x++)
1296  {
1297  if (themeList.at(x).GetName() == themeName)
1298  {
1299  found = true;
1300  baseName = themeList.at(x).GetBaseTheme();
1301  dirName = themeList.at(x).GetDirectoryName();
1302  break;
1303  }
1304  }
1305 
1306  // try to find where the theme is installed
1307  if (found)
1308  {
1309  QString themedir = FindThemeDir(dirName, false);
1310  if (!themedir.isEmpty())
1311  {
1312  LOG(VB_GUI, LOG_INFO, LOC + QString("Adding path '%1' to theme search paths").arg(themedir));
1313  d->m_searchPaths.append(themedir + '/');
1314  }
1315  else
1316  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Could not find ui theme location: %1").arg(themedir));
1317  }
1318  else
1319  {
1320  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Could not find inherited theme: %1").arg(themeName));
1321  }
1322 
1323  themeName = baseName;
1324  }
1325 
1326  if (d->m_isWide)
1327  d->m_searchPaths.append(GetThemesParentDir() + "default-wide/");
1328 
1329  d->m_searchPaths.append(GetThemesParentDir() + "default/");
1330  d->m_searchPaths.append("/tmp/");
1331  return d->m_searchPaths;
1332 }
1333 
1335 {
1336  QFileInfoList fileList;
1337  QList<ThemeInfo> themeList;
1338  QDir themeDirs(GetThemesParentDir());
1339  themeDirs.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
1340  themeDirs.setSorting(QDir::Name | QDir::IgnoreCase);
1341 
1342  fileList.append(themeDirs.entryInfoList());
1343 
1344  themeDirs.setPath(d->m_userThemeDir);
1345 
1346  fileList.append(themeDirs.entryInfoList());
1347 
1348  for (QFileInfoList::iterator it = fileList.begin();
1349  it != fileList.end(); ++it)
1350  {
1351  QFileInfo &theme = *it;
1352 
1353  if (theme.baseName() == "default" ||
1354  theme.baseName() == "default-wide" ||
1355  theme.baseName() == "Slave")
1356  continue;
1357 
1358  ThemeInfo themeInfo(theme.absoluteFilePath());
1359 
1360  if (themeInfo.GetType() & type)
1361  themeList.append(themeInfo);
1362  }
1363 
1364  return themeList;
1365 }
1366 
1367 bool MythUIHelper::FindThemeFile(QString &path)
1368 {
1369  QFileInfo fi(path);
1370 
1371  if (fi.isAbsolute() && fi.exists())
1372  return true;
1373 #ifdef Q_OS_ANDROID
1374  if (path.startsWith("assets:/") && fi.exists())
1375  return true;
1376 #endif
1377 
1378  QString file;
1379  bool foundit = false;
1380  const QStringList searchpath = GetThemeSearchPath();
1381 
1382  for (QStringList::const_iterator ii = searchpath.begin();
1383  ii != searchpath.end(); ++ii)
1384  {
1385  if (fi.isRelative())
1386  {
1387  file = *ii + fi.filePath();
1388  }
1389  else if (fi.isAbsolute() && !fi.isRoot())
1390  {
1391  file = *ii + fi.fileName();
1392  }
1393 
1394  if (QFile::exists(file))
1395  {
1396  path = file;
1397  foundit = true;
1398  break;
1399  }
1400  }
1401 
1402  return foundit;
1403 }
1404 
1405 MythImage *MythUIHelper::LoadCacheImage(QString srcfile, QString label,
1406  MythPainter *painter,
1407  ImageCacheMode cacheMode)
1408 {
1409  LOG(VB_GUI | VB_FILE, LOG_INFO, LOC +
1410  QString("LoadCacheImage(%1,%2)").arg(srcfile).arg(label));
1411 
1412  if (srcfile.isEmpty() || label.isEmpty())
1413  return nullptr;
1414 
1415  if (!(kCacheForceStat & cacheMode))
1416  {
1417  // Some screens include certain images dozens or even hundreds of
1418  // times. Even if the image is in the cache, there is still a
1419  // stat system call on the original file to see if it has changed.
1420  // This code relaxes the original-file check so that the check
1421  // isn't repeated if it was already done within kImageCacheTimeout
1422  // seconds.
1423 
1424  // This only applies to the MEMORY cache
1425  const uint kImageCacheTimeout = 60;
1426 #if QT_VERSION < QT_VERSION_CHECK(5,8,0)
1427  uint now = MythDate::current().toTime_t();
1428 #else
1429  qint64 now = MythDate::current().toSecsSinceEpoch();
1430 #endif
1431 
1432  QMutexLocker locker(d->m_cacheLock);
1433 
1434  if (d->imageCache.contains(label) &&
1435  d->CacheTrack[label] + kImageCacheTimeout > now)
1436  {
1437  d->imageCache[label]->IncrRef();
1438  return d->imageCache[label];
1439  }
1440  }
1441 
1442  MythImage *ret = nullptr;
1443 
1444  // Check Memory Cache
1445  ret = GetImageFromCache(label);
1446 
1447  // If the image is in the memory or we are not ignoring the disk cache
1448  // then proceed to check whether the source file is newer than our cached
1449  // copy
1450  if (ret || !(cacheMode & kCacheIgnoreDisk))
1451  {
1452  // Create url to image in disk cache
1453  QString cachefilepath;
1454  cachefilepath = GetCacheDirByUrl(label) + '/' + label;
1455  QFileInfo cacheFileInfo(cachefilepath);
1456 
1457  // If the file isn't in the disk cache, then we don't want to bother
1458  // checking the last modified times of the original
1459  if (!cacheFileInfo.exists())
1460  return nullptr;
1461 
1462  // Now compare the time on the source versus our cached copy
1463  QDateTime srcLastModified;
1464 
1465  // For internet images this involves querying the headers of the remote
1466  // image. This is slow even without redownloading the whole image
1467  if ((srcfile.startsWith("http://")) ||
1468  (srcfile.startsWith("https://")) ||
1469  (srcfile.startsWith("ftp://")))
1470  {
1471  // If the image is in the memory cache then skip the last modified
1472  // check, since memory cached images are loaded in the foreground
1473  // this can cause an intolerable delay. The images won't stay in
1474  // the cache forever and so eventually they will be checked.
1475  if (ret)
1476  srcLastModified = cacheFileInfo.lastModified();
1477  else
1478  {
1479  srcLastModified =
1481  }
1482  }
1483  else if (srcfile.startsWith("myth://"))
1484  srcLastModified = RemoteFile::LastModified(srcfile);
1485  else
1486  {
1487  if (!FindThemeFile(srcfile))
1488  return nullptr;
1489 
1490  QFileInfo original(srcfile);
1491 
1492  if (original.exists())
1493  srcLastModified = original.lastModified();
1494  }
1495 
1496  // Now compare the timestamps, if the cached image is newer than the
1497  // source image we can use it, otherwise we want to remove it from the
1498  // cache
1499  if (cacheFileInfo.lastModified() >= srcLastModified)
1500  {
1501  // If we haven't already loaded the image from the memory cache
1502  // and we're not ignoring the disk cache, then it's time to load
1503  // it from there instead
1504  if (!ret && (cacheMode == kCacheNormal))
1505  {
1506 
1507  if (painter)
1508  {
1509  ret = painter->GetFormatImage();
1510 
1511  // Load file from disk cache to memory cache
1512  if (ret->Load(cachefilepath))
1513  {
1514  // Add to ram cache, and skip saving to disk since that is
1515  // where we found this in the first place.
1516  CacheImage(label, ret, true);
1517  }
1518  else
1519  {
1520  LOG(VB_GUI | VB_FILE, LOG_WARNING, LOC +
1521  QString("LoadCacheImage: Could not load :%1")
1522  .arg(cachefilepath));
1523 
1524  ret->SetIsInCache(false);
1525  ret->DecrRef();
1526  ret = nullptr;
1527  }
1528  }
1529  }
1530  }
1531  else
1532  {
1533  ret = nullptr;
1534  // If file has changed on disk, then remove it from the memory
1535  // and disk cache
1536  RemoveFromCacheByURL(label);
1537  }
1538  }
1539 
1540  return ret;
1541 }
1542 
1544 {
1545  QFont font = QApplication::font();
1546  font.setPointSize(GetMythMainWindow()->NormalizeFontSize(25));
1547  font.setWeight(QFont::Bold);
1548 
1549  return font;
1550 }
1551 
1553 {
1554  QFont font = QApplication::font();
1555  font.setPointSize(GetMythMainWindow()->NormalizeFontSize(16));
1556  font.setWeight(QFont::Bold);
1557 
1558  return font;
1559 }
1560 
1562 {
1563  QFont font = QApplication::font();
1564  font.setPointSize(GetMythMainWindow()->NormalizeFontSize(12));
1565  font.setWeight(QFont::Bold);
1566 
1567  return font;
1568 }
1569 
1571 {
1572  if (qobject_cast<QApplication*>(qApp))
1573  {
1574  QCoreApplication::postEvent(
1577  }
1578 }
1579 
1581 {
1582  if (qobject_cast<QApplication*>(qApp))
1583  {
1584  QCoreApplication::postEvent(
1587  }
1588 }
1589 
1591 {
1592  if (qobject_cast<QApplication*>(qApp))
1593  {
1594  QCoreApplication::postEvent(
1597  }
1598 }
1599 
1601 {
1602  if (d->screensaver)
1603  {
1604  d->screensaver->Disable();
1605  d->screensaverEnabled = false;
1606  }
1607 }
1608 
1610 {
1611  if (d->screensaver)
1612  {
1613  d->screensaver->Restore();
1614  d->screensaverEnabled = true;
1615  }
1616 }
1617 
1619 {
1620  if (d->screensaver)
1621  {
1622  d->screensaver->Reset();
1623  d->screensaverEnabled = false;
1624  }
1625 }
1626 
1628 {
1629  return d->screensaverEnabled;
1630 }
1631 
1633 {
1634  if (!d->screensaver)
1635  return false;
1636 
1637  return d->screensaver->Asleep();
1638 }
1639 
1642 void MythUIHelper::SetX11Display(const QString &display)
1643 {
1644  x11_display = display;
1645 }
1646 
1648 {
1649  return x11_display;
1650 }
1651 
1652 void MythUIHelper::AddCurrentLocation(QString location)
1653 {
1654  QMutexLocker locker(&m_locationLock);
1655 
1656  if (m_currentLocation.isEmpty() || m_currentLocation.last() != location)
1657  m_currentLocation.push_back(location);
1658 }
1659 
1661 {
1662  QMutexLocker locker(&m_locationLock);
1663 
1664  if (m_currentLocation.isEmpty())
1665  return QString("UNKNOWN");
1666 
1667  return m_currentLocation.takeLast();
1668 }
1669 
1670 QString MythUIHelper::GetCurrentLocation(bool fullPath, bool mainStackOnly)
1671 {
1672  QString result;
1673  QMutexLocker locker(&m_locationLock);
1674 
1675  if (fullPath)
1676  {
1677  // get main stack top screen
1679  result = stack->GetLocation(true);
1680 
1681  if (!mainStackOnly)
1682  {
1683  // get popup stack main screen
1684  stack = GetMythMainWindow()->GetStack("popup stack");
1685 
1686  if (!stack->GetLocation(true).isEmpty())
1687  result += '/' + stack->GetLocation(false);
1688  }
1689 
1690  // if there's a location in the stringlist add that (non mythui screen or external app running)
1691  if (!m_currentLocation.isEmpty())
1692  {
1693  for (int x = 0; x < m_currentLocation.count(); x++)
1694  result += '/' + m_currentLocation[x];
1695  }
1696  }
1697  else
1698  {
1699  // get main stack top screen
1701  result = stack->GetLocation(false);
1702 
1703  if (!mainStackOnly)
1704  {
1705  // get popup stack top screen
1706  stack = GetMythMainWindow()->GetStack("popup stack");
1707 
1708  if (!stack->GetLocation(false).isEmpty())
1709  result = stack->GetLocation(false);
1710  }
1711 
1712  // if there's a location in the stringlist use that (non mythui screen or external app running)
1713  if (!m_currentLocation.isEmpty())
1714  result = m_currentLocation.last();
1715  }
1716 
1717  if (result.isEmpty())
1718  result = "UNKNOWN";
1719 
1720  return result;
1721 }
1722 
1724 {
1725  return d->m_imageThreadPool;
1726 }
1727 
1729 {
1730  return d->GetPixelAspectRatio();
1731 }
1732 
1733 QSize MythUIHelper::GetBaseSize(void) const
1734 {
1735  return QSize(d->m_baseWidth, d->m_baseHeight);
1736 }
1737 
1739 {
1740  d->m_fontStretch = stretch;
1741 }
1742 
1744 {
1745  return d->m_fontStretch;
1746 }
static DisplayRes * GetDisplayRes(bool lock=false)
Factory method that returns a DisplayRes singleton.
Definition: DisplayRes.cpp:18
double GetPixelAspectRatio(void) const
Returns the pixel aspect ratio of the display.
Definition: DisplayRes.h:132
QString GetCacheDirByUrl(QString url)
Look at the url being read and decide whether the cached version should go into the theme cache or th...
MythImage * CacheImage(const QString &url, MythImage *im, bool nodisk=false)
QString FindThemeDir(const QString &themename, bool doFallback=true)
Returns the full path to the theme denoted by themename.
Controls all instances of the screensaver.
Definition: screensaver.h:42
QMutex m_locationLock
Definition: mythuihelper.h:152
void SetFontStretch(int stretch)
void Disable(void)
Definition: screensaver.cpp:56
static QString themedir
Definition: mythdirs.cpp:21
void LoadQtConfig(void)
double GetPixelAspectRatio(void)
QPalette m_palette
Colour scheme.
static QString GetX11Display(void)
DisplayRes * display_res
bool Load(MythImageReader *reader)
Definition: mythimage.cpp:287
ImageCacheMode
Definition: mythuihelper.h:24
QFont GetMediumFont(void)
void DisableScreensaver(void)
bool IsImageInCache(const QString &url)
int DecrRef(void) override
Decrements reference count and deletes on 0.
Definition: mythimage.cpp:71
void PruneCacheDir(QString dirname)
Remove all files in the cache that haven't been accessed in a user configurable number of days.
#define LOC
void GetResolutionSetting(const QString &type, int &width, int &height, double &forced_aspect, double &refresh_rate, int index=-1)
bool IsWide() const
Definition: themeinfo.cpp:246
QString GetThumbnailDir(void)
Returns the directory where all non-theme thumbnail files should be cached.
Definition: mythdirs.cpp:249
void RemoveCacheDir(const QString &dirname)
QSize GetBaseSize(void) const
void ClearOldImageCache(void)
QString GetLocation(bool fullPath) const
void AddCurrentLocation(QString location)
MythScreenStack * GetStack(const QString &stackname)
bool IsScreenSetup(void)
#define DEFAULT_UI_THEME
Definition: mythuihelper.h:12
void Init(void)
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QString GetCurrentLocation(bool fullPath=false, bool mainStackOnly=true)
MythScreenStack * GetMainStack()
void DoResetScreensaver(void)
QFont GetSmallFont(void)
QString GetThemeDir(void)
void Restore(void)
Definition: screensaver.cpp:63
void DestroyMythUI()
void OverrideSettingForSession(const QString &key, const QString &newValue)
Overrides the given setting for the execution time of the process.
Definition: mythdb.cpp:786
bool FindThemeFile(QString &filename)
bool IsInitialized(void) const
Has Init() been called on this screen?
QString GetName() const
Definition: themeinfo.h:30
void SetIsInCache(bool bCached)
Definition: mythimage.cpp:89
int GetNumSetting(const QString &key, int defaultval)
Definition: mythdb.cpp:563
void(* exec_program)(const QString &cmd)
Definition: mythuihelper.h:34
MythUIHelperPrivate(MythUIHelper *p)
MThreadPool * m_imageThreadPool
static void SetX11Display(const QString &display)
This needs to be set before MythUIHelper is initialized so that the MythUIHelper::Init() can detect X...
QString GetThemeCacheDir(void)
QString FindMenuThemeDir(const QString &menuname)
Returns the full path to the menu theme denoted by menuname.
double GetPixelAspectRatio(void) const
VERBOSE_PREAMBLE false
Definition: verbosedefs.h:85
QStringList GetThemeSearchPath(void)
MythUIHelperPrivate * d
Definition: mythuihelper.h:150
virtual MythScreenType * GetTopScreen(void) const
QStringList m_currentLocation
Definition: mythuihelper.h:153
void DoDisableScreensaver(void)
bool SwitchToGUI(tmode which_gui=GUI)
Switches to the GUI resolution specified.
Definition: DisplayRes.cpp:188
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QList< ThemeInfo > GetThemes(ThemeType type)
static int GetNumberXineramaScreens(void)
int IncrRef(void) override
Increments reference count.
Definition: mythimage.cpp:63
QString GetShareDir(void)
Definition: mythdirs.cpp:222
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
static void ParseGeometryOverride(const QString &geometry)
Parse an X11 style command line geometry string.
bool GetBoolSetting(const QString &key, bool defaultval)
Definition: mythdb.cpp:555
QString GetMenuThemeDir(void)
void ExcludeFromCacheSize(MythImage *im)
void DoRestoreScreensaver(void)
void(* configplugin)(const QString &cmd)
Definition: mythuihelper.h:36
void StoreGUIsettings(void)
Apply any user overrides to the screen geometry.
bool Initialize(void)
Initialize DisplayRes, normally called automatically.
Definition: DisplayRes.cpp:49
QDateTime GetLastModified(const QString &url)
Gets the Last Modified timestamp for a URI.
int GetFontStretch(void) const
void UpdateImageCache(void)
QDateTime LastModified(void) const
QStringList m_searchPaths
static void destroyMythUI(void)
QFont GetBigFont(void)
void WaitForScreenChange(void) const
QString GetThemeBaseCacheDir(void)
Returns the base directory where all theme related files should be cached.
Definition: mythdirs.cpp:257
MythImage * GetImageFromCache(const QString &url)
Returns a reference counted image base on the URL.
MythUIHelper * GetMythUI()
QString GetSetting(const QString &_key, const QString &defaultval)
Definition: mythdb.cpp:362
ScreenSaverControl * screensaver
void RemoveFromCacheByURL(const QString &url)
MythMainWindow * GetMythMainWindow(void)
MythImage * LoadCacheImage(QString srcfile, QString label, MythPainter *painter, ImageCacheMode cacheMode=kCacheNormal)
Returns a reference counted image from the cache.
bool m_themeloaded
Do we have a palette and pixmap to use?
void RestoreScreensaver(void)
QString GetThemeName(void)
#define FALLBACK_UI_THEME
Definition: mythuihelper.h:13
ThemeType
Definition: themeinfo.h:14
bool IsTopScreenInitialized(void)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
void(* eject)(void)
Definition: mythuihelper.h:38
void GetScreenBounds(void)
Get screen size from Qt, respecting for user's multiple screen prefs.
static QMutex uiLock
QString RemoveCurrentLocation(void)
QAtomicInteger< qint64 > m_maxCacheSize
void IncludeInCacheSize(MythImage *im)
MythUIHelper * parent
void(* plugin)(const QString &cmd)
Definition: mythuihelper.h:37
static QString x11_display
Definition: mythuihelper.h:122
bool GetScreenIsAsleep(void)
static MythUIHelper * getMythUI(void)
string themeName
Definition: mythburn.py:189
QString GetThemesParentDir(void)
Definition: mythdirs.cpp:225
MythUIMenuCallbacks callbacks
void SaveSetting(const QString &key, int newValue)
Definition: mythdb.cpp:243
void ResetScreensaver(void)
MythDB * GetMythDB(void)
Definition: mythdb.cpp:46
The DisplayRes module allows for the display resolution and refresh rateto be changed "on the fly".
Definition: DisplayRes.h:32
void(* exec_program_tv)(const QString &cmd)
Definition: mythuihelper.h:35
static MythUIHelper * mythui
const QSize * GetBaseRes() const
Definition: themeinfo.h:29
bool IsGeometryOverridden(void)
MThreadPool * GetImageThreadPool(void)
MythImage * GetFormatImage()
Returns a blank reference counted image in the format required for the Draw functions for this painte...
QString GetHostName(void)
void GetScreenBounds(int &xbase, int &ybase, int &width, int &height)
QString GetRemoteCacheDir(void)
Returns the directory for all files cached from the backend.
Definition: mythdirs.cpp:241
Default UTC.
Definition: mythdate.h:14
MythUIMenuCallbacks * GetMenuCBs(void)
bool GetScreensaverEnabled(void)
QMap< QString, MythImage * > imageCache
void GetScreenSettings(float &wmult, float &hmult)
static void SwitchToDesktop(void)
Return the screen to the original desktop settings.
Definition: DisplayRes.cpp:43
QAtomicInteger< qint64 > m_cacheSize
void RemoveFromCacheByFile(const QString &fname)
void ResetLanguage(void)
QMap< QString, qint64 > CacheTrack