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