1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/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 // Copyright:   (c) Robert Roebling
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13 
14 #if wxUSE_GLCANVAS
15 
16 #include "wx/glcanvas.h"
17 
18 #include <gtk/gtk.h>
19 #include <gdk/gdkx.h>
20 #include "wx/gtk/private/gtk2-compat.h"
21 
22 #if WXWIN_COMPATIBILITY_2_8
23 
24 //-----------------------------------------------------------------------------
25 // "realize" from m_wxwindow: used to create m_glContext implicitly
26 //-----------------------------------------------------------------------------
27 
28 extern "C" {
29 static void
gtk_glwindow_realized_callback(GtkWidget * WXUNUSED (widget),wxGLCanvas * win)30 gtk_glwindow_realized_callback( GtkWidget *WXUNUSED(widget), wxGLCanvas *win )
31 {
32     win->GTKInitImplicitContext();
33 }
34 }
35 
36 #endif // WXWIN_COMPATIBILITY_2_8
37 
38 //-----------------------------------------------------------------------------
39 // "map" from m_wxwindow
40 //-----------------------------------------------------------------------------
41 
42 #ifndef __WXGTK3__
43 extern "C" {
44 static void
gtk_glwindow_map_callback(GtkWidget * WXUNUSED (widget),wxGLCanvas * win)45 gtk_glwindow_map_callback( GtkWidget * WXUNUSED(widget), wxGLCanvas *win )
46 {
47     wxPaintEvent event( win->GetId() );
48     event.SetEventObject( win );
49     win->HandleWindowEvent( event );
50 
51     win->m_exposed = false;
52     win->GetUpdateRegion().Clear();
53 }
54 }
55 #endif
56 
57 //-----------------------------------------------------------------------------
58 // "expose_event" of m_wxwindow
59 //-----------------------------------------------------------------------------
60 
61 extern "C" {
62 #ifdef __WXGTK3__
draw(GtkWidget *,cairo_t * cr,wxGLCanvas * win)63 static gboolean draw(GtkWidget*, cairo_t* cr, wxGLCanvas* win)
64 {
65     win->m_exposed = true;
66     if (win->m_cairoPaintContext == NULL)
67     {
68         win->m_cairoPaintContext = cr;
69         cairo_reference(cr);
70     }
71     double x1, y1, x2, y2;
72     cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
73     win->GetUpdateRegion().Union(int(x1), int(y1), int(x2 - x1), int(y2 - y1));
74     return false;
75 }
76 #else
77 static gboolean
78 gtk_glwindow_expose_callback( GtkWidget *WXUNUSED(widget), GdkEventExpose *gdk_event, wxGLCanvas *win )
79 {
80     win->m_exposed = true;
81 
82     win->GetUpdateRegion().Union( gdk_event->area.x,
83                                   gdk_event->area.y,
84                                   gdk_event->area.width,
85                                   gdk_event->area.height );
86     return false;
87 }
88 #endif
89 }
90 
91 //-----------------------------------------------------------------------------
92 // "size_allocate" of m_wxwindow
93 //-----------------------------------------------------------------------------
94 
95 extern "C" {
96 static void
gtk_glcanvas_size_callback(GtkWidget * WXUNUSED (widget),GtkAllocation * WXUNUSED (alloc),wxGLCanvas * win)97 gtk_glcanvas_size_callback(GtkWidget *WXUNUSED(widget),
98                            GtkAllocation * WXUNUSED(alloc),
99                            wxGLCanvas *win)
100 {
101     wxSizeEvent event( wxSize(win->m_width,win->m_height), win->GetId() );
102     event.SetEventObject( win );
103     win->HandleWindowEvent( event );
104 }
105 }
106 
107 //-----------------------------------------------------------------------------
108 // emission hook for "parent-set"
109 //-----------------------------------------------------------------------------
110 
111 extern "C" {
112 static gboolean
parent_set_hook(GSignalInvocationHint *,guint,const GValue * param_values,void * data)113 parent_set_hook(GSignalInvocationHint*, guint, const GValue* param_values, void* data)
114 {
115     wxGLCanvas* win = (wxGLCanvas*)data;
116     if (g_value_peek_pointer(&param_values[0]) == win->m_wxwindow)
117     {
118         const XVisualInfo* xvi = win->GetXVisualInfo();
119         GdkVisual* visual = gtk_widget_get_visual(win->m_wxwindow);
120         if (GDK_VISUAL_XVISUAL(visual)->visualid != xvi->visualid)
121         {
122             GdkScreen* screen = gtk_widget_get_screen(win->m_wxwindow);
123             visual = gdk_x11_screen_lookup_visual(screen, xvi->visualid);
124 #ifdef __WXGTK3__
125             gtk_widget_set_visual(win->m_wxwindow, visual);
126 #else
127             GdkColormap* colormap = gdk_colormap_new(visual, false);
128             gtk_widget_set_colormap(win->m_wxwindow, colormap);
129             g_object_unref(colormap);
130 #endif
131         }
132         // remove hook
133         return false;
134     }
135     return true;
136 }
137 }
138 
139 //---------------------------------------------------------------------------
140 // wxGlCanvas
141 //---------------------------------------------------------------------------
142 
IMPLEMENT_CLASS(wxGLCanvas,wxWindow)143 IMPLEMENT_CLASS(wxGLCanvas, wxWindow)
144 
145 wxGLCanvas::wxGLCanvas(wxWindow *parent,
146                        wxWindowID id,
147                        const int *attribList,
148                        const wxPoint& pos,
149                        const wxSize& size,
150                        long style,
151                        const wxString& name,
152                        const wxPalette& palette)
153 #if WXWIN_COMPATIBILITY_2_8
154     : m_createImplicitContext(false)
155 #endif
156 {
157     Create(parent, id, pos, size, style, name, attribList, palette);
158 }
159 
160 #if WXWIN_COMPATIBILITY_2_8
161 
wxGLCanvas(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name,const int * attribList,const wxPalette & palette)162 wxGLCanvas::wxGLCanvas(wxWindow *parent,
163                        wxWindowID id,
164                        const wxPoint& pos,
165                        const wxSize& size,
166                        long style,
167                        const wxString& name,
168                        const int *attribList,
169                        const wxPalette& palette)
170     : m_createImplicitContext(true)
171 {
172     m_sharedContext = NULL;
173     m_sharedContextOf = NULL;
174 
175     Create(parent, id, pos, size, style, name, attribList, palette);
176 }
177 
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)178 wxGLCanvas::wxGLCanvas(wxWindow *parent,
179                        const wxGLContext *shared,
180                        wxWindowID id,
181                        const wxPoint& pos,
182                        const wxSize& size,
183                        long style,
184                        const wxString& name,
185                        const int *attribList,
186                        const wxPalette& palette)
187     : m_createImplicitContext(true)
188 {
189     m_sharedContext = const_cast<wxGLContext *>(shared);
190 
191     Create(parent, id, pos, size, style, name, attribList, palette);
192 }
193 
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)194 wxGLCanvas::wxGLCanvas(wxWindow *parent,
195                        const wxGLCanvas *shared,
196                        wxWindowID id,
197                        const wxPoint& pos, const wxSize& size,
198                        long style, const wxString& name,
199                        const int *attribList,
200                        const wxPalette& palette )
201     : m_createImplicitContext(true)
202 {
203     m_sharedContext = NULL;
204     m_sharedContextOf = const_cast<wxGLCanvas *>(shared);
205 
206     Create(parent, id, pos, size, style, name, attribList, palette);
207 }
208 
209 #endif // WXWIN_COMPATIBILITY_2_8
210 
IsAvailable()211 static bool IsAvailable()
212 {
213 #ifdef GDK_WINDOWING_X11
214     if ( !GDK_IS_X11_DISPLAY(gdk_display_get_default()) )
215 #endif
216     {
217         wxSafeShowMessage(_("Fatal Error"), _("wxGLCanvas is only supported on X11 currently.  You may be able to\nwork around this by setting environment variable GDK_BACKEND=x11 before starting\nyour program."));
218         return false;
219     }
220 
221     return true;
222 }
223 
Create(wxWindow * parent,wxWindowID id,const wxPoint & pos,const wxSize & size,long style,const wxString & name,const int * attribList,const wxPalette & palette)224 bool wxGLCanvas::Create(wxWindow *parent,
225                         wxWindowID id,
226                         const wxPoint& pos,
227                         const wxSize& size,
228                         long style,
229                         const wxString& name,
230                         const int *attribList,
231                         const wxPalette& palette)
232 {
233     if ( !IsAvailable() )
234         return false;
235 
236 #if wxUSE_PALETTE
237     wxASSERT_MSG( !palette.IsOk(), wxT("palettes not supported") );
238 #endif // wxUSE_PALETTE
239     wxUnusedVar(palette); // Unused when wxDEBUG_LEVEL==0
240 
241     m_exposed = false;
242     m_noExpose = true;
243     m_nativeSizeEvent = true;
244 #ifdef __WXGTK3__
245     m_cairoPaintContext = NULL;
246     m_backgroundStyle = wxBG_STYLE_PAINT;
247 #endif
248 
249     if ( !InitVisual(attribList) )
250         return false;
251 
252     // watch for the "parent-set" signal on m_wxwindow so we can set colormap
253     // before m_wxwindow is realized (which will occur before
254     // wxWindow::Create() returns if parent is already visible)
255     unsigned sig_id = g_signal_lookup("parent-set", GTK_TYPE_WIDGET);
256     g_signal_add_emission_hook(sig_id, 0, parent_set_hook, this, NULL);
257 
258     wxWindow::Create( parent, id, pos, size, style, name );
259 
260     gtk_widget_set_double_buffered(m_wxwindow, false);
261 
262 #if WXWIN_COMPATIBILITY_2_8
263     g_signal_connect(m_wxwindow, "realize",       G_CALLBACK(gtk_glwindow_realized_callback), this);
264 #endif // WXWIN_COMPATIBILITY_2_8
265 #ifdef __WXGTK3__
266     g_signal_connect(m_wxwindow, "draw", G_CALLBACK(draw), this);
267 #else
268     g_signal_connect(m_wxwindow, "map",           G_CALLBACK(gtk_glwindow_map_callback),      this);
269     g_signal_connect(m_wxwindow, "expose_event",  G_CALLBACK(gtk_glwindow_expose_callback),   this);
270 #endif
271     g_signal_connect(m_widget,   "size_allocate", G_CALLBACK(gtk_glcanvas_size_callback),     this);
272 
273 #if WXWIN_COMPATIBILITY_2_8
274     // if our parent window is already visible, we had been realized before we
275     // connected to the "realize" signal and hence our m_glContext hasn't been
276     // initialized yet and we have to do it now
277     if (gtk_widget_get_realized(m_wxwindow))
278         gtk_glwindow_realized_callback( m_wxwindow, this );
279 #endif // WXWIN_COMPATIBILITY_2_8
280 
281 #ifndef __WXGTK3__
282     if (gtk_widget_get_mapped(m_wxwindow))
283         gtk_glwindow_map_callback( m_wxwindow, this );
284 #endif
285 
286     return true;
287 }
288 
SetBackgroundStyle(wxBackgroundStyle)289 bool wxGLCanvas::SetBackgroundStyle(wxBackgroundStyle /* style */)
290 {
291     return false;
292 }
293 
GetXWindow() const294 Window wxGLCanvas::GetXWindow() const
295 {
296     GdkWindow* window = GTKGetDrawingWindow();
297     return window ? GDK_WINDOW_XID(window) : 0;
298 }
299 
OnInternalIdle()300 void wxGLCanvas::OnInternalIdle()
301 {
302     if (m_exposed)
303     {
304 #ifdef __WXGTK3__
305         GTKSendPaintEvents(m_cairoPaintContext);
306         cairo_destroy(m_cairoPaintContext);
307         m_cairoPaintContext = NULL;
308 #else
309         wxPaintEvent event( GetId() );
310         event.SetEventObject( this );
311         HandleWindowEvent( event );
312 #endif
313 
314         m_exposed = false;
315         GetUpdateRegion().Clear();
316     }
317 
318     wxWindow::OnInternalIdle();
319 }
320 
321 #if WXWIN_COMPATIBILITY_2_8
322 
GTKInitImplicitContext()323 void wxGLCanvas::GTKInitImplicitContext()
324 {
325     if ( !m_glContext && m_createImplicitContext )
326     {
327         wxGLContext *share = m_sharedContext;
328         if ( !share && m_sharedContextOf )
329             share = m_sharedContextOf->m_glContext;
330 
331         m_glContext = new wxGLContext(this, share);
332     }
333 }
334 
335 #endif // WXWIN_COMPATIBILITY_2_8
336 
337 #endif // wxUSE_GLCANVAS
338