565f7fc6c7e2e9917793b323be446eb899e209d0
[vlc.git] / modules / control / rc.c
1 /*****************************************************************************
2  * rc.c : remote control stdin/stdout module for vlc
3  *****************************************************************************
4  * Copyright (C) 2004-2009 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
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35
36 #include <errno.h>                                                 /* ENOMEM */
37 #include <ctype.h>
38 #include <signal.h>
39 #include <assert.h>
40
41 #include <vlc_interface.h>
42 #include <vlc_aout.h>
43 #include <vlc_vout.h>
44 #include <vlc_osd.h>
45 #include <vlc_playlist.h>
46 #include <vlc_keys.h>
47
48 #ifdef HAVE_UNISTD_H
49 #    include <unistd.h>
50 #endif
51
52 #ifdef HAVE_SYS_TIME_H
53 #    include <sys/time.h>
54 #endif
55 #include <sys/types.h>
56
57 #include <vlc_network.h>
58 #include "vlc_url.h"
59
60 #include <vlc_charset.h>
61
62 #if defined(AF_UNIX) && !defined(AF_LOCAL)
63 #    define AF_LOCAL AF_UNIX
64 #endif
65
66 #if defined(AF_LOCAL) && ! defined(WIN32)
67 #    include <sys/un.h>
68 #endif
69
70 #define MAX_LINE_LENGTH 1024
71 #define STATUS_CHANGE "status change: "
72
73 /* input_state_e from <vlc_input.h> */
74 static const char *ppsz_input_state[] = {
75     [INIT_S] = N_("Initializing"),
76     [OPENING_S] = N_("Opening"),
77     [PLAYING_S] = N_("Play"),
78     [PAUSE_S] = N_("Pause"),
79     [END_S] = N_("End"),
80     [ERROR_S] = N_("Error"),
81 };
82
83 /*****************************************************************************
84  * Local prototypes
85  *****************************************************************************/
86 static int  Activate     ( vlc_object_t * );
87 static void Deactivate   ( vlc_object_t * );
88 static void Run          ( intf_thread_t * );
89
90 static void Help         ( intf_thread_t *, bool );
91 static void RegisterCallbacks( intf_thread_t * );
92
93 static bool ReadCommand( intf_thread_t *, char *, int * );
94
95 static input_item_t *parse_MRL( intf_thread_t *, char * );
96
97 static int  Input        ( vlc_object_t *, char const *,
98                            vlc_value_t, vlc_value_t, void * );
99 static int  Playlist     ( vlc_object_t *, char const *,
100                            vlc_value_t, vlc_value_t, void * );
101 static int  Quit         ( vlc_object_t *, char const *,
102                            vlc_value_t, vlc_value_t, void * );
103 static int  Intf         ( vlc_object_t *, char const *,
104                            vlc_value_t, vlc_value_t, void * );
105 static int  Volume       ( vlc_object_t *, char const *,
106                            vlc_value_t, vlc_value_t, void * );
107 static int  VolumeMove   ( vlc_object_t *, char const *,
108                            vlc_value_t, vlc_value_t, void * );
109 static int  VideoConfig  ( vlc_object_t *, char const *,
110                            vlc_value_t, vlc_value_t, void * );
111 static int  AudioConfig  ( vlc_object_t *, char const *,
112                            vlc_value_t, vlc_value_t, void * );
113 static int  Menu         ( vlc_object_t *, char const *,
114                            vlc_value_t, vlc_value_t, void * );
115 static int  Statistics   ( vlc_object_t *, char const *,
116                            vlc_value_t, vlc_value_t, void * );
117
118 static int updateStatistics( intf_thread_t *, input_item_t *);
119
120 /* Status Callbacks */
121 static int TimeOffsetChanged( vlc_object_t *, char const *,
122                               vlc_value_t, vlc_value_t , void * );
123 static int VolumeChanged    ( vlc_object_t *, char const *,
124                               vlc_value_t, vlc_value_t, void * );
125 static int StateChanged     ( vlc_object_t *, char const *,
126                               vlc_value_t, vlc_value_t, void * );
127 static int RateChanged      ( vlc_object_t *, char const *,
128                               vlc_value_t, vlc_value_t, void * );
129
130 struct intf_sys_t
131 {
132     int *pi_socket_listen;
133     int i_socket;
134     char *psz_unix_path;
135
136     /* status changes */
137     vlc_mutex_t       status_lock;
138     playlist_status_t i_last_state;
139
140 #ifdef WIN32
141     HANDLE hConsoleIn;
142     bool b_quiet;
143 #endif
144 };
145
146 #define msg_rc( ... ) __msg_rc( p_intf, __VA_ARGS__ )
147
148 LIBVLC_FORMAT(2, 3)
149 static void __msg_rc( intf_thread_t *p_intf, const char *psz_fmt, ... )
150 {
151     va_list args;
152     char fmt_eol[strlen (psz_fmt) + 3];
153
154     snprintf (fmt_eol, sizeof (fmt_eol), "%s\r\n", psz_fmt);
155     va_start( args, psz_fmt );
156
157     if( p_intf->p_sys->i_socket == -1 )
158         utf8_vfprintf( stdout, fmt_eol, args );
159     else
160         net_vaPrintf( p_intf, p_intf->p_sys->i_socket, NULL, fmt_eol, args );
161     va_end( args );
162 }
163
164 /*****************************************************************************
165  * Module descriptor
166  *****************************************************************************/
167 #define POS_TEXT N_("Show stream position")
168 #define POS_LONGTEXT N_("Show the current position in seconds within the " \
169                         "stream from time to time." )
170
171 #define TTY_TEXT N_("Fake TTY")
172 #define TTY_LONGTEXT N_("Force the rc module to use stdin as if it was a TTY.")
173
174 #define UNIX_TEXT N_("UNIX socket command input")
175 #define UNIX_LONGTEXT N_("Accept commands over a Unix socket rather than " \
176                          "stdin." )
177
178 #define HOST_TEXT N_("TCP command input")
179 #define HOST_LONGTEXT N_("Accept commands over a socket rather than stdin. " \
180             "You can set the address and port the interface will bind to." )
181
182 #ifdef WIN32
183 #define QUIET_TEXT N_("Do not open a DOS command box interface")
184 #define QUIET_LONGTEXT N_( \
185     "By default the rc interface plugin will start a DOS command box. " \
186     "Enabling the quiet mode will not bring this command box but can also " \
187     "be pretty annoying when you want to stop VLC and no video window is " \
188     "open." )
189 #endif
190
191 vlc_module_begin ()
192     set_shortname( N_("RC"))
193     set_category( CAT_INTERFACE )
194     set_subcategory( SUBCAT_INTERFACE_MAIN )
195     set_description( N_("Remote control interface") )
196     add_bool( "rc-show-pos", 0, NULL, POS_TEXT, POS_LONGTEXT, true )
197
198 #ifdef WIN32
199     add_bool( "rc-quiet", 0, NULL, QUIET_TEXT, QUIET_LONGTEXT, false )
200 #else
201 #if defined (HAVE_ISATTY)
202     add_bool( "rc-fake-tty", 0, NULL, TTY_TEXT, TTY_LONGTEXT, true )
203 #endif
204     add_string( "rc-unix", 0, NULL, UNIX_TEXT, UNIX_LONGTEXT, true )
205 #endif
206     add_string( "rc-host", 0, NULL, HOST_TEXT, HOST_LONGTEXT, true )
207
208     set_capability( "interface", 20 )
209
210     set_callbacks( Activate, Deactivate )
211 vlc_module_end ()
212
213 /*****************************************************************************
214  * Activate: initialize and create stuff
215  *****************************************************************************/
216 static int Activate( vlc_object_t *p_this )
217 {
218     intf_thread_t *p_intf = (intf_thread_t*)p_this;
219     char *psz_host, *psz_unix_path = NULL;
220     int  *pi_socket = NULL;
221
222 #ifndef WIN32
223 #if defined(HAVE_ISATTY)
224     /* Check that stdin is a TTY */
225     if( !config_GetInt( p_intf, "rc-fake-tty" ) && !isatty( 0 ) )
226     {
227         msg_Warn( p_intf, "fd 0 is not a TTY" );
228         return VLC_EGENERIC;
229     }
230 #endif
231
232     psz_unix_path = config_GetPsz( p_intf, "rc-unix" );
233     if( psz_unix_path )
234     {
235         int i_socket;
236
237 #ifndef AF_LOCAL
238         msg_Warn( p_intf, "your OS doesn't support filesystem sockets" );
239         free( psz_unix_path );
240         return VLC_EGENERIC;
241 #else
242         struct sockaddr_un addr;
243
244         memset( &addr, 0, sizeof(struct sockaddr_un) );
245
246         msg_Dbg( p_intf, "trying UNIX socket" );
247
248         if( (i_socket = socket( AF_LOCAL, SOCK_STREAM, 0 ) ) < 0 )
249         {
250             msg_Warn( p_intf, "can't open socket: %m" );
251             free( psz_unix_path );
252             return VLC_EGENERIC;
253         }
254
255         addr.sun_family = AF_LOCAL;
256         strncpy( addr.sun_path, psz_unix_path, sizeof( addr.sun_path ) );
257         addr.sun_path[sizeof( addr.sun_path ) - 1] = '\0';
258
259         if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr))
260          && (errno == EADDRINUSE)
261          && connect (i_socket, (struct sockaddr *)&addr, sizeof (addr))
262          && (errno == ECONNREFUSED))
263         {
264             msg_Info (p_intf, "Removing dead UNIX socket: %s", psz_unix_path);
265             unlink (psz_unix_path);
266
267             if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr)))
268             {
269                 msg_Err (p_intf, "cannot bind UNIX socket at %s: %m",
270                          psz_unix_path);
271                 free (psz_unix_path);
272                 net_Close (i_socket);
273                 return VLC_EGENERIC;
274             }
275         }
276
277         if( listen( i_socket, 1 ) )
278         {
279             msg_Warn( p_intf, "can't listen on socket: %m");
280             free( psz_unix_path );
281             net_Close( i_socket );
282             return VLC_EGENERIC;
283         }
284
285         /* FIXME: we need a core function to merge listening sockets sets */
286         pi_socket = calloc( 2, sizeof( int ) );
287         if( pi_socket == NULL )
288         {
289             free( psz_unix_path );
290             net_Close( i_socket );
291             return VLC_ENOMEM;
292         }
293         pi_socket[0] = i_socket;
294         pi_socket[1] = -1;
295 #endif /* AF_LOCAL */
296     }
297 #endif /* !WIN32 */
298
299     if( ( pi_socket == NULL ) &&
300         ( psz_host = config_GetPsz( p_intf, "rc-host" ) ) != NULL )
301     {
302         vlc_url_t url;
303
304         vlc_UrlParse( &url, psz_host, 0 );
305
306         msg_Dbg( p_intf, "base: %s, port: %d", url.psz_host, url.i_port );
307
308         pi_socket = net_ListenTCP(p_this, url.psz_host, url.i_port);
309         if( pi_socket == NULL )
310         {
311             msg_Warn( p_intf, "can't listen to %s port %i",
312                       url.psz_host, url.i_port );
313             vlc_UrlClean( &url );
314             free( psz_host );
315             return VLC_EGENERIC;
316         }
317
318         vlc_UrlClean( &url );
319         free( psz_host );
320     }
321
322     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
323     if( !p_intf->p_sys )
324         return VLC_ENOMEM;
325
326     p_intf->p_sys->pi_socket_listen = pi_socket;
327     p_intf->p_sys->i_socket = -1;
328     p_intf->p_sys->psz_unix_path = psz_unix_path;
329     vlc_mutex_init( &p_intf->p_sys->status_lock );
330     p_intf->p_sys->i_last_state = PLAYLIST_STOPPED;
331
332     /* Non-buffered stdout */
333     setvbuf( stdout, (char *)NULL, _IOLBF, 0 );
334
335     p_intf->pf_run = Run;
336
337 #ifdef WIN32
338     p_intf->p_sys->b_quiet = config_GetInt( p_intf, "rc-quiet" );
339     if( !p_intf->p_sys->b_quiet ) { CONSOLE_INTRO_MSG; }
340 #else
341     CONSOLE_INTRO_MSG;
342 #endif
343
344     msg_rc( _("Remote control interface initialized. Type `help' for help.") );
345     return VLC_SUCCESS;
346 }
347
348 /*****************************************************************************
349  * Deactivate: uninitialize and cleanup
350  *****************************************************************************/
351 static void Deactivate( vlc_object_t *p_this )
352 {
353     intf_thread_t *p_intf = (intf_thread_t*)p_this;
354
355     net_ListenClose( p_intf->p_sys->pi_socket_listen );
356     if( p_intf->p_sys->i_socket != -1 )
357         net_Close( p_intf->p_sys->i_socket );
358     if( p_intf->p_sys->psz_unix_path != NULL )
359     {
360 #if defined(AF_LOCAL) && !defined(WIN32)
361         unlink( p_intf->p_sys->psz_unix_path );
362 #endif
363         free( p_intf->p_sys->psz_unix_path );
364     }
365     vlc_mutex_destroy( &p_intf->p_sys->status_lock );
366     free( p_intf->p_sys );
367 }
368
369 /*****************************************************************************
370  * RegisterCallbacks: Register callbacks to dynamic variables
371  *****************************************************************************/
372 static void RegisterCallbacks( intf_thread_t *p_intf )
373 {
374     /* Register commands that will be cleaned up upon object destruction */
375 #define ADD( name, type, target )                                   \
376     var_Create( p_intf, name, VLC_VAR_ ## type | VLC_VAR_ISCOMMAND ); \
377     var_AddCallback( p_intf, name, target, NULL );
378     ADD( "quit", VOID, Quit )
379     ADD( "intf", STRING, Intf )
380
381     ADD( "add", STRING, Playlist )
382     ADD( "repeat", STRING, Playlist )
383     ADD( "loop", STRING, Playlist )
384     ADD( "random", STRING, Playlist )
385     ADD( "enqueue", STRING, Playlist )
386     ADD( "playlist", VOID, Playlist )
387     ADD( "sort", VOID, Playlist )
388     ADD( "play", VOID, Playlist )
389     ADD( "stop", VOID, Playlist )
390     ADD( "clear", VOID, Playlist )
391     ADD( "prev", VOID, Playlist )
392     ADD( "next", VOID, Playlist )
393     ADD( "goto", INTEGER, Playlist )
394     ADD( "status", INTEGER, Playlist )
395
396     /* OSD menu commands */
397     ADD(  "menu", STRING, Menu )
398
399     /* DVD commands */
400     ADD( "pause", VOID, Input )
401     ADD( "seek", INTEGER, Input )
402     ADD( "title", STRING, Input )
403     ADD( "title_n", VOID, Input )
404     ADD( "title_p", VOID, Input )
405     ADD( "chapter", STRING, Input )
406     ADD( "chapter_n", VOID, Input )
407     ADD( "chapter_p", VOID, Input )
408
409     ADD( "fastforward", VOID, Input )
410     ADD( "rewind", VOID, Input )
411     ADD( "faster", VOID, Input )
412     ADD( "slower", VOID, Input )
413     ADD( "normal", VOID, Input )
414
415     ADD( "atrack", STRING, Input )
416     ADD( "vtrack", STRING, Input )
417     ADD( "strack", STRING, Input )
418
419     /* video commands */
420     ADD( "vratio", STRING, VideoConfig )
421     ADD( "vcrop", STRING, VideoConfig )
422     ADD( "vzoom", STRING, VideoConfig )
423     ADD( "snapshot", VOID, VideoConfig )
424
425     /* audio commands */
426     ADD( "volume", STRING, Volume )
427     ADD( "volup", STRING, VolumeMove )
428     ADD( "voldown", STRING, VolumeMove )
429     ADD( "adev", STRING, AudioConfig )
430     ADD( "achan", STRING, AudioConfig )
431
432     /* misc menu commands */
433     ADD( "stats", BOOL, Statistics )
434
435 #undef ADD
436 }
437
438 /*****************************************************************************
439  * Run: rc thread
440  *****************************************************************************
441  * This part of the interface is in a separate thread so that we can call
442  * exec() from within it without annoying the rest of the program.
443  *****************************************************************************/
444 static void Run( intf_thread_t *p_intf )
445 {
446     input_thread_t * p_input;
447     playlist_t *     p_playlist;
448
449     char p_buffer[ MAX_LINE_LENGTH + 1 ];
450     bool b_showpos = config_GetInt( p_intf, "rc-show-pos" );
451     bool b_longhelp = false;
452
453     int  i_size = 0;
454     int  i_oldpos = 0;
455     int  i_newpos;
456     int  canc = vlc_savecancel();
457
458     p_buffer[0] = 0;
459     p_input = NULL;
460     p_playlist = NULL;
461
462     /* Register commands that will be cleaned up upon object destruction */
463     RegisterCallbacks( p_intf );
464
465     /* status callbacks */
466     /* Listen to audio volume updates */
467     var_AddCallback( p_intf->p_libvlc, "volume-change", VolumeChanged, p_intf );
468
469 #ifdef WIN32
470     /* Get the file descriptor of the console input */
471     p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
472     if( p_intf->p_sys->hConsoleIn == INVALID_HANDLE_VALUE )
473     {
474         msg_Err( p_intf, "couldn't find user input handle" );
475         vlc_object_kill( p_intf );
476     }
477 #endif
478
479     while( vlc_object_alive( p_intf ) )
480     {
481         char *psz_cmd, *psz_arg;
482         bool b_complete;
483
484         if( p_intf->p_sys->pi_socket_listen != NULL &&
485             p_intf->p_sys->i_socket == -1 )
486         {
487             p_intf->p_sys->i_socket =
488                 net_Accept( p_intf, p_intf->p_sys->pi_socket_listen,
489                             INTF_IDLE_SLEEP );
490             if( p_intf->p_sys->i_socket == -1 ) continue;
491         }
492
493         b_complete = ReadCommand( p_intf, p_buffer, &i_size );
494
495         /* Manage the input part */
496         if( p_input == NULL )
497         {
498             if( p_playlist )
499             {
500                 p_input = vlc_object_find( p_playlist, VLC_OBJECT_INPUT,
501                                                        FIND_CHILD );
502             }
503             else
504             {
505                 p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
506                                                    FIND_ANYWHERE );
507                 if( p_input )
508                 {
509                     p_playlist = pl_Hold( p_input );
510                 }
511             }
512             /* New input has been registered */
513             if( p_input )
514             {
515                 if( !p_input->b_dead || vlc_object_alive (p_input) )
516                 {
517                     char *psz_uri =
518                             input_item_GetURI( input_GetItem( p_input ) );
519                     msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri );
520                     free( psz_uri );
521                     msg_rc( STATUS_CHANGE "( audio volume: %d )",
522                             config_GetInt( p_intf, "volume" ));
523                 }
524                 var_AddCallback( p_input, "state", StateChanged, p_intf );
525                 var_AddCallback( p_input, "rate-faster", RateChanged, p_intf );
526                 var_AddCallback( p_input, "rate-slower", RateChanged, p_intf );
527                 var_AddCallback( p_input, "rate", RateChanged, p_intf );
528                 var_AddCallback( p_input, "time-offset", TimeOffsetChanged,
529                                  p_intf );
530             }
531         }
532         else if( p_input->b_dead )
533         {
534             var_DelCallback( p_input, "state", StateChanged, p_intf );
535             var_DelCallback( p_input, "rate-faster", RateChanged, p_intf );
536             var_DelCallback( p_input, "rate-slower", RateChanged, p_intf );
537             var_DelCallback( p_input, "rate", RateChanged, p_intf );
538             var_DelCallback( p_input, "time-offset", TimeOffsetChanged,
539                              p_intf );
540             vlc_object_release( p_input );
541             p_input = NULL;
542
543             if( p_playlist )
544             {
545                 PL_LOCK;
546                 p_intf->p_sys->i_last_state = (int) PLAYLIST_STOPPED;
547                 msg_rc( STATUS_CHANGE "( stop state: 0 )" );
548                 PL_UNLOCK;
549             }
550         }
551
552         if( (p_input != NULL) && !p_input->b_dead && vlc_object_alive (p_input) &&
553             (p_playlist != NULL) )
554         {
555             PL_LOCK;
556             int status = playlist_Status( p_playlist );
557             if( p_intf->p_sys->i_last_state != status )
558             {
559                 if( status == PLAYLIST_STOPPED )
560                 {
561                     p_intf->p_sys->i_last_state = PLAYLIST_STOPPED;
562                     msg_rc( STATUS_CHANGE "( stop state: 5 )" );
563                 }
564                 else if( status == PLAYLIST_RUNNING )
565                 {
566                     p_intf->p_sys->i_last_state = PLAYLIST_RUNNING;
567                     msg_rc( STATUS_CHANGE "( play state: 3 )" );
568                 }
569                 else if( status == PLAYLIST_PAUSED )
570                 {
571                     p_intf->p_sys->i_last_state = PLAYLIST_PAUSED;
572                     msg_rc( STATUS_CHANGE "( pause state: 4 )" );
573                 }
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( "%s", 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: %"PRId64"s )",
961                 (var_GetTime( p_input, "time-offset" )/1000000) );
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     char cmd[6];
988
989     vlc_mutex_lock( &p_intf->p_sys->status_lock );
990     p_playlist = pl_Hold( p_intf );
991     PL_LOCK;
992     int i_status = playlist_Status( p_playlist );
993     PL_UNLOCK;
994     pl_Release( p_intf );
995
996     switch( i_status )
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     msg_rc( STATUS_CHANGE "( %s state: %d ): %s", cmd, newval.i_int,
1011             ppsz_input_state[ newval.i_int ] );
1012
1013     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
1014     return VLC_SUCCESS;
1015 }
1016
1017 static int RateChanged( vlc_object_t *p_this, char const *psz_cmd,
1018     vlc_value_t oldval, vlc_value_t newval, void *p_data )
1019 {
1020     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd);
1021     VLC_UNUSED(oldval); VLC_UNUSED(newval);
1022     intf_thread_t *p_intf = (intf_thread_t*)p_data;
1023     input_thread_t *p_input = NULL;
1024
1025     vlc_mutex_lock( &p_intf->p_sys->status_lock );
1026     p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1027     if( p_input )
1028     {
1029         msg_rc( STATUS_CHANGE "( new rate: %d )",
1030                 var_GetInteger( p_input, "rate" ) );
1031         vlc_object_release( p_input );
1032     }
1033     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
1034     return VLC_SUCCESS;
1035 }
1036
1037 /********************************************************************
1038  * Command routines
1039  ********************************************************************/
1040 static int Input( vlc_object_t *p_this, char const *psz_cmd,
1041                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
1042 {
1043     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1044     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1045     input_thread_t *p_input;
1046     vlc_value_t     val;
1047
1048     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1049     if( !p_input ) return VLC_ENOOBJ;
1050
1051     var_Get( p_input, "state", &val );
1052     if( ( val.i_int == PAUSE_S ) &&
1053         ( strcmp( psz_cmd, "pause" ) != 0 ) )
1054     {
1055         msg_rc( _("Press menu select or pause to continue.") );
1056         vlc_object_release( p_input );
1057         return VLC_EGENERIC;
1058     }
1059
1060     /* Parse commands that only require an input */
1061     if( !strcmp( psz_cmd, "pause" ) )
1062     {
1063         var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_PLAY_PAUSE );
1064         vlc_object_release( p_input );
1065         return VLC_SUCCESS;
1066     }
1067     else if( !strcmp( psz_cmd, "seek" ) )
1068     {
1069         if( strlen( newval.psz_string ) > 0 &&
1070             newval.psz_string[strlen( newval.psz_string ) - 1] == '%' )
1071         {
1072             val.f_float = (float)atof( newval.psz_string ) / 100.0;
1073             var_Set( p_input, "position", val );
1074         }
1075         else
1076         {
1077             val.i_time = ((int64_t)atoi( newval.psz_string )) * 1000000;
1078             var_Set( p_input, "time", val );
1079         }
1080         vlc_object_release( p_input );
1081         return VLC_SUCCESS;
1082     }
1083     else if ( !strcmp( psz_cmd, "fastforward" ) )
1084     {
1085         if( var_GetBool( p_input, "can-rate" ) )
1086         {
1087             int i_rate = var_GetInteger( p_input, "rate" );
1088             i_rate = (i_rate < 0) ? -i_rate : i_rate * 2;
1089             var_SetInteger( p_input, "rate", i_rate );
1090         }
1091         else
1092         {
1093             var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_JUMP_FORWARD_EXTRASHORT );
1094         }
1095         vlc_object_release( p_input );
1096         return VLC_SUCCESS;
1097     }
1098     else if ( !strcmp( psz_cmd, "rewind" ) )
1099     {
1100         if( var_GetBool( p_input, "can-rewind" ) )
1101         {
1102             int i_rate = var_GetInteger( p_input, "rate" );
1103             i_rate = (i_rate > 0) ? -i_rate : i_rate / 2;
1104             var_SetInteger( p_input, "rate", i_rate );
1105         }
1106         else
1107         {
1108             var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_JUMP_BACKWARD_EXTRASHORT );
1109         }
1110         vlc_object_release( p_input );
1111         return VLC_SUCCESS;
1112     }
1113     else if ( !strcmp( psz_cmd, "faster" ) )
1114     {
1115         var_Set( p_input, "rate-faster", val );
1116         vlc_object_release( p_input );
1117         return VLC_SUCCESS;
1118     }
1119     else if ( !strcmp( psz_cmd, "slower" ) )
1120     {
1121         var_Set( p_input, "rate-slower", val );
1122         vlc_object_release( p_input );
1123         return VLC_SUCCESS;
1124     }
1125     else if ( !strcmp( psz_cmd, "normal" ) )
1126     {
1127         val.i_int = INPUT_RATE_DEFAULT;
1128         var_Set( p_input, "rate", val );
1129         vlc_object_release( p_input );
1130         return VLC_SUCCESS;
1131     }
1132     else if( !strcmp( psz_cmd, "chapter" ) ||
1133              !strcmp( psz_cmd, "chapter_n" ) ||
1134              !strcmp( psz_cmd, "chapter_p" ) )
1135     {
1136         if( !strcmp( psz_cmd, "chapter" ) )
1137         {
1138             if ( *newval.psz_string )
1139             {
1140                 /* Set. */
1141                 val.i_int = atoi( newval.psz_string );
1142                 var_Set( p_input, "chapter", val );
1143             }
1144             else
1145             {
1146                 /* Get. */
1147                 var_Get( p_input, "chapter", &val );
1148                 int i_chapter_count = var_CountChoices( p_input, "chapter" );
1149                 msg_rc( "Currently playing chapter %d/%d.", val.i_int,
1150                         i_chapter_count );
1151             }
1152         }
1153         else if( !strcmp( psz_cmd, "chapter_n" ) )
1154             var_SetVoid( p_input, "next-chapter" );
1155         else if( !strcmp( psz_cmd, "chapter_p" ) )
1156             var_SetVoid( p_input, "prev-chapter" );
1157         vlc_object_release( p_input );
1158         return VLC_SUCCESS;
1159     }
1160     else if( !strcmp( psz_cmd, "title" ) ||
1161              !strcmp( psz_cmd, "title_n" ) ||
1162              !strcmp( psz_cmd, "title_p" ) )
1163     {
1164         if( !strcmp( psz_cmd, "title" ) )
1165         {
1166             if ( *newval.psz_string )
1167             {
1168                 /* Set. */
1169                 val.i_int = atoi( newval.psz_string );
1170                 var_Set( p_input, "title", val );
1171             }
1172             else
1173             {
1174                 /* Get. */
1175                 var_Get( p_input, "title", &val );
1176                 int i_title_count = var_CountChoices( p_input, "title" );
1177                 msg_rc( "Currently playing title %d/%d.", val.i_int,
1178                         i_title_count );
1179             }
1180         }
1181         else if( !strcmp( psz_cmd, "title_n" ) )
1182             var_SetVoid( p_input, "next-title" );
1183         else if( !strcmp( psz_cmd, "title_p" ) )
1184             var_SetVoid( p_input, "prev-title" );
1185
1186         vlc_object_release( p_input );
1187         return VLC_SUCCESS;
1188     }
1189     else if(    !strcmp( psz_cmd, "atrack" )
1190              || !strcmp( psz_cmd, "vtrack" )
1191              || !strcmp( psz_cmd, "strack" ) )
1192     {
1193         const char *psz_variable;
1194         vlc_value_t val_name;
1195         int i_error;
1196
1197         if( !strcmp( psz_cmd, "atrack" ) )
1198         {
1199             psz_variable = "audio-es";
1200         }
1201         else if( !strcmp( psz_cmd, "vtrack" ) )
1202         {
1203             psz_variable = "video-es";
1204         }
1205         else
1206         {
1207             psz_variable = "spu-es";
1208         }
1209
1210         /* Get the descriptive name of the variable */
1211         var_Change( p_input, psz_variable, VLC_VAR_GETTEXT,
1212                      &val_name, NULL );
1213         if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1214
1215         if( newval.psz_string && *newval.psz_string )
1216         {
1217             /* set */
1218             vlc_value_t val;
1219             val.i_int = atoi( newval.psz_string );
1220
1221             i_error = var_Set( p_input, psz_variable, val );
1222         }
1223         else
1224         {
1225             /* get */
1226             vlc_value_t val, text;
1227             int i, i_value;
1228
1229             if ( var_Get( p_input, psz_variable, &val ) < 0 )
1230             {
1231                 vlc_object_release( p_input );
1232                 return VLC_EGENERIC;
1233             }
1234             i_value = val.i_int;
1235
1236             if ( var_Change( p_input, psz_variable,
1237                              VLC_VAR_GETLIST, &val, &text ) < 0 )
1238             {
1239                 vlc_object_release( p_input );
1240                 return VLC_EGENERIC;
1241             }
1242
1243             msg_rc( "+----[ %s ]", val_name.psz_string );
1244             for ( i = 0; i < val.p_list->i_count; i++ )
1245             {
1246                 if ( i_value == val.p_list->p_values[i].i_int )
1247                     msg_rc( "| %i - %s *", val.p_list->p_values[i].i_int,
1248                             text.p_list->p_values[i].psz_string );
1249                 else
1250                     msg_rc( "| %i - %s", val.p_list->p_values[i].i_int,
1251                             text.p_list->p_values[i].psz_string );
1252             }
1253             var_FreeList( &val, &text );
1254             msg_rc( "+----[ end of %s ]", val_name.psz_string );
1255
1256             free( val_name.psz_string );
1257
1258             i_error = VLC_SUCCESS;
1259         }
1260         vlc_object_release( p_input );
1261         return i_error;
1262     }
1263
1264     /* Never reached. */
1265     vlc_object_release( p_input );
1266     return VLC_EGENERIC;
1267 }
1268
1269 static void print_playlist( intf_thread_t *p_intf, playlist_item_t *p_item, int i_level )
1270 {
1271     int i;
1272     char psz_buffer[MSTRTIME_MAX_SIZE];
1273     for( i = 0; i< p_item->i_children; i++ )
1274     {
1275         if( p_item->pp_children[i]->p_input->i_duration != -1 )
1276         {
1277             secstotimestr( psz_buffer, p_item->pp_children[i]->p_input->i_duration / 1000000 );
1278             msg_rc( "|%*s- %s (%s)", 2 * i_level, "", p_item->pp_children[i]->p_input->psz_name, psz_buffer );
1279         }
1280         else
1281             msg_rc( "|%*s- %s", 2 * i_level, "", p_item->pp_children[i]->p_input->psz_name );
1282
1283         if( p_item->pp_children[i]->i_children >= 0 )
1284             print_playlist( p_intf, p_item->pp_children[i], i_level + 1 );
1285     }
1286 }
1287
1288 static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
1289                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
1290 {
1291     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1292     vlc_value_t val;
1293
1294     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1295     playlist_t *p_playlist = pl_Hold( p_this );
1296     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1297
1298     if( p_input )
1299     {
1300         var_Get( p_input, "state", &val );
1301         vlc_object_release( p_input );
1302
1303         if( val.i_int == PAUSE_S )
1304         {
1305             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
1306             pl_Release( p_this );
1307             return VLC_EGENERIC;
1308         }
1309     }
1310
1311     /* Parse commands that require a playlist */
1312     if( !strcmp( psz_cmd, "prev" ) )
1313     {
1314         playlist_Prev( p_playlist );
1315     }
1316     else if( !strcmp( psz_cmd, "next" ) )
1317     {
1318         playlist_Next( p_playlist );
1319     }
1320     else if( !strcmp( psz_cmd, "play" ) )
1321     {
1322         msg_Warn( p_playlist, "play" );
1323         playlist_Play( p_playlist );
1324     }
1325     else if( !strcmp( psz_cmd, "repeat" ) )
1326     {
1327         bool b_update = true;
1328
1329         var_Get( p_playlist, "repeat", &val );
1330
1331         if( strlen( newval.psz_string ) > 0 )
1332         {
1333             if ( ( !strncmp( newval.psz_string, "on", 2 ) && ( val.b_bool == true ) ) ||
1334                  ( !strncmp( newval.psz_string, "off", 3 ) && ( val.b_bool == false ) ) )
1335             {
1336                 b_update = false;
1337             }
1338         }
1339
1340         if ( b_update )
1341         {
1342             val.b_bool = !val.b_bool;
1343             var_Set( p_playlist, "repeat", val );
1344         }
1345         msg_rc( "Setting repeat to %d", val.b_bool );
1346     }
1347     else if( !strcmp( psz_cmd, "loop" ) )
1348     {
1349         bool b_update = true;
1350
1351         var_Get( p_playlist, "loop", &val );
1352
1353         if( strlen( newval.psz_string ) > 0 )
1354         {
1355             if ( ( !strncmp( newval.psz_string, "on", 2 ) && ( val.b_bool == true ) ) ||
1356                  ( !strncmp( newval.psz_string, "off", 3 ) && ( val.b_bool == false ) ) )
1357             {
1358                 b_update = false;
1359             }
1360         }
1361
1362         if ( b_update )
1363         {
1364             val.b_bool = !val.b_bool;
1365             var_Set( p_playlist, "loop", val );
1366         }
1367         msg_rc( "Setting loop to %d", val.b_bool );
1368     }
1369     else if( !strcmp( psz_cmd, "random" ) )
1370     {
1371         bool b_update = true;
1372
1373         var_Get( p_playlist, "random", &val );
1374
1375         if( strlen( newval.psz_string ) > 0 )
1376         {
1377             if ( ( !strncmp( newval.psz_string, "on", 2 ) && ( val.b_bool == true ) ) ||
1378                  ( !strncmp( newval.psz_string, "off", 3 ) && ( val.b_bool == false ) ) )
1379             {
1380                 b_update = false;
1381             }
1382         }
1383
1384         if ( b_update )
1385         {
1386             val.b_bool = !val.b_bool;
1387             var_Set( p_playlist, "random", val );
1388         }
1389         msg_rc( "Setting random to %d", val.b_bool );
1390     }
1391     else if (!strcmp( psz_cmd, "goto" ) )
1392     {
1393         int i_pos = atoi( newval.psz_string );
1394         /* The playlist stores 2 times the same item: onelevel & category */
1395         int i_size = p_playlist->items.i_size / 2;
1396
1397         if( i_pos <= 0 )
1398             msg_rc( _("Error: `goto' needs an argument greater than zero.") );
1399         else if( i_pos <= i_size )
1400         {
1401             playlist_item_t *p_item, *p_parent;
1402             p_item = p_parent = p_playlist->items.p_elems[i_pos*2-1];
1403             while( p_parent->p_parent )
1404                 p_parent = p_parent->p_parent;
1405             playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Unlocked,
1406                     p_parent, p_item );
1407         }
1408         else
1409             msg_rc( _("Playlist has only %d elements"), i_size );
1410     }
1411     else if( !strcmp( psz_cmd, "stop" ) )
1412     {
1413         playlist_Stop( p_playlist );
1414     }
1415     else if( !strcmp( psz_cmd, "clear" ) )
1416     {
1417         playlist_Stop( p_playlist );
1418         playlist_Clear( p_playlist, pl_Unlocked );
1419     }
1420     else if( !strcmp( psz_cmd, "add" ) &&
1421              newval.psz_string && *newval.psz_string )
1422     {
1423         input_item_t *p_item = parse_MRL( p_intf, newval.psz_string );
1424
1425         if( p_item )
1426         {
1427             msg_rc( "Trying to add %s to playlist.", newval.psz_string );
1428             int i_ret =playlist_AddInput( p_playlist, p_item,
1429                      PLAYLIST_GO|PLAYLIST_APPEND, PLAYLIST_END, true,
1430                      pl_Unlocked );
1431             vlc_gc_decref( p_item );
1432             if( i_ret != VLC_SUCCESS )
1433             {
1434                 return VLC_EGENERIC;
1435             }
1436         }
1437     }
1438     else if( !strcmp( psz_cmd, "enqueue" ) &&
1439              newval.psz_string && *newval.psz_string )
1440     {
1441         input_item_t *p_item = parse_MRL( p_intf, newval.psz_string );
1442
1443         if( p_item )
1444         {
1445             msg_rc( "trying to enqueue %s to playlist", newval.psz_string );
1446             if( playlist_AddInput( p_playlist, p_item,
1447                                PLAYLIST_APPEND, PLAYLIST_END, true,
1448                                pl_Unlocked ) != VLC_SUCCESS )
1449             {
1450                 return VLC_EGENERIC;
1451             }
1452         }
1453     }
1454     else if( !strcmp( psz_cmd, "playlist" ) )
1455     {
1456         msg_rc( "+----[ Playlist ]" );
1457         print_playlist( p_intf, p_playlist->p_root_category, 0 );
1458         msg_rc( "+----[ End of playlist ]" );
1459     }
1460
1461     else if( !strcmp( psz_cmd, "sort" ))
1462     {
1463         playlist_RecursiveNodeSort( p_playlist, p_playlist->p_root_onelevel,
1464                                     SORT_ARTIST, ORDER_NORMAL );
1465     }
1466     else if( !strcmp( psz_cmd, "status" ) )
1467     {
1468         input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1469         if( p_input )
1470         {
1471             /* Replay the current state of the system. */
1472             char *psz_uri =
1473                     input_item_GetURI( input_GetItem( p_input ) );
1474             msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri );
1475             free( psz_uri );
1476             msg_rc( STATUS_CHANGE "( audio volume: %d )",
1477                     config_GetInt( p_intf, "volume" ));
1478
1479             PL_LOCK;
1480             switch( playlist_Status(p_playlist) )
1481             {
1482                 case PLAYLIST_STOPPED:
1483                     msg_rc( STATUS_CHANGE "( stop state: 5 )" );
1484                     break;
1485                 case PLAYLIST_RUNNING:
1486                     msg_rc( STATUS_CHANGE "( play state: 3 )" );
1487                     break;
1488                 case PLAYLIST_PAUSED:
1489                     msg_rc( STATUS_CHANGE "( pause state: 4 )" );
1490                     break;
1491                 default:
1492                     msg_rc( STATUS_CHANGE "( unknown state: -1 )" );
1493                     break;
1494             }
1495             PL_UNLOCK;
1496             vlc_object_release( p_input );
1497         }
1498     }
1499
1500     /*
1501      * sanity check
1502      */
1503     else
1504     {
1505         msg_rc( "unknown command!" );
1506     }
1507
1508     pl_Release( p_this );
1509     return VLC_SUCCESS;
1510 }
1511
1512 static int Quit( vlc_object_t *p_this, char const *psz_cmd,
1513                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1514 {
1515     VLC_UNUSED(p_data); VLC_UNUSED(psz_cmd);
1516     VLC_UNUSED(oldval); VLC_UNUSED(newval);
1517
1518     libvlc_Quit( p_this->p_libvlc );
1519     return VLC_SUCCESS;
1520 }
1521
1522 static int Intf( vlc_object_t *p_this, char const *psz_cmd,
1523                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1524 {
1525     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1526
1527     return intf_Create( p_this->p_libvlc, newval.psz_string );
1528 }
1529
1530 static int Volume( vlc_object_t *p_this, char const *psz_cmd,
1531                    vlc_value_t oldval, vlc_value_t newval, void *p_data )
1532 {
1533     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1534     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1535     input_thread_t *p_input = NULL;
1536     int i_error = VLC_EGENERIC;
1537
1538     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1539     if( !p_input )
1540         return VLC_ENOOBJ;
1541
1542     if( p_input )
1543     {
1544         vlc_value_t val;
1545
1546         var_Get( p_input, "state", &val );
1547         if( val.i_int == PAUSE_S )
1548         {
1549             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
1550             vlc_object_release( p_input );
1551             return VLC_EGENERIC;
1552         }
1553         vlc_object_release( p_input );
1554     }
1555
1556     if ( *newval.psz_string )
1557     {
1558         /* Set. */
1559         audio_volume_t i_volume = atoi( newval.psz_string );
1560         if ( (i_volume > (audio_volume_t)AOUT_VOLUME_MAX) )
1561         {
1562             msg_rc( "Volume must be in the range %d-%d.", AOUT_VOLUME_MIN,
1563                     AOUT_VOLUME_MAX );
1564             i_error = VLC_EBADVAR;
1565         }
1566         else
1567         {
1568             if( i_volume == AOUT_VOLUME_MIN )
1569             {
1570                 var_SetInteger( p_intf->p_libvlc, "key-action", ACTIONID_VOL_MUTE );
1571             }
1572             i_error = aout_VolumeSet( p_this, i_volume );
1573             osd_Volume( p_this );
1574             msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
1575         }
1576     }
1577     else
1578     {
1579         /* Get. */
1580         audio_volume_t i_volume;
1581         if ( aout_VolumeGet( p_this, &i_volume ) < 0 )
1582         {
1583             i_error = VLC_EGENERIC;
1584         }
1585         else
1586         {
1587             msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
1588             i_error = VLC_SUCCESS;
1589         }
1590     }
1591
1592     return i_error;
1593 }
1594
1595 static int VolumeMove( vlc_object_t *p_this, char const *psz_cmd,
1596                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1597 {
1598     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1599     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1600     audio_volume_t i_volume;
1601     input_thread_t *p_input = NULL;
1602     int i_nb_steps = atoi(newval.psz_string);
1603     int i_error = VLC_SUCCESS;
1604     int i_volume_step = 0;
1605
1606     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1607     if( !p_input )
1608         return VLC_ENOOBJ;
1609
1610     if( p_input )
1611     {
1612         vlc_value_t val;
1613
1614         var_Get( p_input, "state", &val );
1615         if( val.i_int == PAUSE_S )
1616         {
1617             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
1618             vlc_object_release( p_input );
1619             return VLC_EGENERIC;
1620         }
1621         vlc_object_release( p_input );
1622     }
1623
1624     i_volume_step = config_GetInt( p_intf->p_libvlc, "volume-step" );
1625     if ( i_nb_steps <= 0 || i_nb_steps > (AOUT_VOLUME_MAX/i_volume_step) )
1626     {
1627         i_nb_steps = 1;
1628     }
1629
1630     if ( !strcmp(psz_cmd, "volup") )
1631     {
1632         if ( aout_VolumeUp( p_this, i_nb_steps, &i_volume ) < 0 )
1633             i_error = VLC_EGENERIC;
1634     }
1635     else
1636     {
1637         if ( aout_VolumeDown( p_this, i_nb_steps, &i_volume ) < 0 )
1638             i_error = VLC_EGENERIC;
1639     }
1640     osd_Volume( p_this );
1641
1642     if ( !i_error ) msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
1643     return i_error;
1644 }
1645
1646
1647 static int VideoConfig( vlc_object_t *p_this, char const *psz_cmd,
1648                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
1649 {
1650     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1651     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1652     input_thread_t *p_input = NULL;
1653     vout_thread_t * p_vout;
1654     const char * psz_variable = NULL;
1655     int i_error;
1656
1657     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1658     if( !p_input )
1659         return VLC_ENOOBJ;
1660
1661     p_vout = vlc_object_find( p_input, VLC_OBJECT_VOUT, FIND_CHILD );
1662     vlc_object_release( p_input );
1663     if( !p_vout )
1664         return VLC_ENOOBJ;
1665
1666     if( !strcmp( psz_cmd, "vcrop" ) )
1667     {
1668         psz_variable = "crop";
1669     }
1670     else if( !strcmp( psz_cmd, "vratio" ) )
1671     {
1672         psz_variable = "aspect-ratio";
1673     }
1674     else if( !strcmp( psz_cmd, "vzoom" ) )
1675     {
1676         psz_variable = "zoom";
1677     }
1678     else if( !strcmp( psz_cmd, "snapshot" ) )
1679     {
1680         psz_variable = "video-snapshot";
1681     }
1682     else
1683         /* This case can't happen */
1684         assert( 0 );
1685
1686     if( newval.psz_string && *newval.psz_string )
1687     {
1688         /* set */
1689         if( !strcmp( psz_variable, "zoom" ) )
1690         {
1691             vlc_value_t val;
1692             val.f_float = atof( newval.psz_string );
1693             i_error = var_Set( p_vout, psz_variable, val );
1694         }
1695         else
1696         {
1697             i_error = var_Set( p_vout, psz_variable, newval );
1698         }
1699     }
1700     else if( !strcmp( psz_cmd, "snapshot" ) )
1701     {
1702         vlc_value_t val;
1703         val.b_bool = true;
1704         i_error = var_Set( p_vout, psz_variable, val );
1705     }
1706     else
1707     {
1708         /* get */
1709         vlc_value_t val_name;
1710         vlc_value_t val, text;
1711         int i;
1712         float f_value = 0.;
1713         char *psz_value = NULL;
1714
1715         if ( var_Get( p_vout, psz_variable, &val ) < 0 )
1716         {
1717             vlc_object_release( p_vout );
1718             return VLC_EGENERIC;
1719         }
1720         if( !strcmp( psz_variable, "zoom" ) )
1721         {
1722             f_value = val.f_float;
1723         }
1724         else
1725         {
1726             psz_value = strdup( val.psz_string );
1727         }
1728
1729         if ( var_Change( p_vout, psz_variable,
1730                          VLC_VAR_GETLIST, &val, &text ) < 0 )
1731         {
1732             vlc_object_release( p_vout );
1733             free( psz_value );
1734             return VLC_EGENERIC;
1735         }
1736
1737         /* Get the descriptive name of the variable */
1738         var_Change( p_vout, psz_variable, VLC_VAR_GETTEXT,
1739                     &val_name, NULL );
1740         if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1741
1742         msg_rc( "+----[ %s ]", val_name.psz_string );
1743         if( !strcmp( psz_variable, "zoom" ) )
1744         {
1745             for ( i = 0; i < val.p_list->i_count; i++ )
1746             {
1747                 if ( f_value == val.p_list->p_values[i].f_float )
1748                     msg_rc( "| %f - %s *", val.p_list->p_values[i].f_float,
1749                             text.p_list->p_values[i].psz_string );
1750                 else
1751                     msg_rc( "| %f - %s", val.p_list->p_values[i].f_float,
1752                             text.p_list->p_values[i].psz_string );
1753             }
1754         }
1755         else
1756         {
1757             for ( i = 0; i < val.p_list->i_count; i++ )
1758             {
1759                 if ( !strcmp( psz_value, val.p_list->p_values[i].psz_string ) )
1760                     msg_rc( "| %s - %s *", val.p_list->p_values[i].psz_string,
1761                             text.p_list->p_values[i].psz_string );
1762                 else
1763                     msg_rc( "| %s - %s", val.p_list->p_values[i].psz_string,
1764                             text.p_list->p_values[i].psz_string );
1765             }
1766             free( psz_value );
1767         }
1768         var_FreeList( &val, &text );
1769         msg_rc( "+----[ end of %s ]", val_name.psz_string );
1770
1771         free( val_name.psz_string );
1772
1773         i_error = VLC_SUCCESS;
1774     }
1775     vlc_object_release( p_vout );
1776     return i_error;
1777 }
1778
1779 static int AudioConfig( vlc_object_t *p_this, char const *psz_cmd,
1780                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
1781 {
1782     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1783     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1784     input_thread_t *p_input = NULL;
1785     aout_instance_t * p_aout;
1786     const char * psz_variable;
1787     vlc_value_t val_name;
1788     int i_error;
1789
1790     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1791     if( !p_input )
1792         return VLC_ENOOBJ;
1793
1794     if( p_input )
1795     {
1796         vlc_value_t val;
1797
1798         var_Get( p_input, "state", &val );
1799         if( val.i_int == PAUSE_S )
1800         {
1801             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
1802             vlc_object_release( p_input );
1803             return VLC_EGENERIC;
1804         }
1805         vlc_object_release( p_input );
1806     }
1807
1808     p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, FIND_ANYWHERE );
1809     if ( p_aout == NULL ) return VLC_ENOOBJ;
1810
1811     if ( !strcmp( psz_cmd, "adev" ) )
1812     {
1813         psz_variable = "audio-device";
1814     }
1815     else
1816     {
1817         psz_variable = "audio-channels";
1818     }
1819
1820     /* Get the descriptive name of the variable */
1821     var_Change( (vlc_object_t *)p_aout, psz_variable, VLC_VAR_GETTEXT,
1822                  &val_name, NULL );
1823     if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1824
1825     if ( !*newval.psz_string )
1826     {
1827         /* Retrieve all registered ***. */
1828         vlc_value_t val, text;
1829         int i, i_value;
1830
1831         if ( var_Get( (vlc_object_t *)p_aout, psz_variable, &val ) < 0 )
1832         {
1833             vlc_object_release( (vlc_object_t *)p_aout );
1834             return VLC_EGENERIC;
1835         }
1836         i_value = val.i_int;
1837
1838         if ( var_Change( (vlc_object_t *)p_aout, psz_variable,
1839                          VLC_VAR_GETLIST, &val, &text ) < 0 )
1840         {
1841             vlc_object_release( (vlc_object_t *)p_aout );
1842             return VLC_EGENERIC;
1843         }
1844
1845         msg_rc( "+----[ %s ]", val_name.psz_string );
1846         for ( i = 0; i < val.p_list->i_count; i++ )
1847         {
1848             if ( i_value == val.p_list->p_values[i].i_int )
1849                 msg_rc( "| %i - %s *", val.p_list->p_values[i].i_int,
1850                         text.p_list->p_values[i].psz_string );
1851             else
1852                 msg_rc( "| %i - %s", val.p_list->p_values[i].i_int,
1853                         text.p_list->p_values[i].psz_string );
1854         }
1855         var_FreeList( &val, &text );
1856         msg_rc( "+----[ end of %s ]", val_name.psz_string );
1857
1858         free( val_name.psz_string );
1859         i_error = VLC_SUCCESS;
1860     }
1861     else
1862     {
1863         vlc_value_t val;
1864         val.i_int = atoi( newval.psz_string );
1865
1866         i_error = var_Set( (vlc_object_t *)p_aout, psz_variable, val );
1867     }
1868     vlc_object_release( (vlc_object_t *)p_aout );
1869
1870     return i_error;
1871 }
1872
1873 /* OSD menu commands */
1874 static int Menu( vlc_object_t *p_this, char const *psz_cmd,
1875     vlc_value_t oldval, vlc_value_t newval, void *p_data )
1876 {
1877     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1878     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1879     playlist_t    *p_playlist = NULL;
1880     int i_error = VLC_SUCCESS;
1881     vlc_value_t val;
1882
1883     if ( !*newval.psz_string )
1884     {
1885         msg_rc( _("Please provide one of the following parameters:") );
1886         msg_rc( "[on|off|up|down|left|right|select]" );
1887         return VLC_EGENERIC;
1888     }
1889
1890     p_playlist = pl_Hold( p_this );
1891     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1892
1893     if( p_input )
1894     {
1895         var_Get( p_input, "state", &val );
1896         vlc_object_release( p_input );
1897
1898         if( ( val.i_int == PAUSE_S ) &&
1899             ( strcmp( newval.psz_string, "select" ) != 0 ) )
1900         {
1901             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
1902             pl_Release( p_this );
1903             return VLC_EGENERIC;
1904         }
1905     }
1906     pl_Release( p_this );
1907
1908     val.psz_string = strdup( newval.psz_string );
1909     if( !val.psz_string )
1910         return VLC_ENOMEM;
1911     if( !strcmp( val.psz_string, "on" ) || !strcmp( val.psz_string, "show" ))
1912         osd_MenuShow( p_this );
1913     else if( !strcmp( val.psz_string, "off" )
1914           || !strcmp( val.psz_string, "hide" ) )
1915         osd_MenuHide( p_this );
1916     else if( !strcmp( val.psz_string, "up" ) )
1917         osd_MenuUp( p_this );
1918     else if( !strcmp( val.psz_string, "down" ) )
1919         osd_MenuDown( p_this );
1920     else if( !strcmp( val.psz_string, "left" ) )
1921         osd_MenuPrev( p_this );
1922     else if( !strcmp( val.psz_string, "right" ) )
1923         osd_MenuNext( p_this );
1924     else if( !strcmp( val.psz_string, "select" ) )
1925         osd_MenuActivate( p_this );
1926     else
1927     {
1928         msg_rc( _("Please provide one of the following parameters:") );
1929         msg_rc( "[on|off|up|down|left|right|select]" );
1930         i_error = VLC_EGENERIC;
1931     }
1932
1933     free( val.psz_string );
1934     return i_error;
1935 }
1936
1937 static int Statistics ( vlc_object_t *p_this, char const *psz_cmd,
1938     vlc_value_t oldval, vlc_value_t newval, void *p_data )
1939 {
1940     VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(p_data);
1941     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1942     input_thread_t *p_input = NULL;
1943     int i_error;
1944
1945     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1946     if( !p_input )
1947         return VLC_ENOOBJ;
1948
1949     if( !strcmp( psz_cmd, "stats" ) )
1950     {
1951         vlc_mutex_lock( &input_GetItem(p_input)->lock );
1952         updateStatistics( p_intf, input_GetItem(p_input) );
1953         vlc_mutex_unlock( &input_GetItem(p_input)->lock );
1954     }
1955     /*
1956      * sanity check
1957      */
1958     else
1959     {
1960         msg_rc(_("Unknown command!") );
1961     }
1962
1963     vlc_object_release( p_input );
1964     i_error = VLC_SUCCESS;
1965     return i_error;
1966 }
1967
1968 static int updateStatistics( intf_thread_t *p_intf, input_item_t *p_item )
1969 {
1970     if( !p_item ) return VLC_EGENERIC;
1971
1972     vlc_mutex_lock( &p_item->p_stats->lock );
1973     msg_rc( "+----[ begin of statistical info ]" );
1974
1975     /* Input */
1976     msg_rc(_("+-[Incoming]"));
1977     msg_rc(_("| input bytes read : %8.0f kB"),
1978             (float)(p_item->p_stats->i_read_bytes)/1000 );
1979     msg_rc(_("| input bitrate    :   %6.0f kb/s"),
1980             (float)(p_item->p_stats->f_input_bitrate)*8000 );
1981     msg_rc(_("| demux bytes read : %8.0f kB"),
1982             (float)(p_item->p_stats->i_demux_read_bytes)/1000 );
1983     msg_rc(_("| demux bitrate    :   %6.0f kb/s"),
1984             (float)(p_item->p_stats->f_demux_bitrate)*8000 );
1985     msg_rc("|");
1986     /* Video */
1987     msg_rc(_("+-[Video Decoding]"));
1988     msg_rc(_("| video decoded    :    %5i"),
1989             p_item->p_stats->i_decoded_video );
1990     msg_rc(_("| frames displayed :    %5i"),
1991             p_item->p_stats->i_displayed_pictures );
1992     msg_rc(_("| frames lost      :    %5i"),
1993             p_item->p_stats->i_lost_pictures );
1994     msg_rc("|");
1995     /* Audio*/
1996     msg_rc(_("+-[Audio Decoding]"));
1997     msg_rc(_("| audio decoded    :    %5i"),
1998             p_item->p_stats->i_decoded_audio );
1999     msg_rc(_("| buffers played   :    %5i"),
2000             p_item->p_stats->i_played_abuffers );
2001     msg_rc(_("| buffers lost     :    %5i"),
2002             p_item->p_stats->i_lost_abuffers );
2003     msg_rc("|");
2004     /* Sout */
2005     msg_rc(_("+-[Streaming]"));
2006     msg_rc(_("| packets sent     :    %5i"), p_item->p_stats->i_sent_packets );
2007     msg_rc(_("| bytes sent       : %8.0f kB"),
2008             (float)(p_item->p_stats->i_sent_bytes)/1000 );
2009     msg_rc(_("| sending bitrate  :   %6.0f kb/s"),
2010             (float)(p_item->p_stats->f_send_bitrate*8)*1000 );
2011     msg_rc("|");
2012     msg_rc( "+----[ end of statistical info ]" );
2013     vlc_mutex_unlock( &p_item->p_stats->lock );
2014
2015     return VLC_SUCCESS;
2016 }
2017
2018 #ifdef WIN32
2019 bool ReadWin32( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
2020 {
2021     INPUT_RECORD input_record;
2022     DWORD i_dw;
2023
2024     /* On Win32, select() only works on socket descriptors */
2025     while( WaitForSingleObject( p_intf->p_sys->hConsoleIn,
2026                                 INTF_IDLE_SLEEP/1000 ) == WAIT_OBJECT_0 )
2027     {
2028         while( vlc_object_alive( p_intf ) && *pi_size < MAX_LINE_LENGTH &&
2029                ReadConsoleInput( p_intf->p_sys->hConsoleIn, &input_record,
2030                                  1, &i_dw ) )
2031         {
2032             if( input_record.EventType != KEY_EVENT ||
2033                 !input_record.Event.KeyEvent.bKeyDown ||
2034                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
2035                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL||
2036                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_MENU ||
2037                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CAPITAL )
2038             {
2039                 /* nothing interesting */
2040                 continue;
2041             }
2042
2043             p_buffer[ *pi_size ] = input_record.Event.KeyEvent.uChar.AsciiChar;
2044
2045             /* Echo out the command */
2046             putc( p_buffer[ *pi_size ], stdout );
2047
2048             /* Handle special keys */
2049             if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
2050             {
2051                 putc( '\n', stdout );
2052                 break;
2053             }
2054             switch( p_buffer[ *pi_size ] )
2055             {
2056             case '\b':
2057                 if( *pi_size )
2058                 {
2059                     *pi_size -= 2;
2060                     putc( ' ', stdout );
2061                     putc( '\b', stdout );
2062                 }
2063                 break;
2064             case '\r':
2065                 (*pi_size) --;
2066                 break;
2067             }
2068
2069             (*pi_size)++;
2070         }
2071
2072         if( *pi_size == MAX_LINE_LENGTH ||
2073             p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
2074         {
2075             p_buffer[ *pi_size ] = 0;
2076             return true;
2077         }
2078     }
2079
2080     return false;
2081 }
2082 #endif
2083
2084 bool ReadCommand( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
2085 {
2086     int i_read = 0;
2087
2088 #ifdef WIN32
2089     if( p_intf->p_sys->i_socket == -1 && !p_intf->p_sys->b_quiet )
2090         return ReadWin32( p_intf, p_buffer, pi_size );
2091     else if( p_intf->p_sys->i_socket == -1 )
2092     {
2093         msleep( INTF_IDLE_SLEEP );
2094         return false;
2095     }
2096 #endif
2097
2098     while( vlc_object_alive( p_intf ) && *pi_size < MAX_LINE_LENGTH &&
2099            (i_read = net_Read( p_intf, p_intf->p_sys->i_socket == -1 ?
2100                        0 /*STDIN_FILENO*/ : p_intf->p_sys->i_socket, NULL,
2101                   (uint8_t *)p_buffer + *pi_size, 1, false ) ) > 0 )
2102     {
2103         if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
2104             break;
2105
2106         (*pi_size)++;
2107     }
2108
2109     /* Connection closed */
2110     if( i_read <= 0 )
2111     {
2112         if( p_intf->p_sys->i_socket != -1 )
2113         {
2114             net_Close( p_intf->p_sys->i_socket );
2115             p_intf->p_sys->i_socket = -1;
2116         }
2117         else
2118         {
2119             /* Standard input closed: exit */
2120             vlc_value_t empty;
2121             Quit( VLC_OBJECT(p_intf), NULL, empty, empty, NULL );
2122         }
2123
2124         p_buffer[ *pi_size ] = 0;
2125         return true;
2126     }
2127
2128     if( *pi_size == MAX_LINE_LENGTH ||
2129         p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
2130     {
2131         p_buffer[ *pi_size ] = 0;
2132         return true;
2133     }
2134
2135     return false;
2136 }
2137
2138 /*****************************************************************************
2139  * parse_MRL: build a input item from a full mrl
2140  *****************************************************************************
2141  * MRL format: "simplified-mrl [:option-name[=option-value]]"
2142  * We don't check for '"' or '\'', we just assume that a ':' that follows a
2143  * space is a new option. Should be good enough for our purpose.
2144  *****************************************************************************/
2145 static input_item_t *parse_MRL( intf_thread_t *p_intf, char *psz_mrl )
2146 {
2147 #define SKIPSPACE( p ) { while( *p == ' ' || *p == '\t' ) p++; }
2148 #define SKIPTRAILINGSPACE( p, d ) \
2149     { char *e=d; while( e > p && (*(e-1)==' ' || *(e-1)=='\t') ){e--;*e=0;} }
2150
2151     input_item_t *p_item = NULL;
2152     char *psz_item = NULL, *psz_item_mrl = NULL, *psz_orig;
2153     char **ppsz_options = NULL;
2154     int i, i_options = 0;
2155
2156     if( !psz_mrl ) return 0;
2157
2158     psz_mrl = psz_orig = strdup( psz_mrl );
2159     while( *psz_mrl )
2160     {
2161         SKIPSPACE( psz_mrl );
2162         psz_item = psz_mrl;
2163
2164         for( ; *psz_mrl; psz_mrl++ )
2165         {
2166             if( (*psz_mrl == ' ' || *psz_mrl == '\t') && psz_mrl[1] == ':' )
2167             {
2168                 /* We have a complete item */
2169                 break;
2170             }
2171             if( (*psz_mrl == ' ' || *psz_mrl == '\t') &&
2172                 (psz_mrl[1] == '"' || psz_mrl[1] == '\'') && psz_mrl[2] == ':')
2173             {
2174                 /* We have a complete item */
2175                 break;
2176             }
2177         }
2178
2179         if( *psz_mrl ) { *psz_mrl = 0; psz_mrl++; }
2180         SKIPTRAILINGSPACE( psz_item, psz_item + strlen( psz_item ) );
2181
2182         /* Remove '"' and '\'' if necessary */
2183         if( *psz_item == '"' && psz_item[strlen(psz_item)-1] == '"' )
2184         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
2185         if( *psz_item == '\'' && psz_item[strlen(psz_item)-1] == '\'' )
2186         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
2187
2188         if( !psz_item_mrl ) psz_item_mrl = psz_item;
2189         else if( *psz_item )
2190         {
2191             i_options++;
2192             ppsz_options = realloc( ppsz_options, i_options * sizeof(char *) );
2193             ppsz_options[i_options - 1] = &psz_item[1];
2194         }
2195
2196         if( *psz_mrl ) SKIPSPACE( psz_mrl );
2197     }
2198
2199     /* Now create a playlist item */
2200     if( psz_item_mrl )
2201     {
2202         p_item = input_item_New( p_intf, psz_item_mrl, NULL );
2203         for( i = 0; i < i_options; i++ )
2204         {
2205             input_item_AddOption( p_item, ppsz_options[i], VLC_INPUT_OPTION_TRUSTED );
2206         }
2207     }
2208
2209     if( i_options ) free( ppsz_options );
2210     free( psz_orig );
2211
2212     return p_item;
2213 }