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