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