1 /*****************************************************************************
2 * osd_parser.c - The OSD Menu parser core code.
3 *****************************************************************************
4 * Copyright (C) 2005 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_config.h>
35 #include <vlc_image.h>
37 #include <vlc_charset.h>
42 /*****************************************************************************
44 *****************************************************************************/
45 static const char *ppsz_button_states[] = { "unselect", "select", "pressed" };
47 /* OSD Menu structure support routines */
48 static osd_menu_t *osd_MenuNew( osd_menu_t *, const char *, int, int );
49 static osd_button_t *osd_ButtonNew( const char *, int, int );
50 static osd_state_t *osd_StateNew( vlc_object_t *, const char *, const char * );
52 static void osd_MenuFree ( vlc_object_t *, osd_menu_t * );
53 static void osd_ButtonFree( vlc_object_t *, osd_button_t * );
54 static void osd_StatesFree( vlc_object_t *, osd_state_t * );
56 static picture_t *osd_LoadImage( vlc_object_t *, const char *);
58 /*****************************************************************************
59 * osd_LoadImage: loads the logo image into memory
60 *****************************************************************************/
61 static picture_t *osd_LoadImage( vlc_object_t *p_this, const char *psz_filename )
63 picture_t *p_pic = NULL;
64 image_handler_t *p_image;
65 video_format_t fmt_in, fmt_out;
67 memset( &fmt_in, 0, sizeof(video_format_t) );
68 memset( &fmt_out, 0, sizeof(video_format_t) );
70 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
71 p_image = image_HandlerCreate( p_this );
74 p_pic = image_ReadUrl( p_image, psz_filename, &fmt_in, &fmt_out );
75 image_HandlerDelete( p_image );
77 else msg_Err( p_this, "unable to handle this chroma" );
82 /*****************************************************************************
83 * Create a new Menu structure
84 *****************************************************************************/
85 static osd_menu_t *osd_MenuNew( osd_menu_t *p_menu, const char *psz_path, int i_x, int i_y )
87 if( !p_menu ) return NULL;
89 p_menu->p_state = (osd_menu_state_t *) malloc( sizeof( osd_menu_state_t ) );
90 if( !p_menu->p_state )
92 msg_Err( p_menu, "Memory allocation for OSD Menu state failed" );
96 memset(p_menu->p_state, 0, sizeof(osd_menu_state_t));
97 if( psz_path != NULL )
98 p_menu->psz_path = strdup( psz_path );
100 p_menu->psz_path = NULL;
107 /*****************************************************************************
109 *****************************************************************************/
110 static void osd_MenuFree( vlc_object_t *p_this, osd_menu_t *p_menu )
112 msg_Dbg( p_this, "freeing menu" );
113 osd_ButtonFree( p_this, p_menu->p_button );
114 p_menu->p_button = NULL;
115 p_menu->p_last_button = NULL;
116 if( p_menu->psz_path ) free( p_menu->psz_path );
117 p_menu->psz_path = NULL;
118 if( p_menu->p_state ) free( p_menu->p_state );
119 p_menu->p_state = NULL;
122 /*****************************************************************************
123 * Create a new button
124 *****************************************************************************/
125 static osd_button_t *osd_ButtonNew( const char *psz_action, int i_x, int i_y )
127 osd_button_t *p_button = NULL;
128 p_button = (osd_button_t*) malloc( sizeof(osd_button_t) );
132 memset( p_button, 0, sizeof(osd_button_t) );
133 p_button->psz_action = strdup(psz_action);
134 p_button->psz_action_down = NULL;
135 p_button->p_feedback = NULL;
142 /*****************************************************************************
144 *****************************************************************************/
145 static void osd_ButtonFree( vlc_object_t *p_this, osd_button_t *p_button )
147 osd_button_t *p_current = p_button;
148 osd_button_t *p_next = NULL;
149 osd_button_t *p_prev = NULL;
151 /* First walk to the end. */
152 while( p_current->p_next )
154 p_next = p_current->p_next;
157 /* Then free end first and walk to the start. */
158 while( p_current->p_prev )
160 msg_Dbg( p_this, "+ freeing button %s [%p]", p_current->psz_action, p_current );
161 p_prev = p_current->p_prev;
163 if( p_current->p_next )
165 if( p_current->p_next->psz_name )
166 free( p_current->p_next->psz_name );
167 if( p_current->p_next->psz_action )
168 free( p_current->p_next->psz_action );
169 if( p_current->p_next->psz_action_down )
170 free( p_current->p_next->psz_action_down );
171 if( p_current->p_feedback && p_current->p_feedback->p_data_orig )
172 free( p_current->p_feedback->p_data_orig );
173 if( p_current->p_feedback )
174 free( p_current->p_feedback );
176 p_current->p_next->psz_action_down = NULL;
177 p_current->p_next->psz_action = NULL;
178 p_current->p_next->psz_name = NULL;
179 p_current->p_feedback = NULL;
181 /* Free all states first */
182 if( p_current->p_next->p_states )
183 osd_StatesFree( p_this, p_current->p_next->p_states );
184 p_current->p_next->p_states = NULL;
185 if( p_current->p_next) free( p_current->p_next );
186 p_current->p_next = NULL;
189 if( p_current->p_up )
191 if( p_current->p_up->psz_name )
192 free( p_current->p_up->psz_name );
193 if( p_current->p_up->psz_action )
194 free( p_current->p_up->psz_action );
195 if( p_current->p_up->psz_action_down )
196 free( p_current->p_up->psz_action_down );
197 if( p_current->p_feedback && p_current->p_feedback->p_data_orig )
198 free( p_current->p_feedback->p_data_orig );
199 if( p_current->p_feedback )
200 free( p_current->p_feedback );
202 p_current->p_up->psz_action_down = NULL;
203 p_current->p_up->psz_action = NULL;
204 p_current->p_up->psz_name = NULL;
205 p_current->p_feedback = NULL;
207 /* Free all states first */
208 if( p_current->p_up->p_states )
209 osd_StatesFree( p_this, p_current->p_up->p_states );
210 p_current->p_up->p_states = NULL;
211 if( p_current->p_up ) free( p_current->p_up );
212 p_current->p_up = NULL;
215 /* Free the last one. */
218 msg_Dbg( p_this, "+ freeing button %s [%p]", p_button->psz_action, p_button );
219 if( p_button->psz_name ) free( p_button->psz_name );
220 if( p_button->psz_action ) free( p_button->psz_action );
221 if( p_button->psz_action_down ) free( p_button->psz_action_down );
222 if( p_current->p_feedback && p_current->p_feedback->p_data_orig )
223 free( p_current->p_feedback->p_data_orig );
224 if( p_current->p_feedback )
225 free( p_current->p_feedback );
227 p_button->psz_name = NULL;
228 p_button->psz_action = NULL;
229 p_button->psz_action_down = NULL;
230 p_current->p_feedback = NULL;
232 if( p_button->p_states )
233 osd_StatesFree( p_this, p_button->p_states );
234 p_button->p_states = NULL;
240 /*****************************************************************************
241 * Create a new state image
242 *****************************************************************************/
243 static osd_state_t *osd_StateNew( vlc_object_t *p_this, const char *psz_file, const char *psz_state )
245 osd_state_t *p_state = NULL;
246 p_state = (osd_state_t*) malloc( sizeof(osd_state_t) );
250 memset( p_state, 0, sizeof(osd_state_t) );
251 p_state->p_pic = osd_LoadImage( p_this, psz_file );
255 p_state->psz_state = strdup( psz_state );
256 if( strncmp( ppsz_button_states[0], psz_state, strlen(ppsz_button_states[0]) ) == 0 )
257 p_state->i_state = OSD_BUTTON_UNSELECT;
258 else if( strncmp( ppsz_button_states[1], psz_state, strlen(ppsz_button_states[1]) ) == 0 )
259 p_state->i_state = OSD_BUTTON_SELECT;
260 else if( strncmp( ppsz_button_states[2], psz_state, strlen(ppsz_button_states[2]) ) == 0 )
261 p_state->i_state = OSD_BUTTON_PRESSED;
266 /*****************************************************************************
268 *****************************************************************************/
269 static void osd_StatesFree( vlc_object_t *p_this, osd_state_t *p_states )
271 osd_state_t *p_state = p_states;
272 osd_state_t *p_next = NULL;
273 osd_state_t *p_prev = NULL;
275 while( p_state->p_next )
277 p_next = p_state->p_next;
280 /* Then free end first and walk to the start. */
281 while( p_state->p_prev )
283 msg_Dbg( p_this, " |- freeing state %s [%p]", p_state->psz_state, p_state );
284 p_prev = p_state->p_prev;
286 if( p_state->p_next )
288 if( p_state->p_next->p_pic && p_state->p_next->p_pic->p_data_orig )
289 free( p_state->p_next->p_pic->p_data_orig );
290 if( p_state->p_next->p_pic ) free( p_state->p_next->p_pic );
291 p_state->p_next->p_pic = NULL;
292 if( p_state->p_next->psz_state ) free( p_state->p_next->psz_state );
293 p_state->p_next->psz_state = NULL;
294 free( p_state->p_next );
295 p_state->p_next = NULL;
298 /* Free the last one. */
301 msg_Dbg( p_this, " |- freeing state %s [%p]", p_state->psz_state, p_states );
302 if( p_states->p_pic && p_states->p_pic->p_data_orig )
303 free( p_states->p_pic->p_data_orig );
304 if( p_states->p_pic ) free( p_states->p_pic );
305 p_states->p_pic = NULL;
306 if( p_state->psz_state ) free( p_state->psz_state );
307 p_state->psz_state = NULL;
313 /*****************************************************************************
314 * osd_ConfigLoader: Load and parse osd text configurationfile
315 *****************************************************************************/
316 int osd_ConfigLoader( vlc_object_t *p_this, const char *psz_file,
317 osd_menu_t **p_menu )
319 osd_button_t *p_current = NULL; /* button currently processed */
320 osd_button_t *p_prev = NULL; /* previous processed button */
322 #define MAX_FILE_PATH 256
326 msg_Dbg( p_this, "opening osd definition file %s", psz_file );
327 fd = utf8_fopen( psz_file, "r" );
330 msg_Err( p_this, "failed to open OSD definition file %s", psz_file );
334 /* Read first line */
337 char action[25] = "";
338 char path[MAX_FILE_PATH] = "";
339 char *psz_path = NULL;
342 /* override images path ? */
343 psz_path = config_GetPsz( p_this, "osdmenu-file-path" );
344 if( psz_path == NULL )
346 result = fscanf(fd, "%24s %255s", &action[0], &path[0] );
350 /* psz_path is not null and therefor &path[0] cannot be NULL
351 * it might be null terminated.
353 strncpy( &path[0], psz_path, MAX_FILE_PATH );
357 /* NULL terminate before asking the length of path[] */
358 path[MAX_FILE_PATH-1] = '\0';
359 i_len = strlen(&path[0]);
360 if( i_len == MAX_FILE_PATH )
361 i_len--; /* truncate to prevent buffer overflow */
362 #if defined(WIN32) || defined(UNDER_CE)
363 if( (i_len > 0) && path[i_len] != '\\' )
366 if( (i_len > 0) && path[i_len] != '/' )
369 path[i_len+1] = '\0';
370 if( result == 0 || result == EOF )
372 msg_Dbg( p_this, "%s=%s", &action[0], &path[0] );
375 *p_menu = osd_MenuNew( *p_menu, NULL, 0, 0 );
377 *p_menu = osd_MenuNew( *p_menu, &path[0], 0, 0 );
383 /* read successive lines */
386 osd_state_t *p_state_current = NULL; /* button state currently processed */
387 osd_state_t *p_state_prev = NULL; /* previous state processed button */
390 char action[25] = "";
397 result = fscanf( fd, "%24s %24s (%d,%d)", &cmd[0], &action[0], &i_x, &i_y );
400 if( strncmp( &cmd[0], "action", 6 ) != 0 )
402 msg_Dbg( p_this, " + %s hotkey=%s (%d,%d)", &cmd[0], &action[0], i_x, i_y );
405 p_current = osd_ButtonNew( &action[0], i_x, i_y );
410 p_prev->p_next = p_current;
412 (*p_menu)->p_button = p_current;
413 p_current->p_prev = p_prev;
415 /* parse all states */
420 result = fscanf( fd, "\t%24s", &state[0] );
424 /* FIXME: We only parse one level deep now */
425 if( strncmp( &state[0], "action", 6 ) == 0 )
427 osd_button_t *p_up = NULL;
429 result = fscanf( fd, "%24s (%d,%d)", &action[0], &i_x, &i_y );
432 /* create new button */
433 p_up = osd_ButtonNew( &action[0], i_x, i_y );
437 p_up->p_down = p_current;
438 p_current->p_up = p_up;
439 msg_Dbg( p_this, " + (menu up) hotkey=%s (%d,%d)", &action[0], i_x, i_y );
440 /* Parse type state */
441 result = fscanf( fd, "\t%24s %24s", &cmd[0], &type[0] );
444 if( strncmp( &cmd[0], "type", 4 ) == 0 )
446 if( strncmp( &type[0], "volume", 6 ) == 0 )
448 (*p_menu)->p_state->p_volume = p_up;
449 msg_Dbg( p_this, " + type=%s", &type[0] );
452 /* Parse range state */
453 result = fscanf( fd, "\t%24s", &state[0] );
456 /* Parse the range state */
457 if( strncmp( &state[0], "range", 5 ) == 0 )
459 osd_state_t *p_range_current = NULL; /* range state currently processed */
460 osd_state_t *p_range_prev = NULL; /* previous state processed range */
463 p_up->b_range = VLC_TRUE;
465 result = fscanf( fd, "\t%24s", &action[0] );
469 result = fscanf( fd, "\t%d", &i_index );
473 msg_Dbg( p_this, " + (menu up) hotkey down %s, file=%s%s", &action[0], (*p_menu)->psz_path, &file[0] );
475 if( p_up->psz_action_down ) free( p_up->psz_action_down );
476 p_up->psz_action_down = strdup( &action[0] );
478 /* Parse range contstruction :
487 result = fscanf( fd, "\t%255s", &file[0] );
490 if( strncmp( &file[0], "end", 3 ) == 0 )
493 p_range_prev = p_range_current;
495 if( (*p_menu)->psz_path )
497 size_t i_path_size = strlen( (*p_menu)->psz_path );
498 size_t i_file_size = strlen( &file[0] );
500 strncpy( &path[0], (*p_menu)->psz_path, i_path_size );
501 strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
502 path[ i_path_size + i_file_size ] = '\0';
504 p_range_current = osd_StateNew( p_this, &path[0], "pressed" );
506 else /* absolute paths are used. */
507 p_range_current = osd_StateNew( p_this, &file[0], "pressed" );
509 if( !p_range_current || !p_range_current->p_pic )
512 /* increment the number of ranges for this button */
516 p_range_prev->p_next = p_range_current;
518 p_up->p_states = p_range_current;
519 p_range_current->p_prev = p_range_prev;
521 msg_Dbg( p_this, " |- range=%d, file=%s%s",
523 (*p_menu)->psz_path, &file[0] );
527 osd_state_t *p_range = NULL;
529 /* Find the default index for state range */
530 p_range = p_up->p_states;
531 while( (--i_index > 0) && p_range->p_next )
533 osd_state_t *p_temp = NULL;
534 p_temp = p_range->p_next;
537 p_up->p_current_state = p_range;
539 else p_up->p_current_state = p_up->p_states;
542 result = fscanf( fd, "\t%24s", &state[0] );
545 if( strncmp( &state[0], "end", 3 ) != 0 )
548 /* Continue at the beginning of the while() */
552 /* Parse the range state */
553 if( strncmp( &state[0], "range", 5 ) == 0 )
555 osd_state_t *p_range_current = NULL; /* range state currently processed */
556 osd_state_t *p_range_prev = NULL; /* previous state processed range */
559 p_current->b_range = VLC_TRUE;
561 result = fscanf( fd, "\t%24s", &action[0] );
565 result = fscanf( fd, "\t%d", &i_index );
569 msg_Dbg( p_this, " + hotkey down %s, file=%s%s", &action[0], (*p_menu)->psz_path, &file[0] );
570 if( p_current->psz_action_down ) free( p_current->psz_action_down );
571 p_current->psz_action_down = strdup( &action[0] );
573 /* Parse range contstruction :
582 result = fscanf( fd, "\t%255s", &file[0] );
585 if( strncmp( &file[0], "end", 3 ) == 0 )
588 p_range_prev = p_range_current;
590 if( (*p_menu)->psz_path )
592 size_t i_path_size = strlen( (*p_menu)->psz_path );
593 size_t i_file_size = strlen( &file[0] );
595 strncpy( &path[0], (*p_menu)->psz_path, i_path_size );
596 strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
597 path[ i_path_size + i_file_size ] = '\0';
599 p_range_current = osd_StateNew( p_this, &path[0], "pressed" );
601 else /* absolute paths are used. */
602 p_range_current = osd_StateNew( p_this, &file[0], "pressed" );
604 if( !p_range_current || !p_range_current->p_pic )
607 /* increment the number of ranges for this button */
608 p_current->i_ranges++;
611 p_range_prev->p_next = p_range_current;
613 p_current->p_states = p_range_current;
614 p_range_current->p_prev = p_range_prev;
616 msg_Dbg( p_this, " |- range=%d, file=%s%s",
618 (*p_menu)->psz_path, &file[0] );
622 osd_state_t *p_range = NULL;
624 /* Find the default index for state range */
625 p_range = p_current->p_states;
626 while( (--i_index > 0) && p_range->p_next )
628 osd_state_t *p_temp = NULL;
629 p_temp = p_range->p_next;
632 p_current->p_current_state = p_range;
634 else p_current->p_current_state = p_current->p_states;
635 /* Continue at the beginning of the while() */
638 if( strncmp( &state[0], "end", 3 ) == 0 )
641 result = fscanf( fd, "\t%255s", &file[0] );
645 p_state_prev = p_state_current;
647 if( ( strncmp( ppsz_button_states[0], &state[0], strlen(ppsz_button_states[0]) ) != 0 ) &&
648 ( strncmp( ppsz_button_states[1], &state[0], strlen(ppsz_button_states[1]) ) != 0 ) &&
649 ( strncmp( ppsz_button_states[2], &state[0], strlen(ppsz_button_states[2]) ) != 0 ) )
651 msg_Err( p_this, "invalid button state %s for button %s expected %d: unselect, select or pressed)",
652 &state[0], &action[0], strlen(&state[0]));
656 if( (*p_menu)->psz_path )
658 size_t i_path_size = strlen( (*p_menu)->psz_path );
659 size_t i_file_size = strlen( &file[0] );
661 strncpy( &path[0], (*p_menu)->psz_path, i_path_size );
662 strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
663 path[ i_path_size + i_file_size ] = '\0';
665 p_state_current = osd_StateNew( p_this, &path[0], &state[0] );
667 else /* absolute paths are used. */
668 p_state_current = osd_StateNew( p_this, &file[0], &state[0] );
670 if( !p_state_current || !p_state_current->p_pic )
674 p_state_prev->p_next = p_state_current;
676 p_current->p_states = p_state_current;
677 p_state_current->p_prev = p_state_prev;
679 msg_Dbg( p_this, " |- state=%s, file=%s%s", &state[0], (*p_menu)->psz_path, &file[0] );
681 p_current->p_current_state = p_current->p_states;
684 /* Find the last button and store its pointer.
685 * The OSD menu behaves like a roundrobin list.
687 p_current = (*p_menu)->p_button;
688 while( p_current && p_current->p_next )
690 osd_button_t *p_temp = NULL;
691 p_temp = p_current->p_next;
694 (*p_menu)->p_last_button = p_current;
700 msg_Err( p_this, "parsing file failed (returned %d)", result );
705 /*****************************************************************************
706 * osd_ConfigUnload: Load and parse osd text configurationfile
707 *****************************************************************************/
708 void osd_ConfigUnload( vlc_object_t *p_this, osd_menu_t **p_osd)
710 msg_Dbg( p_this, "unloading OSD menu structure" );
711 osd_MenuFree( p_this, *p_osd );