VLCMain.m 17.3 KB
Newer Older
1
/*****************************************************************************
2
 * VLCMain.m: MacOS X interface module
3
 *****************************************************************************
4
 * Copyright (C) 2002-2016 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 39
#include <stdatomic.h>

40
#include <vlc_common.h>
41
#include <vlc_actions.h>
42
#include <vlc_dialog.h>
43
#include <vlc_url.h>
44 45
#include <vlc_variables.h>

46
#import "CompatibilityFixes.h"
47
#import "VLCInputManager.h"
48
#import "VLCMainMenu.h"
49
#import "VLCVoutView.h"
50
#import "prefs.h"
51 52
#import "VLCPlaylist.h"
#import "VLCPlaylistInfo.h"
53
#import "VLCPlaylistInfo.h"
54
#import "VLCOpenWindowController.h"
55
#import "VLCBookmarksWindowController.h"
56
#import "VLCCoreDialogProvider.h"
57
#import "VLCSimplePrefsController.h"
58
#import "VLCCoreInteraction.h"
59
#import "VLCTrackSynchronizationWindowController.h"
60
#import "VLCExtensionsManager.h"
61
#import "VLCResumeDialogController.h"
62
#import "VLCLogWindowController.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 96 97
        @try {
            [VLCApplication sharedApplication];
            [VLCMain sharedInstance];
98

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

102 103 104 105 106 107
            msg_Dbg(p_intf, "Finished loading macosx interface");
            return VLC_SUCCESS;
        } @catch (NSException *exception) {
            msg_Err(p_intf, "Loading the macosx interface failed. Do you have a valid window server?");
            return VLC_EGENERIC;
        }
108
    }
109 110
}

111 112
void CloseIntf (vlc_object_t *p_this)
{
113 114 115
    @autoreleasepool {
        msg_Dbg(p_this, "Closing macosx interface");
        [[VLCMain sharedInstance] applicationWillTerminate:nil];
116
        [VLCMain killInstance];
117 118

        p_interface_thread = nil;
119
    }
120 121
}

122 123
#pragma mark -
#pragma mark Variables Callback
124

125 126 127 128 129
/*****************************************************************************
 * 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
 *****************************************************************************/
130 131
static int ShowController(vlc_object_t *p_this, const char *psz_variable,
                     vlc_value_t old_val, vlc_value_t new_val, void *param)
132
{
133 134 135
    @autoreleasepool {
        dispatch_async(dispatch_get_main_queue(), ^{

136
            intf_thread_t * p_intf = getIntf();
137 138 139 140 141 142 143 144 145
            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];
            }
146

147
        });
148

149 150
        return VLC_SUCCESS;
    }
151 152
}

153
#pragma mark -
154 155
#pragma mark Private

156
@interface VLCMain ()
157
#ifdef HAVE_SPARKLE
158
    <SUUpdaterDelegate>
159
#endif
160
{
161 162
    intf_thread_t *p_intf;
    BOOL launched;
163 164 165

    BOOL b_active_videoplayback;

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

186
    bool b_intf_terminating; /* Makes sure applicationWillTerminate will be called only once */
187
}
188

189
@end
190

191
/*****************************************************************************
192
 * VLCMain implementation
193 194 195
 *****************************************************************************/
@implementation VLCMain

196 197 198
#pragma mark -
#pragma mark Initialization

199 200
static VLCMain *sharedInstance = nil;

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

    return sharedInstance;
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
209 210
}

211 212 213 214 215
+ (void)killInstance
{
    sharedInstance = nil;
}

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

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

224
        _input_manager = [[VLCInputManager alloc] initWithMain:self];
225 226 227

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

231
        _mainmenu = [[VLCMainMenu alloc] init];
232
        _statusBarIcon = [[VLCStatusBarIcon  alloc] init];
233

234 235 236 237 238
        _voutController = [[VLCVoutWindowController alloc] init];
        _playlist = [[VLCPlaylist alloc] init];

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

239 240
        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);
241

242 243 244 245
        // Load them here already to apply stored profiles
        _videoEffectsPanel = [[VLCVideoEffectsWindowController alloc] init];
        _audioEffectsPanel = [[VLCAudioEffectsWindowController alloc] init];

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
        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)
262
                [[VLCApplication sharedApplication] setApplicationIconImage: [NSImage imageNamed:@"VLC-Xmas"]];
263 264
            else
                [[VLCApplication sharedApplication] setApplicationIconImage: [NSImage imageNamed:@"VLC"]];
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 280 281 282 283 284
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
    _coreinteraction = [VLCCoreInteraction sharedInstance];

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

