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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "glcanvas.h"
16 #include "wx/wxprec.h"
18 #if defined(__BORLANDC__)
26 #include "wx/settings.h"
32 #include "wx/msw/private.h"
34 // DLL options compatibility check:
36 WX_CHECK_BUILD_OPTIONS("wxGL")
38 #include "wx/glcanvas.h"
41 The following two compiler directives are specific to the Microsoft Visual
42 C++ family of compilers
44 Fundementally what they do is instruct the linker to use these two libraries
45 for the resolution of symbols. In essence, this is the equivalent of adding
46 these two libraries to either the Makefile or project file.
48 This is NOT a recommended technique, and certainly is unlikely to be used
49 anywhere else in wxWidgets given it is so specific to not only wxMSW, but
50 also the VC compiler. However, in the case of opengl support, it's an
51 applicable technique as opengl is optional in setup.h This code (wrapped by
52 wxUSE_GLCANVAS), now allows opengl support to be added purely by modifying
53 setup.h rather than by having to modify either the project or DSP fle.
55 See MSDN for further information on the exact usage of these commands.
58 # pragma comment( lib, "opengl32" )
59 # pragma comment( lib, "glu32" )
63 static const wxChar
*wxGLCanvasClassName
= wxT("wxGLCanvasClass");
64 static const wxChar
*wxGLCanvasClassNameNoRedraw
= wxT("wxGLCanvasClassNR");
66 LRESULT WXDLLEXPORT APIENTRY _EXPORT
wxWndProc(HWND hWnd
, UINT message
,
67 WPARAM wParam
, LPARAM lParam
);
70 * GLContext implementation
73 wxGLContext::wxGLContext(bool WXUNUSED(isRGB
), wxGLCanvas
*win
, const wxPalette
& WXUNUSED(palette
))
77 m_hDC
= win
->GetHDC();
79 m_glContext
= wglCreateContext((HDC
) m_hDC
);
80 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGL context") );
82 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
85 wxGLContext::wxGLContext(
86 bool WXUNUSED(isRGB
), wxGLCanvas
*win
,
87 const wxPalette
& WXUNUSED(palette
),
88 const wxGLContext
*other
/* for sharing display lists */
93 m_hDC
= win
->GetHDC();
95 m_glContext
= wglCreateContext((HDC
) m_hDC
);
96 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGL context") );
99 wglShareLists( other
->m_glContext
, m_glContext
);
101 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
104 wxGLContext::~wxGLContext()
108 wglMakeCurrent(NULL
, NULL
);
109 wglDeleteContext(m_glContext
);
113 void wxGLContext::SwapBuffers()
117 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
118 ::SwapBuffers((HDC
) m_hDC
); //blits the backbuffer into DC
122 void wxGLContext::SetCurrent()
126 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
130 setupPixelFormat(hDC);
135 void wxGLContext::SetColour(const wxChar
*colour
)
137 wxColour col
= wxTheColourDatabase
->Find(colour
);
140 float r
= (float)(col
.Red()/256.0);
141 float g
= (float)(col
.Green()/256.0);
142 float b
= (float)(col
.Blue()/256.0);
149 * wxGLCanvas implementation
152 IMPLEMENT_CLASS(wxGLCanvas
, wxWindow
)
154 BEGIN_EVENT_TABLE(wxGLCanvas
, wxWindow
)
155 EVT_SIZE(wxGLCanvas::OnSize
)
156 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged
)
157 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette
)
160 wxGLCanvas::wxGLCanvas(wxWindow
*parent
, wxWindowID id
,
161 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
162 int *attribList
, const wxPalette
& palette
) : wxWindow()
164 m_glContext
= (wxGLContext
*) NULL
;
166 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
170 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
173 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
175 SetupPixelFormat(attribList
);
176 SetupPalette(palette
);
178 m_glContext
= new wxGLContext(TRUE
, this, palette
);
181 wxGLCanvas::wxGLCanvas( wxWindow
*parent
,
182 const wxGLContext
*shared
, wxWindowID id
,
183 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
184 int *attribList
, const wxPalette
& palette
)
187 m_glContext
= (wxGLContext
*) NULL
;
189 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
193 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
196 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
198 SetupPixelFormat(attribList
);
199 SetupPalette(palette
);
201 m_glContext
= new wxGLContext(TRUE
, this, palette
, shared
);
204 // Not very useful for wxMSW, but this is to be wxGTK compliant
206 wxGLCanvas::wxGLCanvas( wxWindow
*parent
, const wxGLCanvas
*shared
, wxWindowID id
,
207 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
208 int *attribList
, const wxPalette
& palette
):
211 m_glContext
= (wxGLContext
*) NULL
;
213 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
217 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
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
& WXUNUSED(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
WXUNUSED(i
))
577 void glColorPointerEXT(GLint
WXUNUSED(size
), GLenum
WXUNUSED(type
), GLsizei
WXUNUSED(stride
), GLsizei
WXUNUSED(count
), const GLvoid
*WXUNUSED(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
WXUNUSED(stride
), GLsizei
WXUNUSED(count
), const GLboolean
*WXUNUSED(pointer
))
600 void glGetPointervEXT(GLenum
WXUNUSED(pname
), GLvoid
* *WXUNUSED(params
))
604 void glIndexPointerEXT(GLenum
WXUNUSED(type
), GLsizei
WXUNUSED(stride
), GLsizei
WXUNUSED(count
), const GLvoid
*WXUNUSED(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
WXUNUSED(size
), GLenum
WXUNUSED(type
), GLsizei
WXUNUSED(stride
), GLsizei
WXUNUSED(count
), const GLvoid
*WXUNUSED(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
WXUNUSED(target
), GLsizei
WXUNUSED(start
), GLsizei
WXUNUSED(count
), GLenum
WXUNUSED(format
), GLenum
WXUNUSED(type
), const GLvoid
*WXUNUSED(table
))
646 /* EXT_color_table */
647 void glColorTableEXT(GLenum
WXUNUSED(target
), GLenum
WXUNUSED(internalformat
), GLsizei
WXUNUSED(width
), GLenum
WXUNUSED(format
), GLenum
WXUNUSED(type
), const GLvoid
*WXUNUSED(table
))
651 void glCopyColorTableEXT(GLenum
WXUNUSED(target
), GLenum
WXUNUSED(internalformat
), GLint
WXUNUSED(x
), GLint
WXUNUSED(y
), GLsizei
WXUNUSED(width
))
655 void glGetColorTableEXT(GLenum
WXUNUSED(target
), GLenum
WXUNUSED(format
), GLenum
WXUNUSED(type
), GLvoid
*WXUNUSED(table
))
659 void glGetColorTableParamaterfvEXT(GLenum
WXUNUSED(target
), GLenum
WXUNUSED(pname
), GLfloat
*WXUNUSED(params
))
663 void glGetColorTavleParameterivEXT(GLenum
WXUNUSED(target
), GLenum
WXUNUSED(pname
), GLint
*WXUNUSED(params
))
667 /* SGI_compiled_vertex_array */
668 void glLockArraysSGI(GLint
WXUNUSED(first
), GLsizei
WXUNUSED(count
))
672 void glUnlockArraysSGI()
677 /* SGI_cull_vertex */
678 void glCullParameterdvSGI(GLenum
WXUNUSED(pname
), GLdouble
* WXUNUSED(params
))
682 void glCullParameterfvSGI(GLenum
WXUNUSED(pname
), GLfloat
* WXUNUSED(params
))
687 void glIndexFuncSGI(GLenum
WXUNUSED(func
), GLclampf
WXUNUSED(ref
))
691 /* SGI_index_material */
692 void glIndexMaterialSGI(GLenum
WXUNUSED(face
), GLenum
WXUNUSED(mode
))
697 void glAddSwapHintRectWin(GLint
WXUNUSED(x
), GLint
WXUNUSED(y
), GLsizei
WXUNUSED(width
), GLsizei
WXUNUSED(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"));