I need to write something using Cocoa to surface raw mouse movement data. Optimally, the app would just be a little daemon that would run, passing the data to a socket server which another application could tap into to gain access to the events.
Can anyone point me in the right direction with regard to approach and tools? I am not even sure where to begin with this right now.
The other simple way to do this is to add a global event monitor (10.6 only, however):
id eventHandler = [NSEvent addGlobalMonitorForEventsMatchingMask:NSMouseMovedMask handler:^(NSEvent * mouseEvent) {
NSLog(#"Mouse moved: %#", NSStringFromPoint([mouseEvent locationInWindow]));
}];
Then when you're done tracking, you do:
[NSEvent removeMonitor:eventHandler];
Write an EventTap. Documentation can be found here.
In MacOS X every event (e.g. every keyboard key pressed, every mouse key pressed or mouse movement) creates an event that travels the following path:
Driver (Kernel) -> Window Server (privileged) -> User (Login) Session -> Active Application
Everywhere where I wrote an arrow (->) an EventTap can be placed to either only look at the event (a listen only EventTap) or to either modify or drop the event (an event filtering EventTap). Please note that to catch an event between Driver and WindowServer, your daemon must run with root privileges.
Here is some sample code:
// Compile with:
// gcc -framework ApplicationServices -o MouseWatcher MouseWatcher.c
//
// Start with:
// ./MouseWatcher
//
// Terminate by hitting CTRL+C
#include <ApplicationServices/ApplicationServices.h>
static CGEventRef myEventTapCallback (
CGEventTapProxy proxy,
CGEventType type,
CGEventRef event,
void * refcon
) {
CGPoint mouseLocation;
// If we would get different kind of events, we can distinguish them
// by the variable "type", but we know we only get mouse moved events
mouseLocation = CGEventGetLocation(event);
printf(
"Mouse is at x/y: %ld/%ld\n",
(long)mouseLocation.x,
(long)mouseLocation.y
);
// Pass on the event, we must not modify it anyway, we are a listener
return event;
}
int main (
int argc,
char ** argv
) {
CGEventMask emask;
CFMachPortRef myEventTap;
CFRunLoopSourceRef eventTapRLSrc;
// We only want one kind of event at the moment: The mouse has moved
emask = CGEventMaskBit(kCGEventMouseMoved);
// Create the Tap
myEventTap = CGEventTapCreate (
kCGSessionEventTap, // Catch all events for current user session
kCGTailAppendEventTap, // Append to end of EventTap list
kCGEventTapOptionListenOnly, // We only listen, we don't modify
emask,
&myEventTapCallback,
NULL // We need no extra data in the callback
);
// Create a RunLoop Source for it
eventTapRLSrc = CFMachPortCreateRunLoopSource(
kCFAllocatorDefault,
myEventTap,
0
);
// Add the source to the current RunLoop
CFRunLoopAddSource(
CFRunLoopGetCurrent(),
eventTapRLSrc,
kCFRunLoopDefaultMode
);
// Keep the RunLoop running forever
CFRunLoopRun();
// Not reached (RunLoop above never stops running)
return 0;
}
The answer from Dave is the nicer Cocoa way of doing pretty much the same thing; basically Cocoa does the same as I do in my sample above behind the scenes, just wrapped into a static method. The code of Dave works only on 10.6, though, the above works in 10.4, 10.5 and 10.6.
Related
What I want:
I have a program running. When the program is in the tray and out of focus, I want to have a couple of global shortcuts set up to send messages to the program. What do I mean by "send messages"? Well, inside my program, all I want is to have an access to some flag, which would indicate the state the specified key-pair (fired or not). I would poll the flag in the loop and take a decision from there.
What I found:
System-wide hotkey for an application
system wide shortcut for Mac OS X
What I do not understand:
From the links above it looks like I have to pass a handler when registering a hotkey. On a hotkey press, OS calls the handler. It that right? What I do not understand is how in the world the system would call a handler inside my program if my program is running.
I think your main problem is that you do not understand how Mac programming was done in the days before objective C and Cocoa became the norm. Before that, most programming was done in C (or C++) using Carbon. This name was used for a library that was supposed to be a "carbon" copy of a more modern set of APIs during the transition between Mac OS (Classic) and Mac OS X.
Another thing you have to understand is that the registration of hotkeys as given in the examples you give above must be paired by a registration of a Carbon Event handler that will be invoked when you hit that hotkey combination.
That said, I think you should read this legacy document about the Carbon Event Manager:
https://developer.apple.com/legacy/library/documentation/Carbon/Conceptual/Carbon_Event_Manager/CarbonEvents.pdf
And pay particular attention to how Carbon Events are supposed to be registered. I particularly use:
OSStatus InstallEventHandler(EventTargetRef target,
EventHandlerUPP handlerProc,
UInt32 numTypes,
const EventTypeSpec* typeList,
void* userData,
EventHandlerRef* handlerRef);
The way I use it is that I made an objective C wrapper in which I basically do the following:
This is a part of a class, let's call it MyOwnEventHandler:
- (EventHandlerRef)handlerRef {
if ( handlerRef == nil ) {
NSAssert( InstallEventHandler(GetApplicationEventTarget(),
&EventHandler,
0,
nil,
self,
&handlerRef ) == noErr, #"handlerRef" );
}
return handlerRef;
}
// this is a Carbon callback that the OS invokes when your app gets
// a hotkey event that must be handled by you
OSStatus EventHandler( EventHandlerCallRef inHandler,
EventRef inEvent,
void* inUserData )
{
EventHotKeyID hotKeyID;
GetEventParameter( inEvent,
kEventParamDirectObject,
typeEventHotKeyID,
nil,
sizeof(EventHotKeyID),
nil,
&hotKeyID );
// use this to get your MyOwnEventHandler object back if need be
// the reason why we get this is because we passed self in InstallEventHandler
// in Carbon event callbacks you cannot access self directly
// because this is a C callback, not an objective C method
MyOwnEventHandler* handler = (MyOwnEventHandler *)inUserData;
// handle the hotkey here - I usually store the id of the EventHotKeyID struct
// in a objective C hotkey object to look up events in an array of registered hotkeys
return eventNotHandledErr; // return this error for other handlers to handle this event as well
}
// call this objective C wrapper method to register your Carbon Event handler
- (void)registerForGettingHotKeyEvents {
const EventTypeSpec kHotKeysEvent[] = {{ kEventClassKeyboard, kEventHotKeyPressed }};
AddEventTypesToHandler( [self handlerRef], GetEventTypeCount(kHotKeysEvent), kHotKeysEvent );
}
// call this objective C wrapper method to unregister your Carbon Event handler
- (void)unregisterFromGettingHotKeyEvents {
const EventTypeSpec kHotKeysEvent[] = {{ kEventClassKeyboard, kEventHotKeyPressed }};
RemoveEventTypesFromHandler( [self handlerRef], GetEventTypeCount(kHotKeysEvent), kHotKeysEvent );
}
I hope this helps. If you are stuck somewhere let me know and I will try to help you.
I've pored through most of the posts regarding the creation of Global Hotkeys using Carbon. Is it possible in the hot key handler function to return eventNotHandledErr and have the event passed on to the next handler? Here's some pseudocode:
OSStatus myHotKeyHandler(EventHandlerCallRef nextHandler, EventRef anEvent, void *userData)
{
OSStatus result;
if ( appX is running || appY is running ) {
[(MyAppController *) userData doSomething];
result = noErr;
} else {
result = eventNotHandledErr;
}
return result;
}
In the event that I'm not in application X or Y, I want to be able to pass the event on. Is this possible?
I know I can set up a notification for application switched events, but that requires Enable access for assistive devices to be turned on. If there's a way to pass the event to the next handler, that would be great.
We are running on a Windows Client Platform (generally WinXP) in niche industry program that runs in a 640x480 window back to an AS/400 server. To reduce errors I want to watch for when the title bar of the program changes. Then I need to capture the keyboard entries to validate. I'll then make sure each of the entries is valid since the archaic program does no validation. I'll can then do a pop-up then warning the end-user if errors occur and to reduce/eliminate the exception reports.
My question is how can I capture the event of the application title bar change = 'string' that I need? API call? Aiming to do this in VB unless another would be notable cleaner.
WinEvents should work well here. These are lightweight events that get fired when certain UI changes take place - eg names of objects change - which includes Titlebar text changes. One benefit to this type of hook is that you can set it up to deliver the notifications back to your own process, so you don't need to deal with hooking or IPC. (It also works against both 32-bit and 64-bit processes.)
This is easiest to do in plain C/C++; but can be done in .Net (VB, C#) if you add the appropriate [DllImport]'s.
#include <windows.h>
#include <stdio.h>
#define WM_NAMECHANGED WM_APP
HWND g_hwndTarget; // window we're listening to
void CALLBACK WinEventProc(
HWINEVENTHOOK hWinEventHook,
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwEventThread,
DWORD dwmsEventTime
)
{
// Check this is the window we want. Titlebar name changes result in these
// two values (obtained by looking at some titlebar changes with the
// Accessible Event Watcher tool in the Windows SDK)
if(hwnd == g_hwndTarget && idObject == OBJID_WINDOW && idChild == CHILDID_SELF)
{
// Do minimal work here, just hand off event to mainline.
// If you do anything here that has a message loop - eg display a dialog or
// messagebox, you can get reentrancy.
PostThreadMessage(GetCurrentThreadId(), WM_NAMECHANGED, 0, 0);
}
return;
}
void ReportName(HWND hwnd)
{
WCHAR szName[128];
GetWindowText(hwnd, szName, ARRAYSIZE(szName));
wprintf(L"hwnd 0x%08lx has title: %s\n", HandleToLong(hwnd), szName);
}
int main()
{
wprintf(L"Place mouse pointer over window titlebar to report name changes for and hit return...\n");
getchar();
POINT pt;
GetCursorPos(&pt);
g_hwndTarget = WindowFromPoint(pt);
ReportName(g_hwndTarget);
// Note: this doesn't work for console windows, which are managed by CSRSS.EXE. Simplest (though not efficient) workaround for those
// is to use threadId=0 and filter by hwnd in the callback.
DWORD threadId = GetWindowThreadProcessId(g_hwndTarget, NULL);
// This says: call the callback when any UI elements in the specified thread change
// name. _OUTOFCONTEXT means deliver the notifications in this process, don't hook.
HWINEVENTHOOK hook = SetWinEventHook(EVENT_OBJECT_NAMECHANGE, EVENT_OBJECT_NAMECHANGE, NULL, WinEventProc, 0, threadId, WINEVENT_OUTOFCONTEXT);
// TODO: add error checking as appropriate.
wprintf(L"Waiting...\n");
// Thread needs to have a message loop for SetWinEventHook to work for out-of-context messages.
UINT count = 10;
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message == WM_NAMECHANGED)
{
ReportName(g_hwndTarget);
if(--count == 0)
{
break;
}
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnhookWinEvent(hook);
return 0;
}
Things to watch for: you might get false-positives; and if the name changes rapidly, by the time you get the first event, the name may be at the second value, so you may appear to see two events for the second value. Neither of these should be an issue if you're just using this as a trigger to check for a specified value, however.
I am assuming that you do not own the code for the target application. In this case, there's no easy "call me back when the title changes" event. You then have 2 options to do what you need, which I will outline below.
Easy but not airtight
Have your application get the main window of the target application (this should be easy enough) and poll its title every 100msec or so. When you detect a change, act accordingly.
Difficult but correct
Hook into the target application using e.g. a global CBT hook. Once your code runs in their process subclass their main window, causing all window messages to go through your code first. When your code sees a WM_SETTEXT message going to the main window, you can actively notify your "other" application on the spot using your choice of IPC. If all you need to do is just shout "hey!" to your other application, do so with an auto-reset event (it will be easiest). Of course all this points heavily to unmanaged code.
If the easy solution is not good enough and the difficult one is too much, you can try using an automation library like White (I 've never used it, so I can't really say more than that).
My cocoa app runs background tasks, which I would like to stop when the user becomes idle (no keyboard/mouse input) and then resume when the user becomes active again. Is there a way to register for idle-state notifications?
In case you can't link to Carbon (ie. you want to compile x86_64 bit binary) you can wrap this function (which returns current idle time in seconds resolution as double - CFTimeInterval) in a timer:
#include <IOKit/IOKitLib.h>
CFTimeInterval CFDateGetIdleTimeInterval() {
mach_port_t port;
io_iterator_t iter;
CFTypeRef value = kCFNull;
uint64_t idle = 0;
CFMutableDictionaryRef properties = NULL;
io_registry_entry_t entry;
IOMasterPort(MACH_PORT_NULL, &port);
IOServiceGetMatchingServices(port, IOServiceMatching("IOHIDSystem"), &iter);
if (iter) {
if ((entry = IOIteratorNext(iter))) {
if (IORegistryEntryCreateCFProperties(entry, &properties, kCFAllocatorDefault, 0) == KERN_SUCCESS && properties) {
if (CFDictionaryGetValueIfPresent(properties, CFSTR("HIDIdleTime"), &value)) {
if (CFGetTypeID(value) == CFDataGetTypeID()) {
CFDataGetBytes(value, CFRangeMake(0, sizeof(idle)), (UInt8 *) &idle);
} else if (CFGetTypeID(value) == CFNumberGetTypeID()) {
CFNumberGetValue(value, kCFNumberSInt64Type, &idle);
}
}
CFRelease(properties);
}
IOObjectRelease(entry);
}
IOObjectRelease(iter);
}
return idle / 1000000000.0;
}
You'll need to link your code to IOKit.framework
There's a Carbon API that will send a notification when there hasn't been a user event after a certain duration called EventLoopIdleTimer. Uli Kusterer has written a Cocoa wrapper for here (look for UKIdleTimer).
If you want something lower level, you may be able to implement the behavior you want with a combination of timers and the CoreGraphics function CGEventSourceSecondsSinceLastEventType (available in <CoreGraphics/CGEventSource.h>).
Apple's Technical Q&A QA1340 Registering and unregistering for sleep and wake notifications may be what you are looking for.
If you need more control than NSWorkspaceWillSleepNotification (Listing 1), use I/O Kit and register to receive power notifications (Listing 3).
I used a different approach.
Subclassing UIApplication I override the sendEvent method filtering touches (actually you can filter any kind of event, acceleration, touches, etc.).
Using a shared variable and a background timer I managed the "idle".
Every time the user touch the screen the variable is set with current timeInterval (current time).
The timer fire method checks for the elapsed time since last touch, if greater than the threshold (in my case was around 90seconds) you can POST your own notification.
I used this simple approach to create a custom set of apps that after some idle time automatically call the "screensaver" app.
Nothing clever, it just do the job.
Hope that helps.
So, I've wasted a bunch of time creating this really cool keyboard macro application. It works great, the only problem is that after a couple minutes, it just stops working. It ceases to get called when I press a key.
I haven't been able to lock it down, but it always takes at least 30 seconds to happen. Usually it won't happen for several minutes. I'll have intercepted and sent out many events by then. The application is still running when it happens.
Here's an example of what I'm doing to listen
-(void)listen {
CFMachPortRef downEventTap = CGEventTapCreate(kCGHIDEventTap,kCGHeadInsertEventTap,kCGEventTapOptionDefault,CGEventMaskBit(kCGEventKeyDown),&onKeyDown,self);
downSourceRef = CFMachPortCreateRunLoopSource(NULL, downEventTap, 0);
CFRelease(downEventTap);
CFRunLoopAddSource(CFRunLoopGetCurrent(), downSourceRef, kCFRunLoopDefaultMode);
CFRelease(downSourceRef)
}
And the handler -
CGEventRef onKeyDown(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
NSLog(#"DOWN (%i)", CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode));
// When it matches, I return CGEventCreate(NULL) to stop the event
return event;
}
Also note that when I intercept an event (and return that CGEventCreate(NULL)), I usually issue one or more key presses of my own, using the following code. Note that KeyCmd, etc, are just shortcuts to the normal constants.
- (void)sendKey:(KeyCode)code cmd:(BOOL)cmd alt:(BOOL)alt ctl:(BOOL)ctl shift:(BOOL)shift {
CGEventFlags flags = 0;
if (cmd) flags = flags | KeyCmd;
if (alt) flags = flags | KeyAlt;
if (ctl) flags = flags | KeyCtl;
if (shift) flags = flags | KeyShift;
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState);
CGEventRef keyDownPress = CGEventCreateKeyboardEvent(source, (CGKeyCode)code, YES);
CGEventSetFlags(keyDownPress, flags);
CGEventPost(kCGAnnotatedSessionEventTap, keyDownPress);
CFRelease(keyDownPress);
CFRelease(source);
}
Thanks!
There is a bug in Snow Leopard, I think, that stops your listener if something times out.
In your keyDown handler, check for the following type, and just re-enable the listener.
if (type == kCGEventTapDisabledByTimeout) {
NSLog(#"Event Taps Disabled! Re-enabling");
CGEventTapEnable(eventTap, true);
return event;
}