interface.c 15.8 KB
Newer Older
1
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
2
3
4
 * interface.c: interface access for other threads
 * This library provides basic functions for threads to interact with user
 * interface, such as command line.
5
 *****************************************************************************
6
 * Copyright (C) 1998-2004 the VideoLAN team
gbazin's avatar
gbazin committed
7
 * $Id$
8
 *
Sam Hocevar's avatar
   
Sam Hocevar committed
9
 * Authors: Vincent Seguin <seguin@via.ecp.fr>
10
11
12
13
14
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
zorglub's avatar
zorglub committed
15
 *
16
17
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
20
 *
21
22
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24
 *****************************************************************************/
Michel Kaempf's avatar
Michel Kaempf committed
25

zorglub's avatar
zorglub committed
26
27
28
29
30
31
/**
 *   \file
 *   This file contains functions related to interface management
 */


32
/*****************************************************************************
Michel Kaempf's avatar
Michel Kaempf committed
33
 * Preamble
34
 *****************************************************************************/
35
#include <stdlib.h>                                      /* free(), strtol() */
36
#include <stdio.h>                                                   /* FILE */
37
#include <string.h>                                            /* strerror() */
Christophe Massiot's avatar
Christophe Massiot committed
38

39
#include <vlc/vlc.h>
Laurent Aimar's avatar
Laurent Aimar committed
40
#include <vlc/input.h>
41

Renaud Dartus's avatar
   
Renaud Dartus committed
42
43
#include "audio_output.h"

44
#include "vlc_interface.h"
45
#include "vlc_video.h"
Vincent Seguin's avatar
Vincent Seguin committed
46
#include "video_output.h"
Michel Kaempf's avatar
Michel Kaempf committed
47

48
49
50
#ifdef __APPLE__
#    include <Cocoa/Cocoa.h>
#endif
hartman's avatar
hartman committed
51

52
/*****************************************************************************
53
 * Local prototypes
54
 *****************************************************************************/
55
static void Manager( intf_thread_t *p_intf );
gbazin's avatar
   
gbazin committed
56
57
58
59
60
61
static void RunInterface( intf_thread_t *p_intf );

static int SwitchIntfCallback( vlc_object_t *, char const *,
                               vlc_value_t , vlc_value_t , void * );
static int AddIntfCallback( vlc_object_t *, char const *,
                            vlc_value_t , vlc_value_t , void * );
62

63
#ifdef __APPLE__
hartman's avatar
-    
hartman committed
64
65
66
67
68
/*****************************************************************************
 * VLCApplication interface
 *****************************************************************************/
@interface VLCApplication : NSApplication
{
zorglub's avatar
zorglub committed
69
   libvlc_int_t *o_libvlc;
hartman's avatar
-    
hartman committed
70
71
}

zorglub's avatar
zorglub committed
72
- (void)setVLC: (libvlc_int_t *)p_libvlc;
73

hartman's avatar
-    
hartman committed
74
75
76
@end
#endif

77
/*****************************************************************************
Vincent Seguin's avatar
Vincent Seguin committed
78
 * intf_Create: prepare interface before main loop
79
 *****************************************************************************
80
81
 * This function opens output devices and creates specific interfaces. It sends
 * its own error messages.
82
 *****************************************************************************/
zorglub's avatar
zorglub committed
83
84
/**
 * Create the interface, and prepare it for main loop.
85
 * You can give some additional options to be used for interface initialization
zorglub's avatar
zorglub committed
86
 *
zorglub's avatar
zorglub committed
87
88
 * \param p_this the calling vlc_object_t
 * \param psz_module a prefered interface module
89
90
 * \param i_options number additional options
 * \param ppsz_options additional option strings
zorglub's avatar
zorglub committed
91
92
 * \return a pointer to the created interface thread, NULL on error
 */
93
94
intf_thread_t* __intf_Create( vlc_object_t *p_this, const char *psz_module,
                              int i_options, char **ppsz_options  )
