1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/glcanvas.cpp
3 // Purpose: wxGLCanvas, for using OpenGL with wxWindows under MS Windows
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "glcanvas.h"
16 #include "wx/wxprec.h"
18 #if defined(__BORLANDC__)
26 #include "wx/settings.h"
31 #include "wx/msw/private.h"
33 // DLL options compatibility check:
35 WX_CHECK_BUILD_OPTIONS("wxGL")
37 #include "wx/glcanvas.h"
40 The following two compiler directives are specific to the Microsoft Visual
41 C++ family of compilers
43 Fundementally what they do is instruct the linker to use these two libraries
44 for the resolution of symbols. In essence, this is the equivalent of adding
45 these two libraries to either the Makefile or project file.
47 This is NOT a recommended technique, and certainly is unlikely to be used
48 anywhere else in wxWindows given it is so specific to not only wxMSW, but
49 also the VC compiler. However, in the case of opengl support, it's an
50 applicable technique as opengl is optional in setup.h This code (wrapped by
51 wxUSE_GLCANVAS), now allows opengl support to be added purely by modifying
52 setup.h rather than by having to modify either the project or DSP fle.
54 See MSDN for further information on the exact usage of these commands.
57 # pragma comment( lib, "opengl32" )
58 # pragma comment( lib, "glu32" )
62 static const wxChar
*wxGLCanvasClassName
= wxT("wxGLCanvasClass");
63 static const wxChar
*wxGLCanvasClassNameNoRedraw
= wxT("wxGLCanvasClassNR");
65 LRESULT WXDLLEXPORT APIENTRY _EXPORT
wxWndProc(HWND hWnd
, UINT message
,
66 WPARAM wParam
, LPARAM lParam
);
69 * GLContext implementation
72 wxGLContext::wxGLContext(bool isRGB
, wxGLCanvas
*win
, const wxPalette
& palette
)
76 m_hDC
= win
->GetHDC();
78 m_glContext
= wglCreateContext((HDC
) m_hDC
);
79 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGl context") );
81 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
84 wxGLContext::wxGLContext(
85 bool isRGB
, wxGLCanvas
*win
,
86 const wxPalette
& palette
,
87 const wxGLContext
*other
/* for sharing display lists */
92 m_hDC
= win
->GetHDC();
94 m_glContext
= wglCreateContext((HDC
) m_hDC
);
95 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGl context") );
98 wglShareLists( other
->m_glContext
, m_glContext
);
100 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
103 wxGLContext::~wxGLContext()
107 wglMakeCurrent(NULL
, NULL
);
108 wglDeleteContext(m_glContext
);
112 void wxGLContext::SwapBuffers()
116 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
117 ::SwapBuffers((HDC
) m_hDC
); //blits the backbuffer into DC
121 void wxGLContext::SetCurrent()
125 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
129 setupPixelFormat(hDC);
134 void wxGLContext::SetColour(const wxChar
*colour
)
139 wxColour
*col
= wxTheColourDatabase
->FindColour(colour
);
142 r
= (float)(col
->Red()/256.0);
143 g
= (float)(col
->Green()/256.0);
144 b
= (float)(col
->Blue()/256.0);
151 * wxGLCanvas implementation
154 IMPLEMENT_CLASS(wxGLCanvas
, wxWindow
)
156 BEGIN_EVENT_TABLE(wxGLCanvas
, wxWindow
)
157 EVT_SIZE(wxGLCanvas::OnSize
)
158 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged
)
159 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette
)
162 wxGLCanvas::wxGLCanvas(wxWindow
*parent
, wxWindowID id
,
163 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
164 int *attribList
, const wxPalette
& palette
) : wxWindow()
166 m_glContext
= (wxGLContext
*) NULL
;
168 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
172 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
173 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
176 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
178 SetupPixelFormat(attribList
);
179 SetupPalette(palette
);
181 m_glContext
= new wxGLContext(TRUE
, this, palette
);
184 wxGLCanvas::wxGLCanvas( wxWindow
*parent
,
185 const wxGLContext
*shared
, wxWindowID id
,
186 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
187 int *attribList
, const wxPalette
& palette
)
190 m_glContext
= (wxGLContext
*) NULL
;
192 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
196 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
197 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
200 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
202 SetupPixelFormat(attribList
);
203 SetupPalette(palette
);
205 m_glContext
= new wxGLContext(TRUE
, this, palette
, shared
);
208 // Not very useful for wxMSW, but this is to be wxGTK compliant
210 wxGLCanvas::wxGLCanvas( wxWindow
*parent
, const wxGLCanvas
*shared
, wxWindowID id
,
211 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
212 int *attribList
, const wxPalette
& palette
):
215 m_glContext
= (wxGLContext
*) NULL
;
217 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
221 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
222 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
225 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
227 SetupPixelFormat(attribList
);
228 SetupPalette(palette
);
230 wxGLContext
*sharedContext
=0;
231 if (shared
) sharedContext
=shared
->GetContext();
232 m_glContext
= new wxGLContext(TRUE
, this, palette
, sharedContext
);
235 wxGLCanvas::~wxGLCanvas()
240 ::ReleaseDC((HWND
) GetHWND(), (HDC
) m_hDC
);
243 // Replaces wxWindow::Create functionality, since we need to use a different
245 bool wxGLCanvas::Create(wxWindow
*parent
,
250 const wxString
& name
)
252 static bool s_registeredGLCanvasClass
= FALSE
;
254 // We have to register a special window class because we need
255 // the CS_OWNDC style for GLCanvas.
258 From Angel Popov <jumpo@bitex.com>
260 Here are two snips from a dicussion in the OpenGL Gamedev list that explains
261 how this problem can be fixed:
263 "There are 5 common DCs available in Win95. These are aquired when you call
264 GetDC or GetDCEx from a window that does _not_ have the OWNDC flag.
265 OWNDC flagged windows do not get their DC from the common DC pool, the issue
266 is they require 800 bytes each from the limited 64Kb local heap for GDI."
268 "The deal is, if you hold onto one of the 5 shared DC's too long (as GL apps
269 do), Win95 will actually "steal" it from you. MakeCurrent fails,
270 apparently, because Windows re-assigns the HDC to a different window. The
271 only way to prevent this, the only reliable means, is to set CS_OWNDC."
274 if (!s_registeredGLCanvasClass
)
278 // the fields which are common to all classes
279 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
280 wndclass
.cbClsExtra
= 0;
281 wndclass
.cbWndExtra
= sizeof( DWORD
); // VZ: what is this DWORD used for?
282 wndclass
.hInstance
= wxhInstance
;
283 wndclass
.hIcon
= (HICON
) NULL
;
284 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
285 wndclass
.lpszMenuName
= NULL
;
287 // Register the GLCanvas class name
288 wndclass
.hbrBackground
= (HBRUSH
)NULL
;
289 wndclass
.lpszClassName
= wxGLCanvasClassName
;
290 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| CS_OWNDC
;
292 if ( !::RegisterClass(&wndclass
) )
294 wxLogLastError(wxT("RegisterClass(wxGLCanvasClass)"));
298 // Register the GLCanvas class name for windows which don't do full repaint
300 wndclass
.lpszClassName
= wxGLCanvasClassNameNoRedraw
;
301 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
303 if ( !::RegisterClass(&wndclass
) )
305 wxLogLastError(wxT("RegisterClass(wxGLCanvasClassNameNoRedraw)"));
307 ::UnregisterClass(wxGLCanvasClassName
, wxhInstance
);
312 s_registeredGLCanvasClass
= TRUE
;
315 wxCHECK_MSG( parent
, FALSE
, wxT("can't create wxWindow without parent") );
317 if ( !CreateBase(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) )
320 parent
->AddChild(this);
325 A general rule with OpenGL and Win32 is that any window that will have a
326 HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
327 You can find references about this within the knowledge base and most OpenGL
328 books that contain the wgl function descriptions.
332 msflags
|= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
333 msflags
|= MSWGetStyle(style
, & exStyle
) ;
335 return MSWCreate(wxGLCanvasClassName
, NULL
, pos
, size
, msflags
, exStyle
);
338 static void AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR
& pfd
, int *attribList
)
341 pfd
.dwFlags
&= ~PFD_DOUBLEBUFFER
;
342 pfd
.iPixelType
= PFD_TYPE_COLORINDEX
;
346 while( (attribList
[arg
]!=0) )
348 switch( attribList
[arg
++] )
351 pfd
.iPixelType
= PFD_TYPE_RGBA
;
353 case WX_GL_BUFFER_SIZE
:
354 pfd
.cColorBits
= attribList
[arg
++];
357 // this member looks like it may be obsolete
358 if (attribList
[arg
] > 0) {
359 pfd
.iLayerType
= (BYTE
)PFD_OVERLAY_PLANE
;
360 } else if (attribList
[arg
] < 0) {
361 pfd
.iLayerType
= (BYTE
)PFD_UNDERLAY_PLANE
;
363 pfd
.iLayerType
= (BYTE
)PFD_MAIN_PLANE
;
367 case WX_GL_DOUBLEBUFFER
:
368 pfd
.dwFlags
|= PFD_DOUBLEBUFFER
;
371 pfd
.dwFlags
|= PFD_STEREO
;
373 case WX_GL_AUX_BUFFERS
:
374 pfd
.cAuxBuffers
= attribList
[arg
++];
377 pfd
.cColorBits
+= (pfd
.cRedBits
= attribList
[arg
++]);
379 case WX_GL_MIN_GREEN
:
380 pfd
.cColorBits
+= (pfd
.cGreenBits
= attribList
[arg
++]);
383 pfd
.cColorBits
+= (pfd
.cBlueBits
= attribList
[arg
++]);
385 case WX_GL_MIN_ALPHA
:
386 // doesn't count in cColorBits
387 pfd
.cAlphaBits
= attribList
[arg
++];
389 case WX_GL_DEPTH_SIZE
:
390 pfd
.cDepthBits
= attribList
[arg
++];
392 case WX_GL_STENCIL_SIZE
:
393 pfd
.cStencilBits
= attribList
[arg
++];
395 case WX_GL_MIN_ACCUM_RED
:
396 pfd
.cAccumBits
+= (pfd
.cAccumRedBits
= attribList
[arg
++]);
398 case WX_GL_MIN_ACCUM_GREEN
:
399 pfd
.cAccumBits
+= (pfd
.cAccumGreenBits
= attribList
[arg
++]);
401 case WX_GL_MIN_ACCUM_BLUE
:
402 pfd
.cAccumBits
+= (pfd
.cAccumBlueBits
= attribList
[arg
++]);
404 case WX_GL_MIN_ACCUM_ALPHA
:
405 pfd
.cAccumBits
+= (pfd
.cAccumAlphaBits
= attribList
[arg
++]);
414 void wxGLCanvas::SetupPixelFormat(int *attribList
) // (HDC hDC)
416 PIXELFORMATDESCRIPTOR pfd
= {
417 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
421 PFD_DOUBLEBUFFER
, /* support double-buffering */
422 PFD_TYPE_RGBA
, /* color type */
423 16, /* prefered 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, /* accum bits (ignored) */
429 16, /* depth buffer */
430 0, /* no stencil buffer */
431 0, /* no auxiliary buffers */
432 PFD_MAIN_PLANE
, /* main layer */
434 0, 0, 0, /* no layer, visible, damage masks */
437 AdjustPFDForAttributes(pfd
, attribList
);
439 int pixelFormat
= ChoosePixelFormat((HDC
) m_hDC
, &pfd
);
440 if (pixelFormat
== 0) {
441 wxLogLastError(_T("ChoosePixelFormat"));
444 if ( !::SetPixelFormat((HDC
) m_hDC
, pixelFormat
, &pfd
) ) {
445 wxLogLastError(_T("SetPixelFormat"));
450 void wxGLCanvas::SetupPalette(const wxPalette
& palette
)
452 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
453 PIXELFORMATDESCRIPTOR pfd
;
455 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
457 if (pfd
.dwFlags
& PFD_NEED_PALETTE
)
467 if ( !m_palette
.Ok() )
469 m_palette
= CreateDefaultPalette();
474 SelectPalette((HDC
) m_hDC
, (HPALETTE
) m_palette
.GetHPALETTE(), FALSE
);
475 RealizePalette((HDC
) m_hDC
);
479 wxPalette
wxGLCanvas::CreateDefaultPalette()
481 PIXELFORMATDESCRIPTOR pfd
;
483 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
485 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
487 paletteSize
= 1 << pfd
.cColorBits
;
490 (LOGPALETTE
*) malloc(sizeof(LOGPALETTE
) + paletteSize
* sizeof(PALETTEENTRY
));
491 pPal
->palVersion
= 0x300;
492 pPal
->palNumEntries
= paletteSize
;
494 /* build a simple RGB color palette */
496 int redMask
= (1 << pfd
.cRedBits
) - 1;
497 int greenMask
= (1 << pfd
.cGreenBits
) - 1;
498 int blueMask
= (1 << pfd
.cBlueBits
) - 1;
501 for (i
=0; i
<paletteSize
; ++i
) {
502 pPal
->palPalEntry
[i
].peRed
=
503 (((i
>> pfd
.cRedShift
) & redMask
) * 255) / redMask
;
504 pPal
->palPalEntry
[i
].peGreen
=
505 (((i
>> pfd
.cGreenShift
) & greenMask
) * 255) / greenMask
;
506 pPal
->palPalEntry
[i
].peBlue
=
507 (((i
>> pfd
.cBlueShift
) & blueMask
) * 255) / blueMask
;
508 pPal
->palPalEntry
[i
].peFlags
= 0;
512 HPALETTE hPalette
= CreatePalette(pPal
);
516 palette
.SetHPALETTE((WXHPALETTE
) hPalette
);
521 void wxGLCanvas::SwapBuffers()
524 m_glContext
->SwapBuffers();
527 void wxGLCanvas::OnSize(wxSizeEvent
& event
)
531 void wxGLCanvas::SetCurrent()
535 m_glContext
->SetCurrent();
539 void wxGLCanvas::SetColour(const wxChar
*colour
)
542 m_glContext
->SetColour(colour
);
545 // TODO: Have to have this called by parent frame (?)
546 // So we need wxFrame to call OnQueryNewPalette for all children...
547 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent
& event
)
549 /* realize palette if this is the current window */
550 if ( GetPalette()->Ok() ) {
551 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
552 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
553 ::RealizePalette((HDC
) GetHDC());
555 event
.SetPaletteRealized(TRUE
);
558 event
.SetPaletteRealized(FALSE
);
561 // I think this doesn't have to be propagated to child windows.
562 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent
& event
)
564 /* realize palette if this is *not* the current window */
566 GetPalette()->Ok() && (this != event
.GetChangedWindow()) )
568 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
569 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
570 ::RealizePalette((HDC
) GetHDC());
575 /* Give extensions proper function names. */
577 /* EXT_vertex_array */
578 void glArrayElementEXT(GLint i
)
582 void glColorPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
586 void glDrawArraysEXT(GLenum mode
, GLint first
, GLsizei count
)
588 #ifdef GL_EXT_vertex_array
589 static PFNGLDRAWARRAYSEXTPROC proc
= 0;
593 proc
= (PFNGLDRAWARRAYSEXTPROC
) wglGetProcAddress("glDrawArraysEXT");
597 (* proc
) (mode
, first
, count
);
601 void glEdgeFlagPointerEXT(GLsizei stride
, GLsizei count
, const GLboolean
*pointer
)
605 void glGetPointervEXT(GLenum pname
, GLvoid
* *params
)
609 void glIndexPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
613 void glNormalPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
615 #ifdef GL_EXT_vertex_array
616 static PFNGLNORMALPOINTEREXTPROC proc
= 0;
620 proc
= (PFNGLNORMALPOINTEREXTPROC
) wglGetProcAddress("glNormalPointerEXT");
624 (* proc
) (type
, stride
, count
, pointer
);
628 void glTexCoordPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
632 void glVertexPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
634 #ifdef GL_EXT_vertex_array
635 static PFNGLVERTEXPOINTEREXTPROC proc
= 0;
639 proc
= (PFNGLVERTEXPOINTEREXTPROC
) wglGetProcAddress("glVertexPointerEXT");
642 (* proc
) (size
, type
, stride
, count
, pointer
);
646 /* EXT_color_subtable */
647 void glColorSubtableEXT(GLenum target
, GLsizei start
, GLsizei count
, GLenum format
, GLenum type
, const GLvoid
*table
)
651 /* EXT_color_table */
652 void glColorTableEXT(GLenum target
, GLenum internalformat
, GLsizei width
, GLenum format
, GLenum type
, const GLvoid
*table
)
656 void glCopyColorTableEXT(GLenum target
, GLenum internalformat
, GLint x
, GLint y
, GLsizei width
)
660 void glGetColorTableEXT(GLenum target
, GLenum format
, GLenum type
, GLvoid
*table
)
664 void glGetColorTableParamaterfvEXT(GLenum target
, GLenum pname
, GLfloat
*params
)
668 void glGetColorTavleParameterivEXT(GLenum target
, GLenum pname
, GLint
*params
)
672 /* SGI_compiled_vertex_array */
673 void glLockArraysSGI(GLint first
, GLsizei count
)
677 void glUnlockArraysSGI()
682 /* SGI_cull_vertex */
683 void glCullParameterdvSGI(GLenum pname
, GLdouble
* params
)
687 void glCullParameterfvSGI(GLenum pname
, GLfloat
* params
)
692 void glIndexFuncSGI(GLenum func
, GLclampf ref
)
696 /* SGI_index_material */
697 void glIndexMaterialSGI(GLenum face
, GLenum mode
)
702 void glAddSwapHintRectWin(GLint x
, GLint y
, GLsizei width
, GLsizei height
)
707 //---------------------------------------------------------------------------
709 //---------------------------------------------------------------------------
711 IMPLEMENT_CLASS(wxGLApp
, wxApp
)
713 bool wxGLApp::InitGLVisual(int *attribList
)
716 PIXELFORMATDESCRIPTOR pfd
= {
717 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
721 PFD_DOUBLEBUFFER
, /* support double-buffering */
722 PFD_TYPE_RGBA
, /* color type */
723 16, /* prefered color depth */
724 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
725 0, /* no alpha buffer */
726 0, /* alpha bits (ignored) */
727 0, /* no accumulation buffer */
728 0, 0, 0, 0, /* accum bits (ignored) */
729 16, /* depth buffer */
730 0, /* no stencil buffer */
731 0, /* no auxiliary buffers */
732 PFD_MAIN_PLANE
, /* main layer */
734 0, 0, 0, /* no layer, visible, damage masks */
737 AdjustPFDForAttributes(pfd
, attribList
);
739 // use DC for whole (root) screen, since no windows have yet been created
740 pixelFormat
= ChoosePixelFormat(ScreenHDC(), &pfd
);
742 if (pixelFormat
== 0) {
743 wxLogError(_("Failed to initialize OpenGL"));