X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fgui%2Fmacosx%2Fintf.m;h=7ff668c158815214990040f08f24fb80c3fe5b8f;hb=984aadb5ca4f9a8bf01dffae42440320a1ea0f13;hp=6a3a92d5cf8f0cb2c3a49d2fa67f89a78c70b46e;hpb=0f395d9fd7dbd2cca64ac546585fea2010f013ca;p=vlc diff --git a/modules/gui/macosx/intf.m b/modules/gui/macosx/intf.m index 6a3a92d5cf..7ff668c158 100644 --- a/modules/gui/macosx/intf.m +++ b/modules/gui/macosx/intf.m @@ -5,9 +5,9 @@ * $Id$ * * Authors: Jon Lech Johansen - * Christophe Massiot * Derk-Jan Hartman * Felix Paul Kühne + * Pierre d'Herbemont * David Fuhrmann * * This program is free software; you can redistribute it and/or modify @@ -33,7 +33,6 @@ #endif #include /* malloc(), free() */ -#include /* for MAXPATHLEN */ #include #include #include @@ -46,7 +45,6 @@ #import "CompatibilityFixes.h" #import "intf.h" -#import "StringUtility.h" #import "MainMenu.h" #import "VideoView.h" #import "prefs.h" @@ -62,16 +60,17 @@ #import "simple_prefs.h" #import "CoreInteraction.h" #import "TrackSynchronization.h" -#import "VLCVoutWindowController.h" #import "ExtensionsManager.h" +#import "BWQuincyManager.h" +#import "ControlsBar.h" #import "VideoEffects.h" #import "AudioEffects.h" -#import /* for crashlog send mechanism */ #import /* we're the update delegate */ #import "iTunes.h" +#import "Spotify.h" /***************************************************************************** * Local prototypes. @@ -82,18 +81,19 @@ static void updateProgressPanel (void *, const char *, float); static bool checkProgressPanel (void *); static void destroyProgressPanel (void *); -static void MsgCallback(void *data, int type, const msg_item_t *item, const char *format, va_list ap); - 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 *); -static int PlaylistUpdated(vlc_object_t *, const char *, - vlc_value_t, vlc_value_t, void *); +static int PLItemUpdated(vlc_object_t *, const char *, + vlc_value_t, vlc_value_t, void *); + 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 *); +static int BossCallback(vlc_object_t *, const char *, + vlc_value_t, vlc_value_t, void *); #pragma mark - #pragma mark VLC Interface Object Callbacks @@ -107,45 +107,39 @@ int OpenIntf (vlc_object_t *p_this) [VLCApplication sharedApplication]; intf_thread_t *p_intf = (intf_thread_t*) p_this; - - p_intf->p_sys = malloc(sizeof(intf_sys_t)); - if (p_intf->p_sys == NULL) - return VLC_ENOMEM; - - memset(p_intf->p_sys, 0, sizeof(*p_intf->p_sys)); - - /* subscribe to LibVLCCore's messages */ - vlc_LogSet(p_this->p_libvlc, MsgCallback, NULL); - Run(p_intf); [o_pool release]; return VLC_SUCCESS; } -/***************************************************************************** - * CloseIntf: destroy interface - *****************************************************************************/ -void CloseIntf (vlc_object_t *p_this) -{ - intf_thread_t *p_intf = (intf_thread_t*) p_this; - - free(p_intf->p_sys); -} +static NSLock * o_vout_provider_lock = nil; static int WindowControl(vout_window_t *, int i_query, va_list); int WindowOpen(vout_window_t *p_wnd, const vout_window_cfg_t *cfg) { + if (cfg->type != VOUT_WINDOW_TYPE_INVALID + && cfg->type != VOUT_WINDOW_TYPE_NSOBJECT) + return VLC_EGENERIC; + NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; intf_thread_t *p_intf = VLCIntf; if (!p_intf) { msg_Err(p_wnd, "Mac OS X interface not found"); + [o_pool release]; return VLC_EGENERIC; } NSRect proposedVideoViewPosition = NSMakeRect(cfg->x, cfg->y, cfg->width, cfg->height); + [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; + } + SEL sel = @selector(setupVoutForWindow:withProposedVideoViewPosition:); NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[o_vout_controller methodSignatureForSelector:sel]]; [inv setTarget:o_vout_controller]; @@ -159,31 +153,15 @@ int WindowOpen(vout_window_t *p_wnd, const vout_window_cfg_t *cfg) VLCVoutView *videoView = nil; [inv getReturnValue:&videoView]; - if (!videoView) { - msg_Err(p_wnd, "got no video view from the interface"); - [o_pool release]; - return VLC_EGENERIC; - } + // this method is not supposed to fail + assert(videoView != nil); 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; + [o_vout_provider_lock unlock]; - // TODO: find a cleaner way for "start in fullscreen" - if (var_GetBool(pl_Get(VLCIntf), "fullscreen")) { - int i_full = 1; - - SEL sel = @selector(setFullscreen:forWindow:); - NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[[VLCMain sharedInstance] voutController] methodSignatureForSelector:sel]]; - [inv setTarget:[[VLCMain sharedInstance] voutController]]; - [inv setSelector:sel]; - [inv setArgument:&i_full atIndex:2]; - [inv setArgument:&p_wnd atIndex:3]; - [inv performSelectorOnMainThread:@selector(invoke) withObject:nil - waitUntilDone:NO]; - } - - [[VLCMain sharedInstance] setActiveVideoPlayback: YES]; + p_wnd->type = VOUT_WINDOW_TYPE_NSOBJECT; p_wnd->control = WindowControl; [o_pool release]; @@ -192,85 +170,126 @@ int WindowOpen(vout_window_t *p_wnd, const vout_window_cfg_t *cfg) static int WindowControl(vout_window_t *p_wnd, int i_query, va_list args) { + 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; + } + switch(i_query) { case VOUT_WINDOW_SET_STATE: { - NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; unsigned i_state = va_arg(args, unsigned); + if (i_state & VOUT_WINDOW_STATE_BELOW) + { + msg_Dbg(p_wnd, "Ignore change to VOUT_WINDOW_STATE_BELOW"); + goto out; + } + NSInteger i_cooca_level = NSNormalWindowLevel; if (i_state & VOUT_WINDOW_STATE_ABOVE) i_cooca_level = NSStatusWindowLevel; SEL sel = @selector(setWindowLevel:forWindow:); - NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[[VLCMain sharedInstance] voutController] methodSignatureForSelector:sel]]; - [inv setTarget:[[VLCMain sharedInstance] voutController]]; + NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[o_vout_controller methodSignatureForSelector:sel]]; + [inv setTarget:o_vout_controller]; [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]; - [o_pool release]; - return VLC_SUCCESS; + break; } case VOUT_WINDOW_SET_SIZE: { - NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; - unsigned int i_width = va_arg(args, unsigned int); unsigned int i_height = va_arg(args, unsigned int); NSSize newSize = NSMakeSize(i_width, i_height); SEL sel = @selector(setNativeVideoSize:forWindow:); - NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[[VLCMain sharedInstance] voutController] methodSignatureForSelector:sel]]; - [inv setTarget:[[VLCMain sharedInstance] voutController]]; + NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[o_vout_controller methodSignatureForSelector:sel]]; + [inv setTarget:o_vout_controller]; [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]; - [o_pool release]; - return VLC_SUCCESS; + break; } case VOUT_WINDOW_SET_FULLSCREEN: { - NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; + if (var_InheritBool(VLCIntf, "video-wallpaper")) { + msg_Dbg(p_wnd, "Ignore fullscreen event as video-wallpaper is on"); + goto out; + } + int i_full = va_arg(args, int); + BOOL b_animation = YES; - SEL sel = @selector(setFullscreen:forWindow:); - NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[[VLCMain sharedInstance] voutController] methodSignatureForSelector:sel]]; - [inv setTarget:[[VLCMain sharedInstance] voutController]]; + SEL sel = @selector(setFullscreen:forWindow:withAnimation:); + NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[o_vout_controller methodSignatureForSelector:sel]]; + [inv setTarget:o_vout_controller]; [inv setSelector:sel]; [inv setArgument:&i_full atIndex:2]; // starting at 2! [inv setArgument:&p_wnd atIndex:3]; + [inv setArgument:&b_animation atIndex:4]; [inv performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:NO]; - [o_pool release]; - return VLC_SUCCESS; + break; } default: + { msg_Warn(p_wnd, "unsupported control query"); + [o_vout_provider_lock unlock]; + [o_pool release]; return VLC_EGENERIC; + } } + +out: + [o_vout_provider_lock unlock]; + [o_pool release]; + return VLC_SUCCESS; } void WindowClose(vout_window_t *p_wnd) { NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; - [[[VLCMain sharedInstance] voutController] performSelectorOnMainThread:@selector(removeVoutforDisplay:) withObject:[NSValue valueWithPointer:p_wnd] waitUntilDone:NO]; + [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]; [o_pool release]; } +/* Used to abort the app.exec() on OSX after libvlc_Quit is called */ +#include "../../../lib/libvlc_internal.h" /* libvlc_SetExitHandler */ + +static void QuitVLC( void *obj ) +{ + [[VLCApplication sharedApplication] performSelectorOnMainThread:@selector(terminate:) withObject:nil waitUntilDone:NO]; +} + /***************************************************************************** * Run: main loop *****************************************************************************/ static NSLock * o_appLock = nil; // controls access to f_appExit -static NSLock * o_plItemChangedLock = nil; static void Run(intf_thread_t *p_intf) { @@ -278,15 +297,20 @@ static void Run(intf_thread_t *p_intf) [VLCApplication sharedApplication]; o_appLock = [[NSLock alloc] init]; - o_plItemChangedLock = [[NSLock alloc] init]; + o_vout_provider_lock = [[NSLock alloc] init]; + + libvlc_SetExitHandler(p_intf->p_libvlc, QuitVLC, p_intf); [[VLCMain sharedInstance] setIntf: p_intf]; + [NSBundle loadNibNamed: @"MainMenu" owner: NSApp]; [NSApp run]; + msg_Dbg(p_intf, "Run loop has been stopped"); [[VLCMain sharedInstance] applicationWillTerminate:nil]; - [o_plItemChangedLock release]; [o_appLock release]; + [o_vout_provider_lock release]; + o_vout_provider_lock = nil; [o_pool release]; raise(SIGTERM); @@ -295,29 +319,6 @@ static void Run(intf_thread_t *p_intf) #pragma mark - #pragma mark Variables Callback -/***************************************************************************** - * MsgCallback: Callback triggered by the core once a new debug message is - * ready to be displayed. We store everything in a NSArray in our Cocoa part - * of this file. - *****************************************************************************/ -static void MsgCallback(void *data, int type, const msg_item_t *item, const char *format, va_list ap) -{ - int canc = vlc_savecancel(); - char *str; - - if (vasprintf(&str, format, ap) == -1) { - vlc_restorecancel(canc); - return; - } - - NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; - [[VLCMain sharedInstance] processReceivedlibvlcMessage: item ofType: type withStr: str]; - [o_pool release]; - - vlc_restorecancel(canc); - free(str); -} - static int InputEvent(vlc_object_t *p_this, const char *psz_var, vlc_value_t oldval, vlc_value_t new_val, void *param) { @@ -354,7 +355,7 @@ static int InputEvent(vlc_object_t *p_this, const char *psz_var, case INPUT_EVENT_ITEM_INFO: [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO]; [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO]; - [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateInfoandMetaPanel) withObject: nil waitUntilDone:NO]; + [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMetaAndInfo) withObject: nil waitUntilDone:NO]; break; case INPUT_EVENT_BOOKMARK: break; @@ -371,7 +372,6 @@ static int InputEvent(vlc_object_t *p_this, const char *psz_var, case INPUT_EVENT_ITEM_NAME: [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO]; - [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(playlistUpdated) withObject: nil waitUntilDone:NO]; break; case INPUT_EVENT_AUDIO_DELAY: @@ -384,13 +384,7 @@ static int InputEvent(vlc_object_t *p_this, const char *psz_var, [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updatePlaybackPosition) withObject:nil waitUntilDone:NO]; break; - case INPUT_EVENT_ABORT: - [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO]; - [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updatePlaybackPosition) withObject:nil waitUntilDone:NO]; - break; - default: - //msg_Warn(p_this, "unhandled input event (%lld)", new_val.i_int); break; } @@ -403,23 +397,46 @@ static int PLItemChanged(vlc_object_t *p_this, const char *psz_var, { NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init]; - /* Due to constraints within NSAttributedString's main loop runtime handling - * and other issues, we need to wait for -PlaylistItemChanged to finish and - * then -informInputChanged on this non-main thread. */ - [o_plItemChangedLock lock]; - [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(PlaylistItemChanged) withObject:nil waitUntilDone:YES]; - [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(informInputChanged) withObject:nil waitUntilDone:YES]; - [o_plItemChangedLock unlock]; + [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(PlaylistItemChanged) withObject:nil waitUntilDone:NO]; [o_pool release]; return VLC_SUCCESS; } -static int PlaylistUpdated(vlc_object_t *p_this, const char *psz_var, +/** + * 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(playlistUpdated) withObject:nil waitUntilDone:NO]; + + [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(plItemUpdated) withObject:nil waitUntilDone:NO]; + + [o_pool release]; + return VLC_SUCCESS; +} + +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; @@ -445,6 +462,18 @@ static int VolumeUpdated(vlc_object_t *p_this, const char *psz_var, return VLC_SUCCESS; } +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; +} + /***************************************************************************** * ShowController: Callback triggered by the show-intf playlist variable * through the ShowIntf-control-intf, to let us show the controller-win; @@ -454,14 +483,15 @@ static int ShowController(vlc_object_t *p_this, const char *psz_variable, vlc_value_t old_val, vlc_value_t new_val, void *param) { intf_thread_t * p_intf = VLCIntf; - if (p_intf && p_intf->p_sys) { + if (p_intf) { playlist_t * p_playlist = pl_Get(p_intf); BOOL b_fullscreen = var_GetBool(p_playlist, "fullscreen"); - if (strcmp(psz_variable, "intf-toggle-fscontrol") || b_fullscreen) + if (b_fullscreen) [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(showFullscreenController) withObject:nil waitUntilDone:NO]; - else + else if (!strcmp(psz_variable, "intf-show")) [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(showMainWindow) withObject:nil waitUntilDone:NO]; } + return VLC_SUCCESS; } @@ -472,9 +502,8 @@ static int ShowController(vlc_object_t *p_this, const char *psz_variable, static int DialogCallback(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data) { NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init]; - VLCMain *interface = (VLCMain *)data; - if ([[NSString stringWithUTF8String: type] isEqualToString: @"dialog-progress-bar"]) { + if ([[NSString stringWithUTF8String:type] isEqualToString: @"dialog-progress-bar"]) { /* 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; @@ -485,7 +514,7 @@ static int DialogCallback(vlc_object_t *p_this, const char *type, vlc_value_t pr } NSValue *o_value = [NSValue valueWithPointer:value.p_address]; - [[VLCCoreDialogProvider sharedInstance] performEventWithObject: o_value ofType: type]; + [[[VLCMain sharedInstance] coreDialogProvider] performEventWithObject: o_value ofType: type]; [o_pool release]; return VLC_SUCCESS; @@ -495,13 +524,10 @@ void updateProgressPanel (void *priv, const char *text, float value) { NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; - NSString *o_txt; - if (text != NULL) - o_txt = [NSString stringWithUTF8String: text]; - else - o_txt = @""; - - [[[VLCMain sharedInstance] coreDialogProvider] updateProgressPanelWithText: o_txt andNumber: (double)(value * 1000.)]; + NSString *o_txt = toNSStr(text); + dispatch_async(dispatch_get_main_queue(), ^{ + [[[VLCMain sharedInstance] coreDialogProvider] updateProgressPanelWithText: o_txt andNumber: (double)(value * 1000.)]; + }); [o_pool release]; } @@ -518,9 +544,7 @@ void destroyProgressPanel (void *priv) bool checkProgressPanel (void *priv) { - NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; return [[[VLCMain sharedInstance] coreDialogProvider] progressCancelled]; - [o_pool release]; } #pragma mark - @@ -573,12 +597,11 @@ audio_output_t *getAout(void) #pragma mark - #pragma mark Private -@interface VLCMain () +@interface VLCMain () - (void)removeOldPreferences; @end @interface VLCMain (Internal) -- (void)handlePortMessage:(NSPortMessage *)o_msg; - (void)resetMediaKeyJump; - (void)coreChangedMediaKeySupportSetting: (NSNotification *)o_notification; @end @@ -610,18 +633,13 @@ static VLCMain *_o_sharedMainInstance = nil; _o_sharedMainInstance = [super init]; p_intf = NULL; - p_current_input = p_input_changed = NULL; - - o_msg_lock = [[NSLock alloc] init]; - o_msg_arr = [[NSMutableArray arrayWithCapacity: 600] retain]; + p_current_input = NULL; o_open = [[VLCOpen alloc] init]; o_coredialogs = [[VLCCoreDialogProvider alloc] init]; - o_info = [[VLCInfo alloc] init]; o_mainmenu = [[VLCMainMenu alloc] init]; o_coreinteraction = [[VLCCoreInteraction alloc] init]; o_eyetv = [[VLCEyeTVController alloc] init]; - o_mainwindow = [[VLCMainWindow alloc] init]; /* announce our launch to a potential eyetv plugin */ [[NSDistributedNotificationCenter defaultCenter] postNotificationName: @"VLCOSXGUIInit" @@ -635,6 +653,8 @@ static VLCMain *_o_sharedMainInstance = nil; o_vout_controller = [[VLCVoutWindowController alloc] init]; + informInputChangedQueue = dispatch_queue_create("org.videolan.vlc.inputChangedQueue", DISPATCH_QUEUE_SERIAL); + return _o_sharedMainInstance; } @@ -651,7 +671,6 @@ static VLCMain *_o_sharedMainInstance = nil; - (void)awakeFromNib { playlist_t *p_playlist; - vlc_value_t val; if (!p_intf) return; var_Create(p_intf, "intf-change", VLC_VAR_BOOL); @@ -660,20 +679,15 @@ static VLCMain *_o_sharedMainInstance = nil; if (nib_main_loaded) return; - [o_msgs_panel setExcludedFromWindowsMenu: YES]; - [o_msgs_panel setDelegate: self]; - p_playlist = pl_Get(p_intf); - val.b_bool = false; - var_AddCallback(p_intf->p_libvlc, "intf-toggle-fscontrol", ShowController, self); var_AddCallback(p_intf->p_libvlc, "intf-show", ShowController, self); - // var_AddCallback(p_playlist, "item-change", PLItemChanged, self); - var_AddCallback(p_playlist, "activity", PLItemChanged, self); - var_AddCallback(p_playlist, "leaf-to-parent", PlaylistUpdated, self); - var_AddCallback(p_playlist, "playlist-item-append", PlaylistUpdated, self); - var_AddCallback(p_playlist, "playlist-item-deleted", PlaylistUpdated, self); + var_AddCallback(p_intf->p_libvlc, "intf-boss", BossCallback, self); + var_AddCallback(p_playlist, "item-change", PLItemUpdated, self); + var_AddCallback(p_playlist, "input-current", PLItemChanged, self); + var_AddCallback(p_playlist, "playlist-item-append", PLItemAppended, self); + var_AddCallback(p_playlist, "playlist-item-deleted", PLItemRemoved, self); var_AddCallback(p_playlist, "random", PlaybackModeUpdated, self); var_AddCallback(p_playlist, "repeat", PlaybackModeUpdated, self); var_AddCallback(p_playlist, "loop", PlaybackModeUpdated, self); @@ -685,8 +699,7 @@ static VLCMain *_o_sharedMainInstance = nil; var_SetBool(p_playlist, "fullscreen", YES); } - /* load our Core and Shared Dialogs nibs */ - nib_coredialogs_loaded = [NSBundle loadNibNamed:@"CoreDialogs" owner: NSApp]; + /* load our Shared Dialogs nib */ [NSBundle loadNibNamed:@"SharedDialogs" owner: NSApp]; /* subscribe to various interactive dialogues */ @@ -707,8 +720,6 @@ static VLCMain *_o_sharedMainInstance = nil; [o_remote setClickCountEnabledButtons: kRemoteButtonPlay]; [o_remote setDelegate: _o_sharedMainInstance]; - [o_msgs_refresh_btn setImage: [NSImage imageNamed: NSImageNameRefreshTemplate]]; - /* yeah, we are done */ b_nativeFullscreenMode = NO; #ifdef MAC_OS_X_VERSION_10_7 @@ -730,23 +741,46 @@ static VLCMain *_o_sharedMainInstance = nil; [[VLCApplication sharedApplication] setApplicationIconImage: [NSImage imageNamed:@"vlc-xmas"]]; } - [self initStrings]; - nib_main_loaded = TRUE; } +- (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; + + [NSBundle loadNibNamed:@"MainWindow" owner: self]; + [o_mainwindow makeKeyAndOrderFront:nil]; + + [[SUUpdater sharedUpdater] setDelegate:self]; +} + - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + launched = YES; + if (!p_intf) return; + 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"]; + [self updateCurrentlyUsedHotkeys]; /* init media key support */ b_mediaKeySupport = var_InheritBool(VLCIntf, "macosx-mediakeys"); if (b_mediaKeySupport) { o_mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self]; - [o_mediaKeyController startWatchingMediaKeys]; [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys: [SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers], kMediaKeyUsingBundleIdentifiersDefaultsKey, nil]]; @@ -759,46 +793,35 @@ static VLCMain *_o_sharedMainInstance = nil; [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:) name:NSWorkspaceWillSleepNotification object:nil]; - [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(lookForCrashLog) withObject:nil waitUntilDone:NO]; - - /* we will need this, so let's load it here so the interface appears to be more responsive */ - nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner: NSApp]; - /* update the main window */ [o_mainwindow updateWindow]; [o_mainwindow updateTimeSlider]; [o_mainwindow updateVolumeSlider]; -} - -- (void)initStrings -{ - if (!p_intf) - return; - /* messages panel */ - [o_msgs_panel setTitle: _NS("Messages")]; - [o_msgs_crashlog_btn setTitle: _NS("Open CrashLog...")]; - [o_msgs_save_btn setTitle: _NS("Save this Log...")]; - - /* crash reporter panel */ - [o_crashrep_send_btn setTitle: _NS("Send")]; - [o_crashrep_dontSend_btn setTitle: _NS("Don't Send")]; - [o_crashrep_title_txt setStringValue: _NS("VLC crashed previously")]; - [o_crashrep_win setTitle: _NS("VLC crashed previously")]; - [o_crashrep_desc_txt setStringValue: _NS("Do you want to send details on the crash to VLC's development team?\n\nIf you want, you can enter a few lines on what you did before VLC crashed along with other helpful information: a link to download a sample file, a URL of a network stream, ...")]; - [o_crashrep_includeEmail_ckb setTitle: _NS("I agree to be possibly contacted about this bugreport.")]; - [o_crashrep_includeEmail_txt setStringValue: _NS("Only your default E-Mail address will be submitted, including no further information.")]; - [o_crashrep_dontaskagain_ckb setTitle: _NS("Don't ask again")]; + // respect playlist-autostart + // note that PLAYLIST_PLAY will not stop any playback if already started + playlist_t * p_playlist = pl_Get(VLCIntf); + PL_LOCK; + BOOL kidsAround = p_playlist->p_local_category->i_children != 0; + if (kidsAround && var_GetBool(p_playlist, "playlist-autostart")) + playlist_Control(p_playlist, PLAYLIST_PLAY, true); + PL_UNLOCK; } +/* 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; + #pragma mark - #pragma mark Termination +- (BOOL)isTerminating +{ + return f_appExit; +} + - (void)applicationWillTerminate:(NSNotification *)notification { - /* 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; bool isTerminating; [o_appLock lock]; @@ -815,13 +838,6 @@ static VLCMain *_o_sharedMainInstance = nil; [[NSNotificationCenter defaultCenter] postNotificationName: NSApplicationWillTerminateNotification object: nil]; playlist_t * p_playlist = pl_Get(p_intf); - int returnedValue = 0; - - /* always exit fullscreen on quit, otherwise we get ugly artifacts on the next launch */ - if (b_nativeFullscreenMode) { - [o_mainwindow toggleFullScreen: self]; - [NSApp setPresentationOptions:(NSApplicationPresentationDefault)]; - } /* save current video and audio profiles */ [[VLCVideoEffects sharedInstance] saveCurrentProfile]; @@ -841,11 +857,10 @@ static VLCMain *_o_sharedMainInstance = nil; 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); - //var_DelCallback(p_playlist, "item-change", PLItemChanged, self); - var_DelCallback(p_playlist, "activity", PLItemChanged, self); - var_DelCallback(p_playlist, "leaf-to-parent", PlaylistUpdated, self); - var_DelCallback(p_playlist, "playlist-item-append", PlaylistUpdated, self); - var_DelCallback(p_playlist, "playlist-item-deleted", PlaylistUpdated, self); + var_DelCallback(p_playlist, "item-change", PLItemUpdated, self); + var_DelCallback(p_playlist, "input-current", PLItemChanged, self); + var_DelCallback(p_playlist, "playlist-item-append", PLItemAppended, self); + var_DelCallback(p_playlist, "playlist-item-deleted", PLItemRemoved, self); var_DelCallback(p_playlist, "random", PlaybackModeUpdated, self); var_DelCallback(p_playlist, "repeat", PlaybackModeUpdated, self); var_DelCallback(p_playlist, "loop", PlaybackModeUpdated, self); @@ -853,8 +868,12 @@ static VLCMain *_o_sharedMainInstance = nil; var_DelCallback(p_playlist, "mute", VolumeUpdated, self); var_DelCallback(p_intf->p_libvlc, "intf-toggle-fscontrol", ShowController, self); var_DelCallback(p_intf->p_libvlc, "intf-show", ShowController, self); + var_DelCallback(p_intf->p_libvlc, "intf-boss", BossCallback, self); if (p_current_input) { + /* continue playback where you left off */ + [[self playlist] storePlaybackPositionForItem:p_current_input]; + var_DelCallback(p_current_input, "intf-event", InputEvent, [VLCMain sharedInstance]); vlc_object_release(p_current_input); p_current_input = NULL; @@ -863,9 +882,12 @@ static VLCMain *_o_sharedMainInstance = nil; /* remove global observer watching for vout device changes correctly */ [[NSNotificationCenter defaultCenter] removeObserver: self]; + [o_vout_provider_lock lock]; // release before o_info! + // closes all open vouts [o_vout_controller release]; o_vout_controller = nil; + [o_vout_provider_lock unlock]; /* release some other objects here, because it isn't sure whether dealloc * will be called later on */ @@ -883,32 +905,27 @@ static VLCMain *_o_sharedMainInstance = nil; if (o_wizard) [o_wizard release]; - [crashLogURLConnection cancel]; - [crashLogURLConnection release]; + if (!o_bookmarks) + [o_bookmarks release]; [o_coredialogs release]; [o_eyetv release]; + [o_remote release]; /* unsubscribe from libvlc's debug messages */ vlc_LogSet(p_intf->p_libvlc, NULL, NULL); - [o_msg_arr removeAllObjects]; - [o_msg_arr release]; - o_msg_arr = NULL; [o_usedHotkeys release]; o_usedHotkeys = NULL; - [o_msg_lock release]; + [o_mediaKeyController release]; /* write cached user defaults to disk */ [[NSUserDefaults standardUserDefaults] synchronize]; - [o_mainmenu release]; + [o_coreinteraction release]; - libvlc_Quit(p_intf->p_libvlc); - - [o_mainwindow release]; o_mainwindow = NULL; [self setIntf:nil]; @@ -924,6 +941,15 @@ static VLCMain *_o_sharedMainInstance = nil; [[VLCCoreInteraction sharedInstance] stop]; } +/* don't be enthusiastic about an update if we currently play a video */ +- (BOOL)updaterMayCheckForUpdates:(SUUpdater *)bundle +{ + if ([self activeVideoPlayback]) + return NO; + + return YES; +} + #pragma mark - #pragma mark Media Key support @@ -996,17 +1022,30 @@ static VLCMain *_o_sharedMainInstance = nil; - (void)application:(NSApplication *)o_app openFiles:(NSArray *)o_names { - BOOL b_autoplay = config_GetInt(VLCIntf, "macosx-autoplay"); - char *psz_uri = vlc_path2uri([[o_names objectAtIndex:0] UTF8String], "file"); + // 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. + 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; + } + } + + char *psz_uri = vlc_path2uri([[o_names objectAtIndex:0] UTF8String], NULL); // try to add file as subtitle if ([o_names count] == 1 && psz_uri) { input_thread_t * p_input = pl_CurrentInput(VLCIntf); if (p_input) { - BOOL b_returned = NO; - b_returned = input_AddSubtitle(p_input, psz_uri, true); + int i_result = input_AddSubtitleOSD(p_input, [[o_names objectAtIndex:0] UTF8String], true, true); vlc_object_release(p_input); - if (!b_returned) { + if (i_result == VLC_SUCCESS) { free(psz_uri); return; } @@ -1016,8 +1055,8 @@ static VLCMain *_o_sharedMainInstance = nil; NSArray *o_sorted_names = [o_names sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)]; NSMutableArray *o_result = [NSMutableArray arrayWithCapacity: [o_sorted_names count]]; - for (int i = 0; i < [o_sorted_names count]; i++) { - psz_uri = vlc_path2uri([[o_sorted_names objectAtIndex: i] UTF8String], "file"); + for (NSUInteger i = 0; i < [o_sorted_names count]; i++) { + psz_uri = vlc_path2uri([[o_sorted_names objectAtIndex:i] UTF8String], "file"); if (!psz_uri) continue; @@ -1026,12 +1065,7 @@ static VLCMain *_o_sharedMainInstance = nil; [o_result addObject: o_dic]; } - if (b_autoplay) - [o_playlist appendArray: o_result atPos: -1 enqueue: NO]; - else - [o_playlist appendArray: o_result atPos: -1 enqueue: YES]; - - return; + [[[VLCMain sharedInstance] playlist] addPlaylistItems:o_result]; } /* When user click in the Dock icon our double click in the finder */ @@ -1059,10 +1093,12 @@ static VLCMain *_o_sharedMainInstance = nil; [[VLCCoreInteraction sharedInstance] backward]; break; case kRemoteButtonVolume_Plus_Hold: - [[VLCCoreInteraction sharedInstance] volumeUp]; + if (p_intf) + var_SetInteger(p_intf->p_libvlc, "key-action", ACTIONID_VOL_UP); break; case kRemoteButtonVolume_Minus_Hold: - [[VLCCoreInteraction sharedInstance] volumeDown]; + if (p_intf) + var_SetInteger(p_intf->p_libvlc, "key-action", ACTIONID_VOL_DOWN); break; } if (b_remote_button_hold) { @@ -1096,13 +1132,15 @@ static VLCMain *_o_sharedMainInstance = nil; if (config_GetInt(VLCIntf, "macosx-appleremote-sysvol")) [NSSound increaseSystemVolume]; else - [[VLCCoreInteraction sharedInstance] volumeUp]; + if (p_intf) + var_SetInteger(p_intf->p_libvlc, "key-action", ACTIONID_VOL_UP); break; case kRemoteButtonVolume_Minus: if (config_GetInt(VLCIntf, "macosx-appleremote-sysvol")) [NSSound decreaseSystemVolume]; else - [[VLCCoreInteraction sharedInstance] volumeDown]; + if (p_intf) + var_SetInteger(p_intf->p_libvlc, "key-action", ACTIONID_VOL_DOWN); break; case kRemoteButtonRight: if (config_GetInt(VLCIntf, "macosx-appleremote-prevnext")) @@ -1123,7 +1161,7 @@ static VLCMain *_o_sharedMainInstance = nil; /* simulate an event as long as the user holds the button */ b_remote_button_hold = pressedDown; if (pressedDown) { - NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt: buttonIdentifier]; + NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt:buttonIdentifier]; [self performSelector:@selector(executeHoldActionForRemoteButton:) withObject:buttonIdentifierNumber]; } @@ -1197,16 +1235,11 @@ static VLCMain *_o_sharedMainInstance = nil; } } - if (key == 0x0020) { // space key - [[VLCCoreInteraction sharedInstance] playOrPause]; - return YES; - } - val.i_int |= CocoaKeyToVLC(key); BOOL b_found_key = NO; - for (int i = 0; i < [o_usedHotkeys count]; i++) { - NSString *str = [o_usedHotkeys objectAtIndex: i]; + for (NSUInteger i = 0; i < [o_usedHotkeys count]; i++) { + NSString *str = [o_usedHotkeys objectAtIndex:i]; unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa: str]; if ([[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: str]] && @@ -1260,14 +1293,50 @@ static VLCMain *_o_sharedMainInstance = nil; #pragma mark - #pragma mark Interface updaters +- (void)plItemAppended:(NSArray *)o_val +{ + int i_node = [[o_val objectAtIndex:0] intValue]; + int i_item = [[o_val objectAtIndex:1] intValue]; + + [[[self playlist] model] addItem:i_item withParentNode:i_node]; + + // update badge in sidebar + [o_mainwindow updateWindow]; + + [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCMediaKeySupportSettingChanged" + object: nil + userInfo: nil]; +} + +- (void)plItemRemoved:(NSNumber *)o_val +{ + int i_item = [o_val intValue]; + + [[[self playlist] model] removeItem:i_item]; + [[self playlist] deletionCompleted]; + + // update badge in sidebar + [o_mainwindow updateWindow]; + + [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCMediaKeySupportSettingChanged" + object: nil + userInfo: nil]; +} + +// This must be called on main thread - (void)PlaylistItemChanged { + input_thread_t *p_input_changed = NULL; + if (p_current_input && (p_current_input->b_dead || !vlc_object_alive(p_current_input))) { var_DelCallback(p_current_input, "intf-event", InputEvent, [VLCMain sharedInstance]); - p_input_changed = p_current_input; + vlc_object_release(p_current_input); p_current_input = NULL; [o_mainmenu setRateControlsEnabled: NO]; + + [[NSNotificationCenter defaultCenter] postNotificationName:VLCInputChangedNotification + object:nil]; } else if (!p_current_input) { // object is hold here and released then it is dead @@ -1276,25 +1345,46 @@ static VLCMain *_o_sharedMainInstance = nil; var_AddCallback(p_current_input, "intf-event", InputEvent, [VLCMain sharedInstance]); [self playbackStatusUpdated]; [o_mainmenu setRateControlsEnabled: YES]; - if ([self activeVideoPlayback] && [[o_mainwindow videoView] isHidden]) - [o_mainwindow performSelectorOnMainThread:@selector(togglePlaylist:) withObject: nil waitUntilDone:NO]; + + if ([self activeVideoPlayback] && [[o_mainwindow videoView] isHidden]) { + [o_mainwindow changePlaylistState: psPlaylistItemChangedEvent]; + } + p_input_changed = vlc_object_hold(p_current_input); + + [[self playlist] currentlyPlayingItemChanged]; + + [[self playlist] continuePlaybackWhereYouLeftOff:p_current_input]; + + [[NSNotificationCenter defaultCenter] postNotificationName:VLCInputChangedNotification + object:nil]; } } - [o_playlist updateRowSelection]; + [self updateMetaAndInfo]; + [o_mainwindow updateWindow]; [self updateDelays]; [self updateMainMenu]; + + /* + * Due to constraints within NSAttributedString's main loop runtime handling + * and other issues, we need to inform the extension manager on a separate thread. + * The serial queue ensures that changed inputs are propagated in the same order as they arrive. + */ + dispatch_async(informInputChangedQueue, ^{ + [[ExtensionsManager getInstance:p_intf] inputChanged:p_input_changed]; + if (p_input_changed) + vlc_object_release(p_input_changed); + }); } -- (void)informInputChanged +- (void)plItemUpdated { - if (p_input_changed) { - [[ExtensionsManager getInstance:p_intf] inputChanged:p_input_changed]; - vlc_object_release(p_input_changed); - p_input_changed = NULL; - } + [o_mainwindow updateName]; + + if (o_info != NULL) + [o_info updateMetadata]; } - (void)updateMainMenu @@ -1316,6 +1406,8 @@ static VLCMain *_o_sharedMainInstance = nil; - (void)showFullscreenController { + // defer selector here (possibly another time) to ensure that keyWindow is set properly + // (needed for NSApplicationDidBecomeActiveNotification) [o_mainwindow performSelectorOnMainThread:@selector(showFullscreenController) withObject:nil waitUntilDone:NO]; } @@ -1340,38 +1432,52 @@ static VLCMain *_o_sharedMainInstance = nil; [o_mainwindow updateVolumeSlider]; } -- (void)playlistUpdated -{ - [self playbackStatusUpdated]; - [o_playlist playlistUpdated]; - [o_mainwindow updateWindow]; - [o_mainwindow updateName]; -} - - (void)updateRecordState: (BOOL)b_value { [o_mainmenu updateRecordState:b_value]; } -- (void)updateInfoandMetaPanel +- (void)updateMetaAndInfo { - [o_playlist outlineViewSelectionDidChange:nil]; + if (!p_current_input) { + [[self info] updatePanelWithItem:nil]; + return; + } + + input_item_t *p_input_item = input_GetItem(p_current_input); + + [[[self playlist] model] updateItem:p_input_item]; + [[self info] updatePanelWithItem:p_input_item]; } - (void)resumeItunesPlayback:(id)sender { - if (b_has_itunes_paused && var_InheritInteger(p_intf, "macosx-control-itunes") > 1) { - iTunesApplication *iTunesApp = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"]; - if (iTunesApp && [iTunesApp isRunning]) { - if ([iTunesApp playerState] == iTunesEPlSPaused) { - msg_Dbg(p_intf, "Unpause iTunes..."); - [iTunesApp playpause]; + if (var_InheritInteger(p_intf, "macosx-control-itunes") > 1) { + if (b_has_itunes_paused) { + iTunesApplication *iTunesApp = (iTunesApplication *) [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"]; + if (iTunesApp && [iTunesApp isRunning]) { + if ([iTunesApp playerState] == iTunesEPlSPaused) { + msg_Dbg(p_intf, "unpausing iTunes"); + [iTunesApp playpause]; + } + } + } + + if (b_has_spotify_paused) { + SpotifyApplication *spotifyApp = (SpotifyApplication *) [SBApplication applicationWithBundleIdentifier:@"com.spotify.client"]; + if (spotifyApp) { + if ([spotifyApp respondsToSelector:@selector(isRunning)] && [spotifyApp respondsToSelector:@selector(playerState)]) { + if ([spotifyApp isRunning] && [spotifyApp playerState] == kSpotifyPlayerStatePaused) { + msg_Dbg(p_intf, "unpausing Spotify"); + [spotifyApp play]; + } + } } } - } b_has_itunes_paused = NO; + b_has_spotify_paused = NO; o_itunes_play_timer = nil; } @@ -1392,19 +1498,35 @@ static VLCMain *_o_sharedMainInstance = nil; } if (state == PLAYING_S) { - // pause iTunes - if (i_control_itunes > 0 && !b_has_itunes_paused) { - iTunesApplication *iTunesApp = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"]; - if (iTunesApp && [iTunesApp isRunning]) { - if ([iTunesApp playerState] == iTunesEPlSPlaying) { - msg_Dbg(p_intf, "Pause iTunes..."); - [iTunesApp pause]; - b_has_itunes_paused = YES; + if (i_control_itunes > 0) { + // pause iTunes + if (!b_has_itunes_paused) { + iTunesApplication *iTunesApp = (iTunesApplication *) [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"]; + if (iTunesApp && [iTunesApp isRunning]) { + if ([iTunesApp playerState] == iTunesEPlSPlaying) { + msg_Dbg(p_intf, "pausing iTunes"); + [iTunesApp pause]; + b_has_itunes_paused = YES; + } + } + } + + // pause Spotify + if (!b_has_spotify_paused) { + SpotifyApplication *spotifyApp = (SpotifyApplication *) [SBApplication applicationWithBundleIdentifier:@"com.spotify.client"]; + + if (spotifyApp) { + if ([spotifyApp respondsToSelector:@selector(isRunning)] && [spotifyApp respondsToSelector:@selector(playerState)]) { + if ([spotifyApp isRunning] && [spotifyApp playerState] == kSpotifyPlayerStatePlaying) { + msg_Dbg(p_intf, "pausing Spotify"); + [spotifyApp pause]; + b_has_spotify_paused = YES; + } + } } } } - /* Declare user activity. This wakes the display if it is off, and postpones display sleep according to the users system preferences Available from 10.7.3 */ @@ -1427,7 +1549,7 @@ static VLCMain *_o_sharedMainInstance = nil; IOReturn success; /* work-around a bug in 10.7.4 and 10.7.5, so check for 10.7.x < 10.7.4, 10.8 and 10.6 */ - if ((NSAppKitVersionNumber >= 1115.2 && NSAppKitVersionNumber < 1138.45) || OSX_MOUNTAIN_LION || OSX_SNOW_LEOPARD) { + if ((NSAppKitVersionNumber >= 1115.2 && NSAppKitVersionNumber < 1138.45) || OSX_MOUNTAIN_LION || OSX_MAVERICKS || OSX_YOSEMITE || OSX_SNOW_LEOPARD) { CFStringRef reasonForActivity = CFStringCreateWithCString(kCFAllocatorDefault, _("VLC media playback"), kCFStringEncodingUTF8); if ([self activeVideoPlayback]) success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, reasonForActivity, &systemSleepAssertionID); @@ -1461,6 +1583,10 @@ static VLCMain *_o_sharedMainInstance = nil; } if (state == END_S || state == -1) { + /* continue playback where you left off */ + if (p_current_input) + [[self playlist] storePlaybackPositionForItem:p_current_input]; + if (i_control_itunes > 0) { if (o_itunes_play_timer) { [o_itunes_play_timer invalidate]; @@ -1488,7 +1614,6 @@ static VLCMain *_o_sharedMainInstance = nil; - (void)playbackModeUpdated { - vlc_value_t looping,repeating; playlist_t * p_playlist = pl_Get(VLCIntf); bool loop = var_GetBool(p_playlist, "loop"); @@ -1508,22 +1633,20 @@ static VLCMain *_o_sharedMainInstance = nil; [o_mainmenu setShuffle]; } - #pragma mark - #pragma mark Window updater - - - (void)setActiveVideoPlayback:(BOOL)b_value { + assert([NSThread isMainThread]); + b_active_videoplayback = b_value; if (o_mainwindow) { - [o_mainwindow performSelectorOnMainThread:@selector(setVideoplayEnabled) withObject:nil waitUntilDone:YES]; - [o_mainwindow performSelectorOnMainThread:@selector(togglePlaylist:) withObject:nil waitUntilDone:NO]; + [o_mainwindow setVideoplayEnabled]; } // update sleep blockers - [self performSelectorOnMainThread:@selector(playbackStatusUpdated) withObject:nil waitUntilDone:NO]; + [self playbackStatusUpdated]; } #pragma mark - @@ -1541,10 +1664,7 @@ static VLCMain *_o_sharedMainInstance = nil; - (id)controls { - if (o_controls) - return o_controls; - - return nil; + return o_controls; } - (id)bookmarks @@ -1560,9 +1680,6 @@ static VLCMain *_o_sharedMainInstance = nil; - (id)open { - if (!o_open) - return nil; - if (!nib_open_loaded) nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner: NSApp]; @@ -1593,21 +1710,18 @@ static VLCMain *_o_sharedMainInstance = nil; - (id)playlist { - if (o_playlist) - return o_playlist; - - return nil; + return o_playlist; } - (id)info { + if (!o_info) + o_info = [[VLCInfo alloc] init]; + if (! nib_info_loaded) nib_info_loaded = [NSBundle loadNibNamed:@"MediaInfo" owner: NSApp]; - if (o_info) - return o_info; - - return nil; + return o_info; } - (id)wizard @@ -1619,23 +1733,22 @@ static VLCMain *_o_sharedMainInstance = nil; nib_wizard_loaded = [NSBundle loadNibNamed:@"Wizard" owner: NSApp]; [o_wizard initStrings]; } + return o_wizard; } - (id)coreDialogProvider { - if (o_coredialogs) - return o_coredialogs; + if (!nib_coredialogs_loaded) { + nib_coredialogs_loaded = [NSBundle loadNibNamed:@"CoreDialogs" owner: NSApp]; + } - return nil; + return o_coredialogs; } - (id)eyeTVController { - if (o_eyetv) - return o_eyetv; - - return nil; + return o_eyetv; } - (id)appleRemoteController @@ -1649,163 +1762,43 @@ static VLCMain *_o_sharedMainInstance = nil; } #pragma mark - -#pragma mark Crash Log -- (void)sendCrashLog:(NSString *)crashLog withUserComment:(NSString *)userComment -{ - NSString *urlStr = @"http://crash.videolan.org/crashlog/sendcrashreport.php"; - NSURL *url = [NSURL URLWithString:urlStr]; - - NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url]; - [req setHTTPMethod:@"POST"]; - - NSString * email; - if ([o_crashrep_includeEmail_ckb state] == NSOnState) { - ABPerson * contact = [[ABAddressBook sharedAddressBook] me]; - ABMultiValue *emails = [contact valueForProperty:kABEmailProperty]; - email = [emails valueAtIndex:[emails indexForIdentifier: - [emails primaryIdentifier]]]; - } - else - email = [NSString string]; - - NSString *postBody; - postBody = [NSString stringWithFormat:@"CrashLog=%@&Comment=%@&Email=%@\r\n", - [crashLog stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], - [userComment stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], - [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; - - [req setHTTPBody:[postBody dataUsingEncoding:NSUTF8StringEncoding]]; - - /* Released from delegate */ - crashLogURLConnection = [[NSURLConnection alloc] initWithRequest:req delegate:self]; -} - -- (void)connectionDidFinishLoading:(NSURLConnection *)connection -{ - msg_Dbg(p_intf, "crash report successfully sent"); - [crashLogURLConnection release]; - crashLogURLConnection = nil; -} - -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error -{ - msg_Warn (p_intf, "Error when sending the crash report: %s (%li)", [[error localizedDescription] UTF8String], [error code]); - [crashLogURLConnection release]; - crashLogURLConnection = nil; -} - -- (NSString *)latestCrashLogPathPreviouslySeen:(BOOL)previouslySeen -{ - NSString * crashReporter; - if (OSX_MOUNTAIN_LION) - crashReporter = [@"~/Library/Logs/DiagnosticReports" stringByExpandingTildeInPath]; - else - crashReporter = [@"~/Library/Logs/CrashReporter" stringByExpandingTildeInPath]; - NSDirectoryEnumerator *direnum = [[NSFileManager defaultManager] enumeratorAtPath:crashReporter]; - NSString *fname; - NSString * latestLog = nil; - NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; - int year = !previouslySeen ? [defaults integerForKey:@"LatestCrashReportYear"] : 0; - int month = !previouslySeen ? [defaults integerForKey:@"LatestCrashReportMonth"]: 0; - int day = !previouslySeen ? [defaults integerForKey:@"LatestCrashReportDay"] : 0; - int hours = !previouslySeen ? [defaults integerForKey:@"LatestCrashReportHours"]: 0; - - while (fname = [direnum nextObject]) { - [direnum skipDescendents]; - if ([fname hasPrefix:@"VLC"] && [fname hasSuffix:@"crash"]) { - NSArray * compo = [fname componentsSeparatedByString:@"_"]; - if ([compo count] < 3) - continue; - compo = [[compo objectAtIndex:1] componentsSeparatedByString:@"-"]; - if ([compo count] < 4) - continue; - - // Dooh. ugly. - if (year < [[compo objectAtIndex:0] intValue] || - (year ==[[compo objectAtIndex:0] intValue] && - (month < [[compo objectAtIndex:1] intValue] || - (month ==[[compo objectAtIndex:1] intValue] && - (day < [[compo objectAtIndex:2] intValue] || - (day ==[[compo objectAtIndex:2] intValue] && - hours < [[compo objectAtIndex:3] intValue])))))) { - year = [[compo objectAtIndex:0] intValue]; - month = [[compo objectAtIndex:1] intValue]; - day = [[compo objectAtIndex:2] intValue]; - hours = [[compo objectAtIndex:3] intValue]; - latestLog = [crashReporter stringByAppendingPathComponent:fname]; - } - } - } - - if (!(latestLog && [[NSFileManager defaultManager] fileExistsAtPath:latestLog])) - return nil; +#pragma mark Remove old prefs - if (!previouslySeen) { - [defaults setInteger:year forKey:@"LatestCrashReportYear"]; - [defaults setInteger:month forKey:@"LatestCrashReportMonth"]; - [defaults setInteger:day forKey:@"LatestCrashReportDay"]; - [defaults setInteger:hours forKey:@"LatestCrashReportHours"]; - } - return latestLog; -} -- (NSString *)latestCrashLogPath -{ - return [self latestCrashLogPathPreviouslySeen:YES]; -} +static NSString * kVLCPreferencesVersion = @"VLCPreferencesVersion"; +static const int kCurrentPreferencesVersion = 3; -- (void)lookForCrashLog ++ (void)initialize { - NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; - // This pref key doesn't exists? this VLC is an upgrade, and this crash log come from previous version - NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; - BOOL areCrashLogsTooOld = ![defaults integerForKey:@"LatestCrashReportYear"]; - NSString * latestLog = [self latestCrashLogPathPreviouslySeen:NO]; - if (latestLog && !areCrashLogsTooOld) { - if ([defaults integerForKey:@"AlwaysSendCrashReports"] > 0) - [self sendCrashLog:[NSString stringWithContentsOfFile: [self latestCrashLogPath] encoding: NSUTF8StringEncoding error: NULL] withUserComment: [o_crashrep_fld string]]; - else if ([defaults integerForKey:@"AlwaysSendCrashReports"] == 0) - [NSApp runModalForWindow: o_crashrep_win]; - // bail out, the user doesn't want us to send reports - } + NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCurrentPreferencesVersion] + forKey:kVLCPreferencesVersion]; - [o_pool release]; + [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults]; } -- (IBAction)crashReporterAction:(id)sender +- (void)resetAndReinitializeUserDefaults { - if (sender == o_crashrep_send_btn) { - [self sendCrashLog:[NSString stringWithContentsOfFile: [self latestCrashLogPath] encoding: NSUTF8StringEncoding error: NULL] withUserComment: [o_crashrep_fld string]]; - if ([o_crashrep_dontaskagain_ckb state]) - [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"AlwaysSendCrashReports"]; - } else { - if ([o_crashrep_dontaskagain_ckb state]) - [[NSUserDefaults standardUserDefaults] setInteger:-1 forKey:@"AlwaysSendCrashReports"]; - } + // note that [NSUserDefaults resetStandardUserDefaults] will NOT correctly reset to the defaults - [NSApp stopModal]; - [o_crashrep_win orderOut: sender]; -} + NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier]; + [[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain]; -- (IBAction)openCrashLog:(id)sender -{ - NSString * latestLog = [self latestCrashLogPath]; - if (latestLog) { - [[NSWorkspace sharedWorkspace] openFile: latestLog withApplication: @"Console"]; - } else { - NSBeginInformationalAlertSheet(_NS("No CrashLog found"), _NS("Continue"), nil, nil, o_msgs_panel, self, NULL, NULL, nil, @"%@", _NS("Couldn't find any trace of a previous crash.")); - } + // set correct version to avoid question about outdated config + [[NSUserDefaults standardUserDefaults] setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion]; + [[NSUserDefaults standardUserDefaults] synchronize]; } -#pragma mark - -#pragma mark Remove old prefs - - (void)removeOldPreferences { - static NSString * kVLCPreferencesVersion = @"VLCPreferencesVersion"; - static const int kCurrentPreferencesVersion = 2; NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; int version = [defaults integerForKey:kVLCPreferencesVersion]; + + /* + * Store version explicitely in file, for ease of debugging. + * Otherwise, the value will be just defined at app startup, + * as initialized above. + */ + [defaults setInteger:version forKey:kVLCPreferencesVersion]; if (version >= kCurrentPreferencesVersion) return; @@ -1817,19 +1810,17 @@ static VLCMain *_o_sharedMainInstance = nil; return; else config_SaveConfigFile(VLCIntf); // we need to do manually, since we won't quit libvlc cleanly + } else if (version == 2) { + /* version 2 (used by VLC 2.0.x and early versions of 2.1) can lead to exceptions within 2.1 or later + * so we reset the OS X specific prefs here - in practice, no user will notice */ + [self resetAndReinitializeUserDefaults]; + } else { NSArray *libraries = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); if (!libraries || [libraries count] == 0) return; NSString * preferences = [[libraries objectAtIndex:0] stringByAppendingPathComponent:@"Preferences"]; - /* File not found, don't attempt anything */ - if (![[NSFileManager defaultManager] fileExistsAtPath:[preferences stringByAppendingPathComponent:@"org.videolan.vlc"]] && - ![[NSFileManager defaultManager] fileExistsAtPath:[preferences stringByAppendingPathComponent:@"org.videolan.vlc.plist"]]) { - [defaults setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion]; - return; - } - int res = NSRunInformationalAlertPanel(_NS("Remove old preferences?"), _NS("We just found an older version of VLC's preferences files."), _NS("Move To Trash and Relaunch VLC"), _NS("Ignore"), nil, nil); @@ -1838,16 +1829,17 @@ static VLCMain *_o_sharedMainInstance = nil; return; } - NSArray * ourPreferences = [NSArray arrayWithObjects:@"org.videolan.vlc.plist", @"VLC", @"org.videolan.vlc", nil]; - - /* Move the file to trash so that user can find them later */ - [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:preferences destination:nil files:ourPreferences tag:0]; + // Do NOT add the current plist file here as this would conflict with caching. + // Instead, just reset below. + NSArray * ourPreferences = [NSArray arrayWithObjects:@"org.videolan.vlc", @"VLC", nil]; - /* really reset the defaults from now on */ - [NSUserDefaults resetStandardUserDefaults]; + /* Move the file to trash one by one. Using above array the method would stop after first file + not found. */ + for (NSString *file in ourPreferences) { + [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:preferences destination:@"" files:[NSArray arrayWithObject:file] tag:nil]; + } - [defaults setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion]; - [defaults synchronize]; + [self resetAndReinitializeUserDefaults]; } /* Relaunch now */ @@ -1856,113 +1848,10 @@ static VLCMain *_o_sharedMainInstance = nil; /* For some reason we need to fork(), not just execl(), which reports a ENOTSUP then. */ if (fork() != 0) { exit(0); - return; } execl(path, path, NULL); } -#pragma mark - -#pragma mark Errors, warnings and messages -- (IBAction)updateMessagesPanel:(id)sender -{ - [self windowDidBecomeKey:nil]; -} - -- (IBAction)showMessagesPanel:(id)sender -{ - [o_msgs_panel makeKeyAndOrderFront: sender]; -} - -- (void)windowDidBecomeKey:(NSNotification *)o_notification -{ - [o_msgs_table reloadData]; - [o_msgs_table scrollRowToVisible: [o_msg_arr count] - 1]; -} - -- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView -{ - if (aTableView == o_msgs_table) - return [o_msg_arr count]; - return 0; -} - -- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex -{ - NSMutableAttributedString *result = NULL; - - [o_msg_lock lock]; - if (rowIndex < [o_msg_arr count]) - result = [o_msg_arr objectAtIndex: rowIndex]; - [o_msg_lock unlock]; - - if (result != NULL) - return result; - else - return @""; -} - -- (void)processReceivedlibvlcMessage:(const msg_item_t *) item ofType: (int)i_type withStr: (char *)str -{ - if (o_msg_arr) { - NSColor *o_white = [NSColor whiteColor]; - NSColor *o_red = [NSColor redColor]; - NSColor *o_yellow = [NSColor yellowColor]; - NSColor *o_gray = [NSColor grayColor]; - NSString * firstString, * secondString; - - NSColor * pp_color[4] = { o_white, o_red, o_yellow, o_gray }; - static const char * ppsz_type[4] = { ": ", " error: ", " warning: ", " debug: " }; - - NSDictionary *o_attr; - NSMutableAttributedString *o_msg_color; - - [o_msg_lock lock]; - - if ([o_msg_arr count] > 600) { - [o_msg_arr removeObjectAtIndex: 0]; - [o_msg_arr removeObjectAtIndex: 1]; - } - firstString = [NSString stringWithFormat:@"%s%s", item->psz_module, ppsz_type[i_type]]; - secondString = [NSString stringWithFormat:@"%@%s\n", firstString, str]; - - o_attr = [NSDictionary dictionaryWithObject: pp_color[i_type] forKey: NSForegroundColorAttributeName]; - o_msg_color = [[NSMutableAttributedString alloc] initWithString: secondString attributes: o_attr]; - o_attr = [NSDictionary dictionaryWithObject: pp_color[3] forKey: NSForegroundColorAttributeName]; - [o_msg_color setAttributes: o_attr range: NSMakeRange(0, [firstString length])]; - [o_msg_arr addObject: [o_msg_color autorelease]]; - - b_msg_arr_changed = YES; - [o_msg_lock unlock]; - } -} - -- (IBAction)saveDebugLog:(id)sender -{ - NSSavePanel * saveFolderPanel = [[NSSavePanel alloc] init]; - - [saveFolderPanel setCanSelectHiddenExtension: NO]; - [saveFolderPanel setCanCreateDirectories: YES]; - [saveFolderPanel setAllowedFileTypes: [NSArray arrayWithObject:@"rtf"]]; - [saveFolderPanel setNameFieldStringValue:[NSString stringWithFormat: _NS("VLC Debug Log (%s).rtf"), VERSION_MESSAGE]]; - [saveFolderPanel beginSheetModalForWindow: o_msgs_panel completionHandler:^(NSInteger returnCode) { - if (returnCode == NSOKButton) { - NSUInteger count = [o_msg_arr count]; - NSMutableAttributedString * string = [[NSMutableAttributedString alloc] init]; - for (NSUInteger i = 0; i < count; i++) - [string appendAttributedString: [o_msg_arr objectAtIndex: i]]; - - NSData *data = [string RTFFromRange:NSMakeRange(0, [string length]) - documentAttributes:[NSDictionary dictionaryWithObject: NSRTFTextDocumentType forKey: NSDocumentTypeDocumentAttribute]]; - - if ([data writeToFile: [[saveFolderPanel URL] path] atomically: YES] == NO) - msg_Warn(p_intf, "Error while saving the debug log"); - - [string release]; - } - }]; - [saveFolderPanel release]; -} - #pragma mark - #pragma mark Playlist toggling @@ -1977,26 +1866,6 @@ static VLCMain *_o_sharedMainInstance = nil; @implementation VLCMain (Internal) -- (void)handlePortMessage:(NSPortMessage *)o_msg -{ - id ** val; - NSData * o_data; - NSValue * o_value; - NSInvocation * o_inv; - NSConditionLock * o_lock; - - o_data = [[o_msg components] lastObject]; - o_inv = *((NSInvocation **)[o_data bytes]); - [o_inv getArgument: &o_value atIndex: 2]; - val = (id **)[o_value pointerValue]; - [o_inv setArgument: val[1] atIndex: 2]; - o_lock = *(val[0]); - - [o_lock lock]; - [o_inv invoke]; - [o_lock unlockWithCondition: 1]; -} - - (void)resetMediaKeyJump { b_mediakeyJustJumped = NO; @@ -2005,13 +1874,23 @@ static VLCMain *_o_sharedMainInstance = nil; - (void)coreChangedMediaKeySupportSetting: (NSNotification *)o_notification { b_mediaKeySupport = var_InheritBool(VLCIntf, "macosx-mediakeys"); - if (b_mediaKeySupport) { - if (!o_mediaKeyController) - o_mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self]; - [o_mediaKeyController startWatchingMediaKeys]; + if (b_mediaKeySupport && !o_mediaKeyController) + o_mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self]; + + if (b_mediaKeySupport && ([[[[VLCMain sharedInstance] playlist] model] hasChildren] || + p_current_input)) { + if (!b_mediaKeyTrapEnabled) { + b_mediaKeyTrapEnabled = YES; + msg_Dbg(p_intf, "Enable media key support"); + [o_mediaKeyController startWatchingMediaKeys]; + } + } else { + if (b_mediaKeyTrapEnabled) { + b_mediaKeyTrapEnabled = NO; + msg_Dbg(p_intf, "Disable media key support"); + [o_mediaKeyController stopWatchingMediaKeys]; + } } - else if (!b_mediaKeySupport && o_mediaKeyController) - [o_mediaKeyController stopWatchingMediaKeys]; } @end