这个问题来自 How to hook/remap an arbitrary keyboard event on OSX?
到目前为止,我可以使用以下方法点击修饰键和大多数其他键:
_eventTap = CGEventTapCreate( kCGHIDEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionDefault,
CGEventMaskBit( kCGEventKeyDown )
| CGEventMaskBit( kCGEventFlagsChanged )
,
(CGEventTapCallBack)_tapCallback,
(__bridge void *)(self));
值得注意的是,
F3
在采取行动之前正确报告了一个键码 (160)。即我可以通过让我的事件处理程序返回 NULL 来禁用该操作(因此无法传播事件)。但是,F7 到 F12 和 Eject/Power 不会触发回调。
如果我添加:
| CGEventMaskBit( NSSystemDefined )
...现在剩余的 Fx 键确实会触发回调(尽管 Power/Eject 仍然不会),但我无法访问事件的 keyCode 方法。
它产生一个错误:
所以要么:
(1) 我需要一些其他方式从 NSEvent 中提取一些唯一标识符,或者
(2) 我需要在较低的级别点击/ Hook 。
使用 (1),我注意到 NSEvent 有一个 data1 属性。记录这是在十六进制给出:
2015-05-21 12:40:05.428 tap_k[16576:704298] NSSystemDefined: 140b00
2015-05-21 12:40:06.914 tap_k[16576:704298] NSSystemDefined: 100a00
2015-05-21 12:40:06.992 tap_k[16576:704298] NSSystemDefined: 100b00
2015-05-21 12:40:07.600 tap_k[16576:704298] NSSystemDefined: 130a00
2015-05-21 12:40:07.690 tap_k[16576:704298] NSSystemDefined: 130b00
2015-05-21 12:40:08.219 tap_k[16576:704298] NSSystemDefined: 70a00
2015-05-21 12:40:08.277 tap_k[16576:704298] NSSystemDefined: 70b00
2015-05-21 12:40:09.062 tap_k[16576:704298] NSSystemDefined: 10a00
2015-05-21 12:40:09.186 tap_k[16576:704298] NSSystemDefined: 10b00
2015-05-21 12:40:09.637 tap_k[16576:704298] NSSystemDefined: a00
2015-05-21 12:40:09.726 tap_k[16576:704298] NSSystemDefined: b00
.. 当我按下/按下 F6 F7 F8 F9 F10 F11 F12 时。
(此外,对于重复,最后一个值更改为 1)。
所以我想我可以使用这些值来处理事件,并通过其他 NSSystemDefined 事件。
它仍然没有解决捕获 Eject/Power 的问题。
但是有更清洁/更好的方法吗?
如果有人有兴趣玩,这里是完整的代码:
// compile and run from the commandline with:
// clang -fobjc-arc -framework Cocoa ./foo.m -o foo
// sudo ./foo
#import <Foundation/Foundation.h>
#import <AppKit/NSEvent.h>
typedef CFMachPortRef EventTap;
// - - - - - - - - - - - - - - - - - - - - -
@interface KeyChanger : NSObject
{
@private
EventTap _eventTap;
CFRunLoopSourceRef _runLoopSource;
CGEventRef _lastEvent;
}
@end
// - - - - - - - - - - - - - - - - - - - - -
CGEventRef _tapCallback(
CGEventTapProxy proxy,
CGEventType type,
CGEventRef event,
KeyChanger* listener
);
// - - - - - - - - - - - - - - - - - - - - -
@implementation KeyChanger
- (BOOL)tapEvents
{
if (!_eventTap) {
NSLog(@"Initializing an event tap.");
// kCGHeadInsertEventTap -- new event tap should be inserted before any pre-existing event taps at the same location,
_eventTap = CGEventTapCreate( kCGHIDEventTap, // kCGSessionEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionDefault,
CGEventMaskBit( kCGEventKeyDown )
| CGEventMaskBit( kCGEventFlagsChanged )
| CGEventMaskBit( NSSystemDefined )
,
(CGEventTapCallBack)_tapCallback,
(__bridge void *)(self));
if (!_eventTap) {
NSLog(@"unable to create event tap. must run as root or "
"add privlidges for assistive devices to this app.");
return NO;
}
}
CGEventTapEnable(_eventTap, TRUE);
return [self isTapActive];
}
- (BOOL)isTapActive
{
return CGEventTapIsEnabled(_eventTap);
}
- (void)listen
{
if( ! _runLoopSource ) {
if( _eventTap ) { //dont use [self tapActive]
_runLoopSource = CFMachPortCreateRunLoopSource( kCFAllocatorDefault,
_eventTap, 0);
// Add to the current run loop.
CFRunLoopAddSource( CFRunLoopGetCurrent(), _runLoopSource,
kCFRunLoopCommonModes);
NSLog(@"Registering event tap as run loop source.");
CFRunLoopRun();
}else{
NSLog(@"No Event tap in place! You will need to call "
"listen after tapEvents to get events.");
}
}
}
- (CGEventRef)processEvent:(CGEventRef)cgEvent
{
NSEvent* event = [NSEvent eventWithCGEvent:cgEvent];
NSUInteger modifiers = [event modifierFlags] &
(NSCommandKeyMask | NSAlternateKeyMask | NSShiftKeyMask | NSControlKeyMask);
enum {
kVK_ANSI_3 = 0x14,
};
switch( event.type ) {
case NSFlagsChanged:
NSLog(@"NSFlagsChanged: %d", event.keyCode);
break;
case NSSystemDefined:
NSLog(@"NSSystemDefined: %x", event.data1);
return NULL;
case kCGEventKeyDown:
NSLog(@"KeyDown: %d", event.keyCode);
break;
default:
NSLog(@"WTF");
}
// TODO: add other cases and do proper handling of case
if (
//[event.characters caseInsensitiveCompare:@"3"] == NSOrderedSame
event.keyCode == kVK_ANSI_3
&& modifiers == NSShiftKeyMask
)
{
NSLog(@"Got SHIFT+3");
event = [NSEvent keyEventWithType: event.type
location: NSZeroPoint
modifierFlags: event.modifierFlags & ! NSShiftKeyMask
timestamp: event.timestamp
windowNumber: event.windowNumber
context: event.context
characters: @"#"
charactersIgnoringModifiers: @"#"
isARepeat: event.isARepeat
keyCode: event.keyCode];
}
_lastEvent = [event CGEvent];
CFRetain(_lastEvent); // must retain the event. will be released by the system
return _lastEvent;
}
- (void)dealloc
{
if( _runLoopSource ) {
CFRunLoopRemoveSource( CFRunLoopGetCurrent(), _runLoopSource, kCFRunLoopCommonModes );
CFRelease( _runLoopSource );
}
if( _eventTap ) {
//kill the event tap
CGEventTapEnable( _eventTap, FALSE );
CFRelease( _eventTap );
}
}
@end
// - - - - - - - - - - - - - - - - - - - - -
CGEventRef _tapCallback(
CGEventTapProxy proxy,
CGEventType type,
CGEventRef event,
KeyChanger* listener
)
{
//Do not make the NSEvent here.
//NSEvent will throw an exception if we try to make an event from the tap timout type
@autoreleasepool {
if( type == kCGEventTapDisabledByTimeout ) {
NSLog(@"event tap has timed out, re-enabling tap");
[listener tapEvents];
return nil;
}
if( type != kCGEventTapDisabledByUserInput ) {
return [listener processEvent:event];
}
}
return event;
}
// - - - - - - - - - - - - - - - - - - - - -
int main(int argc, const char * argv[])
{
@autoreleasepool {
KeyChanger* keyChanger = [KeyChanger new];
[keyChanger tapEvents];
[keyChanger listen];//blocking call.
}
return 0;
}
最佳答案
我只想指出,如果您还没有,您应该查看 MASShortcut 以查看它如何与 F7-F12 键 Hook 。也就是说,我不相信它可以识别 Power + Eject 键。