OSX global mouse/trackpad hooking - macos

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;
}

Related

How to show simple text messages (toasts/alerts) in a Cocoa MacOS Webview?

I've already searched "everything" about this in Google/Stackoverflow, but I'm still stuck. I have just started developing OSX Apps, so I'm a (almost) complete newbie in Objective-C and Xcode 5 (5.0.2).
All I need is a simple webview to load a webgame from a given URL. This webview must behave just like a very simple Safari browser. My app is already working relatively well. It loads the game OK, and after a lot of struggling I succeeded making it show javascript alerts and confirms.
THE POINT: I need to display a simple text message to the user, in case of no internet connection is detected, then I need to close the app. It seems a very trivial thing, but I can't find a way to do that!!
That's my appDelegate.M:
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize myWebView;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
// Check if there's internet connection:
#include <SystemConfiguration/SystemConfiguration.h>
static BOOL internetOk()
{
BOOL returnValue = NO;
struct sockaddr zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sa_len = sizeof(zeroAddress);
zeroAddress.sa_family = AF_INET;
SCNetworkReachabilityRef reachabilityRef = SCNetworkReachabilityCreateWithAddress(NULL, (const struct sockaddr*)&zeroAddress);
if (reachabilityRef != NULL)
{
SCNetworkReachabilityFlags flags = 0;
if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
{
BOOL isReachable = ((flags & kSCNetworkFlagsReachable) != 0);
BOOL connectionRequired = ((flags & kSCNetworkFlagsConnectionRequired) != 0);
returnValue = (isReachable && !connectionRequired) ? YES : NO;
}
CFRelease(reachabilityRef);
}
return returnValue;
}
// -
if(internetOk())
{
[self.window setContentView:self.myWebView];
[self.window toggleFullScreen:#""];
[self.myWebView setMainFrameURL:#"http://www.mywebgameurl.com"];
}
else
{
// SHOWS ERROR MESSAGE AND CLOSES APP! HOW CAN I DO IT????
}
}
#end
Any help is welcome, thanks!!
You're looking for the NSAlert class, check here for Apple's docs.
Sample usage:
NSAlert* alert = [NSAlert alertWithMessageText:#"Internet Error"
defaultButton:nil
alternateButton:nil
otherButton:nil
informativeTextWithFormat:#"No internet."];
[alert runModal];

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);
}
}
}

Any mouse movement pattern recognizer for Cocoa?

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

Cocoa vs Carbon

What would be the Cocoa equivalent for this code?
#include <Carbon/Carbon.h>
void check_apple_events() {
printf("check_apple_events\n");
RgnHandle cursorRgn = NULL;
Boolean gotEvent=TRUE;
EventRecord event;
while (gotEvent) {
gotEvent = WaitNextEvent(everyEvent, &event, 0L, cursorRgn);
}
}
Start by reading the How Cocoa Applications Handle Apple Events portion of the Cocoa Scripting Guide. Then read the rest of it and ask a more focused question.

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