Fixed bug 2696 - Mac: fix display mode refresh rate calculation

Alex Szpakowski

SDL's Cocoa backend uses the CGDisplayMode API to get refresh rate information about a display mode, but CGDisplayModeGetRefreshRate will return 0 on most non-CRT monitors.

The only way I know of to get correct refresh rate information in OS X is via the CoreVideo DisplayLink API.
I have attached a patch which tries to use the CVDisplayLinkGetNominalOutputVideoRefreshPeriod function if CGDisplayModeGetRefreshRate fails, which fixes display mode refresh rate information on the monitors I tested.

The CVDisplayLink API requires linking with the CoreVideo framework, and the patch updates the various build files to do so.
This commit is contained in:
Sam Lantinga 2014-08-23 10:47:50 -07:00
parent ca62d75878
commit 46e3d957ab
8 changed files with 144 additions and 5 deletions

View file

@ -27,6 +27,10 @@
/* We need this for IODisplayCreateInfoDictionary and kIODisplayOnlyPreferredName */
#include <IOKit/graphics/IOGraphicsLib.h>
/* We need this for CVDisplayLinkGetNominalOutputVideoRefreshPeriod */
#include <CoreVideo/CVBase.h>
#include <CoreVideo/CVDisplayLink.h>
/* we need this for ShowMenuBar() and HideMenuBar(). */
#include <Carbon/Carbon.h>
@ -114,7 +118,7 @@ CG_SetError(const char *prefix, CGDisplayErr result)
}
static SDL_bool
GetDisplayMode(_THIS, const void *moderef, SDL_DisplayMode *mode)
GetDisplayMode(_THIS, const void *moderef, CVDisplayLinkRef link, SDL_DisplayMode *mode)
{
SDL_DisplayModeData *data;
long width = 0;
@ -133,7 +137,7 @@ GetDisplayMode(_THIS, const void *moderef, SDL_DisplayMode *mode)
CFStringRef fmt = CGDisplayModeCopyPixelEncoding(vidmode);
width = (long) CGDisplayModeGetWidth(vidmode);
height = (long) CGDisplayModeGetHeight(vidmode);
refreshRate = (long) CGDisplayModeGetRefreshRate(vidmode);
refreshRate = (long) (CGDisplayModeGetRefreshRate(vidmode) + 0.5);
if (CFStringCompare(fmt, CFSTR(IO32BitDirectPixels),
kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
@ -141,6 +145,9 @@ GetDisplayMode(_THIS, const void *moderef, SDL_DisplayMode *mode)
} else if (CFStringCompare(fmt, CFSTR(IO16BitDirectPixels),
kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
bpp = 16;
} else if (CFStringCompare(fmt, CFSTR(kIO30BitDirectPixels),
kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
bpp = 30;
} else {
bpp = 0; /* ignore 8-bit and such for now. */
}
@ -151,6 +158,7 @@ GetDisplayMode(_THIS, const void *moderef, SDL_DisplayMode *mode)
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060
if (!IS_SNOW_LEOPARD_OR_LATER()) {
CFNumberRef number;
double refresh;
CFDictionaryRef vidmode = (CFDictionaryRef) moderef;
number = CFDictionaryGetValue(vidmode, kCGDisplayWidth);
CFNumberGetValue(number, kCFNumberLongType, &width);
@ -159,15 +167,27 @@ GetDisplayMode(_THIS, const void *moderef, SDL_DisplayMode *mode)
number = CFDictionaryGetValue(vidmode, kCGDisplayBitsPerPixel);
CFNumberGetValue(number, kCFNumberLongType, &bpp);
number = CFDictionaryGetValue(vidmode, kCGDisplayRefreshRate);
CFNumberGetValue(number, kCFNumberLongType, &refreshRate);
CFNumberGetValue(number, kCFNumberDoubleType, &refresh);
refreshRate = (long) (refresh + 0.5);
}
#endif
/* CGDisplayModeGetRefreshRate returns 0 for many non-CRT displays. */
if (refreshRate == 0 && link != NULL) {
CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link);
if ((time.flags & kCVTimeIsIndefinite) == 0 && time.timeValue != 0) {
refreshRate = (long) ((time.timeScale / (double) time.timeValue) + 0.5);
}
}
mode->format = SDL_PIXELFORMAT_UNKNOWN;
switch (bpp) {
case 16:
mode->format = SDL_PIXELFORMAT_ARGB1555;
break;
case 30:
mode->format = SDL_PIXELFORMAT_ARGB2101010;
break;
case 32:
mode->format = SDL_PIXELFORMAT_ARGB8888;
break;
@ -241,6 +261,7 @@ Cocoa_InitModes(_THIS)
SDL_DisplayData *displaydata;
SDL_DisplayMode mode;
const void *moderef = NULL;
CVDisplayLinkRef link = NULL;
if (pass == 0) {
if (!CGDisplayIsMain(displays[i])) {
@ -277,16 +298,21 @@ Cocoa_InitModes(_THIS)
}
displaydata->display = displays[i];
CVDisplayLinkCreateWithCGDisplay(displays[i], &link);
SDL_zero(display);
/* this returns a stddup'ed string */
display.name = (char *)Cocoa_GetDisplayName(displays[i]);
if (!GetDisplayMode (_this, moderef, &mode)) {
if (!GetDisplayMode(_this, moderef, link, &mode)) {
CVDisplayLinkRelease(link);
Cocoa_ReleaseDisplayMode(_this, moderef);
SDL_free(display.name);
SDL_free(displaydata);
continue;
}
CVDisplayLinkRelease(link);
display.desktop_mode = mode;
display.current_mode = mode;
display.driverdata = displaydata;
@ -328,13 +354,16 @@ Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
#endif
if (modes) {
CVDisplayLinkRef link = NULL;
const CFIndex count = CFArrayGetCount(modes);
CFIndex i;
CVDisplayLinkCreateWithCGDisplay(data->display, &link);
for (i = 0; i < count; i++) {
const void *moderef = CFArrayGetValueAtIndex(modes, i);
SDL_DisplayMode mode;
if (GetDisplayMode(_this, moderef, &mode)) {
if (GetDisplayMode(_this, moderef, link, &mode)) {
if (IS_SNOW_LEOPARD_OR_LATER()) {
CGDisplayModeRetain((CGDisplayModeRef) moderef);
}
@ -342,6 +371,7 @@ Cocoa_GetDisplayModes(_THIS, SDL_VideoDisplay * display)
}
}
CVDisplayLinkRelease(link);
Cocoa_ReleaseDisplayModeList(_this, modes);
}
}