- Append CRLF manually
[vlc.git] / 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  *         Jean-Paul Saman <jpsaman #_at_# m2x _replaceWith#dot_ nl>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30
31 #include <errno.h>                                                 /* ENOMEM */
32 #include <stdio.h>
33 #include <ctype.h>
34 #include <signal.h>
35
36 #include <vlc/vlc.h>
37 #include <vlc/intf.h>
38 #include <vlc/aout.h>
39 #include <vlc/vout.h>
40 #include <vlc_osd.h>
41
42 #ifdef HAVE_UNISTD_H
43 #    include <unistd.h>
44 #endif
45
46 #ifdef HAVE_SYS_TIME_H
47 #    include <sys/time.h>
48 #endif
49 #include <sys/types.h>
50
51 #include "vlc_error.h"
52 #include "network.h"
53
54 #if defined(AF_UNIX) && !defined(AF_LOCAL)
55 #    define AF_LOCAL AF_UNIX
56 #endif
57
58 #if defined(AF_LOCAL) && ! defined(WIN32)
59 #    include <sys/un.h>
60 #endif
61
62 #define MAX_LINE_LENGTH 256
63 #define STATUS_CHANGE "status change: "
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 void Help         ( intf_thread_t *, vlc_bool_t );
73
74 static vlc_bool_t ReadCommand( intf_thread_t *, char *, int * );
75
76 static playlist_item_t *parse_MRL( intf_thread_t *, char * );
77
78 static int  Input        ( vlc_object_t *, char const *,
79                            vlc_value_t, vlc_value_t, void * );
80 static int  Playlist     ( vlc_object_t *, char const *,
81                            vlc_value_t, vlc_value_t, void * );
82 static int  Other        ( vlc_object_t *, char const *,
83                            vlc_value_t, vlc_value_t, void * );
84 static int  Quit         ( vlc_object_t *, char const *,
85                            vlc_value_t, vlc_value_t, void * );
86 static int  Intf         ( vlc_object_t *, char const *,
87                            vlc_value_t, vlc_value_t, void * );
88 static int  Volume       ( vlc_object_t *, char const *,
89                            vlc_value_t, vlc_value_t, void * );
90 static int  VolumeMove   ( vlc_object_t *, char const *,
91                            vlc_value_t, vlc_value_t, void * );
92 static int  AudioConfig  ( vlc_object_t *, char const *,
93                            vlc_value_t, vlc_value_t, void * );
94 static int  Menu         ( vlc_object_t *, char const *,
95                            vlc_value_t, vlc_value_t, void * );
96
97 /* Status Callbacks */
98 static int TimeOffsetChanged( vlc_object_t *, char const *,
99                               vlc_value_t, vlc_value_t , void * );
100 static int VolumeChanged    ( vlc_object_t *, char const *,
101                               vlc_value_t, vlc_value_t, void * );
102 static int StateChanged     ( vlc_object_t *, char const *,
103                               vlc_value_t, vlc_value_t, void * );
104 static int RateChanged      ( vlc_object_t *, char const *,
105                               vlc_value_t, vlc_value_t, void * );
106
107 struct intf_sys_t
108 {
109     int *pi_socket_listen;
110     int i_socket;
111     char *psz_unix_path;
112
113     /* status changes */
114     vlc_mutex_t       status_lock;
115     playlist_status_t i_last_state;
116     
117 #ifdef WIN32
118     HANDLE hConsoleIn;
119     vlc_bool_t b_quiet;
120 #endif
121 };
122
123 #ifdef HAVE_VARIADIC_MACROS
124 #   define msg_rc( psz_format, args... ) \
125       __msg_rc( p_intf, psz_format, ## args )
126 #endif
127
128 void __msg_rc( intf_thread_t *p_intf, const char *psz_fmt, ... )
129 {
130     va_list args;
131     va_start( args, psz_fmt );
132     
133     if( p_intf->p_sys->i_socket == -1 )
134         vprintf( psz_fmt, args );
135     else
136     {
137         net_vaPrintf( p_intf, p_intf->p_sys->i_socket, NULL, psz_fmt, args );
138         net_Write( p_intf, p_intf->p_sys->i_socket, NULL, (uint8_t *)"\r\n",
139                    2 );
140     }
141     va_end( args );
142 }
143
144 /*****************************************************************************
145  * Module descriptor
146  *****************************************************************************/
147 #define POS_TEXT N_("Show stream position")
148 #define POS_LONGTEXT N_("Show the current position in seconds within the " \
149                         "stream from time to time." )
150
151 #define TTY_TEXT N_("Fake TTY")
152 #define TTY_LONGTEXT N_("Force the rc module to use stdin as if it was a TTY.")
153
154 #define UNIX_TEXT N_("UNIX socket command input")
155 #define UNIX_LONGTEXT N_("Accept commands over a Unix socket rather than " \
156                          "stdin." )
157
158 #define HOST_TEXT N_("TCP command input")
159 #define HOST_LONGTEXT N_("Accept commands over a socket rather than stdin. " \
160             "You can set the address and port the interface will bind to." )
161
162 #ifdef WIN32
163 #define QUIET_TEXT N_("Do not open a DOS command box interface")
164 #define QUIET_LONGTEXT N_( \
165     "By default the rc interface plugin will start a DOS command box. " \
166     "Enabling the quiet mode will not bring this command box but can also " \
167     "be pretty annoying when you want to stop VLC and no video window is " \
168     "open." )
169 #endif
170
171 vlc_module_begin();
172     set_shortname( _("RC"));
173     set_category( CAT_INTERFACE );
174     set_subcategory( SUBCAT_INTERFACE_GENERAL );
175     set_description( _("Remote control interface") );
176     add_bool( "rc-show-pos", 0, NULL, POS_TEXT, POS_LONGTEXT, VLC_TRUE );
177 #ifdef HAVE_ISATTY
178     add_bool( "rc-fake-tty", 0, NULL, TTY_TEXT, TTY_LONGTEXT, VLC_TRUE );
179 #endif
180     add_string( "rc-unix", 0, NULL, UNIX_TEXT, UNIX_LONGTEXT, VLC_TRUE );
181     add_string( "rc-host", 0, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE );
182
183 #ifdef WIN32
184     add_bool( "rc-quiet", 0, NULL, QUIET_TEXT, QUIET_LONGTEXT, VLC_FALSE );
185 #endif
186
187     set_capability( "interface", 20 );
188     set_callbacks( Activate, Deactivate );
189 vlc_module_end();
190
191 /*****************************************************************************
192  * Activate: initialize and create stuff
193  *****************************************************************************/
194 static int Activate( vlc_object_t *p_this )
195 {
196     intf_thread_t *p_intf = (intf_thread_t*)p_this;
197     playlist_t *p_playlist;
198     char *psz_host, *psz_unix_path;
199     int  *pi_socket = NULL;
200
201 #if defined(HAVE_ISATTY) && !defined(WIN32)
202     /* Check that stdin is a TTY */
203     if( !config_GetInt( p_intf, "rc-fake-tty" ) && !isatty( 0 ) )
204     {
205         msg_Warn( p_intf, "fd 0 is not a TTY" );
206         return VLC_EGENERIC;
207     }
208 #endif
209
210     psz_unix_path = config_GetPsz( p_intf, "rc-unix" );
211     if( psz_unix_path )
212     {
213         int i_socket;
214
215 #if !defined(AF_LOCAL) || defined(WIN32)
216         msg_Warn( p_intf, "your OS doesn't support filesystem sockets" );
217         free( psz_unix_path );
218         return VLC_EGENERIC;
219 #else
220         struct sockaddr_un addr;
221         int i_ret;
222
223         memset( &addr, 0, sizeof(struct sockaddr_un) );
224
225         msg_Dbg( p_intf, "trying UNIX socket" );
226
227         if( (i_socket = socket( AF_LOCAL, SOCK_STREAM, 0 ) ) < 0 )
228         {
229             msg_Warn( p_intf, "can't open socket: %s", strerror(errno) );
230             free( psz_unix_path );
231             return VLC_EGENERIC;
232         }
233
234         addr.sun_family = AF_LOCAL;
235         strncpy( addr.sun_path, psz_unix_path, sizeof( addr.sun_path ) );
236         addr.sun_path[sizeof( addr.sun_path ) - 1] = '\0';
237
238         if( (i_ret = bind( i_socket, (struct sockaddr*)&addr,
239                            sizeof(struct sockaddr_un) ) ) < 0 )
240         {
241             msg_Warn( p_intf, "couldn't bind socket to address: %s",
242                       strerror(errno) );
243             free( psz_unix_path );
244             net_Close( i_socket );
245             return VLC_EGENERIC;
246         }
247
248         if( ( i_ret = listen( i_socket, 1 ) ) < 0 )
249         {
250             msg_Warn( p_intf, "can't listen on socket: %s", strerror(errno));
251             free( psz_unix_path );
252             net_Close( i_socket );
253             return VLC_EGENERIC;
254         }
255
256         /* FIXME: we need a core function to merge listening sockets sets */
257         pi_socket = calloc( 2, sizeof( int ) );
258         if( pi_socket == NULL )
259         {
260             free( psz_unix_path );
261             net_Close( i_socket );
262             return VLC_ENOMEM;
263         }
264         pi_socket[0] = i_socket;
265         pi_socket[1] = -1;
266 #endif
267     }
268
269     if( ( pi_socket == NULL ) &&
270         ( psz_host = config_GetPsz( p_intf, "rc-host" ) ) != NULL )
271     {
272         vlc_url_t url;
273
274         vlc_UrlParse( &url, psz_host, 0 );
275
276         msg_Dbg( p_intf, "base %s port %d", url.psz_host, url.i_port );
277
278         pi_socket = net_ListenTCP(p_this, url.psz_host, url.i_port);
279         if( pi_socket == NULL )
280         {
281             msg_Warn( p_intf, "can't listen to %s port %i",
282                       url.psz_host, url.i_port );
283             vlc_UrlClean( &url );
284             free( psz_host );
285             return VLC_EGENERIC;
286         }
287
288         vlc_UrlClean( &url );
289         free( psz_host );
290     }
291
292     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
293     if( !p_intf->p_sys )
294     {
295         msg_Err( p_intf, "no memory" );
296         return VLC_ENOMEM;
297     }
298
299     p_intf->p_sys->pi_socket_listen = pi_socket;
300     p_intf->p_sys->i_socket = -1;
301     p_intf->p_sys->psz_unix_path = psz_unix_path;
302     vlc_mutex_init( p_intf, &p_intf->p_sys->status_lock );
303     p_intf->p_sys->i_last_state = PLAYLIST_STOPPED;
304     
305     /* Non-buffered stdout */
306     setvbuf( stdout, (char *)NULL, _IOLBF, 0 );
307
308     p_intf->pf_run = Run;
309
310 #ifdef WIN32
311     p_intf->p_sys->b_quiet = config_GetInt( p_intf, "rc-quiet" );
312     if( !p_intf->p_sys->b_quiet ) { CONSOLE_INTRO_MSG; }
313 #else
314     CONSOLE_INTRO_MSG;
315 #endif
316
317     /* Force "no-view" mode */
318     p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
319                                                  FIND_ANYWHERE );
320     if( p_playlist )
321     {
322         vlc_mutex_lock( &p_playlist->object_lock );
323         p_playlist->status.i_view = -1;
324         vlc_mutex_unlock( &p_playlist->object_lock );
325         vlc_object_release( p_playlist );
326     }
327
328     msg_rc( _("Remote control interface initialized, `h' for help") );
329     return VLC_SUCCESS;
330 }
331
332 /*****************************************************************************
333  * Deactivate: uninitialize and cleanup
334  *****************************************************************************/
335 static void Deactivate( vlc_object_t *p_this )
336 {
337     intf_thread_t *p_intf = (intf_thread_t*)p_this;
338
339     net_ListenClose( p_intf->p_sys->pi_socket_listen );
340     if( p_intf->p_sys->i_socket != -1 )
341         net_Close( p_intf->p_sys->i_socket );
342     if( p_intf->p_sys->psz_unix_path != NULL )
343     {
344 #if defined(AF_LOCAL) && !defined(WIN32)
345         unlink( p_intf->p_sys->psz_unix_path );
346 #endif
347         free( p_intf->p_sys->psz_unix_path );
348     }
349     vlc_mutex_destroy( &p_intf->p_sys->status_lock );    
350     free( p_intf->p_sys );
351 }
352
353 /*****************************************************************************
354  * Run: rc thread
355  *****************************************************************************
356  * This part of the interface is in a separate thread so that we can call
357  * exec() from within it without annoying the rest of the program.
358  *****************************************************************************/
359 static void Run( intf_thread_t *p_intf )
360 {
361     input_thread_t * p_input;
362     playlist_t *     p_playlist;
363
364     char       p_buffer[ MAX_LINE_LENGTH + 1 ];
365     vlc_bool_t b_showpos = config_GetInt( p_intf, "rc-show-pos" );
366     vlc_bool_t b_longhelp = VLC_FALSE;
367
368     int        i_size = 0;
369     int        i_oldpos = 0;
370     int        i_newpos;
371
372     p_buffer[0] = 0;
373     p_input = NULL;
374     p_playlist = NULL;
375  
376     /* Register commands that will be cleaned up upon object destruction */
377     var_Create( p_intf, "quit", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
378     var_AddCallback( p_intf, "quit", Quit, NULL );
379     var_Create( p_intf, "intf", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
380     var_AddCallback( p_intf, "intf", Intf, NULL );
381
382     var_Create( p_intf, "add", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
383     var_AddCallback( p_intf, "add", Playlist, NULL );
384     var_Create( p_intf, "playlist", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
385     var_AddCallback( p_intf, "playlist", Playlist, NULL );
386     var_Create( p_intf, "play", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
387     var_AddCallback( p_intf, "play", Playlist, NULL );
388     var_Create( p_intf, "stop", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
389     var_AddCallback( p_intf, "stop", Playlist, NULL );
390     var_Create( p_intf, "prev", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
391     var_AddCallback( p_intf, "prev", Playlist, NULL );
392     var_Create( p_intf, "next", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
393     var_AddCallback( p_intf, "next", Playlist, NULL );
394     var_Create( p_intf, "goto", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
395     var_AddCallback( p_intf, "goto", Playlist, NULL );
396  
397     /* marquee on the fly items */
398     var_Create( p_intf, "marq-marquee", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
399     var_AddCallback( p_intf, "marq-marquee", Other, NULL );
400     var_Create( p_intf, "marq-x", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
401     var_AddCallback( p_intf, "marq-x", Other, NULL );
402     var_Create( p_intf, "marq-y", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
403     var_AddCallback( p_intf, "marq-y", Other, NULL );
404     var_Create( p_intf, "marq-position", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
405     var_AddCallback( p_intf, "marq-position", Other, NULL );
406     var_Create( p_intf, "marq-color", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
407     var_AddCallback( p_intf, "marq-color", Other, NULL );
408     var_Create( p_intf, "marq-opacity", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
409     var_AddCallback( p_intf, "marq-opacity", Other, NULL );
410     var_Create( p_intf, "marq-timeout", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
411     var_AddCallback( p_intf, "marq-timeout", Other, NULL );
412     var_Create( p_intf, "marq-size", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
413     var_AddCallback( p_intf, "marq-size", Other, NULL );
414
415     var_Create( p_intf, "mosaic-alpha", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
416     var_AddCallback( p_intf, "mosaic-alpha", Other, NULL );
417     var_Create( p_intf, "mosaic-height", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
418     var_AddCallback( p_intf, "mosaic-height", Other, NULL );
419     var_Create( p_intf, "mosaic-width", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
420     var_AddCallback( p_intf, "mosaic-width", Other, NULL );
421     var_Create( p_intf, "mosaic-xoffset", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
422     var_AddCallback( p_intf, "mosaic-xoffset", Other, NULL );
423     var_Create( p_intf, "mosaic-yoffset", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
424     var_AddCallback( p_intf, "mosaic-yoffset", Other, NULL );
425     var_Create( p_intf, "mosaic-align", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
426     var_AddCallback( p_intf, "mosaic-align", Other, NULL );
427     var_Create( p_intf, "mosaic-vborder", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
428     var_AddCallback( p_intf, "mosaic-vborder", Other, NULL );
429     var_Create( p_intf, "mosaic-hborder", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
430     var_AddCallback( p_intf, "mosaic-hborder", Other, NULL );
431     var_Create( p_intf, "mosaic-position",
432                      VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
433     var_AddCallback( p_intf, "mosaic-position", Other, NULL );
434     var_Create( p_intf, "mosaic-rows", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
435     var_AddCallback( p_intf, "mosaic-rows", Other, NULL );
436     var_Create( p_intf, "mosaic-cols", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
437     var_AddCallback( p_intf, "mosaic-cols", Other, NULL );
438     var_Create( p_intf, "mosaic-keep-aspect-ratio",
439                      VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
440     var_AddCallback( p_intf, "mosaic-keep-aspect-ratio", Other, NULL );
441
442     /* time on the fly items */
443     var_Create( p_intf, "time-format", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
444     var_AddCallback( p_intf, "time-format", Other, NULL );
445     var_Create( p_intf, "time-x", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
446     var_AddCallback( p_intf, "time-x", Other, NULL );
447     var_Create( p_intf, "time-y", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
448     var_AddCallback( p_intf, "time-y", Other, NULL );
449     var_Create( p_intf, "time-position", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
450     var_AddCallback( p_intf, "time-position", Other, NULL );
451     var_Create( p_intf, "time-color", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
452     var_AddCallback( p_intf, "time-color", Other, NULL );
453     var_Create( p_intf, "time-opacity", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
454     var_AddCallback( p_intf, "time-opacity", Other, NULL );
455     var_Create( p_intf, "time-size", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
456     var_AddCallback( p_intf, "time-size", Other, NULL );
457     
458     /* logo on the fly items */
459     var_Create( p_intf, "logo-file", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
460     var_AddCallback( p_intf, "logo-file", Other, NULL );
461     var_Create( p_intf, "logo-x", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
462     var_AddCallback( p_intf, "logo-x", Other, NULL );
463     var_Create( p_intf, "logo-y", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
464     var_AddCallback( p_intf, "logo-y", Other, NULL );
465     var_Create( p_intf, "logo-position", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
466     var_AddCallback( p_intf, "logo-position", Other, NULL );
467     var_Create( p_intf, "logo-transparency", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
468     var_AddCallback( p_intf, "logo-transparency", Other, NULL );
469
470     /* OSD menu commands */
471     var_Create( p_intf, "menu", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
472     var_AddCallback( p_intf, "menu", Menu, NULL ); 
473
474     /* DVD commands */
475     var_Create( p_intf, "pause", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
476     var_AddCallback( p_intf, "pause", Input, NULL );
477     var_Create( p_intf, "seek", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
478     var_AddCallback( p_intf, "seek", Input, NULL );
479     var_Create( p_intf, "title", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
480     var_AddCallback( p_intf, "title", Input, NULL );
481     var_Create( p_intf, "title_n", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
482     var_AddCallback( p_intf, "title_n", Input, NULL );
483     var_Create( p_intf, "title_p", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
484     var_AddCallback( p_intf, "title_p", Input, NULL );
485     var_Create( p_intf, "chapter", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
486     var_AddCallback( p_intf, "chapter", Input, NULL );
487     var_Create( p_intf, "chapter_n", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
488     var_AddCallback( p_intf, "chapter_n", Input, NULL );
489     var_Create( p_intf, "chapter_p", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
490     var_AddCallback( p_intf, "chapter_p", Input, NULL );
491
492     var_Create( p_intf, "fastforward", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
493     var_AddCallback( p_intf, "fastforward", Input, NULL );
494     var_Create( p_intf, "rewind", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
495     var_AddCallback( p_intf, "rewind", Input, NULL );
496     var_Create( p_intf, "faster", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
497     var_AddCallback( p_intf, "faster", Input, NULL );
498     var_Create( p_intf, "slower", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
499     var_AddCallback( p_intf, "slower", Input, NULL );
500     var_Create( p_intf, "normal", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
501     var_AddCallback( p_intf, "normal", Input, NULL );
502
503     /* audio commands */
504     var_Create( p_intf, "volume", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
505     var_AddCallback( p_intf, "volume", Volume, NULL );
506     var_Create( p_intf, "volup", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
507     var_AddCallback( p_intf, "volup", VolumeMove, NULL );
508     var_Create( p_intf, "voldown", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
509     var_AddCallback( p_intf, "voldown", VolumeMove, NULL );
510     var_Create( p_intf, "adev", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
511     var_AddCallback( p_intf, "adev", AudioConfig, NULL );
512     var_Create( p_intf, "achan", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
513     var_AddCallback( p_intf, "achan", AudioConfig, NULL );
514
515     /* status callbacks */
516     /* Listen to audio volume updates */
517     var_AddCallback( p_intf->p_vlc, "volume", VolumeChanged, p_intf );
518     
519 #ifdef WIN32
520     /* Get the file descriptor of the console input */
521     p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
522     if( p_intf->p_sys->hConsoleIn == INVALID_HANDLE_VALUE )
523     {
524         msg_Err( p_intf, "Couldn't open STD_INPUT_HANDLE" );
525         p_intf->b_die = VLC_TRUE;
526     }
527 #endif
528
529     while( !p_intf->b_die )
530     {
531         char *psz_cmd, *psz_arg;
532         vlc_bool_t b_complete;
533
534         if( p_intf->p_sys->pi_socket_listen != NULL &&
535             p_intf->p_sys->i_socket == -1 )
536         {
537             p_intf->p_sys->i_socket =
538                 net_Accept( p_intf, p_intf->p_sys->pi_socket_listen, 0 );
539         }
540
541         b_complete = ReadCommand( p_intf, p_buffer, &i_size );
542
543         /* Manage the input part */
544         if( p_input == NULL )
545         {
546             if( p_playlist )
547             {
548                 p_input = vlc_object_find( p_playlist, VLC_OBJECT_INPUT,
549                                                        FIND_CHILD );
550             }
551             else
552             {
553                 p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
554                                                    FIND_ANYWHERE );
555                 if( p_input )
556                 {
557                     p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
558                                                            FIND_PARENT );
559                 }
560             }
561             /* New input has been registered */
562             if( p_input )
563             {
564                 if( !p_input->b_dead || !p_input->b_die )
565                 {
566                     msg_rc( STATUS_CHANGE "( New input: %s )", p_input->input.p_item->psz_uri );
567                     msg_rc( STATUS_CHANGE "( audio volume: %d )", config_GetInt( p_intf, "volume" ));
568                 }
569                 var_AddCallback( p_input, "state", StateChanged, p_intf );
570                 var_AddCallback( p_input, "rate-faster", RateChanged, p_intf );
571                 var_AddCallback( p_input, "rate-slower", RateChanged, p_intf );
572                 var_AddCallback( p_input, "rate", RateChanged, p_intf );
573                 var_AddCallback( p_input, "time-offset", TimeOffsetChanged, p_intf );
574             }
575         }
576         else if( p_input->b_dead )
577         {
578             var_DelCallback( p_input, "state", StateChanged, p_intf );
579             var_DelCallback( p_input, "rate-faster", RateChanged, p_intf );
580             var_DelCallback( p_input, "rate-slower", RateChanged, p_intf );
581             var_DelCallback( p_input, "rate", RateChanged, p_intf );
582             var_DelCallback( p_input, "time-offset", TimeOffsetChanged, p_intf );
583             vlc_object_release( p_input );
584             p_input = NULL;
585
586             if( p_playlist )
587             {
588                 vlc_mutex_lock( &p_playlist->object_lock );
589                 p_intf->p_sys->i_last_state = (int) PLAYLIST_STOPPED;
590                 msg_rc( STATUS_CHANGE "( stop state: 0 )" );
591                 vlc_mutex_unlock( &p_playlist->object_lock );
592             }
593         }
594         
595         if( (p_input != NULL) && !p_input->b_dead && !p_input->b_die &&
596             (p_playlist != NULL) )
597         {
598             vlc_mutex_lock( &p_playlist->object_lock );
599             if( (p_intf->p_sys->i_last_state != p_playlist->status.i_status) &&
600                 (p_playlist->status.i_status == PLAYLIST_STOPPED) )
601             {
602                 p_intf->p_sys->i_last_state = PLAYLIST_STOPPED;
603                 msg_rc( STATUS_CHANGE "( stop state: 0 )" );
604             }
605             else if( (p_intf->p_sys->i_last_state != p_playlist->status.i_status) &&
606                 (p_playlist->status.i_status == PLAYLIST_RUNNING) )
607             {
608                 p_intf->p_sys->i_last_state = p_playlist->status.i_status;
609                 msg_rc( STATUS_CHANGE "( play state: 1 )" );
610             }
611             else if( (p_intf->p_sys->i_last_state != p_playlist->status.i_status) &&
612                 (p_playlist->status.i_status == PLAYLIST_PAUSED) )
613             {
614                 p_intf->p_sys->i_last_state = p_playlist->status.i_status;
615                 msg_rc( STATUS_CHANGE "( pause state: 2 )" );
616             }
617             vlc_mutex_unlock( &p_playlist->object_lock );
618         }
619
620         if( p_input && b_showpos )
621         {
622             i_newpos = 100 * var_GetFloat( p_input, "position" );
623             if( i_oldpos != i_newpos )
624             {
625                 i_oldpos = i_newpos;
626                 msg_rc( "pos: %d%%", i_newpos );
627             }
628         }
629
630         /* Is there something to do? */
631         if( !b_complete ) continue;
632
633
634         /* Skip heading spaces */
635         psz_cmd = p_buffer;
636         while( *psz_cmd == ' ' )
637         {
638             psz_cmd++;
639         }
640
641         /* Split psz_cmd at the first space and make sure that
642          * psz_arg is valid */
643         psz_arg = strchr( psz_cmd, ' ' );
644         if( psz_arg )
645         {
646             *psz_arg++ = 0;
647             while( *psz_arg == ' ' )
648             {
649                 psz_arg++;
650             }
651         }
652         else
653         {
654             psz_arg = "";
655         }
656
657         /* If the user typed a registered local command, try it */
658         if( var_Type( p_intf, psz_cmd ) & VLC_VAR_ISCOMMAND )
659         {
660             vlc_value_t val;
661             int i_ret;
662
663             val.psz_string = psz_arg;
664             i_ret = var_Set( p_intf, psz_cmd, val );
665             msg_rc( "%s: returned %i (%s)",
666                     psz_cmd, i_ret, vlc_error( i_ret ) );
667         }
668         /* Or maybe it's a global command */
669         else if( var_Type( p_intf->p_libvlc, psz_cmd ) & VLC_VAR_ISCOMMAND )
670         {
671             vlc_value_t val;
672             int i_ret;
673
674             val.psz_string = psz_arg;
675             /* FIXME: it's a global command, but we should pass the
676              * local object as an argument, not p_intf->p_libvlc. */
677             i_ret = var_Set( p_intf->p_libvlc, psz_cmd, val );
678             if( i_ret != 0 )
679             {
680                 msg_rc( "%s: returned %i (%s)",
681                          psz_cmd, i_ret, vlc_error( i_ret ) );
682             }
683         }
684         else if( !strcmp( psz_cmd, "logout" ) )
685         {
686             /* Close connection */
687             if( p_intf->p_sys->i_socket != -1 )
688             {
689                 net_Close( p_intf->p_sys->i_socket );
690             }
691             p_intf->p_sys->i_socket = -1;
692         }
693         else if( !strcmp( psz_cmd, "info" ) )
694         {
695             if( p_input )
696             {
697                 int i, j;
698                 vlc_mutex_lock( &p_input->input.p_item->lock );
699                 for ( i = 0; i < p_input->input.p_item->i_categories; i++ )
700                 {
701                     info_category_t *p_category =
702                         p_input->input.p_item->pp_categories[i];
703
704                     msg_rc( "+----[ %s ]", p_category->psz_name );
705                     msg_rc( "| " );
706                     for ( j = 0; j < p_category->i_infos; j++ )
707                     {
708                         info_t *p_info = p_category->pp_infos[j];
709                         msg_rc( "| %s: %s", p_info->psz_name,
710                                 p_info->psz_value );
711                     }
712                     msg_rc( "| " );
713                 }
714                 msg_rc( "+----[ end of stream info ]" );
715                 vlc_mutex_unlock( &p_input->input.p_item->lock );
716             }
717             else
718             {
719                 msg_rc( "no input" );
720             }
721         }
722         else if( !strcmp( psz_cmd, "is_playing" ) )
723         {
724             if( ! p_input )
725             {
726                 msg_rc( "0" );
727             }
728             else
729             {
730                 msg_rc( "1" );
731
732                 /* FIXME: This is a hack */
733                 /* Replay the current state of the system. */
734                 msg_rc( STATUS_CHANGE "( New input: %s )", p_input->input.p_item->psz_uri );
735                 msg_rc( STATUS_CHANGE "( audio volume: %d )", config_GetInt( p_intf, "volume" ));
736
737                 if( p_playlist )
738                 {
739                     vlc_mutex_lock( &p_playlist->object_lock );
740                     switch( p_playlist->status.i_status )
741                     {
742                         case PLAYLIST_STOPPED:
743                             msg_rc( STATUS_CHANGE "( stop state: 0 )" );
744                             break;
745                         case PLAYLIST_RUNNING:
746                             msg_rc( STATUS_CHANGE "( play state: %d )", var_GetInteger( p_input, "state" ) );
747                             break;
748                         case PLAYLIST_PAUSED:
749                             msg_rc( STATUS_CHANGE "( pause state: 2 )" );
750                             break;
751                         default:
752                             msg_rc( STATUS_CHANGE "( state unknown )" );
753                             break;
754                     }
755                     vlc_mutex_unlock( &p_playlist->object_lock );
756                 } /* End of current playlist status */                
757             }
758         }
759         else if( !strcmp( psz_cmd, "get_time" ) )
760         {
761             if( ! p_input )
762             {
763                 msg_rc("0");
764             }
765             else
766             {
767                 vlc_value_t time;
768                 var_Get( p_input, "time", &time );
769                 msg_rc( "%i", time.i_time / 1000000);
770             }
771         }
772         else if( !strcmp( psz_cmd, "get_length" ) )
773         {
774             if( ! p_input )
775             {
776                 msg_rc("0");
777             }
778             else
779             {
780                 vlc_value_t time;
781                 var_Get( p_input, "length", &time );
782                 msg_rc( "%i", time.i_time / 1000000);
783             }
784         }
785         else if( !strcmp( psz_cmd, "get_title" ) )
786         {
787             if( ! p_input )
788             {
789                 msg_rc("");
790             }
791             else
792             {
793                 msg_rc( "%s", p_input->input.p_item->psz_name );
794             }
795         }
796         else if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "h", 1 )
797                  || !strncmp( psz_cmd, "H", 1 ) || !strncmp( psz_cmd, "?", 1 ) )
798         {
799             if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "H", 1 ) )
800                  b_longhelp = VLC_TRUE;
801             else b_longhelp = VLC_FALSE;
802
803             Help( p_intf, b_longhelp );
804         }
805         else switch( psz_cmd[0] )
806         {
807         case 'f':
808         case 'F':
809             if( p_input )
810             {
811                 vout_thread_t *p_vout;
812                 p_vout = vlc_object_find( p_input,
813                                           VLC_OBJECT_VOUT, FIND_CHILD );
814
815                 if( p_vout )
816                 {
817                     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
818                     vlc_object_release( p_vout );
819                 }
820             }
821             break;
822
823         case 's':
824         case 'S':
825             ;
826             break;
827
828         case '\0':
829             /* Ignore empty lines */
830             break;
831
832         default:
833             msg_rc(_("unknown command `%s', type `help' for help"), psz_cmd);
834             break;
835         }
836
837         /* Command processed */
838         i_size = 0; p_buffer[0] = 0;
839     }
840
841     msg_rc( STATUS_CHANGE "( stop state: 0 )" );
842     msg_rc( STATUS_CHANGE "( quit )" );
843
844     if( p_input )
845     {
846         var_DelCallback( p_input, "state", StateChanged, p_intf );
847         var_DelCallback( p_input, "rate-faster", RateChanged, p_intf );
848         var_DelCallback( p_input, "rate-slower", RateChanged, p_intf );
849         var_DelCallback( p_input, "rate", RateChanged, p_intf );
850         var_DelCallback( p_input, "time-offset", TimeOffsetChanged, p_intf );
851         vlc_object_release( p_input );
852         p_input = NULL;
853     }
854
855     if( p_playlist )
856     {
857         vlc_object_release( p_playlist );
858         p_playlist = NULL;
859     }
860     
861     var_DelCallback( p_intf->p_vlc, "volume", VolumeChanged, p_intf );
862 }
863
864 static void Help( intf_thread_t *p_intf, vlc_bool_t b_longhelp)
865 {                
866     msg_rc(_("+----[ Remote control commands ]"));
867     msg_rc(  "| ");
868     msg_rc(_("| add XYZ  . . . . . . . . . . add XYZ to playlist"));
869     msg_rc(_("| playlist . . .  show items currently in playlist"));
870     msg_rc(_("| play . . . . . . . . . . . . . . . . play stream"));
871     msg_rc(_("| stop . . . . . . . . . . . . . . . . stop stream"));
872     msg_rc(_("| next . . . . . . . . . . . .  next playlist item"));
873     msg_rc(_("| prev . . . . . . . . . .  previous playlist item"));
874     msg_rc(_("| goto . . . . . . . . . . . .  goto item at index"));
875     msg_rc(_("| title [X]  . . . . set/get title in current item"));
876     msg_rc(_("| title_n  . . . . . .  next title in current item"));
877     msg_rc(_("| title_p  . . . .  previous title in current item"));
878     msg_rc(_("| chapter [X]  . . set/get chapter in current item"));
879     msg_rc(_("| chapter_n  . . . .  next chapter in current item"));
880     msg_rc(_("| chapter_p  . .  previous chapter in current item"));
881     msg_rc(  "| ");
882     msg_rc(_("| seek X . seek in seconds, for instance `seek 12'"));
883     msg_rc(_("| pause  . . . . . . . . . . . . . .  toggle pause"));
884     msg_rc(_("| fastforward  . . . . . .  .  set to maximum rate"));
885     msg_rc(_("| rewind  . . . . . . . . . .  set to minimum rate"));
886     msg_rc(_("| faster . . . . . . . .  faster playing of stream"));
887     msg_rc(_("| slower . . . . . . . .  slower playing of stream"));
888     msg_rc(_("| normal . . . . . . . .  normal playing of stream"));
889     msg_rc(_("| f  . . . . . . . . . . . . . . toggle fullscreen"));
890     msg_rc(_("| info . . .  information about the current stream"));
891     msg_rc(  "| ");
892     msg_rc(_("| volume [X] . . . . . . . .  set/get audio volume"));
893     msg_rc(_("| volup [X]  . . . . .  raise audio volume X steps"));
894     msg_rc(_("| voldown [X]  . . . .  lower audio volume X steps"));
895     msg_rc(_("| adev [X] . . . . . . . . .  set/get audio device"));
896     msg_rc(_("| achan [X]. . . . . . . .  set/get audio channels"));
897     msg_rc(_("| menu [on|off|up|down|left|right|select] use menu"));
898     msg_rc(  "| ");
899    
900     if (b_longhelp)
901     {
902         msg_rc(_("| marq-marquee STRING  . . overlay STRING in video"));
903         msg_rc(_("| marq-x X . . . . . . . . . . . .offset from left"));
904         msg_rc(_("| marq-y Y . . . . . . . . . . . . offset from top"));
905         msg_rc(_("| marq-position #. . .  .relative position control"));
906         msg_rc(_("| marq-color # . . . . . . . . . . font color, RGB"));
907         msg_rc(_("| marq-opacity # . . . . . . . . . . . . . opacity"));
908         msg_rc(_("| marq-timeout T. . . . . . . . . . timeout, in ms"));
909         msg_rc(_("| marq-size # . . . . . . . . font size, in pixels"));
910         msg_rc(  "| ");
911         msg_rc(_("| time-format STRING . . . overlay STRING in video"));
912         msg_rc(_("| time-x X . . . . . . . . . . . .offset from left"));
913         msg_rc(_("| time-y Y . . . . . . . . . . . . offset from top"));
914         msg_rc(_("| time-position #. . . . . . . . relative position"));
915         msg_rc(_("| time-color # . . . . . . . . . . font color, RGB"));
916         msg_rc(_("| time-opacity # . . . . . . . . . . . . . opacity"));
917         msg_rc(_("| time-size # . . . . . . . . font size, in pixels"));
918         msg_rc(  "| ");
919         msg_rc(_("| logo-file STRING . . . the overlay file path/name"));
920         msg_rc(_("| logo-x X . . . . . . . . . . . .offset from left"));
921         msg_rc(_("| logo-y Y . . . . . . . . . . . . offset from top"));
922         msg_rc(_("| logo-position #. . . . . . . . relative position"));
923         msg_rc(_("| logo-transparency #. . . . . . . . .transparency"));
924         msg_rc(  "| ");
925         msg_rc(_("| mosaic-alpha # . . . . . . . . . . . . . . alpha"));
926         msg_rc(_("| mosaic-height #. . . . . . . . . . . . . .height"));
927         msg_rc(_("| mosaic-width # . . . . . . . . . . . . . . width"));
928         msg_rc(_("| mosaic-xoffset # . . . .top left corner position"));
929         msg_rc(_("| mosaic-yoffset # . . . .top left corner position"));
930         msg_rc(_("| mosaic-align 0..2,4..6,8..10. . .mosaic alignment"));
931         msg_rc(_("| mosaic-vborder # . . . . . . . . vertical border"));
932         msg_rc(_("| mosaic-hborder # . . . . . . . horizontal border"));
933         msg_rc(_("| mosaic-position {0=auto,1=fixed} . . . .position"));
934         msg_rc(_("| mosaic-rows #. . . . . . . . . . .number of rows"));
935         msg_rc(_("| mosaic-cols #. . . . . . . . . . .number of cols"));
936         msg_rc(_("| mosaic-keep-aspect-ratio {0,1} . . .aspect ratio"));
937         msg_rc(  "| ");
938     }
939     msg_rc(_("| help . . . . . . . . . . . . . this help message"));
940     msg_rc(_("| longhelp . . . . . . . . . a longer help message"));
941     msg_rc(_("| logout . . . . .  exit (if in socket connection)"));
942     msg_rc(_("| quit . . . . . . . . . . . . . . . . .  quit vlc"));
943     msg_rc(  "| ");
944     msg_rc(_("+----[ end of help ]"));
945 }
946
947 /********************************************************************
948  * Status callback routines
949  ********************************************************************/
950 static int TimeOffsetChanged( vlc_object_t *p_this, char const *psz_cmd,
951     vlc_value_t oldval, vlc_value_t newval, void *p_data )
952 {
953     intf_thread_t *p_intf = (intf_thread_t*)p_data;
954     input_thread_t *p_input = NULL;
955     
956     vlc_mutex_lock( &p_intf->p_sys->status_lock );
957     p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
958     if( p_input )
959     {
960         msg_rc( STATUS_CHANGE "( time-offset: %d )", var_GetInteger( p_input, "time-offset" ) );
961         vlc_object_release( p_input );
962     }
963     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
964     return VLC_SUCCESS;
965 }
966
967 static int VolumeChanged( vlc_object_t *p_this, char const *psz_cmd,
968     vlc_value_t oldval, vlc_value_t newval, void *p_data )
969 {
970     intf_thread_t *p_intf = (intf_thread_t*)p_data;
971     
972     vlc_mutex_lock( &p_intf->p_sys->status_lock );
973     msg_rc( STATUS_CHANGE "( audio volume: %d )", newval.i_int );
974     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
975     return VLC_SUCCESS;
976 }
977
978 static int StateChanged( vlc_object_t *p_this, char const *psz_cmd,
979     vlc_value_t oldval, vlc_value_t newval, void *p_data )
980 {
981     intf_thread_t *p_intf = (intf_thread_t*)p_data;
982     playlist_t    *p_playlist = NULL;
983     input_thread_t *p_input = NULL;
984         
985     vlc_mutex_lock( &p_intf->p_sys->status_lock );
986     p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
987     if( p_input )
988     {
989         p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_PARENT );
990         if( p_playlist )
991         {
992             char cmd[5] = "";
993             switch( p_playlist->status.i_status )
994             {
995             case PLAYLIST_STOPPED:
996                 strncpy( &cmd[0], "stop", 4);
997                 cmd[4] = '\0';
998                 break;
999             case PLAYLIST_RUNNING:
1000                 strncpy( &cmd[0], "play", 4);
1001                 cmd[4] = '\0';
1002                 break;
1003             case PLAYLIST_PAUSED:
1004                 strncpy( &cmd[0], "pause", 5);
1005                 cmd[5] = '\0';
1006                 break;
1007             } /* var_GetInteger( p_input, "state" )  */
1008             msg_rc( STATUS_CHANGE "( %s state: %d )", &cmd[0], newval.i_int );
1009             vlc_object_release( p_playlist );
1010         }
1011         vlc_object_release( p_input );
1012     }
1013     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
1014     return VLC_SUCCESS;
1015 }
1016
1017 static int RateChanged( vlc_object_t *p_this, char const *psz_cmd,
1018     vlc_value_t oldval, vlc_value_t newval, void *p_data )
1019 {
1020     intf_thread_t *p_intf = (intf_thread_t*)p_data;
1021     input_thread_t *p_input = NULL;
1022     
1023     vlc_mutex_lock( &p_intf->p_sys->status_lock );
1024     p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1025     if( p_input )
1026     {
1027         msg_rc( STATUS_CHANGE "( new rate: %d )", var_GetInteger( p_input, "rate" ) );
1028         vlc_object_release( p_input );
1029     }
1030     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
1031     return VLC_SUCCESS;
1032 }
1033
1034 /********************************************************************
1035  * Command routines
1036  ********************************************************************/
1037 static int Input( vlc_object_t *p_this, char const *psz_cmd,
1038                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
1039 {
1040     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1041     input_thread_t *p_input;
1042     vlc_value_t     val;
1043
1044     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1045     if( !p_input ) return VLC_ENOOBJ;
1046
1047     var_Get( p_input, "state", &val );
1048     if( ( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) ) &&
1049         ( strcmp( psz_cmd, "pause" ) != 0 ) )
1050     {
1051         msg_rc( _("press pause to continue") );
1052         vlc_object_release( p_input );
1053         return VLC_EGENERIC;
1054     }
1055     
1056     /* Parse commands that only require an input */
1057     if( !strcmp( psz_cmd, "pause" ) )
1058     {
1059         val.i_int = PAUSE_S;
1060
1061         var_Set( p_input, "state", val );
1062         vlc_object_release( p_input );
1063         return VLC_SUCCESS;
1064     }
1065     else if( !strcmp( psz_cmd, "seek" ) )
1066     {
1067         if( strlen( newval.psz_string ) > 0 &&
1068             newval.psz_string[strlen( newval.psz_string ) - 1] == '%' )
1069         {
1070             val.f_float = (float)atoi( newval.psz_string ) / 100.0;
1071             var_Set( p_input, "position", val );
1072         }
1073         else
1074         {
1075             val.i_time = ((int64_t)atoi( newval.psz_string )) * 1000000;
1076             var_Set( p_input, "time", val );
1077         }
1078         vlc_object_release( p_input );
1079         return VLC_SUCCESS;
1080     }
1081     else if ( !strcmp( psz_cmd, "fastforward" ) )
1082     {
1083         val.i_int = config_GetInt( p_intf, "key-jump+3sec" );
1084         var_Set( p_intf->p_vlc, "key-pressed", val );
1085         
1086         vlc_object_release( p_input );
1087         return VLC_SUCCESS;
1088     }
1089     else if ( !strcmp( psz_cmd, "rewind" ) )
1090     {
1091         val.i_int = config_GetInt( p_intf, "key-jump-3sec" );
1092         var_Set( p_intf->p_vlc, "key-pressed", val );
1093
1094         vlc_object_release( p_input );
1095         return VLC_SUCCESS;
1096     }
1097     else if ( !strcmp( psz_cmd, "faster" ) )
1098     {
1099         val.b_bool = VLC_TRUE;
1100
1101         var_Set( p_input, "rate-faster", val );
1102         vlc_object_release( p_input );
1103         return VLC_SUCCESS;
1104     }
1105     else if ( !strcmp( psz_cmd, "slower" ) )
1106     {
1107         val.b_bool = VLC_TRUE;
1108
1109         var_Set( p_input, "rate-slower", val );
1110         vlc_object_release( p_input );
1111         return VLC_SUCCESS;
1112     }
1113     else if ( !strcmp( psz_cmd, "normal" ) )
1114     {
1115         val.i_int = INPUT_RATE_DEFAULT;
1116
1117         var_Set( p_input, "rate", val );
1118         vlc_object_release( p_input );
1119         return VLC_SUCCESS;
1120     }
1121     else if( !strcmp( psz_cmd, "chapter" ) ||
1122              !strcmp( psz_cmd, "chapter_n" ) ||
1123              !strcmp( psz_cmd, "chapter_p" ) )
1124     {
1125         if( !strcmp( psz_cmd, "chapter" ) )
1126         {
1127             if ( *newval.psz_string )
1128             {
1129                 /* Set. */
1130                 val.i_int = atoi( newval.psz_string );
1131                 var_Set( p_input, "chapter", val );
1132             }
1133             else
1134             {
1135                 vlc_value_t val_list;
1136
1137                 /* Get. */
1138                 var_Get( p_input, "chapter", &val );
1139                 var_Change( p_input, "chapter", VLC_VAR_GETCHOICES,
1140                             &val_list, NULL );
1141                 msg_rc( "Currently playing chapter %d/%d",
1142                         val.i_int, val_list.p_list->i_count );
1143                 var_Change( p_this, "chapter", VLC_VAR_FREELIST,
1144                             &val_list, NULL );
1145             }
1146         }
1147         else if( !strcmp( psz_cmd, "chapter_n" ) )
1148         {
1149             val.b_bool = VLC_TRUE;
1150             var_Set( p_input, "next-chapter", val );
1151         }
1152         else if( !strcmp( psz_cmd, "chapter_p" ) )
1153         {
1154             val.b_bool = VLC_TRUE;
1155             var_Set( p_input, "prev-chapter", val );
1156         }
1157
1158         vlc_object_release( p_input );
1159         return VLC_SUCCESS;
1160     }
1161     else if( !strcmp( psz_cmd, "title" ) ||
1162              !strcmp( psz_cmd, "title_n" ) ||
1163              !strcmp( psz_cmd, "title_p" ) )
1164     {
1165         if( !strcmp( psz_cmd, "title" ) )
1166         {
1167             if ( *newval.psz_string )
1168             {
1169                 /* Set. */
1170                 val.i_int = atoi( newval.psz_string );
1171                 var_Set( p_input, "title", val );
1172             }
1173             else
1174             {
1175                 vlc_value_t val_list;
1176
1177                 /* Get. */
1178                 var_Get( p_input, "title", &val );
1179                 var_Change( p_input, "title", VLC_VAR_GETCHOICES,
1180                             &val_list, NULL );
1181                 msg_rc( "Currently playing title %d/%d",
1182                         val.i_int, val_list.p_list->i_count );
1183                 var_Change( p_this, "title", VLC_VAR_FREELIST,
1184                             &val_list, NULL );
1185             }
1186         }
1187         else if( !strcmp( psz_cmd, "title_n" ) )
1188         {
1189             val.b_bool = VLC_TRUE;
1190             var_Set( p_input, "next-title", val );
1191         }
1192         else if( !strcmp( psz_cmd, "title_p" ) )
1193         {
1194             val.b_bool = VLC_TRUE;
1195             var_Set( p_input, "prev-title", val );
1196         }
1197
1198         vlc_object_release( p_input );
1199         return VLC_SUCCESS;
1200     }
1201
1202     /* Never reached. */
1203     return VLC_EGENERIC;
1204 }
1205
1206 static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
1207                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
1208 {
1209     vlc_value_t val;
1210     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1211     playlist_t *p_playlist;
1212
1213     p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
1214                                            FIND_ANYWHERE );
1215     if( !p_playlist )
1216     {
1217         return VLC_ENOOBJ;
1218     }
1219
1220     if( p_playlist->p_input )
1221     {
1222         vlc_value_t val;
1223         var_Get( p_playlist->p_input, "state", &val );
1224         if( ( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) ) &&
1225             ( strcmp( psz_cmd, "pause" ) != 0 ) )
1226         {
1227             msg_rc( _("press pause to continue") );
1228             vlc_object_release( p_playlist );
1229             return VLC_EGENERIC;
1230         }
1231     }    
1232     
1233     /* Parse commands that require a playlist */
1234     if( !strcmp( psz_cmd, "prev" ) )
1235     {
1236         playlist_Prev( p_playlist );
1237     }
1238     else if( !strcmp( psz_cmd, "next" ) )
1239     {
1240         playlist_Next( p_playlist );
1241     }
1242     else if( !strcmp( psz_cmd, "play" ) )
1243     {
1244         if( p_playlist->p_input )
1245         {
1246             vlc_value_t val;
1247             
1248             var_Get( p_playlist->p_input, "rate", &val );
1249             if( val.i_int != INPUT_RATE_DEFAULT )
1250             {
1251                 val.i_int = INPUT_RATE_DEFAULT;
1252                 var_Set( p_playlist->p_input, "rate", val );
1253             }
1254             else
1255             {
1256                 playlist_Play( p_playlist );
1257             }
1258         }
1259     }
1260     else if (!strcmp( psz_cmd, "goto" ) )
1261     {
1262         if( strlen( newval.psz_string ) > 0) 
1263         {
1264             val.i_int = atoi( newval.psz_string );
1265             playlist_Goto( p_playlist, val.i_int); 
1266         }
1267     }
1268     else if( !strcmp( psz_cmd, "stop" ) )
1269     {
1270         playlist_Stop( p_playlist );
1271     }
1272     else if( !strcmp( psz_cmd, "add" ) &&
1273              newval.psz_string && *newval.psz_string )
1274     {
1275         playlist_item_t *p_item = parse_MRL( p_intf, newval.psz_string );
1276
1277         if( p_item )
1278         {
1279             msg_rc( "trying to add %s to playlist", newval.psz_string );
1280             playlist_AddItem( p_playlist, p_item,
1281                               PLAYLIST_GO|PLAYLIST_APPEND, PLAYLIST_END );
1282         }
1283     }
1284     else if( !strcmp( psz_cmd, "playlist" ) )
1285     {
1286         int i;
1287         for ( i = 0; i < p_playlist->i_size; i++ )
1288         {
1289             msg_rc( "|%s%s   %s|%s|", i == p_playlist->i_index ? "*" : " ",
1290                     p_playlist->pp_items[i]->input.psz_name,
1291                     p_playlist->pp_items[i]->input.psz_uri,
1292                     p_playlist->pp_items[i]->i_parents > 0 ?
1293                     p_playlist->pp_items[i]->pp_parents[0]->p_parent->input.psz_name : "" );
1294         }
1295         if ( i == 0 )
1296         {
1297             msg_rc( "| no entries" );
1298         }
1299     }
1300  
1301     /*
1302      * sanity check
1303      */
1304     else
1305     {
1306         msg_rc( "unknown command!" );
1307     }
1308
1309     vlc_object_release( p_playlist );
1310     return VLC_SUCCESS;
1311 }
1312
1313 static int Other( vlc_object_t *p_this, char const *psz_cmd,
1314                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
1315 {
1316     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1317     vlc_object_t  *p_playlist;
1318     vlc_value_t    val;
1319     vlc_object_t  *p_input;
1320
1321     p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
1322     if( !p_playlist )
1323     {
1324         return VLC_ENOOBJ;
1325     }
1326     
1327     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1328     if( !p_input )
1329     {
1330         vlc_object_release( p_playlist );
1331         return VLC_ENOOBJ;
1332     }
1333
1334     if( p_input )
1335     {
1336         var_Get( p_input, "state", &val );
1337         if( ( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) ) &&
1338             ( strcmp( psz_cmd, "pause" ) != 0 ) )
1339         {
1340             msg_rc( _("press pause to continue") );
1341             vlc_object_release( p_playlist );
1342             vlc_object_release( p_input );
1343             return VLC_EGENERIC;
1344         }
1345     }
1346     
1347     /* Parse miscellaneous commands */
1348     if( !strcmp( psz_cmd, "marq-marquee" ) )
1349     {
1350         if( strlen( newval.psz_string ) > 0 )
1351         {
1352             val.psz_string = newval.psz_string;
1353             var_Set( p_input->p_libvlc, "marq-marquee", val );
1354         }
1355         else 
1356         {
1357                 val.psz_string = "";
1358                 var_Set( p_input->p_libvlc, "marq-marquee", val);
1359         }
1360     }
1361     else if( !strcmp( psz_cmd, "marq-x" ) )
1362     {
1363         if( strlen( newval.psz_string ) > 0) 
1364         {
1365             val.i_int = atoi( newval.psz_string );
1366             var_Set( p_input->p_libvlc, "marq-x", val );
1367         }
1368     }
1369     else if( !strcmp( psz_cmd, "marq-y" ) )
1370     {
1371         if( strlen( newval.psz_string ) > 0) 
1372         {
1373             val.i_int = atoi( newval.psz_string );
1374             var_Set( p_input->p_libvlc, "marq-y", val );
1375         }
1376     }
1377     else if( !strcmp( psz_cmd, "marq-position" ) )
1378     {
1379         if( strlen( newval.psz_string ) > 0) 
1380         {
1381             val.i_int = atoi( newval.psz_string );
1382             var_Set( p_input->p_libvlc, "marq-position", val );
1383         }
1384     }
1385     else if( !strcmp( psz_cmd, "marq-color" ) )
1386     {
1387         if( strlen( newval.psz_string ) > 0) 
1388         {
1389             val.i_int = strtol( newval.psz_string, NULL, 0 );
1390             var_Set( p_input->p_libvlc, "marq-color", val );
1391         }
1392     }
1393     else if( !strcmp( psz_cmd, "marq-opacity" ) )
1394     {
1395         if( strlen( newval.psz_string ) > 0) 
1396         {
1397             val.i_int = strtol( newval.psz_string, NULL, 0 );
1398             var_Set( p_input->p_libvlc, "marq-opacity", val );
1399         }
1400     }
1401     else if( !strcmp( psz_cmd, "marq-size" ) )
1402     {
1403         if( strlen( newval.psz_string ) > 0) 
1404         {
1405             val.i_int = atoi( newval.psz_string );
1406             var_Set( p_input->p_libvlc, "marq-size", val );
1407         }
1408     }
1409     else if( !strcmp( psz_cmd, "marq-timeout" ) )
1410     {
1411         if( strlen( newval.psz_string ) > 0) 
1412         {
1413             val.i_int = atoi( newval.psz_string );
1414             var_Set( p_input, "marq-timeout", val );
1415         }
1416     }
1417     else if( !strcmp( psz_cmd, "mosaic-alpha" ) )
1418     {
1419         if( strlen( newval.psz_string ) > 0)
1420         {
1421             val.i_int = atoi( newval.psz_string );
1422             var_Set( p_input->p_libvlc, "mosaic-alpha", val );
1423         }
1424     }
1425     else if( !strcmp( psz_cmd, "mosaic-height" ) )
1426     {
1427         if( strlen( newval.psz_string ) > 0)
1428         {
1429             val.i_int = atoi( newval.psz_string );
1430             var_Set( p_input->p_libvlc, "mosaic-height", val );
1431         }
1432     }
1433     else if( !strcmp( psz_cmd, "mosaic-width" ) )
1434     {
1435         if( strlen( newval.psz_string ) > 0)
1436         {
1437             val.i_int = atoi( newval.psz_string );
1438             var_Set( p_input->p_libvlc, "mosaic-width", val );
1439         }
1440     }
1441     else if( !strcmp( psz_cmd, "mosaic-xoffset" ) )
1442     {
1443         if( strlen( newval.psz_string ) > 0)
1444         {
1445             val.i_int = atoi( newval.psz_string );
1446             var_Set( p_input->p_libvlc, "mosaic-xoffset", val );
1447         }
1448     }
1449     else if( !strcmp( psz_cmd, "mosaic-yoffset" ) )
1450     {
1451         if( strlen( newval.psz_string ) > 0)
1452         {
1453             val.i_int = atoi( newval.psz_string );
1454             var_Set( p_input->p_libvlc, "mosaic-yoffset", val );
1455         }
1456     }
1457     else if( !strcmp( psz_cmd, "mosaic-align" ) )
1458     {
1459         if( strlen( newval.psz_string ) > 0 )
1460         {
1461             val.i_int = atoi( newval.psz_string );
1462             var_Set( p_input->p_libvlc, "mosaic-align", val );
1463         }
1464     }
1465     else if( !strcmp( psz_cmd, "mosaic-vborder" ) )
1466     {
1467         if( strlen( newval.psz_string ) > 0)
1468         {
1469             val.i_int = atoi( newval.psz_string );
1470             var_Set( p_input->p_libvlc, "mosaic-vborder", val );
1471         }
1472     }
1473     else if( !strcmp( psz_cmd, "mosaic-hborder" ) )
1474     {
1475         if( strlen( newval.psz_string ) > 0)
1476         {
1477             val.i_int = atoi( newval.psz_string );
1478             var_Set( p_input->p_libvlc, "mosaic-hborder", val );
1479         }
1480     }
1481     else if( !strcmp( psz_cmd, "mosaic-position" ) )
1482     {
1483         if( strlen( newval.psz_string ) > 0)
1484         {
1485             val.i_int = atoi( newval.psz_string );
1486             var_Set( p_input->p_libvlc, "mosaic-position", val );
1487         }
1488     }
1489     else if( !strcmp( psz_cmd, "mosaic-rows" ) )
1490     {
1491         if( strlen( newval.psz_string ) > 0)
1492         {
1493             val.i_int = atoi( newval.psz_string );
1494             var_Set( p_input->p_libvlc, "mosaic-rows", val );
1495         }
1496     }
1497     else if( !strcmp( psz_cmd, "mosaic-cols" ) )
1498     {
1499         if( strlen( newval.psz_string ) > 0)
1500         {
1501             val.i_int = atoi( newval.psz_string );
1502             var_Set( p_input->p_libvlc, "mosaic-cols", val );
1503         }
1504     }
1505     else if( !strcmp( psz_cmd, "mosaic-keep-aspect-ratio" ) )
1506     {
1507         if( strlen( newval.psz_string ) > 0)
1508         {
1509             val.i_int = atoi( newval.psz_string );
1510             var_Set( p_input->p_libvlc, "mosaic-keep-aspect-ratio", val );
1511         }
1512     }
1513     else if( !strcmp( psz_cmd, "time-format" ) )
1514     {
1515         if( strlen( newval.psz_string ) > 0 )
1516         {
1517             val.psz_string = newval.psz_string;
1518             var_Set( p_input->p_libvlc, "time-format", val );
1519         }
1520         else 
1521         {
1522             val.psz_string = "";
1523             var_Set( p_input->p_libvlc, "time-format", val);
1524         }
1525     }
1526     else if( !strcmp( psz_cmd, "time-x" ) )
1527     {
1528         if( strlen( newval.psz_string ) > 0) 
1529         {
1530             val.i_int = atoi( newval.psz_string );
1531             var_Set( p_input->p_libvlc, "time-x", val );
1532         }
1533     }
1534     else if( !strcmp( psz_cmd, "time-y" ) )
1535     {
1536         if( strlen( newval.psz_string ) > 0) 
1537         {
1538             val.i_int = atoi( newval.psz_string );
1539             var_Set( p_input->p_libvlc, "time-y", val );
1540         }
1541     }
1542     else if( !strcmp( psz_cmd, "time-position" ) )
1543     {
1544         if( strlen( newval.psz_string ) > 0) 
1545         {
1546             val.i_int = atoi( newval.psz_string );
1547             var_Set( p_input->p_libvlc, "time-position", val );
1548         }
1549     }
1550     else if( !strcmp( psz_cmd, "time-color" ) )
1551     {
1552         if( strlen( newval.psz_string ) > 0) 
1553         {
1554             val.i_int = strtol( newval.psz_string, NULL, 0 );
1555             var_Set( p_input->p_libvlc, "time-color", val );
1556         }
1557     }
1558     else if( !strcmp( psz_cmd, "time-opacity" ) )
1559     {
1560         if( strlen( newval.psz_string ) > 0) 
1561         {
1562             val.i_int = strtol( newval.psz_string, NULL, 0 );
1563             var_Set( p_input->p_libvlc, "time-opacity", val );
1564         }
1565     }
1566     else if( !strcmp( psz_cmd, "time-size" ) )
1567     {
1568         if( strlen( newval.psz_string ) > 0) 
1569         {
1570             val.i_int = atoi( newval.psz_string );
1571             var_Set( p_input->p_libvlc, "time-size", val );
1572         }
1573     }
1574     else if( !strcmp( psz_cmd, "logo-file" ) )
1575     {
1576         if( strlen( newval.psz_string ) > 0 )
1577         {
1578             val.psz_string = newval.psz_string;
1579             var_Set( p_input->p_libvlc, "logo-file", val );
1580         }
1581     }
1582     else if( !strcmp( psz_cmd, "logo-x" ) )
1583     {
1584         if( strlen( newval.psz_string ) > 0) 
1585         {
1586             val.i_int = atoi( newval.psz_string );
1587             var_Set( p_input->p_libvlc, "logo-x", val );
1588         }
1589     }
1590     else if( !strcmp( psz_cmd, "logo-y" ) )
1591     {
1592         if( strlen( newval.psz_string ) > 0) 
1593         {
1594             val.i_int = atoi( newval.psz_string );
1595             var_Set( p_input->p_libvlc, "logo-y", val );
1596         }
1597     }
1598     else if( !strcmp( psz_cmd, "logo-position" ) )
1599     {
1600         if( strlen( newval.psz_string ) > 0) 
1601         {
1602             val.i_int = atoi( newval.psz_string );
1603             var_Set( p_input->p_libvlc, "logo-position", val );
1604         }
1605     }
1606     else if( !strcmp( psz_cmd, "logo-transparency" ) )
1607     {
1608         if( strlen( newval.psz_string ) > 0) 
1609         {
1610             val.i_int = strtol( newval.psz_string, NULL, 0 );
1611             var_Set( p_input->p_libvlc, "logo-transparency", val );
1612         }
1613     }
1614
1615     /*
1616      * sanity check
1617      */
1618     else
1619     {
1620         msg_rc( "unknown command!" );
1621     }
1622
1623     vlc_object_release( p_playlist );
1624     vlc_object_release( p_input );
1625     return VLC_SUCCESS;
1626 }
1627
1628 static int Quit( vlc_object_t *p_this, char const *psz_cmd,
1629                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1630 {
1631     playlist_t *p_playlist;
1632     
1633     p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
1634     if( p_playlist )
1635     {
1636         playlist_Stop( p_playlist );
1637         vlc_object_release( p_playlist );
1638     }    
1639     p_this->p_vlc->b_die = VLC_TRUE;
1640     return VLC_SUCCESS;
1641 }
1642
1643 static int Intf( vlc_object_t *p_this, char const *psz_cmd,
1644                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1645 {
1646     intf_thread_t *p_newintf;
1647
1648     p_newintf = intf_Create( p_this->p_vlc, newval.psz_string );
1649
1650     if( p_newintf )
1651     {
1652         p_newintf->b_block = VLC_FALSE;
1653         if( intf_RunThread( p_newintf ) )
1654         {
1655             vlc_object_detach( p_newintf );
1656             intf_Destroy( p_newintf );
1657         }
1658     }
1659
1660     return VLC_SUCCESS;
1661 }
1662
1663 static int Volume( vlc_object_t *p_this, char const *psz_cmd,
1664                    vlc_value_t oldval, vlc_value_t newval, void *p_data )
1665 {
1666     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1667     int i_error;
1668
1669     if ( *newval.psz_string )
1670     {
1671         /* Set. */
1672         audio_volume_t i_volume = atoi( newval.psz_string );
1673         if ( (i_volume > (audio_volume_t)AOUT_VOLUME_MAX) )
1674         {
1675             msg_rc( "Volume must be in the range %d-%d", AOUT_VOLUME_MIN,
1676                     AOUT_VOLUME_MAX );
1677             i_error = VLC_EBADVAR;
1678         }
1679         else
1680         {
1681             if( i_volume == AOUT_VOLUME_MIN )
1682             {
1683                 vlc_value_t keyval;
1684
1685                 keyval.i_int = config_GetInt( p_intf, "key-vol-mute" );
1686                 var_Set( p_intf->p_vlc, "key-pressed", keyval );
1687             }        
1688             i_error = aout_VolumeSet( p_this, i_volume );
1689         }
1690     }
1691     else
1692     {
1693         /* Get. */
1694         audio_volume_t i_volume;
1695         if ( aout_VolumeGet( p_this, &i_volume ) < 0 )
1696         {
1697             i_error = VLC_EGENERIC;
1698         }
1699         else
1700         {
1701             msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
1702             i_error = VLC_SUCCESS;
1703         }
1704     }
1705
1706     return i_error;
1707 }
1708
1709 static int VolumeMove( vlc_object_t *p_this, char const *psz_cmd,
1710                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1711 {
1712     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1713     audio_volume_t i_volume;
1714     int i_nb_steps = atoi(newval.psz_string);
1715     int i_error = VLC_SUCCESS;
1716
1717     if ( i_nb_steps <= 0 || i_nb_steps > (AOUT_VOLUME_MAX/AOUT_VOLUME_STEP) )
1718     {
1719         i_nb_steps = 1;
1720     }
1721
1722     if ( !strcmp(psz_cmd, "volup") )
1723     {
1724         if ( aout_VolumeUp( p_this, i_nb_steps, &i_volume ) < 0 )
1725             i_error = VLC_EGENERIC;
1726         osd_VolumeUp( p_this );
1727     }
1728     else
1729     {
1730         if ( aout_VolumeDown( p_this, i_nb_steps, &i_volume ) < 0 )
1731             i_error = VLC_EGENERIC;
1732         osd_VolumeDown( p_this );
1733     }
1734
1735     if ( !i_error ) msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
1736     return i_error;
1737 }
1738
1739 static int AudioConfig( vlc_object_t *p_this, char const *psz_cmd,
1740                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
1741 {
1742     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1743     aout_instance_t * p_aout;
1744     const char * psz_variable;
1745     vlc_value_t val_name;
1746     int i_error;
1747
1748     p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, FIND_ANYWHERE );
1749     if ( p_aout == NULL ) return VLC_ENOOBJ;
1750
1751     if ( !strcmp( psz_cmd, "adev" ) )
1752     {
1753         psz_variable = "audio-device";
1754     }
1755     else
1756     {
1757         psz_variable = "audio-channels";
1758     }
1759
1760     /* Get the descriptive name of the variable */
1761     var_Change( (vlc_object_t *)p_aout, psz_variable, VLC_VAR_GETTEXT,
1762                  &val_name, NULL );
1763     if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1764
1765     if ( !*newval.psz_string )
1766     {
1767         /* Retrieve all registered ***. */
1768         vlc_value_t val, text;
1769         int i, i_value;
1770
1771         if ( var_Get( (vlc_object_t *)p_aout, psz_variable, &val ) < 0 )
1772         {
1773             vlc_object_release( (vlc_object_t *)p_aout );
1774             return VLC_EGENERIC;
1775         }
1776         i_value = val.i_int;
1777
1778         if ( var_Change( (vlc_object_t *)p_aout, psz_variable,
1779                          VLC_VAR_GETLIST, &val, &text ) < 0 )
1780         {
1781             vlc_object_release( (vlc_object_t *)p_aout );
1782             return VLC_EGENERIC;
1783         }
1784
1785         msg_rc( "+----[ %s ]", val_name.psz_string );
1786         for ( i = 0; i < val.p_list->i_count; i++ )
1787         {
1788             if ( i_value == val.p_list->p_values[i].i_int )
1789                 msg_rc( "| %i - %s *", val.p_list->p_values[i].i_int,
1790                         text.p_list->p_values[i].psz_string );
1791             else
1792                 msg_rc( "| %i - %s", val.p_list->p_values[i].i_int,
1793                         text.p_list->p_values[i].psz_string );
1794         }
1795         var_Change( (vlc_object_t *)p_aout, psz_variable, VLC_VAR_FREELIST,
1796                     &val, &text );
1797         msg_rc( "+----[ end of %s ]", val_name.psz_string );
1798
1799         if( val_name.psz_string ) free( val_name.psz_string );
1800         i_error = VLC_SUCCESS;
1801     }
1802     else
1803     {
1804         vlc_value_t val;
1805         val.i_int = atoi( newval.psz_string );
1806
1807         i_error = var_Set( (vlc_object_t *)p_aout, psz_variable, val );
1808     }
1809     vlc_object_release( (vlc_object_t *)p_aout );
1810
1811     return i_error;
1812 }
1813
1814 /* OSD menu commands */
1815 static int Menu( vlc_object_t *p_this, char const *psz_cmd,
1816     vlc_value_t oldval, vlc_value_t newval, void *p_data )
1817 {
1818     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1819     playlist_t    *p_playlist = NULL;
1820     vlc_value_t val;
1821     int i_error = VLC_EGENERIC;
1822
1823     if ( !*newval.psz_string )
1824     {
1825         msg_rc( _("please provide one of the following paramaters") );
1826         msg_rc( "[on|off|up|down|left|right|select]" );
1827         return i_error;
1828     }
1829     
1830     p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
1831     if( !p_playlist )
1832         return VLC_ENOOBJ;
1833
1834     if( p_playlist->p_input )
1835     {
1836         var_Get( p_playlist->p_input, "state", &val );
1837         if( ( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) ) &&
1838             ( strcmp( newval.psz_string, "select" ) != 0 ) )
1839         {
1840             msg_rc( _("press menu select to continue") );
1841             vlc_object_release( p_playlist );
1842             return VLC_EGENERIC;
1843         }
1844         vlc_object_release( p_playlist );
1845     }
1846     
1847     val.psz_string = strdup( newval.psz_string );
1848     if( !strcmp( val.psz_string, "on" ) || !strcmp( val.psz_string, "show" ))
1849         osd_MenuShow( p_this );
1850     else if( !strcmp( val.psz_string, "off" ) || !strcmp( val.psz_string, "hide" ) )
1851         osd_MenuHide( p_this );
1852     else if( !strcmp( val.psz_string, "up" ) )
1853         osd_MenuUp( p_this );
1854     else if( !strcmp( val.psz_string, "down" ) )
1855         osd_MenuDown( p_this );
1856     else if( !strcmp( val.psz_string, "left" ) )
1857         osd_MenuPrev( p_this );
1858     else if( !strcmp( val.psz_string, "right" ) )
1859         osd_MenuNext( p_this );
1860     else if( !strcmp( val.psz_string, "select" ) )
1861         osd_MenuActivate( p_this );
1862     else
1863     {
1864         msg_rc( _("please provide one of the following paramaters") );
1865         msg_rc( "[on|off|up|down|left|right|select]" );
1866         if( val.psz_string ) free( val.psz_string );
1867             return i_error;
1868     }
1869
1870     i_error = VLC_SUCCESS;
1871     if( val.psz_string ) free( val.psz_string );
1872     return i_error;
1873 }
1874
1875 #ifdef WIN32
1876 vlc_bool_t ReadWin32( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
1877 {
1878     INPUT_RECORD input_record;
1879     DWORD i_dw;
1880
1881     /* On Win32, select() only works on socket descriptors */
1882     while( WaitForSingleObject( p_intf->p_sys->hConsoleIn,
1883                                 INTF_IDLE_SLEEP/1000 ) == WAIT_OBJECT_0 )
1884     {
1885         while( !p_intf->b_die && *pi_size < MAX_LINE_LENGTH &&
1886                ReadConsoleInput( p_intf->p_sys->hConsoleIn, &input_record,
1887                                  1, &i_dw ) )
1888         {
1889             if( input_record.EventType != KEY_EVENT ||
1890                 !input_record.Event.KeyEvent.bKeyDown ||
1891                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
1892                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL||
1893                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_MENU ||
1894                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CAPITAL )
1895             {
1896                 /* nothing interesting */
1897                 continue;
1898             }
1899
1900             p_buffer[ *pi_size ] = input_record.Event.KeyEvent.uChar.AsciiChar;
1901
1902             /* Echo out the command */
1903             putc( p_buffer[ *pi_size ], stdout );
1904
1905             /* Handle special keys */
1906             if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1907             {
1908                 putc( '\n', stdout );
1909                 break;
1910             }
1911             switch( p_buffer[ *pi_size ] )
1912             {
1913             case '\b':
1914                 if( *pi_size )
1915                 {
1916                     *pi_size -= 2;
1917                     putc( ' ', stdout );
1918                     putc( '\b', stdout );
1919                 }
1920                 break;
1921             case '\r':
1922                 (*pi_size) --;
1923                 break;
1924             }
1925
1926             (*pi_size)++;
1927         }
1928
1929         if( *pi_size == MAX_LINE_LENGTH ||
1930             p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1931         {
1932             p_buffer[ *pi_size ] = 0;
1933             return VLC_TRUE;
1934         }
1935     }
1936
1937     return VLC_FALSE;
1938 }
1939 #endif
1940
1941 vlc_bool_t ReadCommand( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
1942 {
1943     int i_read = 0;
1944
1945 #ifdef WIN32
1946     if( p_intf->p_sys->i_socket == -1 && !p_intf->p_sys->b_quiet )
1947         return ReadWin32( p_intf, p_buffer, pi_size );
1948     else if( p_intf->p_sys->i_socket == -1 )
1949     {
1950         msleep( INTF_IDLE_SLEEP );
1951         return VLC_FALSE;
1952     }
1953 #endif
1954
1955     while( !p_intf->b_die && *pi_size < MAX_LINE_LENGTH &&
1956            (i_read = net_ReadNonBlock( p_intf, p_intf->p_sys->i_socket == -1 ?
1957                        0 /*STDIN_FILENO*/ : p_intf->p_sys->i_socket, NULL,
1958                   (uint8_t *)p_buffer + *pi_size, 1, INTF_IDLE_SLEEP ) ) > 0 )
1959     {
1960         if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1961             break;
1962
1963         (*pi_size)++;
1964     }
1965
1966     /* Connection closed */
1967     if( i_read == -1 )
1968     {
1969         p_intf->p_sys->i_socket = -1;
1970         p_buffer[ *pi_size ] = 0;
1971         return VLC_TRUE;
1972     }
1973
1974     if( *pi_size == MAX_LINE_LENGTH ||
1975         p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
1976     {
1977         p_buffer[ *pi_size ] = 0;
1978         return VLC_TRUE;
1979     }
1980
1981     return VLC_FALSE;
1982 }
1983
1984 /*****************************************************************************
1985  * parse_MRL: build a playlist item from a full mrl
1986  *****************************************************************************
1987  * MRL format: "simplified-mrl [:option-name[=option-value]]"
1988  * We don't check for '"' or '\'', we just assume that a ':' that follows a
1989  * space is a new option. Should be good enough for our purpose.
1990  *****************************************************************************/
1991 static playlist_item_t *parse_MRL( intf_thread_t *p_intf, char *psz_mrl )
1992 {
1993 #define SKIPSPACE( p ) { while( *p && ( *p == ' ' || *p == '\t' ) ) p++; }
1994 #define SKIPTRAILINGSPACE( p, d ) \
1995     { char *e=d; while( e > p && (*(e-1)==' ' || *(e-1)=='\t') ){e--;*e=0;} }
1996
1997     playlist_item_t *p_item = NULL;
1998     char *psz_item = NULL, *psz_item_mrl = NULL, *psz_orig;
1999     char **ppsz_options = NULL;
2000     int i, i_options = 0;
2001
2002     if( !psz_mrl ) return 0;
2003
2004     psz_mrl = psz_orig = strdup( psz_mrl );
2005     while( *psz_mrl )
2006     {
2007         SKIPSPACE( psz_mrl );
2008         psz_item = psz_mrl;
2009
2010         for( ; *psz_mrl; psz_mrl++ )
2011         {
2012             if( (*psz_mrl == ' ' || *psz_mrl == '\t') && psz_mrl[1] == ':' )
2013             {
2014                 /* We have a complete item */
2015                 break;
2016             }
2017             if( (*psz_mrl == ' ' || *psz_mrl == '\t') &&
2018                 (psz_mrl[1] == '"' || psz_mrl[1] == '\'') && psz_mrl[2] == ':')
2019             {
2020                 /* We have a complete item */
2021                 break;
2022             }
2023         }
2024
2025         if( *psz_mrl ) { *psz_mrl = 0; psz_mrl++; }
2026         SKIPTRAILINGSPACE( psz_item, psz_item + strlen( psz_item ) );
2027
2028         /* Remove '"' and '\'' if necessary */
2029         if( *psz_item == '"' && psz_item[strlen(psz_item)-1] == '"' )
2030         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
2031         if( *psz_item == '\'' && psz_item[strlen(psz_item)-1] == '\'' )
2032         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
2033
2034         if( !psz_item_mrl ) psz_item_mrl = psz_item;
2035         else if( *psz_item )
2036         {
2037             i_options++;
2038             ppsz_options = realloc( ppsz_options, i_options * sizeof(char *) );
2039             ppsz_options[i_options - 1] = &psz_item[1];
2040         }
2041
2042         if( *psz_mrl ) SKIPSPACE( psz_mrl );
2043     }
2044
2045     /* Now create a playlist item */
2046     if( psz_item_mrl )
2047     {
2048         p_item = playlist_ItemNew( p_intf, psz_item_mrl, psz_item_mrl );
2049         for( i = 0; i < i_options; i++ )
2050         {
2051             playlist_ItemAddOption( p_item, ppsz_options[i] );
2052         }
2053     }
2054
2055     if( i_options ) free( ppsz_options );
2056     free( psz_orig );
2057
2058     return p_item;
2059 }