//======================================================================== // GLFW - An OpenGL framework // Platform: Carbon/AGL/CGL // API Version: 2.7 // 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 (void)SetWindowTitleWithCFString( _glfwWin.window, CFSTR( "GLFW Window" ) ); (void)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 (void)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 ); (void)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 ) { (void)CollapseWindow( _glfwWin.window, TRUE ); } } //======================================================================== // Window un-iconification //======================================================================== void _glfwPlatformRestoreWindow( void ) { if( !_glfwWin.fullscreen ) { (void)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.. (void)CGLSetParameter( _glfwWin.cglContext, kCGLCPSwapInterval, (GLint*) &CGLparameter ); } else { // ..or here (void)aglSetInteger( _glfwWin.aglContext, AGL_SWAP_INTERVAL, &AGLparameter ); } } //======================================================================== // Read back framebuffer parameters from the context //======================================================================== #define _getAGLAttribute( aglAttributeName, variableName ) \ { \ GLint aglValue; \ (void)aglDescribePixelFormat( _glfwWin.aglPixelFormat, aglAttributeName, &aglValue ); \ variableName = aglValue; \ } #define _getCGLAttribute( cglAttributeName, variableName ) \ { \ GLint cglValue; \ (void)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 (void)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 ) ); } }