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