From d6798be0c5adc0c40dd3f7f9e94eab5a4de96613 Mon Sep 17 00:00:00 2001 From: Mark Kendall Date: Thu, 26 Dec 2019 12:34:33 +0000 Subject: [PATCH] MythDisplay: Extensive cleanup and refactor - the previous changes to MythDisplay resulted in a confusing API that was amalgamated from various sources. - cleanup API for consistency - mostly removing previous GUI/VIDEO/ DESKTOP mode stuff to ensure we always track the actual video mode in use - start retrieving the EDID (X11 and DRM only at the moment) - start using the EDID data (VideoColourspace) - use the modeline to determine current screen resolution in MythXDisplay - as it is more current than the old method. - remove old, unused code from MythXDisplay. - sundry changes to method signatures for consistency and ease of use --- .../libmythtv/decoders/mythvdpauhelper.cpp | 2 +- mythtv/libs/libmythtv/mythplayer.cpp | 14 +- mythtv/libs/libmythtv/mythvideoout.cpp | 20 +- mythtv/libs/libmythtv/mythvideoout.h | 2 +- .../libmythtv/opengl/mythvideooutopengl.cpp | 2 +- mythtv/libs/libmythtv/tv_play.cpp | 8 +- mythtv/libs/libmythtv/videocolourspace.cpp | 24 +- mythtv/libs/libmythtv/videocolourspace.h | 42 +-- mythtv/libs/libmythui/mythdisplay.cpp | 324 +++++++++--------- mythtv/libs/libmythui/mythdisplay.h | 121 +++---- mythtv/libs/libmythui/mythdisplaymode.cpp | 24 +- mythtv/libs/libmythui/mythdisplaymode.h | 9 +- mythtv/libs/libmythui/mythuihelper.cpp | 7 +- .../platforms/mythdisplayandroid.cpp | 18 +- .../libmythui/platforms/mythdisplayandroid.h | 3 +- .../libmythui/platforms/mythdisplaydrm.cpp | 24 +- .../libs/libmythui/platforms/mythdisplaydrm.h | 2 +- .../libmythui/platforms/mythdisplayosx.cpp | 57 +-- .../libs/libmythui/platforms/mythdisplayosx.h | 4 +- .../platforms/mythdisplaywindows.cpp | 46 ++- .../libmythui/platforms/mythdisplaywindows.h | 2 +- .../libmythui/platforms/mythdisplayx11.cpp | 190 ++++++---- .../libs/libmythui/platforms/mythdisplayx11.h | 15 +- .../libmythui/platforms/mythdrmdevice.cpp | 12 +- .../libs/libmythui/platforms/mythdrmdevice.h | 6 +- .../libs/libmythui/platforms/mythxdisplay.cpp | 128 +++---- .../libs/libmythui/platforms/mythxdisplay.h | 75 ++-- mythtv/libs/libmythui/screensaver-x11.cpp | 2 +- mythtv/libs/libmythui/screensaver.cpp | 2 +- .../programs/mythfrontend/globalsettings.cpp | 15 +- 30 files changed, 605 insertions(+), 595 deletions(-) diff --git a/mythtv/libs/libmythtv/decoders/mythvdpauhelper.cpp b/mythtv/libs/libmythtv/decoders/mythvdpauhelper.cpp index 12701aac596..cfb128ddd01 100644 --- a/mythtv/libs/libmythtv/decoders/mythvdpauhelper.cpp +++ b/mythtv/libs/libmythtv/decoders/mythvdpauhelper.cpp @@ -90,7 +90,7 @@ static const char* DummyGetError(VdpStatus /*status*/) MythVDPAUHelper::MythVDPAUHelper(void) : m_createdDevice(true) { - m_display = OpenMythXDisplay(false); + m_display = MythXDisplay::OpenMythXDisplay(false); if (!m_display) return; diff --git a/mythtv/libs/libmythtv/mythplayer.cpp b/mythtv/libs/libmythtv/mythplayer.cpp index 6e114a82ff6..d56d64b128b 100644 --- a/mythtv/libs/libmythtv/mythplayer.cpp +++ b/mythtv/libs/libmythtv/mythplayer.cpp @@ -472,7 +472,7 @@ void MythPlayer::ReinitVideo(bool ForceUpdate) // the display refresh rate may have been changed by VideoOutput if (m_videoSync) { - int ri = m_display->GetDisplayInfo(m_frameInterval).Rate(); + int ri = m_display->GetRefreshInterval(m_frameInterval); if (ri != m_videoSync->getRefreshInterval()) { LOG(VB_PLAYBACK, LOG_INFO, LOC + @@ -1535,7 +1535,7 @@ void MythPlayer::InitAVSync(void) { m_videoSync->Start(); - m_refreshRate = m_display->GetDisplayInfo(m_frameInterval).Rate(); + m_refreshRate = m_display->GetRefreshInterval(m_frameInterval); m_rtcBase = 0; m_priorAudioTimecode = 0; @@ -2079,7 +2079,7 @@ bool MythPlayer::CanSupportDoubleRate(void) else if (m_display) { // used by the decoder before m_videoSync is created - refreshinterval = m_display->GetDisplayInfo(m_frameInterval).Rate(); + refreshinterval = m_display->GetRefreshInterval(m_frameInterval); } // At this point we may not have the correct frame rate. @@ -2169,8 +2169,8 @@ void MythPlayer::VideoStart(void) m_refreshRate = m_frameInterval; float temp_speed = (m_playSpeed == 0.0F) ? m_audio.GetStretchFactor() : m_playSpeed; - int fr_int = (1000000.0 / m_videoFrameRate / static_cast(temp_speed)); - int rf_int = m_display->GetDisplayInfo(fr_int).Rate(); + int fr_int = static_cast(1000000.0 / m_videoFrameRate / static_cast(temp_speed)); + int displayinterval = m_display->GetRefreshInterval(fr_int); // Default to interlaced playback but set the tracker to progressive // Enable autodetection of interlaced/progressive from video stream @@ -2195,13 +2195,13 @@ void MythPlayer::VideoStart(void) } else if (m_videoOutput) { - m_videoSync = VideoSync::BestMethod(m_videoOutput, static_cast(rf_int)); + m_videoSync = VideoSync::BestMethod(m_videoOutput, static_cast(displayinterval)); m_doubleFramerate = CanSupportDoubleRate(); // needs m_videoSync m_videoOutput->SetDeinterlacing(true, m_doubleFramerate); } if (!m_videoSync) - m_videoSync = new BusyWaitVideoSync(m_videoOutput, rf_int); + m_videoSync = new BusyWaitVideoSync(m_videoOutput, displayinterval); InitAVSync(); m_videoSync->Start(); diff --git a/mythtv/libs/libmythtv/mythvideoout.cpp b/mythtv/libs/libmythtv/mythvideoout.cpp index 6e78888b885..97f057aa085 100644 --- a/mythtv/libs/libmythtv/mythvideoout.cpp +++ b/mythtv/libs/libmythtv/mythvideoout.cpp @@ -918,29 +918,28 @@ void MythVideoOutput::DiscardFrames(bool KeyFrame, bool /*unused*/) * * \param width,height Resolution of the video we will be playing */ -void MythVideoOutput::ResizeForVideo(int Width, int Height) +void MythVideoOutput::ResizeForVideo(QSize Size) { if (!m_display) return; if (!m_display->UsingVideoModes()) return; - if (!Width || !Height) + if (Size.isEmpty()) { - Width = m_window.GetVideoDispDim().width(); - Height = m_window.GetVideoDispDim().height(); - if (!Width || !Height) + Size = m_window.GetVideoDispDim(); + if (Size.isEmpty()) return; } float rate = m_dbDisplayProfile ? m_dbDisplayProfile->GetOutput() : 0.0F; - bool hide = m_display->NextModeIsLarger(Width, Height); + bool hide = m_display->NextModeIsLarger(Size); MythMainWindow* window = GetMythMainWindow(); if (hide) window->hide(); - if (m_display->SwitchToVideo(Width, Height, static_cast(rate))) + if (m_display->SwitchToVideo(Size, static_cast(rate))) { // Switching to custom display resolution succeeded // Make a note of the new size @@ -987,21 +986,20 @@ void MythVideoOutput::InitDisplayMeasurements(void) if (!m_display) return; - DisplayInfo disp = m_display->GetDisplayInfo(); - QString source = "Actual"; + QString source = "Actual"; // get the physical dimensions (in mm) of the display. If using // DisplayRes, this will be overridden when we call ResizeForVideo float disp_aspect = m_window.GetDisplayAspect(); QSize disp_dim = m_dbDisplayDimensionsMM; if (disp_dim.isEmpty()) - disp_dim = disp.m_size; + disp_dim = m_display->GetPhysicalSize(); else source = "Database"; m_window.SetDisplayProperties(disp_dim, disp_aspect); // Determine window and screen dimensions in pixels - QSize screen_size = disp.m_res; + QSize screen_size = m_display->GetResolution(); QSize window_size = m_window.GetWindowRect().size(); if (screen_size.isEmpty()) diff --git a/mythtv/libs/libmythtv/mythvideoout.h b/mythtv/libs/libmythtv/mythvideoout.h index b33f591a360..69222c755d4 100644 --- a/mythtv/libs/libmythtv/mythvideoout.h +++ b/mythtv/libs/libmythtv/mythvideoout.h @@ -68,7 +68,7 @@ class MythVideoOutput virtual void EmbedInWidget(const QRect &EmbedRect); bool IsEmbedding(void); virtual void StopEmbedding(void); - virtual void ResizeForVideo(int Width = 0, int Height = 0); + virtual void ResizeForVideo(QSize Size = QSize()); virtual void Zoom(ZoomDirection Direction); virtual void ToggleMoveBottomLine(void); virtual void SaveBottomLine(void); diff --git a/mythtv/libs/libmythtv/opengl/mythvideooutopengl.cpp b/mythtv/libs/libmythtv/opengl/mythvideooutopengl.cpp index 85363625c39..5cd744472c6 100644 --- a/mythtv/libs/libmythtv/opengl/mythvideooutopengl.cpp +++ b/mythtv/libs/libmythtv/opengl/mythvideooutopengl.cpp @@ -229,7 +229,7 @@ bool MythVideoOutputOpenGL::Init(const QSize &VideoDim, const QSize &VideoDispDi // Set the display mode if required if (m_display->UsingVideoModes() && !m_window.IsEmbedding()) - ResizeForVideo(size.width(), size.height()); + ResizeForVideo(size); InitDisplayMeasurements(); // Create buffers diff --git a/mythtv/libs/libmythtv/tv_play.cpp b/mythtv/libs/libmythtv/tv_play.cpp index b86bfc094f0..4be92c898a1 100644 --- a/mythtv/libs/libmythtv/tv_play.cpp +++ b/mythtv/libs/libmythtv/tv_play.cpp @@ -1293,10 +1293,10 @@ TV::~TV(void) MythDisplay* display = MythDisplay::AcquireRelease(); if (display->UsingVideoModes()) { - bool hide = display->NextModeIsLarger(MythDisplay::GUI); + bool hide = display->NextModeIsLarger(display->GetGUIResolution()); if (hide) mwnd->hide(); - display->SwitchToGUI(MythDisplay::GUI, true); + display->SwitchToGUI(true); if (hide) mwnd->Show(); } @@ -8639,10 +8639,10 @@ void TV::DoEditSchedule(int editType) MythDisplay* display = MythDisplay::AcquireRelease(); if (display->UsingVideoModes()) { - bool hide = display->NextModeIsLarger(MythDisplay::GUI); + bool hide = display->NextModeIsLarger(display->GetGUIResolution()); if (hide) mwnd->hide(); - display->SwitchToGUI(MythDisplay::GUI, true); + display->SwitchToGUI(true); if (hide) mwnd->Show(); } diff --git a/mythtv/libs/libmythtv/videocolourspace.cpp b/mythtv/libs/libmythtv/videocolourspace.cpp index 7170b71f0b7..0b6d526bb43 100644 --- a/mythtv/libs/libmythtv/videocolourspace.cpp +++ b/mythtv/libs/libmythtv/videocolourspace.cpp @@ -1,6 +1,7 @@ // MythTV #include "mythcorecontext.h" #include "mythlogging.h" +#include "mythdisplay.h" #include "mythavutil.h" #include "videocolourspace.h" @@ -82,6 +83,17 @@ VideoColourSpace::VideoColourSpace(VideoColourSpace *Parent) SetSaturation(m_dbSettings[kPictureAttribute_Colour]); SetHue(m_dbSettings[kPictureAttribute_Hue]); SetFullRange(m_dbSettings[kPictureAttribute_Range]); + MythDisplay* display = MythDisplay::AcquireRelease(); + MythEDID& edid = display->GetEDID(); + // We assume sRGB/Rec709 by default + if (edid.Valid() && !edid.IsSRGB()) + { + m_customDisplayGamma = edid.Gamma(); + m_customDisplayPrimaries = new ColourPrimaries; + MythEDID::Primaries displayprimaries = edid.ColourPrimaries(); + memcpy(m_customDisplayPrimaries, &displayprimaries, sizeof(ColourPrimaries)); + } + MythDisplay::AcquireRelease(false); m_updatesDisabled = false; Update(); } @@ -259,7 +271,7 @@ void VideoColourSpace::Update(void) m_primaryMatrix = GetPrimaryConversion(m_colourPrimaries, m_displayPrimaries); bool primchanged = !qFuzzyCompare(tmpsrcgamma, m_colourGamma) || !qFuzzyCompare(tmpdspgamma, m_displayGamma) || - !qFuzzyCompare(tmpmatrix, m_primaryMatrix); + !qFuzzyCompare(tmpmatrix, m_primaryMatrix); Debug(); emit Updated(primchanged); } @@ -470,8 +482,9 @@ QMatrix4x4 VideoColourSpace::GetPrimaryConversion(int Source, int Dest) QMatrix4x4 result; // identity auto source = static_cast(Source); auto dest = static_cast(Dest); + auto force = m_customDisplayPrimaries != nullptr; - if ((source == dest) || (m_primariesMode == PrimariesDisabled)) + if (!force && ((source == dest) || (m_primariesMode == PrimariesDisabled))) return result; ColourPrimaries srcprimaries; @@ -483,12 +496,11 @@ QMatrix4x4 VideoColourSpace::GetPrimaryConversion(int Source, int Dest) // and destination. Most people will not notice the difference bt709 and bt610 etc // and we avoid extra GPU processing. // BT2020 is currently the main target - which is easily differentiated by its gamma. - if ((m_primariesMode == PrimariesAuto) && qFuzzyCompare(m_colourGamma + 1.0F, m_displayGamma + 1.0F)) + if (!force && (m_primariesMode == PrimariesAuto) && qFuzzyCompare(m_colourGamma + 1.0F, m_displayGamma + 1.0F)) return result; - // N.B. Custom primaries are not yet implemented but will, some day soon, - // be read from the EDID - if (m_customDisplayPrimaries != nullptr) + // Use the display's chromaticities if not sRGB + if (force) { dstprimaries = *m_customDisplayPrimaries; m_displayGamma = m_customDisplayGamma; diff --git a/mythtv/libs/libmythtv/videocolourspace.h b/mythtv/libs/libmythtv/videocolourspace.h index 0dd1db57b10..91132716841 100644 --- a/mythtv/libs/libmythtv/videocolourspace.h +++ b/mythtv/libs/libmythtv/videocolourspace.h @@ -73,27 +73,27 @@ class VideoColourSpace : public QObject, public QMatrix4x4, public ReferenceCoun PictureAttributeSupported m_supportedAttributes {kPictureAttributeSupported_None}; QMap m_dbSettings; - bool m_fullRange {true}; - float m_brightness {0.0F}; - float m_contrast {1.0F}; - float m_saturation {1.0F}; - float m_hue {0.0F}; - float m_alpha {1.0F}; - int m_colourSpace {AVCOL_SPC_UNSPECIFIED}; - int m_colourSpaceDepth {8}; - int m_range {AVCOL_RANGE_MPEG}; - bool m_updatesDisabled {true}; - int m_colourShifted {0}; - int m_colourTransfer {AVCOL_TRC_BT709}; - PrimariesMode m_primariesMode {PrimariesAuto}; - int m_colourPrimaries {AVCOL_PRI_BT709}; - int m_displayPrimaries {AVCOL_PRI_BT709}; - float m_colourGamma {2.2F}; - float m_displayGamma {2.2F}; - QMatrix4x4 m_primaryMatrix; - float m_customDisplayGamma {2.2F}; - ColourPrimaries *m_customDisplayPrimaries {nullptr}; - VideoColourSpace *m_parent {nullptr}; + bool m_fullRange { true }; + float m_brightness { 0.0F }; + float m_contrast { 1.0F }; + float m_saturation { 1.0F }; + float m_hue { 0.0F }; + float m_alpha { 1.0F }; + int m_colourSpace { AVCOL_SPC_UNSPECIFIED }; + int m_colourSpaceDepth { 8 }; + int m_range { AVCOL_RANGE_MPEG }; + bool m_updatesDisabled { true }; + int m_colourShifted { 0 }; + int m_colourTransfer { AVCOL_TRC_BT709 }; + PrimariesMode m_primariesMode { PrimariesAuto }; + int m_colourPrimaries { AVCOL_PRI_BT709 }; + int m_displayPrimaries { AVCOL_PRI_BT709 }; + float m_colourGamma { 2.2F }; + float m_displayGamma { 2.2F }; + QMatrix4x4 m_primaryMatrix { }; + float m_customDisplayGamma { 2.2F }; + ColourPrimaries *m_customDisplayPrimaries { nullptr }; + VideoColourSpace *m_parent { nullptr }; }; #endif diff --git a/mythtv/libs/libmythui/mythdisplay.cpp b/mythtv/libs/libmythui/mythdisplay.cpp index feb39bca744..eae7cd35cce 100644 --- a/mythtv/libs/libmythui/mythdisplay.cpp +++ b/mythtv/libs/libmythui/mythdisplay.cpp @@ -150,8 +150,13 @@ void MythDisplay::SetWidget(QWidget *MainWindow) QScreen *desired = GetDesiredScreen(); if (desired && (desired != window->screen())) { - DebugScreen(desired, "Moving to"); + // If we have changed the video mode for the old screen then reset + // it to the default/desktop mode + SwitchToDesktop(); + // Ensure we completely re-initialise when the new screen is set + m_initialised = false; + DebugScreen(desired, "Moving to"); // If this is a virtual desktop, move the window into the screen, // otherwise just set the screen - both of which should trigger a // screenChanged event. @@ -178,16 +183,20 @@ int MythDisplay::GetScreenCount(void) double MythDisplay::GetPixelAspectRatio(void) { - DisplayInfo info = GetDisplayInfo(); - if (info.m_size.width() > 0 && info.m_size.height() > 0 && - info.m_res.width() > 0 && info.m_res.height() > 0) + if (m_physicalSize.width() > 0 && m_physicalSize.height() > 0 && + m_resolution.width() > 0 && m_resolution.height() > 0) { - return (info.m_size.width() / static_cast(info.m_res.width())) / - (info.m_size.height() / static_cast(info.m_res.height())); + return (m_physicalSize.width() / static_cast(m_resolution.width())) / + (m_physicalSize.height() / static_cast(m_resolution.height())); } return 1.0; } +QSize MythDisplay::GetGUIResolution(void) +{ + return m_guiMode.Resolution(); +} + /*! \brief Return a pointer to the screen to use. * * This function looks at the users screen preference, and will return @@ -265,8 +274,7 @@ void MythDisplay::ScreenChanged(QScreen *qScreen) DebugScreen(qScreen, "Changed to"); m_screen = qScreen; connect(m_screen, &QScreen::geometryChanged, this, &MythDisplay::GeometryChanged); - m_videoModes.clear(); - InitialiseModes(); + Initialise(); emit CurrentScreenChanged(qScreen); } @@ -293,33 +301,24 @@ void MythDisplay::GeometryChanged(const QRect &Geo) .arg(Geo.width()).arg(Geo.height()).arg(Geo.left()).arg(Geo.top())); } -float MythDisplay::SanitiseRefreshRate(int Rate) -{ - static const float kDefaultRate = 1000000.0F / 60.0F; - float fixed = kDefaultRate; - if (Rate > 0) - { - fixed = static_cast(Rate) / 2.0F; - if (fixed < kDefaultRate) - fixed = kDefaultRate; - } - return fixed; -} - -DisplayInfo MythDisplay::GetDisplayInfo(int /*VideoRate*/) +void MythDisplay::UpdateCurrentMode(void) { // This is the final fallback when no other platform specifics are available // It is usually accurate apart from the refresh rate - which is often // rounded down. + m_edid = MythEDID(); QScreen *screen = GetCurrentScreen(); - DisplayInfo ret; if (!screen) - return ret; - ret.m_rate = 1000000.0F / static_cast(screen->refreshRate()); - ret.m_res = screen->size(); - ret.m_size = QSize(static_cast(screen->physicalSize().width()), - static_cast(screen->physicalSize().height())); - return ret; + { + m_refreshRate = 60.0; + m_physicalSize = QSize(160, 90); + m_resolution = QSize(1920, 1080); + return; + } + m_refreshRate = screen->refreshRate(); + m_resolution = screen->size(); + m_physicalSize = QSize(static_cast(screen->physicalSize().width()), + static_cast(screen->physicalSize().height())); } /// \brief Return true if the MythTV windows should span all screens. @@ -367,46 +366,47 @@ void MythDisplay::DebugScreen(QScreen *qScreen, const QString &Message) } } -void MythDisplay::InitialiseModes(void) +void MythDisplay::Initialise(void) { - m_last.Init(); - m_curMode = GUI; - - // Initialise DESKTOP mode - DisplayInfo info = GetDisplayInfo(); - int pixelwidth = info.m_res.width(); - int pixelheight = info.m_res.height(); - int mmwidth = info.m_size.width(); - int mmheight = info.m_size.height(); - double refreshrate = 1000000.0 / static_cast(info.m_rate); + m_videoModes.clear(); + m_overrideVideoModes.clear(); + UpdateCurrentMode(); - m_mode[DESKTOP].Init(); - m_mode[DESKTOP] = MythDisplayMode(pixelwidth, pixelheight, mmwidth, mmheight, -1.0, refreshrate); - LOG(VB_GENERAL, LOG_NOTICE, LOC + QString("Desktop video mode: %1x%2 %3 Hz") - .arg(pixelwidth).arg(pixelheight).arg(refreshrate, 0, 'f', 3)); + // Set the desktop mode - which is the mode at startup. We must always return + // the screen to this mode. + if (!m_initialised) + { + // Only ever set this once or after a screen change + m_initialised = true; + m_desktopMode = MythDisplayMode(m_resolution, m_physicalSize, -1.0, m_refreshRate); + LOG(VB_GENERAL, LOG_NOTICE, LOC + QString("Desktop video mode: %1x%2 %3Hz") + .arg(m_resolution.width()).arg(m_resolution.height()).arg(m_refreshRate, 0, 'f', 3)); + if (m_edid.Valid()) + { + if (m_edid.IsSRGB()) + LOG(VB_GENERAL, LOG_NOTICE, LOC + "Display is using sRGB colourspace"); + else + LOG(VB_GENERAL, LOG_NOTICE, LOC + "Display has custom colourspace"); + } + } - // Initialize GUI mode - m_mode[GUI].Init(); - pixelwidth = pixelheight = 0; + // Set the gui mode from database settings + int pixelwidth = m_resolution.width(); + int pixelheight = m_resolution.height(); + int mmwidth = m_physicalSize.width(); + int mmheight = m_physicalSize.height(); + double refreshrate = m_refreshRate; double aspectratio = 0.0; GetMythDB()->GetResolutionSetting("GuiVidMode", pixelwidth, pixelheight, aspectratio, refreshrate); GetMythDB()->GetResolutionSetting("DisplaySize", mmwidth, mmheight); - m_mode[GUI] = MythDisplayMode(pixelwidth, pixelheight, mmwidth, mmheight, -1.0, refreshrate); + m_guiMode = MythDisplayMode(pixelwidth, pixelheight, mmwidth, mmheight, -1.0, refreshrate); - if (m_mode[DESKTOP] == m_mode[GUI] && qFuzzyIsNull(refreshrate)) - { - // same resolution as current desktop - m_last = m_mode[DESKTOP]; - } - - // Initialize default VIDEO mode + // Set default video mode pixelwidth = pixelheight = 0; GetMythDB()->GetResolutionSetting("TVVidMode", pixelwidth, pixelheight, aspectratio, refreshrate); - m_mode[VIDEO] = MythDisplayMode(pixelwidth, pixelheight, mmwidth, mmheight, aspectratio, refreshrate); - - // Initialize video override mode - m_inSizeToOutputMode.clear(); + m_videoMode = MythDisplayMode(pixelwidth, pixelheight, mmwidth, mmheight, aspectratio, refreshrate); + // Initialise video override modes for (int i = 0; true; ++i) { int iw = 0; @@ -421,61 +421,59 @@ void MythDisplay::InitialiseModes(void) GetMythDB()->GetResolutionSetting("VidMode", iw, ih, iaspect, irate, i); GetMythDB()->GetResolutionSetting("TVVidMode", ow, oh, oaspect, orate, i); - if (!(iw || ih || !qFuzzyIsNull(irate))) + if (!(iw || ih || !qFuzzyIsNull(irate)) || !(ih && ow && oh)) break; - if (!(ih && ow && oh)) - break; - - uint64_t key = MythDisplayMode::CalcKey(iw, ih, irate); - - MythDisplayMode scr(ow, oh, mmwidth, mmheight, oaspect, orate); - - m_inSizeToOutputMode[key] = scr; + uint64_t key = MythDisplayMode::CalcKey(QSize(iw, ih), irate); + MythDisplayMode scr(QSize(ow, oh), QSize(mmwidth, mmheight), oaspect, orate); + m_overrideVideoModes[key] = scr; } } -/*! \brief Check whether the next mode is smaller in size than the current mode. +/*! \brief Check whether the next mode is larger in size than the current mode. * * This is used to allow the caller to force an update of the main window to ensure * the window is fully resized and is in no way clipped. Not an issue if the next * mode is smaller. */ -bool MythDisplay::NextModeIsLarger(Mode NextMode) +bool MythDisplay::NextModeIsLarger(QSize Size) { - MythDisplayMode next = m_mode[NextMode]; - return next.Width() > m_last.Width() || next.Height() > m_last.Height(); + return Size.width() > m_resolution.width() || Size.height() > m_resolution.height(); } -bool MythDisplay::NextModeIsLarger(int Width, int Height) -{ - return Width > m_last.Width() || Height > m_last.Height(); -} - -/// \brief Return the screen to the original desktop settings +/*! \brief Return the screen to the original desktop video mode + * + * \note It is assume the app is exiting once this is called. +*/ void MythDisplay::SwitchToDesktop() { - SwitchToGUI(DESKTOP); + MythDisplayMode current(m_resolution, m_physicalSize, -1.0, m_refreshRate); + if (current == m_desktopMode) + return; + SwitchToVideoMode(m_desktopMode.Resolution(), m_desktopMode.RefreshRate()); } /** \brief Switches to the resolution and refresh rate defined in the * database for the specified video resolution and frame rate. */ -bool MythDisplay::SwitchToVideo(int Width, int Height, double Rate) +bool MythDisplay::SwitchToVideo(QSize Size, double Rate) { - Mode nextmode = VIDEO; // default VIDEO mode - MythDisplayMode next = m_mode[nextmode]; + MythDisplayMode next = m_videoMode; + MythDisplayMode current(m_resolution, m_physicalSize, -1.0, m_refreshRate); double targetrate = 0.0; + double aspectoverride = 0.0; // try to find video override mode - uint64_t key = MythDisplayMode::FindBestScreen(m_inSizeToOutputMode, - Width, Height, Rate); + uint64_t key = MythDisplayMode::FindBestScreen(m_overrideVideoModes, + Size.width(), Size.height(), Rate); if (key != 0) { - m_mode[nextmode = CUSTOM_VIDEO] = next = m_inSizeToOutputMode[key]; - LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Found custom screen override %1x%2") - .arg(next.Width()).arg(next.Height())); + next = m_overrideVideoModes[key]; + if (next.AspectRatio() > 0.0) + aspectoverride = next.AspectRatio(); + LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Found custom screen override %1x%2 Aspect %3") + .arg(next.Width()).arg(next.Height()).arg(aspectoverride)); } // If requested refresh rate is 0, attempt to match video fps @@ -488,116 +486,95 @@ bool MythDisplay::SwitchToVideo(int Width, int Height, double Rate) // need to change video mode? MythDisplayMode::FindBestMatch(GetVideoModes(), next, targetrate); + if ((next == current) && (MythDisplayMode::CompareRates(current.RefreshRate(), targetrate))) + { + LOG(VB_GENERAL, LOG_INFO, LOC + QString("Using current mode %1x%2@%3Hz") + .arg(m_resolution.width()).arg(m_resolution.height()).arg(m_refreshRate)); + return true; + } - bool chg = !(next == m_last) || !(MythDisplayMode::CompareRates(m_last.RefreshRate(), targetrate)); - - LOG(VB_GENERAL, LOG_INFO, LOC + QString("%1 %2x%3 %4 Hz") - .arg(chg ? "Changing to" : "Using") - .arg(next.Width()).arg(next.Height()) - .arg(targetrate, 0, 'f', 3)); + LOG(VB_GENERAL, LOG_ERR, LOC + QString("Trying mode %1x%2@%3Hz") + .arg(next.Width()).arg(next.Height()).arg(next.RefreshRate(), 0, 'f', 3)); - if (chg && !SwitchToVideoMode(next.Width(), next.Height(), targetrate)) + if (!SwitchToVideoMode(next.Resolution(), targetrate)) { - LOG(VB_GENERAL, LOG_ERR, LOC + QString("SwitchToVideo: Video size %1 x %2: " - "xrandr failed for %3 x %4") - .arg(Width).arg(Height).arg(next.Width()).arg(next.Height())); + LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to change mode to %1x%2@%3Hz") + .arg(next.Width()).arg(next.Height()).arg(next.RefreshRate(), 0, 'f', 3)); return false; } - m_curMode = nextmode; - - m_last = next; - m_last.SetRefreshRate(targetrate); - - LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("SwitchToVideo: Video size %1x%2") - .arg(Width).arg(Height)); - LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("%1 displaying resolution %2x%3, %4mmx%5mm") - .arg((chg) ? "Switched to" : "Already") - .arg(m_last.Width()).arg(m_last.Height()) - .arg(m_last.WidthMM()).arg(m_last.HeightMM())); - - if (chg) - PauseForModeSwitch(); + if (next.Resolution() != m_resolution) + WaitForScreenChange(); - return chg; + // N.B. We used a computed aspect ratio unless overridden + m_aspectRatio = aspectoverride > 0.0 ? aspectoverride : 0.0; + UpdateCurrentMode(); + LOG(VB_GENERAL, LOG_INFO, LOC + QString("Switched to %1x%2@%3Hz for video %4x%5") + .arg(m_resolution.width()).arg(m_resolution.height()) + .arg(m_refreshRate, 0, 'f', 3).arg(Size.width()).arg(Size.height())); + PauseForModeSwitch(); + return true; } -/** \brief Switches to the GUI resolution specified. - * - * If which_gui is GUI then this switches to the resolution and refresh rate set - * in the database for the GUI. If which_gui is set to CUSTOM_GUI then we switch - * to the resolution and refresh rate specified in the last call to SwitchToCustomGUI. - * - * \param which_gui either regular GUI or CUSTOM_GUI - * \sa SwitchToCustomGUI +/** \brief Switches to the GUI resolution. */ -bool MythDisplay::SwitchToGUI(Mode NextMode, bool Wait) +bool MythDisplay::SwitchToGUI(bool Wait) { - MythDisplayMode next = m_mode[NextMode]; - auto targetrate = static_cast(NAN); - - // need to change video mode? - // If GuiVidModeRefreshRate is 0, assume any refresh rate is good enough. - if (qFuzzyIsNull(next.RefreshRate())) - next.SetRefreshRate(m_last.RefreshRate()); - - MythDisplayMode::FindBestMatch(GetVideoModes(), next, targetrate); - bool chg = !(next == m_last) || !(MythDisplayMode::CompareRates(m_last.RefreshRate(), targetrate)); - - LOG(VB_GENERAL, LOG_INFO, LOC + QString("%1 %2x%3 %4 Hz") - .arg(chg ? "Changing to" : "Using") - .arg(next.Width()).arg(next.Height()) - .arg(targetrate, 0, 'f', 3)); + // If the current resolution is the same as the GUI resolution then do nothing + // as refresh rate should not be critical for the GUI. + if (m_resolution == m_guiMode.Resolution()) + { + LOG(VB_GENERAL, LOG_INFO, LOC + QString("Using %1x%2@%3Hz for GUI") + .arg(m_resolution.width()).arg(m_resolution.height()).arg(m_refreshRate)); + return true; + } - if (chg && !SwitchToVideoMode(next.Width(), next.Height(), targetrate)) + if (!SwitchToVideoMode(m_guiMode.Resolution(), m_guiMode.RefreshRate())) { - LOG(VB_GENERAL, LOG_ERR, LOC + - QString("SwitchToGUI: xrandr failed for %1x%2 %3 Hz") - .arg(next.Width()).arg(next.Height()) - .arg(next.RefreshRate(), 0, 'f', 3)); + LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to change mode to %1x%2@%3Hz") + .arg(m_guiMode.Width()).arg(m_guiMode.Height()).arg(m_guiMode.RefreshRate(), 0, 'f', 3)); return false; } - if (Wait && (next.Width() != m_last.Width() || next.Height() != m_last.Height())) + if (Wait && (m_resolution != m_guiMode.Resolution())) WaitForScreenChange(); - m_curMode = NextMode; - - m_last = next; - m_last.SetRefreshRate(targetrate); - - LOG(VB_GENERAL, LOG_INFO, LOC + QString("SwitchToGUI: Switched to %1x%2 %3 Hz") - .arg(m_last.Width()).arg(m_last.Height()).arg(GetRefreshRate(), 0, 'f', 3)); - - return chg; + UpdateCurrentMode(); + m_aspectRatio = 0.0; + LOG(VB_GENERAL, LOG_INFO, LOC + QString("Switched to %1x%2@%3Hz") + .arg(m_resolution.width()).arg(m_resolution.height()).arg(m_refreshRate, 0, 'f', 3)); + return true; } double MythDisplay::GetRefreshRate(void) { - return m_last.RefreshRate(); + return m_refreshRate; } -std::vector MythDisplay::GetRefreshRates(int Width, int Height) +int MythDisplay::GetRefreshInterval(int Fallback) { - auto tr = static_cast(NAN); - std::vector empty; - - const MythDisplayMode drs(Width, Height, 0, 0, -1.0, 0.0); - const DisplayModeVector& drv = GetVideoModes(); - int t = MythDisplayMode::FindBestMatch(drv, drs, tr); - - if (t < 0) - return empty; + if (m_refreshRate > 20.0 && m_refreshRate < 200.0) + return static_cast(lround(1000000.0 / m_refreshRate)); + return Fallback; +} - return drv[static_cast(t)].RefreshRates(); +std::vector MythDisplay::GetRefreshRates(QSize Size) +{ + auto targetrate = static_cast(NAN); + const MythDisplayMode mode(Size, QSize(0, 0), -1.0, 0.0); + const vector& modes = GetVideoModes(); + int match = MythDisplayMode::FindBestMatch(modes, mode, targetrate); + if (match < 0) + return std::vector(); + return modes[static_cast(match)].RefreshRates(); } -bool MythDisplay::SwitchToVideoMode(int /*Width*/, int /*Height*/, double /*FrameRate*/) +bool MythDisplay::SwitchToVideoMode(QSize, double) { return false; } -const DisplayModeVector& MythDisplay::GetVideoModes(void) +const vector &MythDisplay::GetVideoModes(void) { return m_videoModes; } @@ -609,7 +586,18 @@ const DisplayModeVector& MythDisplay::GetVideoModes(void) */ double MythDisplay::GetAspectRatio(void) { - return m_last.AspectRatio(); + if (m_aspectRatio > 0.0) + return m_aspectRatio; + + if (m_physicalSize.width() > 0 && m_physicalSize.height() > 0) + return static_cast(m_physicalSize.width()) / m_physicalSize.height(); + + return 16.0 / 9.0; +} + +MythEDID& MythDisplay::GetEDID(void) +{ + return m_edid; } /*! \brief Estimate the overall display aspect ratio for multi screen setups. @@ -714,12 +702,12 @@ double MythDisplay::EstimateVirtualAspectRatio(void) QSize MythDisplay::GetResolution(void) { - return QSize(m_last.Width(), m_last.Height()); + return m_resolution; } QSize MythDisplay::GetPhysicalSize(void) { - return QSize(m_last.WidthMM(), m_last.HeightMM()); + return m_physicalSize; } void MythDisplay::WaitForScreenChange(void) diff --git a/mythtv/libs/libmythui/mythdisplay.h b/mythtv/libs/libmythui/mythdisplay.h index 2ee2bdadd66..e7d70fcb4fa 100644 --- a/mythtv/libs/libmythui/mythdisplay.h +++ b/mythtv/libs/libmythui/mythdisplay.h @@ -10,31 +10,11 @@ #include "mythuiexp.h" #include "referencecounter.h" #include "mythdisplaymode.h" +#include "mythedid.h" // Std #include -#define VALID_RATE(rate) ((rate) > 20.0F && (rate) < 200.0F) - -class DisplayInfo -{ - public: - DisplayInfo(void) = default; - explicit DisplayInfo(int Rate) - : m_rate(Rate) - { - } - - int Rate(void) const - { - return static_cast(lroundf(m_rate)); - } - - QSize m_size { 0, 0}; - QSize m_res { 0, 0}; - float m_rate { -1 }; -}; - class MUI_PUBLIC MythDisplay : public QObject, public ReferenceCounter { Q_OBJECT @@ -44,75 +24,74 @@ class MUI_PUBLIC MythDisplay : public QObject, public ReferenceCounter public: static MythDisplay* AcquireRelease(bool Acquire = true); - typedef enum - { - GUI = 0, - VIDEO = 1, - CUSTOM_GUI = 2, - CUSTOM_VIDEO = 3, - DESKTOP = 4, - MAX_MODES = 5, - } Mode; - - QScreen* GetCurrentScreen (void); - static int GetScreenCount (void); - double GetPixelAspectRatio(void); - - virtual DisplayInfo GetDisplayInfo(int VideoRate = 0); - static bool SpanAllScreens(void); - static QString GetExtraScreenInfo(QScreen *qScreen); - - virtual bool UsingVideoModes (void) { return false; } - virtual const DisplayModeVector& GetVideoModes(void); - bool NextModeIsLarger (Mode NextMode); - bool NextModeIsLarger (int Width, int Height); - void SwitchToDesktop (void); - bool SwitchToGUI (Mode NextMode = GUI, bool Wait = false); - bool SwitchToVideo (int Width, int Height, double Rate = 0.0); - QSize GetResolution (void); - QSize GetPhysicalSize (void); - double GetRefreshRate (void); - double GetAspectRatio (void); + virtual bool UsingVideoModes(void) { return false; } + virtual const vector& GetVideoModes(void); + + static bool SpanAllScreens (void); + static QString GetExtraScreenInfo (QScreen *qScreen); + + QScreen* GetCurrentScreen (void); + static int GetScreenCount (void); + double GetPixelAspectRatio (void); + QSize GetGUIResolution (void); + bool NextModeIsLarger (QSize Size); + void SwitchToDesktop (void); + bool SwitchToGUI (bool Wait = false); + bool SwitchToVideo (QSize Size, double Rate = 0.0); + QSize GetResolution (void); + QSize GetPhysicalSize (void); + double GetRefreshRate (void); + int GetRefreshInterval (int Fallback); + double GetAspectRatio (void); double EstimateVirtualAspectRatio(void); - std::vector GetRefreshRates(int Width, int Height); + MythEDID& GetEDID (void); + std::vector GetRefreshRates(QSize Size); public slots: - virtual void ScreenChanged(QScreen *qScreen); - static void PrimaryScreenChanged (QScreen *qScreen); - void ScreenAdded (QScreen *qScreen); - void ScreenRemoved (QScreen *qScreen); - void GeometryChanged (const QRect &Geometry); + virtual void ScreenChanged (QScreen *qScreen); + static void PrimaryScreenChanged (QScreen *qScreen); + void ScreenAdded (QScreen *qScreen); + void ScreenRemoved (QScreen *qScreen); + void GeometryChanged (const QRect &Geometry); signals: - void CurrentScreenChanged (QScreen *Screen); - void ScreenCountChanged (int Screens); + void CurrentScreenChanged (QScreen *qScreen); + void ScreenCountChanged (int Screens); protected: MythDisplay(); virtual ~MythDisplay(); + virtual void UpdateCurrentMode (void); + virtual bool SwitchToVideoMode (QSize Size, double Framerate); + void DebugModes (void) const; void SetWidget (QWidget *MainWindow); static QScreen* GetDesiredScreen (void); static void DebugScreen (QScreen *qScreen, const QString &Message); - static float SanitiseRefreshRate(int Rate); - - void InitialiseModes (void); - virtual bool SwitchToVideoMode (int Width, int Height, double Framerate); - void WaitForScreenChange(void); - - QWidget* m_widget { nullptr }; - QScreen* m_screen { nullptr }; - mutable std::vector m_videoModes { }; + void Initialise (void); + void WaitForScreenChange(void); + + double m_refreshRate { 0.0 }; + double m_aspectRatio { 0.0 }; + QSize m_resolution { 0, 0 }; + QSize m_physicalSize { 0, 0 }; + MythEDID m_edid { }; + QWidget* m_widget { nullptr }; + QScreen* m_screen { nullptr }; + vector m_videoModes { }; private: Q_DISABLE_COPY(MythDisplay) static void PauseForModeSwitch(void); - Mode m_curMode { GUI }; - MythDisplayMode m_mode[MAX_MODES] { }; - MythDisplayMode m_last { }; // mirror of mode[current_mode] - DisplayModeMap m_inSizeToOutputMode { }; + bool m_initialised { false }; + MythDisplayMode m_desktopMode { }; + MythDisplayMode m_guiMode { }; + MythDisplayMode m_videoMode { }; + DisplayModeMap m_overrideVideoModes { }; + + MythDisplayMode m_last { }; }; #endif // MYTHDISPLAY_H diff --git a/mythtv/libs/libmythui/mythdisplaymode.cpp b/mythtv/libs/libmythui/mythdisplaymode.cpp index 067dd0793d9..a13ae625a91 100644 --- a/mythtv/libs/libmythui/mythdisplaymode.cpp +++ b/mythtv/libs/libmythui/mythdisplaymode.cpp @@ -7,6 +7,17 @@ // Std #include +MythDisplayMode::MythDisplayMode(QSize Resolution, QSize PhysicalSize, + double AspectRatio, double RefreshRate) + : m_width(Resolution.width()), + m_height(Resolution.height()), + m_widthMM(PhysicalSize.width()), + m_heightMM(PhysicalSize.height()) +{ + SetAspectRatio(AspectRatio); + if (RefreshRate > 0) + m_refreshRates.push_back(RefreshRate); +} MythDisplayMode::MythDisplayMode(int Width, int Height, int MMWidth, int MMHeight, double AspectRatio, double RefreshRate) : m_width(Width), @@ -39,6 +50,11 @@ void MythDisplayMode::Init(void) m_aspect = -1.0; } +QSize MythDisplayMode::Resolution(void) const +{ + return QSize(m_width, m_height); +} + int MythDisplayMode::Width(void) const { return m_width; @@ -107,10 +123,10 @@ void MythDisplayMode::SetRefreshRate(double Rate) AddRefreshRate(Rate); } -uint64_t MythDisplayMode::CalcKey(int Width, int Height, double Rate) +uint64_t MythDisplayMode::CalcKey(QSize Size, double Rate) { - return (static_cast(Width) << 34) | - (static_cast(Height) << 18) | + return (static_cast(Size.width()) << 34) | + (static_cast(Size.height()) << 18) | (static_cast(Rate * 1000.0)); } @@ -120,7 +136,7 @@ bool MythDisplayMode::CompareRates(double First, double Second, double Precision return qAbs(First - Second) < Precision; } -int MythDisplayMode::FindBestMatch(const DisplayModeVector Modes, +int MythDisplayMode::FindBestMatch(const vector Modes, const MythDisplayMode& Mode, double &TargetRate) { double videorate = Mode.RefreshRate(); diff --git a/mythtv/libs/libmythui/mythdisplaymode.h b/mythtv/libs/libmythui/mythdisplaymode.h index b09be91089f..161a621e3e7 100644 --- a/mythtv/libs/libmythui/mythdisplaymode.h +++ b/mythtv/libs/libmythui/mythdisplaymode.h @@ -2,6 +2,7 @@ #define MYTHDISPLAYMODE_H_ // Qt +#include #include // MythTV @@ -15,7 +16,6 @@ class MythDisplayMode; using namespace std; -typedef vector DisplayModeVector; typedef map DisplayModeMap; class MUI_PUBLIC MythDisplayMode @@ -25,9 +25,12 @@ class MUI_PUBLIC MythDisplayMode bool operator == (const MythDisplayMode& Other) const; MythDisplayMode() = default; + MythDisplayMode(QSize Resolution, QSize PhysicalSize, + double AspectRatio, double RefreshRate); MythDisplayMode(int Width, int Height, int MMWidth, int MMHeight, double AspectRatio, double RefreshRate); void Init (void); + QSize Resolution (void) const; int Width (void) const; int Height (void) const; int WidthMM (void) const; @@ -39,9 +42,9 @@ class MUI_PUBLIC MythDisplayMode void ClearRefreshRates(void); void SetRefreshRate(double Rate); const std::vector& RefreshRates(void) const; - static int FindBestMatch (const DisplayModeVector Modes, + static int FindBestMatch (const vector Modes, const MythDisplayMode& Mode, double& TargetRate); - static uint64_t CalcKey (int Width, int Height, double Rate); + static uint64_t CalcKey (QSize Size, double Rate); static bool CompareRates (double First, double Second, double Precision = 0.01); static uint64_t FindBestScreen(const DisplayModeMap& Map, int Width, int Height, double Rate); diff --git a/mythtv/libs/libmythui/mythuihelper.cpp b/mythtv/libs/libmythui/mythuihelper.cpp index 1952a4ac860..9574cd0b987 100644 --- a/mythtv/libs/libmythui/mythuihelper.cpp +++ b/mythtv/libs/libmythui/mythuihelper.cpp @@ -203,8 +203,9 @@ MythUIHelperPrivate::~MythUIHelperPrivate() if (m_display) { - if (m_display->UsingVideoModes()) - m_display->SwitchToDesktop(); + // N.B. we always call this to ensure the desktop mode is restored + // in case the setting was changed + m_display->SwitchToDesktop(); MythDisplay::AcquireRelease(false); } } @@ -428,7 +429,7 @@ void MythUIHelper::LoadQtConfig(void) // Switch to desired GUI resolution if (d->m_display->UsingVideoModes()) - d->m_display->SwitchToGUI(MythDisplay::GUI, true); + d->m_display->SwitchToGUI(true); // Note the possibly changed screen settings d->GetScreenBounds(); diff --git a/mythtv/libs/libmythui/platforms/mythdisplayandroid.cpp b/mythtv/libs/libmythui/platforms/mythdisplayandroid.cpp index a9640ff6c75..843d668c1ce 100644 --- a/mythtv/libs/libmythui/platforms/mythdisplayandroid.cpp +++ b/mythtv/libs/libmythui/platforms/mythdisplayandroid.cpp @@ -10,13 +10,14 @@ MythDisplayAndroid::MythDisplayAndroid() : MythDisplay() { + Initialise(); } MythDisplayAndroid::~MythDisplayAndroid() { } -DisplayInfo MythDisplayAndroid::GetDisplayInfo(int VideoRate) +void MythDisplayAndroid::UpdateCurrentMode(void) { DisplayInfo ret; QAndroidJniEnvironment env; @@ -42,13 +43,12 @@ DisplayInfo MythDisplayAndroid::GetDisplayInfo(int VideoRate) .arg(xdpi).arg(ydpi) ); - if (VALID_RATE(rate)) - ret.m_rate = 1000000.0F / rate; - else - ret.m_rate = SanitiseRefreshRate(VideoRate); - ret.m_res = QSize((uint)width, (uint)height); - ret.m_size = QSize((uint)width, (uint)height); + m_refreshRate = static_cast(rate); + m_resolution = QSize(width, height); + m_physicalSize = QSize(width, height); if (xdpi > 0 && ydpi > 0) - ret.m_size = QSize((uint)width/xdpi*25.4F, (uint)height/ydpi*25.4F); - return ret; + { + m_physicalSize = QSize(static_cast(width / xdpi * 25.4F), + static_cast(height / ydpi * 25.4F)); + } } diff --git a/mythtv/libs/libmythui/platforms/mythdisplayandroid.h b/mythtv/libs/libmythui/platforms/mythdisplayandroid.h index b606c5217ef..912d599dbe3 100644 --- a/mythtv/libs/libmythui/platforms/mythdisplayandroid.h +++ b/mythtv/libs/libmythui/platforms/mythdisplayandroid.h @@ -9,8 +9,7 @@ class MythDisplayAndroid : public MythDisplay public: MythDisplayAndroid(); ~MythDisplayAndroid() override; - - DisplayInfo GetDisplayInfo(int VideoRate = 0) override; + void UpdateCurrentMode(void) override; }; #endif // MYTHDISPLAYANDROID_H diff --git a/mythtv/libs/libmythui/platforms/mythdisplaydrm.cpp b/mythtv/libs/libmythui/platforms/mythdisplaydrm.cpp index 7f0dc633964..cc7cd9c0722 100644 --- a/mythtv/libs/libmythui/platforms/mythdisplaydrm.cpp +++ b/mythtv/libs/libmythui/platforms/mythdisplaydrm.cpp @@ -8,6 +8,7 @@ MythDisplayDRM::MythDisplayDRM() : MythDisplay() { m_device = new MythDRMDevice(m_screen); + Initialise(); } MythDisplayDRM::~MythDisplayDRM() @@ -30,18 +31,15 @@ void MythDisplayDRM::ScreenChanged(QScreen *qScreen) m_device = new MythDRMDevice(m_screen); } -DisplayInfo MythDisplayDRM::GetDisplayInfo(int VideoRate) +void MythDisplayDRM::UpdateCurrentMode(void) { - DisplayInfo result; - if (!m_device) - return result; - - result.m_size = m_device->GetPhysicalSize(); - result.m_res = m_device->GetResolution(); - float rate = m_device->GetRefreshRate(); - if (VALID_RATE(rate)) - result.m_rate = 1000000.0F / rate; - else - result.m_rate = SanitiseRefreshRate(VideoRate); - return result; + if (m_device) + { + m_refreshRate = m_device->GetRefreshRate(); + m_resolution = m_device->GetResolution(); + m_physicalSize = m_device->GetPhysicalSize(); + m_edid = m_device->GetEDID(); + return; + } + MythDisplay::UpdateCurrentMode(); } diff --git a/mythtv/libs/libmythui/platforms/mythdisplaydrm.h b/mythtv/libs/libmythui/platforms/mythdisplaydrm.h index fe5994a358a..c90ea1e6d20 100644 --- a/mythtv/libs/libmythui/platforms/mythdisplaydrm.h +++ b/mythtv/libs/libmythui/platforms/mythdisplaydrm.h @@ -17,7 +17,7 @@ class MythDisplayDRM final : public MythDisplay MythDisplayDRM(); ~MythDisplayDRM() override; - DisplayInfo GetDisplayInfo (int VideoRate = 0) override; + void UpdateCurrentMode(void) override; public slots: void ScreenChanged(QScreen *qScreen) override; diff --git a/mythtv/libs/libmythui/platforms/mythdisplayosx.cpp b/mythtv/libs/libmythui/platforms/mythdisplayosx.cpp index a86fb30780c..9832b2fabda 100644 --- a/mythtv/libs/libmythui/platforms/mythdisplayosx.cpp +++ b/mythtv/libs/libmythui/platforms/mythdisplayosx.cpp @@ -10,7 +10,7 @@ MythDisplayOSX::MythDisplayOSX() : MythDisplay() { - InitialiseModes(); + Initialise(); } MythDisplayOSX::~MythDisplayOSX() @@ -18,34 +18,35 @@ MythDisplayOSX::~MythDisplayOSX() ClearModes(); } -DisplayInfo MythDisplayOSX::GetDisplayInfo(int VideoRate) +void MythDisplayOSX::UpdateCurrentMode(void) { - DisplayInfo ret; if (!HasMythMainWindow()) - return ret; + { + MythDisplay::UpdateCurrentMode(); + return; + } WId win = (qobject_cast(MythMainWindow::getMainWindow()))->winId(); CGDirectDisplayID disp = GetOSXDisplay(win); if (!disp) - return ret; + { + MythDisplay::UpdateCurrentMode(); + return; + } CGDisplayModeRef mode = CGDisplayCopyDisplayMode(disp); if (!mode) - return ret; - double rate = CGDisplayModeGetRefreshRate(mode); + { + MythDisplay::UpdateCurrentMode(); + return; + } + + m_refreshRate = CGDisplayModeGetRefreshRate(mode); + m_resolution = QSize(static_cast(CGDisplayModeGetWidth(mode)), + static_cast(CGDisplayModeGetHeight(mode))); //bool interlaced = CGDisplayModeGetIOFlags(mode) & kDisplayModeInterlacedFlag; - size_t width = CGDisplayModeGetWidth(mode); - size_t height = CGDisplayModeGetHeight(mode); CGDisplayModeRelease(mode); - - if (VALID_RATE(static_cast(rate))) - ret.m_rate = 1000000.0F / static_cast(rate); - else - ret.m_rate = SanitiseRefreshRate(VideoRate); - CGSize sizemm = CGDisplayScreenSize(disp); - ret.m_size = QSize(static_cast(sizemm.width), static_cast(sizemm.height)); - ret.m_res = QSize(static_cast(width), static_cast(height)); - return ret; + m_physicalSize = QSize(static_cast(sizemm.width), static_cast(sizemm.height)); } bool MythDisplayOSX::UsingVideoModes(void) @@ -78,8 +79,8 @@ const std::vector& MythDisplayOSX::GetVideoModes(void) CGDisplayModeRef mode = (CGDisplayModeRef)CFArrayGetValueAtIndex(modes, i); double rate = CGDisplayModeGetRefreshRate(mode); bool interlaced = CGDisplayModeGetIOFlags(mode) & kDisplayModeInterlacedFlag; - size_t width = CGDisplayModeGetWidth(mode); - size_t height = CGDisplayModeGetHeight(mode); + int width = static_cast(CGDisplayModeGetWidth(mode)); + int height = static_cast(CGDisplayModeGetHeight(mode)); // See note in MythDisplayX11 if (interlaced) @@ -89,14 +90,14 @@ const std::vector& MythDisplayOSX::GetVideoModes(void) continue; } - uint64_t key = MythDisplayMode::CalcKey(width, height, 0.0); + QSize resolution(width, height); + uint64_t key = MythDisplayMode::CalcKey(resolution, 0.0); if (screen_map.find(key) == screen_map.end()) - screen_map[key] = MythDisplayMode(width, height, sizemm.width, - sizemm.height, -1.0, rate); + screen_map[key] = MythDisplayMode(resolution, QSize(sizemm.width, sizemm.height), + -1.0, rate); else screen_map[key].AddRefreshRate(rate); - m_modeMap.insert(MythDisplayMode::CalcKey(width, height, rate), - CGDisplayModeRetain(mode)); + m_modeMap.insert(MythDisplayMode::CalcKey(resolution, rate), CGDisplayModeRetain(mode)); } CFRelease(modes); @@ -107,7 +108,7 @@ const std::vector& MythDisplayOSX::GetVideoModes(void) return m_videoModes; } -bool MythDisplayOSX::SwitchToVideoMode(int Width, int Height, double DesiredRate) +bool MythDisplayOSX::SwitchToVideoMode(QSize Size, double DesiredRate) { if (!HasMythMainWindow()) return false; @@ -117,7 +118,7 @@ bool MythDisplayOSX::SwitchToVideoMode(int Width, int Height, double DesiredRate return false; auto rate = static_cast(NAN); - MythDisplayMode desired(Width, Height, 0, 0, -1.0, DesiredRate); + MythDisplayMode desired(Size, QSize(0, 0), -1.0, DesiredRate); int idx = MythDisplayMode::FindBestMatch(m_videoModes, desired, rate); if (idx < 0) { @@ -125,7 +126,7 @@ bool MythDisplayOSX::SwitchToVideoMode(int Width, int Height, double DesiredRate return false; } - auto mode = MythDisplayMode::CalcKey(Width, Height, rate); + auto mode = MythDisplayMode::CalcKey(Size, rate); if (!m_modeMap.contains(mode)) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to find mode"); diff --git a/mythtv/libs/libmythui/platforms/mythdisplayosx.h b/mythtv/libs/libmythui/platforms/mythdisplayosx.h index 0177e5eac2d..48e2a7727f0 100644 --- a/mythtv/libs/libmythui/platforms/mythdisplayosx.h +++ b/mythtv/libs/libmythui/platforms/mythdisplayosx.h @@ -13,11 +13,11 @@ class MythDisplayOSX : public MythDisplay MythDisplayOSX(); ~MythDisplayOSX() override; - DisplayInfo GetDisplayInfo(int VideoRate = 0) override; + void UpdateCurrentMode(void) override; bool UsingVideoModes(void) override; const std::vector& GetVideoModes(void) override; - bool SwitchToVideoMode(int Width, int Height, double DesiredRate) override; + bool SwitchToVideoMode(QSize Size, double DesiredRate) override; private: void ClearModes(void); diff --git a/mythtv/libs/libmythui/platforms/mythdisplaywindows.cpp b/mythtv/libs/libmythui/platforms/mythdisplaywindows.cpp index 58b3b0c6436..f21a2fc6970 100644 --- a/mythtv/libs/libmythui/platforms/mythdisplaywindows.cpp +++ b/mythtv/libs/libmythui/platforms/mythdisplaywindows.cpp @@ -4,43 +4,37 @@ MythDisplayWindows::MythDisplayWindows() : MythDisplay() { + Initialise(); } MythDisplayWindows::~MythDisplayWindows() { } -DisplayInfo MythDisplayWindows::GetDisplayInfo(int VideoRate) +void MythDisplayWindows::UpdateCurrentMode(void) { - DisplayInfo ret; HDC hdc = GetDC((HWND)GetWindowID()); - int rate = 0; - if (hdc) + if (!hdc) { - rate = GetDeviceCaps(hdc, VREFRESH); - int width = GetDeviceCaps(hdc, HORZSIZE); - int height = GetDeviceCaps(hdc, VERTSIZE); - ret.m_size = QSize((uint)width, (uint)height); - width = GetDeviceCaps(hdc, HORZRES); - height = GetDeviceCaps(hdc, VERTRES); - ret.m_res = QSize((uint)width, (uint)height); + MythDisplay::UpdateCurrentMode(); + return; } - if (VALID_RATE(rate)) + int rate = GetDeviceCaps(hdc, VREFRESH); + m_physicalSize = QSize(GetDeviceCaps(hdc, HORZSIZE), + GetDeviceCaps(hdc, VERTSIZE)); + m_resolution = QSize(GetDeviceCaps(hdc, HORZRES), + GetDeviceCaps(hdc, VERTRES)); + + // see http://support.microsoft.com/kb/2006076 + switch (rate) { - // see http://support.microsoft.com/kb/2006076 - switch (rate) - { - case 23: ret.m_rate = 41708; break; // 23.976Hz - case 29: ret.m_rate = 33367; break; // 29.970Hz - case 47: ret.m_rate = 20854; break; // 47.952Hz - case 59: ret.m_rate = 16683; break; // 59.940Hz - case 71: ret.m_rate = 13903; break; // 71.928Hz - case 119: ret.m_rate = 8342; break; // 119.880Hz - default: ret.m_rate = 1000000.0F / (float)rate; - } + case 23: m_refreshRate = 23.976; break; + case 29: m_refreshRate = 29.970; break; + case 47: m_refreshRate = 47.952; break; + case 59: m_refreshRate = 59.940; break; + case 71: m_refreshRate = 71.928; break; + case 119: m_refreshRate = 119.880; break; + default: m_refreshRate = static_cast(rate); } - else - ret.m_rate = SanitiseRefreshRate(VideoRate); - return ret; } diff --git a/mythtv/libs/libmythui/platforms/mythdisplaywindows.h b/mythtv/libs/libmythui/platforms/mythdisplaywindows.h index 6f5f9703c49..4b6224c2805 100644 --- a/mythtv/libs/libmythui/platforms/mythdisplaywindows.h +++ b/mythtv/libs/libmythui/platforms/mythdisplaywindows.h @@ -10,7 +10,7 @@ class MythDisplayWindows : public MythDisplay MythDisplayWindows(); ~MythDisplayWindows() override; - DisplayInfo GetDisplayInfo(int VideoRate = 0) override; + void UpdateCurrentMode(void) override; }; #endif // MYTHDISPLAYWINDOWS_H diff --git a/mythtv/libs/libmythui/platforms/mythdisplayx11.cpp b/mythtv/libs/libmythui/platforms/mythdisplayx11.cpp index 9f7b8ca36fa..d3101a5e604 100644 --- a/mythtv/libs/libmythui/platforms/mythdisplayx11.cpp +++ b/mythtv/libs/libmythui/platforms/mythdisplayx11.cpp @@ -3,17 +3,16 @@ #include "mythcorecontext.h" #include "mythlogging.h" #include "mythdisplayx11.h" -#include "mythxdisplay.h" -#ifdef USING_XRANDR -#include // always last -#endif +// X11 +#include #define LOC QString("DisplayX11: ") MythDisplayX11::MythDisplayX11() + : MythDisplay() { - InitialiseModes(); + Initialise(); } bool MythDisplayX11::IsAvailable(void) @@ -29,22 +28,19 @@ bool MythDisplayX11::IsAvailable(void) return s_available; } -DisplayInfo MythDisplayX11::GetDisplayInfo(int VideoRate) +void MythDisplayX11::UpdateCurrentMode(void) { - DisplayInfo ret; - MythXDisplay *disp = OpenMythXDisplay(); - if (!disp) - return ret; - - float rate = disp->GetRefreshRate(); - if (VALID_RATE(rate)) - ret.m_rate = 1000000.0F / rate; - else - ret.m_rate = SanitiseRefreshRate(VideoRate); - ret.m_res = disp->GetDisplaySize(); - ret.m_size = disp->GetDisplayDimensions(); - delete disp; - return ret; + MythXDisplay *display = MythXDisplay::OpenMythXDisplay(); + if (display) + { + m_refreshRate = display->GetRefreshRate(); + m_resolution = display->GetDisplaySize(); + m_physicalSize = display->GetDisplayDimensions(); + GetEDID(display); + delete display; + return; + } + MythDisplay::UpdateCurrentMode(); } #ifdef USING_XRANDR @@ -57,52 +53,23 @@ bool MythDisplayX11::UsingVideoModes(void) const std::vector& MythDisplayX11::GetVideoModes(void) { - if (!m_videoModes.empty()) + if (!m_videoModes.empty() || !m_screen) return m_videoModes; m_videoModes.clear(); m_modeMap.clear(); - MythXDisplay *display = OpenMythXDisplay(); + MythXDisplay *display = MythXDisplay::OpenMythXDisplay(); if (!display) return m_videoModes; - RROutput current = 0; - XRROutputInfo *output = nullptr; XRRScreenResources* res = XRRGetScreenResourcesCurrent(display->GetDisplay(), display->GetRoot()); - for (int i = 0; i < res->noutput; ++i) - { - if (output) - { - XRRFreeOutputInfo(output); - output = nullptr; - } - - output = XRRGetOutputInfo(display->GetDisplay(), res, res->outputs[i]); - if (!output || output->nameLen < 1) - continue; - if (output->connection != RR_Connected) - { - LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Output '%1' is disconnected") - .arg(output->name)); - continue; - } - - QString name(output->name); - if (name == m_screen->name()) - { - LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Matched '%1' to output %2") - .arg(m_screen->name()).arg(res->outputs[i])); - current = res->outputs[i]; - break; - } - } + XRROutputInfo *output = GetOutput(res, display, m_screen); - if (!current) + if (!output) { LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to find an output that matches '%1'") .arg(m_screen->name())); - XRRFreeOutputInfo(output); XRRFreeScreenResources(res); delete display; return m_videoModes; @@ -138,12 +105,14 @@ const std::vector& MythDisplayX11::GetVideoModes(void) continue; } - uint64_t key = MythDisplayMode::CalcKey(width, height, 0.0); + QSize resolution(width, height); + QSize physical(mmwidth, mmheight); + uint64_t key = MythDisplayMode::CalcKey(resolution, 0.0); if (screenmap.find(key) == screenmap.end()) - screenmap[key] = MythDisplayMode(width, height, mmwidth, mmheight, -1.0, rate); + screenmap[key] = MythDisplayMode(resolution, physical, -1.0, rate); else screenmap[key].AddRefreshRate(rate); - m_modeMap.insert(MythDisplayMode::CalcKey(width, height, rate), rrmode); + m_modeMap.insert(MythDisplayMode::CalcKey(resolution, rate), rrmode); } for (auto it = screenmap.begin(); screenmap.end() != it; ++it) @@ -156,7 +125,7 @@ const std::vector& MythDisplayX11::GetVideoModes(void) return m_videoModes; } -bool MythDisplayX11::SwitchToVideoMode(int Width, int Height, double DesiredRate) +bool MythDisplayX11::SwitchToVideoMode(QSize Size, double DesiredRate) { if (!m_crtc) (void)GetVideoModes(); @@ -164,7 +133,8 @@ bool MythDisplayX11::SwitchToVideoMode(int Width, int Height, double DesiredRate return false; auto rate = static_cast(NAN); - MythDisplayMode desired(Width, Height, 0, 0, -1.0, DesiredRate); + QSize dummy(0, 0); + MythDisplayMode desired(Size, dummy, -1.0, DesiredRate); int idx = MythDisplayMode::FindBestMatch(m_videoModes, desired, rate); if (idx < 0) @@ -173,14 +143,14 @@ bool MythDisplayX11::SwitchToVideoMode(int Width, int Height, double DesiredRate return false; } - auto mode = MythDisplayMode::CalcKey(Width, Height, rate); + auto mode = MythDisplayMode::CalcKey(Size, rate); if (!m_modeMap.contains(mode)) { LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to find mode"); return false; } - MythXDisplay *display = OpenMythXDisplay(); + MythXDisplay *display = MythXDisplay::OpenMythXDisplay(); if (!display) return false; @@ -208,4 +178,104 @@ bool MythDisplayX11::SwitchToVideoMode(int Width, int Height, double DesiredRate LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to set video mode"); return RRSetConfigSuccess == status; } + +XRROutputInfo* MythDisplayX11::GetOutput(XRRScreenResources* Resources, + MythXDisplay* mDisplay, + QScreen* qScreen, RROutput *Output) +{ + if (!(Resources && mDisplay && qScreen)) + return nullptr; + + XRROutputInfo *result = nullptr; + for (int i = 0; i < Resources->noutput; ++i) + { + if (result) + { + XRRFreeOutputInfo(result); + result = nullptr; + } + + result = XRRGetOutputInfo(mDisplay->GetDisplay(), Resources, Resources->outputs[i]); + if (!result || result->nameLen < 1) + continue; + if (result->connection != RR_Connected) + { + LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Output '%1' is disconnected") + .arg(result->name)); + continue; + } + + QString name(result->name); + if (name == qScreen->name()) + { + LOG(VB_GENERAL, LOG_DEBUG, LOC + QString("Matched '%1' to output %2") + .arg(qScreen->name()).arg(Resources->outputs[i])); + if (Output) + *Output = Resources->outputs[i]; + return result; + } + } + XRRFreeOutputInfo(result); + return nullptr; +} +#endif // USING_XRANDR + +void MythDisplayX11::GetEDID(MythXDisplay *mDisplay) +{ +#ifdef USING_XRANDR + if (!mDisplay) + { + m_edid = MythEDID(); + return; + } + + XRRScreenResources* res = XRRGetScreenResourcesCurrent(mDisplay->GetDisplay(), mDisplay->GetRoot()); + RROutput rroutput = 0; + XRROutputInfo *output = GetOutput(res, mDisplay, m_screen, &rroutput); + + while (rroutput) + { + Atom edidproperty = XInternAtom(mDisplay->GetDisplay(), RR_PROPERTY_RANDR_EDID, false); + if (!edidproperty) + break; + + int propertycount = 0; + Atom* properties = XRRListOutputProperties(mDisplay->GetDisplay(), rroutput, &propertycount); + if (!properties) + break; + + bool found = false; + for (int i = 0; i < propertycount; ++i) + { + if (properties[i] == edidproperty) + { + found = true; + break; + } + } + XFree(properties); + if (!found) + break; + + Atom actualtype = 0; + int actualformat = 0;; + unsigned long bytesafter = 0; + unsigned long nitems = 0; + unsigned char* data = nullptr; + if (XRRGetOutputProperty(mDisplay->GetDisplay(), rroutput, edidproperty, + 0, 128, false, false, AnyPropertyType, &actualtype, + &actualformat, &nitems, &bytesafter, &data) == Success) + { + if (actualtype == XA_INTEGER && actualformat == 8) + m_edid = MythEDID(reinterpret_cast(data), + static_cast(nitems)); + } + break; + } + XRRFreeOutputInfo(output); + XRRFreeScreenResources(res); +#else + (void)mDisplay; + m_edid = MythEDID(); #endif +} diff --git a/mythtv/libs/libmythui/platforms/mythdisplayx11.h b/mythtv/libs/libmythui/platforms/mythdisplayx11.h index 9b2ea140cdd..03dde12e38f 100644 --- a/mythtv/libs/libmythui/platforms/mythdisplayx11.h +++ b/mythtv/libs/libmythui/platforms/mythdisplayx11.h @@ -6,6 +6,11 @@ // MythTV #include "mythdisplay.h" +#include "mythxdisplay.h" + +#ifdef USING_XRANDR +#include // always last +#endif class MythDisplayX11 : public MythDisplay { @@ -13,15 +18,21 @@ class MythDisplayX11 : public MythDisplay MythDisplayX11(); ~MythDisplayX11() override = default; static bool IsAvailable(void); - DisplayInfo GetDisplayInfo(int VideoRate = 0) override; + void UpdateCurrentMode(void) override final; #ifdef USING_XRANDR bool UsingVideoModes(void) override; const std::vector& GetVideoModes(void) override; - bool SwitchToVideoMode(int Width, int Height, double DesiredRate) override; + bool SwitchToVideoMode(QSize Size, double DesiredRate) override; + + private: + XRROutputInfo* GetOutput(XRRScreenResources* Resources, MythXDisplay* mDisplay, + QScreen* qScreen, RROutput* Output = nullptr); #endif private: + void GetEDID(MythXDisplay* mDisplay); + QMap m_modeMap { }; unsigned long m_crtc { 0 }; }; diff --git a/mythtv/libs/libmythui/platforms/mythdrmdevice.cpp b/mythtv/libs/libmythui/platforms/mythdrmdevice.cpp index 971ea49c938..1bc0f10f2d3 100644 --- a/mythtv/libs/libmythui/platforms/mythdrmdevice.cpp +++ b/mythtv/libs/libmythui/platforms/mythdrmdevice.cpp @@ -96,7 +96,7 @@ QSize MythDRMDevice::GetPhysicalSize(void) const return m_physicalSize; } -float MythDRMDevice::GetRefreshRate(void) const +double MythDRMDevice::GetRefreshRate(void) const { return m_refreshRate; } @@ -106,6 +106,11 @@ bool MythDRMDevice::Authenticated(void) const return m_authenticated; } +MythEDID MythDRMDevice::GetEDID(void) +{ + return m_edid; +} + static QString GetConnectorName(drmModeConnector *Connector) { static const QString connectorNames[DRM_MODE_CONNECTOR_DPI + 1] = @@ -178,6 +183,7 @@ bool MythDRMDevice::Initialise(void) m_physicalSize = QSize(static_cast(connector->mmWidth), static_cast(connector->mmHeight)); m_serialNumber = serial; + m_edid = edid; break; } else if (!edid.Valid()) @@ -260,9 +266,9 @@ bool MythDRMDevice::Initialise(void) if (m_crtc->mode_valid) { drmModeModeInfo mode = m_crtc->mode; - m_refreshRate = (mode.clock * 1000.0F) / (mode.htotal * mode.vtotal); + m_refreshRate = (mode.clock * 1000.0) / (mode.htotal * mode.vtotal); if (mode.flags & DRM_MODE_FLAG_INTERLACE) - m_refreshRate *= 2.0F; + m_refreshRate *= 2.0; } LOG(VB_GENERAL, LOG_DEBUG, LOC + "Initialised"); diff --git a/mythtv/libs/libmythui/platforms/mythdrmdevice.h b/mythtv/libs/libmythui/platforms/mythdrmdevice.h index 17100a6849c..7b09baa52f0 100644 --- a/mythtv/libs/libmythui/platforms/mythdrmdevice.h +++ b/mythtv/libs/libmythui/platforms/mythdrmdevice.h @@ -25,8 +25,9 @@ class MythDRMDevice : public ReferenceCounter QScreen* GetScreen (void) const; QSize GetResolution (void) const; QSize GetPhysicalSize(void) const; - float GetRefreshRate (void) const; + double GetRefreshRate (void) const; bool Authenticated (void) const; + MythEDID GetEDID (void); private: Q_DISABLE_COPY(MythDRMDevice) @@ -49,11 +50,12 @@ class MythDRMDevice : public ReferenceCounter drmModeConnector* m_connector { nullptr }; QSize m_resolution { }; QSize m_physicalSize { }; - float m_refreshRate { 0.0F }; + double m_refreshRate { 0.0 }; QString m_serialNumber { }; drmModeCrtc* m_crtc { nullptr }; int m_crtcIdx { -1 }; LogLevel_t m_verbose { LOG_INFO }; + MythEDID m_edid { }; }; #endif // MYTHDRMDEVICE_H diff --git a/mythtv/libs/libmythui/platforms/mythxdisplay.cpp b/mythtv/libs/libmythui/platforms/mythxdisplay.cpp index 4dc1fa3e9a3..70912f31b45 100644 --- a/mythtv/libs/libmythui/platforms/mythxdisplay.cpp +++ b/mythtv/libs/libmythui/platforms/mythxdisplay.cpp @@ -1,7 +1,8 @@ -/** This file is intended to hold X11 specific utility functions */ +// Std #include #include +// MythTV #include "config.h" // for CONFIG_DARWIN #include "mythlogging.h" #include "mythuihelper.h" @@ -17,15 +18,8 @@ extern "C" { } using XErrorCallbackType = int (*)(Display *, XErrorEvent *); using XErrorVectorType = std::vector; -std::map xerrors; -std::map xerror_handlers; -std::map xdisplays; -#endif // USING_X11 - -#include - -// Everything below this line is only compiled if using X11 -#ifdef USING_X11 +static std::map xerrors; +static std::map xerror_handlers; static int ErrorHandler(Display *d, XErrorEvent *xeev) { @@ -33,30 +27,27 @@ static int ErrorHandler(Display *d, XErrorEvent *xeev) return 0; } -void LockMythXDisplays(bool lock) +class MythXLocker { - if (lock) + public: + explicit MythXLocker(MythXDisplay* Disp) + : m_disp(Disp) { - std::map::iterator it; - for (it = xdisplays.begin(); it != xdisplays.end(); ++it) - it->second->Lock(); + if (m_disp) + m_disp->Lock(); } - else + + ~MythXLocker() { - std::map::reverse_iterator it; - for (it = xdisplays.rbegin(); it != xdisplays.rend(); ++it) - it->second->Unlock(); + if (m_disp) + m_disp->Unlock(); } -} -MythXDisplay *GetMythXDisplay(Display *d) -{ - if (xdisplays.count(d)) - return xdisplays[d]; - return nullptr; -} + private: + MythXDisplay *m_disp { nullptr }; +}; -MythXDisplay *OpenMythXDisplay(bool Warn /*= true*/) +MythXDisplay* MythXDisplay::OpenMythXDisplay(bool Warn /*= true*/) { auto *disp = new MythXDisplay(); if (disp && disp->Open()) @@ -73,11 +64,7 @@ MythXDisplay::~MythXDisplay() MythXLocker locker(this); if (m_disp) { - if (m_gc) - XFreeGC(m_disp, m_gc); StopLog(); - if (xdisplays.count(m_disp)) - xdisplays.erase(m_disp); XCloseDisplay(m_disp); m_disp = nullptr; } @@ -96,46 +83,14 @@ bool MythXDisplay::Open(void) if (!m_disp) return false; - xdisplays[m_disp] = this; m_screenNum = DefaultScreen(m_disp); m_screen = DefaultScreenOfDisplay(m_disp); - m_black = XBlackPixel(m_disp, m_screenNum); m_depth = DefaultDepthOfScreen(m_screen); m_root = DefaultRootWindow(m_disp); return true; } -bool MythXDisplay::CreateGC(Window win) -{ - StartLog(); - XLOCK(this, m_gc = XCreateGC(m_disp, win, 0, nullptr)); - return StopLog(); -} - -void MythXDisplay::SetForeground(unsigned long color) -{ - if (!m_gc) - return; - XLOCK(this, XSetForeground(m_disp, m_gc, color)); -} - -void MythXDisplay::FillRectangle(Window win, const QRect &rect) -{ - if (!m_gc) - return; - XLOCK(this, XFillRectangle(m_disp, win, m_gc, - rect.left(), rect.top(), - rect.width(), rect.height())); -} - -void MythXDisplay::MoveResizeWin(Window win, const QRect &rect) -{ - XLOCK(this, XMoveResizeWindow(m_disp, win, - rect.left(), rect.top(), - rect.width(), rect.height())); -} - /** * Return the size of the X Display in pixels. This corresponds to * the size of the desktop, not necessarily to the size of single @@ -143,10 +98,19 @@ void MythXDisplay::MoveResizeWin(Window win, const QRect &rect) */ QSize MythXDisplay::GetDisplaySize(void) { + XF86VidModeModeLine mode; + int dummy = 0; MythXLocker locker(this); - int displayWidthPixel = DisplayWidth( m_disp, m_screenNum); - int displayHeightPixel = DisplayHeight(m_disp, m_screenNum); - return {displayWidthPixel, displayHeightPixel}; + + if (!XF86VidModeGetModeLine(m_disp, m_screenNum, &dummy, &mode)) + { + LOG(VB_GENERAL, LOG_ERR, "X11 ModeLine query failed"); + // Fallback to old display size code - which is not updated for mode switches + return QSize(DisplayWidth(m_disp, m_screenNum), + DisplayHeight(m_disp, m_screenNum)); + } + + return QSize(mode.hdisplay, mode.vdisplay); } /** @@ -162,7 +126,7 @@ QSize MythXDisplay::GetDisplayDimensions(void) return {displayWidthMM, displayHeightMM}; } -float MythXDisplay::GetRefreshRate(void) +double MythXDisplay::GetRefreshRate(void) { XF86VidModeModeLine mode_line; int dot_clock = 0; @@ -185,8 +149,7 @@ float MythXDisplay::GetRefreshRate(void) rate = (dot_clock * 1000.0) / rate; - if (((mode_line.flags & V_INTERLACE) != 0) && - rate > 24.5 && rate < 30.5) + if (((mode_line.flags & V_INTERLACE) != 0) && rate > 24.5 && rate < 30.5) { LOG(VB_PLAYBACK, LOG_INFO, "Doubling refresh rate for interlaced display."); @@ -196,9 +159,9 @@ float MythXDisplay::GetRefreshRate(void) return rate; } -void MythXDisplay::Sync(bool flush) +void MythXDisplay::Sync(bool Flush) { - XLOCK(this, XSync(m_disp, flush)); + XLOCK(this, XSync(m_disp, Flush)); } void MythXDisplay::StartLog(void) @@ -222,12 +185,12 @@ bool MythXDisplay::StopLog(void) return CheckErrors(); } -bool MythXDisplay::CheckErrors(Display *disp) +bool MythXDisplay::CheckErrors(Display *Disp) { - if (!disp) + if (!Disp) CheckOrphanedErrors(); - Display *d = disp ? disp : m_disp; + Display *d = Disp ? Disp : m_disp; if (!d) return false; @@ -241,23 +204,16 @@ bool MythXDisplay::CheckErrors(Display *disp) if (events.empty()) return true; - for (int i = events.size() -1; i>=0; --i) + for (size_t i = 0; i < events.size(); ++i) { char buf[200]; XGetErrorText(d, events[i].error_code, buf, sizeof(buf)); LOG(VB_GENERAL, LOG_ERR, - QString("\n" - "XError type: %1\n" - " serial no: %2\n" - " err code: %3 (%4)\n" - " req code: %5\n" - " minor code: %6\n" - "resource id: %7\n") - .arg(events[i].type) - .arg(events[i].serial) + QString("XError type: %1\nSerial no: %2\nErr code: %3 (%4)\n" + "Req code: %5\nmMinor code: %6\nResource id: %7\n") + .arg(events[i].type).arg(events[i].serial) .arg(events[i].error_code).arg(buf) - .arg(events[i].request_code) - .arg(events[i].minor_code) + .arg(events[i].request_code).arg(events[i].minor_code) .arg(events[i].resourceid)); } xerrors.erase(d); diff --git a/mythtv/libs/libmythui/platforms/mythxdisplay.h b/mythtv/libs/libmythui/platforms/mythxdisplay.h index 37dfb797393..dec8dfc3337 100644 --- a/mythtv/libs/libmythui/platforms/mythxdisplay.h +++ b/mythtv/libs/libmythui/platforms/mythxdisplay.h @@ -1,8 +1,7 @@ -/** This file is intended to hold X11 specific utility functions */ - #ifndef MYTHXDISPLAY_X_ #define MYTHXDISPLAY_X_ +// Qt #include #include @@ -10,72 +9,50 @@ #include #include #include -#include +// MythTV #include "mythuiexp.h" +// Std +#include + +#define XLOCK(dpy, arg) { dpy->Lock(); arg; dpy->Unlock(); } + class MUI_PUBLIC MythXDisplay { public: + + static MythXDisplay* OpenMythXDisplay(bool Warn = true); MythXDisplay() = default; ~MythXDisplay(); - Display *GetDisplay(void) { return m_disp; } - QString GetDisplayName(void) const{ return m_displayName;} - int GetScreen(void) const { return m_screenNum; } - void Lock(void) { m_lock.lock(); } - void Unlock(void) { m_lock.unlock(); } - int GetDepth(void) const { return m_depth; } - Window GetRoot(void) const { return m_root; } - GC GetGC(void) const { return m_gc; } - unsigned long GetBlack(void) const { return m_black; } + Display *GetDisplay(void) { return m_disp; } + QString GetDisplayName(void) const{ return m_displayName; } + int GetScreen(void) const { return m_screenNum; } + void Lock(void) { m_lock.lock(); } + void Unlock(void) { m_lock.unlock(); } + int GetDepth(void) const { return m_depth; } + Window GetRoot(void) const { return m_root; } bool Open(void); - bool CreateGC(Window win); - void SetForeground(unsigned long color); - void FillRectangle(Window win, const QRect &rect); - void MoveResizeWin(Window win, const QRect &rect); QSize GetDisplaySize(void); QSize GetDisplayDimensions(void); - float GetRefreshRate(void); - void Sync(bool flush = false); + double GetRefreshRate(void); + void Sync(bool Flush = false); void StartLog(void); bool StopLog(void); private: - bool CheckErrors(Display *disp = nullptr); + bool CheckErrors(Display *Disp = nullptr); void CheckOrphanedErrors(void); - Display *m_disp {nullptr}; - int m_screenNum {0}; - Screen *m_screen {nullptr}; - int m_depth {0}; - unsigned long m_black {0}; - GC m_gc {nullptr}; - Window m_root {0}; - QMutex m_lock {QMutex::Recursive}; - QString m_displayName{}; + Display *m_disp { nullptr }; + int m_screenNum { 0 }; + Screen *m_screen { nullptr }; + int m_depth { 0 }; + Window m_root { 0 }; + QMutex m_lock { QMutex::Recursive }; + QString m_displayName{ }; }; -class MythXLocker -{ - public: - explicit MythXLocker(MythXDisplay*d) : m_disp(d) - { - if (m_disp) m_disp->Lock(); - } - - ~MythXLocker() - { - if (m_disp) m_disp->Unlock(); - } - - private: - MythXDisplay *m_disp {nullptr}; -}; - -MUI_PUBLIC void LockMythXDisplays(bool lock); -MUI_PUBLIC MythXDisplay *GetMythXDisplay(Display*); -MUI_PUBLIC MythXDisplay *OpenMythXDisplay(bool Warn = true); -#define XLOCK(dpy, arg) { dpy->Lock(); arg; dpy->Unlock(); } #endif // USING_X11 // These X11 defines conflict with the QT key event enum values diff --git a/mythtv/libs/libmythui/screensaver-x11.cpp b/mythtv/libs/libmythui/screensaver-x11.cpp index a5480f08104..bfe3c88cac5 100644 --- a/mythtv/libs/libmythui/screensaver-x11.cpp +++ b/mythtv/libs/libmythui/screensaver-x11.cpp @@ -46,7 +46,7 @@ class ScreenSaverX11Private LOG(VB_GENERAL, LOG_INFO, LOC + "XScreenSaver support enabled"); } - m_display = OpenMythXDisplay(false); + m_display = MythXDisplay::OpenMythXDisplay(false); if (m_display) { int dummy0 = 0; diff --git a/mythtv/libs/libmythui/screensaver.cpp b/mythtv/libs/libmythui/screensaver.cpp index 8992f36737e..ac24641a8c2 100644 --- a/mythtv/libs/libmythui/screensaver.cpp +++ b/mythtv/libs/libmythui/screensaver.cpp @@ -30,7 +30,7 @@ ScreenSaverControl::ScreenSaverControl() : m_screenSavers.push_back(new ScreenSaverDBus()); #endif #if defined(USING_X11) - MythXDisplay* display = OpenMythXDisplay(false); + MythXDisplay* display = MythXDisplay::OpenMythXDisplay(false); if (display) { m_screenSavers.push_back(new ScreenSaverX11()); diff --git a/mythtv/programs/mythfrontend/globalsettings.cpp b/mythtv/programs/mythfrontend/globalsettings.cpp index 48a0fc4bfa0..0b4ac8b857a 100644 --- a/mythtv/programs/mythfrontend/globalsettings.cpp +++ b/mythtv/programs/mythfrontend/globalsettings.cpp @@ -2350,28 +2350,27 @@ void HostRefreshRateComboBoxSetting::ChangeResolution(StandardSetting * setting) setEnabled(!list.empty()); } -vector HostRefreshRateComboBoxSetting::GetRefreshRates( - const QString &res) +vector HostRefreshRateComboBoxSetting::GetRefreshRates(const QString &res) { QStringList slist = res.split("x"); - int w = 0; - int h = 0; + int width = 0; + int height = 0; bool ok0 = false; bool ok1 = false; if (2 == slist.size()) { - w = slist[0].toInt(&ok0); - h = slist[1].toInt(&ok1); + width = slist[0].toInt(&ok0); + height = slist[1].toInt(&ok1); } vector result; if (ok0 && ok1) { + QSize size(width, height); MythDisplay *display = MythDisplay::AcquireRelease(); - result = display->GetRefreshRates(w, h); + result = display->GetRefreshRates(size); MythDisplay::AcquireRelease(false); } - return result; }