/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield 
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 * OpenSceneGraph Public License for more details.
*/

#ifdef __APPLE__
#include <osg/observer_ptr>

#include <osgViewer/api/Carbon/PixelBufferCarbon>
#include <osgViewer/api/Carbon/GraphicsWindowCarbon>

#include <osg/DeleteHandler>

#include <Carbon/Carbon.h>
#include <OpenGL/OpenGL.h>

#include <iostream>

using namespace osgViewer;


// Carbon-Eventhandler to handle the click in the close-widget and the resize of windows

static pascal OSStatus GraphicsWindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void* userData) 
{
    WindowRef           window;
    Rect                bounds;
    OSStatus            result = eventNotHandledErr; /* report failure by default */
    
    osg::notify(osg::INFO) << "GraphicsWindowEventHandler" << std::endl;

    GraphicsWindowCarbon* w = (GraphicsWindowCarbon*)userData;
    if (!w)
        return result;
    
    GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL,
                         sizeof(window), NULL, &window);
                         
    switch(GetEventClass(event))
    {
        case kEventClassTablet:
        case kEventClassMouse:
            if (w->handleMouseEvent(event))
                result = noErr;
            break;
               
        case kEventClassKeyboard:
            if (w->handleKeyboardEvent(event))
                result = noErr;
            break;
            
        case kEventClassWindow: {
        
            switch (GetEventKind(event))
                {
                    case kEventWindowBoundsChanging:
                        // left the code for live-resizing, but it is not used, because of window-refreshing issues...
                        GetEventParameter( event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &bounds );
                        
                        w->resized(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top);
                        w->getEventQueue()->windowResize(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top, w->getEventQueue()->getTime());
                        w->requestRedraw();
                        result = noErr;
                        break;
                            
                    case kEventWindowBoundsChanged:
                        InvalWindowRect(window, GetWindowPortBounds(window, &bounds));
                        GetWindowBounds(window, kWindowContentRgn, &bounds);
                        w->resized(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top);
                        w->getEventQueue()->windowResize(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top, w->getEventQueue()->getTime());
                        result = noErr;
                        break;
                    
                    case kEventWindowClose:
                        w->requestClose();
                        result = noErr;
                        break;
         
                    default: 
                        break;
                }
            }
        default:
            //std::cout << "unknown: " << GetEventClass(event) << std::endl;
            break;
    }
    
    //if (result == eventNotHandledErr)
    //    result = CallNextEventHandler (nextHandler, event);
        
    return result;
}


static bool s_quit_requested = false;

// Application eventhandler -- listens for a quit-event 
static pascal OSStatus ApplicationEventHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
{
    
    HICommand commandStruct;

    OSErr  err = eventNotHandledErr;

    GetEventParameter (inEvent, kEventParamDirectObject, typeHICommand, NULL, sizeof(HICommand), NULL, &commandStruct);

    switch(commandStruct.commandID) {
        case kHICommandQuit:
            s_quit_requested = true;
            err = noErr;
            break;
                    
    }
    
    return err;
}

// AppleEventHandler, listens to the Quit-AppleEvent
static pascal OSErr QuitAppleEventHandler(const AppleEvent *theAppleEvent, AppleEvent *reply, long handlerRefcon ) {
    s_quit_requested = true;
    return (noErr);
}


namespace osgViewer
{

// small helper class which maps the raw key codes to osgGA::GUIEventAdapter::Keys

class OSXKeyboardMap {

    public:
        OSXKeyboardMap()
        {
            _keymap[53                ] =  osgGA::GUIEventAdapter::KEY_Escape;
            _keymap[115                ] =  osgGA::GUIEventAdapter::KEY_Home;
            _keymap[76                ] =  osgGA::GUIEventAdapter::KEY_KP_Enter;
            _keymap[119                ] =  osgGA::GUIEventAdapter::KEY_End;
            _keymap[36                ] =  osgGA::GUIEventAdapter::KEY_Return;
            _keymap[116                ] =  osgGA::GUIEventAdapter::KEY_Page_Up;
            _keymap[121                ] = osgGA::GUIEventAdapter::KEY_Page_Down;
            _keymap[123                ] = osgGA::GUIEventAdapter::KEY_Left;
            _keymap[124                ] = osgGA::GUIEventAdapter::KEY_Right;
            _keymap[126                ] = osgGA::GUIEventAdapter::KEY_Up;
            _keymap[125                ] = osgGA::GUIEventAdapter::KEY_Down;
            _keymap[51                ] = osgGA::GUIEventAdapter::KEY_BackSpace;
            _keymap[48                ] = osgGA::GUIEventAdapter::KEY_Tab;
            _keymap[49                ] = osgGA::GUIEventAdapter::KEY_Space;
            _keymap[117                ] = osgGA::GUIEventAdapter::KEY_Delete;
            
            _keymap[122                    ] = osgGA::GUIEventAdapter::KEY_F1;
            _keymap[120                    ] = osgGA::GUIEventAdapter::KEY_F2;
            _keymap[99                    ] = osgGA::GUIEventAdapter::KEY_F3;
            _keymap[118                    ] = osgGA::GUIEventAdapter::KEY_F4;
            _keymap[96                    ] = osgGA::GUIEventAdapter::KEY_F5;
            _keymap[97                    ] = osgGA::GUIEventAdapter::KEY_F6;
            _keymap[98                    ] = osgGA::GUIEventAdapter::KEY_F7;
            _keymap[100                    ] = osgGA::GUIEventAdapter::KEY_F8;
            _keymap[101                    ] = osgGA::GUIEventAdapter::KEY_F9;
            _keymap[109                    ] = osgGA::GUIEventAdapter::KEY_F10;
            _keymap[103                    ] = osgGA::GUIEventAdapter::KEY_F11;
            _keymap[111                    ] = osgGA::GUIEventAdapter::KEY_F12;
            
            _keymap[75                    ] = osgGA::GUIEventAdapter::KEY_KP_Divide;
            _keymap[67                    ] = osgGA::GUIEventAdapter::KEY_KP_Multiply;
            _keymap[78                    ] = osgGA::GUIEventAdapter::KEY_KP_Subtract;
            _keymap[69                    ] = osgGA::GUIEventAdapter::KEY_KP_Add;
            _keymap[89                    ] = osgGA::GUIEventAdapter::KEY_KP_Home;
            _keymap[91                    ] = osgGA::GUIEventAdapter::KEY_KP_Up;
            _keymap[92                    ] = osgGA::GUIEventAdapter::KEY_KP_Page_Up;
            _keymap[86                    ] = osgGA::GUIEventAdapter::KEY_KP_Left;
            _keymap[87                    ] = osgGA::GUIEventAdapter::KEY_KP_Begin;
            _keymap[88                    ] = osgGA::GUIEventAdapter::KEY_KP_Right;
            _keymap[83                    ] = osgGA::GUIEventAdapter::KEY_KP_End;
            _keymap[84                    ] = osgGA::GUIEventAdapter::KEY_KP_Down;
            _keymap[85                    ] = osgGA::GUIEventAdapter::KEY_KP_Page_Down;
            _keymap[82                    ] = osgGA::GUIEventAdapter::KEY_KP_Insert;
            _keymap[65                    ] = osgGA::GUIEventAdapter::KEY_KP_Delete;

        }
        
