1 /*****************************************************************************
2 * osd.c - The OSD Menu core code.
3 *****************************************************************************
4 * Copyright (C) 2005-2007 M2X
7 * Authors: Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <vlc_image.h>
36 /*****************************************************************************
38 *****************************************************************************/
40 static void osd_UpdateState( osd_menu_state_t *, int, int, int, int, picture_t * );
41 static inline osd_state_t *osd_VolumeStateChange( osd_state_t *, int );
42 static int osd_VolumeStep( vlc_object_t *, int, int );
43 static vlc_bool_t osd_isVisible( osd_menu_t *p_osd );
44 static int osd_ParserLoad( vlc_object_t *, const char *, osd_menu_t ** );
45 static void osd_ParserUnload( vlc_object_t *, osd_menu_t ** );
47 static vlc_bool_t osd_isVisible( osd_menu_t *p_osd )
51 var_Get( p_osd, "osd-menu-visible", &val );
55 /*****************************************************************************
56 * Wrappers for loading and unloading osd parser modules.
57 *****************************************************************************/
58 static int osd_ParserLoad( vlc_object_t *p_this, const char *psz_file,
59 osd_menu_t **pp_menu )
61 osd_menu_t *p_menu = *pp_menu;
63 if( pp_menu && p_menu ) return VLC_EGENERIC;
65 p_menu = vlc_object_create( p_this, VLC_OBJECT_OSDMENU );
68 msg_Err( p_this, "out of memory" );
71 vlc_object_attach( p_this, p_menu );
73 /* Stuff needed for Parser */
74 p_menu->psz_file = strdup( psz_file );
75 p_menu->p_image = image_HandlerCreate( p_this );
76 if( !p_menu->p_image || !p_menu->psz_file )
78 msg_Err( p_this, "unable to load images, aborting .." );
79 osd_ParserUnload( p_this, pp_menu );
85 char *psz_ext = strrchr( p_menu->psz_file, '.' );
87 if( psz_ext && !strcmp( psz_ext, ".cfg") )
88 psz_type = "import-osd";
90 psz_type = "import-osd-xml";
92 p_menu->p_parser = module_Need( p_menu, "osd parser",
94 if( !p_menu->p_parser )
96 osd_ParserUnload( p_this, pp_menu );
103 static void osd_ParserUnload( vlc_object_t *p_this, osd_menu_t **pp_menu )
105 osd_menu_t *p_menu = (osd_menu_t *) *pp_menu;
107 if( p_menu->p_parser )
109 module_Unneed( p_menu, p_menu->p_parser );
111 p_menu->p_parser = NULL;
113 if( p_menu->p_image )
114 image_HandlerDelete( p_menu->p_image );
115 if( p_menu->psz_file )
116 free( p_menu->psz_file );
118 vlc_object_detach( p_menu );
119 vlc_object_destroy( p_menu );
123 /*****************************************************************************
125 *****************************************************************************/
126 osd_menu_t *__osd_MenuCreate( vlc_object_t *p_this, const char *psz_file )
128 osd_menu_t *p_osd = NULL;
133 /* to be sure to avoid multiple creation */
134 var_Create( p_this->p_libvlc, "osd_mutex", VLC_VAR_MUTEX );
135 var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
136 vlc_mutex_lock( lockval.p_address );
138 p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
143 /* Parse configuration file */
144 if( osd_ParserLoad( p_this, psz_file, &p_osd ) )
147 /* Setup default button (first button) */
148 p_osd->p_state->p_visible = p_osd->p_button;
149 p_osd->p_state->p_visible->p_current_state =
150 osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );
151 p_osd->i_width = p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch;
152 p_osd->i_height = p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines;
154 if( p_osd->p_state->p_volume )
156 /* Update the volume state images to match the current volume */
157 i_volume = config_GetInt( p_this, "volume" );
158 i_steps = osd_VolumeStep( p_this, i_volume, p_osd->p_state->p_volume->i_ranges );
159 p_osd->p_state->p_volume->p_current_state = osd_VolumeStateChange(
160 p_osd->p_state->p_volume->p_states, i_steps );
162 /* Initialize OSD state */
163 osd_UpdateState( p_osd->p_state, p_osd->i_x, p_osd->i_y,
164 p_osd->i_width, p_osd->i_height, NULL );
166 vlc_object_yield( p_osd );
167 vlc_object_attach( p_osd, p_this->p_libvlc );
169 /* Signal when an update of OSD menu is needed */
170 var_Create( p_osd, "osd-menu-update", VLC_VAR_BOOL );
171 var_Create( p_osd, "osd-menu-visible", VLC_VAR_BOOL );
173 val.b_bool = VLC_FALSE;
174 var_Set( p_osd, "osd-menu-update", val );
175 var_Set( p_osd, "osd-menu-visible", val );
177 vlc_mutex_unlock( lockval.p_address );
181 msg_Err( p_this, "creating OSD menu object failed" );
184 image_HandlerDelete( p_osd->p_image );
185 if( p_osd->psz_file )
186 free( p_osd->psz_file );
188 vlc_mutex_unlock( lockval.p_address );
189 vlc_object_destroy( p_osd );
190 vlc_mutex_unlock( lockval.p_address );
194 void __osd_MenuDelete( vlc_object_t *p_this, osd_menu_t *p_osd )
198 if( !p_osd || !p_this ) return;
200 var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
201 vlc_mutex_lock( lockval.p_address );
203 vlc_object_release( p_osd );
204 if( p_osd->p_internals->i_refcount > 0 )
206 vlc_mutex_unlock( lockval.p_address );
210 var_Destroy( p_osd, "osd-menu-visible" );
211 var_Destroy( p_osd, "osd-menu-update" );
213 osd_ParserUnload( p_this, &p_osd );
215 vlc_mutex_unlock( lockval.p_address );
218 osd_state_t *__osd_StateChange( osd_state_t *p_states, const int i_state )
220 osd_state_t *p_current = p_states;
221 osd_state_t *p_temp = NULL;
224 for( i=0; p_current != NULL; i++ )
226 if( p_current->i_state == i_state )
228 p_temp = p_current->p_next;
234 /* The volume can be modified in another interface while the OSD Menu
235 * has not been instantiated yet. This routines updates the "volume OSD menu item"
236 * to reflect the current state of the GUI.
238 static inline osd_state_t *osd_VolumeStateChange( osd_state_t *p_current, int i_steps )
240 osd_state_t *p_temp = NULL;
243 if( i_steps < 0 ) i_steps = 0;
245 for( i=0; (i < i_steps) && (p_current != NULL); i++ )
247 p_temp = p_current->p_next;
248 if( !p_temp ) return p_current;
251 return (!p_temp) ? p_current : p_temp;
254 /* Update the state of the OSD Menu */
255 static void osd_UpdateState( osd_menu_state_t *p_state, int i_x, int i_y,
256 int i_width, int i_height, picture_t *p_pic )
260 p_state->i_width = i_width;
261 p_state->i_height = i_height;
262 p_state->p_pic = p_pic;
265 void __osd_MenuShow( vlc_object_t *p_this )
267 osd_menu_t *p_osd = NULL;
268 osd_button_t *p_button = NULL;
271 p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
274 msg_Err( p_this, "osd_MenuNext failed" );
278 var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
279 vlc_mutex_lock( lockval.p_address );
281 #if defined(OSD_MENU_DEBUG)
282 msg_Dbg( p_osd, "menu on" );
284 p_button = p_osd->p_state->p_visible;
287 if( !p_button->b_range )
288 p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_UNSELECT );
289 p_osd->p_state->p_visible = p_osd->p_button;
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->p_states, OSD_BUTTON_SELECT );
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->p_pic->p[Y_PLANE].i_visible_pitch,
298 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
299 p_osd->p_state->p_visible->p_current_state->p_pic );
300 osd_SetMenuUpdate( p_osd, VLC_TRUE );
302 osd_SetMenuVisible( p_osd, VLC_TRUE );
304 vlc_object_release( (vlc_object_t*) p_osd );
305 vlc_mutex_unlock( lockval.p_address );
308 void __osd_MenuHide( vlc_object_t *p_this )
310 osd_menu_t *p_osd = NULL;
313 p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
316 msg_Err( p_this, "osd_MenuNext failed" );
320 var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
321 vlc_mutex_lock( lockval.p_address );
323 #if defined(OSD_MENU_DEBUG)
324 msg_Dbg( p_osd, "menu off" );
326 osd_UpdateState( p_osd->p_state,
327 p_osd->p_state->i_x, p_osd->p_state->i_y,
329 osd_SetMenuUpdate( p_osd, VLC_TRUE );
331 vlc_object_release( (vlc_object_t*) p_osd );
332 vlc_mutex_unlock( lockval.p_address );
335 void __osd_MenuActivate( vlc_object_t *p_this )
337 osd_menu_t *p_osd = NULL;
338 osd_button_t *p_button = NULL;
341 p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
344 msg_Err( p_this, "osd_MenuNext failed" );
348 if( osd_isVisible( p_osd ) == VLC_FALSE )
350 vlc_object_release( (vlc_object_t*) p_osd );
354 var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
355 vlc_mutex_lock( lockval.p_address );
357 #if defined(OSD_MENU_DEBUG)
358 msg_Dbg( p_osd, "select" );
360 p_button = p_osd->p_state->p_visible;
362 * Is there a menu item above or below? If so, then select it.
364 if( p_button && p_button->p_up )
366 vlc_object_release( (vlc_object_t*) p_osd );
367 vlc_mutex_unlock( lockval.p_address );
368 __osd_MenuUp( p_this ); /* "menu select" means go to menu item above. */
371 if( p_button && p_button->p_down )
373 vlc_object_release( (vlc_object_t*) p_osd );
374 vlc_mutex_unlock( lockval.p_address );
375 __osd_MenuDown( p_this ); /* "menu select" means go to menu item below. */
379 if( p_button && !p_button->b_range )
381 p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_PRESSED );
382 osd_UpdateState( p_osd->p_state,
383 p_button->i_x, p_button->i_y,
384 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,
385 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
386 p_button->p_current_state->p_pic );
387 osd_SetMenuUpdate( p_osd, VLC_TRUE );
388 osd_SetMenuVisible( p_osd, VLC_TRUE );
389 osd_SetKeyPressed( VLC_OBJECT(p_osd->p_libvlc), config_GetInt( p_osd, p_button->psz_action ) );
390 #if defined(OSD_MENU_DEBUG)
391 msg_Dbg( p_osd, "select (%d, %s)", config_GetInt( p_osd, p_button->psz_action ), p_button->psz_action );
394 vlc_object_release( (vlc_object_t*) p_osd );
395 vlc_mutex_unlock( lockval.p_address );
398 void __osd_MenuNext( vlc_object_t *p_this )
400 osd_menu_t *p_osd = NULL;
401 osd_button_t *p_button = NULL;
404 p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
407 msg_Err( p_this, "osd_MenuNext failed" );
411 if( osd_isVisible( p_osd ) == VLC_FALSE )
413 vlc_object_release( (vlc_object_t*) p_osd );
417 var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
418 vlc_mutex_lock( lockval.p_address );
420 p_button = p_osd->p_state->p_visible;
423 if( !p_button->b_range )
424 p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_UNSELECT );
425 if( p_button->p_next )
426 p_osd->p_state->p_visible = p_button->p_next;
428 p_osd->p_state->p_visible = p_osd->p_button;
430 if( !p_osd->p_state->p_visible->b_range )
431 p_osd->p_state->p_visible->p_current_state =
432 osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );
434 osd_UpdateState( p_osd->p_state,
435 p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y,
436 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,
437 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
438 p_osd->p_state->p_visible->p_current_state->p_pic );
439 osd_SetMenuUpdate( p_osd, VLC_TRUE );
441 #if defined(OSD_MENU_DEBUG)
442 msg_Dbg( p_osd, "direction right [button %s]", p_osd->p_state->p_visible->psz_action );
445 vlc_object_release( (vlc_object_t*) p_osd );
446 vlc_mutex_unlock( lockval.p_address );
449 void __osd_MenuPrev( vlc_object_t *p_this )
451 osd_menu_t *p_osd = NULL;
452 osd_button_t *p_button = NULL;
455 p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
458 msg_Err( p_this, "osd_MenuPrev failed" );
462 if( osd_isVisible( p_osd ) == VLC_FALSE )
464 vlc_object_release( (vlc_object_t*) p_osd );
468 var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
469 vlc_mutex_lock( lockval.p_address );
471 p_button = p_osd->p_state->p_visible;
474 if( !p_button->b_range )
475 p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_UNSELECT );
476 if( p_button->p_prev )
477 p_osd->p_state->p_visible = p_button->p_prev;
479 p_osd->p_state->p_visible = p_osd->p_last_button;
481 if( !p_osd->p_state->p_visible->b_range )
482 p_osd->p_state->p_visible->p_current_state =
483 osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );
485 osd_UpdateState( p_osd->p_state,
486 p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y,
487 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,
488 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
489 p_osd->p_state->p_visible->p_current_state->p_pic );
490 osd_SetMenuUpdate( p_osd, VLC_TRUE );
492 #if defined(OSD_MENU_DEBUG)
493 msg_Dbg( p_osd, "direction left [button %s]", p_osd->p_state->p_visible->psz_action );
496 vlc_object_release( (vlc_object_t*) p_osd );
497 vlc_mutex_unlock( lockval.p_address );
500 void __osd_MenuUp( vlc_object_t *p_this )
502 osd_menu_t *p_osd = NULL;
503 osd_button_t *p_button = NULL;
505 #if defined(OSD_MENU_DEBUG)
508 p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
511 msg_Err( p_this, "osd_MenuDown failed" );
515 if( osd_isVisible( p_osd ) == VLC_FALSE )
517 vlc_object_release( (vlc_object_t*) p_osd );
521 var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
522 vlc_mutex_lock( lockval.p_address );
524 p_button = p_osd->p_state->p_visible;
527 if( !p_button->b_range )
529 p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_SELECT );
531 p_osd->p_state->p_visible = p_button->p_up;
534 if( p_button->b_range && p_osd->p_state->p_visible->b_range )
536 osd_state_t *p_temp = p_osd->p_state->p_visible->p_current_state;
537 if( p_temp && p_temp->p_next )
538 p_osd->p_state->p_visible->p_current_state = p_temp->p_next;
540 else if( !p_osd->p_state->p_visible->b_range )
542 p_osd->p_state->p_visible->p_current_state =
543 osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );
546 osd_UpdateState( p_osd->p_state,
547 p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y,
548 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,
549 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
550 p_osd->p_state->p_visible->p_current_state->p_pic );
551 osd_SetMenuUpdate( p_osd, VLC_TRUE );
552 /* If this is a range style action with associated images of only one state,
553 * then perform "menu select" on every menu navigation
555 if( p_button->b_range )
557 osd_SetKeyPressed( VLC_OBJECT(p_osd->p_libvlc), config_GetInt(p_osd, p_button->psz_action) );
558 #if defined(OSD_MENU_DEBUG)
559 msg_Dbg( p_osd, "select (%d, %s)", val.i_int, p_button->psz_action );
563 #if defined(OSD_MENU_DEBUG)
564 msg_Dbg( p_osd, "direction up [button %s]", p_osd->p_state->p_visible->psz_action );
567 vlc_object_release( (vlc_object_t*) p_osd );
568 vlc_mutex_unlock( lockval.p_address );
571 void __osd_MenuDown( vlc_object_t *p_this )
573 osd_menu_t *p_osd = NULL;
574 osd_button_t *p_button = NULL;
576 #if defined(OSD_MENU_DEBUG)
580 p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
583 msg_Err( p_this, "osd_MenuDown failed" );
587 if( osd_isVisible( p_osd ) == VLC_FALSE )
589 vlc_object_release( (vlc_object_t*) p_osd );
593 var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
594 vlc_mutex_lock( lockval.p_address );
596 p_button = p_osd->p_state->p_visible;
599 if( !p_button->b_range )
601 p_button->p_current_state = osd_StateChange( p_button->p_states, OSD_BUTTON_SELECT );
602 if( p_button->p_down )
603 p_osd->p_state->p_visible = p_button->p_down;
606 if( p_button->b_range && p_osd->p_state->p_visible->b_range )
608 osd_state_t *p_temp = p_osd->p_state->p_visible->p_current_state;
609 if( p_temp && p_temp->p_prev )
610 p_osd->p_state->p_visible->p_current_state = p_temp->p_prev;
612 else if( !p_osd->p_state->p_visible->b_range )
614 p_osd->p_state->p_visible->p_current_state =
615 osd_StateChange( p_osd->p_state->p_visible->p_states, OSD_BUTTON_SELECT );
618 osd_UpdateState( p_osd->p_state,
619 p_osd->p_state->p_visible->i_x, p_osd->p_state->p_visible->i_y,
620 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,
621 p_osd->p_state->p_visible->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
622 p_osd->p_state->p_visible->p_current_state->p_pic );
623 osd_SetMenuUpdate( p_osd, VLC_TRUE );
624 /* If this is a range style action with associated images of only one state,
625 * then perform "menu select" on every menu navigation
627 if( p_button->b_range )
629 osd_SetKeyPressed( VLC_OBJECT(p_osd->p_libvlc), config_GetInt(p_osd, p_button->psz_action_down) );
630 #if defined(OSD_MENU_DEBUG)
631 msg_Dbg( p_osd, "select (%d, %s)", val.i_int, p_button->psz_action_down );
635 #if defined(OSD_MENU_DEBUG)
636 msg_Dbg( p_osd, "direction down [button %s]", p_osd->p_state->p_visible->psz_action );
639 vlc_object_release( (vlc_object_t*) p_osd );
640 vlc_mutex_unlock( lockval.p_address );
643 static int osd_VolumeStep( vlc_object_t *p_this, int i_volume, int i_steps )
645 int i_volume_step = 0;
648 i_volume_step = config_GetInt( p_this->p_libvlc, "volume-step" );
649 return (i_volume/i_volume_step);
653 * Display current audio volume bitmap
655 * The OSD Menu audio volume bar is updated to reflect the new audio volume. Call this function
656 * when the audio volume is updated outside the OSD menu command "menu up", "menu down" or "menu select".
658 void __osd_Volume( vlc_object_t *p_this )
660 osd_menu_t *p_osd = NULL;
661 osd_button_t *p_button = NULL;
666 p_osd = vlc_object_find( p_this, VLC_OBJECT_OSDMENU, FIND_ANYWHERE );
669 msg_Err( p_this, "OSD menu volume update failed" );
673 if( p_osd->p_state && p_osd->p_state->p_volume )
675 var_Get( p_this->p_libvlc, "osd_mutex", &lockval );
676 vlc_mutex_lock( lockval.p_address );
678 p_button = p_osd->p_state->p_volume;
679 if( p_osd->p_state->p_volume )
680 p_osd->p_state->p_visible = p_osd->p_state->p_volume;
681 if( p_button && p_button->b_range )
683 /* Update the volume state images to match the current volume */
684 i_volume = config_GetInt( p_this, "volume" );
685 i_steps = osd_VolumeStep( p_this, i_volume, p_button->i_ranges );
686 p_button->p_current_state = osd_VolumeStateChange( p_button->p_states, i_steps );
688 osd_UpdateState( p_osd->p_state,
689 p_button->i_x, p_button->i_y,
690 p_button->p_current_state->p_pic->p[Y_PLANE].i_visible_pitch,
691 p_button->p_current_state->p_pic->p[Y_PLANE].i_visible_lines,
692 p_button->p_current_state->p_pic );
693 osd_SetMenuUpdate( p_osd, VLC_TRUE );
694 osd_SetMenuVisible( p_osd, VLC_TRUE );
696 vlc_object_release( (vlc_object_t*) p_osd );
697 vlc_mutex_unlock( lockval.p_address );