Capturing all multitouch trackpad input in Cocoa

后端 未结 2 1725
迷失自我
迷失自我 2020-12-25 08:07

Using touchesBeganWithEvent, touchesEndedWithEvent, etc you can get the touch data from the multitouch trackpad, but is there a way to block that touch data from moving the

相关标签:
2条回答
  • 2020-12-25 08:30

    As noted by valexa, using NSEventMask for CGEventTap is a hack. Tarmes also notes that Rob Keniger's answer no longer works (OS X >= 10.8). Luckily, Apple has provided a way to do this quite easily by using kCGEventMaskForAllEvents and converting the CGEventRef to an NSEvent within the callback:

    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();
    }
    

    Note that the call to CFRunLoopRun() is included since this snippet was taken from a project which could not use NSApplication but instead had a bare-bones CFRunLoop. Omit it if you use NSApplication.

    0 讨论(0)
  • 2020-12-25 08:54

    UPDATE: my answer below no longer works. See the answer here.

    Usually to do this you'd need to use a Quartz Event Tap, although the touch events don't seem to be "officially" supported by the CGEvent API. The non-multitouch event types in NSEvent.h seem to map to the CGEvent types in CGEventTypes.h, so the multitouch ones will probably work, even if they're not documented.

    In order to block the events from propagating, you need to return NULL from the event tap callback.

    You'd need some code like this:

    #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;
    }
    
    0 讨论(0)
提交回复
热议问题