CGDisplayIOServicePort is deprecated in OS X >= 10.9, how to replace?

后端 未结 3 2077
不知归路
不知归路 2021-02-07 10:14

I did small app to allow quickly change screen resolutions on multiple monitors. I want to show product name as title of the monitor, and it\'s very simple to find using this co

相关标签:
3条回答
  • 2021-02-07 10:42

    It looks like @Eun's post missed a piece of information to close this discussion. With a little search, I found that IOServicePortFromCGDisplayID is not an API which Apple provides. Rather, it's a piece of open source code found here: https://github.com/glfw/glfw/blob/e0a6772e5e4c672179fc69a90bcda3369792ed1f/src/cocoa_monitor.m

    I copied IOServicePortFromCGDisplayID and also 'getDisplayName' from it. I needed two tweaks to make it work on OS X 10.10.

    1. Remove the code to handle serial number in IOServicePortFromCGDisplayID. (CFDictionaryGetValue for kDisplaySerialNumber returns NULL for me.)
    2. Remove project specific error handling code in getDisplayName.

    If you need more information

    • Issue tracker of the problem: github.com/glfw/glfw/issues/165
    • Commit for the solution: github.com/glfw/glfw/commit/e0a6772e5e4c672179fc69a90bcda3369792ed1f

    I would thank Matthew Henry who submitted the code there.

    0 讨论(0)
  • 2021-02-07 10:42

    Here is my take on the issue. I also started with the code from GLFW 3.1, file cocoa_monitor.m.
    But I had to modify it in different ways than Hiroshi said, so here goes:

    // Get the name of the specified display
    - (NSString*) screenNameForDisplay: (NSNumber*) screen_id
    {
        CGDirectDisplayID displayID = [screen_id unsignedIntValue];
    
        io_service_t serv = [self IOServicePortFromCGDisplayID: displayID];
        if (serv == 0)
            return @"unknown";
    
        CFDictionaryRef info = IODisplayCreateInfoDictionary(serv, kIODisplayOnlyPreferredName);
        IOObjectRelease(serv);
    
        CFStringRef display_name;
        CFDictionaryRef names = CFDictionaryGetValue(info, CFSTR(kDisplayProductName));
    
        if ( !names ||
             !CFDictionaryGetValueIfPresent(names, CFSTR("en_US"), (const void**) & display_name)  )
        {
            // This may happen if a desktop Mac is running headless
            CFRelease( info );
            return @"unknown";
        }
    
        NSString * displayname = [NSString stringWithString: (__bridge NSString *) display_name];
        CFRelease(info);
        return displayname;
    }
    
    
    // Returns the io_service_t (an int) corresponding to a CG display ID, or 0 on failure.
    // The io_service_t should be released with IOObjectRelease when not needed.
    
    - (io_service_t) IOServicePortFromCGDisplayID: (CGDirectDisplayID) displayID
    {
        io_iterator_t iter;
        io_service_t serv, servicePort = 0;
    
        CFMutableDictionaryRef matching = IOServiceMatching("IODisplayConnect");
    
        // releases matching for us
        kern_return_t err = IOServiceGetMatchingServices( kIOMasterPortDefault, matching, & iter );
        if ( err )
            return 0;
    
        while ( (serv = IOIteratorNext(iter)) != 0 )
        {
            CFDictionaryRef displayInfo;
            CFNumberRef vendorIDRef;
            CFNumberRef productIDRef;
            CFNumberRef serialNumberRef;
    
            displayInfo = IODisplayCreateInfoDictionary( serv, kIODisplayOnlyPreferredName );
    
            Boolean success;
            success =  CFDictionaryGetValueIfPresent( displayInfo, CFSTR(kDisplayVendorID),  (const void**) & vendorIDRef );
            success &= CFDictionaryGetValueIfPresent( displayInfo, CFSTR(kDisplayProductID), (const void**) & productIDRef );
    
            if ( !success )
            {
                CFRelease(displayInfo);
                continue;
            }
    
            SInt32 vendorID;
            CFNumberGetValue( vendorIDRef, kCFNumberSInt32Type, &vendorID );
            SInt32 productID;
            CFNumberGetValue( productIDRef, kCFNumberSInt32Type, &productID );
    
            // If a serial number is found, use it.
            // Otherwise serial number will be nil (= 0) which will match with the output of 'CGDisplaySerialNumber'
            SInt32 serialNumber = 0;
            if ( CFDictionaryGetValueIfPresent(displayInfo, CFSTR(kDisplaySerialNumber), (const void**) & serialNumberRef) )
            {
                CFNumberGetValue( serialNumberRef, kCFNumberSInt32Type, &serialNumber );
            }
    
            // If the vendor and product id along with the serial don't match
            // then we are not looking at the correct monitor.
            // NOTE: The serial number is important in cases where two monitors
            //       are the exact same.
            if( CGDisplayVendorNumber(displayID) != vendorID ||
                CGDisplayModelNumber(displayID)  != productID ||
                CGDisplaySerialNumber(displayID) != serialNumber )
            {
                CFRelease(displayInfo);
                continue;
            }
    
            servicePort = serv;
            CFRelease(displayInfo);
            break;
        }
    
        IOObjectRelease(iter);
        return servicePort;
    }
    

    This works fine for me in a screensaver I wrote under macOS 10.11 (El Capitan). I tested it with the built-in display of my MacBookPro and an Apple Display connected via Thunderbolt.

    0 讨论(0)
  • 2021-02-07 10:42
    NSString* screenNameForDisplay(CGDirectDisplayID displayID)
    {
        NSString *screenName = nil;
        io_service_t service = IOServicePortFromCGDisplayID(displayID);
        if (service)
        {
            NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(service, 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];
    }
    
    0 讨论(0)
提交回复
热议问题