]> git.sesse.net Git - vlc/blob - src/osd/osd_parser.c
Forward port of branches/0.8.1-jpsaman-thedj revision 12070. The OSD menu subsystem...
[vlc] / src / osd / osd_parser.c
1 /*****************************************************************************\r
2  * osd_parser.c - The OSD Menu  parser core code.\r
3  *****************************************************************************\r
4  * Copyright (C) 2005 M2X\r
5  * $Id: osd_parser.c 9451 2004-12-01 01:07:08Z jpsaman $\r
6  *\r
7  * Authors: Jean-Paul Saman <jpsaman #_at_# m2x dot nl>\r
8  *\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
13  *\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
18  *\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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.\r
22  *****************************************************************************/\r
23 \r
24 /*****************************************************************************\r
25  * Preamble\r
26  *****************************************************************************/\r
27 #include <stdlib.h>\r
28 #include <string.h>\r
29 \r
30 #include <vlc/vlc.h>\r
31 #include <vlc_config.h>\r
32 //#include <vlc_es.h>\r
33 #include <vlc_video.h>\r
34 \r
35 #include <vlc_keys.h>\r
36 #include <vlc_image.h>\r
37 #include <vlc_osd.h>\r
38 \r
39 \r
40 #undef OSD_MENU_DEBUG\r
41 \r
42 /*****************************************************************************\r
43  * Local prototypes\r
44  *****************************************************************************/\r
45 static const char *ppsz_button_states[] = { "unselect", "select", "pressed" };\r
46 \r
47 /* OSD Menu structure support routines */\r
48 static osd_menu_t   *osd_MenuNew( osd_menu_t *, const char *, int, int );\r
49 static osd_button_t *osd_ButtonNew( const char *, int, int );\r
50 static osd_state_t  *osd_StateNew( vlc_object_t *, const char *, const char * );\r
51 \r
52 static void osd_MenuFree  ( vlc_object_t *, osd_menu_t * );\r
53 static void osd_ButtonFree( vlc_object_t *, osd_button_t * );\r
54 static void osd_StatesFree( vlc_object_t *, osd_state_t * );\r
55 \r
56 static picture_t *osd_LoadImage( vlc_object_t *, const char *);\r
57 \r
58 #if 0\r
59 /*****************************************************************************\r
60  * osd_YuvaYuvp\r
61  *****************************************************************************/\r
62 static picture_t *osd_YuvaYuvp( vlc_object_t *p_this, picture_t *p_picture )\r
63 {\r
64     video_format_t *p_fmt = NULL;\r
65     int i = 0, j = 0, n = 0, p = 0;\r
66     int i_max_entries = 256;\r
67 \r
68 #ifdef RANDOM_DITHERING\r
69     int i_seed = 0xdeadbeef; /* random seed */\r
70 #else\r
71     int *pi_delta = NULL;\r
72 #endif\r
73     int i_pixels = p_picture->p[0].i_visible_lines\r
74                     * p_picture->p[0].i_pitch;\r
75     int i_iterator = p_picture->p[0].i_visible_lines * 3 / 4\r
76                         * p_picture->p[0].i_pitch\r
77                     + p_picture->p[0].i_pitch * 1 / 3;\r
78     int i_tolerance = 0;\r
79 \r
80     p_fmt = (video_format_t*) malloc( sizeof( video_format_t ) );\r
81     if( !p_fmt )\r
82     {\r
83         msg_Err( p_this, "couldn't allocate video_format_t ... aborting YUVA to YUVP conversion of picture" );\r
84         return p_picture;\r
85     }\r
86     p_fmt->i_chroma = VLC_FOURCC('Y','U','V','P');\r
87     p_fmt->p_palette = (video_palette_t *) malloc( sizeof( video_palette_t ) );\r
88     if( !p_fmt->p_palette )\r
89     {\r
90         msg_Err( p_this, "couldn't allocate video_palette_t ... aborting YUVA to YUVP conversion of picture" );\r
91         free( p_fmt );\r
92         return p_picture;        \r
93     }    \r
94     p_fmt->p_palette->i_entries = 0;\r
95 \r
96     /* Find best iterator using Euclide’s algorithm */\r
97     for( ; i_iterator > 1 ; i_iterator-- )\r
98     {\r
99         int a = i_pixels;\r
100         int b = i_iterator;\r
101         int c;\r
102 \r
103         while( b )\r
104         {\r
105             c = a % b;\r
106             a = b;\r
107             b = c;\r
108         }\r
109 \r
110         if( a == 1 )\r
111         {\r
112             break;\r
113         }\r
114     }\r
115 \r
116     /* Count colors, build best palette */\r
117     for( i_tolerance = 0; i_tolerance < 128; i_tolerance++ )\r
118     {\r
119         vlc_bool_t b_success = VLC_TRUE;\r
120         p_fmt->p_palette->i_entries = 0;\r
121 \r
122         for( i = 0; i < i_pixels ; )\r
123         {\r
124             uint8_t y  = 0, u = 0, v = 0, a = 0;\r
125             y = p_picture->p[0].p_pixels[i];\r
126             u = p_picture->p[1].p_pixels[i];\r
127             v = p_picture->p[2].p_pixels[i];\r
128             a = p_picture->p[3].p_pixels[i];\r
129             for( j = 0; j < p_fmt->p_palette->i_entries; j++ )\r
130             {\r
131                 if( abs((int)p_fmt->p_palette->palette[j][0] - (int)y) <= i_tolerance &&\r
132                     abs((int)p_fmt->p_palette->palette[j][1] - (int)u) <= i_tolerance &&\r
133                     abs((int)p_fmt->p_palette->palette[j][2] - (int)v) <= i_tolerance &&\r
134                     abs((int)p_fmt->p_palette->palette[j][3] - (int)a) <= i_tolerance / 2 )\r
135                 {\r
136                     break;\r
137                 }\r
138             }\r
139             if( j == p_fmt->p_palette->i_entries )\r
140             {\r
141                 p_fmt->p_palette->palette[j][0] = y;\r
142                 p_fmt->p_palette->palette[j][1] = u;\r
143                 p_fmt->p_palette->palette[j][2] = v;\r
144                 p_fmt->p_palette->palette[j][3] = a;\r
145                 p_fmt->p_palette->i_entries++;\r
146             }\r
147             if( p_fmt->p_palette->i_entries >= i_max_entries )\r
148             {\r
149                 b_success = VLC_FALSE;\r
150                 break;\r
151             }\r
152             i += i_iterator;\r
153             if( i > i_pixels )\r
154             {\r
155                 i -= i_pixels;\r
156             }\r
157         }\r
158 \r
159         if( b_success )\r
160         {\r
161             break;\r
162         }\r
163     }\r
164 \r
165 #if OSD_MENU_DEBUG\r
166     msg_Dbg( p_this, "best palette has %d colors", p_fmt->p_palette->i_entries );\r
167 #endif\r
168 \r
169 #ifndef RANDOM_DITHERING\r
170     pi_delta = malloc( ( p_picture->p[0].i_pitch + 1 ) * sizeof(int) * 4  );\r
171     if( !pi_delta )\r
172     {\r
173         msg_Err( p_this, "couldn't allocate video_palette_t ... aborting YUVA to YUVP conversion of picture" );\r
174         free( p_fmt->p_palette );\r
175         free( p_fmt );\r
176         return p_picture;        \r
177     }    \r
178     \r
179     for( i = 0; i < (p_picture->p[0].i_pitch + 1) * 4 ; i++ )\r
180     {\r
181         pi_delta[ i ] = 0;\r
182     }\r
183 #endif\r
184 \r
185     /* Fill image with our new colours */\r
186     for( p = 0; p < p_picture->p[0].i_visible_lines ; p++ )\r
187     {\r
188         int i_ydelta = 0, i_udelta = 0, i_vdelta = 0, i_adelta = 0;\r
189 \r
190         for( n = 0; n < p_picture->p[0].i_pitch ; n++ )\r
191         {\r
192             int i_offset = p * p_picture->p[0].i_pitch + n;\r
193             int y, u, v, a;\r
194             int i_mindist, i_best;\r
195 \r
196             y = (int)p_picture->p[0].p_pixels[i_offset];\r
197             u = (int)p_picture->p[1].p_pixels[i_offset];\r
198             v = (int)p_picture->p[2].p_pixels[i_offset];\r
199             a = (int)p_picture->p[3].p_pixels[i_offset];\r
200 \r
201             /* Add dithering compensation */\r
202 #ifdef RANDOM_DITHERING\r
203             y += ((i_seed & 0xff) - 0x80) * i_tolerance / 0x80;\r
204             u += (((i_seed >> 8) & 0xff) - 0x80) * i_tolerance / 0x80;\r
205             v += (((i_seed >> 16) & 0xff) - 0x80) * i_tolerance / 0x80;\r
206             a += (((i_seed >> 24) & 0xff) - 0x80) * i_tolerance / 0x80;\r
207 #else\r
208             y += i_ydelta + pi_delta[ n * 4 ];\r
209             u += i_udelta + pi_delta[ n * 4 + 1 ];\r
210             v += i_vdelta + pi_delta[ n * 4 + 2 ];\r
211             a += i_adelta + pi_delta[ n * 4 + 3 ];\r
212 #endif\r
213 \r
214             /* Find best colour in palette */\r
215             for( i_mindist = 99999999, i_best = 0, j = 0; j < p_fmt->p_palette->i_entries; j++ )\r
216             {\r
217                 int i_dist = 0;\r
218 \r
219                 i_dist += abs((int)p_fmt->p_palette->palette[j][0] - y);\r
220                 i_dist += abs((int)p_fmt->p_palette->palette[j][1] - u);\r
221                 i_dist += abs((int)p_fmt->p_palette->palette[j][2] - v);\r
222                 i_dist += 2 * abs((int)p_fmt->p_palette->palette[j][3] - a);\r
223 \r
224                 if( i_dist < i_mindist )\r
225                 {\r
226                     i_mindist = i_dist;\r
227                     i_best = j;\r
228                 }\r
229             }\r
230 \r
231             /* Set pixel to best color */\r
232             p_picture->p[0].p_pixels[i_offset] = i_best;\r
233 \r
234             /* Update dithering state */\r
235 #ifdef RANDOM_DITHERING\r
236             i_seed = (i_seed * 0x1283837) ^ 0x789479 ^ (i_seed >> 13);\r
237 #else\r
238             i_ydelta = y - (int)p_fmt->p_palette->palette[i_best][0];\r
239             i_udelta = u - (int)p_fmt->p_palette->palette[i_best][1];\r
240             i_vdelta = v - (int)p_fmt->p_palette->palette[i_best][2];\r
241             i_adelta = a - (int)p_fmt->p_palette->palette[i_best][3];\r
242             pi_delta[ n * 4 ] = i_ydelta * 3 / 8;\r
243             pi_delta[ n * 4 + 1 ] = i_udelta * 3 / 8;\r
244             pi_delta[ n * 4 + 2 ] = i_vdelta * 3 / 8;\r
245             pi_delta[ n * 4 + 3 ] = i_adelta * 3 / 8;\r
246             i_ydelta = i_ydelta * 5 / 8;\r
247             i_udelta = i_udelta * 5 / 8;\r
248             i_vdelta = i_vdelta * 5 / 8;\r
249             i_adelta = i_adelta * 5 / 8;\r
250 #endif\r
251         }\r
252     }\r
253 \r
254 #ifndef RANDOM_DITHERING\r
255     free( pi_delta );\r
256 #endif\r
257 \r
258     /* pad palette */\r
259     for( i = p_fmt->p_palette->i_entries; i < i_max_entries; i++ )\r
260     {\r
261         p_fmt->p_palette->palette[i][0] = 0;\r
262         p_fmt->p_palette->palette[i][1] = 0;\r
263         p_fmt->p_palette->palette[i][2] = 0;\r
264         p_fmt->p_palette->palette[i][3] = 0;\r
265     }\r
266     p_fmt->p_palette->i_entries = i_max_entries;\r
267 #if OSD_MENU_DEBUG\r
268     msg_Dbg( p_this, "best palette has %d colors", p_fmt->p_palette->i_entries );\r
269 #endif\r
270 \r
271     p_picture->format.i_chroma = VLC_FOURCC('Y','U','V','P');\r
272     if( p_picture->format.p_palette )\r
273         free( p_picture->format.p_palette );\r
274     p_picture->format.p_palette = p_fmt->p_palette;\r
275     free( p_fmt );\r
276     return p_picture;\r
277 }\r
278 #endif\r
279 /*****************************************************************************\r
280  * osd_LoadImage: loads the logo image into memory\r
281  *****************************************************************************/\r
282 static picture_t *osd_LoadImage( vlc_object_t *p_this, const char *psz_filename )\r
283 {\r
284     picture_t *p_pic = NULL;\r
285     image_handler_t *p_image;\r
286     video_format_t fmt_in = {0}, fmt_out = {0};\r
287 \r
288     fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');\r
289     p_image = image_HandlerCreate( p_this );\r
290     if( p_image )\r
291     {\r
292         p_pic = image_ReadUrl( p_image, psz_filename, &fmt_in, &fmt_out );\r
293         image_HandlerDelete( p_image );\r
294 #if 0        \r
295         p_pic = osd_YuvaYuvp( p_this, p_pic );\r
296 #endif\r
297     }\r
298     else msg_Err( p_this, "unable to handle this chroma" );\r
299 \r
300     return p_pic;\r
301 }\r
302 \r
303 /*****************************************************************************\r
304  * Create a new Menu structure\r
305  *****************************************************************************/\r
306 static osd_menu_t *osd_MenuNew( osd_menu_t *p_menu, const char *psz_path, int i_x, int i_y )\r
307 {\r
308     if( !p_menu ) return NULL;\r
309     \r
310     p_menu->p_state = (osd_menu_state_t *) malloc( sizeof( osd_menu_state_t ) );\r
311     if( !p_menu->p_state )\r
312         msg_Err( p_menu, "memory allocation for OSD Menu state failed." );\r
313 \r
314     if( psz_path != NULL )\r
315         p_menu->psz_path = strdup( psz_path );\r
316     else\r
317         p_menu->psz_path = NULL;\r
318     p_menu->i_x = i_x;\r
319     p_menu->i_y = i_y;\r
320     \r
321     return p_menu; \r
322 }\r
323 \r
324 /*****************************************************************************\r
325  * Free the menu\r
326  *****************************************************************************/\r
327 static void osd_MenuFree( vlc_object_t *p_this, osd_menu_t *p_menu )\r
328 {\r
329     msg_Dbg( p_this, "freeing menu" );\r
330     osd_ButtonFree( p_this, p_menu->p_button );\r
331     p_menu->p_button = NULL;\r
332     p_menu->p_last_button = NULL;\r
333     if( p_menu->psz_path ) free( p_menu->psz_path );\r
334     p_menu->psz_path = NULL;\r
335     if( p_menu->p_state ) free( p_menu->p_state );\r
336     p_menu->p_state = NULL;\r
337 }\r
338 \r
339 /*****************************************************************************\r
340  * Create a new button\r
341  *****************************************************************************/\r
342 static osd_button_t *osd_ButtonNew( const char *psz_action, int i_x, int i_y )\r
343 {\r
344     osd_button_t *p_button = NULL;\r
345     p_button = (osd_button_t*) malloc( sizeof(osd_button_t) );\r
346     if( !p_button )\r
347         return NULL;\r
348     \r
349     memset( p_button, 0, sizeof(osd_button_t) );\r
350     p_button->psz_action = strdup(psz_action);\r
351     p_button->psz_action_down = NULL;\r
352     p_button->p_feedback = NULL;\r
353     p_button->i_x = i_x;\r
354     p_button->i_y = i_y;\r
355     \r
356     return p_button;\r
357 }\r
358 \r
359 /*****************************************************************************\r
360  * Free a button\r
361  *****************************************************************************/ \r
362 static void osd_ButtonFree( vlc_object_t *p_this, osd_button_t *p_button )\r
363 {\r
364     osd_button_t *p_current = p_button;\r
365     osd_button_t *p_next = NULL;\r
366     osd_button_t *p_prev = NULL;\r
367     \r
368     /* First walk to the end. */\r
369     while( p_current->p_next )\r
370     {\r
371         p_next = p_current->p_next;\r
372         p_current = p_next;        \r
373     }\r
374     /* Then free end first and walk to the start. */\r
375     while( p_current->p_prev )\r
376     {\r
377         msg_Dbg( p_this, "+ freeing button %s [%p]", p_current->psz_action, p_current );\r
378         p_prev = p_current->p_prev;\r
379         p_current = p_prev;\r
380         if( p_current->p_next )\r
381         {\r
382             if( p_current->p_next->psz_name ) \r
383                 free( p_current->p_next->psz_name );\r
384             if( p_current->p_next->psz_action )\r
385                 free( p_current->p_next->psz_action );\r
386             if( p_current->p_next->psz_action_down )\r
387                 free( p_current->p_next->psz_action_down );\r
388             if( p_current->p_feedback && p_current->p_feedback->p_data_orig )\r
389                 free( p_current->p_feedback->p_data_orig );\r
390             if( p_current->p_feedback )\r
391                 free( p_current->p_feedback );\r
392             \r
393             p_current->p_next->psz_action_down = NULL;\r
394             p_current->p_next->psz_action = NULL;\r
395             p_current->p_next->psz_name = NULL;\r
396             p_current->p_feedback = NULL;\r
397                             \r
398             /* Free all states first */            \r
399             if( p_current->p_next->p_states )   \r
400                 osd_StatesFree( p_this, p_current->p_next->p_states );\r
401             p_current->p_next->p_states = NULL;          \r
402             if( p_current->p_next) free( p_current->p_next );\r
403             p_current->p_next = NULL;  \r
404         }            \r
405         \r
406         if( p_current->p_up )\r
407         {\r
408             if( p_current->p_up->psz_name ) \r
409                 free( p_current->p_up->psz_name );\r
410             if( p_current->p_up->psz_action )\r
411                 free( p_current->p_up->psz_action );\r
412             if( p_current->p_up->psz_action_down )\r
413                 free( p_current->p_up->psz_action_down );\r
414             if( p_current->p_feedback && p_current->p_feedback->p_data_orig )\r
415                 free( p_current->p_feedback->p_data_orig );\r
416             if( p_current->p_feedback )\r
417                 free( p_current->p_feedback );\r
418             \r
419             p_current->p_up->psz_action_down = NULL;\r
420             p_current->p_up->psz_action = NULL;\r
421             p_current->p_up->psz_name = NULL;\r
422             p_current->p_feedback = NULL;\r
423             \r
424             /* Free all states first */            \r
425             if( p_current->p_up->p_states )   \r
426                 osd_StatesFree( p_this, p_current->p_up->p_states );\r
427             p_current->p_up->p_states = NULL;          \r
428             if( p_current->p_up ) free( p_current->p_up );\r
429             p_current->p_up = NULL;          \r
430         }\r
431     }    \r
432     /* Free the last one. */\r
433     if( p_button ) \r
434     {\r
435         msg_Dbg( p_this, "+ freeing button %s [%p]", p_button->psz_action, p_button );    \r
436         if( p_button->psz_name ) free( p_button->psz_name );\r
437         if( p_button->psz_action ) free( p_button->psz_action );\r
438         if( p_button->psz_action_down ) free( p_button->psz_action_down );   \r
439         if( p_current->p_feedback && p_current->p_feedback->p_data_orig )\r
440             free( p_current->p_feedback->p_data_orig );\r
441         if( p_current->p_feedback )\r
442             free( p_current->p_feedback );\r
443         \r
444         p_button->psz_name = NULL;\r
445         p_button->psz_action = NULL;\r
446         p_button->psz_action_down = NULL;\r
447         p_current->p_feedback = NULL;\r
448                 \r
449         if( p_button->p_states )\r
450             osd_StatesFree( p_this, p_button->p_states );\r
451         p_button->p_states = NULL;          \r
452         free( p_button );\r
453         p_button = NULL;\r
454     }\r
455 }\r
456 \r
457 /*****************************************************************************\r
458  * Create a new state image\r
459  *****************************************************************************/\r
460 static osd_state_t *osd_StateNew( vlc_object_t *p_this, const char *psz_file, const char *psz_state )\r
461 {\r
462     osd_state_t *p_state = NULL;\r
463     p_state = (osd_state_t*) malloc( sizeof(osd_state_t) );\r
464     if( !p_state )\r
465         return NULL;\r
466         \r
467     memset( p_state, 0, sizeof(osd_state_t) );    \r
468     p_state->p_pic = osd_LoadImage( p_this, psz_file );\r
469 \r
470     if( psz_state )\r
471     {\r
472         p_state->psz_state = strdup( psz_state );\r
473         if( strncmp( ppsz_button_states[0], psz_state, strlen(ppsz_button_states[0]) ) == 0 )\r
474             p_state->i_state = OSD_BUTTON_UNSELECT;\r
475         else if( strncmp( ppsz_button_states[1], psz_state, strlen(ppsz_button_states[1]) ) == 0 )\r
476             p_state->i_state = OSD_BUTTON_SELECT;\r
477         else if( strncmp( ppsz_button_states[2], psz_state, strlen(ppsz_button_states[2]) ) == 0 )\r
478             p_state->i_state = OSD_BUTTON_PRESSED;\r
479     }\r
480     return p_state;\r
481 }\r
482 \r
483 /*****************************************************************************\r
484  * Free state images\r
485  *****************************************************************************/\r
486 static void osd_StatesFree( vlc_object_t *p_this, osd_state_t *p_states )\r
487 {\r
488     osd_state_t *p_state = p_states;\r
489     osd_state_t *p_next = NULL;\r
490     osd_state_t *p_prev = NULL;\r
491     \r
492     while( p_state->p_next )\r
493     {\r
494         p_next = p_state->p_next;\r
495         p_state = p_next;\r
496     }\r
497     /* Then free end first and walk to the start. */\r
498     while( p_state->p_prev )\r
499     {\r
500         msg_Dbg( p_this, " |- freeing state %s [%p]", p_state->psz_state, p_state );\r
501         p_prev = p_state->p_prev;\r
502         p_state = p_prev;\r
503         if( p_state->p_next )\r
504         {\r
505             if( p_state->p_next->p_pic && p_state->p_next->p_pic->p_data_orig )\r
506                 free( p_state->p_next->p_pic->p_data_orig );\r
507             if( p_state->p_next->p_pic ) free( p_state->p_next->p_pic );\r
508             p_state->p_next->p_pic = NULL;      \r
509             if( p_state->p_next->psz_state ) free( p_state->p_next->psz_state );\r
510             p_state->p_next->psz_state = NULL;\r
511             free( p_state->p_next );\r
512             p_state->p_next = NULL;        \r
513         }\r
514     }\r
515     /* Free the last one. */\r
516     if( p_states )\r
517     {\r
518         msg_Dbg( p_this, " |- freeing state %s [%p]", p_state->psz_state, p_states );\r
519         if( p_states->p_pic && p_states->p_pic->p_data_orig )\r
520             free( p_states->p_pic->p_data_orig );\r
521         if( p_states->p_pic ) free( p_states->p_pic );\r
522         p_states->p_pic = NULL;\r
523         if( p_state->psz_state ) free( p_state->psz_state );\r
524         p_state->psz_state = NULL;\r
525         free( p_states );\r
526         p_states = NULL;\r
527     }\r
528 }\r
529 \r
530 /*****************************************************************************\r
531  * osd_ConfigLoader: Load and parse osd text configurationfile\r
532  *****************************************************************************/\r
533 int osd_ConfigLoader( vlc_object_t *p_this, const char *psz_file,\r
534     osd_menu_t **p_menu )\r
535 {\r
536     osd_button_t   *p_current = NULL; /* button currently processed */\r
537     osd_button_t   *p_prev = NULL;    /* previous processed button */ \r
538 \r
539 #define MAX_FILE_PATH 256    \r
540     FILE       *fd = NULL;\r
541     int        result = 0;\r
542     \r
543     msg_Dbg( p_this, "opening osd definition file %s", psz_file );\r
544     fd = fopen( psz_file, "r" );\r
545     if( !fd )  \r
546     {\r
547         msg_Err( p_this, "failed opening osd definition file %s", psz_file );\r
548         return VLC_EGENERIC;\r
549     }\r
550     \r
551     /* Read first line */    \r
552     if( !feof( fd ) )\r
553     {\r
554         char action[25] = "";\r
555         char path[MAX_FILE_PATH] = "";\r
556         char *psz_path = NULL;\r
557         size_t i_len = 0;\r
558 \r
559         /* override images path ? */\r
560         psz_path = config_GetPsz( p_this, "osdmenu-file-path" );\r
561         if( psz_path == NULL )\r
562         {                                    \r
563             result = fscanf(fd, "%24s %255s", &action[0], &path[0] );\r
564         }\r
565         else\r
566         {\r
567             /* psz_path is not null and therefor &path[0] cannot be NULL \r
568              * it might be null terminated.\r
569              */\r
570             strncpy( &path[0], psz_path, MAX_FILE_PATH );\r
571             free( psz_path );\r
572             psz_path = NULL;\r
573         }\r
574         /* NULL terminate before asking the length of path[] */\r
575         path[MAX_FILE_PATH-1] = '\0';\r
576         i_len = strlen(&path[0]);\r
577         if( i_len == MAX_FILE_PATH )\r
578             i_len--; /* truncate to prevent buffer overflow */\r
579 #if defined(WIN32) || defined(UNDER_CE)\r
580         if( (i_len > 0) && path[i_len] != '\\' )\r
581             path[i_len] = '\\';\r
582 #else        \r
583         if( (i_len > 0) && path[i_len] != '/' )\r
584             path[i_len] = '/';\r
585 #endif\r
586         path[i_len+1] = '\0';\r
587         if( result == 0 || result == EOF )\r
588             goto error;                        \r
589         msg_Dbg( p_this, "%s=%s", &action[0], &path[0] );\r
590          \r
591         if( i_len == 0 )\r
592             *p_menu = osd_MenuNew( *p_menu, NULL, 0, 0 );\r
593         else\r
594             *p_menu = osd_MenuNew( *p_menu, &path[0], 0, 0 );\r
595     }\r
596     \r
597     if( !*p_menu )\r
598         goto error;\r
599         \r
600     /* read successive lines */\r
601     while( !feof( fd ) )\r
602     {   \r
603         osd_state_t   *p_state_current = NULL; /* button state currently processed */\r
604         osd_state_t   *p_state_prev = NULL;    /* previous state processed button */ \r
605         \r
606         char cmd[25] = "";      \r
607         char action[25] = "";\r
608         char state[25]  = "";\r
609         char file[256]  = "";\r
610         char path[512]  = "";        \r
611         int  i_x = 0;\r
612         int  i_y = 0;\r
613 \r
614         result = fscanf( fd, "%24s %24s (%d,%d)", &cmd[0], &action[0], &i_x, &i_y );\r
615         if( result == 0 )\r
616             goto error;              \r
617         if( strncmp( &cmd[0], "action", 6 ) != 0 )\r
618             break;\r
619         msg_Dbg( p_this, " + %s hotkey=%s (%d,%d)", &cmd[0], &action[0], i_x, i_y );                    \r
620                 \r
621         p_prev = p_current;\r
622         p_current = osd_ButtonNew( &action[0], i_x, i_y );   \r
623         if( !p_current )\r
624             goto error;\r
625         \r
626         if( p_prev )\r
627             p_prev->p_next = p_current;            \r
628         else\r
629             (*p_menu)->p_button = p_current;            \r
630         p_current->p_prev = p_prev;\r
631         \r
632         /* parse all states */\r
633         while( !feof( fd ) )\r
634         {\r
635             char type[25] = "";\r
636             \r
637             result = fscanf( fd, "\t%24s", &state[0] );   \r
638             if( result == 0 )\r
639                 goto error;\r
640             \r
641             /* FIXME: We only parse one level deep now */    \r
642             if( strncmp( &state[0], "action", 6 ) == 0 )\r
643             {\r
644                 osd_button_t   *p_up = NULL;\r
645                 \r
646                 result = fscanf( fd, "%24s (%d,%d)", &action[0], &i_x, &i_y );\r
647                 if( result == 0 )\r
648                     goto error;\r
649                 /* create new button */                \r
650                 p_up = osd_ButtonNew( &action[0], i_x, i_y );                 \r
651                 if( !p_up ) \r
652                     goto error;\r
653                 /* Link to list */                    \r
654                 p_up->p_down = p_current;\r
655                 p_current->p_up = p_up;                 \r
656                 msg_Dbg( p_this, " + (menu up) hotkey=%s (%d,%d)", &action[0], i_x, i_y );\r
657                 /* Parse type state */\r
658                 result = fscanf( fd, "\t%24s %24s", &cmd[0], &type[0] );   \r
659                 if( result == 0 )\r
660                     goto error;\r
661                 if( strncmp( &cmd[0], "type", 4 ) == 0 )\r
662                 {  \r
663                     if( strncmp( &type[0], "volume", 6 ) == 0 )\r
664                     {\r
665                         (*p_menu)->p_state->p_volume = p_up;\r
666                         msg_Dbg( p_this, " + type=%s", &type[0] );\r
667                     }\r
668                 }\r
669                 /* Parse range state */\r
670                 result = fscanf( fd, "\t%24s", &state[0] );   \r
671                 if( result == 0 )\r
672                     goto error;\r
673                 /* Parse the range state */        \r
674                 if( strncmp( &state[0], "range", 5 ) == 0 )\r
675                 {\r
676                     osd_state_t   *p_range_current = NULL; /* range state currently processed */\r
677                     osd_state_t   *p_range_prev = NULL;    /* previous state processed range */ \r
678                     int i_index = 0;\r
679                                                             \r
680                     p_up->b_range = VLC_TRUE;\r
681     \r
682                     result = fscanf( fd, "\t%24s", &action[0] );   \r
683                     if( result == 0 )\r
684                         goto error;\r
685                     \r
686                     result = fscanf( fd, "\t%d", &i_index );   \r
687                     if( result == 0 )\r
688                         goto error;\r
689                                                                                             \r
690                     msg_Dbg( p_this, " + (menu up) hotkey down %s, file=%s%s", &action[0], (*p_menu)->psz_path, &file[0] );\r
691                     \r
692                     if( p_up->psz_action_down ) free( p_up->psz_action_down );\r
693                     p_up->psz_action_down = strdup( &action[0] );                     \r
694                     \r
695                     /* Parse range contstruction :\r
696                      * range <hotkey> \r
697                      *      <state1> <file1>\r
698                      *\r
699                      *      <stateN> <fileN>\r
700                      * end \r
701                      */                \r
702                     while( !feof( fd ) )\r
703                     {\r
704                         result = fscanf( fd, "\t%255s", &file[0] );   \r
705                         if( result == 0 )\r
706                             goto error;\r
707                         if( strncmp( &file[0], "end", 3 ) == 0 )\r
708                             break;\r
709                         \r
710                         p_range_prev = p_range_current;\r
711             \r
712                         if( (*p_menu)->psz_path )\r
713                         {\r
714                             size_t i_path_size = strlen( (*p_menu)->psz_path );\r
715                             size_t i_file_size = strlen( &file[0] );\r
716                             \r
717                             strncpy( &path[0], (*p_menu)->psz_path, i_path_size );\r
718                             strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );\r
719                             path[ i_path_size + i_file_size ] = '\0';\r
720                             \r
721                             p_range_current = osd_StateNew( p_this, &path[0], "pressed" );\r
722                         }\r
723                         else /* absolute paths are used. */\r
724                             p_range_current = osd_StateNew( p_this, &file[0], "pressed" );\r
725                             \r
726                         if( !p_range_current || !p_range_current->p_pic )\r
727                             goto error;\r
728                             \r
729                         /* increment the number of ranges for this button */                \r
730                         p_up->i_ranges++;\r
731                         \r
732                         if( p_range_prev )\r
733                             p_range_prev->p_next = p_range_current;\r
734                         else\r
735                             p_up->p_states = p_range_current;                                \r
736                         p_range_current->p_prev = p_range_prev;\r
737                         \r
738                         msg_Dbg( p_this, "  |- range=%d, file=%s%s", \r
739                                 p_up->i_ranges, \r
740                                 (*p_menu)->psz_path, &file[0] );                        \r
741                     }\r
742                     if( i_index > 0 )\r
743                     { \r
744                         osd_state_t *p_range = NULL;\r
745                         \r
746                         /* Find the default index for state range */                        \r
747                         p_range = p_up->p_states;\r
748                         while( (--i_index > 0) && p_range->p_next )\r
749                         {\r
750                             osd_state_t *p_temp = NULL;\r
751                             p_temp = p_range->p_next;\r
752                             p_range = p_temp;\r
753                         }\r
754                         p_up->p_current_state = p_range;\r
755                     }                    \r
756                     else p_up->p_current_state = p_up->p_states;\r
757                     \r
758                 }   \r
759                 result = fscanf( fd, "\t%24s", &state[0] );   \r
760                 if( result == 0 )\r
761                     goto error;                    \r
762                 if( strncmp( &state[0], "end", 3 ) != 0 )\r
763                     goto error;\r
764                     \r
765                 /* Continue at the beginning of the while() */\r
766                 continue;\r
767             }\r
768             \r
769             /* Parse the range state */        \r
770             if( strncmp( &state[0], "range", 5 ) == 0 )\r
771             {\r
772                 osd_state_t   *p_range_current = NULL; /* range state currently processed */\r
773                 osd_state_t   *p_range_prev = NULL;    /* previous state processed range */ \r
774                 int i_index = 0;\r
775                                                         \r
776                 p_current->b_range = VLC_TRUE;\r
777 \r
778                 result = fscanf( fd, "\t%24s", &action[0] );   \r
779                 if( result == 0 )\r
780                     goto error;\r
781                 \r
782                 result = fscanf( fd, "\t%d", &i_index );   \r
783                 if( result == 0 )\r
784                     goto error;\r
785                                                                                         \r
786                 msg_Dbg( p_this, " + hotkey down %s, file=%s%s", &action[0], (*p_menu)->psz_path, &file[0] );                        \r
787                 if( p_current->psz_action_down ) free( p_current->psz_action_down );\r
788                 p_current->psz_action_down = strdup( &action[0] );                     \r
789                 \r
790                 /* Parse range contstruction :\r
791                  * range <hotkey> \r
792                  *      <state1> <file1>\r
793                  *\r
794                  *      <stateN> <fileN>\r
795                  * end \r
796                  */                \r
797                 while( !feof( fd ) )\r
798                 {\r
799                     result = fscanf( fd, "\t%255s", &file[0] );   \r
800                     if( result == 0 )\r
801                         goto error;\r
802                     if( strncmp( &file[0], "end", 3 ) == 0 )\r
803                         break;\r
804                     \r
805                     p_range_prev = p_range_current;\r
806         \r
807                     if( (*p_menu)->psz_path )\r
808                     {\r
809                         size_t i_path_size = strlen( (*p_menu)->psz_path );\r
810                         size_t i_file_size = strlen( &file[0] );\r
811                         \r
812                         strncpy( &path[0], (*p_menu)->psz_path, i_path_size );\r
813                         strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );\r
814                         path[ i_path_size + i_file_size ] = '\0';\r
815                         \r
816                         p_range_current = osd_StateNew( p_this, &path[0], "pressed" );\r
817                     }\r
818                     else /* absolute paths are used. */\r
819                         p_range_current = osd_StateNew( p_this, &file[0], "pressed" );\r
820                         \r
821                     if( !p_range_current || !p_range_current->p_pic )\r
822                         goto error;\r
823                         \r
824                     /* increment the number of ranges for this button */                \r
825                     p_current->i_ranges++;\r
826                     \r
827                     if( p_range_prev )\r
828                         p_range_prev->p_next = p_range_current;\r
829                     else\r
830                         p_current->p_states = p_range_current;                                \r
831                     p_range_current->p_prev = p_range_prev;\r
832                     \r
833                     msg_Dbg( p_this, "  |- range=%d, file=%s%s", \r
834                             p_current->i_ranges, \r
835                             (*p_menu)->psz_path, &file[0] );                        \r
836                 }\r
837                 if( i_index > 0 )\r
838                 {             \r
839                     osd_state_t *p_range = NULL;\r
840                        \r
841                     /* Find the default index for state range */                        \r
842                     p_range = p_current->p_states;\r
843                     while( (--i_index > 0) && p_range->p_next )\r
844                     {\r
845                         osd_state_t *p_temp = NULL;\r
846                         p_temp = p_range->p_next;\r
847                         p_range = p_temp;\r
848                     }\r
849                     p_current->p_current_state = p_range;\r
850                 }                    \r
851                 else p_current->p_current_state = p_current->p_states;\r
852                 /* Continue at the beginning of the while() */\r
853                 continue;\r
854             }   \r
855             if( strncmp( &state[0], "end", 3 ) == 0 )\r
856                 break;\r
857                 \r
858             result = fscanf( fd, "\t%255s", &file[0] );   \r
859             if( result == 0 )\r
860                 goto error;                                \r
861             \r
862             p_state_prev = p_state_current;\r
863 \r
864             if( ( strncmp( ppsz_button_states[0], &state[0], strlen(ppsz_button_states[0]) ) != 0 ) &&\r
865                 ( strncmp( ppsz_button_states[1], &state[0], strlen(ppsz_button_states[1]) ) != 0 ) &&\r
866                 ( strncmp( ppsz_button_states[2], &state[0], strlen(ppsz_button_states[2]) ) != 0 ) )\r
867             {\r
868                 msg_Err( p_this, "invalid button state %s for button %s expected %d: unselect, select or pressed)",\r
869                                     &state[0], &action[0], strlen(&state[0]));\r
870                 goto error;\r
871             }\r
872             \r
873             if( (*p_menu)->psz_path )\r
874             {\r
875                 size_t i_path_size = strlen( (*p_menu)->psz_path );\r
876                 size_t i_file_size = strlen( &file[0] );\r
877                 \r
878                 strncpy( &path[0], (*p_menu)->psz_path, i_path_size );\r
879                 strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );\r
880                 path[ i_path_size + i_file_size ] = '\0';\r
881                 \r
882                 p_state_current = osd_StateNew( p_this, &path[0], &state[0] );\r
883             }\r
884             else /* absolute paths are used. */\r
885                 p_state_current = osd_StateNew( p_this, &file[0], &state[0] );\r
886                 \r
887             if( !p_state_current || !p_state_current->p_pic )\r
888                 goto error;\r
889                 \r
890             if( p_state_prev )\r
891                 p_state_prev->p_next = p_state_current;\r
892             else\r
893                 p_current->p_states = p_state_current;                                \r
894             p_state_current->p_prev = p_state_prev;\r
895             \r
896             msg_Dbg( p_this, " |- state=%s, file=%s%s", &state[0], (*p_menu)->psz_path, &file[0] );\r
897         }\r
898         p_current->p_current_state = p_current->p_states;\r
899     }\r
900 \r
901     /* Find the last button and store its pointer. \r
902      * The OSD menu behaves like a roundrobin list.\r
903      */        \r
904     p_current = (*p_menu)->p_button;\r
905     while( p_current && p_current->p_next )\r
906     {\r
907         osd_button_t *p_temp = NULL;\r
908         p_temp = p_current->p_next;\r
909         p_current = p_temp;\r
910     }\r
911     (*p_menu)->p_last_button = p_current;\r
912     fclose( fd );\r
913     return 0;\r
914     \r
915 #undef MAX_FILE_PATH \r
916 error:\r
917     msg_Err( p_this, "parsing file failed (returned %d)", result );\r
918     fclose( fd );\r
919     return 1;        \r
920 }\r
921 \r
922 /*****************************************************************************\r
923  * osd_ConfigUnload: Load and parse osd text configurationfile\r
924  *****************************************************************************/\r
925 void osd_ConfigUnload( vlc_object_t *p_this, osd_menu_t **p_osd)\r
926 {\r
927     msg_Dbg( p_this, "unloading OSD menu structure" );\r
928     osd_MenuFree( p_this, *p_osd );\r
929 }\r