intf.m 18.3 KB
Newer Older
1
/*****************************************************************************
2
 * intf.m: MacOS X interface module
3
 *****************************************************************************
4
 * Copyright (C) 2002-2015 VLC authors and VideoLAN
5
 * $Id$
6
 *
7
 * Authors: Derk-Jan Hartman <hartman at videolan.org>
8
 *          Felix Paul Kühne <fkuehne at videolan dot org>
9
 *          Pierre d'Herbemont <pdherbemont # videolan org>
10
 *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
11
12
13
14
15
 *
 * 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.
16
 *
17
18
19
20
21
22
23
 * 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
24
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25
26
27
28
29
 *****************************************************************************/

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

34
35
#import "intf.h"

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

45
#import "CompatibilityFixes.h"
46
#import "InputManager.h"
47
#import "MainMenu.h"
48
#import "VideoView.h"
49
50
#import "prefs.h"
#import "playlist.h"
51
#import "playlistinfo.h"
52
53
#import "open.h"
#import "bookmarks.h"
54
#import "coredialogs.h"
55
#import "simple_prefs.h"
56
#import "CoreInteraction.h"
57
#import "TrackSynchronization.h"
58
#import "ExtensionsManager.h"
59
#import "BWQuincyManager.h"
60
#import "ResumeDialogController.h"
61

62
#import "VideoEffects.h"
63
#import "AudioEffects.h"
64
#import "intf-prefs.h"
65

66
#ifdef HAVE_SPARKLE
Rafaël Carré's avatar
Rafaël Carré committed
67
#import <Sparkle/Sparkle.h>                 /* we're the update delegate */
68
#endif
69

70
#pragma mark -
71
#pragma mark VLC Interface Object Callbacks
72

73
74
75
/*****************************************************************************
 * OpenIntf: initialize interface
 *****************************************************************************/
76
int OpenIntf (vlc_object_t *p_this)
Jérome Decoodt's avatar
Jérome Decoodt committed
77
{
78
79
80
    @autoreleasepool {
        intf_thread_t *p_intf = (intf_thread_t*) p_this;
        msg_Dbg(p_intf, "Starting macosx interface");
81

82
83
        [VLCApplication sharedApplication];
        [[VLCMain sharedInstance] setIntf: p_intf];
84

85
86
        [NSBundle loadNibNamed:@"MainMenu" owner:[[VLCMain sharedInstance] mainMenu]];

87
        [VLCVoutWindowController sharedInstance];
88

89
90
        return VLC_SUCCESS;
    }
91
92
}

93
94
void CloseIntf (vlc_object_t *p_this)
{
95
96
97
    @autoreleasepool {
        msg_Dbg(p_this, "Closing macosx interface");
        [[VLCMain sharedInstance] applicationWillTerminate:nil];
98
    }
99
100
}

101
102
#pragma mark -
#pragma mark Variables Callback
103

104
105
106
107
108
/*****************************************************************************
 * 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
 *****************************************************************************/
109
110
static int ShowController(vlc_object_t *p_this, const char *psz_variable,
                     vlc_value_t old_val, vlc_value_t new_val, void *param)
111
{
112
113
114
115
116
117
118
119
120
121
122
123
124
    @autoreleasepool {
        dispatch_async(dispatch_get_main_queue(), ^{

            intf_thread_t * p_intf = VLCIntf;
            if (p_intf) {
                playlist_t * p_playlist = pl_Get(p_intf);
                BOOL b_fullscreen = var_GetBool(p_playlist, "fullscreen");
                if (b_fullscreen)
                    [[VLCMain sharedInstance] showFullscreenController];

                else if (!strcmp(psz_variable, "intf-show"))
                    [[[VLCMain sharedInstance] mainWindow] makeKeyAndOrderFront:nil];
            }
David's avatar
David committed
125

126
        });
127

128
129
        return VLC_SUCCESS;
    }
130
131
}

132
#pragma mark -
133
134
#pragma mark Private

