1//======================================================================== 2// GLFW 3.2 OS X - www.glfw.org 3//------------------------------------------------------------------------ 4// Copyright (c) 2002-2006 Marcus Geelnard 5// Copyright (c) 2006-2016 Camilla Berglund <elmindreda@glfw.org> 6// 7// This software is provided 'as-is', without any express or implied 8// warranty. In no event will the authors be held liable for any damages 9// arising from the use of this software. 10// 11// Permission is granted to anyone to use this software for any purpose, 12// including commercial applications, and to alter it and redistribute it 13// freely, subject to the following restrictions: 14// 15// 1. The origin of this software must not be misrepresented; you must not 16// claim that you wrote the original software. If you use this software 17// in a product, an acknowledgment in the product documentation would 18// be appreciated but is not required. 19// 20// 2. Altered source versions must be plainly marked as such, and must not 21// be misrepresented as being the original software. 22// 23// 3. This notice may not be removed or altered from any source 24// distribution. 25// 26//======================================================================== 27 28#include "internal.h" 29 30#include <stdlib.h> 31#include <limits.h> 32 33#include <IOKit/graphics/IOGraphicsLib.h> 34#include <CoreVideo/CVBase.h> 35#include <CoreVideo/CVDisplayLink.h> 36#include <ApplicationServices/ApplicationServices.h> 37 38 39// Get the name of the specified display 40// 41static char* getDisplayName(CGDirectDisplayID displayID) 42{ 43 char* name; 44 CFDictionaryRef info, names; 45 CFStringRef value; 46 CFIndex size; 47 48 // NOTE: This uses a deprecated function because Apple has 49 // (as of January 2015) not provided any alternative 50 info = IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), 51 kIODisplayOnlyPreferredName); 52 names = CFDictionaryGetValue(info, CFSTR(kDisplayProductName)); 53 54 if (!names || !CFDictionaryGetValueIfPresent(names, CFSTR("en_US"), 55 (const void**) &value)) 56 { 57 // This may happen if a desktop Mac is running headless 58 _glfwInputError(GLFW_PLATFORM_ERROR, 59 "Cocoa: Failed to retrieve display name"); 60 61 CFRelease(info); 62 return strdup("Unknown"); 63 } 64 65 size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value), 66 kCFStringEncodingUTF8); 67 name = calloc(size + 1, 1); 68 CFStringGetCString(value, name, size, kCFStringEncodingUTF8); 69 70 CFRelease(info); 71 72 return name; 73} 74 75// Check whether the display mode should be included in enumeration 76// 77static GLFWbool modeIsGood(CGDisplayModeRef mode) 78{ 79 uint32_t flags = CGDisplayModeGetIOFlags(mode); 80 if (!(flags & kDisplayModeValidFlag) || !(flags & kDisplayModeSafeFlag)) 81 return GLFW_FALSE; 82 83 if (flags & kDisplayModeInterlacedFlag) 84 return GLFW_FALSE; 85 86 if (flags & kDisplayModeStretchedFlag) 87 return GLFW_FALSE; 88 89 CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); 90 if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) && 91 CFStringCompare(format, CFSTR(IO32BitDirectPixels), 0)) 92 { 93 CFRelease(format); 94 return GLFW_FALSE; 95 } 96 97 CFRelease(format); 98 return GLFW_TRUE; 99} 100 101// Convert Core Graphics display mode to GLFW video mode 102// 103static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode, 104 CVDisplayLinkRef link) 105{ 106 GLFWvidmode result; 107 result.width = (int) CGDisplayModeGetWidth(mode); 108 result.height = (int) CGDisplayModeGetHeight(mode); 109 result.refreshRate = (int) CGDisplayModeGetRefreshRate(mode); 110 111 if (result.refreshRate == 0) 112 { 113 const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link); 114 if (!(time.flags & kCVTimeIsIndefinite)) 115 result.refreshRate = (int) (time.timeScale / (double) time.timeValue); 116 } 117 118 CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); 119 120 if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) == 0) 121 { 122 result.redBits = 5; 123 result.greenBits = 5; 124 result.blueBits = 5; 125 } 126 else 127 { 128 result.redBits = 8; 129 result.greenBits = 8; 130 result.blueBits = 8; 131 } 132 133 CFRelease(format); 134 return result; 135} 136 137// Starts reservation for display fading 138// 139static CGDisplayFadeReservationToken beginFadeReservation(void) 140{ 141 CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken; 142 143 if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess) 144 CGDisplayFade(token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); 145 146 return token; 147} 148 149// Ends reservation for display fading 150// 151static void endFadeReservation(CGDisplayFadeReservationToken token) 152{ 153 if (token != kCGDisplayFadeReservationInvalidToken) 154 { 155 CGDisplayFade(token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); 156 CGReleaseDisplayFadeReservation(token); 157 } 158} 159 160 161////////////////////////////////////////////////////////////////////////// 162////// GLFW internal API ////// 163////////////////////////////////////////////////////////////////////////// 164 165// Change the current video mode 166// 167GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) 168{ 169 CFArrayRef modes; 170 CFIndex count, i; 171 CVDisplayLinkRef link; 172 CGDisplayModeRef native = NULL; 173 GLFWvidmode current; 174 const GLFWvidmode* best; 175 176 best = _glfwChooseVideoMode(monitor, desired); 177 _glfwPlatformGetVideoMode(monitor, ¤t); 178 if (_glfwCompareVideoModes(¤t, best) == 0) 179 return GLFW_TRUE; 180 181 CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link); 182 183 modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL); 184 count = CFArrayGetCount(modes); 185 186 for (i = 0; i < count; i++) 187 { 188 CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); 189 if (!modeIsGood(dm)) 190 continue; 191 192 const GLFWvidmode mode = vidmodeFromCGDisplayMode(dm, link); 193 if (_glfwCompareVideoModes(best, &mode) == 0) 194 { 195 native = dm; 196 break; 197 } 198 } 199 200 if (native) 201 { 202 if (monitor->ns.previousMode == NULL) 203 monitor->ns.previousMode = CGDisplayCopyDisplayMode(monitor->ns.displayID); 204 205 CGDisplayFadeReservationToken token = beginFadeReservation(); 206 CGDisplaySetDisplayMode(monitor->ns.displayID, native, NULL); 207 endFadeReservation(token); 208 } 209 210 CFRelease(modes); 211 CVDisplayLinkRelease(link); 212 213 if (!native) 214 { 215 _glfwInputError(GLFW_PLATFORM_ERROR, 216 "Cocoa: Monitor mode list changed"); 217 return GLFW_FALSE; 218 } 219 220 return GLFW_TRUE; 221} 222 223// Restore the previously saved (original) video mode 224// 225void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor) 226{ 227 if (monitor->ns.previousMode) 228 { 229 CGDisplayFadeReservationToken token = beginFadeReservation(); 230 CGDisplaySetDisplayMode(monitor->ns.displayID, 231 monitor->ns.previousMode, NULL); 232 endFadeReservation(token); 233 234 CGDisplayModeRelease(monitor->ns.previousMode); 235 monitor->ns.previousMode = NULL; 236 } 237} 238 239 240////////////////////////////////////////////////////////////////////////// 241////// GLFW platform API ////// 242////////////////////////////////////////////////////////////////////////// 243 244_GLFWmonitor** _glfwPlatformGetMonitors(int* count) 245{ 246 uint32_t i, found = 0, displayCount; 247 _GLFWmonitor** monitors; 248 CGDirectDisplayID* displays; 249 250 *count = 0; 251 252 CGGetOnlineDisplayList(0, NULL, &displayCount); 253 displays = calloc(displayCount, sizeof(CGDirectDisplayID)); 254 monitors = calloc(displayCount, sizeof(_GLFWmonitor*)); 255 256 CGGetOnlineDisplayList(displayCount, displays, &displayCount); 257 258 for (i = 0; i < displayCount; i++) 259 { 260 _GLFWmonitor* monitor; 261 262 if (CGDisplayIsAsleep(displays[i])) 263 continue; 264 265 const CGSize size = CGDisplayScreenSize(displays[i]); 266 char* name = getDisplayName(displays[i]); 267 268 monitor = _glfwAllocMonitor(name, size.width, size.height); 269 monitor->ns.displayID = displays[i]; 270 monitor->ns.unitNumber = CGDisplayUnitNumber(displays[i]); 271 272 free(name); 273 274 found++; 275 monitors[found - 1] = monitor; 276 } 277 278 free(displays); 279 280 *count = found; 281 return monitors; 282} 283 284GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) 285{ 286 // HACK: Compare unit numbers instead of display IDs to work around display 287 // replacement on machines with automatic graphics switching 288 return first->ns.unitNumber == second->ns.unitNumber; 289} 290 291void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) 292{ 293 const CGRect bounds = CGDisplayBounds(monitor->ns.displayID); 294 295 if (xpos) 296 *xpos = (int) bounds.origin.x; 297 if (ypos) 298 *ypos = (int) bounds.origin.y; 299} 300 301GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) 302{ 303 CFArrayRef modes; 304 CFIndex found, i, j; 305 GLFWvidmode* result; 306 CVDisplayLinkRef link; 307 308 *count = 0; 309 310 CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link); 311 312 modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL); 313 found = CFArrayGetCount(modes); 314 result = calloc(found, sizeof(GLFWvidmode)); 315 316 for (i = 0; i < found; i++) 317 { 318 CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); 319 if (!modeIsGood(dm)) 320 continue; 321 322 const GLFWvidmode mode = vidmodeFromCGDisplayMode(dm, link); 323 324 for (j = 0; j < *count; j++) 325 { 326 if (_glfwCompareVideoModes(result + j, &mode) == 0) 327 break; 328 } 329 330 // Skip duplicate modes 331 if (i < *count) 332 continue; 333 334 (*count)++; 335 result[*count - 1] = mode; 336 } 337 338 CFRelease(modes); 339 CVDisplayLinkRelease(link); 340 return result; 341} 342 343void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode) 344{ 345 CGDisplayModeRef displayMode; 346 CVDisplayLinkRef link; 347 348 CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link); 349 350 displayMode = CGDisplayCopyDisplayMode(monitor->ns.displayID); 351 *mode = vidmodeFromCGDisplayMode(displayMode, link); 352 CGDisplayModeRelease(displayMode); 353 354 CVDisplayLinkRelease(link); 355} 356 357void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) 358{ 359 uint32_t i, size = CGDisplayGammaTableCapacity(monitor->ns.displayID); 360 CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue)); 361 362 CGGetDisplayTransferByTable(monitor->ns.displayID, 363 size, 364 values, 365 values + size, 366 values + size * 2, 367 &size); 368 369 _glfwAllocGammaArrays(ramp, size); 370 371 for (i = 0; i < size; i++) 372 { 373 ramp->red[i] = (unsigned short) (values[i] * 65535); 374 ramp->green[i] = (unsigned short) (values[i + size] * 65535); 375 ramp->blue[i] = (unsigned short) (values[i + size * 2] * 65535); 376 } 377 378 free(values); 379} 380 381void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) 382{ 383 int i; 384 CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue)); 385 386 for (i = 0; i < ramp->size; i++) 387 { 388 values[i] = ramp->red[i] / 65535.f; 389 values[i + ramp->size] = ramp->green[i] / 65535.f; 390 values[i + ramp->size * 2] = ramp->blue[i] / 65535.f; 391 } 392 393 CGSetDisplayTransferByTable(monitor->ns.displayID, 394 ramp->size, 395 values, 396 values + ramp->size, 397 values + ramp->size * 2); 398 399 free(values); 400} 401 402 403////////////////////////////////////////////////////////////////////////// 404////// GLFW native API ////// 405////////////////////////////////////////////////////////////////////////// 406 407GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* handle) 408{ 409 _GLFWmonitor* monitor = (_GLFWmonitor*) handle; 410 _GLFW_REQUIRE_INIT_OR_RETURN(kCGNullDirectDisplay); 411 return monitor->ns.displayID; 412} 413 414