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