variables.c 40.7 KB
Newer Older
1 2 3
/*****************************************************************************
 * variables.c: routines for object variables handling
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2002-2009 VLC authors and VideoLAN
5
 * $Id$
6 7 8
 *
 * Authors: Samuel Hocevar <sam@zoy.org>
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
9 10 11
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
12
 * (at your option) any later version.
13
 *
14 15
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
16 17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
18
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
19 20 21
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software 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 32 33
#ifdef HAVE_SEARCH_H
# include <search.h>
#endif
34
#include <assert.h>
35
#include <float.h>
36 37
#include <math.h>
#include <limits.h>
38

39
#include <vlc_common.h>
40
#include <vlc_arrays.h>
41 42 43 44 45
#include <vlc_charset.h>
#include "libvlc.h"
#include "variables.h"
#include "config/configuration.h"

46
typedef struct callback_entry_t
47
{
48 49 50 51 52
    union
    {
        vlc_callback_t       pf_value_callback;
        vlc_list_callback_t  pf_list_callback;
        void *               p_callback;
53
    };
54
    void *         p_data;
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
} callback_entry_t;

typedef struct variable_ops_t
{
    int  (*pf_cmp) ( vlc_value_t, vlc_value_t );
    void (*pf_dup) ( vlc_value_t * );
    void (*pf_free) ( vlc_value_t * );
} variable_ops_t;

typedef struct callback_table_t
{
    int                i_entries;
    callback_entry_t * p_entries;
} callback_table_t;

/**
 * The structure describing a variable.
 * \note vlc_value_t is the common union for variable values
 */
struct variable_t
{
    char *       psz_name; /**< The variable unique name (must be first) */

    /** The variable's exported value */
    vlc_value_t  val;

    /** The variable display name, mainly for use by the interfaces */
    char *       psz_text;

    const variable_ops_t *ops;

    int          i_type;   /**< The type of the variable */
    unsigned     i_usage;  /**< Reference count */

    /** If the variable has min/max/step values */
    vlc_value_t  min, max, step;

    /** List of choices */
    vlc_list_t   choices;
    /** List of friendly names for the choices */
    vlc_list_t   choices_text;

    /** Set to TRUE if the variable is in a callback */
    bool   b_incallback;

    /** Registered value callbacks */
    callback_table_t    value_callbacks;
    /** Registered list callbacks */
    callback_table_t    list_callbacks;
104 105
};

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
106 107 108 109 110 111 112 113 114 115
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;
}

116 117 118 119 120 121 122
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 );
}
123 124 125
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; }

126
static void DupDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
127 128 129 130
static void DupString( vlc_value_t *p_val )
{
    p_val->psz_string = strdup( p_val->psz_string ? p_val->psz_string :  "" );
}
131

132
static void FreeDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
133 134
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 138 139 140 141
addr_ops   = { CmpAddress, DupDummy,  FreeDummy,  },
bool_ops   = { CmpBool,    DupDummy,  FreeDummy,  },
float_ops  = { CmpFloat,   DupDummy,  FreeDummy,  },
int_ops    = { CmpInt,     DupDummy,  FreeDummy,  },
string_ops = { CmpString,  DupString, FreeString, },
142
coords_ops = { NULL,       DupDummy,  FreeDummy,  };
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;

158
    vlc_mutex_lock(&priv->var_lock);
159 160 161 162
    pp_var = tfind( &psz_name, &priv->var_root, varcmp );
    return (pp_var != NULL) ? *pp_var : NULL;
}

163 164 165 166 167 168 169 170 171 172 173 174 175
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 );
    }
176

177 178
    free( p_var->psz_name );
    free( p_var->psz_text );
179
    free( p_var->value_callbacks.p_entries );
180 181 182
    free( p_var );
}

