variables.c 41.5 KB
Newer Older
1 2 3
/*****************************************************************************
 * variables.c: routines for object variables handling
 *****************************************************************************
4
 * Copyright (C) 2002-2009 the VideoLAN team
5
 * $Id$
6 7 8 9 10 11 12
 *
 * Authors: Samuel Hocevar <sam@zoy.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
13
 *
14 15 16 17 18 19 20
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
Antoine Cellerier's avatar
Antoine Cellerier committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 23 24 25 26
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
27 28 29 30
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

31
#include <vlc_common.h>
32
#include <vlc_charset.h>
Clément Stenac's avatar
Clément Stenac committed
33
#include "variables.h"
34

35
#include "libvlc.h"
36

37
#include <search.h>
38
#include <assert.h>
39 40
#include <math.h>
#include <limits.h>
41

42 43 44 45 46 47 48 49 50
/*****************************************************************************
 * Private types
 *****************************************************************************/
struct callback_entry_t
{
    vlc_callback_t pf_callback;
    void *         p_data;
};

51 52 53
/*****************************************************************************
 * Local comparison functions, returns 0 if v == w, < 0 if v < w, > 0 if v > w
 *****************************************************************************/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
54 55 56 57 58 59 60 61 62 63
static int CmpBool( vlc_value_t v, vlc_value_t w )
{
    return v.b_bool ? w.b_bool ? 0 : 1 : w.b_bool ? -1 : 0;
}

static int CmpInt( vlc_value_t v, vlc_value_t w )
{
    return v.i_int == w.i_int ? 0 : v.i_int > w.i_int ? 1 : -1;
}

64 65
static int CmpTime( vlc_value_t v, vlc_value_t w )
{
66
    return v.i_time == w.i_time ? 0 : v.i_time > w.i_time ? 1 : -1;
67
}
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
68

69 70 71 72 73 74 75
static int CmpString( vlc_value_t v, vlc_value_t w )
{
    if( !v.psz_string )
        return !w.psz_string ? 0 : -1;
    else
        return !w.psz_string ? 1 : strcmp( v.psz_string, w.psz_string );
}
76 77 78 79 80 81
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; }
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; }

/*****************************************************************************
 * Local duplication functions, and local deallocation functions
 *****************************************************************************/
82
static void DupDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
83 84 85 86
static void DupString( vlc_value_t *p_val )
{
    p_val->psz_string = strdup( p_val->psz_string ? p_val->psz_string :  "" );
}
87

88
static void FreeDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
89
static void FreeString( vlc_value_t *p_val ) { free( p_val->psz_string ); }
Gildas Bazin's avatar
 
Gildas Bazin committed
90
static void FreeMutex( vlc_value_t *p_val ) { vlc_mutex_destroy( (vlc_mutex_t*)p_val->p_address ); free( p_val->p_address ); }
91

Gildas Bazin's avatar
 
Gildas Bazin committed
92 93 94 95 96
static void FreeList( vlc_value_t *p_val )
{
    int i;
    for( i = 0; i < p_val->p_list->i_count; i++ )
    {
97
        switch( p_val->p_list->pi_types[i] & VLC_VAR_CLASS )
Gildas Bazin's avatar
 
Gildas Bazin committed
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
        {
        case VLC_VAR_STRING:
            FreeString( &p_val->p_list->p_values[i] );
            break;
        case VLC_VAR_MUTEX:
            FreeMutex( &p_val->p_list->p_values[i] );
            break;
        default:
            break;
        }
    }

    if( p_val->p_list->i_count )
    {
        free( p_val->p_list->p_values );
        free( p_val->p_list->pi_types );
    }
    free( p_val->p_list );
}

118
static const struct variable_ops_t
119
void_ops   = { NULL,       DupDummy,  FreeDummy,  },
120 121 122 123 124 125
addr_ops   = { CmpAddress, DupDummy,  FreeDummy,  },
bool_ops   = { CmpBool,    DupDummy,  FreeDummy,  },
float_ops  = { CmpFloat,   DupDummy,  FreeDummy,  },
int_ops    = { CmpInt,     DupDummy,  FreeDummy,  },
mutex_ops  = { CmpAddress, DupDummy,  FreeMutex,  },
string_ops = { CmpString,  DupString, FreeString, },
126 127
time_ops   = { CmpTime,    DupDummy,  FreeDummy,  },
coords_ops = { NULL,       DupDummy,  FreeDummy,  };
128

