intf.m 67.7 KB
Newer Older
1
/*****************************************************************************
2
 * intf.m: MacOS X interface module
3
 *****************************************************************************
4
 * Copyright (C) 2002-2013 VLC authors and VideoLAN
5
 * $Id$
6
7
 *
 * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8
 *          Derk-Jan Hartman <hartman at videolan.org>
9
 *          Felix Paul Kühne <fkuehne at videolan dot org>
10
 *          Pierre d'Herbemont <pdherbemont # videolan org>
11
 *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
12
13
14
15
16
 *
 * 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.
17
 *
18
19
20
21
22
23
24
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * 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
25
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26
27
28
29
30
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
31
32
33
34
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

35
36
#include <stdlib.h>                                      /* malloc(), free() */
#include <string.h>
hartman's avatar
hartman committed
37
#include <vlc_common.h>
38
#include <vlc_keys.h>
39
#include <vlc_dialog.h>
40
#include <vlc_url.h>
41
#include <vlc_modules.h>
42
#include <vlc_plugin.h>
43
#include <vlc_vout_display.h>
44
#include <unistd.h> /* execl() */
45

46
#import "CompatibilityFixes.h"
47
#import "intf.h"
48
#import "MainMenu.h"
49
#import "VideoView.h"
50
51
#import "prefs.h"
#import "playlist.h"
52
#import "playlistinfo.h"
53
54
55
56
#import "controls.h"
#import "open.h"
#import "wizard.h"
#import "bookmarks.h"
57
#import "coredialogs.h"
58
#import "AppleRemote.h"
59
#import "eyetv.h"
60
#import "simple_prefs.h"
61
#import "CoreInteraction.h"
62
#import "TrackSynchronization.h"
63
#import "ExtensionsManager.h"
64
#import "BWQuincyManager.h"
65
#import "ControlsBar.h"
66

67
#import "VideoEffects.h"
68
#import "AudioEffects.h"
69

70
#ifdef UPDATE_CHECK
Rafaël Carré's avatar
Rafaël Carré committed
71
#import <Sparkle/Sparkle.h>                 /* we're the update delegate */
72
#endif
73

74
#import "iTunes.h"
75
#import "Spotify.h"
76

77
78
79
/*****************************************************************************
 * Local prototypes.
 *****************************************************************************/
80
static void Run (intf_thread_t *p_intf);
81

82
83
84
85
static void updateProgressPanel (void *, const char *, float);
static bool checkProgressPanel (void *);
static void destroyProgressPanel (void *);

86
87
88
89
static int InputEvent(vlc_object_t *, const char *,
                      vlc_value_t, vlc_value_t, void *);
static int PLItemChanged(vlc_object_t *, const char *,
                         vlc_value_t, vlc_value_t, void *);
90
91
static int PLItemUpdated(vlc_object_t *, const char *,
                         vlc_value_t, vlc_value_t, void *);
David's avatar
David committed
92

93
94
95
96
static int PlaybackModeUpdated(vlc_object_t *, const char *,
                               vlc_value_t, vlc_value_t, void *);
static int VolumeUpdated(vlc_object_t *, const char *,
                         vlc_value_t, vlc_value_t, void *);
97
98
static int BossCallback(vlc_object_t *, const char *,
                         vlc_value_t, vlc_value_t, void *);
99

100
#pragma mark -
101
#pragma mark VLC Interface Object Callbacks
102

103
104
105
106
static bool b_intf_starting = false;
static vlc_mutex_t start_mutex = VLC_STATIC_MUTEX;
static vlc_cond_t  start_cond = VLC_STATIC_COND;

107
108
109
/*****************************************************************************
 * OpenIntf: initialize interface
 *****************************************************************************/
110
int OpenIntf (vlc_object_t *p_this)
Jérome Decoodt's avatar
Jérome Decoodt committed
111
{
112
113
    NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
    [VLCApplication sharedApplication];
114

115
    intf_thread_t *p_intf = (intf_thread_t*) p_this;
116
    msg_Dbg(p_intf, "Starting macosx interface");
117
    Run(p_intf);
118

119
    [o_pool release];
Jean-Paul Saman's avatar
Jean-Paul Saman committed
120
    return VLC_SUCCESS;
121
122
}

123
124
static NSLock * o_vout_provider_lock = nil;

125
static int WindowControl(vout_window_t *, int i_query, va_list);
126

