1
<chapter id="extensions">
2
<title>Gnash Extensions</title>
5
Gnash supports extending the SWF specification by creating
6
custom ActionScript classes that are compiled code, as opposed to
7
the existing method of defining custom classes as
8
ActionScript. Executing compiled code has many performance
9
benefits over having to interpret the byte stream of the
14
I can already hear people complaining now about the concept of
15
extending SWF, so this in no way affects Gnash's ability to play
16
SWF movies when functioning as a browser plugin.
17
Gnash's goal is still to function in a way that is compatible
18
with the current proprietary Flash player.
22
But at the same time, we see SWF as the ideal scripting language
23
for a digital multi-media streaming environment. There are many
24
resources for SWF movie creators for widgets, higher level APIs,
25
all sorts of desirable things. But for those of use committed to
26
using free software tools for SWF, our options are very
31
Rather than launching a multi-year project to duplicate all
32
classes in the commercial Flash IDE, it's much more efficient to
33
use existing development libraries much like Python or Perl
34
do. The extension mechanism in Gnash allows wrappers to be created
35
for any C or C++ development library. Unlike the proprietary Flash
36
IDE, which compiles all the extension libraries into byte codes
37
from ActionScript, the support is moved to the player side. Movies
38
with all of the goodies of the proprietary IDE in them play in
39
Gnash just fine, as it's all just byte codes by then.
43
This trick works because until SWF version 9, all the
44
ActionScript class names and methods are passed as ASCII strings
45
into the SWF movie. So the Gnash Virtual Machine just loads the
46
extension file if that class name is invoked in the movie. All
47
extension files specify the class name and methods it implements
48
in an identical style as adding any new ActionScript class. The
49
advantage is the class itself is compiled code, and runs much
50
faster than the equivalent byte codes which all have to be
55
<title>Creating A New Extension</title>
58
Each new extension should live in it's own directory. The
59
extensions included in Gnash are all in the
60
<emphasis>gnash/extensions</emphasis> directory. Creating an extension
61
requires a Makefile.am,
65
If you are adding this extension to the Gnash source tree
66
itself, then you need to make two changes to add the new
71
The first change is to add the directory to the list in
72
extensions/Makefile.am. This can be done either by adding the
73
new directory to the SUBDIRS setting, or by wrapping it in a
78
The other change is to add it to the AC_OUTPUT list in
79
<emphasis>configure.ac</emphasis> so the new directory will be
80
configured along with the rest of Gnash.
84
Each extension should have an ActionScript source file included
85
that tests the new class, and this file should be referenced in
86
the new Makefile.am in the <emphasis>check_PROGRAMS</emphasis>
87
variable so that "make check" works.
91
When creating an extension that is a wrapper for an existing
92
development library API, it's often better to make this a thin
93
layer, than to get carried away with creating beautiful
94
abstractions. Higher-level classes that offer a lot of new
95
functionality are fine, but is different than wrapping a library
96
so it can be used from within Gnash.
100
<title>Crafting an Extension</title>
103
All extensions have the same requirements, namely setting up a
104
few defined function callbacks, which the Gnash VM then uses
105
to do the right thing. The initial two function callbacks are
106
for handling the interface of the newly created object so that
107
Gnash can find and use it.
111
The first function is commonly called
112
<emphasis>attachInterface</emphasis>, and this sets the other
113
function callbacks for all the methods this class
114
supports. The method callbacks are attached to the parent
115
class by using <emphasis>init_member()</emphasis> to set a C
116
function pointer to the string value used in the SWF movie.
120
// Attach DummyClass 'func1' and 'func2' methods to the given object
122
attachInterface(as_object& obj) {
123
obj.init_member("func1", &ext_func1);
124
obj.init_member("func2", &ext_func2);
129
The second function is commonly called
130
<emphasis>getInterface()</emphasis>, and this returns a
131
pointer to a static prototype of the class.
132
Gnash uses garbage collection for ActionScript objects
133
so you need to register the static with the VM to give it
134
a chance to be marked as reachable.
140
static boost::intrusive_ptr<as_object> o;
143
VM::get().addStatic(o);
151
This is the callback that gets executed when constructing a
152
new object for the VM. In this example we'll assume the new
153
ActionScript class is called <emphasis>DummyExt</emphasis>,
154
and has two methods, <emphasis>func1</emphasis> and
155
<emphasis>func2</emphasis>.
159
dummyext_ctor(const fn_call& fn)
161
DummyExt *obj = new DummyExt(); // will setup prototypes
163
return as_value(obj);
168
The trick for the above simple constructor to work is that
169
class appartenence is setup in the C++ DummyExt constructor
170
itself, which should derive from as_object and construct the
171
base passing it the interface (prototype) of it's class.
175
class DummyExt : public as_object
180
as_object(getInterface()) // returns the static prototype
187
Initialize the extension. This is looked for by the extension
188
handling code in each extension, and is executed when the
189
extension is loaded. This is the main entry point into the
190
extension. This function can be found because the prefix of
191
<emphasis>dummyext</emphasis>, which also matches the file
192
name of the extension. Gnash uses the name of the extension
193
file itself when looking for the init function.
198
dummyext_class_init(as_object &obj)
200
static builtin_function* cl=NULL;
203
// Create a builtin function using the given constructor
204
// to instanciate objects and exporting the given interface
205
cl = new builtin_function(&dummyext_ctor, getInterface());
206
VM::get().addStatic(cl); // will forbid to collect the class
209
obj.init_member("DummyExt", cl);
215
The callbacks are all C functions. Like all the other code
216
that implements ActionScript, parameters to the function are
217
passed in using the <emphasis>fn_call</emphasis> data
218
structure. The return code, if any, is also returned using
219
this data structure. <emphasis>this_ptr</emphasis> is the
220
object that the method is a member of.
223
// Creates a new button with the label as the text.
224
as_value func1(const fn_call& fn) {
225
// Following line will ensure 'func1' is called for a DummyExt instance,
226
// or would throw an exception which should behave as if we returned the
228
boost::intrusive_ptr<DummyExt> ptr = ensureType<DummyExt>(fn.this_ptr);
231
std::string label = fn.arg(0).to_string();
232
bool ret = ptr->dummy_text_label(label);
233
return as_value(ret);
243
<title>Debugging An Extension</title>
246
As extensions are loaded dynamically at runtime, debugging one
247
can be difficult. You can use GDB, but you have the problem of
248
not being able to set a breakpoint in Gnash until
249
<emphasis>after</emphasis> the extension has been loaded into
250
Gnash's VM. The easy solution is to use the Gnash debugger.
254
You can insert these few lines in any file that you wish to
255
manually start the debugger. Once at the console, you can attach
256
GDB to the process. Then you can set breakpoints, etc... and you
257
are at the point of execution where the console was started. To
258
then continue playing the movie, type the <emphasis>c</emphasis>
259
(for continue) command to the Gnash console.
262
// Get the debugger instance
263
static Debugger& debugger = Debugger::getDefaultInstance();
265
// Enable the debugger
266
debugger.enabled(true);
267
// Stop and drop into a console
272
You can also resort to the time honored technique of creating a
273
loop before the point you want to set a breakpoint for. Gnash
274
will stop playing the movie at this point, and then you can
275
externally attach GDB to the running process, or type
276
<emphasis>^C</emphasis> to drop into the GDB command console.
286
Once you have set the breakpoints you want, reset the value of
287
the <emphasis>stall</emphasis> variable to break out of the
288
loop, and the SWF movie will then continue playing.
291
(gdb) set variable stall = false;
298
<title>Included Extensions</title>
301
Gnash has some extensions included in the distribution. This is
302
mostly because they were written by the Gnash team. Extensions
303
can be external to gnash, Gnash needs no compiled in knowledge
304
to load an extension file.