rc: remove one vlc_object_find.
[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_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 1024
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( "%s", psz_msg );
638                     free( psz_msg );
639                 }
640             }
641             free( psz_mycmd );
642         }
643         /* If the user typed a registered local command, try it */
644         else if( var_Type( p_intf, psz_cmd ) & VLC_VAR_ISCOMMAND )
645         {
646             vlc_value_t val;
647             int i_ret;
648
649             val.psz_string = psz_arg;
650             i_ret = var_Set( p_intf, psz_cmd, val );
651             msg_rc( "%s: returned %i (%s)",
652                     psz_cmd, i_ret, vlc_error( i_ret ) );
653         }
654         /* Or maybe it's a global command */
655         else if( var_Type( p_intf->p_libvlc, psz_cmd ) & VLC_VAR_ISCOMMAND )
656         {
657             vlc_value_t val;
658             int i_ret;
659
660             val.psz_string = psz_arg;
661             /* FIXME: it's a global command, but we should pass the
662              * local object as an argument, not p_intf->p_libvlc. */
663             i_ret = var_Set( p_intf->p_libvlc, psz_cmd, val );
664             if( i_ret != 0 )
665             {
666                 msg_rc( "%s: returned %i (%s)",
667                          psz_cmd, i_ret, vlc_error( i_ret ) );
668             }
669         }
670         else if( !strcmp( psz_cmd, "logout" ) )
671         {
672             /* Close connection */
673             if( p_intf->p_sys->i_socket != -1 )
674             {
675                 net_Close( p_intf->p_sys->i_socket );
676             }
677             p_intf->p_sys->i_socket = -1;
678         }
679         else if( !strcmp( psz_cmd, "info" ) )
680         {
681             if( p_input )
682             {
683                 int i, j;
684                 vlc_mutex_lock( &input_GetItem(p_input)->lock );
685                 for ( i = 0; i < input_GetItem(p_input)->i_categories; i++ )
686                 {
687                     info_category_t *p_category = input_GetItem(p_input)
688                                                         ->pp_categories[i];
689
690                     msg_rc( "+----[ %s ]", p_category->psz_name );
691                     msg_rc( "| " );
692                     for ( j = 0; j < p_category->i_infos; j++ )
693                     {
694                         info_t *p_info = p_category->pp_infos[j];
695                         msg_rc( "| %s: %s", p_info->psz_name,
696                                 p_info->psz_value );
697                     }
698                     msg_rc( "| " );
699                 }
700                 msg_rc( "+----[ end of stream info ]" );
701                 vlc_mutex_unlock( &input_GetItem(p_input)->lock );
702             }
703             else
704             {
705                 msg_rc( "no input" );
706             }
707         }
708         else if( !strcmp( psz_cmd, "is_playing" ) )
709         {
710             if( ! p_input )
711             {
712                 msg_rc( "0" );
713             }
714             else
715             {
716                 msg_rc( "1" );
717             }
718         }
719         else if( !strcmp( psz_cmd, "get_time" ) )
720         {
721             if( ! p_input )
722             {
723                 msg_rc("0");
724             }
725             else
726             {
727                 vlc_value_t time;
728                 var_Get( p_input, "time", &time );
729                 msg_rc( "%"PRIu64, time.i_time / 1000000);
730             }
731         }
732         else if( !strcmp( psz_cmd, "get_length" ) )
733         {
734             if( ! p_input )
735             {
736                 msg_rc("0");
737             }
738             else
739             {
740                 vlc_value_t time;
741                 var_Get( p_input, "length", &time );
742                 msg_rc( "%"PRIu64, time.i_time / 1000000);
743             }
744         }
745         else if( !strcmp( psz_cmd, "get_title" ) )
746         {
747             if( ! p_input )
748             {
749                 msg_rc("%s", "");
750             }
751             else
752             {
753                 msg_rc( "%s", input_GetItem(p_input)->psz_name );
754             }
755         }
756         else if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "h", 1 )
757                  || !strncmp( psz_cmd, "H", 1 ) || !strncmp( psz_cmd, "?", 1 ) )
758         {
759             if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "H", 1 ) )
760                  b_longhelp = true;
761             else b_longhelp = false;
762
763             Help( p_intf, b_longhelp );
764         }
765         else if( !strcmp( psz_cmd, "key" ) || !strcmp( psz_cmd, "hotkey" ) )
766         {
767             var_SetInteger( p_intf->p_libvlc, "key-pressed",
768                             config_GetInt( p_intf, psz_arg ) );
769         }
770         else switch( psz_cmd[0] )
771         {
772         case 'f':
773         case 'F':
774             if( p_input )
775             {
776                 vout_thread_t *p_vout;
777                 p_vout = vlc_object_find( p_input,
778                                           VLC_OBJECT_VOUT, FIND_CHILD );
779
780                 if( p_vout )
781                 {
782                     vlc_value_t val;
783                     bool b_update = false;
784                     var_Get( p_vout, "fullscreen", &val );
785                     val.b_bool = !val.b_bool;
786                     if( !strncmp( psz_arg, "on", 2 )
787                         && ( val.b_bool == true ) )
788                     {
789                         b_update = true;
790                         val.b_bool = true;
791                     }
792                     else if( !strncmp( psz_arg, "off", 3 )
793                              && ( val.b_bool == false ) )
794                     {
795                         b_update = true;
796                         val.b_bool = false;
797                     }
798                     else if( strncmp( psz_arg, "off", 3 )
799                              && strncmp( psz_arg, "on", 2 ) )
800                         b_update = true;
801                     if( b_update ) var_Set( p_vout, "fullscreen", val );
802                     vlc_object_release( p_vout );
803                 }
804             }
805             break;
806
807         case 's':
808         case 'S':
809             ;
810             break;
811
812         case '\0':
813             /* Ignore empty lines */
814             break;
815
816         default:
817             msg_rc(_("Unknown command `%s'. Type `help' for help."), psz_cmd);
818             break;
819         }
820
821         /* Command processed */
822         i_size = 0; p_buffer[0] = 0;
823     }
824
825     msg_rc( STATUS_CHANGE "( stop state: 0 )" );
826     msg_rc( STATUS_CHANGE "( quit )" );
827
828     if( p_input )
829     {
830         var_DelCallback( p_input, "state", StateChanged, p_intf );
831         var_DelCallback( p_input, "rate-faster", RateChanged, p_intf );
832         var_DelCallback( p_input, "rate-slower", RateChanged, p_intf );
833         var_DelCallback( p_input, "rate", RateChanged, p_intf );
834         var_DelCallback( p_input, "time-offset", TimeOffsetChanged, p_intf );
835         vlc_object_release( p_input );
836         p_input = NULL;
837     }
838
839     if( p_playlist )
840     {
841         vlc_object_release( p_playlist );
842         p_playlist = NULL;
843     }
844
845     var_DelCallback( p_intf->p_libvlc, "volume-change", VolumeChanged, p_intf );
846     vlc_restorecancel( canc );
847 }
848
849 static void Help( intf_thread_t *p_intf, bool b_longhelp)
850 {
851     msg_rc(_("+----[ Remote control commands ]"));
852     msg_rc(  "| ");
853     msg_rc(_("| add XYZ  . . . . . . . . . . . . add XYZ to playlist"));
854     msg_rc(_("| enqueue XYZ  . . . . . . . . . queue XYZ to playlist"));
855     msg_rc(_("| playlist . . . . .  show items currently in playlist"));
856     msg_rc(_("| play . . . . . . . . . . . . . . . . . . play stream"));
857     msg_rc(_("| stop . . . . . . . . . . . . . . . . . . stop stream"));
858     msg_rc(_("| next . . . . . . . . . . . . . .  next playlist item"));
859     msg_rc(_("| prev . . . . . . . . . . . .  previous playlist item"));
860     msg_rc(_("| goto . . . . . . . . . . . . . .  goto item at index"));
861     msg_rc(_("| repeat [on|off] . . . .  toggle playlist item repeat"));
862     msg_rc(_("| loop [on|off] . . . . . . . . . toggle playlist loop"));
863     msg_rc(_("| random [on|off] . . . . . . .  toggle random jumping"));
864     msg_rc(_("| clear . . . . . . . . . . . . . . clear the playlist"));
865     msg_rc(_("| status . . . . . . . . . . . current playlist status"));
866     msg_rc(_("| title [X]  . . . . . . set/get title in current item"));
867     msg_rc(_("| title_n  . . . . . . . .  next title in current item"));
868     msg_rc(_("| title_p  . . . . . .  previous title in current item"));
869     msg_rc(_("| chapter [X]  . . . . set/get chapter in current item"));
870     msg_rc(_("| chapter_n  . . . . . .  next chapter in current item"));
871     msg_rc(_("| chapter_p  . . . .  previous chapter in current item"));
872     msg_rc(  "| ");
873     msg_rc(_("| seek X . . . seek in seconds, for instance `seek 12'"));
874     msg_rc(_("| pause  . . . . . . . . . . . . . . . .  toggle pause"));
875     msg_rc(_("| fastforward  . . . . . . . .  .  set to maximum rate"));
876     msg_rc(_("| rewind  . . . . . . . . . . . .  set to minimum rate"));
877     msg_rc(_("| faster . . . . . . . . . .  faster playing of stream"));
878     msg_rc(_("| slower . . . . . . . . . .  slower playing of stream"));
879     msg_rc(_("| normal . . . . . . . . . .  normal playing of stream"));
880     msg_rc(_("| f [on|off] . . . . . . . . . . . . toggle fullscreen"));
881     msg_rc(_("| info . . . . .  information about the current stream"));
882     msg_rc(_("| stats  . . . . . . . .  show statistical information"));
883     msg_rc(_("| get_time . . seconds elapsed since stream's beginning"));
884     msg_rc(_("| is_playing . . . .  1 if a stream plays, 0 otherwise"));
885     msg_rc(_("| get_title . . . . .  the title of the current stream"));
886     msg_rc(_("| get_length . . . .  the length of the current stream"));
887     msg_rc(  "| ");
888     msg_rc(_("| volume [X] . . . . . . . . . .  set/get audio volume"));
889     msg_rc(_("| volup [X]  . . . . . . .  raise audio volume X steps"));
890     msg_rc(_("| voldown [X]  . . . . . .  lower audio volume X steps"));
891     msg_rc(_("| adev [X] . . . . . . . . . . .  set/get audio device"));
892     msg_rc(_("| achan [X]. . . . . . . . . .  set/get audio channels"));
893     msg_rc(_("| atrack [X] . . . . . . . . . . . set/get audio track"));
894     msg_rc(_("| vtrack [X] . . . . . . . . . . . set/get video track"));
895     msg_rc(_("| vratio [X]  . . . . . . . set/get video aspect ratio"));
896     msg_rc(_("| vcrop [X]  . . . . . . . . . . .  set/get video crop"));
897     msg_rc(_("| vzoom [X]  . . . . . . . . . . .  set/get video zoom"));
898     msg_rc(_("| snapshot . . . . . . . . . . . . take video snapshot"));
899     msg_rc(_("| strack [X] . . . . . . . . . set/get subtitles track"));
900     msg_rc(_("| key [hotkey name] . . . . . .  simulate hotkey press"));
901     msg_rc(_("| menu . . [on|off|up|down|left|right|select] use menu"));
902     msg_rc(  "| ");
903
904     if (b_longhelp)
905     {
906         msg_rc(_("| @name marq-marquee  STRING  . . overlay STRING in video"));
907         msg_rc(_("| @name marq-x X . . . . . . . . . . . .offset from left"));
908         msg_rc(_("| @name marq-y Y . . . . . . . . . . . . offset from top"));
909         msg_rc(_("| @name marq-position #. . .  .relative position control"));
910         msg_rc(_("| @name marq-color # . . . . . . . . . . font color, RGB"));
911         msg_rc(_("| @name marq-opacity # . . . . . . . . . . . . . opacity"));
912         msg_rc(_("| @name marq-timeout T. . . . . . . . . . timeout, in ms"));
913         msg_rc(_("| @name marq-size # . . . . . . . . font size, in pixels"));
914         msg_rc(  "| ");
915         msg_rc(_("| @name logo-file STRING . . .the overlay file path/name"));
916         msg_rc(_("| @name logo-x X . . . . . . . . . . . .offset from left"));
917         msg_rc(_("| @name logo-y Y . . . . . . . . . . . . offset from top"));
918         msg_rc(_("| @name logo-position #. . . . . . . . relative position"));
919         msg_rc(_("| @name logo-transparency #. . . . . . . . .transparency"));
920         msg_rc(  "| ");
921         msg_rc(_("| @name mosaic-alpha # . . . . . . . . . . . . . . alpha"));
922         msg_rc(_("| @name mosaic-height #. . . . . . . . . . . . . .height"));
923         msg_rc(_("| @name mosaic-width # . . . . . . . . . . . . . . width"));
924         msg_rc(_("| @name mosaic-xoffset # . . . .top left corner position"));
925         msg_rc(_("| @name mosaic-yoffset # . . . .top left corner position"));
926         msg_rc(_("| @name mosaic-offsets x,y(,x,y)*. . . . list of offsets"));
927         msg_rc(_("| @name mosaic-align 0..2,4..6,8..10. . .mosaic alignment"));
928         msg_rc(_("| @name mosaic-vborder # . . . . . . . . vertical border"));
929         msg_rc(_("| @name mosaic-hborder # . . . . . . . horizontal border"));
930         msg_rc(_("| @name mosaic-position {0=auto,1=fixed} . . . .position"));
931         msg_rc(_("| @name mosaic-rows #. . . . . . . . . . .number of rows"));
932         msg_rc(_("| @name mosaic-cols #. . . . . . . . . . .number of cols"));
933         msg_rc(_("| @name mosaic-order id(,id)* . . . . order of pictures "));
934         msg_rc(_("| @name mosaic-keep-aspect-ratio {0,1} . . .aspect ratio"));
935         msg_rc(  "| ");
936     }
937     msg_rc(_("| help . . . . . . . . . . . . . . . this help message"));
938     msg_rc(_("| longhelp . . . . . . . . . . . a longer help message"));
939     msg_rc(_("| logout . . . . . . .  exit (if in socket connection)"));
940     msg_rc(_("| quit . . . . . . . . . . . . . . . . . . .  quit vlc"));
941     msg_rc(  "| ");
942     msg_rc(_("+----[ end of help ]"));
943 }
944
945 /********************************************************************
946  * Status callback routines
947  ********************************************************************/
948 static int TimeOffsetChanged( vlc_object_t *p_this, char const *psz_cmd,
949     vlc_value_t oldval, vlc_value_t newval, void *p_data )
950 {
951     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd);
952     VLC_UNUSED(oldval); VLC_UNUSED(newval);
953     intf_thread_t *p_intf = (intf_thread_t*)p_data;
954     input_thread_t *p_input = NULL;
955
956     vlc_mutex_lock( &p_intf->p_sys->status_lock );
957     p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
958     if( p_input )
959     {
960         msg_rc( STATUS_CHANGE "( time-offset: %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     char cmd[6];
988
989     vlc_mutex_lock( &p_intf->p_sys->status_lock );
990     p_playlist = pl_Hold( p_intf );
991     PL_LOCK;
992     int i_status = playlist_Status( p_playlist );
993     PL_UNLOCK;
994     pl_Release( p_intf );
995
996     switch( i_status )
997     {
998     case PLAYLIST_STOPPED:
999         strcpy( cmd, "stop" );
1000         break;
1001     case PLAYLIST_RUNNING:
1002         strcpy( cmd, "play" );
1003         break;
1004     case PLAYLIST_PAUSED:
1005         strcpy( cmd, "pause" );
1006         break;
1007     default:
1008         cmd[0] = '\0';
1009     } /* var_GetInteger( p_input, "state" )  */
1010     msg_rc( STATUS_CHANGE "( %s state: %d ): %s", cmd, newval.i_int,
1011             ppsz_input_state[ newval.i_int ] );
1012
1013     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
1014     return VLC_SUCCESS;
1015 }
1016
1017 static int RateChanged( vlc_object_t *p_this, char const *psz_cmd,
1018     vlc_value_t oldval, vlc_value_t newval, void *p_data )
1019 {
1020     VLC_UNUSED(p_this); VLC_UNUSED(psz_cmd);
1021     VLC_UNUSED(oldval); VLC_UNUSED(newval);
1022     intf_thread_t *p_intf = (intf_thread_t*)p_data;
1023     input_thread_t *p_input = NULL;
1024
1025     vlc_mutex_lock( &p_intf->p_sys->status_lock );
1026     p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1027     if( p_input )
1028     {
1029         msg_rc( STATUS_CHANGE "( new rate: %d )",
1030                 var_GetInteger( p_input, "rate" ) );
1031         vlc_object_release( p_input );
1032     }
1033     vlc_mutex_unlock( &p_intf->p_sys->status_lock );
1034     return VLC_SUCCESS;
1035 }
1036
1037 /********************************************************************
1038  * Command routines
1039  ********************************************************************/
1040 static int Input( vlc_object_t *p_this, char const *psz_cmd,
1041                   vlc_value_t oldval, vlc_value_t newval, void *p_data )
1042 {
1043     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1044     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1045     input_thread_t *p_input;
1046     vlc_value_t     val;
1047
1048     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1049     if( !p_input ) return VLC_ENOOBJ;
1050
1051     var_Get( p_input, "state", &val );
1052     if( ( val.i_int == PAUSE_S ) &&
1053         ( strcmp( psz_cmd, "pause" ) != 0 ) )
1054     {
1055         msg_rc( _("Press menu select or pause to continue.") );
1056         vlc_object_release( p_input );
1057         return VLC_EGENERIC;
1058     }
1059
1060     /* Parse commands that only require an input */
1061     if( !strcmp( psz_cmd, "pause" ) )
1062     {
1063         val.i_int = config_GetInt( p_intf, "key-play-pause" );
1064         var_Set( p_intf->p_libvlc, "key-pressed", val );
1065         vlc_object_release( p_input );
1066         return VLC_SUCCESS;
1067     }
1068     else if( !strcmp( psz_cmd, "seek" ) )
1069     {
1070         if( strlen( newval.psz_string ) > 0 &&
1071             newval.psz_string[strlen( newval.psz_string ) - 1] == '%' )
1072         {
1073             val.f_float = (float)atof( newval.psz_string ) / 100.0;
1074             var_Set( p_input, "position", val );
1075         }
1076         else
1077         {
1078             val.i_time = ((int64_t)atoi( newval.psz_string )) * 1000000;
1079             var_Set( p_input, "time", val );
1080         }
1081         vlc_object_release( p_input );
1082         return VLC_SUCCESS;
1083     }
1084     else if ( !strcmp( psz_cmd, "fastforward" ) )
1085     {
1086         if( var_GetBool( p_input, "can-rate" ) )
1087         {
1088             int i_rate = var_GetInteger( p_input, "rate" );
1089             i_rate = (i_rate < 0) ? -i_rate : i_rate * 2;
1090             var_SetInteger( p_input, "rate", i_rate );
1091         }
1092         else
1093         {
1094             val.i_int = config_GetInt( p_intf, "key-jump+extrashort" );
1095             var_Set( p_intf->p_libvlc, "key-pressed", val );
1096         }
1097         vlc_object_release( p_input );
1098         return VLC_SUCCESS;
1099     }
1100     else if ( !strcmp( psz_cmd, "rewind" ) )
1101     {
1102         if( var_GetBool( p_input, "can-rewind" ) )
1103         {
1104             int i_rate = var_GetInteger( p_input, "rate" );
1105             i_rate = (i_rate > 0) ? -i_rate : i_rate / 2;
1106             var_SetInteger( p_input, "rate", i_rate );
1107         }
1108         else
1109         {
1110             val.i_int = config_GetInt( p_intf, "key-jump-extrashort" );
1111             var_Set( p_intf->p_libvlc, "key-pressed", val );
1112         }
1113         vlc_object_release( p_input );
1114         return VLC_SUCCESS;
1115     }
1116     else if ( !strcmp( psz_cmd, "faster" ) )
1117     {
1118         var_Set( p_input, "rate-faster", val );
1119         vlc_object_release( p_input );
1120         return VLC_SUCCESS;
1121     }
1122     else if ( !strcmp( psz_cmd, "slower" ) )
1123     {
1124         var_Set( p_input, "rate-slower", val );
1125         vlc_object_release( p_input );
1126         return VLC_SUCCESS;
1127     }
1128     else if ( !strcmp( psz_cmd, "normal" ) )
1129     {
1130         val.i_int = INPUT_RATE_DEFAULT;
1131         var_Set( p_input, "rate", val );
1132         vlc_object_release( p_input );
1133         return VLC_SUCCESS;
1134     }
1135     else if( !strcmp( psz_cmd, "chapter" ) ||
1136              !strcmp( psz_cmd, "chapter_n" ) ||
1137              !strcmp( psz_cmd, "chapter_p" ) )
1138     {
1139         if( !strcmp( psz_cmd, "chapter" ) )
1140         {
1141             if ( *newval.psz_string )
1142             {
1143                 /* Set. */
1144                 val.i_int = atoi( newval.psz_string );
1145                 var_Set( p_input, "chapter", val );
1146             }
1147             else
1148             {
1149                 vlc_value_t val_list;
1150
1151                 /* Get. */
1152                 var_Get( p_input, "chapter", &val );
1153                 var_Change( p_input, "chapter", VLC_VAR_GETCHOICES,
1154                             &val_list, NULL );
1155                 msg_rc( "Currently playing chapter %d/%d.",
1156                         val.i_int, val_list.p_list->i_count );
1157                 var_Change( p_this, "chapter", VLC_VAR_FREELIST,
1158                             &val_list, NULL );
1159             }
1160         }
1161         else if( !strcmp( psz_cmd, "chapter_n" ) )
1162         {
1163             val.b_bool = true;
1164             var_Set( p_input, "next-chapter", val );
1165         }
1166         else if( !strcmp( psz_cmd, "chapter_p" ) )
1167         {
1168             val.b_bool = true;
1169             var_Set( p_input, "prev-chapter", val );
1170         }
1171         vlc_object_release( p_input );
1172         return VLC_SUCCESS;
1173     }
1174     else if( !strcmp( psz_cmd, "title" ) ||
1175              !strcmp( psz_cmd, "title_n" ) ||
1176              !strcmp( psz_cmd, "title_p" ) )
1177     {
1178         if( !strcmp( psz_cmd, "title" ) )
1179         {
1180             if ( *newval.psz_string )
1181             {
1182                 /* Set. */
1183                 val.i_int = atoi( newval.psz_string );
1184                 var_Set( p_input, "title", val );
1185             }
1186             else
1187             {
1188                 vlc_value_t val_list;
1189
1190                 /* Get. */
1191                 var_Get( p_input, "title", &val );
1192                 var_Change( p_input, "title", VLC_VAR_GETCHOICES,
1193                             &val_list, NULL );
1194                 msg_rc( "Currently playing title %d/%d.",
1195                         val.i_int, val_list.p_list->i_count );
1196                 var_Change( p_this, "title", VLC_VAR_FREELIST,
1197                             &val_list, NULL );
1198             }
1199         }
1200         else if( !strcmp( psz_cmd, "title_n" ) )
1201         {
1202             val.b_bool = true;
1203             var_Set( p_input, "next-title", val );
1204         }
1205         else if( !strcmp( psz_cmd, "title_p" ) )
1206         {
1207             val.b_bool = true;
1208             var_Set( p_input, "prev-title", val );
1209         }
1210
1211         vlc_object_release( p_input );
1212         return VLC_SUCCESS;
1213     }
1214     else if(    !strcmp( psz_cmd, "atrack" )
1215              || !strcmp( psz_cmd, "vtrack" )
1216              || !strcmp( psz_cmd, "strack" ) )
1217     {
1218         const char *psz_variable;
1219         vlc_value_t val_name;
1220         int i_error;
1221
1222         if( !strcmp( psz_cmd, "atrack" ) )
1223         {
1224             psz_variable = "audio-es";
1225         }
1226         else if( !strcmp( psz_cmd, "vtrack" ) )
1227         {
1228             psz_variable = "video-es";
1229         }
1230         else
1231         {
1232             psz_variable = "spu-es";
1233         }
1234
1235         /* Get the descriptive name of the variable */
1236         var_Change( p_input, psz_variable, VLC_VAR_GETTEXT,
1237                      &val_name, NULL );
1238         if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1239
1240         if( newval.psz_string && *newval.psz_string )
1241         {
1242             /* set */
1243             vlc_value_t val;
1244             val.i_int = atoi( newval.psz_string );
1245
1246             i_error = var_Set( p_input, psz_variable, val );
1247         }
1248         else
1249         {
1250             /* get */
1251             vlc_value_t val, text;
1252             int i, i_value;
1253
1254             if ( var_Get( p_input, psz_variable, &val ) < 0 )
1255             {
1256                 vlc_object_release( p_input );
1257                 return VLC_EGENERIC;
1258             }
1259             i_value = val.i_int;
1260
1261             if ( var_Change( p_input, psz_variable,
1262                              VLC_VAR_GETLIST, &val, &text ) < 0 )
1263             {
1264                 vlc_object_release( p_input );
1265                 return VLC_EGENERIC;
1266             }
1267
1268             msg_rc( "+----[ %s ]", val_name.psz_string );
1269             for ( i = 0; i < val.p_list->i_count; i++ )
1270             {
1271                 if ( i_value == val.p_list->p_values[i].i_int )
1272                     msg_rc( "| %i - %s *", val.p_list->p_values[i].i_int,
1273                             text.p_list->p_values[i].psz_string );
1274                 else
1275                     msg_rc( "| %i - %s", val.p_list->p_values[i].i_int,
1276                             text.p_list->p_values[i].psz_string );
1277             }
1278             var_Change( p_input, psz_variable, VLC_VAR_FREELIST,
1279                         &val, &text );
1280             msg_rc( "+----[ end of %s ]", val_name.psz_string );
1281
1282             free( val_name.psz_string );
1283
1284             i_error = VLC_SUCCESS;
1285         }
1286         vlc_object_release( p_input );
1287         return i_error;
1288     }
1289
1290     /* Never reached. */
1291     vlc_object_release( p_input );
1292     return VLC_EGENERIC;
1293 }
1294
1295 static void print_playlist( intf_thread_t *p_intf, playlist_item_t *p_item, int i_level )
1296 {
1297     int i;
1298     char psz_buffer[MSTRTIME_MAX_SIZE];
1299     for( i = 0; i< p_item->i_children; i++ )
1300     {
1301         if( p_item->pp_children[i]->p_input->i_duration != -1 )
1302         {
1303             secstotimestr( psz_buffer, p_item->pp_children[i]->p_input->i_duration / 1000000 );
1304             msg_rc( "|%*s- %s (%s)", 2 * i_level, "", p_item->pp_children[i]->p_input->psz_name, psz_buffer );
1305         }
1306         else
1307             msg_rc( "|%*s- %s", 2 * i_level, "", p_item->pp_children[i]->p_input->psz_name );
1308
1309         if( p_item->pp_children[i]->i_children >= 0 )
1310             print_playlist( p_intf, p_item->pp_children[i], i_level + 1 );
1311     }
1312 }
1313
1314 static int Playlist( vlc_object_t *p_this, char const *psz_cmd,
1315                      vlc_value_t oldval, vlc_value_t newval, void *p_data )
1316 {
1317     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1318     vlc_value_t val;
1319
1320     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1321     playlist_t *p_playlist = pl_Hold( p_this );
1322     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1323
1324     if( p_input )
1325     {
1326         var_Get( p_input, "state", &val );
1327         vlc_object_release( p_input );
1328
1329         if( val.i_int == PAUSE_S )
1330         {
1331             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
1332             pl_Release( p_this );
1333             return VLC_EGENERIC;
1334         }
1335     }
1336
1337     /* Parse commands that require a playlist */
1338     if( !strcmp( psz_cmd, "prev" ) )
1339     {
1340         playlist_Prev( p_playlist );
1341     }
1342     else if( !strcmp( psz_cmd, "next" ) )
1343     {
1344         playlist_Next( p_playlist );
1345     }
1346     else if( !strcmp( psz_cmd, "play" ) )
1347     {
1348         msg_Warn( p_playlist, "play" );
1349         playlist_Play( p_playlist );
1350     }
1351     else if( !strcmp( psz_cmd, "repeat" ) )
1352     {
1353         bool b_update = true;
1354
1355         var_Get( p_playlist, "repeat", &val );
1356
1357         if( strlen( newval.psz_string ) > 0 )
1358         {
1359             if ( ( !strncmp( newval.psz_string, "on", 2 ) && ( val.b_bool == true ) ) ||
1360                  ( !strncmp( newval.psz_string, "off", 3 ) && ( val.b_bool == false ) ) )
1361             {
1362                 b_update = false;
1363             }
1364         }
1365
1366         if ( b_update )
1367         {
1368             val.b_bool = !val.b_bool;
1369             var_Set( p_playlist, "repeat", val );
1370         }
1371         msg_rc( "Setting repeat to %d", val.b_bool );
1372     }
1373     else if( !strcmp( psz_cmd, "loop" ) )
1374     {
1375         bool b_update = true;
1376
1377         var_Get( p_playlist, "loop", &val );
1378
1379         if( strlen( newval.psz_string ) > 0 )
1380         {
1381             if ( ( !strncmp( newval.psz_string, "on", 2 ) && ( val.b_bool == true ) ) ||
1382                  ( !strncmp( newval.psz_string, "off", 3 ) && ( val.b_bool == false ) ) )
1383             {
1384                 b_update = false;
1385             }
1386         }
1387
1388         if ( b_update )
1389         {
1390             val.b_bool = !val.b_bool;
1391             var_Set( p_playlist, "loop", val );
1392         }
1393         msg_rc( "Setting loop to %d", val.b_bool );
1394     }
1395     else if( !strcmp( psz_cmd, "random" ) )
1396     {
1397         bool b_update = true;
1398
1399         var_Get( p_playlist, "random", &val );
1400
1401         if( strlen( newval.psz_string ) > 0 )
1402         {
1403             if ( ( !strncmp( newval.psz_string, "on", 2 ) && ( val.b_bool == true ) ) ||
1404                  ( !strncmp( newval.psz_string, "off", 3 ) && ( val.b_bool == false ) ) )
1405             {
1406                 b_update = false;
1407             }
1408         }
1409
1410         if ( b_update )
1411         {
1412             val.b_bool = !val.b_bool;
1413             var_Set( p_playlist, "random", val );
1414         }
1415         msg_rc( "Setting random to %d", val.b_bool );
1416     }
1417     else if (!strcmp( psz_cmd, "goto" ) )
1418     {
1419         int i_pos = atoi( newval.psz_string );
1420         /* The playlist stores 2 times the same item: onelevel & category */
1421         int i_size = p_playlist->items.i_size / 2;
1422
1423         if( i_pos <= 0 )
1424             msg_rc( _("Error: `goto' needs an argument greater than zero.") );
1425         else if( i_pos <= i_size )
1426         {
1427             playlist_item_t *p_item, *p_parent;
1428             p_item = p_parent = p_playlist->items.p_elems[i_pos*2-1];
1429             while( p_parent->p_parent )
1430                 p_parent = p_parent->p_parent;
1431             playlist_Control( p_playlist, PLAYLIST_VIEWPLAY, pl_Unlocked,
1432                     p_parent, p_item );
1433         }
1434         else
1435             msg_rc( _("Playlist has only %d elements"), i_size );
1436     }
1437     else if( !strcmp( psz_cmd, "stop" ) )
1438     {
1439         playlist_Stop( p_playlist );
1440     }
1441     else if( !strcmp( psz_cmd, "clear" ) )
1442     {
1443         playlist_Stop( p_playlist );
1444         playlist_Clear( p_playlist, pl_Unlocked );
1445     }
1446     else if( !strcmp( psz_cmd, "add" ) &&
1447              newval.psz_string && *newval.psz_string )
1448     {
1449         input_item_t *p_item = parse_MRL( p_intf, newval.psz_string );
1450
1451         if( p_item )
1452         {
1453             msg_rc( "Trying to add %s to playlist.", newval.psz_string );
1454             int i_ret =playlist_AddInput( p_playlist, p_item,
1455                      PLAYLIST_GO|PLAYLIST_APPEND, PLAYLIST_END, true,
1456                      pl_Unlocked );
1457             vlc_gc_decref( p_item );
1458             if( i_ret != VLC_SUCCESS )
1459             {
1460                 return VLC_EGENERIC;
1461             }
1462         }
1463     }
1464     else if( !strcmp( psz_cmd, "enqueue" ) &&
1465              newval.psz_string && *newval.psz_string )
1466     {
1467         input_item_t *p_item = parse_MRL( p_intf, newval.psz_string );
1468
1469         if( p_item )
1470         {
1471             msg_rc( "trying to enqueue %s to playlist", newval.psz_string );
1472             if( playlist_AddInput( p_playlist, p_item,
1473                                PLAYLIST_APPEND, PLAYLIST_END, true,
1474                                pl_Unlocked ) != VLC_SUCCESS )
1475             {
1476                 return VLC_EGENERIC;
1477             }
1478         }
1479     }
1480     else if( !strcmp( psz_cmd, "playlist" ) )
1481     {
1482         msg_rc( "+----[ Playlist ]" );
1483         print_playlist( p_intf, p_playlist->p_root_category, 0 );
1484         msg_rc( "+----[ End of playlist ]" );
1485     }
1486
1487     else if( !strcmp( psz_cmd, "sort" ))
1488     {
1489         playlist_RecursiveNodeSort( p_playlist, p_playlist->p_root_onelevel,
1490                                     SORT_ARTIST, ORDER_NORMAL );
1491     }
1492     else if( !strcmp( psz_cmd, "status" ) )
1493     {
1494         input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1495         if( p_input )
1496         {
1497             /* Replay the current state of the system. */
1498             char *psz_uri =
1499                     input_item_GetURI( input_GetItem( p_input ) );
1500             msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri );
1501             free( psz_uri );
1502             msg_rc( STATUS_CHANGE "( audio volume: %d )",
1503                     config_GetInt( p_intf, "volume" ));
1504
1505             PL_LOCK;
1506             switch( playlist_Status(p_playlist) )
1507             {
1508                 case PLAYLIST_STOPPED:
1509                     msg_rc( STATUS_CHANGE "( stop state: 5 )" );
1510                     break;
1511                 case PLAYLIST_RUNNING:
1512                     msg_rc( STATUS_CHANGE "( play state: 3 )" );
1513                     break;
1514                 case PLAYLIST_PAUSED:
1515                     msg_rc( STATUS_CHANGE "( pause state: 4 )" );
1516                     break;
1517                 default:
1518                     msg_rc( STATUS_CHANGE "( unknown state: -1 )" );
1519                     break;
1520             }
1521             PL_UNLOCK;
1522             vlc_object_release( p_input );
1523         }
1524     }
1525
1526     /*
1527      * sanity check
1528      */
1529     else
1530     {
1531         msg_rc( "unknown command!" );
1532     }
1533
1534     pl_Release( p_this );
1535     return VLC_SUCCESS;
1536 }
1537
1538 static int Quit( vlc_object_t *p_this, char const *psz_cmd,
1539                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1540 {
1541     VLC_UNUSED(p_data); VLC_UNUSED(psz_cmd);
1542     VLC_UNUSED(oldval); VLC_UNUSED(newval);
1543
1544     libvlc_Quit( p_this->p_libvlc );
1545     return VLC_SUCCESS;
1546 }
1547
1548 static int Intf( vlc_object_t *p_this, char const *psz_cmd,
1549                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
1550 {
1551     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1552     intf_thread_t *p_newintf = NULL;
1553
1554     p_newintf = intf_Create( p_this->p_libvlc, newval.psz_string );
1555     if( p_newintf )
1556     {
1557         if( intf_RunThread( p_newintf ) )
1558         {
1559             vlc_object_detach( p_newintf );
1560             vlc_object_release( p_newintf );
1561         }
1562     }
1563
1564     return VLC_SUCCESS;
1565 }
1566
1567 static int Volume( vlc_object_t *p_this, char const *psz_cmd,
1568                    vlc_value_t oldval, vlc_value_t newval, void *p_data )
1569 {
1570     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1571     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1572     input_thread_t *p_input = NULL;
1573     int i_error = VLC_EGENERIC;
1574
1575     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1576     if( !p_input )
1577         return VLC_ENOOBJ;
1578
1579     if( p_input )
1580     {
1581         vlc_value_t val;
1582
1583         var_Get( p_input, "state", &val );
1584         if( val.i_int == PAUSE_S )
1585         {
1586             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
1587             vlc_object_release( p_input );
1588             return VLC_EGENERIC;
1589         }
1590         vlc_object_release( p_input );
1591     }
1592
1593     if ( *newval.psz_string )
1594     {
1595         /* Set. */
1596         audio_volume_t i_volume = atoi( newval.psz_string );
1597         if ( (i_volume > (audio_volume_t)AOUT_VOLUME_MAX) )
1598         {
1599             msg_rc( "Volume must be in the range %d-%d.", AOUT_VOLUME_MIN,
1600                     AOUT_VOLUME_MAX );
1601             i_error = VLC_EBADVAR;
1602         }
1603         else
1604         {
1605             if( i_volume == AOUT_VOLUME_MIN )
1606             {
1607                 vlc_value_t keyval;
1608
1609                 keyval.i_int = config_GetInt( p_intf, "key-vol-mute" );
1610                 var_Set( p_intf->p_libvlc, "key-pressed", keyval );
1611             }
1612             i_error = aout_VolumeSet( p_this, i_volume );
1613             osd_Volume( p_this );
1614             msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
1615         }
1616     }
1617     else
1618     {
1619         /* Get. */
1620         audio_volume_t i_volume;
1621         if ( aout_VolumeGet( p_this, &i_volume ) < 0 )
1622         {
1623             i_error = VLC_EGENERIC;
1624         }
1625         else
1626         {
1627             msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
1628             i_error = VLC_SUCCESS;
1629         }
1630     }
1631
1632     return i_error;
1633 }
1634
1635 static int VolumeMove( vlc_object_t *p_this, char const *psz_cmd,
1636                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
1637 {
1638     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1639     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1640     audio_volume_t i_volume;
1641     input_thread_t *p_input = NULL;
1642     int i_nb_steps = atoi(newval.psz_string);
1643     int i_error = VLC_SUCCESS;
1644     int i_volume_step = 0;
1645
1646     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1647     if( !p_input )
1648         return VLC_ENOOBJ;
1649
1650     if( p_input )
1651     {
1652         vlc_value_t val;
1653
1654         var_Get( p_input, "state", &val );
1655         if( val.i_int == PAUSE_S )
1656         {
1657             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
1658             vlc_object_release( p_input );
1659             return VLC_EGENERIC;
1660         }
1661         vlc_object_release( p_input );
1662     }
1663
1664     i_volume_step = config_GetInt( p_intf->p_libvlc, "volume-step" );
1665     if ( i_nb_steps <= 0 || i_nb_steps > (AOUT_VOLUME_MAX/i_volume_step) )
1666     {
1667         i_nb_steps = 1;
1668     }
1669
1670     if ( !strcmp(psz_cmd, "volup") )
1671     {
1672         if ( aout_VolumeUp( p_this, i_nb_steps, &i_volume ) < 0 )
1673             i_error = VLC_EGENERIC;
1674     }
1675     else
1676     {
1677         if ( aout_VolumeDown( p_this, i_nb_steps, &i_volume ) < 0 )
1678             i_error = VLC_EGENERIC;
1679     }
1680     osd_Volume( p_this );
1681
1682     if ( !i_error ) msg_rc( STATUS_CHANGE "( audio volume: %d )", i_volume );
1683     return i_error;
1684 }
1685
1686
1687 static int VideoConfig( vlc_object_t *p_this, char const *psz_cmd,
1688                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
1689 {
1690     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1691     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1692     input_thread_t *p_input = NULL;
1693     vout_thread_t * p_vout;
1694     const char * psz_variable = NULL;
1695     int i_error;
1696
1697     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1698     if( !p_input )
1699         return VLC_ENOOBJ;
1700
1701     p_vout = vlc_object_find( p_input, VLC_OBJECT_VOUT, FIND_CHILD );
1702     vlc_object_release( p_input );
1703     if( !p_vout )
1704         return VLC_ENOOBJ;
1705
1706     if( !strcmp( psz_cmd, "vcrop" ) )
1707     {
1708         psz_variable = "crop";
1709     }
1710     else if( !strcmp( psz_cmd, "vratio" ) )
1711     {
1712         psz_variable = "aspect-ratio";
1713     }
1714     else if( !strcmp( psz_cmd, "vzoom" ) )
1715     {
1716         psz_variable = "zoom";
1717     }
1718     else if( !strcmp( psz_cmd, "snapshot" ) )
1719     {
1720         psz_variable = "video-snapshot";
1721     }
1722     else
1723         /* This case can't happen */
1724         assert( 0 );
1725
1726     if( newval.psz_string && *newval.psz_string )
1727     {
1728         /* set */
1729         if( !strcmp( psz_variable, "zoom" ) )
1730         {
1731             vlc_value_t val;
1732             val.f_float = atof( newval.psz_string );
1733             i_error = var_Set( p_vout, psz_variable, val );
1734         }
1735         else
1736         {
1737             i_error = var_Set( p_vout, psz_variable, newval );
1738         }
1739     }
1740     else if( !strcmp( psz_cmd, "snapshot" ) )
1741     {
1742         vlc_value_t val;
1743         val.b_bool = true;
1744         i_error = var_Set( p_vout, psz_variable, val );
1745     }
1746     else
1747     {
1748         /* get */
1749         vlc_value_t val_name;
1750         vlc_value_t val, text;
1751         int i;
1752         float f_value = 0.;
1753         char *psz_value = NULL;
1754
1755         if ( var_Get( p_vout, psz_variable, &val ) < 0 )
1756         {
1757             vlc_object_release( p_vout );
1758             return VLC_EGENERIC;
1759         }
1760         if( !strcmp( psz_variable, "zoom" ) )
1761         {
1762             f_value = val.f_float;
1763         }
1764         else
1765         {
1766             psz_value = strdup( val.psz_string );
1767         }
1768
1769         if ( var_Change( p_vout, psz_variable,
1770                          VLC_VAR_GETLIST, &val, &text ) < 0 )
1771         {
1772             vlc_object_release( p_vout );
1773             free( psz_value );
1774             return VLC_EGENERIC;
1775         }
1776
1777         /* Get the descriptive name of the variable */
1778         var_Change( p_vout, psz_variable, VLC_VAR_GETTEXT,
1779                     &val_name, NULL );
1780         if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1781
1782         msg_rc( "+----[ %s ]", val_name.psz_string );
1783         if( !strcmp( psz_variable, "zoom" ) )
1784         {
1785             for ( i = 0; i < val.p_list->i_count; i++ )
1786             {
1787                 if ( f_value == val.p_list->p_values[i].f_float )
1788                     msg_rc( "| %f - %s *", val.p_list->p_values[i].f_float,
1789                             text.p_list->p_values[i].psz_string );
1790                 else
1791                     msg_rc( "| %f - %s", val.p_list->p_values[i].f_float,
1792                             text.p_list->p_values[i].psz_string );
1793             }
1794         }
1795         else
1796         {
1797             for ( i = 0; i < val.p_list->i_count; i++ )
1798             {
1799                 if ( !strcmp( psz_value, val.p_list->p_values[i].psz_string ) )
1800                     msg_rc( "| %s - %s *", val.p_list->p_values[i].psz_string,
1801                             text.p_list->p_values[i].psz_string );
1802                 else
1803                     msg_rc( "| %s - %s", val.p_list->p_values[i].psz_string,
1804                             text.p_list->p_values[i].psz_string );
1805             }
1806             free( psz_value );
1807         }
1808         var_Change( p_vout, psz_variable, VLC_VAR_FREELIST,
1809                     &val, &text );
1810         msg_rc( "+----[ end of %s ]", val_name.psz_string );
1811
1812         free( val_name.psz_string );
1813
1814         i_error = VLC_SUCCESS;
1815     }
1816     vlc_object_release( p_vout );
1817     return i_error;
1818 }
1819
1820 static int AudioConfig( vlc_object_t *p_this, char const *psz_cmd,
1821                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
1822 {
1823     VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1824     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1825     input_thread_t *p_input = NULL;
1826     aout_instance_t * p_aout;
1827     const char * psz_variable;
1828     vlc_value_t val_name;
1829     int i_error;
1830
1831     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1832     if( !p_input )
1833         return VLC_ENOOBJ;
1834
1835     if( p_input )
1836     {
1837         vlc_value_t val;
1838
1839         var_Get( p_input, "state", &val );
1840         if( val.i_int == PAUSE_S )
1841         {
1842             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
1843             vlc_object_release( p_input );
1844             return VLC_EGENERIC;
1845         }
1846         vlc_object_release( p_input );
1847     }
1848
1849     p_aout = vlc_object_find( p_this, VLC_OBJECT_AOUT, FIND_ANYWHERE );
1850     if ( p_aout == NULL ) return VLC_ENOOBJ;
1851
1852     if ( !strcmp( psz_cmd, "adev" ) )
1853     {
1854         psz_variable = "audio-device";
1855     }
1856     else
1857     {
1858         psz_variable = "audio-channels";
1859     }
1860
1861     /* Get the descriptive name of the variable */
1862     var_Change( (vlc_object_t *)p_aout, psz_variable, VLC_VAR_GETTEXT,
1863                  &val_name, NULL );
1864     if( !val_name.psz_string ) val_name.psz_string = strdup(psz_variable);
1865
1866     if ( !*newval.psz_string )
1867     {
1868         /* Retrieve all registered ***. */
1869         vlc_value_t val, text;
1870         int i, i_value;
1871
1872         if ( var_Get( (vlc_object_t *)p_aout, psz_variable, &val ) < 0 )
1873         {
1874             vlc_object_release( (vlc_object_t *)p_aout );
1875             return VLC_EGENERIC;
1876         }
1877         i_value = val.i_int;
1878
1879         if ( var_Change( (vlc_object_t *)p_aout, psz_variable,
1880                          VLC_VAR_GETLIST, &val, &text ) < 0 )
1881         {
1882             vlc_object_release( (vlc_object_t *)p_aout );
1883             return VLC_EGENERIC;
1884         }
1885
1886         msg_rc( "+----[ %s ]", val_name.psz_string );
1887         for ( i = 0; i < val.p_list->i_count; i++ )
1888         {
1889             if ( i_value == val.p_list->p_values[i].i_int )
1890                 msg_rc( "| %i - %s *", val.p_list->p_values[i].i_int,
1891                         text.p_list->p_values[i].psz_string );
1892             else
1893                 msg_rc( "| %i - %s", val.p_list->p_values[i].i_int,
1894                         text.p_list->p_values[i].psz_string );
1895         }
1896         var_Change( (vlc_object_t *)p_aout, psz_variable, VLC_VAR_FREELIST,
1897                     &val, &text );
1898         msg_rc( "+----[ end of %s ]", val_name.psz_string );
1899
1900         free( val_name.psz_string );
1901         i_error = VLC_SUCCESS;
1902     }
1903     else
1904     {
1905         vlc_value_t val;
1906         val.i_int = atoi( newval.psz_string );
1907
1908         i_error = var_Set( (vlc_object_t *)p_aout, psz_variable, val );
1909     }
1910     vlc_object_release( (vlc_object_t *)p_aout );
1911
1912     return i_error;
1913 }
1914
1915 /* OSD menu commands */
1916 static int Menu( vlc_object_t *p_this, char const *psz_cmd,
1917     vlc_value_t oldval, vlc_value_t newval, void *p_data )
1918 {
1919     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data);
1920     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1921     playlist_t    *p_playlist = NULL;
1922     int i_error = VLC_SUCCESS;
1923     vlc_value_t val;
1924
1925     if ( !*newval.psz_string )
1926     {
1927         msg_rc( _("Please provide one of the following parameters:") );
1928         msg_rc( "[on|off|up|down|left|right|select]" );
1929         return VLC_EGENERIC;
1930     }
1931
1932     p_playlist = pl_Hold( p_this );
1933     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1934
1935     if( p_input )
1936     {
1937         var_Get( p_input, "state", &val );
1938         vlc_object_release( p_input );
1939
1940         if( ( val.i_int == PAUSE_S ) &&
1941             ( strcmp( newval.psz_string, "select" ) != 0 ) )
1942         {
1943             msg_rc( _("Type 'menu select' or 'pause' to continue.") );
1944             pl_Release( p_this );
1945             return VLC_EGENERIC;
1946         }
1947     }
1948     pl_Release( p_this );
1949
1950     val.psz_string = strdup( newval.psz_string );
1951     if( !val.psz_string )
1952         return VLC_ENOMEM;
1953     if( !strcmp( val.psz_string, "on" ) || !strcmp( val.psz_string, "show" ))
1954         osd_MenuShow( p_this );
1955     else if( !strcmp( val.psz_string, "off" )
1956           || !strcmp( val.psz_string, "hide" ) )
1957         osd_MenuHide( p_this );
1958     else if( !strcmp( val.psz_string, "up" ) )
1959         osd_MenuUp( p_this );
1960     else if( !strcmp( val.psz_string, "down" ) )
1961         osd_MenuDown( p_this );
1962     else if( !strcmp( val.psz_string, "left" ) )
1963         osd_MenuPrev( p_this );
1964     else if( !strcmp( val.psz_string, "right" ) )
1965         osd_MenuNext( p_this );
1966     else if( !strcmp( val.psz_string, "select" ) )
1967         osd_MenuActivate( p_this );
1968     else
1969     {
1970         msg_rc( _("Please provide one of the following parameters:") );
1971         msg_rc( "[on|off|up|down|left|right|select]" );
1972         i_error = VLC_EGENERIC;
1973     }
1974
1975     free( val.psz_string );
1976     return i_error;
1977 }
1978
1979 static int Statistics ( vlc_object_t *p_this, char const *psz_cmd,
1980     vlc_value_t oldval, vlc_value_t newval, void *p_data )
1981 {
1982     VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(p_data);
1983     intf_thread_t *p_intf = (intf_thread_t*)p_this;
1984     input_thread_t *p_input = NULL;
1985     int i_error;
1986
1987     p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
1988     if( !p_input )
1989         return VLC_ENOOBJ;
1990
1991     if( !strcmp( psz_cmd, "stats" ) )
1992     {
1993         vlc_mutex_lock( &input_GetItem(p_input)->lock );
1994         updateStatistics( p_intf, input_GetItem(p_input) );
1995         vlc_mutex_unlock( &input_GetItem(p_input)->lock );
1996     }
1997     /*
1998      * sanity check
1999      */
2000     else
2001     {
2002         msg_rc(_("Unknown command!") );
2003     }
2004
2005     vlc_object_release( p_input );
2006     i_error = VLC_SUCCESS;
2007     return i_error;
2008 }
2009
2010 static int updateStatistics( intf_thread_t *p_intf, input_item_t *p_item )
2011 {
2012     if( !p_item ) return VLC_EGENERIC;
2013
2014     vlc_mutex_lock( &p_item->p_stats->lock );
2015     msg_rc( "+----[ begin of statistical info ]" );
2016
2017     /* Input */
2018     msg_rc(_("+-[Incoming]"));
2019     msg_rc(_("| input bytes read : %8.0f kB"),
2020             (float)(p_item->p_stats->i_read_bytes)/1000 );
2021     msg_rc(_("| input bitrate    :   %6.0f kb/s"),
2022             (float)(p_item->p_stats->f_input_bitrate)*8000 );
2023     msg_rc(_("| demux bytes read : %8.0f kB"),
2024             (float)(p_item->p_stats->i_demux_read_bytes)/1000 );
2025     msg_rc(_("| demux bitrate    :   %6.0f kb/s"),
2026             (float)(p_item->p_stats->f_demux_bitrate)*8000 );
2027     msg_rc("|");
2028     /* Video */
2029     msg_rc(_("+-[Video Decoding]"));
2030     msg_rc(_("| video decoded    :    %5i"),
2031             p_item->p_stats->i_decoded_video );
2032     msg_rc(_("| frames displayed :    %5i"),
2033             p_item->p_stats->i_displayed_pictures );
2034     msg_rc(_("| frames lost      :    %5i"),
2035             p_item->p_stats->i_lost_pictures );
2036     msg_rc("|");
2037     /* Audio*/
2038     msg_rc(_("+-[Audio Decoding]"));
2039     msg_rc(_("| audio decoded    :    %5i"),
2040             p_item->p_stats->i_decoded_audio );
2041     msg_rc(_("| buffers played   :    %5i"),
2042             p_item->p_stats->i_played_abuffers );
2043     msg_rc(_("| buffers lost     :    %5i"),
2044             p_item->p_stats->i_lost_abuffers );
2045     msg_rc("|");
2046     /* Sout */
2047     msg_rc(_("+-[Streaming]"));
2048     msg_rc(_("| packets sent     :    %5i"), p_item->p_stats->i_sent_packets );
2049     msg_rc(_("| bytes sent       : %8.0f kB"),
2050             (float)(p_item->p_stats->i_sent_bytes)/1000 );
2051     msg_rc(_("| sending bitrate  :   %6.0f kb/s"),
2052             (float)(p_item->p_stats->f_send_bitrate*8)*1000 );
2053     msg_rc("|");
2054     msg_rc( "+----[ end of statistical info ]" );
2055     vlc_mutex_unlock( &p_item->p_stats->lock );
2056
2057     return VLC_SUCCESS;
2058 }
2059
2060 #ifdef WIN32
2061 bool ReadWin32( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
2062 {
2063     INPUT_RECORD input_record;
2064     DWORD i_dw;
2065
2066     /* On Win32, select() only works on socket descriptors */
2067     while( WaitForSingleObject( p_intf->p_sys->hConsoleIn,
2068                                 INTF_IDLE_SLEEP/1000 ) == WAIT_OBJECT_0 )
2069     {
2070         while( vlc_object_alive( p_intf ) && *pi_size < MAX_LINE_LENGTH &&
2071                ReadConsoleInput( p_intf->p_sys->hConsoleIn, &input_record,
2072                                  1, &i_dw ) )
2073         {
2074             if( input_record.EventType != KEY_EVENT ||
2075                 !input_record.Event.KeyEvent.bKeyDown ||
2076                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
2077                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL||
2078                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_MENU ||
2079                 input_record.Event.KeyEvent.wVirtualKeyCode == VK_CAPITAL )
2080             {
2081                 /* nothing interesting */
2082                 continue;
2083             }
2084
2085             p_buffer[ *pi_size ] = input_record.Event.KeyEvent.uChar.AsciiChar;
2086
2087             /* Echo out the command */
2088             putc( p_buffer[ *pi_size ], stdout );
2089
2090             /* Handle special keys */
2091             if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
2092             {
2093                 putc( '\n', stdout );
2094                 break;
2095             }
2096             switch( p_buffer[ *pi_size ] )
2097             {
2098             case '\b':
2099                 if( *pi_size )
2100                 {
2101                     *pi_size -= 2;
2102                     putc( ' ', stdout );
2103                     putc( '\b', stdout );
2104                 }
2105                 break;
2106             case '\r':
2107                 (*pi_size) --;
2108                 break;
2109             }
2110
2111             (*pi_size)++;
2112         }
2113
2114         if( *pi_size == MAX_LINE_LENGTH ||
2115             p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
2116         {
2117             p_buffer[ *pi_size ] = 0;
2118             return true;
2119         }
2120     }
2121
2122     return false;
2123 }
2124 #endif
2125
2126 bool ReadCommand( intf_thread_t *p_intf, char *p_buffer, int *pi_size )
2127 {
2128     int i_read = 0;
2129
2130 #ifdef WIN32
2131     if( p_intf->p_sys->i_socket == -1 && !p_intf->p_sys->b_quiet )
2132         return ReadWin32( p_intf, p_buffer, pi_size );
2133     else if( p_intf->p_sys->i_socket == -1 )
2134     {
2135         msleep( INTF_IDLE_SLEEP );
2136         return false;
2137     }
2138 #endif
2139
2140     while( vlc_object_alive( p_intf ) && *pi_size < MAX_LINE_LENGTH &&
2141            (i_read = net_Read( p_intf, p_intf->p_sys->i_socket == -1 ?
2142                        0 /*STDIN_FILENO*/ : p_intf->p_sys->i_socket, NULL,
2143                   (uint8_t *)p_buffer + *pi_size, 1, false ) ) > 0 )
2144     {
2145         if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
2146             break;
2147
2148         (*pi_size)++;
2149     }
2150
2151     /* Connection closed */
2152     if( i_read <= 0 )
2153     {
2154         if( p_intf->p_sys->i_socket != -1 )
2155         {
2156             net_Close( p_intf->p_sys->i_socket );
2157             p_intf->p_sys->i_socket = -1;
2158         }
2159         else
2160         {
2161             /* Standard input closed: exit */
2162             vlc_value_t empty;
2163             Quit( VLC_OBJECT(p_intf), NULL, empty, empty, NULL );
2164         }
2165
2166         p_buffer[ *pi_size ] = 0;
2167         return true;
2168     }
2169
2170     if( *pi_size == MAX_LINE_LENGTH ||
2171         p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
2172     {
2173         p_buffer[ *pi_size ] = 0;
2174         return true;
2175     }
2176
2177     return false;
2178 }
2179
2180 /*****************************************************************************
2181  * parse_MRL: build a input item from a full mrl
2182  *****************************************************************************
2183  * MRL format: "simplified-mrl [:option-name[=option-value]]"
2184  * We don't check for '"' or '\'', we just assume that a ':' that follows a
2185  * space is a new option. Should be good enough for our purpose.
2186  *****************************************************************************/
2187 static input_item_t *parse_MRL( intf_thread_t *p_intf, char *psz_mrl )
2188 {
2189 #define SKIPSPACE( p ) { while( *p == ' ' || *p == '\t' ) p++; }
2190 #define SKIPTRAILINGSPACE( p, d ) \
2191     { char *e=d; while( e > p && (*(e-1)==' ' || *(e-1)=='\t') ){e--;*e=0;} }
2192
2193     input_item_t *p_item = NULL;
2194     char *psz_item = NULL, *psz_item_mrl = NULL, *psz_orig;
2195     char **ppsz_options = NULL;
2196     int i, i_options = 0;
2197
2198     if( !psz_mrl ) return 0;
2199
2200     psz_mrl = psz_orig = strdup( psz_mrl );
2201     while( *psz_mrl )
2202     {
2203         SKIPSPACE( psz_mrl );
2204         psz_item = psz_mrl;
2205
2206         for( ; *psz_mrl; psz_mrl++ )
2207         {
2208             if( (*psz_mrl == ' ' || *psz_mrl == '\t') && psz_mrl[1] == ':' )
2209             {
2210                 /* We have a complete item */
2211                 break;
2212             }
2213             if( (*psz_mrl == ' ' || *psz_mrl == '\t') &&
2214                 (psz_mrl[1] == '"' || psz_mrl[1] == '\'') && psz_mrl[2] == ':')
2215             {
2216                 /* We have a complete item */
2217                 break;
2218             }
2219         }
2220
2221         if( *psz_mrl ) { *psz_mrl = 0; psz_mrl++; }
2222         SKIPTRAILINGSPACE( psz_item, psz_item + strlen( psz_item ) );
2223
2224         /* Remove '"' and '\'' if necessary */
2225         if( *psz_item == '"' && psz_item[strlen(psz_item)-1] == '"' )
2226         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
2227         if( *psz_item == '\'' && psz_item[strlen(psz_item)-1] == '\'' )
2228         { psz_item++; psz_item[strlen(psz_item)-1] = 0; }
2229
2230         if( !psz_item_mrl ) psz_item_mrl = psz_item;
2231         else if( *psz_item )
2232         {
2233             i_options++;
2234             ppsz_options = realloc( ppsz_options, i_options * sizeof(char *) );
2235             ppsz_options[i_options - 1] = &psz_item[1];
2236         }
2237
2238         if( *psz_mrl ) SKIPSPACE( psz_mrl );
2239     }
2240
2241     /* Now create a playlist item */
2242     if( psz_item_mrl )
2243     {
2244         p_item = input_item_New( p_intf, psz_item_mrl, NULL );
2245         for( i = 0; i < i_options; i++ )
2246         {
2247             input_item_AddOption( p_item, ppsz_options[i], VLC_INPUT_OPTION_TRUSTED );
2248         }
2249     }
2250
2251     if( i_options ) free( ppsz_options );
2252     free( psz_orig );
2253
2254     return p_item;
2255 }