X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a8d20c57aec929ec861194892784143fe2e74125..e28cc3ca0aa9bb1c7e9613c447ca94b13a8bc858:/src/msw/glcanvas.cpp diff --git a/src/msw/glcanvas.cpp b/src/msw/glcanvas.cpp index 0f24a7cff6..a16f03e7b8 100644 --- a/src/msw/glcanvas.cpp +++ b/src/msw/glcanvas.cpp @@ -1,509 +1,791 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: glcanvas.cpp -// Purpose: wxGLCanvas, for using OpenGL with wxWindows under MS Windows +// Name: src/msw/glcanvas.cpp +// Purpose: wxGLCanvas, for using OpenGL with wxWidgets under MS Windows // Author: Julian Smart // Modified by: // Created: 04/01/98 // RCS-ID: $Id$ // Copyright: (c) Julian Smart -// Licence: wxWindows licence +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ -#pragma implementation "glcanvas.h" -#endif +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- #include "wx/wxprec.h" #if defined(__BORLANDC__) -#pragma hdrstop + #pragma hdrstop #endif -#include - #if wxUSE_GLCANVAS #ifndef WX_PRECOMP -#include + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/app.h" #endif -#include -#include -#include - -#include +#include "wx/msw/private.h" -static const wxChar *wxGLCanvasClassName = wxT("wxGLCanvasClass"); -static const wxChar *wxGLCanvasClassNameNoRedraw = wxT("wxGLCanvasClassNR"); +#include "wx/glcanvas.h" +// from src/msw/window.cpp LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); -/* - * GLContext implementation - */ - -wxGLContext::wxGLContext(bool isRGB, wxGLCanvas *win, const wxPalette& palette) -{ - m_window = win; +#ifdef GL_EXT_vertex_array + #define WXUNUSED_WITHOUT_GL_EXT_vertex_array(name) name +#else + #define WXUNUSED_WITHOUT_GL_EXT_vertex_array(name) WXUNUSED(name) +#endif - m_hDC = win->GetHDC(); +// ---------------------------------------------------------------------------- +// define possibly missing WGL constants +// ---------------------------------------------------------------------------- + +#ifndef WGL_ARB_pixel_format +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NUMBER_OVERLAYS_ARB 0x2008 +#define WGL_NUMBER_UNDERLAYS_ARB 0x2009 +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_STEREO_ARB 0x2012 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_ALPHA_BITS_ARB 0x201B +#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_FULL_ACCELERATION_ARB 0x2027 +#endif - m_glContext = wglCreateContext((HDC) m_hDC); - wxCHECK_RET( m_glContext, wxT("Couldn't create OpenGl context") ); +#ifndef WGL_ARB_multisample +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 +#endif - wglMakeCurrent((HDC) m_hDC, m_glContext); -} +// ---------------------------------------------------------------------------- +// libraries +// ---------------------------------------------------------------------------- -wxGLContext::wxGLContext( - bool isRGB, wxGLCanvas *win, - const wxPalette& palette, - const wxGLContext *other /* for sharing display lists */ - ) -{ - m_window = win; +/* + The following two compiler directives are specific to the Microsoft Visual + C++ family of compilers + + Fundementally what they do is instruct the linker to use these two libraries + for the resolution of symbols. In essence, this is the equivalent of adding + these two libraries to either the Makefile or project file. + + This is NOT a recommended technique, and certainly is unlikely to be used + anywhere else in wxWidgets given it is so specific to not only wxMSW, but + also the VC compiler. However, in the case of opengl support, it's an + applicable technique as opengl is optional in setup.h This code (wrapped by + wxUSE_GLCANVAS), now allows opengl support to be added purely by modifying + setup.h rather than by having to modify either the project or DSP fle. + + See MSDN for further information on the exact usage of these commands. +*/ +#ifdef _MSC_VER +# pragma comment( lib, "opengl32" ) +# pragma comment( lib, "glu32" ) +#endif - m_hDC = win->GetHDC(); +// ---------------------------------------------------------------------------- +// wxGLContext +// ---------------------------------------------------------------------------- - m_glContext = wglCreateContext((HDC) m_hDC); - wxCHECK_RET( m_glContext, wxT("Couldn't create OpenGl context") ); +IMPLEMENT_CLASS(wxGLContext, wxObject) - if( other != 0 ) - wglShareLists( other->m_glContext, m_glContext ); +wxGLContext::wxGLContext(wxGLCanvas *win, const wxGLContext* other) +{ + m_glContext = wglCreateContext(win->GetHDC()); + wxCHECK_RET( m_glContext, wxT("Couldn't create OpenGL context") ); - wglMakeCurrent((HDC) m_hDC, m_glContext); + if ( other ) + { + if ( !wglShareLists(other->m_glContext, m_glContext) ) + { + wxLogLastError(wxT("wglShareLists")); + } + } } wxGLContext::~wxGLContext() { - if (m_glContext) - { - wglMakeCurrent(NULL, NULL); + // note that it's ok to delete the context even if it's the current one wglDeleteContext(m_glContext); - } } -void wxGLContext::SwapBuffers() +bool wxGLContext::SetCurrent(const wxGLCanvas& win) const { - if (m_glContext) - { - wglMakeCurrent((HDC) m_hDC, m_glContext); - ::SwapBuffers((HDC) m_hDC); //blits the backbuffer into DC - } -} - -void wxGLContext::SetCurrent() -{ - if (m_glContext) - { - wglMakeCurrent((HDC) m_hDC, m_glContext); - } - - /* - setupPixelFormat(hDC); - setupPalette(hDC); - */ -} - -void wxGLContext::SetColour(const char *colour) -{ - float r = 0.0; - float g = 0.0; - float b = 0.0; - wxColour *col = wxTheColourDatabase->FindColour(colour); - if (col) - { - r = (float)(col->Red()/256.0); - g = (float)(col->Green()/256.0); - b = (float)(col->Blue()/256.0); - glColor3f( r, g, b); - } + if ( !wglMakeCurrent(win.GetHDC(), m_glContext) ) + { + wxLogLastError(wxT("wglMakeCurrent")); + return false; + } + return true; } +// ============================================================================ +// wxGLCanvas +// ============================================================================ -/* - * wxGLCanvas implementation - */ - -IMPLEMENT_CLASS(wxGLCanvas, wxScrolledWindow) +IMPLEMENT_CLASS(wxGLCanvas, wxWindow) -BEGIN_EVENT_TABLE(wxGLCanvas, wxScrolledWindow) - EVT_SIZE(wxGLCanvas::OnSize) +BEGIN_EVENT_TABLE(wxGLCanvas, wxWindow) +#if wxUSE_PALETTE EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged) EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette) +#endif END_EVENT_TABLE() -wxGLCanvas::wxGLCanvas(wxWindow *parent, wxWindowID id, - const wxPoint& pos, const wxSize& size, long style, const wxString& name, - int *attribList, const wxPalette& palette) : wxScrolledWindow() -{ - m_glContext = (wxGLContext*) NULL; - - bool ret = Create(parent, id, pos, size, style, name); +// ---------------------------------------------------------------------------- +// wxGLCanvas construction +// ---------------------------------------------------------------------------- - if ( ret ) - { - SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE)); - SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT)); - } +static int ChoosePixelFormatARB(HDC hdc, const int *attribList); - m_hDC = (WXHDC) ::GetDC((HWND) GetHWND()); - - SetupPixelFormat(attribList); - SetupPalette(palette); - - m_glContext = new wxGLContext(TRUE, this, palette); +void wxGLCanvas::Init() +{ +#if WXWIN_COMPATIBILITY_2_8 + m_glContext = NULL; +#endif + m_hDC = NULL; } -wxGLCanvas::wxGLCanvas( wxWindow *parent, - const wxGLContext *shared, wxWindowID id, - const wxPoint& pos, const wxSize& size, long style, const wxString& name, - int *attribList, const wxPalette& palette ) - : wxScrolledWindow() +wxGLCanvas::wxGLCanvas(wxWindow *parent, + wxWindowID id, + const int *attribList, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name, + const wxPalette& palette) { - m_glContext = (wxGLContext*) NULL; - - bool ret = Create(parent, id, pos, size, style, name); - - if ( ret ) - { - SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE)); - SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT)); - } - - m_hDC = (WXHDC) ::GetDC((HWND) GetHWND()); - - SetupPixelFormat(attribList); - SetupPalette(palette); + Init(); - m_glContext = new wxGLContext(TRUE, this, palette, shared ); + (void)Create(parent, id, pos, size, style, name, attribList, palette); } -// Not very useful for wxMSW, but this is to be wxGTK compliant +wxGLCanvas::~wxGLCanvas() +{ + ::ReleaseDC(GetHwnd(), m_hDC); +} -wxGLCanvas::wxGLCanvas( wxWindow *parent, const wxGLCanvas *shared, wxWindowID id, - const wxPoint& pos, const wxSize& size, long style, const wxString& name, - int *attribList, const wxPalette& palette ): - wxScrolledWindow() +// Replaces wxWindow::Create functionality, since we need to use a different +// window class +bool wxGLCanvas::CreateWindow(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name) { - m_glContext = (wxGLContext*) NULL; + wxCHECK_MSG( parent, false, wxT("can't create wxWindow without parent") ); - bool ret = Create(parent, id, pos, size, style, name); + if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) ) + return false; - if ( ret ) - { - SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE)); - SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT)); - } + parent->AddChild(this); - m_hDC = (WXHDC) ::GetDC((HWND) GetHWND()); + /* + A general rule with OpenGL and Win32 is that any window that will have a + HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS. + You can find references about this within the knowledge base and most OpenGL + books that contain the wgl function descriptions. + */ + WXDWORD exStyle = 0; + DWORD msflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + msflags |= MSWGetStyle(style, &exStyle); - SetupPixelFormat(attribList); - SetupPalette(palette); + if ( !MSWCreate(wxApp::GetRegisteredClassName(wxT("wxGLCanvas"), -1, CS_OWNDC), + NULL, pos, size, msflags, exStyle) ) + return false; - wxGLContext *sharedContext=0; - if (shared) sharedContext=shared->GetContext(); - m_glContext = new wxGLContext(TRUE, this, palette, sharedContext ); -} - -wxGLCanvas::~wxGLCanvas() -{ - if (m_glContext) - delete m_glContext; + m_hDC = ::GetDC(GetHwnd()); + if ( !m_hDC ) + return false; - ::ReleaseDC((HWND) GetHWND(), (HDC) m_hDC); + return true; } -// Replaces wxWindow::Create functionality, since we need to use a different -// window class bool wxGLCanvas::Create(wxWindow *parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, - const wxString& name) + const wxString& name, + const int *attribList, + const wxPalette& palette) { - static bool s_registeredGLCanvasClass = FALSE; + // Create the window first: we will either use it as is or use it to query + // for multisampling support and recreate it later with another pixel format + if ( !CreateWindow(parent, id, pos, size, style, name) ) + return false; - // We have to register a special window class because we need - // the CS_OWNDC style for GLCanvas. + PIXELFORMATDESCRIPTOR pfd; + const int setupVal = DoSetup(pfd, attribList); + if ( setupVal == 0 ) // PixelFormat error + return false; - /* - From Angel Popov + if ( setupVal == -1 ) // FSAA requested + { + // now that we have a valid OpenGL window, query it for FSAA support + int pixelFormat; + { + wxGLContext ctx(this); + ctx.SetCurrent(*this); + pixelFormat = ::ChoosePixelFormatARB(m_hDC, attribList); + } + + if ( pixelFormat > 0 ) + { + // from http://msdn.microsoft.com/en-us/library/ms537559(VS.85).aspx: + // + // Setting the pixel format of a window more than once can + // lead to significant complications for the Window Manager + // and for multithread applications, so it is not allowed. An + // application can only set the pixel format of a window one + // time. Once a window's pixel format is set, it cannot be + // changed. + // + // so we need to delete the old window and create the new one + + // destroy Window + ::ReleaseDC(GetHwnd(), m_hDC); + m_hDC = 0; + + parent->RemoveChild(this); + const HWND hwnd = GetHwnd(); + DissociateHandle(); // will do SetHWND(0); + ::DestroyWindow(hwnd); + + // now recreate with FSAA pixelFormat + if ( !CreateWindow(parent, id, pos, size, style, name) ) + return false; + + if ( !::SetPixelFormat(m_hDC, pixelFormat, &pfd) ) + { + wxLogLastError(wxT("SetPixelFormat")); + return false; + } + } + } - Here are two snips from a dicussion in the OpenGL Gamedev list that explains - how this problem can be fixed: +#if wxUSE_PALETTE + if ( !SetupPalette(palette) ) + return false; +#else // !wxUSE_PALETTE + wxUnusedVar(palette); +#endif // wxUSE_PALETTE/!wxUSE_PALETTE - "There are 5 common DCs available in Win95. These are aquired when you call - GetDC or GetDCEx from a window that does _not_ have the OWNDC flag. - OWNDC flagged windows do not get their DC from the common DC pool, the issue - is they require 800 bytes each from the limited 64Kb local heap for GDI." + return true; +} - "The deal is, if you hold onto one of the 5 shared DC's too long (as GL apps - do), Win95 will actually "steal" it from you. MakeCurrent fails, - apparently, because Windows re-assigns the HDC to a different window. The - only way to prevent this, the only reliable means, is to set CS_OWNDC." - */ +// ---------------------------------------------------------------------------- +// operations +// ---------------------------------------------------------------------------- - if (!s_registeredGLCanvasClass) - { - WNDCLASS wndclass; - - // the fields which are common to all classes - wndclass.lpfnWndProc = (WNDPROC)wxWndProc; - wndclass.cbClsExtra = 0; - wndclass.cbWndExtra = sizeof( DWORD ); // VZ: what is this DWORD used for? - wndclass.hInstance = wxhInstance; - wndclass.hIcon = (HICON) NULL; - wndclass.hCursor = ::LoadCursor((HINSTANCE)NULL, IDC_ARROW); - wndclass.lpszMenuName = NULL; - - // Register the GLCanvas class name - wndclass.hbrBackground = (HBRUSH)NULL; - wndclass.lpszClassName = wxGLCanvasClassName; - wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC; - - if ( !::RegisterClass(&wndclass) ) +bool wxGLCanvas::SwapBuffers() +{ + if ( !::SwapBuffers(m_hDC) ) { - wxLogLastError(wxT("RegisterClass(wxGLCanvasClass)")); - return FALSE; + wxLogLastError(wxT("SwapBuffers")); + return false; } - // Register the GLCanvas class name for windows which don't do full repaint - // on resize - wndclass.lpszClassName = wxGLCanvasClassNameNoRedraw; - wndclass.style &= ~(CS_HREDRAW | CS_VREDRAW); + return true; +} - if ( !::RegisterClass(&wndclass) ) - { - wxLogLastError(wxT("RegisterClass(wxGLCanvasClassNameNoRedraw)")); - ::UnregisterClass(wxGLCanvasClassName, wxhInstance); +// ---------------------------------------------------------------------------- +// multi sample support +// ---------------------------------------------------------------------------- + +// this macro defines a variable of type "name_t" called "name" and initializes +// it with the pointer to WGL function "name" (which may be NULL) +#define wxDEFINE_WGL_FUNC(name) \ + name##_t name = (name##_t)wglGetProcAddress(#name) - return FALSE; +/* static */ +bool wxGLCanvasBase::IsExtensionSupported(const char *extension) +{ + static const char *s_extensionsList = (char *)wxUIntPtr(-1); + if ( s_extensionsList == (char *)wxUIntPtr(-1) ) + { + typedef const char * (WINAPI *wglGetExtensionsStringARB_t)(HDC hdc); + + wxDEFINE_WGL_FUNC(wglGetExtensionsStringARB); + if ( wglGetExtensionsStringARB ) + { + s_extensionsList = wglGetExtensionsStringARB(wglGetCurrentDC()); + } + else + { + typedef const char * (WINAPI * wglGetExtensionsStringEXT_t)(); + + wxDEFINE_WGL_FUNC(wglGetExtensionsStringEXT); + if ( wglGetExtensionsStringEXT ) + { + s_extensionsList = wglGetExtensionsStringEXT(); + } + else + { + s_extensionsList = NULL; + } + } } - s_registeredGLCanvasClass = TRUE; - } + return s_extensionsList && IsExtensionInList(s_extensionsList, extension); +} - wxCHECK_MSG( parent, FALSE, wxT("can't create wxWindow without parent") ); +// this is a wrapper around wglChoosePixelFormatARB(): returns the pixel format +// index matching the given attributes on success or 0 on failure +static int ChoosePixelFormatARB(HDC hdc, const int *attribList) +{ + if ( !wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample") ) + return 0; + + typedef BOOL (WINAPI * wglChoosePixelFormatARB_t) + (HDC hdc, + const int *piAttribIList, + const FLOAT *pfAttribFList, + UINT nMaxFormats, + int *piFormats, + UINT *nNumFormats + ); - if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) ) - return FALSE; + wxDEFINE_WGL_FUNC(wglChoosePixelFormatARB); + if ( !wglChoosePixelFormatARB ) + return 0; // should not occur if extension is supported - parent->AddChild(this); + int iAttributes[128]; + int dst = 0; // index in iAttributes array - DWORD msflags = 0; - if ( style & wxBORDER ) - msflags |= WS_BORDER; - if ( style & wxTHICK_FRAME ) - msflags |= WS_THICKFRAME; + #define ADD_ATTR(attr, value) \ + iAttributes[dst++] = attr; iAttributes[dst++] = value - /* - A general rule with OpenGL and Win32 is that any window that will have a - HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS. - You can find references about this within the knowledge base and most OpenGL - books that contain the wgl function descriptions. - */ + ADD_ATTR( WGL_DRAW_TO_WINDOW_ARB, GL_TRUE ); + ADD_ATTR( WGL_SUPPORT_OPENGL_ARB, GL_TRUE ); + ADD_ATTR( WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB ); - msflags |= WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + if ( !attribList ) + { + ADD_ATTR( WGL_COLOR_BITS_ARB, 24 ); + ADD_ATTR( WGL_ALPHA_BITS_ARB, 8 ); + ADD_ATTR( WGL_DEPTH_BITS_ARB, 16 ); + ADD_ATTR( WGL_STENCIL_BITS_ARB, 0 ); + ADD_ATTR( WGL_DOUBLE_BUFFER_ARB, GL_TRUE ); + ADD_ATTR( WGL_SAMPLE_BUFFERS_ARB, GL_TRUE ); + ADD_ATTR( WGL_SAMPLES_ARB, 4 ); + } + else // have custom attributes + { + #define ADD_ATTR_VALUE(attr) ADD_ATTR(attr, attribList[src++]) + + int src = 0; + while ( attribList[src] ) + { + switch ( attribList[src++] ) + { + case WX_GL_RGBA: + ADD_ATTR( WGL_COLOR_BITS_ARB, 24 ); + ADD_ATTR( WGL_ALPHA_BITS_ARB, 8 ); + break; + + case WX_GL_BUFFER_SIZE: + ADD_ATTR_VALUE( WGL_COLOR_BITS_ARB); + break; + + case WX_GL_LEVEL: + if ( attribList[src] > 0 ) + { + ADD_ATTR( WGL_NUMBER_OVERLAYS_ARB, 1 ); + } + else if ( attribList[src] <0 ) + { + ADD_ATTR( WGL_NUMBER_UNDERLAYS_ARB, 1 ); + } + //else: ignore it + + src++; // skip the value in any case + break; + + case WX_GL_DOUBLEBUFFER: + ADD_ATTR( WGL_DOUBLE_BUFFER_ARB, GL_TRUE ); + break; + + case WX_GL_STEREO: + ADD_ATTR( WGL_STEREO_ARB, GL_TRUE ); + break; + + case WX_GL_AUX_BUFFERS: + ADD_ATTR_VALUE( WGL_AUX_BUFFERS_ARB ); + break; + + case WX_GL_MIN_RED: + ADD_ATTR_VALUE( WGL_RED_BITS_ARB ); + break; + + case WX_GL_MIN_GREEN: + ADD_ATTR_VALUE( WGL_GREEN_BITS_ARB ); + break; + + case WX_GL_MIN_BLUE: + ADD_ATTR_VALUE( WGL_BLUE_BITS_ARB ); + break; + + case WX_GL_MIN_ALPHA: + ADD_ATTR_VALUE( WGL_ALPHA_BITS_ARB ); + break; + + case WX_GL_DEPTH_SIZE: + ADD_ATTR_VALUE( WGL_DEPTH_BITS_ARB ); + break; + + case WX_GL_STENCIL_SIZE: + ADD_ATTR_VALUE( WGL_STENCIL_BITS_ARB ); + break; + + case WX_GL_MIN_ACCUM_RED: + ADD_ATTR_VALUE( WGL_ACCUM_RED_BITS_ARB ); + break; + + case WX_GL_MIN_ACCUM_GREEN: + ADD_ATTR_VALUE( WGL_ACCUM_GREEN_BITS_ARB ); + break; + + case WX_GL_MIN_ACCUM_BLUE: + ADD_ATTR_VALUE( WGL_ACCUM_BLUE_BITS_ARB ); + break; + + case WX_GL_MIN_ACCUM_ALPHA: + ADD_ATTR_VALUE( WGL_ACCUM_ALPHA_BITS_ARB ); + break; + + case WX_GL_SAMPLE_BUFFERS: + ADD_ATTR_VALUE( WGL_SAMPLE_BUFFERS_ARB ); + break; + + case WX_GL_SAMPLES: + ADD_ATTR_VALUE( WGL_SAMPLES_ARB ); + break; + } + } + + #undef ADD_ATTR_VALUE + } - bool want3D; - WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D); + #undef ADD_ATTR - // Even with extended styles, need to combine with WS_BORDER - // for them to look right. - if ( want3D || (m_windowStyle & wxSIMPLE_BORDER) || (m_windowStyle & wxRAISED_BORDER ) || - (m_windowStyle & wxSUNKEN_BORDER) || (m_windowStyle & wxDOUBLE_BORDER)) - { - msflags |= WS_BORDER; - } + iAttributes[dst++] = 0; - // calculate the value to return from WM_GETDLGCODE handler - if ( GetWindowStyleFlag() & wxWANTS_CHARS ) - { - // want everything: i.e. all keys and WM_CHAR message - m_lDlgCode = DLGC_WANTARROWS | DLGC_WANTCHARS | - DLGC_WANTTAB | DLGC_WANTMESSAGE; - } + int pf; + UINT numFormats = 0; - return MSWCreate(wxGLCanvasClassName, NULL, pos, size, msflags, exStyle); + if ( !wglChoosePixelFormatARB(hdc, iAttributes, NULL, 1, &pf, &numFormats) ) + { + wxLogLastError(wxT("wglChoosePixelFormatARB")); + return 0; + } + + // Although TRUE is returned if no matching formats are found (see + // http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt), pf is + // not initialized in this case so we need to check for numFormats being + // not 0 explicitly (however this is not an error so don't call + // wxLogLastError() here). + if ( !numFormats ) + pf = 0; + + return pf; } -static void AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR& pfd, int *attribList) +// ---------------------------------------------------------------------------- +// pixel format stuff +// ---------------------------------------------------------------------------- + +// returns true if pfd was adjusted accordingly to attributes provided, false +// if there is an error with attributes or -1 if the attributes indicate +// features not supported by ChoosePixelFormat() at all (currently only multi +// sampling) +static int +AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR& pfd, const int *attribList) { - if (attribList) { + if ( !attribList ) + return 1; + + // remove default attributes pfd.dwFlags &= ~PFD_DOUBLEBUFFER; pfd.iPixelType = PFD_TYPE_COLORINDEX; - pfd.cColorBits = 0; - int arg=0; - while( (attribList[arg]!=0) ) + bool requestFSAA = false; + for ( int arg = 0; attribList[arg]; ) { - switch( attribList[arg++] ) - { - case WX_GL_RGBA: - pfd.iPixelType = PFD_TYPE_RGBA; - break; - case WX_GL_BUFFER_SIZE: - pfd.cColorBits = attribList[arg++]; - break; - case WX_GL_LEVEL: - // this member looks like it may be obsolete - if (attribList[arg] > 0) { - pfd.iLayerType = PFD_OVERLAY_PLANE; - } else if (attribList[arg] < 0) { - pfd.iLayerType = PFD_UNDERLAY_PLANE; - } else { - pfd.iLayerType = PFD_MAIN_PLANE; - } - arg++; - break; - case WX_GL_DOUBLEBUFFER: - pfd.dwFlags |= PFD_DOUBLEBUFFER; - break; - case WX_GL_STEREO: - pfd.dwFlags |= PFD_STEREO; - break; - case WX_GL_AUX_BUFFERS: - pfd.cAuxBuffers = attribList[arg++]; - break; - case WX_GL_MIN_RED: - pfd.cColorBits += (pfd.cRedBits = attribList[arg++]); - break; - case WX_GL_MIN_GREEN: - pfd.cColorBits += (pfd.cGreenBits = attribList[arg++]); - break; - case WX_GL_MIN_BLUE: - pfd.cColorBits += (pfd.cBlueBits = attribList[arg++]); - break; - case WX_GL_MIN_ALPHA: - // doesn't count in cColorBits - pfd.cAlphaBits = attribList[arg++]; - break; - case WX_GL_DEPTH_SIZE: - pfd.cDepthBits = attribList[arg++]; - break; - case WX_GL_STENCIL_SIZE: - pfd.cStencilBits = attribList[arg++]; - break; - case WX_GL_MIN_ACCUM_RED: - pfd.cAccumBits += (pfd.cAccumRedBits = attribList[arg++]); - break; - case WX_GL_MIN_ACCUM_GREEN: - pfd.cAccumBits += (pfd.cAccumGreenBits = attribList[arg++]); - break; - case WX_GL_MIN_ACCUM_BLUE: - pfd.cAccumBits += (pfd.cAccumBlueBits = attribList[arg++]); - break; - case WX_GL_MIN_ACCUM_ALPHA: - pfd.cAccumBits += (pfd.cAccumAlphaBits = attribList[arg++]); - break; - default: - break; - } + switch ( attribList[arg++] ) + { + case WX_GL_RGBA: + pfd.iPixelType = PFD_TYPE_RGBA; + break; + + case WX_GL_BUFFER_SIZE: + pfd.cColorBits = attribList[arg++]; + break; + + case WX_GL_LEVEL: + // this member looks like it may be obsolete + if ( attribList[arg] > 0 ) + pfd.iLayerType = PFD_OVERLAY_PLANE; + else if ( attribList[arg] < 0 ) + pfd.iLayerType = (BYTE)PFD_UNDERLAY_PLANE; + else + pfd.iLayerType = PFD_MAIN_PLANE; + arg++; + break; + + case WX_GL_DOUBLEBUFFER: + pfd.dwFlags |= PFD_DOUBLEBUFFER; + break; + + case WX_GL_STEREO: + pfd.dwFlags |= PFD_STEREO; + break; + + case WX_GL_AUX_BUFFERS: + pfd.cAuxBuffers = attribList[arg++]; + break; + + case WX_GL_MIN_RED: + pfd.cColorBits += (pfd.cRedBits = attribList[arg++]); + break; + + case WX_GL_MIN_GREEN: + pfd.cColorBits += (pfd.cGreenBits = attribList[arg++]); + break; + + case WX_GL_MIN_BLUE: + pfd.cColorBits += (pfd.cBlueBits = attribList[arg++]); + break; + + case WX_GL_MIN_ALPHA: + // doesn't count in cColorBits + pfd.cAlphaBits = attribList[arg++]; + break; + + case WX_GL_DEPTH_SIZE: + pfd.cDepthBits = attribList[arg++]; + break; + + case WX_GL_STENCIL_SIZE: + pfd.cStencilBits = attribList[arg++]; + break; + + case WX_GL_MIN_ACCUM_RED: + pfd.cAccumBits += (pfd.cAccumRedBits = attribList[arg++]); + break; + + case WX_GL_MIN_ACCUM_GREEN: + pfd.cAccumBits += (pfd.cAccumGreenBits = attribList[arg++]); + break; + + case WX_GL_MIN_ACCUM_BLUE: + pfd.cAccumBits += (pfd.cAccumBlueBits = attribList[arg++]); + break; + + case WX_GL_MIN_ACCUM_ALPHA: + pfd.cAccumBits += (pfd.cAccumAlphaBits = attribList[arg++]); + break; + + case WX_GL_SAMPLE_BUFFERS: + case WX_GL_SAMPLES: + // There is no support for multisample when using PIXELFORMATDESCRIPTOR + requestFSAA = true; // Remember that multi sample is requested. + arg++; // will call ChoosePixelFormatARB() later + break; + } } - } + + return requestFSAA ? -1 : 1; } -void wxGLCanvas::SetupPixelFormat(int *attribList) // (HDC hDC) +/* static */ +int +wxGLCanvas::ChooseMatchingPixelFormat(HDC hdc, + const int *attribList, + PIXELFORMATDESCRIPTOR *ppfd) { - int pixelFormat; - PIXELFORMATDESCRIPTOR pfd = { - sizeof(PIXELFORMATDESCRIPTOR), /* size */ - 1, /* version */ + // default neutral pixel format + PIXELFORMATDESCRIPTOR pfd = + { + sizeof(PIXELFORMATDESCRIPTOR), // size + 1, // version PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | - PFD_DOUBLEBUFFER, /* support double-buffering */ - PFD_TYPE_RGBA, /* color type */ - 16, /* prefered color depth */ - 0, 0, 0, 0, 0, 0, /* color bits (ignored) */ - 0, /* no alpha buffer */ - 0, /* alpha bits (ignored) */ - 0, /* no accumulation buffer */ - 0, 0, 0, 0, /* accum bits (ignored) */ - 16, /* depth buffer */ - 0, /* no stencil buffer */ - 0, /* no auxiliary buffers */ - PFD_MAIN_PLANE, /* main layer */ - 0, /* reserved */ - 0, 0, 0, /* no layer, visible, damage masks */ + PFD_DOUBLEBUFFER, // use double-buffering by default + PFD_TYPE_RGBA, // default pixel type + 0, // preferred color depth (don't care) + 0, 0, 0, 0, 0, 0, // color bits and shift bits (ignored) + 0, 0, // alpha bits and shift (ignored) + 0, // accumulation total bits + 0, 0, 0, 0, // accumulator RGBA bits (not used) + 16, // depth buffer + 0, // no stencil buffer + 0, // no auxiliary buffers + PFD_MAIN_PLANE, // main layer + 0, // reserved + 0, 0, 0, // no layer, visible, damage masks }; - AdjustPFDForAttributes(pfd, attribList); + if ( !ppfd ) + ppfd = &pfd; + else + *ppfd = pfd; - pixelFormat = ChoosePixelFormat((HDC) m_hDC, &pfd); - if (pixelFormat == 0) { - wxLogWarning(_("ChoosePixelFormat failed.")); - } - else { - if (SetPixelFormat((HDC) m_hDC, pixelFormat, &pfd) != TRUE) { - wxLogWarning(_("SetPixelFormat failed.")); + // adjust the PFD using the provided attributes and also check if we can + // use PIXELFORMATDESCRIPTOR at all: if multisampling is requested, we + // can't as it's not supported by ChoosePixelFormat() + switch ( AdjustPFDForAttributes(*ppfd, attribList) ) + { + case 1: + return ::ChoosePixelFormat(hdc, ppfd); + + default: + wxFAIL_MSG( "unexpected AdjustPFDForAttributes() return value" ); + // fall through + + case 0: + // error in attributes + return 0; + + case -1: + // requestFSAA == true, will continue as normal + // in order to query later for a FSAA pixelformat + return -1; } - } } -void wxGLCanvas::SetupPalette(const wxPalette& palette) +/* static */ +bool wxGLCanvasBase::IsDisplaySupported(const int *attribList) { - int pixelFormat = GetPixelFormat((HDC) m_hDC); - PIXELFORMATDESCRIPTOR pfd; + // We need a device context to test the pixel format, so get one + // for the root window. + return wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList) > 0; +} + +int wxGLCanvas::DoSetup(PIXELFORMATDESCRIPTOR &pfd, const int *attribList) +{ + int pixelFormat = ChooseMatchingPixelFormat(m_hDC, attribList, &pfd); - DescribePixelFormat((HDC) m_hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd); + const bool requestFSAA = pixelFormat == -1; + if ( requestFSAA ) + pixelFormat = ::ChoosePixelFormat(m_hDC, &pfd); - if (pfd.dwFlags & PFD_NEED_PALETTE) + if ( !pixelFormat ) { + wxLogLastError(wxT("ChoosePixelFormat")); + return 0; } - else + + if ( !::SetPixelFormat(m_hDC, pixelFormat, &pfd) ) { - return; + wxLogLastError(wxT("SetPixelFormat")); + return 0; } + return requestFSAA ? -1 : 1; +} + +// ---------------------------------------------------------------------------- +// palette stuff +// ---------------------------------------------------------------------------- + +#if wxUSE_PALETTE + +bool wxGLCanvas::SetupPalette(const wxPalette& palette) +{ + const int pixelFormat = ::GetPixelFormat(m_hDC); + if ( !pixelFormat ) + { + wxLogLastError(wxT("GetPixelFormat")); + return false; + } + + PIXELFORMATDESCRIPTOR pfd; + if ( !::DescribePixelFormat(m_hDC, pixelFormat, sizeof(pfd), &pfd) ) + { + wxLogLastError(wxT("DescribePixelFormat")); + return false; + } + + if ( !(pfd.dwFlags & PFD_NEED_PALETTE) ) + return true; + m_palette = palette; - if ( !m_palette.Ok() ) + if ( !m_palette.IsOk() ) { m_palette = CreateDefaultPalette(); + if ( !m_palette.IsOk() ) + return false; } - if (m_palette.Ok()) + if ( !::SelectPalette(m_hDC, GetHpaletteOf(m_palette), FALSE) ) { - SelectPalette((HDC) m_hDC, (HPALETTE) m_palette.GetHPALETTE(), FALSE); - RealizePalette((HDC) m_hDC); + wxLogLastError(wxT("SelectPalette")); + return false; } + + if ( ::RealizePalette(m_hDC) == GDI_ERROR ) + { + wxLogLastError(wxT("RealizePalette")); + return false; + } + + return true; } wxPalette wxGLCanvas::CreateDefaultPalette() { PIXELFORMATDESCRIPTOR pfd; int paletteSize; - int pixelFormat = GetPixelFormat((HDC) m_hDC); + int pixelFormat = GetPixelFormat(m_hDC); - DescribePixelFormat((HDC) m_hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd); + DescribePixelFormat(m_hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd); paletteSize = 1 << pfd.cColorBits; LOGPALETTE* pPal = (LOGPALETTE*) malloc(sizeof(LOGPALETTE) + paletteSize * sizeof(PALETTEENTRY)); pPal->palVersion = 0x300; - pPal->palNumEntries = paletteSize; + pPal->palNumEntries = (WORD)paletteSize; /* build a simple RGB color palette */ - { int redMask = (1 << pfd.cRedBits) - 1; int greenMask = (1 << pfd.cGreenBits) - 1; int blueMask = (1 << pfd.cBlueBits) - 1; - int i; - for (i=0; ipalPalEntry[i].peRed = - (((i >> pfd.cRedShift) & redMask) * 255) / redMask; + (BYTE)((((i >> pfd.cRedShift) & redMask) * 255) / redMask); pPal->palPalEntry[i].peGreen = - (((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask; + (BYTE)((((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask); pPal->palPalEntry[i].peBlue = - (((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask; + (BYTE)((((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask); pPal->palPalEntry[i].peFlags = 0; } - } HPALETTE hPalette = CreatePalette(pPal); free(pPal); @@ -514,251 +796,105 @@ wxPalette wxGLCanvas::CreateDefaultPalette() return palette; } -void wxGLCanvas::SwapBuffers() -{ - if (m_glContext) - m_glContext->SwapBuffers(); -} - -void wxGLCanvas::OnSize(wxSizeEvent& event) -{ - int width, height; - GetClientSize(& width, & height); - - if (m_glContext) - { - m_glContext->SetCurrent(); - - glViewport(0, 0, (GLint)width, (GLint)height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 15.0 ); - glMatrixMode(GL_MODELVIEW); - } -} - -void wxGLCanvas::SetCurrent() -{ - if (m_glContext) - { - m_glContext->SetCurrent(); - } -} - -void wxGLCanvas::SetColour(const char *colour) -{ - if (m_glContext) - m_glContext->SetColour(colour); -} - -// TODO: Have to have this called by parent frame (?) -// So we need wxFrame to call OnQueryNewPalette for all children... void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent& event) { /* realize palette if this is the current window */ - if ( GetPalette()->Ok() ) { + if ( GetPalette()->IsOk() ) { ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE()); - ::SelectPalette((HDC) GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE); - ::RealizePalette((HDC) GetHDC()); + ::SelectPalette(GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE); + ::RealizePalette(GetHDC()); Refresh(); - event.SetPaletteRealized(TRUE); + event.SetPaletteRealized(true); } else - event.SetPaletteRealized(FALSE); + event.SetPaletteRealized(false); } -// I think this doesn't have to be propagated to child windows. void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent& event) { /* realize palette if this is *not* the current window */ if ( GetPalette() && - GetPalette()->Ok() && (this != event.GetChangedWindow()) ) + GetPalette()->IsOk() && (this != event.GetChangedWindow()) ) { ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE()); - ::SelectPalette((HDC) GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE); - ::RealizePalette((HDC) GetHDC()); + ::SelectPalette(GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE); + ::RealizePalette(GetHDC()); Refresh(); } } -/* Give extensions proper function names. */ - -/* EXT_vertex_array */ -void glArrayElementEXT(GLint i) -{ -} - -void glColorPointerEXT(GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer) -{ -} - -void glDrawArraysEXT(GLenum mode, GLint first, GLsizei count) -{ -#ifdef GL_EXT_vertex_array - static PFNGLDRAWARRAYSEXTPROC proc = 0; - - if ( !proc ) - { - proc = (PFNGLDRAWARRAYSEXTPROC) wglGetProcAddress("glDrawArraysEXT"); - } - - if ( proc ) - (* proc) (mode, first, count); -#endif -} - -void glEdgeFlagPointerEXT(GLsizei stride, GLsizei count, const GLboolean *pointer) -{ -} - -void glGetPointervEXT(GLenum pname, GLvoid* *params) -{ -} - -void glIndexPointerEXT(GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer) -{ -} - -void glNormalPointerEXT(GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer) -{ -#ifdef GL_EXT_vertex_array - static PFNGLNORMALPOINTEREXTPROC proc = 0; - - if ( !proc ) - { - proc = (PFNGLNORMALPOINTEREXTPROC) wglGetProcAddress("glNormalPointerEXT"); - } - - if ( proc ) - (* proc) (type, stride, count, pointer); -#endif -} - -void glTexCoordPointerEXT(GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer) -{ -} - -void glVertexPointerEXT(GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer) -{ -#ifdef GL_EXT_vertex_array - static PFNGLVERTEXPOINTEREXTPROC proc = 0; - - if ( !proc ) - { - proc = (PFNGLVERTEXPOINTEREXTPROC) wglGetProcAddress("glVertexPointerEXT"); - } - if ( proc ) - (* proc) (size, type, stride, count, pointer); -#endif -} - -/* EXT_color_subtable */ -void glColorSubtableEXT(GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *table) -{ -} - -/* EXT_color_table */ -void glColorTableEXT(GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table) -{ -} - -void glCopyColorTableEXT(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width) -{ -} - -void glGetColorTableEXT(GLenum target, GLenum format, GLenum type, GLvoid *table) -{ -} +#endif // wxUSE_PALETTE -void glGetColorTableParamaterfvEXT(GLenum target, GLenum pname, GLfloat *params) -{ -} +// ---------------------------------------------------------------------------- +// deprecated wxGLCanvas methods using implicit wxGLContext +// ---------------------------------------------------------------------------- -void glGetColorTavleParameterivEXT(GLenum target, GLenum pname, GLint *params) -{ -} +// deprecated constructors creating an implicit m_glContext +#if WXWIN_COMPATIBILITY_2_8 -/* SGI_compiled_vertex_array */ -void glLockArraysSGI(GLint first, GLsizei count) +wxGLCanvas::wxGLCanvas(wxWindow *parent, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name, + const int *attribList, + const wxPalette& palette) { -} + Init(); -void glUnlockArraysSGI() -{ + if ( Create(parent, id, pos, size, style, name, attribList, palette) ) + m_glContext = new wxGLContext(this); } - -/* SGI_cull_vertex */ -void glCullParameterdvSGI(GLenum pname, GLdouble* params) +wxGLCanvas::wxGLCanvas(wxWindow *parent, + const wxGLContext *shared, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name, + const int *attribList, + const wxPalette& palette) { -} + Init(); -void glCullParameterfvSGI(GLenum pname, GLfloat* params) -{ + if ( Create(parent, id, pos, size, style, name, attribList, palette) ) + m_glContext = new wxGLContext(this, shared); } -/* SGI_index_func */ -void glIndexFuncSGI(GLenum func, GLclampf ref) +wxGLCanvas::wxGLCanvas(wxWindow *parent, + const wxGLCanvas *shared, + wxWindowID id, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name, + const int *attribList, + const wxPalette& palette) { -} + Init(); -/* SGI_index_material */ -void glIndexMaterialSGI(GLenum face, GLenum mode) -{ + if ( Create(parent, id, pos, size, style, name, attribList, palette) ) + m_glContext = new wxGLContext(this, shared ? shared->m_glContext : NULL); } -/* WIN_swap_hint */ -void glAddSwapHintRectWin(GLint x, GLint y, GLsizei width, GLsizei height) -{ -} +#endif // WXWIN_COMPATIBILITY_2_8 -//--------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- // wxGLApp -//--------------------------------------------------------------------------- +// ---------------------------------------------------------------------------- -IMPLEMENT_CLASS(wxGLApp, wxApp) - -bool wxGLApp::InitGLVisual(int *attribList) +bool wxGLApp::InitGLVisual(const int *attribList) { - int pixelFormat; - PIXELFORMATDESCRIPTOR pfd = { - sizeof(PIXELFORMATDESCRIPTOR), /* size */ - 1, /* version */ - PFD_SUPPORT_OPENGL | - PFD_DRAW_TO_WINDOW | - PFD_DOUBLEBUFFER, /* support double-buffering */ - PFD_TYPE_RGBA, /* color type */ - 16, /* prefered color depth */ - 0, 0, 0, 0, 0, 0, /* color bits (ignored) */ - 0, /* no alpha buffer */ - 0, /* alpha bits (ignored) */ - 0, /* no accumulation buffer */ - 0, 0, 0, 0, /* accum bits (ignored) */ - 16, /* depth buffer */ - 0, /* no stencil buffer */ - 0, /* no auxiliary buffers */ - PFD_MAIN_PLANE, /* main layer */ - 0, /* reserved */ - 0, 0, 0, /* no layer, visible, damage masks */ - }; - - AdjustPFDForAttributes(pfd, attribList); - - // use DC for whole (root) screen, since no windows have yet been created - pixelFormat = ChoosePixelFormat((HDC) ::GetDC(NULL), &pfd); - - if (pixelFormat == 0) { - wxLogError(_("Failed to initialize OpenGL")); - return FALSE; - } - - return TRUE; -} + if ( !wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList) ) + { + wxLogError(_("Failed to initialize OpenGL")); + return false; + } -wxGLApp::~wxGLApp() -{ + return true; } -#endif - // wxUSE_GLCANVAS +#endif // wxUSE_GLCANVAS