127
int WindowOpen(vout_window_t *p_wnd, const vout_window_cfg_t *cfg)
128
{
129
130
131
132
    if (cfg->type != VOUT_WINDOW_TYPE_INVALID
     && cfg->type != VOUT_WINDOW_TYPE_NSOBJECT)
        return VLC_EGENERIC;

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
    msg_Dbg(p_wnd, "Opening video window");

    /*
     * HACK: Wait 200ms for the interface to come up.
     * WindowOpen might be called before the mac intf is started. Lets wait until OpenIntf gets called
     * and does basic initialization. Enqueuing the vout controller request into the main loop later on
     * ensures that the actual window is created after the interface is fully initialized
     * (applicationDidFinishLaunching).
     *
     * Timeout is needed as the mac intf is not always started at all.
     */
    mtime_t deadline = mdate() + 200000;
    vlc_mutex_lock(&start_mutex);
    while (!b_intf_starting) {
        if (vlc_cond_timedwait(&start_cond, &start_mutex, deadline)) {
            break; // timeout
        }
    }

    if (!b_intf_starting) {
        msg_Err(p_wnd, "Cannot create vout as Mac OS X interface was not found");
        vlc_mutex_unlock(&start_mutex);
155
156
        return VLC_EGENERIC;
    }
157
158
159
160
    vlc_mutex_unlock(&start_mutex);

    NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];

161
    NSRect proposedVideoViewPosition = NSMakeRect(cfg->x, cfg->y, cfg->width, cfg->height);
162

163
    [o_vout_provider_lock lock];
164
    VLCVoutWindowController *o_vout_controller = [[VLCMain sharedInstance] voutController];
165
166
167
168
169
170
    if (!o_vout_controller) {
        [o_vout_provider_lock unlock];
        [o_pool release];
        return VLC_EGENERIC;
    }

171
172
173
174
175
176
177
178
179
180
181
182
183
    SEL sel = @selector(setupVoutForWindow:withProposedVideoViewPosition:);
    NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[o_vout_controller methodSignatureForSelector:sel]];
    [inv setTarget:o_vout_controller];
    [inv setSelector:sel];
    [inv setArgument:&p_wnd atIndex:2]; // starting at 2!
    [inv setArgument:&proposedVideoViewPosition atIndex:3];

    [inv performSelectorOnMainThread:@selector(invoke) withObject:nil
                       waitUntilDone:YES];

    VLCVoutView *videoView = nil;
    [inv getReturnValue:&videoView];

184
185
    // this method is not supposed to fail
    assert(videoView != nil);
186

187
188
189
    msg_Dbg(VLCIntf, "returning videoview with proposed position x=%i, y=%i, width=%i, height=%i", cfg->x, cfg->y, cfg->width, cfg->height);
    p_wnd->handle.nsobject = videoView;

190
    [o_vout_provider_lock unlock];
191

192
    p_wnd->type = VOUT_WINDOW_TYPE_NSOBJECT;
193
    p_wnd->control = WindowControl;
194

195
    [o_pool release];
196
197
198
    return VLC_SUCCESS;
}

199
static int WindowControl(vout_window_t *p_wnd, int i_query, va_list args)
200
{
201
202
203
204
205
206
207
208
209
210
    NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];

    [o_vout_provider_lock lock];
    VLCVoutWindowController *o_vout_controller = [[VLCMain sharedInstance] voutController];
    if (!o_vout_controller) {
        [o_vout_provider_lock unlock];
        [o_pool release];
        return VLC_EGENERIC;
    }

211
    switch(i_query) {
212
213
        case VOUT_WINDOW_SET_STATE:
        {
214
            unsigned i_state = va_arg(args, unsigned);
215

216
217
218
219
220
221
            if (i_state & VOUT_WINDOW_STATE_BELOW)
            {
                msg_Dbg(p_wnd, "Ignore change to VOUT_WINDOW_STATE_BELOW");
                goto out;
            }

222
223
224
225
226
            NSInteger i_cooca_level = NSNormalWindowLevel;
            if (i_state & VOUT_WINDOW_STATE_ABOVE)
                i_cooca_level = NSStatusWindowLevel;

            SEL sel = @selector(setWindowLevel:forWindow:);
227
228
            NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[o_vout_controller methodSignatureForSelector:sel]];
            [inv setTarget:o_vout_controller];
229
230
231
232
233
234
            [inv setSelector:sel];
            [inv setArgument:&i_cooca_level atIndex:2]; // starting at 2!
            [inv setArgument:&p_wnd atIndex:3];
            [inv performSelectorOnMainThread:@selector(invoke) withObject:nil
                               waitUntilDone:NO];

235
            break;
236
237
238
        }
        case VOUT_WINDOW_SET_SIZE:
        {
239
240
            unsigned int i_width  = va_arg(args, unsigned int);
            unsigned int i_height = va_arg(args, unsigned int);
241

242
            NSSize newSize = NSMakeSize(i_width, i_height);
243
            SEL sel = @selector(setNativeVideoSize:forWindow:);
244
245
            NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[o_vout_controller methodSignatureForSelector:sel]];
            [inv setTarget:o_vout_controller];
