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
48 struct callback_entry_t
*next
;
51 vlc_callback_t pf_value_callback
;
52 vlc_list_callback_t pf_list_callback
;
58 typedef struct variable_ops_t
60 int (*pf_cmp
) ( vlc_value_t
, vlc_value_t
);
61 void (*pf_dup
) ( vlc_value_t
* );
62 void (*pf_free
) ( vlc_value_t
* );
66 * The structure describing a variable.
67 * \note vlc_value_t is the common union for variable values
71 char * psz_name
; /**< The variable unique name (must be first) */
73 /** The variable's exported value */
76 /** The variable display name, mainly for use by the interfaces */
79 const variable_ops_t
*ops
;
81 int i_type
; /**< The type of the variable */
82 unsigned i_usage
; /**< Reference count */
84 /** If the variable has min/max/step values */
85 vlc_value_t min
, max
, step
;
87 /** List of choices */
89 /** List of friendly names for the choices */
93 /** Set to TRUE if the variable is in a callback */
96 /** Registered value callbacks */
97 callback_entry_t
*value_callbacks
;
98 /** Registered list callbacks */
99 callback_entry_t
*list_callbacks
;
102 static int CmpBool( vlc_value_t v
, vlc_value_t w
)
104 return v
.b_bool
? w
.b_bool
? 0 : 1 : w
.b_bool
? -1 : 0;
107 static int CmpInt( vlc_value_t v
, vlc_value_t w
)
109 return v
.i_int
== w
.i_int
? 0 : v
.i_int
> w
.i_int
? 1 : -1;
112 static int CmpString( vlc_value_t v
, vlc_value_t w
)
115 return !w
.psz_string
? 0 : -1;
117 return !w
.psz_string
? 1 : strcmp( v
.psz_string
, w
.psz_string
);
119 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; }
120 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; }
122 static void DupDummy( vlc_value_t
*p_val
) { (void)p_val
; /* unused */ }
123 static void DupString( vlc_value_t
*p_val
)
125 p_val
->psz_string
= strdup( p_val
->psz_string
? p_val
->psz_string
: "" );
128 static void FreeDummy( vlc_value_t
*p_val
) { (void)p_val
; /* unused */ }
129 static void FreeString( vlc_value_t
*p_val
) { free( p_val
->psz_string
); }
131 static const struct variable_ops_t
132 void_ops
= { NULL
, DupDummy
, FreeDummy
, },
133 addr_ops
= { CmpAddress
, DupDummy
, FreeDummy
, },
134 bool_ops
= { CmpBool
, DupDummy
, FreeDummy
, },
135 float_ops
= { CmpFloat
, DupDummy
, FreeDummy
, },
136 int_ops
= { CmpInt
, DupDummy
, FreeDummy
, },
137 string_ops
= { CmpString
, DupString
, FreeString
, },
138 coords_ops
= { NULL
, DupDummy
, FreeDummy
, };
140 static int varcmp( const void *a
, const void *b
)
142 const variable_t
*va
= a
, *vb
= b
;
144 /* psz_name must be first */
145 assert( va
== (const void *)&va
->psz_name
);
146 return strcmp( va
->psz_name
, vb
->psz_name
);
149 static variable_t
*Lookup( vlc_object_t
*obj
, const char *psz_name
)
151 vlc_object_internals_t
*priv
= vlc_internals( obj
);
154 vlc_mutex_lock(&priv
->var_lock
);
155 pp_var
= tfind( &psz_name
, &priv
->var_root
, varcmp
);
156 return (pp_var
!= NULL
) ? *pp_var
: NULL
;
159 static void Destroy( variable_t
*p_var
)
161 p_var
->ops
->pf_free( &p_var
->val
);
163 for (size_t i
= 0, count
= p_var
->choices_count
; i
< count
; i
++)
165 p_var
->ops
->pf_free(&p_var
->choices
[i
]);
166 free(p_var
->choices_text
[i
]);
168 free(p_var
->choices
);
169 free(p_var
->choices_text
);
171 free( p_var
->psz_name
);
172 free( p_var
->psz_text
);
173 while (unlikely(p_var
->value_callbacks
!= NULL
))
175 callback_entry_t
*next
= p_var
->value_callbacks
->next
;
177 free(p_var
->value_callbacks
);
178 p_var
->value_callbacks
= next
;
180 assert(p_var
->list_callbacks
== NULL
);
185 * Adjusts a value to fit the constraints for a certain variable:
186 * - If the value is lower than the minimum, use the minimum.
187 * - If the value is higher than the maximum, use the maximum.
188 * - If the variable has steps, round the value to the nearest step.
190 static void CheckValue(variable_t
*var
, vlc_value_t
*val
)
192 /* Check that our variable is within the bounds */
193 switch (var
->i_type
& VLC_VAR_TYPE
)
195 case VLC_VAR_INTEGER
:
196 if (val
->i_int
< var
->min
.i_int
)
197 val
->i_int
= var
->min
.i_int
;
198 else if (val
->i_int
> var
->max
.i_int
)
199 val
->i_int
= var
->max
.i_int
;
200 if (var
->step
.i_int
!= 0 && (val
->i_int
% var
->step
.i_int
))
203 val
->i_int
= (val
->i_int
+ (var
->step
.i_int
/ 2))
204 / var
->step
.i_int
* var
->step
.i_int
;
206 val
->i_int
= (val
->i_int
- (var
->step
.i_int
/ 2))
207 / var
->step
.i_int
* var
->step
.i_int
;
212 if (isless(val
->f_float
, var
->min
.f_float
))
213 val
->f_float
= var
->min
.f_float
;
214 else if (isgreater(val
->f_float
, var
->max
.f_float
))
215 val
->f_float
= var
->max
.f_float
;
216 if (var
->step
.f_float
!= 0.f
)
217 val
->f_float
= var
->step
.f_float
218 * roundf(val
->f_float
/ var
->step
.f_float
);
224 * Waits until the variable is inactive (i.e. not executing a callback)
226 static void WaitUnused(vlc_object_t
*obj
, variable_t
*var
)
228 vlc_object_internals_t
*priv
= vlc_internals(obj
);
230 mutex_cleanup_push(&priv
->var_lock
);
231 while (var
->b_incallback
)
232 vlc_cond_wait(&priv
->var_wait
, &priv
->var_lock
);
236 static void TriggerCallback(vlc_object_t
*obj
, variable_t
*var
,
237 const char *name
, vlc_value_t prev
)
241 callback_entry_t
*entry
= var
->value_callbacks
;
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
);
253 entry
->pf_value_callback(obj
, name
, prev
, var
->val
, entry
->p_data
);
256 while (entry
!= NULL
);
258 vlc_mutex_lock(&priv
->var_lock
);
259 var
->b_incallback
= false;
260 vlc_cond_broadcast(&priv
->var_wait
);
263 static void TriggerListCallback(vlc_object_t
*obj
, variable_t
*var
,
264 const char *name
, int action
, vlc_value_t
*val
)
268 callback_entry_t
*entry
= var
->list_callbacks
;
272 vlc_object_internals_t
*priv
= vlc_internals(obj
);
274 assert(!var
->b_incallback
);
275 var
->b_incallback
= true;
276 vlc_mutex_unlock(&priv
->var_lock
);
280 entry
->pf_list_callback(obj
, name
, action
, val
, entry
->p_data
);
283 while (entry
!= NULL
);
285 vlc_mutex_lock(&priv
->var_lock
);
286 var
->b_incallback
= false;
287 vlc_cond_broadcast(&priv
->var_wait
);
290 int (var_Create
)( vlc_object_t
*p_this
, const char *psz_name
, int i_type
)
294 variable_t
*p_var
= calloc( 1, sizeof( *p_var
) );
298 p_var
->psz_name
= strdup( psz_name
);
299 p_var
->psz_text
= NULL
;
301 p_var
->i_type
= i_type
& ~VLC_VAR_DOINHERIT
;
305 p_var
->choices_count
= 0;
306 p_var
->choices
= NULL
;
307 p_var
->choices_text
= NULL
;
309 p_var
->b_incallback
= false;
310 p_var
->value_callbacks
= NULL
;
312 /* Always initialize the variable, even if it is a list variable; this
313 * will lead to errors if the variable is not initialized, but it will
314 * not cause crashes in the variable handling. */
315 switch( i_type
& VLC_VAR_CLASS
)
318 p_var
->ops
= &bool_ops
;
319 p_var
->val
.b_bool
= false;
321 case VLC_VAR_INTEGER
:
322 p_var
->ops
= &int_ops
;
323 p_var
->val
.i_int
= 0;
324 p_var
->min
.i_int
= INT64_MIN
;
325 p_var
->max
.i_int
= INT64_MAX
;
328 p_var
->ops
= &string_ops
;
329 p_var
->val
.psz_string
= NULL
;
332 p_var
->ops
= &float_ops
;
333 p_var
->val
.f_float
= 0.f
;
334 p_var
->min
.f_float
= -FLT_MAX
;
335 p_var
->max
.f_float
= FLT_MAX
;
338 p_var
->ops
= &coords_ops
;
339 p_var
->val
.coords
.x
= p_var
->val
.coords
.y
= 0;
341 case VLC_VAR_ADDRESS
:
342 p_var
->ops
= &addr_ops
;
343 p_var
->val
.p_address
= NULL
;
346 p_var
->ops
= &void_ops
;
349 vlc_assert_unreachable ();
352 if (i_type
& VLC_VAR_DOINHERIT
)
353 var_Inherit(p_this
, psz_name
, i_type
, &p_var
->val
);
355 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
356 variable_t
**pp_var
, *p_oldvar
;
357 int ret
= VLC_SUCCESS
;
359 vlc_mutex_lock( &p_priv
->var_lock
);
361 pp_var
= tsearch( p_var
, &p_priv
->var_root
, varcmp
);
362 if( unlikely(pp_var
== NULL
) )
364 else if( (p_oldvar
= *pp_var
) == p_var
) /* Variable create */
365 p_var
= NULL
; /* Variable created */
366 else /* Variable already exists */
368 assert (((i_type
^ p_oldvar
->i_type
) & VLC_VAR_CLASS
) == 0);
370 p_oldvar
->i_type
|= i_type
& VLC_VAR_ISCOMMAND
;
372 vlc_mutex_unlock( &p_priv
->var_lock
);
374 /* If we did not need to create a new variable, free everything... */
380 void (var_Destroy
)(vlc_object_t
*p_this
, const char *psz_name
)
386 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
388 p_var
= Lookup( p_this
, psz_name
);
390 msg_Dbg( p_this
, "attempt to destroy nonexistent variable \"%s\"",
392 else if( --p_var
->i_usage
== 0 )
394 assert(!p_var
->b_incallback
);
395 tdelete( p_var
, &p_priv
->var_root
, varcmp
);
399 assert(p_var
->i_usage
!= -1u);
402 vlc_mutex_unlock( &p_priv
->var_lock
);
408 static void CleanupVar( void *var
)
413 void var_DestroyAll( vlc_object_t
*obj
)
415 vlc_object_internals_t
*priv
= vlc_internals( obj
);
417 tdestroy( priv
->var_root
, CleanupVar
);
418 priv
->var_root
= NULL
;
421 int (var_Change
)(vlc_object_t
*p_this
, const char *psz_name
, int i_action
, ...)
424 int ret
= VLC_SUCCESS
;
431 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
433 p_var
= Lookup( p_this
, psz_name
);
436 vlc_mutex_unlock( &p_priv
->var_lock
);
440 va_start(ap
, i_action
);
444 *va_arg(ap
, vlc_value_t
*) = p_var
->min
;
447 *va_arg(ap
, vlc_value_t
*) = p_var
->max
;
449 case VLC_VAR_SETMINMAX
:
450 assert(p_var
->ops
->pf_free
== FreeDummy
);
451 p_var
->min
= va_arg(ap
, vlc_value_t
);
452 p_var
->max
= va_arg(ap
, vlc_value_t
);
454 case VLC_VAR_SETSTEP
:
455 assert(p_var
->ops
->pf_free
== FreeDummy
);
456 p_var
->step
= va_arg(ap
, vlc_value_t
);
457 CheckValue( p_var
, &p_var
->val
);
459 case VLC_VAR_GETSTEP
:
460 switch (p_var
->i_type
& VLC_VAR_TYPE
)
462 case VLC_VAR_INTEGER
:
463 if (p_var
->step
.i_int
== 0)
467 if (p_var
->step
.f_float
== 0.f
)
473 if (ret
== VLC_SUCCESS
)
474 *va_arg(ap
, vlc_value_t
*) = p_var
->step
;
476 case VLC_VAR_ADDCHOICE
:
478 vlc_value_t val
= va_arg(ap
, vlc_value_t
);
479 const char *text
= va_arg(ap
, const char *);
480 size_t count
= p_var
->choices_count
;
482 TAB_APPEND(p_var
->choices_count
, p_var
->choices
, val
);
483 p_var
->ops
->pf_dup(&p_var
->choices
[count
]);
484 TAB_APPEND(count
, p_var
->choices_text
, NULL
);
485 assert(count
== p_var
->choices_count
);
487 p_var
->choices_text
[count
- 1] = strdup(text
);
489 TriggerListCallback(p_this
, p_var
, psz_name
, VLC_VAR_ADDCHOICE
,
493 case VLC_VAR_DELCHOICE
:
495 vlc_value_t val
= va_arg(ap
, vlc_value_t
);
496 size_t count
= p_var
->choices_count
, i
;
498 for (i
= 0; i
< count
; i
++)
499 if (p_var
->ops
->pf_cmp(p_var
->choices
[i
], val
) == 0)
508 p_var
->ops
->pf_free(&p_var
->choices
[i
]);
509 free(p_var
->choices_text
[i
]);
510 TAB_ERASE(p_var
->choices_count
, p_var
->choices
, i
);
511 TAB_ERASE(count
, p_var
->choices_text
, i
);
512 assert(count
== p_var
->choices_count
);
514 TriggerListCallback(p_this
, p_var
, psz_name
, VLC_VAR_DELCHOICE
,
518 case VLC_VAR_CHOICESCOUNT
:
519 *va_arg(ap
, size_t *) = p_var
->choices_count
;
521 case VLC_VAR_CLEARCHOICES
:
522 for (size_t i
= 0; i
< p_var
->choices_count
; i
++)
523 p_var
->ops
->pf_free(&p_var
->choices
[i
]);
524 for (size_t i
= 0; i
< p_var
->choices_count
; i
++)
525 free(p_var
->choices_text
[i
]);
526 TAB_CLEAN(p_var
->choices_count
, p_var
->choices
);
527 free(p_var
->choices_text
);
528 p_var
->choices_text
= NULL
;
530 TriggerListCallback(p_this
, p_var
, psz_name
, VLC_VAR_CLEARCHOICES
, NULL
);
532 case VLC_VAR_SETVALUE
:
533 /* Duplicate data if needed */
534 newval
= va_arg(ap
, vlc_value_t
);
535 p_var
->ops
->pf_dup( &newval
);
536 /* Backup needed stuff */
538 /* Check boundaries and list */
539 CheckValue( p_var
, &newval
);
540 /* Set the variable */
542 /* Free data if needed */
543 p_var
->ops
->pf_free( &oldval
);
545 case VLC_VAR_GETCHOICES
:
547 size_t *count
= va_arg(ap
, size_t *);
548 vlc_value_t
**values
= va_arg(ap
, vlc_value_t
**);
549 char ***texts
= va_arg(ap
, char ***);
551 *count
= p_var
->choices_count
;
552 *values
= xmalloc(p_var
->choices_count
* sizeof (**values
));
554 for (size_t i
= 0; i
< p_var
->choices_count
; i
++)
556 vlc_value_t
*val
= (*values
) + i
;
557 *val
= p_var
->choices
[i
];
558 p_var
->ops
->pf_dup(val
);
563 char **tab
= xmalloc(p_var
->choices_count
* sizeof (*tab
));
566 for (size_t i
= 0; i
< p_var
->choices_count
; i
++)
567 tab
[i
] = (p_var
->choices_text
[i
] != NULL
)
568 ? strdup(p_var
->choices_text
[i
]) : NULL
;
572 case VLC_VAR_SETTEXT
:
574 const char *text
= va_arg(ap
, const char *);
576 free( p_var
->psz_text
);
577 p_var
->psz_text
= (text
!= NULL
) ? strdup(text
) : NULL
;
580 case VLC_VAR_GETTEXT
:
581 *va_arg(ap
, char **) = (p_var
->psz_text
!= NULL
)
582 ? strdup(p_var
->psz_text
) : NULL
;
588 vlc_mutex_unlock( &p_priv
->var_lock
);
592 int (var_GetAndSet
)(vlc_object_t
*p_this
, const char *psz_name
, int i_action
,
601 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
603 p_var
= Lookup( p_this
, psz_name
);
606 vlc_mutex_unlock( &p_priv
->var_lock
);
610 WaitUnused( p_this
, p_var
);
612 /* Duplicated data if needed */
613 //p_var->ops->pf_dup( &val );
615 /* Backup needed stuff */
618 /* depending of the action requiered */
621 case VLC_VAR_BOOL_TOGGLE
:
622 assert( ( p_var
->i_type
& VLC_VAR_BOOL
) == VLC_VAR_BOOL
);
623 p_var
->val
.b_bool
= !p_var
->val
.b_bool
;
625 case VLC_VAR_INTEGER_ADD
:
626 assert( ( p_var
->i_type
& VLC_VAR_INTEGER
) == VLC_VAR_INTEGER
);
627 p_var
->val
.i_int
+= p_val
->i_int
;
629 case VLC_VAR_INTEGER_OR
:
630 assert( ( p_var
->i_type
& VLC_VAR_INTEGER
) == VLC_VAR_INTEGER
);
631 p_var
->val
.i_int
|= p_val
->i_int
;
633 case VLC_VAR_INTEGER_NAND
:
634 assert( ( p_var
->i_type
& VLC_VAR_INTEGER
) == VLC_VAR_INTEGER
);
635 p_var
->val
.i_int
&= ~p_val
->i_int
;
638 vlc_mutex_unlock( &p_priv
->var_lock
);
642 /* Check boundaries */
643 CheckValue( p_var
, &p_var
->val
);
646 /* Deal with callbacks.*/
647 TriggerCallback( p_this
, p_var
, psz_name
, oldval
);
649 vlc_mutex_unlock( &p_priv
->var_lock
);
653 int (var_Type
)(vlc_object_t
*p_this
, const char *psz_name
)
660 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
662 p_var
= Lookup( p_this
, psz_name
);
665 i_type
= p_var
->i_type
;
666 if (p_var
->choices_count
> 0)
667 i_type
|= VLC_VAR_HASCHOICE
;
669 vlc_mutex_unlock( &p_priv
->var_lock
);
674 int (var_SetChecked
)(vlc_object_t
*p_this
, const char *psz_name
,
675 int expected_type
, vlc_value_t val
)
682 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
684 p_var
= Lookup( p_this
, psz_name
);
687 vlc_mutex_unlock( &p_priv
->var_lock
);
691 assert( expected_type
== 0 ||
692 (p_var
->i_type
& VLC_VAR_CLASS
) == expected_type
);
693 assert ((p_var
->i_type
& VLC_VAR_CLASS
) != VLC_VAR_VOID
);
695 WaitUnused( p_this
, p_var
);
697 /* Duplicate data if needed */
698 p_var
->ops
->pf_dup( &val
);
700 /* Backup needed stuff */
703 /* Check boundaries and list */
704 CheckValue( p_var
, &val
);
706 /* Set the variable */
709 /* Deal with callbacks */
710 TriggerCallback( p_this
, p_var
, psz_name
, oldval
);
712 /* Free data if needed */
713 p_var
->ops
->pf_free( &oldval
);
715 vlc_mutex_unlock( &p_priv
->var_lock
);
719 int (var_Set
)(vlc_object_t
*p_this
, const char *psz_name
, vlc_value_t val
)
721 return var_SetChecked( p_this
, psz_name
, 0, val
);
724 int (var_GetChecked
)(vlc_object_t
*p_this
, const char *psz_name
,
725 int expected_type
, vlc_value_t
*p_val
)
729 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
731 int err
= VLC_SUCCESS
;
733 p_var
= Lookup( p_this
, psz_name
);
736 assert( expected_type
== 0 ||
737 (p_var
->i_type
& VLC_VAR_CLASS
) == expected_type
);
738 assert ((p_var
->i_type
& VLC_VAR_CLASS
) != VLC_VAR_VOID
);
740 /* Really get the variable */
743 /* Duplicate value if needed */
744 p_var
->ops
->pf_dup( p_val
);
749 vlc_mutex_unlock( &p_priv
->var_lock
);
753 int (var_Get
)(vlc_object_t
*p_this
, const char *psz_name
, vlc_value_t
*p_val
)
755 return var_GetChecked( p_this
, psz_name
, 0, p_val
);
762 } vlc_callback_type_t
;
764 static void AddCallback( vlc_object_t
*p_this
, const char *psz_name
,
765 callback_entry_t
*restrict entry
,
766 vlc_callback_type_t i_type
)
772 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
774 p_var
= Lookup( p_this
, psz_name
);
777 vlc_mutex_unlock( &p_priv
->var_lock
);
778 msg_Err( p_this
, "cannot add callback %p to nonexistent variable '%s'",
779 entry
->p_callback
, psz_name
);
783 WaitUnused( p_this
, p_var
);
785 callback_entry_t
**pp
;
787 if (i_type
== vlc_value_callback
)
788 pp
= &p_var
->value_callbacks
;
790 pp
= &p_var
->list_callbacks
;
795 vlc_mutex_unlock( &p_priv
->var_lock
);
798 void (var_AddCallback
)(vlc_object_t
*p_this
, const char *psz_name
,
799 vlc_callback_t pf_callback
, void *p_data
)
801 callback_entry_t
*entry
= xmalloc(sizeof (*entry
));
803 entry
->pf_value_callback
= pf_callback
;
804 entry
->p_data
= p_data
;
805 AddCallback(p_this
, psz_name
, entry
, vlc_value_callback
);
808 static void DelCallback( vlc_object_t
*p_this
, const char *psz_name
,
809 const callback_entry_t
*restrict match
,
810 vlc_callback_type_t i_type
)
812 callback_entry_t
**pp
, *entry
;
817 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
819 p_var
= Lookup( p_this
, psz_name
);
822 vlc_mutex_unlock( &p_priv
->var_lock
);
823 msg_Err( p_this
, "cannot delete callback %p from nonexistent "
824 "variable '%s'", match
->p_callback
, psz_name
);
828 WaitUnused( p_this
, p_var
);
830 if (i_type
== vlc_value_callback
)
831 pp
= &p_var
->value_callbacks
;
833 pp
= &p_var
->list_callbacks
;
836 assert(entry
!= NULL
);
838 while (entry
->p_callback
!= match
->p_callback
839 || entry
->p_data
!= match
->p_data
)
843 assert(entry
!= NULL
);
847 vlc_mutex_unlock( &p_priv
->var_lock
);
851 void (var_DelCallback
)(vlc_object_t
*p_this
, const char *psz_name
,
852 vlc_callback_t pf_callback
, void *p_data
)
854 callback_entry_t entry
;
855 entry
.pf_value_callback
= pf_callback
;
856 entry
.p_data
= p_data
;
858 DelCallback(p_this
, psz_name
, &entry
, vlc_value_callback
);
861 void (var_TriggerCallback
)(vlc_object_t
*p_this
, const char *psz_name
)
863 vlc_object_internals_t
*p_priv
= vlc_internals( p_this
);
864 variable_t
*p_var
= Lookup( p_this
, psz_name
);
867 WaitUnused( p_this
, p_var
);
869 /* Deal with callbacks. Tell we're in a callback, release the lock,
870 * call stored functions, retake the lock. */
871 TriggerCallback( p_this
, p_var
, psz_name
, p_var
->val
);
873 vlc_mutex_unlock( &p_priv
->var_lock
);
876 void (var_AddListCallback
)(vlc_object_t
*p_this
, const char *psz_name
,
877 vlc_list_callback_t pf_callback
, void *p_data
)
879 callback_entry_t
*entry
= xmalloc(sizeof (*entry
));
881 entry
->pf_list_callback
= pf_callback
;
882 entry
->p_data
= p_data
;
883 AddCallback(p_this
, psz_name
, entry
, vlc_list_callback
);
886 void (var_DelListCallback
)(vlc_object_t
*p_this
, const char *psz_name
,
887 vlc_list_callback_t pf_callback
, void *p_data
)
889 callback_entry_t entry
;
891 entry
.pf_list_callback
= pf_callback
;
892 entry
.p_data
= p_data
;
893 DelCallback(p_this
, psz_name
, &entry
, vlc_list_callback
);
896 /** Parse a stringified option
897 * This function parse a string option and create the associated object
899 * The option must be of the form "[no[-]]foo[=bar]" where foo is the
900 * option name and bar is the value of the option.
901 * \param p_obj the object in which the variable must be created
902 * \param psz_option the option to parse
903 * \param trusted whether the option is set by a trusted input or not
906 void var_OptionParse( vlc_object_t
*p_obj
, const char *psz_option
,
909 char *psz_name
, *psz_value
;
914 val
.psz_string
= NULL
;
916 /* It's too much of a hassle to remove the ':' when we parse
918 if( psz_option
[0] == ':' )
924 psz_name
= strdup( psz_option
);
925 if( psz_name
== NULL
)
928 psz_value
= strchr( psz_name
, '=' );
929 if( psz_value
!= NULL
)
932 i_type
= config_GetType( psz_name
);
933 if( !i_type
&& !psz_value
)
935 /* check for "no-foo" or "nofoo" */
936 if( !strncmp( psz_name
, "no-", 3 ) )
938 memmove( psz_name
, psz_name
+ 3, strlen(psz_name
) + 1 - 3 );
940 else if( !strncmp( psz_name
, "no", 2 ) )
942 memmove( psz_name
, psz_name
+ 2, strlen(psz_name
) + 1 - 2 );
944 else goto cleanup
; /* Option doesn't exist */
947 i_type
= config_GetType( psz_name
);
949 if( !i_type
) goto cleanup
; /* Option doesn't exist */
951 if( ( i_type
!= VLC_VAR_BOOL
) &&
952 ( !psz_value
|| !*psz_value
) ) goto cleanup
; /* Invalid value */
954 /* check if option is unsafe */
955 if( !trusted
&& !config_IsSafe( psz_name
) )
957 msg_Err( p_obj
, "unsafe option \"%s\" has been ignored for "
958 "security reasons", psz_name
);
963 /* Create the variable in the input object.
964 * Children of the input object will be able to retrieve this value
965 * thanks to the inheritance property of the object variables. */
966 var_Create( p_obj
, psz_name
, i_type
);
974 long long int value
= strtoll( psz_value
, &endptr
, 0 );
975 if( endptr
== psz_value
) /* Not an integer */
976 val
.b_bool
= strcasecmp( psz_value
, "true" ) == 0
977 || strcasecmp( psz_value
, "yes" ) == 0;
979 val
.b_bool
= value
!= 0;
982 val
.b_bool
= !b_isno
;
985 case VLC_VAR_INTEGER
:
986 val
.i_int
= strtoll( psz_value
, NULL
, 0 );
990 val
.f_float
= us_atof( psz_value
);
994 val
.psz_string
= psz_value
;
1001 var_Set( p_obj
, psz_name
, val
);
1007 int (var_LocationParse
)(vlc_object_t
*obj
, const char *mrl
, const char *pref
)
1009 int ret
= VLC_SUCCESS
;
1010 size_t preflen
= strlen (pref
) + 1;
1012 assert(mrl
!= NULL
);
1013 while (*mrl
!= '\0')
1015 mrl
+= strspn (mrl
, ":;"); /* skip leading colon(s) */
1017 size_t len
= strcspn (mrl
, ":;");
1018 char *buf
= malloc (preflen
+ len
);
1020 if (likely(buf
!= NULL
))
1022 /* NOTE: this does not support the "no-<varname>" bool syntax. */
1023 /* DO NOT use asprintf() here; it won't work! Think again. */
1024 snprintf (buf
, preflen
+ len
, "%s%s", pref
, mrl
);
1025 var_OptionParse (obj
, buf
, false);
1036 int var_Inherit( vlc_object_t
*p_this
, const char *psz_name
, int i_type
,
1037 vlc_value_t
*p_val
)
1039 i_type
&= VLC_VAR_CLASS
;
1040 for( vlc_object_t
*obj
= p_this
; obj
!= NULL
; obj
= obj
->obj
.parent
)
1042 if( var_GetChecked( obj
, psz_name
, i_type
, p_val
) == VLC_SUCCESS
)
1046 /* else take value from config */
1047 switch( i_type
& VLC_VAR_CLASS
)
1049 case VLC_VAR_STRING
:
1050 p_val
->psz_string
= config_GetPsz( psz_name
);
1051 if( !p_val
->psz_string
) p_val
->psz_string
= strdup("");
1054 p_val
->f_float
= config_GetFloat( psz_name
);
1056 case VLC_VAR_INTEGER
:
1057 p_val
->i_int
= config_GetInt( psz_name
);
1060 p_val
->b_bool
= config_GetInt( psz_name
) > 0;
1063 vlc_assert_unreachable();
1064 case VLC_VAR_ADDRESS
:
1070 int (var_InheritURational
)(vlc_object_t
*object
,
1071 unsigned *num
, unsigned *den
,
1074 char *str
= var_InheritString(object
, var
);
1079 unsigned n
= strtoul(str
, &sep
, 10);
1084 /* Decimal integer */
1090 /* Decimal fraction */
1091 d
= strtoul(sep
+ 1, &sep
, 10);
1097 /* Decimal number */
1101 while ((c
= *(++sep
)) != '\0') {
1122 } else if (d
== 0) {
1126 vlc_ureduce(num
, den
, n
, d
, 0);
1134 return VLC_EGENERIC
;
1137 static void DumpVariable(const void *data
, const VISIT which
, const int depth
)
1139 if (which
!= postorder
&& which
!= leaf
)
1143 const variable_t
*var
= *(const variable_t
**)data
;
1144 const char *typename
= "unknown";
1146 switch (var
->i_type
& VLC_VAR_TYPE
)
1148 case VLC_VAR_VOID
: typename
= "void"; break;
1149 case VLC_VAR_BOOL
: typename
= "bool"; break;
1150 case VLC_VAR_INTEGER
: typename
= "integer"; break;
1151 case VLC_VAR_STRING
: typename
= "string"; break;
1152 case VLC_VAR_FLOAT
: typename
= "float"; break;
1153 case VLC_VAR_COORDS
: typename
= "coordinates"; break;
1154 case VLC_VAR_ADDRESS
: typename
= "address"; break;
1155 default: typename
= "unknown"; break;
1158 printf(" *-o \"%s\" (%s", var
->psz_name
, typename
);
1159 if (var
->psz_text
!= NULL
)
1160 printf(", %s", var
->psz_text
);
1162 if (var
->i_type
& VLC_VAR_HASCHOICE
)
1163 fputs(", has choices", stdout
);
1164 if (var
->i_type
& VLC_VAR_ISCOMMAND
)
1165 fputs(", command", stdout
);
1166 if (var
->value_callbacks
!= NULL
)
1170 for (callback_entry_t
*entry
= var
->value_callbacks
;
1172 entry
= entry
->next
)
1175 printf(", %zu callbacks", count
);
1178 switch (var
->i_type
& VLC_VAR_CLASS
)
1183 printf(": %s", var
->val
.b_bool
? "true" : "false");
1185 case VLC_VAR_INTEGER
:
1186 printf(": %"PRId64
, var
->val
.i_int
);
1188 case VLC_VAR_STRING
:
1189 printf(": \"%s\"", var
->val
.psz_string
);
1192 printf(": %f", var
->val
.f_float
);
1194 case VLC_VAR_COORDS
:
1195 printf(": %"PRId32
"x%"PRId32
,
1196 var
->val
.coords
.x
, var
->val
.coords
.y
);
1198 case VLC_VAR_ADDRESS
:
1199 printf(": %p", var
->val
.p_address
);
1205 void DumpVariables(vlc_object_t
*obj
)
1207 vlc_mutex_lock(&vlc_internals(obj
)->var_lock
);
1208 if (vlc_internals(obj
)->var_root
== NULL
)
1209 puts(" `-o No variables");
1211 twalk(vlc_internals(obj
)->var_root
, DumpVariable
);
1212 vlc_mutex_unlock(&vlc_internals(obj
)->var_lock
);
1215 static thread_local
void *twalk_ctx
;
1217 static void TwalkGetNames(const void *data
, const VISIT which
, const int depth
)
1219 if (which
!= postorder
&& which
!= leaf
)
1223 const variable_t
*var
= *(const variable_t
**)data
;
1224 DECL_ARRAY(char *) *names
= twalk_ctx
;
1225 char *dup
= strdup(var
->psz_name
);
1227 ARRAY_APPEND(*names
, dup
);
1230 char **var_GetAllNames(vlc_object_t
*obj
)
1232 vlc_object_internals_t
*priv
= vlc_internals(obj
);
1234 DECL_ARRAY(char *) names
;
1238 vlc_mutex_lock(&priv
->var_lock
);
1239 twalk(priv
->var_root
, TwalkGetNames
);
1240 vlc_mutex_unlock(&priv
->var_lock
);
1242 if (names
.i_size
== 0)
1244 ARRAY_APPEND(names
, NULL
);
1245 return names
.p_elems
;