【问题标题】:Modify NSEvent to send a different key than the one that was pressed修改 NSEvent 以发送与按下的键不同的键
【发布时间】:2011-08-12 17:45:21
【问题描述】:

我正在尝试创建一个用于辅助技术目的的 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" 以发送不同的字符。谢谢。

【问题讨论】:

    标签: objective-c macos keyboard-hook nsevent


    【解决方案1】:

    您无法使用NSEvent API 执行此操作,但您可以使用CGEventTap 执行此操作。您可以创建一个活动事件点击并注册a callback,该a callback 接收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' 键击”。我很困惑。然后我记得我还没有停止应用程序。)

    【讨论】:

    • Objective-C 的新手...帮助一些示例代码?和我上面的一样吗?
    • 以防万一:CGEvent api都是C。不需要objc。
    • 我必须在“系统偏好设置”>“辅助功能”中“启用辅助设备访问”才能使其正常工作。如果您使用管理员帐户登录,我认为这对于 CGEventTap 是不必要的。
    • 启用辅助设备访问后,您还需要重新启动 Xcode。
    • 还有什么可以注射的吗?就像不修改一个关键事件,而是添加一个?我正在尝试这样做。
    【解决方案2】:

    我碰巧遇到了这个答案,需要做同样的事情,但只针对我自己的应用程序中的事件 而不是全局的。对于这个更简单的问题,有一个更简单的解决方案,我在这里指出它对其他人有用:

    • 我通过为 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)
            }
    
        }
    
    }
    

    【讨论】:

      【解决方案3】:

      Swift 4+ 版本的james_alvarez's answer:

      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)
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2021-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-07
        • 1970-01-01
        • 1970-01-01
        • 2012-07-12
        • 1970-01-01
        相关资源
        最近更新 更多