183 184 185 186 187 188 189 190 191 192 193 194
/**
 * Adjusts a value to fit the constraints for a certain variable:
 * - If the value is lower than the minimum, use the minimum.
 * - If the value is higher than the maximum, use the maximum.
 * - If the variable has steps, round the value to the nearest step.
 */
static void CheckValue(variable_t *var, vlc_value_t *val)
{
    /* Check that our variable is within the bounds */
    switch (var->i_type & VLC_VAR_TYPE)
    {
        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))
200 201 202 203 204 205 206 207
            {
                if (val->i_int > 0)
                    val->i_int = (val->i_int + (var->step.i_int / 2))
                                 / var->step.i_int * var->step.i_int;
                else
                    val->i_int = (val->i_int - (var->step.i_int / 2))
                                 / var->step.i_int * var->step.i_int;
            }
208 209 210
            break;

        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 217
                val->f_float = var->step.f_float
                              * roundf(val->f_float / var->step.f_float);
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
            break;
    }
}

/**
 * Waits until the variable is inactive (i.e. not executing a callback)
 */
static void WaitUnused(vlc_object_t *obj, variable_t *var)
{
    vlc_object_internals_t *priv = vlc_internals(obj);

    mutex_cleanup_push(&priv->var_lock);
    while (var->b_incallback)
        vlc_cond_wait(&priv->var_wait, &priv->var_lock);
    vlc_cleanup_pop();
}

static void TriggerCallback(vlc_object_t *obj, variable_t *var,
                            const char *name, vlc_value_t prev)
{
    assert(obj != NULL);

    size_t count = var->value_callbacks.i_entries;
    if (count == 0)
        return;

    callback_entry_t *entries = var->value_callbacks.p_entries;
    vlc_object_internals_t *priv = vlc_internals(obj);

    assert(!var->b_incallback);
    var->b_incallback = true;
    vlc_mutex_unlock(&priv->var_lock);

    for (size_t i = 0; i < count; i++)
252 253
        entries[i].pf_value_callback(obj, name, prev, var->val,
                                     entries[i].p_data);
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276

    vlc_mutex_lock(&priv->var_lock);
    var->b_incallback = false;
    vlc_cond_broadcast(&priv->var_wait);
}

static void TriggerListCallback(vlc_object_t *obj, variable_t *var,
                                const char *name, int action, vlc_value_t *val)
{
    assert(obj != NULL);

    size_t count = var->list_callbacks.i_entries;
    if (count == 0)
        return;

    callback_entry_t *entries = var->list_callbacks.p_entries;
    vlc_object_internals_t *priv = vlc_internals(obj);

    assert(!var->b_incallback);
    var->b_incallback = true;
    vlc_mutex_unlock(&priv->var_lock);

    for (size_t i = 0; i < count; i++)
277
        entries[i].pf_list_callback(obj, name, action, val,
278 279 280 281 282 283 284
                                      entries[i].p_data);

    vlc_mutex_lock(&priv->var_lock);
    var->b_incallback = false;
    vlc_cond_broadcast(&priv->var_wait);
}

285
#undef var_Create
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
286 287 288
/**
 * Initialize a vlc variable
 *
289 290 291
 * 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
292 293 294 295 296 297
 *
 * \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
 */
