Как отслеживать состояние ключа глобального модификатора (в любом приложении)? - PullRequest
11 голосов
/ 21 октября 2009

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

У меня проблема с поведением kEventHotKeyReleased:

Скажем, например, я нажимаю комбинацию клавиш Cmd-Shift-P. Как только я отпускаю клавишу «P», запускается событие горячей клавиши. Мне нужно иметь возможность инициировать событие (или запустить его вручную), когда все клавиш не нажаты (т. Е. Клавиши Cmd и Shift тоже отпущены).

Легко отслеживать горячие клавиши, но я ничего не видел для мониторинга отдельных нажатий клавиш. Если бы я мог следить за состояниями клавиш-модификаторов, я бы занимался бизнесом.

Любые советы о том, как это сделать?

Заранее спасибо!


UPDATE:

Я пытался использовать kEventRawKeyUp и kEventRawKeyModifiersChanged, но, хотя kEventHotKeyReleased работает, эти два не работают, хотя я настроил их точно так же, как kEventHotKeyReleased.

EventTypeSpec eventTypes[] = {{kEventClassKeyboard, kEventHotKeyReleased}, {kEventClassKeyboard, kEventRawKeyUp}};
// Changing the order in the list does not help, nor does removing kEventHotKeyReleased
OSStatus err = InstallApplicationEventHandler(&globalHotkeyHandler, GetEventTypeCount(eventTypes), eventTypes, NULL, NULL);
// err == noErr after this line

Метод globalHotKeyHandler вызывается для kEventHotKeyReleased, но не для kEventRawKeyUp по какой-то причине, которую я не могу понять. Вот как выглядит мой globalHotKeyHandler метод:

OSStatus globalHotkeyHandler(EventHandlerCallRef nextHandler, EventRef anEvent, void *userData) {
    NSLog(@"Something happened!");
}

Нужно ли сделать дополнительный звонок или что-то еще, что я забыл?

N.B: На первый взгляд может показаться, что Access for Assistive Devices отключен, но это не . Так что я довольно невежественен.


ОБНОВЛЕНИЕ 2:

Я немного исследовал предложенный Лейбовицн CGEventTap и придумал эту настройку:

CFMachPortRef keyUpEventTap = CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,kCGEventKeyUp,&keyUpCallback,NULL);
CFRunLoopSourceRef keyUpRunLoopSourceRef = CFMachPortCreateRunLoopSource(NULL, keyUpEventTap, 0);
CFRelease(keyUpEventTap);
CFRunLoopAddSource(CFRunLoopGetCurrent(), keyUpRunLoopSourceRef, kCFRunLoopDefaultMode);
CFRelease(keyUpRunLoopSourceRef);

... и обратный вызов:

CGEventRef keyUpCallback (CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
    NSLog(@"KeyUp event tapped!");
    return event;
}

Как видите, я использую kCGEventKeyUp в качестве маски для события, но каким-то образом я получаю события мыши внизу ??! ??


ОБНОВЛЕНИЕ 3:

Хорошо, забудьте, что я пропустил строку в документе, в которой говорится, что для этого параметра используется CGEventMaskBit (kCGEventKeyUp), поэтому правильный вызов:

CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,CGEventMaskBit(kCGEventKeyUp),&keyUpCallback,NULL);

У меня все еще есть проблема: клавиши-модификаторы не вызывают kCGEventKeyUp ...


ОБНОВЛЕНИЕ 4:

Хорошо, забудь об этом снова ... Я обязательно отвечу на свои вопросы через 5 минут после того, как задаю их сегодня, да!

Чтобы перехватить клавиши-модификаторы, используйте kCGEventFlagsChanged:

CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionListenOnly,CGEventMaskBit(kCGEventFlagsChanged),&callbackFunction,NULL);

Итак, по сути, у меня работает определение состояния ключа и ключа модификатора, но Мне все еще интересно узнать , почему kEventRawKeyUp не работает ...


N.B .: Также обратите внимание, что я разрабатываю на Tiger с целью как можно большей поддержки новых и старых версий ОС. CGEventTap - только 10.4+, поэтому я буду использовать это сейчас, но было бы желательно использовать обратно совместимое решение.

Ответы [ 2 ]

3 голосов
/ 22 октября 2009

Один из вариантов - использовать EventTaps. Это позволяет вам отслеживать все события клавиатуры. Увидеть: http://developer.apple.com/mac/library/documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html#//apple_ref/c/func/CGEventTapCreate

К сожалению, отводы событий перестают работать, если приложение запрашивает безопасный ввод. Например Quicken.

1 голос
/ 22 октября 2009
OSStatus err = InstallApplicationEventHandler(&globalHotkeyHandler, GetEventTypeCount(eventTypes), eventTypes, NULL, NULL);

Это не глобально. Это устанавливает обработчик только тогда, когда ваше собственное приложение активно, и (я полагаю) после собственных фильтров событий Carbon Event Manager.

Вам необходимо использовать InstallEventHandler, который принимает цель события в качестве первого параметра (InstallApplicationEventHandler - это макрос, который передает цель события приложения).

Для событий, которые происходят, когда ваше приложение не активно, желаемая цель - GetEventMonitorTarget(). Для событий, которые происходят, когда ваше приложение активно , желаемая цель - GetEventDispatcherTarget(). Чтобы перехватывать события независимо от того, какое приложение активно, установите обработчик для обеих целей.

Однако в настоящее время я бы просто использовал CGEventTaps, как предложил Лейбовицн.

...