VLCMain.m 18.1 KB
Newer Older
1
/*****************************************************************************
2
 * VLCMain.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
Antoine Cellerier's avatar
Antoine Cellerier 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
#import "VLCMain.h"
35

36 37
#include <stdlib.h>                                      /* malloc(), free() */
#include <string.h>
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 44
#include <vlc_variables.h>

45
#import "CompatibilityFixes.h"
46
#import "VLCInputManager.h"
47
#import "VLCMainMenu.h"
48
#import "VLCVoutView.h"
49
#import "prefs.h"
50 51
#import "VLCPlaylist.h"
#import "VLCPlaylistInfo.h"
52
#import "VLCPlaylistInfo.h"
53
#import "VLCOpenWindowController.h"
54
#import "VLCBookmarksWindowController.h"
55
#import "VLCCoreDialogProvider.h"
56
#import "simple_prefs.h"
57
#import "VLCCoreInteraction.h"
58
#import "VLCTrackSynchronizationWindowController.h"
59
#import "VLCExtensionsManager.h"
60
#import "BWQuincyManager.h"
61
#import "VLCResumeDialogController.h"
62
#import "VLCDebugMessageWindowController.h"
63
#import "VLCConvertAndSaveWindowController.h"
64

65
#import "VLCVideoEffectsWindowController.h"
66
#import "VLCAudioEffectsWindowController.h"
67
#import "VLCMain+OldPrefs.h"
68
#import "VLCApplication.h"
69

70
#ifdef HAVE_SPARKLE
71
#import <Sparkle/Sparkle.h>                 /* we're the update delegate */
72
#endif
73

74
#pragma mark -
75
#pragma mark VLC Interface Object Callbacks
76

77 78 79
/*****************************************************************************
 * OpenIntf: initialize interface
 *****************************************************************************/
80 81 82 83 84 85 86 87

static intf_thread_t *p_interface_thread;

intf_thread_t *getIntf()
{
    return p_interface_thread;
}

88
int OpenIntf (vlc_object_t *p_this)
89
{
90 91
    @autoreleasepool {
        intf_thread_t *p_intf = (intf_thread_t*) p_this;
92
        p_interface_thread = p_intf;
93
        msg_Dbg(p_intf, "Starting macosx interface");
94

95
        [VLCApplication sharedApplication];
96
        [VLCMain sharedInstance];
97

98
        [NSBundle loadNibNamed:@"MainMenu" owner:[[VLCMain sharedInstance] mainMenu]];
99
        [[[VLCMain sharedInstance] mainWindow] makeKeyAndOrderFront:nil];
100

101
        msg_Dbg(p_intf, "Finished loading macosx interface");
102 103
        return VLC_SUCCESS;
    }
104 105
}

106 107
void CloseIntf (vlc_object_t *p_this)
{
108 109 110
    @autoreleasepool {
        msg_Dbg(p_this, "Closing macosx interface");
        [[VLCMain sharedInstance] applicationWillTerminate:nil];
111
        [VLCMain killInstance];
112 113

        p_interface_thread = nil;
114
    }
115 116
}

117 118
#pragma mark -
#pragma mark Variables Callback
119

120 121 122 123 124
/*****************************************************************************
 * 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
 *****************************************************************************/
125 126
static int ShowController(vlc_object_t *p_this, const char *psz_variable,
                     vlc_value_t old_val, vlc_value_t new_val, void *param)
127
{
128 129 130
    @autoreleasepool {
        dispatch_async(dispatch_get_main_queue(), ^{

131
            intf_thread_t * p_intf = getIntf();
132 133 134 135 136 137 138 139 140
            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];
            }
141

142
        });
143

144 145
        return VLC_SUCCESS;
    }
146 147
}

148
#pragma mark -
149 150
#pragma mark Private

151 152 153 154 155
@interface VLCMain () <BWQuincyManagerDelegate
#ifdef HAVE_SPARKLE
    , SUUpdaterDelegate
