chroma: cvpx: always use cached copy
[vlc.git] / src / misc / variables.c
blob2426215954e5651d7fec1f1a29d8c58f922a3260
1 /*****************************************************************************
2 * variables.c: routines for object variables handling
3 *****************************************************************************
4 * Copyright (C) 2002-2009 VLC authors and VideoLAN
5 * $Id$
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 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #ifdef HAVE_SEARCH_H
32 # include <search.h>
33 #endif
34 #include <assert.h>
35 #include <float.h>
36 #include <math.h>
37 #include <limits.h>
39 #include <vlc_common.h>
40 #include <vlc_arrays.h>
41 #include <vlc_charset.h>
42 #include "libvlc.h"
43 #include "variables.h"
44 #include "config/configuration.h"
46 typedef struct callback_entry_t
48 union
50 vlc_callback_t pf_value_callback;
51 vlc_list_callback_t pf_list_callback;
52 void * p_callback;
54 void * p_data;
55 } callback_entry_t;
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 * );
62 } variable_ops_t;
64 typedef struct callback_table_t
66 int i_entries;
67 callback_entry_t * p_entries;
68 } callback_table_t;
70 /**
71 * The structure describing a variable.
72 * \note vlc_value_t is the common union for variable values
74 struct variable_t
76 char * psz_name; /**< The variable unique name (must be first) */
78 /** The variable's exported value */
79 vlc_value_t val;
81 /** The variable display name, mainly for use by the interfaces */
82 char * psz_text;
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 */
93 vlc_list_t 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 */
98 bool b_incallback;
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 )
118 if( !v.psz_string )
119 return !w.psz_string ? 0 : -1;
120 else
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 );
156 variable_t **pp_var;
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 );
180 free( p_var );
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))
201 if (val->i_int > 0)
202 val->i_int = (val->i_int + (var->step.i_int / 2))
203 / var->step.i_int * var->step.i_int;
204 else
205 val->i_int = (val->i_int - (var->step.i_int / 2))
206 / var->step.i_int * var->step.i_int;
208 break;
210 case VLC_VAR_FLOAT:
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);
218 break;
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);
232 vlc_cleanup_pop();
235 static void TriggerCallback(vlc_object_t *obj, variable_t *var,
236 const char *name, vlc_value_t prev)
238 assert(obj != NULL);
240 size_t count = var->value_callbacks.i_entries;
241 if (count == 0)
242 return;
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,
253 entries[i].p_data);
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)
263 assert(obj != NULL);
265 size_t count = var->list_callbacks.i_entries;
266 if (count == 0)
267 return;
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,
278 entries[i].p_data);
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 )
287 assert( p_this );
289 variable_t *p_var = calloc( 1, sizeof( *p_var ) );
290 if( p_var == NULL )
291 return VLC_ENOMEM;
293 p_var->psz_name = strdup( psz_name );
294 p_var->psz_text = NULL;
296 p_var->i_type = i_type & ~VLC_VAR_DOINHERIT;
298 p_var->i_usage = 1;
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 )
313 case VLC_VAR_BOOL:
314 p_var->ops = &bool_ops;
315 p_var->val.b_bool = false;
316 break;
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;
322 break;
323 case VLC_VAR_STRING:
324 p_var->ops = &string_ops;
325 p_var->val.psz_string = NULL;
326 break;
327 case VLC_VAR_FLOAT:
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;
332 break;
333 case VLC_VAR_COORDS:
334 p_var->ops = &coords_ops;
335 p_var->val.coords.x = p_var->val.coords.y = 0;
336 break;
337 case VLC_VAR_ADDRESS:
338 p_var->ops = &addr_ops;
339 p_var->val.p_address = NULL;
340 break;
341 case VLC_VAR_VOID:
342 p_var->ops = &void_ops;
343 break;
344 default:
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) )
359 ret = VLC_ENOMEM;
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);
365 p_oldvar->i_usage++;
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... */
371 if( p_var != NULL )
372 Destroy( p_var );
373 return ret;
376 void (var_Destroy)(vlc_object_t *p_this, const char *psz_name)
378 variable_t *p_var;
380 assert( p_this );
382 vlc_object_internals_t *p_priv = vlc_internals( p_this );
384 p_var = Lookup( p_this, psz_name );
385 if( p_var == NULL )
386 msg_Dbg( p_this, "attempt to destroy nonexistent variable \"%s\"",
387 psz_name );
388 else if( --p_var->i_usage == 0 )
390 assert(!p_var->b_incallback);
391 tdelete( p_var, &p_priv->var_root, varcmp );
393 else
395 assert(p_var->i_usage != -1u);
396 p_var = NULL;
398 vlc_mutex_unlock( &p_priv->var_lock );
400 if( p_var != NULL )
401 Destroy( p_var );
404 static void CleanupVar( void *var )
406 Destroy( 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;
421 variable_t *p_var;
422 vlc_value_t oldval;
423 vlc_value_t newval;
425 assert( p_this );
427 vlc_object_internals_t *p_priv = vlc_internals( p_this );
429 p_var = Lookup( p_this, psz_name );
430 if( p_var == NULL )
432 vlc_mutex_unlock( &p_priv->var_lock );
433 return VLC_ENOVAR;
436 switch( i_action )
438 case VLC_VAR_GETMIN:
439 *p_val = p_var->min;
440 break;
441 case VLC_VAR_GETMAX:
442 *p_val = p_var->max;
443 break;
444 case VLC_VAR_SETMINMAX:
445 assert(p_var->ops->pf_free == FreeDummy);
446 p_var->min = *p_val;
447 p_var->max = *p_val2;
448 break;
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 );
453 break;
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)
459 ret = VLC_EGENERIC;
460 break;
461 case VLC_VAR_FLOAT:
462 if (p_var->step.f_float == 0.f)
463 ret = VLC_EGENERIC;
464 break;
465 default:
466 ret = VLC_EGENERIC;
468 if (ret == VLC_SUCCESS)
469 *p_val = p_var->step;
470 break;
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);
486 break;
488 case VLC_VAR_DELCHOICE:
490 int i;
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 )
494 break;
496 if( i == p_var->choices.i_count )
498 /* Not found */
499 vlc_mutex_unlock( &p_priv->var_lock );
500 return VLC_EGENERIC;
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);
510 break;
512 case VLC_VAR_CHOICESCOUNT:
513 p_val->i_int = p_var->choices.i_count;
514 break;
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);
529 break;
530 case VLC_VAR_SETVALUE:
531 /* Duplicate data if needed */
532 newval = *p_val;
533 p_var->ops->pf_dup( &newval );
534 /* Backup needed stuff */
535 oldval = p_var->val;
536 /* Check boundaries and list */
537 CheckValue( p_var, &newval );
538 /* Set the variable */
539 p_var->val = newval;
540 /* Free data if needed */
541 p_var->ops->pf_free( &oldval );
542 break;
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;
549 if( p_val2 )
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] );
561 if( p_val2 )
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;
568 break;
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 );
573 else
574 p_var->psz_text = NULL;
575 break;
576 case VLC_VAR_GETTEXT:
577 p_val->psz_string = p_var->psz_text ? strdup( p_var->psz_text )
578 : NULL;
579 break;
580 default:
581 break;
584 vlc_mutex_unlock( &p_priv->var_lock );
586 return ret;
589 int (var_GetAndSet)(vlc_object_t *p_this, const char *psz_name, int i_action,
590 vlc_value_t *p_val)
592 variable_t *p_var;
593 vlc_value_t oldval;
595 assert( p_this );
596 assert( p_val );
598 vlc_object_internals_t *p_priv = vlc_internals( p_this );
600 p_var = Lookup( p_this, psz_name );
601 if( p_var == NULL )
603 vlc_mutex_unlock( &p_priv->var_lock );
604 return VLC_ENOVAR;
607 WaitUnused( p_this, p_var );
609 /* Duplicated data if needed */
610 //p_var->ops->pf_dup( &val );
612 /* Backup needed stuff */
613 oldval = p_var->val;
615 /* depending of the action requiered */
616 switch( i_action )
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;
621 break;
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;
625 break;
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;
629 break;
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;
633 break;
634 default:
635 vlc_mutex_unlock( &p_priv->var_lock );
636 return VLC_EGENERIC;
639 /* Check boundaries */
640 CheckValue( p_var, &p_var->val );
641 *p_val = p_var->val;
643 /* Deal with callbacks.*/
644 TriggerCallback( p_this, p_var, psz_name, oldval );
646 vlc_mutex_unlock( &p_priv->var_lock );
647 return VLC_SUCCESS;
650 int (var_Type)(vlc_object_t *p_this, const char *psz_name)
652 variable_t *p_var;
653 int i_type = 0;
655 assert( p_this );
657 vlc_object_internals_t *p_priv = vlc_internals( p_this );
659 p_var = Lookup( p_this, psz_name );
660 if( p_var != NULL )
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 );
668 return i_type;
671 int (var_SetChecked)(vlc_object_t *p_this, const char *psz_name,
672 int expected_type, vlc_value_t val)
674 variable_t *p_var;
675 vlc_value_t oldval;
677 assert( p_this );
679 vlc_object_internals_t *p_priv = vlc_internals( p_this );
681 p_var = Lookup( p_this, psz_name );
682 if( p_var == NULL )
684 vlc_mutex_unlock( &p_priv->var_lock );
685 return VLC_ENOVAR;
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 */
698 oldval = p_var->val;
700 /* Check boundaries and list */
701 CheckValue( p_var, &val );
703 /* Set the variable */
704 p_var->val = val;
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 );
713 return VLC_SUCCESS;
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)
724 assert( p_this );
726 vlc_object_internals_t *p_priv = vlc_internals( p_this );
727 variable_t *p_var;
728 int err = VLC_SUCCESS;
730 p_var = Lookup( p_this, psz_name );
731 if( p_var != NULL )
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 */
738 *p_val = p_var->val;
740 /* Duplicate value if needed */
741 p_var->ops->pf_dup( p_val );
743 else
744 err = VLC_ENOVAR;
746 vlc_mutex_unlock( &p_priv->var_lock );
747 return err;
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 );
755 typedef enum
757 vlc_value_callback,
758 vlc_list_callback
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 )
764 variable_t *p_var;
766 assert( p_this );
768 vlc_object_internals_t *p_priv = vlc_internals( p_this );
770 p_var = Lookup( p_this, psz_name );
771 if( p_var == NULL )
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 );
776 return;
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;
784 else
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 )
804 int i_entry;
805 variable_t *p_var;
806 #ifndef NDEBUG
807 bool b_found_similar = false;
808 #endif
810 assert( p_this );
812 vlc_object_internals_t *p_priv = vlc_internals( p_this );
814 p_var = Lookup( p_this, psz_name );
815 if( p_var == NULL )
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 );
820 return;
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;
828 else
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 )
836 break;
838 #ifndef NDEBUG
839 else if( p_table->p_entries[i_entry].p_callback == entry.p_callback )
840 b_found_similar = true;
841 #endif
844 if( i_entry < 0 )
846 #ifndef NDEBUG
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();
851 #endif
852 vlc_mutex_unlock( &p_priv->var_lock );
853 return;
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 );
875 if( p_var != NULL )
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
908 * variable
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
914 * \return nothing
916 void var_OptionParse( vlc_object_t *p_obj, const char *psz_option,
917 bool trusted )
919 char *psz_name, *psz_value;
920 int i_type;
921 bool b_isno = false;
922 vlc_value_t val;
924 val.psz_string = NULL;
926 /* It's too much of a hassle to remove the ':' when we parse
927 * the cmd line :) */
928 if( psz_option[0] == ':' )
929 psz_option++;
931 if( !psz_option[0] )
932 return;
934 psz_name = strdup( psz_option );
935 if( psz_name == NULL )
936 return;
938 psz_value = strchr( psz_name, '=' );
939 if( psz_value != NULL )
940 *psz_value++ = '\0';
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 */
956 b_isno = true;
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 );
969 free( psz_name );
970 return;
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 );
978 switch( i_type )
980 case VLC_VAR_BOOL:
981 val.b_bool = !b_isno;
982 break;
984 case VLC_VAR_INTEGER:
985 val.i_int = strtoll( psz_value, NULL, 0 );
986 break;
988 case VLC_VAR_FLOAT:
989 val.f_float = us_atof( psz_value );
990 break;
992 case VLC_VAR_STRING:
993 val.psz_string = psz_value;
994 break;
996 default:
997 goto cleanup;
1000 var_Set( p_obj, psz_name, val );
1002 cleanup:
1003 free( psz_name );
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);
1025 free (buf);
1027 else
1028 ret = VLC_ENOMEM;
1029 mrl += len;
1032 return ret;
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 )
1042 return 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("");
1051 break;
1052 case VLC_VAR_FLOAT:
1053 p_val->f_float = config_GetFloat( psz_name );
1054 break;
1055 case VLC_VAR_INTEGER:
1056 p_val->i_int = config_GetInt( psz_name );
1057 break;
1058 case VLC_VAR_BOOL:
1059 p_val->b_bool = config_GetInt( psz_name ) > 0;
1060 break;
1061 default:
1062 vlc_assert_unreachable();
1063 case VLC_VAR_ADDRESS:
1064 return VLC_ENOOBJ;
1066 return VLC_SUCCESS;
1069 int (var_InheritURational)(vlc_object_t *object,
1070 unsigned *num, unsigned *den,
1071 const char *var)
1073 char *str = var_InheritString(object, var);
1074 if (str == NULL)
1075 goto error;
1077 char *sep;
1078 unsigned n = strtoul(str, &sep, 10);
1079 unsigned d;
1081 switch (*sep) {
1082 case '\0':
1083 /* Decimal integer */
1084 d = 1;
1085 break;
1087 case ':':
1088 case '/':
1089 /* Decimal fraction */
1090 d = strtoul(sep + 1, &sep, 10);
1091 if (*sep != '\0')
1092 goto error;
1093 break;
1095 case '.': {
1096 /* Decimal number */
1097 unsigned char c;
1099 d = 1;
1100 while ((c = *(++sep)) != '\0') {
1101 c -= '0';
1103 if (c >= 10)
1104 goto error;
1106 n = n * 10 + c;
1107 d *= 10;
1109 break;
1112 default:
1113 goto error;
1116 free(str);
1118 if (n == 0) {
1119 *num = 0;
1120 *den = d ? 1 : 0;
1121 } else if (d == 0) {
1122 *num = 1;
1123 *den = 0;
1124 } else
1125 vlc_ureduce(num, den, n, d, 0);
1127 return VLC_SUCCESS;
1129 error:
1130 free(str);
1131 *num = 0;
1132 *den = 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 );
1143 break;
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)
1164 return;
1165 (void) depth;
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);
1185 putchar(')');
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)
1195 case VLC_VAR_VOID:
1196 break;
1197 case VLC_VAR_BOOL:
1198 printf(": %s", var->val.b_bool ? "true" : "false");
1199 break;
1200 case VLC_VAR_INTEGER:
1201 printf(": %"PRId64, var->val.i_int );
1202 break;
1203 case VLC_VAR_STRING:
1204 printf(": \"%s\"", var->val.psz_string );
1205 break;
1206 case VLC_VAR_FLOAT:
1207 printf(": %f", var->val.f_float );
1208 break;
1209 case VLC_VAR_COORDS:
1210 printf(": %"PRId32"x%"PRId32,
1211 var->val.coords.x, var->val.coords.y);
1212 break;
1213 case VLC_VAR_ADDRESS:
1214 printf(": %p", var->val.p_address);
1215 break;
1217 putchar('\n');
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");
1225 else
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)
1235 return;
1236 (void) depth;
1238 const variable_t *var = *(const variable_t **)data;
1239 DECL_ARRAY(char *) *names = twalk_ctx;
1240 char *dup = strdup(var->psz_name);
1241 if (dup != NULL)
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;
1250 ARRAY_INIT(names);
1252 twalk_ctx = &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)
1258 return NULL;
1259 ARRAY_APPEND(names, NULL);
1260 return names.p_elems;