Any mouse movement pattern recognizer for Cocoa? - macos

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

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.

Receive notification for power cord on/off in OSX Cocoa for the laptop

ALL,
Does OSX sends any notification when the power cord is plugged/unplugged in the Mac laptop? Or plugging/unplugging the laptop from the docking station?
It does send something on shutdown/reboot/wake-up/etc, but I didn't find anything for that specific event.
Am I missing something? Or this is not available?
TIA!
There are no high-level notifications, but the IOPowerSources framework can post a message to your run loop whenever the power state changes.
My project has multiple PowerCondition objects that monitor various aspects of the current power (A/C vs. Battery, how much battery is left, and so on).
Here's a fragment of the PowerCondition class that shows how to observe power source change events via the run loop and turn that into a notification that multiple objects can observe (this is fairly old Obj-C):
#define kPowerConditionChangedNotification #"PowerConditionChanged"
static NSUInteger PowerChangeListenerCount = 0;
static CFRunLoopSourceRef PowerChangeSource = NULL;
static void PowerChangeCallback(void *context)
{
// Called by CFRunLoopSourceRef when something changes
// Post a notification so that all PowerCondition observers can reevaluate
[[NSNotificationCenter defaultCenter] postNotificationName:kPowerConditionChangedNotification object:nil];
}
#interface PowerCondition
{
BOOL listening;
//...
}
- (void)powerStateDidChangeNotification:(NSNotification*)notification;
#end
// ...
#implementation PowerCondition
- (void)startMonitoringCondition
{
if (!listening)
{
// Observe the power condition change notification
DebugLogger(#"condition '%#'",[self description]);
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(powerStateDidChangeNotification:)
name:kPowerConditionChangedNotification
object:nil];
if (PowerChangeListenerCount++==0)
{
// This is the first observer: create and install the run loop source that will fire the notification
BetaAssert(PowerChangeSource==NULL,#"zero PowerChangeListenerCount, but PowerChangeSource!=NULL");
PowerChangeSource = IOPSNotificationCreateRunLoopSource(PowerChangeCallback,NULL);
CFRunLoopAddSource([[NSRunLoop mainRunLoop] getCFRunLoop],PowerChangeSource,kCFRunLoopCommonModes);
DebugLoggerMessage(#"installed PowerChangeSource(PowerChangeCallback) in run loop");
}
listening = YES;
previousTestState = -1; // neither YES nor NO
}
}
- (void)stopMonitoringCondition
{
if (listening)
{
// Stop observing power condition change notifications
PowerLogger(#"condition '%#'",[self description]);
[[NSNotificationCenter defaultCenter] removeObserver:self];
BetaAssert(PowerChangeListenerCount!=0,#"PowerChangeListenerCount==0");
if (--PowerChangeListenerCount==0)
{
// This was the last power change observer: remove the run loop source the fires the notifications
BetaAssertNotNil(PowerChangeSource);
CFRunLoopRemoveSource([[NSRunLoop mainRunLoop] getCFRunLoop],PowerChangeSource,kCFRunLoopCommonModes);
CFRelease(PowerChangeSource);
PowerChangeSource = NULL;
DebugLoggerMessage(#"removed PowerChangeSource(PowerChangeCallback) from run loop");
}
listening = NO;
}
}
- (void)powerStateDidChangeNotification:(NSNotification*)notification
{
// Evaluate power state here
BOOL battery = NO; // assume unlimited/external power
double capacityRemaining = 1.0; // assume 100%
// First, determine if the system's active power source is AC or battery
CFTypeRef powerBlob = IOPSCopyPowerSourcesInfo();
CFArrayRef powerSourcesRef = IOPSCopyPowerSourcesList(powerBlob);
CFIndex count = CFArrayGetCount(powerSourcesRef);
if (count!=0)
{
// There's precious little explination what the meaning of the different power sources
// are or why there would be more than one. As far as I can tell, everything can be
// determined by obtaining the first (and probably only) power source description.
NSDictionary* powerInfo = (__bridge id)IOPSGetPowerSourceDescription(powerBlob,CFArrayGetValueAtIndex(powerSourcesRef,0));
if (![powerInfo[#kIOPSPowerSourceStateKey] isEqualToString:#kIOPSACPowerValue])
{
// Power source is not AC (must be battery or UPS)
battery = YES;
// Calculate the remaining capacity, as a fraction
NSNumber* capacityValue = powerInfo[#kIOPSCurrentCapacityKey];
NSNumber* maxValue = powerInfo[#kIOPSMaxCapacityKey];
if (capacityValue!=nil && maxValue!=nil)
capacityRemaining = [capacityValue doubleValue]/[maxValue doubleValue];
DebugLogger(#"power source is '%#', battery=%d, capacity=%#, max=%#, remaining=%f%%",
powerInfo[#kIOPSPowerSourceStateKey],
(int)battery,
capacityValue,maxValue,capacityRemaining*100);
}
}
// ...
}
Note that the callback is installed on the main run loop, so the notification will always be posted on the main thread.

OSX global mouse/trackpad hooking

I am not familiar with Apple's OSX
What I want to do is to set a global(system-wide) hook for 4 fingers scrolling (mouse and trackpad) and to be able
to change the scrolling events (to make it more iOS like) because system preferences does't cover it.
yes I assume there is a lot of programs like that but I want to make it my self (to learn OSX programming more).
My question is: What is the best template in Xcode to do so (there is many templates to start with and I have read about them , but I still can't understand which one is the best for it).
My question maybe a bit silly but I hope it is on topic for SO.
Thank you in advance. :)
Well the template you want to start with is OS X -> Application -> Cocoa Application
Then having this in your AppDelegate.m is a good place to start, as far as hooking into global mouse/trackpad events:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
CFMachPortRef eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, kCGEventMaskForAllEvents, handleCGEvent, (__bridge void *)(self));
CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetMain(), runLoopSource, kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
}
CGEventRef handleCGEvent(CGEventTapProxy proxy, CGEventType type, CGEventRef eventRef, void *refcon)
{
if (type == kCGEventLeftMouseDown /*|| type == kCGEventMouseMoved || type == kCGEventMouseDragged || ...*/) {
}
return eventRef;
}

NSDatePicker action method not firing

Scenario:
The user is entering a date in an NSDatePicker in its textual form with no stepper (on OS X), and when they hit return on the keyboard, I'd like a message to be sent to the controller.
In an NSTextField, I would just hook the action up in Interface Builder, or set it from code, and when the user hits enter, the action message is sent to the target.
The date picker allows to set an action message and a target, but I can't get the action to fire. When I hit enter in the date picker, the action message does not get called.
Am I doing something wrong, or is there a workaround that I have to use? I would not be adverse to subclassing any of the classes involved, if that is what it takes.
An NSDatePicker will not handle or forward key events triggered by the Return or Enter key.
The solution is to subclass NSDatePicker to get the desired behavior in keyDown:
#import "datePickerClass.h"
#implementation datePickerClass
- (void)keyDown:(NSEvent *)theEvent{
unsigned short n = [theEvent keyCode];
if (n == 36 || n == 76) {
NSLog(#"Return key or Enter key");
// do your action
//
} else {
[super keyDown:theEvent];// normal behavior
}
}
#end
That's it.
Edit : also you can use NSCarriageReturnCharacter and NSEnterCharacter
NSString* const s = [theEvent charactersIgnoringModifiers];
unichar const key = [s characterAtIndex:0];
if (key == NSCarriageReturnCharacter || key == NSEnterCharacter) {

How to Capture / Post system-wide Keyboard / Mouse events under Mac OS X?

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.

Resources