]> git.sesse.net Git - vlc/blob - modules/control/rc.c
1e8c282100af1aa0d0ae6572eeed1d6fc4488605
[vlc] / modules / control / rc.c
1 /*****************************************************************************
2  * rc.c : remote control stdin/stdout module for vlc
3  *****************************************************************************
4  * Copyright (C) 2004 - 2005 VideoLAN
5  * $Id$
6  *
7  * Author: Peter Surda <shurdeek@panorama.sth.ac.at>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <string.h>
29
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <signal.h>
34
35 #include <vlc/vlc.h>
36 #include <vlc/intf.h>
37 #include <vlc/aout.h>
38 #include <vlc/vout.h>
39
40 #ifdef HAVE_UNISTD_H
41 #    include <unistd.h>
42 #endif
43
44 #ifdef HAVE_SYS_TIME_H
45 #    include <sys/time.h>
46 #endif
47 #include <sys/types.h>
48
49 #include "vlc_error.h"
50 #include "network.h"
51
52 #if defined(PF_UNIX) && !defined(PF_LOCAL)
53 #    define PF_LOCAL PF_UNIX
54 #endif
55 #if defined(AF_UNIX) && !defined(AF_LOCAL)
56 #    define AF_LOCAL AF_UNIX
57 #endif
58
59 #ifdef PF_LOCAL
60 #    include <sys/un.h>
61 #endif
62
63 #define MAX_LINE_LENGTH 256
64
65 /*****************************************************************************
66  * Local prototypes
67  *****************************************************************************/
68 static int  Activate     ( vlc_object_t * );
69 static void Deactivate   ( vlc_object_t * );
70 static void Run          ( intf_thread_t * );
71
72 static vlc_bool_t ReadCommand( intf_thread_t *, char *, int * );
73
74 static playlist_item_t *parse_MRL( intf_thread_t *, char * );
75
76 static int  Input        ( vlc_object_t *, char const *,
77                            vlc_value_t, vlc_value_t, void * );
78 static int  Playlist     ( vlc_object_t *, char const *,
79                            vlc_value_t, vlc_value_t, void * );
80 static int  Other        ( vlc_object_t *, char const *,
81                            vlc_value_t, vlc_value_t, void * );
82 static int  Quit         ( vlc_object_t *, char const *,
83                            vlc_value_t, vlc_value_t, void * );
84 static int  Intf         ( vlc_object_t *, char const *,
85                            vlc_value_t, vlc_value_t, void * );
86 static int  Volume       ( vlc_object_t *, char const *,
87                            vlc_value_t, vlc_value_t, void * );
88 static int  VolumeMove   ( vlc_object_t *, char const *,
89                            vlc_value_t, vlc_value_t, void * );
90 static int  AudioConfig  ( vlc_object_t *, char const *,
91                            vlc_value_t, vlc_value_t, void * );
92
93 struct intf_sys_t
94 {
95     int i_socket_listen;
96     int i_socket;
97     char *psz_unix_path;
98     vlc_bool_t b_extend;
99
100 #ifdef WIN32
101     HANDLE hConsoleIn;
102     vlc_bool_t b_quiet;
103 #endif
104 };
105
106 #ifdef HAVE_VARIADIC_MACROS
107 #   define msg_rc( psz_format, args... ) \
108       __msg_rc( p_intf, psz_format, ## args )
109 #endif
110
111 void __msg_rc( intf_thread_t *p_intf, const char *psz_fmt, ... )
112 {
113     va_list args;
114     va_start( args, psz_fmt );
115     if( p_intf->p_sys->i_socket == -1 ) vprintf( psz_fmt, args );
116     else
117     { net_vaPrintf( p_intf, p_intf->p_sys->i_socket, NULL, psz_fmt, args );
118       net_Printf( VLC_OBJECT(p_intf), p_intf->p_sys->i_socket, NULL, "\r" ); }
119     va_end( args );
120 }
121
122 /*****************************************************************************
123  * Module descriptor
124  *****************************************************************************/
125 #define POS_TEXT N_("Show stream position")
126 #define POS_LONGTEXT N_("Show the current position in seconds within the " \
127                         "stream from time to time." )
128
129 #define TTY_TEXT N_("Fake TTY")
130 #define TTY_LONGTEXT N_("Force the rc module to use stdin as if it was a TTY.")
131
132 #define UNIX_TEXT N_("UNIX socket command input")
133 #define UNIX_LONGTEXT N_("Accept commands over a Unix socket rather than " \
134                          "stdin." )
135
136 #define HOST_TEXT N_("TCP command input")
137 #define HOST_LONGTEXT N_("Accept commands over a socket rather than stdin. " \
138             "You can set the address and port the interface will bind to." )
139 #define EXTEND_TEXT N_("Extended help")
140 #define EXTEND_LONGTEXT N_("List additional commands.")
141
142 #ifdef WIN32
143 #define QUIET_TEXT N_("Do not open a DOS command box interface")
144 #define QUIET_LONGTEXT N_( \
145     "By default the rc interface plugin will start a DOS command box. " \
146     "Enabling the quiet mode will not bring this command box but can also " \
147     "be pretty annoying when you want to stop VLC and no video window is " \
148     "open." )
149 #endif
150
151 vlc_module_begin();
152     set_category( CAT_INTERFACE );
153     set_subcategory( SUBCAT_INTERFACE_GENERAL );
154     set_description( _("Remote control interface") );
155     add_bool( "rc-show-pos", 0, NULL, POS_TEXT, POS_LONGTEXT, VLC_TRUE );
156 #ifdef HAVE_ISATTY
157     add_bool( "rc-fake-tty", 0, NULL, TTY_TEXT, TTY_LONGTEXT, VLC_TRUE );
158 #endif
159     add_string( "rc-unix", 0, NULL, UNIX_TEXT, UNIX_LONGTEXT, VLC_TRUE );
160     add_string( "rc-host", 0, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE );
161
162 #ifdef WIN32
163     add_bool( "rc-quiet", 0, NULL, QUIET_TEXT, QUIET_LONGTEXT, VLC_FALSE );
164 #endif
165     add_bool( "rc-extend", 0, NULL, EXTEND_TEXT, EXTEND_LONGTEXT, VLC_FALSE );
166
167     set_capability( "interface", 20 );
168     set_callbacks( Activate, Deactivate );
169 vlc_module_end();
170
171 /*****************************************************************************
172  * Activate: initialize and create stuff
173  *****************************************************************************/
174 static int Activate( vlc_object_t *p_this )
175 {
176     intf_thread_t *p_intf = (intf_thread_t*)p_this;
177     playlist_t *p_playlist;
178     char *psz_host, *psz_unix_path;
179     int i_socket = -1;
180
181 #if defined(HAVE_ISATTY) && !defined(WIN32)
182     /* Check that stdin is a TTY */
183     if( !config_GetInt( p_intf, "rc-fake-tty" ) && !isatty( 0 ) )
184     {
185         msg_Warn( p_intf, "fd 0 is not a TTY" );
186         return VLC_EGENERIC;
187     }
188 #endif
189
190     psz_unix_path = config_GetPsz( p_intf, "rc-unix" );
191     if( psz_unix_path )
192     {
193 #ifndef PF_LOCAL
194         msg_Warn( p_intf, "your OS doesn't support filesystem sockets" );
195         free( psz_unix_path );
196         return VLC_EGENERIC;
197 #else
198         struct sockaddr_un addr;
199         int i_ret;
200
201         memset( &addr, 0, sizeof(struct sockaddr_un) );
202
203         msg_Dbg( p_intf, "trying UNIX socket" );
204
205         if( (i_socket = socket( PF_LOCAL, SOCK_STREAM, 0 ) ) < 0 )
206         {
207             msg_Warn( p_intf, "can't open socket: %s", strerror(errno) );
208             free( psz_unix_path );
209             return VLC_EGENERIC;
210         }
211
212         addr.sun_family = AF_LOCAL;
213         strncpy( addr.sun_path, psz_unix_path, sizeof( addr.sun_path ) );
214         addr.sun_path[sizeof( addr.sun_path ) - 1] = '\0';
215
216         if( (i_ret = bind( i_socket, (struct sockaddr*)&addr,
217                            sizeof(struct sockaddr_un) ) ) < 0 )
218         {
219             msg_Warn( p_intf, "couldn't bind socket to address: %s",
220                       strerror(errno) );
221             free( psz_unix_path );
222             net_Close( i_socket );
223             return VLC_EGENERIC;
224         }
225
226         if( ( i_ret = listen( i_socket, 1 ) ) < 0 )
227         {
228             msg_Warn( p_intf, "can't listen on socket: %s", strerror(errno));
229             free( psz_unix_path );
230             net_Close( i_socket );
231             return VLC_EGENERIC;
232         }
233 #endif
234     }
235
236     if( ( i_socket == -1) &&
237         ( psz_host = config_GetPsz( p_intf, "rc-host" ) ) != NULL )
238     {
239         vlc_url_t url;
240
241         vlc_UrlParse( &url, psz_host, 0 );
242
243         msg_Dbg( p_intf, "base %s port %d", url.psz_host, url.i_port );
244
245         if( (i_socket = net_ListenTCP(p_this, url.psz_host, url.i_port)) == -1)
246         {
247             msg_Warn( p_intf, "can't listen to %s port %i",
248                       url.psz_host, url.i_port );
249             vlc_UrlClean( &url );
250             free( psz_host );
251             return VLC_EGENERIC;
252         }
253
254         vlc_UrlClean( &url );
255         free( psz_host );
256     }
257
258     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
259     if( !p_intf->p_sys )
260     {
261         msg_Err( p_intf, "no memory" );
262         return VLC_ENOMEM;
263     }
264
265     p_intf->p_sys->i_socket_listen = i_socket;
266     p_intf->p_sys->i_socket = -1;
267     p_intf->p_sys->psz_unix_path = psz_unix_path;
268
269     /* Non-buffered stdout */
270     setvbuf( stdout, (char *)NULL, _IOLBF, 0 );
271
272     p_intf->pf_run = Run;
273
274 #ifdef WIN32
275     p_intf->p_sys->b_quiet = config_GetInt( p_intf, "rc-quiet" );
276     if( !p_intf->p_sys->b_quiet ) { CONSOLE_INTRO_MSG; }
277 #else
278     CONSOLE_INTRO_MSG;
279 #endif
280
281     /* Force "no-view" mode */
282     p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
283                                                  FIND_ANYWHERE );
284     if( p_playlist )
285     {
286         vlc_mutex_lock( &p_playlist->object_lock );
287         p_playlist->status.i_view = -1;
288         vlc_mutex_unlock( &p_playlist->object_lock );
289         vlc_object_release( p_playlist );
290     }
291
292     msg_rc( _("Remote control interface initialized, `h' for help\n") );
293     return VLC_SUCCESS;
294 }
295
296 /*****************************************************************************
297  * Deactivate: uninitialize and cleanup
298  *****************************************************************************/
299 static void Deactivate( vlc_object_t *p_this )
300 {
301     intf_thread_t *p_intf = (intf_thread_t*)p_this;
302
303     if( p_intf->p_sys->i_socket_listen != -1 )
304         net_Close( p_intf->p_sys->i_socket_listen );
305     if( p_intf->p_sys->i_socket != -1 )
306         net_Close( p_intf->p_sys->i_socket );
307     if( p_intf->p_sys->psz_unix_path != NULL )
308     {
309 #ifdef PF_LOCAL
310         unlink( p_intf->p_sys->psz_unix_path );
311 #endif
312         free( p_intf->p_sys->psz_unix_path );
313     }
314     free( p_intf->p_sys );
315 }
316
317 /*****************************************************************************
318  * Run: rc thread
319  *****************************************************************************
320  * This part of the interface is in a separate thread so that we can call
321  * exec() from within it without annoying the rest of the program.
322  *****************************************************************************/
323 static void Run( intf_thread_t *p_intf )
324 {
325     input_thread_t * p_input;
326     playlist_t *     p_playlist;
327
328     char       p_buffer[ MAX_LINE_LENGTH + 1 ];
329     vlc_bool_t b_showpos = config_GetInt( p_intf, "rc-show-pos" );
330
331     int        i_size = 0;
332     int        i_oldpos = 0;
333     int        i_newpos;
334
335     p_buffer[0] = 0;
336     p_input = NULL;
337     p_playlist = NULL;
338  
339     p_intf->p_sys->b_extend = config_GetInt( p_intf, "rc-extend" );
340     /* Register commands that will be cleaned up upon object destruction */
341     var_Create( p_intf, "quit", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
342     var_AddCallback( p_intf, "quit", Quit, NULL );
343     var_Create( p_intf, "intf", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
344     var_AddCallback( p_intf, "intf", Intf, NULL );
345
346     var_Create( p_intf, "add", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
347     var_AddCallback( p_intf, "add", Playlist, NULL );
348     var_Create( p_intf, "playlist", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
349     var_AddCallback( p_intf, "playlist", Playlist, NULL );
350     var_Create( p_intf, "play", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
351     var_AddCallback( p_intf, "play", Playlist, NULL );
352     var_Create( p_intf, "stop", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
353     var_AddCallback( p_intf, "stop", Playlist, NULL );
354     var_Create( p_intf, "prev", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
355     var_AddCallback( p_intf, "prev", Playlist, NULL );
356     var_Create( p_intf, "next", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
357     var_AddCallback( p_intf, "next", Playlist, NULL );
358     var_Create( p_intf, "goto", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
359     var_AddCallback( p_intf, "goto", Playlist, NULL );
360  
361     /* marquee on the fly items */
362     var_Create( p_intf, "marq-marquee", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
363     var_AddCallback( p_intf, "marq-marquee", Other, NULL );
364     var_Create( p_intf, "marq-x", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
365     var_AddCallback( p_intf, "marq-x", Other, NULL );
366     var_Create( p_intf, "marq-y", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
367     var_AddCallback( p_intf, "marq-y", Other, NULL );
368     var_Create( p_intf, "marq-position", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
369     var_AddCallback( p_intf, "marq-position", Other, NULL );
370     var_Create( p_intf, "marq-color", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
371     var_AddCallback( p_intf, "marq-color", Other, NULL );
372     var_Create( p_intf, "marq-opacity", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
373     var_AddCallback( p_intf, "marq-opacity", Other, NULL );
374     var_Create( p_intf, "marq-timeout", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
375     var_AddCallback( p_intf, "marq-timeout", Other, NULL );
376
377     var_Create( p_intf, "mosaic-alpha", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
378     var_AddCallback( p_intf, "mosaic-alpha", Other, NULL );
379     var_Create( p_intf, "mosaic-height", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
380     var_AddCallback( p_intf, "mosaic-height", Other, NULL );
381     var_Create( p_intf, "mosaic-width", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
382     var_AddCallback( p_intf, "mosaic-width", Other, NULL );
383     var_Create( p_intf, "mosaic-xoffset", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
384     var_AddCallback( p_intf, "mosaic-xoffset", Other, NULL );
385     var_Create( p_intf, "mosaic-yoffset", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
386     var_AddCallback( p_intf, "mosaic-yoffset", Other, NULL );
387     var_Create( p_intf, "mosaic-vborder", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
388     var_AddCallback( p_intf, "mosaic-vborder", Other, NULL );
389     var_Create( p_intf, "mosaic-hborder", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
390     var_AddCallback( p_intf, "mosaic-hborder", Other, NULL );
391     var_Create( p_intf, "mosaic-position",
392                      VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
393     var_AddCallback( p_intf, "mosaic-position", Other, NULL );
394     var_Create( p_intf, "mosaic-rows", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
395     var_AddCallback( p_intf, "mosaic-rows", Other, NULL );
396     var_Create( p_intf, "mosaic-cols", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
397     var_AddCallback( p_intf, "mosaic-cols", Other, NULL );
398     var_Create( p_intf, "mosaic-keep-aspect-ratio",
399                      VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
400     var_AddCallback( p_intf, "mosaic-keep-aspect-ratio", Other, NULL );
401
402     /* time on the fly items */
403     var_Create( p_intf, "time-format", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
404     var_AddCallback( p_intf, "time-format", Other, NULL );
405     var_Create( p_intf, "time-x", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
406     var_AddCallback( p_intf, "time-x", Other, NULL );
407     var_Create( p_intf, "time-y", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
408     var_AddCallback( p_intf, "time-y", Other, NULL );
409     var_Create( p_intf, "time-position", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
410     var_AddCallback( p_intf, "time-position", Other, NULL );
411     var_Create( p_intf, "time-color", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
412     var_AddCallback( p_intf, "time-color", Other, NULL );
413     var_Create( p_intf, "time-opacity", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
414     var_AddCallback( p_intf, "time-opacity", Other, NULL );
415     
416     var_Create( p_intf, "pause", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
417     var_AddCallback( p_intf, "pause", Input, NULL );
418     var_Create( p_intf, "seek", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
419     var_AddCallback( p_intf, "seek", Input, NULL );
420     var_Create( p_intf, "title", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
421     var_AddCallback( p_intf, "title", Input, NULL );
422     var_Create( p_intf, "title_n", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
423     var_AddCallback( p_intf, "title_n", Input, NULL );
424     var_Create( p_intf, "title_p", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
425     var_AddCallback( p_intf, "title_p", Input, NULL );
426     var_Create( p_intf, "chapter", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
427     var_AddCallback( p_intf, "chapter", Input, NULL );
428     var_Create( p_intf, "chapter_n", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
429     var_AddCallback( p_intf, "chapter_n", Input, NULL );
430     var_Create( p_intf, "chapter_p", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
431     var_AddCallback( p_intf, "chapter_p", Input, NULL );
432
433     var_Create( p_intf, "fastforward", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
434     var_AddCallback( p_intf, "fastforward", Input, NULL );
435     var_Create( p_intf, "rewind", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
436     var_AddCallback( p_intf, "rewind", Input, NULL );
437
438     var_Create( p_intf, "volume", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
439     var_AddCallback( p_intf, "volume", Volume, NULL );
440     var_Create( p_intf, "volup", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
441     var_AddCallback( p_intf, "volup", VolumeMove, NULL );
442     var_Create( p_intf, "voldown", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
443     var_AddCallback( p_intf, "voldown", VolumeMove, NULL );
444     var_Create( p_intf, "adev", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
445     var_AddCallback( p_intf, "adev", AudioConfig, NULL );
446     var_Create( p_intf, "achan", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
447     var_AddCallback( p_intf, "achan", AudioConfig, NULL );
448
449 #ifdef WIN32
450     /* Get the file descriptor of the console input */
451     p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
452     if( p_intf->p_sys->hConsoleIn == INVALID_HANDLE_VALUE )
453     {
454         msg_Err( p_intf, "Couldn't open STD_INPUT_HANDLE" );
455         p_intf->b_die = VLC_TRUE;
456     }
457 #endif
458
459     while( !p_intf->b_die )
460     {
461         char *psz_cmd, *psz_arg;
462         vlc_bool_t b_complete;
463
464         if( p_intf->p_sys->i_socket_listen != - 1 &&
465             p_intf->p_sys->i_socket == -1 )
466         {
467             p_intf->p_sys->i_socket =
468                 net_Accept( p_intf, p_intf->p_sys->i_socket_listen, 0 );
469         }
470
471         b_complete = ReadCommand( p_intf, p_buffer, &i_size );
472
473         /* Manage the input part */
474         if( p_input == NULL )
475         {
476             if( p_playlist )
477             {
478                 p_input = vlc_object_find( p_playlist, VLC_OBJECT_INPUT,
479                                                        FIND_CHILD );
480             }
481             else
482             {
483                 p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
484                                                    FIND_ANYWHERE );
485                 if( p_input )
486                 {
487                     p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
488                                                            FIND_PARENT );
489                 }
490             }
491         }
492         else if( p_input->b_dead )
493         {
494             vlc_object_release( p_input );
495             p_input = NULL;
496         }
497
498         if( p_input && b_showpos )
499         {
500             i_newpos = 100 * var_GetFloat( p_input, "position" );
501             if( i_oldpos != i_newpos )
502             {
503                 i_oldpos = i_newpos;
504                 msg_rc( "pos: %d%%\n", i_newpos );
505             }
506         }
507
508         /* Is there something to do? */
509         if( !b_complete ) continue;
510
511
512         /* Skip heading spaces */
513         psz_cmd = p_buffer;
514         while( *psz_cmd == ' ' )
515         {
516             psz_cmd++;
517         }
518
519         /* Split psz_cmd at the first space and make sure that
520          * psz_arg is valid */
521         psz_arg = strchr( psz_cmd, ' ' );
522         if( psz_arg )
523         {
524             *psz_arg++ = 0;
525             while( *psz_arg == ' ' )
526             {
527                 psz_arg++;
528             }
529         }
530         else
531         {
532             psz_arg = "";
533         }
534
535         /* If the user typed a registered local command, try it */
536         if( var_Type( p_intf, psz_cmd ) & VLC_VAR_ISCOMMAND )
537         {
538             vlc_value_t val;
539             int i_ret;
540
541             val.psz_string = psz_arg;
542             i_ret = var_Set( p_intf, psz_cmd, val );
543             msg_rc( "%s: returned %i (%s)\n",
544                     psz_cmd, i_ret, vlc_error( i_ret ) );
545         }
546         /* Or maybe it's a global command */
547         else if( var_Type( p_intf->p_libvlc, psz_cmd ) & VLC_VAR_ISCOMMAND )
548         {
549             vlc_value_t val;
550             int i_ret;
551
552             val.psz_string = psz_arg;
553             /* FIXME: it's a global command, but we should pass the
554              * local object as an argument, not p_intf->p_libvlc. */
555             i_ret = var_Set( p_intf->p_libvlc, psz_cmd, val );
556             if( i_ret != 0 )
557             {
558                 msg_rc( "%s: returned %i (%s)\n",
559                          psz_cmd, i_ret, vlc_error( i_ret ) );
560             }
561         }
562         else if( !strcmp( psz_cmd, "logout" ) )
563         {
564             /* Close connection */
565             if( p_intf->p_sys->i_socket != -1 )
566             {
567                 net_Close( p_intf->p_sys->i_socket );
568             }
569             p_intf->p_sys->i_socket = -1;
570         }
571         else if( !strcmp( psz_cmd, "info" ) )
572         {
573             if( p_input )
574             {
575                 int i, j;
576                 vlc_mutex_lock( &p_input->input.p_item->lock );
577                 for ( i = 0; i < p_input->input.p_item->i_categories; i++ )
578                 {
579                     info_category_t *p_category =
580                         p_input->input.p_item->pp_categories[i];
581
582                     msg_rc( "+----[ %s ]\n", p_category->psz_name );
583                     msg_rc( "| \n" );
584                     for ( j = 0; j < p_category->i_infos; j++ )
585                     {
586                         info_t *p_info = p_category->pp_infos[j];
587                         msg_rc( "| %s: %s\n", p_info->psz_name,
588                                 p_info->psz_value );
589                     }
590                     msg_rc( "| \n" );
591                 }
592                 msg_rc( "+----[ end of stream info ]\n" );
593                 vlc_mutex_unlock( &p_input->input.p_item->lock );
594             }
595             else
596             {
597                 msg_rc( "no input\n" );
598             }
599         }
600         else if( !strcmp( psz_cmd, "is_playing" ) )
601         {
602             if( ! p_input )
603             {
604                 msg_rc( "0\n" );
605             }
606             else
607             {
608                 msg_rc( "1\n" );
609             }
610         }
611         else if( !strcmp( psz_cmd, "get_time" ) )
612         {
613             if( ! p_input )
614             {
615                 msg_rc("0\n");
616             }
617             else
618             {
619                 vlc_value_t time;
620                 var_Get( p_input, "time", &time );
621                 msg_rc( "%i\n", time.i_time / 1000000);
622             }
623         }
624         else if( !strcmp( psz_cmd, "get_length" ) )
625         {
626             if( ! p_input )
627             {
628                 msg_rc("0\n");
629             }
630             else
631             {
632                 vlc_value_t time;
633                 var_Get( p_input, "length", &time );
634                 msg_rc( "%i\n", time.i_time / 1000000);
635             }
636         }
637         else if( !strcmp( psz_cmd, "get_title" ) )
638         {
639             if( ! p_input )
640             {
641                 msg_rc("\n");
642             }
643             else
644             {
645                 msg_rc( "%s\n", p_input->input.p_item->psz_name );
646             }
647         }
648         else switch( psz_cmd[0] )
649         {
650         case 'f':
651         case 'F':
652             if( p_input )
653             {
654                 vout_thread_t *p_vout;
655                 p_vout = vlc_object_find( p_input,
656                                           VLC_OBJECT_VOUT, FIND_CHILD );
657
658                 if( p_vout )
659                 {
660                     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
661                     vlc_object_release( p_vout );
662                 }
663             }
664             break;
665
666         case 's':
667         case 'S':
668             ;
669             break;
670
671         case '?':
672         case 'h':
673         case 'H':
674             msg_rc(_("+----[ Remote control commands ]\n"));
675             msg_rc(  "| \n");
676             msg_rc(_("| add XYZ  . . . . . . . . . . add XYZ to playlist\n"));
677             msg_rc(_("| playlist . . .  show items currently in playlist\n"));
678             msg_rc(_("| play . . . . . . . . . . . . . . . . play stream\n"));
679             msg_rc(_("| stop . . . . . . . . . . . . . . . . stop stream\n"));
680             msg_rc(_("| next . . . . . . . . . . . .  next playlist item\n"));
681             msg_rc(_("| prev . . . . . . . . . .  previous playlist item\n"));
682             msg_rc(_("| goto . . . . . . . . . . . .  goto item at index\n"));
683             msg_rc(_("| title [X]  . . . . set/get title in current item\n"));
684             msg_rc(_("| title_n  . . . . . .  next title in current item\n"));
685             msg_rc(_("| title_p  . . . .  previous title in current item\n"));
686             msg_rc(_("| chapter [X]  . . set/get chapter in current item\n"));
687             msg_rc(_("| chapter_n  . . . .  next chapter in current item\n"));
688             msg_rc(_("| chapter_p  . .  previous chapter in current item\n"));
689             msg_rc(  "| \n");
690             msg_rc(_("| seek X . seek in seconds, for instance `seek 12'\n"));
691             msg_rc(_("| pause  . . . . . . . . . . . . . .  toggle pause\n"));
692             msg_rc(_("| fastforward  . . . . . .  .  set to maximum rate\n"));
693             msg_rc(_("| rewind  . . . . . . . . . .  set to minimum rate\n"));
694             msg_rc(_("| f  . . . . . . . . . . . . . . toggle fullscreen\n"));
695             msg_rc(_("| info . . .  information about the current stream\n"));
696             msg_rc(  "| \n");
697             msg_rc(_("| volume [X] . . . . . . . .  set/get audio volume\n"));
698             msg_rc(_("| volup [X]  . . . . .  raise audio volume X steps\n"));
699             msg_rc(_("| voldown [X]  . . . .  lower audio volume X steps\n"));
700             msg_rc(_("| adev [X] . . . . . . . . .  set/get audio device\n"));
701             msg_rc(_("| achan [X]. . . . . . . .  set/get audio channels\n"));
702             msg_rc(  "| \n");
703             
704             if (p_intf->p_sys->b_extend)
705             {
706                 msg_rc(_("| marq-marquee STRING  . . overlay STRING in video\n"));
707                 msg_rc(_("| marq-x X . . . . .  offset of marquee, from left\n"));
708                 msg_rc(_("| marq-y Y . . . . . . offset of marquee, from top\n"));
709                 msg_rc(_("| marq-position #. . .  .relative position control\n"));
710                 msg_rc(_("| marq-color # . . . .  font color of marquee, RGB\n"));
711                 msg_rc(_("| marq-opacity # . . . . . . . .opacity of marquee\n"));
712                 msg_rc(_("| marq-timeout T. . . .  timeout of marquee, in ms\n"));
713                 msg_rc(  "| \n");
714                 msg_rc(_("| time-format STRING . . . overlay STRING in video\n"));
715                 msg_rc(_("| time-x X . . . . .offset of timestamp, from left\n"));
716                 msg_rc(_("| time-y Y . . . . . offset of timestamp, from top\n"));
717                 msg_rc(_("| time-position #. . .  .relative position control\n"));
718                 msg_rc(_("| time-color # . . .  font color of timestamp, RGB\n"));
719                 msg_rc(_("| time-opacity # . . . . . . .opacity of timestamp\n"));
720                 msg_rc(  "| \n");
721             }    
722             msg_rc(_("| help . . . . . . . . . . . . . this help message\n"));
723             msg_rc(_("| logout . . . . .  exit (if in socket connection)\n"));
724             msg_rc(_("| quit . . . . . . . . . . . . . . . . .  quit vlc\n"));
725             msg_rc(  "| \n");
726             msg_rc(_("+----[ end of help ]\n"));
727             break;
728
729         case '\0':
730             /* Ignore empty lines */
731             break;
732
733         default:
734             msg_rc(_("unknown command `%s', type `help' for help\n"), psz_cmd);
735             break;
736         }
737
738         /* Command processed */
739         i_size = 0; p_buffer[0] = 0;
740     }
741
742     if( p_input )
743     {
744         vlc_object_release( p_input );
745         p_input = NULL;
746     }
747
748     if( p_playlist )
749     {
750         vlc_object_release( p_playlist );
751         p_playlist = NULL;
752     }
753 }
754
755 static int Input( vlc_object_t *p_this, char const *psz_cmd,
756                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
757 {
758     intf_thread_t *p_intf = (intf_thread_t*)p_this;
759     input_thread_t *p_input;
760     vlc_value_t     val;
761
762     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
763     if( !p_input ) return VLC_ENOOBJ;
764
765     /* Parse commands that only require an input */
766     if( !strcmp( psz_cmd, "pause" ) )
767     {
768         val.i_int = PAUSE_S;
769
770         var_Set( p_input, "state", val );
771         vlc_object_release( p_input );
772         return VLC_SUCCESS;
773     }
774     else if( !strcmp( psz_cmd, "seek" ) )
775     {
776         if( strlen( newval.psz_string ) > 0 &&
777             newval.psz_string[strlen( newval.psz_string ) - 1] == '%' )
778         {
779             val.f_float = (float)atoi( newval.psz_string ) / 100.0;
780             var_Set( p_input, "position", val );
781         }
782         else
783         {
784             val.i_time = ((int64_t)atoi( newval.psz_string )) * 1000000;
785             var_Set( p_input, "time", val );
786         }
787         vlc_object_release( p_input );
788         return VLC_SUCCESS;
789     }
790     else if ( !strcmp( psz_cmd, "fastforward" ) )
791     {
792         val.i_int = INPUT_RATE_MAX;
793
794         var_Set( p_input, "rate", val );
795         vlc_object_release( p_input );
796         return VLC_SUCCESS;
797     }
798     else if ( !strcmp( psz_cmd, "rewind" ) )
799     {
800         val.i_int = INPUT_RATE_MIN;
801
802         var_Set( p_input, "rate", val );
803         vlc_object_release( p_input );
804         return VLC_SUCCESS;
805     }
806     else if( !strcmp( psz_cmd, "chapter" ) ||
807              !strcmp( psz_cmd, "chapter_n" ) ||
808              !strcmp( psz_cmd, "chapter_p" ) )
809     {
810         if( !strcmp( psz_cmd, "chapter" ) )
811         {
812             if ( *newval.psz_string )
813             {
814                 /* Set. */
815                 val.i_int = atoi( newval.psz_string );
816                 var_Set( p_input, "chapter", val );
817             }
818             else
819             {
820                 vlc_value_t val_list;
821
822                 /* Get. */
823                 var_Get( p_input, "chapter", &val );
824                 var_Change( p_input, "chapter", VLC_VAR_GETCHOICES,
825                             &val_list, NULL );
826                 msg_rc( "Currently playing chapter %d/%d\n",
827                         val.i_int, val_list.p_list->i_count );
828                 var_Change( p_this, "chapter", VLC_VAR_FREELIST,
829                             &val_list, NULL );
830             }
831         }
832         else if( !strcmp( psz_cmd, "chapter_n" ) )
833         {
834             val.b_bool = VLC_TRUE;
835             var_Set( p_input, "next-chapter", val );
836         }
837         else if( !strcmp( psz_cmd, "chapter_p" ) )
838         {
839             val.b_bool = VLC_TRUE;
840             var_Set( p_input, "prev-chapter", val );
841         }
842
843         vlc_object_release( p_input );
844         return VLC_SUCCESS;
845     }
846     else if( !strcmp( psz_cmd, "title" ) ||
847              !strcmp( psz_cmd, "title_n" ) ||
848              !strcmp( psz_cmd, "title_p" ) )
849     {
850         if( !strcmp( psz_cmd, "title" ) )
851         {
852             if ( *newval.psz_string )
853             {
854                 /* Set. */
855                 val.i_int = atoi( newval.psz_string );
856                 var_Set( p_input, "title", val );
857             }
858             else
859             {
860                 vlc_value_t val_list;
861
862                 /* Get. */
863                 var_Get( p_input, "title", &val );
864                 var_Change( p_input, "title", VLC_VAR_GETCHOICES,
865                             &val_list, NULL );
866                 msg_rc( "Currently playing title %d/%d\n",
867                         val.i_int, val_list.p_list->i_count );
868                 var_Change( p_this, "title", VLC_VAR_FREELIST,
869                             &val_list, NULL );
870             }
871         }
872         else if( !strcmp( psz_cmd, "title_n" ) )
873         {
874             val.b_bool = VLC_TRUE;
875             var_Set( p_input, "next-title", val );
876         }
877         else if( !strcmp( psz_cmd, "title_p" ) )
878         {
879             val.b_bool = VLC_TRUE;
880             var_Set( p_input, "prev-title", val );
881         }
882
883         vlc_object_release( p_input );
884         return VLC_SUCCESS;
885     }
886
887     /* Never reached. */
888     return VLC_EGENERIC;
889 }
890
891 static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
892                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
893 {
894     vlc_value_t val;
895     intf_thread_t *p_intf = (intf_thread_t*)p_this;
896     playlist_t *p_playlist;
897
898     p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
899                                            FIND_ANYWHERE );
900     if( !p_playlist )
901     {
902         return VLC_ENOOBJ;
903     }
904
905     /* Parse commands that require a playlist */
906     if( !strcmp( psz_cmd, "prev" ) )
907     {
908         playlist_Prev( p_playlist );
909     }
910     else if( !strcmp( psz_cmd, "next" ) )
911     {
912         playlist_Next( p_playlist );
913     }
914     else if( !strcmp( psz_cmd, "play" ) )
915     {
916         playlist_Play( p_playlist );
917     }
918     else if (!strcmp( psz_cmd, "goto" ) )
919     {
920         if( strlen( newval.psz_string ) > 0) 
921         {
922             val.i_int = atoi( newval.psz_string );
923             playlist_Goto( p_playlist, val.i_int); 
924         }
925     }
926     else if( !strcmp( psz_cmd, "stop" ) )
927     {
928         playlist_Stop( p_playlist );
929     }
930     else if( !strcmp( psz_cmd, "add" ) &&
931              newval.psz_string && *newval.psz_string )
932     {
933         playlist_item_t *p_item = parse_MRL( p_intf, newval.psz_string );
934
935         if( p_item )
936         {
937             msg_rc( "trying to add %s to playlist\n", newval.psz_string );
938             playlist_AddItem( p_playlist, p_item,
939                               PLAYLIST_GO|PLAYLIST_APPEND, PLAYLIST_END );
940         }
941     }
942     else if( !strcmp( psz_cmd, "playlist" ) )
943     {
944         int i;
945         for ( i = 0; i < p_playlist->i_size; i++ )
946         {
947             msg_rc( "|%s%s   %s|%s|\n", i == p_playlist->i_index ? "*" : " ",
948                     p_playlist->pp_items[i]->input.psz_name,
949                     p_playlist->pp_items[i]->input.psz_uri,
950                     p_playlist->pp_items[i]->i_parents > 0 ?
951                     p_playlist->pp_items[i]->pp_parents[0]->p_parent->input.psz_name : "" );
952         }
953         if ( i == 0 )
954         {
955             msg_rc( "| no entries\n" );
956         }
957     }
958  
959     /*
960      * sanity check
961      */
962     else
963     {
964         msg_rc( "unknown command!\n" );
965     }
966
967     vlc_object_release( p_playlist );
968     return VLC_SUCCESS;
969 }
970
971 static int Other( vlc_object_t *p_this, char const *psz_cmd,
972                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
973 {
974     intf_thread_t *p_intf = (intf_thread_t*)p_this;
975     vlc_object_t *p_pl;
976     vlc_value_t     val;
977     vlc_object_t *p_inp;
978
979     p_pl = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
980                                            FIND_ANYWHERE );
981     if( !p_pl )
982     {
983         return VLC_ENOOBJ;
984     }
985     
986     p_inp = vlc_object_find( p_this, VLC_OBJECT_INPUT,
987                                            FIND_ANYWHERE );
988     if( !p_inp )
989     {
990         return VLC_ENOOBJ;
991     }
992
993     /* Parse miscellaneous commands */
994     if( !strcmp( psz_cmd, "marq-marquee" ) )
995     {
996         if( strlen( newval.psz_string ) > 0 )
997         {
998             val.psz_string = newval.psz_string;
999             var_Set( p_inp->p_libvlc, "marq-marquee", val );
1000         }
1001         else 
1002         {
1003                 val.psz_string = "";
1004                 var_Set( p_inp->p_libvlc, "marq-marquee", val);
1005         }
1006     }
1007     else if( !strcmp( psz_cmd, "marq-x" ) )
1008     {
1009         if( strlen( newval.psz_string ) > 0) 
1010         {
1011             val.i_int = atoi( newval.psz_string );
1012             var_Set( p_inp->p_libvlc, "marq-x", val );
1013         }
1014     }
1015     else if( !strcmp( psz_cmd, "marq-y" ) )
1016     {
1017         if( strlen( newval.psz_string ) > 0) 
1018         {
1019             val.i_int = atoi( newval.psz_string );
1020             var_Set( p_inp->p_libvlc, "marq-y", val );
1021         }
1022     }
1023     else if( !strcmp( psz_cmd, "marq-position" ) )
1024     {
1025         if( strlen( newval.psz_string ) > 0) 
1026         {
1027             val.i_int = atoi( newval.psz_string );
1028             var_Set( p_inp->p_libvlc, "marq-position", val );
1029         }
1030     }
1031     else if( !strcmp( psz_cmd, "marq-color" ) )
1032     {
1033         if( strlen( newval.psz_string ) > 0) 
1034         {
1035             val.i_int = strtol( newval.psz_string, NULL, 0 );
1036             var_Set( p_inp->p_libvlc, "marq-color", val );
1037         }
1038     }
1039     else if( !strcmp( psz_cmd, "marq-opacity" ) )
1040     {
1041         if( strlen( newval.psz_string ) > 0) 
1042         {
1043             val.i_int = strtol( newval.psz_string, NULL, 0 );
1044             var_Set( p_inp->p_libvlc, "marq-opacity", val );
1045         }
1046     }
1047     else if( !strcmp( psz_cmd, "marq-timeout" ) )
1048     {
1049         if( strlen( newval.psz_string ) > 0) 
1050         {
1051             val.i_int = atoi( newval.psz_string );
1052             var_Set( p_inp, "marq-timeout", val );
1053         }
1054     }
1055     else if( !strcmp( psz_cmd, "mosaic-alpha" ) )
1056     {
1057         if( strlen( newval.psz_string) > 0)
1058         {
1059             val.i_int = atoi( newval.psz_string );
1060             var_Set( p_inp->p_libvlc, "mosaic-alpha", val );
1061         }
1062     }
1063     else if( !strcmp( psz_cmd, "mosaic-height" ) )
1064     {
1065         if( strlen( newval.psz_string) > 0)
1066         {
1067             val.i_int = atoi( newval.psz_string );
1068             var_Set( p_inp->p_libvlc, "mosaic-height", val );
1069         }
1070     }
1071     else if( !strcmp( psz_cmd, "mosaic-width" ) )
1072     {
1073         if( strlen( newval.psz_string) > 0)
1074         {
1075             val.i_int = atoi( newval.psz_string );
1076             var_Set( p_inp->p_libvlc, "mosaic-width", val );
1077         }
1078     }
1079     else if( !strcmp( psz_cmd, "mosaic-xoffset" ) )
1080     {
1081         if( strlen( newval.psz_string) > 0)
1082         {
1083             val.i_int = atoi( newval.psz_string );
1084             var_Set( p_inp->p_libvlc, "mosaic-xoffset", val );
1085         }
1086     }
1087     else if( !strcmp( psz_cmd, "mosaic-yoffset" ) )
1088     {
1089         if( strlen( newval.psz_string) > 0)
1090         {
1091             val.i_int = atoi( newval.psz_string );
1092             var_Set( p_inp->p_libvlc, "mosaic-yoffset", val );
1093         }
1094     }
1095     else if( !strcmp( psz_cmd, "mosaic-vborder" ) )
1096     {
1097         if( strlen( newval.psz_string) > 0)
1098         {
1099             val.i_int = atoi( newval.psz_string );
1100             var_Set( p_inp->p_libvlc, "mosaic-vborder", val );
1101         }
1102     }
1103     else if( !strcmp( psz_cmd, "mosaic-hborder" ) )
1104     {
1105         if( strlen( newval.psz_string) > 0)
1106         {
1107             val.i_int = atoi( newval.psz_string );
1108             var_Set( p_inp->p_libvlc, "mosaic-hborder", val );
1109         }
1110     }
1111     else if( !strcmp( psz_cmd, "mosaic-position" ) )
1112     {
1113         if( strlen( newval.psz_string) > 0)
1114         {
1115             val.i_int = atoi( newval.psz_string );
1116             var_Set( p_inp->p_libvlc, "mosaic-position", val );
1117         }
1118     }
1119     else if( !strcmp( psz_cmd, "mosaic-rows" ) )
1120     {
1121         if( strlen( newval.psz_string) > 0)
1122         {
1123             val.i_int = atoi( newval.psz_string );
1124             var_Set( p_inp->p_libvlc, "mosaic-rows", val );
1125         }
1126     }
1127     else if( !strcmp( psz_cmd, "mosaic-cols" ) )
1128     {
1129         if( strlen( newval.psz_string) > 0)
1130         {
1131             val.i_int = atoi( newval.psz_string );
1132             var_Set( p_inp->p_libvlc, "mosaic-cols", val );
1133         }
1134     }
1135     else if( !strcmp( psz_cmd, "mosaic-keep-aspect-ratio" ) )
1136     {
1137         if( strlen( newval.psz_string) > 0)
1138         {
1139             val.i_int = atoi( newval.psz_string );
1140             var_Set( p_inp->p_libvlc, "mosaic-keep-aspect-ratio", val );
1141         }
1142     }
1143     else if( !strcmp( psz_cmd, "time-format" ) )
1144     {
1145         if( strlen( newval.psz_string ) > 0 )
1146         {
1147             val.psz_string = newval.psz_string;
1148             var_Set( p_inp->p_libvlc, "time-format", val );
1149         }
1150         else 
1151         {
1152                 val.psz_string = "";
1153                 var_Set( p_inp->p_libvlc, "time-format", val);
1154         }
1155     }
1156     else if( !strcmp( psz_cmd, "time-x" ) )
1157     {
1158         if( strlen( newval.psz_string ) > 0) 
1159         {
1160             val.i_int = atoi( newval.psz_string );
1161             var_Set( p_inp->p_libvlc, "time-x", val );
1162         }
1163     }
1164     else if( !strcmp( psz_cmd, "time-y" ) )
1165     {
1166         if( strlen( newval.psz_string ) > 0) 
1167         {
1168             val.i_int = atoi( newval.psz_string );
1169             var_Set( p_inp->p_libvlc, "time-y", val );
1170         }
1171     }
1172     else if( !strcmp( psz_cmd, "time-position" ) )
1173     {
1174         if( strlen( newval.psz_string ) > 0) 
1175         {
1176             val.i_int = atoi( newval.psz_string );
1177             var_Set( p_inp->p_libvlc, "time-position", val );
1178         }
1179     }
1180     else if( !strcmp( psz_cmd, "time-color" ) )
1181     {
1182         if( strlen( newval.psz_string ) > 0) 
1183         {
1184             val.i_int = strtol( newval.psz_string, NULL, 0 );
1185             var_Set( p_inp->p_libvlc, "time-color", val );
1186         }
1187     }
1188     else if( !strcmp( psz_cmd, "time-opacity" ) )
1189     {
1190         if( strlen( newval.psz_string ) > 0) 
1191         {
1192             val.i_int = strtol( newval.psz_string, NULL, 0 );
1193             var_Set( p_inp->p_libvlc, "time-opacity", val );
1194         }
1195     }
1196  
1197     /*
1198      * sanity check
1199      */
1200     else
1201     {
1202         msg_rc( "unknown command!\n" );
1203     }
1204
1205     vlc_object_release( p_pl );
1206     vlc_object_release( p_inp );
1207     return VLC_SUCCESS;
1208 }
1209
1210 static int Quit( vlc_object_t *p_this, char const *psz_cmd,
1211                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1212 {
1213     p_this->p_vlc->b_die = VLC_TRUE;
1214     return VLC_SUCCESS;
1215 }
1216
1217 static int Intf( vlc_object_t *p_this, char const *psz_cmd,
1218                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1219 {
1220     intf_thread_t *p_newintf;
1221
1222     p_newintf = intf_Create( p_this->p_vlc, newval.psz_string );
1223
1224     if( p_newintf )
1225     {
1226         p_newintf->b_block = VLC_FALSE;
1227         if( intf_RunThread( p_newintf ) )
1228         {
1229             vlc_object_detach( p_newintf );
1230             intf_Destroy( p_newintf );
1231         }
1232     }
1233
1234     return VLC_SUCCESS;
1235 }
1236
1237 static int Volume( vlc_object_t *p_this, char const *psz_cmd,
1238                    vlc_value_t oldval, vlc_value_t newval, void *p_data )
1239 {
1240     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1241     int i_error;
1242
1243     if ( *newval.psz_string )
1244     {
1245         /* Set. */
1246         audio_volume_t i_volume = atoi( newval.psz_string );
1247         if ( i_volume > AOUT_VOLUME_MAX )
1248         {
1249             msg_rc( "Volume must be in the range %d-%d\n", AOUT_VOLUME_MIN,
1250                     AOUT_VOLUME_MAX );
1251             i_error = VLC_EBADVAR;
1252         }
1253         else i_error = aout_VolumeSet( p_this, i_volume );
1254     }
1255     else
1256     {
1257         /* Get. */
1258         audio_volume_t i_volume;
1259         if ( aout_VolumeGet( p_this, &i_volume ) < 0 )
1260         {
1261             i_error = VLC_EGENERIC;
1262         }
1263         else
1264         {
1265             msg_rc( "Volume is %d\n", i_volume );
1266             i_error = VLC_SUCCESS;
1267         }
1268     }
1269
1270     return i_error;
1271 }
1272
1273 static int VolumeMove( vlc_object_t *p_this, char const *psz_cmd,
1274                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1275 {
1276     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1277     audio_volume_t i_volume;
1278     int i_nb_steps = atoi(newval.psz_string);
1279     int i_error = VLC_SUCCESS;
1280
1281     if ( i_nb_steps <= 0 || i_nb_steps > (AOUT_VOLUME_MAX/AOUT_VOLUME_STEP) )
1282     {
1283         i_nb_steps = 1;
1284     }
1285
1286     if ( !strcmp(psz_cmd, "volup") )
1287     {
1288         if ( aout_VolumeUp( p_this, i_nb_steps, &i_volume ) < 0 )
1289             i_error = VLC_EGENERIC;
1290     }
1291     else
1292     {
1293         if ( aout_VolumeDown( p_this, i_nb_steps, &i_volume ) < 0 )
1294             i_error = VLC_EGENERIC;
1295     }
1296
1297     if ( !i_error ) msg_rc( "Volume is %d\n", i_volume );
1298     return i_error;
1299 }
1300
1301 static int AudioConfig( vlc_object_t *p_this, char const *psz_cmd,
1302                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
1303 {
1304     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1305     aout_instance_t * p_aout;
1306     const char * psz_variable;
1307     vlc_value_t val_name;
1308     int i_error;
1309
1310     p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, FIND_ANYWHERE );
1311     if ( p_aout == NULL ) return VLC_ENOOBJ;
1312
1313     if ( !strcmp( psz_cmd, "adev" ) )
1314     {
1315         psz_variable = "audio-device";
1316     }
1317     else
1318     {
1319         psz_variable = "audio-channels";
1320     }
1321
1322     /* Get the descriptive name of the variable */
1323     var_Change( (vlc_object_t *)p_aout, psz_variable, VLC_VAR_GETTEXT,
1324                  &val_name, NULL );
1325     if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1326
1327     if ( !*newval.psz_string )
1328     {
1329         /* Retrieve all registered ***. */
1330         vlc_value_t val, text;
1331         int i, i_value;
1332
1333         if ( var_Get( (vlc_object_t *)p_aout, psz_variable, &val ) < 0 )
1334         {
1335             vlc_object_release( (vlc_object_t *)p_aout );
1336             return VLC_EGENERIC;
1337         }
1338         i_value = val.i_int;
1339
1340         if ( var_Change( (vlc_object_t *)p_aout, psz_variable,
1341                          VLC_VAR_GETLIST, &val, &text ) < 0 )
1342         {
1343             vlc_object_release( (vlc_object_t *)p_aout );
1344             return VLC_EGENERIC;
1345         }
1346
1347         msg_rc( "+----[ %s ]\n", val_name.psz_string );
1348         for ( i = 0; i < val.p_list->i_count; i++ )
1349         {
1350             if ( i_value == val.p_list->p_values[i].i_int )
1351                 msg_rc( "| %i - %s *\n", val.p_list->p_values[i].i_int,
1352                         text.p_list->p_values[i].psz_string );
1353             else
1354                 msg_rc( "| %i - %s\n", val.p_list->p_values[i].i_int,
1355                         text.p_list->p_values[i].psz_string );
1356         }
1357         var_Change( (vlc_object_t *)p_aout, psz_variable, VLC_VAR_FREELIST,
1358                     &val, &text );
1359         msg_rc( "+----[ end of %s ]\n", val_name.psz_string );
1360
1361         if( val_name.psz_string ) free( val_name.psz_string );
1362         i_error = VLC_SUCCESS;
1363     }
1364     else
1365     {
1366         vlc_value_t val;
1367         val.i_int = atoi( newval.psz_string );
1368
1369         i_error = var_Set( (vlc_object_t *)p_aout, psz_variable, val );
1370     }
1371     vlc_object_release( (vlc_object_t *)p_aout );
1372
1373     return i_error;
1374 }
1375
1376 #ifdef WIN32
1377 vlc_bool_t ReadWin32( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
1378 {
1379     INPUT_RECORD input_record;
1380     DWORD i_dw;
1381
1382     /* On Win32, select() only works on socket descriptors */
1383     while( WaitForSingleObject( p_intf->p_sys->hConsoleIn,
1384                                 INTF_IDLE_SLEEP/1000 ) == WAIT_OBJECT_0 )
1385     {
1386         while( !p_intf->b_die && *pi_size < MAX_LINE_LENGTH &&
1387                ReadConsoleInput( p_intf->p_sys->hConsoleIn, &input_record,
1388                                  1, &i_dw ) )
1389         {
1390             if( input_record.EventType != KEY_EVENT ||
1391                 !input_record.Event.KeyEvent.bKeyDown ||
1392                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
1393                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL||
1394                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_MENU ||
1395                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CAPITAL )
1396             {
1397                 /* nothing interesting */
1398                 continue;
1399             }
1400
1401             p_buffer[ *pi_size ] = input_record.Event.KeyEvent.uChar.AsciiChar;
1402
1403             /* Echo out the command */
1404             putc( p_buffer[ *pi_size ], stdout );
1405
1406             /* Handle special keys */
1407             if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1408             {
1409                 putc( '\n', stdout );
1410                 break;
1411             }
1412             switch( p_buffer[ *pi_size ] )
1413             {
1414             case '\b':
1415                 if( *pi_size )
1416                 {
1417                     *pi_size -= 2;
1418                     putc( ' ', stdout );
1419                     putc( '\b', stdout );
1420                 }
1421                 break;
1422             case '\r':
1423                 (*pi_size) --;
1424                 break;
1425             }
1426
1427             (*pi_size)++;
1428         }
1429
1430         if( *pi_size == MAX_LINE_LENGTH ||
1431             p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1432         {
1433             p_buffer[ *pi_size ] = 0;
1434             return VLC_TRUE;
1435         }
1436     }
1437
1438     return VLC_FALSE;
1439 }
1440 #endif
1441
1442 vlc_bool_t ReadCommand( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
1443 {
1444     int i_read = 0;
1445
1446 #ifdef WIN32
1447     if( p_intf->p_sys->i_socket == -1 && !p_intf->p_sys->b_quiet )
1448         return ReadWin32( p_intf, p_buffer, pi_size );
1449     else if( p_intf->p_sys->i_socket == -1 )
1450     {
1451         msleep( INTF_IDLE_SLEEP );
1452         return VLC_FALSE;
1453     }
1454 #endif
1455
1456     while( !p_intf->b_die && *pi_size < MAX_LINE_LENGTH &&
1457            (i_read = net_ReadNonBlock( p_intf, p_intf->p_sys->i_socket == -1 ?
1458                        0 /*STDIN_FILENO*/ : p_intf->p_sys->i_socket, NULL,
1459                        p_buffer + *pi_size, 1, INTF_IDLE_SLEEP ) ) > 0 )
1460     {
1461         if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1462             break;
1463
1464         (*pi_size)++;
1465     }
1466
1467     /* Connection closed */
1468     if( i_read == -1 )
1469     {
1470         p_intf->p_sys->i_socket = -1;
1471         p_buffer[ *pi_size ] = 0;
1472         return VLC_TRUE;
1473     }
1474
1475     if( *pi_size == MAX_LINE_LENGTH ||
1476         p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1477     {
1478         p_buffer[ *pi_size ] = 0;
1479         return VLC_TRUE;
1480     }
1481
1482     return VLC_FALSE;
1483 }
1484
1485 /*****************************************************************************
1486  * parse_MRL: build a playlist item from a full mrl
1487  *****************************************************************************
1488  * MRL format: "simplified-mrl [:option-name[=option-value]]"
1489  * We don't check for '"' or '\'', we just assume that a ':' that follows a
1490  * space is a new option. Should be good enough for our purpose.
1491  *****************************************************************************/
1492 static playlist_item_t *parse_MRL( intf_thread_t *p_intf, char *psz_mrl )
1493 {
1494 #define SKIPSPACE( p ) { while( *p && ( *p == ' ' || *p == '\t' ) ) p++; }
1495 #define SKIPTRAILINGSPACE( p, d ) \
1496     { char *e=d; while( e > p && (*(e-1)==' ' || *(e-1)=='\t') ){e--;*e=0;} }
1497
1498     playlist_item_t *p_item = NULL;
1499     char *psz_item = NULL, *psz_item_mrl = NULL, *psz_orig;
1500     char **ppsz_options = NULL;
1501     int i, i_options = 0;
1502
1503     if( !psz_mrl ) return 0;
1504
1505     psz_mrl = psz_orig = strdup( psz_mrl );
1506     while( *psz_mrl )
1507     {
1508         SKIPSPACE( psz_mrl );
1509         psz_item = psz_mrl;
1510
1511         for( ; *psz_mrl; psz_mrl++ )
1512         {
1513             if( (*psz_mrl == ' ' || *psz_mrl == '\t') && psz_mrl[1] == ':' )
1514             {
1515                 /* We have a complete item */
1516                 break;
1517             }
1518             if( (*psz_mrl == ' ' || *psz_mrl == '\t') &&
1519                 (psz_mrl[1] == '"' || psz_mrl[1] == '\'') && psz_mrl[2] == ':')
1520             {
1521                 /* We have a complete item */
1522                 break;
1523             }
1524         }
1525
1526         if( *psz_mrl ) { *psz_mrl = 0; psz_mrl++; }
1527         SKIPTRAILINGSPACE( psz_item, psz_item + strlen( psz_item ) );
1528
1529         /* Remove '"' and '\'' if necessary */
1530         if( *psz_item == '"' && psz_item[strlen(psz_item)-1] == '"' )
1531         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
1532         if( *psz_item == '\'' && psz_item[strlen(psz_item)-1] == '\'' )
1533         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
1534
1535         if( !psz_item_mrl ) psz_item_mrl = psz_item;
1536         else if( *psz_item )
1537         {
1538             i_options++;
1539             ppsz_options = realloc( ppsz_options, i_options * sizeof(char *) );
1540             ppsz_options[i_options - 1] = &psz_item[1];
1541         }
1542
1543         if( *psz_mrl ) SKIPSPACE( psz_mrl );
1544     }
1545
1546     /* Now create a playlist item */
1547     if( psz_item_mrl )
1548     {
1549         p_item = playlist_ItemNew( p_intf, psz_item_mrl, psz_item_mrl );
1550         for( i = 0; i < i_options; i++ )
1551         {
1552             playlist_ItemAddOption( p_item, ppsz_options[i] );
1553         }
1554     }
1555
1556     if( i_options ) free( ppsz_options );
1557     free( psz_orig );
1558
1559     return p_item;
1560 }