root/modules/control/rc.c

Revision d228fdd28ef03b992c97af16cb842acd7c879831, 73.3 kB (checked in by Pierre d'Herbemont <pdherbemont@videolan.org>, 2 months ago)

playlist: Move more members as private.

  • Property mode set to 100644
Line 
1 /*****************************************************************************
2  * rc.c : remote control stdin/stdout module for vlc
3  *****************************************************************************
4  * Copyright (C) 2004-2007 the VideoLAN team
5  * $Id$
6  *
7  * Author: Peter Surda <shurdeek@panorama.sth.ac.at>
8  *         Jean-Paul Saman <jpsaman #_at_# m2x _replaceWith#dot_ nl>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34
35 #include <errno.h>                                                 /* ENOMEM */
36 #include <ctype.h>
37 #include <signal.h>
38
39 #include <vlc_interface.h>
40 #include <vlc_aout.h>
41 #include <vlc_vout.h>
42 #include <vlc_osd.h>
43 #include <vlc_playlist.h>
44
45 #ifdef HAVE_UNISTD_H
46 #    include <unistd.h>
47 #endif
48
49 #ifdef HAVE_SYS_TIME_H
50 #    include <sys/time.h>
51 #endif
52 #include <sys/types.h>
53
54 #include <vlc_network.h>
55 #include "vlc_url.h"
56
57 #include <vlc_charset.h>
58
59 #if defined(AF_UNIX) && !defined(AF_LOCAL)
60 #    define AF_LOCAL AF_UNIX
61 #endif
62
63 #if defined(AF_LOCAL) && ! defined(WIN32)
64 #    include <sys/un.h>
65 #endif
66
67 #define MAX_LINE_LENGTH 256
68 #define STATUS_CHANGE "status change: "
69
70 /* input_state_e from <vlc_input.h> */
71 static const char *ppsz_input_state[] = {
72     N_("Initializing"),
73     N_("Opening"),
74     N_("Buffer"),
75     N_("Play"),
76     N_("Pause"),
77     N_("Stop"),
78     N_("Forward"),
79     N_("Backward"),
80     N_("End"),
81     N_("Error"),
82 };
83
84 /*****************************************************************************
85  * Local prototypes
86  *****************************************************************************/
87 static int  Activate     ( vlc_object_t * );
88 static void Deactivate   ( vlc_object_t * );
89 static void Run          ( intf_thread_t * );
90
91 static void Help         ( intf_thread_t *, bool );
92 static void RegisterCallbacks( intf_thread_t * );
93
94 static bool ReadCommand( intf_thread_t *, char *, int * );
95
96 static input_item_t *parse_MRL( intf_thread_t *, char * );
97
98 static int  Input        ( vlc_object_t *, char const *,
99                            vlc_value_t, vlc_value_t, void * );
100 static int  Playlist     ( vlc_object_t *, char const *,
101                            vlc_value_t, vlc_value_t, void * );
102 static int  Quit         ( vlc_object_t *, char const *,
103                            vlc_value_t, vlc_value_t, void * );
104 static int  Intf         ( vlc_object_t *, char const *,
105                            vlc_value_t, vlc_value_t, void * );
106 static int  Volume       ( vlc_object_t *, char const *,
107                            vlc_value_t, vlc_value_t, void * );
108 static int  VolumeMove   ( vlc_object_t *, char const *,
109                            vlc_value_t, vlc_value_t, void * );
110 static int  VideoConfig  ( vlc_object_t *, char const *,
111                            vlc_value_t, vlc_value_t, void * );
112 static int  AudioConfig  ( vlc_object_t *, char const *,
113                            vlc_value_t, vlc_value_t, void * );
114 static int  Menu         ( vlc_object_t *, char const *,
115                            vlc_value_t, vlc_value_t, void * );
116 static int  Statistics   ( vlc_object_t *, char const *,
117                            vlc_value_t, vlc_value_t, void * );
118
119 static int updateStatistics( intf_thread_t *, input_item_t *);
120
121 /* Status Callbacks */
122 static int TimeOffsetChanged( vlc_object_t *, char const *,
123                               vlc_value_t, vlc_value_t , void * );
124 static int VolumeChanged    ( vlc_object_t *, char const *,
125                               vlc_value_t, vlc_value_t, void * );
126 static int StateChanged     ( vlc_object_t *, char const *,
127                               vlc_value_t, vlc_value_t, void * );
128 static int RateChanged      ( vlc_object_t *, char const *,
129                               vlc_value_t, vlc_value_t, void * );
130
131 struct intf_sys_t
132 {
133     int *pi_socket_listen;
134     int i_socket;
135     char *psz_unix_path;
136
137     /* status changes */
138     vlc_mutex_t       status_lock;
139     playlist_status_t i_last_state;
140
141 #ifdef WIN32
142     HANDLE hConsoleIn;
143     bool b_quiet;
144 #endif
145 };
146
147 #define msg_rc( ... ) __msg_rc( p_intf, __VA_ARGS__ )
148
149 static void __msg_rc( intf_thread_t *p_intf, const char *psz_fmt, ... )
150 {
151     va_list args;
152     char fmt_eol[strlen (psz_fmt) + 3];
153
154     snprintf (fmt_eol, sizeof (fmt_eol), "%s\r\n", psz_fmt);
155     va_start( args, psz_fmt );
156
157     if( p_intf->p_sys->i_socket == -1 )
158         utf8_vfprintf( stdout, fmt_eol, args );
159     else
160         net_vaPrintf( p_intf, p_intf->p_sys->i_socket, NULL, fmt_eol, args );
161     va_end( args );
162 }
163
164 /*****************************************************************************
165  * Module descriptor
166  *****************************************************************************/
167 #define POS_TEXT N_("Show stream position")
168 #define POS_LONGTEXT N_("Show the current position in seconds within the " \
169                         "stream from time to time." )
170
171 #define TTY_TEXT N_("Fake TTY")
172 #define TTY_LONGTEXT N_("Force the rc module to use stdin as if it was a TTY.")
173
174 #define UNIX_TEXT N_("UNIX socket command input")
175 #define UNIX_LONGTEXT N_("Accept commands over a Unix socket rather than " \
176                          "stdin." )
177
178 #define HOST_TEXT N_("TCP command input")
179 #define HOST_LONGTEXT N_("Accept commands over a socket rather than stdin. " \
180             "You can set the address and port the interface will bind to." )
181
182 #ifdef WIN32
183 #define QUIET_TEXT N_("Do not open a DOS command box interface")
184 #define QUIET_LONGTEXT N_( \
185     "By default the rc interface plugin will start a DOS command box. " \
186     "Enabling the quiet mode will not bring this command box but can also " \
187     "be pretty annoying when you want to stop VLC and no video window is " \
188     "open." )
189 #endif
190
191 vlc_module_begin();
192     set_shortname( N_("RC"));
193     set_category( CAT_INTERFACE );
194     set_subcategory( SUBCAT_INTERFACE_MAIN );
195     set_description( N_("Remote control interface") );
196     add_bool( "rc-show-pos", 0, NULL, POS_TEXT, POS_LONGTEXT, true );
197
198 #ifdef WIN32
199     add_bool( "rc-quiet", 0, NULL, QUIET_TEXT, QUIET_LONGTEXT, false );
200 #else
201 #if defined (HAVE_ISATTY)
202     add_bool( "rc-fake-tty", 0, NULL, TTY_TEXT, TTY_LONGTEXT, true );
203 #endif
204     add_string( "rc-unix", 0, NULL, UNIX_TEXT, UNIX_LONGTEXT, true );
205 #endif
206     add_string( "rc-host", 0, NULL, HOST_TEXT, HOST_LONGTEXT, true );
207
208     set_capability( "interface", 20 );
209
210     set_callbacks( Activate, Deactivate );
211 vlc_module_end();
212
213 /*****************************************************************************
214  * Activate: initialize and create stuff
215  *****************************************************************************/
216 static int Activate( vlc_object_t *p_this )
217 {
218     intf_thread_t *p_intf = (intf_thread_t*)p_this;
219     char *psz_host, *psz_unix_path;
220     int  *pi_socket = NULL;
221
222 #ifndef WIN32
223 #if defined(HAVE_ISATTY)
224     /* Check that stdin is a TTY */
225     if( !config_GetInt( p_intf, "rc-fake-tty" ) && !isatty( 0 ) )
226     {
227         msg_Warn( p_intf, "fd 0 is not a TTY" );
228         return VLC_EGENERIC;
229     }
230 #endif
231
232     psz_unix_path = config_GetPsz( p_intf, "rc-unix" );
233     if( psz_unix_path )
234     {
235         int i_socket;
236
237 #ifndef AF_LOCAL
238         msg_Warn( p_intf, "your OS doesn't support filesystem sockets" );
239         free( psz_unix_path );
240         return VLC_EGENERIC;
241 #else
242         struct sockaddr_un addr;
243
244         memset( &addr, 0, sizeof(struct sockaddr_un) );
245
246         msg_Dbg( p_intf, "trying UNIX socket" );
247
248         if( (i_socket = socket( AF_LOCAL, SOCK_STREAM, 0 ) ) < 0 )
249         {
250             msg_Warn( p_intf, "can't open socket: %m" );
251             free( psz_unix_path );
252             return VLC_EGENERIC;
253         }
254
255         addr.sun_family = AF_LOCAL;
256         strncpy( addr.sun_path, psz_unix_path, sizeof( addr.sun_path ) );
257         addr.sun_path[sizeof( addr.sun_path ) - 1] = '\0';
258
259         if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr))
260          && (errno == EADDRINUSE)
261          && connect (i_socket, (struct sockaddr *)&addr, sizeof (addr))
262          && (errno == ECONNREFUSED))
263         {
264             msg_Info (p_intf, "Removing dead UNIX socket: %s", psz_unix_path);
265             unlink (psz_unix_path);
266
267             if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr)))
268             {
269                 msg_Err (p_intf, "cannot bind UNIX socket at %s: %m",
270                          psz_unix_path);
271                 free (psz_unix_path);
272                 net_Close (i_socket);
273                 return VLC_EGENERIC;
274             }
275         }
276
277         if( listen( i_socket, 1 ) )
278         {
279             msg_Warn( p_intf, "can't listen on socket: %m");
280             free( psz_unix_path );
281             net_Close( i_socket );
282             return VLC_EGENERIC;
283         }
284
285         /* FIXME: we need a core function to merge listening sockets sets */
286         pi_socket = calloc( 2, sizeof( int ) );
287         if( pi_socket == NULL )
288         {
289             free( psz_unix_path );
290             net_Close( i_socket );
291             return VLC_ENOMEM;
292         }
293         pi_socket[0] = i_socket;
294         pi_socket[1] = -1;
295 #endif /* AF_LOCAL */
296     }
297 #endif /* !WIN32 */
298
299     if( ( pi_socket == NULL ) &&
300         ( psz_host = config_GetPsz( p_intf, "rc-host" ) ) != NULL )
301     {
302         vlc_url_t url;
303
304         vlc_UrlParse( &url, psz_host, 0 );
305
306         msg_Dbg( p_intf, "base: %s, port: %d", url.psz_host, url.i_port );
307
308         pi_socket = net_ListenTCP(p_this, url.psz_host, url.i_port);
309         if( pi_socket == NULL )
310         {
311             msg_Warn( p_intf, "can't listen to %s port %i",
312                       url.psz_host, url.i_port );
313             vlc_UrlClean( &url );
314             free( psz_host );
315             return VLC_EGENERIC;
316         }
317
318         vlc_UrlClean( &url );
319         free( psz_host );
320     }
321
322     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
323     if( !p_intf->p_sys )
324         return VLC_ENOMEM;
325
326     p_intf->p_sys->pi_socket_listen = pi_socket;
327     p_intf->p_sys->i_socket = -1;
328     p_intf->p_sys->psz_unix_path = psz_unix_path;
329     vlc_mutex_init( &p_intf->p_sys->status_lock );
330     p_intf->p_sys->i_last_state = PLAYLIST_STOPPED;
331
332     /* Non-buffered stdout */
333     setvbuf( stdout, (char *)NULL, _IOLBF, 0 );
334
335     p_intf->pf_run = Run;
336
337 #ifdef WIN32
338     p_intf->p_sys->b_quiet = config_GetInt( p_intf, "rc-quiet" );
339     if( !p_intf->p_sys->b_quiet ) { CONSOLE_INTRO_MSG; }
340 #else
341     CONSOLE_INTRO_MSG;
342 #endif
343
344     msg_rc( _("Remote control interface initialized. Type `help' for help.") );
345     return VLC_SUCCESS;
346 }
347
348 /*****************************************************************************
349  * Deactivate: uninitialize and cleanup
350  *****************************************************************************/
351 static void Deactivate( vlc_object_t *p_this )
352 {
353     intf_thread_t *p_intf = (intf_thread_t*)p_this;
354
355     net_ListenClose( p_intf->p_sys->pi_socket_listen );
356     if( p_intf->p_sys->i_socket != -1 )
357         net_Close( p_intf->p_sys->i_socket );
358     if( p_intf->p_sys->psz_unix_path != NULL )
359     {
360 #if defined(AF_LOCAL) && !defined(WIN32)
361         unlink( p_intf->p_sys->psz_unix_path );
362 #endif
363         free( p_intf->p_sys->psz_unix_path );
364     }
365     vlc_mutex_destroy( &p_intf->p_sys->status_lock );
366     free( p_intf->p_sys );
367 }
368
369 /*****************************************************************************
370  * RegisterCallbacks: Register callbacks to dynamic variables
371  *****************************************************************************/
372 static void RegisterCallbacks( intf_thread_t *p_intf )
373 {
374     /* Register commands that will be cleaned up upon object destruction */
375 #define ADD( name, type, target )                                   \
376     var_Create( p_intf, name, VLC_VAR_ ## type | VLC_VAR_ISCOMMAND ); \
377     var_AddCallback( p_intf, name, target, NULL );
378     ADD( "quit", VOID, Quit )
379     ADD( "intf", STRING, Intf )
380
381     ADD( "add", STRING, Playlist )
382     ADD( "repeat", STRING, Playlist )
383     ADD( "loop", STRING, Playlist )
384     ADD( "random", STRING, Playlist )
385     ADD( "enqueue", STRING, Playlist )
386     ADD( "playlist", VOID, Playlist )
387     ADD( "sort", VOID, Playlist )
388     ADD( "play", VOID, Playlist )
389     ADD( "stop", VOID, Playlist )
390     ADD( "clear", VOID, Playlist )
391     ADD( "prev", VOID, Playlist )
392     ADD( "next", VOID, Playlist )
393     ADD( "goto", INTEGER, Playlist )
394     ADD( "status", INTEGER, Playlist )
395
396     /* OSD menu commands */
397     ADD(  "menu", STRING, Menu )
398
399     /* DVD commands */
400     ADD( "pause", VOID, Input )
401     ADD( "seek", INTEGER, Input )
402     ADD( "title", STRING, Input )
403     ADD( "title_n", VOID, Input )
404     ADD( "title_p", VOID, Input )
405     ADD( "chapter", STRING, Input )
406     ADD( "chapter_n", VOID, Input )
407     ADD( "chapter_p", VOID, Input )
408
409     ADD( "fastforward", VOID, Input )
410     ADD( "rewind", VOID, Input )
411     ADD( "faster", VOID, Input )
412     ADD( "slower", VOID, Input )
413     ADD( "normal", VOID, Input )
414
415     ADD( "atrack", STRING, Input )
416     ADD( "vtrack", STRING, Input )
417     ADD( "strack", STRING, Input )
418
419     /* video commands */
420     ADD( "vratio", STRING, VideoConfig )
421     ADD( "vcrop", STRING, VideoConfig )
422     ADD( "vzoom", STRING, VideoConfig )
423     ADD( "snapshot", VOID, VideoConfig )
424
425     /* audio commands */
426     ADD( "volume", STRING, Volume )
427     ADD( "volup", STRING, VolumeMove )
428     ADD( "voldown", STRING, VolumeMove )
429     ADD( "adev", STRING, AudioConfig )
430     ADD( "achan", STRING, AudioConfig )
431
432     /* misc menu commands */
433     ADD( "stats", BOOL, Statistics )
434
435 #undef ADD
436 }
437
438 /*****************************************************************************
439  * Run: rc thread
440  *****************************************************************************
441  * This part of the interface is in a separate thread so that we can call
442  * exec() from within it without annoying the rest of the program.
443  *****************************************************************************/
444 static void Run( intf_thread_t *p_intf )
445 {
446     input_thread_t * p_input;
447     playlist_t *     p_playlist;
448
449     char p_buffer[ MAX_LINE_LENGTH + 1 ];
450     bool b_showpos = config_GetInt( p_intf, "rc-show-pos" );
451     bool b_longhelp = false;
452
453     int  i_size = 0;
454     int  i_oldpos = 0;
455     int  i_newpos;
456     int  canc = vlc_savecancel();
457
458     p_buffer[0] = 0;
459     p_input = NULL;
460     p_playlist = NULL;
461
462     /* Register commands that will be cleaned up upon object destruction */
463     RegisterCallbacks( p_intf );
464
465     /* status callbacks */
466     /* Listen to audio volume updates */
467     var_AddCallback( p_intf->p_libvlc, "volume-change", VolumeChanged, p_intf );
468
469 #ifdef WIN32
470     /* Get the file descriptor of the console input */
471     p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
472     if( p_intf->p_sys->hConsoleIn == INVALID_HANDLE_VALUE )
473     {
474         msg_Err( p_intf, "couldn't find user input handle" );
475         vlc_object_kill( p_intf );
476     }
477 #endif
478
479     while( vlc_object_alive( p_intf ) )
480     {
481         char *psz_cmd, *psz_arg;
482         bool b_complete;
483
484         if( p_intf->p_sys->pi_socket_listen != NULL &&
485             p_intf->p_sys->i_socket == -1 )
486         {
487             p_intf->p_sys->i_socket =
488                 net_Accept( p_intf, p_intf->p_sys->pi_socket_listen,
489                             INTF_IDLE_SLEEP );
490             if( p_intf->p_sys->i_socket == -1 ) continue;
491         }
492
493         b_complete = ReadCommand( p_intf, p_buffer, &i_size );
494
495         /* Manage the input part */
496         if( p_input == NULL )
497         {
498             if( p_playlist )
499             {
500                 p_input = vlc_object_find( p_playlist, VLC_OBJECT_INPUT,
501                                                        FIND_CHILD );
502             }
503             else
504             {
505                 p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
506                                                    FIND_ANYWHERE );
507                 if( p_input )
508                 {
509                     p_playlist = pl_Hold( p_input );
510                 }
511             }
512             /* New input has been registered */
513             if( p_input )
514             {
515                 if( !p_input->b_dead || vlc_object_alive (p_input) )
516                 {
517                     char *psz_uri =
518                             input_item_GetURI( input_GetItem( p_input ) );
519                     msg_rc( STATUS_CHANGE "( new input: %s )", psz_uri );
520                     free( psz_uri );
521                     msg_rc( STATUS_CHANGE "( audio volume: %d )",
522                             config_GetInt( p_intf, "volume" ));
523                 }
524                 var_AddCallback( p_input, "state", StateChanged, p_intf );
525                 var_AddCallback( p_input, "rate-faster", RateChanged, p_intf );
526                 var_AddCallback( p_input, "rate-slower", RateChanged, p_intf );
527                 var_AddCallback( p_input, "rate", RateChanged, p_intf );
528                 var_AddCallback( p_input, "time-offset", TimeOffsetChanged,
529                                  p_intf );
530             }
531         }
532         else if( p_input->b_dead )
533         {
534             var_DelCallback( p_input, "state", StateChanged, p_intf );
535             var_DelCallback( p_input, "rate-faster", RateChanged, p_intf );
536             var_DelCallback( p_input, "rate-slower", RateChanged, p_intf );
537             var_DelCallback( p_input, "rate", RateChanged, p_intf );
538             var_DelCallback( p_input, "time-offset", TimeOffsetChanged,
539                              p_intf );
540             vlc_object_release( p_input );
541             p_input = NULL;
542
543             if( p_playlist )
544             {
545                 PL_LOCK;
546                 p_intf->p_sys->i_last_state = (int) PLAYLIST_STOPPED;
547                 msg_rc( STATUS_CHANGE "( stop state: 0 )" );
548                 PL_UNLOCK;
549             }
550         }
551
552         if( (p_input != NULL) && !p_input->b_dead && vlc_object_alive (p_input) &&
553             (p_playlist != NULL) )
554         {
555             PL_LOCK;
556             int status = playlist_Status( p_playlist );
557             if( (p_intf->p_sys->i_last_state != status) &&
558                 (status == PLAYLIST_STOPPED) )
559             {
560                 p_intf->p_sys->i_last_state = PLAYLIST_STOPPED;
561                 msg_rc( STATUS_CHANGE "( stop state: 5 )" );
562             }
563             else if(
564                 (p_intf->p_sys->i_last_state != status) &&
565                 (status == PLAYLIST_RUNNING) )
566             {
567                 p_intf->p_sys->i_last_state = PLAYLIST_RUNNING;
568                  msg_rc( STATUS_CHANGE "( play state: 3 )" );
569             }
570             else if(
571                 (p_intf->p_sys->i_last_state != status) &&
572                 (status == PLAYLIST_PAUSED) )
573             {
574                 p_intf->p_sys->i_last_state = PLAYLIST_PAUSED;
575                 msg_rc( STATUS_CHANGE "( pause state: 4 )" );
576             }
577             PL_UNLOCK;
578         }
579
580         if( p_input && b_showpos )
581         {
582             i_newpos = 100 * var_GetFloat( p_input, "position" );
583             if( i_oldpos != i_newpos )
584             {
585                 i_oldpos = i_newpos;
586                 msg_rc( "pos: %d%%", i_newpos );
587             }
588         }
589
590         /* Is there something to do? */
591         if( !b_complete ) continue;
592
593         /* Skip heading spaces */
594         psz_cmd = p_buffer;
595         while( *psz_cmd == ' ' )
596         {
597             psz_cmd++;
598         }
599
600         /* Split psz_cmd at the first space and make sure that
601          * psz_arg is valid */
602         psz_arg = strchr( psz_cmd, ' ' );
603         if( psz_arg )
604         {
605             *psz_arg++ = 0;
606             while( *psz_arg == ' ' )
607             {
608                 psz_arg++;
609             }
610         }
611         else
612         {
613             psz_arg = (char*)"";
614         }
615
616         /* module specfic commands: @<module name> <command> <args...> */
617         if( *psz_cmd == '@' && *psz_arg )
618         {
619             /* Parse miscellaneous commands */
620             char *psz_alias = psz_cmd + 1;
621             char *psz_mycmd = strdup( psz_arg );
622             char *psz_myarg = strchr( psz_mycmd, ' ' );
623             char *psz_msg;
624
625             if( !psz_myarg )
626             {
627                 msg_rc( "Not enough parameters." );
628             }
629             else
630             {
631                 *psz_myarg = '\0';
632                 psz_myarg ++;
633
634                 var_Command( p_intf, psz_alias, psz_mycmd, psz_myarg,
635                              &psz_msg );
636
637                 if( psz_msg )
638                 {
639                     msg_rc( psz_msg );
640                     free( psz_msg );
641                 }
642             }
643             free( psz_mycmd );
644         }
645         /* If the user typed a registered local command, try it */
646         else if( var_Type( p_intf, psz_cmd ) & VLC_VAR_ISCOMMAND )
647         {
648             vlc_value_t val;
649             int i_ret;
650
651             val.psz_string = psz_arg;
652             i_ret = var_Set( p_intf, psz_cmd, val );
653             msg_rc( "%s: returned %i (%s)",
654                     psz_cmd, i_ret, vlc_error( i_ret ) );
655         }
656         /* Or maybe it's a global command */
657         else if( var_Type( p_intf->p_libvlc, psz_cmd ) & VLC_VAR_ISCOMMAND )
658         {
659             vlc_value_t val;
660             int i_ret;
661
662             val.psz_string = psz_arg;
663             /* FIXME: it's a global command, but we should pass the
664              * local object as an argument, not p_intf->p_libvlc. */
665             i_ret = var_Set( p_intf->p_libvlc, psz_cmd, val );
666             if( i_ret != 0 )
667             {
668                 msg_rc( "%s: returned %i (%s)",
669                          psz_cmd, i_ret, vlc_error( i_ret ) );
670             }
671         }
672         else if( !strcmp( psz_cmd, "logout" ) )
673         {
674             /* Close connection */
675             if( p_intf->p_sys->i_socket != -1 )
676             {
677                 net_Close( p_intf->p_sys->i_socket );
678             }
679             p_intf->p_sys->i_socket = -1;
680         }
681         else if( !strcmp( psz_cmd, "info" ) )
682         {
683             if( p_input )
684             {
685                 int i, j;
686                 vlc_mutex_lock( &input_GetItem(p_input)->lock );
687                 for ( i = 0; i < input_GetItem(p_input)->i_categories; i++ )
688                 {
689                     info_category_t *p_category = input_GetItem(p_input)
690                                                         ->pp_categories[i];
691
692                     msg_rc( "+----[ %s ]", p_category->psz_name );
693                     msg_rc( "| " );
694                     for ( j = 0; j < p_category->i_infos; j++ )
695                     {
696                         info_t *p_info = p_category->pp_infos[j];
697                         msg_rc( "| %s: %s", p_info->psz_name,
698                                 p_info->psz_value );
699                     }
700                     msg_rc( "| " );
701                 }
702                 msg_rc( "+----[ end of stream info ]" );
703                 vlc_mutex_unlock( &input_GetItem(p_input)->lock );
704             }
705             else
706             {
707                 msg_rc( "no input" );
708             }
709         }
710         else if( !strcmp( psz_cmd, "is_playing" ) )
711         {
712             if( ! p_input )
713             {
714                 msg_rc( "0" );
715             }
716             else
717             {
718                 msg_rc( "1" );
719             }
720         }
721         else if( !strcmp( psz_cmd, "get_time" ) )
722         {
723             if( ! p_input )
724             {
725                 msg_rc("0");
726             }
727             else
728             {
729                 vlc_value_t time;
730                 var_Get( p_input, "time", &time );
731                 msg_rc( "%i", time.i_time / 1000000);
732             }
733         }
734         else if( !strcmp( psz_cmd, "get_length" ) )
735         {
736             if( ! p_input )
737             {
738                 msg_rc("0");
739             }
740             else
741             {
742                 vlc_value_t time;
743                 var_Get( p_input, "length", &time );
744                 msg_rc( "%i", time.i_time / 1000000);
745             }
746         }
747         else if( !strcmp( psz_cmd, "get_title" ) )
748         {
749             if( ! p_input )
750             {
751                 msg_rc("");
752             }
753             else
754             {
755                 msg_rc( "%s", input_GetItem(p_input)->psz_name );
756             }
757         }
758         else if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "h", 1 )
759                  || !strncmp( psz_cmd, "H", 1 ) || !strncmp( psz_cmd, "?", 1 ) )
760         {
761             if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "H", 1 ) )
762                  b_longhelp = true;
763             else b_longhelp = false;
764
765             Help( p_intf, b_longhelp );
766         }
767         else if( !strcmp( psz_cmd, "key" ) || !strcmp( psz_cmd, "hotkey" ) )
768         {
769             var_SetInteger( p_intf->p_libvlc, "key-pressed",
770                             config_GetInt( p_intf, psz_arg ) );
771         }
772         else switch( psz_cmd[0] )
773         {
774         case 'f':
775         case 'F':
776             if( p_input )
777             {
778                 vout_thread_t *p_vout;
779                 p_vout = vlc_object_find( p_input,
780                                           VLC_OBJECT_VOUT, FIND_CHILD );
781
782                 if( p_vout )
783                 {
784                     vlc_value_t val;
785                     bool b_update = false;
786                     var_Get( p_vout, "fullscreen", &val );
787                     val.b_bool = !val.b_bool;
788                     if( !strncmp( psz_arg, "on", 2 )
789                         && ( val.b_bool == true ) )
790                     {
791                         b_update = true;
792                         val.b_bool = true;
793                     }
794                     else if( !strncmp( psz_arg, "off", 3 )
795                              && ( val.b_bool == false ) )
796                     {
797                         b_update = true;
798                         val.b_bool = false;
799                     }
800                     else if( strncmp( psz_arg, "off", 3 )
801                              && strncmp( psz_arg, "on", 2 ) )
802                         b_update = true;
803                     if( b_update ) var_Set( p_vout, "fullscreen", val );
804                     vlc_object_release( p_vout );
805                 }
806             }
807             break;
808
809         case 's':
810         case 'S':
811             ;
812             break;
813
814         case '\0':
815             /* Ignore empty lines */
816             break;
817
818         default:
819             msg_rc(_("Unknown command `%s'. Type `help' for help."), psz_cmd);
820             break;
821         }
822
823         /* Command processed */
824         i_size = 0; p_buffer[0] = 0;
825     }
826
827     msg_rc( STATUS_CHANGE "( stop state: 0 )" );
828     msg_rc( STATUS_CHANGE "( quit )" );
829
830     if( p_input )
831     {
832         var_DelCallback( p_input, "state", StateChanged, p_intf );
833         var_DelCallback( p_input, "rate-faster", RateChanged, p_intf );
834         var_DelCallback( p_input, "rate-slower", RateChanged, p_intf );
835         var_DelCallback( p_input, "rate", RateChanged, p_intf );
836         var_DelCallback( p_input, "time-offset", TimeOffsetChanged, p_intf );
837         vlc_object_release( p_input );
838         p_input = NULL;
839     }
840
841     if( p_playlist )
842     {
843         vlc_object_release( p_playlist );