如何监视全局修饰键状态(在任何应用程序中)?

发布于 2024-08-08 07:12:45 字数 3153 浏览 8 评论 0原文

我在 Cocoa 项目中使用一些 Carbon 代码来处理来自其他应用程序的全局关键事件(快捷方式)。目前,我已经设置了一个 kEventHotKeyReleased 事件处理程序,当我的应用程序不活动时,我可以成功获取热键。这会触发我的应用程序中的一些操作。

kEventHotKeyReleased 的行为存在的问题是:

假设我按下了 Cmd-Shift-P 组合键。一旦我释放“P”键,就会触发热键事件。 所有键均未按下(即:Cmd 和 Shift 键也被释放)时,我需要能够触发事件(或手动触发事件)。

监视热键很容易,但我没有看到任何监视单个击键的信息。如果我可以监控修饰键状态,我就可以开展业务了。

关于如何执行此操作有任何提示吗?

提前致谢!


更新:

我尝试过使用kEventRawKeyUpkEventRawKeyModifiersChanged,但是虽然kEventHotKeyReleased可以工作,但这两个不起作用,即使我以与 kEventHotKeyReleased 完全相同的方式设置它们。

EventTypeSpec eventTypes[] = {{kEventClassKeyboard, kEventHotKeyReleased}, {kEventClassKeyboard, kEventRawKeyUp}};
// Changing the order in the list does not help, nor does removing kEventHotKeyReleased
OSStatus err = InstallApplicationEventHandler(&globalHotkeyHandler, GetEventTypeCount(eventTypes), eventTypes, NULL, NULL);
// err == noErr after this line

globalHotKeyHandler 方法是为 kEventHotKeyReleased 调用的,但不是为 kEventRawKeyUp 调用的,出于某种我似乎无法理解的原因。我的 globalHotKeyHandler 方法如下所示:

OSStatus globalHotkeyHandler(EventHandlerCallRef nextHandler, EventRef anEvent, void *userData) {
    NSLog(@"Something happened!");
}

是否需要进行额外的调用或我忘记了其他内容?

注意:乍一看,辅助设备访问似乎可能被禁用,但事实并非如此。所以我很无知。


更新2:

我对Leibowitzn建议的CGEventTap进行了一些研究,并提出了这个设置:

CFMachPortRef keyUpEventTap = CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,kCGEventKeyUp,&keyUpCallback,NULL);
CFRunLoopSourceRef keyUpRunLoopSourceRef = CFMachPortCreateRunLoopSource(NULL, keyUpEventTap, 0);
CFRelease(keyUpEventTap);
CFRunLoopAddSource(CFRunLoopGetCurrent(), keyUpRunLoopSourceRef, kCFRunLoopDefaultMode);
CFRelease(keyUpRunLoopSourceRef);

...以及回调:

CGEventRef keyUpCallback (CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
    NSLog(@"KeyUp event tapped!");
    return event;
}

如你所见,我正在使用< code>kCGEventKeyUp 作为事件点击的掩码,但不知何故我收到了鼠标按下事件 ??!??


更新3:

好吧忘记了,我忽略了文档中说要使用 CGEventMaskBit(kCGEventKeyUp) 作为此参数的行,所以正确的调用是:

CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,CGEventMaskBit(kCGEventKeyUp),&keyUpCallback,NULL);

但我仍然遇到问题:修饰键不要触发 kCGEventKeyUp...


更新 4:

好吧,再次忘记这一点...我今天问完问题后 5 分钟一定会回答自己的问题,呵呵!

要拦截修饰键,请使用kCGEventFlagsChanged

CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,CGEventMaskBit(kCGEventFlagsChanged),&callbackFunction,NULL);

因此本质上我已经使按键和修饰键状态检测正常工作,但是我仍然有兴趣了解为什么 kEventRawKeyUp 不起作用...


注意:另请注意,我正在 Tiger 上进行开发,目标是尽可能支持新版和旧版操作系统。 CGEventTap 仅是 10.4+,所以我现在将使用它,但向后兼容的解决方案将受到欢迎。

I'm using some Carbon code in my Cocoa project for handling global key events (shortcuts) from other applications. Currently I have setup a kEventHotKeyReleased event handler and I can successfully obtain hot keys when my application is not active. That triggers some operation in my application.

The problem I have with the behavior of kEventHotKeyReleased is:

Say for example I press the Cmd-Shift-P key combination. As soon as I release the "P" key the hot key event is triggered. I need to be able to trigger the event (or manually trigger it) when all of the keys are unpressed (i.e: the Cmd and Shift keys are released too).

It is easy to monitor for hot keys but I have seen nothing for monitoring individual keystrokes. If I could monitor the modifier key states I would be in business.

Any hints on how to do this?

Thanks in advance!


UPDATE:

I've tried using kEventRawKeyUp and kEventRawKeyModifiersChanged but while kEventHotKeyReleased works those two don't even though I set them up in the exact same way as kEventHotKeyReleased.