135
@interface VLCMain () <BWQuincyManagerDelegate>
136
{
137
138
139
    intf_thread_t *p_intf;
    BOOL launched;
    int items_at_launch;
140
141
142
143

    BOOL nib_main_loaded;       /* main nibfile */
    BOOL nib_open_loaded;       /* open nibfile */
    BOOL nib_about_loaded;      /* about nibfile */
144
145
    BOOL nib_prefs_loaded;      /* preferences xibfile */
    BOOL nib_sprefs_loaded;      /* simple preferences xibfile */
146
147
148
149
    BOOL nib_coredialogs_loaded; /* CoreDialogs nibfile */
    BOOL nib_bookmarks_loaded;   /* Bookmarks nibfile */
    BOOL b_active_videoplayback;

150
151
152
153
154
155
156
157
158
    VLCMainMenu *_mainmenu;
    VLCPrefs *_prefs;
    VLCSimplePrefs *_sprefs;
    VLCOpen *_open;
    VLCCoreDialogProvider *_coredialogs;
    VLCBookmarks *_bookmarks;
    VLCCoreInteraction *_coreinteraction;
    ResumeDialogController *_resume_dialog;
    VLCInputManager *_input_manager;
159
    VLCPlaylist *_playlist;
160

161
    bool b_intf_terminating; /* Makes sure applicationWillTerminate will be called only once */
162
}
163

164
@end
165

166
/*****************************************************************************
Jérome Decoodt's avatar
Jérome Decoodt committed
167
 * VLCMain implementation
168
169
170
 *****************************************************************************/
@implementation VLCMain

171
172
173
#pragma mark -
#pragma mark Initialization

hartman's avatar
hartman committed
174
175
+ (VLCMain *)sharedInstance
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
176
    static VLCMain *sharedInstance = nil;
177
178
179
180
181
182
183
    static dispatch_once_t pred;

    dispatch_once(&pred, ^{
        sharedInstance = [VLCMain new];
    });

    return sharedInstance;
hartman's avatar
hartman committed
184
185
}

Jérome Decoodt's avatar
Jérome Decoodt committed
186
- (id)init
hartman's avatar
hartman committed
187
{
188
    self = [super init];
189

Felix Paul Kühne's avatar
Felix Paul Kühne committed
190
191
    if (self) {
        p_intf = NULL;
192

Felix Paul Kühne's avatar
Felix Paul Kühne committed
193
        [VLCApplication sharedApplication].delegate = self;
194

Felix Paul Kühne's avatar
Felix Paul Kühne committed
195
196
197
198
199
        /* announce our launch to a potential eyetv plugin */
        [[NSDistributedNotificationCenter defaultCenter] postNotificationName: @"VLCOSXGUIInit"
                                                                       object: @"VLCEyeTVSupport"
                                                                     userInfo: NULL
                                                           deliverImmediately: YES];
200

Felix Paul Kühne's avatar
Felix Paul Kühne committed
201
202
203
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"LiveUpdateTheMessagesPanel"];
        [defaults registerDefaults:appDefaults];
204

205
        _mainmenu = [[VLCMainMenu alloc] init];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
206
207
        _voutController = [[VLCVoutWindowController alloc] init];
    }
208

209
    return self;
hartman's avatar
hartman committed
210
211
}

212
213
- (void)setIntf: (intf_thread_t *)p_mainintf
{
hartman's avatar
hartman committed
214
215
216
    p_intf = p_mainintf;
}

217
218
- (intf_thread_t *)intf
{
hartman's avatar
hartman committed
219
220
221
    return p_intf;
}

222
223
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
224
225
226
    _playlist = [[VLCPlaylist alloc] init];
    [NSBundle loadNibNamed:@"MainWindow" owner:self];
    [[self mainWindow] makeKeyAndOrderFront:nil];
227
228
229
230
231
232
233
234
235
236
237
238
239
    _open = [[VLCOpen alloc] init];
    _coreinteraction = [VLCCoreInteraction sharedInstance];

    playlist_t * p_playlist = pl_Get(VLCIntf);
    PL_LOCK;
    items_at_launch = p_playlist->p_local_category->i_children;
    PL_UNLOCK;

#ifdef HAVE_SPARKLE
    [[SUUpdater sharedUpdater] setDelegate:self];
#endif
}

