1//======================================================================== 2// GLFW 3.5 macOS - www.glfw.org 3//------------------------------------------------------------------------ 4// Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org> 5// 6// This software is provided 'as-is', without any express or implied 7// warranty. In no event will the authors be held liable for any damages 8// arising from the use of this software. 9// 10// Permission is granted to anyone to use this software for any purpose, 11// including commercial applications, and to alter it and redistribute it 12// freely, subject to the following restrictions: 13// 14// 1. The origin of this software must not be misrepresented; you must not 15// claim that you wrote the original software. If you use this software 16// in a product, an acknowledgment in the product documentation would 17// be appreciated but is not required. 18// 19// 2. Altered source versions must be plainly marked as such, and must not 20// be misrepresented as being the original software. 21// 22// 3. This notice may not be removed or altered from any source 23// distribution. 24// 25//======================================================================== 26 27#include "internal.h" 28 29#if defined(_GLFW_COCOA) 30 31#include <unistd.h> 32#include <math.h> 33#include <assert.h> 34 35static void makeContextCurrentNSGL(_GLFWwindow* window) 36{ 37 @autoreleasepool { 38 39 if (window) 40 [window->context.nsgl.object makeCurrentContext]; 41 else 42 [NSOpenGLContext clearCurrentContext]; 43 44 _glfwPlatformSetTls(&_glfw.contextSlot, window); 45 46 } // autoreleasepool 47} 48 49static void swapBuffersNSGL(_GLFWwindow* window) 50{ 51 @autoreleasepool { 52 53 // HACK: Simulate vsync with usleep as NSGL swap interval does not apply to 54 // windows with a non-visible occlusion state 55 if (window->ns.occluded) 56 { 57 int interval = 0; 58 [window->context.nsgl.object getValues:&interval 59 forParameter:NSOpenGLContextParameterSwapInterval]; 60 61 if (interval > 0) 62 { 63 const double framerate = 60.0; 64 const uint64_t frequency = _glfwPlatformGetTimerFrequency(); 65 const uint64_t value = _glfwPlatformGetTimerValue(); 66 67 const double elapsed = value / (double) frequency; 68 const double period = 1.0 / framerate; 69 const double delay = period - fmod(elapsed, period); 70 71 usleep(floorl(delay * 1e6)); 72 } 73 } 74 75 [window->context.nsgl.object flushBuffer]; 76 77 } // autoreleasepool 78} 79 80static void swapIntervalNSGL(int interval) 81{ 82 @autoreleasepool { 83 84 _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); 85 assert(window != NULL); 86 87 [window->context.nsgl.object setValues:&interval 88 forParameter:NSOpenGLContextParameterSwapInterval]; 89 90 } // autoreleasepool 91} 92 93static int extensionSupportedNSGL(const char* extension) 94{ 95 // There are no NSGL extensions 96 return GLFW_FALSE; 97} 98 99static GLFWglproc getProcAddressNSGL(const char* procname) 100{ 101 CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, 102 procname, 103 kCFStringEncodingASCII); 104 105 GLFWglproc symbol = CFBundleGetFunctionPointerForName(_glfw.nsgl.framework, 106 symbolName); 107 108 CFRelease(symbolName); 109 110 return symbol; 111} 112 113static void destroyContextNSGL(_GLFWwindow* window) 114{ 115 @autoreleasepool { 116 117 [window->context.nsgl.pixelFormat release]; 118 window->context.nsgl.pixelFormat = nil; 119 120 [window->context.nsgl.object release]; 121 window->context.nsgl.object = nil; 122 123 } // autoreleasepool 124} 125 126 127////////////////////////////////////////////////////////////////////////// 128////// GLFW internal API ////// 129////////////////////////////////////////////////////////////////////////// 130 131// Initialize OpenGL support 132// 133GLFWbool _glfwInitNSGL(void) 134{ 135 if (_glfw.nsgl.framework) 136 return GLFW_TRUE; 137 138 _glfw.nsgl.framework = 139 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); 140 if (_glfw.nsgl.framework == NULL) 141 { 142 _glfwInputError(GLFW_API_UNAVAILABLE, 143 "NSGL: Failed to locate OpenGL framework"); 144 return GLFW_FALSE; 145 } 146 147 return GLFW_TRUE; 148} 149 150// Terminate OpenGL support 151// 152void _glfwTerminateNSGL(void) 153{ 154} 155 156// Create the OpenGL context 157// 158GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, 159 const _GLFWctxconfig* ctxconfig, 160 const _GLFWfbconfig* fbconfig) 161{ 162 if (ctxconfig->client == GLFW_OPENGL_ES_API) 163 { 164 _glfwInputError(GLFW_API_UNAVAILABLE, 165 "NSGL: OpenGL ES is not available via NSGL"); 166 return GLFW_FALSE; 167 } 168 169 if (ctxconfig->major > 2) 170 { 171 if (ctxconfig->major == 3 && ctxconfig->minor < 2) 172 { 173 _glfwInputError(GLFW_VERSION_UNAVAILABLE, 174 "NSGL: The targeted version of macOS does not support OpenGL 3.0 or 3.1 but may support 3.2 and above"); 175 return GLFW_FALSE; 176 } 177 } 178 179 if (ctxconfig->major >= 3 && ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) 180 { 181 _glfwInputError(GLFW_VERSION_UNAVAILABLE, 182 "NSGL: The compatibility profile is not available on macOS"); 183 return GLFW_FALSE; 184 } 185 186 // Context robustness modes (GL_KHR_robustness) are not supported by 187 // macOS but are not a hard constraint, so ignore and continue 188 189 // Context release behaviors (GL_KHR_context_flush_control) are not 190 // supported by macOS but are not a hard constraint, so ignore and continue 191 192 // Debug contexts (GL_KHR_debug) are not supported by macOS but are not 193 // a hard constraint, so ignore and continue 194 195 // No-error contexts (GL_KHR_no_error) are not supported by macOS but 196 // are not a hard constraint, so ignore and continue 197 198#define ADD_ATTRIB(a) \ 199{ \ 200 assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ 201 attribs[index++] = a; \ 202} 203#define SET_ATTRIB(a, v) { ADD_ATTRIB(a); ADD_ATTRIB(v); } 204 205 NSOpenGLPixelFormatAttribute attribs[40]; 206 int index = 0; 207 208 ADD_ATTRIB(NSOpenGLPFAAccelerated); 209 ADD_ATTRIB(NSOpenGLPFAClosestPolicy); 210 211 if (ctxconfig->nsgl.offline) 212 { 213 ADD_ATTRIB(NSOpenGLPFAAllowOfflineRenderers); 214 // NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in 215 // Info.plist for unbundled applications 216 // HACK: This assumes that NSOpenGLPixelFormat will remain 217 // a straightforward wrapper of its CGL counterpart 218 ADD_ATTRIB(kCGLPFASupportsAutomaticGraphicsSwitching); 219 } 220 221 if (ctxconfig->major >= 4) 222 { 223 SET_ATTRIB(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); 224 } 225 else if (ctxconfig->major >= 3) 226 { 227 SET_ATTRIB(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); 228 } 229 230 if (ctxconfig->major <= 2) 231 { 232 if (fbconfig->auxBuffers != GLFW_DONT_CARE) 233 SET_ATTRIB(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); 234 235 if (fbconfig->accumRedBits != GLFW_DONT_CARE && 236 fbconfig->accumGreenBits != GLFW_DONT_CARE && 237 fbconfig->accumBlueBits != GLFW_DONT_CARE && 238 fbconfig->accumAlphaBits != GLFW_DONT_CARE) 239 { 240 const int accumBits = fbconfig->accumRedBits + 241 fbconfig->accumGreenBits + 242 fbconfig->accumBlueBits + 243 fbconfig->accumAlphaBits; 244 245 SET_ATTRIB(NSOpenGLPFAAccumSize, accumBits); 246 } 247 } 248 249 if (fbconfig->redBits != GLFW_DONT_CARE && 250 fbconfig->greenBits != GLFW_DONT_CARE && 251 fbconfig->blueBits != GLFW_DONT_CARE) 252 { 253 int colorBits = fbconfig->redBits + 254 fbconfig->greenBits + 255 fbconfig->blueBits; 256 257 // macOS needs non-zero color size, so set reasonable values 258 if (colorBits == 0) 259 colorBits = 24; 260 else if (colorBits < 15) 261 colorBits = 15; 262 263 SET_ATTRIB(NSOpenGLPFAColorSize, colorBits); 264 } 265 266 if (fbconfig->alphaBits != GLFW_DONT_CARE) 267 SET_ATTRIB(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); 268 269 if (fbconfig->depthBits != GLFW_DONT_CARE) 270 SET_ATTRIB(NSOpenGLPFADepthSize, fbconfig->depthBits); 271 272 if (fbconfig->stencilBits != GLFW_DONT_CARE) 273 SET_ATTRIB(NSOpenGLPFAStencilSize, fbconfig->stencilBits); 274 275 if (fbconfig->stereo) 276 { 277#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 278 _glfwInputError(GLFW_FORMAT_UNAVAILABLE, 279 "NSGL: Stereo rendering is deprecated"); 280 return GLFW_FALSE; 281#else 282 ADD_ATTRIB(NSOpenGLPFAStereo); 283#endif 284 } 285 286 if (fbconfig->doublebuffer) 287 ADD_ATTRIB(NSOpenGLPFADoubleBuffer); 288 289 if (fbconfig->samples != GLFW_DONT_CARE) 290 { 291 if (fbconfig->samples == 0) 292 { 293 SET_ATTRIB(NSOpenGLPFASampleBuffers, 0); 294 } 295 else 296 { 297 SET_ATTRIB(NSOpenGLPFASampleBuffers, 1); 298 SET_ATTRIB(NSOpenGLPFASamples, fbconfig->samples); 299 } 300 } 301 302 // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB 303 // framebuffer, so there's no need (and no way) to request it 304 305 ADD_ATTRIB(0); 306 307#undef ADD_ATTRIB 308#undef SET_ATTRIB 309 310 window->context.nsgl.pixelFormat = 311 [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; 312 if (window->context.nsgl.pixelFormat == nil) 313 { 314 _glfwInputError(GLFW_FORMAT_UNAVAILABLE, 315 "NSGL: Failed to find a suitable pixel format"); 316 return GLFW_FALSE; 317 } 318 319 NSOpenGLContext* share = nil; 320 321 if (ctxconfig->share) 322 share = ctxconfig->share->context.nsgl.object; 323 324 window->context.nsgl.object = 325 [[NSOpenGLContext alloc] initWithFormat:window->context.nsgl.pixelFormat 326 shareContext:share]; 327 if (window->context.nsgl.object == nil) 328 { 329 _glfwInputError(GLFW_VERSION_UNAVAILABLE, 330 "NSGL: Failed to create OpenGL context"); 331 return GLFW_FALSE; 332 } 333 334 if (fbconfig->transparent) 335 { 336 GLint opaque = 0; 337 [window->context.nsgl.object setValues:&opaque 338 forParameter:NSOpenGLContextParameterSurfaceOpacity]; 339 } 340 341 [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.scaleFramebuffer]; 342 343 [window->context.nsgl.object setView:window->ns.view]; 344 345 window->context.makeCurrent = makeContextCurrentNSGL; 346 window->context.swapBuffers = swapBuffersNSGL; 347 window->context.swapInterval = swapIntervalNSGL; 348 window->context.extensionSupported = extensionSupportedNSGL; 349 window->context.getProcAddress = getProcAddressNSGL; 350 window->context.destroy = destroyContextNSGL; 351 352 return GLFW_TRUE; 353} 354 355 356////////////////////////////////////////////////////////////////////////// 357////// GLFW native API ////// 358////////////////////////////////////////////////////////////////////////// 359 360GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle) 361{ 362 _GLFW_REQUIRE_INIT_OR_RETURN(nil); 363 364 if (_glfw.platform.platformID != GLFW_PLATFORM_COCOA) 365 { 366 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, 367 "NSGL: Platform not initialized"); 368 return nil; 369 } 370 371 _GLFWwindow* window = (_GLFWwindow*) handle; 372 assert(window != NULL); 373 374 if (window->context.source != GLFW_NATIVE_CONTEXT_API) 375 { 376 _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); 377 return nil; 378 } 379 380 return window->context.nsgl.object; 381} 382 383#endif // _GLFW_COCOA 384 385