Big wxGL classes refactoring/cleanup:
[wxWidgets.git] / src / msw / glcanvas.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/glcanvas.cpp
3 // Purpose: wxGLCanvas, for using OpenGL with wxWidgets under MS Windows
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #include "wx/wxprec.h"
21
22 #if defined(__BORLANDC__)
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_GLCANVAS
27
28 #ifndef WX_PRECOMP
29 #include "wx/intl.h"
30 #include "wx/log.h"
31 #include "wx/app.h"
32 #include "wx/module.h"
33 #endif
34
35 #include "wx/msw/private.h"
36
37 #include "wx/glcanvas.h"
38
39 // from src/msw/window.cpp
40 LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message,
41 WPARAM wParam, LPARAM lParam);
42
43 #ifdef GL_EXT_vertex_array
44 #define WXUNUSED_WITHOUT_GL_EXT_vertex_array(name) name
45 #else
46 #define WXUNUSED_WITHOUT_GL_EXT_vertex_array(name) WXUNUSED(name)
47 #endif
48
49 // ----------------------------------------------------------------------------
50 // libraries
51 // ----------------------------------------------------------------------------
52
53 /*
54 The following two compiler directives are specific to the Microsoft Visual
55 C++ family of compilers
56
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.
60
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.
67
68 See MSDN for further information on the exact usage of these commands.
69 */
70 #ifdef _MSC_VER
71 # pragma comment( lib, "opengl32" )
72 # pragma comment( lib, "glu32" )
73 #endif
74
75 // ----------------------------------------------------------------------------
76 // constants
77 // ----------------------------------------------------------------------------
78
79 static const wxChar *wxGLCanvasClassName = wxT("wxGLCanvasClass");
80 static const wxChar *wxGLCanvasClassNameNoRedraw = wxT("wxGLCanvasClassNR");
81
82 // ============================================================================
83 // implementation
84 // ============================================================================
85
86 // ----------------------------------------------------------------------------
87 // wxGLModule is responsible for unregistering wxGLCanvasClass Windows class
88 // ----------------------------------------------------------------------------
89
90 class wxGLModule : public wxModule
91 {
92 public:
93 bool OnInit() { return true; }
94 void OnExit() { UnregisterClasses(); }
95
96 // register the GL classes if not done yet, return true if ok, false if
97 // registration failed
98 static bool RegisterClasses();
99
100 // unregister the classes, done automatically on program termination
101 static void UnregisterClasses();
102
103 private:
104 // wxGLCanvas is only used from the main thread so this is MT-ok
105 static bool ms_registeredGLClasses;
106
107 DECLARE_DYNAMIC_CLASS(wxGLModule)
108 };
109
110 IMPLEMENT_DYNAMIC_CLASS(wxGLModule, wxModule)
111
112 bool wxGLModule::ms_registeredGLClasses = false;
113
114 /* static */
115 bool wxGLModule::RegisterClasses()
116 {
117 if ( ms_registeredGLClasses )
118 return true;
119
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
123 WNDCLASS wndclass;
124
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;
133
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;
138
139 if ( !::RegisterClass(&wndclass) )
140 {
141 wxLogLastError(wxT("RegisterClass(wxGLCanvasClass)"));
142 return false;
143 }
144
145 // Register the GLCanvas class name for windows which don't do full repaint
146 // on resize
147 wndclass.lpszClassName = wxGLCanvasClassNameNoRedraw;
148 wndclass.style &= ~(CS_HREDRAW | CS_VREDRAW);
149
150 if ( !::RegisterClass(&wndclass) )
151 {
152 wxLogLastError(wxT("RegisterClass(wxGLCanvasClassNameNoRedraw)"));
153
154 ::UnregisterClass(wxGLCanvasClassName, wxhInstance);
155
156 return false;
157 }
158
159 ms_registeredGLClasses = true;
160
161 return true;
162 }
163
164 /* static */
165 void wxGLModule::UnregisterClasses()
166 {
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 )
171 {
172 ::UnregisterClass(wxGLCanvasClassName, wxhInstance);
173 ::UnregisterClass(wxGLCanvasClassNameNoRedraw, wxhInstance);
174
175 ms_registeredGLClasses = false;
176 }
177 }
178
179 // ----------------------------------------------------------------------------
180 // wxGLContext
181 // ----------------------------------------------------------------------------
182
183 IMPLEMENT_CLASS(wxGLContext, wxObject)
184
185 wxGLContext::wxGLContext(wxGLCanvas *win, const wxGLContext* other)
186 {
187 m_glContext = wglCreateContext(win->GetHDC());
188 wxCHECK_RET( m_glContext, wxT("Couldn't create OpenGL context") );
189
190 if ( other )
191 {
192 if ( !wglShareLists(other->m_glContext, m_glContext) )
193 wxLogLastError(_T("wglShareLists"));
194 }
195 }
196
197 wxGLContext::~wxGLContext()
198 {
199 // note that it's ok to delete the context even if it's the current one
200 wglDeleteContext(m_glContext);
201 }
202
203 void wxGLContext::SetCurrent(const wxGLCanvas& win) const
204 {
205 if ( !wglMakeCurrent(win.GetHDC(), m_glContext) )
206 {
207 wxLogLastError(_T("wglMakeCurrent"));
208 }
209 }
210
211 // ============================================================================
212 // wxGLCanvas
213 // ============================================================================
214
215 IMPLEMENT_CLASS(wxGLCanvas, wxWindow)
216
217 BEGIN_EVENT_TABLE(wxGLCanvas, wxWindow)
218 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged)
219 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette)
220 END_EVENT_TABLE()
221
222 // ----------------------------------------------------------------------------
223 // wxGLCanvas construction
224 // ----------------------------------------------------------------------------
225
226 void wxGLCanvas::Init()
227 {
228 #if WXWIN_COMPATIBILITY_2_8
229 m_glContext = NULL;
230 #endif
231 m_hDC = NULL;
232 }
233
234 wxGLCanvas::wxGLCanvas(wxWindow *parent,
235 wxWindowID id,
236 const int *attribList,
237 const wxPoint& pos,
238 const wxSize& size,
239 long style,
240 const wxString& name,
241 const wxPalette& palette)
242 {
243 Init();
244
245 (void)Create(parent, id, pos, size, style, name, attribList, palette);
246 }
247
248 wxGLCanvas::~wxGLCanvas()
249 {
250 #if WXWIN_COMPATIBILITY_2_8
251 delete m_glContext;
252 #endif
253
254 ::ReleaseDC(GetHwnd(), m_hDC);
255 }
256
257 // Replaces wxWindow::Create functionality, since we need to use a different
258 // window class
259 bool wxGLCanvas::Create(wxWindow *parent,
260 wxWindowID id,
261 const wxPoint& pos,
262 const wxSize& size,
263 long style,
264 const wxString& name,
265 const int *attribList,
266 const wxPalette& palette)
267 {
268 wxCHECK_MSG( parent, false, wxT("can't create wxWindow without parent") );
269
270 if ( !wxGLModule::RegisterClasses() )
271 {
272 wxLogError(_("Failed to register OpenGL window class."));
273
274 return false;
275 }
276
277 if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) )
278 return false;
279
280 parent->AddChild(this);
281
282 /*
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.
287 */
288 WXDWORD exStyle = 0;
289 DWORD msflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
290 msflags |= MSWGetStyle(style, &exStyle);
291
292 if ( !MSWCreate(wxGLCanvasClassName, NULL, pos, size, msflags, exStyle) )
293 return false;
294
295 m_hDC = ::GetDC(GetHwnd());
296 if ( !m_hDC )
297 return false;
298
299 if ( !DoSetup(attribList) )
300 return false;
301
302 #if wxUSE_PALETTE
303 if ( !SetupPalette(palette) )
304 return false;
305 #else // !wxUSE_PALETTE
306 wxUnusedVar(palette);
307 #endif // wxUSE_PALETTE/!wxUSE_PALETTE
308
309 return true;
310 }
311
312 // ----------------------------------------------------------------------------
313 // operations
314 // ----------------------------------------------------------------------------
315
316 void wxGLCanvas::SwapBuffers()
317 {
318 if ( !::SwapBuffers(m_hDC) )
319 wxLogLastError(_T("SwapBuffers"));
320 }
321
322 // ----------------------------------------------------------------------------
323 // pixel format stuff
324 // ----------------------------------------------------------------------------
325
326 static void
327 AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR& pfd, const int *attribList)
328 {
329 if ( !attribList )
330 return;
331
332 pfd.dwFlags &= ~PFD_DOUBLEBUFFER;
333 pfd.iPixelType = PFD_TYPE_COLORINDEX;
334 pfd.cColorBits = 0;
335 int arg=0;
336
337 while ( attribList[arg] )
338 {
339 switch ( attribList[arg++] )
340 {
341 case WX_GL_RGBA:
342 pfd.iPixelType = PFD_TYPE_RGBA;
343 break;
344 case WX_GL_BUFFER_SIZE:
345 pfd.cColorBits = attribList[arg++];
346 break;
347 case WX_GL_LEVEL:
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;
353 else
354 pfd.iLayerType = PFD_MAIN_PLANE;
355 arg++;
356 break;
357 case WX_GL_DOUBLEBUFFER:
358 pfd.dwFlags |= PFD_DOUBLEBUFFER;
359 break;
360 case WX_GL_STEREO:
361 pfd.dwFlags |= PFD_STEREO;
362 break;
363 case WX_GL_AUX_BUFFERS:
364 pfd.cAuxBuffers = attribList[arg++];
365 break;
366 case WX_GL_MIN_RED:
367 pfd.cColorBits = (pfd.cColorBits +
368 (pfd.cRedBits = attribList[arg++]));
369 break;
370 case WX_GL_MIN_GREEN:
371 pfd.cColorBits = (pfd.cColorBits +
372 (pfd.cGreenBits = attribList[arg++]));
373 break;
374 case WX_GL_MIN_BLUE:
375 pfd.cColorBits = (pfd.cColorBits +
376 (pfd.cBlueBits = attribList[arg++]));
377 break;
378 case WX_GL_MIN_ALPHA:
379 // doesn't count in cColorBits
380 pfd.cAlphaBits = attribList[arg++];
381 break;
382 case WX_GL_DEPTH_SIZE:
383 pfd.cDepthBits = attribList[arg++];
384 break;
385 case WX_GL_STENCIL_SIZE:
386 pfd.cStencilBits = attribList[arg++];
387 break;
388 case WX_GL_MIN_ACCUM_RED:
389 pfd.cAccumBits = (pfd.cAccumBits +
390 (pfd.cAccumRedBits = attribList[arg++]));
391 break;
392 case WX_GL_MIN_ACCUM_GREEN:
393 pfd.cAccumBits = (pfd.cAccumBits +
394 (pfd.cAccumGreenBits = attribList[arg++]));
395 break;
396 case WX_GL_MIN_ACCUM_BLUE:
397 pfd.cAccumBits = (pfd.cAccumBits +
398 (pfd.cAccumBlueBits = attribList[arg++]));
399 break;
400 case WX_GL_MIN_ACCUM_ALPHA:
401 pfd.cAccumBits = (pfd.cAccumBits +
402 (pfd.cAccumAlphaBits = attribList[arg++]));
403 break;
404 }
405 }
406 }
407
408 /* static */
409 int
410 wxGLCanvas::ChooseMatchingPixelFormat(HDC hdc,
411 const int *attribList,
412 PIXELFORMATDESCRIPTOR *ppfd)
413 {
414 // default neutral pixel format
415 PIXELFORMATDESCRIPTOR pfd =
416 {
417 sizeof(PIXELFORMATDESCRIPTOR), // size
418 1, // version
419 PFD_SUPPORT_OPENGL |
420 PFD_DRAW_TO_WINDOW |
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)
429 16, // depth buffer
430 0, // no stencil buffer
431 0, // no auxiliary buffers
432 PFD_MAIN_PLANE, // main layer
433 0, // reserved
434 0, 0, 0, // no layer, visible, damage masks
435 };
436
437 if ( !ppfd )
438 ppfd = &pfd;
439 else
440 *ppfd = pfd;
441
442 AdjustPFDForAttributes(*ppfd, attribList);
443
444 return ::ChoosePixelFormat(hdc, ppfd);
445 }
446
447 bool wxGLCanvas::DoSetup(const int *attribList)
448 {
449 PIXELFORMATDESCRIPTOR pfd;
450 const int pixelFormat = ChooseMatchingPixelFormat(m_hDC, attribList, &pfd);
451 if ( !pixelFormat )
452 {
453 wxLogLastError(_T("ChoosePixelFormat"));
454 return false;
455 }
456
457 if ( !::SetPixelFormat(m_hDC, pixelFormat, &pfd) )
458 {
459 wxLogLastError(_T("SetPixelFormat"));
460 return false;
461 }
462
463 return true;
464 }
465
466 // ----------------------------------------------------------------------------
467 // palette stuff
468 // ----------------------------------------------------------------------------
469
470 #if wxUSE_PALETTE
471
472 bool wxGLCanvas::SetupPalette(const wxPalette& palette)
473 {
474 const int pixelFormat = ::GetPixelFormat(m_hDC);
475 if ( !pixelFormat )
476 {
477 wxLogLastError(_T("GetPixelFormat"));
478 return false;
479 }
480
481 PIXELFORMATDESCRIPTOR pfd;
482 if ( !::DescribePixelFormat(m_hDC, pixelFormat, sizeof(pfd), &pfd) )
483 {
484 wxLogLastError(_T("DescribePixelFormat"));
485 return false;
486 }
487
488 if ( !(pfd.dwFlags & PFD_NEED_PALETTE) )
489 return true;
490
491 m_palette = palette;
492
493 if ( !m_palette.Ok() )
494 {
495 m_palette = CreateDefaultPalette();
496 if ( !m_palette.Ok() )
497 return false;
498 }
499
500 if ( !::SelectPalette(m_hDC, GetHpaletteOf(m_palette), FALSE) )
501 {
502 wxLogLastError(_T("SelectPalette"));
503 return false;
504 }
505
506 if ( ::RealizePalette(m_hDC) == GDI_ERROR )
507 {
508 wxLogLastError(_T("RealizePalette"));
509 return false;
510 }
511
512 return true;
513 }
514
515 wxPalette wxGLCanvas::CreateDefaultPalette()
516 {
517 PIXELFORMATDESCRIPTOR pfd;
518 int paletteSize;
519 int pixelFormat = GetPixelFormat(m_hDC);
520
521 DescribePixelFormat(m_hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
522
523 paletteSize = 1 << pfd.cColorBits;
524
525 LOGPALETTE* pPal =
526 (LOGPALETTE*) malloc(sizeof(LOGPALETTE) + paletteSize * sizeof(PALETTEENTRY));
527 pPal->palVersion = 0x300;
528 pPal->palNumEntries = (WORD)paletteSize;
529
530 /* build a simple RGB color palette */
531 {
532 int redMask = (1 << pfd.cRedBits) - 1;
533 int greenMask = (1 << pfd.cGreenBits) - 1;
534 int blueMask = (1 << pfd.cBlueBits) - 1;
535 int i;
536
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;
545 }
546 }
547
548 HPALETTE hPalette = CreatePalette(pPal);
549 free(pPal);
550
551 wxPalette palette;
552 palette.SetHPALETTE((WXHPALETTE) hPalette);
553
554 return palette;
555 }
556
557 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent& event)
558 {
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());
564 Refresh();
565 event.SetPaletteRealized(true);
566 }
567 else
568 event.SetPaletteRealized(false);
569 }
570
571 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent& event)
572 {
573 /* realize palette if this is *not* the current window */
574 if ( GetPalette() &&
575 GetPalette()->Ok() && (this != event.GetChangedWindow()) )
576 {
577 ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE());
578 ::SelectPalette(GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE);
579 ::RealizePalette(GetHDC());
580 Refresh();
581 }
582 }
583
584 #endif // wxUSE_PALETTE
585
586 // ----------------------------------------------------------------------------
587 // deprecated wxGLCanvas methods using implicit wxGLContext
588 // ----------------------------------------------------------------------------
589
590 // deprecated constructors creating an implicit m_glContext
591 #if WXWIN_COMPATIBILITY_2_8
592
593 wxGLCanvas::wxGLCanvas(wxWindow *parent,
594 wxWindowID id,
595 const wxPoint& pos,
596 const wxSize& size,
597 long style,
598 const wxString& name,
599 const int *attribList,
600 const wxPalette& palette)
601 {
602 Init();
603
604 if ( Create(parent, id, pos, size, style, name, attribList, palette) )
605 m_glContext = new wxGLContext(this);
606 }
607
608 wxGLCanvas::wxGLCanvas(wxWindow *parent,
609 const wxGLContext *shared,
610 wxWindowID id,
611 const wxPoint& pos,
612 const wxSize& size,
613 long style,
614 const wxString& name,
615 const int *attribList,
616 const wxPalette& palette)
617 {
618 Init();
619
620 if ( Create(parent, id, pos, size, style, name, attribList, palette) )
621 m_glContext = new wxGLContext(this, shared);
622 }
623
624 wxGLCanvas::wxGLCanvas(wxWindow *parent,
625 const wxGLCanvas *shared,
626 wxWindowID id,
627 const wxPoint& pos,
628 const wxSize& size,
629 long style,
630 const wxString& name,
631 const int *attribList,
632 const wxPalette& palette)
633 {
634 Init();
635
636 if ( Create(parent, id, pos, size, style, name, attribList, palette) )
637 m_glContext = new wxGLContext(this, shared ? shared->m_glContext : NULL);
638 }
639
640 #endif // WXWIN_COMPATIBILITY_2_8
641
642
643 // ----------------------------------------------------------------------------
644 // wxGLApp
645 // ----------------------------------------------------------------------------
646
647 bool wxGLApp::InitGLVisual(const int *attribList)
648 {
649 if ( !wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList) )
650 {
651 wxLogError(_("Failed to initialize OpenGL"));
652 return false;
653 }
654
655 return true;
656 }
657
658 #endif // wxUSE_GLCANVAS