From 5be809ab049b70a79d73a561910bffc837e9fdfd Mon Sep 17 00:00:00 2001 From: Paul MARTZ Date: Mon, 15 Mar 2010 17:20:43 +0000 Subject: [PATCH] 2.8 branch: Support for OS X 10.6. Several changes to GraphicsWIndowCocoa and DarwinUtils. The following trunk revisions were merged in this commit: 9879, 9895, 10208, 10340, 10417, 10456, 10622, 10858, and 10887. --- .../osgViewer/api/Cocoa/GraphicsHandleCocoa | 53 + .../osgViewer/api/Cocoa/GraphicsWindowCocoa | 199 +++ include/osgViewer/api/Cocoa/PixelBufferCocoa | 115 ++ src/osgViewer/CMakeLists.txt | 136 +- src/osgViewer/DarwinUtils.h | 152 ++ src/osgViewer/DarwinUtils.mm | 390 +++++ src/osgViewer/GraphicsWindowCocoa.mm | 1520 +++++++++++++++++ src/osgViewer/PixelBufferCocoa.mm | 144 ++ 8 files changed, 2650 insertions(+), 59 deletions(-) create mode 100644 include/osgViewer/api/Cocoa/GraphicsHandleCocoa create mode 100755 include/osgViewer/api/Cocoa/GraphicsWindowCocoa create mode 100755 include/osgViewer/api/Cocoa/PixelBufferCocoa create mode 100755 src/osgViewer/DarwinUtils.h create mode 100755 src/osgViewer/DarwinUtils.mm create mode 100755 src/osgViewer/GraphicsWindowCocoa.mm create mode 100755 src/osgViewer/PixelBufferCocoa.mm diff --git a/include/osgViewer/api/Cocoa/GraphicsHandleCocoa b/include/osgViewer/api/Cocoa/GraphicsHandleCocoa new file mode 100644 index 000000000..d8558c5b5 --- /dev/null +++ b/include/osgViewer/api/Cocoa/GraphicsHandleCocoa @@ -0,0 +1,53 @@ +/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2009 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. +*/ + +#ifndef OSGVIEWER_GRAPHICSHANDLECOCOA +#define OSGVIEWER_GRAPHICSHANDLECOCOA 1 + +#include + + +#ifdef __OBJC__ +@class NSOpenGLContext; +#else +class NSOpenGLContext; +#endif + +namespace osgViewer +{ + +/** Class to encapsulate platform-specific OpenGL context handle variables. + * Derived osg::GraphicsContext classes can inherit from this class to + * share OpenGL resources.*/ + +class OSGVIEWER_EXPORT GraphicsHandleCocoa +{ + public: + + GraphicsHandleCocoa(): + _context(0) {} + + /** Set native AGL graphics context.*/ + inline void setNSOpenGLContext(NSOpenGLContext* context) { _context = context; } + + /** Get native AGL graphics context.*/ + inline NSOpenGLContext* getNSOpenGLContext() const { return _context; } + + protected: + + NSOpenGLContext* _context; +}; + +} + +#endif diff --git a/include/osgViewer/api/Cocoa/GraphicsWindowCocoa b/include/osgViewer/api/Cocoa/GraphicsWindowCocoa new file mode 100755 index 000000000..965ce3a1b --- /dev/null +++ b/include/osgViewer/api/Cocoa/GraphicsWindowCocoa @@ -0,0 +1,199 @@ +/* -*-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. +*/ + +/* Note, elements of GraphicsWindowX11 have used Prodcer/RenderSurface_X11.cpp as both + * a guide to use of X11/GLX and copiying directly in the case of setBorder(). + * These elements are license under OSGPL as above, with Copyright (C) 2001-2004 Don Burns. + */ + +#ifndef OSGVIEWER_GRAPHICSWINDOWCOCOA +#define OSGVIEWER_GRAPHICSWINDOWCOCOA 1 + +#ifdef __APPLE__ + +#ifdef __OBJC__ +@class GraphicsWindowCocoaWindow; +@class GraphicsWindowCocoaGLView; +@class NSOpenGLContext; +@class NSWindow; +@class NSView; +#else +class GraphicsWindowCocoaGLView; +class GraphicsWindowCocoaWindow; +class NSOpenGLContext; +class NSWindow; +class NSView; +#endif + +#include +#include + +// we may not include any cocoa-header here, because this will pollute the name-sapce and tend to compile-errors + +namespace osgViewer +{ + +class GraphicsWindowCocoa : public osgViewer::GraphicsWindow, public osgViewer::GraphicsHandleCocoa +{ + public: + class Implementation; + + GraphicsWindowCocoa(osg::GraphicsContext::Traits* traits): + osgViewer::GraphicsWindow(), + osgViewer::GraphicsHandleCocoa(), + _valid(false), + _initialized(false), + _realized(false), + _closeRequested(false), + _checkForEvents(true), + _ownsWindow(true), + _currentCursor(RightArrowCursor), + _window(NULL) + { + _traits = traits; + + init(); + + if (valid()) + { + setState( new osg::State ); + getState()->setGraphicsContext(this); + + if (_traits.valid() && _traits->sharedContext) + { + getState()->setContextID( _traits->sharedContext->getState()->getContextID() ); + incrementContextIDUsageCount( getState()->getContextID() ); + } + else + { + getState()->setContextID( osg::GraphicsContext::createNewContextID() ); + } + } + } + + virtual bool isSameKindAs(const Object* object) const { return dynamic_cast(object)!=0; } + virtual const char* libraryName() const { return "osgViewer"; } + virtual const char* className() const { return "GraphicsWindowCarbon"; } + + virtual bool valid() const { return _valid; } + + /** Realise the GraphicsContext.*/ + virtual bool realizeImplementation(); + + /** Return true if the graphics context has been realised and is ready to use.*/ + virtual bool isRealizedImplementation() const { return _realized; } + + /** Close the graphics context.*/ + virtual void closeImplementation(); + + /** Make this graphics context current.*/ + virtual bool makeCurrentImplementation(); + + /** Release the graphics context.*/ + virtual bool releaseContextImplementation(); + + /** Swap the front and back buffers.*/ + virtual void swapBuffersImplementation(); + + /** Check to see if any events have been generated.*/ + virtual void checkEvents(); + + /** Set Window decoration.*/ + virtual bool setWindowDecorationImplementation(bool flag); + + /** Get focus.*/ + virtual void grabFocus(); + + /** Get focus on if the pointer is in this window.*/ + virtual void grabFocusIfPointerInWindow(); + + bool requestClose() { bool b = _closeRequested; _closeRequested = true; return b; } + + virtual void resizedImplementation(int x, int y, int width, int height); + + virtual bool setWindowRectangleImplementation(int x, int y, int width, int height); + + virtual void setWindowName (const std::string & name); + virtual void useCursor(bool cursorOn); + virtual void setCursor(MouseCursor mouseCursor); + + /** WindowData is used to pass in the Cocoa window handle attached the GraphicsContext::Traits structure. */ + class WindowData : public osg::Referenced + { + public: + enum Options { CreateOnlyView = 1, CheckForEvents = 2, PoseAsStandaloneApp = 4}; + WindowData(unsigned int options) + : _createOnlyView(options & CreateOnlyView), + _checkForEvents(options & CheckForEvents), + _poseAsStandaloneApp(options & PoseAsStandaloneApp), + _view(NULL) + { + } + + inline NSView* getCreatedNSView() { return _view; } + bool createOnlyView() const { return _createOnlyView; } + bool checkForEvents() const { return _checkForEvents; } + bool poseAsStandaloneApp() const { return _poseAsStandaloneApp; } + + protected: + inline void setCreatedNSView(NSView* view) { _view = view; } + + private: + bool _createOnlyView, _checkForEvents, _poseAsStandaloneApp; + NSView* _view; + + friend class GraphicsWindowCocoa; + + }; + + NSOpenGLContext* getContext() { return _context; } + GraphicsWindowCocoaWindow* getWindow() { return _window; } + + void setVSync(bool f); + + /** adapts a resize / move of the window, coords in global screen space */ + void adaptResize(int x, int y, int w, int h); + + protected: + + void init(); + + void transformMouseXY(float& x, float& y); + void setupNSWindow(NSWindow* win); + + + virtual ~GraphicsWindowCocoa(); + + + bool _valid; + bool _initialized; + bool _realized; + bool _useWindowDecoration; + + + + private: + + + bool _closeRequested, _checkForEvents,_ownsWindow; + MouseCursor _currentCursor; + GraphicsWindowCocoaWindow* _window; + GraphicsWindowCocoaGLView* _view; + NSOpenGLContext* _context; + bool _updateContext; +}; + +} + +#endif +#endif diff --git a/include/osgViewer/api/Cocoa/PixelBufferCocoa b/include/osgViewer/api/Cocoa/PixelBufferCocoa new file mode 100755 index 000000000..6a1a51731 --- /dev/null +++ b/include/osgViewer/api/Cocoa/PixelBufferCocoa @@ -0,0 +1,115 @@ +/* -*-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. +*/ + + + +#ifndef OSGVIEWER_PIXELBUFFERCOCOA +#define OSGVIEWER_PIXELBUFFERCOCOA 1 + +#ifdef __APPLE__ + +#include +#include +#include + +namespace osgViewer +{ + +class OSGVIEWER_EXPORT PixelBufferCocoa : public osg::GraphicsContext, public osgViewer::GraphicsHandleCocoa +{ + public: + struct Implementation; + + PixelBufferCocoa(osg::GraphicsContext::Traits* traits): + osg::GraphicsContext(), + osgViewer::GraphicsHandleCocoa(), + _valid(false), + _initialized(false), + _realized(false), + _context(NULL) + { + _traits = traits; + + init(); + + if (valid()) + { + setState( new osg::State ); + getState()->setGraphicsContext(this); + + if (_traits.valid() && _traits->sharedContext) + { + getState()->setContextID( _traits->sharedContext->getState()->getContextID() ); + incrementContextIDUsageCount( getState()->getContextID() ); + } + else + { + getState()->setContextID( osg::GraphicsContext::createNewContextID() ); + } + + } + } + + virtual bool isSameKindAs(const Object* object) const { return dynamic_cast(object)!=0; } + virtual const char* libraryName() const { return "osgViewer"; } + virtual const char* className() const { return "PixelBufferCarbon"; } + + virtual bool valid() const { return _valid; } + + /** Realise the GraphicsContext.*/ + virtual bool realizeImplementation(); + + /** Return true if the graphics context has been realised and is ready to use.*/ + virtual bool isRealizedImplementation() const { return _realized; } + + /** Close the graphics context.*/ + virtual void closeImplementation(); + + /** Make this graphics context current.*/ + virtual bool makeCurrentImplementation(); + + /** Make this graphics context current with specified read context implementation. */ + virtual bool makeContextCurrentImplementation(osg::GraphicsContext* readContext); + + /** Release the graphics context.*/ + virtual bool releaseContextImplementation(); + + /** Bind the graphics context to associated texture implementation.*/ + virtual void bindPBufferToTextureImplementation( GLenum buffer ); + + /** Swap the front and back buffers.*/ + virtual void swapBuffersImplementation(); + + NSOpenGLContext* getContext() { return _context; } + public: + + + protected: + + + + ~PixelBufferCocoa(); + + void init(); + + bool _valid; + bool _initialized; + bool _realized; + NSOpenGLContext* _context; +}; + +} + +#endif + +#endif diff --git a/src/osgViewer/CMakeLists.txt b/src/osgViewer/CMakeLists.txt index 11afe0676..3dca0c677 100644 --- a/src/osgViewer/CMakeLists.txt +++ b/src/osgViewer/CMakeLists.txt @@ -65,87 +65,105 @@ IF(WIN32) ELSE(WIN32) IF(APPLE) - SET(OSG_WINDOWING_SYSTEM "Carbon" CACHE STRING "Windowing system type for graphics window creation, options Carbon or X11.") + SET(OSG_WINDOWING_SYSTEM "Carbon" CACHE STRING "Windowing system type for graphics window creation, options Carbon, Cocoa or X11.") ELSE(APPLE) SET(OSG_WINDOWING_SYSTEM "X11" CACHE STRING "Windowing system type for graphics window creation. options only X11") ENDIF(APPLE) - - IF(${OSG_WINDOWING_SYSTEM} STREQUAL "Carbon") - - # FIXME: OS X needs selection mechanism for Cocoa, Carbon, X11 + IF(${OSG_WINDOWING_SYSTEM} STREQUAL "Cocoa") + ADD_DEFINITIONS(-DUSE_DARWIN_COCOA_IMPLEMENTATION) SET(LIB_PUBLIC_HEADERS ${LIB_PUBLIC_HEADERS} - ${HEADER_PATH}/api/Carbon/GraphicsWindowCarbon - ${HEADER_PATH}/api/Carbon/PixelBufferCarbon + ${HEADER_PATH}/api/Cocoa/GraphicsHandleCocoa + ${HEADER_PATH}/api/Cocoa/GraphicsWindowCocoa + ${HEADER_PATH}/api/Cocoa/PixelBufferCocoa ) SET(LIB_COMMON_FILES ${LIB_COMMON_FILES} - GraphicsWindowCarbon.cpp - PixelBufferCarbon.cpp + GraphicsWindowCocoa.mm + DarwinUtils.h + DarwinUtils.mm + PixelBufferCocoa.mm ) - ELSE(${OSG_WINDOWING_SYSTEM} STREQUAL "Carbon") + SET(LIB_EXTRA_LIBS ${COCOA_LIBRARY} ${LIB_EXTRA_LIBS}) - # X11 for everybody else + ELSE(${OSG_WINDOWING_SYSTEM} STREQUAL "Cocoa") + IF(${OSG_WINDOWING_SYSTEM} STREQUAL "Carbon") + ADD_DEFINITIONS(-DUSE_DARWIN_CARBON_IMPLEMENTATION) + SET(LIB_PUBLIC_HEADERS ${LIB_PUBLIC_HEADERS} + ${HEADER_PATH}/api/Carbon/GraphicsWindowCarbon + ${HEADER_PATH}/api/Carbon/PixelBufferCarbon + ) + SET(LIB_COMMON_FILES ${LIB_COMMON_FILES} + GraphicsWindowCarbon.cpp + DarwinUtils.h + DarwinUtils.mm + PixelBufferCarbon.cpp + ) + SET(LIB_EXTRA_LIBS ${COCOA_LIBRARY} ${LIB_EXTRA_LIBS}) + + ELSE(${OSG_WINDOWING_SYSTEM} STREQUAL "Carbon") + + # X11 for everybody else - INCLUDE(FindPkgConfig OPTIONAL) - IF (PKG_CONFIG_FOUND) + INCLUDE(FindPkgConfig OPTIONAL) + IF (PKG_CONFIG_FOUND) - PKG_CHECK_MODULES(XRANDR xrandr) - - IF (XRANDR_FOUND) - OPTION(OSGVIEWER_USE_XRANDR "Set to ON to enable Xrandr support for GraphicsWindowX11." ON) - ELSE(XRANDR_FOUND) + PKG_CHECK_MODULES(XRANDR xrandr) + + IF (XRANDR_FOUND) + OPTION(OSGVIEWER_USE_XRANDR "Set to ON to enable Xrandr support for GraphicsWindowX11." ON) + ELSE(XRANDR_FOUND) + SET(OSGVIEWER_USE_XRANDR OFF) + ENDIF (XRANDR_FOUND) + ELSE(PKG_CONFIG_FOUND) SET(OSGVIEWER_USE_XRANDR OFF) - ENDIF (XRANDR_FOUND) - ELSE(PKG_CONFIG_FOUND) - SET(OSGVIEWER_USE_XRANDR OFF) - ENDIF(PKG_CONFIG_FOUND) + ENDIF(PKG_CONFIG_FOUND) - SET(LIB_PUBLIC_HEADERS ${LIB_PUBLIC_HEADERS} - ${HEADER_PATH}/api/X11/GraphicsWindowX11 - ${HEADER_PATH}/api/X11/PixelBufferX11 - ) + SET(LIB_PUBLIC_HEADERS ${LIB_PUBLIC_HEADERS} + ${HEADER_PATH}/api/X11/GraphicsWindowX11 + ${HEADER_PATH}/api/X11/PixelBufferX11 + ) - SET(LIB_COMMON_FILES ${LIB_COMMON_FILES} - GraphicsWindowX11.cpp - PixelBufferX11.cpp - ) - - IF(OSGVIEWER_USE_XRANDR) - - ADD_DEFINITIONS(-DOSGVIEWER_USE_XRANDR) - SET(LIB_PRIVATE_HEADERS ${LIB_PRIVATE_HEADERS} ${XRANDR_INCLUDE_DIRS} ) - - IF (X11_Xrandr_LIB) - SET(LIB_EXTRA_LIBS ${X11_Xrandr_LIB} ${LIB_EXTRA_LIBS}) - ELSE(X11_Xrandr_LIB) - SET(LIB_EXTRA_LIBS ${XRANDR_LIBRARIES} ${LIB_EXTRA_LIBS}) - ENDIF(X11_Xrandr_LIB) + SET(LIB_COMMON_FILES ${LIB_COMMON_FILES} + GraphicsWindowX11.cpp + PixelBufferX11.cpp + ) - ENDIF(OSGVIEWER_USE_XRANDR) - - # X11 on Apple requires X11 library plus OpenGL linking hack on Leopard - IF(APPLE) - # Find GL/glx.h - IF(EXISTS ${CMAKE_OSX_SYSROOT}/usr/X11/include/GL/glx.h) - SET(OPENGL_INCLUDE_DIR /usr/X11/include) - SET(OPENGL_LIBRARIES /usr/X11/lib/libGL.dylib) - ELSEIF(EXISTS ${CMAKE_OSX_SYSROOT}/usr/X11R6/include/GL/glx.h) - SET(OPENGL_INCLUDE_DIR /usr/X11R6/include) - SET(OPENGL_LIBRARIES /usr/X11R6/lib/libGL.dylib) - ENDIF(EXISTS ${CMAKE_OSX_SYSROOT}/usr/X11/include/GL/glx.h) - INCLUDE_DIRECTORIES(BEFORE SYSTEM ${OPENGL_INCLUDE_DIR}) - - SET(LIB_EXTRA_LIBS ${X11_X11_LIB} ${OPENGL_LIBRARIES} ${LIB_EXTRA_LIBS}) - SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-dylib_file,/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib:${CMAKE_OSX_SYSROOT}/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib") - ENDIF(APPLE) + IF(OSGVIEWER_USE_XRANDR) + ADD_DEFINITIONS(-DOSGVIEWER_USE_XRANDR) + SET(LIB_PRIVATE_HEADERS ${LIB_PRIVATE_HEADERS} ${XRANDR_INCLUDE_DIRS} ) - ENDIF(${OSG_WINDOWING_SYSTEM} STREQUAL "Carbon") + IF (X11_Xrandr_LIB) + SET(LIB_EXTRA_LIBS ${X11_Xrandr_LIB} ${LIB_EXTRA_LIBS}) + ELSE(X11_Xrandr_LIB) + SET(LIB_EXTRA_LIBS ${XRANDR_LIBRARIES} ${LIB_EXTRA_LIBS}) + ENDIF(X11_Xrandr_LIB) + + ENDIF(OSGVIEWER_USE_XRANDR) + # X11 on Apple requires X11 library plus OpenGL linking hack on Leopard + IF(APPLE) + # Find GL/glx.h + IF(EXISTS ${CMAKE_OSX_SYSROOT}/usr/X11/include/GL/glx.h) + SET(OPENGL_INCLUDE_DIR /usr/X11/include) + SET(OPENGL_LIBRARIES /usr/X11/lib/libGL.dylib) + ELSEIF(EXISTS ${CMAKE_OSX_SYSROOT}/usr/X11R6/include/GL/glx.h) + SET(OPENGL_INCLUDE_DIR /usr/X11R6/include) + SET(OPENGL_LIBRARIES /usr/X11R6/lib/libGL.dylib) + ENDIF(EXISTS ${CMAKE_OSX_SYSROOT}/usr/X11/include/GL/glx.h) + INCLUDE_DIRECTORIES(BEFORE SYSTEM ${OPENGL_INCLUDE_DIR}) + + SET(LIB_EXTRA_LIBS ${X11_X11_LIB} ${OPENGL_LIBRARIES} ${LIB_EXTRA_LIBS}) + SET(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} -Wl,-dylib_file,/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib:${CMAKE_OSX_SYSROOT}/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib") + ENDIF(APPLE) + + + ENDIF(${OSG_WINDOWING_SYSTEM} STREQUAL "Carbon") + ENDIF(${OSG_WINDOWING_SYSTEM} STREQUAL "Cocoa") ENDIF(WIN32) diff --git a/src/osgViewer/DarwinUtils.h b/src/osgViewer/DarwinUtils.h new file mode 100755 index 000000000..bed2ffa4d --- /dev/null +++ b/src/osgViewer/DarwinUtils.h @@ -0,0 +1,152 @@ +/* + * DarwinUtils.h + * OpenSceneGraph + * + * Created by Stephan Huber on 27.06.08. + * Copyright 2008 Stephan Maximilian Huber, digital mind. All rights reserved. + * + */ + +#ifdef __APPLE__ + +#ifndef DARWIN_UTILS_HEADER_ +#define DARWIN_UTILS_HEADER_ + +#include +#include +#include +#include + + +//#define USE_DARWIN_COCOA_IMPLEMENTATION 1 +//#define USE_DARWIN_CARBON_IMPLEMENTATION 1 + +namespace osgDarwin { + + +/** 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: + class WindowAdapter : public osg::Referenced { + + public: + WindowAdapter() : osg::Referenced() {} + + virtual bool valid() = 0; + virtual void getWindowBounds(CGRect& rect) = 0; + virtual osgViewer::GraphicsWindow* getWindow() = 0; + + protected: + virtual ~WindowAdapter() {} + }; + + MenubarController(); + static MenubarController* instance(); + + void attachWindow(WindowAdapter* win); + void update(); + void detachWindow(osgViewer::GraphicsWindow* win); + + private: + typedef std::list< osg::ref_ptr< WindowAdapter > > WindowList; + WindowList _list; + bool _menubarShown; + CGRect _availRect; + CGRect _mainScreenBounds; + OpenThreads::Mutex _mutex; + +}; + + + +struct DarwinWindowingSystemInterface : public osg::GraphicsContext::WindowingSystemInterface +{ + public: + DarwinWindowingSystemInterface(); + + /** dtor */ + ~DarwinWindowingSystemInterface(); + + /** @return a CGDirectDisplayID for a ScreenIdentifier */ + CGDirectDisplayID getDisplayID(const osg::GraphicsContext::ScreenIdentifier& si); + + /** @return count of attached screens */ + virtual unsigned int getNumScreens(const osg::GraphicsContext::ScreenIdentifier& si) ; + + virtual void getScreenSettings(const osg::GraphicsContext::ScreenIdentifier& si, osg::GraphicsContext::ScreenSettings & resolution); + + virtual void enumerateScreenSettings(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, osg::GraphicsContext::ScreenSettingsList & resolutionList); + + virtual bool setScreenSettings (const osg::GraphicsContext::ScreenIdentifier & si, const osg::GraphicsContext::ScreenSettings & settings); + + /** return the top left coord of a specific screen in global screen space */ + void getScreenTopLeft(const osg::GraphicsContext::ScreenIdentifier& si, int& x, int& y); + + + + /** returns screen-ndx containing rect x,y,w,h */ + unsigned int getScreenContaining(int x, int y, int w, int h); + + protected: + + /** implementation of setScreenResolution */ + bool setScreenResolutionImpl(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, unsigned int width, unsigned int height) ; + + /** implementation of setScreenRefreshRate */ + bool setScreenRefreshRateImpl(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, double refreshRate); + + + template + osg::GraphicsContext* createGraphicsContextImplementation(osg::GraphicsContext::Traits* traits) + { + if (traits->pbuffer) + { + osg::ref_ptr pbuffer = new PixelBufferImplementation(traits); + if (pbuffer->valid()) return pbuffer.release(); + else return 0; + } + else + { + osg::ref_ptr window = new GraphicsWindowImplementation(traits); + if (window->valid()) return window.release(); + else return 0; + } + } + + + private: + CGDisplayCount _displayCount; + CGDirectDisplayID* _displayIds; + + +}; + +template +struct RegisterWindowingSystemInterfaceProxy +{ + RegisterWindowingSystemInterfaceProxy() + { + osg::GraphicsContext::setWindowingSystemInterface(new WSI); + } + + ~RegisterWindowingSystemInterfaceProxy() + { + if (osg::Referenced::getDeleteHandler()) + { + osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0); + osg::Referenced::getDeleteHandler()->flushAll(); + } + + osg::GraphicsContext::setWindowingSystemInterface(0); + } +}; + + + +} + +#endif + +#endif // __APPLE__ diff --git a/src/osgViewer/DarwinUtils.mm b/src/osgViewer/DarwinUtils.mm new file mode 100755 index 000000000..fa909d09e --- /dev/null +++ b/src/osgViewer/DarwinUtils.mm @@ -0,0 +1,390 @@ +/* + * DarwinUtils.cpp + * OpenSceneGraph + * + * Created by Stephan Huber on 27.06.08. + * Copyright 2008 Stephan Maximilian Huber, digital mind. All rights reserved. + * + */ + +#include +#include +#include "DarwinUtils.h" +#include + +@interface MenubarToggler : NSObject { + +} + +-(void) show: (id) data; +-(void) hide: (id) data; + +@end + +@implementation MenubarToggler + + + +-(void) hide:(id) data +{ + OSErr error = SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar); + if (error) { + osg::notify(osg::DEBUG_INFO) << "MenubarToggler::hide failed with " << error << std::endl; + } +} + + +-(void) show:(id) data +{ + OSErr error = SetSystemUIMode(kUIModeNormal, 0); + if (error) { + osg::notify(osg::DEBUG_INFO) << "MenubarToggler::show failed with " << error << std::endl; + } +} + + +@end + +namespace osgDarwin { + + +static inline CGRect toCGRect(NSRect nsRect) +{ + CGRect cgRect; + + cgRect.origin.x = nsRect.origin.x; + cgRect.origin.y = nsRect.origin.y; + cgRect.size.width = nsRect.size.width; + cgRect.size.height = nsRect.size.height; + + return cgRect; +} + + +MenubarController::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 + + NSRect rect = [[[NSScreen screens] objectAtIndex: 0] visibleFrame]; + _availRect = toCGRect(rect); + + // now we need the rect of the main-display including the menubar and the dock + _mainScreenBounds = CGDisplayBounds( CGMainDisplayID() ); + + + // NSRect 0/0 is bottom/left, _mainScreenBounds 0/0 is top/left + _availRect.origin.y = _mainScreenBounds.size.height - _availRect.size.height - _availRect.origin.y; + + + // hide the menubar initially + SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar); +} + + + + +MenubarController* MenubarController::instance() +{ + static osg::ref_ptr s_menubar_controller = new MenubarController(); + return s_menubar_controller.get(); +} + + +void MenubarController::attachWindow(WindowAdapter* win) +{ + OpenThreads::ScopedLock lock(_mutex); + _list.push_back(win); + update(); +} + + +void MenubarController::detachWindow(osgViewer::GraphicsWindow* win) +{ + OpenThreads::ScopedLock lock(_mutex); + for(WindowList::iterator i = _list.begin(); i != _list.end(); ) { + if ((*i)->getWindow() == 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() +{ + unsigned int windowsCoveringMenubarArea = 0; + unsigned int windowsIntersectingMainScreen = 0; + for(WindowList::iterator i = _list.begin(); i != _list.end(); ) { + WindowAdapter* wi = (*i).get(); + if (wi->valid()) { + CGRect windowBounds; + wi->getWindowBounds(windowBounds); + + if (CGRectIntersectsRect(_mainScreenBounds, windowBounds)) + { + ++windowsIntersectingMainScreen; + // osg::notify(osg::ALWAYS) << "testing rect " << windowBounds.origin.x << "/" << windowBounds.origin.y << " " << windowBounds.size.width << "x" << windowBounds.size.height << std::endl; + // osg::notify(osg::ALWAYS) << "against " << _availRect.origin.x << "/" << _availRect.origin.y << " " << _availRect.size.width << "x" << _availRect.size.height << std::endl; + // the window intersects the main-screen, does it intersect with the menubar/dock? + if (((_availRect.origin.y > _mainScreenBounds.origin.y) && (_availRect.origin.y > windowBounds.origin.y)) || + ((_availRect.origin.x > _mainScreenBounds.origin.x) && (_availRect.origin.x > windowBounds.origin.x)) || + ((_availRect.size.width < _mainScreenBounds.size.width) && (_availRect.origin.x + _availRect.size.width < windowBounds.origin.x + windowBounds.size.width)) || + ((_availRect.size.height < _mainScreenBounds.size.height) && (_availRect.origin.y + _availRect.size.height < windowBounds.origin.y + windowBounds.size.height) )) + { + ++windowsCoveringMenubarArea; + } + } + + ++i; + } + else + i = _list.erase(i); + } + + // if we use the cocoa implementation then we have a NSRunLoop in place, and so we can use the deferred menubar-toggling which is thread safe + + #ifdef USE_DARWIN_COCOA_IMPLEMENTATION + + // SetSystemUIMode is not threadsafe, you'll get crashes if you call this method from other threads + // so use a small NSObject to switch the menubar on the main thread via performSelectorOnMainThread + + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + if (windowsCoveringMenubarArea && _menubarShown) + { + + //error = SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar); + MenubarToggler* toggler = [[MenubarToggler alloc] init]; + [toggler performSelectorOnMainThread: @selector(hide:) withObject:NULL waitUntilDone: YES]; + [toggler autorelease]; + } + if (!windowsCoveringMenubarArea && !_menubarShown) + { + //error = SetSystemUIMode(kUIModeNormal, 0); + MenubarToggler* toggler = [[MenubarToggler alloc] init]; + [toggler performSelectorOnMainThread: @selector(show:) withObject:NULL waitUntilDone: YES]; + [toggler autorelease]; + } + [pool release]; + + #else + + OSErr error; + + // 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); + } + #endif + + _menubarShown = !windowsCoveringMenubarArea; +} + + + +/** 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; +} + + + +/** ctor, get a list of all attached displays */ +DarwinWindowingSystemInterface::DarwinWindowingSystemInterface() : + _displayCount(0), + _displayIds(NULL) +{ + ProcessSerialNumber sn = { 0, kCurrentProcess }; + TransformProcessType(&sn,kProcessTransformToForegroundApplication); + SetFrontProcess(&sn); + + if( CGGetActiveDisplayList( 0, NULL, &_displayCount ) != CGDisplayNoErr ) + osg::notify(osg::WARN) << "DarwinWindowingSystemInterface: could not get # of screens" << std::endl; + + _displayIds = new CGDirectDisplayID[_displayCount]; + if( CGGetActiveDisplayList( _displayCount, _displayIds, &_displayCount ) != CGDisplayNoErr ) + osg::notify(osg::WARN) << "DarwinWindowingSystemInterface: CGGetActiveDisplayList failed" << std::endl; + + } + +/** dtor */ +DarwinWindowingSystemInterface::~DarwinWindowingSystemInterface() +{ + 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 */ +CGDirectDisplayID DarwinWindowingSystemInterface::getDisplayID(const osg::GraphicsContext::ScreenIdentifier& si) { + if (si.screenNum < static_cast(_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 */ +unsigned int DarwinWindowingSystemInterface::getNumScreens(const osg::GraphicsContext::ScreenIdentifier& si) +{ + return _displayCount; +} + +void DarwinWindowingSystemInterface::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; +} + + +void DarwinWindowingSystemInterface::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(bounds.origin.x); + y = static_cast(bounds.origin.y); + + // osg::notify(osg::DEBUG_INFO) << "topleft of screen " << si.screenNum <<" " << bounds.origin.x << "/" << bounds.origin.y << std::endl; +} + + +bool DarwinWindowingSystemInterface::setScreenSettings(const osg::GraphicsContext::ScreenIdentifier &si, const osg::GraphicsContext::ScreenSettings & settings) +{ + bool result = setScreenResolutionImpl(si, settings.width, settings.height); + if (result) + setScreenRefreshRateImpl(si, settings.refreshRate); + + return result; +} + + + +/** implementation of setScreenResolution */ +bool DarwinWindowingSystemInterface::setScreenResolutionImpl(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, unsigned int width, unsigned int height) +{ + CGDirectDisplayID displayid = getDisplayID(screenIdentifier); + + // add next line and on following line replace hard coded depth and refresh rate + CGRefreshRate refresh = getDictDouble (CGDisplayCurrentMode(displayid), kCGDisplayRefreshRate); + CFDictionaryRef display_mode_values = + CGDisplayBestModeForParametersAndRefreshRate( + displayid, + CGDisplayBitsPerPixel(displayid), + width, height, + refresh, + NULL); + + + CGDisplaySwitchToMode(displayid, display_mode_values); + return true; +} + +/** implementation of setScreenRefreshRate */ +bool DarwinWindowingSystemInterface::setScreenRefreshRateImpl(const osg::GraphicsContext::ScreenIdentifier& screenIdentifier, double refreshRate) { + + boolean_t success(false); + unsigned width, height; + getScreenResolution(screenIdentifier, width, height); + + CGDirectDisplayID displayid = getDisplayID(screenIdentifier); + + // add next line and on following line replace hard coded depth and refresh rate + CFDictionaryRef display_mode_values = + CGDisplayBestModeForParametersAndRefreshRate( + displayid, + CGDisplayBitsPerPixel(displayid), + width, height, + refreshRate, + &success); + + + if (success) + CGDisplaySwitchToMode(displayid, display_mode_values); + + return (success != 0); +} + + +unsigned int DarwinWindowingSystemInterface::getScreenContaining(int x, int y, int w, int h) +{ + CGRect rect = CGRectMake(x,y,w,h); + for(unsigned int i = 0; i < _displayCount; ++i) { + CGRect bounds = CGDisplayBounds( getDisplayID(i) ); + if (CGRectIntersectsRect(bounds, rect)) { + return i; + } + } + + return 0; +} + + + + + + +} diff --git a/src/osgViewer/GraphicsWindowCocoa.mm b/src/osgViewer/GraphicsWindowCocoa.mm new file mode 100755 index 000000000..f645a87ea --- /dev/null +++ b/src/osgViewer/GraphicsWindowCocoa.mm @@ -0,0 +1,1520 @@ +/* + * GraphicsWindowCocoa.cpp + * OpenSceneGraph + * + * Created by Stephan Huber on 27.06.08. + * Copyright 2008 Stephan Maximilian Huber, digital mind. All rights reserved. + * + * Some code borrowed from the implementation of CocoaViewer, + * Created by Eric Wing on 11/12/06. and ported by Martin Lavery 7/06/07 + * + * Other snippets are borrowed from the Cocoa-implementation of the SDL-lib + */ + +#include +#include +#include + +#include + +#include "DarwinUtils.h" + +//#define DEBUG_OUT(s) std::cout << "GraphicsWindowCocoa :: " << s << std::endl; + +#define DEBUG_OUT(s) ; + +static bool s_quit_requested = false; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 +@interface NSApplication(NSAppleMenu) +- (void)setAppleMenu:(NSMenu *)menu; +@end +#endif + + +// ---------------------------------------------------------------------------------------------------------- +// small helper class remapping key-codes +// ---------------------------------------------------------------------------------------------------------- +// small helper class which maps the raw key codes to osgGA::GUIEventAdapter::Keys + +class CocoaKeyboardMap { + + public: + CocoaKeyboardMap() + { + _keymap[27] = osgGA::GUIEventAdapter::KEY_Escape; + _keymap[13] = osgGA::GUIEventAdapter::KEY_Return; + _keymap[3] = osgGA::GUIEventAdapter::KEY_KP_Enter; + _keymap[9] = osgGA::GUIEventAdapter::KEY_Tab; + _keymap[32] = osgGA::GUIEventAdapter::KEY_Space; + _keymap[127] = osgGA::GUIEventAdapter::KEY_BackSpace; + + + _keymap[NSHomeFunctionKey] = osgGA::GUIEventAdapter::KEY_Home; + _keymap[NSEndFunctionKey] = osgGA::GUIEventAdapter::KEY_End; + _keymap[NSPageUpFunctionKey] = osgGA::GUIEventAdapter::KEY_Page_Up; + _keymap[NSPageDownFunctionKey] = osgGA::GUIEventAdapter::KEY_Page_Down; + _keymap[NSLeftArrowFunctionKey] = osgGA::GUIEventAdapter::KEY_Left; + _keymap[NSRightArrowFunctionKey] = osgGA::GUIEventAdapter::KEY_Right; + _keymap[NSUpArrowFunctionKey] = osgGA::GUIEventAdapter::KEY_Up; + _keymap[NSDownArrowFunctionKey] = osgGA::GUIEventAdapter::KEY_Down; + + _keymap[NSDeleteFunctionKey] = osgGA::GUIEventAdapter::KEY_Delete; + + _keymap[NSF1FunctionKey] = osgGA::GUIEventAdapter::KEY_F1; + _keymap[NSF2FunctionKey] = osgGA::GUIEventAdapter::KEY_F2; + _keymap[NSF3FunctionKey] = osgGA::GUIEventAdapter::KEY_F3; + _keymap[NSF4FunctionKey] = osgGA::GUIEventAdapter::KEY_F4; + _keymap[NSF5FunctionKey] = osgGA::GUIEventAdapter::KEY_F5; + _keymap[NSF6FunctionKey] = osgGA::GUIEventAdapter::KEY_F6; + _keymap[NSF7FunctionKey] = osgGA::GUIEventAdapter::KEY_F7; + _keymap[NSF8FunctionKey] = osgGA::GUIEventAdapter::KEY_F8; + _keymap[NSF9FunctionKey] = osgGA::GUIEventAdapter::KEY_F9; + + _keymap[NSF10FunctionKey] = osgGA::GUIEventAdapter::KEY_F10; + _keymap[NSF11FunctionKey] = osgGA::GUIEventAdapter::KEY_F11; + _keymap[NSF12FunctionKey] = osgGA::GUIEventAdapter::KEY_F12; + _keymap[NSF13FunctionKey] = osgGA::GUIEventAdapter::KEY_F13; + _keymap[NSF14FunctionKey] = osgGA::GUIEventAdapter::KEY_F14; + _keymap[NSF15FunctionKey] = osgGA::GUIEventAdapter::KEY_F15; + _keymap[NSF16FunctionKey] = osgGA::GUIEventAdapter::KEY_F16; + _keymap[NSF17FunctionKey] = osgGA::GUIEventAdapter::KEY_F17; + _keymap[NSF18FunctionKey] = osgGA::GUIEventAdapter::KEY_F18; + _keymap[NSF19FunctionKey] = osgGA::GUIEventAdapter::KEY_F19; + + _keymap[NSF20FunctionKey] = osgGA::GUIEventAdapter::KEY_F20; + _keymap[NSF21FunctionKey] = osgGA::GUIEventAdapter::KEY_F21; + _keymap[NSF22FunctionKey] = osgGA::GUIEventAdapter::KEY_F22; + _keymap[NSF23FunctionKey] = osgGA::GUIEventAdapter::KEY_F23; + _keymap[NSF24FunctionKey] = osgGA::GUIEventAdapter::KEY_F24; + _keymap[NSF25FunctionKey] = osgGA::GUIEventAdapter::KEY_F25; + _keymap[NSF26FunctionKey] = osgGA::GUIEventAdapter::KEY_F26; + _keymap[NSF27FunctionKey] = osgGA::GUIEventAdapter::KEY_F27; + _keymap[NSF28FunctionKey] = osgGA::GUIEventAdapter::KEY_F28; + _keymap[NSF29FunctionKey] = osgGA::GUIEventAdapter::KEY_F29; + + _keymap[NSF30FunctionKey] = osgGA::GUIEventAdapter::KEY_F30; + _keymap[NSF31FunctionKey] = osgGA::GUIEventAdapter::KEY_F31; + _keymap[NSF32FunctionKey] = osgGA::GUIEventAdapter::KEY_F32; + _keymap[NSF33FunctionKey] = osgGA::GUIEventAdapter::KEY_F33; + _keymap[NSF34FunctionKey] = osgGA::GUIEventAdapter::KEY_F34; + _keymap[NSF35FunctionKey] = osgGA::GUIEventAdapter::KEY_F35; + + + _keypadmap['='] = osgGA::GUIEventAdapter::KEY_KP_Equal; + _keypadmap['*'] = osgGA::GUIEventAdapter::KEY_KP_Multiply; + _keypadmap['+'] = osgGA::GUIEventAdapter::KEY_KP_Add; + _keypadmap['-'] = osgGA::GUIEventAdapter::KEY_KP_Subtract; + _keypadmap['.'] = osgGA::GUIEventAdapter::KEY_KP_Decimal; + _keypadmap['/'] = osgGA::GUIEventAdapter::KEY_KP_Divide; + + _keypadmap['0'] = osgGA::GUIEventAdapter::KEY_KP_0; + _keypadmap['1'] = osgGA::GUIEventAdapter::KEY_KP_1; + _keypadmap['2'] = osgGA::GUIEventAdapter::KEY_KP_2; + _keypadmap['3'] = osgGA::GUIEventAdapter::KEY_KP_3; + _keypadmap['4'] = osgGA::GUIEventAdapter::KEY_KP_4; + _keypadmap['5'] = osgGA::GUIEventAdapter::KEY_KP_5; + _keypadmap['6'] = osgGA::GUIEventAdapter::KEY_KP_6; + _keypadmap['7'] = osgGA::GUIEventAdapter::KEY_KP_7; + _keypadmap['8'] = osgGA::GUIEventAdapter::KEY_KP_8; + _keypadmap['9'] = osgGA::GUIEventAdapter::KEY_KP_9; + } + + ~CocoaKeyboardMap() { + } + + unsigned int remapKey(unsigned int key, bool pressedOnKeypad = false) + { + if (pressedOnKeypad) { + KeyMap::iterator itr = _keypadmap.find(key); + if (itr == _keypadmap.end()) return key; + else return itr->second; + } + + KeyMap::iterator itr = _keymap.find(key); + if (itr == _keymap.end()) return key; + else return itr->second; + } + private: + typedef std::map KeyMap; + KeyMap _keymap, _keypadmap; +}; + + +// ---------------------------------------------------------------------------------------------------------- +// remapCocoaKey +// ---------------------------------------------------------------------------------------------------------- +static unsigned int remapCocoaKey(unsigned int key, bool pressedOnKeypad = false) +{ + static CocoaKeyboardMap s_CocoaKeyboardMap; + return s_CocoaKeyboardMap.remapKey(key, pressedOnKeypad); +} + + +std::ostream& operator<<(std::ostream& os, const NSRect& rect) +{ + os << rect.origin.x << "/" << rect.origin.y << " " << rect.size.width << "x" << rect.size.height; + return os; +} + +// ---------------------------------------------------------------------------------------------------------- +// Cocoa uses a coordinate system where its origin is in the bottom left corner, +// osg and quartz uses top left for the origin +// +// these 2 methods convets rects between the different coordinate systems +// ---------------------------------------------------------------------------------------------------------- + +static NSRect convertFromQuartzCoordinates(const NSRect& rect) +{ + NSRect frame = [[[NSScreen screens] objectAtIndex: 0] frame]; + float y = frame.size.height - rect.origin.y - rect.size.height; + NSRect converted = NSMakeRect(rect.origin.x, y, rect.size.width, rect.size.height); + + // std::cout << "converting from Quartz " << rect << " to " << converted << " using screen rect " << frame << std::endl; + + return converted; +} + +static NSRect convertToQuartzCoordinates(const NSRect& rect) +{ + NSRect frame = [[[NSScreen screens] objectAtIndex: 0] frame]; + + float y = frame.size.height - (rect.origin.y + rect.size.height); + NSRect converted = NSMakeRect(rect.origin.x, y, rect.size.width, rect.size.height); + + // std::cout << "converting To Quartz " << rect << " to " << converted << " using screen rect " << frame << std::endl; + + return converted; +} + +#pragma mark CocoaAppDelegate + +// ---------------------------------------------------------------------------------------------------------- +// the app-delegate, handling quit-requests +// ---------------------------------------------------------------------------------------------------------- + +@interface CocoaAppDelegate : NSObject +{ +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification; +@end + +@implementation CocoaAppDelegate +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ + s_quit_requested = true; + DEBUG_OUT("quit requested "); + return NSTerminateCancel; +} + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification +{ + DEBUG_OUT("applicationDidFinishLaunching"); +} + +@end + +#pragma mark GraphicsWindowCocoaWindow + +// ---------------------------------------------------------------------------------------------------------- +// GraphicsWindowCocoaWindow, implements canBecomeKeyWindow + canBecomeMainWindow +// ---------------------------------------------------------------------------------------------------------- + +@interface GraphicsWindowCocoaWindow : NSWindow +{ +} + +- (BOOL) canBecomeKeyWindow; +- (BOOL) canBecomeMainWindow; + +@end + +@implementation GraphicsWindowCocoaWindow + + +- (BOOL) canBecomeKeyWindow +{ + return YES; +} + +- (BOOL) canBecomeMainWindow +{ + return YES; +} + +@end + +#pragma mark GraphicsWindowCocoaGLView + + +// ---------------------------------------------------------------------------------------------------------- +// GraphicsWindowCocoaGLView +// custom NSOpenGLView-class handling mouse- and keyboard-events, forwarding them to the EventQueue +// some code borrowed from the example osgCocoaViewer from E.Wing +// ---------------------------------------------------------------------------------------------------------- + +@interface GraphicsWindowCocoaGLView : NSOpenGLView +{ + @private + osgViewer::GraphicsWindowCocoa* _win; + BOOL _isUsingCtrlClick, _isUsingOptionClick; + unsigned int _cachedModifierFlags; + BOOL _handleTabletEvents; + +} +- (void)setGraphicsWindowCocoa: (osgViewer::GraphicsWindowCocoa*) win; + +- (void)keyDown:(NSEvent *)theEvent; +- (void)keyUp:(NSEvent *)theEvent; +- (void)flagsChanged:(NSEvent *)theEvent; +- (void) mouseMoved:(NSEvent*)theEvent; +- (void) mouseDown:(NSEvent*)theEvent; +- (void) mouseDragged:(NSEvent*)theEvent; +- (void) mouseUp:(NSEvent*)theEvent; +- (void) rightMouseDown:(NSEvent*)theEvent; +- (void) rightMouseDragged:(NSEvent*)theEvent; +- (void) rightMouseUp:(NSEvent*)theEvent; +- (void) otherMouseDown:(NSEvent*)theEvent; +- (void) otherMouseDragged:(NSEvent*)theEvent; +- (void) otherMouseUp:(NSEvent*)theEvent; + +- (NSPoint) getLocalPoint: (NSEvent*)theEvent; +- (void) handleModifiers: (NSEvent*)theEvent; +- (void) setIsUsingCtrlClick:(BOOL)is_using_ctrl_click; +- (BOOL) isUsingCtrlClick; +- (void) setIsUsingOptionClick:(BOOL)is_using_option_click; +- (BOOL) isUsingOptionClick; + +- (void) doLeftMouseButtonDown:(NSEvent*)theEvent; +- (void) doLeftMouseButtonUp:(NSEvent*)theEvent; +- (void) doRightMouseButtonDown:(NSEvent*)theEvent; +- (void) doRightMouseButtonUp:(NSEvent*)theEvent; +- (void) doMiddleMouseButtonDown:(NSEvent*)theEvent; +- (void) doExtraMouseButtonDown:(NSEvent*)theEvent buttonNumber:(int)button_number; +- (void) doMiddleMouseButtonUp:(NSEvent*)theEvent; +- (void) doExtraMouseButtonUp:(NSEvent*)theEvent buttonNumber:(int)button_number; +- (void) scrollWheel:(NSEvent*)theEvent; + +- (void)tabletPoint:(NSEvent *)theEvent; +- (void)tabletProximity:(NSEvent *)theEvent; +- (void)handleTabletEvents:(NSEvent*)theEvent; + +- (BOOL)acceptsFirstResponder; +- (BOOL)becomeFirstResponder; +- (BOOL)resignFirstResponder; + +@end + +@implementation GraphicsWindowCocoaGLView + + +-(void) setGraphicsWindowCocoa: (osgViewer::GraphicsWindowCocoa*) win +{ + _win = win; +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (BOOL)becomeFirstResponder +{ + return YES; +} + +- (BOOL)resignFirstResponder +{ + return YES; +} + + +- (NSPoint) getLocalPoint: (NSEvent*)theEvent +{ + return [self convertPoint:[theEvent locationInWindow] fromView:nil]; +} + + +- (void) handleModifiers: (NSEvent*)theEvent +{ + DEBUG_OUT("handling modifiers"); + + if ((!_win) || (!_win->getEventQueue())) + return; // no event queue in place + + unsigned int flags = [theEvent modifierFlags]; + + if (flags == _cachedModifierFlags) + return; + + const unsigned int masks[] = { + NSShiftKeyMask, + NSControlKeyMask, + NSAlternateKeyMask, + NSCommandKeyMask, + NSAlphaShiftKeyMask + }; + + const unsigned int keys[] = { + osgGA::GUIEventAdapter::KEY_Shift_L, + osgGA::GUIEventAdapter::KEY_Control_L, + osgGA::GUIEventAdapter::KEY_Alt_L, + osgGA::GUIEventAdapter::KEY_Super_L, + osgGA::GUIEventAdapter::KEY_Caps_Lock + }; + + for(unsigned int i = 0; i < 5; ++i) { + + if ((flags & masks[i]) && !(_cachedModifierFlags & masks[i])) + { + _win->getEventQueue()->keyPress(keys[i]); + } + + if (!(flags & masks[i]) && (_cachedModifierFlags & masks[i])) + { + _win->getEventQueue()->keyRelease(keys[i]); + } + } + + _cachedModifierFlags = flags; + +} + +- (void)flagsChanged:(NSEvent *)theEvent { + [self handleModifiers: theEvent]; +} + +- (void) mouseMoved:(NSEvent*)theEvent +{ + NSPoint converted_point = [self getLocalPoint: theEvent]; + DEBUG_OUT("Mouse moved" << converted_point.x << "/" << converted_point.y); + _win->getEventQueue()->mouseMotion(converted_point.x, converted_point.y); +} + + + +- (void) mouseDown:(NSEvent*)theEvent +{ + DEBUG_OUT("Mouse down"); + // Because many Mac users have only a 1-button mouse, we should provide ways + // to access the button 2 and 3 actions of osgViewer. + // I will use the Ctrl modifer to represent right-clicking + // and Option modifier to represent middle clicking. + if([theEvent modifierFlags] & NSControlKeyMask) + { + [self setIsUsingCtrlClick:YES]; + [self doRightMouseButtonDown:theEvent]; + } + else if([theEvent modifierFlags] & NSAlternateKeyMask) + { + [self setIsUsingOptionClick:YES]; + [self doMiddleMouseButtonDown:theEvent]; + } + else + { + [self doLeftMouseButtonDown:theEvent]; + } + + if ([theEvent subtype] == NSTabletPointEventSubtype) { + _handleTabletEvents = true; + [self handleTabletEvents:theEvent]; + } +} + + +- (void) mouseDragged:(NSEvent*)theEvent +{ + if (!_win) return; + + NSPoint converted_point = [self getLocalPoint: theEvent]; + _win->getEventQueue()->mouseMotion(converted_point.x, converted_point.y); + + if (_handleTabletEvents) + [self handleTabletEvents:theEvent]; +} + + +- (void) mouseUp:(NSEvent*)theEvent +{ + // Because many Mac users have only a 1-button mouse, we should provide ways + // to access the button 2 and 3 actions of osgViewer. + // I will use the Ctrl modifer to represent right-clicking + // and Option modifier to represent middle clicking. + if([self isUsingCtrlClick] == YES) + { + [self setIsUsingCtrlClick:NO]; + [self doRightMouseButtonUp:theEvent]; + } + else if([self isUsingOptionClick] == YES) + { + [self setIsUsingOptionClick:NO]; + [self doMiddleMouseButtonUp:theEvent]; + } + else + { + [self doLeftMouseButtonUp:theEvent]; + } + _handleTabletEvents = false; +} + +- (void) rightMouseDown:(NSEvent*)theEvent +{ + [self doRightMouseButtonDown:theEvent]; +} + +- (void) rightMouseDragged:(NSEvent*)theEvent +{ + if (!_win) return; + + NSPoint converted_point = [self getLocalPoint: theEvent]; + _win->getEventQueue()->mouseMotion(converted_point.x, converted_point.y); +} + +- (void) rightMouseUp:(NSEvent*)theEvent +{ + [self doRightMouseButtonUp:theEvent]; + _handleTabletEvents = false; +} + +// "otherMouse" seems to capture middle button and any other buttons beyond (4th, etc). +- (void) otherMouseDown:(NSEvent*)theEvent +{ + // Button 0 is left + // Button 1 is right + // Button 2 is middle + // Button 3 keeps going + // osgViewer expects 1 for left, 3 for right, 2 for middle + // osgViewer has a reversed number mapping for right and middle compared to Cocoa + if([theEvent buttonNumber] == 2) + { + [self doMiddleMouseButtonDown:theEvent]; + } + else // buttonNumber should be 3,4,5,etc; must map to 4,5,6,etc in osgViewer + { + [self doExtraMouseButtonDown:theEvent buttonNumber:[theEvent buttonNumber]]; + } +} + +- (void) otherMouseDragged:(NSEvent*)theEvent +{ + if (!_win) return; + + NSPoint converted_point = [self getLocalPoint: theEvent]; + _win->getEventQueue()->mouseMotion(converted_point.x, converted_point.y); + +} + +// "otherMouse" seems to capture middle button and any other buttons beyond (4th, etc). +- (void) otherMouseUp:(NSEvent*)theEvent +{ + + // Button 0 is left + // Button 1 is right + // Button 2 is middle + // Button 3 keeps going + // osgViewer expects 1 for left, 3 for right, 2 for middle + // osgViewer has a reversed number mapping for right and middle compared to Cocoa + if([theEvent buttonNumber] == 2) + { + [self doMiddleMouseButtonUp:theEvent]; + } + else // buttonNumber should be 3,4,5,etc; must map to 4,5,6,etc in osgViewer + { + // I don't think osgViewer does anything for these additional buttons, + // but just in case, pass them along. But as a Cocoa programmer, you might + // think about things you can do natively here instead of passing the buck. + [self doExtraMouseButtonUp:theEvent buttonNumber:[theEvent buttonNumber]]; + } +} + +- (void) setIsUsingCtrlClick:(BOOL)is_using_ctrl_click +{ + _isUsingCtrlClick = is_using_ctrl_click; +} + +- (BOOL) isUsingCtrlClick +{ + return _isUsingCtrlClick; +} + +- (void) setIsUsingOptionClick:(BOOL)is_using_option_click +{ + _isUsingOptionClick = is_using_option_click; +} + +- (BOOL) isUsingOptionClick +{ + return _isUsingOptionClick; +} + + +- (void) doLeftMouseButtonDown:(NSEvent*)theEvent +{ + if (!_win) return; + + NSPoint converted_point = [self getLocalPoint: theEvent]; + + if([theEvent clickCount] == 1) + { + _win->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, 1); + } + else + { + _win->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, 1); + } +} + +- (void) doLeftMouseButtonUp:(NSEvent*)theEvent +{ + if (!_win) return; + + NSPoint converted_point = [self getLocalPoint: theEvent]; + + _win->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, 1); + +} + +- (void) doRightMouseButtonDown:(NSEvent*)theEvent +{ + if (!_win) return; + + NSPoint converted_point = [self getLocalPoint: theEvent]; + if([theEvent clickCount] == 1) + { + _win->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, 3); + } + else + { + _win->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, 3); + } + +} + + +- (void) doRightMouseButtonUp:(NSEvent*)theEvent +{ + if (!_win) return; + + NSPoint converted_point = [self getLocalPoint: theEvent]; + _win->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, 3); +} + +- (void) doMiddleMouseButtonDown:(NSEvent*)theEvent +{ + if (!_win) return; + + DEBUG_OUT("middleMouseDown "); + + NSPoint converted_point = [self getLocalPoint: theEvent]; + + if([theEvent clickCount] == 1) + { + _win->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, 2); + } + else + { + _win->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, 2); + } +} + +- (void) doExtraMouseButtonDown:(NSEvent*)theEvent buttonNumber:(int)button_number +{ + if (!_win) return; + + DEBUG_OUT("extraMouseDown btn: " << button_number); + + NSPoint converted_point = [self getLocalPoint: theEvent]; + if([theEvent clickCount] == 1) + { + _win->getEventQueue()->mouseButtonPress(converted_point.x, converted_point.y, button_number+1); + } + else + { + _win->getEventQueue()->mouseDoubleButtonPress(converted_point.x, converted_point.y, button_number+1); + } +} + + +- (void) doMiddleMouseButtonUp:(NSEvent*)theEvent +{ + if (!_win) return; + + NSPoint converted_point = [self getLocalPoint: theEvent]; + _win->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, 2); + +} + +- (void) doExtraMouseButtonUp:(NSEvent*)theEvent buttonNumber:(int)button_number +{ + if (!_win) return; + + NSPoint converted_point = [self getLocalPoint: theEvent]; + _win->getEventQueue()->mouseButtonRelease(converted_point.x, converted_point.y, button_number+1); +} + + + +- (void) scrollWheel:(NSEvent*)theEvent +{ + if (!_win) return; + + // Unfortunately, it turns out mouseScroll2D doesn't actually do anything. + // The camera manipulators don't seem to implement any code that utilize the scroll values. + // This this call does nothing. + _win->getEventQueue()->mouseScroll2D([theEvent deltaX], [theEvent deltaY]); +} + + + +- (void)keyDown:(NSEvent *)theEvent +{ + if (!_win) return; + + NSString* chars = [theEvent charactersIgnoringModifiers]; + unsigned int keyCode = remapCocoaKey([chars characterAtIndex:0], ([theEvent modifierFlags] & NSFunctionKeyMask) ); + // std::cout << "key dn: " <<[chars characterAtIndex:0] << "=" << keyCode << std::endl; + _win->getEventQueue()->keyPress( remapCocoaKey(keyCode), [theEvent timestamp]); +} + + +- (void)keyUp:(NSEvent *)theEvent +{ + if (!_win) return; + + NSString* chars = [theEvent charactersIgnoringModifiers]; + unsigned int keyCode = remapCocoaKey([chars characterAtIndex:0], ([theEvent modifierFlags] & NSFunctionKeyMask)); + // std::cout << "key up: " <<[chars characterAtIndex:0] << "=" << keyCode << std::endl; + _win->getEventQueue()->keyRelease( remapCocoaKey(keyCode), [theEvent timestamp]); +} + + +- (void)tabletPoint:(NSEvent *)theEvent +{ + //_handleTabletEvents = YES; + //[self handleTabletEvents:theEvent]; +} + +-(void)handleTabletEvents:(NSEvent *)theEvent +{ + if (!_win) return; + + float pressure = [theEvent pressure]; + _win->getEventQueue()->penPressure(pressure); + NSPoint tilt = [theEvent tilt]; + + _win->getEventQueue()->penOrientation (tilt.x, tilt.y, [theEvent rotation]); +} + + +- (void)tabletProximity:(NSEvent *)theEvent +{ + if (!_win) return; + + osgGA::GUIEventAdapter::TabletPointerType pt(osgGA::GUIEventAdapter::UNKNOWN); + switch ([theEvent pointingDeviceType]) { + case NSPenPointingDevice: + pt = osgGA::GUIEventAdapter::PEN; + break; + case NSCursorPointingDevice: + pt = osgGA::GUIEventAdapter::PUCK; + break; + case NSEraserPointingDevice: + pt = osgGA::GUIEventAdapter::ERASER; + break; + default: + break; + } + _win->getEventQueue()->penProximity(pt, [theEvent isEnteringProximity]); +} + + +@end + + +#pragma mark GraphicsWindowCocoaDelegate + + +// ---------------------------------------------------------------------------------------------------------- +// the window-delegate, handles moving/resizing of the window etc. +// ---------------------------------------------------------------------------------------------------------- + +@interface GraphicsWindowCocoaDelegate : NSObject +{ + @private + osgViewer::GraphicsWindowCocoa* _win; + BOOL _inDidMove; +} + +- (id)initWith: (osgViewer::GraphicsWindowCocoa*) win; +- (void)windowDidMove:(NSNotification *)notification; +- (void)windowDidResize:(NSNotification *)notification; +- (BOOL)windowShouldClose:(id)window; +- (void)updateWindowBounds; + +@end + + +@implementation GraphicsWindowCocoaDelegate + +- (id)initWith: (osgViewer::GraphicsWindowCocoa*) win +{ + _inDidMove = false; + _win = win; + return [super init]; +} + + +- (void)windowDidMove:(NSNotification *)notification +{ + [self updateWindowBounds]; +} + +- (void)windowDidResize:(NSNotification *)notification +{ + [self updateWindowBounds]; +} + +-(void)updateWindowBounds +{ + if (_inDidMove) return; + _inDidMove = true; + + GraphicsWindowCocoaWindow* nswin = _win->getWindow(); + NSRect bounds = [nswin contentRectForFrameRect: [nswin frame] ]; + + // convert to quartz-coordinate-system + bounds = convertToQuartzCoordinates(bounds); + + // std::cout << "windowdidmove: " << bounds.origin.x << " " << bounds.origin.y << " " << bounds.size.width << " " << bounds.size.height << std::endl; + + _win->adaptResize(bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height); + //_win->getEventQueue()->windowResize(bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height, _win->getEventQueue()->getTime()); + _win->requestRedraw(); + _inDidMove = false; +} + +- (BOOL)windowShouldClose:(id)window +{ + return _win->requestClose(); +} + +@end + + +#pragma mark CocoaWindowAdapter + + + +using namespace osgDarwin; +namespace osgViewer { + + +// ---------------------------------------------------------------------------------------------------------- +// small adapter class to handle the dock/menubar +// ---------------------------------------------------------------------------------------------------------- + +class CocoaWindowAdapter : public MenubarController::WindowAdapter { +public: + CocoaWindowAdapter(GraphicsWindowCocoa* win) : MenubarController::WindowAdapter(), _win(win) {} + + virtual bool valid() { return (_win.valid() && _win->valid()); } + + virtual void getWindowBounds(CGRect& rect) + { + NSRect nsrect = [_win->getWindow() frame]; + nsrect = convertToQuartzCoordinates(nsrect); + + rect.origin.x = nsrect.origin.x; + rect.origin.y = nsrect.origin.y; + rect.size.width = nsrect.size.width; + rect.size.height = nsrect.size.height; + } + + virtual osgViewer::GraphicsWindow* getWindow() {return _win.get(); } +private: + osg::observer_ptr _win; +}; + +#pragma mark GraphicsWindowCocoa + + + +// ---------------------------------------------------------------------------------------------------------- +// init +// ---------------------------------------------------------------------------------------------------------- + +void GraphicsWindowCocoa::init() +{ + if (_initialized) return; + + _closeRequested = false; + _ownsWindow = false; + _context = NULL; + _window = NULL; + _updateContext = false; + _valid = _initialized = true; +} + + +// ---------------------------------------------------------------------------------------------------------- +// setupNSWindow +// sets up the NSWindow, adds delegates, etc +// ---------------------------------------------------------------------------------------------------------- + +void GraphicsWindowCocoa::setupNSWindow(NSWindow* win) +{ + + [win setReleasedWhenClosed:NO]; + [win setDisplaysWhenScreenProfileChanges:YES]; + GraphicsWindowCocoaDelegate* delegate = [[GraphicsWindowCocoaDelegate alloc] initWith: this]; + [win setDelegate: delegate ]; + //[delegate autorelease]; + + [win makeKeyAndOrderFront:nil]; + [win setAcceptsMouseMovedEvents: YES]; + +} + + +// ---------------------------------------------------------------------------------------------------------- +// realizeImplementation, creates the window + context +// ---------------------------------------------------------------------------------------------------------- + +bool GraphicsWindowCocoa::realizeImplementation() +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + unsigned int style(NSBorderlessWindowMask); + + if (_traits->windowDecoration) { + style = NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask; + + // supportsResize works only with windows with titlebar + if (_traits->supportsResize) + style |= NSResizableWindowMask; + } + + DarwinWindowingSystemInterface* wsi = dynamic_cast(osg::GraphicsContext::getWindowingSystemInterface()); + int screenLeft(0), screenTop(0); + if (wsi) { + wsi->getScreenTopLeft((*_traits), screenLeft, screenTop); + } + + NSRect rect = NSMakeRect(_traits->x + screenLeft, _traits->y + screenTop, _traits->width, _traits->height); + + _ownsWindow = true; + + // should we create a NSView only?? + WindowData* windowData = _traits->inheritedWindowData ? dynamic_cast(_traits->inheritedWindowData.get()) : NULL; + if (windowData) + { + if (windowData->createOnlyView()) + _ownsWindow = false; + _checkForEvents = windowData->checkForEvents(); + + } + + + osg::notify(osg::DEBUG_INFO) << "GraphicsWindowCocoa::realizeImplementation / ownsWindow: " << _ownsWindow << " checkForEvents: " << _checkForEvents << std::endl; + + if (_ownsWindow) + { + _window = [[GraphicsWindowCocoaWindow alloc] initWithContentRect: rect styleMask: style backing: NSBackingStoreBuffered defer: NO]; + + if (!_window) { + osg::notify(osg::WARN) << "GraphicsWindowCocoa::realizeImplementation :: could not create window" << std::endl; + return false; + } + + rect = convertFromQuartzCoordinates(rect); + [_window setFrameOrigin: rect.origin]; + } + + NSOpenGLPixelFormatAttribute attr[32]; + int i = 0; + + attr[i++] = NSOpenGLPFADepthSize; + attr[i++] = static_cast(_traits->depth); + + if (_traits->doubleBuffer) { + attr[i++] = NSOpenGLPFADoubleBuffer; + } + + if (_traits->alpha) { + attr[i++] = NSOpenGLPFAAlphaSize; + attr[i++] = static_cast(_traits->alpha); + } + + if (_traits->stencil) { + attr[i++] = NSOpenGLPFAStencilSize; + attr[i++] = static_cast(_traits->stencil); + } + + + if (_traits->sampleBuffers) { + attr[i++] = NSOpenGLPFASampleBuffers; + attr[i++] = static_cast(_traits->sampleBuffers); + attr[i++] = NSOpenGLPFASamples; + attr[i++] = static_cast(_traits->samples); + } + + + attr[i++] = NSOpenGLPFAAccelerated; + attr[i] = static_cast(0); + + // create the context + NSOpenGLContext* sharedContext = NULL; + + GraphicsHandleCocoa* graphicsHandleCocoa = dynamic_cast(_traits->sharedContext); + if (graphicsHandleCocoa) + { + sharedContext = graphicsHandleCocoa->getNSOpenGLContext(); + } + + NSOpenGLPixelFormat* pixelformat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr ]; + _context = [[NSOpenGLContext alloc] initWithFormat: pixelformat shareContext: sharedContext]; + + if (!_context) { + osg::notify(osg::WARN) << "GraphicsWindowCocoa::realizeImplementation :: could not create context" << std::endl; + return false; + } + GraphicsWindowCocoaGLView* theView = [[ GraphicsWindowCocoaGLView alloc ] initWithFrame:[ _window frame ] ]; + [theView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable) ]; + [theView setGraphicsWindowCocoa: this]; + [theView setOpenGLContext:_context]; + _view = theView; + osg::notify(osg::DEBUG_INFO) << "GraphicsWindowCocoa::realizeImplementation / view: " << theView << std::endl; + + if (_ownsWindow) { + [_window setContentView: theView]; + setupNSWindow(_window); + [theView release]; + + MenubarController::instance()->attachWindow( new CocoaWindowAdapter(this) ); + } + else + { + windowData->setCreatedNSView(theView); + } + + [pool release]; + + + useCursor(_traits->useCursor); + setWindowName(_traits->windowName); + setVSync(_traits->vsync); + + MenubarController::instance()->update(); + + // Cocoa's origin is bottom/left: + getEventQueue()->getCurrentEventState()->setMouseYOrientation(osgGA::GUIEventAdapter::Y_INCREASING_UPWARDS); + + _valid = _initialized = _realized = true; + return _valid; +} + + + + +// ---------------------------------------------------------------------------------------------------------- +// closeImplementation +// ---------------------------------------------------------------------------------------------------------- +void GraphicsWindowCocoa::closeImplementation() +{ + _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 (_view) { + [_view setGraphicsWindowCocoa: NULL]; + } + + if (_window) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + // we have to close + release the window in the main-thread + + [_window performSelectorOnMainThread: @selector(close) withObject:NULL waitUntilDone: YES]; + [_window performSelectorOnMainThread: @selector(release) withObject:NULL waitUntilDone: YES]; + [pool release]; + } + + _window = NULL; + _view = NULL; +} + + +// ---------------------------------------------------------------------------------------------------------- +// makeCurrentImplementation +// ---------------------------------------------------------------------------------------------------------- + +bool GraphicsWindowCocoa:: makeCurrentImplementation() +{ + if (_updateContext) + { + [_context update]; + _updateContext = false; + } + + [_context makeCurrentContext]; + return true; +} + + +// ---------------------------------------------------------------------------------------------------------- +// releaseContextImplementation +// ---------------------------------------------------------------------------------------------------------- + +bool GraphicsWindowCocoa::releaseContextImplementation() +{ + [NSOpenGLContext clearCurrentContext]; + return true; +} + + +// ---------------------------------------------------------------------------------------------------------- +// swapBuffersImplementation +// ---------------------------------------------------------------------------------------------------------- + +void GraphicsWindowCocoa::swapBuffersImplementation() +{ + [_context flushBuffer]; +} + + +// ---------------------------------------------------------------------------------------------------------- +// checkEvents +// process all pending events +// ---------------------------------------------------------------------------------------------------------- +void GraphicsWindowCocoa::checkEvents() +{ + if (!_checkForEvents) + return; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + while(1) + { + /* NOTE: It may be better to use something like + NSEventTrackingRunLoopMode since we don't necessarily want all + timers/sources/observers to run, only those which would + run while tracking events. However, it should be noted that + NSEventTrackingRunLoopMode is in the common set of modes + so it may not effectively make much of a difference. + */ + NSEvent *event = [ NSApp + nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue: YES]; + if(!event) + break; + [NSApp sendEvent: event]; + } + + if (_closeRequested) + getEventQueue()->closeWindow(); + + if (s_quit_requested) { + getEventQueue()->quitApplication(); + s_quit_requested = false; + } + + [pool release]; +} + + + +// ---------------------------------------------------------------------------------------------------------- +// setWindowDecorationImplementation +// +// unfortunately there's no way to change the decoration of a window, so we create an new one +// and swap the content +// ---------------------------------------------------------------------------------------------------------- + +bool GraphicsWindowCocoa::setWindowDecorationImplementation(bool flag) +{ + if (!_realized || !_ownsWindow) return false; + + NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init]; + + unsigned int style(NSBorderlessWindowMask); + + if (flag) { + style = NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask; + + // supportsResize works only with windows with titlebar + if (_traits->supportsResize) + style |= NSResizableWindowMask; + } + NSRect rect = [_window contentRectForFrameRect: [_window frame] ]; + GraphicsWindowCocoaWindow* new_win = [[GraphicsWindowCocoaWindow alloc] initWithContentRect: rect styleMask: style backing: NSBackingStoreBuffered defer: NO]; + + if (new_win) { + [new_win setContentView: [_window contentView]]; + setupNSWindow(new_win); + [new_win setTitle: [_window title]]; + [_window close]; + [_window release]; + + _window = new_win; + [_window makeKeyAndOrderFront: nil]; + } + + [localPool release]; + + return true; +} + + +// ---------------------------------------------------------------------------------------------------------- +// grabFocus +// ---------------------------------------------------------------------------------------------------------- +void GraphicsWindowCocoa::grabFocus() +{ + if (_ownsWindow) + [_window makeKeyAndOrderFront: nil]; +} + + +// ---------------------------------------------------------------------------------------------------------- +// grabFocusIfPointerInWindow +// ---------------------------------------------------------------------------------------------------------- +void GraphicsWindowCocoa::grabFocusIfPointerInWindow() +{ + osg::notify(osg::INFO) << "GraphicsWindowCocoa :: grabFocusIfPointerInWindow not implemented yet " << std::endl; +} + + +// ---------------------------------------------------------------------------------------------------------- +// resizedImplementation +// ---------------------------------------------------------------------------------------------------------- + +void GraphicsWindowCocoa::resizedImplementation(int x, int y, int width, int height) +{ + DEBUG_OUT("resized implementation" << x << " " << y << " " << width << " " << height); + GraphicsContext::resizedImplementation(x, y, width, height); + + _updateContext = true; + + MenubarController::instance()->update(); + getEventQueue()->windowResize(x,y,width, height, getEventQueue()->getTime()); +} + + + + +// ---------------------------------------------------------------------------------------------------------- +// setWindowRectangleImplementation +// ---------------------------------------------------------------------------------------------------------- +bool GraphicsWindowCocoa::setWindowRectangleImplementation(int x, int y, int width, int height) +{ + if (!_ownsWindow) + return false; + + NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init]; + + DarwinWindowingSystemInterface* wsi = dynamic_cast(osg::GraphicsContext::getWindowingSystemInterface()); + int screenLeft(0), screenTop(0); + if (wsi) { + wsi->getScreenTopLeft((*_traits), screenLeft, screenTop); + } + + + NSRect rect = NSMakeRect(x+screenLeft,y+screenTop,width, height); + rect = convertFromQuartzCoordinates(rect); + + [_window setFrame: [NSWindow frameRectForContentRect: rect styleMask: [_window styleMask]] display: YES]; + [_context update]; + MenubarController::instance()->update(); + + [localPool release]; + + return true; +} + + +// ---------------------------------------------------------------------------------------------------------- +// +// ---------------------------------------------------------------------------------------------------------- + +void GraphicsWindowCocoa::adaptResize(int x, int y, int w, int h) +{ + + DarwinWindowingSystemInterface* wsi = dynamic_cast(osg::GraphicsContext::getWindowingSystemInterface()); + int screenLeft(0), screenTop(0); + if (wsi) { + + // get the screen containing the window + unsigned int screenNdx = wsi->getScreenContaining(x,y,w,h); + + // update traits + _traits->screenNum = screenNdx; + + // get top left of screen + wsi->getScreenTopLeft((*_traits), screenLeft, screenTop); + } + + resized(x-screenLeft,y-screenTop,w,h); +} + + +// ---------------------------------------------------------------------------------------------------------- +// setWindowName +// ---------------------------------------------------------------------------------------------------------- + +void GraphicsWindowCocoa::setWindowName (const std::string & name) +{ + if (_traits.valid()) _traits->windowName = name; + + if (!_ownsWindow) + return; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSString* title = [NSString stringWithCString: name.c_str() encoding: NSUTF8StringEncoding]; + [_window setTitle: title]; + [title release]; + [pool release]; +} + + +// ---------------------------------------------------------------------------------------------------------- +// useCursor +// ---------------------------------------------------------------------------------------------------------- + +void GraphicsWindowCocoa::useCursor(bool cursorOn) +{ + if (_traits.valid()) + _traits->useCursor = cursorOn; + DarwinWindowingSystemInterface* wsi = dynamic_cast(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) << "GraphicsWindowCocoa::useCursor failed with " << err << std::endl; + } +} + + +// ---------------------------------------------------------------------------------------------------------- +// setCursor +// ---------------------------------------------------------------------------------------------------------- + +void GraphicsWindowCocoa::setCursor(MouseCursor mouseCursor) +{ + NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init]; + + switch (mouseCursor) + { + + case NoCursor: + [NSCursor hide]; + break; + + case LeftArrowCursor: + [[NSCursor arrowCursor] set]; + break; + + case TextCursor: + [[NSCursor IBeamCursor] set]; + break; + + case CrosshairCursor: + [[NSCursor crosshairCursor] set]; + break; + + default: + osg::notify(osg::INFO) << "GraphicsWindowCocoa::setCursor :: unsupported MouseCursor: " << mouseCursor << std::endl; + } + + [localPool release]; +} + + +// ---------------------------------------------------------------------------------------------------------- +// setVSync +// ---------------------------------------------------------------------------------------------------------- + +void GraphicsWindowCocoa::setVSync(bool f) +{ + GLint VBL(f?1:0); + [_context setValues:&VBL forParameter:NSOpenGLCPSwapInterval]; +} + + +// ---------------------------------------------------------------------------------------------------------- +// d'tor +// ---------------------------------------------------------------------------------------------------------- + +GraphicsWindowCocoa::~GraphicsWindowCocoa() +{ + close(); +} + + + +#pragma mark CocoaWindowingSystemInterface + +// ---------------------------------------------------------------------------------------------------------- +// CocoaWindowingSystemInterface +// ---------------------------------------------------------------------------------------------------------- + +struct CocoaWindowingSystemInterface : public DarwinWindowingSystemInterface { + + CocoaWindowingSystemInterface() + : DarwinWindowingSystemInterface() + + { + } + + void initAsStandaloneApplication() + { + static bool s_inited = false; + if (s_inited) return; + s_inited = true; + + osg::notify(osg::INFO) << "CocoaWindowingSystemInterface::initAsStandaloneApplication " << std::endl; + + ProcessSerialNumber psn; + if (!GetCurrentProcess(&psn)) { + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); + } + + NSAutoreleasePool* localPool = [[NSAutoreleasePool alloc] init]; + + if (NSApp == nil) { + [NSApplication sharedApplication]; + } + + [NSApp setDelegate: [[CocoaAppDelegate alloc] init] ]; + + createApplicationMenus(); + + [NSApp finishLaunching]; + + [localPool release]; + } + + virtual osg::GraphicsContext* createGraphicsContext(osg::GraphicsContext::Traits* traits) + { + if (!traits->pbuffer) + { + GraphicsWindowCocoa::WindowData* windowData = traits->inheritedWindowData ? dynamic_cast(traits->inheritedWindowData.get()) : NULL; + + if (!windowData || (windowData && windowData->poseAsStandaloneApp())) + { + initAsStandaloneApplication(); + } + } + + return createGraphicsContextImplementation(traits); + } + + virtual ~CocoaWindowingSystemInterface() + { + } + +private: + NSString *getApplicationName(void) + { + NSDictionary *dict; + NSString *appName = 0; + + /* Determine the application name */ + dict = (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()); + if (dict) + appName = [dict objectForKey: @"CFBundleName"]; + + if (![appName length]) + appName = [[NSProcessInfo processInfo] processName]; + + return appName; + } + + void createApplicationMenus(void) + { + NSString *appName; + NSString *title; + NSMenu *appleMenu; + NSMenuItem *menuItem; + + /* Create the main menu bar */ + [NSApp setMainMenu:[[NSMenu alloc] init]]; + + /* Create the application menu */ + appName = getApplicationName(); + appleMenu = [[NSMenu alloc] initWithTitle:@""]; + + /* Add menu items */ + title = [@"About " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + NSMenu* service_menu = [[NSMenu alloc] init]; + NSMenuItem* service_menu_item = [[NSMenuItem alloc] initWithTitle:@"Services" action:nil keyEquivalent:@""]; + [service_menu_item setSubmenu: service_menu]; + [appleMenu addItem: service_menu_item]; + [NSApp setServicesMenu: service_menu]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Hide " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@/*"h"*/"h"]; + + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@/*"h"*/""]; + [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; + + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [appleMenu addItem:[NSMenuItem separatorItem]]; + + title = [@"Quit " stringByAppendingString:appName]; + [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@/*"q"*/"q"]; + + /* Put menu into the menubar */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:appleMenu]; + [[NSApp mainMenu] addItem:menuItem]; + [menuItem release]; + + /* Tell the application object that this is now the application menu */ + [NSApp setAppleMenu:appleMenu]; + [appleMenu release]; + + + } + +}; + +} + +#ifdef USE_DARWIN_COCOA_IMPLEMENTATION +RegisterWindowingSystemInterfaceProxy createWindowingSystemInterfaceProxy; +#endif + +// declare C entry point for static compilation. +extern "C" void graphicswindow_Cocoa(void) +{ + osg::GraphicsContext::setWindowingSystemInterface(new osgViewer::CocoaWindowingSystemInterface()); +} diff --git a/src/osgViewer/PixelBufferCocoa.mm b/src/osgViewer/PixelBufferCocoa.mm new file mode 100755 index 000000000..a16cef0a1 --- /dev/null +++ b/src/osgViewer/PixelBufferCocoa.mm @@ -0,0 +1,144 @@ +/* + * PixelBufferCocoa.cpp + * OpenSceneGraph + * + * Created by Stephan Huber on 27.06.08. + * Copyright 2008 Stephan Maximilian Huber, digital mind. All rights reserved. + * + */ + +#include +#include +#include +#include + +namespace osgViewer { + + +void PixelBufferCocoa::init() +{ + //std::cout << "PixelBufferCocoa :: init not implemented yet " << std::endl; + + _valid = _initialized = true; + + +} + +bool PixelBufferCocoa::realizeImplementation() +{ + std::cout << "PixelBufferCocoa :: realizeImplementation not implemented yet " << std::endl; + + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NSOpenGLPixelFormatAttribute attr[32]; + int i = 0; + + attr[i++] = NSOpenGLPFADepthSize; + attr[i++] = static_cast(_traits->depth); + + if (_traits->doubleBuffer) { + attr[i++] = NSOpenGLPFADoubleBuffer; + } + + if (_traits->alpha) { + attr[i++] = NSOpenGLPFAAlphaSize; + attr[i++] = static_cast(_traits->alpha); + } + + if (_traits->stencil) { + attr[i++] = NSOpenGLPFAStencilSize; + attr[i++] = static_cast(_traits->stencil); + } + + + if (_traits->sampleBuffers) { + attr[i++] = NSOpenGLPFASampleBuffers; + attr[i++] = static_cast(_traits->sampleBuffers); + attr[i++] = NSOpenGLPFASamples; + attr[i++] = static_cast(_traits->samples); + } + + attr[i++] = NSOpenGLPFAPixelBuffer; // for pbuffer usage + attr[i++] = NSOpenGLPFAAccelerated; + attr[i] = static_cast(0); + + // create the context + NSOpenGLContext* sharedContext = NULL; + + GraphicsHandleCocoa* graphicsHandleCocoa = dynamic_cast(_traits->sharedContext); + if (graphicsHandleCocoa) + { + sharedContext = graphicsHandleCocoa->getNSOpenGLContext(); + } + + + NSOpenGLPixelFormat* pixelformat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attr ]; + _context = [[NSOpenGLContext alloc] initWithFormat: pixelformat shareContext: sharedContext]; + NSOpenGLPixelBuffer* pbuffer = [[NSOpenGLPixelBuffer alloc] initWithTextureTarget: _traits->target textureInternalFormat: _traits->format textureMaxMipMapLevel: _traits->level pixelsWide: _traits->width pixelsHigh: _traits->height]; + + [_context setPixelBuffer: pbuffer cubeMapFace: _traits->face mipMapLevel:_traits->level currentVirtualScreen: nil]; + + [pool release]; + + _realized = (_context != nil); + return _realized; +} + + +void PixelBufferCocoa::closeImplementation() +{ + _realized = false; + + +} + + + +/** Make this graphics context current.*/ +bool PixelBufferCocoa::makeCurrentImplementation() +{ + // osg::notify(osg::INFO) << "PixelBufferCocoa::makeCurrentImplementation" << std::endl; + + [_context makeCurrentContext]; + return true; +} + + +/** Make this graphics context current with specified read context implementation. */ +bool PixelBufferCocoa::makeContextCurrentImplementation(osg::GraphicsContext* readContext) +{ + return makeCurrentImplementation(); +} + +/** Release the graphics context.*/ +bool PixelBufferCocoa::releaseContextImplementation() +{ + // osg::notify(osg::INFO) << "PixelBufferCocoa::releaseContextImplementation" << std::endl; + + [NSOpenGLContext clearCurrentContext]; + return true; +} + +/** Bind the graphics context to associated texture implementation.*/ +void PixelBufferCocoa::bindPBufferToTextureImplementation( GLenum buffer ) +{ + std::cout << "PixelBufferCocoa :: bindPBufferToTextureImplementation not implemented yet " << std::endl; +} + +/** Swap the front and back buffers.*/ +void PixelBufferCocoa::swapBuffersImplementation() +{ + osg::notify(osg::INFO) << "PixelBufferCocoa::swapBuffersImplementation" << std::endl; + [_context flushBuffer]; +} + + PixelBufferCocoa::~PixelBufferCocoa() + { + [_context release]; + } + + + + +} +