285
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
286
{
287 288
    launched = YES;

289 290
    if (!p_intf)
        return;
291

292
    [_coreinteraction updateCurrentlyUsedHotkeys];
293

294
    [self migrateOldPreferences];
295

296 297 298 299
    /* Handle sleep notification */
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:)
           name:NSWorkspaceWillSleepNotification object:nil];

300
    /* update the main window */
301 302 303
    [[self mainWindow] updateWindow];
    [[self mainWindow] updateTimeSlider];
    [[self mainWindow] updateVolumeSlider];
304

305 306
    // respect playlist-autostart
    // note that PLAYLIST_PLAY will not stop any playback if already started
307
    playlist_t * p_playlist = pl_Get(getIntf());
308
    PL_LOCK;
309
    BOOL kidsAround = p_playlist->p_playing->i_children != 0;
310
    if (kidsAround && var_GetBool(p_playlist, "playlist-autostart"))
311 312
        playlist_Control(p_playlist, PLAYLIST_PLAY, true);
    PL_UNLOCK;
313 314
}

315 316 317
#pragma mark -
#pragma mark Termination

318 319
- (BOOL)isTerminating
{
320
    return b_intf_terminating;
321 322
}

323
- (void)applicationWillTerminate:(NSNotification *)notification
324
{
325
    if (b_intf_terminating)
326
        return;
327
    b_intf_terminating = true;
328

329
    [_input_manager onPlaybackHasEnded:nil];
330

331 332
    if (notification == nil)
        [[NSNotificationCenter defaultCenter] postNotificationName: NSApplicationWillTerminateNotification object: nil];
333

334
    playlist_t * p_playlist = pl_Get(p_intf);
335

336
    /* save current video and audio profiles */
337
    [[self videoEffectsPanel] saveCurrentProfileAtTerminate];
338
    [[self audioEffectsPanel] saveCurrentProfileAtTerminate];
339

340
    /* Save some interface state in configuration, at module quit */
341 342 343
    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"));
344

345
    msg_Dbg(p_intf, "Terminating");
346

347 348
    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);
349

350
    [[NSNotificationCenter defaultCenter] removeObserver: self];
351

352
    // closes all open vouts
353
    _voutController = nil;
354

355 356
    /* write cached user defaults to disk */
    [[NSUserDefaults standardUserDefaults] synchronize];
Derk-Jan Hartman's avatar
Derk-Jan Hartman committed
357 358
}

359 360
#pragma mark -
#pragma mark Sparkle delegate
361

