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
153 bool spanall =
false;
158 result.append(tr(
"Spanning %1 screens").arg(screencount));
159 result.append(tr(
"Total bounds") + QString(
"\t: %1x%2")
168 result.append(tr(
"Supported HDR formats\t: %1").arg(hdr.join(
",")));
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)));
177 result.append(tr(
"Variable refresh rate '%1': %2 %3")
179 m_vrrState->Enabled() ? tr(
"Enabled") : tr(
"Disabled"),
184 const auto screens = QGuiApplication::screens();
187 result.reserve(result.size() + (9 * screens.size()));
188 for (
auto *screen : std::as_const(screens))
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));
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()));
204 result.append(tr(
"Aspect ratio") + QString(
"\t: %1 (%2)")
205 .arg(aspect, 0,
'f', 3).arg(source));
208 result.append(tr(
"Current mode") + QString(
"\t: %1x%2@%3Hz")
214 result.append(tr(
"Available modes:"));
215 for (
const auto & mode : std::ranges::reverse_view(modes))
216 result.append(
" " + mode.ToString());
226 : m_screen(GetDesiredScreen())
235 auto *guiapp = qobject_cast<QGuiApplication *>(QCoreApplication::instance());
236 if (guiapp ==
nullptr)
246 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Deleting");
276 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Have main widget");
280 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Have main window");
306 return QGuiApplication::screens().size();
356 QScreen* newscreen =
nullptr;
366 bool windowed =
GetMythDB()->GetBoolSetting(
"RunFrontendInWindow",
false) &&
372 QPoint point = windowed ?
override.topLeft() :
override.bottomRight();
373 QList screens = QGuiApplication::screens();
374 for (QScreen *screen : std::as_const(screens))
376 if (screen->geometry().contains(point))
379 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
380 "Geometry override places window in screen '%1'").arg(newscreen->name()));
389 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Using primary screen for multiscreen");
390 newscreen = QGuiApplication::primaryScreen();
397 QList screens = QGuiApplication::screens();
398 for (QScreen *screen : std::as_const(screens))
400 if (!name.isEmpty() && name == screen->name())
402 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Found screen '%1'").arg(name));
412 int screen_num = name.toInt(&ok);
413 QList<QScreen *>screens = QGuiApplication::screens();
414 if (ok && (screen_num >= 0) && (screen_num < screens.size()))
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];
425 QScreen *primary = QGuiApplication::primaryScreen();
426 if (name.isEmpty() && primary)
428 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Defaulting to primary screen (%1)")
429 .arg(primary->name()));
431 else if (name !=
"-1" && primary)
433 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen '%1' not found, defaulting to primary screen (%2)")
434 .arg(name, primary->name()));
449 disconnect(
m_screen,
nullptr,
this,
nullptr);
460 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Qt screen pixel ratio changed to %1")
461 .arg(DPI, 2,
'f', 2,
'0'));
478 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen '%1' removed").arg(qScreen->name()));
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()));
511 m_physicalSize = QSize(
static_cast<int>(screen->physicalSize().width()),
512 static_cast<int>(screen->physicalSize().height()));
523 QString mfg = qScreen->manufacturer();
526 QString model = qScreen->model();
529 return QString(
"(Make: %1 Model: %2)").arg(mfg, model);
537 auto geom = qScreen->geometry();
538 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"%1 screen '%2' %3")
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()));
546 if (qScreen->virtualGeometry() != geom)
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()));
571 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Desktop video mode: %1x%2 %3Hz")
576 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"Display is using sRGB colourspace");
578 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"Display has custom colourspace");
590 double aspectratio = 0.0;
591 GetMythDB()->GetResolutionSetting(
"GuiVidMode", pixelwidth, pixelheight, aspectratio, refreshrate);
592 GetMythDB()->GetResolutionSetting(
"DisplaySize", mmwidth, mmheight);
596 pixelwidth = pixelheight = 0;
597 GetMythDB()->GetResolutionSetting(
"TVVidMode", pixelwidth, pixelheight, aspectratio, refreshrate);
601 for (
int i = 0;
true; ++i)
607 double iaspect = 0.0;
608 double oaspect = 0.0;
612 GetMythDB()->GetResolutionSetting(
"VidMode", iw, ih, iaspect, irate, i);
613 GetMythDB()->GetResolutionSetting(
"TVVidMode", ow, oh, oaspect, orate, i);
615 if ((!iw && !ih && qFuzzyIsNull(irate)) || !(ih && ow && oh))
619 MythDisplayMode scr(QSize(ow, oh), QSize(mmwidth, mmheight), oaspect, orate);
634 const auto screens = QGuiApplication::screens();
635 for (
auto * screen : std::as_const(screens))
637 auto dim = screen->geometry();
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));
643 const auto * primary = QGuiApplication::primaryScreen();
646 if (!screens.empty())
647 primary = screens.front();
650 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Qt has no screens!");
655 LOG(VB_GUI, LOG_INFO,
LOC +QString(
"Primary screen: %1.").arg(primary->name()));
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));
664 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Using entire desktop."));
669 if (!
GetMythDB()->GetBoolSetting(
"ForceFullScreen",
false) &&
670 GetMythDB()->GetBoolSetting(
"RunFrontendInWindow",
false))
672 LOG(VB_GUI, LOG_INFO,
LOC +
"Running in a window");
682 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Using screen %1: %2x%3 at %4+%5")
720 double targetrate = 0.0;
721 double aspectoverride = 0.0;
725 Size.width(), Size.height(), Rate);
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));
739 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Trying to match best refresh rate %1Hz")
740 .arg(Rate, 0,
'f', 3));
755 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Ignoring mode switch to %1Hz - VRR enabled")
756 .arg(Rate, 0,
'f', 3));
759 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Allowing mode switch with VRR enabled for new resolution");
765 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Using current mode %1x%2@%3Hz")
770 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Trying mode %1x%2@%3Hz")
775 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to change mode to %1x%2@%3Hz")
786 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Switched to %1x%2@%3Hz for video %4x%5")
788 .arg(
m_refreshRate, 0,
'f', 3).arg(Size.width()).arg(Size.height()));
804 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Using %1x%2@%3Hz for GUI")
811 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to change mode to %1x%2@%3Hz")
821 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Switched to %1x%2@%3Hz")
842 auto max = std::get<1>(range) > 60 ? std::get<1>(range) : 60;
855 auto targetrate =
static_cast<double>(NAN);
861 return modes[
static_cast<size_t>(match)].RefreshRates();
894 auto valid = [](
double Aspect) {
return (Aspect > 0.1 && Aspect < 10.0); };
900 Source = tr(
"Video mode override");
923 Source = tr(
"Multiscreen estimate");
934 if (valid(calculated))
936 if ((
override < 0.0) || !valid(detected))
938 Source = tr(
"Square pixels");
971 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Supported HDR formats: %1")
973 if (
auto brightness =
m_hdrState->GetMaxLuminance(); brightness > 1.0)
975 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Display reports max brightness of %1 nits")
976 .arg(
static_cast<int>(brightness)));
992 auto sortscreens = [](
const QScreen* First,
const QScreen* Second)
994 if (First->geometry().left() < Second->geometry().left())
996 if (First->geometry().top() < Second->geometry().top())
1002 double result = 16.0 / 9.0;
1004 QList<QScreen*> screens;
1006 screens =
m_screen->virtualSiblings();
1007 if (screens.empty())
1013 std::sort(screens.begin(), screens.end(), sortscreens);
1014 QList<double> aspectratios;
1015 QSize totalresolution;
1020 for (
auto it = screens.constBegin() ; it != screens.constEnd(); ++it)
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())
1030 lastleft = geom.left();
1032 if (lasttop < geom.top())
1035 lasttop = geom.top();
1038 aspectratios << (*it)->physicalSize().width() / (*it)->physicalSize().height();
1043 if (!totalresolution.isEmpty())
1044 result =
static_cast<double>(totalresolution.width()) / totalresolution.height();
1046 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen layout: %1x%2").arg(rows).arg(columns));
1047 if (rows == columns)
1049 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Grid layout");
1051 else if (rows == 1 && columns > 1)
1053 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Horizontal layout");
1055 else if (columns == 1 && rows > 1)
1057 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Vertical layout");
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()));
1068 double aspectratio = 0.0;
1069 double average = 0.0;
1071 for (
auto it2 = aspectratios.constBegin() ; it2 != aspectratios.constEnd(); ++it2, ++count)
1073 aspectratio += *it2;
1074 average = aspectratio / count;
1075 if (qAbs(*it2 - average) > 0.1)
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()));
1084 aspectratio = (average * columns) / rows;
1085 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Estimated aspect ratio: %1")
1107 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Waiting for resolution change");
1110 timer.setSingleShot(
true);
1112 &timer, [](){
LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Timed out waiting for screen change"); });
1128 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Waiting for new screen");
1131 timer.setSingleShot(
true);
1133 &timer, [](){
LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Timed out waiting for new screen"); });
1144 if (pauselengthinms)
1146 LOG(VB_GENERAL, LOG_INFO,
LOC +
1147 QString(
"Pausing %1ms for video mode switch").arg(pauselengthinms));
1150 timer.setSingleShot(
true);
1153 timer.start(pauselengthinms);
1163 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Available modes:");
1164 QStringList rateslist;
1165 for (
const auto & videoMode : std::ranges::reverse_view(
m_videoModes))
1167 auto rates = videoMode.RefreshRates();
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?");
1174 LOG(VB_PLAYBACK, LOG_INFO, QString(
"%1x%2\t%3")
1175 .arg(videoMode.Width()).arg(videoMode.Height()).arg(rateslist.join(
"\t")));
1187 auto forcevrr = CmdLine.
toBool(
"vrr");
1188 bool gsyncchanged =
false;
1189 bool freesyncchanged =
false;
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");
1198 QSurfaceFormat format;
1201 if (qEnvironmentVariableIsSet(
"MYTHTV_DEPTH"))
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);
1210 format.setDepthBufferSize(0);
1211 format.setStencilBufferSize(0);
1213 format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
1214 format.setProfile(QSurfaceFormat::CompatibilityProfile);
1215 format.setSwapInterval(SwapInterval);
1216 QSurfaceFormat::setDefaultFormat(format);
1221 QApplication::setDesktopSettingsAware(
false);
1224#if CONFIG_DRM && CONFIG_QTPRIVATEHEADERS
1230#if CONFIG_WAYLANDEXTRAS
1235 MythDRMDevice::SetupDRM(CmdLine);
1241#if defined (Q_OS_LINUX) && CONFIG_EGL && CONFIG_X11
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");
1261 if (vendor.contains(
"nvidia", Qt::CaseInsensitive) && !
force)
1263 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Not requesting EGL for vendor '%1'").arg(vendor));
1265 else if (!vendor.isEmpty() ||
force)
1267 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Requesting EGL for vendor '%1'").arg(vendor));
1268 if (!qEnvironmentVariableIsSet(
"QT_XCB_GL_INTEGRATION"))
1270 qputenv(
"QT_XCB_GL_INTEGRATION",
"xcb_egl");
1276#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1278 QApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
1281 if (qEnvironmentVariableIsEmpty(
"QT_ENABLE_HIGHDPI_SCALING"))
1283 qputenv(
"QT_ENABLE_HIGHDPI_SCALING",
"0");
1288 if (
auto display = CmdLine.
toString(
"display"); !display.isEmpty())
1299 if (forcevrr && !(gsyncchanged || freesyncchanged))
1300 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Variable refresh rate not adjusted");
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
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)