246
247
248
249
250
251
            [inv setSelector:sel];
            [inv setArgument:&newSize atIndex:2]; // starting at 2!
            [inv setArgument:&p_wnd atIndex:3];
            [inv performSelectorOnMainThread:@selector(invoke) withObject:nil
                               waitUntilDone:NO];

252
            break;
253
254
255
        }
        case VOUT_WINDOW_SET_FULLSCREEN:
        {
256
257
258
259
260
            if (var_InheritBool(VLCIntf, "video-wallpaper")) {
                msg_Dbg(p_wnd, "Ignore fullscreen event as video-wallpaper is on");
                goto out;
            }

261
            int i_full = va_arg(args, int);
262
            BOOL b_animation = YES;
263

264
            SEL sel = @selector(setFullscreen:forWindow:withAnimation:);
265
266
            NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[o_vout_controller methodSignatureForSelector:sel]];
            [inv setTarget:o_vout_controller];
267
268
269
            [inv setSelector:sel];
            [inv setArgument:&i_full atIndex:2]; // starting at 2!
            [inv setArgument:&p_wnd atIndex:3];
270
            [inv setArgument:&b_animation atIndex:4];
271
272
273
            [inv performSelectorOnMainThread:@selector(invoke) withObject:nil
                               waitUntilDone:NO];

274
            break;
275
276
        }
        default:
277
        {
278
            msg_Warn(p_wnd, "unsupported control query");
279
280
            [o_vout_provider_lock unlock];
            [o_pool release];
281
            return VLC_EGENERIC;
282
        }
283
    }
284

285
out:
286
287
288
    [o_vout_provider_lock unlock];
    [o_pool release];
    return VLC_SUCCESS;
289
290
}

291
void WindowClose(vout_window_t *p_wnd)
292
{
293
    NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
294

295
296
297
298
299
300
301
302
303
304
    [o_vout_provider_lock lock];
    VLCVoutWindowController *o_vout_controller = [[VLCMain sharedInstance] voutController];
    if (!o_vout_controller) {
        [o_vout_provider_lock unlock];
        [o_pool release];
        return;
    }

    [o_vout_controller performSelectorOnMainThread:@selector(removeVoutforDisplay:) withObject:[NSValue valueWithPointer:p_wnd] waitUntilDone:NO];
    [o_vout_provider_lock unlock];
305

306
    [o_pool release];
307
308
}

309
310
/* Used to abort the app.exec() on OSX after libvlc_Quit is called */
#include "../../../lib/libvlc_internal.h" /* libvlc_SetExitHandler */
311

312
313
314
315
316
static void QuitVLC( void *obj )
{
    [[VLCApplication sharedApplication] performSelectorOnMainThread:@selector(terminate:) withObject:nil waitUntilDone:NO];
}

317
318
319
/*****************************************************************************
 * Run: main loop
 *****************************************************************************/
320
static NSLock * o_appLock = nil;    // controls access to f_appExit
321

