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