+/*****************************************************************************\r
+ * osd_parser.c - The OSD Menu parser core code.\r
+ *****************************************************************************\r
+ * Copyright (C) 2005 M2X\r
+ * $Id: osd_parser.c 9451 2004-12-01 01:07:08Z jpsaman $\r
+ *\r
+ * Authors: Jean-Paul Saman <jpsaman #_at_# m2x dot nl>\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.\r
+ *****************************************************************************/\r
+\r
+/*****************************************************************************\r
+ * Preamble\r
+ *****************************************************************************/\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#include <vlc/vlc.h>\r
+#include <vlc_config.h>\r
+//#include <vlc_es.h>\r
+#include <vlc_video.h>\r
+\r
+#include <vlc_keys.h>\r
+#include <vlc_image.h>\r
+#include <vlc_osd.h>\r
+\r
+\r
+#undef OSD_MENU_DEBUG\r
+\r
+/*****************************************************************************\r
+ * Local prototypes\r
+ *****************************************************************************/\r
+static const char *ppsz_button_states[] = { "unselect", "select", "pressed" };\r
+\r
+/* OSD Menu structure support routines */\r
+static osd_menu_t *osd_MenuNew( osd_menu_t *, const char *, int, int );\r
+static osd_button_t *osd_ButtonNew( const char *, int, int );\r
+static osd_state_t *osd_StateNew( vlc_object_t *, const char *, const char * );\r
+\r
+static void osd_MenuFree ( vlc_object_t *, osd_menu_t * );\r
+static void osd_ButtonFree( vlc_object_t *, osd_button_t * );\r
+static void osd_StatesFree( vlc_object_t *, osd_state_t * );\r
+\r
+static picture_t *osd_LoadImage( vlc_object_t *, const char *);\r
+\r
+#if 0\r
+/*****************************************************************************\r
+ * osd_YuvaYuvp\r
+ *****************************************************************************/\r
+static picture_t *osd_YuvaYuvp( vlc_object_t *p_this, picture_t *p_picture )\r
+{\r
+ video_format_t *p_fmt = NULL;\r
+ int i = 0, j = 0, n = 0, p = 0;\r
+ int i_max_entries = 256;\r
+\r
+#ifdef RANDOM_DITHERING\r
+ int i_seed = 0xdeadbeef; /* random seed */\r
+#else\r
+ int *pi_delta = NULL;\r
+#endif\r
+ int i_pixels = p_picture->p[0].i_visible_lines\r
+ * p_picture->p[0].i_pitch;\r
+ int i_iterator = p_picture->p[0].i_visible_lines * 3 / 4\r
+ * p_picture->p[0].i_pitch\r
+ + p_picture->p[0].i_pitch * 1 / 3;\r
+ int i_tolerance = 0;\r
+\r
+ p_fmt = (video_format_t*) malloc( sizeof( video_format_t ) );\r
+ if( !p_fmt )\r
+ {\r
+ msg_Err( p_this, "couldn't allocate video_format_t ... aborting YUVA to YUVP conversion of picture" );\r
+ return p_picture;\r
+ }\r
+ p_fmt->i_chroma = VLC_FOURCC('Y','U','V','P');\r
+ p_fmt->p_palette = (video_palette_t *) malloc( sizeof( video_palette_t ) );\r
+ if( !p_fmt->p_palette )\r
+ {\r
+ msg_Err( p_this, "couldn't allocate video_palette_t ... aborting YUVA to YUVP conversion of picture" );\r
+ free( p_fmt );\r
+ return p_picture; \r
+ } \r
+ p_fmt->p_palette->i_entries = 0;\r
+\r
+ /* Find best iterator using Euclide’s algorithm */\r
+ for( ; i_iterator > 1 ; i_iterator-- )\r
+ {\r
+ int a = i_pixels;\r
+ int b = i_iterator;\r
+ int c;\r
+\r
+ while( b )\r
+ {\r
+ c = a % b;\r
+ a = b;\r
+ b = c;\r
+ }\r
+\r
+ if( a == 1 )\r
+ {\r
+ break;\r
+ }\r
+ }\r
+\r
+ /* Count colors, build best palette */\r
+ for( i_tolerance = 0; i_tolerance < 128; i_tolerance++ )\r
+ {\r
+ vlc_bool_t b_success = VLC_TRUE;\r
+ p_fmt->p_palette->i_entries = 0;\r
+\r
+ for( i = 0; i < i_pixels ; )\r
+ {\r
+ uint8_t y = 0, u = 0, v = 0, a = 0;\r
+ y = p_picture->p[0].p_pixels[i];\r
+ u = p_picture->p[1].p_pixels[i];\r
+ v = p_picture->p[2].p_pixels[i];\r
+ a = p_picture->p[3].p_pixels[i];\r
+ for( j = 0; j < p_fmt->p_palette->i_entries; j++ )\r
+ {\r
+ if( abs((int)p_fmt->p_palette->palette[j][0] - (int)y) <= i_tolerance &&\r
+ abs((int)p_fmt->p_palette->palette[j][1] - (int)u) <= i_tolerance &&\r
+ abs((int)p_fmt->p_palette->palette[j][2] - (int)v) <= i_tolerance &&\r
+ abs((int)p_fmt->p_palette->palette[j][3] - (int)a) <= i_tolerance / 2 )\r
+ {\r
+ break;\r
+ }\r
+ }\r
+ if( j == p_fmt->p_palette->i_entries )\r
+ {\r
+ p_fmt->p_palette->palette[j][0] = y;\r
+ p_fmt->p_palette->palette[j][1] = u;\r
+ p_fmt->p_palette->palette[j][2] = v;\r
+ p_fmt->p_palette->palette[j][3] = a;\r
+ p_fmt->p_palette->i_entries++;\r
+ }\r
+ if( p_fmt->p_palette->i_entries >= i_max_entries )\r
+ {\r
+ b_success = VLC_FALSE;\r
+ break;\r
+ }\r
+ i += i_iterator;\r
+ if( i > i_pixels )\r
+ {\r
+ i -= i_pixels;\r
+ }\r
+ }\r
+\r
+ if( b_success )\r
+ {\r
+ break;\r
+ }\r
+ }\r
+\r
+#if OSD_MENU_DEBUG\r
+ msg_Dbg( p_this, "best palette has %d colors", p_fmt->p_palette->i_entries );\r
+#endif\r
+\r
+#ifndef RANDOM_DITHERING\r
+ pi_delta = malloc( ( p_picture->p[0].i_pitch + 1 ) * sizeof(int) * 4 );\r
+ if( !pi_delta )\r
+ {\r
+ msg_Err( p_this, "couldn't allocate video_palette_t ... aborting YUVA to YUVP conversion of picture" );\r
+ free( p_fmt->p_palette );\r
+ free( p_fmt );\r
+ return p_picture; \r
+ } \r
+ \r
+ for( i = 0; i < (p_picture->p[0].i_pitch + 1) * 4 ; i++ )\r
+ {\r
+ pi_delta[ i ] = 0;\r
+ }\r
+#endif\r
+\r
+ /* Fill image with our new colours */\r
+ for( p = 0; p < p_picture->p[0].i_visible_lines ; p++ )\r
+ {\r
+ int i_ydelta = 0, i_udelta = 0, i_vdelta = 0, i_adelta = 0;\r
+\r
+ for( n = 0; n < p_picture->p[0].i_pitch ; n++ )\r
+ {\r
+ int i_offset = p * p_picture->p[0].i_pitch + n;\r
+ int y, u, v, a;\r
+ int i_mindist, i_best;\r
+\r
+ y = (int)p_picture->p[0].p_pixels[i_offset];\r
+ u = (int)p_picture->p[1].p_pixels[i_offset];\r
+ v = (int)p_picture->p[2].p_pixels[i_offset];\r
+ a = (int)p_picture->p[3].p_pixels[i_offset];\r
+\r
+ /* Add dithering compensation */\r
+#ifdef RANDOM_DITHERING\r
+ y += ((i_seed & 0xff) - 0x80) * i_tolerance / 0x80;\r
+ u += (((i_seed >> 8) & 0xff) - 0x80) * i_tolerance / 0x80;\r
+ v += (((i_seed >> 16) & 0xff) - 0x80) * i_tolerance / 0x80;\r
+ a += (((i_seed >> 24) & 0xff) - 0x80) * i_tolerance / 0x80;\r
+#else\r
+ y += i_ydelta + pi_delta[ n * 4 ];\r
+ u += i_udelta + pi_delta[ n * 4 + 1 ];\r
+ v += i_vdelta + pi_delta[ n * 4 + 2 ];\r
+ a += i_adelta + pi_delta[ n * 4 + 3 ];\r
+#endif\r
+\r
+ /* Find best colour in palette */\r
+ for( i_mindist = 99999999, i_best = 0, j = 0; j < p_fmt->p_palette->i_entries; j++ )\r
+ {\r
+ int i_dist = 0;\r
+\r
+ i_dist += abs((int)p_fmt->p_palette->palette[j][0] - y);\r
+ i_dist += abs((int)p_fmt->p_palette->palette[j][1] - u);\r
+ i_dist += abs((int)p_fmt->p_palette->palette[j][2] - v);\r
+ i_dist += 2 * abs((int)p_fmt->p_palette->palette[j][3] - a);\r
+\r
+ if( i_dist < i_mindist )\r
+ {\r
+ i_mindist = i_dist;\r
+ i_best = j;\r
+ }\r
+ }\r
+\r
+ /* Set pixel to best color */\r
+ p_picture->p[0].p_pixels[i_offset] = i_best;\r
+\r
+ /* Update dithering state */\r
+#ifdef RANDOM_DITHERING\r
+ i_seed = (i_seed * 0x1283837) ^ 0x789479 ^ (i_seed >> 13);\r
+#else\r
+ i_ydelta = y - (int)p_fmt->p_palette->palette[i_best][0];\r
+ i_udelta = u - (int)p_fmt->p_palette->palette[i_best][1];\r
+ i_vdelta = v - (int)p_fmt->p_palette->palette[i_best][2];\r
+ i_adelta = a - (int)p_fmt->p_palette->palette[i_best][3];\r
+ pi_delta[ n * 4 ] = i_ydelta * 3 / 8;\r
+ pi_delta[ n * 4 + 1 ] = i_udelta * 3 / 8;\r
+ pi_delta[ n * 4 + 2 ] = i_vdelta * 3 / 8;\r
+ pi_delta[ n * 4 + 3 ] = i_adelta * 3 / 8;\r
+ i_ydelta = i_ydelta * 5 / 8;\r
+ i_udelta = i_udelta * 5 / 8;\r
+ i_vdelta = i_vdelta * 5 / 8;\r
+ i_adelta = i_adelta * 5 / 8;\r
+#endif\r
+ }\r
+ }\r
+\r
+#ifndef RANDOM_DITHERING\r
+ free( pi_delta );\r
+#endif\r
+\r
+ /* pad palette */\r
+ for( i = p_fmt->p_palette->i_entries; i < i_max_entries; i++ )\r
+ {\r
+ p_fmt->p_palette->palette[i][0] = 0;\r
+ p_fmt->p_palette->palette[i][1] = 0;\r
+ p_fmt->p_palette->palette[i][2] = 0;\r
+ p_fmt->p_palette->palette[i][3] = 0;\r
+ }\r
+ p_fmt->p_palette->i_entries = i_max_entries;\r
+#if OSD_MENU_DEBUG\r
+ msg_Dbg( p_this, "best palette has %d colors", p_fmt->p_palette->i_entries );\r
+#endif\r
+\r
+ p_picture->format.i_chroma = VLC_FOURCC('Y','U','V','P');\r
+ if( p_picture->format.p_palette )\r
+ free( p_picture->format.p_palette );\r
+ p_picture->format.p_palette = p_fmt->p_palette;\r
+ free( p_fmt );\r
+ return p_picture;\r
+}\r
+#endif\r
+/*****************************************************************************\r
+ * osd_LoadImage: loads the logo image into memory\r
+ *****************************************************************************/\r
+static picture_t *osd_LoadImage( vlc_object_t *p_this, const char *psz_filename )\r
+{\r
+ picture_t *p_pic = NULL;\r
+ image_handler_t *p_image;\r
+ video_format_t fmt_in = {0}, fmt_out = {0};\r
+\r
+ fmt_out.i_chroma = VLC_FOURCC('Y','U','V','A');\r
+ p_image = image_HandlerCreate( p_this );\r
+ if( p_image )\r
+ {\r
+ p_pic = image_ReadUrl( p_image, psz_filename, &fmt_in, &fmt_out );\r
+ image_HandlerDelete( p_image );\r
+#if 0 \r
+ p_pic = osd_YuvaYuvp( p_this, p_pic );\r
+#endif\r
+ }\r
+ else msg_Err( p_this, "unable to handle this chroma" );\r
+\r
+ return p_pic;\r
+}\r
+\r
+/*****************************************************************************\r
+ * Create a new Menu structure\r
+ *****************************************************************************/\r
+static osd_menu_t *osd_MenuNew( osd_menu_t *p_menu, const char *psz_path, int i_x, int i_y )\r
+{\r
+ if( !p_menu ) return NULL;\r
+ \r
+ p_menu->p_state = (osd_menu_state_t *) malloc( sizeof( osd_menu_state_t ) );\r
+ if( !p_menu->p_state )\r
+ msg_Err( p_menu, "memory allocation for OSD Menu state failed." );\r
+\r
+ if( psz_path != NULL )\r
+ p_menu->psz_path = strdup( psz_path );\r
+ else\r
+ p_menu->psz_path = NULL;\r
+ p_menu->i_x = i_x;\r
+ p_menu->i_y = i_y;\r
+ \r
+ return p_menu; \r
+}\r
+\r
+/*****************************************************************************\r
+ * Free the menu\r
+ *****************************************************************************/\r
+static void osd_MenuFree( vlc_object_t *p_this, osd_menu_t *p_menu )\r
+{\r
+ msg_Dbg( p_this, "freeing menu" );\r
+ osd_ButtonFree( p_this, p_menu->p_button );\r
+ p_menu->p_button = NULL;\r
+ p_menu->p_last_button = NULL;\r
+ if( p_menu->psz_path ) free( p_menu->psz_path );\r
+ p_menu->psz_path = NULL;\r
+ if( p_menu->p_state ) free( p_menu->p_state );\r
+ p_menu->p_state = NULL;\r
+}\r
+\r
+/*****************************************************************************\r
+ * Create a new button\r
+ *****************************************************************************/\r
+static osd_button_t *osd_ButtonNew( const char *psz_action, int i_x, int i_y )\r
+{\r
+ osd_button_t *p_button = NULL;\r
+ p_button = (osd_button_t*) malloc( sizeof(osd_button_t) );\r
+ if( !p_button )\r
+ return NULL;\r
+ \r
+ memset( p_button, 0, sizeof(osd_button_t) );\r
+ p_button->psz_action = strdup(psz_action);\r
+ p_button->psz_action_down = NULL;\r
+ p_button->p_feedback = NULL;\r
+ p_button->i_x = i_x;\r
+ p_button->i_y = i_y;\r
+ \r
+ return p_button;\r
+}\r
+\r
+/*****************************************************************************\r
+ * Free a button\r
+ *****************************************************************************/ \r
+static void osd_ButtonFree( vlc_object_t *p_this, osd_button_t *p_button )\r
+{\r
+ osd_button_t *p_current = p_button;\r
+ osd_button_t *p_next = NULL;\r
+ osd_button_t *p_prev = NULL;\r
+ \r
+ /* First walk to the end. */\r
+ while( p_current->p_next )\r
+ {\r
+ p_next = p_current->p_next;\r
+ p_current = p_next; \r
+ }\r
+ /* Then free end first and walk to the start. */\r
+ while( p_current->p_prev )\r
+ {\r
+ msg_Dbg( p_this, "+ freeing button %s [%p]", p_current->psz_action, p_current );\r
+ p_prev = p_current->p_prev;\r
+ p_current = p_prev;\r
+ if( p_current->p_next )\r
+ {\r
+ if( p_current->p_next->psz_name ) \r
+ free( p_current->p_next->psz_name );\r
+ if( p_current->p_next->psz_action )\r
+ free( p_current->p_next->psz_action );\r
+ if( p_current->p_next->psz_action_down )\r
+ free( p_current->p_next->psz_action_down );\r
+ if( p_current->p_feedback && p_current->p_feedback->p_data_orig )\r
+ free( p_current->p_feedback->p_data_orig );\r
+ if( p_current->p_feedback )\r
+ free( p_current->p_feedback );\r
+ \r
+ p_current->p_next->psz_action_down = NULL;\r
+ p_current->p_next->psz_action = NULL;\r
+ p_current->p_next->psz_name = NULL;\r
+ p_current->p_feedback = NULL;\r
+ \r
+ /* Free all states first */ \r
+ if( p_current->p_next->p_states ) \r
+ osd_StatesFree( p_this, p_current->p_next->p_states );\r
+ p_current->p_next->p_states = NULL; \r
+ if( p_current->p_next) free( p_current->p_next );\r
+ p_current->p_next = NULL; \r
+ } \r
+ \r
+ if( p_current->p_up )\r
+ {\r
+ if( p_current->p_up->psz_name ) \r
+ free( p_current->p_up->psz_name );\r
+ if( p_current->p_up->psz_action )\r
+ free( p_current->p_up->psz_action );\r
+ if( p_current->p_up->psz_action_down )\r
+ free( p_current->p_up->psz_action_down );\r
+ if( p_current->p_feedback && p_current->p_feedback->p_data_orig )\r
+ free( p_current->p_feedback->p_data_orig );\r
+ if( p_current->p_feedback )\r
+ free( p_current->p_feedback );\r
+ \r
+ p_current->p_up->psz_action_down = NULL;\r
+ p_current->p_up->psz_action = NULL;\r
+ p_current->p_up->psz_name = NULL;\r
+ p_current->p_feedback = NULL;\r
+ \r
+ /* Free all states first */ \r
+ if( p_current->p_up->p_states ) \r
+ osd_StatesFree( p_this, p_current->p_up->p_states );\r
+ p_current->p_up->p_states = NULL; \r
+ if( p_current->p_up ) free( p_current->p_up );\r
+ p_current->p_up = NULL; \r
+ }\r
+ } \r
+ /* Free the last one. */\r
+ if( p_button ) \r
+ {\r
+ msg_Dbg( p_this, "+ freeing button %s [%p]", p_button->psz_action, p_button ); \r
+ if( p_button->psz_name ) free( p_button->psz_name );\r
+ if( p_button->psz_action ) free( p_button->psz_action );\r
+ if( p_button->psz_action_down ) free( p_button->psz_action_down ); \r
+ if( p_current->p_feedback && p_current->p_feedback->p_data_orig )\r
+ free( p_current->p_feedback->p_data_orig );\r
+ if( p_current->p_feedback )\r
+ free( p_current->p_feedback );\r
+ \r
+ p_button->psz_name = NULL;\r
+ p_button->psz_action = NULL;\r
+ p_button->psz_action_down = NULL;\r
+ p_current->p_feedback = NULL;\r
+ \r
+ if( p_button->p_states )\r
+ osd_StatesFree( p_this, p_button->p_states );\r
+ p_button->p_states = NULL; \r
+ free( p_button );\r
+ p_button = NULL;\r
+ }\r
+}\r
+\r
+/*****************************************************************************\r
+ * Create a new state image\r
+ *****************************************************************************/\r
+static osd_state_t *osd_StateNew( vlc_object_t *p_this, const char *psz_file, const char *psz_state )\r
+{\r
+ osd_state_t *p_state = NULL;\r
+ p_state = (osd_state_t*) malloc( sizeof(osd_state_t) );\r
+ if( !p_state )\r
+ return NULL;\r
+ \r
+ memset( p_state, 0, sizeof(osd_state_t) ); \r
+ p_state->p_pic = osd_LoadImage( p_this, psz_file );\r
+\r
+ if( psz_state )\r
+ {\r
+ p_state->psz_state = strdup( psz_state );\r
+ if( strncmp( ppsz_button_states[0], psz_state, strlen(ppsz_button_states[0]) ) == 0 )\r
+ p_state->i_state = OSD_BUTTON_UNSELECT;\r
+ else if( strncmp( ppsz_button_states[1], psz_state, strlen(ppsz_button_states[1]) ) == 0 )\r
+ p_state->i_state = OSD_BUTTON_SELECT;\r
+ else if( strncmp( ppsz_button_states[2], psz_state, strlen(ppsz_button_states[2]) ) == 0 )\r
+ p_state->i_state = OSD_BUTTON_PRESSED;\r
+ }\r
+ return p_state;\r
+}\r
+\r
+/*****************************************************************************\r
+ * Free state images\r
+ *****************************************************************************/\r
+static void osd_StatesFree( vlc_object_t *p_this, osd_state_t *p_states )\r
+{\r
+ osd_state_t *p_state = p_states;\r
+ osd_state_t *p_next = NULL;\r
+ osd_state_t *p_prev = NULL;\r
+ \r
+ while( p_state->p_next )\r
+ {\r
+ p_next = p_state->p_next;\r
+ p_state = p_next;\r
+ }\r
+ /* Then free end first and walk to the start. */\r
+ while( p_state->p_prev )\r
+ {\r
+ msg_Dbg( p_this, " |- freeing state %s [%p]", p_state->psz_state, p_state );\r
+ p_prev = p_state->p_prev;\r
+ p_state = p_prev;\r
+ if( p_state->p_next )\r
+ {\r
+ if( p_state->p_next->p_pic && p_state->p_next->p_pic->p_data_orig )\r
+ free( p_state->p_next->p_pic->p_data_orig );\r
+ if( p_state->p_next->p_pic ) free( p_state->p_next->p_pic );\r
+ p_state->p_next->p_pic = NULL; \r
+ if( p_state->p_next->psz_state ) free( p_state->p_next->psz_state );\r
+ p_state->p_next->psz_state = NULL;\r
+ free( p_state->p_next );\r
+ p_state->p_next = NULL; \r
+ }\r
+ }\r
+ /* Free the last one. */\r
+ if( p_states )\r
+ {\r
+ msg_Dbg( p_this, " |- freeing state %s [%p]", p_state->psz_state, p_states );\r
+ if( p_states->p_pic && p_states->p_pic->p_data_orig )\r
+ free( p_states->p_pic->p_data_orig );\r
+ if( p_states->p_pic ) free( p_states->p_pic );\r
+ p_states->p_pic = NULL;\r
+ if( p_state->psz_state ) free( p_state->psz_state );\r
+ p_state->psz_state = NULL;\r
+ free( p_states );\r
+ p_states = NULL;\r
+ }\r
+}\r
+\r
+/*****************************************************************************\r
+ * osd_ConfigLoader: Load and parse osd text configurationfile\r
+ *****************************************************************************/\r
+int osd_ConfigLoader( vlc_object_t *p_this, const char *psz_file,\r
+ osd_menu_t **p_menu )\r
+{\r
+ osd_button_t *p_current = NULL; /* button currently processed */\r
+ osd_button_t *p_prev = NULL; /* previous processed button */ \r
+\r
+#define MAX_FILE_PATH 256 \r
+ FILE *fd = NULL;\r
+ int result = 0;\r
+ \r
+ msg_Dbg( p_this, "opening osd definition file %s", psz_file );\r
+ fd = fopen( psz_file, "r" );\r
+ if( !fd ) \r
+ {\r
+ msg_Err( p_this, "failed opening osd definition file %s", psz_file );\r
+ return VLC_EGENERIC;\r
+ }\r
+ \r
+ /* Read first line */ \r
+ if( !feof( fd ) )\r
+ {\r
+ char action[25] = "";\r
+ char path[MAX_FILE_PATH] = "";\r
+ char *psz_path = NULL;\r
+ size_t i_len = 0;\r
+\r
+ /* override images path ? */\r
+ psz_path = config_GetPsz( p_this, "osdmenu-file-path" );\r
+ if( psz_path == NULL )\r
+ { \r
+ result = fscanf(fd, "%24s %255s", &action[0], &path[0] );\r
+ }\r
+ else\r
+ {\r
+ /* psz_path is not null and therefor &path[0] cannot be NULL \r
+ * it might be null terminated.\r
+ */\r
+ strncpy( &path[0], psz_path, MAX_FILE_PATH );\r
+ free( psz_path );\r
+ psz_path = NULL;\r
+ }\r
+ /* NULL terminate before asking the length of path[] */\r
+ path[MAX_FILE_PATH-1] = '\0';\r
+ i_len = strlen(&path[0]);\r
+ if( i_len == MAX_FILE_PATH )\r
+ i_len--; /* truncate to prevent buffer overflow */\r
+#if defined(WIN32) || defined(UNDER_CE)\r
+ if( (i_len > 0) && path[i_len] != '\\' )\r
+ path[i_len] = '\\';\r
+#else \r
+ if( (i_len > 0) && path[i_len] != '/' )\r
+ path[i_len] = '/';\r
+#endif\r
+ path[i_len+1] = '\0';\r
+ if( result == 0 || result == EOF )\r
+ goto error; \r
+ msg_Dbg( p_this, "%s=%s", &action[0], &path[0] );\r
+ \r
+ if( i_len == 0 )\r
+ *p_menu = osd_MenuNew( *p_menu, NULL, 0, 0 );\r
+ else\r
+ *p_menu = osd_MenuNew( *p_menu, &path[0], 0, 0 );\r
+ }\r
+ \r
+ if( !*p_menu )\r
+ goto error;\r
+ \r
+ /* read successive lines */\r
+ while( !feof( fd ) )\r
+ { \r
+ osd_state_t *p_state_current = NULL; /* button state currently processed */\r
+ osd_state_t *p_state_prev = NULL; /* previous state processed button */ \r
+ \r
+ char cmd[25] = ""; \r
+ char action[25] = "";\r
+ char state[25] = "";\r
+ char file[256] = "";\r
+ char path[512] = ""; \r
+ int i_x = 0;\r
+ int i_y = 0;\r
+\r
+ result = fscanf( fd, "%24s %24s (%d,%d)", &cmd[0], &action[0], &i_x, &i_y );\r
+ if( result == 0 )\r
+ goto error; \r
+ if( strncmp( &cmd[0], "action", 6 ) != 0 )\r
+ break;\r
+ msg_Dbg( p_this, " + %s hotkey=%s (%d,%d)", &cmd[0], &action[0], i_x, i_y ); \r
+ \r
+ p_prev = p_current;\r
+ p_current = osd_ButtonNew( &action[0], i_x, i_y ); \r
+ if( !p_current )\r
+ goto error;\r
+ \r
+ if( p_prev )\r
+ p_prev->p_next = p_current; \r
+ else\r
+ (*p_menu)->p_button = p_current; \r
+ p_current->p_prev = p_prev;\r
+ \r
+ /* parse all states */\r
+ while( !feof( fd ) )\r
+ {\r
+ char type[25] = "";\r
+ \r
+ result = fscanf( fd, "\t%24s", &state[0] ); \r
+ if( result == 0 )\r
+ goto error;\r
+ \r
+ /* FIXME: We only parse one level deep now */ \r
+ if( strncmp( &state[0], "action", 6 ) == 0 )\r
+ {\r
+ osd_button_t *p_up = NULL;\r
+ \r
+ result = fscanf( fd, "%24s (%d,%d)", &action[0], &i_x, &i_y );\r
+ if( result == 0 )\r
+ goto error;\r
+ /* create new button */ \r
+ p_up = osd_ButtonNew( &action[0], i_x, i_y ); \r
+ if( !p_up ) \r
+ goto error;\r
+ /* Link to list */ \r
+ p_up->p_down = p_current;\r
+ p_current->p_up = p_up; \r
+ msg_Dbg( p_this, " + (menu up) hotkey=%s (%d,%d)", &action[0], i_x, i_y );\r
+ /* Parse type state */\r
+ result = fscanf( fd, "\t%24s %24s", &cmd[0], &type[0] ); \r
+ if( result == 0 )\r
+ goto error;\r
+ if( strncmp( &cmd[0], "type", 4 ) == 0 )\r
+ { \r
+ if( strncmp( &type[0], "volume", 6 ) == 0 )\r
+ {\r
+ (*p_menu)->p_state->p_volume = p_up;\r
+ msg_Dbg( p_this, " + type=%s", &type[0] );\r
+ }\r
+ }\r
+ /* Parse range state */\r
+ result = fscanf( fd, "\t%24s", &state[0] ); \r
+ if( result == 0 )\r
+ goto error;\r
+ /* Parse the range state */ \r
+ if( strncmp( &state[0], "range", 5 ) == 0 )\r
+ {\r
+ osd_state_t *p_range_current = NULL; /* range state currently processed */\r
+ osd_state_t *p_range_prev = NULL; /* previous state processed range */ \r
+ int i_index = 0;\r
+ \r
+ p_up->b_range = VLC_TRUE;\r
+ \r
+ result = fscanf( fd, "\t%24s", &action[0] ); \r
+ if( result == 0 )\r
+ goto error;\r
+ \r
+ result = fscanf( fd, "\t%d", &i_index ); \r
+ if( result == 0 )\r
+ goto error;\r
+ \r
+ msg_Dbg( p_this, " + (menu up) hotkey down %s, file=%s%s", &action[0], (*p_menu)->psz_path, &file[0] );\r
+ \r
+ if( p_up->psz_action_down ) free( p_up->psz_action_down );\r
+ p_up->psz_action_down = strdup( &action[0] ); \r
+ \r
+ /* Parse range contstruction :\r
+ * range <hotkey> \r
+ * <state1> <file1>\r
+ *\r
+ * <stateN> <fileN>\r
+ * end \r
+ */ \r
+ while( !feof( fd ) )\r
+ {\r
+ result = fscanf( fd, "\t%255s", &file[0] ); \r
+ if( result == 0 )\r
+ goto error;\r
+ if( strncmp( &file[0], "end", 3 ) == 0 )\r
+ break;\r
+ \r
+ p_range_prev = p_range_current;\r
+ \r
+ if( (*p_menu)->psz_path )\r
+ {\r
+ size_t i_path_size = strlen( (*p_menu)->psz_path );\r
+ size_t i_file_size = strlen( &file[0] );\r
+ \r
+ strncpy( &path[0], (*p_menu)->psz_path, i_path_size );\r
+ strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );\r
+ path[ i_path_size + i_file_size ] = '\0';\r
+ \r
+ p_range_current = osd_StateNew( p_this, &path[0], "pressed" );\r
+ }\r
+ else /* absolute paths are used. */\r
+ p_range_current = osd_StateNew( p_this, &file[0], "pressed" );\r
+ \r
+ if( !p_range_current || !p_range_current->p_pic )\r
+ goto error;\r
+ \r
+ /* increment the number of ranges for this button */ \r
+ p_up->i_ranges++;\r
+ \r
+ if( p_range_prev )\r
+ p_range_prev->p_next = p_range_current;\r
+ else\r
+ p_up->p_states = p_range_current; \r
+ p_range_current->p_prev = p_range_prev;\r
+ \r
+ msg_Dbg( p_this, " |- range=%d, file=%s%s", \r
+ p_up->i_ranges, \r
+ (*p_menu)->psz_path, &file[0] ); \r
+ }\r
+ if( i_index > 0 )\r
+ { \r
+ osd_state_t *p_range = NULL;\r
+ \r
+ /* Find the default index for state range */ \r
+ p_range = p_up->p_states;\r
+ while( (--i_index > 0) && p_range->p_next )\r
+ {\r
+ osd_state_t *p_temp = NULL;\r
+ p_temp = p_range->p_next;\r
+ p_range = p_temp;\r
+ }\r
+ p_up->p_current_state = p_range;\r
+ } \r
+ else p_up->p_current_state = p_up->p_states;\r
+ \r
+ } \r
+ result = fscanf( fd, "\t%24s", &state[0] ); \r
+ if( result == 0 )\r
+ goto error; \r
+ if( strncmp( &state[0], "end", 3 ) != 0 )\r
+ goto error;\r
+ \r
+ /* Continue at the beginning of the while() */\r
+ continue;\r
+ }\r
+ \r
+ /* Parse the range state */ \r
+ if( strncmp( &state[0], "range", 5 ) == 0 )\r
+ {\r
+ osd_state_t *p_range_current = NULL; /* range state currently processed */\r
+ osd_state_t *p_range_prev = NULL; /* previous state processed range */ \r
+ int i_index = 0;\r
+ \r
+ p_current->b_range = VLC_TRUE;\r
+\r
+ result = fscanf( fd, "\t%24s", &action[0] ); \r
+ if( result == 0 )\r
+ goto error;\r
+ \r
+ result = fscanf( fd, "\t%d", &i_index ); \r
+ if( result == 0 )\r
+ goto error;\r
+ \r
+ msg_Dbg( p_this, " + hotkey down %s, file=%s%s", &action[0], (*p_menu)->psz_path, &file[0] ); \r
+ if( p_current->psz_action_down ) free( p_current->psz_action_down );\r
+ p_current->psz_action_down = strdup( &action[0] ); \r
+ \r
+ /* Parse range contstruction :\r
+ * range <hotkey> \r
+ * <state1> <file1>\r
+ *\r
+ * <stateN> <fileN>\r
+ * end \r
+ */ \r
+ while( !feof( fd ) )\r
+ {\r
+ result = fscanf( fd, "\t%255s", &file[0] ); \r
+ if( result == 0 )\r
+ goto error;\r
+ if( strncmp( &file[0], "end", 3 ) == 0 )\r
+ break;\r
+ \r
+ p_range_prev = p_range_current;\r
+ \r
+ if( (*p_menu)->psz_path )\r
+ {\r
+ size_t i_path_size = strlen( (*p_menu)->psz_path );\r
+ size_t i_file_size = strlen( &file[0] );\r
+ \r
+ strncpy( &path[0], (*p_menu)->psz_path, i_path_size );\r
+ strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );\r
+ path[ i_path_size + i_file_size ] = '\0';\r
+ \r
+ p_range_current = osd_StateNew( p_this, &path[0], "pressed" );\r
+ }\r
+ else /* absolute paths are used. */\r
+ p_range_current = osd_StateNew( p_this, &file[0], "pressed" );\r
+ \r
+ if( !p_range_current || !p_range_current->p_pic )\r
+ goto error;\r
+ \r
+ /* increment the number of ranges for this button */ \r
+ p_current->i_ranges++;\r
+ \r
+ if( p_range_prev )\r
+ p_range_prev->p_next = p_range_current;\r
+ else\r
+ p_current->p_states = p_range_current; \r
+ p_range_current->p_prev = p_range_prev;\r
+ \r
+ msg_Dbg( p_this, " |- range=%d, file=%s%s", \r
+ p_current->i_ranges, \r
+ (*p_menu)->psz_path, &file[0] ); \r
+ }\r
+ if( i_index > 0 )\r
+ { \r
+ osd_state_t *p_range = NULL;\r
+ \r
+ /* Find the default index for state range */ \r
+ p_range = p_current->p_states;\r
+ while( (--i_index > 0) && p_range->p_next )\r
+ {\r
+ osd_state_t *p_temp = NULL;\r
+ p_temp = p_range->p_next;\r
+ p_range = p_temp;\r
+ }\r
+ p_current->p_current_state = p_range;\r
+ } \r
+ else p_current->p_current_state = p_current->p_states;\r
+ /* Continue at the beginning of the while() */\r
+ continue;\r
+ } \r
+ if( strncmp( &state[0], "end", 3 ) == 0 )\r
+ break;\r
+ \r
+ result = fscanf( fd, "\t%255s", &file[0] ); \r
+ if( result == 0 )\r
+ goto error; \r
+ \r
+ p_state_prev = p_state_current;\r
+\r
+ if( ( strncmp( ppsz_button_states[0], &state[0], strlen(ppsz_button_states[0]) ) != 0 ) &&\r
+ ( strncmp( ppsz_button_states[1], &state[0], strlen(ppsz_button_states[1]) ) != 0 ) &&\r
+ ( strncmp( ppsz_button_states[2], &state[0], strlen(ppsz_button_states[2]) ) != 0 ) )\r
+ {\r
+ msg_Err( p_this, "invalid button state %s for button %s expected %d: unselect, select or pressed)",\r
+ &state[0], &action[0], strlen(&state[0]));\r
+ goto error;\r
+ }\r
+ \r
+ if( (*p_menu)->psz_path )\r
+ {\r
+ size_t i_path_size = strlen( (*p_menu)->psz_path );\r
+ size_t i_file_size = strlen( &file[0] );\r
+ \r
+ strncpy( &path[0], (*p_menu)->psz_path, i_path_size );\r
+ strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );\r
+ path[ i_path_size + i_file_size ] = '\0';\r
+ \r
+ p_state_current = osd_StateNew( p_this, &path[0], &state[0] );\r
+ }\r
+ else /* absolute paths are used. */\r
+ p_state_current = osd_StateNew( p_this, &file[0], &state[0] );\r
+ \r
+ if( !p_state_current || !p_state_current->p_pic )\r
+ goto error;\r
+ \r
+ if( p_state_prev )\r
+ p_state_prev->p_next = p_state_current;\r
+ else\r
+ p_current->p_states = p_state_current; \r
+ p_state_current->p_prev = p_state_prev;\r
+ \r
+ msg_Dbg( p_this, " |- state=%s, file=%s%s", &state[0], (*p_menu)->psz_path, &file[0] );\r
+ }\r
+ p_current->p_current_state = p_current->p_states;\r
+ }\r
+\r
+ /* Find the last button and store its pointer. \r
+ * The OSD menu behaves like a roundrobin list.\r
+ */ \r
+ p_current = (*p_menu)->p_button;\r
+ while( p_current && p_current->p_next )\r
+ {\r
+ osd_button_t *p_temp = NULL;\r
+ p_temp = p_current->p_next;\r
+ p_current = p_temp;\r
+ }\r
+ (*p_menu)->p_last_button = p_current;\r
+ fclose( fd );\r
+ return 0;\r
+ \r
+#undef MAX_FILE_PATH \r
+error:\r
+ msg_Err( p_this, "parsing file failed (returned %d)", result );\r
+ fclose( fd );\r
+ return 1; \r
+}\r
+\r
+/*****************************************************************************\r
+ * osd_ConfigUnload: Load and parse osd text configurationfile\r
+ *****************************************************************************/\r
+void osd_ConfigUnload( vlc_object_t *p_this, osd_menu_t **p_osd)\r
+{\r
+ msg_Dbg( p_this, "unloading OSD menu structure" );\r
+ osd_MenuFree( p_this, *p_osd );\r
+}\r