X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/2341cf5f5955a55d80aebca877a14098803af6a3..eea4d01c65f9b29baa1193db762b4c6b8144af24:/src/msw/glcanvas.cpp diff --git a/src/msw/glcanvas.cpp b/src/msw/glcanvas.cpp index feb72526b1..a16f03e7b8 100644 --- a/src/msw/glcanvas.cpp +++ b/src/msw/glcanvas.cpp @@ -1,6 +1,6 @@ ///////////////////////////////////////////////////////////////////////////// // Name: src/msw/glcanvas.cpp -// Purpose: wxGLCanvas, for using OpenGL with wxWindows under MS Windows +// Purpose: wxGLCanvas, for using OpenGL with wxWidgets under MS Windows // Author: Julian Smart // Modified by: // Created: 04/01/98 @@ -9,21 +9,23 @@ // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) -#pragma implementation "glcanvas.h" -#endif +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- #include "wx/wxprec.h" #if defined(__BORLANDC__) -#pragma hdrstop + #pragma hdrstop #endif #if wxUSE_GLCANVAS #ifndef WX_PRECOMP - #include "wx/frame.h" - #include "wx/settings.h" #include "wx/intl.h" #include "wx/log.h" #include "wx/app.h" @@ -31,12 +33,54 @@ #include "wx/msw/private.h" -// DLL options compatibility check: -#include "wx/build.h" -WX_CHECK_BUILD_OPTIONS("wxGL") - #include "wx/glcanvas.h" +// from src/msw/window.cpp +LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, + WPARAM wParam, LPARAM lParam); + +#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 + +// ---------------------------------------------------------------------------- +// 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 + +#ifndef WGL_ARB_multisample +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 +#endif + +// ---------------------------------------------------------------------------- +// libraries +// ---------------------------------------------------------------------------- + /* The following two compiler directives are specific to the Microsoft Visual C++ family of compilers @@ -46,7 +90,7 @@ WX_CHECK_BUILD_OPTIONS("wxGL") 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 wxWindows given it is so specific to not only wxMSW, but + 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 @@ -59,456 +103,689 @@ WX_CHECK_BUILD_OPTIONS("wxGL") # pragma comment( lib, "glu32" ) #endif +// ---------------------------------------------------------------------------- +// wxGLContext +// ---------------------------------------------------------------------------- -static const wxChar *wxGLCanvasClassName = wxT("wxGLCanvasClass"); -static const wxChar *wxGLCanvasClassNameNoRedraw = wxT("wxGLCanvasClassNR"); - -LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message, - WPARAM wParam, LPARAM lParam); - -/* - * GLContext implementation - */ +IMPLEMENT_CLASS(wxGLContext, wxObject) -wxGLContext::wxGLContext(bool WXUNUSED(isRGB), wxGLCanvas *win, const wxPalette& WXUNUSED(palette)) +wxGLContext::wxGLContext(wxGLCanvas *win, const wxGLContext* other) { - m_window = win; - - m_hDC = win->GetHDC(); - - m_glContext = wglCreateContext((HDC) m_hDC); - wxCHECK_RET( m_glContext, wxT("Couldn't create OpenGl context") ); - - wglMakeCurrent((HDC) m_hDC, m_glContext); -} + m_glContext = wglCreateContext(win->GetHDC()); + wxCHECK_RET( m_glContext, wxT("Couldn't create OpenGL context") ); -wxGLContext::wxGLContext( - bool WXUNUSED(isRGB), wxGLCanvas *win, - const wxPalette& WXUNUSED(palette), - const wxGLContext *other /* for sharing display lists */ - ) -{ - m_window = win; - - m_hDC = win->GetHDC(); - - m_glContext = wglCreateContext((HDC) m_hDC); - wxCHECK_RET( m_glContext, wxT("Couldn't create OpenGl context") ); - - if( other != 0 ) - wglShareLists( other->m_glContext, m_glContext ); - - 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() -{ - if (m_glContext) - { - wglMakeCurrent((HDC) m_hDC, m_glContext); - ::SwapBuffers((HDC) m_hDC); //blits the backbuffer into DC - } } -void wxGLContext::SetCurrent() +bool wxGLContext::SetCurrent(const wxGLCanvas& win) const { - if (m_glContext) - { - wglMakeCurrent((HDC) m_hDC, m_glContext); - } - - /* - setupPixelFormat(hDC); - setupPalette(hDC); - */ -} - -void wxGLContext::SetColour(const wxChar *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 implementation - */ +// ============================================================================ +// wxGLCanvas +// ============================================================================ IMPLEMENT_CLASS(wxGLCanvas, wxWindow) BEGIN_EVENT_TABLE(wxGLCanvas, wxWindow) - EVT_SIZE(wxGLCanvas::OnSize) +#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) : wxWindow() -{ - m_glContext = (wxGLContext*) NULL; +// ---------------------------------------------------------------------------- +// wxGLCanvas construction +// ---------------------------------------------------------------------------- - bool ret = Create(parent, id, pos, size, style, name); +static int ChoosePixelFormatARB(HDC hdc, const int *attribList); - if ( ret ) - { - SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)); - SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); - } - - 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 ) - : wxWindow() +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::GetColour(wxSYS_COLOUR_3DFACE)); - SetFont(wxSystemSettings::GetFont(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( wxWindow *parent, const wxGLCanvas *shared, wxWindowID id, - const wxPoint& pos, const wxSize& size, long style, const wxString& name, - int *attribList, const wxPalette& palette ): - wxWindow() +wxGLCanvas::~wxGLCanvas() { - m_glContext = (wxGLContext*) NULL; + ::ReleaseDC(GetHwnd(), m_hDC); +} - bool ret = Create(parent, id, pos, size, style, name); +// 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) +{ + wxCHECK_MSG( parent, false, wxT("can't create wxWindow without parent") ); - if ( ret ) - { - SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE)); - SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); - } + if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) ) + return false; - m_hDC = (WXHDC) ::GetDC((HWND) GetHWND()); + parent->AddChild(this); - SetupPixelFormat(attribList); - SetupPalette(palette); + /* + 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); - wxGLContext *sharedContext=0; - if (shared) sharedContext=shared->GetContext(); - m_glContext = new wxGLContext(TRUE, this, palette, sharedContext ); -} + if ( !MSWCreate(wxApp::GetRegisteredClassName(wxT("wxGLCanvas"), -1, CS_OWNDC), + NULL, pos, size, msflags, exStyle) ) + return false; -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 +// ---------------------------------------------------------------------------- - return FALSE; +// 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) + +/* 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 + ); + + wxDEFINE_WGL_FUNC(wglChoosePixelFormatARB); + if ( !wglChoosePixelFormatARB ) + return 0; // should not occur if extension is supported + + int iAttributes[128]; + int dst = 0; // index in iAttributes array - if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) ) - return FALSE; + #define ADD_ATTR(attr, value) \ + iAttributes[dst++] = attr; iAttributes[dst++] = value - parent->AddChild(this); + 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 ); - DWORD msflags = 0; + 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 + } - /* - 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. - */ + #undef ADD_ATTR - WXDWORD exStyle = 0; - msflags |= WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN; - msflags |= MSWGetStyle(style, & exStyle) ; + iAttributes[dst++] = 0; - return MSWCreate(wxGLCanvasClassName, NULL, pos, size, msflags, exStyle); + int pf; + UINT numFormats = 0; + + 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 = (BYTE)PFD_OVERLAY_PLANE; - } else if (attribList[arg] < 0) { - pfd.iLayerType = (BYTE)PFD_UNDERLAY_PLANE; - } else { - pfd.iLayerType = (BYTE)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) { - 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; - int pixelFormat = ChoosePixelFormat((HDC) m_hDC, &pfd); - if (pixelFormat == 0) { - wxLogLastError(_T("ChoosePixelFormat")); - } - else { - if ( !::SetPixelFormat((HDC) m_hDC, pixelFormat, &pfd) ) { - wxLogLastError(_T("SetPixelFormat")); + // 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) ) + { + 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) ) { - return; + 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 ( !::SelectPalette(m_hDC, GetHpaletteOf(m_palette), FALSE) ) + { + wxLogLastError(wxT("SelectPalette")); + return false; } - if (m_palette.Ok()) + if ( ::RealizePalette(m_hDC) == GDI_ERROR ) { - SelectPalette((HDC) m_hDC, (HPALETTE) m_palette.GetHPALETTE(), FALSE); - RealizePalette((HDC) m_hDC); + 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); @@ -519,238 +796,105 @@ wxPalette wxGLCanvas::CreateDefaultPalette() return palette; } -void wxGLCanvas::SwapBuffers() -{ - if (m_glContext) - m_glContext->SwapBuffers(); -} - -void wxGLCanvas::OnSize(wxSizeEvent& WXUNUSED(event)) -{ -} - -void wxGLCanvas::SetCurrent() -{ - if (m_glContext) - { - m_glContext->SetCurrent(); - } -} - -void wxGLCanvas::SetColour(const wxChar *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 WXUNUSED(i)) -{ -} - -void glColorPointerEXT(GLint WXUNUSED(size), GLenum WXUNUSED(type), GLsizei WXUNUSED(stride), GLsizei WXUNUSED(count), const GLvoid *WXUNUSED(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 WXUNUSED(stride), GLsizei WXUNUSED(count), const GLboolean *WXUNUSED(pointer)) -{ -} - -void glGetPointervEXT(GLenum WXUNUSED(pname), GLvoid* *WXUNUSED(params)) -{ -} - -void glIndexPointerEXT(GLenum WXUNUSED(type), GLsizei WXUNUSED(stride), GLsizei WXUNUSED(count), const GLvoid *WXUNUSED(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 WXUNUSED(size), GLenum WXUNUSED(type), GLsizei WXUNUSED(stride), GLsizei WXUNUSED(count), const GLvoid *WXUNUSED(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 WXUNUSED(target), GLsizei WXUNUSED(start), GLsizei WXUNUSED(count), GLenum WXUNUSED(format), GLenum WXUNUSED(type), const GLvoid *WXUNUSED(table)) -{ -} - -/* EXT_color_table */ -void glColorTableEXT(GLenum WXUNUSED(target), GLenum WXUNUSED(internalformat), GLsizei WXUNUSED(width), GLenum WXUNUSED(format), GLenum WXUNUSED(type), const GLvoid *WXUNUSED(table)) -{ -} - -void glCopyColorTableEXT(GLenum WXUNUSED(target), GLenum WXUNUSED(internalformat), GLint WXUNUSED(x), GLint WXUNUSED(y), GLsizei WXUNUSED(width)) -{ -} - -void glGetColorTableEXT(GLenum WXUNUSED(target), GLenum WXUNUSED(format), GLenum WXUNUSED(type), GLvoid *WXUNUSED(table)) -{ -} +#endif // wxUSE_PALETTE -void glGetColorTableParamaterfvEXT(GLenum WXUNUSED(target), GLenum WXUNUSED(pname), GLfloat *WXUNUSED(params)) -{ -} +// ---------------------------------------------------------------------------- +// deprecated wxGLCanvas methods using implicit wxGLContext +// ---------------------------------------------------------------------------- -void glGetColorTavleParameterivEXT(GLenum WXUNUSED(target), GLenum WXUNUSED(pname), GLint *WXUNUSED(params)) -{ -} +// deprecated constructors creating an implicit m_glContext +#if WXWIN_COMPATIBILITY_2_8 -/* SGI_compiled_vertex_array */ -void glLockArraysSGI(GLint WXUNUSED(first), GLsizei WXUNUSED(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 WXUNUSED(pname), GLdouble* WXUNUSED(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 WXUNUSED(pname), GLfloat* WXUNUSED(params)) -{ + if ( Create(parent, id, pos, size, style, name, attribList, palette) ) + m_glContext = new wxGLContext(this, shared); } -/* SGI_index_func */ -void glIndexFuncSGI(GLenum WXUNUSED(func), GLclampf WXUNUSED(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 WXUNUSED(face), GLenum WXUNUSED(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 WXUNUSED(x), GLint WXUNUSED(y), GLsizei WXUNUSED(width), GLsizei WXUNUSED(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(ScreenHDC(), &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