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();
346 QScreen* newscreen =
nullptr;
356 bool windowed =
GetMythDB()->GetBoolSetting(
"RunFrontendInWindow",
false) &&
362 QPoint point = windowed ?
override.topLeft() :
override.bottomRight();
363 QList screens = QGuiApplication::screens();
364 for (QScreen *screen : std::as_const(screens))
366 if (screen->geometry().contains(point))
369 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
370 "Geometry override places window in screen '%1'").arg(newscreen->name()));
379 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Using primary screen for multiscreen");
380 newscreen = QGuiApplication::primaryScreen();
387 QList screens = QGuiApplication::screens();
388 for (QScreen *screen : std::as_const(screens))
390 if (!name.isEmpty() && name == screen->name())
392 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Found screen '%1'").arg(name));
402 int screen_num = name.toInt(&ok);
403 QList<QScreen *>screens = QGuiApplication::screens();
404 if (ok && (screen_num >= 0) && (screen_num < screens.size()))
406 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Found screen number %1 (%2)")
407 .arg(name, screens[screen_num]->name()));
408 newscreen = screens[screen_num];
415 QScreen *primary = QGuiApplication::primaryScreen();
416 if (name.isEmpty() && primary)
418 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Defaulting to primary screen (%1)")
419 .arg(primary->name()));
421 else if (name !=
"-1" && primary)
423 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen '%1' not found, defaulting to primary screen (%2)")
424 .arg(name, primary->name()));
439 disconnect(
m_screen,
nullptr,
this,
nullptr);
450 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Qt screen pixel ratio changed to %1")
451 .arg(DPI, 2,
'f', 2,
'0'));
468 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen '%1' removed").arg(qScreen->name()));
474 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"New screen geometry: %1x%2+%3+%4")
475 .arg(Geo.width()).arg(Geo.height()).arg(Geo.left()).arg(Geo.top()));
501 m_physicalSize = QSize(
static_cast<int>(screen->physicalSize().width()),
502 static_cast<int>(screen->physicalSize().height()));
513 QString mfg = qScreen->manufacturer();
516 QString model = qScreen->model();
519 return QString(
"(Make: %1 Model: %2)").arg(mfg, model);
527 auto geom = qScreen->geometry();
528 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"%1 screen '%2' %3")
530 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Qt screen pixel ratio: %1")
531 .arg(qScreen->devicePixelRatio(), 2,
'f', 2,
'0'));
532 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Geometry: %1x%2+%3+%4 Size(Qt): %5mmx%6mm")
533 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top())
534 .arg(qScreen->physicalSize().width()).arg(qScreen->physicalSize().height()));
536 if (qScreen->virtualGeometry() != geom)
538 geom = qScreen->virtualGeometry();
539 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Total virtual geometry: %1x%2+%3+%4")
540 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top()));
561 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Desktop video mode: %1x%2 %3Hz")
566 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"Display is using sRGB colourspace");
568 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"Display has custom colourspace");
580 double aspectratio = 0.0;
581 GetMythDB()->GetResolutionSetting(
"GuiVidMode", pixelwidth, pixelheight, aspectratio, refreshrate);
582 GetMythDB()->GetResolutionSetting(
"DisplaySize", mmwidth, mmheight);
586 pixelwidth = pixelheight = 0;
587 GetMythDB()->GetResolutionSetting(
"TVVidMode", pixelwidth, pixelheight, aspectratio, refreshrate);
591 for (
int i = 0;
true; ++i)
597 double iaspect = 0.0;
598 double oaspect = 0.0;
602 GetMythDB()->GetResolutionSetting(
"VidMode", iw, ih, iaspect, irate, i);
603 GetMythDB()->GetResolutionSetting(
"TVVidMode", ow, oh, oaspect, orate, i);
605 if ((!iw && !ih && qFuzzyIsNull(irate)) || !(ih && ow && oh))
609 MythDisplayMode scr(QSize(ow, oh), QSize(mmwidth, mmheight), oaspect, orate);
624 const auto screens = QGuiApplication::screens();
625 for (
auto * screen : std::as_const(screens))
627 auto dim = screen->geometry();
629 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Screen %1: %2x%3 %4")
630 .arg(screen->name()).arg(dim.width()).arg(dim.height()).arg(extra));
633 const auto * primary = QGuiApplication::primaryScreen();
636 if (!screens.empty())
637 primary = screens.front();
640 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Qt has no screens!");
645 LOG(VB_GUI, LOG_INFO,
LOC +QString(
"Primary screen: %1.").arg(primary->name()));
648 auto dim = primary->virtualSize();
649 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Total desktop dim: %1x%2, over %3 screen[s].")
650 .arg(dim.width()).arg(dim.height()).arg(numScreens));
654 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Using entire desktop."));
659 if (!
GetMythDB()->GetBoolSetting(
"ForceFullScreen",
false) &&
660 GetMythDB()->GetBoolSetting(
"RunFrontendInWindow",
false))
662 LOG(VB_GUI, LOG_INFO,
LOC +
"Running in a window");
672 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Using screen %1: %2x%3 at %4+%5")
710 double targetrate = 0.0;
711 double aspectoverride = 0.0;
715 Size.width(), Size.height(), Rate);
722 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Found custom screen override %1x%2 Aspect %3")
723 .arg(next.
Width()).arg(next.
Height()).arg(aspectoverride));
729 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Trying to match best refresh rate %1Hz")
730 .arg(Rate, 0,
'f', 3));
745 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Ignoring mode switch to %1Hz - VRR enabled")
746 .arg(Rate, 0,
'f', 3));
749 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Allowing mode switch with VRR enabled for new resolution");
755 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Using current mode %1x%2@%3Hz")
760 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Trying mode %1x%2@%3Hz")
765 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to change mode to %1x%2@%3Hz")
776 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Switched to %1x%2@%3Hz for video %4x%5")
778 .arg(
m_refreshRate, 0,
'f', 3).arg(Size.width()).arg(Size.height()));
794 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Using %1x%2@%3Hz for GUI")
801 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to change mode to %1x%2@%3Hz")
811 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Switched to %1x%2@%3Hz")
832 auto max = std::get<1>(range) > 60 ? std::get<1>(range) : 60;
845 auto targetrate =
static_cast<double>(NAN);
851 return modes[
static_cast<size_t>(match)].RefreshRates();
884 auto valid = [](
double Aspect) {
return (Aspect > 0.1 && Aspect < 10.0); };
890 Source = tr(
"Video mode override");
913 Source = tr(
"Multiscreen estimate");
924 if (valid(calculated))
926 if ((
override < 0.0) || !valid(detected))
928 Source = tr(
"Square pixels");
961 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Supported HDR formats: %1")
963 if (
auto brightness =
m_hdrState->GetMaxLuminance(); brightness > 1.0)
965 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Display reports max brightness of %1 nits")
966 .arg(
static_cast<int>(brightness)));
982 auto sortscreens = [](
const QScreen* First,
const QScreen* Second)
984 if (First->geometry().left() < Second->geometry().left())
986 if (First->geometry().top() < Second->geometry().top())
992 double result = 16.0 / 9.0;
994 QList<QScreen*> screens;
996 screens =
m_screen->virtualSiblings();
1001 std::sort(screens.begin(), screens.end(), sortscreens);
1002 QList<double> aspectratios;
1003 QSize totalresolution;
1008 for (
auto it = screens.constBegin() ; it != screens.constEnd(); ++it)
1010 QRect geom = (*it)->geometry();
1011 totalresolution += geom.size();
1012 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"%1x%2+%3+%4 %5")
1013 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top())
1014 .arg((*it)->physicalSize().width() / (*it)->physicalSize().height()));
1015 if (lastleft < geom.left())
1018 lastleft = geom.left();
1020 if (lasttop < geom.top())
1023 lasttop = geom.top();
1026 aspectratios << (*it)->physicalSize().width() / (*it)->physicalSize().height();
1031 if (!totalresolution.isEmpty())
1032 result =
static_cast<double>(totalresolution.width()) / totalresolution.height();
1034 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen layout: %1x%2").arg(rows).arg(columns));
1035 if (rows == columns)
1037 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Grid layout");
1039 else if (rows == 1 && columns > 1)
1041 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Horizontal layout");
1043 else if (columns == 1 && rows > 1)
1045 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Vertical layout");
1049 LOG(VB_GENERAL, LOG_INFO,
1050 LOC + QString(
"Unsupported layout - defaulting to %1 (%2/%3)")
1051 .arg(result).arg(totalresolution.width()).arg(totalresolution.height()));
1056 double aspectratio = 0.0;
1057 double average = 0.0;
1059 for (
auto it2 = aspectratios.constBegin() ; it2 != aspectratios.constEnd(); ++it2, ++count)
1061 aspectratio += *it2;
1062 average = aspectratio / count;
1063 if (qAbs(*it2 - average) > 0.1)
1065 LOG(VB_GENERAL, LOG_INFO,
LOC +
1066 QString(
"Inconsistent aspect ratios - defaulting to %1 (%2/%3)")
1067 .arg(result).arg(totalresolution.width()).arg(totalresolution.height()));
1072 aspectratio = (average * columns) / rows;
1073 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Estimated aspect ratio: %1")
1095 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Waiting for resolution change");
1098 timer.setSingleShot(
true);
1100 &timer, [](){
LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Timed out wating for screen change"); });
1116 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Waiting for new screen");
1119 timer.setSingleShot(
true);
1121 &timer, [](){
LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Timed out waiting for new screen"); });
1132 if (pauselengthinms)
1134 LOG(VB_GENERAL, LOG_INFO,
LOC +
1135 QString(
"Pausing %1ms for video mode switch").arg(pauselengthinms));
1138 timer.setSingleShot(
true);
1141 timer.start(pauselengthinms);
1151 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Available modes:");
1154 auto rates = (*it).RefreshRates();
1155 QStringList rateslist;
1156 for (
auto it2 = rates.crbegin(); it2 != rates.crend(); ++it2)
1157 rateslist.append(QString(
"%1").arg(*it2, 2,
'f', 2,
'0'));
1158 if (rateslist.empty())
1159 rateslist.append(
"Variable rate?");
1160 LOG(VB_PLAYBACK, LOG_INFO, QString(
"%1x%2\t%3")
1161 .arg((*it).Width()).arg((*it).Height()).arg(rateslist.join(
"\t")));
1173 auto forcevrr = CmdLine.
toBool(
"vrr");
1174 bool gsyncchanged =
false;
1175 bool freesyncchanged =
false;
1177#if CONFIG_QTWEBENGINE
1178 QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
1179 QQuickWindow::setSceneGraphBackend(
"software");
1180 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Using shared OpenGL Contexts");
1184 QSurfaceFormat format;
1187 if (qEnvironmentVariableIsSet(
"MYTHTV_DEPTH"))
1190 int depth =
std::clamp(qEnvironmentVariableIntValue(
"MYTHTV_DEPTH"), 6, 16);
1191 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Trying to force depth to '%1'").arg(depth));
1192 format.setRedBufferSize(depth);
1196 format.setDepthBufferSize(0);
1197 format.setStencilBufferSize(0);
1199 format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
1200 format.setProfile(QSurfaceFormat::CompatibilityProfile);
1201 format.setSwapInterval(SwapInterval);
1202 QSurfaceFormat::setDefaultFormat(format);
1207 QApplication::setDesktopSettingsAware(
false);
1210#if CONFIG_DRM && CONFIG_QTPRIVATEHEADERS
1216#if CONFIG_WAYLANDEXTRAS
1221 MythDRMDevice::SetupDRM(CmdLine);
1227#if defined (Q_OS_LINUX) && CONFIG_EGL && CONFIG_X11
1239 QString soft = qgetenv(
"LIBGL_ALWAYS_SOFTWARE");
1240 bool ignore = soft ==
"1" || soft.compare(
"true", Qt::CaseInsensitive) == 0;
1241 bool allow = qEnvironmentVariableIsEmpty(
"MYTHTV_NO_EGL") && !ignore;
1242 bool force = !qEnvironmentVariableIsEmpty(
"MYTHTV_FORCE_EGL");
1247 if (vendor.contains(
"nvidia", Qt::CaseInsensitive) && !
force)
1249 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Not requesting EGL for vendor '%1'").arg(vendor));
1251 else if (!vendor.isEmpty() ||
force)
1253 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Requesting EGL for vendor '%1'").arg(vendor));
1254 setenv(
"QT_XCB_GL_INTEGRATION",
"xcb_egl", 0);
1259#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1261 QApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
1265 if (
auto display = CmdLine.
toString(
"display"); !display.isEmpty())
1276 if (forcevrr && !(gsyncchanged || freesyncchanged))
1277 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()
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 CurrentScreenChanged(QScreen *qScreen)
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()
void CurrentDPIChanged(qreal DPI)
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)