Mac事件点击只会延迟丢弃的事件

时间:2010-05-12 09:57:41

标签: c++ macos events quartz-graphics macos-carbon

我正在尝试编写一些在Mac OSX 10.6上启用时丢弃所有键盘和鼠标事件的代码。我的代码以root用户身份运行。我正在采取的方法是创建一个事件点击,丢弃传递给它的所有事件(启用时)。事件点击回调函数如下所示:

CGEventRef MyTapCallback(CGEventTapProxy proxy,
                         CGEventType type,
                         CGEventRef event,
                         void *refcon)
{
    return CKeyLocker::isEnabled() ? NULL : event;
}

我用来启用和禁用事件抽头的代码如下所示:

void CKeyLocker::enable(bool bEnable)
{
    if (bEnable == m_bEnabled)
        return;

    if (bEnable)
    {
        // which events are we interested in?
        CGEventMask evMask = kCGEventMaskForAllEvents;
        CFMachPortRef mp = CGEventTapCreate(kCGHIDEventTap,
                                            kCGHeadInsertEventTap,
                                            kCGEventTapOptionDefault,
                                            evMask,
                                            MyTapCallback,
                                            NULL);

        if (mp)
        {
            qDebug() << "Tap created and active. mp =" << mp;
            m_enabledTap = mp;
            m_bEnabled = true;
        }
    }
    else
    {
        CGEventTapEnable(m_enabledTap, false);
        CFRelease(m_enabledTap);
        m_enabledTap =0;
        m_bEnabled = false;
        qDebug() << "Tap destroyed and inactive";
    }
}

当事件点击处于活动状态时,此方法非常有效 - 我可以根据需要敲击键盘和鼠标,并且没有任何事件通过系统。但是,当点击被禁用时,我在点击激活时按下的所有键都出现在当前窗口中。这就像事件点击只是推迟事件,而不是摧毁它们,这很奇怪,因为Mac文档明确指出:

  

如果事件点击是活动过滤器,则回调函数应返回以下之一:

     

传入的(可能已修改的)事件。此事件将传递回事件系统。

     

新建的活动。将新事件传递回事件系统后,新事件将与原始事件一起发布。

     

如果要删除传入的事件,则为NULL。

我正在返回NULL,但事件似乎没有被删除。有什么想法吗?

3 个答案:

答案 0 :(得分:1)

链接的评论没有我所看到的答案,所以我会从我看到的东西中转出一些信息。

首先,我对CGEventTapCreateForPSN好运。这就好像系统为你提供了一些限制你的水龙头的余地。但是,从这个例子来看,这似乎还不够。

接下来 - 这个/可能/只需要你...在你的回电中,你可能想要(也可能需要)检查以下事件:

switch (type)
{
    case kCGEventTapDisabledByTimeout:
    case kCGEventTapDisabledByUserInput:
    {
        CFMachPortRef *pTap = (CFMachPortRef*)refcon;
        CGEventTapEnable( *pTap, true );
        return NULL;
    }
    default:
        break;
}

无论各种文档是做什么或不做什么,我都观察到操作系统感觉它正在“探测”糟糕的回调;基本上禁用事件点击回调是普遍吃事件。如果你在这些情况下重新注册,操作系统似乎没问题,好像说:好吧,你似乎知道你在做什么,但我可能会再次捅你一点确认。

答案 1 :(得分:1)

这很奇怪,我们使用事件抽头来实现相同的目的(在给定场景中输入阻塞)并且完美地工作10.4 - 10.8.2。一件事,它不应该阻止或从密码对话框接收事件(这不是一个大惊喜)

我现在看到的与你的样本相比有所不同:

  • 我们使用kCGTailAppendEventTap而不是kCGHeadInsertEventTap(这应该不重要)
  • 我们在已安装的回调中执行一些事件记录
  • 我们在一些自注入事件中有一些用户事件数据,这些数据被过滤掉,但除此之外我们只返回NULL以丢弃不需要的事件(就像你一样),我可以确认,并非所有事件都是可以忽略的!
  • 我们以这种方式打开/关闭事件:
bool SetInputFilter(bool bOn)
{
    bool result = false;
    CFRunLoopRef runLoopRef = CFRunLoopGetMain();

    if (bOn) {
        // Create an event tap.
        CGEventMask eventMask = kCGEventMaskForAllEvents;
        if ((m_eventTapInput = CGEventTapCreate(kCGHIDEventTap, 
                                              kCGTailAppendEventTap, 
                                              kCGEventTapOptionDefault,
                                              eventMask, CGInputEventCallback, this)) == NULL) {
            Log(L"Failed to create event tap");
            return result;
        }

        // Create a run loop source.
        m_runLoopEventTapSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, m_eventTapInput, 0);
        CFRelease(m_eventTapInput);   // CFMachPortCreateRunLoopSource retains m_eventTapInput
        if (m_runLoopEventTapSource == NULL) {
            Log(L"Failed to create run loop source for event tap");
            return result;
        }

        // Add to the current run loop.
        CFRunLoopAddSource(runLoopRef, m_runLoopEventTapSource, kCFRunLoopCommonModes);//kCFRunLoopDefaultMode);
        CFRelease(m_runLoopEventTapSource);   // CFRunLoopAddSource retains m_runLoopEventTapSource
        result = true;
    }
    else {
        // Disable the event tap.
        if (m_eventTapInput)
            CGEventTapEnable(m_eventTapInput, false);

        // Remove our run loop source from the current run loop.
        if (runLoopRef && m_runLoopEventTapSource) {
            CFRunLoopRemoveSource(runLoopRef, m_runLoopEventTapSource, kCFRunLoopCommonModes);//kCFRunLoopDefaultMode);
            m_runLoopEventTapSource = NULL;   // removing m_runLoopEventTapSource releases last reference of m_runLoopEventTapSource too
            m_eventTapInput = NULL;           // removing m_runLoopEventTapSource releases last reference of m_eventTapInput too
        }
    }
    return result;
}

答案 2 :(得分:0)

我可以验证返回NULL确实有效地删除了一些事件,但是我也看到过它没有的时间,确切地说它是如何决定允许的删除不清楚但看起来像是大规模删除似乎被阻止了,例如:当你连续删除超过100个事件。