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