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