298
int var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
299
{
300 301
    assert( p_this );

302 303 304
    variable_t *p_var = calloc( 1, sizeof( *p_var ) );
    if( p_var == NULL )
        return VLC_ENOMEM;
305 306

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

Gildas Bazin's avatar
 
Gildas Bazin committed
309
    p_var->i_type = i_type & ~VLC_VAR_DOINHERIT;
310

311
    p_var->i_usage = 1;
312

Gildas Bazin's avatar
 
Gildas Bazin committed
313 314
    p_var->choices.i_count = 0;
    p_var->choices.p_values = NULL;
Gildas Bazin's avatar
 
Gildas Bazin committed
315 316
    p_var->choices_text.i_count = 0;
    p_var->choices_text.p_values = NULL;
317

318
    p_var->b_incallback = false;
319
    p_var->value_callbacks = (callback_table_t){ 0 };
320

321 322 323
    /* 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. */
324
    switch( i_type & VLC_VAR_CLASS )
325
    {
326
        case VLC_VAR_BOOL:
327
            p_var->ops = &bool_ops;
328
            p_var->val.b_bool = false;
329 330
            break;
        case VLC_VAR_INTEGER:
331
            p_var->ops = &int_ops;
332
            p_var->val.i_int = 0;
333 334
            p_var->min.i_int = INT64_MIN;
            p_var->max.i_int = INT64_MAX;
335 336
            break;
        case VLC_VAR_STRING:
337
            p_var->ops = &string_ops;
338
            p_var->val.psz_string = NULL;
339 340
            break;
        case VLC_VAR_FLOAT:
341
            p_var->ops = &float_ops;
342
            p_var->val.f_float = 0.f;
343
            p_var->min.f_float = -FLT_MAX;
344
            p_var->max.f_float = FLT_MAX;
345
            break;
346 347 348 349
        case VLC_VAR_COORDS:
            p_var->ops = &coords_ops;
            p_var->val.coords.x = p_var->val.coords.y = 0;
            break;
350
        case VLC_VAR_ADDRESS:
351
            p_var->ops = &addr_ops;
352 353
            p_var->val.p_address = NULL;
            break;
354
        case VLC_VAR_VOID:
355
            p_var->ops = &void_ops;
356 357
            break;
        default:
358
            vlc_assert_unreachable ();
359
    }
360

361 362
    if (i_type & VLC_VAR_DOINHERIT)
        var_Inherit(p_this, psz_name, i_type, &p_var->val);
363 364

    vlc_object_internals_t *p_priv = vlc_internals( p_this );
365 366
    variable_t **pp_var, *p_oldvar;
    int ret = VLC_SUCCESS;
367 368 369

    vlc_mutex_lock( &p_priv->var_lock );

370 371 372
    pp_var = tsearch( p_var, &p_priv->var_root, varcmp );
    if( unlikely(pp_var == NULL) )
        ret = VLC_ENOMEM;
373 374 375
    else if( (p_oldvar = *pp_var) == p_var ) /* Variable create */
        p_var = NULL; /* Variable created */
    else /* Variable already exists */
376
    {
377
        assert (((i_type ^ p_oldvar->i_type) & VLC_VAR_CLASS) == 0);
378
        p_oldvar->i_usage++;
379
        p_oldvar->i_type |= i_type & VLC_VAR_ISCOMMAND;
380
    }
381
    vlc_mutex_unlock( &p_priv->var_lock );
382

383 384 385 386
    /* If we did not need to create a new variable, free everything... */
    if( p_var != NULL )
        Destroy( p_var );
    return ret;
387 388
}

Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
389 390 391
/**
 * Destroy a vlc variable
 *
392 393
 * 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
394 395 396 397
 *
 * \param p_this The object that holds the variable
 * \param psz_name The name of the variable
 */
398
void (var_Destroy)(vlc_object_t *p_this, const char *psz_name)
399
{
400
    variable_t *p_var;
401 402 403

    assert( p_this );

404
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
405

406 407
    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
408 409 410
        msg_Dbg( p_this, "attempt to destroy nonexistent variable \"%s\"",
                 psz_name );
    else if( --p_var->i_usage == 0 )
411
    {
412
        assert(!p_var->b_incallback);
413
        tdelete( p_var, &p_priv->var_root, varcmp );
414
    }
415
    else
416 417
    {
        assert(p_var->i_usage != -1u);
418
        p_var = NULL;
419
    }
420
    vlc_mutex_unlock( &p_priv->var_lock );
421

422 423
    if( p_var != NULL )
        Destroy( p_var );
424 425
}

426 427 428 429 430 431 432 433 434 435
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 );
436
    priv->var_root = NULL;
