rc.c 56.4 KB
Newer Older
1
/*****************************************************************************
Anil Daoud's avatar
Anil Daoud committed
2
 * rc.c : remote control stdin/stdout module for vlc
3
 *****************************************************************************
4
 * Copyright (C) 2004 - 2005 VideoLAN
5
 * $Id$
6
 *
7
 * Author: Peter Surda <shurdeek@panorama.sth.ac.at>
8 9 10 11 12
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
13
 *
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */
#include <string.h>

#include <errno.h>                                                 /* ENOMEM */
#include <stdio.h>
#include <ctype.h>
33
#include <signal.h>
34 35 36

#include <vlc/vlc.h>
#include <vlc/intf.h>
37
#include <vlc/aout.h>
38 39 40 41 42 43 44 45 46 47 48
#include <vlc/vout.h>

#ifdef HAVE_UNISTD_H
#    include <unistd.h>
#endif

#ifdef HAVE_SYS_TIME_H
#    include <sys/time.h>
#endif
#include <sys/types.h>

49
#include "vlc_error.h"
50 51
#include "network.h"

52 53 54 55 56 57 58 59
#if defined(PF_UNIX) && !defined(PF_LOCAL)
#    define PF_LOCAL PF_UNIX
#endif
#if defined(AF_UNIX) && !defined(AF_LOCAL)
#    define AF_LOCAL AF_UNIX
#endif

#ifdef PF_LOCAL
60 61
#    include <sys/un.h>
#endif
62

63 64 65 66 67 68
#define MAX_LINE_LENGTH 256

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
static int  Activate     ( vlc_object_t * );
69 70 71 72
static void Deactivate   ( vlc_object_t * );
static void Run          ( intf_thread_t * );

static vlc_bool_t ReadCommand( intf_thread_t *, char *, int * );
73

74 75
static playlist_item_t *parse_MRL( intf_thread_t *, char * );

