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