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