322
static void Run(intf_thread_t *p_intf)
323
{
324
    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
325
    [VLCApplication sharedApplication];
326

327
    o_appLock = [[NSLock alloc] init];
328
    o_vout_provider_lock = [[NSLock alloc] init];
329

330
    libvlc_SetExitHandler(p_intf->p_libvlc, QuitVLC, p_intf);
hartman's avatar
-    
hartman committed
331
    [[VLCMain sharedInstance] setIntf: p_intf];
332

333
334
335
336
337
    vlc_mutex_lock(&start_mutex);
    b_intf_starting = true;
    vlc_cond_signal(&start_cond);
    vlc_mutex_unlock(&start_mutex);

hartman's avatar
hartman committed
338
    [NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
339

340
    [NSApp run];
341
    msg_Dbg(p_intf, "Run loop has been stopped");
342
    [[VLCMain sharedInstance] applicationWillTerminate:nil];
343
    [o_appLock release];
344
345
    [o_vout_provider_lock release];
    o_vout_provider_lock = nil;
346
    [o_pool release];
347
348

    raise(SIGTERM);
349
350
}

351
352
#pragma mark -
#pragma mark Variables Callback
353

354
355
static int InputEvent(vlc_object_t *p_this, const char *psz_var,
                       vlc_value_t oldval, vlc_value_t new_val, void *param)
356
{
357
    NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
358
359
    switch (new_val.i_int) {
        case INPUT_EVENT_STATE:
360
            [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(playbackStatusUpdated) withObject: nil waitUntilDone:NO];
361
362
            break;
        case INPUT_EVENT_RATE:
363
            [[[VLCMain sharedInstance] mainMenu] performSelectorOnMainThread:@selector(updatePlaybackRate) withObject: nil waitUntilDone:NO];
364
365
            break;
        case INPUT_EVENT_POSITION:
366
            [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updatePlaybackPosition) withObject: nil waitUntilDone:NO];
367
368
369
            break;
        case INPUT_EVENT_TITLE:
        case INPUT_EVENT_CHAPTER:
370
            [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
371
372
            break;
        case INPUT_EVENT_CACHE:
373
            [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainWindow) withObject: nil waitUntilDone: NO];
374
375
            break;
        case INPUT_EVENT_STATISTICS:
376
377
378
            dispatch_async(dispatch_get_main_queue(), ^{
                [[[VLCMain sharedInstance] info] updateStatistics];
            });
379
380
381
382
383
384
385
386
387
388
389
            break;
        case INPUT_EVENT_ES:
            break;
        case INPUT_EVENT_TELETEXT:
            break;
        case INPUT_EVENT_AOUT:
            break;
        case INPUT_EVENT_VOUT:
            break;
        case INPUT_EVENT_ITEM_META:
        case INPUT_EVENT_ITEM_INFO:
390
            [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
391
            [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
392
            [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMetaAndInfo) withObject: nil waitUntilDone:NO];
393
394
395
396
            break;
        case INPUT_EVENT_BOOKMARK:
            break;
        case INPUT_EVENT_RECORD:
397
            [[VLCMain sharedInstance] updateRecordState: var_GetBool(p_this, "record")];
398
399
            break;
        case INPUT_EVENT_PROGRAM:
400
            [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
401
402
403
404
405
406
407
            break;
        case INPUT_EVENT_ITEM_EPG:
            break;
        case INPUT_EVENT_SIGNAL:
            break;

        case INPUT_EVENT_ITEM_NAME:
408
            [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
409
410
411
412
            break;

        case INPUT_EVENT_AUDIO_DELAY:
        case INPUT_EVENT_SUBTITLE_DELAY:
413
            [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateDelays) withObject:nil waitUntilDone:NO];
414
415
416
            break;

        case INPUT_EVENT_DEAD:
417
            [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
418
            [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updatePlaybackPosition) withObject:nil waitUntilDone:NO];
419
420
421
422
            break;

        default:
            break;
Jean-Paul Saman's avatar
Jean-Paul Saman committed
423
    }
424

425
    [o_pool release];
426
427
428
    return VLC_SUCCESS;
}

429
430
static int PLItemChanged(vlc_object_t *p_this, const char *psz_var,
                         vlc_value_t oldval, vlc_value_t new_val, void *param)
431
{
432
    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
433

434
    [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(PlaylistItemChanged) withObject:nil waitUntilDone:NO];
435

436
    [o_pool release];
437
438
439
    return VLC_SUCCESS;
}

440
441
442
443
444
445
446
447
448
449
450
451
452
453
/**
 * Callback for item-change variable. Is triggered after update of duration or metadata.
 */
static int PLItemUpdated(vlc_object_t *p_this, const char *psz_var,
                         vlc_value_t oldval, vlc_value_t new_val, void *param)
{
    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];

    [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(plItemUpdated) withObject:nil waitUntilDone:NO];

    [o_pool release];
    return VLC_SUCCESS;
}

David's avatar
David committed
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
static int PLItemAppended(vlc_object_t *p_this, const char *psz_var,
                           vlc_value_t oldval, vlc_value_t new_val, void *param)
{
    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];

    playlist_add_t *p_add = new_val.p_address;
    NSArray *o_val = [NSArray arrayWithObjects:[NSNumber numberWithInt:p_add->i_node], [NSNumber numberWithInt:p_add->i_item], nil];
    [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(plItemAppended:) withObject:o_val waitUntilDone:NO];

    [o_pool release];
    return VLC_SUCCESS;
}

static int PLItemRemoved(vlc_object_t *p_this, const char *psz_var,
                           vlc_value_t oldval, vlc_value_t new_val, void *param)
{
    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];

    NSNumber *o_val = [NSNumber numberWithInt:new_val.i_int];
    [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(plItemRemoved:) withObject:o_val waitUntilDone:NO];

    [o_pool release];
    return VLC_SUCCESS;
}

479
480
static int PlaybackModeUpdated(vlc_object_t *p_this, const char *psz_var,
                         vlc_value_t oldval, vlc_value_t new_val, void *param)
481
{
482
    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
483
    [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(playbackModeUpdated) withObject:nil waitUntilDone:NO];
484

485
    [o_pool release];
486
487
488
    return VLC_SUCCESS;
}

489
490
static int VolumeUpdated(vlc_object_t *p_this, const char *psz_var,
                         vlc_value_t oldval, vlc_value_t new_val, void *param)
491
{
492
    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
493
    [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateVolume) withObject:nil waitUntilDone:NO];
494

495
    [o_pool release];
496
497
498
    return VLC_SUCCESS;
}

