无法使用 CGEventTap 阻止大写锁定
-
05-10-2019 - |
题
我正在使用 Quartz CGEventTap 尝试全局拦截大写锁定按键并阻止它们(让它们做一些有用的事情)。我成功检测到大写锁定按键,但迄今为止无法阻止它们。我的代码(源自 这 stackoverflow 的答案)是这样的:
eventTap = CGEventTapCreate(kCGHIDEventTap,
kCGTailAppendEventTap,
kCGEventTapOptionDefault,
eventMask,
myCGEventCallback,
&oldFlags);
runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef theEvent, void *refcon)
{
CGEventFlags *oldFlags = (CGEventFlags *)refcon;
switch (type)
{
case kCGEventFlagsChanged:
{
CGEventFlags newFlags = CGEventGetFlags(theEvent);
CGEventFlags changedFlags = *oldFlags ^ newFlags;
*oldFlags = newFlags;
if (changedFlags == 65536)
{
NSLog(@"Capslock pressed. Let's not return the event");
return NULL;
}
break;
}
default:
break;
}
NSLog(@"Different modifier than capslock. Returning the event");
return theEvent;
}
如果我理解正确,返回 NULL 应该有效地阻止按键传播。事实上,它也适用于“正常”的 keyup 和 -down 事件。然而,大写锁定无论如何都会切换。有什么想法吗?我做出了错误的假设吗?和/或我怎样才能以不同的方式做事来实现我的目标?
谢谢,
雷神
解决方案
你不能像那样阻止大写锁定。大写锁定是一种情况,由键盘驱动程序处理,而不是由 Window Server 或单个应用程序处理。按键事件在 OS X 中传播如下:
键盘驱动程序 -> Window 服务器 -> 用户会话 -> 活动应用程序
在每个级别传播(由“->”表示),您可以放置事件点击并阻止和/或修改关键事件。Keyboard Driver 和 Window Server 都是特权组件,另外两个是普通用户级组件。
最早可以捕获键盘事件是当事件从驱动程序(内核空间)传播到 Window 服务器(用户空间,但仍然具有特权)时。然而,如上所述,大写锁定状态是在驱动程序内部处理的,因此捕获事件已经为时已晚。驱动程序将已经启用键盘上的大写锁定指示灯(如果有必要,并且不由硬件自行执行)并记住大写锁定状态处于打开状态,这会对将来的所有按键产生影响。
您可以通过编程方式关闭 HID 设备的大写锁定灯(Apple 甚至有 示例代码),但这不会关闭大写锁定,它只是关闭灯。
我认为使用事件点击来实现的唯一方法是手动将每个带有大写锁定的按键重写为不带大写锁定的按键(未设置大写锁定标志且附加字母(如果有)为小写)。此外,为了不让用户感到困惑,您可以关闭大写锁定灯,如 Apple 的示例代码所示。
其他替代方案是:编写你自己的键盘驱动程序,控制键盘而不是苹果的标准驱动程序(没有你想象的那么难,但仍然足够难)或侵入驱动程序(邪恶,但有效)。例如。一些键盘重新映射器仅覆盖驱动程序的单个方法(驱动程序在 OS X 中是 C++,因此它们是 OO 对象,您可以在运行时动态覆盖 C++ 方法;并不是真正覆盖它们,而是操纵方法表)。这两种解决方案的问题是:第一个意味着,除非您的驱动程序与 Apple 的驱动程序一样好,否则某些 USB 键盘可能无法与您的驱动程序配合使用,而另一些则可以,第二个意味着如果您犯了任何错误,系统会通过内核恐慌来惩罚此错误。
我正在寻找一种在 Mac 上以编程方式切换大写锁定的方法,但到目前为止还没有成功。但我可以向你保证,事件点击并不是正确的选择。