1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/glcanvas.cpp
3 // Purpose: wxGLCanvas, for using OpenGL with wxWidgets under MS Windows
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
22 #if defined(__BORLANDC__)
32 #include "wx/module.h"
35 #include "wx/msw/private.h"
37 #include "wx/glcanvas.h"
39 // from src/msw/window.cpp
40 LRESULT WXDLLEXPORT APIENTRY _EXPORT
wxWndProc(HWND hWnd
, UINT message
,
41 WPARAM wParam
, LPARAM lParam
);
43 #ifdef GL_EXT_vertex_array
44 #define WXUNUSED_WITHOUT_GL_EXT_vertex_array(name) name
46 #define WXUNUSED_WITHOUT_GL_EXT_vertex_array(name) WXUNUSED(name)
49 // ----------------------------------------------------------------------------
51 // ----------------------------------------------------------------------------
54 The following two compiler directives are specific to the Microsoft Visual
55 C++ family of compilers
57 Fundementally what they do is instruct the linker to use these two libraries
58 for the resolution of symbols. In essence, this is the equivalent of adding
59 these two libraries to either the Makefile or project file.
61 This is NOT a recommended technique, and certainly is unlikely to be used
62 anywhere else in wxWidgets given it is so specific to not only wxMSW, but
63 also the VC compiler. However, in the case of opengl support, it's an
64 applicable technique as opengl is optional in setup.h This code (wrapped by
65 wxUSE_GLCANVAS), now allows opengl support to be added purely by modifying
66 setup.h rather than by having to modify either the project or DSP fle.
68 See MSDN for further information on the exact usage of these commands.
71 # pragma comment( lib, "opengl32" )
72 # pragma comment( lib, "glu32" )
75 // ----------------------------------------------------------------------------
77 // ----------------------------------------------------------------------------
79 static const wxChar
*wxGLCanvasClassName
= wxT("wxGLCanvasClass");
80 static const wxChar
*wxGLCanvasClassNameNoRedraw
= wxT("wxGLCanvasClassNR");
82 // ============================================================================
84 // ============================================================================
86 // ----------------------------------------------------------------------------
87 // wxGLModule is responsible for unregistering wxGLCanvasClass Windows class
88 // ----------------------------------------------------------------------------
90 class wxGLModule
: public wxModule
93 bool OnInit() { return true; }
94 void OnExit() { UnregisterClasses(); }
96 // register the GL classes if not done yet, return true if ok, false if
97 // registration failed
98 static bool RegisterClasses();
100 // unregister the classes, done automatically on program termination
101 static void UnregisterClasses();
104 // wxGLCanvas is only used from the main thread so this is MT-ok
105 static bool ms_registeredGLClasses
;
107 DECLARE_DYNAMIC_CLASS(wxGLModule
)
110 IMPLEMENT_DYNAMIC_CLASS(wxGLModule
, wxModule
)
112 bool wxGLModule::ms_registeredGLClasses
= false;
115 bool wxGLModule::RegisterClasses()
117 if ( ms_registeredGLClasses
)
120 // We have to register a special window class because we need the CS_OWNDC
121 // style for GLCanvas: some OpenGL drivers are buggy and don't work with
122 // windows without this style
125 // the fields which are common to all classes
126 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
127 wndclass
.cbClsExtra
= 0;
128 wndclass
.cbWndExtra
= sizeof( DWORD
); // VZ: what is this DWORD used for?
129 wndclass
.hInstance
= wxhInstance
;
130 wndclass
.hIcon
= (HICON
) NULL
;
131 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
132 wndclass
.lpszMenuName
= NULL
;
134 // Register the GLCanvas class name
135 wndclass
.hbrBackground
= (HBRUSH
)NULL
;
136 wndclass
.lpszClassName
= wxGLCanvasClassName
;
137 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| CS_OWNDC
;
139 if ( !::RegisterClass(&wndclass
) )
141 wxLogLastError(wxT("RegisterClass(wxGLCanvasClass)"));
145 // Register the GLCanvas class name for windows which don't do full repaint
147 wndclass
.lpszClassName
= wxGLCanvasClassNameNoRedraw
;
148 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
150 if ( !::RegisterClass(&wndclass
) )
152 wxLogLastError(wxT("RegisterClass(wxGLCanvasClassNameNoRedraw)"));
154 ::UnregisterClass(wxGLCanvasClassName
, wxhInstance
);
159 ms_registeredGLClasses
= true;
165 void wxGLModule::UnregisterClasses()
167 // we need to unregister the classes in case we're in a DLL which is
168 // unloaded and then loaded again because if we don't, the registration is
169 // going to fail in wxGLCanvas::Create() the next time we're loaded
170 if ( ms_registeredGLClasses
)
172 ::UnregisterClass(wxGLCanvasClassName
, wxhInstance
);
173 ::UnregisterClass(wxGLCanvasClassNameNoRedraw
, wxhInstance
);
175 ms_registeredGLClasses
= false;
179 // ----------------------------------------------------------------------------
181 // ----------------------------------------------------------------------------
183 IMPLEMENT_CLASS(wxGLContext
, wxObject
)
185 wxGLContext::wxGLContext(wxGLCanvas
*win
, const wxGLContext
* other
)
187 m_glContext
= wglCreateContext(win
->GetHDC());
188 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGL context") );
192 if ( !wglShareLists(other
->m_glContext
, m_glContext
) )
193 wxLogLastError(_T("wglShareLists"));
197 wxGLContext::~wxGLContext()
199 // note that it's ok to delete the context even if it's the current one
200 wglDeleteContext(m_glContext
);
203 bool wxGLContext::SetCurrent(const wxGLCanvas
& win
) const
205 if ( !wglMakeCurrent(win
.GetHDC(), m_glContext
) )
207 wxLogLastError(_T("wglMakeCurrent"));
213 // ============================================================================
215 // ============================================================================
217 IMPLEMENT_CLASS(wxGLCanvas
, wxWindow
)
219 BEGIN_EVENT_TABLE(wxGLCanvas
, wxWindow
)
220 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged
)
221 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette
)
224 // ----------------------------------------------------------------------------
225 // wxGLCanvas construction
226 // ----------------------------------------------------------------------------
228 void wxGLCanvas::Init()
230 #if WXWIN_COMPATIBILITY_2_8
236 wxGLCanvas::wxGLCanvas(wxWindow
*parent
,
238 const int *attribList
,
242 const wxString
& name
,
243 const wxPalette
& palette
)
247 (void)Create(parent
, id
, pos
, size
, style
, name
, attribList
, palette
);
250 wxGLCanvas::~wxGLCanvas()
252 ::ReleaseDC(GetHwnd(), m_hDC
);
255 // Replaces wxWindow::Create functionality, since we need to use a different
257 bool wxGLCanvas::Create(wxWindow
*parent
,
262 const wxString
& name
,
263 const int *attribList
,
264 const wxPalette
& palette
)
266 wxCHECK_MSG( parent
, false, wxT("can't create wxWindow without parent") );
268 if ( !wxGLModule::RegisterClasses() )
270 wxLogError(_("Failed to register OpenGL window class."));
275 if ( !CreateBase(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) )
278 parent
->AddChild(this);
281 A general rule with OpenGL and Win32 is that any window that will have a
282 HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
283 You can find references about this within the knowledge base and most OpenGL
284 books that contain the wgl function descriptions.
287 DWORD msflags
= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
288 msflags
|= MSWGetStyle(style
, &exStyle
);
290 if ( !MSWCreate(wxGLCanvasClassName
, NULL
, pos
, size
, msflags
, exStyle
) )
293 m_hDC
= ::GetDC(GetHwnd());
297 if ( !DoSetup(attribList
) )
301 if ( !SetupPalette(palette
) )
303 #else // !wxUSE_PALETTE
304 wxUnusedVar(palette
);
305 #endif // wxUSE_PALETTE/!wxUSE_PALETTE
310 // ----------------------------------------------------------------------------
312 // ----------------------------------------------------------------------------
314 bool wxGLCanvas::SwapBuffers()
316 if ( !::SwapBuffers(m_hDC
) )
318 wxLogLastError(_T("SwapBuffers"));
325 // ----------------------------------------------------------------------------
326 // pixel format stuff
327 // ----------------------------------------------------------------------------
330 AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR
& pfd
, const int *attribList
)
335 // remove default attributes
336 pfd
.dwFlags
&= ~PFD_DOUBLEBUFFER
;
337 pfd
.iPixelType
= PFD_TYPE_COLORINDEX
;
339 for ( int arg
= 0; attribList
[arg
]; )
341 switch ( attribList
[arg
++] )
344 pfd
.iPixelType
= PFD_TYPE_RGBA
;
347 case WX_GL_BUFFER_SIZE
:
348 pfd
.cColorBits
= attribList
[arg
++];
352 // this member looks like it may be obsolete
353 if ( attribList
[arg
] > 0 )
354 pfd
.iLayerType
= PFD_OVERLAY_PLANE
;
355 else if ( attribList
[arg
] < 0 )
356 pfd
.iLayerType
= (BYTE
)PFD_UNDERLAY_PLANE
;
358 pfd
.iLayerType
= PFD_MAIN_PLANE
;
362 case WX_GL_DOUBLEBUFFER
:
363 pfd
.dwFlags
|= PFD_DOUBLEBUFFER
;
367 pfd
.dwFlags
|= PFD_STEREO
;
370 case WX_GL_AUX_BUFFERS
:
371 pfd
.cAuxBuffers
= attribList
[arg
++];
375 pfd
.cColorBits
+= (pfd
.cRedBits
= attribList
[arg
++]);
378 case WX_GL_MIN_GREEN
:
379 pfd
.cColorBits
+= (pfd
.cGreenBits
= attribList
[arg
++]);
383 pfd
.cColorBits
+= (pfd
.cBlueBits
= attribList
[arg
++]);
386 case WX_GL_MIN_ALPHA
:
387 // doesn't count in cColorBits
388 pfd
.cAlphaBits
= attribList
[arg
++];
391 case WX_GL_DEPTH_SIZE
:
392 pfd
.cDepthBits
= attribList
[arg
++];
395 case WX_GL_STENCIL_SIZE
:
396 pfd
.cStencilBits
= attribList
[arg
++];
399 case WX_GL_MIN_ACCUM_RED
:
400 pfd
.cAccumBits
+= (pfd
.cAccumRedBits
= attribList
[arg
++]);
403 case WX_GL_MIN_ACCUM_GREEN
:
404 pfd
.cAccumBits
+= (pfd
.cAccumGreenBits
= attribList
[arg
++]);
407 case WX_GL_MIN_ACCUM_BLUE
:
408 pfd
.cAccumBits
+= (pfd
.cAccumBlueBits
= attribList
[arg
++]);
411 case WX_GL_MIN_ACCUM_ALPHA
:
412 pfd
.cAccumBits
+= (pfd
.cAccumAlphaBits
= attribList
[arg
++]);
420 wxGLCanvas::ChooseMatchingPixelFormat(HDC hdc
,
421 const int *attribList
,
422 PIXELFORMATDESCRIPTOR
*ppfd
)
424 // default neutral pixel format
425 PIXELFORMATDESCRIPTOR pfd
=
427 sizeof(PIXELFORMATDESCRIPTOR
), // size
431 PFD_DOUBLEBUFFER
, // use double-buffering by default
432 PFD_TYPE_RGBA
, // default pixel type
433 0, // preferred color depth (don't care)
434 0, 0, 0, 0, 0, 0, // color bits and shift bits (ignored)
435 0, 0, // alpha bits and shift (ignored)
436 0, // accumulation total bits
437 0, 0, 0, 0, // accumulator RGBA bits (not used)
439 0, // no stencil buffer
440 0, // no auxiliary buffers
441 PFD_MAIN_PLANE
, // main layer
443 0, 0, 0, // no layer, visible, damage masks
451 AdjustPFDForAttributes(*ppfd
, attribList
);
453 return ::ChoosePixelFormat(hdc
, ppfd
);
457 bool wxGLCanvasBase::IsDisplaySupported(const int *attribList
)
459 // We need a device context to test the pixel format, so get one
460 // for the root window.
461 return wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList
) > 0;
464 bool wxGLCanvas::DoSetup(const int *attribList
)
466 PIXELFORMATDESCRIPTOR pfd
;
467 const int pixelFormat
= ChooseMatchingPixelFormat(m_hDC
, attribList
, &pfd
);
470 wxLogLastError(_T("ChoosePixelFormat"));
474 if ( !::SetPixelFormat(m_hDC
, pixelFormat
, &pfd
) )
476 wxLogLastError(_T("SetPixelFormat"));
483 // ----------------------------------------------------------------------------
485 // ----------------------------------------------------------------------------
489 bool wxGLCanvas::SetupPalette(const wxPalette
& palette
)
491 const int pixelFormat
= ::GetPixelFormat(m_hDC
);
494 wxLogLastError(_T("GetPixelFormat"));
498 PIXELFORMATDESCRIPTOR pfd
;
499 if ( !::DescribePixelFormat(m_hDC
, pixelFormat
, sizeof(pfd
), &pfd
) )
501 wxLogLastError(_T("DescribePixelFormat"));
505 if ( !(pfd
.dwFlags
& PFD_NEED_PALETTE
) )
510 if ( !m_palette
.Ok() )
512 m_palette
= CreateDefaultPalette();
513 if ( !m_palette
.Ok() )
517 if ( !::SelectPalette(m_hDC
, GetHpaletteOf(m_palette
), FALSE
) )
519 wxLogLastError(_T("SelectPalette"));
523 if ( ::RealizePalette(m_hDC
) == GDI_ERROR
)
525 wxLogLastError(_T("RealizePalette"));
532 wxPalette
wxGLCanvas::CreateDefaultPalette()
534 PIXELFORMATDESCRIPTOR pfd
;
536 int pixelFormat
= GetPixelFormat(m_hDC
);
538 DescribePixelFormat(m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
540 paletteSize
= 1 << pfd
.cColorBits
;
543 (LOGPALETTE
*) malloc(sizeof(LOGPALETTE
) + paletteSize
* sizeof(PALETTEENTRY
));
544 pPal
->palVersion
= 0x300;
545 pPal
->palNumEntries
= (WORD
)paletteSize
;
547 /* build a simple RGB color palette */
549 int redMask
= (1 << pfd
.cRedBits
) - 1;
550 int greenMask
= (1 << pfd
.cGreenBits
) - 1;
551 int blueMask
= (1 << pfd
.cBlueBits
) - 1;
554 for (i
=0; i
<paletteSize
; ++i
) {
555 pPal
->palPalEntry
[i
].peRed
=
556 (BYTE
)((((i
>> pfd
.cRedShift
) & redMask
) * 255) / redMask
);
557 pPal
->palPalEntry
[i
].peGreen
=
558 (BYTE
)((((i
>> pfd
.cGreenShift
) & greenMask
) * 255) / greenMask
);
559 pPal
->palPalEntry
[i
].peBlue
=
560 (BYTE
)((((i
>> pfd
.cBlueShift
) & blueMask
) * 255) / blueMask
);
561 pPal
->palPalEntry
[i
].peFlags
= 0;
565 HPALETTE hPalette
= CreatePalette(pPal
);
569 palette
.SetHPALETTE((WXHPALETTE
) hPalette
);
574 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent
& event
)
576 /* realize palette if this is the current window */
577 if ( GetPalette()->Ok() ) {
578 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
579 ::SelectPalette(GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
580 ::RealizePalette(GetHDC());
582 event
.SetPaletteRealized(true);
585 event
.SetPaletteRealized(false);
588 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent
& event
)
590 /* realize palette if this is *not* the current window */
592 GetPalette()->Ok() && (this != event
.GetChangedWindow()) )
594 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
595 ::SelectPalette(GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
596 ::RealizePalette(GetHDC());
601 #endif // wxUSE_PALETTE
603 // ----------------------------------------------------------------------------
604 // deprecated wxGLCanvas methods using implicit wxGLContext
605 // ----------------------------------------------------------------------------
607 // deprecated constructors creating an implicit m_glContext
608 #if WXWIN_COMPATIBILITY_2_8
610 wxGLCanvas::wxGLCanvas(wxWindow
*parent
,
615 const wxString
& name
,
616 const int *attribList
,
617 const wxPalette
& palette
)
621 if ( Create(parent
, id
, pos
, size
, style
, name
, attribList
, palette
) )
622 m_glContext
= new wxGLContext(this);
625 wxGLCanvas::wxGLCanvas(wxWindow
*parent
,
626 const wxGLContext
*shared
,
631 const wxString
& name
,
632 const int *attribList
,
633 const wxPalette
& palette
)
637 if ( Create(parent
, id
, pos
, size
, style
, name
, attribList
, palette
) )
638 m_glContext
= new wxGLContext(this, shared
);
641 wxGLCanvas::wxGLCanvas(wxWindow
*parent
,
642 const wxGLCanvas
*shared
,
647 const wxString
& name
,
648 const int *attribList
,
649 const wxPalette
& palette
)
653 if ( Create(parent
, id
, pos
, size
, style
, name
, attribList
, palette
) )
654 m_glContext
= new wxGLContext(this, shared
? shared
->m_glContext
: NULL
);
657 #endif // WXWIN_COMPATIBILITY_2_8
660 // ----------------------------------------------------------------------------
662 // ----------------------------------------------------------------------------
664 bool wxGLApp::InitGLVisual(const int *attribList
)
666 if ( !wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList
) )
668 wxLogError(_("Failed to initialize OpenGL"));
675 #endif // wxUSE_GLCANVAS