Moved wxGLCanvas to more normal positions
[wxWidgets.git] / src / msw / glcanvas.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: glcanvas.cpp
3 // Purpose: wxGLCanvas, for using OpenGL with wxWindows under MS Windows
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "glcanvas.h"
14 #endif
15
16 #include "wx/wxprec.h"
17
18 #if defined(__BORLANDC__)
19 #pragma hdrstop
20 #endif
21
22 #include <wx/setup.h>
23
24 #if wxUSE_GLCANVAS
25
26 #ifndef WX_PRECOMP
27 #include <wx/frame.h>
28 #endif
29
30 #include <wx/msw/private.h>
31
32 #include <wx/glcanvas.h>
33
34 wxChar wxGLCanvasClassName[] = wxT("wxGLCanvasClass");
35
36 LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message,
37 WPARAM wParam, LPARAM lParam);
38
39 /*
40 * GLContext implementation
41 */
42
43 wxGLContext::wxGLContext(bool isRGB, wxGLCanvas *win, const wxPalette& palette)
44 {
45 m_window = win;
46
47 m_hDC = win->GetHDC();
48
49 m_glContext = wglCreateContext((HDC) m_hDC);
50 wxCHECK_RET( m_glContext, "Couldn't create OpenGl context" );
51
52 wglMakeCurrent((HDC) m_hDC, m_glContext);
53 }
54
55 wxGLContext::wxGLContext(
56 bool isRGB, wxGLCanvas *win,
57 const wxPalette& palette,
58 const wxGLContext *other /* for sharing display lists */
59 )
60 {
61 m_window = win;
62
63 m_hDC = win->GetHDC();
64
65 m_glContext = wglCreateContext((HDC) m_hDC);
66 wxCHECK_RET( m_glContext, "Couldn't create OpenGl context" );
67
68 if( other != 0 )
69 wglShareLists( other->m_glContext, m_glContext );
70
71 wglMakeCurrent((HDC) m_hDC, m_glContext);
72 }
73
74 wxGLContext::~wxGLContext()
75 {
76 if (m_glContext)
77 {
78 wglMakeCurrent(NULL, NULL);
79 wglDeleteContext(m_glContext);
80 }
81 }
82
83 void wxGLContext::SwapBuffers()
84 {
85 if (m_glContext)
86 {
87 wglMakeCurrent((HDC) m_hDC, m_glContext);
88 ::SwapBuffers((HDC) m_hDC); //blits the backbuffer into DC
89 }
90 }
91
92 void wxGLContext::SetCurrent()
93 {
94 if (m_glContext)
95 {
96 wglMakeCurrent((HDC) m_hDC, m_glContext);
97 }
98
99 /*
100 setupPixelFormat(hDC);
101 setupPalette(hDC);
102 */
103 }
104
105 void wxGLContext::SetColour(const char *colour)
106 {
107 float r = 0.0;
108 float g = 0.0;
109 float b = 0.0;
110 wxColour *col = wxTheColourDatabase->FindColour(colour);
111 if (col)
112 {
113 r = (float)(col->Red()/256.0);
114 g = (float)(col->Green()/256.0);
115 b = (float)(col->Blue()/256.0);
116 glColor3f( r, g, b);
117 }
118 }
119
120
121 /*
122 * wxGLCanvas implementation
123 */
124
125 IMPLEMENT_CLASS(wxGLCanvas, wxScrolledWindow)
126
127 BEGIN_EVENT_TABLE(wxGLCanvas, wxScrolledWindow)
128 EVT_SIZE(wxGLCanvas::OnSize)
129 EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged)
130 EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette)
131 END_EVENT_TABLE()
132
133 wxGLCanvas::wxGLCanvas(wxWindow *parent, wxWindowID id,
134 const wxPoint& pos, const wxSize& size, long style, const wxString& name,
135 int *attribList /* not used yet! */, const wxPalette& palette):
136 wxScrolledWindow()
137 {
138 m_glContext = (wxGLContext*) NULL;
139
140 bool ret = Create(parent, id, pos, size, style, name);
141
142 if ( ret )
143 {
144 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
145 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
146 }
147
148 m_hDC = (WXHDC) ::GetDC((HWND) GetHWND());
149
150 SetupPixelFormat();
151 SetupPalette(palette);
152
153 m_glContext = new wxGLContext(TRUE, this, palette);
154 }
155
156 wxGLCanvas::wxGLCanvas( wxWindow *parent,
157 const wxGLContext *shared, wxWindowID id,
158 const wxPoint& pos, const wxSize& size, long style, const wxString& name,
159 int *attribList, const wxPalette& palette )
160 : wxScrolledWindow()
161 {
162 m_glContext = (wxGLContext*) NULL;
163
164 bool ret = Create(parent, id, pos, size, style, name);
165
166 if ( ret )
167 {
168 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
169 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
170 }
171
172 m_hDC = (WXHDC) ::GetDC((HWND) GetHWND());
173
174 SetupPixelFormat();
175 SetupPalette(palette);
176
177 m_glContext = new wxGLContext(TRUE, this, palette, shared );
178 }
179
180 // Not very usefull for wxMSW, but this is to be wxGTK compliant
181
182 wxGLCanvas::wxGLCanvas( wxWindow *parent, const wxGLCanvas *shared, wxWindowID id,
183 const wxPoint& pos, const wxSize& size, long style, const wxString& name,
184 int *attribList, const wxPalette& palette ):
185 wxScrolledWindow()
186 {
187 m_glContext = (wxGLContext*) NULL;
188
189 bool ret = Create(parent, id, pos, size, style, name);
190
191 if ( ret )
192 {
193 SetBackgroundColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
194 SetFont(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
195 }
196
197 m_hDC = (WXHDC) ::GetDC((HWND) GetHWND());
198
199 SetupPixelFormat();
200 SetupPalette(palette);
201
202 wxGLContext *sharedContext=0;
203 if (shared) sharedContext=shared->GetContext();
204 m_glContext = new wxGLContext(TRUE, this, palette, sharedContext );
205 }
206
207 wxGLCanvas::~wxGLCanvas()
208 {
209 if (m_glContext)
210 delete m_glContext;
211
212 ::ReleaseDC((HWND) GetHWND(), (HDC) m_hDC);
213 }
214
215 // Replaces wxWindow::Create functionality, since we need to use a different window class
216 bool wxGLCanvas::Create(wxWindow *parent, wxWindowID id,
217 const wxPoint& pos, const wxSize& size, long style, const wxString& name)
218 {
219 static bool registeredGLCanvasClass = FALSE;
220
221 // We have to register a special window class because we need
222 // the CS_OWNDC style for GLCanvas.
223
224 /*
225 From Angel Popov <jumpo@bitex.com>
226
227 Here are two snips from a dicussion in the OpenGL Gamedev list that explains
228 how this problem can be fixed:
229
230 "There are 5 common DCs available in Win95. These are aquired when you call
231 GetDC or GetDCEx from a window that does _not_ have the OWNDC flag.
232 OWNDC flagged windows do not get their DC from the common DC pool, the issue
233 is they require 800 bytes each from the limited 64Kb local heap for GDI."
234
235 "The deal is, if you hold onto one of the 5 shared DC's too long (as GL apps
236 do), Win95 will actually "steal" it from you. MakeCurrent fails,
237 apparently, because Windows re-assigns the HDC to a different window. The
238 only way to prevent this, the only reliable means, is to set CS_OWNDC."
239 */
240
241 if (!registeredGLCanvasClass)
242 {
243 WNDCLASS wndclass;
244
245 static const long styleNormal = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC;
246
247 // the fields which are common to all classes
248 wndclass.lpfnWndProc = (WNDPROC)wxWndProc;
249 wndclass.cbClsExtra = 0;
250 wndclass.cbWndExtra = sizeof( DWORD ); // VZ: what is this DWORD used for?
251 wndclass.hInstance = wxhInstance;
252 wndclass.hIcon = (HICON) NULL;
253 wndclass.hCursor = ::LoadCursor((HINSTANCE)NULL, IDC_ARROW);
254 wndclass.lpszMenuName = NULL;
255
256 // Register the GLCanvas class name
257 wndclass.hbrBackground = (HBRUSH)NULL;
258 wndclass.lpszClassName = wxGLCanvasClassName;
259 wndclass.style = styleNormal;
260
261 if ( !RegisterClass(&wndclass) )
262 {
263 wxLogLastError("RegisterClass(wxGLCanvasClass)");
264
265 return FALSE;
266 }
267 registeredGLCanvasClass = TRUE;
268 }
269
270 wxCHECK_MSG( parent, FALSE, wxT("can't create wxWindow without parent") );
271
272 if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) )
273 return FALSE;
274
275 parent->AddChild(this);
276
277 DWORD msflags = 0;
278 if ( style & wxBORDER )
279 msflags |= WS_BORDER;
280 if ( style & wxTHICK_FRAME )
281 msflags |= WS_THICKFRAME;
282
283 msflags |= WS_CHILD | WS_VISIBLE;
284 if ( style & wxCLIP_CHILDREN )
285 msflags |= WS_CLIPCHILDREN;
286
287 bool want3D;
288 WXDWORD exStyle = Determine3DEffects(WS_EX_CLIENTEDGE, &want3D);
289
290 // Even with extended styles, need to combine with WS_BORDER
291 // for them to look right.
292 if ( want3D || (m_windowStyle & wxSIMPLE_BORDER) || (m_windowStyle & wxRAISED_BORDER ) ||
293 (m_windowStyle & wxSUNKEN_BORDER) || (m_windowStyle & wxDOUBLE_BORDER))
294 {
295 msflags |= WS_BORDER;
296 }
297
298 // calculate the value to return from WM_GETDLGCODE handler
299 if ( GetWindowStyleFlag() & wxWANTS_CHARS )
300 {
301 // want everything: i.e. all keys and WM_CHAR message
302 m_lDlgCode = DLGC_WANTARROWS | DLGC_WANTCHARS |
303 DLGC_WANTTAB | DLGC_WANTMESSAGE;
304 }
305
306 MSWCreate(m_windowId, parent, wxGLCanvasClassName, this, NULL,
307 pos.x, pos.y,
308 WidthDefault(size.x), HeightDefault(size.y),
309 msflags, NULL, exStyle);
310
311 return TRUE;
312
313 }
314
315 void wxGLCanvas::SetupPixelFormat() // (HDC hDC)
316 {
317 PIXELFORMATDESCRIPTOR pfd = {
318 sizeof(PIXELFORMATDESCRIPTOR), /* size */
319 1, /* version */
320 PFD_SUPPORT_OPENGL |
321 PFD_DRAW_TO_WINDOW |
322 PFD_DOUBLEBUFFER, /* support double-buffering */
323 PFD_TYPE_RGBA, /* color type */
324 16, /* prefered color depth */
325 0, 0, 0, 0, 0, 0, /* color bits (ignored) */
326 0, /* no alpha buffer */
327 0, /* alpha bits (ignored) */
328 0, /* no accumulation buffer */
329 0, 0, 0, 0, /* accum bits (ignored) */
330 16, /* depth buffer */
331 0, /* no stencil buffer */
332 0, /* no auxiliary buffers */
333 PFD_MAIN_PLANE, /* main layer */
334 0, /* reserved */
335 0, 0, 0, /* no layer, visible, damage masks */
336 };
337 int pixelFormat;
338
339 pixelFormat = ChoosePixelFormat((HDC) m_hDC, &pfd);
340 if (pixelFormat == 0) {
341 MessageBox(WindowFromDC((HDC) m_hDC), "ChoosePixelFormat failed.", "Error",
342 MB_ICONERROR | MB_OK);
343 exit(1);
344 }
345
346 if (SetPixelFormat((HDC) m_hDC, pixelFormat, &pfd) != TRUE) {
347 MessageBox(WindowFromDC((HDC) m_hDC), "SetPixelFormat failed.", "Error",
348 MB_ICONERROR | MB_OK);
349 exit(1);
350 }
351 }
352
353 void wxGLCanvas::SetupPalette(const wxPalette& palette)
354 {
355 int pixelFormat = GetPixelFormat((HDC) m_hDC);
356 PIXELFORMATDESCRIPTOR pfd;
357
358 DescribePixelFormat((HDC) m_hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
359
360 if (pfd.dwFlags & PFD_NEED_PALETTE)
361 {
362 }
363 else
364 {
365 return;
366 }
367
368 m_palette = palette;
369
370 if ( !m_palette.Ok() )
371 {
372 m_palette = CreateDefaultPalette();
373 }
374
375 if (m_palette.Ok())
376 {
377 SelectPalette((HDC) m_hDC, (HPALETTE) m_palette.GetHPALETTE(), FALSE);
378 RealizePalette((HDC) m_hDC);
379 }
380 }
381
382 wxPalette wxGLCanvas::CreateDefaultPalette()
383 {
384 PIXELFORMATDESCRIPTOR pfd;
385 int paletteSize;
386 int pixelFormat = GetPixelFormat((HDC) m_hDC);
387
388 DescribePixelFormat((HDC) m_hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
389
390 paletteSize = 1 << pfd.cColorBits;
391
392 LOGPALETTE* pPal =
393 (LOGPALETTE*) malloc(sizeof(LOGPALETTE) + paletteSize * sizeof(PALETTEENTRY));
394 pPal->palVersion = 0x300;
395 pPal->palNumEntries = paletteSize;
396
397 /* build a simple RGB color palette */
398 {
399 int redMask = (1 << pfd.cRedBits) - 1;
400 int greenMask = (1 << pfd.cGreenBits) - 1;
401 int blueMask = (1 << pfd.cBlueBits) - 1;
402 int i;
403
404 for (i=0; i<paletteSize; ++i) {
405 pPal->palPalEntry[i].peRed =
406 (((i >> pfd.cRedShift) & redMask) * 255) / redMask;
407 pPal->palPalEntry[i].peGreen =
408 (((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask;
409 pPal->palPalEntry[i].peBlue =
410 (((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask;
411 pPal->palPalEntry[i].peFlags = 0;
412 }
413 }
414
415 HPALETTE hPalette = CreatePalette(pPal);
416 free(pPal);
417
418 wxPalette palette;
419 palette.SetHPALETTE((WXHPALETTE) hPalette);
420
421 return palette;
422 }
423
424 void wxGLCanvas::SwapBuffers()
425 {
426 if (m_glContext)
427 m_glContext->SwapBuffers();
428 }
429
430 void wxGLCanvas::OnSize(wxSizeEvent& event)
431 {
432 int width, height;
433 GetClientSize(& width, & height);
434
435 if (m_glContext)
436 {
437 m_glContext->SetCurrent();
438
439 glViewport(0, 0, (GLint)width, (GLint)height);
440 glMatrixMode(GL_PROJECTION);
441 glLoadIdentity();
442 glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 15.0 );
443 glMatrixMode(GL_MODELVIEW);
444 }
445 }
446
447 void wxGLCanvas::SetCurrent()
448 {
449 if (m_glContext)
450 {
451 m_glContext->SetCurrent();
452 }
453 }
454
455 void wxGLCanvas::SetColour(const char *colour)
456 {
457 if (m_glContext)
458 m_glContext->SetColour(colour);
459 }
460
461 // TODO: Have to have this called by parent frame (?)
462 // So we need wxFrame to call OnQueryNewPalette for all children...
463 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent& event)
464 {
465 /* realize palette if this is the current window */
466 if ( GetPalette()->Ok() ) {
467 ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE());
468 ::SelectPalette((HDC) GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE);
469 ::RealizePalette((HDC) GetHDC());
470 Refresh();
471 event.SetPaletteRealized(TRUE);
472 }
473 else
474 event.SetPaletteRealized(FALSE);
475 }
476
477 // I think this doesn't have to be propagated to child windows.
478 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent& event)
479 {
480 /* realize palette if this is *not* the current window */
481 if ( GetPalette() &&
482 GetPalette()->Ok() && (this != event.GetChangedWindow()) )
483 {
484 ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE());
485 ::SelectPalette((HDC) GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE);
486 ::RealizePalette((HDC) GetHDC());
487 Refresh();
488 }
489 }
490
491 /* Give extensions proper function names. */
492
493 /* EXT_vertex_array */
494 void glArrayElementEXT(GLint i)
495 {
496 }
497
498 void glColorPointerEXT(GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer)
499 {
500 }
501
502 void glDrawArraysEXT(GLenum mode, GLint first, GLsizei count)
503 {
504 #ifdef GL_EXT_vertex_array
505 static PFNGLDRAWARRAYSEXTPROC proc = 0;
506
507 if ( !proc )
508 {
509 proc = (PFNGLDRAWARRAYSEXTPROC) wglGetProcAddress("glDrawArraysEXT");
510 }
511
512 if ( proc )
513 (* proc) (mode, first, count);
514 #endif
515 }
516
517 void glEdgeFlagPointerEXT(GLsizei stride, GLsizei count, const GLboolean *pointer)
518 {
519 }
520
521 void glGetPointervEXT(GLenum pname, GLvoid* *params)
522 {
523 }
524
525 void glIndexPointerEXT(GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer)
526 {
527 }
528
529 void glNormalPointerEXT(GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer)
530 {
531 #ifdef GL_EXT_vertex_array
532 static PFNGLNORMALPOINTEREXTPROC proc = 0;
533
534 if ( !proc )
535 {
536 proc = (PFNGLNORMALPOINTEREXTPROC) wglGetProcAddress("glNormalPointerEXT");
537 }
538
539 if ( proc )
540 (* proc) (type, stride, count, pointer);
541 #endif
542 }
543
544 void glTexCoordPointerEXT(GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer)
545 {
546 }
547
548 void glVertexPointerEXT(GLint size, GLenum type, GLsizei stride, GLsizei count, const GLvoid *pointer)
549 {
550 #ifdef GL_EXT_vertex_array
551 static PFNGLVERTEXPOINTEREXTPROC proc = 0;
552
553 if ( !proc )
554 {
555 proc = (PFNGLVERTEXPOINTEREXTPROC) wglGetProcAddress("glVertexPointerEXT");
556 }
557
558 if ( proc )
559 (* proc) (size, type, stride, count, pointer);
560 #endif
561 }
562
563 /* EXT_color_subtable */
564 void glColorSubtableEXT(GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const GLvoid *table)
565 {
566 }
567
568 /* EXT_color_table */
569 void glColorTableEXT(GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const GLvoid *table)
570 {
571 }
572
573 void glCopyColorTableEXT(GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width)
574 {
575 }
576
577 void glGetColorTableEXT(GLenum target, GLenum format, GLenum type, GLvoid *table)
578 {
579 }
580
581 void glGetColorTableParamaterfvEXT(GLenum target, GLenum pname, GLfloat *params)
582 {
583 }
584
585 void glGetColorTavleParameterivEXT(GLenum target, GLenum pname, GLint *params)
586 {
587 }
588
589 /* SGI_compiled_vertex_array */
590 void glLockArraysSGI(GLint first, GLsizei count)
591 {
592 }
593
594 void glUnlockArraysSGI()
595 {
596 }
597
598
599 /* SGI_cull_vertex */
600 void glCullParameterdvSGI(GLenum pname, GLdouble* params)
601 {
602 }
603
604 void glCullParameterfvSGI(GLenum pname, GLfloat* params)
605 {
606 }
607
608 /* SGI_index_func */
609 void glIndexFuncSGI(GLenum func, GLclampf ref)
610 {
611 }
612
613 /* SGI_index_material */
614 void glIndexMaterialSGI(GLenum face, GLenum mode)
615 {
616 }
617
618 /* WIN_swap_hint */
619 void glAddSwapHintRectWin(GLint x, GLint y, GLsizei width, GLsizei height)
620 {
621 }
622
623 #endif
624 // wxUSE_GLCANVAS