1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "../../SDL_internal.h" 22 23/* NSOpenGL implementation of SDL OpenGL support */ 24 25#if SDL_VIDEO_OPENGL_CGL 26#include "SDL_cocoavideo.h" 27#include "SDL_cocoaopengl.h" 28 29#include <OpenGL/CGLTypes.h> 30#include <OpenGL/OpenGL.h> 31#include <OpenGL/CGLRenderers.h> 32 33#include "SDL_loadso.h" 34#include "SDL_opengl.h" 35 36#define DEFAULT_OPENGL "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib" 37 38@implementation SDLOpenGLContext : NSOpenGLContext 39 40- (id)initWithFormat:(NSOpenGLPixelFormat *)format 41 shareContext:(NSOpenGLContext *)share 42{ 43 self = [super initWithFormat:format shareContext:share]; 44 if (self) { 45 SDL_AtomicSet(&self->dirty, 0); 46 self->window = NULL; 47 } 48 return self; 49} 50 51- (void)scheduleUpdate 52{ 53 SDL_AtomicAdd(&self->dirty, 1); 54} 55 56/* This should only be called on the thread on which a user is using the context. */ 57- (void)updateIfNeeded 58{ 59 int value = SDL_AtomicSet(&self->dirty, 0); 60 if (value > 0) { 61 /* We call the real underlying update here, since -[SDLOpenGLContext update] just calls us. */ 62 [super update]; 63 } 64} 65 66/* This should only be called on the thread on which a user is using the context. */ 67- (void)update 68{ 69 /* This ensures that regular 'update' calls clear the atomic dirty flag. */ 70 [self scheduleUpdate]; 71 [self updateIfNeeded]; 72} 73 74/* Updates the drawable for the contexts and manages related state. */ 75- (void)setWindow:(SDL_Window *)newWindow 76{ 77 if (self->window) { 78 SDL_WindowData *oldwindowdata = (SDL_WindowData *)self->window->driverdata; 79 80 /* Make sure to remove us from the old window's context list, or we'll get scheduled updates from it too. */ 81 NSMutableArray *contexts = oldwindowdata->nscontexts; 82 @synchronized (contexts) { 83 [contexts removeObject:self]; 84 } 85 } 86 87 self->window = newWindow; 88 89 if (newWindow) { 90 SDL_WindowData *windowdata = (SDL_WindowData *)newWindow->driverdata; 91 92 /* Now sign up for scheduled updates for the new window. */ 93 NSMutableArray *contexts = windowdata->nscontexts; 94 @synchronized (contexts) { 95 [contexts addObject:self]; 96 } 97 98 if ([self view] != [windowdata->nswindow contentView]) { 99 [self setView:[windowdata->nswindow contentView]]; 100 if (self == [NSOpenGLContext currentContext]) { 101 [self update]; 102 } else { 103 [self scheduleUpdate]; 104 } 105 } 106 } else { 107 [self clearDrawable]; 108 if (self == [NSOpenGLContext currentContext]) { 109 [self update]; 110 } else { 111 [self scheduleUpdate]; 112 } 113 } 114} 115 116@end 117 118 119int 120Cocoa_GL_LoadLibrary(_THIS, const char *path) 121{ 122 /* Load the OpenGL library */ 123 if (path == NULL) { 124 path = SDL_getenv("SDL_OPENGL_LIBRARY"); 125 } 126 if (path == NULL) { 127 path = DEFAULT_OPENGL; 128 } 129 _this->gl_config.dll_handle = SDL_LoadObject(path); 130 if (!_this->gl_config.dll_handle) { 131 return -1; 132 } 133 SDL_strlcpy(_this->gl_config.driver_path, path, 134 SDL_arraysize(_this->gl_config.driver_path)); 135 return 0; 136} 137 138void * 139Cocoa_GL_GetProcAddress(_THIS, const char *proc) 140{ 141 return SDL_LoadFunction(_this->gl_config.dll_handle, proc); 142} 143 144void 145Cocoa_GL_UnloadLibrary(_THIS) 146{ 147 SDL_UnloadObject(_this->gl_config.dll_handle); 148 _this->gl_config.dll_handle = NULL; 149} 150 151SDL_GLContext 152Cocoa_GL_CreateContext(_THIS, SDL_Window * window) 153{ @autoreleasepool 154{ 155 SDL_VideoDisplay *display = SDL_GetDisplayForWindow(window); 156 SDL_DisplayData *displaydata = (SDL_DisplayData *)display->driverdata; 157 SDL_bool lion_or_later = floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_6; 158 NSOpenGLPixelFormatAttribute attr[32]; 159 NSOpenGLPixelFormat *fmt; 160 SDLOpenGLContext *context; 161 NSOpenGLContext *share_context = nil; 162 int i = 0; 163 const char *glversion; 164 int glversion_major; 165 int glversion_minor; 166 167 if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_ES) { 168 SDL_SetError ("OpenGL ES is not supported on this platform"); 169 return NULL; 170 } 171 if ((_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) && !lion_or_later) { 172 SDL_SetError ("OpenGL Core Profile is not supported on this platform version"); 173 return NULL; 174 } 175 176 attr[i++] = NSOpenGLPFAAllowOfflineRenderers; 177 178 /* specify a profile if we're on Lion (10.7) or later. */ 179 if (lion_or_later) { 180 NSOpenGLPixelFormatAttribute profile = NSOpenGLProfileVersionLegacy; 181 if (_this->gl_config.profile_mask == SDL_GL_CONTEXT_PROFILE_CORE) { 182 profile = NSOpenGLProfileVersion3_2Core; 183 } 184 attr[i++] = NSOpenGLPFAOpenGLProfile; 185 attr[i++] = profile; 186 } 187 188 attr[i++] = NSOpenGLPFAColorSize; 189 attr[i++] = SDL_BYTESPERPIXEL(display->current_mode.format)*8; 190 191 attr[i++] = NSOpenGLPFADepthSize; 192 attr[i++] = _this->gl_config.depth_size; 193 194 if (_this->gl_config.double_buffer) { 195 attr[i++] = NSOpenGLPFADoubleBuffer; 196 } 197 198 if (_this->gl_config.stereo) { 199 attr[i++] = NSOpenGLPFAStereo; 200 } 201 202 if (_this->gl_config.stencil_size) { 203 attr[i++] = NSOpenGLPFAStencilSize; 204 attr[i++] = _this->gl_config.stencil_size; 205 } 206 207 if ((_this->gl_config.accum_red_size + 208 _this->gl_config.accum_green_size + 209 _this->gl_config.accum_blue_size + 210 _this->gl_config.accum_alpha_size) > 0) { 211 attr[i++] = NSOpenGLPFAAccumSize; 212 attr[i++] = _this->gl_config.accum_red_size + _this->gl_config.accum_green_size + _this->gl_config.accum_blue_size + _this->gl_config.accum_alpha_size; 213 } 214 215 if (_this->gl_config.multisamplebuffers) { 216 attr[i++] = NSOpenGLPFASampleBuffers; 217 attr[i++] = _this->gl_config.multisamplebuffers; 218 } 219 220 if (_this->gl_config.multisamplesamples) { 221 attr[i++] = NSOpenGLPFASamples; 222 attr[i++] = _this->gl_config.multisamplesamples; 223 attr[i++] = NSOpenGLPFANoRecovery; 224 } 225 226 if (_this->gl_config.accelerated >= 0) { 227 if (_this->gl_config.accelerated) { 228 attr[i++] = NSOpenGLPFAAccelerated; 229 } else { 230 attr[i++] = NSOpenGLPFARendererID; 231 attr[i++] = kCGLRendererGenericFloatID; 232 } 233 } 234 235 attr[i++] = NSOpenGLPFAScreenMask; 236 attr[i++] = CGDisplayIDToOpenGLDisplayMask(displaydata->display); 237 attr[i] = 0; 238 239 fmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr]; 240 if (fmt == nil) { 241 SDL_SetError("Failed creating OpenGL pixel format"); 242 return NULL; 243 } 244 245 if (_this->gl_config.share_with_current_context) { 246 share_context = (NSOpenGLContext*)SDL_GL_GetCurrentContext(); 247 } 248 249 context = [[SDLOpenGLContext alloc] initWithFormat:fmt shareContext:share_context]; 250 251 [fmt release]; 252 253 if (context == nil) { 254 SDL_SetError("Failed creating OpenGL context"); 255 return NULL; 256 } 257 258 if ( Cocoa_GL_MakeCurrent(_this, window, context) < 0 ) { 259 Cocoa_GL_DeleteContext(_this, context); 260 SDL_SetError("Failed making OpenGL context current"); 261 return NULL; 262 } 263 264 if (_this->gl_config.major_version < 3 && 265 _this->gl_config.profile_mask == 0 && 266 _this->gl_config.flags == 0) { 267 /* This is a legacy profile, so to match other backends, we're done. */ 268 } else { 269 const GLubyte *(APIENTRY * glGetStringFunc)(GLenum); 270 271 glGetStringFunc = (const GLubyte *(APIENTRY *)(GLenum)) SDL_GL_GetProcAddress("glGetString"); 272 if (!glGetStringFunc) { 273 Cocoa_GL_DeleteContext(_this, context); 274 SDL_SetError ("Failed getting OpenGL glGetString entry point"); 275 return NULL; 276 } 277 278 glversion = (const char *)glGetStringFunc(GL_VERSION); 279 if (glversion == NULL) { 280 Cocoa_GL_DeleteContext(_this, context); 281 SDL_SetError ("Failed getting OpenGL context version"); 282 return NULL; 283 } 284 285 if (SDL_sscanf(glversion, "%d.%d", &glversion_major, &glversion_minor) != 2) { 286 Cocoa_GL_DeleteContext(_this, context); 287 SDL_SetError ("Failed parsing OpenGL context version"); 288 return NULL; 289 } 290 291 if ((glversion_major < _this->gl_config.major_version) || 292 ((glversion_major == _this->gl_config.major_version) && (glversion_minor < _this->gl_config.minor_version))) { 293 Cocoa_GL_DeleteContext(_this, context); 294 SDL_SetError ("Failed creating OpenGL context at version requested"); 295 return NULL; 296 } 297 298 /* In the future we'll want to do this, but to match other platforms 299 we'll leave the OpenGL version the way it is for now 300 */ 301 /*_this->gl_config.major_version = glversion_major;*/ 302 /*_this->gl_config.minor_version = glversion_minor;*/ 303 } 304 return context; 305}} 306 307int 308Cocoa_GL_MakeCurrent(_THIS, SDL_Window * window, SDL_GLContext context) 309{ @autoreleasepool 310{ 311 if (context) { 312 SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context; 313 [nscontext setWindow:window]; 314 [nscontext updateIfNeeded]; 315 [nscontext makeCurrentContext]; 316 } else { 317 [NSOpenGLContext clearCurrentContext]; 318 } 319 320 return 0; 321}} 322 323void 324Cocoa_GL_GetDrawableSize(_THIS, SDL_Window * window, int * w, int * h) 325{ 326 SDL_WindowData *windata = (SDL_WindowData *) window->driverdata; 327 NSView *contentView = [windata->nswindow contentView]; 328 NSRect viewport = [contentView bounds]; 329 330 /* This gives us the correct viewport for a Retina-enabled view, only 331 * supported on 10.7+. */ 332 if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) { 333 viewport = [contentView convertRectToBacking:viewport]; 334 } 335 336 if (w) { 337 *w = viewport.size.width; 338 } 339 340 if (h) { 341 *h = viewport.size.height; 342 } 343} 344 345int 346Cocoa_GL_SetSwapInterval(_THIS, int interval) 347{ @autoreleasepool 348{ 349 NSOpenGLContext *nscontext; 350 GLint value; 351 int status; 352 353 if (interval < 0) { /* no extension for this on Mac OS X at the moment. */ 354 return SDL_SetError("Late swap tearing currently unsupported"); 355 } 356 357 nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext(); 358 if (nscontext != nil) { 359 value = interval; 360 [nscontext setValues:&value forParameter:NSOpenGLCPSwapInterval]; 361 status = 0; 362 } else { 363 status = SDL_SetError("No current OpenGL context"); 364 } 365 366 return status; 367}} 368 369int 370Cocoa_GL_GetSwapInterval(_THIS) 371{ @autoreleasepool 372{ 373 NSOpenGLContext *nscontext; 374 GLint value; 375 int status = 0; 376 377 nscontext = (NSOpenGLContext*)SDL_GL_GetCurrentContext(); 378 if (nscontext != nil) { 379 [nscontext getValues:&value forParameter:NSOpenGLCPSwapInterval]; 380 status = (int)value; 381 } 382 383 return status; 384}} 385 386void 387Cocoa_GL_SwapWindow(_THIS, SDL_Window * window) 388{ @autoreleasepool 389{ 390 SDLOpenGLContext* nscontext = (SDLOpenGLContext*)SDL_GL_GetCurrentContext(); 391 [nscontext flushBuffer]; 392 [nscontext updateIfNeeded]; 393}} 394 395void 396Cocoa_GL_DeleteContext(_THIS, SDL_GLContext context) 397{ @autoreleasepool 398{ 399 SDLOpenGLContext *nscontext = (SDLOpenGLContext *)context; 400 401 [nscontext setWindow:NULL]; 402 [nscontext release]; 403}} 404 405#endif /* SDL_VIDEO_OPENGL_CGL */ 406 407/* vi: set ts=4 sw=4 expandtab: */ 408