4 * An glib SMIL library.
6 * Authored By Koos Vriezen <koos.vriezen@gmail.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
25 #include <glib/gmessages.h>
26 #include <glib/gprintf.h>
27 #include <clutter/clutter-main.h>
29 #include "laugh-timing.h"
31 #define LAUGH_TIMING_CONTAINER_GET_PRIVATE(obj) \
32 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), LAUGH_TYPE_TIMING_CONTAINER, LaughTimingContainerPrivate))
35 static gpointer laugh_timing_container_parent_class
= ((void *)0);
37 static void laugh_timing_container_finalize (GObject
*object
)
39 G_OBJECT_CLASS (laugh_timing_container_parent_class
)->finalize (object
);
42 static void laugh_timing_container_dispose (GObject
*object
)
44 LaughTimingContainer
*self
= LAUGH_TIMING_CONTAINER(object
);
45 LaughRoleTiming
*segment
= self
->timing_role
;
47 G_OBJECT_CLASS (laugh_timing_container_parent_class
)->dispose (object
);
49 laugh_timing_role_delete (segment
);
53 _laugh_timing_container_init (LaughNode
*node
, LaughInitializer
*initializer
)
55 LaughTimingContainer
*self
= (LaughTimingContainer
*) node
;
57 LaughRoleTiming
*parent_segment
= initializer
->parent_segment
;
60 g_hash_table_foreach (node
->attributes
, laugh_attributes_set
, node
);
62 laugh_timing_role_add(initializer
->parent_segment
, self
->timing_role
);
64 initializer
->parent_segment
= self
->timing_role
;
66 for (child
= node
->first_child
; child
; child
= child
->next_sibling
)
67 laugh_node_init (child
, initializer
);
69 initializer
->parent_segment
= parent_segment
;
72 static void _laugh_timing_container_set_attribute (LaughNode
*node
,
73 LaughNodeAttributeId att
, const gchar
*value
, gpointer
*undo
)
75 const gchar
*val
= value
;
76 LaughTimingContainer
*self
= (LaughTimingContainer
*)node
;
78 laugh_node_base_set_attribute (node
, att
, val
, undo
);
79 /*g_printf ("_laugh_timing_container_set_attribute %s=%s\n", laugh_attribute_from_id (att), val);*/
82 val
= *(const gchar
**)undo
;
84 laugh_timing_setting_set_attribute (self
->timing_role
, att
, val
);
87 static LaughRole
*_laugh_timing_container_role (LaughNode
*node
, LaughRoleType type
)
89 LaughTimingContainer
*self
= (LaughTimingContainer
*) node
;
92 case LaughRoleTypeTiming
:
93 return (LaughRole
*) self
->timing_role
;
99 static void laugh_timing_container_class_init (LaughTimingContainerClass
*klass
)
101 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
102 LaughNodeClass
*node_class
= (LaughNodeClass
*) klass
;
104 laugh_timing_container_parent_class
= g_type_class_peek_parent (klass
);
106 gobject_class
->finalize
= laugh_timing_container_finalize
;
107 gobject_class
->dispose
= laugh_timing_container_dispose
;
108 node_class
->init
= _laugh_timing_container_init
;
109 node_class
->set_attribute
= _laugh_timing_container_set_attribute
;
110 node_class
->role
= _laugh_timing_container_role
;
112 /*g_type_class_add_private (gobject_class, sizeof (LaughTimingContainerPrivate));*/
116 void laugh_timing_container_instance_init (GTypeInstance
*instance
, gpointer g_class
)
118 LaughTimingContainer
*self
= (LaughTimingContainer
*)instance
;
120 /*self->priv = LAUGH_TIMING_CONTAINER_GET_PRIVATE (self);*/
122 self
->timing_role
= laugh_timing_role_new ((LaughNode
*) self
);
125 GType
laugh_timing_container_get_type (void)
127 static GType type
= 0;
129 static const GTypeInfo info
= {
130 sizeof (LaughTimingContainerClass
),
131 NULL
, /* base_init */
132 NULL
, /* base_finalize */
133 (GClassInitFunc
) laugh_timing_container_class_init
, /* class_init */
134 NULL
, /* class_finalize */
135 NULL
, /* class_data */
136 sizeof (LaughTimingContainer
),
138 laugh_timing_container_instance_init
/* instance_init */
140 type
= g_type_register_static (LAUGH_TYPE_NODE
,
141 "LaughTimingContainerType",
147 static int _timing_parse_time (const gchar
*tstr
, int *value
) {
150 const gchar
*nstart
= NULL
;
151 const gchar
*p
= tstr
;
163 } else if (*p
== '-') {
168 } else if (*p
>= '0' && *p
<= '9') {
171 } else if (*p
== '.') {
177 } else if (*p
== ' ') {
186 num
= g_strndup (nstart
, p
- tstr
);
187 *value
= sign
* (int) (1000 * g_strtod (nstart
, NULL
));
193 } else if (*p
== 'h') {
196 } else if (*p
!= ' ')
205 static int _get_timing (const gchar
*tstr
, LaughTiming
*timing
) {
207 const gchar
*p
= tstr
;
215 timing
->type
= LaughTimingUnknown
;
219 lower
= g_ascii_strdown (p
, -1);
222 if (_timing_parse_time (lower
, &timing
->offset
)) {
223 timing
->type
= LaughTimingTime
;
224 } else if (!strncmp (lower
, "id(", 3)) {
225 p
= (const gchar
*) strchr (lower
+ 3, ')');
227 idref
= g_strndup (lower
+ 3, p
- lower
- 3);
231 const char *q
= strchr (p
, '(');
235 } else if (!strncmp (lower
, "indefinite", 10)) {
236 timing
->type
= LaughTimingIndefinite
;
237 } else if (!strncmp (lower
, "media", 5)) {
238 timing
->type
= LaughTimingMedia
;
240 if (LaughTimingUnknown
== timing
->type
) {
241 const gchar
*idref_start
= p
;
246 last_esc
= last_esc
? 0 : 1;
247 } else if (*p
== '.' && !last_esc
) {
252 idref
= g_strdup (idref_start
);
254 idref
= g_strndup (idref_start
, p
- idref_start
);
258 timing
->element_id
= idref
;
259 if (_timing_parse_time (p
, &timing
->offset
)) {
260 timing
->type
= LaughTimingStartSync
;
261 } else if (*p
&& !strncmp (p
, "end", 3)) {
262 timing
->type
= LaughTimingEndSync
;
263 _timing_parse_time (p
+3, &timing
->offset
);
264 } else if (*p
&& !strncmp (p
, "begin", 5)) {
265 timing
->type
= LaughTimingStartSync
;
266 _timing_parse_time (p
+5, &timing
->offset
);
267 } else if (*p
&& !strncmp (p
, "activateevent", 13)) {
268 timing
->type
= LaughTimingActivated
;
269 _timing_parse_time (p
+13, &timing
->offset
);
270 } else if (*p
&& !strncmp (p
, "inboundsevent", 13)) {
271 timing
->type
= LaughTimingInbounds
;
272 _timing_parse_time (p
+13, &timing
->offset
);
273 } else if (*p
&& !strncmp (p
, "outofboundsevent", 16)) {
274 timing
->type
= LaughTimingOutbounds
;
275 _timing_parse_time (p
+16, &timing
->offset
);
277 g_printerr ("get_timings no match %s", lower
);
285 LaughTiming
*laugh_timing_new ()
287 return g_new0 (LaughTiming
, 1);
290 void laugh_timing_delete (LaughTiming
*timing
)
292 if (timing
->element_id
)
293 g_free (timing
->element_id
);
294 if (LaughTimingTime
== timing
->type
&& timing
->handler_id
)
295 g_source_remove (timing
->handler_id
);
299 LaughRoleTiming
*laugh_timing_role_new (LaughNode
*node
)
301 LaughRoleTiming
*segment
= g_new0 (LaughRoleTiming
, 1);
303 segment
->role
.type
= LaughRoleTypeTiming
;
304 segment
->node
= node
;
309 void laugh_timing_role_delete (LaughRoleTiming
*segment
)
311 LaughRoleTiming
*parent
= segment
->parent
;
314 laugh_timing_delete (segment
->begin
);
316 laugh_timing_delete (segment
->dur
);
318 laugh_timing_delete (segment
->end
);
320 if (segment
->sub_segments
)
321 g_printerr ("TimingSegment %s leaking sub segments\n",
322 laugh_tag_from_id (segment
->node
->id
));
325 GSList
*s
= parent
->sub_segments
;
326 for ( ;s
; s
= s
->next
) {
327 GSList
*thread
= (GSList
*) s
->data
;
328 if (g_slist_find (thread
, segment
)) {
329 thread
= g_slist_remove (thread
, segment
);
331 parent
->sub_segments
= g_slist_remove (parent
->sub_segments
,
342 LaughTiming
*laugh_timing_new_from_string (const gchar
*value
)
344 LaughTiming
*t
= laugh_timing_new ();
345 if (laugh_timing_set_value (t
, value
))
347 laugh_timing_delete (t
);
351 void laugh_timing_role_add (LaughRoleTiming
*s
, LaughRoleTiming
*child
)
353 if (!s
->sub_segments
|| LaughTagIdPar
== s
->node
->id
) {
354 s
->sub_segments
= g_slist_append (s
->sub_segments
,
355 g_slist_append (NULL
, child
));
357 GSList
*last
= g_slist_last (s
->sub_segments
);
358 last
->data
= g_slist_append ((GSList
*) last
->data
, child
);
364 gboolean
laugh_timing_setting_set_attribute (LaughRoleTiming
*segment
,
365 LaughNodeAttributeId att
, const gchar
*val
)
367 LaughTiming
**timing
;
370 case LaughAttrIdBegin
:
371 timing
= &segment
->begin
;
374 timing
= &segment
->dur
;
377 timing
= &segment
->end
;
384 laugh_timing_set_value (*timing
, val
);
386 laugh_timing_delete (*timing
);
390 *timing
= laugh_timing_new_from_string (val
);
396 void _laugh_timing_stop (LaughTiming
*timing
)
398 if (timing
&& timing
->handler_id
) {
399 switch (timing
->type
) {
400 case LaughTimingTime
:
401 g_source_remove (timing
->handler_id
);
403 /*TODO disconnect other types
404 g_signal_handler_disconnect (timing->handler_id);*/
408 timing
->handler_id
= 0;
412 static gboolean
_laugh_timing_timeout (gpointer data
);
414 static void _laugh_timing_setting_stop_freeze (LaughRoleTiming
*segment
)
418 _laugh_timing_stop (segment
->begin
);
419 _laugh_timing_stop (segment
->dur
);
420 _laugh_timing_stop (segment
->end
);
422 /*TODO determine freeze */
423 for (s
= segment
->sub_segments
; s
; s
= s
->next
) {
425 for (sub
= (GSList
*) s
->data
; sub
; sub
= sub
->next
) {
426 LaughRoleTiming
*seg
= (LaughRoleTiming
*) sub
->data
;
427 if (seg
->active
|| LaughStateFreezed
== seg
->node
->state
) {
428 laugh_timing_setting_stop (seg
);
434 segment
->active
= FALSE
;
437 static void _laugh_timing_setting_stopped (LaughRoleTiming
*segment
)
440 LaughRoleTiming
*parent
= segment
->parent
;
441 gboolean last_one
= TRUE
;
442 LaughRoleTiming
*start_next
= NULL
;
443 gboolean freeze
= FALSE
;
444 gboolean found
= FALSE
;
446 g_printf ("laugh_timing_setting_stopped %s\n",
447 laugh_tag_from_id (segment
->node
->id
));
450 for (s
= parent
->sub_segments
; s
; s
= s
->next
) {
451 GSList
*sub
= (GSList
*) s
->data
;
452 GSList
*l
= found
? NULL
: g_slist_find (sub
, segment
);
456 start_next
= (LaughRoleTiming
*) l
->next
->data
;
459 const gchar
*fill
= laugh_node_get_attribute (
460 segment
->node
, LaughAttrIdFill
);
461 if ((!fill
&& !segment
->dur
&& !segment
->end
) ||
462 (fill
&& !strcmp (fill
, "freeze")) ||
463 (fill
&& !strcmp (fill
, "hold")))
466 } else if (last_one
) {
467 LaughRoleTiming
*seg
=
468 (LaughRoleTiming
*) g_slist_last (sub
)->data
;
469 last_one
= seg
->node
->state
>= LaughStateStopped
;
471 if (found
&& !last_one
)
476 _laugh_timing_setting_stop_freeze (segment
);
477 laugh_node_freeze (segment
->node
);
479 laugh_timing_setting_stop (segment
);
483 laugh_timing_setting_start (start_next
);
486 (parent
->dur
->type
== LaughTimingTime
&& !parent
->dur
->handler_id
)))
487 _laugh_timing_setting_stopped (parent
);
489 g_printf ("freeze %s\n", laugh_tag_from_id (segment
->node
->id
));
492 laugh_timing_setting_stop (segment
);
496 static void _laugh_timing_setting_started (LaughRoleTiming
*segment
)
500 g_printf ("laugh_timing_setting_started %s\n",
501 laugh_tag_from_id (segment
->node
->id
));
502 laugh_node_start (segment
->node
);
504 if (segment
->dur
&& LaughTimingTime
== segment
->dur
->type
) {
505 if (segment
->dur
->offset
> 0)
506 segment
->dur
->handler_id
= g_timeout_add (segment
->dur
->offset
,
507 _laugh_timing_timeout
, segment
);
509 _laugh_timing_setting_stopped (segment
);
512 if (segment
->active
) {
513 if (segment
->sub_segments
) {
514 for (s
= segment
->sub_segments
; s
&& segment
->active
; s
= s
->next
)
515 laugh_timing_setting_start (
516 (LaughRoleTiming
*)((GSList
*) s
->data
)->data
);
517 } else if (!segment
->dur
&& !segment
->end
) {
518 _laugh_timing_setting_stopped (segment
);
523 static gboolean
_laugh_timing_timeout (gpointer data
)
525 LaughRoleTiming
*segment
= (LaughRoleTiming
*) data
;
527 if (segment
->begin
&& segment
->begin
->handler_id
) {
528 laugh_timing_notify (segment
, segment
->begin
->handler_id
);
529 segment
->begin
->handler_id
= 0;
530 } else if (segment
->dur
&& segment
->dur
->handler_id
) {
531 laugh_timing_notify (segment
, segment
->dur
->handler_id
);
532 segment
->dur
->handler_id
= 0;
535 return FALSE
; /*TODO handle repeat*/
538 gboolean
laugh_timing_setting_start (LaughRoleTiming
*segment
)
540 LaughTimingType tt
= LaughTimingTime
;
543 g_printf ("laugh_timing_setting_start %s\n",
544 laugh_tag_from_id (segment
->node
->id
));
545 segment
->active
= TRUE
;
546 segment
->start_time_stamp
= clutter_get_timestamp ();
548 if (segment
->begin
) {
549 tt
= segment
->begin
->type
;
550 offset
= segment
->begin
->offset
;
554 case LaughTimingTime
:
556 segment
->begin
->handler_id
= g_timeout_add (offset
,
557 _laugh_timing_timeout
, segment
);
559 _laugh_timing_setting_started (segment
);
561 case LaughTimingStartSync
:
562 if (segment
->begin
->element_id
) {
563 LaughRoleTiming
*role
;
564 LaughNode
*elm
= laugh_document_get_element_by_id (
565 segment
->node
->document
,
566 laugh_document_id_from_string (
567 segment
->node
->document
, segment
->begin
->element_id
));
570 role
= (LaughRoleTiming
*) laugh_node_role_get (elm
, LaughRoleTypeTiming
);
573 if (elm
->state
>= LaughStateBegun
) {
574 glong diff
= (clutter_get_timestamp ()-role
->start_time_stamp
) / 1000;
576 segment
->begin
->handler_id
= g_timeout_add (offset
- diff
,
577 _laugh_timing_timeout
, segment
);
579 _laugh_timing_setting_started (segment
);
581 g_printf ("TODO: laugh_timing_setting_start StartSync\n");
592 void laugh_timing_notify (LaughRoleTiming
*segment
, gulong handler_id
)
594 if (segment
->begin
&& segment
->begin
->handler_id
== handler_id
) {
595 _laugh_timing_setting_started (segment
);
596 } else if (segment
->dur
&& segment
->dur
->handler_id
== handler_id
) {
597 _laugh_timing_setting_stopped (segment
);
601 void laugh_timing_setting_stop (LaughRoleTiming
*segment
)
603 g_printf ("laugh_timing_setting_stop %s\n",
604 laugh_tag_from_id (segment
->node
->id
));
606 _laugh_timing_setting_stop_freeze (segment
);
608 laugh_node_stop (segment
->node
);
611 static LaughRoleTiming
*
612 _laugh_timing_settting_find (LaughRoleTiming
*segment
, const LaughNode
*node
)
614 if (segment
->node
== node
)
616 if (segment
->sub_segments
) {
618 for (s
= segment
->sub_segments
; s
; s
= s
->next
) {
620 for (sub
= (GSList
*) s
->data
; sub
; sub
= sub
->next
) {
621 LaughRoleTiming
*seg
= _laugh_timing_settting_find (
622 (LaughRoleTiming
*) sub
->data
, node
);
631 static void _laugh_timing_settting_sub_segments_force_stop (LaughRoleTiming
*segment
)
634 for (s
= segment
->sub_segments
; s
; s
= s
->next
) {
636 for (sub
= (GSList
*) s
->data
; sub
; sub
= sub
->next
) {
637 LaughRoleTiming
*seg
= (LaughRoleTiming
*) sub
->data
;
639 laugh_timing_setting_stop (seg
);
646 static void _laugh_timing_settting_recursive_start (LaughRoleTiming
*segment
)
649 g_printerr ("jump target starting underflow error\n");
652 if (!segment
->active
) {
653 _laugh_timing_settting_recursive_start (segment
->parent
);
654 laugh_timing_setting_start (segment
);
658 void laugh_timing_setting_jump (LaughNode
*target
)
660 LaughRoleTiming
*segment
= target
->document
->timing
;
661 LaughRoleTiming
*seg
;
663 if (LaughStateBegun
== target
->state
) {
664 g_printerr ("jump target already active\n");
668 segment
= _laugh_timing_settting_find (segment
, target
);
670 g_printerr ("jump target not in tree\n");
674 for (seg
= segment
->parent
; seg
; seg
= seg
->parent
)
676 g_printf ("laugh_timing_setting_jump force stop %s\n",
677 laugh_tag_from_id (seg
->node
->id
));
678 if (LaughTagIdPar
!= seg
->node
->id
)
679 _laugh_timing_settting_sub_segments_force_stop (seg
);
680 _laugh_timing_settting_recursive_start (segment
);
685 LaughNode
*laugh_timing_container_new (LaughDocument
*doc
, LaughNodeTagId id
,
686 GHashTable
*attributes
)
688 LaughNode
*n
= (LaughNode
*)g_object_new(LAUGH_TYPE_TIMING_CONTAINER
, NULL
);
690 laugh_node_base_construct (doc
, n
, id
, attributes
);
695 int laugh_timing_set_value (LaughTiming
*timing
, const gchar
*value
)
697 return _get_timing (value
, timing
);
700 /*#define TIMING_TEST*/
704 /* gcc laugh-timing.c -o timing-test `pkg-config --cflags --libs glib-2.0` -DTIMING_TEST*/
706 #define TEST_PARSE_TIME(s) \
709 int b = _timing_parse_time(s, &val); \
710 g_printf( "_timing_parse_time(%s) => %d: %d\n", (s), b, val); \
713 const char *timing_string (LaughTimingType tt
) {
715 case LaughTimingUnknown
:
717 case LaughTimingTime
:
719 case LaughTimingIndefinite
:
721 case LaughTimingMedia
:
723 case LaughTimingActivated
:
725 case LaughTimingInbounds
:
727 case LaughTimingOutbounds
:
729 case LaughTimingEndSync
:
731 case LaughTimingStartSync
:
739 #define TEST_GET_TIMING(s) \
741 LaughTiming *t = laugh_timing_new_from_string(s); \
742 g_printf( "_get_timing %s => target %s event %s offset%d\n", \
743 (s), t->element_id, timing_string(t->type), t->offset); \
744 laugh_timing_delete (t); \
748 TEST_PARSE_TIME("12");
749 TEST_PARSE_TIME("12s");
750 TEST_PARSE_TIME("12m");
751 TEST_PARSE_TIME("12h");
752 TEST_PARSE_TIME("12.4");
753 TEST_PARSE_TIME("12.4s");
754 TEST_PARSE_TIME("12.4m");
755 TEST_PARSE_TIME("12.4h");
756 TEST_PARSE_TIME(".4");
757 TEST_PARSE_TIME(".4s");
758 TEST_PARSE_TIME("+.4");
759 TEST_PARSE_TIME("+.4s");
760 TEST_PARSE_TIME("-.4");
761 TEST_PARSE_TIME("-.4s");
762 TEST_GET_TIMING("4");
763 TEST_GET_TIMING("myimage.activateEvent");
764 TEST_GET_TIMING("myimage.activateEvent+2");
765 TEST_GET_TIMING("myimage.endSync");
766 TEST_GET_TIMING("myimage.endSync-2m");
767 TEST_GET_TIMING("indefinite");