95
{
96
    intf_thread_t * p_intf;
97
    int i;
Michel Kaempf's avatar
Michel Kaempf committed
98

Vincent Seguin's avatar
Vincent Seguin committed
99
    /* Allocate structure */
100
    p_intf = vlc_object_create( p_this, VLC_OBJECT_INTF );
Vincent Seguin's avatar
Vincent Seguin committed
101
102
    if( !p_intf )
    {
103
104
        msg_Err( p_this, "out of memory" );
        return NULL;
105
    }
gbazin's avatar
gbazin committed
106
107
    p_intf->pf_request_window = NULL;
    p_intf->pf_release_window = NULL;
108
    p_intf->pf_control_window = NULL;
109
    p_intf->b_play = VLC_FALSE;
110
    p_intf->b_interaction = VLC_FALSE;
111

112
113
114
115
116
    for( i = 0 ; i< i_options; i++ )
    {
        var_OptionParse( p_intf, ppsz_options[i] );
    }

Sam Hocevar's avatar
   
Sam Hocevar committed
117
    /* Choose the best module */
118
    p_intf->p_module = module_Need( p_intf, "interface", psz_module, VLC_FALSE );
119

Sam Hocevar's avatar
   
Sam Hocevar committed
120
    if( p_intf->p_module == NULL )
121
    {
zorglub's avatar
zorglub committed
122
        msg_Err( p_intf, "no suitable interface module" );
123
124
        vlc_object_destroy( p_intf );
        return NULL;
125
    }
126

127
    /* Initialize structure */
Sam Hocevar's avatar
Sam Hocevar committed
128
129
    p_intf->b_menu        = VLC_FALSE;
    p_intf->b_menu_change = VLC_FALSE;
130

Sam Hocevar's avatar
   
Sam Hocevar committed
131
    /* Initialize mutexes */
132
133
    vlc_mutex_init( p_intf, &p_intf->change_lock );

Sam Hocevar's avatar
Sam Hocevar committed
134
135
    /* Attach interface to its parent object */
    vlc_object_attach( p_intf, p_this );
136
137

    return p_intf;
138
139
140
}

/*****************************************************************************
141
 * intf_RunThread: launch the interface thread
142
 *****************************************************************************
143
144
 * This function either creates a new thread and runs the interface in it,
 * or runs the interface in the current thread, depending on b_block.
145
 *****************************************************************************/
zorglub's avatar
zorglub committed
146
147
148
/**
 * Run the interface thread.
 *
zorglub's avatar
zorglub committed
149
 * If b_block is not set, runs the interface in the thread, else,
zorglub's avatar
zorglub committed
150
151
152
153
 * creates a new thread and runs the interface.
 * \param p_intf the interface thread
 * \return VLC_SUCCESS on success, an error number else
 */
