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"
35 static const wxChar
*wxGLCanvasClassName
= wxT("wxGLCanvasClass");
36 static const wxChar
*wxGLCanvasClassNameNoRedraw
= wxT("wxGLCanvasClassNR");
38 LRESULT WXDLLEXPORT APIENTRY _EXPORT
wxWndProc(HWND hWnd
, UINT message
,
39 WPARAM wParam
, LPARAM lParam
);
42 * GLContext implementation
45 wxGLContext::wxGLContext(bool isRGB
, wxGLCanvas
*win
, const wxPalette
& palette
)
49 m_hDC
= win
->GetHDC();
51 m_glContext
= wglCreateContext((HDC
) m_hDC
);
52 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGl context") );
54 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
57 wxGLContext::wxGLContext(
58 bool isRGB
, wxGLCanvas
*win
,
59 const wxPalette
& palette
,
60 const wxGLContext
*other
/* for sharing display lists */
65 m_hDC
= win
->GetHDC();
67 m_glContext
= wglCreateContext((HDC
) m_hDC
);
68 wxCHECK_RET( m_glContext
, wxT("Couldn't create OpenGl context") );
71 wglShareLists( other
->m_glContext
, m_glContext
);
73 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
76 wxGLContext::~wxGLContext()
80 wglMakeCurrent(NULL
, NULL
);
81 wglDeleteContext(m_glContext
);
85 void wxGLContext::SwapBuffers()
89 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
90 ::SwapBuffers((HDC
) m_hDC
); //blits the backbuffer into DC
94 void wxGLContext::SetCurrent()
98 wglMakeCurrent((HDC
) m_hDC
, m_glContext
);
102 setupPixelFormat(hDC);
107 void wxGLContext::SetColour(const wxChar
*colour
)
112 wxColour
*col
= wxTheColourDatabase
->FindColour(colour
);
115 r
= (float)(col
->Red()/256.0);
116 g
= (float)(col
->Green()/256.0);
117 b
= (float)(col
->Blue()/256.0);
124 * wxGLCanvas implementation
127 IMPLEMENT_CLASS(wxGLCanvas
, wxWindow
)
129 BEGIN_EVENT_TABLE(wxGLCanvas
, wxWindow
)
130 EVT_SIZE(wxGLCanvas::OnSize
)
131 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged
)
132 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette
)
135 wxGLCanvas::wxGLCanvas(wxWindow
*parent
, wxWindowID id
,
136 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
137 int *attribList
, const wxPalette
& palette
) : wxWindow()
139 m_glContext
= (wxGLContext
*) NULL
;
141 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
145 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
146 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
149 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
151 SetupPixelFormat(attribList
);
152 SetupPalette(palette
);
154 m_glContext
= new wxGLContext(TRUE
, this, palette
);
157 wxGLCanvas::wxGLCanvas( wxWindow
*parent
,
158 const wxGLContext
*shared
, wxWindowID id
,
159 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
160 int *attribList
, const wxPalette
& palette
)
163 m_glContext
= (wxGLContext
*) NULL
;
165 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
169 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
170 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
173 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
175 SetupPixelFormat(attribList
);
176 SetupPalette(palette
);
178 m_glContext
= new wxGLContext(TRUE
, this, palette
, shared
);
181 // Not very useful for wxMSW, but this is to be wxGTK compliant
183 wxGLCanvas::wxGLCanvas( wxWindow
*parent
, const wxGLCanvas
*shared
, wxWindowID id
,
184 const wxPoint
& pos
, const wxSize
& size
, long style
, const wxString
& name
,
185 int *attribList
, const wxPalette
& palette
):
188 m_glContext
= (wxGLContext
*) NULL
;
190 bool ret
= Create(parent
, id
, pos
, size
, style
, name
);
194 SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
));
195 SetFont(wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT
));
198 m_hDC
= (WXHDC
) ::GetDC((HWND
) GetHWND());
200 SetupPixelFormat(attribList
);
201 SetupPalette(palette
);
203 wxGLContext
*sharedContext
=0;
204 if (shared
) sharedContext
=shared
->GetContext();
205 m_glContext
= new wxGLContext(TRUE
, this, palette
, sharedContext
);
208 wxGLCanvas::~wxGLCanvas()
213 ::ReleaseDC((HWND
) GetHWND(), (HDC
) m_hDC
);
216 // Replaces wxWindow::Create functionality, since we need to use a different
218 bool wxGLCanvas::Create(wxWindow
*parent
,
223 const wxString
& name
)
225 static bool s_registeredGLCanvasClass
= FALSE
;
227 // We have to register a special window class because we need
228 // the CS_OWNDC style for GLCanvas.
231 From Angel Popov <jumpo@bitex.com>
233 Here are two snips from a dicussion in the OpenGL Gamedev list that explains
234 how this problem can be fixed:
236 "There are 5 common DCs available in Win95. These are aquired when you call
237 GetDC or GetDCEx from a window that does _not_ have the OWNDC flag.
238 OWNDC flagged windows do not get their DC from the common DC pool, the issue
239 is they require 800 bytes each from the limited 64Kb local heap for GDI."
241 "The deal is, if you hold onto one of the 5 shared DC's too long (as GL apps
242 do), Win95 will actually "steal" it from you. MakeCurrent fails,
243 apparently, because Windows re-assigns the HDC to a different window. The
244 only way to prevent this, the only reliable means, is to set CS_OWNDC."
247 if (!s_registeredGLCanvasClass
)
251 // the fields which are common to all classes
252 wndclass
.lpfnWndProc
= (WNDPROC
)wxWndProc
;
253 wndclass
.cbClsExtra
= 0;
254 wndclass
.cbWndExtra
= sizeof( DWORD
); // VZ: what is this DWORD used for?
255 wndclass
.hInstance
= wxhInstance
;
256 wndclass
.hIcon
= (HICON
) NULL
;
257 wndclass
.hCursor
= ::LoadCursor((HINSTANCE
)NULL
, IDC_ARROW
);
258 wndclass
.lpszMenuName
= NULL
;
260 // Register the GLCanvas class name
261 wndclass
.hbrBackground
= (HBRUSH
)NULL
;
262 wndclass
.lpszClassName
= wxGLCanvasClassName
;
263 wndclass
.style
= CS_HREDRAW
| CS_VREDRAW
| CS_DBLCLKS
| CS_OWNDC
;
265 if ( !::RegisterClass(&wndclass
) )
267 wxLogLastError(wxT("RegisterClass(wxGLCanvasClass)"));
271 // Register the GLCanvas class name for windows which don't do full repaint
273 wndclass
.lpszClassName
= wxGLCanvasClassNameNoRedraw
;
274 wndclass
.style
&= ~(CS_HREDRAW
| CS_VREDRAW
);
276 if ( !::RegisterClass(&wndclass
) )
278 wxLogLastError(wxT("RegisterClass(wxGLCanvasClassNameNoRedraw)"));
280 ::UnregisterClass(wxGLCanvasClassName
, wxhInstance
);
285 s_registeredGLCanvasClass
= TRUE
;
288 wxCHECK_MSG( parent
, FALSE
, wxT("can't create wxWindow without parent") );
290 if ( !CreateBase(parent
, id
, pos
, size
, style
, wxDefaultValidator
, name
) )
293 parent
->AddChild(this);
296 if ( style
& wxBORDER
)
297 msflags
|= WS_BORDER
;
298 if ( style
& wxTHICK_FRAME
)
299 msflags
|= WS_THICKFRAME
;
302 A general rule with OpenGL and Win32 is that any window that will have a
303 HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
304 You can find references about this within the knowledge base and most OpenGL
305 books that contain the wgl function descriptions.
308 msflags
|= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
311 WXDWORD exStyle
= Determine3DEffects(WS_EX_CLIENTEDGE
, &want3D
);
313 // Even with extended styles, need to combine with WS_BORDER
314 // for them to look right.
315 if ( want3D
|| (m_windowStyle
& wxSIMPLE_BORDER
) || (m_windowStyle
& wxRAISED_BORDER
) ||
316 (m_windowStyle
& wxSUNKEN_BORDER
) || (m_windowStyle
& wxDOUBLE_BORDER
))
318 msflags
|= WS_BORDER
;
321 return MSWCreate(wxGLCanvasClassName
, NULL
, pos
, size
, msflags
, exStyle
);
324 static void AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR
& pfd
, int *attribList
)
327 pfd
.dwFlags
&= ~PFD_DOUBLEBUFFER
;
328 pfd
.iPixelType
= PFD_TYPE_COLORINDEX
;
332 while( (attribList
[arg
]!=0) )
334 switch( attribList
[arg
++] )
337 pfd
.iPixelType
= PFD_TYPE_RGBA
;
339 case WX_GL_BUFFER_SIZE
:
340 pfd
.cColorBits
= attribList
[arg
++];
343 // this member looks like it may be obsolete
344 if (attribList
[arg
] > 0) {
345 pfd
.iLayerType
= (BYTE
)PFD_OVERLAY_PLANE
;
346 } else if (attribList
[arg
] < 0) {
347 pfd
.iLayerType
= (BYTE
)PFD_UNDERLAY_PLANE
;
349 pfd
.iLayerType
= (BYTE
)PFD_MAIN_PLANE
;
353 case WX_GL_DOUBLEBUFFER
:
354 pfd
.dwFlags
|= PFD_DOUBLEBUFFER
;
357 pfd
.dwFlags
|= PFD_STEREO
;
359 case WX_GL_AUX_BUFFERS
:
360 pfd
.cAuxBuffers
= attribList
[arg
++];
363 pfd
.cColorBits
+= (pfd
.cRedBits
= attribList
[arg
++]);
365 case WX_GL_MIN_GREEN
:
366 pfd
.cColorBits
+= (pfd
.cGreenBits
= attribList
[arg
++]);
369 pfd
.cColorBits
+= (pfd
.cBlueBits
= attribList
[arg
++]);
371 case WX_GL_MIN_ALPHA
:
372 // doesn't count in cColorBits
373 pfd
.cAlphaBits
= attribList
[arg
++];
375 case WX_GL_DEPTH_SIZE
:
376 pfd
.cDepthBits
= attribList
[arg
++];
378 case WX_GL_STENCIL_SIZE
:
379 pfd
.cStencilBits
= attribList
[arg
++];
381 case WX_GL_MIN_ACCUM_RED
:
382 pfd
.cAccumBits
+= (pfd
.cAccumRedBits
= attribList
[arg
++]);
384 case WX_GL_MIN_ACCUM_GREEN
:
385 pfd
.cAccumBits
+= (pfd
.cAccumGreenBits
= attribList
[arg
++]);
387 case WX_GL_MIN_ACCUM_BLUE
:
388 pfd
.cAccumBits
+= (pfd
.cAccumBlueBits
= attribList
[arg
++]);
390 case WX_GL_MIN_ACCUM_ALPHA
:
391 pfd
.cAccumBits
+= (pfd
.cAccumAlphaBits
= attribList
[arg
++]);
400 void wxGLCanvas::SetupPixelFormat(int *attribList
) // (HDC hDC)
402 PIXELFORMATDESCRIPTOR pfd
= {
403 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
407 PFD_DOUBLEBUFFER
, /* support double-buffering */
408 PFD_TYPE_RGBA
, /* color type */
409 16, /* prefered color depth */
410 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
411 0, /* no alpha buffer */
412 0, /* alpha bits (ignored) */
413 0, /* no accumulation buffer */
414 0, 0, 0, 0, /* accum bits (ignored) */
415 16, /* depth buffer */
416 0, /* no stencil buffer */
417 0, /* no auxiliary buffers */
418 PFD_MAIN_PLANE
, /* main layer */
420 0, 0, 0, /* no layer, visible, damage masks */
423 AdjustPFDForAttributes(pfd
, attribList
);
425 int pixelFormat
= ChoosePixelFormat((HDC
) m_hDC
, &pfd
);
426 if (pixelFormat
== 0) {
427 wxLogLastError(_T("ChoosePixelFormat"));
430 if ( !::SetPixelFormat((HDC
) m_hDC
, pixelFormat
, &pfd
) ) {
431 wxLogLastError(_T("SetPixelFormat"));
436 void wxGLCanvas::SetupPalette(const wxPalette
& palette
)
438 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
439 PIXELFORMATDESCRIPTOR pfd
;
441 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
443 if (pfd
.dwFlags
& PFD_NEED_PALETTE
)
453 if ( !m_palette
.Ok() )
455 m_palette
= CreateDefaultPalette();
460 SelectPalette((HDC
) m_hDC
, (HPALETTE
) m_palette
.GetHPALETTE(), FALSE
);
461 RealizePalette((HDC
) m_hDC
);
465 wxPalette
wxGLCanvas::CreateDefaultPalette()
467 PIXELFORMATDESCRIPTOR pfd
;
469 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
471 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
473 paletteSize
= 1 << pfd
.cColorBits
;
476 (LOGPALETTE
*) malloc(sizeof(LOGPALETTE
) + paletteSize
* sizeof(PALETTEENTRY
));
477 pPal
->palVersion
= 0x300;
478 pPal
->palNumEntries
= paletteSize
;
480 /* build a simple RGB color palette */
482 int redMask
= (1 << pfd
.cRedBits
) - 1;
483 int greenMask
= (1 << pfd
.cGreenBits
) - 1;
484 int blueMask
= (1 << pfd
.cBlueBits
) - 1;
487 for (i
=0; i
<paletteSize
; ++i
) {
488 pPal
->palPalEntry
[i
].peRed
=
489 (((i
>> pfd
.cRedShift
) & redMask
) * 255) / redMask
;
490 pPal
->palPalEntry
[i
].peGreen
=
491 (((i
>> pfd
.cGreenShift
) & greenMask
) * 255) / greenMask
;
492 pPal
->palPalEntry
[i
].peBlue
=
493 (((i
>> pfd
.cBlueShift
) & blueMask
) * 255) / blueMask
;
494 pPal
->palPalEntry
[i
].peFlags
= 0;
498 HPALETTE hPalette
= CreatePalette(pPal
);
502 palette
.SetHPALETTE((WXHPALETTE
) hPalette
);
507 void wxGLCanvas::SwapBuffers()
510 m_glContext
->SwapBuffers();
513 void wxGLCanvas::OnSize(wxSizeEvent
& event
)
517 void wxGLCanvas::SetCurrent()
521 m_glContext
->SetCurrent();
525 void wxGLCanvas::SetColour(const wxChar
*colour
)
528 m_glContext
->SetColour(colour
);
531 // TODO: Have to have this called by parent frame (?)
532 // So we need wxFrame to call OnQueryNewPalette for all children...
533 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent
& event
)
535 /* realize palette if this is the current window */
536 if ( GetPalette()->Ok() ) {
537 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
538 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
539 ::RealizePalette((HDC
) GetHDC());
541 event
.SetPaletteRealized(TRUE
);
544 event
.SetPaletteRealized(FALSE
);
547 // I think this doesn't have to be propagated to child windows.
548 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent
& event
)
550 /* realize palette if this is *not* the current window */
552 GetPalette()->Ok() && (this != event
.GetChangedWindow()) )
554 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
555 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
556 ::RealizePalette((HDC
) GetHDC());
561 /* Give extensions proper function names. */
563 /* EXT_vertex_array */
564 void glArrayElementEXT(GLint i
)
568 void glColorPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
572 void glDrawArraysEXT(GLenum mode
, GLint first
, GLsizei count
)
574 #ifdef GL_EXT_vertex_array
575 static PFNGLDRAWARRAYSEXTPROC proc
= 0;
579 proc
= (PFNGLDRAWARRAYSEXTPROC
) wglGetProcAddress("glDrawArraysEXT");
583 (* proc
) (mode
, first
, count
);
587 void glEdgeFlagPointerEXT(GLsizei stride
, GLsizei count
, const GLboolean
*pointer
)
591 void glGetPointervEXT(GLenum pname
, GLvoid
* *params
)
595 void glIndexPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
599 void glNormalPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
601 #ifdef GL_EXT_vertex_array
602 static PFNGLNORMALPOINTEREXTPROC proc
= 0;
606 proc
= (PFNGLNORMALPOINTEREXTPROC
) wglGetProcAddress("glNormalPointerEXT");
610 (* proc
) (type
, stride
, count
, pointer
);
614 void glTexCoordPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
618 void glVertexPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
620 #ifdef GL_EXT_vertex_array
621 static PFNGLVERTEXPOINTEREXTPROC proc
= 0;
625 proc
= (PFNGLVERTEXPOINTEREXTPROC
) wglGetProcAddress("glVertexPointerEXT");
628 (* proc
) (size
, type
, stride
, count
, pointer
);
632 /* EXT_color_subtable */
633 void glColorSubtableEXT(GLenum target
, GLsizei start
, GLsizei count
, GLenum format
, GLenum type
, const GLvoid
*table
)
637 /* EXT_color_table */
638 void glColorTableEXT(GLenum target
, GLenum internalformat
, GLsizei width
, GLenum format
, GLenum type
, const GLvoid
*table
)
642 void glCopyColorTableEXT(GLenum target
, GLenum internalformat
, GLint x
, GLint y
, GLsizei width
)
646 void glGetColorTableEXT(GLenum target
, GLenum format
, GLenum type
, GLvoid
*table
)
650 void glGetColorTableParamaterfvEXT(GLenum target
, GLenum pname
, GLfloat
*params
)
654 void glGetColorTavleParameterivEXT(GLenum target
, GLenum pname
, GLint
*params
)
658 /* SGI_compiled_vertex_array */
659 void glLockArraysSGI(GLint first
, GLsizei count
)
663 void glUnlockArraysSGI()
668 /* SGI_cull_vertex */
669 void glCullParameterdvSGI(GLenum pname
, GLdouble
* params
)
673 void glCullParameterfvSGI(GLenum pname
, GLfloat
* params
)
678 void glIndexFuncSGI(GLenum func
, GLclampf ref
)
682 /* SGI_index_material */
683 void glIndexMaterialSGI(GLenum face
, GLenum mode
)
688 void glAddSwapHintRectWin(GLint x
, GLint y
, GLsizei width
, GLsizei height
)
693 //---------------------------------------------------------------------------
695 //---------------------------------------------------------------------------
697 IMPLEMENT_CLASS(wxGLApp
, wxApp
)
699 bool wxGLApp::InitGLVisual(int *attribList
)
702 PIXELFORMATDESCRIPTOR pfd
= {
703 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
707 PFD_DOUBLEBUFFER
, /* support double-buffering */
708 PFD_TYPE_RGBA
, /* color type */
709 16, /* prefered color depth */
710 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
711 0, /* no alpha buffer */
712 0, /* alpha bits (ignored) */
713 0, /* no accumulation buffer */
714 0, 0, 0, 0, /* accum bits (ignored) */
715 16, /* depth buffer */
716 0, /* no stencil buffer */
717 0, /* no auxiliary buffers */
718 PFD_MAIN_PLANE
, /* main layer */
720 0, 0, 0, /* no layer, visible, damage masks */
723 AdjustPFDForAttributes(pfd
, attribList
);
725 // use DC for whole (root) screen, since no windows have yet been created
726 pixelFormat
= ChoosePixelFormat(ScreenHDC(), &pfd
);
728 if (pixelFormat
== 0) {
729 wxLogError(_("Failed to initialize OpenGL"));