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