glfw/lib/carbon/carbon_window.c
2010-09-09 02:33:14 +02:00

1276 lines
45 KiB
C

//========================================================================
// 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 <elmindreda@elmindreda.org>
// 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));
}
}