我正在尝试为辅助技术目的创建一个OS X键盘钩子(即不要担心,不是键盘记录器)。
当用户按下一个键时,我想要阻止真正的键按,而发送一个假的键按(我选择的字符)。
我有以下代码:
- (void) hookTheKeyboard {
CGEventMask keyboardMask = CGEventMaskBit(kCGEventKeyDown);
id eventHandler = [NSEvent addGlobalMonitorForEventsMatchingMask:keyboardMask handler:^(NSEvent *keyboardEvent) {
NSLog(@"keyDown: %c", [[keyboardEvent characters] characterAtIndex:0]);
//Want to: Stop the keyboard input
//Want to: Send another key input instead
}];
}
对完成这些目标有帮助吗?基本上是修改NSEvent "keyboardEvent"来发送一个不同的字符。谢谢。
您不能使用NSEvent
API执行此操作,但是您可以使用CGEventTap
执行此操作。您可以创建一个活动的事件tap并注册一个回调,接收CGEventRef
,并可以修改它(如果需要)并返回它来修改实际的事件流。
编辑
下面是一个简单的程序,在运行时,将每个"b"键替换为"v":
#import <Cocoa/Cocoa.h>
CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
//0x0b is the virtual keycode for "b"
//0x09 is the virtual keycode for "v"
if (CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode) == 0x0B) {
CGEventSetIntegerValueField(event, kCGKeyboardEventKeycode, 0x09);
}
return event;
}
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
CFRunLoopSourceRef runLoopSource;
CFMachPortRef eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, kCGEventMaskForAllEvents, myCGEventCallback, NULL);
if (!eventTap) {
NSLog(@"Couldn't create event tap!");
exit(1);
}
runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
CFRunLoopRun();
CFRelease(eventTap);
CFRelease(runLoopSource);
[pool release];
exit(0);
}
(有趣的故事:当我在编辑这篇文章时,我一直试图写"替换每一个'b'键",但它一直是"替换每一个'v'键"。)我很困惑。然后我想起来我还没有停止应用程序
我碰巧遇到了这个答案,需要做同样的事情,但只针对我自己的应用程序中的事件,而不是全局的。对于这个简单得多的问题,有一个简单得多的解决方案,我在这里指出,以防对其他人有用:
- 我在窗口拦截事件,通过创建一个覆盖sendEvent:。然后我检查键事件(KeyUp或KeyDown),然后简单地创建一个新的事件使用几乎所有的数据从以前的事件,然后调用NSWindow超类与此事件代替。
这似乎对我来说工作完美,我甚至不必修改keyCode部分-但也许这可能是一个问题…
在Swift中的示例:
class KeyInterceptorWindow : NSWindow {
override func sendEvent(theEvent: NSEvent) {
if theEvent.type == .KeyDown || theEvent.type == .KeyUp {
println(theEvent.description)
let newEvent = NSEvent.keyEventWithType(theEvent.type,
location: theEvent.locationInWindow,
modifierFlags: theEvent.modifierFlags,
timestamp: theEvent.timestamp,
windowNumber: theEvent.windowNumber,
context: theEvent.context,
characters: "H",
charactersIgnoringModifiers: theEvent.charactersIgnoringModifiers!,
isARepeat: theEvent.ARepeat,
keyCode: theEvent.keyCode)
super.sendEvent(newEvent!)
} else {
super.sendEvent(theEvent)
}
}
}
Swift 4+版本的james_alvarez的回答:
class KeyInterceptorWindow: NSWindow {
override func sendEvent(_ event: NSEvent) {
if [.keyDown, .keyUp].contains(event.type) {
let newEvent = NSEvent.keyEvent(with: event.type,
location: event.locationInWindow,
modifierFlags: event.modifierFlags,
timestamp: event.timestamp,
windowNumber: event.windowNumber,
context: nil,
characters: "H",
charactersIgnoringModifiers: event.charactersIgnoringModifiers ?? "",
isARepeat: event.isARepeat,
keyCode: event.keyCode)
if let newEvent = newEvent {
super.sendEvent(newEvent)
}
} else {
super.sendEvent(event)
}
}
}