11

I need to programmatically disable/suppress system-wide touch gestures on Mac OS. I'm referring to gestures such as the 4-finger swipe between spaces, etc.

I've looked to EventTap but that doesn't appear to be an option (despite previous reports here - perhaps it's changed under 10.8)

I've also tried numerous ways of changing the the system preferences programatically. For example, I've tried using IOConnectSetCFProperties on the service having located it using IORegistryEntryCreateCFProperties.

I've also delved into the trackpad preference pane to see how they do it, and I tried to reproduce it (ignore any create/release inconsistencies, this is just test code):

    NSInteger zero = 0;
    CFNumberRef numberWith0 = CFNumberCreate(kCFAllocatorDefault, kCFNumberNSIntegerType, &zero);

    CFMutableDictionaryRef propertyDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
                                                                    &kCFTypeDictionaryKeyCallBacks,
                                                                    &kCFTypeDictionaryValueCallBacks);

    CFDictionarySetValue(propertyDict, @"TrackpadFourFingerHorizSwipeGesture", numberWith0);

    io_connect_t connect = getEVSHandle(); // Found in the MachineSettings framework

    if (!connect)
    {
        NSLog(@"Unable to get EVS handle");
    }

    kern_return_t status = IOConnectSetCFProperties(connect, propertyDict);

    if (status != KERN_SUCCESS)
    {
        NSLog(@"Unable to get set IO properties");
    }

    CFRelease(propertyDict);
    CFPreferencesSetValue(CFSTR("com.apple.trackpad.fourFingerHorizSwipeGesture"), _numberWith0, kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);

    CFPreferencesSetValue(CFSTR("TrackpadFourFingerHorizSwipeGesture"), _numberWith0, CFSTR("com.apple.driver.AppleBluetoothMultitouch.trackpad"), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);

    CFPreferencesSynchronize(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);

    status = BSKernelPreferenceChanged(CFSTR("com.apple.driver.AppleBluetoothMultitouch.trackpad"));

In this case it appears to work, there are no errors and the option becomes disabled in the system preference pane, however the four finger gesture continues to work. I suspect that logging out then in will have an effect, but I haven't tried because that's not good enough in any case.

It's worth noting that the Pref Pane itself also calls BSKernelPreferenceChanged, but I don't know which framework that might be in order to link to it. Perhaps that's the key to the problem...

UPDATE: Actually I've now found it and linked it to. Adding that call made no difference, although it returns 1 which may indicate an error. I've added the call to the code above.

Finally I tried this from the terminal:

defaults write -globalDomain com.apple.trackpad.fourFingerHorizSwipeGesture 0
defaults write  com.apple.driver.AppleBluetoothMultitouch.trackpad TrackpadFourFingerHorizSwipeGesture 0

That doesn't have an immediate effect either.

I don't believe that this isn't possible, there must be a way...

MAS compatibility is not required.

tarmes
  • 15,366
  • 10
  • 53
  • 87
  • For the "defaults write" bit, what do you mean by "doesn't have an immediate effect" Does it work after reboot? Many times you have to 'killall dock' and 'killall finder' to get some of the defaults to take effect. – jeremy Dec 06 '12 at 18:26
  • Hi Jeremy. Indeed, but I need it to have an immediate effect, like it does when you change the trackpad options in the preference pane. – tarmes Dec 06 '12 at 18:50
  • you could try relaunching finder, if you don't want to log out. – Grady Player Dec 06 '12 at 23:08
  • Sorry, perhaps this isn't clear. I want to be able to use the trackpad for my own application. Currently this isn't possible because there are so many gestures that are taken over by the system. I'd therefore like to disable the gestures when my app is in use, and then enable them again afterwards, and I don't want to bother the user with relaunching stuff, it should all be invisible and instantaneous. – tarmes Dec 07 '12 at 07:49
  • Why are you trying to do this? If a user has configured these gestures, why are you trying to override the user's settings? – Jonathan Grynspan Dec 07 '12 at 16:56
  • So that the user will have more gestures available for the app. They'll only apply while he/she is holding down a hot key, after that the trackpad will behave as normal. – tarmes Dec 07 '12 at 17:01
  • This is indirect, but if you have a way of tracing API calls you could see how [Magic Mouse helper programs](http://onsoftware.en.softonic.com/the-best-magic-mouse-software) work. Or try contacting the author of [BetterTouchTool](http://www.bettertouchtool.net/) and see if they're willing to give tips. – GargantuChet Dec 13 '12 at 14:02
  • Hi. I've spoken to the BTT author (who's been very helpful), but he couldn't solve this either. – tarmes Dec 13 '12 at 14:24

2 Answers2

1

I'm also trying to do this.

Event taps does not work, neither does having a view that is first responder.

From Apple docs:

However, there are certain system-wide gestures, such as a four-finger swipe. for which the system implementation takes precedence over any gesture handling an application performs.

The only way i've been able to stop the system wide gestures is using CGDisplayCapture. This gives my application exclusive access to all events... but also a fullscreen drawing context.

Perhaps it's possible to see what calls are made to the quartz event services when entering this mode

https://developer.apple.com/library/mac/#documentation/graphicsimaging/Conceptual/QuartzDisplayServicesConceptual/Articles/DisplayCapture.html

Johan Nordberg
  • 356
  • 4
  • 8
0

I think you are looking in the wrong spot for disabling the touch events. The way OSX (and many other systems) is that the first responder in the view chain to handle the event will stop the event from propagating. You will need to write event handlers in your views for each of the touch events you want to handle, and if they exist, the OS will stop sending the events all the way to finder or whatever other application is next in line to handle the touch events.

See: http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/EventOverview/HandlingTouchEvents/HandlingTouchEvents.html

Specifically: Handling Multi-Touch Events (call setAcceptsTouchEvents, then implement touches...WithEvent...)

Hope that helps!

jeremy
  • 4,294
  • 3
  • 22
  • 36
  • Hi. I have no idea if that would work for a standard app of if the global gestures would still happen. In any case I can't do that because I'm trying to add gestures to another app (controlling it via the Assistive API). So, I have to do what I'm trying to do... – tarmes Dec 07 '12 at 17:00
  • On hotkey, can you load a transparent full screen view to the screen that can capture the events? – jeremy Dec 07 '12 at 17:13
  • Unfortunately not, since the user still needs to be able to interact with the other app. – tarmes Dec 07 '12 at 20:00
  • I think they will pass through if you don't handle them, might be worth a try. Have you looked at [NSEvent addGlobalMonitorForEventsMatchingMask]? – jeremy Dec 07 '12 at 21:30