how to disable keys in OS X? - macos

I've an application that needs disable some keys while the application in running (i.e: A, option, command, shift).
how can I do that?
the language or method used does not matter.

You can inspect, modify, and block keyboard events with a CGEventTap.
The user will need to grant your app assistive privileges via the Security pane of System Preferences for events to be disabled before posting.
Something like this:
- (void)setKeyBlocker {
// You should filter this better than kCGEventMaskForAllEvents, depending on your needs.
CFMachPortRef eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, kCGEventMaskForAllEvents, cgEventCallback, NULL);
CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
CFRelease(eventTap);
CFRelease(runLoopSource);
}
CGEventRef callback(CGEventTapProxy proxy, CGEventType type, CGEventRef cgEvent, void *refcon) {
NSEvent *event = [NSEvent eventWithCGEvent:cgEvent];
if (event.type == kCGEventKeyDown) {
if ([event.characters isEqualToString:#"a"]) {
// Kill event
return NULL;
}
}
return cgEvent;
}

Related

How to read mouse delta in Quartz events?

In a Quartz application, I am trying to freeze the mouse pointer on the screen but continue to register mouse mouvements from the user. I have found how to freeze the pointer:
CGAssociateMouseAndMouseCursorPosition(false);
I am following the documentation but don't know how to get and read events that contain mouse delta (change in X and Y) data.
I found the solution. I am now getting the mouse movements like this:
CGEventMask eventMask = CGEventMaskBit(kCGEventMouseMoved);
CFMachPortRef tap = CGEventTapCreate( kCGAnnotatedSessionEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionListenOnly,
eventMask,
eventOccurred,
NULL);
CFRunLoopSourceRef rl = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tap, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), rl, kCFRunLoopCommonModes);
CGEventTapEnable(tap, true);
where the callback is:
CGEventRef eventOccurred(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon) {
if (type == kCGEventMouseMoved) {
d_x = CGEventGetIntegerValueField(event, kCGMouseEventDeltaX);
d_y = CGEventGetIntegerValueField(event, kCGMouseEventDeltaY);
}
return event;
}
Note: d_xand d_y are globally defined int that store the mouse movement between each event.

(OS X) Prevent keyobard during programmatic mouse click