437 438
}

439
#undef var_Change
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
440 441
/**
 * Perform an action on a variable
442
 *
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
443 444 445 446 447 448
 * \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
 */
449 450
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 )
451
{
452
    int ret = VLC_SUCCESS;
453
    variable_t *p_var;
Gildas Bazin's avatar
 
Gildas Bazin committed
454
    vlc_value_t oldval;
Erwan Tulou's avatar
Erwan Tulou committed
455
    vlc_value_t newval;
456 457 458

    assert( p_this );

459
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
460

461 462
    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
463
    {
464
        vlc_mutex_unlock( &p_priv->var_lock );
465 466 467 468 469
        return VLC_ENOVAR;
    }

    switch( i_action )
    {
470
        case VLC_VAR_GETMIN:
471
            *p_val = p_var->min;
472 473
            break;
        case VLC_VAR_GETMAX:
474
            *p_val = p_var->max;
475
            break;
476
        case VLC_VAR_SETMINMAX:
477
            assert(p_var->ops->pf_free == FreeDummy);
478 479 480
            p_var->min = *p_val;
            p_var->max = *p_val2;
            break;
481
        case VLC_VAR_SETSTEP:
482
            assert(p_var->ops->pf_free == FreeDummy);
483
            p_var->step = *p_val;
484 485
            CheckValue( p_var, &p_var->val );
            break;
486
        case VLC_VAR_GETSTEP:
487 488 489
            switch (p_var->i_type & VLC_VAR_TYPE)
            {
                case VLC_VAR_INTEGER:
490
                    if (p_var->step.i_int == 0)
491 492 493
                        ret = VLC_EGENERIC;
                    break;
                case VLC_VAR_FLOAT:
494
                    if (p_var->step.f_float == 0.f)
495 496 497 498 499
                        ret = VLC_EGENERIC;
                    break;
                default:
                    ret = VLC_EGENERIC;
            }
500 501
            if (ret == VLC_SUCCESS)
                *p_val = p_var->step;
502
            break;
503
        case VLC_VAR_ADDCHOICE:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
504 505
        {
            int i = p_var->choices.i_count;
506

507 508 509 510 511
            TAB_APPEND(p_var->choices.i_count,
                       p_var->choices.p_values, *p_val);
            assert(i == p_var->choices_text.i_count);
            TAB_APPEND(p_var->choices_text.i_count,
                       p_var->choices_text.p_values, *p_val);
512
            p_var->ops->pf_dup( &p_var->choices.p_values[i] );
Gildas Bazin's avatar
 
Gildas Bazin committed
513 514 515
            p_var->choices_text.p_values[i].psz_string =
                ( p_val2 && p_val2->psz_string ) ?
                strdup( p_val2->psz_string ) : NULL;
516

517
            TriggerListCallback(p_this, p_var, psz_name, VLC_VAR_ADDCHOICE, p_val);
518
            break;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
519
        }
520
        case VLC_VAR_DELCHOICE:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
521 522 523
        {
            int i;

Gildas Bazin's avatar
 
Gildas Bazin committed
524
            for( i = 0 ; i < p_var->choices.i_count ; i++ )
525
                if( p_var->ops->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
526 527
                    break;

Gildas Bazin's avatar
 
Gildas Bazin committed
528
            if( i == p_var->choices.i_count )
529 530
            {
                /* Not found */
531
                vlc_mutex_unlock( &p_priv->var_lock );
532 533 534
                return VLC_EGENERIC;
            }

535
            p_var->ops->pf_free( &p_var->choices.p_values[i] );
536
            free( p_var->choices_text.p_values[i].psz_string );
537 538 539
            TAB_ERASE(p_var->choices.i_count, p_var->choices.p_values, i);
            TAB_ERASE(p_var->choices_text.i_count,
                      p_var->choices_text.p_values, i);
540

541
            TriggerListCallback(p_this, p_var, psz_name, VLC_VAR_DELCHOICE, p_val);
542
            break;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
543
        }
Gildas Bazin's avatar
 
Gildas Bazin committed
544 545 546
        case VLC_VAR_CHOICESCOUNT:
            p_val->i_int = p_var->choices.i_count;
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
547
        case VLC_VAR_CLEARCHOICES:
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
548
            for( int i = 0 ; i < p_var->choices.i_count ; i++ )
549
                p_var->ops->pf_free( &p_var->choices.p_values[i] );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
550
            for( int i = 0 ; i < p_var->choices_text.i_count ; i++ )
551 552
                free( p_var->choices_text.p_values[i].psz_string );

553 554
            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
555 556 557

            p_var->choices.i_count = 0;
            p_var->choices.p_values = NULL;
558 559
            p_var->choices_text.i_count = 0;
            p_var->choices_text.p_values = NULL;
560
            TriggerListCallback(p_this, p_var, psz_name, VLC_VAR_CLEARCHOICES, NULL);
Gildas Bazin's avatar
 
Gildas Bazin committed
561 562 563
            break;
        case VLC_VAR_SETVALUE:
            /* Duplicate data if needed */
Erwan Tulou's avatar
Erwan Tulou committed
564 565
            newval = *p_val;
            p_var->ops->pf_dup( &newval );
Gildas Bazin's avatar
 
Gildas Bazin committed
566 567 568
            /* Backup needed stuff */
            oldval = p_var->val;
            /* Check boundaries and list */
Erwan Tulou's avatar
Erwan Tulou committed
569
            CheckValue( p_var, &newval );
Gildas Bazin's avatar
 
Gildas Bazin committed
570
            /* Set the variable */
Erwan Tulou's avatar
Erwan Tulou committed
571
            p_var->val = newval;
Gildas Bazin's avatar
 
Gildas Bazin committed
572
            /* Free data if needed */
573
            p_var->ops->pf_free( &oldval );
Gildas Bazin's avatar
 
Gildas Bazin committed
574
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
575
        case VLC_VAR_GETCHOICES:
576 577 578
            p_val->p_list = xmalloc( sizeof(vlc_list_t) );
            p_val->p_list->p_values =
                xmalloc( p_var->choices.i_count * sizeof(vlc_value_t) );
579
            p_val->p_list->i_type = p_var->i_type;
Gildas Bazin's avatar
 
Gildas Bazin committed
580
            p_val->p_list->i_count = p_var->choices.i_count;
581 582
            if( p_val2 )
            {
583 584 585
                p_val2->p_list = xmalloc( sizeof(vlc_list_t) );
                p_val2->p_list->p_values =
                    xmalloc( p_var->choices.i_count * sizeof(vlc_value_t) );
586 587 588
                p_val2->p_list->i_type = VLC_VAR_STRING;
                p_val2->p_list->i_count = p_var->choices.i_count;
            }
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
589
            for( int i = 0 ; i < p_var->choices.i_count ; i++ )
590
            {
Gildas Bazin's avatar
 
Gildas Bazin committed
591
                p_val->p_list->p_values[i] = p_var->choices.p_values[i];
592
                p_var->ops->pf_dup( &p_val->p_list->p_values[i] );
Gildas Bazin's avatar
 
Gildas Bazin committed
593 594 595 596 597 598
                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;
                }
599 600
            }
            break;
Gildas Bazin's avatar
 
Gildas Bazin committed
601
        case VLC_VAR_SETTEXT:
602
            free( p_var->psz_text );
Gildas Bazin's avatar
 
Gildas Bazin committed
603 604
            if( p_val && p_val->psz_string )
                p_var->psz_text = strdup( p_val->psz_string );
605
            else
Rémi Duraffort's avatar
Rémi Duraffort committed
606
                p_var->psz_text = NULL;
Gildas Bazin's avatar
 
Gildas Bazin committed
607 608
            break;
        case VLC_VAR_GETTEXT:
609 610
            p_val->psz_string = p_var->psz_text ? strdup( p_var->psz_text )
                                                : NULL;
611 612 613 614 615
            break;
        default:
            break;
    }

616
    vlc_mutex_unlock( &p_priv->var_lock );
617

618
    return ret;
619 620
}

