Reputation: 3147
Is there anyway to intercept and change/ignore, globally, a given shortcut in Objective-C for a Mac application?
An example is BetterTouchTool, it can override any shortcut you provide.
What I want to do is prevent the 'quit' shortcut (i.e. CMD+q) when a specific application is open (because in this instance the shortcut is inadvertantly pressed and closes the application undesirably for some people).
In short, can I listen for any global key events and then change the event before it gets delivered to its intended application?
Upvotes: 5
Views: 2351
Reputation: 3147
Here's how to setup the event listener
CFMachPortRef eventTap = CGEventTapCreate(kCGHIDEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionDefault,
CGEventMaskBit(kCGEventKeyDown),
&KeyDownCallback,
NULL);
CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(NULL, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
CFRelease(runLoopSource);
CGEventTapEnable(eventTap, true);
And then here is the "callback":
static CGEventRef KeyDownCallback(CGEventTapProxy proxy,
CGEventType type,
CGEventRef event,
void *refcon)
{
/* Do something with the event */
NSEvent *e = [NSEvent eventWithCGEvent:event];
return event;
}
on the parsed NSEvent, there are the modifierFlags
and keyCode
properties. keyCode
is the code of the key that was pressed, and modifierFlags
is the different modifiers that were pressed (Shift, Alt/Option, Command, etc).
Simply return NULL;
in the KeyDownCallback
method to stop the event from propogating.
Note: there seems to be an issue with the event tap timing out, to solve this problem you can 'reset' the event tap.
In the KeyDownCallback
method, check if the CGEventType type
is kCGEventTapDisabledByTimeout
like so:
if (type == kCGEventTapDisabledByTimeout)
{
/* Reset eventTap */
return NULL;
}
and where Reset eventTap
is, execute the setup of the event listener above again.
Upvotes: 5