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