621
#undef var_GetAndSet
622 623 624 625 626 627 628 629 630
/**
 * 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
 */
631
int var_GetAndSet( vlc_object_t *p_this, const char *psz_name, int i_action,
632
                   vlc_value_t *p_val )
633 634 635
{
    variable_t *p_var;
    vlc_value_t oldval;
636 637

    assert( p_this );
638
    assert( p_val );
639

640 641
    vlc_object_internals_t *p_priv = vlc_internals( p_this );

642 643
    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
644 645
    {
        vlc_mutex_unlock( &p_priv->var_lock );
646
        return VLC_ENOVAR;
647 648
    }

649
    WaitUnused( p_this, p_var );
650 651 652 653 654 655 656 657 658 659

    /* 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 )
    {
660
    case VLC_VAR_BOOL_TOGGLE:
661 662 663
        assert( ( p_var->i_type & VLC_VAR_BOOL ) == VLC_VAR_BOOL );
        p_var->val.b_bool = !p_var->val.b_bool;
        break;
664
    case VLC_VAR_INTEGER_ADD:
665
        assert( ( p_var->i_type & VLC_VAR_INTEGER ) == VLC_VAR_INTEGER );
666 667 668 669 670 671 672 673 674
        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;
675
        break;
676 677 678 679 680 681 682
    default:
        vlc_mutex_unlock( &p_priv->var_lock );
        return VLC_EGENERIC;
    }

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

685
    /* Deal with callbacks.*/
686
    TriggerCallback( p_this, p_var, psz_name, oldval );
687 688

    vlc_mutex_unlock( &p_priv->var_lock );
689
    return VLC_SUCCESS;
690 691
}