#endif
>
156
{
157 158 159
    intf_thread_t *p_intf;
    BOOL launched;
    int items_at_launch;
160 161 162

    BOOL b_active_videoplayback;

163
    NSWindowController *_mainWindowController;
164 165 166
    VLCMainMenu *_mainmenu;
    VLCPrefs *_prefs;
    VLCSimplePrefs *_sprefs;
167
    VLCOpenWindowController *_open;
168
    VLCCoreDialogProvider *_coredialogs;
169
    VLCBookmarksWindowController *_bookmarks;
170
    VLCCoreInteraction *_coreinteraction;
171
    VLCResumeDialogController *_resume_dialog;
172
    VLCInputManager *_input_manager;
173
    VLCPlaylist *_playlist;
174
    VLCDebugMessageWindowController *_messagePanelController;
175
    VLCStatusBarIcon *_statusBarIcon;
176
    VLCTrackSynchronizationWindowController *_trackSyncPanel;
177
    VLCAudioEffectsWindowController *_audioEffectsPanel;
178
    VLCVideoEffectsWindowController *_videoEffectsPanel;
179
    VLCConvertAndSaveWindowController *_convertAndSaveWindow;
180
    VLCExtensionsManager *_extensionsManager;
181
    VLCInfo *_currentMediaInfoPanel;
182

183
    bool b_intf_terminating; /* Makes sure applicationWillTerminate will be called only once */
184
}
185

186
@end
187

188
/*****************************************************************************
189
 * VLCMain implementation
190 191 192
 *****************************************************************************/
@implementation VLCMain

193 194 195
#pragma mark -
#pragma mark Initialization

196 197
static VLCMain *sharedInstance = nil;

198
+ (VLCMain *)sharedInstance;
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
199
{
200 201
    static dispatch_once_t pred;
    dispatch_once(&pred, ^{
202
        sharedInstance = [[VLCMain alloc] init];
203 204 205
    });

    return sharedInstance;
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
206 207
}

208 209 210 211 212
+ (void)killInstance
{
    sharedInstance = nil;
}

213
- (id)init
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
214
{
215
    self = [super init];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
216
    if (self) {
217
        p_intf = getIntf();
218

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

221
        _input_manager = [[VLCInputManager alloc] initWithMain:self];
222 223 224

        // first initalize extensions dialog provider, then core dialog
        // provider which will register both at the core
225
        _extensionsManager = [[VLCExtensionsManager alloc] init];
226
        _coredialogs = [[VLCCoreDialogProvider alloc] init];
227

228
        _mainmenu = [[VLCMainMenu alloc] init];
229
        _statusBarIcon = [[VLCStatusBarIcon  alloc] init];
230

231 232 233 234 235 236 237
        _voutController = [[VLCVoutWindowController alloc] init];
        _playlist = [[VLCPlaylist alloc] init];

        _mainWindowController = [[NSWindowController alloc] initWithWindowNibName:@"MainWindow"];

        var_Create(p_intf, "intf-change", VLC_VAR_BOOL);

238 239
        var_AddCallback(p_intf->obj.libvlc, "intf-toggle-fscontrol", ShowController, (__bridge void *)self);
        var_AddCallback(p_intf->obj.libvlc, "intf-show", ShowController, (__bridge void *)self);
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259

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

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

        if (var_InheritInteger(p_intf, "macosx-icon-change")) {
            /* 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"]];
        }

Felix Paul Kühne's avatar
Felix Paul Kühne committed
260 261 262 263 264
        /* announce our launch to a potential eyetv plugin */
        [[NSDistributedNotificationCenter defaultCenter] postNotificationName: @"VLCOSXGUIInit"
                                                                       object: @"VLCEyeTVSupport"
                                                                     userInfo: NULL
                                                           deliverImmediately: YES];
265

Felix Paul Kühne's avatar
Felix Paul Kühne committed
266
    }
267

268
    return self;
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
269 270
}

271
- (void)dealloc
272
{
273
    msg_Dbg(getIntf(), "Deinitializing VLCMain object");
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
274 275
}

276 277 278 279
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
    _coreinteraction = [VLCCoreInteraction sharedInstance];

280
    playlist_t * p_playlist = pl_Get(getIntf());
281
    PL_LOCK;
282
    items_at_launch = p_playlist->p_playing->i_children;
283 284 285 286 287 288 289
    PL_UNLOCK;

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

290
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
291
{
292 293
    launched = YES;

294 295
    if (!p_intf)
        return;
296

297 298 299 300 301 302 303 304 305 306 307
    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"];

308
    [_coreinteraction updateCurrentlyUsedHotkeys];
309

310
    [self removeOldPreferences];
311

312 313 314 315
    /* Handle sleep notification */
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:)
           name:NSWorkspaceWillSleepNotification object:nil];

