]> git.sesse.net Git - vlc/blob - src/interface/interface.c
. gestion du gamma en 8bpp grayscale
[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     case 3:                                                            /* ^C */
227         p_intf->b_die = 1;
228         break;  
229     case '0':                                               /* source change */
230     case '1':
231     case '2':
232     case '3':
233     case '4':
234     case '5':
235     case '6':
236     case '7':
237     case '8':
238     case '9':                    
239         /* Change channel - return code is ignored since SelectChannel displays
240          * its own error messages */
241         intf_SelectChannel( p_intf, i_key - '0' );        
242         break;
243     case '+':                                                    /* volume + */
244         // ??
245         break;
246     case '-':                                                    /* volume - */
247         // ??
248         break;
249     case 'M':                                                 /* toggle mute */
250     case 'm':                    
251         // ??
252         break;      
253     case 'g':                                                     /* gamma - */
254         if( (p_intf->p_vout != NULL) && (p_intf->p_vout->f_gamma > -INTF_GAMMA_LIMIT) )
255         {
256             vlc_mutex_lock( &p_intf->p_vout->change_lock );
257             p_intf->p_vout->f_gamma   -= INTF_GAMMA_STEP;                        
258             p_intf->p_vout->i_changes |= VOUT_GAMMA_CHANGE;
259             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
260         }                    
261         break;                                        
262     case 'G':                                                     /* gamma + */
263         if( (p_intf->p_vout != NULL) && (p_intf->p_vout->f_gamma < INTF_GAMMA_LIMIT) )
264         {       
265             vlc_mutex_lock( &p_intf->p_vout->change_lock );
266             p_intf->p_vout->f_gamma   += INTF_GAMMA_STEP;
267             p_intf->p_vout->i_changes |= VOUT_GAMMA_CHANGE;
268             vlc_mutex_unlock( &p_intf->p_vout->change_lock );
269         }                    
270         break;  
271     case 'c':                                            /* toggle grayscale */
272         if( p_intf->p_vout != NULL )
273         {
274             vlc_mutex_lock( &p_intf->p_vout->change_lock );                        
275             p_intf->p_vout->b_grayscale = !p_intf->p_vout->b_grayscale;                    
276             p_intf->p_vout->i_changes  |= VOUT_GRAYSCALE_CHANGE;                        
277             vlc_mutex_unlock( &p_intf->p_vout->change_lock );      
278         }
279         break;  
280     case ' ':                                            /* toggle interface */
281         if( p_intf->p_vout != NULL )
282         {
283             vlc_mutex_lock( &p_intf->p_vout->change_lock );                        
284             p_intf->p_vout->b_interface     = !p_intf->p_vout->b_interface;                    
285             p_intf->p_vout->i_changes |= VOUT_INTF_CHANGE;                        
286             vlc_mutex_unlock( &p_intf->p_vout->change_lock );      
287         }
288         break;                                
289     case 'i':                                                 /* toggle info */
290         if( p_intf->p_vout != NULL )
291         {
292             vlc_mutex_lock( &p_intf->p_vout->change_lock );                        
293             p_intf->p_vout->b_info     = !p_intf->p_vout->b_info;                    
294             p_intf->p_vout->i_changes |= VOUT_INFO_CHANGE;                        
295             vlc_mutex_unlock( &p_intf->p_vout->change_lock );      
296         }
297         break;                                
298     case 's':                                              /* toggle scaling */
299         if( p_intf->p_vout != NULL )
300         {
301             vlc_mutex_lock( &p_intf->p_vout->change_lock );                        
302             p_intf->p_vout->b_scale    = !p_intf->p_vout->b_scale;                    
303             p_intf->p_vout->i_changes |= VOUT_SCALE_CHANGE;                        
304             vlc_mutex_unlock( &p_intf->p_vout->change_lock );      
305         }
306         break;                                
307    default:                                                   /* unknown key */
308         return( 1 );        
309     }
310
311     return( 0 );    
312 }
313
314 /* following functions are local */
315                     
316 /*******************************************************************************
317  * LoadChannels: load channels description from a file
318  *******************************************************************************
319  * This structe describes all interface-specific data of the main (interface)
320  * thread.
321  * Each line of the file is a semicolon separated list of the following 
322  * fields :
323  *      integer         channel number
324  *      string          channel description
325  *      integer         input method (see input.h)
326  *      string          input source
327  *      integer         input port
328  *      integer         input vlan
329  * The last field must end with a semicolon.
330  * Comments and empty lines are not explicitely allowed, but lines with parsing
331  * errors are ignored without warning.
332  *******************************************************************************/
333 static int LoadChannels( intf_thread_t *p_intf, char *psz_filename )
334 {
335     FILE *              p_file;                                        /* file */
336     intf_channel_t *    p_channel;                          /* current channel */    
337     char                psz_line[INTF_MAX_CMD_SIZE];            /* line buffer */
338     int                 i_index;                     /* channel or field index */        
339
340     /* Set default value */
341     p_intf->p_channel = NULL;    
342     
343     /* Open file */
344     p_file = fopen( psz_filename, "r" );
345     if( p_file == NULL )
346     {
347         intf_ErrMsg("error: can't open %s (%s)\n", psz_filename, strerror(errno));
348         return( 1 );        
349     }
350
351     /* First pass: count number of lines */
352     for( i_index = 0; fgets( psz_line, INTF_MAX_CMD_SIZE, p_file ) != NULL; i_index++ )
353     {
354         ;        
355     }
356
357     if( i_index != 0 )
358     {        
359         /* Allocate array and rewind - some of the lines may be invalid, and the
360          * array will probably be larger than the actual number of channels, but
361          * it has no consequence. */
362         p_intf->p_channel = malloc( sizeof( intf_channel_t ) * i_index );
363         if( p_intf->p_channel == NULL )
364         {
365             intf_ErrMsg("error: %s\n", strerror(ENOMEM));
366             fclose( p_file );
367             return( 1 );            
368         }        
369         p_channel = p_intf->p_channel;        
370         rewind( p_file );
371
372         /* Second pass: read channels descriptions */
373         while( fgets( psz_line, INTF_MAX_CMD_SIZE, p_file ) != NULL )
374         {
375             if( !ParseChannel( p_channel, psz_line ) )
376             {
377                 intf_DbgMsg("channel [%d] %s : method %d (%s:%d vlan %d)\n",
378                             p_channel->i_channel, p_channel->psz_description,
379                             p_channel->i_input_method, p_channel->psz_input_source,
380                             p_channel->i_input_port, p_channel->i_input_vlan );                
381                 p_channel++;                
382             }
383         }
384         
385         /* Add marker at the end of the array */
386         p_channel->i_channel = -1;
387     }
388
389     /* Close file */
390     fclose( p_file );    
391     return( 0 );
392 }
393
394 /******************************************************************************
395  * UnloadChannels: unload channels description
396  ******************************************************************************
397  * This function free all resources allocated by LoadChannels, if any.
398  ******************************************************************************/
399 static void UnloadChannels( intf_thread_t *p_intf )
400 {
401     int i_channel;                                           /* channel index */    
402     
403     if( p_intf->p_channel != NULL )
404     {
405         /* Free allocated strings */
406         for( i_channel = 0; 
407              p_intf->p_channel[ i_channel ].i_channel != -1; 
408              i_channel++ )
409         {
410             if( p_intf->p_channel[ i_channel ].psz_description != NULL )
411             {                
412                 free( p_intf->p_channel[ i_channel ].psz_description );
413             }
414             if( p_intf->p_channel[ i_channel ].psz_input_source != NULL )
415             {                
416                 free( p_intf->p_channel[ i_channel ].psz_input_source );            
417             }            
418         }        
419
420         /* Free array */
421         free( p_intf->p_channel );        
422         p_intf->p_channel = NULL;        
423     }    
424 }
425
426
427 /*******************************************************************************
428  * ParseChannel: parse a channel description line
429  *******************************************************************************
430  * See LoadChannels. This function return non 0 on parsing error.
431  *******************************************************************************/
432 static int ParseChannel( intf_channel_t *p_channel, char *psz_str )
433 {
434     char *      psz_index;                                /* current character */
435     char *      psz_end;                             /* end pointer for strtol */    
436     int         i_field;                          /* field number, -1 on error */
437     int         i_field_length;               /* field length, for text fields */
438
439     /* Set some default fields */
440     p_channel->i_channel =              0;
441     p_channel->psz_description =        NULL;
442     p_channel->i_input_method =         0;
443     p_channel->psz_input_source =       NULL;
444     p_channel->i_input_port =           0;
445     p_channel->i_input_vlan =           0;    
446
447     /* Parse string */
448     i_field = 0;
449     for( psz_index = psz_str; (i_field != -1) && (*psz_index != '\0'); psz_index++ )
450     {
451         if( *psz_index == ';' )
452         {
453             /* Mark end of field */
454             *psz_index = '\0';
455
456             /* Parse field */
457             switch( i_field++ )
458             {
459             case 0:                                          /* channel number */
460                 p_channel->i_channel = strtol( psz_str, &psz_end, 0);
461                 if( (*psz_str == '\0') || (*psz_end != '\0') )
462                 {
463                     i_field = -1;
464                 }
465                 break;                
466             case 1:                                     /* channel description */
467                 i_field_length = strlen( psz_str );                
468                 if( i_field_length != 0 )
469                 {
470                     p_channel->psz_description = malloc( i_field_length + 1 );
471                     if( p_channel->psz_description == NULL )
472                     {
473                         intf_ErrMsg("error: %s\n", strerror( ENOMEM ));                        
474                         i_field = -1;                        
475                     }
476                     else
477                     {                        
478                         strcpy( p_channel->psz_description, psz_str );                        
479                     }                        
480                 }
481                 break;                
482             case 2:                                            /* input method */
483                 p_channel->i_input_method = strtol( psz_str, &psz_end, 0);
484                 if( (*psz_str == '\0') || (*psz_end != '\0') )
485                 {
486                     i_field = -1;
487                 }
488                 break;                
489             case 3:                                            /* input source */
490                 i_field_length = strlen( psz_str );                
491                 if( i_field_length != 0 )
492                 {
493                     p_channel->psz_input_source = malloc( i_field_length + 1 );
494                     if( p_channel->psz_input_source == NULL )
495                     {
496                         intf_ErrMsg("error: %s\n", strerror( ENOMEM ));                        
497                         i_field = -1;                        
498                     }
499                     else
500                     {                        
501                         strcpy( p_channel->psz_input_source, psz_str );                        
502                     }                        
503                 }
504                 break;                
505             case 4:                                              /* input port */
506                 p_channel->i_input_port = strtol( psz_str, &psz_end, 0);
507                 if( (*psz_str == '\0') || (*psz_end != '\0') )
508                 {
509                     i_field = -1;
510                 }
511                 break;                                
512             case 5:                                              /* input vlan */
513                 p_channel->i_channel = strtol( psz_str, &psz_end, 0);
514                 if( (*psz_str == '\0') || (*psz_end != '\0') )
515                 {
516                     i_field = -1;
517                 }
518                 break;                
519                 /* ... following fields are ignored */
520             }
521
522             /* Set new beginning of field */
523             psz_str = psz_index + 1;
524         }
525     }      
526
527     /* At least the first three fields must be parsed sucessfully for function
528      * success. Other parsing errors are returned using i_field = -1. */    
529     if( i_field < 3 )
530     {
531         /* Function fails. Free allocated strings */
532         if( p_channel->psz_description != NULL )
533         {
534             free( p_channel->psz_description );            
535         }
536         if( p_channel->psz_input_source != NULL )
537         {
538             free( p_channel->psz_input_source );            
539         }                
540         return( 1 );        
541     }
542
543     /* Return success */
544     return( 0 );    
545 }