129 130 131
/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
132
static void     WaitUnused  ( vlc_object_t *, variable_t * );
133

134
static void     CheckValue  ( variable_t *, vlc_value_t * );
135

136
static int      TriggerCallback( vlc_object_t *, variable_t *, const char *,
137
                                 vlc_value_t );
Gildas Bazin's avatar
 
Gildas Bazin committed
138

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
static int varcmp( const void *a, const void *b )
{
    const variable_t *va = a, *vb = b;

    /* psz_name must be first */
    assert( va == (const void *)&va->psz_name );
    return strcmp( va->psz_name, vb->psz_name );
}

static variable_t *Lookup( vlc_object_t *obj, const char *psz_name )
{
    vlc_object_internals_t *priv = vlc_internals( obj );
    variable_t **pp_var;

    vlc_assert_locked( &priv->var_lock );
    pp_var = tfind( &psz_name, &priv->var_root, varcmp );
    return (pp_var != NULL) ? *pp_var : NULL;
}

158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
static void Destroy( variable_t *p_var )
{
    p_var->ops->pf_free( &p_var->val );
    if( p_var->choices.i_count )
    {
        for( int i = 0 ; i < p_var->choices.i_count ; i++ )
        {
            p_var->ops->pf_free( &p_var->choices.p_values[i] );
            free( p_var->choices_text.p_values[i].psz_string );
        }
        free( p_var->choices.p_values );
        free( p_var->choices_text.p_values );
    }
    free( p_var->psz_name );
    free( p_var->psz_text );
    free( p_var->p_entries );
    free( p_var );
}

177
#undef var_Create
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
178 179 180
/**
 * Initialize a vlc variable
 *
181 182 183
 * We hash the given string and insert it into the sorted list. The insertion
 * may require slow memory copies, but think about what we gain in the log(n)
 * lookup phase when setting/getting the variable value!
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
184 185 186 187 188 189
 *
 * \param p_this The object in which to create the variable
 * \param psz_name The name of the variable
 * \param i_type The variables type. Must be one of \ref var_type combined with
 *               zero or more \ref var_flags
 */