499
500
501
502
503
504
505
506
507
508
509
510
static int BossCallback(vlc_object_t *p_this, const char *psz_var,
                        vlc_value_t oldval, vlc_value_t new_val, void *param)
{
    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];

    [[VLCCoreInteraction sharedInstance] performSelectorOnMainThread:@selector(pause) withObject:nil waitUntilDone:NO];
    [[VLCApplication sharedApplication] hide:nil];

    [o_pool release];
    return VLC_SUCCESS;
}

511
512
513
514
515
/*****************************************************************************
 * ShowController: Callback triggered by the show-intf playlist variable
 * through the ShowIntf-control-intf, to let us show the controller-win;
 * usually when in fullscreen-mode
 *****************************************************************************/
516
517
static int ShowController(vlc_object_t *p_this, const char *psz_variable,
                     vlc_value_t old_val, vlc_value_t new_val, void *param)
518
519
{
    intf_thread_t * p_intf = VLCIntf;
520
    if (p_intf) {
521
522
        playlist_t * p_playlist = pl_Get(p_intf);
        BOOL b_fullscreen = var_GetBool(p_playlist, "fullscreen");
523
        if (b_fullscreen)
524
            [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(showFullscreenController) withObject:nil waitUntilDone:NO];
525
        else if (!strcmp(psz_variable, "intf-show"))
526
            [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(showMainWindow) withObject:nil waitUntilDone:NO];
527
    }
528

529
530
531
    return VLC_SUCCESS;
}

532
/*****************************************************************************
533
 * DialogCallback: Callback triggered by the "dialog-*" variables
534
 * to let the intf display error and interaction dialogs
535
 *****************************************************************************/
536
static int DialogCallback(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
537
538
{
    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
539

540
    if ([[NSString stringWithUTF8String:type] isEqualToString: @"dialog-progress-bar"]) {
541
542
543
544
545
546
547
548
        /* the progress panel needs to update itself and therefore wants special treatment within this context */
        dialog_progress_bar_t *p_dialog = (dialog_progress_bar_t *)value.p_address;

        p_dialog->pf_update = updateProgressPanel;
        p_dialog->pf_check = checkProgressPanel;
        p_dialog->pf_destroy = destroyProgressPanel;
        p_dialog->p_sys = VLCIntf->p_libvlc;
    }
549

550
    NSValue *o_value = [NSValue valueWithPointer:value.p_address];
David's avatar
David committed
551
    [[[VLCMain sharedInstance] coreDialogProvider] performEventWithObject: o_value ofType: type];
552

553
554
555
    [o_pool release];
    return VLC_SUCCESS;
}
bigben's avatar
bigben committed
556

557
558
559
560
void updateProgressPanel (void *priv, const char *text, float value)
{
    NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];

561
562
563
564
    NSString *o_txt = toNSStr(text);
    dispatch_async(dispatch_get_main_queue(), ^{
        [[[VLCMain sharedInstance] coreDialogProvider] updateProgressPanelWithText: o_txt andNumber: (double)(value * 1000.)];
    });
565
566
567
568
569
570
571

    [o_pool release];
}

void destroyProgressPanel (void *priv)
{
    NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
572
573
574
575

    if ([[NSApplication sharedApplication] isRunning])
        [[[VLCMain sharedInstance] coreDialogProvider] performSelectorOnMainThread:@selector(destroyProgressPanel) withObject:nil waitUntilDone:YES];

576
577
578
579
580
581
582
583
    [o_pool release];
}

bool checkProgressPanel (void *priv)
{
    return [[[VLCMain sharedInstance] coreDialogProvider] progressCancelled];
}

584
585
586
587
588
589
#pragma mark -
#pragma mark Helpers

input_thread_t *getInput(void)
{
    intf_thread_t *p_intf = VLCIntf;
590
591
    if (!p_intf)
        return NULL;
592
    return pl_CurrentInput(p_intf);
593
594
595
596
597
}

vout_thread_t *getVout(void)
{
    input_thread_t *p_input = getInput();
598
599
    if (!p_input)
        return NULL;
600
601
602
603
604
    vout_thread_t *p_vout = input_GetVout(p_input);
    vlc_object_release(p_input);
    return p_vout;
}

605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
vout_thread_t *getVoutForActiveWindow(void)
{
    vout_thread_t *p_vout = nil;

    id currentWindow = [NSApp keyWindow];
    if ([currentWindow respondsToSelector:@selector(videoView)]) {
        VLCVoutView *videoView = [currentWindow videoView];
        if (videoView) {
            p_vout = [videoView voutThread];
        }
    }

    if (!p_vout)
        p_vout = getVout();

    return p_vout;
}

