From 18176782e705bfcc06baab74daaab1d6769dd9f0 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 9 Jun 2018 19:22:04 -0700 Subject: [PATCH] Update SPMediaKeyTap to a more recent version. --- .../NSObject+SPInvocationGrabbing.m | 92 ++-- ThirdParty/SPMediaKeyTap/SPMediaKeyTap.h | 27 +- ThirdParty/SPMediaKeyTap/SPMediaKeyTap.m | 441 ++++++++++-------- 3 files changed, 306 insertions(+), 254 deletions(-) diff --git a/ThirdParty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m b/ThirdParty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m index b0262c37a..3ca7eb2d2 100644 --- a/ThirdParty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m +++ b/ThirdParty/SPMediaKeyTap/SPInvocationGrabbing/NSObject+SPInvocationGrabbing.m @@ -11,68 +11,66 @@ @implementation SPInvocationGrabber - (id)initWithObject:(id)obj; { - return [self initWithObject:obj stacktraceSaving:YES]; + return [self initWithObject:obj stacktraceSaving:YES]; } -(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack; { - self.object = obj; + self.object = obj; - if(saveStack) - [self saveBacktrace]; + if(saveStack) + [self saveBacktrace]; - return self; + return self; } -(void)dealloc; { - free(frameStrings); - self.object = nil; - self.invocation = nil; + free(frameStrings); + self.object = nil; + self.invocation = nil; } @synthesize invocation = _invocation, object = _object; @synthesize backgroundAfterForward, onMainAfterForward, waitUntilDone; - (void)runInBackground; { - @autoreleasepool { - [self invoke]; - } + [self invoke]; } - (void)forwardInvocation:(NSInvocation *)anInvocation { - [anInvocation retainArguments]; - anInvocation.target = _object; - self.invocation = anInvocation; - - if(backgroundAfterForward) - [NSThread detachNewThreadSelector:@selector(runInBackground) toTarget:self withObject:nil]; - else if(onMainAfterForward) + [anInvocation retainArguments]; + anInvocation.target = _object; + self.invocation = anInvocation; + + if(backgroundAfterForward) + [NSThread detachNewThreadSelector:@selector(runInBackground) toTarget:self withObject:nil]; + else if(onMainAfterForward) [self performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:waitUntilDone]; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)inSelector { - NSMethodSignature *signature = [super methodSignatureForSelector:inSelector]; - if (signature == NULL) - signature = [_object methodSignatureForSelector:inSelector]; - - return signature; + NSMethodSignature *signature = [super methodSignatureForSelector:inSelector]; + if (signature == NULL) + signature = [_object methodSignatureForSelector:inSelector]; + + return signature; } - (void)invoke; { - @try { - [_invocation invoke]; - } - @catch (NSException * e) { - NSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e); - [self printBacktrace]; - printf("\n"); - [e raise]; - } + @try { + [_invocation invoke]; + } + @catch (NSException * e) { + NSLog(@"SPInvocationGrabber's target raised %@:\n\t%@\nInvocation was originally scheduled at:", e.name, e); + [self printBacktrace]; + printf("\n"); + [e raise]; + } - self.invocation = nil; - self.object = nil; + self.invocation = nil; + self.object = nil; } -(void)saveBacktrace; @@ -83,40 +81,40 @@ } -(void)printBacktrace; { - for(int x = 3; x < frameCount; x++) { - if(frameStrings[x] == NULL) { break; } - printf("%s\n", frameStrings[x]); - } + for(int x = 3; x < frameCount; x++) { + if(frameStrings[x] == NULL) { break; } + printf("%s\n", frameStrings[x]); + } } @end @implementation NSObject (SPInvocationGrabbing) -(id)grab; { - return [[SPInvocationGrabber alloc] initWithObject:self]; + return [[SPInvocationGrabber alloc] initWithObject:self]; } -(id)invokeAfter:(NSTimeInterval)delta; { - id grabber = [self grab]; - [NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO]; - return grabber; + id grabber = [self grab]; + [NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO]; + return grabber; } - (id)nextRunloop; { - return [self invokeAfter:0]; + return [self invokeAfter:0]; } -(id)inBackground; { SPInvocationGrabber *grabber = [self grab]; - grabber.backgroundAfterForward = YES; - return grabber; + grabber.backgroundAfterForward = YES; + return grabber; } -(id)onMainAsync:(BOOL)async; { SPInvocationGrabber *grabber = [self grab]; - grabber.onMainAfterForward = YES; + grabber.onMainAfterForward = YES; grabber.waitUntilDone = !async; - return grabber; + return grabber; } @end diff --git a/ThirdParty/SPMediaKeyTap/SPMediaKeyTap.h b/ThirdParty/SPMediaKeyTap/SPMediaKeyTap.h index aa974d238..d2ed793b8 100644 --- a/ThirdParty/SPMediaKeyTap/SPMediaKeyTap.h +++ b/ThirdParty/SPMediaKeyTap/SPMediaKeyTap.h @@ -4,18 +4,19 @@ // http://overooped.com/post/2593597587/mediakeys -#define SPSystemDefinedEventMediaKeys 8 +#define SPSystemDefinedEventMediaKeys 8 +#define SPPassthroughEventData2Value -10 @interface SPMediaKeyTap : NSObject { - EventHandlerRef _app_switching_ref; - EventHandlerRef _app_terminating_ref; - CFMachPortRef _eventPort; - CFRunLoopSourceRef _eventPortSource; - CFRunLoopRef _tapThreadRL; - BOOL _shouldInterceptMediaKeyEvents; - id _delegate; - // The app that is frontmost in this list owns media keys - NSMutableArray *_mediaKeyAppList; + EventHandlerRef _app_switching_ref; + EventHandlerRef _app_terminating_ref; + CFMachPortRef _eventPort; + CFRunLoopSourceRef _eventPortSource; + CFRunLoopRef _tapThreadRL; + BOOL _shouldInterceptMediaKeyEvents; + id _delegate; + // The app that is frontmost in this list owns media keys + NSMutableArray *_mediaKeyAppList; } + (NSArray*)defaultMediaKeyUserBundleIdentifiers; @@ -25,6 +26,9 @@ -(void)startWatchingMediaKeys; -(void)stopWatchingMediaKeys; -(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event; + +@property NSArray *blackListBundleIdentifiers; + @end @interface NSObject (SPMediaKeyTapDelegate) @@ -36,8 +40,9 @@ extern "C" { #endif extern NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey; +extern NSString *kMediaKeyUsingBlackListBundleIdentifiersDefaultsKey; extern NSString *kIgnoreMediaKeysDefaultsKey; #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/ThirdParty/SPMediaKeyTap/SPMediaKeyTap.m b/ThirdParty/SPMediaKeyTap/SPMediaKeyTap.m index d6d5d6ee9..e3e2b442a 100644 --- a/ThirdParty/SPMediaKeyTap/SPMediaKeyTap.m +++ b/ThirdParty/SPMediaKeyTap/SPMediaKeyTap.m @@ -1,6 +1,6 @@ // Copyright (c) 2010 Spotify AB #import "SPMediaKeyTap.h" -#import "SPInvocationGrabbing/NSObject+SPInvocationGrabbing.h" // https://gist.github.com/511181, in submodule +#import "NSObject+SPInvocationGrabbing.h" // https://gist.github.com/511181, in submodule @interface SPMediaKeyTap () -(BOOL)shouldInterceptMediaKeyEvents; @@ -24,79 +24,85 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv #pragma mark Setup and teardown -(id)initWithDelegate:(id)delegate; { - _delegate = delegate; - [self startWatchingAppSwitching]; - singleton = self; - _mediaKeyAppList = [NSMutableArray new]; + _delegate = delegate; + [self startWatchingAppSwitching]; + singleton = self; + _mediaKeyAppList = [NSMutableArray new]; + _blackListBundleIdentifiers = [[NSUserDefaults standardUserDefaults] + arrayForKey:kMediaKeyUsingBlackListBundleIdentifiersDefaultsKey]; _tapThreadRL=nil; _eventPort=nil; _eventPortSource=nil; - return self; + return self; } -(void)dealloc; { - [self stopWatchingMediaKeys]; - [self stopWatchingAppSwitching]; + [self stopWatchingMediaKeys]; + [self stopWatchingAppSwitching]; } -(void)startWatchingAppSwitching; { - // Listen to "app switched" event, so that we don't intercept media keys if we - // weren't the last "media key listening" app to be active - EventTypeSpec eventType = { kEventClassApplication, kEventAppFrontSwitched }; - OSStatus err = InstallApplicationEventHandler(NewEventHandlerUPP(appSwitched), 1, &eventType, (__bridge void *)(self), &_app_switching_ref); - assert(err == noErr); - - eventType.eventKind = kEventAppTerminated; - err = InstallApplicationEventHandler(NewEventHandlerUPP(appTerminated), 1, &eventType, (__bridge void *)(self), &_app_terminating_ref); - assert(err == noErr); + // Listen to "app switched" event, so that we don't intercept media keys if we + // weren't the last "media key listening" app to be active + EventTypeSpec eventType = { kEventClassApplication, kEventAppFrontSwitched }; + OSStatus err = InstallApplicationEventHandler(NewEventHandlerUPP(appSwitched), 1, &eventType, (__bridge void*)self, &_app_switching_ref); + assert(err == noErr); + + eventType.eventKind = kEventAppTerminated; + err = InstallApplicationEventHandler(NewEventHandlerUPP(appTerminated), 1, &eventType, (__bridge void*)self, &_app_terminating_ref); + assert(err == noErr); } -(void)stopWatchingAppSwitching; { - if(!_app_switching_ref) return; - RemoveEventHandler(_app_switching_ref); - _app_switching_ref = NULL; + if(!_app_switching_ref) return; + RemoveEventHandler(_app_switching_ref); + _app_switching_ref = NULL; } -(void)startWatchingMediaKeys;{ // Prevent having multiple mediaKeys threads [self stopWatchingMediaKeys]; - - [self setShouldInterceptMediaKeyEvents:YES]; - - // Add an event tap to intercept the system defined media key events - _eventPort = CGEventTapCreate(kCGSessionEventTap, - kCGHeadInsertEventTap, - kCGEventTapOptionDefault, - CGEventMaskBit(NX_SYSDEFINED), - tapEventCallback, - (__bridge void *)(self)); - assert(_eventPort != NULL); - + + [self setShouldInterceptMediaKeyEvents:YES]; + + @synchronized(self){ + // Add an event tap to intercept the system defined media key events + _eventPort = CGEventTapCreate(kCGSessionEventTap, + kCGHeadInsertEventTap, + kCGEventTapOptionDefault, + CGEventMaskBit(NX_SYSDEFINED), + tapEventCallback, + (__bridge void*)self); + assert(_eventPort != NULL); + _eventPortSource = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, _eventPort, 0); - assert(_eventPortSource != NULL); - - // Let's do this in a separate thread so that a slow app doesn't lag the event tap - [NSThread detachNewThreadSelector:@selector(eventTapThread) toTarget:self withObject:nil]; + assert(_eventPortSource != NULL); + + // Let's do this in a separate thread so that a slow app doesn't lag the event tap + [NSThread detachNewThreadSelector:@selector(eventTapThread) toTarget:self withObject:nil]; + } } -(void)stopWatchingMediaKeys; { - // TODO: Shut down thread, remove event tap port and source - - if(_tapThreadRL){ - CFRunLoopStop(_tapThreadRL); - _tapThreadRL=nil; - } - - if(_eventPort){ - CFMachPortInvalidate(_eventPort); - CFRelease(_eventPort); - _eventPort=nil; - } - - if(_eventPortSource){ - CFRelease(_eventPortSource); - _eventPortSource=nil; + // TODO: Shut down thread, remove event tap port and source + + @synchronized(self) { + if (_tapThreadRL) { + CFRunLoopStop(_tapThreadRL); + _tapThreadRL = nil; + } + + if (_eventPort) { + CFMachPortInvalidate(_eventPort); + CFRelease(_eventPort); + _eventPort = nil; + } + + if (_eventPortSource) { + CFRelease(_eventPortSource); + _eventPortSource = nil; + } } } @@ -106,75 +112,91 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv +(BOOL)usesGlobalMediaKeyTap { #ifdef _DEBUG - // breaking in gdb with a key tap inserted sometimes locks up all mouse and keyboard input forever, forcing reboot - return NO; + // breaking in gdb with a key tap inserted sometimes locks up all mouse and keyboard input forever, forcing reboot + return NO; #else - // XXX(nevyn): MediaKey event tap doesn't work on 10.4, feel free to figure out why if you have the energy. - return - ![[NSUserDefaults standardUserDefaults] boolForKey:kIgnoreMediaKeysDefaultsKey] - && floor(NSAppKitVersionNumber) >= 949/*NSAppKitVersionNumber10_5*/; + // XXX(nevyn): MediaKey event tap doesn't work on 10.4, feel free to figure out why if you have the energy. + return + ![[NSUserDefaults standardUserDefaults] boolForKey:kIgnoreMediaKeysDefaultsKey] + && floor(NSAppKitVersionNumber) >= 949/*NSAppKitVersionNumber10_5*/; #endif } + (NSArray*)defaultMediaKeyUserBundleIdentifiers; { - return [NSArray arrayWithObjects: - [[NSBundle mainBundle] bundleIdentifier], // your app - @"com.spotify.client", - @"com.apple.iTunes", - @"com.apple.QuickTimePlayerX", - @"com.apple.quicktimeplayer", - @"com.apple.iWork.Keynote", - @"com.apple.iPhoto", - @"org.videolan.vlc", - @"com.apple.Aperture", - @"com.plexsquared.Plex", - @"com.soundcloud.desktop", - @"org.niltsh.MPlayerX", - @"com.ilabs.PandorasHelper", - @"com.mahasoftware.pandabar", - @"com.bitcartel.pandorajam", - @"org.clementine-player.clementine", - @"fm.last.Last.fm", - @"fm.last.Scrobbler", - @"com.beatport.BeatportPro", - @"com.Timenut.SongKey", - @"com.macromedia.fireworks", // the tap messes up their mouse input - @"at.justp.Theremin", - nil - ]; + return [NSArray arrayWithObjects: + [[NSBundle mainBundle] bundleIdentifier], + @"com.spotify.client", + @"com.apple.iTunes", + @"com.apple.QuickTimePlayerX", + @"com.apple.quicktimeplayer", + @"com.apple.iWork.Keynote", + @"com.apple.iPhoto", + @"org.videolan.vlc", + @"com.apple.Aperture", + @"com.plexsquared.Plex", + @"com.soundcloud.desktop", + @"org.niltsh.MPlayerX", + @"com.ilabs.PandorasHelper", + @"com.mahasoftware.pandabar", + @"com.bitcartel.pandorajam", + @"org.clementine-player.clementine", + @"fm.last.Last.fm", + @"fm.last.Scrobbler", + @"com.beatport.BeatportPro", + @"com.Timenut.SongKey", + @"com.macromedia.fireworks", // the tap messes up their mouse input + @"at.justp.Theremin", + @"ru.ya.themblsha.YandexMusic", + @"com.jriver.MediaCenter18", + @"com.jriver.MediaCenter19", + @"com.jriver.MediaCenter20", + @"co.rackit.mate", + @"com.ttitt.b-music", + @"com.beardedspice.BeardedSpice", //BeardedSpice + @"com.plug.Plug", + @"com.plug.Plug2", + @"com.netease.163music", + @"com.coppertino.Vox", + @"com.tidal.desktop", + @"com.amazon.music", + nil + ]; } - -(BOOL)shouldInterceptMediaKeyEvents; { - BOOL shouldIntercept = NO; - @synchronized(self) { - shouldIntercept = _shouldInterceptMediaKeyEvents; - } - return shouldIntercept; + BOOL shouldIntercept = NO; + @synchronized(self) { + shouldIntercept = _shouldInterceptMediaKeyEvents; + } + return shouldIntercept; } -(void)pauseTapOnTapThread:(BOOL)yeahno; { - CGEventTapEnable(self->_eventPort, yeahno); + @synchronized(self){ + if (self->_eventPort) { + CGEventTapEnable(self->_eventPort, yeahno); + } + } } -(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting; { - BOOL oldSetting; - @synchronized(self) { - oldSetting = _shouldInterceptMediaKeyEvents; - _shouldInterceptMediaKeyEvents = newSetting; - } - if(_tapThreadRL && oldSetting != newSetting) { - id grab = [self grab]; - [grab pauseTapOnTapThread:newSetting]; - NSTimer *timer = [NSTimer timerWithTimeInterval:0 invocation:[grab invocation] repeats:NO]; - CFRunLoopAddTimer(_tapThreadRL, (CFRunLoopTimerRef)timer, kCFRunLoopCommonModes); - } + BOOL oldSetting; + @synchronized(self) { + oldSetting = _shouldInterceptMediaKeyEvents; + _shouldInterceptMediaKeyEvents = newSetting; + } + if(_tapThreadRL && oldSetting != newSetting) { + id grab = [self grab]; + [grab pauseTapOnTapThread:newSetting]; + NSTimer *timer = [NSTimer timerWithTimeInterval:0 invocation:[grab invocation] repeats:NO]; + CFRunLoopAddTimer(_tapThreadRL, (__bridge CFRunLoopTimerRef)timer, kCFRunLoopCommonModes); + } } -#pragma mark +#pragma mark #pragma mark - #pragma mark Event tap callbacks @@ -182,152 +204,179 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv static CGEventRef tapEventCallback2(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) { - SPMediaKeyTap *self = (__bridge SPMediaKeyTap *)(refcon); + SPMediaKeyTap *self = (__bridge id)refcon; if(type == kCGEventTapDisabledByTimeout) { - NSLog(@"Media key event tap was disabled by timeout"); - CGEventTapEnable(self->_eventPort, TRUE); - return event; - } else if(type == kCGEventTapDisabledByUserInput) { - // Was disabled manually by -[pauseTapOnTapThread] - return event; - } - NSEvent *nsEvent = nil; - @try { - nsEvent = [NSEvent eventWithCGEvent:event]; - } - @catch (NSException * e) { - NSLog(@"Strange CGEventType: %d: %@", type, e); - assert(0); - return event; - } + NSLog(@"Media key event tap was disabled by timeout"); + @synchronized(self){ + if (self->_eventPort) { + CGEventTapEnable(self->_eventPort, TRUE); + } + } + return event; + } else if(type == kCGEventTapDisabledByUserInput) { + // Was disabled manually by -[pauseTapOnTapThread] + return event; + } + NSEvent *nsEvent = nil; + @try { + nsEvent = [NSEvent eventWithCGEvent:event]; + } + @catch (NSException * e) { + NSLog(@"Strange CGEventType: %d: %@", type, e); + assert(0); + return event; + } - if (type != NX_SYSDEFINED || [nsEvent subtype] != SPSystemDefinedEventMediaKeys) - return event; + // Passthrough marker found + if (nsEvent.data2 == SPPassthroughEventData2Value) { + return event; + } + + if (type != NX_SYSDEFINED || [nsEvent subtype] != SPSystemDefinedEventMediaKeys) + return event; - int keyCode = (([nsEvent data1] & 0xFFFF0000) >> 16); - if (keyCode != NX_KEYTYPE_PLAY && keyCode != NX_KEYTYPE_FAST && keyCode != NX_KEYTYPE_REWIND && keyCode != NX_KEYTYPE_PREVIOUS && keyCode != NX_KEYTYPE_NEXT) - return event; + int keyCode = (([nsEvent data1] & 0xFFFF0000) >> 16); + + if (keyCode != NX_KEYTYPE_PLAY + && keyCode != NX_KEYTYPE_FAST + && keyCode != NX_KEYTYPE_REWIND + && keyCode != NX_KEYTYPE_PREVIOUS + && keyCode != NX_KEYTYPE_NEXT + && keyCode != NX_KEYTYPE_MUTE + && keyCode != NX_KEYTYPE_SOUND_UP + && keyCode != NX_KEYTYPE_SOUND_DOWN + ) + return event; - if (![self shouldInterceptMediaKeyEvents]) - return event; - - [self performSelectorOnMainThread:@selector(handleAndReleaseMediaKeyEvent:) withObject:nsEvent waitUntilDone:NO]; - - return NULL; + if (![self shouldInterceptMediaKeyEvents]) + return event; + + [self performSelectorOnMainThread:@selector(handleAndReleaseMediaKeyEvent:) withObject:nsEvent waitUntilDone:NO]; + + return NULL; } static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) { - return tapEventCallback2(proxy, type, event, refcon); + return tapEventCallback2(proxy, type, event, refcon); } // event will have been retained in the other thread -(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event { - [_delegate mediaKeyTap:self receivedMediaKeyEvent:event]; + [_delegate mediaKeyTap:self receivedMediaKeyEvent:event]; } - --(void)eventTapThread; +- (void)eventTapThread; { - _tapThreadRL = CFRunLoopGetCurrent(); - CFRunLoopAddSource(_tapThreadRL, _eventPortSource, kCFRunLoopCommonModes); - CFRunLoopRun(); + @synchronized(self) { + if (_eventPortSource) { + _tapThreadRL = CFRunLoopGetCurrent(); + + if (_tapThreadRL) { + CFRunLoopAddSource(_tapThreadRL, _eventPortSource, + kCFRunLoopCommonModes); + } + } + } + CFRunLoopRun(); } #pragma mark Task switching callbacks NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey = @"SPApplicationsNeedingMediaKeys"; +NSString *kMediaKeyUsingBlackListBundleIdentifiersDefaultsKey = @"SPApplicationsNotNeedingMediaKeys"; NSString *kIgnoreMediaKeysDefaultsKey = @"SPIgnoreMediaKeys"; -(void)mediaKeyAppListChanged; { - if([_mediaKeyAppList count] == 0) return; - - /*NSLog(@"--"); - int i = 0; - for (NSValue *psnv in _mediaKeyAppList) { - ProcessSerialNumber psn; [psnv getValue:&psn]; - NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary( - &psn, - kProcessDictionaryIncludeAllInformationMask - ) autorelease]; - NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey]; - NSLog(@"%d: %@", i++, bundleIdentifier); - }*/ - - ProcessSerialNumber mySerial, topSerial; - GetCurrentProcess(&mySerial); - [[_mediaKeyAppList objectAtIndex:0] getValue:&topSerial]; + if([_mediaKeyAppList count] == 0) return; - Boolean same; - OSErr err = SameProcess(&mySerial, &topSerial, &same); - [self setShouldInterceptMediaKeyEvents:(err == noErr && same)]; + /*NSLog(@"--"); + int i = 0; + for (NSValue *psnv in _mediaKeyAppList) { + ProcessSerialNumber psn; [psnv getValue:&psn]; + NSDictionary *processInfo = [(id)ProcessInformationCopyDictionary( + &psn, + kProcessDictionaryIncludeAllInformationMask + ) autorelease]; + NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey]; + NSLog(@"%d: %@", i++, bundleIdentifier); + }*/ + + ProcessSerialNumber mySerial, topSerial; + GetCurrentProcess(&mySerial); + [[_mediaKeyAppList objectAtIndex:0] getValue:&topSerial]; + + Boolean same; + OSErr err = SameProcess(&mySerial, &topSerial, &same); + [self setShouldInterceptMediaKeyEvents:(err == noErr && same)]; } --(void)appIsNowFrontmost:(ProcessSerialNumber)psn; +- (void)appIsNowFrontmost:(ProcessSerialNumber)psn; { - NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)]; + NSValue *psnv = + [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)]; - CFDictionaryRef cfDict = ProcessInformationCopyDictionary( - &psn, - kProcessDictionaryIncludeAllInformationMask - ); - - if (!cfDict) return; - - NSDictionary *processInfo = (__bridge id)cfDict; - NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey]; - - CFRelease(cfDict); + NSDictionary *processInfo = CFBridgingRelease(ProcessInformationCopyDictionary( + &psn, kProcessDictionaryIncludeAllInformationMask)); + NSString *bundleIdentifier = + [processInfo objectForKey:(id)kCFBundleIdentifierKey]; - NSArray *whitelistIdentifiers = [[NSUserDefaults standardUserDefaults] arrayForKey:kMediaKeyUsingBundleIdentifiersDefaultsKey]; - if(![whitelistIdentifiers containsObject:bundleIdentifier]) return; + if ([self.blackListBundleIdentifiers containsObject:bundleIdentifier]) { + NSLog(@"Media key event tap was activated by blacklist"); + [self setShouldInterceptMediaKeyEvents:YES]; + return; + } + NSArray *whitelistIdentifiers = [[NSUserDefaults standardUserDefaults] + arrayForKey:kMediaKeyUsingBundleIdentifiersDefaultsKey]; + if (![whitelistIdentifiers containsObject:bundleIdentifier]) + return; - [_mediaKeyAppList removeObject:psnv]; - [_mediaKeyAppList insertObject:psnv atIndex:0]; - [self mediaKeyAppListChanged]; + [_mediaKeyAppList removeObject:psnv]; + [_mediaKeyAppList insertObject:psnv atIndex:0]; + [self mediaKeyAppListChanged]; } -(void)appTerminated:(ProcessSerialNumber)psn; { - NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)]; - [_mediaKeyAppList removeObject:psnv]; - [self mediaKeyAppListChanged]; + NSValue *psnv = [NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)]; + [_mediaKeyAppList removeObject:psnv]; + [self mediaKeyAppListChanged]; } static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef evt, void* userData) { - SPMediaKeyTap *self = (__bridge id)userData; + SPMediaKeyTap *self = (__bridge id)userData; ProcessSerialNumber newSerial; GetFrontProcess(&newSerial); - - [self appIsNowFrontmost:newSerial]; - + + [self appIsNowFrontmost:newSerial]; + return CallNextEventHandler(nextHandler, evt); } static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData) { - SPMediaKeyTap *self = (__bridge id)userData; - - ProcessSerialNumber deadPSN; + SPMediaKeyTap *self = (__bridge id)userData; - GetEventParameter( - evt, - kEventParamProcessID, - typeProcessSerialNumber, - NULL, - sizeof(deadPSN), - NULL, - &deadPSN - ); + ProcessSerialNumber deadPSN; - - [self appTerminated:deadPSN]; + GetEventParameter( + evt, + kEventParamProcessID, + typeProcessSerialNumber, + NULL, + sizeof(deadPSN), + NULL, + &deadPSN + ); + + + [self appTerminated:deadPSN]; return CallNextEventHandler(nextHandler, evt); }