190
int var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
191
{
192 193
    assert( p_this );

194 195 196
    variable_t *p_var = calloc( 1, sizeof( *p_var ) );
    if( p_var == NULL )
        return VLC_ENOMEM;
197 198

    p_var->psz_name = strdup( psz_name );
Gildas Bazin's avatar
 
Gildas Bazin committed
199
    p_var->psz_text = NULL;
200

Gildas Bazin's avatar
 
Gildas Bazin committed
201
    p_var->i_type = i_type & ~VLC_VAR_DOINHERIT;
202

203
    p_var->i_usage = 1;
204

205
    p_var->i_default = -1;
Gildas Bazin's avatar
 
Gildas Bazin committed
206 207
    p_var->choices.i_count = 0;
    p_var->choices.p_values = NULL;
Gildas Bazin's avatar
 
Gildas Bazin committed
208 209
    p_var->choices_text.i_count = 0;
    p_var->choices_text.p_values = NULL;
210

211
    p_var->b_incallback = false;
212 213 214
    p_var->i_entries = 0;
    p_var->p_entries = NULL;

215 216 217
    /* Always initialize the variable, even if it is a list variable; this
     * will lead to errors if the variable is not initialized, but it will
     * not cause crashes in the variable handling. */
218
    switch( i_type & VLC_VAR_CLASS )
219
    {
220
        case VLC_VAR_BOOL:
221
            p_var->ops = &bool_ops;
222
            p_var->val.b_bool = false;
223 224
            break;
        case VLC_VAR_INTEGER:
225
            p_var->ops = &int_ops;
226 227 228
            p_var->val.i_int = 0;
            break;
        case VLC_VAR_STRING:
229
            p_var->ops = &string_ops;
230
            p_var->val.psz_string = NULL;
231 232
            break;
        case VLC_VAR_FLOAT:
233
            p_var->ops = &float_ops;
234 235 236
            p_var->val.f_float = 0.0;
            break;
        case VLC_VAR_TIME:
237
            p_var->ops = &time_ops;
238
            p_var->val.i_time = 0;
239
            break;
240 241 242 243
        case VLC_VAR_COORDS:
            p_var->ops = &coords_ops;
            p_var->val.coords.x = p_var->val.coords.y = 0;
            break;
244
        case VLC_VAR_ADDRESS:
245
            p_var->ops = &addr_ops;
246 247
            p_var->val.p_address = NULL;
            break;
248
        case VLC_VAR_MUTEX:
249
            p_var->ops = &mutex_ops;
250
            p_var->val.p_address = malloc( sizeof(vlc_mutex_t) );
251
            vlc_mutex_init( (vlc_mutex_t*)p_var->val.p_address );
252
            break;
253 254
        default:
            p_var->ops = &void_ops;
255
#ifndef NDEBUG
256
            if( (i_type & VLC_VAR_CLASS) != VLC_VAR_VOID )
257 258 259
                msg_Err( p_this, "Creating the variable '%s' without a type",
                          psz_name );
#endif
260
    }
261

Gildas Bazin's avatar
 
Gildas Bazin committed
262 263
    if( i_type & VLC_VAR_DOINHERIT )
    {
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
264
        if( var_Inherit( p_this, psz_name, i_type, &p_var->val ) )
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
            msg_Err( p_this, "cannot inherit value for %s", psz_name );
        else if( i_type & VLC_VAR_HASCHOICE )
        {
            /* We must add the inherited value to our choice list */
            p_var->i_default = 0;

            INSERT_ELEM( p_var->choices.p_values, p_var->choices.i_count,
                         0, p_var->val );
            INSERT_ELEM( p_var->choices_text.p_values,
                         p_var->choices_text.i_count, 0, p_var->val );
            p_var->ops->pf_dup( &p_var->choices.p_values[0] );
            p_var->choices_text.p_values[0].psz_string = NULL;
        }
    }

    vlc_object_internals_t *p_priv = vlc_internals( p_this );
281 282
    variable_t **pp_var, *p_oldvar;
    int ret = VLC_SUCCESS;
283 284 285

    vlc_mutex_lock( &p_priv->var_lock );

286 287 288 289 290 291 292 293 294 295 296 297 298
    pp_var = tsearch( p_var, &p_priv->var_root, varcmp );
    if( unlikely(pp_var == NULL) )
        ret = VLC_ENOMEM;
    else if( (p_oldvar = *pp_var) == p_var )
        p_var = NULL;
    else if( unlikely((i_type ^ p_oldvar->i_type) & VLC_VAR_CLASS) )
    {    /* If the types differ, variable creation failed. */
         msg_Err( p_this, "Variable '%s' (0x%04x) already exist "
                  "but with a different type (0x%04x)",
                  psz_name, p_oldvar->i_type, i_type );
         ret = VLC_EBADVAR;
    }
    else
299
    {
300 301
        p_oldvar->i_usage++;
        p_oldvar->i_type |= i_type & (VLC_VAR_ISCOMMAND|VLC_VAR_HASCHOICE);
302
    }
303
    vlc_mutex_unlock( &p_priv->var_lock );
304

305 306 307 308
    /* If we did not need to create a new variable, free everything... */
    if( p_var != NULL )
        Destroy( p_var );
    return ret;
309 310
}

311
#undef var_Destroy
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
312 313 314
/**
 * Destroy a vlc variable
 *
315 316
 * Look for the variable and destroy it if it is found. As in var_Create we
 * do a call to memmove() but we have performance counterparts elsewhere.
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
317 318 319 320
 *
 * \param p_this The object that holds the variable
 * \param psz_name The name of the variable
 */
321
int var_Destroy( vlc_object_t *p_this, const char *psz_name )
322
{
323
    variable_t *p_var;
324 325 326

    assert( p_this );

327
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
328

329
    vlc_mutex_lock( &p_priv->var_lock );
330

331 332
    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
333
    {
334
        vlc_mutex_unlock( &p_priv->var_lock );
335
        return VLC_ENOVAR;
336 337
    }

338
    WaitUnused( p_this, p_var );
339

340 341
    if( --p_var->i_usage == 0 )
        tdelete( p_var, &p_priv->var_root, varcmp );
342
    else
343
        p_var = NULL;
344
    vlc_mutex_unlock( &p_priv->var_lock );
345

346 347
    if( p_var != NULL )
        Destroy( p_var );
348 349 350
    return VLC_SUCCESS;
}

