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