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