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