        ~OSXKeyboardMap() {
        }
        
        unsigned int remapKey(unsigned int key, unsigned int rawkey)
        {
            KeyMap::iterator itr = _keymap.find(rawkey);
            if (itr == _keymap.end()) return key;
            else return itr->second;
        }
    private:
        typedef std::map<unsigned int, osgGA::GUIEventAdapter::KeySymbol> KeyMap;
        KeyMap _keymap;
};

/** remaps a native os x keycode to a GUIEventAdapter-keycode */
static unsigned int remapOSXKey(unsigned int key, unsigned int rawkey)
{
    static OSXKeyboardMap s_OSXKeyboardMap;
    return s_OSXKeyboardMap.remapKey(key,rawkey);
}




#pragma mark * * * MenubarController * * * 

/** the MenubarController class checks all open windows if they intersect with the menubar / dock and hide the menubar/dock if necessary */
class MenubarController : public osg::Referenced 
{

    public:
        MenubarController() : 
            osg::Referenced(), 
            _list(), 
            _menubarShown(false),
            _mutex() 
        {
            // the following code will query the system for the available rect on the main-display (typically the displaying showing the menubar + the dock

            GDHandle mainScreenDevice;
            
            DMGetGDeviceByDisplayID((DisplayIDType) CGMainDisplayID(), &mainScreenDevice, true);
            GetAvailableWindowPositioningBounds (mainScreenDevice, &_availRect);
            
            // now we need the rect of the main-display including the menubar and the dock
            _mainScreenBounds = CGDisplayBounds( CGMainDisplayID() );
            
            // hide the menubar initially
            SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
        }
        
        static MenubarController* instance();
        
        void attachWindow(GraphicsWindowCarbon* win);
        void update();
        void detachWindow(GraphicsWindowCarbon* win);
        
    private: 
        typedef std::list< osg::observer_ptr< GraphicsWindowCarbon > > WindowList;
        WindowList            _list;
        bool                _menubarShown;
        Rect                _availRect;
        CGRect                _mainScreenBounds;
        OpenThreads::Mutex    _mutex;
        
};


MenubarController* MenubarController::instance() 
{
    static osg::ref_ptr<MenubarController> s_menubar_controller = new MenubarController();
    return s_menubar_controller.get();
}


void MenubarController::attachWindow(GraphicsWindowCarbon* win)
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
    _list.push_back(win);
    update();
}


void MenubarController::detachWindow(GraphicsWindowCarbon* win) 
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
    for(WindowList::iterator i = _list.begin(); i != _list.end(); ) {
        if ((*i).get() == win)
            i = _list.erase(i);
        else 
            ++i;
    }
    update();
}

// iterate through all open windows and check, if they intersect the area occupied by the menubar/dock, and if so, hide the menubar/dock

void MenubarController::update() 
{
    OSErr error(noErr);
    unsigned int windowsCoveringMenubarArea = 0;    
    unsigned int windowsIntersectingMainScreen = 0;
    for(WindowList::iterator i = _list.begin(); i != _list.end(); ) {
        if ((*i).valid()) {
            GraphicsWindowCarbon* w = (*i).get();
            Rect windowBounds;
            error = GetWindowBounds(w->getNativeWindowRef(), kWindowStructureRgn, &windowBounds);
            
            bool intersect = !( (_mainScreenBounds.origin.x > windowBounds.right) ||
                                (_mainScreenBounds.origin.x + _mainScreenBounds.size.width < windowBounds.left) ||
                                (_mainScreenBounds.origin.y > windowBounds.bottom) ||
                                (_mainScreenBounds.origin.y + _mainScreenBounds.size.height < windowBounds.top));
            if (intersect && !error)
            {
                ++windowsIntersectingMainScreen;
                
                // the window intersects the main-screen, does it intersect with the menubar/dock?
                if (((_availRect.top > _mainScreenBounds.origin.y) && (_availRect.top > windowBounds.top)) ||
                    ((_availRect.left > _mainScreenBounds.origin.x) && (_availRect.left > windowBounds.left)) || 
                    ((_availRect.right < _mainScreenBounds.origin.x + _mainScreenBounds.size.width) && (_availRect.right < windowBounds.right)) || 
                    ((_availRect.bottom < _mainScreenBounds.origin.y + _mainScreenBounds.size.height) && (_availRect.bottom < windowBounds.bottom) ))
                {
                    ++windowsCoveringMenubarArea;
                }
            }
            
            ++i;
        }
        else
            i= _list.erase(i);
    }
    
    // see http://developer.apple.com/technotes/tn2002/tn2062.html for hiding the dock+menubar
        
    if (windowsCoveringMenubarArea && _menubarShown)
        error = SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
    
    if (!windowsCoveringMenubarArea && !_menubarShown)
        error = SetSystemUIMode(kUIModeNormal, 0);
        _menubarShown = !windowsCoveringMenubarArea;
    
    // osg::notify(osg::DEBUG_INFO) << "MenubarController:: " << windowsCoveringMenubarArea << " windows covering the menubar/dock area, " << windowsIntersectingMainScreen << " intersecting mainscreen" << std::endl;
}


#pragma mark * * * OSXWindowingSystemInterface * * * 

struct OSXCarbonWindowingSystemInterface : public osg::GraphicsContext::WindowingSystemInterface
{
    
