Нажатие на событие Mac просто задерживает отклоненные события

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

Это действительно странно, мы используем отводы событий для той же цели (блокировка ввода в данном сценарии) и отлично работает 10.4 - 10.8.2. кроме одной вещи, он не должен блокировать или получать события из диалогового окна ввода пароля (что не является большой неожиданностью)

То, что я вижу сейчас, отличается от того, что вы видите:

  • мы используем kCGTailAppendEventTap вместо kCGHeadInsertEventTap (это не должно иметь значения)
  • мы делаем некоторую регистрацию событий в установленном обратном вызове
  • у нас есть некоторые пользовательские данные о событиях в самоинъецированных событиях, которые отфильтровываются, но кроме этого мы просто возвращаем NULL для удаления нежелательного события (как вы делаете), я могу подтвердить, что не все события игнорируются!
  • мы включаем / выключаем событие, нажмите таким образом:
bool SetInputFilter (bool bOn)
{
    bool result = false;
    CFRunLoopRef runLoopRef = CFRunLoopGetMain ();

    если (Бон) {
        // Создать событие касания.
        CGEventMask eventMask = kCGEventMaskForAllEvents;
        if ((m_eventTapInput = CGEventTapCreate(kCGHIDEventTap, 
                                              kCGTailAppendEventTap, 
                                              kCGEventTapOptionDefault,
                                              eventMask, CGInputEventCallback, this)) == NULL) {
            Журнал (L"Не удалось создать событие нажмите");
            вернуть результат;
        }

        // Создать источник цикла выполнения.
        m_runLoopEventTapSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, m_eventTapInput, 0);
        CFRelease(m_eventTapInput);   // CFMachPortCreateRunLoopSource сохраняет m_eventTapInput
        if (m_runLoopEventTapSource == NULL) {
            Журнал (L"Не удалось создать источник цикла выполнения для события");
            вернуть результат;
        }

        // Добавить в текущий цикл выполнения.
        CFRunLoopAddSource(runLoopRef, m_runLoopEventTapSource, kCFRunLoopCommonModes);//kCFRunLoopDefaultMode);
        CFRelease(m_runLoopEventTapSource);   // CFRunLoopAddSource сохраняет m_runLoopEventTapSource
        результат = правда;
    }
    еще {
        // Отключить касание события.
        if (m_eventTapInput)
            CGEventTapEnable(m_eventTapInput, false);

        // Удалить наш источник цикла выполнения из текущего цикла выполнения.
        if (runLoopRef && m_runLoopEventTapSource) {
            CFRunLoopRemoveSource(runLoopRef, m_runLoopEventTapSource, kCFRunLoopCommonModes);//kCFRunLoopDefaultMode);
            m_runLoopEventTapSource = NULL;   // удаление m_runLoopEventTapSource освобождает последнюю ссылку на m_runLoopEventTapSource
            m_eventTapInput = NULL;           // удаление m_runLoopEventTapSource освобождает последнюю ссылку на m_eventTapInput
        }
    }
    вернуть результат;
}

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

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

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

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

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

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

Другие вопросы по тегам