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