    /** ctor, get a list of all attached displays */
    OSXCarbonWindowingSystemInterface() :
        _displayCount(0),
        _displayIds(NULL)
    {
        ProcessSerialNumber sn = { 0, kCurrentProcess };
        TransformProcessType(&sn,kProcessTransformToForegroundApplication);
        SetFrontProcess(&sn);
        
        if( CGGetActiveDisplayList( 0, NULL, &_displayCount ) != CGDisplayNoErr )
            osg::notify(osg::WARN) << "OSXCarbonWindowingSystemInterface: could not get # of screens" << std::endl;
            
        _displayIds = new CGDirectDisplayID[_displayCount];
        if( CGGetActiveDisplayList( _displayCount, _displayIds, &_displayCount ) != CGDisplayNoErr )
            osg::notify(osg::WARN) << "OSXCarbonWindowingSystemInterface: CGGetActiveDisplayList failed" << std::endl;
        
        // register application event handler and AppleEventHandler to get quit-events:
        static const EventTypeSpec menueventSpec = {kEventClassCommand, kEventCommandProcess};
        OSErr status = InstallEventHandler(GetApplicationEventTarget(), NewEventHandlerUPP(ApplicationEventHandler), 1, &menueventSpec, 0, NULL);
        status = AEInstallEventHandler( kCoreEventClass, kAEQuitApplication, NewAEEventHandlerUPP(QuitAppleEventHandler), 0, false);
    }
    
    /** dtor */
    ~OSXCarbonWindowingSystemInterface()
    {
        if (osg::Referenced::getDeleteHandler())
        {
            osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);
            osg::Referenced::getDeleteHandler()->flushAll();
        }

        if (_displayIds) delete[] _displayIds;
        _displayIds = NULL;
    }
    
    /** @return a CGDirectDisplayID for a ScreenIdentifier */
    inline CGDirectDisplayID getDisplayID(const osg::GraphicsContext::ScreenIdentifier& si) {
        if (si.screenNum < _displayCount)
            return _displayIds[si.screenNum];
        else {
            osg::notify(osg::WARN) << "GraphicsWindowCarbon :: invalid screen # " << si.screenNum << ", returning main-screen instead" << std::endl;
            return _displayIds[0];
        }
    }

    /** @return count of attached screens */
    virtual unsigned int getNumScreens(const osg::GraphicsContext::ScreenIdentifier& si) 
    {
        return _displayCount;
    }

    /** returns the resolution of a specific display */
    virtual void getScreenSettings(const osg::GraphicsContext::ScreenIdentifier& si, osg::GraphicsContext::ScreenSettings & resolution)
    {
        CGDirectDisplayID id = getDisplayID(si);
        resolution.width = CGDisplayPixelsWide(id);
        resolution.height = CGDisplayPixelsHigh(id);
        resolution.colorDepth = CGDisplayBitsPerPixel(id);
        resolution.refreshRate = getDictDouble (CGDisplayCurrentMode(id), kCGDisplayRefreshRate);        // Not tested
        if (resolution.refreshRate<0) resolution.refreshRate = 0;
    }

    /** return the top left coord of a specific screen in global screen space */
    void getScreenTopLeft(const osg::GraphicsContext::ScreenIdentifier& si, int& x, int& y) {
        CGRect bounds = CGDisplayBounds( getDisplayID(si) );
        x = static_cast<int>(bounds.origin.x);
        y = static_cast<int>(bounds.origin.y);
        
        // osg::notify(osg::DEBUG_INFO) << "topleft of screen " << si.screenNum <<" " << bounds.origin.x << "/" << bounds.origin.y << std::endl;
    }
    
    /** Helper method to get a double value out of a CFDictionary */
    static double getDictDouble (CFDictionaryRef refDict, CFStringRef key)
    {
        double value;
        CFNumberRef number_value = (CFNumberRef) CFDictionaryGetValue(refDict, key);
        if (!number_value) // if can't get a number for the dictionary
            return -1;  // fail
        if (!CFNumberGetValue(number_value, kCFNumberDoubleType, &value)) // or if cant convert it
            return -1; // fail
        return value; // otherwise return the long value
    }

    /** Helper method to get a long value out of a CFDictionary */
    static long getDictLong(CFDictionaryRef refDict, CFStringRef key)        // const void* key?
    {
        long value = 0;
        CFNumberRef number_value = (CFNumberRef)CFDictionaryGetValue(refDict, key); 
        if (!number_value) // if can't get a number for the dictionary
            return -1;  // fail
        if (!CFNumberGetValue(number_value, kCFNumberLongType, &value)) // or if cant convert it
            return -1; // fail
        return value;
    }

    /** implementation of setScreenSettings */
    virtual bool setScreenSettings(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, const osg::GraphicsContext::ScreenSettings & resolution) 
    { 
        CGDirectDisplayID displayID = getDisplayID(screenIdentifier);
        
        CGRefreshRate refresh = resolution.refreshRate>0 ? resolution.refreshRate : getDictDouble (CGDisplayCurrentMode(displayID), kCGDisplayRefreshRate);  
        size_t depth = resolution.colorDepth>0 ? resolution.colorDepth : CGDisplayBitsPerPixel(displayID);
        CFDictionaryRef display_mode_values =
            CGDisplayBestModeForParametersAndRefreshRate(
                            displayID, 
                            depth,
                            resolution.width, resolution.height,
                            refresh,  
                            NULL);


        CGDisplaySwitchToMode(displayID, display_mode_values);    
        return true; 
    }

    virtual void enumerateScreenSettings(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, osg::GraphicsContext::ScreenSettingsList & resolutionList) {
        // Warning! This method has not been tested.
        resolutionList.clear();

        CGDirectDisplayID displayID = getDisplayID(screenIdentifier);
        CFArrayRef availableModes = CGDisplayAvailableModes(displayID);
        unsigned int numberOfAvailableModes = CFArrayGetCount(availableModes);
        for (unsigned int i=0; i<numberOfAvailableModes; ++i) {
            // look at each mode in the available list
            CFDictionaryRef mode = (CFDictionaryRef)CFArrayGetValueAtIndex(availableModes, i);
            osg::GraphicsContext::ScreenSettings tmpSR;

            long width = getDictLong(mode, kCGDisplayWidth);
            tmpSR.width = width<=0 ? 0 : width;
            long height = getDictLong(mode, kCGDisplayHeight);
            tmpSR.height = height<=0 ? 0 : height;
            long rate = getDictLong(mode, kCGDisplayRefreshRate);
            tmpSR.refreshRate = rate<=0 ? 0 : rate;
            long depth = getDictLong(mode, kCGDisplayBitsPerPixel);
            tmpSR.colorDepth = depth<=0 ? 0 : depth;

            resolutionList.push_back(tmpSR);
        }
    }

    virtual osg::GraphicsContext* createGraphicsContext(osg::GraphicsContext::Traits* traits)
    {
        if (traits->pbuffer)
        {
            osg::ref_ptr<osgViewer::PixelBufferCarbon> pbuffer = new PixelBufferCarbon(traits);
            if (pbuffer->valid()) return pbuffer.release();
            else return 0;
        }
        else
        {
            osg::ref_ptr<osgViewer::GraphicsWindowCarbon> window = new GraphicsWindowCarbon(traits);
            if (window->valid()) return window.release();
            else return 0;
        }
    }
    
    
    
    private:
        CGDisplayCount        _displayCount;
        CGDirectDisplayID*    _displayIds;
};

}


