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