我需要使用Cocoa编写一些东西来表示原始鼠标移动数据。最理想的情况是,应用程序只是一个可以运行的小守护进程,将数据传递给另一个应用程序可以访问的套接字服务器以获取对事件的访问权。
有人能指出我在方法和工具方面的正确方向吗?我现在还不确定从哪里开始。
答案 0 :(得分:19)
另一种简单的方法是add a global event monitor(仅限10.6):
id eventHandler = [NSEvent addGlobalMonitorForEventsMatchingMask:NSMouseMovedMask handler:^(NSEvent * mouseEvent) {
NSLog(@"Mouse moved: %@", NSStringFromPoint([mouseEvent locationInWindow]));
}];
然后当你完成跟踪时,你会这样做:
[NSEvent removeMonitor:eventHandler];
答案 1 :(得分:15)
写一个EventTap。文档可以是found here。
在MacOS X中,每个事件(例如,按下每个键盘键,按下每个鼠标键或鼠标移动)都会创建一个沿着以下路径传播的事件:
Driver (Kernel) -> Window Server (privileged) -> User (Login) Session -> Active Application
在我写一个箭头(->
)的地方,可以放置一个EventTap来查看事件(只监听EventTap)或修改或删除事件(事件过滤EventTap)。请注意,要捕获Driver和WindowServer之间的事件,您的守护程序必须以root权限运行。
以下是一些示例代码:
// 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;
}
Dave的答案是更好的Cocoa方式做同样的事情;基本上Cocoa和我在幕后的示例中做的一样,只是包装成静态方法。 Dave的代码仅适用于10.6,但上述代码适用于10.4,10.5和10.6。