darling-IOGraphics/tools/modelist.c
2020-07-03 22:03:44 -04:00

711 lines
23 KiB
C

/*cc -o /tmp/modelist modelist.c -framework IOKit -framework ApplicationServices -Wall -g
*/
#include <mach/mach.h>
#include <mach/thread_switch.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <CoreFoundation/CoreFoundation.h>
#include <ApplicationServices/ApplicationServices.h>
#include <IOKit/IOKitLib.h>
#include <libkern/OSByteOrder.h>
#include <IOKit/IOMessage.h>
#include <IOKit/IOCFURLAccess.h>
#include <IOKit/graphics/IOGraphicsLib.h>
#include <IOKit/graphics/IOGraphicsLibPrivate.h>
#include <assert.h>
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
struct IOFBModeList
{
CFBundleRef bundle;
io_service_t framebuffer;
CFMutableDictionaryRef kernelInfo;
CFMutableDictionaryRef modes;
CFMutableArrayRef modesArray;
CFMutableDictionaryRef overrides;
Boolean suppressRefresh;
Boolean detailedRefresh;
IOItemCount safemodeCount;
IOItemCount builtinCount;
IOItemCount televisionCount;
IOItemCount simulscanCount;
IOItemCount maxRefreshDigits;
CFMutableArrayRef refreshNames;
Boolean refreshList;
uint32_t significantFlags;
};
typedef struct IOFBModeList * IOFBModeListRef;
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
struct ModeInfo
{
IODisplayModeInformation * info;
UInt32 index;
UInt32 cgIndex;
SInt32 numRefresh;
SInt32 numSafe;
SInt32 numPreset;
UInt32 digits;
Boolean notReco;
};
typedef struct ModeInfo ModeInfo;
enum
{
kCompareRefresh = 0x00000001,
kCompareAll = 0xffffffff,
};
static int
CompareModes(IOFBModeListRef modeListRef,
IODisplayModeInformation * left, IODisplayModeInformation * right,
IOOptionBits compare)
{
UInt32 leftFlags, rightFlags, differFlags;
if (!left)
return (1);
else if (!right)
return (-1);
leftFlags = left->flags;
rightFlags = right->flags;
differFlags = leftFlags ^ rightFlags;
if (modeListRef->simulscanCount && (kDisplayModeSimulscanFlag & differFlags))
{
if (kDisplayModeSimulscanFlag & leftFlags)
return (1);
else
return (-1);
}
if (modeListRef->builtinCount && (kDisplayModeBuiltInFlag & differFlags))
{
if (kDisplayModeBuiltInFlag & leftFlags)
return (1);
else
return (-1);
}
if (kDisplayModeTelevisionFlag & leftFlags & rightFlags)
{
// both TV, order ntsc first
if ((left->refreshRate < 55*65536) && (right->refreshRate > 55*65536))
return (1);
else if ((left->refreshRate > 55*65536) && (right->refreshRate < 55*65536))
return (-1);
}
if (left->nominalWidth > right->nominalWidth)
return (1);
else if (left->nominalWidth != right->nominalWidth)
return (-1);
if (left->nominalHeight > right->nominalHeight)
return (1);
else if (left->nominalHeight != right->nominalHeight)
return (-1);
if (kDisplayModeStretchedFlag & differFlags)
{
if (kDisplayModeStretchedFlag & leftFlags)
return (1);
else
return (-1);
}
if (kDisplayModeInterlacedFlag & differFlags)
{
if (kDisplayModeInterlacedFlag & leftFlags)
return (1);
else
return (-1);
}
if (compare & kCompareRefresh)
{
if (left->refreshRate > right->refreshRate)
return (1);
else if (left->refreshRate != right->refreshRate)
return (-1);
}
return (0);
}
static int
qsort_cmp(void * ref, const void * _left, const void * _right)
{
IOFBModeListRef modeListRef = (IOFBModeListRef) ref;
IODisplayModeInformation * left = ((ModeInfo *) _left)->info;
IODisplayModeInformation * right = ((ModeInfo *) _right)->info;
return (CompareModes(modeListRef, left, right, kCompareAll));
}
#define k256ColorString "256 Colors"
#define kThousandsString "Thousands"
#define kMillionsString "Millions"
#define kTrillionsString "Trillions"
#define kPALString "PAL"
#define kNTSCString "NTSC"
#define kNoRefreshRateString "n/a"
#define kStretchedString "stretched"
#define kSimulscanString "simulscan"
#define kInterlacedString "interlaced"
#define kExternalString "external"
#define kRefreshString "%%.%ldf Hertz"
#define kTVRefreshString "%%.%ldf Hertz (%%@)"
#define kHxVString "%d x %d"
#define kHxVpString "%d x %dp"
#define kHxViString "%d x %di"
#define kNoHertz0FlagString "%@"
#define kNoHertz1FlagString "%@ (%@)"
#define kNoHertz2FlagString "%@ (%@, %@)"
#define kNoHertz3FlagString "%@ (%@, %@, %@)"
#define kNoHertz4FlagString "%@ (%@, %@, %@, %@)"
#define kNoHertz5FlagString "%@ (%@, %@, %@, %@, %@)"
#define kHertz0FlagString "%%@, %%.%ldf Hz"
#define kHertz1FlagString "%%@, %%.%ldf Hz (%%@)"
#define kHertz2FlagString "%%@, %%.%ldf Hz (%%@, %%@)"
#define kHertz3FlagString "%%@, %%.%ldf Hz (%%@, %%@, %%@)"
#define kHertz4FlagString "%%@, %%.%ldf Hz (%%@, %%@, %%@, %%@)"
#define kHertz5FlagString "%%@, %%.%ldf Hz (%%@, %%@, %%@, %%@, %%@)"
CFStringRef
TimingName(IOFBModeListRef modeListRef, IODisplayModeInformation * info, CFIndex * refRateIndex)
{
CFStringRef formatStr, formatStr2, refStr, resStr, finalStr;
CFStringRef palNTSCStr, stretchedStr, simulscanStr, interlacedStr, externalStr;
uint32_t flags, flagCount;
CFStringRef flagStrs[5] = { 0 };
CFIndex k, count;
float refRate = info->refreshRate / 65536.0;
flagCount = 0;
if (kDisplayModeTelevisionFlag & info->flags)
{
palNTSCStr = refRate < 55.0 ? CFSTR(kPALString) : CFSTR(kNTSCString);
palNTSCStr = CFBundleCopyLocalizedString(modeListRef->bundle, palNTSCStr, palNTSCStr, 0);
}
else
palNTSCStr = 0;
stretchedStr = CFBundleCopyLocalizedString(modeListRef->bundle, CFSTR(kStretchedString), CFSTR(kStretchedString), 0);
simulscanStr = CFBundleCopyLocalizedString(modeListRef->bundle, CFSTR(kSimulscanString), CFSTR(kSimulscanString), 0);
interlacedStr = CFBundleCopyLocalizedString(modeListRef->bundle, CFSTR(kInterlacedString), CFSTR(kInterlacedString), 0);
externalStr = CFBundleCopyLocalizedString(modeListRef->bundle, CFSTR(kExternalString), CFSTR(kExternalString), 0);
if (modeListRef->refreshList)
{
if (modeListRef->suppressRefresh)
{
refStr = CFSTR(kNoRefreshRateString);
refStr = CFBundleCopyLocalizedString(modeListRef->bundle, refStr, refStr, 0);
}
else
{
if (kDisplayModeTelevisionFlag & info->flags)
formatStr = CFSTR(kTVRefreshString);
else
formatStr = CFSTR(kRefreshString);
formatStr = CFBundleCopyLocalizedString(modeListRef->bundle, formatStr, formatStr, 0);
formatStr2 = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
formatStr,
modeListRef->maxRefreshDigits);
refStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
formatStr2, refRate,
palNTSCStr );
CFRelease(formatStr);
}
count = CFArrayGetCount(modeListRef->refreshNames);
for (k = 0;
(k < count) && !CFEqual(refStr, CFArrayGetValueAtIndex(modeListRef->refreshNames, k));
k++) {}
if (k == count)
CFArrayAppendValue(modeListRef->refreshNames, refStr);
CFRelease(refStr);
if (refRateIndex)
*refRateIndex = k;
}
flags = modeListRef->significantFlags & info->flags;
if (!modeListRef->refreshList && (kDisplayModeTelevisionFlag & info->flags))
flagStrs[flagCount++] = palNTSCStr;
if (flags & kDisplayModeStretchedFlag)
flagStrs[flagCount++] = stretchedStr;
if (flags & kDisplayModeSimulscanFlag)
flagStrs[flagCount++] = simulscanStr;
if ((kDisplayModeBuiltInFlag & modeListRef->significantFlags)
&& !(flags & kDisplayModeBuiltInFlag))
flagStrs[flagCount++] = externalStr;
if (kDisplayModeInterlacedFlag ==
(flags & (kDisplayModeInterlacedFlag | kDisplayModeTelevisionFlag)))
flagStrs[flagCount++] = interlacedStr;
resStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
CFSTR(kHxVString),
info->nominalWidth, info->nominalHeight);
if (modeListRef->refreshList || modeListRef->suppressRefresh)
{
switch (flagCount)
{
case 0: formatStr = CFSTR(kNoHertz0FlagString); break;
case 1: formatStr = CFSTR(kNoHertz1FlagString); break;
case 2: formatStr = CFSTR(kNoHertz2FlagString); break;
case 3: formatStr = CFSTR(kNoHertz3FlagString); break;
case 4: formatStr = CFSTR(kNoHertz4FlagString); break;
default:
case 5: formatStr = CFSTR(kNoHertz5FlagString); break;
}
formatStr = CFBundleCopyLocalizedString(modeListRef->bundle, formatStr, formatStr, 0);
finalStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
formatStr,
resStr,
flagStrs[0],
flagStrs[1],
flagStrs[2],
flagStrs[3],
flagStrs[4]);
}
else
{
switch (flagCount)
{
case 0: formatStr2 = CFSTR(kHertz0FlagString); break;
case 1: formatStr2 = CFSTR(kHertz1FlagString); break;
case 2: formatStr2 = CFSTR(kHertz2FlagString); break;
case 3: formatStr2 = CFSTR(kHertz3FlagString); break;
case 4: formatStr2 = CFSTR(kHertz4FlagString); break;
default:
case 5: formatStr2 = CFSTR(kHertz5FlagString); break;
}
formatStr2 = CFBundleCopyLocalizedString(modeListRef->bundle, formatStr2, formatStr2, 0);
formatStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
formatStr2,
modeListRef->maxRefreshDigits);
CFRelease(formatStr2);
finalStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL,
formatStr,
resStr,
refRate,
flagStrs[0],
flagStrs[1],
flagStrs[2],
flagStrs[3],
flagStrs[4]);
}
CFRelease(formatStr);
if (palNTSCStr)
CFRelease(palNTSCStr);
if (stretchedStr)
CFRelease(stretchedStr);
if (simulscanStr)
CFRelease(simulscanStr);
if (interlacedStr)
CFRelease(interlacedStr);
if (externalStr)
CFRelease(externalStr);
return (finalStr);
}
kern_return_t
ModeList(IOFBModeListRef modeListRef, Boolean reco)
{
CFMutableDictionaryRef dict;
CFMutableArrayRef array;
CFDataRef data;
SInt32 i, j, k;
CFIndex modeCount, newCount, cgIndex = 0;
IODisplayModeInformation * info;
ModeInfo * modeArray;
do
{
dict = CFDictionaryCreateMutable( kCFAllocatorDefault, (CFIndex) 0,
(CFDictionaryKeyCallBacks *) 0,
&kCFTypeDictionaryValueCallBacks );
modeListRef->modes = dict;
dict = (CFMutableDictionaryRef) IORegistryEntryCreateCFProperty(
modeListRef->framebuffer,
CFSTR(kIOFBConfigKey),
kCFAllocatorDefault, kNilOptions);
if (!dict)
break;
array = (CFMutableArrayRef) CFDictionaryGetValue(dict, CFSTR(kIOFBModesKey));
if (!array)
break;
// pick up existing config
modeListRef->kernelInfo = dict;
CFRetain(array);
modeListRef->modesArray = array;
modeListRef->suppressRefresh = (0 != CFDictionaryGetValue(dict, CFSTR("IOFB0Hz")));
modeListRef->detailedRefresh = (0 != CFDictionaryGetValue(dict, CFSTR("IOFBmHz")));
modeCount = CFArrayGetCount( modeListRef->modesArray );
newCount = modeCount;
modeArray = calloc(modeCount, sizeof(ModeInfo));
for( i = 0; i < modeCount; i++ )
{
const void * key;
CFNumberRef num;
dict = (CFMutableDictionaryRef) CFArrayGetValueAtIndex( modeListRef->modesArray, i );
num = CFDictionaryGetValue( dict, CFSTR(kIOFBModeIDKey) );
CFNumberGetValue( num, kCFNumberSInt32Type, (SInt32 *) &key );
CFDictionarySetValue( modeListRef->modes, key, dict );
if (!dict)
break;
data = CFDictionaryGetValue(dict, CFSTR(kIOFBModeDMKey));
if (!data)
break;
info = (IODisplayModeInformation *) CFDataGetBytePtr(data);
// if (info->flags & kDisplayModeNeverShowFlag)
// continue;
modeArray[i].index = i;
modeArray[i].info = info;
modeArray[i].cgIndex = cgIndex;
cgIndex += info->maxDepthIndex + 1;
#if 0
printf("%ld: %ld x %ld @ %f Hz\n", modeArray[i].cgIndex,
info->nominalWidth, info->nominalHeight,
info->refreshRate / 65536.0);
#endif
if (info->flags & kDisplayModeSafeFlag)
modeListRef->safemodeCount++;
// if (info->flags & kDisplayModeAlwaysShowFlag)
// if (info->flags & kDisplayModeDefaultFlag)
if (info->flags & kDisplayModeSimulscanFlag)
modeListRef->simulscanCount++;
if (info->flags & kDisplayModeBuiltInFlag)
modeListRef->builtinCount++;
if (info->flags & kDisplayModeTelevisionFlag)
modeListRef->televisionCount++;
}
qsort_r(modeArray, modeCount, sizeof(modeArray[0]), modeListRef, &qsort_cmp);
#define discard() { modeArray[i].notReco = true; continue; }
// group refresh rates
{
UInt32 lastSame;
lastSame = 0;
for (i = 0; i < modeCount; i++)
{
if (i != lastSame)
{
if (0 == CompareModes(modeListRef, modeArray[lastSame].info, modeArray[i].info, 0))
{
// number that follow (total - 1)
modeArray[lastSame].numRefresh++;
modeArray[i].numRefresh = lastSame - i;
}
else
lastSame = i;
}
if (modeArray[i].info->flags & kDisplayModeSafeFlag)
modeArray[lastSame].numSafe++;
if (!(modeArray[i].info->flags & kDisplayModeNotPresetFlag))
modeArray[lastSame].numPreset++;
}
}
if (reco)
{
// prune with safety / not preset
for( i = 0; i < modeCount; i++ )
{
info = modeArray[i].info;
do
{
// if (modeListRef->safemodeCount
// && !(info->flags & (kDisplayModeSafeFlag | kDisplayModeTelevisionFlag)))
// discard();
// if (info->flags & kDisplayModeNotPresetFlag)
// discard();
}
while (false);
}
}
// prune refresh rates
if (reco)
{
for( i = 0; i < modeCount; i += modeArray[i].numRefresh + 1 )
{
info = modeArray[i].info;
do
{
if (info->flags & kDisplayModeTelevisionFlag)
continue;
// if (modeListRef->safemodeCount)
{
// keep only highest
Boolean haveHighestReco = false;
for (j = i + modeArray[i].numRefresh; j >= i; j--)
{
if (haveHighestReco)
modeArray[j].notReco = true;
else {
if ((!modeArray[i].numPreset)
|| (!(modeArray[j].info->flags & kDisplayModeNotPresetFlag)))
{
uint32_t target;
target = (modeArray[j].info->flags & kDisplayModeSafeFlag)
? (86 << 16) : (86 << 16);
haveHighestReco = !modeArray[j].notReco
&& (modeArray[j].info->refreshRate < target);
}
if (!haveHighestReco)
modeArray[j].notReco = true;
}
}
continue;
}
}
while (false);
}
}
// <reco/>
// unique refresh rates
for( i = 0; i < modeCount; i += modeArray[i].numRefresh + 1 )
{
float ref1, ref2, mult;
modeArray[i].digits = 0; //modeListRef->maxRefreshDigits;
mult = 1.0;
for (j = i; j < (i + modeArray[i].numRefresh + 1); j++)
{
if (modeArray[j].notReco)
continue;
ref1 = modeArray[j].info->refreshRate / 65536.0;
for (k = i; k < (i + modeArray[i].numRefresh + 1); k++)
{
if (k == j)
continue;
if (modeArray[k].notReco)
continue;
ref2 = modeArray[k].info->refreshRate / 65536.0;
while (modeArray[i].digits < 5)
{
//if (modeArray[i].digits)
// printf("-----> %f, %f, %f, %f\n", ref1, ref2, roundf(ref1 * mult), roundf(ref2 * mult));
if (roundf(ref1 * mult) != roundf(ref2 * mult))
break;
modeArray[i].digits++;
mult *= 10.0;
}
}
}
if (modeArray[i].digits > modeListRef->maxRefreshDigits)
modeListRef->maxRefreshDigits = modeArray[i].digits;
}
// <unique/>
printf("\nOut:\n");
modeListRef->refreshNames = CFArrayCreateMutable( kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks );
modeListRef->refreshList = false;
do
{
modeListRef->significantFlags =
1 * kDisplayModeStretchedFlag
| 1 * kDisplayModeSimulscanFlag
| 0 * kDisplayModeBuiltInFlag;
printf("-------------\n");
for (i = 0; i < modeCount; i += modeArray[i].numRefresh + 1)
{
CFStringRef name;
CFIndex idx;
CFIndex mask = 0;
info = modeArray[i].info;
#if 0
printf("%ld x %ld \n",
info->nominalWidth, info->nominalHeight);
printf("numRefresh %d, numSafe %d, numPreset %d\n",
modeArray[i].numRefresh,
modeArray[i].numSafe,
modeArray[i].numPreset);
#endif
for (j = i; j < (i + modeArray[i].numRefresh + 1); j++)
{
info = modeArray[j].info;
name = TimingName(modeListRef, info, &idx);
if (!modeListRef->refreshList || (j == i))
{
if (!modeListRef->refreshList && modeArray[j].notReco)
printf("*");
printf("%s", CFStringGetCStringPtr(name, kCFStringEncodingMacRoman));
if (!modeListRef->refreshList)
printf("\n");
}
if (modeListRef->refreshList)
{
printf(" %c[%ld]", modeArray[j].notReco ? '*' : ' ', idx);
mask |= (1 << idx);
}
CFRelease(name);
#if 0
printf(" %s%s%s",
(info->flags & kDisplayModeSafeFlag) ? "safe " : "",
(info->flags & kDisplayModeDefaultFlag) ? "default " : "",
(info->flags & kDisplayModeNotPresetFlag) ? "notpreset " : ""
);
#endif
}
if (modeListRef->refreshList)
printf("\n");
}
if (modeListRef->refreshList)
{
CFShow(modeListRef->refreshNames);
break;
}
modeListRef->refreshList = true;
}
while (true);
}
while (false);
// CFShow(modeListRef->modesArray);
return( kIOReturnSuccess );
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int main( int argc, char * argv[] )
{
kern_return_t kr;
io_string_t path;
CFURLRef url;
CFIndex i;
CGError err;
CGDisplayCount max;
CGDirectDisplayID displayIDs[8];
err = CGGetOnlineDisplayList(8, displayIDs, &max);
if(err != kCGErrorSuccess)
exit(1);
if(max > 8)
max = 8;
for(i = 0; i < max; i++ )
{
struct IOFBModeList _xxx = { 0 };
IOFBModeListRef modeListRef = &_xxx;
modeListRef->framebuffer = CGDisplayIOServicePort(displayIDs[i]);
url = CFURLCreateWithFileSystemPath(
kCFAllocatorDefault,
CFSTR("/System/Library/Frameworks/IOKit.framework"),
kCFURLPOSIXPathStyle, true);
if (url)
modeListRef->bundle = CFBundleCreate(kCFAllocatorDefault, url);
if (!modeListRef->bundle) exit(1);
kr = IORegistryEntryGetPath(modeListRef->framebuffer, kIOServicePlane, path);
assert( KERN_SUCCESS == kr );
printf("\nDisplay %#x: %s\n", displayIDs[i], path);
ModeList(modeListRef, true || true);
}
exit(0);
return(0);
}