240
241
- (void)awakeFromNib
{
242
243
    if (!p_intf) return;
    var_Create(p_intf, "intf-change", VLC_VAR_BOOL);
244

245
    /* Check if we already did this once */
246
247
    if (nib_main_loaded)
        return;
248

249
    // TODO: take care of VLCIntf initialization order
250
    _input_manager = [[VLCInputManager alloc] initWithMain:self];
hartman's avatar
hartman committed
251

252
253
    var_AddCallback(p_intf->p_libvlc, "intf-toggle-fscontrol", ShowController, (__bridge void *)self);
    var_AddCallback(p_intf->p_libvlc, "intf-show", ShowController, (__bridge void *)self);
bigben's avatar
bigben committed
254

255
    playlist_t *p_playlist = pl_Get(p_intf);
256
257
    if ([NSApp currentSystemPresentationOptions] & NSApplicationPresentationFullScreen)
        var_SetBool(p_playlist, "fullscreen", YES);
258

David's avatar
David committed
259
    /* load our Shared Dialogs nib */
260
    [NSBundle loadNibNamed:@"SharedDialogs" owner: NSApp];
261

262
    _nativeFullscreenMode = var_InheritBool(p_intf, "macosx-nativefullscreenmode");
263

264
    if (config_GetInt(VLCIntf, "macosx-icon-change")) {
265
266
267
268
269
270
271
272
273
274
275
276
        /* 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]];

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

277
    nib_main_loaded = TRUE;
278
279
}

280
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
281
{
Rafaël Carré's avatar
Rafaël Carré committed
282
283
    launched = YES;

284
285
    if (!p_intf)
        return;
286

287
288
289
290
291
292
293
294
295
296
297
    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"];

298
    [_coreinteraction updateCurrentlyUsedHotkeys];
299

300
    [self removeOldPreferences];
301

302
303
304
305
    /* Handle sleep notification */
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:)
           name:NSWorkspaceWillSleepNotification object:nil];

306
307
308
309
    /* update the main window */
    [o_mainwindow updateWindow];
    [o_mainwindow updateTimeSlider];
    [o_mainwindow updateVolumeSlider];
310

311
312
313
314
    /* Hack: Playlist is started before the interface.
     * Thus, call additional updaters as we might miss these events if posted before
     * the callbacks are registered.
     */
315
    [_input_manager inputThreadChanged];
316
    [_playlist playbackModeUpdated];
317

318
319
    // respect playlist-autostart
    // note that PLAYLIST_PLAY will not stop any playback if already started
320
321
    playlist_t * p_playlist = pl_Get(VLCIntf);
    PL_LOCK;
322
    BOOL kidsAround = p_playlist->p_local_category->i_children != 0;
323
    if (kidsAround && var_GetBool(p_playlist, "playlist-autostart"))
324
325
        playlist_Control(p_playlist, PLAYLIST_PLAY, true);
    PL_UNLOCK;
326
327
}

328
329
330
#pragma mark -
#pragma mark Termination

331
332
- (BOOL)isTerminating
{
333
    return b_intf_terminating;
334
335
}

336
- (void)applicationWillTerminate:(NSNotification *)notification
337
{
338
    if (b_intf_terminating)
339
        return;
340
    b_intf_terminating = true;
341

342
    [_input_manager resumeItunesPlayback:nil];
343

344
345
    if (notification == nil)
        [[NSNotificationCenter defaultCenter] postNotificationName: NSApplicationWillTerminateNotification object: nil];
346

347
    playlist_t * p_playlist = pl_Get(p_intf);
348

349
    /* save current video and audio profiles */
350
    [[VLCVideoEffects sharedInstance] saveCurrentProfile];
351
    [[VLCAudioEffects sharedInstance] saveCurrentProfile];
352

353
    /* Save some interface state in configuration, at module quit */
354
355
356
    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"));
357

358
    msg_Dbg(p_intf, "Terminating");
359

360
361
    var_DelCallback(p_intf->p_libvlc, "intf-toggle-fscontrol", ShowController, (__bridge void *)self);
    var_DelCallback(p_intf->p_libvlc, "intf-show", ShowController, (__bridge void *)self);
362

363
    [[NSNotificationCenter defaultCenter] removeObserver: self];
Jérome Decoodt's avatar
Jérome Decoodt committed
364

365
    [_voutController.lock lock];
366
    // closes all open vouts
367
    _voutController = nil;
368
    [_voutController.lock unlock];
369

370
    /* unsubscribe from libvlc's debug messages */
371
    vlc_LogSet(p_intf->p_libvlc, NULL, NULL);
372

373
374
375
    /* write cached user defaults to disk */
    [[NSUserDefaults standardUserDefaults] synchronize];

376
377
    o_mainwindow = NULL;

378
    [self setIntf:nil];
hartman's avatar
hartman committed
379
380
}

