1 /*****************************************************************************
\r
2 * osd_parser.c - The OSD Menu parser core code.
\r
3 *****************************************************************************
\r
4 * Copyright (C) 2005 M2X
\r
7 * Authors: Jean-Paul Saman <jpsaman #_at_# m2x dot nl>
\r
9 * This program is free software; you can redistribute it and/or modify
\r
10 * it under the terms of the GNU General Public License as published by
\r
11 * the Free Software Foundation; either version 2 of the License, or
\r
12 * (at your option) any later version.
\r
14 * This program is distributed in the hope that it will be useful,
\r
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
17 * GNU General Public License for more details.
\r
19 * You should have received a copy of the GNU General Public License
\r
20 * along with this program; if not, write to the Free Software
\r
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
\r
22 *****************************************************************************/
\r
24 /*****************************************************************************
\r
26 *****************************************************************************/
\r
30 #include <vlc/vlc.h>
\r
31 #include <vlc_config.h>
\r
32 #include <vlc_video.h>
\r
34 #include <vlc_keys.h>
\r
35 #include <vlc_image.h>
\r
36 #include <vlc_osd.h>
\r
39 #undef OSD_MENU_DEBUG
\r
41 /*****************************************************************************
\r
43 *****************************************************************************/
\r
44 static const char *ppsz_button_states[] = { "unselect", "select", "pressed" };
\r
46 /* OSD Menu structure support routines */
\r
47 static osd_menu_t *osd_MenuNew( osd_menu_t *, const char *, int, int );
\r
48 static osd_button_t *osd_ButtonNew( const char *, int, int );
\r
49 static osd_state_t *osd_StateNew( vlc_object_t *, const char *, const char * );
\r
51 static void osd_MenuFree ( vlc_object_t *, osd_menu_t * );
\r
52 static void osd_ButtonFree( vlc_object_t *, osd_button_t * );
\r
53 static void osd_StatesFree( vlc_object_t *, osd_state_t * );
\r
55 static picture_t *osd_LoadImage( vlc_object_t *, const char *);
\r
57 /*****************************************************************************
\r
58 * osd_LoadImage: loads the logo image into memory
\r
59 *****************************************************************************/
\r
60 static picture_t *osd_LoadImage( vlc_object_t *p_this, const char *psz_filename )
\r
62 picture_t *p_pic = NULL;
\r
63 image_handler_t *p_image;
\r
64 video_format_t fmt_in = {0}, fmt_out = {0};
\r
66 fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');
\r
67 p_image = image_HandlerCreate( p_this );
\r
70 p_pic = image_ReadUrl( p_image, psz_filename, &fmt_in, &fmt_out );
\r
71 image_HandlerDelete( p_image );
\r
73 p_pic = osd_YuvaYuvp( p_this, p_pic );
\r
76 else msg_Err( p_this, "unable to handle this chroma" );
\r
81 /*****************************************************************************
\r
82 * Create a new Menu structure
\r
83 *****************************************************************************/
\r
84 static osd_menu_t *osd_MenuNew( osd_menu_t *p_menu, const char *psz_path, int i_x, int i_y )
\r
86 if( !p_menu ) return NULL;
\r
88 p_menu->p_state = (osd_menu_state_t *) malloc( sizeof( osd_menu_state_t ) );
\r
89 if( !p_menu->p_state )
\r
90 msg_Err( p_menu, "memory allocation for OSD Menu state failed." );
\r
92 if( psz_path != NULL )
\r
93 p_menu->psz_path = strdup( psz_path );
\r
95 p_menu->psz_path = NULL;
\r
102 /*****************************************************************************
\r
104 *****************************************************************************/
\r
105 static void osd_MenuFree( vlc_object_t *p_this, osd_menu_t *p_menu )
\r
107 msg_Dbg( p_this, "freeing menu" );
\r
108 osd_ButtonFree( p_this, p_menu->p_button );
\r
109 p_menu->p_button = NULL;
\r
110 p_menu->p_last_button = NULL;
\r
111 if( p_menu->psz_path ) free( p_menu->psz_path );
\r
112 p_menu->psz_path = NULL;
\r
113 if( p_menu->p_state ) free( p_menu->p_state );
\r
114 p_menu->p_state = NULL;
\r
117 /*****************************************************************************
\r
118 * Create a new button
\r
119 *****************************************************************************/
\r
120 static osd_button_t *osd_ButtonNew( const char *psz_action, int i_x, int i_y )
\r
122 osd_button_t *p_button = NULL;
\r
123 p_button = (osd_button_t*) malloc( sizeof(osd_button_t) );
\r
127 memset( p_button, 0, sizeof(osd_button_t) );
\r
128 p_button->psz_action = strdup(psz_action);
\r
129 p_button->psz_action_down = NULL;
\r
130 p_button->p_feedback = NULL;
\r
131 p_button->i_x = i_x;
\r
132 p_button->i_y = i_y;
\r
137 /*****************************************************************************
\r
139 *****************************************************************************/
\r
140 static void osd_ButtonFree( vlc_object_t *p_this, osd_button_t *p_button )
\r
142 osd_button_t *p_current = p_button;
\r
143 osd_button_t *p_next = NULL;
\r
144 osd_button_t *p_prev = NULL;
\r
146 /* First walk to the end. */
\r
147 while( p_current->p_next )
\r
149 p_next = p_current->p_next;
\r
150 p_current = p_next;
\r
152 /* Then free end first and walk to the start. */
\r
153 while( p_current->p_prev )
\r
155 msg_Dbg( p_this, "+ freeing button %s [%p]", p_current->psz_action, p_current );
\r
156 p_prev = p_current->p_prev;
\r
157 p_current = p_prev;
\r
158 if( p_current->p_next )
\r
160 if( p_current->p_next->psz_name )
\r
161 free( p_current->p_next->psz_name );
\r
162 if( p_current->p_next->psz_action )
\r
163 free( p_current->p_next->psz_action );
\r
164 if( p_current->p_next->psz_action_down )
\r
165 free( p_current->p_next->psz_action_down );
\r
166 if( p_current->p_feedback && p_current->p_feedback->p_data_orig )
\r
167 free( p_current->p_feedback->p_data_orig );
\r
168 if( p_current->p_feedback )
\r
169 free( p_current->p_feedback );
\r
171 p_current->p_next->psz_action_down = NULL;
\r
172 p_current->p_next->psz_action = NULL;
\r
173 p_current->p_next->psz_name = NULL;
\r
174 p_current->p_feedback = NULL;
\r
176 /* Free all states first */
\r
177 if( p_current->p_next->p_states )
\r
178 osd_StatesFree( p_this, p_current->p_next->p_states );
\r
179 p_current->p_next->p_states = NULL;
\r
180 if( p_current->p_next) free( p_current->p_next );
\r
181 p_current->p_next = NULL;
\r
184 if( p_current->p_up )
\r
186 if( p_current->p_up->psz_name )
\r
187 free( p_current->p_up->psz_name );
\r
188 if( p_current->p_up->psz_action )
\r
189 free( p_current->p_up->psz_action );
\r
190 if( p_current->p_up->psz_action_down )
\r
191 free( p_current->p_up->psz_action_down );
\r
192 if( p_current->p_feedback && p_current->p_feedback->p_data_orig )
\r
193 free( p_current->p_feedback->p_data_orig );
\r
194 if( p_current->p_feedback )
\r
195 free( p_current->p_feedback );
\r
197 p_current->p_up->psz_action_down = NULL;
\r
198 p_current->p_up->psz_action = NULL;
\r
199 p_current->p_up->psz_name = NULL;
\r
200 p_current->p_feedback = NULL;
\r
202 /* Free all states first */
\r
203 if( p_current->p_up->p_states )
\r
204 osd_StatesFree( p_this, p_current->p_up->p_states );
\r
205 p_current->p_up->p_states = NULL;
\r
206 if( p_current->p_up ) free( p_current->p_up );
\r
207 p_current->p_up = NULL;
\r
210 /* Free the last one. */
\r
213 msg_Dbg( p_this, "+ freeing button %s [%p]", p_button->psz_action, p_button );
\r
214 if( p_button->psz_name ) free( p_button->psz_name );
\r
215 if( p_button->psz_action ) free( p_button->psz_action );
\r
216 if( p_button->psz_action_down ) free( p_button->psz_action_down );
\r
217 if( p_current->p_feedback && p_current->p_feedback->p_data_orig )
\r
218 free( p_current->p_feedback->p_data_orig );
\r
219 if( p_current->p_feedback )
\r
220 free( p_current->p_feedback );
\r
222 p_button->psz_name = NULL;
\r
223 p_button->psz_action = NULL;
\r
224 p_button->psz_action_down = NULL;
\r
225 p_current->p_feedback = NULL;
\r
227 if( p_button->p_states )
\r
228 osd_StatesFree( p_this, p_button->p_states );
\r
229 p_button->p_states = NULL;
\r
235 /*****************************************************************************
\r
236 * Create a new state image
\r
237 *****************************************************************************/
\r
238 static osd_state_t *osd_StateNew( vlc_object_t *p_this, const char *psz_file, const char *psz_state )
\r
240 osd_state_t *p_state = NULL;
\r
241 p_state = (osd_state_t*) malloc( sizeof(osd_state_t) );
\r
245 memset( p_state, 0, sizeof(osd_state_t) );
\r
246 p_state->p_pic = osd_LoadImage( p_this, psz_file );
\r
250 p_state->psz_state = strdup( psz_state );
\r
251 if( strncmp( ppsz_button_states[0], psz_state, strlen(ppsz_button_states[0]) ) == 0 )
\r
252 p_state->i_state = OSD_BUTTON_UNSELECT;
\r
253 else if( strncmp( ppsz_button_states[1], psz_state, strlen(ppsz_button_states[1]) ) == 0 )
\r
254 p_state->i_state = OSD_BUTTON_SELECT;
\r
255 else if( strncmp( ppsz_button_states[2], psz_state, strlen(ppsz_button_states[2]) ) == 0 )
\r
256 p_state->i_state = OSD_BUTTON_PRESSED;
\r
261 /*****************************************************************************
\r
262 * Free state images
\r
263 *****************************************************************************/
\r
264 static void osd_StatesFree( vlc_object_t *p_this, osd_state_t *p_states )
\r
266 osd_state_t *p_state = p_states;
\r
267 osd_state_t *p_next = NULL;
\r
268 osd_state_t *p_prev = NULL;
\r
270 while( p_state->p_next )
\r
272 p_next = p_state->p_next;
\r
275 /* Then free end first and walk to the start. */
\r
276 while( p_state->p_prev )
\r
278 msg_Dbg( p_this, " |- freeing state %s [%p]", p_state->psz_state, p_state );
\r
279 p_prev = p_state->p_prev;
\r
281 if( p_state->p_next )
\r
283 if( p_state->p_next->p_pic && p_state->p_next->p_pic->p_data_orig )
\r
284 free( p_state->p_next->p_pic->p_data_orig );
\r
285 if( p_state->p_next->p_pic ) free( p_state->p_next->p_pic );
\r
286 p_state->p_next->p_pic = NULL;
\r
287 if( p_state->p_next->psz_state ) free( p_state->p_next->psz_state );
\r
288 p_state->p_next->psz_state = NULL;
\r
289 free( p_state->p_next );
\r
290 p_state->p_next = NULL;
\r
293 /* Free the last one. */
\r
296 msg_Dbg( p_this, " |- freeing state %s [%p]", p_state->psz_state, p_states );
\r
297 if( p_states->p_pic && p_states->p_pic->p_data_orig )
\r
298 free( p_states->p_pic->p_data_orig );
\r
299 if( p_states->p_pic ) free( p_states->p_pic );
\r
300 p_states->p_pic = NULL;
\r
301 if( p_state->psz_state ) free( p_state->psz_state );
\r
302 p_state->psz_state = NULL;
\r
308 /*****************************************************************************
\r
309 * osd_ConfigLoader: Load and parse osd text configurationfile
\r
310 *****************************************************************************/
\r
311 int osd_ConfigLoader( vlc_object_t *p_this, const char *psz_file,
\r
312 osd_menu_t **p_menu )
\r
314 osd_button_t *p_current = NULL; /* button currently processed */
\r
315 osd_button_t *p_prev = NULL; /* previous processed button */
\r
317 #define MAX_FILE_PATH 256
\r
321 msg_Dbg( p_this, "opening osd definition file %s", psz_file );
\r
322 fd = fopen( psz_file, "r" );
\r
325 msg_Err( p_this, "failed opening osd definition file %s", psz_file );
\r
326 return VLC_EGENERIC;
\r
329 /* Read first line */
\r
332 char action[25] = "";
\r
333 char path[MAX_FILE_PATH] = "";
\r
334 char *psz_path = NULL;
\r
337 /* override images path ? */
\r
338 psz_path = config_GetPsz( p_this, "osdmenu-file-path" );
\r
339 if( psz_path == NULL )
\r
341 result = fscanf(fd, "%24s %255s", &action[0], &path[0] );
\r
345 /* psz_path is not null and therefor &path[0] cannot be NULL
\r
346 * it might be null terminated.
\r
348 strncpy( &path[0], psz_path, MAX_FILE_PATH );
\r
352 /* NULL terminate before asking the length of path[] */
\r
353 path[MAX_FILE_PATH-1] = '\0';
\r
354 i_len = strlen(&path[0]);
\r
355 if( i_len == MAX_FILE_PATH )
\r
356 i_len--; /* truncate to prevent buffer overflow */
\r
357 #if defined(WIN32) || defined(UNDER_CE)
\r
358 if( (i_len > 0) && path[i_len] != '\\' )
\r
359 path[i_len] = '\\';
\r
361 if( (i_len > 0) && path[i_len] != '/' )
\r
364 path[i_len+1] = '\0';
\r
365 if( result == 0 || result == EOF )
\r
367 msg_Dbg( p_this, "%s=%s", &action[0], &path[0] );
\r
370 *p_menu = osd_MenuNew( *p_menu, NULL, 0, 0 );
\r
372 *p_menu = osd_MenuNew( *p_menu, &path[0], 0, 0 );
\r
378 /* read successive lines */
\r
379 while( !feof( fd ) )
\r
381 osd_state_t *p_state_current = NULL; /* button state currently processed */
\r
382 osd_state_t *p_state_prev = NULL; /* previous state processed button */
\r
384 char cmd[25] = "";
\r
385 char action[25] = "";
\r
386 char state[25] = "";
\r
387 char file[256] = "";
\r
388 char path[512] = "";
\r
392 result = fscanf( fd, "%24s %24s (%d,%d)", &cmd[0], &action[0], &i_x, &i_y );
\r
395 if( strncmp( &cmd[0], "action", 6 ) != 0 )
\r
397 msg_Dbg( p_this, " + %s hotkey=%s (%d,%d)", &cmd[0], &action[0], i_x, i_y );
\r
399 p_prev = p_current;
\r
400 p_current = osd_ButtonNew( &action[0], i_x, i_y );
\r
405 p_prev->p_next = p_current;
\r
407 (*p_menu)->p_button = p_current;
\r
408 p_current->p_prev = p_prev;
\r
410 /* parse all states */
\r
411 while( !feof( fd ) )
\r
413 char type[25] = "";
\r
415 result = fscanf( fd, "\t%24s", &state[0] );
\r
419 /* FIXME: We only parse one level deep now */
\r
420 if( strncmp( &state[0], "action", 6 ) == 0 )
\r
422 osd_button_t *p_up = NULL;
\r
424 result = fscanf( fd, "%24s (%d,%d)", &action[0], &i_x, &i_y );
\r
427 /* create new button */
\r
428 p_up = osd_ButtonNew( &action[0], i_x, i_y );
\r
431 /* Link to list */
\r
432 p_up->p_down = p_current;
\r
433 p_current->p_up = p_up;
\r
434 msg_Dbg( p_this, " + (menu up) hotkey=%s (%d,%d)", &action[0], i_x, i_y );
\r
435 /* Parse type state */
\r
436 result = fscanf( fd, "\t%24s %24s", &cmd[0], &type[0] );
\r
439 if( strncmp( &cmd[0], "type", 4 ) == 0 )
\r
441 if( strncmp( &type[0], "volume", 6 ) == 0 )
\r
443 (*p_menu)->p_state->p_volume = p_up;
\r
444 msg_Dbg( p_this, " + type=%s", &type[0] );
\r
447 /* Parse range state */
\r
448 result = fscanf( fd, "\t%24s", &state[0] );
\r
451 /* Parse the range state */
\r
452 if( strncmp( &state[0], "range", 5 ) == 0 )
\r
454 osd_state_t *p_range_current = NULL; /* range state currently processed */
\r
455 osd_state_t *p_range_prev = NULL; /* previous state processed range */
\r
458 p_up->b_range = VLC_TRUE;
\r
460 result = fscanf( fd, "\t%24s", &action[0] );
\r
464 result = fscanf( fd, "\t%d", &i_index );
\r
468 msg_Dbg( p_this, " + (menu up) hotkey down %s, file=%s%s", &action[0], (*p_menu)->psz_path, &file[0] );
\r
470 if( p_up->psz_action_down ) free( p_up->psz_action_down );
\r
471 p_up->psz_action_down = strdup( &action[0] );
\r
473 /* Parse range contstruction :
\r
480 while( !feof( fd ) )
\r
482 result = fscanf( fd, "\t%255s", &file[0] );
\r
485 if( strncmp( &file[0], "end", 3 ) == 0 )
\r
488 p_range_prev = p_range_current;
\r
490 if( (*p_menu)->psz_path )
\r
492 size_t i_path_size = strlen( (*p_menu)->psz_path );
\r
493 size_t i_file_size = strlen( &file[0] );
\r
495 strncpy( &path[0], (*p_menu)->psz_path, i_path_size );
\r
496 strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
\r
497 path[ i_path_size + i_file_size ] = '\0';
\r
499 p_range_current = osd_StateNew( p_this, &path[0], "pressed" );
\r
501 else /* absolute paths are used. */
\r
502 p_range_current = osd_StateNew( p_this, &file[0], "pressed" );
\r
504 if( !p_range_current || !p_range_current->p_pic )
\r
507 /* increment the number of ranges for this button */
\r
511 p_range_prev->p_next = p_range_current;
\r
513 p_up->p_states = p_range_current;
\r
514 p_range_current->p_prev = p_range_prev;
\r
516 msg_Dbg( p_this, " |- range=%d, file=%s%s",
\r
518 (*p_menu)->psz_path, &file[0] );
\r
522 osd_state_t *p_range = NULL;
\r
524 /* Find the default index for state range */
\r
525 p_range = p_up->p_states;
\r
526 while( (--i_index > 0) && p_range->p_next )
\r
528 osd_state_t *p_temp = NULL;
\r
529 p_temp = p_range->p_next;
\r
532 p_up->p_current_state = p_range;
\r
534 else p_up->p_current_state = p_up->p_states;
\r
537 result = fscanf( fd, "\t%24s", &state[0] );
\r
540 if( strncmp( &state[0], "end", 3 ) != 0 )
\r
543 /* Continue at the beginning of the while() */
\r
547 /* Parse the range state */
\r
548 if( strncmp( &state[0], "range", 5 ) == 0 )
\r
550 osd_state_t *p_range_current = NULL; /* range state currently processed */
\r
551 osd_state_t *p_range_prev = NULL; /* previous state processed range */
\r
554 p_current->b_range = VLC_TRUE;
\r
556 result = fscanf( fd, "\t%24s", &action[0] );
\r
560 result = fscanf( fd, "\t%d", &i_index );
\r
564 msg_Dbg( p_this, " + hotkey down %s, file=%s%s", &action[0], (*p_menu)->psz_path, &file[0] );
\r
565 if( p_current->psz_action_down ) free( p_current->psz_action_down );
\r
566 p_current->psz_action_down = strdup( &action[0] );
\r
568 /* Parse range contstruction :
\r
575 while( !feof( fd ) )
\r
577 result = fscanf( fd, "\t%255s", &file[0] );
\r
580 if( strncmp( &file[0], "end", 3 ) == 0 )
\r
583 p_range_prev = p_range_current;
\r
585 if( (*p_menu)->psz_path )
\r
587 size_t i_path_size = strlen( (*p_menu)->psz_path );
\r
588 size_t i_file_size = strlen( &file[0] );
\r
590 strncpy( &path[0], (*p_menu)->psz_path, i_path_size );
\r
591 strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
\r
592 path[ i_path_size + i_file_size ] = '\0';
\r
594 p_range_current = osd_StateNew( p_this, &path[0], "pressed" );
\r
596 else /* absolute paths are used. */
\r
597 p_range_current = osd_StateNew( p_this, &file[0], "pressed" );
\r
599 if( !p_range_current || !p_range_current->p_pic )
\r
602 /* increment the number of ranges for this button */
\r
603 p_current->i_ranges++;
\r
606 p_range_prev->p_next = p_range_current;
\r
608 p_current->p_states = p_range_current;
\r
609 p_range_current->p_prev = p_range_prev;
\r
611 msg_Dbg( p_this, " |- range=%d, file=%s%s",
\r
612 p_current->i_ranges,
\r
613 (*p_menu)->psz_path, &file[0] );
\r
617 osd_state_t *p_range = NULL;
\r
619 /* Find the default index for state range */
\r
620 p_range = p_current->p_states;
\r
621 while( (--i_index > 0) && p_range->p_next )
\r
623 osd_state_t *p_temp = NULL;
\r
624 p_temp = p_range->p_next;
\r
627 p_current->p_current_state = p_range;
\r
629 else p_current->p_current_state = p_current->p_states;
\r
630 /* Continue at the beginning of the while() */
\r
633 if( strncmp( &state[0], "end", 3 ) == 0 )
\r
636 result = fscanf( fd, "\t%255s", &file[0] );
\r
640 p_state_prev = p_state_current;
\r
642 if( ( strncmp( ppsz_button_states[0], &state[0], strlen(ppsz_button_states[0]) ) != 0 ) &&
\r
643 ( strncmp( ppsz_button_states[1], &state[0], strlen(ppsz_button_states[1]) ) != 0 ) &&
\r
644 ( strncmp( ppsz_button_states[2], &state[0], strlen(ppsz_button_states[2]) ) != 0 ) )
\r
646 msg_Err( p_this, "invalid button state %s for button %s expected %d: unselect, select or pressed)",
\r
647 &state[0], &action[0], strlen(&state[0]));
\r
651 if( (*p_menu)->psz_path )
\r
653 size_t i_path_size = strlen( (*p_menu)->psz_path );
\r
654 size_t i_file_size = strlen( &file[0] );
\r
656 strncpy( &path[0], (*p_menu)->psz_path, i_path_size );
\r
657 strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
\r
658 path[ i_path_size + i_file_size ] = '\0';
\r
660 p_state_current = osd_StateNew( p_this, &path[0], &state[0] );
\r
662 else /* absolute paths are used. */
\r
663 p_state_current = osd_StateNew( p_this, &file[0], &state[0] );
\r
665 if( !p_state_current || !p_state_current->p_pic )
\r
669 p_state_prev->p_next = p_state_current;
\r
671 p_current->p_states = p_state_current;
\r
672 p_state_current->p_prev = p_state_prev;
\r
674 msg_Dbg( p_this, " |- state=%s, file=%s%s", &state[0], (*p_menu)->psz_path, &file[0] );
\r
676 p_current->p_current_state = p_current->p_states;
\r
679 /* Find the last button and store its pointer.
\r
680 * The OSD menu behaves like a roundrobin list.
\r
682 p_current = (*p_menu)->p_button;
\r
683 while( p_current && p_current->p_next )
\r
685 osd_button_t *p_temp = NULL;
\r
686 p_temp = p_current->p_next;
\r
687 p_current = p_temp;
\r
689 (*p_menu)->p_last_button = p_current;
\r
693 #undef MAX_FILE_PATH
\r
695 msg_Err( p_this, "parsing file failed (returned %d)", result );
\r
700 /*****************************************************************************
\r
701 * osd_ConfigUnload: Load and parse osd text configurationfile
\r
702 *****************************************************************************/
\r
703 void osd_ConfigUnload( vlc_object_t *p_this, osd_menu_t **p_osd)
\r
705 msg_Dbg( p_this, "unloading OSD menu structure" );
\r
706 osd_MenuFree( p_this, *p_osd );
\r