1 /*****************************************************************************
2 * cli.c : remote control stdin/stdout module for vlc
3 *****************************************************************************
4 * Copyright (C) 2004-2009 the VideoLAN team
6 * Author: Peter Surda <shurdeek@panorama.sth.ac.at>
7 * Jean-Paul Saman <jpsaman #_at_# m2x _replaceWith#dot_ nl>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
32 #include <errno.h> /* ENOMEM */
35 #include <sys/types.h>
45 #define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
46 #include <vlc_common.h>
47 #include <vlc_plugin.h>
48 #include <vlc_interface.h>
49 #include <vlc_player.h>
50 #include <vlc_actions.h>
52 #include <vlc_network.h>
54 #include <vlc_charset.h>
56 #if defined(PF_UNIX) && !defined(PF_LOCAL)
57 # define PF_LOCAL PF_UNIX
60 #if defined(AF_LOCAL) && ! defined(_WIN32)
73 struct cli_client client
;
80 int *pi_socket_listen
;
83 #define MAX_LINE_LENGTH 1024
88 struct cli_handler handler
;
93 static int cmdcmp(const void *a
, const void *b
)
95 const char *const *na
= a
;
96 const char *const *nb
= b
;
98 return strcmp(*na
, *nb
);
101 void RegisterHandlers(intf_thread_t
*intf
, const struct cli_handler
*handlers
,
102 size_t count
, void *opaque
)
104 intf_sys_t
*sys
= intf
->p_sys
;
106 for (size_t i
= 0; i
< count
; i
++)
108 struct command
*cmd
= malloc(sizeof (*cmd
));
109 if (unlikely(cmd
== NULL
))
112 cmd
->handler
= handlers
[i
];
115 struct command
**pp
= tsearch(&cmd
->name
, &sys
->commands
, cmdcmp
);
116 if (unlikely(pp
== NULL
))
122 assert(*pp
== cmd
); /* Fails if duplicate command */
126 static int Help(struct cli_client
*cl
, const char *const *args
, size_t count
,
129 msg_rc("%s", _("+----[ Remote control commands ]"));
131 msg_rc("%s", _("| add XYZ . . . . . . . . . . . . add XYZ to playlist"));
132 msg_rc("%s", _("| enqueue XYZ . . . . . . . . . queue XYZ to playlist"));
133 msg_rc("%s", _("| playlist . . . . . show items currently in playlist"));
134 msg_rc("%s", _("| play . . . . . . . . . . . . . . . . . . play stream"));
135 msg_rc("%s", _("| stop . . . . . . . . . . . . . . . . . . stop stream"));
136 msg_rc("%s", _("| next . . . . . . . . . . . . . . next playlist item"));
137 msg_rc("%s", _("| prev . . . . . . . . . . . . previous playlist item"));
138 msg_rc("%s", _("| goto . . . . . . . . . . . . . . goto item at index"));
139 msg_rc("%s", _("| repeat [on|off] . . . . toggle playlist item repeat"));
140 msg_rc("%s", _("| loop [on|off] . . . . . . . . . toggle playlist loop"));
141 msg_rc("%s", _("| random [on|off] . . . . . . . toggle random jumping"));
142 msg_rc("%s", _("| clear . . . . . . . . . . . . . . clear the playlist"));
143 msg_rc("%s", _("| status . . . . . . . . . . . current playlist status"));
144 msg_rc("%s", _("| title [X] . . . . . . set/get title in current item"));
145 msg_rc("%s", _("| title_n . . . . . . . . next title in current item"));
146 msg_rc("%s", _("| title_p . . . . . . previous title in current item"));
147 msg_rc("%s", _("| chapter [X] . . . . set/get chapter in current item"));
148 msg_rc("%s", _("| chapter_n . . . . . . next chapter in current item"));
149 msg_rc("%s", _("| chapter_p . . . . previous chapter in current item"));
151 msg_rc("%s", _("| seek X . . . seek in seconds, for instance `seek 12'"));
152 msg_rc("%s", _("| pause . . . . . . . . . . . . . . . . toggle pause"));
153 msg_rc("%s", _("| fastforward . . . . . . . . . set to maximum rate"));
154 msg_rc("%s", _("| rewind . . . . . . . . . . . . set to minimum rate"));
155 msg_rc("%s", _("| faster . . . . . . . . . . faster playing of stream"));
156 msg_rc("%s", _("| slower . . . . . . . . . . slower playing of stream"));
157 msg_rc("%s", _("| normal . . . . . . . . . . normal playing of stream"));
158 msg_rc("%s", _("| frame. . . . . . . . . . play frame by frame"));
159 msg_rc("%s", _("| f [on|off] . . . . . . . . . . . . toggle fullscreen"));
160 msg_rc("%s", _("| info . . . . . information about the current stream"));
161 msg_rc("%s", _("| stats . . . . . . . . show statistical information"));
162 msg_rc("%s", _("| get_time . . seconds elapsed since stream's beginning"));
163 msg_rc("%s", _("| is_playing . . . . 1 if a stream plays, 0 otherwise"));
164 msg_rc("%s", _("| get_title . . . . . the title of the current stream"));
165 msg_rc("%s", _("| get_length . . . . the length of the current stream"));
167 msg_rc("%s", _("| volume [X] . . . . . . . . . . set/get audio volume"));
168 msg_rc("%s", _("| volup [X] . . . . . . . raise audio volume X steps"));
169 msg_rc("%s", _("| voldown [X] . . . . . . lower audio volume X steps"));
170 msg_rc("%s", _("| adev [device] . . . . . . . . set/get audio device"));
171 msg_rc("%s", _("| achan [X]. . . . . . . . . . set/get audio channels"));
172 msg_rc("%s", _("| atrack [X] . . . . . . . . . . . set/get audio track"));
173 msg_rc("%s", _("| vtrack [X] . . . . . . . . . . . set/get video track"));
174 msg_rc("%s", _("| vratio [X] . . . . . . . set/get video aspect ratio"));
175 msg_rc("%s", _("| vcrop [X] . . . . . . . . . . . set/get video crop"));
176 msg_rc("%s", _("| vzoom [X] . . . . . . . . . . . set/get video zoom"));
177 msg_rc("%s", _("| snapshot . . . . . . . . . . . . take video snapshot"));
178 msg_rc("%s", _("| record [on|off] . . . . . . . . . . toggle recording"));
179 msg_rc("%s", _("| strack [X] . . . . . . . . . set/get subtitle track"));
180 msg_rc("%s", _("| key [hotkey name] . . . . . . simulate hotkey press"));
182 msg_rc("%s", _("| help . . . . . . . . . . . . . . . this help message"));
183 msg_rc("%s", _("| logout . . . . . . . exit (if in socket connection)"));
184 msg_rc("%s", _("| quit . . . . . . . . . . . . . . . . . . . quit vlc"));
186 msg_rc("%s", _("+----[ end of help ]"));
187 (void) args
; (void) count
; (void) data
;
191 static int Intf(struct cli_client
*cl
, const char *const *args
, size_t count
,
194 intf_thread_t
*intf
= data
;
198 return intf_Create(vlc_object_instance(intf
), count
== 1 ? "" : args
[1]);
201 static int Quit(struct cli_client
*cl
, const char *const *args
, size_t count
,
204 intf_thread_t
*intf
= data
;
206 libvlc_Quit(vlc_object_instance(intf
));
207 (void) cl
; (void) args
; (void) count
;
211 static int LogOut(struct cli_client
*cl
, const char *const *args
, size_t count
,
214 intf_thread_t
*intf
= data
;
215 intf_sys_t
*sys
= intf
->p_sys
;
217 /* Close connection */
219 if (sys
->pi_socket_listen
!= NULL
)
221 vlc_mutex_lock(&cl
->output_lock
);
223 vlc_mutex_unlock(&cl
->output_lock
);
228 { /* Force end-of-file on the standard input. */
229 int fd
= vlc_open("/dev/null", O_RDONLY
);
231 { /* POSIX requires flushing before, and seeking after, replacing a
232 * file descriptor underneath an I/O stream.
235 dup2(fd
, 0 /* fileno(sys->stream) */);
236 fseek(cl
->stream
, 0, SEEK_SET
);
241 if (sys
->i_socket
!= -1)
243 net_Close(sys
->i_socket
);
247 (void) args
; (void) count
;
251 static int KeyAction(struct cli_client
*cl
, const char *const *args
, size_t n
,
254 intf_thread_t
*intf
= data
;
255 vlc_object_t
*vlc
= VLC_OBJECT(vlc_object_instance(intf
));
258 return VLC_EGENERIC
; /* EINVAL */
260 var_SetInteger(vlc
, "key-action", vlc_actions_get_id(args
[1]));
265 static const struct cli_handler cmds
[] =
267 { "longhelp", Help
},
272 { "logout", LogOut
},
276 { "key", KeyAction
},
277 { "hotkey", KeyAction
},
280 static int Process(intf_thread_t
*intf
, struct cli_client
*cl
, const char *line
)
282 intf_sys_t
*sys
= intf
->p_sys
;
283 /* Skip heading spaces */
284 const char *cmd
= line
+ strspn(line
, " ");
288 return 0; /* Ignore empty line */
292 int val
= wordexp(cmd
, &we
, 0);
296 if (val
== WRDE_NOSPACE
)
299 error
: wordfree(&we
);
304 cli_printf(cl
, N_("parse error"));
308 size_t count
= we
.we_wordc
;
309 const char **args
= vlc_alloc(count
, sizeof (*args
));
310 if (unlikely(args
== NULL
))
316 for (size_t i
= 0; i
< we
.we_wordc
; i
++)
317 args
[i
] = we
.we_wordv
[i
];
319 /* Split psz_cmd at the first space and make sure that
320 * psz_arg is valid */
321 const char *args
[] = { cmd
, NULL
};
323 char *arg
= strchr(cmd
, ' ');
328 arg
+= strspn(arg
, " ");
337 const struct command
**pp
= tfind(&args
[0], &sys
->commands
, cmdcmp
);
341 const struct command
*c
= *pp
;;
343 ret
= c
->handler
.callback(cl
, args
, count
, c
->data
);
347 cli_printf(cl
, _("Unknown command `%s'. Type `help' for help."),
361 static ssize_t
cli_writev(struct cli_client
*cl
,
362 const struct iovec
*iov
, unsigned iovlen
)
366 vlc_mutex_lock(&cl
->output_lock
);
368 val
= vlc_writev(cl
->fd
, iov
, iovlen
);
370 errno
= EPIPE
, val
= -1;
371 vlc_mutex_unlock(&cl
->output_lock
);
375 static int cli_vprintf(struct cli_client
*cl
, const char *fmt
, va_list args
)
378 int len
= vasprintf(&msg
, fmt
, args
);
380 if (likely(len
>= 0))
382 struct iovec iov
[2] = { { msg
, len
}, { (char *)"\n", 1 } };
384 cli_writev(cl
, iov
, ARRAY_SIZE(iov
));
391 int cli_printf(struct cli_client
*cl
, const char *fmt
, ...)
397 len
= cli_vprintf(cl
, fmt
, ap
);
402 static void msg_vprint(intf_thread_t
*p_intf
, const char *psz_fmt
, va_list args
)
404 intf_sys_t
*sys
= p_intf
->p_sys
;
406 cli_vprintf(&sys
->client
, psz_fmt
, args
);
409 void msg_print(intf_thread_t
*intf
, const char *fmt
, ...)
414 msg_vprint(intf
, fmt
, ap
);
418 static void *cli_client_thread(void *data
)
420 struct cli_client
*cl
= data
;
421 intf_thread_t
*intf
= cl
->intf
;
423 while (cl
->stream
!= NULL
)
425 char cmd
[MAX_LINE_LENGTH
+ 1];
427 if (fgets(cmd
, sizeof (cmd
), cl
->stream
) == NULL
)
430 int canc
= vlc_savecancel();
432 cmd
[strlen(cmd
) - 1] = '\0'; /* remove trailing LF */
433 Process(intf
, cl
, cmd
);
434 vlc_restorecancel(canc
);
437 if (cl
->stream
== stdin
)
439 int canc
= vlc_savecancel();
440 libvlc_Quit(vlc_object_instance(intf
));
441 vlc_restorecancel(canc
);
447 static void *Run(void *data
)
449 intf_thread_t
*intf
= data
;
450 intf_sys_t
*sys
= intf
->p_sys
;
454 struct cli_client
*cl
= &sys
->client
;
456 while (cl
->stream
== NULL
)
458 assert(sys
->pi_socket_listen
!= NULL
);
460 int fd
= net_Accept(intf
, sys
->pi_socket_listen
);
464 int canc
= vlc_savecancel();
466 fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
) & ~O_NONBLOCK
);
467 cl
->stream
= fdopen(fd
, "r");
468 if (cl
->stream
!= NULL
)
470 vlc_mutex_lock(&cl
->output_lock
);
472 vlc_mutex_unlock(&cl
->output_lock
);
477 vlc_restorecancel(canc
);
480 cli_client_thread(cl
);
482 if (sys
->pi_socket_listen
== NULL
)
490 static void msg_vprint(intf_thread_t
*p_intf
, const char *psz_fmt
, va_list args
)
492 char fmt_eol
[strlen (psz_fmt
) + 3], *msg
;
495 snprintf (fmt_eol
, sizeof (fmt_eol
), "%s\r\n", psz_fmt
);
496 len
= vasprintf( &msg
, fmt_eol
, args
);
501 if( p_intf
->p_sys
->i_socket
== -1 )
502 utf8_fprintf( stdout
, "%s", msg
);
504 net_Write( p_intf
, p_intf
->p_sys
->i_socket
, msg
, len
);
509 void msg_print(intf_thread_t
*intf
, const char *fmt
, ...)
514 msg_vprint(intf
, fmt
, ap
);
518 int cli_printf(struct cli_client
*cl
, const char *fmt
, ...)
523 msg_vprint(cl
->intf
, fmt
, ap
);
528 #if !VLC_WINSTORE_APP
529 static bool ReadWin32( intf_thread_t
*p_intf
, unsigned char *p_buffer
, int *pi_size
)
531 INPUT_RECORD input_record
;
534 /* On Win32, select() only works on socket descriptors */
535 while( WaitForSingleObjectEx( p_intf
->p_sys
->hConsoleIn
,
536 MS_FROM_VLC_TICK(INTF_IDLE_SLEEP
), TRUE
) == WAIT_OBJECT_0
)
538 // Prefer to fail early when there's not enough space to store a 4 bytes
539 // UTF8 character. The function will be immediatly called again and we won't
541 while( *pi_size
< MAX_LINE_LENGTH
- 4 &&
542 ReadConsoleInput( p_intf
->p_sys
->hConsoleIn
, &input_record
, 1, &i_dw
) )
544 if( input_record
.EventType
!= KEY_EVENT
||
545 !input_record
.Event
.KeyEvent
.bKeyDown
||
546 input_record
.Event
.KeyEvent
.wVirtualKeyCode
== VK_SHIFT
||
547 input_record
.Event
.KeyEvent
.wVirtualKeyCode
== VK_CONTROL
||
548 input_record
.Event
.KeyEvent
.wVirtualKeyCode
== VK_MENU
||
549 input_record
.Event
.KeyEvent
.wVirtualKeyCode
== VK_CAPITAL
)
551 /* nothing interesting */
554 if( input_record
.Event
.KeyEvent
.uChar
.AsciiChar
== '\n' ||
555 input_record
.Event
.KeyEvent
.uChar
.AsciiChar
== '\r' )
557 putc( '\n', stdout
);
560 switch( input_record
.Event
.KeyEvent
.uChar
.AsciiChar
)
565 if ( *pi_size
> 1 && (p_buffer
[*pi_size
- 1] & 0xC0) == 0x80 )
567 // pi_size currently points to the character to be written, so
568 // we need to roll back from 2 bytes to start erasing the previous
571 unsigned int nbBytes
= 1;
572 while( *pi_size
> 0 && (p_buffer
[*pi_size
] & 0xC0) == 0x80 )
577 assert( clz( (unsigned char)~(p_buffer
[*pi_size
]) ) == nbBytes
+ 1 );
578 // The first utf8 byte will be overriden by a \0
582 p_buffer
[*pi_size
] = 0;
584 fputs( "\b \b", stdout
);
588 WCHAR psz_winput
[] = { input_record
.Event
.KeyEvent
.uChar
.UnicodeChar
, L
'\0' };
589 char* psz_input
= FromWide( psz_winput
);
590 int input_size
= strlen(psz_input
);
591 if ( *pi_size
+ input_size
> MAX_LINE_LENGTH
)
593 p_buffer
[ *pi_size
] = 0;
596 strcpy( (char*)&p_buffer
[*pi_size
], psz_input
);
597 utf8_fprintf( stdout
, "%s", psz_input
);
599 *pi_size
+= input_size
;
604 p_buffer
[ *pi_size
] = 0;
614 static bool ReadCommand(intf_thread_t
*p_intf
, char *p_buffer
, int *pi_size
)
616 #if !VLC_WINSTORE_APP
617 if( p_intf
->p_sys
->i_socket
== -1 && !p_intf
->p_sys
->b_quiet
)
618 return ReadWin32( p_intf
, (unsigned char*)p_buffer
, pi_size
);
619 else if( p_intf
->p_sys
->i_socket
== -1 )
621 vlc_tick_sleep( INTF_IDLE_SLEEP
);
626 while( *pi_size
< MAX_LINE_LENGTH
)
628 if( p_intf
->p_sys
->i_socket
== -1 )
630 if( read( 0/*STDIN_FILENO*/, p_buffer
+ *pi_size
, 1 ) <= 0 )
631 { /* Standard input closed: exit */
632 libvlc_Quit( vlc_object_instance(p_intf
) );
633 p_buffer
[*pi_size
] = 0;
638 { /* Connection closed */
639 if( net_Read( p_intf
, p_intf
->p_sys
->i_socket
, p_buffer
+ *pi_size
,
642 net_Close( p_intf
->p_sys
->i_socket
);
643 p_intf
->p_sys
->i_socket
= -1;
644 p_buffer
[*pi_size
] = 0;
649 if( p_buffer
[ *pi_size
] == '\r' || p_buffer
[ *pi_size
] == '\n' )
655 if( *pi_size
== MAX_LINE_LENGTH
||
656 p_buffer
[ *pi_size
] == '\r' || p_buffer
[ *pi_size
] == '\n' )
658 p_buffer
[ *pi_size
] = 0;
665 /*****************************************************************************
667 *****************************************************************************
668 * This part of the interface is in a separate thread so that we can call
669 * exec() from within it without annoying the rest of the program.
670 *****************************************************************************/
671 static void *Run( void *data
)
673 intf_thread_t
*p_intf
= data
;
674 intf_sys_t
*p_sys
= p_intf
->p_sys
;
676 char p_buffer
[ MAX_LINE_LENGTH
+ 1 ];
679 int canc
= vlc_savecancel( );
683 #if !VLC_WINSTORE_APP
684 /* Get the file descriptor of the console input */
685 p_intf
->p_sys
->hConsoleIn
= GetStdHandle(STD_INPUT_HANDLE
);
686 if( p_intf
->p_sys
->hConsoleIn
== INVALID_HANDLE_VALUE
)
688 msg_Err( p_intf
, "couldn't find user input handle" );
697 vlc_restorecancel( canc
);
699 if( p_sys
->pi_socket_listen
!= NULL
&& p_sys
->i_socket
== -1 )
702 net_Accept( p_intf
, p_sys
->pi_socket_listen
);
703 if( p_sys
->i_socket
== -1 ) continue;
706 b_complete
= ReadCommand( p_intf
, p_buffer
, &i_size
);
707 canc
= vlc_savecancel( );
709 /* Is there something to do? */
710 if( !b_complete
) continue;
712 struct cli_client cl
= { p_intf
};
714 Process(p_intf
, &cl
, p_buffer
);
716 /* Command processed */
717 i_size
= 0; p_buffer
[0] = 0;
720 vlc_assert_unreachable();
724 #define msg_rc(...) msg_print(p_intf, __VA_ARGS__)
725 #include "../intromsg.h"
728 /*****************************************************************************
729 * Activate: initialize and create stuff
730 *****************************************************************************/
731 static int Activate( vlc_object_t
*p_this
)
733 intf_thread_t
*p_intf
= (intf_thread_t
*)p_this
;
734 char *psz_host
, *psz_unix_path
= NULL
;
735 int *pi_socket
= NULL
;
738 #if defined(HAVE_ISATTY)
739 /* Check that stdin is a TTY */
740 if( !var_InheritBool( p_intf
, "rc-fake-tty" ) && !isatty( 0 ) )
742 msg_Warn( p_intf
, "fd 0 is not a TTY" );
747 psz_unix_path
= var_InheritString( p_intf
, "rc-unix" );
751 struct sockaddr_un addr
;
753 memset( &addr
, 0, sizeof(struct sockaddr_un
) );
755 msg_Dbg( p_intf
, "trying UNIX socket" );
757 /* The given unix path cannot be longer than sun_path - 1 to take into
758 * account the terminated null character. */
759 if ( strlen(psz_unix_path
) + 1 >= sizeof( addr
.sun_path
) )
761 msg_Err( p_intf
, "rc-unix value is longer than expected" );
765 if( (i_socket
= vlc_socket( PF_LOCAL
, SOCK_STREAM
, 0, false ) ) < 0 )
767 msg_Warn( p_intf
, "can't open socket: %s", vlc_strerror_c(errno
) );
768 free( psz_unix_path
);
772 addr
.sun_family
= AF_LOCAL
;
773 strncpy( addr
.sun_path
, psz_unix_path
, sizeof( addr
.sun_path
) - 1 );
774 addr
.sun_path
[sizeof( addr
.sun_path
) - 1] = '\0';
776 if (bind (i_socket
, (struct sockaddr
*)&addr
, sizeof (addr
))
777 && (errno
== EADDRINUSE
)
778 && connect (i_socket
, (struct sockaddr
*)&addr
, sizeof (addr
))
779 && (errno
== ECONNREFUSED
))
781 msg_Info (p_intf
, "Removing dead UNIX socket: %s", psz_unix_path
);
782 unlink (psz_unix_path
);
784 if (bind (i_socket
, (struct sockaddr
*)&addr
, sizeof (addr
)))
786 msg_Err (p_intf
, "cannot bind UNIX socket at %s: %s",
787 psz_unix_path
, vlc_strerror_c(errno
));
788 free (psz_unix_path
);
789 net_Close (i_socket
);
794 if( listen( i_socket
, 1 ) )
796 msg_Warn (p_intf
, "can't listen on socket: %s",
797 vlc_strerror_c(errno
));
798 free( psz_unix_path
);
799 net_Close( i_socket
);
803 /* FIXME: we need a core function to merge listening sockets sets */
804 pi_socket
= calloc( 2, sizeof( int ) );
805 if( pi_socket
== NULL
)
807 free( psz_unix_path
);
808 net_Close( i_socket
);
811 pi_socket
[0] = i_socket
;
814 #endif /* AF_LOCAL */
817 if( ( pi_socket
== NULL
) &&
818 ( psz_host
= var_InheritString( p_intf
, "rc-host" ) ) != NULL
)
822 vlc_UrlParse( &url
, psz_host
);
823 if( url
.psz_host
== NULL
)
825 vlc_UrlClean( &url
);
826 char *psz_backward_compat_host
;
827 if( asprintf( &psz_backward_compat_host
, "//%s", psz_host
) < 0 )
833 psz_host
= psz_backward_compat_host
;
834 vlc_UrlParse( &url
, psz_host
);
837 msg_Dbg( p_intf
, "base: %s, port: %d", url
.psz_host
, url
.i_port
);
839 pi_socket
= net_ListenTCP(p_this
, url
.psz_host
, url
.i_port
);
840 if( pi_socket
== NULL
)
842 msg_Warn( p_intf
, "can't listen to %s port %i",
843 url
.psz_host
, url
.i_port
);
844 vlc_UrlClean( &url
);
849 vlc_UrlClean( &url
);
853 intf_sys_t
*p_sys
= malloc( sizeof( *p_sys
) );
854 if( unlikely(p_sys
== NULL
) )
856 net_ListenClose( pi_socket
);
857 free( psz_unix_path
);
861 p_intf
->p_sys
= p_sys
;
862 p_sys
->commands
= NULL
;
863 p_sys
->pi_socket_listen
= pi_socket
;
865 RegisterHandlers(p_intf
, cmds
, ARRAY_SIZE(cmds
), p_intf
);
867 /* Line-buffered stdout */
868 setvbuf( stdout
, (char *)NULL
, _IOLBF
, 0 );
871 struct cli_client
*cl
= &p_sys
->client
;
873 vlc_mutex_init(&cl
->output_lock
);
875 if (pi_socket
== NULL
)
886 p_sys
->psz_unix_path
= psz_unix_path
;
888 p_sys
->i_socket
= -1;
890 p_sys
->b_quiet
= true;
892 p_sys
->b_quiet
= var_InheritBool( p_intf
, "rc-quiet" );
893 if( !p_sys
->b_quiet
)
894 intf_consoleIntroMsg( p_intf
);
898 p_sys
->player_cli
= RegisterPlayer(p_intf
);
899 if (unlikely(p_sys
->player_cli
== NULL
))
902 RegisterPlaylist(p_intf
);
904 if( vlc_clone( &p_sys
->thread
, Run
, p_intf
, VLC_THREAD_PRIORITY_LOW
) )
907 msg_print(p_intf
, "%s",
908 _("Remote control interface initialized. Type `help' for help."));
913 if (p_sys
->player_cli
!= NULL
)
914 DeregisterPlayer(p_intf
, p_sys
->player_cli
);
915 tdestroy(p_sys
->commands
, free
);
916 net_ListenClose( pi_socket
);
917 free( psz_unix_path
);
922 /*****************************************************************************
923 * Deactivate: uninitialize and cleanup
924 *****************************************************************************/
925 static void Deactivate( vlc_object_t
*p_this
)
927 intf_thread_t
*p_intf
= (intf_thread_t
*)p_this
;
928 intf_sys_t
*p_sys
= p_intf
->p_sys
;
930 vlc_cancel( p_sys
->thread
);
931 vlc_join( p_sys
->thread
, NULL
);
933 DeregisterPlayer(p_intf
, p_sys
->player_cli
);
934 tdestroy(p_sys
->commands
, free
);
936 if (p_sys
->pi_socket_listen
!= NULL
)
938 net_ListenClose(p_sys
->pi_socket_listen
);
940 struct cli_client
*cl
= &p_sys
->client
;
942 if (cl
->stream
!= NULL
)
944 if (p_sys
->psz_unix_path
!= NULL
)
946 unlink(p_sys
->psz_unix_path
);
947 free(p_sys
->psz_unix_path
);
950 if (p_sys
->i_socket
!= -1)
951 net_Close(p_sys
->i_socket
);
957 /*****************************************************************************
959 *****************************************************************************/
960 #define POS_TEXT N_("Show stream position")
961 #define POS_LONGTEXT N_("Show the current position in seconds within the " \
962 "stream from time to time." )
964 #define TTY_TEXT N_("Fake TTY")
965 #define TTY_LONGTEXT N_("Force the rc module to use stdin as if it was a TTY.")
967 #define UNIX_TEXT N_("UNIX socket command input")
968 #define UNIX_LONGTEXT N_("Accept commands over a Unix socket rather than " \
971 #define HOST_TEXT N_("TCP command input")
972 #define HOST_LONGTEXT N_("Accept commands over a socket rather than stdin. " \
973 "You can set the address and port the interface will bind to." )
976 #define QUIET_TEXT N_("Do not open a DOS command box interface")
977 #define QUIET_LONGTEXT N_( \
978 "By default the rc interface plugin will start a DOS command box. " \
979 "Enabling the quiet mode will not bring this command box but can also " \
980 "be pretty annoying when you want to stop VLC and no video window is " \
985 set_shortname(N_("RC"))
986 set_category(CAT_INTERFACE
)
987 set_subcategory(SUBCAT_INTERFACE_MAIN
)
988 set_description(N_("Remote control interface"))
989 add_bool("rc-show-pos", false, POS_TEXT
, POS_LONGTEXT
, true)
992 add_bool("rc-quiet", false, QUIET_TEXT
, QUIET_LONGTEXT
, false)
994 #if defined (HAVE_ISATTY)
995 add_bool("rc-fake-tty", false, TTY_TEXT
, TTY_LONGTEXT
, true)
998 add_string("rc-unix", NULL
, UNIX_TEXT
, UNIX_LONGTEXT
, true)
1001 add_string("rc-host", NULL
, HOST_TEXT
, HOST_LONGTEXT
, true)
1003 set_capability("interface", 20)
1005 set_callbacks(Activate
, Deactivate
)
1006 add_shortcut("cli", "rc", "oldrc")