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