2010-06-28 12 views
12

J'ai besoin d'écrire quelque chose en utilisant Cocoa pour faire la surface des données brutes de mouvement de la souris. De manière optimale, l'application serait juste un petit démon qui fonctionnerait en transmettant les données à un serveur socket qu'une autre application pourrait utiliser pour accéder aux événements.Démon de suivi de souris

Quelqu'un peut-il me diriger dans la bonne direction en ce qui concerne l'approche et les outils? Je ne suis même pas sûr de savoir par où commencer.

Répondre

18

L'autre façon simple de le faire est de add a global event monitor (10,6 seulement, cependant):

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

Ensuite, lorsque vous avez terminé le suivi, vous faites:

[NSEvent removeMonitor:eventHandler]; 
+0

Mais fonctionne seulement avec 10,6, alors que mon code fonctionne même avec 10,4 et plus :-P – Mecki

+1

@Mecki oui, c'est la seule mise en garde. Si l'affiche peut cibler 10.6 exclusivement, alors un moniteur global est définitivement plus facile. Sinon, il aura besoin d'un robinet d'événement. –

+0

Merci pour l'info. J'ai essayé d'ajouter le code de Dave à mon application, mais j'obtiens une erreur de compilation indiquant que NSEvent peut ne pas répondre à mouseLocation et que NSStringFromPoint reçoit un argument non valide. Quel est le signe avant le (NSEvent * mouseEvent) ?? Est-ce le problème? Je ne suis pas familier avec cette syntaxe ... Étant donné que je ne peux pas essayer cela, laissez-moi demander, cela me donnera-t-il les données brutes de la souris? Fondamentalement, même si la souris est ancrée dans le coin supérieur gauche, j'ai besoin de savoir que l'utilisateur a continué à le déplacer vers le haut et à gauche, que ce soit le cas. Est-ce que cela va accomplir cela? Merci encore! – Raconteur

13

Écrire un EventTap. La documentation peut être found here.

Dans Mac OS X chaque événement (par exemple, chaque touche du clavier enfoncée, toutes les touches de la souris mouvement pressé ou la souris) crée un événement qui parcourt le chemin suivant:

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

Partout où j'ai écrit une flèche (->) un EventTap peut être placé pour regarder uniquement l'événement (une seule EventTap d'écoute) ou pour modifier ou supprimer l'événement (un EventTap de filtrage d'événements). S'il vous plaît noter que pour attraper un événement entre Driver et WindowServer, votre démon doit fonctionner avec les privilèges root.

Voici quelques exemples de 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; 
} 

La réponse de Dave est la plus belle façon de cacao de faire à peu près la même chose; Fondamentalement Cocoa fait la même chose que je fais dans mon exemple ci-dessus dans les coulisses, juste enveloppé dans une méthode statique. Le code de Dave ne fonctionne que sur 10.6, cependant, ce qui précède fonctionne dans 10.4, 10.5 et 10.6.

+0

Salut, merci pour Mecki ce. J'utilise 10.6, et en tant que tel, la méthode de ligne unique décrite par Dave est une solution plus souhaitable. Appréciez votre contribution! – Raconteur

+0

Passez 'self' comme argument' refcon' pour pouvoir appeler une méthode sur votre classe ObjC lorsque le tap est déclenché. Voir http://stackoverflow.com/a/14985036/130910 – vaughan

+1

Appelez '[NSEvent eventWithCGEvent: event]' pour obtenir un 'NSEvent' avec une API plus agréable pour travailler avec le code ObjC. Dans l'ARC, vous devez faire le pont. Exemple: '[(__bridge MyClass *) refcon myHandlerInstanceMethod: e];' – vaughan

Questions connexes