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
);
184 //-----------------------------------------------------------------------------
185 // "size_allocate" of m_wxwindow
186 //-----------------------------------------------------------------------------
190 gtk_glcanvas_size_callback( GtkWidget
*WXUNUSED(widget
), GtkAllocation
* alloc
, wxGLCanvas
*win
)
193 wxapp_install_idle_handler();
198 wxSizeEvent
event( wxSize(win
->m_width
,win
->m_height
), win
->GetId() );
199 event
.SetEventObject( win
);
200 win
->GetEventHandler()->ProcessEvent( event
);
204 //---------------------------------------------------------------------------
206 //---------------------------------------------------------------------------
208 IMPLEMENT_CLASS(wxGLCanvas
, wxWindow
)
210 BEGIN_EVENT_TABLE(wxGLCanvas
, wxWindow
)
211 EVT_SIZE(wxGLCanvas::OnSize
)
214 wxGLCanvas::wxGLCanvas( wxWindow
*parent
, wxWindowID id
,
216 const wxPoint
& pos
, const wxSize
& size
,
217 long style
, const wxString
& name
,
218 const wxPalette
& palette
)
219 : m_createImplicitContext(false)
221 Create( parent
, NULL
, NULL
, id
, pos
, size
, style
, name
, attribList
, palette
);
224 wxGLCanvas::wxGLCanvas( wxWindow
*parent
, wxWindowID id
,
225 const wxPoint
& pos
, const wxSize
& size
,
226 long style
, const wxString
& name
,
228 const wxPalette
& palette
)
229 : m_createImplicitContext(true)
231 Create( parent
, NULL
, NULL
, id
, pos
, size
, style
, name
, attribList
, palette
);
234 wxGLCanvas::wxGLCanvas( wxWindow
*parent
,
235 const wxGLContext
*shared
,
237 const wxPoint
& pos
, const wxSize
& size
,
238 long style
, const wxString
& name
,
240 const wxPalette
& palette
)
241 : m_createImplicitContext(true)
243 Create( parent
, shared
, NULL
, id
, pos
, size
, style
, name
, attribList
, palette
);
246 wxGLCanvas::wxGLCanvas( wxWindow
*parent
,
247 const wxGLCanvas
*shared
,
249 const wxPoint
& pos
, const wxSize
& size
,
250 long style
, const wxString
& name
,
252 const wxPalette
& palette
)
253 : m_createImplicitContext(true)
255 Create( parent
, NULL
, shared
, id
, pos
, size
, style
, name
, attribList
, palette
);
258 bool wxGLCanvas::Create( wxWindow
*parent
,
259 const wxGLContext
*shared
,
260 const wxGLCanvas
*shared_context_of
,
262 const wxPoint
& pos
, const wxSize
& size
,
263 long style
, const wxString
& name
,
265 const wxPalette
& palette
)
267 m_sharedContext
= (wxGLContext
*)shared
; // const_cast
268 m_sharedContextOf
= (wxGLCanvas
*)shared_context_of
; // const_cast
269 m_glContext
= (wxGLContext
*) NULL
;
273 m_nativeSizeEvent
= true;
277 // to be sure the glx version is known
278 wxGLCanvas::QueryGLXVersion();
280 if (wxGLCanvas::GetGLXVersion() >= 13)
282 // GLX >= 1.3 uses a GLXFBConfig
283 GLXFBConfig
* fbc
= NULL
;
284 if (wxTheApp
->m_glFBCInfo
!= NULL
)
286 fbc
= (GLXFBConfig
*) wxTheApp
->m_glFBCInfo
;
287 m_canFreeFBC
= false; // owned by wxTheApp - don't free upon destruction
291 fbc
= (GLXFBConfig
*) wxGLCanvas::ChooseGLFBC(attribList
);
294 m_fbc
= fbc
; // save for later use
295 wxCHECK_MSG( m_fbc
, false, _T("required FBConfig couldn't be found") );
298 XVisualInfo
*vi
= NULL
;
299 if (wxTheApp
->m_glVisualInfo
!= NULL
)
301 vi
= (XVisualInfo
*)wxTheApp
->m_glVisualInfo
;
302 m_canFreeVi
= false; // owned by wxTheApp - don't free upon destruction
306 if (wxGLCanvas::GetGLXVersion() >= 13)
308 vi
= glXGetVisualFromFBConfig(GDK_DISPLAY(), m_fbc
[0]);
311 vi
= (XVisualInfo
*) ChooseGLVisual(attribList
);
316 m_vi
= vi
; // save for later use
318 wxCHECK_MSG( m_vi
, false, _T("required visual couldn't be found") );
320 GdkColormap
*colormap
;
322 // MR: This needs a fix for lower gtk+ versions too. Might need to rethink logic (FIXME)
323 #if defined(__WXGTK20__) && GTK_CHECK_VERSION(2,2,0)
324 if (!gtk_check_version(2,2,0))
326 wxWindow::Create( parent
, id
, pos
, size
, style
, name
);
328 m_glWidget
= m_wxwindow
;
330 GdkScreen
*screen
= gtk_widget_get_screen( m_glWidget
);
331 colormap
= gdk_screen_get_default_colormap(screen
);
332 visual
= gdk_colormap_get_visual(colormap
);
334 if (GDK_VISUAL_XVISUAL(visual
)->visualid
!= vi
->visualid
)
336 visual
= gdk_x11_screen_lookup_visual( screen
, vi
->visualid
);
337 colormap
= gdk_colormap_new(visual
, FALSE
);
340 gtk_widget_set_colormap( m_glWidget
, colormap
);
345 visual
= gdkx_visual_get( vi
->visualid
);
346 colormap
= gdk_colormap_new( visual
, TRUE
);
348 gtk_widget_push_colormap( colormap
);
350 wxWindow::Create( parent
, id
, pos
, size
, style
, name
);
351 m_glWidget
= m_wxwindow
;
354 gtk_widget_set_double_buffered( m_glWidget
, FALSE
);
356 g_signal_connect(m_wxwindow
, "realize", G_CALLBACK(gtk_glwindow_realized_callback
), this);
357 g_signal_connect(m_wxwindow
, "map", G_CALLBACK(gtk_glwindow_map_callback
), this);
358 g_signal_connect(m_wxwindow
, "expose_event", G_CALLBACK(gtk_glwindow_expose_callback
), this);
359 g_signal_connect(m_widget
, "size_allocate", G_CALLBACK(gtk_glcanvas_size_callback
), this);
361 if (gtk_check_version(2,2,0) != NULL
)
363 gtk_widget_pop_colormap();
366 // if our parent window is already visible, we had been realized before we
367 // connected to the "realize" signal and hence our m_glContext hasn't been
368 // initialized yet and we have to do it now
369 if (GTK_WIDGET_REALIZED(m_wxwindow
))
370 gtk_glwindow_realized_callback( m_wxwindow
, this );
372 if (GTK_WIDGET_MAPPED(m_wxwindow
))
373 gtk_glwindow_map_callback( m_wxwindow
, this );
378 wxGLCanvas::~wxGLCanvas()
380 GLXFBConfig
* fbc
= (GLXFBConfig
*) m_fbc
;
381 if (fbc
&& m_canFreeFBC
)
384 XVisualInfo
*vi
= (XVisualInfo
*) m_vi
;
385 if (vi
&& m_canFreeVi
)
391 void* wxGLCanvas::ChooseGLVisual(int *attribList
)
394 GetGLAttribListFromWX( attribList
, data
);
395 attribList
= (int*) data
;
397 Display
*dpy
= GDK_DISPLAY();
399 return glXChooseVisual( dpy
, DefaultScreen(dpy
), attribList
);
402 void* wxGLCanvas::ChooseGLFBC(int *attribList
)
405 GetGLAttribListFromWX( attribList
, data
);
406 attribList
= (int*) data
;
409 return glXChooseFBConfig( GDK_DISPLAY(), DefaultScreen(GDK_DISPLAY()),
410 attribList
, &returned
);
414 void wxGLCanvas::GetGLAttribListFromWX(int *wx_attribList
, int *gl_attribList
)
418 if (wxGLCanvas::GetGLXVersion() >= 13)
419 // leave GLX >= 1.3 choose the default attributes
420 gl_attribList
[0] = 0;
424 // default settings if attriblist = 0
425 gl_attribList
[i
++] = GLX_RGBA
;
426 gl_attribList
[i
++] = GLX_DOUBLEBUFFER
;
427 gl_attribList
[i
++] = GLX_DEPTH_SIZE
; gl_attribList
[i
++] = 1;
428 gl_attribList
[i
++] = GLX_RED_SIZE
; gl_attribList
[i
++] = 1;
429 gl_attribList
[i
++] = GLX_GREEN_SIZE
; gl_attribList
[i
++] = 1;
430 gl_attribList
[i
++] = GLX_BLUE_SIZE
; gl_attribList
[i
++] = 1;
431 gl_attribList
[i
++] = GLX_ALPHA_SIZE
; gl_attribList
[i
++] = 0;
432 gl_attribList
[i
++] = None
;
438 while( (wx_attribList
[arg
]!=0) && (p
<510) )
440 switch( wx_attribList
[arg
++] )
443 if (wxGLCanvas::GetGLXVersion() <= 12)
444 // for GLX >= 1.3, GLX_RGBA is useless (setting this flags will crash on most opengl implm)
445 gl_attribList
[p
++] = GLX_RGBA
;
447 case WX_GL_BUFFER_SIZE
:
448 gl_attribList
[p
++] = GLX_BUFFER_SIZE
;
449 gl_attribList
[p
++] = wx_attribList
[arg
++];
452 gl_attribList
[p
++] = GLX_LEVEL
;
453 gl_attribList
[p
++] = wx_attribList
[arg
++];
455 case WX_GL_DOUBLEBUFFER
:
456 if (wxGLCanvas::GetGLXVersion() <= 12)
457 gl_attribList
[p
++] = GLX_DOUBLEBUFFER
;
459 // for GLX >= 1.3, GLX_DOUBLEBUFFER format is different (1 <=> True)
460 // it seems this flag is useless for some hardware opengl implementation.
461 // but for Mesa 6.2.1, this flag is used so don't ignore it.
462 gl_attribList
[p
++] = GLX_DOUBLEBUFFER
;
463 gl_attribList
[p
++] = 1;
466 gl_attribList
[p
++] = GLX_STEREO
;
468 case WX_GL_AUX_BUFFERS
:
469 gl_attribList
[p
++] = GLX_AUX_BUFFERS
;
470 gl_attribList
[p
++] = wx_attribList
[arg
++];
473 gl_attribList
[p
++] = GLX_RED_SIZE
;
474 gl_attribList
[p
++] = wx_attribList
[arg
++];
476 case WX_GL_MIN_GREEN
:
477 gl_attribList
[p
++] = GLX_GREEN_SIZE
;
478 gl_attribList
[p
++] = wx_attribList
[arg
++];
481 gl_attribList
[p
++] = GLX_BLUE_SIZE
;
482 gl_attribList
[p
++] = wx_attribList
[arg
++];
484 case WX_GL_MIN_ALPHA
:
485 gl_attribList
[p
++] = GLX_ALPHA_SIZE
;
486 gl_attribList
[p
++] = wx_attribList
[arg
++];
488 case WX_GL_DEPTH_SIZE
:
489 gl_attribList
[p
++] = GLX_DEPTH_SIZE
;
490 gl_attribList
[p
++] = wx_attribList
[arg
++];
492 case WX_GL_STENCIL_SIZE
:
493 gl_attribList
[p
++] = GLX_STENCIL_SIZE
;
494 gl_attribList
[p
++] = wx_attribList
[arg
++];
496 case WX_GL_MIN_ACCUM_RED
:
497 gl_attribList
[p
++] = GLX_ACCUM_RED_SIZE
;
498 gl_attribList
[p
++] = wx_attribList
[arg
++];
500 case WX_GL_MIN_ACCUM_GREEN
:
501 gl_attribList
[p
++] = GLX_ACCUM_GREEN_SIZE
;
502 gl_attribList
[p
++] = wx_attribList
[arg
++];
504 case WX_GL_MIN_ACCUM_BLUE
:
505 gl_attribList
[p
++] = GLX_ACCUM_BLUE_SIZE
;
506 gl_attribList
[p
++] = wx_attribList
[arg
++];
508 case WX_GL_MIN_ACCUM_ALPHA
:
509 gl_attribList
[p
++] = GLX_ACCUM_ALPHA_SIZE
;
510 gl_attribList
[p
++] = wx_attribList
[arg
++];
517 gl_attribList
[p
] = 0;
521 void wxGLCanvas::QueryGLXVersion()
523 if (m_glxVersion
== 0)
525 // check the GLX version
526 int glxMajorVer
, glxMinorVer
;
527 bool ok
= glXQueryVersion(GDK_DISPLAY(), &glxMajorVer
, &glxMinorVer
);
528 wxASSERT_MSG( ok
, _T("GLX version not found") );
530 m_glxVersion
= 10; // 1.0 by default
532 m_glxVersion
= glxMajorVer
*10 + glxMinorVer
;
536 int wxGLCanvas::GetGLXVersion()
538 wxASSERT_MSG( m_glxVersion
>0, _T("GLX version has not been initialized with wxGLCanvas::QueryGLXVersion()") );
543 void wxGLCanvas::SwapBuffers()
545 GdkWindow
*window
= GTK_PIZZA(m_wxwindow
)->bin_window
;
546 glXSwapBuffers( GDK_DISPLAY(), GDK_WINDOW_XWINDOW( window
) );
549 void wxGLCanvas::OnSize(wxSizeEvent
& WXUNUSED(event
))
553 void wxGLCanvas::SetCurrent(const wxGLContext
& RC
) const
555 RC
.SetCurrent(*this);
558 void wxGLCanvas::SetCurrent()
561 m_glContext
->SetCurrent(*this);
564 void wxGLCanvas::SetColour( const wxChar
*colour
)
566 wxColour col
= wxTheColourDatabase
->Find(colour
);
569 float r
= (float)(col
.Red()/256.0);
570 float g
= (float)(col
.Green()/256.0);
571 float b
= (float)(col
.Blue()/256.0);
576 void wxGLCanvas::OnInternalIdle()
578 if (/*m_glContext &&*/ m_exposed
)
580 wxPaintEvent
event( GetId() );
581 event
.SetEventObject( this );
582 GetEventHandler()->ProcessEvent( event
);
585 GetUpdateRegion().Clear();
588 wxWindow::OnInternalIdle();
593 //---------------------------------------------------------------------------
595 //---------------------------------------------------------------------------
597 IMPLEMENT_CLASS(wxGLApp
, wxApp
)
604 XFree(m_glVisualInfo
);
607 bool wxGLApp::InitGLVisual(int *attribList
)
609 wxGLCanvas::QueryGLXVersion();
611 if (wxGLCanvas::GetGLXVersion() >= 13)
616 m_glFBCInfo
= wxGLCanvas::ChooseGLFBC(attribList
);
621 XFree(m_glVisualInfo
);
622 m_glVisualInfo
= glXGetVisualFromFBConfig(GDK_DISPLAY(), ((GLXFBConfig
*)m_glFBCInfo
)[0]);
624 return (m_glFBCInfo
!= NULL
) && (m_glVisualInfo
!= NULL
);
630 XFree(m_glVisualInfo
);
631 m_glVisualInfo
= wxGLCanvas::ChooseGLVisual(attribList
);
632 return m_glVisualInfo
!= NULL
;