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