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