]> git.sesse.net Git - vlc/blob - src/misc/variables.c
* ./Makefile.am: fixed missing build dependencies for the Mozilla plugin.
[vlc] / src / misc / variables.c
1 /*****************************************************************************
2  * variables.c: routines for object variables handling
3  *****************************************************************************
4  * Copyright (C) 2002 VideoLAN
5  * $Id: variables.c,v 1.9 2002/10/28 13:25:56 sam Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <vlc/vlc.h>
28
29 #ifdef HAVE_STDLIB_H
30 #   include <stdlib.h>                                          /* realloc() */
31 #endif
32
33 /*****************************************************************************
34  * Private types
35  *****************************************************************************/
36 struct callback_entry_t
37 {
38     vlc_callback_t pf_callback;
39     void *         p_data;
40 };
41
42 /*****************************************************************************
43  * Local prototypes
44  *****************************************************************************/
45 static int GetUnused      ( vlc_object_t *, const char * );
46 static u32 HashString     ( const char * );
47 static int Insert         ( variable_t *, int, const char * );
48 static int InsertInner    ( variable_t *, int, u32 );
49 static int Lookup         ( variable_t *, int, const char * );
50 static int LookupInner    ( variable_t *, int, u32 );
51
52 static void CheckValue    ( variable_t *, vlc_value_t * );
53
54 /*****************************************************************************
55  * var_Create: initialize a vlc variable
56  *****************************************************************************
57  * We hash the given string and insert it into the sorted list. The insertion
58  * may require slow memory copies, but think about what we gain in the log(n)
59  * lookup phase when setting/getting the variable value!
60  *****************************************************************************/
61 int __var_Create( vlc_object_t *p_this, const char *psz_name, int i_type )
62 {
63     int i_new;
64     variable_t *p_var;
65
66     vlc_mutex_lock( &p_this->var_lock );
67
68     /* FIXME: if the variable already exists, we don't duplicate it. But we
69      * duplicate the lookups. It's not that serious, but if anyone finds some
70      * time to rework Insert() so that only one lookup has to be done, feel
71      * free to do so. */
72     i_new = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
73
74     if( i_new >= 0 )
75     {
76         /* If the types differ, variable creation failed. */
77         if( i_type != p_this->p_vars[i_new].i_type )
78         {
79             vlc_mutex_unlock( &p_this->var_lock );
80             return VLC_EBADVAR;
81         }
82
83         p_this->p_vars[i_new].i_usage++;
84         vlc_mutex_unlock( &p_this->var_lock );
85         return VLC_SUCCESS;
86     }
87
88     i_new = Insert( p_this->p_vars, p_this->i_vars, psz_name );
89
90     if( (p_this->i_vars & 15) == 15 )
91     {
92         p_this->p_vars = realloc( p_this->p_vars,
93                                   (p_this->i_vars+17) * sizeof(variable_t) );
94     }
95
96     memmove( p_this->p_vars + i_new + 1,
97              p_this->p_vars + i_new,
98              (p_this->i_vars - i_new) * sizeof(variable_t) );
99
100     p_this->i_vars++;
101
102     p_var = &p_this->p_vars[i_new];
103
104     p_var->i_hash = HashString( psz_name );
105     p_var->psz_name = strdup( psz_name );
106
107     p_var->i_type = i_type;
108     memset( &p_var->val, 0, sizeof(vlc_value_t) );
109
110     p_var->i_usage = 1;
111
112     p_var->b_min = VLC_FALSE;
113     p_var->b_max = VLC_FALSE;
114     p_var->b_step = VLC_FALSE;
115     p_var->b_select = VLC_FALSE;
116     p_var->p_choice = NULL;
117     p_var->b_incallback = VLC_FALSE;
118
119     p_var->i_entries = 0;
120     p_var->p_entries = NULL;
121
122     /* Always initialize the variable */
123     switch( i_type )
124     {
125         case VLC_VAR_BOOL:
126             p_var->val.b_bool = VLC_FALSE;
127             break;
128         case VLC_VAR_INTEGER:
129             p_var->val.i_int = 0;
130             break;
131         case VLC_VAR_STRING:
132         case VLC_VAR_MODULE:
133         case VLC_VAR_FILE:
134             p_var->val.psz_string = strdup( "" );
135             break;
136         case VLC_VAR_FLOAT:
137             p_var->val.f_float = 0.0;
138             break;
139         case VLC_VAR_TIME:
140             /* TODO */
141             break;
142         case VLC_VAR_ADDRESS:
143         case VLC_VAR_COMMAND:
144             p_var->val.p_address = NULL;
145             break;
146         case VLC_VAR_MUTEX:
147             p_var->val.p_address = malloc( sizeof(vlc_mutex_t) );
148             vlc_mutex_init( p_this, (vlc_mutex_t*)p_var->val.p_address );
149             break;
150     }
151
152     vlc_mutex_unlock( &p_this->var_lock );
153
154     return VLC_SUCCESS;
155 }
156
157 /*****************************************************************************
158  * var_Destroy: destroy a vlc variable
159  *****************************************************************************
160  * Look for the variable and destroy it if it is found. As in var_Create we
161  * do a call to memmove() but we have performance counterparts elsewhere.
162  *****************************************************************************/
163 int __var_Destroy( vlc_object_t *p_this, const char *psz_name )
164 {
165     int i_var;
166     variable_t *p_var;
167
168     vlc_mutex_lock( &p_this->var_lock );
169
170     i_var = GetUnused( p_this, psz_name );
171     if( i_var < 0 )
172     {
173         vlc_mutex_unlock( &p_this->var_lock );
174         return i_var;
175     }
176
177     p_var = &p_this->p_vars[i_var];
178
179     if( p_var->i_usage > 1 )
180     {
181         p_var->i_usage--;
182         vlc_mutex_unlock( &p_this->var_lock );
183         return VLC_SUCCESS;
184     }
185
186     /* Free value if needed */
187     switch( p_var->i_type )
188     {
189         case VLC_VAR_STRING:
190         case VLC_VAR_MODULE:
191         case VLC_VAR_FILE:
192             free( p_var->val.psz_string );
193             break;
194
195         case VLC_VAR_MUTEX:
196             vlc_mutex_destroy( (vlc_mutex_t*)p_var->val.p_address );
197             free( p_var->val.p_address );
198             break;
199     }
200
201     /* Free callbacks if needed */
202     if( p_var->p_entries )
203     {
204         free( p_var->p_entries );
205     }
206
207     free( p_var->psz_name );
208
209     memmove( p_this->p_vars + i_var,
210              p_this->p_vars + i_var + 1,
211              (p_this->i_vars - i_var - 1) * sizeof(variable_t) );
212
213     if( (p_this->i_vars & 15) == 0 )
214     {
215         p_this->p_vars = realloc( p_this->p_vars,
216                           (p_this->i_vars) * sizeof( variable_t ) );
217     }
218
219     p_this->i_vars--;
220
221     vlc_mutex_unlock( &p_this->var_lock );
222
223     return VLC_SUCCESS;
224 }
225
226 /*****************************************************************************
227  * var_Change: perform an action on a variable
228  *****************************************************************************
229  * 
230  *****************************************************************************/
231 int __var_Change( vlc_object_t *p_this, const char *psz_name,
232                   int i_action, vlc_value_t *p_val )
233 {
234     int i_var;
235     variable_t *p_var;
236
237     vlc_mutex_lock( &p_this->var_lock );
238
239     i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
240
241     if( i_var < 0 )
242     {
243         vlc_mutex_unlock( &p_this->var_lock );
244         return VLC_ENOVAR;
245     }
246
247     p_var = &p_this->p_vars[i_var];
248
249     switch( i_action )
250     {
251         case VLC_VAR_SETMIN:
252             p_var->b_min = VLC_TRUE;
253             p_var->min = *p_val;
254             CheckValue( p_var, &p_var->val );
255             break;
256         case VLC_VAR_SETMAX:
257             p_var->b_max = VLC_TRUE;
258             p_var->max = *p_val;
259             CheckValue( p_var, &p_var->val );
260             break;
261         case VLC_VAR_SETSTEP:
262             p_var->b_step = VLC_TRUE;
263             p_var->step = *p_val;
264             CheckValue( p_var, &p_var->val );
265             break;
266
267         case VLC_VAR_SETCHOICE:
268             p_var->b_select = VLC_TRUE;
269             break;
270
271         default:
272             break;
273     }
274
275     vlc_mutex_unlock( &p_this->var_lock );
276
277     return VLC_SUCCESS;
278 }
279
280 /*****************************************************************************
281  * var_Type: request a variable's type, 0 if not found
282  *****************************************************************************
283  * 
284  *****************************************************************************/
285 int __var_Type( vlc_object_t *p_this, const char *psz_name )
286 {
287     int i_var, i_type;
288
289     vlc_mutex_lock( &p_this->var_lock );
290
291     i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
292
293     if( i_var < 0 )
294     {
295         vlc_mutex_unlock( &p_this->var_lock );
296         return 0;
297     }
298
299     i_type = p_this->p_vars[i_var].i_type;
300
301     vlc_mutex_unlock( &p_this->var_lock );
302
303     return i_type;
304 }
305
306 /*****************************************************************************
307  * var_Set: set a variable's value
308  *****************************************************************************
309  *
310  *****************************************************************************/
311 int __var_Set( vlc_object_t *p_this, const char *psz_name, vlc_value_t val )
312 {
313     int i_var;
314     variable_t *p_var;
315     vlc_value_t oldval;
316
317     vlc_mutex_lock( &p_this->var_lock );
318
319     i_var = GetUnused( p_this, psz_name );
320     if( i_var < 0 )
321     {
322         vlc_mutex_unlock( &p_this->var_lock );
323         return i_var;
324     }
325
326     p_var = &p_this->p_vars[i_var];
327
328     /* Duplicate data if needed */
329     switch( p_var->i_type )
330     {
331         case VLC_VAR_STRING:
332         case VLC_VAR_MODULE:
333         case VLC_VAR_FILE:
334             val.psz_string = strdup( val.psz_string );
335             break;
336     }
337
338     /* Backup needed stuff */
339     oldval = p_var->val;
340
341     /* Check boundaries */
342     CheckValue( p_var, &val );
343
344     /* Deal with callbacks. Tell we're in a callback, release the lock,
345      * call stored functions, retake the lock. */
346     if( p_var->i_entries )
347     {
348         int i_var;
349         int i_entries = p_var->i_entries;
350         callback_entry_t *p_entries = p_var->p_entries;
351
352         p_var->b_incallback = VLC_TRUE;
353         vlc_mutex_unlock( &p_this->var_lock );
354
355         /* The real calls */
356         for( ; i_entries-- ; )
357         {
358             p_entries[i_entries].pf_callback( p_this, psz_name, oldval, val,
359                                               p_entries[i_entries].p_data );
360         }
361
362         vlc_mutex_lock( &p_this->var_lock );
363
364         i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
365         if( i_var < 0 )
366         {
367             msg_Err( p_this, "variable %s has disappeared" );
368             vlc_mutex_unlock( &p_this->var_lock );
369             return VLC_ENOVAR;
370         }
371
372         p_var = &p_this->p_vars[i_var];
373         p_var->b_incallback = VLC_FALSE;
374     }
375
376     /* Set the variable */
377     p_var->val = val;
378
379     /* Free data if needed */
380     switch( p_var->i_type )
381     {
382         case VLC_VAR_STRING:
383         case VLC_VAR_MODULE:
384         case VLC_VAR_FILE:
385             free( oldval.psz_string );
386             break;
387     }
388
389     vlc_mutex_unlock( &p_this->var_lock );
390
391     return VLC_SUCCESS;
392 }
393
394 /*****************************************************************************
395  * var_Get: get a variable's value
396  *****************************************************************************
397  *
398  *****************************************************************************/
399 int __var_Get( vlc_object_t *p_this, const char *psz_name, vlc_value_t *p_val )
400 {
401     int i_var;
402     variable_t *p_var;
403
404     vlc_mutex_lock( &p_this->var_lock );
405
406     i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
407
408     if( i_var < 0 )
409     {
410         vlc_mutex_unlock( &p_this->var_lock );
411         return VLC_ENOVAR;
412     }
413
414     p_var = &p_this->p_vars[i_var];
415
416     /* Some variables trigger special behaviour. */
417     switch( p_var->i_type )
418     {
419         case VLC_VAR_COMMAND:
420             if( p_var->val.p_address )
421             {
422                 /* We need to save data before releasing the lock */
423                 int i_ret;
424                 int (*pf_command) (vlc_object_t *, char *, char *) =
425                                           p_var->val.p_address;
426                 char *psz_cmd = strdup( p_var->psz_name );
427                 char *psz_arg = strdup( p_val->psz_string );
428
429                 vlc_mutex_unlock( &p_this->var_lock );
430
431                 i_ret = pf_command( p_this, psz_cmd, psz_arg );
432                 free( psz_cmd );
433                 free( psz_arg );
434
435                 return i_ret;
436             }
437             break;
438     }
439
440     /* Really set the variable */
441     *p_val = p_var->val;
442
443     /* Duplicate value if needed */
444     switch( p_var->i_type )
445     {
446         case VLC_VAR_STRING:
447         case VLC_VAR_MODULE:
448         case VLC_VAR_FILE:
449             if( p_val->psz_string )
450             {
451                 p_val->psz_string = strdup( p_val->psz_string );
452             }
453             break;
454     }
455
456     vlc_mutex_unlock( &p_this->var_lock );
457
458     return VLC_SUCCESS;
459 }
460
461 /*****************************************************************************
462  * var_AddCallback: register a callback in a variable
463  *****************************************************************************
464  * We store a function pointer pf_callback that will be called upon variable
465  * modification. p_data is a generic pointer that will be passed as additional
466  * argument to the callback function.
467  *****************************************************************************/
468 int __var_AddCallback( vlc_object_t *p_this, const char *psz_name,
469                        vlc_callback_t pf_callback, void *p_data )
470 {
471     int i_entry, i_var;
472     variable_t *p_var;
473
474     vlc_mutex_lock( &p_this->var_lock );
475
476     i_var = GetUnused( p_this, psz_name );
477     if( i_var < 0 )
478     {
479         vlc_mutex_unlock( &p_this->var_lock );
480         return i_var;
481     }
482
483     p_var = &p_this->p_vars[i_var];
484     i_entry = p_var->i_entries++;
485
486     if( i_entry )
487     {
488         p_var->p_entries = realloc( p_var->p_entries,
489                                sizeof( callback_entry_t ) * p_var->i_entries );
490     }
491     else
492     {
493         p_var->p_entries = malloc( sizeof( callback_entry_t ) );
494     }
495
496     p_var->p_entries[ i_entry ].pf_callback = pf_callback;
497     p_var->p_entries[ i_entry ].p_data = p_data;
498         
499     vlc_mutex_unlock( &p_this->var_lock );
500
501     return VLC_SUCCESS;
502 }
503
504 /*****************************************************************************
505  * var_DelCallback: remove a callback from a variable
506  *****************************************************************************
507  * pf_callback and p_data have to be given again, because different objects
508  * might have registered the same callback function.
509  *****************************************************************************/
510 int __var_DelCallback( vlc_object_t *p_this, const char *psz_name,
511                        vlc_callback_t pf_callback, void *p_data )
512 {
513     int i_entry, i_var;
514     variable_t *p_var;
515
516     vlc_mutex_lock( &p_this->var_lock );
517
518     i_var = GetUnused( p_this, psz_name );
519     if( i_var < 0 )
520     {
521         vlc_mutex_unlock( &p_this->var_lock );
522         return i_var;
523     }
524
525     p_var = &p_this->p_vars[i_var];
526
527     for( i_entry = p_var->i_entries ; i_entry-- ; )
528     {
529         if( p_var->p_entries[i_entry].pf_callback == pf_callback
530             || p_var->p_entries[i_entry].p_data == p_data )
531         {
532             break;
533         }
534     }
535
536     if( i_entry < 0 )
537     {
538         vlc_mutex_unlock( &p_this->var_lock );
539         return VLC_EGENERIC;
540     }
541
542     p_var->i_entries--;
543
544     memmove( p_var->p_entries + i_entry,
545              p_var->p_entries + i_entry + 1,
546              sizeof( callback_entry_t ) * ( p_var->i_entries - i_entry ) );
547
548     if( p_var->i_entries )
549     {
550         p_var->p_entries = realloc( p_var->p_entries,
551                        sizeof( callback_entry_t ) * ( p_var->i_entries ) );
552     }
553     else
554     {
555         free( p_var->p_entries );
556         p_var->p_entries = NULL;
557     }
558
559     vlc_mutex_unlock( &p_this->var_lock );
560
561     return VLC_SUCCESS;
562 }
563
564 /* Following functions are local */
565
566 /*****************************************************************************
567  * GetUnused: find an unused variable from its name
568  *****************************************************************************
569  * We do i_tries tries before giving up, just in case the variable is being
570  * modified and called from a callback.
571  *****************************************************************************/
572 static int GetUnused( vlc_object_t *p_this, const char *psz_name )
573 {
574     int i_var, i_tries = 0;
575
576     while( VLC_TRUE )
577     {
578         i_var = Lookup( p_this->p_vars, p_this->i_vars, psz_name );
579         if( i_var < 0 )
580         {
581             return VLC_ENOVAR;
582         }
583
584         if( ! p_this->p_vars[i_var].b_incallback )
585         {
586             return i_var;
587         }
588
589         if( i_tries++ > 100 )
590         {
591             msg_Err( p_this, "caught in a callback deadlock?" );
592             return VLC_ETIMEOUT;
593         }
594
595         vlc_mutex_unlock( &p_this->var_lock );
596         msleep( THREAD_SLEEP );
597         vlc_mutex_lock( &p_this->var_lock );
598     }
599 }
600
601 /*****************************************************************************
602  * HashString: our cool hash function
603  *****************************************************************************
604  * This function is not intended to be crypto-secure, we only want it to be
605  * fast and not suck too much. This one is pretty fast and did 0 collisions
606  * in wenglish's dictionary.
607  *****************************************************************************/
608 static u32 HashString( const char *psz_string )
609 {
610     u32 i_hash = 0;
611
612     while( *psz_string )
613     {
614         i_hash += *psz_string++;
615         i_hash += i_hash << 10;
616         i_hash ^= i_hash >> 8;
617     }
618
619     return i_hash;
620 }
621
622 /*****************************************************************************
623  * Insert: find an empty slot to insert a new variable
624  *****************************************************************************
625  * We use a recursive inner function indexed on the hash. This function does
626  * nothing in the rare cases where a collision may occur, see Lookup()
627  * to see how we handle them.
628  * XXX: does this really need to be written recursively?
629  *****************************************************************************/
630 static int Insert( variable_t *p_vars, int i_count, const char *psz_name )
631 {
632     if( i_count == 0 )
633     {
634         return 0;
635     }
636
637     return InsertInner( p_vars, i_count, HashString( psz_name ) );
638 }
639
640 static int InsertInner( variable_t *p_vars, int i_count, u32 i_hash )
641 {
642     int i_middle;
643
644     if( i_hash <= p_vars[0].i_hash )
645     {
646         return 0;
647     }
648
649     if( i_hash >= p_vars[i_count - 1].i_hash )
650     {
651         return i_count;
652     }
653
654     i_middle = i_count / 2;
655
656     /* We know that 0 < i_middle */
657     if( i_hash < p_vars[i_middle].i_hash )
658     {
659         return InsertInner( p_vars, i_middle, i_hash );
660     }
661
662     /* We know that i_middle + 1 < i_count */
663     if( i_hash > p_vars[i_middle + 1].i_hash )
664     {
665         return i_middle + 1 + InsertInner( p_vars + i_middle + 1,
666                                            i_count - i_middle - 1,
667                                            i_hash );
668     }
669
670     return i_middle + 1;
671 }
672
673 /*****************************************************************************
674  * Lookup: find an existing variable given its name
675  *****************************************************************************
676  * We use a recursive inner function indexed on the hash. Care is taken of
677  * possible hash collisions.
678  * XXX: does this really need to be written recursively?
679  *****************************************************************************/
680 static int Lookup( variable_t *p_vars, int i_count, const char *psz_name )
681 {
682     u32 i_hash;
683     int i, i_pos;
684
685     if( i_count == 0 )
686     {
687         return -1;
688     }
689
690     i_hash = HashString( psz_name );
691
692     i_pos = LookupInner( p_vars, i_count, i_hash );
693
694     /* Hash not found */
695     if( i_hash != p_vars[i_pos].i_hash )
696     {
697         return -1;
698     }
699
700     /* Hash found, entry found */
701     if( !strcmp( psz_name, p_vars[i_pos].psz_name ) )
702     {
703         return i_pos;
704     }
705
706     /* Hash collision! This should be very rare, but we cannot guarantee
707      * it will never happen. Just do an exhaustive search amongst all
708      * entries with the same hash. */
709     for( i = i_pos - 1 ; i > 0 && i_hash == p_vars[i].i_hash ; i-- )
710     {
711         if( !strcmp( psz_name, p_vars[i].psz_name ) )
712         {
713             return i;
714         }
715     }
716
717     for( i = i_pos + 1 ; i < i_count && i_hash == p_vars[i].i_hash ; i++ )
718     {
719         if( !strcmp( psz_name, p_vars[i].psz_name ) )
720         {
721             return i;
722         }
723     }
724
725     /* Hash found, but entry not found */
726     return -1;
727 }
728
729 static int LookupInner( variable_t *p_vars, int i_count, u32 i_hash )
730 {
731     int i_middle;
732
733     if( i_hash <= p_vars[0].i_hash )
734     {
735         return 0;
736     }
737
738     if( i_hash >= p_vars[i_count-1].i_hash )
739     {
740         return i_count - 1;
741     }
742
743     i_middle = i_count / 2;
744
745     /* We know that 0 < i_middle */
746     if( i_hash < p_vars[i_middle].i_hash )
747     {
748         return LookupInner( p_vars, i_middle, i_hash );
749     }
750
751     /* We know that i_middle + 1 < i_count */
752     if( i_hash > p_vars[i_middle].i_hash )
753     {
754         return i_middle + LookupInner( p_vars + i_middle,
755                                        i_count - i_middle,
756                                        i_hash );
757     }
758
759     return i_middle;
760 }
761
762 /*****************************************************************************
763  * CheckValue: check that a value is valid
764  *****************************************************************************
765  * This function checks p_val's value against p_var's limitations such as
766  * minimal and maximal value, step, in-list position, and changes p_val if
767  * necessary.
768  *****************************************************************************/
769 static void CheckValue ( variable_t *p_var, vlc_value_t *p_val )
770 {
771     switch( p_var->i_type )
772     {
773         case VLC_VAR_INTEGER:
774             if( p_var->b_step && p_var->step.i_int
775                               && (p_val->i_int % p_var->step.i_int) )
776             {
777                 p_val->i_int = (p_val->i_int + (p_var->step.i_int / 2))
778                                / p_var->step.i_int * p_var->step.i_int;
779             }
780             if( p_var->b_min && p_val->i_int < p_var->min.i_int )
781             {
782                 p_val->i_int = p_var->min.i_int;
783             }
784             if( p_var->b_max && p_val->i_int > p_var->max.i_int )
785             {
786                 p_val->i_int = p_var->max.i_int;
787             }
788             break;
789         case VLC_VAR_FLOAT:
790             if( p_var->b_step && p_var->step.f_float )
791             {
792                 float f_round = p_var->step.f_float * (float)(int)( 0.5 +
793                                         p_val->f_float / p_var->step.f_float );
794                 if( p_val->f_float != f_round )
795                 {
796                     p_val->f_float = f_round;
797                 }
798             }
799             if( p_var->b_min && p_val->f_float < p_var->min.f_float )
800             {
801                 p_val->f_float = p_var->min.f_float;
802             }
803             if( p_var->b_max && p_val->f_float > p_var->max.f_float )
804             {
805                 p_val->f_float = p_var->max.f_float;
806             }
807             break;
808         case VLC_VAR_TIME:
809             /* TODO */
810             break;
811     }
812 }
813