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