362
#ifdef HAVE_SPARKLE
363 364 365
/* received directly before the update gets installed, so let's shut down a bit */
- (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update
{
366
    [NSApp activateIgnoringOtherApps:YES];
367 368
    [_coreinteraction stopListeningWithAppleRemote];
    [_coreinteraction stop];
369 370
}

371 372 373 374 375 376 377 378
/* don't be enthusiastic about an update if we currently play a video */
- (BOOL)updaterMayCheckForUpdates:(SUUpdater *)bundle
{
    if ([self activeVideoPlayback])
        return NO;

    return YES;
}
379
#endif
380

381 382 383
#pragma mark -
#pragma mark Other notification

384 385 386 387
/* Listen to the remote in exclusive mode, only when VLC is the active
   application */
- (void)applicationDidBecomeActive:(NSNotification *)aNotification
{
388 389 390
    if (!p_intf)
        return;
    if (var_InheritBool(p_intf, "macosx-appleremote") == YES)
391
        [_coreinteraction startListeningWithAppleRemote];
392 393 394
}
- (void)applicationDidResignActive:(NSNotification *)aNotification
{
395 396
    if (!p_intf)
        return;
397
    [_coreinteraction stopListeningWithAppleRemote];
398 399
}

400 401 402
/* Triggered when the computer goes to sleep */
- (void)computerWillSleep: (NSNotification *)notification
{
403
    [_coreinteraction pause];
404 405
}

406
#pragma mark -
407
#pragma mark File opening over dock icon
408

409
- (void)application:(NSApplication *)o_app openFiles:(NSArray *)o_names
410
{
411 412 413 414
    // 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.
415
    NSArray *resultItems = o_names;
416
    if (launched == NO) {
417 418 419 420 421 422 423
        NSArray *launchArgs = [[NSProcessInfo processInfo] arguments];

        if (launchArgs) {
            NSSet *launchArgsSet = [NSSet setWithArray:launchArgs];
            NSMutableSet *itemSet = [NSMutableSet setWithArray:o_names];
            [itemSet minusSet:launchArgsSet];
            resultItems = [itemSet allObjects];
424 425 426
        }
    }

427
    NSArray *o_sorted_names = [resultItems sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
428
    NSMutableArray *o_result = [NSMutableArray arrayWithCapacity: [o_sorted_names count]];
429
    for (NSUInteger i = 0; i < [o_sorted_names count]; i++) {
430
        char *psz_uri = vlc_path2uri([[o_sorted_names objectAtIndex:i] UTF8String], "file");
431
        if (!psz_uri)
432
            continue;
433

434
        NSDictionary *o_dic = [NSDictionary dictionaryWithObject:toNSStr(psz_uri) forKey:@"ITEM_URL"];
435
        [o_result addObject: o_dic];
436
        free(psz_uri);
437
    }
438

439
    [[[VLCMain sharedInstance] playlist] addPlaylistItems:o_result tryAsSubtitle:YES];
440 441 442 443
}

/* When user click in the Dock icon our double click in the finder */
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)hasVisibleWindows
444
{
445
    if (!hasVisibleWindows)
446
        [[self mainWindow] makeKeyAndOrderFront:self];
447 448 449 450

    return YES;
}

451 452
- (void)showFullscreenController
{
453 454
    // defer selector here (possibly another time) to ensure that keyWindow is set properly
    // (needed for NSApplicationDidBecomeActiveNotification)
455
    [[self mainWindow] performSelectorOnMainThread:@selector(showFullscreenController) withObject:nil waitUntilDone:NO];
456 457
}

458 459
- (void)setActiveVideoPlayback:(BOOL)b_value
{
460 461
    assert([NSThread isMainThread]);

462
    b_active_videoplayback = b_value;
463 464
    if ([self mainWindow]) {
        [[self mainWindow] setVideoplayEnabled];
465
    }
466 467

    // update sleep blockers
468
    [_input_manager playbackStatusUpdated];
469 470
}

471 472 473
#pragma mark -
#pragma mark Other objects getters

474
- (VLCMainMenu *)mainMenu
475
{
476
    return _mainmenu;
477 478
}

479 480 481 482 483
- (VLCStatusBarIcon *)statusBarIcon
{
    return _statusBarIcon;
}

484
- (VLCMainWindow *)mainWindow
485
{
486
    return (VLCMainWindow *)[_mainWindowController window];
487 488
}

489
- (VLCInputManager *)inputManager
490
{
491 492 493
    return _input_manager;
}

494
- (VLCExtensionsManager *)extensionsManager
495 496 497 498
{
    return _extensionsManager;
}

499
- (VLCLogWindowController *)debugMsgPanel
500 501
{
    if (!_messagePanelController)
502
        _messagePanelController = [[VLCLogWindowController alloc] init];
503 504 505 506

    return _messagePanelController;
}

507
- (VLCTrackSynchronizationWindowController *)trackSyncPanel
508 509
{
    if (!_trackSyncPanel)
510
        _trackSyncPanel = [[VLCTrackSynchronizationWindowController alloc] init];
511 512 513 514

    return _trackSyncPanel;
}

515
- (VLCAudioEffectsWindowController *)audioEffectsPanel
516 517 518 519
{
    return _audioEffectsPanel;
}

520
- (VLCVideoEffectsWindowController *)videoEffectsPanel
521 522 523 524
{
    return _videoEffectsPanel;
}

525 526 527 528 529 530 531 532
- (VLCInfo *)currentMediaInfoPanel;
{
    if (!_currentMediaInfoPanel)
        _currentMediaInfoPanel = [[VLCInfo alloc] init];

    return _currentMediaInfoPanel;
}

533
- (VLCBookmarksWindowController *)bookmarks
534 535
{
    if (!_bookmarks)
536
        _bookmarks = [[VLCBookmarksWindowController alloc] init];
537

538
    return _bookmarks;
539 540
}

541
- (VLCOpenWindowController *)open
542
{
543
    if (!_open)
544
        _open = [[VLCOpenWindowController alloc] init];
545

546
    return _open;
547 548
}

549
- (VLCConvertAndSaveWindowController *)convertAndSaveWindow
550 551
{
    if (_convertAndSaveWindow == nil)
552
        _convertAndSaveWindow = [[VLCConvertAndSaveWindowController alloc] init];
553 554 555 556

    return _convertAndSaveWindow;
}

557
- (VLCSimplePrefsController *)simplePreferences
558
{
559
    if (!_sprefs)
560
        _sprefs = [[VLCSimplePrefsController alloc] init];
561

562
    return _sprefs;
563 564
}

565
- (VLCPrefs *)preferences
566
{
567 568
    if (!_prefs)
        _prefs = [[VLCPrefs alloc] init];
569

570
    return _prefs;
571 572
}

573
- (VLCPlaylist *)playlist
574
{
575
    return _playlist;
576 577
}

578
- (VLCCoreDialogProvider *)coreDialogProvider
579
{
580
    return _coredialogs;
581 582
}

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

588
    return _resume_dialog;
589 590
}

591
- (BOOL)activeVideoPlayback
592
{
593
    return b_active_videoplayback;
594 595
}

596
@end