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