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