actually set the variable here
[swfdec.git] / swfdec / swfdec_player.c
blobb139fe3c5346c71c68629a52089638609aa1d2f6
1 /* Swfdec
2 * Copyright (C) 2006-2008 Benjamin Otte <otte@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include <errno.h>
25 #include <math.h>
26 #include <string.h>
27 #include <stdlib.h>
29 #include "swfdec_player_internal.h"
30 #include "swfdec_as_frame_internal.h"
31 #include "swfdec_as_internal.h"
32 #include "swfdec_as_strings.h"
33 #include "swfdec_audio_internal.h"
34 #include "swfdec_button_movie.h" /* for mouse cursor */
35 #include "swfdec_cache.h"
36 #include "swfdec_debug.h"
37 #include "swfdec_enums.h"
38 #include "swfdec_event.h"
39 #include "swfdec_filter.h"
40 #include "swfdec_internal.h"
41 #include "swfdec_loader_internal.h"
42 #include "swfdec_marshal.h"
43 #include "swfdec_movie.h"
44 #include "swfdec_renderer_internal.h"
45 #include "swfdec_resource.h"
46 #include "swfdec_sandbox.h"
47 #include "swfdec_script_internal.h"
48 #include "swfdec_sprite_movie.h"
49 #include "swfdec_text_field_movie.h"
50 #include "swfdec_utils.h"
52 /*** gtk-doc ***/
54 /**
55 * SECTION:SwfdecPlayer
56 * @title: SwfdecPlayer
57 * @short_description: main playback object
59 * A #SwfdecPlayer is the main object used for playing back Flash files through
60 * Swfdec.
62 * A player interacts with the outside world in a multitude of ways. The most
63 * important ones are described below.
65 * Input is handled via the
66 * <link linkend="swfdec-SwfdecLoader">SwfdecLoader</link> class. A
67 * #SwfdecLoader is set on a new player using swfdec_player_set_loader().
69 * When the loader has provided enough data, you can start playing the file.
70 * This is done in steps by calling swfdec_player_advance() - preferrably as
71 * often as swfdec_player_get_next_event() indicates. Or you can provide user input
72 * to the player by calling for example swfdec_player_handle_mouse().
74 * You can use swfdec_player_render() to draw the current state of the player.
75 * After that, connect to the SwfdecPlayer:invalidate signal to be notified of
76 * changes.
78 * Audio output is handled via the
79 * <link linkend="swfdec-SwfdecAudio">SwfdecAudio</link> class. One
80 * #SwfdecAudio object is created for every output using the
81 * SwfdecPlayer::audio-added signal.
84 /**
85 * SwfdecPlayer:
87 * This is the base object used for playing Flash files.
90 /**
91 * SECTION:Enumerations
92 * @title: Enumerations
93 * @short_description: enumerations used in Swfdec
95 * This file lists all of the enumerations used in various parts of Swfdec.
98 /**
99 * SwfdecMouseCursor:
100 * @SWFDEC_MOUSE_CURSOR_NORMAL: a normal mouse cursor
101 * @SWFDEC_MOUSE_CURSOR_NONE: no mouse image
102 * @SWFDEC_MOUSE_CURSOR_TEXT: a mouse cursor suitable for text editing
103 * @SWFDEC_MOUSE_CURSOR_CLICK: a mouse cursor for clicking a hyperlink or a
104 * button
106 * This enumeration describes the possible types for the SwfdecPlayer::mouse-cursor
107 * property.
111 * SwfdecAlignment:
112 * @SWFDEC_ALIGNMENT_TOP_LEFT: top left
113 * @SWFDEC_ALIGNMENT_TOP: top
114 * @SWFDEC_ALIGNMENT_TOP_RIGHT: top right
115 * @SWFDEC_ALIGNMENT_LEFT: left
116 * @SWFDEC_ALIGNMENT_CENTER: center
117 * @SWFDEC_ALIGNMENT_RIGHT: right
118 * @SWFDEC_ALIGNMENT_BOTTOM_LEFT: left
119 * @SWFDEC_ALIGNMENT_BOTTOM: bottom
120 * @SWFDEC_ALIGNMENT_BOTTOM_RIGHT: bottom right
122 * These are the possible values for the alignment of an unscaled movie.
126 * SwfdecScaleMode:
127 * @SWFDEC_SCALE_SHOW_ALL: Show the whole content as large as possible
128 * @SWFDEC_SCALE_NO_BORDER: Fill the whole area, possibly cropping parts
129 * @SWFDEC_SCALE_EXACT_FIT: Fill the whole area, don't keep aspect ratio
130 * @SWFDEC_SCALE_NONE: Do not scale the movie at all
132 * Describes how the movie should be scaled if the given size doesn't equal the
133 * movie's size.
137 * SwfdecKey:
138 * @SWFDEC_KEY_LEFT_MOUSE: the left mouse button
139 * @SWFDEC_KEY_RIGHT_MOUSE: the left mouse button
140 * @SWFDEC_KEY_MIDDLE_MOUSE: the middle mouse button
141 * @SWFDEC_KEY_BACKSPACE: the backspace key
142 * @SWFDEC_KEY_TAB: the tab key
143 * @SWFDEC_KEY_CLEAR: the clear key
144 * @SWFDEC_KEY_ENTER: the enter key
145 * @SWFDEC_KEY_SHIFT: the shift key
146 * @SWFDEC_KEY_CONTROL: the control key
147 * @SWFDEC_KEY_ALT: the alt key
148 * @SWFDEC_KEY_CAPS_LOCK: the caps lock key
149 * @SWFDEC_KEY_ESCAPE: the escape key
150 * @SWFDEC_KEY_SPACE: the space key
151 * @SWFDEC_KEY_PAGE_UP: the page up key
152 * @SWFDEC_KEY_PAGE_DOWN: the page down key
153 * @SWFDEC_KEY_END: the end key
154 * @SWFDEC_KEY_HOME: the home key
155 * @SWFDEC_KEY_LEFT: the left key
156 * @SWFDEC_KEY_UP: the up key
157 * @SWFDEC_KEY_RIGHT: the right key
158 * @SWFDEC_KEY_DOWN: the down key
159 * @SWFDEC_KEY_INSERT: the insert key
160 * @SWFDEC_KEY_DELETE: the delete key
161 * @SWFDEC_KEY_HELP: the help key
162 * @SWFDEC_KEY_0: the 0 key
163 * @SWFDEC_KEY_1: the 1 key
164 * @SWFDEC_KEY_2: the 2 key
165 * @SWFDEC_KEY_3: the 3 key
166 * @SWFDEC_KEY_4: the 4 key
167 * @SWFDEC_KEY_5: the 5 key
168 * @SWFDEC_KEY_6: the 6 key
169 * @SWFDEC_KEY_7: the 7 key
170 * @SWFDEC_KEY_8: the 8 key
171 * @SWFDEC_KEY_9: the 9 key
172 * @SWFDEC_KEY_A: the ! key
173 * @SWFDEC_KEY_B: the B key
174 * @SWFDEC_KEY_C: the C key
175 * @SWFDEC_KEY_D: the D key
176 * @SWFDEC_KEY_E: the E key
177 * @SWFDEC_KEY_F: the F key
178 * @SWFDEC_KEY_G: the G key
179 * @SWFDEC_KEY_H: the H key
180 * @SWFDEC_KEY_I: the I key
181 * @SWFDEC_KEY_J: the J key
182 * @SWFDEC_KEY_K: the K key
183 * @SWFDEC_KEY_L: the L key
184 * @SWFDEC_KEY_M: the M key
185 * @SWFDEC_KEY_N: the N key
186 * @SWFDEC_KEY_O: the O key
187 * @SWFDEC_KEY_P: the P key
188 * @SWFDEC_KEY_Q: the Q key
189 * @SWFDEC_KEY_R: the R key
190 * @SWFDEC_KEY_S: the S key
191 * @SWFDEC_KEY_T: the T key
192 * @SWFDEC_KEY_U: the U key
193 * @SWFDEC_KEY_V: the V key
194 * @SWFDEC_KEY_W: the W key
195 * @SWFDEC_KEY_X: the X key
196 * @SWFDEC_KEY_Y: the Y key
197 * @SWFDEC_KEY_Z: the Z key
198 * @SWFDEC_KEY_NUMPAD_0: the 0 key on the numeric keypad
199 * @SWFDEC_KEY_NUMPAD_1: the 1 key on the numeric keypad
200 * @SWFDEC_KEY_NUMPAD_2: the 2 key on the numeric keypad
201 * @SWFDEC_KEY_NUMPAD_3: the 3 key on the numeric keypad
202 * @SWFDEC_KEY_NUMPAD_4: the 4 key on the numeric keypad
203 * @SWFDEC_KEY_NUMPAD_5: the 5 key on the numeric keypad
204 * @SWFDEC_KEY_NUMPAD_6: the 6 key on the numeric keypad
205 * @SWFDEC_KEY_NUMPAD_7: the 7 key on the numeric keypad
206 * @SWFDEC_KEY_NUMPAD_8: the 8 key on the numeric keypad
207 * @SWFDEC_KEY_NUMPAD_9: the 9 key on the numeric keypad
208 * @SWFDEC_KEY_NUMPAD_MULTIPLY: the multiply key on the numeric keypad
209 * @SWFDEC_KEY_NUMPAD_ADD: the add key on the numeric keypad
210 * @SWFDEC_KEY_NUMPAD_SUBTRACT: the subtract key on the numeric keypad
211 * @SWFDEC_KEY_NUMPAD_DECIMAL: the decimal key on the numeric keypad
212 * @SWFDEC_KEY_NUMPAD_DIVIDE: the divide key on the numeric keypad
213 * @SWFDEC_KEY_F1: the F1 key
214 * @SWFDEC_KEY_F2: the F2 key
215 * @SWFDEC_KEY_F3: the F3 key
216 * @SWFDEC_KEY_F4: the F4 key
217 * @SWFDEC_KEY_F5: the F5 key
218 * @SWFDEC_KEY_F6: the F6 key
219 * @SWFDEC_KEY_F7: the F7 key
220 * @SWFDEC_KEY_F8: the F8 key
221 * @SWFDEC_KEY_F9: the F9 key
222 * @SWFDEC_KEY_F10: the F10 key
223 * @SWFDEC_KEY_F11: the F11 key
224 * @SWFDEC_KEY_F12: the F12 key
225 * @SWFDEC_KEY_F13: the F13 key
226 * @SWFDEC_KEY_F14: the F14 key
227 * @SWFDEC_KEY_F15: the F15 key
228 * @SWFDEC_KEY_NUM_LOCK: the num lock key
229 * @SWFDEC_KEY_SCROLL_LOCK: the scroll lock key
230 * @SWFDEC_KEY_SEMICOLON: the semicolon key (on English keyboards)
231 * @SWFDEC_KEY_EQUAL: the equal key (on English keyboards)
232 * @SWFDEC_KEY_COMMA: the comma key (on English keyboards)
233 * @SWFDEC_KEY_MINUS: the minus key (on English keyboards)
234 * @SWFDEC_KEY_DOT: the dot key (on English keyboards)
235 * @SWFDEC_KEY_SLASH: the slash key (on English keyboards)
236 * @SWFDEC_KEY_GRAVE: the grave key (on English keyboards)
237 * @SWFDEC_KEY_LEFT_BRACKET: the left bracket key (on English keyboards)
238 * @SWFDEC_KEY_BACKSLASH: the backslash key (on English keyboards)
239 * @SWFDEC_KEY_RIGHT_BRACKET: the right bracket key (on English keyboards)
240 * @SWFDEC_KEY_APOSTROPHE: the apostrophe key (on English keyboards)
242 * Lists all known key codes in Swfdec and their meanings on an English
243 * keyboard. Note that key codes in Flash represent virtual key codes as used
244 * in Microsoft Windows.
247 /*** timeval type mapping ***/
250 * SWFDEC_TYPE_TIME_VAL:
252 * This type wraps a @GTimeVal object as a boxed type and makes it available
253 * for use in object properties.
255 static gpointer
256 swfdec_time_val_copy (gpointer boxed)
258 return g_memdup (boxed, sizeof (GTimeVal));
261 GType
262 swfdec_time_val_get_type (void)
264 static GType type = 0;
266 if (!type) {
267 type = g_boxed_type_register_static ("SwfdecTimeVal",
268 swfdec_time_val_copy, g_free);
271 return type;
274 /*** Timeouts ***/
276 static SwfdecTick
277 swfdec_player_get_next_event_time (SwfdecPlayer *player)
279 SwfdecPlayerPrivate *priv = player->priv;
281 if (priv->timeouts) {
282 SwfdecTick next = ((SwfdecTimeout *) priv->timeouts->data)->timestamp;
283 /* This can happen because advancing only uses millisecond granularity */
284 if (next < priv->time)
285 return 0;
286 else
287 return next - priv->time;
288 } else {
289 return G_MAXUINT64;
294 * swfdec_player_add_timeout:
295 * @player: a #SwfdecPlayer
296 * @timeout: timeout to add
298 * Adds a timeout to @player. The timeout will be removed automatically when
299 * triggered, so you need to use swfdec_player_add_timeout() to add it again.
300 * The #SwfdecTimeout struct and callback does not use a data callback pointer.
301 * It's suggested that you use the struct as part of your own bigger struct
302 * and get it back like this:
303 * <programlisting>
304 * typedef struct {
305 * // ...
306 * SwfdecTimeout timeout;
307 * } MyStruct;
309 * static void
310 * my_struct_timeout_callback (SwfdecTimeout *timeout)
312 * MyStruct *mystruct = (MyStruct *) ((void *) timeout - G_STRUCT_OFFSET (MyStruct, timeout));
314 * // do stuff
316 * </programlisting>
318 void
319 swfdec_player_add_timeout (SwfdecPlayer *player, SwfdecTimeout *timeout)
321 SwfdecPlayerPrivate *priv;
322 GList *walk;
323 SwfdecTick next_tick;
325 g_return_if_fail (SWFDEC_IS_PLAYER (player));
326 g_return_if_fail (timeout != NULL);
327 g_return_if_fail (timeout->timestamp >= player->priv->time);
328 g_return_if_fail (timeout->callback != NULL);
330 priv = player->priv;
331 SWFDEC_LOG ("adding timeout %p in %"G_GUINT64_FORMAT" msecs", timeout,
332 SWFDEC_TICKS_TO_MSECS (timeout->timestamp - priv->time));
333 next_tick = swfdec_player_get_next_event_time (player);
334 /* the order is important, on events with the same time, we make sure the new one is last */
335 for (walk = priv->timeouts; walk; walk = walk->next) {
336 SwfdecTimeout *cur = walk->data;
337 if (cur->timestamp > timeout->timestamp)
338 break;
340 priv->timeouts = g_list_insert_before (priv->timeouts, walk, timeout);
341 if (next_tick != swfdec_player_get_next_event_time (player))
342 g_object_notify (G_OBJECT (player), "next-event");
346 * swfdec_player_remove_timeout:
347 * @player: a #SwfdecPlayer
348 * @timeout: a timeout that should be removed
350 * Removes the @timeout from the list of scheduled timeouts. The timeout must
351 * have been added with swfdec_player_add_timeout() before.
353 void
354 swfdec_player_remove_timeout (SwfdecPlayer *player, SwfdecTimeout *timeout)
356 SwfdecPlayerPrivate *priv;
357 SwfdecTick next_tick;
359 g_return_if_fail (SWFDEC_IS_PLAYER (player));
360 g_return_if_fail (timeout != NULL);
361 /* FIXME: can't use that due to rounding issues */
362 //g_return_if_fail (timeout->timestamp >= player->priv->time);
363 g_return_if_fail (timeout->callback != NULL);
365 SWFDEC_LOG ("removing timeout %p", timeout);
366 priv = player->priv;
367 next_tick = swfdec_player_get_next_event_time (player);
368 priv->timeouts = g_list_remove (priv->timeouts, timeout);
369 if (next_tick != swfdec_player_get_next_event_time (player))
370 g_object_notify (G_OBJECT (player), "next-event");
373 /*** Actions ***/
375 typedef struct {
376 SwfdecActor * actor; /* the actor to trigger the action on */
377 SwfdecScript * script; /* script to execute or NULL to trigger action */
378 SwfdecEventType event; /* the action to trigger */
379 guint8 key;
380 } SwfdecPlayerAction;
382 typedef struct {
383 gpointer object;
384 SwfdecActionFunc func;
385 gpointer data;
386 } SwfdecPlayerExternalAction;
388 static void
389 swfdec_player_compress_actions (SwfdecRingBuffer *buffer)
391 SwfdecPlayerAction *action, tmp;
392 guint i = 0;
394 for (i = swfdec_ring_buffer_get_n_elements (buffer); i > 0; i--) {
395 action = swfdec_ring_buffer_pop (buffer);
396 g_assert (action);
397 if (action->actor == NULL)
398 continue;
399 tmp = *action;
400 action = swfdec_ring_buffer_push (buffer);
401 *action = tmp;
403 SWFDEC_INFO ("compresed action queue to %u elements",
404 swfdec_ring_buffer_get_n_elements (buffer));
405 for (i = 0; i < swfdec_ring_buffer_get_n_elements (buffer); i++) {
406 action = swfdec_ring_buffer_peek_nth (buffer, i);
407 g_assert (action->actor != NULL);
411 static void
412 swfdec_player_do_add_action (SwfdecPlayer *player, guint importance, SwfdecPlayerAction *act)
414 SwfdecPlayerPrivate *priv = player->priv;
415 SwfdecPlayerAction *action = swfdec_ring_buffer_push (priv->actions[importance]);
416 if (action == NULL) {
417 /* try to get rid of freed actions */
418 swfdec_player_compress_actions (priv->actions[importance]);
419 action = swfdec_ring_buffer_push (priv->actions[importance]);
420 if (action == NULL) {
421 if (swfdec_ring_buffer_get_size (priv->actions[importance]) == 256) {
422 SWFDEC_WARNING ("256 levels of recursion were exceeded in one action list.");
424 swfdec_ring_buffer_set_size (priv->actions[importance],
425 swfdec_ring_buffer_get_size (priv->actions[importance]) + 16);
426 action = swfdec_ring_buffer_push (priv->actions[importance]);
427 g_assert (action);
430 *action = *act;
434 * swfdec_player_add_event:
435 * @player: a #SwfdecPlayer
436 * @movie: the movie on which to trigger the event
437 * @type: type of the event
438 * @type: key of the event
439 * @importance: importance of the event
441 * Adds an action to the @player. Actions are used by Flash player to solve
442 * reentrancy issues. Instead of calling back into the Actionscript engine,
443 * an action is queued for later execution. So if you're writing code that
444 * is calling Actionscript code, you want to do this by using actions.
446 void
447 swfdec_player_add_action (SwfdecPlayer *player, SwfdecActor *actor,
448 SwfdecEventType type, guint8 key, guint importance)
450 SwfdecPlayerAction action = { actor, NULL, type, key };
452 g_return_if_fail (SWFDEC_IS_PLAYER (player));
453 g_return_if_fail (SWFDEC_IS_ACTOR (actor));
454 g_return_if_fail (importance < SWFDEC_PLAYER_N_ACTION_QUEUES);
456 SWFDEC_LOG ("adding action %s %u", SWFDEC_MOVIE (actor)->name, type);
457 swfdec_player_do_add_action (player, importance, &action);
460 void
461 swfdec_player_add_action_script (SwfdecPlayer *player, SwfdecActor *actor,
462 SwfdecScript *script, guint importance)
464 SwfdecPlayerAction action = { actor, script, 0 };
466 g_return_if_fail (SWFDEC_IS_PLAYER (player));
467 g_return_if_fail (SWFDEC_IS_ACTOR (actor));
468 g_return_if_fail (script != NULL);
469 g_return_if_fail (importance < SWFDEC_PLAYER_N_ACTION_QUEUES);
471 SWFDEC_LOG ("adding action script %s %s", SWFDEC_MOVIE (actor)->name, script->name);
472 swfdec_player_do_add_action (player, importance, &action);
476 * swfdec_player_remove_all_actions:
477 * @player: a #SwfdecPlayer
478 * @movie: movie pointer identifying the actions to be removed
480 * Removes all actions associated with @movie that have not yet been executed.
481 * See swfdec_player_add_action() for details about actions.
483 void
484 swfdec_player_remove_all_actions (SwfdecPlayer *player, SwfdecActor *actor)
486 SwfdecPlayerAction *action;
487 SwfdecPlayerPrivate *priv;
488 guint i, j;
490 g_return_if_fail (SWFDEC_IS_PLAYER (player));
491 g_return_if_fail (SWFDEC_IS_ACTOR (actor));
493 priv = player->priv;
494 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
495 for (j = 0; j < swfdec_ring_buffer_get_n_elements (priv->actions[i]); j++) {
496 action = swfdec_ring_buffer_peek_nth (priv->actions[i], j);
498 if (action->actor == actor) {
499 SWFDEC_LOG ("removing action %p %u",
500 action->actor, action->event);
501 action->actor = NULL;
507 static gboolean
508 swfdec_player_do_action (SwfdecPlayer *player)
510 SwfdecPlayerAction *action;
511 SwfdecPlayerPrivate *priv;
512 guint i;
514 priv = player->priv;
515 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
516 do {
517 action = swfdec_ring_buffer_pop (priv->actions[i]);
518 if (action == NULL)
519 break;
520 } while (action->actor == NULL); /* skip removed actions */
521 if (action) {
522 if (action->script) {
523 SwfdecSandbox *sandbox = SWFDEC_MOVIE (action->actor)->resource->sandbox;
524 SwfdecAsObject *object = swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (action->actor));
525 swfdec_sandbox_use (sandbox);
526 swfdec_as_object_run (object, action->script);
527 swfdec_sandbox_unuse (sandbox);
528 } else {
529 swfdec_actor_execute (action->actor, action->event, action->key);
531 return TRUE;
535 return FALSE;
538 static void
539 swfdec_player_perform_external_actions (SwfdecPlayer *player)
541 SwfdecPlayerExternalAction *action;
542 SwfdecPlayerPrivate *priv = player->priv;
543 guint i;
545 /* remove timeout if it exists - do this before executing stuff below */
546 if (priv->external_timeout.callback) {
547 swfdec_player_remove_timeout (player, &priv->external_timeout);
548 priv->external_timeout.callback = NULL;
551 /* we need to query the number of current actions so newly added ones aren't
552 * executed in here */
553 for (i = swfdec_ring_buffer_get_n_elements (priv->external_actions); i > 0; i--) {
554 action = swfdec_ring_buffer_pop (priv->external_actions);
555 g_assert (action != NULL);
556 /* skip removed actions */
557 if (action->object == NULL)
558 continue;
559 action->func (action->object, action->data);
560 swfdec_player_perform_actions (player);
564 static void
565 swfdec_player_trigger_external_actions (SwfdecTimeout *advance)
567 SwfdecPlayerPrivate *priv = (SwfdecPlayerPrivate *) ((void *) ((guint8 *) advance - G_STRUCT_OFFSET (SwfdecPlayerPrivate, external_timeout)));
569 priv->external_timeout.callback = NULL;
570 swfdec_player_perform_external_actions (priv->player);
573 void
574 swfdec_player_add_external_action (SwfdecPlayer *player, gpointer object,
575 SwfdecActionFunc action_func, gpointer action_data)
577 SwfdecPlayerExternalAction *action;
578 SwfdecPlayerPrivate *priv;
580 g_return_if_fail (SWFDEC_IS_PLAYER (player));
581 g_return_if_fail (object != NULL);
582 g_return_if_fail (action_func != NULL);
584 SWFDEC_LOG ("adding external action %p %p %p", object, action_func, action_data);
585 priv = player->priv;
586 action = swfdec_ring_buffer_push (priv->external_actions);
587 if (action == NULL) {
588 /* FIXME: limit number of actions to not get inf loops due to scripts? */
589 swfdec_ring_buffer_set_size (priv->external_actions,
590 swfdec_ring_buffer_get_size (priv->external_actions) + 16);
591 action = swfdec_ring_buffer_push (priv->external_actions);
592 g_assert (action);
594 action->object = object;
595 action->func = action_func;
596 action->data = action_data;
597 if (!priv->external_timeout.callback) {
598 /* trigger execution immediately.
599 * But if initialized, keep at least 100ms from when the last external
600 * timeout triggered. This is a crude method to get around infinite loops
601 * when script actions executed by external actions trigger another external
602 * action that would execute instantly.
604 if (priv->initialized) {
605 priv->external_timeout.timestamp = MAX (priv->time,
606 priv->external_timeout.timestamp + SWFDEC_MSECS_TO_TICKS (100));
607 } else {
608 priv->external_timeout.timestamp = priv->time;
610 priv->external_timeout.callback = swfdec_player_trigger_external_actions;
611 swfdec_player_add_timeout (player, &priv->external_timeout);
615 void
616 swfdec_player_remove_all_external_actions (SwfdecPlayer *player, gpointer object)
618 SwfdecPlayerExternalAction *action;
619 SwfdecPlayerPrivate *priv;
620 guint i;
622 g_return_if_fail (SWFDEC_IS_PLAYER (player));
623 g_return_if_fail (object != NULL);
625 priv = player->priv;
626 for (i = 0; i < swfdec_ring_buffer_get_n_elements (priv->external_actions); i++) {
627 action = swfdec_ring_buffer_peek_nth (priv->external_actions, i);
629 if (action->object == object) {
630 SWFDEC_LOG ("removing external action %p %p %p",
631 action->object, action->func, action->data);
632 action->object = NULL;
637 /*** SwfdecPlayer ***/
639 enum {
640 INVALIDATE,
641 ADVANCE,
642 HANDLE_KEY,
643 HANDLE_MOUSE,
644 AUDIO_ADDED,
645 AUDIO_REMOVED,
646 LAUNCH,
647 FSCOMMAND,
648 MISSING_PLUGINS,
649 QUERY_SIZE,
650 LAST_SIGNAL
653 static guint signals[LAST_SIGNAL] = { 0, };
655 enum {
656 PROP_0,
657 PROP_CACHE_SIZE,
658 PROP_INITIALIZED,
659 PROP_DEFAULT_WIDTH,
660 PROP_DEFAULT_HEIGHT,
661 PROP_RATE,
662 PROP_MOUSE_CURSOR,
663 PROP_NEXT_EVENT,
664 PROP_BACKGROUND_COLOR,
665 PROP_WIDTH,
666 PROP_HEIGHT,
667 PROP_ALIGNMENT,
668 PROP_SCALE,
669 PROP_SCRIPTING,
670 PROP_SYSTEM,
671 PROP_MAX_RUNTIME,
672 PROP_LOADER_TYPE,
673 PROP_SOCKET_TYPE,
674 PROP_BASE_URL,
675 PROP_URL,
676 PROP_VARIABLES,
677 PROP_START_TIME,
678 PROP_FOCUS,
679 PROP_RENDERER,
680 PROP_FULLSCREEN,
681 PROP_ALLOW_FULLSCREEN,
682 PROP_SELECTION
685 G_DEFINE_TYPE (SwfdecPlayer, swfdec_player, SWFDEC_TYPE_AS_CONTEXT)
687 static guint
688 swfdec_player_alignment_to_flags (SwfdecAlignment alignment)
690 static const guint align_flags[9] = {
691 SWFDEC_ALIGN_FLAG_TOP | SWFDEC_ALIGN_FLAG_LEFT,
692 SWFDEC_ALIGN_FLAG_TOP,
693 SWFDEC_ALIGN_FLAG_TOP | SWFDEC_ALIGN_FLAG_RIGHT,
694 SWFDEC_ALIGN_FLAG_LEFT,
696 SWFDEC_ALIGN_FLAG_RIGHT,
697 SWFDEC_ALIGN_FLAG_BOTTOM | SWFDEC_ALIGN_FLAG_LEFT,
698 SWFDEC_ALIGN_FLAG_BOTTOM,
699 SWFDEC_ALIGN_FLAG_BOTTOM | SWFDEC_ALIGN_FLAG_RIGHT
701 return align_flags[alignment];
704 static SwfdecAlignment
705 swfdec_player_alignment_from_flags (guint flags)
707 if (flags & SWFDEC_ALIGN_FLAG_TOP) {
708 if (flags & SWFDEC_ALIGN_FLAG_LEFT) {
709 return SWFDEC_ALIGNMENT_TOP_LEFT;
710 } else if (flags & SWFDEC_ALIGN_FLAG_RIGHT) {
711 return SWFDEC_ALIGNMENT_TOP_RIGHT;
712 } else {
713 return SWFDEC_ALIGNMENT_TOP;
715 } else if (flags & SWFDEC_ALIGN_FLAG_BOTTOM) {
716 if (flags & SWFDEC_ALIGN_FLAG_LEFT) {
717 return SWFDEC_ALIGNMENT_BOTTOM_LEFT;
718 } else if (flags & SWFDEC_ALIGN_FLAG_RIGHT) {
719 return SWFDEC_ALIGNMENT_BOTTOM_RIGHT;
720 } else {
721 return SWFDEC_ALIGNMENT_BOTTOM;
723 } else {
724 if (flags & SWFDEC_ALIGN_FLAG_LEFT) {
725 return SWFDEC_ALIGNMENT_LEFT;
726 } else if (flags & SWFDEC_ALIGN_FLAG_RIGHT) {
727 return SWFDEC_ALIGNMENT_RIGHT;
728 } else {
729 return SWFDEC_ALIGNMENT_CENTER;
734 static void
735 swfdec_player_get_property (GObject *object, guint param_id, GValue *value,
736 GParamSpec * pspec)
738 SwfdecPlayer *player = SWFDEC_PLAYER (object);
739 SwfdecPlayerPrivate *priv = player->priv;
741 switch (param_id) {
742 case PROP_CACHE_SIZE:
743 g_value_set_ulong (value, swfdec_cache_get_max_cache_size (priv->cache));
744 break;
745 case PROP_INITIALIZED:
746 g_value_set_boolean (value, swfdec_player_is_initialized (player));
747 break;
748 case PROP_DEFAULT_WIDTH:
749 g_value_set_uint (value, priv->width);
750 break;
751 case PROP_DEFAULT_HEIGHT:
752 g_value_set_uint (value, priv->height);
753 break;
754 case PROP_RATE:
755 g_value_set_double (value, priv->rate / 256.0);
756 break;
757 case PROP_MOUSE_CURSOR:
758 g_value_set_enum (value, priv->mouse_cursor);
759 break;
760 case PROP_NEXT_EVENT:
761 g_value_set_uint (value, swfdec_player_get_next_event (player));
762 break;
763 case PROP_BACKGROUND_COLOR:
764 g_value_set_uint (value, priv->bgcolor ? priv->bgcolor : SWFDEC_COLOR_WHITE);
765 break;
766 case PROP_WIDTH:
767 g_value_set_int (value, priv->stage_width);
768 break;
769 case PROP_HEIGHT:
770 g_value_set_int (value, priv->stage_height);
771 break;
772 case PROP_ALIGNMENT:
773 g_value_set_enum (value, swfdec_player_alignment_from_flags (priv->align_flags));
774 break;
775 case PROP_SCALE:
776 g_value_set_enum (value, priv->scale_mode);
777 break;
778 case PROP_SCRIPTING:
779 g_value_set_object (value, priv->scripting);
780 break;
781 case PROP_SYSTEM:
782 g_value_set_object (value, priv->system);
783 break;
784 case PROP_MAX_RUNTIME:
785 g_value_set_ulong (value, priv->max_runtime);
786 break;
787 case PROP_LOADER_TYPE:
788 g_value_set_gtype (value, priv->loader_type);
789 break;
790 case PROP_SOCKET_TYPE:
791 g_value_set_gtype (value, priv->socket_type);
792 break;
793 case PROP_URL:
794 g_value_set_boxed (value, priv->url);
795 break;
796 case PROP_BASE_URL:
797 g_value_set_boxed (value, priv->base_url);
798 break;
799 case PROP_VARIABLES:
800 g_value_set_string (value, priv->variables);
801 break;
802 case PROP_FOCUS:
803 g_value_set_boolean (value, priv->has_focus);
804 break;
805 case PROP_RENDERER:
806 g_value_set_object (value, priv->renderer);
807 break;
808 case PROP_FULLSCREEN:
809 g_value_set_boolean (value, priv->fullscreen);
810 break;
811 case PROP_ALLOW_FULLSCREEN:
812 g_value_set_boolean (value, priv->allow_fullscreen);
813 break;
814 case PROP_SELECTION:
815 g_value_set_string (value, priv->selection);
816 break;
817 default:
818 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
819 break;
823 static void
824 swfdec_player_emit_signals (SwfdecPlayer *player)
826 SwfdecPlayerPrivate *priv = player->priv;
827 GList *walk;
829 /* emit invalidate signal */
830 if (priv->invalidations->len != 0) {
831 g_signal_emit (player, signals[INVALIDATE], 0,
832 priv->invalidations->data, priv->invalidations->len);
833 g_array_set_size (priv->invalidations, 0);
836 /* emit audio-added for all added audio streams */
837 for (walk = priv->audio; walk; walk = walk->next) {
838 SwfdecAudio *audio = walk->data;
840 swfdec_audio_update_matrix (audio);
841 if (!audio->added) {
842 g_signal_emit (player, signals[AUDIO_ADDED], 0, audio);
843 audio->added = TRUE;
847 /* emit missing-plugin signal for newly discovered plugins */
848 if (priv->missing_plugins) {
849 GSList *swalk;
850 guint i = 0, n_plugins = g_slist_length (priv->missing_plugins);
851 char **details = g_new (char *, n_plugins + 1);
853 for (swalk = priv->missing_plugins; swalk; swalk = swalk->next) {
854 details[i++] = swalk->data;
856 details[i] = NULL;
857 g_slist_free (priv->missing_plugins);
858 priv->missing_plugins = NULL;
859 SWFDEC_INFO ("emitting missing plugins signal for %u plugins", n_plugins);
860 g_signal_emit (player, signals[MISSING_PLUGINS], 0, details);
861 g_strfreev (details);
865 void
866 swfdec_player_update_scale (SwfdecPlayer *player)
868 SwfdecPlayerPrivate *priv = player->priv;
869 int width, height;
870 double scale_x, scale_y;
871 GList *walk;
873 priv->stage.width = priv->stage_width >= 0 ? priv->stage_width : (int) priv->width;
874 priv->stage.height = priv->stage_height >= 0 ? priv->stage_height : (int) priv->height;
876 if (priv->stage.height == 0 || priv->stage.width == 0) {
877 cairo_matrix_init_scale (&priv->stage_to_global,
878 SWFDEC_TWIPS_SCALE_FACTOR, SWFDEC_TWIPS_SCALE_FACTOR);
879 priv->global_to_stage = priv->stage_to_global;
880 cairo_matrix_invert (&priv->global_to_stage);
881 return;
883 if (priv->width == 0 || priv->height == 0) {
884 scale_x = 1.0;
885 scale_y = 1.0;
886 } else {
887 scale_x = (double) priv->stage.width / priv->width;
888 scale_y = (double) priv->stage.height / priv->height;
890 switch (priv->scale_mode) {
891 case SWFDEC_SCALE_SHOW_ALL:
892 scale_x = MIN (scale_x, scale_y);
893 scale_y = scale_x;
894 break;
895 case SWFDEC_SCALE_NO_BORDER:
896 scale_x = MAX (scale_x, scale_y);
897 scale_y = scale_x;
898 break;
899 case SWFDEC_SCALE_EXACT_FIT:
900 break;
901 case SWFDEC_SCALE_NONE:
902 scale_x = 1.0;
903 scale_y = 1.0;
904 break;
905 default:
906 g_assert_not_reached ();
908 width = priv->stage.width - ceil (priv->width * scale_x);
909 height = priv->stage.height - ceil (priv->height * scale_y);
910 if (priv->align_flags & SWFDEC_ALIGN_FLAG_LEFT) {
911 width = 0;
912 } else if (priv->align_flags & SWFDEC_ALIGN_FLAG_RIGHT) {
913 width = width;
914 } else {
915 width = width / 2;
917 if (priv->align_flags & SWFDEC_ALIGN_FLAG_TOP) {
918 height = 0;
919 } else if (priv->align_flags & SWFDEC_ALIGN_FLAG_BOTTOM) {
920 height = height;
921 } else {
922 height = height / 2;
924 SWFDEC_LOG ("coordinate translation is %g * x + %d - %g * y + %d",
925 scale_x, width, scale_y, height);
926 cairo_matrix_init_translate (&priv->global_to_stage, width, height);
927 cairo_matrix_scale (&priv->global_to_stage, SWFDEC_TWIPS_TO_DOUBLE (scale_x),
928 SWFDEC_TWIPS_TO_DOUBLE (scale_y));
929 priv->stage_to_global = priv->global_to_stage;
930 if (cairo_matrix_invert (&priv->stage_to_global)) {
931 g_assert_not_reached ();
933 /* FIXME: notify textfields more gentle about the update */
934 /* FIXME: these events can cause invalidations in TextFields */
935 for (walk = priv->roots; walk; walk = walk->next) {
936 g_signal_emit_by_name (walk->data, "matrix-changed");
938 swfdec_player_invalidate (player, NULL, NULL);
939 if (!swfdec_player_is_locked (player))
940 swfdec_player_emit_signals (player);
943 static void
944 swfdec_player_set_property (GObject *object, guint param_id, const GValue *value,
945 GParamSpec *pspec)
947 SwfdecPlayer *player = SWFDEC_PLAYER (object);
948 SwfdecPlayerPrivate *priv = player->priv;
950 switch (param_id) {
951 case PROP_CACHE_SIZE:
952 swfdec_cache_set_max_cache_size (priv->cache, g_value_get_ulong (value));
953 break;
954 case PROP_WIDTH:
955 swfdec_player_set_size (player, g_value_get_int (value), priv->stage_height);
956 break;
957 case PROP_HEIGHT:
958 swfdec_player_set_size (player, priv->stage_width, g_value_get_int (value));
959 break;
960 case PROP_ALIGNMENT:
961 priv->align_flags = swfdec_player_alignment_to_flags (g_value_get_enum (value));
962 swfdec_player_update_scale (player);
963 break;
964 case PROP_SCALE:
965 swfdec_player_set_scale_mode (player, g_value_get_enum (value));
966 break;
967 case PROP_SCRIPTING:
968 swfdec_player_set_scripting (player, g_value_get_object (value));
969 break;
970 case PROP_SYSTEM:
971 g_object_unref (priv->system);
972 if (g_value_get_object (value)) {
973 priv->system = SWFDEC_SYSTEM (g_value_dup_object (value));
974 } else {
975 priv->system = swfdec_system_new ();
977 break;
978 case PROP_MAX_RUNTIME:
979 swfdec_player_set_maximum_runtime (player, g_value_get_ulong (value));
980 break;
981 case PROP_LOADER_TYPE:
982 g_return_if_fail (G_TYPE_IS_INSTANTIATABLE (g_value_get_gtype (value)));
983 priv->loader_type = g_value_get_gtype (value);
984 break;
985 case PROP_SOCKET_TYPE:
986 priv->socket_type = g_value_get_gtype (value);
987 break;
988 case PROP_URL:
989 swfdec_player_set_url (player, g_value_get_boxed (value));
990 break;
991 case PROP_BASE_URL:
992 swfdec_player_set_base_url (player, g_value_get_boxed (value));
993 break;
994 case PROP_VARIABLES:
995 swfdec_player_set_variables (player, g_value_get_boxed (value));
996 break;
997 case PROP_START_TIME:
999 const GTimeVal *set = g_value_get_boxed (value);
1000 if (set)
1001 SWFDEC_AS_CONTEXT (player)->start_time = *set;
1002 /* else use default time from context */
1004 break;
1005 case PROP_FOCUS:
1006 swfdec_player_set_focus (player, g_value_get_boolean (value));
1007 break;
1008 case PROP_RENDERER:
1009 swfdec_player_set_renderer (player, g_value_get_object (value));
1010 break;
1011 case PROP_ALLOW_FULLSCREEN:
1012 swfdec_player_set_allow_fullscreen (player, g_value_get_boolean (value));
1013 break;
1014 default:
1015 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1016 break;
1020 static void
1021 swfdec_player_stop_ticking (SwfdecPlayer *player)
1023 SwfdecPlayerPrivate *priv;
1025 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1027 priv = player->priv;
1028 if (priv->iterate_timeout.callback == NULL)
1029 return;
1030 swfdec_player_remove_timeout (player, &priv->iterate_timeout);
1031 priv->iterate_timeout.callback = NULL;
1034 static void
1035 swfdec_player_dispose (GObject *object)
1037 SwfdecPlayer *player = SWFDEC_PLAYER (object);
1038 SwfdecPlayerPrivate *priv = player->priv;
1039 guint i;
1041 swfdec_player_stop_all_sounds (player);
1042 swfdec_function_list_clear (&priv->resource_requests);
1043 g_hash_table_destroy (priv->registered_classes);
1044 g_hash_table_destroy (priv->scripting_callbacks);
1046 g_list_foreach (priv->loading_policy_files, (GFunc) g_object_unref, NULL);
1047 g_list_free (priv->loading_policy_files);
1048 priv->loading_policy_files = NULL;
1049 g_slist_foreach (priv->policy_files, (GFunc) g_object_unref, NULL);
1050 g_slist_free (priv->policy_files);
1051 priv->policy_files = NULL;
1052 g_slist_free (priv->invalid_pending);
1053 priv->invalid_pending = NULL;
1055 while (priv->roots)
1056 swfdec_movie_destroy (priv->roots->data);
1058 /* we do this here so references to GC'd objects get freed */
1059 G_OBJECT_CLASS (swfdec_player_parent_class)->dispose (object);
1060 /* must happen after disposing context, some objects unroot themselves */
1061 swfdec_function_list_clear (&priv->rooted);
1063 swfdec_player_remove_all_external_actions (player, player);
1064 #ifndef G_DISABLE_ASSERT
1066 SwfdecPlayerExternalAction *action;
1067 while ((action = swfdec_ring_buffer_pop (priv->external_actions)) != NULL) {
1068 g_assert (action->object == NULL); /* skip removed actions */
1072 SwfdecPlayerAction *action;
1073 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
1074 while ((action = swfdec_ring_buffer_pop (priv->actions[i])) != NULL) {
1075 g_assert (action->actor == NULL); /* skip removed actions */
1079 #endif
1080 swfdec_ring_buffer_free (priv->external_actions);
1081 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
1082 swfdec_ring_buffer_free (priv->actions[i]);
1084 g_assert (priv->actors == NULL);
1085 g_assert (priv->audio == NULL);
1086 g_slist_free (priv->sandboxes);
1087 if (priv->external_timeout.callback)
1088 swfdec_player_remove_timeout (player, &priv->external_timeout);
1089 swfdec_player_stop_ticking (player);
1090 g_assert (priv->timeouts == NULL);
1091 g_list_free (priv->intervals);
1092 priv->intervals = NULL;
1093 g_object_unref (priv->cache);
1094 if (priv->system) {
1095 g_object_unref (priv->system);
1096 priv->system = NULL;
1098 g_array_free (priv->invalidations, TRUE);
1099 priv->invalidations = NULL;
1100 if (priv->renderer) {
1101 g_object_unref (priv->renderer);
1102 priv->renderer = NULL;
1104 if (priv->runtime) {
1105 g_timer_destroy (priv->runtime);
1106 priv->runtime = NULL;
1108 if (priv->base_url) {
1109 swfdec_url_free (priv->base_url);
1110 priv->base_url = NULL;
1112 if (priv->url) {
1113 swfdec_url_free (priv->url);
1114 priv->url = NULL;
1116 g_free (priv->variables);
1117 priv->variables = NULL;
1120 static void
1121 swfdec_player_broadcast (SwfdecPlayer *player, const char *object_name,
1122 const char *signal_name, guint argc, SwfdecAsValue *argv)
1124 GSList *walk;
1125 SwfdecAsValue vals[3];
1126 SwfdecAsObject *obj;
1128 /* FIXME: extend when needed, by increasing the array size above */
1129 g_return_if_fail (argc <= 2);
1131 if (argc > 0) {
1132 memcpy (&vals[1], argv, argc * sizeof (SwfdecAsValue));
1135 SWFDEC_DEBUG ("broadcasting message %s.%s", object_name, signal_name);
1136 /* FIXME: sandbox ordering? */
1137 for (walk = player->priv->sandboxes; walk; walk = walk->next) {
1138 SwfdecSandbox *sandbox = walk->data;
1139 swfdec_sandbox_use (sandbox);
1140 swfdec_as_object_get_variable (SWFDEC_AS_CONTEXT (player)->global, object_name, &vals[0]);
1141 if (!SWFDEC_AS_VALUE_IS_COMPOSITE (&vals[0]))
1142 return;
1143 obj = SWFDEC_AS_VALUE_GET_COMPOSITE (&vals[0]);
1144 if (obj == NULL)
1145 return;
1146 SWFDEC_AS_VALUE_SET_STRING (&vals[0], signal_name);
1147 swfdec_as_object_call (obj, SWFDEC_AS_STR_broadcastMessage, argc + 1, vals, NULL);
1148 swfdec_sandbox_unuse (sandbox);
1152 void
1153 swfdec_player_invalidate_focusrect (SwfdecPlayer *player)
1155 SwfdecPlayerPrivate *priv;
1157 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1159 priv = player->priv;
1161 if (swfdec_rect_is_empty (&priv->focusrect))
1162 return;
1164 swfdec_player_invalidate (player, NULL, &priv->focusrect);
1165 swfdec_rect_init_empty (&priv->focusrect);
1169 * swfdec_player_grab_focus:
1170 * @player: the player
1171 * @actor: the actor to give focus or %NULL to unset focus
1173 * This function handles passing the focus around. It is supposed to be called
1174 * by all functions that wish to change keyboard focus. Note that only the
1175 * currently focused movie receives keyboard events - i.e. key_pressed and
1176 * key_released vfuncs.
1178 void
1179 swfdec_player_grab_focus (SwfdecPlayer *player, SwfdecActor *actor)
1181 SwfdecAsValue vals[2];
1182 SwfdecPlayerPrivate *priv;
1183 SwfdecActorClass *klass;
1184 SwfdecActor *prev;
1186 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1187 g_return_if_fail (actor == NULL || SWFDEC_IS_ACTOR (actor));
1189 /* set variables */
1190 priv = player->priv;
1191 if (actor == priv->focus) {
1192 SWFDEC_DEBUG ("nothing to do, focus change request from movie %s to itself",
1193 actor ? SWFDEC_MOVIE (actor)->name : "---");
1194 return;
1196 prev = priv->focus;
1197 if (prev) {
1198 SWFDEC_AS_VALUE_SET_MOVIE (&vals[0], SWFDEC_MOVIE (prev));
1199 } else {
1200 SWFDEC_AS_VALUE_SET_NULL (&vals[0]);
1202 if (actor) {
1203 SWFDEC_AS_VALUE_SET_MOVIE (&vals[1], SWFDEC_MOVIE (actor));
1204 } else {
1205 SWFDEC_AS_VALUE_SET_NULL (&vals[1]);
1207 if (prev) {
1208 swfdec_sandbox_use (SWFDEC_MOVIE (prev)->resource->sandbox);
1209 swfdec_as_relay_call (SWFDEC_AS_RELAY (prev), SWFDEC_AS_STR_onKillFocus,
1210 1, &vals[1], NULL);
1211 swfdec_sandbox_unuse (SWFDEC_MOVIE (prev)->resource->sandbox);
1212 klass = SWFDEC_ACTOR_GET_CLASS (prev);
1213 if (klass->focus_out)
1214 klass->focus_out (prev);
1216 priv->focus_previous = prev;
1217 priv->focus = actor;
1218 swfdec_player_invalidate_focusrect (player);
1219 if (actor) {
1220 swfdec_sandbox_use (SWFDEC_MOVIE (actor)->resource->sandbox);
1221 swfdec_as_relay_call (SWFDEC_AS_RELAY (actor), SWFDEC_AS_STR_onSetFocus,
1222 1, &vals[0], NULL);
1223 swfdec_sandbox_unuse (SWFDEC_MOVIE (actor)->resource->sandbox);
1224 klass = SWFDEC_ACTOR_GET_CLASS (actor);
1225 if (klass->focus_in)
1226 klass->focus_in (actor);
1228 swfdec_player_broadcast (player, SWFDEC_AS_STR_Selection, SWFDEC_AS_STR_onSetFocus, 2, vals);
1231 static void
1232 swfdec_player_update_mouse_cursor (SwfdecPlayer *player)
1234 SwfdecPlayerPrivate *priv = player->priv;
1235 SwfdecMouseCursor new = SWFDEC_MOUSE_CURSOR_NORMAL;
1237 if (!priv->mouse_visible) {
1238 new = SWFDEC_MOUSE_CURSOR_NONE;
1239 } else if (priv->mouse_grab != NULL) {
1240 SwfdecActorClass *klass = SWFDEC_ACTOR_GET_CLASS (priv->mouse_grab);
1242 if (klass->mouse_cursor)
1243 new = klass->mouse_cursor (priv->mouse_grab);
1244 else
1245 new = SWFDEC_MOUSE_CURSOR_CLICK;
1248 if (new != priv->mouse_cursor) {
1249 priv->mouse_cursor = new;
1250 g_object_notify (G_OBJECT (player), "mouse-cursor");
1255 * swfdec_player_set_drag_movie:
1256 * @player: a #SwfdecPlayer
1257 * @drag: the movie to be dragged by the mouse or NULL to unset
1258 * @center: TRUE if the center of @drag should be at the mouse pointer, FALSE if (0,0)
1259 * of @drag should be at the mouse pointer.
1260 * @rect: NULL or the rectangle that clips the mouse position. The coordinates
1261 * are in the coordinate system of the parent of @drag.
1263 * Sets or unsets the movie that is dragged by the mouse.
1265 void
1266 swfdec_player_set_drag_movie (SwfdecPlayer *player, SwfdecActor *drag, gboolean center,
1267 SwfdecRect *rect)
1269 SwfdecPlayerPrivate *priv;
1271 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1272 g_return_if_fail (drag == NULL || SWFDEC_IS_ACTOR (drag));
1274 /* FIXME: need to do anything with old drag? */
1275 priv = player->priv;
1276 priv->mouse_drag = drag;
1277 priv->mouse_drag_center = center;
1278 if (drag && !center) {
1279 priv->mouse_drag_x = priv->mouse_x;
1280 priv->mouse_drag_y = priv->mouse_y;
1281 swfdec_player_stage_to_global (player, &priv->mouse_drag_x, &priv->mouse_drag_y);
1282 if (SWFDEC_MOVIE (drag)->parent)
1283 swfdec_movie_global_to_local (SWFDEC_MOVIE (drag)->parent, &priv->mouse_drag_x, &priv->mouse_drag_y);
1284 priv->mouse_drag_x -= SWFDEC_MOVIE (drag)->matrix.x0;
1285 priv->mouse_drag_y -= SWFDEC_MOVIE (drag)->matrix.y0;
1287 if (rect) {
1288 priv->mouse_drag_rect = *rect;
1289 } else {
1290 priv->mouse_drag_rect.x0 = -G_MAXDOUBLE;
1291 priv->mouse_drag_rect.y0 = -G_MAXDOUBLE;
1292 priv->mouse_drag_rect.x1 = G_MAXDOUBLE;
1293 priv->mouse_drag_rect.y1 = G_MAXDOUBLE;
1295 SWFDEC_DEBUG ("starting drag in %g %g %g %g",
1296 priv->mouse_drag_rect.x0, priv->mouse_drag_rect.y0,
1297 priv->mouse_drag_rect.x1, priv->mouse_drag_rect.y1);
1298 if (SWFDEC_MOVIE (drag))
1299 SWFDEC_MOVIE (drag)->modified = TRUE;
1302 static void
1303 swfdec_player_grab_mouse_movie (SwfdecPlayer *player)
1305 SwfdecPlayerPrivate *priv = player->priv;
1306 GList *walk;
1307 double x, y;
1308 SwfdecActor *below_mouse = NULL;
1310 x = priv->mouse_x;
1311 y = priv->mouse_y;
1312 swfdec_player_stage_to_global (player, &x, &y);
1313 for (walk = g_list_last (priv->roots); walk; walk = walk->prev) {
1314 SwfdecMovie *movie = swfdec_movie_get_movie_at (walk->data, x, y, TRUE);
1315 if (SWFDEC_IS_ACTOR (movie) &&
1316 swfdec_actor_get_mouse_events (SWFDEC_ACTOR (movie))) {
1317 below_mouse = SWFDEC_ACTOR (movie);
1318 break;
1321 if (swfdec_player_is_mouse_pressed (player)) {
1322 /* a mouse grab is active */
1323 if (priv->mouse_grab) {
1324 if (below_mouse == priv->mouse_grab &&
1325 priv->mouse_below != priv->mouse_grab) {
1326 SwfdecActorClass *klass = SWFDEC_ACTOR_GET_CLASS (priv->mouse_grab);
1327 if (klass->mouse_in)
1328 klass->mouse_in (priv->mouse_grab);
1329 } else if (below_mouse != priv->mouse_grab &&
1330 priv->mouse_below == priv->mouse_grab) {
1331 SwfdecActorClass *klass = SWFDEC_ACTOR_GET_CLASS (priv->mouse_grab);
1332 if (klass->mouse_out)
1333 klass->mouse_out (priv->mouse_grab);
1336 } else {
1337 /* no mouse grab is active */
1338 if (below_mouse != priv->mouse_grab) {
1339 if (priv->mouse_grab) {
1340 SwfdecActorClass *klass = SWFDEC_ACTOR_GET_CLASS (priv->mouse_grab);
1341 if (klass->mouse_out)
1342 klass->mouse_out (priv->mouse_grab);
1344 if (below_mouse) {
1345 SwfdecActorClass *klass = SWFDEC_ACTOR_GET_CLASS (below_mouse);
1346 if (klass->mouse_in)
1347 klass->mouse_in (below_mouse);
1350 priv->mouse_grab = below_mouse;
1352 priv->mouse_below = below_mouse;
1353 SWFDEC_DEBUG ("%s %p has mouse at %g %g",
1354 priv->mouse_grab ? G_OBJECT_TYPE_NAME (priv->mouse_grab) : "---",
1355 priv->mouse_grab, priv->mouse_x, priv->mouse_y);
1358 static gboolean
1359 swfdec_player_do_mouse_move (SwfdecPlayer *player, double x, double y)
1361 SwfdecPlayerPrivate *priv = player->priv;
1362 GList *walk;
1364 if (priv->mouse_x != x || priv->mouse_y != y) {
1365 priv->mouse_x = x;
1366 priv->mouse_y = y;
1367 for (walk = priv->actors; walk; walk = walk->next) {
1368 swfdec_actor_queue_script (walk->data, SWFDEC_EVENT_MOUSE_MOVE);
1370 swfdec_player_broadcast (player, SWFDEC_AS_STR_Mouse, SWFDEC_AS_STR_onMouseMove, 0, NULL);
1372 swfdec_player_grab_mouse_movie (player);
1373 if (priv->mouse_grab) {
1374 SwfdecActorClass *klass = SWFDEC_ACTOR_GET_CLASS (priv->mouse_grab);
1375 swfdec_player_stage_to_global (player, &x, &y);
1376 swfdec_movie_global_to_local (SWFDEC_MOVIE (priv->mouse_grab), &x, &y);
1377 if (klass->mouse_move)
1378 klass->mouse_move (priv->mouse_grab, x, y);
1381 /* FIXME: allow events to pass through */
1382 return TRUE;
1385 static gboolean
1386 swfdec_player_do_mouse_press (SwfdecPlayer *player, guint button)
1388 SwfdecPlayerPrivate *priv = player->priv;
1389 GList *walk;
1391 priv->mouse_button |= 1 << button;
1392 if (button == 0) {
1393 for (walk = priv->actors; walk; walk = walk->next) {
1394 swfdec_actor_queue_script (walk->data, SWFDEC_EVENT_MOUSE_DOWN);
1396 swfdec_player_broadcast (player, SWFDEC_AS_STR_Mouse, SWFDEC_AS_STR_onMouseDown, 0, NULL);
1398 if (priv->mouse_grab) {
1399 SwfdecActorClass *klass = SWFDEC_ACTOR_GET_CLASS (priv->mouse_grab);
1400 if (klass->mouse_press)
1401 klass->mouse_press (priv->mouse_grab, button);
1404 /* FIXME: allow events to pass through */
1405 return TRUE;
1408 static gboolean
1409 swfdec_player_do_mouse_release (SwfdecPlayer *player, guint button)
1411 SwfdecPlayerPrivate *priv = player->priv;
1412 GList *walk;
1414 priv->mouse_button &= ~(1 << button);
1415 if (button == 0) {
1416 for (walk = priv->actors; walk; walk = walk->next) {
1417 swfdec_actor_queue_script (walk->data, SWFDEC_EVENT_MOUSE_UP);
1419 swfdec_player_broadcast (player, SWFDEC_AS_STR_Mouse, SWFDEC_AS_STR_onMouseUp, 0, NULL);
1421 if (priv->mouse_grab) {
1422 SwfdecActorClass *klass = SWFDEC_ACTOR_GET_CLASS (priv->mouse_grab);
1423 if (klass->mouse_release)
1424 klass->mouse_release (priv->mouse_grab, button);
1425 if (button == 0 && priv->mouse_grab != priv->mouse_below) {
1426 priv->mouse_grab = priv->mouse_below;
1427 if (priv->mouse_grab) {
1428 klass = SWFDEC_ACTOR_GET_CLASS (priv->mouse_grab);
1429 if (klass->mouse_in)
1430 klass->mouse_in (priv->mouse_grab);
1435 /* FIXME: allow events to pass through */
1436 return TRUE;
1439 static int
1440 swfdec_player_focus_sort (gconstpointer ca, gconstpointer cb)
1442 SwfdecMovie *a = SWFDEC_MOVIE (ca);
1443 SwfdecMovie *b = SWFDEC_MOVIE (cb);
1444 double xa, ya, xb, yb;
1445 int result;
1446 char *na, *nb;
1448 swfdec_movie_update (a);
1449 swfdec_movie_update (b);
1451 xa = a->extents.x0;
1452 ya = a->extents.y0;
1453 swfdec_movie_local_to_global (a->parent, &xa, &ya);
1454 xb = b->extents.x0;
1455 yb = b->extents.y0;
1456 swfdec_movie_local_to_global (b->parent, &xb, &yb);
1458 if (ya < yb)
1459 return 1;
1460 else if (ya > yb)
1461 return -1;
1463 if (xa < xb)
1464 return 1;
1465 else if (xa > xb)
1466 return -1;
1468 na = swfdec_movie_get_path (a, TRUE);
1469 nb = swfdec_movie_get_path (b, TRUE);
1470 result = strcmp (na, nb);
1471 g_free (na);
1472 g_free (nb);
1473 if (result < 0)
1474 return 1;
1475 if (result > 0)
1476 return -1;
1478 if (a->depth < b->depth)
1479 return 1;
1480 else if (a->depth > b->depth)
1481 return -1;
1483 /* provide consistent sorting as long as we are active */
1484 if (a < b)
1485 return -1;
1486 else
1487 return 1;
1490 static GList *
1491 swfdec_player_get_tab_movies (SwfdecPlayer *player, const GList *current)
1493 SwfdecAsObject *object;
1494 SwfdecAsValue val;
1495 const GList *walk;
1496 GList *ret = NULL;
1498 for (walk = current; walk; walk = walk->next) {
1499 SwfdecActor *actor = walk->data;
1501 if (!SWFDEC_IS_ACTOR (actor))
1502 continue;
1504 object = swfdec_as_relay_get_as_object (SWFDEC_AS_RELAY (actor));
1505 swfdec_sandbox_use (SWFDEC_MOVIE (actor)->resource->sandbox);
1506 if (SWFDEC_IS_TEXT_FIELD_MOVIE (actor)) {
1507 SwfdecTextFieldMovie *text = SWFDEC_TEXT_FIELD_MOVIE (actor);
1508 if (text->editable)
1509 ret = g_list_prepend (ret, actor);
1510 } else if (SWFDEC_MOVIE (actor)->parent != NULL) {
1511 swfdec_as_object_get_variable (object, SWFDEC_AS_STR_tabEnabled, &val);
1512 if (swfdec_as_value_to_boolean (SWFDEC_AS_CONTEXT (player), &val)) {
1513 /* Flash queries again - why not? :/ */
1514 swfdec_as_object_get_variable (object, SWFDEC_AS_STR_tabEnabled, &val);
1515 ret = g_list_prepend (ret, actor);
1516 } else if (SWFDEC_AS_VALUE_IS_UNDEFINED (&val) &&
1517 swfdec_actor_get_mouse_events (actor)) {
1518 ret = g_list_prepend (ret, actor);
1522 if (SWFDEC_MOVIE (actor)->parent == NULL)
1523 SWFDEC_AS_VALUE_SET_UNDEFINED (&val);
1524 else
1525 swfdec_as_object_get_variable (object, SWFDEC_AS_STR_tabChildren, &val);
1527 if (SWFDEC_AS_VALUE_IS_UNDEFINED (&val) ||
1528 swfdec_as_value_to_boolean (SWFDEC_AS_CONTEXT (player), &val)) {
1529 GList *list;
1530 swfdec_sandbox_unuse (SWFDEC_MOVIE (actor)->resource->sandbox);
1531 list = swfdec_player_get_tab_movies (player, SWFDEC_MOVIE (actor)->list);
1532 if (list)
1533 ret = g_list_concat (list, ret);
1534 } else {
1535 swfdec_sandbox_unuse (SWFDEC_MOVIE (actor)->resource->sandbox);
1538 return ret;
1541 static void
1542 swfdec_player_handle_tab (SwfdecPlayer *player, gboolean forward)
1544 SwfdecPlayerPrivate *priv = player->priv;
1545 GList *walk, *list;
1546 GList *free_list = NULL;
1547 SwfdecActor *actor;
1549 if (priv->focus_list) {
1550 list = priv->focus_list;
1551 } else {
1552 free_list = swfdec_player_get_tab_movies (player, priv->roots);
1553 free_list = g_list_sort (free_list, swfdec_player_focus_sort);
1554 list = free_list;
1557 if (priv->focus) {
1558 walk = g_list_find (list, priv->focus);
1559 } else {
1560 walk = NULL;
1562 if (walk == NULL) {
1563 walk = forward ? list : g_list_last (list);
1564 actor = walk ? walk->data : NULL;
1565 } else {
1566 walk = forward ? walk->next : walk->prev;
1567 if (walk == NULL) {
1568 SWFDEC_FIXME ("try tabbing out of the flash movie here");
1569 walk = forward ? list : g_list_last (list);
1571 actor = walk ? walk->data : NULL;
1573 swfdec_player_grab_focus (player, actor);
1575 if (free_list)
1576 g_list_free (free_list);
1579 static void
1580 swfdec_player_handle_special_keys_before (SwfdecPlayer *player, guint key)
1582 if (key == SWFDEC_KEY_ESCAPE) {
1583 swfdec_player_set_fullscreen (player, FALSE);
1587 static void
1588 swfdec_player_handle_special_keys_after (SwfdecPlayer *player, guint key)
1590 if (key == SWFDEC_KEY_TAB) {
1591 gboolean forward = swfdec_player_is_key_pressed (player, SWFDEC_KEY_SHIFT);
1592 swfdec_player_handle_tab (player, forward);
1596 static guint8
1597 swfdec_player_get_conditional_key (guint keycode, guint character)
1599 static guint special_keys[] = {
1601 SWFDEC_KEY_LEFT,
1602 SWFDEC_KEY_RIGHT,
1603 SWFDEC_KEY_HOME,
1604 SWFDEC_KEY_END,
1605 SWFDEC_KEY_INSERT,
1606 SWFDEC_KEY_DELETE,
1608 SWFDEC_KEY_BACKSPACE,
1613 SWFDEC_KEY_ENTER,
1614 SWFDEC_KEY_UP,
1615 SWFDEC_KEY_DOWN,
1616 SWFDEC_KEY_PAGE_UP,
1617 SWFDEC_KEY_PAGE_DOWN,
1618 SWFDEC_KEY_TAB,
1619 SWFDEC_KEY_ESCAPE
1621 guint i;
1623 if (keycode != 0) {
1624 for (i = 0; i < G_N_ELEMENTS (special_keys); i++) {
1625 if (special_keys[i] == keycode)
1626 return i;
1630 if (character >= 32 && character <= 126)
1631 return character;
1633 return 0;
1636 static gboolean
1637 swfdec_player_do_handle_key (SwfdecPlayer *player, guint keycode, guint character, gboolean down)
1639 SwfdecPlayerPrivate *priv = player->priv;
1640 GList *walk;
1642 g_assert (keycode < 256);
1644 if (!swfdec_player_lock (player))
1645 return FALSE;
1646 /* set the correct variables */
1647 priv->last_keycode = keycode;
1648 priv->last_character = character;
1649 if (down) {
1650 priv->key_pressed[keycode / 8] |= 1 << keycode % 8;
1651 } else {
1652 priv->key_pressed[keycode / 8] &= ~(1 << keycode % 8);
1655 if (down)
1656 swfdec_player_handle_special_keys_before (player, keycode);
1658 if (down) {
1659 guint8 cond = swfdec_player_get_conditional_key (keycode, character);
1661 for (walk = priv->actors; walk; walk = walk->next) {
1662 swfdec_actor_queue_script_with_key (walk->data, SWFDEC_EVENT_KEY_PRESS,
1663 cond);
1667 swfdec_player_broadcast (player, SWFDEC_AS_STR_Key,
1668 down ? SWFDEC_AS_STR_onKeyDown : SWFDEC_AS_STR_onKeyUp, 0, NULL);
1670 if (priv->focus) {
1671 SwfdecActorClass *klass = SWFDEC_ACTOR_GET_CLASS (priv->focus);
1672 if (down) {
1673 if (klass->key_press)
1674 klass->key_press (priv->focus, keycode, character);
1675 } else {
1676 if (klass->key_release)
1677 klass->key_release (priv->focus, keycode, character);
1680 if (down)
1681 swfdec_player_handle_special_keys_after (player, keycode);
1682 swfdec_player_perform_actions (player);
1683 swfdec_player_unlock (player);
1685 return TRUE;
1688 static gboolean
1689 swfdec_player_do_handle_mouse (SwfdecPlayer *player,
1690 double x, double y, int button)
1692 gboolean ret;
1694 if (!swfdec_player_lock (player))
1695 return FALSE;
1697 SWFDEC_LOG ("handling mouse for %g %g %d", x, y, button);
1698 ret = swfdec_player_do_mouse_move (player, x, y);
1699 if (button > 0) {
1700 ret |= swfdec_player_do_mouse_press (player, button - 1);
1701 } else if (button < 0) {
1702 ret |= swfdec_player_do_mouse_release (player, -button - 1);
1704 swfdec_player_perform_actions (player);
1705 swfdec_player_unlock (player);
1707 return ret;
1710 void
1711 swfdec_player_global_to_stage (SwfdecPlayer *player, double *x, double *y)
1713 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1714 g_return_if_fail (x != NULL);
1715 g_return_if_fail (y != NULL);
1717 cairo_matrix_transform_point (&player->priv->global_to_stage, x, y);
1720 void
1721 swfdec_player_stage_to_global (SwfdecPlayer *player, double *x, double *y)
1723 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1724 g_return_if_fail (x != NULL);
1725 g_return_if_fail (y != NULL);
1727 cairo_matrix_transform_point (&player->priv->stage_to_global, x, y);
1730 static void
1731 swfdec_player_execute_on_load_init (SwfdecPlayer *player)
1733 GList *walk;
1735 /* FIXME: This can be made a LOT faster with correct caching, but I'm lazy */
1736 do {
1737 for (walk = player->priv->actors; walk; walk = walk->next) {
1738 SwfdecMovie *movie = walk->data;
1739 SwfdecResource *resource = swfdec_movie_get_own_resource (movie);
1740 if (resource == NULL)
1741 continue;
1742 if (swfdec_resource_emit_on_load_init (resource))
1743 break;
1745 } while (walk != NULL);
1748 static void
1749 swfdec_player_iterate (SwfdecTimeout *timeout)
1751 SwfdecPlayerPrivate *priv = (SwfdecPlayerPrivate *) ((void *) ((guint8 *) timeout - G_STRUCT_OFFSET (SwfdecPlayerPrivate, iterate_timeout)));
1752 SwfdecPlayer *player = priv->player;
1753 GList *walk;
1755 /* add timeout again - do this first because later code can change it */
1756 /* FIXME: rounding issues? */
1757 priv->iterate_timeout.timestamp += SWFDEC_TICKS_PER_SECOND * 256 / priv->rate;
1758 swfdec_player_add_timeout (player, &priv->iterate_timeout);
1759 swfdec_player_perform_external_actions (player);
1760 SWFDEC_INFO ("=== START ITERATION ===");
1761 /* start the iteration. This performs a goto next frame on all
1762 * movies that are not stopped. It also queues onEnterFrame.
1764 for (walk = priv->actors; walk; walk = walk->next) {
1765 SwfdecActorClass *klass = SWFDEC_ACTOR_GET_CLASS (walk->data);
1766 if (klass->iterate_start)
1767 klass->iterate_start (walk->data);
1769 swfdec_player_perform_actions (player);
1770 SWFDEC_INFO ("=== STOP ITERATION ===");
1771 /* this loop allows removal of walk->data */
1772 walk = priv->actors;
1773 while (walk) {
1774 SwfdecMovie *cur = walk->data;
1775 SwfdecActorClass *klass = SWFDEC_ACTOR_GET_CLASS (cur);
1776 walk = walk->next;
1777 g_assert (klass->iterate_end);
1778 if (!klass->iterate_end (SWFDEC_ACTOR (cur)))
1779 swfdec_movie_destroy (cur);
1781 swfdec_player_execute_on_load_init (player);
1782 swfdec_function_list_execute_and_clear (&priv->resource_requests, player);
1783 swfdec_player_perform_actions (player);
1786 static void
1787 swfdec_player_advance_audio (SwfdecPlayer *player, guint samples)
1789 SwfdecAudio *audio;
1790 GList *walk;
1792 if (samples == 0)
1793 return;
1795 /* don't use for loop here, because we need to advance walk before
1796 * removing the audio */
1797 walk = player->priv->audio;
1798 while (walk) {
1799 audio = walk->data;
1800 walk = walk->next;
1801 if (swfdec_audio_iterate (audio, samples) == 0)
1802 swfdec_audio_remove (audio);
1806 static void
1807 swfdec_player_do_advance (SwfdecPlayer *player, gulong msecs, guint audio_samples)
1809 SwfdecPlayerPrivate *priv = player->priv;
1810 SwfdecTimeout *timeout;
1811 SwfdecTick target_time;
1813 if (!swfdec_player_lock (player))
1814 return;
1816 g_assert (priv->timeouts != NULL);
1818 target_time = priv->time + SWFDEC_MSECS_TO_TICKS (msecs);
1819 SWFDEC_DEBUG ("advancing %lu msecs (%u audio frames)", msecs, audio_samples);
1821 timeout = priv->timeouts->data;
1822 swfdec_player_advance_audio (player, audio_samples);
1823 if (timeout->timestamp <= target_time) {
1824 priv->timeouts = g_list_remove (priv->timeouts, timeout);
1825 priv->time = timeout->timestamp;
1826 SWFDEC_LOG ("activating timeout %p now (timeout is %"G_GUINT64_FORMAT,
1827 timeout, timeout->timestamp);
1828 timeout->callback (timeout);
1829 swfdec_player_perform_actions (player);
1831 priv->time = target_time;
1833 g_object_notify (G_OBJECT (player), "next-event");
1834 swfdec_player_unlock (player);
1837 void
1838 swfdec_player_perform_actions (SwfdecPlayer *player)
1840 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1842 while (swfdec_player_do_action (player));
1845 /* used for breakpoints */
1846 void
1847 swfdec_player_lock_soft (SwfdecPlayer *player)
1849 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1850 g_assert (!swfdec_player_is_locked (player));
1851 g_assert (player->priv->invalidations->len == 0);
1853 g_object_freeze_notify (G_OBJECT (player));
1854 g_timer_start (player->priv->runtime);
1855 player->priv->locked = TRUE;
1856 SWFDEC_DEBUG ("LOCKED");
1859 gboolean
1860 swfdec_player_lock (SwfdecPlayer *player)
1862 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
1863 g_assert (!swfdec_player_is_locked (player));
1864 g_assert (swfdec_ring_buffer_get_n_elements (player->priv->actions[0]) == 0);
1865 g_assert (swfdec_ring_buffer_get_n_elements (player->priv->actions[1]) == 0);
1866 g_assert (swfdec_ring_buffer_get_n_elements (player->priv->actions[2]) == 0);
1867 g_assert (swfdec_ring_buffer_get_n_elements (player->priv->actions[3]) == 0);
1869 if (swfdec_as_context_is_aborted (SWFDEC_AS_CONTEXT (player)))
1870 return FALSE;
1872 g_object_ref (player);
1873 swfdec_player_lock_soft (player);
1874 return TRUE;
1877 static void
1878 swfdec_player_update_drag_movie (SwfdecPlayer *player)
1880 SwfdecPlayerPrivate *priv = player->priv;
1881 double x, y;
1882 SwfdecMovie *movie;
1884 if (priv->mouse_drag == NULL)
1885 return;
1887 movie = SWFDEC_MOVIE (priv->mouse_drag);
1888 swfdec_movie_update (movie);
1889 x = priv->mouse_x;
1890 y = priv->mouse_y;
1891 swfdec_player_stage_to_global (player, &x, &y);
1892 if (movie->parent)
1893 swfdec_movie_global_to_local (movie->parent, &x, &y);
1894 if (priv->mouse_drag_center) {
1895 x -= (movie->extents.x1 - movie->extents.x0) / 2;
1896 y -= (movie->extents.y1 - movie->extents.y0) / 2;
1897 } else {
1898 x -= priv->mouse_drag_x;
1899 y -= priv->mouse_drag_y;
1901 x = CLAMP (x, priv->mouse_drag_rect.x0, priv->mouse_drag_rect.x1);
1902 y = CLAMP (y, priv->mouse_drag_rect.y0, priv->mouse_drag_rect.y1);
1903 SWFDEC_LOG ("mouse is at %g %g, originally (%g %g)", x, y, priv->mouse_x, priv->mouse_y);
1904 if (x != movie->matrix.x0 || y != movie->matrix.y0) {
1905 swfdec_movie_begin_update_matrix (movie);
1906 movie->matrix.x0 = x;
1907 movie->matrix.y0 = y;
1908 swfdec_movie_end_update_matrix (movie);
1912 /* runs queued invalidations for all movies and resets the movies */
1913 static void
1914 swfdec_player_update_movies (SwfdecPlayer *player)
1916 SwfdecPlayerPrivate *priv = player->priv;
1917 SwfdecMovie *movie;
1918 cairo_matrix_t matrix;
1919 GSList *walk;
1921 swfdec_player_update_drag_movie (player);
1922 for (walk = priv->invalid_pending; walk; walk = walk->next) {
1923 movie = walk->data;
1925 swfdec_movie_update (movie);
1926 g_assert (movie->invalidate_last);
1928 if (movie->parent)
1929 swfdec_movie_local_to_global_matrix (movie->parent, &matrix);
1930 else
1931 cairo_matrix_init_identity (&matrix);
1932 swfdec_movie_invalidate (movie, &matrix, TRUE);
1933 /* also clear invalidation flag from first invalidation */
1934 movie->invalidate_last = FALSE;
1936 g_slist_free (priv->invalid_pending);
1937 priv->invalid_pending = NULL;
1940 static void
1941 swfdec_player_update_focusrect (SwfdecPlayer *player)
1943 SwfdecPlayerPrivate *priv = player->priv;
1944 SwfdecMovie *movie;
1946 if (!swfdec_rect_is_empty (&priv->focusrect))
1947 return;
1949 if (priv->focus == NULL ||
1950 !swfdec_actor_has_focusrect (priv->focus))
1951 return;
1953 movie = SWFDEC_MOVIE (priv->focus);
1954 g_assert (movie->state == SWFDEC_MOVIE_UP_TO_DATE);
1955 priv->focusrect = movie->extents;
1956 if (movie->parent)
1957 swfdec_movie_rect_local_to_global (movie->parent, &priv->focusrect);
1958 swfdec_player_invalidate (player, NULL, &priv->focusrect);
1961 static void
1962 swfdec_player_update_selection (SwfdecPlayer *player)
1964 SwfdecPlayerPrivate *priv = player->priv;
1966 if (SWFDEC_IS_TEXT_FIELD_MOVIE (priv->focus)) {
1967 SwfdecTextFieldMovie *text = SWFDEC_TEXT_FIELD_MOVIE (priv->focus);
1968 gsize start, end;
1969 swfdec_text_buffer_get_selection (text->text, &start, &end);
1970 if (start != end) {
1971 const char *s = swfdec_text_buffer_get_text (text->text);
1972 if (priv->selection != NULL &&
1973 priv->selection[end - start] == '\0' &&
1974 strncmp (s + start, priv->selection, end - start) == 0)
1975 return;
1976 g_free (priv->selection);
1977 priv->selection = g_strndup (s + start, end - start);
1978 g_object_notify (G_OBJECT (player), "selection");
1979 return;
1982 /* only possible if both are NULL */
1983 if (priv->selection == NULL)
1984 return;
1985 g_free (priv->selection);
1986 priv->selection = NULL;
1987 g_object_notify (G_OBJECT (player), "selection");
1990 /* used for breakpoints */
1991 void
1992 swfdec_player_unlock_soft (SwfdecPlayer *player)
1994 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1995 g_assert (swfdec_player_is_locked (player));
1997 SWFDEC_DEBUG ("UNLOCK");
1998 g_timer_stop (player->priv->runtime);
1999 swfdec_player_update_movies (player);
2000 swfdec_player_update_mouse_cursor (player);
2001 swfdec_player_update_focusrect (player);
2002 swfdec_player_update_selection (player);
2003 g_object_thaw_notify (G_OBJECT (player));
2004 swfdec_player_emit_signals (player);
2005 player->priv->locked = FALSE;
2008 /* This function does all the things that happen during rendering */
2009 static void
2010 swfdec_player_pretend_to_render (SwfdecPlayer *player)
2012 SwfdecPlayerPrivate *priv = player->priv;
2013 GList *walk;
2015 for (walk = priv->actors; walk; walk = walk->next) {
2016 if (SWFDEC_IS_TEXT_FIELD_MOVIE (walk->data)) {
2017 SwfdecTextFieldMovie *text = walk->data;
2018 gboolean onScroller_emitted = text->onScroller_emitted;
2019 text->onScroller_emitted = TRUE;
2020 swfdec_text_field_movie_autosize (text);
2021 text->onScroller_emitted = onScroller_emitted;
2026 void
2027 swfdec_player_unlock (SwfdecPlayer *player)
2029 SwfdecAsContext *context;
2031 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2032 g_assert (swfdec_ring_buffer_get_n_elements (player->priv->actions[0]) == 0);
2033 g_assert (swfdec_ring_buffer_get_n_elements (player->priv->actions[1]) == 0);
2034 g_assert (swfdec_ring_buffer_get_n_elements (player->priv->actions[2]) == 0);
2035 g_assert (swfdec_ring_buffer_get_n_elements (player->priv->actions[3]) == 0);
2036 context = SWFDEC_AS_CONTEXT (player);
2037 g_return_if_fail (context->state != SWFDEC_AS_CONTEXT_INTERRUPTED);
2039 swfdec_player_pretend_to_render (player);
2041 if (context->state == SWFDEC_AS_CONTEXT_RUNNING)
2042 swfdec_as_context_maybe_gc (SWFDEC_AS_CONTEXT (player));
2043 swfdec_player_unlock_soft (player);
2044 g_object_unref (player);
2047 static gboolean
2048 swfdec_accumulate_or (GSignalInvocationHint *ihint, GValue *return_accu,
2049 const GValue *handler_return, gpointer data)
2051 if (g_value_get_boolean (handler_return))
2052 g_value_set_boolean (return_accu, TRUE);
2053 return TRUE;
2056 static gboolean
2057 swfdec_accumulate_quit (GSignalInvocationHint *ihint, GValue *return_accu,
2058 const GValue *handler_return, gpointer data)
2060 if (g_value_get_boolean (handler_return))
2061 g_value_set_boolean (return_accu, TRUE);
2062 return FALSE;
2065 static void
2066 swfdec_player_mark_string_object (gpointer key, gpointer value, gpointer data)
2068 swfdec_as_string_mark (key);
2069 swfdec_gc_object_mark (value);
2072 static void
2073 swfdec_player_mark (SwfdecAsContext *context)
2075 SwfdecPlayer *player = SWFDEC_PLAYER (context);
2076 SwfdecPlayerPrivate *priv = player->priv;
2078 g_hash_table_foreach (priv->registered_classes, swfdec_player_mark_string_object, NULL);
2079 g_hash_table_foreach (priv->scripting_callbacks, swfdec_player_mark_string_object, NULL);
2080 g_list_foreach (priv->roots, (GFunc) swfdec_gc_object_mark, NULL);
2081 g_list_foreach (priv->intervals, (GFunc) swfdec_gc_object_mark, NULL);
2082 g_slist_foreach (priv->sandboxes, (GFunc) swfdec_gc_object_mark, NULL);
2083 swfdec_function_list_execute (&priv->rooted, player);
2084 swfdec_gc_object_mark (priv->resource);
2086 SWFDEC_AS_CONTEXT_CLASS (swfdec_player_parent_class)->mark (context);
2089 static void
2090 swfdec_player_get_time (SwfdecAsContext *context, GTimeVal *tv)
2092 *tv = context->start_time;
2094 /* FIXME: what granularity do we want? Currently it's milliseconds */
2095 g_time_val_add (tv, SWFDEC_TICKS_TO_MSECS (SWFDEC_PLAYER (context)->priv->time) * 1000);
2098 static gboolean
2099 swfdec_player_check_continue (SwfdecAsContext *context)
2101 SwfdecPlayer *player = SWFDEC_PLAYER (context);
2102 SwfdecPlayerPrivate *priv = player->priv;
2104 if (priv->max_runtime == 0)
2105 return TRUE;
2106 return g_timer_elapsed (priv->runtime, NULL) * 1000 <= priv->max_runtime;
2109 static gboolean
2110 swfdec_player_do_query_size (SwfdecPlayer *player, gboolean fullscreen,
2111 int *width, int *height)
2113 if (fullscreen) {
2114 SwfdecPlayerPrivate *priv = player->priv;
2115 *width = priv->system->screen_width;
2116 *height = priv->system->screen_height;
2117 } else {
2118 *width = -1;
2119 *height = -1;
2121 return TRUE;
2124 static void
2125 swfdec_player_class_init (SwfdecPlayerClass *klass)
2127 GObjectClass *object_class = G_OBJECT_CLASS (klass);
2128 SwfdecAsContextClass *context_class = SWFDEC_AS_CONTEXT_CLASS (klass);
2130 g_type_class_add_private (klass, sizeof (SwfdecPlayerPrivate));
2132 object_class->get_property = swfdec_player_get_property;
2133 object_class->set_property = swfdec_player_set_property;
2134 object_class->dispose = swfdec_player_dispose;
2136 g_object_class_install_property (object_class, PROP_INITIALIZED,
2137 g_param_spec_boolean ("initialized", "initialized", "TRUE when the player has initialized its basic values",
2138 FALSE, G_PARAM_READABLE));
2139 g_object_class_install_property (object_class, PROP_DEFAULT_WIDTH,
2140 g_param_spec_uint ("default-width", "default width", "default width of the movie",
2141 0, G_MAXUINT, 0, G_PARAM_READABLE));
2142 g_object_class_install_property (object_class, PROP_DEFAULT_HEIGHT,
2143 g_param_spec_uint ("default-height", "default height", "default height of the movie",
2144 0, G_MAXUINT, 0, G_PARAM_READABLE));
2145 g_object_class_install_property (object_class, PROP_RATE,
2146 g_param_spec_double ("rate", "rate", "rate in frames per second",
2147 0.0, 256.0, 0.0, G_PARAM_READABLE));
2148 g_object_class_install_property (object_class, PROP_MOUSE_CURSOR,
2149 g_param_spec_enum ("mouse-cursor", "mouse cursor", "how the mouse pointer should be presented",
2150 SWFDEC_TYPE_MOUSE_CURSOR, SWFDEC_MOUSE_CURSOR_NONE, G_PARAM_READABLE));
2151 g_object_class_install_property (object_class, PROP_NEXT_EVENT,
2152 g_param_spec_long ("next-event", "next event", "how many milliseconds until the next event or -1 when no event pending",
2153 -1, G_MAXLONG, -1, G_PARAM_READABLE));
2154 g_object_class_install_property (object_class, PROP_CACHE_SIZE,
2155 g_param_spec_ulong ("cache-size", "cache size", "maximum cache size in bytes",
2156 0, G_MAXULONG, 32 * 1024 * 1024, G_PARAM_READWRITE));
2157 g_object_class_install_property (object_class, PROP_BACKGROUND_COLOR,
2158 g_param_spec_uint ("background-color", "background color", "ARGB color used to draw the background",
2159 0, G_MAXUINT, SWFDEC_COLOR_COMBINE (0xFF, 0xFF, 0xFF, 0xFF), G_PARAM_READABLE));
2160 g_object_class_install_property (object_class, PROP_WIDTH,
2161 g_param_spec_int ("width", "width", "current width of the movie",
2162 -1, G_MAXINT, -1, G_PARAM_READWRITE));
2163 g_object_class_install_property (object_class, PROP_HEIGHT,
2164 g_param_spec_int ("height", "height", "current height of the movie",
2165 -1, G_MAXINT, -1, G_PARAM_READWRITE));
2166 g_object_class_install_property (object_class, PROP_ALIGNMENT,
2167 g_param_spec_enum ("alignment", "alignment", "point of the screen to align the output to",
2168 SWFDEC_TYPE_ALIGNMENT, SWFDEC_ALIGNMENT_CENTER, G_PARAM_READWRITE));
2169 g_object_class_install_property (object_class, PROP_SCALE,
2170 g_param_spec_enum ("scale-mode", "scale mode", "method used to scale the movie",
2171 SWFDEC_TYPE_SCALE_MODE, SWFDEC_SCALE_SHOW_ALL, G_PARAM_READWRITE));
2172 g_object_class_install_property (object_class, PROP_SCRIPTING,
2173 g_param_spec_object ("scripting", "scripting", "external scripting implementation",
2174 SWFDEC_TYPE_PLAYER_SCRIPTING, G_PARAM_READWRITE));
2175 g_object_class_install_property (object_class, PROP_SYSTEM,
2176 g_param_spec_object ("system", "system", "object holding system information",
2177 SWFDEC_TYPE_SYSTEM, G_PARAM_READWRITE));
2178 g_object_class_install_property (object_class, PROP_MAX_RUNTIME,
2179 g_param_spec_ulong ("max-runtime", "maximum runtime", "maximum time in msecs scripts may run in the player before aborting",
2180 0, G_MAXULONG, 10 * 1000, G_PARAM_READWRITE));
2181 g_object_class_install_property (object_class, PROP_LOADER_TYPE,
2182 g_param_spec_gtype ("loader-type", "loader type", "type to use for creating loaders",
2183 SWFDEC_TYPE_LOADER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
2184 g_object_class_install_property (object_class, PROP_SOCKET_TYPE,
2185 g_param_spec_gtype ("socket-type", "socket type", "type to use for creating sockets",
2186 SWFDEC_TYPE_SOCKET, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
2187 g_object_class_install_property (object_class, PROP_URL,
2188 g_param_spec_boxed ("url", "url", "URL of resource currently played back or NULL if not set",
2189 SWFDEC_TYPE_URL, G_PARAM_READABLE));
2190 g_object_class_install_property (object_class, PROP_BASE_URL,
2191 g_param_spec_boxed ("base-url", "base url", "base URL for creating new resource or NULL if not set yet",
2192 SWFDEC_TYPE_URL, G_PARAM_READWRITE));
2193 g_object_class_install_property (object_class, PROP_VARIABLES,
2194 g_param_spec_string ("variables", "variables", "variables to use when setting the URL",
2195 NULL, G_PARAM_READWRITE));
2196 g_object_class_install_property (object_class, PROP_START_TIME,
2197 g_param_spec_boxed ("start-time", "start-time", "time to use as the beginning time for this player",
2198 SWFDEC_TYPE_TIME_VAL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
2199 g_object_class_install_property (object_class, PROP_FOCUS,
2200 g_param_spec_boolean ("focus", "focus", "TRUE if the player has keyboard focus",
2201 TRUE, G_PARAM_READWRITE));
2202 g_object_class_install_property (object_class, PROP_RENDERER,
2203 g_param_spec_object ("renderer", "renderer", "the renderer used by this player",
2204 SWFDEC_TYPE_RENDERER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
2205 g_object_class_install_property (object_class, PROP_FULLSCREEN,
2206 g_param_spec_boolean ("fullscreen", "fullscreen", "if the player is in fullscreen mode",
2207 FALSE, G_PARAM_READABLE));
2208 g_object_class_install_property (object_class, PROP_ALLOW_FULLSCREEN,
2209 g_param_spec_boolean ("allow-fullscreen", "allow fullscreen",
2210 "if the player is allowed to change into fullscreen mode",
2211 FALSE, G_PARAM_READWRITE));
2212 g_object_class_install_property (object_class, PROP_SELECTION,
2213 g_param_spec_string ("selection", "selection", "currently selected text",
2214 NULL, G_PARAM_READABLE));
2217 * SwfdecPlayer::invalidate:
2218 * @player: the #SwfdecPlayer affected
2219 * @rectangles: a number of smaller rectangles for fine-grained control over
2220 * changes
2221 * @n_rectangles: number of rectangles in @rectangles
2223 * This signal is emitted whenever graphical elements inside the player have
2224 * changed. It provides two ways to look at the changes: By looking at the
2225 * @extents parameter, it provides a simple way to get a single rectangle that
2226 * encloses all changes. By looking at the @rectangles array, you can get
2227 * finer control over changes which is very useful if your rendering system
2228 * provides a way to handle regions.
2230 signals[INVALIDATE] = g_signal_new ("invalidate", G_TYPE_FROM_CLASS (klass),
2231 G_SIGNAL_RUN_LAST, 0, NULL, NULL, swfdec_marshal_VOID__POINTER_UINT,
2232 G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);
2234 * SwfdecPlayer::advance:
2235 * @player: the #SwfdecPlayer affected
2236 * @msecs: the amount of milliseconds the player will advance
2237 * @audio_samples: number of frames the audio is advanced (in 44100Hz steps)
2239 * Emitted whenever the player advances.
2241 signals[ADVANCE] = g_signal_new ("advance", G_TYPE_FROM_CLASS (klass),
2242 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwfdecPlayerClass, advance),
2243 NULL, NULL, swfdec_marshal_VOID__ULONG_UINT,
2244 G_TYPE_NONE, 2, G_TYPE_ULONG, G_TYPE_UINT);
2246 * SwfdecPlayer::handle-key:
2247 * @player: the #SwfdecPlayer affected
2248 * @key: #SwfdecKey that was pressed or released
2249 * @pressed: %TRUE if the @key was pressed or %FALSE if it was released
2251 * This signal is emitted whenever @player should respond to a key event. If
2252 * any of the handlers returns TRUE, swfdec_player_key_press() or
2253 * swfdec_player_key_release() will return TRUE. Note that unlike many event
2254 * handlers in gtk, returning TRUE will not stop further event handlers from
2255 * being invoked. Use g_signal_stop_emission() in that case.
2257 * Returns: TRUE if this handler handles the event.
2259 signals[HANDLE_KEY] = g_signal_new ("handle-key", G_TYPE_FROM_CLASS (klass),
2260 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwfdecPlayerClass, handle_key),
2261 swfdec_accumulate_or, NULL, swfdec_marshal_BOOLEAN__UINT_UINT_BOOLEAN,
2262 G_TYPE_BOOLEAN, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_BOOLEAN);
2264 * SwfdecPlayer::handle-mouse:
2265 * @player: the #SwfdecPlayer affected
2266 * @x: new x coordinate of the mouse
2267 * @y: new y coordinate of the mouse
2268 * @button: 0 for a mouse move, a positive number if a button was pressed,
2269 * a negative number if a button was released
2271 * This signal is emitted whenever @player should respond to a mouse event. If
2272 * any of the handlers returns TRUE, swfdec_player_handle_mouse() will return
2273 * TRUE. Note that unlike many event handlers in gtk, returning TRUE will not
2274 * stop further event handlers from being invoked. Use g_signal_stop_emission()
2275 * in that case.
2277 * Returns: TRUE if this handler handles the event.
2279 signals[HANDLE_MOUSE] = g_signal_new ("handle-mouse", G_TYPE_FROM_CLASS (klass),
2280 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwfdecPlayerClass, handle_mouse),
2281 swfdec_accumulate_or, NULL, swfdec_marshal_BOOLEAN__DOUBLE_DOUBLE_INT,
2282 G_TYPE_BOOLEAN, 3, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_INT);
2284 * SwfdecPlayer::audio-added:
2285 * @player: the #SwfdecPlayer affected
2286 * @audio: the audio stream that was added
2288 * Emitted whenever a new audio stream was added to @player.
2290 signals[AUDIO_ADDED] = g_signal_new ("audio-added", G_TYPE_FROM_CLASS (klass),
2291 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
2292 G_TYPE_NONE, 1, SWFDEC_TYPE_AUDIO);
2294 * SwfdecPlayer::audio-removed:
2295 * @player: the #SwfdecPlayer affected
2296 * @audio: the audio stream that was removed
2298 * Emitted whenever an audio stream was removed from @player. The stream will
2299 * have been added with the SwfdecPlayer::audio-added signal previously.
2301 signals[AUDIO_REMOVED] = g_signal_new ("audio-removed", G_TYPE_FROM_CLASS (klass),
2302 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
2303 G_TYPE_NONE, 1, SWFDEC_TYPE_AUDIO);
2305 * SwfdecPlayer::fscommand:
2306 * @player: the #SwfdecPlayer affected
2307 * @command: the command to execute. This is a lower case string.
2308 * @parameter: parameter to pass to the command. The parameter depends on the
2309 * function.
2311 * This signal is emited whenever a Flash script command (also known as
2312 * fscommand) is encountered. This method is ued by the Flash file to
2313 * communicate with the hosting environment. In web browsers it is used to
2314 * call Javascript functions. Standalone Flash players understand a limited
2315 * set of functions. They vary from player to player, but the most common are
2316 * listed here: <itemizedlist>
2317 * <listitem><para>"quit": quits the player.</para></listitem>
2318 * <listitem><para>"fullscreen": A boolean setting (parameter is "true" or
2319 * "false") that sets the player into fullscreen mode.</para></listitem>
2320 * <listitem><para>"allowscale": A boolean setting that tells the player to
2321 * not scale the Flash application.</para></listitem>
2322 * <listitem><para>"showmenu": A boolean setting that tells the Flash player
2323 * to not show its own entries in the right-click menu.</para></listitem>
2324 * <listitem><para>"exec": Run an external executable. The parameter
2325 * specifies the path.</para></listitem>
2326 * <listitem><para>"trapallkeys": A boolean setting that tells the Flash
2327 * player to pass all key events to the Flash application instead of using it
2328 * for keyboard shortcuts or similar.</para></listitem>
2329 * </itemizedlist>
2331 /* FIXME: document fscommand:toggle */
2332 signals[FSCOMMAND] = g_signal_new ("fscommand", G_TYPE_FROM_CLASS (klass),
2333 G_SIGNAL_RUN_LAST, 0, NULL, NULL, swfdec_marshal_VOID__STRING_STRING,
2334 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
2336 * SwfdecPlayer::launch:
2337 * @player: the #SwfdecPlayer affected
2338 * @url: URL to open
2339 * @target: target to load the URL into
2340 * @data: optional data to pass on with the request. Can be %NULL indicating
2341 * no data should be passed.
2342 * @header_count: number of custom HTTP headers to be sent
2343 * @header_names: names of the custom HTTP headers. %NULL terminated
2344 * @header_values: values of the custom HTTP headers. %NULL terminated
2346 * Emitted whenever the @player encounters an URL that should be loaded into
2347 * a target the Flash player does not recognize. In most cases this happens
2348 * when the user clicks a link in an embedded Flash movie that should open a
2349 * new web page.
2350 * The effect of calling any swfdec functions on the emitting @player is undefined.
2352 signals[LAUNCH] = g_signal_new ("launch", G_TYPE_FROM_CLASS (klass),
2353 G_SIGNAL_RUN_LAST, 0, NULL, NULL, swfdec_marshal_VOID__STRING_STRING_BOXED_UINT_BOXED_BOXED,
2354 G_TYPE_NONE, 6, G_TYPE_STRING, G_TYPE_STRING, SWFDEC_TYPE_BUFFER, G_TYPE_UINT, G_TYPE_STRV, G_TYPE_STRV);
2356 * SwfdecPlayer::missing-plugins:
2357 * @player: the #SwfdecPlayer missing plugins
2358 * @details: the details strings for all missing plugins
2360 * Emitted whenever a plugin is detected that GStreamer cannot currently
2361 * handle because it is missing plugins to do so. You should use
2362 * gst_install_plugins_async() to install those plugins.
2364 signals[MISSING_PLUGINS] = g_signal_new ("missing-plugins", G_TYPE_FROM_CLASS (klass),
2365 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwfdecPlayerClass, missing_plugins),
2366 NULL, NULL, g_cclosure_marshal_VOID__BOXED,
2367 G_TYPE_NONE, 1, G_TYPE_STRV);
2370 * SwfdecPlayer::query-size:
2371 * @player: the #SwfdecPlayer that resizes
2372 * @fullscreen: %TRUE if the player queries the fullscreen size, %FALSE for
2373 * the default size
2374 * @width: pointer to an integer that takes the width to use
2375 * @height: pointer to an integer that takes the height to use
2377 * This signals is emitted whenever the player is (un)fullscreened. In this
2378 * case it requests the new size the Flash file will be displayed in
2379 * immediately. If you want to provide values, connect to this signal. The
2380 * values don't have to be exact, you can still call swfdec_player_set_size()
2381 * later on. However, it will look visually nicer if your values here are
2382 * correct. By default, the screen resolution values will be used for
2383 * fullscreen and the default size will be used otherwise.
2385 * Returns: TRUE if this handler properly sets @width and @height and no
2386 * other handlers should be invoked.
2388 signals[QUERY_SIZE] = g_signal_new ("query-size", G_TYPE_FROM_CLASS (klass),
2389 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwfdecPlayerClass, query_size),
2390 swfdec_accumulate_quit, NULL, swfdec_marshal_BOOLEAN__BOOLEAN_POINTER_POINTER,
2391 G_TYPE_BOOLEAN, 3, G_TYPE_BOOLEAN, G_TYPE_POINTER, G_TYPE_POINTER);
2393 context_class->mark = swfdec_player_mark;
2394 context_class->get_time = swfdec_player_get_time;
2395 context_class->check_continue = swfdec_player_check_continue;
2397 klass->advance = swfdec_player_do_advance;
2398 klass->handle_key = swfdec_player_do_handle_key;
2399 klass->handle_mouse = swfdec_player_do_handle_mouse;
2400 klass->query_size = swfdec_player_do_query_size;
2403 static void
2404 swfdec_player_init (SwfdecPlayer *player)
2406 SwfdecPlayerPrivate *priv;
2407 guint i;
2409 player->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (player, SWFDEC_TYPE_PLAYER, SwfdecPlayerPrivate);
2410 priv->player = player;
2412 priv->system = swfdec_system_new ();
2413 priv->registered_classes = g_hash_table_new (g_direct_hash, g_direct_equal);
2414 priv->scripting_callbacks = g_hash_table_new (g_direct_hash, g_direct_equal);
2416 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
2417 priv->actions[i] = swfdec_ring_buffer_new_for_type (SwfdecPlayerAction, 16);
2419 priv->external_actions = swfdec_ring_buffer_new_for_type (SwfdecPlayerExternalAction, 8);
2420 // Big cache is required to allow images in the sizes of 3000x2000
2421 priv->cache = swfdec_cache_new (32 * 1024 * 1024);
2422 priv->socket_type = SWFDEC_TYPE_SOCKET;
2424 priv->runtime = g_timer_new ();
2425 g_timer_stop (priv->runtime);
2426 priv->max_runtime = 10 * 1000;
2427 priv->invalidations = g_array_new (FALSE, FALSE, sizeof (SwfdecRectangle));
2428 priv->mouse_visible = TRUE;
2429 priv->mouse_cursor = SWFDEC_MOUSE_CURSOR_NORMAL;
2430 priv->stage_width = -1;
2431 priv->stage_height = -1;
2432 priv->has_focus = TRUE;
2434 cairo_matrix_init_scale (&priv->stage_to_global,
2435 SWFDEC_TWIPS_SCALE_FACTOR, SWFDEC_TWIPS_SCALE_FACTOR);
2436 priv->global_to_stage = priv->stage_to_global;
2437 cairo_matrix_invert (&priv->global_to_stage);
2438 swfdec_sound_matrix_init_identity (&priv->sound_matrix);
2441 void
2442 swfdec_player_stop_all_sounds (SwfdecPlayer *player)
2444 SwfdecPlayerPrivate *priv;
2446 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2448 priv = player->priv;
2449 while (priv->audio) {
2450 swfdec_audio_remove (priv->audio->data);
2454 void
2455 swfdec_player_stop_sounds (SwfdecPlayer *player, SwfdecAudioRemoveFunc func, gpointer data)
2457 GList *walk;
2459 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2460 g_return_if_fail (func);
2462 walk = player->priv->audio;
2463 while (walk) {
2464 SwfdecAudio *audio = walk->data;
2465 walk = walk->next;
2466 if (func (audio, data))
2467 swfdec_audio_remove (audio);
2471 static void
2472 swfdec_player_invalidate_movie (SwfdecMovie *movie, double xscale, double yscale,
2473 SwfdecRectangle *rect)
2475 GSList *walk;
2476 /* FIXME: We should likely always enlarge by one and not only for filters */
2477 gboolean enlarge = !SWFDEC_IS_TEXT_FIELD_MOVIE (movie);
2479 while (movie != NULL) {
2480 for (walk = movie->filters; walk; walk = walk->next) {
2481 if (enlarge) {
2482 rect->width++;
2483 rect->height++;
2484 enlarge = FALSE;
2486 swfdec_filter_get_rectangle (walk->data, rect, xscale, yscale, rect);
2488 movie = movie->parent;
2493 * swfdec_player_invalidate:
2494 * @player: Player to invalidate in
2495 * @movie: the movie that causes the invalidation or %NULL if the invalidation
2496 * is not specific to a movie. The invalid region will be enhanced by
2497 * the area required by filters. Also the "invalidate" signal will be
2498 * emitted on the movie and all its parents.
2499 * @rect: rectangle to invalidate in global coordiantes or %NULL for the
2500 * whole player
2502 * Invalidates the given area of the player. This causes this area to be
2503 * emitted as part of the SwfdecPlayer::invalidate signal.
2505 void
2506 swfdec_player_invalidate (SwfdecPlayer *player, SwfdecMovie *movie, const SwfdecRect *rect)
2508 SwfdecPlayerPrivate *priv;
2509 SwfdecRectangle r;
2510 SwfdecRect tmp;
2511 guint i;
2513 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2515 priv = player->priv;
2516 if (rect == NULL) {
2517 r = priv->stage;
2518 } else {
2519 if (swfdec_rect_is_empty (rect))
2520 return;
2522 swfdec_rect_transform (&tmp, rect, &priv->global_to_stage);
2523 swfdec_rectangle_init_rect (&r, &tmp);
2524 swfdec_player_invalidate_movie (movie,
2525 player->priv->global_to_stage.xx * SWFDEC_TWIPS_SCALE_FACTOR,
2526 player->priv->global_to_stage.yy * SWFDEC_TWIPS_SCALE_FACTOR,
2527 &r);
2529 /* FIXME: currently we clamp the rectangle to the visible area, it might
2530 * be useful to allow out-of-bounds drawing. In that case this needs to be
2531 * changed */
2532 swfdec_rectangle_intersect (&r, &r, &priv->stage);
2533 if (swfdec_rectangle_is_empty (&r))
2534 return;
2537 SWFDEC_LOG (" invalidating %d %d %d %d", r.x, r.y, r.width, r.height);
2538 /* FIXME: get region code into swfdec? */
2539 for (i = 0; i < priv->invalidations->len; i++) {
2540 SwfdecRectangle *cur = &g_array_index (priv->invalidations, SwfdecRectangle, i);
2541 if (swfdec_rectangle_contains (cur, &r))
2542 break;
2543 if (swfdec_rectangle_contains (&r, cur)) {
2544 *cur = r;
2545 break;
2548 if (i == priv->invalidations->len) {
2549 g_array_append_val (priv->invalidations, r);
2551 SWFDEC_DEBUG ("toplevel invalidation of %d %d %d %d - now %u subregions",
2552 r.x, r.y, r.width, r.height,
2553 priv->invalidations->len);
2556 void
2557 swfdec_player_set_background_color (SwfdecPlayer *player, SwfdecColor bgcolor)
2559 SwfdecPlayerPrivate *priv;
2561 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2563 priv = player->priv;
2564 if (priv->bgcolor) {
2565 SWFDEC_DEBUG ("not setting background color twice");
2566 return;
2569 SWFDEC_INFO ("setting bgcolor to %08X", bgcolor);
2570 priv->bgcolor = bgcolor;
2571 swfdec_player_invalidate (player, NULL, NULL);
2572 g_object_notify (G_OBJECT (player), "background-color");
2576 * swfdec_player_get_level:
2577 * @player: a #SwfdecPlayer
2578 * @name: a name that is supposed to refer to a level
2579 * @version: version to use for case sensitivity checks
2581 * Checks if the given @name refers to a level, and if so, returns the level.
2582 * An example for such a name is "_level5". These strings are used to refer to
2583 * root movies inside the Flash player.
2585 * Returns: the level referred to by @name or -1 if none
2588 swfdec_player_get_level (SwfdecPlayer *player, const char *name, guint version)
2590 char *end;
2591 gulong l;
2593 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), -1);
2594 g_return_val_if_fail (name != NULL, -1);
2596 /* check name starts with "_level" */
2597 if (swfdec_strncmp (version, name, "_level", 6) != 0)
2598 return -1;
2599 name += 6;
2600 /* extract depth from rest string (or fail if it's not a depth) */
2601 errno = 0;
2602 l = strtoul (name, &end, 10);
2603 if (errno != 0 || *end != 0 || l > G_MAXINT)
2604 return -1;
2605 return l;
2608 SwfdecSpriteMovie *
2609 swfdec_player_create_movie_at_level (SwfdecPlayer *player, SwfdecResource *resource,
2610 int level)
2612 SwfdecMovie *movie;
2613 const char *s;
2615 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
2616 g_return_val_if_fail (level >= 0, NULL);
2617 g_return_val_if_fail (swfdec_player_get_movie_at_level (player, level) == NULL, NULL);
2619 /* create new root movie */
2620 s = swfdec_as_context_give_string (SWFDEC_AS_CONTEXT (player), g_strdup_printf ("_level%d", level));
2621 movie = swfdec_movie_new (player, level - 16384, NULL, resource, NULL, s);
2622 movie->name = SWFDEC_AS_STR_EMPTY;
2623 return SWFDEC_SPRITE_MOVIE (movie);
2627 * swfdec_player_get_movie_at_level:
2628 * @player: a #SwfdecPlayer
2629 * @level: number of the level
2631 * This function is used to look up root movies in the given @player.
2633 * Returns: the #SwfdecMovie located at the given level or %NULL if there is no
2634 * movie at that level.
2636 SwfdecSpriteMovie *
2637 swfdec_player_get_movie_at_level (SwfdecPlayer *player, int level)
2639 GList *walk;
2640 int depth;
2642 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
2643 g_return_val_if_fail (level >= 0, NULL);
2645 depth = level - 16384;
2646 /* find movie */
2647 for (walk = player->priv->roots; walk; walk = walk->next) {
2648 SwfdecMovie *cur = walk->data;
2649 if (cur->depth < depth)
2650 continue;
2651 if (cur->depth == depth)
2652 return SWFDEC_SPRITE_MOVIE (cur);
2653 break;
2655 return NULL;
2658 void
2659 swfdec_player_launch_with_headers (SwfdecPlayer *player, const char *url,
2660 const char *target, SwfdecBuffer *data, guint header_count,
2661 char **header_names, char **header_values)
2663 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2664 g_return_if_fail (url != NULL);
2665 g_return_if_fail (target != NULL);
2666 g_return_if_fail (header_count == 0 || header_names != NULL);
2667 g_return_if_fail (header_count == 0 || header_values != NULL);
2668 g_return_if_fail (header_names == NULL ||
2669 header_names[header_count] == NULL);
2670 g_return_if_fail (header_values == NULL ||
2671 header_values[header_count] == NULL);
2673 if (!g_ascii_strncasecmp (url, "FSCommand:", strlen ("FSCommand:"))) {
2674 const char *command = url + strlen ("FSCommand:");
2675 g_signal_emit (player, signals[FSCOMMAND], 0, command, target);
2676 return;
2678 g_signal_emit (player, signals[LAUNCH], 0, url, target, data, header_count,
2679 header_names, header_values);
2682 void
2683 swfdec_player_start_ticking (SwfdecPlayer *player)
2685 SwfdecPlayerPrivate *priv;
2687 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2688 g_return_if_fail (player->priv->initialized);
2689 g_return_if_fail (player->priv->iterate_timeout.callback == NULL);
2691 priv = player->priv;
2692 priv->iterate_timeout.callback = swfdec_player_iterate;
2693 priv->iterate_timeout.timestamp = priv->time + SWFDEC_TICKS_PER_SECOND * 256 / priv->rate / 10;
2694 swfdec_player_add_timeout (player, &priv->iterate_timeout);
2695 SWFDEC_LOG ("initialized iterate timeout %p to %"G_GUINT64_FORMAT" (now %"G_GUINT64_FORMAT")",
2696 &priv->iterate_timeout, priv->iterate_timeout.timestamp, priv->time);
2700 * swfdec_player_initialize:
2701 * @player: a #SwfdecPlayer
2702 * @version: Flash version to use
2703 * @rate: framerate in 256th or 0 for undefined
2704 * @width: width of movie
2705 * @height: height of movie
2707 * Initializes the player to the given @version, @width, @height and @rate. If
2708 * the player is already initialized, this function does nothing.
2710 void
2711 swfdec_player_initialize (SwfdecPlayer *player, guint rate, guint width, guint height)
2713 SwfdecPlayerPrivate *priv;
2715 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2716 g_return_if_fail (rate > 0);
2718 priv = player->priv;
2719 if (!priv->initialized) {
2720 priv->initialized = TRUE;
2721 g_object_notify (G_OBJECT (player), "initialized");
2722 } else {
2723 /* FIXME: need to kick all other movies out here */
2724 swfdec_player_stop_ticking (player);
2727 SWFDEC_INFO ("initializing player to size %ux%u and rate %u/256", width, height, rate);
2728 if (rate != priv->rate) {
2729 priv->rate = rate;
2730 g_object_notify (G_OBJECT (player), "rate");
2732 if (priv->width != width) {
2733 priv->width = width;
2734 g_object_notify (G_OBJECT (player), "default-width");
2736 if (priv->height != height) {
2737 priv->height = height;
2738 g_object_notify (G_OBJECT (player), "default-height");
2740 priv->broadcasted_width = priv->internal_width = priv->stage_width >= 0 ? (guint) priv->stage_width : priv->width;
2741 priv->broadcasted_height = priv->internal_height = priv->stage_height >= 0 ? (guint) priv->stage_height : priv->height;
2742 swfdec_player_update_scale (player);
2746 * swfdec_player_get_export_class:
2747 * @player: a #SwfdecPlayer
2748 * @name: garbage-collected string naming the export
2750 * Looks up the constructor for characters that are exported using @name.
2752 * Returns: a #SwfdecAsObject naming the constructor or %NULL if none
2754 SwfdecAsObject *
2755 swfdec_player_get_export_class (SwfdecPlayer *player, const char *name)
2757 SwfdecPlayerPrivate *priv = player->priv;
2758 SwfdecAsObject *ret;
2760 ret = g_hash_table_lookup (priv->registered_classes, name);
2761 if (ret) {
2762 SWFDEC_LOG ("found registered class %p for %s", ret, name);
2763 return ret;
2765 return NULL;
2769 * swfdec_player_set_export_class:
2770 * @player: a #SwfdecPlayer
2771 * @name: garbage-collected string naming the export
2772 * @object: object to use as constructor or %NULL for none
2774 * Sets the constructor to be used for instances created using the object
2775 * exported with @name.
2777 void
2778 swfdec_player_set_export_class (SwfdecPlayer *player, const char *name, SwfdecAsObject *object)
2780 SwfdecPlayerPrivate *priv;
2782 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2783 g_return_if_fail (name != NULL);
2784 g_return_if_fail (object == NULL || SWFDEC_IS_AS_OBJECT (object));
2786 priv = player->priv;
2787 if (object) {
2788 SWFDEC_LOG ("setting class %p for %s", object, name);
2789 g_hash_table_insert (priv->registered_classes, (gpointer) name, object);
2790 } else {
2791 g_hash_table_remove (priv->registered_classes, name);
2796 * swfdec_player_create_socket:
2797 * @player: a #SwfdecPlayer
2798 * @hostname: the host name to connect to.
2799 * @port: the port to connect to
2801 * Creates a new socket connecting to the given hostname and port.
2803 * Returns: a new socket
2805 SwfdecSocket *
2806 swfdec_player_create_socket (SwfdecPlayer *player, const char *hostname, guint port)
2808 SwfdecSocket *sock;
2809 SwfdecSocketClass *klass;
2811 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
2812 g_return_val_if_fail (hostname != NULL, NULL);
2813 g_return_val_if_fail (port > 0, NULL);
2815 sock = g_object_new (player->priv->socket_type, NULL);
2816 klass = SWFDEC_SOCKET_GET_CLASS (sock);
2817 klass->connect (sock, player, hostname, port);
2819 return sock;
2822 SwfdecURL *
2823 swfdec_player_create_url (SwfdecPlayer *player, const char *string)
2825 SwfdecURL *url;
2827 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
2828 g_return_val_if_fail (string != NULL, NULL);
2830 url = swfdec_url_new_relative (player->priv->base_url, string);
2831 /* FIXME: check that we don't go below base for local urls */
2832 return url;
2835 SwfdecLoader *
2836 swfdec_player_load_with_headers (SwfdecPlayer *player, const char *url,
2837 SwfdecBuffer *buffer, guint header_count, const char **header_names,
2838 const char **header_values)
2840 SwfdecLoader *loader;
2841 SwfdecLoaderClass *klass;
2843 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
2844 g_return_val_if_fail (url != NULL, NULL);
2845 g_return_val_if_fail (header_count == 0 || header_names != NULL, NULL);
2846 g_return_val_if_fail (header_count == 0 || header_values != NULL, NULL);
2847 g_return_val_if_fail (header_names == NULL ||
2848 header_names[header_count] == NULL, NULL);
2849 g_return_val_if_fail (header_values == NULL ||
2850 header_values[header_count] == NULL, NULL);
2852 loader = g_object_new (player->priv->loader_type, NULL);
2853 klass = SWFDEC_LOADER_GET_CLASS (loader);
2854 g_return_val_if_fail (klass->load != NULL, NULL);
2855 klass->load (loader, player, url, buffer, header_count, header_names,
2856 header_values);
2858 return loader;
2861 void
2862 swfdec_player_add_missing_plugin (SwfdecPlayer *player, const char *detail)
2864 SwfdecPlayerPrivate *priv;
2866 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2867 g_return_if_fail (detail != NULL);
2869 priv = player->priv;
2870 if (g_slist_find_custom (priv->missing_plugins, detail, (GCompareFunc) strcmp))
2871 return;
2873 SWFDEC_INFO ("adding missing plugin: %s\n", detail);
2874 priv->missing_plugins = g_slist_prepend (priv->missing_plugins, g_strdup (detail));
2877 static void
2878 swfdec_player_update_size (gpointer playerp, gpointer unused)
2880 SwfdecPlayer *player = playerp;
2881 SwfdecPlayerPrivate *priv = player->priv;
2883 priv->internal_width = priv->stage_width >=0 ? (guint) priv->stage_width : priv->width;
2884 priv->internal_height = priv->stage_height >=0 ? (guint) priv->stage_height : priv->height;
2886 if (priv->scale_mode != SWFDEC_SCALE_NONE)
2887 return;
2889 /* only broadcast once */
2890 if (priv->internal_width == priv->broadcasted_width &&
2891 priv->internal_height == priv->broadcasted_height)
2892 return;
2894 priv->broadcasted_width = priv->internal_width;
2895 priv->broadcasted_height = priv->internal_height;
2896 swfdec_player_broadcast (player, SWFDEC_AS_STR_Stage, SWFDEC_AS_STR_onResize, 0, NULL);
2899 void
2900 swfdec_player_set_fullscreen (SwfdecPlayer *player, gboolean fullscreen)
2902 SwfdecPlayerPrivate *priv;
2903 SwfdecAsValue val;
2904 gboolean result;
2906 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2908 priv = player->priv;
2909 if (priv->fullscreen == fullscreen)
2910 return;
2912 if (fullscreen && !priv->allow_fullscreen) {
2913 SWFDEC_INFO ("going fullscreen not allowed");
2914 return;
2917 result = FALSE;
2918 g_signal_emit (player, signals[QUERY_SIZE], 0, fullscreen,
2919 &priv->stage_width, &priv->stage_height, &result);
2921 priv->fullscreen = fullscreen;
2922 g_object_notify (G_OBJECT (player), "fullscreen");
2923 SWFDEC_AS_VALUE_SET_BOOLEAN (&val, fullscreen);
2924 swfdec_player_update_scale (player);
2925 if (SWFDEC_AS_CONTEXT (player)->global) {
2926 SwfdecSandbox *sandbox = swfdec_sandbox_get (player);
2927 swfdec_sandbox_unuse (sandbox);
2928 swfdec_player_update_size (player, NULL);
2929 swfdec_player_broadcast (player, SWFDEC_AS_STR_Stage, SWFDEC_AS_STR_onFullScreen, 1, &val);
2930 swfdec_sandbox_use (sandbox);
2931 } else {
2932 swfdec_player_update_size (player, NULL);
2933 swfdec_player_broadcast (player, SWFDEC_AS_STR_Stage, SWFDEC_AS_STR_onFullScreen, 1, &val);
2937 /** PUBLIC API ***/
2940 * swfdec_player_new:
2941 * @debugger: %NULL or a #SwfdecAsDebugger to use for debugging this player.
2943 * Creates a new player. This function is supposed to be used for testing.
2944 * Because of this, the created player will behave as predictable as possible.
2945 * For example, it will generate the same random number sequence every time.
2946 * The function calls swfdec_init () for you if it wasn't called before.
2948 * Returns: The new player
2950 SwfdecPlayer *
2951 swfdec_player_new (SwfdecAsDebugger *debugger)
2953 SwfdecPlayer *player;
2955 g_return_val_if_fail (debugger == NULL || SWFDEC_IS_AS_DEBUGGER (debugger), NULL);
2957 swfdec_init ();
2958 player = g_object_new (SWFDEC_TYPE_PLAYER, "random-seed", 0,
2959 "loader-type", SWFDEC_TYPE_FILE_LOADER, "socket-type", SWFDEC_TYPE_SOCKET,
2960 "max-runtime", 0,
2961 "debugger", debugger, NULL);
2963 return player;
2967 * swfdec_player_mouse_move:
2968 * @player: a #SwfdecPlayer
2969 * @x: x coordinate of mouse
2970 * @y: y coordinate of mouse
2972 * Updates the current mouse position. If the mouse has left the area of @player,
2973 * you should pass values outside the movie size for @x and @y. You will
2974 * probably want to call swfdec_player_advance() before to update the player to
2975 * the correct time when calling this function.
2977 * Returns: %TRUE if the mouse event was handled. %FALSE if the event should be
2978 * propagated further. A mouse event may not be handled if the user
2979 * clicked on a translucent area.
2981 gboolean
2982 swfdec_player_mouse_move (SwfdecPlayer *player, double x, double y)
2984 gboolean ret;
2986 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
2988 g_signal_emit (player, signals[HANDLE_MOUSE], 0, x, y, 0, &ret);
2990 return ret;
2994 * swfdec_player_mouse_press:
2995 * @player: a #SwfdecPlayer
2996 * @x: x coordinate of mouse
2997 * @y: y coordinate of mouse
2998 * @button: number of the button that was pressed. Swfdec supports up to 32
2999 * buttons.
3001 * Tells the @player that the mouse button @button was pressed at the given
3002 * coordinate.
3004 * Returns: %TRUE if the mouse event was handled. %FALSE if the event should be
3005 * propagated further. A mouse event may not be handled if the user
3006 * clicked on a translucent area.
3008 gboolean
3009 swfdec_player_mouse_press (SwfdecPlayer *player, double x, double y,
3010 guint button)
3012 gboolean ret;
3014 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
3015 g_return_val_if_fail (button > 0 && button <= 32, FALSE);
3017 g_signal_emit (player, signals[HANDLE_MOUSE], 0, x, y, button, &ret);
3019 return ret;
3023 * swfdec_player_mouse_release:
3024 * @player: a #SwfdecPlayer
3025 * @x: x coordinate of mouse
3026 * @y: y coordinate of mouse
3027 * @button: number of the button that was released. Swfdec supports up to 32
3028 * buttons.
3030 * Tells the @player that the mouse button @button was released at the given
3031 * coordinate.
3033 * Returns: %TRUE if the mouse event was handled. %FALSE if the event should be
3034 * propagated further. A mouse event may not be handled if the user
3035 * clicked on a translucent area.
3037 gboolean
3038 swfdec_player_mouse_release (SwfdecPlayer *player, double x, double y,
3039 guint button)
3041 gboolean ret;
3043 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
3044 g_return_val_if_fail (button > 0 && button <= 32, FALSE);
3046 g_signal_emit (player, signals[HANDLE_MOUSE], 0, x, y, -button, &ret);
3048 return ret;
3052 * swfdec_player_key_press:
3053 * @player: a #SwfdecPlayer
3054 * @keycode: the key that was pressed, must be smaller than 256.
3055 * @character: UCS4 of the character that was inserted or 0 if none
3057 * Call this function to make the @player react to a key press. A list of
3058 * defined key codes is defined by #SwfdecKey. You will likely need to
3059 * translate from your keyboard API to the Flash key codes.
3061 * Returns: %TRUE if the key press was handled by the @player, %FALSE if it
3062 * should be propagated further
3064 gboolean
3065 swfdec_player_key_press (SwfdecPlayer *player, guint keycode, guint character)
3067 gboolean ret;
3069 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
3070 g_return_val_if_fail (player->priv->has_focus, FALSE);
3071 g_return_val_if_fail (keycode < 256, FALSE);
3073 g_signal_emit (player, signals[HANDLE_KEY], 0, keycode, character, TRUE, &ret);
3075 return ret;
3079 * swfdec_player_key_release:
3080 * @player: a #SwfdecPlayer
3081 * @keycode: the key that was released
3082 * @character: UCS4 of the character that was inserted or 0 if none
3084 * Call this function to make the @player react to a key being released. See
3085 * swfdec_player_key_press() for details.
3087 * Returns: %TRUE if the key press was handled by the @player, %FALSE if it
3088 * should be propagated further
3090 gboolean
3091 swfdec_player_key_release (SwfdecPlayer *player, guint keycode, guint character)
3093 gboolean ret;
3095 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
3096 g_return_val_if_fail (player->priv->has_focus, FALSE);
3097 g_return_val_if_fail (keycode < 256, FALSE);
3099 g_signal_emit (player, signals[HANDLE_KEY], 0, keycode, character, FALSE, &ret);
3101 return ret;
3104 static void
3105 swfdec_player_render_focusrect (SwfdecPlayer *player, cairo_t *cr)
3107 #define LINE_WIDTH (3.0)
3108 SwfdecPlayerPrivate *priv;
3109 double w, h;
3110 SwfdecRect rect;
3112 priv = player->priv;
3113 if (swfdec_rect_is_empty (&priv->focusrect))
3114 return;
3116 rect = priv->focusrect;
3117 cairo_save (cr);
3118 /* I wonder why this has to be yellow... */
3119 cairo_set_source_rgb (cr, 1.0, 1.0, 0.0);
3120 cairo_set_line_width (cr, LINE_WIDTH);
3121 swfdec_player_global_to_stage (player, &rect.x0, &rect.y0);
3122 swfdec_player_global_to_stage (player, &rect.x1, &rect.y1);
3123 w = MAX (rect.x1 - rect.x0 - LINE_WIDTH, 0);
3124 h = MAX (rect.y1 - rect.y0 - LINE_WIDTH, 0);
3125 cairo_rectangle (cr, rect.x0 + LINE_WIDTH / 2, rect.y0 + LINE_WIDTH / 2, w, h);
3126 cairo_stroke (cr);
3127 cairo_restore (cr);
3128 #undef LINE_WIDTH
3132 * swfdec_player_render:
3133 * @player: a #SwfdecPlayer
3134 * @cr: #cairo_t to render to
3136 * Renders the given area of the current frame to @cr. This function just calls
3137 * swfdec_player_render_with_renderer() using the @player's renderer.
3139 void
3140 swfdec_player_render (SwfdecPlayer *player, cairo_t *cr)
3142 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3143 g_return_if_fail (cr != NULL);
3145 swfdec_player_render_with_renderer (player, cr, player->priv->renderer);
3149 * swfdec_player_render_with_renderer:
3150 * @player: a #SwfdecPlayer
3151 * @cr: #cairo_t to render to
3152 * @renderer: Renderer to use for rendering
3154 * Renders the given area of the current frame to @cr. If you only want to
3155 * redraw parts of the player, like when responding to a
3156 * SwfdecPlayer:invalidate signal, set a clip on @cr using cairo_clip():
3157 * <informalexample><programlisting>
3158 * cairo_rectangle (cr, x, y, width, height);
3159 * cairo_clip (cr);
3160 * swfdec_player_render_with_renderer (player, cr, renderer);
3161 * </programlisting></informalexample>
3162 * Only redrawing parts of the player improves performance considerably.
3164 void
3165 swfdec_player_render_with_renderer (SwfdecPlayer *player, cairo_t *cr,
3166 SwfdecRenderer *renderer)
3168 static const SwfdecColorTransform trans = { FALSE, 256, 0, 256, 0, 256, 0, 256, 0 };
3169 SwfdecPlayerPrivate *priv;
3170 GList *walk;
3172 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3173 g_return_if_fail (cr != NULL);
3174 g_return_if_fail (SWFDEC_IS_RENDERER (renderer));
3176 /* FIXME: fail when !initialized? */
3177 if (!swfdec_player_is_initialized (player))
3178 return;
3180 priv = player->priv;
3182 swfdec_renderer_attach (renderer, cr);
3183 /* clip the area */
3184 cairo_save (cr);
3185 /* compute the rectangle */
3186 SWFDEC_INFO ("=== %p: START RENDER ===", player);
3187 /* convert the cairo matrix */
3188 cairo_transform (cr, &priv->global_to_stage);
3190 for (walk = priv->roots; walk; walk = walk->next) {
3191 SwfdecMovie *movie = walk->data;
3192 if (movie->visible)
3193 swfdec_movie_render (movie, cr, &trans);
3195 cairo_restore (cr);
3196 /* NB: we render the focusrect after restoring, so the focusrect doesn't scale */
3197 swfdec_player_render_focusrect (player, cr);
3199 SWFDEC_INFO ("=== %p: END RENDER ===", player);
3203 * swfdec_player_advance:
3204 * @player: the #SwfdecPlayer to advance
3205 * @msecs: number of milliseconds to advance at maximum
3207 * Advances @player by @msecs or at most one event, whatever happens first in
3208 * the player's timeline. You should make sure to call this function as often
3209 * as swfdec_player_get_next_event() indicates or your player will not appear
3210 * smooth.
3212 * Returns: actual number of milliseconds advanced.
3214 gulong
3215 swfdec_player_advance (SwfdecPlayer *player, gulong msecs)
3217 SwfdecPlayerPrivate *priv;
3218 guint frames;
3219 glong max;
3221 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), 0);
3223 /* find the max time to advance */
3224 max = swfdec_player_get_next_event (player);
3225 if (max < 0)
3226 msecs = 0;
3227 else
3228 msecs = MIN ((gulong) max, msecs);
3229 priv = player->priv;
3230 frames = SWFDEC_TICKS_TO_SAMPLES (priv->time + SWFDEC_MSECS_TO_TICKS (msecs))
3231 - SWFDEC_TICKS_TO_SAMPLES (priv->time);
3232 g_signal_emit (player, signals[ADVANCE], 0, msecs, frames);
3234 return msecs;
3238 * swfdec_player_is_initialized:
3239 * @player: a #SwfdecPlayer
3241 * Determines if the @player is initalized yet. An initialized player is able
3242 * to provide basic values like width, height or rate. A player may not be
3243 * initialized if the loader it was started with does not reference a Flash
3244 * resources or it did not provide enough data yet. If a player is initialized,
3245 * it will never be uninitialized again.
3247 * Returns: TRUE if the basic values are known.
3249 gboolean
3250 swfdec_player_is_initialized (SwfdecPlayer *player)
3252 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
3254 return player->priv->initialized;
3258 * swfdec_player_get_next_event:
3259 * @player: ia #SwfdecPlayer
3261 * Queries how long to the next event. This is the next time when you should
3262 * call swfdec_player_advance() to forward to.
3264 * Returns: number of milliseconds until next event or -1 if no outstanding event
3266 glong
3267 swfdec_player_get_next_event (SwfdecPlayer *player)
3269 SwfdecTick tick;
3270 guint ret;
3272 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), 0);
3274 if (swfdec_as_context_is_aborted (SWFDEC_AS_CONTEXT (player)))
3275 return -1;
3277 tick = swfdec_player_get_next_event_time (player);
3278 if (tick == G_MAXUINT64)
3279 return -1;
3280 /* round up to full msecs */
3281 ret = SWFDEC_TICKS_TO_MSECS (tick + SWFDEC_TICKS_PER_SECOND / 1000 - 1);
3283 return ret;
3287 * swfdec_player_get_rate:
3288 * @player: a #SwfdecPlayer
3290 * Queries the framerate of this movie. This number specifies the number
3291 * of frames that are supposed to pass per second. It is a multiple of 1/256.
3293 * Returns: The framerate of this movie or 0 if it isn't known yet or the
3294 * movie doesn't have a framerate.
3296 double
3297 swfdec_player_get_rate (SwfdecPlayer *player)
3299 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), 0.0);
3301 return player->priv->rate / 256.0;
3305 * swfdec_player_get_default_size:
3306 * @player: a #SwfdecPlayer
3307 * @width: integer to store the width in or %NULL
3308 * @height: integer to store the height in or %NULL
3310 * If the default size of the movie is initialized, fills in @width and @height
3311 * with the size. Otherwise @width and @height are set to 0.
3313 void
3314 swfdec_player_get_default_size (SwfdecPlayer *player, guint *width, guint *height)
3316 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3318 if (width)
3319 *width = player->priv->width;
3320 if (height)
3321 *height = player->priv->height;
3325 * swfdec_player_get_size:
3326 * @player: a #SwfdecPlayer
3327 * @width: integer to store the width in or %NULL
3328 * @height: integer to store the height in or %NULL
3330 * Gets the currently set image size. If the default width or height should be
3331 * used, the width or height respectively is set to -1.
3333 void
3334 swfdec_player_get_size (SwfdecPlayer *player, int *width, int *height)
3336 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3338 if (width)
3339 *width = player->priv->stage_width;
3340 if (height)
3341 *height = player->priv->stage_height;
3345 * swfdec_player_set_size:
3346 * @player: a #SwfdecPlayer
3347 * @width: desired width of the movie or -1 for default
3348 * @height: desired height of the movie or -1 for default
3350 * Sets the image size to the given values. The image size is what the area that
3351 * the @player will render and advocate with scripts.
3353 void
3354 swfdec_player_set_size (SwfdecPlayer *player, int width, int height)
3356 SwfdecPlayerPrivate *priv;
3357 gboolean changed = FALSE;
3359 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3360 g_return_if_fail (width >= -1);
3361 g_return_if_fail (height >= -1);
3363 priv = player->priv;
3364 if (priv->stage_width != width) {
3365 priv->stage_width = width;
3366 g_object_notify (G_OBJECT (player), "width");
3367 changed = TRUE;
3369 if (priv->stage_height != height) {
3370 priv->stage_height = height;
3371 g_object_notify (G_OBJECT (player), "height");
3372 changed = TRUE;
3374 swfdec_player_update_scale (player);
3375 if (changed)
3376 swfdec_player_add_external_action (player, player, swfdec_player_update_size, NULL);
3380 * swfdec_player_get_audio:
3381 * @player: a #SwfdecPlayer
3383 * Returns a list of all currently active audio streams in @player.
3385 * Returns: A #GList of #SwfdecAudio. You must not modify or free this list.
3387 /* FIXME: I don't like this function */
3388 const GList *
3389 swfdec_player_get_audio (SwfdecPlayer * player)
3391 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
3393 return player->priv->audio;
3397 * swfdec_player_get_background_color:
3398 * @player: a #SwfdecPlayer
3400 * Gets the current suggested background color. The color will be an ARGB-color,
3401 * with the MSB being the alpha value. Note that Swfdec will not render the
3402 * background color itself, so if you want the background to not be translucent
3403 * it is your job to clear the background using this color.
3405 * Returns: the background color as an ARGB value
3407 guint
3408 swfdec_player_get_background_color (SwfdecPlayer *player)
3410 guint bgcolor;
3412 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), SWFDEC_COLOR_COMBINE (0xFF, 0xFF, 0xFF, 0xFF));
3414 bgcolor = player->priv->bgcolor;
3415 return bgcolor ? bgcolor : SWFDEC_COLOR_WHITE;
3419 * swfdec_player_get_scale_mode:
3420 * @player: a #SwfdecPlayer
3422 * Gets the currrent mode used for scaling the movie. See #SwfdecScaleMode for
3423 * the different modes.
3425 * Returns: the current scale mode
3427 SwfdecScaleMode
3428 swfdec_player_get_scale_mode (SwfdecPlayer *player)
3430 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), SWFDEC_SCALE_SHOW_ALL);
3432 return player->priv->scale_mode;
3436 * swfdec_player_set_scale_mode:
3437 * @player: a #SwfdecPlayer
3438 * @mode: a #SwfdecScaleMode
3440 * Sets the currrent mode used for scaling the movie. See #SwfdecScaleMode for
3441 * the different modes.
3443 void
3444 swfdec_player_set_scale_mode (SwfdecPlayer *player, SwfdecScaleMode mode)
3446 SwfdecPlayerPrivate *priv;
3448 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3450 priv = player->priv;
3451 if (priv->scale_mode != mode) {
3452 priv->scale_mode = mode;
3453 swfdec_player_update_scale (player);
3454 g_object_notify (G_OBJECT (player), "scale-mode");
3455 swfdec_player_add_external_action (player, player, swfdec_player_update_size, NULL);
3460 * swfdec_player_get_alignment:
3461 * @player: a #SwfdecPlayer
3463 * Gets the alignment of the player. The alignment describes what point is used
3464 * as the anchor for drawing the contents. See #SwfdecAlignment for possible
3465 * values.
3467 * Returns: the current alignment
3469 SwfdecAlignment
3470 swfdec_player_get_alignment (SwfdecPlayer *player)
3472 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), SWFDEC_ALIGNMENT_CENTER);
3474 return swfdec_player_alignment_from_flags (player->priv->align_flags);
3478 * swfdec_player_set_alignment:
3479 * @player: a #SwfdecPlayer
3480 * @align: #SwfdecAlignment to set
3482 * Sets the alignment to @align. For details about alignment, see
3483 * swfdec_player_get_alignment() and #SwfdecAlignment.
3485 void
3486 swfdec_player_set_alignment (SwfdecPlayer *player, SwfdecAlignment align)
3488 guint flags;
3490 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3492 flags = swfdec_player_alignment_to_flags (align);
3493 swfdec_player_set_align_flags (player, flags);
3496 void
3497 swfdec_player_set_align_flags (SwfdecPlayer *player, guint flags)
3499 SwfdecPlayerPrivate *priv;
3501 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3503 priv = player->priv;
3504 if (flags != priv->align_flags) {
3505 priv->align_flags = flags;
3506 swfdec_player_update_scale (player);
3507 g_object_notify (G_OBJECT (player), "alignment");
3512 * swfdec_player_get_maximum_runtime:
3513 * @player: a #SwfdecPlayer
3515 * Queries the given @player for how long scripts may run. see
3516 * swfdec_player_set_maximum_runtime() for a longer discussion of this value.
3518 * Returns: the maximum time in milliseconds that scripts are allowed to run or
3519 * 0 for infinite.
3521 gulong
3522 swfdec_player_get_maximum_runtime (SwfdecPlayer *player)
3524 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), 0);
3526 return player->priv->max_runtime;
3530 * swfdec_player_set_maximum_runtime:
3531 * @player: a #SwfdecPlayer
3532 * @msecs: time in milliseconds that scripts are allowed to run or 0 for
3533 * infinite
3535 * Sets the time that the player may use to let internal scripts run. If the
3536 * Flash file that is currently played back does not manage to complete its
3537 * scripts in the given time, it is aborted. You cannot continue the scripts at
3538 * a later point in time. However, your application may become unresponsive and
3539 * your users annoyed if they cannot interact with it for too long. To give a
3540 * reference point, the Adobe Flash player usually sets this value to 10
3541 * seconds. Note that this time determines the maximum time calling
3542 * swfdec_player_advance() may take, even if it is called with a large value.
3543 * Also note that this setting is ignored when running inside a debugger.
3545 void
3546 swfdec_player_set_maximum_runtime (SwfdecPlayer *player, gulong msecs)
3548 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3550 player->priv->max_runtime = msecs;
3551 g_object_notify (G_OBJECT (player), "max-runtime");
3555 * swfdec_player_get_scripting:
3556 * @player: a #SwfdecPlayer
3558 * Gets the current scripting implementation in use. If no implementation is in
3559 * use (the default), %NULL is returned.
3561 * Returns: the current scripting implementation used or %NULL if none
3563 SwfdecPlayerScripting *
3564 swfdec_player_get_scripting (SwfdecPlayer *player)
3566 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
3568 return player->priv->scripting;
3572 * swfdec_player_set_scripting:
3573 * @player: a #SwfdecPlayer
3574 * @scripting: the scripting implementation to use or %NULL to disable scripting
3576 * Sets the implementation to use for external scripting in the given @player.
3577 * Note that this is different from the internal script engine. See the
3578 * #SwfdecPlayerScripting paragraph for details about external scripting.
3580 void
3581 swfdec_player_set_scripting (SwfdecPlayer *player, SwfdecPlayerScripting *scripting)
3583 SwfdecPlayerPrivate *priv;
3585 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3586 g_return_if_fail (scripting == NULL || SWFDEC_IS_PLAYER_SCRIPTING (scripting));
3588 priv = player->priv;
3589 if (priv->scripting == scripting)
3590 return;
3592 if (priv->scripting)
3593 g_object_unref (priv->scripting);
3594 priv->scripting = g_object_ref (scripting);
3595 g_object_notify (G_OBJECT (player), "scripting");
3598 static void
3599 swfdec_player_update_focus (gpointer playerp, gpointer unused)
3601 SwfdecPlayer *player = playerp;
3602 SwfdecPlayerPrivate *priv = player->priv;
3604 if (priv->has_focus) {
3605 if (priv->focus == NULL)
3606 swfdec_player_grab_focus (player, priv->focus_previous);
3607 } else {
3608 swfdec_player_grab_focus (player, NULL);
3613 * swfdec_player_get_focus:
3614 * @player: a #SwfdecPlayer
3616 * Checks if the @player has keyboard focus. See swfdec_player_set_focus() for
3617 * details.
3619 * Returns: %TRUE if the player has keyboard focus.
3621 gboolean
3622 swfdec_player_get_focus (SwfdecPlayer *player)
3624 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
3626 return player->priv->has_focus;
3630 * swfdec_player_set_focus:
3631 * @player: the player
3632 * @focus: if the player is focussed
3634 * Tells the @player whether keyboard focus is inside it. The player will use
3635 * this information to draw focus indicators around objects. Note that this
3636 * update will not happen immediately, but only the next time you call
3637 * swfdec_player_advance(). The player is focussed by default. So if you
3638 * integrate it into a widget system such, you likely want to unset this upon
3639 * creation of the player.
3640 * <note><para>The player must be focussed to receive keyboard events.</para>
3641 * </note>
3643 void
3644 swfdec_player_set_focus (SwfdecPlayer *player, gboolean focus)
3646 SwfdecPlayerPrivate *priv;
3648 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3650 priv = player->priv;
3651 if (priv->has_focus == focus)
3652 return;
3654 priv->has_focus = focus;
3655 swfdec_player_add_external_action (player, player, swfdec_player_update_focus, NULL);
3656 g_object_notify (G_OBJECT (player), "focus");
3660 * swfdec_player_get_renderer:
3661 * @player: a player
3663 * Gets the current renderer in use. See swfdec_player_set_renderer() for
3664 * details.
3666 * Returns: the current #SwfdecRenderer in use.
3668 SwfdecRenderer *
3669 swfdec_player_get_renderer (SwfdecPlayer *player)
3671 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
3673 return player->priv->renderer;
3677 * swfdec_player_set_renderer:
3678 * @player: a player
3679 * @renderer: the renderer to use
3681 * Sets the renderer to be used by the @player. Setting the correct renderer is
3682 * mostly relevant for TextField flash objects with native fonts, as the
3683 * renderer provides those. It can also be very relevant for performance
3684 * reasons. See the #SwfdecRenderer documentation for details.
3686 void
3687 swfdec_player_set_renderer (SwfdecPlayer *player, SwfdecRenderer *renderer)
3689 SwfdecPlayerPrivate *priv;
3691 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3693 priv = player->priv;
3694 if (renderer) {
3695 g_object_ref (renderer);
3696 } else {
3697 renderer = swfdec_renderer_new_default (player);
3699 if (priv->renderer)
3700 g_object_unref (priv->renderer);
3701 priv->renderer = renderer;
3702 g_object_notify (G_OBJECT (player), "renderer");
3706 * swfdec_player_get_base_url:
3707 * @player: a #SwfdecPlayer
3709 * Gets the base URL that this player uses when resolving a relative URL. It is
3710 * automatically set to the parent directory of the currently played back
3711 * resource, but can be changed using swfdec_player_set_base_url(). When no
3712 * resource has been set on the @player yet, %NULL is returned.
3714 * Returns: the base #SwfdecURL for resolving relative links or %NULL
3716 const SwfdecURL *
3717 swfdec_player_get_base_url (SwfdecPlayer *player)
3719 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
3721 return player->priv->base_url;
3725 * swfdec_player_set_base_url:
3726 * @player: a #SwfdecPlayer
3727 * @url: a #SwfdecURL or %NULL to reset to defaults
3729 * Sets the URL that will be used for resolving realtive links inside the
3730 * @player.
3732 void
3733 swfdec_player_set_base_url (SwfdecPlayer *player, const SwfdecURL *url)
3735 SwfdecPlayerPrivate *priv;
3737 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3739 priv = player->priv;
3740 if (priv->base_url)
3741 swfdec_url_free (priv->base_url);
3742 if (url == NULL) {
3743 if (priv->url) {
3744 priv->base_url = swfdec_url_new_parent (priv->url);
3745 } else {
3746 priv->base_url = NULL;
3748 } else {
3749 priv->base_url = swfdec_url_copy (url);
3751 g_object_notify (G_OBJECT (player), "base-url");
3755 * swfdec_player_get_url:
3756 * @player: a #SwfdecPlayer
3758 * Gets the URL of the resource that is currently played back. If no URL has
3759 * been set on the @player yet, %NULL is returned.
3761 * Returns: the #SwfdecURL currently played back or %NULL
3763 const SwfdecURL *
3764 swfdec_player_get_url (SwfdecPlayer *player)
3766 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
3768 if (player->priv->resource == NULL)
3769 return NULL;
3771 return swfdec_loader_get_url (player->priv->resource->loader);
3775 * swfdec_player_set_url:
3776 * @player: a #SwfdecPlayer
3777 * @url: the url for the initial reference in this player
3779 * Sets the @url for the main data. This function may only be called once.
3781 void
3782 swfdec_player_set_url (SwfdecPlayer *player, const SwfdecURL *url)
3784 SwfdecPlayerPrivate *priv;
3785 SwfdecLoader *loader;
3786 SwfdecMovie *movie;
3788 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3789 g_return_if_fail (player->priv->url == NULL);
3790 g_return_if_fail (url != NULL);
3792 g_object_freeze_notify (G_OBJECT (player));
3793 priv = player->priv;
3794 priv->url = swfdec_url_copy (url);
3795 if (priv->base_url == NULL) {
3796 priv->base_url = swfdec_url_new_parent (url);
3797 g_object_notify (G_OBJECT (player), "base-url");
3799 /* we initialize url and base_url before requesting the loader, so the loader
3800 * can query them */
3801 loader = swfdec_player_load (player, swfdec_url_get_url (url), NULL);
3802 priv->resource = swfdec_resource_new (player, loader, priv->variables);
3803 movie = swfdec_movie_new (player, -16384, NULL, priv->resource, NULL, SWFDEC_AS_STR__level0);
3804 SWFDEC_ACTOR (movie)->focusrect = SWFDEC_FLASH_YES;
3805 movie->name = SWFDEC_AS_STR_EMPTY;
3806 g_object_unref (loader);
3807 g_object_notify (G_OBJECT (player), "url");
3808 g_object_thaw_notify (G_OBJECT (player));
3812 * swfdec_player_get_variables:
3813 * @player: a #SwfdecPlayer
3815 * Gets the initial variables for this player. See swfdec_player_set_variables()
3816 * for details about variables.
3818 * Returns: a string represetation of the current variables or %NULL if none are
3819 * set on the @player.
3821 const char *
3822 swfdec_player_get_variables (SwfdecPlayer *player)
3824 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
3826 return player->priv->variables;
3830 * swfdec_player_set_variables:
3831 * @player: a #SwfdecPlayer
3832 * @variables: a string that is checked to be in 'application/x-www-form-urlencoded'
3833 * syntax describing the arguments to set on the new player or NULL for
3834 * none.
3836 * Sets the loader for the main data. This function may only be called if
3837 * swfdec_player_set_url() has not been called yet.
3838 * If the @variables are set and validate, they will be set as properties on the
3839 * root movie.
3841 void
3842 swfdec_player_set_variables (SwfdecPlayer *player, const char *variables)
3844 SwfdecPlayerPrivate *priv;
3846 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3847 g_return_if_fail (player->priv->url == NULL);
3849 priv = player->priv;
3850 g_free (priv->variables);
3851 priv->variables = g_strdup (variables);
3852 g_object_notify (G_OBJECT (player), "variables");
3856 * swfdec_player_get_fullscreen:
3857 * @player: the player
3859 * CHecks if the player is in fullscreen mode currently. If the player is
3860 * in fullscreen mode, it assumes it occupies the whole screen. A player will
3861 * only ever go into fullscreen, if you have allowed it by calling
3862 * swfdec_player_set_allow_fullscreen().
3864 * Returns: %TRUE if the player is in fullscreen mode currently
3866 gboolean
3867 swfdec_player_get_fullscreen (SwfdecPlayer *player)
3869 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
3871 return player->priv->fullscreen;
3875 * swfdec_player_get_allow_fullscreen:
3876 * @player: the player
3878 * Checks if the player is allowed to go fullscreen. See
3879 * swfdec_player_set_allow_fullscreen() for details.
3881 * Returns: %TRUE if the player is allowed to go fullscreen
3883 gboolean
3884 swfdec_player_get_allow_fullscreen (SwfdecPlayer *player)
3886 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
3888 return player->priv->allow_fullscreen;
3892 * swfdec_player_set_allow_fullscreen:
3893 * @player: the player
3894 * @allow: if the player should be allowed to go fullscreen
3896 * Sets if the player is allowed to go fullscreen. If a player is allowed to go
3897 * fullscreen, it may set the SwfdecPlayer::fullscreen property to %TRUE.
3898 * Players are not allowed to go fullscreen by default. Usually applications
3899 * only want to allow going fullscreen in response to mouse or keyboard events.
3901 void
3902 swfdec_player_set_allow_fullscreen (SwfdecPlayer *player, gboolean allow)
3904 g_return_if_fail (SWFDEC_IS_PLAYER (player));
3906 player->priv->allow_fullscreen = allow;
3907 g_object_notify (G_OBJECT (player), "allow-fullscreen");
3911 * swfdec_player_get_selection:
3912 * @player: the player
3914 * Retrieves the currently selected text of the player. If no text is currently
3915 * selected, %NULL is returned.
3917 * Returns: the currently selected text or %NULL
3919 const char *
3920 swfdec_player_get_selection (SwfdecPlayer *player)
3922 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
3924 return player->priv->selection;