#include <sys/cdefs.h>
#include <mach/mach.h>
#include <mach/thread_switch.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <libc.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFBundlePriv.h>
#include <IOKit/IOKitLib.h>
#include <libkern/OSByteOrder.h>
#include <IOKit/graphics/IOGraphicsLib.h>
#include <IOKit/graphics/IOGraphicsLibPrivate.h>
#include <IOKit/graphics/IOGraphicsTypesPrivate.h>
#include <IOKit/graphics/IOGraphicsEngine.h>
#include <IOKit/platform/IOPlatformSupportPrivate.h>
#include "IOGraphicsLibInternal.h"
#define DEBUGPARAMS 0
#define SPOOF_EDID 0
#define arrayCount(x) (sizeof(x) / sizeof(x[0]))
__private_extern__ IOReturn
readFile(const char *path, vm_address_t * objAddr, vm_size_t * objSize);
__private_extern__ CFMutableDictionaryRef
readPlist( const char * path, UInt32 key );
__private_extern__ Boolean
writePlist( const char * path, CFMutableDictionaryRef dict, UInt32 key __unused );
static char gIODisplayBoardID[256] = { 0 };
static void
setDictionaryDisplayIconValue(CFMutableDictionaryRef dst, CFDictionaryRef src)
{
CFTypeRef value = NULL;
if (CFDictionaryGetValueIfPresent(src, CFSTR("display-icon"), (const void**)&value))
CFDictionarySetValue(dst, CFSTR("display-icon"), value);
}
static void
setDictionaryDisplayResolutionPreviewValues(CFMutableDictionaryRef dst, CFDictionaryRef src)
{
CFTypeRef value = NULL;
if (CFDictionaryGetValueIfPresent(src, CFSTR("display-resolution-preview-icon"), (const void**)&value))
CFDictionarySetValue(dst, CFSTR("display-resolution-preview-icon"), value);
if (CFDictionaryGetValueIfPresent(src, CFSTR("resolution-preview-x"), (const void**)&value))
CFDictionarySetValue(dst, CFSTR("resolution-preview-x"), value);
if (CFDictionaryGetValueIfPresent(src, CFSTR("resolution-preview-y"), (const void**)&value))
CFDictionarySetValue(dst, CFSTR("resolution-preview-y"), value);
if (CFDictionaryGetValueIfPresent(src, CFSTR("resolution-preview-width"), (const void**)&value))
CFDictionarySetValue(dst, CFSTR("resolution-preview-width"), value);
if (CFDictionaryGetValueIfPresent(src, CFSTR("resolution-preview-height"), (const void**)&value))
CFDictionarySetValue(dst, CFSTR("resolution-preview-height"), value);
}
static void
MaxTimingRangeRec( IODisplayTimingRange * range )
{
bzero( range, sizeof( IODisplayTimingRange) );
range->supportedSyncFlags = 0xffffffff;
range->supportedSignalLevels = 0xffffffff;
range->supportedSignalConfigs = 0xffffffff;
range->maxFrameRate = 0xffffffff;
range->maxLineRate = 0xffffffff;
range->maxPixelClock = 0xffffffff;
range->maxPixelError = 0xffffffff;
range->maxHorizontalTotal = 0xffffffff;
range->maxVerticalTotal = 0xffffffff;
range->maxHorizontalActiveClocks = 0xffffffff;
range->maxHorizontalBlankingClocks = 0xffffffff;
range->maxHorizontalSyncOffsetClocks = 0xffffffff;
range->maxHorizontalPulseWidthClocks = 0xffffffff;
range->maxVerticalActiveClocks = 0xffffffff;
range->maxVerticalBlankingClocks = 0xffffffff;
range->maxVerticalSyncOffsetClocks = 0xffffffff;
range->maxVerticalPulseWidthClocks = 0xffffffff;
range->maxHorizontalBorderLeft = 0xffffffff;
range->maxHorizontalBorderRight = 0xffffffff;
range->maxVerticalBorderTop = 0xffffffff;
range->maxVerticalBorderBottom = 0xffffffff;
range->charSizeHorizontalActive = 1;
range->charSizeHorizontalBlanking = 1;
range->charSizeHorizontalSyncOffset = 1;
range->charSizeHorizontalSyncPulse = 1;
range->charSizeVerticalActive = 1;
range->charSizeVerticalBlanking = 1;
range->charSizeVerticalSyncOffset = 1;
range->charSizeVerticalSyncPulse = 1;
range->charSizeHorizontalBorderLeft = 1;
range->charSizeHorizontalBorderRight = 1;
range->charSizeVerticalBorderTop = 1;
range->charSizeVerticalBorderBottom = 1;
range->charSizeHorizontalTotal = 1;
range->charSizeVerticalTotal = 1;
}
static Boolean
EDIDDescToDisplayTimingRangeRec( EDID * edid, EDIDGeneralDesc * desc,
IODisplayTimingRange * range )
{
UInt8 byte;
if( !edid || (edid->version < 1) || (edid->revision < 1))
return( false );
if( desc->flag1 || desc->flag2 || desc->flag3)
return( false );
if( 0xfd != desc->type)
return( false );
MaxTimingRangeRec( range );
byte = edid->displayParams[0];
if (!(0x80 & byte))
{
range->supportedSignalLevels = 1 << ((byte >> 5) & 3);
range->supportedSyncFlags = ((byte & 1) ? kIORangeSupportsVSyncSerration : 0)
| ((byte & 2) ? kIORangeSupportsSyncOnGreen : 0)
| ((byte & 4) ? kIORangeSupportsCompositeSync : 0)
| ((byte & 8) ? kIORangeSupportsSeparateSyncs : 0);
}
range->supportedSignalConfigs = kIORangeSupportsInterlacedCEATiming;
range->minVerticalPulseWidthClocks = 1;
range->minHorizontalPulseWidthClocks = 1;
range->minFrameRate = desc->data[0];
range->maxFrameRate = desc->data[1];
range->minLineRate = desc->data[2] * 1000;
range->maxLineRate = desc->data[3] * 1000;
range->maxPixelClock = desc->data[4] * 10000000ULL;
range->minHorizontalActiveClocks = 640;
range->minVerticalActiveClocks = 480;
if( range->minLineRate)
range->maxHorizontalActiveClocks = range->maxPixelClock / range->minLineRate;
if( range->minFrameRate)
range->maxVerticalActiveClocks = range->maxPixelClock
/ (range->minHorizontalActiveClocks * range->minFrameRate);
return( true );
}
static CFMutableDictionaryRef
IODisplayCreateOverrides( io_service_t framebuffer, IOOptionBits options,
IODisplayVendorID vendor, IODisplayProductID product,
UInt32 serialNumber __unused,
uint32_t manufactureYear,
uint32_t manufactureWeek,
Boolean isDigital )
{
char path[256];
CFTypeRef obj = 0;
CFMutableDictionaryRef dict = 0;
#define DISPLAY_BUNDLE_PATH "/System/Library/Displays"
static const char * overridesPath1 = DISPLAY_BUNDLE_PATH "/Contents/Resources/Overrides";
static const char * overridesPath2 = DISPLAY_BUNDLE_PATH "/Overrides";
const char * overridesPath = overridesPath1;
if (0 != access(overridesPath, F_OK)) overridesPath = overridesPath2;
if( 0 == (options & kIODisplayMatchingInfo)) {
snprintf( path, sizeof(path), "%s"
"/" kDisplayVendorID "-%x"
"/" kDisplayProductID "-%x",
overridesPath, (unsigned) vendor, (unsigned) product );
obj = readPlist( path, ((vendor & 0xffff) << 16) | (product & 0xffff) );
if ((!obj) && manufactureYear && manufactureWeek)
{
snprintf( path, sizeof(path), "%s"
"/" kDisplayVendorID "-%x"
"/" kDisplayYearOfManufacture "-%d"
"-" kDisplayWeekOfManufacture "-%d",
overridesPath,
(unsigned) vendor,
manufactureYear, manufactureWeek );
obj = readPlist( path, ((vendor & 0xffff) << 16) | (product & 0xffff) );
}
if (obj)
{
if( CFDictionaryGetTypeID() == CFGetTypeID( obj ))
{
dict = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, obj);
}
else if( CFArrayGetTypeID() == CFGetTypeID( obj ))
{
CFArrayRef array;
CFIndex count, idx;
CFTypeRef obj2;
CFDictionaryRef matching, candidate;
array = obj;
candidate = 0;
count = CFArrayGetCount(array);
for (idx = 0; idx < count; idx++, candidate = 0)
{
obj2 = CFArrayGetValueAtIndex(array, idx);
if (CFDictionaryGetTypeID() != CFGetTypeID(obj2))
continue;
candidate = obj2;
matching = CFDictionaryGetValue(candidate, CFSTR(kIODisplayOverrideMatchingKey));
if (!matching)
break;
if (CFDictionaryGetTypeID() != CFGetTypeID(matching))
continue;
obj2 = CFDictionaryGetValue(matching, CFSTR(kIODisplayIsDigitalKey));
if ((obj2 == kCFBooleanTrue) && !isDigital)
continue;
if ((obj2 == kCFBooleanFalse) && isDigital)
continue;
break;
}
if (candidate)
dict = CFDictionaryCreateMutableCopy( kCFAllocatorDefault, 0, candidate);
}
CFRelease( obj );
}
}
if( !dict)
dict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if( dict) do {
CFStringRef string;
CFURLRef url;
CFBundleRef bdl;
if((kIODisplayMatchingInfo | kIODisplayNoProductName) & options)
continue;
snprintf( path, sizeof(path), DISPLAY_BUNDLE_PATH);
string = CFStringCreateWithCString( kCFAllocatorDefault, path,
kCFStringEncodingMacRoman );
if( !string)
continue;
url = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, string,
kCFURLPOSIXPathStyle, true );
CFRelease(string);
if( !url)
continue;
bdl = CFBundleCreate( kCFAllocatorDefault, url);
if( bdl) {
CFDictionarySetValue( dict, CFSTR(kDisplayBundleKey), bdl);
CFRelease(bdl);
}
CFRelease(url);
} while( false );
if (dict) {
if (gIODisplayBoardID[0] == 0) {
io_registry_entry_t ioRegRoot = IORegistryGetRootEntry(kIOMasterPortDefault);
if (ioRegRoot) {
CFDataRef boardId = (CFDataRef) IORegistryEntrySearchCFProperty(ioRegRoot,
kIOServicePlane,
CFSTR("board-id"),
kCFAllocatorDefault,
kIORegistryIterateRecursively);
IOObjectRelease(ioRegRoot);
if (boardId) {
size_t len = CFDataGetLength(boardId);
if (len > sizeof(gIODisplayBoardID)) len = sizeof(gIODisplayBoardID);
strlcpy(gIODisplayBoardID, (const char *) CFDataGetBytePtr(boardId), len);
CFRelease(boardId);
}
}
}
char enclosureColor[8] = { 0 };
uint8_t r = 0;
uint8_t g = 0;
uint8_t b = 0;
IOReturn ret = IOPlatformGetDeviceColor(kIOPlatformDeviceEnclosureColorKey, &r, &g, &b);
if (ret == kIOReturnSuccess)
snprintf(enclosureColor, sizeof(enclosureColor), "-%x%x%x", r, g, b);
CFDataRef builtin = (CFDataRef) IORegistryEntryCreateCFProperty(framebuffer,
CFSTR(kIOFBBuiltInKey),
kCFAllocatorDefault, kNilOptions);
CFMutableDictionaryRef iconDict = NULL;
snprintf(path, sizeof(path), "%s"
"/" "Icons.plist",
overridesPath);
if (access(path, F_OK) == 0) iconDict = readPlist(path, 0);
CFStringRef vendorIdString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%x"), (unsigned int)vendor);
CFStringRef productIdString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%x"), (unsigned int)product);
CFStringRef productIdWithColorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%x%s"), (unsigned int)product, enclosureColor);
CFStringRef modelString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s"), gIODisplayBoardID);
CFStringRef modelWithColorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%s%s"), gIODisplayBoardID, enclosureColor);
snprintf(path, sizeof(path), "%s"
"/" kDisplayVendorID "-%x"
"/" kDisplayProductID "-%x-%s%s.icns",
overridesPath,
(unsigned)vendor, (unsigned)product, gIODisplayBoardID, enclosureColor);
Boolean foundIcon = false;
if (access(path, F_OK) == 0)
foundIcon = true;
if (!foundIcon) {
snprintf(path, sizeof(path), "%s"
"/" kDisplayVendorID "-%x"
"/" kDisplayYearOfManufacture "-%d"
"-" kDisplayWeekOfManufacture "-%d-%s%s.icns",
overridesPath,
(unsigned)vendor,
manufactureYear, manufactureWeek, gIODisplayBoardID, enclosureColor);
if (access(path, F_OK) == 0)
foundIcon = true;
}
CFStringRef productModelDisplayIconFilePath = NULL;
if (foundIcon)
productModelDisplayIconFilePath = CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, path);
CFStringRef modelDisplayIconFilePath = NULL;
if (builtin) {
snprintf(path, sizeof(path), "%s/Models/%s%s.icns", overridesPath, gIODisplayBoardID, enclosureColor);
if (access(path, F_OK) == 0)
modelDisplayIconFilePath = CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, path);
}
snprintf(path, sizeof(path), "%s"
"/" kDisplayVendorID "-%x"
"/" kDisplayProductID "-%x%s.icns",
overridesPath,
(unsigned)vendor, (unsigned)product, enclosureColor);
foundIcon = false;
if (access(path, F_OK) == 0)
foundIcon = true;
if (!foundIcon) {
snprintf(path, sizeof(path), "%s"
"/" kDisplayVendorID "-%x"
"/" kDisplayYearOfManufacture "-%d"
"-" kDisplayWeekOfManufacture "-%d%s.icns",
overridesPath,
(unsigned)vendor,
manufactureYear, manufactureWeek, enclosureColor);
if (access(path, F_OK) == 0)
foundIcon = true;
}
CFStringRef productDisplayIconFilePath = NULL;
if (foundIcon)
productDisplayIconFilePath = CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, path);
snprintf(path, sizeof(path), "%s"
"/" kDisplayVendorID "-%x.icns",
overridesPath,
(unsigned)vendor);
CFStringRef vendorDisplayIconFilePath = NULL;
if (access(path, F_OK) == 0)
vendorDisplayIconFilePath = CFStringCreateWithFileSystemRepresentation(kCFAllocatorDefault, path);
CFMutableDictionaryRef displayDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFMutableDictionaryRef modelIdsDict = NULL;
if (iconDict && builtin && CFDictionaryGetValueIfPresent(iconDict, CFSTR("board-ids"), (const void**)&modelIdsDict)) {
CFMutableDictionaryRef modelDict = NULL;
if (CFDictionaryGetValueIfPresent(modelIdsDict, modelWithColorString, (const void**)&modelDict)
|| CFDictionaryGetValueIfPresent(modelIdsDict, modelString, (const void**)&modelDict)) {
setDictionaryDisplayIconValue(displayDict, modelDict);
setDictionaryDisplayResolutionPreviewValues(displayDict, modelDict);
}
}
if (builtin && !CFDictionaryContainsKey(displayDict, CFSTR("display-icon")) && modelDisplayIconFilePath)
CFDictionarySetValue(dict, CFSTR("display-icon"), vendorDisplayIconFilePath);
CFMutableDictionaryRef vendorIdsDict = NULL;
if (iconDict && CFDictionaryGetValueIfPresent(iconDict, CFSTR("vendors"), (const void**)&vendorIdsDict)) {
CFMutableDictionaryRef vendorDict = NULL;
if (CFDictionaryGetValueIfPresent(vendorIdsDict, vendorIdString, (const void**)&vendorDict)) {
CFMutableDictionaryRef displayIdsDict = NULL;
if (CFDictionaryGetValueIfPresent(vendorDict, CFSTR("products"), (const void**)&displayIdsDict)) {
CFMutableDictionaryRef deviceDict = NULL;
if (CFDictionaryGetValueIfPresent(displayIdsDict, productIdWithColorString, (const void**)&deviceDict)
|| CFDictionaryGetValueIfPresent(displayIdsDict, productIdString, (const void**)&deviceDict)) {
if (builtin && CFDictionaryGetValueIfPresent(deviceDict, CFSTR("board-ids"), (const void**)&modelIdsDict)) {
CFMutableDictionaryRef modelDict = NULL;
if (CFDictionaryGetValueIfPresent(modelIdsDict, modelWithColorString, (const void**)&modelDict)
|| CFDictionaryGetValueIfPresent(modelIdsDict, modelString, (const void**)&modelDict)) {
setDictionaryDisplayIconValue(displayDict, modelDict);
setDictionaryDisplayResolutionPreviewValues(displayDict, modelDict);
if (!CFDictionaryContainsKey(displayDict, CFSTR("display-icon")) && productModelDisplayIconFilePath)
CFDictionarySetValue(displayDict, CFSTR("display-icon"), productModelDisplayIconFilePath);
}
}
if (!builtin) {
if (!CFDictionaryContainsKey(displayDict, CFSTR("display-icon")))
setDictionaryDisplayIconValue(displayDict, deviceDict);
if (!CFDictionaryContainsKey(displayDict, CFSTR("display-icon")) && productDisplayIconFilePath)
CFDictionarySetValue(displayDict, CFSTR("display-icon"), productDisplayIconFilePath);
}
if (!CFDictionaryContainsKey(displayDict, CFSTR("display-resolution-preview-icon")))
setDictionaryDisplayResolutionPreviewValues(displayDict, deviceDict);
}
}
if (!builtin) {
if (!CFDictionaryContainsKey(displayDict, CFSTR("display-icon")) && productDisplayIconFilePath)
CFDictionarySetValue(displayDict, CFSTR("display-icon"), productDisplayIconFilePath);
if (!CFDictionaryContainsKey(displayDict, CFSTR("display-icon")))
setDictionaryDisplayIconValue(displayDict, vendorDict);
if (!CFDictionaryContainsKey(displayDict, CFSTR("display-icon")) && vendorDisplayIconFilePath)
CFDictionarySetValue(displayDict, CFSTR("display-icon"), vendorDisplayIconFilePath);
}
if (!CFDictionaryContainsKey(displayDict, CFSTR("display-resolution-preview-icon")))
setDictionaryDisplayResolutionPreviewValues(displayDict, vendorDict);
}
if (!builtin) {
if (!CFDictionaryContainsKey(displayDict, CFSTR("display-icon")) && productDisplayIconFilePath)
CFDictionarySetValue(displayDict, CFSTR("display-icon"), productDisplayIconFilePath);
if (!CFDictionaryContainsKey(displayDict, CFSTR("display-icon")) && vendorDisplayIconFilePath)
CFDictionarySetValue(displayDict, CFSTR("display-icon"), vendorDisplayIconFilePath);
if (!CFDictionaryContainsKey(displayDict, CFSTR("display-icon")))
setDictionaryDisplayIconValue(displayDict, vendorIdsDict);
}
}
setDictionaryDisplayIconValue(dict, displayDict);
setDictionaryDisplayResolutionPreviewValues(dict, displayDict);
CFRelease(displayDict);
if (iconDict)
CFRelease(iconDict);
if (builtin)
CFRelease(builtin);
if (vendorDisplayIconFilePath)
CFRelease(vendorDisplayIconFilePath);
if (productDisplayIconFilePath)
CFRelease(productDisplayIconFilePath);
if (modelDisplayIconFilePath)
CFRelease(modelDisplayIconFilePath);
if (productModelDisplayIconFilePath)
CFRelease(productModelDisplayIconFilePath);
CFRelease(modelWithColorString);
CFRelease(modelString);
CFRelease(productIdWithColorString);
CFRelease(productIdString);
CFRelease(vendorIdString);
}
return( dict );
}
static void
EDIDInfo( struct EDID * edid,
IODisplayVendorID * vendor, IODisplayProductID * product,
UInt32 * serialNumber,
uint32_t * manufactureYear, uint32_t * manufactureWeek,
Boolean * isDigital )
{
SInt32 sint;
if (vendor)
*vendor = (edid->vendorProduct[0] << 8) | edid->vendorProduct[1];
if (product)
*product = (edid->vendorProduct[3] << 8) | edid->vendorProduct[2];
if (isDigital)
*isDigital = (0 != (0x80 & edid->displayParams[0]));
if( serialNumber) {
sint = (edid->serialNumber[3] << 24)
| (edid->serialNumber[2] << 16)
| (edid->serialNumber[1] << 8)
| (edid->serialNumber[0]);
if( sint == 0x01010101)
sint = 0;
*serialNumber = sint;
}
if (manufactureYear) *manufactureYear = edid->yearOfManufacture + 1990;
if (manufactureWeek) *manufactureWeek = edid->weekOfManufacture;
}
__private_extern__ Boolean
IODisplayEDIDName( EDID * edid, char * name )
{
char * oname = name;
EDIDDesc * desc;
int i,j;
Boolean ok;
char c;
if( !edid || (edid->version < 1) || (edid->revision < 1))
return( false );
desc = edid->descriptors;
for( i = 0; i < 4; i++, desc++) {
if( desc->general.flag1 || desc->general.flag2 || desc->general.flag3)
continue;
if( 0xfc != desc->general.type)
continue;
for( j = 0; j < (int) sizeof(desc->general.data); j++) {
c = desc->general.data[j];
if( c != 0x0a)
*oname++ = c;
else
break;
}
}
ok = (oname != name);
if( ok)
*oname++ = 0;
return( ok );
}
struct MakeOneLocalContext {
CFBundleRef bdl;
CFMutableDictionaryRef dict;
CFStringRef key;
};
static void MakeOneLocalization( const void * item, void * context )
{
struct MakeOneLocalContext * ctx = (struct MakeOneLocalContext *) context;
CFStringRef value = NULL;
CFDictionaryRef stringTable = NULL;
CFURLRef url;
CFDataRef tableData = NULL;
CFStringRef errStr;
SInt32 errCode;
url = CFBundleCopyResourceURLForLocalization( ctx->bdl,
CFSTR("Localizable"), CFSTR("strings"), NULL, item );
if (url && CFURLCreateDataAndPropertiesFromResource( kCFAllocatorDefault,
url, &tableData, NULL, NULL, &errCode)) {
stringTable = CFPropertyListCreateFromXMLData( kCFAllocatorDefault,
tableData, kCFPropertyListImmutable, &errStr);
if (errStr)
CFRelease( errStr);
CFRelease( tableData);
}
if( url)
CFRelease(url);
if( stringTable)
value = CFDictionaryGetValue(stringTable, ctx->key);
if (!value)
value = ctx->key;
{
SInt32 languageCode, regionCode, scriptCode;
CFStringEncoding stringEncoding;
if( CFBundleGetLocalizationInfoForLocalization( item, &languageCode, ®ionCode,
&scriptCode, &stringEncoding )) {
item = CFBundleCopyLocalizationForLocalizationInfo( languageCode, regionCode,
scriptCode, stringEncoding );
} else
item = CFRetain(item);
}
CFDictionarySetValue( ctx->dict, item, value );
CFRelease( item );
if( stringTable)
CFRelease( stringTable );
}
static void GenerateProductName( CFMutableDictionaryRef dict,
EDID * edid, SInt32 displayType, IOOptionBits options )
{
CFStringRef key;
CFBundleRef bdl;
CFArrayRef localizations;
struct MakeOneLocalContext ctx;
static const char * type2Name[] = {
NULL, NULL, "Color LCD", NULL, "Multiple Scan Display", "Multiple Scan Display", "Multiple Scan Display", "Multiple Scan Display", NULL, "Full-Page Display", "VGA Display", "Television", "Television", NULL, "Color LCD", "Two-Page Display", "Two-Page Display", NULL, NULL, NULL, "Color LCD", NULL, NULL };
key = CFDictionaryGetValue( dict, CFSTR(kDisplayProductName));
if( key) {
if( CFStringGetTypeID() != CFGetTypeID( key ))
return;
CFRetain(key);
}
bdl = (CFBundleRef) CFDictionaryGetValue( dict, CFSTR(kDisplayBundleKey));
if( !key) {
char sbuf[ 128 ];
const char * name = NULL;
if( IODisplayEDIDName(edid, sbuf))
name = sbuf;
else if (edid)
name = "Unknown Display";
else {
if( displayType < (int) (sizeof( type2Name) / sizeof(type2Name[0])))
name = type2Name[displayType];
if( !name)
name = "Unknown Display";
}
key = CFStringCreateWithCString( kCFAllocatorDefault, name,
kCFStringEncodingMacRoman );
if( !key)
return;
}
if( bdl) {
localizations = CFBundleCopyBundleLocalizations( bdl);
if (localizations)
{
ctx.bdl = bdl;
ctx.dict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
ctx.key = key;
if( kIODisplayOnlyPreferredName & options) {
CFArrayRef temp = localizations;
localizations = CFBundleCopyPreferredLocalizationsFromArray( temp );
CFRelease( temp );
}
CFArrayApplyFunction( localizations,
CFRangeMake(0, CFArrayGetCount(localizations)),
&MakeOneLocalization,
&ctx);
CFDictionarySetValue( dict, CFSTR(kDisplayProductName), ctx.dict);
CFRelease( localizations );
CFRelease( ctx.dict );
}
}
CFRelease( key );
}
io_service_t
IODisplayForFramebuffer(
io_service_t framebuffer,
IOOptionBits options __unused )
{
IOReturn kr;
io_iterator_t iter;
io_service_t service = 0;
if( IOObjectConformsTo( framebuffer, "IODisplay"))
{
IOObjectRetain(framebuffer);
return( framebuffer );
}
kr = IORegistryEntryCreateIterator( framebuffer, kIOServicePlane,
kIORegistryIterateRecursively, &iter);
if( kr != kIOReturnSuccess )
return( 0 );
do
{
for( ;
(service = IOIteratorNext( iter));
IOObjectRelease(service)) {
if( IOObjectConformsTo( service, "IODisplay"))
break;
}
}
while (!service && !IOIteratorIsValid(iter) && (IOIteratorReset(iter), true));
IOObjectRelease( iter );
return( service );
}
enum {
kDisplayGestaltBrightnessAffectsGammaMask = (1 << 0),
kDisplayGestaltViewAngleAffectsGammaMask = (1 << 1)
};
static void
IODisplayDictAddValues(const void *key, const void *value, void *context)
{
CFMutableDictionaryRef dict = context;
CFDictionaryAddValue(dict, key, value);
}
static Boolean
IODisplayIsHDMISink(CFMutableDictionaryRef dict)
{
CFDataRef data = 0;
EDID * edid = 0;
CFIndex count;
UInt8 * blocks;
if (! dict) {
return false;
}
data = CFDictionaryGetValue(dict, CFSTR(kIODisplayEDIDKey));
edid = (EDID *) CFDataGetBytePtr(data);
if (! (data && edid)) {
return false;
}
if ((edid->version != 1) || (edid->revision != 3)) {
return false;
}
count = CFDataGetLength(data);
if ((size_t)count <= sizeof(EDID)) {
return false;
}
blocks = (UInt8 *)(edid + 1);
count -= sizeof(EDID);
while (count >= 128) {
UInt8 tag = blocks[0];
if (tag == kExtTagCEA) {
CEA861EXT * ext = (CEA861EXT *)blocks;
IOByteCount offset;
offset = ext->detailedTimingsOffset;
if (offset < 4) {
return false;
}
offset -= 4;
if (0x03 <= ext->version) {
IOByteCount index = 0;
while (index < offset) {
IOByteCount length = (ext->data[index] & 0x1f) + 1;
if (((ext->data[index] & 0xe0) == 0x60) && (length >= 4)) {
if ((ext->data[index + 1] == 0x03) &&
(ext->data[index + 2] == 0x0c) &&
(ext->data[index + 3] == 0x00)) {
return true;
}
}
index += length;
}
}
}
count -= 128;
blocks += 128;
}
return false;
}
#ifndef kIODisplayIsHDMISinkKey
#define kIODisplayIsHDMISinkKey "IODisplayIsHDMISink"
#endif
static CFDictionaryRef
_IODisplayCreateInfoDictionary(
io_service_t framebuffer,
IOOptionBits options )
{
IOReturn kr;
io_service_t service = 0;
Boolean isDigital = false;
CFDataRef data = 0;
CFNumberRef num;
CFMutableDictionaryRef dict = 0;
CFMutableDictionaryRef regDict;
CFMutableDictionaryRef fbRegDict;
CFTypeRef obj;
SInt32 sint;
UInt8 low;
float fnum;
EDID * edid = 0;
IODisplayVendorID vendor = 0;
IODisplayProductID product = 0;
SInt32 displayType = 0;
UInt32 serialNumber = 0;
uint32_t manufactureYear = 0;
uint32_t manufactureWeek = 0;
io_string_t path;
int i;
IODisplayTimingRange displayRange;
if( !(service = IODisplayForFramebuffer( framebuffer, options))) {
dict = CFDictionaryCreateMutable( kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
if( dict)
CFDictionarySetValue( dict, CFSTR(kIODisplayLocationKey), CFSTR("unknown"));
return( dict );
}
do {
regDict = 0;
kr = IORegistryEntryCreateCFProperties( service, ®Dict,
kCFAllocatorDefault, kNilOptions );
if( kr != kIOReturnSuccess)
continue;
num = CFDictionaryGetValue( regDict, CFSTR(kDisplayVendorID) );
if( num)
CFNumberGetValue( num, kCFNumberSInt32Type, &vendor );
num = CFDictionaryGetValue( regDict, CFSTR(kDisplayProductID) );
if( num)
CFNumberGetValue( num, kCFNumberSInt32Type, &product );
num = CFDictionaryGetValue( regDict, CFSTR(kAppleDisplayTypeKey) );
if( num) {
CFNumberGetValue( num, kCFNumberSInt32Type, &displayType );
if( (vendor == kDisplayVendorIDUnknown) && (displayType == 10))
product = kDisplayProductIDGeneric;
}
data = CFDictionaryGetValue( regDict, CFSTR(kIODisplayEDIDKey) );
#if SPOOF_EDID
#warning ****************
#warning ** SPOOF_EDID **
#warning ****************
if (data)
{
EDIDInfo( (EDID *) CFDataGetBytePtr(data), &vendor, &product, NULL, NULL, NULL, NULL);
if (0x10ac == vendor)
{
data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
spoofEDID, sizeof(spoofEDID), kCFAllocatorNull);
}
vendor = product = 0;
}
#endif
if( !data)
continue;
edid = (EDID *) CFDataGetBytePtr( data );
if( vendor && product)
EDIDInfo( edid, 0, 0, &serialNumber, &manufactureYear, &manufactureWeek, &isDigital );
else
EDIDInfo( edid, &vendor, &product, &serialNumber, &manufactureYear, &manufactureWeek, &isDigital );
} while( false );
if( !vendor && !product) {
vendor = kDisplayVendorIDUnknown;
product = kDisplayProductIDGeneric;
}
dict = IODisplayCreateOverrides( framebuffer, options, vendor, product,
serialNumber, manufactureYear, manufactureWeek,
isDigital );
#define makeInt( key, value ) \
num = CFNumberCreate( kCFAllocatorDefault, kCFNumberSInt32Type, &value ); \
CFDictionaryAddValue( dict, key, num ); \
CFRelease( num );
#define addFloat( key ) \
num = CFNumberCreate( kCFAllocatorDefault, kCFNumberFloatType, &fnum ); \
CFDictionaryAddValue( dict, key, num ); \
CFRelease( num );
do {
if( !dict)
continue;
makeInt( CFSTR( kDisplayVendorID ), vendor );
makeInt( CFSTR( kDisplayProductID ), product );
if( serialNumber) {
makeInt( CFSTR( kDisplaySerialNumber ), serialNumber );
}
kr = IORegistryEntryGetPath( service, kIOServicePlane, path );
if( KERN_SUCCESS == kr) {
CFStringRef string;
string = CFStringCreateWithCString( kCFAllocatorDefault, path,
kCFStringEncodingMacRoman );
if( string) {
CFDictionaryAddValue( dict, CFSTR(kIODisplayLocationKey), string);
CFRelease(string);
}
}
if ((kIODisplayNoProductName | kIODisplayMatchingInfo) & options)
CFDictionaryRemoveValue( dict, CFSTR(kDisplayProductName) );
if( options & kIODisplayMatchingInfo)
continue;
if (data)
{
CFDictionaryAddValue(dict, CFSTR(kIODisplayEDIDKey), data);
CFDictionaryAddValue(dict, CFSTR(kIODisplayEDIDOriginalKey), data);
}
data = CFDictionaryGetValue(dict, CFSTR(kIODisplayEDIDKey));
if (data)
edid = (EDID *) CFDataGetBytePtr(data);
else
edid = 0;
if (regDict)
{
obj = CFDictionaryGetValue( regDict, CFSTR(kIODisplayConnectFlagsKey) );
if( obj)
CFDictionarySetValue( dict, CFSTR(kIODisplayConnectFlagsKey), obj );
obj = CFDictionaryGetValue( regDict, CFSTR(kIODisplayPrefKeyKey) );
if( obj)
CFDictionarySetValue( dict, CFSTR(kIODisplayPrefKeyKey), obj );
CFDictionaryRef attrDict;
attrDict = CFDictionaryGetValue(regDict, CFSTR(kIODisplayAttributesKey));
if (attrDict && (CFDictionaryGetTypeID() == CFGetTypeID(attrDict)))
CFDictionaryApplyFunction(attrDict, &IODisplayDictAddValues, dict);
}
if( IOObjectConformsTo( service, "IOBacklightDisplay"))
CFDictionarySetValue( dict, CFSTR(kIODisplayHasBacklightKey), kCFBooleanTrue );
kr = IORegistryEntryCreateCFProperties(framebuffer, &fbRegDict,
kCFAllocatorDefault, kNilOptions );
if (kIOReturnSuccess == kr)
{
if( (obj = CFDictionaryGetValue(fbRegDict, CFSTR(kIOFBTransformKey))))
{
CFNumberGetValue(obj, kCFNumberSInt32Type, &sint);
sint = (sint & kIOFBRotateFlags) | ((sint >> 4) & kIOFBRotateFlags);
makeInt( CFSTR(kIOFBTransformKey), sint );
}
if( (obj = CFDictionaryGetValue(fbRegDict, CFSTR("graphic-options"))))
CFDictionaryAddValue(dict, CFSTR("graphic-options"), obj);
CFRelease(fbRegDict);
}
data = CFDictionaryGetValue( dict, CFSTR("dmdg") );
if( data)
sint = OSReadBigInt32((void *) CFDataGetBytePtr(data), 0);
else
sint = kDisplayGestaltBrightnessAffectsGammaMask;
if( kDisplayGestaltBrightnessAffectsGammaMask & sint)
CFDictionaryAddValue( dict, CFSTR(kDisplayBrightnessAffectsGamma), kCFBooleanTrue );
if( kDisplayGestaltViewAngleAffectsGammaMask & sint)
CFDictionaryAddValue( dict, CFSTR(kDisplayViewAngleAffectsGamma), kCFBooleanTrue );
if (!(kIODisplayNoProductName & options))
GenerateProductName( dict, edid, displayType, options );
if( !edid)
continue;
if( 0x80 & edid->displayParams[0]) {
CFDictionarySetValue( dict, CFSTR(kIODisplayIsDigitalKey), kCFBooleanTrue );
if( kDisplayAppleVendorID == vendor) {
CFDictionaryAddValue( dict, CFSTR(kDisplayFixedPixelFormat), kCFBooleanTrue );
sint = kDisplaySubPixelLayoutRGB;
makeInt( CFSTR( kDisplaySubPixelLayout ), sint );
CFDictionaryRemoveValue( dict, CFSTR(kDisplayBrightnessAffectsGamma) );
CFDictionarySetValue( dict, CFSTR(kDisplayViewAngleAffectsGamma), kCFBooleanTrue );
}
}
for( i = 0; i < 4; i++ ) {
if( EDIDDescToDisplayTimingRangeRec( edid,
&edid->descriptors[i].general,
&displayRange )) {
if( !CFDictionaryGetValue( dict, CFSTR("drng"))) {
data = CFDataCreate( kCFAllocatorDefault,
(UInt8 *) &edid->descriptors[i].general, sizeof(EDIDGeneralDesc));
if( data) {
CFDictionarySetValue(dict, CFSTR("drng"), data);
CFRelease(data);
}
}
if( !CFDictionaryGetValue( dict, CFSTR("trng"))) {
data = CFDataCreate( kCFAllocatorDefault,
(UInt8 *) &displayRange, sizeof(displayRange));
if( data) {
CFDictionarySetValue(dict, CFSTR("trng"), data);
CFRelease(data);
}
}
break;
}
}
sint = edid->weekOfManufacture;
makeInt( CFSTR( kDisplayWeekOfManufacture ), sint );
sint = edid->yearOfManufacture + 1990;
makeInt( CFSTR( kDisplayYearOfManufacture ), sint );
sint = edid->displayParams[1] * 10;
makeInt( CFSTR( kDisplayHorizontalImageSize ), sint );
sint = edid->displayParams[2] * 10;
makeInt( CFSTR( kDisplayVerticalImageSize ), sint );
low = edid->colorCharacteristics[0];
fnum = (edid->colorCharacteristics[2] << 2) | ((low >> 6) & 3);
fnum /= (1 << 10);
addFloat( CFSTR( kDisplayRedPointX ) );
fnum = (edid->colorCharacteristics[3] << 2) | ((low >> 4) & 3);
fnum /= (1 << 10);
addFloat( CFSTR( kDisplayRedPointY ) );
fnum = (edid->colorCharacteristics[4] << 2) | ((low >> 2) & 3);
fnum /= (1 << 10);
addFloat( CFSTR( kDisplayGreenPointX ) );
fnum = (edid->colorCharacteristics[5] << 2) | ((low >> 0) & 3);
fnum /= (1 << 10);
addFloat( CFSTR( kDisplayGreenPointY ) );
low = edid->colorCharacteristics[1];
fnum = (edid->colorCharacteristics[6] << 2) | ((low >> 6) & 3);
fnum /= (1 << 10);
addFloat( CFSTR( kDisplayBluePointX ) );
fnum = (edid->colorCharacteristics[7] << 2) | ((low >> 4) & 3);
fnum /= (1 << 10);
addFloat( CFSTR( kDisplayBluePointY ) );
fnum = (edid->colorCharacteristics[8] << 2) | ((low >> 2) & 3);
fnum /= (1 << 10);
addFloat( CFSTR( kDisplayWhitePointX ) );
fnum = (edid->colorCharacteristics[9] << 2) | ((low >> 0) & 3);
fnum /= (1 << 10);
addFloat( CFSTR( kDisplayWhitePointY ) );
fnum = edid->displayParams[3];
fnum = (fnum + 100.0) / 100.0;
addFloat( CFSTR( kDisplayWhiteGamma ) );
if (IODisplayIsHDMISink(dict)) {
CFDictionarySetValue(dict, CFSTR(kIODisplayIsHDMISinkKey), kCFBooleanTrue);
} else {
CFDictionarySetValue(dict, CFSTR(kIODisplayIsHDMISinkKey), kCFBooleanFalse);
}
} while( false );
if (service)
IOObjectRelease(service);
if( regDict)
CFRelease( regDict );
return( dict );
}
IOReturn
IODisplayCopyParameters(
io_service_t service,
IOOptionBits options,
CFDictionaryRef * params )
{
if( (service = IODisplayForFramebuffer( service, options)))
{
*params = IORegistryEntryCreateCFProperty( service, CFSTR(kIODisplayParametersKey),
kCFAllocatorDefault, kNilOptions );
IOObjectRelease(service);
}
else
*params = 0;
return( *params ? kIOReturnSuccess : kIOReturnUnsupported );
}
IOReturn
IODisplayCopyFloatParameters(
io_service_t service __unused,
IOOptionBits options __unused,
CFDictionaryRef * params __unused )
{
return( kIOReturnUnsupported );
}
IOReturn
IODisplayGetIntegerRangeParameter(
io_service_t service,
IOOptionBits options,
CFStringRef parameterName,
SInt32 * value,
SInt32 * min,
SInt32 * max )
{
IOReturn err;
CFDictionaryRef params;
CFDictionaryRef param;
CFNumberRef num;
#if DEBUGPARAMS
const char * cStr = 0;
if( (cStr = CFStringGetCStringPtr( parameterName, kCFStringEncodingMacRoman))
&& (cStr = getenv(cStr)))
parameterName = CFStringCreateWithCString( kCFAllocatorDefault, cStr,
kCFStringEncodingMacRoman );
#endif
do {
err = IODisplayCopyParameters( service, options, ¶ms );
if( err != kIOReturnSuccess)
continue;
param = CFDictionaryGetValue( params, parameterName );
if( !param) {
err = kIOReturnUnsupported;
continue;
}
if( value && (num = CFDictionaryGetValue( param, CFSTR(kIODisplayValueKey))))
CFNumberGetValue( num, kCFNumberSInt32Type, value );
if( min && (num = CFDictionaryGetValue( param, CFSTR(kIODisplayMinValueKey))))
CFNumberGetValue( num, kCFNumberSInt32Type, min );
if( max && (num = CFDictionaryGetValue( param, CFSTR(kIODisplayMaxValueKey))))
CFNumberGetValue( num, kCFNumberSInt32Type, max );
} while( false );
if( params)
CFRelease(params);
#if DEBUGPARAMS
if( cStr)
CFRelease(parameterName);
#endif
return( err );
}
IOReturn
IODisplayGetFloatParameter(
io_service_t service,
IOOptionBits options,
CFStringRef parameterName,
float * value )
{
IOReturn err;
SInt32 ivalue, min, max;
err = IODisplayGetIntegerRangeParameter( service, options, parameterName,
&ivalue, &min, &max );
if( err)
return( err);
if( min == max)
*value = 0;
else
*value = (((float) ivalue) - ((float) min)) / (((float) max) - ((float) min));
return( err );
}
IOReturn
IODisplaySetParameters(
io_service_t service,
IOOptionBits options,
CFDictionaryRef params )
{
IOReturn err;
if( !(service = IODisplayForFramebuffer( service, options)))
return( kIOReturnUnsupported );
err = IORegistryEntrySetCFProperties( service, params );
IOObjectRelease(service);
return( err );
}
IOReturn
IODisplaySetIntegerParameter(
io_service_t service,
IOOptionBits options __unused,
CFStringRef parameterName,
SInt32 value )
{
IOReturn err;
CFDictionaryRef dict;
CFNumberRef num;
#if DEBUGPARAMS
const char * cStr;
if( (cStr = CFStringGetCStringPtr( parameterName, kCFStringEncodingMacRoman))
&& (cStr = getenv(cStr)))
parameterName = CFStringCreateWithCString( kCFAllocatorDefault, cStr,
kCFStringEncodingMacRoman );
#endif
num = CFNumberCreate( kCFAllocatorDefault, kCFNumberSInt32Type, &value );
if( !num)
return( kIOReturnNoMemory );
dict = CFDictionaryCreate( kCFAllocatorDefault,
(const void **) ¶meterName, (const void **) &num, 1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
CFRelease(num);
if( !dict)
return( kIOReturnNoMemory );
err = IODisplaySetParameters( service, kNilOptions, dict );
CFRelease(dict);
#if DEBUGPARAMS
if( cStr)
CFRelease(parameterName);
#endif
return( err );
}
IOReturn
IODisplaySetFloatParameter(
io_service_t service,
IOOptionBits options,
CFStringRef parameterName,
float value )
{
IOReturn err;
SInt32 ivalue, min, max;
err = IODisplayGetIntegerRangeParameter( service, options, parameterName,
NULL, &min, &max );
if( err)
return( err);
ivalue = roundf((value * (((float) max) - ((float) min)) + ((float) min)));
err = IODisplaySetIntegerParameter( service, options, parameterName, ivalue );
return( err );
}
IOReturn
IODisplayCommitParameters(
io_service_t service,
IOOptionBits options )
{
return( IODisplaySetIntegerParameter( service, options,
CFSTR(kIODisplayParametersCommitKey), 1));
}
#undef IOCreateDisplayInfoDictionary
CFDictionaryRef
IOCreateDisplayInfoDictionary(
io_service_t framebuffer,
IOOptionBits options )
{
return( _IODisplayCreateInfoDictionary(framebuffer, options));
}
CFDictionaryRef
IODisplayCreateInfoDictionary(
io_service_t framebuffer,
IOOptionBits options )
{
return( _IODisplayCreateInfoDictionary(framebuffer, options));
}
SInt32
IODisplayMatchDictionaries(
CFDictionaryRef matching1,
CFDictionaryRef matching2,
IOOptionBits options __unused )
{
CFNumberRef num1, num2;
CFStringRef str1, str2;
SInt32 matches = 0;
if( !matching1 || !matching2)
return( -1 );
do {
num1 = CFDictionaryGetValue( matching1, CFSTR(kDisplayVendorID) );
num2 = CFDictionaryGetValue( matching2, CFSTR(kDisplayVendorID) );
if( !num1 || !num2)
continue;
if( !CFEqual( num1, num2))
continue;
num1 = CFDictionaryGetValue( matching1, CFSTR(kDisplayProductID) );
num2 = CFDictionaryGetValue( matching2, CFSTR(kDisplayProductID) );
if( !num1 || !num2)
continue;
if( !CFEqual( num1, num2))
continue;
num1 = CFDictionaryGetValue( matching1, CFSTR(kDisplaySerialNumber) );
num2 = CFDictionaryGetValue( matching2, CFSTR(kDisplaySerialNumber) );
if( num1 && num2 && (!CFEqual( num1, num2)))
continue;
str1 = CFDictionaryGetValue( matching1, CFSTR(kIODisplayLocationKey) );
str2 = CFDictionaryGetValue( matching2, CFSTR(kIODisplayLocationKey) );
if( str1 && str2 && (!CFEqual( str1, str2)))
continue;
matches = 1000;
} while( false );
return( matches );
}