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