I'm trying to port keynav to OS X. It is basically working, but I'm facing a problem due to the inability to change (global) monitored events.
Is it possible to suppress a keyboard modifier during a mouse click event? Maybe using a sequence, for example creating a keyboard event with "control key up" + mouse click?
This is my mouse click method:
- (void)clickMouseAtPoint:(CGPoint)point
{
CGEventRef move = CGEventCreateMouseEvent(
NULL, kCGEventMouseMoved,
point,
kCGMouseButtonLeft // ignored
);
CGEventRef click1_down = CGEventCreateMouseEvent(
NULL, kCGEventLeftMouseDown,
point,
kCGMouseButtonLeft
);
CGEventRef click1_up = CGEventCreateMouseEvent(
NULL, kCGEventLeftMouseUp,
point,
kCGMouseButtonLeft
);
CGEventPost(kCGHIDEventTap, move);
CGEventPost(kCGHIDEventTap, click1_down);
CGEventPost(kCGHIDEventTap, click1_up);
CFRelease(click1_up);
CFRelease(click1_down);
CFRelease(move1);
}
So this is what I ended up with. Background to what is done here in particular: The keynav is overlayed to the whole desktop when pressing ctrl+comma, a user may navigate the cross-hair using holding ctrl+SomeKeys, and simulate a mouse click at the cross-hait position by a second ctrl+comma (awaitClick is set when the second click is awaited in the code snippet below).
Now, before the click is done the control key modifier is removed from the keyboard event. Also, while keynav is shown all keyboard input is swapped with keycode -1. I found this by experimenting that this suppresses a key press.
If this is a good way or not, I can't tell. But it works reliably so far.
CGEventRef
myCGEventCallback(CGEventTapProxy proxy, CGEventType type,
CGEventRef event, void *refcon)
{
if ((type != kCGEventKeyDown) && (type != kCGEventKeyUp))
return event;
CGKeyCode keycode = (CGKeyCode)CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
EWAppDelegate *appInstance = [NSApplication sharedApplication].delegate;
CGEventFlags flags = CGEventGetFlags(event);
if (_awaitClick && keycode == (CGKeyCode)43 && type == kCGEventKeyDown)
{
flags = flags & ~kCGEventFlagMaskControl;
CGEventSetFlags(event, flags);
NSLog(#"suppressed control key");
}
BOOL didHandle = NO;
if (type == kCGEventKeyDown)
{
didHandle = [appInstance handleNavKey:keycode flags:flags];
}
NSLog(#"pressed keycode %d", keycode);
if (didHandle || _navIsSHowing) {
CGEventSetIntegerValueField(event, kCGKeyboardEventKeycode, (int64_t)-1);
NSLog(#"nav");
}
return event;
}
- (BOOL)registerGlobalKeyBoardModifier
{
CFMachPortRef eventTap;
CGEventMask eventMask;
CFRunLoopSourceRef runLoopSource;
eventMask = ((1 << kCGEventKeyDown) | (1 << kCGEventKeyUp));
eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0,
eventMask, myCGEventCallback, NULL);
if (!eventTap) {
fprintf(stderr, "failed to create event tap\n");
return NO;
}
runLoopSource = CFMachPortCreateRunLoopSource(
kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,
kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
return YES;
}

cocoa global watch for modifier key press

Using addGlobalMonitorForEventsMatchingMask, I can check for keys being pressed. And when an event is fired, I can check the modifier flags to see if any modifiers were used in conjunction.
But I need to know if a modifier is pressed without another key being pressed.
How would I do this?
You're looking for events of type kCGEventFlagsChanged:
CGEventMask eventMask = (1 << kCGEventFlagsChanged);
CFMachPortRef eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, eventMask, cgEventCallback, NULL);
...
CGEventRef cgEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef cgEvent, void *refcon)
{
NSEvent *event = [NSEvent eventWithCGEvent:cgEvent];
if (event.type == kCGEventFlagsChanged) {
NSLog(#"modifier key!");
}
}

Stop Intercepting Keyboard Input While App Running - CGEventTap

What is the correct way to stop watching keyboard event taps using CGEventTap?
I am building a simple background app that converts the output of specific keys. Thanks to this excellent post on CGEventTap, I've been able to enable the key conversion. Unfortunately, I do not seem to be able to stop it short of killing the app.
The following method is called when the user toggles a checkbox to turn the functionality ON or OFF. Toggle ON happens immediately. Toggle OFF can take a minute or more before it takes affect. I see via log that the "Disabled. Stop converting taps." is detected. But the key conversion just keeps on going. I don't understand why.
- (void)watchEventTap
{
#autoreleasepool
{
CFRunLoopSourceRef runLoopSource = NULL;
CFMachPortRef eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, CGEventMaskBit(kCGEventKeyUp) | CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(NX_SYSDEFINED), myCGEventCallback, NULL);
runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
if (!eventTap)
{
NSLog(#"Couldn't create event tap!");
exit(1);
}
if (self.shortcutEnabled) // User default toggled ON
{
NSLog(#"Enabled. Convert taps.");
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
// CFRunLoopRun(); // This blocks rest of app from executing
}
else // User default toggled OFF
{
NSLog(#"Disabled. Stop converting taps.");
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, false);
// Clean up the event tap and source after ourselves.
CFMachPortInvalidate(eventTap);
CFRunLoopSourceInvalidate(runLoopSource);
CFRelease(eventTap);
CFRelease(runLoopSource);
eventTap = NULL;
runLoopSource = NULL;
}
}
// exit(0); // This blocks rest of app from executing
}
Thanks for any suggestions. I'm new building Mac OS X apps, so please forgive me if I'm doing something ignorant.
Thanks to an experienced Mac developer, I got my issue resolved. I was creating a new runLoopsSource every time the method was called.
Now I've created instance variables for the tapEvent and runLoop. Only one line was needed to stop the eventTap. Modified method below:
- (void)watchEventTap
{
#autoreleasepool
{
if ( [[NSUserDefaults standardUserDefaults] isEnabledNumLockDV] == YES ) // User default toggled ON
{
_runLoopSource = NULL;
_eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, CGEventMaskBit(kCGEventKeyUp) | CGEventMaskBit(kCGEventKeyDown) | CGEventMaskBit(NX_SYSDEFINED), myCGEventCallback, NULL);
_runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, _eventTap, 0);
if (!_eventTap)
{
NSLog(#"Couldn't create event tap!");
exit(1);
}
NSLog(#"Enabled. Convert taps.");
CFRunLoopAddSource(CFRunLoopGetCurrent(), _runLoopSource, kCFRunLoopCommonModes);
CGEventTapEnable(_eventTap, true);
}
else if ( [[NSUserDefaults standardUserDefaults] isEnabledNumLockDV] == NO ) // User default toggled OFF
{
NSLog(#"Disabled. Stop converting taps.");
CGEventTapEnable(_eventTap, false);
}
}
}

How to trap global keydown/keyup events in cocoa

I want to trap, modify and divert all the keydown/keyup events in the system within my cocoa app. I know about CGEventTapCreate but, didn't found any working code from net.
Thanks
Found Solution:
self.machPortRef = CGEventTapCreate(kCGSessionEventTap,
kCGTailAppendEventTap,
kCGEventTapOptionDefault,
CGEventMaskBit(kCGEventKeyDown),
(CGEventTapCallBack)eventTapFunction,
self);
if (self.machPortRef == NULL)
{
printf("CGEventTapCreate failed!\n");
} else {
self.eventSrc = CFMachPortCreateRunLoopSource(NULL, self.machPortRef, 0);
if ( self.eventSrc == NULL )
{
printf( "No event run loop src?\n" );
}else {
CFRunLoopRef runLoop = CFRunLoopGetCurrent(); //GetCFRunLoopFromEventLoop(GetMainEventLoop ());
// Get the CFRunLoop primitive for the Carbon Main Event Loop, and add the new event souce
CFRunLoopAddSource(runLoop, self.eventSrc, kCFRunLoopDefaultMode);
}
}
Properties :
CFMachPortRef machPortRef;
CFRunLoopSourceRef eventSrc;
Event Handler:
CGEventRef eventTapFunction(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
{
//printf("eventTap triggered\n");
return event;
}

Resources