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