diff --git a/modules/notify/osx_notifications.m b/modules/notify/osx_notifications.m index 96fde610de0c84cb64cdd3fc428919956410b2db..d61131a77c94dae6d9d50bda83cea90eb5479559 100644 --- a/modules/notify/osx_notifications.m +++ b/modules/notify/osx_notifications.m @@ -4,11 +4,11 @@ * This plugin provides support for macOS notifications on current playlist * item changes. ***************************************************************************** - * Copyright © 2008, 2011, 2012, 2015, 2018 the VideoLAN team + * Copyright © 2008, 2011, 2012, 2015, 2018, 2019 the VideoLAN team * - * Authors: Rafaël Carré <funman@videolanorg> - * Felix Paul Kühne <fkuehne@videolan.org - * Marvin Scholz <epirat07@gmail.com> + * Authors: Marvin Scholz <epirat07@gmail.com> + * Felix Paul Kühne <fkuehne # videolan.org> + * Rafaël Carré <funman@videolanorg> * * 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 @@ -31,14 +31,12 @@ # include "config.h" #endif -#import <Foundation/Foundation.h> #import <Cocoa/Cocoa.h> #include <vlc_common.h> #include <vlc_plugin.h> -#include <vlc_playlist_legacy.h> -#include <vlc_input.h> -#include <vlc_meta.h> +#include <vlc_playlist.h> +#include <vlc_player.h> #include <vlc_interface.h> #include <vlc_url.h> @@ -46,14 +44,14 @@ #pragma mark Class interfaces @interface VLCNotificationDelegate : NSObject <NSUserNotificationCenterDelegate> { - /** Interface thread, required for skipping to the next item */ - intf_thread_t * _Nonnull interfaceThread; - /** Holds the last notification so it can be cleared when the next one is delivered */ - NSUserNotification * _Nullable lastNotification; - - /** Indicates if VLC is in foreground */ - BOOL isInForeground; + NSUserNotification * _Nullable _lastNotification; + + /* the playlist reference */ + vlc_playlist_t *_p_playlist; + + /* the listener ID for playlist notifications */ + vlc_playlist_listener_id *_playlistListenerID; } /** @@ -62,23 +60,43 @@ - (instancetype)initWithInterfaceThread:(intf_thread_t * _Nonnull)intf_thread; /** - * Delegate method called when the current input changed + * Delegate method called when the current playlist item changed */ -- (void)currentInputDidChanged:(input_thread_t * _Nonnull)input; +- (void)currentPlaylistItemChanged:(size_t)index; @end - -#pragma mark - -#pragma mark Local prototypes struct intf_sys_t { void *vlcNotificationDelegate; }; -static int InputCurrent(vlc_object_t *, const char *, - vlc_value_t, vlc_value_t, void *); +#pragma mark - +#pragma mark callback +static void +cb_playlist_current_item_changed(vlc_playlist_t *playlist, + ssize_t index, + void *p_data) +{ + dispatch_async(dispatch_get_main_queue(), ^{ + VLCNotificationDelegate *notificationDelegate = (__bridge VLCNotificationDelegate *)p_data; + [notificationDelegate currentPlaylistItemChanged:index]; + }); +} + +static const struct vlc_playlist_callbacks playlist_callbacks = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + cb_playlist_current_item_changed, + NULL, + NULL, +}; #pragma mark - #pragma mark C module functions @@ -88,7 +106,6 @@ static int InputCurrent(vlc_object_t *, const char *, static int Open(vlc_object_t *p_this) { intf_thread_t *p_intf = (intf_thread_t *)p_this; - playlist_t *p_playlist = pl_Get(p_intf); intf_sys_t *p_sys = p_intf->p_sys = calloc(1, sizeof(intf_sys_t)); if (!p_sys) @@ -106,8 +123,6 @@ static int Open(vlc_object_t *p_this) p_sys->vlcNotificationDelegate = (__bridge_retained void*)notificationDelegate; } - var_AddCallback(p_playlist, "input-current", InputCurrent, p_intf); - return VLC_SUCCESS; } @@ -117,12 +132,7 @@ static int Open(vlc_object_t *p_this) static void Close(vlc_object_t *p_this) { intf_thread_t *p_intf = (intf_thread_t *)p_this; - playlist_t *p_playlist = pl_Get(p_intf); intf_sys_t *p_sys = p_intf->p_sys; - - // Remove the callback, this must be done here, before deallocating the - // notification delegate object - var_DelCallback(p_playlist, "input-current", InputCurrent, p_intf); @autoreleasepool { // Transfer ownership of notification delegate object back to ARC @@ -136,27 +146,6 @@ static void Close(vlc_object_t *p_this) free(p_sys); } -/* - * Callback invoked on playlist item change - */ -static int InputCurrent(vlc_object_t *p_this, const char *psz_var, - vlc_value_t oldval, vlc_value_t newval, void *param) -{ - intf_thread_t *p_intf = (intf_thread_t *)param; - intf_sys_t *p_sys = p_intf->p_sys; - input_thread_t *p_input = newval.p_address; - VLC_UNUSED(oldval); - - @autoreleasepool { - VLCNotificationDelegate *notificationDelegate = - (__bridge VLCNotificationDelegate*)p_sys->vlcNotificationDelegate; - - [notificationDelegate currentInputDidChanged:(input_thread_t *)p_input]; - } - - return VLC_SUCCESS; -} - /** * Transfers a null-terminated UTF-8 C "string" to a NSString * in a way that the NSString takes ownership of it. @@ -192,18 +181,13 @@ static inline NSString* CharsToNSString(char * _Nullable cStr) self = [super init]; if (self) { - interfaceThread = intf_thread; - - // Subscribe to notifications to determine if VLC is in foreground or not - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationActiveChange:) - name:NSApplicationDidBecomeActiveNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationActiveChange:) - name:NSApplicationDidResignActiveNotification - object:nil]; + _p_playlist = vlc_intf_GetMainPlaylist(intf_thread); + vlc_playlist_Lock(_p_playlist); + _playlistListenerID = vlc_playlist_AddListener(_p_playlist, + &playlist_callbacks, + (__bridge void *)self, + YES); + vlc_playlist_Unlock(_p_playlist); [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self]; } @@ -211,36 +195,58 @@ static inline NSString* CharsToNSString(char * _Nullable cStr) return self; } -- (void)currentInputDidChanged:(input_thread_t *)input +- (void)dealloc { - if (!input) - return; - - input_item_t *item = input_GetItem(input); - if (!item) + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + // Clear a remaining lastNotification in Notification Center, if any + if (_lastNotification) { + [[NSUserNotificationCenter defaultUserNotificationCenter] + removeDeliveredNotification:_lastNotification]; + _lastNotification = nil; + } + + if (_p_playlist) { + if (_playlistListenerID) { + vlc_playlist_Lock(_p_playlist); + vlc_playlist_RemoveListener(_p_playlist, _playlistListenerID); + vlc_playlist_Unlock(_p_playlist); + } + } +} + +- (void)currentPlaylistItemChanged:(size_t)index +{ + vlc_player_t *player = vlc_playlist_GetPlayer(_p_playlist); + vlc_player_Lock(player); + input_item_t *inputItem = vlc_player_HoldCurrentMedia(player); + vlc_player_Unlock(player); + if (inputItem == NULL) { return; + } // Get title, first try now playing - NSString *title = CharsToNSString(input_item_GetNowPlayingFb(item)); + NSString *title = CharsToNSString(input_item_GetNowPlayingFb(inputItem)); // Fallback to item title or name if ([title length] == 0) - title = CharsToNSString(input_item_GetTitleFbName(item)); + title = CharsToNSString(input_item_GetTitleFbName(inputItem)); // If there is still not title, do not notify - if (unlikely([title length] == 0)) + if (unlikely([title length] == 0)) { return; + } // Get artist name - NSString *artist = CharsToNSString(input_item_GetArtist(item)); + NSString *artist = CharsToNSString(input_item_GetArtist(inputItem)); // Get album name - NSString *album = CharsToNSString(input_item_GetAlbum(item)); + NSString *album = CharsToNSString(input_item_GetAlbum(inputItem)); // Get coverart path NSString *artPath = nil; - char *psz_arturl = input_item_GetArtURL(item); + char *psz_arturl = input_item_GetArtURL(inputItem); if (psz_arturl) { artPath = CharsToNSString(vlc_uri2path(psz_arturl)); free(psz_arturl); @@ -259,16 +265,6 @@ static inline NSString* CharsToNSString(char * _Nullable cStr) [self notifyWithTitle:title description:desc imagePath:artPath]; } -/* - * Called when the applications activity status changes - */ -- (void)applicationActiveChange:(NSNotification *)n { - if (n.name == NSApplicationDidBecomeActiveNotification) - isInForeground = YES; - else if (n.name == NSApplicationDidResignActiveNotification) - isInForeground = NO; -} - /* * Called when the user interacts with a notification */ @@ -278,7 +274,9 @@ static inline NSString* CharsToNSString(char * _Nullable cStr) // Check if notification button ("Skip") was clicked if (notification.activationType == NSUserNotificationActivationTypeActionButtonClicked) { // Skip to next song - playlist_Next(pl_Get(interfaceThread)); + vlc_playlist_Lock(_p_playlist); + vlc_playlist_Next(_p_playlist); + vlc_playlist_Unlock(_p_playlist); } } @@ -289,10 +287,10 @@ static inline NSString* CharsToNSString(char * _Nullable cStr) didDeliverNotification:(NSUserNotification *)notification { // Only keep the most recent notification in the Notification Center - if (lastNotification) - [center removeDeliveredNotification:lastNotification]; + if (_lastNotification) + [center removeDeliveredNotification:_lastNotification]; - lastNotification = notification; + _lastNotification = notification; } /* @@ -335,21 +333,6 @@ static inline NSString* CharsToNSString(char * _Nullable cStr) deliverNotification:notification]; } -/* - * Cleanup - */ -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - // Clear a remaining lastNotification in Notification Center, if any - if (lastNotification) { - [[NSUserNotificationCenter defaultUserNotificationCenter] - removeDeliveredNotification:lastNotification]; - lastNotification = nil; - } -} - @end