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 #include "wx/wxprec.h"
14 #if defined(__BORLANDC__)
22 #include "wx/settings.h"
26 #include "wx/module.h"
29 #include "wx/msw/private.h"
31 // DLL options compatibility check:
33 WX_CHECK_BUILD_OPTIONS("wxGL")
35 #include "wx/glcanvas.h"
37 #if GL_EXT_vertex_array
38 #define WXUNUSED_WITHOUT_GL_EXT_vertex_array(name) name
40 #define WXUNUSED_WITHOUT_GL_EXT_vertex_array(name) WXUNUSED(name)
44 The following two compiler directives are specific to the Microsoft Visual
45 C++ family of compilers
47 Fundementally what they do is instruct the linker to use these two libraries
48 for the resolution of symbols. In essence, this is the equivalent of adding
49 these two libraries to either the Makefile or project file.
51 This is NOT a recommended technique, and certainly is unlikely to be used
52 anywhere else in wxWidgets given it is so specific to not only wxMSW, but
53 also the VC compiler. However, in the case of opengl support, it's an
54 applicable technique as opengl is optional in setup.h This code (wrapped by
55 wxUSE_GLCANVAS), now allows opengl support to be added purely by modifying
56 setup.h rather than by having to modify either the project or DSP fle.
58 See MSDN for further information on the exact usage of these commands.
61 # pragma comment( lib, "opengl32" )
62 # pragma comment( lib, "glu32" )
66 static const wxChar
*wxGLCanvasClassName
= wxT("wxGLCanvasClass");
67 static const wxChar
*wxGLCanvasClassNameNoRedraw
= wxT("wxGLCanvasClassNR");
69 LRESULT WXDLLEXPORT APIENTRY _EXPORT
wxWndProc(HWND hWnd
, UINT message
,
70 WPARAM wParam
, LPARAM lParam
);
72 // ----------------------------------------------------------------------------
73 // wxGLModule is responsible for unregistering wxGLCanvasClass Windows class
74 // ----------------------------------------------------------------------------
76 class wxGLModule
: public wxModule
79 bool OnInit() { return true; }
80 void OnExit() { UnregisterClasses(); }
82 // register the GL classes if not done yet, return true if ok, false if
83 // registration failed
84 static bool RegisterClasses();
86 // unregister the classes, done automatically on program termination
87 static void UnregisterClasses();
90 // wxGLCanvas is only used from the main thread so this is MT-ok
91 static bool ms_registeredGLClasses
;
93 DECLARE_DYNAMIC_CLASS(wxGLModule
)
96 IMPLEMENT_DYNAMIC_CLASS(wxGLModule
, wxModule
)
98 bool wxGLModule::ms_registeredGLClasses
= false;
101 bool wxGLModule::RegisterClasses()
103 if (ms_registeredGLClasses
)
106 // We have to register a special window class because we need the CS_OWNDC
107 // style for GLCanvas.
110 From Angel Popov <jumpo@bitex.com>
112 Here are two snips from a dicussion in the OpenGL Gamedev list that explains
113 how this problem can be fixed:
115 "There are 5 common DCs available in Win95. These are aquired when you call
116 GetDC or GetDCEx from a window that does _not_ have the OWNDC flag.
117 OWNDC flagged windows do not get their DC from the common DC pool, the issue
118 is they require 800 bytes each from the limited 64Kb local heap for GDI."
120 "The deal is, if you hold onto one of the 5 shared DC's too long (as GL apps
121 do), Win95 will actually "steal" it from you. MakeCurrent fails,
122 apparently, because Windows re-assigns the HDC to a different window. The
123 only way to prevent this, the only reliable means, is to set CS_OWNDC."
128 // the fields which are common to all classes
129 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
130 wndclass
.cbClsExtra
= 0;
131 wndclass
.cbWndExtra
= sizeof( DWORD
); // VZ: what is this DWORD used for?
132 wndclass
.hInstance
= wxhInstance
;
133 wndclass
.hIcon
= (HICON
) NULL
;
134 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
135 wndclass
.lpszMenuName
= NULL
;
137 // Register the GLCanvas class name
138 wndclass
.hbrBackground
= (HBRUSH
)NULL
;
139 wndclass
.lpszClassName
= wxGLCanvasClassName
;
140 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| CS_OWNDC
;
142 if ( !::RegisterClass(&wndclass
) )
144 wxLogLastError(wxT("RegisterClass(wxGLCanvasClass)"));
148 // Register the GLCanvas class name for windows which don't do full repaint
150 wndclass
.lpszClassName
= wxGLCanvasClassNameNoRedraw
;
151 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
153 if ( !::RegisterClass(&wndclass
) )
155 wxLogLastError(wxT("RegisterClass(wxGLCanvasClassNameNoRedraw)"));
157 ::UnregisterClass(wxGLCanvasClassName
, wxhInstance
);
162 ms_registeredGLClasses
= true;
168 void wxGLModule::UnregisterClasses()
170 // we need to unregister the classes in case we're in a DLL which is
171 // unloaded and then loaded again because if we don't, the registration is
172 // going to fail in wxGLCanvas::Create() the next time we're loaded
173 if ( ms_registeredGLClasses
)
175 ::UnregisterClass(wxGLCanvasClassName
, wxhInstance
);
176 ::UnregisterClass(wxGLCanvasClassNameNoRedraw
, wxhInstance
);
178 ms_registeredGLClasses
= false;
183 * GLContext implementation
186 IMPLEMENT_CLASS(wxGLContext
, wxObject
)
188 wxGLContext::wxGLContext(wxGLCanvas
* win
, const wxGLContext
* other
/* for sharing display lists */)
190 m_glContext
= wglCreateContext((HDC
) win
->GetHDC());
191 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGL context") );
194 wglShareLists( other
->m_glContext
, m_glContext
);
197 wxGLContext::~wxGLContext()
199 // If this context happens to be the current context, wglDeleteContext() makes it un-current first.
200 wglDeleteContext(m_glContext
);
203 void wxGLContext::SetCurrent(const wxGLCanvas
& win
) const
205 wglMakeCurrent((HDC
) win
.GetHDC(), m_glContext
);
210 * wxGLCanvas implementation
213 IMPLEMENT_CLASS(wxGLCanvas
, wxWindow
)
215 BEGIN_EVENT_TABLE(wxGLCanvas
, wxWindow
)
216 EVT_SIZE(wxGLCanvas::OnSize
)
217 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged
)
218 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette
)
221 wxGLCanvas::wxGLCanvas(wxWindow
*parent
, wxWindowID id
, int *attribList
,
222 const wxPoint
& pos
, const wxSize
& size
, long style
,
223 const wxString
& name
, const wxPalette
& palette
) : wxWindow()
227 if (Create(parent
, id
, pos
, size
, style
, name
))
229 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
232 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
234 SetupPixelFormat(attribList
);
235 SetupPalette(palette
);
237 // This ctor does *not* create an instance of wxGLContext,
238 // m_glContext intentionally remains NULL.
241 wxGLCanvas::wxGLCanvas(wxWindow
*parent
, wxWindowID id
,
242 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
243 int *attribList
, const wxPalette
& palette
) : wxWindow()
245 m_glContext
= (wxGLContext
*) NULL
;
247 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
251 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
254 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
256 SetupPixelFormat(attribList
);
257 SetupPalette(palette
);
259 m_glContext
= new wxGLContext(this);
262 wxGLCanvas::wxGLCanvas( wxWindow
*parent
,
263 const wxGLContext
*shared
, wxWindowID id
,
264 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
265 int *attribList
, const wxPalette
& palette
)
268 m_glContext
= (wxGLContext
*) NULL
;
270 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
274 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
277 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
279 SetupPixelFormat(attribList
);
280 SetupPalette(palette
);
282 m_glContext
= new wxGLContext(this, shared
);
285 // Not very useful for wxMSW, but this is to be wxGTK compliant
287 wxGLCanvas::wxGLCanvas( wxWindow
*parent
, const wxGLCanvas
*shared
, wxWindowID id
,
288 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
289 int *attribList
, const wxPalette
& palette
):
292 m_glContext
= (wxGLContext
*) NULL
;
294 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
298 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
301 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
303 SetupPixelFormat(attribList
);
304 SetupPalette(palette
);
306 wxGLContext
*sharedContext
=0;
307 if (shared
) sharedContext
=shared
->GetContext();
308 m_glContext
= new wxGLContext(this, sharedContext
);
311 wxGLCanvas::~wxGLCanvas()
315 ::ReleaseDC((HWND
) GetHWND(), (HDC
) m_hDC
);
318 // Replaces wxWindow::Create functionality, since we need to use a different
320 bool wxGLCanvas::Create(wxWindow
*parent
,
325 const wxString
& name
)
327 wxCHECK_MSG( parent
, false, wxT("can't create wxWindow without parent") );
329 if ( !wxGLModule::RegisterClasses() )
331 wxLogError(_("Failed to register OpenGL window class."));
336 if ( !CreateBase(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) )
339 parent
->AddChild(this);
344 A general rule with OpenGL and Win32 is that any window that will have a
345 HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
346 You can find references about this within the knowledge base and most OpenGL
347 books that contain the wgl function descriptions.
351 msflags
|= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
352 msflags
|= MSWGetStyle(style
, & exStyle
) ;
354 return MSWCreate(wxGLCanvasClassName
, NULL
, pos
, size
, msflags
, exStyle
);
357 static void AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR
& pfd
, int *attribList
)
360 pfd
.dwFlags
&= ~PFD_DOUBLEBUFFER
;
361 pfd
.iPixelType
= PFD_TYPE_COLORINDEX
;
365 while( (attribList
[arg
]!=0) )
367 switch( attribList
[arg
++] )
370 pfd
.iPixelType
= PFD_TYPE_RGBA
;
372 case WX_GL_BUFFER_SIZE
:
373 pfd
.cColorBits
= (BYTE
)attribList
[arg
++];
376 // this member looks like it may be obsolete
377 if (attribList
[arg
] > 0) {
378 pfd
.iLayerType
= (BYTE
)PFD_OVERLAY_PLANE
;
379 } else if (attribList
[arg
] < 0) {
380 pfd
.iLayerType
= (BYTE
)PFD_UNDERLAY_PLANE
;
382 pfd
.iLayerType
= (BYTE
)PFD_MAIN_PLANE
;
386 case WX_GL_DOUBLEBUFFER
:
387 pfd
.dwFlags
|= PFD_DOUBLEBUFFER
;
390 pfd
.dwFlags
|= PFD_STEREO
;
392 case WX_GL_AUX_BUFFERS
:
393 pfd
.cAuxBuffers
= (BYTE
)attribList
[arg
++];
396 pfd
.cColorBits
= (BYTE
)(pfd
.cColorBits
+ (pfd
.cRedBits
= (BYTE
)attribList
[arg
++]));
398 case WX_GL_MIN_GREEN
:
399 pfd
.cColorBits
= (BYTE
)(pfd
.cColorBits
+ (pfd
.cGreenBits
= (BYTE
)attribList
[arg
++]));
402 pfd
.cColorBits
= (BYTE
)(pfd
.cColorBits
+ (pfd
.cBlueBits
= (BYTE
)attribList
[arg
++]));
404 case WX_GL_MIN_ALPHA
:
405 // doesn't count in cColorBits
406 pfd
.cAlphaBits
= (BYTE
)attribList
[arg
++];
408 case WX_GL_DEPTH_SIZE
:
409 pfd
.cDepthBits
= (BYTE
)attribList
[arg
++];
411 case WX_GL_STENCIL_SIZE
:
412 pfd
.cStencilBits
= (BYTE
)attribList
[arg
++];
414 case WX_GL_MIN_ACCUM_RED
:
415 pfd
.cAccumBits
= (BYTE
)(pfd
.cAccumBits
+ (pfd
.cAccumRedBits
= (BYTE
)attribList
[arg
++]));
417 case WX_GL_MIN_ACCUM_GREEN
:
418 pfd
.cAccumBits
= (BYTE
)(pfd
.cAccumBits
+ (pfd
.cAccumGreenBits
= (BYTE
)attribList
[arg
++]));
420 case WX_GL_MIN_ACCUM_BLUE
:
421 pfd
.cAccumBits
= (BYTE
)(pfd
.cAccumBits
+ (pfd
.cAccumBlueBits
= (BYTE
)attribList
[arg
++]));
423 case WX_GL_MIN_ACCUM_ALPHA
:
424 pfd
.cAccumBits
= (BYTE
)(pfd
.cAccumBits
+ (pfd
.cAccumAlphaBits
= (BYTE
)attribList
[arg
++]));
433 void wxGLCanvas::SetupPixelFormat(int *attribList
) // (HDC hDC)
435 PIXELFORMATDESCRIPTOR pfd
= {
436 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
440 PFD_DOUBLEBUFFER
, /* support double-buffering */
441 PFD_TYPE_RGBA
, /* color type */
442 16, /* preferred color depth */
443 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
444 0, /* no alpha buffer */
445 0, /* alpha bits (ignored) */
446 0, /* no accumulation buffer */
447 0, 0, 0, 0, /* accum bits (ignored) */
448 16, /* depth buffer */
449 0, /* no stencil buffer */
450 0, /* no auxiliary buffers */
451 PFD_MAIN_PLANE
, /* main layer */
453 0, 0, 0, /* no layer, visible, damage masks */
456 AdjustPFDForAttributes(pfd
, attribList
);
458 int pixelFormat
= ChoosePixelFormat((HDC
) m_hDC
, &pfd
);
459 if (pixelFormat
== 0) {
460 wxLogLastError(_T("ChoosePixelFormat"));
463 if ( !::SetPixelFormat((HDC
) m_hDC
, pixelFormat
, &pfd
) ) {
464 wxLogLastError(_T("SetPixelFormat"));
469 void wxGLCanvas::SetupPalette(const wxPalette
& palette
)
471 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
472 PIXELFORMATDESCRIPTOR pfd
;
474 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
476 if (pfd
.dwFlags
& PFD_NEED_PALETTE
)
486 if ( !m_palette
.Ok() )
488 m_palette
= CreateDefaultPalette();
493 ::SelectPalette((HDC
) m_hDC
, (HPALETTE
) m_palette
.GetHPALETTE(), FALSE
);
494 ::RealizePalette((HDC
) m_hDC
);
498 wxPalette
wxGLCanvas::CreateDefaultPalette()
500 PIXELFORMATDESCRIPTOR pfd
;
502 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
504 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
506 paletteSize
= 1 << pfd
.cColorBits
;
509 (LOGPALETTE
*) malloc(sizeof(LOGPALETTE
) + paletteSize
* sizeof(PALETTEENTRY
));
510 pPal
->palVersion
= 0x300;
511 pPal
->palNumEntries
= (WORD
)paletteSize
;
513 /* build a simple RGB color palette */
515 int redMask
= (1 << pfd
.cRedBits
) - 1;
516 int greenMask
= (1 << pfd
.cGreenBits
) - 1;
517 int blueMask
= (1 << pfd
.cBlueBits
) - 1;
520 for (i
=0; i
<paletteSize
; ++i
) {
521 pPal
->palPalEntry
[i
].peRed
=
522 (BYTE
)((((i
>> pfd
.cRedShift
) & redMask
) * 255) / redMask
);
523 pPal
->palPalEntry
[i
].peGreen
=
524 (BYTE
)((((i
>> pfd
.cGreenShift
) & greenMask
) * 255) / greenMask
);
525 pPal
->palPalEntry
[i
].peBlue
=
526 (BYTE
)((((i
>> pfd
.cBlueShift
) & blueMask
) * 255) / blueMask
);
527 pPal
->palPalEntry
[i
].peFlags
= 0;
531 HPALETTE hPalette
= CreatePalette(pPal
);
535 palette
.SetHPALETTE((WXHPALETTE
) hPalette
);
540 void wxGLCanvas::SwapBuffers()
542 ::SwapBuffers((HDC
) m_hDC
);
545 void wxGLCanvas::OnSize(wxSizeEvent
& WXUNUSED(event
))
549 void wxGLCanvas::SetCurrent(const wxGLContext
& RC
) const
551 // although on MSW it works even if the window is still hidden, it doesn't
552 // under wxGTK and documentation mentions that SetCurrent() can only be
553 // called for a shown window, so check it
554 wxASSERT_MSG( GetParent()->IsShown(), _T("can't make hidden GL canvas current") );
556 RC
.SetCurrent(*this);
559 void wxGLCanvas::SetCurrent()
561 // although on MSW it works even if the window is still hidden, it doesn't
562 // under wxGTK and documentation mentions that SetCurrent() can only be
563 // called for a shown window, so check it
564 wxASSERT_MSG( GetParent()->IsShown(),
565 _T("can't make hidden GL canvas current") );
569 m_glContext
->SetCurrent(*this);
573 void wxGLCanvas::SetColour(const wxChar
*colour
)
575 wxColour col
= wxTheColourDatabase
->Find(colour
);
579 float r
= (float)(col
.Red()/256.0);
580 float g
= (float)(col
.Green()/256.0);
581 float b
= (float)(col
.Blue()/256.0);
586 // TODO: Have to have this called by parent frame (?)
587 // So we need wxFrame to call OnQueryNewPalette for all children...
588 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent
& event
)
590 /* realize palette if this is the current window */
591 if ( GetPalette()->Ok() ) {
592 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
593 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
594 ::RealizePalette((HDC
) GetHDC());
596 event
.SetPaletteRealized(true);
599 event
.SetPaletteRealized(false);
602 // I think this doesn't have to be propagated to child windows.
603 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent
& event
)
605 /* realize palette if this is *not* the current window */
607 GetPalette()->Ok() && (this != event
.GetChangedWindow()) )
609 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
610 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
611 ::RealizePalette((HDC
) GetHDC());
617 //---------------------------------------------------------------------------
619 //---------------------------------------------------------------------------
621 IMPLEMENT_CLASS(wxGLApp
, wxApp
)
623 bool wxGLApp::InitGLVisual(int *attribList
)
626 PIXELFORMATDESCRIPTOR pfd
= {
627 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
631 PFD_DOUBLEBUFFER
, /* support double-buffering */
632 PFD_TYPE_RGBA
, /* color type */
633 16, /* preferred color depth */
634 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
635 0, /* no alpha buffer */
636 0, /* alpha bits (ignored) */
637 0, /* no accumulation buffer */
638 0, 0, 0, 0, /* accum bits (ignored) */
639 16, /* depth buffer */
640 0, /* no stencil buffer */
641 0, /* no auxiliary buffers */
642 PFD_MAIN_PLANE
, /* main layer */
644 0, 0, 0, /* no layer, visible, damage masks */
647 AdjustPFDForAttributes(pfd
, attribList
);
649 // use DC for whole (root) screen, since no windows have yet been created
650 pixelFormat
= ChoosePixelFormat(ScreenHDC(), &pfd
);
652 if (pixelFormat
== 0) {
653 wxLogError(_("Failed to initialize OpenGL"));