692
#undef var_Type
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
693 694 695 696
/**
 * Request a variable's type
 *
 * \return The variable type if it exists, or 0 if the
697
 * variable could not be found.
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
698 699
 * \see \ref var_type
 */
700
int var_Type( vlc_object_t *p_this, const char *psz_name )
701
{
702 703
    variable_t *p_var;
    int i_type = 0;
704 705 706

    assert( p_this );

707
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
708

709 710
    p_var = Lookup( p_this, psz_name );
    if( p_var != NULL )
711
    {
712
        i_type = p_var->i_type;
713 714 715
        if( p_var->choices.i_count > 0 )
            i_type |= VLC_VAR_HASCHOICE;
    }
716
    vlc_mutex_unlock( &p_priv->var_lock );
717 718

    return i_type;
719 720
}

721
#undef var_SetChecked
722 723
int var_SetChecked( vlc_object_t *p_this, const char *psz_name,
                    int expected_type, vlc_value_t val )
724
{
725 726
    variable_t *p_var;
    vlc_value_t oldval;
727 728 729

    assert( p_this );

730
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
731

732 733
    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
734
    {
735
        vlc_mutex_unlock( &p_priv->var_lock );
736
        return VLC_ENOVAR;
737 738
    }

739
    assert( expected_type == 0 ||
740
            (p_var->i_type & VLC_VAR_CLASS) == expected_type );
741
    assert ((p_var->i_type & VLC_VAR_CLASS) != VLC_VAR_VOID);
742

743 744
    WaitUnused( p_this, p_var );

745
    /* Duplicate data if needed */
746
    p_var->ops->pf_dup( &val );
747

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

751
    /* Check boundaries and list */
752 753
    CheckValue( p_var, &val );

754 755 756
    /* Set the variable */
    p_var->val = val;

757
    /* Deal with callbacks */
758
    TriggerCallback( p_this, p_var, psz_name, oldval );
759 760

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

763
    vlc_mutex_unlock( &p_priv->var_lock );
764
    return VLC_SUCCESS;
765 766
}

