]> git.sesse.net Git - vlc/blob - src/osd/osd.c
adc5e57f995dc0a1c176a256177f5cc9408464e1
[vlc] / src / osd / osd.c
1 /*****************************************************************************
2  * osd.c - The OSD Menu core code.
3  *****************************************************************************
4  * Copyright (C) 2005-2007 M2X
5  * $Id$
6  *
7  * Authors: Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #include <vlc/vlc.h>
29 #include <vlc_keys.h>
30 #include <vlc_osd.h>
31 #include <vlc_image.h>
32
33 #include "libvlc.h"
34
35 #undef OSD_MENU_DEBUG
36
37 /*****************************************************************************
38  * Local prototypes
39  *****************************************************************************/
40
41 static void osd_UpdateState( osd_menu_state_t *, int, int, int, int, picture_t * );
42 static inline osd_state_t *osd_VolumeStateChange( osd_state_t *, int );
43 static int osd_VolumeStep( vlc_object_t *, int, int );
44 static vlc_bool_t osd_isVisible( osd_menu_t *p_osd );
45 static osd_menu_t *osd_ParserLoad( vlc_object_t *, const char * );
46 static void osd_ParserUnload( vlc_object_t *, osd_menu_t * );
47
48 static vlc_bool_t osd_isVisible( osd_menu_t *p_osd )
49 {
50     vlc_value_t val;
51
52     var_Get( p_osd, "osd-menu-visible", &val );
53     return val.b_bool;
54 }
55
56 /*****************************************************************************
57  * Wrappers for loading and unloading osd parser modules.
58  *****************************************************************************/
59 static osd_menu_t *osd_ParserLoad( vlc_object_t *p_this, const char *psz_file )
60 {
61     osd_menu_t *p_menu;
62
63     p_menu = vlc_object_create( p_this, VLC_OBJECT_OSDMENU );
64     if( !p_menu )
65     {
66         msg_Err( p_this, "out of memory" );
67         return NULL;
68     }
69     vlc_object_attach( p_this, p_menu );
70
71     /* Stuff needed for Parser */
72     p_menu->psz_file = strdup( psz_file );
73     p_menu->p_image = image_HandlerCreate( p_this );
74     if( !p_menu->p_image || !p_menu->psz_file )
75     {
76         msg_Err( p_this, "unable to load images, aborting .." );
77         osd_ParserUnload( p_this, p_menu );
78         return NULL;
79     }
80     else
81     {
82         char *psz_type;
83         char *psz_ext = strrchr( p_menu->psz_file, '.' );
84
85         if( psz_ext && !strcmp( psz_ext, ".cfg") )
86             psz_type = "import-osd";
87         else
88             psz_type = "import-osd-xml";
89
90         p_menu->p_parser = module_Need( p_menu, "osd parser",
91                                         psz_type, VLC_TRUE );
92         if( !p_menu->p_parser )
93         {
94             osd_ParserUnload( p_this, p_menu );
95             return NULL;
96         }
97     }
98     return p_menu;
99 }
100
101 static void osd_ParserUnload( vlc_object_t *p_this, osd_menu_t *p_menu )
102 {
103     if( p_menu->p_parser )
104     {
105         module_Unneed( p_menu, p_menu->p_parser );
106     }
107     p_menu->p_parser = NULL;
108
109     if( p_menu->p_image )
110         image_HandlerDelete( p_menu->p_image );
111     if( p_menu->psz_file )
112         free( p_menu->psz_file );
113
114     vlc_object_detach( p_menu );
115     vlc_object_destroy( p_menu );
116     p_menu = NULL;
117 }
118
119 /*****************************************************************************
120  * OSD menu Funtions
121  *****************************************************************************/
122 osd_menu_t *__osd_MenuCreate( vlc_object_t *p_this, const char *psz_file )
123 {
124     osd_menu_t  *p_osd = NULL;
125     vlc_value_t lockval;
126     int         i_volume = 0;
127     int         i_steps = 0;
128
129     /* to be sure to avoid multiple creation */
130     var_Create( p_this->p_libvlc, "osd_mutex", VLC_VAR_MUTEX );
131     var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
132     vlc_mutex_lock( lockval.p_address );
133
134     p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
135     if( p_osd == NULL )
136     {
137         vlc_value_t val;
138
139         /* Parse configuration file */
140         p_osd = osd_ParserLoad( p_this, psz_file );
141         if( !p_osd )
142             goto error;
143
144         /* Setup default button (first button) */
145         p_osd->p_state->p_visible = p_osd->p_button;
146         p_osd->p_state->p_visible->p_current_state =
147             osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );
148         p_osd->i_width  = p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch;
149         p_osd->i_height = p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines;
150
151         if( p_osd->p_state->p_volume )
152         {
153             /* Update the volume state images to match the current volume */
154             i_volume = config_GetInt( p_this, "volume" );
155             i_steps = osd_VolumeStep( p_this, i_volume, p_osd->p_state->p_volume->i_ranges );
156             p_osd->p_state->p_volume->p_current_state = osd_VolumeStateChange(
157                                     p_osd->p_state->p_volume->p_states, i_steps );
158         }
159         /* Initialize OSD state */
160         osd_UpdateState( p_osd->p_state, p_osd->i_x, p_osd->i_y,
161                          p_osd->i_width, p_osd->i_height, NULL );
162
163         vlc_object_yield( p_osd );
164         vlc_object_attach( p_osd, p_this->p_libvlc );
165
166         /* Signal when an update of OSD menu is needed */
167         var_Create( p_osd, "osd-menu-update", VLC_VAR_BOOL );
168         var_Create( p_osd, "osd-menu-visible", VLC_VAR_BOOL );
169
170         val.b_bool = VLC_FALSE;
171         var_Set( p_osd, "osd-menu-update", val );
172         var_Set( p_osd, "osd-menu-visible", val );
173     }
174     vlc_mutex_unlock( lockval.p_address );
175     return p_osd;
176
177 error:
178     msg_Err( p_this, "creating OSD menu object failed" );
179
180     if( p_osd->p_image )
181         image_HandlerDelete( p_osd->p_image );
182     if( p_osd->psz_file )
183         free( p_osd->psz_file );
184
185     vlc_mutex_unlock( lockval.p_address );
186     vlc_object_destroy( p_osd );
187     vlc_mutex_unlock( lockval.p_address );
188     return NULL;
189 }
190
191 void __osd_MenuDelete( vlc_object_t *p_this, osd_menu_t *p_osd )
192 {
193     vlc_value_t lockval;
194
195     if( !p_osd || !p_this ) return;
196
197     var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
198     vlc_mutex_lock( lockval.p_address );
199
200     vlc_object_release( p_osd );
201     if( p_osd->p_internals->i_refcount > 0 )
202     {
203         vlc_mutex_unlock( lockval.p_address );
204         return;
205     }
206
207     var_Destroy( p_osd, "osd-menu-visible" );
208     var_Destroy( p_osd, "osd-menu-update" );
209
210     osd_ParserUnload( p_this, p_osd );
211
212     vlc_mutex_unlock( lockval.p_address );
213 }
214
215 osd_state_t *__osd_StateChange( osd_state_t *p_states, const int i_state )
216 {
217     osd_state_t *p_current = p_states;
218     osd_state_t *p_temp = NULL;
219     int i = 0;
220
221     for( i=0; p_current != NULL; i++ )
222     {
223         if( p_current->i_state == i_state )
224             return p_current;
225         p_temp = p_current->p_next;
226         p_current = p_temp;
227     }
228     return p_states;
229 }
230
231 /* The volume can be modified in another interface while the OSD Menu
232  * has not been instantiated yet. This routines updates the "volume OSD menu item"
233  * to reflect the current state of the GUI.
234  */
235 static inline osd_state_t *osd_VolumeStateChange( osd_state_t *p_current, int i_steps )
236 {
237     osd_state_t *p_temp = NULL;
238     int i;
239
240     if( i_steps < 0 ) i_steps = 0;
241
242     for( i=0; (i < i_steps) && (p_current != NULL); i++ )
243     {
244         p_temp = p_current->p_next;
245         if( !p_temp ) return p_current;
246         p_current = p_temp;
247     }
248     return (!p_temp) ? p_current : p_temp;
249 }
250
251 /* Update the state of the OSD Menu */
252 static void osd_UpdateState( osd_menu_state_t *p_state, int i_x, int i_y,
253         int i_width, int i_height, picture_t *p_pic )
254 {
255     p_state->i_x = i_x;
256     p_state->i_y = i_y;
257     p_state->i_width = i_width;
258     p_state->i_height = i_height;
259     p_state->p_pic = p_pic;
260 }
261
262 void __osd_MenuShow( vlc_object_t *p_this )
263 {
264     osd_menu_t *p_osd = NULL;
265     osd_button_t *p_button = NULL;
266     vlc_value_t lockval;
267
268     p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
269     if( p_osd == NULL )
270     {
271         msg_Err( p_this, "osd_MenuNext failed" );
272         return;
273     }
274
275     var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
276     vlc_mutex_lock( lockval.p_address );
277
278 #if defined(OSD_MENU_DEBUG)
279     msg_Dbg( p_osd, "menu on" );
280 #endif
281     p_button = p_osd->p_state->p_visible;
282     if( p_button )
283     {
284         if( !p_button->b_range )
285             p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_UNSELECT );
286         p_osd->p_state->p_visible = p_osd->p_button;
287
288         if( !p_osd->p_state->p_visible->b_range )
289             p_osd->p_state->p_visible->p_current_state =
290                 osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );
291
292         osd_UpdateState( p_osd->p_state,
293                 p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y,
294                 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,
295                 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
296                 p_osd->p_state->p_visible->p_current_state->p_pic );
297         osd_SetMenuUpdate( p_osd, VLC_TRUE );
298     }
299     osd_SetMenuVisible( p_osd, VLC_TRUE );
300
301     vlc_object_release( (vlc_object_t*) p_osd );
302     vlc_mutex_unlock( lockval.p_address );
303 }
304
305 void __osd_MenuHide( vlc_object_t *p_this )
306 {
307     osd_menu_t *p_osd = NULL;
308     vlc_value_t lockval;
309
310     p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
311     if( p_osd == NULL )
312     {
313         msg_Err( p_this, "osd_MenuNext failed" );
314         return;
315     }
316
317     var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
318     vlc_mutex_lock( lockval.p_address );
319
320 #if defined(OSD_MENU_DEBUG)
321     msg_Dbg( p_osd, "menu off" );
322 #endif
323     osd_UpdateState( p_osd->p_state,
324                 p_osd->p_state->i_x, p_osd->p_state->i_y,
325                 0, 0, NULL );
326     osd_SetMenuUpdate( p_osd, VLC_TRUE );
327
328     vlc_object_release( (vlc_object_t*) p_osd );
329     vlc_mutex_unlock( lockval.p_address );
330 }
331
332 void __osd_MenuActivate( vlc_object_t *p_this )
333 {
334     osd_menu_t *p_osd = NULL;
335     osd_button_t *p_button = NULL;
336     vlc_value_t lockval;
337
338     p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
339     if( p_osd == NULL )
340     {
341         msg_Err( p_this, "osd_MenuNext failed" );
342         return;
343     }
344
345     if( osd_isVisible( p_osd ) == VLC_FALSE )
346     {
347         vlc_object_release( (vlc_object_t*) p_osd );
348         return;
349     }
350
351     var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
352     vlc_mutex_lock( lockval.p_address );
353
354 #if defined(OSD_MENU_DEBUG)
355     msg_Dbg( p_osd, "select" );
356 #endif
357     p_button = p_osd->p_state->p_visible;
358     /*
359      * Is there a menu item above or below? If so, then select it.
360      */
361     if( p_button && p_button->p_up )
362     {
363         vlc_object_release( (vlc_object_t*) p_osd );
364         vlc_mutex_unlock( lockval.p_address );
365         __osd_MenuUp( p_this );   /* "menu select" means go to menu item above. */
366         return;
367     }
368     if( p_button && p_button->p_down )
369     {
370         vlc_object_release( (vlc_object_t*) p_osd );
371         vlc_mutex_unlock( lockval.p_address );
372         __osd_MenuDown( p_this ); /* "menu select" means go to menu item below. */
373         return;
374     }
375
376     if( p_button && !p_button->b_range )
377     {
378         p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_PRESSED );
379         osd_UpdateState( p_osd->p_state,
380                 p_button->i_x, p_button->i_y,
381                 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,
382                 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
383                 p_button->p_current_state->p_pic );
384         osd_SetMenuUpdate( p_osd, VLC_TRUE );
385         osd_SetMenuVisible( p_osd, VLC_TRUE );
386         osd_SetKeyPressed( VLC_OBJECT(p_osd->p_libvlc), config_GetInt( p_osd, p_button->psz_action ) );
387 #if defined(OSD_MENU_DEBUG)
388         msg_Dbg( p_osd, "select (%d, %s)", config_GetInt( p_osd, p_button->psz_action ), p_button->psz_action );
389 #endif
390     }
391     vlc_object_release( (vlc_object_t*) p_osd );
392     vlc_mutex_unlock( lockval.p_address );
393 }
394
395 void __osd_MenuNext( vlc_object_t *p_this )
396 {
397     osd_menu_t *p_osd = NULL;
398     osd_button_t *p_button = NULL;
399     vlc_value_t lockval;
400
401     p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
402     if( p_osd == NULL )
403     {
404         msg_Err( p_this, "osd_MenuNext failed" );
405         return;
406     }
407
408     if( osd_isVisible( p_osd ) == VLC_FALSE )
409     {
410         vlc_object_release( (vlc_object_t*) p_osd );
411         return;
412     }
413
414     var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
415     vlc_mutex_lock( lockval.p_address );
416
417     p_button = p_osd->p_state->p_visible;
418     if( p_button )
419     {
420         if( !p_button->b_range )
421             p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_UNSELECT );
422         if( p_button->p_next )
423             p_osd->p_state->p_visible = p_button->p_next;
424         else
425             p_osd->p_state->p_visible = p_osd->p_button;
426
427         if( !p_osd->p_state->p_visible->b_range )
428             p_osd->p_state->p_visible->p_current_state =
429                 osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );
430
431         osd_UpdateState( p_osd->p_state,
432                 p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y,
433                 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,
434                 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
435                 p_osd->p_state->p_visible->p_current_state->p_pic );
436         osd_SetMenuUpdate( p_osd, VLC_TRUE );
437     }
438 #if defined(OSD_MENU_DEBUG)
439     msg_Dbg( p_osd, "direction right [button %s]", p_osd->p_state->p_visible->psz_action );
440 #endif
441
442     vlc_object_release( (vlc_object_t*) p_osd );
443     vlc_mutex_unlock( lockval.p_address );
444 }
445
446 void __osd_MenuPrev( vlc_object_t *p_this )
447 {
448     osd_menu_t *p_osd = NULL;
449     osd_button_t *p_button = NULL;
450     vlc_value_t lockval;
451
452     p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
453     if( p_osd == NULL )
454     {
455         msg_Err( p_this, "osd_MenuPrev failed" );
456         return;
457     }
458
459     if( osd_isVisible( p_osd ) == VLC_FALSE )
460     {
461         vlc_object_release( (vlc_object_t*) p_osd );
462         return;
463     }
464
465     var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
466     vlc_mutex_lock( lockval.p_address );
467
468     p_button = p_osd->p_state->p_visible;
469     if( p_button )
470     {
471         if( !p_button->b_range )
472             p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_UNSELECT );
473         if( p_button->p_prev )
474             p_osd->p_state->p_visible = p_button->p_prev;
475         else
476             p_osd->p_state->p_visible = p_osd->p_last_button;
477
478         if( !p_osd->p_state->p_visible->b_range )
479             p_osd->p_state->p_visible->p_current_state =
480                 osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );
481
482         osd_UpdateState( p_osd->p_state,
483                 p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y,
484                 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,
485                 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
486                 p_osd->p_state->p_visible->p_current_state->p_pic );
487         osd_SetMenuUpdate( p_osd, VLC_TRUE );
488     }
489 #if defined(OSD_MENU_DEBUG)
490     msg_Dbg( p_osd, "direction left [button %s]", p_osd->p_state->p_visible->psz_action );
491 #endif
492
493     vlc_object_release( (vlc_object_t*) p_osd );
494     vlc_mutex_unlock( lockval.p_address );
495 }
496
497 void __osd_MenuUp( vlc_object_t *p_this )
498 {
499     osd_menu_t *p_osd = NULL;
500     osd_button_t *p_button = NULL;
501     vlc_value_t lockval;
502 #if defined(OSD_MENU_DEBUG)
503     vlc_value_t val;
504 #endif
505     p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
506     if( p_osd == NULL )
507     {
508         msg_Err( p_this, "osd_MenuDown failed" );
509         return;
510     }
511
512     if( osd_isVisible( p_osd ) == VLC_FALSE )
513     {
514         vlc_object_release( (vlc_object_t*) p_osd );
515         return;
516     }
517
518     var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
519     vlc_mutex_lock( lockval.p_address );
520
521     p_button = p_osd->p_state->p_visible;
522     if( p_button )
523     {
524         if( !p_button->b_range )
525         {
526             p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_SELECT );
527             if( p_button->p_up )
528                 p_osd->p_state->p_visible = p_button->p_up;
529         }
530
531         if( p_button->b_range && p_osd->p_state->p_visible->b_range )
532         {
533             osd_state_t *p_temp = p_osd->p_state->p_visible->p_current_state;
534             if( p_temp && p_temp->p_next )
535                 p_osd->p_state->p_visible->p_current_state = p_temp->p_next;
536         }
537         else if( !p_osd->p_state->p_visible->b_range )
538         {
539             p_osd->p_state->p_visible->p_current_state =
540                 osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );
541         }
542
543         osd_UpdateState( p_osd->p_state,
544                 p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y,
545                 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,
546                 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
547                 p_osd->p_state->p_visible->p_current_state->p_pic );
548         osd_SetMenuUpdate( p_osd, VLC_TRUE );
549         /* If this is a range style action with associated images of only one state,
550             * then perform "menu select" on every menu navigation
551             */
552         if( p_button->b_range )
553         {
554             osd_SetKeyPressed( VLC_OBJECT(p_osd->p_libvlc), config_GetInt(p_osd, p_button->psz_action) );
555 #if defined(OSD_MENU_DEBUG)
556             msg_Dbg( p_osd, "select (%d, %s)", val.i_int, p_button->psz_action );
557 #endif
558         }
559     }
560 #if defined(OSD_MENU_DEBUG)
561     msg_Dbg( p_osd, "direction up [button %s]", p_osd->p_state->p_visible->psz_action );
562 #endif
563
564     vlc_object_release( (vlc_object_t*) p_osd );
565     vlc_mutex_unlock( lockval.p_address );
566 }
567
568 void __osd_MenuDown( vlc_object_t *p_this )
569 {
570     osd_menu_t *p_osd = NULL;
571     osd_button_t *p_button = NULL;
572     vlc_value_t lockval;
573 #if defined(OSD_MENU_DEBUG)
574     vlc_value_t val;
575 #endif
576
577     p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
578     if( p_osd == NULL )
579     {
580         msg_Err( p_this, "osd_MenuDown failed" );
581         return;
582     }
583
584     if( osd_isVisible( p_osd ) == VLC_FALSE )
585     {
586         vlc_object_release( (vlc_object_t*) p_osd );
587         return;
588     }
589
590     var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
591     vlc_mutex_lock( lockval.p_address );
592
593     p_button = p_osd->p_state->p_visible;
594     if( p_button )
595     {
596         if( !p_button->b_range )
597         {
598             p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_SELECT );
599             if( p_button->p_down )
600                 p_osd->p_state->p_visible = p_button->p_down;
601         }
602
603         if( p_button->b_range && p_osd->p_state->p_visible->b_range )
604         {
605             osd_state_t *p_temp = p_osd->p_state->p_visible->p_current_state;
606             if( p_temp && p_temp->p_prev )
607                 p_osd->p_state->p_visible->p_current_state = p_temp->p_prev;
608         }
609         else if( !p_osd->p_state->p_visible->b_range )
610         {
611             p_osd->p_state->p_visible->p_current_state =
612                 osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );
613         }
614
615         osd_UpdateState( p_osd->p_state,
616                 p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y,
617                 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,
618                 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
619                 p_osd->p_state->p_visible->p_current_state->p_pic );
620         osd_SetMenuUpdate( p_osd, VLC_TRUE );
621         /* If this is a range style action with associated images of only one state,
622          * then perform "menu select" on every menu navigation
623          */
624         if( p_button->b_range )
625         {
626             osd_SetKeyPressed( VLC_OBJECT(p_osd->p_libvlc), config_GetInt(p_osd, p_button->psz_action_down) );
627 #if defined(OSD_MENU_DEBUG)
628             msg_Dbg( p_osd, "select (%d, %s)", val.i_int, p_button->psz_action_down );
629 #endif
630         }
631     }
632 #if defined(OSD_MENU_DEBUG)
633     msg_Dbg( p_osd, "direction down [button %s]", p_osd->p_state->p_visible->psz_action );
634 #endif
635
636     vlc_object_release( (vlc_object_t*) p_osd );
637     vlc_mutex_unlock( lockval.p_address );
638 }
639
640 static int osd_VolumeStep( vlc_object_t *p_this, int i_volume, int i_steps )
641 {
642     int i_volume_step = 0;
643     (void)i_steps;
644
645     i_volume_step = config_GetInt( p_this->p_libvlc, "volume-step" );
646     return (i_volume/i_volume_step);
647 }
648
649 /**
650  * Display current audio volume bitmap
651  *
652  * The OSD Menu audio volume bar is updated to reflect the new audio volume. Call this function
653  * when the audio volume is updated outside the OSD menu command "menu up", "menu down" or "menu select".
654  */
655 void __osd_Volume( vlc_object_t *p_this )
656 {
657     osd_menu_t *p_osd = NULL;
658     osd_button_t *p_button = NULL;
659     vlc_value_t lockval;
660     int i_volume = 0;
661     int i_steps = 0;
662
663     p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
664     if( p_osd == NULL )
665     {
666         msg_Err( p_this, "OSD menu volume update failed" );
667         return;
668     }
669
670     if( p_osd->p_state && p_osd->p_state->p_volume )
671     {
672         var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
673         vlc_mutex_lock( lockval.p_address );
674
675         p_button = p_osd->p_state->p_volume;
676         if( p_osd->p_state->p_volume )
677             p_osd->p_state->p_visible = p_osd->p_state->p_volume;
678         if( p_button && p_button->b_range )
679         {
680             /* Update the volume state images to match the current volume */
681             i_volume = config_GetInt( p_this, "volume" );
682             i_steps = osd_VolumeStep( p_this, i_volume, p_button->i_ranges );
683             p_button->p_current_state = osd_VolumeStateChange( p_button->p_states, i_steps );
684
685             osd_UpdateState( p_osd->p_state,
686                     p_button->i_x, p_button->i_y,
687                     p_button->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,
688                     p_button->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
689                     p_button->p_current_state->p_pic );
690             osd_SetMenuUpdate( p_osd, VLC_TRUE );
691             osd_SetMenuVisible( p_osd, VLC_TRUE );
692         }
693         vlc_object_release( (vlc_object_t*) p_osd );
694         vlc_mutex_unlock( lockval.p_address );
695     }
696 }