1 /*****************************************************************************
2 * variables.c: routines for object variables handling
3 *****************************************************************************
4 * Copyright (C) 2002-2006 the VideoLAN team
7 * Authors: Samuel Hocevar <sam@zoy.org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
31 #include <vlc_common.h>
32 #include "variables.h"
36 #include "vlc_interface.h"
38 /*****************************************************************************
40 *****************************************************************************/
41 struct callback_entry_t
43 vlc_callback_t pf_callback
;
47 /*****************************************************************************
48 * Local comparison functions, returns 0 if v == w, < 0 if v < w, > 0 if v > w
49 *****************************************************************************/
50 static int CmpBool( vlc_value_t v
, vlc_value_t w
) { return v
.b_bool
? w
.b_bool
? 0 : 1 : w
.b_bool
? -1 : 0; }
51 static int CmpInt( vlc_value_t v
, vlc_value_t w
) { return v
.i_int
== w
.i_int
? 0 : v
.i_int
> w
.i_int
? 1 : -1; }
52 static int CmpTime( vlc_value_t v
, vlc_value_t w
)
54 return v
.i_time
== w
.i_time
? 0 : v
.i_time
> w
.i_time
? 1 : -1;
56 static int CmpString( vlc_value_t v
, vlc_value_t w
)
59 return !w
.psz_string
? 0 : -1;
61 return !w
.psz_string
? 1 : strcmp( v
.psz_string
, w
.psz_string
);
63 static int CmpFloat( vlc_value_t v
, vlc_value_t w
) { return v
.f_float
== w
.f_float
? 0 : v
.f_float
> w
.f_float
? 1 : -1; }
64 static int CmpAddress( vlc_value_t v
, vlc_value_t w
) { return v
.p_address
== w
.p_address
? 0 : v
.p_address
> w
.p_address
? 1 : -1; }
66 /*****************************************************************************
67 * Local duplication functions, and local deallocation functions
68 *****************************************************************************/
69 static void DupDummy( vlc_value_t
*p_val
) { (void)p_val
; /* unused */ }
70 static void DupString( vlc_value_t
*p_val
) { if( p_val
->psz_string
) p_val
->psz_string
= strdup( p_val
->psz_string
); }
72 static void DupList( vlc_value_t
*p_val
)
75 vlc_list_t
*p_list
= malloc( sizeof(vlc_list_t
) );
77 p_list
->i_count
= p_val
->p_list
->i_count
;
78 if( p_val
->p_list
->i_count
)
80 p_list
->p_values
= malloc( p_list
->i_count
* sizeof(vlc_value_t
) );
81 p_list
->pi_types
= malloc( p_list
->i_count
* sizeof(int) );
85 p_list
->p_values
= NULL
;
86 p_list
->pi_types
= NULL
;
89 for( i
= 0; i
< p_list
->i_count
; i
++ )
91 p_list
->p_values
[i
] = p_val
->p_list
->p_values
[i
];
92 p_list
->pi_types
[i
] = p_val
->p_list
->pi_types
[i
];
93 switch( p_val
->p_list
->pi_types
[i
] & VLC_VAR_TYPE
)
97 DupString( &p_list
->p_values
[i
] );
104 p_val
->p_list
= p_list
;
107 static void FreeDummy( vlc_value_t
*p_val
) { (void)p_val
; /* unused */ }
108 static void FreeString( vlc_value_t
*p_val
) { free( p_val
->psz_string
); }
109 static void FreeMutex( vlc_value_t
*p_val
) { vlc_mutex_destroy( (vlc_mutex_t
*)p_val
->p_address
); free( p_val
->p_address
); }
111 static void FreeList( vlc_value_t
*p_val
)
114 for( i
= 0; i
< p_val
->p_list
->i_count
; i
++ )
116 switch( p_val
->p_list
->pi_types
[i
] & VLC_VAR_TYPE
)
119 FreeString( &p_val
->p_list
->p_values
[i
] );
122 FreeMutex( &p_val
->p_list
->p_values
[i
] );
129 if( p_val
->p_list
->i_count
)
131 free( p_val
->p_list
->p_values
);
132 free( p_val
->p_list
->pi_types
);
134 free( p_val
->p_list
);
137 /*****************************************************************************
139 *****************************************************************************/
140 static int GetUnused ( vlc_object_t
*, const char * );
141 static uint32_t HashString ( const char * );
142 static int Insert ( variable_t
*, int, const char * );
143 static int InsertInner ( variable_t
*, int, uint32_t );
144 static int Lookup ( variable_t
*, int, const char * );
145 static int LookupInner ( variable_t
*, int, uint32_t );
147 static void CheckValue ( variable_t
*, vlc_value_t
* );
149 static int InheritValue( vlc_object_t
*, const char *, vlc_value_t
*,
153 * Initialize a vlc variable
155 * We hash the given string and insert it into the sorted list. The insertion
156 * may require slow memory copies, but think about what we gain in the log(n)
157 * lookup phase when setting/getting the variable value!
159 * \param p_this The object in which to create the variable
160 * \param psz_name The name of the variable
161 * \param i_type The variables type. Must be one of \ref var_type combined with
162 * zero or more \ref var_flags
164 int __var_Create( vlc_object_t
*p_this
, const char *psz_name
, int i_type
)
168 static vlc_list_t dummy_null_list
= {0, NULL
, NULL
};
169 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
171 vlc_refcheck( p_this
);
172 vlc_mutex_lock( &p_priv
->var_lock
);
174 /* FIXME: if the variable already exists, we don't duplicate it. But we
175 * duplicate the lookups. It's not that serious, but if anyone finds some
176 * time to rework Insert() so that only one lookup has to be done, feel
178 i_new
= Lookup( p_priv
->p_vars
, p_priv
->i_vars
, psz_name
);
182 /* If the types differ, variable creation failed. */
183 if( (i_type
& ~(VLC_VAR_DOINHERIT
|VLC_VAR_ISCOMMAND
)) != p_priv
->p_vars
[i_new
].i_type
)
185 vlc_mutex_unlock( &p_priv
->var_lock
);
189 p_priv
->p_vars
[i_new
].i_usage
++;
190 if( i_type
& VLC_VAR_ISCOMMAND
)
191 p_priv
->p_vars
[i_new
].i_type
|= VLC_VAR_ISCOMMAND
;
192 vlc_mutex_unlock( &p_priv
->var_lock
);
196 i_new
= Insert( p_priv
->p_vars
, p_priv
->i_vars
, psz_name
);
198 if( (p_priv
->i_vars
& 15) == 15 )
200 p_priv
->p_vars
= realloc( p_priv
->p_vars
,
201 (p_priv
->i_vars
+17) * sizeof(variable_t
) );
204 memmove( p_priv
->p_vars
+ i_new
+ 1,
205 p_priv
->p_vars
+ i_new
,
206 (p_priv
->i_vars
- i_new
) * sizeof(variable_t
) );
210 p_var
= &p_priv
->p_vars
[i_new
];
211 memset( p_var
, 0, sizeof(*p_var
) );
213 p_var
->i_hash
= HashString( psz_name
);
214 p_var
->psz_name
= strdup( psz_name
);
215 p_var
->psz_text
= NULL
;
217 p_var
->i_type
= i_type
& ~VLC_VAR_DOINHERIT
;
218 memset( &p_var
->val
, 0, sizeof(vlc_value_t
) );
220 p_var
->pf_dup
= DupDummy
;
221 p_var
->pf_free
= FreeDummy
;
225 p_var
->i_default
= -1;
226 p_var
->choices
.i_count
= 0;
227 p_var
->choices
.p_values
= NULL
;
228 p_var
->choices_text
.i_count
= 0;
229 p_var
->choices_text
.p_values
= NULL
;
231 p_var
->b_incallback
= false;
232 p_var
->i_entries
= 0;
233 p_var
->p_entries
= NULL
;
235 /* Always initialize the variable, even if it is a list variable; this
236 * will lead to errors if the variable is not initialized, but it will
237 * not cause crashes in the variable handling. */
238 switch( i_type
& VLC_VAR_TYPE
)
241 p_var
->pf_cmp
= CmpBool
;
242 p_var
->val
.b_bool
= false;
244 case VLC_VAR_INTEGER
:
246 p_var
->pf_cmp
= CmpInt
;
247 p_var
->val
.i_int
= 0;
252 case VLC_VAR_DIRECTORY
:
253 case VLC_VAR_VARIABLE
:
254 p_var
->pf_cmp
= CmpString
;
255 p_var
->pf_dup
= DupString
;
256 p_var
->pf_free
= FreeString
;
257 p_var
->val
.psz_string
= NULL
;
260 p_var
->pf_cmp
= CmpFloat
;
261 p_var
->val
.f_float
= 0.0;
264 p_var
->pf_cmp
= CmpTime
;
265 p_var
->val
.i_time
= 0;
267 case VLC_VAR_ADDRESS
:
268 p_var
->pf_cmp
= CmpAddress
;
269 p_var
->val
.p_address
= NULL
;
272 p_var
->pf_cmp
= CmpAddress
;
273 p_var
->pf_free
= FreeMutex
;
274 p_var
->val
.p_address
= malloc( sizeof(vlc_mutex_t
) );
275 vlc_mutex_init( (vlc_mutex_t
*)p_var
->val
.p_address
);
278 p_var
->pf_cmp
= CmpAddress
;
279 p_var
->pf_dup
= DupList
;
280 p_var
->pf_free
= FreeList
;
281 p_var
->val
.p_list
= &dummy_null_list
;
285 /* Duplicate the default data we stored. */
286 p_var
->pf_dup( &p_var
->val
);
288 if( i_type
& VLC_VAR_DOINHERIT
)
292 if( InheritValue( p_this
, psz_name
, &val
, p_var
->i_type
)
295 /* Free data if needed */
296 p_var
->pf_free( &p_var
->val
);
297 /* Set the variable */
300 if( i_type
& VLC_VAR_HASCHOICE
)
302 /* We must add the inherited value to our choice list */
303 p_var
->i_default
= 0;
305 INSERT_ELEM( p_var
->choices
.p_values
, p_var
->choices
.i_count
,
307 INSERT_ELEM( p_var
->choices_text
.p_values
,
308 p_var
->choices_text
.i_count
, 0, val
);
309 p_var
->pf_dup( &p_var
->choices
.p_values
[0] );
310 p_var
->choices_text
.p_values
[0].psz_string
= NULL
;
315 vlc_mutex_unlock( &p_priv
->var_lock
);
321 * Destroy a vlc variable
323 * Look for the variable and destroy it if it is found. As in var_Create we
324 * do a call to memmove() but we have performance counterparts elsewhere.
326 * \param p_this The object that holds the variable
327 * \param psz_name The name of the variable
329 int __var_Destroy( vlc_object_t
*p_this
, const char *psz_name
)
333 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
335 vlc_refcheck( p_this
);
336 vlc_mutex_lock( &p_priv
->var_lock
);
338 i_var
= GetUnused( p_this
, psz_name
);
341 vlc_mutex_unlock( &p_priv
->var_lock
);
345 p_var
= &p_priv
->p_vars
[i_var
];
347 if( p_var
->i_usage
> 1 )
350 vlc_mutex_unlock( &p_priv
->var_lock
);
354 /* Free value if needed */
355 p_var
->pf_free( &p_var
->val
);
357 /* Free choice list if needed */
358 if( p_var
->choices
.i_count
)
360 for( i
= 0 ; i
< p_var
->choices
.i_count
; i
++ )
362 p_var
->pf_free( &p_var
->choices
.p_values
[i
] );
363 free( p_var
->choices_text
.p_values
[i
].psz_string
);
365 free( p_var
->choices
.p_values
);
366 free( p_var
->choices_text
.p_values
);
369 /* Free callbacks if needed */
370 if( p_var
->p_entries
)
372 free( p_var
->p_entries
);
375 free( p_var
->psz_name
);
376 free( p_var
->psz_text
);
378 memmove( p_priv
->p_vars
+ i_var
,
379 p_priv
->p_vars
+ i_var
+ 1,
380 (p_priv
->i_vars
- i_var
- 1) * sizeof(variable_t
) );
382 if( (p_priv
->i_vars
& 15) == 0 )
384 p_priv
->p_vars
= realloc( p_priv
->p_vars
,
385 (p_priv
->i_vars
) * sizeof( variable_t
) );
390 vlc_mutex_unlock( &p_priv
->var_lock
);
396 * Perform an action on a variable
398 * \param p_this The object that holds the variable
399 * \param psz_name The name of the variable
400 * \param i_action The action to perform. Must be one of \ref var_action
401 * \param p_val First action parameter
402 * \param p_val2 Second action parameter
404 int __var_Change( vlc_object_t
*p_this
, const char *psz_name
,
405 int i_action
, vlc_value_t
*p_val
, vlc_value_t
*p_val2
)
410 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
412 vlc_refcheck( p_this
);
413 vlc_mutex_lock( &p_priv
->var_lock
);
415 i_var
= Lookup( p_priv
->p_vars
, p_priv
->i_vars
, psz_name
);
419 vlc_mutex_unlock( &p_priv
->var_lock
);
423 p_var
= &p_priv
->p_vars
[i_var
];
428 if( p_var
->i_type
& VLC_VAR_HASMIN
)
430 p_var
->pf_free( &p_var
->min
);
432 p_var
->i_type
|= VLC_VAR_HASMIN
;
434 p_var
->pf_dup( &p_var
->min
);
435 CheckValue( p_var
, &p_var
->val
);
438 if( p_var
->i_type
& VLC_VAR_HASMIN
)
444 if( p_var
->i_type
& VLC_VAR_HASMAX
)
446 p_var
->pf_free( &p_var
->max
);
448 p_var
->i_type
|= VLC_VAR_HASMAX
;
450 p_var
->pf_dup( &p_var
->max
);
451 CheckValue( p_var
, &p_var
->val
);
454 if( p_var
->i_type
& VLC_VAR_HASMAX
)
459 case VLC_VAR_SETSTEP
:
460 if( p_var
->i_type
& VLC_VAR_HASSTEP
)
462 p_var
->pf_free( &p_var
->step
);
464 p_var
->i_type
|= VLC_VAR_HASSTEP
;
465 p_var
->step
= *p_val
;
466 p_var
->pf_dup( &p_var
->step
);
467 CheckValue( p_var
, &p_var
->val
);
469 case VLC_VAR_GETSTEP
:
470 if( p_var
->i_type
& VLC_VAR_HASSTEP
)
472 *p_val
= p_var
->step
;
475 case VLC_VAR_ADDCHOICE
:
476 /* FIXME: the list is sorted, dude. Use something cleverer. */
477 for( i
= p_var
->choices
.i_count
; i
-- ; )
479 if( p_var
->pf_cmp( p_var
->choices
.p_values
[i
], *p_val
) < 0 )
485 /* The new place is i+1 */
488 if( p_var
->i_default
>= i
)
493 INSERT_ELEM( p_var
->choices
.p_values
, p_var
->choices
.i_count
,
495 INSERT_ELEM( p_var
->choices_text
.p_values
,
496 p_var
->choices_text
.i_count
, i
, *p_val
);
497 p_var
->pf_dup( &p_var
->choices
.p_values
[i
] );
498 p_var
->choices_text
.p_values
[i
].psz_string
=
499 ( p_val2
&& p_val2
->psz_string
) ?
500 strdup( p_val2
->psz_string
) : NULL
;
502 CheckValue( p_var
, &p_var
->val
);
504 case VLC_VAR_DELCHOICE
:
505 /* FIXME: the list is sorted, dude. Use something cleverer. */
506 for( i
= 0 ; i
< p_var
->choices
.i_count
; i
++ )
508 if( p_var
->pf_cmp( p_var
->choices
.p_values
[i
], *p_val
) == 0 )
514 if( i
== p_var
->choices
.i_count
)
517 vlc_mutex_unlock( &p_priv
->var_lock
);
521 if( p_var
->i_default
> i
)
525 else if( p_var
->i_default
== i
)
527 p_var
->i_default
= -1;
530 p_var
->pf_free( &p_var
->choices
.p_values
[i
] );
531 free( p_var
->choices_text
.p_values
[i
].psz_string
);
532 REMOVE_ELEM( p_var
->choices
.p_values
, p_var
->choices
.i_count
, i
);
533 REMOVE_ELEM( p_var
->choices_text
.p_values
,
534 p_var
->choices_text
.i_count
, i
);
536 CheckValue( p_var
, &p_var
->val
);
538 case VLC_VAR_CHOICESCOUNT
:
539 p_val
->i_int
= p_var
->choices
.i_count
;
541 case VLC_VAR_CLEARCHOICES
:
542 for( i
= 0 ; i
< p_var
->choices
.i_count
; i
++ )
544 p_var
->pf_free( &p_var
->choices
.p_values
[i
] );
546 for( i
= 0 ; i
< p_var
->choices_text
.i_count
; i
++ )
547 free( p_var
->choices_text
.p_values
[i
].psz_string
);
549 if( p_var
->choices
.i_count
) free( p_var
->choices
.p_values
);
550 if( p_var
->choices_text
.i_count
) free( p_var
->choices_text
.p_values
);
552 p_var
->choices
.i_count
= 0;
553 p_var
->choices
.p_values
= NULL
;
554 p_var
->choices_text
.i_count
= 0;
555 p_var
->choices_text
.p_values
= NULL
;
556 p_var
->i_default
= -1;
558 case VLC_VAR_SETDEFAULT
:
559 /* FIXME: the list is sorted, dude. Use something cleverer. */
560 for( i
= 0 ; i
< p_var
->choices
.i_count
; i
++ )
562 if( p_var
->pf_cmp( p_var
->choices
.p_values
[i
], *p_val
) == 0 )
568 if( i
== p_var
->choices
.i_count
)
574 p_var
->i_default
= i
;
575 CheckValue( p_var
, &p_var
->val
);
577 case VLC_VAR_SETVALUE
:
578 /* Duplicate data if needed */
579 p_var
->pf_dup( p_val
);
580 /* Backup needed stuff */
582 /* Check boundaries and list */
583 CheckValue( p_var
, p_val
);
584 /* Set the variable */
586 /* Free data if needed */
587 p_var
->pf_free( &oldval
);
589 case VLC_VAR_GETCHOICES
:
590 case VLC_VAR_GETLIST
:
591 p_val
->p_list
= malloc( sizeof(vlc_list_t
) );
592 if( p_val2
) p_val2
->p_list
= malloc( sizeof(vlc_list_t
) );
593 if( p_var
->choices
.i_count
)
595 p_val
->p_list
->p_values
= malloc( p_var
->choices
.i_count
596 * sizeof(vlc_value_t
) );
597 p_val
->p_list
->pi_types
= malloc( p_var
->choices
.i_count
601 p_val2
->p_list
->p_values
=
602 malloc( p_var
->choices
.i_count
* sizeof(vlc_value_t
) );
603 p_val2
->p_list
->pi_types
=
604 malloc( p_var
->choices
.i_count
* sizeof(int) );
607 p_val
->p_list
->i_count
= p_var
->choices
.i_count
;
608 if( p_val2
) p_val2
->p_list
->i_count
= p_var
->choices
.i_count
;
609 for( i
= 0 ; i
< p_var
->choices
.i_count
; i
++ )
611 p_val
->p_list
->p_values
[i
] = p_var
->choices
.p_values
[i
];
612 p_val
->p_list
->pi_types
[i
] = p_var
->i_type
;
613 p_var
->pf_dup( &p_val
->p_list
->p_values
[i
] );
616 p_val2
->p_list
->p_values
[i
].psz_string
=
617 p_var
->choices_text
.p_values
[i
].psz_string
?
618 strdup(p_var
->choices_text
.p_values
[i
].psz_string
) : NULL
;
619 p_val2
->p_list
->pi_types
[i
] = VLC_VAR_STRING
;
623 case VLC_VAR_FREELIST
:
625 if( p_val2
&& p_val2
->p_list
)
627 for( i
= 0; i
< p_val2
->p_list
->i_count
; i
++ )
628 free( p_val2
->p_list
->p_values
[i
].psz_string
);
629 if( p_val2
->p_list
->i_count
)
631 free( p_val2
->p_list
->p_values
);
632 free( p_val2
->p_list
->pi_types
);
634 free( p_val2
->p_list
);
637 case VLC_VAR_SETTEXT
:
638 free( p_var
->psz_text
);
639 if( p_val
&& p_val
->psz_string
)
640 p_var
->psz_text
= strdup( p_val
->psz_string
);
642 case VLC_VAR_GETTEXT
:
643 p_val
->psz_string
= NULL
;
644 if( p_var
->psz_text
)
646 p_val
->psz_string
= strdup( p_var
->psz_text
);
649 case VLC_VAR_INHERITVALUE
:
653 if( InheritValue( p_this
,
654 p_val2
? p_val2
->psz_string
: psz_name
,
655 &val
, p_var
->i_type
)
658 /* Duplicate already done */
660 /* Backup needed stuff */
662 /* Check boundaries and list */
663 CheckValue( p_var
, &val
);
664 /* Set the variable */
666 /* Free data if needed */
667 p_var
->pf_free( &oldval
);
673 p_var
->pf_dup( p_val
);
677 case VLC_VAR_TRIGGER_CALLBACKS
:
679 /* Deal with callbacks. Tell we're in a callback, release the lock,
680 * call stored functions, retake the lock. */
681 if( p_var
->i_entries
)
684 int i_entries
= p_var
->i_entries
;
685 callback_entry_t
*p_entries
= p_var
->p_entries
;
687 p_var
->b_incallback
= true;
688 vlc_mutex_unlock( &p_priv
->var_lock
);
691 for( ; i_entries
-- ; )
693 p_entries
[i_entries
].pf_callback( p_this
, psz_name
, p_var
->val
, p_var
->val
,
694 p_entries
[i_entries
].p_data
);
697 vlc_mutex_lock( &p_priv
->var_lock
);
699 i_var
= Lookup( p_priv
->p_vars
, p_priv
->i_vars
, psz_name
);
702 msg_Err( p_this
, "variable %s has disappeared", psz_name
);
703 vlc_mutex_unlock( &p_priv
->var_lock
);
707 p_var
= &p_priv
->p_vars
[i_var
];
708 p_var
->b_incallback
= false;
717 vlc_mutex_unlock( &p_priv
->var_lock
);
723 * Request a variable's type
725 * \return The variable type if it exists, or 0 if the
726 * variable could not be found.
729 int __var_Type( vlc_object_t
*p_this
, const char *psz_name
)
732 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
734 vlc_mutex_lock( &p_priv
->var_lock
);
736 i_var
= Lookup( p_priv
->p_vars
, p_priv
->i_vars
, psz_name
);
740 vlc_mutex_unlock( &p_priv
->var_lock
);
744 i_type
= p_priv
->p_vars
[i_var
].i_type
;
746 vlc_mutex_unlock( &p_priv
->var_lock
);
752 * Set a variable's value
754 * \param p_this The object that hold the variable
755 * \param psz_name The name of the variable
756 * \param val the value to set
758 int __var_Set( vlc_object_t
*p_this
, const char *psz_name
, vlc_value_t val
)
763 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
765 vlc_refcheck( p_this
);
766 vlc_mutex_lock( &p_priv
->var_lock
);
768 i_var
= GetUnused( p_this
, psz_name
);
771 vlc_mutex_unlock( &p_priv
->var_lock
);
775 p_var
= &p_priv
->p_vars
[i_var
];
777 /* Duplicate data if needed */
778 p_var
->pf_dup( &val
);
780 /* Backup needed stuff */
783 /* Check boundaries and list */
784 CheckValue( p_var
, &val
);
786 /* Set the variable */
789 /* Deal with callbacks. Tell we're in a callback, release the lock,
790 * call stored functions, retake the lock. */
791 if( p_var
->i_entries
)
794 int i_entries
= p_var
->i_entries
;
795 callback_entry_t
*p_entries
= p_var
->p_entries
;
797 p_var
->b_incallback
= true;
798 vlc_mutex_unlock( &p_priv
->var_lock
);
801 for( ; i_entries
-- ; )
803 p_entries
[i_entries
].pf_callback( p_this
, psz_name
, oldval
, val
,
804 p_entries
[i_entries
].p_data
);
807 vlc_mutex_lock( &p_priv
->var_lock
);
809 i_var
= Lookup( p_priv
->p_vars
, p_priv
->i_vars
, psz_name
);
812 msg_Err( p_this
, "variable %s has disappeared", psz_name
);
813 vlc_mutex_unlock( &p_priv
->var_lock
);
817 p_var
= &p_priv
->p_vars
[i_var
];
818 p_var
->b_incallback
= false;
821 /* Free data if needed */
822 p_var
->pf_free( &oldval
);
824 vlc_mutex_unlock( &p_priv
->var_lock
);
830 * Get a variable's value
832 * \param p_this The object that holds the variable
833 * \param psz_name The name of the variable
834 * \param p_val Pointer to a vlc_value_t that will hold the variable's value
835 * after the function is finished
837 int __var_Get( vlc_object_t
*p_this
, const char *psz_name
, vlc_value_t
*p_val
)
841 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
843 vlc_refcheck( p_this
);
844 vlc_mutex_lock( &p_priv
->var_lock
);
846 i_var
= Lookup( p_priv
->p_vars
, p_priv
->i_vars
, psz_name
);
850 vlc_mutex_unlock( &p_priv
->var_lock
);
854 p_var
= &p_priv
->p_vars
[i_var
];
856 /* Really get the variable */
859 /* Duplicate value if needed */
860 p_var
->pf_dup( p_val
);
862 vlc_mutex_unlock( &p_priv
->var_lock
);
869 * Finds a process-wide mutex, creates it if needed, and locks it.
870 * Unlock with vlc_mutex_unlock().
872 vlc_mutex_t
*var_AcquireMutex( const char *name
)
874 libvlc_global_data_t
*p_global
= vlc_global();
877 if( var_Create( p_global
, name
, VLC_VAR_MUTEX
) )
880 var_Get( p_global
, name
, &val
);
881 vlc_mutex_lock( val
.p_address
);
882 return val
.p_address
;
887 * Register a callback in a variable
889 * We store a function pointer that will be called upon variable
892 * \param p_this The object that holds the variable
893 * \param psz_name The name of the variable
894 * \param pf_callback The function pointer
895 * \param p_data A generic pointer that will be passed as the last
896 * argument to the callback function.
898 * \warning The callback function is run in the thread that calls var_Set on
899 * the variable. Use proper locking. This thread may not have much
900 * time to spare, so keep callback functions short.
902 int __var_AddCallback( vlc_object_t
*p_this
, const char *psz_name
,
903 vlc_callback_t pf_callback
, void *p_data
)
907 callback_entry_t entry
;
908 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
910 vlc_refcheck( p_this
);
911 entry
.pf_callback
= pf_callback
;
912 entry
.p_data
= p_data
;
914 vlc_mutex_lock( &p_priv
->var_lock
);
916 i_var
= GetUnused( p_this
, psz_name
);
919 vlc_mutex_unlock( &p_priv
->var_lock
);
923 p_var
= &p_priv
->p_vars
[i_var
];
925 INSERT_ELEM( p_var
->p_entries
,
930 vlc_mutex_unlock( &p_priv
->var_lock
);
936 * Remove a callback from a variable
938 * pf_callback and p_data have to be given again, because different objects
939 * might have registered the same callback function.
941 int __var_DelCallback( vlc_object_t
*p_this
, const char *psz_name
,
942 vlc_callback_t pf_callback
, void *p_data
)
946 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
948 vlc_refcheck( p_this
);
949 vlc_mutex_lock( &p_priv
->var_lock
);
951 i_var
= GetUnused( p_this
, psz_name
);
954 vlc_mutex_unlock( &p_priv
->var_lock
);
958 p_var
= &p_priv
->p_vars
[i_var
];
960 for( i_entry
= p_var
->i_entries
; i_entry
-- ; )
962 if( p_var
->p_entries
[i_entry
].pf_callback
== pf_callback
963 && p_var
->p_entries
[i_entry
].p_data
== p_data
)
971 vlc_mutex_unlock( &p_priv
->var_lock
);
975 REMOVE_ELEM( p_var
->p_entries
, p_var
->i_entries
, i_entry
);
977 vlc_mutex_unlock( &p_priv
->var_lock
);
983 * Trigger callback on a variable
985 * \param p_this The object that hold the variable
986 * \param psz_name The name of the variable
988 int __var_TriggerCallback( vlc_object_t
*p_this
, const char *psz_name
)
993 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
995 vlc_mutex_lock( &p_priv
->var_lock
);
997 i_var
= GetUnused( p_this
, psz_name
);
1000 vlc_mutex_unlock( &p_priv
->var_lock
);
1004 p_var
= &p_priv
->p_vars
[i_var
];
1006 /* Backup needed stuff */
1007 oldval
= p_var
->val
;
1009 /* Deal with callbacks. Tell we're in a callback, release the lock,
1010 * call stored functions, retake the lock. */
1011 if( p_var
->i_entries
)
1014 int i_entries
= p_var
->i_entries
;
1015 callback_entry_t
*p_entries
= p_var
->p_entries
;
1017 p_var
->b_incallback
= true;
1018 vlc_mutex_unlock( &p_priv
->var_lock
);
1020 /* The real calls */
1021 for( ; i_entries
-- ; )
1023 p_entries
[i_entries
].pf_callback( p_this
, psz_name
, oldval
, oldval
,
1024 p_entries
[i_entries
].p_data
);
1027 vlc_mutex_lock( &p_priv
->var_lock
);
1029 i_var
= Lookup( p_priv
->p_vars
, p_priv
->i_vars
, psz_name
);
1032 msg_Err( p_this
, "variable %s has disappeared", psz_name
);
1033 vlc_mutex_unlock( &p_priv
->var_lock
);
1037 p_var
= &p_priv
->p_vars
[i_var
];
1038 p_var
->b_incallback
= false;
1041 vlc_mutex_unlock( &p_priv
->var_lock
);
1045 /** Parse a stringified option
1046 * This function parse a string option and create the associated object
1048 * The option must be of the form "[no[-]]foo[=bar]" where foo is the
1049 * option name and bar is the value of the option.
1050 * \param p_obj the object in which the variable must be created
1051 * \param psz_option the option to parse
1052 * \param trusted whether the option is set by a trusted input or not
1055 void var_OptionParse( vlc_object_t
*p_obj
, const char *psz_option
,
1058 char *psz_name
, *psz_value
;
1060 bool b_isno
= false;
1063 val
.psz_string
= NULL
;
1065 /* It's too much of a hassle to remove the ':' when we parse
1066 * the cmd line :) */
1067 if( psz_option
[0] == ':' )
1070 if( !psz_option
[0] )
1073 psz_name
= strdup( psz_option
);
1074 if( psz_name
== NULL
)
1077 psz_value
= strchr( psz_name
, '=' );
1078 if( psz_value
!= NULL
)
1079 *psz_value
++ = '\0';
1081 /* FIXME: :programs should be handled generically */
1082 if( !strcmp( psz_name
, "programs" ) )
1083 i_type
= VLC_VAR_LIST
;
1085 i_type
= config_GetType( p_obj
, psz_name
);
1087 if( !i_type
&& !psz_value
)
1089 /* check for "no-foo" or "nofoo" */
1090 if( !strncmp( psz_name
, "no-", 3 ) )
1092 memmove( psz_name
, psz_name
+ 3, strlen(psz_name
) + 1 - 3 );
1094 else if( !strncmp( psz_name
, "no", 2 ) )
1096 memmove( psz_name
, psz_name
+ 2, strlen(psz_name
) + 1 - 2 );
1098 else goto cleanup
; /* Option doesn't exist */
1101 i_type
= config_GetType( p_obj
, psz_name
);
1103 if( !i_type
) goto cleanup
; /* Option doesn't exist */
1105 if( ( i_type
!= VLC_VAR_BOOL
) &&
1106 ( !psz_value
|| !*psz_value
) ) goto cleanup
; /* Invalid value */
1108 /* check if option is unsafe */
1111 module_config_t
*p_config
= config_FindConfig( p_obj
, psz_name
);
1112 if( !p_config
->b_safe
)
1114 msg_Err( p_obj
, "unsafe option \"%s\" has been ignored for "
1115 "security reasons", psz_name
);
1120 /* Create the variable in the input object.
1121 * Children of the input object will be able to retreive this value
1122 * thanks to the inheritance property of the object variables. */
1123 var_Create( p_obj
, psz_name
, i_type
);
1128 val
.b_bool
= !b_isno
;
1131 case VLC_VAR_INTEGER
:
1132 val
.i_int
= strtol( psz_value
, NULL
, 0 );
1136 val
.f_float
= atof( psz_value
);
1139 case VLC_VAR_STRING
:
1140 case VLC_VAR_MODULE
:
1142 case VLC_VAR_DIRECTORY
:
1143 val
.psz_string
= psz_value
;
1148 char *psz_orig
, *psz_var
;
1149 vlc_list_t
*p_list
= malloc(sizeof(vlc_list_t
));
1150 val
.p_list
= p_list
;
1151 p_list
->i_count
= 0;
1153 psz_var
= psz_orig
= strdup(psz_value
);
1154 while( psz_var
&& *psz_var
)
1156 char *psz_item
= psz_var
;
1158 while( *psz_var
&& *psz_var
!= ',' ) psz_var
++;
1159 if( *psz_var
== ',' )
1164 val2
.i_int
= strtol( psz_item
, NULL
, 0 );
1165 INSERT_ELEM( p_list
->p_values
, p_list
->i_count
,
1166 p_list
->i_count
, val2
);
1167 /* p_list->i_count is incremented twice by INSERT_ELEM */
1169 INSERT_ELEM( p_list
->pi_types
, p_list
->i_count
,
1170 p_list
->i_count
, VLC_VAR_INTEGER
);
1180 var_Set( p_obj
, psz_name
, val
);
1187 /* Following functions are local */
1189 /*****************************************************************************
1190 * GetUnused: find an unused variable from its name
1191 *****************************************************************************
1192 * We do i_tries tries before giving up, just in case the variable is being
1193 * modified and called from a callback.
1194 *****************************************************************************/
1195 static int GetUnused( vlc_object_t
*p_this
, const char *psz_name
)
1197 int i_var
, i_tries
= 0;
1198 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
1202 i_var
= Lookup( p_priv
->p_vars
, p_priv
->i_vars
, psz_name
);
1208 if( ! p_priv
->p_vars
[i_var
].b_incallback
)
1213 if( i_tries
++ > 100 )
1215 msg_Err( p_this
, "caught in a callback deadlock?" );
1216 return VLC_ETIMEOUT
;
1219 vlc_mutex_unlock( &p_priv
->var_lock
);
1220 msleep( THREAD_SLEEP
);
1221 vlc_mutex_lock( &p_priv
->var_lock
);
1225 /*****************************************************************************
1226 * HashString: our cool hash function
1227 *****************************************************************************
1228 * This function is not intended to be crypto-secure, we only want it to be
1229 * fast and not suck too much. This one is pretty fast and did 0 collisions
1230 * in wenglish's dictionary.
1231 *****************************************************************************/
1232 static uint32_t HashString( const char *psz_string
)
1234 uint32_t i_hash
= 0;
1236 while( *psz_string
)
1238 i_hash
+= *psz_string
++;
1239 i_hash
+= i_hash
<< 10;
1240 i_hash
^= i_hash
>> 8;
1246 /*****************************************************************************
1247 * Insert: find an empty slot to insert a new variable
1248 *****************************************************************************
1249 * We use a recursive inner function indexed on the hash. This function does
1250 * nothing in the rare cases where a collision may occur, see Lookup()
1251 * to see how we handle them.
1252 * XXX: does this really need to be written recursively?
1253 *****************************************************************************/
1254 static int Insert( variable_t
*p_vars
, int i_count
, const char *psz_name
)
1261 return InsertInner( p_vars
, i_count
, HashString( psz_name
) );
1264 static int InsertInner( variable_t
*p_vars
, int i_count
, uint32_t i_hash
)
1268 if( i_hash
<= p_vars
[0].i_hash
)
1273 if( i_hash
>= p_vars
[i_count
- 1].i_hash
)
1278 i_middle
= i_count
/ 2;
1280 /* We know that 0 < i_middle */
1281 if( i_hash
< p_vars
[i_middle
].i_hash
)
1283 return InsertInner( p_vars
, i_middle
, i_hash
);
1286 /* We know that i_middle + 1 < i_count */
1287 if( i_hash
> p_vars
[i_middle
+ 1].i_hash
)
1289 return i_middle
+ 1 + InsertInner( p_vars
+ i_middle
+ 1,
1290 i_count
- i_middle
- 1,
1294 return i_middle
+ 1;
1297 /*****************************************************************************
1298 * Lookup: find an existing variable given its name
1299 *****************************************************************************
1300 * We use a recursive inner function indexed on the hash. Care is taken of
1301 * possible hash collisions.
1302 * XXX: does this really need to be written recursively?
1303 *****************************************************************************/
1304 static int Lookup( variable_t
*p_vars
, int i_count
, const char *psz_name
)
1314 i_hash
= HashString( psz_name
);
1316 i_pos
= LookupInner( p_vars
, i_count
, i_hash
);
1318 /* Hash not found */
1319 if( i_hash
!= p_vars
[i_pos
].i_hash
)
1324 /* Hash found, entry found */
1325 if( !strcmp( psz_name
, p_vars
[i_pos
].psz_name
) )
1330 /* Hash collision! This should be very rare, but we cannot guarantee
1331 * it will never happen. Just do an exhaustive search amongst all
1332 * entries with the same hash. */
1333 for( i
= i_pos
- 1 ; i
> 0 && i_hash
== p_vars
[i
].i_hash
; i
-- )
1335 if( !strcmp( psz_name
, p_vars
[i
].psz_name
) )
1341 for( i
= i_pos
+ 1 ; i
< i_count
&& i_hash
== p_vars
[i
].i_hash
; i
++ )
1343 if( !strcmp( psz_name
, p_vars
[i
].psz_name
) )
1349 /* Hash found, but entry not found */
1353 static int LookupInner( variable_t
*p_vars
, int i_count
, uint32_t i_hash
)
1357 if( i_hash
<= p_vars
[0].i_hash
)
1362 if( i_hash
>= p_vars
[i_count
-1].i_hash
)
1367 i_middle
= i_count
/ 2;
1369 /* We know that 0 < i_middle */
1370 if( i_hash
< p_vars
[i_middle
].i_hash
)
1372 return LookupInner( p_vars
, i_middle
, i_hash
);
1375 /* We know that i_middle + 1 < i_count */
1376 if( i_hash
> p_vars
[i_middle
].i_hash
)
1378 return i_middle
+ LookupInner( p_vars
+ i_middle
,
1386 /*****************************************************************************
1387 * CheckValue: check that a value is valid wrt. a variable
1388 *****************************************************************************
1389 * This function checks p_val's value against p_var's limitations such as
1390 * minimal and maximal value, step, in-list position, and modifies p_val if
1392 ****************************************************************************/
1393 static void CheckValue ( variable_t
*p_var
, vlc_value_t
*p_val
)
1395 /* Check that our variable is in the list */
1396 if( p_var
->i_type
& VLC_VAR_HASCHOICE
&& p_var
->choices
.i_count
)
1400 /* FIXME: the list is sorted, dude. Use something cleverer. */
1401 for( i
= p_var
->choices
.i_count
; i
-- ; )
1403 if( p_var
->pf_cmp( *p_val
, p_var
->choices
.p_values
[i
] ) == 0 )
1409 /* If not found, change it to anything vaguely valid */
1412 /* Free the old variable, get the new one, dup it */
1413 p_var
->pf_free( p_val
);
1414 *p_val
= p_var
->choices
.p_values
[p_var
->i_default
>= 0
1415 ? p_var
->i_default
: 0 ];
1416 p_var
->pf_dup( p_val
);
1420 /* Check that our variable is within the bounds */
1421 switch( p_var
->i_type
& VLC_VAR_TYPE
)
1423 case VLC_VAR_INTEGER
:
1424 if( p_var
->i_type
& VLC_VAR_HASSTEP
&& p_var
->step
.i_int
1425 && (p_val
->i_int
% p_var
->step
.i_int
) )
1427 p_val
->i_int
= (p_val
->i_int
+ (p_var
->step
.i_int
/ 2))
1428 / p_var
->step
.i_int
* p_var
->step
.i_int
;
1430 if( p_var
->i_type
& VLC_VAR_HASMIN
1431 && p_val
->i_int
< p_var
->min
.i_int
)
1433 p_val
->i_int
= p_var
->min
.i_int
;
1435 if( p_var
->i_type
& VLC_VAR_HASMAX
1436 && p_val
->i_int
> p_var
->max
.i_int
)
1438 p_val
->i_int
= p_var
->max
.i_int
;
1442 if( p_var
->i_type
& VLC_VAR_HASSTEP
&& p_var
->step
.f_float
)
1444 float f_round
= p_var
->step
.f_float
* (float)(int)( 0.5 +
1445 p_val
->f_float
/ p_var
->step
.f_float
);
1446 if( p_val
->f_float
!= f_round
)
1448 p_val
->f_float
= f_round
;
1451 if( p_var
->i_type
& VLC_VAR_HASMIN
1452 && p_val
->f_float
< p_var
->min
.f_float
)
1454 p_val
->f_float
= p_var
->min
.f_float
;
1456 if( p_var
->i_type
& VLC_VAR_HASMAX
1457 && p_val
->f_float
> p_var
->max
.f_float
)
1459 p_val
->f_float
= p_var
->max
.f_float
;
1468 /*****************************************************************************
1469 * InheritValue: try to inherit the value of this variable from the same one
1470 * in our closest parent.
1471 *****************************************************************************/
1472 static int InheritValue( vlc_object_t
*p_this
, const char *psz_name
,
1473 vlc_value_t
*p_val
, int i_type
)
1478 /* No need to take the structure lock,
1479 * we are only looking for our parents */
1481 if( !p_this
->p_parent
)
1483 switch( i_type
& VLC_VAR_TYPE
)
1486 case VLC_VAR_DIRECTORY
:
1487 case VLC_VAR_STRING
:
1488 case VLC_VAR_MODULE
:
1489 p_val
->psz_string
= config_GetPsz( p_this
, psz_name
);
1490 if( !p_val
->psz_string
) p_val
->psz_string
= strdup("");
1493 p_val
->f_float
= config_GetFloat( p_this
, psz_name
);
1495 case VLC_VAR_INTEGER
:
1496 case VLC_VAR_HOTKEY
:
1497 p_val
->i_int
= config_GetInt( p_this
, psz_name
);
1500 p_val
->b_bool
= config_GetInt( p_this
, psz_name
);
1504 char *psz_orig
, *psz_var
;
1505 vlc_list_t
*p_list
= malloc(sizeof(vlc_list_t
));
1506 p_val
->p_list
= p_list
;
1507 p_list
->i_count
= 0;
1509 psz_var
= psz_orig
= config_GetPsz( p_this
, psz_name
);
1510 while( psz_var
&& *psz_var
)
1512 char *psz_item
= psz_var
;
1514 while( *psz_var
&& *psz_var
!= ',' ) psz_var
++;
1515 if( *psz_var
== ',' )
1520 val
.i_int
= strtol( psz_item
, NULL
, 0 );
1521 INSERT_ELEM( p_list
->p_values
, p_list
->i_count
,
1522 p_list
->i_count
, val
);
1523 /* p_list->i_count is incremented twice by INSERT_ELEM */
1525 INSERT_ELEM( p_list
->pi_types
, p_list
->i_count
,
1526 p_list
->i_count
, VLC_VAR_INTEGER
);
1539 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
->p_parent
);
1541 /* Look for the variable */
1542 vlc_mutex_lock( &p_priv
->var_lock
);
1544 i_var
= Lookup( p_priv
->p_vars
, p_priv
->i_vars
, psz_name
);
1549 p_var
= &p_priv
->p_vars
[i_var
];
1551 /* Really get the variable */
1552 *p_val
= p_var
->val
;
1554 /* Duplicate value if needed */
1555 p_var
->pf_dup( p_val
);
1557 vlc_mutex_unlock( &p_priv
->var_lock
);
1561 vlc_mutex_unlock( &p_priv
->var_lock
);
1563 /* We're still not there */
1565 return InheritValue( p_this
->p_parent
, psz_name
, p_val
, i_type
);
1568 /**********************************************************************
1569 * Execute a var command on an object identified by its name
1570 **********************************************************************/
1571 int __var_Command( vlc_object_t
*p_this
, const char *psz_name
,
1572 const char *psz_cmd
, const char *psz_arg
, char **psz_msg
)
1574 vlc_object_t
*p_obj
= vlc_object_find_name( p_this
->p_libvlc
,
1575 psz_name
, FIND_CHILD
);
1581 *psz_msg
= strdup( "Unknown destination object." );
1585 vlc_refcheck( p_this
);
1586 i_type
= var_Type( p_obj
, psz_cmd
);
1587 if( !( i_type
&VLC_VAR_ISCOMMAND
) )
1589 vlc_object_release( p_obj
);
1591 *psz_msg
= strdup( "Variable doesn't exist or isn't a command." );
1592 return VLC_EGENERIC
;
1598 case VLC_VAR_INTEGER
:
1599 i_ret
= var_SetInteger( p_obj
, psz_cmd
, atoi( psz_arg
) );
1602 i_ret
= var_SetFloat( p_obj
, psz_cmd
, atof( psz_arg
) );
1604 case VLC_VAR_STRING
:
1605 i_ret
= var_SetString( p_obj
, psz_cmd
, psz_arg
);
1608 i_ret
= var_SetBool( p_obj
, psz_cmd
, atoi( psz_arg
) );
1611 i_ret
= VLC_EGENERIC
;
1615 vlc_object_release( p_obj
);
1619 *psz_msg
= (char*)malloc( 80 );
1620 sprintf( *psz_msg
, "%s on object %s returned %i (%s)",
1621 psz_cmd
, psz_name
, i_ret
, vlc_error( i_ret
) );