MythTV  master
mythmainwindow.cpp
Go to the documentation of this file.
1 #include "mythmainwindow.h"
3 
4 // C headers
5 #include <cmath>
6 
7 // C++ headers
8 #include <algorithm>
9 #include <utility>
10 #include <vector>
11 using namespace std;
12 
13 // QT headers
14 #include <QWaitCondition>
15 #include <QApplication>
16 #include <QTimer>
17 #include <QHash>
18 #include <QFile>
19 #include <QDir>
20 #include <QEvent>
21 #include <QKeyEvent>
22 #include <QKeySequence>
23 #include <QSize>
24 #include <QWindow>
25 
26 // Platform headers
27 #include "unistd.h"
28 
29 // libmythbase headers
30 #include "mythdb.h"
31 #include "mythlogging.h"
32 #include "mythevent.h"
33 #include "mythdirs.h"
34 #include "compat.h"
35 #include "mythsignalingtimer.h"
36 #include "mythcorecontext.h"
37 #include "mythmedia.h"
38 #include "mythmiscutil.h"
39 #include "mythdate.h"
40 
41 // libmythui headers
42 #include "myththemebase.h"
43 #include "screensaver.h"
44 #include "lirc.h"
45 #include "lircevent.h"
46 #include "mythudplistener.h"
47 #include "mythrender_base.h"
48 #include "mythuistatetracker.h"
49 #include "mythuiactions.h"
50 #include "mythrect.h"
51 #include "mythuidefines.h"
52 #include "mythdisplay.h"
53 
54 #ifdef USING_APPLEREMOTE
55 #include "AppleRemoteListener.h"
56 #endif
57 
58 #ifdef USE_JOYSTICK_MENU
59 #include "jsmenu.h"
60 #include "jsmenuevent.h"
61 #endif
62 
63 #ifdef USING_LIBCEC
64 #include "cecadapter.h"
65 #endif
66 
67 #include "mythscreentype.h"
68 #include "mythpainter.h"
69 #ifdef USING_OPENGL
70 #include "mythpainteropengl.h"
71 #endif
72 #include "mythpainter_qt.h"
73 #include "mythgesture.h"
74 #include "mythuihelper.h"
75 #include "mythdialogbox.h"
76 
77 #ifdef _WIN32
78 #include "mythpainter_d3d9.h"
79 #endif
80 
81 #ifdef Q_OS_ANDROID
82 #include <QtAndroid>
83 #endif
84 
85 #define GESTURE_TIMEOUT 1000
86 #define STANDBY_TIMEOUT 90 // Minutes
87 #define LONGPRESS_INTERVAL 1000
88 
89 #define LOC QString("MythMainWindow: ")
90 
92 {
93  public:
94  void AddMapping(int key, const QString& action)
95  {
96  m_actionMap[key].append(action);
97  }
98 
99  bool GetMapping(int key, QStringList &actions)
100  {
101  if (m_actionMap.count(key) > 0)
102  {
103  actions += m_actionMap[key];
104  return true;
105  }
106  return false;
107  }
108 
109  QMap<int, QStringList> m_actionMap;
110 };
111 
112 struct JumpData
113 {
114  void (*m_callback)(void);
115  QString m_destination;
116  QString m_description;
118  QString m_localAction;
119 };
120 
121 struct MPData {
122  QString m_description;
124 };
125 
127 {
128  public:
130  m_gesture(MythGesture())
131  {
132  }
133 
134  static int TranslateKeyNum(QKeyEvent *e);
135 
136  float m_wmult {1.0F};
137  float m_hmult {1.0F};
138  int m_screenwidth {0};
139  int m_screenheight {0};
140 
143 
144  int m_xbase {0};
145  int m_ybase {0};
146  bool m_doesFillScreen {false};
147 
148  bool m_ignoreLircKeys {false};
149  bool m_ignoreJoystickKeys {false};
150 
151  LIRC *m_lircThread {nullptr};
152 
153 #ifdef USE_JOYSTICK_MENU
154  JoystickMenuThread *m_joystickThread {nullptr};
155 #endif
156 
157 #ifdef USING_APPLEREMOTE
158  AppleRemoteListener *m_appleRemoteListener {nullptr};
159  AppleRemote *m_appleRemote {nullptr};
160 #endif
161 
162 #ifdef USING_LIBCEC
163  CECAdapter *m_cecAdapter {nullptr};
164 #endif
165 
166  bool m_exitingtomain {false};
167  bool m_popwindows {false};
168 
170  bool m_useDB {true};
171 
172  QHash<QString, KeyContext *> m_keyContexts;
173  QMap<int, JumpData*> m_jumpMap;
174  QMap<QString, JumpData> m_destinationMap;
175  QMap<QString, MPData> m_mediaPluginMap;
176  QHash<QString, QHash<QString, QString> > m_actionText;
177 
178  void (*m_exitMenuCallback)(void) {nullptr};
179 
180  void (*m_exitMenuMediaDeviceCallback)(MythMediaDevice* mediadevice) {nullptr};
181  MythMediaDevice * m_mediaDeviceForCallback {nullptr};
182 
183  int m_escapekey {0};
184 
185  QObject *m_sysEventHandler {nullptr};
186 
187  int m_drawInterval {1000 / MythMainWindow::drawRefresh};
188  MythSignalingTimer *m_drawTimer {nullptr};
189  QVector<MythScreenStack *> m_stackList;
190  MythScreenStack *m_mainStack {nullptr};
191 
193  MythPainter *m_painter {nullptr};
194  MythRender *m_render {nullptr};
195 
197 
199  QTimer *m_gestureTimer {nullptr};
200  QTimer *m_hideMouseTimer {nullptr};
201 
202  /* compatibility only, FIXME remove */
203  std::vector<QWidget *> m_widgetList;
204  QMap<QWidget *, bool> m_enabledWidgets;
205 
206  QWidget *m_paintwin {nullptr};
207 
208  QWidget *m_oldpaintwin {nullptr};
209  MythPainter *m_oldpainter {nullptr};
210  MythRender *m_oldrender {nullptr};
211 
213  uint m_drawDisabledDepth {0};
214  bool m_drawEnabled {true};
215 
216  MythThemeBase *m_themeBase {nullptr};
217  MythUDPListener *m_udpListener {nullptr};
218 
219  MythNotificationCenter *m_nc {nullptr};
220  QTimer *m_idleTimer {nullptr};
221  int m_idleTime {0};
222  bool m_standby {false};
223  bool m_enteringStandby {false};
224  bool m_disableIdle {false};
225 
226  bool m_allowInput {true};
227 
228  bool m_pendingUpdate {false};
229 
230  // window aspect
231  bool m_firstinit {true};
232  bool m_bSavedPOS {false};
233  // Support for long press
234  int m_longPressKeyCode {0};
235  ulong m_longPressTime {0};
236 };
237 
238 // Make keynum in QKeyEvent be equivalent to what's in QKeySequence
240 {
241  int keynum = e->key();
242 
243  if ((keynum != Qt::Key_Shift ) && (keynum !=Qt::Key_Control ) &&
244  (keynum != Qt::Key_Meta ) && (keynum !=Qt::Key_Alt ) &&
245  (keynum != Qt::Key_Super_L) && (keynum !=Qt::Key_Super_R ) &&
246  (keynum != Qt::Key_Hyper_L) && (keynum !=Qt::Key_Hyper_R ) &&
247  (keynum != Qt::Key_AltGr ) && (keynum !=Qt::Key_CapsLock ) &&
248  (keynum != Qt::Key_NumLock) && (keynum !=Qt::Key_ScrollLock ))
249  {
250  Qt::KeyboardModifiers modifiers;
251  // if modifiers have been pressed, rebuild keynum
252  if ((modifiers = e->modifiers()) != Qt::NoModifier)
253  {
254  int modnum = Qt::NoModifier;
255  if (((modifiers & Qt::ShiftModifier) != 0U) &&
256  (keynum > 0x7f) &&
257  (keynum != Qt::Key_Backtab))
258  modnum |= Qt::SHIFT;
259  if ((modifiers & Qt::ControlModifier) != 0U)
260  modnum |= Qt::CTRL;
261  if ((modifiers & Qt::MetaModifier) != 0U)
262  modnum |= Qt::META;
263  if ((modifiers & Qt::AltModifier) != 0U)
264  modnum |= Qt::ALT;
265  modnum &= ~Qt::UNICODE_ACCEL;
266  return (keynum |= modnum);
267  }
268  }
269 
270  return keynum;
271 }
272 
273 static MythMainWindow *mainWin = nullptr;
274 static QMutex mainLock;
275 
285 {
286  if (mainWin)
287  return mainWin;
288 
289  QMutexLocker lock(&mainLock);
290 
291  if (!mainWin)
292  {
293  mainWin = new MythMainWindow(useDB);
295  }
296 
297  return mainWin;
298 }
299 
301 {
302  if (gCoreContext)
303  gCoreContext->SetGUIObject(nullptr);
304  delete mainWin;
305  mainWin = nullptr;
306 }
307 
309 {
311 }
312 
314 {
315  return (mainWin);
316 }
317 
319 {
321 }
322 
324 {
326 }
327 
329 {
330  if (!mainWin ||
332  return nullptr;
334 }
335 
336 #ifdef USING_OPENGL
338  MythMainWindowPrivate *priv,
339  MythRenderOpenGL *rend)
340  : QWidget(win),
341  m_parent(win),
342  d(priv),
343  m_render(rend)
344 {
345  setAttribute(Qt::WA_NoSystemBackground);
346  setAttribute(Qt::WA_NativeWindow);
347  setAttribute(Qt::WA_DontCreateNativeAncestors);
348  winId();
349 #ifdef Q_OS_MACOS
350  setVisible(true); // must be visible before OpenGL initialisation on OSX
351 #endif
352  m_render->setWidget(this);
353 }
354 
355 QPaintEngine *MythPainterWindowGL::paintEngine() const
356 {
357  return testAttribute(Qt::WA_PaintOnScreen) ? nullptr : m_parent->paintEngine();
358 }
359 
361 {
362  if (m_render)
363  {
364  m_render->DecrRef();
365  m_render = nullptr;
366  }
367 }
368 
369 void MythPainterWindowGL::paintEvent(QPaintEvent *pe)
370 {
371  d->m_repaintRegion = d->m_repaintRegion.united(pe->region());
372  m_parent->drawScreen();
373 }
374 #endif
375 
376 #ifdef _WIN32
378  MythMainWindowPrivate *priv)
379  : QWidget(win),
380  m_parent(win), d(priv)
381 {
382  setAutoBufferSwap(false);
383 }
384 
386 {
387  d->m_repaintRegion = d->m_repaintRegion.united(pe->region());
388  m_parent->drawScreen();
389 }
390 #endif
391 
393  MythMainWindowPrivate *priv)
394  : QWidget(win),
395  m_parent(win), d(priv)
396 {
397  setAttribute(Qt::WA_NoSystemBackground);
398 }
399 
400 void MythPainterWindowQt::paintEvent(QPaintEvent *pe)
401 {
402  d->m_repaintRegion = d->m_repaintRegion.united(pe->region());
403  m_parent->drawScreen();
404 }
405 
407  : QWidget(nullptr)
408 {
409  d = new MythMainWindowPrivate;
410 
411  setObjectName("mainwindow");
412 
413  d->m_allowInput = false;
414 
415  // This prevents database errors from RegisterKey() when there is no DB:
416  d->m_useDB = useDB;
417  d->m_painter = nullptr;
418  d->m_paintwin = nullptr;
419  d->m_oldpainter = nullptr;
420  d->m_oldpaintwin = nullptr;
421  d->m_oldrender = nullptr;
422 
423  //Init();
424 
425  d->m_ignoreLircKeys = false;
426  d->m_ignoreJoystickKeys = false;
427  d->m_exitingtomain = false;
428  d->m_popwindows = true;
429  d->m_exitMenuCallback = nullptr;
430  d->m_exitMenuMediaDeviceCallback = nullptr;
431  d->m_mediaDeviceForCallback = nullptr;
432  d->m_escapekey = Qt::Key_Escape;
433  d->m_mainStack = nullptr;
434  d->m_sysEventHandler = nullptr;
435 
436  installEventFilter(this);
437 
438  d->m_lircThread = nullptr;
439  StartLIRC();
440 
441 #ifdef USE_JOYSTICK_MENU
442  d->m_ignoreJoystickKeys = false;
443 
444  QString joy_config_file = GetConfDir() + "/joystickmenurc";
445 
446  d->m_joystickThread = nullptr;
448  if (d->m_joystickThread->Init(joy_config_file))
450 #endif
451 
452 #ifdef USING_APPLEREMOTE
455 
459  {
460  d->m_appleRemote->start();
461  }
462  else
463  {
464  // start listening failed, no remote receiver present
465  delete d->m_appleRemote;
466  delete d->m_appleRemoteListener;
467  d->m_appleRemote = nullptr;
468  d->m_appleRemoteListener = nullptr;
469  }
470 #endif
471 
472 #ifdef USING_LIBCEC
473  d->m_cecAdapter = new CECAdapter();
474  if (!d->m_cecAdapter->IsValid())
475  {
476  delete d->m_cecAdapter;
477  d->m_cecAdapter = nullptr;
478  }
479 #endif
480 
482 
483  InitKeys();
484 
485  d->m_gestureTimer = new QTimer(this);
486  connect(d->m_gestureTimer, SIGNAL(timeout()), this, SLOT(mouseTimeout()));
487  d->m_hideMouseTimer = new QTimer(this);
488  d->m_hideMouseTimer->setSingleShot(true);
489  d->m_hideMouseTimer->setInterval(3000); // 3 seconds
490  connect(d->m_hideMouseTimer, SIGNAL(timeout()), SLOT(HideMouseTimeout()));
491 
492  d->m_drawTimer = new MythSignalingTimer(this, SLOT(animate()));
494 
495  d->m_allowInput = true;
496 
497  d->m_repaintRegion = QRegion(QRect(0,0,0,0));
498 
499  d->m_drawEnabled = true;
500 
501  connect(this, SIGNAL(signalRemoteScreenShot(QString,int,int)),
502  this, SLOT(doRemoteScreenShot(QString,int,int)),
503  Qt::BlockingQueuedConnection);
504  connect(this, SIGNAL(signalSetDrawEnabled(bool)),
505  this, SLOT(SetDrawEnabled(bool)),
506  Qt::BlockingQueuedConnection);
507 #ifdef Q_OS_ANDROID
508  connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)),
509  this, SLOT(onApplicationStateChange(Qt::ApplicationState)));
510 #endif
511 
512  // We need to listen for playback start/end events
513  gCoreContext->addListener(this);
514 
515  d->m_idleTime = gCoreContext->GetNumSetting("FrontendIdleTimeout",
517 
518  if (d->m_idleTime < 0)
519  d->m_idleTime = 0;
520 
521  d->m_idleTimer = new QTimer(this);
522  d->m_idleTimer->setSingleShot(false);
523  d->m_idleTimer->setInterval(1000 * 60 * d->m_idleTime);
524  connect(d->m_idleTimer, SIGNAL(timeout()), SLOT(IdleTimeout()));
525  if (d->m_idleTime > 0)
526  d->m_idleTimer->start();
527 }
528 
530 {
532 
533  d->m_drawTimer->stop();
534 
535  while (!d->m_stackList.isEmpty())
536  {
537  MythScreenStack *stack = d->m_stackList.back();
538  d->m_stackList.pop_back();
539 
540  if (stack == d->m_mainStack)
541  d->m_mainStack = nullptr;
542 
543  delete stack;
544  }
545 
546  delete d->m_themeBase;
547 
548  while (!d->m_keyContexts.isEmpty())
549  {
550  KeyContext *context = *d->m_keyContexts.begin();
551  d->m_keyContexts.erase(d->m_keyContexts.begin());
552  delete context;
553  }
554 
555 #ifdef USE_LIRC
556  if (d->m_lircThread)
557  {
559  d->m_lircThread = nullptr;
560  }
561 #endif
562 
563 #ifdef USE_JOYSTICK_MENU
564  if (d->m_joystickThread)
565  {
566  if (d->m_joystickThread->isRunning())
567  {
568  d->m_joystickThread->Stop();
569  d->m_joystickThread->wait();
570  }
571 
572  delete d->m_joystickThread;
573  d->m_joystickThread = nullptr;
574  }
575 #endif
576 
577 #ifdef USING_APPLEREMOTE
578  delete d->m_appleRemote;
579  delete d->m_appleRemoteListener;
580 #endif
581 
582 #ifdef USING_LIBCEC
583  delete d->m_cecAdapter;
584 #endif
585 
586  delete d->m_nc;
587 
588  delete d->m_painter;
589  // Don't delete. If the app is closing down it causes intermittent segfaults
590  if (d->m_render)
592 
594 
595  delete d;
596 }
597 
599 {
600  return d->m_painter;
601 }
602 
604 {
605  return d->m_nc;
606 }
607 
609 {
610  return d->m_paintwin;
611 }
612 
614 {
615  if (d->m_paintwin)
616  {
617  d->m_paintwin->show();
618  d->m_paintwin->raise();
619  }
620 }
621 
623 {
624  if (d->m_paintwin)
625  {
626  d->m_paintwin->clearMask();
627  if (!(d->m_render && d->m_render->IsShared()))
628  d->m_paintwin->hide();
629  }
630 }
631 
633 {
634  return d->m_render;
635 }
636 
638 {
639  d->m_stackList.push_back(stack);
640  if (main)
641  d->m_mainStack = stack;
642 }
643 
645 {
646  MythScreenStack *stack = d->m_stackList.back();
647  d->m_stackList.pop_back();
648  if (stack == d->m_mainStack)
649  d->m_mainStack = nullptr;
650  delete stack;
651 }
652 
654 {
655  return d->m_stackList.size();
656 }
657 
659 {
660  return d->m_mainStack;
661 }
662 
663 MythScreenStack *MythMainWindow::GetStack(const QString &stackname)
664 {
665  for (auto it = d->m_stackList.begin(); it != d->m_stackList.end(); ++it)
666  {
667  if ((*it)->objectName() == stackname)
668  return *it;
669  }
670  return nullptr;
671 }
672 
674 {
675  if (pos >= 0 && pos < d->m_stackList.size())
676  return d->m_stackList.at(pos);
677 
678  return nullptr;
679 }
680 
682 {
683  if (!d->m_drawEnabled || !d->m_paintwin)
684  return;
685 
686  d->m_drawTimer->blockSignals(true);
687 
688  bool redraw = false;
689 
690  if (!d->m_repaintRegion.isEmpty())
691  redraw = true;
692 
693  for (auto it = d->m_stackList.begin(); it != d->m_stackList.end(); ++it)
694  {
695  QVector<MythScreenType *> drawList;
696  (*it)->GetDrawOrder(drawList);
697 
698  for (auto screenit = drawList.begin(); screenit != drawList.end();
699  ++screenit)
700  {
701  (*screenit)->Pulse();
702 
703  if ((*screenit)->NeedsRedraw())
704  {
705  QRegion topDirty = (*screenit)->GetDirtyArea();
706  (*screenit)->ResetNeedsRedraw();
707  d->m_repaintRegion = d->m_repaintRegion.united(topDirty);
708  redraw = true;
709  }
710  }
711  }
712 
713  if (redraw && !(d->m_render && d->m_render->IsShared()))
714  d->m_paintwin->update(d->m_repaintRegion);
715 
716  for (auto it = d->m_stackList.begin(); it != d->m_stackList.end(); ++it)
717  (*it)->ScheduleInitIfNeeded();
718 
719  d->m_drawTimer->blockSignals(false);
720 }
721 
723 {
724  if (!d->m_drawEnabled)
725  return;
726 
727  if (!d->m_painter->SupportsClipping())
729  else
730  {
731  // Ensure that the region is not larger than the screen which
732  // can happen with bad themes
734 
735  // Check for any widgets that have been updated since we built
736  // the dirty region list in ::animate()
737  for (auto it = d->m_stackList.begin(); it != d->m_stackList.end(); ++it)
738  {
739  QVector<MythScreenType *> redrawList;
740  (*it)->GetDrawOrder(redrawList);
741 
742  for (auto screenit = redrawList.begin(); screenit != redrawList.end();
743  ++screenit)
744  {
745  if ((*screenit)->NeedsRedraw())
746  {
747 #if QT_VERSION < QT_VERSION_CHECK(5, 8, 0)
748  QRegion topDirty = (*screenit)->GetDirtyArea();
749  QVector<QRect> wrects = topDirty.rects();
750  for (int i = 0; i < wrects.size(); i++)
751  {
752  bool foundThisRect = false;
753  QVector<QRect> drects = d->m_repaintRegion.rects();
754  for (int j = 0; j < drects.size(); j++)
755  {
756  // Can't use QRegion::contains because it only
757  // checks for overlap. QRect::contains checks
758  // if fully contained.
759  if (drects[j].contains(wrects[i]))
760  {
761  foundThisRect = true;
762  break;
763  }
764  }
765 
766  if (!foundThisRect)
767  return;
768  }
769 #else
770  for (const QRect& wrect: (*screenit)->GetDirtyArea())
771  {
772  bool foundThisRect = false;
773  for (const QRect& drect: d->m_repaintRegion)
774  {
775  // Can't use QRegion::contains because it only
776  // checks for overlap. QRect::contains checks
777  // if fully contained.
778  if (drect.contains(wrect))
779  {
780  foundThisRect = true;
781  break;
782  }
783  }
784 
785  if (!foundThisRect)
786  return;
787  }
788 #endif
789  }
790  }
791  }
792  }
793 
794  if (!(d->m_render && d->m_render->IsShared()))
795  draw();
796 
797  d->m_repaintRegion = QRegion(QRect(0, 0, 0, 0));
798 }
799 
800 void MythMainWindow::draw(MythPainter *painter /* = 0 */)
801 {
802  if (!painter)
803  painter = d->m_painter;
804 
805  if (!painter)
806  return;
807 
808  painter->Begin(d->m_paintwin);
809 
810  if (!painter->SupportsClipping())
811  d->m_repaintRegion = QRegion(d->m_uiScreenRect);
812 
813 #if QT_VERSION < QT_VERSION_CHECK(5, 8, 0)
814  QVector<QRect> rects = d->m_repaintRegion.rects();
815  for (int i = 0; i < rects.size(); i++)
816  {
817  const QRect& r = rects[i];
818 #else
819  for (const QRect& r : d->m_repaintRegion)
820  {
821 #endif
822  if (r.width() == 0 || r.height() == 0)
823  continue;
824 
825  if (r != d->m_uiScreenRect)
826  painter->SetClipRect(r);
827 
828  for (auto it = d->m_stackList.begin(); it != d->m_stackList.end(); ++it)
829  {
830  QVector<MythScreenType *> redrawList;
831  (*it)->GetDrawOrder(redrawList);
832 
833  for (auto screenit = redrawList.begin(); screenit != redrawList.end();
834  ++screenit)
835  {
836  (*screenit)->Draw(painter, 0, 0, 255, r);
837  }
838  }
839  }
840 
841  painter->End();
842  d->m_repaintRegion = QRegion();
843 }
844 
845 // virtual
846 QPaintEngine *MythMainWindow::paintEngine() const
847 {
848  return testAttribute(Qt::WA_PaintOnScreen) ? nullptr : QWidget::paintEngine();
849 }
850 
851 void MythMainWindow::closeEvent(QCloseEvent *e)
852 {
853  if (e->spontaneous())
854  {
855  auto *key = new QKeyEvent(QEvent::KeyPress, d->m_escapekey,
856  Qt::NoModifier);
857  QCoreApplication::postEvent(this, key);
858  e->ignore();
859  }
860  else
861  QWidget::closeEvent(e);
862 }
863 
864 void MythMainWindow::GrabWindow(QImage &image)
865 {
866  WId winid = 0;
867  QWidget *active = QApplication::activeWindow();
868  if (active)
869  winid = active->winId();
870  else
871  // According to the following we page, you "just pass 0 as the
872  // window id, indicating that we want to grab the entire screen".
873  //
874  // https://doc.qt.io/qt-5/qtwidgets-desktop-screenshot-example.html#screenshot-class-implementation
875  winid = 0;
876 
877  QScreen *screen = MythDisplay::AcquireRelease()->GetCurrentScreen();
879  if (screen)
880  {
881  QPixmap p = screen->grabWindow(winid);
882  image = p.toImage();
883  }
884 }
885 
886 /* This is required to allow a screenshot to be requested by another thread
887  * other than the UI thread, and to wait for the screenshot before returning.
888  * It is used by mythweb for the remote access screenshots
889  */
890 void MythMainWindow::doRemoteScreenShot(const QString& filename, int x, int y)
891 {
892  // This will be running in the UI thread, as is required by QPixmap
893  QStringList args;
894  args << QString::number(x);
895  args << QString::number(y);
896  args << filename;
897 
899  qApp->sendEvent(this, &me);
900 }
901 
902 void MythMainWindow::RemoteScreenShot(QString filename, int x, int y)
903 {
904  // This will be running in a non-UI thread and is used to trigger a
905  // function in the UI thread, and waits for completion of that handler
906  emit signalRemoteScreenShot(std::move(filename), x, y);
907 }
908 
909 bool MythMainWindow::SaveScreenShot(const QImage &image, QString filename)
910 {
911  if (filename.isEmpty())
912  {
913  QString fpath = GetMythDB()->GetSetting("ScreenShotPath", "/tmp");
914  filename = QString("%1/myth-screenshot-%2.png").arg(fpath)
915  .arg(MythDate::toString(
917  }
918 
919  QString extension = filename.section('.', -1, -1);
920  if (extension == "jpg")
921  extension = "JPEG";
922  else
923  extension = "PNG";
924 
925  LOG(VB_GENERAL, LOG_INFO, QString("Saving screenshot to %1 (%2x%3)")
926  .arg(filename).arg(image.width()).arg(image.height()));
927 
928  if (image.save(filename, extension.toLatin1(), 100))
929  {
930  LOG(VB_GENERAL, LOG_INFO, "MythMainWindow::screenShot succeeded");
931  return true;
932  }
933 
934  LOG(VB_GENERAL, LOG_INFO, "MythMainWindow::screenShot Failed!");
935  return false;
936 }
937 
938 bool MythMainWindow::ScreenShot(int w, int h, QString filename)
939 {
940  QImage img;
941  GrabWindow(img);
942  if (w <= 0)
943  w = img.width();
944  if (h <= 0)
945  h = img.height();
946 
947  img = img.scaled(w, h, Qt::KeepAspectRatio, Qt::SmoothTransformation);
948  return SaveScreenShot(img, std::move(filename));
949 }
950 
951 bool MythMainWindow::event(QEvent *e)
952 {
953  if (!updatesEnabled() && (e->type() == QEvent::UpdateRequest))
954  d->m_pendingUpdate = true;
955 
956  if (e->type() == QEvent::Show && !e->spontaneous())
957  {
958  QCoreApplication::postEvent(
959  this, new QEvent(MythEvent::kMythPostShowEventType));
960  }
961 
962  if (e->type() == MythEvent::kMythPostShowEventType)
963  {
964  raise();
965  activateWindow();
966  return true;
967  }
968 
969 #ifdef USING_APPLEREMOTE
970  if (d->m_appleRemote)
971  {
972  if (e->type() == QEvent::WindowActivate)
974 
975  if (e->type() == QEvent::WindowDeactivate)
977  }
978 #endif
979 
980  return QWidget::event(e);
981 }
982 
983 void MythMainWindow::Init(const QString& forcedpainter, bool mayReInit)
984 {
985  d->m_useDB = ! gCoreContext->GetDB()->SuppressDBMessages();
986 
987  if ( !(mayReInit || d->m_firstinit) )
988  return;
989 
992 
994  (GetMythDB()->GetNumSetting("GuiOffsetX") == 0 &&
995  GetMythDB()->GetNumSetting("GuiWidth") == 0 &&
996  GetMythDB()->GetNumSetting("GuiOffsetY") == 0 &&
997  GetMythDB()->GetNumSetting("GuiHeight") == 0);
998 
999  // Set window border based on fullscreen attribute
1000  Qt::WindowFlags flags = Qt::Window;
1001 
1002  bool inwindow = GetMythDB()->GetBoolSetting("RunFrontendInWindow", false);
1003  bool fullscreen = d->m_doesFillScreen && !MythUIHelper::IsGeometryOverridden();
1004 
1005  // On Compiz/Unit, when the window is fullscreen and frameless changing
1006  // screen position ends up stuck. Adding a border temporarily prevents this
1007  setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint);
1008 
1009  if (!inwindow)
1010  {
1011  LOG(VB_GENERAL, LOG_INFO, "Using Frameless Window");
1012  flags |= Qt::FramelessWindowHint;
1013  }
1014 
1015  // Workaround Qt/Windows playback bug?
1016 #ifdef _WIN32
1017  flags |= Qt::MSWindowsOwnDC;
1018 #endif
1019 
1020  // NOTE if running fullscreen AND windowed (i.e. borders etc) then we do not
1021  // have any idea at this time of the size of the borders/decorations.
1022  // Typically, on linux, this means we create the UI slightly larger than
1023  // required - as X adds the decorations at a later point.
1024 
1025  if (fullscreen && !inwindow)
1026  {
1027  LOG(VB_GENERAL, LOG_INFO, "Using Full Screen Window");
1028  if (d->m_firstinit)
1029  {
1030  // During initialization, we force being fullscreen using setWindowState
1031  // otherwise, in ubuntu's unity, the side bar often stays visible
1032  setWindowState(Qt::WindowFullScreen);
1033  }
1034  }
1035  else
1036  {
1037  // reset type
1038  setWindowState(Qt::WindowNoState);
1039  }
1040 
1041  if (gCoreContext->GetBoolSetting("AlwaysOnTop", false))
1042  {
1043  flags |= Qt::WindowStaysOnTopHint;
1044  }
1045 
1046  setWindowFlags(flags);
1047 
1048  // NB there may be a chicken and egg problem here with screen dimensions
1049  // MythUIHelper will be initialised to the current/default screen but we
1050  // may move to a new screen here - which may have different dimensions
1051  d->m_display->SetWidget(this);
1052 
1053  QTimer::singleShot(1000, this, SLOT(DelayedAction()));
1054 
1056  d->m_uiScreenRect = QRect(0, 0, d->m_screenwidth, d->m_screenheight);
1057 
1058  LOG(VB_GENERAL, LOG_INFO, QString("UI Screen Resolution: %1 x %2")
1059  .arg(d->m_screenwidth).arg(d->m_screenheight));
1060 
1062  Show();
1063 
1064  if (!GetMythDB()->GetBoolSetting("HideMouseCursor", false))
1065  setMouseTracking(true); // Required for mouse cursor auto-hide
1066  // Set cursor call must come after Show() to work on some systems.
1067  ShowMouseCursor(false);
1068 
1069  move(d->m_xbase, d->m_ybase);
1070 
1071  if (d->m_paintwin)
1072  {
1074  d->m_paintwin = nullptr;
1075  d->m_drawTimer->stop();
1076  }
1077 
1078  if (d->m_painter)
1079  {
1080  d->m_oldpainter = d->m_painter;
1081  d->m_painter = nullptr;
1082  }
1083 
1084  if (d->m_render)
1085  {
1086  d->m_oldrender = d->m_render;
1087  d->m_render = nullptr;
1088  }
1089 
1090  QString painter = forcedpainter.isEmpty() ?
1091  GetMythDB()->GetSetting("ThemePainter", AUTO_PAINTER) : forcedpainter;
1092 #ifdef _WIN32
1093  if (painter == AUTO_PAINTER || painter == D3D9_PAINTER)
1094  {
1095  LOG(VB_GENERAL, LOG_INFO, QString("Using the %1 painter").arg(D3D9_PAINTER));
1096  d->painter = new MythD3D9Painter();
1097  d->m_paintwin = new MythPainterWindowD3D9(this, d);
1098  }
1099 #endif
1100 #ifdef USING_OPENGL
1101  if ((!d->m_painter && !d->m_paintwin) &&
1102  (painter == AUTO_PAINTER || painter.contains(OPENGL_PAINTER)))
1103  {
1105  d->m_render = gl;
1106  if (!gl)
1107  {
1108  LOG(VB_GENERAL, LOG_ERR, "Failed to create OpenGL render.");
1109  }
1110  else
1111  {
1112  d->m_painter = new MythOpenGLPainter(gl);
1113  // NB MythPainterWindowGL takes ownership of gl
1114  d->m_paintwin = new MythPainterWindowGL(this, d, gl);
1115 
1116  // we need to initialise MythRenderOpenGL before checking
1117  // IsRecommendedRenderer
1118  if (!gl->Init() || (painter == AUTO_PAINTER && !gl->IsRecommendedRenderer()))
1119  {
1120  LOG(VB_GENERAL, LOG_WARNING,
1121  "OpenGL painter not recommended with this system's "
1122  "hardware/drivers. Falling back to Qt painter.");
1123  d->m_render->DecrRef();
1124  d->m_render = nullptr;
1125  delete d->m_painter;
1126  delete d->m_paintwin;
1127  d->m_painter = nullptr;
1128  d->m_paintwin = nullptr;
1129  }
1130  }
1131  }
1132 #endif
1133 
1134  // NOLINTNEXTLINE(readability-misleading-indentation)
1135  if (!d->m_painter && !d->m_paintwin)
1136  {
1137  LOG(VB_GENERAL, LOG_INFO, "Using the Qt painter");
1138  d->m_painter = new MythQtPainter();
1139  d->m_paintwin = new MythPainterWindowQt(this, d);
1140  }
1141 
1142  if (!d->m_paintwin)
1143  {
1144  LOG(VB_GENERAL, LOG_ERR, "MythMainWindow failed to create a "
1145  "painter window.");
1146  return;
1147  }
1148 
1149  if (d->m_painter->GetName() != "Qt")
1150  {
1151  setAttribute(Qt::WA_NoSystemBackground);
1152  setAutoFillBackground(false);
1153  }
1154 
1157 
1158  // Redraw the window now to avoid race conditions in EGLFS (Qt5.4) if a
1159  // 2nd window (e.g. TVPlayback) is created before this is redrawn.
1160 #ifdef ANDROID
1161  LOG(VB_GENERAL, LOG_INFO, QString("Platform name is %1").arg(qApp->platformName()));
1162 # define EARLY_SHOW_PLATFORM_NAME_CHECK "android"
1163 #else
1164 # define EARLY_SHOW_PLATFORM_NAME_CHECK "egl"
1165 #endif
1166  if (qApp->platformName().contains(EARLY_SHOW_PLATFORM_NAME_CHECK))
1167  qApp->processEvents();
1168 
1169  if (!GetMythDB()->GetBoolSetting("HideMouseCursor", false))
1170  d->m_paintwin->setMouseTracking(true); // Required for mouse cursor auto-hide
1171 
1173  if (d->m_themeBase)
1174  d->m_themeBase->Reload();
1175  else
1176  d->m_themeBase = new MythThemeBase();
1177 
1178  if (!d->m_nc)
1179  {
1180  d->m_nc = new MythNotificationCenter();
1181  }
1182 }
1183 
1185 {
1187  Show();
1188 
1189 #ifdef Q_OS_ANDROID
1190  QtAndroid::hideSplashScreen();
1191 #endif
1192 }
1193 
1195 {
1196  RegisterKey("Global", ACTION_UP, QT_TRANSLATE_NOOP("MythControls",
1197  "Up Arrow"), "Up");
1198  RegisterKey("Global", ACTION_DOWN, QT_TRANSLATE_NOOP("MythControls",
1199  "Down Arrow"), "Down");
1200  RegisterKey("Global", ACTION_LEFT, QT_TRANSLATE_NOOP("MythControls",
1201  "Left Arrow"), "Left");
1202  RegisterKey("Global", ACTION_RIGHT, QT_TRANSLATE_NOOP("MythControls",
1203  "Right Arrow"), "Right");
1204  RegisterKey("Global", "NEXT", QT_TRANSLATE_NOOP("MythControls",
1205  "Move to next widget"), "Tab");
1206  RegisterKey("Global", "PREVIOUS", QT_TRANSLATE_NOOP("MythControls",
1207  "Move to preview widget"), "Backtab");
1208  RegisterKey("Global", ACTION_SELECT, QT_TRANSLATE_NOOP("MythControls",
1209  "Select"), "Return,Enter,Space");
1210  RegisterKey("Global", "BACKSPACE", QT_TRANSLATE_NOOP("MythControls",
1211  "Backspace"), "Backspace");
1212  RegisterKey("Global", "ESCAPE", QT_TRANSLATE_NOOP("MythControls",
1213  "Escape"), "Esc");
1214  RegisterKey("Global", "MENU", QT_TRANSLATE_NOOP("MythControls",
1215  "Pop-up menu"), "M,Meta+Enter");
1216  RegisterKey("Global", "INFO", QT_TRANSLATE_NOOP("MythControls",
1217  "More information"), "I");
1218  RegisterKey("Global", "DELETE", QT_TRANSLATE_NOOP("MythControls",
1219  "Delete"), "D");
1220  RegisterKey("Global", "EDIT", QT_TRANSLATE_NOOP("MythControls",
1221  "Edit"), "E");
1222  RegisterKey("Global", ACTION_SCREENSHOT, QT_TRANSLATE_NOOP("MythControls",
1223  "Save screenshot"), "");
1224  RegisterKey("Global", ACTION_HANDLEMEDIA, QT_TRANSLATE_NOOP("MythControls",
1225  "Play a media resource"), "");
1226 
1227  RegisterKey("Global", "PAGEUP", QT_TRANSLATE_NOOP("MythControls",
1228  "Page Up"), "PgUp");
1229  RegisterKey("Global", "PAGEDOWN", QT_TRANSLATE_NOOP("MythControls",
1230  "Page Down"), "PgDown");
1231  RegisterKey("Global", "PAGETOP", QT_TRANSLATE_NOOP("MythControls",
1232  "Page to top of list"), "");
1233  RegisterKey("Global", "PAGEMIDDLE", QT_TRANSLATE_NOOP("MythControls",
1234  "Page to middle of list"), "");
1235  RegisterKey("Global", "PAGEBOTTOM", QT_TRANSLATE_NOOP("MythControls",
1236  "Page to bottom of list"), "");
1237 
1238  RegisterKey("Global", "PREVVIEW", QT_TRANSLATE_NOOP("MythControls",
1239  "Previous View"), "Home");
1240  RegisterKey("Global", "NEXTVIEW", QT_TRANSLATE_NOOP("MythControls",
1241  "Next View"), "End");
1242 
1243  RegisterKey("Global", "HELP", QT_TRANSLATE_NOOP("MythControls",
1244  "Help"), "F1");
1245  RegisterKey("Global", "EJECT", QT_TRANSLATE_NOOP("MythControls"
1246  ,"Eject Removable Media"), "");
1247 
1248  RegisterKey("Global", "CUT", QT_TRANSLATE_NOOP("MythControls",
1249  "Cut text from textedit"), "Ctrl+X");
1250  RegisterKey("Global", "COPY", QT_TRANSLATE_NOOP("MythControls"
1251  ,"Copy text from textedit"), "Ctrl+C");
1252  RegisterKey("Global", "PASTE", QT_TRANSLATE_NOOP("MythControls",
1253  "Paste text into textedit"), "Ctrl+V");
1254  RegisterKey("Global", "NEWLINE", QT_TRANSLATE_NOOP("MythControls",
1255  "Insert newline into textedit"), "Ctrl+Return");
1256  RegisterKey("Global", "UNDO", QT_TRANSLATE_NOOP("MythControls",
1257  "Undo"), "Ctrl+Z");
1258  RegisterKey("Global", "REDO", QT_TRANSLATE_NOOP("MythControls",
1259  "Redo"), "Ctrl+Y");
1260  RegisterKey("Global", "SEARCH", QT_TRANSLATE_NOOP("MythControls",
1261  "Show incremental search dialog"), "Ctrl+S");
1262 
1263  RegisterKey("Global", ACTION_0, QT_TRANSLATE_NOOP("MythControls","0"), "0");
1264  RegisterKey("Global", ACTION_1, QT_TRANSLATE_NOOP("MythControls","1"), "1");
1265  RegisterKey("Global", ACTION_2, QT_TRANSLATE_NOOP("MythControls","2"), "2");
1266  RegisterKey("Global", ACTION_3, QT_TRANSLATE_NOOP("MythControls","3"), "3");
1267  RegisterKey("Global", ACTION_4, QT_TRANSLATE_NOOP("MythControls","4"), "4");
1268  RegisterKey("Global", ACTION_5, QT_TRANSLATE_NOOP("MythControls","5"), "5");
1269  RegisterKey("Global", ACTION_6, QT_TRANSLATE_NOOP("MythControls","6"), "6");
1270  RegisterKey("Global", ACTION_7, QT_TRANSLATE_NOOP("MythControls","7"), "7");
1271  RegisterKey("Global", ACTION_8, QT_TRANSLATE_NOOP("MythControls","8"), "8");
1272  RegisterKey("Global", ACTION_9, QT_TRANSLATE_NOOP("MythControls","9"), "9");
1273 
1274  RegisterKey("Global", ACTION_TVPOWERON, QT_TRANSLATE_NOOP("MythControls",
1275  "Turn the display on"), "");
1276  RegisterKey("Global", ACTION_TVPOWEROFF, QT_TRANSLATE_NOOP("MythControls",
1277  "Turn the display off"), "");
1278 
1279  RegisterKey("Global", "SYSEVENT01", QT_TRANSLATE_NOOP("MythControls",
1280  "Trigger System Key Event #1"), "");
1281  RegisterKey("Global", "SYSEVENT02", QT_TRANSLATE_NOOP("MythControls",
1282  "Trigger System Key Event #2"), "");
1283  RegisterKey("Global", "SYSEVENT03", QT_TRANSLATE_NOOP("MythControls",
1284  "Trigger System Key Event #3"), "");
1285  RegisterKey("Global", "SYSEVENT04", QT_TRANSLATE_NOOP("MythControls",
1286  "Trigger System Key Event #4"), "");
1287  RegisterKey("Global", "SYSEVENT05", QT_TRANSLATE_NOOP("MythControls",
1288  "Trigger System Key Event #5"), "");
1289  RegisterKey("Global", "SYSEVENT06", QT_TRANSLATE_NOOP("MythControls",
1290  "Trigger System Key Event #6"), "");
1291  RegisterKey("Global", "SYSEVENT07", QT_TRANSLATE_NOOP("MythControls",
1292  "Trigger System Key Event #7"), "");
1293  RegisterKey("Global", "SYSEVENT08", QT_TRANSLATE_NOOP("MythControls",
1294  "Trigger System Key Event #8"), "");
1295  RegisterKey("Global", "SYSEVENT09", QT_TRANSLATE_NOOP("MythControls",
1296  "Trigger System Key Event #9"), "");
1297  RegisterKey("Global", "SYSEVENT10", QT_TRANSLATE_NOOP("MythControls",
1298  "Trigger System Key Event #10"), "");
1299 
1300  // these are for the html viewer widget (MythUIWebBrowser)
1301  RegisterKey("Browser", "ZOOMIN", QT_TRANSLATE_NOOP("MythControls",
1302  "Zoom in on browser window"), ".,>");
1303  RegisterKey("Browser", "ZOOMOUT", QT_TRANSLATE_NOOP("MythControls",
1304  "Zoom out on browser window"), ",,<");
1305  RegisterKey("Browser", "TOGGLEINPUT", QT_TRANSLATE_NOOP("MythControls",
1306  "Toggle where keyboard input goes to"), "F1");
1307 
1308  RegisterKey("Browser", "MOUSEUP", QT_TRANSLATE_NOOP("MythControls",
1309  "Move mouse pointer up"), "2");
1310  RegisterKey("Browser", "MOUSEDOWN", QT_TRANSLATE_NOOP("MythControls",
1311  "Move mouse pointer down"), "8");
1312  RegisterKey("Browser", "MOUSELEFT", QT_TRANSLATE_NOOP("MythControls",
1313  "Move mouse pointer left"), "4");
1314  RegisterKey("Browser", "MOUSERIGHT", QT_TRANSLATE_NOOP("MythControls",
1315  "Move mouse pointer right"), "6");
1316  RegisterKey("Browser", "MOUSELEFTBUTTON", QT_TRANSLATE_NOOP("MythControls",
1317  "Mouse Left button click"), "5");
1318 
1319  RegisterKey("Browser", "PAGEDOWN", QT_TRANSLATE_NOOP("MythControls",
1320  "Scroll down half a page"), "9");
1321  RegisterKey("Browser", "PAGEUP", QT_TRANSLATE_NOOP("MythControls",
1322  "Scroll up half a page"), "3");
1323  RegisterKey("Browser", "PAGELEFT", QT_TRANSLATE_NOOP("MythControls",
1324  "Scroll left half a page"), "7");
1325  RegisterKey("Browser", "PAGERIGHT", QT_TRANSLATE_NOOP("MythControls",
1326  "Scroll right half a page"), "1");
1327 
1328  RegisterKey("Browser", "NEXTLINK", QT_TRANSLATE_NOOP("MythControls",
1329  "Move selection to next link"), "Z");
1330  RegisterKey("Browser", "PREVIOUSLINK", QT_TRANSLATE_NOOP("MythControls",
1331  "Move selection to previous link"), "Q");
1332  RegisterKey("Browser", "FOLLOWLINK", QT_TRANSLATE_NOOP("MythControls",
1333  "Follow selected link"), "Return,Space,Enter");
1334  RegisterKey("Browser", "HISTORYBACK", QT_TRANSLATE_NOOP("MythControls",
1335  "Go back to previous page"), "R,Backspace");
1336  RegisterKey("Browser", "HISTORYFORWARD", QT_TRANSLATE_NOOP("MythControls",
1337  "Go forward to previous page"), "F");
1338 
1339  RegisterKey("Main Menu", "EXITPROMPT", QT_TRANSLATE_NOOP("MythControls",
1340  "Display System Exit Prompt"), "Esc");
1341  RegisterKey("Main Menu", "EXIT", QT_TRANSLATE_NOOP("MythControls",
1342  "System Exit"), "");
1343  RegisterKey("Main Menu", "STANDBYMODE",QT_TRANSLATE_NOOP("MythControls",
1344  "Enter Standby Mode"), "");
1345  RegisterKey("Long Press", "LONGPRESS1",QT_TRANSLATE_NOOP("MythControls",
1346  "Up to 16 Keys that allow Long Press"), "");
1347  RegisterKey("Long Press", "LONGPRESS2",QT_TRANSLATE_NOOP("MythControls",
1348  "Up to 16 Keys that allow Long Press"), "");
1349  RegisterKey("Long Press", "LONGPRESS3",QT_TRANSLATE_NOOP("MythControls",
1350  "Up to 16 Keys that allow Long Press"), "");
1351  RegisterKey("Long Press", "LONGPRESS4",QT_TRANSLATE_NOOP("MythControls",
1352  "Up to 16 Keys that allow Long Press"), "");
1353 }
1354 
1356 {
1357  ClearKeyContext("Global");
1358  ClearKeyContext("Browser");
1359  ClearKeyContext("Main Menu");
1360  InitKeys();
1361 }
1362 
1364 {
1365  delete d->m_oldpainter;
1366  d->m_oldpainter = nullptr;
1367 
1368  delete d->m_oldpaintwin;
1369  d->m_oldpaintwin = nullptr;
1370 
1371  // For OpenGL contexts (at least), deleting the painter window also
1372  // deletes the render context
1373  d->m_oldrender = nullptr;
1374 
1377 
1378  d->m_drawTimer->start(1000 / drawRefresh);
1379 }
1380 
1382 {
1383  bool inwindow = GetMythDB()->GetBoolSetting("RunFrontendInWindow", false);
1384  bool fullscreen = d->m_doesFillScreen && !MythUIHelper::IsGeometryOverridden();
1385  if (fullscreen && !inwindow && !d->m_firstinit)
1386  showFullScreen();
1387  else
1388  show();
1389  d->m_firstinit = false;
1390 }
1391 
1392 void MythMainWindow::MoveResize(QRect &Geometry)
1393 {
1394  setFixedSize(Geometry.size());
1395  setGeometry(Geometry);
1396 
1397  if (d->m_paintwin)
1398  {
1399  d->m_paintwin->setFixedSize(Geometry.size());
1400  d->m_paintwin->setGeometry(0, 0, Geometry.width(), Geometry.height());
1401  }
1402 }
1403 
1405 {
1406  QMutexLocker locker(&d->m_drawDisableLock);
1409  SetDrawEnabled(false);
1410  return d->m_drawDisabledDepth;
1411 }
1412 
1414 {
1415  QMutexLocker locker(&d->m_drawDisableLock);
1416  if (d->m_drawDisabledDepth)
1417  {
1419  if (!d->m_drawDisabledDepth && !d->m_drawEnabled)
1420  SetDrawEnabled(true);
1421  }
1422  return d->m_drawDisabledDepth;
1423 }
1424 
1426 {
1427  if (!gCoreContext->IsUIThread())
1428  {
1429  emit signalSetDrawEnabled(enable);
1430  return;
1431  }
1432 
1433  setUpdatesEnabled(enable);
1434  d->m_drawEnabled = enable;
1435 
1436  if (enable)
1437  {
1438  if (d->m_pendingUpdate)
1439  {
1440  QApplication::postEvent(this, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
1441  d->m_pendingUpdate = false;
1442  }
1443  d->m_drawTimer->start(1000 / drawRefresh);
1445  }
1446  else
1447  {
1449  d->m_drawTimer->stop();
1450  }
1451 }
1452 
1454 {
1455  for (auto it = d->m_stackList.begin(); it != d->m_stackList.end(); ++it)
1456  {
1457  if (enable)
1458  (*it)->EnableEffects();
1459  else
1460  (*it)->DisableEffects();
1461  }
1462 }
1463 
1465 {
1466  return d->m_exitingtomain;
1467 }
1468 
1470 {
1471  bool jumpdone = !(d->m_popwindows);
1472 
1473  d->m_exitingtomain = true;
1474 
1475  MythScreenStack *toplevel = GetMainStack();
1476  if (toplevel && d->m_popwindows)
1477  {
1478  MythScreenType *screen = toplevel->GetTopScreen();
1479  if (screen && screen->objectName() != QString("mainmenu"))
1480  {
1481  MythEvent xe("EXIT_TO_MENU");
1482  gCoreContext->dispatch(xe);
1483  if (screen->objectName() == QString("video playback window"))
1484  {
1485  auto *me = new MythEvent("EXIT_TO_MENU");
1486  QCoreApplication::postEvent(screen, me);
1487  }
1488  else
1489  {
1490  auto *key = new QKeyEvent(QEvent::KeyPress, d->m_escapekey,
1491  Qt::NoModifier);
1492  QCoreApplication::postEvent(this, key);
1494  // Notifications have their own stack. We need to continue
1495  // the propagation of the escape here if there are notifications.
1496  int num = nc->DisplayedNotifications();
1497  if (num > 0)
1498  QCoreApplication::postEvent(
1499  this, new QEvent(MythEvent::kExitToMainMenuEventType));
1500  }
1501  return;
1502  }
1503  jumpdone = true;
1504  }
1505 
1506  if (jumpdone)
1507  {
1508  d->m_exitingtomain = false;
1509  d->m_popwindows = true;
1510  if (d->m_exitMenuCallback)
1511  {
1512  void (*callback)(void) = d->m_exitMenuCallback;
1513  d->m_exitMenuCallback = nullptr;
1514  callback();
1515  }
1516  else if (d->m_exitMenuMediaDeviceCallback)
1517  {
1518  void (*callback)(MythMediaDevice*) = d->m_exitMenuMediaDeviceCallback;
1519  MythMediaDevice * mediadevice = d->m_mediaDeviceForCallback;
1520  d->m_mediaDeviceForCallback = nullptr;
1521  callback(mediadevice);
1522  }
1523  }
1524 }
1525 
1536 bool MythMainWindow::TranslateKeyPress(const QString &context,
1537  QKeyEvent *e, QStringList &actions,
1538  bool allowJumps)
1539 {
1540  actions.clear();
1541 
1542  // Special case for custom QKeyEvent where the action is embedded directly
1543  // in the QKeyEvent text property. Used by MythFEXML http extension
1544  if (e->key() == 0 && !e->text().isEmpty() &&
1545  e->modifiers() == Qt::NoModifier)
1546  {
1547  QString action = e->text();
1548  // check if it is a jumppoint
1549  if (!d->m_destinationMap.contains(action))
1550  {
1551  actions.append(action);
1552  return false;
1553  }
1554 
1555  if (allowJumps)
1556  {
1557  // This does not filter the jump based on the current location but
1558  // is consistent with handling of other actions that do not need
1559  // a keybinding. The network control code tries to match
1560  // GetCurrentLocation with the jumppoint but matching is utterly
1561  // inconsistent e.g. mainmenu<->Main Menu, Playback<->Live TV
1562  JumpTo(action);
1563  return true;
1564  }
1565 
1566  return false;
1567  }
1568 
1569  int keynum = d->TranslateKeyNum(e);
1570 
1571  QStringList localActions;
1572  if (allowJumps && (d->m_jumpMap.count(keynum) > 0) &&
1573  (!d->m_jumpMap[keynum]->m_localAction.isEmpty()) &&
1574  (d->m_keyContexts.value(context)) &&
1575  (d->m_keyContexts.value(context)->GetMapping(keynum, localActions)))
1576  {
1577  if (localActions.contains(d->m_jumpMap[keynum]->m_localAction))
1578  allowJumps = false;
1579  }
1580 
1581  if (allowJumps && d->m_jumpMap.count(keynum) > 0 &&
1582  !d->m_jumpMap[keynum]->m_exittomain && d->m_exitMenuCallback == nullptr)
1583  {
1584  void (*callback)(void) = d->m_jumpMap[keynum]->m_callback;
1585  callback();
1586  return true;
1587  }
1588 
1589  if (allowJumps &&
1590  d->m_jumpMap.count(keynum) > 0 && d->m_exitMenuCallback == nullptr)
1591  {
1592  d->m_exitingtomain = true;
1593  d->m_exitMenuCallback = d->m_jumpMap[keynum]->m_callback;
1594  QCoreApplication::postEvent(
1595  this, new QEvent(MythEvent::kExitToMainMenuEventType));
1596  return true;
1597  }
1598 
1599  if (d->m_keyContexts.value(context))
1600  d->m_keyContexts.value(context)->GetMapping(keynum, actions);
1601 
1602  if (context != "Global")
1603  d->m_keyContexts.value("Global")->GetMapping(keynum, actions);
1604 
1605  return false;
1606 }
1607 
1608 void MythMainWindow::ClearKey(const QString &context, const QString &action)
1609 {
1610  KeyContext * keycontext = d->m_keyContexts.value(context);
1611  if (keycontext == nullptr) return;
1612 
1613  QMutableMapIterator<int, QStringList> it(keycontext->m_actionMap);
1614  while (it.hasNext())
1615  {
1616  it.next();
1617  QStringList list = it.value();
1618 
1619  list.removeAll(action);
1620  if (list.isEmpty())
1621  it.remove();
1622  }
1623 }
1624 
1625 void MythMainWindow::ClearKeyContext(const QString &context)
1626 {
1627  KeyContext *keycontext = d->m_keyContexts.value(context);
1628  if (keycontext != nullptr)
1629  keycontext->m_actionMap.clear();
1630 }
1631 
1632 void MythMainWindow::BindKey(const QString &context, const QString &action,
1633  const QString &key)
1634 {
1635  QKeySequence keyseq(key);
1636 
1637  if (!d->m_keyContexts.contains(context))
1638  d->m_keyContexts.insert(context, new KeyContext());
1639 
1640  for (unsigned int i = 0; i < (uint)keyseq.count(); i++)
1641  {
1642  int keynum = keyseq[i];
1643  keynum &= ~Qt::UNICODE_ACCEL;
1644 
1645  QStringList dummyaction("");
1646  if (d->m_keyContexts.value(context)->GetMapping(keynum, dummyaction))
1647  {
1648  LOG(VB_GENERAL, LOG_WARNING,
1649  QString("Key %1 is bound to multiple actions in context %2.")
1650  .arg(key).arg(context));
1651  }
1652 
1653  d->m_keyContexts.value(context)->AddMapping(keynum, action);
1654 #if 0
1655  LOG(VB_GENERAL, LOG_DEBUG, QString("Binding: %1 to action: %2 (%3)")
1656  .arg(key).arg(action).arg(context));
1657 #endif
1658 
1659  if (action == "ESCAPE" && context == "Global" && i == 0)
1660  d->m_escapekey = keynum;
1661  }
1662 }
1663 
1664 void MythMainWindow::RegisterKey(const QString &context, const QString &action,
1665  const QString &description, const QString &key)
1666 {
1667  QString keybind = key;
1668 
1669  MSqlQuery query(MSqlQuery::InitCon());
1670 
1671  if (d->m_useDB && query.isConnected())
1672  {
1673  query.prepare("SELECT keylist, description FROM keybindings WHERE "
1674  "context = :CONTEXT AND action = :ACTION AND "
1675  "hostname = :HOSTNAME ;");
1676  query.bindValue(":CONTEXT", context);
1677  query.bindValue(":ACTION", action);
1678  query.bindValue(":HOSTNAME", GetMythDB()->GetHostName());
1679 
1680  if (query.exec() && query.next())
1681  {
1682  keybind = query.value(0).toString();
1683  QString db_description = query.value(1).toString();
1684 
1685  // Update keybinding description if changed
1686  if (db_description != description)
1687  {
1688  LOG(VB_GENERAL, LOG_NOTICE,
1689  "Updating keybinding description...");
1690  query.prepare(
1691  "UPDATE keybindings "
1692  "SET description = :DESCRIPTION "
1693  "WHERE context = :CONTEXT AND "
1694  " action = :ACTION AND "
1695  " hostname = :HOSTNAME");
1696 
1697  query.bindValue(":DESCRIPTION", description);
1698  query.bindValue(":CONTEXT", context);
1699  query.bindValue(":ACTION", action);
1700  query.bindValue(":HOSTNAME", GetMythDB()->GetHostName());
1701 
1702  if (!query.exec() && !(GetMythDB()->SuppressDBMessages()))
1703  {
1704  MythDB::DBError("Update Keybinding", query);
1705  }
1706  }
1707  }
1708  else
1709  {
1710  const QString& inskey = keybind;
1711 
1712  query.prepare("INSERT INTO keybindings (context, action, "
1713  "description, keylist, hostname) VALUES "
1714  "( :CONTEXT, :ACTION, :DESCRIPTION, :KEYLIST, "
1715  ":HOSTNAME );");
1716  query.bindValue(":CONTEXT", context);
1717  query.bindValue(":ACTION", action);
1718  query.bindValue(":DESCRIPTION", description);
1719  query.bindValue(":KEYLIST", inskey);
1720  query.bindValue(":HOSTNAME", GetMythDB()->GetHostName());
1721 
1722  if (!query.exec() && !(GetMythDB()->SuppressDBMessages()))
1723  {
1724  MythDB::DBError("Insert Keybinding", query);
1725  }
1726  }
1727  }
1728 
1729  BindKey(context, action, keybind);
1730  d->m_actionText[context][action] = description;
1731 }
1732 
1733 QString MythMainWindow::GetKey(const QString &context,
1734  const QString &action)
1735 {
1736  MSqlQuery query(MSqlQuery::InitCon());
1737  if (!query.isConnected())
1738  return "?";
1739 
1740  query.prepare("SELECT keylist "
1741  "FROM keybindings "
1742  "WHERE context = :CONTEXT AND "
1743  " action = :ACTION AND "
1744  " hostname = :HOSTNAME");
1745  query.bindValue(":CONTEXT", context);
1746  query.bindValue(":ACTION", action);
1747  query.bindValue(":HOSTNAME", GetMythDB()->GetHostName());
1748 
1749  if (!query.exec() || !query.isActive() || !query.next())
1750  return "?";
1751 
1752  return query.value(0).toString();
1753 }
1754 
1755 QString MythMainWindow::GetActionText(const QString &context,
1756  const QString &action) const
1757 {
1758  if (d->m_actionText.contains(context))
1759  {
1760  QHash<QString, QString> entry = d->m_actionText.value(context);
1761  if (entry.contains(action))
1762  return entry.value(action);
1763  }
1764  return "";
1765 }
1766 
1767 void MythMainWindow::ClearJump(const QString &destination)
1768 {
1769  /* make sure that the jump point exists (using [] would add it)*/
1770  if (d->m_destinationMap.find(destination) == d->m_destinationMap.end())
1771  {
1772  LOG(VB_GENERAL, LOG_ERR,
1773  "Cannot clear ficticious jump point" + destination);
1774  return;
1775  }
1776 
1777  QMutableMapIterator<int, JumpData*> it(d->m_jumpMap);
1778  while (it.hasNext())
1779  {
1780  it.next();
1781  JumpData *jd = it.value();
1782  if (jd->m_destination == destination)
1783  it.remove();
1784  }
1785 }
1786 
1787 
1788 void MythMainWindow::BindJump(const QString &destination, const QString &key)
1789 {
1790  /* make sure the jump point exists */
1791  if (d->m_destinationMap.find(destination) == d->m_destinationMap.end())
1792  {
1793  LOG(VB_GENERAL, LOG_ERR,
1794  "Cannot bind to ficticious jump point" + destination);
1795  return;
1796  }
1797 
1798  QKeySequence keyseq(key);
1799 
1800  for (unsigned int i = 0; i < (uint)keyseq.count(); i++)
1801  {
1802  int keynum = keyseq[i];
1803  keynum &= ~Qt::UNICODE_ACCEL;
1804 
1805  if (d->m_jumpMap.count(keynum) == 0)
1806  {
1807 #if 0
1808  LOG(VB_GENERAL, LOG_DEBUG, QString("Binding: %1 to JumpPoint: %2")
1809  .arg(keybind).arg(destination));
1810 #endif
1811 
1812  d->m_jumpMap[keynum] = &d->m_destinationMap[destination];
1813  }
1814  else
1815  {
1816  LOG(VB_GENERAL, LOG_WARNING,
1817  QString("Key %1 is already bound to a jump point.").arg(key));
1818  }
1819  }
1820 #if 0
1821  else
1822  LOG(VB_GENERAL, LOG_DEBUG,
1823  QString("JumpPoint: %2 exists, no keybinding") .arg(destination));
1824 #endif
1825 }
1826 
1827 void MythMainWindow::RegisterJump(const QString &destination,
1828  const QString &description,
1829  const QString &key, void (*callback)(void),
1830  bool exittomain, QString localAction)
1831 {
1832  QString keybind = key;
1833 
1834  MSqlQuery query(MSqlQuery::InitCon());
1835  if (query.isConnected())
1836  {
1837  query.prepare("SELECT keylist FROM jumppoints WHERE "
1838  "destination = :DEST and hostname = :HOST ;");
1839  query.bindValue(":DEST", destination);
1840  query.bindValue(":HOST", GetMythDB()->GetHostName());
1841 
1842  if (query.exec() && query.next())
1843  {
1844  keybind = query.value(0).toString();
1845  }
1846  else
1847  {
1848  const QString& inskey = keybind;
1849 
1850  query.prepare("INSERT INTO jumppoints (destination, description, "
1851  "keylist, hostname) VALUES ( :DEST, :DESC, :KEYLIST, "
1852  ":HOST );");
1853  query.bindValue(":DEST", destination);
1854  query.bindValue(":DESC", description);
1855  query.bindValue(":KEYLIST", inskey);
1856  query.bindValue(":HOST", GetMythDB()->GetHostName());
1857 
1858  if (!query.exec() || !query.isActive())
1859  {
1860  MythDB::DBError("Insert Jump Point", query);
1861  }
1862  }
1863  }
1864 
1865  JumpData jd =
1866  { callback, destination, description, exittomain, std::move(localAction) };
1867  d->m_destinationMap[destination] = jd;
1868 
1869  BindJump(destination, keybind);
1870 }
1871 
1873 {
1874  QList<QString> destinations = d->m_destinationMap.keys();
1875  QList<QString>::Iterator it;
1876  for (it = destinations.begin(); it != destinations.end(); ++it)
1877  ClearJump(*it);
1878 }
1879 
1880 void MythMainWindow::JumpTo(const QString& destination, bool pop)
1881 {
1882  if (d->m_destinationMap.count(destination) > 0 && d->m_exitMenuCallback == nullptr)
1883  {
1884  d->m_exitingtomain = true;
1885  d->m_popwindows = pop;
1886  d->m_exitMenuCallback = d->m_destinationMap[destination].m_callback;
1887  QCoreApplication::postEvent(
1888  this, new QEvent(MythEvent::kExitToMainMenuEventType));
1889  return;
1890  }
1891 }
1892 
1893 bool MythMainWindow::DestinationExists(const QString& destination) const
1894 {
1895  return d->m_destinationMap.count(destination) > 0;
1896 }
1897 
1899 {
1900  return d->m_destinationMap.keys();
1901 }
1902 
1903 void MythMainWindow::RegisterMediaPlugin(const QString &name,
1904  const QString &desc,
1905  MediaPlayCallback fn)
1906 {
1907  if (d->m_mediaPluginMap.count(name) == 0)
1908  {
1909  LOG(VB_GENERAL, LOG_NOTICE,
1910  QString("Registering %1 as a media playback plugin.").arg(name));
1911  MPData mpd = {desc, fn};
1912  d->m_mediaPluginMap[name] = mpd;
1913  }
1914  else
1915  {
1916  LOG(VB_GENERAL, LOG_NOTICE,
1917  QString("%1 is already registered as a media playback plugin.")
1918  .arg(name));
1919  }
1920 }
1921 
1922 bool MythMainWindow::HandleMedia(const QString &handler, const QString &mrl,
1923  const QString &plot, const QString &title,
1924  const QString &subtitle,
1925  const QString &director, int season,
1926  int episode, const QString &inetref,
1927  int lenMins, const QString &year,
1928  const QString &id, bool useBookmarks)
1929 {
1930  QString lhandler(handler);
1931  if (lhandler.isEmpty())
1932  lhandler = "Internal";
1933 
1934  // Let's see if we have a plugin that matches the handler name...
1935  if (d->m_mediaPluginMap.count(lhandler))
1936  {
1937  d->m_mediaPluginMap[lhandler].m_playFn(mrl, plot, title, subtitle,
1938  director, season, episode,
1939  inetref, lenMins, year, id,
1940  useBookmarks);
1941  return true;
1942  }
1943 
1944  return false;
1945 }
1946 
1948 {
1949 #ifdef USING_LIBCEC
1950  if (d->m_cecAdapter)
1951  d->m_cecAdapter->Action((poweron) ? ACTION_TVPOWERON : ACTION_TVPOWEROFF);
1952 #else
1953  (void) poweron;
1954 #endif
1955 }
1956 
1964  void *Opaque1, void *Opaque2)
1965 {
1967  if (!window)
1968  {
1969  LOG(VB_GENERAL, LOG_ERR, "Callback failed - no MythMainWindow");
1970  return;
1971  }
1972 
1973  QWaitCondition wait;
1974  QMutex lock;
1975  lock.lock();
1976  auto *event = new MythCallbackEvent(Function, &wait, Opaque1, Opaque2);
1977  QCoreApplication::postEvent(window, event, Qt::HighEventPriority);
1978  int count = 0;
1979  while (!wait.wait(&lock, 100) && (count += 100))
1980  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Waited %1ms for %2").arg(count).arg(Debug));
1981  lock.unlock();
1982 }
1983 
1985 {
1986  d->m_allowInput = allow;
1987 }
1988 
1990 {
1991  /* complete the stroke if its our first timeout */
1992  if (d->m_gesture.recording())
1993  {
1994  d->m_gesture.stop();
1995  }
1996 
1997  /* get the last gesture */
1999 
2000  if (e->gesture() < MythGestureEvent::Click)
2001  QCoreApplication::postEvent(this, e);
2002 }
2003 
2004 // Return code = true to skip further processing, false to continue
2005 // sNewEvent: Caller must pass in a QScopedPointer that will be used
2006 // to delete a new event if one is created.
2008  QScopedPointer<QEvent> &sNewEvent)
2009 {
2010  QEvent *newEvent = nullptr;
2011  auto *ke = dynamic_cast<QKeyEvent*>(*e);
2012  if (!ke)
2013  return false;
2014  int keycode = ke->key();
2015  // Ignore unknown key codes
2016  if (keycode == 0)
2017  return false;
2018 
2019  switch ((*e)->type())
2020  {
2021  case QEvent::KeyPress:
2022  {
2023  // Check if we are in the middle of a long press
2024  if (keycode == d->m_longPressKeyCode)
2025  {
2026  if (ke->timestamp() - d->m_longPressTime < LONGPRESS_INTERVAL
2027  || d->m_longPressTime == 0)
2028  {
2029  // waiting for release of key.
2030  return true; // discard the key press
2031  }
2032 
2033  // expired log press - generate long key
2034  newEvent = new QKeyEvent(QEvent::KeyPress, keycode,
2035  ke->modifiers() | Qt::MetaModifier, ke->nativeScanCode(),
2036  ke->nativeVirtualKey(), ke->nativeModifiers(),
2037  ke->text(), false,1);
2038  *e = newEvent;
2039  sNewEvent.reset(newEvent);
2040  d->m_longPressTime = 0; // indicate we have generated the long press
2041  return false;
2042  }
2043  // If we got a keycode different from the long press keycode it
2044  // may have been injected by a jump point. Ignore it.
2045  if (d->m_longPressKeyCode != 0)
2046  return false;
2047 
2048  // Process start of possible new long press.
2049  d->m_longPressKeyCode = 0;
2050  QStringList actions;
2051  bool handled = TranslateKeyPress("Long Press",
2052  ke, actions,false);
2053  if (handled)
2054  {
2055  // This shoudl never happen,, because we passed in false
2056  // to say do not process jump points and yet it returned true
2057  // to say it processed a jump point.
2058  LOG(VB_GUI, LOG_ERR, QString("TranslateKeyPress Long Press Invalid Response"));
2059  return true;
2060  }
2061  if (!actions.empty() && actions[0].startsWith("LONGPRESS"))
2062  {
2063  // Beginning of a press
2064  d->m_longPressKeyCode = keycode;
2065  d->m_longPressTime = ke->timestamp();
2066  return true; // discard the key press
2067  }
2068  break;
2069  }
2070  case QEvent::KeyRelease:
2071  {
2072  if (keycode == d->m_longPressKeyCode)
2073  {
2074  if (ke->isAutoRepeat())
2075  return true;
2076  if (d->m_longPressTime > 0)
2077  {
2078  // short press or non-repeating keyboard - generate key
2079  Qt::KeyboardModifiers modifier = Qt::NoModifier;
2080  if (ke->timestamp() - d->m_longPressTime >= LONGPRESS_INTERVAL)
2081  {
2082  // non-repeatng keyboard
2083  modifier = Qt::MetaModifier;
2084  }
2085  newEvent = new QKeyEvent(QEvent::KeyPress, keycode,
2086  ke->modifiers() | modifier, ke->nativeScanCode(),
2087  ke->nativeVirtualKey(), ke->nativeModifiers(),
2088  ke->text(), false,1);
2089  *e = newEvent;
2090  sNewEvent.reset(newEvent);
2091  d->m_longPressKeyCode = 0;
2092  return false;
2093  }
2094 
2095  // end of long press
2096  d->m_longPressKeyCode = 0;
2097  return true;
2098  }
2099  break;
2100  }
2101  default:
2102  break;
2103  }
2104  return false;
2105 }
2106 
2107 bool MythMainWindow::eventFilter(QObject * /*watched*/, QEvent *e)
2108 {
2109  /* Don't let anything through if input is disallowed. */
2110  if (!d->m_allowInput)
2111  return true;
2112 
2113  QScopedPointer<QEvent> sNewEvent(nullptr);
2114  if (keyLongPressFilter(&e, sNewEvent))
2115  return true;
2116 
2117  switch (e->type())
2118  {
2119  case QEvent::KeyPress:
2120  {
2121  ResetIdleTimer();
2122  auto *ke = dynamic_cast<QKeyEvent*>(e);
2123 
2124  // Work around weird GCC run-time bug. Only manifest on Mac OS X
2125  if (!ke)
2126  ke = static_cast<QKeyEvent *>(e);
2127 
2128 #ifdef Q_OS_LINUX
2129  // Fixups for _some_ linux native codes that QT doesn't know
2130  if (ke->key() <= 0)
2131  {
2132  int keycode = 0;
2133  switch(ke->nativeScanCode())
2134  {
2135  case 209: // XF86AudioPause
2136  keycode = Qt::Key_MediaPause;
2137  break;
2138  default:
2139  break;
2140  }
2141 
2142  if (keycode > 0)
2143  {
2144  auto *key = new QKeyEvent(QEvent::KeyPress, keycode,
2145  ke->modifiers());
2146  QObject *key_target = getTarget(*key);
2147  if (!key_target)
2148  QCoreApplication::postEvent(this, key);
2149  else
2150  QCoreApplication::postEvent(key_target, key);
2151 
2152  return true;
2153  }
2154  }
2155 #endif
2156 
2157  QVector<MythScreenStack *>::Iterator it;
2158  for (it = d->m_stackList.end()-1; it != d->m_stackList.begin()-1; --it)
2159  {
2160  MythScreenType *top = (*it)->GetTopScreen();
2161  if (top)
2162  {
2163  if (top->keyPressEvent(ke))
2164  return true;
2165 
2166  // Note: The following break prevents keypresses being
2167  // sent to windows below popups
2168  if ((*it)->objectName() == "popup stack")
2169  break;
2170  }
2171  }
2172  break;
2173  }
2174  case QEvent::MouseButtonPress:
2175  {
2176  ResetIdleTimer();
2177  ShowMouseCursor(true);
2178  if (!d->m_gesture.recording())
2179  {
2180  d->m_gesture.start();
2181  auto *mouseEvent = dynamic_cast<QMouseEvent*>(e);
2182  if (!mouseEvent)
2183  return false;
2184  d->m_gesture.record(mouseEvent->pos());
2185 
2186  /* start a single shot timer */
2187  d->m_gestureTimer->start(GESTURE_TIMEOUT);
2188 
2189  return true;
2190  }
2191  break;
2192  }
2193  case QEvent::MouseButtonRelease:
2194  {
2195  ResetIdleTimer();
2196  ShowMouseCursor(true);
2197  if (d->m_gestureTimer->isActive())
2198  d->m_gestureTimer->stop();
2199 
2200  if (d->m_gesture.recording())
2201  {
2202  d->m_gesture.stop();
2204 
2205  auto *mouseEvent = dynamic_cast<QMouseEvent*>(e);
2206 
2207  /* handle clicks separately */
2208  if (ge->gesture() == MythGestureEvent::Click)
2209  {
2210  if (!mouseEvent)
2211  return false;
2212 
2213  QPoint p = mouseEvent->pos();
2214 
2215  ge->SetPosition(p);
2216 
2218  switch (mouseEvent->button())
2219  {
2220  case Qt::LeftButton :
2222  break;
2223  case Qt::RightButton :
2225  break;
2226  case Qt::MidButton :
2228  break;
2229  case Qt::XButton1 :
2231  break;
2232  case Qt::XButton2 :
2234  break;
2235  default :
2236  button = MythGestureEvent::NoButton;
2237  }
2238 
2239  ge->SetButton(button);
2240 
2241  for (auto it = d->m_stackList.end()-1;
2242  it != d->m_stackList.begin()-1;
2243  --it)
2244  {
2245  MythScreenType *screen = (*it)->GetTopScreen();
2246 
2247  if (!screen || !screen->ContainsPoint(p))
2248  continue;
2249 
2250  if (screen->gestureEvent(ge))
2251  break;
2252 
2253  // Note: The following break prevents clicks being
2254  // sent to windows below popups
2255  //
2256  // we want to permit this in some cases, e.g.
2257  // when the music miniplayer is on screen or a
2258  // non-interactive alert/news scroller. So these
2259  // things need to be in a third or more stack
2260  if ((*it)->objectName() == "popup stack")
2261  break;
2262  }
2263 
2264  delete ge;
2265  }
2266  else
2267  {
2268  bool handled = false;
2269 
2270  if (!mouseEvent)
2271  {
2272  QCoreApplication::postEvent(this, ge);
2273  return true;
2274  }
2275 
2276  QPoint p = mouseEvent->pos();
2277 
2278  ge->SetPosition(p);
2279 
2280  for (auto it = d->m_stackList.end()-1;
2281  it != d->m_stackList.begin()-1;
2282  --it)
2283  {
2284  MythScreenType *screen = (*it)->GetTopScreen();
2285 
2286  if (!screen || !screen->ContainsPoint(p))
2287  continue;
2288 
2289  if (screen->gestureEvent(ge))
2290  {
2291  handled = true;
2292  break;
2293  }
2294 
2295  // Note: The following break prevents clicks being
2296  // sent to windows below popups
2297  //
2298  // we want to permit this in some cases, e.g.
2299  // when the music miniplayer is on screen or a
2300  // non-interactive alert/news scroller. So these
2301  // things need to be in a third or more stack
2302  if ((*it)->objectName() == "popup stack")
2303  break;
2304  }
2305 
2306  if (handled)
2307  {
2308  delete ge;
2309  }
2310  else
2311  {
2312  QCoreApplication::postEvent(this, ge);
2313  }
2314  }
2315 
2316  return true;
2317  }
2318  break;
2319  }
2320  case QEvent::MouseMove:
2321  {
2322  ResetIdleTimer();
2323  ShowMouseCursor(true);
2324  if (d->m_gesture.recording())
2325  {
2326  /* reset the timer */
2327  d->m_gestureTimer->stop();
2328  d->m_gestureTimer->start(GESTURE_TIMEOUT);
2329 
2330  auto *mouseEvent = dynamic_cast<QMouseEvent*>(e);
2331  if (!mouseEvent)
2332  return false;
2333  d->m_gesture.record(mouseEvent->pos());
2334  return true;
2335  }
2336  break;
2337  }
2338  case QEvent::Wheel:
2339  {
2340  ResetIdleTimer();
2341  ShowMouseCursor(true);
2342  auto* qmw = static_cast<QWheelEvent*>(e);
2343  int delta = qmw->delta();
2344  if (delta>0)
2345  {
2346  qmw->accept();
2347  auto *key = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up,
2348  Qt::NoModifier);
2349  QObject *key_target = getTarget(*key);
2350  if (!key_target)
2351  QCoreApplication::postEvent(this, key);
2352  else
2353  QCoreApplication::postEvent(key_target, key);
2354  }
2355  if (delta<0)
2356  {
2357  qmw->accept();
2358  auto *key = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down,
2359  Qt::NoModifier);
2360  QObject *key_target = getTarget(*key);
2361  if (!key_target)
2362  QCoreApplication::postEvent(this, key);
2363  else
2364  QCoreApplication::postEvent(key_target, key);
2365  }
2366  break;
2367  }
2368  default:
2369  break;
2370  }
2371 
2372  return false;
2373 }
2374 
2376 {
2377  if (ce->type() == MythGestureEvent::kEventType)
2378  {
2379  auto *ge = static_cast<MythGestureEvent*>(ce);
2380  MythScreenStack *toplevel = GetMainStack();
2381  if (toplevel)
2382  {
2383  MythScreenType *screen = toplevel->GetTopScreen();
2384  if (screen)
2385  screen->gestureEvent(ge);
2386  }
2387  LOG(VB_GUI, LOG_DEBUG, QString("Gesture: %1") .arg(QString(*ge)));
2388  }
2389  else if (ce->type() == MythEvent::kExitToMainMenuEventType &&
2390  d->m_exitingtomain)
2391  {
2392  ExitToMainMenu();
2393  }
2394  else if (ce->type() == ExternalKeycodeEvent::kEventType)
2395  {
2396  auto *eke = static_cast<ExternalKeycodeEvent *>(ce);
2397  int keycode = eke->getKeycode();
2398 
2399  QKeyEvent key(QEvent::KeyPress, keycode, Qt::NoModifier);
2400 
2401  QObject *key_target = getTarget(key);
2402  if (!key_target)
2403  QCoreApplication::sendEvent(this, &key);
2404  else
2405  QCoreApplication::sendEvent(key_target, &key);
2406  }
2407 #if defined(USE_LIRC) || defined(USING_APPLEREMOTE)
2408  else if (ce->type() == LircKeycodeEvent::kEventType &&
2409  !d->m_ignoreLircKeys)
2410  {
2411  auto *lke = static_cast<LircKeycodeEvent *>(ce);
2412 
2413  if (LircKeycodeEvent::kLIRCInvalidKeyCombo == lke->modifiers())
2414  {
2415  LOG(VB_GENERAL, LOG_WARNING,
2416  QString("Attempt to convert LIRC key sequence '%1' "
2417  "to a Qt key sequence failed.")
2418  .arg(lke->lirctext()));
2419  }
2420  else
2421  {
2423  if (GetMythUI()->GetScreenIsAsleep())
2424  return;
2425 
2426  QKeyEvent key(lke->keytype(), lke->key(),
2427  lke->modifiers(), lke->text());
2428 
2429  QObject *key_target = getTarget(key);
2430  if (!key_target)
2431  QCoreApplication::sendEvent(this, &key);
2432  else
2433  QCoreApplication::sendEvent(key_target, &key);
2434  }
2435  }
2436 #endif
2437 #ifdef USE_JOYSTICK_MENU
2438  else if (ce->type() == JoystickKeycodeEvent::kEventType &&
2440  {
2441  auto *jke = static_cast<JoystickKeycodeEvent *>(ce);
2442  int keycode = jke->getKeycode();
2443 
2444  if (keycode)
2445  {
2447  if (GetMythUI()->GetScreenIsAsleep())
2448  return;
2449 
2450  Qt::KeyboardModifiers mod = Qt::KeyboardModifiers(keycode & Qt::MODIFIER_MASK);
2451  int k = (keycode & ~Qt::MODIFIER_MASK); /* trim off the mod */
2452  QString text;
2453 
2454  QKeyEvent key(jke->isKeyDown() ? QEvent::KeyPress :
2455  QEvent::KeyRelease, k, mod, text);
2456 
2457  QObject *key_target = getTarget(key);
2458  if (!key_target)
2459  QCoreApplication::sendEvent(this, &key);
2460  else
2461  QCoreApplication::sendEvent(key_target, &key);
2462  }
2463  else
2464  {
2465  LOG(VB_GENERAL, LOG_WARNING,
2466  QString("attempt to convert '%1' to a key sequence failed. "
2467  "Fix your key mappings.")
2468  .arg(jke->getJoystickMenuText()));
2469  }
2470  }
2471 #endif
2472  else if (ce->type() == MythMediaEvent::kEventType)
2473  {
2474  auto *me = static_cast<MythMediaEvent*>(ce);
2475 
2476  // A listener based system might be more efficient, but we should never
2477  // have that many screens open at once so impact should be minimal.
2478  //
2479  // This approach is simpler for everyone to follow. Plugin writers
2480  // don't have to worry about adding their screens to the list because
2481  // all screens receive media events.
2482  //
2483  // Events are even sent to hidden or backgrounded screens, this avoids
2484  // the need for those to poll for changes when they become visible again
2485  // however this needs to be kept in mind if media changes trigger
2486  // actions which would not be appropriate when the screen doesn't have
2487  // focus. It is the programmers responsibility to ignore events when
2488  // necessary.
2489  for (auto it = d->m_stackList.begin(); it != d->m_stackList.end(); ++it)
2490  {
2491  QVector<MythScreenType *> screenList;
2492  (*it)->GetScreenList(screenList);
2493  for (auto sit = screenList.begin(); sit != screenList.end(); ++sit)
2494  {
2495  MythScreenType *screen = (*sit);
2496  if (screen)
2497  screen->mediaEvent(me);
2498  }
2499  }
2500 
2501  // Debugging
2502  MythMediaDevice *device = me->getDevice();
2503  if (device)
2504  {
2505  LOG(VB_GENERAL, LOG_DEBUG, QString("Media Event: %1 - %2")
2506  .arg(device->getDevicePath()).arg(device->getStatus()));
2507  }
2508  }
2509  else if (ce->type() == ScreenSaverEvent::kEventType)
2510  {
2511  auto *sse = static_cast<ScreenSaverEvent *>(ce);
2512  switch (sse->getSSEventType())
2513  {
2515  {
2517  break;
2518  }
2520  {
2522  break;
2523  }
2525  {
2527  break;
2528  }
2529  default:
2530  {
2531  LOG(VB_GENERAL, LOG_ERR,
2532  QString("Unknown ScreenSaverEvent type: %1")
2533  .arg(sse->getSSEventType()));
2534  }
2535  }
2536  }
2537  else if (ce->type() == MythEvent::kPushDisableDrawingEventType)
2538  {
2539  PushDrawDisabled();
2540  }
2541  else if (ce->type() == MythEvent::kPopDisableDrawingEventType)
2542  {
2543  PopDrawDisabled();
2544  }
2545  else if (ce->type() == MythEvent::kLockInputDevicesEventType)
2546  {
2547  LockInputDevices(true);
2548  PauseIdleTimer(true);
2549  }
2550  else if (ce->type() == MythEvent::kUnlockInputDevicesEventType)
2551  {
2552  LockInputDevices(false);
2553  PauseIdleTimer(false);
2554  }
2555  else if (ce->type() == MythEvent::kDisableUDPListenerEventType)
2556  {
2557  d->m_udpListener->Disable();
2558  }
2559  else if (ce->type() == MythEvent::kEnableUDPListenerEventType)
2560  {
2561  d->m_udpListener->Enable();
2562  }
2563  else if (ce->type() == MythEvent::MythEventMessage)
2564  {
2565  auto *me = static_cast<MythEvent *>(ce);
2566  QString message = me->Message();
2567 
2568  if (message.startsWith(ACTION_HANDLEMEDIA))
2569  {
2570  if (me->ExtraDataCount() == 1)
2571  HandleMedia("Internal", me->ExtraData(0));
2572  else if (me->ExtraDataCount() >= 11)
2573  {
2574  bool usebookmark = true;
2575  if (me->ExtraDataCount() >= 12)
2576  usebookmark = (me->ExtraData(11).toInt() != 0);
2577  HandleMedia("Internal", me->ExtraData(0),
2578  me->ExtraData(1), me->ExtraData(2),
2579  me->ExtraData(3), me->ExtraData(4),
2580  me->ExtraData(5).toInt(), me->ExtraData(6).toInt(),
2581  me->ExtraData(7), me->ExtraData(8).toInt(),
2582  me->ExtraData(9), me->ExtraData(10),
2583  usebookmark);
2584  }
2585  else
2586  LOG(VB_GENERAL, LOG_ERR, "Failed to handle media");
2587  }
2588  else if (message.startsWith(ACTION_SCREENSHOT))
2589  {
2590  int width = 0;
2591  int height = 0;
2592  QString filename;
2593 
2594  if (me->ExtraDataCount() >= 2)
2595  {
2596  width = me->ExtraData(0).toInt();
2597  height = me->ExtraData(1).toInt();
2598 
2599  if (me->ExtraDataCount() == 3)
2600  filename = me->ExtraData(2);
2601  }
2602  ScreenShot(width, height, filename);
2603  }
2604  else if (message == ACTION_GETSTATUS)
2605  {
2606  QVariantMap state;
2607  state.insert("state", "idle");
2608  state.insert("menutheme",
2609  GetMythDB()->GetSetting("menutheme", "defaultmenu"));
2610  state.insert("currentlocation", GetMythUI()->GetCurrentLocation());
2612  }
2613  else if (message == "CLEAR_SETTINGS_CACHE")
2614  {
2615  // update the idle time
2616  d->m_idleTime = gCoreContext->GetNumSetting("FrontendIdleTimeout",
2617  STANDBY_TIMEOUT);
2618 
2619  if (d->m_idleTime < 0)
2620  d->m_idleTime = 0;
2621 
2622  bool isActive = d->m_idleTimer->isActive();
2623 
2624  if (isActive)
2625  d->m_idleTimer->stop();
2626 
2627  if (d->m_idleTime > 0)
2628  {
2629  d->m_idleTimer->setInterval(1000 * 60 * d->m_idleTime);
2630 
2631  if (isActive)
2632  d->m_idleTimer->start();
2633 
2634  LOG(VB_GENERAL, LOG_INFO, QString("Updating the frontend idle time to: %1 mins").arg(d->m_idleTime));
2635  }
2636  else
2637  LOG(VB_GENERAL, LOG_INFO, "Frontend idle timeout is disabled");
2638  }
2639  else if (message == "NOTIFICATION")
2640  {
2641  MythNotification mn(*me);
2643  return;
2644  }
2645  else if (message == "RECONNECT_SUCCESS" && d->m_standby)
2646  {
2647  // If the connection to the master backend has just been (re-)established
2648  // but we're in standby, make sure the backend is not blocked from
2649  // shutting down.
2651  }
2652  }
2653  else if (ce->type() == MythEvent::MythUserMessage)
2654  {
2655  auto *me = static_cast<MythEvent *>(ce);
2656  const QString& message = me->Message();
2657 
2658  if (!message.isEmpty())
2659  ShowOkPopup(message);
2660  }
2661  else if (ce->type() == MythNotificationCenterEvent::kEventType)
2662  {
2664  }
2665  else if (ce->type() == MythCallbackEvent::kCallbackType)
2666  {
2667  auto *me = static_cast<MythCallbackEvent*>(ce);
2668  if (me && me->m_function)
2669  me->m_function(me->m_opaque1, me->m_opaque2, me->m_opaque3);
2670  }
2671 }
2672 
2673 QObject *MythMainWindow::getTarget(QKeyEvent &key)
2674 {
2675  QObject *key_target = nullptr;
2676 
2677  key_target = QWidget::keyboardGrabber();
2678 
2679  if (!key_target)
2680  {
2681  QWidget *focus_widget = qApp->focusWidget();
2682  if (focus_widget && focus_widget->isEnabled())
2683  {
2684  key_target = focus_widget;
2685 
2686  // Yes this is special code for handling the
2687  // the escape key.
2688  if (key.key() == d->m_escapekey && focus_widget->topLevelWidget())
2689  key_target = focus_widget->topLevelWidget();
2690  }
2691  }
2692 
2693  if (!key_target)
2694  key_target = this;
2695 
2696  return key_target;
2697 }
2698 
2700 {
2701  float floatSize = pointSize;
2702  float desired = 100.0;
2703 
2704 #ifdef _WIN32
2705  // logicalDpiY not supported in Windows.
2706  int logicalDpiY = 100;
2707  HDC hdc = GetDC(nullptr);
2708  if (hdc)
2709  {
2710  logicalDpiY = GetDeviceCaps(hdc, LOGPIXELSY);
2711  ReleaseDC(nullptr, hdc);
2712  }
2713 #else
2714  int logicalDpiY = this->logicalDpiY();
2715 #endif
2716 
2717  // adjust for screen resolution relative to 100 dpi
2718  floatSize = floatSize * desired / logicalDpiY;
2719  // adjust for myth GUI size relative to 800x600
2720  floatSize = floatSize * d->m_hmult;
2721  // round to the nearest point size
2722  pointSize = lroundf(floatSize);
2723 
2724  return pointSize;
2725 }
2726 
2728 {
2729  MythRect ret;
2730  ret.setWidth((int)(rect.width() * d->m_wmult));
2731  ret.setHeight((int)(rect.height() * d->m_hmult));
2732  ret.moveTopLeft(QPoint((int)(rect.x() * d->m_wmult),
2733  (int)(rect.y() * d->m_hmult)));
2734  ret = ret.normalized();
2735 
2736  return ret;
2737 }
2738 
2739 QPoint MythMainWindow::NormPoint(const QPoint &point)
2740 {
2741  QPoint ret;
2742  ret.setX((int)(point.x() * d->m_wmult));
2743  ret.setY((int)(point.y() * d->m_hmult));
2744 
2745  return ret;
2746 }
2747 
2748 QSize MythMainWindow::NormSize(const QSize &size)
2749 {
2750  QSize ret;
2751  ret.setWidth((int)(size.width() * d->m_wmult));
2752  ret.setHeight((int)(size.height() * d->m_hmult));
2753 
2754  return ret;
2755 }
2756 
2757 int MythMainWindow::NormX(const int x)
2758 {
2759  return qRound(x * d->m_wmult);
2760 }
2761 
2762 int MythMainWindow::NormY(const int y)
2763 {
2764  return qRound(y * d->m_hmult);
2765 }
2766 
2767 void MythMainWindow::SetScalingFactors(float wmult, float hmult)
2768 {
2769  d->m_wmult = wmult;
2770  d->m_hmult = hmult;
2771 }
2772 
2774 {
2775  return d->m_uiScreenRect;
2776 }
2777 
2779 {
2780  d->m_uiScreenRect = rect;
2781 }
2782 
2784 {
2785  return d->m_drawInterval;
2786 }
2787 
2789 {
2790 #ifdef USE_LIRC
2791  if (d->m_lircThread)
2792  {
2794  d->m_lircThread = nullptr;
2795  }
2796 
2797  QString config_file = GetConfDir() + "/lircrc";
2798  if (!QFile::exists(config_file))
2799  config_file = QDir::homePath() + "/.lircrc";
2800 
2801  /* lircd socket moved from /dev/ to /var/run/lirc/ in lirc 0.8.6 */
2802  QString lirc_socket = "/dev/lircd";
2803  if (!QFile::exists(lirc_socket))
2804  lirc_socket = "/var/run/lirc/lircd";
2805 
2806  d->m_lircThread = new LIRC(
2807  this,
2808  GetMythDB()->GetSetting("LircSocket", lirc_socket),
2809  "mythtv", config_file);
2810 
2811  if (d->m_lircThread->Init())
2812  {
2813  d->m_lircThread->start();
2814  }
2815  else
2816  {
2818  d->m_lircThread = nullptr;
2819  }
2820 #endif
2821 }
2822 
2824 {
2825  if( locked )
2826  LOG(VB_GENERAL, LOG_INFO, "Locking input devices");
2827  else
2828  LOG(VB_GENERAL, LOG_INFO, "Unlocking input devices");
2829 
2830 #ifdef USE_LIRC
2831  d->m_ignoreLircKeys = locked;
2832 #endif
2833 
2834 #ifdef USE_JOYSTICK_MENU
2835  d->m_ignoreJoystickKeys = locked;
2836 #endif
2837 }
2838 
2840 {
2841  if (show && GetMythDB()->GetBoolSetting("HideMouseCursor", false))
2842  return;
2843 
2844  // Set cursor call must come after Show() to work on some systems.
2845  setCursor(show ? (Qt::ArrowCursor) : (Qt::BlankCursor));
2846 
2847  if (show)
2848  d->m_hideMouseTimer->start();
2849 }
2850 
2852 {
2853  ShowMouseCursor(false);
2854 }
2855 
2857 {
2858  if (d->m_disableIdle)
2859  return;
2860 
2861  if (d->m_idleTime == 0 ||
2862  !d->m_idleTimer->isActive() ||
2863  (d->m_standby && d->m_enteringStandby))
2864  return;
2865 
2866  if (d->m_standby)
2867  ExitStandby(false);
2868 
2869  QMetaObject::invokeMethod(d->m_idleTimer, "start");
2870 }
2871 
2873 {
2874  if (d->m_disableIdle)
2875  return;
2876 
2877  // don't do anything if the idle timer is disabled
2878  if (d->m_idleTime == 0)
2879  return;
2880 
2881  if (pause)
2882  {
2883  LOG(VB_GENERAL, LOG_NOTICE, "Suspending idle timer");
2884  QMetaObject::invokeMethod(d->m_idleTimer, "stop");
2885  }
2886  else
2887  {
2888  LOG(VB_GENERAL, LOG_NOTICE, "Resuming idle timer");
2889  QMetaObject::invokeMethod(d->m_idleTimer, "start");
2890  }
2891 
2892  // ResetIdleTimer();
2893 }
2894 
2896 {
2897  if (d->m_disableIdle)
2898  return;
2899 
2900  d->m_enteringStandby = false;
2901 
2902  if (d->m_idleTime > 0 && !d->m_standby)
2903  {
2904  LOG(VB_GENERAL, LOG_NOTICE, QString("Entering standby mode after "
2905  "%1 minutes of inactivity")
2906  .arg(d->m_idleTime));
2907  EnterStandby(false);
2908  if (gCoreContext->GetNumSetting("idleTimeoutSecs", 0) > 0)
2909  {
2910  d->m_enteringStandby = true;
2911  JumpTo("Standby Mode");
2912  }
2913  }
2914 }
2915 
2917 {
2918  if (manual && d->m_enteringStandby)
2919  d->m_enteringStandby = false;
2920 
2921  if (d->m_standby)
2922  return;
2923 
2924  // We've manually entered standby mode and we want to pause the timer
2925  // to prevent it being Reset
2926  if (manual)
2927  {
2928  PauseIdleTimer(true);
2929  LOG(VB_GENERAL, LOG_NOTICE, QString("Entering standby mode"));
2930  }
2931 
2932  d->m_standby = true;
2934 
2935  QVariantMap state;
2936  state.insert("state", "standby");
2937  state.insert("menutheme",
2938  GetMythDB()->GetSetting("menutheme", "defaultmenu"));
2939  state.insert("currentlocation", GetMythUI()->GetCurrentLocation());
2941 
2942  // Cache WOL settings in case DB goes down
2943  QString masterserver = gCoreContext->GetSetting
2944  ("MasterServerName");
2946  ("BackendServerAddr", masterserver);
2948  gCoreContext->GetSetting("WOLbackendCommand", "");
2949 
2950  // While in standby do not attempt to wake the backend
2951  gCoreContext->SetWOLAllowed(false);
2952 }
2953 
2955 {
2956  if (d->m_enteringStandby)
2957  return;
2958 
2959  if (manual)
2960  PauseIdleTimer(false);
2961  else if (gCoreContext->GetNumSetting("idleTimeoutSecs", 0) > 0)
2962  JumpTo("Main Menu");
2963 
2964  if (!d->m_standby)
2965  return;
2966 
2967  LOG(VB_GENERAL, LOG_NOTICE, "Leaving standby mode");
2968 
2969  d->m_standby = false;
2970 
2971  // We may attempt to wake the backend
2972  gCoreContext->SetWOLAllowed(true);
2973 
2975 
2976  QVariantMap state;
2977  state.insert("state", "idle");
2978  state.insert("menutheme",
2979  GetMythDB()->GetSetting("menutheme", "defaultmenu"));
2980  state.insert("currentlocation", GetMythUI()->GetCurrentLocation());
2982 }
2983 
2984 void MythMainWindow::DisableIdleTimer(bool disableIdle)
2985 {
2986  if ((d->m_disableIdle = disableIdle))
2987  QMetaObject::invokeMethod(d->m_idleTimer, "stop");
2988  else
2989  QMetaObject::invokeMethod(d->m_idleTimer, "start");
2990 }
2991 
2992 void MythMainWindow::onApplicationStateChange(Qt::ApplicationState state)
2993 {
2994  LOG(VB_GENERAL, LOG_NOTICE, QString("Application State Changed to %1").arg(state));
2995  switch (state)
2996  {
2997  case Qt::ApplicationState::ApplicationActive:
2998  PopDrawDisabled();
2999  break;
3000  case Qt::ApplicationState::ApplicationSuspended:
3001  PushDrawDisabled();
3002  break;
3003  default:
3004  break;
3005  }
3006 }
3007 /* vim: set expandtab tabstop=4 shiftwidth=4: */
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:781
bool record(const QPoint &p)
Record a point.
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
QMap< int, QStringList > m_actionMap
#define ACTION_6
Definition: mythuiactions.h:10
static Type kEventType
Definition: screensaver.h:23
void setHeight(const QString &sHeight)
Definition: mythrect.cpp:233
MythUDPListener * m_udpListener
void paintEvent(QPaintEvent *e) override
uint PushDrawDisabled(void)
~MythPainterWindowGL() override
const QString & getDevicePath() const
Definition: mythmedia.h:61
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:862
#define D3D9_PAINTER
Definition: mythuidefines.h:6
#define OPENGL_PAINTER
Definition: mythuidefines.h:3
int DisplayedNotifications(void) const
Returns number of notifications currently displayed.
static int TranslateKeyNum(QKeyEvent *e)
bool isListeningToRemote()
Definition: AppleRemote.cpp:72
void MoveResize(QRect &Geometry)
virtual void start(int msec)
MythMainWindowPrivate * d
MediaPlayCallback m_playFn
bool m_useDB
To allow or prevent database access.
JoystickMenuThread * m_joystickThread
QPaintEngine * paintEngine() const override
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
void(* m_exitMenuMediaDeviceCallback)(MythMediaDevice *mediadevice)
AppleRemote * m_appleRemote
#define ACTION_SCREENSHOT
Definition: mythuiactions.h:22
QString GetActionText(const QString &context, const QString &action) const
MythConfirmationDialog * ShowOkPopup(const QString &message, QObject *parent, const char *slot, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
static const unsigned kLIRCInvalidKeyCombo
Definition: lircevent.h:26
MythMainWindowPrivate * d
bool IsShared(void) const
Warning: The reference count can be decremented between the call to this function and the use of it's...
static Type MythEventMessage
Definition: mythevent.h:66
void ClearJump(const QString &destination)
MythPainterWindowGL(MythMainWindow *win, MythMainWindowPrivate *priv, MythRenderOpenGL *rend)
static AppleRemote * Get()
Definition: AppleRemote.cpp:47
void SetPosition(QPoint position)
Definition: mythgesture.h:115
#define ACTION_0
Definition: mythuiactions.h:4
void removeListener(QObject *listener)
Remove a listener to the observable.
MythScreenStack * GetStackAt(int pos)
#define ACTION_2
Definition: mythuiactions.h:6
static int GetMasterServerPort(void)
Returns the Master Backend control port If no master server port has been defined in the database,...
void signalSetDrawEnabled(bool enable)
QMap< int, JumpData * > m_jumpMap
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:125
MythScreenStack * m_mainStack
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
bool Init(void)
Definition: lirc.cpp:164
static MythMainWindow * getMainWindow(const bool useDB=true)
Return the existing main window, or create one.
#define ACTION_UP
Definition: mythuiactions.h:16
AppleRemoteListener * m_appleRemoteListener
MythNotificationCenter * m_nc
bool isConnected(void)
Only updated once during object creation.
Definition: mythdbcon.h:135
MythPainter * GetMythPainter(void)
uint PopDrawDisabled(void)
int NormX(const int x)
bool recording(void) const
Determine if the stroke is being recorded.
QString GetSettingOnHost(const QString &key, const QString &host, const QString &defaultval="")
void AllowInput(bool allow)
void DestroyMythMainWindow(void)
void SetWidget(QWidget *MainWindow)
MythScreenStack * GetStack(const QString &stackname)
void onApplicationStateChange(Qt::ApplicationState state)
virtual bool SupportsClipping(void)=0
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
void stop(void)
Stop recording.
#define LOC
MythScreenStack * GetMainStack()
void DoResetScreensaver(void)
void addListener(QObject *listener)
Add a listener to the observable.
QMap< QWidget *, bool > m_enabledWidgets
static bool ScreenShot(int w=0, int h=0, QString filename="")
#define ACTION_RIGHT
Definition: mythuiactions.h:19
Gesture gesture(void) const
Get the gesture type.
Definition: mythgesture.h:107
static bool SaveScreenShot(const QImage &image, QString filename="")
QSize NormSize(const QSize &size)
static MythNotificationCenter * GetInstance(void)
returns the MythNotificationCenter singleton
void HandleTVPower(bool poweron)
QString m_destination
static Type kEventType
Definition: mythevent.h:92
virtual void start(void)
Definition: lirc.cpp:322
QScreen * GetCurrentScreen(void)
Return a pointer to the screen to use.
void ResetIdleTimer(void)
void paintEvent(QPaintEvent *e) override
QString m_description
void Init(const QString &forcedpainter=QString(), bool mayReInit=true)
void ClearKeyContext(const QString &context)
void customEvent(QEvent *ce) override
static Type kPushDisableDrawingEventType
Definition: mythevent.h:71
static void(* m_callback)(void *, QString &)
static Type kCallbackType
Definition: mythevent.h:135
void ExitStandby(bool manual=true)
void BindKey(const QString &context, const QString &action, const QString &key)
virtual bool blockSignals(bool block)
virtual void ReleaseResources(void)
MythNotificationCenter * GetCurrentNotificationCenter()
MythGestureEvent * gesture(void) const
Complete the gesture event of the last completed stroke.
#define ACTION_HANDLEMEDIA
Definition: mythuiactions.h:21
void closeEvent(QCloseEvent *e) override
bool gestureEvent(MythGestureEvent *) override
Mouse click/movement handler, receives mouse gesture events from the QCoreApplication event loop.
static Type kPopDisableDrawingEventType
Definition: mythevent.h:72
static void HandleCallback(const QString &Debug, MythCallbackEvent::Callback Function, void *Opaque1, void *Opaque2)
Static convenience method to initiate a callback into the UI thread and wait for the result.
QString GetConfDir(void)
Definition: mythdirs.cpp:224
virtual void End()
Definition: mythpainter.h:51
#define ACTION_SELECT
Definition: mythuiactions.h:15
QVariant value(int i) const
Definition: mythdbcon.h:198
void BlockShutdown(void)
static MythDisplay * AcquireRelease(bool Acquire=true)
Definition: mythdisplay.cpp:62
virtual QString GetName(void)=0
void SetScalingFactors(float wmult, float hmult)
MythRect NormRect(const MythRect &rect)
void RegisterKey(const QString &context, const QString &action, const QString &description, const QString &key)
#define ACTION_8
Definition: mythuiactions.h:12
Interface between mythtv and lircd.
Definition: lirc.h:24
This class is used as a container for messages.
Definition: mythevent.h:16
MythMainWindowPrivate * d
void signalRemoteScreenShot(QString filename, int x, int y)
#define EARLY_SHOW_PLATFORM_NAME_CHECK
virtual void mediaEvent(MythMediaEvent *event)
Media/Device status event handler, received from MythMediaMonitor.
Definition: mythuitype.cpp:996
MythPainter * m_oldpainter
static void show(uint8_t *buf, int length)
Definition: ringbuffer.c:316
virtual MythScreenType * GetTopScreen(void) const
int NormalizeFontSize(int pointSize)
QObject * getTarget(QKeyEvent &key)
A C++ ripoff of the stroke library for MythTV.
static Type kEventType
Definition: lircevent.h:24
bool eventFilter(QObject *o, QEvent *e) override
static const uint16_t * d
static Type kLockInputDevicesEventType
Definition: mythevent.h:73
void DoDisableScreensaver(void)
#define ACTION_1
Definition: mythuiactions.h:5
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
QString GetSetting(const QString &key, const QString &defaultval="")
static MythRenderOpenGL * Create(const QString &Painter, QPaintDevice *Device=nullptr)
int GetStackCount(void)
MythMainWindowPrivate * d
QWidget * GetPaintWindow()
#define STANDBY_TIMEOUT
void ProcessQueue(void)
ProcessQueue will be called by the GUI event handler and will process all queued MythNotifications an...
void startListening()
Definition: AppleRemote.cpp:82
bool IsExitingToMain(void) const
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
MythRenderOpenGL * m_render
bool isRunning(void) const
Definition: mthread.cpp:274
bool isActive(void) const
Definition: mythdbcon.h:204
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:17
#define ACTION_7
Definition: mythuiactions.h:11
MythPainterWindowQt(MythMainWindow *win, MythMainWindowPrivate *priv)
#define ACTION_TVPOWERON
Definition: mythuiactions.h:25
void DoRestoreScreensaver(void)
static Type MythUserMessage
Definition: mythevent.h:67
bool Queue(const MythNotification &notification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
void BindJump(const QString &destination, const QString &key)
void doRemoteScreenShot(const QString &filename, int x, int y)
QVector< MythScreenStack * > m_stackList
void UpdateImageCache(void)
unsigned int uint
Definition: compat.h:140
#define ACTION_GETSTATUS
Definition: mythuiactions.h:27
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:534
void JumpTo(const QString &destination, bool pop=true)
MythMediaDevice * m_mediaDeviceForCallback
void DisableIdleTimer(bool disableIdle=true)
void setWidth(const QString &sWidth)
Definition: mythrect.cpp:222
A custom event that represents a mouse gesture.
Definition: mythgesture.h:39
MythPainter * GetCurrentPainter()
bool GetMapping(int key, QStringList &actions)
void RemoteScreenShot(QString filename, int x, int y)
int GetDrawInterval() const
#define ACTION_TVPOWEROFF
Definition: mythuiactions.h:24
void AllowShutdown(void)
QPaintEngine * paintEngine() const override
void AddMapping(int key, const QString &action)
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:101
void RegisterMediaPlugin(const QString &name, const QString &desc, MediaPlayCallback fn)
MythUIHelper * GetMythUI()
bool event(QEvent *e) override
QMap< QString, MPData > m_mediaPluginMap
void start(void)
Start recording.
MythRender * GetRenderDevice()
bool HasMythMainWindow(void)
void dispatch(const MythEvent &event)
static QString GetKey(const QString &context, const QString &action)
MythMainWindow * GetMythMainWindow(void)
void(* m_exitMenuCallback)(void)
bool Init(QString &config_file)
Initialise the class variables with values from the config file.
Definition: jsmenu.cpp:72
std::vector< QWidget * > m_widgetList
void Reload(void)
static Type kEventType
Definition: mythmedia.h:192
static Type kEnableUDPListenerEventType
Definition: mythevent.h:77
virtual void stop(void)
bool usebookmark
Definition: mythburn.py:150
void PauseIdleTimer(bool pause)
void SetUIScreenRect(QRect &rect)
static Type kDisableUDPListenerEventType
Definition: mythevent.h:76
static MythMainWindow * mainWin
int GetNumSetting(const QString &key, int defaultval=0)
static void destroyMainWindow()
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:806
static Type kEventType
Definition: jsmenuevent.h:27
void LockInputDevices(bool locked)
bool keyPressEvent(QKeyEvent *) override
Key event handler.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static Type kExitToMainMenuEventType
Definition: mythevent.h:69
void(*)(void *, void *, void *) Callback
Definition: mythevent.h:125
static QMutex mainLock
void SetGUIObject(QObject *gui)
void stopListening()
QStringList EnumerateDestinations(void) const
bool GetBoolSetting(const QString &key, bool defaultval=false)
static Type kEventType
Definition: mythgesture.h:121
bool DestinationExists(const QString &destination) const
void SetDrawEnabled(bool enable)
This class is essentially a workaround for a Qt 4.5.2 bug where it will get stuck in the Qt event loo...
#define ACTION_4
Definition: mythuiactions.h:8
QHash< QString, QHash< QString, QString > > m_actionText
#define ACTION_9
Definition: mythuiactions.h:13
QMap< QString, JumpData > m_destinationMap
void EnterStandby(bool manual=true)
MythPainterWindowD3D9(MythMainWindow *win, MythMainWindowPrivate *priv)
void moveTopLeft(const MythPoint &point)
Definition: mythrect.cpp:252
void SetEffectsEnabled(bool enable)
"yyyy-MM-ddThh-mm-ss.zzz"
Definition: mythdate.h:26
MythThemeBase * m_themeBase
void draw(MythPainter *painter=nullptr)
void setListener(Listener *listener)
Definition: AppleRemote.cpp:77
QHash< QString, KeyContext * > m_keyContexts
MythDB * GetDB(void)
static void GrabWindow(QImage &image)
int NormY(const int y)
#define LONGPRESS_INTERVAL
void paintEvent(QPaintEvent *e) override
static void ResetScreensaver(void)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:602
friend class MythPainterWindowGL
#define AUTO_PAINTER
Definition: mythuidefines.h:5
MythDB * GetMythDB(void)
Definition: mythdb.cpp:46
#define ACTION_LEFT
Definition: mythuiactions.h:18
QPoint NormPoint(const QPoint &point)
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:179
bool HandleMedia(const QString &handler, const QString &mrl, const QString &plot="", const QString &title="", const QString &subtitle="", const QString &director="", int season=0, int episode=0, const QString &inetref="", int lenMins=120, const QString &year="1895", const QString &id="", bool useBookmarks=false)
Screen in which all other widgets are contained and rendered.
#define ACTION_5
Definition: mythuiactions.h:9
Contains the points in a stroke, and translates them into gestures.
Definition: mythgesture.h:144
bool keyLongPressFilter(QEvent **e, QScopedPointer< QEvent > &sNewEvent)
#define ACTION_3
Definition: mythuiactions.h:7
void ClearKey(const QString &context, const QString &action)
bool ContainsPoint(const QPoint &point) const
Check if the given point falls within this widgets area.
void AddScreenStack(MythScreenStack *stack, bool main=false)
void SetButton(Button button)
Definition: mythgesture.h:118
static bool IsGeometryOverridden(void)
MythMainWindow(const bool useDB=true)
friend class MythPainterWindowQt
Main object for injecting key strokes based on joystick movements.
Definition: jsmenu.h:81
#define GESTURE_TIMEOUT
friend class MythPainterWindowD3D9
QString m_localAction
virtual void Begin(QPaintDevice *parent)
Definition: mythpainter.h:50
QString m_description
static Type kUnlockInputDevicesEventType
Definition: mythevent.h:74
static Type kMythPostShowEventType
Definition: mythevent.h:70
void Stop(void)
Definition: jsmenu.h:93
void RegisterJump(const QString &destination, const QString &description, const QString &key, void(*callback)(void), bool exittomain=true, QString localAction="")
void ReinitDone(void)
void ReloadKeys(void)
virtual void SetClipRect(const QRect &clipRect)
Definition: mythpainter.cpp:48
void StartLIRC(void)
void SetWOLAllowed(bool allow)
#define ACTION_DOWN
Definition: mythuiactions.h:17
void ShowMouseCursor(bool show)
virtual void deleteLater(void)
Definition: lirc.cpp:99
static void SetState(QVariantMap &newstate)
void setWidget(QWidget *Widget)
void DelayedAction(void)
void GetScreenSettings(float &wmult, float &hmult)
MythMediaStatus getStatus() const
Definition: mythmedia.h:70
MythSignalingTimer * m_drawTimer
int(*)(const QString &, const QString &, const QString &, const QString &, const QString &, int, int, const QString &, int, const QString &, const QString &, bool) MediaPlayCallback
MythNotificationCenter * GetNotificationCenter(void)
bool IsRecommendedRenderer(void)
virtual ~MythMainWindow()