make the g_print a debug message
[swfdec.git] / libswfdec / swfdec_player.c
blob57f0de4392227b8770264653c7c043d87d354e5b
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); 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 swfdec_player_compress_actions (player->actions[importance]);
371 action = swfdec_ring_buffer_push (player->actions[importance]);
372 if (action == NULL) {
373 if (swfdec_ring_buffer_get_size (player->actions[importance]) == 256) {
374 SWFDEC_WARNING ("256 levels of recursion were exceeded in one action list.");
376 swfdec_ring_buffer_set_size (player->actions[importance],
377 swfdec_ring_buffer_get_size (player->actions[importance]) + 16);
378 action = swfdec_ring_buffer_push (player->actions[importance]);
379 g_assert (action);
382 *action = *act;
386 * swfdec_player_add_event:
387 * @player: a #SwfdecPlayer
388 * @movie: the movie on which to trigger the event
389 * @type: type of the event
390 * @importance: importance of the event
392 * Adds an action to the @player. Actions are used by Flash player to solve
393 * reentrancy issues. Instead of calling back into the Actionscript engine,
394 * an action is queued for later execution. So if you're writing code that
395 * is calling Actionscript code, you want to do this by using actions.
397 void
398 swfdec_player_add_action (SwfdecPlayer *player, SwfdecMovie *movie, SwfdecEventType type,
399 guint importance)
401 SwfdecPlayerAction action = { movie, NULL, type };
403 g_return_if_fail (SWFDEC_IS_PLAYER (player));
404 g_return_if_fail (SWFDEC_IS_MOVIE (movie));
405 g_return_if_fail (importance < SWFDEC_PLAYER_N_ACTION_QUEUES);
407 SWFDEC_LOG ("adding action %s %u", movie->name, type);
408 swfdec_player_do_add_action (player, importance, &action);
411 void
412 swfdec_player_add_action_script (SwfdecPlayer *player, SwfdecMovie *movie,
413 SwfdecScript *script, guint importance)
415 SwfdecPlayerAction action = { movie, script, 0 };
417 g_return_if_fail (SWFDEC_IS_PLAYER (player));
418 g_return_if_fail (SWFDEC_IS_MOVIE (movie));
419 g_return_if_fail (script != NULL);
420 g_return_if_fail (importance < SWFDEC_PLAYER_N_ACTION_QUEUES);
422 SWFDEC_LOG ("adding action script %s %s", movie->name, script->name);
423 swfdec_player_do_add_action (player, importance, &action);
427 * swfdec_player_remove_all_actions:
428 * @player: a #SwfdecPlayer
429 * @movie: movie pointer identifying the actions to be removed
431 * Removes all actions associated with @movie that have not yet been executed.
432 * See swfdec_player_add_action() for details about actions.
434 void
435 swfdec_player_remove_all_actions (SwfdecPlayer *player, SwfdecMovie *movie)
437 SwfdecPlayerAction *action;
438 guint i, j;
440 g_return_if_fail (SWFDEC_IS_PLAYER (player));
441 g_return_if_fail (SWFDEC_IS_MOVIE (movie));
443 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
444 for (j = 0; j < swfdec_ring_buffer_get_n_elements (player->actions[i]); j++) {
445 action = swfdec_ring_buffer_peek_nth (player->actions[i], j);
447 if (action->movie == movie) {
448 SWFDEC_LOG ("removing action %p %u",
449 action->movie, action->event);
450 action->movie = NULL;
456 static gboolean
457 swfdec_player_do_action (SwfdecPlayer *player)
459 SwfdecPlayerAction *action;
460 guint i;
462 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
463 do {
464 action = swfdec_ring_buffer_pop (player->actions[i]);
465 if (action == NULL)
466 break;
467 } while (action->movie == NULL); /* skip removed actions */
468 if (action) {
469 if (action->script) {
470 swfdec_as_object_run_with_security (SWFDEC_AS_OBJECT (action->movie),
471 action->script, SWFDEC_SECURITY (action->movie->resource));
472 } else {
473 swfdec_movie_execute (action->movie, action->event);
475 return TRUE;
479 return FALSE;
482 static void
483 swfdec_player_perform_external_actions (SwfdecPlayer *player)
485 SwfdecPlayerExternalAction *action;
486 guint i;
488 /* remove timeout if it exists - do this before executing stuff below */
489 if (player->external_timeout.callback) {
490 swfdec_player_remove_timeout (player, &player->external_timeout);
491 player->external_timeout.callback = NULL;
494 /* we need to query the number of current actions so newly added ones aren't
495 * executed in here */
496 for (i = swfdec_ring_buffer_get_n_elements (player->external_actions); i > 0; i--) {
497 action = swfdec_ring_buffer_pop (player->external_actions);
498 g_assert (action != NULL);
499 /* skip removed actions */
500 if (action->object == NULL)
501 continue;
502 action->func (action->object, action->data);
503 swfdec_player_perform_actions (player);
507 static void
508 swfdec_player_trigger_external_actions (SwfdecTimeout *advance)
510 SwfdecPlayer *player = SWFDEC_PLAYER ((guint8 *) advance - G_STRUCT_OFFSET (SwfdecPlayer, external_timeout));
512 player->external_timeout.callback = NULL;
513 swfdec_player_perform_external_actions (player);
516 void
517 swfdec_player_add_external_action (SwfdecPlayer *player, gpointer object,
518 SwfdecActionFunc action_func, gpointer action_data)
520 SwfdecPlayerExternalAction *action;
522 g_return_if_fail (SWFDEC_IS_PLAYER (player));
523 g_return_if_fail (object != NULL);
524 g_return_if_fail (action_func != NULL);
526 SWFDEC_LOG ("adding external action %p %p %p", object, action_func, action_data);
527 action = swfdec_ring_buffer_push (player->external_actions);
528 if (action == NULL) {
529 /* FIXME: limit number of actions to not get inf loops due to scripts? */
530 swfdec_ring_buffer_set_size (player->external_actions,
531 swfdec_ring_buffer_get_size (player->external_actions) + 16);
532 action = swfdec_ring_buffer_push (player->external_actions);
533 g_assert (action);
535 action->object = object;
536 action->func = action_func;
537 action->data = action_data;
538 if (!player->external_timeout.callback) {
539 /* trigger execution immediately.
540 * But if initialized, keep at least 100ms from when the last external
541 * timeout triggered. This is a crude method to get around infinite loops
542 * when script actions executed by external actions trigger another external
543 * action that would execute instantly.
545 if (player->initialized) {
546 player->external_timeout.timestamp = MAX (player->time,
547 player->external_timeout.timestamp + SWFDEC_MSECS_TO_TICKS (100));
548 } else {
549 player->external_timeout.timestamp = player->time;
551 player->external_timeout.callback = swfdec_player_trigger_external_actions;
552 swfdec_player_add_timeout (player, &player->external_timeout);
556 void
557 swfdec_player_remove_all_external_actions (SwfdecPlayer *player, gpointer object)
559 SwfdecPlayerExternalAction *action;
560 guint i;
562 g_return_if_fail (SWFDEC_IS_PLAYER (player));
563 g_return_if_fail (object != NULL);
565 for (i = 0; i < swfdec_ring_buffer_get_n_elements (player->external_actions); i++) {
566 action = swfdec_ring_buffer_peek_nth (player->external_actions, i);
568 if (action->object == object) {
569 SWFDEC_LOG ("removing external action %p %p %p",
570 action->object, action->func, action->data);
571 action->object = NULL;
576 /*** SwfdecPlayer ***/
578 enum {
579 INVALIDATE,
580 ADVANCE,
581 HANDLE_KEY,
582 HANDLE_MOUSE,
583 AUDIO_ADDED,
584 AUDIO_REMOVED,
585 LAUNCH,
586 FSCOMMAND,
587 LAST_SIGNAL
590 static guint signals[LAST_SIGNAL] = { 0, };
592 enum {
593 PROP_0,
594 PROP_CACHE_SIZE,
595 PROP_INITIALIZED,
596 PROP_DEFAULT_WIDTH,
597 PROP_DEFAULT_HEIGHT,
598 PROP_RATE,
599 PROP_MOUSE_CURSOR,
600 PROP_NEXT_EVENT,
601 PROP_BACKGROUND_COLOR,
602 PROP_WIDTH,
603 PROP_HEIGHT,
604 PROP_ALIGNMENT,
605 PROP_SCALE,
606 PROP_SYSTEM,
607 PROP_MAX_RUNTIME
610 G_DEFINE_TYPE (SwfdecPlayer, swfdec_player, SWFDEC_TYPE_AS_CONTEXT)
612 void
613 swfdec_player_remove_movie (SwfdecPlayer *player, SwfdecMovie *movie)
615 swfdec_movie_remove (movie);
616 player->movies = g_list_remove (player->movies, movie);
619 static guint
620 swfdec_player_alignment_to_flags (SwfdecAlignment alignment)
622 static const guint align_flags[9] = {
623 SWFDEC_ALIGN_FLAG_TOP | SWFDEC_ALIGN_FLAG_LEFT,
624 SWFDEC_ALIGN_FLAG_TOP,
625 SWFDEC_ALIGN_FLAG_TOP | SWFDEC_ALIGN_FLAG_RIGHT,
626 SWFDEC_ALIGN_FLAG_LEFT,
628 SWFDEC_ALIGN_FLAG_RIGHT,
629 SWFDEC_ALIGN_FLAG_BOTTOM | SWFDEC_ALIGN_FLAG_LEFT,
630 SWFDEC_ALIGN_FLAG_BOTTOM,
631 SWFDEC_ALIGN_FLAG_BOTTOM | SWFDEC_ALIGN_FLAG_RIGHT
633 return align_flags[alignment];
636 static SwfdecAlignment
637 swfdec_player_alignment_from_flags (guint flags)
639 if (flags & SWFDEC_ALIGN_FLAG_TOP) {
640 if (flags & SWFDEC_ALIGN_FLAG_LEFT) {
641 return SWFDEC_ALIGNMENT_TOP_LEFT;
642 } else if (flags & SWFDEC_ALIGN_FLAG_RIGHT) {
643 return SWFDEC_ALIGNMENT_TOP_RIGHT;
644 } else {
645 return SWFDEC_ALIGNMENT_TOP;
647 } else if (flags & SWFDEC_ALIGN_FLAG_BOTTOM) {
648 if (flags & SWFDEC_ALIGN_FLAG_LEFT) {
649 return SWFDEC_ALIGNMENT_BOTTOM_LEFT;
650 } else if (flags & SWFDEC_ALIGN_FLAG_RIGHT) {
651 return SWFDEC_ALIGNMENT_BOTTOM_RIGHT;
652 } else {
653 return SWFDEC_ALIGNMENT_BOTTOM;
655 } else {
656 if (flags & SWFDEC_ALIGN_FLAG_LEFT) {
657 return SWFDEC_ALIGNMENT_LEFT;
658 } else if (flags & SWFDEC_ALIGN_FLAG_RIGHT) {
659 return SWFDEC_ALIGNMENT_RIGHT;
660 } else {
661 return SWFDEC_ALIGNMENT_CENTER;
666 static void
667 swfdec_player_get_property (GObject *object, guint param_id, GValue *value,
668 GParamSpec * pspec)
670 SwfdecPlayer *player = SWFDEC_PLAYER (object);
672 switch (param_id) {
673 case PROP_BACKGROUND_COLOR:
674 g_value_set_uint (value, swfdec_player_get_background_color (player));
675 break;
676 case PROP_CACHE_SIZE:
677 g_value_set_uint (value, player->cache->max_size);
678 break;
679 case PROP_INITIALIZED:
680 g_value_set_boolean (value, swfdec_player_is_initialized (player));
681 break;
682 case PROP_DEFAULT_WIDTH:
683 g_value_set_uint (value, player->width);
684 break;
685 case PROP_DEFAULT_HEIGHT:
686 g_value_set_uint (value, player->height);
687 break;
688 case PROP_RATE:
689 g_value_set_double (value, player->rate / 256.0);
690 break;
691 case PROP_MOUSE_CURSOR:
692 g_value_set_enum (value, player->mouse_cursor);
693 break;
694 case PROP_NEXT_EVENT:
695 g_value_set_uint (value, swfdec_player_get_next_event (player));
696 break;
697 case PROP_WIDTH:
698 g_value_set_int (value, player->stage_width);
699 break;
700 case PROP_HEIGHT:
701 g_value_set_int (value, player->stage_height);
702 break;
703 case PROP_ALIGNMENT:
704 g_value_set_enum (value, swfdec_player_alignment_from_flags (player->align_flags));
705 break;
706 case PROP_SCALE:
707 g_value_set_enum (value, player->scale_mode);
708 break;
709 case PROP_SYSTEM:
710 g_value_set_object (value, player->system);
711 break;
712 case PROP_MAX_RUNTIME:
713 g_value_set_ulong (value, player->max_runtime);
714 break;
715 default:
716 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
717 break;
721 static void
722 swfdec_player_update_scale (SwfdecPlayer *player)
724 int width, height;
725 double scale_x, scale_y;
727 player->stage.width = player->stage_width >= 0 ? player->stage_width : (int) player->width;
728 player->stage.height = player->stage_height >= 0 ? player->stage_height : (int) player->height;
729 if (player->stage.height == 0 || player->stage.width == 0) {
730 player->scale_x = 1.0;
731 player->scale_y = 1.0;
732 player->offset_x = 0;
733 player->offset_y = 0;
734 return;
736 if (player->width == 0 || player->height == 0) {
737 scale_x = 1.0;
738 scale_y = 1.0;
739 } else {
740 scale_x = (double) player->stage.width / player->width;
741 scale_y = (double) player->stage.height / player->height;
743 switch (player->scale_mode) {
744 case SWFDEC_SCALE_SHOW_ALL:
745 player->scale_x = MIN (scale_x, scale_y);
746 player->scale_y = player->scale_x;
747 break;
748 case SWFDEC_SCALE_NO_BORDER:
749 player->scale_x = MAX (scale_x, scale_y);
750 player->scale_y = player->scale_x;
751 break;
752 case SWFDEC_SCALE_EXACT_FIT:
753 player->scale_x = scale_x;
754 player->scale_y = scale_y;
755 break;
756 case SWFDEC_SCALE_NONE:
757 player->scale_x = 1.0;
758 player->scale_y = 1.0;
759 break;
760 default:
761 g_assert_not_reached ();
763 width = player->stage.width - ceil (player->width * player->scale_x);
764 height = player->stage.height - ceil (player->height * player->scale_y);
765 if (player->align_flags & SWFDEC_ALIGN_FLAG_LEFT) {
766 player->offset_x = 0;
767 } else if (player->align_flags & SWFDEC_ALIGN_FLAG_RIGHT) {
768 player->offset_x = width;
769 } else {
770 player->offset_x = width / 2;
772 if (player->align_flags & SWFDEC_ALIGN_FLAG_TOP) {
773 player->offset_y = 0;
774 } else if (player->align_flags & SWFDEC_ALIGN_FLAG_BOTTOM) {
775 player->offset_y = height;
776 } else {
777 player->offset_y = height / 2;
779 SWFDEC_LOG ("coordinate translation is %g * x + %d - %g * y + %d",
780 player->scale_x, player->offset_x, player->scale_y, player->offset_y);
781 #if 0
782 /* FIXME: make this emit the signal at the right time */
783 player->invalid.x0 = 0;
784 player->invalid.y0 = 0;
785 player->invalid.x1 = player->stage_width;
786 player->invalid.y1 = player->stage_height;
787 #endif
790 static void
791 swfdec_player_set_property (GObject *object, guint param_id, const GValue *value,
792 GParamSpec *pspec)
794 SwfdecPlayer *player = SWFDEC_PLAYER (object);
796 switch (param_id) {
797 case PROP_BACKGROUND_COLOR:
798 swfdec_player_set_background_color (player, g_value_get_uint (value));
799 break;
800 case PROP_CACHE_SIZE:
801 player->cache->max_size = g_value_get_uint (value);
802 break;
803 case PROP_WIDTH:
804 swfdec_player_set_size (player, g_value_get_int (value), player->stage_height);
805 break;
806 case PROP_HEIGHT:
807 swfdec_player_set_size (player, player->stage_width, g_value_get_int (value));
808 break;
809 case PROP_ALIGNMENT:
810 player->align_flags = swfdec_player_alignment_to_flags (g_value_get_enum (value));
811 swfdec_player_update_scale (player);
812 break;
813 case PROP_SCALE:
814 swfdec_player_set_scale_mode (player, g_value_get_enum (value));
815 break;
816 case PROP_SYSTEM:
817 g_object_unref (player->system);
818 if (g_value_get_object (value)) {
819 player->system = SWFDEC_SYSTEM (g_value_dup_object (value));
820 } else {
821 player->system = swfdec_system_new ();
823 break;
824 case PROP_MAX_RUNTIME:
825 swfdec_player_set_maximum_runtime (player, g_value_get_ulong (value));
826 break;
827 default:
828 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
829 break;
833 static void
834 swfdec_player_dispose (GObject *object)
836 SwfdecPlayer *player = SWFDEC_PLAYER (object);
837 guint i;
839 swfdec_player_stop_all_sounds (player);
840 swfdec_player_resource_request_finish (player);
841 g_hash_table_destroy (player->registered_classes);
843 while (player->roots)
844 swfdec_movie_destroy (player->roots->data);
845 if (player->resource) {
846 g_object_unref (player->resource);
847 player->resource = NULL;
849 while (player->rooted_objects)
850 swfdec_player_unroot_object (player, player->rooted_objects->data);
852 /* we do this here so references to GC'd objects get freed */
853 G_OBJECT_CLASS (swfdec_player_parent_class)->dispose (object);
855 swfdec_player_remove_all_external_actions (player, player);
856 #ifndef G_DISABLE_ASSERT
858 SwfdecPlayerExternalAction *action;
859 while ((action = swfdec_ring_buffer_pop (player->external_actions)) != NULL) {
860 g_assert (action->object == NULL); /* skip removed actions */
864 SwfdecPlayerAction *action;
865 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
866 while ((action = swfdec_ring_buffer_pop (player->actions[i])) != NULL) {
867 g_assert (action->movie == NULL); /* skip removed actions */
871 #endif
872 swfdec_ring_buffer_free (player->external_actions);
873 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
874 swfdec_ring_buffer_free (player->actions[i]);
876 g_assert (player->movies == NULL);
877 g_assert (player->audio == NULL);
878 if (player->external_timeout.callback)
879 swfdec_player_remove_timeout (player, &player->external_timeout);
880 if (player->rate) {
881 swfdec_player_remove_timeout (player, &player->iterate_timeout);
883 g_assert (player->timeouts == NULL);
884 g_list_free (player->intervals);
885 player->intervals = NULL;
886 swfdec_cache_unref (player->cache);
887 if (player->system) {
888 g_object_unref (player->system);
889 player->system = NULL;
891 g_array_free (player->invalidations, TRUE);
892 player->invalidations = NULL;
893 if (player->runtime) {
894 g_timer_destroy (player->runtime);
895 player->runtime = NULL;
899 static void
900 swfdec_player_broadcast (SwfdecPlayer *player, const char *object_name, const char *signal)
902 SwfdecAsValue val;
903 SwfdecAsObject *obj;
905 SWFDEC_DEBUG ("broadcasting message %s.%s", object_name, signal);
906 obj = SWFDEC_AS_CONTEXT (player)->global;
907 swfdec_as_object_get_variable (obj, object_name, &val);
908 if (!SWFDEC_AS_VALUE_IS_OBJECT (&val))
909 return;
910 obj = SWFDEC_AS_VALUE_GET_OBJECT (&val);
911 SWFDEC_AS_VALUE_SET_STRING (&val, signal);
912 swfdec_as_object_call (obj, SWFDEC_AS_STR_broadcastMessage, 1, &val, NULL);
915 static void
916 swfdec_player_update_mouse_cursor (SwfdecPlayer *player)
918 SwfdecMouseCursor new = SWFDEC_MOUSE_CURSOR_NORMAL;
920 if (!player->mouse_visible) {
921 new = SWFDEC_MOUSE_CURSOR_NONE;
922 } else if (player->mouse_grab != NULL) {
923 /* FIXME: this needs to be more sophisticated, since SwfdecEditText may
924 * want to have different mouse cursors depending on location (it supports
925 * links in theory)
927 if (SWFDEC_IS_BUTTON_MOVIE (player->mouse_grab))
928 new = SWFDEC_MOUSE_CURSOR_CLICK;
931 if (new != player->mouse_cursor) {
932 player->mouse_cursor = new;
933 g_object_notify (G_OBJECT (player), "mouse-cursor");
937 static void
938 swfdec_player_update_drag_movie (SwfdecPlayer *player)
940 double x, y;
941 SwfdecMovie *movie;
943 if (player->mouse_drag == NULL)
944 return;
946 movie = player->mouse_drag;
947 g_assert (movie->cache_state == SWFDEC_MOVIE_UP_TO_DATE);
948 x = player->mouse_x;
949 y = player->mouse_y;
950 swfdec_player_stage_to_global (player, &x, &y);
951 if (movie->parent)
952 swfdec_movie_global_to_local (movie->parent, &x, &y);
953 if (player->mouse_drag_center) {
954 x -= (movie->extents.x1 - movie->extents.x0) / 2;
955 y -= (movie->extents.y1 - movie->extents.y0) / 2;
956 } else {
957 x -= player->mouse_drag_x;
958 y -= player->mouse_drag_y;
960 x = CLAMP (x, player->mouse_drag_rect.x0, player->mouse_drag_rect.x1);
961 y = CLAMP (y, player->mouse_drag_rect.y0, player->mouse_drag_rect.y1);
962 SWFDEC_LOG ("mouse is at %g %g, originally (%g %g)", x, y, player->mouse_x, player->mouse_y);
963 if (x != movie->matrix.x0 || y != movie->matrix.y0) {
964 movie->matrix.x0 = x;
965 movie->matrix.y0 = y;
966 swfdec_movie_queue_update (movie, SWFDEC_MOVIE_INVALID_MATRIX);
971 * swfdec_player_set_drag_movie:
972 * @player: a #SwfdecPlayer
973 * @drag: the movie to be dragged by the mouse or NULL to unset
974 * @center: TRUE if the center of @drag should be at the mouse pointer, FALSE if (0,0)
975 * of @drag should be at the mouse pointer.
976 * @rect: NULL or the rectangle that clips the mouse position. The coordinates
977 * are in the coordinate system of the parent of @drag.
979 * Sets or unsets the movie that is dragged by the mouse.
981 void
982 swfdec_player_set_drag_movie (SwfdecPlayer *player, SwfdecMovie *drag, gboolean center,
983 SwfdecRect *rect)
985 g_return_if_fail (SWFDEC_IS_PLAYER (player));
986 g_return_if_fail (drag == NULL || SWFDEC_IS_MOVIE (drag));
988 /* FIXME: need to do anything with old drag? */
989 player->mouse_drag = drag;
990 player->mouse_drag_center = center;
991 if (drag && !center) {
992 player->mouse_drag_x = player->mouse_x;
993 player->mouse_drag_y = player->mouse_y;
994 swfdec_player_stage_to_global (player, &player->mouse_drag_x, &player->mouse_drag_y);
995 if (drag->parent)
996 swfdec_movie_global_to_local (drag->parent, &player->mouse_drag_x, &player->mouse_drag_y);
997 player->mouse_drag_x -= drag->matrix.x0;
998 player->mouse_drag_y -= drag->matrix.y0;
1000 if (rect) {
1001 player->mouse_drag_rect = *rect;
1002 } else {
1003 player->mouse_drag_rect.x0 = -G_MAXDOUBLE;
1004 player->mouse_drag_rect.y0 = -G_MAXDOUBLE;
1005 player->mouse_drag_rect.x1 = G_MAXDOUBLE;
1006 player->mouse_drag_rect.y1 = G_MAXDOUBLE;
1008 SWFDEC_DEBUG ("starting drag in %g %g %g %g",
1009 player->mouse_drag_rect.x0, player->mouse_drag_rect.y0,
1010 player->mouse_drag_rect.x1, player->mouse_drag_rect.y1);
1011 /* FIXME: need a way to make sure we get updated */
1012 if (drag) {
1013 swfdec_movie_update (drag);
1014 drag->modified = TRUE;
1015 swfdec_player_update_drag_movie (player);
1019 static void
1020 swfdec_player_update_mouse_position (SwfdecPlayer *player)
1022 GList *walk;
1023 SwfdecMovie *mouse_grab = NULL;
1025 if (player->mouse_button) {
1026 mouse_grab = player->mouse_grab;
1027 } else {
1028 double x, y;
1029 /* if the mouse button is pressed the grab widget stays the same (I think) */
1030 x = player->mouse_x;
1031 y = player->mouse_y;
1032 swfdec_player_stage_to_global (player, &x, &y);
1033 for (walk = g_list_last (player->roots); walk; walk = walk->prev) {
1034 mouse_grab = swfdec_movie_get_movie_at (walk->data, x, y);
1035 if (mouse_grab)
1036 break;
1039 SWFDEC_DEBUG ("%s %p has mouse at %g %g",
1040 mouse_grab ? G_OBJECT_TYPE_NAME (mouse_grab) : "---",
1041 mouse_grab, player->mouse_x, player->mouse_y);
1042 if (player->mouse_grab && mouse_grab != player->mouse_grab)
1043 swfdec_movie_send_mouse_change (player->mouse_grab, TRUE);
1044 player->mouse_grab = mouse_grab;
1045 if (mouse_grab)
1046 swfdec_movie_send_mouse_change (mouse_grab, FALSE);
1049 static void
1050 swfdec_player_do_mouse_move (SwfdecPlayer *player)
1052 GList *walk;
1054 swfdec_player_update_drag_movie (player);
1055 for (walk = player->movies; walk; walk = walk->next) {
1056 swfdec_movie_queue_script (walk->data, SWFDEC_EVENT_MOUSE_MOVE);
1058 swfdec_player_broadcast (player, SWFDEC_AS_STR_Mouse, SWFDEC_AS_STR_onMouseMove);
1059 swfdec_player_update_mouse_position (player);
1062 static void
1063 swfdec_player_do_mouse_button (SwfdecPlayer *player)
1065 GList *walk;
1066 guint event;
1067 const char *event_name;
1069 if (player->mouse_button) {
1070 event = SWFDEC_EVENT_MOUSE_DOWN;
1071 event_name = SWFDEC_AS_STR_onMouseDown;
1072 } else {
1073 event = SWFDEC_EVENT_MOUSE_UP;
1074 event_name = SWFDEC_AS_STR_onMouseUp;
1076 for (walk = player->movies; walk; walk = walk->next) {
1077 swfdec_movie_queue_script (walk->data, event);
1079 swfdec_player_broadcast (player, SWFDEC_AS_STR_Mouse, event_name);
1080 if (player->mouse_grab)
1081 swfdec_movie_send_mouse_change (player->mouse_grab, FALSE);
1084 static void
1085 swfdec_player_emit_signals (SwfdecPlayer *player)
1087 GList *walk;
1089 /* emit invalidate signal */
1090 if (!swfdec_rectangle_is_empty (&player->invalid_extents)) {
1091 g_signal_emit (player, signals[INVALIDATE], 0, &player->invalid_extents,
1092 player->invalidations->data, player->invalidations->len);
1093 swfdec_rectangle_init_empty (&player->invalid_extents);
1094 g_array_set_size (player->invalidations, 0);
1097 /* emit audio-added for all added audio streams */
1098 for (walk = player->audio; walk; walk = walk->next) {
1099 SwfdecAudio *audio = walk->data;
1101 if (audio->added)
1102 continue;
1103 g_signal_emit (player, signals[AUDIO_ADDED], 0, audio);
1104 audio->added = TRUE;
1108 static gboolean
1109 swfdec_player_do_handle_key (SwfdecPlayer *player, guint keycode, guint character, gboolean down)
1111 g_assert (keycode < 256);
1113 if (!swfdec_player_lock (player))
1114 return FALSE;
1115 /* set the correct variables */
1116 player->last_keycode = keycode;
1117 player->last_character = character;
1118 if (down) {
1119 player->key_pressed[keycode / 8] |= 1 << keycode % 8;
1120 } else {
1121 player->key_pressed[keycode / 8] &= ~(1 << keycode % 8);
1123 swfdec_player_broadcast (player, SWFDEC_AS_STR_Key, down ? SWFDEC_AS_STR_onKeyDown : SWFDEC_AS_STR_onKeyUp);
1124 swfdec_player_perform_actions (player);
1125 swfdec_player_unlock (player);
1127 return TRUE;
1130 static gboolean
1131 swfdec_player_do_handle_mouse (SwfdecPlayer *player,
1132 double x, double y, int button)
1134 if (!swfdec_player_lock (player))
1135 return FALSE;
1137 SWFDEC_LOG ("handling mouse at %g %g %d", x, y, button);
1138 if (player->mouse_x != x || player->mouse_y != y) {
1139 player->mouse_x = x;
1140 player->mouse_y = y;
1141 swfdec_player_do_mouse_move (player);
1143 if (player->mouse_button != button) {
1144 player->mouse_button = button;
1145 swfdec_player_do_mouse_button (player);
1147 swfdec_player_perform_actions (player);
1148 swfdec_player_unlock (player);
1150 /* FIXME: allow events to pass through */
1151 return TRUE;
1154 void
1155 swfdec_player_global_to_stage (SwfdecPlayer *player, double *x, double *y)
1157 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1158 g_return_if_fail (x != NULL);
1159 g_return_if_fail (y != NULL);
1161 *x = *x / SWFDEC_TWIPS_SCALE_FACTOR * player->scale_x + player->offset_x;
1162 *y = *y / SWFDEC_TWIPS_SCALE_FACTOR * player->scale_y + player->offset_y;
1165 void
1166 swfdec_player_stage_to_global (SwfdecPlayer *player, double *x, double *y)
1168 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1169 g_return_if_fail (x != NULL);
1170 g_return_if_fail (y != NULL);
1172 *x = (*x - player->offset_x) / player->scale_x * SWFDEC_TWIPS_SCALE_FACTOR;
1173 *y = (*y - player->offset_y) / player->scale_y * SWFDEC_TWIPS_SCALE_FACTOR;
1176 static void
1177 swfdec_player_execute_on_load_init (SwfdecPlayer *player)
1179 GList *walk;
1181 /* FIXME: This can be made a LOT faster with correct caching, but I'm lazy */
1182 do {
1183 for (walk = player->movies; walk; walk = walk->next) {
1184 SwfdecMovie *movie = walk->data;
1185 SwfdecResource *resource = swfdec_movie_get_own_resource (movie);
1186 if (resource == NULL)
1187 continue;
1188 if (swfdec_resource_emit_on_load_init (resource))
1189 break;
1191 } while (walk != NULL);
1194 static void
1195 swfdec_player_iterate (SwfdecTimeout *timeout)
1197 SwfdecPlayer *player = SWFDEC_PLAYER ((guint8 *) timeout - G_STRUCT_OFFSET (SwfdecPlayer, iterate_timeout));
1198 GList *walk;
1200 /* add timeout again - do this first because later code can change it */
1201 /* FIXME: rounding issues? */
1202 player->iterate_timeout.timestamp += SWFDEC_TICKS_PER_SECOND * 256 / player->rate;
1203 swfdec_player_add_timeout (player, &player->iterate_timeout);
1204 swfdec_player_perform_external_actions (player);
1205 SWFDEC_INFO ("=== START ITERATION ===");
1206 /* start the iteration. This performs a goto next frame on all
1207 * movies that are not stopped. It also queues onEnterFrame.
1209 for (walk = player->movies; walk; walk = walk->next) {
1210 SwfdecMovieClass *klass = SWFDEC_MOVIE_GET_CLASS (walk->data);
1211 if (klass->iterate_start)
1212 klass->iterate_start (walk->data);
1214 swfdec_player_perform_actions (player);
1215 SWFDEC_INFO ("=== STOP ITERATION ===");
1216 /* this loop allows removal of walk->data */
1217 walk = player->movies;
1218 while (walk) {
1219 SwfdecMovie *cur = walk->data;
1220 SwfdecMovieClass *klass = SWFDEC_MOVIE_GET_CLASS (cur);
1221 walk = walk->next;
1222 g_assert (klass->iterate_end);
1223 if (!klass->iterate_end (cur))
1224 swfdec_movie_destroy (cur);
1226 swfdec_player_execute_on_load_init (player);
1227 swfdec_player_resource_request_perform (player);
1228 swfdec_player_perform_actions (player);
1231 static void
1232 swfdec_player_advance_audio (SwfdecPlayer *player, guint samples)
1234 SwfdecAudio *audio;
1235 GList *walk;
1237 if (samples == 0)
1238 return;
1240 /* don't use for loop here, because we need to advance walk before
1241 * removing the audio */
1242 walk = player->audio;
1243 while (walk) {
1244 audio = walk->data;
1245 walk = walk->next;
1246 if (swfdec_audio_iterate (audio, samples) == 0)
1247 swfdec_audio_remove (audio);
1251 static void
1252 swfdec_player_do_advance (SwfdecPlayer *player, gulong msecs, guint audio_samples)
1254 SwfdecTimeout *timeout;
1255 SwfdecTick target_time;
1256 guint frames_now;
1258 if (!swfdec_player_lock (player))
1259 return;
1261 target_time = player->time + SWFDEC_MSECS_TO_TICKS (msecs);
1262 SWFDEC_DEBUG ("advancing %lu msecs (%u audio frames)", msecs, audio_samples);
1264 for (timeout = player->timeouts ? player->timeouts->data : NULL;
1265 timeout && timeout->timestamp <= target_time;
1266 timeout = player->timeouts ? player->timeouts->data : NULL) {
1267 player->timeouts = g_list_remove (player->timeouts, timeout);
1268 frames_now = SWFDEC_TICKS_TO_SAMPLES (timeout->timestamp) -
1269 SWFDEC_TICKS_TO_SAMPLES (player->time);
1270 player->time = timeout->timestamp;
1271 swfdec_player_advance_audio (player, frames_now);
1272 audio_samples -= frames_now;
1273 SWFDEC_LOG ("activating timeout %p now (timeout is %"G_GUINT64_FORMAT", target time is %"G_GUINT64_FORMAT,
1274 timeout, timeout->timestamp, target_time);
1275 timeout->callback (timeout);
1276 swfdec_player_perform_actions (player);
1278 if (target_time > player->time) {
1279 frames_now = SWFDEC_TICKS_TO_SAMPLES (target_time) -
1280 SWFDEC_TICKS_TO_SAMPLES (player->time);
1281 player->time = target_time;
1282 swfdec_player_advance_audio (player, frames_now);
1283 audio_samples -= frames_now;
1285 g_assert (audio_samples == 0);
1287 swfdec_player_unlock (player);
1290 void
1291 swfdec_player_perform_actions (SwfdecPlayer *player)
1293 GList *walk;
1295 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1297 while (swfdec_player_do_action (player));
1298 for (walk = player->roots; walk; walk = walk->next) {
1299 swfdec_movie_update (walk->data);
1301 /* update the state of the mouse when stuff below it moved */
1302 if (swfdec_rectangle_contains_point (&player->invalid_extents, player->mouse_x, player->mouse_y)) {
1303 SWFDEC_INFO ("=== NEED TO UPDATE mouse post-iteration ===");
1304 swfdec_player_update_mouse_position (player);
1305 while (swfdec_player_do_action (player));
1306 for (walk = player->roots; walk; walk = walk->next) {
1307 swfdec_movie_update (walk->data);
1312 /* used for breakpoints */
1313 void
1314 swfdec_player_lock_soft (SwfdecPlayer *player)
1316 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1317 g_assert (swfdec_rectangle_is_empty (&player->invalid_extents));
1319 g_object_freeze_notify (G_OBJECT (player));
1320 g_timer_start (player->runtime);
1321 SWFDEC_DEBUG ("LOCKED");
1324 gboolean
1325 swfdec_player_lock (SwfdecPlayer *player)
1327 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
1328 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[0]) == 0);
1329 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[1]) == 0);
1330 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[2]) == 0);
1331 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[3]) == 0);
1333 if (swfdec_as_context_is_aborted (SWFDEC_AS_CONTEXT (player)))
1334 return FALSE;
1336 g_object_ref (player);
1337 swfdec_player_lock_soft (player);
1338 return TRUE;
1341 /* used for breakpoints */
1342 void
1343 swfdec_player_unlock_soft (SwfdecPlayer *player)
1345 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1347 SWFDEC_DEBUG ("UNLOCK");
1348 g_timer_stop (player->runtime);
1349 swfdec_player_update_mouse_cursor (player);
1350 g_object_thaw_notify (G_OBJECT (player));
1351 swfdec_player_emit_signals (player);
1354 void
1355 swfdec_player_unlock (SwfdecPlayer *player)
1357 SwfdecAsContext *context;
1359 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1360 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[0]) == 0);
1361 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[1]) == 0);
1362 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[2]) == 0);
1363 g_assert (swfdec_ring_buffer_get_n_elements (player->actions[3]) == 0);
1364 context = SWFDEC_AS_CONTEXT (player);
1365 g_return_if_fail (context->state != SWFDEC_AS_CONTEXT_INTERRUPTED);
1367 if (context->state == SWFDEC_AS_CONTEXT_RUNNING)
1368 swfdec_as_context_maybe_gc (SWFDEC_AS_CONTEXT (player));
1369 swfdec_player_unlock_soft (player);
1370 g_object_unref (player);
1373 static gboolean
1374 swfdec_accumulate_or (GSignalInvocationHint *ihint, GValue *return_accu,
1375 const GValue *handler_return, gpointer data)
1377 if (g_value_get_boolean (handler_return))
1378 g_value_set_boolean (return_accu, TRUE);
1379 return TRUE;
1382 static void
1383 swfdec_player_mark_string_object (gpointer key, gpointer value, gpointer data)
1385 swfdec_as_string_mark (key);
1386 swfdec_as_object_mark (value);
1389 static void
1390 swfdec_player_mark_rooted_object (gpointer object, gpointer unused)
1392 if (SWFDEC_IS_RESOURCE (object)) {
1393 swfdec_resource_mark (object);
1394 } else if (SWFDEC_IS_AS_OBJECT (object)) {
1395 swfdec_as_object_mark (object);
1399 static void
1400 swfdec_player_mark (SwfdecAsContext *context)
1402 SwfdecPlayer *player = SWFDEC_PLAYER (context);
1404 g_hash_table_foreach (player->registered_classes, swfdec_player_mark_string_object, NULL);
1405 swfdec_as_object_mark (player->MovieClip);
1406 swfdec_as_object_mark (player->Video);
1407 g_list_foreach (player->roots, (GFunc) swfdec_as_object_mark, NULL);
1408 g_list_foreach (player->intervals, (GFunc) swfdec_as_object_mark, NULL);
1409 g_list_foreach (player->rooted_objects, swfdec_player_mark_rooted_object, NULL);
1411 SWFDEC_AS_CONTEXT_CLASS (swfdec_player_parent_class)->mark (context);
1414 static void
1415 swfdec_player_get_time (SwfdecAsContext *context, GTimeVal *tv)
1417 *tv = context->start_time;
1419 /* FIXME: what granularity do we want? Currently it's milliseconds */
1420 g_time_val_add (tv, SWFDEC_TICKS_TO_MSECS (SWFDEC_PLAYER (context)->time) * 1000);
1423 static gboolean
1424 swfdec_player_check_continue (SwfdecAsContext *context)
1426 SwfdecPlayer *player = SWFDEC_PLAYER (context);
1428 if (player->max_runtime == 0)
1429 return TRUE;
1430 return g_timer_elapsed (player->runtime, NULL) * 1000 <= player->max_runtime;
1433 static void
1434 swfdec_player_class_init (SwfdecPlayerClass *klass)
1436 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1437 SwfdecAsContextClass *context_class = SWFDEC_AS_CONTEXT_CLASS (klass);
1439 object_class->get_property = swfdec_player_get_property;
1440 object_class->set_property = swfdec_player_set_property;
1441 object_class->dispose = swfdec_player_dispose;
1443 g_object_class_install_property (object_class, PROP_INITIALIZED,
1444 g_param_spec_boolean ("initialized", "initialized", "TRUE when the player has initialized its basic values",
1445 FALSE, G_PARAM_READABLE));
1446 g_object_class_install_property (object_class, PROP_DEFAULT_WIDTH,
1447 g_param_spec_uint ("default-width", "default width", "default width of the movie",
1448 0, G_MAXUINT, 0, G_PARAM_READABLE));
1449 g_object_class_install_property (object_class, PROP_DEFAULT_HEIGHT,
1450 g_param_spec_uint ("default-height", "default height", "default height of the movie",
1451 0, G_MAXUINT, 0, G_PARAM_READABLE));
1452 g_object_class_install_property (object_class, PROP_RATE,
1453 g_param_spec_double ("rate", "rate", "rate in frames per second",
1454 0.0, 256.0, 0.0, G_PARAM_READABLE));
1455 g_object_class_install_property (object_class, PROP_MOUSE_CURSOR,
1456 g_param_spec_enum ("mouse-cursor", "mouse cursor", "how the mouse pointer should be presented",
1457 SWFDEC_TYPE_MOUSE_CURSOR, SWFDEC_MOUSE_CURSOR_NONE, G_PARAM_READABLE));
1458 g_object_class_install_property (object_class, PROP_NEXT_EVENT,
1459 g_param_spec_long ("next-event", "next event", "how many milliseconds until the next event or 0 when no event pending",
1460 -1, G_MAXLONG, -1, G_PARAM_READABLE));
1461 g_object_class_install_property (object_class, PROP_CACHE_SIZE,
1462 g_param_spec_uint ("cache-size", "cache size", "maximum cache size in bytes",
1463 0, G_MAXUINT, 50 * 1024 * 1024, G_PARAM_READABLE));
1464 g_object_class_install_property (object_class, PROP_BACKGROUND_COLOR,
1465 g_param_spec_uint ("background-color", "background color", "ARGB color used to draw the background",
1466 0, G_MAXUINT, SWFDEC_COLOR_COMBINE (0xFF, 0xFF, 0xFF, 0xFF), G_PARAM_READWRITE));
1467 g_object_class_install_property (object_class, PROP_WIDTH,
1468 g_param_spec_int ("width", "width", "current width of the movie",
1469 -1, G_MAXINT, -1, G_PARAM_READWRITE));
1470 g_object_class_install_property (object_class, PROP_HEIGHT,
1471 g_param_spec_int ("height", "height", "current height of the movie",
1472 -1, G_MAXINT, -1, G_PARAM_READWRITE));
1473 g_object_class_install_property (object_class, PROP_ALIGNMENT,
1474 g_param_spec_enum ("alignment", "alignment", "point of the screen to align the output to",
1475 SWFDEC_TYPE_ALIGNMENT, SWFDEC_ALIGNMENT_CENTER, G_PARAM_READWRITE));
1476 g_object_class_install_property (object_class, PROP_SCALE,
1477 g_param_spec_enum ("scale-mode", "scale mode", "method used to scale the movie",
1478 SWFDEC_TYPE_SCALE_MODE, SWFDEC_SCALE_SHOW_ALL, G_PARAM_READWRITE));
1479 g_object_class_install_property (object_class, PROP_SCALE,
1480 g_param_spec_object ("system", "system", "object holding system information",
1481 SWFDEC_TYPE_SYSTEM, G_PARAM_READWRITE));
1482 g_object_class_install_property (object_class, PROP_MAX_RUNTIME,
1483 g_param_spec_ulong ("max-runtime", "maximum runtime", "maximum time in msecs scripts may run in the player before aborting",
1484 0, G_MAXULONG, 10 * 1000, G_PARAM_READWRITE));
1487 * SwfdecPlayer::invalidate:
1488 * @player: the #SwfdecPlayer affected
1489 * @extents: the smallest rectangle enclosing the full region of changes
1490 * @rectangles: a number of smaller rectangles for fine-grained control over
1491 * changes
1492 * @n_rectangles: number of rectangles in @rectangles
1494 * This signal is emitted whenever graphical elements inside the player have
1495 * changed. It provides two ways to look at the changes: By looking at the
1496 * @extents parameter, it provides a simple way to get a single rectangle that
1497 * encloses all changes. By looking at the @rectangles array, you can get
1498 * finer control over changes which is very useful if your rendering system
1499 * provides a way to handle regions.
1501 signals[INVALIDATE] = g_signal_new ("invalidate", G_TYPE_FROM_CLASS (klass),
1502 G_SIGNAL_RUN_LAST, 0, NULL, NULL, swfdec_marshal_VOID__BOXED_POINTER_UINT,
1503 G_TYPE_NONE, 3, SWFDEC_TYPE_RECTANGLE, G_TYPE_POINTER, G_TYPE_UINT);
1505 * SwfdecPlayer::advance:
1506 * @player: the #SwfdecPlayer affected
1507 * @msecs: the amount of milliseconds the player will advance
1508 * @audio_samples: number of frames the audio is advanced (in 44100Hz steps)
1510 * Emitted whenever the player advances.
1512 signals[ADVANCE] = g_signal_new ("advance", G_TYPE_FROM_CLASS (klass),
1513 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwfdecPlayerClass, advance),
1514 NULL, NULL, swfdec_marshal_VOID__ULONG_UINT,
1515 G_TYPE_NONE, 2, G_TYPE_ULONG, G_TYPE_UINT);
1517 * SwfdecPlayer::handle-key:
1518 * @player: the #SwfdecPlayer affected
1519 * @key: #SwfdecKey that was pressed or released
1520 * @pressed: %TRUE if the @key was pressed or %FALSE if it was released
1522 * This signal is emitted whenever @player should respond to a key event. If
1523 * any of the handlers returns TRUE, swfdec_player_key_press() or
1524 * swfdec_player_key_release() will return TRUE. Note that unlike many event
1525 * handlers in gtk, returning TRUE will not stop further event handlers from
1526 * being invoked. Use g_signal_stop_emission() in that case.
1528 * Returns: TRUE if this handler handles the event.
1530 signals[HANDLE_KEY] = g_signal_new ("handle-key", G_TYPE_FROM_CLASS (klass),
1531 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwfdecPlayerClass, handle_key),
1532 swfdec_accumulate_or, NULL, swfdec_marshal_BOOLEAN__UINT_UINT_BOOLEAN,
1533 G_TYPE_BOOLEAN, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_BOOLEAN);
1535 * SwfdecPlayer::handle-mouse:
1536 * @player: the #SwfdecPlayer affected
1537 * @x: new x coordinate of the mouse
1538 * @y: new y coordinate of the mouse
1539 * @button: 1 if the button is pressed, 0 if not
1541 * This signal is emitted whenever @player should respond to a mouse event. If
1542 * any of the handlers returns TRUE, swfdec_player_handle_mouse() will return
1543 * TRUE. Note that unlike many event handlers in gtk, returning TRUE will not
1544 * stop further event handlers from being invoked. Use g_signal_stop_emission()
1545 * in that case.
1547 * Returns: TRUE if this handler handles the event.
1549 signals[HANDLE_MOUSE] = g_signal_new ("handle-mouse", G_TYPE_FROM_CLASS (klass),
1550 G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (SwfdecPlayerClass, handle_mouse),
1551 swfdec_accumulate_or, NULL, swfdec_marshal_BOOLEAN__DOUBLE_DOUBLE_INT,
1552 G_TYPE_BOOLEAN, 3, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_INT);
1554 * SwfdecPlayer::audio-added:
1555 * @player: the #SwfdecPlayer affected
1556 * @audio: the audio stream that was added
1558 * Emitted whenever a new audio stream was added to @player.
1560 signals[AUDIO_ADDED] = g_signal_new ("audio-added", G_TYPE_FROM_CLASS (klass),
1561 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
1562 G_TYPE_NONE, 1, SWFDEC_TYPE_AUDIO);
1564 * SwfdecPlayer::audio-removed:
1565 * @player: the #SwfdecPlayer affected
1566 * @audio: the audio stream that was removed
1568 * Emitted whenever an audio stream was removed from @player. The stream will
1569 * have been added with the SwfdecPlayer::audio-added signal previously.
1571 signals[AUDIO_REMOVED] = g_signal_new ("audio-removed", G_TYPE_FROM_CLASS (klass),
1572 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
1573 G_TYPE_NONE, 1, SWFDEC_TYPE_AUDIO);
1575 * SwfdecPlayer::fscommand:
1576 * @player: the #SwfdecPlayer affected
1577 * @command: the command to execute. This is a lower case string.
1578 * @parameter: parameter to pass to the command. The parameter depends on the
1579 * function.
1581 * This signal is emited whenever a Flash script command (also known as
1582 * fscommand) is encountered. This method is ued by the Flash file to
1583 * communicate with the hosting environment. In web browsers it is used to
1584 * call Javascript functions. Standalone Flash players understand a limited
1585 * set of functions. They vary from player to player, but the most common are
1586 * listed here: <itemizedlist>
1587 * <listitem><para>"quit": quits the player.</para></listitem>
1588 * <listitem><para>"fullscreen": A boolean setting (parameter is "true" or
1589 * "false") that sets the player into fullscreen mode.</para></listitem>
1590 * <listitem><para>"allowscale": A boolean setting that tells the player to
1591 * not scale the Flash application.</para></listitem>
1592 * <listitem><para>"showmenu": A boolean setting that tells the Flash player
1593 * to not show its own entries in the right-click menu.</para></listitem>
1594 * <listitem><para>"exec": Run an external executable. The parameter
1595 * specifies the path.</para></listitem>
1596 * <listitem><para>"trapallkeys": A boolean setting that tells the Flash
1597 * player to pass all key events to the Flash application instead of using it
1598 * for keyboard shortcuts or similar.</para></listitem>
1599 * </itemizedlist>
1601 signals[FSCOMMAND] = g_signal_new ("fscommand", G_TYPE_FROM_CLASS (klass),
1602 G_SIGNAL_RUN_LAST, 0, NULL, NULL, swfdec_marshal_VOID__STRING_STRING,
1603 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
1605 * SwfdecPlayer::launch:
1606 * @player: the #SwfdecPlayer affected
1607 * @request: the type of request
1608 * @url: URL to open
1609 * @target: target to load the URL into
1610 * @data: optional data to pass on with the request. Will be of mime type
1611 * application/x-www-form-urlencoded. Can be %NULL indicating no data
1612 * should be passed.
1614 * Emitted whenever the @player encounters an URL that should be loaded into
1615 * a target the Flash player does not recognize. In most cases this happens
1616 * when the user clicks a link in an embedded Flash movie that should open a
1617 * new web page.
1618 * The effect of calling any swfdec functions on the emitting @player is undefined.
1620 signals[LAUNCH] = g_signal_new ("launch", G_TYPE_FROM_CLASS (klass),
1621 G_SIGNAL_RUN_LAST, 0, NULL, NULL, swfdec_marshal_VOID__ENUM_STRING_STRING_BOXED,
1622 G_TYPE_NONE, 4, SWFDEC_TYPE_LOADER_REQUEST, G_TYPE_STRING, G_TYPE_STRING,
1623 SWFDEC_TYPE_BUFFER);
1625 context_class->mark = swfdec_player_mark;
1626 context_class->get_time = swfdec_player_get_time;
1627 context_class->check_continue = swfdec_player_check_continue;
1629 klass->advance = swfdec_player_do_advance;
1630 klass->handle_key = swfdec_player_do_handle_key;
1631 klass->handle_mouse = swfdec_player_do_handle_mouse;
1634 static void
1635 swfdec_player_init (SwfdecPlayer *player)
1637 guint i;
1639 player->system = swfdec_system_new ();
1640 player->registered_classes = g_hash_table_new (g_direct_hash, g_direct_equal);
1642 for (i = 0; i < SWFDEC_PLAYER_N_ACTION_QUEUES; i++) {
1643 player->actions[i] = swfdec_ring_buffer_new_for_type (SwfdecPlayerAction, 16);
1645 player->external_actions = swfdec_ring_buffer_new_for_type (SwfdecPlayerExternalAction, 8);
1646 player->cache = swfdec_cache_new (50 * 1024 * 1024); /* 100 MB */
1647 player->bgcolor = SWFDEC_COLOR_COMBINE (0xFF, 0xFF, 0xFF, 0xFF);
1649 player->runtime = g_timer_new ();
1650 g_timer_stop (player->runtime);
1651 player->max_runtime = 10 * 1000;
1652 player->invalidations = g_array_new (FALSE, FALSE, sizeof (SwfdecRectangle));
1653 player->mouse_visible = TRUE;
1654 player->mouse_cursor = SWFDEC_MOUSE_CURSOR_NORMAL;
1655 player->iterate_timeout.callback = swfdec_player_iterate;
1656 player->stage_width = -1;
1657 player->stage_height = -1;
1659 swfdec_player_resource_request_init (player);
1662 void
1663 swfdec_player_stop_all_sounds (SwfdecPlayer *player)
1665 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1667 while (player->audio) {
1668 swfdec_audio_remove (player->audio->data);
1672 void
1673 swfdec_player_stop_sounds (SwfdecPlayer *player, SwfdecAudioRemoveFunc func, gpointer data)
1675 GList *walk;
1677 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1678 g_return_if_fail (func);
1680 walk = player->audio;
1681 while (walk) {
1682 SwfdecAudio *audio = walk->data;
1683 walk = walk->next;
1684 if (func (audio, data))
1685 swfdec_audio_remove (audio);
1689 /* rect is in global coordinates */
1690 void
1691 swfdec_player_invalidate (SwfdecPlayer *player, const SwfdecRect *rect)
1693 SwfdecRectangle r;
1694 SwfdecRect tmp;
1695 guint i;
1697 if (swfdec_rect_is_empty (rect)) {
1698 SWFDEC_ERROR ("called with an empty rectanle. In theory this shouldn't happen.");
1699 SWFDEC_ERROR (" However, degenerate matrixes can cause this. We need a fix for that.");
1700 return;
1703 tmp = *rect;
1704 swfdec_player_global_to_stage (player, &tmp.x0, &tmp.y0);
1705 swfdec_player_global_to_stage (player, &tmp.x1, &tmp.y1);
1706 swfdec_rectangle_init_rect (&r, &tmp);
1707 /* FIXME: currently we clamp the rectangle to the visible area, it might
1708 * be useful to allow out-of-bounds drawing. In that case this needs to be
1709 * changed */
1710 swfdec_rectangle_intersect (&r, &r, &player->stage);
1711 if (swfdec_rectangle_is_empty (&r))
1712 return;
1714 /* FIXME: get region code into swfdec? */
1715 for (i = 0; i < player->invalidations->len; i++) {
1716 SwfdecRectangle *cur = &g_array_index (player->invalidations, SwfdecRectangle, i);
1717 if (swfdec_rectangle_contains (cur, &r))
1718 break;
1719 if (swfdec_rectangle_contains (&r, cur)) {
1720 *cur = r;
1721 swfdec_rectangle_union (&player->invalid_extents, &player->invalid_extents, &r);
1724 if (i == player->invalidations->len) {
1725 g_array_append_val (player->invalidations, r);
1726 swfdec_rectangle_union (&player->invalid_extents, &player->invalid_extents, &r);
1728 SWFDEC_DEBUG ("toplevel invalidation of %g %g %g %g - invalid region now %d %d %d %d (%u subregions)",
1729 rect->x0, rect->y0, rect->x1, rect->y1,
1730 player->invalid_extents.x, player->invalid_extents.y,
1731 player->invalid_extents.x + player->invalid_extents.width,
1732 player->invalid_extents.y + player->invalid_extents.height,
1733 player->invalidations->len);
1737 * swfdec_player_get_level:
1738 * @player: a #SwfdecPlayer
1739 * @name: a name that is supposed to refer to a level
1741 * Checks if the given @name refers to a level, and if so, returns the level.
1742 * An example for such a name is "_level5". These strings are used to refer to
1743 * root movies inside the Flash player.
1745 * Returns: the level referred to by @name or -1 if none
1748 swfdec_player_get_level (SwfdecPlayer *player, const char *name)
1750 char *end;
1751 gulong l;
1753 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), -1);
1754 g_return_val_if_fail (name != NULL, -1);
1756 /* check name starts with "_level" */
1757 if (swfdec_strncmp (SWFDEC_AS_CONTEXT (player)->version, name, "_level", 6) != 0)
1758 return -1;
1759 name += 6;
1760 /* extract depth from rest string (or fail if it's not a depth) */
1761 errno = 0;
1762 l = strtoul (name, &end, 10);
1763 if (errno != 0 || *end != 0 || l > G_MAXINT)
1764 return -1;
1765 return l;
1768 SwfdecSpriteMovie *
1769 swfdec_player_create_movie_at_level (SwfdecPlayer *player, SwfdecResource *resource,
1770 int level)
1772 SwfdecMovie *movie;
1773 const char *s;
1775 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
1776 g_return_val_if_fail (level >= 0, NULL);
1777 g_return_val_if_fail (swfdec_player_get_movie_at_level (player, level) == NULL, NULL);
1779 /* create new root movie */
1780 s = swfdec_as_context_give_string (SWFDEC_AS_CONTEXT (player), g_strdup_printf ("_level%d", level));
1781 movie = swfdec_movie_new (player, level - 16384, NULL, resource, NULL, s);
1782 if (movie == NULL)
1783 return NULL;
1784 movie->name = SWFDEC_AS_STR_EMPTY;
1785 return SWFDEC_SPRITE_MOVIE (movie);
1789 * swfdec_player_get_movie_at_level:
1790 * @player: a #SwfdecPlayer
1791 * @level: number of the level
1793 * This function is used to look up root movies in the given @player.
1795 * Returns: the #SwfdecMovie located at the given level or %NULL if there is no
1796 * movie at that level.
1798 SwfdecSpriteMovie *
1799 swfdec_player_get_movie_at_level (SwfdecPlayer *player, int level)
1801 GList *walk;
1802 int depth;
1804 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
1805 g_return_val_if_fail (level >= 0, NULL);
1807 depth = level - 16384;
1808 /* find movie */
1809 for (walk = player->roots; walk; walk = walk->next) {
1810 SwfdecMovie *cur = walk->data;
1811 if (cur->depth < depth)
1812 continue;
1813 if (cur->depth == depth)
1814 return SWFDEC_SPRITE_MOVIE (cur);
1815 break;
1817 return NULL;
1820 void
1821 swfdec_player_launch (SwfdecPlayer *player, SwfdecLoaderRequest request, const char *url,
1822 const char *target, SwfdecBuffer *data)
1824 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1825 g_return_if_fail (url != NULL);
1826 g_return_if_fail (target != NULL);
1828 if (!g_ascii_strncasecmp (url, "FSCommand:", strlen ("FSCommand:"))) {
1829 const char *command = url + strlen ("FSCommand:");
1830 g_signal_emit (player, signals[FSCOMMAND], 0, command, target);
1831 return;
1833 g_signal_emit (player, signals[LAUNCH], 0, (int) request, url, target, data);
1837 * swfdec_player_initialize:
1838 * @player: a #SwfdecPlayer
1839 * @version: Flash version to use
1840 * @rate: framerate in 256th or 0 for undefined
1841 * @width: width of movie
1842 * @height: height of movie
1844 * Initializes the player to the given @version, @width, @height and @rate. If
1845 * the player is already initialized, this function does nothing.
1847 void
1848 swfdec_player_initialize (SwfdecPlayer *player, guint version,
1849 guint rate, guint width, guint height)
1851 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1852 g_return_if_fail (rate > 0);
1854 if (!player->initialized) {
1855 SwfdecAsContext *context = SWFDEC_AS_CONTEXT (player);
1856 swfdec_as_context_startup (context, version);
1857 /* reset state for initialization */
1858 /* FIXME: have a better way to do this */
1859 if (context->state == SWFDEC_AS_CONTEXT_RUNNING) {
1860 context->state = SWFDEC_AS_CONTEXT_NEW;
1861 swfdec_sprite_movie_init_context (player, version);
1862 swfdec_video_movie_init_context (player, version);
1863 swfdec_net_connection_init_context (player, version);
1864 swfdec_net_stream_init_context (player, version);
1866 swfdec_as_context_run_init_script (context, swfdec_initialize,
1867 sizeof (swfdec_initialize), 8);
1869 if (context->state == SWFDEC_AS_CONTEXT_NEW) {
1870 context->state = SWFDEC_AS_CONTEXT_RUNNING;
1871 swfdec_as_object_set_constructor (player->roots->data, player->MovieClip);
1874 player->initialized = TRUE;
1875 g_object_notify (G_OBJECT (player), "initialized");
1876 } else {
1877 /* FIXME: need to kick all other movies out here */
1878 swfdec_player_remove_timeout (player, &player->iterate_timeout);
1881 SWFDEC_INFO ("initializing player to size %ux%u and rate %u/256", width, height, rate);
1882 if (rate != player->rate) {
1883 player->rate = rate;
1884 g_object_notify (G_OBJECT (player), "rate");
1886 if (player->width != width) {
1887 player->width = width;
1888 g_object_notify (G_OBJECT (player), "default-width");
1890 if (player->height != height) {
1891 player->height = height;
1892 g_object_notify (G_OBJECT (player), "default-height");
1894 player->internal_width = player->stage_width >= 0 ? (guint) player->stage_width : player->width;
1895 player->internal_height = player->stage_height >= 0 ? (guint) player->stage_height : player->height;
1896 swfdec_player_update_scale (player);
1898 player->iterate_timeout.timestamp = player->time + SWFDEC_TICKS_PER_SECOND * 256 / player->rate / 10;
1899 swfdec_player_add_timeout (player, &player->iterate_timeout);
1900 SWFDEC_LOG ("initialized iterate timeout %p to %"G_GUINT64_FORMAT" (now %"G_GUINT64_FORMAT")",
1901 &player->iterate_timeout, player->iterate_timeout.timestamp, player->time);
1905 * swfdec_player_get_export_class:
1906 * @player: a #SwfdecPlayer
1907 * @name: garbage-collected string naming the export
1909 * Looks up the constructor for characters that are exported using @name.
1911 * Returns: a #SwfdecAsObject naming the constructor or %NULL if none
1913 SwfdecAsObject *
1914 swfdec_player_get_export_class (SwfdecPlayer *player, const char *name)
1916 SwfdecAsObject *ret;
1918 ret = g_hash_table_lookup (player->registered_classes, name);
1919 if (ret) {
1920 SWFDEC_LOG ("found registered class %p for %s", ret, name);
1921 return ret;
1923 return player->MovieClip;
1927 * swfdec_player_set_export_class:
1928 * @player: a #SwfdecPlayer
1929 * @name: garbage-collected string naming the export
1930 * @object: object to use as constructor or %NULL for none
1932 * Sets the constructor to be used for instances created using the object
1933 * exported with @name.
1935 void
1936 swfdec_player_set_export_class (SwfdecPlayer *player, const char *name, SwfdecAsObject *object)
1938 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1939 g_return_if_fail (name != NULL);
1940 g_return_if_fail (object == NULL || SWFDEC_IS_AS_OBJECT (object));
1942 if (object) {
1943 SWFDEC_LOG ("setting class %p for %s", object, name);
1944 g_hash_table_insert (player->registered_classes, (gpointer) name, object);
1945 } else {
1946 g_hash_table_remove (player->registered_classes, name);
1950 /* FIXME:
1951 * I don't like the idea of rooting arbitrary objects very much. And so far,
1952 * this API is only necessary for the objects used for loading data. So it seems
1953 * like a good idea to revisit the refcounting and GCing of resources.
1955 void
1956 swfdec_player_root_object (SwfdecPlayer *player, GObject *object)
1958 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1959 g_return_if_fail (G_IS_OBJECT (object));
1961 g_object_ref (object);
1962 player->rooted_objects = g_list_prepend (player->rooted_objects, object);
1965 void
1966 swfdec_player_unroot_object (SwfdecPlayer *player, GObject *object)
1968 GList *entry;
1970 g_return_if_fail (SWFDEC_IS_PLAYER (player));
1971 g_return_if_fail (G_IS_OBJECT (object));
1972 entry = g_list_find (player->rooted_objects, object);
1973 g_return_if_fail (entry != NULL);
1974 g_object_unref (object);
1975 player->rooted_objects = g_list_delete_link (player->rooted_objects, entry);
1978 /** PUBLIC API ***/
1981 * swfdec_player_new:
1982 * @debugger: %NULL or a #SwfdecAsDebugger to use for debugging this player.
1984 * Creates a new player. This function is supposed to be used for testing.
1985 * Because of this, the created player will behave as predictable as possible.
1986 * For example, it will generate the same random number sequence every time.
1987 * The function calls swfdec_init () for you if it wasn't called before.
1989 * Returns: The new player
1991 SwfdecPlayer *
1992 swfdec_player_new (SwfdecAsDebugger *debugger)
1994 static const GTimeVal the_beginning = { 1035840244, 0 };
1995 SwfdecPlayer *player;
1997 g_return_val_if_fail (debugger == NULL || SWFDEC_IS_AS_DEBUGGER (debugger), NULL);
1999 swfdec_init ();
2000 player = g_object_new (SWFDEC_TYPE_PLAYER, "random-seed", 0,
2001 "max-runtime", 0,
2002 "debugger", debugger, NULL);
2003 /* FIXME: make this a property or something and don't set it here */
2004 SWFDEC_AS_CONTEXT (player)->start_time = the_beginning;
2006 return player;
2010 * swfdec_player_set_loader:
2011 * @player: a #SwfdecPlayer
2012 * @loader: the loader to use for this player. Takes ownership of the given loader.
2014 * Sets the loader for the main data. This function only works if no loader has
2015 * been set on @player yet.
2016 * For details, see swfdec_player_set_loader_with_variables().
2018 void
2019 swfdec_player_set_loader (SwfdecPlayer *player, SwfdecLoader *loader)
2021 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2022 g_return_if_fail (player->roots == NULL);
2023 g_return_if_fail (SWFDEC_IS_LOADER (loader));
2025 swfdec_player_set_loader_with_variables (player, loader, NULL);
2029 * swfdec_player_set_loader_with_variables:
2030 * @player: a #SwfdecPlayer
2031 * @loader: the loader to use for this player. Takes ownership of the given loader.
2032 * @variables: a string that is checked to be in 'application/x-www-form-urlencoded'
2033 * syntax describing the arguments to set on the new player or NULL for
2034 * none.
2036 * Sets the loader for the main data. This function only works if no loader has
2037 * been set on @player yet.
2038 * If the @variables are set and validate, they will be set as properties on the
2039 * root movie.
2041 void
2042 swfdec_player_set_loader_with_variables (SwfdecPlayer *player, SwfdecLoader *loader,
2043 const char *variables)
2045 SwfdecMovie *movie;
2047 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2048 g_return_if_fail (player->resource == NULL);
2049 g_return_if_fail (SWFDEC_IS_LOADER (loader));
2051 player->resource = swfdec_resource_new (player, loader, variables);
2052 movie = swfdec_movie_new (player, -16384, NULL, player->resource, NULL, SWFDEC_AS_STR__level0);
2053 movie->name = SWFDEC_AS_STR_EMPTY;
2054 g_object_unref (loader);
2058 * swfdec_player_new_from_file:
2059 * @filename: name of the file to play
2061 * Creates a player to play back the given file. If the file does not
2062 * exist or another error occurs, the player will be in an error state and not
2063 * be initialized.
2064 * This function calls swfdec_init () for you if it wasn't called before.
2066 * Returns: a new player
2068 SwfdecPlayer *
2069 swfdec_player_new_from_file (const char *filename)
2071 SwfdecLoader *loader;
2072 SwfdecPlayer *player;
2074 g_return_val_if_fail (filename != NULL, NULL);
2076 loader = swfdec_file_loader_new (filename);
2077 player = swfdec_player_new (NULL);
2078 swfdec_player_set_loader (player, loader);
2080 return player;
2084 * swfdec_init:
2086 * Initializes the Swfdec library.
2088 void
2089 swfdec_init (void)
2091 static gboolean _inited = FALSE;
2092 const char *s;
2094 if (_inited)
2095 return;
2097 _inited = TRUE;
2099 if (!g_thread_supported ())
2100 g_thread_init (NULL);
2101 g_type_init ();
2102 oil_init ();
2104 s = g_getenv ("SWFDEC_DEBUG");
2105 if (s && s[0]) {
2106 char *end;
2107 int level;
2109 level = strtoul (s, &end, 0);
2110 if (end[0] == 0) {
2111 swfdec_debug_set_level (level);
2117 * swfdec_player_handle_mouse:
2118 * @player: a #SwfdecPlayer
2119 * @x: x coordinate of mouse
2120 * @y: y coordinate of mouse
2121 * @button: 1 for pressed, 0 for not pressed
2123 * Updates the current mouse status. If the mouse has left the area of @player,
2124 * you should pass values outside the movie size for @x and @y. You will
2125 * probably want to call swfdec_player_advance() before to update the player to
2126 * the correct time when calling this function.
2128 * Returns: %TRUE if the mouse event was handled. %FALSE to propagate the event
2129 * further. A mouse event may not be handled if the user clicked on a
2130 * translucent area.
2132 gboolean
2133 swfdec_player_handle_mouse (SwfdecPlayer *player,
2134 double x, double y, int button)
2136 gboolean ret;
2138 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
2139 g_return_val_if_fail (button == 0 || button == 1, FALSE);
2141 g_signal_emit (player, signals[HANDLE_MOUSE], 0, x, y, button, &ret);
2143 return ret;
2147 * swfdec_player_key_press:
2148 * @player: a #SwfdecPlayer
2149 * @keycode: the key that was pressed
2150 * @character: UCS4 of the character that was inserted or 0 if none
2152 * Call this function to make the @player react to a key press. Be sure to
2153 * check that keycode transformations are done correctly. For a list of
2154 * keycodes see FIXME.
2156 * Returns: %TRUE if the key press was handled by the @player, %FALSE if it
2157 * should be propagated further
2159 gboolean
2160 swfdec_player_key_press (SwfdecPlayer *player, guint keycode, guint character)
2162 gboolean ret;
2164 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
2165 g_return_val_if_fail (keycode < 256, FALSE);
2167 g_signal_emit (player, signals[HANDLE_KEY], 0, keycode, character, TRUE, &ret);
2169 return ret;
2173 * swfdec_player_key_release:
2174 * @player: a #SwfdecPlayer
2175 * @keycode: the key that was released
2176 * @character: UCS4 of the character that was inserted or 0 if none
2178 * Call this function to make the @player react to a key being released. See
2179 * swfdec_player_key_press() for details.
2181 * Returns: %TRUE if the key press was handled by the @player, %FALSE if it
2182 * should be propagated further
2184 gboolean
2185 swfdec_player_key_release (SwfdecPlayer *player, guint keycode, guint character)
2187 gboolean ret;
2189 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
2190 g_return_val_if_fail (keycode < 256, FALSE);
2192 g_signal_emit (player, signals[HANDLE_KEY], 0, keycode, character, FALSE, &ret);
2194 return ret;
2198 * swfdec_player_render:
2199 * @player: a #SwfdecPlayer
2200 * @cr: #cairo_t to render to
2201 * @x: x coordinate of top left position to render
2202 * @y: y coordinate of top left position to render
2203 * @width: width of area to render or 0 for full width
2204 * @height: height of area to render or 0 for full height
2206 * Renders the given area of the current frame to @cr.
2208 void
2209 swfdec_player_render (SwfdecPlayer *player, cairo_t *cr,
2210 double x, double y, double width, double height)
2212 static const SwfdecColorTransform trans = { 256, 0, 256, 0, 256, 0, 256, 0 };
2213 GList *walk;
2214 SwfdecRect real;
2216 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2217 g_return_if_fail (cr != NULL);
2218 g_return_if_fail (width >= 0.0);
2219 g_return_if_fail (height >= 0.0);
2221 /* FIXME: fail when !initialized? */
2222 if (!swfdec_player_is_initialized (player))
2223 return;
2225 if (width == 0.0)
2226 width = player->stage_width;
2227 if (height == 0.0)
2228 height = player->stage_height;
2229 /* clip the area */
2230 cairo_save (cr);
2231 cairo_rectangle (cr, x, y, width, height);
2232 cairo_clip (cr);
2233 /* compute the rectangle */
2234 x -= player->offset_x;
2235 y -= player->offset_y;
2236 real.x0 = floor (x * SWFDEC_TWIPS_SCALE_FACTOR) / player->scale_x;
2237 real.y0 = floor (y * SWFDEC_TWIPS_SCALE_FACTOR) / player->scale_y;
2238 real.x1 = ceil ((x + width) * SWFDEC_TWIPS_SCALE_FACTOR) / player->scale_x;
2239 real.y1 = ceil ((y + height) * SWFDEC_TWIPS_SCALE_FACTOR) / player->scale_y;
2240 SWFDEC_INFO ("=== %p: START RENDER, area %g %g %g %g ===", player,
2241 real.x0, real.y0, real.x1, real.y1);
2242 /* convert the cairo matrix */
2243 cairo_translate (cr, player->offset_x, player->offset_y);
2244 cairo_scale (cr, player->scale_x / SWFDEC_TWIPS_SCALE_FACTOR, player->scale_y / SWFDEC_TWIPS_SCALE_FACTOR);
2245 swfdec_color_set_source (cr, player->bgcolor);
2246 cairo_paint (cr);
2248 for (walk = player->roots; walk; walk = walk->next) {
2249 swfdec_movie_render (walk->data, cr, &trans, &real);
2251 SWFDEC_INFO ("=== %p: END RENDER ===", player);
2252 cairo_restore (cr);
2256 * swfdec_player_advance:
2257 * @player: the #SwfdecPlayer to advance
2258 * @msecs: number of milliseconds to advance
2260 * Advances @player by @msecs. You should make sure to call this function as
2261 * often as the SwfdecPlayer::next-event property indicates.
2263 void
2264 swfdec_player_advance (SwfdecPlayer *player, gulong msecs)
2266 guint frames;
2267 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2269 frames = SWFDEC_TICKS_TO_SAMPLES (player->time + SWFDEC_MSECS_TO_TICKS (msecs))
2270 - SWFDEC_TICKS_TO_SAMPLES (player->time);
2271 g_signal_emit (player, signals[ADVANCE], 0, msecs, frames);
2275 * swfdec_player_is_initialized:
2276 * @player: a #SwfdecPlayer
2278 * Determines if the @player is initalized yet. An initialized player is able
2279 * to provide basic values like width, height or rate. A player may not be
2280 * initialized if the loader it was started with does not reference a Flash
2281 * resources or it did not provide enough data yet. If a player is initialized,
2282 * it will never be uninitialized again.
2284 * Returns: TRUE if the basic values are known.
2286 gboolean
2287 swfdec_player_is_initialized (SwfdecPlayer *player)
2289 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), FALSE);
2291 return player->initialized;
2295 * swfdec_player_get_next_event:
2296 * @player: ia #SwfdecPlayer
2298 * Queries how long to the next event. This is the next time when you should
2299 * call swfdec_player_advance() to forward to.
2301 * Returns: number of milliseconds until next event or -1 if no outstanding event
2303 glong
2304 swfdec_player_get_next_event (SwfdecPlayer *player)
2306 SwfdecTick tick;
2307 guint ret;
2309 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), 0);
2311 if (swfdec_as_context_is_aborted (SWFDEC_AS_CONTEXT (player)))
2312 return -1;
2314 tick = swfdec_player_get_next_event_time (player);
2315 if (tick == G_MAXUINT64)
2316 return -1;
2317 /* round up to full msecs */
2318 ret = SWFDEC_TICKS_TO_MSECS (tick + SWFDEC_TICKS_PER_SECOND / 1000 - 1);
2320 return ret;
2324 * swfdec_player_get_rate:
2325 * @player: a #SwfdecPlayer
2327 * Queries the framerate of this movie. This number specifies the number
2328 * of frames that are supposed to pass per second. It is a
2329 * multiple of 1/256. It is possible that the movie has no framerate if it does
2330 * not display a Flash movie but an FLV video for example. This does not mean
2331 * it will not change however.
2333 * Returns: The framerate of this movie or 0 if it isn't known yet or the
2334 * movie doesn't have a framerate.
2336 double
2337 swfdec_player_get_rate (SwfdecPlayer *player)
2339 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), 0.0);
2341 return player->rate / 256.0;
2345 * swfdec_player_get_default_size:
2346 * @player: a #SwfdecPlayer
2347 * @width: integer to store the width in or %NULL
2348 * @height: integer to store the height in or %NULL
2350 * If the default size of the movie is initialized, fills in @width and @height
2351 * with the size. Otherwise @width and @height are set to 0.
2353 void
2354 swfdec_player_get_default_size (SwfdecPlayer *player, guint *width, guint *height)
2356 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2358 if (width)
2359 *width = player->width;
2360 if (height)
2361 *height = player->height;
2365 * swfdec_player_get_size:
2366 * @player: a #SwfdecPlayer
2367 * @width: integer to store the width in or %NULL
2368 * @height: integer to store the height in or %NULL
2370 * Gets the currently set image size. If the default width or height should be
2371 * used, the width or height respectively is set to -1.
2373 void
2374 swfdec_player_get_size (SwfdecPlayer *player, int *width, int *height)
2376 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2378 if (width)
2379 *width = player->stage_width;
2380 if (height)
2381 *height = player->stage_height;
2384 static void
2385 swfdec_player_update_size (gpointer playerp, gpointer unused)
2387 SwfdecPlayer *player = playerp;
2388 guint width, height;
2390 /* FIXME: only update if not fullscreen */
2391 width = player->stage_width >=0 ? (guint) player->stage_width : player->width;
2392 height = player->stage_height >=0 ? (guint) player->stage_height : player->height;
2393 /* only broadcast once */
2394 if (width == player->internal_width && height == player->internal_height)
2395 return;
2397 player->internal_width = width;
2398 player->internal_height = height;
2399 if (player->scale_mode == SWFDEC_SCALE_NONE)
2400 swfdec_player_broadcast (player, SWFDEC_AS_STR_Stage, SWFDEC_AS_STR_onResize);
2404 * swfdec_player_set_size:
2405 * @player: a #SwfdecPlayer
2406 * @width: desired width of the movie or -1 for default
2407 * @height: desired height of the movie or -1 for default
2409 * Sets the image size to the given values. The image size is what the area that
2410 * the @player will render and advocate with scripts.
2412 void
2413 swfdec_player_set_size (SwfdecPlayer *player, int width, int height)
2415 gboolean changed = FALSE;
2417 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2418 g_return_if_fail (width >= -1);
2419 g_return_if_fail (height >= -1);
2421 if (player->stage_width != width) {
2422 player->stage_width = width;
2423 g_object_notify (G_OBJECT (player), "width");
2424 changed = TRUE;
2426 if (player->stage_height != height) {
2427 player->stage_height = height;
2428 g_object_notify (G_OBJECT (player), "height");
2429 changed = TRUE;
2431 swfdec_player_update_scale (player);
2432 if (changed)
2433 swfdec_player_add_external_action (player, player, swfdec_player_update_size, NULL);
2437 * swfdec_player_get_audio:
2438 * @player: a #SwfdecPlayer
2440 * Returns a list of all currently active audio streams in @player.
2442 * Returns: A #GList of #SwfdecAudio. You must not modify or free this list.
2444 /* FIXME: I don't like this function */
2445 const GList *
2446 swfdec_player_get_audio (SwfdecPlayer * player)
2448 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), NULL);
2450 return player->audio;
2454 * swfdec_player_get_background_color:
2455 * @player: a #SwfdecPlayer
2457 * Gets the current background color. The color will be an ARGB-quad, with the
2458 * MSB being the alpha value.
2460 * Returns: the background color as an ARGB value
2462 guint
2463 swfdec_player_get_background_color (SwfdecPlayer *player)
2465 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), SWFDEC_COLOR_COMBINE (0xFF, 0xFF, 0xFF, 0xFF));
2467 return player->bgcolor;
2471 * swfdec_player_set_background_color:
2472 * @player: a #SwfdecPlayer
2473 * @color: new color to use as background color
2475 * Sets a new background color as an ARGB value. To get transparency, set the
2476 * value to 0. To get a black beackground, use 0xFF000000.
2478 void
2479 swfdec_player_set_background_color (SwfdecPlayer *player, guint color)
2481 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2483 player->bgcolor_set = TRUE;
2484 if (player->bgcolor == color)
2485 return;
2486 player->bgcolor = color;
2487 g_object_notify (G_OBJECT (player), "background-color");
2488 if (swfdec_player_is_initialized (player)) {
2489 g_signal_emit (player, signals[INVALIDATE], 0, 0.0, 0.0,
2490 (double) player->width, (double) player->height);
2495 * swfdec_player_get_scale_mode:
2496 * @player: a #SwfdecPlayer
2498 * Gets the currrent mode used for scaling the movie. See #SwfdecScaleMode for
2499 * the different modes.
2501 * Returns: the current scale mode
2503 SwfdecScaleMode
2504 swfdec_player_get_scale_mode (SwfdecPlayer *player)
2506 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), SWFDEC_SCALE_SHOW_ALL);
2508 return player->scale_mode;
2512 * swfdec_player_set_scale_mode:
2513 * @player: a #SwfdecPlayer
2514 * @mode: a #SwfdecScaleMode
2516 * Sets the currrent mode used for scaling the movie. See #SwfdecScaleMode for
2517 * the different modes.
2519 void
2520 swfdec_player_set_scale_mode (SwfdecPlayer *player, SwfdecScaleMode mode)
2522 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2524 if (player->scale_mode != mode) {
2525 player->scale_mode = mode;
2526 swfdec_player_update_scale (player);
2527 g_object_notify (G_OBJECT (player), "scale-mode");
2532 * swfdec_player_get_alignment:
2533 * @player: a #SwfdecPlayer
2535 * Gets the alignment of the player. The alignment describes what point is used
2536 * as the anchor for drawing the contents. See #SwfdecAlignment for possible
2537 * values.
2539 * Returns: the current alignment
2541 SwfdecAlignment
2542 swfdec_player_get_alignment (SwfdecPlayer *player)
2544 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), SWFDEC_ALIGNMENT_CENTER);
2546 return swfdec_player_alignment_from_flags (player->align_flags);
2550 * swfdec_player_set_alignment:
2551 * @player: a #SwfdecPlayer
2552 * @align: #SwfdecAlignment to set
2554 * Sets the alignment to @align. For details about alignment, see
2555 * swfdec_player_get_alignment() and #SwfdecAlignment.
2557 void
2558 swfdec_player_set_alignment (SwfdecPlayer *player, SwfdecAlignment align)
2560 guint flags;
2562 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2564 flags = swfdec_player_alignment_to_flags (align);
2565 swfdec_player_set_align_flags (player, flags);
2568 void
2569 swfdec_player_set_align_flags (SwfdecPlayer *player, guint flags)
2571 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2573 if (flags != player->align_flags) {
2574 player->align_flags = flags;
2575 swfdec_player_update_scale (player);
2576 g_object_notify (G_OBJECT (player), "alignment");
2581 * swfdec_player_get_maximum_runtime:
2582 * @player: a #SwfdecPlayer
2584 * Queries the given @player for how long scripts may run. see
2585 * swfdec_player_set_maximum_runtime() for a longer discussion of this value.
2587 * Returns: the maximum time in milliseconds that scripts are allowed to run or
2588 * 0 for infinite.
2590 gulong
2591 swfdec_player_get_maximum_runtime (SwfdecPlayer *player)
2593 g_return_val_if_fail (SWFDEC_IS_PLAYER (player), 0);
2595 return player->max_runtime;
2599 * swfdec_player_set_maximum_runtime:
2600 * @player: a #SwfdecPlayer
2601 * @msecs: time in milliseconds that scripts are allowed to run or 0 for
2602 * infinite
2604 * Sets the time that the player may use to let internal scripts run. If the
2605 * Flash file that is currently played back does not manage to complete its
2606 * scripts in the given time, it is aborted. You cannot continue the scripts at
2607 * a later point in time. However, your application may become unresponsive and
2608 * your users annoyed if they cannot interact with it for too long. To give a
2609 * reference point, the Adobe Flash player usually sets this value to 10
2610 * seconds. Note that this time determines the maximum time calling
2611 * swfdec_player_advance() may take, even if it is called with a large value.
2612 * Also note that this setting is ignored when running inside a debugger.
2614 void
2615 swfdec_player_set_maximum_runtime (SwfdecPlayer *player, gulong msecs)
2617 g_return_if_fail (SWFDEC_IS_PLAYER (player));
2619 player->max_runtime = msecs;
2620 g_object_notify (G_OBJECT (player), "max-runtime");