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