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