351 352 353 354 355 356 357 358 359 360 361 362
static void CleanupVar( void *var )
{
    Destroy( var );
}

void var_DestroyAll( vlc_object_t *obj )
{
    vlc_object_internals_t *priv = vlc_internals( obj );

    tdestroy( priv->var_root, CleanupVar );
}

363
#undef var_Change
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
364 365
/**
 * Perform an action on a variable
366
 *
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
367 368 369 370 371 372
 * \param p_this The object that holds the variable
 * \param psz_name The name of the variable
 * \param i_action The action to perform. Must be one of \ref var_action
 * \param p_val First action parameter
 * \param p_val2 Second action parameter
 */
373 374
int var_Change( vlc_object_t *p_this, const char *psz_name,
                int i_action, vlc_value_t *p_val, vlc_value_t *p_val2 )
375
{
376
    int i;
377
    variable_t *p_var;
Gildas Bazin's avatar
 
Gildas Bazin committed
378
    vlc_value_t oldval;
Erwan Tulou's avatar
Erwan Tulou committed
379
    vlc_value_t newval;
380 381 382

    assert( p_this );

383
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
384

385
    vlc_mutex_lock( &p_priv->var_lock );
386

387 388
    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
389
    {
390
        vlc_mutex_unlock( &p_priv->var_lock );
391 392 393 394 395 396
        return VLC_ENOVAR;
    }

    switch( i_action )
    {
        case VLC_VAR_SETMIN:
397 398
            if( p_var->i_type & VLC_VAR_HASMIN )
            {
399
                p_var->ops->pf_free( &p_var->min );
400 401
            }
            p_var->i_type |= VLC_VAR_HASMIN;
402
            p_var->min = *p_val;
403
            p_var->ops->pf_dup( &p_var->min );
404 405
            CheckValue( p_var, &p_var->val );
            break;
406 407 408 409 410 411
        case VLC_VAR_GETMIN:
            if( p_var->i_type & VLC_VAR_HASMIN )
            {
                *p_val = p_var->min;
            }
            break;
412
        case VLC_VAR_SETMAX:
413 414
            if( p_var->i_type & VLC_VAR_HASMAX )
            {
415
                p_var->ops->pf_free( &p_var->max );
416 417
            }
            p_var->i_type |= VLC_VAR_HASMAX;
418
            p_var->max = *p_val;
419
            p_var->ops->pf_dup( &p_var->max );
420 421
            CheckValue( p_var, &p_var->val );
            break;
422 423 424 425 426 427
        case VLC_VAR_GETMAX:
            if( p_var->i_type & VLC_VAR_HASMAX )
            {
                *p_val = p_var->max;
            }
            break;
428
        case VLC_VAR_SETSTEP:
429 430
            if( p_var->i_type & VLC_VAR_HASSTEP )
            {
431
                p_var->ops->pf_free( &p_var->step );
432 433
            }
            p_var->i_type |= VLC_VAR_HASSTEP;
434
            p_var->step = *p_val;
435
            p_var->ops->pf_dup( &p_var->step );
436 437
            CheckValue( p_var, &p_var->val );
            break;
438 439 440 441 442 443
        case VLC_VAR_GETSTEP:
            if( p_var->i_type & VLC_VAR_HASSTEP )
            {
                *p_val = p_var->step;
            }
            break;
444
        case VLC_VAR_ADDCHOICE:
445
            i = p_var->choices.i_count;
446

Gildas Bazin's avatar
 
Gildas Bazin committed
447 448
            INSERT_ELEM( p_var->choices.p_values, p_var->choices.i_count,
                         i, *p_val );
Gildas Bazin's avatar
 
Gildas Bazin committed
449
            INSERT_ELEM( p_var->choices_text.p_values,
Gildas Bazin's avatar
 
Gildas Bazin committed
450
                         p_var->choices_text.i_count, i, *p_val );
451
            p_var->ops->pf_dup( &p_var->choices.p_values[i] );
Gildas Bazin's avatar
 
Gildas Bazin committed
452 453 454
            p_var->choices_text.p_values[i].psz_string =
                ( p_val2 && p_val2->psz_string ) ?
                strdup( p_val2->psz_string ) : NULL;
455

456 457 458
            CheckValue( p_var, &p_var->val );
            break;
        case VLC_VAR_DELCHOICE:
Gildas Bazin's avatar
 
Gildas Bazin committed
459
            for( i = 0 ; i < p_var->choices.i_count ; i++ )
460
            {
461
                if( p_var->ops->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
462 463 464 465 466
                {
                    break;
                }
            }

Gildas Bazin's avatar
 
Gildas Bazin committed
467
            if( i == p_var->choices.i_count )
468 469
            {
                /* Not found */
470
                vlc_mutex_unlock( &p_priv->var_lock );
471 472 473 474 475 476 477 478 479 480 481 482
                return VLC_EGENERIC;
            }

            if( p_var->i_default > i )
            {
                p_var->i_default--;
            }
            else if( p_var->i_default == i )
            {
                p_var->i_default = -1;
            }

483
            p_var->ops->pf_free( &p_var->choices.p_values[i] );
484
            free( p_var->choices_text.p_values[i].psz_string );
Gildas Bazin's avatar
 
Gildas Bazin committed
485
            REMOVE_ELEM( p_var->choices.p_values, p_var->choices.i_count, i );
Gildas Bazin's avatar
 
Gildas Bazin committed
486 487
            REMOVE_ELEM( p_var->choices_text.p_values,
                         p_var->choices_text.i_count, i );
488 489 490

            CheckValue( p_var, &p_var->val );
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
491 492 493
        case VLC_VAR_CHOICESCOUNT:
            p_val->i_int = p_var->choices.i_count;
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
494 495 496
        case VLC_VAR_CLEARCHOICES:
            for( i = 0 ; i < p_var->choices.i_count ; i++ )
            {
497
                p_var->ops->pf_free( &p_var->choices.p_values[i] );
Gildas Bazin's avatar
 
Gildas Bazin committed
498
            }
499
            for( i = 0 ; i < p_var->choices_text.i_count ; i++ )
500 501
                free( p_var->choices_text.p_values[i].psz_string );

502 503
            if( p_var->choices.i_count ) free( p_var->choices.p_values );
            if( p_var->choices_text.i_count ) free( p_var->choices_text.p_values );
Gildas Bazin's avatar
 
Gildas Bazin committed
504 505 506

            p_var->choices.i_count = 0;
            p_var->choices.p_values = NULL;
507 508
            p_var->choices_text.i_count = 0;
            p_var->choices_text.p_values = NULL;
Gildas Bazin's avatar
 
Gildas Bazin committed
509 510
            p_var->i_default = -1;
            break;
511 512
        case VLC_VAR_SETDEFAULT:
            /* FIXME: the list is sorted, dude. Use something cleverer. */
Gildas Bazin's avatar
 
Gildas Bazin committed
513
            for( i = 0 ; i < p_var->choices.i_count ; i++ )
514
            {
515
                if( p_var->ops->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
516 517 518 519 520
                {
                    break;
                }
            }

Gildas Bazin's avatar
 
Gildas Bazin committed
521
            if( i == p_var->choices.i_count )
522 523 524 525 526 527
            {
                /* Not found */
                break;
            }

            p_var->i_default = i;
528 529
            CheckValue( p_var, &p_var->val );
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
530 531
        case VLC_VAR_SETVALUE:
            /* Duplicate data if needed */
Erwan Tulou's avatar
Erwan Tulou committed
532 533
            newval = *p_val;
            p_var->ops->pf_dup( &newval );
Gildas Bazin's avatar
 
Gildas Bazin committed
534 535 536
            /* Backup needed stuff */
            oldval = p_var->val;
            /* Check boundaries and list */
Erwan Tulou's avatar
Erwan Tulou committed
537
            CheckValue( p_var, &newval );
Gildas Bazin's avatar
 
Gildas Bazin committed
538
            /* Set the variable */
Erwan Tulou's avatar
Erwan Tulou committed
539
            p_var->val = newval;
Gildas Bazin's avatar
 
Gildas Bazin committed
540
            /* Free data if needed */
541
            p_var->ops->pf_free( &oldval );
Gildas Bazin's avatar
 
Gildas Bazin committed
542
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
543
        case VLC_VAR_GETCHOICES:
544
        case VLC_VAR_GETLIST:
Gildas Bazin's avatar
 
Gildas Bazin committed
545
            p_val->p_list = malloc( sizeof(vlc_list_t) );
Gildas Bazin's avatar
 
Gildas Bazin committed
546
            if( p_val2 ) p_val2->p_list = malloc( sizeof(vlc_list_t) );
Gildas Bazin's avatar
 
Gildas Bazin committed
547
            if( p_var->choices.i_count )
Gildas Bazin's avatar
 
Gildas Bazin committed
548
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
549 550
                p_val->p_list->p_values = malloc( p_var->choices.i_count
                                                  * sizeof(vlc_value_t) );
Gildas Bazin's avatar
 
Gildas Bazin committed
551 552 553 554 555 556 557 558 559 560
                p_val->p_list->pi_types = malloc( p_var->choices.i_count
                                                  * sizeof(int) );
                if( p_val2 )
                {
                    p_val2->p_list->p_values =
                        malloc( p_var->choices.i_count * sizeof(vlc_value_t) );
                    p_val2->p_list->pi_types =
                        malloc( p_var->choices.i_count * sizeof(int) );
                }
            }
Gildas Bazin's avatar
 
Gildas Bazin committed
561
            p_val->p_list->i_count = p_var->choices.i_count;
Gildas Bazin's avatar
 
Gildas Bazin committed
562
            if( p_val2 ) p_val2->p_list->i_count = p_var->choices.i_count;
Gildas Bazin's avatar
 
Gildas Bazin committed
563
            for( i = 0 ; i < p_var->choices.i_count ; i++ )
564
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
565
                p_val->p_list->p_values[i] = p_var->choices.p_values[i];
Gildas Bazin's avatar
 
Gildas Bazin committed
566
                p_val->p_list->pi_types[i] = p_var->i_type;
567
                p_var->ops->pf_dup( &p_val->p_list->p_values[i] );
Gildas Bazin's avatar
 
Gildas Bazin committed
568 569 570 571 572 573 574
                if( p_val2 )
                {
                    p_val2->p_list->p_values[i].psz_string =
                        p_var->choices_text.p_values[i].psz_string ?
                    strdup(p_var->choices_text.p_values[i].psz_string) : NULL;
                    p_val2->p_list->pi_types[i] = VLC_VAR_STRING;
                }
575 576
            }
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
577
        case VLC_VAR_SETTEXT:
578
            free( p_var->psz_text );
Gildas Bazin's avatar
 
Gildas Bazin committed
579 580
            if( p_val && p_val->psz_string )
                p_var->psz_text = strdup( p_val->psz_string );
581
            else
Rémi Duraffort's avatar
Rémi Duraffort committed
582
                p_var->psz_text = NULL;
Gildas Bazin's avatar
 
Gildas Bazin committed
583 584
            break;
        case VLC_VAR_GETTEXT:
585 586
            p_val->psz_string = p_var->psz_text ? strdup( p_var->psz_text )
                                                : NULL;
587
            break;
588 589 590 591
        case VLC_VAR_SETISCOMMAND:
            p_var->i_type |= VLC_VAR_ISCOMMAND;
            break;

592 593 594 595
        default:
            break;
    }

596
    vlc_mutex_unlock( &p_priv->var_lock );
597 598 599 600

    return VLC_SUCCESS;
}

