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