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