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