623
audio_output_t *getAout(void)
624
{
625
626
    intf_thread_t *p_intf = VLCIntf;
    if (!p_intf)
627
        return NULL;
628
    return playlist_GetAout(pl_Get(p_intf));
629
630
}

631
#pragma mark -
632
633
#pragma mark Private

634
@interface VLCMain () <BWQuincyManagerDelegate>
635
636
637
638
639
640
- (void)removeOldPreferences;
@end

@interface VLCMain (Internal)
- (void)resetMediaKeyJump;
- (void)coreChangedMediaKeySupportSetting: (NSNotification *)o_notification;
641
@end
642

643
/*****************************************************************************
Jérome Decoodt's avatar
Jérome Decoodt committed
644
 * VLCMain implementation
645
646
647
 *****************************************************************************/
@implementation VLCMain

648
@synthesize voutController=o_vout_controller;
649
@synthesize nativeFullscreenMode=b_nativeFullscreenMode;
650

651
652
653
#pragma mark -
#pragma mark Initialization

hartman's avatar
hartman committed
654
655
656
657
658
659
660
static VLCMain *_o_sharedMainInstance = nil;

+ (VLCMain *)sharedInstance
{
    return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
}

Jérome Decoodt's avatar
Jérome Decoodt committed
661
- (id)init
hartman's avatar
hartman committed
662
{
663
    if (_o_sharedMainInstance) {
hartman's avatar
hartman committed
664
        [self dealloc];
665
        return _o_sharedMainInstance;
666
    } else
hartman's avatar
hartman committed
667
        _o_sharedMainInstance = [super init];
668

669
    p_intf = NULL;
670
    p_current_input = NULL;
671

672
    o_open = [[VLCOpen alloc] init];
673
    o_coredialogs = [[VLCCoreDialogProvider alloc] init];
674
    o_mainmenu = [[VLCMainMenu alloc] init];
675
    o_coreinteraction = [[VLCCoreInteraction alloc] init];
676
677
678
679
680
681
682
    o_eyetv = [[VLCEyeTVController alloc] init];

    /* announce our launch to a potential eyetv plugin */
    [[NSDistributedNotificationCenter defaultCenter] postNotificationName: @"VLCOSXGUIInit"
                                                                   object: @"VLCEyeTVSupport"
                                                                 userInfo: NULL
                                                       deliverImmediately: YES];
683
684
685
686
687

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"LiveUpdateTheMessagesPanel"];
    [defaults registerDefaults:appDefaults];

688
689
    o_vout_controller = [[VLCVoutWindowController alloc] init];

690
691
    informInputChangedQueue = dispatch_queue_create("org.videolan.vlc.inputChangedQueue", DISPATCH_QUEUE_SERIAL);

hartman's avatar
hartman committed
692
693
694
    return _o_sharedMainInstance;
}

695
696
- (void)setIntf: (intf_thread_t *)p_mainintf
{
hartman's avatar
hartman committed
697
698
699
    p_intf = p_mainintf;
}

700
701
- (intf_thread_t *)intf
{
hartman's avatar
hartman committed
702
703
704
    return p_intf;
}

