【问题标题】:How to capture process-space key events in OSX command-line tool, CoreFoundation, C API?如何在 OSX 命令行工具、CoreFoundation、C API 中捕获进程空间关键事件?
【发布时间】:2016-07-29 13:12:58
【问题描述】:

我正在研究如何在 C,仅使用CoreFoundation,(没有Cocoa,没有NSEvents)。经过一些研究,我可以轻松地将这个 MCVE 放在“全局级别”(这需要以 root 权限运行程序),但无法弄清楚如何捕获和处理仅限程序的进程空间。我也没有找到关于如何做到这一点的文档。我在 SO 上发现了这个领域的一些问题,但所有这些问题都是基于 Cocoa API 的。我很乐意提供所需的任何其他信息。

// gcc -Wall -o test test.c -framework ApplicationServices
// sudo test

#include <ApplicationServices/ApplicationServices.h>

CGEventRef testEventCallback(CGEventTapProxy proxy, 
                             CGEventType type, 
                             CGEventRef event, 
                             void *refcon)
{
    printf( " Event Type: %d\n", type );
    return event;
}


int main(int argc, char *argv[])
{
    CFMachPortRef       eventPort;
    CFRunLoopSourceRef  eventSrc;
    CFRunLoopRef        runLoop;
    CGEventMask mask = CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(kCGEventKeyUp);
    eventPort = CGEventTapCreate(kCGSessionEventTap,
                                kCGHeadInsertEventTap,
                                kCGEventTapOptionListenOnly,
                                mask,
                                testEventCallback,
                                NULL );
    if ( eventPort == NULL ){
        printf( "NULL eventPort\n" );
        return 1;
    }

    eventSrc = CFMachPortCreateRunLoopSource(NULL, eventPort, 0);
    if ( eventSrc == NULL ){
        printf( "NULL eventSrc\n" );
        return 1;
    }

    runLoop = CFRunLoopGetCurrent();
    if ( runLoop == NULL ){
        printf( "NULL runLoop\n" );
        return 1;
    }

    CFRunLoopAddSource(runLoop,  eventSrc, kCFRunLoopDefaultMode);
    CFRunLoopRun();
    return 0;
}

【问题讨论】:

  • “程序的进程空间”是什么意思?
  • 感谢@Willeke 的提问:我的意思是“不是系统范围的”,但仅适用于特定程序。我不需要监视用户放入其他打开程序的关键事件,而只需要监视这个特定的命令行工具。希望我现在更清楚一点。
  • CGEventTapCreate 的文档链接到事件点击位置。在底部它说“除了上面描述的三个点击点之外,还可以放置一个事件点击,将带注释的事件传递给特定的应用程序。有关更多信息,请参阅函数 CGEventTapCreateForPSN。”。
  • 这是非常有价值的信息,@Willeke。我设法通过调用GetFrontProcess() 来修改获取PSN 的代码。但是我不明白为什么它仍然需要 超级用户权限 才能运行 CGEventTapCreateForPSNsudo test…我一定遗漏了一些非常明显的东西。
  • 命令行工具从来没有这样的键盘焦点。如果您从终端运行它,那么它是具有键盘焦点的终端。在命令行工具的进程空间中不存在关键事件。

标签: c macos core-foundation


【解决方案1】:

这里有一些代码可以在终端中运行的 Swift 命令行工具中运行。它点击通常会路由到 Terminal.app 的事件,或者当工具开始运行时最前面的任何进程。它避免了使用命令键,因此您可以在需要时使用命令周期轻松终止程序。由于GetFrontProcess() 现在已弃用,它使用NSWorkspace 来定位前端进程。

let lock = DispatchQueue(label: "lock")
var events = [CGEvent]()

// track keyboard events using an event tap.
let callback: CGEventTapCallBack =  { (tapProxy, eventType, event, _) -> Unmanaged<CGEvent>? in
  // don't consume command keys.
  if !event.flags.contains(.maskCommand) {
    if eventType == .keyDown {
      lock.sync {
        events.append(event)
      }
    }
    return nil
  }
  return Unmanaged<CGEvent>.passUnretained(event)
}

let eventMask = CGEventMask((1 << CGEventType.keyDown.rawValue) | (1 << CGEventType.keyUp.rawValue))
if let frontmostProcess = NSWorkspace.shared.frontmostApplication?.processIdentifier, let eventTap = CGEvent.tapCreateForPid(pid: frontmostProcess, place: .headInsertEventTap, options: .defaultTap, eventsOfInterest: eventMask, callback: callback, userInfo: nil) {
  let source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0)
  CFRunLoopAddSource(CFRunLoopGetCurrent(), source, .commonModes)
  CGEvent.tapEnable(tap: eventTap, enable: true)
  CFRunLoopRun()
}

【讨论】:

    猜你喜欢
    • 2015-02-07
    • 2013-08-15
    • 1970-01-01
    • 2017-01-21
    • 2020-08-31
    • 1970-01-01
    • 1970-01-01
    • 2011-02-05
    • 1970-01-01
    相关资源
    最近更新 更多