Нажатие на событие Mac просто задерживает отклоненные события - PullRequest
18 голосов
/ 12 мая 2010

Я пытаюсь написать код, который отбрасывает все события клавиатуры и мыши при включении в 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 ]

1 голос
/ 26 марта 2012

Связанный комментарий не имеет ответа на то, что я вижу, поэтому я дам немного информации из того, что я видел, когда возился с этим материалом.

Во-первых, мне повезло больше сCGEventTapCreateForPSN.Как будто система дает вам некоторую свободу действий для ограничения вашего крана.Однако из этого примера выглядит, что этого недостаточно.

Далее - и это / может / все, что вам нужно ... При обратном вызове вы, вероятно, хотите (и, возможно, должны) проверить наличиеследующие события:

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

Независимо от того, что говорит или не говорит различная документация, я наблюдал, что ОС чувствует, что она "проверяет" плохие обратные вызовы;в основном отключение обратных вызовов к событию, которые являются универсальными событиями.Если в этих случаях вы перерегистрируете систему, то, похоже, операционная система будет в порядке, как если бы она говорила: «ОК, вы, похоже, знаете, что делаете, но я, вероятно, еще раз подтолкну вас, чтобы убедиться».

1 голос
/ 26 октября 2012

Это действительно странно, мы используем отводы событий для той же цели (блокировка ввода в данном сценарии) и отлично работает 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;
}
0 голосов
/ 29 февраля 2012

Я могу убедиться, что возвращение NULL действительно удаляет некоторые события, но я также видел случаи, когда это не так, точно, как он решает, какие разрешения разрешить, неясно, но похоже, что массовые удаления, как представляется, предотвращаются, например: когда вы удалить более 100 событий или около того подряд.

...