1 /*****************************************************************************
2 * variables.c: routines for object variables handling
3 *****************************************************************************
4 * Copyright (C) 2002-2009 VLC authors and VideoLAN
7 * Authors: Samuel Hocevar <sam@zoy.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
39 #include <vlc_common.h>
40 #include <vlc_arrays.h>
41 #include <vlc_charset.h>
43 #include "variables.h"
44 #include "config/configuration.h"
46 typedef struct callback_entry_t
50 vlc_callback_t pf_value_callback
;
51 vlc_list_callback_t pf_list_callback
;
57 typedef struct variable_ops_t
59 int (*pf_cmp
) ( vlc_value_t
, vlc_value_t
);
60 void (*pf_dup
) ( vlc_value_t
* );
61 void (*pf_free
) ( vlc_value_t
* );
64 typedef struct callback_table_t
67 callback_entry_t
* p_entries
;
71 * The structure describing a variable.
72 * \note vlc_value_t is the common union for variable values
76 char * psz_name
; /**< The variable unique name (must be first) */
78 /** The variable's exported value */
81 /** The variable display name, mainly for use by the interfaces */
84 const variable_ops_t
*ops
;
86 int i_type
; /**< The type of the variable */
87 unsigned i_usage
; /**< Reference count */
89 /** If the variable has min/max/step values */
90 vlc_value_t min
, max
, step
;
92 /** List of choices */
94 /** List of friendly names for the choices */
95 vlc_list_t choices_text
;
97 /** Set to TRUE if the variable is in a callback */
100 /** Registered value callbacks */
101 callback_table_t value_callbacks
;
102 /** Registered list callbacks */
103 callback_table_t list_callbacks
;
106 static int CmpBool( vlc_value_t v
, vlc_value_t w
)
108 return v
.b_bool
? w
.b_bool
? 0 : 1 : w
.b_bool
? -1 : 0;
111 static int CmpInt( vlc_value_t v
, vlc_value_t w
)
113 return v
.i_int
== w
.i_int
? 0 : v
.i_int
> w
.i_int
? 1 : -1;
116 static int CmpString( vlc_value_t v
, vlc_value_t w
)
119 return !w
.psz_string
? 0 : -1;
121 return !w
.psz_string
? 1 : strcmp( v
.psz_string
, w
.psz_string
);
123 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; }
124 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; }
126 static void DupDummy( vlc_value_t
*p_val
) { (void)p_val
; /* unused */ }
127 static void DupString( vlc_value_t
*p_val
)
129 p_val
->psz_string
= strdup( p_val
->psz_string
? p_val
->psz_string
: "" );
132 static void FreeDummy( vlc_value_t
*p_val
) { (void)p_val
; /* unused */ }
133 static void FreeString( vlc_value_t
*p_val
) { free( p_val
->psz_string
); }
135 static const struct variable_ops_t
136 void_ops
= { NULL
, DupDummy
, FreeDummy
, },
137 addr_ops
= { CmpAddress
, DupDummy
, FreeDummy
, },
138 bool_ops
= { CmpBool
, DupDummy
, FreeDummy
, },
139 float_ops
= { CmpFloat
, DupDummy
, FreeDummy
, },
140 int_ops
= { CmpInt
, DupDummy
, FreeDummy
, },
141 string_ops
= { CmpString
, DupString
, FreeString
, },
142 coords_ops
= { NULL
, DupDummy
, FreeDummy
, };
144 static int varcmp( const void *a
, const void *b
)
146 const variable_t
*va
= a
, *vb
= b
;
148 /* psz_name must be first */
149 assert( va
== (const void *)&va
->psz_name
);
150 return strcmp( va
->psz_name
, vb
->psz_name
);
153 static variable_t
*Lookup( vlc_object_t
*obj
, const char *psz_name
)
155 vlc_object_internals_t
*priv
= vlc_internals( obj
);
158 vlc_mutex_lock(&priv
->var_lock
);
159 pp_var
= tfind( &psz_name
, &priv
->var_root
, varcmp
);
160 return (pp_var
!= NULL
) ? *pp_var
: NULL
;
163 static void Destroy( variable_t
*p_var
)
165 p_var
->ops
->pf_free( &p_var
->val
);
166 if( p_var
->choices
.i_count
)
168 for( int i
= 0 ; i
< p_var
->choices
.i_count
; i
++ )
170 p_var
->ops
->pf_free( &p_var
->choices
.p_values
[i
] );
171 free( p_var
->choices_text
.p_values
[i
].psz_string
);
173 free( p_var
->choices
.p_values
);
174 free( p_var
->choices_text
.p_values
);
177 free( p_var
->psz_name
);
178 free( p_var
->psz_text
);
179 free( p_var
->value_callbacks
.p_entries
);
184 * Adjusts a value to fit the constraints for a certain variable:
185 * - If the value is lower than the minimum, use the minimum.
186 * - If the value is higher than the maximum, use the maximum.
187 * - If the variable has steps, round the value to the nearest step.
189 static void CheckValue(variable_t
*var
, vlc_value_t
*val
)
191 /* Check that our variable is within the bounds */
192 switch (var
->i_type
& VLC_VAR_TYPE
)
194 case VLC_VAR_INTEGER
:
195 if (val
->i_int
< var
->min
.i_int
)
196 val
->i_int
= var
->min
.i_int
;
197 if (val
->i_int
> var
->max
.i_int
)
198 val
->i_int
= var
->max
.i_int
;
199 if (var
->step
.i_int
!= 0 && (val
->i_int
% var
->step
.i_int
))
202 val
->i_int
= (val
->i_int
+ (var
->step
.i_int
/ 2))
203 / var
->step
.i_int
* var
->step
.i_int
;
205 val
->i_int
= (val
->i_int
- (var
->step
.i_int
/ 2))
206 / var
->step
.i_int
* var
->step
.i_int
;
211 if (isless(val
->f_float
, var
->min
.f_float
))
212 val
->f_float
= var
->min
.f_float
;
213 if (isgreater(val
->f_float
, var
->max
.f_float
))
214 val
->f_float
= var
->max
.f_float
;
215 if (var
->step
.f_float
!= 0.f
)
216 val
->f_float
= var
->step
.f_float
217 * roundf(val
->f_float
/ var
->step
.f_float
);
223 * Waits until the variable is inactive (i.e. not executing a callback)
225 static void WaitUnused(vlc_object_t
*obj
, variable_t
*var
)
227 vlc_object_internals_t
*priv
= vlc_internals(obj
);
229 mutex_cleanup_push(&priv
->var_lock
);
230 while (var
->b_incallback
)
231 vlc_cond_wait(&priv
->var_wait
, &priv
->var_lock
);
235 static void TriggerCallback(vlc_object_t
*obj
, variable_t
*var
,
236 const char *name
, vlc_value_t prev
)
240 size_t count
= var
->value_callbacks
.i_entries
;
244 callback_entry_t
*entries
= var
->value_callbacks
.p_entries
;
245 vlc_object_internals_t
*priv
= vlc_internals(obj
);
247 assert(!var
->b_incallback
);
248 var
->b_incallback
= true;
249 vlc_mutex_unlock(&priv
->var_lock
);
251 for (size_t i
= 0; i
< count
; i
++)
252 entries
[i
].pf_value_callback(obj
, name
, prev
, var
->val
,
255 vlc_mutex_lock(&priv
->var_lock
);
256 var
->b_incallback
= false;
257 vlc_cond_broadcast(&priv
->var_wait
);
260 static void TriggerListCallback(vlc_object_t
*obj
, variable_t
*var
,
261 const char *name
, int action
, vlc_value_t
*val
)
265 size_t count
= var
->list_callbacks
.i_entries
;
269 callback_entry_t
*entries
= var
->list_callbacks
.p_entries
;
270 vlc_object_internals_t
*priv
= vlc_internals(obj
);
272 assert(!var
->b_incallback
);
273 var
->b_incallback
= true;
274 vlc_mutex_unlock(&priv
->var_lock
);
276 for (size_t i
= 0; i
< count
; i
++)
277 entries
[i
].pf_list_callback(obj
, name
, action
, val
,
280 vlc_mutex_lock(&priv
->var_lock
);
281 var
->b_incallback
= false;
282 vlc_cond_broadcast(&priv
->var_wait
);
285 int (var_Create
)( vlc_object_t
*p_this
, const char *psz_name
, int i_type
)
289 variable_t
*p_var
= calloc( 1, sizeof( *p_var
) );
293 p_var
->psz_name
= strdup( psz_name
);
294 p_var
->psz_text
= NULL
;
296 p_var
->i_type
= i_type
& ~VLC_VAR_DOINHERIT
;
300 p_var
->choices
.i_count
= 0;
301 p_var
->choices
.p_values
= NULL
;
302 p_var
->choices_text
.i_count
= 0;
303 p_var
->choices_text
.p_values
= NULL
;
305 p_var
->b_incallback
= false;
306 p_var
->value_callbacks
= (callback_table_t
){ 0, NULL
};
308 /* Always initialize the variable, even if it is a list variable; this
309 * will lead to errors if the variable is not initialized, but it will
310 * not cause crashes in the variable handling. */
311 switch( i_type
& VLC_VAR_CLASS
)
314 p_var
->ops
= &bool_ops
;
315 p_var
->val
.b_bool
= false;
317 case VLC_VAR_INTEGER
:
318 p_var
->ops
= &int_ops
;
319 p_var
->val
.i_int
= 0;
320 p_var
->min
.i_int
= INT64_MIN
;
321 p_var
->max
.i_int
= INT64_MAX
;
324 p_var
->ops
= &string_ops
;
325 p_var
->val
.psz_string
= NULL
;
328 p_var
->ops
= &float_ops
;
329 p_var
->val
.f_float
= 0.f
;
330 p_var
->min
.f_float
= -FLT_MAX
;
331 p_var
->max
.f_float
= FLT_MAX
;
334 p_var
->ops
= &coords_ops
;
335 p_var
->val
.coords
.x
= p_var
->val
.coords
.y
= 0;
337 case VLC_VAR_ADDRESS
:
338 p_var
->ops
= &addr_ops
;
339 p_var
->val
.p_address
= NULL
;
342 p_var
->ops
= &void_ops
;
345 vlc_assert_unreachable ();
348 if (i_type
& VLC_VAR_DOINHERIT
)
349 var_Inherit(p_this
, psz_name
, i_type
, &p_var
->val
);
351 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
352 variable_t
**pp_var
, *p_oldvar
;
353 int ret
= VLC_SUCCESS
;
355 vlc_mutex_lock( &p_priv
->var_lock
);
357 pp_var
= tsearch( p_var
, &p_priv
->var_root
, varcmp
);
358 if( unlikely(pp_var
== NULL
) )
360 else if( (p_oldvar
= *pp_var
) == p_var
) /* Variable create */
361 p_var
= NULL
; /* Variable created */
362 else /* Variable already exists */
364 assert (((i_type
^ p_oldvar
->i_type
) & VLC_VAR_CLASS
) == 0);
366 p_oldvar
->i_type
|= i_type
& VLC_VAR_ISCOMMAND
;
368 vlc_mutex_unlock( &p_priv
->var_lock
);
370 /* If we did not need to create a new variable, free everything... */
376 void (var_Destroy
)(vlc_object_t
*p_this
, const char *psz_name
)
382 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
384 p_var
= Lookup( p_this
, psz_name
);
386 msg_Dbg( p_this
, "attempt to destroy nonexistent variable \"%s\"",
388 else if( --p_var
->i_usage
== 0 )
390 assert(!p_var
->b_incallback
);
391 tdelete( p_var
, &p_priv
->var_root
, varcmp
);
395 assert(p_var
->i_usage
!= -1u);
398 vlc_mutex_unlock( &p_priv
->var_lock
);
404 static void CleanupVar( void *var
)
409 void var_DestroyAll( vlc_object_t
*obj
)
411 vlc_object_internals_t
*priv
= vlc_internals( obj
);
413 tdestroy( priv
->var_root
, CleanupVar
);
414 priv
->var_root
= NULL
;
417 int (var_Change
)(vlc_object_t
*p_this
, const char *psz_name
,
418 int i_action
, vlc_value_t
*p_val
, vlc_value_t
*p_val2
)
420 int ret
= VLC_SUCCESS
;
427 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
429 p_var
= Lookup( p_this
, psz_name
);
432 vlc_mutex_unlock( &p_priv
->var_lock
);
444 case VLC_VAR_SETMINMAX
:
445 assert(p_var
->ops
->pf_free
== FreeDummy
);
447 p_var
->max
= *p_val2
;
449 case VLC_VAR_SETSTEP
:
450 assert(p_var
->ops
->pf_free
== FreeDummy
);
451 p_var
->step
= *p_val
;
452 CheckValue( p_var
, &p_var
->val
);
454 case VLC_VAR_GETSTEP
:
455 switch (p_var
->i_type
& VLC_VAR_TYPE
)
457 case VLC_VAR_INTEGER
:
458 if (p_var
->step
.i_int
== 0)
462 if (p_var
->step
.f_float
== 0.f
)
468 if (ret
== VLC_SUCCESS
)
469 *p_val
= p_var
->step
;
471 case VLC_VAR_ADDCHOICE
:
473 int i
= p_var
->choices
.i_count
;
475 TAB_APPEND(p_var
->choices
.i_count
,
476 p_var
->choices
.p_values
, *p_val
);
477 assert(i
== p_var
->choices_text
.i_count
);
478 TAB_APPEND(p_var
->choices_text
.i_count
,
479 p_var
->choices_text
.p_values
, *p_val
);
480 p_var
->ops
->pf_dup( &p_var
->choices
.p_values
[i
] );
481 p_var
->choices_text
.p_values
[i
].psz_string
=
482 ( p_val2
&& p_val2
->psz_string
) ?
483 strdup( p_val2
->psz_string
) : NULL
;
485 TriggerListCallback(p_this
, p_var
, psz_name
, VLC_VAR_ADDCHOICE
, p_val
);
488 case VLC_VAR_DELCHOICE
:
492 for( i
= 0 ; i
< p_var
->choices
.i_count
; i
++ )
493 if( p_var
->ops
->pf_cmp( p_var
->choices
.p_values
[i
], *p_val
) == 0 )
496 if( i
== p_var
->choices
.i_count
)
499 vlc_mutex_unlock( &p_priv
->var_lock
);
503 p_var
->ops
->pf_free( &p_var
->choices
.p_values
[i
] );
504 free( p_var
->choices_text
.p_values
[i
].psz_string
);
505 TAB_ERASE(p_var
->choices
.i_count
, p_var
->choices
.p_values
, i
);
506 TAB_ERASE(p_var
->choices_text
.i_count
,
507 p_var
->choices_text
.p_values
, i
);
509 TriggerListCallback(p_this
, p_var
, psz_name
, VLC_VAR_DELCHOICE
, p_val
);
512 case VLC_VAR_CHOICESCOUNT
:
513 p_val
->i_int
= p_var
->choices
.i_count
;
515 case VLC_VAR_CLEARCHOICES
:
516 for( int i
= 0 ; i
< p_var
->choices
.i_count
; i
++ )
517 p_var
->ops
->pf_free( &p_var
->choices
.p_values
[i
] );
518 for( int i
= 0 ; i
< p_var
->choices_text
.i_count
; i
++ )
519 free( p_var
->choices_text
.p_values
[i
].psz_string
);
521 if( p_var
->choices
.i_count
) free( p_var
->choices
.p_values
);
522 if( p_var
->choices_text
.i_count
) free( p_var
->choices_text
.p_values
);
524 p_var
->choices
.i_count
= 0;
525 p_var
->choices
.p_values
= NULL
;
526 p_var
->choices_text
.i_count
= 0;
527 p_var
->choices_text
.p_values
= NULL
;
528 TriggerListCallback(p_this
, p_var
, psz_name
, VLC_VAR_CLEARCHOICES
, NULL
);
530 case VLC_VAR_SETVALUE
:
531 /* Duplicate data if needed */
533 p_var
->ops
->pf_dup( &newval
);
534 /* Backup needed stuff */
536 /* Check boundaries and list */
537 CheckValue( p_var
, &newval
);
538 /* Set the variable */
540 /* Free data if needed */
541 p_var
->ops
->pf_free( &oldval
);
543 case VLC_VAR_GETCHOICES
:
544 p_val
->p_list
= xmalloc( sizeof(vlc_list_t
) );
545 p_val
->p_list
->p_values
=
546 xmalloc( p_var
->choices
.i_count
* sizeof(vlc_value_t
) );
547 p_val
->p_list
->i_type
= p_var
->i_type
;
548 p_val
->p_list
->i_count
= p_var
->choices
.i_count
;
551 p_val2
->p_list
= xmalloc( sizeof(vlc_list_t
) );
552 p_val2
->p_list
->p_values
=
553 xmalloc( p_var
->choices
.i_count
* sizeof(vlc_value_t
) );
554 p_val2
->p_list
->i_type
= VLC_VAR_STRING
;
555 p_val2
->p_list
->i_count
= p_var
->choices
.i_count
;
557 for( int i
= 0 ; i
< p_var
->choices
.i_count
; i
++ )
559 p_val
->p_list
->p_values
[i
] = p_var
->choices
.p_values
[i
];
560 p_var
->ops
->pf_dup( &p_val
->p_list
->p_values
[i
] );
563 p_val2
->p_list
->p_values
[i
].psz_string
=
564 p_var
->choices_text
.p_values
[i
].psz_string
?
565 strdup(p_var
->choices_text
.p_values
[i
].psz_string
) : NULL
;
569 case VLC_VAR_SETTEXT
:
570 free( p_var
->psz_text
);
571 if( p_val
&& p_val
->psz_string
)
572 p_var
->psz_text
= strdup( p_val
->psz_string
);
574 p_var
->psz_text
= NULL
;
576 case VLC_VAR_GETTEXT
:
577 p_val
->psz_string
= p_var
->psz_text
? strdup( p_var
->psz_text
)
584 vlc_mutex_unlock( &p_priv
->var_lock
);
589 int (var_GetAndSet
)(vlc_object_t
*p_this
, const char *psz_name
, int i_action
,
598 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
600 p_var
= Lookup( p_this
, psz_name
);
603 vlc_mutex_unlock( &p_priv
->var_lock
);
607 WaitUnused( p_this
, p_var
);
609 /* Duplicated data if needed */
610 //p_var->ops->pf_dup( &val );
612 /* Backup needed stuff */
615 /* depending of the action requiered */
618 case VLC_VAR_BOOL_TOGGLE
:
619 assert( ( p_var
->i_type
& VLC_VAR_BOOL
) == VLC_VAR_BOOL
);
620 p_var
->val
.b_bool
= !p_var
->val
.b_bool
;
622 case VLC_VAR_INTEGER_ADD
:
623 assert( ( p_var
->i_type
& VLC_VAR_INTEGER
) == VLC_VAR_INTEGER
);
624 p_var
->val
.i_int
+= p_val
->i_int
;
626 case VLC_VAR_INTEGER_OR
:
627 assert( ( p_var
->i_type
& VLC_VAR_INTEGER
) == VLC_VAR_INTEGER
);
628 p_var
->val
.i_int
|= p_val
->i_int
;
630 case VLC_VAR_INTEGER_NAND
:
631 assert( ( p_var
->i_type
& VLC_VAR_INTEGER
) == VLC_VAR_INTEGER
);
632 p_var
->val
.i_int
&= ~p_val
->i_int
;
635 vlc_mutex_unlock( &p_priv
->var_lock
);
639 /* Check boundaries */
640 CheckValue( p_var
, &p_var
->val
);
643 /* Deal with callbacks.*/
644 TriggerCallback( p_this
, p_var
, psz_name
, oldval
);
646 vlc_mutex_unlock( &p_priv
->var_lock
);
650 int (var_Type
)(vlc_object_t
*p_this
, const char *psz_name
)
657 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
659 p_var
= Lookup( p_this
, psz_name
);
662 i_type
= p_var
->i_type
;
663 if( p_var
->choices
.i_count
> 0 )
664 i_type
|= VLC_VAR_HASCHOICE
;
666 vlc_mutex_unlock( &p_priv
->var_lock
);
671 int (var_SetChecked
)(vlc_object_t
*p_this
, const char *psz_name
,
672 int expected_type
, vlc_value_t val
)
679 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
681 p_var
= Lookup( p_this
, psz_name
);
684 vlc_mutex_unlock( &p_priv
->var_lock
);
688 assert( expected_type
== 0 ||
689 (p_var
->i_type
& VLC_VAR_CLASS
) == expected_type
);
690 assert ((p_var
->i_type
& VLC_VAR_CLASS
) != VLC_VAR_VOID
);
692 WaitUnused( p_this
, p_var
);
694 /* Duplicate data if needed */
695 p_var
->ops
->pf_dup( &val
);
697 /* Backup needed stuff */
700 /* Check boundaries and list */
701 CheckValue( p_var
, &val
);
703 /* Set the variable */
706 /* Deal with callbacks */
707 TriggerCallback( p_this
, p_var
, psz_name
, oldval
);
709 /* Free data if needed */
710 p_var
->ops
->pf_free( &oldval
);
712 vlc_mutex_unlock( &p_priv
->var_lock
);
716 int (var_Set
)(vlc_object_t
*p_this
, const char *psz_name
, vlc_value_t val
)
718 return var_SetChecked( p_this
, psz_name
, 0, val
);
721 int (var_GetChecked
)(vlc_object_t
*p_this
, const char *psz_name
,
722 int expected_type
, vlc_value_t
*p_val
)
726 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
728 int err
= VLC_SUCCESS
;
730 p_var
= Lookup( p_this
, psz_name
);
733 assert( expected_type
== 0 ||
734 (p_var
->i_type
& VLC_VAR_CLASS
) == expected_type
);
735 assert ((p_var
->i_type
& VLC_VAR_CLASS
) != VLC_VAR_VOID
);
737 /* Really get the variable */
740 /* Duplicate value if needed */
741 p_var
->ops
->pf_dup( p_val
);
746 vlc_mutex_unlock( &p_priv
->var_lock
);
750 int (var_Get
)(vlc_object_t
*p_this
, const char *psz_name
, vlc_value_t
*p_val
)
752 return var_GetChecked( p_this
, psz_name
, 0, p_val
);
759 } vlc_callback_type_t
;
761 static void AddCallback( vlc_object_t
*p_this
, const char *psz_name
,
762 callback_entry_t entry
, vlc_callback_type_t i_type
)
768 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
770 p_var
= Lookup( p_this
, psz_name
);
773 vlc_mutex_unlock( &p_priv
->var_lock
);
774 msg_Err( p_this
, "cannot add callback %p to nonexistent variable '%s'",
775 entry
.p_callback
, psz_name
);
779 WaitUnused( p_this
, p_var
);
781 callback_table_t
*p_table
;
782 if (i_type
== vlc_value_callback
)
783 p_table
= &p_var
->value_callbacks
;
785 p_table
= &p_var
->list_callbacks
;
786 TAB_APPEND(p_table
->i_entries
, p_table
->p_entries
, entry
);
788 vlc_mutex_unlock( &p_priv
->var_lock
);
791 void (var_AddCallback
)(vlc_object_t
*p_this
, const char *psz_name
,
792 vlc_callback_t pf_callback
, void *p_data
)
794 callback_entry_t entry
;
795 entry
.pf_value_callback
= pf_callback
;
796 entry
.p_data
= p_data
;
798 AddCallback(p_this
, psz_name
, entry
, vlc_value_callback
);
801 static void DelCallback( vlc_object_t
*p_this
, const char *psz_name
,
802 callback_entry_t entry
, vlc_callback_type_t i_type
)
807 bool b_found_similar
= false;
812 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
814 p_var
= Lookup( p_this
, psz_name
);
817 vlc_mutex_unlock( &p_priv
->var_lock
);
818 msg_Err( p_this
, "cannot delete callback %p from nonexistent "
819 "variable '%s'", entry
.p_callback
, psz_name
);
823 WaitUnused( p_this
, p_var
);
825 callback_table_t
*p_table
;
826 if (i_type
== vlc_value_callback
)
827 p_table
= &p_var
->value_callbacks
;
829 p_table
= &p_var
->list_callbacks
;
831 for( i_entry
= p_table
->i_entries
; i_entry
-- ; )
833 if( p_table
->p_entries
[i_entry
].p_callback
== entry
.p_callback
834 && p_table
->p_entries
[i_entry
].p_data
== entry
.p_data
)
839 else if( p_table
->p_entries
[i_entry
].p_callback
== entry
.p_callback
)
840 b_found_similar
= true;
847 if( b_found_similar
)
848 fprintf( stderr
, "Calling var_DelCallback for '%s' with the same "
849 "function but not the same data.", psz_name
);
850 vlc_assert_unreachable();
852 vlc_mutex_unlock( &p_priv
->var_lock
);
856 TAB_ERASE(p_table
->i_entries
, p_table
->p_entries
, i_entry
);
858 vlc_mutex_unlock( &p_priv
->var_lock
);
861 void (var_DelCallback
)(vlc_object_t
*p_this
, const char *psz_name
,
862 vlc_callback_t pf_callback
, void *p_data
)
864 callback_entry_t entry
;
865 entry
.pf_value_callback
= pf_callback
;
866 entry
.p_data
= p_data
;
868 DelCallback(p_this
, psz_name
, entry
, vlc_value_callback
);
871 void (var_TriggerCallback
)(vlc_object_t
*p_this
, const char *psz_name
)
873 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
874 variable_t
*p_var
= Lookup( p_this
, psz_name
);
877 WaitUnused( p_this
, p_var
);
879 /* Deal with callbacks. Tell we're in a callback, release the lock,
880 * call stored functions, retake the lock. */
881 TriggerCallback( p_this
, p_var
, psz_name
, p_var
->val
);
883 vlc_mutex_unlock( &p_priv
->var_lock
);
886 void (var_AddListCallback
)(vlc_object_t
*p_this
, const char *psz_name
,
887 vlc_list_callback_t pf_callback
, void *p_data
)
889 callback_entry_t entry
;
890 entry
.pf_list_callback
= pf_callback
;
891 entry
.p_data
= p_data
;
893 AddCallback(p_this
, psz_name
, entry
, vlc_list_callback
);
896 void (var_DelListCallback
)(vlc_object_t
*p_this
, const char *psz_name
,
897 vlc_list_callback_t pf_callback
, void *p_data
)
899 callback_entry_t entry
;
900 entry
.pf_list_callback
= pf_callback
;
901 entry
.p_data
= p_data
;
903 DelCallback(p_this
, psz_name
, entry
, vlc_list_callback
);
906 /** Parse a stringified option
907 * This function parse a string option and create the associated object
909 * The option must be of the form "[no[-]]foo[=bar]" where foo is the
910 * option name and bar is the value of the option.
911 * \param p_obj the object in which the variable must be created
912 * \param psz_option the option to parse
913 * \param trusted whether the option is set by a trusted input or not
916 void var_OptionParse( vlc_object_t
*p_obj
, const char *psz_option
,
919 char *psz_name
, *psz_value
;
924 val
.psz_string
= NULL
;
926 /* It's too much of a hassle to remove the ':' when we parse
928 if( psz_option
[0] == ':' )
934 psz_name
= strdup( psz_option
);
935 if( psz_name
== NULL
)
938 psz_value
= strchr( psz_name
, '=' );
939 if( psz_value
!= NULL
)
942 i_type
= config_GetType( psz_name
);
943 if( !i_type
&& !psz_value
)
945 /* check for "no-foo" or "nofoo" */
946 if( !strncmp( psz_name
, "no-", 3 ) )
948 memmove( psz_name
, psz_name
+ 3, strlen(psz_name
) + 1 - 3 );
950 else if( !strncmp( psz_name
, "no", 2 ) )
952 memmove( psz_name
, psz_name
+ 2, strlen(psz_name
) + 1 - 2 );
954 else goto cleanup
; /* Option doesn't exist */
957 i_type
= config_GetType( psz_name
);
959 if( !i_type
) goto cleanup
; /* Option doesn't exist */
961 if( ( i_type
!= VLC_VAR_BOOL
) &&
962 ( !psz_value
|| !*psz_value
) ) goto cleanup
; /* Invalid value */
964 /* check if option is unsafe */
965 if( !trusted
&& !config_IsSafe( psz_name
) )
967 msg_Err( p_obj
, "unsafe option \"%s\" has been ignored for "
968 "security reasons", psz_name
);
973 /* Create the variable in the input object.
974 * Children of the input object will be able to retrieve this value
975 * thanks to the inheritance property of the object variables. */
976 var_Create( p_obj
, psz_name
, i_type
);
981 val
.b_bool
= !b_isno
;
984 case VLC_VAR_INTEGER
:
985 val
.i_int
= strtoll( psz_value
, NULL
, 0 );
989 val
.f_float
= us_atof( psz_value
);
993 val
.psz_string
= psz_value
;
1000 var_Set( p_obj
, psz_name
, val
);
1006 int (var_LocationParse
)(vlc_object_t
*obj
, const char *mrl
, const char *pref
)
1008 int ret
= VLC_SUCCESS
;
1009 size_t preflen
= strlen (pref
) + 1;
1011 assert(mrl
!= NULL
);
1012 while (*mrl
!= '\0')
1014 mrl
+= strspn (mrl
, ":;"); /* skip leading colon(s) */
1016 size_t len
= strcspn (mrl
, ":;");
1017 char *buf
= malloc (preflen
+ len
);
1019 if (likely(buf
!= NULL
))
1021 /* NOTE: this does not support the "no-<varname>" bool syntax. */
1022 /* DO NOT use asprintf() here; it won't work! Think again. */
1023 snprintf (buf
, preflen
+ len
, "%s%s", pref
, mrl
);
1024 var_OptionParse (obj
, buf
, false);
1035 int var_Inherit( vlc_object_t
*p_this
, const char *psz_name
, int i_type
,
1036 vlc_value_t
*p_val
)
1038 i_type
&= VLC_VAR_CLASS
;
1039 for( vlc_object_t
*obj
= p_this
; obj
!= NULL
; obj
= obj
->obj
.parent
)
1041 if( var_GetChecked( obj
, psz_name
, i_type
, p_val
) == VLC_SUCCESS
)
1045 /* else take value from config */
1046 switch( i_type
& VLC_VAR_CLASS
)
1048 case VLC_VAR_STRING
:
1049 p_val
->psz_string
= config_GetPsz( psz_name
);
1050 if( !p_val
->psz_string
) p_val
->psz_string
= strdup("");
1053 p_val
->f_float
= config_GetFloat( psz_name
);
1055 case VLC_VAR_INTEGER
:
1056 p_val
->i_int
= config_GetInt( psz_name
);
1059 p_val
->b_bool
= config_GetInt( psz_name
) > 0;
1062 vlc_assert_unreachable();
1063 case VLC_VAR_ADDRESS
:
1069 int (var_InheritURational
)(vlc_object_t
*object
,
1070 unsigned *num
, unsigned *den
,
1073 char *str
= var_InheritString(object
, var
);
1078 unsigned n
= strtoul(str
, &sep
, 10);
1083 /* Decimal integer */
1089 /* Decimal fraction */
1090 d
= strtoul(sep
+ 1, &sep
, 10);
1096 /* Decimal number */
1100 while ((c
= *(++sep
)) != '\0') {
1121 } else if (d
== 0) {
1125 vlc_ureduce(num
, den
, n
, d
, 0);
1133 return VLC_EGENERIC
;
1136 void var_FreeList( vlc_value_t
*p_val
, vlc_value_t
*p_val2
)
1138 switch( p_val
->p_list
->i_type
& VLC_VAR_CLASS
)
1140 case VLC_VAR_STRING
:
1141 for( int i
= 0; i
< p_val
->p_list
->i_count
; i
++ )
1142 free( p_val
->p_list
->p_values
[i
].psz_string
);
1146 free( p_val
->p_list
->p_values
);
1147 free( p_val
->p_list
);
1149 if( p_val2
!= NULL
)
1151 assert( p_val2
->p_list
!= NULL
);
1152 assert( p_val2
->p_list
->i_type
== VLC_VAR_STRING
);
1154 for( int i
= 0; i
< p_val2
->p_list
->i_count
; i
++ )
1155 free( p_val2
->p_list
->p_values
[i
].psz_string
);
1156 free( p_val2
->p_list
->p_values
);
1157 free( p_val2
->p_list
);
1161 static void DumpVariable(const void *data
, const VISIT which
, const int depth
)
1163 if (which
!= postorder
&& which
!= leaf
)
1167 const variable_t
*var
= *(const variable_t
**)data
;
1168 const char *typename
= "unknown";
1170 switch (var
->i_type
& VLC_VAR_TYPE
)
1172 case VLC_VAR_VOID
: typename
= "void"; break;
1173 case VLC_VAR_BOOL
: typename
= "bool"; break;
1174 case VLC_VAR_INTEGER
: typename
= "integer"; break;
1175 case VLC_VAR_STRING
: typename
= "string"; break;
1176 case VLC_VAR_FLOAT
: typename
= "float"; break;
1177 case VLC_VAR_COORDS
: typename
= "coordinates"; break;
1178 case VLC_VAR_ADDRESS
: typename
= "address"; break;
1179 default: typename
= "unknown"; break;
1182 printf(" *-o \"%s\" (%s", var
->psz_name
, typename
);
1183 if (var
->psz_text
!= NULL
)
1184 printf(", %s", var
->psz_text
);
1186 if (var
->i_type
& VLC_VAR_HASCHOICE
)
1187 fputs(", has choices", stdout
);
1188 if (var
->i_type
& VLC_VAR_ISCOMMAND
)
1189 fputs(", command", stdout
);
1190 if (var
->value_callbacks
.i_entries
)
1191 printf(", %d callbacks", var
->value_callbacks
.i_entries
);
1193 switch (var
->i_type
& VLC_VAR_CLASS
)
1198 printf(": %s", var
->val
.b_bool
? "true" : "false");
1200 case VLC_VAR_INTEGER
:
1201 printf(": %"PRId64
, var
->val
.i_int
);
1203 case VLC_VAR_STRING
:
1204 printf(": \"%s\"", var
->val
.psz_string
);
1207 printf(": %f", var
->val
.f_float
);
1209 case VLC_VAR_COORDS
:
1210 printf(": %"PRId32
"x%"PRId32
,
1211 var
->val
.coords
.x
, var
->val
.coords
.y
);
1213 case VLC_VAR_ADDRESS
:
1214 printf(": %p", var
->val
.p_address
);
1220 void DumpVariables(vlc_object_t
*obj
)
1222 vlc_mutex_lock(&vlc_internals(obj
)->var_lock
);
1223 if (vlc_internals(obj
)->var_root
== NULL
)
1224 puts(" `-o No variables");
1226 twalk(vlc_internals(obj
)->var_root
, DumpVariable
);
1227 vlc_mutex_unlock(&vlc_internals(obj
)->var_lock
);
1230 static thread_local
void *twalk_ctx
;
1232 static void TwalkGetNames(const void *data
, const VISIT which
, const int depth
)
1234 if (which
!= postorder
&& which
!= leaf
)
1238 const variable_t
*var
= *(const variable_t
**)data
;
1239 DECL_ARRAY(char *) *names
= twalk_ctx
;
1240 char *dup
= strdup(var
->psz_name
);
1242 ARRAY_APPEND(*names
, dup
);
1245 char **var_GetAllNames(vlc_object_t
*obj
)
1247 vlc_object_internals_t
*priv
= vlc_internals(obj
);
1249 DECL_ARRAY(char *) names
;
1253 vlc_mutex_lock(&priv
->var_lock
);
1254 twalk(priv
->var_root
, TwalkGetNames
);
1255 vlc_mutex_unlock(&priv
->var_lock
);
1257 if (names
.i_size
== 0)
1259 ARRAY_APPEND(names
, NULL
);
1260 return names
.p_elems
;