initial pass at #ifdef'ing xml load/save
[platform/upstream/gstreamer.git] / gst / gstobject.c
1 /* GStreamer
2  * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3  *                    2000 Wim Taymans <wtay@chello.be>
4  *
5  * gstobject.c: Fundamental class used for all of GStreamer
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include "gst_private.h"
24
25 #include "gstobject.h"
26
27 /* Object signals and args */
28 enum {
29   PARENT_SET,
30 #ifndef GST_DISABLE_XML
31   OBJECT_SAVED,
32 #endif
33   LAST_SIGNAL
34 };
35
36 enum {
37   ARG_0,
38   /* FILL ME */
39 };
40
41 enum {
42   SO_OBJECT_LOADED,
43   SO_LAST_SIGNAL
44 };
45
46 typedef struct _GstSignalObject GstSignalObject;
47 typedef struct _GstSignalObjectClass GstSignalObjectClass;
48
49 static GtkType          gst_signal_object_get_type      (void);
50 static void             gst_signal_object_class_init    (GstSignalObjectClass *klass);
51 static void             gst_signal_object_init          (GstSignalObject *object);
52
53 static guint gst_signal_object_signals[SO_LAST_SIGNAL] = { 0 };
54
55 static void             gst_object_class_init           (GstObjectClass *klass);
56 static void             gst_object_init                 (GstObject *object);
57
58 static void             gst_object_real_destroy         (GtkObject *gtk_object);
59 static void             gst_object_shutdown             (GtkObject *gtk_object);
60 static void             gst_object_finalize             (GtkObject *gtk_object);
61
62 static GtkObjectClass *parent_class = NULL;
63 static guint gst_object_signals[LAST_SIGNAL] = { 0 };
64
65 void
66 gst_object_inititialize (void)
67 {
68 }
69
70 GtkType
71 gst_object_get_type (void)
72 {
73   static GtkType object_type = 0;
74
75   if (!object_type) {
76     static const GtkTypeInfo object_info = {
77       "GstObject",
78       sizeof(GstObject),
79       sizeof(GstObjectClass),
80       (GtkClassInitFunc)gst_object_class_init,
81       (GtkObjectInitFunc)gst_object_init,
82       (GtkArgSetFunc)NULL,
83       (GtkArgGetFunc)NULL,
84       (GtkClassInitFunc)NULL,
85     };
86     object_type = gtk_type_unique(gtk_object_get_type(),&object_info);
87   }
88   return object_type;
89 }
90
91 static void
92 gst_object_class_init (GstObjectClass *klass)
93 {
94   GtkObjectClass *gtkobject_class;
95
96   gtkobject_class = (GtkObjectClass*) klass;
97
98   parent_class = gtk_type_class (gtk_object_get_type ());
99
100   gst_object_signals[PARENT_SET] =
101     gtk_signal_new ("parent_set", GTK_RUN_LAST, gtkobject_class->type,
102                     GTK_SIGNAL_OFFSET (GstObjectClass, parent_set),
103                     gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1,
104                     GST_TYPE_OBJECT);
105 #ifndef GST_DISABLE_XML
106   gst_object_signals[OBJECT_SAVED] =
107     gtk_signal_new ("object_saved", GTK_RUN_LAST, gtkobject_class->type,
108                     GTK_SIGNAL_OFFSET (GstObjectClass, object_saved),
109                     gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1,
110                     GTK_TYPE_POINTER);
111 #endif
112   gtk_object_class_add_signals (gtkobject_class, gst_object_signals, LAST_SIGNAL);
113
114   klass->path_string_separator = "/";
115   klass->signal_object = gtk_type_new (gst_signal_object_get_type ());
116
117   gtkobject_class->shutdown = gst_object_shutdown;
118   gtkobject_class->destroy = gst_object_real_destroy;
119   gtkobject_class->finalize = gst_object_finalize;
120 }
121
122 static void
123 gst_object_init (GstObject *object)
124 {
125   GstObjectClass *oclass;
126
127   oclass = GST_OBJECT_CLASS (GTK_OBJECT (object)->klass);
128
129   object->lock = g_mutex_new();
130 #ifdef HAVE_ATOMIC_H
131   atomic_set(&(object->refcount),1);
132 #else
133   object->refcount = 1;
134 #endif
135   object->parent = NULL;
136
137   object->flags = 0;
138   GST_FLAG_SET (object, GST_FLOATING);
139 }
140
141 /**
142  * gst_object_new:
143  *
144  * Create a new, empty object.  Not very useful, should never be used.
145  *
146  * Returns: new object
147  */
148 GstObject*
149 gst_object_new (void)
150 {
151   return GST_OBJECT (gtk_type_new (gst_object_get_type ()));
152 }
153
154 /**
155  * gst_object_ref:
156  * @object: GstObject to reference
157  *
158  * Increments the refence count on the object.
159  *
160  * Returns: A pointer to the object
161  */
162 GstObject*
163 gst_object_ref (GstObject *object)
164 {
165   g_return_val_if_fail (GST_IS_OBJECT (object), NULL);
166
167   GST_DEBUG (GST_CAT_REFCOUNTING, "ref '%s' %d->%d\n",GST_OBJECT_NAME(object),
168              GTK_OBJECT(object)->ref_count,GTK_OBJECT(object)->ref_count+1);
169
170   gtk_object_ref (GTK_OBJECT (object));
171
172   return object;
173 }
174 #define gst_object_ref gst_object_ref
175
176 /**
177  * gst_object_unref:
178  * @object: GstObject to unreference
179  *
180  * Decrements the refence count on the object.  If reference count hits
181  * zero, destroy the object.
182  */
183 void
184 gst_object_unref (GstObject *object)
185 {
186   g_return_if_fail (GST_IS_OBJECT (object));
187
188   GST_DEBUG (GST_CAT_REFCOUNTING, "unref '%s' %d->%d\n",GST_OBJECT_NAME(object),
189              GTK_OBJECT(object)->ref_count,GTK_OBJECT(object)->ref_count-1);
190
191   gtk_object_unref (GTK_OBJECT (object));
192 }
193 #define gst_object_unref gst_object_unref
194
195 /**
196  * gst_object_sink:
197  * @object: GstObject to sink
198  *
199  * Removes floating reference on an object.  Any newly created object has
200  * a refcount of 1 and is FLOATING.  This function should be used when
201  * creating a new object to symbolically 'take ownership of' the object.
202  */
203 void
204 gst_object_sink (GstObject *object)
205 {
206   g_return_if_fail (object != NULL);
207   g_return_if_fail (GST_IS_OBJECT (object));
208
209   GST_DEBUG (GST_CAT_REFCOUNTING, "sink '%s'\n",GST_OBJECT_NAME(object));
210   if (GST_OBJECT_FLOATING (object))
211   {
212     GST_FLAG_UNSET (object, GST_FLOATING);
213     gst_object_unref (object);
214   }
215 }
216
217 void
218 gst_object_destroy (GstObject *object)
219 {
220   g_return_if_fail (object != NULL);
221   g_return_if_fail (GST_IS_OBJECT (object));
222
223   GST_DEBUG (GST_CAT_REFCOUNTING, "destroy '%s'\n",GST_OBJECT_NAME(object));
224   if (!GST_OBJECT_DESTROYED (object))
225   {
226     /* need to hold a reference count around all class method
227      * invocations.
228      */
229     gst_object_ref (object);
230     GTK_OBJECT (object)->klass->shutdown (GTK_OBJECT (object));
231     gst_object_unref (object);
232   }
233 }
234
235 static void
236 gst_object_shutdown (GtkObject *object)
237 {
238   GST_DEBUG (GST_CAT_REFCOUNTING, "shutdown '%s'\n",GST_OBJECT_NAME(object));
239   GST_FLAG_SET (GST_OBJECT (object), GST_DESTROYED);
240   parent_class->shutdown (GTK_OBJECT (object));
241 }
242
243 /* finilize is called when the object has to free its resources */
244 static void
245 gst_object_real_destroy (GtkObject *gtk_object)
246 {
247   GST_DEBUG (GST_CAT_REFCOUNTING, "destroy '%s'\n",GST_OBJECT_NAME(gtk_object));
248
249   GST_OBJECT_PARENT (gtk_object) = NULL;
250
251   parent_class->destroy (gtk_object);
252 }
253
254 /* finilize is called when the object has to free its resources */
255 static void
256 gst_object_finalize (GtkObject *gtk_object)
257 {
258   GstObject *object;
259
260   object = GST_OBJECT (gtk_object);
261
262   GST_DEBUG (GST_CAT_REFCOUNTING, "finalize '%s'\n",GST_OBJECT_NAME(object));
263
264   if (object->name != NULL)
265     g_free (object->name);
266
267   g_mutex_free (object->lock);
268
269   parent_class->finalize (gtk_object);
270 }
271
272 /**
273  * gst_object_set_name:
274  * @object: GstObject to set the name of
275  * @name: new name of object
276  *
277  * Set the name of the object.
278  */
279 void
280 gst_object_set_name (GstObject *object, const gchar *name)
281 {
282   g_return_if_fail (object != NULL);
283   g_return_if_fail (GST_IS_OBJECT (object));
284   g_return_if_fail (name != NULL);
285
286   if (object->name != NULL)
287     g_free (object->name);
288
289   object->name = g_strdup (name);
290 }
291
292 /**
293  * gst_object_get_name:
294  * @object: GstObject to get the name of
295  *
296  * Get the name of the object.
297  *
298  * Returns: name of the object
299  */
300 const gchar*
301 gst_object_get_name (GstObject *object)
302 {
303   g_return_val_if_fail (object != NULL, NULL);
304   g_return_val_if_fail (GST_IS_OBJECT (object), NULL);
305
306   return object->name;
307 }
308
309 /**
310  * gst_object_set_parent:
311  * @object: GstObject to set parent of
312  * @parent: new parent of object
313  *
314  * Set the parent of the object.  The object's reference count is
315  * incremented.
316  * signals the parent-set signal
317  */
318 void
319 gst_object_set_parent (GstObject *object, GstObject *parent)
320 {
321   g_return_if_fail (object != NULL);
322   g_return_if_fail (GST_IS_OBJECT (object));
323   g_return_if_fail (parent != NULL);
324   g_return_if_fail (GST_IS_OBJECT (parent));
325   g_return_if_fail (object != parent);
326
327   if (object->parent != NULL) {
328     GST_ERROR_OBJECT (object,object->parent, "object's parent is already set, must unparent first");
329     return;
330   }
331
332   gst_object_ref (object);
333   gst_object_sink (object);
334   object->parent = parent;
335
336   gtk_signal_emit (GTK_OBJECT (object), gst_object_signals[PARENT_SET], parent);
337 }
338
339 /**
340  * gst_object_get_parent:
341  * @object: GstObject to get parent of
342  *
343  * Return the parent of the object.
344  *
345  * Returns: parent of the object
346  */
347 GstObject*
348 gst_object_get_parent (GstObject *object)
349 {
350   g_return_val_if_fail (object != NULL, NULL);
351   g_return_val_if_fail (GST_IS_OBJECT (object), NULL);
352
353   return object->parent;
354 }
355
356 /**
357  * gst_object_unparent:
358  * @object: GstObject to unparent
359  *
360  * Clear the parent of the object, removing the associated reference.
361  */
362 void
363 gst_object_unparent (GstObject *object)
364 {
365   g_return_if_fail (object != NULL);
366   g_return_if_fail (GST_IS_OBJECT(object));
367   if (object->parent == NULL)
368     return;
369
370   object->parent = NULL;
371   gst_object_unref (object);
372 }
373
374 /**
375  * gst_object_ref:
376  * @object: GstObject to reference
377  *
378  * Increments the refence count on the object.
379  *
380  * Returns: Apointer to the Object
381  */
382 #ifndef gst_object_ref
383 GstObject*
384 gst_object_ref (GstObject *object)
385 {
386   g_return_if_fail (object != NULL, NULL);
387   g_return_if_fail (GST_IS_OBJECT (object), NULL);
388
389 #ifdef HAVE_ATOMIC_H
390   g_return_if_fail (atomic_read (&(object->refcount)) > 0);
391   atomic_inc (&(object->refcount))
392 #else
393   g_return_if_fail (object->refcount > 0);
394   GST_LOCK (object);
395   object->refcount++;
396   GST_UNLOCK (object);
397 #endif
398
399   return object;
400 }
401 #endif /* gst_object_ref */
402
403 /**
404  * gst_object_unref:
405  * @object: GstObject to unreference
406  *
407  * Decrements the refence count on the object.  If reference count hits
408  * zero, destroy the object.
409  */
410 #ifndef gst_object_unref
411 void
412 gst_object_unref (GstObject *object)
413 {
414   int reftest;
415
416   g_return_if_fail (object != NULL);
417   g_return_if_fail (GST_IS_OBJECT (object));
418
419 #ifdef HAVE_ATOMIC_H
420   g_return_if_fail (atomic_read (&(object->refcount)) > 0);
421   reftest = atomic_dec_and_test (&(object->refcount))
422 #else
423   g_return_if_fail (object->refcount > 0);
424   GST_LOCK (object);
425   object->refcount--;
426   reftest = (object->refcount == 0);
427   GST_UNLOCK (object);
428 #endif
429
430   /* if we ended up with the refcount at zero */
431   if (reftest) {
432     /* get the count to 1 for gtk_object_destroy() */
433 #ifdef HAVE_ATOMIC_H
434     atomic_set (&(object->refcount),1);
435 #else
436     object->refcount = 1;
437 #endif
438     /* destroy it */
439     gtk_object_destroy (GTK_OBJECT (object));
440     /* drop the refcount back to zero */
441 #ifdef HAVE_ATOMIC_H
442     atomic_set (&(object->refcount),0);
443 #else
444     object->refcount = 0;
445 #endif
446     /* finalize the object */
447     // FIXME this is an evil hack that should be killed
448 // FIXMEFIXMEFIXMEFIXME
449 //    gtk_object_finalize(GTK_OBJECT(object));
450   }
451 }
452 #endif /* gst_object_unref */
453
454 /**
455  * gst_object_check_uniqueness:
456  * @list: a list of #GstObject to check through
457  * @name: the name to search for
458  *
459  * This function checks through the list of objects to see if the name
460  * given appears in the list as the name of an object.  It returns TRUE if
461  * the name does not exist in the list.
462  *
463  * Returns: TRUE if the name doesn't appear in the list, FALSE if it does.
464  */
465 gboolean
466 gst_object_check_uniqueness (GList *list, const gchar *name)
467 {
468   GstObject *child;
469
470   while (list) {
471     child = GST_OBJECT (list->data);
472     list = g_list_next(list);
473       
474     if (strcmp(GST_OBJECT_NAME(child), name) == 0) return FALSE;
475   }
476
477   return TRUE;
478 }
479
480
481 #ifndef GST_DISABLE_XML
482
483 /**
484  * gst_object_save_thyself:
485  * @object: GstObject to save
486  * @parent: The parent XML node to save the object into
487  *
488  * Saves the given object into the parent XML node.
489  *
490  * Returns: the new xmlNodePtr with the saved object
491  */
492 xmlNodePtr
493 gst_object_save_thyself (GstObject *object, xmlNodePtr parent)
494 {
495   GstObjectClass *oclass;
496
497   g_return_val_if_fail (object != NULL, parent);
498   g_return_val_if_fail (GST_IS_OBJECT (object), parent);
499   g_return_val_if_fail (parent != NULL, parent);
500
501   oclass = GST_OBJECT_CLASS (GTK_OBJECT (object)->klass);
502
503   if (oclass->save_thyself)
504     oclass->save_thyself (object, parent);
505
506 #ifndef GST_DISABLE_XML
507   gtk_signal_emit (GTK_OBJECT (object), gst_object_signals[OBJECT_SAVED], parent);
508 #endif
509
510   return parent;
511 }
512
513 #endif // GST_DISABLE_XML
514
515 /**
516  * gst_object_get_path_string:
517  * @object: GstObject to get the path from
518  *
519  * Generates a string describing the path of the object in
520  * the object hierarchy. Usefull for debugging
521  *
522  * Returns: a string describing the path of the object
523  */
524 gchar*
525 gst_object_get_path_string (GstObject *object)
526 {
527   GSList *parentage = NULL;
528   GSList *parents;
529   void *parent;
530   gchar *prevpath, *path;
531   const char *component;
532   gchar *separator = "";
533   gboolean free_component;
534
535   parentage = g_slist_prepend (NULL, object);
536
537   path = g_strdup ("");
538
539   // first walk the object hierarchy to build a list of the parents
540   do {
541     if (GST_IS_OBJECT (object)) {
542       parent = gst_object_get_parent (object);
543     } else {
544       parentage = g_slist_prepend (parentage, NULL);
545       parent = NULL;
546     }
547
548     if (parent != NULL) {
549       parentage = g_slist_prepend (parentage, parent);
550     }
551
552     object = parent;
553   } while (object != NULL);
554
555   // then walk the parent list and print them out
556   parents = parentage;
557   while (parents) {
558     if (GST_IS_OBJECT (parents->data)) {
559       GstObjectClass *oclass = GST_OBJECT_CLASS (GTK_OBJECT (parents->data)->klass);
560
561       component = gst_object_get_name (parents->data);
562       separator = oclass->path_string_separator;
563       free_component = FALSE;
564     } else {
565       component = g_strdup_printf("%p",parents->data);
566       separator = "/";
567       free_component = TRUE;
568     }
569
570     prevpath = path;
571     path = g_strjoin (separator, prevpath, component, NULL);
572     g_free(prevpath);
573     if (free_component)
574       g_free((gchar *)component);
575
576     parents = g_slist_next(parents);
577   }
578
579   g_slist_free (parentage);
580
581   return path;
582 }
583
584
585
586 struct _GstSignalObject {
587   GtkObject object;
588 };
589
590 struct _GstSignalObjectClass {
591   GtkObjectClass        parent_class;
592
593   /* signals */
594 #ifndef GST_DISABLE_XML
595   void          (*object_loaded)           (GstSignalObject *object, GstObject *new, xmlNodePtr self);
596 #endif GST_DISABLE_XML
597 };
598
599 static GtkType
600 gst_signal_object_get_type (void)
601 {
602   static GtkType signal_object_type = 0;
603
604   if (!signal_object_type) {
605     static const GtkTypeInfo signal_object_info = {
606       "GstSignalObject",
607       sizeof(GstSignalObject),
608       sizeof(GstSignalObjectClass),
609       (GtkClassInitFunc)gst_signal_object_class_init,
610       (GtkObjectInitFunc)gst_signal_object_init,
611       (GtkArgSetFunc)NULL,
612       (GtkArgGetFunc)NULL,
613       (GtkClassInitFunc)NULL,
614     };
615     signal_object_type = gtk_type_unique(gtk_object_get_type(),&signal_object_info);
616   }
617   return signal_object_type;
618 }
619
620 static void
621 gst_signal_object_class_init (GstSignalObjectClass *klass)
622 {
623   GtkObjectClass *gtkobject_class;
624
625   gtkobject_class = (GtkObjectClass*) klass;
626
627   parent_class = gtk_type_class (gtk_object_get_type ());
628
629 #ifndef GST_DISABLE_XML
630   gst_signal_object_signals[SO_OBJECT_LOADED] =
631     gtk_signal_new ("object_loaded", GTK_RUN_LAST, gtkobject_class->type,
632                     GTK_SIGNAL_OFFSET (GstSignalObjectClass, object_loaded),
633                     gtk_marshal_NONE__POINTER_POINTER, GTK_TYPE_NONE, 2,
634                     GST_TYPE_OBJECT, GTK_TYPE_POINTER);
635   gtk_object_class_add_signals (gtkobject_class, gst_signal_object_signals, LAST_SIGNAL);
636 #endif
637 }
638
639 static void
640 gst_signal_object_init (GstSignalObject *object)
641 {
642 }
643
644 /**
645  * gst_class_signal_connect
646  * @klass: the GstObjectClass to attach the signal to
647  * @name: the name of the signal to attach to
648  * @func: the signal function
649  * @func_data: a pointer to user data
650  *
651  * Connect to a class signal.
652  *
653  * Returns: the signal id.
654  */
655 guint
656 gst_class_signal_connect (GstObjectClass *klass,
657                           const gchar    *name,
658                           GtkSignalFunc  func,
659                           gpointer       func_data)
660 {
661   return gtk_signal_connect (klass->signal_object, name, func, func_data);
662 }
663
664 #ifndef GST_DISABLE_XML
665 /**
666  * gst_class_signal_emit_by_name:
667  * @object: the object that sends the signal
668  * @name: the name of the signal to emit
669  * @self: data for the signal
670  *
671  * emits the named class signal.
672  */
673 void
674 gst_class_signal_emit_by_name (GstObject *object,
675                                const gchar *name,
676                                xmlNodePtr self)
677 {
678   GstObjectClass *oclass;
679
680   oclass = GST_OBJECT_CLASS (GTK_OBJECT (object)->klass);
681
682   gtk_signal_emit_by_name (oclass->signal_object, name, object, self);
683 }
684
685 #endif // GST_DISABLE_XML