svn:keywords
[vlc.git] / modules / misc / osd / simple.c
1 /*****************************************************************************
2  * simple.c - The OSD Menu simple parser code.
3  *****************************************************************************
4  * Copyright (C) 2005-2007 M2X
5  * $Id$
6  *
7  * Authors: Jean-Paul Saman
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 #include <vlc/vlc.h>
29 #include <vlc_vout.h>
30 #include <vlc_config.h>
31
32 #include <vlc_keys.h>
33 #include <vlc_image.h>
34 #include <vlc_osd.h>
35 #include <vlc_charset.h>
36
37 #include "osd_menu.h"
38
39 int osd_parser_simpleOpen( vlc_object_t *p_this );
40
41 /*****************************************************************************
42  * Simple parser open function
43  *****************************************************************************/
44 int osd_parser_simpleOpen( vlc_object_t *p_this )
45 {
46     osd_menu_t     *p_menu = (osd_menu_t *) p_this;
47     osd_button_t   *p_current = NULL; /* button currently processed */
48     osd_button_t   *p_prev = NULL;    /* previous processed button */
49
50 #define MAX_FILE_PATH 256
51     FILE       *fd = NULL;
52     int        result = 0;
53
54     if( !p_menu ) return VLC_ENOOBJ;
55
56     msg_Dbg( p_this, "opening osdmenu definition file %s", p_menu->psz_file );
57     fd = utf8_fopen( p_menu->psz_file, "r" );
58     if( !fd )
59     {
60         msg_Err( p_this, "failed to open osdmenu definition file %s",
61                 p_menu->psz_file );
62         return VLC_EGENERIC;
63     }
64
65     /* Read first line */
66     if( !feof( fd ) )
67     {
68         char action[25] = "";
69         char cmd[25] = "";
70         char path[MAX_FILE_PATH] = "";
71         char *psz_path = NULL;
72         size_t i_len = 0;
73         long pos = 0;
74
75         result = fscanf(fd, "%24s %255s", &action[0], &path[0] );
76
77         /* override images path ? */
78         psz_path = config_GetPsz( p_this, "osdmenu-file-path" );
79         if( psz_path )
80         {
81             /* psz_path is not null and therefor &path[0] cannot be NULL
82              * it might be null terminated.
83              */
84             strncpy( &path[0], psz_path, MAX_FILE_PATH );
85             free( psz_path );
86             psz_path = NULL;
87         }
88         /* NULL terminate before asking the length of path[] */
89         path[MAX_FILE_PATH-1] = '\0';
90         i_len = strlen(&path[0]);
91         if( i_len == MAX_FILE_PATH )
92             i_len--; /* truncate to prevent buffer overflow */
93 #if defined(WIN32) || defined(UNDER_CE)
94         if( (i_len > 0) && path[i_len] != '\\' )
95             path[i_len] = '\\';
96 #else
97         if( (i_len > 0) && path[i_len] != '/' )
98             path[i_len] = '/';
99 #endif
100         path[i_len+1] = '\0';
101         if( result == 0 || result == EOF )
102             goto error;
103         msg_Dbg( p_this, "osdmenu dir %s", &path[0] );
104
105         if( i_len == 0 )
106             p_menu = osd_MenuNew( p_menu, NULL, 0, 0 );
107         else
108             p_menu = osd_MenuNew( p_menu, &path[0], 0, 0 );
109
110         /* Peek for 'style' argument */
111         pos = ftell( fd );
112         if( pos < 0 )
113                 goto error;
114
115         result = fscanf(fd, "%24s %24s", &cmd[0], &action[0] );
116         if( result == 0 || result == EOF )
117             goto error;
118
119         msg_Dbg( p_this, "osdmenu %s %s", &cmd[0], &action[0] );
120         if( strncmp( &cmd[0], "style", 5 ) == 0 )
121         {
122             if( strncmp( &action[0], "default", 7) == 0 )
123             {
124                 p_menu->i_style = OSD_MENU_STYLE_SIMPLE;
125             }
126             else if( strncmp( &action[0], "concat", 6) == 0 )
127             {
128                 p_menu->i_style = OSD_MENU_STYLE_CONCAT;
129             }
130         }
131         else
132         {
133             result = fseek( fd, pos, SEEK_SET );
134             if( result < 0 )
135                 goto error;
136         }
137     }
138
139     if( !p_menu )
140         goto error;
141
142     /* read successive lines */
143     while( !feof( fd ) )
144     {
145         osd_state_t   *p_state_current = NULL; /* button state currently processed */
146         osd_state_t   *p_state_prev = NULL;    /* previous state processed button */
147
148         char cmd[25] = "";
149         char action[25] = "";
150         char state[25]  = "";
151         char file[256]  = "";
152         char path[512]  = "";
153         int  i_x = 0;
154         int  i_y = 0;
155
156         result = fscanf( fd, "%24s %24s (%d,%d)", &cmd[0], &action[0], &i_x, &i_y );
157         if( result == 0 )
158             goto error;
159         if( strncmp( &cmd[0], "action", 6 ) != 0 )
160             break;
161         msg_Dbg( p_this, " + %s hotkey=%s (%d,%d)", &cmd[0], &action[0], i_x, i_y );
162
163         p_prev = p_current;
164         p_current = osd_ButtonNew( &action[0], i_x, i_y );
165         if( !p_current )
166             goto error;
167
168         if( p_prev )
169             p_prev->p_next = p_current;
170         else
171             p_menu->p_button = p_current;
172         p_current->p_prev = p_prev;
173
174         /* parse all states */
175         while( !feof( fd ) )
176         {
177             char type[25] = "";
178
179             result = fscanf( fd, "\t%24s", &state[0] );
180             if( result == 0 )
181                 goto error;
182
183             /* FIXME: We only parse one level deep now */
184             if( strncmp( &state[0], "action", 6 ) == 0 )
185             {
186                 osd_button_t   *p_up = NULL;
187
188                 result = fscanf( fd, "%24s (%d,%d)", &action[0], &i_x, &i_y );
189                 if( result == 0 )
190                     goto error;
191                 /* create new button */
192                 p_up = osd_ButtonNew( &action[0], i_x, i_y );
193                 if( !p_up )
194                     goto error;
195                 /* Link to list */
196                 p_up->p_down = p_current;
197                 p_current->p_up = p_up;
198                 msg_Dbg( p_this, " + (menu up) hotkey=%s (%d,%d)", &action[0], i_x, i_y );
199                 /* Parse type state */
200                 result = fscanf( fd, "\t%24s %24s", &cmd[0], &type[0] );
201                 if( result == 0 )
202                     goto error;
203                 if( strncmp( &cmd[0], "type", 4 ) == 0 )
204                 {
205                     if( strncmp( &type[0], "volume", 6 ) == 0 )
206                     {
207                         p_menu->p_state->p_volume = p_up;
208                         msg_Dbg( p_this, " + type=%s", &type[0] );
209                     }
210                 }
211                 /* Parse range state */
212                 result = fscanf( fd, "\t%24s", &state[0] );
213                 if( result == 0 )
214                     goto error;
215                 /* Parse the range state */
216                 if( strncmp( &state[0], "range", 5 ) == 0 )
217                 {
218                     osd_state_t   *p_range_current = NULL; /* range state currently processed */
219                     osd_state_t   *p_range_prev = NULL;    /* previous state processed range */
220                     int i_index = 0;
221
222                     p_up->b_range = VLC_TRUE;
223
224                     result = fscanf( fd, "\t%24s", &action[0] );
225                     if( result == 0 )
226                         goto error;
227
228                     result = fscanf( fd, "\t%d", &i_index );
229                     if( result == 0 )
230                         goto error;
231
232                     msg_Dbg( p_this, " + (menu up) hotkey down %s, file=%s%s",
233                              &action[0], p_menu->psz_path, &file[0] );
234
235                     if( p_up->psz_action_down ) free( p_up->psz_action_down );
236                     p_up->psz_action_down = strdup( &action[0] );
237
238                     /* Parse range contstruction :
239                      * range <hotkey>
240                      *      <state1> <file1>
241                      *
242                      *      <stateN> <fileN>
243                      * end
244                      */
245                     while( !feof( fd ) )
246                     {
247                         result = fscanf( fd, "\t%255s", &file[0] );
248                         if( result == 0 )
249                             goto error;
250                         if( strncmp( &file[0], "end", 3 ) == 0 )
251                             break;
252
253                         p_range_prev = p_range_current;
254
255                         if( p_menu->psz_path )
256                         {
257                             size_t i_path_size = strlen( p_menu->psz_path );
258                             size_t i_file_size = strlen( &file[0] );
259
260                             strncpy( &path[0], p_menu->psz_path, i_path_size );
261                             strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
262                             path[ i_path_size + i_file_size ] = '\0';
263
264                             p_range_current = osd_StateNew( p_menu, &path[0], "pressed" );
265                         }
266                         else /* absolute paths are used. */
267                             p_range_current = osd_StateNew( p_menu, &file[0], "pressed" );
268
269                         if( !p_range_current || !p_range_current->p_pic )
270                             goto error;
271
272                         p_range_current->i_x = i_x;
273                         p_range_current->i_y = i_y;
274
275                         /* increment the number of ranges for this button */
276                         p_up->i_ranges++;
277
278                         if( p_range_prev )
279                             p_range_prev->p_next = p_range_current;
280                         else
281                             p_up->p_states = p_range_current;
282                         p_range_current->p_prev = p_range_prev;
283
284                         msg_Dbg( p_this, "  |- range=%d, file=%s%s",
285                                  p_up->i_ranges,
286                                  p_menu->psz_path, &file[0] );
287                     }
288                     if( i_index > 0 )
289                     {
290                         osd_state_t *p_range = NULL;
291
292                         /* Find the default index for state range */
293                         p_range = p_up->p_states;
294                         while( (--i_index > 0) && p_range->p_next )
295                         {
296                             osd_state_t *p_temp = NULL;
297                             p_temp = p_range->p_next;
298                             p_range = p_temp;
299                         }
300                         p_up->p_current_state = p_range;
301                     }
302                     else p_up->p_current_state = p_up->p_states;
303
304                 }
305                 result = fscanf( fd, "\t%24s", &state[0] );
306                 if( result == 0 )
307                     goto error;
308                 if( strncmp( &state[0], "end", 3 ) != 0 )
309                     goto error;
310
311                 /* Continue at the beginning of the while() */
312                 continue;
313             }
314
315             /* Parse the range state */
316             if( strncmp( &state[0], "range", 5 ) == 0 )
317             {
318                 osd_state_t   *p_range_current = NULL; /* range state currently processed */
319                 osd_state_t   *p_range_prev = NULL;    /* previous state processed range */
320                 int i_index = 0;
321
322                 p_current->b_range = VLC_TRUE;
323
324                 result = fscanf( fd, "\t%24s", &action[0] );
325                 if( result == 0 )
326                     goto error;
327
328                 result = fscanf( fd, "\t%d", &i_index );
329                 if( result == 0 )
330                     goto error;
331
332                 msg_Dbg( p_this, " + hotkey down %s, file=%s%s", 
333                          &action[0], p_menu->psz_path, &file[0] );
334                 if( p_current->psz_action_down ) 
335                     free( p_current->psz_action_down );
336                 p_current->psz_action_down = strdup( &action[0] );
337
338                 /* Parse range contstruction :
339                  * range <hotkey>
340                  *      <state1> <file1>
341                  *
342                  *      <stateN> <fileN>
343                  * end
344                  */
345                 while( !feof( fd ) )
346                 {
347                     result = fscanf( fd, "\t%255s", &file[0] );
348                     if( result == 0 )
349                         goto error;
350                     if( strncmp( &file[0], "end", 3 ) == 0 )
351                         break;
352
353                     p_range_prev = p_range_current;
354
355                     if( p_menu->psz_path )
356                     {
357                         size_t i_path_size = strlen( p_menu->psz_path );
358                         size_t i_file_size = strlen( &file[0] );
359
360                         strncpy( &path[0], p_menu->psz_path, i_path_size );
361                         strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
362                         path[ i_path_size + i_file_size ] = '\0';
363
364                         p_range_current = osd_StateNew( p_menu, &path[0], "pressed" );
365                     }
366                     else /* absolute paths are used. */
367                         p_range_current = osd_StateNew( p_menu, &file[0], "pressed" );
368
369                     if( !p_range_current || !p_range_current->p_pic )
370                         goto error;
371
372                     p_range_current->i_x = i_x;
373                     p_range_current->i_y = i_y;
374
375                     /* increment the number of ranges for this button */
376                     p_current->i_ranges++;
377
378                     if( p_range_prev )
379                         p_range_prev->p_next = p_range_current;
380                     else
381                         p_current->p_states = p_range_current;
382                     p_range_current->p_prev = p_range_prev;
383
384                     msg_Dbg( p_this, "  |- range=%d, file=%s%s",
385                              p_current->i_ranges,
386                              p_menu->psz_path, &file[0] );
387                 }
388                 if( i_index > 0 )
389                 {
390                     osd_state_t *p_range = NULL;
391
392                     /* Find the default index for state range */
393                     p_range = p_current->p_states;
394                     while( (--i_index > 0) && p_range->p_next )
395                     {
396                         osd_state_t *p_temp = NULL;
397                         p_temp = p_range->p_next;
398                         p_range = p_temp;
399                     }
400                     p_current->p_current_state = p_range;
401                 }
402                 else p_current->p_current_state = p_current->p_states;
403                 /* Continue at the beginning of the while() */
404                 continue;
405             }
406             if( strncmp( &state[0], "end", 3 ) == 0 )
407                 break;
408
409             result = fscanf( fd, "\t%255s", &file[0] );
410             if( result == 0 )
411                 goto error;
412
413             p_state_prev = p_state_current;
414
415             if( ( strncmp( ppsz_button_states[0], &state[0], strlen(ppsz_button_states[0]) ) != 0 ) &&
416                 ( strncmp( ppsz_button_states[1], &state[0], strlen(ppsz_button_states[1]) ) != 0 ) &&
417                 ( strncmp( ppsz_button_states[2], &state[0], strlen(ppsz_button_states[2]) ) != 0 ) )
418             {
419                 msg_Err( p_this, "invalid button state %s for button %s "
420                          "expected %u: unselect, select or pressed)",
421                          &state[0], &action[0], (unsigned)strlen(&state[0]));
422                 goto error;
423             }
424
425             if( p_menu->psz_path )
426             {
427                 size_t i_path_size = strlen( p_menu->psz_path );
428                 size_t i_file_size = strlen( &file[0] );
429
430                 strncpy( &path[0], p_menu->psz_path, i_path_size );
431                 strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
432                 path[ i_path_size + i_file_size ] = '\0';
433
434                 p_state_current = osd_StateNew( p_menu, &path[0], &state[0] );
435             }
436             else /* absolute paths are used. */
437                 p_state_current = osd_StateNew( p_menu, &file[0], &state[0] );
438
439             if( !p_state_current || !p_state_current->p_pic )
440                 goto error;
441
442             p_state_current->i_x = i_x;
443             p_state_current->i_y = i_y;
444
445             if( p_state_prev )
446                 p_state_prev->p_next = p_state_current;
447             else
448                 p_current->p_states = p_state_current;
449             p_state_current->p_prev = p_state_prev;
450
451             msg_Dbg( p_this, " |- state=%s, file=%s%s", &state[0],
452                      p_menu->psz_path, &file[0] );
453         }
454         p_current->p_current_state = p_current->p_states;
455     }
456
457     /* Find the last button and store its pointer.
458      * The OSD menu behaves like a roundrobin list.
459      */
460     p_current = p_menu->p_button;
461     while( p_current && p_current->p_next )
462     {
463         osd_button_t *p_temp = NULL;
464         p_temp = p_current->p_next;
465         p_current = p_temp;
466     }
467     p_menu->p_last_button = p_current;
468     fclose( fd );
469     return VLC_SUCCESS;
470
471 #undef MAX_FILE_PATH
472 error:
473     msg_Err( p_menu, "parsing file failed (returned %d)", result );
474     osd_MenuFree( p_menu );
475     fclose( fd );
476     return VLC_EGENERIC;
477 }