views:

633

answers:

2

Hello! I was wondering if you could help me figure out how to progmatically get the Display Name for a monitor by using its Display ID number in Mac OS X (10.5)? A requirement is if I give a function the Display ID, it'll provide the Display Name in return (or vice versa).

Display Name looks something like this: "Color LCD", "SAMSUNG"

Display ID looks something like this: "69671872", "893830283"

NSScreen in Cocoa (Obj-C), or CGGetActiveDisplayList in Quartz (C), allow you to get the Display ID number for a monitor. Neither appear to have a method to get the Display Name. Oh no! Here's the code for NSScreen to get the Display ID:

NSArray *screenArray = [NSScreen screens];
NSDictionary *screenDescription = [[screenArray objectAtIndex:0] deviceDescription];
NSLog(@"Device ID: %@", [screenDescription objectForKey:@"NSScreenNumber"]);

System Profiler, and Displays under System Preferences, reference displays by Display Name, not Display ID.

I'm asking as I want to run an AppleScript, and it requires a Display Name rather than a Display ID. Any help is MUCH appreciated! :)

+4  A: 

This gives you the localized display name:

static void KeyArrayCallback(const void* key, const void* value, void* context) { CFArrayAppendValue(context, key);  }

- (NSString*)localizedDisplayProductName
{
    NSDictionary* screenDictionary = [[NSScreen mainScreen] deviceDescription];
    NSNumber* screenID = [screenDictionary objectForKey:@"NSScreenNumber"];
    CGDirectDisplayID aID = [screenID unsignedIntValue];   
    CFStringRef localName = NULL;
    io_connect_t displayPort = CGDisplayIOServicePort(aID);
    CFDictionaryRef dict = (CFDictionaryRef)IODisplayCreateInfoDictionary(displayPort, 0);
    CFDictionaryRef names = CFDictionaryGetValue(dict, CFSTR(kDisplayProductName));
    if(names)
    {
     CFArrayRef langKeys = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
     CFDictionaryApplyFunction(names, KeyArrayCallback, (void*)langKeys);
     CFArrayRef orderLangKeys = CFBundleCopyPreferredLocalizationsFromArray(langKeys);
     CFRelease(langKeys);
     if(orderLangKeys && CFArrayGetCount(orderLangKeys))
     {
      CFStringRef langKey = CFArrayGetValueAtIndex(orderLangKeys, 0);
      localName = CFDictionaryGetValue(names, langKey);
      CFRetain(localName);
     }
     CFRelease(orderLangKeys);
    }
    CFRelease(dict);
    return [(NSString*)localName autorelease];
}
weichsel
If you write a function like this, you should autorelease localName, otherwise most people will leak it since it violates cocoa memory management. That or name the function copyLocalizedDisplayProductName.
Jason Coco
Thanks for the hint!
weichsel
Thanks very much for the solution! You've made my day!!! :)On my system I ended up with the langKey grabbing a language other than en_US. It was going for ja_JP. I assume there's probably a way to determine what language your system is set at, in which case I could probably just use that and grab the Display Name value out of the localName dict. Shouldn't be too tough! :)
+3  A: 

Or if you don't want to mess with the preferred localization array, pass the kIODisplayOnlyPreferredName flag to IODisplayCreateInfoDictionary()

Here is a less CoreFoundation, more Cocoa and somewhat reduced code that will do the same thing:

NSString* screenNameForDisplay(CGDirectDisplayID displayID)
{
    NSString *screenName = nil;

    NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), kIODisplayOnlyPreferredName);
    NSDictionary *localizedNames = [deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];

    if ([localizedNames count] > 0) {
     screenName = [[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] retain];
    }

    [deviceInfo release];
    return [screenName autorelease];
}
0xced
Nice! Didn't know about kIODisplayOnlyPreferredName.
weichsel