601
#undef var_GetAndSet
602 603 604 605 606 607 608 609 610
/**
 * Perform a Get and Set on a variable
 *
 * \param p_this: The object that hold the variable
 * \param psz_name: the name of the variable
 * \param i_action: the action to perform
 * \param p_val: The action parameter
 * \return vlc error codes
 */
611
int var_GetAndSet( vlc_object_t *p_this, const char *psz_name, int i_action,
612
                   vlc_value_t *p_val )
613
{
614
    int i_ret;
615 616
    variable_t *p_var;
    vlc_value_t oldval;
617 618

    assert( p_this );
619
    assert( p_val );
620

621 622 623
    vlc_object_internals_t *p_priv = vlc_internals( p_this );

    vlc_mutex_lock( &p_priv->var_lock );
624 625
    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
626 627
    {
        vlc_mutex_unlock( &p_priv->var_lock );
628
        return VLC_ENOVAR;
629 630
    }

631
    WaitUnused( p_this, p_var );
632 633 634 635 636 637 638 639 640 641

    /* Duplicated data if needed */
    //p_var->ops->pf_dup( &val );

    /* Backup needed stuff */
    oldval = p_var->val;

    /* depending of the action requiered */
    switch( i_action )
    {
642
    case VLC_VAR_BOOL_TOGGLE:
643 644 645
        assert( ( p_var->i_type & VLC_VAR_BOOL ) == VLC_VAR_BOOL );
        p_var->val.b_bool = !p_var->val.b_bool;
        break;
646
    case VLC_VAR_INTEGER_ADD:
647
        assert( ( p_var->i_type & VLC_VAR_INTEGER ) == VLC_VAR_INTEGER );
648 649 650 651 652 653 654 655 656
        p_var->val.i_int += p_val->i_int;
        break;
    case VLC_VAR_INTEGER_OR:
        assert( ( p_var->i_type & VLC_VAR_INTEGER ) == VLC_VAR_INTEGER );
        p_var->val.i_int |= p_val->i_int;
        break;
    case VLC_VAR_INTEGER_NAND:
        assert( ( p_var->i_type & VLC_VAR_INTEGER ) == VLC_VAR_INTEGER );
        p_var->val.i_int &= ~p_val->i_int;
657
        break;
658 659 660 661 662 663 664
    default:
        vlc_mutex_unlock( &p_priv->var_lock );
        return VLC_EGENERIC;
    }

    /*  Check boundaries */
    CheckValue( p_var, &p_var->val );
665
    *p_val = p_var->val;
666

667
    /* Deal with callbacks.*/
668
    i_ret = TriggerCallback( p_this, p_var, psz_name, oldval );
669 670 671 672 673 674

    vlc_mutex_unlock( &p_priv->var_lock );

    return i_ret;
}

