6#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
7#include <QtSystemDetection>
11#include <QApplication>
12#include <QElapsedTimer>
15#include "libmythbase/mythconfig.h"
18#include <QQuickWindow>
33#if CONFIG_WAYLANDEXTRAS
58#define LOC QString("Display: ")
104#if CONFIG_WAYLANDEXTRAS
150 bool spanall =
false;
155 result.append(tr(
"Spanning %1 screens").arg(screencount));
156 result.append(tr(
"Total bounds") + QString(
"\t: %1x%2")
165 result.append(tr(
"Supported HDR formats\t: %1").arg(hdr.join(
",")));
167 result.append(tr(
"HDR mode switching is not available"));
168 if (
auto brightness =
m_hdrState->GetMaxLuminance(); brightness > 1.0)
169 result.append(tr(
"Max display brightness\t: %1 nits").arg(
static_cast<int>(brightness)));
174 result.append(tr(
"Variable refresh rate '%1': %2 %3")
176 m_vrrState->Enabled() ? tr(
"Enabled") : tr(
"Disabled"),
181 const auto screens = QGuiApplication::screens();
183 for (
auto *screen : std::as_const(screens))
188 auto id = QString(
"(%1)").arg(screen->manufacturer());
189 if (screen ==
current && !spanall)
190 result.append(tr(
"Current screen\t: %1 %2").arg(screen->name(),
id));
192 result.append(tr(
"Screen\t\t: %1 %2").arg(screen->name(),
id));
193 result.append(tr(
"Size") + QString(
"\t\t: %1mmx%2mm")
194 .arg(screen->physicalSize().width()).arg(screen->physicalSize().height()));
199 result.append(tr(
"Aspect ratio") + QString(
"\t: %1 (%2)")
200 .arg(aspect, 0,
'f', 3).arg(source));
203 result.append(tr(
"Current mode") + QString(
"\t: %1x%2@%3Hz")
209 result.append(tr(
"Available modes:"));
210 for (
auto it = modes.crbegin(); it != modes.crend(); ++it)
211 result.append(
" " + it->ToString());
221 : m_screen(GetDesiredScreen())
230 auto *guiapp = qobject_cast<QGuiApplication *>(QCoreApplication::instance());
231 if (guiapp ==
nullptr)
241 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Deleting");
271 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Have main widget");
275 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Have main window");
301 return QGuiApplication::screens().size();
351 QScreen* newscreen =
nullptr;
361 bool windowed =
GetMythDB()->GetBoolSetting(
"RunFrontendInWindow",
false) &&
367 QPoint point = windowed ?
override.topLeft() :
override.bottomRight();
368 QList screens = QGuiApplication::screens();
369 for (QScreen *screen : std::as_const(screens))
371 if (screen->geometry().contains(point))
374 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
375 "Geometry override places window in screen '%1'").arg(newscreen->name()));
384 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Using primary screen for multiscreen");
385 newscreen = QGuiApplication::primaryScreen();
392 QList screens = QGuiApplication::screens();
393 for (QScreen *screen : std::as_const(screens))
395 if (!name.isEmpty() && name == screen->name())
397 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Found screen '%1'").arg(name));
407 int screen_num = name.toInt(&ok);
408 QList<QScreen *>screens = QGuiApplication::screens();
409 if (ok && (screen_num >= 0) && (screen_num < screens.size()))
411 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Found screen number %1 (%2)")
412 .arg(name, screens[screen_num]->name()));
413 newscreen = screens[screen_num];
420 QScreen *primary = QGuiApplication::primaryScreen();
421 if (name.isEmpty() && primary)
423 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Defaulting to primary screen (%1)")
424 .arg(primary->name()));
426 else if (name !=
"-1" && primary)
428 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen '%1' not found, defaulting to primary screen (%2)")
429 .arg(name, primary->name()));
444 disconnect(
m_screen,
nullptr,
this,
nullptr);
455 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Qt screen pixel ratio changed to %1")
456 .arg(DPI, 2,
'f', 2,
'0'));
473 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen '%1' removed").arg(qScreen->name()));
479 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"New screen geometry: %1x%2+%3+%4")
480 .arg(Geo.width()).arg(Geo.height()).arg(Geo.left()).arg(Geo.top()));
506 m_physicalSize = QSize(
static_cast<int>(screen->physicalSize().width()),
507 static_cast<int>(screen->physicalSize().height()));
518 QString mfg = qScreen->manufacturer();
521 QString model = qScreen->model();
524 return QString(
"(Make: %1 Model: %2)").arg(mfg, model);
532 auto geom = qScreen->geometry();
533 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"%1 screen '%2' %3")
535 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Qt screen pixel ratio: %1")
536 .arg(qScreen->devicePixelRatio(), 2,
'f', 2,
'0'));
537 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Geometry: %1x%2+%3+%4 Size(Qt): %5mmx%6mm")
538 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top())
539 .arg(qScreen->physicalSize().width()).arg(qScreen->physicalSize().height()));
541 if (qScreen->virtualGeometry() != geom)
543 geom = qScreen->virtualGeometry();
544 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Total virtual geometry: %1x%2+%3+%4")
545 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top()));
566 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Desktop video mode: %1x%2 %3Hz")
571 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"Display is using sRGB colourspace");
573 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"Display has custom colourspace");
585 double aspectratio = 0.0;
586 GetMythDB()->GetResolutionSetting(
"GuiVidMode", pixelwidth, pixelheight, aspectratio, refreshrate);
587 GetMythDB()->GetResolutionSetting(
"DisplaySize", mmwidth, mmheight);
591 pixelwidth = pixelheight = 0;
592 GetMythDB()->GetResolutionSetting(
"TVVidMode", pixelwidth, pixelheight, aspectratio, refreshrate);
596 for (
int i = 0;
true; ++i)
602 double iaspect = 0.0;
603 double oaspect = 0.0;
607 GetMythDB()->GetResolutionSetting(
"VidMode", iw, ih, iaspect, irate, i);
608 GetMythDB()->GetResolutionSetting(
"TVVidMode", ow, oh, oaspect, orate, i);
610 if ((!iw && !ih && qFuzzyIsNull(irate)) || !(ih && ow && oh))
614 MythDisplayMode scr(QSize(ow, oh), QSize(mmwidth, mmheight), oaspect, orate);
629 const auto screens = QGuiApplication::screens();
630 for (
auto * screen : std::as_const(screens))
632 auto dim = screen->geometry();
634 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Screen %1: %2x%3 %4")
635 .arg(screen->name()).arg(dim.width()).arg(dim.height()).arg(extra));
638 const auto * primary = QGuiApplication::primaryScreen();
641 if (!screens.empty())
642 primary = screens.front();
645 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Qt has no screens!");
650 LOG(VB_GUI, LOG_INFO,
LOC +QString(
"Primary screen: %1.").arg(primary->name()));
653 auto dim = primary->virtualSize();
654 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Total desktop dim: %1x%2, over %3 screen[s].")
655 .arg(dim.width()).arg(dim.height()).arg(numScreens));
659 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Using entire desktop."));
664 if (!
GetMythDB()->GetBoolSetting(
"ForceFullScreen",
false) &&
665 GetMythDB()->GetBoolSetting(
"RunFrontendInWindow",
false))
667 LOG(VB_GUI, LOG_INFO,
LOC +
"Running in a window");
677 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Using screen %1: %2x%3 at %4+%5")
715 double targetrate = 0.0;
716 double aspectoverride = 0.0;
720 Size.width(), Size.height(), Rate);
727 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Found custom screen override %1x%2 Aspect %3")
728 .arg(next.
Width()).arg(next.
Height()).arg(aspectoverride));
734 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Trying to match best refresh rate %1Hz")
735 .arg(Rate, 0,
'f', 3));
750 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Ignoring mode switch to %1Hz - VRR enabled")
751 .arg(Rate, 0,
'f', 3));
754 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Allowing mode switch with VRR enabled for new resolution");
760 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Using current mode %1x%2@%3Hz")
765 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Trying mode %1x%2@%3Hz")
770 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to change mode to %1x%2@%3Hz")
781 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Switched to %1x%2@%3Hz for video %4x%5")
783 .arg(
m_refreshRate, 0,
'f', 3).arg(Size.width()).arg(Size.height()));
799 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Using %1x%2@%3Hz for GUI")
806 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to change mode to %1x%2@%3Hz")
816 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Switched to %1x%2@%3Hz")
837 auto max = std::get<1>(range) > 60 ? std::get<1>(range) : 60;
850 auto targetrate =
static_cast<double>(NAN);
856 return modes[
static_cast<size_t>(match)].RefreshRates();
889 auto valid = [](
double Aspect) {
return (Aspect > 0.1 && Aspect < 10.0); };
895 Source = tr(
"Video mode override");
918 Source = tr(
"Multiscreen estimate");
929 if (valid(calculated))
931 if ((
override < 0.0) || !valid(detected))
933 Source = tr(
"Square pixels");
966 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Supported HDR formats: %1")
968 if (
auto brightness =
m_hdrState->GetMaxLuminance(); brightness > 1.0)
970 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Display reports max brightness of %1 nits")
971 .arg(
static_cast<int>(brightness)));
987 auto sortscreens = [](
const QScreen* First,
const QScreen* Second)
989 if (First->geometry().left() < Second->geometry().left())
991 if (First->geometry().top() < Second->geometry().top())
997 double result = 16.0 / 9.0;
999 QList<QScreen*> screens;
1001 screens =
m_screen->virtualSiblings();
1002 if (screens.empty())
1006 std::sort(screens.begin(), screens.end(), sortscreens);
1007 QList<double> aspectratios;
1008 QSize totalresolution;
1013 for (
auto it = screens.constBegin() ; it != screens.constEnd(); ++it)
1015 QRect geom = (*it)->geometry();
1016 totalresolution += geom.size();
1017 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"%1x%2+%3+%4 %5")
1018 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top())
1019 .arg((*it)->physicalSize().width() / (*it)->physicalSize().height()));
1020 if (lastleft < geom.left())
1023 lastleft = geom.left();
1025 if (lasttop < geom.top())
1028 lasttop = geom.top();
1031 aspectratios << (*it)->physicalSize().width() / (*it)->physicalSize().height();
1036 if (!totalresolution.isEmpty())
1037 result =
static_cast<double>(totalresolution.width()) / totalresolution.height();
1039 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen layout: %1x%2").arg(rows).arg(columns));
1040 if (rows == columns)
1042 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Grid layout");
1044 else if (rows == 1 && columns > 1)
1046 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Horizontal layout");
1048 else if (columns == 1 && rows > 1)
1050 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Vertical layout");
1054 LOG(VB_GENERAL, LOG_INFO,
1055 LOC + QString(
"Unsupported layout - defaulting to %1 (%2/%3)")
1056 .arg(result).arg(totalresolution.width()).arg(totalresolution.height()));
1061 double aspectratio = 0.0;
1062 double average = 0.0;
1064 for (
auto it2 = aspectratios.constBegin() ; it2 != aspectratios.constEnd(); ++it2, ++count)
1066 aspectratio += *it2;
1067 average = aspectratio / count;
1068 if (qAbs(*it2 - average) > 0.1)
1070 LOG(VB_GENERAL, LOG_INFO,
LOC +
1071 QString(
"Inconsistent aspect ratios - defaulting to %1 (%2/%3)")
1072 .arg(result).arg(totalresolution.width()).arg(totalresolution.height()));
1077 aspectratio = (average * columns) / rows;
1078 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Estimated aspect ratio: %1")
1100 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Waiting for resolution change");
1103 timer.setSingleShot(
true);
1105 &timer, [](){
LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Timed out waiting for screen change"); });
1121 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Waiting for new screen");
1124 timer.setSingleShot(
true);
1126 &timer, [](){
LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Timed out waiting for new screen"); });
1137 if (pauselengthinms)
1139 LOG(VB_GENERAL, LOG_INFO,
LOC +
1140 QString(
"Pausing %1ms for video mode switch").arg(pauselengthinms));
1143 timer.setSingleShot(
true);
1146 timer.start(pauselengthinms);
1156 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Available modes:");
1159 auto rates = (*it).RefreshRates();
1160 QStringList rateslist;
1161 for (
auto it2 = rates.crbegin(); it2 != rates.crend(); ++it2)
1162 rateslist.append(QString(
"%1").arg(*it2, 2,
'f', 2,
'0'));
1163 if (rateslist.empty())
1164 rateslist.append(
"Variable rate?");
1165 LOG(VB_PLAYBACK, LOG_INFO, QString(
"%1x%2\t%3")
1166 .arg((*it).Width()).arg((*it).Height()).arg(rateslist.join(
"\t")));
1178 auto forcevrr = CmdLine.
toBool(
"vrr");
1179 bool gsyncchanged =
false;
1180 bool freesyncchanged =
false;
1182#if CONFIG_QTWEBENGINE
1183 QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
1184 QQuickWindow::setSceneGraphBackend(
"software");
1185 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Using shared OpenGL Contexts");
1189 QSurfaceFormat format;
1192 if (qEnvironmentVariableIsSet(
"MYTHTV_DEPTH"))
1195 int depth =
std::clamp(qEnvironmentVariableIntValue(
"MYTHTV_DEPTH"), 6, 16);
1196 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Trying to force depth to '%1'").arg(depth));
1197 format.setRedBufferSize(depth);
1201 format.setDepthBufferSize(0);
1202 format.setStencilBufferSize(0);
1204 format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
1205 format.setProfile(QSurfaceFormat::CompatibilityProfile);
1206 format.setSwapInterval(SwapInterval);
1207 QSurfaceFormat::setDefaultFormat(format);
1212 QApplication::setDesktopSettingsAware(
false);
1215#if CONFIG_DRM && CONFIG_QTPRIVATEHEADERS
1221#if CONFIG_WAYLANDEXTRAS
1226 MythDRMDevice::SetupDRM(CmdLine);
1232#if defined (Q_OS_LINUX) && CONFIG_EGL && CONFIG_X11
1244 QString soft = qgetenv(
"LIBGL_ALWAYS_SOFTWARE");
1245 bool ignore = soft ==
"1" || soft.compare(
"true", Qt::CaseInsensitive) == 0;
1246 bool allow = qEnvironmentVariableIsEmpty(
"MYTHTV_NO_EGL") && !ignore;
1247 bool force = !qEnvironmentVariableIsEmpty(
"MYTHTV_FORCE_EGL");
1252 if (vendor.contains(
"nvidia", Qt::CaseInsensitive) && !
force)
1254 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Not requesting EGL for vendor '%1'").arg(vendor));
1256 else if (!vendor.isEmpty() ||
force)
1258 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Requesting EGL for vendor '%1'").arg(vendor));
1259 setenv(
"QT_XCB_GL_INTEGRATION",
"xcb_egl", 0);
1264#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1266 QApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
1270 if (
auto display = CmdLine.
toString(
"display"); !display.isEmpty())
1281 if (forcevrr && !(gsyncchanged || freesyncchanged))
1282 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Variable refresh rate not adjusted");
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
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)
static bool IsAvailable()
double GetPixelAspectRatio()
virtual bool SwitchToVideoMode(QSize Size, double Framerate)
void InitScreenBounds()
Get screen size from Qt while respecting the user's multiscreen settings.
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 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.
MythDisplayMode m_guiMode
double GetRefreshRate() const
bool SwitchToVideo(QSize Size, double Rate=0.0)
Switches to the resolution and refresh rate defined in the database for the specified video resolutio...
void PhysicalDPIChanged(qreal DPI)
static QScreen * GetDesiredScreen()
static void DebugScreen(QScreen *qScreen, const QString &Message)
virtual bool VideoModesAvailable()
static MythDisplay * Create(MythMainWindow *MainWindow)
Create a MythDisplay object appropriate for the current platform.
bool SwitchToGUI(bool Wait=false)
Switches to the GUI resolution.
virtual void ScreenChanged(QScreen *qScreen)
The actual screen in use has changed. We must use it.
MythDisplayModes m_videoModes
bool m_waitForModeChanges
void ScreenRemoved(QScreen *qScreen)
MythDisplayMode m_desktopMode
QScreen * GetCurrentScreen()
Return a pointer to the screen to use.
static void PrimaryScreenChanged(QScreen *qScreen)
void ScreenCountChanged(int Screens)
void ScreenAdded(QScreen *qScreen)
DisplayModeMap m_overrideVideoModes
double m_aspectRatioOverride
void WaitForScreenChange()
virtual const MythDisplayModes & GetVideoModes()
static QString GetExtraScreenInfo(QScreen *qScreen)
void SetWidget(QWidget *MainWindow)
Set the QWidget and QWindow in use.
std::chrono::microseconds GetRefreshInterval(std::chrono::microseconds Fallback) const
MythDisplayMode m_videoMode
bool NextModeIsLarger(QSize Size)
Check whether the next mode is larger in size than the current mode.
static bool SpanAllScreens()
Return true if the MythTV windows should span all screens.
static int GetScreenCount()
MythHDRDesc GetHDRSupport() const
static QString GetEGLVendor(void)
static bool s_gsyncResetOnExit
static void ForceGSync(bool Enable)
Enable or disable GSync before the main window is created.
static MythHDRPtr Create(class MythDisplay *MDisplay, const MythHDRDesc &Desc)
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.
static void SetQtX11Display(const QString &DisplayStr)
static const struct wl_interface * types[]
std::enable_if_t< std::is_floating_point_v< T >, std::chrono::microseconds > microsecondsFromFloat(T value)
Helper function for convert a floating point number to a duration.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
std::vector< MythDisplayMode > MythDisplayModes
std::vector< double > MythDisplayRates
std::shared_ptr< class MythHDR > MythHDRPtr
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
static eu8 clamp(eu8 value, eu8 low, eu8 high)