]> git.sesse.net Git - vlc/blob - modules/control/rtci.c
Improvements to preferences
[vlc] / modules / control / rtci.c
1 /*****************************************************************************
2  * rtci.c : real time control interface stdin/stdout module for vlc
3  *****************************************************************************
4  * Copyright (C) 2004 VideoLAN
5  * $Id: rc.c 8847 2004-09-29 11:36:37Z markfm $
6  *
7  * Author: Peter Surda <shurdeek@panorama.sth.ac.at>
8  *         Jean-Paul Saman
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  *  Module purpose -- provide a socket based interface to control various 
27  *  VLC parameters and obtain status information on the fly.  Output 
28  *  will be purposely less verbose than on some interfaces -- this 
29  *  interface is intended more for comms with an external program than a human
30  *****************************************************************************/
31  
32 /*****************************************************************************
33  * Preamble
34  *****************************************************************************/
35 #include <stdlib.h>                                      /* malloc(), free() */
36 #include <string.h>
37
38 #include <errno.h>                                                 /* ENOMEM */
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <signal.h>
42
43 #include <vlc/vlc.h>
44 #include <vlc/intf.h>
45 #include <vlc/aout.h>
46 #include <vlc/vout.h>
47
48 #ifdef HAVE_UNISTD_H
49 #    include <unistd.h>
50 #endif
51
52 #ifdef HAVE_SYS_TIME_H
53 #    include <sys/time.h>
54 #endif
55 #include <sys/types.h>
56
57 #include "vlc_error.h"
58 #include "network.h"
59
60 #if defined(PF_UNIX) && !defined(PF_LOCAL)
61 #    define PF_LOCAL PF_UNIX
62 #endif
63 #if defined(AF_UNIX) && !defined(AF_LOCAL)
64 #    define AF_LOCAL AF_UNIX
65 #endif
66
67 #ifdef PF_LOCAL
68 #    include <sys/un.h>
69 #endif
70
71 #define MAX_LINE_LENGTH 256
72
73 /*****************************************************************************
74  * Local prototypes
75  *****************************************************************************/
76 static int  Activate     ( vlc_object_t * );
77 static void Deactivate   ( vlc_object_t * );
78 static void Run          ( intf_thread_t * );
79
80 static vlc_bool_t ReadCommand( intf_thread_t *, char *, int * );
81
82 static int  Input        ( vlc_object_t *, char const *,
83                            vlc_value_t, vlc_value_t, void * );
84 static int  Playlist     ( vlc_object_t *, char const *,
85                            vlc_value_t, vlc_value_t, void * );
86 static int  Other        ( vlc_object_t *, char const *,
87                            vlc_value_t, vlc_value_t, void * );
88 static int  Quit         ( vlc_object_t *, char const *,
89                            vlc_value_t, vlc_value_t, void * );
90 static int  Intf         ( vlc_object_t *, char const *,
91                            vlc_value_t, vlc_value_t, void * );
92 static int  Volume       ( vlc_object_t *, char const *,
93                            vlc_value_t, vlc_value_t, void * );
94 static int  VolumeMove   ( vlc_object_t *, char const *,
95                            vlc_value_t, vlc_value_t, void * );
96 static int  AudioConfig  ( vlc_object_t *, char const *,
97                            vlc_value_t, vlc_value_t, void * );
98
99 struct intf_sys_t
100 {
101     int i_socket_listen;
102     int i_socket;
103     char *psz_unix_path;
104     vlc_bool_t b_extend;
105     
106 #ifdef WIN32
107     HANDLE hConsoleIn;
108 #endif
109 };
110
111 #ifdef HAVE_VARIADIC_MACROS
112 #   define msg_rtci( psz_format, args... ) \
113       __msg_rtci( p_intf, psz_format, ## args )
114 #endif
115
116 static void __msg_rtci( intf_thread_t *p_intf, const char *psz_fmt, ... )
117 {
118     va_list args;
119     va_start( args, psz_fmt );
120     if( p_intf->p_sys->i_socket == -1 ) vprintf( psz_fmt, args );
121     else net_vaPrintf( p_intf, p_intf->p_sys->i_socket, psz_fmt, args );
122     va_end( args );
123 }
124
125 /*****************************************************************************
126  * Module descriptor
127  *****************************************************************************/
128 #define POS_TEXT N_("Show stream position")
129 #define POS_LONGTEXT N_("Show the current position in seconds within the " \
130                         "stream from time to time." )
131
132 #define TTY_TEXT N_("Fake TTY")
133 #define TTY_LONGTEXT N_("Force the rtci module to use stdin as if it was a TTY.")
134
135 #define UNIX_TEXT N_("UNIX socket command input")
136 #define UNIX_LONGTEXT N_("Accept commands over a Unix socket rather than " \
137                          "stdin." )
138
139 #define HOST_TEXT N_("TCP command input")
140 #define HOST_LONGTEXT N_("Accept commands over a socket rather than stdin. " \
141             "You can set the address and port the interface will bind to." )
142 #define EXTEND_TEXT N_("Extended help")
143 #define EXTEND_LONGTEXT N_("List additional commands.")
144             
145
146 #ifdef WIN32
147 #define QUIET_TEXT N_("Do not open a DOS command box interface")
148 #define QUIET_LONGTEXT N_( \
149     "By default the rtci interface plugin will start a DOS command box. " \
150     "Enabling the quiet mode will not launch this command box." )
151 #endif
152
153 vlc_module_begin();
154     set_description( _("Real time control interface") );
155     set_category( CAT_INTERFACE );
156     set_subcategory( SUBCAT_INTERFACE_CONTROL );
157     add_bool( "rtci-show-pos", 0, NULL, POS_TEXT, POS_LONGTEXT, VLC_TRUE );
158 #ifdef HAVE_ISATTY
159     add_bool( "rtci-fake-tty", 0, NULL, TTY_TEXT, TTY_LONGTEXT, VLC_TRUE );
160 #endif
161     add_string( "rtci-unix", 0, NULL, UNIX_TEXT, UNIX_LONGTEXT, VLC_TRUE );
162     add_string( "rtci-host", 0, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE );
163
164 #ifdef WIN32
165     add_bool( "rtci-quiet", 0, NULL, QUIET_TEXT, QUIET_LONGTEXT, VLC_FALSE );
166 #endif
167     add_bool( "rtci-extend", 0, NULL, EXTEND_TEXT, EXTEND_LONGTEXT, VLC_FALSE );
168
169     set_capability( "interface", 20 );
170     set_callbacks( Activate, Deactivate );
171 vlc_module_end();
172
173 /*****************************************************************************
174  * Activate: initialize and create stuff
175  *****************************************************************************/
176 static int Activate( vlc_object_t *p_this )
177 {
178     intf_thread_t *p_intf = (intf_thread_t*)p_this;
179     char *psz_host, *psz_unix_path;
180     int i_socket = -1;
181
182 #if defined(HAVE_ISATTY) && !defined(WIN32)
183     /* Check that stdin is a TTY */
184     if( !config_GetInt( p_intf, "rtci-fake-tty" ) && !isatty( 0 ) )
185     {
186         msg_Warn( p_intf, "fd 0 is not a TTY" );
187         return VLC_EGENERIC;
188     }
189 #endif
190
191     psz_unix_path = config_GetPsz( p_intf, "rtci-unix" );
192     if( psz_unix_path )
193     {
194 #ifndef PF_LOCAL
195         msg_Warn( p_intf, "your OS doesn't support filesystem sockets" );
196         free( psz_unix_path );
197         return VLC_EGENERIC;
198 #else
199         struct sockaddr_un addr;
200         int i_ret;
201
202         memset( &addr, 0, sizeof(struct sockaddr_un) );
203
204         msg_Dbg( p_intf, "trying UNIX socket" );
205
206         if( (i_socket = socket( PF_LOCAL, SOCK_STREAM, 0 ) ) < 0 )
207         {
208             msg_Warn( p_intf, "can't open socket: %s", strerror(errno) );
209             free( psz_unix_path );
210             return VLC_EGENERIC;
211         }
212
213         addr.sun_family = AF_LOCAL;
214         strncpy( addr.sun_path, psz_unix_path, sizeof( addr.sun_path ) );
215         addr.sun_path[sizeof( addr.sun_path ) - 1] = '\0';
216
217         if( (i_ret = bind( i_socket, (struct sockaddr*)&addr,
218                            sizeof(struct sockaddr_un) ) ) < 0 )
219         {
220             msg_Warn( p_intf, "couldn't bind socket to address: %s",
221                       strerror(errno) );
222             free( psz_unix_path );
223             net_Close( i_socket );
224             return VLC_EGENERIC;
225         }
226
227         if( ( i_ret = listen( i_socket, 1 ) ) < 0 )
228         {
229             msg_Warn( p_intf, "can't listen on socket: %s", strerror(errno));
230             free( psz_unix_path );
231             net_Close( i_socket );
232             return VLC_EGENERIC;
233         }
234 #endif
235     }
236
237     if( ( i_socket == -1) &&
238         ( psz_host = config_GetPsz( p_intf, "rtci-host" ) ) != NULL )
239     {
240         vlc_url_t url;
241
242         vlc_UrlParse( &url, psz_host, 0 );
243
244         msg_Dbg( p_intf, "base %s port %d", url.psz_host, url.i_port );
245
246         if( (i_socket = net_ListenTCP(p_this, url.psz_host, url.i_port)) == -1)
247         {
248             msg_Warn( p_intf, "can't listen to %s port %i",
249                       url.psz_host, url.i_port );
250             vlc_UrlClean( &url );
251             free( psz_host );
252             return VLC_EGENERIC;
253         }
254
255         vlc_UrlClean( &url );
256         free( psz_host );
257     }
258
259     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
260     if( !p_intf->p_sys )
261     {
262         msg_Err( p_intf, "no memory" );
263         return VLC_ENOMEM;
264     }
265
266     p_intf->p_sys->i_socket_listen = i_socket;
267     p_intf->p_sys->i_socket = -1;
268     p_intf->p_sys->psz_unix_path = psz_unix_path;
269
270     /* Non-buffered stdout */
271     setvbuf( stdout, (char *)NULL, _IOLBF, 0 );
272
273     p_intf->pf_run = Run;
274
275 #ifdef WIN32
276     if( !config_GetInt( p_intf, "rtci-quiet" ) ) { CONSOLE_INTRO_MSG; }
277 #else
278     CONSOLE_INTRO_MSG;
279 #endif
280
281     msg_rtci( _("Real time control interface initialized, `h' for help\n") );
282     return VLC_SUCCESS;
283 }
284
285 /*****************************************************************************
286  * Deactivate: uninitialize and cleanup
287  *****************************************************************************/
288 static void Deactivate( vlc_object_t *p_this )
289 {
290     intf_thread_t *p_intf = (intf_thread_t*)p_this;
291
292     if( p_intf->p_sys->i_socket_listen != -1 )
293         net_Close( p_intf->p_sys->i_socket_listen );
294     if( p_intf->p_sys->i_socket != -1 )
295         net_Close( p_intf->p_sys->i_socket );
296     if( p_intf->p_sys->psz_unix_path != NULL )
297     {
298 #ifdef PF_LOCAL
299         unlink( p_intf->p_sys->psz_unix_path );
300 #endif
301         free( p_intf->p_sys->psz_unix_path );
302     }
303     free( p_intf->p_sys );
304 }
305
306 /*****************************************************************************
307  * Run: rtci thread
308  *****************************************************************************
309  * This part of the interface is in a separate thread so that we can call
310  * exec() from within it without annoying the rest of the program.
311  *****************************************************************************/
312 static void Run( intf_thread_t *p_intf )
313 {
314     input_thread_t * p_input;
315     playlist_t *     p_playlist;
316
317     char       p_buffer[ MAX_LINE_LENGTH + 1 ];
318     vlc_bool_t b_showpos = config_GetInt( p_intf, "rtci-show-pos" );
319
320     int        i_size = 0;
321     int        i_oldpos = 0;
322     int        i_newpos;
323
324     p_buffer[0] = 0;
325     p_input = NULL;
326     p_playlist = NULL;
327  
328     p_intf->p_sys->b_extend = config_GetInt( p_intf, "rtci-extend" );
329     /* Register commands that will be cleaned up upon object destruction */
330     var_Create( p_intf, "quit", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
331     var_AddCallback( p_intf, "quit", Quit, NULL );
332     var_Create( p_intf, "intf", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
333     var_AddCallback( p_intf, "intf", Intf, NULL );
334
335     var_Create( p_intf, "add", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
336     var_AddCallback( p_intf, "add", Playlist, NULL );
337     var_Create( p_intf, "playlist", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
338     var_AddCallback( p_intf, "playlist", Playlist, NULL );
339     var_Create( p_intf, "play", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
340     var_AddCallback( p_intf, "play", Playlist, NULL );
341     var_Create( p_intf, "stop", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
342     var_AddCallback( p_intf, "stop", Playlist, NULL );
343     var_Create( p_intf, "prev", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
344     var_AddCallback( p_intf, "prev", Playlist, NULL );
345     var_Create( p_intf, "next", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
346     var_AddCallback( p_intf, "next", Playlist, NULL );
347   
348     var_Create( p_intf, "marq-marquee", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
349     var_AddCallback( p_intf, "marq-marquee", Other, NULL );
350     var_Create( p_intf, "marq-x", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
351     var_AddCallback( p_intf, "marq-x", Other, NULL );
352     var_Create( p_intf, "marq-y", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
353     var_AddCallback( p_intf, "marq-y", Other, NULL );
354     var_Create( p_intf, "marq-timeout", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
355     var_AddCallback( p_intf, "marq-timeout", Other, NULL );
356
357     var_Create( p_intf, "pause", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
358     var_AddCallback( p_intf, "pause", Input, NULL );
359     var_Create( p_intf, "seek", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
360     var_AddCallback( p_intf, "seek", Input, NULL );
361     var_Create( p_intf, "title", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
362     var_AddCallback( p_intf, "title", Input, NULL );
363     var_Create( p_intf, "title_n", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
364     var_AddCallback( p_intf, "title_n", Input, NULL );
365     var_Create( p_intf, "title_p", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
366     var_AddCallback( p_intf, "title_p", Input, NULL );
367     var_Create( p_intf, "chapter", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
368     var_AddCallback( p_intf, "chapter", Input, NULL );
369     var_Create( p_intf, "chapter_n", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
370     var_AddCallback( p_intf, "chapter_n", Input, NULL );
371     var_Create( p_intf, "chapter_p", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
372     var_AddCallback( p_intf, "chapter_p", Input, NULL );
373
374     var_Create( p_intf, "volume", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
375     var_AddCallback( p_intf, "volume", Volume, NULL );
376     var_Create( p_intf, "volup", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
377     var_AddCallback( p_intf, "volup", VolumeMove, NULL );
378     var_Create( p_intf, "voldown", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
379     var_AddCallback( p_intf, "voldown", VolumeMove, NULL );
380     var_Create( p_intf, "adev", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
381     var_AddCallback( p_intf, "adev", AudioConfig, NULL );
382     var_Create( p_intf, "achan", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
383     var_AddCallback( p_intf, "achan", AudioConfig, NULL );
384
385 #ifdef WIN32
386     /* Get the file descriptor of the console input */
387     p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
388     if( p_intf->p_sys->hConsoleIn == INVALID_HANDLE_VALUE )
389     {
390         msg_Err( p_intf, "Couldn't open STD_INPUT_HANDLE" );
391         p_intf->b_die = VLC_TRUE;
392     }
393 #endif
394
395     while( !p_intf->b_die )
396     {
397         char *psz_cmd, *psz_arg;
398         vlc_bool_t b_complete;
399
400         if( p_intf->p_sys->i_socket_listen != - 1 &&
401             p_intf->p_sys->i_socket == -1 )
402         {
403             p_intf->p_sys->i_socket =
404                 net_Accept( p_intf, p_intf->p_sys->i_socket_listen, 0 );
405         }
406
407         b_complete = ReadCommand( p_intf, p_buffer, &i_size );
408
409         /* Manage the input part */
410         if( p_input == NULL )
411         {
412             if( p_playlist )
413             {
414                 p_input = vlc_object_find( p_playlist, VLC_OBJECT_INPUT,
415                                                        FIND_CHILD );
416             }
417             else
418             {
419                 p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
420                                                    FIND_ANYWHERE );
421                 if( p_input )
422                 {
423                     p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
424                                                            FIND_PARENT );
425                 }
426             }
427         }
428         else if( p_input->b_dead )
429         {
430             vlc_object_release( p_input );
431             p_input = NULL;
432         }
433
434         if( p_input && b_showpos )
435         {
436             i_newpos = 100 * var_GetFloat( p_input, "position" );
437             if( i_oldpos != i_newpos )
438             {
439                 i_oldpos = i_newpos;
440                 msg_rtci( "pos: %d%%\n", i_newpos );
441             }
442         }
443
444         /* Is there something to do? */
445         if( !b_complete ) continue;
446
447
448         /* Skip heading spaces */
449         psz_cmd = p_buffer;
450         while( *psz_cmd == ' ' )
451         {
452             psz_cmd++;
453         }
454
455         /* Split psz_cmd at the first space and make sure that
456          * psz_arg is valid */
457         psz_arg = strchr( psz_cmd, ' ' );
458         if( psz_arg )
459         {
460             *psz_arg++ = 0;
461             while( *psz_arg == ' ' )
462             {
463                 psz_arg++;
464             }
465         }
466         else
467         {
468             psz_arg = "";
469         }
470
471         /* If the user typed a registered local command, try it */
472         if( var_Type( p_intf, psz_cmd ) & VLC_VAR_ISCOMMAND )
473         {
474             vlc_value_t val;
475             int i_ret;
476
477             val.psz_string = psz_arg;
478             i_ret = var_Set( p_intf, psz_cmd, val );
479             msg_rtci( "%s: returned %i (%s)\n",
480                     psz_cmd, i_ret, vlc_error( i_ret ) );
481         }
482         /* Or maybe it's a global command */
483         else if( var_Type( p_intf->p_libvlc, psz_cmd ) & VLC_VAR_ISCOMMAND )
484         {
485             vlc_value_t val;
486             int i_ret;
487
488             val.psz_string = psz_arg;
489             /* FIXME: it's a global command, but we should pass the
490              * local object as an argument, not p_intf->p_libvlc. */
491             i_ret = var_Set( p_intf->p_libvlc, psz_cmd, val );
492             if( i_ret != 0 )
493             {
494                 msg_rtci( "%s: returned %i (%s)\n",
495                          psz_cmd, i_ret, vlc_error( i_ret ) );
496             }
497         }
498         else if( !strcmp( psz_cmd, "logout" ) )
499         {
500             /* Close connection */
501             if( p_intf->p_sys->i_socket != -1 )
502             {
503                 net_Close( p_intf->p_sys->i_socket );
504             }
505             p_intf->p_sys->i_socket = -1;
506         }
507         else if( !strcmp( psz_cmd, "info" ) )
508         {
509             if( p_input )
510             {
511                 int i, j;
512                 vlc_mutex_lock( &p_input->input.p_item->lock );
513                 for ( i = 0; i < p_input->input.p_item->i_categories; i++ )
514                 {
515                     info_category_t *p_category =
516                         p_input->input.p_item->pp_categories[i];
517
518                     msg_rtci( "+----[ %s ]\n", p_category->psz_name );
519                     msg_rtci( "| \n" );
520                     for ( j = 0; j < p_category->i_infos; j++ )
521                     {
522                         info_t *p_info = p_category->pp_infos[j];
523                         msg_rtci( "| %s: %s\n", p_info->psz_name,
524                                 p_info->psz_value );
525                     }
526                     msg_rtci( "| \n" );
527                 }
528                 msg_rtci( "+----[ end of stream info ]\n" );
529                 vlc_mutex_unlock( &p_input->input.p_item->lock );
530             }
531             else
532             {
533                 msg_rtci( "no input\n" );
534             }
535         }
536         else if( !strcmp( psz_cmd, "is_playing" ) )
537         {
538             if( ! p_input )
539             {
540                 msg_rtci( "0\n" );
541             }
542             else
543             {
544                 msg_rtci( "1\n" );
545             }
546         }
547         else if( !strcmp( psz_cmd, "get_time" ) )
548         {
549             if( ! p_input )
550             {
551                 msg_rtci("0\n");
552             }
553             else
554             {
555                 vlc_value_t time;
556                 var_Get( p_input, "time", &time );
557                 msg_rtci( "%i\n", time.i_time / 1000000);
558             }
559         }
560         else if( !strcmp( psz_cmd, "get_length" ) )
561         {
562             if( ! p_input )
563             {
564                 msg_rtci("0\n");
565             }
566             else
567             {
568                 vlc_value_t time;
569                 var_Get( p_input, "length", &time );
570                 msg_rtci( "%i\n", time.i_time / 1000000);
571             }
572         }
573         else if( !strcmp( psz_cmd, "get_title" ) )
574         {
575             if( ! p_input )
576             {
577                 msg_rtci("\n");
578             }
579             else
580             {
581                 msg_rtci( "%s\n", p_input->input.p_item->psz_name );
582             }
583         }
584         else switch( psz_cmd[0] )
585         {
586         case 'f':
587         case 'F':
588             if( p_input )
589             {
590                 vout_thread_t *p_vout;
591                 p_vout = vlc_object_find( p_input,
592                                           VLC_OBJECT_VOUT, FIND_CHILD );
593
594                 if( p_vout )
595                 {
596                     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
597                     vlc_object_release( p_vout );
598                 }
599             }
600             break;
601
602         case 's':
603         case 'S':
604             ;
605             break;
606
607         case '?':
608         case 'h':
609         case 'H':
610             msg_rtci(_("+----[ Remote control commands ]\n"));
611             msg_rtci("| \n");
612             msg_rtci(_("| add XYZ  . . . . . . . . . . add XYZ to playlist\n"));
613             msg_rtci(_("| playlist . . .  show items currently in playlist\n"));
614             msg_rtci(_("| play . . . . . . . . . . . . . . . . play stream\n"));
615             msg_rtci(_("| stop . . . . . . . . . . . . . . . . stop stream\n"));
616             msg_rtci(_("| next . . . . . . . . . . . .  next playlist item\n"));
617             msg_rtci(_("| prev . . . . . . . . . .  previous playlist item\n"));
618             msg_rtci(_("| title [X]  . . . . set/get title in current item\n"));
619             msg_rtci(_("| title_n  . . . . . .  next title in current item\n"));
620             msg_rtci(_("| title_p  . . . .  previous title in current item\n"));
621             msg_rtci(_("| chapter [X]  . . set/get chapter in current item\n"));
622             msg_rtci(_("| chapter_n  . . . .  next chapter in current item\n"));
623             msg_rtci(_("| chapter_p  . .  previous chapter in current item\n"));
624             msg_rtci("| \n");
625             msg_rtci(_("| seek X . seek in seconds, for instance `seek 12'\n"));
626             msg_rtci(_("| pause  . . . . . . . . . . . . . .  toggle pause\n"));
627             msg_rtci(_("| f  . . . . . . . . . . . . . . toggle fullscreen\n"));
628             msg_rtci(_("| info . . .  information about the current stream\n"));
629             msg_rtci("| \n");
630             msg_rtci(_("| volume [X] . . . . . . . .  set/get audio volume\n"));
631             msg_rtci(_("| volup [X]  . . . . .  raise audio volume X steps\n"));
632             msg_rtci(_("| voldown [X]  . . . .  lower audio volume X steps\n"));
633             msg_rtci(_("| adev [X] . . . . . . . . .  set/get audio device\n"));
634             msg_rtci(_("| achan [X]. . . . . . . .  set/get audio channels\n"));
635             msg_rtci("| \n");
636             if (p_intf->p_sys->b_extend)
637             {
638                    msg_rtci(_("| marq-marquee STRING  . . overlay STRING in video\n"));
639                msg_rtci(_("| marq-x X . . . . . .offset of marquee, from left\n"));
640                msg_rtci(_("| marq-y Y . . . . . . offset of marquee, from top\n"));
641                msg_rtci(_("| marq-timeout T. . . . .timeout of marquee, in ms\n"));
642                msg_rtci("| \n");
643             }    
644             msg_rtci(_("| help . . . . . . . . . . . . . this help message\n"));
645             msg_rtci(_("| logout . . . . . .exit (if in socket connection)\n"));
646             msg_rtci(_("| quit . . . . . . . . . . . . . . . . .  quit vlc\n"));
647             msg_rtci("| \n");
648             msg_rtci(_("+----[ end of help ]\n"));
649             break;
650
651         case '\0':
652             /* Ignore empty lines */
653             break;
654
655         default:
656             msg_rtci(_("unknown command `%s', type `help' for help\n"), psz_cmd);
657             break;
658         }
659
660         /* Command processed */
661         i_size = 0; p_buffer[0] = 0;
662     }
663
664     if( p_input )
665     {
666         vlc_object_release( p_input );
667         p_input = NULL;
668     }
669
670     if( p_playlist )
671     {
672         vlc_object_release( p_playlist );
673         p_playlist = NULL;
674     }
675 }
676
677 static int Input( vlc_object_t *p_this, char const *psz_cmd,
678                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
679 {
680     intf_thread_t *p_intf = (intf_thread_t*)p_this;
681     input_thread_t *p_input;
682     vlc_value_t     val;
683
684     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
685     if( !p_input ) return VLC_ENOOBJ;
686
687     /* Parse commands that only require an input */
688     if( !strcmp( psz_cmd, "pause" ) )
689     {
690         val.i_int = PAUSE_S;
691
692         var_Set( p_input, "state", val );
693         vlc_object_release( p_input );
694         return VLC_SUCCESS;
695     }
696     else if( !strcmp( psz_cmd, "seek" ) )
697     {
698         if( strlen( newval.psz_string ) > 0 &&
699             newval.psz_string[strlen( newval.psz_string ) - 1] == '%' )
700         {
701             val.f_float = (float)atoi( newval.psz_string ) / 100.0;
702             var_Set( p_input, "position", val );
703         }
704         else
705         {
706             val.i_time = ((int64_t)atoi( newval.psz_string )) * 1000000;
707             var_Set( p_input, "time", val );
708         }
709         vlc_object_release( p_input );
710         return VLC_SUCCESS;
711     }
712    
713     else if( !strcmp( psz_cmd, "chapter" ) ||
714              !strcmp( psz_cmd, "chapter_n" ) ||
715              !strcmp( psz_cmd, "chapter_p" ) )
716     {
717         if( !strcmp( psz_cmd, "chapter" ) )
718         {
719             if ( *newval.psz_string )
720             {
721                 /* Set. */
722                 val.i_int = atoi( newval.psz_string );
723                 var_Set( p_input, "chapter", val );
724             }
725             else
726             {
727                 vlc_value_t val_list;
728
729                 /* Get. */
730                 var_Get( p_input, "chapter", &val );
731                 var_Change( p_input, "chapter", VLC_VAR_GETCHOICES,
732                             &val_list, NULL );
733                 msg_rtci( "Currently playing chapter %d/%d\n",
734                         val.i_int, val_list.p_list->i_count );
735                 var_Change( p_this, "chapter", VLC_VAR_FREELIST,
736                             &val_list, NULL );
737             }
738         }
739         else if( !strcmp( psz_cmd, "chapter_n" ) )
740         {
741             val.b_bool = VLC_TRUE;
742             var_Set( p_input, "next-chapter", val );
743         }
744         else if( !strcmp( psz_cmd, "chapter_p" ) )
745         {
746             val.b_bool = VLC_TRUE;
747             var_Set( p_input, "prev-chapter", val );
748         }
749
750         vlc_object_release( p_input );
751         return VLC_SUCCESS;
752     }
753     else if( !strcmp( psz_cmd, "title" ) ||
754              !strcmp( psz_cmd, "title_n" ) ||
755              !strcmp( psz_cmd, "title_p" ) )
756     {
757         if( !strcmp( psz_cmd, "title" ) )
758         {
759             if ( *newval.psz_string )
760             {
761                 /* Set. */
762                 val.i_int = atoi( newval.psz_string );
763                 var_Set( p_input, "title", val );
764             }
765             else
766             {
767                 vlc_value_t val_list;
768
769                 /* Get. */
770                 var_Get( p_input, "title", &val );
771                 var_Change( p_input, "title", VLC_VAR_GETCHOICES,
772                             &val_list, NULL );
773                 msg_rtci( "Currently playing title %d/%d\n",
774                         val.i_int, val_list.p_list->i_count );
775                 var_Change( p_this, "title", VLC_VAR_FREELIST,
776                             &val_list, NULL );
777             }
778         }
779         else if( !strcmp( psz_cmd, "title_n" ) )
780         {
781             val.b_bool = VLC_TRUE;
782             var_Set( p_input, "next-title", val );
783         }
784         else if( !strcmp( psz_cmd, "title_p" ) )
785         {
786             val.b_bool = VLC_TRUE;
787             var_Set( p_input, "prev-title", val );
788         }
789
790         vlc_object_release( p_input );
791         return VLC_SUCCESS;
792     }
793
794     /* Never reached. */
795     return VLC_EGENERIC;
796 }
797
798 static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
799                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
800 {
801     intf_thread_t *p_intf = (intf_thread_t*)p_this;
802     playlist_t *p_playlist;
803
804     p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
805                                            FIND_ANYWHERE );
806     if( !p_playlist )
807     {
808         return VLC_ENOOBJ;
809     }
810
811     /* Parse commands that require a playlist */
812     if( !strcmp( psz_cmd, "prev" ) )
813     {
814         playlist_Prev( p_playlist );
815     }
816     else if( !strcmp( psz_cmd, "next" ) )
817     {
818         playlist_Next( p_playlist );
819     }
820     else if( !strcmp( psz_cmd, "play" ) )
821     {
822         playlist_Play( p_playlist );
823     }
824     else if( !strcmp( psz_cmd, "stop" ) )
825     {
826         playlist_Stop( p_playlist );
827     }
828     else if( !strcmp( psz_cmd, "add" ) )
829     {
830         msg_rtci( "trying to add %s to playlist\n", newval.psz_string );
831         playlist_Add( p_playlist, newval.psz_string, newval.psz_string,
832                       PLAYLIST_GO|PLAYLIST_APPEND, PLAYLIST_END );
833     }
834     else if( !strcmp( psz_cmd, "playlist" ) )
835     {
836         int i;
837         for ( i = 0; i < p_playlist->i_size; i++ )
838         {
839             msg_rtci( "|%s%s   %s|\n", i == p_playlist->i_index?"*":" ",
840                     p_playlist->pp_items[i]->input.psz_name,
841                     p_playlist->pp_items[i]->input.psz_uri );
842         }
843         if ( i == 0 )
844         {
845             msg_rtci( "| no entries\n" );
846         }
847     }
848  
849     /*
850      * sanity check
851      */
852     else
853     {
854         msg_rtci( "unknown command!\n" );
855     }
856
857     vlc_object_release( p_playlist );
858     return VLC_SUCCESS;
859 }
860
861 static int Other( vlc_object_t *p_this, char const *psz_cmd,
862                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
863 {
864     intf_thread_t *p_intf = (intf_thread_t*)p_this;
865     vlc_object_t *p_pl;
866     vlc_value_t     val;
867
868     p_pl = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
869                                            FIND_ANYWHERE );
870     if( !p_pl )
871     {
872         return VLC_ENOOBJ;
873     }
874
875     /* Parse miscellaneous commands */
876     if( !strcmp( psz_cmd, "marq-marquee" ) )
877     {
878         if( strlen( newval.psz_string ) > 0 )
879         {
880             val.psz_string = newval.psz_string;
881             var_Set( p_pl, "marq-marquee", val );
882         }
883         else 
884         {
885                 val.psz_string = "";
886                 var_Set( p_pl, "marq-marquee", val);
887         }
888     }
889     else if( !strcmp( psz_cmd, "marq-x" ) )
890     {
891         if( strlen( newval.psz_string ) > 0) 
892         {
893             val.i_int = atoi( newval.psz_string );
894             var_Set( p_pl, "marq-x", val );
895         }
896     }
897     else if( !strcmp( psz_cmd, "marq-y" ) )
898     {
899         if( strlen( newval.psz_string ) > 0) 
900         {
901             val.i_int = atoi( newval.psz_string );
902             var_Set( p_pl, "marq-y", val );
903         }
904     }
905     else if( !strcmp( psz_cmd, "marq-timeout" ) )
906     {
907         if( strlen( newval.psz_string ) > 0) 
908         {
909             val.i_int = atoi( newval.psz_string );
910             var_Set( p_pl, "marq-timeout", val );
911         }
912     }
913  
914     /*
915      * sanity check
916      */
917     else
918     {
919         msg_rtci( "unknown command!\n" );
920     }
921
922     vlc_object_release( p_pl );
923     return VLC_SUCCESS;
924 }
925
926 static int Quit( vlc_object_t *p_this, char const *psz_cmd,
927                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
928 {
929     p_this->p_vlc->b_die = VLC_TRUE;
930     return VLC_SUCCESS;
931 }
932
933 static int Intf( vlc_object_t *p_this, char const *psz_cmd,
934                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
935 {
936     intf_thread_t *p_newintf;
937
938     p_newintf = intf_Create( p_this->p_vlc, newval.psz_string );
939
940     if( p_newintf )
941     {
942         p_newintf->b_block = VLC_FALSE;
943         if( intf_RunThread( p_newintf ) )
944         {
945             vlc_object_detach( p_newintf );
946             intf_Destroy( p_newintf );
947         }
948     }
949
950     return VLC_SUCCESS;
951 }
952
953 static int Volume( vlc_object_t *p_this, char const *psz_cmd,
954                    vlc_value_t oldval, vlc_value_t newval, void *p_data )
955 {
956     intf_thread_t *p_intf = (intf_thread_t*)p_this;
957     int i_error;
958
959     if ( *newval.psz_string )
960     {
961         /* Set. */
962         audio_volume_t i_volume = atoi( newval.psz_string );
963         if ( i_volume > AOUT_VOLUME_MAX )
964         {
965             msg_rtci( "Volume must be in the range %d-%d\n", AOUT_VOLUME_MIN,
966                     AOUT_VOLUME_MAX );
967             i_error = VLC_EBADVAR;
968         }
969         else i_error = aout_VolumeSet( p_this, i_volume );
970     }
971     else
972     {
973         /* Get. */
974         audio_volume_t i_volume;
975         if ( aout_VolumeGet( p_this, &i_volume ) < 0 )
976         {
977             i_error = VLC_EGENERIC;
978         }
979         else
980         {
981             msg_rtci( "Volume is %d\n", i_volume );
982             i_error = VLC_SUCCESS;
983         }
984     }
985
986     return i_error;
987 }
988
989 static int VolumeMove( vlc_object_t *p_this, char const *psz_cmd,
990                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
991 {
992     intf_thread_t *p_intf = (intf_thread_t*)p_this;
993     audio_volume_t i_volume;
994     int i_nb_steps = atoi(newval.psz_string);
995     int i_error = VLC_SUCCESS;
996
997     if ( i_nb_steps <= 0 || i_nb_steps > (AOUT_VOLUME_MAX/AOUT_VOLUME_STEP) )
998     {
999         i_nb_steps = 1;
1000     }
1001
1002     if ( !strcmp(psz_cmd, "volup") )
1003     {
1004         if ( aout_VolumeUp( p_this, i_nb_steps, &i_volume ) < 0 )
1005             i_error = VLC_EGENERIC;
1006     }
1007     else
1008     {
1009         if ( aout_VolumeDown( p_this, i_nb_steps, &i_volume ) < 0 )
1010             i_error = VLC_EGENERIC;
1011     }
1012
1013     if ( !i_error ) msg_rtci( "Volume is %d\n", i_volume );
1014     return i_error;
1015 }
1016
1017 static int AudioConfig( vlc_object_t *p_this, char const *psz_cmd,
1018                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
1019 {
1020     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1021     aout_instance_t * p_aout;
1022     const char * psz_variable;
1023     vlc_value_t val_name;
1024     int i_error;
1025
1026     p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, FIND_ANYWHERE );
1027     if ( p_aout == NULL ) return VLC_ENOOBJ;
1028
1029     if ( !strcmp( psz_cmd, "adev" ) )
1030     {
1031         psz_variable = "audio-device";
1032     }
1033     else
1034     {
1035         psz_variable = "audio-channels";
1036     }
1037
1038     /* Get the descriptive name of the variable */
1039     var_Change( (vlc_object_t *)p_aout, psz_variable, VLC_VAR_GETTEXT,
1040                  &val_name, NULL );
1041     if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1042
1043     if ( !*newval.psz_string )
1044     {
1045         /* Retrieve all registered ***. */
1046         vlc_value_t val, text;
1047         int i, i_value;
1048
1049         if ( var_Get( (vlc_object_t *)p_aout, psz_variable, &val ) < 0 )
1050         {
1051             vlc_object_release( (vlc_object_t *)p_aout );
1052             return VLC_EGENERIC;
1053         }
1054         i_value = val.i_int;
1055
1056         if ( var_Change( (vlc_object_t *)p_aout, psz_variable,
1057                          VLC_VAR_GETLIST, &val, &text ) < 0 )
1058         {
1059             vlc_object_release( (vlc_object_t *)p_aout );
1060             return VLC_EGENERIC;
1061         }
1062
1063         msg_rtci( "+----[ %s ]\n", val_name.psz_string );
1064         for ( i = 0; i < val.p_list->i_count; i++ )
1065         {
1066             if ( i_value == val.p_list->p_values[i].i_int )
1067                 msg_rtci( "| %i - %s *\n", val.p_list->p_values[i].i_int,
1068                         text.p_list->p_values[i].psz_string );
1069             else
1070                 msg_rtci( "| %i - %s\n", val.p_list->p_values[i].i_int,
1071                         text.p_list->p_values[i].psz_string );
1072         }
1073         var_Change( (vlc_object_t *)p_aout, psz_variable, VLC_VAR_FREELIST,
1074                     &val, &text );
1075         msg_rtci( "+----[ end of %s ]\n", val_name.psz_string );
1076
1077         if( val_name.psz_string ) free( val_name.psz_string );
1078         i_error = VLC_SUCCESS;
1079     }
1080     else
1081     {
1082         vlc_value_t val;
1083         val.i_int = atoi( newval.psz_string );
1084
1085         i_error = var_Set( (vlc_object_t *)p_aout, psz_variable, val );
1086     }
1087     vlc_object_release( (vlc_object_t *)p_aout );
1088
1089     return i_error;
1090 }
1091
1092 #ifdef WIN32
1093 vlc_bool_t ReadWin32( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
1094 {
1095     INPUT_RECORD input_record;
1096     DWORD i_dw;
1097
1098     /* On Win32, select() only works on socket descriptors */
1099     while( WaitForSingleObject( p_intf->p_sys->hConsoleIn,
1100                                 INTF_IDLE_SLEEP/1000 ) == WAIT_OBJECT_0 )
1101     {
1102         while( !p_intf->b_die && *pi_size < MAX_LINE_LENGTH &&
1103                ReadConsoleInput( p_intf->p_sys->hConsoleIn, &input_record,
1104                                  1, &i_dw ) )
1105         {
1106             if( input_record.EventType != KEY_EVENT ||
1107                 !input_record.Event.KeyEvent.bKeyDown ||
1108                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
1109                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL||
1110                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_MENU ||
1111                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CAPITAL )
1112             {
1113                 /* nothing interesting */
1114                 continue;
1115             }
1116
1117             p_buffer[ *pi_size ] = input_record.Event.KeyEvent.uChar.AsciiChar;
1118
1119             /* Echo out the command */
1120             putc( p_buffer[ *pi_size ], stdout );
1121
1122             /* Handle special keys */
1123             if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1124             {
1125                 putc( '\n', stdout );
1126                 break;
1127             }
1128             switch( p_buffer[ *pi_size ] )
1129             {
1130             case '\b':
1131                 if( *pi_size )
1132                 {
1133                     *pi_size -= 2;
1134                     putc( ' ', stdout );
1135                     putc( '\b', stdout );
1136                 }
1137                 break;
1138             case '\r':
1139                 (*pi_size) --;
1140                 break;
1141             }
1142
1143             (*pi_size)++;
1144         }
1145
1146         if( *pi_size == MAX_LINE_LENGTH ||
1147             p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1148         {
1149             p_buffer[ *pi_size ] = 0;
1150             return VLC_TRUE;
1151         }
1152     }
1153
1154     return VLC_FALSE;
1155 }
1156 #endif
1157
1158 vlc_bool_t ReadCommand( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
1159 {
1160     int i_read = 0;
1161
1162 #ifdef WIN32
1163     if( p_intf->p_sys->i_socket == -1 )
1164         return ReadWin32( p_intf, p_buffer, pi_size );
1165 #endif
1166
1167     while( !p_intf->b_die && *pi_size < MAX_LINE_LENGTH &&
1168            (i_read = net_ReadNonBlock( p_intf, p_intf->p_sys->i_socket == -1 ?
1169                        STDIN_FILENO : p_intf->p_sys->i_socket,
1170                        p_buffer + *pi_size, 1, INTF_IDLE_SLEEP ) ) > 0 )
1171     {
1172         if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1173             break;
1174
1175         (*pi_size)++;
1176     }
1177
1178     /* Connection closed */
1179     if( i_read == -1 )
1180     {
1181         p_intf->p_sys->i_socket = -1;
1182         p_buffer[ *pi_size ] = 0;
1183         return VLC_TRUE;
1184     }
1185
1186     if( *pi_size == MAX_LINE_LENGTH ||
1187         p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1188     {
1189         p_buffer[ *pi_size ] = 0;
1190         return VLC_TRUE;
1191     }
1192
1193     return VLC_FALSE;
1194 }