675
#undef var_Type
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
676 677 678 679
/**
 * Request a variable's type
 *
 * \return The variable type if it exists, or 0 if the
680
 * variable could not be found.
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
681 682
 * \see \ref var_type
 */
683
int var_Type( vlc_object_t *p_this, const char *psz_name )
684
{
685 686
    variable_t *p_var;
    int i_type = 0;
687 688 689

    assert( p_this );

690
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
691

692
    vlc_mutex_lock( &p_priv->var_lock );
693

694 695 696
    p_var = Lookup( p_this, psz_name );
    if( p_var != NULL )
        i_type = p_var->i_type;
697

698
    vlc_mutex_unlock( &p_priv->var_lock );
699 700

    return i_type;
701 702
}

703
#undef var_SetChecked
704 705
int var_SetChecked( vlc_object_t *p_this, const char *psz_name,
                    int expected_type, vlc_value_t val )
706
{
707
    int i_ret = VLC_SUCCESS;
708 709
    variable_t *p_var;
    vlc_value_t oldval;
710 711 712

    assert( p_this );

713
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
714

715
    vlc_mutex_lock( &p_priv->var_lock );
716

717 718
    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
719
    {
720
        vlc_mutex_unlock( &p_priv->var_lock );
721
        return VLC_ENOVAR;
722 723
    }

724
    assert( expected_type == 0 ||
725
            (p_var->i_type & VLC_VAR_CLASS) == expected_type );
726 727 728 729 730 731
#ifndef NDEBUG
        /* Alert if the type is VLC_VAR_VOID */
        if( ( p_var->i_type & VLC_VAR_TYPE ) == VLC_VAR_VOID )
            msg_Warn( p_this, "Calling var_Set on the void variable '%s' (0x%04x)", psz_name, p_var->i_type );
#endif

732

733 734
    WaitUnused( p_this, p_var );

735
    /* Duplicate data if needed */
736
    p_var->ops->pf_dup( &val );
737

738 739
    /* Backup needed stuff */
    oldval = p_var->val;
740

741
    /* Check boundaries and list */
742 743
    CheckValue( p_var, &val );

744 745 746
    /* Set the variable */
    p_var->val = val;

747
    /* Deal with callbacks */
748
    i_ret = TriggerCallback( p_this, p_var, psz_name, oldval );
749 750

    /* Free data if needed */
751
    p_var->ops->pf_free( &oldval );
752

753
    vlc_mutex_unlock( &p_priv->var_lock );
754

755
    return i_ret;
756 757
}

