MythTV master
mythdisplay.cpp
Go to the documentation of this file.
1// Std
2#include <algorithm>
3#include <ranges>
4
5//Qt
6#include <QtGlobal>
7#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
8#include <QtEnvironmentVariables>
9#include <QtSystemDetection>
10#endif
11#include <QTimer>
12#include <QThread>
13#include <QApplication>
14#include <QElapsedTimer>
15#include <QWindow>
16
17#include "libmythbase/mythconfig.h"
18
19#if CONFIG_QTWEBENGINE
20#include <QQuickWindow>
21#endif
22
23// MythTV
24#include "libmythbase/compat.h"
27#include "mythuihelper.h"
28#include "mythdisplay.h"
29#include "opengl/mythegl.h"
30#include "mythmainwindow.h"
31
32#if CONFIG_QTDBUS
34#endif
35#if CONFIG_WAYLANDEXTRAS
37#endif
38#ifdef Q_OS_ANDROID
40#endif
41#ifdef Q_OS_DARWIN
43#endif
44#if CONFIG_X11
48#endif
49#if CONFIG_DRM
52#endif
53#ifdef Q_OS_WINDOWS
55#endif
56#if CONFIG_MMAL
58#endif
59
60#define LOC QString("Display: ")
61
96MythDisplay* MythDisplay::Create([[maybe_unused]] MythMainWindow* MainWindow)
97{
98 MythDisplay* result = nullptr;
99#if CONFIG_X11
101 result = new MythDisplayX11();
102#endif
103#if CONFIG_QTDBUS
104 // Disabled for now as org.gnome.Mutter.DisplayConfig.ApplyConfiguration does
105 // not seem to be actually implemented by anyone.
106#if CONFIG_WAYLANDEXTRAS
107 //if (MythWaylandDevice::IsAvailable())
108#endif
109 //{
110 // if (!result)
111 // result = MythDisplayMutter::Create();
112 //}
113#endif
114#if CONFIG_DRM
115 if (!result)
116 {
117 result = new MythDisplayDRM(MainWindow);
118 // On the Pi, use MythDisplayRPI if mode switching is not available via DRM
119#if CONFIG_MMAL
120 if (!result->VideoModesAvailable())
121 {
122 delete result;
123 result = nullptr;
124 }
125#endif
126 }
127#endif
128#if CONFIG_MMAL
129 if (!result)
130 result = new MythDisplayRPI();
131#endif
132#ifdef Q_OS_DARWIN
133 if (!result)
134 result = new MythDisplayOSX();
135#endif
136#ifdef Q_OS_ANDROID
137 if (!result)
138 result = new MythDisplayAndroid();
139#endif
140#ifdef Q_OS_WINDOWS
141 if (!result)
142 result = new MythDisplayWindows();
143#endif
144 if (!result)
145 result = new MythDisplay();
146 return result;
147}
148
150{
151 QStringList result;
152 result.reserve(7);
153 bool spanall = false;
154 int screencount = MythDisplay::GetScreenCount();
155 if (MythDisplay::SpanAllScreens() && screencount > 1)
156 {
157 spanall = true;
158 result.append(tr("Spanning %1 screens").arg(screencount));
159 result.append(tr("Total bounds") + QString("\t: %1x%2")
160 .arg(GetScreenBounds().width()).arg(GetScreenBounds().height()));
161 result.append("");
162 }
163
164 if (m_hdrState)
165 {
166 auto types = m_hdrState->m_supportedTypes;
167 auto hdr = m_hdrState->TypesToString();
168 result.append(tr("Supported HDR formats\t: %1").arg(hdr.join(",")));
169 if (types && !m_hdrState->IsControllable())
170 result.append(tr("HDR mode switching is not available"));
171 if (auto brightness = m_hdrState->GetMaxLuminance(); brightness > 1.0)
172 result.append(tr("Max display brightness\t: %1 nits").arg(static_cast<int>(brightness)));
173 }
174
175 if (m_vrrState)
176 {
177 result.append(tr("Variable refresh rate '%1': %2 %3")
178 .arg(m_vrrState->TypeToString(),
179 m_vrrState->Enabled() ? tr("Enabled") : tr("Disabled"),
180 m_vrrState->RangeDescription()));
181 }
182
183 auto * current = GetCurrentScreen();
184 const auto screens = QGuiApplication::screens();
185 bool first = true;
186 // This reservation is an approximation. One screen, four video modes.
187 result.reserve(result.size() + (9 * screens.size()));
188 for (auto *screen : std::as_const(screens))
189 {
190 if (!first)
191 result.append("");
192 first = false;
193 auto id = QString("(%1)").arg(screen->manufacturer());
194 if (screen == current && !spanall)
195 result.append(tr("Current screen\t: %1 %2").arg(screen->name(), id));
196 else
197 result.append(tr("Screen\t\t: %1 %2").arg(screen->name(), id));
198 result.append(tr("Size") + QString("\t\t: %1mmx%2mm")
199 .arg(screen->physicalSize().width()).arg(screen->physicalSize().height()));
200 if (screen == current)
201 {
202 QString source;
203 auto aspect = GetAspectRatio(source);
204 result.append(tr("Aspect ratio") + QString("\t: %1 (%2)")
205 .arg(aspect, 0, 'f', 3).arg(source));
206 if (!spanall)
207 {
208 result.append(tr("Current mode") + QString("\t: %1x%2@%3Hz")
209 .arg(GetResolution().width()).arg(GetResolution().height())
210 .arg(GetRefreshRate(), 0, 'f', 2));
211 const auto & modes = GetVideoModes();
212 if (!modes.empty())
213 {
214 result.append(tr("Available modes:"));
215 for (const auto & mode : std::ranges::reverse_view(modes))
216 result.append(" " + mode.ToString());
217 }
218 }
219 }
220 }
221
222 return result;
223}
224
226 : m_screen(GetDesiredScreen())
227{
228 DebugScreen(m_screen, "Using");
229 if (m_screen)
230 {
231 connect(m_screen, &QScreen::geometryChanged, this, &MythDisplay::GeometryChanged);
232 connect(m_screen, &QScreen::physicalDotsPerInchChanged, this, &MythDisplay::PhysicalDPIChanged);
233 }
234
235 auto *guiapp = qobject_cast<QGuiApplication *>(QCoreApplication::instance());
236 if (guiapp == nullptr)
237 return;
238
239 connect(guiapp, &QGuiApplication::screenRemoved, this, &MythDisplay::ScreenRemoved);
240 connect(guiapp, &QGuiApplication::screenAdded, this, &MythDisplay::ScreenAdded);
241 connect(guiapp, &QGuiApplication::primaryScreenChanged, this, &MythDisplay::PrimaryScreenChanged);
242}
243
245{
246 LOG(VB_GENERAL, LOG_INFO, LOC + "Deleting");
247}
248
262void MythDisplay::SetWidget(QWidget *MainWindow)
263{
264 QWidget* oldwidget = m_widget;
265 m_widget = MainWindow;
266 if (!m_modeComplete)
268
269 QWindow* oldwindow = m_window;
270 if (m_widget)
271 m_window = m_widget->windowHandle();
272 else
273 m_window = nullptr;
274
275 if (m_widget && (m_widget != oldwidget))
276 LOG(VB_GENERAL, LOG_INFO, LOC + "Have main widget");
277
278 if (m_window && (m_window != oldwindow))
279 {
280 LOG(VB_GENERAL, LOG_INFO, LOC + "Have main window");
281
282 connect(m_window, &QWindow::screenChanged, this, &MythDisplay::ScreenChanged, Qt::UniqueConnection);
283 QScreen *desired = GetDesiredScreen();
284 // If we have changed the video mode for the old screen then reset
285 // it to the default/desktop mode
286 if (oldwindow)
288 // Ensure we completely re-initialise when the new screen is set
289 m_initialised = false;
290 if (desired != m_screen)
291 DebugScreen(desired, "Moving to");
292 m_window->setScreen(desired);
293 // WaitForNewScreen doesn't work as intended. It successfully filters
294 // out unwanted screenChanged signals after moving screens - but always
295 //times out. This just delays startup by 500ms - so ignore on startup as it isn't needed.
298 m_firstScreenChange = false;
300 return;
301 }
302}
303
305{
306 return QGuiApplication::screens().size();
307}
308
310{
311 if (m_physicalSize.isEmpty() || m_resolution.isEmpty())
312 return 1.0;
313
314 // HD-Ready or better displays always have square pixels
315 if (m_resolution.height() >= 720)
316 return 1.0;
317
318 return (m_physicalSize.width() / static_cast<double>(m_resolution.width())) /
319 (m_physicalSize.height() / static_cast<double>(m_resolution.height()));
320}
321
323{
324 return m_guiMode.Resolution();
325}
326
328{
329 return m_screenBounds;
330}
331
345{
346 return m_screen;
347}
348
350{
351 return m_window;
352}
353
355{
356 QScreen* newscreen = nullptr;
357
358 // If geometry is overriden at the command line level, try and determine
359 // which screen that applies to (if any).
360 // N.B. So many potential issues here e.g. should the geometry override be
361 // ignored after first use? (as it will continue to override the screen
362 // regardless of changes to screen preference).
364 {
365 // this matches the check in MythMainWindow
366 bool windowed = GetMythDB()->GetBoolSetting("RunFrontendInWindow", false) &&
368 QRect override = MythMainWindow::GetGeometryOverride();
369 // When windowed, we use topleft as a best guess as to which screen we belong in.
370 // When fullscreen, Qt appears to use the reverse - though this may be
371 // the window manager rather than Qt. So could be wrong.
372 QPoint point = windowed ? override.topLeft() : override.bottomRight();
373 QList screens = QGuiApplication::screens();
374 for (QScreen *screen : std::as_const(screens))
375 {
376 if (screen->geometry().contains(point))
377 {
378 newscreen = screen;
379 LOG(VB_GENERAL, LOG_INFO, LOC + QString(
380 "Geometry override places window in screen '%1'").arg(newscreen->name()));
381 break;
382 }
383 }
384 }
385
386 // If spanning all screens, then always use the primary display
387 if (!newscreen && MythDisplay::SpanAllScreens())
388 {
389 LOG(VB_GENERAL, LOG_INFO, LOC + "Using primary screen for multiscreen");
390 newscreen = QGuiApplication::primaryScreen();
391 }
392
393 QString name = gCoreContext->GetSetting("XineramaScreen", nullptr);
394 // Lookup by name
395 if (!newscreen)
396 {
397 QList screens = QGuiApplication::screens();
398 for (QScreen *screen : std::as_const(screens))
399 {
400 if (!name.isEmpty() && name == screen->name())
401 {
402 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Found screen '%1'").arg(name));
403 newscreen = screen;
404 }
405 }
406 }
407
408 // No name match. These were previously numbers.
409 if (!newscreen)
410 {
411 bool ok = false;
412 int screen_num = name.toInt(&ok);
413 QList<QScreen *>screens = QGuiApplication::screens();
414 if (ok && (screen_num >= 0) && (screen_num < screens.size()))
415 {
416 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Found screen number %1 (%2)")
417 .arg(name, screens[screen_num]->name()));
418 newscreen = screens[screen_num];
419 }
420 }
421
422 // For anything else, return the primary screen.
423 if (!newscreen)
424 {
425 QScreen *primary = QGuiApplication::primaryScreen();
426 if (name.isEmpty() && primary)
427 {
428 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Defaulting to primary screen (%1)")
429 .arg(primary->name()));
430 }
431 else if (name != "-1" && primary)
432 {
433 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Screen '%1' not found, defaulting to primary screen (%2)")
434 .arg(name, primary->name()));
435 }
436 newscreen = primary;
437 }
438
439 return newscreen;
440}
441
444void MythDisplay::ScreenChanged(QScreen *qScreen)
445{
446 if (qScreen == m_screen)
447 return;
448 if (m_screen)
449 disconnect(m_screen, nullptr, this, nullptr);
450 DebugScreen(qScreen, "Changed to");
451 m_screen = qScreen;
452 connect(m_screen, &QScreen::geometryChanged, this, &MythDisplay::GeometryChanged);
453 connect(m_screen, &QScreen::physicalDotsPerInchChanged, this, &MythDisplay::PhysicalDPIChanged);
454 Initialise();
455 emit DisplayChanged();
456}
457
459{
460 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Qt screen pixel ratio changed to %1")
461 .arg(DPI, 2, 'f', 2, '0'));
462 emit DisplayChanged();
463}
464
466{
467 DebugScreen(qScreen, "New primary");
468}
469
470void MythDisplay::ScreenAdded(QScreen* qScreen)
471{
472 DebugScreen(qScreen, "New");
473 emit ScreenCountChanged(QGuiApplication::screens().size());
474}
475
476void MythDisplay::ScreenRemoved(QScreen* qScreen)
477{
478 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Screen '%1' removed").arg(qScreen->name()));
479 emit ScreenCountChanged(QGuiApplication::screens().size());
480}
481
482void MythDisplay::GeometryChanged(const QRect Geo)
483{
484 LOG(VB_GENERAL, LOG_INFO, LOC + QString("New screen geometry: %1x%2+%3+%4")
485 .arg(Geo.width()).arg(Geo.height()).arg(Geo.left()).arg(Geo.top()));
486}
487
495{
496 // Certain platform implementations do not have a window to access at startup
497 // and hence use this implementation. Flag the status as incomplete to ensure
498 // we try to retrieve the full details at the first opportunity.
499 m_modeComplete = false;
500 m_edid = MythEDID();
501 QScreen *screen = GetCurrentScreen();
502 if (!screen)
503 {
504 m_refreshRate = 60.0;
505 m_physicalSize = QSize(0, 0);
506 m_resolution = QSize(1920, 1080);
507 return;
508 }
509 m_refreshRate = screen->refreshRate();
510 m_resolution = screen->size();
511 m_physicalSize = QSize(static_cast<int>(screen->physicalSize().width()),
512 static_cast<int>(screen->physicalSize().height()));
513}
514
517{
518 return gCoreContext->GetSetting("XineramaScreen", nullptr) == "-1";
519}
520
521QString MythDisplay::GetExtraScreenInfo(QScreen *qScreen)
522{
523 QString mfg = qScreen->manufacturer();
524 if (mfg.isEmpty())
525 mfg = "Unknown";
526 QString model = qScreen->model();
527 if (model.isEmpty())
528 model = "Unknown";
529 return QString("(Make: %1 Model: %2)").arg(mfg, model);
530}
531
532void MythDisplay::DebugScreen(QScreen *qScreen, const QString &Message)
533{
534 if (!qScreen)
535 return;
536
537 auto geom = qScreen->geometry();
538 LOG(VB_GENERAL, LOG_INFO, LOC + QString("%1 screen '%2' %3")
539 .arg(Message, qScreen->name(), GetExtraScreenInfo(qScreen)));
540 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Qt screen pixel ratio: %1")
541 .arg(qScreen->devicePixelRatio(), 2, 'f', 2, '0'));
542 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Geometry: %1x%2+%3+%4 Size(Qt): %5mmx%6mm")
543 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top())
544 .arg(qScreen->physicalSize().width()).arg(qScreen->physicalSize().height()));
545
546 if (qScreen->virtualGeometry() != geom)
547 {
548 geom = qScreen->virtualGeometry();
549 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Total virtual geometry: %1x%2+%3+%4")
550 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top()));
551 }
552}
553
555{
556 m_videoModes.clear();
557 m_overrideVideoModes.clear();
559 // Note: The EDID is retrieved in UpdateCurrentMode and we need the EDID to
560 // check for refresh rate range support.
563
564 // Set the desktop mode - which is the mode at startup. We must always return
565 // the screen to this mode.
566 if (!m_initialised)
567 {
568 // Only ever set this once or after a screen change
569 m_initialised = true;
571 LOG(VB_GENERAL, LOG_NOTICE, LOC + QString("Desktop video mode: %1x%2 %3Hz")
572 .arg(m_resolution.width()).arg(m_resolution.height()).arg(m_refreshRate, 0, 'f', 3));
573 if (m_edid.Valid())
574 {
575 if (m_edid.IsSRGB())
576 LOG(VB_GENERAL, LOG_NOTICE, LOC + "Display is using sRGB colourspace");
577 else
578 LOG(VB_GENERAL, LOG_NOTICE, LOC + "Display has custom colourspace");
579
580 InitHDR();
581 }
582 }
583
584 // Set the gui mode from database settings
585 int pixelwidth = m_resolution.width();
586 int pixelheight = m_resolution.height();
587 int mmwidth = m_physicalSize.width();
588 int mmheight = m_physicalSize.height();
589 double refreshrate = m_refreshRate;
590 double aspectratio = 0.0;
591 GetMythDB()->GetResolutionSetting("GuiVidMode", pixelwidth, pixelheight, aspectratio, refreshrate);
592 GetMythDB()->GetResolutionSetting("DisplaySize", mmwidth, mmheight);
593 m_guiMode = MythDisplayMode(pixelwidth, pixelheight, mmwidth, mmheight, -1.0, refreshrate);
594
595 // Set default video mode
596 pixelwidth = pixelheight = 0;
597 GetMythDB()->GetResolutionSetting("TVVidMode", pixelwidth, pixelheight, aspectratio, refreshrate);
598 m_videoMode = MythDisplayMode(pixelwidth, pixelheight, mmwidth, mmheight, aspectratio, refreshrate);
599
600 // Initialise video override modes
601 for (int i = 0; true; ++i)
602 {
603 int iw = 0;
604 int ih = 0;
605 int ow = 0;
606 int oh = 0;
607 double iaspect = 0.0;
608 double oaspect = 0.0;
609 double irate = 0.0;
610 double orate = 0.0;
611
612 GetMythDB()->GetResolutionSetting("VidMode", iw, ih, iaspect, irate, i);
613 GetMythDB()->GetResolutionSetting("TVVidMode", ow, oh, oaspect, orate, i);
614
615 if ((!iw && !ih && qFuzzyIsNull(irate)) || !(ih && ow && oh))
616 break;
617
618 uint64_t key = MythDisplayMode::CalcKey(QSize(iw, ih), irate);
619 MythDisplayMode scr(QSize(ow, oh), QSize(mmwidth, mmheight), oaspect, orate);
620 m_overrideVideoModes[key] = scr;
621 }
622}
623
624
633{
634 const auto screens = QGuiApplication::screens();
635 for (auto * screen : std::as_const(screens))
636 {
637 auto dim = screen->geometry();
638 auto extra = MythDisplay::GetExtraScreenInfo(screen);
639 LOG(VB_GUI, LOG_INFO, LOC + QString("Screen %1: %2x%3 %4")
640 .arg(screen->name()).arg(dim.width()).arg(dim.height()).arg(extra));
641 }
642
643 const auto * primary = QGuiApplication::primaryScreen();
644 if (!primary)
645 {
646 if (!screens.empty())
647 primary = screens.front();
648 if (!primary)
649 {
650 LOG(VB_GENERAL, LOG_ERR, LOC + "Qt has no screens!");
651 return;
652 }
653 }
654
655 LOG(VB_GUI, LOG_INFO, LOC +QString("Primary screen: %1.").arg(primary->name()));
656
657 auto numScreens = MythDisplay::GetScreenCount();
658 auto dim = primary->virtualSize();
659 LOG(VB_GUI, LOG_INFO, LOC + QString("Total desktop dim: %1x%2, over %3 screen[s].")
660 .arg(dim.width()).arg(dim.height()).arg(numScreens));
661
663 {
664 LOG(VB_GUI, LOG_INFO, LOC + QString("Using entire desktop."));
665 m_screenBounds = primary->virtualGeometry();
666 return;
667 }
668
669 if (!GetMythDB()->GetBoolSetting("ForceFullScreen", false) &&
670 GetMythDB()->GetBoolSetting("RunFrontendInWindow", false))
671 {
672 LOG(VB_GUI, LOG_INFO, LOC + "Running in a window");
673 // This doesn't include the area occupied by the
674 // Windows taskbar, or the Mac OS X menu bar and Dock
675 m_screenBounds = m_screen->availableGeometry();
676 }
677 else
678 {
679 m_screenBounds = m_screen->geometry();
680 }
681
682 LOG(VB_GUI, LOG_INFO, LOC + QString("Using screen %1: %2x%3 at %4+%5")
683 .arg(m_screen->name()).arg(m_screenBounds.width()).arg(m_screenBounds.height())
684 .arg(m_screenBounds.left()).arg(m_screenBounds.top()));
685}
686
694{
695 return Size.width() > m_resolution.width() || Size.height() > m_resolution.height();
696}
697
703{
705 if (current == m_desktopMode)
706 return;
708}
709
713bool MythDisplay::SwitchToVideo(QSize Size, double Rate)
714{
715 if (!m_modeComplete)
717
720 double targetrate = 0.0;
721 double aspectoverride = 0.0;
722
723 // try to find video override mode
725 Size.width(), Size.height(), Rate);
726
727 if (key != 0)
728 {
729 next = m_overrideVideoModes[key];
730 if (next.AspectRatio() > 0.0)
731 aspectoverride = next.AspectRatio();
732 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Found custom screen override %1x%2 Aspect %3")
733 .arg(next.Width()).arg(next.Height()).arg(aspectoverride));
734 }
735
736 // If requested refresh rate is 0, attempt to match video fps
737 if (qFuzzyIsNull(next.RefreshRate()))
738 {
739 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Trying to match best refresh rate %1Hz")
740 .arg(Rate, 0, 'f', 3));
741 next.SetRefreshRate(Rate);
742 }
743
744 // need to change video mode?
745 (void)MythDisplayMode::FindBestMatch(GetVideoModes(), next, targetrate);
746
747 // If GSync or FreeSync are enabled, ignore refresh rate only changes.
748 // N.B. This check is not used when switching to GUI (which already ignores
749 // rate only changes) or switching back to the desktop (where we must reset
750 // the display to the original state).
751 if (m_vrrState && m_vrrState->Enabled())
752 {
753 if (next.Resolution() == current.Resolution())
754 {
755 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Ignoring mode switch to %1Hz - VRR enabled")
756 .arg(Rate, 0, 'f', 3));
757 return true;
758 }
759 LOG(VB_GENERAL, LOG_INFO, LOC + "Allowing mode switch with VRR enabled for new resolution");
760 }
761
762 // No need for change
763 if ((next == current) && (MythDisplayMode::CompareRates(current.RefreshRate(), targetrate)))
764 {
765 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Using current mode %1x%2@%3Hz")
766 .arg(m_resolution.width()).arg(m_resolution.height()).arg(m_refreshRate));
767 return true;
768 }
769
770 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Trying mode %1x%2@%3Hz")
771 .arg(next.Width()).arg(next.Height()).arg(next.RefreshRate(), 0, 'f', 3));
772
773 if (!SwitchToVideoMode(next.Resolution(), targetrate))
774 {
775 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to change mode to %1x%2@%3Hz")
776 .arg(next.Width()).arg(next.Height()).arg(next.RefreshRate(), 0, 'f', 3));
777 return false;
778 }
779
780 if (next.Resolution() != m_resolution)
782
783 // N.B. We used a computed aspect ratio unless overridden
784 m_aspectRatioOverride = aspectoverride > 0.0 ? aspectoverride : 0.0;
786 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Switched to %1x%2@%3Hz for video %4x%5")
787 .arg(m_resolution.width()).arg(m_resolution.height())
788 .arg(m_refreshRate, 0, 'f', 3).arg(Size.width()).arg(Size.height()));
790 return true;
791}
792
796{
797 if (!m_modeComplete)
799
800 // If the current resolution is the same as the GUI resolution then do nothing
801 // as refresh rate should not be critical for the GUI.
803 {
804 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Using %1x%2@%3Hz for GUI")
805 .arg(m_resolution.width()).arg(m_resolution.height()).arg(m_refreshRate));
806 return true;
807 }
808
810 {
811 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to change mode to %1x%2@%3Hz")
812 .arg(m_guiMode.Width()).arg(m_guiMode.Height()).arg(m_guiMode.RefreshRate(), 0, 'f', 3));
813 return false;
814 }
815
816 if (Wait && (m_resolution != m_guiMode.Resolution()))
818
821 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Switched to %1x%2@%3Hz")
822 .arg(m_resolution.width()).arg(m_resolution.height()).arg(m_refreshRate, 0, 'f', 3));
823 return true;
824}
825
827{
828 return m_refreshRate;
829}
830
831std::chrono::microseconds MythDisplay::GetRefreshInterval(std::chrono::microseconds Fallback) const
832{
833 // If FreeSync or GSync are enabled, return the maximum refresh rate.
834 // N.B. This may need more work as the max may not be well defined - especially
835 // if the resolution is changing. Displays should however support at least 60Hz
836 // at all resolutions which should be fine in the vast majority of cases (as the
837 // only place the refresh interval is functionally important is in checking
838 // for double rate deinterlacing support).
839 if (m_vrrState && m_vrrState->Enabled())
840 {
841 const auto range = m_vrrState->GetRange();
842 auto max = std::get<1>(range) > 60 ? std::get<1>(range) : 60;
843 return microsecondsFromFloat(1000000.0 / max);
844 }
845
846 if (m_refreshRate > 20.0 && m_refreshRate < 200.0)
847 return microsecondsFromFloat(1000000.0 / m_refreshRate);
848 if (Fallback > 33ms) // ~30Hz
849 Fallback /= 2;
850 return Fallback;
851}
852
854{
855 auto targetrate = static_cast<double>(NAN);
856 const MythDisplayMode mode(Size, QSize(0, 0), -1.0, 0.0);
857 const auto & modes = GetVideoModes();
858 int match = MythDisplayMode::FindBestMatch(modes, mode, targetrate);
859 if (match < 0)
860 return {};
861 return modes[static_cast<size_t>(match)].RefreshRates();
862}
863
864bool MythDisplay::SwitchToVideoMode(QSize /*Size*/, double /*Framerate*/)
865{
866 return false;
867}
868
870{
871 return m_videoModes;
872}
873
892double MythDisplay::GetAspectRatio(QString &Source, bool IgnoreModeOverride)
893{
894 auto valid = [](double Aspect) { return (Aspect > 0.1 && Aspect < 10.0); };
895
896 // Override for this video mode
897 // Is this behaviour still needed?
898 if (!IgnoreModeOverride && valid(m_aspectRatioOverride))
899 {
900 Source = tr("Video mode override");
902 }
903
904 // General override for invalid/misleading EDIDs or multiscreen setups
905 // New default of -1.0 equates to square pixels for modern displays
906 bool multiscreen = MythDisplay::SpanAllScreens() && GetScreenCount() > 1;
907 double override = gCoreContext->GetFloatSettingOnHost("XineramaMonitorAspectRatio",
908 gCoreContext->GetHostName(), -1.0);
909
910 // Zero (not valid) indicates auto
911 if (valid(override))
912 {
913 Source = tr("Override");
914 return override;
915 }
916
917 // Auto for multiscreen is a best guess
918 if (multiscreen)
919 {
920 double aspect = EstimateVirtualAspectRatio();
921 if (valid(aspect))
922 {
923 Source = tr("Multiscreen estimate");
924 return aspect;
925 }
926 }
927
928 double calculated = m_resolution.isEmpty() ? 0.0 :
929 static_cast<double>(m_resolution.width()) / m_resolution.height();
930 double detected = m_physicalSize.isEmpty() ? 0.0 :
931 static_cast<double>(m_physicalSize.width()) / m_physicalSize.height();
932
933 // Assume pixel aspect ratio is 1 (square pixels)
934 if (valid(calculated))
935 {
936 if ((override < 0.0) || !valid(detected))
937 {
938 Source = tr("Square pixels");
939 return calculated;
940 }
941 }
942
943 // Based on actual physical size if available
944 if (valid(detected))
945 {
946 Source = tr("Detected");
947 return detected;
948 }
949
950 // the aspect ratio of last resort
951 Source = tr("Guessed");
952 return 16.0 / 9.0;
953}
954
956{
957 return m_edid;
958}
959
961{
962 return m_hdrState;
963}
964
966{
967 if (m_edid.Valid())
968 {
969 auto hdrdesc = m_edid.GetHDRSupport();
970 m_hdrState = MythHDR::Create(this, hdrdesc);
971 LOG(VB_GENERAL, LOG_NOTICE, LOC + QString("Supported HDR formats: %1")
972 .arg(m_hdrState->TypesToString().join(",")));
973 if (auto brightness = m_hdrState->GetMaxLuminance(); brightness > 1.0)
974 {
975 LOG(VB_GENERAL, LOG_NOTICE, LOC + QString("Display reports max brightness of %1 nits")
976 .arg(static_cast<int>(brightness)));
977 }
978 }
979}
980
991{
992 auto sortscreens = [](const QScreen* First, const QScreen* Second)
993 {
994 if (First->geometry().left() < Second->geometry().left())
995 return true;
996 if (First->geometry().top() < Second->geometry().top())
997 return true;
998 return false;
999 };
1000
1001 // default
1002 double result = 16.0 / 9.0;
1003
1004 QList<QScreen*> screens;
1005 if (m_screen)
1006 screens = m_screen->virtualSiblings();
1007 if (screens.empty())
1008 return result;
1009
1010 // N.B. This sorting may not be needed
1011 // QList doesn't always play well with std::ranges
1012 // NOLINTNEXTLINE(modernize-use-ranges)
1013 std::sort(screens.begin(), screens.end(), sortscreens);
1014 QList<double> aspectratios;
1015 QSize totalresolution;
1016 int lasttop = 0;
1017 int lastleft = 0;
1018 int rows = 1;
1019 int columns = 1;
1020 for (auto it = screens.constBegin() ; it != screens.constEnd(); ++it)
1021 {
1022 QRect geom = (*it)->geometry();
1023 totalresolution += geom.size();
1024 LOG(VB_PLAYBACK, LOG_DEBUG, LOC + QString("%1x%2+%3+%4 %5")
1025 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top())
1026 .arg((*it)->physicalSize().width() / (*it)->physicalSize().height()));
1027 if (lastleft < geom.left())
1028 {
1029 columns++;
1030 lastleft = geom.left();
1031 }
1032 if (lasttop < geom.top())
1033 {
1034 rows++;
1035 lasttop = geom.top();
1036 lastleft = 0;
1037 }
1038 aspectratios << (*it)->physicalSize().width() / (*it)->physicalSize().height();
1039 }
1040
1041 // If all else fails, use the total resolution and assume pixel aspect ratio
1042 // equals display aspect ratio
1043 if (!totalresolution.isEmpty())
1044 result = static_cast<double>(totalresolution.width()) / totalresolution.height();
1045
1046 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Screen layout: %1x%2").arg(rows).arg(columns));
1047 if (rows == columns)
1048 {
1049 LOG(VB_GENERAL, LOG_DEBUG, LOC + "Grid layout");
1050 }
1051 else if (rows == 1 && columns > 1)
1052 {
1053 LOG(VB_GENERAL, LOG_DEBUG, LOC + "Horizontal layout");
1054 }
1055 else if (columns == 1 && rows > 1)
1056 {
1057 LOG(VB_GENERAL, LOG_DEBUG, LOC + "Vertical layout");
1058 }
1059 else
1060 {
1061 LOG(VB_GENERAL, LOG_INFO,
1062 LOC + QString("Unsupported layout - defaulting to %1 (%2/%3)")
1063 .arg(result).arg(totalresolution.width()).arg(totalresolution.height()));
1064 return result;
1065 }
1066
1067 // validate aspect ratios - with a little fuzzyness
1068 double aspectratio = 0.0;
1069 double average = 0.0;
1070 int count = 1;
1071 for (auto it2 = aspectratios.constBegin() ; it2 != aspectratios.constEnd(); ++it2, ++count)
1072 {
1073 aspectratio += *it2;
1074 average = aspectratio / count;
1075 if (qAbs(*it2 - average) > 0.1)
1076 {
1077 LOG(VB_GENERAL, LOG_INFO, LOC +
1078 QString("Inconsistent aspect ratios - defaulting to %1 (%2/%3)")
1079 .arg(result).arg(totalresolution.width()).arg(totalresolution.height()));
1080 return result;
1081 }
1082 }
1083
1084 aspectratio = (average * columns) / rows;
1085 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Estimated aspect ratio: %1")
1086 .arg(aspectratio));
1087 return aspectratio;
1088}
1089
1091{
1092 return m_resolution;
1093}
1094
1096{
1097 return m_physicalSize;
1098}
1099
1101{
1102 // Some implementations may have their own mechanism for ensuring the mode
1103 // is updated before continuing
1105 return;
1106
1107 LOG(VB_GENERAL, LOG_INFO, LOC + "Waiting for resolution change");
1108 QEventLoop loop;
1109 QTimer timer;
1110 timer.setSingleShot(true);
1111 connect(&timer, &QTimer::timeout,
1112 &timer, [](){ LOG(VB_GENERAL, LOG_WARNING, LOC + "Timed out waiting for screen change"); });
1113 QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
1114 QObject::connect(m_screen, &QScreen::geometryChanged, &loop, &QEventLoop::quit);
1115 // 500ms maximum wait
1116 timer.start(500ms);
1117 loop.exec();
1118}
1119
1121{
1122 // N.B. This isn't working as intended as it always times out rather than
1123 // exiting deliberately. It does however somehow filter out unwanted screenChanged
1124 // events that otherwise often put the widget in the wrong screen.
1125 // Needs more investigation - but for now it works:)
1126 if (!m_widget || !m_widget->windowHandle())
1127 return;
1128 LOG(VB_GENERAL, LOG_INFO, LOC + "Waiting for new screen");
1129 QEventLoop loop;
1130 QTimer timer;
1131 timer.setSingleShot(true);
1132 connect(&timer, &QTimer::timeout,
1133 &timer, [](){ LOG(VB_GENERAL, LOG_WARNING, LOC + "Timed out waiting for new screen"); });
1134 QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
1135 QObject::connect(m_widget->windowHandle(), &QWindow::screenChanged, &loop, &QEventLoop::quit);
1136 // 500ms maximum wait
1137 timer.start(500ms);
1138 loop.exec();
1139}
1140
1142{
1143 int pauselengthinms = gCoreContext->GetNumSetting("VideoModeChangePauseMS", 0);
1144 if (pauselengthinms)
1145 {
1146 LOG(VB_GENERAL, LOG_INFO, LOC +
1147 QString("Pausing %1ms for video mode switch").arg(pauselengthinms));
1148 QEventLoop loop;
1149 QTimer timer;
1150 timer.setSingleShot(true);
1151 QObject::connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
1152 // 500ms maximum wait
1153 timer.start(pauselengthinms);
1154 loop.exec();
1155 }
1156}
1157
1159{
1160 // This is intentionally formatted to match the output of xrandr for comparison
1161 if (VERBOSE_LEVEL_CHECK(VB_PLAYBACK, LOG_INFO))
1162 {
1163 LOG(VB_PLAYBACK, LOG_INFO, LOC + "Available modes:");
1164 QStringList rateslist;
1165 for (const auto & videoMode : std::ranges::reverse_view(m_videoModes))
1166 {
1167 auto rates = videoMode.RefreshRates();
1168 rateslist.clear();
1169 rateslist.reserve(rates.size());
1170 for (double rate : std::ranges::reverse_view(rates))
1171 rateslist.append(QString("%1").arg(rate, 2, 'f', 2, '0'));
1172 if (rateslist.empty())
1173 rateslist.append("Variable rate?"); // clazy:exclude=reserve-candidates
1174 LOG(VB_PLAYBACK, LOG_INFO, QString("%1x%2\t%3")
1175 .arg(videoMode.Width()).arg(videoMode.Height()).arg(rateslist.join("\t")));
1176 }
1177 }
1178}
1179
1185void MythDisplay::ConfigureQtGUI(int SwapInterval, const MythCommandLineParser& CmdLine)
1186{
1187 auto forcevrr = CmdLine.toBool("vrr");
1188 bool gsyncchanged = false;
1189 bool freesyncchanged = false;
1190
1191#if CONFIG_QTWEBENGINE
1192 QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
1193 QQuickWindow::setSceneGraphBackend("software");
1194 LOG(VB_GENERAL, LOG_INFO, LOC + "Using shared OpenGL Contexts");
1195#endif
1196
1197 // Set the default surface format. Explicitly required on some platforms.
1198 QSurfaceFormat format;
1199 // Allow overriding the default depth - use with caution as Qt will likely
1200 // crash if it cannot find a matching visual.
1201 if (qEnvironmentVariableIsSet("MYTHTV_DEPTH"))
1202 {
1203 // Note: Don't set depth and stencil to give Qt as much flexibility as possible
1204 int depth = std::clamp(qEnvironmentVariableIntValue("MYTHTV_DEPTH"), 6, 16);
1205 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Trying to force depth to '%1'").arg(depth));
1206 format.setRedBufferSize(depth);
1207 }
1208 else
1209 {
1210 format.setDepthBufferSize(0);
1211 format.setStencilBufferSize(0);
1212 }
1213 format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
1214 format.setProfile(QSurfaceFormat::CompatibilityProfile);
1215 format.setSwapInterval(SwapInterval);
1216 QSurfaceFormat::setDefaultFormat(format);
1217
1218#ifdef Q_OS_DARWIN
1219 // Without this, we can't set focus to any of the CheckBoxSetting, and most
1220 // of the MythPushButton widgets, and they don't use the themed background.
1221 QApplication::setDesktopSettingsAware(false);
1222#endif
1223
1224#if CONFIG_DRM && CONFIG_QTPRIVATEHEADERS
1225 // Avoid trying to setup DRM if we are definitely not going to use it.
1226#if CONFIG_X11
1228#endif
1229 {
1230#if CONFIG_WAYLANDEXTRAS
1231 // When vt switching this still detects wayland servers, so disabled for now
1232 //if (!MythWaylandDevice::IsAvailable())
1233#endif
1234 {
1235 MythDRMDevice::SetupDRM(CmdLine);
1236 freesyncchanged = MythDRMVRR::s_freeSyncResetOnExit;
1237 }
1238 }
1239#endif
1240
1241#if defined (Q_OS_LINUX) && CONFIG_EGL && CONFIG_X11
1242 // We want to use EGL for VAAPI/MMAL/DRMPRIME rendering to ensure we
1243 // can use zero copy video buffers for the best performance.
1244 // To force Qt to use EGL we must set 'QT_XCB_GL_INTEGRATION' to 'xcb_egl'
1245 // and this must be done before any GUI is created. If the platform plugin is
1246 // not xcb then this should have no effect.
1247 // This does however break when using NVIDIA drivers - which do not support
1248 // EGL like other drivers so we try to check the EGL vendor - and we currently
1249 // have no need for EGL with NVIDIA (that may change however).
1250 // NOTE force using EGL by setting MYTHTV_FORCE_EGL
1251 // NOTE disable using EGL by setting MYTHTV_NO_EGL
1252 // NOTE We have no Qt platform information, window/surface or logging when this is called.
1253 QString soft = qgetenv("LIBGL_ALWAYS_SOFTWARE");
1254 bool ignore = soft == "1" || soft.compare("true", Qt::CaseInsensitive) == 0;
1255 bool allow = qEnvironmentVariableIsEmpty("MYTHTV_NO_EGL") && !ignore;
1256 bool force = !qEnvironmentVariableIsEmpty("MYTHTV_FORCE_EGL");
1257 if ((force || allow) && MythDisplayX11::IsAvailable())
1258 {
1259 // N.B. By default, ignore EGL if vendor string is not returned
1260 QString vendor = MythEGL::GetEGLVendor();
1261 if (vendor.contains("nvidia", Qt::CaseInsensitive) && !force)
1262 {
1263 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Not requesting EGL for vendor '%1'").arg(vendor));
1264 }
1265 else if (!vendor.isEmpty() || force)
1266 {
1267 LOG(VB_GENERAL, LOG_INFO, LOC + QString("Requesting EGL for vendor '%1'").arg(vendor));
1268 if (!qEnvironmentVariableIsSet("QT_XCB_GL_INTEGRATION"))
1269 {
1270 qputenv("QT_XCB_GL_INTEGRATION", "xcb_egl");
1271 }
1272 }
1273 }
1274#endif
1275
1276#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1277 // Ignore desktop scaling
1278 QApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
1279#else
1280 // Disable high DPI scaling unless defined in the environment
1281 if (qEnvironmentVariableIsEmpty("QT_ENABLE_HIGHDPI_SCALING"))
1282 {
1283 qputenv("QT_ENABLE_HIGHDPI_SCALING", "0");
1284 }
1285#endif
1286
1287#if CONFIG_X11
1288 if (auto display = CmdLine.toString("display"); !display.isEmpty())
1290 // GSync support via libXNVCtrl
1291 // Note: FreeSync support is checked in MythDRMDevice::SetupDRM
1292 if (forcevrr)
1293 {
1294 MythGSync::ForceGSync(CmdLine.toUInt("vrr") > 0);
1295 gsyncchanged = MythGSync::s_gsyncResetOnExit;
1296 }
1297#endif
1298
1299 if (forcevrr && !(gsyncchanged || freesyncchanged))
1300 LOG(VB_GENERAL, LOG_INFO, LOC + "Variable refresh rate not adjusted");
1301}
1302
1303#include "moc_mythdisplay.cpp"
Parent class for defining application command line parsers.
bool toBool(const QString &key) const
Returns stored QVariant as a boolean.
QString toString(const QString &key) const
Returns stored QVariant as a QString, falling to default if not provided.
uint toUInt(const QString &key) const
Returns stored QVariant as an unsigned integer, falling to default if not provided.
QString GetHostName(void)
QString GetSetting(const QString &key, const QString &defaultval="")
int GetNumSetting(const QString &key, int defaultval=0)
double GetFloatSettingOnHost(const QString &key, const QString &host, double defaultval=0.0)
static bool s_freeSyncResetOnExit
Definition: mythdrmvrr.h:11
int Height() const
static bool CompareRates(double First, double Second, double Precision=0.01)
Determine whether two rates are considered equal with the given precision.
static int FindBestMatch(const MythDisplayModes &Modes, const MythDisplayMode &Mode, double &TargetRate)
void SetRefreshRate(double Rate)
double AspectRatio() const
double RefreshRate() const
static uint64_t FindBestScreen(const DisplayModeMap &Map, int Width, int Height, double Rate)
static uint64_t CalcKey(QSize Size, double Rate)
QSize Resolution() const
static bool IsAvailable()
MythEDID & GetEDID()
QWidget * m_widget
Definition: mythdisplay.h:95
double GetPixelAspectRatio()
virtual bool SwitchToVideoMode(QSize Size, double Framerate)
QSize m_resolution
Definition: mythdisplay.h:92
void InitScreenBounds()
Get screen size from Qt while respecting the user's multiscreen settings.
MythHDRPtr GetHDRState()
QStringList GetDescription()
static void PauseForModeSwitch()
QWindow * GetCurrentWindow()
double EstimateVirtualAspectRatio()
Estimate the overall display aspect ratio for multi screen setups.
double GetAspectRatio(QString &Source, bool IgnoreModeOverride=false)
Returns current screen aspect ratio.
void WaitForNewScreen()
void SwitchToDesktop()
Return the screen to the original desktop video mode.
virtual void UpdateCurrentMode()
Retrieve screen details.
static void GeometryChanged(QRect Geometry)
MythDisplayRates GetRefreshRates(QSize Size)
static void ConfigureQtGUI(int SwapInterval, const MythCommandLineParser &CmdLine)
Shared static initialisation code for all MythTV GUI applications.
QSize GetPhysicalSize()
MythDisplayMode m_guiMode
Definition: mythdisplay.h:110
QWindow * m_window
Definition: mythdisplay.h:96
bool m_firstScreenChange
Definition: mythdisplay.h:107
double GetRefreshRate() const
bool m_initialised
Definition: mythdisplay.h:106
bool SwitchToVideo(QSize Size, double Rate=0.0)
Switches to the resolution and refresh rate defined in the database for the specified video resolutio...
QSize m_physicalSize
Definition: mythdisplay.h:93
void PhysicalDPIChanged(qreal DPI)
static QScreen * GetDesiredScreen()
QSize GetGUIResolution()
static void DebugScreen(QScreen *qScreen, const QString &Message)
virtual bool VideoModesAvailable()
Definition: mythdisplay.h:29
static MythDisplay * Create(MythMainWindow *MainWindow)
Create a MythDisplay object appropriate for the current platform.
Definition: mythdisplay.cpp:96
bool SwitchToGUI(bool Wait=false)
Switches to the GUI resolution.
void Initialise()
double m_refreshRate
Definition: mythdisplay.h:90
virtual void ScreenChanged(QScreen *qScreen)
The actual screen in use has changed. We must use it.
MythDisplayModes m_videoModes
Definition: mythdisplay.h:98
bool m_waitForModeChanges
Definition: mythdisplay.h:88
void ScreenRemoved(QScreen *qScreen)
MythDisplayMode m_desktopMode
Definition: mythdisplay.h:109
QScreen * GetCurrentScreen()
Return a pointer to the screen to use.
static void PrimaryScreenChanged(QScreen *qScreen)
QRect GetScreenBounds()
~MythDisplay() override
void ScreenCountChanged(int Screens)
void ScreenAdded(QScreen *qScreen)
DisplayModeMap m_overrideVideoModes
Definition: mythdisplay.h:112
MythHDRPtr m_hdrState
Definition: mythdisplay.h:99
double m_aspectRatioOverride
Definition: mythdisplay.h:91
QScreen * m_screen
Definition: mythdisplay.h:97
MythEDID m_edid
Definition: mythdisplay.h:94
void WaitForScreenChange()
virtual const MythDisplayModes & GetVideoModes()
static QString GetExtraScreenInfo(QScreen *qScreen)
void SetWidget(QWidget *MainWindow)
Set the QWidget and QWindow in use.
void InitHDR()
MythVRRPtr m_vrrState
Definition: mythdisplay.h:100
void DisplayChanged()
QRect m_screenBounds
Definition: mythdisplay.h:108
std::chrono::microseconds GetRefreshInterval(std::chrono::microseconds Fallback) const
MythDisplayMode m_videoMode
Definition: mythdisplay.h:111
QSize GetResolution()
bool NextModeIsLarger(QSize Size)
Check whether the next mode is larger in size than the current mode.
bool m_modeComplete
Definition: mythdisplay.h:89
static bool SpanAllScreens()
Return true if the MythTV windows should span all screens.
static int GetScreenCount()
void DebugModes() const
MythHDRDesc GetHDRSupport() const
Definition: mythedid.cpp:100
bool IsSRGB() const
Definition: mythedid.cpp:75
bool Valid() const
Definition: mythedid.cpp:40
static QString GetEGLVendor(void)
Definition: mythegl.cpp:86
static bool s_gsyncResetOnExit
Definition: mythnvcontrol.h:13
static void ForceGSync(bool Enable)
Enable or disable GSync before the main window is created.
static MythHDRPtr Create(class MythDisplay *MDisplay, const MythHDRDesc &Desc)
Definition: mythhdr.cpp:31
static QRect GetGeometryOverride()
static bool WindowIsAlwaysFullscreen()
Return true if the current platform only supports fullscreen windows.
static bool GeometryIsOverridden()
static MythVRRPtr Create(class MythDisplay *MDisplay)
Create a concrete implementation of MythVRR suitable for the given Display.
Definition: mythvrr.cpp:56
static void SetQtX11Display(const QString &DisplayStr)
static const struct wl_interface * types[]
@ quit
Definition: lirc_client.h:34
std::chrono::microseconds microsecondsFromFloat(T value)
Helper function for convert a floating point number to a duration.
Definition: mythchrono.h:92
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythDB * GetMythDB(void)
Definition: mythdb.cpp:50
#define LOC
Definition: mythdisplay.cpp:60
std::vector< MythDisplayMode > MythDisplayModes
std::vector< double > MythDisplayRates
std::shared_ptr< class MythHDR > MythHDRPtr
Definition: mythhdr.h:30
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:204