Sunday, January 1, 2012

[Cocos2D Mac] Disable System Trackpad Gesture And Create Gestures Yourself

When you are making an application completely using trackpad with self-designed gestures, or using system gestures in different ways, you may want to disable system gestures like Four-Finger-Swipe, etc. For example, if we want to use Four-Finger-Swipe to show desktop instead of switch between screens, we should first keep the system from using Four-Finger-Swipe gesture. Unfortunately, the system gestures have the top priority in Mac OS X, that it still works when other applications are running. Thus, I found 2 ways to solve this problem, and I think the later is more applicable.

1. Using AppleScript in Cocoa

NSAppleScript *script = [[NSAppleScript alloc] initWithContentsOfURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"yourFile" ofType:@"scpt"]] error: nil];

[script executeAndReturnError: NULL];

2. Using EventTap

First, we should create an EventTap, which captures system defined gestures include NSEventMaskGesture, NSEventMaskMagnify, NSEventMaskSwipe, NSEventMaskRotate, NSEventMaskBeginGesture, and NSEventMaskEndGesture.
You can capture whatever gestures defined before by add a mask using CGEventMask. 
The following just captures swipe gestures:

In AppDelegate.m: 

- (void)createEventTap
{
    CFRunLoopSourceRef runLoopSource;
   
    CGEventMask eventMask = (NSEventMaskSwipe);
   
    eventTap = CGEventTapCreate(kCGHIDEventTap,
                                kCGHeadInsertEventTap,
                                kCGEventTapOptionDefault,
                                eventMask,
                                MyCGEventCallback,
                                NULL); 
   
    runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault,
                                                  eventTap,
                                                  0);
   
    CFRunLoopAddSource(CFRunLoopGetCurrent(),
                       runLoopSource,
                       kCFRunLoopCommonModes);
   
    CGEventTapEnable(eventTap, true);

In method applicationDidFinishLaunching:


[self createEventTap]; 

Then you can use the gesture captured in your CGEventCallback, like:


CGEventRef MyCGEventCallback(CGEventTapProxy proxy,
                             CGEventType type,
                             CGEventRef theEvent,
                             void *refcon)
{
    return NULL;

In MyCGEventCallback, if you return NULL, then the system just ignores the gesture, which means our work is partly done. But we may want to use the gesture instead of just disable it. Then we can use touchEvents defined in Cocos2d, which defined as:

-(BOOL) ccTouchesBeganWithEvent:(NSEvent *)event;
-(BOOL) ccTouchesMovedWithEvent:(NSEvent *)event;
-(BOOL) ccTouchesEndedWithEvent:(NSEvent *)event;
-(BOOL) ccTouchesCancelledWithEvent:(NSEvent *)event;


In fact, they are defined in origin as:

- (void)touchesBeganWithEvent:(NSEvent *)event;
- (void)touchesMovedWithEvent:(NSEvent *)event;
- (void)touchesEndedWithEvent:(NSEvent *)event;

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

If you are using a CCLayer, use the first one. Otherwise, use the second one.
A simple example:


-(BOOL) ccTouchesBeganWithEvent:(NSEvent *)event

{
    //NSLog(@"touch begin");
    NSSet * touches = [event touchesMatchingPhase:NSTouchPhaseBegan inView:[[CCDirectorMac sharedDirector] openGLView]];
    [player onTouchBegan:[touches retain]];
    return YES;
}

No comments:

Post a Comment