7#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
8#include <QtSystemDetection>
12#include <QApplication>
13#include <QElapsedTimer>
16#include "libmythbase/mythconfig.h"
19#include <QQuickWindow>
34#if CONFIG_WAYLANDEXTRAS
59#define LOC QString("Display: ")
105#if CONFIG_WAYLANDEXTRAS
151 bool spanall =
false;
156 result.append(tr(
"Spanning %1 screens").arg(screencount));
157 result.append(tr(
"Total bounds") + QString(
"\t: %1x%2")
166 result.append(tr(
"Supported HDR formats\t: %1").arg(hdr.join(
",")));
168 result.append(tr(
"HDR mode switching is not available"));
169 if (
auto brightness =
m_hdrState->GetMaxLuminance(); brightness > 1.0)
170 result.append(tr(
"Max display brightness\t: %1 nits").arg(
static_cast<int>(brightness)));
175 result.append(tr(
"Variable refresh rate '%1': %2 %3")
177 m_vrrState->Enabled() ? tr(
"Enabled") : tr(
"Disabled"),
182 const auto screens = QGuiApplication::screens();
184 for (
auto *screen : std::as_const(screens))
189 auto id = QString(
"(%1)").arg(screen->manufacturer());
190 if (screen ==
current && !spanall)
191 result.append(tr(
"Current screen\t: %1 %2").arg(screen->name(),
id));
193 result.append(tr(
"Screen\t\t: %1 %2").arg(screen->name(),
id));
194 result.append(tr(
"Size") + QString(
"\t\t: %1mmx%2mm")
195 .arg(screen->physicalSize().width()).arg(screen->physicalSize().height()));
200 result.append(tr(
"Aspect ratio") + QString(
"\t: %1 (%2)")
201 .arg(aspect, 0,
'f', 3).arg(source));
204 result.append(tr(
"Current mode") + QString(
"\t: %1x%2@%3Hz")
210 result.append(tr(
"Available modes:"));
211 for (
const auto & mode : std::ranges::reverse_view(modes))
212 result.append(
" " + mode.ToString());
222 : m_screen(GetDesiredScreen())
231 auto *guiapp = qobject_cast<QGuiApplication *>(QCoreApplication::instance());
232 if (guiapp ==
nullptr)
242 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Deleting");
272 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Have main widget");
276 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Have main window");
302 return QGuiApplication::screens().size();
352 QScreen* newscreen =
nullptr;
362 bool windowed =
GetMythDB()->GetBoolSetting(
"RunFrontendInWindow",
false) &&
368 QPoint point = windowed ?
override.topLeft() :
override.bottomRight();
369 QList screens = QGuiApplication::screens();
370 for (QScreen *screen : std::as_const(screens))
372 if (screen->geometry().contains(point))
375 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
376 "Geometry override places window in screen '%1'").arg(newscreen->name()));
385 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Using primary screen for multiscreen");
386 newscreen = QGuiApplication::primaryScreen();
393 QList screens = QGuiApplication::screens();
394 for (QScreen *screen : std::as_const(screens))
396 if (!name.isEmpty() && name == screen->name())
398 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Found screen '%1'").arg(name));
408 int screen_num = name.toInt(&ok);
409 QList<QScreen *>screens = QGuiApplication::screens();
410 if (ok && (screen_num >= 0) && (screen_num < screens.size()))
412 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Found screen number %1 (%2)")
413 .arg(name, screens[screen_num]->name()));
414 newscreen = screens[screen_num];
421 QScreen *primary = QGuiApplication::primaryScreen();
422 if (name.isEmpty() && primary)
424 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Defaulting to primary screen (%1)")
425 .arg(primary->name()));
427 else if (name !=
"-1" && primary)
429 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen '%1' not found, defaulting to primary screen (%2)")
430 .arg(name, primary->name()));
445 disconnect(
m_screen,
nullptr,
this,
nullptr);
456 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Qt screen pixel ratio changed to %1")
457 .arg(DPI, 2,
'f', 2,
'0'));
474 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen '%1' removed").arg(qScreen->name()));
480 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"New screen geometry: %1x%2+%3+%4")
481 .arg(Geo.width()).arg(Geo.height()).arg(Geo.left()).arg(Geo.top()));
507 m_physicalSize = QSize(
static_cast<int>(screen->physicalSize().width()),
508 static_cast<int>(screen->physicalSize().height()));
519 QString mfg = qScreen->manufacturer();
522 QString model = qScreen->model();
525 return QString(
"(Make: %1 Model: %2)").arg(mfg, model);
533 auto geom = qScreen->geometry();
534 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"%1 screen '%2' %3")
536 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Qt screen pixel ratio: %1")
537 .arg(qScreen->devicePixelRatio(), 2,
'f', 2,
'0'));
538 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Geometry: %1x%2+%3+%4 Size(Qt): %5mmx%6mm")
539 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top())
540 .arg(qScreen->physicalSize().width()).arg(qScreen->physicalSize().height()));
542 if (qScreen->virtualGeometry() != geom)
544 geom = qScreen->virtualGeometry();
545 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Total virtual geometry: %1x%2+%3+%4")
546 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top()));
567 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Desktop video mode: %1x%2 %3Hz")
572 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"Display is using sRGB colourspace");
574 LOG(VB_GENERAL, LOG_NOTICE,
LOC +
"Display has custom colourspace");
586 double aspectratio = 0.0;
587 GetMythDB()->GetResolutionSetting(
"GuiVidMode", pixelwidth, pixelheight, aspectratio, refreshrate);
588 GetMythDB()->GetResolutionSetting(
"DisplaySize", mmwidth, mmheight);
592 pixelwidth = pixelheight = 0;
593 GetMythDB()->GetResolutionSetting(
"TVVidMode", pixelwidth, pixelheight, aspectratio, refreshrate);
597 for (
int i = 0;
true; ++i)
603 double iaspect = 0.0;
604 double oaspect = 0.0;
608 GetMythDB()->GetResolutionSetting(
"VidMode", iw, ih, iaspect, irate, i);
609 GetMythDB()->GetResolutionSetting(
"TVVidMode", ow, oh, oaspect, orate, i);
611 if ((!iw && !ih && qFuzzyIsNull(irate)) || !(ih && ow && oh))
615 MythDisplayMode scr(QSize(ow, oh), QSize(mmwidth, mmheight), oaspect, orate);
630 const auto screens = QGuiApplication::screens();
631 for (
auto * screen : std::as_const(screens))
633 auto dim = screen->geometry();
635 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Screen %1: %2x%3 %4")
636 .arg(screen->name()).arg(dim.width()).arg(dim.height()).arg(extra));
639 const auto * primary = QGuiApplication::primaryScreen();
642 if (!screens.empty())
643 primary = screens.front();
646 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Qt has no screens!");
651 LOG(VB_GUI, LOG_INFO,
LOC +QString(
"Primary screen: %1.").arg(primary->name()));
654 auto dim = primary->virtualSize();
655 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Total desktop dim: %1x%2, over %3 screen[s].")
656 .arg(dim.width()).arg(dim.height()).arg(numScreens));
660 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Using entire desktop."));
665 if (!
GetMythDB()->GetBoolSetting(
"ForceFullScreen",
false) &&
666 GetMythDB()->GetBoolSetting(
"RunFrontendInWindow",
false))
668 LOG(VB_GUI, LOG_INFO,
LOC +
"Running in a window");
678 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Using screen %1: %2x%3 at %4+%5")
716 double targetrate = 0.0;
717 double aspectoverride = 0.0;
721 Size.width(), Size.height(), Rate);
728 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Found custom screen override %1x%2 Aspect %3")
729 .arg(next.
Width()).arg(next.
Height()).arg(aspectoverride));
735 LOG(VB_PLAYBACK, LOG_INFO,
LOC + QString(
"Trying to match best refresh rate %1Hz")
736 .arg(Rate, 0,
'f', 3));
751 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Ignoring mode switch to %1Hz - VRR enabled")
752 .arg(Rate, 0,
'f', 3));
755 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Allowing mode switch with VRR enabled for new resolution");
761 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Using current mode %1x%2@%3Hz")
766 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Trying mode %1x%2@%3Hz")
771 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to change mode to %1x%2@%3Hz")
782 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Switched to %1x%2@%3Hz for video %4x%5")
784 .arg(
m_refreshRate, 0,
'f', 3).arg(Size.width()).arg(Size.height()));
800 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Using %1x%2@%3Hz for GUI")
807 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Failed to change mode to %1x%2@%3Hz")
817 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Switched to %1x%2@%3Hz")
838 auto max = std::get<1>(range) > 60 ? std::get<1>(range) : 60;
851 auto targetrate =
static_cast<double>(NAN);
857 return modes[
static_cast<size_t>(match)].RefreshRates();
890 auto valid = [](
double Aspect) {
return (Aspect > 0.1 && Aspect < 10.0); };
896 Source = tr(
"Video mode override");
919 Source = tr(
"Multiscreen estimate");
930 if (valid(calculated))
932 if ((
override < 0.0) || !valid(detected))
934 Source = tr(
"Square pixels");
967 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Supported HDR formats: %1")
969 if (
auto brightness =
m_hdrState->GetMaxLuminance(); brightness > 1.0)
971 LOG(VB_GENERAL, LOG_NOTICE,
LOC + QString(
"Display reports max brightness of %1 nits")
972 .arg(
static_cast<int>(brightness)));
988 auto sortscreens = [](
const QScreen* First,
const QScreen* Second)
990 if (First->geometry().left() < Second->geometry().left())
992 if (First->geometry().top() < Second->geometry().top())
998 double result = 16.0 / 9.0;
1000 QList<QScreen*> screens;
1002 screens =
m_screen->virtualSiblings();
1003 if (screens.empty())
1009 std::sort(screens.begin(), screens.end(), sortscreens);
1010 QList<double> aspectratios;
1011 QSize totalresolution;
1016 for (
auto it = screens.constBegin() ; it != screens.constEnd(); ++it)
1018 QRect geom = (*it)->geometry();
1019 totalresolution += geom.size();
1020 LOG(VB_PLAYBACK, LOG_DEBUG,
LOC + QString(
"%1x%2+%3+%4 %5")
1021 .arg(geom.width()).arg(geom.height()).arg(geom.left()).arg(geom.top())
1022 .arg((*it)->physicalSize().width() / (*it)->physicalSize().height()));
1023 if (lastleft < geom.left())
1026 lastleft = geom.left();
1028 if (lasttop < geom.top())
1031 lasttop = geom.top();
1034 aspectratios << (*it)->physicalSize().width() / (*it)->physicalSize().height();
1039 if (!totalresolution.isEmpty())
1040 result =
static_cast<double>(totalresolution.width()) / totalresolution.height();
1042 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Screen layout: %1x%2").arg(rows).arg(columns));
1043 if (rows == columns)
1045 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Grid layout");
1047 else if (rows == 1 && columns > 1)
1049 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Horizontal layout");
1051 else if (columns == 1 && rows > 1)
1053 LOG(VB_GENERAL, LOG_DEBUG,
LOC +
"Vertical layout");
1057 LOG(VB_GENERAL, LOG_INFO,
1058 LOC + QString(
"Unsupported layout - defaulting to %1 (%2/%3)")
1059 .arg(result).arg(totalresolution.width()).arg(totalresolution.height()));
1064 double aspectratio = 0.0;
1065 double average = 0.0;
1067 for (
auto it2 = aspectratios.constBegin() ; it2 != aspectratios.constEnd(); ++it2, ++count)
1069 aspectratio += *it2;
1070 average = aspectratio / count;
1071 if (qAbs(*it2 - average) > 0.1)
1073 LOG(VB_GENERAL, LOG_INFO,
LOC +
1074 QString(
"Inconsistent aspect ratios - defaulting to %1 (%2/%3)")
1075 .arg(result).arg(totalresolution.width()).arg(totalresolution.height()));
1080 aspectratio = (average * columns) / rows;
1081 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Estimated aspect ratio: %1")
1103 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Waiting for resolution change");
1106 timer.setSingleShot(
true);
1108 &timer, [](){
LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Timed out waiting for screen change"); });
1124 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Waiting for new screen");
1127 timer.setSingleShot(
true);
1129 &timer, [](){
LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Timed out waiting for new screen"); });
1140 if (pauselengthinms)
1142 LOG(VB_GENERAL, LOG_INFO,
LOC +
1143 QString(
"Pausing %1ms for video mode switch").arg(pauselengthinms));
1146 timer.setSingleShot(
true);
1149 timer.start(pauselengthinms);
1159 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"Available modes:");
1160 for (
const auto & videoMode : std::ranges::reverse_view(
m_videoModes))
1162 auto rates = videoMode.RefreshRates();
1163 QStringList rateslist;
1164 for (
double rate : std::ranges::reverse_view(rates))
1165 rateslist.append(QString(
"%1").arg(rate, 2,
'f', 2,
'0'));
1166 if (rateslist.empty())
1167 rateslist.append(
"Variable rate?");
1168 LOG(VB_PLAYBACK, LOG_INFO, QString(
"%1x%2\t%3")
1169 .arg(videoMode.Width()).arg(videoMode.Height()).arg(rateslist.join(
"\t")));
1181 auto forcevrr = CmdLine.
toBool(
"vrr");
1182 bool gsyncchanged =
false;
1183 bool freesyncchanged =
false;
1185#if CONFIG_QTWEBENGINE
1186 QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
1187 QQuickWindow::setSceneGraphBackend(
"software");
1188 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Using shared OpenGL Contexts");
1192 QSurfaceFormat format;
1195 if (qEnvironmentVariableIsSet(
"MYTHTV_DEPTH"))
1198 int depth =
std::clamp(qEnvironmentVariableIntValue(
"MYTHTV_DEPTH"), 6, 16);
1199 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Trying to force depth to '%1'").arg(depth));
1200 format.setRedBufferSize(depth);
1204 format.setDepthBufferSize(0);
1205 format.setStencilBufferSize(0);
1207 format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
1208 format.setProfile(QSurfaceFormat::CompatibilityProfile);
1209 format.setSwapInterval(SwapInterval);
1210 QSurfaceFormat::setDefaultFormat(format);
1215 QApplication::setDesktopSettingsAware(
false);
1218#if CONFIG_DRM && CONFIG_QTPRIVATEHEADERS
1224#if CONFIG_WAYLANDEXTRAS
1229 MythDRMDevice::SetupDRM(CmdLine);
1235#if defined (Q_OS_LINUX) && CONFIG_EGL && CONFIG_X11
1247 QString soft = qgetenv(
"LIBGL_ALWAYS_SOFTWARE");
1248 bool ignore = soft ==
"1" || soft.compare(
"true", Qt::CaseInsensitive) == 0;
1249 bool allow = qEnvironmentVariableIsEmpty(
"MYTHTV_NO_EGL") && !ignore;
1250 bool force = !qEnvironmentVariableIsEmpty(
"MYTHTV_FORCE_EGL");
1255 if (vendor.contains(
"nvidia", Qt::CaseInsensitive) && !
force)
1257 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Not requesting EGL for vendor '%1'").arg(vendor));
1259 else if (!vendor.isEmpty() ||
force)
1261 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Requesting EGL for vendor '%1'").arg(vendor));
1262 setenv(
"QT_XCB_GL_INTEGRATION",
"xcb_egl", 0);
1267#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1269 QApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
1272 if (qEnvironmentVariableIsEmpty(
"QT_ENABLE_HIGHDPI_SCALING"))
1274 qputenv(
"QT_ENABLE_HIGHDPI_SCALING",
"0");
1279 if (
auto display = CmdLine.
toString(
"display"); !display.isEmpty())
1290 if (forcevrr && !(gsyncchanged || freesyncchanged))
1291 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::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)