316
    /* update the main window */
317 318 319
    [[self mainWindow] updateWindow];
    [[self mainWindow] updateTimeSlider];
    [[self mainWindow] updateVolumeSlider];
320

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

331 332 333
#pragma mark -
#pragma mark Termination

334 335
- (BOOL)isTerminating
{
336
    return b_intf_terminating;
337 338
}

339
- (void)applicationWillTerminate:(NSNotification *)notification
340
{
341
    if (b_intf_terminating)
342
        return;
343
    b_intf_terminating = true;
344

345
    [_input_manager resumeItunesPlayback:nil];
346

347 348
    if (notification == nil)
        [[NSNotificationCenter defaultCenter] postNotificationName: NSApplicationWillTerminateNotification object: nil];
349

350
    playlist_t * p_playlist = pl_Get(p_intf);
351

352
    /* save current video and audio profiles */
353
    [[self videoEffectsPanel] saveCurrentProfile];
354
    [[self audioEffectsPanel] saveCurrentProfile];
355

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

361
    msg_Dbg(p_intf, "Terminating");
362

363 364
    var_DelCallback(p_intf->obj.libvlc, "intf-toggle-fscontrol", ShowController, (__bridge void *)self);
    var_DelCallback(p_intf->obj.libvlc, "intf-show", ShowController, (__bridge void *)self);
365

366
    [[NSNotificationCenter defaultCenter] removeObserver: self];
367

368
    [_voutController.lock lock];
369
    // closes all open vouts
370
    _voutController = nil;
371
    [_voutController.lock unlock];
372

373 374
    /* write cached user defaults to disk */
    [[NSUserDefaults standardUserDefaults] synchronize];
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
375 376
}

377 378
#pragma mark -
#pragma mark Sparkle delegate
379

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

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

    return YES;
}
397
#endif
398

399 400 401
#pragma mark -
#pragma mark Other notification

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

418 419 420
/* Triggered when the computer goes to sleep */
- (void)computerWillSleep: (NSNotification *)notification
{
421
    [_coreinteraction pause];
422 423
}

424
#pragma mark -
425
#pragma mark File opening over dock icon
426

427
- (void)application:(NSApplication *)o_app openFiles:(NSArray *)o_names
428
{
429 430 431 432
    // 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.
433 434 435 436 437 438 439 440 441 442 443
    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;
        }
    }

444 445
    NSArray *o_sorted_names = [o_names sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
    NSMutableArray *o_result = [NSMutableArray arrayWithCapacity: [o_sorted_names count]];
446
    for (NSUInteger i = 0; i < [o_sorted_names count]; i++) {
447
        char *psz_uri = vlc_path2uri([[o_sorted_names objectAtIndex:i] UTF8String], "file");
448
        if (!psz_uri)
449
            continue;
450

451
        NSDictionary *o_dic = [NSDictionary dictionaryWithObject:toNSStr(psz_uri) forKey:@"ITEM_URL"];
452
        [o_result addObject: o_dic];
453
        free(psz_uri);
454
    }
455

456
    [[[VLCMain sharedInstance] playlist] addPlaylistItems:o_result tryAsSubtitle:YES];
457 458 459 460
}

/* When user click in the Dock icon our double click in the finder */
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)hasVisibleWindows
461
{
462
    if (!hasVisibleWindows)
463
        [[self mainWindow] makeKeyAndOrderFront:self];
464 465 466 467

    return YES;
}

468 469
- (void)showFullscreenController
{
470 471
    // defer selector here (possibly another time) to ensure that keyWindow is set properly
    // (needed for NSApplicationDidBecomeActiveNotification)
472
    [[self mainWindow] performSelectorOnMainThread:@selector(showFullscreenController) withObject:nil waitUntilDone:NO];
473 474
}

