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