1 /*****************************************************************************
2 * interface.c: interface access for other threads
3 * This library provides basic functions for threads to interact with user
4 * interface, such as command line.
5 *****************************************************************************
6 * Copyright (C) 1998, 1999, 2000 VideoLAN
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public
21 * License along with this program; if not, write to the
22 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 * Boston, MA 02111-1307, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
29 #include <errno.h> /* ENOMEM */
30 #include <stdio.h> /* sprintf() */
31 #include <stdlib.h> /* free(), strtol() */
32 #include <string.h> /* strerror() */
33 #include <sys/types.h> /* on BSD, uio.h needs types.h */
34 #include <sys/uio.h> /* for input.h */
36 #include <dlfcn.h> /* plugins */
45 #include "interface.h"
47 #include "intf_console.h"
50 #include "video_output.h"
54 /*****************************************************************************
55 * intf_channel_t: channel description
56 *****************************************************************************
57 * A 'channel' is a descriptor of an input method. It is used to switch easily
58 * from source to source without having to specify the whole input thread
59 * configuration. The channels array, stored in the interface thread object, is
60 * loaded in intf_Create, and unloaded in intf_Destroy.
61 *****************************************************************************/
62 typedef struct intf_channel_s
64 /* Channel description */
65 int i_channel; /* channel number, -1 for end of array */
66 char * psz_description; /* channel description (owned) */
68 /* Input configuration */
69 int i_input_method; /* input method descriptor */
70 char * psz_input_source; /* source string (owned) */
71 int i_input_port; /* port */
72 int i_input_vlan; /* vlan */
75 /*****************************************************************************
77 *****************************************************************************/
78 static int LoadChannels ( intf_thread_t *p_intf, char *psz_filename );
79 static void UnloadChannels ( intf_thread_t *p_intf );
80 static int ParseChannel ( intf_channel_t *p_channel, char *psz_str );
82 /*****************************************************************************
83 * intf_Create: prepare interface before main loop
84 *****************************************************************************
85 * This function opens output devices and creates specific interfaces. It sends
86 * its own error messages.
87 *****************************************************************************/
88 intf_thread_t* intf_Create( void )
90 intf_thread_t *p_intf;
94 /* Allocate structure */
95 p_intf = malloc( sizeof( intf_thread_t ) );
98 intf_ErrMsg("error: %s\n", strerror( ENOMEM ) );
102 /* Initialize method-dependent functions */
103 psz_method = main_GetPszVariable( VOUT_METHOD_VAR, VOUT_DEFAULT_METHOD );
105 psz_plugin = malloc( sizeof("./interface/intf_.so") + strlen(psz_method) );
106 sprintf( psz_plugin, "./interface/intf_%s.so", psz_method );
108 p_intf->p_intf_plugin = dlopen( psz_plugin, RTLD_NOW | RTLD_GLOBAL );
110 if( p_intf->p_intf_plugin == NULL )
112 intf_ErrMsg( "error: could not open interface plugin %s\n", psz_plugin );
120 p_intf->p_sys_create = dlsym(p_intf->p_intf_plugin, "intf_SysCreate");
121 p_intf->p_sys_manage = dlsym(p_intf->p_intf_plugin, "intf_SysManage");
122 p_intf->p_sys_destroy = dlsym(p_intf->p_intf_plugin, "intf_SysDestroy");
124 /* Initialize structure */
126 p_intf->p_vout = NULL;
127 p_intf->p_input = NULL;
129 /* Load channels - the pointer will be set to NULL on failure. The
130 * return value is ignored since the program can work without
132 LoadChannels( p_intf, main_GetPszVariable( INTF_CHANNELS_VAR, INTF_CHANNELS_DEFAULT ));
134 /* Start interfaces */
135 p_intf->p_console = intf_ConsoleCreate();
136 if( p_intf->p_console == NULL )
138 intf_ErrMsg("error: can't create control console\n");
139 dlclose( p_intf->p_intf_plugin );
143 if( p_intf->p_sys_create( p_intf ) )
145 intf_ErrMsg("error: can't create interface\n");
146 intf_ConsoleDestroy( p_intf->p_console );
147 dlclose( p_intf->p_intf_plugin );
152 intf_Msg("Interface initialized\n");
156 /*****************************************************************************
158 *****************************************************************************
159 * Initialization script and main interface loop.
160 *****************************************************************************/
161 void intf_Run( intf_thread_t *p_intf )
163 /* Execute the initialization script - if a positive number is returned,
164 * the script could be executed but failed */
165 if( intf_ExecScript( main_GetPszVariable( INTF_INIT_SCRIPT_VAR, INTF_INIT_SCRIPT_DEFAULT ) ) > 0 )
167 intf_ErrMsg("warning: error(s) during startup script\n");
171 while(!p_intf->b_die)
173 /* Flush waiting messages */
176 /* Manage specific interface */
177 p_intf->p_sys_manage( p_intf );
179 /* Check attached threads status */
180 if( (p_intf->p_vout != NULL) && p_intf->p_vout->b_error )
182 /* FIXME: add aout error detection ?? */
185 if( (p_intf->p_input != NULL) && p_intf->p_input->b_error )
187 input_DestroyThread( p_intf->p_input, NULL );
188 p_intf->p_input = NULL;
189 intf_DbgMsg("Input thread destroyed\n");
192 /* Sleep to avoid using all CPU - since some interfaces needs to access
193 * keyboard events, a 100ms delay is a good compromise */
194 msleep( INTF_IDLE_SLEEP );
198 /*****************************************************************************
199 * intf_Destroy: clean interface after main loop
200 *****************************************************************************
201 * This function destroys specific interfaces and close output devices.
202 *****************************************************************************/
203 void intf_Destroy( intf_thread_t *p_intf )
205 /* Destroy interfaces */
206 p_intf->p_sys_destroy( p_intf );
207 intf_ConsoleDestroy( p_intf->p_console );
209 /* Unload channels */
210 UnloadChannels( p_intf );
213 dlclose( p_intf->p_intf_plugin );
219 /*****************************************************************************
220 * intf_SelectChannel: change channel
221 *****************************************************************************
222 * Kill existing input, if any, and try to open a new one, using an input
223 * configuration table.
224 *****************************************************************************/
225 int intf_SelectChannel( intf_thread_t * p_intf, int i_channel )
227 intf_channel_t * p_channel; /* channel */
229 /* Look for channel in array */
230 if( p_intf->p_channel != NULL )
232 for( p_channel = p_intf->p_channel; p_channel->i_channel != -1; p_channel++ )
234 if( p_channel->i_channel == i_channel )
240 /* Kill existing input, if any */
241 if( p_intf->p_input != NULL )
243 input_DestroyThread( p_intf->p_input, NULL );
246 intf_Msg("Channel %d: %s\n", i_channel, p_channel->psz_description );
248 /* Open a new input */
249 p_intf->p_input = input_CreateThread( p_channel->i_input_method, p_channel->psz_input_source,
250 p_channel->i_input_port, p_channel->i_input_vlan,
251 p_intf->p_vout, p_main->p_aout, NULL );
252 return( p_intf->p_input == NULL );
257 /* Channel does not exist */
258 intf_Msg("Channel %d does not exist\n", i_channel );
262 /*****************************************************************************
263 * intf_ProcessKey: process standard keys
264 *****************************************************************************
265 * This function will process standard keys and return non 0 if the key was
267 *****************************************************************************/
268 int intf_ProcessKey( intf_thread_t *p_intf, int i_key )
272 case 'Q': /* quit order */
274 case 27: /* escape key */
278 case '0': /* source change */
288 /* Change channel - return code is ignored since SelectChannel displays
289 * its own error messages */
290 intf_SelectChannel( p_intf, i_key - '0' );
292 case '+': /* volume + */
295 case '-': /* volume - */
298 case 'M': /* toggle mute */
302 case 'g': /* gamma - */
303 if( (p_intf->p_vout != NULL) && (p_intf->p_vout->f_gamma > -INTF_GAMMA_LIMIT) )
305 vlc_mutex_lock( &p_intf->p_vout->change_lock );
306 p_intf->p_vout->f_gamma -= INTF_GAMMA_STEP;
307 p_intf->p_vout->i_changes |= VOUT_GAMMA_CHANGE;
308 vlc_mutex_unlock( &p_intf->p_vout->change_lock );
311 case 'G': /* gamma + */
312 if( (p_intf->p_vout != NULL) && (p_intf->p_vout->f_gamma < INTF_GAMMA_LIMIT) )
314 vlc_mutex_lock( &p_intf->p_vout->change_lock );
315 p_intf->p_vout->f_gamma += INTF_GAMMA_STEP;
316 p_intf->p_vout->i_changes |= VOUT_GAMMA_CHANGE;
317 vlc_mutex_unlock( &p_intf->p_vout->change_lock );
320 case 'c': /* toggle grayscale */
321 if( p_intf->p_vout != NULL )
323 vlc_mutex_lock( &p_intf->p_vout->change_lock );
324 p_intf->p_vout->b_grayscale = !p_intf->p_vout->b_grayscale;
325 p_intf->p_vout->i_changes |= VOUT_GRAYSCALE_CHANGE;
326 vlc_mutex_unlock( &p_intf->p_vout->change_lock );
329 case ' ': /* toggle interface */
330 if( p_intf->p_vout != NULL )
332 vlc_mutex_lock( &p_intf->p_vout->change_lock );
333 p_intf->p_vout->b_interface = !p_intf->p_vout->b_interface;
334 p_intf->p_vout->i_changes |= VOUT_INTF_CHANGE;
335 vlc_mutex_unlock( &p_intf->p_vout->change_lock );
338 case 'i': /* toggle info */
339 if( p_intf->p_vout != NULL )
341 vlc_mutex_lock( &p_intf->p_vout->change_lock );
342 p_intf->p_vout->b_info = !p_intf->p_vout->b_info;
343 p_intf->p_vout->i_changes |= VOUT_INFO_CHANGE;
344 vlc_mutex_unlock( &p_intf->p_vout->change_lock );
347 case 's': /* toggle scaling */
348 if( p_intf->p_vout != NULL )
350 vlc_mutex_lock( &p_intf->p_vout->change_lock );
351 p_intf->p_vout->b_scale = !p_intf->p_vout->b_scale;
352 p_intf->p_vout->i_changes |= VOUT_SCALE_CHANGE;
353 vlc_mutex_unlock( &p_intf->p_vout->change_lock );
356 default: /* unknown key */
363 /* following functions are local */
365 /*****************************************************************************
366 * LoadChannels: load channels description from a file
367 *****************************************************************************
368 * This structe describes all interface-specific data of the main (interface)
370 * Each line of the file is a semicolon separated list of the following
372 * integer channel number
373 * string channel description
374 * integer input method (see input.h)
375 * string input source
378 * The last field must end with a semicolon.
379 * Comments and empty lines are not explicitely allowed, but lines with parsing
380 * errors are ignored without warning.
381 *****************************************************************************/
382 static int LoadChannels( intf_thread_t *p_intf, char *psz_filename )
384 FILE * p_file; /* file */
385 intf_channel_t * p_channel; /* current channel */
386 char psz_line[INTF_MAX_CMD_SIZE]; /* line buffer */
387 int i_index; /* channel or field index */
389 /* Set default value */
390 p_intf->p_channel = NULL;
393 p_file = fopen( psz_filename, "r" );
396 intf_ErrMsg("error: can't open %s (%s)\n", psz_filename, strerror(errno));
400 /* First pass: count number of lines */
401 for( i_index = 0; fgets( psz_line, INTF_MAX_CMD_SIZE, p_file ) != NULL; i_index++ )
408 /* Allocate array and rewind - some of the lines may be invalid, and the
409 * array will probably be larger than the actual number of channels, but
410 * it has no consequence. */
411 p_intf->p_channel = malloc( sizeof( intf_channel_t ) * i_index );
412 if( p_intf->p_channel == NULL )
414 intf_ErrMsg("error: %s\n", strerror(ENOMEM));
418 p_channel = p_intf->p_channel;
421 /* Second pass: read channels descriptions */
422 while( fgets( psz_line, INTF_MAX_CMD_SIZE, p_file ) != NULL )
424 if( !ParseChannel( p_channel, psz_line ) )
426 intf_DbgMsg("channel [%d] %s : method %d (%s:%d vlan %d)\n",
427 p_channel->i_channel, p_channel->psz_description,
428 p_channel->i_input_method, p_channel->psz_input_source,
429 p_channel->i_input_port, p_channel->i_input_vlan );
434 /* Add marker at the end of the array */
435 p_channel->i_channel = -1;
443 /*****************************************************************************
444 * UnloadChannels: unload channels description
445 *****************************************************************************
446 * This function free all resources allocated by LoadChannels, if any.
447 *****************************************************************************/
448 static void UnloadChannels( intf_thread_t *p_intf )
450 int i_channel; /* channel index */
452 if( p_intf->p_channel != NULL )
454 /* Free allocated strings */
456 p_intf->p_channel[ i_channel ].i_channel != -1;
459 if( p_intf->p_channel[ i_channel ].psz_description != NULL )
461 free( p_intf->p_channel[ i_channel ].psz_description );
463 if( p_intf->p_channel[ i_channel ].psz_input_source != NULL )
465 free( p_intf->p_channel[ i_channel ].psz_input_source );
470 free( p_intf->p_channel );
471 p_intf->p_channel = NULL;
476 /*****************************************************************************
477 * ParseChannel: parse a channel description line
478 *****************************************************************************
479 * See LoadChannels. This function return non 0 on parsing error.
480 *****************************************************************************/
481 static int ParseChannel( intf_channel_t *p_channel, char *psz_str )
483 char * psz_index; /* current character */
484 char * psz_end; /* end pointer for strtol */
485 int i_field; /* field number, -1 on error */
486 int i_field_length; /* field length, for text fields */
488 /* Set some default fields */
489 p_channel->i_channel = 0;
490 p_channel->psz_description = NULL;
491 p_channel->i_input_method = 0;
492 p_channel->psz_input_source = NULL;
493 p_channel->i_input_port = 0;
494 p_channel->i_input_vlan = 0;
498 for( psz_index = psz_str; (i_field != -1) && (*psz_index != '\0'); psz_index++ )
500 if( *psz_index == ';' )
502 /* Mark end of field */
508 case 0: /* channel number */
509 p_channel->i_channel = strtol( psz_str, &psz_end, 0);
510 if( (*psz_str == '\0') || (*psz_end != '\0') )
515 case 1: /* channel description */
516 i_field_length = strlen( psz_str );
517 if( i_field_length != 0 )
519 p_channel->psz_description = malloc( i_field_length + 1 );
520 if( p_channel->psz_description == NULL )
522 intf_ErrMsg("error: %s\n", strerror( ENOMEM ));
527 strcpy( p_channel->psz_description, psz_str );
531 case 2: /* input method */
532 p_channel->i_input_method = strtol( psz_str, &psz_end, 0);
533 if( (*psz_str == '\0') || (*psz_end != '\0') )
538 case 3: /* input source */
539 i_field_length = strlen( psz_str );
540 if( i_field_length != 0 )
542 p_channel->psz_input_source = malloc( i_field_length + 1 );
543 if( p_channel->psz_input_source == NULL )
545 intf_ErrMsg("error: %s\n", strerror( ENOMEM ));
550 strcpy( p_channel->psz_input_source, psz_str );
554 case 4: /* input port */
555 p_channel->i_input_port = strtol( psz_str, &psz_end, 0);
556 if( (*psz_str == '\0') || (*psz_end != '\0') )
561 case 5: /* input vlan */
562 p_channel->i_channel = strtol( psz_str, &psz_end, 0);
563 if( (*psz_str == '\0') || (*psz_end != '\0') )
568 /* ... following fields are ignored */
571 /* Set new beginning of field */
572 psz_str = psz_index + 1;
576 /* At least the first three fields must be parsed sucessfully for function
577 * success. Other parsing errors are returned using i_field = -1. */
580 /* Function fails. Free allocated strings */
581 if( p_channel->psz_description != NULL )
583 free( p_channel->psz_description );
585 if( p_channel->psz_input_source != NULL )
587 free( p_channel->psz_input_source );