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