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