]> git.sesse.net Git - vlc/blob - modules/control/rc.c
[PATCH] Repeat and Loop - Remote Control Interface by Dermot McGahon.
[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  *         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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <vlc/vlc.h>
29
30 #include <stdlib.h>                                      /* malloc(), free() */
31 #include <string.h>
32
33 #include <errno.h>                                                 /* ENOMEM */
34 #include <stdio.h>
35 #include <ctype.h>
36 #include <signal.h>
37
38 #include <vlc/intf.h>
39 #include <vlc/aout.h>
40 #include <vlc/vout.h>
41 #include <vlc_video.h>
42 #include <vlc_osd.h>
43 #include <vlc_update.h>
44
45 #ifdef HAVE_UNISTD_H
46 #    include <unistd.h>
47 #endif
48
49 #ifdef HAVE_SYS_TIME_H
50 #    include <sys/time.h>
51 #endif
52 #include <sys/types.h>
53
54 #include "vlc_error.h"
55 #include "network.h"
56 #include "vlc_url.h"
57
58 #include "charset.h"
59
60 #if defined(AF_UNIX) && !defined(AF_LOCAL)
61 #    define AF_LOCAL AF_UNIX
62 #endif
63
64 #if defined(AF_LOCAL) && ! defined(WIN32)
65 #    include <sys/un.h>
66 #endif
67
68 #define MAX_LINE_LENGTH 256
69 #define STATUS_CHANGE "status change: "
70
71 /*****************************************************************************
72  * Local prototypes
73  *****************************************************************************/
74 static int  Activate     ( vlc_object_t * );
75 static void Deactivate   ( vlc_object_t * );
76 static void Run          ( intf_thread_t * );
77
78 static void Help         ( intf_thread_t *, vlc_bool_t );
79 static void RegisterCallbacks( intf_thread_t * );
80
81 static vlc_bool_t ReadCommand( intf_thread_t *, char *, int * );
82
83 static input_item_t *parse_MRL( intf_thread_t *, char * );
84
85 static int  Input        ( vlc_object_t *, char const *,
86                            vlc_value_t, vlc_value_t, void * );
87 static int  Playlist     ( vlc_object_t *, char const *,
88                            vlc_value_t, vlc_value_t, void * );
89 static int  Other        ( vlc_object_t *, char const *,
90                            vlc_value_t, vlc_value_t, void * );
91 static int  Quit         ( vlc_object_t *, char const *,
92                            vlc_value_t, vlc_value_t, void * );
93 static int  Intf         ( vlc_object_t *, char const *,
94                            vlc_value_t, vlc_value_t, void * );
95 static int  Volume       ( vlc_object_t *, char const *,
96                            vlc_value_t, vlc_value_t, void * );
97 static int  VolumeMove   ( vlc_object_t *, char const *,
98                            vlc_value_t, vlc_value_t, void * );
99 static int  VideoConfig  ( vlc_object_t *, char const *,
100                            vlc_value_t, vlc_value_t, void * );
101 static int  AudioConfig  ( vlc_object_t *, char const *,
102                            vlc_value_t, vlc_value_t, void * );
103 static int  Menu         ( vlc_object_t *, char const *,
104                            vlc_value_t, vlc_value_t, void * );
105 static void checkUpdates( intf_thread_t *p_intf, char *psz_arg );
106
107 /* Status Callbacks */
108 static int TimeOffsetChanged( vlc_object_t *, char const *,
109                               vlc_value_t, vlc_value_t , void * );
110 static int VolumeChanged    ( vlc_object_t *, char const *,
111                               vlc_value_t, vlc_value_t, void * );
112 static int StateChanged     ( vlc_object_t *, char const *,
113                               vlc_value_t, vlc_value_t, void * );
114 static int RateChanged      ( vlc_object_t *, char const *,
115                               vlc_value_t, vlc_value_t, void * );
116
117 struct intf_sys_t
118 {
119     int *pi_socket_listen;
120     int i_socket;
121     char *psz_unix_path;
122
123     /* status changes */
124     vlc_mutex_t       status_lock;
125     playlist_status_t i_last_state;
126
127 #ifdef WIN32
128     HANDLE hConsoleIn;
129     vlc_bool_t b_quiet;
130 #endif
131 };
132
133 #ifdef HAVE_VARIADIC_MACROS
134 #   define msg_rc( psz_format, args... ) \
135       __msg_rc( p_intf, psz_format, ## args )
136 #endif
137
138 void __msg_rc( intf_thread_t *p_intf, const char *psz_fmt, ... )
139 {
140     va_list args;
141     va_start( args, psz_fmt );
142
143     if( p_intf->p_sys->i_socket == -1 )
144     {
145         utf8_vfprintf( stdout, psz_fmt, args );
146         printf( "\r\n" );
147     }
148     else
149     {
150         net_vaPrintf( p_intf, p_intf->p_sys->i_socket, NULL, psz_fmt, args );
151         net_Write( p_intf, p_intf->p_sys->i_socket, NULL, (uint8_t*)"\r\n", 2 );
152     }
153     va_end( args );
154 }
155
156 /*****************************************************************************
157  * Module descriptor
158  *****************************************************************************/
159 #define POS_TEXT N_("Show stream position")
160 #define POS_LONGTEXT N_("Show the current position in seconds within the " \
161                         "stream from time to time." )
162
163 #define TTY_TEXT N_("Fake TTY")
164 #define TTY_LONGTEXT N_("Force the rc module to use stdin as if it was a TTY.")
165
166 #define UNIX_TEXT N_("UNIX socket command input")
167 #define UNIX_LONGTEXT N_("Accept commands over a Unix socket rather than " \
168                          "stdin." )
169
170 #define HOST_TEXT N_("TCP command input")
171 #define HOST_LONGTEXT N_("Accept commands over a socket rather than stdin. " \
172             "You can set the address and port the interface will bind to." )
173
174 #ifdef WIN32
175 #define QUIET_TEXT N_("Do not open a DOS command box interface")
176 #define QUIET_LONGTEXT N_( \
177     "By default the rc interface plugin will start a DOS command box. " \
178     "Enabling the quiet mode will not bring this command box but can also " \
179     "be pretty annoying when you want to stop VLC and no video window is " \
180     "open." )
181 #endif
182
183 vlc_module_begin();
184     set_shortname( _("RC"));
185     set_category( CAT_INTERFACE );
186     set_subcategory( SUBCAT_INTERFACE_MAIN );
187     set_description( _("Remote control interface") );
188     add_bool( "rc-show-pos", 0, NULL, POS_TEXT, POS_LONGTEXT, VLC_TRUE );
189 #ifdef HAVE_ISATTY
190     add_bool( "rc-fake-tty", 0, NULL, TTY_TEXT, TTY_LONGTEXT, VLC_TRUE );
191 #endif
192     add_string( "rc-unix", 0, NULL, UNIX_TEXT, UNIX_LONGTEXT, VLC_TRUE );
193     add_string( "rc-host", 0, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE );
194
195 #ifdef WIN32
196     add_bool( "rc-quiet", 0, NULL, QUIET_TEXT, QUIET_LONGTEXT, VLC_FALSE );
197 #endif
198
199     set_capability( "interface", 20 );
200     set_callbacks( Activate, Deactivate );
201 vlc_module_end();
202
203 /*****************************************************************************
204  * Activate: initialize and create stuff
205  *****************************************************************************/
206 static int Activate( vlc_object_t *p_this )
207 {
208     intf_thread_t *p_intf = (intf_thread_t*)p_this;
209     char *psz_host, *psz_unix_path;
210     int  *pi_socket = NULL;
211
212 #if defined(HAVE_ISATTY) && !defined(WIN32)
213     /* Check that stdin is a TTY */
214     if( !config_GetInt( p_intf, "rc-fake-tty" ) && !isatty( 0 ) )
215     {
216         msg_Warn( p_intf, "fd 0 is not a TTY" );
217         return VLC_EGENERIC;
218     }
219 #endif
220
221     psz_unix_path = config_GetPsz( p_intf, "rc-unix" );
222     if( psz_unix_path )
223     {
224         int i_socket;
225
226 #if !defined(AF_LOCAL) || defined(WIN32)
227         msg_Warn( p_intf, "your OS doesn't support filesystem sockets" );
228         free( psz_unix_path );
229         return VLC_EGENERIC;
230 #else
231         struct sockaddr_un addr;
232         int i_ret;
233
234         memset( &addr, 0, sizeof(struct sockaddr_un) );
235
236         msg_Dbg( p_intf, "trying UNIX socket" );
237
238         if( (i_socket = socket( AF_LOCAL, SOCK_STREAM, 0 ) ) < 0 )
239         {
240             msg_Warn( p_intf, "can't open socket: %s", strerror(errno) );
241             free( psz_unix_path );
242             return VLC_EGENERIC;
243         }
244
245         addr.sun_family = AF_LOCAL;
246         strncpy( addr.sun_path, psz_unix_path, sizeof( addr.sun_path ) );
247         addr.sun_path[sizeof( addr.sun_path ) - 1] = '\0';
248
249         if( (i_ret = bind( i_socket, (struct sockaddr*)&addr,
250                            sizeof(struct sockaddr_un) ) ) < 0 )
251         {
252             msg_Warn( p_intf, "couldn't bind socket to address: %s",
253                       strerror(errno) );
254             free( psz_unix_path );
255             net_Close( i_socket );
256             return VLC_EGENERIC;
257         }
258
259         if( ( i_ret = listen( i_socket, 1 ) ) < 0 )
260         {
261             msg_Warn( p_intf, "can't listen on socket: %s", strerror(errno));
262             free( psz_unix_path );
263             net_Close( i_socket );
264             return VLC_EGENERIC;
265         }
266
267         /* FIXME: we need a core function to merge listening sockets sets */
268         pi_socket = calloc( 2, sizeof( int ) );
269         if( pi_socket == NULL )
270         {
271             free( psz_unix_path );
272             net_Close( i_socket );
273             return VLC_ENOMEM;
274         }
275         pi_socket[0] = i_socket;
276         pi_socket[1] = -1;
277 #endif
278     }
279
280     if( ( pi_socket == NULL ) &&
281         ( psz_host = config_GetPsz( p_intf, "rc-host" ) ) != NULL )
282     {
283         vlc_url_t url;
284
285         vlc_UrlParse( &url, psz_host, 0 );
286
287         msg_Dbg( p_intf, "base: %s, port: %d", url.psz_host, url.i_port );
288
289         pi_socket = net_ListenTCP(p_this, url.psz_host, url.i_port);
290         if( pi_socket == NULL )
291         {
292             msg_Warn( p_intf, "can't listen to %s port %i",
293                       url.psz_host, url.i_port );
294             vlc_UrlClean( &url );
295             free( psz_host );
296             return VLC_EGENERIC;
297         }
298
299         vlc_UrlClean( &url );
300         free( psz_host );
301     }
302
303     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
304     if( !p_intf->p_sys )
305     {
306         msg_Err( p_intf, "no memory" );
307         return VLC_ENOMEM;
308     }
309
310     p_intf->p_sys->pi_socket_listen = pi_socket;
311     p_intf->p_sys->i_socket = -1;
312     p_intf->p_sys->psz_unix_path = psz_unix_path;
313     vlc_mutex_init( p_intf, &p_intf->p_sys->status_lock );
314     p_intf->p_sys->i_last_state = PLAYLIST_STOPPED;
315
316     /* Non-buffered stdout */
317     setvbuf( stdout, (char *)NULL, _IOLBF, 0 );
318
319     p_intf->pf_run = Run;
320
321 #ifdef WIN32
322     p_intf->p_sys->b_quiet = config_GetInt( p_intf, "rc-quiet" );
323     if( !p_intf->p_sys->b_quiet ) { CONSOLE_INTRO_MSG; }
324 #else
325     CONSOLE_INTRO_MSG;
326 #endif
327
328     msg_rc( _("Remote control interface initialized. Type `help' 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  * RegisterCallbacks: Register callbacks to dynamic variables
355  *****************************************************************************/
356 static void RegisterCallbacks( intf_thread_t *p_intf )
357 {
358     /* Register commands that will be cleaned up upon object destruction */
359     var_Create( p_intf, "quit", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
360     var_AddCallback( p_intf, "quit", Quit, NULL );
361     var_Create( p_intf, "intf", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
362     var_AddCallback( p_intf, "intf", Intf, NULL );
363
364     var_Create( p_intf, "add", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
365     var_AddCallback( p_intf, "add", Playlist, NULL );
366     var_Create( p_intf, "repeat", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
367     var_AddCallback( p_intf, "repeat", Playlist, NULL );
368     var_Create( p_intf, "loop", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
369     var_AddCallback( p_intf, "loop", Playlist, NULL );
370     var_Create( p_intf, "enqueue", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
371     var_AddCallback( p_intf, "enqueue", Playlist, NULL );
372     var_Create( p_intf, "playlist", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
373     var_AddCallback( p_intf, "playlist", Playlist, NULL );
374     var_Create( p_intf, "sort", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
375     var_AddCallback( p_intf, "sort", Playlist, NULL );
376     var_Create( p_intf, "play", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
377     var_AddCallback( p_intf, "play", Playlist, NULL );
378     var_Create( p_intf, "stop", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
379     var_AddCallback( p_intf, "stop", Playlist, NULL );
380     var_Create( p_intf, "clear", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
381     var_AddCallback( p_intf, "clear", Playlist, NULL );
382     var_Create( p_intf, "prev", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
383     var_AddCallback( p_intf, "prev", Playlist, NULL );
384     var_Create( p_intf, "next", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
385     var_AddCallback( p_intf, "next", Playlist, NULL );
386     var_Create( p_intf, "goto", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
387     var_AddCallback( p_intf, "goto", Playlist, NULL );
388     var_Create( p_intf, "status", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
389     var_AddCallback( p_intf, "status", Playlist, NULL );
390
391     /* marquee on the fly items */
392     var_Create( p_intf, "marq-marquee", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
393     var_AddCallback( p_intf, "marq-marquee", Other, NULL );
394     var_Create( p_intf, "marq-x", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
395     var_AddCallback( p_intf, "marq-x", Other, NULL );
396     var_Create( p_intf, "marq-y", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
397     var_AddCallback( p_intf, "marq-y", Other, NULL );
398     var_Create( p_intf, "marq-position", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
399     var_AddCallback( p_intf, "marq-position", Other, NULL );
400     var_Create( p_intf, "marq-color", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
401     var_AddCallback( p_intf, "marq-color", Other, NULL );
402     var_Create( p_intf, "marq-opacity", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
403     var_AddCallback( p_intf, "marq-opacity", Other, NULL );
404     var_Create( p_intf, "marq-timeout", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
405     var_AddCallback( p_intf, "marq-timeout", Other, NULL );
406     var_Create( p_intf, "marq-size", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
407     var_AddCallback( p_intf, "marq-size", Other, NULL );
408
409     var_Create( p_intf, "mosaic-alpha", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
410     var_AddCallback( p_intf, "mosaic-alpha", Other, NULL );
411     var_Create( p_intf, "mosaic-height", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
412     var_AddCallback( p_intf, "mosaic-height", Other, NULL );
413     var_Create( p_intf, "mosaic-width", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
414     var_AddCallback( p_intf, "mosaic-width", Other, NULL );
415     var_Create( p_intf, "mosaic-xoffset", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
416     var_AddCallback( p_intf, "mosaic-xoffset", Other, NULL );
417     var_Create( p_intf, "mosaic-yoffset", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
418     var_AddCallback( p_intf, "mosaic-yoffset", Other, NULL );
419     var_Create( p_intf, "mosaic-offsets", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
420     var_AddCallback( p_intf, "mosaic-offsets", Other, NULL );
421     var_Create( p_intf, "mosaic-align", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
422     var_AddCallback( p_intf, "mosaic-align", Other, NULL );
423     var_Create( p_intf, "mosaic-vborder", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
424     var_AddCallback( p_intf, "mosaic-vborder", Other, NULL );
425     var_Create( p_intf, "mosaic-hborder", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
426     var_AddCallback( p_intf, "mosaic-hborder", Other, NULL );
427     var_Create( p_intf, "mosaic-position",
428                      VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
429     var_AddCallback( p_intf, "mosaic-position", Other, NULL );
430     var_Create( p_intf, "mosaic-rows", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
431     var_AddCallback( p_intf, "mosaic-rows", Other, NULL );
432     var_Create( p_intf, "mosaic-cols", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
433     var_AddCallback( p_intf, "mosaic-cols", Other, NULL );
434     var_Create( p_intf, "mosaic-order", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
435     var_AddCallback( p_intf, "mosaic-order", Other, NULL );
436     var_Create( p_intf, "mosaic-keep-aspect-ratio",
437                      VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
438     var_AddCallback( p_intf, "mosaic-keep-aspect-ratio", Other, NULL );
439
440     /* logo on the fly items */
441     var_Create( p_intf, "logo-file", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
442     var_AddCallback( p_intf, "logo-file", Other, NULL );
443     var_Create( p_intf, "logo-x", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
444     var_AddCallback( p_intf, "logo-x", Other, NULL );
445     var_Create( p_intf, "logo-y", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
446     var_AddCallback( p_intf, "logo-y", Other, NULL );
447     var_Create( p_intf, "logo-position", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
448     var_AddCallback( p_intf, "logo-position", Other, NULL );
449     var_Create( p_intf, "logo-transparency", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
450     var_AddCallback( p_intf, "logo-transparency", Other, NULL );
451
452     /* OSD menu commands */
453     var_Create( p_intf, "menu", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
454     var_AddCallback( p_intf, "menu", Menu, NULL );
455
456     /* DVD commands */
457     var_Create( p_intf, "pause", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
458     var_AddCallback( p_intf, "pause", Input, NULL );
459     var_Create( p_intf, "seek", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
460     var_AddCallback( p_intf, "seek", Input, NULL );
461     var_Create( p_intf, "title", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
462     var_AddCallback( p_intf, "title", Input, NULL );
463     var_Create( p_intf, "title_n", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
464     var_AddCallback( p_intf, "title_n", Input, NULL );
465     var_Create( p_intf, "title_p", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
466     var_AddCallback( p_intf, "title_p", Input, NULL );
467     var_Create( p_intf, "chapter", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
468     var_AddCallback( p_intf, "chapter", Input, NULL );
469     var_Create( p_intf, "chapter_n", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
470     var_AddCallback( p_intf, "chapter_n", Input, NULL );
471     var_Create( p_intf, "chapter_p", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
472     var_AddCallback( p_intf, "chapter_p", Input, NULL );
473
474     var_Create( p_intf, "fastforward", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
475     var_AddCallback( p_intf, "fastforward", Input, NULL );
476     var_Create( p_intf, "rewind", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
477     var_AddCallback( p_intf, "rewind", Input, NULL );
478     var_Create( p_intf, "faster", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
479     var_AddCallback( p_intf, "faster", Input, NULL );
480     var_Create( p_intf, "slower", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
481     var_AddCallback( p_intf, "slower", Input, NULL );
482     var_Create( p_intf, "normal", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
483     var_AddCallback( p_intf, "normal", Input, NULL );
484
485     var_Create( p_intf, "atrack", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
486     var_AddCallback( p_intf, "atrack", Input, NULL );
487     var_Create( p_intf, "vtrack", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
488     var_AddCallback( p_intf, "vtrack", Input, NULL );
489     var_Create( p_intf, "strack", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
490     var_AddCallback( p_intf, "strack", Input, NULL );
491
492     /* video commands */
493     var_Create( p_intf, "vratio", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
494     var_AddCallback( p_intf, "vratio", VideoConfig, NULL );
495     var_Create( p_intf, "vcrop", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
496     var_AddCallback( p_intf, "vcrop", VideoConfig, NULL );
497     var_Create( p_intf, "vzoom", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
498     var_AddCallback( p_intf, "vzoom", VideoConfig, NULL );
499
500     /* audio commands */
501     var_Create( p_intf, "volume", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
502     var_AddCallback( p_intf, "volume", Volume, NULL );
503     var_Create( p_intf, "volup", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
504     var_AddCallback( p_intf, "volup", VolumeMove, NULL );
505     var_Create( p_intf, "voldown", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
506     var_AddCallback( p_intf, "voldown", VolumeMove, NULL );
507     var_Create( p_intf, "adev", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
508     var_AddCallback( p_intf, "adev", AudioConfig, NULL );
509     var_Create( p_intf, "achan", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
510     var_AddCallback( p_intf, "achan", AudioConfig, NULL );
511 }
512
513 /*****************************************************************************
514  * Run: rc thread
515  *****************************************************************************
516  * This part of the interface is in a separate thread so that we can call
517  * exec() from within it without annoying the rest of the program.
518  *****************************************************************************/
519 static void Run( intf_thread_t *p_intf )
520 {
521     input_thread_t * p_input;
522     playlist_t *     p_playlist;
523
524     char       p_buffer[ MAX_LINE_LENGTH + 1 ];
525     vlc_bool_t b_showpos = config_GetInt( p_intf, "rc-show-pos" );
526     vlc_bool_t b_longhelp = VLC_FALSE;
527
528     int        i_size = 0;
529     int        i_oldpos = 0;
530     int        i_newpos;
531
532     p_buffer[0] = 0;
533     p_input = NULL;
534     p_playlist = NULL;
535
536     /* Register commands that will be cleaned up upon object destruction */
537     RegisterCallbacks( p_intf );
538
539     /* status callbacks */
540     /* Listen to audio volume updates */
541     var_AddCallback( p_intf->p_libvlc, "volume-change", VolumeChanged, p_intf );
542
543 #ifdef WIN32
544     /* Get the file descriptor of the console input */
545     p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
546     if( p_intf->p_sys->hConsoleIn == INVALID_HANDLE_VALUE )
547     {
548         msg_Err( p_intf, "couldn't find user input handle" );
549         p_intf->b_die = VLC_TRUE;
550     }
551 #endif
552
553     while( !intf_ShouldDie( p_intf ) )
554     {
555         char *psz_cmd, *psz_arg;
556         vlc_bool_t b_complete;
557
558         if( p_intf->p_sys->pi_socket_listen != NULL &&
559             p_intf->p_sys->i_socket == -1 )
560         {
561             p_intf->p_sys->i_socket =
562                 net_Accept( p_intf, p_intf->p_sys->pi_socket_listen, 0 );
563         }
564
565         b_complete = ReadCommand( p_intf, p_buffer, &i_size );
566
567         /* Manage the input part */
568         if( p_input == NULL )
569         {
570             if( p_playlist )
571             {
572                 p_input = vlc_object_find( p_playlist, VLC_OBJECT_INPUT,
573                                                        FIND_CHILD );
574             }
575             else
576             {
577                 p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
578                                                    FIND_ANYWHERE );
579                 if( p_input )
580                 {
581                     p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
582                                                            FIND_PARENT );
583                 }
584             }
585             /* New input has been registered */
586             if( p_input )
587             {
588                 if( !p_input->b_dead || !p_input->b_die )
589                 {
590                     msg_rc( STATUS_CHANGE "( new input: %s )", p_input->input.p_item->psz_uri );
591                     msg_rc( STATUS_CHANGE "( audio volume: %d )", config_GetInt( p_intf, "volume" ));
592                 }
593                 var_AddCallback( p_input, "state", StateChanged, p_intf );
594                 var_AddCallback( p_input, "rate-faster", RateChanged, p_intf );
595                 var_AddCallback( p_input, "rate-slower", RateChanged, p_intf );
596                 var_AddCallback( p_input, "rate", RateChanged, p_intf );
597                 var_AddCallback( p_input, "time-offset", TimeOffsetChanged, p_intf );
598             }
599         }
600         else if( p_input->b_dead )
601         {
602             var_DelCallback( p_input, "state", StateChanged, p_intf );
603             var_DelCallback( p_input, "rate-faster", RateChanged, p_intf );
604             var_DelCallback( p_input, "rate-slower", RateChanged, p_intf );
605             var_DelCallback( p_input, "rate", RateChanged, p_intf );
606             var_DelCallback( p_input, "time-offset", TimeOffsetChanged, p_intf );
607             vlc_object_release( p_input );
608             p_input = NULL;
609
610             if( p_playlist )
611             {
612                 vlc_mutex_lock( &p_playlist->object_lock );
613                 p_intf->p_sys->i_last_state = (int) PLAYLIST_STOPPED;
614                 msg_rc( STATUS_CHANGE "( stop state: 0 )" );
615                 vlc_mutex_unlock( &p_playlist->object_lock );
616             }
617         }
618
619         if( (p_input != NULL) && !p_input->b_dead && !p_input->b_die &&
620             (p_playlist != NULL) )
621         {
622             vlc_mutex_lock( &p_playlist->object_lock );
623             if( (p_intf->p_sys->i_last_state != p_playlist->status.i_status) &&
624                 (p_playlist->status.i_status == PLAYLIST_STOPPED) )
625             {
626                 p_intf->p_sys->i_last_state = PLAYLIST_STOPPED;
627                 msg_rc( STATUS_CHANGE "( stop state: 0 )" );
628             }
629             else if( (p_intf->p_sys->i_last_state != p_playlist->status.i_status) &&
630                 (p_playlist->status.i_status == PLAYLIST_RUNNING) )
631             {
632                 p_intf->p_sys->i_last_state = p_playlist->status.i_status;
633                 msg_rc( STATUS_CHANGE "( play state: 1 )" );
634             }
635             else if( (p_intf->p_sys->i_last_state != p_playlist->status.i_status) &&
636                 (p_playlist->status.i_status == PLAYLIST_PAUSED) )
637             {
638                 p_intf->p_sys->i_last_state = p_playlist->status.i_status;
639                 msg_rc( STATUS_CHANGE "( pause state: 2 )" );
640             }
641             vlc_mutex_unlock( &p_playlist->object_lock );
642         }
643
644         if( p_input && b_showpos )
645         {
646             i_newpos = 100 * var_GetFloat( p_input, "position" );
647             if( i_oldpos != i_newpos )
648             {
649                 i_oldpos = i_newpos;
650                 msg_rc( "pos: %d%%", i_newpos );
651             }
652         }
653
654         /* Is there something to do? */
655         if( !b_complete ) continue;
656
657
658         /* Skip heading spaces */
659         psz_cmd = p_buffer;
660         while( *psz_cmd == ' ' )
661         {
662             psz_cmd++;
663         }
664
665         /* Split psz_cmd at the first space and make sure that
666          * psz_arg is valid */
667         psz_arg = strchr( psz_cmd, ' ' );
668         if( psz_arg )
669         {
670             *psz_arg++ = 0;
671             while( *psz_arg == ' ' )
672             {
673                 psz_arg++;
674             }
675         }
676         else
677         {
678             psz_arg = "";
679         }
680
681         /* If the user typed a registered local command, try it */
682         if( var_Type( p_intf, psz_cmd ) & VLC_VAR_ISCOMMAND )
683         {
684             vlc_value_t val;
685             int i_ret;
686
687             val.psz_string = psz_arg;
688             i_ret = var_Set( p_intf, psz_cmd, val );
689             msg_rc( "%s: returned %i (%s)",
690                     psz_cmd, i_ret, vlc_error( i_ret ) );
691         }
692         /* Or maybe it's a global command */
693         else if( var_Type( p_intf->p_libvlc_global, psz_cmd ) & VLC_VAR_ISCOMMAND )
694         {
695             vlc_value_t val;
696             int i_ret;
697
698             val.psz_string = psz_arg;
699             /* FIXME: it's a global command, but we should pass the
700              * local object as an argument, not p_intf->p_libvlc_global. */
701             i_ret = var_Set( p_intf->p_libvlc_global, psz_cmd, val );
702             if( i_ret != 0 )
703             {
704                 msg_rc( "%s: returned %i (%s)",
705                          psz_cmd, i_ret, vlc_error( i_ret ) );
706             }
707         }
708         else if( !strcmp( psz_cmd, "logout" ) )
709         {
710             /* Close connection */
711             if( p_intf->p_sys->i_socket != -1 )
712             {
713                 net_Close( p_intf->p_sys->i_socket );
714             }
715             p_intf->p_sys->i_socket = -1;
716         }
717         else if( !strcmp( psz_cmd, "info" ) )
718         {
719             if( p_input )
720             {
721                 int i, j;
722                 vlc_mutex_lock( &p_input->input.p_item->lock );
723                 for ( i = 0; i < p_input->input.p_item->i_categories; i++ )
724                 {
725                     info_category_t *p_category =
726                         p_input->input.p_item->pp_categories[i];
727
728                     msg_rc( "+----[ %s ]", p_category->psz_name );
729                     msg_rc( "| " );
730                     for ( j = 0; j < p_category->i_infos; j++ )
731                     {
732                         info_t *p_info = p_category->pp_infos[j];
733                         msg_rc( "| %s: %s", p_info->psz_name,
734                                 p_info->psz_value );
735                     }
736                     msg_rc( "| " );
737                 }
738                 msg_rc( "+----[ end of stream info ]" );
739                 vlc_mutex_unlock( &p_input->input.p_item->lock );
740             }
741             else
742             {
743                 msg_rc( "no input" );
744             }
745         }
746         else if( !strcmp( psz_cmd, "is_playing" ) )
747         {
748             if( ! p_input )
749             {
750                 msg_rc( "0" );
751             }
752             else
753             {
754                 msg_rc( "1" );
755             }
756         }
757         else if( !strcmp( psz_cmd, "get_time" ) )
758         {
759             if( ! p_input )
760             {
761                 msg_rc("0");
762             }
763             else
764             {
765                 vlc_value_t time;
766                 var_Get( p_input, "time", &time );
767                 msg_rc( "%i", time.i_time / 1000000);
768             }
769         }
770         else if( !strcmp( psz_cmd, "get_length" ) )
771         {
772             if( ! p_input )
773             {
774                 msg_rc("0");
775             }
776             else
777             {
778                 vlc_value_t time;
779                 var_Get( p_input, "length", &time );
780                 msg_rc( "%i", time.i_time / 1000000);
781             }
782         }
783         else if( !strcmp( psz_cmd, "get_title" ) )
784         {
785             if( ! p_input )
786             {
787                 msg_rc("");
788             }
789             else
790             {
791                 msg_rc( "%s", p_input->input.p_item->psz_name );
792             }
793         }
794         else if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "h", 1 )
795                  || !strncmp( psz_cmd, "H", 1 ) || !strncmp( psz_cmd, "?", 1 ) )
796         {
797             if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "H", 1 ) )
798                  b_longhelp = VLC_TRUE;
799             else b_longhelp = VLC_FALSE;
800
801             Help( p_intf, b_longhelp );
802         }
803         else if( !strcmp( psz_cmd, "check-updates" ) )
804         {
805             checkUpdates( p_intf, psz_arg );
806         }
807         else switch( psz_cmd[0] )
808         {
809         case 'f':
810         case 'F':
811             if( p_input )
812             {
813                 vout_thread_t *p_vout;
814                 p_vout = vlc_object_find( p_input,
815                                           VLC_OBJECT_VOUT, FIND_CHILD );
816
817                 if( p_vout )
818                 {
819                     vlc_value_t val;
820                     vlc_bool_t b_update = VLC_FALSE;
821                     var_Get( p_vout, "fullscreen", &val );
822                     val.b_bool = !val.b_bool;
823                     if( !strncmp(psz_arg, "on", 2) && (val.b_bool == VLC_TRUE) )
824                     {
825                         b_update = VLC_TRUE;
826                         val.b_bool = VLC_TRUE;
827                     }
828                     else if( !strncmp(psz_arg, "off", 3)  && (val.b_bool == VLC_FALSE) )
829                     {
830                         b_update = VLC_TRUE;
831                         val.b_bool = VLC_FALSE;
832                     }
833                     else if( strncmp(psz_arg, "off", 3) && strncmp(psz_arg, "on", 2) )
834                         b_update = VLC_TRUE;
835                     if( b_update ) var_Set( p_vout, "fullscreen", val );
836                     vlc_object_release( p_vout );
837                 }
838             }
839             break;
840
841         case 's':
842         case 'S':
843             ;
844             break;
845
846         case '\0':
847             /* Ignore empty lines */
848             break;
849
850         default:
851             msg_rc(_("Unknown command `%s'. Type `help' for help."), psz_cmd);
852             break;
853         }
854
855         /* Command processed */
856         i_size = 0; p_buffer[0] = 0;
857     }
858
859     msg_rc( STATUS_CHANGE "( stop state: 0 )" );
860     msg_rc( STATUS_CHANGE "( quit )" );
861
862     if( p_input )
863     {
864         var_DelCallback( p_input, "state", StateChanged, p_intf );
865         var_DelCallback( p_input, "rate-faster", RateChanged, p_intf );
866         var_DelCallback( p_input, "rate-slower", RateChanged, p_intf );
867         var_DelCallback( p_input, "rate", RateChanged, p_intf );
868         var_DelCallback( p_input, "time-offset", TimeOffsetChanged, p_intf );
869         vlc_object_release( p_input );
870         p_input = NULL;
871     }
872
873     if( p_playlist )
874     {
875         vlc_object_release( p_playlist );
876         p_playlist = NULL;
877     }
878
879     var_DelCallback( p_intf->p_libvlc, "volume-change", VolumeChanged, p_intf );
880 }
881
882 static void Help( intf_thread_t *p_intf, vlc_bool_t b_longhelp)
883 {
884     msg_rc(_("+----[ Remote control commands ]"));
885     msg_rc(  "| ");
886     msg_rc(_("| add XYZ  . . . . . . . . . . add XYZ to playlist"));
887     msg_rc(_("| enqueue XYZ  . . . . . . . queue XYZ to playlist"));
888     msg_rc(_("| playlist . . .  show items currently in playlist"));
889     msg_rc(_("| play . . . . . . . . . . . . . . . . play stream"));
890     msg_rc(_("| stop . . . . . . . . . . . . . . . . stop stream"));
891     msg_rc(_("| next . . . . . . . . . . . .  next playlist item"));
892     msg_rc(_("| prev . . . . . . . . . .  previous playlist item"));
893     msg_rc(_("| goto . . . . . . . . . . . .  goto item at index"));
894     msg_rc(_("| repeat [on|off] . .  toggle playlist item repeat"));
895     msg_rc(_("| loop [on|off] . . . .  toggle playlist item loop"));
896     msg_rc(_("| clear . . . . . . . . . . .   clear the playlist"));
897     msg_rc(_("| status . . . . . . . . . current playlist status"));
898     msg_rc(_("| title [X]  . . . . set/get title in current item"));
899     msg_rc(_("| title_n  . . . . . .  next title in current item"));
900     msg_rc(_("| title_p  . . . .  previous title in current item"));
901     msg_rc(_("| chapter [X]  . . set/get chapter in current item"));
902     msg_rc(_("| chapter_n  . . . .  next chapter in current item"));
903     msg_rc(_("| chapter_p  . .  previous chapter in current item"));
904     msg_rc(  "| ");
905     msg_rc(_("| seek X . seek in seconds, for instance `seek 12'"));
906     msg_rc(_("| pause  . . . . . . . . . . . . . .  toggle pause"));
907     msg_rc(_("| fastforward  . . . . . .  .  set to maximum rate"));
908     msg_rc(_("| rewind  . . . . . . . . . .  set to minimum rate"));
909     msg_rc(_("| faster . . . . . . . .  faster playing of stream"));
910     msg_rc(_("| slower . . . . . . . .  slower playing of stream"));
911     msg_rc(_("| normal . . . . . . . .  normal playing of stream"));
912     msg_rc(_("| f [on|off] . . . . . . . . . . toggle fullscreen"));
913     msg_rc(_("| info . . .  information about the current stream"));
914     msg_rc(_("| get_time . . seconds elapsed since stream's beginning"));
915     msg_rc(_("| is_playing . .  1 if a stream plays, 0 otherwise"));
916     msg_rc(_("| get_title . . .  the title of the current stream"));
917     msg_rc(_("| get_length . .  the length of the current stream"));
918     msg_rc(  "| ");
919     msg_rc(_("| volume [X] . . . . . . . .  set/get audio volume"));
920     msg_rc(_("| volup [X]  . . . . .  raise audio volume X steps"));
921     msg_rc(_("| voldown [X]  . . . .  lower audio volume X steps"));
922     msg_rc(_("| adev [X] . . . . . . . . .  set/get audio device"));
923     msg_rc(_("| achan [X]. . . . . . . .  set/get audio channels"));
924     msg_rc(_("| atrack [X] . . . . . . . . . set/get audio track"));
925     msg_rc(_("| vtrack [X] . . . . . . . . . set/get video track"));
926     msg_rc(_("| vratio [X]  . . . . . set/get video aspect ratio"));
927     msg_rc(_("| vcrop [X]  . . . . . . . . .  set/get video crop"));
928     msg_rc(_("| vzoom [X]  . . . . . . . . .  set/get video zoom"));
929     msg_rc(_("| strack [X] . . . . . . . set/get subtitles track"));
930     msg_rc(_("| menu [on|off|up|down|left|right|select] use menu"));
931     msg_rc(  "| ");
932
933     if (b_longhelp)
934     {
935         msg_rc(_("| marq-marquee STRING  . . overlay STRING in video"));
936         msg_rc(_("| marq-x X . . . . . . . . . . . .offset from left"));
937         msg_rc(_("| marq-y Y . . . . . . . . . . . . offset from top"));
938         msg_rc(_("| marq-position #. . .  .relative position control"));
939         msg_rc(_("| marq-color # . . . . . . . . . . font color, RGB"));
940         msg_rc(_("| marq-opacity # . . . . . . . . . . . . . opacity"));
941         msg_rc(_("| marq-timeout T. . . . . . . . . . timeout, in ms"));
942         msg_rc(_("| marq-size # . . . . . . . . font size, in pixels"));
943         msg_rc(  "| ");
944         msg_rc(_("| logo-file STRING . . .the overlay file path/name"));
945         msg_rc(_("| logo-x X . . . . . . . . . . . .offset from left"));
946         msg_rc(_("| logo-y Y . . . . . . . . . . . . offset from top"));
947         msg_rc(_("| logo-position #. . . . . . . . relative position"));
948         msg_rc(_("| logo-transparency #. . . . . . . . .transparency"));
949         msg_rc(  "| ");
950         msg_rc(_("| mosaic-alpha # . . . . . . . . . . . . . . alpha"));
951         msg_rc(_("| mosaic-height #. . . . . . . . . . . . . .height"));
952         msg_rc(_("| mosaic-width # . . . . . . . . . . . . . . width"));
953         msg_rc(_("| mosaic-xoffset # . . . .top left corner position"));
954         msg_rc(_("| mosaic-yoffset # . . . .top left corner position"));
955         msg_rc(_("| mosaic-offsets x,y(,x,y)*. . . . list of offsets"));
956         msg_rc(_("| mosaic-align 0..2,4..6,8..10. . .mosaic alignment"));
957         msg_rc(_("| mosaic-vborder # . . . . . . . . vertical border"));
958         msg_rc(_("| mosaic-hborder # . . . . . . . horizontal border"));
959         msg_rc(_("| mosaic-position {0=auto,1=fixed} . . . .position"));
960         msg_rc(_("| mosaic-rows #. . . . . . . . . . .number of rows"));
961         msg_rc(_("| mosaic-cols #. . . . . . . . . . .number of cols"));
962         msg_rc(_("| mosaic-order id(,id)* . . . . order of pictures "));
963         msg_rc(_("| mosaic-keep-aspect-ratio {0,1} . . .aspect ratio"));
964         msg_rc(  "| ");
965         msg_rc(_("| check-updates [newer] [equal] [older]\n"
966                  "|               [undef] [info] [source] [binary] [plugin]"));
967         msg_rc(  "| ");
968     }
969     msg_rc(_("| help . . . . . . . . . . . . . this help message"));
970     msg_rc(_("| longhelp . . . . . . . . . a longer help message"));
971     msg_rc(_("| logout . . . . .  exit (if in socket connection)"));
972     msg_rc(_("| quit . . . . . . . . . . . . . . . . .  quit vlc"));
973     msg_rc(  "| ");
974     msg_rc(_("+----[ end of help ]"));
975 }
976
977 /********************************************************************
978  * Status callback routines
979  ********************************************************************/
980 static int TimeOffsetChanged( vlc_object_t *p_this, char const *psz_cmd,
981     vlc_value_t oldval, vlc_value_t newval, void *p_data )
982 {
983     intf_thread_t *p_intf = (intf_thread_t*)p_data;
984     input_thread_t *p_input = NULL;
985
986     vlc_mutex_lock( &p_intf->p_sys->status_lock );
987     p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
988     if( p_input )
989     {
990         msg_rc( STATUS_CHANGE "( time-offset: %d )", var_GetInteger( p_input, "time-offset" ) );
991         vlc_object_release( p_input );
992     }
993     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
994     return VLC_SUCCESS;
995 }
996
997 static int VolumeChanged( vlc_object_t *p_this, char const *psz_cmd,
998     vlc_value_t oldval, vlc_value_t newval, void *p_data )
999 {
1000     intf_thread_t *p_intf = (intf_thread_t*)p_data;
1001
1002     vlc_mutex_lock( &p_intf->p_sys->status_lock );
1003     msg_rc( STATUS_CHANGE "( audio volume: %d )", config_GetInt( p_this, "volume") );
1004     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
1005     return VLC_SUCCESS;
1006 }
1007
1008 static int StateChanged( vlc_object_t *p_this, char const *psz_cmd,
1009     vlc_value_t oldval, vlc_value_t newval, void *p_data )
1010 {
1011     intf_thread_t *p_intf = (intf_thread_t*)p_data;
1012     playlist_t    *p_playlist = NULL;
1013     input_thread_t *p_input = NULL;
1014
1015     vlc_mutex_lock( &p_intf->p_sys->status_lock );
1016     p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1017     if( p_input )
1018     {
1019         p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST, FIND_PARENT );
1020         if( p_playlist )
1021         {
1022             char cmd[5] = "";
1023             switch( p_playlist->status.i_status )
1024             {
1025             case PLAYLIST_STOPPED:
1026                 strncpy( &cmd[0], "stop", 4);
1027                 cmd[4] = '\0';
1028                 break;
1029             case PLAYLIST_RUNNING:
1030                 strncpy( &cmd[0], "play", 4);
1031                 cmd[4] = '\0';
1032                 break;
1033             case PLAYLIST_PAUSED:
1034                 strncpy( &cmd[0], "pause", 5);
1035                 cmd[5] = '\0';
1036                 break;
1037             } /* var_GetInteger( p_input, "state" )  */
1038             msg_rc( STATUS_CHANGE "( %s state: %d )", &cmd[0], newval.i_int );
1039             vlc_object_release( p_playlist );
1040         }
1041         vlc_object_release( p_input );
1042     }
1043     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
1044     return VLC_SUCCESS;
1045 }
1046
1047 static int RateChanged( vlc_object_t *p_this, char const *psz_cmd,
1048     vlc_value_t oldval, vlc_value_t newval, void *p_data )
1049 {
1050     intf_thread_t *p_intf = (intf_thread_t*)p_data;
1051     input_thread_t *p_input = NULL;
1052
1053     vlc_mutex_lock( &p_intf->p_sys->status_lock );
1054     p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1055     if( p_input )
1056     {
1057         msg_rc( STATUS_CHANGE "( new rate: %d )", var_GetInteger( p_input, "rate" ) );
1058         vlc_object_release( p_input );
1059     }
1060     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
1061     return VLC_SUCCESS;
1062 }
1063
1064 /********************************************************************
1065  * Command routines
1066  ********************************************************************/
1067 static int Input( vlc_object_t *p_this, char const *psz_cmd,
1068                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
1069 {
1070     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1071     input_thread_t *p_input;
1072     vlc_value_t     val;
1073
1074     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1075     if( !p_input ) return VLC_ENOOBJ;
1076
1077     var_Get( p_input, "state", &val );
1078     if( ( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) ) &&
1079         ( strcmp( psz_cmd, "pause" ) != 0 ) )
1080     {
1081         msg_rc( _("Press menu select or pause to continue.") );
1082         vlc_object_release( p_input );
1083         return VLC_EGENERIC;
1084     }
1085
1086     /* Parse commands that only require an input */
1087     if( !strcmp( psz_cmd, "pause" ) )
1088     {
1089         val.i_int = config_GetInt( p_intf, "key-play-pause" );
1090         var_Set( p_intf->p_libvlc, "key-pressed", val );
1091         vlc_object_release( p_input );
1092         return VLC_SUCCESS;
1093     }
1094     else if( !strcmp( psz_cmd, "seek" ) )
1095     {
1096         if( strlen( newval.psz_string ) > 0 &&
1097             newval.psz_string[strlen( newval.psz_string ) - 1] == '%' )
1098         {
1099             val.f_float = (float)atoi( newval.psz_string ) / 100.0;
1100             var_Set( p_input, "position", val );
1101         }
1102         else
1103         {
1104             val.i_time = ((int64_t)atoi( newval.psz_string )) * 1000000;
1105             var_Set( p_input, "time", val );
1106         }
1107         vlc_object_release( p_input );
1108         return VLC_SUCCESS;
1109     }
1110     else if ( !strcmp( psz_cmd, "fastforward" ) )
1111     {
1112         val.i_int = config_GetInt( p_intf, "key-jump+extrashort" );
1113         var_Set( p_intf->p_libvlc, "key-pressed", val );
1114         vlc_object_release( p_input );
1115         return VLC_SUCCESS;
1116     }
1117     else if ( !strcmp( psz_cmd, "rewind" ) )
1118     {
1119         val.i_int = config_GetInt( p_intf, "key-jump-extrashort" );
1120         var_Set( p_intf->p_libvlc, "key-pressed", val );
1121         vlc_object_release( p_input );
1122         return VLC_SUCCESS;
1123     }
1124     else if ( !strcmp( psz_cmd, "faster" ) )
1125     {
1126         val.b_bool = VLC_TRUE;
1127         var_Set( p_input, "rate-faster", val );
1128         vlc_object_release( p_input );
1129         return VLC_SUCCESS;
1130     }
1131     else if ( !strcmp( psz_cmd, "slower" ) )
1132     {
1133         val.b_bool = VLC_TRUE;
1134         var_Set( p_input, "rate-slower", val );
1135         vlc_object_release( p_input );
1136         return VLC_SUCCESS;
1137     }
1138     else if ( !strcmp( psz_cmd, "normal" ) )
1139     {
1140         val.i_int = INPUT_RATE_DEFAULT;
1141         var_Set( p_input, "rate", val );
1142         vlc_object_release( p_input );
1143         return VLC_SUCCESS;
1144     }
1145     else if( !strcmp( psz_cmd, "chapter" ) ||
1146              !strcmp( psz_cmd, "chapter_n" ) ||
1147              !strcmp( psz_cmd, "chapter_p" ) )
1148     {
1149         if( !strcmp( psz_cmd, "chapter" ) )
1150         {
1151             if ( *newval.psz_string )
1152             {
1153                 /* Set. */
1154                 val.i_int = atoi( newval.psz_string );
1155                 var_Set( p_input, "chapter", val );
1156             }
1157             else
1158             {
1159                 vlc_value_t val_list;
1160
1161                 /* Get. */
1162                 var_Get( p_input, "chapter", &val );
1163                 var_Change( p_input, "chapter", VLC_VAR_GETCHOICES,
1164                             &val_list, NULL );
1165                 msg_rc( "Currently playing chapter %d/%d.",
1166                         val.i_int, val_list.p_list->i_count );
1167                 var_Change( p_this, "chapter", VLC_VAR_FREELIST,
1168                             &val_list, NULL );
1169             }
1170         }
1171         else if( !strcmp( psz_cmd, "chapter_n" ) )
1172         {
1173             val.b_bool = VLC_TRUE;
1174             var_Set( p_input, "next-chapter", val );
1175         }
1176         else if( !strcmp( psz_cmd, "chapter_p" ) )
1177         {
1178             val.b_bool = VLC_TRUE;
1179             var_Set( p_input, "prev-chapter", val );
1180         }
1181         vlc_object_release( p_input );
1182         return VLC_SUCCESS;
1183     }
1184     else if( !strcmp( psz_cmd, "title" ) ||
1185              !strcmp( psz_cmd, "title_n" ) ||
1186              !strcmp( psz_cmd, "title_p" ) )
1187     {
1188         if( !strcmp( psz_cmd, "title" ) )
1189         {
1190             if ( *newval.psz_string )
1191             {
1192                 /* Set. */
1193                 val.i_int = atoi( newval.psz_string );
1194                 var_Set( p_input, "title", val );
1195             }
1196             else
1197             {
1198                 vlc_value_t val_list;
1199
1200                 /* Get. */
1201                 var_Get( p_input, "title", &val );
1202                 var_Change( p_input, "title", VLC_VAR_GETCHOICES,
1203                             &val_list, NULL );
1204                 msg_rc( "Currently playing title %d/%d.",
1205                         val.i_int, val_list.p_list->i_count );
1206                 var_Change( p_this, "title", VLC_VAR_FREELIST,
1207                             &val_list, NULL );
1208             }
1209         }
1210         else if( !strcmp( psz_cmd, "title_n" ) )
1211         {
1212             val.b_bool = VLC_TRUE;
1213             var_Set( p_input, "next-title", val );
1214         }
1215         else if( !strcmp( psz_cmd, "title_p" ) )
1216         {
1217             val.b_bool = VLC_TRUE;
1218             var_Set( p_input, "prev-title", val );
1219         }
1220
1221         vlc_object_release( p_input );
1222         return VLC_SUCCESS;
1223     }
1224     else if(    !strcmp( psz_cmd, "atrack" )
1225              || !strcmp( psz_cmd, "vtrack" )
1226              || !strcmp( psz_cmd, "strack" ) )
1227     {
1228         char *psz_variable;
1229         vlc_value_t val_name;
1230         int i_error;
1231
1232         if( !strcmp( psz_cmd, "atrack" ) )
1233         {
1234             psz_variable = "audio-es";
1235         }
1236         else if( !strcmp( psz_cmd, "vtrack" ) )
1237         {
1238             psz_variable = "video-es";
1239         }
1240         else
1241         {
1242             psz_variable = "spu-es";
1243         }
1244
1245         /* Get the descriptive name of the variable */
1246         var_Change( p_input, psz_variable, VLC_VAR_GETTEXT,
1247                      &val_name, NULL );
1248         if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1249
1250         if( newval.psz_string && *newval.psz_string )
1251         {
1252             /* set */
1253             vlc_value_t val;
1254             val.i_int = atoi( newval.psz_string );
1255
1256             i_error = var_Set( p_input, psz_variable, val );
1257         }
1258         else
1259         {
1260             /* get */
1261             vlc_value_t val, text;
1262             int i, i_value;
1263
1264             if ( var_Get( p_input, psz_variable, &val ) < 0 )
1265             {
1266                 vlc_object_release( p_input );
1267                 return VLC_EGENERIC;
1268             }
1269             i_value = val.i_int;
1270
1271             if ( var_Change( p_input, psz_variable,
1272                              VLC_VAR_GETLIST, &val, &text ) < 0 )
1273             {
1274                 vlc_object_release( p_input );
1275                 return VLC_EGENERIC;
1276             }
1277
1278             msg_rc( "+----[ %s ]", val_name.psz_string );
1279             for ( i = 0; i < val.p_list->i_count; i++ )
1280             {
1281                 if ( i_value == val.p_list->p_values[i].i_int )
1282                     msg_rc( "| %i - %s *", val.p_list->p_values[i].i_int,
1283                             text.p_list->p_values[i].psz_string );
1284                 else
1285                     msg_rc( "| %i - %s", val.p_list->p_values[i].i_int,
1286                             text.p_list->p_values[i].psz_string );
1287             }
1288             var_Change( p_input, psz_variable, VLC_VAR_FREELIST,
1289                         &val, &text );
1290             msg_rc( "+----[ end of %s ]", val_name.psz_string );
1291
1292             if( val_name.psz_string ) free( val_name.psz_string );
1293
1294             i_error = VLC_SUCCESS;
1295         }
1296         vlc_object_release( p_input );
1297         return i_error;
1298     }
1299
1300     /* Never reached. */
1301     vlc_object_release( p_input );
1302     return VLC_EGENERIC;
1303 }
1304
1305 static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
1306                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
1307 {
1308     vlc_value_t val;
1309
1310     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1311     playlist_t *p_playlist = pl_Yield( p_this );
1312
1313     PL_LOCK;
1314     if( p_playlist->p_input )
1315     {
1316         var_Get( p_playlist->p_input, "state", &val );
1317         if( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) )
1318         {
1319             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
1320             vlc_object_release( p_playlist );
1321             PL_UNLOCK;
1322             return VLC_EGENERIC;
1323         }
1324     }
1325     PL_UNLOCK;
1326
1327     /* Parse commands that require a playlist */
1328     if( !strcmp( psz_cmd, "prev" ) )
1329     {
1330         playlist_Prev( p_playlist );
1331     }
1332     else if( !strcmp( psz_cmd, "next" ) )
1333     {
1334         playlist_Next( p_playlist );
1335     }
1336     else if( !strcmp( psz_cmd, "play" ) )
1337     {
1338         msg_Warn( p_playlist, "play" );
1339         playlist_Play( p_playlist );
1340     }
1341     else if( !strcmp( psz_cmd, "repeat" ) )
1342     {
1343         vlc_bool_t b_update = VLC_TRUE;
1344
1345         var_Get( p_playlist, "repeat", &val );
1346
1347         if( strlen( newval.psz_string ) > 0 )
1348         {
1349             if ( ( !strncmp( newval.psz_string, "on", 2 ) && ( val.b_bool == VLC_TRUE ) ) ||
1350                  ( !strncmp( newval.psz_string, "off", 3 ) && ( val.b_bool == VLC_FALSE ) ) )
1351             {
1352                 b_update = VLC_FALSE;
1353             }
1354         }
1355
1356         if ( b_update )
1357         {
1358             val.b_bool = !val.b_bool;
1359             var_Set( p_playlist, "repeat", val );
1360         }
1361         msg_rc( "Setting repeat to %d", val.b_bool );
1362     }
1363     else if( !strcmp( psz_cmd, "loop" ) )
1364     {
1365         vlc_bool_t b_update = VLC_TRUE;
1366
1367         var_Get( p_playlist, "loop", &val );
1368
1369         if( strlen( newval.psz_string ) > 0 )
1370         {
1371             if ( ( !strncmp( newval.psz_string, "on", 2 ) && ( val.b_bool == VLC_TRUE ) ) ||
1372                  ( !strncmp( newval.psz_string, "off", 3 ) && ( val.b_bool == VLC_FALSE ) ) )
1373             {
1374                 b_update = VLC_FALSE;
1375             }
1376         }
1377
1378         if ( b_update )
1379         {
1380             val.b_bool = !val.b_bool;
1381             var_Set( p_playlist, "loop", val );
1382         }
1383         msg_rc( "Setting loop to %d", val.b_bool );
1384     }
1385     else if (!strcmp( psz_cmd, "goto" ) )
1386     {
1387         msg_rc( _("goto is deprecated" ) );
1388         msg_Err( p_playlist, "goto is deprecated" );
1389     }
1390     else if( !strcmp( psz_cmd, "stop" ) )
1391     {
1392         playlist_Stop( p_playlist );
1393     }
1394     else if( !strcmp( psz_cmd, "clear" ) )
1395     {
1396         playlist_Stop( p_playlist );
1397         vlc_mutex_lock( &p_playlist->object_lock );
1398         playlist_Clear( p_playlist );
1399         vlc_mutex_unlock( &p_playlist->object_lock );
1400     }
1401     else if( !strcmp( psz_cmd, "add" ) &&
1402              newval.psz_string && *newval.psz_string )
1403     {
1404         input_item_t *p_item = parse_MRL( p_intf, newval.psz_string );
1405
1406         if( p_item )
1407         {
1408             msg_rc( "Trying to add %s to playlist.", newval.psz_string );
1409             playlist_PlaylistAddInput( p_playlist, p_item,
1410                               PLAYLIST_GO|PLAYLIST_APPEND, PLAYLIST_END );
1411         }
1412     }
1413     else if( !strcmp( psz_cmd, "enqueue" ) &&
1414              newval.psz_string && *newval.psz_string )
1415     {
1416         input_item_t *p_item = parse_MRL( p_intf, newval.psz_string );
1417
1418         if( p_item )
1419         {
1420             msg_rc( "trying to enqueue %s to playlist", newval.psz_string );
1421             playlist_PlaylistAddInput( p_playlist, p_item,
1422                               PLAYLIST_APPEND, PLAYLIST_END );
1423         }
1424     }
1425     else if( !strcmp( psz_cmd, "playlist" ) )
1426     {
1427         msg_Dbg( p_playlist, "Dumping category" );
1428         playlist_NodeDump( p_playlist, p_playlist->p_root_category, 0 );
1429         msg_Dbg( p_playlist, "Dumping Onelevel" );
1430         playlist_NodeDump( p_playlist, p_playlist->p_root_onelevel, 0 );
1431     }
1432     else if( !strcmp( psz_cmd, "sort" ))
1433     {
1434         playlist_RecursiveNodeSort( p_playlist, p_playlist->p_root_onelevel, 
1435                                     SORT_ARTIST, ORDER_NORMAL );
1436     }
1437     else if( !strcmp( psz_cmd, "status" ) )
1438     {
1439         if( p_playlist->p_input )
1440         {
1441             /* Replay the current state of the system. */
1442             msg_rc( STATUS_CHANGE "( new input: %s )", p_playlist->p_input->input.p_item->psz_uri );
1443             msg_rc( STATUS_CHANGE "( audio volume: %d )", config_GetInt( p_intf, "volume" ));
1444
1445             vlc_mutex_lock( &p_playlist->object_lock );
1446             switch( p_playlist->status.i_status )
1447             {
1448                 case PLAYLIST_STOPPED:
1449                     msg_rc( STATUS_CHANGE "( stop state: 0 )" );
1450                     break;
1451                 case PLAYLIST_RUNNING:
1452                     msg_rc( STATUS_CHANGE "( play state: 1 )" );
1453                     break;
1454                 case PLAYLIST_PAUSED:
1455                     msg_rc( STATUS_CHANGE "( pause state: 2 )" );
1456                     break;
1457                 default:
1458                     msg_rc( STATUS_CHANGE "( state unknown )" );
1459                     break;
1460             }
1461             vlc_mutex_unlock( &p_playlist->object_lock );
1462         }
1463     }
1464
1465     /*
1466      * sanity check
1467      */
1468     else
1469     {
1470         msg_rc( "unknown command!" );
1471     }
1472
1473     vlc_object_release( p_playlist );
1474     return VLC_SUCCESS;
1475 }
1476
1477 static int Other( vlc_object_t *p_this, char const *psz_cmd,
1478                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
1479 {
1480     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1481     vlc_object_t  *p_playlist;
1482     vlc_value_t    val;
1483     vlc_object_t  *p_input;
1484
1485     p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
1486     if( !p_playlist )
1487     {
1488         return VLC_ENOOBJ;
1489     }
1490
1491     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1492     if( !p_input )
1493     {
1494         vlc_object_release( p_playlist );
1495         return VLC_ENOOBJ;
1496     }
1497
1498     if( p_input )
1499     {
1500         var_Get( p_input, "state", &val );
1501         if( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) )
1502         {
1503             msg_rc( _("Type 'pause' to continue.") );
1504             vlc_object_release( p_playlist );
1505             vlc_object_release( p_input );
1506             return VLC_EGENERIC;
1507         }
1508     }
1509
1510     /* Parse miscellaneous commands */
1511     if( !strcmp( psz_cmd, "marq-marquee" ) )
1512     {
1513         if( strlen( newval.psz_string ) > 0 )
1514         {
1515             val.psz_string = newval.psz_string;
1516             var_Set( p_input->p_libvlc_global, "marq-marquee", val );
1517         }
1518         else
1519         {
1520                 val.psz_string = "";
1521                 var_Set( p_input->p_libvlc_global, "marq-marquee", val);
1522         }
1523     }
1524     else if( !strcmp( psz_cmd, "marq-x" ) )
1525     {
1526         if( strlen( newval.psz_string ) > 0)
1527         {
1528             val.i_int = atoi( newval.psz_string );
1529             var_Set( p_input->p_libvlc_global, "marq-x", val );
1530         }
1531     }
1532     else if( !strcmp( psz_cmd, "marq-y" ) )
1533     {
1534         if( strlen( newval.psz_string ) > 0)
1535         {
1536             val.i_int = atoi( newval.psz_string );
1537             var_Set( p_input->p_libvlc_global, "marq-y", val );
1538         }
1539     }
1540     else if( !strcmp( psz_cmd, "marq-position" ) )
1541     {
1542         if( strlen( newval.psz_string ) > 0)
1543         {
1544             val.i_int = atoi( newval.psz_string );
1545             var_Set( p_input->p_libvlc_global, "marq-position", val );
1546         }
1547     }
1548     else if( !strcmp( psz_cmd, "marq-color" ) )
1549     {
1550         if( strlen( newval.psz_string ) > 0)
1551         {
1552             val.i_int = strtol( newval.psz_string, NULL, 0 );
1553             var_Set( p_input->p_libvlc_global, "marq-color", val );
1554         }
1555     }
1556     else if( !strcmp( psz_cmd, "marq-opacity" ) )
1557     {
1558         if( strlen( newval.psz_string ) > 0)
1559         {
1560             val.i_int = strtol( newval.psz_string, NULL, 0 );
1561             var_Set( p_input->p_libvlc_global, "marq-opacity", val );
1562         }
1563     }
1564     else if( !strcmp( psz_cmd, "marq-size" ) )
1565     {
1566         if( strlen( newval.psz_string ) > 0)
1567         {
1568             val.i_int = atoi( newval.psz_string );
1569             var_Set( p_input->p_libvlc_global, "marq-size", val );
1570         }
1571     }
1572     else if( !strcmp( psz_cmd, "marq-timeout" ) )
1573     {
1574         if( strlen( newval.psz_string ) > 0)
1575         {
1576             val.i_int = atoi( newval.psz_string );
1577             var_Set( p_input, "marq-timeout", val );
1578         }
1579     }
1580     else if( !strcmp( psz_cmd, "mosaic-alpha" ) )
1581     {
1582         if( strlen( newval.psz_string ) > 0)
1583         {
1584             val.i_int = atoi( newval.psz_string );
1585             var_Set( p_input->p_libvlc_global, "mosaic-alpha", val );
1586         }
1587     }
1588     else if( !strcmp( psz_cmd, "mosaic-height" ) )
1589     {
1590         if( strlen( newval.psz_string ) > 0)
1591         {
1592             val.i_int = atoi( newval.psz_string );
1593             var_Set( p_input->p_libvlc_global, "mosaic-height", val );
1594         }
1595     }
1596     else if( !strcmp( psz_cmd, "mosaic-width" ) )
1597     {
1598         if( strlen( newval.psz_string ) > 0)
1599         {
1600             val.i_int = atoi( newval.psz_string );
1601             var_Set( p_input->p_libvlc_global, "mosaic-width", val );
1602         }
1603     }
1604     else if( !strcmp( psz_cmd, "mosaic-xoffset" ) )
1605     {
1606         if( strlen( newval.psz_string ) > 0)
1607         {
1608             val.i_int = atoi( newval.psz_string );
1609             var_Set( p_input->p_libvlc_global, "mosaic-xoffset", val );
1610         }
1611     }
1612     else if( !strcmp( psz_cmd, "mosaic-yoffset" ) )
1613     {
1614         if( strlen( newval.psz_string ) > 0)
1615         {
1616             val.i_int = atoi( newval.psz_string );
1617             var_Set( p_input->p_libvlc_global, "mosaic-yoffset", val );
1618         }
1619     }
1620     else if( !strcmp( psz_cmd, "mosaic-align" ) )
1621     {
1622         if( strlen( newval.psz_string ) > 0 )
1623         {
1624             val.i_int = atoi( newval.psz_string );
1625             var_Set( p_input->p_libvlc_global, "mosaic-align", val );
1626         }
1627     }
1628     else if( !strcmp( psz_cmd, "mosaic-vborder" ) )
1629     {
1630         if( strlen( newval.psz_string ) > 0)
1631         {
1632             val.i_int = atoi( newval.psz_string );
1633             var_Set( p_input->p_libvlc_global, "mosaic-vborder", val );
1634         }
1635     }
1636     else if( !strcmp( psz_cmd, "mosaic-hborder" ) )
1637     {
1638         if( strlen( newval.psz_string ) > 0)
1639         {
1640             val.i_int = atoi( newval.psz_string );
1641             var_Set( p_input->p_libvlc_global, "mosaic-hborder", val );
1642         }
1643     }
1644     else if( !strcmp( psz_cmd, "mosaic-position" ) )
1645     {
1646         if( strlen( newval.psz_string ) > 0)
1647         {
1648             val.i_int = atoi( newval.psz_string );
1649             var_Set( p_input->p_libvlc_global, "mosaic-position", val );
1650         }
1651     }
1652     else if( !strcmp( psz_cmd, "mosaic-rows" ) )
1653     {
1654         if( strlen( newval.psz_string ) > 0)
1655         {
1656             val.i_int = atoi( newval.psz_string );
1657             var_Set( p_input->p_libvlc_global, "mosaic-rows", val );
1658         }
1659     }
1660     else if( !strcmp( psz_cmd, "mosaic-cols" ) )
1661     {
1662         if( strlen( newval.psz_string ) > 0)
1663         {
1664             val.i_int = atoi( newval.psz_string );
1665             var_Set( p_input->p_libvlc_global, "mosaic-cols", val );
1666         }
1667     }
1668     else if( !strcmp( psz_cmd, "mosaic-order" ) )
1669     {
1670         if( strlen( newval.psz_string ) > 0)
1671         {
1672             val.psz_string = newval.psz_string;
1673             var_Set( p_input->p_libvlc_global, "mosaic-order", val );
1674         }
1675     }
1676     else if( !strcmp( psz_cmd, "mosaic-offsets" ) )
1677     {
1678         if( strlen( newval.psz_string ) > 0)
1679         {
1680             val.psz_string = newval.psz_string;
1681             var_Set( p_input->p_libvlc_global, "mosaic-offsets", val );
1682         }
1683     }
1684     else if( !strcmp( psz_cmd, "mosaic-keep-aspect-ratio" ) )
1685     {
1686         if( strlen( newval.psz_string ) > 0)
1687         {
1688             val.i_int = atoi( newval.psz_string );
1689             var_Set( p_input->p_libvlc_global, "mosaic-keep-aspect-ratio", val );
1690         }
1691     }
1692     else if( !strcmp( psz_cmd, "logo-file" ) )
1693     {
1694         if( strlen( newval.psz_string ) > 0 )
1695         {
1696             val.psz_string = newval.psz_string;
1697             var_Set( p_input->p_libvlc_global, "logo-file", val );
1698         }
1699     }
1700     else if( !strcmp( psz_cmd, "logo-x" ) )
1701     {
1702         if( strlen( newval.psz_string ) > 0)
1703         {
1704             val.i_int = atoi( newval.psz_string );
1705             var_Set( p_input->p_libvlc_global, "logo-x", val );
1706         }
1707     }
1708     else if( !strcmp( psz_cmd, "logo-y" ) )
1709     {
1710         if( strlen( newval.psz_string ) > 0)
1711         {
1712             val.i_int = atoi( newval.psz_string );
1713             var_Set( p_input->p_libvlc_global, "logo-y", val );
1714         }
1715     }
1716     else if( !strcmp( psz_cmd, "logo-position" ) )
1717     {
1718         if( strlen( newval.psz_string ) > 0)
1719         {
1720             val.i_int = atoi( newval.psz_string );
1721             var_Set( p_input->p_libvlc_global, "logo-position", val );
1722         }
1723     }
1724     else if( !strcmp( psz_cmd, "logo-transparency" ) )
1725     {
1726         if( strlen( newval.psz_string ) > 0)
1727         {
1728             val.i_int = strtol( newval.psz_string, NULL, 0 );
1729             var_Set( p_input->p_libvlc_global, "logo-transparency", val );
1730         }
1731     }
1732
1733     /*
1734      * sanity check
1735      */
1736     else
1737     {
1738         msg_rc( "Unknown command!" );
1739     }
1740
1741     vlc_object_release( p_playlist );
1742     vlc_object_release( p_input );
1743     return VLC_SUCCESS;
1744 }
1745
1746 static int Quit( vlc_object_t *p_this, char const *psz_cmd,
1747                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1748 {
1749     playlist_t *p_playlist;
1750
1751     p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
1752     if( p_playlist )
1753     {
1754         playlist_Stop( p_playlist );
1755         vlc_object_release( p_playlist );
1756     }
1757     p_this->p_libvlc->b_die = VLC_TRUE;
1758     return VLC_SUCCESS;
1759 }
1760
1761 static int Intf( vlc_object_t *p_this, char const *psz_cmd,
1762                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1763 {
1764     intf_thread_t *p_newintf = NULL;
1765
1766     p_newintf = intf_Create( p_this->p_libvlc, newval.psz_string, 0, NULL );
1767     if( p_newintf )
1768     {
1769         p_newintf->b_block = VLC_FALSE;
1770         if( intf_RunThread( p_newintf ) )
1771         {
1772             vlc_object_detach( p_newintf );
1773             intf_Destroy( p_newintf );
1774         }
1775     }
1776
1777     return VLC_SUCCESS;
1778 }
1779
1780 static int Volume( vlc_object_t *p_this, char const *psz_cmd,
1781                    vlc_value_t oldval, vlc_value_t newval, void *p_data )
1782 {
1783     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1784     input_thread_t *p_input = NULL;
1785     int i_error = VLC_EGENERIC;
1786
1787     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1788     if( !p_input )
1789         return VLC_ENOOBJ;
1790
1791     if( p_input )
1792     {
1793         vlc_value_t val;
1794
1795         var_Get( p_input, "state", &val );
1796         if( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) )
1797         {
1798             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
1799             vlc_object_release( p_input );
1800             return VLC_EGENERIC;
1801         }
1802         vlc_object_release( p_input );
1803     }
1804
1805     if ( *newval.psz_string )
1806     {
1807         /* Set. */
1808         audio_volume_t i_volume = atoi( newval.psz_string );
1809         if ( (i_volume > (audio_volume_t)AOUT_VOLUME_MAX) )
1810         {
1811             msg_rc( "Volume must be in the range %d-%d.", AOUT_VOLUME_MIN,
1812                     AOUT_VOLUME_MAX );
1813             i_error = VLC_EBADVAR;
1814         }
1815         else
1816         {
1817             if( i_volume == AOUT_VOLUME_MIN )
1818             {
1819                 vlc_value_t keyval;
1820
1821                 keyval.i_int = config_GetInt( p_intf, "key-vol-mute" );
1822                 var_Set( p_intf->p_libvlc, "key-pressed", keyval );
1823             }
1824             i_error = aout_VolumeSet( p_this, i_volume );
1825             osd_Volume( p_this );
1826             msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
1827         }
1828     }
1829     else
1830     {
1831         /* Get. */
1832         audio_volume_t i_volume;
1833         if ( aout_VolumeGet( p_this, &i_volume ) < 0 )
1834         {
1835             i_error = VLC_EGENERIC;
1836         }
1837         else
1838         {
1839             msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
1840             i_error = VLC_SUCCESS;
1841         }
1842     }
1843
1844     return i_error;
1845 }
1846
1847 static int VolumeMove( vlc_object_t *p_this, char const *psz_cmd,
1848                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1849 {
1850     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1851     audio_volume_t i_volume;
1852     input_thread_t *p_input = NULL;
1853     int i_nb_steps = atoi(newval.psz_string);
1854     int i_error = VLC_SUCCESS;
1855     int i_volume_step = 0;
1856
1857     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1858     if( !p_input )
1859         return VLC_ENOOBJ;
1860
1861     if( p_input )
1862     {
1863         vlc_value_t val;
1864
1865         var_Get( p_input, "state", &val );
1866         if( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) )
1867         {
1868             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
1869             vlc_object_release( p_input );
1870             return VLC_EGENERIC;
1871         }
1872         vlc_object_release( p_input );
1873     }
1874
1875     i_volume_step = config_GetInt( p_intf->p_libvlc, "volume-step" );
1876     if ( i_nb_steps <= 0 || i_nb_steps > (AOUT_VOLUME_MAX/i_volume_step) )
1877     {
1878         i_nb_steps = 1;
1879     }
1880
1881     if ( !strcmp(psz_cmd, "volup") )
1882     {
1883         if ( aout_VolumeUp( p_this, i_nb_steps, &i_volume ) < 0 )
1884             i_error = VLC_EGENERIC;
1885     }
1886     else
1887     {
1888         if ( aout_VolumeDown( p_this, i_nb_steps, &i_volume ) < 0 )
1889             i_error = VLC_EGENERIC;
1890     }
1891     osd_Volume( p_this );
1892
1893     if ( !i_error ) msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
1894     return i_error;
1895 }
1896
1897
1898 static int VideoConfig( vlc_object_t *p_this, char const *psz_cmd,
1899                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
1900 {
1901     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1902     input_thread_t *p_input = NULL;
1903     vout_thread_t * p_vout;
1904     const char * psz_variable;
1905     vlc_value_t val_name;
1906     int i_error;
1907
1908     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1909     if( !p_input )
1910         return VLC_ENOOBJ;
1911
1912     p_vout = vlc_object_find( p_input, VLC_OBJECT_VOUT, FIND_CHILD );
1913     vlc_object_release( p_input );
1914     if( !p_vout )
1915         return VLC_ENOOBJ;
1916
1917     if( !strcmp( psz_cmd, "vcrop" ) )
1918     {
1919         psz_variable = "crop";
1920     }
1921     else if( !strcmp( psz_cmd, "vratio" ) )
1922     {
1923         psz_variable = "aspect-ratio";
1924     }
1925     else /* if( !strcmp( psz_cmd, "vzoom" ) ) */
1926     {
1927         psz_variable = "zoom";
1928     }
1929
1930
1931     /* Get the descriptive name of the variable */
1932     var_Change( p_vout, psz_variable, VLC_VAR_GETTEXT,
1933                  &val_name, NULL );
1934     if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1935
1936     if( newval.psz_string && *newval.psz_string )
1937     {
1938         /* set */
1939         if( !strcmp( psz_variable, "zoom" ) )
1940         {
1941             vlc_value_t val;
1942             val.f_float = atof( newval.psz_string );
1943             i_error = var_Set( p_vout, psz_variable, val );
1944         }
1945         else
1946         {
1947             i_error = var_Set( p_vout, psz_variable, newval );
1948         }
1949     }
1950     else
1951     {
1952         /* get */
1953         vlc_value_t val, text;
1954         int i;
1955         float f_value = 0.;
1956         char *psz_value = NULL;
1957
1958         if ( var_Get( p_vout, psz_variable, &val ) < 0 )
1959         {
1960             vlc_object_release( p_vout );
1961             return VLC_EGENERIC;
1962         }
1963         if( !strcmp( psz_variable, "zoom" ) )
1964         {
1965             f_value = val.f_float;
1966         }
1967         else
1968         {
1969             psz_value = strdup( val.psz_string );
1970         }
1971
1972         if ( var_Change( p_vout, psz_variable,
1973                          VLC_VAR_GETLIST, &val, &text ) < 0 )
1974         {
1975             vlc_object_release( p_vout );
1976             return VLC_EGENERIC;
1977         }
1978
1979         msg_rc( "+----[ %s ]", val_name.psz_string );
1980         if( !strcmp( psz_variable, "zoom" ) )
1981         {
1982             for ( i = 0; i < val.p_list->i_count; i++ )
1983             {
1984                 if ( f_value == val.p_list->p_values[i].f_float )
1985                     msg_rc( "| %f - %s *", val.p_list->p_values[i].f_float,
1986                             text.p_list->p_values[i].psz_string );
1987                 else
1988                     msg_rc( "| %f - %s", val.p_list->p_values[i].f_float,
1989                             text.p_list->p_values[i].psz_string );
1990             }
1991         }
1992         else
1993         {
1994             for ( i = 0; i < val.p_list->i_count; i++ )
1995             {
1996                 if ( !strcmp( psz_value, val.p_list->p_values[i].psz_string ) )
1997                     msg_rc( "| %s - %s *", val.p_list->p_values[i].psz_string,
1998                             text.p_list->p_values[i].psz_string );
1999                 else
2000                     msg_rc( "| %s - %s", val.p_list->p_values[i].psz_string,
2001                             text.p_list->p_values[i].psz_string );
2002             }
2003             free( psz_value );
2004         }
2005         var_Change( p_vout, psz_variable, VLC_VAR_FREELIST,
2006                     &val, &text );
2007         msg_rc( "+----[ end of %s ]", val_name.psz_string );
2008
2009         if( val_name.psz_string ) free( val_name.psz_string );
2010
2011         i_error = VLC_SUCCESS;
2012     }
2013     vlc_object_release( p_vout );
2014     return i_error;
2015 }
2016
2017 static int AudioConfig( vlc_object_t *p_this, char const *psz_cmd,
2018                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
2019 {
2020     intf_thread_t *p_intf = (intf_thread_t*)p_this;
2021     input_thread_t *p_input = NULL;
2022     aout_instance_t * p_aout;
2023     const char * psz_variable;
2024     vlc_value_t val_name;
2025     int i_error;
2026
2027     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
2028     if( !p_input )
2029         return VLC_ENOOBJ;
2030
2031     if( p_input )
2032     {
2033         vlc_value_t val;
2034
2035         var_Get( p_input, "state", &val );
2036         if( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) )        {
2037             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
2038             vlc_object_release( p_input );
2039             return VLC_EGENERIC;
2040         }
2041         vlc_object_release( p_input );
2042     }
2043
2044     p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, FIND_ANYWHERE );
2045     if ( p_aout == NULL ) return VLC_ENOOBJ;
2046
2047     if ( !strcmp( psz_cmd, "adev" ) )
2048     {
2049         psz_variable = "audio-device";
2050     }
2051     else
2052     {
2053         psz_variable = "audio-channels";
2054     }
2055
2056     /* Get the descriptive name of the variable */
2057     var_Change( (vlc_object_t *)p_aout, psz_variable, VLC_VAR_GETTEXT,
2058                  &val_name, NULL );
2059     if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
2060
2061     if ( !*newval.psz_string )
2062     {
2063         /* Retrieve all registered ***. */
2064         vlc_value_t val, text;
2065         int i, i_value;
2066
2067         if ( var_Get( (vlc_object_t *)p_aout, psz_variable, &val ) < 0 )
2068         {
2069             vlc_object_release( (vlc_object_t *)p_aout );
2070             return VLC_EGENERIC;
2071         }
2072         i_value = val.i_int;
2073
2074         if ( var_Change( (vlc_object_t *)p_aout, psz_variable,
2075                          VLC_VAR_GETLIST, &val, &text ) < 0 )
2076         {
2077             vlc_object_release( (vlc_object_t *)p_aout );
2078             return VLC_EGENERIC;
2079         }
2080
2081         msg_rc( "+----[ %s ]", val_name.psz_string );
2082         for ( i = 0; i < val.p_list->i_count; i++ )
2083         {
2084             if ( i_value == val.p_list->p_values[i].i_int )
2085                 msg_rc( "| %i - %s *", val.p_list->p_values[i].i_int,
2086                         text.p_list->p_values[i].psz_string );
2087             else
2088                 msg_rc( "| %i - %s", val.p_list->p_values[i].i_int,
2089                         text.p_list->p_values[i].psz_string );
2090         }
2091         var_Change( (vlc_object_t *)p_aout, psz_variable, VLC_VAR_FREELIST,
2092                     &val, &text );
2093         msg_rc( "+----[ end of %s ]", val_name.psz_string );
2094
2095         if( val_name.psz_string ) free( val_name.psz_string );
2096         i_error = VLC_SUCCESS;
2097     }
2098     else
2099     {
2100         vlc_value_t val;
2101         val.i_int = atoi( newval.psz_string );
2102
2103         i_error = var_Set( (vlc_object_t *)p_aout, psz_variable, val );
2104     }
2105     vlc_object_release( (vlc_object_t *)p_aout );
2106
2107     return i_error;
2108 }
2109
2110 /* OSD menu commands */
2111 static int Menu( vlc_object_t *p_this, char const *psz_cmd,
2112     vlc_value_t oldval, vlc_value_t newval, void *p_data )
2113 {
2114     intf_thread_t *p_intf = (intf_thread_t*)p_this;
2115     playlist_t    *p_playlist = NULL;
2116     vlc_value_t val;
2117     int i_error = VLC_EGENERIC;
2118
2119     if ( !*newval.psz_string )
2120     {
2121         msg_rc( _("Please provide one of the following parameters:") );
2122         msg_rc( "[on|off|up|down|left|right|select]" );
2123         return i_error;
2124     }
2125
2126     p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
2127     if( !p_playlist )
2128         return VLC_ENOOBJ;
2129
2130     if( p_playlist->p_input )
2131     {
2132         var_Get( p_playlist->p_input, "state", &val );
2133         if( ( ( val.i_int == PAUSE_S ) || ( val.i_int == PLAYLIST_PAUSED ) ) &&
2134             ( strcmp( newval.psz_string, "select" ) != 0 ) )
2135         {
2136             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
2137             vlc_object_release( p_playlist );
2138             return VLC_EGENERIC;
2139         }
2140     }
2141     vlc_object_release( p_playlist );
2142
2143     val.psz_string = strdup( newval.psz_string );
2144     if( !strcmp( val.psz_string, "on" ) || !strcmp( val.psz_string, "show" ))
2145         osd_MenuShow( p_this );
2146     else if( !strcmp( val.psz_string, "off" ) || !strcmp( val.psz_string, "hide" ) )
2147         osd_MenuHide( p_this );
2148     else if( !strcmp( val.psz_string, "up" ) )
2149         osd_MenuUp( p_this );
2150     else if( !strcmp( val.psz_string, "down" ) )
2151         osd_MenuDown( p_this );
2152     else if( !strcmp( val.psz_string, "left" ) )
2153         osd_MenuPrev( p_this );
2154     else if( !strcmp( val.psz_string, "right" ) )
2155         osd_MenuNext( p_this );
2156     else if( !strcmp( val.psz_string, "select" ) )
2157         osd_MenuActivate( p_this );
2158     else
2159     {
2160         msg_rc( _("Please provide one of the following parameters:") );
2161         msg_rc( "[on|off|up|down|left|right|select]" );
2162         if( val.psz_string ) free( val.psz_string );
2163             return i_error;
2164     }
2165
2166     i_error = VLC_SUCCESS;
2167     if( val.psz_string ) free( val.psz_string );
2168     return i_error;
2169 }
2170
2171 #ifdef WIN32
2172 vlc_bool_t ReadWin32( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
2173 {
2174     INPUT_RECORD input_record;
2175     DWORD i_dw;
2176
2177     /* On Win32, select() only works on socket descriptors */
2178     while( WaitForSingleObject( p_intf->p_sys->hConsoleIn,
2179                                 INTF_IDLE_SLEEP/1000 ) == WAIT_OBJECT_0 )
2180     {
2181         while( !intf_ShouldDie( p_intf ) && *pi_size < MAX_LINE_LENGTH &&
2182                ReadConsoleInput( p_intf->p_sys->hConsoleIn, &input_record,
2183                                  1, &i_dw ) )
2184         {
2185             if( input_record.EventType != KEY_EVENT ||
2186                 !input_record.Event.KeyEvent.bKeyDown ||
2187                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
2188                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL||
2189                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_MENU ||
2190                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CAPITAL )
2191             {
2192                 /* nothing interesting */
2193                 continue;
2194             }
2195
2196             p_buffer[ *pi_size ] = input_record.Event.KeyEvent.uChar.AsciiChar;
2197
2198             /* Echo out the command */
2199             putc( p_buffer[ *pi_size ], stdout );
2200
2201             /* Handle special keys */
2202             if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
2203             {
2204                 putc( '\n', stdout );
2205                 break;
2206             }
2207             switch( p_buffer[ *pi_size ] )
2208             {
2209             case '\b':
2210                 if( *pi_size )
2211                 {
2212                     *pi_size -= 2;
2213                     putc( ' ', stdout );
2214                     putc( '\b', stdout );
2215                 }
2216                 break;
2217             case '\r':
2218                 (*pi_size) --;
2219                 break;
2220             }
2221
2222             (*pi_size)++;
2223         }
2224
2225         if( *pi_size == MAX_LINE_LENGTH ||
2226             p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
2227         {
2228             p_buffer[ *pi_size ] = 0;
2229             return VLC_TRUE;
2230         }
2231     }
2232
2233     return VLC_FALSE;
2234 }
2235 #endif
2236
2237 vlc_bool_t ReadCommand( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
2238 {
2239     int i_read = 0;
2240
2241 #ifdef WIN32
2242     if( p_intf->p_sys->i_socket == -1 && !p_intf->p_sys->b_quiet )
2243         return ReadWin32( p_intf, p_buffer, pi_size );
2244     else if( p_intf->p_sys->i_socket == -1 )
2245     {
2246         msleep( INTF_IDLE_SLEEP );
2247         return VLC_FALSE;
2248     }
2249 #endif
2250
2251     while( !intf_ShouldDie( p_intf ) && *pi_size < MAX_LINE_LENGTH &&
2252            (i_read = net_ReadNonBlock( p_intf, p_intf->p_sys->i_socket == -1 ?
2253                        0 /*STDIN_FILENO*/ : p_intf->p_sys->i_socket, NULL,
2254                   (uint8_t *)p_buffer + *pi_size, 1, INTF_IDLE_SLEEP ) ) > 0 )
2255     {
2256         if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
2257             break;
2258
2259         (*pi_size)++;
2260     }
2261
2262     /* Connection closed */
2263     if( i_read == -1 )
2264     {
2265         p_intf->p_sys->i_socket = -1;
2266         p_buffer[ *pi_size ] = 0;
2267         return VLC_TRUE;
2268     }
2269
2270     if( *pi_size == MAX_LINE_LENGTH ||
2271         p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
2272     {
2273         p_buffer[ *pi_size ] = 0;
2274         return VLC_TRUE;
2275     }
2276
2277     return VLC_FALSE;
2278 }
2279
2280 /*****************************************************************************
2281  * parse_MRL: build a input item from a full mrl
2282  *****************************************************************************
2283  * MRL format: "simplified-mrl [:option-name[=option-value]]"
2284  * We don't check for '"' or '\'', we just assume that a ':' that follows a
2285  * space is a new option. Should be good enough for our purpose.
2286  *****************************************************************************/
2287 static input_item_t *parse_MRL( intf_thread_t *p_intf, char *psz_mrl )
2288 {
2289 #define SKIPSPACE( p ) { while( *p && ( *p == ' ' || *p == '\t' ) ) p++; }
2290 #define SKIPTRAILINGSPACE( p, d ) \
2291     { char *e=d; while( e > p && (*(e-1)==' ' || *(e-1)=='\t') ){e--;*e=0;} }
2292
2293     input_item_t *p_item = NULL;
2294     char *psz_item = NULL, *psz_item_mrl = NULL, *psz_orig;
2295     char **ppsz_options = NULL;
2296     int i, i_options = 0;
2297
2298     if( !psz_mrl ) return 0;
2299
2300     psz_mrl = psz_orig = strdup( psz_mrl );
2301     while( *psz_mrl )
2302     {
2303         SKIPSPACE( psz_mrl );
2304         psz_item = psz_mrl;
2305
2306         for( ; *psz_mrl; psz_mrl++ )
2307         {
2308             if( (*psz_mrl == ' ' || *psz_mrl == '\t') && psz_mrl[1] == ':' )
2309             {
2310                 /* We have a complete item */
2311                 break;
2312             }
2313             if( (*psz_mrl == ' ' || *psz_mrl == '\t') &&
2314                 (psz_mrl[1] == '"' || psz_mrl[1] == '\'') && psz_mrl[2] == ':')
2315             {
2316                 /* We have a complete item */
2317                 break;
2318             }
2319         }
2320
2321         if( *psz_mrl ) { *psz_mrl = 0; psz_mrl++; }
2322         SKIPTRAILINGSPACE( psz_item, psz_item + strlen( psz_item ) );
2323
2324         /* Remove '"' and '\'' if necessary */
2325         if( *psz_item == '"' && psz_item[strlen(psz_item)-1] == '"' )
2326         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
2327         if( *psz_item == '\'' && psz_item[strlen(psz_item)-1] == '\'' )
2328         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
2329
2330         if( !psz_item_mrl ) psz_item_mrl = psz_item;
2331         else if( *psz_item )
2332         {
2333             i_options++;
2334             ppsz_options = realloc( ppsz_options, i_options * sizeof(char *) );
2335             ppsz_options[i_options - 1] = &psz_item[1];
2336         }
2337
2338         if( *psz_mrl ) SKIPSPACE( psz_mrl );
2339     }
2340
2341     /* Now create a playlist item */
2342     if( psz_item_mrl )
2343     {
2344         p_item = input_ItemNew( p_intf, psz_item_mrl, NULL );
2345         for( i = 0; i < i_options; i++ )
2346         {
2347             input_ItemAddOption( p_item, ppsz_options[i] );
2348         }
2349     }
2350
2351     if( i_options ) free( ppsz_options );
2352     free( psz_orig );
2353
2354     return p_item;
2355 }
2356
2357 /*****************************************************************************
2358  * checkUpdates : check for updates
2359  ****************************************************************************/
2360 static void checkUpdates( intf_thread_t *p_intf, char *psz_arg )
2361 {
2362     update_iterator_t *p_uit;
2363     update_t *p_u = update_New( p_intf );
2364     if( p_u == NULL ) return;
2365     p_uit = update_iterator_New( p_u );
2366     if( p_uit )
2367     {
2368         int s = 0, t = 0;
2369
2370         if( strstr( psz_arg, "newer" ) )
2371             s |= UPDATE_RELEASE_STATUS_NEWER;
2372         if( strstr( psz_arg, "equal" ) )
2373             s |= UPDATE_RELEASE_STATUS_EQUAL;
2374         if( strstr( psz_arg, "older" ) )
2375             s |= UPDATE_RELEASE_STATUS_OLDER;
2376         if( s ) p_uit->i_rs = s;
2377         else p_uit->i_rs = UPDATE_RELEASE_STATUS_NEWER;
2378
2379         if( strstr( psz_arg, "undef" ) )
2380             t |= UPDATE_FILE_TYPE_UNDEF;
2381         if( strstr( psz_arg, "info" ) )
2382             t |= UPDATE_FILE_TYPE_INFO;
2383         if( strstr( psz_arg, "source" ) )
2384             t |= UPDATE_FILE_TYPE_SOURCE;
2385         if( strstr( psz_arg, "binary" ) )
2386             t |= UPDATE_FILE_TYPE_BINARY;
2387         if( strstr( psz_arg, "plugin" ) )
2388             t |= UPDATE_FILE_TYPE_PLUGIN;
2389         if( t ) p_uit->i_t = t;
2390
2391         update_Check( p_u, VLC_FALSE );
2392         update_iterator_Action( p_uit, UPDATE_MIRROR );
2393         msg_rc( "\nUsing mirror: %s (%s) [%s]",
2394                 p_uit->mirror.psz_name,
2395                 p_uit->mirror.psz_location,
2396                 p_uit->mirror.psz_type );
2397         while( (s = update_iterator_Action( p_uit, UPDATE_FILE )) != UPDATE_FAIL )
2398         {
2399             char *psz_tmp;
2400             if( s & UPDATE_RELEASE )
2401             {
2402                 switch( p_uit->release.i_status )
2403                 {
2404                     case UPDATE_RELEASE_STATUS_OLDER:
2405                         psz_tmp = strdup( "older" );
2406                         break;
2407                     case UPDATE_RELEASE_STATUS_EQUAL:
2408                         psz_tmp = strdup( "equal" );
2409                         break;
2410                     case UPDATE_RELEASE_STATUS_NEWER:
2411                         psz_tmp = strdup( "newer" );
2412                         break;
2413                     default:
2414                         psz_tmp = strdup( "?!?" );
2415                         break;
2416                 }
2417                 msg_rc( "\n+----[ VLC %s %s (%s) ] ",
2418                         p_uit->release.psz_version,
2419                         p_uit->release.psz_svn_revision,
2420                         psz_tmp );
2421                 free( psz_tmp );
2422             }
2423             switch( p_uit->file.i_type )
2424             {
2425                 case UPDATE_FILE_TYPE_UNDEF:
2426                     psz_tmp = strdup( "undef" );
2427                     break;
2428                 case UPDATE_FILE_TYPE_INFO:
2429                     psz_tmp = strdup( "info" );
2430                     break;
2431                 case UPDATE_FILE_TYPE_SOURCE:
2432                     psz_tmp = strdup( "source" );
2433                     break;
2434                 case UPDATE_FILE_TYPE_BINARY:
2435                     psz_tmp = strdup( "binary" );
2436                     break;
2437                 case UPDATE_FILE_TYPE_PLUGIN:
2438                     psz_tmp = strdup( "plugin" );
2439                     break;
2440                 default:
2441                     psz_tmp = strdup( "?!?" );
2442                     break;
2443             }
2444             msg_rc( "| %s (%s)", p_uit->file.psz_description, psz_tmp );
2445             free( psz_tmp );
2446             if( p_uit->file.l_size )
2447             {
2448                 if( p_uit->file.l_size > 1024 * 1024 * 1024 )
2449                     asprintf( &psz_tmp, "(%ld GB)",
2450                               p_uit->file.l_size / (1024*1024*1024) );
2451                 if( p_uit->file.l_size > 1024 * 1024 )
2452                     asprintf( &psz_tmp, "(%ld MB)",
2453                               p_uit->file.l_size / (1024*1024) );
2454                 else if( p_uit->file.l_size > 1024 )
2455                     asprintf( &psz_tmp, "(%ld kB)",
2456                               p_uit->file.l_size / 1024 );
2457                 else
2458                     asprintf( &psz_tmp, "(%ld B)", p_uit->file.l_size );
2459             }
2460             else
2461             {
2462                 psz_tmp = strdup( "" );
2463             }
2464             msg_rc( "| %s %s", p_uit->file.psz_url, psz_tmp );
2465             msg_rc( "+----" );
2466             free( psz_tmp );
2467         }
2468         msg_rc( "" );
2469         update_iterator_Delete( p_uit );
2470     }
2471     update_Delete( p_u );
2472 }