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.
Related
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;
}
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!");
}
}
I need some kind of mouse movement pattern recognizer for Cocoa. What I specifically need is to recognize a mouse "shake" or some kind of circular movement. I've read about Protractor but I would like to know if there is some kind of library already implemented.
I'm currently setting a global event monitor to track mouse movements system wide but I need to be able to recognize specific patterns like circular movement, shake, and similar ones.
_eventMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSMouseMovedMask handler:^(NSEvent *eventoEntrada) {
NSLog(#"Movement detected");
NSPoint loc = [NSEvent mouseLocation];
NSLog(#"x:%.2f y:%.2f",loc.x, loc.y);
}];
Is there any library out there to achieve this task?
Thank you!
You can use the Quartz library in mac OS X
1- define the mouse event mask in your applicationDidFinishLaunching method like that
CFMachPortRef mouseEventTap;
CGEventMask mouseEventMask;
CFRunLoopSourceRef runLoopMouseSource;
// Create an event tap. We are interested in key presses.
mouseEventMask = (1 << kCGEventMouseMoved) ;
mouseEventTap = CGEventTapCreate(kCGSessionEventTap, kCGTailAppendEventTap, 0,
mouseEventMask, mouseCGEventCallback, NULL);
if (!mouseEventTap) {
fprintf(stderr, "failed to create event tap\n");
exit(1);
}
// Create a run loop source.
runLoopMouseSource = CFMachPortCreateRunLoopSource(
kCFAllocatorDefault, mouseEventTap, 0);
// Add to the current run loop.
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopMouseSource,
kCFRunLoopCommonModes);
// Enable the event tap.
CGEventTapEnable(mouseEventTap, true);
Then implement the call back function mouseCGEventCallback like this
CGEventRef mouseCGEventCallback(CGEventTapProxy proxy, CGEventType type,
CGEventRef event, void *refcon)
{
if (type == kCGEventMouseMoved)
{
//here you can detect any information you need from the event field key
return event;
}
}
For more information about the event field check this
https://developer.apple.com/library/mac/#documentation/Carbon/Reference/QuartzEventServicesRef/Reference/reference.html#//apple_ref/c/tdef/CGEventField
Hope to be helpful for you
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;
}
For a scripting utility I need to be able to record a series of keyboard and mouse events that occur when an application has focus. The second part is being able to later send those events to the active window.
I do not need to worry about menus or tracking the identifier of which window receives input.
I know how to do this under Windows but have no idea about Mac OS X.
The first thing i will tell you is that you CAN'T do this without the user enabling support for assitive devices in the accessability control panel. It's some kind of security built into OSX.
Here is a code snippit I am using in one of my applications to do this:
//this method calls a carbon method to attach a global event handler
- (void)attachEventHandlers
{
//create our event type spec for the keyup
EventTypeSpec eventType;
eventType.eventClass = kEventClassKeyboard;
eventType.eventKind = kEventRawKeyUp;
//create a callback for our event to fire in
EventHandlerUPP handlerFunction = NewEventHandlerUPP(globalKeyPress);
//install the event handler
OSStatus err = InstallEventHandler(GetEventMonitorTarget(), handlerFunction, 1, &eventType, self, NULL);
//error checking
if( err )
{
//TODO: need an alert sheet here
NSLog(#"Error registering keyboard handler...%d", err);
}
//create our event type spec for the mouse events
EventTypeSpec eventTypeM;
eventTypeM.eventClass = kEventClassMouse;
eventTypeM.eventKind = kEventMouseUp;
//create a callback for our event to fire in
EventHandlerUPP handlerFunctionM = NewEventHandlerUPP(globalMousePress);
//install the event handler
OSStatus errM = InstallEventHandler(GetEventMonitorTarget(), handlerFunctionM, 1, &eventTypeM, self, NULL);
//error checking
if( errM )
{
//TODO: need an alert sheet here
NSLog(#"Error registering mouse handler...%d", err);
}
}
Here is an example of the callback method i am using:
OSStatus globalKeyPress(EventHandlerCallRef nextHandler, EventRef theEvent, void *userData)
{
NSEvent *anEvent = [NSEvent eventWithEventRef:theEvent];
NSEventType type = [anEvent type];
WarStrokerApplication *application = (WarStrokerApplication*)userData;
//is it a key up event?
if( type == NSKeyUp)
{
//which key is it?
switch( [anEvent keyCode] )
{
case NUMERIC_KEYPAD_PLUS:
//this is the character we are using for our toggle
//call the handler function
[application toggleKeyPressed];
break;
//Comment this line back in to figure out the keykode for a particular character
default:
NSLog(#"Keypressed: %d, **%#**", [anEvent keyCode], [anEvent characters]);
break;
}
}
return CallNextEventHandler(nextHandler, theEvent);
}
For the latter part, posting events, use the CGEvent methods provided in ApplicationServices/ApplicationServices.h
Here's an example function to move the mouse to a specified absolute location:
#include <ApplicationServices/ApplicationServices.h>
int to(int x, int y)
{
CGPoint newloc;
CGEventRef eventRef;
newloc.x = x;
newloc.y = y;
eventRef = CGEventCreateMouseEvent(NULL, kCGEventMouseMoved, newloc,
kCGMouseButtonCenter);
//Apparently, a bug in xcode requires this next line
CGEventSetType(eventRef, kCGEventMouseMoved);
CGEventPost(kCGSessionEventTap, eventRef);
CFRelease(eventRef);
return 0;
}
For tapping mouse events, see Link
I haven't checked this under 10.5 Leopard but on 10.4 it works.