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