Захват всех входных данных трекпада мультитач в Cocoa

Используя touchhesBeganWithEvent, touchEEdedWithEvent и т.д. вы можете получить данные касания с трекпада мультитач, но есть ли способ заблокировать данные касания от перемещения мыши/активации общесистемных жестов (подобно тому, как это делается на китайском языке текстовый ввод)?

Ответ 1

Как отмечено valexa, использование NSEventMask для CGEventTap является взломом. Тармес также отмечает, что ответ Роб Кенигер больше не работает (OS X >= 10,8). К счастью, Apple предоставила возможность сделать это довольно легко, используя kCGEventMaskForAllEvents и преобразовывая CGEventRef в NSEvent в обратном вызове:

NSEventMask eventMask = NSEventMaskGesture|NSEventMaskMagnify|NSEventMaskSwipe|NSEventMaskRotate|NSEventMaskBeginGesture|NSEventMaskEndGesture;

CGEventRef eventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eventRef, void *refcon) {
  // convert the CGEventRef to an NSEvent
  NSEvent *event = [NSEvent eventWithCGEvent:eventRef];

  // filter out events which do not match the mask
  if (!(eventMask & NSEventMaskFromType([event type]))) { return [event CGEvent]; }

  // do stuff
  NSLog(@"eventTapCallback: [event type] = %d", [event type]);

  // return the CGEventRef
  return [event CGEvent];
}

void initCGEventTap() {
  CFMachPortRef eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, kCGEventMaskForAllEvents, eventTapCallback, nil);
  CFRunLoopAddSource(CFRunLoopGetCurrent(), CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0), kCFRunLoopCommonModes);
  CGEventTapEnable(eventTap, true);
  CFRunLoopRun();
}

Обратите внимание, что вызов CFRunLoopRun() включен, поскольку этот фрагмент был взят из проекта, который не мог использовать NSApplication, но вместо этого имел CFRunLoop с голубыми костями. Опустите его, если вы используете NSApplication.

Ответ 2

UPDATE: мой ответ ниже больше не работает. См. Ответ здесь.

Обычно для этого вам нужно использовать Quartz Event Tap, хотя сенсорные события, похоже, не "официально" поддерживаются API CGEvent. Типы событий non-multitouch в NSEvent.h, похоже, сопоставляются с типами CGEvent в CGEventTypes.h, поэтому мультитач-функции, вероятно, будут работать, даже если они не документированы.

Чтобы заблокировать события от распространения, вам нужно вернуть NULL из обратного вызова события.

Вам понадобится следующий код:

#import <ApplicationServices/ApplicationServices.h>

//assume CGEventTap eventTap is an ivar or other global

void createEventTap(void)
{
 CFRunLoopSourceRef runLoopSource;

 //listen for touch events
 //this is officially unsupported/undocumented
 //but the NSEvent masks seem to map to the CGEvent types
 //for all other events, so it should work.
 CGEventMask eventMask = (
  NSEventMaskGesture       |
  NSEventMaskMagnify       |
  NSEventMaskSwipe         |
  NSEventMaskRotate        |
  NSEventMaskBeginGesture  |
  NSEventMaskEndGesture
 );

 // Keyboard event taps need Universal Access enabled, 
 // I'm not sure about multi-touch. If necessary, this code needs to 
 // be here to check whether we're allowed to attach an event tap
 if (!AXAPIEnabled()&&!AXIsProcessTrusted()) { 
  // error dialog here 
  NSAlert *alert = [[[NSAlert alloc] init] autorelease];
  [alert addButtonWithTitle:@"OK"];
  [alert setMessageText:@"Could not start event monitoring."];
  [alert setInformativeText:@"Please enable \"access for assistive devices\" in the Universal Access pane of System Preferences."];
  [alert runModal];
  return;
 } 


 //create the event tap
 eventTap = CGEventTapCreate(kCGHIDEventTap, //this intercepts events at the lowest level, where they enter the window server
        kCGHeadInsertEventTap, 
        kCGEventTapOptionDefault, 
        eventMask,
        myCGEventCallback, //this is the callback that we receive when the event fires
        nil); 

 // Create a run loop source.
 runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);

 // Add to the current run loop.
 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);

 // Enable the event tap.
 CGEventTapEnable(eventTap, true);
}


//the CGEvent callback that does the heavy lifting
CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef theEvent, void *refcon)
{
 //handle the event here
 //if you want to capture the event and prevent it propagating as normal, return NULL.

 //if you want to let the event process as normal, return theEvent.
 return theEvent;
}