1 /*****************************************************************************
2  * interface.c: interface access for other threads
3  * This library provides basic functions for threads to interact with user
4  * interface, such as command line.
5  *****************************************************************************
6  * Copyright (C) 1998-2007 VLC authors and VideoLAN
7  * $Id: 6155d5ab7c403055e5c01c5973a6a0192a22e816 $
8  *
9  * Authors: Vincent Seguin <seguin@via.ecp.fr>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25 
26 /**
27  *   \file
28  *   This file contains functions related to interface management
29  */
30 
31 
32 /*****************************************************************************
33  * Preamble
34  *****************************************************************************/
35 
36 #ifdef HAVE_CONFIG_H
37 # include "config.h"
38 #endif
39 
40 #include <unistd.h>
41 
42 #include <vlc_common.h>
43 #include <vlc_modules.h>
44 #include <vlc_interface.h>
45 #include <vlc_playlist.h>
46 #include "libvlc.h"
47 #include "playlist/playlist_internal.h"
48 #include "../lib/libvlc_internal.h"
49 
50 static int AddIntfCallback( vlc_object_t *, char const *,
51                             vlc_value_t , vlc_value_t , void * );
52 
53 /* This lock ensures that the playlist is created only once (per instance). It
54  * also protects the list of running interfaces against concurrent access,
55  * either to add or remove an interface.
56  *
57  * However, it does NOT protect from destruction of the playlist by
58  * intf_DestroyAll(). Instead, care must be taken that intf_Create() and any
59  * other function that depends on the playlist is only called BEFORE
60  * intf_DestroyAll() has the possibility to destroy all interfaces.
61  */
62 static vlc_mutex_t lock = VLC_STATIC_MUTEX;
63 
64 /**
65  * Create and start an interface.
66  *
67  * @param playlist playlist and parent object for the interface
68  * @param chain configuration chain string
69  * @return VLC_SUCCESS or an error code
70  */
intf_Create(playlist_t * playlist,const char * chain)71 int intf_Create( playlist_t *playlist, const char *chain )
72 {
73     /* Allocate structure */
74     intf_thread_t *p_intf = vlc_custom_create( playlist, sizeof( *p_intf ),
75                                                "interface" );
76     if( unlikely(p_intf == NULL) )
77         return VLC_ENOMEM;
78 
79     /* Variable used for interface spawning */
80     vlc_value_t val, text;
81     var_Create( p_intf, "intf-add", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
82     text.psz_string = _("Add Interface");
83     var_Change( p_intf, "intf-add", VLC_VAR_SETTEXT, &text, NULL );
84 #if !defined(_WIN32) && defined(HAVE_ISATTY)
85     if( isatty( 0 ) )
86 #endif
87     {
88         val.psz_string = (char *)"rc,none";
89         text.psz_string = (char *)_("Console");
90         var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
91     }
92     val.psz_string = (char *)"telnet,none";
93     text.psz_string = (char *)_("Telnet");
94     var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
95     val.psz_string = (char *)"http,none";
96     text.psz_string = (char *)_("Web");
97     var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
98     val.psz_string = (char *)"gestures,none";
99     text.psz_string = (char *)_("Mouse Gestures");
100     var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
101 
102     var_AddCallback( p_intf, "intf-add", AddIntfCallback, playlist );
103 
104     /* Choose the best module */
105     char *module;
106 
107     p_intf->p_cfg = NULL;
108     free( config_ChainCreate( &module, &p_intf->p_cfg, chain ) );
109     p_intf->p_module = module_need( p_intf, "interface", module, true );
110     free(module);
111     if( p_intf->p_module == NULL )
112     {
113         msg_Err( p_intf, "no suitable interface module" );
114         goto error;
115     }
116 
117     vlc_mutex_lock( &lock );
118     p_intf->p_next = pl_priv( playlist )->interface;
119     pl_priv( playlist )->interface = p_intf;
120     vlc_mutex_unlock( &lock );
121 
122     return VLC_SUCCESS;
123 
124 error:
125     if( p_intf->p_module )
126         module_unneed( p_intf, p_intf->p_module );
127     config_ChainDestroy( p_intf->p_cfg );
128     vlc_object_release( p_intf );
129     return VLC_EGENERIC;
130 }
131 
132 /**
133  * Creates the playlist if necessary, and return a pointer to it.
134  * @note The playlist is not reference-counted. So the pointer is only valid
135  * until intf_DestroyAll() destroys interfaces.
136  */
intf_GetPlaylist(libvlc_int_t * libvlc)137 static playlist_t *intf_GetPlaylist(libvlc_int_t *libvlc)
138 {
139     playlist_t *playlist;
140 
141     vlc_mutex_lock(&lock);
142     playlist = libvlc_priv(libvlc)->playlist;
143     if (playlist == NULL)
144     {
145         playlist = playlist_Create(VLC_OBJECT(libvlc));
146         libvlc_priv(libvlc)->playlist = playlist;
147     }
148     vlc_mutex_unlock(&lock);
149 
150     return playlist;
151 }
152 
153 /**
154  * Inserts an item in the playlist.
155  *
156  * This function is used during initialization. Unlike playlist_Add() and
157  * variants, it inserts an item to the beginning of the playlist. That is
158  * meant to compensate for the reverse parsing order of the command line.
159  *
160  * @note This function may <b>not</b> be called at the same time as
161  * intf_DestroyAll().
162  */
intf_InsertItem(libvlc_int_t * libvlc,const char * mrl,unsigned optc,const char * const * optv,unsigned flags)163 int intf_InsertItem(libvlc_int_t *libvlc, const char *mrl, unsigned optc,
164                     const char *const *optv, unsigned flags)
165 {
166     playlist_t *playlist = intf_GetPlaylist(libvlc);
167     input_item_t *item = input_item_New(mrl, NULL);
168 
169     if (unlikely(item == NULL))
170         return -1;
171 
172     int ret = -1;
173 
174     if (input_item_AddOptions(item, optc, optv, flags) == VLC_SUCCESS)
175     {
176         playlist_Lock(playlist);
177         if (playlist_NodeAddInput(playlist, item, playlist->p_playing,
178                                   0) != NULL)
179             ret = 0;
180         playlist_Unlock(playlist);
181     }
182     input_item_Release(item);
183     return ret;
184 }
185 
libvlc_InternalPlay(libvlc_int_t * libvlc)186 void libvlc_InternalPlay(libvlc_int_t *libvlc)
187 {
188     playlist_t *pl;
189 
190     vlc_mutex_lock(&lock);
191     pl = libvlc_priv(libvlc)->playlist;
192     vlc_mutex_unlock(&lock);
193 
194     if (pl != NULL && var_GetBool(pl, "playlist-autostart"))
195         playlist_Control(pl, PLAYLIST_PLAY, false);
196 }
197 
198 /**
199  * Starts an interface plugin.
200  */
libvlc_InternalAddIntf(libvlc_int_t * libvlc,const char * name)201 int libvlc_InternalAddIntf(libvlc_int_t *libvlc, const char *name)
202 {
203     playlist_t *playlist = intf_GetPlaylist(libvlc);
204     int ret;
205 
206     if (unlikely(playlist == NULL))
207         ret = VLC_ENOMEM;
208     else
209     if (name != NULL)
210         ret = intf_Create(playlist, name);
211     else
212     {   /* Default interface */
213         char *intf = var_InheritString(libvlc, "intf");
214         if (intf == NULL) /* "intf" has not been set */
215         {
216 #if !defined(_WIN32) && !defined(__OS2__)
217             char *pidfile = var_InheritString(libvlc, "pidfile");
218             if (pidfile != NULL)
219                 free(pidfile);
220             else
221 #endif
222                 msg_Info(libvlc, _("Running vlc with the default interface. "
223                          "Use 'cvlc' to use vlc without interface."));
224         }
225         ret = intf_Create(playlist, intf);
226         free(intf);
227         name = "default";
228     }
229     if (ret != VLC_SUCCESS)
230         msg_Err(libvlc, "interface \"%s\" initialization failed", name);
231     return ret;
232 }
233 
234 /**
235  * Stops and destroys all interfaces, then the playlist.
236  * @warning FIXME
237  * @param libvlc the LibVLC instance
238  */
intf_DestroyAll(libvlc_int_t * libvlc)239 void intf_DestroyAll(libvlc_int_t *libvlc)
240 {
241     playlist_t *playlist;
242 
243     vlc_mutex_lock(&lock);
244     playlist = libvlc_priv(libvlc)->playlist;
245     if (playlist != NULL)
246     {
247         intf_thread_t *intf, **pp = &(pl_priv(playlist)->interface);
248 
249         while ((intf = *pp) != NULL)
250         {
251             *pp = intf->p_next;
252             vlc_mutex_unlock(&lock);
253 
254             module_unneed(intf, intf->p_module);
255             config_ChainDestroy(intf->p_cfg);
256             var_DelCallback(intf, "intf-add", AddIntfCallback, playlist);
257             vlc_object_release(intf);
258 
259             vlc_mutex_lock(&lock);
260         }
261 
262         libvlc_priv(libvlc)->playlist = NULL;
263     }
264     vlc_mutex_unlock(&lock);
265 
266     if (playlist != NULL)
267         playlist_Destroy(playlist);
268 }
269 
270 /* Following functions are local */
271 
AddIntfCallback(vlc_object_t * obj,char const * var,vlc_value_t old,vlc_value_t cur,void * data)272 static int AddIntfCallback( vlc_object_t *obj, char const *var,
273                             vlc_value_t old, vlc_value_t cur, void *data )
274 {
275     playlist_t *playlist = data;
276 
277     int ret = intf_Create( playlist, cur.psz_string );
278     if( ret )
279         msg_Err( obj, "interface \"%s\" initialization failed",
280                  cur.psz_string );
281 
282     (void) var; (void) old;
283     return ret;
284 }
285