7#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
8#include <QtEnvironmentVariables>
9#include <QtSystemDetection>
13#include <QApplication>
14#include <QElapsedTimer>
17#include "libmythbase/mythconfig.h"
20#include <QQuickWindow>
35#if CONFIG_WAYLANDEXTRAS
60#define LOC QString("Display: ")
106#if CONFIG_WAYLANDEXTRAS
152 bool spanall =
false;
157 result.append(tr(
"Spanning %1 screens").arg(screencount));
158 result.append(tr(
"Total bounds") + QString(
"\t: %1x%2")
167 result.append(tr(
"Supported HDR formats\t: %1").arg(hdr.join(
",")));
169 result.append(tr(
"HDR mode switching is not available"));
170 if (
auto brightness =
m_hdrState->GetMaxLuminance(); brightness > 1.0)
171 result.append(tr(
"Max display brightness\t: %1 nits").arg(
static_cast<int>(brightness)));
176 result.append(tr(
"Variable refresh rate '%1': %2 %3")
178 m_vrrState->Enabled() ? tr(
"Enabled") : tr(
"Disabled"),
183 const auto screens = QGuiApplication::screens();
185 for (
auto *screen : std::as_const(screens))
190 auto id = QString(
"(%1)").arg(screen->manufacturer());
191 if (screen ==
current && !spanall)
192 result.append(tr(
"Current screen\t: %1 %2").arg(screen->name(),
id));
194 result.append(tr(
"Screen\t\t: %1 %2").arg(screen->name(),
id));
195 result.append(tr(
"Size") + QString(
"\t\t: %1mmx%2mm")
196 .arg(screen->physicalSize().width()).arg(screen->physicalSize().height()));
201 result.append(tr(
"Aspect ratio") + QString(
"\t: %1 (%2)")
202 .arg(aspect, 0,
'f', 3).arg(source));
205 result.append(tr(
"Current mode") + QString(
"\t: %1x%2@%3Hz")
211 result.append(tr(
"Available modes:"));
212 for (
const auto & mode : std::ranges::reverse_view(modes))
213 result.append(
" " + mode.ToString());
223 : m_screen(GetDesiredScreen())
232 auto *guiapp = qobject_cast<QGuiApplication *>(QCoreApplication::instance());
233 if (guiapp ==
nullptr)
243 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Deleting");
273 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Have main widget");
277 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Have main window");
303 return QGuiApplication::screens().size();
353 QScreen* newscreen =
nullptr;
363 bool windowed =
GetMythDB()->GetBoolSetting(
"RunFrontendInWindow",
false) &&
369 QPoint point = windowed ?
override.topLeft() :
override.bottomRight();
370 QList screens = QGuiApplication::screens();
371 for (QScreen *screen : std::as_const(screens))
373 if (screen->geometry().contains(point))
376 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
377 "Geometry override places window in screen '%1'").arg(newscreen->name()));
386 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Using primary screen for multiscreen");
387 newscreen = QGuiApplication::primaryScreen();
394 QList screens = QGuiApplication::screens();
395 for (QScreen *screen : std::as_const(screens))
397 if (!name.isEmpty() && name == screen->name())
399 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Found screen '%1'").arg(name));
409 int screen_num = name.toInt(&ok);
410 QList<QScreen *>screens = QGuiApplication::screens();
411 if (ok && (screen_num >= 0) && (screen_num < screens.size()))
413 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Found screen number %1 (%2)")
414 .arg(name, screens[screen_num]->name()));
415 newscreen = screens[screen_num];
422 QScreen *primary = QGuiApplication::primaryScreen();
423 if (name.isEmpty() && primary)
425 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Defaulting to primary screen (%1)")
426 .arg(primary->name()));
428 else if (name !=
"-1" && primary)
430 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen '%1' not found, defaulting to primary screen (%2)")
431 .arg(name, primary->name()));
446 disconnect(
m_screen,
nullptr,
this,
nullptr);
457 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Qt screen pixel ratio changed to %1")
458 .arg(DPI, 2,
'f', 2,
'0'));
475 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen '%1' removed").arg(qScreen->name()));
481 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"New screen geometry: %1x%2+%3+%4")
482 .arg(Geo.width()).arg(Geo.height()).arg(Geo.left()).arg(Geo.top()));
508 m_physicalSize = QSize(
static_cast<int>(screen->physicalSize().width()),
509 static_cast<int>(screen->physicalSize().height()));
520 QString mfg = qScreen->manufacturer();
523 QString model = qScreen->model();
526 return QString(
"(Make: %1 Model: %2)").arg(mfg, model);
534 auto geom = qScreen->geometry();
535 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"%1 screen '%2' %3")
537 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Qt screen pixel ratio: %1")
538 .arg(qScreen->devicePixelRatio(), 2,
'f', 2,
'0'));
539 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Geometry: %1x%2+%3+%4 Size(Qt): %5mmx%6mm")
540 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top())
541 .arg(qScreen->physicalSize().width()).arg(qScreen->physicalSize().height()));
543 if (qScreen->virtualGeometry() != geom)
545 geom = qScreen->virtualGeometry();
546 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Total virtual geometry: %1x%2+%3+%4")
547 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top()));
568 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Desktop video mode: %1x%2 %3Hz")
573 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"Display is using sRGB colourspace");
575 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"Display has custom colourspace");
587 double aspectratio = 0.0;
588 GetMythDB()->GetResolutionSetting(
"GuiVidMode", pixelwidth, pixelheight, aspectratio, refreshrate);
589 GetMythDB()->GetResolutionSetting(
"DisplaySize", mmwidth, mmheight);
593 pixelwidth = pixelheight = 0;
594 GetMythDB()->GetResolutionSetting(
"TVVidMode", pixelwidth, pixelheight, aspectratio, refreshrate);
598 for (
int i = 0;
true; ++i)
604 double iaspect = 0.0;
605 double oaspect = 0.0;
609 GetMythDB()->GetResolutionSetting(
"VidMode", iw, ih, iaspect, irate, i);
610 GetMythDB()->GetResolutionSetting(
"TVVidMode", ow, oh, oaspect, orate, i);
612 if ((!iw && !ih && qFuzzyIsNull(irate)) || !(ih && ow && oh))
616 MythDisplayMode scr(QSize(ow, oh), QSize(mmwidth, mmheight), oaspect, orate);
631 const auto screens = QGuiApplication::screens();
632 for (
auto * screen : std::as_const(screens))
634 auto dim = screen->geometry();
636 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Screen %1: %2x%3 %4")
637 .arg(screen->name()).arg(dim.width()).arg(dim.height()).arg(extra));
640 const auto * primary = QGuiApplication::primaryScreen();
643 if (!screens.empty())
644 primary = screens.front();
647 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Qt has no screens!");
652 LOG(VB_GUI, LOG_INFO,
LOC +QString(
"Primary screen: %1.").arg(primary->name()));
655 auto dim = primary->virtualSize();
656 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Total desktop dim: %1x%2, over %3 screen[s].")
657 .arg(dim.width()).arg(dim.height()).arg(numScreens));
661 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Using entire desktop."));
666 if (!
GetMythDB()->GetBoolSetting(
"ForceFullScreen",
false) &&
667 GetMythDB()->GetBoolSetting(
"RunFrontendInWindow",
false))
669 LOG(VB_GUI, LOG_INFO,
LOC +
"Running in a window");
679 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Using screen %1: %2x%3 at %4+%5")
717 double targetrate = 0.0;
718 double aspectoverride = 0.0;
722 Size.width(), Size.height(), Rate);
729 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Found custom screen override %1x%2 Aspect %3")
730 .arg(next.
Width()).arg(next.
Height()).arg(aspectoverride));
736 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Trying to match best refresh rate %1Hz")
737 .arg(Rate, 0,
'f', 3));
752 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Ignoring mode switch to %1Hz - VRR enabled")
753 .arg(Rate, 0,
'f', 3));
756 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Allowing mode switch with VRR enabled for new resolution");
762 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Using current mode %1x%2@%3Hz")
767 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Trying mode %1x%2@%3Hz")
772 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to change mode to %1x%2@%3Hz")
783 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Switched to %1x%2@%3Hz for video %4x%5")
785 .arg(
m_refreshRate, 0,
'f', 3).arg(Size.width()).arg(Size.height()));
801 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Using %1x%2@%3Hz for GUI")
808 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to change mode to %1x%2@%3Hz")
818 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Switched to %1x%2@%3Hz")
839 auto max = std::get<1>(range) > 60 ? std::get<1>(range) : 60;
852 auto targetrate =
static_cast<double>(NAN);
858 return modes[
static_cast<size_t>(match)].RefreshRates();
891 auto valid = [](
double Aspect) {
return (Aspect > 0.1 && Aspect < 10.0); };
897 Source = tr(
"Video mode override");
920 Source = tr(
"Multiscreen estimate");
931 if (valid(calculated))
933 if ((
override < 0.0) || !valid(detected))
935 Source = tr(
"Square pixels");
968 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Supported HDR formats: %1")
970 if (
auto brightness =
m_hdrState->GetMaxLuminance(); brightness > 1.0)
972 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Display reports max brightness of %1 nits")
973 .arg(
static_cast<int>(brightness)));
989 auto sortscreens = [](
const QScreen* First,
const QScreen* Second)
991 if (First->geometry().left() < Second->geometry().left())
993 if (First->geometry().top() < Second->geometry().top())
999 double result = 16.0 / 9.0;
1001 QList<QScreen*> screens;
1003 screens =
m_screen->virtualSiblings();
1004 if (screens.empty())
1010 std::sort(screens.begin(), screens.end(), sortscreens);
1011 QList<double> aspectratios;
1012 QSize totalresolution;
1017 for (
auto it = screens.constBegin() ; it != screens.constEnd(); ++it)
1019 QRect geom = (*it)->geometry();
1020 totalresolution += geom.size();
1021 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"%1x%2+%3+%4 %5")
1022 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top())
1023 .arg((*it)->physicalSize().width() / (*it)->physicalSize().height()));
1024 if (lastleft < geom.left())
1027 lastleft = geom.left();
1029 if (lasttop < geom.top())
1032 lasttop = geom.top();
1035 aspectratios << (*it)->physicalSize().width() / (*it)->physicalSize().height();
1040 if (!totalresolution.isEmpty())
1041 result =
static_cast<double>(totalresolution.width()) / totalresolution.height();
1043 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen layout: %1x%2").arg(rows).arg(columns));
1044 if (rows == columns)
1046 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Grid layout");
1048 else if (rows == 1 && columns > 1)
1050 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Horizontal layout");
1052 else if (columns == 1 && rows > 1)
1054 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Vertical layout");
1058 LOG(VB_GENERAL, LOG_INFO,
1059 LOC + QString(
"Unsupported layout - defaulting to %1 (%2/%3)")
1060 .arg(result).arg(totalresolution.width()).arg(totalresolution.height()));
1065 double aspectratio = 0.0;
1066 double average = 0.0;
1068 for (
auto it2 = aspectratios.constBegin() ; it2 != aspectratios.constEnd(); ++it2, ++count)
1070 aspectratio += *it2;
1071 average = aspectratio / count;
1072 if (qAbs(*it2 - average) > 0.1)
1074 LOG(VB_GENERAL, LOG_INFO,
LOC +
1075 QString(
"Inconsistent aspect ratios - defaulting to %1 (%2/%3)")
1076 .arg(result).arg(totalresolution.width()).arg(totalresolution.height()));
1081 aspectratio = (average * columns) / rows;
1082 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Estimated aspect ratio: %1")
1104 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Waiting for resolution change");
1107 timer.setSingleShot(
true);
1109 &timer, [](){
LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Timed out waiting for screen change"); });
1125 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Waiting for new screen");
1128 timer.setSingleShot(
true);
1130 &timer, [](){
LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Timed out waiting for new screen"); });
1141 if (pauselengthinms)
1143 LOG(VB_GENERAL, LOG_INFO,
LOC +
1144 QString(
"Pausing %1ms for video mode switch").arg(pauselengthinms));
1147 timer.setSingleShot(
true);
1150 timer.start(pauselengthinms);
1160 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Available modes:");
1161 for (
const auto & videoMode : std::ranges::reverse_view(
m_videoModes))
1163 auto rates = videoMode.RefreshRates();
1164 QStringList rateslist;
1165 for (
double rate : std::ranges::reverse_view(rates))
1166 rateslist.append(QString(
"%1").arg(rate, 2,
'f', 2,
'0'));
1167 if (rateslist.empty())
1168 rateslist.append(
"Variable rate?");
1169 LOG(VB_PLAYBACK, LOG_INFO, QString(
"%1x%2\t%3")
1170 .arg(videoMode.Width()).arg(videoMode.Height()).arg(rateslist.join(
"\t")));
1182 auto forcevrr = CmdLine.
toBool(
"vrr");
1183 bool gsyncchanged =
false;
1184 bool freesyncchanged =
false;
1186#if CONFIG_QTWEBENGINE
1187 QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
1188 QQuickWindow::setSceneGraphBackend(
"software");
1189 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Using shared OpenGL Contexts");
1193 QSurfaceFormat format;
1196 if (qEnvironmentVariableIsSet(
"MYTHTV_DEPTH"))
1199 int depth =
std::clamp(qEnvironmentVariableIntValue(
"MYTHTV_DEPTH"), 6, 16);
1200 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Trying to force depth to '%1'").arg(depth));
1201 format.setRedBufferSize(depth);
1205 format.setDepthBufferSize(0);
1206 format.setStencilBufferSize(0);
1208 format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
1209 format.setProfile(QSurfaceFormat::CompatibilityProfile);
1210 format.setSwapInterval(SwapInterval);
1211 QSurfaceFormat::setDefaultFormat(format);
1216 QApplication::setDesktopSettingsAware(
false);
1219#if CONFIG_DRM && CONFIG_QTPRIVATEHEADERS
1225#if CONFIG_WAYLANDEXTRAS
1230 MythDRMDevice::SetupDRM(CmdLine);
1236#if defined (Q_OS_LINUX) && CONFIG_EGL && CONFIG_X11
1248 QString soft = qgetenv(
"LIBGL_ALWAYS_SOFTWARE");
1249 bool ignore = soft ==
"1" || soft.compare(
"true", Qt::CaseInsensitive) == 0;
1250 bool allow = qEnvironmentVariableIsEmpty(
"MYTHTV_NO_EGL") && !ignore;
1251 bool force = !qEnvironmentVariableIsEmpty(
"MYTHTV_FORCE_EGL");
1256 if (vendor.contains(
"nvidia", Qt::CaseInsensitive) && !
force)
1258 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Not requesting EGL for vendor '%1'").arg(vendor));
1260 else if (!vendor.isEmpty() ||
force)
1262 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Requesting EGL for vendor '%1'").arg(vendor));
1263 if (!qEnvironmentVariableIsSet(
"QT_XCB_GL_INTEGRATION"))
1265 qputenv(
"QT_XCB_GL_INTEGRATION",
"xcb_egl");
1271#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1273 QApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
1276 if (qEnvironmentVariableIsEmpty(
"QT_ENABLE_HIGHDPI_SCALING"))
1278 qputenv(
"QT_ENABLE_HIGHDPI_SCALING",
"0");
1283 if (
auto display = CmdLine.
toString(
"display"); !display.isEmpty())
1294 if (forcevrr && !(gsyncchanged || freesyncchanged))
1295 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Variable refresh rate not adjusted");
1298#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
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::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)