Fortunes about QT and "hearing" video codecs
[vlc.git] / src / misc / variables.c
blob0fff2c82d664b90f26c566decd1d2acaae693ff3
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 struct callback_entry_t *next;
49 union
51 vlc_callback_t pf_value_callback;
52 vlc_list_callback_t pf_list_callback;
53 void * p_callback;
55 void * p_data;
56 } callback_entry_t;
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 * );
63 } variable_ops_t;
65 /**
66 * The structure describing a variable.
67 * \note vlc_value_t is the common union for variable values
69 struct variable_t
71 char * psz_name; /**< The variable unique name (must be first) */
73 /** The variable's exported value */
74 vlc_value_t val;
76 /** The variable display name, mainly for use by the interfaces */
77 char * psz_text;
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 */
88 vlc_value_t *choices;
89 /** List of friendly names for the choices */
90 char **choices_text;
91 size_t choices_count;
93 /** Set to TRUE if the variable is in a callback */
94 bool b_incallback;
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 )
114 if( !v.psz_string )
115 return !w.psz_string ? 0 : -1;
116 else
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 );
152 variable_t **pp_var;
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);
181 free( p_var );
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))
202 if (val->i_int > 0)
203 val->i_int = (val->i_int + (var->step.i_int / 2))
204 / var->step.i_int * var->step.i_int;
205 else
206 val->i_int = (val->i_int - (var->step.i_int / 2))
207 / var->step.i_int * var->step.i_int;
209 break;
211 case VLC_VAR_FLOAT:
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);
219 break;
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);
233 vlc_cleanup_pop();
236 static void TriggerCallback(vlc_object_t *obj, variable_t *var,
237 const char *name, vlc_value_t prev)
239 assert(obj != NULL);
241 callback_entry_t *entry = var->value_callbacks;
242 if (entry == NULL)
243 return;
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);
254 entry = entry->next;
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)
266 assert(obj != NULL);
268 callback_entry_t *entry = var->list_callbacks;
269 if (entry == NULL)
270 return;
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);
281 entry = entry->next;
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 )
292 assert( p_this );
294 variable_t *p_var = calloc( 1, sizeof( *p_var ) );
295 if( p_var == NULL )
296 return VLC_ENOMEM;
298 p_var->psz_name = strdup( psz_name );
299 p_var->psz_text = NULL;
301 p_var->i_type = i_type & ~VLC_VAR_DOINHERIT;
303 p_var->i_usage = 1;
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 )
317 case VLC_VAR_BOOL:
318 p_var->ops = &bool_ops;
319 p_var->val.b_bool = false;
320 break;
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;
326 break;
327 case VLC_VAR_STRING:
328 p_var->ops = &string_ops;
329 p_var->val.psz_string = NULL;
330 break;
331 case VLC_VAR_FLOAT:
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;
336 break;
337 case VLC_VAR_COORDS:
338 p_var->ops = &coords_ops;
339 p_var->val.coords.x = p_var->val.coords.y = 0;
340 break;
341 case VLC_VAR_ADDRESS:
342 p_var->ops = &addr_ops;
343 p_var->val.p_address = NULL;
344 break;
345 case VLC_VAR_VOID:
346 p_var->ops = &void_ops;
347 break;
348 default:
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) )
363 ret = VLC_ENOMEM;
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);
369 p_oldvar->i_usage++;
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... */
375 if( p_var != NULL )
376 Destroy( p_var );
377 return ret;
380 void (var_Destroy)(vlc_object_t *p_this, const char *psz_name)
382 variable_t *p_var;
384 assert( p_this );
386 vlc_object_internals_t *p_priv = vlc_internals( p_this );
388 p_var = Lookup( p_this, psz_name );
389 if( p_var == NULL )
390 msg_Dbg( p_this, "attempt to destroy nonexistent variable \"%s\"",
391 psz_name );
392 else if( --p_var->i_usage == 0 )
394 assert(!p_var->b_incallback);
395 tdelete( p_var, &p_priv->var_root, varcmp );
397 else
399 assert(p_var->i_usage != -1u);
400 p_var = NULL;
402 vlc_mutex_unlock( &p_priv->var_lock );
404 if( p_var != NULL )
405 Destroy( p_var );
408 static void CleanupVar( void *var )
410 Destroy( 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, ...)
423 va_list ap;
424 int ret = VLC_SUCCESS;
425 variable_t *p_var;
426 vlc_value_t oldval;
427 vlc_value_t newval;
429 assert( p_this );
431 vlc_object_internals_t *p_priv = vlc_internals( p_this );
433 p_var = Lookup( p_this, psz_name );
434 if( p_var == NULL )
436 vlc_mutex_unlock( &p_priv->var_lock );
437 return VLC_ENOVAR;
440 va_start(ap, i_action);
441 switch( i_action )
443 case VLC_VAR_GETMIN:
444 *va_arg(ap, vlc_value_t *) = p_var->min;
445 break;
446 case VLC_VAR_GETMAX:
447 *va_arg(ap, vlc_value_t *) = p_var->max;
448 break;
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);
453 break;
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 );
458 break;
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)
464 ret = VLC_EGENERIC;
465 break;
466 case VLC_VAR_FLOAT:
467 if (p_var->step.f_float == 0.f)
468 ret = VLC_EGENERIC;
469 break;
470 default:
471 ret = VLC_EGENERIC;
473 if (ret == VLC_SUCCESS)
474 *va_arg(ap, vlc_value_t *) = p_var->step;
475 break;
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);
486 if (text != NULL)
487 p_var->choices_text[count - 1] = strdup(text);
489 TriggerListCallback(p_this, p_var, psz_name, VLC_VAR_ADDCHOICE,
490 &val);
491 break;
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)
500 break;
502 if (i == count)
503 { /* Not found */
504 ret = VLC_EGENERIC;
505 break;
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,
515 &val);
516 break;
518 case VLC_VAR_CHOICESCOUNT:
519 *va_arg(ap, size_t *) = p_var->choices_count;
520 break;
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);
531 break;
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 */
537 oldval = p_var->val;
538 /* Check boundaries and list */
539 CheckValue( p_var, &newval );
540 /* Set the variable */
541 p_var->val = newval;
542 /* Free data if needed */
543 p_var->ops->pf_free( &oldval );
544 break;
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);
561 if( texts != NULL )
563 char **tab = xmalloc(p_var->choices_count * sizeof (*tab));
564 *texts = 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;
570 break;
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;
578 break;
580 case VLC_VAR_GETTEXT:
581 *va_arg(ap, char **) = (p_var->psz_text != NULL)
582 ? strdup(p_var->psz_text) : NULL;
583 break;
584 default:
585 break;
587 va_end(ap);
588 vlc_mutex_unlock( &p_priv->var_lock );
589 return ret;
592 int (var_GetAndSet)(vlc_object_t *p_this, const char *psz_name, int i_action,
593 vlc_value_t *p_val)
595 variable_t *p_var;
596 vlc_value_t oldval;
598 assert( p_this );
599 assert( p_val );
601 vlc_object_internals_t *p_priv = vlc_internals( p_this );
603 p_var = Lookup( p_this, psz_name );
604 if( p_var == NULL )
606 vlc_mutex_unlock( &p_priv->var_lock );
607 return VLC_ENOVAR;
610 WaitUnused( p_this, p_var );
612 /* Duplicated data if needed */
613 //p_var->ops->pf_dup( &val );
615 /* Backup needed stuff */
616 oldval = p_var->val;
618 /* depending of the action requiered */
619 switch( i_action )
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;
624 break;
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;
628 break;
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;
632 break;
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;
636 break;
637 default:
638 vlc_mutex_unlock( &p_priv->var_lock );
639 return VLC_EGENERIC;
642 /* Check boundaries */
643 CheckValue( p_var, &p_var->val );
644 *p_val = p_var->val;
646 /* Deal with callbacks.*/
647 TriggerCallback( p_this, p_var, psz_name, oldval );
649 vlc_mutex_unlock( &p_priv->var_lock );
650 return VLC_SUCCESS;
653 int (var_Type)(vlc_object_t *p_this, const char *psz_name)
655 variable_t *p_var;
656 int i_type = 0;
658 assert( p_this );
660 vlc_object_internals_t *p_priv = vlc_internals( p_this );
662 p_var = Lookup( p_this, psz_name );
663 if( p_var != NULL )
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 );
671 return i_type;
674 int (var_SetChecked)(vlc_object_t *p_this, const char *psz_name,
675 int expected_type, vlc_value_t val)
677 variable_t *p_var;
678 vlc_value_t oldval;
680 assert( p_this );
682 vlc_object_internals_t *p_priv = vlc_internals( p_this );
684 p_var = Lookup( p_this, psz_name );
685 if( p_var == NULL )
687 vlc_mutex_unlock( &p_priv->var_lock );
688 return VLC_ENOVAR;
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 */
701 oldval = p_var->val;
703 /* Check boundaries and list */
704 CheckValue( p_var, &val );
706 /* Set the variable */
707 p_var->val = val;
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 );
716 return VLC_SUCCESS;
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)
727 assert( p_this );
729 vlc_object_internals_t *p_priv = vlc_internals( p_this );
730 variable_t *p_var;
731 int err = VLC_SUCCESS;
733 p_var = Lookup( p_this, psz_name );
734 if( p_var != NULL )
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 */
741 *p_val = p_var->val;
743 /* Duplicate value if needed */
744 p_var->ops->pf_dup( p_val );
746 else
747 err = VLC_ENOVAR;
749 vlc_mutex_unlock( &p_priv->var_lock );
750 return err;
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 );
758 typedef enum
760 vlc_value_callback,
761 vlc_list_callback
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 )
768 variable_t *p_var;
770 assert( p_this );
772 vlc_object_internals_t *p_priv = vlc_internals( p_this );
774 p_var = Lookup( p_this, psz_name );
775 if( p_var == NULL )
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 );
780 return;
783 WaitUnused( p_this, p_var );
785 callback_entry_t **pp;
787 if (i_type == vlc_value_callback)
788 pp = &p_var->value_callbacks;
789 else
790 pp = &p_var->list_callbacks;
792 entry->next = *pp;
793 *pp = entry;
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;
813 variable_t *p_var;
815 assert( p_this );
817 vlc_object_internals_t *p_priv = vlc_internals( p_this );
819 p_var = Lookup( p_this, psz_name );
820 if( p_var == NULL )
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 );
825 return;
828 WaitUnused( p_this, p_var );
830 if (i_type == vlc_value_callback)
831 pp = &p_var->value_callbacks;
832 else
833 pp = &p_var->list_callbacks;
835 entry = *pp;
836 assert(entry != NULL);
838 while (entry->p_callback != match->p_callback
839 || entry->p_data != match->p_data)
841 pp = &entry->next;
842 entry = *pp;
843 assert(entry != NULL);
846 *pp = entry->next;
847 vlc_mutex_unlock( &p_priv->var_lock );
848 free(entry);
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 );
865 if( p_var != NULL )
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
898 * variable
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
904 * \return nothing
906 void var_OptionParse( vlc_object_t *p_obj, const char *psz_option,
907 bool trusted )
909 char *psz_name, *psz_value;
910 int i_type;
911 bool b_isno = false;
912 vlc_value_t val;
914 val.psz_string = NULL;
916 /* It's too much of a hassle to remove the ':' when we parse
917 * the cmd line :) */
918 if( psz_option[0] == ':' )
919 psz_option++;
921 if( !psz_option[0] )
922 return;
924 psz_name = strdup( psz_option );
925 if( psz_name == NULL )
926 return;
928 psz_value = strchr( psz_name, '=' );
929 if( psz_value != NULL )
930 *psz_value++ = '\0';
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 */
946 b_isno = true;
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 );
959 free( psz_name );
960 return;
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 );
968 switch( i_type )
970 case VLC_VAR_BOOL:
971 if( psz_value )
973 char *endptr;
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;
978 else
979 val.b_bool = value != 0;
981 else
982 val.b_bool = !b_isno;
983 break;
985 case VLC_VAR_INTEGER:
986 val.i_int = strtoll( psz_value, NULL, 0 );
987 break;
989 case VLC_VAR_FLOAT:
990 val.f_float = us_atof( psz_value );
991 break;
993 case VLC_VAR_STRING:
994 val.psz_string = psz_value;
995 break;
997 default:
998 goto cleanup;
1001 var_Set( p_obj, psz_name, val );
1003 cleanup:
1004 free( psz_name );
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);
1026 free (buf);
1028 else
1029 ret = VLC_ENOMEM;
1030 mrl += len;
1033 return ret;
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 )
1043 return 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("");
1052 break;
1053 case VLC_VAR_FLOAT:
1054 p_val->f_float = config_GetFloat( psz_name );
1055 break;
1056 case VLC_VAR_INTEGER:
1057 p_val->i_int = config_GetInt( psz_name );
1058 break;
1059 case VLC_VAR_BOOL:
1060 p_val->b_bool = config_GetInt( psz_name ) > 0;
1061 break;
1062 default:
1063 vlc_assert_unreachable();
1064 case VLC_VAR_ADDRESS:
1065 return VLC_ENOOBJ;
1067 return VLC_SUCCESS;
1070 int (var_InheritURational)(vlc_object_t *object,
1071 unsigned *num, unsigned *den,
1072 const char *var)
1074 char *str = var_InheritString(object, var);
1075 if (str == NULL)
1076 goto error;
1078 char *sep;
1079 unsigned n = strtoul(str, &sep, 10);
1080 unsigned d;
1082 switch (*sep) {
1083 case '\0':
1084 /* Decimal integer */
1085 d = 1;
1086 break;
1088 case ':':
1089 case '/':
1090 /* Decimal fraction */
1091 d = strtoul(sep + 1, &sep, 10);
1092 if (*sep != '\0')
1093 goto error;
1094 break;
1096 case '.': {
1097 /* Decimal number */
1098 unsigned char c;
1100 d = 1;
1101 while ((c = *(++sep)) != '\0') {
1102 c -= '0';
1104 if (c >= 10)
1105 goto error;
1107 n = n * 10 + c;
1108 d *= 10;
1110 break;
1113 default:
1114 goto error;
1117 free(str);
1119 if (n == 0) {
1120 *num = 0;
1121 *den = d ? 1 : 0;
1122 } else if (d == 0) {
1123 *num = 1;
1124 *den = 0;
1125 } else
1126 vlc_ureduce(num, den, n, d, 0);
1128 return VLC_SUCCESS;
1130 error:
1131 free(str);
1132 *num = 0;
1133 *den = 0;
1134 return VLC_EGENERIC;
1137 static void DumpVariable(const void *data, const VISIT which, const int depth)
1139 if (which != postorder && which != leaf)
1140 return;
1141 (void) depth;
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);
1161 putchar(')');
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)
1168 size_t count = 0;
1170 for (callback_entry_t *entry = var->value_callbacks;
1171 entry != NULL;
1172 entry = entry->next)
1173 count++;
1175 printf(", %zu callbacks", count);
1178 switch (var->i_type & VLC_VAR_CLASS)
1180 case VLC_VAR_VOID:
1181 break;
1182 case VLC_VAR_BOOL:
1183 printf(": %s", var->val.b_bool ? "true" : "false");
1184 break;
1185 case VLC_VAR_INTEGER:
1186 printf(": %"PRId64, var->val.i_int );
1187 break;
1188 case VLC_VAR_STRING:
1189 printf(": \"%s\"", var->val.psz_string );
1190 break;
1191 case VLC_VAR_FLOAT:
1192 printf(": %f", var->val.f_float );
1193 break;
1194 case VLC_VAR_COORDS:
1195 printf(": %"PRId32"x%"PRId32,
1196 var->val.coords.x, var->val.coords.y);
1197 break;
1198 case VLC_VAR_ADDRESS:
1199 printf(": %p", var->val.p_address);
1200 break;
1202 putchar('\n');
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");
1210 else
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)
1220 return;
1221 (void) depth;
1223 const variable_t *var = *(const variable_t **)data;
1224 DECL_ARRAY(char *) *names = twalk_ctx;
1225 char *dup = strdup(var->psz_name);
1226 if (dup != NULL)
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;
1235 ARRAY_INIT(names);
1237 twalk_ctx = &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)
1243 return NULL;
1244 ARRAY_APPEND(names, NULL);
1245 return names.p_elems;