1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/glcanvas.cpp
3 // Purpose: wxGLCanvas, for using OpenGL/Mesa with wxWidgets and GTK
4 // Author: Robert Roebling
8 // Copyright: (c) Robert Roebling
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
17 #include "wx/glcanvas.h"
22 #include "wx/colour.h"
23 #include "wx/module.h"
33 #include "wx/gtk/win_gtk.h"
34 #include "wx/gtk/private.h"
36 // DLL options compatibility check:
38 WX_CHECK_BUILD_OPTIONS("wxGL")
41 //---------------------------------------------------------------------------
43 //---------------------------------------------------------------------------
44 int wxGLCanvas::m_glxVersion
= 0;
46 //---------------------------------------------------------------------------
48 //---------------------------------------------------------------------------
50 XVisualInfo
*g_vi
= (XVisualInfo
*) NULL
;
52 //---------------------------------------------------------------------------
54 //---------------------------------------------------------------------------
56 IMPLEMENT_CLASS(wxGLContext
,wxObject
)
58 wxGLContext::wxGLContext(wxWindow
* win
, const wxGLContext
* other
)
60 wxGLCanvas
*gc
= (wxGLCanvas
*) win
;
62 if (wxGLCanvas::GetGLXVersion() >= 13)
65 GLXFBConfig
*fbc
= gc
->m_fbc
;
66 wxCHECK_RET( fbc
, _T("invalid GLXFBConfig for OpenGl") );
67 m_glContext
= glXCreateNewContext( GDK_DISPLAY(), fbc
[0], GLX_RGBA_TYPE
,
68 other
? other
->m_glContext
: None
,
74 XVisualInfo
*vi
= (XVisualInfo
*) gc
->m_vi
;
75 wxCHECK_RET( vi
, _T("invalid visual for OpenGl") );
76 m_glContext
= glXCreateContext( GDK_DISPLAY(), vi
,
77 other
? other
->m_glContext
: None
,
83 wxFAIL_MSG( _T("Couldn't create OpenGl context") );
87 wxGLContext::~wxGLContext()
89 if (!m_glContext
) return;
91 if (m_glContext
== glXGetCurrentContext())
93 if (wxGLCanvas::GetGLXVersion() >= 13)
95 glXMakeContextCurrent( GDK_DISPLAY(), None
, None
, NULL
);
98 glXMakeCurrent( GDK_DISPLAY(), None
, NULL
);
101 glXDestroyContext( GDK_DISPLAY(), m_glContext
);
104 void wxGLContext::SetCurrent(const wxGLCanvas
& win
) const
108 GdkWindow
*window
= GTK_PIZZA(win
.m_wxwindow
)->bin_window
;
110 if (wxGLCanvas::GetGLXVersion() >= 13)
112 glXMakeContextCurrent( GDK_DISPLAY(), GDK_WINDOW_XWINDOW(window
), GDK_WINDOW_XWINDOW(window
), m_glContext
);
115 glXMakeCurrent( GDK_DISPLAY(), GDK_WINDOW_XWINDOW(window
), m_glContext
);
120 //-----------------------------------------------------------------------------
121 // "realize" from m_wxwindow
122 //-----------------------------------------------------------------------------
126 gtk_glwindow_realized_callback( GtkWidget
*WXUNUSED(widget
), wxGLCanvas
*win
)
128 if (!win
->m_glContext
&& win
->m_createImplicitContext
)
130 wxGLContext
*share
= win
->m_sharedContext
;
131 if ( !share
&& win
->m_sharedContextOf
)
132 share
= win
->m_sharedContextOf
->GetContext();
134 win
->m_glContext
= new wxGLContext(win
, share
);
141 //-----------------------------------------------------------------------------
142 // "map" from m_wxwindow
143 //-----------------------------------------------------------------------------
147 gtk_glwindow_map_callback( GtkWidget
* WXUNUSED(widget
), wxGLCanvas
*win
)
149 // CF: Can the "if" line be removed, and the code unconditionally (always) be run?
150 if (win
->m_glContext
|| !win
->m_createImplicitContext
)
152 wxPaintEvent
event( win
->GetId() );
153 event
.SetEventObject( win
);
154 win
->GetEventHandler()->ProcessEvent( event
);
156 win
->m_exposed
= false;
157 win
->GetUpdateRegion().Clear();
164 //-----------------------------------------------------------------------------
165 // "expose_event" of m_wxwindow
166 //-----------------------------------------------------------------------------
170 gtk_glwindow_expose_callback( GtkWidget
*WXUNUSED(widget
), GdkEventExpose
*gdk_event
, wxGLCanvas
*win
)
172 // don't need to install idle handler, its done from "event" signal
174 win
->m_exposed
= true;
176 win
->GetUpdateRegion().Union( gdk_event
->area
.x
,
178 gdk_event
->area
.width
,
179 gdk_event
->area
.height
);
183 //-----------------------------------------------------------------------------
184 // "size_allocate" of m_wxwindow
185 //-----------------------------------------------------------------------------
189 gtk_glcanvas_size_callback( GtkWidget
*WXUNUSED(widget
), GtkAllocation
* alloc
, wxGLCanvas
*win
)
192 wxapp_install_idle_handler();
197 wxSizeEvent
event( wxSize(win
->m_width
,win
->m_height
), win
->GetId() );
198 event
.SetEventObject( win
);
199 win
->GetEventHandler()->ProcessEvent( event
);
203 //---------------------------------------------------------------------------
205 //---------------------------------------------------------------------------
207 IMPLEMENT_CLASS(wxGLCanvas
, wxWindow
)
209 BEGIN_EVENT_TABLE(wxGLCanvas
, wxWindow
)
210 EVT_SIZE(wxGLCanvas::OnSize
)
213 wxGLCanvas::wxGLCanvas( wxWindow
*parent
, wxWindowID id
,
215 const wxPoint
& pos
, const wxSize
& size
,
216 long style
, const wxString
& name
,
217 const wxPalette
& palette
)
218 : m_createImplicitContext(false)
220 Create( parent
, NULL
, NULL
, id
, pos
, size
, style
, name
, attribList
, palette
);
223 wxGLCanvas::wxGLCanvas( wxWindow
*parent
, wxWindowID id
,
224 const wxPoint
& pos
, const wxSize
& size
,
225 long style
, const wxString
& name
,
227 const wxPalette
& palette
)
228 : m_createImplicitContext(true)
230 Create( parent
, NULL
, NULL
, id
, pos
, size
, style
, name
, attribList
, palette
);
233 wxGLCanvas::wxGLCanvas( wxWindow
*parent
,
234 const wxGLContext
*shared
,
236 const wxPoint
& pos
, const wxSize
& size
,
237 long style
, const wxString
& name
,
239 const wxPalette
& palette
)
240 : m_createImplicitContext(true)
242 Create( parent
, shared
, NULL
, id
, pos
, size
, style
, name
, attribList
, palette
);
245 wxGLCanvas::wxGLCanvas( wxWindow
*parent
,
246 const wxGLCanvas
*shared
,
248 const wxPoint
& pos
, const wxSize
& size
,
249 long style
, const wxString
& name
,
251 const wxPalette
& palette
)
252 : m_createImplicitContext(true)
254 Create( parent
, NULL
, shared
, id
, pos
, size
, style
, name
, attribList
, palette
);
257 bool wxGLCanvas::Create( wxWindow
*parent
,
258 const wxGLContext
*shared
,
259 const wxGLCanvas
*shared_context_of
,
261 const wxPoint
& pos
, const wxSize
& size
,
262 long style
, const wxString
& name
,
264 const wxPalette
& palette
)
266 m_sharedContext
= (wxGLContext
*)shared
; // const_cast
267 m_sharedContextOf
= (wxGLCanvas
*)shared_context_of
; // const_cast
268 m_glContext
= (wxGLContext
*) NULL
;
272 m_nativeSizeEvent
= true;
276 // to be sure the glx version is known
277 wxGLCanvas::QueryGLXVersion();
279 if (wxGLCanvas::GetGLXVersion() >= 13)
281 // GLX >= 1.3 uses a GLXFBConfig
282 GLXFBConfig
* fbc
= NULL
;
283 if (wxTheApp
->m_glFBCInfo
!= NULL
)
285 fbc
= (GLXFBConfig
*) wxTheApp
->m_glFBCInfo
;
286 m_canFreeFBC
= false; // owned by wxTheApp - don't free upon destruction
290 fbc
= (GLXFBConfig
*) wxGLCanvas::ChooseGLFBC(attribList
);
293 m_fbc
= fbc
; // save for later use
294 wxCHECK_MSG( m_fbc
, false, _T("required FBConfig couldn't be found") );
297 XVisualInfo
*vi
= NULL
;
298 if (wxTheApp
->m_glVisualInfo
!= NULL
)
300 vi
= (XVisualInfo
*)wxTheApp
->m_glVisualInfo
;
301 m_canFreeVi
= false; // owned by wxTheApp - don't free upon destruction
305 if (wxGLCanvas::GetGLXVersion() >= 13)
307 vi
= glXGetVisualFromFBConfig(GDK_DISPLAY(), m_fbc
[0]);
310 vi
= (XVisualInfo
*) ChooseGLVisual(attribList
);
315 m_vi
= vi
; // save for later use
317 wxCHECK_MSG( m_vi
, false, _T("required visual couldn't be found") );
319 GdkColormap
*colormap
;
321 // MR: This needs a fix for lower gtk+ versions too. Might need to rethink logic (FIXME)
322 #if defined(__WXGTK20__) && GTK_CHECK_VERSION(2,2,0)
323 if (!gtk_check_version(2,2,0))
325 wxWindow::Create( parent
, id
, pos
, size
, style
, name
);
327 m_glWidget
= m_wxwindow
;
329 GdkScreen
*screen
= gtk_widget_get_screen( m_glWidget
);
330 colormap
= gdk_screen_get_default_colormap(screen
);
331 visual
= gdk_colormap_get_visual(colormap
);
333 if (GDK_VISUAL_XVISUAL(visual
)->visualid
!= vi
->visualid
)
335 visual
= gdk_x11_screen_lookup_visual( screen
, vi
->visualid
);
336 colormap
= gdk_colormap_new(visual
, FALSE
);
339 gtk_widget_set_colormap( m_glWidget
, colormap
);
344 visual
= gdkx_visual_get( vi
->visualid
);
345 colormap
= gdk_colormap_new( visual
, TRUE
);
347 gtk_widget_push_colormap( colormap
);
349 wxWindow::Create( parent
, id
, pos
, size
, style
, name
);
350 m_glWidget
= m_wxwindow
;
353 gtk_widget_set_double_buffered( m_glWidget
, FALSE
);
355 g_signal_connect(m_wxwindow
, "realize", G_CALLBACK(gtk_glwindow_realized_callback
), this);
356 g_signal_connect(m_wxwindow
, "map", G_CALLBACK(gtk_glwindow_map_callback
), this);
357 g_signal_connect(m_wxwindow
, "expose_event", G_CALLBACK(gtk_glwindow_expose_callback
), this);
358 g_signal_connect(m_widget
, "size_allocate", G_CALLBACK(gtk_glcanvas_size_callback
), this);
360 if (gtk_check_version(2,2,0) != NULL
)
362 gtk_widget_pop_colormap();
365 // if our parent window is already visible, we had been realized before we
366 // connected to the "realize" signal and hence our m_glContext hasn't been
367 // initialized yet and we have to do it now
368 if (GTK_WIDGET_REALIZED(m_wxwindow
))
369 gtk_glwindow_realized_callback( m_wxwindow
, this );
371 if (GTK_WIDGET_MAPPED(m_wxwindow
))
372 gtk_glwindow_map_callback( m_wxwindow
, this );
377 wxGLCanvas::~wxGLCanvas()
379 GLXFBConfig
* fbc
= (GLXFBConfig
*) m_fbc
;
380 if (fbc
&& m_canFreeFBC
)
383 XVisualInfo
*vi
= (XVisualInfo
*) m_vi
;
384 if (vi
&& m_canFreeVi
)
390 void* wxGLCanvas::ChooseGLVisual(int *attribList
)
393 GetGLAttribListFromWX( attribList
, data
);
394 attribList
= (int*) data
;
396 Display
*dpy
= GDK_DISPLAY();
398 return glXChooseVisual( dpy
, DefaultScreen(dpy
), attribList
);
401 void* wxGLCanvas::ChooseGLFBC(int *attribList
)
404 GetGLAttribListFromWX( attribList
, data
);
405 attribList
= (int*) data
;
408 return glXChooseFBConfig( GDK_DISPLAY(), DefaultScreen(GDK_DISPLAY()),
409 attribList
, &returned
);
413 void wxGLCanvas::GetGLAttribListFromWX(int *wx_attribList
, int *gl_attribList
)
417 if (wxGLCanvas::GetGLXVersion() >= 13)
418 // leave GLX >= 1.3 choose the default attributes
419 gl_attribList
[0] = 0;
423 // default settings if attriblist = 0
424 gl_attribList
[i
++] = GLX_RGBA
;
425 gl_attribList
[i
++] = GLX_DOUBLEBUFFER
;
426 gl_attribList
[i
++] = GLX_DEPTH_SIZE
; gl_attribList
[i
++] = 1;
427 gl_attribList
[i
++] = GLX_RED_SIZE
; gl_attribList
[i
++] = 1;
428 gl_attribList
[i
++] = GLX_GREEN_SIZE
; gl_attribList
[i
++] = 1;
429 gl_attribList
[i
++] = GLX_BLUE_SIZE
; gl_attribList
[i
++] = 1;
430 gl_attribList
[i
++] = GLX_ALPHA_SIZE
; gl_attribList
[i
++] = 0;
431 gl_attribList
[i
++] = None
;
437 while( (wx_attribList
[arg
]!=0) && (p
<510) )
439 switch( wx_attribList
[arg
++] )
442 if (wxGLCanvas::GetGLXVersion() <= 12)
443 // for GLX >= 1.3, GLX_RGBA is useless (setting this flags will crash on most opengl implm)
444 gl_attribList
[p
++] = GLX_RGBA
;
446 case WX_GL_BUFFER_SIZE
:
447 gl_attribList
[p
++] = GLX_BUFFER_SIZE
;
448 gl_attribList
[p
++] = wx_attribList
[arg
++];
451 gl_attribList
[p
++] = GLX_LEVEL
;
452 gl_attribList
[p
++] = wx_attribList
[arg
++];
454 case WX_GL_DOUBLEBUFFER
:
455 if (wxGLCanvas::GetGLXVersion() <= 12)
456 gl_attribList
[p
++] = GLX_DOUBLEBUFFER
;
458 // for GLX >= 1.3, GLX_DOUBLEBUFFER format is different (1 <=> True)
459 // it seems this flag is useless for some hardware opengl implementation.
460 // but for Mesa 6.2.1, this flag is used so don't ignore it.
461 gl_attribList
[p
++] = GLX_DOUBLEBUFFER
;
462 gl_attribList
[p
++] = 1;
465 gl_attribList
[p
++] = GLX_STEREO
;
467 case WX_GL_AUX_BUFFERS
:
468 gl_attribList
[p
++] = GLX_AUX_BUFFERS
;
469 gl_attribList
[p
++] = wx_attribList
[arg
++];
472 gl_attribList
[p
++] = GLX_RED_SIZE
;
473 gl_attribList
[p
++] = wx_attribList
[arg
++];
475 case WX_GL_MIN_GREEN
:
476 gl_attribList
[p
++] = GLX_GREEN_SIZE
;
477 gl_attribList
[p
++] = wx_attribList
[arg
++];
480 gl_attribList
[p
++] = GLX_BLUE_SIZE
;
481 gl_attribList
[p
++] = wx_attribList
[arg
++];
483 case WX_GL_MIN_ALPHA
:
484 gl_attribList
[p
++] = GLX_ALPHA_SIZE
;
485 gl_attribList
[p
++] = wx_attribList
[arg
++];
487 case WX_GL_DEPTH_SIZE
:
488 gl_attribList
[p
++] = GLX_DEPTH_SIZE
;
489 gl_attribList
[p
++] = wx_attribList
[arg
++];
491 case WX_GL_STENCIL_SIZE
:
492 gl_attribList
[p
++] = GLX_STENCIL_SIZE
;
493 gl_attribList
[p
++] = wx_attribList
[arg
++];
495 case WX_GL_MIN_ACCUM_RED
:
496 gl_attribList
[p
++] = GLX_ACCUM_RED_SIZE
;
497 gl_attribList
[p
++] = wx_attribList
[arg
++];
499 case WX_GL_MIN_ACCUM_GREEN
:
500 gl_attribList
[p
++] = GLX_ACCUM_GREEN_SIZE
;
501 gl_attribList
[p
++] = wx_attribList
[arg
++];
503 case WX_GL_MIN_ACCUM_BLUE
:
504 gl_attribList
[p
++] = GLX_ACCUM_BLUE_SIZE
;
505 gl_attribList
[p
++] = wx_attribList
[arg
++];
507 case WX_GL_MIN_ACCUM_ALPHA
:
508 gl_attribList
[p
++] = GLX_ACCUM_ALPHA_SIZE
;
509 gl_attribList
[p
++] = wx_attribList
[arg
++];
516 gl_attribList
[p
] = 0;
520 void wxGLCanvas::QueryGLXVersion()
522 if (m_glxVersion
== 0)
524 // check the GLX version
525 int glxMajorVer
, glxMinorVer
;
526 bool ok
= glXQueryVersion(GDK_DISPLAY(), &glxMajorVer
, &glxMinorVer
);
527 wxASSERT_MSG( ok
, _T("GLX version not found") );
529 m_glxVersion
= 10; // 1.0 by default
531 m_glxVersion
= glxMajorVer
*10 + glxMinorVer
;
535 int wxGLCanvas::GetGLXVersion()
537 wxASSERT_MSG( m_glxVersion
>0, _T("GLX version has not been initialized with wxGLCanvas::QueryGLXVersion()") );
542 void wxGLCanvas::SwapBuffers()
544 GdkWindow
*window
= GTK_PIZZA(m_wxwindow
)->bin_window
;
545 glXSwapBuffers( GDK_DISPLAY(), GDK_WINDOW_XWINDOW( window
) );
548 void wxGLCanvas::OnSize(wxSizeEvent
& WXUNUSED(event
))
552 void wxGLCanvas::SetCurrent(const wxGLContext
& RC
) const
554 RC
.SetCurrent(*this);
557 void wxGLCanvas::SetCurrent()
560 m_glContext
->SetCurrent(*this);
563 void wxGLCanvas::SetColour( const wxChar
*colour
)
565 wxColour col
= wxTheColourDatabase
->Find(colour
);
568 float r
= (float)(col
.Red()/256.0);
569 float g
= (float)(col
.Green()/256.0);
570 float b
= (float)(col
.Blue()/256.0);
575 void wxGLCanvas::OnInternalIdle()
577 if (/*m_glContext &&*/ m_exposed
)
579 wxPaintEvent
event( GetId() );
580 event
.SetEventObject( this );
581 GetEventHandler()->ProcessEvent( event
);
584 GetUpdateRegion().Clear();
587 wxWindow::OnInternalIdle();
592 //---------------------------------------------------------------------------
594 //---------------------------------------------------------------------------
596 IMPLEMENT_CLASS(wxGLApp
, wxApp
)
603 XFree(m_glVisualInfo
);
606 bool wxGLApp::InitGLVisual(int *attribList
)
608 wxGLCanvas::QueryGLXVersion();
610 if (wxGLCanvas::GetGLXVersion() >= 13)
615 m_glFBCInfo
= wxGLCanvas::ChooseGLFBC(attribList
);
620 XFree(m_glVisualInfo
);
621 m_glVisualInfo
= glXGetVisualFromFBConfig(GDK_DISPLAY(), ((GLXFBConfig
*)m_glFBCInfo
)[0]);
623 return (m_glFBCInfo
!= NULL
) && (m_glVisualInfo
!= NULL
);
629 XFree(m_glVisualInfo
);
630 m_glVisualInfo
= wxGLCanvas::ChooseGLVisual(attribList
);
631 return m_glVisualInfo
!= NULL
;