381
382
#pragma mark -
#pragma mark Sparkle delegate
383

384
#ifdef HAVE_SPARKLE
385
386
387
/* received directly before the update gets installed, so let's shut down a bit */
- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update
{
388
    [NSApp activateIgnoringOtherApps:YES];
389
390
    [_coreinteraction stopListeningWithAppleRemote];
    [_coreinteraction stop];
391
392
}

393
394
395
396
397
398
399
400
/* don't be enthusiastic about an update if we currently play a video */
- (BOOL)updaterMayCheckForUpdates:(SUUpdater *)bundle
{
    if ([self activeVideoPlayback])
        return NO;

    return YES;
}
401
#endif
402

403
404
405
#pragma mark -
#pragma mark Other notification

406
407
408
409
/* Listen to the remote in exclusive mode, only when VLC is the active
   application */
- (void)applicationDidBecomeActive:(NSNotification *)aNotification
{
410
411
412
    if (!p_intf)
        return;
    if (var_InheritBool(p_intf, "macosx-appleremote") == YES)
413
        [_coreinteraction startListeningWithAppleRemote];
414
415
416
}
- (void)applicationDidResignActive:(NSNotification *)aNotification
{
417
418
    if (!p_intf)
        return;
419
    [_coreinteraction stopListeningWithAppleRemote];
420
421
}

422
423
424
/* Triggered when the computer goes to sleep */
- (void)computerWillSleep: (NSNotification *)notification
{
425
    [_coreinteraction pause];
426
427
}

428
#pragma mark -
429
#pragma mark File opening over dock icon
430

431
- (void)application:(NSApplication *)o_app openFiles:(NSArray *)o_names
432
{
433
434
435
436
    // Only add items here which are getting dropped to to the application icon
    // or are given at startup. If a file is passed via command line, libvlccore
    // will add the item, but cocoa also calls this function. In this case, the
    // invocation is ignored here.
Rafaël Carré's avatar
Rafaël Carré committed
437
438
439
440
441
442
443
444
445
446
447
    if (launched == NO) {
        if (items_at_launch) {
            int items = [o_names count];
            if (items > items_at_launch)
                items_at_launch = 0;
            else
                items_at_launch -= items;
            return;
        }
    }

448
    char *psz_uri = vlc_path2uri([[o_names firstObject] UTF8String], NULL);
David's avatar
David committed
449

450
    // try to add file as subtitle
451
452
453
    if ([o_names count] == 1 && psz_uri) {
        input_thread_t * p_input = pl_CurrentInput(VLCIntf);
        if (p_input) {
454
            int i_result = input_AddSubtitleOSD(p_input, [[o_names firstObject] UTF8String], true, true);
455
            vlc_object_release(p_input);
456
            if (i_result == VLC_SUCCESS) {
457
                free(psz_uri);
458
                return;
459
            }
460
461
        }
    }
462
    free(psz_uri);
463

464
465
    NSArray *o_sorted_names = [o_names sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
    NSMutableArray *o_result = [NSMutableArray arrayWithCapacity: [o_sorted_names count]];
466
    for (NSUInteger i = 0; i < [o_sorted_names count]; i++) {
467
        psz_uri = vlc_path2uri([[o_sorted_names objectAtIndex:i] UTF8String], "file");
468
        if (!psz_uri)
469
            continue;
470

471
        NSDictionary *o_dic = [NSDictionary dictionaryWithObject:toNSStr(psz_uri) forKey:@"ITEM_URL"];
472
        free(psz_uri);
473
        [o_result addObject: o_dic];
474
    }
475

476
    [[[VLCMain sharedInstance] playlist] addPlaylistItems:o_result];
477
478
479
480
}

/* When user click in the Dock icon our double click in the finder */
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)hasVisibleWindows
481
{
482
    if (!hasVisibleWindows)
483
        [o_mainwindow makeKeyAndOrderFront:self];
484
485
486
487

    return YES;
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
488
489
- (void)showFullscreenController
{
490
491
    // defer selector here (possibly another time) to ensure that keyWindow is set properly
    // (needed for NSApplicationDidBecomeActiveNotification)
492
    [o_mainwindow performSelectorOnMainThread:@selector(showFullscreenController) withObject:nil waitUntilDone:NO];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
493
494
}

495
496
- (void)setActiveVideoPlayback:(BOOL)b_value
{
497
498
    assert([NSThread isMainThread]);

499
    b_active_videoplayback = b_value;
500
    if (o_mainwindow) {
501
        [o_mainwindow setVideoplayEnabled];
502
    }
503
504

    // update sleep blockers
505
    [_input_manager playbackStatusUpdated];
506
507
}

508
509
510
#pragma mark -
#pragma mark Other objects getters

511
- (VLCMainMenu *)mainMenu
512
{
513
    return _mainmenu;
514
515
}

516
- (VLCMainWindow *)mainWindow
517
518
519
520
{
    return o_mainwindow;
}

521
- (VLCInputManager *)inputManager
522
{
523
524
525
526
527
528
529
    return _input_manager;
}

- (VLCBookmarks *)bookmarks
{
    if (!_bookmarks)
        _bookmarks = [[VLCBookmarks alloc] init];
530

531
    if (!nib_bookmarks_loaded)
532
        nib_bookmarks_loaded = [NSBundle loadNibNamed:@"Bookmarks" owner:_bookmarks];
533

534
    return _bookmarks;
535
536
}

537
- (VLCOpen *)open
538
539
{
    if (!nib_open_loaded)
540
        nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner: _open];
541

542
    return _open;
543
544
}

