Skip to content

Commit

Permalink
MythDisplayOSX: Modernise OSX display mode switching
Browse files Browse the repository at this point in the history
- this still needs a little work elsewhere as the window is not created
early enough at frontend startup and hence we get invalid default modes.
- also may need to be better handle 'built in' displays (e.g. laptop
screens) that do not have a fixed refresh rate.
  • Loading branch information
mark-kendall committed Dec 8, 2019
1 parent f874200 commit c84a37b
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 64 deletions.
128 changes: 64 additions & 64 deletions mythtv/libs/libmythui/platforms/mythdisplayosx.cpp
Expand Up @@ -5,6 +5,8 @@
#include "mythdisplayosx.h"
#import "util-osx.h"

#define LOC QString("DisplayOSX: ")

MythDisplayOSX::MythDisplayOSX()
: MythDisplay()
{
Expand All @@ -13,6 +15,7 @@ MythDisplayOSX::MythDisplayOSX()

MythDisplayOSX::~MythDisplayOSX()
{
ClearModes();
}

DisplayInfo MythDisplayOSX::GetDisplayInfo(int VideoRate)
Expand All @@ -25,34 +28,23 @@ DisplayInfo MythDisplayOSX::GetDisplayInfo(int VideoRate)
CGDirectDisplayID disp = GetOSXDisplay(win);
if (!disp)
return ret;

CFDictionaryRef ref = CGDisplayCurrentMode(disp);
double rate = 0.0;
bool interlaced = false;
if (ref)
{
rate = get_double_CF(ref, kCGDisplayRefreshRate);
interlaced = get_bool_CF(ref, kCGDisplayModeIsInterlaced);
}

// Consistent with X11 version
if (interlaced)
{
rate *= 2.0;
LOG(VB_PLAYBACK, LOG_INFO, "Doubling refresh rate for interlaced display.");
}
CGDisplayModeRef mode = CGDisplayCopyDisplayMode(disp);
if (!mode)
return ret;
double rate = CGDisplayModeGetRefreshRate(mode);
bool interlaced = CGDisplayModeGetIOFlags(mode) & kDisplayModeInterlacedFlag;
size_t width = CGDisplayModeGetWidth(mode);
size_t height = CGDisplayModeGetHeight(mode);
CGDisplayModeRelease(mode);

if (VALID_RATE(static_cast<float>(rate)))
ret.m_rate = 1000000.0F / static_cast<float>(rate);
else
ret.m_rate = SanitiseRefreshRate(VideoRate);

CGSize size_in_mm = CGDisplayScreenSize(disp);
ret.m_size = QSize((uint) size_in_mm.width, (uint) size_in_mm.height);

int width = CGDisplayPixelsWide(disp);
int height = CGDisplayPixelsHigh(disp);
ret.m_res = QSize(width, height);
CGSize sizemm = CGDisplayScreenSize(disp);
ret.m_size = QSize(static_cast<int>(sizemm.width), static_cast<int>(sizemm.height));
ret.m_res = QSize(static_cast<int>(width), static_cast<int>(height));
return ret;
}

Expand All @@ -68,43 +60,46 @@ const std::vector<MythDisplayMode>& MythDisplayOSX::GetVideoModes(void)
if (!m_videoModes.empty() || !HasMythMainWindow())
return m_videoModes;

WId win = (qobject_cast<QWidget*>(MythMainWindow::getMainWindow()))->winId();
CGDirectDisplayID d = GetOSXDisplay(win);

CFArrayRef displayModes = CGDisplayAvailableModes(d);
ClearModes();

if (nullptr == displayModes)
WId win = (qobject_cast<QWidget*>(MythMainWindow::getMainWindow()))->winId();
CGDirectDisplayID disp = GetOSXDisplay(win);
if (!disp)
return m_videoModes;
CFArrayRef modes = CGDisplayCopyAllDisplayModes(disp, nullptr);
if (!modes)
return m_videoModes;

DisplayModeMap screen_map;
CGSize sizemm = CGDisplayScreenSize(disp);

for (int i = 0; i < CFArrayGetCount(displayModes); ++i)
for (int i = 0; i < CFArrayGetCount(modes); ++i)
{
CFDictionaryRef displayMode =(CFDictionaryRef)CFArrayGetValueAtIndex(displayModes, i);
int width = get_int_CF(displayMode, kCGDisplayWidth);
int height = get_int_CF(displayMode, kCGDisplayHeight);
double refresh = get_double_CF(displayMode, kCGDisplayRefreshRate);
bool interlaced = get_bool_CF(displayMode, kCGDisplayModeIsInterlaced);
// TODO check whether OSX already doubles the rate
if (interlaced)
refresh *= 2.0;
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);

