log error before returning, not after
[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 bool wxGLContext::SetCurrent(const wxGLCanvas& win) const
204 {
205 if ( !wglMakeCurrent(win.GetHDC(), m_glContext) )
206 {
207 wxLogLastError(_T("wglMakeCurrent"));
208 return false;
209 }
210 return true;
211 }
212
213 // ============================================================================
214 // wxGLCanvas
215 // ============================================================================
216
217 IMPLEMENT_CLASS(wxGLCanvas, wxWindow)
218
219 BEGIN_EVENT_TABLE(wxGLCanvas, wxWindow)
220 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged)
221 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette)
222 END_EVENT_TABLE()
223
224 // ----------------------------------------------------------------------------
225 // wxGLCanvas construction
226 // ----------------------------------------------------------------------------
227
228 void wxGLCanvas::Init()
229 {
230 #if WXWIN_COMPATIBILITY_2_8
231 m_glContext = NULL;
232 #endif
233 m_hDC = NULL;
234 }
235
236 wxGLCanvas::wxGLCanvas(wxWindow *parent,
237 wxWindowID id,
238 const int *attribList,
239 const wxPoint& pos,
240 const wxSize& size,
241 long style,
242 const wxString& name,
243 const wxPalette& palette)
244 {
245 Init();
246
247 (void)Create(parent, id, pos, size, style, name, attribList, palette);
248 }
249
250 wxGLCanvas::~wxGLCanvas()
251 {
252 ::ReleaseDC(GetHwnd(), m_hDC);
253 }
254
255 // Replaces wxWindow::Create functionality, since we need to use a different
256 // window class
257 bool wxGLCanvas::Create(wxWindow *parent,
258 wxWindowID id,
259 const wxPoint& pos,
260 const wxSize& size,
261 long style,
262 const wxString& name,
263 const int *attribList,
264 const wxPalette& palette)
265 {
266 wxCHECK_MSG( parent, false, wxT("can't create wxWindow without parent") );
267
268 if ( !wxGLModule::RegisterClasses() )
269 {
270 wxLogError(_("Failed to register OpenGL window class."));
271
272 return false;
273 }
274
275 if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) )
276 return false;
277
278 parent->AddChild(this);
279
280 /*
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.
285 */
286 WXDWORD exStyle = 0;
287 DWORD msflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
288 msflags |= MSWGetStyle(style, &exStyle);
289
290 if ( !MSWCreate(wxGLCanvasClassName, NULL, pos, size, msflags, exStyle) )
291 return false;
292
293 m_hDC = ::GetDC(GetHwnd());
294 if ( !m_hDC )
295 return false;
296
297 if ( !DoSetup(attribList) )
298 return false;
299
300 #if wxUSE_PALETTE
301 if ( !SetupPalette(palette) )
302 return false;
303 #else // !wxUSE_PALETTE
304 wxUnusedVar(palette);
305 #endif // wxUSE_PALETTE/!wxUSE_PALETTE
306
307 return true;
308 }
309
310 // ----------------------------------------------------------------------------
311 // operations
312 // ----------------------------------------------------------------------------
313
314 bool wxGLCanvas::SwapBuffers()
315 {
316 if ( !::SwapBuffers(m_hDC) )
317 {
318 wxLogLastError(_T("SwapBuffers"));
319 return false;
320 }
321
322 return true;
323 }
324
325 // ----------------------------------------------------------------------------
326 // pixel format stuff
327 // ----------------------------------------------------------------------------
328
329 static void
330 AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR& pfd, const int *attribList)
331 {
332 if ( !attribList )
333 return;
334
335 pfd.dwFlags &= ~PFD_DOUBLEBUFFER;
336 pfd.iPixelType = PFD_TYPE_COLORINDEX;
337 pfd.cColorBits = 0;
338 int arg=0;
339
340 while ( attribList[arg] )
341 {
342 switch ( attribList[arg++] )
343 {
344 case WX_GL_RGBA:
345 pfd.iPixelType = PFD_TYPE_RGBA;
346 break;
347 case WX_GL_BUFFER_SIZE:
348 pfd.cColorBits = attribList[arg++];
349 break;
350 case WX_GL_LEVEL:
351 // this member looks like it may be obsolete
352 if ( attribList[arg] > 0 )
353 pfd.iLayerType = PFD_OVERLAY_PLANE;
354 else if ( attribList[arg] < 0 )
355 pfd.iLayerType = (BYTE)PFD_UNDERLAY_PLANE;
356 else
357 pfd.iLayerType = PFD_MAIN_PLANE;
358 arg++;
359 break;
360 case WX_GL_DOUBLEBUFFER:
361 pfd.dwFlags |= PFD_DOUBLEBUFFER;
362 break;
363 case WX_GL_STEREO:
364 pfd.dwFlags |= PFD_STEREO;
365 break;
366 case WX_GL_AUX_BUFFERS:
367 pfd.cAuxBuffers = attribList[arg++];
368 break;
369 case WX_GL_MIN_RED:
370 pfd.cColorBits = (pfd.cColorBits +
371 (pfd.cRedBits = attribList[arg++]));
372 break;
373 case WX_GL_MIN_GREEN:
374 pfd.cColorBits = (pfd.cColorBits +
375 (pfd.cGreenBits = attribList[arg++]));
376 break;
377 case WX_GL_MIN_BLUE:
378 pfd.cColorBits = (pfd.cColorBits +
379 (pfd.cBlueBits = attribList[arg++]));
380 break;
381 case WX_GL_MIN_ALPHA:
382 // doesn't count in cColorBits
383 pfd.cAlphaBits = attribList[arg++];
384 break;
385 case WX_GL_DEPTH_SIZE:
386 pfd.cDepthBits = attribList[arg++];
387 break;
388 case WX_GL_STENCIL_SIZE:
389 pfd.cStencilBits = attribList[arg++];
390 break;
391 case WX_GL_MIN_ACCUM_RED:
392 pfd.cAccumBits = (pfd.cAccumBits +
393 (pfd.cAccumRedBits = attribList[arg++]));
394 break;
395 case WX_GL_MIN_ACCUM_GREEN:
396 pfd.cAccumBits = (pfd.cAccumBits +
397 (pfd.cAccumGreenBits = attribList[arg++]));
398 break;
399 case WX_GL_MIN_ACCUM_BLUE:
400 pfd.cAccumBits = (pfd.cAccumBits +
401 (pfd.cAccumBlueBits = attribList[arg++]));
402 break;
403 case WX_GL_MIN_ACCUM_ALPHA:
404 pfd.cAccumBits = (pfd.cAccumBits +
405 (pfd.cAccumAlphaBits = attribList[arg++]));
406 break;
407 }
408 }
409 }
410
411 /* static */
412 int
413 wxGLCanvas::ChooseMatchingPixelFormat(HDC hdc,
414 const int *attribList,
415 PIXELFORMATDESCRIPTOR *ppfd)
416 {
417 // default neutral pixel format
418 PIXELFORMATDESCRIPTOR pfd =
419 {
420 sizeof(PIXELFORMATDESCRIPTOR), // size
421 1, // version
422 PFD_SUPPORT_OPENGL |
423 PFD_DRAW_TO_WINDOW |
424 PFD_DOUBLEBUFFER, // support double-buffering
425 PFD_TYPE_RGBA, // color type
426 16, // preferred color depth
427 0, 0, 0, 0, 0, 0, // color bits (ignored)
428 0, // no alpha buffer
429 0, // alpha bits (ignored)
430 0, // no accumulation buffer
431 0, 0, 0, 0, // accumulator bits (ignored)
432 16, // depth buffer
433 0, // no stencil buffer
434 0, // no auxiliary buffers
435 PFD_MAIN_PLANE, // main layer
436 0, // reserved
437 0, 0, 0, // no layer, visible, damage masks
438 };
439
440 if ( !ppfd )
441 ppfd = &pfd;
442 else
443 *ppfd = pfd;
444
445 AdjustPFDForAttributes(*ppfd, attribList);
446
447 return ::ChoosePixelFormat(hdc, ppfd);
448 }
449
450 /* static */
451 bool wxGLCanvasBase::IsDisplaySupported(const int *attribList)
452 {
453 // We need a device context to test the pixel format, so get one
454 // for the root window.
455 return wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList) > 0;
456 }
457
458 bool wxGLCanvas::DoSetup(const int *attribList)
459 {
460 PIXELFORMATDESCRIPTOR pfd;
461 const int pixelFormat = ChooseMatchingPixelFormat(m_hDC, attribList, &pfd);
462 if ( !pixelFormat )
463 {
464 wxLogLastError(_T("ChoosePixelFormat"));
465 return false;
466 }
467
468 if ( !::SetPixelFormat(m_hDC, pixelFormat, &pfd) )
469 {
470 wxLogLastError(_T("SetPixelFormat"));
471 return false;
472 }
473
474 return true;
475 }
476
477 // ----------------------------------------------------------------------------
478 // palette stuff
479 // ----------------------------------------------------------------------------
480
481 #if wxUSE_PALETTE
482
483 bool wxGLCanvas::SetupPalette(const wxPalette& palette)
484 {
485 const int pixelFormat = ::GetPixelFormat(m_hDC);
486 if ( !pixelFormat )
487 {
488 wxLogLastError(_T("GetPixelFormat"));
489 return false;
490 }
491
492 PIXELFORMATDESCRIPTOR pfd;
493 if ( !::DescribePixelFormat(m_hDC, pixelFormat, sizeof(pfd), &pfd) )
494 {
495 wxLogLastError(_T("DescribePixelFormat"));
496 return false;
497 }
498
499 if ( !(pfd.dwFlags & PFD_NEED_PALETTE) )
500 return true;
501
502 m_palette = palette;
503
504 if ( !m_palette.Ok() )
505 {
506 m_palette = CreateDefaultPalette();
507 if ( !m_palette.Ok() )
508 return false;
509 }
510
511 if ( !::SelectPalette(m_hDC, GetHpaletteOf(m_palette), FALSE) )
512 {
513 wxLogLastError(_T("SelectPalette"));
514 return false;
515 }
516
517 if ( ::RealizePalette(m_hDC) == GDI_ERROR )
518 {
519 wxLogLastError(_T("RealizePalette"));
520 return false;
521 }
522
523 return true;
524 }
525
526 wxPalette wxGLCanvas::CreateDefaultPalette()
527 {
528 PIXELFORMATDESCRIPTOR pfd;
529 int paletteSize;
530 int pixelFormat = GetPixelFormat(m_hDC);
531
532 DescribePixelFormat(m_hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
533
534 paletteSize = 1 << pfd.cColorBits;
535
536 LOGPALETTE* pPal =
537 (LOGPALETTE*) malloc(sizeof(LOGPALETTE) + paletteSize * sizeof(PALETTEENTRY));
538 pPal->palVersion = 0x300;
539 pPal->palNumEntries = (WORD)paletteSize;
540
541 /* build a simple RGB color palette */
542 {
543 int redMask = (1 << pfd.cRedBits) - 1;
544 int greenMask = (1 << pfd.cGreenBits) - 1;
545 int blueMask = (1 << pfd.cBlueBits) - 1;
546 int i;
547
548 for (i=0; i<paletteSize; ++i) {
549 pPal->palPalEntry[i].peRed =
550 (BYTE)((((i >> pfd.cRedShift) & redMask) * 255) / redMask);
551 pPal->palPalEntry[i].peGreen =
552 (BYTE)((((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask);
553 pPal->palPalEntry[i].peBlue =
554 (BYTE)((((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask);
555 pPal->palPalEntry[i].peFlags = 0;
556 }
557 }
558
559 HPALETTE hPalette = CreatePalette(pPal);
560 free(pPal);
561
562 wxPalette palette;
563 palette.SetHPALETTE((WXHPALETTE) hPalette);
564
565 return palette;
566 }
567
568 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent& event)
569 {
570 /* realize palette if this is the current window */
571 if ( GetPalette()->Ok() ) {
572 ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE());
573 ::SelectPalette(GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE);
574 ::RealizePalette(GetHDC());
575 Refresh();
576 event.SetPaletteRealized(true);
577 }
578 else
579 event.SetPaletteRealized(false);
580 }
581
582 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent& event)
583 {
584 /* realize palette if this is *not* the current window */
585 if ( GetPalette() &&
586 GetPalette()->Ok() && (this != event.GetChangedWindow()) )
587 {
588 ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE());
589 ::SelectPalette(GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE);
590 ::RealizePalette(GetHDC());
591 Refresh();
592 }
593 }
594
595 #endif // wxUSE_PALETTE
596
597 // ----------------------------------------------------------------------------
598 // deprecated wxGLCanvas methods using implicit wxGLContext
599 // ----------------------------------------------------------------------------
600
601 // deprecated constructors creating an implicit m_glContext
602 #if WXWIN_COMPATIBILITY_2_8
603
604 wxGLCanvas::wxGLCanvas(wxWindow *parent,
605 wxWindowID id,
606 const wxPoint& pos,
607 const wxSize& size,
608 long style,
609 const wxString& name,
610 const int *attribList,
611 const wxPalette& palette)
612 {
613 Init();
614
615 if ( Create(parent, id, pos, size, style, name, attribList, palette) )
616 m_glContext = new wxGLContext(this);
617 }
618
619 wxGLCanvas::wxGLCanvas(wxWindow *parent,
620 const wxGLContext *shared,
621 wxWindowID id,
622 const wxPoint& pos,
623 const wxSize& size,
624 long style,
625 const wxString& name,
626 const int *attribList,
627 const wxPalette& palette)
628 {
629 Init();
630
631 if ( Create(parent, id, pos, size, style, name, attribList, palette) )
632 m_glContext = new wxGLContext(this, shared);
633 }
634
635 wxGLCanvas::wxGLCanvas(wxWindow *parent,
636 const wxGLCanvas *shared,
637 wxWindowID id,
638 const wxPoint& pos,
639 const wxSize& size,
640 long style,
641 const wxString& name,
642 const int *attribList,
643 const wxPalette& palette)
644 {
645 Init();
646
647 if ( Create(parent, id, pos, size, style, name, attribList, palette) )
648 m_glContext = new wxGLContext(this, shared ? shared->m_glContext : NULL);
649 }
650
651 #endif // WXWIN_COMPATIBILITY_2_8
652
653
654 // ----------------------------------------------------------------------------
655 // wxGLApp
656 // ----------------------------------------------------------------------------
657
658 bool wxGLApp::InitGLVisual(const int *attribList)
659 {
660 if ( !wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList) )
661 {
662 wxLogError(_("Failed to initialize OpenGL"));
663 return false;
664 }
665
666 return true;
667 }
668
669 #endif // wxUSE_GLCANVAS