#pragma mark * * * GraphicsWindowCarbon * * *




void GraphicsWindowCarbon::init()
{
    if (_initialized) return;

    // getEventQueue()->setCurrentEventState(osgGA::GUIEventAdapter::getAccumulatedEventState().get());
    
    _lastModifierKeys = 0;
    _windowTitleHeight = 0;
    _closeRequested = false;
    _ownsWindow = false;
    _context = NULL;
    _window = NULL;
    _pixelFormat = PixelBufferCarbon::createPixelFormat(_traits.get());
    if (!_pixelFormat)
        osg::notify(osg::WARN) << "GraphicsWindowCarbon::init could not create a valid pixelformat" << std::endl;
    _valid = (_pixelFormat != NULL);
    _initialized = true;
}

bool GraphicsWindowCarbon::setWindowDecorationImplementation(bool flag)
{
    _useWindowDecoration = flag;

    if (_realized)
    {
        OSErr err = noErr;
        Rect bounds;
        GetWindowBounds(getNativeWindowRef(), kWindowContentRgn, &bounds);

        if (_useWindowDecoration)
        {
            err = ChangeWindowAttributes(getNativeWindowRef(),  kWindowStandardDocumentAttributes,  kWindowNoTitleBarAttribute | kWindowNoShadowAttribute);
            SetWindowBounds(getNativeWindowRef(), kWindowContentRgn, &bounds);
        }
        else
        {
            err = ChangeWindowAttributes(getNativeWindowRef(), kWindowNoTitleBarAttribute | kWindowNoShadowAttribute, kWindowStandardDocumentAttributes);
            SetWindowBounds(getNativeWindowRef(), kWindowContentRgn, &bounds);    
        }

        if (err != noErr)
        {
            osg::notify(osg::WARN) << "GraphicsWindowCarbon::setWindowDecoration failed with " << err << std::endl;
            return false;
        }
        
        // update titlebar-height
        Rect titleRect;
        GetWindowBounds(_window, kWindowTitleBarRgn, &titleRect);
        _windowTitleHeight = abs(titleRect.bottom - titleRect.top);
        
        // sth: I don't know why I have to reattach the context to the window here, If I don't do this  I get blank areas, where the titlebar was. 
        // InvalWindowRect doesn't help here :-/
        
        aglSetDrawable(_context, 0);
        aglSetDrawable(_context, GetWindowPort(_window));
                
        MenubarController::instance()->update();
    }

    return true;
}


WindowAttributes GraphicsWindowCarbon::computeWindowAttributes(bool useWindowDecoration, bool supportsResize) {
    WindowAttributes attr; 
    
    if (useWindowDecoration) 
    {
        if (supportsResize)
            attr = (kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute);
        else
            attr = (kWindowStandardDocumentAttributes | kWindowStandardHandlerAttribute) & ~kWindowResizableAttribute;
    }
    else 
    {
        attr = kWindowNoTitleBarAttribute | kWindowNoShadowAttribute | kWindowStandardHandlerAttribute;
        if (supportsResize)
            attr |= kWindowResizableAttribute;
    }
    return attr;
}

void GraphicsWindowCarbon::installEventHandler() {

    // register window event handler to receive resize-events
    EventTypeSpec   windEventList[] = {
        { kEventClassWindow, kEventWindowBoundsChanged},
        { kEventClassWindow, kEventWindowClose},
        
        {kEventClassMouse, kEventMouseDown},
        {kEventClassMouse, kEventMouseUp},
        {kEventClassMouse, kEventMouseMoved},
        {kEventClassMouse, kEventMouseDragged},
        {kEventClassMouse, kEventMouseWheelMoved},
        {kEventClassMouse, 11 /* kEventMouseScroll */},

        {kEventClassKeyboard, kEventRawKeyDown},
        {kEventClassKeyboard, kEventRawKeyRepeat},
        {kEventClassKeyboard, kEventRawKeyUp},
        {kEventClassKeyboard, kEventRawKeyModifiersChanged},
        {kEventClassKeyboard, kEventHotKeyPressed},
        {kEventClassKeyboard, kEventHotKeyReleased},
    };
    
    InstallWindowEventHandler(_window, NewEventHandlerUPP(GraphicsWindowEventHandler),  GetEventTypeCount(windEventList), windEventList, this, NULL);
 }