545
- (VLCSimplePrefs *)simplePreferences
546
{
547
548
    if (!_sprefs)
        _sprefs = [[VLCSimplePrefs alloc] init];
549

550
551
    if (!nib_sprefs_loaded)
        nib_sprefs_loaded = [NSBundle loadNibNamed:@"SimplePreferences" owner: _sprefs];
552

553
    return _sprefs;
554
555
}

556
- (VLCPrefs *)preferences
557
{
558
559
    if (!_prefs)
        _prefs = [[VLCPrefs alloc] init];
560

561
    if (!nib_prefs_loaded)
562
        nib_prefs_loaded = [NSBundle loadNibNamed:@"Preferences" owner: _prefs];
563

564
    return _prefs;
565
566
}

567
- (VLCPlaylist *)playlist
hartman's avatar
hartman committed
568
{
569
    return _playlist;
hartman's avatar
hartman committed
570
571
}

572
- (VLCCoreDialogProvider *)coreDialogProvider
573
{
574
    _coredialogs = [VLCCoreDialogProvider sharedInstance];
David's avatar
David committed
575
    if (!nib_coredialogs_loaded) {
576
        nib_coredialogs_loaded = [NSBundle loadNibNamed:@"CoreDialogs" owner: _coredialogs];
David's avatar
David committed
577
    }
578

579
    return _coredialogs;
580
581
}

582
583
- (ResumeDialogController *)resumeDialog
{
584
585
    if (!_resume_dialog)
        _resume_dialog = [[ResumeDialogController alloc] init];
586

587
    return _resume_dialog;
588
589
}

590
- (BOOL)activeVideoPlayback
591
{
592
    return b_active_videoplayback;
hartman's avatar
hartman committed
593
594
}

595
@end
596

597
598
599
/*****************************************************************************
 * VLCApplication interface
 *****************************************************************************/
600

601
@implementation VLCApplication
602
603
604
// when user selects the quit menu from dock it sends a terminate:
// but we need to send a stop: to properly exits libvlc.
// However, we are not able to change the action-method sent by this standard menu item.
605
// thus we override terminate: to send a stop:
606
// see [af97f24d528acab89969d6541d83f17ce1ecd580] that introduced the removal of setjmp() and longjmp()
607
608
- (void)terminate:(id)sender
{
609
    [self activateIgnoringOtherApps:YES];
610
611
612
    [self stop:sender];
}

613
@end