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 void wxGLContext::SetCurrent(const wxGLCanvas
& win
) const
205 if ( !wglMakeCurrent(win
.GetHDC(), m_glContext
) )
207 wxLogLastError(_T("wglMakeCurrent"));
211 // ============================================================================
213 // ============================================================================
215 IMPLEMENT_CLASS(wxGLCanvas
, wxWindow
)
217 BEGIN_EVENT_TABLE(wxGLCanvas
, wxWindow
)
218 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged
)
219 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette
)
222 // ----------------------------------------------------------------------------
223 // wxGLCanvas construction
224 // ----------------------------------------------------------------------------
226 void wxGLCanvas::Init()
228 #if WXWIN_COMPATIBILITY_2_8
234 wxGLCanvas::wxGLCanvas(wxWindow
*parent
,
236 const int *attribList
,
240 const wxString
& name
,
241 const wxPalette
& palette
)
245 (void)Create(parent
, id
, pos
, size
, style
, name
, attribList
, palette
);
248 wxGLCanvas::~wxGLCanvas()
250 #if WXWIN_COMPATIBILITY_2_8
254 ::ReleaseDC(GetHwnd(), m_hDC
);
257 // Replaces wxWindow::Create functionality, since we need to use a different
259 bool wxGLCanvas::Create(wxWindow
*parent
,
264 const wxString
& name
,
265 const int *attribList
,
266 const wxPalette
& palette
)
268 wxCHECK_MSG( parent
, false, wxT("can't create wxWindow without parent") );
270 if ( !wxGLModule::RegisterClasses() )
272 wxLogError(_("Failed to register OpenGL window class."));
277 if ( !CreateBase(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) )
280 parent
->AddChild(this);
283 A general rule with OpenGL and Win32 is that any window that will have a
284 HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
285 You can find references about this within the knowledge base and most OpenGL
286 books that contain the wgl function descriptions.
289 DWORD msflags
= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
290 msflags
|= MSWGetStyle(style
, &exStyle
);
292 if ( !MSWCreate(wxGLCanvasClassName
, NULL
, pos
, size
, msflags
, exStyle
) )
295 m_hDC
= ::GetDC(GetHwnd());
299 if ( !DoSetup(attribList
) )
303 if ( !SetupPalette(palette
) )
305 #else // !wxUSE_PALETTE
306 wxUnusedVar(palette
);
307 #endif // wxUSE_PALETTE/!wxUSE_PALETTE
312 // ----------------------------------------------------------------------------
314 // ----------------------------------------------------------------------------
316 void wxGLCanvas::SwapBuffers()
318 if ( !::SwapBuffers(m_hDC
) )
319 wxLogLastError(_T("SwapBuffers"));
322 // ----------------------------------------------------------------------------
323 // pixel format stuff
324 // ----------------------------------------------------------------------------
327 AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR
& pfd
, const int *attribList
)
332 pfd
.dwFlags
&= ~PFD_DOUBLEBUFFER
;
333 pfd
.iPixelType
= PFD_TYPE_COLORINDEX
;
337 while ( attribList
[arg
] )
339 switch ( attribList
[arg
++] )
342 pfd
.iPixelType
= PFD_TYPE_RGBA
;
344 case WX_GL_BUFFER_SIZE
:
345 pfd
.cColorBits
= attribList
[arg
++];
348 // this member looks like it may be obsolete
349 if ( attribList
[arg
] > 0 )
350 pfd
.iLayerType
= PFD_OVERLAY_PLANE
;
351 else if ( attribList
[arg
] < 0 )
352 pfd
.iLayerType
= (BYTE
)PFD_UNDERLAY_PLANE
;
354 pfd
.iLayerType
= PFD_MAIN_PLANE
;
357 case WX_GL_DOUBLEBUFFER
:
358 pfd
.dwFlags
|= PFD_DOUBLEBUFFER
;
361 pfd
.dwFlags
|= PFD_STEREO
;
363 case WX_GL_AUX_BUFFERS
:
364 pfd
.cAuxBuffers
= attribList
[arg
++];
367 pfd
.cColorBits
= (pfd
.cColorBits
+
368 (pfd
.cRedBits
= attribList
[arg
++]));
370 case WX_GL_MIN_GREEN
:
371 pfd
.cColorBits
= (pfd
.cColorBits
+
372 (pfd
.cGreenBits
= attribList
[arg
++]));
375 pfd
.cColorBits
= (pfd
.cColorBits
+
376 (pfd
.cBlueBits
= attribList
[arg
++]));
378 case WX_GL_MIN_ALPHA
:
379 // doesn't count in cColorBits
380 pfd
.cAlphaBits
= attribList
[arg
++];
382 case WX_GL_DEPTH_SIZE
:
383 pfd
.cDepthBits
= attribList
[arg
++];
385 case WX_GL_STENCIL_SIZE
:
386 pfd
.cStencilBits
= attribList
[arg
++];
388 case WX_GL_MIN_ACCUM_RED
:
389 pfd
.cAccumBits
= (pfd
.cAccumBits
+
390 (pfd
.cAccumRedBits
= attribList
[arg
++]));
392 case WX_GL_MIN_ACCUM_GREEN
:
393 pfd
.cAccumBits
= (pfd
.cAccumBits
+
394 (pfd
.cAccumGreenBits
= attribList
[arg
++]));
396 case WX_GL_MIN_ACCUM_BLUE
:
397 pfd
.cAccumBits
= (pfd
.cAccumBits
+
398 (pfd
.cAccumBlueBits
= attribList
[arg
++]));
400 case WX_GL_MIN_ACCUM_ALPHA
:
401 pfd
.cAccumBits
= (pfd
.cAccumBits
+
402 (pfd
.cAccumAlphaBits
= attribList
[arg
++]));
410 wxGLCanvas::ChooseMatchingPixelFormat(HDC hdc
,
411 const int *attribList
,
412 PIXELFORMATDESCRIPTOR
*ppfd
)
414 // default neutral pixel format
415 PIXELFORMATDESCRIPTOR pfd
=
417 sizeof(PIXELFORMATDESCRIPTOR
), // size
421 PFD_DOUBLEBUFFER
, // support double-buffering
422 PFD_TYPE_RGBA
, // color type
423 16, // preferred color depth
424 0, 0, 0, 0, 0, 0, // color bits (ignored)
425 0, // no alpha buffer
426 0, // alpha bits (ignored)
427 0, // no accumulation buffer
428 0, 0, 0, 0, // accumulator bits (ignored)
430 0, // no stencil buffer
431 0, // no auxiliary buffers
432 PFD_MAIN_PLANE
, // main layer
434 0, 0, 0, // no layer, visible, damage masks
442 AdjustPFDForAttributes(*ppfd
, attribList
);
444 return ::ChoosePixelFormat(hdc
, ppfd
);
447 bool wxGLCanvas::DoSetup(const int *attribList
)
449 PIXELFORMATDESCRIPTOR pfd
;
450 const int pixelFormat
= ChooseMatchingPixelFormat(m_hDC
, attribList
, &pfd
);
453 wxLogLastError(_T("ChoosePixelFormat"));
457 if ( !::SetPixelFormat(m_hDC
, pixelFormat
, &pfd
) )
459 wxLogLastError(_T("SetPixelFormat"));
466 // ----------------------------------------------------------------------------
468 // ----------------------------------------------------------------------------
472 bool wxGLCanvas::SetupPalette(const wxPalette
& palette
)
474 const int pixelFormat
= ::GetPixelFormat(m_hDC
);
477 wxLogLastError(_T("GetPixelFormat"));
481 PIXELFORMATDESCRIPTOR pfd
;
482 if ( !::DescribePixelFormat(m_hDC
, pixelFormat
, sizeof(pfd
), &pfd
) )
484 wxLogLastError(_T("DescribePixelFormat"));
488 if ( !(pfd
.dwFlags
& PFD_NEED_PALETTE
) )
493 if ( !m_palette
.Ok() )
495 m_palette
= CreateDefaultPalette();
496 if ( !m_palette
.Ok() )
500 if ( !::SelectPalette(m_hDC
, GetHpaletteOf(m_palette
), FALSE
) )
502 wxLogLastError(_T("SelectPalette"));
506 if ( ::RealizePalette(m_hDC
) == GDI_ERROR
)
508 wxLogLastError(_T("RealizePalette"));
515 wxPalette
wxGLCanvas::CreateDefaultPalette()
517 PIXELFORMATDESCRIPTOR pfd
;
519 int pixelFormat
= GetPixelFormat(m_hDC
);
521 DescribePixelFormat(m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
523 paletteSize
= 1 << pfd
.cColorBits
;
526 (LOGPALETTE
*) malloc(sizeof(LOGPALETTE
) + paletteSize
* sizeof(PALETTEENTRY
));
527 pPal
->palVersion
= 0x300;
528 pPal
->palNumEntries
= (WORD
)paletteSize
;
530 /* build a simple RGB color palette */
532 int redMask
= (1 << pfd
.cRedBits
) - 1;
533 int greenMask
= (1 << pfd
.cGreenBits
) - 1;
534 int blueMask
= (1 << pfd
.cBlueBits
) - 1;
537 for (i
=0; i
<paletteSize
; ++i
) {
538 pPal
->palPalEntry
[i
].peRed
=
539 (BYTE
)((((i
>> pfd
.cRedShift
) & redMask
) * 255) / redMask
);
540 pPal
->palPalEntry
[i
].peGreen
=
541 (BYTE
)((((i
>> pfd
.cGreenShift
) & greenMask
) * 255) / greenMask
);
542 pPal
->palPalEntry
[i
].peBlue
=
543 (BYTE
)((((i
>> pfd
.cBlueShift
) & blueMask
) * 255) / blueMask
);
544 pPal
->palPalEntry
[i
].peFlags
= 0;
548 HPALETTE hPalette
= CreatePalette(pPal
);
552 palette
.SetHPALETTE((WXHPALETTE
) hPalette
);
557 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent
& event
)
559 /* realize palette if this is the current window */
560 if ( GetPalette()->Ok() ) {
561 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
562 ::SelectPalette(GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
563 ::RealizePalette(GetHDC());
565 event
.SetPaletteRealized(true);
568 event
.SetPaletteRealized(false);
571 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent
& event
)
573 /* realize palette if this is *not* the current window */
575 GetPalette()->Ok() && (this != event
.GetChangedWindow()) )
577 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
578 ::SelectPalette(GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
579 ::RealizePalette(GetHDC());
584 #endif // wxUSE_PALETTE
586 // ----------------------------------------------------------------------------
587 // deprecated wxGLCanvas methods using implicit wxGLContext
588 // ----------------------------------------------------------------------------
590 // deprecated constructors creating an implicit m_glContext
591 #if WXWIN_COMPATIBILITY_2_8
593 wxGLCanvas::wxGLCanvas(wxWindow
*parent
,
598 const wxString
& name
,
599 const int *attribList
,
600 const wxPalette
& palette
)
604 if ( Create(parent
, id
, pos
, size
, style
, name
, attribList
, palette
) )
605 m_glContext
= new wxGLContext(this);
608 wxGLCanvas::wxGLCanvas(wxWindow
*parent
,
609 const wxGLContext
*shared
,
614 const wxString
& name
,
615 const int *attribList
,
616 const wxPalette
& palette
)
620 if ( Create(parent
, id
, pos
, size
, style
, name
, attribList
, palette
) )
621 m_glContext
= new wxGLContext(this, shared
);
624 wxGLCanvas::wxGLCanvas(wxWindow
*parent
,
625 const wxGLCanvas
*shared
,
630 const wxString
& name
,
631 const int *attribList
,
632 const wxPalette
& palette
)
636 if ( Create(parent
, id
, pos
, size
, style
, name
, attribList
, palette
) )
637 m_glContext
= new wxGLContext(this, shared
? shared
->m_glContext
: NULL
);
640 #endif // WXWIN_COMPATIBILITY_2_8
643 // ----------------------------------------------------------------------------
645 // ----------------------------------------------------------------------------
647 bool wxGLApp::InitGLVisual(const int *attribList
)
649 if ( !wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList
) )
651 wxLogError(_("Failed to initialize OpenGL"));
658 #endif // wxUSE_GLCANVAS