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