1/*cc -o /tmp/modelist modelist.c -framework IOKit -framework ApplicationServices -Wall -g 2*/ 3 4#include <mach/mach.h> 5#include <mach/thread_switch.h> 6#include <sys/file.h> 7#include <sys/stat.h> 8#include <unistd.h> 9#include <string.h> 10#include <stdlib.h> 11 12#include <CoreFoundation/CoreFoundation.h> 13#include <ApplicationServices/ApplicationServices.h> 14 15#include <IOKit/IOKitLib.h> 16#include <libkern/OSByteOrder.h> 17#include <IOKit/IOMessage.h> 18#include <IOKit/IOCFURLAccess.h> 19#include <IOKit/graphics/IOGraphicsLib.h> 20#include <IOKit/graphics/IOGraphicsLibPrivate.h> 21 22#include <assert.h> 23 24/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 25 26struct IOFBModeList 27{ 28 CFBundleRef bundle; 29 io_service_t framebuffer; 30 31 CFMutableDictionaryRef kernelInfo; 32 CFMutableDictionaryRef modes; 33 CFMutableArrayRef modesArray; 34 CFMutableDictionaryRef overrides; 35 36 Boolean suppressRefresh; 37 Boolean detailedRefresh; 38 39 IOItemCount safemodeCount; 40 IOItemCount builtinCount; 41 IOItemCount televisionCount; 42 IOItemCount simulscanCount; 43 IOItemCount maxRefreshDigits; 44 45 CFMutableArrayRef refreshNames; 46 47 Boolean refreshList; 48 uint32_t significantFlags; 49}; 50typedef struct IOFBModeList * IOFBModeListRef; 51 52/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 53 54struct ModeInfo 55{ 56 IODisplayModeInformation * info; 57 UInt32 index; 58 UInt32 cgIndex; 59 SInt32 numRefresh; 60 SInt32 numSafe; 61 SInt32 numPreset; 62 UInt32 digits; 63 Boolean notReco; 64}; 65typedef struct ModeInfo ModeInfo; 66 67enum 68{ 69 kCompareRefresh = 0x00000001, 70 kCompareAll = 0xffffffff, 71}; 72 73static int 74CompareModes(IOFBModeListRef modeListRef, 75 IODisplayModeInformation * left, IODisplayModeInformation * right, 76 IOOptionBits compare) 77{ 78 UInt32 leftFlags, rightFlags, differFlags; 79 80 if (!left) 81 return (1); 82 else if (!right) 83 return (-1); 84 85 leftFlags = left->flags; 86 rightFlags = right->flags; 87 differFlags = leftFlags ^ rightFlags; 88 89 if (modeListRef->simulscanCount && (kDisplayModeSimulscanFlag & differFlags)) 90 { 91 if (kDisplayModeSimulscanFlag & leftFlags) 92 return (1); 93 else 94 return (-1); 95 } 96 if (modeListRef->builtinCount && (kDisplayModeBuiltInFlag & differFlags)) 97 { 98 if (kDisplayModeBuiltInFlag & leftFlags) 99 return (1); 100 else 101 return (-1); 102 } 103 104 if (kDisplayModeTelevisionFlag & leftFlags & rightFlags) 105 { 106 // both TV, order ntsc first 107 if ((left->refreshRate < 55*65536) && (right->refreshRate > 55*65536)) 108 return (1); 109 else if ((left->refreshRate > 55*65536) && (right->refreshRate < 55*65536)) 110 return (-1); 111 } 112 113 if (left->nominalWidth > right->nominalWidth) 114 return (1); 115 else if (left->nominalWidth != right->nominalWidth) 116 return (-1); 117 if (left->nominalHeight > right->nominalHeight) 118 return (1); 119 else if (left->nominalHeight != right->nominalHeight) 120 return (-1); 121 122 if (kDisplayModeStretchedFlag & differFlags) 123 { 124 if (kDisplayModeStretchedFlag & leftFlags) 125 return (1); 126 else 127 return (-1); 128 } 129 130 if (kDisplayModeInterlacedFlag & differFlags) 131 { 132 if (kDisplayModeInterlacedFlag & leftFlags) 133 return (1); 134 else 135 return (-1); 136 } 137 138 if (compare & kCompareRefresh) 139 { 140 if (left->refreshRate > right->refreshRate) 141 return (1); 142 else if (left->refreshRate != right->refreshRate) 143 return (-1); 144 } 145 146 return (0); 147} 148 149static int 150qsort_cmp(void * ref, const void * _left, const void * _right) 151{ 152 IOFBModeListRef modeListRef = (IOFBModeListRef) ref; 153 154 IODisplayModeInformation * left = ((ModeInfo *) _left)->info; 155 IODisplayModeInformation * right = ((ModeInfo *) _right)->info; 156 157 return (CompareModes(modeListRef, left, right, kCompareAll)); 158} 159 160 161 162#define k256ColorString "256 Colors" 163#define kThousandsString "Thousands" 164#define kMillionsString "Millions" 165#define kTrillionsString "Trillions" 166 167#define kPALString "PAL" 168#define kNTSCString "NTSC" 169 170#define kNoRefreshRateString "n/a" 171 172#define kStretchedString "stretched" 173#define kSimulscanString "simulscan" 174#define kInterlacedString "interlaced" 175#define kExternalString "external" 176 177#define kRefreshString "%%.%ldf Hertz" 178#define kTVRefreshString "%%.%ldf Hertz (%%@)" 179 180#define kHxVString "%d x %d" 181#define kHxVpString "%d x %dp" 182#define kHxViString "%d x %di" 183 184#define kNoHertz0FlagString "%@" 185#define kNoHertz1FlagString "%@ (%@)" 186#define kNoHertz2FlagString "%@ (%@, %@)" 187#define kNoHertz3FlagString "%@ (%@, %@, %@)" 188#define kNoHertz4FlagString "%@ (%@, %@, %@, %@)" 189#define kNoHertz5FlagString "%@ (%@, %@, %@, %@, %@)" 190 191#define kHertz0FlagString "%%@, %%.%ldf Hz" 192#define kHertz1FlagString "%%@, %%.%ldf Hz (%%@)" 193#define kHertz2FlagString "%%@, %%.%ldf Hz (%%@, %%@)" 194#define kHertz3FlagString "%%@, %%.%ldf Hz (%%@, %%@, %%@)" 195#define kHertz4FlagString "%%@, %%.%ldf Hz (%%@, %%@, %%@, %%@)" 196#define kHertz5FlagString "%%@, %%.%ldf Hz (%%@, %%@, %%@, %%@, %%@)" 197 198CFStringRef 199TimingName(IOFBModeListRef modeListRef, IODisplayModeInformation * info, CFIndex * refRateIndex) 200{ 201 202 CFStringRef formatStr, formatStr2, refStr, resStr, finalStr; 203 CFStringRef palNTSCStr, stretchedStr, simulscanStr, interlacedStr, externalStr; 204 uint32_t flags, flagCount; 205 CFStringRef flagStrs[5] = { 0 }; 206 CFIndex k, count; 207 208 float refRate = info->refreshRate / 65536.0; 209 210 flagCount = 0; 211 212 if (kDisplayModeTelevisionFlag & info->flags) 213 { 214 palNTSCStr = refRate < 55.0 ? CFSTR(kPALString) : CFSTR(kNTSCString); 215 palNTSCStr = CFBundleCopyLocalizedString(modeListRef->bundle, palNTSCStr, palNTSCStr, 0); 216 } 217 else 218 palNTSCStr = 0; 219 220 stretchedStr = CFBundleCopyLocalizedString(modeListRef->bundle, CFSTR(kStretchedString), CFSTR(kStretchedString), 0); 221 simulscanStr = CFBundleCopyLocalizedString(modeListRef->bundle, CFSTR(kSimulscanString), CFSTR(kSimulscanString), 0); 222 interlacedStr = CFBundleCopyLocalizedString(modeListRef->bundle, CFSTR(kInterlacedString), CFSTR(kInterlacedString), 0); 223 externalStr = CFBundleCopyLocalizedString(modeListRef->bundle, CFSTR(kExternalString), CFSTR(kExternalString), 0); 224 225 if (modeListRef->refreshList) 226 { 227 228 if (modeListRef->suppressRefresh) 229 { 230 refStr = CFSTR(kNoRefreshRateString); 231 refStr = CFBundleCopyLocalizedString(modeListRef->bundle, refStr, refStr, 0); 232 } 233 else 234 { 235 if (kDisplayModeTelevisionFlag & info->flags) 236 formatStr = CFSTR(kTVRefreshString); 237 else 238 formatStr = CFSTR(kRefreshString); 239 240 formatStr = CFBundleCopyLocalizedString(modeListRef->bundle, formatStr, formatStr, 0); 241 242 formatStr2 = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, 243 formatStr, 244 modeListRef->maxRefreshDigits); 245 refStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, 246 formatStr2, refRate, 247 palNTSCStr ); 248 CFRelease(formatStr); 249 } 250 251 count = CFArrayGetCount(modeListRef->refreshNames); 252 for (k = 0; 253 (k < count) && !CFEqual(refStr, CFArrayGetValueAtIndex(modeListRef->refreshNames, k)); 254 k++) {} 255 256 if (k == count) 257 CFArrayAppendValue(modeListRef->refreshNames, refStr); 258 CFRelease(refStr); 259 260 if (refRateIndex) 261 *refRateIndex = k; 262 } 263 264 flags = modeListRef->significantFlags & info->flags; 265 266 if (!modeListRef->refreshList && (kDisplayModeTelevisionFlag & info->flags)) 267 flagStrs[flagCount++] = palNTSCStr; 268 269 if (flags & kDisplayModeStretchedFlag) 270 flagStrs[flagCount++] = stretchedStr; 271 272 if (flags & kDisplayModeSimulscanFlag) 273 flagStrs[flagCount++] = simulscanStr; 274 275 if ((kDisplayModeBuiltInFlag & modeListRef->significantFlags) 276 && !(flags & kDisplayModeBuiltInFlag)) 277 flagStrs[flagCount++] = externalStr; 278 279 if (kDisplayModeInterlacedFlag == 280 (flags & (kDisplayModeInterlacedFlag | kDisplayModeTelevisionFlag))) 281 flagStrs[flagCount++] = interlacedStr; 282 283 284 resStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, 285 CFSTR(kHxVString), 286 info->nominalWidth, info->nominalHeight); 287 288 if (modeListRef->refreshList || modeListRef->suppressRefresh) 289 { 290 switch (flagCount) 291 { 292 case 0: formatStr = CFSTR(kNoHertz0FlagString); break; 293 case 1: formatStr = CFSTR(kNoHertz1FlagString); break; 294 case 2: formatStr = CFSTR(kNoHertz2FlagString); break; 295 case 3: formatStr = CFSTR(kNoHertz3FlagString); break; 296 case 4: formatStr = CFSTR(kNoHertz4FlagString); break; 297 default: 298 case 5: formatStr = CFSTR(kNoHertz5FlagString); break; 299 } 300 301 formatStr = CFBundleCopyLocalizedString(modeListRef->bundle, formatStr, formatStr, 0); 302 303 finalStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, 304 formatStr, 305 resStr, 306 flagStrs[0], 307 flagStrs[1], 308 flagStrs[2], 309 flagStrs[3], 310 flagStrs[4]); 311 } 312 else 313 { 314 switch (flagCount) 315 { 316 case 0: formatStr2 = CFSTR(kHertz0FlagString); break; 317 case 1: formatStr2 = CFSTR(kHertz1FlagString); break; 318 case 2: formatStr2 = CFSTR(kHertz2FlagString); break; 319 case 3: formatStr2 = CFSTR(kHertz3FlagString); break; 320 case 4: formatStr2 = CFSTR(kHertz4FlagString); break; 321 default: 322 case 5: formatStr2 = CFSTR(kHertz5FlagString); break; 323 } 324 325 formatStr2 = CFBundleCopyLocalizedString(modeListRef->bundle, formatStr2, formatStr2, 0); 326 327 formatStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, 328 formatStr2, 329 modeListRef->maxRefreshDigits); 330 331 CFRelease(formatStr2); 332 333 finalStr = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, 334 formatStr, 335 resStr, 336 refRate, 337 flagStrs[0], 338 flagStrs[1], 339 flagStrs[2], 340 flagStrs[3], 341 flagStrs[4]); 342 } 343 344 CFRelease(formatStr); 345 346 if (palNTSCStr) 347 CFRelease(palNTSCStr); 348 if (stretchedStr) 349 CFRelease(stretchedStr); 350 if (simulscanStr) 351 CFRelease(simulscanStr); 352 if (interlacedStr) 353 CFRelease(interlacedStr); 354 if (externalStr) 355 CFRelease(externalStr); 356 357 return (finalStr); 358} 359 360kern_return_t 361ModeList(IOFBModeListRef modeListRef, Boolean reco) 362{ 363 CFMutableDictionaryRef dict; 364 CFMutableArrayRef array; 365 CFDataRef data; 366 SInt32 i, j, k; 367 CFIndex modeCount, newCount, cgIndex = 0; 368 IODisplayModeInformation * info; 369 ModeInfo * modeArray; 370 371 do 372 { 373 dict = CFDictionaryCreateMutable( kCFAllocatorDefault, (CFIndex) 0, 374 (CFDictionaryKeyCallBacks *) 0, 375 &kCFTypeDictionaryValueCallBacks ); 376 modeListRef->modes = dict; 377 378 dict = (CFMutableDictionaryRef) IORegistryEntryCreateCFProperty( 379 modeListRef->framebuffer, 380 CFSTR(kIOFBConfigKey), 381 kCFAllocatorDefault, kNilOptions); 382 if (!dict) 383 break; 384 array = (CFMutableArrayRef) CFDictionaryGetValue(dict, CFSTR(kIOFBModesKey)); 385 if (!array) 386 break; 387 388 // pick up existing config 389 modeListRef->kernelInfo = dict; 390 CFRetain(array); 391 modeListRef->modesArray = array; 392 393 modeListRef->suppressRefresh = (0 != CFDictionaryGetValue(dict, CFSTR("IOFB0Hz"))); 394 395 modeListRef->detailedRefresh = (0 != CFDictionaryGetValue(dict, CFSTR("IOFBmHz"))); 396 397 modeCount = CFArrayGetCount( modeListRef->modesArray ); 398 newCount = modeCount; 399 400 modeArray = calloc(modeCount, sizeof(ModeInfo)); 401 402 for( i = 0; i < modeCount; i++ ) 403 { 404 const void * key; 405 CFNumberRef num; 406 407 dict = (CFMutableDictionaryRef) CFArrayGetValueAtIndex( modeListRef->modesArray, i ); 408 num = CFDictionaryGetValue( dict, CFSTR(kIOFBModeIDKey) ); 409 CFNumberGetValue( num, kCFNumberSInt32Type, (SInt32 *) &key ); 410 CFDictionarySetValue( modeListRef->modes, key, dict ); 411 412 if (!dict) 413 break; 414 data = CFDictionaryGetValue(dict, CFSTR(kIOFBModeDMKey)); 415 if (!data) 416 break; 417 418 info = (IODisplayModeInformation *) CFDataGetBytePtr(data); 419 420// if (info->flags & kDisplayModeNeverShowFlag) 421// continue; 422 423 modeArray[i].index = i; 424 modeArray[i].info = info; 425 modeArray[i].cgIndex = cgIndex; 426 cgIndex += info->maxDepthIndex + 1; 427 428#if 0 429 printf("%ld: %ld x %ld @ %f Hz\n", modeArray[i].cgIndex, 430 info->nominalWidth, info->nominalHeight, 431 info->refreshRate / 65536.0); 432#endif 433 434 if (info->flags & kDisplayModeSafeFlag) 435 modeListRef->safemodeCount++; 436 437// if (info->flags & kDisplayModeAlwaysShowFlag) 438// if (info->flags & kDisplayModeDefaultFlag) 439 440 if (info->flags & kDisplayModeSimulscanFlag) 441 modeListRef->simulscanCount++; 442 if (info->flags & kDisplayModeBuiltInFlag) 443 modeListRef->builtinCount++; 444 if (info->flags & kDisplayModeTelevisionFlag) 445 modeListRef->televisionCount++; 446 } 447 448 449 qsort_r(modeArray, modeCount, sizeof(modeArray[0]), modeListRef, &qsort_cmp); 450 451 452#define discard() { modeArray[i].notReco = true; continue; } 453 454 455 // group refresh rates 456 457 { 458 UInt32 lastSame; 459 460 lastSame = 0; 461 for (i = 0; i < modeCount; i++) 462 { 463 if (i != lastSame) 464 { 465 if (0 == CompareModes(modeListRef, modeArray[lastSame].info, modeArray[i].info, 0)) 466 { 467 // number that follow (total - 1) 468 modeArray[lastSame].numRefresh++; 469 470 modeArray[i].numRefresh = lastSame - i; 471 } 472 else 473 lastSame = i; 474 } 475 if (modeArray[i].info->flags & kDisplayModeSafeFlag) 476 modeArray[lastSame].numSafe++; 477 478 if (!(modeArray[i].info->flags & kDisplayModeNotPresetFlag)) 479 modeArray[lastSame].numPreset++; 480 } 481 } 482 483 484 if (reco) 485 { 486 // prune with safety / not preset 487 488 for( i = 0; i < modeCount; i++ ) 489 { 490 info = modeArray[i].info; 491 do 492 { 493// if (modeListRef->safemodeCount 494// && !(info->flags & (kDisplayModeSafeFlag | kDisplayModeTelevisionFlag))) 495// discard(); 496 497// if (info->flags & kDisplayModeNotPresetFlag) 498// discard(); 499 } 500 while (false); 501 } 502 } 503 504 // prune refresh rates 505 if (reco) 506 { 507 for( i = 0; i < modeCount; i += modeArray[i].numRefresh + 1 ) 508 { 509 info = modeArray[i].info; 510 do 511 { 512 if (info->flags & kDisplayModeTelevisionFlag) 513 continue; 514 515// if (modeListRef->safemodeCount) 516 { 517 // keep only highest 518 Boolean haveHighestReco = false; 519 for (j = i + modeArray[i].numRefresh; j >= i; j--) 520 { 521 if (haveHighestReco) 522 modeArray[j].notReco = true; 523 else { 524 525 if ((!modeArray[i].numPreset) 526 || (!(modeArray[j].info->flags & kDisplayModeNotPresetFlag))) 527 { 528 uint32_t target; 529 target = (modeArray[j].info->flags & kDisplayModeSafeFlag) 530 ? (86 << 16) : (86 << 16); 531 532 haveHighestReco = !modeArray[j].notReco 533 && (modeArray[j].info->refreshRate < target); 534 } 535 if (!haveHighestReco) 536 modeArray[j].notReco = true; 537 } 538 } 539 continue; 540 } 541 } 542 while (false); 543 } 544 } 545 // <reco/> 546 547 // unique refresh rates 548 549 for( i = 0; i < modeCount; i += modeArray[i].numRefresh + 1 ) 550 { 551 float ref1, ref2, mult; 552 553 modeArray[i].digits = 0; //modeListRef->maxRefreshDigits; 554 mult = 1.0; 555 556 for (j = i; j < (i + modeArray[i].numRefresh + 1); j++) 557 { 558 if (modeArray[j].notReco) 559 continue; 560 ref1 = modeArray[j].info->refreshRate / 65536.0; 561 for (k = i; k < (i + modeArray[i].numRefresh + 1); k++) 562 { 563 if (k == j) 564 continue; 565 if (modeArray[k].notReco) 566 continue; 567 ref2 = modeArray[k].info->refreshRate / 65536.0; 568 while (modeArray[i].digits < 5) 569 { 570//if (modeArray[i].digits) 571// printf("-----> %f, %f, %f, %f\n", ref1, ref2, roundf(ref1 * mult), roundf(ref2 * mult)); 572 if (roundf(ref1 * mult) != roundf(ref2 * mult)) 573 break; 574 modeArray[i].digits++; 575 mult *= 10.0; 576 } 577 } 578 } 579 580 if (modeArray[i].digits > modeListRef->maxRefreshDigits) 581 modeListRef->maxRefreshDigits = modeArray[i].digits; 582 } 583 // <unique/> 584 585 printf("\nOut:\n"); 586 587 modeListRef->refreshNames = CFArrayCreateMutable( kCFAllocatorDefault, 0, 588 &kCFTypeArrayCallBacks ); 589 590 modeListRef->refreshList = false; 591 592 do 593 { 594 modeListRef->significantFlags = 595 1 * kDisplayModeStretchedFlag 596 | 1 * kDisplayModeSimulscanFlag 597 | 0 * kDisplayModeBuiltInFlag; 598 599 printf("-------------\n"); 600 for (i = 0; i < modeCount; i += modeArray[i].numRefresh + 1) 601 { 602 CFStringRef name; 603 CFIndex idx; 604 CFIndex mask = 0; 605 606 info = modeArray[i].info; 607 608 #if 0 609 printf("%ld x %ld \n", 610 info->nominalWidth, info->nominalHeight); 611 612 printf("numRefresh %d, numSafe %d, numPreset %d\n", 613 modeArray[i].numRefresh, 614 modeArray[i].numSafe, 615 modeArray[i].numPreset); 616 #endif 617 618 for (j = i; j < (i + modeArray[i].numRefresh + 1); j++) 619 { 620 info = modeArray[j].info; 621 622 name = TimingName(modeListRef, info, &idx); 623 if (!modeListRef->refreshList || (j == i)) 624 { 625 if (!modeListRef->refreshList && modeArray[j].notReco) 626 printf("*"); 627 printf(CFStringGetCStringPtr(name, kCFStringEncodingMacRoman)); 628 if (!modeListRef->refreshList) 629 printf("\n"); 630 } 631 if (modeListRef->refreshList) 632 { 633 printf(" %c[%d]", modeArray[j].notReco ? '*' : ' ', idx); 634 mask |= (1 << idx); 635 } 636 637 CFRelease(name); 638 #if 0 639 printf(" %s%s%s", 640 (info->flags & kDisplayModeSafeFlag) ? "safe " : "", 641 (info->flags & kDisplayModeDefaultFlag) ? "default " : "", 642 (info->flags & kDisplayModeNotPresetFlag) ? "notpreset " : "" 643 ); 644 #endif 645 } 646 if (modeListRef->refreshList) 647 printf("\n"); 648 } 649 if (modeListRef->refreshList) 650 { 651 CFShow(modeListRef->refreshNames); 652 break; 653 } 654 modeListRef->refreshList = true; 655 } 656 while (true); 657 } 658 while (false); 659 660// CFShow(modeListRef->modesArray); 661 return( kIOReturnSuccess ); 662} 663 664/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 665 666int main( int argc, char * argv[] ) 667{ 668 kern_return_t kr; 669 io_string_t path; 670 CFURLRef url; 671 CFIndex i; 672 CGError err; 673 CGDisplayCount max; 674 CGDirectDisplayID displayIDs[8]; 675 676 err = CGGetOnlineDisplayList(8, displayIDs, &max); 677 if(err != kCGErrorSuccess) 678 exit(1); 679 if(max > 8) 680 max = 8; 681 682 683 684 for(i = 0; i < max; i++ ) 685 { 686 struct IOFBModeList _xxx = { 0 }; 687 IOFBModeListRef modeListRef = &_xxx; 688 modeListRef->framebuffer = CGDisplayIOServicePort(displayIDs[i]); 689 690 691 url = CFURLCreateWithFileSystemPath( 692 kCFAllocatorDefault, 693 CFSTR("/System/Library/Frameworks/IOKit.framework"), 694 kCFURLPOSIXPathStyle, true); 695 if (url) 696 modeListRef->bundle = CFBundleCreate(kCFAllocatorDefault, url); 697 698if (!modeListRef->bundle) exit(1); 699 700 kr = IORegistryEntryGetPath(modeListRef->framebuffer, kIOServicePlane, path); 701 assert( KERN_SUCCESS == kr ); 702 printf("\nDisplay %p: %s\n", displayIDs[i], path); 703 704 ModeList(modeListRef, true || true); 705 } 706 707 exit(0); 708 return(0); 709} 710 711