You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

895 lines
28 KiB

/*****************************************************************************
* cli.c : remote control stdin/stdout module for vlc
*****************************************************************************
* Copyright (C) 2004-2009 the VideoLAN team
*
* Author: Peter Surda <shurdeek@panorama.sth.ac.at>
* Jean-Paul Saman <jpsaman #_at_# m2x _replaceWith#dot_ nl>
*
* 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.
*
* 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
/*****************************************************************************
* Preamble
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <errno.h> /* ENOMEM */
#include <assert.h>
#include <math.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef HAVE_WORDEXP_H
#include <wordexp.h>
#endif
#ifdef HAVE_SEARCH_H
#include <search.h>
#endif
#define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_interface.h>
#include <vlc_player.h>
#include <vlc_actions.h>
#include <vlc_fs.h>
#include <vlc_network.h>
#include <vlc_url.h>
#include <vlc_charset.h>
#if defined(PF_UNIX) && !defined(PF_LOCAL)
# define PF_LOCAL PF_UNIX
#endif
#if defined(AF_LOCAL) && ! defined(_WIN32)
# include <sys/un.h>
#endif
#include "cli.h"
#define MAX_LINE_LENGTH 1024
static void msg_vprint(intf_thread_t *p_intf, const char *psz_fmt, va_list args)
{
#ifndef _WIN32
intf_sys_t *sys = p_intf->p_sys;
int fd;
vlc_mutex_lock(&sys->output_lock);
fd = sys->fd;
if (fd != -1)
{
char *msg;
int len = vasprintf(&msg, psz_fmt, args);
if (unlikely(len < 0))
return;
struct iovec iov[2] = { { msg, len }, { (char *)"\n", 1 } };
vlc_writev(sys->fd, iov, ARRAY_SIZE(iov));
free(msg);
}
vlc_mutex_unlock(&sys->output_lock);
#else
char fmt_eol[strlen (psz_fmt) + 3], *msg;
int len;
snprintf (fmt_eol, sizeof (fmt_eol), "%s\r\n", psz_fmt);
len = vasprintf( &msg, fmt_eol, args );
if( len < 0 )
return;
if( p_intf->p_sys->i_socket == -1 )
utf8_fprintf( stdout, "%s", msg );
else
net_Write( p_intf, p_intf->p_sys->i_socket, msg, len );
free( msg );
#endif
}
void msg_print(intf_thread_t *intf, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
msg_vprint(intf, fmt, ap);
va_end(ap);
}
static int cmdcmp(const void *a, const void *b)
{
const char *const *na = a;
const char *const *nb = b;
return strcmp(*na, *nb);
}
void RegisterHandlers(intf_thread_t *intf, const struct cli_handler *handlers,
size_t count)
{
intf_sys_t *sys = intf->p_sys;
for (size_t i = 0; i < count; i++)
{
const char *const *name = &handlers[i].name;
const char *const **pp;
pp = tsearch(name, &sys->commands, cmdcmp);
if (unlikely(pp == NULL))
continue;
assert(*pp == name); /* Fails if duplicate command */
}
}
#if defined (_WIN32) && !VLC_WINSTORE_APP
# include "../intromsg.h"
#endif
static int Help( intf_thread_t *p_intf, const char *const *args, size_t count)
{
msg_rc("%s", _("+----[ Remote control commands ]"));
msg_rc( "| ");
msg_rc("%s", _("| add XYZ . . . . . . . . . . . . add XYZ to playlist"));
msg_rc("%s", _("| enqueue XYZ . . . . . . . . . queue XYZ to playlist"));
msg_rc("%s", _("| playlist . . . . . show items currently in playlist"));
msg_rc("%s", _("| play . . . . . . . . . . . . . . . . . . play stream"));
msg_rc("%s", _("| stop . . . . . . . . . . . . . . . . . . stop stream"));
msg_rc("%s", _("| next . . . . . . . . . . . . . . next playlist item"));
msg_rc("%s", _("| prev . . . . . . . . . . . . previous playlist item"));
msg_rc("%s", _("| goto . . . . . . . . . . . . . . goto item at index"));
msg_rc("%s", _("| repeat [on|off] . . . . toggle playlist item repeat"));
msg_rc("%s", _("| loop [on|off] . . . . . . . . . toggle playlist loop"));
msg_rc("%s", _("| random [on|off] . . . . . . . toggle random jumping"));
msg_rc("%s", _("| clear . . . . . . . . . . . . . . clear the playlist"));
msg_rc("%s", _("| status . . . . . . . . . . . current playlist status"));
msg_rc("%s", _("| title [X] . . . . . . set/get title in current item"));
msg_rc("%s", _("| title_n . . . . . . . . next title in current item"));
msg_rc("%s", _("| title_p . . . . . . previous title in current item"));
msg_rc("%s", _("| chapter [X] . . . . set/get chapter in current item"));
msg_rc("%s", _("| chapter_n . . . . . . next chapter in current item"));
msg_rc("%s", _("| chapter_p . . . . previous chapter in current item"));
msg_rc( "| ");
msg_rc("%s", _("| seek X . . . seek in seconds, for instance `seek 12'"));
msg_rc("%s", _("| pause . . . . . . . . . . . . . . . . toggle pause"));
msg_rc("%s", _("| fastforward . . . . . . . . . set to maximum rate"));
msg_rc("%s", _("| rewind . . . . . . . . . . . . set to minimum rate"));
msg_rc("%s", _("| faster . . . . . . . . . . faster playing of stream"));
msg_rc("%s", _("| slower . . . . . . . . . . slower playing of stream"));
msg_rc("%s", _("| normal . . . . . . . . . . normal playing of stream"));
msg_rc("%s", _("| frame. . . . . . . . . . play frame by frame"));
msg_rc("%s", _("| f [on|off] . . . . . . . . . . . . toggle fullscreen"));
msg_rc("%s", _("| info . . . . . information about the current stream"));
msg_rc("%s", _("| stats . . . . . . . . show statistical information"));
msg_rc("%s", _("| get_time . . seconds elapsed since stream's beginning"));
msg_rc("%s", _("| is_playing . . . . 1 if a stream plays, 0 otherwise"));
msg_rc("%s", _("| get_title . . . . . the title of the current stream"));
msg_rc("%s", _("| get_length . . . . the length of the current stream"));
msg_rc( "| ");
msg_rc("%s", _("| volume [X] . . . . . . . . . . set/get audio volume"));
msg_rc("%s", _("| volup [X] . . . . . . . raise audio volume X steps"));
msg_rc("%s", _("| voldown [X] . . . . . . lower audio volume X steps"));
msg_rc("%s", _("| adev [device] . . . . . . . . set/get audio device"));
msg_rc("%s", _("| achan [X]. . . . . . . . . . set/get audio channels"));
msg_rc("%s", _("| atrack [X] . . . . . . . . . . . set/get audio track"));
msg_rc("%s", _("| vtrack [X] . . . . . . . . . . . set/get video track"));
msg_rc("%s", _("| vratio [X] . . . . . . . set/get video aspect ratio"));
msg_rc("%s", _("| vcrop [X] . . . . . . . . . . . set/get video crop"));
msg_rc("%s", _("| vzoom [X] . . . . . . . . . . . set/get video zoom"));
msg_rc("%s", _("| snapshot . . . . . . . . . . . . take video snapshot"));
msg_rc("%s", _("| record [on|off] . . . . . . . . . . toggle recording"));
msg_rc("%s", _("| strack [X] . . . . . . . . . set/get subtitle track"));
msg_rc("%s", _("| key [hotkey name] . . . . . . simulate hotkey press"));
msg_rc( "| ");
msg_rc("%s", _("| help . . . . . . . . . . . . . . . this help message"));
msg_rc("%s", _("| logout . . . . . . . exit (if in socket connection)"));
msg_rc("%s", _("| quit . . . . . . . . . . . . . . . . . . . quit vlc"));
msg_rc( "| ");
msg_rc("%s", _("+----[ end of help ]"));
(void) args; (void) count;
return 0;
}
static int Intf(intf_thread_t *intf, const char *const *args, size_t count)
{
return intf_Create(vlc_object_instance(intf), count == 1 ? "" : args[1]);
}
static int Quit(intf_thread_t *intf, const char *const *args, size_t count)
{
libvlc_Quit(vlc_object_instance(intf));
(void) args; (void) count;
return 0;
}
static int LogOut(intf_thread_t *intf, const char *const *args, size_t count)
{
intf_sys_t *sys = intf->p_sys;
/* Close connection */
#ifndef _WIN32
if (sys->pi_socket_listen != NULL)
{
vlc_mutex_lock(&sys->output_lock);
sys->fd = -1;
vlc_mutex_unlock(&sys->output_lock);
fclose(sys->stream);
sys->stream = NULL;
}
else
{ /* Force end-of-file on the standard input. */
int fd = vlc_open("/dev/null", O_RDONLY);
if (fd != -1)
{ /* POSIX requires flushing before, and seeking after, replacing a
* file descriptor underneath an I/O stream.
*/
fflush(sys->stream);
dup2(fd, 0 /* fileno(sys->stream) */);
fseek(sys->stream, 0, SEEK_SET);
vlc_close(fd);
}
}
#else
if (sys->i_socket != -1)
{
net_Close(sys->i_socket);
sys->i_socket = -1;
}
#endif
(void) args; (void) count;
return 0;
}
static int KeyAction(intf_thread_t *intf, const char *const *args, size_t n)
{
vlc_object_t *vlc = VLC_OBJECT(vlc_object_instance(intf));
if (n != 1)
return VLC_EGENERIC; /* EINVAL */
var_SetInteger(vlc, "key-action", vlc_actions_get_id(args[1]));
return 0;
}
static const struct cli_handler cmds[] =
{
{ "longhelp", Help },
{ "h", Help },
{ "help", Help },
{ "H", Help },
{ "?", Help },
{ "logout", LogOut },
{ "quit", Quit },
{ "intf", Intf },
{ "key", KeyAction },
{ "hotkey", KeyAction },
};
static int UnknownCmd(intf_thread_t *intf, const char *const *args,
size_t count)
{
msg_print(intf, _("Unknown command `%s'. Type `help' for help."), args[0]);
(void) count;
return VLC_EGENERIC;
}
static int Process(intf_thread_t *intf, const char *line)
{
intf_sys_t *sys = intf->p_sys;
/* Skip heading spaces */
const char *cmd = line + strspn(line, " ");
int ret;
if (*cmd == '\0')
return 0; /* Ignore empty line */
#ifdef HAVE_WORDEXP
wordexp_t we;
int val = wordexp(cmd, &we, 0);
if (val != 0)
{
if (val == WRDE_NOSPACE)
{
ret = VLC_ENOMEM;
error: wordfree(&we);
}
else
ret = VLC_EGENERIC;
msg_print(intf, N_("parse error"));
return ret;
}
size_t count = we.we_wordc;
const char **args = vlc_alloc(count, sizeof (*args));
if (unlikely(args == NULL))
{
ret = VLC_ENOMEM;
goto error;
}
for (size_t i = 0; i < we.we_wordc; i++)
args[i] = we.we_wordv[i];
#else
/* Split psz_cmd at the first space and make sure that
* psz_arg is valid */
const char *args[] = { cmd, NULL };
size_t count = 1;
char *arg = strchr(cmd, ' ');
if (arg != NULL)
{
*(arg++) = '\0';
arg += strspn(arg, " ");
if (*arg)
count++;
}
#endif
if (count > 0)
{
int (*cb)(intf_thread_t *, const char *const *, size_t) = UnknownCmd;
const struct cli_handler **h = tfind(&args[0], &sys->commands, cmdcmp);
if (h != NULL)
cb = (*h)->callback;
ret = cb(intf, args, count);
}
#ifdef HAVE_WORDEXP
free(args);
wordfree(&we);
#endif
return ret;
}
#ifndef _WIN32
static void *Run(void *data)
{
intf_thread_t *intf = data;
intf_sys_t *sys = intf->p_sys;
for (;;)
{
char buf[MAX_LINE_LENGTH + 1];
while (sys->stream == NULL)
{
assert(sys->pi_socket_listen != NULL);
int fd = net_Accept(intf, sys->pi_socket_listen);
if (fd == -1)
continue;
int canc = vlc_savecancel();
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
sys->stream = fdopen(fd, "r");
if (sys->stream != NULL)
{
vlc_mutex_lock(&sys->output_lock);
sys->fd = fd;
vlc_mutex_unlock(&sys->output_lock);
}
else
vlc_close(fd);
vlc_restorecancel(canc);
}
char *cmd = fgets(buf, sizeof (buf), sys->stream);
if (cmd != NULL)
{
int canc = vlc_savecancel();
if (cmd[0] != '\0')
cmd[strlen(cmd) - 1] = '\0'; /* remove trailing LF */
Process(intf, cmd);
vlc_restorecancel(canc);
}
else if (sys->pi_socket_listen == NULL)
break;
else
LogOut(intf, NULL, 0);
}
int canc = vlc_savecancel();
libvlc_Quit(vlc_object_instance(intf));
vlc_restorecancel(canc);
return NULL;
}
#else
#if !VLC_WINSTORE_APP
static bool ReadWin32( intf_thread_t *p_intf, unsigned char *p_buffer, int *pi_size )
{
INPUT_RECORD input_record;
DWORD i_dw;
/* On Win32, select() only works on socket descriptors */
while( WaitForSingleObjectEx( p_intf->p_sys->hConsoleIn,
MS_FROM_VLC_TICK(INTF_IDLE_SLEEP), TRUE ) == WAIT_OBJECT_0 )
{
// Prefer to fail early when there's not enough space to store a 4 bytes
// UTF8 character. The function will be immediatly called again and we won't
// lose an input
while( *pi_size < MAX_LINE_LENGTH - 4 &&
ReadConsoleInput( p_intf->p_sys->hConsoleIn, &input_record, 1, &i_dw ) )
{
if( input_record.EventType != KEY_EVENT ||
!input_record.Event.KeyEvent.bKeyDown ||
input_record.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT ||
input_record.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL||
input_record.Event.KeyEvent.wVirtualKeyCode == VK_MENU ||
input_record.Event.KeyEvent.wVirtualKeyCode == VK_CAPITAL )
{
/* nothing interesting */
continue;
}
if( input_record.Event.KeyEvent.uChar.AsciiChar == '\n' ||
input_record.Event.KeyEvent.uChar.AsciiChar == '\r' )
{
putc( '\n', stdout );
break;
}
switch( input_record.Event.KeyEvent.uChar.AsciiChar )
{
case '\b':
if ( *pi_size == 0 )
break;
if ( *pi_size > 1 && (p_buffer[*pi_size - 1] & 0xC0) == 0x80 )
{
// pi_size currently points to the character to be written, so
// we need to roll back from 2 bytes to start erasing the previous
// character
(*pi_size) -= 2;
unsigned int nbBytes = 1;
while( *pi_size > 0 && (p_buffer[*pi_size] & 0xC0) == 0x80 )
{
(*pi_size)--;
nbBytes++;
}
assert( clz( (unsigned char)~(p_buffer[*pi_size]) ) == nbBytes + 1 );
// The first utf8 byte will be overriden by a \0
}
else
(*pi_size)--;
p_buffer[*pi_size] = 0;
fputs( "\b \b", stdout );
break;
default:
{
WCHAR psz_winput[] = { input_record.Event.KeyEvent.uChar.UnicodeChar, L'\0' };
char* psz_input = FromWide( psz_winput );
int input_size = strlen(psz_input);
if ( *pi_size + input_size > MAX_LINE_LENGTH )
{
p_buffer[ *pi_size ] = 0;
return false;
}
strcpy( (char*)&p_buffer[*pi_size], psz_input );
utf8_fprintf( stdout, "%s", psz_input );
free(psz_input);
*pi_size += input_size;
}
}
}
p_buffer[ *pi_size ] = 0;
return true;
}
vlc_testcancel ();
return false;
}
#endif
static bool ReadCommand(intf_thread_t *p_intf, char *p_buffer, int *pi_size)
{
#if !VLC_WINSTORE_APP
if( p_intf->p_sys->i_socket == -1 && !p_intf->p_sys->b_quiet )
return ReadWin32( p_intf, (unsigned char*)p_buffer, pi_size );
else if( p_intf->p_sys->i_socket == -1 )
{
vlc_tick_sleep( INTF_IDLE_SLEEP );
return false;
}
#endif
while( *pi_size < MAX_LINE_LENGTH )
{
if( p_intf->p_sys->i_socket == -1 )
{
if( read( 0/*STDIN_FILENO*/, p_buffer + *pi_size, 1 ) <= 0 )
{ /* Standard input closed: exit */
libvlc_Quit( vlc_object_instance(p_intf) );
p_buffer[*pi_size] = 0;
return true;
}
}
else
{ /* Connection closed */
if( net_Read( p_intf, p_intf->p_sys->i_socket, p_buffer + *pi_size,
1 ) <= 0 )
{
net_Close( p_intf->p_sys->i_socket );
p_intf->p_sys->i_socket = -1;
p_buffer[*pi_size] = 0;
return true;
}
}
if( p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
break;
(*pi_size)++;
}
if( *pi_size == MAX_LINE_LENGTH ||
p_buffer[ *pi_size ] == '\r' || p_buffer[ *pi_size ] == '\n' )
{
p_buffer[ *pi_size ] = 0;
return true;
}
return false;
}
/*****************************************************************************
* 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( void *data )
{
intf_thread_t *p_intf = data;
intf_sys_t *p_sys = p_intf->p_sys;
char p_buffer[ MAX_LINE_LENGTH + 1 ];
int i_size = 0;
int canc = vlc_savecancel( );
p_buffer[0] = 0;
#if !VLC_WINSTORE_APP
/* Get the file descriptor of the console input */
p_intf->p_sys->hConsoleIn = GetStdHandle(STD_INPUT_HANDLE);
if( p_intf->p_sys->hConsoleIn == INVALID_HANDLE_VALUE )
{
msg_Err( p_intf, "couldn't find user input handle" );
return NULL;
}
#endif
for( ;; )
{
bool b_complete;
vlc_restorecancel( canc );
if( p_sys->pi_socket_listen != NULL && p_sys->i_socket == -1 )
{
p_sys->i_socket =
net_Accept( p_intf, p_sys->pi_socket_listen );
if( p_sys->i_socket == -1 ) continue;
}
b_complete = ReadCommand( p_intf, p_buffer, &i_size );
canc = vlc_savecancel( );
/* Is there something to do? */
if( !b_complete ) continue;
Process(p_intf, p_buffer);
/* Command processed */
i_size = 0; p_buffer[0] = 0;
}
vlc_assert_unreachable();
}
#endif
/*****************************************************************************
* Activate: initialize and create stuff
*****************************************************************************/
static int Activate( vlc_object_t *p_this )
{
/* FIXME: This function is full of memory leaks and bugs in error paths. */
intf_thread_t *p_intf = (intf_thread_t*)p_this;
char *psz_host, *psz_unix_path = NULL;
int *pi_socket = NULL;
#ifndef _WIN32
#if defined(HAVE_ISATTY)
/* Check that stdin is a TTY */
if( !var_InheritBool( p_intf, "rc-fake-tty" ) && !isatty( 0 ) )
{
msg_Warn( p_intf, "fd 0 is not a TTY" );
return VLC_EGENERIC;
}
#endif
#ifdef AF_LOCAL
psz_unix_path = var_InheritString( p_intf, "rc-unix" );
if( psz_unix_path )
{
int i_socket;
struct sockaddr_un addr;
memset( &addr, 0, sizeof(struct sockaddr_un) );
msg_Dbg( p_intf, "trying UNIX socket" );
/* The given unix path cannot be longer than sun_path - 1 to take into
* account the terminated null character. */
if ( strlen(psz_unix_path) + 1 >= sizeof( addr.sun_path ) )
{
msg_Err( p_intf, "rc-unix value is longer than expected" );
return VLC_EGENERIC;
}
if( (i_socket = vlc_socket( PF_LOCAL, SOCK_STREAM, 0, false ) ) < 0 )
{
msg_Warn( p_intf, "can't open socket: %s", vlc_strerror_c(errno) );
free( psz_unix_path );
return VLC_EGENERIC;
}
addr.sun_family = AF_LOCAL;
strncpy( addr.sun_path, psz_unix_path, sizeof( addr.sun_path ) - 1 );
addr.sun_path[sizeof( addr.sun_path ) - 1] = '\0';
if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr))
&& (errno == EADDRINUSE)
&& connect (i_socket, (struct sockaddr *)&addr, sizeof (addr))
&& (errno == ECONNREFUSED))
{
msg_Info (p_intf, "Removing dead UNIX socket: %s", psz_unix_path);
unlink (psz_unix_path);
if (bind (i_socket, (struct sockaddr *)&addr, sizeof (addr)))
{
msg_Err (p_intf, "cannot bind UNIX socket at %s: %s",
psz_unix_path, vlc_strerror_c(errno));
free (psz_unix_path);
net_Close (i_socket);
return VLC_EGENERIC;
}
}
if( listen( i_socket, 1 ) )
{
msg_Warn (p_intf, "can't listen on socket: %s",
vlc_strerror_c(errno));
free( psz_unix_path );
net_Close( i_socket );
return VLC_EGENERIC;
}
/* 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;
}
#endif /* AF_LOCAL */
#endif /* !_WIN32 */
if( ( pi_socket == NULL ) &&
( psz_host = var_InheritString( p_intf, "rc-host" ) ) != NULL )
{
vlc_url_t url;
vlc_UrlParse( &url, psz_host );
if( url.psz_host == NULL )
{
vlc_UrlClean( &url );
char *psz_backward_compat_host;
if( asprintf( &psz_backward_compat_host, "//%s", psz_host ) < 0 )
{
free( psz_host );
return VLC_EGENERIC;
}
free( psz_host );
psz_host = psz_backward_compat_host;
vlc_UrlParse( &url, psz_host );
}
msg_Dbg( p_intf, "base: %s, port: %d", url.psz_host, url.i_port );
pi_socket = net_ListenTCP(p_this, url.psz_host, url.i_port);
if( pi_socket == NULL )
{
msg_Warn( p_intf, "can't listen to %s port %i",
url.psz_host, url.i_port );
vlc_UrlClean( &url );
free( psz_host );
return VLC_EGENERIC;
}
vlc_UrlClean( &url );
free( psz_host );
}
intf_sys_t *p_sys = malloc( sizeof( *p_sys ) );
if( unlikely(p_sys == NULL) )
{
net_ListenClose( pi_socket );
free( psz_unix_path );
return VLC_ENOMEM;
}
p_intf->p_sys = p_sys;
p_sys->commands = NULL;
vlc_mutex_init(&p_sys->output_lock);
p_sys->pi_socket_listen = pi_socket;
p_sys->playlist = vlc_intf_GetMainPlaylist(p_intf);;
RegisterHandlers(p_intf, cmds, ARRAY_SIZE(cmds));
/* Line-buffered stdout */
setvbuf( stdout, (char *)NULL, _IOLBF, 0 );
#ifndef _WIN32
if (pi_socket == NULL)
{
p_sys->stream = stdin;
p_sys->fd = 1;
}
else
{
p_sys->stream = NULL;
p_sys->fd = -1;
}
p_sys->psz_unix_path = psz_unix_path;
#else
p_sys->i_socket = -1;
#if VLC_WINSTORE_APP
p_sys->b_quiet = true;
#else
p_sys->b_quiet = var_InheritBool( p_intf, "rc-quiet" );
if( !p_sys->b_quiet )
intf_consoleIntroMsg( p_intf );
#endif
#endif
p_sys->player_cli = RegisterPlayer(p_intf);
if (unlikely(p_sys->player_cli == NULL))
goto error;
RegisterPlaylist(p_intf);
if( vlc_clone( &p_sys->thread, Run, p_intf, VLC_THREAD_PRIORITY_LOW ) )
goto error;
msg_rc( "%s", _("Remote control interface initialized. Type `help' for help.") );
return VLC_SUCCESS;
error:
net_ListenClose( pi_socket );
free( psz_unix_path );
free( p_sys );
return VLC_EGENERIC;
}
static void dummy_free(void *p)
{
(void) p;
}
/*****************************************************************************
* Deactivate: uninitialize and cleanup
*****************************************************************************/
static void Deactivate( vlc_object_t *p_this )
{
intf_thread_t *p_intf = (intf_thread_t*)p_this;
intf_sys_t *p_sys = p_intf->p_sys;
vlc_cancel( p_sys->thread );
vlc_join( p_sys->thread, NULL );
DeregisterPlayer(p_intf, p_sys->player_cli);
tdestroy(p_sys->commands, dummy_free);
if (p_sys->pi_socket_listen != NULL)
{
net_ListenClose(p_sys->pi_socket_listen);
#ifndef _WIN32
if (p_sys->stream != NULL)
fclose(p_sys->stream);
if (p_sys->psz_unix_path != NULL)
{
unlink(p_sys->psz_unix_path);
free(p_sys->psz_unix_path);
}
#else
if (p_sys->i_socket != -1)
net_Close(p_sys->i_socket);
#endif
}
free( p_sys );
}
/*****************************************************************************
* Module descriptor
*****************************************************************************/
#define POS_TEXT N_("Show stream position")
#define POS_LONGTEXT N_("Show the current position in seconds within the " \
"stream from time to time." )
#define TTY_TEXT N_("Fake TTY")
#define TTY_LONGTEXT N_("Force the rc module to use stdin as if it was a TTY.")
#define UNIX_TEXT N_("UNIX socket command input")
#define UNIX_LONGTEXT N_("Accept commands over a Unix socket rather than " \
"stdin." )
#define HOST_TEXT N_("TCP command input")
#define HOST_LONGTEXT N_("Accept commands over a socket rather than stdin. " \
"You can set the address and port the interface will bind to." )
#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
vlc_module_begin()
set_shortname(N_("RC"))
set_category(CAT_INTERFACE)
set_subcategory(SUBCAT_INTERFACE_MAIN)
set_description(N_("Remote control interface"))
add_bool("rc-show-pos", false, POS_TEXT, POS_LONGTEXT, true)
#ifdef _WIN32
add_bool("rc-quiet", false, QUIET_TEXT, QUIET_LONGTEXT, false)
#else
#if defined (HAVE_ISATTY)
add_bool("rc-fake-tty", false, TTY_TEXT, TTY_LONGTEXT, true)
#endif
#ifdef AF_LOCAL
add_string("rc-unix", NULL, UNIX_TEXT, UNIX_LONGTEXT, true)
#endif
#endif
add_string("rc-host", NULL, HOST_TEXT, HOST_LONGTEXT, true)
set_capability("interface", 20)
set_callbacks(Activate, Deactivate)
add_shortcut("cli", "rc", "oldrc")
vlc_module_end()