Нажатие на событие 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 событий или около того подряд.