705
706
- (void)awakeFromNib
{
707
    playlist_t *p_playlist;
708
709
    if (!p_intf) return;
    var_Create(p_intf, "intf-change", VLC_VAR_BOOL);
710

711
712
    /* Check if we already did this once. Opening the other nibs calls it too,
     because VLCMain is the owner */
713
714
    if (nib_main_loaded)
        return;
715

716
    p_playlist = pl_Get(p_intf);
hartman's avatar
hartman committed
717

718
719
    var_AddCallback(p_intf->p_libvlc, "intf-toggle-fscontrol", ShowController, self);
    var_AddCallback(p_intf->p_libvlc, "intf-show", ShowController, self);
720
    var_AddCallback(p_intf->p_libvlc, "intf-boss", BossCallback, self);
721
    var_AddCallback(p_playlist, "item-change", PLItemUpdated, self);
722
    var_AddCallback(p_playlist, "input-current", PLItemChanged, self);
David's avatar
David committed
723
724
    var_AddCallback(p_playlist, "playlist-item-append", PLItemAppended, self);
    var_AddCallback(p_playlist, "playlist-item-deleted", PLItemRemoved, self);
725
726
727
728
729
    var_AddCallback(p_playlist, "random", PlaybackModeUpdated, self);
    var_AddCallback(p_playlist, "repeat", PlaybackModeUpdated, self);
    var_AddCallback(p_playlist, "loop", PlaybackModeUpdated, self);
    var_AddCallback(p_playlist, "volume", VolumeUpdated, self);
    var_AddCallback(p_playlist, "mute", VolumeUpdated, self);
bigben's avatar
bigben committed
730

731
    if (!OSX_SNOW_LEOPARD) {
732
        if ([NSApp currentSystemPresentationOptions] & NSApplicationPresentationFullScreen)
733
            var_SetBool(p_playlist, "fullscreen", YES);
734
    }
735

David's avatar
David committed
736
    /* load our Shared Dialogs nib */
737
    [NSBundle loadNibNamed:@"SharedDialogs" owner: NSApp];
738

739
    /* subscribe to various interactive dialogues */
740
741
742
743
744
745
746
747
748
749
750
    var_Create(p_intf, "dialog-error", VLC_VAR_ADDRESS);
    var_AddCallback(p_intf, "dialog-error", DialogCallback, self);
    var_Create(p_intf, "dialog-critical", VLC_VAR_ADDRESS);
    var_AddCallback(p_intf, "dialog-critical", DialogCallback, self);
    var_Create(p_intf, "dialog-login", VLC_VAR_ADDRESS);
    var_AddCallback(p_intf, "dialog-login", DialogCallback, self);
    var_Create(p_intf, "dialog-question", VLC_VAR_ADDRESS);
    var_AddCallback(p_intf, "dialog-question", DialogCallback, self);
    var_Create(p_intf, "dialog-progress-bar", VLC_VAR_ADDRESS);
    var_AddCallback(p_intf, "dialog-progress-bar", DialogCallback, self);
    dialog_Register(p_intf);
751

752
753
754
755
756
    /* init Apple Remote support */
    o_remote = [[AppleRemote alloc] init];
    [o_remote setClickCountEnabledButtons: kRemoteButtonPlay];
    [o_remote setDelegate: _o_sharedMainInstance];

757
    /* yeah, we are done */
758
759
    b_nativeFullscreenMode = NO;
#ifdef MAC_OS_X_VERSION_10_7
760
    if (!OSX_SNOW_LEOPARD)
761
        b_nativeFullscreenMode = var_InheritBool(p_intf, "macosx-nativefullscreenmode");
762
#endif
763

764
    if (config_GetInt(VLCIntf, "macosx-icon-change")) {
765
766
767
768
769
770
771
772
773
774
775
776
777
        /* After day 354 of the year, the usual VLC cone is replaced by another cone
         * wearing a Father Xmas hat.
         * Note: this icon doesn't represent an endorsement of The Coca-Cola Company.
         */
        NSCalendar *gregorian =
        [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
        NSUInteger dayOfYear = [gregorian ordinalityOfUnit:NSDayCalendarUnit inUnit:NSYearCalendarUnit forDate:[NSDate date]];
        [gregorian release];

        if (dayOfYear >= 354)
            [[VLCApplication sharedApplication] setApplicationIconImage: [NSImage imageNamed:@"vlc-xmas"]];
    }

778
    nib_main_loaded = TRUE;
779
780
}

Rafaël Carré's avatar
Rafaël Carré committed
781
782
783
784
785
786
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
    playlist_t * p_playlist = pl_Get(VLCIntf);
    PL_LOCK;
    items_at_launch = p_playlist->p_local_category->i_children;
    PL_UNLOCK;
787

788
    [NSBundle loadNibNamed:@"MainWindow" owner: self];
789
790
791
792

    // This cannot be called directly here, as the main loop is not running yet so it would have no effect.
    // So lets enqueue it into the loop for later execution.
    [o_mainwindow performSelector:@selector(makeKeyAndOrderFront:) withObject:nil afterDelay:0];
793

794
#ifdef UPDATE_CHECK
795
    [[SUUpdater sharedUpdater] setDelegate:self];
796
#endif
Rafaël Carré's avatar
Rafaël Carré committed
797
798
}

799
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
800
{
Rafaël Carré's avatar
Rafaël Carré committed
801
802
    launched = YES;

803
804
    if (!p_intf)
        return;
805

806
807
808
809
810
811
812
813
814
815
816
    NSString *appVersion = [[[NSBundle mainBundle] infoDictionary] valueForKey: @"CFBundleVersion"];
    NSRange endRande = [appVersion rangeOfString:@"-"];
    if (endRande.location != NSNotFound)
        appVersion = [appVersion substringToIndex:endRande.location];

    BWQuincyManager *quincyManager = [BWQuincyManager sharedQuincyManager];
    [quincyManager setApplicationVersion:appVersion];
    [quincyManager setSubmissionURL:@"http://crash.videolan.org/crash_v200.php"];
    [quincyManager setDelegate:self];
    [quincyManager setCompanyName:@"VideoLAN"];

817
818
    [self updateCurrentlyUsedHotkeys];

819
    /* init media key support */
820
821
    b_mediaKeySupport = var_InheritBool(VLCIntf, "macosx-mediakeys");
    if (b_mediaKeySupport) {
822
823
824
825
826
        o_mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self];
        [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
                                                                 [SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers], kMediaKeyUsingBundleIdentifiersDefaultsKey,
                                                                 nil]];
    }
