123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- //
- // Screenshot.m
- // DisplayBacklight
- //
- // Based on the Apple ScreenSnapshot example:
- // https://developer.apple.com/library/mac/samplecode/ScreenSnapshot/Listings/ScreenSnapshot_ScreenSnapshotAppDelegate_m.html
- //
- // Created by Thomas Buck on 27.12.15.
- // Copyright © 2015 xythobuz. All rights reserved.
- //
-
- // Uncomment to store a screenshot for each display in the build directory
- //#define DEBUG_SCREENSHOT
-
- #import "AppDelegate.h"
- #import "Screenshot.h"
-
- static BOOL displayRegistrationCallBackSuccessful;
-
- static void displayRegisterReconfigurationCallback(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *userInfo) {
- AppDelegate *appDelegate = (__bridge AppDelegate*)userInfo;
- static BOOL DisplayConfigurationChanged = NO;
-
- // Before display reconfiguration, this callback fires to inform
- // applications of a pending configuration change. The callback runs
- // once for each on-line display. The flags passed in are set to
- // kCGDisplayBeginConfigurationFlag. This callback does not
- // carry other per-display information, as details of how a
- // reconfiguration affects a particular device rely on device-specific
- // behaviors which may not be exposed by a device driver.
- //
- // After display reconfiguration, at the time the callback function
- // is invoked, all display state reported by CoreGraphics, QuickDraw,
- // and the Carbon Display Manager API will be up to date. This callback
- // runs after the Carbon Display Manager notification callbacks.
- // The callback runs once for each added, removed, and currently
- // on-line display. Note that in the case of removed displays, calls into
- // the CoreGraphics API with the removed display ID will fail.
-
- // Because the callback is called for each display I use DisplayConfigurationChanged to
- // make sure we only disable the menu to change displays once and then refresh it only once.
- if (flags == kCGDisplayBeginConfigurationFlag) {
- if (DisplayConfigurationChanged == NO) {
- [appDelegate stopAmbilight];
- DisplayConfigurationChanged = YES;
- }
- } else if (DisplayConfigurationChanged == YES) {
- [appDelegate newDisplayList:[Screenshot listDisplays]];
- DisplayConfigurationChanged = NO;
- }
- }
-
- @implementation Screenshot
-
- + (void)init:(AppDelegate *)appDelegate {
- // Applications who want to register for notifications of display changes would use
- // CGDisplayRegisterReconfigurationCallback
- //
- // Display changes are reported via a callback mechanism.
- //
- // Callbacks are invoked when the app is listening for events,
- // on the event processing thread, or from within the display
- // reconfiguration function when in the program that is driving the
- // reconfiguration.
- displayRegistrationCallBackSuccessful = NO; // Hasn't been tried yet.
- CGError err = CGDisplayRegisterReconfigurationCallback(displayRegisterReconfigurationCallback, (__bridge void * _Nullable)(appDelegate));
- if (err == kCGErrorSuccess) {
- displayRegistrationCallBackSuccessful = YES;
- }
- }
-
- + (void)close:(AppDelegate *)appDelegate {
- // CGDisplayRemoveReconfigurationCallback Removes the registration of a callback function that’s invoked
- // whenever a local display is reconfigured. We only remove the registration if it was successful in the first place.
- if (displayRegistrationCallBackSuccessful == YES) {
- CGDisplayRemoveReconfigurationCallback(displayRegisterReconfigurationCallback, (__bridge void * _Nullable)(appDelegate));
- }
- }
-
- + (NSArray *)listDisplays {
- CGDisplayCount dspCount = 0;
- CGError err = CGGetActiveDisplayList(0, NULL, &dspCount);
- if (err != CGDisplayNoErr) {
- NSLog(@"Couldn't list any active displays (%d)!\n", err);
- return nil;
- }
-
- CGDirectDisplayID *displays = calloc((size_t)dspCount, sizeof(CGDirectDisplayID));
- err = CGGetActiveDisplayList(dspCount, displays, &dspCount);
- if (err != CGDisplayNoErr) {
- NSLog(@"Couldn't get active display list (%d)!\n", err);
- return nil;
- }
-
- if (dspCount > 0) {
- NSMutableArray *array = [NSMutableArray arrayWithCapacity:dspCount];
- for (int i = 0; i < dspCount; i++) {
- [array addObject:[NSNumber numberWithInt:displays[i]]];
- }
-
- return [array copy];
- } else {
- NSLog(@"No displays found!\n");
- return nil;
- }
- }
-
- + (NSString *)displayNameFromDisplayID:(NSNumber *)displayID {
- NSDictionary *displayInfo = CFBridgingRelease(IODisplayCreateInfoDictionary(CGDisplayIOServicePort([displayID unsignedIntValue]), kIODisplayOnlyPreferredName));
- NSDictionary *localizedNames = [displayInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
-
- // Use the first name
- NSString *displayProductName = nil;
- if ([localizedNames count] > 0) {
- displayProductName = [localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]];
-
- #ifdef DEBUG
- NSLog(@"Display %u named \"%@\"!\n", [displayID unsignedIntValue], displayProductName);
- #endif
- }
-
- return displayProductName;
- }
-
- + (NSBitmapImageRep *)screenshot:(NSNumber *)displayID {
- CGImageRef image = CGDisplayCreateImage([displayID unsignedIntValue]);
- NSBitmapImageRep *imgRep = [[NSBitmapImageRep alloc] initWithCGImage:image];
- CFRelease(image);
-
- #ifdef DEBUG_SCREENSHOT
- NSData *data = [imgRep representationUsingType:NSPNGFileType properties:[NSDictionary dictionary]];
- NSString *path = [NSString stringWithFormat:@"test-%u-%@.png", [displayID unsignedIntValue], [Screenshot displayNameFromDisplayID:displayID]];
- NSError *error;
- if ([data writeToFile:path options:0 error:&error] == YES) {
- NSLog(@"Wrote debug image to \"%@\"\n", path);
- } else {
- NSLog(@"Error writing debug image to \"%@\": %@\n", path, error);
- }
- #endif
-
- return imgRep;
- }
-
- @end
|