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 #include "wx/glcanvas.h"
36 The following two compiler directives are specific to the Microsoft Visual
37 C++ family of compilers
39 Fundementally what they do is instruct the linker to use these two libraries
40 for the resolution of symbols. In essence, this is the equivalent of adding
41 these two libraries to either the Makefile or project file.
43 This is NOT a recommended technique, and certainly is unlikely to be used
44 anywhere else in wxWindows given it is so specific to not only wxMSW, but
45 also the VC compiler. However, in the case of opengl support, it's an
46 applicable technique as opengl is optional in setup.h This code (wrapped by
47 wxUSE_GLCANVAS), now allows opengl support to be added purely by modifying
48 setup.h rather than by having to modify either the project or DSP fle.
50 See MSDN for further information on the exact usage of these commands.
53 # pragma comment( lib, "opengl32" )
54 # pragma comment( lib, "glu32" )
57 static const wxChar
*wxGLCanvasClassName
= wxT("wxGLCanvasClass");
58 static const wxChar
*wxGLCanvasClassNameNoRedraw
= wxT("wxGLCanvasClassNR");
60 LRESULT WXDLLEXPORT APIENTRY _EXPORT
wxWndProc(HWND hWnd
, UINT message
,
61 WPARAM wParam
, LPARAM lParam
);
64 * GLContext implementation
67 wxGLContext::wxGLContext(bool isRGB
, wxGLCanvas
*win
, const wxPalette
& palette
)
71 m_hDC
= win
->GetHDC();
73 m_glContext
= wglCreateContext((HDC
) m_hDC
);
74 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGl context") );
76 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
79 wxGLContext::wxGLContext(
80 bool isRGB
, wxGLCanvas
*win
,
81 const wxPalette
& palette
,
82 const wxGLContext
*other
/* for sharing display lists */
87 m_hDC
= win
->GetHDC();
89 m_glContext
= wglCreateContext((HDC
) m_hDC
);
90 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGl context") );
93 wglShareLists( other
->m_glContext
, m_glContext
);
95 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
98 wxGLContext::~wxGLContext()
102 wglMakeCurrent(NULL
, NULL
);
103 wglDeleteContext(m_glContext
);
107 void wxGLContext::SwapBuffers()
111 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
112 ::SwapBuffers((HDC
) m_hDC
); //blits the backbuffer into DC
116 void wxGLContext::SetCurrent()
120 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
124 setupPixelFormat(hDC);
129 void wxGLContext::SetColour(const wxChar
*colour
)
134 wxColour
*col
= wxTheColourDatabase
->FindColour(colour
);
137 r
= (float)(col
->Red()/256.0);
138 g
= (float)(col
->Green()/256.0);
139 b
= (float)(col
->Blue()/256.0);
146 * wxGLCanvas implementation
149 IMPLEMENT_CLASS(wxGLCanvas
, wxWindow
)
151 BEGIN_EVENT_TABLE(wxGLCanvas
, wxWindow
)
152 EVT_SIZE(wxGLCanvas::OnSize
)
153 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged
)
154 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette
)
157 wxGLCanvas::wxGLCanvas(wxWindow
*parent
, wxWindowID id
,
158 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
159 int *attribList
, const wxPalette
& palette
) : wxWindow()
161 m_glContext
= (wxGLContext
*) NULL
;
163 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
167 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
168 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
171 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
173 SetupPixelFormat(attribList
);
174 SetupPalette(palette
);
176 m_glContext
= new wxGLContext(TRUE
, this, palette
);
179 wxGLCanvas::wxGLCanvas( wxWindow
*parent
,
180 const wxGLContext
*shared
, wxWindowID id
,
181 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
182 int *attribList
, const wxPalette
& palette
)
185 m_glContext
= (wxGLContext
*) NULL
;
187 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
191 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
192 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
195 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
197 SetupPixelFormat(attribList
);
198 SetupPalette(palette
);
200 m_glContext
= new wxGLContext(TRUE
, this, palette
, shared
);
203 // Not very useful for wxMSW, but this is to be wxGTK compliant
205 wxGLCanvas::wxGLCanvas( wxWindow
*parent
, const wxGLCanvas
*shared
, wxWindowID id
,
206 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
207 int *attribList
, const wxPalette
& palette
):
210 m_glContext
= (wxGLContext
*) NULL
;
212 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
216 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
217 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
220 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
222 SetupPixelFormat(attribList
);
223 SetupPalette(palette
);
225 wxGLContext
*sharedContext
=0;
226 if (shared
) sharedContext
=shared
->GetContext();
227 m_glContext
= new wxGLContext(TRUE
, this, palette
, sharedContext
);
230 wxGLCanvas::~wxGLCanvas()
235 ::ReleaseDC((HWND
) GetHWND(), (HDC
) m_hDC
);
238 // Replaces wxWindow::Create functionality, since we need to use a different
240 bool wxGLCanvas::Create(wxWindow
*parent
,
245 const wxString
& name
)
247 static bool s_registeredGLCanvasClass
= FALSE
;
249 // We have to register a special window class because we need
250 // the CS_OWNDC style for GLCanvas.
253 From Angel Popov <jumpo@bitex.com>
255 Here are two snips from a dicussion in the OpenGL Gamedev list that explains
256 how this problem can be fixed:
258 "There are 5 common DCs available in Win95. These are aquired when you call
259 GetDC or GetDCEx from a window that does _not_ have the OWNDC flag.
260 OWNDC flagged windows do not get their DC from the common DC pool, the issue
261 is they require 800 bytes each from the limited 64Kb local heap for GDI."
263 "The deal is, if you hold onto one of the 5 shared DC's too long (as GL apps
264 do), Win95 will actually "steal" it from you. MakeCurrent fails,
265 apparently, because Windows re-assigns the HDC to a different window. The
266 only way to prevent this, the only reliable means, is to set CS_OWNDC."
269 if (!s_registeredGLCanvasClass
)
273 // the fields which are common to all classes
274 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
275 wndclass
.cbClsExtra
= 0;
276 wndclass
.cbWndExtra
= sizeof( DWORD
); // VZ: what is this DWORD used for?
277 wndclass
.hInstance
= wxhInstance
;
278 wndclass
.hIcon
= (HICON
) NULL
;
279 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
280 wndclass
.lpszMenuName
= NULL
;
282 // Register the GLCanvas class name
283 wndclass
.hbrBackground
= (HBRUSH
)NULL
;
284 wndclass
.lpszClassName
= wxGLCanvasClassName
;
285 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| CS_OWNDC
;
287 if ( !::RegisterClass(&wndclass
) )
289 wxLogLastError(wxT("RegisterClass(wxGLCanvasClass)"));
293 // Register the GLCanvas class name for windows which don't do full repaint
295 wndclass
.lpszClassName
= wxGLCanvasClassNameNoRedraw
;
296 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
298 if ( !::RegisterClass(&wndclass
) )
300 wxLogLastError(wxT("RegisterClass(wxGLCanvasClassNameNoRedraw)"));
302 ::UnregisterClass(wxGLCanvasClassName
, wxhInstance
);
307 s_registeredGLCanvasClass
= TRUE
;
310 wxCHECK_MSG( parent
, FALSE
, wxT("can't create wxWindow without parent") );
312 if ( !CreateBase(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) )
315 parent
->AddChild(this);
320 A general rule with OpenGL and Win32 is that any window that will have a
321 HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
322 You can find references about this within the knowledge base and most OpenGL
323 books that contain the wgl function descriptions.
327 msflags
|= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
328 msflags
|= MSWGetStyle(style
, & exStyle
) ;
330 return MSWCreate(wxGLCanvasClassName
, NULL
, pos
, size
, msflags
, exStyle
);
333 static void AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR
& pfd
, int *attribList
)
336 pfd
.dwFlags
&= ~PFD_DOUBLEBUFFER
;
337 pfd
.iPixelType
= PFD_TYPE_COLORINDEX
;
341 while( (attribList
[arg
]!=0) )
343 switch( attribList
[arg
++] )
346 pfd
.iPixelType
= PFD_TYPE_RGBA
;
348 case WX_GL_BUFFER_SIZE
:
349 pfd
.cColorBits
= attribList
[arg
++];
352 // this member looks like it may be obsolete
353 if (attribList
[arg
] > 0) {
354 pfd
.iLayerType
= (BYTE
)PFD_OVERLAY_PLANE
;
355 } else if (attribList
[arg
] < 0) {
356 pfd
.iLayerType
= (BYTE
)PFD_UNDERLAY_PLANE
;
358 pfd
.iLayerType
= (BYTE
)PFD_MAIN_PLANE
;
362 case WX_GL_DOUBLEBUFFER
:
363 pfd
.dwFlags
|= PFD_DOUBLEBUFFER
;
366 pfd
.dwFlags
|= PFD_STEREO
;
368 case WX_GL_AUX_BUFFERS
:
369 pfd
.cAuxBuffers
= attribList
[arg
++];
372 pfd
.cColorBits
+= (pfd
.cRedBits
= attribList
[arg
++]);
374 case WX_GL_MIN_GREEN
:
375 pfd
.cColorBits
+= (pfd
.cGreenBits
= attribList
[arg
++]);
378 pfd
.cColorBits
+= (pfd
.cBlueBits
= attribList
[arg
++]);
380 case WX_GL_MIN_ALPHA
:
381 // doesn't count in cColorBits
382 pfd
.cAlphaBits
= attribList
[arg
++];
384 case WX_GL_DEPTH_SIZE
:
385 pfd
.cDepthBits
= attribList
[arg
++];
387 case WX_GL_STENCIL_SIZE
:
388 pfd
.cStencilBits
= attribList
[arg
++];
390 case WX_GL_MIN_ACCUM_RED
:
391 pfd
.cAccumBits
+= (pfd
.cAccumRedBits
= attribList
[arg
++]);
393 case WX_GL_MIN_ACCUM_GREEN
:
394 pfd
.cAccumBits
+= (pfd
.cAccumGreenBits
= attribList
[arg
++]);
396 case WX_GL_MIN_ACCUM_BLUE
:
397 pfd
.cAccumBits
+= (pfd
.cAccumBlueBits
= attribList
[arg
++]);
399 case WX_GL_MIN_ACCUM_ALPHA
:
400 pfd
.cAccumBits
+= (pfd
.cAccumAlphaBits
= attribList
[arg
++]);
409 void wxGLCanvas::SetupPixelFormat(int *attribList
) // (HDC hDC)
411 PIXELFORMATDESCRIPTOR pfd
= {
412 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
416 PFD_DOUBLEBUFFER
, /* support double-buffering */
417 PFD_TYPE_RGBA
, /* color type */
418 16, /* prefered color depth */
419 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
420 0, /* no alpha buffer */
421 0, /* alpha bits (ignored) */
422 0, /* no accumulation buffer */
423 0, 0, 0, 0, /* accum bits (ignored) */
424 16, /* depth buffer */
425 0, /* no stencil buffer */
426 0, /* no auxiliary buffers */
427 PFD_MAIN_PLANE
, /* main layer */
429 0, 0, 0, /* no layer, visible, damage masks */
432 AdjustPFDForAttributes(pfd
, attribList
);
434 int pixelFormat
= ChoosePixelFormat((HDC
) m_hDC
, &pfd
);
435 if (pixelFormat
== 0) {
436 wxLogLastError(_T("ChoosePixelFormat"));
439 if ( !::SetPixelFormat((HDC
) m_hDC
, pixelFormat
, &pfd
) ) {
440 wxLogLastError(_T("SetPixelFormat"));
445 void wxGLCanvas::SetupPalette(const wxPalette
& palette
)
447 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
448 PIXELFORMATDESCRIPTOR pfd
;
450 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
452 if (pfd
.dwFlags
& PFD_NEED_PALETTE
)
462 if ( !m_palette
.Ok() )
464 m_palette
= CreateDefaultPalette();
469 SelectPalette((HDC
) m_hDC
, (HPALETTE
) m_palette
.GetHPALETTE(), FALSE
);
470 RealizePalette((HDC
) m_hDC
);
474 wxPalette
wxGLCanvas::CreateDefaultPalette()
476 PIXELFORMATDESCRIPTOR pfd
;
478 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
480 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
482 paletteSize
= 1 << pfd
.cColorBits
;
485 (LOGPALETTE
*) malloc(sizeof(LOGPALETTE
) + paletteSize
* sizeof(PALETTEENTRY
));
486 pPal
->palVersion
= 0x300;
487 pPal
->palNumEntries
= paletteSize
;
489 /* build a simple RGB color palette */
491 int redMask
= (1 << pfd
.cRedBits
) - 1;
492 int greenMask
= (1 << pfd
.cGreenBits
) - 1;
493 int blueMask
= (1 << pfd
.cBlueBits
) - 1;
496 for (i
=0; i
<paletteSize
; ++i
) {
497 pPal
->palPalEntry
[i
].peRed
=
498 (((i
>> pfd
.cRedShift
) & redMask
) * 255) / redMask
;
499 pPal
->palPalEntry
[i
].peGreen
=
500 (((i
>> pfd
.cGreenShift
) & greenMask
) * 255) / greenMask
;
501 pPal
->palPalEntry
[i
].peBlue
=
502 (((i
>> pfd
.cBlueShift
) & blueMask
) * 255) / blueMask
;
503 pPal
->palPalEntry
[i
].peFlags
= 0;
507 HPALETTE hPalette
= CreatePalette(pPal
);
511 palette
.SetHPALETTE((WXHPALETTE
) hPalette
);
516 void wxGLCanvas::SwapBuffers()
519 m_glContext
->SwapBuffers();
522 void wxGLCanvas::OnSize(wxSizeEvent
& event
)
526 void wxGLCanvas::SetCurrent()
530 m_glContext
->SetCurrent();
534 void wxGLCanvas::SetColour(const wxChar
*colour
)
537 m_glContext
->SetColour(colour
);
540 // TODO: Have to have this called by parent frame (?)
541 // So we need wxFrame to call OnQueryNewPalette for all children...
542 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent
& event
)
544 /* realize palette if this is the current window */
545 if ( GetPalette()->Ok() ) {
546 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
547 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
548 ::RealizePalette((HDC
) GetHDC());
550 event
.SetPaletteRealized(TRUE
);
553 event
.SetPaletteRealized(FALSE
);
556 // I think this doesn't have to be propagated to child windows.
557 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent
& event
)
559 /* realize palette if this is *not* the current window */
561 GetPalette()->Ok() && (this != event
.GetChangedWindow()) )
563 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
564 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
565 ::RealizePalette((HDC
) GetHDC());
570 /* Give extensions proper function names. */
572 /* EXT_vertex_array */
573 void glArrayElementEXT(GLint i
)
577 void glColorPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
581 void glDrawArraysEXT(GLenum mode
, GLint first
, GLsizei count
)
583 #ifdef GL_EXT_vertex_array
584 static PFNGLDRAWARRAYSEXTPROC proc
= 0;
588 proc
= (PFNGLDRAWARRAYSEXTPROC
) wglGetProcAddress("glDrawArraysEXT");
592 (* proc
) (mode
, first
, count
);
596 void glEdgeFlagPointerEXT(GLsizei stride
, GLsizei count
, const GLboolean
*pointer
)
600 void glGetPointervEXT(GLenum pname
, GLvoid
* *params
)
604 void glIndexPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
608 void glNormalPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
610 #ifdef GL_EXT_vertex_array
611 static PFNGLNORMALPOINTEREXTPROC proc
= 0;
615 proc
= (PFNGLNORMALPOINTEREXTPROC
) wglGetProcAddress("glNormalPointerEXT");
619 (* proc
) (type
, stride
, count
, pointer
);
623 void glTexCoordPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
627 void glVertexPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
629 #ifdef GL_EXT_vertex_array
630 static PFNGLVERTEXPOINTEREXTPROC proc
= 0;
634 proc
= (PFNGLVERTEXPOINTEREXTPROC
) wglGetProcAddress("glVertexPointerEXT");
637 (* proc
) (size
, type
, stride
, count
, pointer
);
641 /* EXT_color_subtable */
642 void glColorSubtableEXT(GLenum target
, GLsizei start
, GLsizei count
, GLenum format
, GLenum type
, const GLvoid
*table
)
646 /* EXT_color_table */
647 void glColorTableEXT(GLenum target
, GLenum internalformat
, GLsizei width
, GLenum format
, GLenum type
, const GLvoid
*table
)
651 void glCopyColorTableEXT(GLenum target
, GLenum internalformat
, GLint x
, GLint y
, GLsizei width
)
655 void glGetColorTableEXT(GLenum target
, GLenum format
, GLenum type
, GLvoid
*table
)
659 void glGetColorTableParamaterfvEXT(GLenum target
, GLenum pname
, GLfloat
*params
)
663 void glGetColorTavleParameterivEXT(GLenum target
, GLenum pname
, GLint
*params
)
667 /* SGI_compiled_vertex_array */
668 void glLockArraysSGI(GLint first
, GLsizei count
)
672 void glUnlockArraysSGI()
677 /* SGI_cull_vertex */
678 void glCullParameterdvSGI(GLenum pname
, GLdouble
* params
)
682 void glCullParameterfvSGI(GLenum pname
, GLfloat
* params
)
687 void glIndexFuncSGI(GLenum func
, GLclampf ref
)
691 /* SGI_index_material */
692 void glIndexMaterialSGI(GLenum face
, GLenum mode
)
697 void glAddSwapHintRectWin(GLint x
, GLint y
, GLsizei width
, GLsizei height
)
702 //---------------------------------------------------------------------------
704 //---------------------------------------------------------------------------
706 IMPLEMENT_CLASS(wxGLApp
, wxApp
)
708 bool wxGLApp::InitGLVisual(int *attribList
)
711 PIXELFORMATDESCRIPTOR pfd
= {
712 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
716 PFD_DOUBLEBUFFER
, /* support double-buffering */
717 PFD_TYPE_RGBA
, /* color type */
718 16, /* prefered color depth */
719 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
720 0, /* no alpha buffer */
721 0, /* alpha bits (ignored) */
722 0, /* no accumulation buffer */
723 0, 0, 0, 0, /* accum bits (ignored) */
724 16, /* depth buffer */
725 0, /* no stencil buffer */
726 0, /* no auxiliary buffers */
727 PFD_MAIN_PLANE
, /* main layer */
729 0, 0, 0, /* no layer, visible, damage masks */
732 AdjustPFDForAttributes(pfd
, attribList
);
734 // use DC for whole (root) screen, since no windows have yet been created
735 pixelFormat
= ChoosePixelFormat(ScreenHDC(), &pfd
);
737 if (pixelFormat
== 0) {
738 wxLogError(_("Failed to initialize OpenGL"));