bool GraphicsWindowCarbon::realizeImplementation()
{
    
    if (!_initialized) init();
    if (!_initialized) return false;
    if (!_traits) return false;
    
    osg::notify(osg::INFO) << "GraphicsWindowCarbon:: realizeIMplementation" << std::endl;
    
    setWindowDecoration(_traits->windowDecoration);
    useCursor(_traits->useCursor);

    // move the window to the right screen
    OSXCarbonWindowingSystemInterface* wsi = dynamic_cast<OSXCarbonWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
    int screenLeft(0), screenTop(0);
    if (wsi) {
        
        wsi->getScreenTopLeft((*_traits), screenLeft, screenTop);
        _traits->y += screenTop;
        _traits->x += screenLeft;
    }
    
    WindowData *windowData = ( _traits.get() && _traits->inheritedWindowData.get() ) ? static_cast<osgViewer::GraphicsWindowCarbon::WindowData*>(_traits->inheritedWindowData.get()) : 0; 
     
    _ownsWindow = (windowData) ? (windowData->getNativeWindowRef() == NULL) : true;
    
    if (_ownsWindow) {
        
        // create the window
        Rect bounds = {_traits->y, _traits->x, _traits->y + _traits->height, _traits->x + _traits->width};
        OSStatus err = 0;
        WindowAttributes attr = computeWindowAttributes(_useWindowDecoration, _traits->supportsResize);
        
        err = CreateNewWindow(kDocumentWindowClass, attr, &bounds, &_window);

        if (err) {
            osg::notify(osg::WARN) << "GraphicsWindowCarbon::realizeImplementation() failed creating a window: " << err << std::endl;
            return false;
        } else {
            osg::notify(osg::INFO) << "GraphicsWindowCarbon::realizeImplementation() - window created with bounds(" << bounds.top << ", " << bounds.left << ", " << bounds.bottom << ", " << bounds.right << ")" << std::endl;
        }
    }
    else {
         _window = windowData->getNativeWindowRef();
    }
    
    Rect titleRect;
    GetWindowBounds(_window, kWindowTitleBarRgn, &titleRect);
    _windowTitleHeight = abs(titleRect.bottom - titleRect.top);
    
    if ((_ownsWindow) || (windowData && windowData->installEventHandler()))
        installEventHandler();
    
    // set the window title
    setWindowName(_traits->windowName);
    
    // create the context
    AGLContext sharedContextCarbon = NULL;
    
    GraphicsWindowCarbon* graphicsWindowCarbon = dynamic_cast<GraphicsWindowCarbon*>(_traits->sharedContext);
    if (graphicsWindowCarbon) 
    {
        sharedContextCarbon = graphicsWindowCarbon->getAGLContext();
    }
    else
    {
        PixelBufferCarbon* pixelbuffer = dynamic_cast<PixelBufferCarbon*>(_traits->sharedContext);
        if (pixelbuffer) {
            sharedContextCarbon = pixelbuffer->getAGLContext();
        }
    }
    _context = aglCreateContext (_pixelFormat, sharedContextCarbon);


    if (!_context) {
        osg::notify(osg::WARN) << "GraphicsWindowCarbon::realizeImplementation failed creating a context: " << aglGetError() << std::endl;
        return false;
    }

     
    if ( windowData && windowData->getAGLDrawable() ) {
        aglSetDrawable(_context, (AGLDrawable)*(windowData->getAGLDrawable()) ); 
                
    } else {
        aglSetDrawable(_context, GetWindowPort(_window)); 
        ShowWindow(_window); 
        MenubarController::instance()->attachWindow(this);
    }
    
    makeCurrent();

    if ((_traits->useMultiThreadedOpenGLEngine) && (OpenThreads::GetNumberOfProcessors() > 1)) {
        // enable Multi-threaded OpenGL Execution:
        CGLError cgerr = kCGLNoError;
        CGLContextObj ctx = CGLGetCurrentContext();

#if 0
        cgerr =  CGLEnable( ctx, kCGLCEMPEngine);
#else
        // the above use of kCGLCEMPEngine is not backwards compatible
        // so we'll use the raw value of it to keep things compiling on older
        // versions of OSX.
        cgerr =  CGLEnable( ctx, static_cast <CGLContextEnable>(313) );
#endif    
        if (cgerr != kCGLNoError )
        {
            osg::notify(osg::INFO) << "GraphicsWindowCarbon:: Multi-threaded OpenGL Execution not available" << std::endl;
        } 
    }
    
    InitCursor();
    
    //enable vsync
    if (_traits->vsync) {
        GLint swap = 1;
        aglSetInteger (_context, AGL_SWAP_INTERVAL, &swap);
    }

    _realized = true;
    return _realized;
}



bool GraphicsWindowCarbon::makeCurrentImplementation()
{
        
    return (aglSetCurrentContext(_context) == GL_TRUE);
}

bool GraphicsWindowCarbon::releaseContextImplementation()
{
    if (!_realized)
    {
        osg::notify(osg::NOTICE)<<"Warning: GraphicsWindow not realized, cannot do makeCurrent."<<std::endl;
        return false;
    }

    // osg::notify(osg::NOTICE)<<"makeCurrentImplementation "<<this<<" "<<OpenThreads::Thread::CurrentThread()<<std::endl;
    // osg::notify(osg::NOTICE)<<"   glXMakeCurrent ("<<_display<<","<<_window<<","<<_glxContext<<std::endl;
    return (aglSetCurrentContext(NULL) == GL_TRUE);
}



void GraphicsWindowCarbon::closeImplementation()
{
    // osg::notify(osg::INFO) << "GraphicsWindowCarbon::closeImplementation" << std::endl;
    _valid = false;
    _realized = false;
    
    // there's a possibility that the MenubarController is destructed already, so prevent a crash:
    MenubarController* mbc = MenubarController::instance();
    if (mbc) mbc->detachWindow(this);
    
    if (_pixelFormat)
    {
        aglDestroyPixelFormat(_pixelFormat);
        _pixelFormat = NULL;
    }
    
    if (_context) 
    {
        aglSetDrawable(_context, NULL);
        aglSetCurrentContext(NULL);
        aglDestroyContext(_context);
        _context = NULL;
    }
    
    if (_ownsWindow && _window) DisposeWindow(_window);
    _window = NULL;
}



void GraphicsWindowCarbon::swapBuffersImplementation()
{
    aglSwapBuffers(_context);
}



void GraphicsWindowCarbon::resizedImplementation(int x, int y, int width, int height)
{
    GraphicsContext::resizedImplementation(x, y, width, height);

    aglUpdateContext(_context);
    MenubarController::instance()->update();
}



