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 <chrono>
10#include <utility>
11#include <vector>
12
13// QT
14#include <QtGlobal>
15#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
16#include <QtSystemDetection>
17#include <QtVersionChecks>
18#endif
19#include <QWaitCondition>
20#include <QApplication>
21#include <QHash>
22#include <QFile>
23#include <QDir>
24#include <QEvent>
25#include <QKeyEvent>
26#include <QKeySequence>
27#include <QInputMethodEvent>
28#include <QSize>
29#include <QWindow>
30
31// Platform headers
32#include "unistd.h"
33
34// libmythbase headers
35#include "libmythbase/compat.h"
38#include "libmythbase/mythdb.h"
44
45// libmythui headers
46#include "myththemebase.h"
47#include "mythudplistener.h"
48#include "mythrender_base.h"
49#include "mythuistatetracker.h"
50#include "mythuiactions.h"
51#include "mythrect.h"
52#include "mythdisplay.h"
53#include "mythscreentype.h"
54#include "mythpainter.h"
55#include "mythpainterwindow.h"
56#include "mythgesture.h"
57#include "mythuihelper.h"
58#include "mythdialogbox.h"
59#include "mythscreensaver.h"
61
62
63#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
64#ifdef Q_OS_ANDROID
65#include <QtAndroid>
66#endif
67#endif
68
69static constexpr std::chrono::milliseconds GESTURE_TIMEOUT { 1s };
70static constexpr std::chrono::minutes STANDBY_TIMEOUT { 90min };
71static constexpr std::chrono::milliseconds LONGPRESS_INTERVAL { 1s };
72
73#define LOC QString("MythMainWindow: ")
74
75static MythMainWindow *s_mainWin = nullptr;
76static QMutex s_mainLock;
77
86{
87 if (s_mainWin)
88 return s_mainWin;
89
90 QMutexLocker lock(&s_mainLock);
91
92 if (!s_mainWin)
93 {
94 s_mainWin = new MythMainWindow(UseDB);
96 }
97
98 return s_mainWin;
99}
100
102{
103 if (gCoreContext)
104 gCoreContext->SetGUIObject(nullptr);
105 delete s_mainWin;
106 s_mainWin = nullptr;
107}
108
110{
112}
113
115{
116 return s_mainWin != nullptr;
117}
118
120{
122}
123
125{
127}
128
130{
132 return nullptr;
134}
135
137 : m_display(MythDisplay::Create(this))
138{
139 // Switch to desired GUI resolution
141 m_display->SwitchToGUI(true);
142
145
147
148 setObjectName("mainwindow");
149
150 m_priv->m_allowInput = false;
151
152 // This prevents database errors from RegisterKey() when there is no DB:
153 m_priv->m_useDB = UseDB;
154
155 //Init();
156
157 m_priv->m_exitingtomain = false;
158 m_priv->m_popwindows = true;
159 m_priv->m_exitMenuCallback = nullptr;
162 m_priv->m_escapekey = Qt::Key_Escape;
163 m_priv->m_mainStack = nullptr;
164
165 installEventFilter(this);
166
168
169 InitKeys();
170
171 m_priv->m_gestureTimer = new QTimer(this);
173 m_priv->m_hideMouseTimer = new QTimer(this);
174 m_priv->m_hideMouseTimer->setSingleShot(true);
175 m_priv->m_hideMouseTimer->setInterval(3s);
177 m_priv->m_allowInput = true;
179 m_refreshTimer.setInterval(17ms);
180 m_refreshTimer.start();
181 setUpdatesEnabled(true);
182
184 Qt::BlockingQueuedConnection);
186 Qt::BlockingQueuedConnection);
187#ifdef Q_OS_ANDROID
188 connect(qApp, &QApplication::applicationStateChanged, this, &MythMainWindow::OnApplicationStateChange);
189#endif
190
191 // We need to listen for playback start/end events
193
194 // Idle timer setup
195 m_idleTime =
196 gCoreContext->GetDurSetting<std::chrono::minutes>("FrontendIdleTimeout",
198 if (m_idleTime < 0min)
199 m_idleTime = 0min;
200 m_idleTimer.setInterval(m_idleTime);
202 if (m_idleTime > 0min)
203 m_idleTimer.start();
204
206 if (m_screensaver)
207 {
211 }
212}
213
215{
216 delete m_screensaver;
217
218 if (gCoreContext != nullptr)
220
222
223 while (!m_priv->m_stackList.isEmpty())
224 {
225 MythScreenStack *stack = m_priv->m_stackList.back();
226 m_priv->m_stackList.pop_back();
227
228 if (stack == m_priv->m_mainStack)
229 m_priv->m_mainStack = nullptr;
230
231 delete stack;
232 }
233
234 delete m_themeBase;
235
236 for (auto iter = m_priv->m_keyContexts.begin();
237 iter != m_priv->m_keyContexts.end();
238 iter = m_priv->m_keyContexts.erase(iter))
239 {
240 KeyContext *context = *iter;
241 delete context;
242 }
243
244 delete m_deviceHandler;
245 delete m_priv->m_nc;
246
248
249 // N.B. we always call this to ensure the desktop mode is restored
250 // in case the setting was changed
252 delete m_display;
253
254 delete m_priv;
255}
256
258{
259 return m_display;
260}
261
263{
264 return m_painter;
265}
266
268{
269 return m_priv->m_nc;
270}
271
273{
274 return m_painterWin;
275}
276
278{
279 if (m_painterWin)
280 {
281 m_painterWin->show();
282 m_painterWin->raise();
283 }
284}
285
287{
288 if (m_painterWin)
289 {
290 m_painterWin->clearMask();
292 m_painterWin->hide();
293 }
294}
295
297{
299}
300
302{
303 m_priv->m_stackList.push_back(Stack);
304 if (Main)
305 m_priv->m_mainStack = Stack;
306}
307
309{
310 MythScreenStack *stack = m_priv->m_stackList.back();
311 m_priv->m_stackList.pop_back();
312 if (stack == m_priv->m_mainStack)
313 m_priv->m_mainStack = nullptr;
314 delete stack;
315}
316
318{
319 return m_priv->m_stackList.size();
320}
321
323{
324 return m_priv->m_mainStack;
325}
326
328{
329 for (auto * widget : std::as_const(m_priv->m_stackList))
330 if (widget->objectName() == Stackname)
331 return widget;
332 return nullptr;
333}
334
336{
337 if (Position >= 0 && Position < m_priv->m_stackList.size())
338 return m_priv->m_stackList.at(Position);
339 return nullptr;
340}
341
343{
344 if (!(m_painterWin && updatesEnabled()))
345 return;
346
347 bool redraw = false;
348
349 if (!m_repaintRegion.isEmpty())
350 redraw = true;
351
352 // The call to GetDrawOrder can apparently alter m_stackList.
353 // NOLINTNEXTLINE(modernize-loop-convert,readability-qualified-auto) // both, qt6
354 for (auto it = m_priv->m_stackList.begin(); it != m_priv->m_stackList.end(); ++it)
355 {
356 QVector<MythScreenType *> drawList;
357 (*it)->GetDrawOrder(drawList);
358
359 for (auto *screen : std::as_const(drawList))
360 {
361 screen->Pulse();
362
363 if (screen->NeedsRedraw())
364 {
365 QRegion topDirty = screen->GetDirtyArea();
366 screen->ResetNeedsRedraw();
367 m_repaintRegion = m_repaintRegion.united(topDirty);
368 redraw = true;
369 }
370 }
371 }
372
373 if (redraw && !m_painterWin->RenderIsShared())
375
376 for (auto *widget : std::as_const(m_priv->m_stackList))
377 widget->ScheduleInitIfNeeded();
378}
379
381{
382 if (!(m_painterWin && updatesEnabled()))
383 return;
384
385 if (Event)
386 m_repaintRegion = m_repaintRegion.united(Event->region());
387
389 {
391 }
392 else
393 {
394 // Ensure that the region is not larger than the screen which
395 // can happen with bad themes
397
398 // Check for any widgets that have been updated since we built
399 // the dirty region list in ::animate()
400 // The call to GetDrawOrder can apparently alter m_stackList.
401 // NOLINTNEXTLINE(modernize-loop-convert,readability-qualified-auto) // both, qt6
402 for (auto it = m_priv->m_stackList.begin(); it != m_priv->m_stackList.end(); ++it)
403 {
404 QVector<MythScreenType *> redrawList;
405 (*it)->GetDrawOrder(redrawList);
406
407 for (const auto *screen : std::as_const(redrawList))
408 {
409 if (screen->NeedsRedraw())
410 {
411 for (const QRect& wrect: screen->GetDirtyArea())
412 {
413 bool foundThisRect = false;
414 for (const QRect& drect: m_repaintRegion)
415 {
416 // Can't use QRegion::contains because it only
417 // checks for overlap. QRect::contains checks
418 // if fully contained.
419 if (drect.contains(wrect))
420 {
421 foundThisRect = true;
422 break;
423 }
424 }
425
426 if (!foundThisRect)
427 return;
428 }
429 }
430 }
431 }
432 }
433
435 Draw();
436
437 m_repaintRegion = QRegion();
438}
439
441{
442 if (!Painter)
443 Painter = m_painter;
444 if (!Painter)
445 return;
446
447 Painter->Begin(m_painterWin);
448
449 if (!Painter->SupportsClipping())
451
452 for (const QRect& rect : m_repaintRegion)
453 {
454 if (rect.width() == 0 || rect.height() == 0)
455 continue;
456
457 if (rect != m_uiScreenRect)
458 Painter->SetClipRect(rect);
459
460 // The call to GetDrawOrder can apparently alter m_stackList.
461 // NOLINTNEXTLINE(modernize-loop-convert,readability-qualified-auto) // both, qt6
462 for (auto it = m_priv->m_stackList.begin(); it != m_priv->m_stackList.end(); ++it)
463 {
464 QVector<MythScreenType *> redrawList;
465 (*it)->GetDrawOrder(redrawList);
466 for (auto *screen : std::as_const(redrawList))
467 screen->Draw(Painter, 0, 0, 255, rect);
468 }
469 }
470
471 Painter->End();
472 m_repaintRegion = QRegion();
473}
474
475// virtual
476QPaintEngine *MythMainWindow::paintEngine() const
477{
478 return testAttribute(Qt::WA_PaintOnScreen) ? nullptr : QWidget::paintEngine();
479}
480
482{
483 if (Event->spontaneous())
484 {
485 auto * key = new QKeyEvent(QEvent::KeyPress, m_priv->m_escapekey, Qt::NoModifier);
486 QCoreApplication::postEvent(this, key);
487 Event->ignore();
488 return;
489 }
490
491 QWidget::closeEvent(Event);
492}
493
494void MythMainWindow::GrabWindow(QImage& Image)
495{
496 WId winid = 0;
497 auto * active = QApplication::activeWindow();
498 if (active)
499 {
500 winid = active->winId();
501 }
502 else
503 {
504 // According to the following we page, you "just pass 0 as the
505 // window id, indicating that we want to grab the entire screen".
506 //
507 // https://doc.qt.io/qt-5/qtwidgets-desktop-screenshot-example.html#screenshot-class-implementation
508 winid = 0;
509 }
510
511 auto * display = GetMythMainWindow()->GetDisplay();
512 if (auto * screen = display->GetCurrentScreen(); screen)
513 {
514 QPixmap image = screen->grabWindow(winid);
515 Image = image.toImage();
516 }
517}
518
519/* This is required to allow a screenshot to be requested by another thread
520 * other than the UI thread, and to wait for the screenshot before returning.
521 * It is used by mythweb for the remote access screenshots
522 */
523void MythMainWindow::DoRemoteScreenShot(const QString& Filename, int Width, int Height)
524{
525 // This will be running in the UI thread, as is required by QPixmap
526 QStringList args;
527 args << QString::number(Width);
528 args << QString::number(Height);
529 args << Filename;
531 QCoreApplication::sendEvent(this, &me);
532}
533
534void MythMainWindow::RemoteScreenShot(QString Filename, int Width, int Height)
535{
536 // This will be running in a non-UI thread and is used to trigger a
537 // function in the UI thread, and waits for completion of that handler
538 emit SignalRemoteScreenShot(std::move(Filename), Width, Height);
539}
540
541bool MythMainWindow::SaveScreenShot(const QImage& Image, QString Filename)
542{
543 if (Filename.isEmpty())
544 {
545 QString fpath = GetMythDB()->GetSetting("ScreenShotPath", "/tmp");
546 Filename = QString("%1/myth-screenshot-%2.png")
548 }
549
550 QString extension = Filename.section('.', -1, -1);
551 if (extension == "jpg")
552 extension = "JPEG";
553 else
554 extension = "PNG";
555
556 LOG(VB_GENERAL, LOG_INFO, QString("Saving screenshot to %1 (%2x%3)")
557 .arg(Filename).arg(Image.width()).arg(Image.height()));
558
559 if (Image.save(Filename, extension.toLatin1().constData(), 100))
560 {
561 LOG(VB_GENERAL, LOG_INFO, "MythMainWindow::screenShot succeeded");
562 return true;
563 }
564
565 LOG(VB_GENERAL, LOG_INFO, "MythMainWindow::screenShot Failed!");
566 return false;
567}
568
569bool MythMainWindow::ScreenShot(int Width, int Height, QString Filename)
570{
571 QImage img;
572 GrabWindow(img);
573 if (Width <= 0)
574 Width = img.width();
575 if (Height <= 0)
576 Height = img.height();
577 img = img.scaled(Width, Height, Qt::KeepAspectRatio, Qt::SmoothTransformation);
578 return SaveScreenShot(img, std::move(Filename));
579}
580
582{
583 if (HasMythMainWindow())
585}
586
588{
589 if (HasMythMainWindow())
591}
592
594{
595 if (HasMythMainWindow())
597}
598
600{
601 if (HasMythMainWindow())
602 {
604 if (window->m_screensaver)
605 return window->m_screensaver->Asleep();
606 }
607 return false;
608}
609
611{
612 if (HasMythMainWindow())
614 return false;
615}
616
618{
619 if (!updatesEnabled() && (Event->type() == QEvent::UpdateRequest))
620 m_priv->m_pendingUpdate = true;
621
622 if (Event->type() == QEvent::Show && !Event->spontaneous())
623 QCoreApplication::postEvent(this, new QEvent(MythEvent::kMythPostShowEventType));
624
626 {
627 raise();
628 activateWindow();
629 return true;
630 }
631
632 if ((Event->type() == QEvent::WindowActivate) || (Event->type() == QEvent::WindowDeactivate))
634
635 return QWidget::event(Event);
636}
637
639{
643 QApplication::setStyle("Windows");
644}
645
646void MythMainWindow::Init(bool MayReInit)
647{
648 LoadQtConfig();
649 m_display->SetWidget(nullptr);
650 m_priv->m_useDB = ! gCoreContext->GetDB()->SuppressDBMessages();
651
652 if (!(MayReInit || m_priv->m_firstinit))
653 return;
654
655 // Set window border based on fullscreen attribute
656 Qt::WindowFlags flags = Qt::Window;
657
659 bool inwindow = m_wantWindow && !m_qtFullScreen;
660 bool fullscreen = m_wantFullScreen && !GeometryIsOverridden();
661
662 // On Compiz/Unit, when the window is fullscreen and frameless changing
663 // screen position ends up stuck. Adding a border temporarily prevents this
664 setWindowFlags(windowFlags() & ~Qt::FramelessWindowHint);
665
666 if (!inwindow)
667 {
668 LOG(VB_GENERAL, LOG_INFO, "Using Frameless Window");
669 flags |= Qt::FramelessWindowHint;
670 }
671
672 // Workaround Qt/Windows playback bug?
673#ifdef Q_OS_WINDOWS
674 flags |= Qt::MSWindowsOwnDC;
675#endif
676
677 // NOTE if running fullscreen AND windowed (i.e. borders etc) then we do not
678 // have any idea at this time of the size of the borders/decorations.
679 // Typically, on linux, this means we create the UI slightly larger than
680 // required - as X adds the decorations at a later point.
681
682 if (fullscreen && !inwindow)
683 {
684 LOG(VB_GENERAL, LOG_INFO, "Using Full Screen Window");
685 if (m_priv->m_firstinit)
686 {
687 // During initialization, we force being fullscreen using setWindowState
688 // otherwise, in ubuntu's unity, the side bar often stays visible
689 setWindowState(Qt::WindowFullScreen);
690 }
691 }
692 else
693 {
694 // reset type
695 setWindowState(Qt::WindowNoState);
696 }
697
699 flags |= Qt::WindowStaysOnTopHint;
700
701 setWindowFlags(flags);
702
703 // SetWidget may move the widget into a new screen.
704 m_display->SetWidget(this);
705 QTimer::singleShot(1s, this, &MythMainWindow::DelayedAction);
706
707 // Ensure we have latest screen bounds if we have moved
709 SetUIScreenRect({{0, 0}, m_screenRect.size()});
711 Show();
712
713 // The window is sometimes not created until Show has been called - so try
714 // MythDisplay::setWidget again to ensure we listen for QScreen changes
715 m_display->SetWidget(this);
716
717 if (!GetMythDB()->GetBoolSetting("HideMouseCursor", false))
718 setMouseTracking(true); // Required for mouse cursor auto-hide
719 // Set cursor call must come after Show() to work on some systems.
720 ShowMouseCursor(false);
721
722 move(m_screenRect.topLeft());
723
724 if (m_painterWin || m_painter)
725 {
726 LOG(VB_GENERAL, LOG_INFO, "Destroying painter and painter window");
728 }
729
730 QString warningmsg = MythPainterWindow::CreatePainters(this, m_painterWin, m_painter);
731 if (!warningmsg.isEmpty())
732 {
733 LOG(VB_GENERAL, LOG_WARNING, warningmsg);
734 }
735
736 if (!m_painterWin)
737 {
738 LOG(VB_GENERAL, LOG_ERR, "MythMainWindow failed to create a painter window.");
739 return;
740 }
741
742 if (m_painter && m_painter->GetName() != "Qt")
743 {
744 setAttribute(Qt::WA_NoSystemBackground);
745 setAutoFillBackground(false);
746 }
747 setAttribute(Qt::WA_InputMethodEnabled);
748
751
752 // Redraw the window now to avoid race conditions in EGLFS (Qt5.4) if a
753 // 2nd window (e.g. TVPlayback) is created before this is redrawn.
754#ifdef Q_OS_ANDROID
755 static const QLatin1String EARLY_SHOW_PLATFORM_NAME_CHECK { "android" };
756#else
757 static const QLatin1String EARLY_SHOW_PLATFORM_NAME_CHECK { "egl" };
758#endif
759 if (QGuiApplication::platformName().contains(EARLY_SHOW_PLATFORM_NAME_CHECK))
760 QCoreApplication::processEvents();
761
762 if (!GetMythDB()->GetBoolSetting("HideMouseCursor", false))
763 m_painterWin->setMouseTracking(true); // Required for mouse cursor auto-hide
764
766 if (m_themeBase)
768 else
769 m_themeBase = new MythThemeBase(this);
770
771 if (!m_priv->m_nc)
773
774 emit SignalWindowReady();
775
776 if (!warningmsg.isEmpty())
777 {
778 MythNotification notification(warningmsg, "");
779 m_priv->m_nc->Queue(notification);
780 }
781}
782
784{
786 Show();
787
788#ifdef Q_OS_ANDROID
789#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
790 QtAndroid::hideSplashScreen();
791#else
792 QNativeInterface::QAndroidApplication::hideSplashScreen();
793#endif
794#endif
795}
796
798{
799 RegisterKey("Global", ACTION_UP, QT_TRANSLATE_NOOP("MythControls",
800 "Up Arrow"), "Up");
801 RegisterKey("Global", ACTION_DOWN, QT_TRANSLATE_NOOP("MythControls",
802 "Down Arrow"), "Down");
803 RegisterKey("Global", ACTION_LEFT, QT_TRANSLATE_NOOP("MythControls",
804 "Left Arrow"), "Left");
805 RegisterKey("Global", ACTION_RIGHT, QT_TRANSLATE_NOOP("MythControls",
806 "Right Arrow"), "Right");
807 RegisterKey("Global", "NEXT", QT_TRANSLATE_NOOP("MythControls",
808 "Move to next widget"), "Tab");
809 RegisterKey("Global", "PREVIOUS", QT_TRANSLATE_NOOP("MythControls",
810 "Move to preview widget"), "Backtab");
811 RegisterKey("Global", ACTION_SELECT, QT_TRANSLATE_NOOP("MythControls",
812 "Select"), "Return,Enter,Space");
813 RegisterKey("Global", "BACKSPACE", QT_TRANSLATE_NOOP("MythControls",
814 "Backspace"), "Backspace");
815 RegisterKey("Global", "ESCAPE", QT_TRANSLATE_NOOP("MythControls",
816 "Escape"), "Esc,Back");
817 RegisterKey("Global", "MENU", QT_TRANSLATE_NOOP("MythControls",
818 "Pop-up menu"), "M,Meta+Enter,Ctrl+M,Menu");
819 RegisterKey("Global", "INFO", QT_TRANSLATE_NOOP("MythControls",
820 "More information"), "I,Ctrl+I,Home Page");
821 RegisterKey("Global", "DELETE", QT_TRANSLATE_NOOP("MythControls",
822 "Delete"), "D,Ctrl+E");
823 RegisterKey("Global", "EDIT", QT_TRANSLATE_NOOP("MythControls",
824 "Edit"), "E");
825 RegisterKey("Global", ACTION_SCREENSHOT, QT_TRANSLATE_NOOP("MythControls",
826 "Save screenshot"), "");
827 RegisterKey("Global", ACTION_HANDLEMEDIA, QT_TRANSLATE_NOOP("MythControls",
828 "Play a media resource"), "");
829
830 RegisterKey("Global", "PAGEUP", QT_TRANSLATE_NOOP("MythControls",
831 "Page Up"), "PgUp");
832 RegisterKey("Global", "PAGEDOWN", QT_TRANSLATE_NOOP("MythControls",
833 "Page Down"), "PgDown");
834 RegisterKey("Global", "PAGETOP", QT_TRANSLATE_NOOP("MythControls",
835 "Page to top of list"), "");
836 RegisterKey("Global", "PAGEMIDDLE", QT_TRANSLATE_NOOP("MythControls",
837 "Page to middle of list"), "");
838 RegisterKey("Global", "PAGEBOTTOM", QT_TRANSLATE_NOOP("MythControls",
839 "Page to bottom of list"), "");
840
841 RegisterKey("Global", "PREVVIEW", QT_TRANSLATE_NOOP("MythControls",
842 "Previous View"), "Home,Media Previous");
843 RegisterKey("Global", "NEXTVIEW", QT_TRANSLATE_NOOP("MythControls",
844 "Next View"), "End,Media Next");
845
846 RegisterKey("Global", "HELP", QT_TRANSLATE_NOOP("MythControls",
847 "Help"), "F1");
848 RegisterKey("Global", "EJECT", QT_TRANSLATE_NOOP("MythControls"
849 ,"Eject Removable Media"), "");
850
851 RegisterKey("Global", "CUT", QT_TRANSLATE_NOOP("MythControls",
852 "Cut text from textedit"), "Ctrl+X");
853 RegisterKey("Global", "COPY", QT_TRANSLATE_NOOP("MythControls"
854 ,"Copy text from textedit"), "Ctrl+C");
855 RegisterKey("Global", "PASTE", QT_TRANSLATE_NOOP("MythControls",
856 "Paste text into textedit"), "Ctrl+V");
857 RegisterKey("Global", "NEWLINE", QT_TRANSLATE_NOOP("MythControls",
858 "Insert newline into textedit"), "Ctrl+Return");
859 RegisterKey("Global", "UNDO", QT_TRANSLATE_NOOP("MythControls",
860 "Undo"), "Ctrl+Z");
861 RegisterKey("Global", "REDO", QT_TRANSLATE_NOOP("MythControls",
862 "Redo"), "Ctrl+Y");
863 RegisterKey("Global", "SEARCH", QT_TRANSLATE_NOOP("MythControls",
864 "Show incremental search dialog"), "Ctrl+S,Search");
865
866 RegisterKey("Global", ACTION_0, QT_TRANSLATE_NOOP("MythControls","0"), "0");
867 RegisterKey("Global", ACTION_1, QT_TRANSLATE_NOOP("MythControls","1"), "1");
868 RegisterKey("Global", ACTION_2, QT_TRANSLATE_NOOP("MythControls","2"), "2");
869 RegisterKey("Global", ACTION_3, QT_TRANSLATE_NOOP("MythControls","3"), "3");
870 RegisterKey("Global", ACTION_4, QT_TRANSLATE_NOOP("MythControls","4"), "4");
871 RegisterKey("Global", ACTION_5, QT_TRANSLATE_NOOP("MythControls","5"), "5");
872 RegisterKey("Global", ACTION_6, QT_TRANSLATE_NOOP("MythControls","6"), "6");
873 RegisterKey("Global", ACTION_7, QT_TRANSLATE_NOOP("MythControls","7"), "7");
874 RegisterKey("Global", ACTION_8, QT_TRANSLATE_NOOP("MythControls","8"), "8");
875 RegisterKey("Global", ACTION_9, QT_TRANSLATE_NOOP("MythControls","9"), "9");
876
877 RegisterKey("Global", ACTION_TVPOWERON, QT_TRANSLATE_NOOP("MythControls",
878 "Turn the display on"), "");
879 RegisterKey("Global", ACTION_TVPOWEROFF, QT_TRANSLATE_NOOP("MythControls",
880 "Turn the display off"), "");
881
882 RegisterKey("Global", "SYSEVENT01", QT_TRANSLATE_NOOP("MythControls",
883 "Trigger System Key Event #1"), "");
884 RegisterKey("Global", "SYSEVENT02", QT_TRANSLATE_NOOP("MythControls",
885 "Trigger System Key Event #2"), "");
886 RegisterKey("Global", "SYSEVENT03", QT_TRANSLATE_NOOP("MythControls",
887 "Trigger System Key Event #3"), "");
888 RegisterKey("Global", "SYSEVENT04", QT_TRANSLATE_NOOP("MythControls",
889 "Trigger System Key Event #4"), "");
890 RegisterKey("Global", "SYSEVENT05", QT_TRANSLATE_NOOP("MythControls",
891 "Trigger System Key Event #5"), "");
892 RegisterKey("Global", "SYSEVENT06", QT_TRANSLATE_NOOP("MythControls",
893 "Trigger System Key Event #6"), "");
894 RegisterKey("Global", "SYSEVENT07", QT_TRANSLATE_NOOP("MythControls",
895 "Trigger System Key Event #7"), "");
896 RegisterKey("Global", "SYSEVENT08", QT_TRANSLATE_NOOP("MythControls",
897 "Trigger System Key Event #8"), "");
898 RegisterKey("Global", "SYSEVENT09", QT_TRANSLATE_NOOP("MythControls",
899 "Trigger System Key Event #9"), "");
900 RegisterKey("Global", "SYSEVENT10", QT_TRANSLATE_NOOP("MythControls",
901 "Trigger System Key Event #10"), "");
902
903 // these are for the html viewer widget (MythUIWebBrowser)
904 RegisterKey("Browser", "ZOOMIN", QT_TRANSLATE_NOOP("MythControls",
905 "Zoom in on browser window"), ".,>,Ctrl+F,Media Fast Forward");
906 RegisterKey("Browser", "ZOOMOUT", QT_TRANSLATE_NOOP("MythControls",
907 "Zoom out on browser window"), ",,<,Ctrl+B,Media Rewind");
908 RegisterKey("Browser", "TOGGLEINPUT", QT_TRANSLATE_NOOP("MythControls",
909 "Toggle where keyboard input goes to"), "F1");
910 RegisterKey("Browser", "RELOAD" , QT_TRANSLATE_NOOP("MythControls",
911 "Reload the current webpage"), "F2");
912 RegisterKey("Browser", "FULLRELOAD", QT_TRANSLATE_NOOP("MythControls",
913 "Reload the current webpage bypassing the cache"), "F3");
914
915 RegisterKey("Browser", "MOUSEUP", QT_TRANSLATE_NOOP("MythControls",
916 "Move mouse pointer up"), "2");
917 RegisterKey("Browser", "MOUSEDOWN", QT_TRANSLATE_NOOP("MythControls",
918 "Move mouse pointer down"), "8");
919 RegisterKey("Browser", "MOUSELEFT", QT_TRANSLATE_NOOP("MythControls",
920 "Move mouse pointer left"), "4");
921 RegisterKey("Browser", "MOUSERIGHT", QT_TRANSLATE_NOOP("MythControls",
922 "Move mouse pointer right"), "6");
923 RegisterKey("Browser", "MOUSELEFTBUTTON", QT_TRANSLATE_NOOP("MythControls",
924 "Mouse Left button click"), "5");
925
926 RegisterKey("Browser", "PAGEDOWN", QT_TRANSLATE_NOOP("MythControls",
927 "Scroll down half a page"), "9");
928 RegisterKey("Browser", "PAGEUP", QT_TRANSLATE_NOOP("MythControls",
929 "Scroll up half a page"), "3");
930 RegisterKey("Browser", "PAGELEFT", QT_TRANSLATE_NOOP("MythControls",
931 "Scroll left half a page"), "7");
932 RegisterKey("Browser", "PAGERIGHT", QT_TRANSLATE_NOOP("MythControls",
933 "Scroll right half a page"), "1");
934
935 RegisterKey("Browser", "NEXTLINK", QT_TRANSLATE_NOOP("MythControls",
936 "Move selection to next link"), "Z");
937 RegisterKey("Browser", "PREVIOUSLINK", QT_TRANSLATE_NOOP("MythControls",
938 "Move selection to previous link"), "Q");
939 RegisterKey("Browser", "FOLLOWLINK", QT_TRANSLATE_NOOP("MythControls",
940 "Follow selected link"), "Return,Space,Enter");
941 RegisterKey("Browser", "HISTORYBACK", QT_TRANSLATE_NOOP("MythControls",
942 "Go back to previous page"), "R,Backspace");
943 RegisterKey("Browser", "HISTORYFORWARD", QT_TRANSLATE_NOOP("MythControls",
944 "Go forward to previous page"), "F");
945
946 RegisterKey("Main Menu", "EXITPROMPT", QT_TRANSLATE_NOOP("MythControls",
947 "Display System Exit Prompt"), "Esc,Back");
948 RegisterKey("Main Menu", "EXIT", QT_TRANSLATE_NOOP("MythControls",
949 "System Exit"), "");
950 RegisterKey("Main Menu", "STANDBYMODE",QT_TRANSLATE_NOOP("MythControls",
951 "Enter Standby Mode"), "");
952 RegisterKey("Long Press", "LONGPRESS1",QT_TRANSLATE_NOOP("MythControls",
953 "Up to 16 Keys that allow Long Press"), "");
954 RegisterKey("Long Press", "LONGPRESS2",QT_TRANSLATE_NOOP("MythControls",
955 "Up to 16 Keys that allow Long Press"), "");
956 RegisterKey("Long Press", "LONGPRESS3",QT_TRANSLATE_NOOP("MythControls",
957 "Up to 16 Keys that allow Long Press"), "");
958 RegisterKey("Long Press", "LONGPRESS4",QT_TRANSLATE_NOOP("MythControls",
959 "Up to 16 Keys that allow Long Press"), "");
960}
961
963{
964 ClearKeyContext("Global");
965 ClearKeyContext("Browser");
966 ClearKeyContext("Main Menu");
967 InitKeys();
968}
969
971{
972 bool inwindow = m_wantWindow && !m_qtFullScreen;
973 bool fullscreen = m_wantFullScreen && !GeometryIsOverridden();
974 if (fullscreen && !inwindow && !m_priv->m_firstinit)
975 showFullScreen();
976 else
977 show();
978 m_priv->m_firstinit = false;
979}
980
981void MythMainWindow::MoveResize(QRect& Geometry)
982{
983 setFixedSize(Geometry.size());
984 setGeometry(Geometry);
985 if (m_painterWin)
986 {
987 m_painterWin->setFixedSize(Geometry.size());
988 m_painterWin->setGeometry(0, 0, Geometry.width(), Geometry.height());
989 }
990}
991
993{
994 QMutexLocker locker(&m_priv->m_drawDisableLock);
996 if (m_priv->m_drawDisabledDepth && updatesEnabled())
997 SetDrawEnabled(false);
999}
1000
1002{
1003 QMutexLocker locker(&m_priv->m_drawDisableLock);
1005 {
1007 if (!m_priv->m_drawDisabledDepth && !updatesEnabled())
1008 SetDrawEnabled(true);
1009 }
1011}
1012
1014{
1015 if (!gCoreContext->IsUIThread())
1016 {
1017 emit SignalSetDrawEnabled(Enable);
1018 return;
1019 }
1020
1021 setUpdatesEnabled(Enable);
1022 if (Enable)
1023 {
1025 {
1026 QApplication::postEvent(this, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
1027 m_priv->m_pendingUpdate = false;
1028 }
1030 }
1031 else
1032 {
1034 }
1035}
1036
1038{
1039 for (auto *widget : std::as_const(m_priv->m_stackList))
1040 {
1041 if (Enable)
1042 widget->EnableEffects();
1043 else
1044 widget->DisableEffects();
1045 }
1046}
1047
1049{
1050 return m_priv->m_exitingtomain;
1051}
1052
1054{
1055 bool jumpdone = !(m_priv->m_popwindows);
1056
1057 m_priv->m_exitingtomain = true;
1058
1059 MythScreenStack *toplevel = GetMainStack();
1060 if (toplevel && m_priv->m_popwindows)
1061 {
1062 MythScreenType *screen = toplevel->GetTopScreen();
1063 if (screen && screen->objectName() != QString("mainmenu"))
1064 {
1065 MythEvent xe("EXIT_TO_MENU");
1067 if (screen->objectName() == QString("video playback window"))
1068 {
1069 auto *me = new MythEvent("EXIT_TO_MENU");
1070 QCoreApplication::postEvent(screen, me);
1071 }
1072 else
1073 {
1074 auto *key = new QKeyEvent(QEvent::KeyPress, m_priv->m_escapekey,
1075 Qt::NoModifier);
1076 QCoreApplication::postEvent(this, key);
1078 // Notifications have their own stack. We need to continue
1079 // the propagation of the escape here if there are notifications.
1080 int num = nc->DisplayedNotifications();
1081 if (num > 0)
1082 QCoreApplication::postEvent(
1083 this, new QEvent(MythEvent::kExitToMainMenuEventType));
1084 }
1085 return;
1086 }
1087 jumpdone = true;
1088 }
1089
1090 if (jumpdone)
1091 {
1092 m_priv->m_exitingtomain = false;
1093 m_priv->m_popwindows = true;
1095 {
1096 void (*callback)(void) = m_priv->m_exitMenuCallback;
1097 m_priv->m_exitMenuCallback = nullptr;
1098 callback();
1099 }
1101 {
1105 callback(mediadevice);
1106 }
1107 }
1108}
1109
1120bool MythMainWindow::TranslateKeyPress(const QString& Context, QKeyEvent* Event,
1121 QStringList& Actions, bool AllowJumps)
1122{
1123 Actions.clear();
1124
1125 // Special case for custom QKeyEvent where the action is embedded directly
1126 // in the QKeyEvent text property. Used by MythFEXML http extension
1127 if (Event && Event->key() == 0 &&
1128 !Event->text().isEmpty() &&
1129 Event->modifiers() == Qt::NoModifier)
1130 {
1131 QString action = Event->text();
1132 // check if it is a jumppoint
1133 if (!m_priv->m_destinationMap.contains(action))
1134 {
1135 Actions.append(action);
1136 return false;
1137 }
1138
1139 if (AllowJumps)
1140 {
1141 // This does not filter the jump based on the current location but
1142 // is consistent with handling of other actions that do not need
1143 // a keybinding. The network control code tries to match
1144 // GetCurrentLocation with the jumppoint but matching is utterly
1145 // inconsistent e.g. mainmenu<->Main Menu, Playback<->Live TV
1146 JumpTo(action);
1147 return true;
1148 }
1149
1150 return false;
1151 }
1152
1154
1155 QStringList localActions;
1156 auto * keycontext = m_priv->m_keyContexts.value(Context);
1157 if (AllowJumps && (m_priv->m_jumpMap.contains(keynum)) &&
1158 (!m_priv->m_jumpMap[keynum]->m_localAction.isEmpty()) &&
1159 keycontext && (keycontext->GetMapping(keynum, localActions)))
1160 {
1161 if (localActions.contains(m_priv->m_jumpMap[keynum]->m_localAction))
1162 AllowJumps = false;
1163 }
1164
1165 if (AllowJumps && m_priv->m_jumpMap.contains(keynum) &&
1166 !m_priv->m_jumpMap[keynum]->m_exittomain && m_priv->m_exitMenuCallback == nullptr)
1167 {
1168 void (*callback)(void) = m_priv->m_jumpMap[keynum]->m_callback;
1169 callback();
1170 return true;
1171 }
1172
1173 if (AllowJumps &&
1174 m_priv->m_jumpMap.contains(keynum) && m_priv->m_exitMenuCallback == nullptr)
1175 {
1176 m_priv->m_exitingtomain = true;
1177 m_priv->m_exitMenuCallback = m_priv->m_jumpMap[keynum]->m_callback;
1178 QCoreApplication::postEvent(
1179 this, new QEvent(MythEvent::kExitToMainMenuEventType));
1180 return true;
1181 }
1182
1183 if (keycontext)
1184 keycontext->GetMapping(keynum, Actions);
1185
1186 if (Context != "Global")
1187 {
1188 auto * keycontextG = m_priv->m_keyContexts.value("Global");
1189 if (keycontextG)
1190 keycontextG->GetMapping(keynum, Actions);
1191 }
1192
1193 return false;
1194}
1195
1196void MythMainWindow::ClearKey(const QString& Context, const QString& Action)
1197{
1198 auto * keycontext = m_priv->m_keyContexts.value(Context);
1199 if (keycontext == nullptr)
1200 return;
1201
1202 for (auto it = keycontext->m_actionMap.begin();
1203 it != keycontext->m_actionMap.end();
1204 /* no inc */)
1205 {
1206 QStringList list = it.value();
1207 list.removeAll(Action);
1208 if (list.isEmpty())
1209 it = keycontext->m_actionMap.erase(it);
1210 else
1211 ++it;
1212 }
1213}
1214
1215void MythMainWindow::ClearKeyContext(const QString& Context)
1216{
1217 auto * keycontext = m_priv->m_keyContexts.value(Context);
1218 if (keycontext != nullptr)
1219 keycontext->m_actionMap.clear();
1220}
1221
1222void MythMainWindow::BindKey(const QString& Context, const QString& Action, const QString& Key)
1223{
1224 auto * keycontext = m_priv->m_keyContexts.value(Context);
1225 if (keycontext == nullptr)
1226 {
1227 keycontext = new KeyContext();
1228 if (keycontext == nullptr)
1229 return;
1230 m_priv->m_keyContexts.insert(Context, keycontext);
1231 }
1232
1233 QKeySequence keyseq(Key);
1234 for (unsigned int i = 0; i < static_cast<uint>(keyseq.count()); i++)
1235 {
1236#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1237 int keynum = keyseq[i];
1238#else
1239 int keynum = keyseq[i].toCombined();
1240#endif
1241
1242 QStringList dummyaction("");
1243 if (keycontext->GetMapping(keynum, dummyaction))
1244 {
1245 LOG(VB_GENERAL, LOG_WARNING, QString("Key %1 is bound to multiple actions in context %2.")
1246 .arg(Key, Context));
1247 }
1248
1249 keycontext->AddMapping(keynum, Action);
1250#if 0
1251 LOG(VB_GENERAL, LOG_DEBUG, QString("Binding: %1 to action: %2 (%3)")
1252 .arg(Key).arg(Action).arg(Context));
1253#endif
1254
1255 if (Action == "ESCAPE" && Context == "Global" && i == 0)
1256 m_priv->m_escapekey = keynum;
1257 }
1258}
1259
1260void MythMainWindow::RegisterKey(const QString& Context, const QString& Action,
1261 const QString& Description, const QString& Key)
1262{
1263 QString keybind = Key;
1264
1266
1267 if (m_priv->m_useDB && query.isConnected())
1268 {
1269 query.prepare("SELECT keylist, description FROM keybindings WHERE "
1270 "context = :CONTEXT AND action = :ACTION AND "
1271 "hostname = :HOSTNAME ;");
1272 query.bindValue(":CONTEXT", Context);
1273 query.bindValue(":ACTION", Action);
1274 query.bindValue(":HOSTNAME", GetMythDB()->GetHostName());
1275
1276 if (query.exec() && query.next())
1277 {
1278 keybind = query.value(0).toString();
1279 QString db_description = query.value(1).toString();
1280
1281 // Update keybinding description if changed
1282 if (db_description != Description)
1283 {
1284 LOG(VB_GENERAL, LOG_NOTICE,
1285 "Updating keybinding description...");
1286 query.prepare(
1287 "UPDATE keybindings "
1288 "SET description = :DESCRIPTION "
1289 "WHERE context = :CONTEXT AND "
1290 " action = :ACTION AND "
1291 " hostname = :HOSTNAME");
1292
1293 query.bindValue(":DESCRIPTION", Description);
1294 query.bindValue(":CONTEXT", Context);
1295 query.bindValue(":ACTION", Action);
1296 query.bindValue(":HOSTNAME", GetMythDB()->GetHostName());
1297
1298 if (!query.exec() && !(GetMythDB()->SuppressDBMessages()))
1299 {
1300 MythDB::DBError("Update Keybinding", query);
1301 }
1302 }
1303 }
1304 else
1305 {
1306 const QString& inskey = keybind;
1307
1308 query.prepare("INSERT INTO keybindings (context, action, "
1309 "description, keylist, hostname) VALUES "
1310 "( :CONTEXT, :ACTION, :DESCRIPTION, :KEYLIST, "
1311 ":HOSTNAME );");
1312 query.bindValue(":CONTEXT", Context);
1313 query.bindValue(":ACTION", Action);
1314 query.bindValue(":DESCRIPTION", Description);
1315 query.bindValue(":KEYLIST", inskey);
1316 query.bindValue(":HOSTNAME", GetMythDB()->GetHostName());
1317
1318 if (!query.exec() && !(GetMythDB()->SuppressDBMessages()))
1319 {
1320 MythDB::DBError("Insert Keybinding", query);
1321 }
1322 }
1323 }
1324
1325 BindKey(Context, Action, keybind);
1326 m_priv->m_actionText[Context][Action] = Description;
1327}
1328
1329QString MythMainWindow::GetKey(const QString& Context, const QString& Action)
1330{
1332 if (!query.isConnected())
1333 return "?";
1334
1335 query.prepare("SELECT keylist "
1336 "FROM keybindings "
1337 "WHERE context = :CONTEXT AND "
1338 " action = :ACTION AND "
1339 " hostname = :HOSTNAME");
1340 query.bindValue(":CONTEXT", Context);
1341 query.bindValue(":ACTION", Action);
1342 query.bindValue(":HOSTNAME", GetMythDB()->GetHostName());
1343
1344 if (!query.exec() || !query.isActive() || !query.next())
1345 return "?";
1346
1347 return query.value(0).toString();
1348}
1349
1350QString MythMainWindow::GetActionText(const QString& Context,
1351 const QString& Action) const
1352{
1353 if (m_priv->m_actionText.contains(Context))
1354 {
1355 QHash<QString, QString> entry = m_priv->m_actionText.value(Context);
1356 if (entry.contains(Action))
1357 return entry.value(Action);
1358 }
1359 return "";
1360}
1361
1362void MythMainWindow::ClearJump(const QString& Destination)
1363{
1364 // make sure that the jump point exists (using [] would add it)
1365 if (!m_priv->m_destinationMap.contains(Destination))
1366 {
1367 LOG(VB_GENERAL, LOG_ERR, "Cannot clear ficticious jump point" + Destination);
1368 return;
1369 }
1370
1371 for (auto it = m_priv->m_jumpMap.begin();
1372 it != m_priv->m_jumpMap.end();
1373 /* no inc */)
1374 {
1375 JumpData *jd = it.value();
1376 if (jd->m_destination == Destination)
1377 it = m_priv->m_jumpMap.erase(it);
1378 else
1379 ++it;
1380 }
1381}
1382
1383
1384void MythMainWindow::BindJump(const QString& Destination, const QString& Key)
1385{
1386 // make sure the jump point exists
1387 if (!m_priv->m_destinationMap.contains(Destination))
1388 {
1389 LOG(VB_GENERAL, LOG_ERR, "Cannot bind to ficticious jump point" + Destination);
1390 return;
1391 }
1392
1393 QKeySequence keyseq(Key);
1394
1395 for (unsigned int i = 0; i < static_cast<uint>(keyseq.count()); i++)
1396 {
1397#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1398 int keynum = keyseq[i];
1399#else
1400 int keynum = keyseq[i].toCombined();
1401#endif
1402
1403 if (!m_priv->m_jumpMap.contains(keynum))
1404 {
1405#if 0
1406 LOG(VB_GENERAL, LOG_DEBUG, QString("Binding: %1 to JumpPoint: %2")
1407 .arg(keybind).arg(destination));
1408#endif
1409
1410 m_priv->m_jumpMap[keynum] = &m_priv->m_destinationMap[Destination];
1411 }
1412 else
1413 {
1414 LOG(VB_GENERAL, LOG_WARNING, QString("Key %1 is already bound to a jump point.")
1415 .arg(Key));
1416 }
1417 }
1418#if 0
1419 else
1420 LOG(VB_GENERAL, LOG_DEBUG,
1421 QString("JumpPoint: %2 exists, no keybinding") .arg(destination));
1422#endif
1423}
1424
1425void MythMainWindow::RegisterJump(const QString& Destination, const QString& Description,
1426 const QString& Key, void (*Callback)(void),
1427 bool Exittomain, QString LocalAction)
1428{
1429 QString keybind = Key;
1430
1432 if (query.isConnected())
1433 {
1434 query.prepare("SELECT keylist FROM jumppoints WHERE destination = :DEST and hostname = :HOST ;");
1435 query.bindValue(":DEST", Destination);
1436 query.bindValue(":HOST", GetMythDB()->GetHostName());
1437 if (query.exec() && query.next())
1438 {
1439 keybind = query.value(0).toString();
1440 }
1441 else
1442 {
1443 const QString& inskey = keybind;
1444
1445 query.prepare("INSERT INTO jumppoints (destination, description, "
1446 "keylist, hostname) VALUES ( :DEST, :DESC, :KEYLIST, "
1447 ":HOST );");
1448 query.bindValue(":DEST", Destination);
1449 query.bindValue(":DESC", Description);
1450 query.bindValue(":KEYLIST", inskey);
1451 query.bindValue(":HOST", GetMythDB()->GetHostName());
1452 if (!query.exec() || !query.isActive())
1453 MythDB::DBError("Insert Jump Point", query);
1454 }
1455 }
1456
1457 JumpData jd = { Callback, Destination, Description, Exittomain, std::move(LocalAction) };
1458 m_priv->m_destinationMap[Destination] = jd;
1459 BindJump(Destination, keybind);
1460}
1461
1463{
1464 QList<QString> destinations = m_priv->m_destinationMap.keys();
1465 QList<QString>::Iterator it;
1466 for (it = destinations.begin(); it != destinations.end(); ++it)
1467 ClearJump(*it);
1468}
1469
1470void MythMainWindow::JumpTo(const QString& Destination, bool Pop)
1471{
1472 if (m_priv->m_destinationMap.contains(Destination) && m_priv->m_exitMenuCallback == nullptr)
1473 {
1474 m_priv->m_exitingtomain = true;
1475 m_priv->m_popwindows = Pop;
1476 m_priv->m_exitMenuCallback = m_priv->m_destinationMap[Destination].m_callback;
1477 QCoreApplication::postEvent(
1478 this, new QEvent(MythEvent::kExitToMainMenuEventType));
1479 return;
1480 }
1481}
1482
1483bool MythMainWindow::DestinationExists(const QString& Destination) const
1484{
1485 return m_priv->m_destinationMap.contains(Destination);
1486}
1487
1489{
1490 return m_priv->m_destinationMap.keys();
1491}
1492
1493void MythMainWindow::RegisterMediaPlugin(const QString& Name, const QString& Desc,
1494 MediaPlayCallback Func)
1495{
1496 if (!m_priv->m_mediaPluginMap.contains(Name))
1497 {
1498 LOG(VB_GENERAL, LOG_NOTICE, QString("Registering %1 as a media playback plugin.")
1499 .arg(Name));
1500 m_priv->m_mediaPluginMap[Name] = { Desc, Func };
1501 }
1502 else
1503 {
1504 LOG(VB_GENERAL, LOG_NOTICE, QString("%1 is already registered as a media playback plugin.")
1505 .arg(Name));
1506 }
1507}
1508
1509bool MythMainWindow::HandleMedia(const QString& Handler, const QString& Mrl,
1510 const QString& Plot, const QString& Title,
1511 const QString& Subtitle,
1512 const QString& Director, int Season,
1513 int Episode, const QString& Inetref,
1514 std::chrono::minutes LenMins, const QString& Year,
1515 const QString& Id, bool UseBookmarks)
1516{
1517 QString lhandler(Handler);
1518 if (lhandler.isEmpty())
1519 lhandler = "Internal";
1520
1521 // Let's see if we have a plugin that matches the handler name...
1522 if (m_priv->m_mediaPluginMap.contains(lhandler))
1523 {
1524 m_priv->m_mediaPluginMap[lhandler].second(Mrl, Plot, Title, Subtitle,
1525 Director, Season, Episode,
1526 Inetref, LenMins, Year, Id,
1527 UseBookmarks);
1528 return true;
1529 }
1530
1531 return false;
1532}
1533
1535{
1537}
1538
1540{
1541 m_priv->m_allowInput = Allow;
1542}
1543
1545{
1546 // complete the stroke if its our first timeout
1547 if (m_priv->m_gesture.Recording())
1548 m_priv->m_gesture.Stop(true);
1549
1550 // get the last gesture
1551 auto * event = m_priv->m_gesture.GetGesture();
1552 if (event->GetGesture() < MythGestureEvent::Click)
1553 QCoreApplication::postEvent(this, event);
1554}
1555
1556// Return code = true to skip further processing, false to continue
1557// sNewEvent: Caller must pass in a QScopedPointer that will be used
1558// to delete a new event if one is created.
1559bool MythMainWindow::KeyLongPressFilter(QEvent** Event, QScopedPointer<QEvent>& NewEvent)
1560{
1561 auto * keyevent = dynamic_cast<QKeyEvent*>(*Event);
1562 if (!keyevent)
1563 return false;
1564 int keycode = keyevent->key();
1565 // Ignore unknown key codes
1566 if (keycode == 0)
1567 return false;
1568
1569 QEvent *newevent = nullptr;
1570 switch ((*Event)->type())
1571 {
1572 case QEvent::KeyPress:
1573 {
1574 // Check if we are in the middle of a long press
1575 if (keycode == m_priv->m_longPressKeyCode)
1576 {
1577 if (std::chrono::milliseconds(keyevent->timestamp()) - m_priv->m_longPressTime < LONGPRESS_INTERVAL
1578 || m_priv->m_longPressTime == 0ms)
1579 {
1580 // waiting for release of key.
1581 return true; // discard the key press
1582 }
1583
1584 // expired log press - generate long key
1585 newevent = new QKeyEvent(QEvent::KeyPress, keycode,
1586 keyevent->modifiers() | Qt::MetaModifier, keyevent->nativeScanCode(),
1587 keyevent->nativeVirtualKey(), keyevent->nativeModifiers(),
1588 keyevent->text(), false,1);
1589 *Event = newevent;
1590 NewEvent.reset(newevent);
1591 m_priv->m_longPressTime = 0ms; // indicate we have generated the long press
1592 return false;
1593 }
1594 // If we got a keycode different from the long press keycode it
1595 // may have been injected by a jump point. Ignore it.
1596 if (m_priv->m_longPressKeyCode != 0)
1597 return false;
1598
1599 // Process start of possible new long press.
1601 QStringList actions;
1602 bool handled = TranslateKeyPress("Long Press", keyevent, actions,false);
1603 if (handled)
1604 {
1605 // This shoudl never happen,, because we passed in false
1606 // to say do not process jump points and yet it returned true
1607 // to say it processed a jump point.
1608 LOG(VB_GUI, LOG_ERR, QString("TranslateKeyPress Long Press Invalid Response"));
1609 return true;
1610 }
1611 if (!actions.empty() && actions[0].startsWith("LONGPRESS"))
1612 {
1613 // Beginning of a press
1614 m_priv->m_longPressKeyCode = keycode;
1615 m_priv->m_longPressTime = std::chrono::milliseconds(keyevent->timestamp());
1616 return true; // discard the key press
1617 }
1618 break;
1619 }
1620 case QEvent::KeyRelease:
1621 {
1622 if (keycode == m_priv->m_longPressKeyCode)
1623 {
1624 if (keyevent->isAutoRepeat())
1625 return true;
1626 if (m_priv->m_longPressTime > 0ms)
1627 {
1628 // short press or non-repeating keyboard - generate key
1629 Qt::KeyboardModifiers modifier = Qt::NoModifier;
1630 if (std::chrono::milliseconds(keyevent->timestamp()) - m_priv->m_longPressTime >= LONGPRESS_INTERVAL)
1631 {
1632 // non-repeatng keyboard
1633 modifier = Qt::MetaModifier;
1634 }
1635 newevent = new QKeyEvent(QEvent::KeyPress, keycode,
1636 keyevent->modifiers() | modifier, keyevent->nativeScanCode(),
1637 keyevent->nativeVirtualKey(), keyevent->nativeModifiers(),
1638 keyevent->text(), false,1);
1639 *Event = newevent;
1640 NewEvent.reset(newevent);
1642 return false;
1643 }
1644
1645 // end of long press
1647 return true;
1648 }
1649 break;
1650 }
1651 default:
1652 break;
1653 }
1654 return false;
1655}
1656
1657bool MythMainWindow::eventFilter(QObject* Watched, QEvent* Event)
1658{
1659 /* Don't let anything through if input is disallowed. */
1660 if (!m_priv->m_allowInput)
1661 return true;
1662
1663 QScopedPointer<QEvent> newevent(nullptr);
1664 if (KeyLongPressFilter(&Event, newevent))
1665 return true;
1666
1667 switch (Event->type())
1668 {
1669 case QEvent::KeyPress:
1670 {
1672 auto * event = dynamic_cast<QKeyEvent*>(Event);
1673
1674 // Work around weird GCC run-time bug. Only manifest on Mac OS X
1675 if (!event)
1676 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
1677 event = static_cast<QKeyEvent*>(Event);
1678
1679#ifdef Q_OS_LINUX
1680 // Fixups for _some_ linux native codes that QT doesn't know
1681 if (event && event->key() <= 0)
1682 {
1683 int keycode = 0;
1684 switch (event->nativeScanCode())
1685 {
1686 case 209: // XF86AudioPause
1687 keycode = Qt::Key_MediaPause;
1688 break;
1689 default:
1690 break;
1691 }
1692
1693 if (keycode > 0)
1694 {
1695 auto * key = new QKeyEvent(QEvent::KeyPress, keycode, event->modifiers());
1696 if (auto * target = GetTarget(*key); target)
1697 QCoreApplication::postEvent(target, key);
1698 else
1699 QCoreApplication::postEvent(this, key);
1700 return true;
1701 }
1702 }
1703#endif
1704
1705 QVector<MythScreenStack *>::const_reverse_iterator it;
1706 for (it = m_priv->m_stackList.rbegin(); it != m_priv->m_stackList.rend(); it++)
1707 {
1708 if (auto * top = (*it)->GetTopScreen(); top)
1709 {
1710 if (top->keyPressEvent(event))
1711 return true;
1712 // Note: The following break prevents keypresses being
1713 // sent to windows below popups
1714 if ((*it)->objectName() == "popup stack")
1715 break;
1716 }
1717 }
1718 break;
1719 }
1720 case QEvent::InputMethod:
1721 {
1723 auto *ie = dynamic_cast<QInputMethodEvent*>(Event);
1724 if (!ie)
1725 return MythUIScreenBounds::eventFilter(Watched, Event);
1726 QWidget *widget = QApplication::focusWidget();
1727 if (widget)
1728 {
1729 ie->accept();
1730 if (widget->isEnabled())
1731 QCoreApplication::instance()->notify(widget, ie);
1732 break;
1733 }
1734 QVector<MythScreenStack *>::const_reverse_iterator it;
1735 for (it = m_priv->m_stackList.rbegin(); it != m_priv->m_stackList.rend(); it++)
1736 {
1737 MythScreenType *top = (*it)->GetTopScreen();
1738 if (top == nullptr)
1739 continue;
1740 if (top->inputMethodEvent(ie))
1741 return true;
1742 // Note: The following break prevents keypresses being
1743 // sent to windows below popups
1744 if ((*it)->objectName() == "popup stack")
1745 break;
1746 }
1747 break;
1748 }
1749 case QEvent::MouseButtonPress:
1750 {
1752 ShowMouseCursor(true);
1753 if (!m_priv->m_gesture.Recording())
1754 {
1756 auto * mouseEvent = dynamic_cast<QMouseEvent*>(Event);
1757 if (!mouseEvent)
1758 return MythUIScreenBounds::eventFilter(Watched, Event);
1759 m_priv->m_gesture.Record(mouseEvent->pos(), mouseEvent->button());
1760 /* start a single shot timer */
1762 return true;
1763 }
1764 break;
1765 }
1766 case QEvent::MouseButtonRelease:
1767 {
1769 ShowMouseCursor(true);
1770 if (m_priv->m_gestureTimer->isActive())
1771 m_priv->m_gestureTimer->stop();
1772
1773 if (m_priv->m_gesture.Recording())
1774 {
1776 auto * gesture = m_priv->m_gesture.GetGesture();
1777 QPoint point { -1, -1 };
1778 auto * mouseevent = dynamic_cast<QMouseEvent*>(Event);
1779 if (mouseevent)
1780 {
1781 point = mouseevent->pos();
1782 gesture->SetPosition(point);
1783 }
1784
1785 // Handle clicks separately
1786 if (gesture->GetGesture() == MythGestureEvent::Click)
1787 {
1788 if (!mouseevent)
1789 return MythUIScreenBounds::eventFilter(Watched, Event);
1790
1791 QVector<MythScreenStack *>::const_reverse_iterator it;
1792 for (it = m_priv->m_stackList.rbegin(); it != m_priv->m_stackList.rend(); it++)
1793 {
1794 auto * screen = (*it)->GetTopScreen();
1795 if (!screen || !screen->ContainsPoint(point))
1796 continue;
1797
1798 if (screen->gestureEvent(gesture))
1799 break;
1800 // Note: The following break prevents clicks being
1801 // sent to windows below popups
1802 //
1803 // we want to permit this in some cases, e.g.
1804 // when the music miniplayer is on screen or a
1805 // non-interactive alert/news scroller. So these
1806 // things need to be in a third or more stack
1807 if ((*it)->objectName() == "popup stack")
1808 break;
1809 }
1810 delete gesture;
1811 }
1812 else
1813 {
1814 bool handled = false;
1815
1816 if (!mouseevent)
1817 {
1818 QCoreApplication::postEvent(this, gesture);
1819 return true;
1820 }
1821
1822 QVector<MythScreenStack *>::const_reverse_iterator it;
1823 for (it = m_priv->m_stackList.rbegin(); it != m_priv->m_stackList.rend(); it++)
1824 {
1825 MythScreenType *screen = (*it)->GetTopScreen();
1826 if (!screen || !screen->ContainsPoint(point))
1827 continue;
1828
1829 if (screen->gestureEvent(gesture))
1830 {
1831 handled = true;
1832 break;
1833 }
1834 // Note: The following break prevents clicks being
1835 // sent to windows below popups
1836 //
1837 // we want to permit this in some cases, e.g.
1838 // when the music miniplayer is on screen or a
1839 // non-interactive alert/news scroller. So these
1840 // things need to be in a third or more stack
1841 if ((*it)->objectName() == "popup stack")
1842 break;
1843 }
1844
1845 if (handled)
1846 delete gesture;
1847 else
1848 QCoreApplication::postEvent(this, gesture);
1849 }
1850
1851 return true;
1852 }
1853 break;
1854 }
1855 case QEvent::MouseMove:
1856 {
1858 ShowMouseCursor(true);
1859 if (m_priv->m_gesture.Recording())
1860 {
1861 // Reset the timer
1862 m_priv->m_gestureTimer->stop();
1864 auto * mouseevent = dynamic_cast<QMouseEvent*>(Event);
1865 if (!mouseevent)
1866 return MythUIScreenBounds::eventFilter(Watched, Event);
1867 m_priv->m_gesture.Record(mouseevent->pos(), mouseevent->button());
1868 return true;
1869 }
1870 break;
1871 }
1872 case QEvent::Wheel:
1873 {
1875 ShowMouseCursor(true);
1876 auto * wheel = dynamic_cast<QWheelEvent*>(Event);
1877 if (wheel == nullptr)
1878 return MythUIScreenBounds::eventFilter(Watched, Event);
1879 int delta = wheel->angleDelta().y();
1880 if (delta>0)
1881 {
1882 wheel->accept();
1883 auto *key = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier);
1884 if (auto * target = GetTarget(*key); target)
1885 QCoreApplication::postEvent(target, key);
1886 else
1887 QCoreApplication::postEvent(this, key);
1888 }
1889 if (delta < 0)
1890 {
1891 wheel->accept();
1892 auto * key = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier);
1893 if (auto * target = GetTarget(*key); !target)
1894 QCoreApplication::postEvent(target, key);
1895 else
1896 QCoreApplication::postEvent(this, key);
1897 }
1898 break;
1899 }
1900 default:
1901 break;
1902 }
1903
1904 return MythUIScreenBounds::eventFilter(Watched, Event);
1905}
1906
1908{
1909 if (Event->type() == MythGestureEvent::kEventType)
1910 {
1911 auto * gesture = dynamic_cast<MythGestureEvent*>(Event);
1912 if (gesture == nullptr)
1913 return;
1914 if (auto * toplevel = GetMainStack(); toplevel)
1915 if (auto * screen = toplevel->GetTopScreen(); screen)
1916 screen->gestureEvent(gesture);
1917 LOG(VB_GUI, LOG_DEBUG, QString("Gesture: %1 (Button: %2)")
1918 .arg(gesture->GetName(), gesture->GetButtonName()));
1919 }
1921 {
1923 }
1924 else if (Event->type() == ExternalKeycodeEvent::kEventType)
1925 {
1926 auto * event = dynamic_cast<ExternalKeycodeEvent *>(Event);
1927 if (event == nullptr)
1928 return;
1929 auto * key = new QKeyEvent(QEvent::KeyPress, event->getKeycode(), Qt::NoModifier);
1930 if (auto * target = GetTarget(*key); target)
1931 QCoreApplication::sendEvent(target, key);
1932 else
1933 QCoreApplication::sendEvent(this, key);
1934 }
1935 else if (Event->type() == MythMediaEvent::kEventType)
1936 {
1937 auto *me = dynamic_cast<MythMediaEvent*>(Event);
1938 if (me == nullptr)
1939 return;
1940
1941 // A listener based system might be more efficient, but we should never
1942 // have that many screens open at once so impact should be minimal.
1943 //
1944 // This approach is simpler for everyone to follow. Plugin writers
1945 // don't have to worry about adding their screens to the list because
1946 // all screens receive media events.
1947 //
1948 // Events are even sent to hidden or backgrounded screens, this avoids
1949 // the need for those to poll for changes when they become visible again
1950 // however this needs to be kept in mind if media changes trigger
1951 // actions which would not be appropriate when the screen doesn't have
1952 // focus. It is the programmers responsibility to ignore events when
1953 // necessary.
1954 for (auto * widget : std::as_const(m_priv->m_stackList))
1955 {
1956 QVector<MythScreenType*> screenList;
1957 widget->GetScreenList(screenList);
1958 for (auto * screen : std::as_const(screenList))
1959 if (screen)
1960 screen->mediaEvent(me);
1961 }
1962
1963 // Debugging
1964 if (MythMediaDevice* device = me->getDevice(); device)
1965 {
1966 LOG(VB_GENERAL, LOG_DEBUG, QString("Media Event: %1 - %2")
1967 .arg(device->getDevicePath()).arg(device->getStatus()));
1968 }
1969 }
1971 {
1973 }
1975 {
1977 }
1978 else if (Event->type() == MythEvent::kLockInputDevicesEventType)
1979 {
1981 PauseIdleTimer(true);
1982 }
1984 {
1986 PauseIdleTimer(false);
1987 }
1989 {
1991 }
1993 {
1995 }
1996 else if (Event->type() == MythEvent::kMythEventMessage)
1997 {
1998 auto * event = dynamic_cast<MythEvent *>(Event);
1999 if (event == nullptr)
2000 return;
2001
2002 QString message = event->Message();
2003 if (message.startsWith(ACTION_HANDLEMEDIA))
2004 {
2005 if (event->ExtraDataCount() == 1)
2006 HandleMedia("Internal", event->ExtraData(0));
2007 else if (event->ExtraDataCount() >= 11)
2008 {
2009 bool usebookmark = true;
2010 if (event->ExtraDataCount() >= 12)
2011 usebookmark = (event->ExtraData(11).toInt() != 0);
2012 HandleMedia("Internal", event->ExtraData(0),
2013 event->ExtraData(1), event->ExtraData(2),
2014 event->ExtraData(3), event->ExtraData(4),
2015 event->ExtraData(5).toInt(), event->ExtraData(6).toInt(),
2016 event->ExtraData(7), std::chrono::minutes(event->ExtraData(8).toInt()),
2017 event->ExtraData(9), event->ExtraData(10),
2018 usebookmark);
2019 }
2020 else
2021 {
2022 LOG(VB_GENERAL, LOG_ERR, "Failed to handle media");
2023 }
2024 }
2025 else if (message.startsWith(ACTION_SCREENSHOT))
2026 {
2027 int width = 0;
2028 int height = 0;
2029 QString filename;
2030 if (event->ExtraDataCount() >= 2)
2031 {
2032 width = event->ExtraData(0).toInt();
2033 height = event->ExtraData(1).toInt();
2034 if (event->ExtraDataCount() == 3)
2035 filename = event->ExtraData(2);
2036 }
2037 ScreenShot(width, height, filename);
2038 }
2039 else if (message == ACTION_GETSTATUS)
2040 {
2041 QVariantMap state;
2042 state.insert("state", "idle");
2043 state.insert("menutheme", GetMythDB()->GetSetting("menutheme", "defaultmenu"));
2044 state.insert("currentlocation", GetMythUI()->GetCurrentLocation());
2046 }
2047 else if (message == "CLEAR_SETTINGS_CACHE")
2048 {
2049 // update the idle time
2050 m_idleTime =
2051 gCoreContext->GetDurSetting<std::chrono::minutes>("FrontendIdleTimeout",
2053
2054 if (m_idleTime < 0min)
2055 m_idleTime = 0min;
2056 m_idleTimer.stop();
2057 if (m_idleTime > 0min)
2058 {
2059 m_idleTimer.setInterval(m_idleTime);
2060 m_idleTimer.start();
2061 LOG(VB_GENERAL, LOG_INFO, QString("Updating the frontend idle time to: %1 mins").arg(m_idleTime.count()));
2062 }
2063 else
2064 {
2065 LOG(VB_GENERAL, LOG_INFO, "Frontend idle timeout is disabled");
2066 }
2067 }
2068 else if (message == "NOTIFICATION")
2069 {
2072 return;
2073 }
2074 else if (message == "RECONNECT_SUCCESS" && m_priv->m_standby)
2075 {
2076 // If the connection to the master backend has just been (re-)established
2077 // but we're in standby, make sure the backend is not blocked from
2078 // shutting down.
2080 }
2081 }
2082 else if (Event->type() == MythEvent::kMythUserMessage)
2083 {
2084 if (auto * event = dynamic_cast<MythEvent *>(Event); event != nullptr)
2085 if (const QString& message = event->Message(); !message.isEmpty())
2086 ShowOkPopup(message);
2087 }
2089 {
2091 }
2092}
2093
2094QObject* MythMainWindow::GetTarget(QKeyEvent& Key)
2095{
2096 auto * target = QWidget::keyboardGrabber();
2097 if (!target)
2098 {
2099 if (auto * widget = QApplication::focusWidget(); widget && widget->isEnabled())
2100 {
2101 target = widget;
2102 // Yes this is special code for handling the
2103 // the escape key.
2104 if (Key.key() == m_priv->m_escapekey && widget->topLevelWidget())
2105 target = widget->topLevelWidget();
2106 }
2107 }
2108
2109 if (!target)
2110 target = this;
2111 return target;
2112}
2113
2115{
2117}
2118
2120{
2121 if (Show && GetMythDB()->GetBoolSetting("HideMouseCursor", false))
2122 return;
2123
2124 // Set cursor call must come after Show() to work on some systems.
2125 setCursor(Show ? (Qt::ArrowCursor) : (Qt::BlankCursor));
2126 if (Show)
2127 m_priv->m_hideMouseTimer->start();
2128}
2129
2131{
2132 ShowMouseCursor(false);
2133}
2134
2139{
2140 m_priv->m_disableIdle = DisableIdle;
2141 if (m_priv->m_disableIdle)
2142 m_idleTimer.stop();
2143 else
2144 m_idleTimer.start();
2145}
2146
2151{
2152 if (m_priv->m_disableIdle)
2153 return;
2154
2155 if (m_idleTime == 0min || !m_idleTimer.isActive() || (m_priv->m_standby && m_priv->m_enteringStandby))
2156 return;
2157
2158 if (m_priv->m_standby)
2159 ExitStandby(false);
2160
2161 m_idleTimer.start();
2162}
2163
2168{
2169 if (m_priv->m_disableIdle)
2170 return;
2171
2172 // don't do anything if the idle timer is disabled
2173 if (m_idleTime == 0min)
2174 return;
2175
2176 if (Pause)
2177 {
2178 LOG(VB_GENERAL, LOG_NOTICE, "Suspending idle timer");
2179 m_idleTimer.stop();
2180 }
2181 else
2182 {
2183 LOG(VB_GENERAL, LOG_NOTICE, "Resuming idle timer");
2184 m_idleTimer.start();
2185 }
2186
2187 // ResetIdleTimer();
2188}
2189
2191{
2192 if (m_priv->m_disableIdle)
2193 return;
2194
2195 m_priv->m_enteringStandby = false;
2196
2197 if (m_idleTime > 0min && !m_priv->m_standby)
2198 {
2199 LOG(VB_GENERAL, LOG_NOTICE,
2200 QString("Entering standby mode after %1 minutes of inactivity").arg(m_idleTime.count()));
2201 EnterStandby(false);
2202 if (gCoreContext->GetNumSetting("idleTimeoutSecs", 0) > 0)
2203 {
2204 m_priv->m_enteringStandby = true;
2205 JumpTo("Standby Mode");
2206 }
2207 }
2208}
2209
2211{
2212 if (Manual && m_priv->m_enteringStandby)
2213 m_priv->m_enteringStandby = false;
2214
2215 if (m_priv->m_standby)
2216 return;
2217
2218 // We've manually entered standby mode and we want to pause the timer
2219 // to prevent it being Reset
2220 if (Manual)
2221 {
2222 PauseIdleTimer(true);
2223 LOG(VB_GENERAL, LOG_NOTICE, QString("Entering standby mode"));
2224 }
2225
2226 m_priv->m_standby = true;
2228
2229 QVariantMap state;
2230 state.insert("state", "standby");
2231 state.insert("menutheme", GetMythDB()->GetSetting("menutheme", "defaultmenu"));
2232 state.insert("currentlocation", GetMythUI()->GetCurrentLocation());
2234
2235 // Cache WOL settings in case DB goes down
2236 QString masterserver = gCoreContext->GetSetting("MasterServerName");
2237 gCoreContext->GetSettingOnHost("BackendServerAddr", masterserver);
2239 gCoreContext->GetSetting("WOLbackendCommand", "");
2240
2241 // While in standby do not attempt to wake the backend
2243}
2244
2246{
2248 return;
2249
2250 if (Manual)
2251 PauseIdleTimer(false);
2252 else if (gCoreContext->GetNumSetting("idleTimeoutSecs", 0) > 0)
2253 JumpTo("Main Menu");
2254
2255 if (!m_priv->m_standby)
2256 return;
2257
2258 LOG(VB_GENERAL, LOG_NOTICE, "Leaving standby mode");
2259 m_priv->m_standby = false;
2260
2261 // We may attempt to wake the backend
2264
2265 QVariantMap state;
2266 state.insert("state", "idle");
2267 state.insert("menutheme", GetMythDB()->GetSetting("menutheme", "defaultmenu"));
2268 state.insert("currentlocation", GetMythUI()->GetCurrentLocation());
2270}
2271
2273{
2274 LOG(VB_GENERAL, LOG_NOTICE, QString("Application State Changed to %1").arg(State));
2275 switch (State)
2276 {
2277 case Qt::ApplicationState::ApplicationActive:
2279 break;
2280 case Qt::ApplicationState::ApplicationSuspended:
2282 break;
2283 default:
2284 break;
2285 }
2286}
2287/* vim: set expandtab tabstop=4 shiftwidth=4: */
An action (for this plugin) consists of a description, and a set of key sequences.
Definition: action.h:41
Event details.
Definition: zmdefines.h:28
static const Type kEventType
Definition: mythevent.h:113
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838
QVariant value(int i) const
Definition: mythdbcon.h:204
bool isActive(void) const
Definition: mythdbcon.h:215
bool isConnected(void) const
Only updated once during object creation.
Definition: mythdbcon.h:137
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
void ResetAudioLanguage(void)
MythDB * GetDB(void)
void SetWOLAllowed(bool allow)
void ResetLanguage(void)
QString GetSetting(const QString &key, const QString &defaultval="")
static int GetMasterServerPort(void)
Returns the Master Backend control port If no master server port has been defined in the database,...
QString GetSettingOnHost(const QString &key, const QString &host, const QString &defaultval="")
void BlockShutdown(void)
void dispatch(const MythEvent &event)
void SetGUIObject(QObject *gui)
int GetNumSetting(const QString &key, int defaultval=0)
void AllowShutdown(void)
std::enable_if_t< std::chrono::__is_duration< T >::value, T > GetDurSetting(const QString &key, T defaultval=T::zero())
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:225
void SwitchToDesktop()
Return the screen to the original desktop video mode.
bool SwitchToGUI(bool Wait=false)
Switches to the GUI resolution.
virtual bool UsingVideoModes()
Definition: mythdisplay.h:30
void SetWidget(QWidget *MainWindow)
Set the QWidget and QWindow in use.
This class is used as a container for messages.
Definition: mythevent.h:17
static const Type kExitToMainMenuEventType
Definition: mythevent.h:82
static const Type kPushDisableDrawingEventType
Definition: mythevent.h:84
static const Type kDisableUDPListenerEventType
Definition: mythevent.h:89
const QString & Message() const
Definition: mythevent.h:65
static const Type kUnlockInputDevicesEventType
Definition: mythevent.h:87
static const Type kPopDisableDrawingEventType
Definition: mythevent.h:85
static const Type kEnableUDPListenerEventType
Definition: mythevent.h:90
static const Type kMythPostShowEventType
Definition: mythevent.h:83
static const Type kMythEventMessage
Definition: mythevent.h:79
static const Type kMythUserMessage
Definition: mythevent.h:80
static const Type kLockInputDevicesEventType
Definition: mythevent.h:86
A custom event that represents a mouse gesture.
Definition: mythgesture.h:40
void SetPosition(QPoint Position)
Definition: mythgesture.h:86
static const Type kEventType
Definition: mythgesture.h:91
bool Record(QPoint Point, Qt::MouseButton Button)
Record a point.
MythGestureEvent * GetGesture() const
Complete the gesture event of the last completed stroke.
bool Recording()
Determine if the stroke is being recorded.
void Start()
Start recording.
void Stop(bool Timeout=false)
Stop recording.
A wrapper around sundry external input devices.
void Action(const QString &Action)
void Event(QEvent *Event) const
QVector< MythScreenStack * > m_stackList
QMap< int, JumpData * > m_jumpMap
std::chrono::milliseconds m_longPressTime
QHash< QString, QHash< QString, QString > > m_actionText
QMap< QString, JumpData > m_destinationMap
MythNotificationCenter * m_nc
QHash< QString, KeyContext * > m_keyContexts
bool m_useDB
To allow or prevent database access.
static int TranslateKeyNum(QKeyEvent *Event)
QMap< QString, MythMediaCallback > m_mediaPluginMap
void(* m_exitMenuMediaDeviceCallback)(MythMediaDevice *mediadevice)
MythMediaDevice * m_mediaDeviceForCallback
MythScreenStack * m_mainStack
void customEvent(QEvent *Event) override
QWidget * GetPaintWindow()
void ClearKeyContext(const QString &Context)
static void DisableScreensaver()
void ShowMouseCursor(bool Show)
MythScreenStack * GetStackAt(int Position)
static void LoadQtConfig()
void ExitStandby(bool Manual=true)
void RemoteScreenShot(QString Filename, int Width, int Height)
MythNotificationCenter * GetCurrentNotificationCenter()
void SetDrawEnabled(bool Enable)
static void ResetScreensaver()
void OnApplicationStateChange(Qt::ApplicationState State)
void SignalRemoteScreenShot(QString Filename, int Width, int Height)
std::chrono::minutes m_idleTime
QString GetActionText(const QString &Context, const QString &Action) const
void PauseIdleTimer(bool Pause)
Pause the idle timeout timer.
MythDisplay * GetDisplay()
void HandleTVAction(const QString &Action)
MythScreenStack * GetMainStack()
static bool ScreenShot(int Width=0, int Height=0, QString Filename="")
void SignalDisableScreensaver()
void drawScreen(QPaintEvent *Event=nullptr)
MythRender * GetRenderDevice()
~MythMainWindow() override
QRegion m_repaintRegion
MythScreenSaverControl * m_screensaver
void JumpTo(const QString &Destination, bool Pop=true)
void BindJump(const QString &Destination, const QString &Key)
bool event(QEvent *Event) override
void SignalResetScreensaver()
bool eventFilter(QObject *Watched, QEvent *Event) override
MythPainter * m_painter
MythPainter * GetPainter()
static MythMainWindow * getMainWindow(bool UseDB=true)
Return the existing main window, or create one.
void SignalRestoreScreensaver()
static bool IsScreensaverAsleep()
void RegisterMediaPlugin(const QString &Name, const QString &Desc, MediaPlayCallback Func)
void ClearKey(const QString &Context, const QString &Action)
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
MythMainWindow(bool UseDB=true)
void RegisterJump(const QString &Destination, const QString &Description, const QString &Key, void(*Callback)(void), bool Exittomain=true, QString LocalAction="")
static bool IsTopScreenInitialized()
bool IsExitingToMain() const
void SignalSetDrawEnabled(bool Enable)
static bool SaveScreenShot(const QImage &Image, QString Filename="")
void MoveResize(QRect &Geometry)
MythThemeBase * m_themeBase
void DoRemoteScreenShot(const QString &Filename, int Width, int Height)
void AddScreenStack(MythScreenStack *Stack, bool Main=false)
MythMainWindowPrivate * m_priv
static void GrabWindow(QImage &Image)
static void destroyMainWindow()
MythScreenStack * GetStack(const QString &Stackname)
void ResetIdleTimer()
Reset the idle timeout timer.
void EnterStandby(bool Manual=true)
static QString GetKey(const QString &Context, const QString &Action)
bool KeyLongPressFilter(QEvent **Event, QScopedPointer< QEvent > &NewEvent)
MythPainterWindow * m_painterWin
bool DestinationExists(const QString &Destination) const
void ClearJump(const QString &Destination)
void RegisterKey(const QString &Context, const QString &Action, const QString &Description, const QString &Key)
QStringList EnumerateDestinations() const
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="", std::chrono::minutes LenMins=2h, const QString &Year="1895", const QString &Id="", bool UseBookmarks=false)
static void RestoreScreensaver()
QPaintEngine * paintEngine() const override
void Draw(MythPainter *Painter=nullptr)
MythDisplay * m_display
void closeEvent(QCloseEvent *Event) override
void SetEffectsEnabled(bool Enable)
void BindKey(const QString &Context, const QString &Action, const QString &Key)
void DisableIdleTimer(bool DisableIdle=true)
Disable the idle timeout timer.
void Init(bool MayReInit=true)
void SignalWindowReady()
MythInputDeviceHandler * m_deviceHandler
QObject * GetTarget(QKeyEvent &Key)
void AllowInput(bool Allow)
static const Type kEventType
Definition: mythmedia.h:193
void ProcessQueue(void)
ProcessQueue will be called by the GUI event handler and will process all queued MythNotifications an...
static MythNotificationCenter * GetInstance(void)
returns the MythNotificationCenter singleton
int DisplayedNotifications(void) const
Returns number of notifications currently displayed.
bool Queue(const MythNotification &notification)
Queue a notification Queue() is thread-safe and can be called from anywhere.
void addListener(QObject *listener)
Add a listener to the observable.
void removeListener(QObject *listener)
Remove a listener to the observable.
static void DestroyPainters(MythPainterWindow *&PaintWin, MythPainter *&Painter)
static QString CreatePainters(MythMainWindow *MainWin, MythPainterWindow *&PaintWin, MythPainter *&Paint)
MythRender * GetRenderDevice()
virtual bool SupportsClipping(void)=0
virtual void SetClipRect(QRect clipRect)
Definition: mythpainter.cpp:45
virtual QString GetName(void)=0
virtual void Begin(QPaintDevice *)
Definition: mythpainter.h:51
virtual void End()
Definition: mythpainter.h:52
Controls all instances of the screensaver.
virtual MythScreenType * GetTopScreen(void) const
Screen in which all other widgets are contained and rendered.
bool gestureEvent(MythGestureEvent *event) override
Mouse click/movement handler, receives mouse gesture events from the QCoreApplication event loop.
bool inputMethodEvent(QInputMethodEvent *event) override
Input Method event handler.
bool IsInitialized(void) const
Has Init() been called on this screen?
static void EnableUDPListener(bool Enable=true)
static void StopUDPListener()
static bool WindowIsAlwaysFullscreen()
Return true if the current platform only supports fullscreen windows.
void UpdateScreenSettings(MythDisplay *mDisplay)
static bool GeometryIsOverridden()
void SetUIScreenRect(QRect Rect)
static void SetState(const QVariantMap &NewState)
bool ContainsPoint(QPoint point) const
Check if the given point falls within this widgets area.
unsigned int uint
Definition: compat.h:60
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythDB * GetMythDB(void)
Definition: mythdb.cpp:50
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
A C++ ripoff of the stroke library for MythTV.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
static constexpr std::chrono::minutes STANDBY_TIMEOUT
MythNotificationCenter * GetNotificationCenter(void)
bool HasMythMainWindow(void)
static constexpr std::chrono::milliseconds LONGPRESS_INTERVAL
static QMutex s_mainLock
MythPainter * GetMythPainter(void)
MythMainWindow * GetMythMainWindow(void)
static MythMainWindow * s_mainWin
void DestroyMythMainWindow(void)
static constexpr std::chrono::milliseconds GESTURE_TIMEOUT
int(*)(const QString &, const QString &, const QString &, const QString &, const QString &, int, int, const QString &, std::chrono::minutes, const QString &, const QString &, bool) MediaPlayCallback
static constexpr const char * ACTION_7
Definition: mythuiactions.h:11
static constexpr const char * ACTION_5
Definition: mythuiactions.h:9
static constexpr const char * ACTION_0
Definition: mythuiactions.h:4
static constexpr const char * ACTION_SCREENSHOT
Definition: mythuiactions.h:22
static constexpr const char * ACTION_LEFT
Definition: mythuiactions.h:18
static constexpr const char * ACTION_DOWN
Definition: mythuiactions.h:17
static constexpr const char * ACTION_3
Definition: mythuiactions.h:7
static constexpr const char * ACTION_1
Definition: mythuiactions.h:5
static constexpr const char * ACTION_TVPOWERON
Definition: mythuiactions.h:25
static constexpr const char * ACTION_HANDLEMEDIA
Definition: mythuiactions.h:21
static constexpr const char * ACTION_TVPOWEROFF
Definition: mythuiactions.h:24
static constexpr const char * ACTION_4
Definition: mythuiactions.h:8
static constexpr const char * ACTION_RIGHT
Definition: mythuiactions.h:19
static constexpr const char * ACTION_SELECT
Definition: mythuiactions.h:15
static constexpr const char * ACTION_UP
Definition: mythuiactions.h:16
static constexpr const char * ACTION_GETSTATUS
Definition: mythuiactions.h:27
static constexpr const char * ACTION_6
Definition: mythuiactions.h:10
static constexpr const char * ACTION_8
Definition: mythuiactions.h:12
static constexpr const char * ACTION_2
Definition: mythuiactions.h:6
static constexpr const char * ACTION_9
Definition: mythuiactions.h:13
MythUIHelper * GetMythUI()
QString toString(const QDateTime &raw_dt, uint format)
Returns formatted string representing the time.
Definition: mythdate.cpp:93
@ kScreenShotFilename
"yyyy-MM-ddThh-mm-ss.zzz"
Definition: mythdate.h:29
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
bool usebookmark
Definition: mythburn.py:177
static void show(uint8_t *buf, int length)
Definition: ringbuffer.cpp:341
QString m_destination
State
Definition: zmserver.h:69