]> git.sesse.net Git - vlc/blob - src/interface/interface.c
* OSX lowlevel work
[vlc] / src / interface / interface.c
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-2004 VideoLAN
7  * $Id$
8  *
9  * Authors: Vincent Seguin <seguin@via.ecp.fr>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 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 General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /**
27  *   \file
28  *   This file contains functions related to interface management
29  */
30
31
32 /*****************************************************************************
33  * Preamble
34  *****************************************************************************/
35 #include <stdlib.h>                                      /* free(), strtol() */
36 #include <stdio.h>                                                   /* FILE */
37 #include <string.h>                                            /* strerror() */
38
39 #include <vlc/vlc.h>
40 #include <vlc/input.h>
41
42 #include "audio_output.h"
43
44 #include "vlc_interface.h"
45 #include "vlc_video.h"
46 #include "video_output.h"
47
48 #ifdef SYS_DARWIN
49 #    include "Cocoa/Cocoa.h"
50 #endif /* SYS_DARWIN */
51
52 /*****************************************************************************
53  * Local prototypes
54  *****************************************************************************/
55 static void Manager( intf_thread_t *p_intf );
56 static void RunInterface( intf_thread_t *p_intf );
57
58 static int SwitchIntfCallback( vlc_object_t *, char const *,
59                                vlc_value_t , vlc_value_t , void * );
60 static int AddIntfCallback( vlc_object_t *, char const *,
61                             vlc_value_t , vlc_value_t , void * );
62
63 /*****************************************************************************
64  * intf_Create: prepare interface before main loop
65  *****************************************************************************
66  * This function opens output devices and creates specific interfaces. It sends
67  * its own error messages.
68  *****************************************************************************/
69 /**
70  * Create the interface, and prepare it for main loop.
71  *
72  * \param p_this the calling vlc_object_t
73  * \param psz_module a prefered interface module
74  * \return a pointer to the created interface thread, NULL on error
75  */
76 intf_thread_t* __intf_Create( vlc_object_t *p_this, const char *psz_module )
77 {
78     intf_thread_t * p_intf;
79
80     /* Allocate structure */
81     p_intf = vlc_object_create( p_this, VLC_OBJECT_INTF );
82     if( !p_intf )
83     {
84         msg_Err( p_this, "out of memory" );
85         return NULL;
86     }
87     p_intf->pf_request_window = NULL;
88     p_intf->pf_release_window = NULL;
89     p_intf->pf_control_window = NULL;
90     p_intf->b_play = VLC_FALSE;
91
92     /* Choose the best module */
93     p_intf->p_module = module_Need( p_intf, "interface", psz_module, 0 );
94
95     if( p_intf->p_module == NULL )
96     {
97         msg_Err( p_intf, "no suitable intf module" );
98         vlc_object_destroy( p_intf );
99         return NULL;
100     }
101
102     /* Initialize structure */
103     p_intf->b_menu        = VLC_FALSE;
104     p_intf->b_menu_change = VLC_FALSE;
105
106     /* Initialize mutexes */
107     vlc_mutex_init( p_intf, &p_intf->change_lock );
108
109     msg_Dbg( p_intf, "interface initialized" );
110
111     /* Attach interface to its parent object */
112     vlc_object_attach( p_intf, p_this );
113
114     return p_intf;
115 }
116
117 /*****************************************************************************
118  * intf_RunThread: launch the interface thread
119  *****************************************************************************
120  * This function either creates a new thread and runs the interface in it,
121  * or runs the interface in the current thread, depending on b_block.
122  *****************************************************************************/
123 /**
124  * Run the interface thread.
125  *
126  * If b_block is not set, runs the interface in the thread, else,
127  * creates a new thread and runs the interface.
128  * \param p_intf the interface thread
129  * \return VLC_SUCCESS on success, an error number else
130  */
131 int intf_RunThread( intf_thread_t *p_intf )
132 {
133 #ifdef SYS_DARWIN
134     if( p_intf->b_block )
135     {
136         /* This is the primary intf */
137         /* Run a manager thread, launch the interface, kill the manager */
138         if( vlc_thread_create( p_intf, "manager", Manager,
139                                VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
140         {
141             msg_Err( p_intf, "cannot spawn manager thread" );
142             return VLC_EGENERIC;
143         }
144     }
145
146     if( p_intf->b_block && !strncmp( p_intf->p_module->psz_shortname, "macosx" , 6 ) )
147     {
148         /* this is OSX, we are cheating :)
149            This is NOT I REPEAT NOT blocking since [NSApp run] is */
150         p_intf->b_block = VLC_FALSE;
151
152         RunInterface( p_intf );
153         p_intf->b_block = VLC_TRUE;
154     }
155     else if( p_intf->b_block && !strncmp( p_intf->p_vlc->psz_object_name, "clivlc", 6 ) )
156     {
157         /* VLC OS X in cli mode ( no blocking [NSApp run] )
158            this is equal to running in normal non-OSX primary intf mode */
159         RunInterface( p_intf );
160         p_intf->b_die = VLC_TRUE;
161     }
162     else
163     {
164         /* If anything else is the primary intf and we are not in cli mode,
165            then don't make it blocking ([NSApp run] will be blocking) 
166            but run it in a seperate thread. */
167         p_intf->b_block = VLC_FALSE;
168 #else
169     if( p_intf->b_block )
170     {
171         /* Run a manager thread, launch the interface, kill the manager */
172         if( vlc_thread_create( p_intf, "manager", Manager,
173                                VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
174         {
175             msg_Err( p_intf, "cannot spawn manager thread" );
176             return VLC_EGENERIC;
177         }
178
179         RunInterface( p_intf );
180
181         p_intf->b_die = VLC_TRUE;
182         /* Do not join the thread... intf_StopThread will do it for us */
183     }
184     else
185     {
186 #endif
187         /* Run the interface in a separate thread */
188         if( vlc_thread_create( p_intf, "interface", RunInterface,
189                                VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
190         {
191             msg_Err( p_intf, "cannot spawn interface thread" );
192             return VLC_EGENERIC;
193         }
194     }
195
196     return VLC_SUCCESS;
197 }
198
199 /**
200  * Stops the interface thread
201  *
202  * This function asks the interface thread to stop
203  * \param p_intf the interface thread
204  * \return nothing
205  */
206 void intf_StopThread( intf_thread_t *p_intf )
207 {
208     /* Tell the interface to die */
209     if( !p_intf->b_block )
210     {
211         p_intf->b_die = VLC_TRUE;
212     }
213
214     /* Wait for the thread to exit */
215     vlc_thread_join( p_intf );
216 }
217
218 /**
219  * \brief Destroy the interface after the main loop endeed.
220  *
221  * Destroys interfaces and closes output devices
222  * \param p_intf the interface thread
223  * \return nothing
224  */
225 void intf_Destroy( intf_thread_t *p_intf )
226 {
227     /* Unlock module if present (a switch may have failed) */
228     if( p_intf->p_module )
229     {
230         module_Unneed( p_intf, p_intf->p_module );
231     }
232
233     vlc_mutex_destroy( &p_intf->change_lock );
234
235     /* Free structure */
236     vlc_object_destroy( p_intf );
237 }
238
239
240 /* Following functions are local */
241
242 /*****************************************************************************
243  * Manager: helper thread for blocking interfaces
244  *****************************************************************************
245  * If the interface is launched in the main thread, it will not listen to
246  * p_vlc->b_die events because it is only supposed to listen to p_intf->b_die.
247  * This thread takes care of the matter.
248  *****************************************************************************/
249 /**
250  * \brief Helper thread for blocking interfaces.
251  * \ingroup vlc_interface
252  *
253  * This is a local function
254  * If the interface is launched in the main thread, it will not listen to
255  * p_vlc->b_die events because it is only supposed to listen to p_intf->b_die.
256  * This thread takes care of the matter.
257  * \see intf_RunThread
258  * \param p_intf an interface thread
259  * \return nothing
260  */
261 static void Manager( intf_thread_t *p_intf )
262 {
263     while( !p_intf->b_die )
264     {
265         msleep( INTF_IDLE_SLEEP );
266
267         if( p_intf->p_vlc->b_die )
268         {
269             p_intf->b_die = VLC_TRUE;
270 #ifdef SYS_DARWIN
271     if( strncmp( p_intf->p_vlc->psz_object_name, "clivlc", 6 ) )
272     {
273         [NSApp stop: NULL];
274     }
275 #endif
276             return;
277         }
278     }
279 }
280
281 /*****************************************************************************
282  * RunInterface: setups necessary data and give control to the interface
283  *****************************************************************************/
284 static void RunInterface( intf_thread_t *p_intf )
285 {
286     static char *ppsz_interfaces[] =
287     {
288         "skins", "Skins",
289         "skins2", "Skins 2",
290         "wxwindows", "wxWindows",
291         NULL, NULL
292     };
293     char **ppsz_parser;
294
295     vlc_list_t *p_list;
296     int i;
297     vlc_value_t val, text;
298     char *psz_intf;
299
300     /* Variable used for interface switching */
301     p_intf->psz_switch_intf = NULL;
302     var_Create( p_intf, "intf-switch", VLC_VAR_STRING |
303                 VLC_VAR_HASCHOICE | VLC_VAR_ISCOMMAND );
304     text.psz_string = _("Switch interface");
305     var_Change( p_intf, "intf-switch", VLC_VAR_SETTEXT, &text, NULL );
306
307     /* Only fill the list with available modules */
308     p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
309     for( ppsz_parser = ppsz_interfaces; *ppsz_parser; ppsz_parser += 2 )
310     {
311         for( i = 0; i < p_list->i_count; i++ )
312         {
313             module_t *p_module = (module_t *)p_list->p_values[i].p_object;
314             if( !strcmp( p_module->psz_object_name, ppsz_parser[0] ) )
315             {
316                 val.psz_string = ppsz_parser[0];
317                 text.psz_string = ppsz_parser[1];
318                 var_Change( p_intf, "intf-switch", VLC_VAR_ADDCHOICE,
319                             &val, &text );
320                 break;
321             }
322         }
323     }
324     vlc_list_release( p_list );
325
326     var_AddCallback( p_intf, "intf-switch", SwitchIntfCallback, NULL );
327
328     /* Variable used for interface spawning */
329     var_Create( p_intf, "intf-add", VLC_VAR_STRING |
330                 VLC_VAR_HASCHOICE | VLC_VAR_ISCOMMAND );
331     text.psz_string = _("Add Interface");
332     var_Change( p_intf, "intf-add", VLC_VAR_SETTEXT, &text, NULL );
333
334     val.psz_string = "rc"; text.psz_string = "Console";
335     var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
336     val.psz_string = "telnet"; text.psz_string = "Telnet Interface";
337     var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
338     val.psz_string = "http"; text.psz_string = "Web Interface";
339     var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
340     val.psz_string = "logger"; text.psz_string = "Debug logging";
341     var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
342     val.psz_string = "sap"; text.psz_string = "SAP Playlist";
343     var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
344     val.psz_string = "gestures"; text.psz_string = "Mouse Gestures";
345     var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
346
347     var_AddCallback( p_intf, "intf-add", AddIntfCallback, NULL );
348
349     do
350     {
351         /* Give control to the interface */
352         p_intf->pf_run( p_intf );
353
354         /* Reset play on start status */
355         p_intf->b_play = VLC_FALSE;
356
357         if( !p_intf->psz_switch_intf )
358         {
359             break;
360         }
361
362         /* Make sure the old interface is completely uninitialized */
363         module_Unneed( p_intf, p_intf->p_module );
364
365         /* Provide ability to switch the main interface on the fly */
366         psz_intf = p_intf->psz_switch_intf;
367         p_intf->psz_switch_intf = NULL;
368
369         vlc_mutex_lock( &p_intf->object_lock );
370         p_intf->b_die = VLC_FALSE;
371         p_intf->b_dead = VLC_FALSE;
372         vlc_mutex_unlock( &p_intf->object_lock );
373
374         p_intf->p_module = module_Need( p_intf, "interface", psz_intf, 0 );
375         free( psz_intf );
376     }
377     while( p_intf->p_module );
378 }
379
380 static int SwitchIntfCallback( vlc_object_t *p_this, char const *psz_cmd,
381                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
382 {
383     intf_thread_t *p_intf = (intf_thread_t *)p_this;
384
385     p_intf->psz_switch_intf =
386         malloc( strlen(newval.psz_string) + sizeof(",none") );
387     sprintf( p_intf->psz_switch_intf, "%s,none", newval.psz_string );
388     p_intf->b_die = VLC_TRUE;
389
390     return VLC_SUCCESS;
391 }
392
393 static int AddIntfCallback( vlc_object_t *p_this, char const *psz_cmd,
394                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
395 {
396     intf_thread_t *p_intf;
397     char *psz_intf = malloc( strlen(newval.psz_string) + sizeof(",none") );
398
399     /* Try to create the interface */
400     sprintf( psz_intf, "%s,none", newval.psz_string );
401     p_intf = intf_Create( p_this->p_vlc, psz_intf );
402     free( psz_intf );
403     if( p_intf == NULL )
404     {
405         msg_Err( p_this, "interface \"%s\" initialization failed",
406                  newval.psz_string );
407         return VLC_EGENERIC;
408     }
409
410     /* Try to run the interface */
411     p_intf->b_block = VLC_FALSE;
412     if( intf_RunThread( p_intf ) != VLC_SUCCESS )
413     {
414         vlc_object_detach( p_intf );
415         intf_Destroy( p_intf );
416         return VLC_EGENERIC;
417     }
418
419     return VLC_SUCCESS;
420 }