]> git.sesse.net Git - vlc/blob - src/interface/interface.c
Changement de cha�ne configurable depuis un fichier texte.
[vlc] / src / interface / interface.c
1 /*******************************************************************************
2  * interface.c: interface access for other threads
3  * (c)1998 VideoLAN
4  *******************************************************************************
5  * This library provides basic functions for threads to interact with user
6  * interface, such as command line.
7  *******************************************************************************/
8
9 /*******************************************************************************
10  * Preamble
11  *******************************************************************************/
12 #include <errno.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/types.h>
17 #include <sys/uio.h>                                        /* for input.h */
18
19 #include "config.h"
20 #include "common.h"
21 #include "mtime.h"
22 #include "vlc_thread.h"
23 #include "input.h"
24 #include "intf_msg.h"
25 #include "interface.h"
26 #include "intf_cmd.h"
27 #include "intf_console.h"
28 #include "main.h"
29 #include "video.h"
30 #include "video_output.h"
31
32 #include "intf_sys.h"
33
34 /*******************************************************************************
35  * intf_channel_t: channel description
36  *******************************************************************************
37  * A 'channel' is a descriptor of an input method. It is used to switch easily
38  * from source to source without having to specify the whole input thread 
39  * configuration. The channels array, stored in the interface thread object, is
40  * loaded in intf_Create, and unloaded in intf_Destroy.
41  *******************************************************************************/
42 typedef struct intf_channel_s
43 {
44     /* Channel description */
45     int         i_channel;              /* channel number, -1 for end of array */
46     char *      psz_description;                /* channel description (owned) */
47     
48     /* Input configuration */
49     int         i_input_method;                     /* input method descriptor */
50     char *      psz_input_source;                     /* source string (owned) */
51     int         i_input_port;                                          /* port */
52     int         i_input_vlan;                                          /* vlan */
53 } intf_channel_t;
54
55 /*******************************************************************************
56  * Local prototypes
57  *******************************************************************************/
58 static int      LoadChannels    ( intf_thread_t *p_intf, char *psz_filename );
59 static void     UnloadChannels  ( intf_thread_t *p_intf );
60 static int      ParseChannel    ( intf_channel_t *p_channel, char *psz_str );
61
62 /*******************************************************************************
63  * intf_Create: prepare interface before main loop
64  *******************************************************************************
65  * This function opens output devices and create specific interfaces. It send
66  * it's own error messages.
67  *******************************************************************************/
68 intf_thread_t* intf_Create( void )
69 {    
70     intf_thread_t *p_intf;                                        
71
72     /* Allocate structure */
73     p_intf = malloc( sizeof( intf_thread_t ) );
74     if( !p_intf )
75     {
76         intf_ErrMsg("error: %s\n", strerror( ENOMEM ) );        
77         return( NULL );
78     }
79
80     /* Initialize structure */
81     p_intf->b_die =     0;    
82     p_intf->p_vout =    NULL;
83     p_intf->p_input =   NULL;   
84
85     /* Load channels - the pointer will be set to NULL on failure. The 
86      * return value is ignored since the program can work without 
87      * channels */
88     LoadChannels( p_intf, main_GetPszVariable( INTF_CHANNELS_VAR, INTF_CHANNELS_DEFAULT ));    
89
90     /* Start interfaces */
91     p_intf->p_console = intf_ConsoleCreate();
92     if( p_intf->p_console == NULL )
93     {
94         intf_ErrMsg("error: can't create control console\n");
95         free( p_intf );
96         return( NULL );
97     }
98     if( intf_SysCreate( p_intf ) )
99     {
100         intf_ErrMsg("error: can't create interface\n");
101         intf_ConsoleDestroy( p_intf->p_console );
102         free( p_intf );
103         return( NULL );
104     }   
105
106     intf_Msg("Interface initialized\n");    
107     return( p_intf );
108 }
109
110 /*******************************************************************************
111  * intf_Run
112  *******************************************************************************
113  * Initialization script and main interface loop.
114  *******************************************************************************/
115 void intf_Run( intf_thread_t *p_intf )
116
117     /* Execute the initialization script - if a positive number is returned, 
118      * the script could be executed but failed */
119     if( intf_ExecScript( main_GetPszVariable( INTF_INIT_SCRIPT_VAR, INTF_INIT_SCRIPT_DEFAULT ) ) > 0 )
120     {
121         intf_ErrMsg("warning: error(s) during startup script\n");
122     }
123
124     /* Main loop */
125     while(!p_intf->b_die)
126     {
127         /* Flush waiting messages */
128         intf_FlushMsg();
129
130         /* Manage specific interface */
131         intf_SysManage( p_intf );
132
133         /* Check attached threads status */
134         if( (p_intf->p_vout != NULL) && p_intf->p_vout->b_error )
135         {
136             //?? add aout error detection
137             p_intf->b_die = 1;            
138         }    
139         if( (p_intf->p_input != NULL) && p_intf->p_input->b_error )
140         {
141             input_DestroyThread( p_intf->p_input, NULL );            
142             p_intf->p_input = NULL;            
143             intf_DbgMsg("Input thread destroyed\n");            
144         }
145
146         /* Sleep to avoid using all CPU - since some interfaces needs to access 
147          * keyboard events, a 100ms delay is a good compromise */
148         msleep( INTF_IDLE_SLEEP );
149     }
150 }
151
152 /*******************************************************************************
153  * intf_Destroy: clean interface after main loop
154  *******************************************************************************
155  * This function destroys specific interfaces and close output devices.
156  *******************************************************************************/
157 void intf_Destroy( intf_thread_t *p_intf )
158 {
159     /* Destroy interfaces */
160     intf_SysDestroy( p_intf );
161     intf_ConsoleDestroy( p_intf->p_console );
162         
163     /* Unload channels */
164     UnloadChannels( p_intf );    
165
166     /* Free structure */
167     free( p_intf );
168 }
169
170 /*******************************************************************************
171  * intf_SelectChannel: change channel
172  *******************************************************************************
173  * Kill existing input, if any, and try to open a new one, using an input
174  * configuration table.
175  *******************************************************************************/
176 int intf_SelectChannel( intf_thread_t * p_intf, int i_channel )
177 {
178     intf_channel_t *    p_channel;                                  /* channel */
179     
180     /* Look for channel in array */
181     if( p_intf->p_channel != NULL )
182     {
183         for( p_channel = p_intf->p_channel; p_channel->i_channel != -1; p_channel++ )
184         {
185             if( p_channel->i_channel == i_channel )
186             {
187                 /*
188                  * Change channel
189                  */
190
191                 /* Kill existing input, if any */
192                 if( p_intf->p_input != NULL )
193                 {        
194                     input_DestroyThread( p_intf->p_input, NULL );
195                 }
196                 
197                 intf_Msg("Channel %d: %s\n", i_channel, p_channel->psz_description );                
198
199                 /* Open a new input */
200                 p_intf->p_input = input_CreateThread( p_channel->i_input_method, p_channel->psz_input_source,
201                                                       p_channel->i_input_port, p_channel->i_input_vlan, 
202                                                       p_intf->p_vout, p_main->p_aout, NULL );        
203                 return( p_intf->p_input == NULL );             
204             }            
205         }        
206     }
207
208     /* Channel does not exist */
209     intf_Msg("Channel %d does not exist\n", i_channel );
210     return( 1 );    
211 }
212
213 /*******************************************************************************
214  * intf_ProcessKey: process standard keys
215  *******************************************************************************
216  * This function will process standard keys and return non 0 if the key was
217  * unknown.
218  *******************************************************************************/
219 int intf_ProcessKey( intf_thread_t *p_intf, int i_key )
220 {
221     switch( i_key )
222     {
223     case 'Q':                                                    /* quit order */
224     case 'q':
225     case 27: /* escape key */
226         p_intf->b_die = 1;
227         break;  
228     case '0':                                                 /* source change */
229     case '1':
230     case '2':
231     case '3':
232     case '4':
233     case '5':
234     case '6':
235     case '7':
236     case '8':
237     case '9':                    
238         /* Change channel - return code is ignored since SelectChannel displays
239          * its own error messages */
240         intf_SelectChannel( p_intf, i_key - '0' );        
241         break;
242     case '+':                                                      /* volume + */
243         // ??
244         break;
245     case '-':                                                      /* volume - */
246         // ??
247         break;
248     case 'M':                                                   /* toggle mute */
249     case 'm':                    
250         // ??
251         break;      
252     case 'g':                                                       /* gamma - */
253         if( (p_intf->p_vout != NULL) && (p_intf->p_vout->f_gamma > -INTF_GAMMA_LIMIT) )
254         {
255             vlc_mutex_lock( &p_intf->p_vout->change_lock );
256             p_intf->p_vout->f_gamma   -= INTF_GAMMA_STEP;                        
257             p_intf->p_vout->i_changes |= VOUT_GAMMA_CHANGE;
258             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
259         }                    
260         break;                                        
261     case 'G':                                                       /* gamma + */
262         if( (p_intf->p_vout != NULL) && (p_intf->p_vout->f_gamma < INTF_GAMMA_LIMIT) )
263         {       
264             vlc_mutex_lock( &p_intf->p_vout->change_lock );
265             p_intf->p_vout->f_gamma   += INTF_GAMMA_STEP;
266             p_intf->p_vout->i_changes |= VOUT_GAMMA_CHANGE;
267             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
268         }                    
269         break;  
270     case 'c':                                              /* toggle grayscale */
271         if( p_intf->p_vout != NULL )
272         {
273             vlc_mutex_lock( &p_intf->p_vout->change_lock );                        
274             p_intf->p_vout->b_grayscale = !p_intf->p_vout->b_grayscale;                    
275             p_intf->p_vout->i_changes  |= VOUT_GRAYSCALE_CHANGE;                        
276             vlc_mutex_unlock( &p_intf->p_vout->change_lock );      
277         }
278         break;  
279     case ' ':                                              /* toggle interface */
280         if( p_intf->p_vout != NULL )
281         {
282             vlc_mutex_lock( &p_intf->p_vout->change_lock );                        
283             p_intf->p_vout->b_interface     = !p_intf->p_vout->b_interface;                    
284             p_intf->p_vout->i_changes |= VOUT_INTF_CHANGE;                        
285             vlc_mutex_unlock( &p_intf->p_vout->change_lock );      
286         }
287         break;                                
288     case 'i':                                                   /* toggle info */
289         if( p_intf->p_vout != NULL )
290         {
291             vlc_mutex_lock( &p_intf->p_vout->change_lock );                        
292             p_intf->p_vout->b_info     = !p_intf->p_vout->b_info;                    
293             p_intf->p_vout->i_changes |= VOUT_INFO_CHANGE;                        
294             vlc_mutex_unlock( &p_intf->p_vout->change_lock );      
295         }
296         break;                                
297     case 's':                                                 /* toggle scaling */
298         if( p_intf->p_vout != NULL )
299         {
300             vlc_mutex_lock( &p_intf->p_vout->change_lock );                        
301             p_intf->p_vout->b_scale    = !p_intf->p_vout->b_scale;                    
302             p_intf->p_vout->i_changes |= VOUT_SCALE_CHANGE;                        
303             vlc_mutex_unlock( &p_intf->p_vout->change_lock );      
304         }
305         break;                                
306    default:                                                    /* unknown key */
307         return( 1 );        
308     }
309
310     return( 0 );    
311 }
312
313 /* following functions are local */
314                     
315 /*******************************************************************************
316  * LoadChannels: load channels description from a file
317  *******************************************************************************
318  * This structe describes all interface-specific data of the main (interface)
319  * thread.
320  * Each line of the file is a semicolon separated list of the following 
321  * fields :
322  *      integer         channel number
323  *      string          channel description
324  *      integer         input method (see input.h)
325  *      string          input source
326  *      integer         input port
327  *      integer         input vlan
328  * The last field must end with a semicolon.
329  * Comments and empty lines are not explicitely allowed, but lines with parsing
330  * errors are ignored without warning.
331  *******************************************************************************/
332 static int LoadChannels( intf_thread_t *p_intf, char *psz_filename )
333 {
334     FILE *              p_file;                                        /* file */
335     intf_channel_t *    p_channel;                          /* current channel */    
336     char                psz_line[INTF_MAX_CMD_SIZE];            /* line buffer */
337     int                 i_index;                     /* channel or field index */        
338
339     /* Set default value */
340     p_intf->p_channel = NULL;    
341     
342     /* Open file */
343     p_file = fopen( psz_filename, "r" );
344     if( p_file == NULL )
345     {
346         intf_ErrMsg("error: can't open %s (%s)\n", psz_filename, strerror(errno));
347         return( 1 );        
348     }
349
350     /* First pass: count number of lines */
351     for( i_index = 0; fgets( psz_line, INTF_MAX_CMD_SIZE, p_file ) != NULL; i_index++ )
352     {
353         ;        
354     }
355
356     if( i_index != 0 )
357     {        
358         /* Allocate array and rewind - some of the lines may be invalid, and the
359          * array will probably be larger than the actual number of channels, but
360          * it has no consequence. */
361         p_intf->p_channel = malloc( sizeof( intf_channel_t ) * i_index );
362         if( p_intf->p_channel == NULL )
363         {
364             intf_ErrMsg("error: %s\n", strerror(ENOMEM));
365             fclose( p_file );
366             return( 1 );            
367         }        
368         p_channel = p_intf->p_channel;        
369         rewind( p_file );
370
371         /* Second pass: read channels descriptions */
372         while( fgets( psz_line, INTF_MAX_CMD_SIZE, p_file ) != NULL )
373         {
374             if( !ParseChannel( p_channel, psz_line ) )
375             {
376                 intf_DbgMsg("channel [%d] %s : method %d (%s:%d vlan %d)\n",
377                             p_channel->i_channel, p_channel->psz_description,
378                             p_channel->i_input_method, p_channel->psz_input_source,
379                             p_channel->i_input_port, p_channel->i_input_vlan );                
380                 p_channel++;                
381             }
382         }
383         
384         /* Add marker at the end of the array */
385         p_channel->i_channel = -1;
386     }
387
388     /* Close file */
389     fclose( p_file );    
390     return( 0 );
391 }
392
393 /******************************************************************************
394  * UnloadChannels: unload channels description
395  ******************************************************************************
396  * This function free all resources allocated by LoadChannels, if any.
397  ******************************************************************************/
398 static void UnloadChannels( intf_thread_t *p_intf )
399 {
400     int i_channel;                                           /* channel index */    
401     
402     if( p_intf->p_channel != NULL )
403     {
404         /* Free allocated strings */
405         for( i_channel = 0; 
406              p_intf->p_channel[ i_channel ].i_channel != -1; 
407              i_channel++ )
408         {
409             if( p_intf->p_channel[ i_channel ].psz_description != NULL )
410             {                
411                 free( p_intf->p_channel[ i_channel ].psz_description );
412             }
413             if( p_intf->p_channel[ i_channel ].psz_input_source != NULL )
414             {                
415                 free( p_intf->p_channel[ i_channel ].psz_input_source );            
416             }            
417         }        
418
419         /* Free array */
420         free( p_intf->p_channel );        
421         p_intf->p_channel = NULL;        
422     }    
423 }
424
425
426 /*******************************************************************************
427  * ParseChannel: parse a channel description line
428  *******************************************************************************
429  * See LoadChannels. This function return non 0 on parsing error.
430  *******************************************************************************/
431 static int ParseChannel( intf_channel_t *p_channel, char *psz_str )
432 {
433     char *      psz_index;                                /* current character */
434     char *      psz_end;                             /* end pointer for strtol */    
435     int         i_field;                          /* field number, -1 on error */
436     int         i_field_length;               /* field length, for text fields */
437
438     /* Set some default fields */
439     p_channel->i_channel =              0;
440     p_channel->psz_description =        NULL;
441     p_channel->i_input_method =         0;
442     p_channel->psz_input_source =       NULL;
443     p_channel->i_input_port =           0;
444     p_channel->i_input_vlan =           0;    
445
446     /* Parse string */
447     i_field = 0;
448     for( psz_index = psz_str; (i_field != -1) && (*psz_index != '\0'); psz_index++ )
449     {
450         if( *psz_index == ';' )
451         {
452             /* Mark end of field */
453             *psz_index = '\0';
454
455             /* Parse field */
456             switch( i_field++ )
457             {
458             case 0:                                          /* channel number */
459                 p_channel->i_channel = strtol( psz_str, &psz_end, 0);
460                 if( (*psz_str == '\0') || (*psz_end != '\0') )
461                 {
462                     i_field = -1;
463                 }
464                 break;                
465             case 1:                                     /* channel description */
466                 i_field_length = strlen( psz_str );                
467                 if( i_field_length != 0 )
468                 {
469                     p_channel->psz_description = malloc( i_field_length + 1 );
470                     if( p_channel->psz_description == NULL )
471                     {
472                         intf_ErrMsg("error: %s\n", strerror( ENOMEM ));                        
473                         i_field = -1;                        
474                     }
475                     else
476                     {                        
477                         strcpy( p_channel->psz_description, psz_str );                        
478                     }                        
479                 }
480                 break;                
481             case 2:                                            /* input method */
482                 p_channel->i_input_method = strtol( psz_str, &psz_end, 0);
483                 if( (*psz_str == '\0') || (*psz_end != '\0') )
484                 {
485                     i_field = -1;
486                 }
487                 break;                
488             case 3:                                            /* input source */
489                 i_field_length = strlen( psz_str );                
490                 if( i_field_length != 0 )
491                 {
492                     p_channel->psz_input_source = malloc( i_field_length + 1 );
493                     if( p_channel->psz_input_source == NULL )
494                     {
495                         intf_ErrMsg("error: %s\n", strerror( ENOMEM ));                        
496                         i_field = -1;                        
497                     }
498                     else
499                     {                        
500                         strcpy( p_channel->psz_input_source, psz_str );                        
501                     }                        
502                 }
503                 break;                
504             case 4:                                              /* input port */
505                 p_channel->i_input_port = strtol( psz_str, &psz_end, 0);
506                 if( (*psz_str == '\0') || (*psz_end != '\0') )
507                 {
508                     i_field = -1;
509                 }
510                 break;                                
511             case 5:                                              /* input vlan */
512                 p_channel->i_channel = strtol( psz_str, &psz_end, 0);
513                 if( (*psz_str == '\0') || (*psz_end != '\0') )
514                 {
515                     i_field = -1;
516                 }
517                 break;                
518                 /* ... following fields are ignored */
519             }
520
521             /* Set new beginning of field */
522             psz_str = psz_index + 1;
523         }
524     }      
525
526     /* At least the first three fields must be parsed sucessfully for function
527      * success. Other parsing errors are returned using i_field = -1. */    
528     if( i_field < 3 )
529     {
530         /* Function fails. Free allocated strings */
531         if( p_channel->psz_description != NULL )
532         {
533             free( p_channel->psz_description );            
534         }
535         if( p_channel->psz_input_source != NULL )
536         {
537             free( p_channel->psz_input_source );            
538         }                
539         return( 1 );        
540     }
541
542     /* Return success */
543     return( 0 );    
544 }