make new Date () predictable
[swfdec.git] / libswfdec / swfdec_player.c
blob7d682085ddd1c3a91cb05e2dc2fecbe3bd6c6573
1 /* Swfdec
2 * Copyright (C) 2006-2007 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>
28 #include <liboil/liboil.h>
30 #include "swfdec_player_internal.h"
31 #include "swfdec_as_frame_internal.h"
32 #include "swfdec_as_internal.h"
33 #include "swfdec_as_strings.h"
34 #include "swfdec_audio_internal.h"
35 #include "swfdec_button_movie.h" /* for mouse cursor */
36 #include "swfdec_cache.h"
37 #include "swfdec_debug.h"
38 #include "swfdec_enums.h"
39 #include "swfdec_event.h"
40 #include "swfdec_flash_security.h"
41 #include "swfdec_initialize.h"
42 #include "swfdec_internal.h"
43 #include "swfdec_loader_internal.h"
44 #include "swfdec_marshal.h"
45 #include "swfdec_movie.h"
46 #include "swfdec_resource.h"
47 #include "swfdec_resource_request.h"
48 #include "swfdec_script_internal.h"
49 #include "swfdec_sprite_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_BACKSPACE: the backspace key
139 * @SWFDEC_KEY_TAB: the tab key
140 * @SWFDEC_KEY_CLEAR: the clear key
141 * @SWFDEC_KEY_ENTER: the enter key
142 * @SWFDEC_KEY_SHIFT: the shift key
143 * @SWFDEC_KEY_CONTROL: the control key
144 * @SWFDEC_KEY_ALT: the alt key
145 * @SWFDEC_KEY_CAPS_LOCK: the caps lock key
146 * @SWFDEC_KEY_ESCAPE: the escape key
147 * @SWFDEC_KEY_SPACE: the space key
148 * @SWFDEC_KEY_PAGE_UP: the page up key
149 * @SWFDEC_KEY_PAGE_DOWN: the page down key
150 * @SWFDEC_KEY_END: the end key
151 * @SWFDEC_KEY_HOME: the home key
152 * @SWFDEC_KEY_LEFT: the left key
153 * @SWFDEC_KEY_UP: the up key
154 * @SWFDEC_KEY_RIGHT: the right key
155 * @SWFDEC_KEY_DOWN: the down key
156 * @SWFDEC_KEY_INSERT: the insert key
157 * @SWFDEC_KEY_DELETE: the delete key
158 * @SWFDEC_KEY_HELP: the help key
159 * @SWFDEC_KEY_0: the 0 key
160 * @SWFDEC_KEY_1: the 1 key
161 * @SWFDEC_KEY_2: the 2 key
162 * @SWFDEC_KEY_3: the 3 key
163 * @SWFDEC_KEY_4: the 4 key
164 * @SWFDEC_KEY_5: the 5 key
165 * @SWFDEC_KEY_6: the 6 key
166 * @SWFDEC_KEY_7: the 7 key
167 * @SWFDEC_KEY_8: the 8 key
168 * @SWFDEC_KEY_9: the 9 key
169 * @SWFDEC_KEY_A: the ! key
170 * @SWFDEC_KEY_B: the B key
171 * @SWFDEC_KEY_C: the C key
172 * @SWFDEC_KEY_D: the D key
173 * @SWFDEC_KEY_E: the E key
174 * @SWFDEC_KEY_F: the F key
175 * @SWFDEC_KEY_G: the G key
176 * @SWFDEC_KEY_H: the H key
177 * @SWFDEC_KEY_I: the I key
178 * @SWFDEC_KEY_J: the J key
179 * @SWFDEC_KEY_K: the K key
180 * @SWFDEC_KEY_L: the L key
181 * @SWFDEC_KEY_M: the M key
182 * @SWFDEC_KEY_N: the N key
183 * @SWFDEC_KEY_O: the O key
184 * @SWFDEC_KEY_P: the P key
185 * @SWFDEC_KEY_Q: the Q key
186 * @SWFDEC_KEY_R: the R key
187 * @SWFDEC_KEY_S: the S key
188 * @SWFDEC_KEY_T: the T key
189 * @SWFDEC_KEY_U: the U key
190 * @SWFDEC_KEY_V: the V key
191 * @SWFDEC_KEY_W: the W key
192 * @SWFDEC_KEY_X: the X key
193 * @SWFDEC_KEY_Y: the Y key
194 * @SWFDEC_KEY_Z: the Z key
195 * @SWFDEC_KEY_NUMPAD_0: the 0 key on the numeric keypad
196 * @SWFDEC_KEY_NUMPAD_1: the 1 key on the numeric keypad
197 * @SWFDEC_KEY_NUMPAD_2: the 2 key on the numeric keypad
198 * @SWFDEC_KEY_NUMPAD_3: the 3 key on the numeric keypad
199 * @SWFDEC_KEY_NUMPAD_4: the 4 key on the numeric keypad
200 * @SWFDEC_KEY_NUMPAD_5: the 5 key on the numeric keypad
201 * @SWFDEC_KEY_NUMPAD_6: the 6 key on the numeric keypad
202 * @SWFDEC_KEY_NUMPAD_7: the 7 key on the numeric keypad
203 * @SWFDEC_KEY_NUMPAD_8: the 8 key on the numeric keypad
204 * @SWFDEC_KEY_NUMPAD_9: the 9 key on the numeric keypad
205 * @SWFDEC_KEY_NUMPAD_MULTIPLY: the multiply key on the numeric keypad
206 * @SWFDEC_KEY_NUMPAD_ADD: the add key on the numeric keypad
207 * @SWFDEC_KEY_NUMPAD_SUBTRACT: the subtract key on the numeric keypad
208 * @SWFDEC_KEY_NUMPAD_DECIMAL: the decimal key on the numeric keypad
209 * @SWFDEC_KEY_NUMPAD_DIVIDE: the divide key on the numeric keypad
210 * @SWFDEC_KEY_F1: the F1 key
211 * @SWFDEC_KEY_F2: the F2 key
212 * @SWFDEC_KEY_F3: the F3 key
213 * @SWFDEC_KEY_F4: the F4 key
214 * @SWFDEC_KEY_F5: the F5 key
215 * @SWFDEC_KEY_F6: the F6 key
216 * @SWFDEC_KEY_F7: the F7 key
217 * @SWFDEC_KEY_F8: the F8 key
218 * @SWFDEC_KEY_F9: the F9 key
219 * @SWFDEC_KEY_F10: the F10 key
220 * @SWFDEC_KEY_F11: the F11 key
221 * @SWFDEC_KEY_F12: the F12 key
222 * @SWFDEC_KEY_F13: the F13 key
223 * @SWFDEC_KEY_F14: the F14 key
224 * @SWFDEC_KEY_F15: the F15 key
225 * @SWFDEC_KEY_NUM_LOCK: the num lock key
226 * @SWFDEC_KEY_SEMICOLON: the semicolon key (on English keyboards)
227 * @SWFDEC_KEY_EQUAL: the equal key (on English keyboards)
228 * @SWFDEC_KEY_MINUS: the minus key (on English keyboards)
229 * @SWFDEC_KEY_SLASH: the slash key (on English keyboards)
230 * @SWFDEC_KEY_GRAVE: the grave key (on English keyboards)
231 * @SWFDEC_KEY_LEFT_BRACKET: the left bracket key (on English keyboards)
232 * @SWFDEC_KEY_BACKSLASH: the backslash key (on English keyboards)
233 * @SWFDEC_KEY_RIGHT_BRACKET: the right bracket key (on English keyboards)
234 * @SWFDEC_KEY_APOSTROPHE: the apostrophe key (on English keyboards)
236 * Lists all known key codes in Swfdec and their meanings on an English
237 * keyboard.
240 /*** Timeouts ***/
242 static SwfdecTick
243 swfdec_player_get_next_event_time (SwfdecPlayer *player)
245 if (player->timeouts) {
246 return ((SwfdecTimeout *) player->timeouts->data)->timestamp - player->time;
247 } else {
248 return G_MAXUINT64;
253 * swfdec_player_add_timeout:
254 * @player: a #SwfdecPlayer
255 * @timeout: timeout to add
257 * Adds a timeout to @player. The timeout will be removed automatically when
258 * triggered, so you need to use swfdec_player_add_timeout() to add it again.
259 * The #SwfdecTimeout struct and callback does not use a data callback pointer.
260 * It's suggested that you use the struct as part of your own bigger struct
261 * and get it back like this:
262 * <programlisting>
263 * typedef struct {
264 * // ...
265 * SwfdecTimeout timeout;
266 * } MyStruct;
268 * static void
269 * my_struct_timeout_callback (SwfdecTimeout *timeout)
271 * MyStruct *mystruct = (MyStruct *) ((void *) timeout - G_STRUCT_OFFSET (MyStruct, timeout));
273 * // do stuff
275 * </programlisting>
277 void
278 swfdec_player_add_timeout (SwfdecPlayer *player, SwfdecTimeout *timeout)
280 GList *walk;
281 SwfdecTick next_tick;
283 g_return_if_fail (SWFDEC_IS_PLAYER (player));
284 g_return_if_fail (timeout != NULL);
285 g_return_if_fail (timeout->timestamp >= player->time);
286 g_return_if_fail (timeout->callback != NULL);
288 SWFDEC_LOG ("adding timeout %p in %"G_GUINT64_FORMAT" msecs", timeout,
289 SWFDEC_TICKS_TO_MSECS (timeout->timestamp - player->time));
290 next_tick = swfdec_player_get_next_event_time (player);
291 /* the order is important, on events with the same time, we make sure the new one is last */
292 for (walk = player->timeouts; walk; walk = walk->next) {
293 SwfdecTimeout *cur = walk->data;
294 if (cur->timestamp > timeout->timestamp)
295 break;
297 player->timeouts = g_list_insert_before (player->timeouts, walk, timeout);
298 if (next_tick != swfdec_player_get_next_event_time (player))
299 g_object_notify (G_OBJECT (player), "next-event");
303 * swfdec_player_remove_timeout:
304 * @player: a #SwfdecPlayer
305 * @timeout: a timeout that should be removed
307 * Removes the @timeout from the list of scheduled timeouts. The timeout must
308 * have been added with swfdec_player_add_timeout() before.
310 void
311 swfdec_player_remove_timeout (SwfdecPlayer *player, SwfdecTimeout *timeout)
313 SwfdecTick next_tick;
315 g_return_if_fail (SWFDEC_IS_PLAYER (player));
316 g_return_if_fail (timeout != NULL);
317 g_return_if_fail (timeout->timestamp >= player->time);
318 g_return_if_fail (timeout->callback != NULL);
320 SWFDEC_LOG ("removing timeout %p", timeout);
321 next_tick = swfdec_player_get_next_event_time (player);
322 player->timeouts = g_list_remove (player->timeouts, timeout);
323 if (next_tick != swfdec_player_get_next_event_time (player))
324 g_object_notify (G_OBJECT (player), "next-event");
327 /*** Actions ***/
329 typedef struct {
330 SwfdecMovie * movie; /* the movie to trigger the action on */
331 SwfdecScript * script; /* script to execute or NULL to trigger action */
332 SwfdecEventType event; /* the action to trigger */
333 } SwfdecPlayerAction;
335 typedef struct {
336 gpointer object;
337 SwfdecActionFunc func;
338 gpointer data;
339 } SwfdecPlayerExternalAction;
341 static void
342 swfdec_player_compress_actions (SwfdecRingBuffer *buffer)
344 SwfdecPlayerAction *action, tmp;
345 guint i = 0;
347 for (i = swfdec_ring_buffer_get_n_elements (buffer) + 1; i > 0; i--) {
348 action = swfdec_ring_buffer_pop (buffer);
349 g_assert (action);
350 if (action->movie == NULL)
351 continue;
352 tmp = *action;
353 action = swfdec_ring_buffer_push (buffer);
354 *action = tmp;
356 SWFDEC_INFO ("compresed action queue to %u elements",
357 swfdec_ring_buffer_get_n_elements (buffer));
358 for (i = 0; i < swfdec_ring_buffer_get_n_elements (buffer); i++) {
359 action = swfdec_ring_buffer_peek_nth (buffer, i);
360 g_assert (action->movie != NULL);
364 static void
365 swfdec_player_do_add_action (SwfdecPlayer *player, guint importance, SwfdecPlayerAction *act)
367 SwfdecPlayerAction *action = swfdec_ring_buffer_push (player->actions[importance]);
368 if (action == NULL) {
369 /* try to get rid of freed actions */
370 if (swfdec_ring_buffer_get_size (player->actions[importance]) >= 256) {
371 swfdec_player_compress_actions (player->actions[importance]);
372 action = swfdec_ring_buffer_push (player->actions[importance]);
373 /* if it doesn't get smaller, bail */
374 if (action == NULL) {
375 swfdec_as_context_abort (SWFDEC_AS_CONTEXT (player),
376 "256 levels of recursion were exceeded in one action list.");
377 return;
379 } else {
380 swfdec_ring_buffer_set_size (player->actions[importance],
381 swfdec_ring_buffer_get_size (player->actions[importance]) + 16);
382 action = swfdec_ring_buffer_push (player->actions[importance]);
383 g_assert (action);
386 *action = *act;
390 * swfdec_player_add_event:
391 * @player: a #SwfdecPlayer
392 * @movie: the movie on which to trigger the event
393 * @type: type of the event
394 * @importance: importance of the event
396 * Adds an action to the @player. Actions are used by Flash player to solve
397 * reentrancy issues. Instead of calling back into the Actionscript engine,
398 * an action is queued for later execution. So if you're writing code that
399 * is calling Actionscript code, you want to do this by using actions.
401 void
402 swfdec_player_add_action (SwfdecPlayer *player, SwfdecMovie *movie, SwfdecEventType type,
403 guint importance)
405 SwfdecPlayerAction action = { movie, NULL, type };
407 g_return_if_fail (SWFDEC_IS_PLAYER (player));
408 g_return_if_fail (SWFDEC_IS_MOVIE (movie));
409 g_return_if_fail (importance < SWFDEC_PLAYER_N_ACTION_QUEUES);
411 SWFDEC_LOG ("adding action %s %u", movie->name, type);
412 swfdec_player_do_add_action (player, importance, &action);
415 void
416 swfdec_player_add_action_script (SwfdecPlayer *player, SwfdecMovie *movie,
417 SwfdecScript *script, guint importance)
419 SwfdecPlayerAction action = { movie, script, 0 };
421 g_return_if_fail (SWFDEC_IS_PLAYER (player));
422 g_return_if_fail (SWFDEC_IS_MOVIE (movie));
423 g_return_if_fail (script != NULL);
424 g_return_if_fail (importance < SWFDEC_PLAYER_N_ACTION_QUEUES);
426 SWFDEC_LOG ("adding action script %s %s", movie->name, script->name);
427 swfdec_player_do_add_action (player, importance, &action);
431 * swfdec_player_remove_all_actions:
432 * @player: a #SwfdecPlayer
433 * @movie: movie pointer identifying the actions to be removed
435 * Removes all actions associated with @movie that have not yet been executed.
436 * See swfdec_player_add_action() for details about actions.
438 void
439 swfdec_player_remove_all_actions (SwfdecPlayer *player, SwfdecMovie *movie)
441 SwfdecPlayerAction *action;
442 guint i, j;
444 g_return_if_fail (SWFDEC_IS_PLAYER (player));
445 g_return_if_fail (SWFDEC_IS_MOVIE (movie));
447 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
448 for (j = 0; j < swfdec_ring_buffer_get_n_elements (player->actions[i]); j++) {
449 action = swfdec_ring_buffer_peek_nth (player->actions[i], j);
451 if (action->movie == movie) {
452 SWFDEC_LOG ("removing action %p %u",
453 action->movie, action->event);
454 action->movie = NULL;
460 static gboolean
461 swfdec_player_do_action (SwfdecPlayer *player)
463 SwfdecPlayerAction *action;
464 guint i;
466 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
467 do {
468 action = swfdec_ring_buffer_pop (player->actions[i]);
469 if (action == NULL)
470 break;
471 } while (action->movie == NULL); /* skip removed actions */
472 if (action) {
473 if (action->script) {
474 swfdec_as_object_run_with_security (SWFDEC_AS_OBJECT (action->movie),
475 action->script, SWFDEC_SECURITY (action->movie->resource));
476 } else {
477 swfdec_movie_execute (action->movie, action->event);
479 return TRUE;
483 return FALSE;
486 static void
487 swfdec_player_perform_external_actions (SwfdecPlayer *player)
489 SwfdecPlayerExternalAction *action;
490 guint i;
492 /* remove timeout if it exists - do this before executing stuff below */
493 if (player->external_timeout.callback) {
494 swfdec_player_remove_timeout (player, &player->external_timeout);
495 player->external_timeout.callback = NULL;
498 /* we need to query the number of current actions so newly added ones aren't
499 * executed in here */
500 for (i = swfdec_ring_buffer_get_n_elements (player->external_actions); i > 0; i--) {
501 action = swfdec_ring_buffer_pop (player->external_actions);
502 g_assert (action != NULL);
503 /* skip removed actions */
504 if (action->object == NULL)
505 continue;
506 action->func (action->object, action->data);
507 swfdec_player_perform_actions (player);
511 static void
512 swfdec_player_trigger_external_actions (SwfdecTimeout *advance)
514 SwfdecPlayer *player = SWFDEC_PLAYER ((guint8 *) advance - G_STRUCT_OFFSET (SwfdecPlayer, external_timeout));
516 player->external_timeout.callback = NULL;
517 swfdec_player_perform_external_actions (player);
520 void
521 swfdec_player_add_external_action (SwfdecPlayer *player, gpointer object,
522 SwfdecActionFunc action_func, gpointer action_data)
524 SwfdecPlayerExternalAction *action;
526 g_return_if_fail (SWFDEC_IS_PLAYER (player));
527 g_return_if_fail (object != NULL);
528 g_return_if_fail (action_func != NULL);
530 SWFDEC_LOG ("adding external action %p %p %p", object, action_func, action_data);
531 action = swfdec_ring_buffer_push (player->external_actions);
532 if (action == NULL) {
533 /* FIXME: limit number of actions to not get inf loops due to scripts? */
534 swfdec_ring_buffer_set_size (player->external_actions,
535 swfdec_ring_buffer_get_size (player->external_actions) + 16);
536 action = swfdec_ring_buffer_push (player->external_actions);
537 g_assert (action);
539 action->object = object;
540 action->func = action_func;
541 action->data = action_data;
542 if (!player->external_timeout.callback) {
543 /* trigger execution immediately.
544 * But if initialized, keep at least 100ms from when the last external
545 * timeout triggered. This is a crude method to get around infinite loops
546 * when script actions executed by external actions trigger another external
547 * action that would execute instantly.
549 if (player->initialized) {
550 player->external_timeout.timestamp = MAX (player->time,
551 player->external_timeout.timestamp + SWFDEC_MSECS_TO_TICKS (100));
552 } else {
553 player->external_timeout.timestamp = player->time;
555 player->external_timeout.callback = swfdec_player_trigger_external_actions;
556 swfdec_player_add_timeout (player, &player->external_timeout);
560 void
561 swfdec_player_remove_all_external_actions (SwfdecPlayer *player, gpointer object)
563 SwfdecPlayerExternalAction *action;
564 guint i;
566 g_return_if_fail (SWFDEC_IS_PLAYER (player));
567 g_return_if_fail (object != NULL);
569 for (i = 0; i < swfdec_ring_buffer_get_n_elements (player->external_actions); i++) {
570 action = swfdec_ring_buffer_peek_nth (player->external_actions, i);
572 if (action->object == object) {
573 SWFDEC_LOG ("removing external action %p %p %p",
574 action->object, action->func, action->data);
575 action->object = NULL;
580 /*** SwfdecPlayer ***/
582 enum {
583 INVALIDATE,
584 ADVANCE,
585 HANDLE_KEY,
586 HANDLE_MOUSE,
587 AUDIO_ADDED,
588 AUDIO_REMOVED,
589 LAUNCH,
590 FSCOMMAND,
591 LAST_SIGNAL
594 static guint signals[LAST_SIGNAL] = { 0, };
596 enum {
597 PROP_0,
598 PROP_CACHE_SIZE,
599 PROP_INITIALIZED,
600 PROP_DEFAULT_WIDTH,
601 PROP_DEFAULT_HEIGHT,
602 PROP_RATE,
603 PROP_MOUSE_CURSOR,
604 PROP_NEXT_EVENT,
605 PROP_BACKGROUND_COLOR,
606 PROP_WIDTH,
607 PROP_HEIGHT,
608 PROP_ALIGNMENT,
609 PROP_SCALE,
610 PROP_SYSTEM,
611 PROP_MAX_RUNTIME
614 G_DEFINE_TYPE (SwfdecPlayer, swfdec_player, SWFDEC_TYPE_AS_CONTEXT)
616 void
617 swfdec_player_remove_movie (SwfdecPlayer *player, SwfdecMovie *movie)
619 swfdec_movie_remove (movie);
620 player->movies = g_list_remove (player->movies, movie);
623 static guint
624 swfdec_player_alignment_to_flags (SwfdecAlignment alignment)
626 static const guint align_flags[9] = {
627 SWFDEC_ALIGN_FLAG_TOP | SWFDEC_ALIGN_FLAG_LEFT,
628 SWFDEC_ALIGN_FLAG_TOP,
629 SWFDEC_ALIGN_FLAG_TOP | SWFDEC_ALIGN_FLAG_RIGHT,
630 SWFDEC_ALIGN_FLAG_LEFT,
632 SWFDEC_ALIGN_FLAG_RIGHT,
633 SWFDEC_ALIGN_FLAG_BOTTOM | SWFDEC_ALIGN_FLAG_LEFT,
634 SWFDEC_ALIGN_FLAG_BOTTOM,
635 SWFDEC_ALIGN_FLAG_BOTTOM | SWFDEC_ALIGN_FLAG_RIGHT
637 return align_flags[alignment];
640 static SwfdecAlignment
641 swfdec_player_alignment_from_flags (guint flags)
643 if (flags & SWFDEC_ALIGN_FLAG_TOP) {
644 if (flags & SWFDEC_ALIGN_FLAG_LEFT) {
645 return SWFDEC_ALIGNMENT_TOP_LEFT;
646 } else if (flags & SWFDEC_ALIGN_FLAG_RIGHT) {
647 return SWFDEC_ALIGNMENT_TOP_RIGHT;
648 } else {
649 return SWFDEC_ALIGNMENT_TOP;
651 } else if (flags & SWFDEC_ALIGN_FLAG_BOTTOM) {
652 if (flags & SWFDEC_ALIGN_FLAG_LEFT) {
653 return SWFDEC_ALIGNMENT_BOTTOM_LEFT;
654 } else if (flags & SWFDEC_ALIGN_FLAG_RIGHT) {
655 return SWFDEC_ALIGNMENT_BOTTOM_RIGHT;
656 } else {
657 return SWFDEC_ALIGNMENT_BOTTOM;
659 } else {
660 if (flags & SWFDEC_ALIGN_FLAG_LEFT) {
661 return SWFDEC_ALIGNMENT_LEFT;
662 } else if (flags & SWFDEC_ALIGN_FLAG_RIGHT) {
663 return SWFDEC_ALIGNMENT_RIGHT;
664 } else {
665 return SWFDEC_ALIGNMENT_CENTER;
670 static void
671 swfdec_player_get_property (GObject *object, guint param_id, GValue *value,
672 GParamSpec * pspec)
674 SwfdecPlayer *player = SWFDEC_PLAYER (object);
676 switch (param_id) {
677 case PROP_BACKGROUND_COLOR:
678 g_value_set_uint (value, swfdec_player_get_background_color (player));
679 break;
680 case PROP_CACHE_SIZE:
681 g_value_set_uint (value, player->cache->max_size);
682 break;
683 case PROP_INITIALIZED:
684 g_value_set_boolean (value, swfdec_player_is_initialized (player));
685 break;
686 case PROP_DEFAULT_WIDTH:
687 g_value_set_uint (value, player->width);
688 break;
689 case PROP_DEFAULT_HEIGHT:
690 g_value_set_uint (value, player->height);
691 break;
692 case PROP_RATE:
693 g_value_set_double (value, player->rate / 256.0);
694 break;
695 case PROP_MOUSE_CURSOR:
696 g_value_set_enum (value, player->mouse_cursor);
697 break;
698 case PROP_NEXT_EVENT:
699 g_value_set_uint (value, swfdec_player_get_next_event (player));
700 break;
701 case PROP_WIDTH:
702 g_value_set_int (value, player->stage_width);
703 break;
704 case PROP_HEIGHT:
705 g_value_set_int (value, player->stage_height);
706 break;
707 case PROP_ALIGNMENT:
708 g_value_set_enum (value, swfdec_player_alignment_from_flags (player->align_flags));
709 break;
710 case PROP_SCALE:
711 g_value_set_enum (value, player->scale_mode);
712 break;
713 case PROP_SYSTEM:
714 g_value_set_object (value, player->system);
715 break;
716 case PROP_MAX_RUNTIME:
717 g_value_set_ulong (value, player->max_runtime);
718 break;
719 default:
720 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
721 break;
725 static void
726 swfdec_player_update_scale (SwfdecPlayer *player)
728 int width, height;
729 double scale_x, scale_y;
731 player->stage.width = player->stage_width >= 0 ? player->stage_width : (int) player->width;
732 player->stage.height = player->stage_height >= 0 ? player->stage_height : (int) player->height;
733 if (player->stage.height == 0 || player->stage.width == 0) {
734 player->scale_x = 1.0;
735 player->scale_y = 1.0;
736 player->offset_x = 0;
737 player->offset_y = 0;
738 return;
740 if (player->width == 0 || player->height == 0) {
741 scale_x = 1.0;
742 scale_y = 1.0;
743 } else {
744 scale_x = (double) player->stage.width / player->width;
745 scale_y = (double) player->stage.height / player->height;
747 switch (player->scale_mode) {
748 case SWFDEC_SCALE_SHOW_ALL:
749 player->scale_x = MIN (scale_x, scale_y);
750 player->scale_y = player->scale_x;
751 break;
752 case SWFDEC_SCALE_NO_BORDER:
753 player->scale_x = MAX (scale_x, scale_y);
754 player->scale_y = player->scale_x;
755 break;
756 case SWFDEC_SCALE_EXACT_FIT:
757 player->scale_x = scale_x;
758 player->scale_y = scale_y;
759 break;
760 case SWFDEC_SCALE_NONE:
761 player->scale_x = 1.0;
762 player->scale_y = 1.0;
763 break;
764 default:
765 g_assert_not_reached ();
767 width = player->stage.width - ceil (player->width * player->scale_x);
768 height = player->stage.height - ceil (player->height * player->scale_y);
769 if (player->align_flags & SWFDEC_ALIGN_FLAG_LEFT) {
770 player->offset_x = 0;
771 } else if (player->align_flags & SWFDEC_ALIGN_FLAG_RIGHT) {
772 player->offset_x = width;
773 } else {
774 player->offset_x = width / 2;
776 if (player->align_flags & SWFDEC_ALIGN_FLAG_TOP) {
777 player->offset_y = 0;
778 } else if (player->align_flags & SWFDEC_ALIGN_FLAG_BOTTOM) {
779 player->offset_y = height;
780 } else {
781 player->offset_y = height / 2;
783 SWFDEC_LOG ("coordinate translation is %g * x + %d - %g * y + %d",
784 player->scale_x, player->offset_x, player->scale_y, player->offset_y);
785 #if 0
786 /* FIXME: make this emit the signal at the right time */
787 player->invalid.x0 = 0;
788 player->invalid.y0 = 0;
789 player->invalid.x1 = player->stage_width;
790 player->invalid.y1 = player->stage_height;
791 #endif
794 static void
795 swfdec_player_set_property (GObject *object, guint param_id, const GValue *value,
796 GParamSpec *pspec)
798 SwfdecPlayer *player = SWFDEC_PLAYER (object);
800 switch (param_id) {
801 case PROP_BACKGROUND_COLOR:
802 swfdec_player_set_background_color (player, g_value_get_uint (value));
803 break;
804 case PROP_CACHE_SIZE:
805 player->cache->max_size = g_value_get_uint (value);
806 break;
807 case PROP_WIDTH:
808 swfdec_player_set_size (player, g_value_get_int (value), player->stage_height);
809 break;
810 case PROP_HEIGHT:
811 swfdec_player_set_size (player, player->stage_width, g_value_get_int (value));
812 break;
813 case PROP_ALIGNMENT:
814 player->align_flags = swfdec_player_alignment_to_flags (g_value_get_enum (value));
815 swfdec_player_update_scale (player);
816 break;
817 case PROP_SCALE:
818 swfdec_player_set_scale_mode (player, g_value_get_enum (value));
819 break;
820 case PROP_SYSTEM:
821 g_object_unref (player->system);
822 if (g_value_get_object (value)) {
823 player->system = SWFDEC_SYSTEM (g_value_dup_object (value));
824 } else {
825 player->system = swfdec_system_new ();
827 break;
828 case PROP_MAX_RUNTIME:
829 swfdec_player_set_maximum_runtime (player, g_value_get_ulong (value));
830 break;
831 default:
832 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
833 break;
837 static void
838 swfdec_player_dispose (GObject *object)
840 SwfdecPlayer *player = SWFDEC_PLAYER (object);
841 guint i;
843 swfdec_player_stop_all_sounds (player);
844 swfdec_player_resource_request_finish (player);
845 g_hash_table_destroy (player->registered_classes);
847 while (player->roots)
848 swfdec_movie_destroy (player->roots->data);
849 if (player->resource) {
850 g_object_unref (player->resource);
851 player->resource = NULL;
854 /* we do this here so references to GC'd objects get freed */
855 G_OBJECT_CLASS (swfdec_player_parent_class)->dispose (object);
857 swfdec_player_remove_all_external_actions (player, player);
858 #ifndef G_DISABLE_ASSERT
860 SwfdecPlayerExternalAction *action;
861 while ((action = swfdec_ring_buffer_pop (player->external_actions)) != NULL) {
862 g_assert (action->object == NULL); /* skip removed actions */
866 SwfdecPlayerAction *action;
867 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
868 while ((action = swfdec_ring_buffer_pop (player->actions[i])) != NULL) {
869 g_assert (action->movie == NULL); /* skip removed actions */
873 #endif
874 swfdec_ring_buffer_free (player->external_actions);
875 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
876 swfdec_ring_buffer_free (player->actions[i]);
878 g_assert (player->movies == NULL);
879 g_assert (player->audio == NULL);
880 if (player->external_timeout.callback)
881 swfdec_player_remove_timeout (player, &player->external_timeout);
882 if (player->rate) {
883 swfdec_player_remove_timeout (player, &player->iterate_timeout);
885 g_assert (player->timeouts == NULL);
886 g_list_free (player->intervals);
887 while (player->rooted_objects)
888 swfdec_player_unroot_object (player, player->rooted_objects->data);
889 player->intervals = NULL;
890 swfdec_cache_unref (player->cache);
891 if (player->system) {
892 g_object_unref (player->system);
893 player->system = NULL;
895 g_array_free (player->invalidations, TRUE);
896 player->invalidations = NULL;
897 if (player->runtime) {
898 g_timer_destroy (player->runtime);
899 player->runtime = NULL;
903 static void
904 swfdec_player_broadcast (SwfdecPlayer *player, const char *object_name, const char *signal)
906 SwfdecAsValue val;
907 SwfdecAsObject *obj;
909 SWFDEC_DEBUG ("broadcasting message %s.%s", object_name, signal);
910 obj = SWFDEC_AS_CONTEXT (player)->global;
911 swfdec_as_object_get_variable (obj, object_name, &val);
912 if (!SWFDEC_AS_VALUE_IS_OBJECT (&val))
913 return;
914 obj = SWFDEC_AS_VALUE_GET_OBJECT (&val);
915 SWFDEC_AS_VALUE_SET_STRING (&val, signal);
916 swfdec_as_object_call (obj, SWFDEC_AS_STR_broadcastMessage, 1, &val, NULL);
919 static void
920 swfdec_player_update_mouse_cursor (SwfdecPlayer *player)
922 SwfdecMouseCursor new = SWFDEC_MOUSE_CURSOR_NORMAL;
924 if (!player->mouse_visible) {
925 new = SWFDEC_MOUSE_CURSOR_NONE;
926 } else if (player->mouse_grab != NULL) {
927 /* FIXME: this needs to be more sophisticated, since SwfdecEditText may
928 * want to have different mouse cursors depending on location (it supports
929 * links in theory)
931 if (SWFDEC_IS_BUTTON_MOVIE (player->mouse_grab))
932 new = SWFDEC_MOUSE_CURSOR_CLICK;
935 if (new != player->mouse_cursor) {
936 player->mouse_cursor = new;
937 g_object_notify (G_OBJECT (player), "mouse-cursor");
941 static void
942 swfdec_player_update_drag_movie (SwfdecPlayer *player)
944 double x, y;
945 SwfdecMovie *movie;
947 if (player->mouse_drag == NULL)
948 return;
950 movie = player->mouse_drag;
951 g_assert (movie->cache_state == SWFDEC_MOVIE_UP_TO_DATE);
952 x = player->mouse_x;
953 y = player->mouse_y;
954 swfdec_player_stage_to_global (player, &x, &y);
955 if (movie->parent)
956 swfdec_movie_global_to_local (movie->parent, &x, &y);
957 if (player->mouse_drag_center) {
958 x -= (movie->extents.x1 - movie->extents.x0) / 2;
959 y -= (movie->extents.y1 - movie->extents.y0) / 2;
960 } else {
961 x -= player->mouse_drag_x;
962 y -= player->mouse_drag_y;
964 x = CLAMP (x, player->mouse_drag_rect.x0, player->mouse_drag_rect.x1);
965 y = CLAMP (y, player->mouse_drag_rect.y0, player->mouse_drag_rect.y1);
966 SWFDEC_LOG ("mouse is at %g %g, originally (%g %g)", x, y, player->mouse_x, player->mouse_y);
967 if (x != movie->matrix.x0 || y != movie->matrix.y0) {
968 movie->matrix.x0 = x;
969 movie->matrix.y0 = y;
970 swfdec_movie_queue_update (movie, SWFDEC_MOVIE_INVALID_MATRIX);
975 * swfdec_player_set_drag_movie:
976 * @player: a #SwfdecPlayer
977 * @drag: the movie to be dragged by the mouse or NULL to unset
978 * @center: TRUE if the center of @drag should be at the mouse pointer, FALSE if (0,0)
979 * of @drag should be at the mouse pointer.
980 * @rect: NULL or the rectangle that clips the mouse position. The coordinates
981 * are in the coordinate system of the parent of @drag.
983 * Sets or unsets the movie that is dragged by the mouse.
985 void
986 swfdec_player_set_drag_movie (SwfdecPlayer *player, SwfdecMovie *drag, gboolean center,
987 SwfdecRect *rect)
989 g_return_if_fail (SWFDEC_IS_PLAYER (player));
990 g_return_if_fail (drag == NULL || SWFDEC_IS_MOVIE (drag));
992 /* FIXME: need to do anything with old drag? */
993 player->mouse_drag = drag;
994 player->mouse_drag_center = center;
995 if (drag && !center) {
996 player->mouse_drag_x = player->mouse_x;
997 player->mouse_drag_y = player->mouse_y;
998 swfdec_player_stage_to_global (player, &player->mouse_drag_x, &player->mouse_drag_y);
999 if (drag->parent)
1000 swfdec_movie_global_to_local (drag->parent, &player->mouse_drag_x, &player->mouse_drag_y);
1001 player->mouse_drag_x -= drag->matrix.x0;
1002 player->mouse_drag_y -= drag->matrix.y0;
1004 if (rect) {
1005 player->mouse_drag_rect = *rect;
1006 } else {
1007 player->mouse_drag_rect.x0 = -G_MAXDOUBLE;
1008 player->mouse_drag_rect.y0 = -G_MAXDOUBLE;
1009 player->mouse_drag_rect.x1 = G_MAXDOUBLE;
1010 player->mouse_drag_rect.y1 = G_MAXDOUBLE;
1012 SWFDEC_DEBUG ("starting drag in %g %g %g %g",
1013 player->mouse_drag_rect.x0, player->mouse_drag_rect.y0,
1014 player->mouse_drag_rect.x1, player->mouse_drag_rect.y1);
1015 /* FIXME: need a way to make sure we get updated */
1016 if (drag) {
1017 swfdec_movie_update (drag);
1018 drag->modified = TRUE;
1019 swfdec_player_update_drag_movie (player);
1023 static void
1024 swfdec_player_update_mouse_position (SwfdecPlayer *player)
1026 GList *walk;
1027 SwfdecMovie *mouse_grab = NULL;
1029 if (player->mouse_button) {
1030 mouse_grab = player->mouse_grab;
1031 } else {
1032 double x, y;
1033 /* if the mouse button is pressed the grab widget stays the same (I think) */
1034 x = player->mouse_x;
1035 y = player->mouse_y;
1036 swfdec_player_stage_to_global (player, &x, &y);
1037 for (walk = g_list_last (player->roots); walk; walk = walk->prev) {
1038 mouse_grab = swfdec_movie_get_movie_at (walk->data, x, y);
1039 if (mouse_grab)
1040 break;
1043 SWFDEC_DEBUG ("%s %p has mouse at %g %g",
1044 mouse_grab ? G_OBJECT_TYPE_NAME (mouse_grab) : "---",
1045 mouse_grab, player->mouse_x, player->mouse_y);
1046 if (player->mouse_grab && mouse_grab != player->mouse_grab)
1047 swfdec_movie_send_mouse_change (player->mouse_grab, TRUE);
1048 player->mouse_grab = mouse_grab;
1049 if (mouse_grab)
1050 swfdec_movie_send_mouse_change (mouse_grab, FALSE);
1053 static void
1054 swfdec_player_do_mouse_move (SwfdecPlayer *player)
1056 GList *walk;
1058 swfdec_player_update_drag_movie (player);
1059 for (walk = player->movies; walk; walk = walk->next) {
1060 swfdec_movie_queue_script (walk->data, SWFDEC_EVENT_MOUSE_MOVE);
1062 swfdec_player_broadcast (player, SWFDEC_AS_STR_Mouse, SWFDEC_AS_STR_onMouseMove);
1063 swfdec_player_update_mouse_position (player);
1066 static void
1067 swfdec_player_do_mouse_button (SwfdecPlayer *player)
1069 GList *walk;
1070 guint event;
1071 const char *event_name;
1073 if (player->mouse_button) {
1074 event = SWFDEC_EVENT_MOUSE_DOWN;
1075 event_name = SWFDEC_AS_STR_onMouseDown;
1076 } else {
1077 event = SWFDEC_EVENT_MOUSE_UP;
1078 event_name = SWFDEC_AS_STR_onMouseUp;
1080 for (walk = player->movies; walk; walk = walk->next) {
1081 swfdec_movie_queue_script (walk->data, event);
1083 swfdec_player_broadcast (player, SWFDEC_AS_STR_Mouse, event_name);
1084 if (player->mouse_grab)
1085 swfdec_movie_send_mouse_change (player->mouse_grab, FALSE);
1088 static void
1089 swfdec_player_emit_signals (SwfdecPlayer *player)
1091 GList *walk;
1093 /* emit invalidate signal */
1094 if (!swfdec_rectangle_is_empty (&player->invalid_extents)) {
1095 g_signal_emit (player, signals[INVALIDATE], 0, &player->invalid_extents,
1096 player->invalidations->data, player->invalidations->len);
1097 swfdec_rectangle_init_empty (&player->invalid_extents);
1098 g_array_set_size (player->invalidations, 0);
1101 /* emit audio-added for all added audio streams */
1102 for (walk = player->audio; walk; walk = walk->next) {
1103 SwfdecAudio *audio = walk->data;
1105 if (audio->added)
1106 continue;
1107 g_signal_emit (player, signals[AUDIO_ADDED], 0, audio);
1108 audio->added = TRUE;
1112 static gboolean
1113 swfdec_player_do_handle_key (SwfdecPlayer *player, guint keycode, guint character, gboolean down)
1115 g_assert (keycode < 256);
1117 if (!swfdec_player_lock (player))
1118 return FALSE;
1119 /* set the correct variables */
1120 player->last_keycode = keycode;
1121 player->last_character = character;
1122 if (down) {
1123 player->key_pressed[keycode / 8] |= 1 << keycode % 8;
1124 } else {
1125 player->key_pressed[keycode / 8] &= ~(1 << keycode % 8);
1127 swfdec_player_broadcast (player, SWFDEC_AS_STR_Key, down ? SWFDEC_AS_STR_onKeyDown : SWFDEC_AS_STR_onKeyUp);
1128 swfdec_player_perform_actions (player);
1129 swfdec_player_unlock (player);
1131 return TRUE;
1134 static gboolean
1135 swfdec_player_do_handle_mouse (SwfdecPlayer *player,
1136 double x, double y, int button)
1138 if (!swfdec_player_lock (player))
1139 return FALSE;
1141 SWFDEC_LOG ("handling mouse at %g %g %d", x, y, button);
1142 if (player->mouse_x != x || player->mouse_y != y) {
1143 player->mouse_x = x;
1144 player->mouse_y = y;
1145 swfdec_player_do_mouse_move (player);
1147 if (player->mouse_button != button) {
1148 player->mouse_button = button;
1149 swfdec_player_do_mouse_button (player);
1151 swfdec_player_perform_actions (player);
1152 swfdec_player_unlock (player);
1154 /* FIXME: allow events to pass through */
1155 return TRUE;
1158 void
1159 swfdec_player_global_to_stage (SwfdecPlayer *player, double *x, double *y)
1161 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1162 g_return_if_fail (x != NULL);
1163 g_return_if_fail (y != NULL);
1165 *x = *x / SWFDEC_TWIPS_SCALE_FACTOR * player->scale_x + player->offset_x;
1166 *y = *y / SWFDEC_TWIPS_SCALE_FACTOR * player->scale_y + player->offset_y;
1169 void
1170 swfdec_player_stage_to_global (SwfdecPlayer *player, double *x, double *y)
1172 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1173 g_return_if_fail (x != NULL);
1174 g_return_if_fail (y != NULL);
1176 *x = (*x - player->offset_x) / player->scale_x * SWFDEC_TWIPS_SCALE_FACTOR;
1177 *y = (*y - player->offset_y) / player->scale_y * SWFDEC_TWIPS_SCALE_FACTOR;
1180 static void
1181 swfdec_player_execute_on_load_init (SwfdecPlayer *player)
1183 GList *walk;
1185 /* FIXME: This can be made a LOT faster with correct caching, but I'm lazy */
1186 do {
1187 for (walk = player->movies; walk; walk = walk->next) {
1188 SwfdecMovie *movie = walk->data;
1189 SwfdecResource *resource = swfdec_movie_get_own_resource (movie);
1190 if (resource == NULL)
1191 continue;
1192 if (swfdec_resource_emit_on_load_init (resource))
1193 break;
1195 } while (walk != NULL);
1198 static void
1199 swfdec_player_iterate (SwfdecTimeout *timeout)
1201 SwfdecPlayer *player = SWFDEC_PLAYER ((guint8 *) timeout - G_STRUCT_OFFSET (SwfdecPlayer, iterate_timeout));
1202 GList *walk;
1204 /* add timeout again - do this first because later code can change it */
1205 /* FIXME: rounding issues? */
1206 player->iterate_timeout.timestamp += SWFDEC_TICKS_PER_SECOND * 256 / player->rate;
1207 swfdec_player_add_timeout (player, &player->iterate_timeout);
1208 swfdec_player_perform_external_actions (player);
1209 SWFDEC_INFO ("=== START ITERATION ===");
1210 /* start the iteration. This performs a goto next frame on all
1211 * movies that are not stopped. It also queues onEnterFrame.
1213 for (walk = player->movies; walk; walk = walk->next) {
1214 SwfdecMovieClass *klass = SWFDEC_MOVIE_GET_CLASS (walk->data);
1215 if (klass->iterate_start)
1216 klass->iterate_start (walk->data);
1218 swfdec_player_perform_actions (player);
1219 SWFDEC_INFO ("=== STOP ITERATION ===");
1220 /* this loop allows removal of walk->data */
1221 walk = player->movies;
1222 while (walk) {
1223 SwfdecMovie *cur = walk->data;
1224 SwfdecMovieClass *klass = SWFDEC_MOVIE_GET_CLASS (cur);
1225 walk = walk->next;
1226 g_assert (klass->iterate_end);
1227 if (!klass->iterate_end (cur))
1228 swfdec_movie_destroy (cur);
1230 swfdec_player_execute_on_load_init (player);
1231 swfdec_player_resource_request_perform (player);
1232 swfdec_player_perform_actions (player);
1235 static void
1236 swfdec_player_advance_audio (SwfdecPlayer *player, guint samples)
1238 SwfdecAudio *audio;
1239 GList *walk;
1241 if (samples == 0)
1242 return;
1244 /* don't use for loop here, because we need to advance walk before
1245 * removing the audio */
1246 walk = player->audio;
1247 while (walk) {
1248 audio = walk->data;
1249 walk = walk->next;
1250 if (swfdec_audio_iterate (audio, samples) == 0)
1251 swfdec_audio_remove (audio);
1255 static void
1256 swfdec_player_do_advance (SwfdecPlayer *player, gulong msecs, guint audio_samples)
1258 SwfdecTimeout *timeout;
1259 SwfdecTick target_time;
1260 guint frames_now;
1262 if (!swfdec_player_lock (player))
1263 return;
1265 target_time = player->time + SWFDEC_MSECS_TO_TICKS (msecs);
1266 SWFDEC_DEBUG ("advancing %lu msecs (%u audio frames)", msecs, audio_samples);
1268 for (timeout = player->timeouts ? player->timeouts->data : NULL;
1269 timeout && timeout->timestamp <= target_time;
1270 timeout = player->timeouts ? player->timeouts->data : NULL) {
1271 player->timeouts = g_list_remove (player->timeouts, timeout);
1272 frames_now = SWFDEC_TICKS_TO_SAMPLES (timeout->timestamp) -
1273 SWFDEC_TICKS_TO_SAMPLES (player->time);
1274 player->time = timeout->timestamp;
1275 swfdec_player_advance_audio (player, frames_now);
1276 audio_samples -= frames_now;
1277 SWFDEC_LOG ("activating timeout %p now (timeout is %"G_GUINT64_FORMAT", target time is %"G_GUINT64_FORMAT,
1278 timeout, timeout->timestamp, target_time);
1279 timeout->callback (timeout);
1280 swfdec_player_perform_actions (player);
1282 if (target_time > player->time) {
1283 frames_now = SWFDEC_TICKS_TO_SAMPLES (target_time) -
1284 SWFDEC_TICKS_TO_SAMPLES (player->time);
1285 player->time = target_time;
1286 swfdec_player_advance_audio (player, frames_now);
1287 audio_samples -= frames_now;
1289 g_assert (audio_samples == 0);
1291 swfdec_player_unlock (player);
1294 void
1295 swfdec_player_perform_actions (SwfdecPlayer *player)
1297 GList *walk;
1299 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1301 while (swfdec_player_do_action (player));
1302 for (walk = player->roots; walk; walk = walk->next) {
1303 swfdec_movie_update (walk->data);
1305 /* update the state of the mouse when stuff below it moved */
1306 if (swfdec_rectangle_contains_point (&player->invalid_extents, player->mouse_x, player->mouse_y)) {
1307 SWFDEC_INFO ("=== NEED TO UPDATE mouse post-iteration ===");
1308 swfdec_player_update_mouse_position (player);
1309 while (swfdec_player_do_action (player));
1310 for (walk = player->roots; walk; walk = walk->next) {
1311 swfdec_movie_update (walk->data);
1316 /* used for breakpoints */
1317 void
1318 swfdec_player_lock_soft (SwfdecPlayer *player)
1320 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1321 g_assert (swfdec_rectangle_is_empty (&player->invalid_extents));
1323 g_object_freeze_notify (G_OBJECT (player));
1324 g_timer_start (player->runtime);
1325 SWFDEC_DEBUG ("LOCKED");
1328 gboolean
1329 swfdec_player_lock (SwfdecPlayer *player)
1331 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
1332 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[0]) == 0);
1333 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[1]) == 0);
1334 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[2]) == 0);
1335 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[3]) == 0);
1337 if (swfdec_as_context_is_aborted (SWFDEC_AS_CONTEXT (player)))
1338 return FALSE;
1340 g_object_ref (player);
1341 swfdec_player_lock_soft (player);
1342 return TRUE;
1345 /* used for breakpoints */
1346 void
1347 swfdec_player_unlock_soft (SwfdecPlayer *player)
1349 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1351 SWFDEC_DEBUG ("UNLOCK");
1352 g_timer_stop (player->runtime);
1353 swfdec_player_update_mouse_cursor (player);
1354 g_object_thaw_notify (G_OBJECT (player));
1355 swfdec_player_emit_signals (player);
1358 void
1359 swfdec_player_unlock (SwfdecPlayer *player)
1361 SwfdecAsContext *context;
1363 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1364 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[0]) == 0);
1365 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[1]) == 0);
1366 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[2]) == 0);
1367 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[3]) == 0);
1368 context = SWFDEC_AS_CONTEXT (player);
1369 g_return_if_fail (context->state != SWFDEC_AS_CONTEXT_INTERRUPTED);
1371 if (context->state == SWFDEC_AS_CONTEXT_RUNNING)
1372 swfdec_as_context_maybe_gc (SWFDEC_AS_CONTEXT (player));
1373 swfdec_player_unlock_soft (player);
1374 g_object_unref (player);
1377 static gboolean
1378 swfdec_accumulate_or (GSignalInvocationHint *ihint, GValue *return_accu,
1379 const GValue *handler_return, gpointer data)
1381 if (g_value_get_boolean (handler_return))
1382 g_value_set_boolean (return_accu, TRUE);
1383 return TRUE;
1386 static void
1387 swfdec_player_mark_string_object (gpointer key, gpointer value, gpointer data)
1389 swfdec_as_string_mark (key);
1390 swfdec_as_object_mark (value);
1393 static void
1394 swfdec_player_mark_rooted_object (gpointer object, gpointer unused)
1396 if (SWFDEC_IS_RESOURCE (object)) {
1397 swfdec_resource_mark (object);
1398 } else if (SWFDEC_IS_AS_OBJECT (object)) {
1399 swfdec_as_object_mark (object);
1403 static void
1404 swfdec_player_mark (SwfdecAsContext *context)
1406 SwfdecPlayer *player = SWFDEC_PLAYER (context);
1408 g_hash_table_foreach (player->registered_classes, swfdec_player_mark_string_object, NULL);
1409 swfdec_as_object_mark (player->MovieClip);
1410 swfdec_as_object_mark (player->Video);
1411 g_list_foreach (player->roots, (GFunc) swfdec_as_object_mark, NULL);
1412 g_list_foreach (player->intervals, (GFunc) swfdec_as_object_mark, NULL);
1413 g_list_foreach (player->rooted_objects, swfdec_player_mark_rooted_object, NULL);
1415 SWFDEC_AS_CONTEXT_CLASS (swfdec_player_parent_class)->mark (context);
1418 static void
1419 swfdec_player_get_time (SwfdecAsContext *context, GTimeVal *tv)
1421 *tv = context->start_time;
1423 /* FIXME: what granularity do we want? Currently it's milliseconds */
1424 g_time_val_add (tv, SWFDEC_TICKS_TO_MSECS (SWFDEC_PLAYER (context)->time) * 1000);
1427 static gboolean
1428 swfdec_player_check_continue (SwfdecAsContext *context)
1430 SwfdecPlayer *player = SWFDEC_PLAYER (context);
1432 if (player->max_runtime == 0)
1433 return TRUE;
1434 return g_timer_elapsed (player->runtime, NULL) * 1000 <= player->max_runtime;
1437 static void
1438 swfdec_player_class_init (SwfdecPlayerClass *klass)
1440 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1441 SwfdecAsContextClass *context_class = SWFDEC_AS_CONTEXT_CLASS (klass);
1443 object_class->get_property = swfdec_player_get_property;
1444 object_class->set_property = swfdec_player_set_property;
1445 object_class->dispose = swfdec_player_dispose;
1447 g_object_class_install_property (object_class, PROP_INITIALIZED,
1448 g_param_spec_boolean ("initialized", "initialized", "TRUE when the player has initialized its basic values",
1449 FALSE, G_PARAM_READABLE));
1450 g_object_class_install_property (object_class, PROP_DEFAULT_WIDTH,
1451 g_param_spec_uint ("default-width", "default width", "default width of the movie",
1452 0, G_MAXUINT, 0, G_PARAM_READABLE));
1453 g_object_class_install_property (object_class, PROP_DEFAULT_HEIGHT,
1454 g_param_spec_uint ("default-height", "default height", "default height of the movie",
1455 0, G_MAXUINT, 0, G_PARAM_READABLE));
1456 g_object_class_install_property (object_class, PROP_RATE,
1457 g_param_spec_double ("rate", "rate", "rate in frames per second",
1458 0.0, 256.0, 0.0, G_PARAM_READABLE));
1459 g_object_class_install_property (object_class, PROP_MOUSE_CURSOR,
1460 g_param_spec_enum ("mouse-cursor", "mouse cursor", "how the mouse pointer should be presented",
1461 SWFDEC_TYPE_MOUSE_CURSOR, SWFDEC_MOUSE_CURSOR_NONE, G_PARAM_READABLE));
1462 g_object_class_install_property (object_class, PROP_NEXT_EVENT,
1463 g_param_spec_long ("next-event", "next event", "how many milliseconds until the next event or 0 when no event pending",
1464 -1, G_MAXLONG, -1, G_PARAM_READABLE));
1465 g_object_class_install_property (object_class, PROP_CACHE_SIZE,
1466 g_param_spec_uint ("cache-size", "cache size", "maximum cache size in bytes",
1467 0, G_MAXUINT, 50 * 1024 * 1024, G_PARAM_READABLE));
1468 g_object_class_install_property (object_class, PROP_BACKGROUND_COLOR,
1469 g_param_spec_uint ("background-color", "background color", "ARGB color used to draw the background",
1470 0, G_MAXUINT, SWFDEC_COLOR_COMBINE (0xFF, 0xFF, 0xFF, 0xFF), G_PARAM_READWRITE));
1471 g_object_class_install_property (object_class, PROP_WIDTH,
1472 g_param_spec_int ("width", "width", "current width of the movie",
1473 -1, G_MAXINT, -1, G_PARAM_READWRITE));
1474 g_object_class_install_property (object_class, PROP_HEIGHT,
1475 g_param_spec_int ("height", "height", "current height of the movie",
1476 -1, G_MAXINT, -1, G_PARAM_READWRITE));
1477 g_object_class_install_property (object_class, PROP_ALIGNMENT,
1478 g_param_spec_enum ("alignment", "alignment", "point of the screen to align the output to",
1479 SWFDEC_TYPE_ALIGNMENT, SWFDEC_ALIGNMENT_CENTER, G_PARAM_READWRITE));
1480 g_object_class_install_property (object_class, PROP_SCALE,
1481 g_param_spec_enum ("scale-mode", "scale mode", "method used to scale the movie",
1482 SWFDEC_TYPE_SCALE_MODE, SWFDEC_SCALE_SHOW_ALL, G_PARAM_READWRITE));
1483 g_object_class_install_property (object_class, PROP_SCALE,
1484 g_param_spec_object ("system", "system", "object holding system information",
1485 SWFDEC_TYPE_SYSTEM, G_PARAM_READWRITE));
1486 g_object_class_install_property (object_class, PROP_MAX_RUNTIME,
1487 g_param_spec_ulong ("max-runtime", "maximum runtime", "maximum time in msecs scripts may run in the player before aborting",
1488 0, G_MAXULONG, 10 * 1000, G_PARAM_READWRITE));
1491 * SwfdecPlayer::invalidate:
1492 * @player: the #SwfdecPlayer affected
1493 * @extents: the smallest rectangle enclosing the full region of changes
1494 * @rectangles: a number of smaller rectangles for fine-grained control over
1495 * changes
1496 * @n_rectangles: number of rectangles in @rectangles
1498 * This signal is emitted whenever graphical elements inside the player have
1499 * changed. It provides two ways to look at the changes: By looking at the
1500 * @extents parameter, it provides a simple way to get a single rectangle that
1501 * encloses all changes. By looking at the @rectangles array, you can get
1502 * finer control over changes which is very useful if your rendering system
1503 * provides a way to handle regions.
1505 signals[INVALIDATE] = g_signal_new ("invalidate", G_TYPE_FROM_CLASS (klass),
1506 G_SIGNAL_RUN_LAST, 0, NULL, NULL, swfdec_marshal_VOID__BOXED_POINTER_UINT,
1507 G_TYPE_NONE, 3, SWFDEC_TYPE_RECTANGLE, G_TYPE_POINTER, G_TYPE_UINT);
1509 * SwfdecPlayer::advance:
1510 * @player: the #SwfdecPlayer affected
1511 * @msecs: the amount of milliseconds the player will advance
1512 * @audio_samples: number of frames the audio is advanced (in 44100Hz steps)
1514 * Emitted whenever the player advances.
1516 signals[ADVANCE] = g_signal_new ("advance", G_TYPE_FROM_CLASS (klass),
1517 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwfdecPlayerClass, advance),
1518 NULL, NULL, swfdec_marshal_VOID__ULONG_UINT,
1519 G_TYPE_NONE, 2, G_TYPE_ULONG, G_TYPE_UINT);
1521 * SwfdecPlayer::handle-key:
1522 * @player: the #SwfdecPlayer affected
1523 * @key: #SwfdecKey that was pressed or released
1524 * @pressed: %TRUE if the @key was pressed or %FALSE if it was released
1526 * This signal is emitted whenever @player should respond to a key event. If
1527 * any of the handlers returns TRUE, swfdec_player_key_press() or
1528 * swfdec_player_key_release() will return TRUE. Note that unlike many event
1529 * handlers in gtk, returning TRUE will not stop further event handlers from
1530 * being invoked. Use g_signal_stop_emission() in that case.
1532 * Returns: TRUE if this handler handles the event.
1534 signals[HANDLE_KEY] = g_signal_new ("handle-key", G_TYPE_FROM_CLASS (klass),
1535 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwfdecPlayerClass, handle_key),
1536 swfdec_accumulate_or, NULL, swfdec_marshal_BOOLEAN__UINT_UINT_BOOLEAN,
1537 G_TYPE_BOOLEAN, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_BOOLEAN);
1539 * SwfdecPlayer::handle-mouse:
1540 * @player: the #SwfdecPlayer affected
1541 * @x: new x coordinate of the mouse
1542 * @y: new y coordinate of the mouse
1543 * @button: 1 if the button is pressed, 0 if not
1545 * This signal is emitted whenever @player should respond to a mouse event. If
1546 * any of the handlers returns TRUE, swfdec_player_handle_mouse() will return
1547 * TRUE. Note that unlike many event handlers in gtk, returning TRUE will not
1548 * stop further event handlers from being invoked. Use g_signal_stop_emission()
1549 * in that case.
1551 * Returns: TRUE if this handler handles the event.
1553 signals[HANDLE_MOUSE] = g_signal_new ("handle-mouse", G_TYPE_FROM_CLASS (klass),
1554 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwfdecPlayerClass, handle_mouse),
1555 swfdec_accumulate_or, NULL, swfdec_marshal_BOOLEAN__DOUBLE_DOUBLE_INT,
1556 G_TYPE_BOOLEAN, 3, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_INT);
1558 * SwfdecPlayer::audio-added:
1559 * @player: the #SwfdecPlayer affected
1560 * @audio: the audio stream that was added
1562 * Emitted whenever a new audio stream was added to @player.
1564 signals[AUDIO_ADDED] = g_signal_new ("audio-added", G_TYPE_FROM_CLASS (klass),
1565 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
1566 G_TYPE_NONE, 1, SWFDEC_TYPE_AUDIO);
1568 * SwfdecPlayer::audio-removed:
1569 * @player: the #SwfdecPlayer affected
1570 * @audio: the audio stream that was removed
1572 * Emitted whenever an audio stream was removed from @player. The stream will
1573 * have been added with the SwfdecPlayer::audio-added signal previously.
1575 signals[AUDIO_REMOVED] = g_signal_new ("audio-removed", G_TYPE_FROM_CLASS (klass),
1576 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
1577 G_TYPE_NONE, 1, SWFDEC_TYPE_AUDIO);
1579 * SwfdecPlayer::fscommand:
1580 * @player: the #SwfdecPlayer affected
1581 * @command: the command to execute. This is a lower case string.
1582 * @parameter: parameter to pass to the command. The parameter depends on the
1583 * function.
1585 * This signal is emited whenever a Flash script command (also known as
1586 * fscommand) is encountered. This method is ued by the Flash file to
1587 * communicate with the hosting environment. In web browsers it is used to
1588 * call Javascript functions. Standalone Flash players understand a limited
1589 * set of functions. They vary from player to player, but the most common are
1590 * listed here: <itemizedlist>
1591 * <listitem><para>"quit": quits the player.</para></listitem>
1592 * <listitem><para>"fullscreen": A boolean setting (parameter is "true" or
1593 * "false") that sets the player into fullscreen mode.</para></listitem>
1594 * <listitem><para>"allowscale": A boolean setting that tells the player to
1595 * not scale the Flash application.</para></listitem>
1596 * <listitem><para>"showmenu": A boolean setting that tells the Flash player
1597 * to not show its own entries in the right-click menu.</para></listitem>
1598 * <listitem><para>"exec": Run an external executable. The parameter
1599 * specifies the path.</para></listitem>
1600 * <listitem><para>"trapallkeys": A boolean setting that tells the Flash
1601 * player to pass all key events to the Flash application instead of using it
1602 * for keyboard shortcuts or similar.</para></listitem>
1603 * </itemizedlist>
1605 signals[FSCOMMAND] = g_signal_new ("fscommand", G_TYPE_FROM_CLASS (klass),
1606 G_SIGNAL_RUN_LAST, 0, NULL, NULL, swfdec_marshal_VOID__STRING_STRING,
1607 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
1609 * SwfdecPlayer::launch:
1610 * @player: the #SwfdecPlayer affected
1611 * @request: the type of request
1612 * @url: URL to open
1613 * @target: target to load the URL into
1614 * @data: optional data to pass on with the request. Will be of mime type
1615 * application/x-www-form-urlencoded. Can be %NULL indicating no data
1616 * should be passed.
1618 * Emitted whenever the @player encounters an URL that should be loaded into
1619 * a target the Flash player does not recognize. In most cases this happens
1620 * when the user clicks a link in an embedded Flash movie that should open a
1621 * new web page.
1622 * The effect of calling any swfdec functions on the emitting @player is undefined.
1624 signals[LAUNCH] = g_signal_new ("launch", G_TYPE_FROM_CLASS (klass),
1625 G_SIGNAL_RUN_LAST, 0, NULL, NULL, swfdec_marshal_VOID__ENUM_STRING_STRING_BOXED,
1626 G_TYPE_NONE, 4, SWFDEC_TYPE_LOADER_REQUEST, G_TYPE_STRING, G_TYPE_STRING,
1627 SWFDEC_TYPE_BUFFER);
1629 context_class->mark = swfdec_player_mark;
1630 context_class->get_time = swfdec_player_get_time;
1631 context_class->check_continue = swfdec_player_check_continue;
1633 klass->advance = swfdec_player_do_advance;
1634 klass->handle_key = swfdec_player_do_handle_key;
1635 klass->handle_mouse = swfdec_player_do_handle_mouse;
1638 static void
1639 swfdec_player_init (SwfdecPlayer *player)
1641 guint i;
1643 player->system = swfdec_system_new ();
1644 player->registered_classes = g_hash_table_new (g_direct_hash, g_direct_equal);
1646 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
1647 player->actions[i] = swfdec_ring_buffer_new_for_type (SwfdecPlayerAction, 16);
1649 player->external_actions = swfdec_ring_buffer_new_for_type (SwfdecPlayerExternalAction, 8);
1650 player->cache = swfdec_cache_new (50 * 1024 * 1024); /* 100 MB */
1651 player->bgcolor = SWFDEC_COLOR_COMBINE (0xFF, 0xFF, 0xFF, 0xFF);
1653 player->runtime = g_timer_new ();
1654 g_timer_stop (player->runtime);
1655 player->max_runtime = 10 * 1000;
1656 player->invalidations = g_array_new (FALSE, FALSE, sizeof (SwfdecRectangle));
1657 player->mouse_visible = TRUE;
1658 player->mouse_cursor = SWFDEC_MOUSE_CURSOR_NORMAL;
1659 player->iterate_timeout.callback = swfdec_player_iterate;
1660 player->stage_width = -1;
1661 player->stage_height = -1;
1663 swfdec_player_resource_request_init (player);
1666 void
1667 swfdec_player_stop_all_sounds (SwfdecPlayer *player)
1669 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1671 while (player->audio) {
1672 swfdec_audio_remove (player->audio->data);
1676 void
1677 swfdec_player_stop_sounds (SwfdecPlayer *player, SwfdecAudioRemoveFunc func, gpointer data)
1679 GList *walk;
1681 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1682 g_return_if_fail (func);
1684 walk = player->audio;
1685 while (walk) {
1686 SwfdecAudio *audio = walk->data;
1687 walk = walk->next;
1688 if (func (audio, data))
1689 swfdec_audio_remove (audio);
1693 /* rect is in global coordinates */
1694 void
1695 swfdec_player_invalidate (SwfdecPlayer *player, const SwfdecRect *rect)
1697 SwfdecRectangle r;
1698 SwfdecRect tmp;
1699 guint i;
1701 if (swfdec_rect_is_empty (rect)) {
1702 SWFDEC_ERROR ("called with an empty rectanle. In theory this shouldn't happen.");
1703 SWFDEC_ERROR (" However, degenerate matrixes can cause this. We need a fix for that.");
1704 return;
1707 tmp = *rect;
1708 swfdec_player_global_to_stage (player, &tmp.x0, &tmp.y0);
1709 swfdec_player_global_to_stage (player, &tmp.x1, &tmp.y1);
1710 swfdec_rectangle_init_rect (&r, &tmp);
1711 /* FIXME: currently we clamp the rectangle to the visible area, it might
1712 * be useful to allow out-of-bounds drawing. In that case this needs to be
1713 * changed */
1714 swfdec_rectangle_intersect (&r, &r, &player->stage);
1715 if (swfdec_rectangle_is_empty (&r))
1716 return;
1718 /* FIXME: get region code into swfdec? */
1719 for (i = 0; i < player->invalidations->len; i++) {
1720 SwfdecRectangle *cur = &g_array_index (player->invalidations, SwfdecRectangle, i);
1721 if (swfdec_rectangle_contains (cur, &r))
1722 break;
1723 if (swfdec_rectangle_contains (&r, cur)) {
1724 *cur = r;
1725 swfdec_rectangle_union (&player->invalid_extents, &player->invalid_extents, &r);
1728 if (i == player->invalidations->len) {
1729 g_array_append_val (player->invalidations, r);
1730 swfdec_rectangle_union (&player->invalid_extents, &player->invalid_extents, &r);
1732 SWFDEC_DEBUG ("toplevel invalidation of %g %g %g %g - invalid region now %d %d %d %d (%u subregions)",
1733 rect->x0, rect->y0, rect->x1, rect->y1,
1734 player->invalid_extents.x, player->invalid_extents.y,
1735 player->invalid_extents.x + player->invalid_extents.width,
1736 player->invalid_extents.y + player->invalid_extents.height,
1737 player->invalidations->len);
1741 * swfdec_player_get_level:
1742 * @player: a #SwfdecPlayer
1743 * @name: a name that is supposed to refer to a level
1745 * Checks if the given @name refers to a level, and if so, returns the level.
1746 * An example for such a name is "_level5". These strings are used to refer to
1747 * root movies inside the Flash player.
1749 * Returns: the level referred to by @name or -1 if none
1752 swfdec_player_get_level (SwfdecPlayer *player, const char *name)
1754 char *end;
1755 gulong l;
1757 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), -1);
1758 g_return_val_if_fail (name != NULL, -1);
1760 /* check name starts with "_level" */
1761 if (swfdec_strncmp (SWFDEC_AS_CONTEXT (player)->version, name, "_level", 6) != 0)
1762 return -1;
1763 name += 6;
1764 /* extract depth from rest string (or fail if it's not a depth) */
1765 errno = 0;
1766 l = strtoul (name, &end, 10);
1767 if (errno != 0 || *end != 0 || l > G_MAXINT)
1768 return -1;
1769 return l;
1772 SwfdecSpriteMovie *
1773 swfdec_player_create_movie_at_level (SwfdecPlayer *player, SwfdecResource *resource,
1774 int level)
1776 SwfdecMovie *movie;
1777 const char *s;
1779 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
1780 g_return_val_if_fail (level >= 0, NULL);
1781 g_return_val_if_fail (swfdec_player_get_movie_at_level (player, level) == NULL, NULL);
1783 /* create new root movie */
1784 s = swfdec_as_context_give_string (SWFDEC_AS_CONTEXT (player), g_strdup_printf ("_level%d", level));
1785 movie = swfdec_movie_new (player, level - 16384, NULL, resource, NULL, s);
1786 if (movie == NULL)
1787 return NULL;
1788 movie->name = SWFDEC_AS_STR_EMPTY;
1789 return SWFDEC_SPRITE_MOVIE (movie);
1793 * swfdec_player_get_movie_at_level:
1794 * @player: a #SwfdecPlayer
1795 * @level: number of the level
1797 * This function is used to look up root movies in the given @player.
1799 * Returns: the #SwfdecMovie located at the given level or %NULL if there is no
1800 * movie at that level.
1802 SwfdecSpriteMovie *
1803 swfdec_player_get_movie_at_level (SwfdecPlayer *player, int level)
1805 GList *walk;
1806 int depth;
1808 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
1809 g_return_val_if_fail (level >= 0, NULL);
1811 depth = level - 16384;
1812 /* find movie */
1813 for (walk = player->roots; walk; walk = walk->next) {
1814 SwfdecMovie *cur = walk->data;
1815 if (cur->depth < depth)
1816 continue;
1817 if (cur->depth == depth)
1818 return SWFDEC_SPRITE_MOVIE (cur);
1819 break;
1821 return NULL;
1824 void
1825 swfdec_player_launch (SwfdecPlayer *player, SwfdecLoaderRequest request, const char *url,
1826 const char *target, SwfdecBuffer *data)
1828 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1829 g_return_if_fail (url != NULL);
1830 g_return_if_fail (target != NULL);
1832 if (!g_ascii_strncasecmp (url, "FSCommand:", strlen ("FSCommand:"))) {
1833 const char *command = url + strlen ("FSCommand:");
1834 g_signal_emit (player, signals[FSCOMMAND], 0, command, target);
1835 return;
1837 g_signal_emit (player, signals[LAUNCH], 0, (int) request, url, target, data);
1841 * swfdec_player_initialize:
1842 * @player: a #SwfdecPlayer
1843 * @version: Flash version to use
1844 * @rate: framerate in 256th or 0 for undefined
1845 * @width: width of movie
1846 * @height: height of movie
1848 * Initializes the player to the given @version, @width, @height and @rate. If
1849 * the player is already initialized, this function does nothing.
1851 void
1852 swfdec_player_initialize (SwfdecPlayer *player, guint version,
1853 guint rate, guint width, guint height)
1855 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1856 g_return_if_fail (rate > 0);
1858 if (!player->initialized) {
1859 SwfdecAsContext *context = SWFDEC_AS_CONTEXT (player);
1860 swfdec_as_context_startup (context, version);
1861 /* reset state for initialization */
1862 /* FIXME: have a better way to do this */
1863 if (context->state == SWFDEC_AS_CONTEXT_RUNNING) {
1864 context->state = SWFDEC_AS_CONTEXT_NEW;
1865 swfdec_sprite_movie_init_context (player, version);
1866 swfdec_video_movie_init_context (player, version);
1867 swfdec_net_connection_init_context (player, version);
1868 swfdec_net_stream_init_context (player, version);
1870 swfdec_as_context_run_init_script (context, swfdec_initialize,
1871 sizeof (swfdec_initialize), 8);
1873 if (context->state == SWFDEC_AS_CONTEXT_NEW) {
1874 context->state = SWFDEC_AS_CONTEXT_RUNNING;
1875 swfdec_as_object_set_constructor (player->roots->data, player->MovieClip);
1878 player->initialized = TRUE;
1879 g_object_notify (G_OBJECT (player), "initialized");
1880 } else {
1881 /* FIXME: need to kick all other movies out here */
1882 swfdec_player_remove_timeout (player, &player->iterate_timeout);
1885 SWFDEC_INFO ("initializing player to size %ux%u and rate %u/256", width, height, rate);
1886 if (rate != player->rate) {
1887 player->rate = rate;
1888 g_object_notify (G_OBJECT (player), "rate");
1890 if (player->width != width) {
1891 player->width = width;
1892 g_object_notify (G_OBJECT (player), "default-width");
1894 if (player->height != height) {
1895 player->height = height;
1896 g_object_notify (G_OBJECT (player), "default-height");
1898 player->internal_width = player->stage_width >= 0 ? (guint) player->stage_width : player->width;
1899 player->internal_height = player->stage_height >= 0 ? (guint) player->stage_height : player->height;
1900 swfdec_player_update_scale (player);
1902 player->iterate_timeout.timestamp = player->time + SWFDEC_TICKS_PER_SECOND * 256 / player->rate / 10;
1903 swfdec_player_add_timeout (player, &player->iterate_timeout);
1904 SWFDEC_LOG ("initialized iterate timeout %p to %"G_GUINT64_FORMAT" (now %"G_GUINT64_FORMAT")",
1905 &player->iterate_timeout, player->iterate_timeout.timestamp, player->time);
1909 * swfdec_player_get_export_class:
1910 * @player: a #SwfdecPlayer
1911 * @name: garbage-collected string naming the export
1913 * Looks up the constructor for characters that are exported using @name.
1915 * Returns: a #SwfdecAsObject naming the constructor or %NULL if none
1917 SwfdecAsObject *
1918 swfdec_player_get_export_class (SwfdecPlayer *player, const char *name)
1920 SwfdecAsObject *ret;
1922 ret = g_hash_table_lookup (player->registered_classes, name);
1923 if (ret) {
1924 SWFDEC_LOG ("found registered class %p for %s", ret, name);
1925 return ret;
1927 return player->MovieClip;
1931 * swfdec_player_set_export_class:
1932 * @player: a #SwfdecPlayer
1933 * @name: garbage-collected string naming the export
1934 * @object: object to use as constructor or %NULL for none
1936 * Sets the constructor to be used for instances created using the object
1937 * exported with @name.
1939 void
1940 swfdec_player_set_export_class (SwfdecPlayer *player, const char *name, SwfdecAsObject *object)
1942 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1943 g_return_if_fail (name != NULL);
1944 g_return_if_fail (object == NULL || SWFDEC_IS_AS_OBJECT (object));
1946 if (object) {
1947 SWFDEC_LOG ("setting class %p for %s", object, name);
1948 g_hash_table_insert (player->registered_classes, (gpointer) name, object);
1949 } else {
1950 g_hash_table_remove (player->registered_classes, name);
1954 /* FIXME:
1955 * I don't like the idea of rooting arbitrary objects very much. And so far,
1956 * this API is only necessary for the objects used for loading data. So it seems
1957 * like a good idea to revisit the refcounting and GCing of resources.
1959 void
1960 swfdec_player_root_object (SwfdecPlayer *player, GObject *object)
1962 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1963 g_return_if_fail (G_IS_OBJECT (object));
1965 g_object_ref (object);
1966 player->rooted_objects = g_list_prepend (player->rooted_objects, object);
1969 void
1970 swfdec_player_unroot_object (SwfdecPlayer *player, GObject *object)
1972 GList *entry;
1974 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1975 g_return_if_fail (G_IS_OBJECT (object));
1976 entry = g_list_find (player->rooted_objects, object);
1977 g_return_if_fail (entry != NULL);
1978 g_object_unref (object);
1979 player->rooted_objects = g_list_delete_link (player->rooted_objects, entry);
1982 /** PUBLIC API ***/
1985 * swfdec_player_new:
1986 * @debugger: %NULL or a #SwfdecAsDebugger to use for debugging this player.
1988 * Creates a new player. This function is supposed to be used for testing.
1989 * Because of this, the created player will behave as predictable as possible.
1990 * For example, it will generate the same random number sequence every time.
1991 * The function calls swfdec_init () for you if it wasn't called before.
1993 * Returns: The new player
1995 SwfdecPlayer *
1996 swfdec_player_new (SwfdecAsDebugger *debugger)
1998 static const GTimeVal the_beginning = { 1035840244, 0 };
1999 SwfdecPlayer *player;
2001 g_return_val_if_fail (debugger == NULL || SWFDEC_IS_AS_DEBUGGER (debugger), NULL);
2003 swfdec_init ();
2004 player = g_object_new (SWFDEC_TYPE_PLAYER, "random-seed", 0,
2005 "max-runtime", 0,
2006 "debugger", debugger, NULL);
2007 /* FIXME: make this a property or something and don't set it here */
2008 SWFDEC_AS_CONTEXT (player)->start_time = the_beginning;
2010 return player;
2014 * swfdec_player_set_loader:
2015 * @player: a #SwfdecPlayer
2016 * @loader: the loader to use for this player. Takes ownership of the given loader.
2018 * Sets the loader for the main data. This function only works if no loader has
2019 * been set on @player yet.
2020 * For details, see swfdec_player_set_loader_with_variables().
2022 void
2023 swfdec_player_set_loader (SwfdecPlayer *player, SwfdecLoader *loader)
2025 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2026 g_return_if_fail (player->roots == NULL);
2027 g_return_if_fail (SWFDEC_IS_LOADER (loader));
2029 swfdec_player_set_loader_with_variables (player, loader, NULL);
2033 * swfdec_player_set_loader_with_variables:
2034 * @player: a #SwfdecPlayer
2035 * @loader: the loader to use for this player. Takes ownership of the given loader.
2036 * @variables: a string that is checked to be in 'application/x-www-form-urlencoded'
2037 * syntax describing the arguments to set on the new player or NULL for
2038 * none.
2040 * Sets the loader for the main data. This function only works if no loader has
2041 * been set on @player yet.
2042 * If the @variables are set and validate, they will be set as properties on the
2043 * root movie.
2045 void
2046 swfdec_player_set_loader_with_variables (SwfdecPlayer *player, SwfdecLoader *loader,
2047 const char *variables)
2049 SwfdecMovie *movie;
2051 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2052 g_return_if_fail (player->resource == NULL);
2053 g_return_if_fail (SWFDEC_IS_LOADER (loader));
2055 player->resource = swfdec_resource_new (player, loader, variables);
2056 movie = swfdec_movie_new (player, -16384, NULL, player->resource, NULL, SWFDEC_AS_STR__level0);
2057 movie->name = SWFDEC_AS_STR_EMPTY;
2058 g_object_unref (loader);
2062 * swfdec_player_new_from_file:
2063 * @filename: name of the file to play
2065 * Creates a player to play back the given file. If the file does not
2066 * exist or another error occurs, the player will be in an error state and not
2067 * be initialized.
2068 * This function calls swfdec_init () for you if it wasn't called before.
2070 * Returns: a new player
2072 SwfdecPlayer *
2073 swfdec_player_new_from_file (const char *filename)
2075 SwfdecLoader *loader;
2076 SwfdecPlayer *player;
2078 g_return_val_if_fail (filename != NULL, NULL);
2080 loader = swfdec_file_loader_new (filename);
2081 player = swfdec_player_new (NULL);
2082 swfdec_player_set_loader (player, loader);
2084 return player;
2088 * swfdec_init:
2090 * Initializes the Swfdec library.
2092 void
2093 swfdec_init (void)
2095 static gboolean _inited = FALSE;
2096 const char *s;
2098 if (_inited)
2099 return;
2101 _inited = TRUE;
2103 if (!g_thread_supported ())
2104 g_thread_init (NULL);
2105 g_type_init ();
2106 oil_init ();
2108 s = g_getenv ("SWFDEC_DEBUG");
2109 if (s && s[0]) {
2110 char *end;
2111 int level;
2113 level = strtoul (s, &end, 0);
2114 if (end[0] == 0) {
2115 swfdec_debug_set_level (level);
2121 * swfdec_player_handle_mouse:
2122 * @player: a #SwfdecPlayer
2123 * @x: x coordinate of mouse
2124 * @y: y coordinate of mouse
2125 * @button: 1 for pressed, 0 for not pressed
2127 * Updates the current mouse status. If the mouse has left the area of @player,
2128 * you should pass values outside the movie size for @x and @y. You will
2129 * probably want to call swfdec_player_advance() before to update the player to
2130 * the correct time when calling this function.
2132 * Returns: %TRUE if the mouse event was handled. %FALSE to propagate the event
2133 * further. A mouse event may not be handled if the user clicked on a
2134 * translucent area.
2136 gboolean
2137 swfdec_player_handle_mouse (SwfdecPlayer *player,
2138 double x, double y, int button)
2140 gboolean ret;
2142 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
2143 g_return_val_if_fail (button == 0 || button == 1, FALSE);
2145 g_signal_emit (player, signals[HANDLE_MOUSE], 0, x, y, button, &ret);
2147 return ret;
2151 * swfdec_player_key_press:
2152 * @player: a #SwfdecPlayer
2153 * @keycode: the key that was pressed
2154 * @character: UCS4 of the character that was inserted or 0 if none
2156 * Call this function to make the @player react to a key press. Be sure to
2157 * check that keycode transformations are done correctly. For a list of
2158 * keycodes see FIXME.
2160 * Returns: %TRUE if the key press was handled by the @player, %FALSE if it
2161 * should be propagated further
2163 gboolean
2164 swfdec_player_key_press (SwfdecPlayer *player, guint keycode, guint character)
2166 gboolean ret;
2168 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
2169 g_return_val_if_fail (keycode < 256, FALSE);
2171 g_signal_emit (player, signals[HANDLE_KEY], 0, keycode, character, TRUE, &ret);
2173 return ret;
2177 * swfdec_player_key_release:
2178 * @player: a #SwfdecPlayer
2179 * @keycode: the key that was released
2180 * @character: UCS4 of the character that was inserted or 0 if none
2182 * Call this function to make the @player react to a key being released. See
2183 * swfdec_player_key_press() for details.
2185 * Returns: %TRUE if the key press was handled by the @player, %FALSE if it
2186 * should be propagated further
2188 gboolean
2189 swfdec_player_key_release (SwfdecPlayer *player, guint keycode, guint character)
2191 gboolean ret;
2193 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
2194 g_return_val_if_fail (keycode < 256, FALSE);
2196 g_signal_emit (player, signals[HANDLE_KEY], 0, keycode, character, FALSE, &ret);
2198 return ret;
2202 * swfdec_player_render:
2203 * @player: a #SwfdecPlayer
2204 * @cr: #cairo_t to render to
2205 * @x: x coordinate of top left position to render
2206 * @y: y coordinate of top left position to render
2207 * @width: width of area to render or 0 for full width
2208 * @height: height of area to render or 0 for full height
2210 * Renders the given area of the current frame to @cr.
2212 void
2213 swfdec_player_render (SwfdecPlayer *player, cairo_t *cr,
2214 double x, double y, double width, double height)
2216 static const SwfdecColorTransform trans = { 256, 0, 256, 0, 256, 0, 256, 0 };
2217 GList *walk;
2218 SwfdecRect real;
2220 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2221 g_return_if_fail (cr != NULL);
2222 g_return_if_fail (width >= 0.0);
2223 g_return_if_fail (height >= 0.0);
2225 /* FIXME: fail when !initialized? */
2226 if (!swfdec_player_is_initialized (player))
2227 return;
2229 if (width == 0.0)
2230 width = player->stage_width;
2231 if (height == 0.0)
2232 height = player->stage_height;
2233 /* clip the area */
2234 cairo_save (cr);
2235 cairo_rectangle (cr, x, y, width, height);
2236 cairo_clip (cr);
2237 /* compute the rectangle */
2238 x -= player->offset_x;
2239 y -= player->offset_y;
2240 real.x0 = floor (x * SWFDEC_TWIPS_SCALE_FACTOR) / player->scale_x;
2241 real.y0 = floor (y * SWFDEC_TWIPS_SCALE_FACTOR) / player->scale_y;
2242 real.x1 = ceil ((x + width) * SWFDEC_TWIPS_SCALE_FACTOR) / player->scale_x;
2243 real.y1 = ceil ((y + height) * SWFDEC_TWIPS_SCALE_FACTOR) / player->scale_y;
2244 SWFDEC_INFO ("=== %p: START RENDER, area %g %g %g %g ===", player,
2245 real.x0, real.y0, real.x1, real.y1);
2246 /* convert the cairo matrix */
2247 cairo_translate (cr, player->offset_x, player->offset_y);
2248 cairo_scale (cr, player->scale_x / SWFDEC_TWIPS_SCALE_FACTOR, player->scale_y / SWFDEC_TWIPS_SCALE_FACTOR);
2249 swfdec_color_set_source (cr, player->bgcolor);
2250 cairo_paint (cr);
2252 for (walk = player->roots; walk; walk = walk->next) {
2253 swfdec_movie_render (walk->data, cr, &trans, &real);
2255 SWFDEC_INFO ("=== %p: END RENDER ===", player);
2256 cairo_restore (cr);
2260 * swfdec_player_advance:
2261 * @player: the #SwfdecPlayer to advance
2262 * @msecs: number of milliseconds to advance
2264 * Advances @player by @msecs. You should make sure to call this function as
2265 * often as the SwfdecPlayer::next-event property indicates.
2267 void
2268 swfdec_player_advance (SwfdecPlayer *player, gulong msecs)
2270 guint frames;
2271 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2273 frames = SWFDEC_TICKS_TO_SAMPLES (player->time + SWFDEC_MSECS_TO_TICKS (msecs))
2274 - SWFDEC_TICKS_TO_SAMPLES (player->time);
2275 g_signal_emit (player, signals[ADVANCE], 0, msecs, frames);
2279 * swfdec_player_is_initialized:
2280 * @player: a #SwfdecPlayer
2282 * Determines if the @player is initalized yet. An initialized player is able
2283 * to provide basic values like width, height or rate. A player may not be
2284 * initialized if the loader it was started with does not reference a Flash
2285 * resources or it did not provide enough data yet. If a player is initialized,
2286 * it will never be uninitialized again.
2288 * Returns: TRUE if the basic values are known.
2290 gboolean
2291 swfdec_player_is_initialized (SwfdecPlayer *player)
2293 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
2295 return player->initialized;
2299 * swfdec_player_get_next_event:
2300 * @player: ia #SwfdecPlayer
2302 * Queries how long to the next event. This is the next time when you should
2303 * call swfdec_player_advance() to forward to.
2305 * Returns: number of milliseconds until next event or -1 if no outstanding event
2307 glong
2308 swfdec_player_get_next_event (SwfdecPlayer *player)
2310 SwfdecTick tick;
2311 guint ret;
2313 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), 0);
2315 if (swfdec_as_context_is_aborted (SWFDEC_AS_CONTEXT (player)))
2316 return -1;
2318 tick = swfdec_player_get_next_event_time (player);
2319 if (tick == G_MAXUINT64)
2320 return -1;
2321 /* round up to full msecs */
2322 ret = SWFDEC_TICKS_TO_MSECS (tick + SWFDEC_TICKS_PER_SECOND / 1000 - 1);
2324 return ret;
2328 * swfdec_player_get_rate:
2329 * @player: a #SwfdecPlayer
2331 * Queries the framerate of this movie. This number specifies the number
2332 * of frames that are supposed to pass per second. It is a
2333 * multiple of 1/256. It is possible that the movie has no framerate if it does
2334 * not display a Flash movie but an FLV video for example. This does not mean
2335 * it will not change however.
2337 * Returns: The framerate of this movie or 0 if it isn't known yet or the
2338 * movie doesn't have a framerate.
2340 double
2341 swfdec_player_get_rate (SwfdecPlayer *player)
2343 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), 0.0);
2345 return player->rate / 256.0;
2349 * swfdec_player_get_default_size:
2350 * @player: a #SwfdecPlayer
2351 * @width: integer to store the width in or %NULL
2352 * @height: integer to store the height in or %NULL
2354 * If the default size of the movie is initialized, fills in @width and @height
2355 * with the size. Otherwise @width and @height are set to 0.
2357 void
2358 swfdec_player_get_default_size (SwfdecPlayer *player, guint *width, guint *height)
2360 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2362 if (width)
2363 *width = player->width;
2364 if (height)
2365 *height = player->height;
2369 * swfdec_player_get_size:
2370 * @player: a #SwfdecPlayer
2371 * @width: integer to store the width in or %NULL
2372 * @height: integer to store the height in or %NULL
2374 * Gets the currently set image size. If the default width or height should be
2375 * used, the width or height respectively is set to -1.
2377 void
2378 swfdec_player_get_size (SwfdecPlayer *player, int *width, int *height)
2380 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2382 if (width)
2383 *width = player->stage_width;
2384 if (height)
2385 *height = player->stage_height;
2388 static void
2389 swfdec_player_update_size (gpointer playerp, gpointer unused)
2391 SwfdecPlayer *player = playerp;
2392 guint width, height;
2394 /* FIXME: only update if not fullscreen */
2395 width = player->stage_width >=0 ? (guint) player->stage_width : player->width;
2396 height = player->stage_height >=0 ? (guint) player->stage_height : player->height;
2397 /* only broadcast once */
2398 if (width == player->internal_width && height == player->internal_height)
2399 return;
2401 player->internal_width = width;
2402 player->internal_height = height;
2403 if (player->scale_mode == SWFDEC_SCALE_NONE)
2404 swfdec_player_broadcast (player, SWFDEC_AS_STR_Stage, SWFDEC_AS_STR_onResize);
2408 * swfdec_player_set_size:
2409 * @player: a #SwfdecPlayer
2410 * @width: desired width of the movie or -1 for default
2411 * @height: desired height of the movie or -1 for default
2413 * Sets the image size to the given values. The image size is what the area that
2414 * the @player will render and advocate with scripts.
2416 void
2417 swfdec_player_set_size (SwfdecPlayer *player, int width, int height)
2419 gboolean changed = FALSE;
2421 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2422 g_return_if_fail (width >= -1);
2423 g_return_if_fail (height >= -1);
2425 if (player->stage_width != width) {
2426 player->stage_width = width;
2427 g_object_notify (G_OBJECT (player), "width");
2428 changed = TRUE;
2430 if (player->stage_height != height) {
2431 player->stage_height = height;
2432 g_object_notify (G_OBJECT (player), "height");
2433 changed = TRUE;
2435 swfdec_player_update_scale (player);
2436 if (changed)
2437 swfdec_player_add_external_action (player, player, swfdec_player_update_size, NULL);
2441 * swfdec_player_get_audio:
2442 * @player: a #SwfdecPlayer
2444 * Returns a list of all currently active audio streams in @player.
2446 * Returns: A #GList of #SwfdecAudio. You must not modify or free this list.
2448 /* FIXME: I don't like this function */
2449 const GList *
2450 swfdec_player_get_audio (SwfdecPlayer * player)
2452 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
2454 return player->audio;
2458 * swfdec_player_get_background_color:
2459 * @player: a #SwfdecPlayer
2461 * Gets the current background color. The color will be an ARGB-quad, with the
2462 * MSB being the alpha value.
2464 * Returns: the background color as an ARGB value
2466 guint
2467 swfdec_player_get_background_color (SwfdecPlayer *player)
2469 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), SWFDEC_COLOR_COMBINE (0xFF, 0xFF, 0xFF, 0xFF));
2471 return player->bgcolor;
2475 * swfdec_player_set_background_color:
2476 * @player: a #SwfdecPlayer
2477 * @color: new color to use as background color
2479 * Sets a new background color as an ARGB value. To get transparency, set the
2480 * value to 0. To get a black beackground, use 0xFF000000.
2482 void
2483 swfdec_player_set_background_color (SwfdecPlayer *player, guint color)
2485 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2487 player->bgcolor_set = TRUE;
2488 if (player->bgcolor == color)
2489 return;
2490 player->bgcolor = color;
2491 g_object_notify (G_OBJECT (player), "background-color");
2492 if (swfdec_player_is_initialized (player)) {
2493 g_signal_emit (player, signals[INVALIDATE], 0, 0.0, 0.0,
2494 (double) player->width, (double) player->height);
2499 * swfdec_player_get_scale_mode:
2500 * @player: a #SwfdecPlayer
2502 * Gets the currrent mode used for scaling the movie. See #SwfdecScaleMode for
2503 * the different modes.
2505 * Returns: the current scale mode
2507 SwfdecScaleMode
2508 swfdec_player_get_scale_mode (SwfdecPlayer *player)
2510 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), SWFDEC_SCALE_SHOW_ALL);
2512 return player->scale_mode;
2516 * swfdec_player_set_scale_mode:
2517 * @player: a #SwfdecPlayer
2518 * @mode: a #SwfdecScaleMode
2520 * Sets the currrent mode used for scaling the movie. See #SwfdecScaleMode for
2521 * the different modes.
2523 void
2524 swfdec_player_set_scale_mode (SwfdecPlayer *player, SwfdecScaleMode mode)
2526 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2528 if (player->scale_mode != mode) {
2529 player->scale_mode = mode;
2530 swfdec_player_update_scale (player);
2531 g_object_notify (G_OBJECT (player), "scale-mode");
2536 * swfdec_player_get_alignment:
2537 * @player: a #SwfdecPlayer
2539 * Gets the alignment of the player. The alignment describes what point is used
2540 * as the anchor for drawing the contents. See #SwfdecAlignment for possible
2541 * values.
2543 * Returns: the current alignment
2545 SwfdecAlignment
2546 swfdec_player_get_alignment (SwfdecPlayer *player)
2548 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), SWFDEC_ALIGNMENT_CENTER);
2550 return swfdec_player_alignment_from_flags (player->align_flags);
2554 * swfdec_player_set_alignment:
2555 * @player: a #SwfdecPlayer
2556 * @align: #SwfdecAlignment to set
2558 * Sets the alignment to @align. For details about alignment, see
2559 * swfdec_player_get_alignment() and #SwfdecAlignment.
2561 void
2562 swfdec_player_set_alignment (SwfdecPlayer *player, SwfdecAlignment align)
2564 guint flags;
2566 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2568 flags = swfdec_player_alignment_to_flags (align);
2569 swfdec_player_set_align_flags (player, flags);
2572 void
2573 swfdec_player_set_align_flags (SwfdecPlayer *player, guint flags)
2575 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2577 if (flags != player->align_flags) {
2578 player->align_flags = flags;
2579 swfdec_player_update_scale (player);
2580 g_object_notify (G_OBJECT (player), "alignment");
2585 * swfdec_player_get_maximum_runtime:
2586 * @player: a #SwfdecPlayer
2588 * Queries the given @player for how long scripts may run. see
2589 * swfdec_player_set_maximum_runtime() for a longer discussion of this value.
2591 * Returns: the maximum time in milliseconds that scripts are allowed to run or
2592 * 0 for infinite.
2594 gulong
2595 swfdec_player_get_maximum_runtime (SwfdecPlayer *player)
2597 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), 0);
2599 return player->max_runtime;
2603 * swfdec_player_set_maximum_runtime:
2604 * @player: a #SwfdecPlayer
2605 * @msecs: time in milliseconds that scripts are allowed to run or 0 for
2606 * infinite
2608 * Sets the time that the player may use to let internal scripts run. If the
2609 * Flash file that is currently played back does not manage to complete its
2610 * scripts in the given time, it is aborted. You cannot continue the scripts at
2611 * a later point in time. However, your application may become unresponsive and
2612 * your users annoyed if they cannot interact with it for too long. To give a
2613 * reference point, the Adobe Flash player usually sets this value to 10
2614 * seconds. Note that this time determines the maximum time calling
2615 * swfdec_player_advance() may take, even if it is called with a large value.
2616 * Also note that this setting is ignored when running inside a debugger.
2618 void
2619 swfdec_player_set_maximum_runtime (SwfdecPlayer *player, gulong msecs)
2621 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2623 player->max_runtime = msecs;
2624 g_object_notify (G_OBJECT (player), "max-runtime");