758
#undef var_Set
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
759
/**
760
 * Set a variable's value
761
 *
762
 * \param p_this The object that hold the variable
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
763
 * \param psz_name The name of the variable
764
 * \param val the value to set
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
765
 */
766
int var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
767 768 769 770
{
    return var_SetChecked( p_this, psz_name, 0, val );
}

771
#undef var_GetChecked
772 773
int var_GetChecked( vlc_object_t *p_this, const char *psz_name,
                    int expected_type, vlc_value_t *p_val )
774
{
775 776
    assert( p_this );

777
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
778 779
    variable_t *p_var;
    int err = VLC_SUCCESS;
780

781
    vlc_mutex_lock( &p_priv->var_lock );
782

783 784
    p_var = Lookup( p_this, psz_name );
    if( p_var != NULL )
785
    {
786
        assert( expected_type == 0 ||
787
                (p_var->i_type & VLC_VAR_CLASS) == expected_type );
788

789 790
        /* Really get the variable */
        *p_val = p_var->val;
791

Rémi Duraffort's avatar
Rémi Duraffort committed
792
#ifndef NDEBUG
Rémi Duraffort's avatar
Rémi Duraffort committed
793 794
        /* Alert if the type is VLC_VAR_VOID */
        if( ( p_var->i_type & VLC_VAR_TYPE ) == VLC_VAR_VOID )
795
            msg_Warn( p_this, "Calling var_Get on the void variable '%s' (0x%04x)", psz_name, p_var->i_type );
Rémi Duraffort's avatar
Rémi Duraffort committed
796
#endif
Rémi Duraffort's avatar
Rémi Duraffort committed
797

798 799 800 801 802
        /* Duplicate value if needed */
        p_var->ops->pf_dup( p_val );
    }
    else
        err = VLC_ENOVAR;
803

804
    vlc_mutex_unlock( &p_priv->var_lock );
805
    return err;
806 807
}

