2219 lines
74 KiB
C++
2219 lines
74 KiB
C++
/* -*-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.
|
|
*
|
|
* This file is Copyright (C) 2007 - André Garneau (andre@pixdev.com) and licensed under OSGPL.
|
|
*
|
|
* 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 <osgViewer/api/Win32/GraphicsWindowWin32>
|
|
#include <osgViewer/api/Win32/PixelBufferWin32>
|
|
|
|
#include <osg/DeleteHandler>
|
|
|
|
#include <vector>
|
|
#include <map>
|
|
#include <sstream>
|
|
#include <windowsx.h>
|
|
|
|
using namespace osgViewer;
|
|
|
|
namespace osgViewer
|
|
{
|
|
|
|
//
|
|
// 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 <typename T> 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() const { return &m_parameters.front(); }
|
|
|
|
protected:
|
|
|
|
void add( const T& t ) { m_parameters.push_back(t); }
|
|
|
|
std::vector<T> m_parameters; // parameters added
|
|
|
|
private:
|
|
|
|
// No implementation for these
|
|
WGLAttributes( const WGLAttributes& );
|
|
WGLAttributes& operator=( const WGLAttributes& );
|
|
};
|
|
|
|
typedef WGLAttributes<int> WGLIntegerAttributes;
|
|
typedef WGLAttributes<float> 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:
|
|
|
|
// A class representing an OpenGL rendering context
|
|
class OpenGLContext
|
|
{
|
|
public:
|
|
|
|
OpenGLContext()
|
|
: _previousHdc(0),
|
|
_previousHglrc(0),
|
|
_hwnd(0),
|
|
_hdc(0),
|
|
_hglrc(0),
|
|
_restorePreviousOnExit(false)
|
|
{}
|
|
|
|
OpenGLContext( HWND hwnd, HDC hdc, HGLRC hglrc )
|
|
: _previousHdc(0),
|
|
_previousHglrc(0),
|
|
_hwnd(hwnd),
|
|
_hdc(hdc),
|
|
_hglrc(hglrc),
|
|
_restorePreviousOnExit(false)
|
|
{}
|
|
|
|
~OpenGLContext();
|
|
|
|
void set( HWND hwnd, HDC hdc, HGLRC hglrc )
|
|
{
|
|
_hwnd = hwnd;
|
|
_hdc = hdc;
|
|
_hglrc = hglrc;
|
|
}
|
|
|
|
HDC deviceContext() { return _hdc; }
|
|
|
|
bool makeCurrent( HDC restoreOnHdc, bool restorePreviousOnExit );
|
|
|
|
protected:
|
|
|
|
//
|
|
// Data members
|
|
//
|
|
|
|
HDC _previousHdc; // previously HDC to restore rendering context on
|
|
HGLRC _previousHglrc; // previously current rendering context
|
|
HWND _hwnd; // handle to OpenGL window
|
|
HDC _hdc; // handle to device context
|
|
HGLRC _hglrc; // handle to OpenGL rendering context
|
|
bool _restorePreviousOnExit; // restore original context on exit
|
|
|
|
private:
|
|
|
|
// no implementation for these
|
|
OpenGLContext( const OpenGLContext& );
|
|
OpenGLContext& operator=( const OpenGLContext& );
|
|
};
|
|
|
|
static std::string osgGraphicsWindowWithCursorClass; //!< Name of Win32 window class (with cursor) used by OSG graphics window instances
|
|
static std::string osgGraphicsWindowWithoutCursorClass; //!< Name of Win32 window class (without cursor) 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 bits per pixel of specified screen
|
|
// (0) is returned if screen is unknown
|
|
virtual void getScreenColorDepth( const osg::GraphicsContext::ScreenIdentifier& si, unsigned int& dmBitsPerPel );
|
|
|
|
// Set the resolution for given screen
|
|
virtual bool setScreenResolution( const osg::GraphicsContext::ScreenIdentifier& si, unsigned int width, unsigned int height );
|
|
|
|
// Set the refresh rate for given screen
|
|
virtual bool setScreenRefreshRate( const osg::GraphicsContext::ScreenIdentifier& si, double refreshRate );
|
|
|
|
// 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 current rendering context that can be used with wglXYZ extensions
|
|
virtual bool getSampleOpenGLContext( OpenGLContext& context, HDC windowHDC, int windowOriginX, int windowOriginY );
|
|
|
|
protected:
|
|
|
|
// Display devices present in the system
|
|
typedef std::vector<DISPLAY_DEVICE> 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;
|
|
|
|
// Get the screen device current mode information
|
|
bool getScreenInformation( const osg::GraphicsContext::ScreenIdentifier& si, DISPLAY_DEVICE& displayDevice, DEVMODE& deviceMode );
|
|
|
|
// Change the screen settings (resolution, refresh rate, etc.)
|
|
bool changeScreenSettings( const osg::GraphicsContext::ScreenIdentifier& si, DISPLAY_DEVICE& displayDevice, DEVMODE& deviceMode );
|
|
|
|
// Register the window classes used by OSG graphics window instances
|
|
void registerWindowClasses();
|
|
|
|
// Unregister the window classes used by OSG graphics window instances
|
|
void unregisterWindowClasses();
|
|
|
|
// Data members
|
|
WindowHandles _activeWindows; //!< handles to active windows
|
|
bool _windowClassesRegistered; //!< true after window classes have been registered
|
|
|
|
private:
|
|
|
|
// No implementation for these
|
|
Win32WindowingSystem( const Win32WindowingSystem& );
|
|
Win32WindowingSystem& operator=( const Win32WindowingSystem& );
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Error reporting
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void reportError( const std::string& msg )
|
|
{
|
|
osg::notify(osg::WARN) << "Error: " << msg.c_str() << std::endl;
|
|
}
|
|
|
|
static void reportError( const std::string& msg, unsigned int errorCode )
|
|
{
|
|
//
|
|
// Some APIs are documented as returning the error in ::GetLastError but apparently do not
|
|
// Skip "Reason" field if the errorCode is still success
|
|
//
|
|
|
|
if (errorCode==0)
|
|
{
|
|
reportError(msg);
|
|
return;
|
|
}
|
|
|
|
osg::notify(osg::WARN) << "Windows Error #" << errorCode << ": " << msg.c_str();
|
|
|
|
LPVOID lpMsgBuf;
|
|
|
|
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
errorCode,
|
|
0, // Default language
|
|
(LPTSTR) &lpMsgBuf,
|
|
0,
|
|
NULL)!=0)
|
|
{
|
|
osg::notify(osg::WARN) << ". Reason: " << LPTSTR(lpMsgBuf) << std::endl;
|
|
::LocalFree(lpMsgBuf);
|
|
}
|
|
else
|
|
{
|
|
osg::notify(osg::WARN) << std::endl;
|
|
}
|
|
}
|
|
|
|
static void reportErrorForScreen( const std::string& msg, const osg::GraphicsContext::ScreenIdentifier& si, unsigned int errorCode )
|
|
{
|
|
std::ostringstream str;
|
|
|
|
str << "[Screen #" << si.screenNum << "] " << msg;
|
|
reportError(str.str(), errorCode);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Keyboard key mapping for Win32
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
class Win32KeyboardMap
|
|
{
|
|
public:
|
|
|
|
Win32KeyboardMap()
|
|
{
|
|
_keymap[VK_ESCAPE ] = osgGA::GUIEventAdapter::KEY_Escape;
|
|
_keymap[VK_F1 ] = osgGA::GUIEventAdapter::KEY_F1;
|
|
_keymap[VK_F2 ] = osgGA::GUIEventAdapter::KEY_F2;
|
|
_keymap[VK_F3 ] = osgGA::GUIEventAdapter::KEY_F3;
|
|
_keymap[VK_F4 ] = osgGA::GUIEventAdapter::KEY_F4;
|
|
_keymap[VK_F5 ] = osgGA::GUIEventAdapter::KEY_F5;
|
|
_keymap[VK_F6 ] = osgGA::GUIEventAdapter::KEY_F6;
|
|
_keymap[VK_F7 ] = osgGA::GUIEventAdapter::KEY_F7;
|
|
_keymap[VK_F8 ] = osgGA::GUIEventAdapter::KEY_F8;
|
|
_keymap[VK_F9 ] = osgGA::GUIEventAdapter::KEY_F9;
|
|
_keymap[VK_F10 ] = osgGA::GUIEventAdapter::KEY_F10;
|
|
_keymap[VK_F11 ] = osgGA::GUIEventAdapter::KEY_F11;
|
|
_keymap[VK_F12 ] = osgGA::GUIEventAdapter::KEY_F12;
|
|
_keymap[0xc0 ] = '`';
|
|
_keymap['0' ] = '0';
|
|
_keymap['1' ] = '1';
|
|
_keymap['2' ] = '2';
|
|
_keymap['3' ] = '3';
|
|
_keymap['4' ] = '4';
|
|
_keymap['5' ] = '5';
|
|
_keymap['6' ] = '6';
|
|
_keymap['7' ] = '7';
|
|
_keymap['8' ] = '8';
|
|
_keymap['9' ] = '9';
|
|
_keymap[0xbd ] = '-';
|
|
_keymap[0xbb ] = '=';
|
|
_keymap[VK_BACK ] = osgGA::GUIEventAdapter::KEY_BackSpace;
|
|
_keymap[VK_TAB ] = osgGA::GUIEventAdapter::KEY_Tab;
|
|
_keymap['A' ] = 'A';
|
|
_keymap['B' ] = 'B';
|
|
_keymap['C' ] = 'C';
|
|
_keymap['D' ] = 'D';
|
|
_keymap['E' ] = 'E';
|
|
_keymap['F' ] = 'F';
|
|
_keymap['G' ] = 'G';
|
|
_keymap['H' ] = 'H';
|
|
_keymap['I' ] = 'I';
|
|
_keymap['J' ] = 'J';
|
|
_keymap['K' ] = 'K';
|
|
_keymap['L' ] = 'L';
|
|
_keymap['M' ] = 'M';
|
|
_keymap['N' ] = 'N';
|
|
_keymap['O' ] = 'O';
|
|
_keymap['P' ] = 'P';
|
|
_keymap['Q' ] = 'Q';
|
|
_keymap['R' ] = 'R';
|
|
_keymap['S' ] = 'S';
|
|
_keymap['T' ] = 'T';
|
|
_keymap['U' ] = 'U';
|
|
_keymap['V' ] = 'V';
|
|
_keymap['W' ] = 'W';
|
|
_keymap['X' ] = 'X';
|
|
_keymap['Y' ] = 'Y';
|
|
_keymap['Z' ] = 'Z';
|
|
_keymap[0xdb ] = '[';
|
|
_keymap[0xdd ] = ']';
|
|
_keymap[0xdc ] = '\\';
|
|
_keymap[VK_CAPITAL ] = osgGA::GUIEventAdapter::KEY_Caps_Lock;
|
|
_keymap[0xba ] = ';';
|
|
_keymap[0xde ] = '\'';
|
|
_keymap[VK_RETURN ] = osgGA::GUIEventAdapter::KEY_Return;
|
|
_keymap[VK_LSHIFT ] = osgGA::GUIEventAdapter::KEY_Shift_L;
|
|
_keymap[0xbc ] = ',';
|
|
_keymap[0xbe ] = '.';
|
|
_keymap[0xbf ] = '/';
|
|
_keymap[VK_RSHIFT ] = osgGA::GUIEventAdapter::KEY_Shift_R;
|
|
_keymap[VK_LCONTROL ] = osgGA::GUIEventAdapter::KEY_Control_L;
|
|
_keymap[VK_LWIN ] = osgGA::GUIEventAdapter::KEY_Super_L;
|
|
_keymap[VK_SPACE ] = ' ';
|
|
_keymap[VK_LMENU ] = osgGA::GUIEventAdapter::KEY_Alt_L;
|
|
_keymap[VK_RMENU ] = osgGA::GUIEventAdapter::KEY_Alt_R;
|
|
_keymap[VK_RWIN ] = osgGA::GUIEventAdapter::KEY_Super_R;
|
|
_keymap[VK_APPS ] = osgGA::GUIEventAdapter::KEY_Menu;
|
|
_keymap[VK_RCONTROL ] = osgGA::GUIEventAdapter::KEY_Control_R;
|
|
_keymap[VK_SNAPSHOT ] = osgGA::GUIEventAdapter::KEY_Print;
|
|
_keymap[VK_SCROLL ] = osgGA::GUIEventAdapter::KEY_Scroll_Lock;
|
|
_keymap[VK_PAUSE ] = osgGA::GUIEventAdapter::KEY_Pause;
|
|
_keymap[VK_HOME ] = osgGA::GUIEventAdapter::KEY_Home;
|
|
_keymap[VK_PRIOR ] = osgGA::GUIEventAdapter::KEY_Page_Up;
|
|
_keymap[VK_END ] = osgGA::GUIEventAdapter::KEY_End;
|
|
_keymap[VK_NEXT ] = osgGA::GUIEventAdapter::KEY_Page_Down;
|
|
_keymap[VK_DELETE ] = osgGA::GUIEventAdapter::KEY_Delete;
|
|
_keymap[VK_INSERT ] = osgGA::GUIEventAdapter::KEY_Insert;
|
|
_keymap[VK_LEFT ] = osgGA::GUIEventAdapter::KEY_Left;
|
|
_keymap[VK_UP ] = osgGA::GUIEventAdapter::KEY_Up;
|
|
_keymap[VK_RIGHT ] = osgGA::GUIEventAdapter::KEY_Right;
|
|
_keymap[VK_DOWN ] = osgGA::GUIEventAdapter::KEY_Down;
|
|
_keymap[VK_NUMLOCK ] = osgGA::GUIEventAdapter::KEY_Num_Lock;
|
|
_keymap[VK_DIVIDE ] = osgGA::GUIEventAdapter::KEY_KP_Divide;
|
|
_keymap[VK_MULTIPLY ] = osgGA::GUIEventAdapter::KEY_KP_Multiply;
|
|
_keymap[VK_SUBTRACT ] = osgGA::GUIEventAdapter::KEY_KP_Subtract;
|
|
_keymap[VK_ADD ] = osgGA::GUIEventAdapter::KEY_KP_Add;
|
|
_keymap[VK_NUMPAD7 ] = osgGA::GUIEventAdapter::KEY_KP_Home;
|
|
_keymap[VK_NUMPAD8 ] = osgGA::GUIEventAdapter::KEY_KP_Up;
|
|
_keymap[VK_NUMPAD9 ] = osgGA::GUIEventAdapter::KEY_KP_Page_Up;
|
|
_keymap[VK_NUMPAD4 ] = osgGA::GUIEventAdapter::KEY_KP_Left;
|
|
_keymap[VK_NUMPAD5 ] = osgGA::GUIEventAdapter::KEY_KP_Begin;
|
|
_keymap[VK_NUMPAD6 ] = osgGA::GUIEventAdapter::KEY_KP_Right;
|
|
_keymap[VK_NUMPAD1 ] = osgGA::GUIEventAdapter::KEY_KP_End;
|
|
_keymap[VK_NUMPAD2 ] = osgGA::GUIEventAdapter::KEY_KP_Down;
|
|
_keymap[VK_NUMPAD3 ] = osgGA::GUIEventAdapter::KEY_KP_Page_Down;
|
|
_keymap[VK_NUMPAD0 ] = osgGA::GUIEventAdapter::KEY_KP_Insert;
|
|
_keymap[VK_DECIMAL ] = osgGA::GUIEventAdapter::KEY_KP_Delete;
|
|
_keymap[VK_CLEAR ] = osgGA::GUIEventAdapter::KEY_Clear;
|
|
}
|
|
|
|
~Win32KeyboardMap() {}
|
|
|
|
int remapKey(int key)
|
|
{
|
|
KeyMap::const_iterator map = _keymap.find(key);
|
|
return map==_keymap.end() ? key : map->second;
|
|
}
|
|
|
|
protected:
|
|
|
|
typedef std::map<int, int> 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::OpenGLContext implementation
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
Win32WindowingSystem::OpenGLContext::~OpenGLContext()
|
|
{
|
|
if (_restorePreviousOnExit && _previousHglrc!=_hglrc && !::wglMakeCurrent(_previousHdc, _previousHglrc))
|
|
{
|
|
reportError("Win32WindowingSystem::OpenGLContext() - Unable to restore current OpenGL rendering context", ::GetLastError());
|
|
}
|
|
|
|
_previousHdc = 0;
|
|
_previousHglrc = 0;
|
|
|
|
if (_hglrc)
|
|
{
|
|
::wglMakeCurrent(_hdc, NULL);
|
|
::wglDeleteContext(_hglrc);
|
|
_hglrc = 0;
|
|
}
|
|
|
|
if (_hdc)
|
|
{
|
|
::ReleaseDC(_hwnd, _hdc);
|
|
_hdc = 0;
|
|
}
|
|
|
|
if (_hwnd)
|
|
{
|
|
::DestroyWindow(_hwnd);
|
|
_hwnd = 0;
|
|
}
|
|
}
|
|
|
|
bool Win32WindowingSystem::OpenGLContext::makeCurrent( HDC restoreOnHdc, bool restorePreviousOnExit )
|
|
{
|
|
if (_hdc==0 || _hglrc==0) return false;
|
|
|
|
_previousHglrc = restorePreviousOnExit ? ::wglGetCurrentContext() : 0;
|
|
_previousHdc = restoreOnHdc;
|
|
|
|
if (_hglrc==_previousHglrc) return true;
|
|
|
|
if (!::wglMakeCurrent(_hdc, _hglrc))
|
|
{
|
|
reportError("Win32WindowingSystem::OpenGLContext() - Unable to set current OpenGL rendering context", ::GetLastError());
|
|
return false;
|
|
}
|
|
|
|
_restorePreviousOnExit = restorePreviousOnExit;
|
|
|
|
return true;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Win32WindowingSystem implementation
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
std::string Win32WindowingSystem::osgGraphicsWindowWithCursorClass;
|
|
std::string Win32WindowingSystem::osgGraphicsWindowWithoutCursorClass;
|
|
|
|
Win32WindowingSystem::Win32WindowingSystem()
|
|
: _windowClassesRegistered(false)
|
|
{
|
|
}
|
|
|
|
Win32WindowingSystem::~Win32WindowingSystem()
|
|
{
|
|
if (osg::Referenced::getDeleteHandler())
|
|
{
|
|
osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);
|
|
osg::Referenced::getDeleteHandler()->flushAll();
|
|
}
|
|
|
|
unregisterWindowClasses();
|
|
}
|
|
|
|
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;
|
|
|
|
// 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::registerWindowClasses()
|
|
{
|
|
if (_windowClassesRegistered) return;
|
|
|
|
//
|
|
// Register the window classes used by OSG GraphicsWindowWin32 instances
|
|
//
|
|
|
|
std::ostringstream str;
|
|
str << "OSG Graphics Window for Win32 [" << ::GetCurrentProcessId() << "]";
|
|
|
|
osgGraphicsWindowWithCursorClass = str.str() + "{ with cursor }";
|
|
osgGraphicsWindowWithoutCursorClass = str.str() + "{ without cursor }";
|
|
|
|
WNDCLASSEX wc;
|
|
|
|
HINSTANCE hinst = ::GetModuleHandle(NULL);
|
|
|
|
//
|
|
// First class: class for OSG Graphics Window with a cursor enabled
|
|
//
|
|
|
|
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 = osgGraphicsWindowWithCursorClass.c_str();
|
|
wc.hIconSm = NULL;
|
|
|
|
if (::RegisterClassEx(&wc)==0)
|
|
{
|
|
unsigned int lastError = ::GetLastError();
|
|
if (lastError!=ERROR_CLASS_ALREADY_EXISTS)
|
|
{
|
|
reportError("Win32WindowingSystem::registerWindowClasses() - Unable to register first window class", lastError);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Second class: class for OSG Graphics Window without a cursor
|
|
//
|
|
|
|
wc.hCursor = NULL;
|
|
wc.lpszClassName = osgGraphicsWindowWithoutCursorClass.c_str();
|
|
|
|
if (::RegisterClassEx(&wc)==0)
|
|
{
|
|
unsigned int lastError = ::GetLastError();
|
|
if (lastError!=ERROR_CLASS_ALREADY_EXISTS)
|
|
{
|
|
reportError("Win32WindowingSystem::registerWindowClasses() - Unable to register second window class", lastError);
|
|
return;
|
|
}
|
|
}
|
|
|
|
_windowClassesRegistered = true;
|
|
}
|
|
|
|
void Win32WindowingSystem::unregisterWindowClasses()
|
|
{
|
|
if (_windowClassesRegistered)
|
|
{
|
|
::UnregisterClass(osgGraphicsWindowWithCursorClass.c_str(), ::GetModuleHandle(NULL));
|
|
::UnregisterClass(osgGraphicsWindowWithoutCursorClass.c_str(), ::GetModuleHandle(NULL));
|
|
_windowClassesRegistered = false;
|
|
}
|
|
}
|
|
|
|
bool Win32WindowingSystem::getSampleOpenGLContext( OpenGLContext& context, HDC windowHDC, int windowOriginX, int windowOriginY )
|
|
{
|
|
context.set(0, 0, 0);
|
|
|
|
registerWindowClasses();
|
|
|
|
HWND hwnd = ::CreateWindowEx(WS_EX_OVERLAPPEDWINDOW,
|
|
osgGraphicsWindowWithoutCursorClass.c_str(),
|
|
NULL,
|
|
WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_DISABLED,
|
|
windowOriginX,
|
|
windowOriginY,
|
|
1,
|
|
1,
|
|
NULL,
|
|
NULL,
|
|
::GetModuleHandle(NULL),
|
|
NULL);
|
|
if (hwnd==0)
|
|
{
|
|
reportError("Win32WindowingSystem::getSampleOpenGLContext() - Unable to create window", ::GetLastError());
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// 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 false;
|
|
}
|
|
|
|
int pixelFormatIndex = ::ChoosePixelFormat(hdc, &pixelFormat);
|
|
if (pixelFormatIndex==0)
|
|
{
|
|
reportError("Win32WindowingSystem::getSampleOpenGLContext() - Unable to choose pixel format", ::GetLastError());
|
|
::ReleaseDC(hwnd, hdc);
|
|
::DestroyWindow(hwnd);
|
|
return false;
|
|
}
|
|
|
|
if (!::SetPixelFormat(hdc, pixelFormatIndex, &pixelFormat))
|
|
{
|
|
reportError("Win32WindowingSystem::getSampleOpenGLContext() - Unable to set pixel format", ::GetLastError());
|
|
::ReleaseDC(hwnd, hdc);
|
|
::DestroyWindow(hwnd);
|
|
return false;
|
|
}
|
|
|
|
HGLRC hglrc = ::wglCreateContext(hdc);
|
|
if (hglrc==0)
|
|
{
|
|
reportError("Win32WindowingSystem::getSampleOpenGLContext() - Unable to create an OpenGL rendering context", ::GetLastError());
|
|
::ReleaseDC(hwnd, hdc);
|
|
::DestroyWindow(hwnd);
|
|
return false;
|
|
}
|
|
|
|
context.set(hwnd, hdc, hglrc);
|
|
|
|
if (!context.makeCurrent(windowHDC, true)) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
unsigned int Win32WindowingSystem::getNumScreens( const osg::GraphicsContext::ScreenIdentifier& si )
|
|
{
|
|
return si.displayNum==0 ? ::GetSystemMetrics(SM_CMONITORS) : 0;
|
|
}
|
|
|
|
bool Win32WindowingSystem::getScreenInformation( const osg::GraphicsContext::ScreenIdentifier& si, DISPLAY_DEVICE& displayDevice, 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;
|
|
}
|
|
|
|
displayDevice = displayDevices[si.screenNum];
|
|
|
|
deviceMode.dmSize = sizeof(deviceMode);
|
|
deviceMode.dmDriverExtra = 0;
|
|
|
|
if (!::EnumDisplaySettings(displayDevice.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 )
|
|
{
|
|
DISPLAY_DEVICE displayDevice;
|
|
DEVMODE deviceMode;
|
|
|
|
if (getScreenInformation(si, displayDevice, deviceMode))
|
|
{
|
|
width = deviceMode.dmPelsWidth;
|
|
height = deviceMode.dmPelsHeight;
|
|
}
|
|
else
|
|
{
|
|
width = 0;
|
|
height = 0;
|
|
}
|
|
}
|
|
|
|
void Win32WindowingSystem::getScreenColorDepth( const osg::GraphicsContext::ScreenIdentifier& si, unsigned int& dmBitsPerPel )
|
|
{
|
|
DISPLAY_DEVICE displayDevice;
|
|
DEVMODE deviceMode;
|
|
|
|
if (getScreenInformation(si, displayDevice, deviceMode))
|
|
{
|
|
dmBitsPerPel = deviceMode.dmBitsPerPel;
|
|
}
|
|
else
|
|
{
|
|
dmBitsPerPel = 0;
|
|
}
|
|
}
|
|
|
|
bool Win32WindowingSystem::changeScreenSettings( const osg::GraphicsContext::ScreenIdentifier& si, DISPLAY_DEVICE& displayDevice, DEVMODE& deviceMode )
|
|
{
|
|
//
|
|
// Start by testing if the change would be successful (without applying it)
|
|
//
|
|
|
|
unsigned int result = ::ChangeDisplaySettingsEx(displayDevice.DeviceName, &deviceMode, NULL, CDS_TEST, NULL);
|
|
if (result==DISP_CHANGE_SUCCESSFUL)
|
|
{
|
|
result = ::ChangeDisplaySettingsEx(displayDevice.DeviceName, &deviceMode, NULL, 0, NULL);
|
|
if (result==DISP_CHANGE_SUCCESSFUL) return true;
|
|
}
|
|
|
|
std::string msg = "Win32WindowingSystem::changeScreenSettings() - Unable to change the screen settings.";
|
|
|
|
switch( result )
|
|
{
|
|
case DISP_CHANGE_BADMODE : msg += " The specified graphics mode is not supported."; break;
|
|
case DISP_CHANGE_FAILED : msg += " The display driver failed the specified graphics mode."; break;
|
|
case DISP_CHANGE_RESTART : msg += " The computer must be restarted for the graphics mode to work."; break;
|
|
default : break;
|
|
}
|
|
|
|
reportErrorForScreen(msg, si, result);
|
|
return false;
|
|
}
|
|
|
|
bool Win32WindowingSystem::setScreenResolution( const osg::GraphicsContext::ScreenIdentifier& si, unsigned int width, unsigned int height )
|
|
{
|
|
DISPLAY_DEVICE displayDevice;
|
|
DEVMODE deviceMode;
|
|
|
|
if (!getScreenInformation(si, displayDevice, deviceMode)) return false;
|
|
|
|
deviceMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
|
|
deviceMode.dmPelsWidth = width;
|
|
deviceMode.dmPelsHeight = height;
|
|
|
|
return changeScreenSettings(si, displayDevice, deviceMode);
|
|
}
|
|
|
|
bool Win32WindowingSystem::setScreenRefreshRate( const osg::GraphicsContext::ScreenIdentifier& si, double refreshRate )
|
|
{
|
|
DISPLAY_DEVICE displayDevice;
|
|
DEVMODE deviceMode;
|
|
|
|
unsigned int width, height;
|
|
getScreenResolution(si, width, height);
|
|
|
|
if (!getScreenInformation(si, displayDevice, deviceMode)) return false;
|
|
|
|
deviceMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
|
|
deviceMode.dmPelsWidth = width;
|
|
deviceMode.dmPelsHeight = height;
|
|
deviceMode.dmDisplayFrequency = refreshRate;
|
|
|
|
return changeScreenSettings(si, displayDevice, deviceMode);
|
|
}
|
|
|
|
void Win32WindowingSystem::getScreenPosition( const osg::GraphicsContext::ScreenIdentifier& si, int& originX, int& originY, unsigned int& width, unsigned int& height )
|
|
{
|
|
DISPLAY_DEVICE displayDevice;
|
|
DEVMODE deviceMode;
|
|
|
|
if (getScreenInformation(si, displayDevice, 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<osgViewer::PixelBufferWin32> pbuffer = new PixelBufferWin32(traits);
|
|
if (pbuffer->valid()) return pbuffer.release();
|
|
else return 0;
|
|
}
|
|
else
|
|
{
|
|
registerWindowClasses();
|
|
|
|
osg::ref_ptr<osgViewer::GraphicsWindowWin32> 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),
|
|
_currentCursor(0),
|
|
_windowProcedure(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),
|
|
_ownsWindow(true),
|
|
_closeWindow(false),
|
|
_destroyWindow(false),
|
|
_destroying(false)
|
|
{
|
|
_traits = traits;
|
|
if (_traits->useCursor) setCursor(LeftArrowCursor);
|
|
|
|
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() );
|
|
}
|
|
}
|
|
}
|
|
|
|
GraphicsWindowWin32::~GraphicsWindowWin32()
|
|
{
|
|
close();
|
|
destroyWindow();
|
|
}
|
|
|
|
void GraphicsWindowWin32::init()
|
|
{
|
|
if (_initialized) return;
|
|
|
|
getEventQueue()->setCurrentEventState(osgGA::GUIEventAdapter::getAccumulatedEventState().get());
|
|
|
|
WindowData *windowData = _traits.get() ? dynamic_cast<WindowData*>(_traits->inheritedWindowData.get()) : 0;
|
|
HWND windowHandle = windowData ? windowData->_hwnd : 0;
|
|
|
|
_ownsWindow = windowHandle==0;
|
|
_closeWindow = false;
|
|
_destroyWindow = false;
|
|
_destroying = false;
|
|
|
|
_initialized = _ownsWindow ? createWindow() : setWindow(windowHandle);
|
|
_valid = _initialized;
|
|
}
|
|
|
|
bool GraphicsWindowWin32::createWindow()
|
|
{
|
|
unsigned int extendedStyle;
|
|
unsigned int windowStyle;
|
|
|
|
if (!determineWindowPositionAndStyle(_traits->windowDecoration,
|
|
_windowOriginXToRealize,
|
|
_windowOriginYToRealize,
|
|
_windowWidthToRealize,
|
|
_windowHeightToRealize,
|
|
windowStyle,
|
|
extendedStyle))
|
|
{
|
|
reportError("GraphicsWindowWin32::createWindow() - Unable to determine the window position and style");
|
|
return false;
|
|
}
|
|
|
|
_hwnd = ::CreateWindowEx(extendedStyle,
|
|
_traits->useCursor ? Win32WindowingSystem::osgGraphicsWindowWithCursorClass.c_str() :
|
|
Win32WindowingSystem::osgGraphicsWindowWithoutCursorClass.c_str(),
|
|
_traits->windowName.c_str(),
|
|
windowStyle,
|
|
_windowOriginXToRealize,
|
|
_windowOriginYToRealize,
|
|
_windowWidthToRealize,
|
|
_windowHeightToRealize,
|
|
NULL,
|
|
NULL,
|
|
::GetModuleHandle(NULL),
|
|
NULL);
|
|
if (_hwnd==0)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::createWindow() - Unable to create window", _traits->screenNum, ::GetLastError());
|
|
return false;
|
|
}
|
|
|
|
_hdc = ::GetDC(_hwnd);
|
|
if (_hdc==0)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::createWindow() - Unable to get window device context", _traits->screenNum, ::GetLastError());
|
|
destroyWindow();
|
|
_hwnd = 0;
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Set the pixel format according to traits specified
|
|
//
|
|
|
|
if (!setPixelFormat())
|
|
{
|
|
::ReleaseDC(_hwnd, _hdc);
|
|
_hdc = 0;
|
|
destroyWindow();
|
|
return false;
|
|
}
|
|
|
|
Win32WindowingSystem::getInterface()->registerWindow(_hwnd, this);
|
|
return true;
|
|
}
|
|
|
|
bool GraphicsWindowWin32::setWindow( HWND handle )
|
|
{
|
|
if (_initialized)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::setWindow() - Window already created; it cannot be changed", _traits->screenNum, ::GetLastError());
|
|
return false;
|
|
}
|
|
|
|
if (handle==0)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::setWindow() - Invalid window handle passed", _traits->screenNum, ::GetLastError());
|
|
return false;
|
|
}
|
|
|
|
_hwnd = handle;
|
|
if (_hwnd==0)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::setWindow() - Unable to retrieve native window handle", _traits->screenNum, ::GetLastError());
|
|
return false;
|
|
}
|
|
|
|
_hdc = ::GetDC(_hwnd);
|
|
if (_hdc==0)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::setWindow() - Unable to get window device context", _traits->screenNum, ::GetLastError());
|
|
_hwnd = 0;
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Check if we must set the pixel format of the inherited window
|
|
//
|
|
|
|
if (_traits.valid() && _traits->setInheritedWindowPixelFormat)
|
|
{
|
|
if (!setPixelFormat())
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::setWindow() - Unable to set the inherited window pixel format", _traits->screenNum, ::GetLastError());
|
|
_hdc = 0;
|
|
_hwnd = 0;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Create the OpenGL rendering context associated with this window
|
|
//
|
|
|
|
_hglrc = ::wglCreateContext(_hdc);
|
|
if (_hglrc==0)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::setWindow() - Unable to create OpenGL rendering context", _traits->screenNum, ::GetLastError());
|
|
::ReleaseDC(_hwnd, _hdc);
|
|
_hdc = 0;
|
|
_hwnd = 0;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!registerWindowProcedure())
|
|
{
|
|
::wglDeleteContext(_hglrc);
|
|
_hglrc = 0;
|
|
::ReleaseDC(_hwnd, _hdc);
|
|
_hdc = 0;
|
|
_hwnd = 0;
|
|
return false;
|
|
}
|
|
|
|
Win32WindowingSystem::getInterface()->registerWindow(_hwnd, this);
|
|
|
|
_initialized = true;
|
|
_valid = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
void GraphicsWindowWin32::destroyWindow( bool deleteNativeWindow )
|
|
{
|
|
if (_destroying) return;
|
|
_destroying = true;
|
|
|
|
if (_hdc)
|
|
{
|
|
releaseContext();
|
|
|
|
if (_hglrc)
|
|
{
|
|
::wglDeleteContext(_hglrc);
|
|
_hglrc = 0;
|
|
}
|
|
|
|
::ReleaseDC(_hwnd, _hdc);
|
|
_hdc = 0;
|
|
}
|
|
|
|
(void)unregisterWindowProcedure();
|
|
|
|
if (_hwnd)
|
|
{
|
|
Win32WindowingSystem::getInterface()->unregisterWindow(_hwnd);
|
|
if (_ownsWindow && deleteNativeWindow) ::DestroyWindow(_hwnd);
|
|
_hwnd = 0;
|
|
}
|
|
|
|
_initialized = false;
|
|
_realized = false;
|
|
_valid = false;
|
|
_destroying = false;
|
|
}
|
|
|
|
bool GraphicsWindowWin32::registerWindowProcedure()
|
|
{
|
|
::SetLastError(0);
|
|
_windowProcedure = (WNDPROC)::SetWindowLongPtr(_hwnd, GWLP_WNDPROC, LONG_PTR(WindowProc));
|
|
unsigned int error = ::GetLastError();
|
|
|
|
if (_windowProcedure==0 && error)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::registerWindowProcedure() - Unable to register window procedure", _traits->screenNum, error);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GraphicsWindowWin32::unregisterWindowProcedure()
|
|
{
|
|
if (_windowProcedure==0 || _hwnd==0) return true;
|
|
|
|
::SetLastError(0);
|
|
WNDPROC wndProc = (WNDPROC)::SetWindowLongPtr(_hwnd, GWLP_WNDPROC, LONG_PTR(_windowProcedure));
|
|
unsigned int error = ::GetLastError();
|
|
|
|
if (wndProc==0 && error)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::unregisterWindowProcedure() - Unable to unregister window procedure", _traits->screenNum, error);
|
|
return false;
|
|
}
|
|
|
|
_windowProcedure = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
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 false;
|
|
|
|
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))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::determineWindowPositionAndStyle() - Unable to adjust window rectangle", _traits->screenNum, ::GetLastError());
|
|
return false;
|
|
}
|
|
|
|
x = corners.left;
|
|
y = corners.top;
|
|
w = corners.right - corners.left + 1;
|
|
h = corners.bottom - corners.top + 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void PreparePixelFormatSpecifications( const osg::GraphicsContext::Traits& traits,
|
|
WGLIntegerAttributes& attributes,
|
|
bool allowSwapExchangeARB )
|
|
{
|
|
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);
|
|
if (allowSwapExchangeARB) 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();
|
|
}
|
|
|
|
static int ChooseMatchingPixelFormat( HDC hdc, int screenNum, const WGLIntegerAttributes& formatSpecifications ,osg::GraphicsContext::Traits* _traits)
|
|
{
|
|
//
|
|
// Access the entry point for the wglChoosePixelFormatARB function
|
|
//
|
|
|
|
WGLChoosePixelFormatARB wglChoosePixelFormatARB = (WGLChoosePixelFormatARB)wglGetProcAddress("wglChoosePixelFormatARB");
|
|
if (wglChoosePixelFormatARB==0)
|
|
{
|
|
// = openGLContext.getTraits()
|
|
reportErrorForScreen("ChooseMatchingPixelFormat() - wglChoosePixelFormatARB extension not found, trying GDI", screenNum, ::GetLastError());
|
|
PIXELFORMATDESCRIPTOR pixelFormat = {
|
|
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
|
|
1, // version number
|
|
PFD_DRAW_TO_WINDOW | // support window
|
|
PFD_SUPPORT_OPENGL | // support OpenGL
|
|
(_traits->doubleBuffer ? PFD_DOUBLEBUFFER : NULL), // double buffered ?
|
|
PFD_TYPE_RGBA, // RGBA type
|
|
_traits->red + _traits->green + _traits->blue, // color depth
|
|
_traits->red ,0, _traits->green ,0, _traits->blue, 0, // shift bits ignored
|
|
_traits->alpha, // alpha buffer ?
|
|
0, // shift bit ignored
|
|
0, // no accumulation buffer
|
|
0, 0, 0, 0, // accum bits ignored
|
|
_traits->depth, // 32 or 16 bit z-buffer ?
|
|
_traits->stencil, // stencil buffer ?
|
|
0, // no auxiliary buffer
|
|
PFD_MAIN_PLANE, // main layer
|
|
0, // reserved
|
|
0, 0, 0 // layer masks ignored
|
|
};
|
|
int pixelFormatIndex = ::ChoosePixelFormat(hdc, &pixelFormat);
|
|
if (pixelFormatIndex == 0)
|
|
{
|
|
reportErrorForScreen("ChooseMatchingPixelFormat() - GDI ChoosePixelFormat Failed.", screenNum, ::GetLastError());
|
|
return -1;
|
|
}
|
|
|
|
::DescribePixelFormat(hdc, pixelFormatIndex ,sizeof(PIXELFORMATDESCRIPTOR),&pixelFormat);
|
|
if (((pixelFormat.dwFlags & PFD_GENERIC_FORMAT) != 0) && ((pixelFormat.dwFlags & PFD_GENERIC_ACCELERATED) == 0))
|
|
{
|
|
osg::notify(osg::WARN) << "Rendering in software: pixelFormatIndex " << pixelFormatIndex << std::endl;
|
|
}
|
|
return pixelFormatIndex;
|
|
}
|
|
|
|
int pixelFormatIndex = 0;
|
|
unsigned int numMatchingPixelFormats = 0;
|
|
|
|
if (!wglChoosePixelFormatARB(hdc,
|
|
formatSpecifications.get(),
|
|
NULL,
|
|
1,
|
|
&pixelFormatIndex,
|
|
&numMatchingPixelFormats))
|
|
{
|
|
reportErrorForScreen("ChooseMatchingPixelFormat() - Unable to choose the requested pixel format", screenNum, ::GetLastError());
|
|
return -1;
|
|
}
|
|
|
|
return numMatchingPixelFormats==0 ? -1 : pixelFormatIndex;
|
|
}
|
|
|
|
bool GraphicsWindowWin32::setPixelFormat()
|
|
{
|
|
Win32WindowingSystem::OpenGLContext openGLContext;
|
|
if (!Win32WindowingSystem::getInterface()->getSampleOpenGLContext(openGLContext, _hdc, _screenOriginX, _screenOriginY)) return false;
|
|
|
|
//
|
|
// Build the specifications of the requested pixel format
|
|
//
|
|
|
|
WGLIntegerAttributes formatSpecs;
|
|
::PreparePixelFormatSpecifications(*_traits, formatSpecs, true);
|
|
|
|
//
|
|
// Choose the closest matching pixel format from the specified traits
|
|
//
|
|
|
|
int pixelFormatIndex = ::ChooseMatchingPixelFormat(openGLContext.deviceContext(), _traits->screenNum, formatSpecs,_traits.get());
|
|
|
|
if (pixelFormatIndex<0)
|
|
{
|
|
unsigned int bpp;
|
|
Win32WindowingSystem::getInterface()->getScreenColorDepth(*_traits.get(), bpp);
|
|
if (bpp < 32) {
|
|
osg::notify(osg::INFO) << "GraphicsWindowWin32::setPixelFormat() - Display setting is not 32 bit colors, "
|
|
<< bpp
|
|
<< " bits per pixel on screen #"
|
|
<< _traits->screenNum
|
|
<< std::endl;
|
|
|
|
_traits->red = bpp / 4; //integer devide, determine minimum number of bits we will accept
|
|
_traits->green = bpp / 4;
|
|
_traits->blue = bpp / 4;
|
|
::PreparePixelFormatSpecifications(*_traits, formatSpecs, true);// try again with WGL_SWAP_METHOD_ARB
|
|
pixelFormatIndex = ::ChooseMatchingPixelFormat(openGLContext.deviceContext(), _traits->screenNum, formatSpecs,_traits.get());
|
|
}
|
|
}
|
|
if (pixelFormatIndex<0)
|
|
{
|
|
::PreparePixelFormatSpecifications(*_traits, formatSpecs, false);
|
|
pixelFormatIndex = ::ChooseMatchingPixelFormat(openGLContext.deviceContext(), _traits->screenNum, formatSpecs,_traits.get());
|
|
if (pixelFormatIndex<0)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::setPixelFormat() - No matching pixel format found based on traits specified", _traits->screenNum, 0);
|
|
return false;
|
|
}
|
|
|
|
osg::notify(osg::INFO) << "GraphicsWindowWin32::setPixelFormat() - Found a matching pixel format but without the WGL_SWAP_METHOD_ARB specification for screen #"
|
|
<< _traits->screenNum
|
|
<< std::endl;
|
|
}
|
|
|
|
//
|
|
// Set the pixel format found
|
|
//
|
|
|
|
PIXELFORMATDESCRIPTOR pfd;
|
|
::memset(&pfd, 0, sizeof(pfd));
|
|
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
|
|
pfd.nVersion = 1;
|
|
|
|
if (!::SetPixelFormat(_hdc, pixelFormatIndex, &pfd))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::setPixelFormat() - Unable to set pixel format", _traits->screenNum, ::GetLastError());
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Create the OpenGL rendering context associated with this window
|
|
//
|
|
|
|
_hglrc = ::wglCreateContext(_hdc);
|
|
if (_hglrc==0)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::setPixelFormat() - Unable to create OpenGL rendering context", _traits->screenNum, ::GetLastError());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GraphicsWindowWin32::setWindowDecorationImplementation( 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))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::setWindowDecoration() - Unable to determine the window position and style", _traits->screenNum, 0);
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Change the window style
|
|
//
|
|
|
|
::SetLastError(0);
|
|
unsigned int result = ::SetWindowLong(_hwnd, GWL_STYLE, windowStyle);
|
|
unsigned int error = ::GetLastError();
|
|
if (result==0 && error)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::setWindowDecoration() - Unable to set window style", _traits->screenNum, error);
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Change the window extended style
|
|
//
|
|
|
|
::SetLastError(0);
|
|
result = ::SetWindowLong(_hwnd, GWL_EXSTYLE, extendedStyle);
|
|
error = ::GetLastError();
|
|
if (result==0 && error)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::setWindowDecoration() - Unable to set window extented style", _traits->screenNum, error);
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// 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))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::setWindowDecoration() - Unable to set new window position and size", _traits->screenNum, ::GetLastError());
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Repaint the desktop to cleanup decorations removed
|
|
//
|
|
|
|
if (!decorated)
|
|
{
|
|
::InvalidateRect(NULL, NULL, TRUE);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GraphicsWindowWin32::realizeImplementation()
|
|
{
|
|
if (_realized) return true;
|
|
|
|
if (!_initialized)
|
|
{
|
|
init();
|
|
if (!_initialized) return false;
|
|
}
|
|
{
|
|
|
|
if (_traits.valid() && _traits->sharedContext)
|
|
{
|
|
GraphicsWindowWin32* sharedContextWin32 = dynamic_cast<GraphicsWindowWin32*>(_traits->sharedContext);
|
|
if (sharedContextWin32)
|
|
{
|
|
struct RestoreContext
|
|
{
|
|
RestoreContext()
|
|
{
|
|
_hdc = wglGetCurrentDC();
|
|
_hglrc = wglGetCurrentContext();
|
|
}
|
|
~RestoreContext()
|
|
{
|
|
if (_hdc)
|
|
{
|
|
wglMakeCurrent(_hdc,_hglrc);
|
|
}
|
|
}
|
|
protected:
|
|
HDC _hdc;
|
|
HGLRC _hglrc;
|
|
} restoreContext;
|
|
|
|
_realized = true;
|
|
bool result = makeCurrent();
|
|
_realized = false;
|
|
|
|
if (!result)
|
|
{
|
|
return false;
|
|
}
|
|
if (!wglShareLists(sharedContextWin32->getWGLContext(), getWGLContext()))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::realizeImplementation() - Unable to share OpenGL context", _traits->screenNum, ::GetLastError());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_ownsWindow)
|
|
{
|
|
//
|
|
// Bring the window on top of other ones (including the taskbar if it covers it completely)
|
|
//
|
|
// NOTE: To cover the taskbar with a window that does not completely cover it, the HWND_TOPMOST
|
|
// Z-order must be used in the code below instead of HWND_TOP.
|
|
// @todo: This should be controlled through a flag in the traits (topMostWindow)
|
|
//
|
|
|
|
if (!::SetWindowPos(_hwnd,
|
|
HWND_TOP,
|
|
_windowOriginXToRealize,
|
|
_windowOriginYToRealize,
|
|
_windowWidthToRealize,
|
|
_windowHeightToRealize,
|
|
SWP_SHOWWINDOW))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::realizeImplementation() - Unable to show window", _traits->screenNum, ::GetLastError());
|
|
return false;
|
|
}
|
|
|
|
if (!::UpdateWindow(_hwnd))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::realizeImplementation() - Unable to update window", _traits->screenNum, ::GetLastError());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
_realized = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GraphicsWindowWin32::makeCurrentImplementation()
|
|
{
|
|
if (!_realized)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::makeCurrentImplementation() - Window not realized; cannot do makeCurrent.", _traits->screenNum, 0);
|
|
return false;
|
|
}
|
|
|
|
if (!::wglMakeCurrent(_hdc, _hglrc))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::makeCurrentImplementation() - Unable to set current OpenGL rendering context", _traits->screenNum, ::GetLastError());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GraphicsWindowWin32::releaseContextImplementation()
|
|
{
|
|
if (!::wglMakeCurrent(_hdc, NULL))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::releaseContextImplementation() - Unable to release current OpenGL rendering context", _traits->screenNum, ::GetLastError());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void GraphicsWindowWin32::closeImplementation()
|
|
{
|
|
destroyWindow();
|
|
|
|
_initialized = false;
|
|
_valid = false;
|
|
_realized = false;
|
|
}
|
|
|
|
void GraphicsWindowWin32::swapBuffersImplementation()
|
|
{
|
|
if (!_realized) return;
|
|
if (!::SwapBuffers(_hdc))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::swapBuffersImplementation() - Unable to swap display buffers", _traits->screenNum, ::GetLastError());
|
|
}
|
|
}
|
|
|
|
void GraphicsWindowWin32::checkEvents()
|
|
{
|
|
if (!_realized) return;
|
|
|
|
MSG msg;
|
|
while (::PeekMessage(&msg, _hwnd, NULL, NULL, PM_REMOVE))
|
|
{
|
|
::TranslateMessage(&msg);
|
|
::DispatchMessage(&msg);
|
|
}
|
|
|
|
if (_closeWindow)
|
|
{
|
|
_closeWindow = false;
|
|
close();
|
|
}
|
|
|
|
if (_destroyWindow)
|
|
{
|
|
_destroyWindow = false;
|
|
destroyWindow(false);
|
|
}
|
|
}
|
|
|
|
void GraphicsWindowWin32::grabFocus()
|
|
{
|
|
if (!::SetForegroundWindow(_hwnd))
|
|
{
|
|
osg::notify(osg::WARN) << "Warning: GraphicsWindowWin32::grabFocus() - Failed grabbing the focus" << std::endl;
|
|
}
|
|
}
|
|
|
|
void GraphicsWindowWin32::grabFocusIfPointerInWindow()
|
|
{
|
|
POINT mousePos;
|
|
if (!::GetCursorPos(&mousePos))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::grabFocusIfPointerInWindow() - Unable to get cursor position", _traits->screenNum, ::GetLastError());
|
|
return;
|
|
}
|
|
|
|
RECT windowRect;
|
|
if (!::GetWindowRect(_hwnd, &windowRect))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::grabFocusIfPointerInWindow() - Unable to get window position", _traits->screenNum, ::GetLastError());
|
|
return;
|
|
}
|
|
|
|
if (mousePos.x>=windowRect.left && mousePos.x<=windowRect.right &&
|
|
mousePos.y>=windowRect.top && mousePos.y<=windowRect.bottom)
|
|
{
|
|
grabFocus();
|
|
}
|
|
}
|
|
|
|
void GraphicsWindowWin32::requestWarpPointer( float x, float y )
|
|
{
|
|
if (!_realized)
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::requestWarpPointer() - Window not realized; cannot warp pointer", _traits->screenNum, 0);
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
RECT windowRect;
|
|
if (!::GetWindowRect(_hwnd, &windowRect))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::requestWarpPointer() - Unable to get window rectangle", _traits->screenNum, ::GetLastError());
|
|
return;
|
|
}
|
|
|
|
if (!::SetCursorPos(windowRect.left + x, windowRect.top + y))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::requestWarpPointer() - Unable to set cursor position", _traits->screenNum, ::GetLastError());
|
|
return;
|
|
}
|
|
#else
|
|
// MIKEC: NEW CODE
|
|
POINT pt;
|
|
pt.x=x;
|
|
pt.y=y;
|
|
// convert point in client area coordinates to screen coordinates
|
|
if (!::ClientToScreen(_hwnd,&pt))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::requestWarpPointer() - Unable to convert cursor position to screen coordinates", _traits->screenNum, ::GetLastError());
|
|
}
|
|
if (!::SetCursorPos(pt.x,pt.y))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::requestWarpPointer() - Unable to set cursor position", _traits->screenNum, ::GetLastError());
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
getEventQueue()->mouseWarped(x,y);
|
|
}
|
|
|
|
bool GraphicsWindowWin32::setWindowRectangleImplementation(int x, int y, int width, int height)
|
|
{
|
|
if (!::SetWindowPos(_hwnd, HWND_TOP, x, y, width, height, SWP_SHOWWINDOW | SWP_FRAMECHANGED))
|
|
{
|
|
reportErrorForScreen("GraphicsWindowWin32::setWindowRectangle() - Unable to set new window position and size", _traits->screenNum, ::GetLastError());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void GraphicsWindowWin32::useCursor( bool cursorOn )
|
|
{
|
|
_traits->useCursor = cursorOn;
|
|
}
|
|
|
|
void GraphicsWindowWin32::setCursor( MouseCursor mouseCursor )
|
|
{
|
|
HCURSOR newCursor = getOrCreateCursor( mouseCursor);
|
|
if (newCursor == _currentCursor) return;
|
|
|
|
_currentCursor = newCursor;
|
|
_traits->useCursor = (_currentCursor != NULL);
|
|
}
|
|
|
|
HCURSOR GraphicsWindowWin32::getOrCreateCursor(MouseCursor mouseCursor)
|
|
{
|
|
std::map<MouseCursor,HCURSOR>::iterator i = _mouseCursorMap.find(mouseCursor);
|
|
if (i != _mouseCursorMap.end()) return i->second;
|
|
|
|
switch (mouseCursor) {
|
|
case NoCursor:
|
|
_mouseCursorMap[mouseCursor] = NULL;
|
|
break;
|
|
case RightArrowCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_ARROW);
|
|
break;
|
|
case LeftArrowCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_ARROW);
|
|
break;
|
|
case InfoCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_SIZEALL);
|
|
break;
|
|
case DestroyCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_NO );
|
|
break;
|
|
case HelpCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_HELP );
|
|
break;
|
|
case CycleCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_NO );
|
|
break;
|
|
case SprayCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_SIZEALL );
|
|
break;
|
|
case WaitCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_WAIT);
|
|
break;
|
|
case TextCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_IBEAM );
|
|
break;
|
|
case CrosshairCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_CROSS );
|
|
break;
|
|
case UpDownCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_SIZENS );
|
|
break;
|
|
case LeftRightCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_SIZEWE );
|
|
break;
|
|
case TopSideCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_UPARROW );
|
|
break;
|
|
case BottomSideCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_UPARROW );
|
|
break;
|
|
case LeftSideCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_SIZEWE);
|
|
break;
|
|
case RightSideCursor:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_SIZEWE );
|
|
break;
|
|
case TopLeftCorner:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_SIZENWSE );
|
|
break;
|
|
case TopRightCorner:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_SIZENESW );
|
|
break;
|
|
case BottomRightCorner:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_SIZENWSE );
|
|
break;
|
|
case BottomLeftCorner:
|
|
_mouseCursorMap[mouseCursor] = LoadCursor( NULL, IDC_SIZENESW );
|
|
break;
|
|
}
|
|
|
|
return _mouseCursorMap[mouseCursor];
|
|
}
|
|
|
|
void GraphicsWindowWin32::adaptKey( WPARAM wParam, LPARAM lParam, int& keySymbol, unsigned int& modifierMask )
|
|
{
|
|
modifierMask = 0;
|
|
|
|
bool rightSide = (lParam & 0x01000000)!=0;
|
|
int virtualKey = ::MapVirtualKeyEx((lParam>>16) & 0xff, 3, ::GetKeyboardLayout(0));
|
|
|
|
BYTE keyState[256];
|
|
|
|
if (virtualKey==0 || !::GetKeyboardState(keyState))
|
|
{
|
|
keySymbol = 0;
|
|
return;
|
|
}
|
|
|
|
switch (virtualKey)
|
|
{
|
|
//////////////////
|
|
case VK_LSHIFT :
|
|
//////////////////
|
|
|
|
modifierMask |= osgGA::GUIEventAdapter::MODKEY_LEFT_SHIFT;
|
|
break;
|
|
|
|
//////////////////
|
|
case VK_RSHIFT :
|
|
//////////////////
|
|
|
|
modifierMask |= osgGA::GUIEventAdapter::MODKEY_RIGHT_SHIFT;
|
|
break;
|
|
|
|
//////////////////
|
|
case VK_CONTROL :
|
|
case VK_LCONTROL :
|
|
//////////////////
|
|
|
|
virtualKey = rightSide ? VK_RCONTROL : VK_LCONTROL;
|
|
modifierMask |= rightSide ? osgGA::GUIEventAdapter::MODKEY_RIGHT_CTRL : osgGA::GUIEventAdapter::MODKEY_LEFT_CTRL;
|
|
break;
|
|
|
|
//////////////////
|
|
case VK_MENU :
|
|
case VK_LMENU :
|
|
//////////////////
|
|
|
|
virtualKey = rightSide ? VK_RMENU : VK_LMENU;
|
|
modifierMask |= rightSide ? osgGA::GUIEventAdapter::MODKEY_RIGHT_ALT : osgGA::GUIEventAdapter::MODKEY_LEFT_ALT;
|
|
break;
|
|
|
|
//////////////////
|
|
default :
|
|
//////////////////
|
|
|
|
virtualKey = wParam;
|
|
break;
|
|
}
|
|
|
|
if (keyState[VK_CAPITAL] & 0x01) modifierMask |= osgGA::GUIEventAdapter::MODKEY_CAPS_LOCK;
|
|
if (keyState[VK_NUMLOCK] & 0x01) modifierMask |= osgGA::GUIEventAdapter::MODKEY_NUM_LOCK;
|
|
|
|
keySymbol = remapWin32Key(virtualKey);
|
|
|
|
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<WORD*>(asciiKey), 0);
|
|
if (numChars>0) keySymbol = asciiKey[0];
|
|
}
|
|
}
|
|
|
|
void GraphicsWindowWin32::transformMouseXY( float& x, float& y )
|
|
{
|
|
if (getEventQueue()->getUseFixedMouseInputRange())
|
|
{
|
|
osgGA::GUIEventAdapter* eventState = getEventQueue()->getCurrentEventState();
|
|
|
|
x = eventState->getXmin() + (eventState->getXmax()-eventState->getXmin())*x/float(_traits->width);
|
|
y = eventState->getYmin() + (eventState->getYmax()-eventState->getYmin())*y/float(_traits->height);
|
|
}
|
|
}
|
|
|
|
LRESULT GraphicsWindowWin32::handleNativeWindowingEvent( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
|
{
|
|
//!@todo adapt windows event time to osgGA event queue time for better resolution
|
|
|
|
double baseTime = _timeOfLastCheckEvents;
|
|
double eventTime = _timeOfLastCheckEvents;
|
|
double resizeTime = eventTime;
|
|
|
|
_timeOfLastCheckEvents = getEventQueue()->getTime();
|
|
|
|
switch(uMsg)
|
|
{
|
|
/////////////////
|
|
case WM_PAINT :
|
|
/////////////////
|
|
|
|
if (_ownsWindow)
|
|
{
|
|
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 :
|
|
/////////////////////
|
|
|
|
{
|
|
::SetCapture(hwnd);
|
|
|
|
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 :
|
|
/////////////////////
|
|
|
|
{
|
|
::ReleaseCapture();
|
|
|
|
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_LBUTTONDBLCLK :
|
|
case WM_MBUTTONDBLCLK :
|
|
case WM_RBUTTONDBLCLK :
|
|
///////////////////////
|
|
|
|
{
|
|
::SetCapture(hwnd);
|
|
|
|
int button;
|
|
|
|
if (uMsg==WM_LBUTTONDBLCLK) button = 1;
|
|
else if (uMsg==WM_MBUTTONDBLCLK) button = 2;
|
|
else button = 3;
|
|
|
|
float mx = GET_X_LPARAM(lParam);
|
|
float my = GET_Y_LPARAM(lParam);
|
|
transformMouseXY(mx, my);
|
|
getEventQueue()->mouseDoubleButtonPress(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()->getCurrentEventState()->setModKeyMask(modifierMask);
|
|
getEventQueue()->keyPress(keySymbol, eventTime);
|
|
}
|
|
break;
|
|
|
|
//////////////////
|
|
case WM_KEYUP :
|
|
case WM_SYSKEYUP :
|
|
//////////////////
|
|
|
|
{
|
|
int keySymbol = 0;
|
|
unsigned int modifierMask = 0;
|
|
adaptKey(wParam, lParam, keySymbol, modifierMask);
|
|
//getEventQueue()->getCurrentEventState()->setModKeyMask(modifierMask);
|
|
getEventQueue()->keyRelease(keySymbol, eventTime);
|
|
}
|
|
break;
|
|
|
|
///////////////////
|
|
case WM_SETCURSOR :
|
|
///////////////////
|
|
if (_traits->useCursor)
|
|
::SetCursor( _currentCursor);
|
|
else
|
|
::SetCursor(NULL);
|
|
return TRUE;
|
|
|
|
/////////////////
|
|
case WM_CLOSE :
|
|
/////////////////
|
|
|
|
getEventQueue()->closeWindow(eventTime);
|
|
break;
|
|
|
|
/////////////////
|
|
case WM_DESTROY :
|
|
/////////////////
|
|
|
|
_destroyWindow = true;
|
|
if (_ownsWindow)
|
|
{
|
|
::PostQuitMessage(0);
|
|
}
|
|
break;
|
|
|
|
//////////////
|
|
case WM_QUIT :
|
|
//////////////
|
|
|
|
_closeWindow = true;
|
|
return wParam;
|
|
|
|
/////////////////
|
|
default :
|
|
/////////////////
|
|
|
|
if (_ownsWindow) return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
break;
|
|
}
|
|
|
|
if (_ownsWindow) return 0;
|
|
|
|
return _windowProcedure==0 ? ::DefWindowProc(hwnd, uMsg, wParam, lParam) :
|
|
::CallWindowProc(_windowProcedure, hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Class responsible for registering the Win32 Windowing System interface
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
struct RegisterWindowingSystemInterfaceProxy
|
|
{
|
|
RegisterWindowingSystemInterfaceProxy()
|
|
{
|
|
osg::GraphicsContext::setWindowingSystemInterface(Win32WindowingSystem::getInterface());
|
|
}
|
|
|
|
~RegisterWindowingSystemInterfaceProxy()
|
|
{
|
|
if (osg::Referenced::getDeleteHandler())
|
|
{
|
|
osg::Referenced::getDeleteHandler()->setNumFramesToRetainObjects(0);
|
|
osg::Referenced::getDeleteHandler()->flushAll();
|
|
}
|
|
|
|
osg::GraphicsContext::setWindowingSystemInterface(0);
|
|
}
|
|
};
|
|
|
|
static RegisterWindowingSystemInterfaceProxy createWindowingSystemInterfaceProxy;
|
|
|
|
}; // namespace OsgViewer
|
|
|
|
|
|
// declare C entry point for static compilation.
|
|
extern "C" void graphicswindow_Win32(void)
|
|
{
|
|
osg::GraphicsContext::setWindowingSystemInterface(osgViewer::Win32WindowingSystem::getInterface());
|
|
}
|