1 // gnash-canvas.cpp: Gtk canvas widget for gnash
2 //
3 // Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 //
19
20 #ifdef HAVE_CONFIG_H
21 #include "gnashconfig.h"
22 #endif
23
24 #include <string>
25
26 #include "gtk_canvas.h"
27 #include "Renderer.h"
28 #include "rc.h"
29 #include "log.h"
30 #include "gtk_glue.h"
31 #include "GnashException.h"
32
33 #ifdef HAVE_VA_VA_H
34 #include "vaapi_utils.h"
35 #endif
36
37 // OpenGL support for rendering in the canvas. This also requires
38 // the GtkGL widget for GTK2.
39 #ifdef HAVE_GTK_GTKGL_H
40 #include "gtk_glue_gtkglext.h"
41 #endif
42
43 // Cairo support for rendering in the canvas.
44 #ifdef RENDERER_CAIRO
45 #include "gtk_glue_cairo.h"
46 #endif
47
48 // Cairo support for rendering in the canvas.
49 #ifdef RENDERER_OPENVG
50 #include "gtk_glue_ovg.h"
51 #endif
52
53 // AGG support, which is the default, for rendering in the canvas.
54 #include "gtk_glue_agg.h"
55
56 #ifdef HAVE_VA_VA_H
57 #include "gtk_glue_agg_vaapi.h"
58 #endif
59
60 struct _GnashCanvas
61 {
62 GtkDrawingArea base_instance;
63 std::unique_ptr<gnash::GtkGlue> glue;
64 std::shared_ptr<gnash::Renderer> renderer;
65 };
66
67 G_DEFINE_TYPE(GnashCanvas, gnash_canvas, GTK_TYPE_DRAWING_AREA)
68
69 static GObjectClass *parent_class = nullptr;
70
71 static void gnash_canvas_class_init(GnashCanvasClass *gnash_canvas_class);
72 static void gnash_canvas_init(GnashCanvas *canvas);
73 static void gnash_canvas_size_allocate(GtkWidget *widget, GtkAllocation *allocation);
74 static gboolean gnash_canvas_expose_event(GtkWidget *widget, GdkEventExpose *event);
75 static gboolean gnash_canvas_configure_event(GtkWidget *widget, GdkEventConfigure *event);
76 static void gnash_canvas_realize(GtkWidget *widget);
77 static void gnash_canvas_after_realize(GtkWidget *widget);
78
79 GtkWidget *
gnash_canvas_new()80 gnash_canvas_new ()
81 {
82 return GTK_WIDGET(g_object_new (GNASH_TYPE_CANVAS, nullptr));
83 }
84
85 static void
gnash_canvas_class_init(GnashCanvasClass * gnash_canvas_class)86 gnash_canvas_class_init(GnashCanvasClass *gnash_canvas_class)
87 {
88 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(gnash_canvas_class);
89
90 parent_class = (GObjectClass *)g_type_class_peek_parent(gnash_canvas_class);
91
92 widget_class->size_allocate = gnash_canvas_size_allocate;
93 widget_class->expose_event = gnash_canvas_expose_event;
94 widget_class->configure_event = gnash_canvas_configure_event;
95 widget_class->realize = gnash_canvas_realize;
96 }
97
98 static void
gnash_canvas_init(GnashCanvas * canvas)99 gnash_canvas_init(GnashCanvas *canvas)
100 {
101
102 canvas->renderer.reset();
103
104 gtk_widget_set_double_buffered(GTK_WIDGET(canvas), FALSE);
105
106 g_signal_connect_after(G_OBJECT(canvas), "realize",
107 G_CALLBACK(gnash_canvas_after_realize), NULL);
108
109 // If we don't set this flag we won't be able to grab focus
110 // ( grabFocus() would be a no-op )
111 GTK_WIDGET_SET_FLAGS (GTK_WIDGET(canvas), GTK_CAN_FOCUS);
112 }
113
114 static void
gnash_canvas_size_allocate(GtkWidget * widget,GtkAllocation * allocation)115 gnash_canvas_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
116 {
117 GnashCanvas *canvas = GNASH_CANVAS(widget);
118
119 gnash::log_debug("gnash_canvas_size_allocate %d %d", allocation->width,
120 allocation->height);
121
122 if (canvas->renderer.get()) {
123 canvas->glue->setRenderHandlerSize(allocation->width,
124 allocation->height);
125 }
126
127 GTK_WIDGET_CLASS(parent_class)->size_allocate (widget, allocation);
128 }
129
130 static gboolean
gnash_canvas_expose_event(GtkWidget * widget,GdkEventExpose * event)131 gnash_canvas_expose_event(GtkWidget *widget, GdkEventExpose *event)
132 {
133 GnashCanvas *canvas = GNASH_CANVAS(widget);
134
135 // In some versions of GTK this can't be const...
136 GdkRegion* nonconst_region = const_cast<GdkRegion*>(event->region);
137
138 canvas->glue->render(nonconst_region);
139
140 return TRUE;
141 }
142
143 static gboolean
gnash_canvas_configure_event(GtkWidget * widget,GdkEventConfigure * event)144 gnash_canvas_configure_event(GtkWidget *widget, GdkEventConfigure *event)
145 {
146 GnashCanvas *canvas = GNASH_CANVAS(widget);
147
148 canvas->glue->configure(widget, event);
149
150 return FALSE;
151 }
152
153 static void
gnash_canvas_realize(GtkWidget * widget)154 gnash_canvas_realize(GtkWidget *widget)
155 {
156 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
157
158 GdkWindowAttr attributes;
159 attributes.window_type = GDK_WINDOW_CHILD;
160 attributes.x = widget->allocation.x;
161 attributes.y = widget->allocation.y;
162 attributes.width = widget->allocation.width;
163 attributes.height = widget->allocation.height;
164 attributes.wclass = GDK_INPUT_OUTPUT;
165 attributes.visual = gtk_widget_get_visual (widget);
166 attributes.colormap = gtk_widget_get_colormap (widget);
167 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
168
169 gint attributes_mask =
170 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
171
172 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
173 &attributes, attributes_mask);
174 gdk_window_set_user_data (widget->window, widget);
175
176 widget->style = gtk_style_attach (widget->style, widget->window);
177 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
178
179 #if defined(RENDERER_CAIRO) || defined(RENDERER_AGG)
180 GnashCanvas *canvas = GNASH_CANVAS(widget);
181 // cairo needs the _drawingArea.window to prepare it ..
182 // TODO: find a way to make 'glue' use independent from actual
183 // renderer in use
184 canvas->glue->prepDrawingArea(GTK_WIDGET(canvas));
185 #endif
186 }
187
188 static void
gnash_canvas_after_realize(GtkWidget * widget)189 gnash_canvas_after_realize(GtkWidget *widget)
190 {
191 GnashCanvas *canvas = GNASH_CANVAS(widget);
192
193 canvas->renderer.reset(canvas->glue->createRenderHandler());
194
195 canvas->glue->setRenderHandlerSize(widget->allocation.width,
196 widget->allocation.height);
197 }
198
199 void
gnash_canvas_setup(GnashCanvas * canvas,std::string & hwaccel,std::string & renderer,int argc,char ** argv[])200 gnash_canvas_setup(GnashCanvas *canvas, std::string& hwaccel,
201 std::string& renderer, int argc, char **argv[])
202 {
203
204 // Order should be VAAPI, X11
205 bool initialized_renderer = false;
206
207 // If a renderer hasn't been defined in gnashrc, or on the command
208 // line, pick a sensible default.
209 if (renderer.empty()) {
210 #ifdef RENDERER_AGG
211 renderer = "agg";
212 #elif defined (RENDERER_CAIRO)
213 renderer = "cairo";
214 #elif defined (RENDERER_OPENGL)
215 renderer = "opengl";
216 #elif defined (RENDERER_OPENVG)
217 renderer = "openvg";
218 #endif
219 }
220
221 // If the Hardware acceleration isn't defined in gnashrc, or on
222 // the command line, pick a sensible default.
223 if (hwaccel.empty()) {
224 hwaccel = "none";
225 }
226
227 #ifdef HAVE_VA_VA_H
228 // Global enable VA-API, if requested
229 gnash::vaapi_set_is_enabled(hwaccel == "vaapi");
230 #endif
231
232 // Use the Cairo renderer. Cairo is also used by GTK2, so using
233 // Cairo makes much sense. Unfortunately, our implementation seems
234 // to have serious performance issues, although it does work.
235 if (renderer == "cairo") {
236 #ifdef RENDERER_CAIRO
237 canvas->glue.reset(new gnash::GtkCairoGlue);
238 #else
239 boost::format fmt = boost::format("Support for renderer %1% "
240 " was not built") % renderer;
241 throw gnash::GnashException(fmt.str());
242 #endif
243 }
244
245 else if (renderer == "opengl") {
246 #ifdef RENDERER_OPENGL
247 canvas->glue.reset(new gnash::GtkGlExtGlue);
248 #else
249 boost::format fmt = boost::format("Support for renderer %1% "
250 " was not built") % renderer;
251 throw gnash::GnashException(fmt.str());
252 #endif
253 }
254 else if ((renderer == "openvg") || (renderer == "ovg")) {
255 renderer = "openvg";
256 #ifdef RENDERER_OPENVG
257 canvas->glue.reset(new gnash::gui::GtkOvgGlue);
258 #else
259 boost::format fmt = boost::format("Support for renderer %1% "
260 " was not built") % renderer;
261 throw gnash::GnashException(fmt.str());
262 #endif
263 }
264 else if (renderer == "agg") {
265 // Use the AGG software library for rendering. While this runs
266 // on any hardware platform, it does have performance issues
267 // on low-end platforms without a GPU. So while AGG may render
268 // streaming video over a network connection just fine,
269 // anything below about 600Mhz CPU may have buffering and
270 // rendering performance issues.
271 // Use LibVva, which works on Nvidia, AT, or Intel 965 GPUs
272 // with AGG or OpenGL.
273 #ifdef RENDERER_AGG
274 #ifdef HAVE_VA_VA_H
275 if (hwaccel == "vaapi") {
276 canvas->glue.reset(new gnash::GtkAggVaapiGlue);
277 // Set the hardware acclerator to the next one to try
278 // if initializing fails.
279 } else
280 #endif
281 {
282 canvas->glue.reset(new gnash::GtkAggGlue);
283 }
284 #else // ndef RENDERER_AGG
285 boost::format fmt = boost::format("Support for renderer %1% "
286 "was not built") % renderer;
287 throw gnash::GnashException(fmt.str());
288 #endif
289 }
290 else {
291 boost::format fmt = boost::format("Non-existent renderer %1% "
292 "specified") % renderer;
293 throw gnash::GnashException(fmt.str());
294 }
295
296 // Initialize the canvas for rendering into
297 initialized_renderer = canvas->glue->init(argc, argv);
298 // If the renderer with the least dependencies fails, we can't
299 // proceed.
300 if (!initialized_renderer) {
301 boost::format fmt = boost::format("Requested renderer %1% (hwaccel: "
302 "%2%) could not be initialized") % renderer % hwaccel;
303 throw gnash::GnashException(fmt.str());
304 }
305
306 if (renderer == "opengl") {
307 // OpenGL glue needs to prepare the drawing area for OpenGL
308 // rendering before
309 // widgets are realized and before the configure event is fired.
310 // TODO: find a way to make '_glue' use independent from
311 // actual renderer in use
312 canvas->glue->prepDrawingArea(GTK_WIDGET(canvas));
313 }
314 }
315
316 void
gnash_canvas_before_rendering(GnashCanvas * canvas,gnash::movie_root * stage)317 gnash_canvas_before_rendering(GnashCanvas *canvas, gnash::movie_root* stage)
318 {
319 canvas->glue->beforeRendering(stage);
320 }
321
322 std::shared_ptr<gnash::Renderer>
gnash_canvas_get_renderer(GnashCanvas * canvas)323 gnash_canvas_get_renderer(GnashCanvas *canvas)
324 {
325 return canvas->renderer;
326 }
327
328