bool GraphicsWindowCarbon::handleMouseEvent(EventRef theEvent)
{

    static unsigned int lastEmulatedMouseButton = 0;
    // mouse down event   
    Point wheresMyMouse;
    GetEventParameter (theEvent, kEventParamWindowMouseLocation, typeQDPoint, NULL, sizeof(wheresMyMouse), NULL, &wheresMyMouse);
    
    wheresMyMouse.v -= _windowTitleHeight;
    if (_useWindowDecoration && (wheresMyMouse.v < 0))
        return false;
    
    Point wheresMyMouseGlobal;
    GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(wheresMyMouse), NULL, &wheresMyMouseGlobal);
    
    EventMouseButton mouseButton = 0;
    GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton, NULL, sizeof(mouseButton), NULL, &mouseButton);
    
    UInt32 modifierKeys;
    GetEventParameter (theEvent,kEventParamKeyModifiers,typeUInt32, NULL,sizeof(modifierKeys), NULL,&modifierKeys);
    
    
    WindowRef win;
    int fwres = FindWindow(wheresMyMouseGlobal, &win);
    // return false when Window is inactive; For enabling click-to-active on window by delegating event to default handler
    if (((fwres != inContent) && (fwres > 0) && (mouseButton >= 1)) || !IsWindowActive(win))
    {
        return false;
    }
    else
    {
        UInt32 clickCount;
        GetEventParameter(theEvent, kEventParamClickCount, typeUInt32, NULL, sizeof(clickCount), NULL, &clickCount);
        // swap right and middle buttons so that middle button is 2, right button is 3.
        if (mouseButton==3) mouseButton = 2;
        else if (mouseButton==2) mouseButton = 3;
        
        // check tablet pointer device and map it to a musebutton
        TabletProximityRec    theTabletRecord;    // The Tablet Proximity Record
        // Extract the Tablet Proximity reccord from the event.
        if(noErr == GetEventParameter(theEvent, kEventParamTabletProximityRec,
                                      typeTabletProximityRec, NULL,
                                      sizeof(TabletProximityRec),
                                      NULL, (void *)&theTabletRecord))
        {            
            osgGA::GUIEventAdapter::TabletPointerType pointerType;
            switch(theTabletRecord.pointerType)
            {
                case 1: // pen
                    pointerType = osgGA::GUIEventAdapter::PEN;
                    break;
                    
                case 2: // puck
                    pointerType = osgGA::GUIEventAdapter::PUCK;
                    break;
                    
                case 3: //eraser
                    pointerType = osgGA::GUIEventAdapter::ERASER;
                    break;

                default:
                   pointerType = osgGA::GUIEventAdapter::UNKNOWN;
                   break;
            }
            
            getEventQueue()->penProximity(pointerType, (theTabletRecord.enterProximity != 0));
        }

        // get tilt and rotation from the pen
        TabletPointRec theTabletPointRecord;
        if(noErr == GetEventParameter(theEvent,  kEventParamTabletPointRec, typeTabletPointRec, NULL, 
                sizeof(TabletPointRec), NULL, (void *)&theTabletPointRecord)) 
        {
            int penRotation = (int)theTabletPointRecord.rotation * 9 / 575; //to get angle between 0 to 360 grad
            penRotation = -(((penRotation + 180) % 360) - 180) ;          //for same range on all plattforms we need -180 to 180
            getEventQueue()->penOrientation (
                    theTabletPointRecord.tiltX * 60 / 32767.0f,  //multiply with 60 to get angle between -60 to 60 grad
                    -theTabletPointRecord.tiltY * 60 / 32767.0f,  //multiply with 60 to get angle between -60 to 60 grad
                    penRotation
            );
        }
            
        switch(GetEventKind(theEvent))
        {
            case kEventMouseDown:
                {
                    float mx =wheresMyMouse.h;
                    float my =wheresMyMouse.v;
                    transformMouseXY(mx, my);
                    
                    lastEmulatedMouseButton = 0;
                    
                    if (mouseButton == 1) 
                    {
                        if( modifierKeys & cmdKey )
                        {
                            mouseButton = lastEmulatedMouseButton = 3;
                        }
                        else if( modifierKeys & optionKey )
                        {
                            mouseButton = lastEmulatedMouseButton = 2;
                        }
                    }
                    
                    if (clickCount > 1) 
                        getEventQueue()->mouseDoubleButtonPress(mx,my, mouseButton);
                    else
                        getEventQueue()->mouseButtonPress(mx, my, mouseButton);
                }
                break;
            case kEventMouseUp:
                {
                    float mx =wheresMyMouse.h;
                    float my =wheresMyMouse.v;
                    transformMouseXY(mx, my);
                    if (lastEmulatedMouseButton > 0) {
                        getEventQueue()->mouseButtonRelease(mx, my, lastEmulatedMouseButton);
                        lastEmulatedMouseButton = 0;
                    }
                    else {
                        getEventQueue()->mouseButtonRelease(mx, my, mouseButton);
                    }
                }
                break;
                
            case kEventMouseDragged:
                {
                    // get pressure from the pen, only when mouse/pen is dragged
                    TabletPointRec    theTabletRecord;
                    if(noErr == GetEventParameter(theEvent,  kEventParamTabletPointRec, typeTabletPointRec, NULL, 
                                    sizeof(TabletPointRec), NULL, (void *)&theTabletRecord)) {
                    
                        getEventQueue()->penPressure(theTabletRecord.pressure / 65535.0f);
            
                    }
                    
                    float mx =wheresMyMouse.h;
                    float my =wheresMyMouse.v;
                    transformMouseXY(mx, my);
                    getEventQueue()->mouseMotion(mx, my);
                }
                break;
                
            case kEventMouseMoved:
                {
                    float mx = wheresMyMouse.h;
                    float my = wheresMyMouse.v;
                    transformMouseXY(mx, my);
                    getEventQueue()->mouseMotion(mx, my);
                }
                break;
                
            // mouse with scroll-wheels
            case kEventMouseWheelMoved:
                {
                    EventMouseWheelAxis axis;
                    SInt32 delta;
                    if (noErr == GetEventParameter( theEvent, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis )) {
                        if (noErr == GetEventParameter( theEvent, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(delta), NULL, &delta )) {
                            switch (axis) {
                                case kEventMouseWheelAxisX:
                                    getEventQueue()->mouseScroll( (delta > 0) ? osgGA::GUIEventAdapter::SCROLL_RIGHT : osgGA::GUIEventAdapter::SCROLL_LEFT);
                                    break;
                                case kEventMouseWheelAxisY:
                                    getEventQueue()->mouseScroll( (delta < 0) ? osgGA::GUIEventAdapter::SCROLL_DOWN : osgGA::GUIEventAdapter::SCROLL_UP);
                                    break;
                            }
                        }
                    }
                }
                break;
            
            // new trackpads and mighty mouse, (not officially documented, see http://developer.apple.com/qa/qa2005/qa1453.html )
            case 11:
                {
                    enum
                        {
                        kEventParamMouseWheelSmoothVerticalDelta       = 'saxy', // typeSInt32
                        kEventParamMouseWheelSmoothHorizontalDelta     = 'saxx' // typeSInt32
                        };
                    
                    SInt32 scroll_delta_x = 0;
                    SInt32 scroll_delta_y = 0;
                    OSErr err = noErr;
                    err = GetEventParameter( theEvent, kEventParamMouseWheelSmoothVerticalDelta, typeLongInteger, NULL, sizeof(scroll_delta_y), NULL, &scroll_delta_y );
                    err = GetEventParameter( theEvent, kEventParamMouseWheelSmoothHorizontalDelta, typeLongInteger, NULL, sizeof(scroll_delta_x), NULL, &scroll_delta_x );
                    
                    if ((scroll_delta_x != 0) || (scroll_delta_y != 0)) {
                        getEventQueue()->mouseScroll2D( scroll_delta_x, scroll_delta_y);
                    }
                }
                break;
            
            default:
                return false;
        }
    }
    
    return true;
}