767
#undef var_Set
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
768
/**
769
 * Set a variable's value
770
 *
771
 * \param p_this The object that hold the variable
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
772
 * \param psz_name The name of the variable
773
 * \param val the value to set
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
774
 */
775
int var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
776 777 778 779
{
    return var_SetChecked( p_this, psz_name, 0, val );
}

780
#undef var_GetChecked
781 782
int var_GetChecked( vlc_object_t *p_this, const char *psz_name,
                    int expected_type, vlc_value_t *p_val )
783
{
784 785
    assert( p_this );

786
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
787 788
    variable_t *p_var;
    int err = VLC_SUCCESS;
789

790 791
    p_var = Lookup( p_this, psz_name );
    if( p_var != NULL )
792
    {
793
        assert( expected_type == 0 ||
794
                (p_var->i_type & VLC_VAR_CLASS) == expected_type );
795
        assert ((p_var->i_type & VLC_VAR_CLASS) != VLC_VAR_VOID);
796

797 798
        /* Really get the variable */
        *p_val = p_var->val;
799

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

806
    vlc_mutex_unlock( &p_priv->var_lock );
807
    return err;
808 809
}

810
#undef var_Get
811 812 813 814 815 816 817 818
/**
 * 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
 */
819
int var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
820 821 822
{
    return var_GetChecked( p_this, psz_name, 0, p_val );
}
823

824 825 826 827 828 829
typedef enum
{
    vlc_value_callback,
    vlc_list_callback
} vlc_callback_type_t;

830
static void AddCallback( vlc_object_t *p_this, const char *psz_name,
831
                        callback_entry_t entry, vlc_callback_type_t i_type )
832 833
{
    variable_t *p_var;
834 835 836

    assert( p_this );

837
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
838

839 840
    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
841
    {
842
        vlc_mutex_unlock( &p_priv->var_lock );
843
        msg_Err( p_this, "cannot add callback %p to nonexistent variable '%s'",
844
                 entry.p_callback, psz_name );
845
        return;
846 847
    }

848
    WaitUnused( p_this, p_var );
849 850 851 852 853 854

    callback_table_t *p_table;
    if (i_type == vlc_value_callback)
        p_table = &p_var->value_callbacks;
    else
        p_table = &p_var->list_callbacks;
855
    TAB_APPEND(p_table->i_entries, p_table->p_entries, entry);
856

857
    vlc_mutex_unlock( &p_priv->var_lock );
858 859
}

860
#undef var_AddCallback
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
861
/**
862
 * Register a callback in a variable
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
863
 *
864 865 866 867 868 869 870 871 872 873 874 875
 * 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.
Sigmund Augdal Helberg's avatar
Sigmund Augdal Helberg committed
876
 */
877 878
void var_AddCallback( vlc_object_t *p_this, const char *psz_name,
                      vlc_callback_t pf_callback, void *p_data )
879 880
{
    callback_entry_t entry;
881
    entry.pf_value_callback = pf_callback;
882 883
    entry.p_data = p_data;

884
    AddCallback(p_this, psz_name, entry, vlc_value_callback);
885 886
}

887 888
static void DelCallback( vlc_object_t *p_this, const char *psz_name,
                         callback_entry_t entry, vlc_callback_type_t i_type )
889
{
890
    int i_entry;
891
    variable_t *p_var;
892 893 894
#ifndef NDEBUG
    bool b_found_similar = false;
#endif
895 896 897

    assert( p_this );

898
    vlc_object_internals_t *p_priv = vlc_internals( p_this );
899

900 901
    p_var = Lookup( p_this, psz_name );
    if( p_var == NULL )
902
    {
903
        vlc_mutex_unlock( &p_priv->var_lock );
Rémi Denis-Courmont's avatar <