Подтвердить что ты не робот

Вызов события 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, но событие, похоже, не удаляется. Любые идеи?

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

То, что я вижу сейчас, отличается от вашего образца:

  • мы используем 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;
}

Ответ 3

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