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>
28 #include "laugh-timing.h"
30 #define LAUGH_TIMING_CONTAINER_GET_PRIVATE(obj) \
31 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), LAUGH_TYPE_TIMING_CONTAINER, LaughTimingContainerPrivate))
34 static gpointer laugh_timing_container_parent_class
= ((void *)0);
36 static void laugh_timing_container_finalize (GObject
*object
)
38 G_OBJECT_CLASS (laugh_timing_container_parent_class
)->finalize (object
);
41 static void laugh_timing_container_dispose (GObject
*object
)
43 LaughTimingContainer
*self
= LAUGH_TIMING_CONTAINER(object
);
44 LaughTimingSegment
*segment
= self
->timing_segment
;
46 G_OBJECT_CLASS (laugh_timing_container_parent_class
)->dispose (object
);
48 laugh_timing_segment_delete (segment
);
52 _laugh_timing_container_init (LaughNode
*node
, LaughInitializer
*initializer
)
54 LaughTimingContainer
*self
= (LaughTimingContainer
*) node
;
56 LaughTimingSegment
*parent_segment
= initializer
->parent_segment
;
59 g_hash_table_foreach (node
->attributes
, laugh_attributes_set
, node
);
61 laugh_timing_segment_add(initializer
->parent_segment
, self
->timing_segment
);
63 initializer
->parent_segment
= self
->timing_segment
;
65 for (child
= node
->first_child
; child
; child
= child
->next_sibling
)
66 laugh_node_init (child
, initializer
);
68 initializer
->parent_segment
= parent_segment
;
71 static void _laugh_timing_container_set_attribute (LaughNode
*node
,
72 LaughNodeAttributeId att
, const gchar
*value
, gpointer
*undo
)
74 const gchar
*val
= value
;
75 LaughTimingContainer
*self
= (LaughTimingContainer
*)node
;
77 laugh_node_base_set_attribute (node
, att
, val
, undo
);
78 g_printf ("_laugh_timing_container_set_attribute %s=%s\n", laugh_attribute_from_id (att
), val
);
81 val
= *(const gchar
**)undo
;
83 laugh_timing_setting_set_attribute (self
->timing_segment
, att
, val
);
86 static void laugh_timing_container_class_init (LaughTimingContainerClass
*klass
)
88 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
89 LaughNodeClass
*node_class
= (LaughNodeClass
*) klass
;
91 laugh_timing_container_parent_class
= g_type_class_peek_parent (klass
);
93 gobject_class
->finalize
= laugh_timing_container_finalize
;
94 gobject_class
->dispose
= laugh_timing_container_dispose
;
95 node_class
->init
= _laugh_timing_container_init
;
96 node_class
->set_attribute
= _laugh_timing_container_set_attribute
;
98 /*g_type_class_add_private (gobject_class, sizeof (LaughTimingContainerPrivate));*/
102 void laugh_timing_container_instance_init (GTypeInstance
*instance
, gpointer g_class
)
104 LaughTimingContainer
*self
= (LaughTimingContainer
*)instance
;
106 /*self->priv = LAUGH_TIMING_CONTAINER_GET_PRIVATE (self);*/
108 self
->timing_segment
= g_new0 (LaughTimingSegment
, 1);
109 self
->timing_segment
->node
= (LaughNode
*) self
;
112 GType
laugh_timing_container_get_type (void)
114 static GType type
= 0;
116 static const GTypeInfo info
= {
117 sizeof (LaughTimingContainerClass
),
118 NULL
, /* base_init */
119 NULL
, /* base_finalize */
120 (GClassInitFunc
) laugh_timing_container_class_init
, /* class_init */
121 NULL
, /* class_finalize */
122 NULL
, /* class_data */
123 sizeof (LaughTimingContainer
),
125 laugh_timing_container_instance_init
/* instance_init */
127 type
= g_type_register_static (LAUGH_TYPE_NODE
,
128 "LaughTimingContainerType",
134 static int _timing_parse_time (const gchar
*tstr
, int *value
) {
137 const gchar
*nstart
= NULL
;
138 const gchar
*p
= tstr
;
150 } else if (*p
== '-') {
155 } else if (*p
>= '0' && *p
<= '9') {
158 } else if (*p
== '.') {
164 } else if (*p
== ' ') {
173 num
= g_strndup (nstart
, p
- tstr
);
174 *value
= sign
* (int) (1000 * g_strtod (nstart
, NULL
));
180 } else if (*p
== 'h') {
183 } else if (*p
!= ' ')
192 static int _get_timing (const gchar
*tstr
, LaughTiming
*timing
) {
194 const gchar
*p
= tstr
;
202 timing
->type
= TimingUnknown
;
206 lower
= g_ascii_strdown (p
, -1);
209 if (_timing_parse_time (lower
, &timing
->offset
)) {
210 timing
->type
= TimingTime
;
211 } else if (!strncmp (lower
, "id(", 3)) {
212 p
= (const gchar
*) strchr (lower
+ 3, ')');
214 idref
= g_strndup (lower
+ 3, p
- lower
- 3);
218 const char *q
= strchr (p
, '(');
222 } else if (!strncmp (lower
, "indefinite", 10)) {
223 timing
->type
= TimingIndefinite
;
224 } else if (!strncmp (lower
, "media", 5)) {
225 timing
->type
= TimingMedia
;
227 if (TimingUnknown
== timing
->type
) {
228 const gchar
*idref_start
= p
;
233 last_esc
= last_esc
? 0 : 1;
234 } else if (*p
== '.' && !last_esc
) {
239 idref
= g_strdup (idref_start
);
241 idref
= g_strndup (idref_start
, p
- idref_start
);
245 timing
->element_id
= idref
;
246 if (_timing_parse_time (p
, &timing
->offset
)) {
247 timing
->type
= TimingStartSync
;
248 } else if (*p
&& !strncmp (p
, "end", 3)) {
249 timing
->type
= TimingEndSync
;
250 _timing_parse_time (p
+3, &timing
->offset
);
251 } else if (*p
&& !strncmp (p
, "begin", 5)) {
252 timing
->type
= TimingStartSync
;
253 _timing_parse_time (p
+5, &timing
->offset
);
254 } else if (*p
&& !strncmp (p
, "activateevent", 13)) {
255 timing
->type
= TimingActivated
;
256 _timing_parse_time (p
+13, &timing
->offset
);
257 } else if (*p
&& !strncmp (p
, "inboundsevent", 13)) {
258 timing
->type
= TimingInbounds
;
259 _timing_parse_time (p
+13, &timing
->offset
);
260 } else if (*p
&& !strncmp (p
, "outofboundsevent", 16)) {
261 timing
->type
= TimingOutbounds
;
262 _timing_parse_time (p
+16, &timing
->offset
);
264 g_printerr ("get_timings no match %s", lower
);
272 LaughTiming
*laugh_timing_new ()
274 return g_new0 (LaughTiming
, 1);
277 void laugh_timing_delete (LaughTiming
*timing
)
279 if (timing
->element_id
)
280 g_free (timing
->element_id
);
284 void laugh_timing_segment_delete (LaughTimingSegment
*segment
)
286 LaughTimingSegment
*parent
= segment
->parent
;
289 laugh_timing_delete (segment
->begin
);
291 laugh_timing_delete (segment
->dur
);
293 laugh_timing_delete (segment
->end
);
295 if (segment
->sub_segments
)
296 g_printerr ("TimingSegment %s leaking sub segments\n",
297 laugh_tag_from_id (segment
->node
->id
));
300 GSList
*s
= parent
->sub_segments
;
301 for ( ;s
; s
= s
->next
) {
302 GSList
*thread
= (GSList
*) s
->data
;
303 if (g_slist_find (thread
, segment
)) {
304 thread
= g_slist_remove (thread
, segment
);
306 parent
->sub_segments
= g_slist_remove (parent
->sub_segments
,
317 LaughTiming
*laugh_timing_new_from_string (const gchar
*value
)
319 LaughTiming
*t
= laugh_timing_new ();
320 if (laugh_timing_set_value (t
, value
))
322 laugh_timing_delete (t
);
326 void laugh_timing_segment_add (LaughTimingSegment
*s
, LaughTimingSegment
*child
)
328 if (!s
->sub_segments
|| LaughTagIdPar
== s
->node
->id
) {
329 s
->sub_segments
= g_slist_append (s
->sub_segments
,
330 g_slist_append (NULL
, child
));
332 GSList
*last
= g_slist_last (s
->sub_segments
);
333 last
->data
= g_slist_append ((GSList
*) last
->data
, child
);
339 gboolean
laugh_timing_setting_set_attribute (LaughTimingSegment
*segment
,
340 LaughNodeAttributeId att
, const gchar
*val
)
342 LaughTiming
**timing
;
345 case LaughAttrIdBegin
:
346 timing
= &segment
->begin
;
349 timing
= &segment
->dur
;
352 timing
= &segment
->end
;
359 laugh_timing_set_value (*timing
, val
);
361 laugh_timing_delete (*timing
);
365 *timing
= laugh_timing_new_from_string (val
);
371 void _laugh_timing_stop (LaughTiming
*timing
)
373 if (timing
&& timing
->handler_id
) {
374 switch (timing
->type
) {
376 g_source_remove (timing
->handler_id
);
378 /*TODO disconnect other types
379 g_signal_handler_disconnect (timing->handler_id);*/
383 timing
->handler_id
= 0;
387 static gboolean
_laugh_timing_timeout (gpointer data
);
389 static void _laugh_timing_setting_stopped (LaughTimingSegment
*segment
)
392 LaughTimingSegment
*parent
= segment
->parent
;
393 gboolean last_one
= TRUE
;
395 g_printf ("laugh_timing_setting_stopped %s\n",
396 laugh_tag_from_id (segment
->node
->id
));
397 laugh_timing_setting_stop (segment
);
400 for (s
= parent
->sub_segments
; s
; s
= s
->next
) {
401 GSList
*l
= g_slist_find ((GSList
*) s
->data
, segment
);
404 laugh_timing_setting_start ((LaughTimingSegment
*)l
->next
->data
);
406 } else if (last_one
) {
407 LaughTimingSegment
*seg
=
408 (LaughTimingSegment
*) g_slist_last (l
)->data
;
409 last_one
&= seg
->node
->state
>= LaughStateStopped
;
414 if (last_one
&& (!parent
->dur
|| !parent
->dur
->handler_id
))
415 _laugh_timing_setting_stopped (parent
);
419 static void _laugh_timing_setting_started (LaughTimingSegment
*segment
)
423 g_printf ("laugh_timing_setting_started %s\n",
424 laugh_tag_from_id (segment
->node
->id
));
425 laugh_node_start (segment
->node
);
427 for (s
= segment
->sub_segments
; s
; s
= s
->next
)
428 laugh_timing_setting_start (
429 (LaughTimingSegment
*)((GSList
*) s
->data
)->data
);
432 if (segment
->dur
->offset
> 0)
433 segment
->dur
->handler_id
= g_timeout_add (segment
->dur
->offset
,
434 _laugh_timing_timeout
, segment
);
436 _laugh_timing_setting_stopped (segment
);
440 static gboolean
_laugh_timing_timeout (gpointer data
)
442 LaughTimingSegment
*segment
= (LaughTimingSegment
*) data
;
444 if (segment
->begin
&& segment
->begin
->handler_id
) {
445 segment
->begin
->handler_id
= 0;
446 _laugh_timing_setting_started (segment
);
447 } else if (segment
->dur
&& segment
->dur
->handler_id
) {
448 segment
->dur
->handler_id
= 0;
449 _laugh_timing_setting_stopped (segment
);
452 return FALSE
; /*TODO handle repeat*/
455 gboolean
laugh_timing_setting_start (LaughTimingSegment
*segment
)
457 LaughTimingType tt
= TimingTime
;
460 g_printf ("laugh_timing_setting_start %s\n",
461 laugh_tag_from_id (segment
->node
->id
));
462 segment
->active
= TRUE
;
464 if (segment
->begin
) {
465 tt
= segment
->begin
->type
;
466 offset
= segment
->begin
->offset
;
472 segment
->begin
->handler_id
= g_timeout_add (offset
,
473 _laugh_timing_timeout
, segment
);
475 _laugh_timing_setting_started (segment
);
485 void laugh_timing_setting_stop (LaughTimingSegment
*segment
)
489 g_printf ("laugh_timing_setting_stop %s\n",
490 laugh_tag_from_id (segment
->node
->id
));
491 _laugh_timing_stop (segment
->begin
);
492 _laugh_timing_stop (segment
->dur
);
493 _laugh_timing_stop (segment
->end
);
495 /*TODO determine freeze */
496 for (s
= segment
->sub_segments
; s
; s
= s
->next
) {
498 for (sub
= (GSList
*) s
->data
; sub
; sub
= sub
->next
) {
499 LaughTimingSegment
*seg
= (LaughTimingSegment
*) sub
->data
;
501 laugh_timing_setting_stop (seg
);
507 segment
->active
= FALSE
;
509 laugh_node_stop (segment
->node
);
512 LaughNode
*laugh_timing_container_new (LaughDocument
*doc
, LaughNodeTagId id
,
513 GHashTable
*attributes
)
515 LaughNode
*n
= (LaughNode
*)g_object_new(LAUGH_TYPE_TIMING_CONTAINER
, NULL
);
517 laugh_node_base_construct (doc
, n
, id
, attributes
);
522 int laugh_timing_set_value (LaughTiming
*timing
, const gchar
*value
)
524 return _get_timing (value
, timing
);
527 /*#define TIMING_TEST*/
531 /* gcc laugh-timing.c -o timing-test `pkg-config --cflags --libs glib-2.0` -DTIMING_TEST*/
533 #define TEST_PARSE_TIME(s) \
536 int b = _timing_parse_time(s, &val); \
537 g_printf( "_timing_parse_time(%s) => %d: %d\n", (s), b, val); \
540 const char *timing_string (LaughTimingType tt
) {
546 case TimingIndefinite
:
550 case TimingActivated
:
554 case TimingOutbounds
:
558 case TimingStartSync
:
566 #define TEST_GET_TIMING(s) \
568 LaughTiming *t = laugh_timing_new_from_string(s); \
569 g_printf( "_get_timing %s => target %s event %s offset%d\n", \
570 (s), t->element_id, timing_string(t->type), t->offset); \
571 laugh_timing_delete (t); \
575 TEST_PARSE_TIME("12");
576 TEST_PARSE_TIME("12s");
577 TEST_PARSE_TIME("12m");
578 TEST_PARSE_TIME("12h");
579 TEST_PARSE_TIME("12.4");
580 TEST_PARSE_TIME("12.4s");
581 TEST_PARSE_TIME("12.4m");
582 TEST_PARSE_TIME("12.4h");
583 TEST_PARSE_TIME(".4");
584 TEST_PARSE_TIME(".4s");
585 TEST_PARSE_TIME("+.4");
586 TEST_PARSE_TIME("+.4s");
587 TEST_PARSE_TIME("-.4");
588 TEST_PARSE_TIME("-.4s");
589 TEST_GET_TIMING("4");
590 TEST_GET_TIMING("myimage.activateEvent");
591 TEST_GET_TIMING("myimage.activateEvent+2");
592 TEST_GET_TIMING("myimage.endSync");
593 TEST_GET_TIMING("myimage.endSync-2m");
594 TEST_GET_TIMING("indefinite");