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