CGEventTapCreateForPSN в Mavericks + (устаревший GetCurrentProcess)

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

В частности, этот код делает то, что я хочу.

GetCurrentProcess(&psn);
CFMachPortRef eventTap = CGEventTapCreateForPSN(
    &psn,
    kCGHeadInsertEventTap,
    kCGEventTapOptionDefault,
    CGEventMaskBit(kCGEventKeyDown)
        | CGEventMaskBit(kCGEventKeyUp),
    eventCallback,
    userInfo);

И мой обратный вызов обрабатывается красиво, причем события перехватываются только из текущего приложения.

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

ProcessSerialNumber psn = { 0, kCurrentProcess };

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

ProcessSerialNumber psn = { 0, kCurrentProcess };
CFMachPortRef eventTap = CGEventTapCreateForPSN(
    &psn,
    kCGHeadInsertEventTap,
    kCGEventTapOptionDefault,
    CGEventMaskBit(kCGEventKeyDown)
        | CGEventMaskBit(kCGEventKeyUp),
    eventCallback,
    userInfo);

Я могу использовать CGEventTapCreate, но он удаляет весь хост, и тогда мне нужно будет фильтровать все, что не направлено на мое приложение, а CGEventTapProxy непрозрачно, и я не знаю, как его использовать для определения если его приложение или нет.

Я подтвердил, что устаревший код все еще работает, но Apple может решить удалить его в любое время. Итак, есть ли у кого-нибудь идея, как я должен действовать для вызова CGEventTapCreateForPSN в Mavericks и далее?

Спасибо!

Ответ 1

Я думаю, что для этой цели вы должны подклассифицировать NSApplication и переопределить метод - (void)sendEvent:(NSEvent *)theEvent. Из docs:

Вам редко нужно найти настоящую потребность в создании пользовательского подкласса NSApplication. В отличие от некоторых объектно-ориентированных библиотек, Cocoa не требует подкласса NSApplication для настройки поведения приложения. Вместо этого он предоставляет множество других способов настройки приложения.

также:

ВАЖНО

Многие классы AppKit полагаются на класс NSApplication и могут работать некорректно, пока этот класс не будет полностью инициализирован. В результате вы не должны, например, пытаться вызывать методы других классов AppKit из метода инициализации подкласса NSApplication.

Таким образом, вы можете перехватить все события, прошедшие через ваше приложение, и вызвать настраиваемые методы протокола NSApplicationDelegate.

// in SubApplication.h

@protocol ExtendedApplicationDelegate : NSApplicationDelegate

- (void)applicationDidTrapSomeInterestingEvent:(NSEvent *)event;

@end

// in SubApplication.m

- (void)sendEvent:(NSEvent *)event
{
    if ([event type] == NSKeyDown && [event keyCode]==_someCode)
    {
      // call application delegate method
    }
    [super sendEvent:event];
}

Я не уверен, что этот подход решает проблему, но вы все еще пытаетесь.