Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

variables.c

00001 /*****************************************************************************
00002  * variables.c: routines for object variables handling
00003  *****************************************************************************
00004  * Copyright (C) 2002-2004 the VideoLAN team
00005  * $Id: variables.c 12509 2005-09-10 15:22:51Z robux4 $
00006  *
00007  * Authors: Samuel Hocevar <[email protected]>
00008  *
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00022  *****************************************************************************/
00023 
00024 /*****************************************************************************
00025  * Preamble
00026  *****************************************************************************/
00027 #include <vlc/vlc.h>
00028 
00029 #ifdef HAVE_STDLIB_H
00030 #   include <stdlib.h>                                          /* realloc() */
00031 #endif
00032 
00033 /*****************************************************************************
00034  * Private types
00035  *****************************************************************************/
00036 struct callback_entry_t
00037 {
00038     vlc_callback_t pf_callback;
00039     void *         p_data;
00040 };
00041 
00042 /*****************************************************************************
00043  * Local comparison functions, returns 0 if v == w, < 0 if v < w, > 0 if v > w
00044  *****************************************************************************/
00045 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; }
00046 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; }
00047 static int CmpTime( vlc_value_t v, vlc_value_t w )
00048 {
00049     return v.i_time == w.i_time ? 0 : v.i_time > w.i_time ? 1 : -1;
00050 }
00051 static int CmpString( vlc_value_t v, vlc_value_t w ) { return strcmp( v.psz_string, w.psz_string ); }
00052 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; }
00053 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; }
00054 
00055 /*****************************************************************************
00056  * Local duplication functions, and local deallocation functions
00057  *****************************************************************************/
00058 static void DupDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
00059 static void DupString( vlc_value_t *p_val ) { p_val->psz_string = strdup( p_val->psz_string ); }
00060 
00061 static void DupList( vlc_value_t *p_val )
00062 {
00063     int i;
00064     vlc_list_t *p_list = malloc( sizeof(vlc_list_t) );
00065 
00066     p_list->i_count = p_val->p_list->i_count;
00067     if( p_val->p_list->i_count )
00068     {
00069         p_list->p_values = malloc( p_list->i_count * sizeof(vlc_value_t) );
00070         p_list->pi_types = malloc( p_list->i_count * sizeof(int) );
00071     }
00072     else
00073     {
00074         p_list->p_values = NULL;
00075         p_list->pi_types = NULL;
00076     }
00077 
00078     for( i = 0; i < p_list->i_count; i++ )
00079     {
00080         p_list->p_values[i] = p_val->p_list->p_values[i];
00081         p_list->pi_types[i] = p_val->p_list->pi_types[i];
00082         switch( p_val->p_list->pi_types[i] & VLC_VAR_TYPE )
00083         {
00084         case VLC_VAR_STRING:
00085 
00086             DupString( &p_list->p_values[i] );
00087             break;
00088         default:
00089             break;
00090         }
00091     }
00092 
00093     p_val->p_list = p_list;
00094 }
00095 
00096 static void FreeDummy( vlc_value_t *p_val ) { (void)p_val; /* unused */ }
00097 static void FreeString( vlc_value_t *p_val ) { free( p_val->psz_string ); }
00098 static void FreeMutex( vlc_value_t *p_val ) { vlc_mutex_destroy( (vlc_mutex_t*)p_val->p_address ); free( p_val->p_address ); }
00099 
00100 static void FreeList( vlc_value_t *p_val )
00101 {
00102     int i;
00103     for( i = 0; i < p_val->p_list->i_count; i++ )
00104     {
00105         switch( p_val->p_list->pi_types[i] & VLC_VAR_TYPE )
00106         {
00107         case VLC_VAR_STRING:
00108             FreeString( &p_val->p_list->p_values[i] );
00109             break;
00110         case VLC_VAR_MUTEX:
00111             FreeMutex( &p_val->p_list->p_values[i] );
00112             break;
00113         default:
00114             break;
00115         }
00116     }
00117 
00118     if( p_val->p_list->i_count )
00119     {
00120         free( p_val->p_list->p_values );
00121         free( p_val->p_list->pi_types );
00122     }
00123     free( p_val->p_list );
00124 }
00125 
00126 /*****************************************************************************
00127  * Local prototypes
00128  *****************************************************************************/
00129 static int      GetUnused   ( vlc_object_t *, const char * );
00130 static uint32_t HashString  ( const char * );
00131 static int      Insert      ( variable_t *, int, const char * );
00132 static int      InsertInner ( variable_t *, int, uint32_t );
00133 static int      Lookup      ( variable_t *, int, const char * );
00134 static int      LookupInner ( variable_t *, int, uint32_t );
00135 
00136 static void     CheckValue  ( variable_t *, vlc_value_t * );
00137 
00138 static int      InheritValue( vlc_object_t *, const char *, vlc_value_t *,
00139                               int );
00140 
00153 int __var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
00154 {
00155     int i_new;
00156     variable_t *p_var;
00157     static vlc_list_t dummy_null_list = {0, NULL, NULL};
00158 
00159     vlc_mutex_lock( &p_this->var_lock );
00160 
00161     /* FIXME: if the variable already exists, we don't duplicate it. But we
00162      * duplicate the lookups. It's not that serious, but if anyone finds some
00163      * time to rework Insert() so that only one lookup has to be done, feel
00164      * free to do so. */
00165     i_new = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
00166 
00167     if( i_new >= 0 )
00168     {
00169         /* If the types differ, variable creation failed. */
00170         if( (i_type & ~VLC_VAR_DOINHERIT) != p_this->p_vars[i_new].i_type )
00171         {
00172             vlc_mutex_unlock( &p_this->var_lock );
00173             return VLC_EBADVAR;
00174         }
00175 
00176         p_this->p_vars[i_new].i_usage++;
00177         vlc_mutex_unlock( &p_this->var_lock );
00178         return VLC_SUCCESS;
00179     }
00180 
00181     i_new = Insert( p_this->p_vars, p_this->i_vars, psz_name );
00182 
00183     if( (p_this->i_vars & 15) == 15 )
00184     {
00185         p_this->p_vars = realloc( p_this->p_vars,
00186                                   (p_this->i_vars+17) * sizeof(variable_t) );
00187     }
00188 
00189     memmove( p_this->p_vars + i_new + 1,
00190              p_this->p_vars + i_new,
00191              (p_this->i_vars - i_new) * sizeof(variable_t) );
00192 
00193     p_this->i_vars++;
00194 
00195     p_var = &p_this->p_vars[i_new];
00196     memset( p_var, 0, sizeof(*p_var) );
00197 
00198     p_var->i_hash = HashString( psz_name );
00199     p_var->psz_name = strdup( psz_name );
00200     p_var->psz_text = NULL;
00201 
00202     p_var->i_type = i_type & ~VLC_VAR_DOINHERIT;
00203     memset( &p_var->val, 0, sizeof(vlc_value_t) );
00204 
00205     p_var->pf_dup = DupDummy;
00206     p_var->pf_free = FreeDummy;
00207 
00208     p_var->i_usage = 1;
00209 
00210     p_var->i_default = -1;
00211     p_var->choices.i_count = 0;
00212     p_var->choices.p_values = NULL;
00213     p_var->choices_text.i_count = 0;
00214     p_var->choices_text.p_values = NULL;
00215 
00216     p_var->b_incallback = VLC_FALSE;
00217     p_var->i_entries = 0;
00218     p_var->p_entries = NULL;
00219 
00220     /* Always initialize the variable, even if it is a list variable; this
00221      * will lead to errors if the variable is not initialized, but it will
00222      * not cause crashes in the variable handling. */
00223     switch( i_type & VLC_VAR_TYPE )
00224     {
00225         case VLC_VAR_BOOL:
00226             p_var->pf_cmp = CmpBool;
00227             p_var->val.b_bool = VLC_FALSE;
00228             break;
00229         case VLC_VAR_INTEGER:
00230         case VLC_VAR_HOTKEY:
00231             p_var->pf_cmp = CmpInt;
00232             p_var->val.i_int = 0;
00233             break;
00234         case VLC_VAR_STRING:
00235         case VLC_VAR_MODULE:
00236         case VLC_VAR_FILE:
00237         case VLC_VAR_DIRECTORY:
00238         case VLC_VAR_VARIABLE:
00239             p_var->pf_cmp = CmpString;
00240             p_var->pf_dup = DupString;
00241             p_var->pf_free = FreeString;
00242             p_var->val.psz_string = "";
00243             break;
00244         case VLC_VAR_FLOAT:
00245             p_var->pf_cmp = CmpFloat;
00246             p_var->val.f_float = 0.0;
00247             break;
00248         case VLC_VAR_TIME:
00249             p_var->pf_cmp = CmpTime;
00250             p_var->val.i_time = 0;
00251             break;
00252         case VLC_VAR_ADDRESS:
00253             p_var->pf_cmp = CmpAddress;
00254             p_var->val.p_address = NULL;
00255             break;
00256         case VLC_VAR_MUTEX:
00257             p_var->pf_cmp = CmpAddress;
00258             p_var->pf_free = FreeMutex;
00259             p_var->val.p_address = malloc( sizeof(vlc_mutex_t) );
00260             vlc_mutex_init( p_this, (vlc_mutex_t*)p_var->val.p_address );
00261             break;
00262         case VLC_VAR_LIST:
00263             p_var->pf_cmp = CmpAddress;
00264             p_var->pf_dup = DupList;
00265             p_var->pf_free = FreeList;
00266             p_var->val.p_list = &dummy_null_list;
00267             break;
00268     }
00269 
00270     /* Duplicate the default data we stored. */
00271     p_var->pf_dup( &p_var->val );
00272 
00273     if( i_type & VLC_VAR_DOINHERIT )
00274     {
00275         vlc_value_t val;
00276 
00277         if( InheritValue( p_this, psz_name, &val, p_var->i_type )
00278             == VLC_SUCCESS );
00279         {
00280             /* Free data if needed */
00281             p_var->pf_free( &p_var->val );
00282             /* Set the variable */
00283             p_var->val = val;
00284 
00285             if( i_type & VLC_VAR_HASCHOICE )
00286             {
00287                 /* We must add the inherited value to our choice list */
00288                 p_var->i_default = 0;
00289 
00290                 INSERT_ELEM( p_var->choices.p_values, p_var->choices.i_count,
00291                              0, val );
00292                 INSERT_ELEM( p_var->choices_text.p_values,
00293                              p_var->choices_text.i_count, 0, val );
00294                 p_var->pf_dup( &p_var->choices.p_values[0] );
00295                 p_var->choices_text.p_values[0].psz_string = NULL;
00296             }
00297         }
00298     }
00299 
00300     vlc_mutex_unlock( &p_this->var_lock );
00301 
00302     return VLC_SUCCESS;
00303 }
00304 
00314 int __var_Destroy( vlc_object_t *p_this, const char *psz_name )
00315 {
00316     int i_var, i;
00317     variable_t *p_var;
00318 
00319     vlc_mutex_lock( &p_this->var_lock );
00320 
00321     i_var = GetUnused( p_this, psz_name );
00322     if( i_var < 0 )
00323     {
00324         vlc_mutex_unlock( &p_this->var_lock );
00325         return i_var;
00326     }
00327 
00328     p_var = &p_this->p_vars[i_var];
00329 
00330     if( p_var->i_usage > 1 )
00331     {
00332         p_var->i_usage--;
00333         vlc_mutex_unlock( &p_this->var_lock );
00334         return VLC_SUCCESS;
00335     }
00336 
00337     /* Free value if needed */
00338     p_var->pf_free( &p_var->val );
00339 
00340     /* Free choice list if needed */
00341     if( p_var->choices.i_count )
00342     {
00343         for( i = 0 ; i < p_var->choices.i_count ; i++ )
00344         {
00345             p_var->pf_free( &p_var->choices.p_values[i] );
00346             if( p_var->choices_text.p_values[i].psz_string )
00347                 free( p_var->choices_text.p_values[i].psz_string );
00348         }
00349         free( p_var->choices.p_values );
00350         free( p_var->choices_text.p_values );
00351     }
00352 
00353     /* Free callbacks if needed */
00354     if( p_var->p_entries )
00355     {
00356         free( p_var->p_entries );
00357     }
00358 
00359     free( p_var->psz_name );
00360     if( p_var->psz_text ) free( p_var->psz_text );
00361 
00362     memmove( p_this->p_vars + i_var,
00363              p_this->p_vars + i_var + 1,
00364              (p_this->i_vars - i_var - 1) * sizeof(variable_t) );
00365 
00366     if( (p_this->i_vars & 15) == 0 )
00367     {
00368         p_this->p_vars = realloc( p_this->p_vars,
00369                           (p_this->i_vars) * sizeof( variable_t ) );
00370     }
00371 
00372     p_this->i_vars--;
00373 
00374     vlc_mutex_unlock( &p_this->var_lock );
00375 
00376     return VLC_SUCCESS;
00377 }
00378 
00388 int __var_Change( vlc_object_t *p_this, const char *psz_name,
00389                   int i_action, vlc_value_t *p_val, vlc_value_t *p_val2 )
00390 {
00391     int i_var, i;
00392     variable_t *p_var;
00393     vlc_value_t oldval;
00394 
00395     vlc_mutex_lock( &p_this->var_lock );
00396 
00397     i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
00398 
00399     if( i_var < 0 )
00400     {
00401         vlc_mutex_unlock( &p_this->var_lock );
00402         return VLC_ENOVAR;
00403     }
00404 
00405     p_var = &p_this->p_vars[i_var];
00406 
00407     switch( i_action )
00408     {
00409         case VLC_VAR_SETMIN:
00410             if( p_var->i_type & VLC_VAR_HASMIN )
00411             {
00412                 p_var->pf_free( &p_var->min );
00413             }
00414             p_var->i_type |= VLC_VAR_HASMIN;
00415             p_var->min = *p_val;
00416             p_var->pf_dup( &p_var->min );
00417             CheckValue( p_var, &p_var->val );
00418             break;
00419         case VLC_VAR_SETMAX:
00420             if( p_var->i_type & VLC_VAR_HASMAX )
00421             {
00422                 p_var->pf_free( &p_var->max );
00423             }
00424             p_var->i_type |= VLC_VAR_HASMAX;
00425             p_var->max = *p_val;
00426             p_var->pf_dup( &p_var->max );
00427             CheckValue( p_var, &p_var->val );
00428             break;
00429         case VLC_VAR_SETSTEP:
00430             if( p_var->i_type & VLC_VAR_HASSTEP )
00431             {
00432                 p_var->pf_free( &p_var->step );
00433             }
00434             p_var->i_type |= VLC_VAR_HASSTEP;
00435             p_var->step = *p_val;
00436             p_var->pf_dup( &p_var->step );
00437             CheckValue( p_var, &p_var->val );
00438             break;
00439         case VLC_VAR_ADDCHOICE:
00440             /* FIXME: the list is sorted, dude. Use something cleverer. */
00441             for( i = p_var->choices.i_count ; i-- ; )
00442             {
00443                 if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) < 0 )
00444                 {
00445                     break;
00446                 }
00447             }
00448 
00449             /* The new place is i+1 */
00450             i++;
00451 
00452             if( p_var->i_default >= i )
00453             {
00454                 p_var->i_default++;
00455             }
00456 
00457             INSERT_ELEM( p_var->choices.p_values, p_var->choices.i_count,
00458                          i, *p_val );
00459             INSERT_ELEM( p_var->choices_text.p_values,
00460                          p_var->choices_text.i_count, i, *p_val );
00461             p_var->pf_dup( &p_var->choices.p_values[i] );
00462             p_var->choices_text.p_values[i].psz_string =
00463                 ( p_val2 && p_val2->psz_string ) ?
00464                 strdup( p_val2->psz_string ) : NULL;
00465 
00466             CheckValue( p_var, &p_var->val );
00467             break;
00468         case VLC_VAR_DELCHOICE:
00469             /* FIXME: the list is sorted, dude. Use something cleverer. */
00470             for( i = 0 ; i < p_var->choices.i_count ; i++ )
00471             {
00472                 if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
00473                 {
00474                     break;
00475                 }
00476             }
00477 
00478             if( i == p_var->choices.i_count )
00479             {
00480                 /* Not found */
00481                 vlc_mutex_unlock( &p_this->var_lock );
00482                 return VLC_EGENERIC;
00483             }
00484 
00485             if( p_var->i_default > i )
00486             {
00487                 p_var->i_default--;
00488             }
00489             else if( p_var->i_default == i )
00490             {
00491                 p_var->i_default = -1;
00492             }
00493 
00494             p_var->pf_free( &p_var->choices.p_values[i] );
00495             if( p_var->choices_text.p_values[i].psz_string )
00496                 free( p_var->choices_text.p_values[i].psz_string );
00497             REMOVE_ELEM( p_var->choices.p_values, p_var->choices.i_count, i );
00498             REMOVE_ELEM( p_var->choices_text.p_values,
00499                          p_var->choices_text.i_count, i );
00500 
00501             CheckValue( p_var, &p_var->val );
00502             break;
00503         case VLC_VAR_CHOICESCOUNT:
00504             p_val->i_int = p_var->choices.i_count;
00505             break;
00506         case VLC_VAR_CLEARCHOICES:
00507             for( i = 0 ; i < p_var->choices.i_count ; i++ )
00508             {
00509                 p_var->pf_free( &p_var->choices.p_values[i] );
00510             }
00511             for( i = 0 ; i < p_var->choices_text.i_count ; i++ )
00512             {
00513                 if( p_var->choices_text.p_values[i].psz_string )
00514                     free( p_var->choices_text.p_values[i].psz_string );
00515             }
00516             if( p_var->choices.i_count ) free( p_var->choices.p_values );
00517             if( p_var->choices_text.i_count ) free( p_var->choices_text.p_values );
00518 
00519             p_var->choices.i_count = 0;
00520             p_var->choices.p_values = NULL;
00521             p_var->choices_text.i_count = 0;
00522             p_var->choices_text.p_values = NULL;
00523             p_var->i_default = -1;
00524             break;
00525         case VLC_VAR_SETDEFAULT:
00526             /* FIXME: the list is sorted, dude. Use something cleverer. */
00527             for( i = 0 ; i < p_var->choices.i_count ; i++ )
00528             {
00529                 if( p_var->pf_cmp( p_var->choices.p_values[i], *p_val ) == 0 )
00530                 {
00531                     break;
00532                 }
00533             }
00534 
00535             if( i == p_var->choices.i_count )
00536             {
00537                 /* Not found */
00538                 break;
00539             }
00540 
00541             p_var->i_default = i;
00542             CheckValue( p_var, &p_var->val );
00543             break;
00544         case VLC_VAR_SETVALUE:
00545             /* Duplicate data if needed */
00546             p_var->pf_dup( p_val );
00547             /* Backup needed stuff */
00548             oldval = p_var->val;
00549             /* Check boundaries and list */
00550             CheckValue( p_var, p_val );
00551             /* Set the variable */
00552             p_var->val = *p_val;
00553             /* Free data if needed */
00554             p_var->pf_free( &oldval );
00555             break;
00556         case VLC_VAR_GETCHOICES:
00557         case VLC_VAR_GETLIST:
00558             p_val->p_list = malloc( sizeof(vlc_list_t) );
00559             if( p_val2 ) p_val2->p_list = malloc( sizeof(vlc_list_t) );
00560             if( p_var->choices.i_count )
00561             {
00562                 p_val->p_list->p_values = malloc( p_var->choices.i_count
00563                                                   * sizeof(vlc_value_t) );
00564                 p_val->p_list->pi_types = malloc( p_var->choices.i_count
00565                                                   * sizeof(int) );
00566                 if( p_val2 )
00567                 {
00568                     p_val2->p_list->p_values =
00569                         malloc( p_var->choices.i_count * sizeof(vlc_value_t) );
00570                     p_val2->p_list->pi_types =
00571                         malloc( p_var->choices.i_count * sizeof(int) );
00572                 }
00573             }
00574             p_val->p_list->i_count = p_var->choices.i_count;
00575             if( p_val2 ) p_val2->p_list->i_count = p_var->choices.i_count;
00576             for( i = 0 ; i < p_var->choices.i_count ; i++ )
00577             {
00578                 p_val->p_list->p_values[i] = p_var->choices.p_values[i];
00579                 p_val->p_list->pi_types[i] = p_var->i_type;
00580                 p_var->pf_dup( &p_val->p_list->p_values[i] );
00581                 if( p_val2 )
00582                 {
00583                     p_val2->p_list->p_values[i].psz_string =
00584                         p_var->choices_text.p_values[i].psz_string ?
00585                     strdup(p_var->choices_text.p_values[i].psz_string) : NULL;
00586                     p_val2->p_list->pi_types[i] = VLC_VAR_STRING;
00587                 }
00588             }
00589             break;
00590         case VLC_VAR_FREELIST:
00591             FreeList( p_val );
00592             if( p_val2 && p_val2->p_list )
00593             {
00594                 for( i = 0; i < p_val2->p_list->i_count; i++ )
00595                     if( p_val2->p_list->p_values[i].psz_string )
00596                         free( p_val2->p_list->p_values[i].psz_string );
00597                 if( p_val2->p_list->i_count )
00598                 {
00599                     free( p_val2->p_list->p_values );
00600                     free( p_val2->p_list->pi_types );
00601                 }
00602                 free( p_val2->p_list );
00603             }
00604             break;
00605         case VLC_VAR_SETTEXT:
00606             if( p_var->psz_text ) free( p_var->psz_text );
00607             if( p_val && p_val->psz_string )
00608                 p_var->psz_text = strdup( p_val->psz_string );
00609             break;
00610         case VLC_VAR_GETTEXT:
00611             p_val->psz_string = NULL;
00612             if( p_var->psz_text )
00613             {
00614                 p_val->psz_string = strdup( p_var->psz_text );
00615             }
00616             break;
00617         case VLC_VAR_INHERITVALUE:
00618             {
00619                 vlc_value_t val;
00620 
00621                 if( InheritValue( p_this, psz_name, &val, p_var->i_type )
00622                     == VLC_SUCCESS );
00623                 {
00624                     /* Duplicate already done */
00625 
00626                     /* Backup needed stuff */
00627                     oldval = p_var->val;
00628                     /* Check boundaries and list */
00629                     CheckValue( p_var, &val );
00630                     /* Set the variable */
00631                     p_var->val = val;
00632                     /* Free data if needed */
00633                     p_var->pf_free( &oldval );
00634                 }
00635 
00636                 if( p_val )
00637                 {
00638                     *p_val = p_var->val;
00639                     p_var->pf_dup( p_val );
00640                 }
00641             }
00642             break;
00643         case VLC_VAR_TRIGGER_CALLBACKS:
00644             {
00645                 /* Deal with callbacks. Tell we're in a callback, release the lock,
00646                  * call stored functions, retake the lock. */
00647                 if( p_var->i_entries )
00648                 {
00649                     int i_var;
00650                     int i_entries = p_var->i_entries;
00651                     callback_entry_t *p_entries = p_var->p_entries;
00652 
00653                     p_var->b_incallback = VLC_TRUE;
00654                     vlc_mutex_unlock( &p_this->var_lock );
00655 
00656                     /* The real calls */
00657                     for( ; i_entries-- ; )
00658                     {
00659                         p_entries[i_entries].pf_callback( p_this, psz_name, p_var->val, p_var->val,
00660                                                           p_entries[i_entries].p_data );
00661                     }
00662 
00663                     vlc_mutex_lock( &p_this->var_lock );
00664 
00665                     i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
00666                     if( i_var < 0 )
00667                     {
00668                         msg_Err( p_this, "variable %s has disappeared", psz_name );
00669                         vlc_mutex_unlock( &p_this->var_lock );
00670                         return VLC_ENOVAR;
00671                     }
00672 
00673                     p_var = &p_this->p_vars[i_var];
00674                     p_var->b_incallback = VLC_FALSE;
00675                 }
00676             }
00677             break;
00678 
00679         default:
00680             break;
00681     }
00682 
00683     vlc_mutex_unlock( &p_this->var_lock );
00684 
00685     return VLC_SUCCESS;
00686 }
00687 
00695 int __var_Type( vlc_object_t *p_this, const char *psz_name )
00696 {
00697     int i_var, i_type;
00698 
00699     vlc_mutex_lock( &p_this->var_lock );
00700 
00701     i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
00702 
00703     if( i_var < 0 )
00704     {
00705         vlc_mutex_unlock( &p_this->var_lock );
00706         return 0;
00707     }
00708 
00709     i_type = p_this->p_vars[i_var].i_type;
00710 
00711     vlc_mutex_unlock( &p_this->var_lock );
00712 
00713     return i_type;
00714 }
00715 
00723 int __var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
00724 {
00725     int i_var;
00726     variable_t *p_var;
00727     vlc_value_t oldval;
00728 
00729     vlc_mutex_lock( &p_this->var_lock );
00730 
00731     i_var = GetUnused( p_this, psz_name );
00732     if( i_var < 0 )
00733     {
00734         vlc_mutex_unlock( &p_this->var_lock );
00735         return i_var;
00736     }
00737 
00738     p_var = &p_this->p_vars[i_var];
00739 
00740     /* Duplicate data if needed */
00741     p_var->pf_dup( &val );
00742 
00743     /* Backup needed stuff */
00744     oldval = p_var->val;
00745 
00746     /* Check boundaries and list */
00747     CheckValue( p_var, &val );
00748 
00749     /* Set the variable */
00750     p_var->val = val;
00751 
00752     /* Deal with callbacks. Tell we're in a callback, release the lock,
00753      * call stored functions, retake the lock. */
00754     if( p_var->i_entries )
00755     {
00756         int i_var;
00757         int i_entries = p_var->i_entries;
00758         callback_entry_t *p_entries = p_var->p_entries;
00759 
00760         p_var->b_incallback = VLC_TRUE;
00761         vlc_mutex_unlock( &p_this->var_lock );
00762 
00763         /* The real calls */
00764         for( ; i_entries-- ; )
00765         {
00766             p_entries[i_entries].pf_callback( p_this, psz_name, oldval, val,
00767                                               p_entries[i_entries].p_data );
00768         }
00769 
00770         vlc_mutex_lock( &p_this->var_lock );
00771 
00772         i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
00773         if( i_var < 0 )
00774         {
00775             msg_Err( p_this, "variable %s has disappeared", psz_name );
00776             vlc_mutex_unlock( &p_this->var_lock );
00777             return VLC_ENOVAR;
00778         }
00779 
00780         p_var = &p_this->p_vars[i_var];
00781         p_var->b_incallback = VLC_FALSE;
00782     }
00783 
00784     /* Free data if needed */
00785     p_var->pf_free( &oldval );
00786 
00787     vlc_mutex_unlock( &p_this->var_lock );
00788 
00789     return VLC_SUCCESS;
00790 }
00791 
00800 int __var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
00801 {
00802     int i_var;
00803     variable_t *p_var;
00804 
00805     vlc_mutex_lock( &p_this->var_lock );
00806 
00807     i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
00808 
00809     if( i_var < 0 )
00810     {
00811         vlc_mutex_unlock( &p_this->var_lock );
00812         return VLC_ENOVAR;
00813     }
00814 
00815     p_var = &p_this->p_vars[i_var];
00816 
00817     /* Really get the variable */
00818     *p_val = p_var->val;
00819 
00820     /* Duplicate value if needed */
00821     p_var->pf_dup( p_val );
00822 
00823     vlc_mutex_unlock( &p_this->var_lock );
00824 
00825     return VLC_SUCCESS;
00826 }
00827 
00844 int __var_AddCallback( vlc_object_t *p_this, const char *psz_name,
00845                        vlc_callback_t pf_callback, void *p_data )
00846 {
00847     int i_var;
00848     variable_t *p_var;
00849     callback_entry_t entry;
00850 
00851     entry.pf_callback = pf_callback;
00852     entry.p_data = p_data;
00853 
00854     vlc_mutex_lock( &p_this->var_lock );
00855 
00856     i_var = GetUnused( p_this, psz_name );
00857     if( i_var < 0 )
00858     {
00859         vlc_mutex_unlock( &p_this->var_lock );
00860         return i_var;
00861     }
00862 
00863     p_var = &p_this->p_vars[i_var];
00864 
00865     INSERT_ELEM( p_var->p_entries,
00866                  p_var->i_entries,
00867                  p_var->i_entries,
00868                  entry );
00869 
00870     vlc_mutex_unlock( &p_this->var_lock );
00871 
00872     return VLC_SUCCESS;
00873 }
00874 
00881 int __var_DelCallback( vlc_object_t *p_this, const char *psz_name,
00882                        vlc_callback_t pf_callback, void *p_data )
00883 {
00884     int i_entry, i_var;
00885     variable_t *p_var;
00886 
00887     vlc_mutex_lock( &p_this->var_lock );
00888 
00889     i_var = GetUnused( p_this, psz_name );
00890     if( i_var < 0 )
00891     {
00892         vlc_mutex_unlock( &p_this->var_lock );
00893         return i_var;
00894     }
00895 
00896     p_var = &p_this->p_vars[i_var];
00897 
00898     for( i_entry = p_var->i_entries ; i_entry-- ; )
00899     {
00900         if( p_var->p_entries[i_entry].pf_callback == pf_callback
00901             && p_var->p_entries[i_entry].p_data == p_data )
00902         {
00903             break;
00904         }
00905     }
00906 
00907     if( i_entry < 0 )
00908     {
00909         vlc_mutex_unlock( &p_this->var_lock );
00910         return VLC_EGENERIC;
00911     }
00912 
00913     REMOVE_ELEM( p_var->p_entries, p_var->i_entries, i_entry );
00914 
00915     vlc_mutex_unlock( &p_this->var_lock );
00916 
00917     return VLC_SUCCESS;
00918 }
00919 
00920 /* Following functions are local */
00921 
00922 /*****************************************************************************
00923  * GetUnused: find an unused variable from its name
00924  *****************************************************************************
00925  * We do i_tries tries before giving up, just in case the variable is being
00926  * modified and called from a callback.
00927  *****************************************************************************/
00928 static int GetUnused( vlc_object_t *p_this, const char *psz_name )
00929 {
00930     int i_var, i_tries = 0;
00931 
00932     while( VLC_TRUE )
00933     {
00934         i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
00935         if( i_var < 0 )
00936         {
00937             return VLC_ENOVAR;
00938         }
00939 
00940         if( ! p_this->p_vars[i_var].b_incallback )
00941         {
00942             return i_var;
00943         }
00944 
00945         if( i_tries++ > 100 )
00946         {
00947             msg_Err( p_this, "caught in a callback deadlock?" );
00948             return VLC_ETIMEOUT;
00949         }
00950 
00951         vlc_mutex_unlock( &p_this->var_lock );
00952         msleep( THREAD_SLEEP );
00953         vlc_mutex_lock( &p_this->var_lock );
00954     }
00955 }
00956 
00957 /*****************************************************************************
00958  * HashString: our cool hash function
00959  *****************************************************************************
00960  * This function is not intended to be crypto-secure, we only want it to be
00961  * fast and not suck too much. This one is pretty fast and did 0 collisions
00962  * in wenglish's dictionary.
00963  *****************************************************************************/
00964 static uint32_t HashString( const char *psz_string )
00965 {
00966     uint32_t i_hash = 0;
00967 
00968     while( *psz_string )
00969     {
00970         i_hash += *psz_string++;
00971         i_hash += i_hash << 10;
00972         i_hash ^= i_hash >> 8;
00973     }
00974 
00975     return i_hash;
00976 }
00977 
00978 /*****************************************************************************
00979  * Insert: find an empty slot to insert a new variable
00980  *****************************************************************************
00981  * We use a recursive inner function indexed on the hash. This function does
00982  * nothing in the rare cases where a collision may occur, see Lookup()
00983  * to see how we handle them.
00984  * XXX: does this really need to be written recursively?
00985  *****************************************************************************/
00986 static int Insert( variable_t *p_vars, int i_count, const char *psz_name )
00987 {
00988     if( i_count == 0 )
00989     {
00990         return 0;
00991     }
00992 
00993     return InsertInner( p_vars, i_count, HashString( psz_name ) );
00994 }
00995 
00996 static int InsertInner( variable_t *p_vars, int i_count, uint32_t i_hash )
00997 {
00998     int i_middle;
00999 
01000     if( i_hash <= p_vars[0].i_hash )
01001     {
01002         return 0;
01003     }
01004 
01005     if( i_hash >= p_vars[i_count - 1].i_hash )
01006     {
01007         return i_count;
01008     }
01009 
01010     i_middle = i_count / 2;
01011 
01012     /* We know that 0 < i_middle */
01013     if( i_hash < p_vars[i_middle].i_hash )
01014     {
01015         return InsertInner( p_vars, i_middle, i_hash );
01016     }
01017 
01018     /* We know that i_middle + 1 < i_count */
01019     if( i_hash > p_vars[i_middle + 1].i_hash )
01020     {
01021         return i_middle + 1 + InsertInner( p_vars + i_middle + 1,
01022                                            i_count - i_middle - 1,
01023                                            i_hash );
01024     }
01025 
01026     return i_middle + 1;
01027 }
01028 
01029 /*****************************************************************************
01030  * Lookup: find an existing variable given its name
01031  *****************************************************************************
01032  * We use a recursive inner function indexed on the hash. Care is taken of
01033  * possible hash collisions.
01034  * XXX: does this really need to be written recursively?
01035  *****************************************************************************/
01036 static int Lookup( variable_t *p_vars, int i_count, const char *psz_name )
01037 {
01038     uint32_t i_hash;
01039     int i, i_pos;
01040 
01041     if( i_count == 0 )
01042     {
01043         return -1;
01044     }
01045 
01046     i_hash = HashString( psz_name );
01047 
01048     i_pos = LookupInner( p_vars, i_count, i_hash );
01049 
01050     /* Hash not found */
01051     if( i_hash != p_vars[i_pos].i_hash )
01052     {
01053         return -1;
01054     }
01055 
01056     /* Hash found, entry found */
01057     if( !strcmp( psz_name, p_vars[i_pos].psz_name ) )
01058     {
01059         return i_pos;
01060     }
01061 
01062     /* Hash collision! This should be very rare, but we cannot guarantee
01063      * it will never happen. Just do an exhaustive search amongst all
01064      * entries with the same hash. */
01065     for( i = i_pos - 1 ; i > 0 && i_hash == p_vars[i].i_hash ; i-- )
01066     {
01067         if( !strcmp( psz_name, p_vars[i].psz_name ) )
01068         {
01069             return i;
01070         }
01071     }
01072 
01073     for( i = i_pos + 1 ; i < i_count && i_hash == p_vars[i].i_hash ; i++ )
01074     {
01075         if( !strcmp( psz_name, p_vars[i].psz_name ) )
01076         {
01077             return i;
01078         }
01079     }
01080 
01081     /* Hash found, but entry not found */
01082     return -1;
01083 }
01084 
01085 static int LookupInner( variable_t *p_vars, int i_count, uint32_t i_hash )
01086 {
01087     int i_middle;
01088 
01089     if( i_hash <= p_vars[0].i_hash )
01090     {
01091         return 0;
01092     }
01093 
01094     if( i_hash >= p_vars[i_count-1].i_hash )
01095     {
01096         return i_count - 1;
01097     }
01098 
01099     i_middle = i_count / 2;
01100 
01101     /* We know that 0 < i_middle */
01102     if( i_hash < p_vars[i_middle].i_hash )
01103     {
01104         return LookupInner( p_vars, i_middle, i_hash );
01105     }
01106 
01107     /* We know that i_middle + 1 < i_count */
01108     if( i_hash > p_vars[i_middle].i_hash )
01109     {
01110         return i_middle + LookupInner( p_vars + i_middle,
01111                                        i_count - i_middle,
01112                                        i_hash );
01113     }
01114 
01115     return i_middle;
01116 }
01117 
01118 /*****************************************************************************
01119  * CheckValue: check that a value is valid wrt. a variable
01120  *****************************************************************************
01121  * This function checks p_val's value against p_var's limitations such as
01122  * minimal and maximal value, step, in-list position, and modifies p_val if
01123  * necessary.
01124  *****************************************************************************/
01125 static void CheckValue ( variable_t *p_var, vlc_value_t *p_val )
01126 {
01127     /* Check that our variable is in the list */
01128     if( p_var->i_type & VLC_VAR_HASCHOICE && p_var->choices.i_count )
01129     {
01130         int i;
01131 
01132         /* FIXME: the list is sorted, dude. Use something cleverer. */
01133         for( i = p_var->choices.i_count ; i-- ; )
01134         {
01135             if( p_var->pf_cmp( *p_val, p_var->choices.p_values[i] ) == 0 )
01136             {
01137                 break;
01138             }
01139         }
01140 
01141         /* If not found, change it to anything vaguely valid */
01142         if( i < 0 )
01143         {
01144             /* Free the old variable, get the new one, dup it */
01145             p_var->pf_free( p_val );
01146             *p_val = p_var->choices.p_values[p_var->i_default >= 0
01147                                           ? p_var->i_default : 0 ];
01148             p_var->pf_dup( p_val );
01149         }
01150     }
01151 
01152     /* Check that our variable is within the bounds */
01153     switch( p_var->i_type & VLC_VAR_TYPE )
01154     {
01155         case VLC_VAR_INTEGER:
01156             if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.i_int
01157                  && (p_val->i_int % p_var->step.i_int) )
01158             {
01159                 p_val->i_int = (p_val->i_int + (p_var->step.i_int / 2))
01160                                / p_var->step.i_int * p_var->step.i_int;
01161             }
01162             if( p_var->i_type & VLC_VAR_HASMIN
01163                  && p_val->i_int < p_var->min.i_int )
01164             {
01165                 p_val->i_int = p_var->min.i_int;
01166             }
01167             if( p_var->i_type & VLC_VAR_HASMAX
01168                  && p_val->i_int > p_var->max.i_int )
01169             {
01170                 p_val->i_int = p_var->max.i_int;
01171             }
01172             break;
01173         case VLC_VAR_FLOAT:
01174             if( p_var->i_type & VLC_VAR_HASSTEP && p_var->step.f_float )
01175             {
01176                 float f_round = p_var->step.f_float * (float)(int)( 0.5 +
01177                                         p_val->f_float / p_var->step.f_float );
01178                 if( p_val->f_float != f_round )
01179                 {
01180                     p_val->f_float = f_round;
01181                 }
01182             }
01183             if( p_var->i_type & VLC_VAR_HASMIN
01184                  && p_val->f_float < p_var->min.f_float )
01185             {
01186                 p_val->f_float = p_var->min.f_float;
01187             }
01188             if( p_var->i_type & VLC_VAR_HASMAX
01189                  && p_val->f_float > p_var->max.f_float )
01190             {
01191                 p_val->f_float = p_var->max.f_float;
01192             }
01193             break;
01194         case VLC_VAR_TIME:
01195             /* FIXME: TODO */
01196             break;
01197     }
01198 }
01199 
01200 /*****************************************************************************
01201  * InheritValue: try to inherit the value of this variable from the same one
01202  *               in our closest parent.
01203  *****************************************************************************/
01204 static int InheritValue( vlc_object_t *p_this, const char *psz_name,
01205                          vlc_value_t *p_val, int i_type )
01206 {
01207     int i_var;
01208     variable_t *p_var;
01209 
01210     /* No need to take the structure lock,
01211      * we are only looking for our parents */
01212 
01213     if( !p_this->p_parent )
01214     {
01215         switch( i_type & VLC_VAR_TYPE )
01216         {
01217         case VLC_VAR_FILE:
01218         case VLC_VAR_DIRECTORY:
01219         case VLC_VAR_STRING:
01220         case VLC_VAR_MODULE:
01221             p_val->psz_string = config_GetPsz( p_this, psz_name );
01222             if( !p_val->psz_string ) p_val->psz_string = strdup("");
01223             break;
01224         case VLC_VAR_FLOAT:
01225             p_val->f_float = config_GetFloat( p_this, psz_name );
01226             break;
01227         case VLC_VAR_INTEGER:
01228         case VLC_VAR_HOTKEY:
01229             p_val->i_int = config_GetInt( p_this, psz_name );
01230             break;
01231         case VLC_VAR_BOOL:
01232             p_val->b_bool = config_GetInt( p_this, psz_name );
01233             break;
01234         case VLC_VAR_LIST:
01235         {
01236             char *psz_orig, *psz_var;
01237             vlc_list_t *p_list = malloc(sizeof(vlc_list_t));
01238             p_val->p_list = p_list;
01239             p_list->i_count = 0;
01240 
01241             psz_var = psz_orig = config_GetPsz( p_this, psz_name );
01242             while( psz_var && *psz_var )
01243             {
01244                 char *psz_item = psz_var;
01245                 vlc_value_t val;
01246                 while( *psz_var && *psz_var != ',' ) psz_var++;
01247                 if( *psz_var == ',' )
01248                 {
01249                     *psz_var = '\0';
01250                     psz_var++;
01251                 }
01252                 val.i_int = strtol( psz_item, NULL, 0 );
01253                 INSERT_ELEM( p_list->p_values, p_list->i_count,
01254                              p_list->i_count, val );
01255                 /* p_list->i_count is incremented twice by INSERT_ELEM */
01256                 p_list->i_count--;
01257                 INSERT_ELEM( p_list->pi_types, p_list->i_count,
01258                              p_list->i_count, VLC_VAR_INTEGER );
01259             }
01260             if( psz_orig ) free( psz_orig );
01261             break;
01262         }
01263         default:
01264             return VLC_ENOOBJ;
01265             break;
01266         }
01267 
01268         return VLC_SUCCESS;
01269     }
01270 
01271     /* Look for the variable */
01272     vlc_mutex_lock( &p_this->p_parent->var_lock );
01273 
01274     i_var = Lookup( p_this->p_parent->p_vars, p_this->p_parent->i_vars,
01275                     psz_name );
01276 
01277     if( i_var >= 0 )
01278     {
01279         /* We found it! */
01280         p_var = &p_this->p_parent->p_vars[i_var];
01281 
01282         /* Really get the variable */
01283         *p_val = p_var->val;
01284 
01285         /* Duplicate value if needed */
01286         p_var->pf_dup( p_val );
01287 
01288         vlc_mutex_unlock( &p_this->p_parent->var_lock );
01289         return VLC_SUCCESS;
01290     }
01291 
01292     vlc_mutex_unlock( &p_this->p_parent->var_lock );
01293 
01294     /* We're still not there */
01295 
01296     return InheritValue( p_this->p_parent, psz_name, p_val, i_type );
01297 }

Generated on Tue Dec 20 10:15:01 2005 for vlc-0.8.4a by  doxygen 1.4.2