使用事件捕获技术消费OSX鼠标/触摸板事件

6
我正在尝试添加一个事件陷阱来启用/禁用魔术触控板上的事件。我认为这应该很简单,即注册一个事件陷阱,当需要时通过返回NULL来丢弃该事件。我的想法是将触控板用于一些特定的、耗时的数据输入,要输入数据的应用程序是第三方的,所以我不能只是添加代码来实现我想要的功能。因此,我想监视系统事件,然后通过一系列的CGEventCreateKeyboardEvent发送所需的输入。
问题在于,返回null似乎不能丢弃事件,进一步调查表明,这不仅限于来自触控板的事件,还包括我的默认USB鼠标。
下面是我的代码。使用下面的代码,我希望无法移动鼠标,如果我将(A)更改为使用kCGEventScrollWheelkCGEventLeftMouseDragged,则该事件被消耗,即滚动或左键拖动不会发生。这是否意味着并非所有事件都可以被丢弃?希望我只是漏了什么显而易见的东西。
  #define case_print(a) case a: printf("%s - %d\n",#a,a); break;


  CGEventRef eventOccurred(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon) {
    int subType =  CGEventGetIntegerValueField(event, kCGMouseEventSubtype);
    if (type == NSEventTypeGesture || subType == NX_SUBTYPE_MOUSE_TOUCH) {
        printf("touchpad\n");

        switch(type) {
                case_print(kCGEventNull)
                case_print(kCGEventLeftMouseDown)
                case_print(kCGEventLeftMouseUp)
                case_print(kCGEventRightMouseDown)
                case_print(kCGEventRightMouseUp)
                case_print(kCGEventMouseMoved)
                case_print(kCGEventLeftMouseDragged)
                case_print(kCGEventRightMouseDragged)
                case_print(kCGEventScrollWheel)
                case_print(kCGEventOtherMouseDown)
                case_print(kCGEventOtherMouseUp)
                case_print(kCGEventOtherMouseDragged)
                case_print(kCGEventTapDisabledByTimeout)
                case_print(kCGEventTapDisabledByUserInput)
                case_print(NSEventTypeGesture)
                case_print(NSEventTypeMagnify)
                case_print(NSEventTypeSwipe)
                case_print(NSEventTypeRotate)
                case_print(NSEventTypeBeginGesture)
                case_print(NSEventTypeEndGesture)
            default:
                printf("default: %d\n",type);
                break;    
        }

        event = NULL;
    }  else {
        if (type == kCGEventMouseMoved) {  // (A)
            printf("discarding mouse event");
            event = NULL;
        }
    }

    return event;
}


CFMachPortRef createEventTap() {  
    CGEventMask eventMask = NSAnyEventMask;

    if (!AXAPIEnabled() && !AXIsProcessTrusted()) { 
        printf("axapi not enabled");
    } 

    return CGEventTapCreate(kCGHIDEventTap, 
                            kCGHeadInsertEventTap, 
                            kCGEventTapOptionDefault, 
                            eventMask, 
                            eventOccurred, 
                            NULL); 
}

int main (int argc, const char * argv[]) {
    CFMachPortRef tap = createEventTap();

    if (tap) {
        CFRunLoopSourceRef rl = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tap, 0);
        CFRunLoopAddSource(CFRunLoopGetMain(), rl, kCFRunLoopCommonModes);
        CGEventTapEnable(tap, true);
        CFRunLoopRun();

        printf("Tap created.\n");
        sleep(-1);
    } else {
        printf("failed!\n");
    }

    return 0;
}

请注意,“axapi未启用”不会输出,尽管我认为无障碍选项不会影响除键盘事件以外的任何内容。
顺便说一下,我看到了一些类似的帖子,介绍如何从触摸板获取事件,但没有适用于丢弃它们的(除了返回null应该可以起作用)。
4个回答

4
我认为目前不可能简单地放弃这些事件。从CGEventTypes.h头文件中可以看到:
“传递给回调的事件由调用代码保留,在回调返回并数据被传回事件系统后释放。如果回调函数返回不同的事件,则该事件将与原始事件一起被调用代码释放,在事件数据被传回事件系统后。”
我已经试过了一些操作,似乎在回调返回后,窗口服务器会主动检查你对事件所做的操作。它只允许删除键盘事件和鼠标上/下事件,但就像忽略鼠标移动事件的删除一样(好吧,鼠标渲染在其他地方处理,我猜),它似乎忽略了鼠标手势的删除(即你的回调返回null)。
考虑到手势敲击在文档中没有特别解释(在此级别未定义类型),这一点也不奇怪。
我尝试返回另一个事件(按键),然后这个事件除了原始手势之外还被处理。在回调中释放事件也不行(当然),只会导致异常。
我唯一没有尝试的事情是直接操纵传递的CGEvent的内部数据,以至少使手势无效(删除所有移动等),但这有点困难,因为没有特定的方法来定义它。我很确定所需的信息在通过CGEventSet*方法访问的各个字段中的某个地方。
我查看了IOLLEvent.h以了解其数据结构,但这太丑陋了,不适合进一步深入研究。希望Lion在CF级别上提供更多关于手势类型事件的信息。

1
那将是 CGEventSetIntegerValueField(event, kCGMouseEventDeltaX, 0); 和 CGEventSetIntegerValueField(event, kCGMouseEventDeltaY, 0); 但也被忽略了。 - valexa

3
我可以证实,在10.6版本中,以下两种方法均不能成功实现此目的:
1- 返回 NULL
2- 返回一个新的鼠标事件,其光标位置与之前相同
3- 返回传递的事件,并将其kCGMouseEventDelta更改为0
我无法确定10.7版本是否有所改善,但说实话,我不抱太大希望。
然而,有一件事情可以做,而不是放弃移动,可以使用CGWarpMouseCursorPosition将光标移回到先前的位置,这样光标就会停止移动,只有第一个位置会稍微改变。

我的经验也是一样的...不能返回null或修改增量来防止鼠标移动。我将光标移回去,但光标仍然在原来的位置闪烁。希望在这里防止这种情况发生:https://dev59.com/vWLVa4cB1Zd3GeqPsggs - ck_

2

太好了。不过链接已经失效了。 - jakob.j

1

如果你的tap是被动的,返回NULL将不会影响事件流。从CGEventTapCallBack参考文献中可知:

"如果事件tap是一个被动监听器,你的回调函数可以返回传入的事件或NULL。在任一情况下,事件流都不受影响。"

然而,看起来你的tap是主动的。因此,返回NULL应该删除事件。你是否考虑修改事件以使其无效?

还请注意,调用CGEventTapCreate需要root用户权限来拦截所有事件。你的进程是否正在以root身份运行?


3
为什么这被标记为答案?你能够阻止鼠标事件的处理吗?我可以停止键盘事件,但无法停止鼠标移动... - ck_

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接