/ CoreGraphics / CGDirectDisplay.m
CGDirectDisplay.m
1 /* 2 This file is part of Darling. 3 4 Copyright (C) 2020 Lubos Dolezel 5 6 Darling is free software: you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 Darling is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with Darling. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #import <AppKit/NSDisplay.h> 21 #import <AppKit/NSScreen.h> 22 #import <CoreGraphics/CGDirectDisplay.h> 23 #import <CoreGraphics/CGError.h> 24 #import <IOKit/graphics/IOGraphicsLib.h> 25 #import <IOKit/graphics/IOGraphicsTypes.h> 26 #include <dlfcn.h> 27 28 // not sure what type or value this has 29 unsigned int kCGDisplayPixelHeight = kCGDisplayHeight; 30 unsigned int kCGDisplayPixelWidth = kCGDisplayWidth; 31 32 const CFStringRef kCGDisplayProductNameKey = CFSTR("kCGDisplayProductNameKey"); 33 34 CGError CGCaptureAllDisplays(void) { 35 return kCGErrorSuccess; 36 } 37 38 CGError CGReleaseAllDisplays(void) { 39 return kCGErrorSuccess; 40 } 41 42 // Our platform abstraction is in AppKit 43 static NSDisplay *currentDisplay(void) { 44 Class cls = NSClassFromString(@"NSDisplay"); 45 46 if (!cls) { 47 if (dlopen("/System/Library/Frameworks/AppKit.framework/Versions/C/" 48 "AppKit", 49 RTLD_LAZY | RTLD_GLOBAL) != NULL) 50 cls = NSClassFromString(@"NSDisplay"); 51 } 52 53 return [cls currentDisplay]; 54 } 55 56 CGDirectDisplayID CGMainDisplayID(void) { 57 NSDisplay *display = currentDisplay(); 58 if (!display) 59 return kCGErrorInvalidConnection; 60 61 NSArray<NSScreen *> *screens = [display screens]; 62 63 for (int i = 0; i < [screens count]; i++) { 64 if (!NSIsEmptyRect([[screens objectAtIndex: i] frame])) { 65 return i + 1; 66 } 67 } 68 69 return kCGNullDirectDisplay; 70 } 71 72 CGError CGGetOnlineDisplayList(uint32_t maxDisplays, 73 CGDirectDisplayID *onlineDisplays, 74 uint32_t *displayCount) 75 { 76 NSDisplay *display = currentDisplay(); 77 if (!display) 78 return kCGErrorInvalidConnection; 79 80 NSArray<NSScreen *> *screens = [display screens]; 81 const CGDirectDisplayID mainDisplay = CGMainDisplayID(); 82 83 *displayCount = 0; 84 85 // Main display should be the first returned 86 if (mainDisplay != kCGNullDirectDisplay) { 87 (*displayCount)++; 88 if (maxDisplays > 0) 89 onlineDisplays[0] = mainDisplay; 90 } 91 92 for (int i = 0; i < [screens count]; i++) { 93 if ((i + 1) != mainDisplay) { 94 if (*displayCount < maxDisplays) 95 onlineDisplays[*displayCount] = i + 1; 96 (*displayCount)++; 97 } 98 } 99 100 return kCGErrorSuccess; 101 } 102 103 size_t CGDisplayPixelsHigh(CGDirectDisplayID displayIndex) { 104 NSDisplay *display = currentDisplay(); 105 if (!display) 106 return kCGErrorInvalidConnection; 107 108 NSArray<NSScreen *> *screens = [display screens]; 109 if (displayIndex > [screens count] || displayIndex <= 0) 110 return 0; 111 112 return NSHeight([[screens objectAtIndex: displayIndex - 1] frame]); 113 } 114 115 size_t CGDisplayPixelsWide(CGDirectDisplayID displayIndex) { 116 NSDisplay *display = currentDisplay(); 117 if (!display) 118 return kCGErrorInvalidConnection; 119 120 NSArray<NSScreen *> *screens = [display screens]; 121 if (displayIndex > [screens count] || displayIndex <= 0) 122 return 0; 123 124 return NSWidth([[screens objectAtIndex: displayIndex - 1] frame]); 125 } 126 127 CGError CGGetActiveDisplayList(uint32_t maxDisplays, 128 CGDirectDisplayID *activeDisplays, 129 uint32_t *displayCount) 130 { 131 NSDisplay *display = currentDisplay(); 132 if (!display) 133 return kCGErrorInvalidConnection; 134 135 NSArray<NSScreen *> *screens = [display screens]; 136 137 *displayCount = 0; 138 for (int i = 0; i < [screens count]; i++) { 139 if (!NSIsEmptyRect([[screens objectAtIndex: i] frame])) { 140 if (*displayCount < maxDisplays) 141 activeDisplays[*displayCount] = i + 1; 142 (*displayCount)++; 143 } 144 } 145 146 return kCGErrorSuccess; 147 } 148 149 CGError CGGetDisplaysWithOpenGLDisplayMask(CGOpenGLDisplayMask mask, 150 uint32_t maxDisplays, 151 CGDirectDisplayID *displays, 152 uint32_t *matchingDisplayCount) 153 { 154 return CGGetOnlineDisplayList(maxDisplays, displays, matchingDisplayCount); 155 } 156 157 CGDirectDisplayID CGOpenGLDisplayMaskToDisplayID(CGOpenGLDisplayMask mask) { 158 return CGMainDisplayID(); 159 } 160 161 CGError CGGetDisplaysWithPoint(CGPoint point, uint32_t maxDisplays, 162 CGDirectDisplayID *displays, 163 uint32_t *matchingDisplayCount) 164 { 165 NSDisplay *display = currentDisplay(); 166 if (!display) 167 return kCGErrorInvalidConnection; 168 169 NSArray<NSScreen *> *screens = [display screens]; 170 *matchingDisplayCount = 0; 171 172 for (int i = 0; i < [screens count]; i++) { 173 NSRect rect = [[screens objectAtIndex: i] frame]; 174 if (NSPointInRect(point, rect)) { 175 if (*matchingDisplayCount < maxDisplays) 176 displays[*matchingDisplayCount] = i + 1; 177 (*matchingDisplayCount)++; 178 } 179 } 180 181 return kCGErrorSuccess; 182 } 183 184 CGError CGGetDisplaysWithRect(CGRect rect, uint32_t maxDisplays, 185 CGDirectDisplayID *displays, 186 uint32_t *matchingDisplayCount) 187 { 188 NSDisplay *display = currentDisplay(); 189 if (!display) 190 return kCGErrorInvalidConnection; 191 192 NSArray<NSScreen *> *screens = [display screens]; 193 *matchingDisplayCount = 0; 194 195 for (int i = 0; i < [screens count]; i++) { 196 NSRect screenRect = [[screens objectAtIndex: i] frame]; 197 if (NSIntersectsRect(rect, screenRect)) { 198 if (*matchingDisplayCount < maxDisplays) 199 displays[*matchingDisplayCount] = i + 1; 200 (*matchingDisplayCount)++; 201 } 202 } 203 204 return kCGErrorSuccess; 205 } 206 207 CGError CGDisplayCapture(CGDirectDisplayID display) { 208 return kCGErrorSuccess; 209 } 210 211 CGError CGDisplayRelease(CGDirectDisplayID display) { 212 return kCGErrorSuccess; 213 } 214 215 CGRect CGDisplayBounds(CGDirectDisplayID displayIndex) { 216 NSDisplay *display = currentDisplay(); 217 if (!display) 218 return NSZeroRect; 219 220 NSArray<NSScreen *> *screens = [display screens]; 221 if (displayIndex > [screens count] || displayIndex <= 0) 222 return NSZeroRect; 223 224 return [[screens objectAtIndex: displayIndex - 1] frame]; 225 } 226 227 CGError CGDisplayHideCursor(CGDirectDisplayID displayIndex) { 228 NSDisplay *display = currentDisplay(); 229 if (!display) 230 return kCGErrorInvalidConnection; 231 232 [display hideCursor]; 233 return kCGErrorSuccess; 234 } 235 236 CGError CGDisplayShowCursor(CGDirectDisplayID displayIndex) { 237 NSDisplay *display = currentDisplay(); 238 if (!display) 239 return kCGErrorInvalidConnection; 240 241 [display unhideCursor]; 242 return kCGErrorSuccess; 243 } 244 245 CFArrayRef CGDisplayAvailableModes(CGDirectDisplayID displayIndex) { 246 NSDisplay *display = currentDisplay(); 247 if (!display) 248 return NULL; 249 return (CFArrayRef) [display modesForScreen: displayIndex - 1]; 250 } 251 252 Boolean CGDisplayIsMain(CGDirectDisplayID display) { 253 return display == 1; 254 } 255 256 CGDirectDisplayID CGDisplayMirrorsDisplay(CGDirectDisplayID display) { 257 // STUB! 258 // TODO: Get this from XRandR 259 return kCGNullDirectDisplay; 260 } 261 262 CGDisplayModeRef CGDisplayCopyDisplayMode(CGDirectDisplayID displayId) { 263 NSDisplay *display = currentDisplay(); 264 if (!display) 265 return NULL; 266 267 NSDictionary *dict = [[display currentModeForScreen: displayId - 1] retain]; 268 269 return (CGDisplayModeRef) dict; 270 } 271 272 void CGDisplayModeRelease(CGDisplayModeRef mode) { 273 if (mode != NULL) 274 CFRelease(mode); 275 } 276 277 CGDisplayModeRef CGDisplayModeRetain(CGDisplayModeRef mode) { 278 if (mode) 279 CFRetain(mode); 280 return mode; 281 } 282 283 size_t CGDisplayModeGetHeight(CGDisplayModeRef mode) { 284 NSDictionary *dict = (NSDictionary *) mode; 285 return [[dict valueForKey: @"Height"] unsignedIntValue]; 286 } 287 288 size_t CGDisplayModeGetWidth(CGDisplayModeRef mode) { 289 NSDictionary *dict = (NSDictionary *) mode; 290 return [[dict valueForKey: @"Width"] unsignedIntValue]; 291 } 292 293 double CGDisplayModeGetRefreshRate(CGDisplayModeRef mode) { 294 NSDictionary *dict = (NSDictionary *) mode; 295 return [[dict valueForKey: @"RefreshRate"] doubleValue]; 296 } 297 298 CFStringRef CGDisplayModeCopyPixelEncoding(CGDisplayModeRef mode) { 299 NSDictionary *dict = (NSDictionary *) mode; 300 unsigned depth = [[dict valueForKey: @"Depth"] unsignedIntValue]; 301 302 switch (depth) { 303 case 24: 304 case 32: 305 return CFSTR(IO32BitDirectPixels); 306 case 16: 307 return CFSTR(IO16BitDirectPixels); 308 default: 309 return CFSTR(""); 310 } 311 } 312 313 CFArrayRef CGDisplayCopyAllDisplayModes(CGDirectDisplayID displayIndex, 314 CFDictionaryRef options) 315 { 316 NSDisplay *display = currentDisplay(); 317 if (!display) 318 return NULL; 319 return (CFArrayRef)[[display modesForScreen: displayIndex - 1] retain]; 320 } 321 322 CGError CGDisplaySetDisplayMode(CGDirectDisplayID displayId, 323 CGDisplayModeRef mode, CFDictionaryRef options) 324 { 325 NSDisplay *display = currentDisplay(); 326 if (!display) 327 return kCGErrorInvalidConnection; 328 BOOL result = [display setMode: mode forScreen: displayId - 1]; 329 330 return result ? kCGErrorSuccess : kCGErrorFailure; 331 } 332 333 static NSData *edidForDisplay(CGDirectDisplayID displayId) { 334 NSDisplay *display = currentDisplay(); 335 if (!display) 336 return nil; 337 338 NSArray<NSScreen *> *screens = [display screens]; 339 if (displayId <= 0 || displayId > [screens count]) 340 return nil; 341 342 NSScreen *screen = [screens objectAtIndex: displayId - 1]; 343 NSData *edid = [screen edid]; 344 if (edid && [edid length] >= 16) 345 return edid; 346 347 return nil; 348 } 349 350 uint32_t CGDisplaySerialNumber(CGDirectDisplayID displayId) { 351 NSData *edid = edidForDisplay(displayId); 352 if (!edid) 353 return displayId; 354 355 return CFSwapInt32LittleToHost(*(uint32_t *) (&[edid bytes][12])); 356 } 357 358 uint32_t CGDisplayModelNumber(CGDirectDisplayID displayId) { 359 NSData *edid = edidForDisplay(displayId); 360 if (!edid) 361 return kDisplayProductIDGeneric; 362 363 return CFSwapInt16LittleToHost(*(uint16_t *) (&[edid bytes][10])); 364 } 365 366 uint32_t CGDisplayVendorNumber(CGDirectDisplayID displayId) { 367 NSData *edid = edidForDisplay(displayId); 368 if (!edid) 369 return kDisplayVendorIDUnknown; 370 371 return CFSwapInt16BigToHost(*(uint16_t *) (&[edid bytes][8])); 372 } 373 374 io_service_t CGDisplayIOServicePort(CGDirectDisplayID displayID) { 375 // The code in this function is: 376 // Copyright (c) 2002-2006 Marcus Geelnard 377 // Copyright (c) 2006-2010 Camilla Berglund <elmindreda@elmindreda.org> 378 // Taken from 379 // https://github.com/glfw/glfw/blob/e0a6772e5e4c672179fc69a90bcda3369792ed1f/src/cocoa_monitor.m 380 381 io_iterator_t iter; 382 io_service_t serv, servicePort = 0; 383 384 CFMutableDictionaryRef matching = IOServiceMatching("IODisplayConnect"); 385 386 // releases matching for us 387 kern_return_t err = 388 IOServiceGetMatchingServices(kIOMasterPortDefault, matching, &iter); 389 if (err) 390 return 0; 391 392 while ((serv = IOIteratorNext(iter)) != 0) { 393 CFDictionaryRef info; 394 CFIndex vendorID, productID, serialNumber; 395 CFNumberRef vendorIDRef, productIDRef, serialNumberRef; 396 Boolean success; 397 398 info = IODisplayCreateInfoDictionary(serv, kIODisplayOnlyPreferredName); 399 400 vendorIDRef = CFDictionaryGetValue(info, CFSTR(kDisplayVendorID)); 401 productIDRef = CFDictionaryGetValue(info, CFSTR(kDisplayProductID)); 402 serialNumberRef = 403 CFDictionaryGetValue(info, CFSTR(kDisplaySerialNumber)); 404 405 if (!vendorIDRef || !productIDRef || !serialNumberRef) { 406 CFRelease(info); 407 continue; 408 } 409 410 success = 411 CFNumberGetValue(vendorIDRef, kCFNumberCFIndexType, &vendorID); 412 success &= CFNumberGetValue(productIDRef, kCFNumberCFIndexType, 413 &productID); 414 success &= CFNumberGetValue(serialNumberRef, kCFNumberCFIndexType, 415 &serialNumber); 416 417 if (!success) { 418 CFRelease(info); 419 continue; 420 } 421 422 // If the vendor and product id along with the serial don't match 423 // then we are not looking at the correct monitor. 424 // NOTE: The serial number is important in cases where two monitors 425 // are the exact same. 426 if (CGDisplayVendorNumber(displayID) != vendorID || 427 CGDisplayModelNumber(displayID) != productID || 428 CGDisplaySerialNumber(displayID) != serialNumber) { 429 CFRelease(info); 430 continue; 431 } 432 433 // The VendorID, Product ID, and the Serial Number all Match Up! 434 // Therefore we have found the appropriate display io_service 435 servicePort = serv; 436 CFRelease(info); 437 break; 438 } 439 440 IOObjectRelease(iter); 441 return servicePort; 442 } 443 444 CGError CGWarpMouseCursorPosition(CGPoint newCursorPosition) { 445 NSDisplay *display = currentDisplay(); 446 if (!display) 447 return kCGErrorInvalidConnection; 448 [display warpMouse: newCursorPosition]; 449 return kCGErrorSuccess; 450 } 451 452 CGError CGAssociateMouseAndMouseCursorPosition(boolean_t connected) { 453 NSDisplay *display = currentDisplay(); 454 if (!display) 455 return kCGErrorInvalidConnection; 456 457 [display grabMouse: connected]; 458 return kCGErrorSuccess; 459 } 460 461 CFDictionaryRef CGDisplayBestModeForParametersAndRefreshRate( 462 CGDirectDisplayID display, size_t bitsPerPixel, size_t width, 463 size_t height, CGRefreshRate refreshRate, boolean_t *exactMatch) 464 { 465 return nil; 466 } 467 468 boolean_t CGDisplayIsCaptured(CGDirectDisplayID display) { 469 return FALSE; 470 } 471 472 CGError CGDisplaySwitchToMode(CGDirectDisplayID display, CFDictionaryRef mode) { 473 return kCGErrorSuccess; 474 } 475 476 CFDictionaryRef CGDisplayCurrentMode(CGDirectDisplayID display) { 477 return nil; 478 } 479 480 size_t CGDisplayModeGetPixelWidth(CGDisplayModeRef mode) { 481 return 0; 482 }