]> git.sesse.net Git - vlc/blob - src/misc/objects.c
9b07e7be38361fe649f32b5d8f634eae8fde7756
[vlc] / src / misc / objects.c
1 /*****************************************************************************
2  * objects.c: vlc_object_t handling
3  *****************************************************************************
4  * Copyright (C) 2004-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /**
25  * \file
26  * This file contains the functions to handle the vlc_object_t type
27  */
28
29
30 /*****************************************************************************
31  * Preamble
32  *****************************************************************************/
33 #include <vlc/vlc.h>
34
35 #ifdef HAVE_STDLIB_H
36 #   include <stdlib.h>                                          /* realloc() */
37 #endif
38
39 #include "../libvlc.h"
40 #include <vlc_vout.h>
41 #include <vlc_aout.h>
42 #include "audio_output/aout_internal.h"
43
44 #include <vlc_access.h>
45 #include <vlc_demux.h>
46 #include <vlc_stream.h>
47
48 #include <vlc_sout.h>
49 #include "stream_output/stream_output.h"
50
51 #include "vlc_playlist.h"
52 #include "vlc_interface.h"
53 #include "vlc_codec.h"
54 #include "vlc_filter.h"
55
56 #include "vlc_httpd.h"
57 #include "vlc_vlm.h"
58 #include "input/vlm_internal.h"
59 #include "vlc_vod.h"
60 #include "vlc_tls.h"
61 #include "vlc_xml.h"
62 #include "vlc_osd.h"
63 #include "vlc_meta.h"
64
65 #include "variables.h"
66
67 /*****************************************************************************
68  * Local prototypes
69  *****************************************************************************/
70 static int  DumpCommand( vlc_object_t *, char const *,
71                          vlc_value_t, vlc_value_t, void * );
72
73 static vlc_object_t * FindObject    ( vlc_object_t *, int, int );
74 static vlc_object_t * FindObjectName( vlc_object_t *, const char *, int );
75 static void           DetachObject  ( vlc_object_t * );
76 static void           PrintObject   ( vlc_object_t *, const char * );
77 static void           DumpStructure ( vlc_object_t *, int, char * );
78 static int            FindIndex     ( vlc_object_t *, vlc_object_t **, int );
79 static void           SetAttachment ( vlc_object_t *, vlc_bool_t );
80
81 static vlc_list_t   * NewList       ( int );
82 static void           ListReplace   ( vlc_list_t *, vlc_object_t *, int );
83 /*static void           ListAppend    ( vlc_list_t *, vlc_object_t * );*/
84 static int            CountChildren ( vlc_object_t *, int );
85 static void           ListChildren  ( vlc_list_t *, vlc_object_t *, int );
86
87 /*****************************************************************************
88  * Local structure lock
89  *****************************************************************************/
90 static vlc_mutex_t    structure_lock;
91 static vlc_object_internals_t global_internals;
92
93 vlc_object_t *vlc_custom_create( vlc_object_t *p_this, size_t i_size,
94                                  int i_type, const char *psz_type )
95 {
96     vlc_object_t *p_new;
97     vlc_object_internals_t *p_priv;
98
99     if( i_type == VLC_OBJECT_GLOBAL )
100     {
101         p_new = p_this;
102         p_priv = &global_internals;
103         memset( p_priv, 0, sizeof( *p_priv ) );
104     }
105     else
106     {
107         p_priv = calloc( 1, sizeof( *p_priv ) + i_size );
108         if( p_priv == NULL )
109             return NULL;
110
111         p_new = (vlc_object_t *)(p_priv + 1);
112     }
113
114     p_new->p_internals = p_priv;
115     p_new->i_object_type = i_type;
116     p_new->psz_object_type = psz_type;
117
118     p_new->psz_object_name = NULL;
119
120     p_new->b_die = VLC_FALSE;
121     p_new->b_error = VLC_FALSE;
122     p_new->b_dead = VLC_FALSE;
123     p_new->b_attached = VLC_FALSE;
124     p_new->b_force = VLC_FALSE;
125
126     p_new->psz_header = NULL;
127
128     p_new->i_flags = 0;
129     if( p_this->i_flags & OBJECT_FLAGS_NODBG )
130         p_new->i_flags |= OBJECT_FLAGS_NODBG;
131     if( p_this->i_flags & OBJECT_FLAGS_QUIET )
132         p_new->i_flags |= OBJECT_FLAGS_QUIET;
133     if( p_this->i_flags & OBJECT_FLAGS_NOINTERACT )
134         p_new->i_flags |= OBJECT_FLAGS_NOINTERACT;
135
136     p_new->i_vars = 0;
137     p_new->p_vars = (variable_t *)malloc( 16 * sizeof( variable_t ) );
138
139     if( !p_new->p_vars )
140     {
141         if( i_type != VLC_OBJECT_GLOBAL )
142             free( p_priv );
143         return NULL;
144     }
145
146     if( i_type == VLC_OBJECT_GLOBAL )
147     {
148         /* If i_type is global, then p_new is actually p_libvlc_global */
149         libvlc_global_data_t *p_libvlc_global = (libvlc_global_data_t *)p_new;
150         p_new->p_libvlc = NULL;
151
152         p_libvlc_global->i_counter = 0;
153         p_new->i_object_id = 0;
154
155         p_libvlc_global->i_objects = 1;
156         p_libvlc_global->pp_objects = malloc( sizeof(vlc_object_t *) );
157         p_libvlc_global->pp_objects[0] = p_new;
158         p_new->b_attached = VLC_TRUE;
159     }
160     else
161     {
162         libvlc_global_data_t *p_libvlc_global = vlc_global();
163         p_new->p_libvlc = ( i_type == VLC_OBJECT_LIBVLC ) ? (libvlc_int_t*)p_new
164                                                        : p_this->p_libvlc;
165
166         vlc_mutex_lock( &structure_lock );
167
168         p_libvlc_global->i_counter++;
169         p_new->i_object_id = p_libvlc_global->i_counter;
170
171         /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's
172          * useless to try and recover anything if pp_objects gets smashed. */
173         TAB_APPEND( p_libvlc_global->i_objects, p_libvlc_global->pp_objects,
174                     p_new );
175
176         vlc_mutex_unlock( &structure_lock );
177     }
178
179     p_new->i_refcount = 0;
180     p_new->p_parent = NULL;
181     p_new->pp_children = NULL;
182     p_new->i_children = 0;
183
184     p_new->p_private = NULL;
185
186     /* Initialize mutexes and condvars */
187     vlc_mutex_init( p_new, &p_new->object_lock );
188     vlc_cond_init( p_new, &p_new->object_wait );
189     vlc_mutex_init( p_new, &p_new->var_lock );
190
191     if( i_type == VLC_OBJECT_GLOBAL )
192     {
193         vlc_mutex_init( p_new, &structure_lock );
194
195         var_Create( p_new, "list", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
196         var_AddCallback( p_new, "list", DumpCommand, NULL );
197         var_Create( p_new, "tree", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
198         var_AddCallback( p_new, "tree", DumpCommand, NULL );
199         var_Create( p_new, "vars", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
200         var_AddCallback( p_new, "vars", DumpCommand, NULL );
201     }
202
203     return p_new;
204 }
205
206
207 /**
208  * Allocates and initializes a vlc object.
209  *
210  * @param i_type known object type (all of them are negative integer values),
211  *               or object byte size (always positive).
212  *
213  * @return the new object, or NULL on error.
214  */
215 void * __vlc_object_create( vlc_object_t *p_this, int i_type )
216 {
217     const char   * psz_type;
218     size_t         i_size;
219
220     switch( i_type )
221     {
222         case VLC_OBJECT_GLOBAL:
223             i_size = sizeof(libvlc_global_data_t);
224             psz_type = "global";
225             break;
226         case VLC_OBJECT_LIBVLC:
227             i_size = sizeof(libvlc_int_t);
228             psz_type = "libvlc";
229             break;
230         case VLC_OBJECT_INTF:
231             i_size = sizeof(intf_thread_t);
232             psz_type = "interface";
233             break;
234         case VLC_OBJECT_DIALOGS:
235             i_size = sizeof(intf_thread_t);
236             psz_type = "dialogs";
237             break;
238         case VLC_OBJECT_PLAYLIST:
239             i_size = sizeof(playlist_t);
240             psz_type = "playlist";
241             break;
242         case VLC_OBJECT_SD:
243             i_size = sizeof(services_discovery_t);
244             psz_type = "services discovery";
245             break;
246         case VLC_OBJECT_INPUT:
247             i_size = sizeof(input_thread_t);
248             psz_type = "input";
249             break;
250         case VLC_OBJECT_DEMUX:
251             i_size = sizeof(demux_t);
252             psz_type = "demux";
253             break;
254         case VLC_OBJECT_ACCESS:
255             i_size = sizeof(access_t);
256             psz_type = "access";
257             break;
258         case VLC_OBJECT_DECODER:
259             i_size = sizeof(decoder_t);
260             psz_type = "decoder";
261             break;
262         case VLC_OBJECT_PACKETIZER:
263             i_size = sizeof(decoder_t);
264             psz_type = "packetizer";
265             break;
266         case VLC_OBJECT_ENCODER:
267             i_size = sizeof(encoder_t);
268             psz_type = "encoder";
269             break;
270         case VLC_OBJECT_FILTER:
271             i_size = sizeof(filter_t);
272             psz_type = "filter";
273             break;
274         case VLC_OBJECT_VOUT:
275             i_size = sizeof(vout_thread_t);
276             psz_type = "video output";
277             break;
278         case VLC_OBJECT_SPU:
279             i_size = sizeof(spu_t);
280             psz_type = "subpicture";
281             break;
282         case VLC_OBJECT_AOUT:
283             i_size = sizeof(aout_instance_t);
284             psz_type = "audio output";
285             break;
286         case VLC_OBJECT_SOUT:
287             i_size = sizeof(sout_instance_t);
288             psz_type = "stream output";
289             break;
290         case VLC_OBJECT_VLM:
291             i_size = sizeof( vlm_t );
292             psz_type = "vlm dameon";
293             break;
294         case VLC_OBJECT_VOD:
295             i_size = sizeof( vod_t );
296             psz_type = "vod server";
297             break;
298         case VLC_OBJECT_TLS:
299             i_size = sizeof( tls_t );
300             psz_type = "tls";
301             break;
302         case VLC_OBJECT_XML:
303             i_size = sizeof( xml_t );
304             psz_type = "xml";
305             break;
306         case VLC_OBJECT_OPENGL:
307             i_size = sizeof( vout_thread_t );
308             psz_type = "opengl";
309             break;
310         case VLC_OBJECT_ANNOUNCE:
311             i_size = sizeof( announce_handler_t );
312             psz_type = "announce";
313             break;
314         case VLC_OBJECT_META_ENGINE:
315             i_size = sizeof( meta_engine_t );
316             psz_type = "meta engine";
317             break;
318         case VLC_OBJECT_OSDMENU:
319             i_size = sizeof( osd_menu_t );
320             psz_type = "osd menu";
321             break;
322         default:
323             i_size = i_type > (int)sizeof(vlc_object_t)
324                          ? i_type : (int)sizeof(vlc_object_t);
325             i_type = VLC_OBJECT_GENERIC;
326             psz_type = "generic";
327             break;
328     }
329
330     return vlc_custom_create( p_this, i_size, i_type, psz_type );
331 }
332
333
334 /**
335  ****************************************************************************
336  * Destroy a vlc object
337  *
338  * This function destroys an object that has been previously allocated with
339  * vlc_object_create. The object's refcount must be zero and it must not be
340  * attached to other objects in any way.
341  *****************************************************************************/
342 void __vlc_object_destroy( vlc_object_t *p_this )
343 {
344     vlc_object_internals_t *p_priv = vlc_internals( p_this );
345     int i_delay = 0;
346
347     if( p_this->i_children )
348     {
349         msg_Err( p_this, "cannot delete object (%i, %s) with children" ,
350                  p_this->i_object_id, p_this->psz_object_name );
351         return;
352     }
353
354     if( p_this->p_parent )
355     {
356         msg_Err( p_this, "cannot delete object (%i, %s) with a parent",
357                  p_this->i_object_id, p_this->psz_object_name );
358         return;
359     }
360
361     while( p_this->i_refcount )
362     {
363         i_delay++;
364
365         /* Don't warn immediately ... 100ms seems OK */
366         if( i_delay == 2 )
367         {
368             msg_Warn( p_this,
369                   "refcount is %i, delaying before deletion (id=%d,type=%d)",
370                   p_this->i_refcount, p_this->i_object_id,
371                   p_this->i_object_type );
372         }
373         else if( i_delay == 10 )
374         {
375             msg_Err( p_this,
376                   "refcount is %i, delaying again (id=%d,type=%d)",
377                   p_this->i_refcount, p_this->i_object_id,
378                   p_this->i_object_type );
379         }
380         else if( i_delay == 20 )
381         {
382             msg_Err( p_this,
383                   "waited too long, cancelling destruction (id=%d,type=%d)",
384                   p_this->i_object_id, p_this->i_object_type );
385             return;
386         }
387
388         msleep( 100000 );
389     }
390
391     /* Destroy the associated variables, starting from the end so that
392      * no memmove calls have to be done. */
393     while( p_this->i_vars )
394     {
395         var_Destroy( p_this, p_this->p_vars[p_this->i_vars - 1].psz_name );
396     }
397
398     free( p_this->p_vars );
399     vlc_mutex_destroy( &p_this->var_lock );
400
401     if( p_this->psz_header ) free( p_this->psz_header );
402
403     if( p_this->i_object_type == VLC_OBJECT_GLOBAL )
404     {
405         libvlc_global_data_t *p_global = (libvlc_global_data_t *)p_this;
406         /* We are the global object ... no need to lock. */
407         free( p_global->pp_objects );
408         p_global->pp_objects = NULL;
409         p_global->i_objects--;
410
411         vlc_mutex_destroy( &structure_lock );
412     }
413     else
414     {
415         libvlc_global_data_t *p_libvlc_global = vlc_global();
416         int i_index;
417
418         vlc_mutex_lock( &structure_lock );
419
420         /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's
421          * useless to try and recover anything if pp_objects gets smashed. */
422         i_index = FindIndex( p_this, p_libvlc_global->pp_objects,
423                              p_libvlc_global->i_objects );
424         REMOVE_ELEM( p_libvlc_global->pp_objects,
425                      p_libvlc_global->i_objects, i_index );
426
427         vlc_mutex_unlock( &structure_lock );
428     }
429
430     vlc_mutex_destroy( &p_this->object_lock );
431     vlc_cond_destroy( &p_this->object_wait );
432
433     /* global is not dynamically allocated by vlc_object_create */
434     if( p_this->i_object_type != VLC_OBJECT_GLOBAL )
435         free( p_priv );
436 }
437
438
439 void __vlc_object_kill( vlc_object_t *p_this )
440 {
441     vlc_mutex_lock( &p_this->object_lock );
442     p_this->b_die = VLC_TRUE;
443     vlc_mutex_unlock( &p_this->object_lock );
444 }
445
446
447 vlc_bool_t __vlc_object_dying_unlocked( vlc_object_t *p_this )
448 {
449     vlc_assert_locked( &p_this->object_lock );
450     return p_this->b_die;
451 }
452
453
454 vlc_bool_t __vlc_object_dying( vlc_object_t *p_this )
455 {
456      vlc_bool_t b;
457      vlc_mutex_lock( &p_this->object_lock );
458      b = __vlc_object_dying_unlocked( p_this );
459      vlc_mutex_unlock( &p_this->object_lock );
460      return b;
461 }
462
463
464 /**
465  * find an object given its ID
466  *
467  * This function looks for the object whose i_object_id field is i_id. We
468  * use a dichotomy so that lookups are in log2(n).
469  *****************************************************************************/
470 void * __vlc_object_get( vlc_object_t *p_this, int i_id )
471 {
472     int i_max, i_middle;
473     vlc_object_t **pp_objects;
474     libvlc_global_data_t *p_libvlc_global = vlc_global();
475
476     vlc_mutex_lock( &structure_lock );
477
478     pp_objects = p_libvlc_global->pp_objects;
479
480     /* Perform our dichotomy */
481     for( i_max = p_libvlc_global->i_objects - 1 ; ; )
482     {
483         i_middle = i_max / 2;
484
485         if( pp_objects[i_middle]->i_object_id > i_id )
486         {
487             i_max = i_middle;
488         }
489         else if( pp_objects[i_middle]->i_object_id < i_id )
490         {
491             if( i_middle )
492             {
493                 pp_objects += i_middle;
494                 i_max -= i_middle;
495             }
496             else
497             {
498                 /* This happens when there are only two remaining objects */
499                 if( pp_objects[i_middle+1]->i_object_id == i_id )
500                 {
501                     vlc_mutex_unlock( &structure_lock );
502                     pp_objects[i_middle+1]->i_refcount++;
503                     return pp_objects[i_middle+1];
504                 }
505                 break;
506             }
507         }
508         else
509         {
510             vlc_mutex_unlock( &structure_lock );
511             pp_objects[i_middle]->i_refcount++;
512             return pp_objects[i_middle];
513         }
514
515         if( i_max == 0 )
516         {
517             /* this means that i_max == i_middle, and since we have already
518              * tested pp_objects[i_middle]), p_found is properly set. */
519             break;
520         }
521     }
522
523     vlc_mutex_unlock( &structure_lock );
524     return NULL;
525 }
526
527 /**
528  ****************************************************************************
529  * find a typed object and increment its refcount
530  *****************************************************************************
531  * This function recursively looks for a given object type. i_mode can be one
532  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
533  *****************************************************************************/
534 void * __vlc_object_find( vlc_object_t *p_this, int i_type, int i_mode )
535 {
536     vlc_object_t *p_found;
537
538     vlc_mutex_lock( &structure_lock );
539
540     /* If we are of the requested type ourselves, don't look further */
541     if( !(i_mode & FIND_STRICT) && p_this->i_object_type == i_type )
542     {
543         p_this->i_refcount++;
544         vlc_mutex_unlock( &structure_lock );
545         return p_this;
546     }
547
548     /* Otherwise, recursively look for the object */
549     if( (i_mode & 0x000f) == FIND_ANYWHERE )
550     {
551         vlc_object_t *p_root = p_this;
552
553         /* Find the root */
554         while( p_root->p_parent != NULL &&
555                p_root != VLC_OBJECT( p_this->p_libvlc ) )
556         {
557             p_root = p_root->p_parent;
558         }
559
560         p_found = FindObject( p_root, i_type, (i_mode & ~0x000f)|FIND_CHILD );
561         if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_libvlc ) )
562         {
563             p_found = FindObject( VLC_OBJECT( p_this->p_libvlc ),
564                                   i_type, (i_mode & ~0x000f)|FIND_CHILD );
565         }
566     }
567     else
568     {
569         p_found = FindObject( p_this, i_type, i_mode );
570     }
571
572     vlc_mutex_unlock( &structure_lock );
573
574     return p_found;
575 }
576
577 /**
578  ****************************************************************************
579  * find a named object and increment its refcount
580  *****************************************************************************
581  * This function recursively looks for a given object name. i_mode can be one
582  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
583  *****************************************************************************/
584 void * __vlc_object_find_name( vlc_object_t *p_this, const char *psz_name,
585                                int i_mode )
586 {
587     vlc_object_t *p_found;
588
589     vlc_mutex_lock( &structure_lock );
590
591     /* If have the requested name ourselves, don't look further */
592     if( !(i_mode & FIND_STRICT)
593         && p_this->psz_object_name
594         && !strcmp( p_this->psz_object_name, psz_name ) )
595     {
596         p_this->i_refcount++;
597         vlc_mutex_unlock( &structure_lock );
598         return p_this;
599     }
600
601     /* Otherwise, recursively look for the object */
602     if( (i_mode & 0x000f) == FIND_ANYWHERE )
603     {
604         vlc_object_t *p_root = p_this;
605
606         /* Find the root */
607         while( p_root->p_parent != NULL &&
608                p_root != VLC_OBJECT( p_this->p_libvlc ) )
609         {
610             p_root = p_root->p_parent;
611         }
612
613         p_found = FindObjectName( p_root, psz_name,
614                                  (i_mode & ~0x000f)|FIND_CHILD );
615         if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_libvlc ) )
616         {
617             p_found = FindObjectName( VLC_OBJECT( p_this->p_libvlc ),
618                                       psz_name, (i_mode & ~0x000f)|FIND_CHILD );
619         }
620     }
621     else
622     {
623         p_found = FindObjectName( p_this, psz_name, i_mode );
624     }
625
626     vlc_mutex_unlock( &structure_lock );
627
628     return p_found;
629 }
630
631 /**
632  ****************************************************************************
633  * increment an object refcount
634  *****************************************************************************/
635 void __vlc_object_yield( vlc_object_t *p_this )
636 {
637     vlc_mutex_lock( &structure_lock );
638     p_this->i_refcount++;
639     vlc_mutex_unlock( &structure_lock );
640 }
641
642 /**
643  ****************************************************************************
644  * decrement an object refcount
645  *****************************************************************************/
646 void __vlc_object_release( vlc_object_t *p_this )
647 {
648     vlc_mutex_lock( &structure_lock );
649     p_this->i_refcount--;
650     vlc_mutex_unlock( &structure_lock );
651 }
652
653 /**
654  ****************************************************************************
655  * attach object to a parent object
656  *****************************************************************************
657  * This function sets p_this as a child of p_parent, and p_parent as a parent
658  * of p_this. This link can be undone using vlc_object_detach.
659  *****************************************************************************/
660 void __vlc_object_attach( vlc_object_t *p_this, vlc_object_t *p_parent )
661 {
662     if( !p_this ) return;
663
664     vlc_mutex_lock( &structure_lock );
665
666     /* Attach the parent to its child */
667     p_this->p_parent = p_parent;
668
669     /* Attach the child to its parent */
670     INSERT_ELEM( p_parent->pp_children, p_parent->i_children,
671                  p_parent->i_children, p_this );
672
673     /* Climb up the tree to see whether we are connected with the root */
674     if( p_parent->b_attached )
675     {
676         SetAttachment( p_this, VLC_TRUE );
677     }
678
679     vlc_mutex_unlock( &structure_lock );
680 }
681
682 /**
683  ****************************************************************************
684  * detach object from its parent
685  *****************************************************************************
686  * This function removes all links between an object and its parent.
687  *****************************************************************************/
688 void __vlc_object_detach( vlc_object_t *p_this )
689 {
690     if( !p_this ) return;
691
692     vlc_mutex_lock( &structure_lock );
693     if( !p_this->p_parent )
694     {
695         msg_Err( p_this, "object is not attached" );
696         vlc_mutex_unlock( &structure_lock );
697         return;
698     }
699
700     /* Climb up the tree to see whether we are connected with the root */
701     if( p_this->p_parent->b_attached )
702     {
703         SetAttachment( p_this, VLC_FALSE );
704     }
705
706     DetachObject( p_this );
707     vlc_mutex_unlock( &structure_lock );
708     p_this = NULL;
709 }
710
711 /**
712  ****************************************************************************
713  * find a list typed objects and increment their refcount
714  *****************************************************************************
715  * This function recursively looks for a given object type. i_mode can be one
716  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
717  *****************************************************************************/
718 vlc_list_t * __vlc_list_find( vlc_object_t *p_this, int i_type, int i_mode )
719 {
720     vlc_list_t *p_list;
721     vlc_object_t **pp_current, **pp_end;
722     int i_count = 0, i_index = 0;
723     libvlc_global_data_t *p_libvlc_global = vlc_global();
724
725     vlc_mutex_lock( &structure_lock );
726
727     /* Look for the objects */
728     switch( i_mode & 0x000f )
729     {
730     case FIND_ANYWHERE:
731         pp_current = p_libvlc_global->pp_objects;
732         pp_end = pp_current + p_libvlc_global->i_objects;
733
734         for( ; pp_current < pp_end ; pp_current++ )
735         {
736             if( (*pp_current)->b_attached
737                  && (*pp_current)->i_object_type == i_type )
738             {
739                 i_count++;
740             }
741         }
742
743         p_list = NewList( i_count );
744         pp_current = p_libvlc_global->pp_objects;
745
746         for( ; pp_current < pp_end ; pp_current++ )
747         {
748             if( (*pp_current)->b_attached
749                  && (*pp_current)->i_object_type == i_type )
750             {
751                 ListReplace( p_list, *pp_current, i_index );
752                 if( i_index < i_count ) i_index++;
753             }
754         }
755     break;
756
757     case FIND_CHILD:
758         i_count = CountChildren( p_this, i_type );
759         p_list = NewList( i_count );
760
761         /* Check allocation was successful */
762         if( p_list->i_count != i_count )
763         {
764             msg_Err( p_this, "list allocation failed!" );
765             p_list->i_count = 0;
766             break;
767         }
768
769         p_list->i_count = 0;
770         ListChildren( p_list, p_this, i_type );
771         break;
772
773     default:
774         msg_Err( p_this, "unimplemented!" );
775         p_list = NewList( 0 );
776         break;
777     }
778
779     vlc_mutex_unlock( &structure_lock );
780
781     return p_list;
782 }
783
784 /*****************************************************************************
785  * DumpCommand: print the current vlc structure
786  *****************************************************************************
787  * This function prints either an ASCII tree showing the connections between
788  * vlc objects, and additional information such as their refcount, thread ID,
789  * etc. (command "tree"), or the same data as a simple list (command "list").
790  *****************************************************************************/
791 static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd,
792                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
793 {
794     libvlc_global_data_t *p_libvlc_global = vlc_global();
795
796     (void)oldval; (void)p_data;
797     if( *psz_cmd == 'l' )
798     {
799         vlc_mutex_lock( &structure_lock );
800
801         vlc_object_t **pp_current, **pp_end;
802
803         pp_current = p_libvlc_global->pp_objects;
804         pp_end = pp_current + p_libvlc_global->i_objects;
805
806         for( ; pp_current < pp_end ; pp_current++ )
807         {
808             if( (*pp_current)->b_attached )
809             {
810                 PrintObject( *pp_current, "" );
811             }
812             else
813             {
814                 printf( " o %.8i %s (not attached)\n",
815                         (*pp_current)->i_object_id,
816                         (*pp_current)->psz_object_type );
817             }
818         }
819
820         vlc_mutex_unlock( &structure_lock );
821     }
822     else
823     {
824         vlc_object_t *p_object = NULL;
825
826         if( *newval.psz_string )
827         {
828             char *end;
829             int i_id = strtol( newval.psz_string, &end, 0 );
830             if( end != newval.psz_string )
831                 p_object = vlc_object_get( p_this, i_id );
832             else
833             {
834                 /* try using the object's name to find it */
835                 vlc_object_t *p_libvlc = vlc_object_get( p_this, 1 );
836                 if( p_libvlc )
837                 {
838                     /* Look in p_libvlc's children tree */
839                     p_object = vlc_object_find_name( p_libvlc,
840                                                      newval.psz_string,
841                                                      FIND_CHILD );
842                     vlc_object_release( p_libvlc );
843                 }
844                 if( !p_object )
845                 {
846                     /* If it's not in libvlc, look in libvlc_global (== p_this) */
847                     p_object = vlc_object_find_name( p_this,
848                                                      newval.psz_string,
849                                                      FIND_CHILD );
850                 }
851             }
852
853             if( !p_object )
854             {
855                 return VLC_ENOOBJ;
856             }
857         }
858
859         vlc_mutex_lock( &structure_lock );
860
861         if( *psz_cmd == 't' )
862         {
863             char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
864
865             if( !p_object )
866                 p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
867
868             psz_foo[0] = '|';
869             DumpStructure( p_object, 0, psz_foo );
870         }
871         else if( *psz_cmd == 'v' )
872         {
873             int i;
874
875             if( !p_object )
876                 p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
877
878             PrintObject( p_object, "" );
879
880             if( !p_object->i_vars )
881                 printf( " `-o No variables\n" );
882             for( i = 0; i < p_object->i_vars; i++ )
883             {
884                 variable_t *p_var = p_object->p_vars + i;
885
886                 const char *psz_type = "unknown";
887                 switch( p_var->i_type & VLC_VAR_TYPE )
888                 {
889 #define MYCASE( type, nice )                \
890                     case VLC_VAR_ ## type:  \
891                         psz_type = nice;    \
892                         break;
893                     MYCASE( VOID, "void" );
894                     MYCASE( BOOL, "bool" );
895                     MYCASE( INTEGER, "integer" );
896                     MYCASE( HOTKEY, "hotkey" );
897                     MYCASE( STRING, "string" );
898                     MYCASE( MODULE, "module" );
899                     MYCASE( FILE, "file" );
900                     MYCASE( DIRECTORY, "directory" );
901                     MYCASE( VARIABLE, "variable" );
902                     MYCASE( FLOAT, "float" );
903                     MYCASE( TIME, "time" );
904                     MYCASE( ADDRESS, "address" );
905                     MYCASE( MUTEX, "mutex" );
906                     MYCASE( LIST, "list" );
907 #undef MYCASE
908                 }
909                 printf( " %c-o \"%s\" (%s",
910                         i + 1 == p_object->i_vars ? '`' : '|',
911                         p_var->psz_name, psz_type );
912                 if( p_var->psz_text )
913                     printf( ", %s", p_var->psz_text );
914                 printf( ")" );
915                 if( p_var->i_type & VLC_VAR_ISCOMMAND )
916                     printf( ", command" );
917                 if( p_var->i_entries )
918                     printf( ", %d callbacks", p_var->i_entries );
919                 switch( p_var->i_type & 0x00f0 )
920                 {
921                     case VLC_VAR_VOID:
922                     case VLC_VAR_MUTEX:
923                         break;
924                     case VLC_VAR_BOOL:
925                         printf( ": %s", p_var->val.b_bool ? "true" : "false" );
926                         break;
927                     case VLC_VAR_INTEGER:
928                         printf( ": %d", p_var->val.i_int );
929                         break;
930                     case VLC_VAR_STRING:
931                         printf( ": \"%s\"", p_var->val.psz_string );
932                         break;
933                     case VLC_VAR_FLOAT:
934                         printf( ": %f", p_var->val.f_float );
935                         break;
936                     case VLC_VAR_TIME:
937                         printf( ": " I64Fi, (int64_t)p_var->val.i_time );
938                         break;
939                     case VLC_VAR_ADDRESS:
940                         printf( ": %p", p_var->val.p_address );
941                         break;
942                     case VLC_VAR_LIST:
943                         printf( ": TODO" );
944                         break;
945                 }
946                 printf( "\n" );
947             }
948         }
949
950         vlc_mutex_unlock( &structure_lock );
951
952         if( *newval.psz_string )
953         {
954             vlc_object_release( p_object );
955         }
956     }
957
958     return VLC_SUCCESS;
959 }
960
961 /*****************************************************************************
962  * vlc_list_release: free a list previously allocated by vlc_list_find
963  *****************************************************************************
964  * This function decreases the refcount of all objects in the list and
965  * frees the list.
966  *****************************************************************************/
967 void vlc_list_release( vlc_list_t *p_list )
968 {
969     int i_index;
970
971     vlc_mutex_lock( &structure_lock );
972     for( i_index = 0; i_index < p_list->i_count; i_index++ )
973     {
974         p_list->p_values[i_index].p_object->i_refcount--;
975     }
976     vlc_mutex_unlock( &structure_lock );
977
978     free( p_list->p_values );
979     free( p_list );
980 }
981
982 /* Following functions are local */
983
984 /*****************************************************************************
985  * FindIndex: find the index of an object in an array of objects
986  *****************************************************************************
987  * This function assumes that p_this can be found in pp_objects. It will not
988  * crash if p_this cannot be found, but will return a wrong value. It is your
989  * duty to check the return value if you are not certain that the object could
990  * be found for sure.
991  *****************************************************************************/
992 static int FindIndex( vlc_object_t *p_this,
993                       vlc_object_t **pp_objects, int i_count )
994 {
995     int i_middle = i_count / 2;
996
997     if( i_count == 0 )
998     {
999         return 0;
1000     }
1001
1002     if( pp_objects[i_middle] == p_this )
1003     {
1004         return i_middle;
1005     }
1006
1007     if( i_count == 1 )
1008     {
1009         return 0;
1010     }
1011
1012     /* We take advantage of the sorted array */
1013     if( pp_objects[i_middle]->i_object_id < p_this->i_object_id )
1014     {
1015         return i_middle + FindIndex( p_this, pp_objects + i_middle,
1016                                              i_count - i_middle );
1017     }
1018     else
1019     {
1020         return FindIndex( p_this, pp_objects, i_middle );
1021     }
1022 }
1023
1024 static vlc_object_t * FindObject( vlc_object_t *p_this, int i_type, int i_mode )
1025 {
1026     int i;
1027     vlc_object_t *p_tmp;
1028
1029     switch( i_mode & 0x000f )
1030     {
1031     case FIND_PARENT:
1032         p_tmp = p_this->p_parent;
1033         if( p_tmp )
1034         {
1035             if( p_tmp->i_object_type == i_type )
1036             {
1037                 p_tmp->i_refcount++;
1038                 return p_tmp;
1039             }
1040             else
1041             {
1042                 return FindObject( p_tmp, i_type, i_mode );
1043             }
1044         }
1045         break;
1046
1047     case FIND_CHILD:
1048         for( i = p_this->i_children; i--; )
1049         {
1050             p_tmp = p_this->pp_children[i];
1051             if( p_tmp->i_object_type == i_type )
1052             {
1053                 p_tmp->i_refcount++;
1054                 return p_tmp;
1055             }
1056             else if( p_tmp->i_children )
1057             {
1058                 p_tmp = FindObject( p_tmp, i_type, i_mode );
1059                 if( p_tmp )
1060                 {
1061                     return p_tmp;
1062                 }
1063             }
1064         }
1065         break;
1066
1067     case FIND_ANYWHERE:
1068         /* Handled in vlc_object_find */
1069         break;
1070     }
1071
1072     return NULL;
1073 }
1074
1075 static vlc_object_t * FindObjectName( vlc_object_t *p_this,
1076                                       const char *psz_name,
1077                                       int i_mode )
1078 {
1079     int i;
1080     vlc_object_t *p_tmp;
1081
1082     switch( i_mode & 0x000f )
1083     {
1084     case FIND_PARENT:
1085         p_tmp = p_this->p_parent;
1086         if( p_tmp )
1087         {
1088             if( p_tmp->psz_object_name
1089                 && !strcmp( p_tmp->psz_object_name, psz_name ) )
1090             {
1091                 p_tmp->i_refcount++;
1092                 return p_tmp;
1093             }
1094             else
1095             {
1096                 return FindObjectName( p_tmp, psz_name, i_mode );
1097             }
1098         }
1099         break;
1100
1101     case FIND_CHILD:
1102         for( i = p_this->i_children; i--; )
1103         {
1104             p_tmp = p_this->pp_children[i];
1105             if( p_tmp->psz_object_name
1106                 && !strcmp( p_tmp->psz_object_name, psz_name ) )
1107             {
1108                 p_tmp->i_refcount++;
1109                 return p_tmp;
1110             }
1111             else if( p_tmp->i_children )
1112             {
1113                 p_tmp = FindObjectName( p_tmp, psz_name, i_mode );
1114                 if( p_tmp )
1115                 {
1116                     return p_tmp;
1117                 }
1118             }
1119         }
1120         break;
1121
1122     case FIND_ANYWHERE:
1123         /* Handled in vlc_object_find */
1124         break;
1125     }
1126
1127     return NULL;
1128 }
1129
1130 static void DetachObject( vlc_object_t *p_this )
1131 {
1132     vlc_object_t *p_parent = p_this->p_parent;
1133     int i_index, i;
1134
1135     /* Remove p_this's parent */
1136     p_this->p_parent = NULL;
1137
1138     /* Remove all of p_parent's children which are p_this */
1139     for( i_index = p_parent->i_children ; i_index-- ; )
1140     {
1141         if( p_parent->pp_children[i_index] == p_this )
1142         {
1143             p_parent->i_children--;
1144             for( i = i_index ; i < p_parent->i_children ; i++ )
1145             {
1146                 p_parent->pp_children[i] = p_parent->pp_children[i+1];
1147             }
1148         }
1149     }
1150
1151     if( p_parent->i_children )
1152     {
1153         p_parent->pp_children = (vlc_object_t **)realloc( p_parent->pp_children,
1154                                p_parent->i_children * sizeof(vlc_object_t *) );
1155     }
1156     else
1157     {
1158         free( p_parent->pp_children );
1159         p_parent->pp_children = NULL;
1160     }
1161 }
1162
1163 /*****************************************************************************
1164  * SetAttachment: recursively set the b_attached flag of a subtree.
1165  *****************************************************************************
1166  * This function is used by the attach and detach functions to propagate
1167  * the b_attached flag in a subtree.
1168  *****************************************************************************/
1169 static void SetAttachment( vlc_object_t *p_this, vlc_bool_t b_attached )
1170 {
1171     int i_index;
1172
1173     for( i_index = p_this->i_children ; i_index-- ; )
1174     {
1175         SetAttachment( p_this->pp_children[i_index], b_attached );
1176     }
1177
1178     p_this->b_attached = b_attached;
1179 }
1180
1181 static void PrintObject( vlc_object_t *p_this, const char *psz_prefix )
1182 {
1183     char psz_children[20], psz_refcount[20], psz_thread[30], psz_name[50],
1184          psz_parent[20];
1185
1186     psz_name[0] = '\0';
1187     if( p_this->psz_object_name )
1188     {
1189         snprintf( psz_name, 49, " \"%s\"", p_this->psz_object_name );
1190         if( psz_name[48] )
1191             psz_name[48] = '\"';
1192     }
1193
1194     psz_children[0] = '\0';
1195     switch( p_this->i_children )
1196     {
1197         case 0:
1198             break;
1199         case 1:
1200             strcpy( psz_children, ", 1 child" );
1201             break;
1202         default:
1203             snprintf( psz_children, 19, ", %i children", p_this->i_children );
1204             break;
1205     }
1206
1207     psz_refcount[0] = '\0';
1208     if( p_this->i_refcount )
1209         snprintf( psz_refcount, 19, ", refcount %i", p_this->i_refcount );
1210
1211     psz_thread[0] = '\0';
1212     if( p_this->b_thread )
1213         snprintf( psz_thread, 29, " (thread %d)", (int)p_this->thread_id );
1214
1215     psz_parent[0] = '\0';
1216     if( p_this->p_parent )
1217         snprintf( psz_parent, 19, ", parent %i", p_this->p_parent->i_object_id );
1218
1219     printf( " %so %.8i %s%s%s%s%s%s\n", psz_prefix,
1220             p_this->i_object_id, p_this->psz_object_type,
1221             psz_name, psz_thread, psz_refcount, psz_children,
1222             psz_parent );
1223 }
1224
1225 static void DumpStructure( vlc_object_t *p_this, int i_level, char *psz_foo )
1226 {
1227     int i;
1228     char i_back = psz_foo[i_level];
1229     psz_foo[i_level] = '\0';
1230
1231     PrintObject( p_this, psz_foo );
1232
1233     psz_foo[i_level] = i_back;
1234
1235     if( i_level / 2 >= MAX_DUMPSTRUCTURE_DEPTH )
1236     {
1237         msg_Warn( p_this, "structure tree is too deep" );
1238         return;
1239     }
1240
1241     for( i = 0 ; i < p_this->i_children ; i++ )
1242     {
1243         if( i_level )
1244         {
1245             psz_foo[i_level-1] = ' ';
1246
1247             if( psz_foo[i_level-2] == '`' )
1248             {
1249                 psz_foo[i_level-2] = ' ';
1250             }
1251         }
1252
1253         if( i == p_this->i_children - 1 )
1254         {
1255             psz_foo[i_level] = '`';
1256         }
1257         else
1258         {
1259             psz_foo[i_level] = '|';
1260         }
1261
1262         psz_foo[i_level+1] = '-';
1263         psz_foo[i_level+2] = '\0';
1264
1265         DumpStructure( p_this->pp_children[i], i_level + 2, psz_foo );
1266     }
1267 }
1268
1269 static vlc_list_t * NewList( int i_count )
1270 {
1271     vlc_list_t * p_list = (vlc_list_t *)malloc( sizeof( vlc_list_t ) );
1272     if( p_list == NULL )
1273     {
1274         return NULL;
1275     }
1276
1277     p_list->i_count = i_count;
1278
1279     if( i_count == 0 )
1280     {
1281         p_list->p_values = NULL;
1282         return p_list;
1283     }
1284
1285     p_list->p_values = malloc( i_count * sizeof( vlc_value_t ) );
1286     if( p_list->p_values == NULL )
1287     {
1288         p_list->i_count = 0;
1289         return p_list;
1290     }
1291
1292     return p_list;
1293 }
1294
1295 static void ListReplace( vlc_list_t *p_list, vlc_object_t *p_object,
1296                          int i_index )
1297 {
1298     if( p_list == NULL || i_index >= p_list->i_count )
1299     {
1300         return;
1301     }
1302
1303     p_object->i_refcount++;
1304
1305     p_list->p_values[i_index].p_object = p_object;
1306
1307     return;
1308 }
1309
1310 /*static void ListAppend( vlc_list_t *p_list, vlc_object_t *p_object )
1311 {
1312     if( p_list == NULL )
1313     {
1314         return;
1315     }
1316
1317     p_list->p_values = realloc( p_list->p_values, (p_list->i_count + 1)
1318                                 * sizeof( vlc_value_t ) );
1319     if( p_list->p_values == NULL )
1320     {
1321         p_list->i_count = 0;
1322         return;
1323     }
1324
1325     p_object->i_refcount++;
1326
1327     p_list->p_values[p_list->i_count].p_object = p_object;
1328     p_list->i_count++;
1329
1330     return;
1331 }*/
1332
1333 static int CountChildren( vlc_object_t *p_this, int i_type )
1334 {
1335     vlc_object_t *p_tmp;
1336     int i, i_count = 0;
1337
1338     for( i = 0; i < p_this->i_children; i++ )
1339     {
1340         p_tmp = p_this->pp_children[i];
1341
1342         if( p_tmp->i_object_type == i_type )
1343         {
1344             i_count++;
1345         }
1346
1347         if( p_tmp->i_children )
1348         {
1349             i_count += CountChildren( p_tmp, i_type );
1350         }
1351     }
1352
1353     return i_count;
1354 }
1355
1356 static void ListChildren( vlc_list_t *p_list, vlc_object_t *p_this, int i_type )
1357 {
1358     vlc_object_t *p_tmp;
1359     int i;
1360
1361     for( i = 0; i < p_this->i_children; i++ )
1362     {
1363         p_tmp = p_this->pp_children[i];
1364
1365         if( p_tmp->i_object_type == i_type )
1366         {
1367             ListReplace( p_list, p_tmp, p_list->i_count++ );
1368         }
1369
1370         if( p_tmp->i_children )
1371         {
1372             ListChildren( p_list, p_tmp, i_type );
1373         }
1374     }
1375 }