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);
298 A general rule with OpenGL and Win32 is that any window that will have a
299 HGLRC built for it must have two flags: WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
300 You can find references about this within the knowledge base and most OpenGL
301 books that contain the wgl function descriptions.
305 msflags
|= WS_CHILD
| WS_VISIBLE
| WS_CLIPSIBLINGS
| WS_CLIPCHILDREN
;
306 msflags
|= MSWGetStyle(style
, & exStyle
) ;
308 return MSWCreate(wxGLCanvasClassName
, NULL
, pos
, size
, msflags
, exStyle
);
311 static void AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR
& pfd
, int *attribList
)
314 pfd
.dwFlags
&= ~PFD_DOUBLEBUFFER
;
315 pfd
.iPixelType
= PFD_TYPE_COLORINDEX
;
319 while( (attribList
[arg
]!=0) )
321 switch( attribList
[arg
++] )
324 pfd
.iPixelType
= PFD_TYPE_RGBA
;
326 case WX_GL_BUFFER_SIZE
:
327 pfd
.cColorBits
= attribList
[arg
++];
330 // this member looks like it may be obsolete
331 if (attribList
[arg
] > 0) {
332 pfd
.iLayerType
= (BYTE
)PFD_OVERLAY_PLANE
;
333 } else if (attribList
[arg
] < 0) {
334 pfd
.iLayerType
= (BYTE
)PFD_UNDERLAY_PLANE
;
336 pfd
.iLayerType
= (BYTE
)PFD_MAIN_PLANE
;
340 case WX_GL_DOUBLEBUFFER
:
341 pfd
.dwFlags
|= PFD_DOUBLEBUFFER
;
344 pfd
.dwFlags
|= PFD_STEREO
;
346 case WX_GL_AUX_BUFFERS
:
347 pfd
.cAuxBuffers
= attribList
[arg
++];
350 pfd
.cColorBits
+= (pfd
.cRedBits
= attribList
[arg
++]);
352 case WX_GL_MIN_GREEN
:
353 pfd
.cColorBits
+= (pfd
.cGreenBits
= attribList
[arg
++]);
356 pfd
.cColorBits
+= (pfd
.cBlueBits
= attribList
[arg
++]);
358 case WX_GL_MIN_ALPHA
:
359 // doesn't count in cColorBits
360 pfd
.cAlphaBits
= attribList
[arg
++];
362 case WX_GL_DEPTH_SIZE
:
363 pfd
.cDepthBits
= attribList
[arg
++];
365 case WX_GL_STENCIL_SIZE
:
366 pfd
.cStencilBits
= attribList
[arg
++];
368 case WX_GL_MIN_ACCUM_RED
:
369 pfd
.cAccumBits
+= (pfd
.cAccumRedBits
= attribList
[arg
++]);
371 case WX_GL_MIN_ACCUM_GREEN
:
372 pfd
.cAccumBits
+= (pfd
.cAccumGreenBits
= attribList
[arg
++]);
374 case WX_GL_MIN_ACCUM_BLUE
:
375 pfd
.cAccumBits
+= (pfd
.cAccumBlueBits
= attribList
[arg
++]);
377 case WX_GL_MIN_ACCUM_ALPHA
:
378 pfd
.cAccumBits
+= (pfd
.cAccumAlphaBits
= attribList
[arg
++]);
387 void wxGLCanvas::SetupPixelFormat(int *attribList
) // (HDC hDC)
389 PIXELFORMATDESCRIPTOR pfd
= {
390 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
394 PFD_DOUBLEBUFFER
, /* support double-buffering */
395 PFD_TYPE_RGBA
, /* color type */
396 16, /* prefered color depth */
397 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
398 0, /* no alpha buffer */
399 0, /* alpha bits (ignored) */
400 0, /* no accumulation buffer */
401 0, 0, 0, 0, /* accum bits (ignored) */
402 16, /* depth buffer */
403 0, /* no stencil buffer */
404 0, /* no auxiliary buffers */
405 PFD_MAIN_PLANE
, /* main layer */
407 0, 0, 0, /* no layer, visible, damage masks */
410 AdjustPFDForAttributes(pfd
, attribList
);
412 int pixelFormat
= ChoosePixelFormat((HDC
) m_hDC
, &pfd
);
413 if (pixelFormat
== 0) {
414 wxLogLastError(_T("ChoosePixelFormat"));
417 if ( !::SetPixelFormat((HDC
) m_hDC
, pixelFormat
, &pfd
) ) {
418 wxLogLastError(_T("SetPixelFormat"));
423 void wxGLCanvas::SetupPalette(const wxPalette
& palette
)
425 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
426 PIXELFORMATDESCRIPTOR pfd
;
428 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
430 if (pfd
.dwFlags
& PFD_NEED_PALETTE
)
440 if ( !m_palette
.Ok() )
442 m_palette
= CreateDefaultPalette();
447 SelectPalette((HDC
) m_hDC
, (HPALETTE
) m_palette
.GetHPALETTE(), FALSE
);
448 RealizePalette((HDC
) m_hDC
);
452 wxPalette
wxGLCanvas::CreateDefaultPalette()
454 PIXELFORMATDESCRIPTOR pfd
;
456 int pixelFormat
= GetPixelFormat((HDC
) m_hDC
);
458 DescribePixelFormat((HDC
) m_hDC
, pixelFormat
, sizeof(PIXELFORMATDESCRIPTOR
), &pfd
);
460 paletteSize
= 1 << pfd
.cColorBits
;
463 (LOGPALETTE
*) malloc(sizeof(LOGPALETTE
) + paletteSize
* sizeof(PALETTEENTRY
));
464 pPal
->palVersion
= 0x300;
465 pPal
->palNumEntries
= paletteSize
;
467 /* build a simple RGB color palette */
469 int redMask
= (1 << pfd
.cRedBits
) - 1;
470 int greenMask
= (1 << pfd
.cGreenBits
) - 1;
471 int blueMask
= (1 << pfd
.cBlueBits
) - 1;
474 for (i
=0; i
<paletteSize
; ++i
) {
475 pPal
->palPalEntry
[i
].peRed
=
476 (((i
>> pfd
.cRedShift
) & redMask
) * 255) / redMask
;
477 pPal
->palPalEntry
[i
].peGreen
=
478 (((i
>> pfd
.cGreenShift
) & greenMask
) * 255) / greenMask
;
479 pPal
->palPalEntry
[i
].peBlue
=
480 (((i
>> pfd
.cBlueShift
) & blueMask
) * 255) / blueMask
;
481 pPal
->palPalEntry
[i
].peFlags
= 0;
485 HPALETTE hPalette
= CreatePalette(pPal
);
489 palette
.SetHPALETTE((WXHPALETTE
) hPalette
);
494 void wxGLCanvas::SwapBuffers()
497 m_glContext
->SwapBuffers();
500 void wxGLCanvas::OnSize(wxSizeEvent
& event
)
504 void wxGLCanvas::SetCurrent()
508 m_glContext
->SetCurrent();
512 void wxGLCanvas::SetColour(const wxChar
*colour
)
515 m_glContext
->SetColour(colour
);
518 // TODO: Have to have this called by parent frame (?)
519 // So we need wxFrame to call OnQueryNewPalette for all children...
520 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent
& event
)
522 /* realize palette if this is the current window */
523 if ( GetPalette()->Ok() ) {
524 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
525 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
526 ::RealizePalette((HDC
) GetHDC());
528 event
.SetPaletteRealized(TRUE
);
531 event
.SetPaletteRealized(FALSE
);
534 // I think this doesn't have to be propagated to child windows.
535 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent
& event
)
537 /* realize palette if this is *not* the current window */
539 GetPalette()->Ok() && (this != event
.GetChangedWindow()) )
541 ::UnrealizeObject((HPALETTE
) GetPalette()->GetHPALETTE());
542 ::SelectPalette((HDC
) GetHDC(), (HPALETTE
) GetPalette()->GetHPALETTE(), FALSE
);
543 ::RealizePalette((HDC
) GetHDC());
548 /* Give extensions proper function names. */
550 /* EXT_vertex_array */
551 void glArrayElementEXT(GLint i
)
555 void glColorPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
559 void glDrawArraysEXT(GLenum mode
, GLint first
, GLsizei count
)
561 #ifdef GL_EXT_vertex_array
562 static PFNGLDRAWARRAYSEXTPROC proc
= 0;
566 proc
= (PFNGLDRAWARRAYSEXTPROC
) wglGetProcAddress("glDrawArraysEXT");
570 (* proc
) (mode
, first
, count
);
574 void glEdgeFlagPointerEXT(GLsizei stride
, GLsizei count
, const GLboolean
*pointer
)
578 void glGetPointervEXT(GLenum pname
, GLvoid
* *params
)
582 void glIndexPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
586 void glNormalPointerEXT(GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
588 #ifdef GL_EXT_vertex_array
589 static PFNGLNORMALPOINTEREXTPROC proc
= 0;
593 proc
= (PFNGLNORMALPOINTEREXTPROC
) wglGetProcAddress("glNormalPointerEXT");
597 (* proc
) (type
, stride
, count
, pointer
);
601 void glTexCoordPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
605 void glVertexPointerEXT(GLint size
, GLenum type
, GLsizei stride
, GLsizei count
, const GLvoid
*pointer
)
607 #ifdef GL_EXT_vertex_array
608 static PFNGLVERTEXPOINTEREXTPROC proc
= 0;
612 proc
= (PFNGLVERTEXPOINTEREXTPROC
) wglGetProcAddress("glVertexPointerEXT");
615 (* proc
) (size
, type
, stride
, count
, pointer
);
619 /* EXT_color_subtable */
620 void glColorSubtableEXT(GLenum target
, GLsizei start
, GLsizei count
, GLenum format
, GLenum type
, const GLvoid
*table
)
624 /* EXT_color_table */
625 void glColorTableEXT(GLenum target
, GLenum internalformat
, GLsizei width
, GLenum format
, GLenum type
, const GLvoid
*table
)
629 void glCopyColorTableEXT(GLenum target
, GLenum internalformat
, GLint x
, GLint y
, GLsizei width
)
633 void glGetColorTableEXT(GLenum target
, GLenum format
, GLenum type
, GLvoid
*table
)
637 void glGetColorTableParamaterfvEXT(GLenum target
, GLenum pname
, GLfloat
*params
)
641 void glGetColorTavleParameterivEXT(GLenum target
, GLenum pname
, GLint
*params
)
645 /* SGI_compiled_vertex_array */
646 void glLockArraysSGI(GLint first
, GLsizei count
)
650 void glUnlockArraysSGI()
655 /* SGI_cull_vertex */
656 void glCullParameterdvSGI(GLenum pname
, GLdouble
* params
)
660 void glCullParameterfvSGI(GLenum pname
, GLfloat
* params
)
665 void glIndexFuncSGI(GLenum func
, GLclampf ref
)
669 /* SGI_index_material */
670 void glIndexMaterialSGI(GLenum face
, GLenum mode
)
675 void glAddSwapHintRectWin(GLint x
, GLint y
, GLsizei width
, GLsizei height
)
680 //---------------------------------------------------------------------------
682 //---------------------------------------------------------------------------
684 IMPLEMENT_CLASS(wxGLApp
, wxApp
)
686 bool wxGLApp::InitGLVisual(int *attribList
)
689 PIXELFORMATDESCRIPTOR pfd
= {
690 sizeof(PIXELFORMATDESCRIPTOR
), /* size */
694 PFD_DOUBLEBUFFER
, /* support double-buffering */
695 PFD_TYPE_RGBA
, /* color type */
696 16, /* prefered color depth */
697 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
698 0, /* no alpha buffer */
699 0, /* alpha bits (ignored) */
700 0, /* no accumulation buffer */
701 0, 0, 0, 0, /* accum bits (ignored) */
702 16, /* depth buffer */
703 0, /* no stencil buffer */
704 0, /* no auxiliary buffers */
705 PFD_MAIN_PLANE
, /* main layer */
707 0, 0, 0, /* no layer, visible, damage masks */
710 AdjustPFDForAttributes(pfd
, attribList
);
712 // use DC for whole (root) screen, since no windows have yet been created
713 pixelFormat
= ChoosePixelFormat(ScreenHDC(), &pfd
);
715 if (pixelFormat
== 0) {
716 wxLogError(_("Failed to initialize OpenGL"));