bool GraphicsWindowCarbon::handleKeyboardEvent(EventRef theEvent)
{
    handleModifierKeys(theEvent);
        
    OSStatus status;
    
    UInt32 rawkey;
    GetEventParameter (theEvent,kEventParamKeyCode,typeUInt32, NULL,sizeof(rawkey), NULL,&rawkey);
    
    // osg::notify(osg::INFO) << "key code: " << rawkey << " modifiers: " << modifierKeys << std::endl;
            
    UInt32 dataSize;
    /* jbw check return status so that we don't allocate a huge array */
    status = GetEventParameter( theEvent, kEventParamKeyUnicodes, typeUnicodeText, NULL, 0, &dataSize, NULL );
    if (status != noErr) return false;
    if (dataSize<=1) return false;
    
    UniChar* uniChars = new UniChar[dataSize+1];
    GetEventParameter( theEvent, kEventParamKeyUnicodes, typeUnicodeText, NULL, dataSize, NULL, (void*)uniChars );
    
    unsigned int keychar = remapOSXKey(static_cast<unsigned long>(uniChars[0]), rawkey);
    
    switch(GetEventKind(theEvent))
    {
        case kEventRawKeyDown:
        case kEventRawKeyRepeat:
        {
            //getEventQueue()->getCurrentEventState()->setModKeyMask(modifierMask);
            osg::notify(osg::INFO) << "GraphicsWindowCarbon::keyPress" << std::endl;
            getEventQueue()->keyPress(keychar);
            break;
        }
        
        case kEventRawKeyUp:
        {                 
            osg::notify(osg::INFO) << "GraphicsWindowCarbon::keyPress" << std::endl;
            //getEventQueue()->getCurrentEventState()->setModKeyMask(modifierMask);
            getEventQueue()->keyRelease(keychar);
            break;
        }
        
        default:
             break;
    
    }

    delete[] uniChars;

    return true;
}

void GraphicsWindowCarbon::handleModifierKey(UInt32 modifierKey, UInt32 modifierMask, osgGA::GUIEventAdapter::KeySymbol keySymbol) {

    if ((modifierKey & modifierMask) && !(_lastModifierKeys & modifierMask))
    {
        getEventQueue()->keyPress(keySymbol);
    }
    
    if (!(modifierKey & modifierMask) && (_lastModifierKeys & modifierMask))
    {
        getEventQueue()->keyRelease(keySymbol);
    }
}

bool GraphicsWindowCarbon::handleModifierKeys(EventRef theEvent) 
{
    UInt32 modifierKeys;
    GetEventParameter (theEvent,kEventParamKeyModifiers,typeUInt32, NULL,sizeof(modifierKeys), NULL,&modifierKeys);

    //std::cout << modifierKeys << std::endl;
    if (_lastModifierKeys == modifierKeys)
        return false;
        
    handleModifierKey(modifierKeys, shiftKey, osgGA::GUIEventAdapter::KEY_Shift_L);
    handleModifierKey(modifierKeys, controlKey, osgGA::GUIEventAdapter::KEY_Control_L);
    handleModifierKey(modifierKeys, optionKey, osgGA::GUIEventAdapter::KEY_Alt_L);
    handleModifierKey(modifierKeys, cmdKey, osgGA::GUIEventAdapter::KEY_Super_L);
    
    // Caps lock needs some special handling, i did not find a way to get informed when the caps-lock-key gets released    
    if ((modifierKeys & alphaLock) && !(_lastModifierKeys & alphaLock))
    {
        getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_Caps_Lock);
        getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KEY_Caps_Lock);
    }
    
    if (!(modifierKeys & alphaLock) && (_lastModifierKeys & alphaLock))
    {
        getEventQueue()->keyPress(osgGA::GUIEventAdapter::KEY_Caps_Lock);
        getEventQueue()->keyRelease(osgGA::GUIEventAdapter::KEY_Caps_Lock);
    }
    
    _lastModifierKeys = modifierKeys;
    return true;
}



void GraphicsWindowCarbon::checkEvents()
{
    if (!_realized) return;
    
    EventRef theEvent;
    EventTargetRef theTarget = GetEventDispatcherTarget();
    while (ReceiveNextEvent(0, NULL, 0,true, &theEvent)== noErr)
    {
        switch(GetEventClass(theEvent))
        {
            case kEventClassMouse: 
                    {
                    // handle the menubar
                    Point wheresMyMouse;
                    GetEventParameter (theEvent, kEventParamMouseLocation, typeQDPoint, NULL, sizeof(wheresMyMouse), NULL, &wheresMyMouse);
                    
                    EventMouseButton mouseButton = 0;
                    GetEventParameter (theEvent, kEventParamMouseButton, typeMouseButton, NULL, sizeof(mouseButton), NULL, &mouseButton);
                    
                    WindowRef win;
                    int fwres = FindWindow(wheresMyMouse, &win);

                    if ((fwres == inMenuBar) && (mouseButton >= 1)) {
                        MenuSelect(wheresMyMouse);
                        HiliteMenu(0);
                        return;
                    }
                    break;
                }

            case kEventClassApplication:
                switch (GetEventKind(theEvent)) {
                    case kEventAppQuit:
                        getEventQueue()->quitApplication();
                        break;
                }
                break;
            
            case kEventClassAppleEvent: 
                {
                    EventRecord eventRecord;
                    ConvertEventRefToEventRecord(theEvent, &eventRecord);
                    AEProcessAppleEvent(&eventRecord);
                    return;
                }
                break;

        }
        SendEventToEventTarget (theEvent, theTarget);
        ReleaseEvent(theEvent);        
    }  
    if (_closeRequested)
        getEventQueue()->closeWindow();
        
    if (s_quit_requested) {
        getEventQueue()->quitApplication();
        s_quit_requested = false;
    }
            
}


