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