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