]> git.sesse.net Git - vlc/blob - src/misc/objects.c
vlc_object_die() opaque wrapper to set b_die, so we can use another
[vlc] / src / misc / objects.c
1 /*****************************************************************************
2  * objects.c: vlc_object_t handling
3  *****************************************************************************
4  * Copyright (C) 2004 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 "vlc_vod.h"
59 #include "vlc_tls.h"
60 #include "vlc_xml.h"
61 #include "vlc_osd.h"
62 #include "vlc_meta.h"
63
64 #include "variables.h"
65
66 /*****************************************************************************
67  * Local prototypes
68  *****************************************************************************/
69 static int  DumpCommand( vlc_object_t *, char const *,
70                          vlc_value_t, vlc_value_t, void * );
71
72 static vlc_object_t * FindObject    ( vlc_object_t *, int, int );
73 static void           DetachObject  ( vlc_object_t * );
74 static void           PrintObject   ( vlc_object_t *, const char * );
75 static void           DumpStructure ( vlc_object_t *, int, char * );
76 static int            FindIndex     ( vlc_object_t *, vlc_object_t **, int );
77 static void           SetAttachment ( vlc_object_t *, vlc_bool_t );
78
79 static vlc_list_t   * NewList       ( int );
80 static void           ListReplace   ( vlc_list_t *, vlc_object_t *, int );
81 /*static void           ListAppend    ( vlc_list_t *, vlc_object_t * );*/
82 static int            CountChildren ( vlc_object_t *, int );
83 static void           ListChildren  ( vlc_list_t *, vlc_object_t *, int );
84
85 /*****************************************************************************
86  * Local structure lock
87  *****************************************************************************/
88 static vlc_mutex_t    structure_lock;
89
90 vlc_object_t *vlc_custom_create( vlc_object_t *p_this, size_t i_size,
91                                  int i_type, const char *psz_type )
92 {
93     vlc_object_t * p_new = NULL;
94
95     if( i_type == VLC_OBJECT_GLOBAL )
96     {
97         p_new = p_this;
98     }
99     else
100     {
101         p_new = malloc( i_size );
102         if( !p_new ) return NULL;
103         memset( p_new, 0, i_size );
104     }
105
106     p_new->i_object_type = i_type;
107     p_new->psz_object_type = psz_type;
108
109     p_new->psz_object_name = NULL;
110
111     p_new->b_die = VLC_FALSE;
112     p_new->b_error = VLC_FALSE;
113     p_new->b_dead = VLC_FALSE;
114     p_new->b_attached = VLC_FALSE;
115     p_new->b_force = VLC_FALSE;
116
117     p_new->psz_header = NULL;
118
119     p_new->i_flags = 0;
120     if( p_this->i_flags & OBJECT_FLAGS_NODBG )
121         p_new->i_flags |= OBJECT_FLAGS_NODBG;
122     if( p_this->i_flags & OBJECT_FLAGS_QUIET )
123         p_new->i_flags |= OBJECT_FLAGS_QUIET;
124     if( p_this->i_flags & OBJECT_FLAGS_NOINTERACT )
125         p_new->i_flags |= OBJECT_FLAGS_NOINTERACT;
126
127     p_new->i_vars = 0;
128     p_new->p_vars = (variable_t *)malloc( 16 * sizeof( variable_t ) );
129
130     if( !p_new->p_vars )
131     {
132         if( i_type != VLC_OBJECT_GLOBAL )
133             free( p_new );
134         return NULL;
135     }
136
137     if( i_type == VLC_OBJECT_GLOBAL )
138     {
139         /* If i_type is global, then p_new is actually p_libvlc_global */
140         p_new->p_libvlc_global = (libvlc_global_data_t*)p_new;
141         p_new->p_libvlc = NULL;
142
143         p_new->p_libvlc_global->i_counter = 0;
144         p_new->i_object_id = 0;
145
146         p_new->p_libvlc_global->i_objects = 1;
147         p_new->p_libvlc_global->pp_objects = malloc( sizeof(vlc_object_t *) );
148         p_new->p_libvlc_global->pp_objects[0] = p_new;
149         p_new->b_attached = VLC_TRUE;
150     }
151     else
152     {
153         p_new->p_libvlc_global = p_this->p_libvlc_global;
154         p_new->p_libvlc = ( i_type == VLC_OBJECT_LIBVLC ) ? (libvlc_int_t*)p_new
155                                                        : p_this->p_libvlc;
156
157         vlc_mutex_lock( &structure_lock );
158
159         p_new->p_libvlc_global->i_counter++;
160         p_new->i_object_id = p_new->p_libvlc_global->i_counter;
161
162         /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's
163          * useless to try and recover anything if pp_objects gets smashed. */
164         TAB_APPEND( p_new->p_libvlc_global->i_objects,
165                     p_new->p_libvlc_global->pp_objects,
166                     p_new );
167
168         vlc_mutex_unlock( &structure_lock );
169     }
170
171     p_new->i_refcount = 0;
172     p_new->p_parent = NULL;
173     p_new->pp_children = NULL;
174     p_new->i_children = 0;
175
176     p_new->p_private = NULL;
177
178     /* Initialize mutexes and condvars */
179     vlc_mutex_init( p_new, &p_new->object_lock );
180     vlc_cond_init( p_new, &p_new->object_wait );
181     vlc_mutex_init( p_new, &p_new->var_lock );
182
183     if( i_type == VLC_OBJECT_GLOBAL )
184     {
185         vlc_mutex_init( p_new, &structure_lock );
186
187         var_Create( p_new, "list", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
188         var_AddCallback( p_new, "list", DumpCommand, NULL );
189         var_Create( p_new, "tree", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
190         var_AddCallback( p_new, "tree", DumpCommand, NULL );
191         var_Create( p_new, "vars", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
192         var_AddCallback( p_new, "vars", DumpCommand, NULL );
193     }
194
195     return p_new;
196 }
197
198
199 /**
200  * Allocates and initializes a vlc object.
201  *
202  * @param i_type known object type (all of them are negative integer values),
203  *               or object byte size (always positive).
204  *
205  * @return the new object, or NULL on error.
206  */
207 void * __vlc_object_create( vlc_object_t *p_this, int i_type )
208 {
209     const char   * psz_type;
210     size_t         i_size;
211
212     switch( i_type )
213     {
214         case VLC_OBJECT_GLOBAL:
215             i_size = sizeof(libvlc_global_data_t);
216             psz_type = "global";
217             break;
218         case VLC_OBJECT_LIBVLC:
219             i_size = sizeof(libvlc_int_t);
220             psz_type = "libvlc";
221             break;
222         case VLC_OBJECT_MODULE:
223             i_size = sizeof(module_t);
224             psz_type = "module";
225             break;
226         case VLC_OBJECT_INTF:
227             i_size = sizeof(intf_thread_t);
228             psz_type = "interface";
229             break;
230         case VLC_OBJECT_DIALOGS:
231             i_size = sizeof(intf_thread_t);
232             psz_type = "dialogs";
233             break;
234         case VLC_OBJECT_PLAYLIST:
235             i_size = sizeof(playlist_t);
236             psz_type = "playlist";
237             break;
238         case VLC_OBJECT_SD:
239             i_size = sizeof(services_discovery_t);
240             psz_type = "services discovery";
241             break;
242         case VLC_OBJECT_INPUT:
243             i_size = sizeof(input_thread_t);
244             psz_type = "input";
245             break;
246         case VLC_OBJECT_DEMUX:
247             i_size = sizeof(demux_t);
248             psz_type = "demux";
249             break;
250         case VLC_OBJECT_STREAM:
251             i_size = sizeof(stream_t);
252             psz_type = "stream";
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     int i_delay = 0;
345
346     if( p_this->i_children )
347     {
348         msg_Err( p_this, "cannot delete object (%i, %s) with children" ,
349                  p_this->i_object_id, p_this->psz_object_name );
350         return;
351     }
352
353     if( p_this->p_parent )
354     {
355         msg_Err( p_this, "cannot delete object (%i, %s) with a parent",
356                  p_this->i_object_id, p_this->psz_object_name );
357         return;
358     }
359
360     while( p_this->i_refcount )
361     {
362         i_delay++;
363
364         /* Don't warn immediately ... 100ms seems OK */
365         if( i_delay == 2 )
366         {
367             msg_Warn( p_this,
368                   "refcount is %i, delaying before deletion (id=%d,type=%d)",
369                   p_this->i_refcount, p_this->i_object_id,
370                   p_this->i_object_type );
371         }
372         else if( i_delay == 10 )
373         {
374             msg_Err( p_this,
375                   "refcount is %i, delaying again (id=%d,type=%d)",
376                   p_this->i_refcount, p_this->i_object_id,
377                   p_this->i_object_type );
378         }
379         else if( i_delay == 20 )
380         {
381             msg_Err( p_this,
382                   "waited too long, cancelling destruction (id=%d,type=%d)",
383                   p_this->i_object_id, p_this->i_object_type );
384             return;
385         }
386
387         msleep( 100000 );
388     }
389
390     /* Destroy the associated variables, starting from the end so that
391      * no memmove calls have to be done. */
392     while( p_this->i_vars )
393     {
394         var_Destroy( p_this, p_this->p_vars[p_this->i_vars - 1].psz_name );
395     }
396
397     free( p_this->p_vars );
398     vlc_mutex_destroy( &p_this->var_lock );
399
400     if( p_this->psz_header ) free( p_this->psz_header );
401
402     if( p_this->i_object_type == VLC_OBJECT_GLOBAL )
403     {
404         /* We are the global object ... no need to lock. */
405         free( p_this->p_libvlc_global->pp_objects );
406         p_this->p_libvlc_global->pp_objects = NULL;
407         p_this->p_libvlc_global->i_objects--;
408
409         vlc_mutex_destroy( &structure_lock );
410     }
411     else
412     {
413         int i_index;
414
415         vlc_mutex_lock( &structure_lock );
416
417         /* Wooohaa! If *this* fails, we're in serious trouble! Anyway it's
418          * useless to try and recover anything if pp_objects gets smashed. */
419         i_index = FindIndex( p_this, p_this->p_libvlc_global->pp_objects,
420                              p_this->p_libvlc_global->i_objects );
421         REMOVE_ELEM( p_this->p_libvlc_global->pp_objects,
422                      p_this->p_libvlc_global->i_objects, i_index );
423
424         vlc_mutex_unlock( &structure_lock );
425     }
426
427     vlc_mutex_destroy( &p_this->object_lock );
428     vlc_cond_destroy( &p_this->object_wait );
429
430     /* global is not dynamically allocated by vlc_object_create */
431     if( p_this->i_object_type != VLC_OBJECT_GLOBAL )
432     {
433         free( p_this );
434         p_this = NULL;
435     }
436 }
437
438
439 void __vlc_object_die( 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 /**
448  * find an object given its ID
449  *
450  * This function looks for the object whose i_object_id field is i_id. We
451  * use a dichotomy so that lookups are in log2(n).
452  *****************************************************************************/
453 void * __vlc_object_get( vlc_object_t *p_this, int i_id )
454 {
455     int i_max, i_middle;
456     vlc_object_t **pp_objects;
457
458     vlc_mutex_lock( &structure_lock );
459
460     pp_objects = p_this->p_libvlc_global->pp_objects;
461
462     /* Perform our dichotomy */
463     for( i_max = p_this->p_libvlc_global->i_objects - 1 ; ; )
464     {
465         i_middle = i_max / 2;
466
467         if( pp_objects[i_middle]->i_object_id > i_id )
468         {
469             i_max = i_middle;
470         }
471         else if( pp_objects[i_middle]->i_object_id < i_id )
472         {
473             if( i_middle )
474             {
475                 pp_objects += i_middle;
476                 i_max -= i_middle;
477             }
478             else
479             {
480                 /* This happens when there are only two remaining objects */
481                 if( pp_objects[i_middle+1]->i_object_id == i_id )
482                 {
483                     vlc_mutex_unlock( &structure_lock );
484                     pp_objects[i_middle+1]->i_refcount++;
485                     return pp_objects[i_middle+1];
486                 }
487                 break;
488             }
489         }
490         else
491         {
492             vlc_mutex_unlock( &structure_lock );
493             pp_objects[i_middle]->i_refcount++;
494             return pp_objects[i_middle];
495         }
496
497         if( i_max == 0 )
498         {
499             /* this means that i_max == i_middle, and since we have already
500              * tested pp_objects[i_middle]), p_found is properly set. */
501             break;
502         }
503     }
504
505     vlc_mutex_unlock( &structure_lock );
506     return NULL;
507 }
508
509 /**
510  ****************************************************************************
511  * find a typed object and increment its refcount
512  *****************************************************************************
513  * This function recursively looks for a given object type. i_mode can be one
514  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
515  *****************************************************************************/
516 void * __vlc_object_find( vlc_object_t *p_this, int i_type, int i_mode )
517 {
518     vlc_object_t *p_found;
519
520     vlc_mutex_lock( &structure_lock );
521
522     /* If we are of the requested type ourselves, don't look further */
523     if( !(i_mode & FIND_STRICT) && p_this->i_object_type == i_type )
524     {
525         p_this->i_refcount++;
526         vlc_mutex_unlock( &structure_lock );
527         return p_this;
528     }
529
530     /* Otherwise, recursively look for the object */
531     if( (i_mode & 0x000f) == FIND_ANYWHERE )
532     {
533         vlc_object_t *p_root = p_this;
534
535         /* Find the root */
536         while( p_root->p_parent != NULL &&
537                p_root != VLC_OBJECT( p_this->p_libvlc ) )
538         {
539             p_root = p_root->p_parent;
540         }
541
542         p_found = FindObject( p_root, i_type, (i_mode & ~0x000f)|FIND_CHILD );
543         if( p_found == NULL && p_root != VLC_OBJECT( p_this->p_libvlc ) )
544         {
545             p_found = FindObject( VLC_OBJECT( p_this->p_libvlc ),
546                                   i_type, (i_mode & ~0x000f)|FIND_CHILD );
547         }
548     }
549     else
550     {
551         p_found = FindObject( p_this, i_type, i_mode );
552     }
553
554     vlc_mutex_unlock( &structure_lock );
555
556     return p_found;
557 }
558
559 /**
560  ****************************************************************************
561  * increment an object refcount
562  *****************************************************************************/
563 void __vlc_object_yield( vlc_object_t *p_this )
564 {
565     vlc_mutex_lock( &structure_lock );
566     p_this->i_refcount++;
567     vlc_mutex_unlock( &structure_lock );
568 }
569
570 /**
571  ****************************************************************************
572  * decrement an object refcount
573  *****************************************************************************/
574 void __vlc_object_release( vlc_object_t *p_this )
575 {
576     vlc_mutex_lock( &structure_lock );
577     p_this->i_refcount--;
578     vlc_mutex_unlock( &structure_lock );
579 }
580
581 /**
582  ****************************************************************************
583  * attach object to a parent object
584  *****************************************************************************
585  * This function sets p_this as a child of p_parent, and p_parent as a parent
586  * of p_this. This link can be undone using vlc_object_detach.
587  *****************************************************************************/
588 void __vlc_object_attach( vlc_object_t *p_this, vlc_object_t *p_parent )
589 {
590     if( !p_this ) return;
591
592     vlc_mutex_lock( &structure_lock );
593
594     /* Attach the parent to its child */
595     p_this->p_parent = p_parent;
596
597     /* Attach the child to its parent */
598     INSERT_ELEM( p_parent->pp_children, p_parent->i_children,
599                  p_parent->i_children, p_this );
600
601     /* Climb up the tree to see whether we are connected with the root */
602     if( p_parent->b_attached )
603     {
604         SetAttachment( p_this, VLC_TRUE );
605     }
606
607     vlc_mutex_unlock( &structure_lock );
608 }
609
610 /**
611  ****************************************************************************
612  * detach object from its parent
613  *****************************************************************************
614  * This function removes all links between an object and its parent.
615  *****************************************************************************/
616 void __vlc_object_detach( vlc_object_t *p_this )
617 {
618     if( !p_this ) return;
619
620     vlc_mutex_lock( &structure_lock );
621     if( !p_this->p_parent )
622     {
623         msg_Err( p_this, "object is not attached" );
624         vlc_mutex_unlock( &structure_lock );
625         return;
626     }
627
628     /* Climb up the tree to see whether we are connected with the root */
629     if( p_this->p_parent->b_attached )
630     {
631         SetAttachment( p_this, VLC_FALSE );
632     }
633
634     DetachObject( p_this );
635     vlc_mutex_unlock( &structure_lock );
636     p_this = NULL;
637 }
638
639 /**
640  ****************************************************************************
641  * find a list typed objects and increment their refcount
642  *****************************************************************************
643  * This function recursively looks for a given object type. i_mode can be one
644  * of FIND_PARENT, FIND_CHILD or FIND_ANYWHERE.
645  *****************************************************************************/
646 vlc_list_t * __vlc_list_find( vlc_object_t *p_this, int i_type, int i_mode )
647 {
648     vlc_list_t *p_list;
649     vlc_object_t **pp_current, **pp_end;
650     int i_count = 0, i_index = 0;
651
652     vlc_mutex_lock( &structure_lock );
653
654     /* Look for the objects */
655     switch( i_mode & 0x000f )
656     {
657     case FIND_ANYWHERE:
658         pp_current = p_this->p_libvlc_global->pp_objects;
659         pp_end = pp_current + p_this->p_libvlc_global->i_objects;
660
661         for( ; pp_current < pp_end ; pp_current++ )
662         {
663             if( (*pp_current)->b_attached
664                  && (*pp_current)->i_object_type == i_type )
665             {
666                 i_count++;
667             }
668         }
669
670         p_list = NewList( i_count );
671         pp_current = p_this->p_libvlc_global->pp_objects;
672
673         for( ; pp_current < pp_end ; pp_current++ )
674         {
675             if( (*pp_current)->b_attached
676                  && (*pp_current)->i_object_type == i_type )
677             {
678                 ListReplace( p_list, *pp_current, i_index );
679                 if( i_index < i_count ) i_index++;
680             }
681         }
682     break;
683
684     case FIND_CHILD:
685         i_count = CountChildren( p_this, i_type );
686         p_list = NewList( i_count );
687
688         /* Check allocation was successful */
689         if( p_list->i_count != i_count )
690         {
691             msg_Err( p_this, "list allocation failed!" );
692             p_list->i_count = 0;
693             break;
694         }
695
696         p_list->i_count = 0;
697         ListChildren( p_list, p_this, i_type );
698         break;
699
700     default:
701         msg_Err( p_this, "unimplemented!" );
702         p_list = NewList( 0 );
703         break;
704     }
705
706     vlc_mutex_unlock( &structure_lock );
707
708     return p_list;
709 }
710
711 /*****************************************************************************
712  * DumpCommand: print the current vlc structure
713  *****************************************************************************
714  * This function prints either an ASCII tree showing the connections between
715  * vlc objects, and additional information such as their refcount, thread ID,
716  * etc. (command "tree"), or the same data as a simple list (command "list").
717  *****************************************************************************/
718 static int DumpCommand( vlc_object_t *p_this, char const *psz_cmd,
719                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
720 {
721     if( *psz_cmd == 'l' )
722     {
723         vlc_mutex_lock( &structure_lock );
724
725         vlc_object_t **pp_current, **pp_end;
726
727         pp_current = p_this->p_libvlc_global->pp_objects;
728         pp_end = pp_current + p_this->p_libvlc_global->i_objects;
729
730         for( ; pp_current < pp_end ; pp_current++ )
731         {
732             if( (*pp_current)->b_attached )
733             {
734                 PrintObject( *pp_current, "" );
735             }
736             else
737             {
738                 printf( " o %.8i %s (not attached)\n",
739                         (*pp_current)->i_object_id,
740                         (*pp_current)->psz_object_type );
741             }
742         }
743
744         vlc_mutex_unlock( &structure_lock );
745     }
746     else
747     {
748         vlc_object_t *p_object = NULL;
749
750         if( *newval.psz_string )
751         {
752             p_object = vlc_object_get( p_this, atoi(newval.psz_string) );
753
754             if( !p_object )
755             {
756                 return VLC_ENOOBJ;
757             }
758         }
759
760         vlc_mutex_lock( &structure_lock );
761
762         if( *psz_cmd == 't' )
763         {
764             char psz_foo[2 * MAX_DUMPSTRUCTURE_DEPTH + 1];
765
766             if( !p_object )
767                 p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
768
769             psz_foo[0] = '|';
770             DumpStructure( p_object, 0, psz_foo );
771         }
772         else if( *psz_cmd == 'v' )
773         {
774             int i;
775
776             if( !p_object )
777                 p_object = p_this->p_libvlc ? VLC_OBJECT(p_this->p_libvlc) : p_this;
778
779             PrintObject( p_object, "" );
780
781             if( !p_object->i_vars )
782                 printf( " `-o No variables\n" );
783             for( i = 0; i < p_object->i_vars; i++ )
784             {
785                 variable_t *p_var = p_object->p_vars + i;
786
787                 const char *psz_type = "unknown";
788                 switch( p_var->i_type & VLC_VAR_TYPE )
789                 {
790 #define MYCASE( type, nice )                \
791                     case VLC_VAR_ ## type:  \
792                         psz_type = nice;    \
793                         break;
794                     MYCASE( VOID, "void" );
795                     MYCASE( BOOL, "bool" );
796                     MYCASE( INTEGER, "integer" );
797                     MYCASE( HOTKEY, "hotkey" );
798                     MYCASE( STRING, "string" );
799                     MYCASE( MODULE, "module" );
800                     MYCASE( FILE, "file" );
801                     MYCASE( DIRECTORY, "directory" );
802                     MYCASE( VARIABLE, "variable" );
803                     MYCASE( FLOAT, "float" );
804                     MYCASE( TIME, "time" );
805                     MYCASE( ADDRESS, "address" );
806                     MYCASE( MUTEX, "mutex" );
807                     MYCASE( LIST, "list" );
808 #undef MYCASE
809                 }
810                 printf( " %c-o \"%s\" (%s",
811                         i + 1 == p_object->i_vars ? '`' : '|',
812                         p_var->psz_name, psz_type );
813                 if( p_var->psz_text )
814                     printf( ", %s", p_var->psz_text );
815                 printf( ")" );
816                 if( p_var->i_type & VLC_VAR_ISCOMMAND )
817                     printf( ", command" );
818                 if( p_var->i_entries )
819                     printf( ", %d callbacks", p_var->i_entries );
820                 switch( p_var->i_type & 0x00f0 )
821                 {
822                     case VLC_VAR_VOID:
823                     case VLC_VAR_MUTEX:
824                         break;
825                     case VLC_VAR_BOOL:
826                         printf( ": %s", p_var->val.b_bool ? "true" : "false" );
827                         break;
828                     case VLC_VAR_INTEGER:
829                         printf( ": %d", p_var->val.i_int );
830                         break;
831                     case VLC_VAR_STRING:
832                         printf( ": \"%s\"", p_var->val.psz_string );
833                         break;
834                     case VLC_VAR_FLOAT:
835                         printf( ": %f", p_var->val.f_float );
836                         break;
837                     case VLC_VAR_TIME:
838                         printf( ": " I64Fi, p_var->val.i_time );
839                         break;
840                     case VLC_VAR_ADDRESS:
841                         printf( ": %p", p_var->val.p_address );
842                         break;
843                     case VLC_VAR_LIST:
844                         printf( ": TODO" );
845                         break;
846                 }
847                 printf( "\n" );
848             }
849         }
850
851         vlc_mutex_unlock( &structure_lock );
852
853         if( *newval.psz_string )
854         {
855             vlc_object_release( p_object );
856         }
857     }
858
859     return VLC_SUCCESS;
860 }
861
862 /*****************************************************************************
863  * vlc_list_release: free a list previously allocated by vlc_list_find
864  *****************************************************************************
865  * This function decreases the refcount of all objects in the list and
866  * frees the list.
867  *****************************************************************************/
868 void vlc_list_release( vlc_list_t *p_list )
869 {
870     int i_index;
871
872     vlc_mutex_lock( &structure_lock );
873     for( i_index = 0; i_index < p_list->i_count; i_index++ )
874     {
875         p_list->p_values[i_index].p_object->i_refcount--;
876     }
877     vlc_mutex_unlock( &structure_lock );
878
879     free( p_list->p_values );
880     free( p_list );
881 }
882
883 /* Following functions are local */
884
885 /*****************************************************************************
886  * FindIndex: find the index of an object in an array of objects
887  *****************************************************************************
888  * This function assumes that p_this can be found in pp_objects. It will not
889  * crash if p_this cannot be found, but will return a wrong value. It is your
890  * duty to check the return value if you are not certain that the object could
891  * be found for sure.
892  *****************************************************************************/
893 static int FindIndex( vlc_object_t *p_this,
894                       vlc_object_t **pp_objects, int i_count )
895 {
896     int i_middle = i_count / 2;
897
898     if( i_count == 0 )
899     {
900         return 0;
901     }
902
903     if( pp_objects[i_middle] == p_this )
904     {
905         return i_middle;
906     }
907
908     if( i_count == 1 )
909     {
910         return 0;
911     }
912
913     /* We take advantage of the sorted array */
914     if( pp_objects[i_middle]->i_object_id < p_this->i_object_id )
915     {
916         return i_middle + FindIndex( p_this, pp_objects + i_middle,
917                                              i_count - i_middle );
918     }
919     else
920     {
921         return FindIndex( p_this, pp_objects, i_middle );
922     }
923 }
924
925 static vlc_object_t * FindObject( vlc_object_t *p_this, int i_type, int i_mode )
926 {
927     int i;
928     vlc_object_t *p_tmp;
929
930     switch( i_mode & 0x000f )
931     {
932     case FIND_PARENT:
933         p_tmp = p_this->p_parent;
934         if( p_tmp )
935         {
936             if( p_tmp->i_object_type == i_type )
937             {
938                 p_tmp->i_refcount++;
939                 return p_tmp;
940             }
941             else
942             {
943                 return FindObject( p_tmp, i_type, i_mode );
944             }
945         }
946         break;
947
948     case FIND_CHILD:
949         for( i = p_this->i_children; i--; )
950         {
951             p_tmp = p_this->pp_children[i];
952             if( p_tmp->i_object_type == i_type )
953             {
954                 p_tmp->i_refcount++;
955                 return p_tmp;
956             }
957             else if( p_tmp->i_children )
958             {
959                 p_tmp = FindObject( p_tmp, i_type, i_mode );
960                 if( p_tmp )
961                 {
962                     return p_tmp;
963                 }
964             }
965         }
966         break;
967
968     case FIND_ANYWHERE:
969         /* Handled in vlc_object_find */
970         break;
971     }
972
973     return NULL;
974 }
975
976 static void DetachObject( vlc_object_t *p_this )
977 {
978     vlc_object_t *p_parent = p_this->p_parent;
979     int i_index, i;
980
981     /* Remove p_this's parent */
982     p_this->p_parent = NULL;
983
984     /* Remove all of p_parent's children which are p_this */
985     for( i_index = p_parent->i_children ; i_index-- ; )
986     {
987         if( p_parent->pp_children[i_index] == p_this )
988         {
989             p_parent->i_children--;
990             for( i = i_index ; i < p_parent->i_children ; i++ )
991             {
992                 p_parent->pp_children[i] = p_parent->pp_children[i+1];
993             }
994         }
995     }
996
997     if( p_parent->i_children )
998     {
999         p_parent->pp_children = (vlc_object_t **)realloc( p_parent->pp_children,
1000                                p_parent->i_children * sizeof(vlc_object_t *) );
1001     }
1002     else
1003     {
1004         free( p_parent->pp_children );
1005         p_parent->pp_children = NULL;
1006     }
1007 }
1008
1009 /*****************************************************************************
1010  * SetAttachment: recursively set the b_attached flag of a subtree.
1011  *****************************************************************************
1012  * This function is used by the attach and detach functions to propagate
1013  * the b_attached flag in a subtree.
1014  *****************************************************************************/
1015 static void SetAttachment( vlc_object_t *p_this, vlc_bool_t b_attached )
1016 {
1017     int i_index;
1018
1019     for( i_index = p_this->i_children ; i_index-- ; )
1020     {
1021         SetAttachment( p_this->pp_children[i_index], b_attached );
1022     }
1023
1024     p_this->b_attached = b_attached;
1025 }
1026
1027 static void PrintObject( vlc_object_t *p_this, const char *psz_prefix )
1028 {
1029     char psz_children[20], psz_refcount[20], psz_thread[30], psz_name[50],
1030          psz_parent[20];
1031
1032     psz_name[0] = '\0';
1033     if( p_this->psz_object_name )
1034     {
1035         snprintf( psz_name, 49, " \"%s\"", p_this->psz_object_name );
1036         if( psz_name[48] )
1037             psz_name[48] = '\"';
1038     }
1039
1040     psz_children[0] = '\0';
1041     switch( p_this->i_children )
1042     {
1043         case 0:
1044             break;
1045         case 1:
1046             strcpy( psz_children, ", 1 child" );
1047             break;
1048         default:
1049             snprintf( psz_children, 19, ", %i children", p_this->i_children );
1050             break;
1051     }
1052
1053     psz_refcount[0] = '\0';
1054     if( p_this->i_refcount )
1055         snprintf( psz_refcount, 19, ", refcount %i", p_this->i_refcount );
1056
1057     psz_thread[0] = '\0';
1058     if( p_this->b_thread )
1059         snprintf( psz_thread, 29, " (thread %d)", (int)p_this->thread_id );
1060
1061     psz_parent[0] = '\0';
1062     if( p_this->p_parent )
1063         snprintf( psz_parent, 19, ", parent %i", p_this->p_parent->i_object_id );
1064
1065     printf( " %so %.8i %s%s%s%s%s%s\n", psz_prefix,
1066             p_this->i_object_id, p_this->psz_object_type,
1067             psz_name, psz_thread, psz_refcount, psz_children,
1068             psz_parent );
1069 }
1070
1071 static void DumpStructure( vlc_object_t *p_this, int i_level, char *psz_foo )
1072 {
1073     int i;
1074     char i_back = psz_foo[i_level];
1075     psz_foo[i_level] = '\0';
1076
1077     PrintObject( p_this, psz_foo );
1078
1079     psz_foo[i_level] = i_back;
1080
1081     if( i_level / 2 >= MAX_DUMPSTRUCTURE_DEPTH )
1082     {
1083         msg_Warn( p_this, "structure tree is too deep" );
1084         return;
1085     }
1086
1087     for( i = 0 ; i < p_this->i_children ; i++ )
1088     {
1089         if( i_level )
1090         {
1091             psz_foo[i_level-1] = ' ';
1092
1093             if( psz_foo[i_level-2] == '`' )
1094             {
1095                 psz_foo[i_level-2] = ' ';
1096             }
1097         }
1098
1099         if( i == p_this->i_children - 1 )
1100         {
1101             psz_foo[i_level] = '`';
1102         }
1103         else
1104         {
1105             psz_foo[i_level] = '|';
1106         }
1107
1108         psz_foo[i_level+1] = '-';
1109         psz_foo[i_level+2] = '\0';
1110
1111         DumpStructure( p_this->pp_children[i], i_level + 2, psz_foo );
1112     }
1113 }
1114
1115 static vlc_list_t * NewList( int i_count )
1116 {
1117     vlc_list_t * p_list = (vlc_list_t *)malloc( sizeof( vlc_list_t ) );
1118     if( p_list == NULL )
1119     {
1120         return NULL;
1121     }
1122
1123     p_list->i_count = i_count;
1124
1125     if( i_count == 0 )
1126     {
1127         p_list->p_values = NULL;
1128         return p_list;
1129     }
1130
1131     p_list->p_values = malloc( i_count * sizeof( vlc_value_t ) );
1132     if( p_list->p_values == NULL )
1133     {
1134         p_list->i_count = 0;
1135         return p_list;
1136     }
1137
1138     return p_list;
1139 }
1140
1141 static void ListReplace( vlc_list_t *p_list, vlc_object_t *p_object,
1142                          int i_index )
1143 {
1144     if( p_list == NULL || i_index >= p_list->i_count )
1145     {
1146         return;
1147     }
1148
1149     p_object->i_refcount++;
1150
1151     p_list->p_values[i_index].p_object = p_object;
1152
1153     return;
1154 }
1155
1156 /*static void ListAppend( vlc_list_t *p_list, vlc_object_t *p_object )
1157 {
1158     if( p_list == NULL )
1159     {
1160         return;
1161     }
1162
1163     p_list->p_values = realloc( p_list->p_values, (p_list->i_count + 1)
1164                                 * sizeof( vlc_value_t ) );
1165     if( p_list->p_values == NULL )
1166     {
1167         p_list->i_count = 0;
1168         return;
1169     }
1170
1171     p_object->i_refcount++;
1172
1173     p_list->p_values[p_list->i_count].p_object = p_object;
1174     p_list->i_count++;
1175
1176     return;
1177 }*/
1178
1179 static int CountChildren( vlc_object_t *p_this, int i_type )
1180 {
1181     vlc_object_t *p_tmp;
1182     int i, i_count = 0;
1183
1184     for( i = 0; i < p_this->i_children; i++ )
1185     {
1186         p_tmp = p_this->pp_children[i];
1187
1188         if( p_tmp->i_object_type == i_type )
1189         {
1190             i_count++;
1191         }
1192
1193         if( p_tmp->i_children )
1194         {
1195             i_count += CountChildren( p_tmp, i_type );
1196         }
1197     }
1198
1199     return i_count;
1200 }
1201
1202 static void ListChildren( vlc_list_t *p_list, vlc_object_t *p_this, int i_type )
1203 {
1204     vlc_object_t *p_tmp;
1205     int i;
1206
1207     for( i = 0; i < p_this->i_children; i++ )
1208     {
1209         p_tmp = p_this->pp_children[i];
1210
1211         if( p_tmp->i_object_type == i_type )
1212         {
1213             ListReplace( p_list, p_tmp, p_list->i_count++ );
1214         }
1215
1216         if( p_tmp->i_children )
1217         {
1218             ListChildren( p_list, p_tmp, i_type );
1219         }
1220     }
1221 }