//======================================================================== // GLFW - An OpenGL framework // Platform: Carbon/AGL/CGL // API Version: 3.0 // WWW: http://www.glfw.org/ //------------------------------------------------------------------------ // Copyright (c) 2002-2006 Marcus Geelnard // Copyright (c) 2003 Keith Bauer // Copyright (c) 2003-2010 Camilla Berglund // Copyright (c) 2006-2007 Robin Leffmann // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would // be appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not // be misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // //======================================================================== #include "internal.h" #define _glfwTestModifier(modifierMask, glfwKey) \ if (changed & modifierMask) \ { \ _glfwInputKey(glfwKey, (modifiers & modifierMask ? GLFW_PRESS : GLFW_RELEASE)); \ } //************************************************************************ //**** GLFW internal functions **** //************************************************************************ static void handleMacModifierChange(UInt32 modifiers) { UInt32 changed = modifiers ^ _glfwInput.Modifiers; // The right *key variants below never actually occur // There also isn't even a broken right command key constant _glfwTestModifier(shiftKey, GLFW_KEY_LSHIFT); _glfwTestModifier(rightShiftKey, GLFW_KEY_RSHIFT); _glfwTestModifier(controlKey, GLFW_KEY_LCTRL); _glfwTestModifier(rightControlKey, GLFW_KEY_RCTRL); _glfwTestModifier(optionKey, GLFW_KEY_LALT); _glfwTestModifier(rightOptionKey, GLFW_KEY_RALT); _glfwTestModifier(cmdKey, GLFW_KEY_LSUPER); _glfwInput.Modifiers = modifiers; } static void handleMacKeyChange(UInt32 keyCode, int action) { switch (keyCode) { case MAC_KEY_ENTER: _glfwInputKey(GLFW_KEY_ENTER, action); break; case MAC_KEY_RETURN: _glfwInputKey(GLFW_KEY_KP_ENTER, action); break; case MAC_KEY_ESC: _glfwInputKey(GLFW_KEY_ESC, action); break; case MAC_KEY_F1: _glfwInputKey(GLFW_KEY_F1, action); break; case MAC_KEY_F2: _glfwInputKey(GLFW_KEY_F2, action); break; case MAC_KEY_F3: _glfwInputKey(GLFW_KEY_F3, action); break; case MAC_KEY_F4: _glfwInputKey(GLFW_KEY_F4, action); break; case MAC_KEY_F5: _glfwInputKey(GLFW_KEY_F5, action); break; case MAC_KEY_F6: _glfwInputKey(GLFW_KEY_F6, action); break; case MAC_KEY_F7: _glfwInputKey(GLFW_KEY_F7, action); break; case MAC_KEY_F8: _glfwInputKey(GLFW_KEY_F8, action); break; case MAC_KEY_F9: _glfwInputKey(GLFW_KEY_F9, action); break; case MAC_KEY_F10: _glfwInputKey(GLFW_KEY_F10, action); break; case MAC_KEY_F11: _glfwInputKey(GLFW_KEY_F11, action); break; case MAC_KEY_F12: _glfwInputKey(GLFW_KEY_F12, action); break; case MAC_KEY_F13: _glfwInputKey(GLFW_KEY_F13, action); break; case MAC_KEY_F14: _glfwInputKey(GLFW_KEY_F14, action); break; case MAC_KEY_F15: _glfwInputKey(GLFW_KEY_F15, action); break; case MAC_KEY_UP: _glfwInputKey(GLFW_KEY_UP, action); break; case MAC_KEY_DOWN: _glfwInputKey(GLFW_KEY_DOWN, action); break; case MAC_KEY_LEFT: _glfwInputKey(GLFW_KEY_LEFT, action); break; case MAC_KEY_RIGHT: _glfwInputKey(GLFW_KEY_RIGHT, action); break; case MAC_KEY_TAB: _glfwInputKey(GLFW_KEY_TAB, action); break; case MAC_KEY_BACKSPACE: _glfwInputKey(GLFW_KEY_BACKSPACE, action); break; case MAC_KEY_HELP: _glfwInputKey(GLFW_KEY_INSERT, action); break; case MAC_KEY_DEL: _glfwInputKey(GLFW_KEY_DEL, action); break; case MAC_KEY_PAGEUP: _glfwInputKey(GLFW_KEY_PAGEUP, action); break; case MAC_KEY_PAGEDOWN: _glfwInputKey(GLFW_KEY_PAGEDOWN, action); break; case MAC_KEY_HOME: _glfwInputKey(GLFW_KEY_HOME, action); break; case MAC_KEY_END: _glfwInputKey(GLFW_KEY_END, action); break; case MAC_KEY_KP_0: _glfwInputKey(GLFW_KEY_KP_0, action); break; case MAC_KEY_KP_1: _glfwInputKey(GLFW_KEY_KP_1, action); break; case MAC_KEY_KP_2: _glfwInputKey(GLFW_KEY_KP_2, action); break; case MAC_KEY_KP_3: _glfwInputKey(GLFW_KEY_KP_3, action); break; case MAC_KEY_KP_4: _glfwInputKey(GLFW_KEY_KP_4, action); break; case MAC_KEY_KP_5: _glfwInputKey(GLFW_KEY_KP_5, action); break; case MAC_KEY_KP_6: _glfwInputKey(GLFW_KEY_KP_6, action); break; case MAC_KEY_KP_7: _glfwInputKey(GLFW_KEY_KP_7, action); break; case MAC_KEY_KP_8: _glfwInputKey(GLFW_KEY_KP_8, action); break; case MAC_KEY_KP_9: _glfwInputKey(GLFW_KEY_KP_9, action); break; case MAC_KEY_KP_DIVIDE: _glfwInputKey(GLFW_KEY_KP_DIVIDE, action); break; case MAC_KEY_KP_MULTIPLY: _glfwInputKey(GLFW_KEY_KP_MULTIPLY, action); break; case MAC_KEY_KP_SUBTRACT: _glfwInputKey(GLFW_KEY_KP_SUBTRACT, action); break; case MAC_KEY_KP_ADD: _glfwInputKey(GLFW_KEY_KP_ADD, action); break; case MAC_KEY_KP_DECIMAL: _glfwInputKey(GLFW_KEY_KP_DECIMAL, action); break; case MAC_KEY_KP_EQUAL: _glfwInputKey(GLFW_KEY_KP_EQUAL, action); break; case MAC_KEY_KP_ENTER: _glfwInputKey(GLFW_KEY_KP_ENTER, action); break; case MAC_KEY_NUMLOCK: _glfwInputKey(GLFW_KEY_KP_NUM_LOCK, action); break; default: { extern void* KCHRPtr; UInt32 state = 0; char charCode = (char)KeyTranslate(KCHRPtr, keyCode, &state); UppercaseText(&charCode, 1, smSystemScript); _glfwInputKey((unsigned char)charCode, action); } break; } } // The set of event class/kind combinations supported by keyEventHandler // This is used by installEventHandlers below static const EventTypeSpec GLFW_KEY_EVENT_TYPES[] = { { kEventClassKeyboard, kEventRawKeyDown }, { kEventClassKeyboard, kEventRawKeyUp }, { kEventClassKeyboard, kEventRawKeyRepeat }, { kEventClassKeyboard, kEventRawKeyModifiersChanged } }; static OSStatus keyEventHandler(EventHandlerCallRef handlerCallRef, EventRef event, void* userData) { UInt32 keyCode; short int keyChar; UInt32 modifiers; switch (GetEventKind(event)) { case kEventRawKeyRepeat: case kEventRawKeyDown: { if (GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode) == noErr) { handleMacKeyChange(keyCode, GLFW_PRESS); } if (GetEventParameter(event, kEventParamKeyUnicodes, typeUnicodeText, NULL, sizeof(keyChar), NULL, &keyChar) == noErr) { _glfwInputChar(keyChar, GLFW_PRESS); } return noErr; } case kEventRawKeyUp: { if (GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode) == noErr) { handleMacKeyChange(keyCode, GLFW_RELEASE); } if (GetEventParameter(event, kEventParamKeyUnicodes, typeUnicodeText, NULL, sizeof(keyChar), NULL, &keyChar) == noErr) { _glfwInputChar(keyChar, GLFW_RELEASE); } return noErr; } case kEventRawKeyModifiersChanged: { if (GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers) == noErr) { handleMacModifierChange(modifiers); return noErr; } } break; } return eventNotHandledErr; } // The set of event class/kind combinations supported by mouseEventHandler // This is used by installEventHandlers below static const EventTypeSpec GLFW_MOUSE_EVENT_TYPES[] = { { kEventClassMouse, kEventMouseDown }, { kEventClassMouse, kEventMouseUp }, { kEventClassMouse, kEventMouseMoved }, { kEventClassMouse, kEventMouseDragged }, { kEventClassMouse, kEventMouseWheelMoved }, }; static OSStatus mouseEventHandler(EventHandlerCallRef handlerCallRef, EventRef event, void* userData) { switch (GetEventKind(event)) { case kEventMouseDown: { WindowRef window; EventRecord oldStyleMacEvent; ConvertEventRefToEventRecord(event, &oldStyleMacEvent); if (FindWindow(oldStyleMacEvent.where, &window) == inMenuBar) { MenuSelect(oldStyleMacEvent.where); HiliteMenu(0); return noErr; } else { EventMouseButton button; if (GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(EventMouseButton), NULL, &button) == noErr) { button -= kEventMouseButtonPrimary; if (button <= GLFW_MOUSE_BUTTON_LAST) { _glfwInputMouseClick(button + GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS); } return noErr; } } break; } case kEventMouseUp: { EventMouseButton button; if (GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(EventMouseButton), NULL, &button) == noErr) { button -= kEventMouseButtonPrimary; if (button <= GLFW_MOUSE_BUTTON_LAST) { _glfwInputMouseClick(button + GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE); } return noErr; } break; } case kEventMouseMoved: case kEventMouseDragged: { HIPoint mouseLocation; if (_glfwWin.mouseLock) { if (GetEventParameter(event, kEventParamMouseDelta, typeHIPoint, NULL, sizeof(HIPoint), NULL, &mouseLocation) != noErr) { break; } _glfwInput.MousePosX += mouseLocation.x; _glfwInput.MousePosY += mouseLocation.y; } else { if (GetEventParameter(event, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(HIPoint), NULL, &mouseLocation) != noErr) { break; } _glfwInput.MousePosX = mouseLocation.x; _glfwInput.MousePosY = mouseLocation.y; if (!_glfwWin.fullscreen) { Rect content; GetWindowBounds(_glfwWin.window, kWindowContentRgn, &content); _glfwInput.MousePosX -= content.left; _glfwInput.MousePosY -= content.top; } } if (_glfwWin.mousePosCallback) { _glfwWin.mousePosCallback(_glfwInput.MousePosX, _glfwInput.MousePosY); } break; } case kEventMouseWheelMoved: { EventMouseWheelAxis axis; if (GetEventParameter(event, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(EventMouseWheelAxis), NULL, &axis) == noErr) { long wheelDelta; if (axis == kEventMouseWheelAxisY && GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(long), NULL, &wheelDelta) == noErr) { _glfwInput.WheelPos += wheelDelta; if (_glfwWin.mouseWheelCallback) _glfwWin.mouseWheelCallback(_glfwInput.WheelPos); return noErr; } } break; } } return eventNotHandledErr; } // The set of event class/kind combinations supported by commandHandler // This is used by installEventHandlers below static const EventTypeSpec GLFW_COMMAND_EVENT_TYPES[] = { { kEventClassCommand, kEventCommandProcess } }; static OSStatus commandHandler(EventHandlerCallRef handlerCallRef, EventRef event, void* userData) { if (_glfwWin.sysKeysDisabled) { // TODO: Give adequate UI feedback that this is the case return eventNotHandledErr; } HICommand command; if (GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &command) == noErr) { switch (command.commandID) { case kHICommandClose: case kHICommandQuit: { // Check if the program wants us to close the window if (_glfwWin.windowCloseCallback) { if (_glfwWin.windowCloseCallback()) glfwCloseWindow(); } else glfwCloseWindow(); return noErr; } } } return eventNotHandledErr; } // The set of event class/kind combinations supported by windowEventHandler // This is used by installEventHandlers below static const EventTypeSpec GLFW_WINDOW_EVENT_TYPES[] = { { kEventClassWindow, kEventWindowBoundsChanged }, { kEventClassWindow, kEventWindowClose }, { kEventClassWindow, kEventWindowDrawContent }, { kEventClassWindow, kEventWindowActivated }, { kEventClassWindow, kEventWindowDeactivated }, }; static OSStatus windowEventHandler(EventHandlerCallRef handlerCallRef, EventRef event, void* userData) { switch (GetEventKind(event)) { case kEventWindowBoundsChanged: { WindowRef window; GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(WindowRef), NULL, &window); Rect rect; GetWindowPortBounds(window, &rect); if (_glfwWin.width != rect.right || _glfwWin.height != rect.bottom) { aglUpdateContext(_glfwWin.aglContext); _glfwWin.width = rect.right; _glfwWin.height = rect.bottom; if (_glfwWin.windowSizeCallback) _glfwWin.windowSizeCallback(_glfwWin.width, _glfwWin.height); // Emulate (force) content invalidation if (_glfwWin.windowRefreshCallback) _glfwWin.windowRefreshCallback(); } break; } case kEventWindowClose: { // Check if the client wants us to close the window if (_glfwWin.windowCloseCallback) { if (_glfwWin.windowCloseCallback()) glfwCloseWindow(); } else glfwCloseWindow(); return noErr; } case kEventWindowDrawContent: { if (_glfwWin.windowRefreshCallback) _glfwWin.windowRefreshCallback(); break; } case kEventWindowActivated: { _glfwWin.active = GL_TRUE; break; } case kEventWindowDeactivated: { _glfwWin.active = GL_FALSE; _glfwInputDeactivation(); break; } } return eventNotHandledErr; } static int installEventHandlers(void) { OSStatus error; _glfwWin.mouseUPP = NewEventHandlerUPP(mouseEventHandler); error = InstallEventHandler(GetApplicationEventTarget(), _glfwWin.mouseUPP, GetEventTypeCount(GLFW_MOUSE_EVENT_TYPES), GLFW_MOUSE_EVENT_TYPES, NULL, NULL); if (error != noErr) { fprintf(stderr, "Failed to install Carbon application mouse event handler\n"); return GL_FALSE; } _glfwWin.commandUPP = NewEventHandlerUPP(commandHandler); error = InstallEventHandler(GetApplicationEventTarget(), _glfwWin.commandUPP, GetEventTypeCount(GLFW_COMMAND_EVENT_TYPES), GLFW_COMMAND_EVENT_TYPES, NULL, NULL); if (error != noErr) { fprintf(stderr, "Failed to install Carbon application command event handler\n"); return GL_FALSE; } _glfwWin.keyboardUPP = NewEventHandlerUPP(keyEventHandler); error = InstallEventHandler(GetApplicationEventTarget(), _glfwWin.keyboardUPP, GetEventTypeCount(GLFW_KEY_EVENT_TYPES), GLFW_KEY_EVENT_TYPES, NULL, NULL); if (error != noErr) { fprintf(stderr, "Failed to install Carbon application key event handler\n"); return GL_FALSE; } return GL_TRUE; } //************************************************************************ //**** Platform implementation functions **** //************************************************************************ #define _setAGLAttribute(aglAttributeName, AGLparameter) \ if (AGLparameter != 0) \ { \ AGLpixelFormatAttributes[numAGLAttrs++] = aglAttributeName; \ AGLpixelFormatAttributes[numAGLAttrs++] = AGLparameter; \ } #define _setCGLAttribute(cglAttributeName, CGLparameter) \ if (CGLparameter != 0) \ { \ CGLpixelFormatAttributes[ numCGLAttrs++ ] = cglAttributeName; \ CGLpixelFormatAttributes[ numCGLAttrs++ ] = CGLparameter; \ } //======================================================================== // Here is where the window is created, and // the OpenGL rendering context is created //======================================================================== int _glfwPlatformOpenWindow(int width, int height, const _GLFWwndconfig* wndconfig, const _GLFWfbconfig* fbconfig) { OSStatus error; unsigned int windowAttributes; ProcessSerialNumber psn; // TODO: Break up this function! _glfwWin.windowUPP = NULL; _glfwWin.mouseUPP = NULL; _glfwWin.keyboardUPP = NULL; _glfwWin.commandUPP = NULL; _glfwWin.window = NULL; _glfwWin.aglContext = NULL; _glfwWin.aglPixelFormat = NULL; _glfwWin.cglContext = NULL; _glfwWin.cglPixelFormat = NULL; _glfwWin.refreshRate = wndconfig->refreshRate; // Fail if OpenGL 3.0 or above was requested if (wndconfig->glMajor > 2) { fprintf(stderr, "OpenGL 3.0+ is not yet supported on Mac OS X\n"); _glfwPlatformCloseWindow(); return GL_FALSE; } if (_glfwLibrary.Unbundled) { if (GetCurrentProcess(&psn) != noErr) { fprintf(stderr, "Failed to get the process serial number\n"); _glfwPlatformCloseWindow(); return GL_FALSE; } if (TransformProcessType(&psn, kProcessTransformToForegroundApplication) != noErr) { fprintf(stderr, "Failed to become a foreground application\n"); _glfwPlatformCloseWindow(); return GL_FALSE; } if (wndconfig->mode == GLFW_FULLSCREEN) { if (SetFrontProcess(&psn) != noErr) { fprintf(stderr, "Failed to become the front process\n"); _glfwPlatformCloseWindow(); return GL_FALSE; } } } if (!installEventHandlers()) { fprintf(stderr, "Failed to install Carbon application event handlers\n"); _glfwPlatformTerminate(); return GL_FALSE; } // Windowed or fullscreen; AGL or CGL? Quite the mess... // AGL appears to be the only choice for attaching OpenGL contexts to // Carbon windows, but it leaves the user no control over fullscreen // mode stretching. Solution: AGL for windowed, CGL for fullscreen. if (wndconfig->mode == GLFW_WINDOW) { // create AGL pixel format attribute list GLint AGLpixelFormatAttributes[256]; int numAGLAttrs = 0; AGLpixelFormatAttributes[numAGLAttrs++] = AGL_RGBA; AGLpixelFormatAttributes[numAGLAttrs++] = AGL_DOUBLEBUFFER; AGLpixelFormatAttributes[numAGLAttrs++] = AGL_CLOSEST_POLICY; if (fbconfig->stereo) AGLpixelFormatAttributes[numAGLAttrs++] = AGL_STEREO; _setAGLAttribute(AGL_AUX_BUFFERS, fbconfig->auxBuffers); _setAGLAttribute(AGL_RED_SIZE, fbconfig->redBits); _setAGLAttribute(AGL_GREEN_SIZE, fbconfig->greenBits); _setAGLAttribute(AGL_BLUE_SIZE, fbconfig->blueBits); _setAGLAttribute(AGL_ALPHA_SIZE, fbconfig->alphaBits); _setAGLAttribute(AGL_DEPTH_SIZE, fbconfig->depthBits); _setAGLAttribute(AGL_STENCIL_SIZE, fbconfig->stencilBits); _setAGLAttribute(AGL_ACCUM_RED_SIZE, fbconfig->accumRedBits); _setAGLAttribute(AGL_ACCUM_GREEN_SIZE, fbconfig->accumGreenBits); _setAGLAttribute(AGL_ACCUM_BLUE_SIZE, fbconfig->accumBlueBits); _setAGLAttribute(AGL_ACCUM_ALPHA_SIZE, fbconfig->accumAlphaBits); if (fbconfig->samples > 1) { _setAGLAttribute(AGL_SAMPLE_BUFFERS_ARB, 1); _setAGLAttribute(AGL_SAMPLES_ARB, fbconfig->samples); AGLpixelFormatAttributes[numAGLAttrs++] = AGL_NO_RECOVERY; } AGLpixelFormatAttributes[numAGLAttrs++] = AGL_NONE; // create pixel format descriptor AGLDevice mainMonitor = GetMainDevice(); _glfwWin.aglPixelFormat = aglChoosePixelFormat(&mainMonitor, 1, AGLpixelFormatAttributes); if (_glfwWin.aglPixelFormat == NULL) { fprintf(stderr, "Failed to choose AGL pixel format: %s\n", aglErrorString(aglGetError())); _glfwPlatformCloseWindow(); return GL_FALSE; } // create AGL context _glfwWin.aglContext = aglCreateContext(_glfwWin.aglPixelFormat, NULL); if (_glfwWin.aglContext == NULL) { fprintf(stderr, "Failed to create AGL context: %s\n", aglErrorString(aglGetError())); _glfwPlatformCloseWindow(); return GL_FALSE; } // create window Rect windowContentBounds; windowContentBounds.left = 0; windowContentBounds.top = 0; windowContentBounds.right = width; windowContentBounds.bottom = height; windowAttributes = (kWindowCloseBoxAttribute | kWindowCollapseBoxAttribute | kWindowStandardHandlerAttribute); if (wndconfig->windowNoResize) windowAttributes |= kWindowLiveResizeAttribute; else { windowAttributes |= (kWindowFullZoomAttribute | kWindowResizableAttribute); } error = CreateNewWindow(kDocumentWindowClass, windowAttributes, &windowContentBounds, &(_glfwWin.window)); if ((error != noErr) || (_glfwWin.window == NULL)) { fprintf(stderr, "Failed to create Carbon window\n"); _glfwPlatformCloseWindow(); return GL_FALSE; } _glfwWin.windowUPP = NewEventHandlerUPP(windowEventHandler); error = InstallWindowEventHandler(_glfwWin.window, _glfwWin.windowUPP, GetEventTypeCount(GLFW_WINDOW_EVENT_TYPES), GLFW_WINDOW_EVENT_TYPES, NULL, NULL); if (error != noErr) { fprintf(stderr, "Failed to install Carbon window event handler\n"); _glfwPlatformCloseWindow(); return GL_FALSE; } // Don't care if we fail here SetWindowTitleWithCFString(_glfwWin.window, CFSTR("GLFW Window")); RepositionWindow(_glfwWin.window, NULL, kWindowCenterOnMainScreen); if (!aglSetDrawable(_glfwWin.aglContext, GetWindowPort(_glfwWin.window))) { fprintf(stderr, "Failed to set the AGL context as the Carbon window drawable: %s\n", aglErrorString(aglGetError())); _glfwPlatformCloseWindow(); return GL_FALSE; } // Make OpenGL context current if (!aglSetCurrentContext(_glfwWin.aglContext)) { fprintf(stderr, "Failed to make AGL context current: %s\n", aglErrorString(aglGetError())); _glfwPlatformCloseWindow(); return GL_FALSE; } ShowWindow(_glfwWin.window); } else { CGDisplayErr cgErr; CGLError cglErr; CFDictionaryRef optimalMode; GLint numCGLvs = 0; CGLPixelFormatAttribute CGLpixelFormatAttributes[64]; int numCGLAttrs = 0; // variables for enumerating color depths GLint rgbColorDepth; // CGL pixel format attributes _setCGLAttribute(kCGLPFADisplayMask, CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay)); if (fbconfig->stereo) CGLpixelFormatAttributes[ numCGLAttrs++ ] = kCGLPFAStereo; if (fbconfig->samples > 1) { _setCGLAttribute(kCGLPFASamples, (CGLPixelFormatAttribute)fbconfig->samples); _setCGLAttribute(kCGLPFASampleBuffers, (CGLPixelFormatAttribute)1); CGLpixelFormatAttributes[ numCGLAttrs++ ] = kCGLPFANoRecovery; } CGLpixelFormatAttributes[ numCGLAttrs++ ] = kCGLPFAFullScreen; CGLpixelFormatAttributes[ numCGLAttrs++ ] = kCGLPFADoubleBuffer; CGLpixelFormatAttributes[ numCGLAttrs++ ] = kCGLPFAAccelerated; CGLpixelFormatAttributes[ numCGLAttrs++ ] = kCGLPFANoRecovery; CGLpixelFormatAttributes[ numCGLAttrs++ ] = kCGLPFAMinimumPolicy; _setCGLAttribute(kCGLPFAAccumSize, (CGLPixelFormatAttribute)( fbconfig->accumRedBits \ + fbconfig->accumGreenBits \ + fbconfig->accumBlueBits \ + fbconfig->accumAlphaBits)); _setCGLAttribute(kCGLPFAAlphaSize, (CGLPixelFormatAttribute)fbconfig->alphaBits); _setCGLAttribute(kCGLPFADepthSize, (CGLPixelFormatAttribute)fbconfig->depthBits); _setCGLAttribute(kCGLPFAStencilSize, (CGLPixelFormatAttribute)fbconfig->stencilBits); _setCGLAttribute(kCGLPFAAuxBuffers, (CGLPixelFormatAttribute)fbconfig->auxBuffers); CGLpixelFormatAttributes[ numCGLAttrs++ ] = (CGLPixelFormatAttribute)NULL; // create a suitable pixel format with above attributes.. cglErr = CGLChoosePixelFormat(CGLpixelFormatAttributes, &_glfwWin.cglPixelFormat, &numCGLvs); if (cglErr != kCGLNoError) { fprintf(stderr, "Failed to choose CGL pixel format: %s\n", CGLErrorString(cglErr)); _glfwPlatformCloseWindow(); return GL_FALSE; } // ..and create a rendering context using that pixel format cglErr = CGLCreateContext(_glfwWin.cglPixelFormat, NULL, &_glfwWin.cglContext); if (cglErr != kCGLNoError) { fprintf(stderr, "Failed to create CGL context: %s\n", CGLErrorString(cglErr)); _glfwPlatformCloseWindow(); return GL_FALSE; } // enumerate depth of RGB channels - unlike AGL, CGL works with // a single parameter reflecting the full depth of the frame buffer CGLDescribePixelFormat(_glfwWin.cglPixelFormat, 0, kCGLPFAColorSize, &rgbColorDepth); // capture the display for our application cgErr = CGCaptureAllDisplays(); if (cgErr != kCGErrorSuccess) { fprintf(stderr, "Failed to capture Core Graphics displays\n"); _glfwPlatformCloseWindow(); return GL_FALSE; } // find closest matching NON-STRETCHED display mode.. optimalMode = CGDisplayBestModeForParametersAndRefreshRateWithProperty( kCGDirectMainDisplay, rgbColorDepth, width, height, wndconfig->refreshRate, NULL, NULL); if (optimalMode == NULL) { fprintf(stderr, "Failed to retrieve Core Graphics display mode\n"); _glfwPlatformCloseWindow(); return GL_FALSE; } // ..and switch to that mode cgErr = CGDisplaySwitchToMode(kCGDirectMainDisplay, optimalMode); if (cgErr != kCGErrorSuccess) { fprintf(stderr, "Failed to switch to Core Graphics display mode\n"); _glfwPlatformCloseWindow(); return GL_FALSE; } // switch to our OpenGL context, and bring it up fullscreen cglErr = CGLSetCurrentContext(_glfwWin.cglContext); if (cglErr != kCGLNoError) { fprintf(stderr, "Failed to make CGL context current: %s\n", CGLErrorString(cglErr)); _glfwPlatformCloseWindow(); return GL_FALSE; } cglErr = CGLSetFullScreen(_glfwWin.cglContext); if (cglErr != kCGLNoError) { fprintf(stderr, "Failed to set CGL fullscreen mode: %s\n", CGLErrorString(cglErr)); _glfwPlatformCloseWindow(); return GL_FALSE; } } return GL_TRUE; } //======================================================================== // Properly kill the window/video display //======================================================================== void _glfwPlatformCloseWindow(void) { if (_glfwWin.mouseUPP != NULL) { DisposeEventHandlerUPP(_glfwWin.mouseUPP); _glfwWin.mouseUPP = NULL; } if (_glfwWin.commandUPP != NULL) { DisposeEventHandlerUPP(_glfwWin.commandUPP); _glfwWin.commandUPP = NULL; } if (_glfwWin.keyboardUPP != NULL) { DisposeEventHandlerUPP(_glfwWin.keyboardUPP); _glfwWin.keyboardUPP = NULL; } if (_glfwWin.windowUPP != NULL) { DisposeEventHandlerUPP(_glfwWin.windowUPP); _glfwWin.windowUPP = NULL; } if (_glfwWin.fullscreen) { if (_glfwWin.cglContext != NULL) { CGLSetCurrentContext(NULL); CGLClearDrawable(_glfwWin.cglContext); CGLDestroyContext(_glfwWin.cglContext); CGReleaseAllDisplays(); _glfwWin.cglContext = NULL; } if (_glfwWin.cglPixelFormat != NULL) { CGLDestroyPixelFormat(_glfwWin.cglPixelFormat); _glfwWin.cglPixelFormat = NULL; } } else { if (_glfwWin.aglContext != NULL) { aglSetCurrentContext(NULL); aglSetDrawable(_glfwWin.aglContext, NULL); aglDestroyContext(_glfwWin.aglContext); _glfwWin.aglContext = NULL; } if (_glfwWin.aglPixelFormat != NULL) { aglDestroyPixelFormat(_glfwWin.aglPixelFormat); _glfwWin.aglPixelFormat = NULL; } } if (_glfwWin.window != NULL) { ReleaseWindow(_glfwWin.window); _glfwWin.window = NULL; } } //======================================================================== // Set the window title //======================================================================== void _glfwPlatformSetWindowTitle(const char* title) { CFStringRef windowTitle; if (!_glfwWin.fullscreen) { windowTitle = CFStringCreateWithCString(kCFAllocatorDefault, title, kCFStringEncodingISOLatin1); SetWindowTitleWithCFString(_glfwWin.window, windowTitle); CFRelease(windowTitle); } } //======================================================================== // Set the window size //======================================================================== void _glfwPlatformSetWindowSize(int width, int height) { if (!_glfwWin.fullscreen) SizeWindow(_glfwWin.window, width, height, TRUE); } //======================================================================== // Set the window position //======================================================================== void _glfwPlatformSetWindowPos(int x, int y) { if (!_glfwWin.fullscreen) MoveWindow(_glfwWin.window, x, y, FALSE); } //======================================================================== // Window iconification //======================================================================== void _glfwPlatformIconifyWindow(void) { if (!_glfwWin.fullscreen) CollapseWindow(_glfwWin.window, TRUE); } //======================================================================== // Window un-iconification //======================================================================== void _glfwPlatformRestoreWindow(void) { if (!_glfwWin.fullscreen) CollapseWindow(_glfwWin.window, FALSE); } //======================================================================== // Swap buffers (double-buffering) and poll any new events //======================================================================== void _glfwPlatformSwapBuffers(void) { if (_glfwWin.fullscreen) CGLFlushDrawable(_glfwWin.cglContext); else aglSwapBuffers(_glfwWin.aglContext); } //======================================================================== // Set double buffering swap interval //======================================================================== void _glfwPlatformSwapInterval(int interval) { GLint AGLparameter = interval; // CGL doesn't seem to like intervals other than 0 (vsync off) or 1 (vsync on) long CGLparameter = (interval ? 1 : 0); if (_glfwWin.fullscreen) { // Don't care if we fail here.. CGLSetParameter(_glfwWin.cglContext, kCGLCPSwapInterval, (GLint*) &CGLparameter); } else { // ..or here aglSetInteger(_glfwWin.aglContext, AGL_SWAP_INTERVAL, &AGLparameter); } } //======================================================================== // Read back framebuffer parameters from the context //======================================================================== #define _getAGLAttribute(aglAttributeName, variableName) \ { \ GLint aglValue; \ aglDescribePixelFormat(_glfwWin.aglPixelFormat, aglAttributeName, &aglValue); \ variableName = aglValue; \ } #define _getCGLAttribute(cglAttributeName, variableName) \ { \ GLint cglValue; \ CGLDescribePixelFormat(_glfwWin.cglPixelFormat, 0, cglAttributeName, &cglValue); \ variableName = cglValue; \ } void _glfwPlatformRefreshWindowParams(void) { GLint rgbColorDepth; GLint rgbaAccumDepth = 0; GLint rgbChannelDepth = 0; if (_glfwWin.fullscreen) { _getCGLAttribute(kCGLPFAAccelerated, _glfwWin.accelerated); _getCGLAttribute(kCGLPFAAlphaSize, _glfwWin.alphaBits); _getCGLAttribute(kCGLPFADepthSize, _glfwWin.depthBits); _getCGLAttribute(kCGLPFAStencilSize, _glfwWin.stencilBits); _getCGLAttribute(kCGLPFAAuxBuffers, _glfwWin.auxBuffers); _getCGLAttribute(kCGLPFAStereo, _glfwWin.stereo); _getCGLAttribute(kCGLPFASamples, _glfwWin.samples); // Enumerate depth of RGB channels - unlike AGL, CGL works with // a single parameter reflecting the full depth of the frame buffer CGLDescribePixelFormat(_glfwWin.cglPixelFormat, 0, kCGLPFAColorSize, &rgbColorDepth); if (rgbColorDepth == 24 || rgbColorDepth == 32) rgbChannelDepth = 8; if (rgbColorDepth == 16) rgbChannelDepth = 5; _glfwWin.redBits = rgbChannelDepth; _glfwWin.greenBits = rgbChannelDepth; _glfwWin.blueBits = rgbChannelDepth; // Get pixel depth of accumulator - I haven't got the slightest idea // how this number conforms to any other channel depth than 8 bits, // so this might end up giving completely knackered results... _getCGLAttribute(kCGLPFAColorSize, rgbaAccumDepth); if (rgbaAccumDepth == 32) rgbaAccumDepth = 8; _glfwWin.accumRedBits = rgbaAccumDepth; _glfwWin.accumGreenBits = rgbaAccumDepth; _glfwWin.accumBlueBits = rgbaAccumDepth; _glfwWin.accumAlphaBits = rgbaAccumDepth; } else { _getAGLAttribute(AGL_ACCELERATED, _glfwWin.accelerated); _getAGLAttribute(AGL_RED_SIZE, _glfwWin.redBits); _getAGLAttribute(AGL_GREEN_SIZE, _glfwWin.greenBits); _getAGLAttribute(AGL_BLUE_SIZE, _glfwWin.blueBits); _getAGLAttribute(AGL_ALPHA_SIZE, _glfwWin.alphaBits); _getAGLAttribute(AGL_DEPTH_SIZE, _glfwWin.depthBits); _getAGLAttribute(AGL_STENCIL_SIZE, _glfwWin.stencilBits); _getAGLAttribute(AGL_ACCUM_RED_SIZE, _glfwWin.accumRedBits); _getAGLAttribute(AGL_ACCUM_GREEN_SIZE, _glfwWin.accumGreenBits); _getAGLAttribute(AGL_ACCUM_BLUE_SIZE, _glfwWin.accumBlueBits); _getAGLAttribute(AGL_ACCUM_ALPHA_SIZE, _glfwWin.accumAlphaBits); _getAGLAttribute(AGL_AUX_BUFFERS, _glfwWin.auxBuffers); _getAGLAttribute(AGL_STEREO, _glfwWin.stereo); _getAGLAttribute(AGL_SAMPLES_ARB, _glfwWin.samples); } } //======================================================================== // Poll for new window and input events //======================================================================== void _glfwPlatformPollEvents(void) { EventRef event; EventTargetRef eventDispatcher = GetEventDispatcherTarget(); while (ReceiveNextEvent(0, NULL, 0.0, TRUE, &event) == noErr) { SendEventToEventTarget(event, eventDispatcher); ReleaseEvent(event); } } //======================================================================== // Wait for new window and input events //======================================================================== void _glfwPlatformWaitEvents(void) { EventRef event; // Wait for new events ReceiveNextEvent(0, NULL, kEventDurationForever, FALSE, &event); // Process the new events _glfwPlatformPollEvents(); } //======================================================================== // Hide mouse cursor (lock it) //======================================================================== void _glfwPlatformHideMouseCursor(void) { CGDisplayHideCursor(kCGDirectMainDisplay); CGAssociateMouseAndMouseCursorPosition(false); } //======================================================================== // Show mouse cursor (unlock it) //======================================================================== void _glfwPlatformShowMouseCursor(void) { CGDisplayShowCursor(kCGDirectMainDisplay); CGAssociateMouseAndMouseCursorPosition(true); } //======================================================================== // Set physical mouse cursor position //======================================================================== void _glfwPlatformSetMouseCursorPos(int x, int y) { Rect content; if (_glfwWin.fullscreen) { CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, CGPointMake(x, y)); } else { GetWindowBounds(_glfwWin.window, kWindowContentRgn, &content); _glfwInput.MousePosX = x + content.left; _glfwInput.MousePosY = y + content.top; CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, CGPointMake(_glfwInput.MousePosX, _glfwInput.MousePosY)); } }