// See note in MythDisplayX11
if (interlaced)
{
LOG(VB_PLAYBACK, LOG_INFO, QString("Ignoring interlaced mode %1x%2 %3i")
.arg(width).arg(height).arg(refresh, 2, 'f', 2, '0'));
LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Ignoring interlaced mode %1x%2 %3i")
.arg(width).arg(height).arg(rate, 2, 'f', 2, '0'));
continue;
}

uint64_t key = MythDisplayMode::CalcKey(width, height, 0.0);
if (screen_map.find(key) == screen_map.end())
screen_map[key] = MythDisplayMode(width, height, 0, 0, -1.0, refresh);
screen_map[key] = MythDisplayMode(width, height, sizemm.width,
sizemm.height, -1.0, rate);
else
screen_map[key].AddRefreshRate(refresh);
screen_map[key].AddRefreshRate(rate);
m_modeMap.insert(MythDisplayMode::CalcKey(width, height, rate),
CGDisplayModeRetain(mode));
}

//CFRelease(displayModes); // this release causes a segfault
CFRelease(modes);

for (auto it = screen_map.begin(); screen_map.end() != it; ++it)
m_videoModes.push_back(it->second);
Expand All @@ -116,37 +111,42 @@ bool MythDisplayOSX::SwitchToVideoMode(int Width, int Height, double DesiredRate
{
if (!HasMythMainWindow())
return false;

WId win = (qobject_cast<QWidget*>(MythMainWindow::getMainWindow()))->winId();
CGDirectDisplayID d = GetOSXDisplay(win);
CFDictionaryRef dispMode = nullptr;
boolean_t match = 0;

// find mode that matches the desired size

if (DesiredRate)
dispMode = CGDisplayBestModeForParametersAndRefreshRate(
d, 32, Width, Height,
(CGRefreshRate)((short)DesiredRate), &match);

if (!match)
dispMode =
CGDisplayBestModeForParameters(d, 32, Width, Height, &match);
CGDirectDisplayID disp = GetOSXDisplay(win);
if (!disp)
return false;

if (!match)
dispMode =
CGDisplayBestModeForParameters(d, 16, Width, Height, &match);
auto rate = static_cast<double>(NAN);
MythDisplayMode desired(Width, Height, 0, 0, -1.0, DesiredRate);
int idx = MythDisplayMode::FindBestMatch(m_videoModes, desired, rate);
if (idx < 0)
{
LOG(VB_GENERAL, LOG_ERR, LOC + "Desired resolution and frame rate not found.");
return false;
}

if (!match)
auto mode = MythDisplayMode::CalcKey(Width, Height, rate);
if (!m_modeMap.contains(mode))
{
LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to find mode");
return false;
}

// switch mode and return success
CGDisplayCapture(d);
CGDisplayCapture(disp);
CGDisplayConfigRef cfg;
CGBeginDisplayConfiguration(&cfg);
CGConfigureDisplayFadeEffect(cfg, 0.3f, 0.5f, 0, 0, 0);
CGConfigureDisplayMode(cfg, d, dispMode);
CGDisplaySetDisplayMode(disp, m_modeMap.value(mode), nullptr);
CGError err = CGCompleteDisplayConfiguration(cfg, kCGConfigureForAppOnly);
CGDisplayRelease(d);
return (err == kCGErrorSuccess);
CGDisplayRelease(disp);
return err == kCGErrorSuccess;
}

void MythDisplayOSX::ClearModes(void)
{
for (auto it = m_modeMap.cbegin(); it != m_modeMap.cend(); ++it)
CGDisplayModeRelease(it.value());
m_modeMap.clear();
m_videoModes.clear();
}
7 changes: 7 additions & 0 deletions mythtv/libs/libmythui/platforms/mythdisplayosx.h
Expand Up @@ -4,6 +4,9 @@
// MythTV
#include "mythdisplay.h"

// CoreGraphics
#include <CoreGraphics/CGDirectDisplay.h>

class MythDisplayOSX : public MythDisplay
{
public:
Expand All @@ -15,6 +18,10 @@ class MythDisplayOSX : public MythDisplay
bool UsingVideoModes(void) override;
const std::vector<MythDisplayMode>& GetVideoModes(void) override;
bool SwitchToVideoMode(int Width, int Height, double DesiredRate) override;

private:
void ClearModes(void);
QMap<uint64_t, CGDisplayModeRef> m_modeMap { };
};

#endif // MYTHDISPLAYOSX_H

0 comments on commit c84a37b

Please sign in to comment.