2010-06-28 4 views
12

Ich muss etwas schreiben, das Cocoa verwendet, um rohe Mausbewegungsdaten zu belichten. Im Idealfall wäre die App nur ein kleiner Daemon, der die Daten an einen Socket-Server weiterleitet, den eine andere Anwendung für den Zugriff auf die Ereignisse nutzen könnte.Mausverfolgungsdämon

Kann mir jemand in Bezug auf Ansatz und Werkzeuge in die richtige Richtung weisen? Ich bin mir nicht einmal sicher, wo ich gerade damit anfangen soll.

Antwort

18

Die andere einfache Möglichkeit, dies zu add a global event monitor (10.6 jedoch nur) zu tun ist:

id eventHandler = [NSEvent addGlobalMonitorForEventsMatchingMask:NSMouseMovedMask handler:^(NSEvent * mouseEvent) { 
    NSLog(@"Mouse moved: %@", NSStringFromPoint([mouseEvent locationInWindow])); 
}]; 

Dann, wenn Sie, getan Tracking Sie tun:

[NSEvent removeMonitor:eventHandler]; 
+0

wurde verbessert Aber funktioniert nur mit 10.6, während mein Code sogar mit 10.4 und aufwärts funktioniert :-P – Mecki

+1

@Mecki yep, das ist der einzige Vorbehalt. Wenn das Poster ausschließlich auf 10.6 zielen kann, ist ein globaler Monitor definitiv einfacher. Sonst braucht er einen Event Tap. –

+0

Danke für die Info. Ich habe versucht, Daves Code zu meiner App hinzuzufügen, aber ich bekomme einen Kompilierungsfehler, der besagt, dass NSEvent möglicherweise nicht auf mouseLocation reagiert und dass NSStringFromPoint ein ungültiges Argument übergeben wird. Was ist das Caret für vor dem (NSEvent * mouseEvent)? Ist das das Problem? Ich bin nicht vertraut mit dieser Syntax ... Angesichts, dass ich dies nicht versuchen kann, lassen Sie mich fragen, wird dies mir die rohen Mausdaten geben? Grundsätzlich, auch wenn die Maus an die obere linke Ecke gebunden ist, muss ich wissen, dass der Benutzer es weiter nach oben und links bewegt hat, sei das der Fall. Wird dies erreicht? Danke nochmal! – Raconteur

13

Schreiben Sie eine EventTap. Die Dokumentation kann found here sein.

In MacOS X jedes Ereignis (zB jede Tastatur-Taste gedrückt, jede Maustaste gedrückt oder Mausbewegung) erzeugt ein Ereignis, das den folgenden Pfad bewegt:

Driver (Kernel) -> Window Server (privileged) -> User (Login) Session -> Active Application 

Überall, wo ich einen Pfeil schrieb (->) ein EventTap kann platziert werden, um entweder nur das Ereignis zu betrachten (nur ListenTick) oder das Ereignis zu modifizieren oder zu löschen (EventTap). Beachten Sie, dass zum Abfangen eines Ereignisses zwischen Driver und WindowServer Ihr Daemon mit Root-Rechten ausgeführt werden muss. Hier

ist ein Beispielcode:

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

Die Antwort von Dave ist die schönere Cocoa Art und Weise so ziemlich das Gleiche zu tun; im Grunde macht Cocoa das selbe wie ich in meinem Beispiel oben hinter den Kulissen, nur eingewickelt in eine statische Methode. Der Code von Dave funktioniert nur auf 10.6, obwohl das oben in 10.4, 10.5 und 10.6 funktioniert.

+0

Hallo Mecki, danke für Dies. Ich benutze 10,6 und daher ist die von Dave beschriebene Einzeilenmethode eine wünschenswerte Lösung. Schätzen Sie Ihre Eingabe! – Raconteur

+0

Übergeben Sie "self" als das Argument "refcon", um eine Methode für Ihre ObjC-Klasse aufrufen zu können, wenn der Tap ausgelöst wird. Siehe http://stackoverflow.com/a/14985036/130910 – vaughan

+1

Rufen Sie '[NSEvent eventWithCGEvent: event]' auf, um ein 'NSEvent' mit einer netteren API zum Arbeiten mit ObjC-Code zu erhalten. In ARC müssen Sie überbrücken. Beispiel: '[(__bridge MyClass *) refcon myHandlerInstanceMethod: e];' – vaughan