EventTypeSpec eventTypes[] = {{kEventClassKeyboard, kEventHotKeyReleased}, {kEventClassKeyboard, kEventRawKeyUp}};
// Changing the order in the list does not help, nor does removing kEventHotKeyReleased
OSStatus err = InstallApplicationEventHandler(&globalHotkeyHandler, GetEventTypeCount(eventTypes), eventTypes, NULL, NULL);
// err == noErr after this line

The globalHotKeyHandler method is called for kEventHotKeyReleased, but not for kEventRawKeyUp for some reason I can't seem to grasp. Here's what my globalHotKeyHandler method looks like:

OSStatus globalHotkeyHandler(EventHandlerCallRef nextHandler, EventRef anEvent, void *userData) {
    NSLog(@"Something happened!");
}

Is there an additional call that needs to be made or something else I forgot?

N.B: At first glance, it seems like it could be that Access for Assistive Devices is disabled but it is not. So I'm pretty clueless.


UPDATE 2:

I investigated a bit on the CGEventTap Leibowitzn suggested and I came up with this setup:

CFMachPortRef keyUpEventTap = CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,kCGEventKeyUp,&keyUpCallback,NULL);
CFRunLoopSourceRef keyUpRunLoopSourceRef = CFMachPortCreateRunLoopSource(NULL, keyUpEventTap, 0);
CFRelease(keyUpEventTap);
CFRunLoopAddSource(CFRunLoopGetCurrent(), keyUpRunLoopSourceRef, kCFRunLoopDefaultMode);
CFRelease(keyUpRunLoopSourceRef);

... and the callback:

CGEventRef keyUpCallback (CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
    NSLog(@"KeyUp event tapped!");
    return event;
}

As you can see I'm using kCGEventKeyUp as the mask for the event tap but somehow I'm receiving mouse down events ??!??


UPDATE 3:

Ok forget that, I overlooked the line in the doc that said to use CGEventMaskBit(kCGEventKeyUp) for this parameter, so the correct call is:

CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,CGEventMaskBit(kCGEventKeyUp),&keyUpCallback,NULL);

I'm still having a problem though: modifier keys do not trigger the kCGEventKeyUp...


UPDATE 4:

Ok forget that again... I'm bound to answer to my own questions 5 minutes after asking them today huh!

To intercept modifier keys, use kCGEventFlagsChanged:

CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,CGEventMaskBit(kCGEventFlagsChanged),&callbackFunction,NULL);

So in essence I got the key and modifier key state detection working, but I'm still interested in knowing why kEventRawKeyUp doesn't work...


N.B: Also note that I'm developing on Tiger with the goal of having support for new and older versions of the OS as much as possible. CGEventTap is 10.4+ only so I'll be using this for now but a backwards-compatible solution would be welcome.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

匿名。 2024-08-15 07:12:45

一种选择是使用 EventTaps。这使您可以监视所有键盘事件。看:
http://developer.apple.com/mac/library/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html#//apple_ref/c/func/CGEventTapCreate

不幸的是,如果应用程序出现事件点击将停止工作正在请求安全输入。例如加快。

One option is to use EventTaps. This lets you monitor all keyboard events. See:
http://developer.apple.com/mac/library/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html#//apple_ref/c/func/CGEventTapCreate

Unfortunately event taps will stop working if an application is requesting secure input. For example Quicken.

冷血 2024-08-15 07:12:45
OSStatus err = InstallApplicationEventHandler(&globalHotkeyHandler, GetEventTypeCount(eventTypes), eventTypes, NULL, NULL);

这不是全球性的。仅当您自己的应用程序处于活动状态时,并且(我相信)在 Carbon 事件管理器自己的事件过滤器之后,才会安装处理程序。

您需要使用InstallEventHandler,它将事件目标作为其第一个参数(InstallApplicationEventHandler 是传递应用程序事件目标的宏)。

对于应用程序未激活时发生的事件,您需要的目标是 GetEventMonitorTarget()。对于在应用程序处于活动状态时发生的事件,您需要的目标是 GetEventDispatcherTarget()。要捕获事件,无论哪个应用程序处于活动状态,请在两个目标上安装处理程序。

不过,现在我只使用 CGEventTaps,正如 Leibowitzn 所建议的那样。

OSStatus err = InstallApplicationEventHandler(&globalHotkeyHandler, GetEventTypeCount(eventTypes), eventTypes, NULL, NULL);

This is not global. This installs the handler only when your own application is active, and (I believe) after the Carbon Event Manager's own event filters.

You need to use InstallEventHandler, which takes an event target as its first parameter (InstallApplicationEventHandler is a macro that passes the application event target).

For events that occur while your application is not active, the target you want is GetEventMonitorTarget(). For events that occur while your application is active, the target you want is GetEventDispatcherTarget(). To catch events no matter what application is active, install your handler on both targets.

Nowadays, though, I'd just use CGEventTaps, as Leibowitzn suggested.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文