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