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