diff --git a/VisualStudio/examples/osglauncher/resources.rc b/VisualStudio/examples/osglauncher/resources.rc index 029ebe5a7..6961bdd80 100644 --- a/VisualStudio/examples/osglauncher/resources.rc +++ b/VisualStudio/examples/osglauncher/resources.rc @@ -1 +1,3 @@ PRODUCER_ICON ICON DISCARDABLE "../../icons/osg.ico" +OSG_ICON ICON DISCARDABLE "osg.ico" + diff --git a/VisualStudio/icons/osg_icon.rc b/VisualStudio/icons/osg_icon.rc index 7e76d5ff7..96e67cef7 100644 --- a/VisualStudio/icons/osg_icon.rc +++ b/VisualStudio/icons/osg_icon.rc @@ -1 +1,4 @@ PRODUCER_ICON ICON DISCARDABLE "osg.ico" + +OSG_ICON ICON DISCARDABLE "osg.ico" + diff --git a/applications/osgviewer/osgviewer.cpp b/applications/osgviewer/osgviewer.cpp index beb9c7932..ab2cbb0b1 100644 --- a/applications/osgviewer/osgviewer.cpp +++ b/applications/osgviewer/osgviewer.cpp @@ -12,6 +12,7 @@ #include #include #include +#include /////////////////////////////////////////////////////////////////////////// // diff --git a/include/osgViewer/GraphicsWindowWin32 b/include/osgViewer/GraphicsWindowWin32 index 4108f9487..152519cef 100644 --- a/include/osgViewer/GraphicsWindowWin32 +++ b/include/osgViewer/GraphicsWindowWin32 @@ -21,6 +21,11 @@ #include +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif +#include + namespace osgViewer { @@ -28,21 +33,9 @@ class GraphicsWindowWin32 : public osgViewer::GraphicsWindow { public: - GraphicsWindowWin32(osg::GraphicsContext::Traits* traits): - _valid(false), - _initialized(false), - _realized(false) - { - _traits = traits; - - init(); - - if (valid()) - { - setState( new osg::State ); - getState()->setContextID( osg::GraphicsContext::createNewContextID() ); - } - } + GraphicsWindowWin32(osg::GraphicsContext::Traits* traits); + + ~GraphicsWindowWin32(); virtual bool valid() const { return _valid; } @@ -75,16 +68,47 @@ class GraphicsWindowWin32 : public osgViewer::GraphicsWindow /** Get focus on if the pointer is in this window.*/ virtual void grabFocusIfPointerInWindow(); - + + /** Handle a native (Win32) windowing event as received from the system */ + virtual LRESULT handleNativeWindowingEvent( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); + protected: void init(); + void registerWindowClass(); + + HWND createWindow(); + void destroyWindow( bool deleteNativeWindow = true ); + + bool determineWindowPositionAndStyle( bool decorated, int& x, int& y, unsigned int& w, unsigned int& h, unsigned int& style, unsigned int& extendedStyle ); + + bool setPixelFormat(); + + void adaptKey( WPARAM wParam, LPARAM lParam, int& keySymbol, unsigned int& modifierMask ); + void transformMouseXY(float& x, float& y); - - bool _valid; + + HWND _hwnd; + HDC _hdc; + HGLRC _hglrc; + + double _timeOfLastCheckEvents; + + int _screenOriginX; + int _screenOriginY; + unsigned int _screenWidth; + unsigned int _screenHeight; + + int _windowOriginXToRealize; + int _windowOriginYToRealize; + unsigned int _windowWidthToRealize; + unsigned int _windowHeightToRealize; + bool _initialized; + bool _valid; bool _realized; + bool _destroying; }; } diff --git a/src/osgViewer/GraphicsWindowWin32.cpp b/src/osgViewer/GraphicsWindowWin32.cpp index 7684ed122..6773a600a 100644 --- a/src/osgViewer/GraphicsWindowWin32.cpp +++ b/src/osgViewer/GraphicsWindowWin32.cpp @@ -1,4 +1,4 @@ -/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield +/* -*-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 @@ -9,124 +9,1345 @@ * 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. -*/ + * + * This file is Copyright (C) 2007 - André Garneau (andre@pixdev.com) and licensed under OSGPL. + * + * Note, some elements of GraphicsWindowWin32 have used the Producer implementation as a reference. + * These elements are licensed under OSGPL as above, with Copyright (C) 2001-2004 Don Burns. + */ #include - +#include +#include +#include +#include using namespace osgViewer; namespace osgViewer { -/** This is the class we need to create for pbuffers, note its not a GraphicsWindow as it won't need any of the event handling and window mapping facilities.*/ +// +// Defines from the WGL_ARB_pixel_format specification document +// See http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt +// + +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_DRAW_TO_BITMAP_ARB 0x2002 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NEED_PALETTE_ARB 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006 +#define WGL_SWAP_METHOD_ARB 0x2007 +#define WGL_NUMBER_OVERLAYS_ARB 0x2008 +#define WGL_NUMBER_UNDERLAYS_ARB 0x2009 +#define WGL_TRANSPARENT_ARB 0x200A +#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037 +#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038 +#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039 +#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A +#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B +#define WGL_SHARE_DEPTH_ARB 0x200C +#define WGL_SHARE_STENCIL_ARB 0x200D +#define WGL_SHARE_ACCUM_ARB 0x200E +#define WGL_SUPPORT_GDI_ARB 0x200F +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_STEREO_ARB 0x2012 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_RED_SHIFT_ARB 0x2016 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_GREEN_SHIFT_ARB 0x2018 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_BLUE_SHIFT_ARB 0x201A +#define WGL_ALPHA_BITS_ARB 0x201B +#define WGL_ALPHA_SHIFT_ARB 0x201C +#define WGL_ACCUM_BITS_ARB 0x201D +#define WGL_ACCUM_RED_BITS_ARB 0x201E +#define WGL_ACCUM_GREEN_BITS_ARB 0x201F +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_GENERIC_ACCELERATION_ARB 0x2026 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_SWAP_EXCHANGE_ARB 0x2028 +#define WGL_SWAP_COPY_ARB 0x2029 +#define WGL_SWAP_UNDEFINED_ARB 0x202A +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_TYPE_COLORINDEX_ARB 0x202C +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 + +// +// Entry points used from the WGL extensions +// +// BOOL wglChoosePixelFormatARB(HDC hdc, +// const int *piAttribIList, +// const FLOAT *pfAttribFList, +// UINT nMaxFormats, +// int *piFormats, +// UINT *nNumFormats); +// + +typedef bool (WINAPI * WGLChoosePixelFormatARB) ( HDC, const int *, const float *, unsigned int, int *, unsigned int * ); + +// +// Utility class to specify the visual attributes for wglChoosePixelFormatARB() function +// + +template class WGLAttributes +{ + public: + + WGLAttributes() {} + ~WGLAttributes() {} + + void begin() { m_parameters.clear(); } + void set( const T& id, const T& value ) { add(id); add(value); } + void enable( const T& id ) { add(id); add(true); } + void disable( const T& id ) { add(id); add(false); } + void end() { add(0); } + + const T* get() { return &m_parameters.front(); } + + protected: + + void add( const T& t ) { m_parameters.push_back(t); } + + std::vector m_parameters; // parameters added + + private: + + // No implementation for these + WGLAttributes( const WGLAttributes& ); + WGLAttributes& operator=( const WGLAttributes& ); +}; + +typedef WGLAttributes WGLIntegerAttributes; +typedef WGLAttributes WGLFloatAttributes; + +// +// Class responsible for interfacing with the Win32 Window Manager +// The behavior of this class is specific to OSG needs and is not a +// generic Windowing interface. +// +// NOTE: This class is intended to be used by a single-thread. +// Multi-threading is not enabled for performance reasons. +// The creation/deletion of graphics windows should be done +// by a single controller thread. That thread should then +// call the checkEvents() method of all created windows periodically. +// This is the case with OSG as a "main" thread does all +// setup, update & event processing. Rendering is done (optionally) by other threads. +// +// !@todo Have a dedicated thread managed by the Win32WindowingSystem class handle the +// creation and event message processing for all windows it manages. This +// is to relieve the "main" thread from having to do this synchronously +// during frame generation. The "main" thread would only have to process +// each osgGA-type window event queue. +// + +class Win32WindowingSystem : public osg::GraphicsContext::WindowingSystemInterface +{ + public: + + static std::string osgGraphicsWindowClassName; //!< Name of Win32 window class used by OSG graphics window instances + + Win32WindowingSystem(); + ~Win32WindowingSystem(); + + // Access the Win32 windowing system through this singleton class. + static Win32WindowingSystem* getInterface() + { + static Win32WindowingSystem* win32Interface = new Win32WindowingSystem; + return win32Interface; + } + + // Return the number of screens present in the system + virtual unsigned int getNumScreens( const osg::GraphicsContext::ScreenIdentifier& si ); + + // Return the resolution of specified screen + // (0,0) is returned if screen is unknown + virtual void getScreenResolution( const osg::GraphicsContext::ScreenIdentifier& si, unsigned int& width, unsigned int& height ); + + // Return the screen position and width/height. + // all zeros returned if screen is unknown + virtual void getScreenPosition( const osg::GraphicsContext::ScreenIdentifier& si, int& originX, int& originY, unsigned int& width, unsigned int& height ); + + // Create a graphics context with given traits + virtual osg::GraphicsContext* createGraphicsContext( osg::GraphicsContext::Traits* traits ); + + // Register a newly created native window along with its application counterpart + // This is required to maintain a link between Windows messages and the application window object + // at event processing time + virtual void registerWindow( HWND hwnd, osgViewer::GraphicsWindowWin32* window ); + + // Unregister a window + // This is called as part of a window being torn down + virtual void unregisterWindow( HWND hwnd ); + + // Get the application window object associated with a native window + virtual osgViewer::GraphicsWindowWin32* getGraphicsWindowFor( HWND hwnd ); + + // Return a valid sample OpenGL Device Context and Rendering Context that can be used with wglXYZ extensions + virtual void getSampleOpenGLContext( HDC& deviceContext, HGLRC& renderingContext ); + + protected: + + // Display devices present in the system + typedef std::vector DisplayDevices; + + // Map Win32 window handles to GraphicsWindowWin32 instance + typedef std::pair< HWND, osgViewer::GraphicsWindowWin32* > WindowHandleEntry; + typedef std::map< HWND, osgViewer::GraphicsWindowWin32* > WindowHandles; + + // Enumerate all display devices and return in passed container + void enumerateDisplayDevices( DisplayDevices& displayDevices ) const; + + bool getScreenInformation( const osg::GraphicsContext::ScreenIdentifier& si, DEVMODE& deviceMode ); + + // Register the window class used by OSG graphics window instances + void registerWindowClass(); + + // Unregister the window class used by OSG graphics window instances + void unregisterWindowClass(); + + // Release sample GL context + void releaseSampleOpenGLContext(); + + // Data members + WindowHandles _activeWindows; //!< handles to active windows + HWND _dummyGLWindow; //!< dummy GL window used to provide a valid context for wglXYZ calls + HDC _dummyGLWindowDC; //!< dummy GL window device context + HGLRC _dummyGLRC; //!< dummy GL rendering context + bool _windowClassRegistered; //!< true after windowing interface has been initialized successfully + + private: + + // No implementation for these + Win32WindowingSystem( const Win32WindowingSystem& ); + Win32WindowingSystem& operator=( const Win32WindowingSystem& ); +}; + +// +// This is the class we need to create for pbuffers and display devices that are not attached to the desktop +// (and thus cannot have windows created on their surface). +// +// Note its not a GraphicsWindow as it won't need any of the event handling and window mapping facilities. +// + class GraphicsContextWin32 : public osg::GraphicsContext { public: - GraphicsContextWin32(osg::GraphicsContext::Traits* traits): - _valid(false) - { - _traits = traits; - } + GraphicsContextWin32(osg::GraphicsContext::Traits* traits); + ~GraphicsContextWin32(); - virtual bool valid() const { return _valid; } + virtual bool valid() const; /** Realise the GraphicsContext implementation, * Pure virtual - must be implemented by concrate implementations of GraphicsContext. */ - virtual bool realizeImplementation() { osg::notify(osg::NOTICE)<<"GraphicsWindow::realizeImplementation() not implemented."<second; + } + + protected: + + typedef std::map KeyMap; + KeyMap _keymap; +}; + +static Win32KeyboardMap s_win32KeyboardMap; +static int remapWin32Key(int key) +{ + return s_win32KeyboardMap.remapKey(key); +} + +////////////////////////////////////////////////////////////////////////////// +// Window procedure for all GraphicsWindowWin32 instances +// Dispatches the call to the actual instance +////////////////////////////////////////////////////////////////////////////// + +static LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + osgViewer::GraphicsWindowWin32* window = Win32WindowingSystem::getInterface()->getGraphicsWindowFor(hwnd); + return window ? window->handleNativeWindowingEvent(hwnd, uMsg, wParam, lParam) : + ::DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +////////////////////////////////////////////////////////////////////////////// +// Win32WindowingSystem implementation +////////////////////////////////////////////////////////////////////////////// + +std::string Win32WindowingSystem::osgGraphicsWindowClassName; + +Win32WindowingSystem::Win32WindowingSystem() +: _dummyGLWindow(0), + _dummyGLWindowDC(0), + _dummyGLRC(0), + _windowClassRegistered(false) +{ +} + +Win32WindowingSystem::~Win32WindowingSystem() +{ + releaseSampleOpenGLContext(); + unregisterWindowClass(); +} + +void Win32WindowingSystem::enumerateDisplayDevices( DisplayDevices& displayDevices ) const +{ + for (unsigned int deviceNum=0;; ++deviceNum) + { + DISPLAY_DEVICE displayDevice; + displayDevice.cb = sizeof(displayDevice); + + if (!::EnumDisplayDevices(NULL, deviceNum, &displayDevice, 0)) break; + + // Do not track devices used for remote access (Terminal Services pseudo-displays, etc.) + if (displayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) continue; + + //!@todo when the GraphicsContextWin32 class is implemented, remove this line + // For the time-being only return display devices that are attached to the desktop + if (!(displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)) continue; + + displayDevices.push_back(displayDevice); + } +} + +void Win32WindowingSystem::registerWindowClass() +{ + if (_windowClassRegistered) return; + + // + // Register the window class used by OSG GraphicsWindowWin32 instances + // + + std::ostringstream str; + str << "OpenSceneGraph Graphics Window for Win32 [" << ::GetCurrentProcessId() << "]"; + + osgGraphicsWindowClassName = str.str(); + + WNDCLASSEX wc; + + HINSTANCE hinst = ::GetModuleHandle(NULL); + + wc.cbSize = sizeof(wc); + wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.lpfnWndProc = WindowProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hinst; + wc.hIcon = ::LoadIcon(hinst, "OSG_ICON"); + wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = 0; + wc.lpszClassName = osgGraphicsWindowClassName.c_str(); + wc.hIconSm = NULL; + + if (::RegisterClassEx(&wc)==0) + { + unsigned int lastError = ::GetLastError(); + if (lastError!=ERROR_CLASS_ALREADY_EXISTS) + { + reportError("Win32WindowingSystem::registerWindowClass() - Unable to register window class", lastError); + return; + } + } + + _windowClassRegistered = true; +} + +void Win32WindowingSystem::unregisterWindowClass() +{ + if (_windowClassRegistered) + { + ::UnregisterClass(osgGraphicsWindowClassName.c_str(), ::GetModuleHandle(NULL)); + _windowClassRegistered = false; + } +} + +void Win32WindowingSystem::getSampleOpenGLContext( HDC& deviceContext, HGLRC& renderingContext ) +{ + deviceContext = _dummyGLWindowDC; + renderingContext = _dummyGLRC; + + if (_dummyGLWindowDC) return; + + registerWindowClass(); + + HWND hwnd = ::CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, + osgGraphicsWindowClassName.c_str(), + NULL, + WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_DISABLED, + 0, + 0, + 256, + 256, + NULL, + NULL, + ::GetModuleHandle(NULL), + NULL); + if (hwnd==0) + { + reportError("Win32WindowingSystem::getSampleOpenGLContext() - Unable to create window", ::GetLastError()); + return; + } + + // + // Set the pixel format of the window + // + + PIXELFORMATDESCRIPTOR pixelFormat = + { + sizeof(PIXELFORMATDESCRIPTOR), + 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL, + PFD_TYPE_RGBA, + 24, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 24, + 0, + 0, + PFD_MAIN_PLANE, + 0, + 0, 0, 0 + }; + + HDC hdc = ::GetDC(hwnd); + if (hdc==0) + { + reportError("Win32WindowingSystem::getSampleOpenGLContext() - Unable to get window device context", ::GetLastError()); + ::DestroyWindow(hwnd); + return; + } + + int pixelFormatIndex = ::ChoosePixelFormat(hdc, &pixelFormat); + if (pixelFormatIndex==0) + { + reportError("Win32WindowingSystem::getSampleOpenGLContext() - Unable to choose pixel format", ::GetLastError()); + ::ReleaseDC(hwnd, hdc); + ::DestroyWindow(hwnd); + return; + } + + if (!::SetPixelFormat(hdc, pixelFormatIndex, &pixelFormat)) + { + reportError("Win32WindowingSystem::getSampleOpenGLContext() - Unable to set pixel format", ::GetLastError()); + ::ReleaseDC(hwnd, hdc); + ::DestroyWindow(hwnd); + return; + } + + HGLRC hglrc = ::wglCreateContext(hdc); + if (hglrc==0) + { + reportError("Win32WindowingSystem::getSampleOpenGLContext() - Unable to create an OpenGL rendering context", ::GetLastError()); + ::ReleaseDC(hwnd, hdc); + ::DestroyWindow(hwnd); + return; + } + + if (!::wglMakeCurrent(hdc, hglrc)) + { + reportError("Win32WindowingSystem::getSampleOpenGLContext() - Unable to set the OpenGL rendering context", ::GetLastError()); + ::wglDeleteContext(hglrc); + ::ReleaseDC(hwnd, hdc); + ::DestroyWindow(hwnd); + return; + } + + _dummyGLWindow = hwnd; + _dummyGLWindowDC = hdc; + _dummyGLRC = hglrc; + + deviceContext = _dummyGLWindowDC; + renderingContext = _dummyGLRC; +} + +void Win32WindowingSystem::releaseSampleOpenGLContext() +{ + if (_dummyGLRC) + { + ::wglMakeCurrent(_dummyGLWindowDC, NULL); + ::wglDeleteContext(_dummyGLRC); + _dummyGLRC = 0; + } + + if (_dummyGLWindowDC) + { + ::ReleaseDC(_dummyGLWindow, _dummyGLWindowDC); + _dummyGLWindowDC = 0; + } + + if (_dummyGLWindow) + { + ::DestroyWindow(_dummyGLWindow); + _dummyGLWindow = 0; + } +} + +unsigned int Win32WindowingSystem::getNumScreens( const osg::GraphicsContext::ScreenIdentifier& si ) +{ + //!@todo change when implementing the GraphicsContextWin32 class. + return si.displayNum==0 ? ::GetSystemMetrics(SM_CMONITORS) : 0; +} + +bool Win32WindowingSystem::getScreenInformation( const osg::GraphicsContext::ScreenIdentifier& si, DEVMODE& deviceMode ) +{ + if (si.displayNum>0) + { + osg::notify(osg::WARN) << "Win32WindowingSystem::getScreenInformation() - The screen identifier on the Win32 platform must always use display number 0. Value received was " << si.displayNum << std::endl; + return false; + } + + DisplayDevices displayDevices; + enumerateDisplayDevices(displayDevices); + + if (si.screenNum>=displayDevices.size()) + { + osg::notify(osg::WARN) << "Win32WindowingSystem::getScreenInformation() - Cannot get information for screen " << si.screenNum << " because it does not exist." << std::endl; + return false; + } + + deviceMode.dmSize = sizeof(deviceMode); + deviceMode.dmDriverExtra = 0; + + if (!::EnumDisplaySettings(displayDevices[si.screenNum].DeviceName, ENUM_CURRENT_SETTINGS, &deviceMode)) + { + std::ostringstream str; + str << "Win32WindowingSystem::getScreenInformation() - Unable to query information for screen number " << si.screenNum; + reportError(str.str(), ::GetLastError()); + return false; + } + + return true; +} + +void Win32WindowingSystem::getScreenResolution( const osg::GraphicsContext::ScreenIdentifier& si, unsigned int& width, unsigned int& height ) +{ + DEVMODE deviceMode; + + if (getScreenInformation(si, deviceMode)) + { + width = deviceMode.dmPelsWidth; + height = deviceMode.dmPelsHeight; + } + else + { + width = 0; + height = 0; + } +} + +void Win32WindowingSystem::getScreenPosition( const osg::GraphicsContext::ScreenIdentifier& si, int& originX, int& originY, unsigned int& width, unsigned int& height ) +{ + DEVMODE deviceMode; + + if (getScreenInformation(si, deviceMode)) + { + originX = deviceMode.dmPosition.x; + originY = deviceMode.dmPosition.y; + width = deviceMode.dmPelsWidth; + height = deviceMode.dmPelsHeight; + } + else + { + originX = 0; + originY = 0; + width = 0; + height = 0; + } +} + +osg::GraphicsContext* Win32WindowingSystem::createGraphicsContext( osg::GraphicsContext::Traits* traits ) +{ + if (traits->pbuffer) + { + osg::ref_ptr pbuffer = new GraphicsContextWin32(traits); + if (pbuffer->valid()) return pbuffer.release(); + else return 0; + } + else + { + registerWindowClass(); + + osg::ref_ptr window = new GraphicsWindowWin32(traits); + if (window->valid()) return window.release(); + else return 0; + } +} + +void Win32WindowingSystem::registerWindow( HWND hwnd, osgViewer::GraphicsWindowWin32* window ) +{ + if (hwnd) _activeWindows.insert(WindowHandleEntry(hwnd, window)); +} + +// +// Unregister a window +// This is called as part of a window being torn down +// + +void Win32WindowingSystem::unregisterWindow( HWND hwnd ) +{ + if (hwnd) _activeWindows.erase(hwnd); +} + +// +// Get the application window object associated with a native window +// + +osgViewer::GraphicsWindowWin32* Win32WindowingSystem::getGraphicsWindowFor( HWND hwnd ) +{ + WindowHandles::const_iterator entry = _activeWindows.find(hwnd); + return entry==_activeWindows.end() ? 0 : entry->second; +} + +////////////////////////////////////////////////////////////////////////////// +// GraphicsWindowWin32 implementation +////////////////////////////////////////////////////////////////////////////// + +GraphicsWindowWin32::GraphicsWindowWin32( osg::GraphicsContext::Traits* traits ) +: _hwnd(0), + _hdc(0), + _hglrc(0), + _timeOfLastCheckEvents(-1.0), + _screenOriginX(0), + _screenOriginY(0), + _screenWidth(0), + _screenHeight(0), + _windowOriginXToRealize(0), + _windowOriginYToRealize(0), + _windowWidthToRealize(0), + _windowHeightToRealize(0), + _initialized(false), + _valid(false), + _realized(false), + _destroying(false) +{ + _traits = traits; + + init(); + + if (valid()) + { + setState( new osg::State ); + getState()->setContextID( osg::GraphicsContext::createNewContextID() ); + Win32WindowingSystem::getInterface()->registerWindow(_hwnd, this); + } +} + +GraphicsWindowWin32::~GraphicsWindowWin32() +{ + close(); + destroyWindow(); } void GraphicsWindowWin32::init() { - osg::notify(osg::NOTICE)<<"GraphicWindowWin32::init() Please implement me!"<windowDecoration, + _windowOriginXToRealize, + _windowOriginYToRealize, + _windowWidthToRealize, + _windowHeightToRealize, + windowStyle, + extendedStyle)) + { + reportError("GraphicsWindowWin32::createWindow() - Unable to determine the window position and style"); + return 0; + } + + _hwnd = ::CreateWindowEx(extendedStyle, + Win32WindowingSystem::osgGraphicsWindowClassName.c_str(), + _traits->windowName.c_str(), + windowStyle, + _windowOriginXToRealize, + _windowOriginYToRealize, + _windowWidthToRealize, + _windowHeightToRealize, + NULL, + NULL, + ::GetModuleHandle(NULL), + NULL); + if (_hwnd==0) + { + reportError("GraphicsWindowWin32::createWindow() - Unable to create window", ::GetLastError()); + return 0; + } + + _hdc = ::GetDC(_hwnd); + if (_hdc==0) + { + reportError("GraphicsWindowWin32::createWindow() - Unable to get window device context", ::GetLastError()); + destroyWindow(); + _hwnd = 0; + return 0; + } + + // + // Set the pixel format according to traits specified + // + + if (!setPixelFormat()) + { + ::ReleaseDC(_hwnd, _hdc); + _hdc = 0; + destroyWindow(); + return 0; + } + + return _hwnd; +} + +void GraphicsWindowWin32::destroyWindow( bool deleteNativeWindow ) +{ + if (_destroying) return; + _destroying = true; + + close(); + + if (_hdc) + { + releaseContext(); + + if (_hglrc) + { + ::wglDeleteContext(_hglrc); + _hglrc = 0; + } + + ::ReleaseDC(_hwnd, _hdc); + _hdc = 0; + } + + if (_hwnd) + { + Win32WindowingSystem::getInterface()->unregisterWindow(_hwnd); + if (deleteNativeWindow) ::DestroyWindow(_hwnd); + _hwnd = 0; + } + + _initialized = false; + _realized = false; + _valid = false; +} + +bool GraphicsWindowWin32::determineWindowPositionAndStyle( bool decorated, int& x, int& y, unsigned int& w, unsigned int& h, unsigned int& style, unsigned int& extendedStyle ) +{ + if (_traits==0) return false; + + // + // Query the screen position and size + // + + osg::GraphicsContext::ScreenIdentifier screenId(_traits->screenNum); + Win32WindowingSystem* windowManager = Win32WindowingSystem::getInterface(); + + windowManager->getScreenPosition(screenId, _screenOriginX, _screenOriginY, _screenWidth, _screenHeight); + if (_screenWidth==0 || _screenHeight==0) return 0; + + x = _traits->x + _screenOriginX; + y = _traits->y + _screenOriginY; + w = _traits->width; + h = _traits->height; + + style = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + + extendedStyle = 0; + + if (decorated) + { + style |= WS_CAPTION | + WS_SYSMENU | + WS_MINIMIZEBOX | + WS_MAXIMIZEBOX; + + if (_traits->supportsResize) style |= WS_SIZEBOX; + + extendedStyle = WS_EX_APPWINDOW | + WS_EX_OVERLAPPEDWINDOW | + WS_EX_ACCEPTFILES | + WS_EX_LTRREADING; + + RECT corners; + + corners.left = x; + corners.top = y; + corners.right = x + w - 1; + corners.bottom = y + h - 1; + + // + // Determine the location of the window corners in order to have + // a client area of the requested size + // + + if (!::AdjustWindowRectEx(&corners, style, FALSE, extendedStyle)) + { + reportError("GraphicsWindowWin32::determineWindowPositionAndStyle() - Unable to adjust window rectangle", ::GetLastError()); + return false; + } + + x = corners.left; + y = corners.top; + w = corners.right - corners.left + 1; + h = corners.bottom - corners.top + 1; + } + + return true; +} + +bool GraphicsWindowWin32::setPixelFormat() +{ + HDC hdc; + HGLRC hglrc; + + Win32WindowingSystem::getInterface()->getSampleOpenGLContext(hdc, hglrc); + + // + // Access the entry point for the wglChoosePixelFormatARB function + // + + WGLChoosePixelFormatARB wglChoosePixelFormatARB = (WGLChoosePixelFormatARB)wglGetProcAddress("wglChoosePixelFormatARB"); + if (wglChoosePixelFormatARB==0) + { + reportError("GraphicsWindowWin32::setPixelFormat() - wglChoosePixelFormatARB extension not found", ::GetLastError()); + return false; + } + + // + // Build the specifications of the requested pixel format + // + + WGLIntegerAttributes attributes; + + attributes.begin(); + + attributes.enable(WGL_DRAW_TO_WINDOW_ARB); + attributes.enable(WGL_SUPPORT_OPENGL_ARB); + + attributes.set(WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB); + attributes.set(WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB); + + attributes.set(WGL_COLOR_BITS_ARB, _traits->red + _traits->green + _traits->blue); + attributes.set(WGL_RED_BITS_ARB, _traits->red); + attributes.set(WGL_GREEN_BITS_ARB, _traits->green); + attributes.set(WGL_BLUE_BITS_ARB, _traits->blue); + attributes.set(WGL_DEPTH_BITS_ARB, _traits->depth); + + if (_traits->doubleBuffer) + { + attributes.enable(WGL_DOUBLE_BUFFER_ARB); + attributes.set(WGL_SWAP_METHOD_ARB, WGL_SWAP_EXCHANGE_ARB); + } + + if (_traits->alpha) attributes.set(WGL_ALPHA_BITS_ARB, _traits->alpha); + if (_traits->stencil) attributes.set(WGL_STENCIL_BITS_ARB, _traits->stencil); + if (_traits->sampleBuffers) attributes.set(WGL_SAMPLE_BUFFERS_ARB, _traits->sampleBuffers); + if (_traits->samples) attributes.set(WGL_SAMPLES_ARB, _traits->samples); + + if (_traits->quadBufferStereo) attributes.enable(WGL_STEREO_ARB); + + attributes.end(); + + // + // Choose the closest pixel format from the specified traits + // + + int pixelFormatIndex = 0; + unsigned int numMatchingPixelFormats = 0; + + if (!wglChoosePixelFormatARB(hdc, + attributes.get(), + NULL, + 1, + &pixelFormatIndex, + &numMatchingPixelFormats)) + { + reportError("GraphicsWindowWin32::setPixelFormat() - Unable to choose the requested pixel format", ::GetLastError()); + return false; + } + + if (numMatchingPixelFormats==0) + { + reportError("GraphicsWindowWin32::setPixelFormat() - No matching pixel format found based on traits specified", ::GetLastError()); + return false; + } + + // + // Set the pixel format found + // + + PIXELFORMATDESCRIPTOR pfd; + ::memset(&pfd, 0, sizeof(pfd)); + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + + if (!::SetPixelFormat(_hdc, pixelFormatIndex, &pfd)) + { + reportError("GraphicsWindowWin32::setPixelFormat() - Unable to set pixel format", ::GetLastError()); + return false; + } + + // + // Create the OpenGL rendering context associated with this window + // + + _hglrc = ::wglCreateContext(_hdc); + if (_hglrc==0) + { + reportError("GraphicsWindowWin32::setPixelFormat() - Unable to create OpenGL rendering context", ::GetLastError()); + return false; + } + + return true; +} + +void GraphicsWindowWin32::setWindowDecoration( bool decorated ) +{ + unsigned int windowStyle; + unsigned int extendedStyle; + + // + // Determine position and size of window with/without decorations to retain the size specified in traits + // + + int x, y; + unsigned int w, h; + + if (!determineWindowPositionAndStyle(decorated, x, y, w, h, windowStyle, extendedStyle)) + { + reportError("GraphicsWindowWin32::setWindowDecoration() - Unable to determine the window position and style"); + return; + } + + // + // Change the window style + // + + ::SetLastError(0); + unsigned int result = ::SetWindowLong(_hwnd, GWL_STYLE, windowStyle); + unsigned int error = ::GetLastError(); + if (result==0 && error) + { + reportError("GraphicsWindowWin32::setWindowDecoration() - Unable to set window style", error); + return; + } + + // + // Change the window extended style + // + + ::SetLastError(0); + result = ::SetWindowLong(_hwnd, GWL_EXSTYLE, extendedStyle); + error = ::GetLastError(); + if (result==0 && error) + { + reportError("GraphicsWindowWin32::setWindowDecoration() - Unable to set window extented style", error); + return; + } + + // + // Change the window position and size and realize the style changes + // + + if (!::SetWindowPos(_hwnd, HWND_TOP, x, y, w, h, SWP_FRAMECHANGED | SWP_NOZORDER | SWP_SHOWWINDOW)) + { + reportError("GraphicsWindowWin32::setWindowDecoration() - Unable to set new window position and size", ::GetLastError()); + return; + } + + // + // Repaint the desktop to cleanup decorations removed + // + + if (!decorated) + { + ::InvalidateRect(NULL, NULL, TRUE); + } } bool GraphicsWindowWin32::realizeImplementation() { - osg::notify(osg::NOTICE)<<"GraphicWindowWin32::realizeImplementation() Please implement me!"<=windowRect.left && mousePos.x<=windowRect.right && + mousePos.y>=windowRect.top && mousePos.y<=windowRect.bottom) + { + grabFocus(); + } } +void GraphicsWindowWin32::adaptKey( WPARAM wParam, LPARAM lParam, int& keySymbol, unsigned int& modifierMask ) +{ + modifierMask = 0; -void GraphicsWindowWin32::transformMouseXY(float& x, float& y) + BYTE keyState[256]; + if (!::GetKeyboardState(keyState)) + { + keySymbol = 0; + return; + } + + bool rightSide = (lParam & 0x01000000)!=0; + + if (keyState[VK_SHIFT] & 0x80) + { + modifierMask |= osgGA::GUIEventAdapter::MODKEY_SHIFT; + modifierMask |= rightSide ? osgGA::GUIEventAdapter::MODKEY_RIGHT_SHIFT : osgGA::GUIEventAdapter::MODKEY_LEFT_SHIFT; + } + + if (keyState[VK_CAPITAL] & 0x01) modifierMask |= osgGA::GUIEventAdapter::MODKEY_CAPS_LOCK; + if (keyState[VK_NUMLOCK] & 0x01) modifierMask |= osgGA::GUIEventAdapter::MODKEY_NUM_LOCK; + + if (keyState[VK_CONTROL] & 0x80) + { + modifierMask |= osgGA::GUIEventAdapter::MODKEY_CTRL; + modifierMask |= rightSide ? osgGA::GUIEventAdapter::MODKEY_RIGHT_CTRL : osgGA::GUIEventAdapter::MODKEY_LEFT_CTRL; + } + + if (lParam & 0x20000000) + { + modifierMask |= osgGA::GUIEventAdapter::MODKEY_LEFT_ALT; + modifierMask |= rightSide ? osgGA::GUIEventAdapter::MODKEY_RIGHT_ALT : osgGA::GUIEventAdapter::MODKEY_LEFT_ALT; + } + + keySymbol = remapWin32Key(wParam); + + if (keySymbol==osgGA::GUIEventAdapter::KEY_Return && rightSide) + { + keySymbol = osgGA::GUIEventAdapter::KEY_KP_Enter; + } + else if ((keySymbol & 0xff00)==0) + { + char asciiKey[2]; + int numChars = ::ToAscii(wParam, (lParam>>16)&0xff, keyState, reinterpret_cast(asciiKey), 0); + if (numChars>0) keySymbol = asciiKey[0]; + } +} + +void GraphicsWindowWin32::transformMouseXY( float& x, float& y ) { if (getEventQueue()->getUseFixedMouseInputRange()) { @@ -136,48 +1357,260 @@ void GraphicsWindowWin32::transformMouseXY(float& x, float& y) } } -struct Win32WindowingSystemInterface : public osg::GraphicsContext::WindowingSystemInterface +LRESULT GraphicsWindowWin32::handleNativeWindowingEvent( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { + //!@todo adapt windows event time to osgGA event queue time for better resolution - Win32WindowingSystemInterface() + double baseTime = _timeOfLastCheckEvents; + double eventTime = _timeOfLastCheckEvents; + double resizeTime = eventTime; + + _timeOfLastCheckEvents = getEventQueue()->getTime(); + + switch(uMsg) { + ///////////////// + case WM_PAINT : + ///////////////// + + { + PAINTSTRUCT paint; + ::BeginPaint(hwnd, &paint); + ::EndPaint(hwnd, &paint); + } + break; + + /////////////////// + case WM_MOUSEMOVE : + /////////////////// + + { + float mx = GET_X_LPARAM(lParam); + float my = GET_Y_LPARAM(lParam); + transformMouseXY(mx, my); + getEventQueue()->mouseMotion(mx, my, eventTime); + } + break; + + ///////////////////// + case WM_LBUTTONDOWN : + case WM_MBUTTONDOWN : + case WM_RBUTTONDOWN : + ///////////////////// + + { + int button; + + if (uMsg==WM_LBUTTONDOWN) button = 1; + else if (uMsg==WM_MBUTTONDOWN) button = 2; + else button = 3; + + float mx = GET_X_LPARAM(lParam); + float my = GET_Y_LPARAM(lParam); + transformMouseXY(mx, my); + getEventQueue()->mouseButtonPress(mx, my, button, eventTime); + } + break; + + ///////////////////// + case WM_LBUTTONUP : + case WM_MBUTTONUP : + case WM_RBUTTONUP : + ///////////////////// + + { + int button; + + if (uMsg==WM_LBUTTONUP) button = 1; + else if (uMsg==WM_MBUTTONUP) button = 2; + else button = 3; + + float mx = GET_X_LPARAM(lParam); + float my = GET_Y_LPARAM(lParam); + transformMouseXY(mx, my); + getEventQueue()->mouseButtonRelease(mx, my, button, eventTime); + } + break; + + //////////////////// + case WM_MOUSEWHEEL : + //////////////////// + + getEventQueue()->mouseScroll(GET_WHEEL_DELTA_WPARAM(wParam)<0 ? osgGA::GUIEventAdapter::SCROLL_DOWN : + osgGA::GUIEventAdapter::SCROLL_UP, + eventTime); + break; + + ///////////////// + case WM_MOVE : + case WM_SIZE : + ///////////////// + + { + POINT origin; + origin.x = 0; + origin.y = 0; + + ::ClientToScreen(hwnd, &origin); + + int windowX = origin.x - _screenOriginX; + int windowY = origin.y - _screenOriginY; + resizeTime = eventTime; + + RECT clientRect; + ::GetClientRect(hwnd, &clientRect); + + int windowWidth; + int windowHeight; + + if (clientRect.bottom==0 && clientRect.right==0) + { + // + // Window has been minimized; keep window width & height to a minimum of 1 pixel + // + + windowWidth = 1; + windowHeight = 1; + } + else + { + windowWidth = clientRect.right; + windowHeight = clientRect.bottom; + } + + if (windowX!=_traits->x || windowY!=_traits->y || windowWidth!=_traits->width || windowHeight!=_traits->height) + { + resized(windowX, windowY, windowWidth, windowHeight); + getEventQueue()->windowResize(windowX, windowY, windowWidth, windowHeight, resizeTime); + } + } + break; + + //////////////////// + case WM_KEYDOWN : + case WM_SYSKEYDOWN : + //////////////////// + + { + int keySymbol = 0; + unsigned int modifierMask = 0; + adaptKey(wParam, lParam, keySymbol, modifierMask); + getEventQueue()->keyPress(keySymbol, eventTime); + getEventQueue()->getCurrentEventState()->setModKeyMask(modifierMask); + } + break; + + ////////////////// + case WM_KEYUP : + case WM_SYSKEYUP : + ////////////////// + + { + int keySymbol = 0; + unsigned int modifierMask = 0; + adaptKey(wParam, lParam, keySymbol, modifierMask); + getEventQueue()->keyRelease(keySymbol, eventTime); + getEventQueue()->getCurrentEventState()->setModKeyMask(modifierMask); + } + break; + + ///////////////// + case WM_CLOSE : + ///////////////// + + ::DestroyWindow(hwnd); + break; + + ///////////////// + case WM_DESTROY : + ///////////////// + + destroyWindow(false); + getEventQueue()->closeWindow(eventTime); + ::PostQuitMessage(0); + break; + + ///////////////// + default : + ///////////////// + + return ::DefWindowProc(hwnd, uMsg, wParam, lParam); + } + + return 0; +} + +////////////////////////////////////////////////////////////////////////////// +// GraphicsContextWin32 implementation +////////////////////////////////////////////////////////////////////////////// + +GraphicsContextWin32::GraphicsContextWin32( osg::GraphicsContext::Traits* traits ) +: _valid(false) +{ + _traits = traits; +} + +GraphicsContextWin32::~GraphicsContextWin32() +{ +} + +bool GraphicsContextWin32::valid() const +{ + return _valid; +} + +bool GraphicsContextWin32::realizeImplementation() +{ + osg::notify(osg::NOTICE) << "GraphicsContextWin32::realizeImplementation() not implemented." << std::endl; return false; +} + +bool GraphicsContextWin32::isRealizedImplementation() const +{ + osg::notify(osg::NOTICE) << "GraphicsContextWin32::isRealizedImplementation() not implemented." << std::endl; return false; +} + +void GraphicsContextWin32::closeImplementation() +{ + osg::notify(osg::NOTICE) << "GraphicsContextWin32::closeImplementation() not implemented." << std::endl; +} + +bool GraphicsContextWin32::makeCurrentImplementation() +{ + osg::notify(osg::NOTICE) << "GraphicsContextWin32::makeCurrentImplementation() not implemented." << std::endl; + return false; +} - } +bool GraphicsContextWin32::makeContextCurrentImplementation( GraphicsContext* /*readContext*/ ) +{ + osg::notify(osg::NOTICE) << "GraphicsContextWin32::makeContextCurrentImplementation(..) not implemented." << std::endl; + return false; +} - virtual unsigned int getNumScreens(const osg::GraphicsContext::ScreenIdentifier& si) - { - osg::notify(osg::NOTICE)<<"Win32WindowingSystemInterface::getNumScreens() Please implement me!"<pbuffer) - { - osg::ref_ptr pbuffer = new GraphicsContextWin32(traits); - if (pbuffer->valid()) return pbuffer.release(); - else return 0; - } - else - { - osg::ref_ptr window = new GraphicsWindowWin32(traits); - if (window->valid()) return window.release(); - else return 0; - } - } +void GraphicsContextWin32::swapBuffersImplementation() +{ + osg::notify(osg::NOTICE) << "GraphicsContextWin32:: swapBuffersImplementation() not implemented." << std::endl; +} -}; +////////////////////////////////////////////////////////////////////////////// +// Class responsible for registering the Win32 Windowing System interface +////////////////////////////////////////////////////////////////////////////// struct RegisterWindowingSystemInterfaceProxy { RegisterWindowingSystemInterfaceProxy() { - osg::GraphicsContext::setWindowingSystemInterface(new Win32WindowingSystemInterface); + osg::GraphicsContext::setWindowingSystemInterface(Win32WindowingSystem::getInterface()); } ~RegisterWindowingSystemInterfaceProxy() @@ -186,4 +1619,6 @@ struct RegisterWindowingSystemInterfaceProxy } }; -RegisterWindowingSystemInterfaceProxy createWindowingSystemInterfaceProxy; +static RegisterWindowingSystemInterfaceProxy createWindowingSystemInterfaceProxy; + +}; // namespace OsgViewer