Sam Hocevar's avatar
Sam Hocevar committed
154
int intf_RunThread( intf_thread_t *p_intf )
155
{
156
#ifdef __APPLE__
hartman's avatar
-    
hartman committed
157
158
    NSAutoreleasePool * o_pool;

159
    if( p_intf->b_block )
Sam Hocevar's avatar
   
Sam Hocevar committed
160
    {
hartman's avatar
hartman committed
161
        /* This is the primary intf */
162
        /* Run a manager thread, launch the interface, kill the manager */
zorglub's avatar
zorglub committed
163
        if( vlc_thread_create( p_intf, "manage", Manager,
164
                               VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
Sam Hocevar's avatar
   
Sam Hocevar committed
165
        {
166
167
            msg_Err( p_intf, "cannot spawn manager thread" );
            return VLC_EGENERIC;
Sam Hocevar's avatar
   
Sam Hocevar committed
168
        }
hartman's avatar
hartman committed
169
    }
Sam Hocevar's avatar
   
Sam Hocevar committed
170

171
    if( p_intf->b_block && strncmp( p_intf->p_libvlc->psz_object_name,
zorglub's avatar
zorglub committed
172
                                    "clivlc", 6) )
hartman's avatar
hartman committed
173
    {
hartman's avatar
-    
hartman committed
174
        o_pool = [[NSAutoreleasePool alloc] init];
zorglub's avatar
zorglub committed
175
        [VLCApplication sharedApplication];
176
        [NSApp setVLC: p_intf->p_libvlc];
hartman's avatar
hartman committed
177
    }
hartman's avatar
-    
hartman committed
178

gbazin's avatar
gbazin committed
179
180
    if( p_intf->b_block &&
        ( !strncmp( p_intf->p_module->psz_object_name, "macosx" , 6 ) ||
181
          !strncmp( p_intf->p_libvlc->psz_object_name, "clivlc", 6 ) ) )
hartman's avatar
hartman committed
182
    {
hartman's avatar
-    
hartman committed
183
        /* VLC in normal primary interface mode */
hartman's avatar
hartman committed
184
        RunInterface( p_intf );
Sam Hocevar's avatar
Sam Hocevar committed
185
        p_intf->b_die = VLC_TRUE;
hartman's avatar
hartman committed
186
187
188
    }
    else
    {
hartman's avatar
-    
hartman committed
189
        /* Run the interface in a separate thread */
190
191
        if( !strcmp( p_intf->p_module->psz_object_name, "macosx" ) )
        {
zorglub's avatar
zorglub committed
192
193
            msg_Err( p_intf, "You cannot run the MacOS X module as an "
                             "extra interface. Please read the "
194
                             "README.MacOSX.rtf file.");
195
196
            return VLC_EGENERIC;
        }
hartman's avatar
-    
hartman committed
197
198
199
200
201
202
203
204
205
206
207
208
209
210
        if( vlc_thread_create( p_intf, "interface", RunInterface,
                               VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
        {
            msg_Err( p_intf, "cannot spawn interface thread" );
            return VLC_EGENERIC;
        }

        if( p_intf->b_block )
        {
            /* VLC in primary interface mode with a working macosx vout */
            [NSApp run];
            p_intf->b_die = VLC_TRUE;
        }
    }
hartman's avatar
hartman committed
211
212
213
214
215
216
217
218
219
220
221
222
#else
    if( p_intf->b_block )
    {
        /* Run a manager thread, launch the interface, kill the manager */
        if( vlc_thread_create( p_intf, "manager", Manager,
                               VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
        {
            msg_Err( p_intf, "cannot spawn manager thread" );
            return VLC_EGENERIC;
        }

        RunInterface( p_intf );
223

hartman's avatar
hartman committed
224
        p_intf->b_die = VLC_TRUE;
225
        /* Do not join the thread... intf_StopThread will do it for us */
Sam Hocevar's avatar
   
Sam Hocevar committed
226
227
    }
    else
228
    {
229
        /* Run the interface in a separate thread */
gbazin's avatar
   
gbazin committed
230
        if( vlc_thread_create( p_intf, "interface", RunInterface,
231
                               VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) )
232
        {
233
234
            msg_Err( p_intf, "cannot spawn interface thread" );
            return VLC_EGENERIC;
Sam Hocevar's avatar
   
Sam Hocevar committed
235
        }
236
    }
hartman's avatar
-    
hartman committed
237
#endif
Stéphane Borel's avatar
Stéphane Borel committed
238

239
240
241
    return VLC_SUCCESS;
}

zorglub's avatar
zorglub committed
242
243
244
245
246
247
248
/**
 * Stops the interface thread
 *
 * This function asks the interface thread to stop
 * \param p_intf the interface thread
 * \return nothing
 */
249
250
251
252
253
void intf_StopThread( intf_thread_t *p_intf )
{
    /* Tell the interface to die */
    if( !p_intf->b_block )
    {
Sam Hocevar's avatar
Sam Hocevar committed
254
        p_intf->b_die = VLC_TRUE;
255
    }
256
257
258

    /* Wait for the thread to exit */
    vlc_thread_join( p_intf );
259
}
260

zorglub's avatar
zorglub committed
261
262
/**
 * \brief Destroy the interface after the main loop endeed.
zorglub's avatar
zorglub committed
263
264
 *
 * Destroys interfaces and closes output devices
zorglub's avatar
zorglub committed
265
266
267
 * \param p_intf the interface thread
 * \return nothing
 */
268
269
void intf_Destroy( intf_thread_t *p_intf )
{
Sam Hocevar's avatar
Sam Hocevar committed
270
271
272
273
274
    /* Unlock module if present (a switch may have failed) */
    if( p_intf->p_module )
    {
        module_Unneed( p_intf, p_intf->p_module );
    }
275

Sam Hocevar's avatar
   
Sam Hocevar committed
276
    vlc_mutex_destroy( &p_intf->change_lock );
277

Sam Hocevar's avatar
   
Sam Hocevar committed
278
    /* Free structure */
279
280
281
    vlc_object_destroy( p_intf );
}

zorglub's avatar
zorglub committed
282

gbazin's avatar
   
gbazin committed
283
/* Following functions are local */
zorglub's avatar
zorglub committed
284

285
286
287
288
289
290
291
/*****************************************************************************
 * Manager: helper thread for blocking interfaces
 *****************************************************************************
 * If the interface is launched in the main thread, it will not listen to
 * p_vlc->b_die events because it is only supposed to listen to p_intf->b_die.
 * This thread takes care of the matter.
 *****************************************************************************/
zorglub's avatar
zorglub committed
292
293
294
295
296
297
298
299
300
301
302
303
/**
 * \brief Helper thread for blocking interfaces.
 * \ingroup vlc_interface
 *
 * This is a local function
 * If the interface is launched in the main thread, it will not listen to
 * p_vlc->b_die events because it is only supposed to listen to p_intf->b_die.
 * This thread takes care of the matter.
 * \see intf_RunThread
 * \param p_intf an interface thread
 * \return nothing
 */
304
305
306
307
308
309
static void Manager( intf_thread_t *p_intf )
{
    while( !p_intf->b_die )
    {
        msleep( INTF_IDLE_SLEEP );

310
        if( p_intf->p_libvlc->b_die )
311
        {
Sam Hocevar's avatar
Sam Hocevar committed
312
            p_intf->b_die = VLC_TRUE;
313
#ifdef __APPLE__
314
    if( strncmp( p_intf->p_libvlc->psz_object_name, "clivlc", 6 ) )
hartman's avatar
hartman committed
315
316
317
318
    {
        [NSApp stop: NULL];
    }
#endif
319
320
321
            return;
        }
    }
322
}
323

gbazin's avatar
   
gbazin committed
324
325
326
327
328
/*****************************************************************************
 * RunInterface: setups necessary data and give control to the interface
 *****************************************************************************/
static void RunInterface( intf_thread_t *p_intf )
{
Sam Hocevar's avatar
Sam Hocevar committed
329
330
331
    static char *ppsz_interfaces[] =
    {
        "skins2", "Skins 2",
zorglub's avatar
zorglub committed
332
#ifndef WIN32
zorglub's avatar
zorglub committed
333
        "wxwidgets", "wxWidgets",
zorglub's avatar
zorglub committed
334
#endif
Sam Hocevar's avatar
Sam Hocevar committed
335
336
337
338
339
340
        NULL, NULL
    };
    char **ppsz_parser;

    vlc_list_t *p_list;
    int i;
gbazin's avatar
   
gbazin committed
341
    vlc_value_t val, text;
Sam Hocevar's avatar
Sam Hocevar committed
342
    char *psz_intf;
gbazin's avatar
   
gbazin committed
343
344
345
346
347
348
349
350

    /* Variable used for interface switching */
    p_intf->psz_switch_intf = NULL;
    var_Create( p_intf, "intf-switch", VLC_VAR_STRING |
                VLC_VAR_HASCHOICE | VLC_VAR_ISCOMMAND );
    text.psz_string = _("Switch interface");
    var_Change( p_intf, "intf-switch", VLC_VAR_SETTEXT, &text, NULL );

Sam Hocevar's avatar
Sam Hocevar committed
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
    /* Only fill the list with available modules */
    p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
    for( ppsz_parser = ppsz_interfaces; *ppsz_parser; ppsz_parser += 2 )
    {
        for( i = 0; i < p_list->i_count; i++ )
        {
            module_t *p_module = (module_t *)p_list->p_values[i].p_object;
            if( !strcmp( p_module->psz_object_name, ppsz_parser[0] ) )
            {
                val.psz_string = ppsz_parser[0];
                text.psz_string = ppsz_parser[1];
                var_Change( p_intf, "intf-switch", VLC_VAR_ADDCHOICE,
                            &val, &text );
                break;
            }
        }
    }
    vlc_list_release( p_list );
gbazin's avatar
   
gbazin committed
369
370
371
372
373
374

    var_AddCallback( p_intf, "intf-switch", SwitchIntfCallback, NULL );

    /* Variable used for interface spawning */
    var_Create( p_intf, "intf-add", VLC_VAR_STRING |
                VLC_VAR_HASCHOICE | VLC_VAR_ISCOMMAND );
hartman's avatar
hartman committed
375
    text.psz_string = _("Add Interface");
gbazin's avatar
   
gbazin committed
376
377
378
379
    var_Change( p_intf, "intf-add", VLC_VAR_SETTEXT, &text, NULL );

    val.psz_string = "rc"; text.psz_string = "Console";
    var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
hartman's avatar
hartman committed
380
381
382
    val.psz_string = "telnet"; text.psz_string = "Telnet Interface";
    var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
    val.psz_string = "http"; text.psz_string = "Web Interface";
gbazin's avatar
   
gbazin committed
383
    var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
hartman's avatar
hartman committed
384
    val.psz_string = "logger"; text.psz_string = "Debug logging";
gbazin's avatar
   
gbazin committed
385
    var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
hartman's avatar
hartman committed
386
    val.psz_string = "gestures"; text.psz_string = "Mouse Gestures";
gbazin's avatar
   
gbazin committed
387
    var_Change( p_intf, "intf-add", VLC_VAR_ADDCHOICE, &val, &text );
gbazin's avatar
   
gbazin committed
388
389
390

    var_AddCallback( p_intf, "intf-add", AddIntfCallback, NULL );

Sam Hocevar's avatar
Sam Hocevar committed
391
392
393
394
    do
    {
        /* Give control to the interface */
        p_intf->pf_run( p_intf );
gbazin's avatar
   
gbazin committed
395

Sam Hocevar's avatar
Sam Hocevar committed
396
397
        /* Reset play on start status */
        p_intf->b_play = VLC_FALSE;
398

Sam Hocevar's avatar
Sam Hocevar committed
399
400
401
402
403
        if( !p_intf->psz_switch_intf )
        {
            break;
        }

404
405
406
        /* Make sure the old interface is completely uninitialized */
        module_Unneed( p_intf, p_intf->p_module );

Sam Hocevar's avatar
Sam Hocevar committed
407
408
        /* Provide ability to switch the main interface on the fly */
        psz_intf = p_intf->psz_switch_intf;
gbazin's avatar
   
gbazin committed
409
410
        p_intf->psz_switch_intf = NULL;

411
412
413
414
415
        vlc_mutex_lock( &p_intf->object_lock );
        p_intf->b_die = VLC_FALSE;
        p_intf->b_dead = VLC_FALSE;
        vlc_mutex_unlock( &p_intf->object_lock );

gbazin's avatar
   
gbazin committed
416
        p_intf->p_module = module_Need( p_intf, "interface", psz_intf, 0 );
gbazin's avatar
   
gbazin committed
417
418
        free( psz_intf );
    }
Sam Hocevar's avatar
Sam Hocevar committed
419
    while( p_intf->p_module );
gbazin's avatar
   
gbazin committed
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
}

static int SwitchIntfCallback( vlc_object_t *p_this, char const *psz_cmd,
                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    intf_thread_t *p_intf = (intf_thread_t *)p_this;

    p_intf->psz_switch_intf =
        malloc( strlen(newval.psz_string) + sizeof(",none") );
    sprintf( p_intf->psz_switch_intf, "%s,none", newval.psz_string );
    p_intf->b_die = VLC_TRUE;

    return VLC_SUCCESS;
}

static int AddIntfCallback( vlc_object_t *p_this, char const *psz_cmd,
                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
    intf_thread_t *p_intf;
    char *psz_intf = malloc( strlen(newval.psz_string) + sizeof(",none") );

    /* Try to create the interface */
    sprintf( psz_intf, "%s,none", newval.psz_string );
443
    p_intf = intf_Create( p_this->p_libvlc, psz_intf, 0, NULL );
gbazin's avatar
   
gbazin committed
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
    free( psz_intf );
    if( p_intf == NULL )
    {
        msg_Err( p_this, "interface \"%s\" initialization failed",
                 newval.psz_string );
        return VLC_EGENERIC;
    }

    /* Try to run the interface */
    p_intf->b_block = VLC_FALSE;
    if( intf_RunThread( p_intf ) != VLC_SUCCESS )
    {
        vlc_object_detach( p_intf );
        intf_Destroy( p_intf );
        return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}
hartman's avatar
-    
hartman committed
463

464
#ifdef __APPLE__
hartman's avatar
-    
hartman committed
465
/*****************************************************************************
zorglub's avatar
zorglub committed
466
 * VLCApplication implementation
hartman's avatar
-    
hartman committed
467
 *****************************************************************************/
zorglub's avatar
zorglub committed
468
@implementation VLCApplication
hartman's avatar
-    
hartman committed
469

zorglub's avatar
zorglub committed
470
- (void)setVLC: (libvlc_int_t *) p_libvlc
471
{
zorglub's avatar
zorglub committed
472
    o_vlc = p_libvlc;
473
474
}

hartman's avatar
-    
hartman committed
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
- (void)stop: (id)sender
{
    NSEvent *o_event;
    NSAutoreleasePool *o_pool;
    [super stop:sender];

    o_pool = [[NSAutoreleasePool alloc] init];
    /* send a dummy event to break out of the event loop */
    o_event = [NSEvent mouseEventWithType: NSLeftMouseDown
                location: NSMakePoint( 1, 1 ) modifierFlags: 0
                timestamp: 1 windowNumber: [[NSApp mainWindow] windowNumber]
                context: [NSGraphicsContext currentContext] eventNumber: 1
                clickCount: 1 pressure: 0.0];
    [NSApp postEvent: o_event atStart: YES];
    [o_pool release];
}

- (void)terminate: (id)sender
{
494
    o_vlc->b_die = VLC_TRUE;
hartman's avatar
-    
hartman committed
495
496
497
498
499
}

@end
#endif