bool GraphicsWindowCarbon::setWindowRectangleImplementation(int x, int y, int width, int height)
{
    Rect bounds = {y, x, y + height, x + width};
    SetWindowBounds(getNativeWindowRef(), kWindowContentRgn, &bounds);
    aglUpdateContext(_context);
    MenubarController::instance()->update();
    return true;
}

void GraphicsWindowCarbon::grabFocus()
{
    SelectWindow(_window);
}



void GraphicsWindowCarbon::grabFocusIfPointerInWindow()
{
   // TODO: implement
   osg::notify(osg::ALWAYS) << "GraphicsWindowCarbon::grabFocusIfPointerInWindow" << std::endl;
}


void GraphicsWindowCarbon::useCursor(bool cursorOn)
{

    if (_traits.valid())
        _traits->useCursor = cursorOn;
    OSXCarbonWindowingSystemInterface* wsi = dynamic_cast<OSXCarbonWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
    if (wsi == NULL) {
        osg::notify(osg::WARN) << "GraphicsWindowCarbon::useCursor :: could not get OSXCarbonWindowingSystemInterface" << std::endl;
        return;
    }
    
    CGDirectDisplayID displayId = wsi->getDisplayID((*_traits));
    CGDisplayErr err = kCGErrorSuccess;
    switch (cursorOn)
    {
        case true:
            err = CGDisplayShowCursor(displayId);
            break;
        case false:
            err = CGDisplayHideCursor(displayId);
            break;
    }
    if (err != kCGErrorSuccess) {
        osg::notify(osg::WARN) << "GraphicsWindowCarbon::useCursor failed with " << err << std::endl;
    }
}

// FIXME: need to implement all cursor types
// FIXME: I used deprecated functions, but don't know if there are any substitutable newer functions...
void GraphicsWindowCarbon::setCursor(MouseCursor mouseCursor)
{
    UInt32 cursor;
    if (_currentCursor == mouseCursor)
      return;
    switch (mouseCursor) 
    {
        case NoCursor:
          HideCursor();
          _currentCursor = mouseCursor;
          return;
        case RightArrowCursor:
            cursor = kThemeArrowCursor;
            break;
        case CrosshairCursor:
            cursor = kThemeCrossCursor;
            break;
        case TextCursor:
            cursor = kThemeIBeamCursor;
            break;
        case UpDownCursor:
            cursor = kThemeResizeUpDownCursor;
            break;
        case LeftRightCursor:
            cursor = kThemeResizeLeftRightCursor;
            break;
        default:
            cursor = kThemeArrowCursor;
            osg::notify(osg::WARN) << "GraphicsWindowCarbon::setCursor doesn't implement cursor: type = " << mouseCursor << std::endl;
    }
    
    _currentCursor = mouseCursor;
    SetThemeCursor(cursor);
    ShowCursor();
}


void GraphicsWindowCarbon::setWindowName (const std::string& name) 
{
    _traits->windowName = name;
    if (!_traits->windowName.empty()) 
    {
        CFStringRef windowtitle = CFStringCreateWithBytes( kCFAllocatorDefault, (const UInt8*)(_traits->windowName.c_str()), _traits->windowName.length(),kCFStringEncodingUTF8, false );
        SetWindowTitleWithCFString( _window, windowtitle );
        CFRelease(windowtitle);
    }
}

void GraphicsWindowCarbon::requestWarpPointer(float x,float y)
{

    OSXCarbonWindowingSystemInterface* wsi = dynamic_cast<OSXCarbonWindowingSystemInterface*>(osg::GraphicsContext::getWindowingSystemInterface());
    if (wsi == NULL) {
        osg::notify(osg::WARN) << "GraphicsWindowCarbon::useCursor :: could not get OSXCarbonWindowingSystemInterface" << std::endl;
        return;
    }

    CGDirectDisplayID displayId = wsi->getDisplayID((*_traits));

    CGPoint point;
    point.x = x + _traits->x;
    point.y = y + _traits->y;
    CGDisplayMoveCursorToPoint(displayId, point);

    getEventQueue()->mouseWarped(x,y);
}


void GraphicsWindowCarbon::transformMouseXY(float& x, float& y)
{
    if (getEventQueue()->getUseFixedMouseInputRange())
    {
        osgGA::GUIEventAdapter* eventState = getEventQueue()->getCurrentEventState();
        x = eventState->getXmin() + (eventState->getXmax()-eventState->getXmin())*x/float(_traits->width);
        y = eventState->getYmin() + (eventState->getYmax()-eventState->getYmin())*y/float(_traits->height);
    }
}






struct RegisterWindowingSystemInterfaceProxy
{
    RegisterWindowingSystemInterfaceProxy()
    {
        osg::GraphicsContext::setWindowingSystemInterface(new osgViewer::OSXCarbonWindowingSystemInterface());
    }

    ~RegisterWindowingSystemInterfaceProxy()
    {
        if (osg::Referenced::getDeleteHandler())
        {
            osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);
            osg::Referenced::getDeleteHandler()->flushAll();
        }

        osg::GraphicsContext::setWindowingSystemInterface(0);
    }
};

RegisterWindowingSystemInterfaceProxy createWindowingSystemInterfaceProxy;

// declare C entry point for static compilation.
extern "C" void graphicswindow_Carbon(void)
{
    osg::GraphicsContext::setWindowingSystemInterface(new osgViewer::OSXCarbonWindowingSystemInterface());
}

#endif
