Big wxGL classes refactoring/cleanup:
[wxWidgets.git] / src / gtk1 / glcanvas.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk1/glcanvas.cpp
3 // Purpose: wxGLCanvas, for using OpenGL/Mesa with wxWidgets and GTK
4 // Author: Robert Roebling
5 // Modified by:
6 // Created: 17/08/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Robert Roebling
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #if wxUSE_GLCANVAS
16
17 #include "wx/glcanvas.h"
18
19 #ifndef WX_PRECOMP
20 #include "wx/app.h"
21 #include "wx/frame.h"
22 #include "wx/colour.h"
23 #include "wx/module.h"
24 #endif // WX_PRECOMP
25
26 extern "C"
27 {
28 #include "gtk/gtk.h"
29 #include "gdk/gdk.h"
30 #include "gdk/gdkx.h"
31 }
32
33 #include "wx/gtk1/win_gtk.h"
34 #include "wx/gtk1/private.h"
35
36 //---------------------------------------------------------------------------
37 // global data
38 //---------------------------------------------------------------------------
39
40 XVisualInfo *g_vi = (XVisualInfo*) NULL;
41
42 //-----------------------------------------------------------------------------
43 // idle system
44 //-----------------------------------------------------------------------------
45
46 extern void wxapp_install_idle_handler();
47 extern bool g_isIdle;
48
49 // ----------------------------------------------------------------------------
50 // helper functions
51 // ----------------------------------------------------------------------------
52
53 // wrapper around glXMakeContextCurrent/glXMakeCurrent depending on GLX
54 // version
55 static void wxMakeContextCurrent(GLXDrawable drawable, GLXContext context)
56 {
57 if (wxGLCanvas::GetGLXVersion() >= 13)
58 glXMakeContextCurrent( GDK_DISPLAY(), drawable, drawable, context);
59 else // GLX <= 1.2 doesn't have glXMakeContextCurrent()
60 glXMakeCurrent( GDK_DISPLAY(), drawable, context);
61 }
62
63 //---------------------------------------------------------------------------
64 // wxGLContext
65 //---------------------------------------------------------------------------
66
67 IMPLEMENT_CLASS(wxGLContext,wxObject)
68
69 wxGLContext::wxGLContext(wxWindow* win, const wxGLContext* other)
70 {
71 wxGLCanvas *gc = (wxGLCanvas*) win;
72
73 if (wxGLCanvas::GetGLXVersion() >= 13)
74 {
75 GLXFBConfig *fbc = gc->m_fbc;
76 wxCHECK_RET( fbc, _T("invalid GLXFBConfig for OpenGl") );
77 m_glContext = glXCreateNewContext( GDK_DISPLAY(), fbc[0], GLX_RGBA_TYPE,
78 other ? other->m_glContext : None,
79 GL_TRUE );
80 }
81 else // GLX <= 1.2
82 {
83 XVisualInfo *vi = (XVisualInfo *) gc->m_vi;
84 wxCHECK_RET( vi, _T("invalid visual for OpenGl") );
85 m_glContext = glXCreateContext( GDK_DISPLAY(), vi,
86 other ? other->m_glContext : None,
87 GL_TRUE );
88 }
89
90 wxASSERT_MSG( m_glContext, _T("Couldn't create OpenGl context") );
91 }
92
93 wxGLContext::~wxGLContext()
94 {
95 if ( !m_glContext )
96 return;
97
98 if ( m_glContext == glXGetCurrentContext() )
99 wxMakeContextCurrent(None, NULL);
100
101 glXDestroyContext( GDK_DISPLAY(), m_glContext );
102 }
103
104 void wxGLContext::SetCurrent(const wxGLCanvas& win) const
105 {
106 if ( !m_glContext )
107 return;
108
109 GdkWindow *window = GTK_PIZZA(win.m_wxwindow)->bin_window;
110 wxCHECK_RET( window, _T("window must be shown") );
111
112 wxMakeContextCurrent(GDK_WINDOW_XWINDOW(window), m_glContext);
113 }
114
115
116 #if WXWIN_COMPATIBILITY_2_8
117
118 //-----------------------------------------------------------------------------
119 // "realize" from m_wxwindow: used to create m_glContext implicitly
120 //-----------------------------------------------------------------------------
121
122 extern "C" {
123 static gint
124 gtk_glwindow_realized_callback( GtkWidget *WXUNUSED(widget), wxGLCanvas *win )
125 {
126 win->GTKInitImplicitContext();
127
128 return FALSE;
129 }
130 }
131
132 #endif // WXWIN_COMPATIBILITY_2_8
133
134 //-----------------------------------------------------------------------------
135 // "map" from m_wxwindow
136 //-----------------------------------------------------------------------------
137
138 extern "C" {
139 static gint
140 gtk_glwindow_map_callback( GtkWidget * WXUNUSED(widget), wxGLCanvas *win )
141 {
142 wxPaintEvent event( win->GetId() );
143 event.SetEventObject( win );
144 win->GetEventHandler()->ProcessEvent( event );
145
146 win->GetUpdateRegion().Clear();
147
148 return FALSE;
149 }
150 }
151
152 //-----------------------------------------------------------------------------
153 // "expose_event" of m_wxwindow
154 //-----------------------------------------------------------------------------
155
156 extern "C" {
157 static void
158 gtk_glwindow_expose_callback( GtkWidget *WXUNUSED(widget), GdkEventExpose *gdk_event, wxGLCanvas *win )
159 {
160 if (g_isIdle)
161 wxapp_install_idle_handler();
162
163 win->GetUpdateRegion().Union( gdk_event->area.x,
164 gdk_event->area.y,
165 gdk_event->area.width,
166 gdk_event->area.height );
167 }
168 }
169
170 //-----------------------------------------------------------------------------
171 // "draw" of m_wxwindow
172 //-----------------------------------------------------------------------------
173
174 extern "C" {
175 static void
176 gtk_glwindow_draw_callback( GtkWidget *WXUNUSED(widget), GdkRectangle *rect, wxGLCanvas *win )
177 {
178 if (g_isIdle)
179 wxapp_install_idle_handler();
180
181 win->GetUpdateRegion().Union( rect->x, rect->y,
182 rect->width, rect->height );
183 }
184 }
185
186 //-----------------------------------------------------------------------------
187 // "size_allocate" of m_wxwindow
188 //-----------------------------------------------------------------------------
189
190 extern "C" {
191 static void
192 gtk_glcanvas_size_callback( GtkWidget *WXUNUSED(widget), GtkAllocation* alloc, wxGLCanvas *win )
193 {
194 if (g_isIdle)
195 wxapp_install_idle_handler();
196
197 if (!win->m_hasVMT)
198 return;
199
200 wxSizeEvent event( wxSize(win->m_width,win->m_height), win->GetId() );
201 event.SetEventObject( win );
202 win->GetEventHandler()->ProcessEvent( event );
203 }
204 }
205
206 //---------------------------------------------------------------------------
207 // wxGlCanvas
208 //---------------------------------------------------------------------------
209
210 IMPLEMENT_CLASS(wxGLCanvas, wxWindow)
211
212 wxGLCanvas::wxGLCanvas(wxWindow *parent,
213 wxWindowID id,
214 const int *attribList,
215 const wxPoint& pos,
216 const wxSize& size,
217 long style,
218 const wxString& name,
219 const wxPalette& palette)
220 #if WXWIN_COMPATIBILITY_2_8
221 : m_createImplicitContext(false)
222 #endif
223 {
224 Create(parent, id, pos, size, style, name, attribList, palette);
225 }
226
227 #if WXWIN_COMPATIBILITY_2_8
228
229 wxGLCanvas::wxGLCanvas(wxWindow *parent,
230 wxWindowID id,
231 const wxPoint& pos,
232 const wxSize& size,
233 long style,
234 const wxString& name,
235 const int *attribList,
236 const wxPalette& palette)
237 : m_createImplicitContext(true)
238 {
239 Create(parent, id, pos, size, style, name, attribList, palette);
240 }
241
242 wxGLCanvas::wxGLCanvas(wxWindow *parent,
243 const wxGLContext *shared,
244 wxWindowID id,
245 const wxPoint& pos,
246 const wxSize& size,
247 long style,
248 const wxString& name,
249 const int *attribList,
250 const wxPalette& palette)
251 : m_createImplicitContext(true)
252 {
253 m_sharedContext = wx_const_cast(wxGLContext *, shared);
254
255 Create(parent, id, pos, size, style, name, attribList, palette);
256 }
257
258 wxGLCanvas::wxGLCanvas(wxWindow *parent,
259 const wxGLCanvas *shared,
260 wxWindowID id,
261 const wxPoint& pos, const wxSize& size,
262 long style, const wxString& name,
263 const int *attribList,
264 const wxPalette& palette )
265 : m_createImplicitContext(true)
266 {
267 m_sharedContextOf = wx_const_cast(wxGLCanvas *, shared);
268
269 Create(parent, id, pos, size, style, name, attribList, palette);
270 }
271
272 #endif // WXWIN_COMPATIBILITY_2_8
273
274 bool wxGLCanvas::Create(wxWindow *parent,
275 wxWindowID id,
276 const wxPoint& pos,
277 const wxSize& size,
278 long style,
279 const wxString& name,
280 const int *attribList,
281 const wxPalette& palette)
282 {
283 m_noExpose = true;
284 m_nativeSizeEvent = true;
285 m_fbc = NULL;
286 m_vi = NULL;
287
288 if (wxGLCanvas::GetGLXVersion() >= 13)
289 {
290 // GLX >= 1.3 uses a GLXFBConfig
291 GLXFBConfig * fbc = NULL;
292 if (wxTheApp->m_glFBCInfo != NULL)
293 {
294 fbc = (GLXFBConfig *) wxTheApp->m_glFBCInfo;
295 m_canFreeFBC = false; // owned by wxTheApp - don't free upon destruction
296 }
297 else
298 {
299 fbc = (GLXFBConfig *) wxGLCanvas::ChooseGLFBC(attribList);
300 m_canFreeFBC = true;
301 }
302 m_fbc = fbc; // save for later use
303 wxCHECK_MSG( m_fbc, false, _T("required FBConfig couldn't be found") );
304 }
305
306 XVisualInfo *vi = NULL;
307 if (wxTheApp->m_glVisualInfo != NULL)
308 {
309 vi = (XVisualInfo *)wxTheApp->m_glVisualInfo;
310 m_canFreeVi = false; // owned by wxTheApp - don't free upon destruction
311 }
312 else
313 {
314 if (wxGLCanvas::GetGLXVersion() >= 13)
315 // GLX >= 1.3
316 vi = glXGetVisualFromFBConfig(GDK_DISPLAY(), m_fbc[0]);
317 else
318 // GLX <= 1.2
319 vi = (XVisualInfo *) ChooseGLVisual(attribList);
320
321 m_canFreeVi = true;
322 }
323
324 m_vi = vi; // save for later use
325
326 wxCHECK_MSG( m_vi, false, _T("required visual couldn't be found") );
327 GdkVisual *visual;
328 GdkColormap *colormap;
329
330 visual = gdkx_visual_get( vi->visualid );
331 colormap = gdk_colormap_new( visual, TRUE );
332
333 gtk_widget_push_colormap( colormap );
334 gtk_widget_push_visual( visual );
335
336 wxWindow::Create( parent, id, pos, size, style, name );
337 m_glWidget = m_wxwindow;
338
339 gtk_pizza_set_clear( GTK_PIZZA(m_wxwindow), FALSE );
340
341 #if WXWIN_COMPATIBILITY_2_8
342 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "realize",
343 GTK_SIGNAL_FUNC(gtk_glwindow_realized_callback), (gpointer) this);
344 #endif // WXWIN_COMPATIBILITY_2_8
345
346 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "map",
347 GTK_SIGNAL_FUNC(gtk_glwindow_map_callback), (gpointer) this);
348
349 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "expose_event",
350 GTK_SIGNAL_FUNC(gtk_glwindow_expose_callback), (gpointer) this);
351
352 gtk_signal_connect( GTK_OBJECT(m_wxwindow), "draw",
353 GTK_SIGNAL_FUNC(gtk_glwindow_draw_callback), (gpointer) this);
354
355 gtk_signal_connect( GTK_OBJECT(m_widget), "size_allocate",
356 GTK_SIGNAL_FUNC(gtk_glcanvas_size_callback), (gpointer) this);
357
358 gtk_widget_pop_visual();
359
360 gtk_widget_pop_colormap();
361
362 #if WXWIN_COMPATIBILITY_2_8
363 // if our parent window is already visible, we had been realized before we
364 // connected to the "realize" signal and hence our m_glContext hasn't been
365 // initialized yet and we have to do it now
366 if (GTK_WIDGET_REALIZED(m_wxwindow))
367 gtk_glwindow_realized_callback( m_wxwindow, this );
368 #endif // WXWIN_COMPATIBILITY_2_8
369
370 if (GTK_WIDGET_MAPPED(m_wxwindow))
371 gtk_glwindow_map_callback( m_wxwindow, this );
372
373 return true;
374 }
375
376 wxGLCanvas::~wxGLCanvas()
377 {
378 GLXFBConfig * fbc = (GLXFBConfig *) m_fbc;
379 if (fbc && m_canFreeFBC)
380 XFree( fbc );
381
382 XVisualInfo *vi = (XVisualInfo *) m_vi;
383 if (vi && m_canFreeVi)
384 XFree( vi );
385 }
386
387 void* wxGLCanvas::ChooseGLVisual(const int *attribList)
388 {
389 int data[512];
390 GetGLAttribListFromWX( attribList, data );
391
392 Display *dpy = GDK_DISPLAY();
393
394 return glXChooseVisual( dpy, DefaultScreen(dpy), data );
395 }
396
397 void* wxGLCanvas::ChooseGLFBC(const int *attribList)
398 {
399 int data[512];
400 GetGLAttribListFromWX( attribList, data );
401
402 int returned;
403 return glXChooseFBConfig( GDK_DISPLAY(), DefaultScreen(GDK_DISPLAY()),
404 data, &returned );
405 }
406
407
408 void
409 wxGLCanvas::GetGLAttribListFromWX(const int *wx_attribList, int *gl_attribList)
410 {
411 if ( !wx_attribList )
412 {
413 if (wxGLCanvas::GetGLXVersion() >= 13)
414 {
415 // leave GLX >= 1.3 choose the default attributes
416 gl_attribList[0] = 0;
417 }
418 else // GLX < 1.3
419 {
420 int i = 0;
421 // default settings if attriblist = 0
422 gl_attribList[i++] = GLX_RGBA;
423 gl_attribList[i++] = GLX_DOUBLEBUFFER;
424 gl_attribList[i++] = GLX_DEPTH_SIZE; gl_attribList[i++] = 1;
425 gl_attribList[i++] = GLX_RED_SIZE; gl_attribList[i++] = 1;
426 gl_attribList[i++] = GLX_GREEN_SIZE; gl_attribList[i++] = 1;
427 gl_attribList[i++] = GLX_BLUE_SIZE; gl_attribList[i++] = 1;
428 gl_attribList[i++] = GLX_ALPHA_SIZE; gl_attribList[i++] = 0;
429 gl_attribList[i++] = None;
430 }
431 }
432 else // have non-default attributes
433 {
434 int arg=0, p=0;
435 while( (wx_attribList[arg]!=0) && (p<510) )
436 {
437 switch( wx_attribList[arg++] )
438 {
439 case WX_GL_RGBA:
440 if (wxGLCanvas::GetGLXVersion() <= 12)
441 {
442 // for GLX >= 1.3, GLX_RGBA is useless (setting this flags will crash on most opengl implm)
443 gl_attribList[p++] = GLX_RGBA;
444 }
445 break;
446 case WX_GL_BUFFER_SIZE:
447 gl_attribList[p++] = GLX_BUFFER_SIZE;
448 gl_attribList[p++] = wx_attribList[arg++];
449 break;
450 case WX_GL_LEVEL:
451 gl_attribList[p++] = GLX_LEVEL;
452 gl_attribList[p++] = wx_attribList[arg++];
453 break;
454 case WX_GL_DOUBLEBUFFER:
455 gl_attribList[p++] = GLX_DOUBLEBUFFER;
456 gl_attribList[p++] = 1;
457 break;
458 case WX_GL_STEREO:
459 gl_attribList[p++] = GLX_STEREO;
460 break;
461 case WX_GL_AUX_BUFFERS:
462 gl_attribList[p++] = GLX_AUX_BUFFERS;
463 gl_attribList[p++] = wx_attribList[arg++];
464 break;
465 case WX_GL_MIN_RED:
466 gl_attribList[p++] = GLX_RED_SIZE;
467 gl_attribList[p++] = wx_attribList[arg++];
468 break;
469 case WX_GL_MIN_GREEN:
470 gl_attribList[p++] = GLX_GREEN_SIZE;
471 gl_attribList[p++] = wx_attribList[arg++];
472 break;
473 case WX_GL_MIN_BLUE:
474 gl_attribList[p++] = GLX_BLUE_SIZE;
475 gl_attribList[p++] = wx_attribList[arg++];
476 break;
477 case WX_GL_MIN_ALPHA:
478 gl_attribList[p++] = GLX_ALPHA_SIZE;
479 gl_attribList[p++] = wx_attribList[arg++];
480 break;
481 case WX_GL_DEPTH_SIZE:
482 gl_attribList[p++] = GLX_DEPTH_SIZE;
483 gl_attribList[p++] = wx_attribList[arg++];
484 break;
485 case WX_GL_STENCIL_SIZE:
486 gl_attribList[p++] = GLX_STENCIL_SIZE;
487 gl_attribList[p++] = wx_attribList[arg++];
488 break;
489 case WX_GL_MIN_ACCUM_RED:
490 gl_attribList[p++] = GLX_ACCUM_RED_SIZE;
491 gl_attribList[p++] = wx_attribList[arg++];
492 break;
493 case WX_GL_MIN_ACCUM_GREEN:
494 gl_attribList[p++] = GLX_ACCUM_GREEN_SIZE;
495 gl_attribList[p++] = wx_attribList[arg++];
496 break;
497 case WX_GL_MIN_ACCUM_BLUE:
498 gl_attribList[p++] = GLX_ACCUM_BLUE_SIZE;
499 gl_attribList[p++] = wx_attribList[arg++];
500 break;
501 case WX_GL_MIN_ACCUM_ALPHA:
502 gl_attribList[p++] = GLX_ACCUM_ALPHA_SIZE;
503 gl_attribList[p++] = wx_attribList[arg++];
504 break;
505 default:
506 break;
507 }
508 }
509
510 gl_attribList[p] = 0;
511 }
512 }
513
514 /* static */
515 int wxGLCanvas::GetGLXVersion()
516 {
517 static int s_glxVersion = 0;
518 if ( s_glxVersion == 0 )
519 {
520 // check the GLX version
521 int glxMajorVer, glxMinorVer;
522 bool ok = glXQueryVersion(GDK_DISPLAY(), &glxMajorVer, &glxMinorVer);
523 wxASSERT_MSG( ok, _T("GLX version not found") );
524 if (!ok)
525 s_glxVersion = 10; // 1.0 by default
526 else
527 s_glxVersion = glxMajorVer*10 + glxMinorVer;
528 }
529
530 return s_glxVersion;
531 }
532
533 void wxGLCanvas::SwapBuffers()
534 {
535 GdkWindow *window = GTK_PIZZA(m_wxwindow)->bin_window;
536 glXSwapBuffers( GDK_DISPLAY(), GDK_WINDOW_XWINDOW( window ) );
537 }
538
539 void wxGLCanvas::OnInternalIdle()
540 {
541 if (!m_updateRegion.IsEmpty())
542 {
543 wxPaintEvent event( GetId() );
544 event.SetEventObject( this );
545 GetEventHandler()->ProcessEvent( event );
546
547 GetUpdateRegion().Clear();
548 }
549
550 wxWindow::OnInternalIdle();
551 }
552
553 #if WXWIN_COMPATIBILITY_2_8
554
555 void wxGLCanvas::GTKInitImplicitContext()
556 {
557 if ( !m_glContext && m_createImplicitContext )
558 {
559 wxGLContext *share = m_sharedContext;
560 if ( !share && m_sharedContextOf )
561 share = m_sharedContextOf->m_glContext;
562
563 m_glContext = new wxGLContext(this, share);
564 }
565 }
566
567 #endif // WXWIN_COMPATIBILITY_2_8
568
569 //---------------------------------------------------------------------------
570 // wxGLApp
571 //---------------------------------------------------------------------------
572
573 bool wxGLApp::InitGLVisual(const int *attribList)
574 {
575 if ( wxGLCanvas::GetGLXVersion() >= 13 )
576 {
577 if (m_glFBCInfo)
578 XFree(m_glFBCInfo);
579 m_glFBCInfo = wxGLCanvas::ChooseGLFBC(attribList);
580
581 if ( !m_glFBCInfo )
582 return false;
583
584 if (m_glVisualInfo)
585 XFree(m_glVisualInfo);
586 m_glVisualInfo = glXGetVisualFromFBConfig(GDK_DISPLAY(), ((GLXFBConfig *)m_glFBCInfo)[0]);
587 }
588 else // GLX <= 1.2
589 {
590 if (m_glVisualInfo)
591 XFree(m_glVisualInfo);
592 m_glVisualInfo = wxGLCanvas::ChooseGLVisual(attribList);
593 }
594
595 return m_glVisualInfo != NULL;
596 }
597
598 #endif // wxUSE_GLCANVAS