1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/glcanvas.cpp
3 // Purpose:     wxGLCanvas, for using OpenGL with wxWidgets under MS Windows
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     04/01/98
7 // Copyright:   (c) Julian Smart
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // ============================================================================
12 // declarations
13 // ============================================================================
14 
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18 
19 #include "wx/wxprec.h"
20 
21 #if defined(__BORLANDC__)
22     #pragma hdrstop
23 #endif
24 
25 #if wxUSE_GLCANVAS
26 
27 #ifndef WX_PRECOMP
28     #include "wx/intl.h"
29     #include "wx/log.h"
30     #include "wx/app.h"
31 #endif
32 
33 #include "wx/msw/private.h"
34 
35 #include "wx/glcanvas.h"
36 
37 // from src/msw/window.cpp
38 LRESULT WXDLLEXPORT APIENTRY _EXPORT wxWndProc(HWND hWnd, UINT message,
39                                    WPARAM wParam, LPARAM lParam);
40 
41 #ifdef GL_EXT_vertex_array
42     #define WXUNUSED_WITHOUT_GL_EXT_vertex_array(name) name
43 #else
44     #define WXUNUSED_WITHOUT_GL_EXT_vertex_array(name) WXUNUSED(name)
45 #endif
46 
47 // ----------------------------------------------------------------------------
48 // define possibly missing WGL constants and types
49 // ----------------------------------------------------------------------------
50 
51 #ifndef WGL_ARB_pixel_format
52 #define WGL_DRAW_TO_WINDOW_ARB            0x2001
53 #define WGL_ACCELERATION_ARB              0x2003
54 #define WGL_NUMBER_OVERLAYS_ARB           0x2008
55 #define WGL_NUMBER_UNDERLAYS_ARB          0x2009
56 #define WGL_SUPPORT_OPENGL_ARB            0x2010
57 #define WGL_DOUBLE_BUFFER_ARB             0x2011
58 #define WGL_STEREO_ARB                    0x2012
59 #define WGL_COLOR_BITS_ARB                0x2014
60 #define WGL_RED_BITS_ARB                  0x2015
61 #define WGL_GREEN_BITS_ARB                0x2017
62 #define WGL_BLUE_BITS_ARB                 0x2019
63 #define WGL_ALPHA_BITS_ARB                0x201B
64 #define WGL_ACCUM_RED_BITS_ARB            0x201E
65 #define WGL_ACCUM_GREEN_BITS_ARB          0x201F
66 #define WGL_ACCUM_BLUE_BITS_ARB           0x2020
67 #define WGL_ACCUM_ALPHA_BITS_ARB          0x2021
68 #define WGL_DEPTH_BITS_ARB                0x2022
69 #define WGL_STENCIL_BITS_ARB              0x2023
70 #define WGL_AUX_BUFFERS_ARB               0x2024
71 #define WGL_FULL_ACCELERATION_ARB         0x2027
72 #endif
73 
74 #ifndef WGL_ARB_multisample
75 #define WGL_SAMPLE_BUFFERS_ARB            0x2041
76 #define WGL_SAMPLES_ARB                   0x2042
77 #endif
78 
79 #ifndef WGL_ARB_create_context
80 #define WGL_ARB_create_context
81 #define WGL_CONTEXT_MAJOR_VERSION_ARB   0x2091
82 #define WGL_CONTEXT_MINOR_VERSION_ARB   0x2092
83 #define WGL_CONTEXT_LAYER_PLANE_ARB     0x2093
84 #define WGL_CONTEXT_FLAGS_ARB           0x2094
85 #define WGL_CONTEXT_DEBUG_BIT_ARB       0x0001
86 #define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB  0x0002
87 
88 #ifndef WGL_ARB_create_context_profile
89 #define WGL_ARB_create_context_profile
90 #define WGL_CONTEXT_PROFILE_MASK_ARB              0x9126
91 #define WGL_CONTEXT_CORE_PROFILE_BIT_ARB          0x00000001
92 #define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
93 #endif
94 
95 #ifndef WGL_ARB_create_context_robustness
96 #define WGL_ARB_create_context_robustness
97 #define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB         0x00000004
98 #define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB     0x8256
99 #define WGL_NO_RESET_NOTIFICATION_ARB                   0x8261
100 #define WGL_LOSE_CONTEXT_ON_RESET_ARB                   0x8252
101 #endif
102 #endif
103 
104 #ifndef WGL_EXT_create_context_es2_profile
105 #define WGL_EXT_create_context_es2_profile
106 #define WGL_CONTEXT_ES2_PROFILE_BIT_EXT           0x00000004
107 #endif
108 
109 #ifndef WGL_EXT_create_context_es_profile
110 #define WGL_EXT_create_context_es_profile
111 #define WGL_CONTEXT_ES_PROFILE_BIT_EXT            0x00000004
112 #endif
113 
114 #ifndef WGL_ARB_framebuffer_sRGB
115 #define WGL_ARB_framebuffer_sRGB
116 #define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB                0x20A9
117 #endif
118 
119 typedef HGLRC(WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)
120     (HDC hDC, HGLRC hShareContext, const int *attribList);
121 
122 // ----------------------------------------------------------------------------
123 // libraries
124 // ----------------------------------------------------------------------------
125 
126 /*
127   The following two compiler directives are specific to the Microsoft Visual
128   C++ family of compilers
129 
130   Fundementally what they do is instruct the linker to use these two libraries
131   for the resolution of symbols. In essence, this is the equivalent of adding
132   these two libraries to either the Makefile or project file.
133 
134   This is NOT a recommended technique, and certainly is unlikely to be used
135   anywhere else in wxWidgets given it is so specific to not only wxMSW, but
136   also the VC compiler. However, in the case of opengl support, it's an
137   applicable technique as opengl is optional in setup.h This code (wrapped by
138   wxUSE_GLCANVAS), now allows opengl support to be added purely by modifying
139   setup.h rather than by having to modify either the project or DSP fle.
140 
141   See MSDN for further information on the exact usage of these commands.
142 */
143 #ifdef _MSC_VER
144 #  pragma comment( lib, "opengl32" )
145 #  pragma comment( lib, "glu32" )
146 #endif
147 
148 // ----------------------------------------------------------------------------
149 // wxGLContext
150 // ----------------------------------------------------------------------------
151 
152 IMPLEMENT_CLASS(wxGLContext, wxObject)
153 
154 // The window will always be created first so the array will be initialized
155 // and then the window will be assigned to the context.
156 // max 8 attributes plus terminator
157 // if first is 0, create legacy context
158 static int s_wglContextAttribs[9] = {0};
159 
wxGLContext(wxGLCanvas * win,const wxGLContext * other)160 wxGLContext::wxGLContext(wxGLCanvas *win, const wxGLContext* other)
161 {
162     if ( s_wglContextAttribs[0] == 0 ) // create legacy context
163     {
164         m_glContext = wglCreateContext(win->GetHDC());
165         wxCHECK_RET( m_glContext, wxT("Couldn't create OpenGL context") );
166     }
167     else // create a context using attributes
168     {
169         // We need to create a temporary context to get the
170         // wglCreateContextAttribsARB function
171         HGLRC tempContext = wglCreateContext(win->GetHDC());
172         wxCHECK_RET( tempContext, wxT("Couldn't create OpenGL context") );
173 
174         wglMakeCurrent(win->GetHDC(), tempContext);
175         PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB
176             = (PFNWGLCREATECONTEXTATTRIBSARBPROC)
177             wglGetProcAddress("wglCreateContextAttribsARB");
178         wglMakeCurrent(win->GetHDC(), NULL);
179         wglDeleteContext(tempContext);
180 
181         if ( !wglCreateContextAttribsARB )
182         {
183             wxLogError(_("Core OpenGL profile is not supported by the OpenGL driver."));
184             return;
185         }
186 
187         m_glContext = wglCreateContextAttribsARB(
188             win->GetHDC(), 0, s_wglContextAttribs);
189         wxCHECK_RET( m_glContext,
190                      wxT("Couldn't create core profile OpenGL context") );
191     }
192 
193     if ( other )
194     {
195         if ( !wglShareLists(other->m_glContext, m_glContext) )
196         {
197             wxLogLastError(wxT("wglShareLists"));
198         }
199     }
200 }
201 
~wxGLContext()202 wxGLContext::~wxGLContext()
203 {
204     // note that it's ok to delete the context even if it's the current one
205     wglDeleteContext(m_glContext);
206 }
207 
SetCurrent(const wxGLCanvas & win) const208 bool wxGLContext::SetCurrent(const wxGLCanvas& win) const
209 {
210     if ( !wglMakeCurrent(win.GetHDC(), m_glContext) )
211     {
212         wxLogLastError(wxT("wglMakeCurrent"));
213         return false;
214     }
215     return true;
216 }
217 
218 // ============================================================================
219 // wxGLCanvas
220 // ============================================================================
221 
222 IMPLEMENT_CLASS(wxGLCanvas, wxWindow)
223 
224 BEGIN_EVENT_TABLE(wxGLCanvas, wxWindow)
225 #if wxUSE_PALETTE
226     EVT_PALETTE_CHANGED(wxGLCanvas::OnPaletteChanged)
227     EVT_QUERY_NEW_PALETTE(wxGLCanvas::OnQueryNewPalette)
228 #endif
229 END_EVENT_TABLE()
230 
231 // ----------------------------------------------------------------------------
232 // wxGLCanvas construction
233 // ----------------------------------------------------------------------------
234 
235 static int ChoosePixelFormatARB(HDC hdc, const int *attribList);
236 
Init()237 void wxGLCanvas::Init()
238 {
239 #if WXWIN_COMPATIBILITY_2_8
240     m_glContext = NULL;
241 #endif
242     m_hDC = NULL;
243 }
244 
wxGLCanvas(wxWindow * parent,wxWindowID id,const int * attribList,const wxPoint & pos,const wxSize & size,long style,const wxString & name,const wxPalette & palette)245 wxGLCanvas::wxGLCanvas(wxWindow *parent,
246                        wxWindowID id,
247                        const int *attribList,
248                        const wxPoint& pos,
249                        const wxSize& size,
250                        long style,
251                        const wxString& name,
252                        const wxPalette& palette)
253 {
254     Init();
255 
256     (void)Create(parent, id, pos, size, style, name, attribList, palette);
257 }
258 
~wxGLCanvas()259 wxGLCanvas::~wxGLCanvas()
260 {
261     ::ReleaseDC(GetHwnd(), m_hDC);
262 }
263 
264 // Replaces wxWindow::Create functionality, since we need to use a different
265 // window class
CreateWindow(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name)266 bool wxGLCanvas::CreateWindow(wxWindow *parent,
267                               wxWindowID id,
268                               const wxPoint& pos,
269                               const wxSize& size,
270                               long style,
271                               const wxString& name)
272 {
273     wxCHECK_MSG( parent, false, wxT("can't create wxWindow without parent") );
274 
275     if ( !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name) )
276         return false;
277 
278     parent->AddChild(this);
279 
280     /*
281        A general rule with OpenGL and Win32 is that any window that will have a
282        HGLRC built for it must have two flags:  WS_CLIPCHILDREN & WS_CLIPSIBLINGS.
283        You can find references about this within the knowledge base and most OpenGL
284        books that contain the wgl function descriptions.
285      */
286     WXDWORD exStyle = 0;
287     DWORD msflags = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
288     msflags |= MSWGetStyle(style, &exStyle);
289 
290     if ( !MSWCreate(wxApp::GetRegisteredClassName(wxT("wxGLCanvas"), -1, CS_OWNDC),
291                     NULL, pos, size, msflags, exStyle) )
292         return false;
293 
294     m_hDC = ::GetDC(GetHwnd());
295     if ( !m_hDC )
296         return false;
297 
298     return true;
299 }
300 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name,const int * attribList,const wxPalette & palette)301 bool wxGLCanvas::Create(wxWindow *parent,
302                         wxWindowID id,
303                         const wxPoint& pos,
304                         const wxSize& size,
305                         long style,
306                         const wxString& name,
307                         const int *attribList,
308                         const wxPalette& palette)
309 {
310     // Create the window first: we will either use it as is or use it to query
311     // for multisampling support and recreate it later with another pixel format
312     if ( !CreateWindow(parent, id, pos, size, style, name) )
313         return false;
314 
315     // these will be used for the context creation attributes
316     // if a core profile is requested
317     bool useGLCoreProfile = false;
318 
319     // the minimum gl core version is 3.0
320     int glVersionMajor = 3,
321         glVersionMinor = 0;
322 
323     // Check for a core profile request
324     if ( attribList )
325     {
326         for ( int i = 0; attribList[i]; )
327         {
328             switch ( attribList[i++] )
329             {
330                 case WX_GL_CORE_PROFILE:
331                     useGLCoreProfile = true;
332                     break;
333 
334                 case WX_GL_MAJOR_VERSION:
335                     glVersionMajor = attribList[i++];
336                     break;
337 
338                 case WX_GL_MINOR_VERSION:
339                     glVersionMinor = attribList[i++];
340                     break;
341 
342                 default:
343                     // ignore all other flags for now
344                     break;
345             }
346         }
347     }
348 
349     if ( useGLCoreProfile )
350     {
351         s_wglContextAttribs[0] = WGL_CONTEXT_MAJOR_VERSION_ARB;
352         s_wglContextAttribs[1] = glVersionMajor;
353         s_wglContextAttribs[2] = WGL_CONTEXT_MINOR_VERSION_ARB;
354         s_wglContextAttribs[3] = glVersionMinor;
355         s_wglContextAttribs[4] = WGL_CONTEXT_FLAGS_ARB;
356         s_wglContextAttribs[5] = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
357         s_wglContextAttribs[6] = WGL_CONTEXT_PROFILE_MASK_ARB;
358         s_wglContextAttribs[7] = WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
359         s_wglContextAttribs[8] = 0; // terminate
360     }
361     else // create legacy/compatibility context
362     {
363         s_wglContextAttribs[0] = 0;
364     }
365 
366 
367     PIXELFORMATDESCRIPTOR pfd;
368     const int setupVal = DoSetup(pfd, attribList);
369     if ( setupVal == 0 ) // PixelFormat error
370         return false;
371 
372     if ( setupVal == -1 ) // FSAA requested
373     {
374         // now that we have a valid OpenGL window, query it for FSAA support
375         int pixelFormat;
376         {
377             wxGLContext ctx(this);
378             ctx.SetCurrent(*this);
379             pixelFormat = ::ChoosePixelFormatARB(m_hDC, attribList);
380         }
381 
382         if ( pixelFormat > 0 )
383         {
384             // from http://msdn.microsoft.com/en-us/library/ms537559(VS.85).aspx:
385             //
386             //      Setting the pixel format of a window more than once can
387             //      lead to significant complications for the Window Manager
388             //      and for multithread applications, so it is not allowed. An
389             //      application can only set the pixel format of a window one
390             //      time. Once a window's pixel format is set, it cannot be
391             //      changed.
392             //
393             // so we need to delete the old window and create the new one
394 
395             // destroy Window
396             ::ReleaseDC(GetHwnd(), m_hDC);
397             m_hDC = 0;
398 
399             parent->RemoveChild(this);
400             const HWND hwnd = GetHwnd();
401             DissociateHandle(); // will do SetHWND(0);
402             ::DestroyWindow(hwnd);
403 
404             // now recreate with FSAA pixelFormat
405             if ( !CreateWindow(parent, id, pos, size, style, name) )
406                 return false;
407 
408             if ( !::SetPixelFormat(m_hDC, pixelFormat, &pfd) )
409             {
410                 wxLogLastError(wxT("SetPixelFormat"));
411                 return false;
412             }
413         }
414     }
415 
416 #if wxUSE_PALETTE
417     if ( !SetupPalette(palette) )
418         return false;
419 #else // !wxUSE_PALETTE
420     wxUnusedVar(palette);
421 #endif // wxUSE_PALETTE/!wxUSE_PALETTE
422 
423     return true;
424 }
425 
426 // ----------------------------------------------------------------------------
427 // operations
428 // ----------------------------------------------------------------------------
429 
SwapBuffers()430 bool wxGLCanvas::SwapBuffers()
431 {
432     if ( !::SwapBuffers(m_hDC) )
433     {
434         wxLogLastError(wxT("SwapBuffers"));
435         return false;
436     }
437 
438     return true;
439 }
440 
441 
442 // ----------------------------------------------------------------------------
443 // multi sample support
444 // ----------------------------------------------------------------------------
445 
446 // this macro defines a variable of type "name_t" called "name" and initializes
447 // it with the pointer to WGL function "name" (which may be NULL)
448 #define wxDEFINE_WGL_FUNC(name) \
449     name##_t name = (name##_t)wglGetProcAddress(#name)
450 
451 /* static */
IsExtensionSupported(const char * extension)452 bool wxGLCanvasBase::IsExtensionSupported(const char *extension)
453 {
454     static const char *s_extensionsList = (char *)wxUIntPtr(-1);
455     if ( s_extensionsList == (char *)wxUIntPtr(-1) )
456     {
457         typedef const char * (WINAPI *wglGetExtensionsStringARB_t)(HDC hdc);
458 
459         wxDEFINE_WGL_FUNC(wglGetExtensionsStringARB);
460         if ( wglGetExtensionsStringARB )
461         {
462             s_extensionsList = wglGetExtensionsStringARB(wglGetCurrentDC());
463         }
464         else
465         {
466             typedef const char * (WINAPI * wglGetExtensionsStringEXT_t)();
467 
468             wxDEFINE_WGL_FUNC(wglGetExtensionsStringEXT);
469             if ( wglGetExtensionsStringEXT )
470             {
471                 s_extensionsList = wglGetExtensionsStringEXT();
472             }
473             else
474             {
475                 s_extensionsList = NULL;
476             }
477         }
478     }
479 
480     return s_extensionsList && IsExtensionInList(s_extensionsList, extension);
481 }
482 
483 // this is a wrapper around wglChoosePixelFormatARB(): returns the pixel format
484 // index matching the given attributes on success or 0 on failure
ChoosePixelFormatARB(HDC hdc,const int * attribList)485 static int ChoosePixelFormatARB(HDC hdc, const int *attribList)
486 {
487     if ( !wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample") )
488         return 0;
489 
490     typedef BOOL (WINAPI * wglChoosePixelFormatARB_t)
491                  (HDC hdc,
492                   const int *piAttribIList,
493                   const FLOAT *pfAttribFList,
494                   UINT nMaxFormats,
495                   int *piFormats,
496                   UINT *nNumFormats
497                  );
498 
499     wxDEFINE_WGL_FUNC(wglChoosePixelFormatARB);
500     if ( !wglChoosePixelFormatARB )
501         return 0; // should not occur if extension is supported
502 
503     int iAttributes[128];
504     int dst = 0; // index in iAttributes array
505 
506     #define ADD_ATTR(attr, value) \
507         iAttributes[dst++] = attr; iAttributes[dst++] = value
508 
509     ADD_ATTR( WGL_DRAW_TO_WINDOW_ARB,    GL_TRUE );
510     ADD_ATTR( WGL_SUPPORT_OPENGL_ARB,    GL_TRUE );
511     ADD_ATTR( WGL_ACCELERATION_ARB,      WGL_FULL_ACCELERATION_ARB );
512 
513     if ( !attribList )
514     {
515         ADD_ATTR( WGL_COLOR_BITS_ARB,          24 );
516         ADD_ATTR( WGL_ALPHA_BITS_ARB,           8 );
517         ADD_ATTR( WGL_DEPTH_BITS_ARB,          16 );
518         ADD_ATTR( WGL_STENCIL_BITS_ARB,         0 );
519         ADD_ATTR( WGL_DOUBLE_BUFFER_ARB,  GL_TRUE );
520         ADD_ATTR( WGL_SAMPLE_BUFFERS_ARB, GL_TRUE );
521         ADD_ATTR( WGL_SAMPLES_ARB,              4 );
522     }
523     else // have custom attributes
524     {
525         #define ADD_ATTR_VALUE(attr) ADD_ATTR(attr, attribList[src++])
526 
527         int src = 0;
528         while ( attribList[src] )
529         {
530             switch ( attribList[src++] )
531             {
532                 case WX_GL_RGBA:
533                     ADD_ATTR( WGL_COLOR_BITS_ARB, 24 );
534                     ADD_ATTR( WGL_ALPHA_BITS_ARB,  8 );
535                     break;
536 
537                 case WX_GL_BUFFER_SIZE:
538                     ADD_ATTR_VALUE( WGL_COLOR_BITS_ARB);
539                     break;
540 
541                 case WX_GL_LEVEL:
542                     if ( attribList[src] > 0 )
543                     {
544                         ADD_ATTR( WGL_NUMBER_OVERLAYS_ARB, 1 );
545                     }
546                     else if ( attribList[src] <0 )
547                     {
548                         ADD_ATTR( WGL_NUMBER_UNDERLAYS_ARB, 1 );
549                     }
550                     //else: ignore it
551 
552                     src++; // skip the value in any case
553                     break;
554 
555                 case WX_GL_DOUBLEBUFFER:
556                     ADD_ATTR( WGL_DOUBLE_BUFFER_ARB, GL_TRUE );
557                     break;
558 
559                 case WX_GL_STEREO:
560                     ADD_ATTR( WGL_STEREO_ARB, GL_TRUE );
561                     break;
562 
563                 case WX_GL_AUX_BUFFERS:
564                     ADD_ATTR_VALUE( WGL_AUX_BUFFERS_ARB );
565                     break;
566 
567                 case WX_GL_MIN_RED:
568                     ADD_ATTR_VALUE( WGL_RED_BITS_ARB );
569                     break;
570 
571                 case WX_GL_MIN_GREEN:
572                     ADD_ATTR_VALUE( WGL_GREEN_BITS_ARB );
573                     break;
574 
575                 case WX_GL_MIN_BLUE:
576                     ADD_ATTR_VALUE( WGL_BLUE_BITS_ARB );
577                     break;
578 
579                 case WX_GL_MIN_ALPHA:
580                     ADD_ATTR_VALUE( WGL_ALPHA_BITS_ARB );
581                    break;
582 
583                 case WX_GL_DEPTH_SIZE:
584                     ADD_ATTR_VALUE( WGL_DEPTH_BITS_ARB );
585                     break;
586 
587                 case WX_GL_STENCIL_SIZE:
588                     ADD_ATTR_VALUE( WGL_STENCIL_BITS_ARB );
589                     break;
590 
591                case WX_GL_MIN_ACCUM_RED:
592                     ADD_ATTR_VALUE( WGL_ACCUM_RED_BITS_ARB );
593                     break;
594 
595                 case WX_GL_MIN_ACCUM_GREEN:
596                     ADD_ATTR_VALUE( WGL_ACCUM_GREEN_BITS_ARB );
597                     break;
598 
599                 case WX_GL_MIN_ACCUM_BLUE:
600                     ADD_ATTR_VALUE( WGL_ACCUM_BLUE_BITS_ARB );
601                     break;
602 
603                 case WX_GL_MIN_ACCUM_ALPHA:
604                     ADD_ATTR_VALUE( WGL_ACCUM_ALPHA_BITS_ARB );
605                     break;
606 
607                 case WX_GL_SAMPLE_BUFFERS:
608                     ADD_ATTR_VALUE( WGL_SAMPLE_BUFFERS_ARB );
609                     break;
610 
611                 case WX_GL_SAMPLES:
612                     ADD_ATTR_VALUE( WGL_SAMPLES_ARB );
613                     break;
614             }
615         }
616 
617         #undef ADD_ATTR_VALUE
618     }
619 
620     #undef ADD_ATTR
621 
622     iAttributes[dst++] = 0;
623 
624     int pf;
625     UINT numFormats = 0;
626 
627     if ( !wglChoosePixelFormatARB(hdc, iAttributes, NULL, 1, &pf, &numFormats) )
628     {
629         wxLogLastError(wxT("wglChoosePixelFormatARB"));
630         return 0;
631     }
632 
633     // Although TRUE is returned if no matching formats are found (see
634     // http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt), pf is
635     // not initialized in this case so we need to check for numFormats being
636     // not 0 explicitly (however this is not an error so don't call
637     // wxLogLastError() here).
638     if ( !numFormats )
639         pf = 0;
640 
641     return pf;
642 }
643 
644 // ----------------------------------------------------------------------------
645 // pixel format stuff
646 // ----------------------------------------------------------------------------
647 
648 // returns true if pfd was adjusted accordingly to attributes provided, false
649 // if there is an error with attributes or -1 if the attributes indicate
650 // features not supported by ChoosePixelFormat() at all (currently only multi
651 // sampling)
652 static int
AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR & pfd,const int * attribList)653 AdjustPFDForAttributes(PIXELFORMATDESCRIPTOR& pfd, const int *attribList)
654 {
655     if ( !attribList )
656         return 1;
657 
658     // remove default attributes
659     pfd.dwFlags &= ~PFD_DOUBLEBUFFER;
660     pfd.iPixelType = PFD_TYPE_COLORINDEX;
661 
662     bool requestFSAA = false;
663     for ( int arg = 0; attribList[arg]; )
664     {
665         switch ( attribList[arg++] )
666         {
667             case WX_GL_RGBA:
668                 pfd.iPixelType = PFD_TYPE_RGBA;
669                 break;
670 
671             case WX_GL_BUFFER_SIZE:
672                 pfd.cColorBits = attribList[arg++];
673                 break;
674 
675             case WX_GL_LEVEL:
676                 // this member looks like it may be obsolete
677                 if ( attribList[arg] > 0 )
678                     pfd.iLayerType = PFD_OVERLAY_PLANE;
679                 else if ( attribList[arg] < 0 )
680                     pfd.iLayerType = (BYTE)PFD_UNDERLAY_PLANE;
681                 else
682                     pfd.iLayerType = PFD_MAIN_PLANE;
683                 arg++;
684                 break;
685 
686             case WX_GL_DOUBLEBUFFER:
687                 pfd.dwFlags |= PFD_DOUBLEBUFFER;
688                 break;
689 
690             case WX_GL_STEREO:
691                 pfd.dwFlags |= PFD_STEREO;
692                 break;
693 
694             case WX_GL_AUX_BUFFERS:
695                 pfd.cAuxBuffers = attribList[arg++];
696                 break;
697 
698             case WX_GL_MIN_RED:
699                 pfd.cColorBits += (pfd.cRedBits = attribList[arg++]);
700                 break;
701 
702             case WX_GL_MIN_GREEN:
703                 pfd.cColorBits += (pfd.cGreenBits = attribList[arg++]);
704                 break;
705 
706             case WX_GL_MIN_BLUE:
707                 pfd.cColorBits += (pfd.cBlueBits = attribList[arg++]);
708                 break;
709 
710             case WX_GL_MIN_ALPHA:
711                 // doesn't count in cColorBits
712                 pfd.cAlphaBits = attribList[arg++];
713                 break;
714 
715             case WX_GL_DEPTH_SIZE:
716                 pfd.cDepthBits = attribList[arg++];
717                 break;
718 
719             case WX_GL_STENCIL_SIZE:
720                 pfd.cStencilBits = attribList[arg++];
721                 break;
722 
723             case WX_GL_MIN_ACCUM_RED:
724                 pfd.cAccumBits += (pfd.cAccumRedBits = attribList[arg++]);
725                 break;
726 
727             case WX_GL_MIN_ACCUM_GREEN:
728                 pfd.cAccumBits += (pfd.cAccumGreenBits = attribList[arg++]);
729                 break;
730 
731             case WX_GL_MIN_ACCUM_BLUE:
732                 pfd.cAccumBits += (pfd.cAccumBlueBits = attribList[arg++]);
733                 break;
734 
735             case WX_GL_MIN_ACCUM_ALPHA:
736                 pfd.cAccumBits += (pfd.cAccumAlphaBits = attribList[arg++]);
737                 break;
738 
739             case WX_GL_SAMPLE_BUFFERS:
740             case WX_GL_SAMPLES:
741                 // There is no support for multisample when using PIXELFORMATDESCRIPTOR
742                 requestFSAA = true; // Remember that multi sample is requested.
743                 arg++;              // will call ChoosePixelFormatARB() later
744                 break;
745         }
746     }
747 
748     return requestFSAA ? -1 : 1;
749 }
750 
751 /* static */
752 int
ChooseMatchingPixelFormat(HDC hdc,const int * attribList,PIXELFORMATDESCRIPTOR * ppfd)753 wxGLCanvas::ChooseMatchingPixelFormat(HDC hdc,
754                                       const int *attribList,
755                                       PIXELFORMATDESCRIPTOR *ppfd)
756 {
757     // default neutral pixel format
758     PIXELFORMATDESCRIPTOR pfd =
759     {
760         sizeof(PIXELFORMATDESCRIPTOR),  // size
761         1,                              // version
762         PFD_SUPPORT_OPENGL |
763         PFD_DRAW_TO_WINDOW |
764         PFD_DOUBLEBUFFER,               // use double-buffering by default
765         PFD_TYPE_RGBA,                  // default pixel type
766         0,                              // preferred color depth (don't care)
767         0, 0, 0, 0, 0, 0,               // color bits and shift bits (ignored)
768         0, 0,                           // alpha bits and shift (ignored)
769         0,                              // accumulation total bits
770         0, 0, 0, 0,                     // accumulator RGBA bits (not used)
771         16,                             // depth buffer
772         0,                              // no stencil buffer
773         0,                              // no auxiliary buffers
774         PFD_MAIN_PLANE,                 // main layer
775         0,                              // reserved
776         0, 0, 0,                        // no layer, visible, damage masks
777     };
778 
779     if ( !ppfd )
780         ppfd = &pfd;
781     else
782         *ppfd = pfd;
783 
784     // adjust the PFD using the provided attributes and also check if we can
785     // use PIXELFORMATDESCRIPTOR at all: if multisampling is requested, we
786     // can't as it's not supported by ChoosePixelFormat()
787     switch ( AdjustPFDForAttributes(*ppfd, attribList) )
788     {
789         case 1:
790             return ::ChoosePixelFormat(hdc, ppfd);
791 
792         default:
793             wxFAIL_MSG( "unexpected AdjustPFDForAttributes() return value" );
794             // fall through
795 
796         case 0:
797             // error in attributes
798             return 0;
799 
800         case -1:
801             // requestFSAA == true, will continue as normal
802             // in order to query later for a FSAA pixelformat
803             return -1;
804     }
805 }
806 
807 /* static */
IsDisplaySupported(const int * attribList)808 bool wxGLCanvasBase::IsDisplaySupported(const int *attribList)
809 {
810     // We need a device context to test the pixel format, so get one
811     // for the root window.
812     return wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList) > 0;
813 }
814 
DoSetup(PIXELFORMATDESCRIPTOR & pfd,const int * attribList)815 int wxGLCanvas::DoSetup(PIXELFORMATDESCRIPTOR &pfd, const int *attribList)
816 {
817     int pixelFormat = ChooseMatchingPixelFormat(m_hDC, attribList, &pfd);
818 
819     const bool requestFSAA = pixelFormat == -1;
820     if ( requestFSAA )
821         pixelFormat = ::ChoosePixelFormat(m_hDC, &pfd);
822 
823     if ( !pixelFormat )
824     {
825         wxLogLastError(wxT("ChoosePixelFormat"));
826         return 0;
827     }
828 
829     if ( !::SetPixelFormat(m_hDC, pixelFormat, &pfd) )
830     {
831         wxLogLastError(wxT("SetPixelFormat"));
832         return 0;
833     }
834 
835     return requestFSAA ? -1 : 1;
836 }
837 
838 // ----------------------------------------------------------------------------
839 // palette stuff
840 // ----------------------------------------------------------------------------
841 
842 #if wxUSE_PALETTE
843 
SetupPalette(const wxPalette & palette)844 bool wxGLCanvas::SetupPalette(const wxPalette& palette)
845 {
846     const int pixelFormat = ::GetPixelFormat(m_hDC);
847     if ( !pixelFormat )
848     {
849         wxLogLastError(wxT("GetPixelFormat"));
850         return false;
851     }
852 
853     PIXELFORMATDESCRIPTOR pfd;
854     if ( !::DescribePixelFormat(m_hDC, pixelFormat, sizeof(pfd), &pfd) )
855     {
856         wxLogLastError(wxT("DescribePixelFormat"));
857         return false;
858     }
859 
860     if ( !(pfd.dwFlags & PFD_NEED_PALETTE) )
861         return true;
862 
863     m_palette = palette;
864 
865     if ( !m_palette.IsOk() )
866     {
867         m_palette = CreateDefaultPalette();
868         if ( !m_palette.IsOk() )
869             return false;
870     }
871 
872     if ( !::SelectPalette(m_hDC, GetHpaletteOf(m_palette), FALSE) )
873     {
874         wxLogLastError(wxT("SelectPalette"));
875         return false;
876     }
877 
878     if ( ::RealizePalette(m_hDC) == GDI_ERROR )
879     {
880         wxLogLastError(wxT("RealizePalette"));
881         return false;
882     }
883 
884     return true;
885 }
886 
CreateDefaultPalette()887 wxPalette wxGLCanvas::CreateDefaultPalette()
888 {
889     PIXELFORMATDESCRIPTOR pfd;
890     int paletteSize;
891     int pixelFormat = GetPixelFormat(m_hDC);
892 
893     DescribePixelFormat(m_hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
894 
895     paletteSize = 1 << pfd.cColorBits;
896 
897     LOGPALETTE* pPal =
898      (LOGPALETTE*) malloc(sizeof(LOGPALETTE) + paletteSize * sizeof(PALETTEENTRY));
899     pPal->palVersion = 0x300;
900     pPal->palNumEntries = (WORD)paletteSize;
901 
902     /* build a simple RGB color palette */
903     int redMask = (1 << pfd.cRedBits) - 1;
904     int greenMask = (1 << pfd.cGreenBits) - 1;
905     int blueMask = (1 << pfd.cBlueBits) - 1;
906 
907     for (int i=0; i<paletteSize; ++i)
908     {
909         pPal->palPalEntry[i].peRed =
910             (BYTE)((((i >> pfd.cRedShift) & redMask) * 255) / redMask);
911         pPal->palPalEntry[i].peGreen =
912             (BYTE)((((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask);
913         pPal->palPalEntry[i].peBlue =
914             (BYTE)((((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask);
915         pPal->palPalEntry[i].peFlags = 0;
916     }
917 
918     HPALETTE hPalette = CreatePalette(pPal);
919     free(pPal);
920 
921     wxPalette palette;
922     palette.SetHPALETTE((WXHPALETTE) hPalette);
923 
924     return palette;
925 }
926 
OnQueryNewPalette(wxQueryNewPaletteEvent & event)927 void wxGLCanvas::OnQueryNewPalette(wxQueryNewPaletteEvent& event)
928 {
929   /* realize palette if this is the current window */
930   if ( GetPalette()->IsOk() ) {
931     ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE());
932     ::SelectPalette(GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE);
933     ::RealizePalette(GetHDC());
934     Refresh();
935     event.SetPaletteRealized(true);
936   }
937   else
938     event.SetPaletteRealized(false);
939 }
940 
OnPaletteChanged(wxPaletteChangedEvent & event)941 void wxGLCanvas::OnPaletteChanged(wxPaletteChangedEvent& event)
942 {
943   /* realize palette if this is *not* the current window */
944   if ( GetPalette() &&
945        GetPalette()->IsOk() && (this != event.GetChangedWindow()) )
946   {
947     ::UnrealizeObject((HPALETTE) GetPalette()->GetHPALETTE());
948     ::SelectPalette(GetHDC(), (HPALETTE) GetPalette()->GetHPALETTE(), FALSE);
949     ::RealizePalette(GetHDC());
950     Refresh();
951   }
952 }
953 
954 #endif // wxUSE_PALETTE
955 
956 // ----------------------------------------------------------------------------
957 // deprecated wxGLCanvas methods using implicit wxGLContext
958 // ----------------------------------------------------------------------------
959 
960 // deprecated constructors creating an implicit m_glContext
961 #if WXWIN_COMPATIBILITY_2_8
962 
wxGLCanvas(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name,const int * attribList,const wxPalette & palette)963 wxGLCanvas::wxGLCanvas(wxWindow *parent,
964                        wxWindowID id,
965                        const wxPoint& pos,
966                        const wxSize& size,
967                        long style,
968                        const wxString& name,
969                        const int *attribList,
970                        const wxPalette& palette)
971 {
972     Init();
973 
974     if ( Create(parent, id, pos, size, style, name, attribList, palette) )
975         m_glContext = new wxGLContext(this);
976 }
977 
wxGLCanvas(wxWindow * parent,const wxGLContext * shared,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name,const int * attribList,const wxPalette & palette)978 wxGLCanvas::wxGLCanvas(wxWindow *parent,
979                        const wxGLContext *shared,
980                        wxWindowID id,
981                        const wxPoint& pos,
982                        const wxSize& size,
983                        long style,
984                        const wxString& name,
985                        const int *attribList,
986                        const wxPalette& palette)
987 {
988     Init();
989 
990     if ( Create(parent, id, pos, size, style, name, attribList, palette) )
991         m_glContext = new wxGLContext(this, shared);
992 }
993 
wxGLCanvas(wxWindow * parent,const wxGLCanvas * shared,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name,const int * attribList,const wxPalette & palette)994 wxGLCanvas::wxGLCanvas(wxWindow *parent,
995                        const wxGLCanvas *shared,
996                        wxWindowID id,
997                        const wxPoint& pos,
998                        const wxSize& size,
999                        long style,
1000                        const wxString& name,
1001                        const int *attribList,
1002                        const wxPalette& palette)
1003 {
1004     Init();
1005 
1006     if ( Create(parent, id, pos, size, style, name, attribList, palette) )
1007         m_glContext = new wxGLContext(this, shared ? shared->m_glContext : NULL);
1008 }
1009 
1010 #endif // WXWIN_COMPATIBILITY_2_8
1011 
1012 
1013 // ----------------------------------------------------------------------------
1014 // wxGLApp
1015 // ----------------------------------------------------------------------------
1016 
InitGLVisual(const int * attribList)1017 bool wxGLApp::InitGLVisual(const int *attribList)
1018 {
1019     if ( !wxGLCanvas::ChooseMatchingPixelFormat(ScreenHDC(), attribList) )
1020     {
1021         wxLogError(_("Failed to initialize OpenGL"));
1022         return false;
1023     }
1024 
1025     return true;
1026 }
1027 
1028 #endif // wxUSE_GLCANVAS
1029