76 77
static int  Input        ( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
gbazin's avatar
 
gbazin committed
78 79
static int  Playlist     ( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
80 81
static int  Other        ( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
gbazin's avatar
 
gbazin committed
82 83 84 85 86 87 88 89 90 91
static int  Quit         ( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
static int  Intf         ( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
static int  Volume       ( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
static int  VolumeMove   ( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
static int  AudioConfig  ( vlc_object_t *, char const *,
                           vlc_value_t, vlc_value_t, void * );
92

93 94
struct intf_sys_t
{
95
    int *pi_socket_listen;
96
    int i_socket;
97
    char *psz_unix_path;
98

99 100
#ifdef WIN32
    HANDLE hConsoleIn;
101
    vlc_bool_t b_quiet;
102 103 104 105
#endif
};

#ifdef HAVE_VARIADIC_MACROS
106 107
#   define msg_rc( psz_format, args... ) \
      __msg_rc( p_intf, psz_format, ## args )
108 109
#endif

110
void __msg_rc( intf_thread_t *p_intf, const char *psz_fmt, ... )
111 112 113 114
{
    va_list args;
    va_start( args, psz_fmt );
    if( p_intf->p_sys->i_socket == -1 ) vprintf( psz_fmt, args );
115
    else
116 117
    { net_vaPrintf( p_intf, p_intf->p_sys->i_socket, NULL, psz_fmt, args );
      net_Printf( VLC_OBJECT(p_intf), p_intf->p_sys->i_socket, NULL, "\r" ); }
118 119 120
    va_end( args );
}

121 122 123
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Christophe Massiot's avatar
Christophe Massiot committed
124
#define POS_TEXT N_("Show stream position")
125 126
#define POS_LONGTEXT N_("Show the current position in seconds within the " \
                        "stream from time to time." )
127

Christophe Massiot's avatar
Christophe Massiot committed
128
#define TTY_TEXT N_("Fake TTY")
Anil Daoud's avatar
Anil Daoud committed
129
#define TTY_LONGTEXT N_("Force the rc module to use stdin as if it was a TTY.")
130

131
#define UNIX_TEXT N_("UNIX socket command input")
132 133
#define UNIX_LONGTEXT N_("Accept commands over a Unix socket rather than " \
                         "stdin." )
134

135
#define HOST_TEXT N_("TCP command input")
136
#define HOST_LONGTEXT N_("Accept commands over a socket rather than stdin. " \
137 138
            "You can set the address and port the interface will bind to." )

139 140 141 142 143 144 145 146 147
#ifdef WIN32
#define QUIET_TEXT N_("Do not open a DOS command box interface")
#define QUIET_LONGTEXT N_( \
    "By default the rc interface plugin will start a DOS command box. " \
    "Enabling the quiet mode will not bring this command box but can also " \
    "be pretty annoying when you want to stop VLC and no video window is " \
    "open." )
#endif

148
vlc_module_begin();
149
    set_shortname( _("RC"));
zorglub's avatar
zorglub committed
150
    set_category( CAT_INTERFACE );
151
    set_subcategory( SUBCAT_INTERFACE_GENERAL );
gbazin's avatar
 
gbazin committed
152
    set_description( _("Remote control interface") );
153
    add_bool( "rc-show-pos", 0, NULL, POS_TEXT, POS_LONGTEXT, VLC_TRUE );
Sam Hocevar's avatar
Sam Hocevar committed
154
#ifdef HAVE_ISATTY
155
    add_bool( "rc-fake-tty", 0, NULL, TTY_TEXT, TTY_LONGTEXT, VLC_TRUE );
Sam Hocevar's avatar
Sam Hocevar committed
156
#endif
157
    add_string( "rc-unix", 0, NULL, UNIX_TEXT, UNIX_LONGTEXT, VLC_TRUE );
158
    add_string( "rc-host", 0, NULL, HOST_TEXT, HOST_LONGTEXT, VLC_TRUE );
159 160 161 162 163

#ifdef WIN32
    add_bool( "rc-quiet", 0, NULL, QUIET_TEXT, QUIET_LONGTEXT, VLC_FALSE );
#endif

164
    set_capability( "interface", 20 );
165
    set_callbacks( Activate, Deactivate );
166 167 168 169 170 171 172 173
vlc_module_end();

/*****************************************************************************
 * Activate: initialize and create stuff
 *****************************************************************************/
static int Activate( vlc_object_t *p_this )
{
    intf_thread_t *p_intf = (intf_thread_t*)p_this;
174
    playlist_t *p_playlist;
175
    char *psz_host, *psz_unix_path;
176
    int *pi_socket = NULL;
177

gbazin's avatar
 
gbazin committed
178
#if defined(HAVE_ISATTY) && !defined(WIN32)
179
    /* Check that stdin is a TTY */
180
    if( !config_GetInt( p_intf, "rc-fake-tty" ) && !isatty( 0 ) )
181 182
    {
        msg_Warn( p_intf, "fd 0 is not a TTY" );
183
        return VLC_EGENERIC;
184 185 186
    }
#endif

187 188
    psz_unix_path = config_GetPsz( p_intf, "rc-unix" );
    if( psz_unix_path )
189
    {
190 191
        int i_socket;

192
#ifndef PF_LOCAL
193
        msg_Warn( p_intf, "your OS doesn't support filesystem sockets" );
194
        free( psz_unix_path );
195 196 197 198 199 200 201 202 203
        return VLC_EGENERIC;
#else
        struct sockaddr_un addr;
        int i_ret;

        memset( &addr, 0, sizeof(struct sockaddr_un) );

        msg_Dbg( p_intf, "trying UNIX socket" );

204
        if( (i_socket = socket( PF_LOCAL, SOCK_STREAM, 0 ) ) < 0 )
205 206
        {
            msg_Warn( p_intf, "can't open socket: %s", strerror(errno) );
207
            free( psz_unix_path );
208 209 210
            return VLC_EGENERIC;
        }

211 212 213
        addr.sun_family = AF_LOCAL;
        strncpy( addr.sun_path, psz_unix_path, sizeof( addr.sun_path ) );
        addr.sun_path[sizeof( addr.sun_path ) - 1] = '\0';
214 215 216 217 218 219

        if( (i_ret = bind( i_socket, (struct sockaddr*)&addr,
                           sizeof(struct sockaddr_un) ) ) < 0 )
        {
            msg_Warn( p_intf, "couldn't bind socket to address: %s",
                      strerror(errno) );
220 221
            free( psz_unix_path );
            net_Close( i_socket );
222 223 224 225 226 227
            return VLC_EGENERIC;
        }

        if( ( i_ret = listen( i_socket, 1 ) ) < 0 )
        {
            msg_Warn( p_intf, "can't listen on socket: %s", strerror(errno));
228 229
            free( psz_unix_path );
            net_Close( i_socket );
230 231
            return VLC_EGENERIC;
        }
232 233 234 235 236 237 238 239 240 241 242

        /* FIXME: we need a core function to merge listening sockets sets */
        pi_socket = calloc( 2, sizeof( int ) );
        if( pi_socket == NULL )
        {
            free( psz_unix_path );
            net_Close( i_socket );
            return VLC_ENOMEM;
        }
        pi_socket[0] = i_socket;
        pi_socket[1] = -1;
243 244
#endif
    }
245

246
    if( ( pi_socket == NULL ) &&
247
        ( psz_host = config_GetPsz( p_intf, "rc-host" ) ) != NULL )
248 249 250
    {
        vlc_url_t url;

251 252 253 254
        vlc_UrlParse( &url, psz_host, 0 );

        msg_Dbg( p_intf, "base %s port %d", url.psz_host, url.i_port );

255 256
        pi_socket = net_ListenTCP(p_this, url.psz_host, url.i_port);
        if( pi_socket == NULL )
257
        {
258 259
            msg_Warn( p_intf, "can't listen to %s port %i",
                      url.psz_host, url.i_port );
260 261 262 263 264 265
            vlc_UrlClean( &url );
            free( psz_host );
            return VLC_EGENERIC;
        }

        vlc_UrlClean( &url );
266
        free( psz_host );
267 268 269 270 271 272 273 274 275
    }

    p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
    if( !p_intf->p_sys )
    {
        msg_Err( p_intf, "no memory" );
        return VLC_ENOMEM;
    }

276
    p_intf->p_sys->pi_socket_listen = pi_socket;
277
    p_intf->p_sys->i_socket = -1;
278
    p_intf->p_sys->psz_unix_path = psz_unix_path;
279

280 281 282 283 284
    /* Non-buffered stdout */
    setvbuf( stdout, (char *)NULL, _IOLBF, 0 );

    p_intf->pf_run = Run;

285
#ifdef WIN32
286 287
    p_intf->p_sys->b_quiet = config_GetInt( p_intf, "rc-quiet" );
    if( !p_intf->p_sys->b_quiet ) { CONSOLE_INTRO_MSG; }
288
#else
gbazin's avatar
 
gbazin committed
289
    CONSOLE_INTRO_MSG;
290
#endif
291

292 293 294 295 296 297 298 299 300 301 302
    /* Force "no-view" mode */
    p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
                                                 FIND_ANYWHERE );
    if( p_playlist )
    {
        vlc_mutex_lock( &p_playlist->object_lock );
        p_playlist->status.i_view = -1;
        vlc_mutex_unlock( &p_playlist->object_lock );
        vlc_object_release( p_playlist );
    }

303
    msg_rc( _("Remote control interface initialized, `h' for help\n") );
304
    return VLC_SUCCESS;
305 306
}

307 308 309 310 311 312 313
/*****************************************************************************
 * Deactivate: uninitialize and cleanup
 *****************************************************************************/
static void Deactivate( vlc_object_t *p_this )
{
    intf_thread_t *p_intf = (intf_thread_t*)p_this;

314
    net_ListenClose( p_intf->p_sys->pi_socket_listen );
315 316
    if( p_intf->p_sys->i_socket != -1 )
        net_Close( p_intf->p_sys->i_socket );
317 318 319 320 321 322 323
    if( p_intf->p_sys->psz_unix_path != NULL )
    {
#ifdef PF_LOCAL
        unlink( p_intf->p_sys->psz_unix_path );
#endif
        free( p_intf->p_sys->psz_unix_path );
    }
324 325 326
    free( p_intf->p_sys );
}

327 328 329 330 331 332 333 334 335
/*****************************************************************************
 * Run: rc thread
 *****************************************************************************
 * This part of the interface is in a separate thread so that we can call
 * exec() from within it without annoying the rest of the program.
 *****************************************************************************/
static void Run( intf_thread_t *p_intf )
{
    input_thread_t * p_input;
336
    playlist_t *     p_playlist;
337 338

    char       p_buffer[ MAX_LINE_LENGTH + 1 ];
339
    vlc_bool_t b_showpos = config_GetInt( p_intf, "rc-show-pos" );
340
    vlc_bool_t b_longhelp = VLC_FALSE;
341

342
    int        i_size = 0;
Laurent Aimar's avatar
Laurent Aimar committed
343 344
    int        i_oldpos = 0;
    int        i_newpos;
345

346
    p_buffer[0] = 0;
gbazin's avatar
 
gbazin committed
347 348
    p_input = NULL;
    p_playlist = NULL;
349
 
350
    /* Register commands that will be cleaned up upon object destruction */
gbazin's avatar
 
gbazin committed
351 352 353 354 355
    var_Create( p_intf, "quit", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "quit", Quit, NULL );
    var_Create( p_intf, "intf", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "intf", Intf, NULL );

356 357 358 359
    var_Create( p_intf, "add", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "add", Playlist, NULL );
    var_Create( p_intf, "playlist", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "playlist", Playlist, NULL );
gbazin's avatar
 
gbazin committed
360 361 362 363 364 365 366 367
    var_Create( p_intf, "play", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "play", Playlist, NULL );
    var_Create( p_intf, "stop", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "stop", Playlist, NULL );
    var_Create( p_intf, "prev", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "prev", Playlist, NULL );
    var_Create( p_intf, "next", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "next", Playlist, NULL );
hartman's avatar
hartman committed
368 369
    var_Create( p_intf, "goto", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "goto", Playlist, NULL );
370 371
 
    /* marquee on the fly items */
372 373
    var_Create( p_intf, "marq-marquee", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "marq-marquee", Other, NULL );
374
    var_Create( p_intf, "marq-x", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
375
    var_AddCallback( p_intf, "marq-x", Other, NULL );
376
    var_Create( p_intf, "marq-y", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
377
    var_AddCallback( p_intf, "marq-y", Other, NULL );
378 379 380 381 382 383
    var_Create( p_intf, "marq-position", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "marq-position", Other, NULL );
    var_Create( p_intf, "marq-color", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "marq-color", Other, NULL );
    var_Create( p_intf, "marq-opacity", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "marq-opacity", Other, NULL );
zorglub's avatar
zorglub committed
384
    var_Create( p_intf, "marq-timeout", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
385
    var_AddCallback( p_intf, "marq-timeout", Other, NULL );
386 387
    var_Create( p_intf, "marq-size", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "marq-size", Other, NULL );
gbazin's avatar
 
gbazin committed
388

389 390 391 392 393 394 395 396 397 398
    var_Create( p_intf, "mosaic-alpha", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "mosaic-alpha", Other, NULL );
    var_Create( p_intf, "mosaic-height", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "mosaic-height", Other, NULL );
    var_Create( p_intf, "mosaic-width", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "mosaic-width", Other, NULL );
    var_Create( p_intf, "mosaic-xoffset", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "mosaic-xoffset", Other, NULL );
    var_Create( p_intf, "mosaic-yoffset", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "mosaic-yoffset", Other, NULL );
399 400
    var_Create( p_intf, "mosaic-align", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "mosaic-align", Other, NULL );
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
    var_Create( p_intf, "mosaic-vborder", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "mosaic-vborder", Other, NULL );
    var_Create( p_intf, "mosaic-hborder", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "mosaic-hborder", Other, NULL );
    var_Create( p_intf, "mosaic-position",
                     VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "mosaic-position", Other, NULL );
    var_Create( p_intf, "mosaic-rows", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "mosaic-rows", Other, NULL );
    var_Create( p_intf, "mosaic-cols", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "mosaic-cols", Other, NULL );
    var_Create( p_intf, "mosaic-keep-aspect-ratio",
                     VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "mosaic-keep-aspect-ratio", Other, NULL );

416 417 418 419 420 421 422 423 424 425 426 427 428
    /* time on the fly items */
    var_Create( p_intf, "time-format", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "time-format", Other, NULL );
    var_Create( p_intf, "time-x", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "time-x", Other, NULL );
    var_Create( p_intf, "time-y", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "time-y", Other, NULL );
    var_Create( p_intf, "time-position", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "time-position", Other, NULL );
    var_Create( p_intf, "time-color", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "time-color", Other, NULL );
    var_Create( p_intf, "time-opacity", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "time-opacity", Other, NULL );
429 430
    var_Create( p_intf, "time-size", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "time-size", Other, NULL );
431
    
432
    /* logo on the fly items */
Mark Moriarty's avatar
Mark Moriarty committed
433 434
    var_Create( p_intf, "logo-file", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "logo-file", Other, NULL );
435 436 437 438 439 440 441 442 443 444
    var_Create( p_intf, "logo-x", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "logo-x", Other, NULL );
    var_Create( p_intf, "logo-y", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "logo-y", Other, NULL );
    var_Create( p_intf, "logo-position", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "logo-position", Other, NULL );
    var_Create( p_intf, "logo-transparency", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "logo-transparency", Other, NULL );
    
    
445 446 447 448
    var_Create( p_intf, "pause", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "pause", Input, NULL );
    var_Create( p_intf, "seek", VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "seek", Input, NULL );
gbazin's avatar
 
gbazin committed
449
    var_Create( p_intf, "title", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
450
    var_AddCallback( p_intf, "title", Input, NULL );
gbazin's avatar
 
gbazin committed
451
    var_Create( p_intf, "title_n", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
452
    var_AddCallback( p_intf, "title_n", Input, NULL );
gbazin's avatar
 
gbazin committed
453
    var_Create( p_intf, "title_p", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
454
    var_AddCallback( p_intf, "title_p", Input, NULL );
gbazin's avatar
 
gbazin committed
455
    var_Create( p_intf, "chapter", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
456
    var_AddCallback( p_intf, "chapter", Input, NULL );
gbazin's avatar
 
gbazin committed
457
    var_Create( p_intf, "chapter_n", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
458
    var_AddCallback( p_intf, "chapter_n", Input, NULL );
gbazin's avatar
 
gbazin committed
459
    var_Create( p_intf, "chapter_p", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
460
    var_AddCallback( p_intf, "chapter_p", Input, NULL );
gbazin's avatar
 
gbazin committed
461

hartman's avatar
hartman committed
462 463 464 465 466
    var_Create( p_intf, "fastforward", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "fastforward", Input, NULL );
    var_Create( p_intf, "rewind", VLC_VAR_VOID | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "rewind", Input, NULL );

gbazin's avatar
 
gbazin committed
467 468 469 470 471 472 473 474 475 476
    var_Create( p_intf, "volume", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "volume", Volume, NULL );
    var_Create( p_intf, "volup", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "volup", VolumeMove, NULL );
    var_Create( p_intf, "voldown", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "voldown", VolumeMove, NULL );
    var_Create( p_intf, "adev", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "adev", AudioConfig, NULL );
    var_Create( p_intf, "achan", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
    var_AddCallback( p_intf, "achan", AudioConfig, NULL );
477

gbazin's avatar
 
gbazin committed
478 479
#ifdef WIN32
    /* Get the file descriptor of the console input */
480 481
    p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
    if( p_intf->p_sys->hConsoleIn == INVALID_HANDLE_VALUE )
gbazin's avatar
 
gbazin committed
482
    {
483
        msg_Err( p_intf, "Couldn't open STD_INPUT_HANDLE" );
gbazin's avatar
 
gbazin committed
484
        p_intf->b_die = VLC_TRUE;
gbazin's avatar
 
gbazin committed
485 486 487
    }
#endif

488 489
    while( !p_intf->b_die )
    {
490 491
        char *psz_cmd, *psz_arg;
        vlc_bool_t b_complete;
492

493
        if( p_intf->p_sys->pi_socket_listen != NULL &&
494
            p_intf->p_sys->i_socket == -1 )
495
        {
496
            p_intf->p_sys->i_socket =
497
                net_Accept( p_intf, p_intf->p_sys->pi_socket_listen, 0 );
498 499
        }

500 501
        b_complete = ReadCommand( p_intf, p_buffer, &i_size );

502 503 504
        /* Manage the input part */
        if( p_input == NULL )
        {
505 506 507 508 509 510 511 512 513
            if( p_playlist )
            {
                p_input = vlc_object_find( p_playlist, VLC_OBJECT_INPUT,
                                                       FIND_CHILD );
            }
            else
            {
                p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
                                                   FIND_ANYWHERE );
514
                if( p_input )
515 516 517 518 519
                {
                    p_playlist = vlc_object_find( p_input, VLC_OBJECT_PLAYLIST,
                                                           FIND_PARENT );
                }
            }
520 521 522 523 524 525 526
        }
        else if( p_input->b_dead )
        {
            vlc_object_release( p_input );
            p_input = NULL;
        }

527
        if( p_input && b_showpos )
528
        {
Laurent Aimar's avatar
Laurent Aimar committed
529 530
            i_newpos = 100 * var_GetFloat( p_input, "position" );
            if( i_oldpos != i_newpos )
531
            {
Laurent Aimar's avatar
Laurent Aimar committed
532
                i_oldpos = i_newpos;
533
                msg_rc( "pos: %d%%\n", i_newpos );
534 535 536 537
            }
        }

        /* Is there something to do? */
538 539 540 541 542 543
        if( !b_complete ) continue;


        /* Skip heading spaces */
        psz_cmd = p_buffer;
        while( *psz_cmd == ' ' )
544
        {
545 546
            psz_cmd++;
        }
547

548 549 550 551 552 553 554
        /* Split psz_cmd at the first space and make sure that
         * psz_arg is valid */
        psz_arg = strchr( psz_cmd, ' ' );
        if( psz_arg )
        {
            *psz_arg++ = 0;
            while( *psz_arg == ' ' )
555
            {
556
                psz_arg++;
557
            }
558 559 560 561 562 563 564 565 566 567 568
        }
        else
        {
            psz_arg = "";
        }

        /* If the user typed a registered local command, try it */
        if( var_Type( p_intf, psz_cmd ) & VLC_VAR_ISCOMMAND )
        {
            vlc_value_t val;
            int i_ret;
569

570 571
            val.psz_string = psz_arg;
            i_ret = var_Set( p_intf, psz_cmd, val );
572
            msg_rc( "%s: returned %i (%s)\n",
573 574 575 576 577 578 579 580 581 582 583 584
                    psz_cmd, i_ret, vlc_error( i_ret ) );
        }
        /* Or maybe it's a global command */
        else if( var_Type( p_intf->p_libvlc, psz_cmd ) & VLC_VAR_ISCOMMAND )
        {
            vlc_value_t val;
            int i_ret;

            val.psz_string = psz_arg;
            /* FIXME: it's a global command, but we should pass the
             * local object as an argument, not p_intf->p_libvlc. */
            i_ret = var_Set( p_intf->p_libvlc, psz_cmd, val );
585 586
            if( i_ret != 0 )
            {
587
                msg_rc( "%s: returned %i (%s)\n",
588 589
                         psz_cmd, i_ret, vlc_error( i_ret ) );
            }
590
        }
zorglub's avatar
zorglub committed
591 592 593 594 595 596 597 598 599
        else if( !strcmp( psz_cmd, "logout" ) )
        {
            /* Close connection */
            if( p_intf->p_sys->i_socket != -1 )
            {
                net_Close( p_intf->p_sys->i_socket );
            }
            p_intf->p_sys->i_socket = -1;
        }
600 601 602
        else if( !strcmp( psz_cmd, "info" ) )
        {
            if( p_input )
603
            {
604 605 606
                int i, j;
                vlc_mutex_lock( &p_input->input.p_item->lock );
                for ( i = 0; i < p_input->input.p_item->i_categories; i++ )
607
                {
608 609 610
                    info_category_t *p_category =
                        p_input->input.p_item->pp_categories[i];

611 612
                    msg_rc( "+----[ %s ]\n", p_category->psz_name );
                    msg_rc( "| \n" );
613 614 615
                    for ( j = 0; j < p_category->i_infos; j++ )
                    {
                        info_t *p_info = p_category->pp_infos[j];
616
                        msg_rc( "| %s: %s\n", p_info->psz_name,
617 618
                                p_info->psz_value );
                    }
619
                    msg_rc( "| \n" );
620
                }
621
                msg_rc( "+----[ end of stream info ]\n" );
622
                vlc_mutex_unlock( &p_input->input.p_item->lock );
623
            }
624
            else
625
            {
626
                msg_rc( "no input\n" );
Sam Hocevar's avatar
Sam Hocevar committed
627
            }
628
        }
629 630 631 632
        else if( !strcmp( psz_cmd, "is_playing" ) )
        {
            if( ! p_input )
            {
633
                msg_rc( "0\n" );
634 635 636
            }
            else
            {
637
                msg_rc( "1\n" );
638 639 640 641 642 643
            }
        }
        else if( !strcmp( psz_cmd, "get_time" ) )
        {
            if( ! p_input )
            {
644
                msg_rc("0\n");
645 646 647 648 649
            }
            else
            {
                vlc_value_t time;
                var_Get( p_input, "time", &time );
650
                msg_rc( "%i\n", time.i_time / 1000000);
651 652 653 654 655 656
            }
        }
        else if( !strcmp( psz_cmd, "get_length" ) )
        {
            if( ! p_input )
            {
657
                msg_rc("0\n");
658 659 660 661 662
            }
            else
            {
                vlc_value_t time;
                var_Get( p_input, "length", &time );
663
                msg_rc( "%i\n", time.i_time / 1000000);
664 665 666 667 668 669
            }
        }
        else if( !strcmp( psz_cmd, "get_title" ) )
        {
            if( ! p_input )
            {
670
                msg_rc("\n");
671 672 673
            }
            else
            {
674
                msg_rc( "%s\n", p_input->input.p_item->psz_name );
675 676
            }
        }
677 678
        else if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "h", 1 )
                 || !strncmp( psz_cmd, "H", 1 ) || !strncmp( psz_cmd, "?", 1 ) )
679
        {
680 681 682 683
            if( !strcmp( psz_cmd, "longhelp" ) || !strncmp( psz_cmd, "H", 1 ) )
                 b_longhelp = VLC_TRUE;
            else b_longhelp = VLC_FALSE;
            
684
            msg_rc(_("+----[ Remote control commands ]\n"));
hartman's avatar
hartman committed
685
            msg_rc(  "| \n");
686 687 688 689 690 691
            msg_rc(_("| add XYZ  . . . . . . . . . . add XYZ to playlist\n"));
            msg_rc(_("| playlist . . .  show items currently in playlist\n"));
            msg_rc(_("| play . . . . . . . . . . . . . . . . play stream\n"));
            msg_rc(_("| stop . . . . . . . . . . . . . . . . stop stream\n"));
            msg_rc(_("| next . . . . . . . . . . . .  next playlist item\n"));
            msg_rc(_("| prev . . . . . . . . . .  previous playlist item\n"));
hartman's avatar
hartman committed
692
            msg_rc(_("| goto . . . . . . . . . . . .  goto item at index\n"));
693 694 695 696 697 698
            msg_rc(_("| title [X]  . . . . set/get title in current item\n"));
            msg_rc(_("| title_n  . . . . . .  next title in current item\n"));
            msg_rc(_("| title_p  . . . .  previous title in current item\n"));
            msg_rc(_("| chapter [X]  . . set/get chapter in current item\n"));
            msg_rc(_("| chapter_n  . . . .  next chapter in current item\n"));
            msg_rc(_("| chapter_p  . .  previous chapter in current item\n"));
hartman's avatar
hartman committed
699
            msg_rc(  "| \n");
700 701
            msg_rc(_("| seek X . seek in seconds, for instance `seek 12'\n"));
            msg_rc(_("| pause  . . . . . . . . . . . . . .  toggle pause\n"));
hartman's avatar
hartman committed
702 703
            msg_rc(_("| fastforward  . . . . . .  .  set to maximum rate\n"));
            msg_rc(_("| rewind  . . . . . . . . . .  set to minimum rate\n"));
704 705
            msg_rc(_("| f  . . . . . . . . . . . . . . toggle fullscreen\n"));
            msg_rc(_("| info . . .  information about the current stream\n"));
hartman's avatar
hartman committed
706
            msg_rc(  "| \n");
707 708 709 710 711
            msg_rc(_("| volume [X] . . . . . . . .  set/get audio volume\n"));
            msg_rc(_("| volup [X]  . . . . .  raise audio volume X steps\n"));
            msg_rc(_("| voldown [X]  . . . .  lower audio volume X steps\n"));
            msg_rc(_("| adev [X] . . . . . . . . .  set/get audio device\n"));
            msg_rc(_("| achan [X]. . . . . . . .  set/get audio channels\n"));
hartman's avatar
hartman committed
712 713
            msg_rc(  "| \n");
            
714
            if (b_longhelp)
715
            {
hartman's avatar
hartman committed
716
                msg_rc(_("| marq-marquee STRING  . . overlay STRING in video\n"));
717 718
                msg_rc(_("| marq-x X . . . . . . . . . . . .offset from left\n"));
                msg_rc(_("| marq-y Y . . . . . . . . . . . . offset from top\n"));
719
                msg_rc(_("| marq-position #. . .  .relative position control\n"));
720 721 722 723
                msg_rc(_("| marq-color # . . . . . . . . . . font color, RGB\n"));
                msg_rc(_("| marq-opacity # . . . . . . . . . . . . . opacity\n"));
                msg_rc(_("| marq-timeout T. . . . . . . . . . timeout, in ms\n"));
                msg_rc(_("| marq-size # . . . . . . . . font size, in pixels\n"));
hartman's avatar
hartman committed
724
                msg_rc(  "| \n");
725
                msg_rc(_("| time-format STRING . . . overlay STRING in video\n"));
726 727 728 729 730 731 732
                msg_rc(_("| time-x X . . . . . . . . . . . .offset from left\n"));
                msg_rc(_("| time-y Y . . . . . . . . . . . . offset from top\n"));
                msg_rc(_("| time-position #. . . . . . . . relative position\n"));
                msg_rc(_("| time-color # . . . . . . . . . . font color, RGB\n"));
                msg_rc(_("| time-opacity # . . . . . . . . . . . . . opacity\n"));
                msg_rc(_("| time-size # . . . . . . . . font size, in pixels\n"));
                msg_rc(  "| \n");
Mark Moriarty's avatar
Mark Moriarty committed
733
                msg_rc(_("| logo-file STRING . . . the overlay file path/name\n"));
734 735 736 737
                msg_rc(_("| logo-x X . . . . . . . . . . . .offset from left\n"));
                msg_rc(_("| logo-y Y . . . . . . . . . . . . offset from top\n"));
                msg_rc(_("| logo-position #. . . . . . . . relative position\n"));
                msg_rc(_("| logo-transparency #. . . . . . . . .transparency\n"));
738
                msg_rc(  "| \n");
dionoea's avatar
dionoea committed
739 740 741 742 743
                msg_rc(_("| mosaic-alpha # . . . . . . . . . . . . . . alpha\n"));
                msg_rc(_("| mosaic-height #. . . . . . . . . . . . . .height\n"));
                msg_rc(_("| mosaic-width # . . . . . . . . . . . . . . width\n"));
                msg_rc(_("| mosaic-xoffset # . . . .top left corner position\n"));
                msg_rc(_("| mosaic-yoffset # . . . .top left corner position\n"));
744
                msg_rc(_("| mosaic-align 0..2,4..6,8..10. . .mosaic alignment\n"));
dionoea's avatar
dionoea committed
745 746 747 748 749 750 751
                msg_rc(_("| mosaic-vborder # . . . . . . . . vertical border\n"));
                msg_rc(_("| mosaic-hborder # . . . . . . . horizontal border\n"));
                msg_rc(_("| mosaic-position {0=auto,1=fixed} . . . .position\n"));
                msg_rc(_("| mosaic-rows #. . . . . . . . . . .number of rows\n"));
                msg_rc(_("| mosaic-cols #. . . . . . . . . . .number of cols\n"));
                msg_rc(_("| mosaic-keep-aspect-ratio {0,1} . . .aspect ratio\n"));
                msg_rc(  "| \n");
752
            }    
753
            msg_rc(_("| help . . . . . . . . . . . . . this help message\n"));
754
            msg_rc(_("| longhelp . . . . . . . . . a longer help message\n"));
hartman's avatar
hartman committed
755
            msg_rc(_("| logout . . . . .  exit (if in socket connection)\n"));
756
            msg_rc(_("| quit . . . . . . . . . . . . . . . . .  quit vlc\n"));
hartman's avatar
hartman committed
757
            msg_rc(  "| \n");
758
            msg_rc(_("+----[ end of help ]\n"));
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
        }
        else switch( psz_cmd[0] )
        {
        case 'f':
        case 'F':
            if( p_input )
            {
                vout_thread_t *p_vout;
                p_vout = vlc_object_find( p_input,
                                          VLC_OBJECT_VOUT, FIND_CHILD );

                if( p_vout )
                {
                    p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
                    vlc_object_release( p_vout );
                }
            }
            break;

        case 's':
        case 'S':
            ;
781 782 783 784 785 786 787
            break;

        case '\0':
            /* Ignore empty lines */
            break;

        default:
788
            msg_rc(_("unknown command `%s', type `help' for help\n"), psz_cmd);
789
            break;
790
        }
791 792 793

        /* Command processed */
        i_size = 0; p_buffer[0] = 0;
794 795 796 797 798 799
    }

    if( p_input )
    {
        vlc_object_release( p_input );
        p_input = NULL;
800
    }
801

802 803 804 805
    if( p_playlist )
    {
        vlc_object_release( p_playlist );
        p_playlist = NULL;
806
    }
807 808
}

809 810
static int Input( vlc_object_t *p_this, char const *psz_cmd,
                  vlc_value_t oldval, vlc_value_t newval, void *p_data )
811
{
812 813
    intf_thread_t *p_intf = (intf_thread_t*)p_this;
    input_thread_t *p_input;
814
    vlc_value_t     val;
815 816

    p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_ANYWHERE );
817
    if( !p_input ) return VLC_ENOOBJ;
818 819 820 821

    /* Parse commands that only require an input */
    if( !strcmp( psz_cmd, "pause" ) )
    {
822 823 824
        val.i_int = PAUSE_S;

        var_Set( p_input, "state", val );
825 826 827
        vlc_object_release( p_input );
        return VLC_SUCCESS;
    }
828 829
    else if( !strcmp( psz_cmd, "seek" ) )
    {
830 831 832
        if( strlen( newval.psz_string ) > 0 &&
            newval.psz_string[strlen( newval.psz_string ) - 1] == '%' )
        {
833 834
            val.f_float = (float)atoi( newval.psz_string ) / 100.0;
            var_Set( p_input, "position", val );
835 836 837
        }
        else
        {
gbazin's avatar
gbazin committed
838
            val.i_time = ((int64_t)atoi( newval.psz_string )) * 1000000;
839
            var_Set( p_input, "time", val );
840 841 842
        }
        vlc_object_release( p_input );
        return VLC_SUCCESS;
843
    }
hartman's avatar
hartman committed
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
    else if ( !strcmp( psz_cmd, "fastforward" ) )
    {
        val.i_int = INPUT_RATE_MAX;

        var_Set( p_input, "rate", val );
        vlc_object_release( p_input );
        return VLC_SUCCESS;
    }
    else if ( !strcmp( psz_cmd, "rewind" ) )
    {
        val.i_int = INPUT_RATE_MIN;

        var_Set( p_input, "rate", val );
        vlc_object_release( p_input );
        return VLC_SUCCESS;
    }
gbazin's avatar
 
gbazin committed
860 861 862 863 864 865
    else if( !strcmp( psz_cmd, "chapter" ) ||
             !strcmp( psz_cmd, "chapter_n" ) ||
             !strcmp( psz_cmd, "chapter_p" ) )
    {
        if( !strcmp( psz_cmd, "chapter" ) )
        {
gbazin's avatar
 
gbazin committed
866
            if ( *newval.psz_string )
gbazin's avatar
 
gbazin committed
867 868
            {
                /* Set. */
869 870
                val.i_int = atoi( newval.psz_string );
                var_Set( p_input, "chapter", val );
gbazin's avatar
 
gbazin committed
871 872 873
            }
            else
            {
874 875
                vlc_value_t val_list;

gbazin's avatar
 
gbazin committed
876
                /* Get. */
877
                var_Get( p_input, "chapter", &val );
878 879
                var_Change( p_input, "chapter", VLC_VAR_GETCHOICES,
                            &val_list, NULL );
880
                msg_rc( "Currently playing chapter %d/%d\n",
881 882 883
                        val.i_int, val_list.p_list->i_count );
                var_Change( p_this, "chapter", VLC_VAR_FREELIST,
                            &val_list, NULL );
gbazin's avatar
 
gbazin committed
884 885 886 887
            }
        }
        else if( !strcmp( psz_cmd, "chapter_n" ) )
        {
888 889
            val.b_bool = VLC_TRUE;
            var_Set( p_input, "next-chapter", val );
gbazin's avatar
 
gbazin committed
890 891 892
        }
        else if( !strcmp( psz_cmd, "chapter_p" ) )
        {
893 894
            val.b_bool = VLC_TRUE;
            var_Set( p_input, "prev-chapter", val );
gbazin's avatar
 
gbazin committed
895 896 897 898 899 900 901 902 903 904 905
        }

        vlc_object_release( p_input );
        return VLC_SUCCESS;
    }
    else if( !strcmp( psz_cmd, "title" ) ||
             !strcmp( psz_cmd, "title_n" ) ||
             !strcmp( psz_cmd, "title_p" ) )
    {
        if( !strcmp( psz_cmd, "title" ) )
        {
gbazin's avatar
 
gbazin committed
906
            if ( *newval.psz_string )
gbazin's avatar
 
gbazin committed
907 908
            {
                /* Set. */
909 910
                val.i_int = atoi( newval.psz_string );
                var_Set( p_input, "title", val );
gbazin's avatar
<