827
828
    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(coreChangedMediaKeySupportSetting:) name: @"VLCMediaKeySupportSettingChanged" object: nil];

829
    [self removeOldPreferences];
830

831
832
833
834
    /* Handle sleep notification */
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:)
           name:NSWorkspaceWillSleepNotification object:nil];

835
836
837
838
    /* update the main window */
    [o_mainwindow updateWindow];
    [o_mainwindow updateTimeSlider];
    [o_mainwindow updateVolumeSlider];
839

840
841
842
843
844
845
846
    /* Hack: Playlist is started before the interface.
     * Thus, call additional updaters as we might miss these events if posted before
     * the callbacks are registered.
     */
    [self PlaylistItemChanged];
    [self playbackModeUpdated];

847
848
    // respect playlist-autostart
    // note that PLAYLIST_PLAY will not stop any playback if already started
849
850
    playlist_t * p_playlist = pl_Get(VLCIntf);
    PL_LOCK;
851
    BOOL kidsAround = p_playlist->p_local_category->i_children != 0;
852
    if (kidsAround && var_GetBool(p_playlist, "playlist-autostart"))
853
854
        playlist_Control(p_playlist, PLAYLIST_PLAY, true);
    PL_UNLOCK;
855
856
}

857
858
859
860
/* don't allow a double termination call. If the user has
 * already invoked the quit then simply return this time. */
static bool f_appExit = false;

861
862
863
#pragma mark -
#pragma mark Termination

864
865
866
867
868
- (BOOL)isTerminating
{
    return f_appExit;
}

869
- (void)applicationWillTerminate:(NSNotification *)notification
870
{
871
    bool isTerminating;
872
873

    [o_appLock lock];
874
875
    isTerminating = f_appExit;
    f_appExit = true;
876
877
878
879
    [o_appLock unlock];

    if (isTerminating)
        return;
880

881
882
    [self resumeItunesPlayback:nil];

883
884
    if (notification == nil)
        [[NSNotificationCenter defaultCenter] postNotificationName: NSApplicationWillTerminateNotification object: nil];
885

886
    playlist_t * p_playlist = pl_Get(p_intf);
887

888
    /* save current video and audio profiles */
889
    [[VLCVideoEffects sharedInstance] saveCurrentProfile];
890
    [[VLCAudioEffects sharedInstance] saveCurrentProfile];
891

892
    /* Save some interface state in configuration, at module quit */
893
894
895
    config_PutInt(p_intf, "random", var_GetBool(p_playlist, "random"));
    config_PutInt(p_intf, "loop", var_GetBool(p_playlist, "loop"));
    config_PutInt(p_intf, "repeat", var_GetBool(p_playlist, "repeat"));
896

897
    msg_Dbg(p_intf, "Terminating");
898

899
    /* unsubscribe from the interactive dialogues */
900
901
902
903
904
905
    dialog_Unregister(p_intf);
    var_DelCallback(p_intf, "dialog-error", DialogCallback, self);
    var_DelCallback(p_intf, "dialog-critical", DialogCallback, self);
    var_DelCallback(p_intf, "dialog-login", DialogCallback, self);
    var_DelCallback(p_intf, "dialog-question", DialogCallback, self);
    var_DelCallback(p_intf, "dialog-progress-bar", DialogCallback, self);
906
    var_DelCallback(p_playlist, "item-change", PLItemUpdated, self);
907
    var_DelCallback(p_playlist, "input-current", PLItemChanged, self);
David's avatar
David committed
908
909
    var_DelCallback(p_playlist, "playlist-item-append", PLItemAppended, self);
    var_DelCallback(p_playlist, "playlist-item-deleted", PLItemRemoved, self);
910
911
912
913
914
    var_DelCallback(p_playlist, "random", PlaybackModeUpdated, self);
    var_DelCallback(p_playlist, "repeat", PlaybackModeUpdated, self);
    var_DelCallback(p_playlist, "loop", PlaybackModeUpdated, self);
    var_DelCallback(p_playlist, "volume", VolumeUpdated, self);
    var_DelCallback(p_playlist, "mute", VolumeUpdated, self);
915
    var_DelCallback(p_intf->p_libvlc, "intf-toggle-fscontrol", ShowController, self);
916
    var_DelCallback(p_intf->p_libvlc, "intf-show", ShowController, self);
917
    var_DelCallback(p_intf->p_libvlc, "intf-boss", BossCallback, self);
918

919
    if (p_current_input) {
920
921
922
        /* continue playback where you left off */
        [[self playlist] storePlaybackPositionForItem:p_current_input];

923
924
        var_DelCallback(p_current_input, "intf-event", InputEvent, [VLCMain sharedInstance]);
        vlc_object_release(p_current_input);