1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/x11/glcanvas.cpp
3// Purpose:     wxGLCanvas, for using OpenGL with wxWidgets
4//              Uses the GLX extension.
5// Author:      Julian Smart and Wolfram Gloger
6// Modified by:
7// Created:     1995, 1999
8// RCS-ID:      $Id: glcanvas.cpp 48536 2007-09-03 22:35:43Z VZ $
9// Copyright:   (c) Julian Smart, Wolfram Gloger
10// Licence:     wxWindows licence
11/////////////////////////////////////////////////////////////////////////////
12
13// for compilers that support precompilation, includes "wx.h".
14#include "wx/wxprec.h"
15
16#if defined(__BORLANDC__)
17    #pragma hdrstop
18#endif
19
20#if wxUSE_GLCANVAS
21
22#include "wx/glcanvas.h"
23
24#ifndef WX_PRECOMP
25    #include "wx/log.h"
26    #include "wx/app.h"
27    #include "wx/utils.h"
28#endif
29
30#ifdef __VMS
31# pragma message disable nosimpint
32#endif
33#include <X11/Xlib.h>
34#ifdef __VMS
35# pragma message enable nosimpint
36#endif
37#include "wx/x11/private.h"
38
39// DLL options compatibility check:
40#include "wx/build.h"
41WX_CHECK_BUILD_OPTIONS("wxGL")
42
43static inline WXWindow wxGetClientAreaWindow(wxWindow* win)
44{
45#ifdef __WXMOTIF__
46    return win->GetClientXWindow();
47#else
48    return win->GetClientAreaWindow();
49#endif
50}
51
52#ifdef OLD_MESA
53// workaround for bug in Mesa's glx.c
54static int bitcount( unsigned long n )
55{
56    int bits;
57    for (bits=0; n>0;)
58    {
59        if(n & 1) bits++;
60        n = n >> 1;
61    }
62    return bits;
63}
64#endif
65
66/*
67 * GLContext implementation
68 */
69
70IMPLEMENT_CLASS(wxGLContext,wxObject)
71
72wxGLContext::wxGLContext( bool WXUNUSED(isRGB), wxWindow *win,
73                          const wxPalette& WXUNUSED(palette) )
74{
75    m_window = win;
76    // m_widget = win->m_wxwindow;
77
78    wxGLCanvas *gc = (wxGLCanvas*) win;
79    XVisualInfo *vi = (XVisualInfo *) gc->m_vi;
80
81    wxCHECK_RET( vi, wxT("invalid visual for OpenGL") );
82
83    m_glContext = glXCreateContext( (Display *)wxGetDisplay(), vi,
84                                    None, GL_TRUE);
85
86    wxCHECK_RET( m_glContext, wxT("Couldn't create OpenGL context") );
87}
88
89wxGLContext::wxGLContext(
90               bool WXUNUSED(isRGB), wxWindow *win,
91               const wxPalette& WXUNUSED(palette),
92               const wxGLContext *other        /* for sharing display lists */
93)
94{
95    m_window = win;
96    // m_widget = win->m_wxwindow;
97
98    wxGLCanvas *gc = (wxGLCanvas*) win;
99    XVisualInfo *vi = (XVisualInfo *) gc->m_vi;
100
101    wxCHECK_RET( vi, wxT("invalid visual for OpenGL") );
102
103    if( other != 0 )
104        m_glContext = glXCreateContext( (Display *)wxGetDisplay(), vi,
105                                        other->m_glContext, GL_TRUE );
106    else
107        m_glContext = glXCreateContext( (Display *)wxGetDisplay(), vi,
108                                        None, GL_TRUE );
109
110    wxCHECK_RET( m_glContext, wxT("Couldn't create OpenGL context") );
111}
112
113wxGLContext::~wxGLContext()
114{
115    if (!m_glContext) return;
116
117    if (m_glContext == glXGetCurrentContext())
118    {
119        glXMakeCurrent( (Display*) wxGetDisplay(), None, NULL);
120    }
121
122    glXDestroyContext( (Display*) wxGetDisplay(), m_glContext );
123}
124
125void wxGLContext::SwapBuffers()
126{
127    if (m_glContext)
128    {
129        Display* display = (Display*) wxGetDisplay();
130        glXSwapBuffers(display, (Window) wxGetClientAreaWindow(m_window));
131    }
132}
133
134void wxGLContext::SetCurrent()
135{
136    if (m_glContext)
137    {
138        Display* display = (Display*) wxGetDisplay();
139        glXMakeCurrent(display, (Window) wxGetClientAreaWindow(m_window),
140                       m_glContext );
141    }
142}
143
144void wxGLContext::SetColour(const wxChar *colour)
145{
146    wxColour the_colour = wxTheColourDatabase->Find(colour);
147    if(the_colour.Ok())
148    {
149        GLboolean b;
150        glGetBooleanv(GL_RGBA_MODE, &b);
151        if(b)
152        {
153            glColor3ub(the_colour.Red(),
154                    the_colour.Green(),
155                    the_colour.Blue());
156        }
157        else
158        {
159#ifdef __WXMOTIF__
160            the_colour.AllocColour(m_window->GetXDisplay());
161#else
162            the_colour.CalcPixel(wxTheApp->GetMainColormap(wxGetDisplay()));
163#endif
164            GLint pix = (GLint)the_colour.GetPixel();
165            if(pix == -1)
166            {
167                wxLogError(wxT("wxGLCanvas: cannot allocate color\n"));
168                return;
169            }
170            glIndexi(pix);
171        }
172    }
173}
174
175void wxGLContext::SetupPixelFormat()
176{
177}
178
179void wxGLContext::SetupPalette( const wxPalette& WXUNUSED(palette) )
180{
181}
182
183wxPalette wxGLContext::CreateDefaultPalette()
184{
185    return wxNullPalette;
186}
187
188
189
190
191/*
192 * GLCanvas implementation
193 */
194
195IMPLEMENT_CLASS(wxGLCanvas, wxScrolledWindow)
196
197BEGIN_EVENT_TABLE(wxGLCanvas, wxScrolledWindow)
198//    EVT_SIZE(wxGLCanvas::OnSize)
199END_EVENT_TABLE()
200
201
202wxGLCanvas::wxGLCanvas( wxWindow *parent, wxWindowID id,
203                        const wxPoint& pos, const wxSize& size,
204                        long style, const wxString& name,
205                        int *attribList,
206                        const wxPalette& palette )
207: wxScrolledWindow(parent, id, pos, size, style, name)
208{
209    Create( parent, NULL, NULL, id, pos, size, style, name, attribList, palette );
210}
211
212wxGLCanvas::wxGLCanvas( wxWindow *parent,
213                        const wxGLContext *shared,
214                        wxWindowID id,
215                        const wxPoint& pos, const wxSize& size,
216                        long style, const wxString& name,
217                        int *attribList,
218                        const wxPalette& palette )
219: wxScrolledWindow(parent, id, pos, size, style, name)
220{
221    Create( parent, shared, NULL, id, pos, size, style, name, attribList, palette );
222}
223
224wxGLCanvas::wxGLCanvas( wxWindow *parent,
225                        const wxGLCanvas *shared,
226                        wxWindowID id,
227                        const wxPoint& pos, const wxSize& size,
228                        long style, const wxString& name,
229                        int *attribList,
230                        const wxPalette& palette )
231: wxScrolledWindow(parent, id, pos, size, style, name)
232{
233    Create( parent, NULL, shared, id, pos, size, style, name, attribList, palette );
234}
235
236
237/*
238bool wxGLCanvas::Create(wxWindow *parent,
239  const wxGLContext *shared, const wxGLCanvas *shared_context_of,
240  wxWindowID id = -1, const wxPoint& pos,
241  const wxSize& size, long style,
242  const wxString& name, int *attribList, const wxPalette& palette):
243    wxScrolledWindow(parent, id, pos, size, style, name)
244*/
245
246bool wxGLCanvas::Create( wxWindow *parent,
247                         const wxGLContext *shared,
248                         const wxGLCanvas *shared_context_of,
249                         wxWindowID id,
250                         const wxPoint& pos, const wxSize& size,
251                         long style, const wxString& name,
252                         int *attribList,
253                         const wxPalette& palette)
254{
255    XVisualInfo *vi, vi_templ;
256    XWindowAttributes xwa;
257    int val, n;
258
259    m_sharedContext = (wxGLContext*)shared;  // const_cast
260    m_sharedContextOf = (wxGLCanvas*)shared_context_of;  // const_cast
261    m_glContext = (wxGLContext*) NULL;
262
263    Display* display = (Display*) wxGetDisplay();
264
265    // Check for the presence of the GLX extension
266    if(!glXQueryExtension(display, NULL, NULL))
267    {
268        wxLogDebug(wxT("wxGLCanvas: GLX extension is missing\n"));
269        return false;
270    }
271
272    if(attribList) {
273      int data[512], arg=0, p=0;
274
275      while( (attribList[arg]!=0) && (p<512) )
276      {
277        switch( attribList[arg++] )
278        {
279          case WX_GL_RGBA: data[p++] = GLX_RGBA; break;
280          case WX_GL_BUFFER_SIZE:
281            data[p++]=GLX_BUFFER_SIZE; data[p++]=attribList[arg++]; break;
282          case WX_GL_LEVEL:
283            data[p++]=GLX_LEVEL; data[p++]=attribList[arg++]; break;
284          case WX_GL_DOUBLEBUFFER: data[p++] = GLX_DOUBLEBUFFER; data[p++] = 1; break;
285          case WX_GL_STEREO: data[p++] = GLX_STEREO; data[p++] = 1; break;
286          case WX_GL_AUX_BUFFERS:
287            data[p++]=GLX_AUX_BUFFERS; data[p++]=attribList[arg++]; break;
288          case WX_GL_MIN_RED:
289            data[p++]=GLX_RED_SIZE; data[p++]=attribList[arg++]; break;
290          case WX_GL_MIN_GREEN:
291            data[p++]=GLX_GREEN_SIZE; data[p++]=attribList[arg++]; break;
292          case WX_GL_MIN_BLUE:
293            data[p++]=GLX_BLUE_SIZE; data[p++]=attribList[arg++]; break;
294          case WX_GL_MIN_ALPHA:
295            data[p++]=GLX_ALPHA_SIZE; data[p++]=attribList[arg++]; break;
296          case WX_GL_DEPTH_SIZE:
297            data[p++]=GLX_DEPTH_SIZE; data[p++]=attribList[arg++]; break;
298          case WX_GL_STENCIL_SIZE:
299            data[p++]=GLX_STENCIL_SIZE; data[p++]=attribList[arg++]; break;
300          case WX_GL_MIN_ACCUM_RED:
301            data[p++]=GLX_ACCUM_RED_SIZE; data[p++]=attribList[arg++]; break;
302          case WX_GL_MIN_ACCUM_GREEN:
303            data[p++]=GLX_ACCUM_GREEN_SIZE; data[p++]=attribList[arg++]; break;
304          case WX_GL_MIN_ACCUM_BLUE:
305            data[p++]=GLX_ACCUM_BLUE_SIZE; data[p++]=attribList[arg++]; break;
306          case WX_GL_MIN_ACCUM_ALPHA:
307            data[p++]=GLX_ACCUM_ALPHA_SIZE; data[p++]=attribList[arg++]; break;
308          default:
309            break;
310        }
311      }
312      data[p] = 0;
313
314      attribList = (int*) data;
315      // Get an appropriate visual
316      vi = glXChooseVisual(display, DefaultScreen(display), attribList);
317      if(!vi) return false;
318
319      // Here we should make sure that vi is the same visual as the
320      // one used by the xwindow drawable in wxCanvas.  However,
321      // there is currently no mechanism for this in wx_canvs.cc.
322    } else {
323        // By default, we use the visual of xwindow
324        // NI: is this really senseful ? opengl in e.g. color index mode ?
325      XGetWindowAttributes(display, (Window)wxGetClientAreaWindow(this), &xwa);
326      vi_templ.visualid = XVisualIDFromVisual(xwa.visual);
327      vi = XGetVisualInfo(display, VisualIDMask, &vi_templ, &n);
328      if(!vi) return false;
329      glXGetConfig(display, vi, GLX_USE_GL, &val);
330      if(!val) return false;
331      // Basically, this is it.  It should be possible to use vi
332      // in glXCreateContext() below.  But this fails with Mesa.
333      // I notified the Mesa author about it; there may be a fix.
334#ifdef OLD_MESA
335      // Construct an attribute list matching the visual
336      int a_list[32];
337      n = 0;
338      if(vi->c_class==TrueColor || vi->c_class==DirectColor) { // RGBA visual
339          a_list[n++] = GLX_RGBA;
340          a_list[n++] = GLX_RED_SIZE;
341          a_list[n++] = bitcount(vi->red_mask);
342          a_list[n++] = GLX_GREEN_SIZE;
343          a_list[n++] = bitcount(vi->green_mask);
344          a_list[n++] = GLX_BLUE_SIZE;
345          a_list[n++] = bitcount(vi->blue_mask);
346          glXGetConfig(display, vi, GLX_ALPHA_SIZE, &val);
347          a_list[n++] = GLX_ALPHA_SIZE;
348          a_list[n++] = val;
349      } else { // Color index visual
350          glXGetConfig(display, vi, GLX_BUFFER_SIZE, &val);
351          a_list[n++] = GLX_BUFFER_SIZE;
352          a_list[n++] = val;
353      }
354      a_list[n] = None;
355      // XFree(vi);
356      vi = glXChooseVisual(display, DefaultScreen(display), a_list);
357      if(!vi) return false;
358#endif /* OLD_MESA */
359    }
360
361    m_vi = vi;  // safe for later use
362
363    wxCHECK_MSG( m_vi, false, wxT("required visual couldn't be found") );
364
365    // Create the GLX context and make it current
366
367    wxGLContext *share= m_sharedContext;
368    if (share==NULL && m_sharedContextOf)
369        share = m_sharedContextOf->GetContext();
370
371    m_glContext = new wxGLContext( TRUE, this, wxNullPalette, share );
372
373#ifndef OLD_MESA
374    // XFree(vi);
375#endif
376    SetCurrent();
377
378    return true;
379}
380
381wxGLCanvas::~wxGLCanvas(void)
382{
383    XVisualInfo *vi = (XVisualInfo *) m_vi;
384
385    if (vi) XFree( vi );
386    if (m_glContext) delete m_glContext;
387
388    // Display* display = (Display*) GetXDisplay();
389    // if(glx_cx) glXDestroyContext(display, glx_cx);
390}
391
392void wxGLCanvas::SwapBuffers()
393{
394    if( m_glContext ) m_glContext->SwapBuffers();
395
396    // Display* display = (Display*) GetXDisplay();
397    // if(glx_cx) glXSwapBuffers(display, (Window) GetClientAreaWindow());
398}
399
400void wxGLCanvas::SetCurrent()
401{
402    if( m_glContext ) m_glContext->SetCurrent();
403
404    // Display* display = (Display*) GetXDisplay();
405    // if(glx_cx) glXMakeCurrent(display, (Window) GetClientAreaWindow(), glx_cx);
406}
407
408void wxGLCanvas::SetColour(const wxChar *col)
409{
410    if( m_glContext ) m_glContext->SetColour(col);
411}
412
413#endif
414    // wxUSE_GLCANVAS
415