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