808
#undef var_Get
809 810 811 812 813 814 815 816
/**
 * Get a variable's value
 *
 * \param p_this The object that holds the variable
 * \param psz_name The name of the variable
 * \param p_val Pointer to a vlc_value_t that will hold the variable's value
 *              after the function is finished
 */
817
int var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
818 819 820
{
    return var_GetChecked( p_this, psz_name, 0, p_val );
}
821

822
#undef var_AddCallback
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838
/**
 * Register a callback in a variable
 *
 * We store a function pointer that will be called upon variable
 * modification.
 *
 * \param p_this The object that holds the variable
 * \param psz_name The name of the variable
 * \param pf_callback The function pointer
 * \param p_data A generic pointer that will be passed as the last
 *               argument to the callback function.
 *
 * \warning The callback function is run in the thread that calls var_Set on
 *          the variable. Use proper locking. This thread may not have much
 *          time to spare, so keep callback functions short.
 */
839 840
int var_AddCallback( vlc_object_t *p_this, const char *psz_name,
                     vlc_callback_t pf_callback, void *p_data )
841 842
{
    variable_t *p_var;
843
    callback_entry_t entry;
844 845 846

    assert( p_this );

847
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
848

849 850
    entry.pf_callback = pf_callback;
    entry.p_data = p_data;
851

852
    vlc_mutex_lock( &p_priv->var_lock );
853

854 855
    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
856
    {
857 858 859 860
#ifndef NDEBUG
        msg_Warn( p_this, "Failed to add a callback to the non-existing "
                          "variable '%s'", psz_name );
#endif
861
        vlc_mutex_unlock( &p_priv->var_lock );
862
        return VLC_ENOVAR;
863 864
    }