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