Remove ThirdParty SPMediaKeyTap
This commit is contained in:
parent
d0ee3622ed
commit
5e3ed2af4b
7 changed files with 1 additions and 629 deletions
|
@ -7,14 +7,8 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#import <IOKit/hidsystem/ev_keymap.h>
|
|
||||||
|
|
||||||
@class SPMediaKeyTap;
|
|
||||||
|
|
||||||
@interface MediaKeysApplication : NSApplication
|
@interface MediaKeysApplication : NSApplication
|
||||||
{
|
|
||||||
SPMediaKeyTap *keyTap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
#import "MediaKeysApplication.h"
|
#import "MediaKeysApplication.h"
|
||||||
#import "AppController.h"
|
#import "AppController.h"
|
||||||
#import "SPMediaKeyTap.h"
|
|
||||||
#import "Logging.h"
|
#import "Logging.h"
|
||||||
|
|
||||||
#import <MediaPlayer/MPNowPlayingInfoCenter.h>
|
#import <MediaPlayer/MPNowPlayingInfoCenter.h>
|
||||||
|
@ -22,11 +21,6 @@
|
||||||
- (void)finishLaunching {
|
- (void)finishLaunching {
|
||||||
[super finishLaunching];
|
[super finishLaunching];
|
||||||
|
|
||||||
[[NSUserDefaults standardUserDefaults] addObserver:self
|
|
||||||
forKeyPath:@"allowLastfmMediaKeys"
|
|
||||||
options:NSKeyValueObservingOptionNew
|
|
||||||
context:nil];
|
|
||||||
|
|
||||||
MPRemoteCommandCenter *remoteCommandCenter = [MPRemoteCommandCenter sharedCommandCenter];
|
MPRemoteCommandCenter *remoteCommandCenter = [MPRemoteCommandCenter sharedCommandCenter];
|
||||||
|
|
||||||
[remoteCommandCenter.playCommand setEnabled:YES];
|
[remoteCommandCenter.playCommand setEnabled:YES];
|
||||||
|
|
|
@ -131,8 +131,6 @@
|
||||||
836D28A818086386005B7299 /* MiniModeMenuTitleTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 836D28A718086386005B7299 /* MiniModeMenuTitleTransformer.m */; };
|
836D28A818086386005B7299 /* MiniModeMenuTitleTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 836D28A718086386005B7299 /* MiniModeMenuTitleTransformer.m */; };
|
||||||
836F5BF91A357A01002730CC /* sidplay.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8314D6411A354DFF00EEE8E6 /* sidplay.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
836F5BF91A357A01002730CC /* sidplay.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8314D6411A354DFF00EEE8E6 /* sidplay.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
836FB5A718206F2500B3AD2D /* Hively.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 836FB5471820538800B3AD2D /* Hively.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
836FB5A718206F2500B3AD2D /* Hively.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 836FB5471820538800B3AD2D /* Hively.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||||
83790D501809F4980073CF51 /* NSObject+SPInvocationGrabbing.m in Sources */ = {isa = PBXBuildFile; fileRef = 83790D4D1809F4980073CF51 /* NSObject+SPInvocationGrabbing.m */; };
|
|
||||||
83790D511809F4980073CF51 /* SPMediaKeyTap.m in Sources */ = {isa = PBXBuildFile; fileRef = 83790D4F1809F4980073CF51 /* SPMediaKeyTap.m */; };
|
|
||||||
838491211807F38A00E7332D /* NowPlayingBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 8384911D1807F38A00E7332D /* NowPlayingBarView.m */; };
|
838491211807F38A00E7332D /* NowPlayingBarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 8384911D1807F38A00E7332D /* NowPlayingBarView.m */; };
|
||||||
838491221807F38A00E7332D /* NowPlayingBarController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8384911E1807F38A00E7332D /* NowPlayingBarController.xib */; };
|
838491221807F38A00E7332D /* NowPlayingBarController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8384911E1807F38A00E7332D /* NowPlayingBarController.xib */; };
|
||||||
838491231807F38A00E7332D /* NowPlayingBarController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8384911F1807F38A00E7332D /* NowPlayingBarController.m */; };
|
838491231807F38A00E7332D /* NowPlayingBarController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8384911F1807F38A00E7332D /* NowPlayingBarController.m */; };
|
||||||
|
@ -888,10 +886,6 @@
|
||||||
836F6B2518BDB80D0095E648 /* vgmstream.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = vgmstream.xcodeproj; path = Plugins/vgmstream/vgmstream.xcodeproj; sourceTree = "<group>"; };
|
836F6B2518BDB80D0095E648 /* vgmstream.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = vgmstream.xcodeproj; path = Plugins/vgmstream/vgmstream.xcodeproj; sourceTree = "<group>"; };
|
||||||
836FB5421820538700B3AD2D /* Hively.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Hively.xcodeproj; path = Plugins/Hively/Hively.xcodeproj; sourceTree = "<group>"; };
|
836FB5421820538700B3AD2D /* Hively.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Hively.xcodeproj; path = Plugins/Hively/Hively.xcodeproj; sourceTree = "<group>"; };
|
||||||
8375B05117FFEA400092A79F /* OpusPlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpusPlugin.xcodeproj; path = Plugins/Opus/OpusPlugin.xcodeproj; sourceTree = "<group>"; };
|
8375B05117FFEA400092A79F /* OpusPlugin.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpusPlugin.xcodeproj; path = Plugins/Opus/OpusPlugin.xcodeproj; sourceTree = "<group>"; };
|
||||||
83790D4C1809F4980073CF51 /* NSObject+SPInvocationGrabbing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+SPInvocationGrabbing.h"; sourceTree = "<group>"; };
|
|
||||||
83790D4D1809F4980073CF51 /* NSObject+SPInvocationGrabbing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+SPInvocationGrabbing.m"; sourceTree = "<group>"; };
|
|
||||||
83790D4E1809F4980073CF51 /* SPMediaKeyTap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPMediaKeyTap.h; sourceTree = "<group>"; };
|
|
||||||
83790D4F1809F4980073CF51 /* SPMediaKeyTap.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPMediaKeyTap.m; sourceTree = "<group>"; };
|
|
||||||
8384911D1807F38A00E7332D /* NowPlayingBarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NowPlayingBarView.m; path = Window/NowPlayingBarView.m; sourceTree = "<group>"; };
|
8384911D1807F38A00E7332D /* NowPlayingBarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NowPlayingBarView.m; path = Window/NowPlayingBarView.m; sourceTree = "<group>"; };
|
||||||
8384911E1807F38A00E7332D /* NowPlayingBarController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = NowPlayingBarController.xib; path = Window/NowPlayingBarController.xib; sourceTree = "<group>"; };
|
8384911E1807F38A00E7332D /* NowPlayingBarController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = NowPlayingBarController.xib; path = Window/NowPlayingBarController.xib; sourceTree = "<group>"; };
|
||||||
8384911F1807F38A00E7332D /* NowPlayingBarController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NowPlayingBarController.m; path = Window/NowPlayingBarController.m; sourceTree = "<group>"; };
|
8384911F1807F38A00E7332D /* NowPlayingBarController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = NowPlayingBarController.m; path = Window/NowPlayingBarController.m; sourceTree = "<group>"; };
|
||||||
|
@ -1097,7 +1091,6 @@
|
||||||
177EBF770B8BC2A70000BC8C /* ThirdParty */ = {
|
177EBF770B8BC2A70000BC8C /* ThirdParty */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
83790D4A1809F4980073CF51 /* SPMediaKeyTap */,
|
|
||||||
177EBF850B8BC2A70000BC8C /* ImageTextCell */,
|
177EBF850B8BC2A70000BC8C /* ImageTextCell */,
|
||||||
179790DD0C087AB7001D6996 /* OpenURLPanel */,
|
179790DD0C087AB7001D6996 /* OpenURLPanel */,
|
||||||
);
|
);
|
||||||
|
@ -1596,25 +1589,6 @@
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
83790D4A1809F4980073CF51 /* SPMediaKeyTap */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
83790D4B1809F4980073CF51 /* SPInvocationGrabbing */,
|
|
||||||
83790D4E1809F4980073CF51 /* SPMediaKeyTap.h */,
|
|
||||||
83790D4F1809F4980073CF51 /* SPMediaKeyTap.m */,
|
|
||||||
);
|
|
||||||
path = SPMediaKeyTap;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
83790D4B1809F4980073CF51 /* SPInvocationGrabbing */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
83790D4C1809F4980073CF51 /* NSObject+SPInvocationGrabbing.h */,
|
|
||||||
83790D4D1809F4980073CF51 /* NSObject+SPInvocationGrabbing.m */,
|
|
||||||
);
|
|
||||||
path = SPInvocationGrabbing;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
83B0669D180D5668008E3612 /* Products */ = {
|
83B0669D180D5668008E3612 /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -1837,7 +1811,7 @@
|
||||||
LastUpgradeCheck = 1100;
|
LastUpgradeCheck = 1100;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
8D1107260486CEB800E47090 = {
|
8D1107260486CEB800E47090 = {
|
||||||
DevelopmentTeam = "";
|
DevelopmentTeam = RXH4D9SUXM;
|
||||||
LastSwiftMigration = 1220;
|
LastSwiftMigration = 1220;
|
||||||
ProvisioningStyle = Automatic;
|
ProvisioningStyle = Automatic;
|
||||||
};
|
};
|
||||||
|
@ -2324,7 +2298,6 @@
|
||||||
8E75757209F31D5A0080F1EE /* PlaylistController.m in Sources */,
|
8E75757209F31D5A0080F1EE /* PlaylistController.m in Sources */,
|
||||||
8E75757309F31D5A0080F1EE /* PlaylistEntry.m in Sources */,
|
8E75757309F31D5A0080F1EE /* PlaylistEntry.m in Sources */,
|
||||||
8E75757409F31D5A0080F1EE /* PlaylistView.m in Sources */,
|
8E75757409F31D5A0080F1EE /* PlaylistView.m in Sources */,
|
||||||
83790D511809F4980073CF51 /* SPMediaKeyTap.m in Sources */,
|
|
||||||
8E75757509F31D5A0080F1EE /* Shuffle.m in Sources */,
|
8E75757509F31D5A0080F1EE /* Shuffle.m in Sources */,
|
||||||
8E1296DB0A2BA9CE00443124 /* PlaylistHeaderView.m in Sources */,
|
8E1296DB0A2BA9CE00443124 /* PlaylistHeaderView.m in Sources */,
|
||||||
8E07AB790AAC930B00A4B32F /* PreferencesController.m in Sources */,
|
8E07AB790AAC930B00A4B32F /* PreferencesController.m in Sources */,
|
||||||
|
@ -2340,7 +2313,6 @@
|
||||||
8E9A30160BA792DC0091081B /* NSFileHandle+CreateFile.m in Sources */,
|
8E9A30160BA792DC0091081B /* NSFileHandle+CreateFile.m in Sources */,
|
||||||
179790E10C087AB7001D6996 /* OpenURLPanel.m in Sources */,
|
179790E10C087AB7001D6996 /* OpenURLPanel.m in Sources */,
|
||||||
EDAAA41F25A665C000731773 /* PositionSliderToolbarItem.swift in Sources */,
|
EDAAA41F25A665C000731773 /* PositionSliderToolbarItem.swift in Sources */,
|
||||||
83790D501809F4980073CF51 /* NSObject+SPInvocationGrabbing.m in Sources */,
|
|
||||||
1791FF900CB43A2C0070BC5C /* MediaKeysApplication.m in Sources */,
|
1791FF900CB43A2C0070BC5C /* MediaKeysApplication.m in Sources */,
|
||||||
838491211807F38A00E7332D /* NowPlayingBarView.m in Sources */,
|
838491211807F38A00E7332D /* NowPlayingBarView.m in Sources */,
|
||||||
5604D45B0D60349B004F5C5D /* SpotlightWindowController.m in Sources */,
|
5604D45B0D60349B004F5C5D /* SpotlightWindowController.m in Sources */,
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
@interface SPInvocationGrabber : NSObject {
|
|
||||||
id _object;
|
|
||||||
NSInvocation *_invocation;
|
|
||||||
int frameCount;
|
|
||||||
char **frameStrings;
|
|
||||||
BOOL backgroundAfterForward;
|
|
||||||
BOOL onMainAfterForward;
|
|
||||||
BOOL waitUntilDone;
|
|
||||||
}
|
|
||||||
-(id)initWithObject:(id)obj;
|
|
||||||
-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack;
|
|
||||||
@property (readonly, retain, nonatomic) id object;
|
|
||||||
@property (readonly, retain, nonatomic) NSInvocation *invocation;
|
|
||||||
@property BOOL backgroundAfterForward;
|
|
||||||
@property BOOL onMainAfterForward;
|
|
||||||
@property BOOL waitUntilDone;
|
|
||||||
-(void)invoke; // will release object and invocation
|
|
||||||
-(void)printBacktrace;
|
|
||||||
-(void)saveBacktrace;
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface NSObject (SPInvocationGrabbing)
|
|
||||||
-(id)grab;
|
|
||||||
-(id)invokeAfter:(NSTimeInterval)delta;
|
|
||||||
-(id)nextRunloop;
|
|
||||||
-(id)inBackground;
|
|
||||||
-(id)onMainAsync:(BOOL)async;
|
|
||||||
@end
|
|
|
@ -1,120 +0,0 @@
|
||||||
#import "NSObject+SPInvocationGrabbing.h"
|
|
||||||
#import <execinfo.h>
|
|
||||||
|
|
||||||
#pragma mark Invocation grabbing
|
|
||||||
@interface SPInvocationGrabber ()
|
|
||||||
@property (readwrite, retain, nonatomic) id object;
|
|
||||||
@property (readwrite, retain, nonatomic) NSInvocation *invocation;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation SPInvocationGrabber
|
|
||||||
- (id)initWithObject:(id)obj;
|
|
||||||
{
|
|
||||||
return [self initWithObject:obj stacktraceSaving:YES];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(id)initWithObject:(id)obj stacktraceSaving:(BOOL)saveStack;
|
|
||||||
{
|
|
||||||
self.object = obj;
|
|
||||||
|
|
||||||
if(saveStack)
|
|
||||||
[self saveBacktrace];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
-(void)dealloc;
|
|
||||||
{
|
|
||||||
free(frameStrings);
|
|
||||||
self.object = nil;
|
|
||||||
self.invocation = nil;
|
|
||||||
}
|
|
||||||
@synthesize invocation = _invocation, object = _object;
|
|
||||||
|
|
||||||
@synthesize backgroundAfterForward, onMainAfterForward, waitUntilDone;
|
|
||||||
- (void)runInBackground;
|
|
||||||
{
|
|
||||||
[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)
|
|
||||||
[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;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (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];
|
|
||||||
}
|
|
||||||
|
|
||||||
self.invocation = nil;
|
|
||||||
self.object = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)saveBacktrace;
|
|
||||||
{
|
|
||||||
void *backtraceFrames[128];
|
|
||||||
frameCount = backtrace(&backtraceFrames[0], 128);
|
|
||||||
frameStrings = backtrace_symbols(&backtraceFrames[0], frameCount);
|
|
||||||
}
|
|
||||||
-(void)printBacktrace;
|
|
||||||
{
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
-(id)invokeAfter:(NSTimeInterval)delta;
|
|
||||||
{
|
|
||||||
id grabber = [self grab];
|
|
||||||
[NSTimer scheduledTimerWithTimeInterval:delta target:grabber selector:@selector(invoke) userInfo:nil repeats:NO];
|
|
||||||
return grabber;
|
|
||||||
}
|
|
||||||
- (id)nextRunloop;
|
|
||||||
{
|
|
||||||
return [self invokeAfter:0];
|
|
||||||
}
|
|
||||||
-(id)inBackground;
|
|
||||||
{
|
|
||||||
SPInvocationGrabber *grabber = [self grab];
|
|
||||||
grabber.backgroundAfterForward = YES;
|
|
||||||
return grabber;
|
|
||||||
}
|
|
||||||
-(id)onMainAsync:(BOOL)async;
|
|
||||||
{
|
|
||||||
SPInvocationGrabber *grabber = [self grab];
|
|
||||||
grabber.onMainAfterForward = YES;
|
|
||||||
grabber.waitUntilDone = !async;
|
|
||||||
return grabber;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
48
ThirdParty/SPMediaKeyTap/SPMediaKeyTap.h
vendored
48
ThirdParty/SPMediaKeyTap/SPMediaKeyTap.h
vendored
|
@ -1,48 +0,0 @@
|
||||||
#include <Cocoa/Cocoa.h>
|
|
||||||
#import <IOKit/hidsystem/ev_keymap.h>
|
|
||||||
#import <Carbon/Carbon.h>
|
|
||||||
|
|
||||||
// http://overooped.com/post/2593597587/mediakeys
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
+ (NSArray*)defaultMediaKeyUserBundleIdentifiers;
|
|
||||||
|
|
||||||
-(id)initWithDelegate:(id)delegate;
|
|
||||||
|
|
||||||
+(BOOL)usesGlobalMediaKeyTap;
|
|
||||||
-(BOOL)startWatchingMediaKeys;
|
|
||||||
-(void)stopWatchingMediaKeys;
|
|
||||||
-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event;
|
|
||||||
|
|
||||||
@property NSArray *blackListBundleIdentifiers;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@interface NSObject (SPMediaKeyTapDelegate)
|
|
||||||
-(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event;
|
|
||||||
@end
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern NSString *kMediaKeyUsingBundleIdentifiersDefaultsKey;
|
|
||||||
extern NSString *kMediaKeyUsingBlackListBundleIdentifiersDefaultsKey;
|
|
||||||
extern NSString *kIgnoreMediaKeysDefaultsKey;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
390
ThirdParty/SPMediaKeyTap/SPMediaKeyTap.m
vendored
390
ThirdParty/SPMediaKeyTap/SPMediaKeyTap.m
vendored
|
@ -1,390 +0,0 @@
|
||||||
// Copyright (c) 2010 Spotify AB
|
|
||||||
#import "SPMediaKeyTap.h"
|
|
||||||
#import "NSObject+SPInvocationGrabbing.h" // https://gist.github.com/511181, in submodule
|
|
||||||
|
|
||||||
@interface SPMediaKeyTap ()
|
|
||||||
-(BOOL)shouldInterceptMediaKeyEvents;
|
|
||||||
-(void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting;
|
|
||||||
-(void)startWatchingAppSwitching;
|
|
||||||
-(void)stopWatchingAppSwitching;
|
|
||||||
-(void)eventTapThread;
|
|
||||||
@end
|
|
||||||
static SPMediaKeyTap *singleton = nil;
|
|
||||||
|
|
||||||
static pascal OSStatus appSwitched (EventHandlerCallRef nextHandler, EventRef evt, void* userData);
|
|
||||||
static pascal OSStatus appTerminated (EventHandlerCallRef nextHandler, EventRef evt, void* userData);
|
|
||||||
static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon);
|
|
||||||
|
|
||||||
|
|
||||||
// Inspired by http://gist.github.com/546311
|
|
||||||
|
|
||||||
@implementation SPMediaKeyTap
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Setup and teardown
|
|
||||||
-(id)initWithDelegate:(id)delegate;
|
|
||||||
{
|
|
||||||
_delegate = delegate;
|
|
||||||
[self startWatchingAppSwitching];
|
|
||||||
singleton = self;
|
|
||||||
_mediaKeyAppList = [NSMutableArray new];
|
|
||||||
_blackListBundleIdentifiers = [[NSUserDefaults standardUserDefaults]
|
|
||||||
arrayForKey:kMediaKeyUsingBlackListBundleIdentifiersDefaultsKey];
|
|
||||||
_tapThreadRL=nil;
|
|
||||||
_eventPort=nil;
|
|
||||||
_eventPortSource=nil;
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
-(void)dealloc;
|
|
||||||
{
|
|
||||||
[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);
|
|
||||||
}
|
|
||||||
-(void)stopWatchingAppSwitching;
|
|
||||||
{
|
|
||||||
if(!_app_switching_ref) return;
|
|
||||||
RemoveEventHandler(_app_switching_ref);
|
|
||||||
_app_switching_ref = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(BOOL)startWatchingMediaKeys;{
|
|
||||||
// Prevent having multiple mediaKeys threads
|
|
||||||
[self stopWatchingMediaKeys];
|
|
||||||
|
|
||||||
[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);
|
|
||||||
|
|
||||||
if (_eventPort != NULL){
|
|
||||||
_eventPortSource = CFMachPortCreateRunLoopSource(kCFAllocatorSystemDefault, _eventPort, 0);
|
|
||||||
|
|
||||||
if (_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];
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[self setShouldInterceptMediaKeyEvents:NO];
|
|
||||||
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-(void)stopWatchingMediaKeys;
|
|
||||||
{
|
|
||||||
// TODO<nevyn>: 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Accessors
|
|
||||||
|
|
||||||
+(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;
|
|
||||||
#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*/;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (NSArray*)defaultMediaKeyUserBundleIdentifiers;
|
|
||||||
{
|
|
||||||
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",
|
|
||||||
@"com.apple.systempreferences", // the tap messes up security dialogs
|
|
||||||
nil
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(BOOL)shouldInterceptMediaKeyEvents;
|
|
||||||
{
|
|
||||||
BOOL shouldIntercept = NO;
|
|
||||||
@synchronized(self) {
|
|
||||||
shouldIntercept = _shouldInterceptMediaKeyEvents;
|
|
||||||
}
|
|
||||||
return shouldIntercept;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)pauseTapOnTapThread:(BOOL)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, (__bridge CFRunLoopTimerRef)timer, kCFRunLoopCommonModes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark
|
|
||||||
#pragma mark -
|
|
||||||
#pragma mark Event tap callbacks
|
|
||||||
|
|
||||||
// Note: method called on background thread
|
|
||||||
|
|
||||||
static CGEventRef tapEventCallback2(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *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]
|
|
||||||
return event;
|
|
||||||
}
|
|
||||||
NSEvent *nsEvent = nil;
|
|
||||||
@try {
|
|
||||||
nsEvent = [NSEvent eventWithCGEvent:event];
|
|
||||||
}
|
|
||||||
@catch (NSException * e) {
|
|
||||||
NSLog(@"Strange CGEventType: %d: %@", type, e);
|
|
||||||
assert(0);
|
|
||||||
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;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// event will have been retained in the other thread
|
|
||||||
-(void)handleAndReleaseMediaKeyEvent:(NSEvent *)event {
|
|
||||||
[_delegate mediaKeyTap:self receivedMediaKeyEvent:event];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)eventTapThread;
|
|
||||||
{
|
|
||||||
@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];
|
|
||||||
|
|
||||||
Boolean same;
|
|
||||||
OSErr err = SameProcess(&mySerial, &topSerial, &same);
|
|
||||||
[self setShouldInterceptMediaKeyEvents:(err == noErr && same)];
|
|
||||||
|
|
||||||
}
|
|
||||||
- (void)appIsNowFrontmost:(ProcessSerialNumber)psn;
|
|
||||||
{
|
|
||||||
NSValue *psnv =
|
|
||||||
[NSValue valueWithBytes:&psn objCType:@encode(ProcessSerialNumber)];
|
|
||||||
|
|
||||||
NSDictionary *processInfo = CFBridgingRelease(ProcessInformationCopyDictionary(
|
|
||||||
&psn, kProcessDictionaryIncludeAllInformationMask));
|
|
||||||
NSString *bundleIdentifier =
|
|
||||||
[processInfo objectForKey:(id)kCFBundleIdentifierKey];
|
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
-(void)appTerminated:(ProcessSerialNumber)psn;
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
ProcessSerialNumber newSerial;
|
|
||||||
GetFrontProcess(&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;
|
|
||||||
|
|
||||||
GetEventParameter(
|
|
||||||
evt,
|
|
||||||
kEventParamProcessID,
|
|
||||||
typeProcessSerialNumber,
|
|
||||||
NULL,
|
|
||||||
sizeof(deadPSN),
|
|
||||||
NULL,
|
|
||||||
&deadPSN
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
[self appTerminated:deadPSN];
|
|
||||||
return CallNextEventHandler(nextHandler, evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
Loading…
Reference in a new issue