475 476
- (void)setActiveVideoPlayback:(BOOL)b_value
{
477 478
    assert([NSThread isMainThread]);

479
    b_active_videoplayback = b_value;
480 481
    if ([self mainWindow]) {
        [[self mainWindow] setVideoplayEnabled];
482
    }
483 484

    // update sleep blockers
485
    [_input_manager playbackStatusUpdated];
486 487
}

488 489 490
#pragma mark -
#pragma mark Other objects getters

491
- (VLCMainMenu *)mainMenu
492
{
493
    return _mainmenu;
494 495
}

496 497 498 499 500
- (VLCStatusBarIcon *)statusBarIcon
{
    return _statusBarIcon;
}

501
- (VLCMainWindow *)mainWindow
502
{
503
    return (VLCMainWindow *)[_mainWindowController window];
504 505
}

506
- (VLCInputManager *)inputManager
507
{
508 509 510
    return _input_manager;
}

511
- (VLCExtensionsManager *)extensionsManager
512 513 514 515
{
    return _extensionsManager;
}

516
- (VLCDebugMessageWindowController *)debugMsgPanel
517 518
{
    if (!_messagePanelController)
519
        _messagePanelController = [[VLCDebugMessageWindowController alloc] init];
520 521 522 523

    return _messagePanelController;
}

524
- (VLCTrackSynchronizationWindowController *)trackSyncPanel
525 526
{
    if (!_trackSyncPanel)
527
        _trackSyncPanel = [[VLCTrackSynchronizationWindowController alloc] init];
528 529 530 531

    return _trackSyncPanel;
}

532
- (VLCAudioEffectsWindowController *)audioEffectsPanel
533 534
{
    if (!_audioEffectsPanel)
535
        _audioEffectsPanel = [[VLCAudioEffectsWindowController alloc] init];
536 537 538 539

    return _audioEffectsPanel;
}

540
- (VLCVideoEffectsWindowController *)videoEffectsPanel
541 542
{
    if (!_videoEffectsPanel)
543
        _videoEffectsPanel = [[VLCVideoEffectsWindowController alloc] init];
544 545 546 547

    return _videoEffectsPanel;
}

548 549 550 551 552 553 554 555
- (VLCInfo *)currentMediaInfoPanel;
{
    if (!_currentMediaInfoPanel)
        _currentMediaInfoPanel = [[VLCInfo alloc] init];

    return _currentMediaInfoPanel;
}

556
- (VLCBookmarksWindowController *)bookmarks
557 558
{
    if (!_bookmarks)
559
        _bookmarks = [[VLCBookmarksWindowController alloc] init];
560

561
    return _bookmarks;
562 563
}

564
- (VLCOpenWindowController *)open
565
{
566
    if (!_open)
567
        _open = [[VLCOpenWindowController alloc] init];
568

569
    return _open;
570 571
}

572
- (VLCConvertAndSaveWindowController *)convertAndSaveWindow
573 574
{
    if (_convertAndSaveWindow == nil)
575
        _convertAndSaveWindow = [[VLCConvertAndSaveWindowController alloc] init];
576 577 578 579

    return _convertAndSaveWindow;
}

580
- (VLCSimplePrefs *)simplePreferences
581
{
582 583
    if (!_sprefs)
        _sprefs = [[VLCSimplePrefs alloc] init];
584

585
    return _sprefs;
586 587
}

588
- (VLCPrefs *)preferences
589
{
590 591
    if (!_prefs)
        _prefs = [[VLCPrefs alloc] init];
592

593
    return _prefs;
594 595
}

596
- (VLCPlaylist *)playlist
597
{
598
    return _playlist;
599 600
}

601
- (VLCCoreDialogProvider *)coreDialogProvider
602
{
603
    return _coredialogs;
604 605
}

606
- (VLCResumeDialogController *)resumeDialog
607
{
608
    if (!_resume_dialog)
609
        _resume_dialog = [[VLCResumeDialogController alloc] init];
610

611
    return _resume_dialog;
612 613
}

614
- (BOOL)activeVideoPlayback
615
{
616
    return b_active_videoplayback;
617 618
}

619
@end