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,5,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 = { .m_callback=Callback, .m_destination=Destination,
1458 .m_description=Description, .m_exittomain=Exittomain,
1459 .m_localAction=std::move(LocalAction) };
1460 m_priv->m_destinationMap[Destination] = jd;
1461 BindJump(Destination, keybind);
1462}
1463
1465{
1466 QList<QString> destinations = m_priv->m_destinationMap.keys();
1467 QList<QString>::Iterator it;
1468 for (it = destinations.begin(); it != destinations.end(); ++it)
1469 ClearJump(*it);
1470}
1471
1472void MythMainWindow::JumpTo(const QString& Destination, bool Pop)
1473{
1474 if (m_priv->m_destinationMap.contains(Destination) && m_priv->m_exitMenuCallback == nullptr)
1475 {
1476 m_priv->m_exitingtomain = true;
1477 m_priv->m_popwindows = Pop;
1478 m_priv->m_exitMenuCallback = m_priv->m_destinationMap[Destination].m_callback;
1479 QCoreApplication::postEvent(
1480 this, new QEvent(MythEvent::kExitToMainMenuEventType));
1481 return;
1482 }
1483}
1484
1485bool MythMainWindow::DestinationExists(const QString& Destination) const
1486{
1487 return m_priv->m_destinationMap.contains(Destination);
1488}
1489
1491{
1492 return m_priv->m_destinationMap.keys();
1493}
1494
1495void MythMainWindow::RegisterMediaPlugin(const QString& Name, const QString& Desc,
1496 MediaPlayCallback Func)
1497{
1498 if (!m_priv->m_mediaPluginMap.contains(Name))
1499 {
1500 LOG(VB_GENERAL, LOG_NOTICE, QString("Registering %1 as a media playback plugin.")
1501 .arg(Name));
1502 m_priv->m_mediaPluginMap[Name] = { Desc, Func };
1503 }
1504 else
1505 {
1506 LOG(VB_GENERAL, LOG_NOTICE, QString("%1 is already registered as a media playback plugin.")
1507 .arg(Name));
1508 }
1509}
1510
1511bool MythMainWindow::HandleMedia(const QString& Handler, const QString& Mrl,
1512 const QString& Plot, const QString& Title,
1513 const QString& Subtitle,
1514 const QString& Director, int Season,
1515 int Episode, const QString& Inetref,
1516 std::chrono::minutes LenMins, const QString& Year,
1517 const QString& Id, bool UseBookmarks)
1518{
1519 QString lhandler(Handler);
1520 if (lhandler.isEmpty())
1521 lhandler = "Internal";
1522
1523 // Let's see if we have a plugin that matches the handler name...
1524 if (m_priv->m_mediaPluginMap.contains(lhandler))
1525 {
1526 m_priv->m_mediaPluginMap[lhandler].second(Mrl, Plot, Title, Subtitle,
1527 Director, Season, Episode,
1528 Inetref, LenMins, Year, Id,
1529 UseBookmarks);
1530 return true;
1531 }
1532
1533 return false;
1534}
1535
1537{
1539}
1540
1542{
1543 m_priv->m_allowInput = Allow;
1544}
1545
1547{
1548 // complete the stroke if its our first timeout
1549 if (m_priv->m_gesture.Recording())
1550 m_priv->m_gesture.Stop(true);
1551
1552 // get the last gesture
1553 auto * event = m_priv->m_gesture.GetGesture();
1554 if (event->GetGesture() < MythGestureEvent::Click)
1555 QCoreApplication::postEvent(this, event);
1556}
1557
1558// Return code = true to skip further processing, false to continue
1559// sNewEvent: Caller must pass in a QScopedPointer that will be used
1560// to delete a new event if one is created.
1561bool MythMainWindow::KeyLongPressFilter(QEvent** Event, QScopedPointer<QEvent>& NewEvent)
1562{
1563 auto * keyevent = dynamic_cast<QKeyEvent*>(*Event);
1564 if (!keyevent)
1565 return false;
1566 int keycode = keyevent->key();
1567 // Ignore unknown key codes
1568 if (keycode == 0)
1569 return false;
1570
1571 QEvent *newevent = nullptr;
1572 switch ((*Event)->type())
1573 {
1574 case QEvent::KeyPress:
1575 {
1576 // Check if we are in the middle of a long press
1577 if (keycode == m_priv->m_longPressKeyCode)
1578 {
1579 if (std::chrono::milliseconds(keyevent->timestamp()) - m_priv->m_longPressTime < LONGPRESS_INTERVAL
1580 || m_priv->m_longPressTime == 0ms)
1581 {
1582 // waiting for release of key.
1583 return true; // discard the key press
1584 }
1585
1586 // expired log press - generate long key
1587 newevent = new QKeyEvent(QEvent::KeyPress, keycode,
1588 keyevent->modifiers() | Qt::MetaModifier, keyevent->nativeScanCode(),
1589 keyevent->nativeVirtualKey(), keyevent->nativeModifiers(),
1590 keyevent->text(), false,1);
1591 *Event = newevent;
1592 NewEvent.reset(newevent);
1593 m_priv->m_longPressTime = 0ms; // indicate we have generated the long press
1594 return false;
1595 }
1596 // If we got a keycode different from the long press keycode it
1597 // may have been injected by a jump point. Ignore it.
1598 if (m_priv->m_longPressKeyCode != 0)
1599 return false;
1600
1601 // Process start of possible new long press.
1603 QStringList actions;
1604 bool handled = TranslateKeyPress("Long Press", keyevent, actions,false);
1605 if (handled)
1606 {
1607 // This shoudl never happen,, because we passed in false
1608 // to say do not process jump points and yet it returned true
1609 // to say it processed a jump point.
1610 LOG(VB_GUI, LOG_ERR, QString("TranslateKeyPress Long Press Invalid Response"));
1611 return true;
1612 }
1613 if (!actions.empty() && actions[0].startsWith("LONGPRESS"))
1614 {
1615 // Beginning of a press
1616 m_priv->m_longPressKeyCode = keycode;
1617 m_priv->m_longPressTime = std::chrono::milliseconds(keyevent->timestamp());
1618 return true; // discard the key press
1619 }
1620 break;
1621 }
1622 case QEvent::KeyRelease:
1623 {
1624 if (keycode == m_priv->m_longPressKeyCode)
1625 {
1626 if (keyevent->isAutoRepeat())
1627 return true;
1628 if (m_priv->m_longPressTime > 0ms)
1629 {
1630 // short press or non-repeating keyboard - generate key
1631 Qt::KeyboardModifiers modifier = Qt::NoModifier;
1632 if (std::chrono::milliseconds(keyevent->timestamp()) - m_priv->m_longPressTime >= LONGPRESS_INTERVAL)
1633 {
1634 // non-repeatng keyboard
1635 modifier = Qt::MetaModifier;
1636 }
1637 newevent = new QKeyEvent(QEvent::KeyPress, keycode,
1638 keyevent->modifiers() | modifier, keyevent->nativeScanCode(),
1639 keyevent->nativeVirtualKey(), keyevent->nativeModifiers(),
1640 keyevent->text(), false,1);
1641 *Event = newevent;
1642 NewEvent.reset(newevent);
1644 return false;
1645 }
1646
1647 // end of long press
1649 return true;
1650 }
1651 break;
1652 }
1653 default:
1654 break;
1655 }
1656 return false;
1657}
1658
1659bool MythMainWindow::eventFilter(QObject* Watched, QEvent* Event)
1660{
1661 /* Don't let anything through if input is disallowed. */
1662 if (!m_priv->m_allowInput)
1663 return true;
1664
1665 QScopedPointer<QEvent> newevent(nullptr);
1666 if (KeyLongPressFilter(&Event, newevent))
1667 return true;
1668
1669 switch (Event->type())
1670 {
1671 case QEvent::KeyPress:
1672 {
1674 auto * event = dynamic_cast<QKeyEvent*>(Event);
1675
1676 // Work around weird GCC run-time bug. Only manifest on Mac OS X
1677 if (!event)
1678 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
1679 event = static_cast<QKeyEvent*>(Event);
1680
1681#ifdef Q_OS_LINUX
1682 // Fixups for _some_ linux native codes that QT doesn't know
1683 if (event && event->key() <= 0)
1684 {
1685 int keycode = 0;
1686 switch (event->nativeScanCode())
1687 {
1688 case 209: // XF86AudioPause
1689 keycode = Qt::Key_MediaPause;
1690 break;
1691 default:
1692 break;
1693 }
1694
1695 if (keycode > 0)
1696 {
1697 auto * key = new QKeyEvent(QEvent::KeyPress, keycode, event->modifiers());
1698 if (auto * target = GetTarget(*key); target)
1699 QCoreApplication::postEvent(target, key);
1700 else
1701 QCoreApplication::postEvent(this, key);
1702 return true;
1703 }
1704 }
1705#endif
1706
1707 QVector<MythScreenStack *>::const_reverse_iterator it;
1708 for (it = m_priv->m_stackList.rbegin(); it != m_priv->m_stackList.rend(); it++)
1709 {
1710 if (auto * top = (*it)->GetTopScreen(); top)
1711 {
1712 if (top->keyPressEvent(event))
1713 return true;
1714 // Note: The following break prevents keypresses being
1715 // sent to windows below popups
1716 if ((*it)->objectName() == "popup stack")
1717 break;
1718 }
1719 }
1720 break;
1721 }
1722 case QEvent::InputMethod:
1723 {
1725 auto *ie = dynamic_cast<QInputMethodEvent*>(Event);
1726 if (!ie)
1727 return MythUIScreenBounds::eventFilter(Watched, Event);
1728 QWidget *widget = QApplication::focusWidget();
1729 if (widget)
1730 {
1731 ie->accept();
1732 if (widget->isEnabled())
1733 QCoreApplication::instance()->notify(widget, ie);
1734 break;
1735 }
1736 QVector<MythScreenStack *>::const_reverse_iterator it;
1737 for (it = m_priv->m_stackList.rbegin(); it != m_priv->m_stackList.rend(); it++)
1738 {
1739 MythScreenType *top = (*it)->GetTopScreen();
1740 if (top == nullptr)
1741 continue;
1742 if (top->inputMethodEvent(ie))
1743 return true;
1744 // Note: The following break prevents keypresses being
1745 // sent to windows below popups
1746 if ((*it)->objectName() == "popup stack")
1747 break;
1748 }
1749 break;
1750 }
1751 case QEvent::MouseButtonPress:
1752 {
1754 ShowMouseCursor(true);
1755 if (!m_priv->m_gesture.Recording())
1756 {
1758 auto * mouseEvent = dynamic_cast<QMouseEvent*>(Event);
1759 if (!mouseEvent)
1760 return MythUIScreenBounds::eventFilter(Watched, Event);
1761 m_priv->m_gesture.Record(mouseEvent->pos(), mouseEvent->button());
1762 /* start a single shot timer */
1764 return true;
1765 }
1766 break;
1767 }
1768 case QEvent::MouseButtonRelease:
1769 {
1771 ShowMouseCursor(true);
1772 if (m_priv->m_gestureTimer->isActive())
1773 m_priv->m_gestureTimer->stop();
1774
1775 if (m_priv->m_gesture.Recording())
1776 {
1778 auto * gesture = m_priv->m_gesture.GetGesture();
1779 QPoint point { -1, -1 };
1780 auto * mouseevent = dynamic_cast<QMouseEvent*>(Event);
1781 if (mouseevent)
1782 {
1783 point = mouseevent->pos();
1784 gesture->SetPosition(point);
1785 }
1786
1787 // Handle clicks separately
1788 if (gesture->GetGesture() == MythGestureEvent::Click)
1789 {
1790 if (!mouseevent)
1791 return MythUIScreenBounds::eventFilter(Watched, Event);
1792
1793 QVector<MythScreenStack *>::const_reverse_iterator it;
1794 for (it = m_priv->m_stackList.rbegin(); it != m_priv->m_stackList.rend(); it++)
1795 {
1796 auto * screen = (*it)->GetTopScreen();
1797 if (!screen || !screen->ContainsPoint(point))
1798 continue;
1799
1800 if (screen->gestureEvent(gesture))
1801 break;
1802 // Note: The following break prevents clicks being
1803 // sent to windows below popups
1804 //
1805 // we want to permit this in some cases, e.g.
1806 // when the music miniplayer is on screen or a
1807 // non-interactive alert/news scroller. So these
1808 // things need to be in a third or more stack
1809 if ((*it)->objectName() == "popup stack")
1810 break;
1811 }
1812 delete gesture;
1813 }
1814 else
1815 {
1816 bool handled = false;
1817
1818 if (!mouseevent)
1819 {
1820 QCoreApplication::postEvent(this, gesture);
1821 return true;
1822 }
1823
1824 QVector<MythScreenStack *>::const_reverse_iterator it;
1825 for (it = m_priv->m_stackList.rbegin(); it != m_priv->m_stackList.rend(); it++)
1826 {
1827 MythScreenType *screen = (*it)->GetTopScreen();
1828 if (!screen || !screen->ContainsPoint(point))
1829 continue;
1830
1831 if (screen->gestureEvent(gesture))
1832 {
1833 handled = true;
1834 break;
1835 }
1836 // Note: The following break prevents clicks being
1837 // sent to windows below popups
1838 //
1839 // we want to permit this in some cases, e.g.
1840 // when the music miniplayer is on screen or a
1841 // non-interactive alert/news scroller. So these
1842 // things need to be in a third or more stack
1843 if ((*it)->objectName() == "popup stack")
1844 break;
1845 }
1846
1847 if (handled)
1848 delete gesture;
1849 else
1850 QCoreApplication::postEvent(this, gesture);
1851 }
1852
1853 return true;
1854 }
1855 break;
1856 }
1857 case QEvent::MouseMove:
1858 {
1860 ShowMouseCursor(true);
1861 if (m_priv->m_gesture.Recording())
1862 {
1863 // Reset the timer
1864 m_priv->m_gestureTimer->stop();
1866 auto * mouseevent = dynamic_cast<QMouseEvent*>(Event);
1867 if (!mouseevent)
1868 return MythUIScreenBounds::eventFilter(Watched, Event);
1869 m_priv->m_gesture.Record(mouseevent->pos(), mouseevent->button());
1870 return true;
1871 }
1872 break;
1873 }
1874 case QEvent::Wheel:
1875 {
1877 ShowMouseCursor(true);
1878 auto * wheel = dynamic_cast<QWheelEvent*>(Event);
1879 if (wheel == nullptr)
1880 return MythUIScreenBounds::eventFilter(Watched, Event);
1881 int delta = wheel->angleDelta().y();
1882 if (delta>0)
1883 {
1884 wheel->accept();
1885 auto *key = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier);
1886 if (auto * target = GetTarget(*key); target)
1887 QCoreApplication::postEvent(target, key);
1888 else
1889 QCoreApplication::postEvent(this, key);
1890 }
1891 if (delta < 0)
1892 {
1893 wheel->accept();
1894 auto * key = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier);
1895 if (auto * target = GetTarget(*key); !target)
1896 QCoreApplication::postEvent(target, key);
1897 else
1898 QCoreApplication::postEvent(this, key);
1899 }
1900 break;
1901 }
1902 default:
1903 break;
1904 }
1905
1906 return MythUIScreenBounds::eventFilter(Watched, Event);
1907}
1908
1910{
1911 if (Event->type() == MythGestureEvent::kEventType)
1912 {
1913 auto * gesture = dynamic_cast<MythGestureEvent*>(Event);
1914 if (gesture == nullptr)
1915 return;
1916 if (auto * toplevel = GetMainStack(); toplevel)
1917 if (auto * screen = toplevel->GetTopScreen(); screen)
1918 screen->gestureEvent(gesture);
1919 LOG(VB_GUI, LOG_DEBUG, QString("Gesture: %1 (Button: %2)")
1920 .arg(gesture->GetName(), gesture->GetButtonName()));
1921 }
1923 {
1925 }
1926 else if (Event->type() == ExternalKeycodeEvent::kEventType)
1927 {
1928 auto * event = dynamic_cast<ExternalKeycodeEvent *>(Event);
1929 if (event == nullptr)
1930 return;
1931 auto key = QKeyEvent(QEvent::KeyPress, event->getKeycode(), Qt::NoModifier);
1932 if (auto * target = GetTarget(key); target)
1933 QCoreApplication::sendEvent(target, &key);
1934 else
1935 QCoreApplication::sendEvent(this, &key);
1936 }
1937 else if (Event->type() == MythMediaEvent::kEventType)
1938 {
1939 auto *me = dynamic_cast<MythMediaEvent*>(Event);
1940 if (me == nullptr)
1941 return;
1942
1943 // A listener based system might be more efficient, but we should never
1944 // have that many screens open at once so impact should be minimal.
1945 //
1946 // This approach is simpler for everyone to follow. Plugin writers
1947 // don't have to worry about adding their screens to the list because
1948 // all screens receive media events.
1949 //
1950 // Events are even sent to hidden or backgrounded screens, this avoids
1951 // the need for those to poll for changes when they become visible again
1952 // however this needs to be kept in mind if media changes trigger
1953 // actions which would not be appropriate when the screen doesn't have
1954 // focus. It is the programmers responsibility to ignore events when
1955 // necessary.
1956 for (auto * widget : std::as_const(m_priv->m_stackList))
1957 {
1958 QVector<MythScreenType*> screenList;
1959 widget->GetScreenList(screenList);
1960 for (auto * screen : std::as_const(screenList))
1961 if (screen)
1962 screen->mediaEvent(me);
1963 }
1964
1965 // Debugging
1966 if (MythMediaDevice* device = me->getDevice(); device)
1967 {
1968 LOG(VB_GENERAL, LOG_DEBUG, QString("Media Event: %1 - %2")
1969 .arg(device->getDevicePath()).arg(device->getStatus()));
1970 }
1971 }
1973 {
1975 }
1977 {
1979 }
1980 else if (Event->type() == MythEvent::kLockInputDevicesEventType)
1981 {
1983 PauseIdleTimer(true);
1984 }
1986 {
1988 PauseIdleTimer(false);
1989 }
1991 {
1993 }
1995 {
1997 }
1998 else if (Event->type() == MythEvent::kMythEventMessage)
1999 {
2000 auto * event = dynamic_cast<MythEvent *>(Event);
2001 if (event == nullptr)
2002 return;
2003
2004 QString message = event->Message();
2005 if (message.startsWith(ACTION_HANDLEMEDIA))
2006 {
2007 if (event->ExtraDataCount() == 1)
2008 HandleMedia("Internal", event->ExtraData(0));
2009 else if (event->ExtraDataCount() >= 11)
2010 {
2011 bool usebookmark = true;
2012 if (event->ExtraDataCount() >= 12)
2013 usebookmark = (event->ExtraData(11).toInt() != 0);
2014 HandleMedia("Internal", event->ExtraData(0),
2015 event->ExtraData(1), event->ExtraData(2),
2016 event->ExtraData(3), event->ExtraData(4),
2017 event->ExtraData(5).toInt(), event->ExtraData(6).toInt(),
2018 event->ExtraData(7), std::chrono::minutes(event->ExtraData(8).toInt()),
2019 event->ExtraData(9), event->ExtraData(10),
2020 usebookmark);
2021 }
2022 else
2023 {
2024 LOG(VB_GENERAL, LOG_ERR, "Failed to handle media");
2025 }
2026 }
2027 else if (message.startsWith(ACTION_SCREENSHOT))
2028 {
2029 int width = 0;
2030 int height = 0;
2031 QString filename;
2032 if (event->ExtraDataCount() >= 2)
2033 {
2034 width = event->ExtraData(0).toInt();
2035 height = event->ExtraData(1).toInt();
2036 if (event->ExtraDataCount() == 3)
2037 filename = event->ExtraData(2);
2038 }
2039 ScreenShot(width, height, filename);
2040 }
2041 else if (message == ACTION_GETSTATUS)
2042 {
2043 QVariantMap state;
2044 state.insert("state", "idle");
2045 state.insert("menutheme", GetMythDB()->GetSetting("menutheme", "defaultmenu"));
2046 state.insert("currentlocation", GetMythUI()->GetCurrentLocation());
2048 }
2049 else if (message == "CLEAR_SETTINGS_CACHE")
2050 {
2051 // update the idle time
2052 m_idleTime =
2053 gCoreContext->GetDurSetting<std::chrono::minutes>("FrontendIdleTimeout",
2055
2056 if (m_idleTime < 0min)
2057 m_idleTime = 0min;
2058 m_idleTimer.stop();
2059 if (m_idleTime > 0min)
2060 {
2061 m_idleTimer.setInterval(m_idleTime);
2062 m_idleTimer.start();
2063 LOG(VB_GENERAL, LOG_INFO, QString("Updating the frontend idle time to: %1 mins").arg(m_idleTime.count()));
2064 }
2065 else
2066 {
2067 LOG(VB_GENERAL, LOG_INFO, "Frontend idle timeout is disabled");
2068 }
2069 }
2070 else if (message == "NOTIFICATION")
2071 {
2074 return;
2075 }
2076 else if (message == "RECONNECT_SUCCESS" && m_priv->m_standby)
2077 {
2078 // If the connection to the master backend has just been (re-)established
2079 // but we're in standby, make sure the backend is not blocked from
2080 // shutting down.
2082 }
2083 }
2084 else if (Event->type() == MythEvent::kMythUserMessage)
2085 {
2086 if (auto * event = dynamic_cast<MythEvent *>(Event); event != nullptr)
2087 if (const QString& message = event->Message(); !message.isEmpty())
2088 ShowOkPopup(message);
2089 }
2091 {
2093 }
2094}
2095
2096QObject* MythMainWindow::GetTarget(QKeyEvent& Key)
2097{
2098 auto * target = QWidget::keyboardGrabber();
2099 if (!target)
2100 {
2101 if (auto * widget = QApplication::focusWidget(); widget && widget->isEnabled())
2102 {
2103 target = widget;
2104 // Yes this is special code for handling the
2105 // the escape key.
2106 if (Key.key() == m_priv->m_escapekey && widget->topLevelWidget())
2107 target = widget->topLevelWidget();
2108 }
2109 }
2110
2111 if (!target)
2112 target = this;
2113 return target;
2114}
2115
2117{
2119}
2120
2122{
2123 if (Show && GetMythDB()->GetBoolSetting("HideMouseCursor", false))
2124 return;
2125
2126 // Set cursor call must come after Show() to work on some systems.
2127 setCursor(Show ? (Qt::ArrowCursor) : (Qt::BlankCursor));
2128 if (Show)
2129 m_priv->m_hideMouseTimer->start();
2130}
2131
2133{
2134 ShowMouseCursor(false);
2135}
2136
2141{
2142 m_priv->m_disableIdle = DisableIdle;
2143 if (m_priv->m_disableIdle)
2144 m_idleTimer.stop();
2145 else
2146 m_idleTimer.start();
2147}
2148
2153{
2154 if (m_priv->m_disableIdle)
2155 return;
2156
2157 if (m_idleTime == 0min || !m_idleTimer.isActive() || (m_priv->m_standby && m_priv->m_enteringStandby))
2158 return;
2159
2160 if (m_priv->m_standby)
2161 ExitStandby(false);
2162
2163 m_idleTimer.start();
2164}
2165
2170{
2171 if (m_priv->m_disableIdle)
2172 return;
2173
2174 // don't do anything if the idle timer is disabled
2175 if (m_idleTime == 0min)
2176 return;
2177
2178 if (Pause)
2179 {
2180 LOG(VB_GENERAL, LOG_NOTICE, "Suspending idle timer");
2181 m_idleTimer.stop();
2182 }
2183 else
2184 {
2185 LOG(VB_GENERAL, LOG_NOTICE, "Resuming idle timer");
2186 m_idleTimer.start();
2187 }
2188
2189 // ResetIdleTimer();
2190}
2191
2193{
2194 if (m_priv->m_disableIdle)
2195 return;
2196
2197 m_priv->m_enteringStandby = false;
2198
2199 if (m_idleTime > 0min && !m_priv->m_standby)
2200 {
2201 LOG(VB_GENERAL, LOG_NOTICE,
2202 QString("Entering standby mode after %1 minutes of inactivity").arg(m_idleTime.count()));
2203 EnterStandby(false);
2204 if (gCoreContext->GetNumSetting("idleTimeoutSecs", 0) > 0)
2205 {
2206 m_priv->m_enteringStandby = true;
2207 JumpTo("Standby Mode");
2208 }
2209 }
2210}
2211
2213{
2214 if (Manual && m_priv->m_enteringStandby)
2215 m_priv->m_enteringStandby = false;
2216
2217 if (m_priv->m_standby)
2218 return;
2219
2220 // We've manually entered standby mode and we want to pause the timer
2221 // to prevent it being Reset
2222 if (Manual)
2223 {
2224 PauseIdleTimer(true);
2225 LOG(VB_GENERAL, LOG_NOTICE, QString("Entering standby mode"));
2226 }
2227
2228 m_priv->m_standby = true;
2230
2231 QVariantMap state;
2232 state.insert("state", "standby");
2233 state.insert("menutheme", GetMythDB()->GetSetting("menutheme", "defaultmenu"));
2234 state.insert("currentlocation", GetMythUI()->GetCurrentLocation());
2236
2237 // Cache WOL settings in case DB goes down
2238 QString masterserver = gCoreContext->GetSetting("MasterServerName");
2239 gCoreContext->GetSettingOnHost("BackendServerAddr", masterserver);
2241 gCoreContext->GetSetting("WOLbackendCommand", "");
2242
2243 // While in standby do not attempt to wake the backend
2245}
2246
2248{
2250 return;
2251
2252 if (Manual)
2253 PauseIdleTimer(false);
2254 else if (gCoreContext->GetNumSetting("idleTimeoutSecs", 0) > 0)
2255 JumpTo("Main Menu");
2256
2257 if (!m_priv->m_standby)
2258 return;
2259
2260 LOG(VB_GENERAL, LOG_NOTICE, "Leaving standby mode");
2261 m_priv->m_standby = false;
2262
2263 // We may attempt to wake the backend
2266
2267 QVariantMap state;
2268 state.insert("state", "idle");
2269 state.insert("menutheme", GetMythDB()->GetSetting("menutheme", "defaultmenu"));
2270 state.insert("currentlocation", GetMythUI()->GetCurrentLocation());
2272}
2273
2275{
2276 LOG(VB_GENERAL, LOG_NOTICE, QString("Application State Changed to %1").arg(State));
2277 switch (State)
2278 {
2279 case Qt::ApplicationState::ApplicationActive:
2281 break;
2282 case Qt::ApplicationState::ApplicationSuspended:
2284 break;
2285 default:
2286 break;
2287 }
2288}
2289/* 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="")
T GetDurSetting(const QString &key, T defaultval=T::zero())
void BlockShutdown(void)
void dispatch(const MythEvent &event)
void SetGUIObject(QObject *gui)
int GetNumSetting(const QString &key, int defaultval=0)
void AllowShutdown(void)
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:339
QString m_destination
void(* m_callback)(void)
State
Definition: zmserver.h:69