]> git.saurik.com Git - wxWidgets.git/blob - src/msw/glcanvas.cpp
document On{Open,Save}Document()
[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 return false;
319 wxLogLastError(_T("SwapBuffers"));
320 }
321 return true;
322 }
323
324 // ----------------------------------------------------------------------------
325 // pixel format stuff
326 // ----------------------------------------------------------------------------
327
328 static void
329 AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR& pfd, const int *attribList)
330 {
331 if ( !attribList )
332 return;
333
334 pfd.dwFlags &= ~PFD_DOUBLEBUFFER;
335 pfd.iPixelType = PFD_TYPE_COLORINDEX;
336 pfd.cColorBits = 0;
337 int arg=0;
338
339 while ( attribList[arg] )
340 {
341 switch ( attribList[arg++] )
342 {
343 case WX_GL_RGBA:
344 pfd.iPixelType = PFD_TYPE_RGBA;
345 break;
346 case WX_GL_BUFFER_SIZE:
347 pfd.cColorBits = attribList[arg++];
348 break;
349 case WX_GL_LEVEL:
350 // this member looks like it may be obsolete
351 if ( attribList[arg] > 0 )
352 pfd.iLayerType = PFD_OVERLAY_PLANE;
353 else if ( attribList[arg] < 0 )
354 pfd.iLayerType = (BYTE)PFD_UNDERLAY_PLANE;
355 else
356 pfd.iLayerType = PFD_MAIN_PLANE;
357 arg++;
358 break;
359 case WX_GL_DOUBLEBUFFER:
360 pfd.dwFlags |= PFD_DOUBLEBUFFER;
361 break;
362 case WX_GL_STEREO:
363 pfd.dwFlags |= PFD_STEREO;
364 break;
365 case WX_GL_AUX_BUFFERS:
366 pfd.cAuxBuffers = attribList[arg++];
367 break;
368 case WX_GL_MIN_RED:
369 pfd.cColorBits = (pfd.cColorBits +
370 (pfd.cRedBits = attribList[arg++]));
371 break;
372 case WX_GL_MIN_GREEN:
373 pfd.cColorBits = (pfd.cColorBits +
374 (pfd.cGreenBits = attribList[arg++]));
375 break;
376 case WX_GL_MIN_BLUE:
377 pfd.cColorBits = (pfd.cColorBits +
378 (pfd.cBlueBits = attribList[arg++]));
379 break;
380 case WX_GL_MIN_ALPHA:
381 // doesn't count in cColorBits
382 pfd.cAlphaBits = attribList[arg++];
383 break;
384 case WX_GL_DEPTH_SIZE:
385 pfd.cDepthBits = attribList[arg++];
386 break;
387 case WX_GL_STENCIL_SIZE:
388 pfd.cStencilBits = attribList[arg++];
389 break;
390 case WX_GL_MIN_ACCUM_RED:
391 pfd.cAccumBits = (pfd.cAccumBits +
392 (pfd.cAccumRedBits = attribList[arg++]));
393 break;
394 case WX_GL_MIN_ACCUM_GREEN:
395 pfd.cAccumBits = (pfd.cAccumBits +
396 (pfd.cAccumGreenBits = attribList[arg++]));
397 break;
398 case WX_GL_MIN_ACCUM_BLUE:
399 pfd.cAccumBits = (pfd.cAccumBits +
400 (pfd.cAccumBlueBits = attribList[arg++]));
401 break;
402 case WX_GL_MIN_ACCUM_ALPHA:
403 pfd.cAccumBits = (pfd.cAccumBits +
404 (pfd.cAccumAlphaBits = attribList[arg++]));
405 break;
406 }
407 }
408 }
409
410 /* static */
411 int
412 wxGLCanvas::ChooseMatchingPixelFormat(HDC hdc,
413 const int *attribList,
414 PIXELFORMATDESCRIPTOR *ppfd)
415 {
416 // default neutral pixel format
417 PIXELFORMATDESCRIPTOR pfd =
418 {
419 sizeof(PIXELFORMATDESCRIPTOR), // size
420 1, // version
421 PFD_SUPPORT_OPENGL |
422 PFD_DRAW_TO_WINDOW |
423 PFD_DOUBLEBUFFER, // support double-buffering
424 PFD_TYPE_RGBA, // color type
425 16, // preferred color depth
426 0, 0, 0, 0, 0, 0, // color bits (ignored)
427 0, // no alpha buffer
428 0, // alpha bits (ignored)
429 0, // no accumulation buffer
430 0, 0, 0, 0, // accumulator bits (ignored)
431 16, // depth buffer
432 0, // no stencil buffer
433 0, // no auxiliary buffers
434 PFD_MAIN_PLANE, // main layer
435 0, // reserved
436 0, 0, 0, // no layer, visible, damage masks
437 };
438
439 if ( !ppfd )
440 ppfd = &pfd;
441 else
442 *ppfd = pfd;
443
444 AdjustPFDForAttributes(*ppfd, attribList);
445
446 return ::ChoosePixelFormat(hdc, ppfd);
447 }
448
449 /* static */
450 bool wxGLCanvasBase::IsDisplaySupported(const int *attribList)
451 {
452 // We need a device context to test the pixel format, so get one
453 // for the root window.
454 return wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList) > 0;
455 }
456
457 bool wxGLCanvas::DoSetup(const int *attribList)
458 {
459 PIXELFORMATDESCRIPTOR pfd;
460 const int pixelFormat = ChooseMatchingPixelFormat(m_hDC, attribList, &pfd);
461 if ( !pixelFormat )
462 {
463 wxLogLastError(_T("ChoosePixelFormat"));
464 return false;
465 }
466
467 if ( !::SetPixelFormat(m_hDC, pixelFormat, &pfd) )
468 {
469 wxLogLastError(_T("SetPixelFormat"));
470 return false;
471 }
472
473 return true;
474 }
475
476 // ----------------------------------------------------------------------------
477 // palette stuff
478 // ----------------------------------------------------------------------------
479
480 #if wxUSE_PALETTE
481
482 bool wxGLCanvas::SetupPalette(const wxPalette& palette)
483 {
484 const int pixelFormat = ::GetPixelFormat(m_hDC);
485 if ( !pixelFormat )
486 {
487 wxLogLastError(_T("GetPixelFormat"));
488 return false;
489 }
490
491 PIXELFORMATDESCRIPTOR pfd;
492 if ( !::DescribePixelFormat(m_hDC, pixelFormat, sizeof(pfd), &pfd) )
493 {
494 wxLogLastError(_T("DescribePixelFormat"));
495 return false;
496 }
497
498 if ( !(pfd.dwFlags & PFD_NEED_PALETTE) )
499 return true;
500
501 m_palette = palette;
502
503 if ( !m_palette.Ok() )
504 {
505 m_palette = CreateDefaultPalette();
506 if ( !m_palette.Ok() )
507 return false;
508 }
509
510 if ( !::SelectPalette(m_hDC, GetHpaletteOf(m_palette), FALSE) )
511 {
512 wxLogLastError(_T("SelectPalette"));
513 return false;
514 }
515
516 if ( ::RealizePalette(m_hDC) == GDI_ERROR )
517 {
518 wxLogLastError(_T("RealizePalette"));
519 return false;
520 }
521
522 return true;
523 }
524
525 wxPalette wxGLCanvas::CreateDefaultPalette()
526 {
527 PIXELFORMATDESCRIPTOR pfd;
528 int paletteSize;
529 int pixelFormat = GetPixelFormat(m_hDC);
530
531 DescribePixelFormat(m_hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
532
533 paletteSize = 1 << pfd.cColorBits;
534
535 LOGPALETTE* pPal =
536 (LOGPALETTE*) malloc(sizeof(LOGPALETTE) + paletteSize * sizeof(PALETTEENTRY));
537 pPal->palVersion = 0x300;
538 pPal->palNumEntries = (WORD)paletteSize;
539
540 /* build a simple RGB color palette */
541 {
542 int redMask = (1 << pfd.cRedBits) - 1;
543 int greenMask = (1 << pfd.cGreenBits) - 1;
544 int blueMask = (1 << pfd.cBlueBits) - 1;
545 int i;
546
547 for (i=0; i<paletteSize; ++i) {
548 pPal->palPalEntry[i].peRed =
549 (BYTE)((((i >> pfd.cRedShift) & redMask) * 255) / redMask);
550 pPal->palPalEntry[i].peGreen =
551 (BYTE)((((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask);
552 pPal->palPalEntry[i].peBlue =
553 (BYTE)((((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask);
554 pPal->palPalEntry[i].peFlags = 0;
555 }
556 }
557
558 HPALETTE hPalette = CreatePalette(pPal);
559 free(pPal);
560
561 wxPalette palette;
562 palette.SetHPALETTE((WXHPALETTE) hPalette);
563
564 return palette;
565 }
566
567 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent& event)
568 {
569 /* realize palette if this is the current window */
570 if ( GetPalette()->Ok() ) {
571 ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE());
572 ::SelectPalette(GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE);
573 ::RealizePalette(GetHDC());
574 Refresh();
575 event.SetPaletteRealized(true);
576 }
577 else
578 event.SetPaletteRealized(false);
579 }
580
581 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent& event)
582 {
583 /* realize palette if this is *not* the current window */
584 if ( GetPalette() &&
585 GetPalette()->Ok() && (this != event.GetChangedWindow()) )
586 {
587 ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE());
588 ::SelectPalette(GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE);
589 ::RealizePalette(GetHDC());
590 Refresh();
591 }
592 }
593
594 #endif // wxUSE_PALETTE
595
596 // ----------------------------------------------------------------------------
597 // deprecated wxGLCanvas methods using implicit wxGLContext
598 // ----------------------------------------------------------------------------
599
600 // deprecated constructors creating an implicit m_glContext
601 #if WXWIN_COMPATIBILITY_2_8
602
603 wxGLCanvas::wxGLCanvas(wxWindow *parent,
604 wxWindowID id,
605 const wxPoint& pos,
606 const wxSize& size,
607 long style,
608 const wxString& name,
609 const int *attribList,
610 const wxPalette& palette)
611 {
612 Init();
613
614 if ( Create(parent, id, pos, size, style, name, attribList, palette) )
615 m_glContext = new wxGLContext(this);
616 }
617
618 wxGLCanvas::wxGLCanvas(wxWindow *parent,
619 const wxGLContext *shared,
620 wxWindowID id,
621 const wxPoint& pos,
622 const wxSize& size,
623 long style,
624 const wxString& name,
625 const int *attribList,
626 const wxPalette& palette)
627 {
628 Init();
629
630 if ( Create(parent, id, pos, size, style, name, attribList, palette) )
631 m_glContext = new wxGLContext(this, shared);
632 }
633
634 wxGLCanvas::wxGLCanvas(wxWindow *parent,
635 const wxGLCanvas *shared,
636 wxWindowID id,
637 const wxPoint& pos,
638 const wxSize& size,
639 long style,
640 const wxString& name,
641 const int *attribList,
642 const wxPalette& palette)
643 {
644 Init();
645
646 if ( Create(parent, id, pos, size, style, name, attribList, palette) )
647 m_glContext = new wxGLContext(this, shared ? shared->m_glContext : NULL);
648 }
649
650 #endif // WXWIN_COMPATIBILITY_2_8
651
652
653 // ----------------------------------------------------------------------------
654 // wxGLApp
655 // ----------------------------------------------------------------------------
656
657 bool wxGLApp::InitGLVisual(const int *attribList)
658 {
659 if ( !wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList) )
660 {
661 wxLogError(_("Failed to initialize OpenGL"));
662 return false;
663 }
664
665 return true;
666 }
667
668 #endif // wxUSE_GLCANVAS