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 #if WXWIN_COMPATIBILITY_2_8
256 ::ReleaseDC(GetHwnd(), m_hDC
);
259 // Replaces wxWindow::Create functionality, since we need to use a different
261 bool wxGLCanvas::Create(wxWindow
*parent
,
266 const wxString
& name
,
267 const int *attribList
,
268 const wxPalette
& palette
)
270 wxCHECK_MSG( parent
, false, wxT("can't create wxWindow without parent") );
272 if ( !wxGLModule::RegisterClasses() )
274 wxLogError(_("Failed to register OpenGL window class."));
279 if ( !CreateBase(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) )
282 parent
->AddChild(this);
285 A general rule with OpenGL and Win32 is that any window that will have a
286 HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
287 You can find references about this within the knowledge base and most OpenGL
288 books that contain the wgl function descriptions.
291 DWORD msflags
= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
292 msflags
|= MSWGetStyle(style
, &exStyle
);
294 if ( !MSWCreate(wxGLCanvasClassName
, NULL
, pos
, size
, msflags
, exStyle
) )
297 m_hDC
= ::GetDC(GetHwnd());
301 if ( !DoSetup(attribList
) )
305 if ( !SetupPalette(palette
) )
307 #else // !wxUSE_PALETTE
308 wxUnusedVar(palette
);
309 #endif // wxUSE_PALETTE/!wxUSE_PALETTE
314 // ----------------------------------------------------------------------------
316 // ----------------------------------------------------------------------------
318 bool wxGLCanvas::SwapBuffers()
320 if ( !::SwapBuffers(m_hDC
) )
323 wxLogLastError(_T("SwapBuffers"));
328 // ----------------------------------------------------------------------------
329 // pixel format stuff
330 // ----------------------------------------------------------------------------
333 AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR
& pfd
, const int *attribList
)
338 pfd
.dwFlags
&= ~PFD_DOUBLEBUFFER
;
339 pfd
.iPixelType
= PFD_TYPE_COLORINDEX
;
343 while ( attribList
[arg
] )
345 switch ( attribList
[arg
++] )
348 pfd
.iPixelType
= PFD_TYPE_RGBA
;
350 case WX_GL_BUFFER_SIZE
:
351 pfd
.cColorBits
= attribList
[arg
++];
354 // this member looks like it may be obsolete
355 if ( attribList
[arg
] > 0 )
356 pfd
.iLayerType
= PFD_OVERLAY_PLANE
;
357 else if ( attribList
[arg
] < 0 )
358 pfd
.iLayerType
= (BYTE
)PFD_UNDERLAY_PLANE
;
360 pfd
.iLayerType
= PFD_MAIN_PLANE
;
363 case WX_GL_DOUBLEBUFFER
:
364 pfd
.dwFlags
|= PFD_DOUBLEBUFFER
;
367 pfd
.dwFlags
|= PFD_STEREO
;
369 case WX_GL_AUX_BUFFERS
:
370 pfd
.cAuxBuffers
= attribList
[arg
++];
373 pfd
.cColorBits
= (pfd
.cColorBits
+
374 (pfd
.cRedBits
= attribList
[arg
++]));
376 case WX_GL_MIN_GREEN
:
377 pfd
.cColorBits
= (pfd
.cColorBits
+
378 (pfd
.cGreenBits
= attribList
[arg
++]));
381 pfd
.cColorBits
= (pfd
.cColorBits
+
382 (pfd
.cBlueBits
= attribList
[arg
++]));
384 case WX_GL_MIN_ALPHA
:
385 // doesn't count in cColorBits
386 pfd
.cAlphaBits
= attribList
[arg
++];
388 case WX_GL_DEPTH_SIZE
:
389 pfd
.cDepthBits
= attribList
[arg
++];
391 case WX_GL_STENCIL_SIZE
:
392 pfd
.cStencilBits
= attribList
[arg
++];
394 case WX_GL_MIN_ACCUM_RED
:
395 pfd
.cAccumBits
= (pfd
.cAccumBits
+
396 (pfd
.cAccumRedBits
= attribList
[arg
++]));
398 case WX_GL_MIN_ACCUM_GREEN
:
399 pfd
.cAccumBits
= (pfd
.cAccumBits
+
400 (pfd
.cAccumGreenBits
= attribList
[arg
++]));
402 case WX_GL_MIN_ACCUM_BLUE
:
403 pfd
.cAccumBits
= (pfd
.cAccumBits
+
404 (pfd
.cAccumBlueBits
= attribList
[arg
++]));
406 case WX_GL_MIN_ACCUM_ALPHA
:
407 pfd
.cAccumBits
= (pfd
.cAccumBits
+
408 (pfd
.cAccumAlphaBits
= attribList
[arg
++]));
416 wxGLCanvas::ChooseMatchingPixelFormat(HDC hdc
,
417 const int *attribList
,
418 PIXELFORMATDESCRIPTOR
*ppfd
)
420 // default neutral pixel format
421 PIXELFORMATDESCRIPTOR pfd
=
423 sizeof(PIXELFORMATDESCRIPTOR
), // size
427 PFD_DOUBLEBUFFER
, // support double-buffering
428 PFD_TYPE_RGBA
, // color type
429 16, // preferred color depth
430 0, 0, 0, 0, 0, 0, // color bits (ignored)
431 0, // no alpha buffer
432 0, // alpha bits (ignored)
433 0, // no accumulation buffer
434 0, 0, 0, 0, // accumulator bits (ignored)
436 0, // no stencil buffer
437 0, // no auxiliary buffers
438 PFD_MAIN_PLANE
, // main layer
440 0, 0, 0, // no layer, visible, damage masks
448 AdjustPFDForAttributes(*ppfd
, attribList
);
450 return ::ChoosePixelFormat(hdc
, ppfd
);
453 bool wxGLCanvas::DoSetup(const int *attribList
)
455 PIXELFORMATDESCRIPTOR pfd
;
456 const int pixelFormat
= ChooseMatchingPixelFormat(m_hDC
, attribList
, &pfd
);
459 wxLogLastError(_T("ChoosePixelFormat"));
463 if ( !::SetPixelFormat(m_hDC
, pixelFormat
, &pfd
) )
465 wxLogLastError(_T("SetPixelFormat"));
472 // ----------------------------------------------------------------------------
474 // ----------------------------------------------------------------------------
478 bool wxGLCanvas::SetupPalette(const wxPalette
& palette
)
480 const int pixelFormat
= ::GetPixelFormat(m_hDC
);
483 wxLogLastError(_T("GetPixelFormat"));
487 PIXELFORMATDESCRIPTOR pfd
;
488 if ( !::DescribePixelFormat(m_hDC
, pixelFormat
, sizeof(pfd
), &pfd
) )
490 wxLogLastError(_T("DescribePixelFormat"));
494 if ( !(pfd
.dwFlags
& PFD_NEED_PALETTE
) )
499 if ( !m_palette
.Ok() )
501 m_palette
= CreateDefaultPalette();
502 if ( !m_palette
.Ok() )
506 if ( !::SelectPalette(m_hDC
, GetHpaletteOf(m_palette
), FALSE
) )
508 wxLogLastError(_T("SelectPalette"));
512 if ( ::RealizePalette(m_hDC
) == GDI_ERROR
)
514 wxLogLastError(_T("RealizePalette"));
521 wxPalette
wxGLCanvas::CreateDefaultPalette()
523 PIXELFORMATDESCRIPTOR pfd
;
525 int pixelFormat
= GetPixelFormat(m_hDC
);
527 DescribePixelFormat(m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
529 paletteSize
= 1 << pfd
.cColorBits
;
532 (LOGPALETTE
*) malloc(sizeof(LOGPALETTE
) + paletteSize
* sizeof(PALETTEENTRY
));
533 pPal
->palVersion
= 0x300;
534 pPal
->palNumEntries
= (WORD
)paletteSize
;
536 /* build a simple RGB color palette */
538 int redMask
= (1 << pfd
.cRedBits
) - 1;
539 int greenMask
= (1 << pfd
.cGreenBits
) - 1;
540 int blueMask
= (1 << pfd
.cBlueBits
) - 1;
543 for (i
=0; i
<paletteSize
; ++i
) {
544 pPal
->palPalEntry
[i
].peRed
=
545 (BYTE
)((((i
>> pfd
.cRedShift
) & redMask
) * 255) / redMask
);
546 pPal
->palPalEntry
[i
].peGreen
=
547 (BYTE
)((((i
>> pfd
.cGreenShift
) & greenMask
) * 255) / greenMask
);
548 pPal
->palPalEntry
[i
].peBlue
=
549 (BYTE
)((((i
>> pfd
.cBlueShift
) & blueMask
) * 255) / blueMask
);
550 pPal
->palPalEntry
[i
].peFlags
= 0;
554 HPALETTE hPalette
= CreatePalette(pPal
);
558 palette
.SetHPALETTE((WXHPALETTE
) hPalette
);
563 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent
& event
)
565 /* realize palette if this is the current window */
566 if ( GetPalette()->Ok() ) {
567 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
568 ::SelectPalette(GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
569 ::RealizePalette(GetHDC());
571 event
.SetPaletteRealized(true);
574 event
.SetPaletteRealized(false);
577 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent
& event
)
579 /* realize palette if this is *not* the current window */
581 GetPalette()->Ok() && (this != event
.GetChangedWindow()) )
583 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
584 ::SelectPalette(GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
585 ::RealizePalette(GetHDC());
590 #endif // wxUSE_PALETTE
592 // ----------------------------------------------------------------------------
593 // deprecated wxGLCanvas methods using implicit wxGLContext
594 // ----------------------------------------------------------------------------
596 // deprecated constructors creating an implicit m_glContext
597 #if WXWIN_COMPATIBILITY_2_8
599 wxGLCanvas::wxGLCanvas(wxWindow
*parent
,
604 const wxString
& name
,
605 const int *attribList
,
606 const wxPalette
& palette
)
610 if ( Create(parent
, id
, pos
, size
, style
, name
, attribList
, palette
) )
611 m_glContext
= new wxGLContext(this);
614 wxGLCanvas::wxGLCanvas(wxWindow
*parent
,
615 const wxGLContext
*shared
,
620 const wxString
& name
,
621 const int *attribList
,
622 const wxPalette
& palette
)
626 if ( Create(parent
, id
, pos
, size
, style
, name
, attribList
, palette
) )
627 m_glContext
= new wxGLContext(this, shared
);
630 wxGLCanvas::wxGLCanvas(wxWindow
*parent
,
631 const wxGLCanvas
*shared
,
636 const wxString
& name
,
637 const int *attribList
,
638 const wxPalette
& palette
)
642 if ( Create(parent
, id
, pos
, size
, style
, name
, attribList
, palette
) )
643 m_glContext
= new wxGLContext(this, shared
? shared
->m_glContext
: NULL
);
646 #endif // WXWIN_COMPATIBILITY_2_8
649 // ----------------------------------------------------------------------------
651 // ----------------------------------------------------------------------------
653 bool wxGLApp::InitGLVisual(const int *attribList
)
655 if ( !wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList
) )
657 wxLogError(_("Failed to initialize OpenGL"));
664 #endif // wxUSE_GLCANVAS