2010-06-28 57 views
12

我需要用Cocoa編寫一些東西來表面原始鼠標移動數據。最理想的情況是,應用程序只是一個可以運行的小守護程序,將數據傳遞給另一個應用程序可以使用的套接字服務器來訪問事件。鼠標跟蹤守護進程

任何人都可以在方向和工具方面指出正確的方向嗎?我甚至不知道現在該從哪裏開始。

回答

18

其他簡單的方式做,這是add a global event monitor(10.6只,不過):

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

然後,當你做跟蹤,你這樣做:

[NSEvent removeMonitor:eventHandler]; 
+0

但只適用於10.6,而我的代碼甚至可以使用10.4以上:-P – Mecki 2010-06-28 19:48:25

+1

@Mecki yep,這是唯一的警告。如果海報可以專門定位10.6,那麼全球顯示器肯定更容易。否則,他需要一個事件點擊。 – 2010-06-28 19:49:17

+0

感謝您的信息。我試着將Dave的代碼添加到我的應用程序中,但是我得到一個編譯錯誤,說NSEvent可能不會響應mouseLocation,並且NSStringFromPoint傳遞了一個無效參數。 (NSEvent * mouseEvent)之前的插入符號是什麼?這是問題嗎?我不熟悉這種語法... 鑑於我不能嘗試這個,讓我問,這會給我的鼠標數據?基本上,即使鼠標固定在左上角,我也需要知道用戶繼續向上移動並離開,就是這樣。這會實現嗎? 再次感謝! – Raconteur 2010-06-28 21:09:44

13

寫一個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和我在後面的示例中做的一樣,只是包裝成靜態方法。 Dave的代碼只能在10.6上運行,不過,上面的代碼在10.4,10.5和10.6中運行。

+0

嗨Mecki,感謝這個。我使用的是10.6,因此,Dave描述的單線方法是更理想的解決方案。感謝您的意見! – Raconteur 2010-06-28 22:26:44

+0

將'self'作爲'refcon'參數傳入,以便在觸發時可以調用ObjC類中的方法。請參閱http://stackoverflow.com/a/14985036/130910 – vaughan 2014-03-03 06:17:01

+1

調用'[NSEvent eventWithCGEvent:event]'得到一個更好的API用於處理ObjC代碼的'NSEvent'。在ARC你需要橋接。例如:'[(__bridge MyClass *)refcon myHandlerInstanceMethod:e];' – vaughan 2014-03-03 06:19:32