Update SPMediaKeyTap to a more recent version.
parent
268a293a7a
commit
f0c7c0b777
|
@ -34,9 +34,7 @@
|
|||
@synthesize backgroundAfterForward, onMainAfterForward, waitUntilDone;
|
||||
- (void)runInBackground;
|
||||
{
|
||||
@autoreleasepool {
|
||||
[self invoke];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// http://overooped.com/post/2593597587/mediakeys
|
||||
|
||||
#define SPSystemDefinedEventMediaKeys 8
|
||||
#define SPPassthroughEventData2Value -10
|
||||
|
||||
@interface SPMediaKeyTap : NSObject {
|
||||
EventHandlerRef _app_switching_ref;
|
||||
|
@ -25,6 +26,9 @@
|
|||
-(void)startWatchingMediaKeys;
|
||||
-(void)stopWatchingMediaKeys;
|
||||
-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event;
|
||||
|
||||
@property NSArray *blackListBundleIdentifiers;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSObject (SPMediaKeyTapDelegate)
|
||||
|
@ -36,6 +40,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
extern NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey;
|
||||
extern NSString *kMediaKeyUsingBlackListBundleIdentifiersDefaultsKey;
|
||||
extern NSString *kIgnoreMediaKeysDefaultsKey;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -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;
|
||||
|
@ -28,6 +28,8 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
|
|||
[self startWatchingAppSwitching];
|
||||
singleton = self;
|
||||
_mediaKeyAppList = [NSMutableArray new];
|
||||
_blackListBundleIdentifiers = [[NSUserDefaults standardUserDefaults]
|
||||
arrayForKey:kMediaKeyUsingBlackListBundleIdentifiersDefaultsKey];
|
||||
_tapThreadRL=nil;
|
||||
_eventPort=nil;
|
||||
_eventPortSource=nil;
|
||||
|
@ -44,11 +46,11 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
|
|||
// 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);
|
||||
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);
|
||||
err = InstallApplicationEventHandler(NewEventHandlerUPP(appTerminated), 1, &eventType, (__bridge void*)self, &_app_terminating_ref);
|
||||
assert(err == noErr);
|
||||
}
|
||||
-(void)stopWatchingAppSwitching;
|
||||
|
@ -64,13 +66,14 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
|
|||
|
||||
[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));
|
||||
(__bridge void*)self);
|
||||
assert(_eventPort != NULL);
|
||||
|
||||
_eventPortSource = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, _eventPort, 0);
|
||||
|
@ -78,25 +81,28 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
|
|||
|
||||
// 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<nevyn>: Shut down thread, remove event tap port and source
|
||||
|
||||
if(_tapThreadRL){
|
||||
@synchronized(self) {
|
||||
if (_tapThreadRL) {
|
||||
CFRunLoopStop(_tapThreadRL);
|
||||
_tapThreadRL=nil;
|
||||
_tapThreadRL = nil;
|
||||
}
|
||||
|
||||
if(_eventPort){
|
||||
if (_eventPort) {
|
||||
CFMachPortInvalidate(_eventPort);
|
||||
CFRelease(_eventPort);
|
||||
_eventPort=nil;
|
||||
_eventPort = nil;
|
||||
}
|
||||
|
||||
if(_eventPortSource){
|
||||
if (_eventPortSource) {
|
||||
CFRelease(_eventPortSource);
|
||||
_eventPortSource=nil;
|
||||
_eventPortSource = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,7 +125,7 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
|
|||
+ (NSArray*)defaultMediaKeyUserBundleIdentifiers;
|
||||
{
|
||||
return [NSArray arrayWithObjects:
|
||||
[[NSBundle mainBundle] bundleIdentifier], // your app
|
||||
[[NSBundle mainBundle] bundleIdentifier],
|
||||
@"com.spotify.client",
|
||||
@"com.apple.iTunes",
|
||||
@"com.apple.QuickTimePlayerX",
|
||||
|
@ -141,11 +147,23 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
|
|||
@"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;
|
||||
|
@ -157,7 +175,11 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
|
|||
|
||||
-(void)pauseTapOnTapThread:(BOOL)yeahno;
|
||||
{
|
||||
@synchronized(self){
|
||||
if (self->_eventPort) {
|
||||
CGEventTapEnable(self->_eventPort, yeahno);
|
||||
}
|
||||
}
|
||||
}
|
||||
-(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting;
|
||||
{
|
||||
|
@ -170,7 +192,7 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
|
|||
id grab = [self grab];
|
||||
[grab pauseTapOnTapThread:newSetting];
|
||||
NSTimer *timer = [NSTimer timerWithTimeInterval:0 invocation:[grab invocation] repeats:NO];
|
||||
CFRunLoopAddTimer(_tapThreadRL, (CFRunLoopTimerRef)timer, kCFRunLoopCommonModes);
|
||||
CFRunLoopAddTimer(_tapThreadRL, (__bridge CFRunLoopTimerRef)timer, kCFRunLoopCommonModes);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,11 +204,15 @@ 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");
|
||||
@synchronized(self){
|
||||
if (self->_eventPort) {
|
||||
CGEventTapEnable(self->_eventPort, TRUE);
|
||||
}
|
||||
}
|
||||
return event;
|
||||
} else if(type == kCGEventTapDisabledByUserInput) {
|
||||
// Was disabled manually by -[pauseTapOnTapThread]
|
||||
|
@ -202,11 +228,25 @@ static CGEventRef tapEventCallback2(CGEventTapProxy proxy, CGEventType type, CGE
|
|||
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)
|
||||
|
||||
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])
|
||||
|
@ -228,17 +268,25 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
|
|||
[_delegate mediaKeyTap:self receivedMediaKeyEvent:event];
|
||||
}
|
||||
|
||||
|
||||
-(void)eventTapThread;
|
||||
- (void)eventTapThread;
|
||||
{
|
||||
@synchronized(self) {
|
||||
if (_eventPortSource) {
|
||||
_tapThreadRL = CFRunLoopGetCurrent();
|
||||
CFRunLoopAddSource(_tapThreadRL, _eventPortSource, kCFRunLoopCommonModes);
|
||||
|
||||
if (_tapThreadRL) {
|
||||
CFRunLoopAddSource(_tapThreadRL, _eventPortSource,
|
||||
kCFRunLoopCommonModes);
|
||||
}
|
||||
}
|
||||
}
|
||||
CFRunLoopRun();
|
||||
}
|
||||
|
||||
#pragma mark Task switching callbacks
|
||||
|
||||
NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey = @"SPApplicationsNeedingMediaKeys";
|
||||
NSString *kMediaKeyUsingBlackListBundleIdentifiersDefaultsKey = @"SPApplicationsNotNeedingMediaKeys";
|
||||
NSString *kIgnoreMediaKeysDefaultsKey = @"SPIgnoreMediaKeys";
|
||||
|
||||
|
||||
|
@ -268,24 +316,25 @@ NSString *kIgnoreMediaKeysDefaultsKey = @"SPIgnoreMediaKeys";
|
|||
[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
|
||||
);
|
||||
NSDictionary *processInfo = CFBridgingRelease(ProcessInformationCopyDictionary(
|
||||
&psn, kProcessDictionaryIncludeAllInformationMask));
|
||||
NSString *bundleIdentifier =
|
||||
[processInfo objectForKey:(id)kCFBundleIdentifierKey];
|
||||
|
||||
if (!cfDict) return;
|
||||
|
||||
NSDictionary *processInfo = (__bridge id)cfDict;
|
||||
NSString *bundleIdentifier = [processInfo objectForKey:(id)kCFBundleIdentifierKey];
|
||||
|
||||
CFRelease(cfDict);
|
||||
|
||||
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];
|
||||
|
|
Loading…
Reference in New Issue