Simple RGB LED controller for Mac OS X
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Screenshot.m 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. //
  2. // Screenshot.m
  3. // CaseLights
  4. //
  5. // Based on the Apple ScreenSnapshot example:
  6. // https://developer.apple.com/library/mac/samplecode/ScreenSnapshot/Listings/ScreenSnapshot_ScreenSnapshotAppDelegate_m.html
  7. //
  8. // Created by Thomas Buck on 27.12.15.
  9. // Copyright © 2015 xythobuz. All rights reserved.
  10. //
  11. // Uncomment to store a screenshot for each display in the build directory
  12. //#define DEBUG_SCREENSHOT
  13. #import "AppDelegate.h"
  14. #import "Screenshot.h"
  15. static BOOL displayRegistrationCallBackSuccessful;
  16. static void displayRegisterReconfigurationCallback(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *userInfo) {
  17. AppDelegate *appDelegate = (__bridge AppDelegate*)userInfo;
  18. static BOOL DisplayConfigurationChanged = NO;
  19. // Before display reconfiguration, this callback fires to inform
  20. // applications of a pending configuration change. The callback runs
  21. // once for each on-line display. The flags passed in are set to
  22. // kCGDisplayBeginConfigurationFlag. This callback does not
  23. // carry other per-display information, as details of how a
  24. // reconfiguration affects a particular device rely on device-specific
  25. // behaviors which may not be exposed by a device driver.
  26. //
  27. // After display reconfiguration, at the time the callback function
  28. // is invoked, all display state reported by CoreGraphics, QuickDraw,
  29. // and the Carbon Display Manager API will be up to date. This callback
  30. // runs after the Carbon Display Manager notification callbacks.
  31. // The callback runs once for each added, removed, and currently
  32. // on-line display. Note that in the case of removed displays, calls into
  33. // the CoreGraphics API with the removed display ID will fail.
  34. // Because the callback is called for each display I use DisplayConfigurationChanged to
  35. // make sure we only disable the menu to change displays once and then refresh it only once.
  36. if (flags == kCGDisplayBeginConfigurationFlag) {
  37. if (DisplayConfigurationChanged == NO) {
  38. [appDelegate clearDisplayUI];
  39. DisplayConfigurationChanged = YES;
  40. }
  41. } else if (DisplayConfigurationChanged == YES) {
  42. [appDelegate updateDisplayUI:[Screenshot listDisplays]];
  43. DisplayConfigurationChanged = NO;
  44. }
  45. }
  46. @implementation Screenshot
  47. + (void)init:(AppDelegate *)appDelegate {
  48. // Applications who want to register for notifications of display changes would use
  49. // CGDisplayRegisterReconfigurationCallback
  50. //
  51. // Display changes are reported via a callback mechanism.
  52. //
  53. // Callbacks are invoked when the app is listening for events,
  54. // on the event processing thread, or from within the display
  55. // reconfiguration function when in the program that is driving the
  56. // reconfiguration.
  57. displayRegistrationCallBackSuccessful = NO; // Hasn't been tried yet.
  58. CGError err = CGDisplayRegisterReconfigurationCallback(displayRegisterReconfigurationCallback, (__bridge void * _Nullable)(appDelegate));
  59. if (err == kCGErrorSuccess) {
  60. displayRegistrationCallBackSuccessful = YES;
  61. }
  62. }
  63. + (void)close:(AppDelegate *)appDelegate {
  64. // CGDisplayRemoveReconfigurationCallback Removes the registration of a callback function that’s invoked
  65. // whenever a local display is reconfigured. We only remove the registration if it was successful in the first place.
  66. if (displayRegistrationCallBackSuccessful == YES) {
  67. CGDisplayRemoveReconfigurationCallback(displayRegisterReconfigurationCallback, (__bridge void * _Nullable)(appDelegate));
  68. }
  69. }
  70. + (NSArray *)listDisplays {
  71. CGDisplayCount dspCount = 0;
  72. CGError err = CGGetActiveDisplayList(0, NULL, &dspCount);
  73. if (err != CGDisplayNoErr) {
  74. NSLog(@"Couldn't list any active displays (%d)!\n", err);
  75. return nil;
  76. }
  77. CGDirectDisplayID *displays = calloc((size_t)dspCount, sizeof(CGDirectDisplayID));
  78. err = CGGetActiveDisplayList(dspCount, displays, &dspCount);
  79. if (err != CGDisplayNoErr) {
  80. NSLog(@"Couldn't get active display list (%d)!\n", err);
  81. return nil;
  82. }
  83. if (dspCount > 0) {
  84. NSMutableArray *array = [NSMutableArray arrayWithCapacity:dspCount];
  85. for (int i = 0; i < dspCount; i++) {
  86. [array addObject:[NSNumber numberWithInt:displays[i]]];
  87. }
  88. return [array copy];
  89. } else {
  90. NSLog(@"No displays found!\n");
  91. return nil;
  92. }
  93. }
  94. + (NSString *)displayNameFromDisplayID:(NSNumber *)displayID {
  95. NSDictionary *displayInfo = CFBridgingRelease(IODisplayCreateInfoDictionary(CGDisplayIOServicePort([displayID unsignedIntValue]), kIODisplayOnlyPreferredName));
  96. NSDictionary *localizedNames = [displayInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
  97. // Use the first name
  98. NSString *displayProductName = nil;
  99. if ([localizedNames count] > 0) {
  100. displayProductName = [localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]];
  101. #ifdef DEBUG
  102. NSLog(@"Display %u named \"%@\"!\n", [displayID unsignedIntValue], displayProductName);
  103. #endif
  104. }
  105. return displayProductName;
  106. }
  107. + (NSBitmapImageRep *)screenshot:(NSNumber *)displayID {
  108. CGImageRef image = CGDisplayCreateImage([displayID unsignedIntValue]);
  109. NSBitmapImageRep *imgRep = [[NSBitmapImageRep alloc] initWithCGImage:image];
  110. CFRelease(image);
  111. #ifdef DEBUG_SCREENSHOT
  112. NSData *data = [imgRep representationUsingType:NSPNGFileType properties:[NSDictionary dictionary]];
  113. NSString *path = [NSString stringWithFormat:@"test-%u-%@.png", [displayID unsignedIntValue], [Screenshot displayNameFromDisplayID:displayID]];
  114. NSError *error;
  115. if ([data writeToFile:path options:0 error:&error] == YES) {
  116. NSLog(@"Wrote debug image to \"%@\"\n", path);
  117. } else {
  118. NSLog(@"Error writing debug image to \"%@\": %@\n", path, error);
  119. }
  120. #endif
  121. return imgRep;
  122. }
  123. @end