2013-10-07 50 views
2

我在想如何編寫代碼來監視全局鼠標按鈕。這將用於OS X,我想嘗試在Qt/C++中編寫它。如何檢測全局鼠標按鈕事件

首先,我不知道如何捕捉這些全局事件。監視器應用程序不會顯示GUI,它只是一個在後臺運行並檢測被點擊的鼠標按鈕的進程。

在程序的第二部分,我想根據按下的鼠標鍵啓動熱鍵。

我的最終想法是製作一個像steerMouse這樣的免費程序,只是爲了弄清楚它是如何完成的。

我在尋求從哪裏開始的指導 - 我如何檢測全局鼠標按鈕事件?

+0

感謝庫巴爲我糾正了原來的問題,對不起我的英語,仍在學習。 – Angoll

回答

6

僅僅使用Qt是不可能的。有詳細問題的another question。它歸結爲:

  1. QApplication安裝一個事件過濾器會讓你收到鼠標事件,同時光標在任何應用程序窗口,但不應超出它。這對你的情況沒有幫助。

  2. 如果小部件使用grabMouse()抓取鼠標,它將全局接收所有鼠標事件,但與其他應用程序的交互變得不可能。

所以,你需要求助於使用平臺特定的API來做到這一點 - 這意味着Cocoa和Objective C/C++編寫。有一個question與優秀的答案,提供幾乎所有我們需要的,但Qt集成。

下面顯示的缺失部分正在將獨立代碼與Qt集成在一起。這段代碼顯示了一個空的小部件,只是爲了證明我們正確地處理了我們的應用程序和它之外的鼠標事件。

這是一個完整的工作示例,使用Cocoa。它需要進入.mm文件;不要忘記將它添加到您的qmake項目文件中的OBJECTIVE_SOURCES而不是SOURCES!)。

不幸的是,沒有一個函數/方法會從NSEvent轉換爲QMouseEvent。最好的可以做的是複製&粘貼一些代碼qnsview.mm。這很不幸,但是Qt平臺抽象設計的結果:平臺代碼最終致電QWindowSystemInterface::handleMouseEvent(....)將事件發佈到應用程序。

#include <QApplication> 
#include <QAbstractNativeEventFilter> 
#include <QTextStream> 
#include <QWidget> 
#include <cstdio> 
#import <AppKit/AppKit.h> 

QTextStream out(stdout); 

class MyEventFilter : public QAbstractNativeEventFilter { 
public: 
    bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) { 
     Q_UNUSED(eventType) Q_UNUSED(result) 
     NSEvent * event = (NSEvent*)message; 
     switch ([event type]) { 
     case NSLeftMouseDown: 
      out << "Lv"; break; 
     case NSLeftMouseUp: 
      out << "L^"; break; 
     case NSRightMouseDown: 
      out << "Rv"; break; 
     case NSRightMouseUp: 
      out << "R^"; break; 
     case NSOtherMouseDown: 
      out << [event buttonNumber] << "v"; break; 
     case NSOtherMouseUp: 
      out << [event buttonNumber] << "^"; break; 
     default: 
      return false; 
     } 
     out << endl; 
     return false; 
    } 
}; 

int main(int argc, char *argv[]) 
{ 
    QApplication a(argc, argv); 
    QSharedPointer<QAbstractNativeEventFilter> filter(new MyEventFilter); 
    const int mask = 
      NSLeftMouseDownMask | NSLeftMouseUpMask | 
      NSRightMouseDownMask | NSRightMouseUpMask | 
      NSOtherMouseDownMask | NSOtherMouseUpMask; 
    // The global monitoring handler is *not* called for events sent to our application 
    id monitorId = [NSEvent addGlobalMonitorForEventsMatchingMask:mask handler:^(NSEvent* event) { 
     filter->nativeEventFilter("NSEvent", event, 0); 
    }]; 
    // We also need to handle events coming to our application 
    a.installNativeEventFilter(filter.data()); 
    QWidget w; 
    w.show(); 
    int rc = a.exec(); 
    [NSEvent removeMonitor:monitorId]; 
    return rc; 
} 
+0

謝謝庫巴,作爲答案。現在我更瞭解這些事件是如何在MacOs中發揮作用的,並且我有我想做的早期版本:) – Angoll

1

聽起來像是你要勾上OSX全球鼠標事件。

我已經在Windows中完成了,取得了巨大的成功。我知道要尋找什麼。

這裏是一個快速搜索後,我可以在它身上找到最好的東西:

https://code.google.com/p/jnativehook/

https://code.google.com/p/jnativehook/source/browse/branches/1.1/src/native/osx/NativeThread.c

基本上JNativeHook執行以下操作:

它創建AC線與正確回調處理鼠標的系統功能。當鼠標(和鍵盤)由系統處理時,回調會獲取信息。然後通過回調將信息轉發到代碼的Java端。

您需要創建一個線程,將其正確掛接到系統,然後將信息輸出到要記錄或顯示的位置。超過90%的工作是在上面的NativeThread.c鏈接中完成的。這裏有一些關鍵部分。

線305至552有以下幾種:

switch (type) { 
//... 
case kCGEventLeftMouseDown: 
     button = kVK_LBUTTON; 
     SetModifierMask(kCGEventFlagMaskButtonLeft); 
     goto BUTTONDOWN; 

case kCGEventRightMouseDown: 
     button = kVK_RBUTTON; 
     SetModifierMask(kCGEventFlagMaskButtonRight); 
     goto BUTTONDOWN; 

case kCGEventOtherMouseDown: 
     button = CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber); 

     if (button == kVK_MBUTTON) { 
       SetModifierMask(kCGEventFlagMaskButtonCenter); 
     } 
     else if (button == kVK_XBUTTON1) { 
       SetModifierMask(kCGEventFlagMaskXButton1); 
     } 
     else if (button == kVK_XBUTTON2) { 
       SetModifierMask(kCGEventFlagMaskXButton2); 
     } 
BUTTONDOWN: 
     #ifdef DEBUG 
     fprintf(stdout, "LowLevelProc(): Button Pressed (%i)\n", (unsigned int) button); 
     #endif 

     // Track the number of clicks. 
     #ifdef DEBUG 
     fprintf(stdout, "LowLevelProc(): Click Time (%lli)\n", (CGEventGetTimestamp(event) - click_time)/1000000); 
     #endif 

     if ((long) (CGEventGetTimestamp(event) - click_time)/1000000 <= GetMultiClickTime()) { 
       click_count++; 
     } 
     else { 
       click_count = 1; 
     } 
     click_time = CGEventGetTimestamp(event); 

     event_point = CGEventGetLocation(event); 
     jbutton = NativeToJButton(button); 
     jmodifiers = NativeToJEventMask(GetModifiers()); 

     // Fire mouse pressed event. 
     objMouseEvent = (*env)->NewObject(
                   env, 
                   clsMouseEvent, 
                   idMouseButtonEvent, 
                   org_jnativehook_mouse_NativeMouseEvent_NATIVE_MOUSE_PRESSED, 
                   (jlong) event_time, 
                   jmodifiers, 
                   (jint) event_point.x, 
                   (jint) event_point.y, 
                   (jint) click_count, 
                   jbutton); 
     (*env)->CallVoidMethod(env, objGlobalScreen, idDispatchEvent, objMouseEvent); 
     (*env)->DeleteLocalRef(env, objMouseEvent); 
     break; 

case kCGEventLeftMouseUp: 
     button = kVK_LBUTTON; 
     UnsetModifierMask(kCGEventFlagMaskButtonLeft); 
     goto BUTTONUP; 

case kCGEventRightMouseUp: 
     button = kVK_RBUTTON; 
     UnsetModifierMask(kCGEventFlagMaskButtonRight); 
     goto BUTTONUP; 

case kCGEventOtherMouseUp: 
     button = CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber); 

     if (button == kVK_MBUTTON) { 
       UnsetModifierMask(kCGEventFlagMaskButtonCenter); 
     } 
     else if (button == kVK_XBUTTON1) { 
       UnsetModifierMask(kCGEventFlagMaskXButton1); 
     } 
     else if (button == kVK_XBUTTON2) { 
       UnsetModifierMask(kCGEventFlagMaskXButton2); 
     } 

BUTTONUP: 
     #ifdef DEBUG 
     fprintf(stdout, "LowLevelProc(): Button Released (%i)\n", (unsigned int) button); 
     #endif 

     event_point = CGEventGetLocation(event); 
     jbutton = NativeToJButton(button); 
     jmodifiers = NativeToJEventMask(GetModifiers()); 

     // Fire mouse released event. 
     objMouseEvent = (*env)->NewObject(
                   env, 
                   clsMouseEvent, 
                   idMouseButtonEvent, 
                   org_jnativehook_mouse_NativeMouseEvent_NATIVE_MOUSE_RELEASED, 
                   (jlong) event_time, 
                   jmodifiers, 
                   (jint) event_point.x, 
                   (jint) event_point.y, 
                   (jint) click_count, 
                   jbutton); 
     (*env)->CallVoidMethod(env, objGlobalScreen, idDispatchEvent, objMouseEvent); 
     (*env)->DeleteLocalRef(env, objMouseEvent); 

     if (mouse_dragged != true) { 
       // Fire mouse clicked event. 
       objMouseEvent = (*env)->NewObject(
                     env, 
                     clsMouseEvent, 
                     idMouseButtonEvent, 
                     org_jnativehook_mouse_NativeMouseEvent_NATIVE_MOUSE_CLICKED, 
                     (jlong) event_time, 
                     jmodifiers, 
                     (jint) event_point.x, 
                     (jint) event_point.y, 
                     (jint) click_count, 
                     jbutton); 
       (*env)->CallVoidMethod(env, objGlobalScreen, idDispatchEvent, objMouseEvent); 
       (*env)->DeleteLocalRef(env, objMouseEvent); 
     } 
     break; 


case kCGEventLeftMouseDragged: 
case kCGEventRightMouseDragged: 
case kCGEventOtherMouseDragged: 
     event_point = CGEventGetLocation(event); 

     #ifdef DEBUG 
     fprintf(stdout, "LowLevelProc(): Motion Notified (%f, %f)\n", event_point.x, event_point.y); 
     #endif 

     // Reset the click count. 
     if (click_count != 0 && (long) (CGEventGetTimestamp(event) - click_time)/1000000 > GetMultiClickTime()) { 
       click_count = 0; 
     } 
     jmodifiers = NativeToJEventMask(GetModifiers()); 

     // Set the mouse dragged flag. 
     mouse_dragged = true; 

     // Fire mouse dragged event. 
     objMouseEvent = (*env)->NewObject(
                   env, 
                   clsMouseEvent, 
                   idMouseMotionEvent, 
                   org_jnativehook_mouse_NativeMouseEvent_NATIVE_MOUSE_DRAGGED, 
                   (jlong) event_time, 
                   jmodifiers, 
                   (jint) event_point.x, 
                   (jint) event_point.y, 
                   (jint) click_count); 
     (*env)->CallVoidMethod(env, objGlobalScreen, idDispatchEvent, objMouseEvent); 
     (*env)->DeleteLocalRef(env, objMouseEvent); 
     break; 

case kCGEventMouseMoved: 
     event_point = CGEventGetLocation(event); 
     #ifdef DEBUG 
     fprintf(stdout, "LowLevelProc(): Motion Notified (%f, %f)\n", event_point.x, event_point.y); 
     #endif 

     // Reset the click count. 
     if (click_count != 0 && (long) (CGEventGetTimestamp(event) - click_time)/1000000 > GetMultiClickTime()) { 
       click_count = 0; 
     } 
     jmodifiers = NativeToJEventMask(GetModifiers()); 

     // Set the mouse dragged flag. 
     mouse_dragged = false; 

     // Fire mouse moved event. 
     objMouseEvent = (*env)->NewObject(
                   env, 
                   clsMouseEvent, 
                   idMouseMotionEvent, 
                   org_jnativehook_mouse_NativeMouseEvent_NATIVE_MOUSE_MOVED, 
                   (jlong) event_time, 
                   jmodifiers, 
                   (jint) event_point.x, 
                   (jint) event_point.y, 
                   (jint) click_count); 
     (*env)->CallVoidMethod(env, objGlobalScreen, idDispatchEvent, objMouseEvent); 
     (*env)->DeleteLocalRef(env, objMouseEvent); 
     break; 

case kCGEventScrollWheel: 
     event_point = CGEventGetLocation(event); 

     // TODO Figure out of kCGScrollWheelEventDeltaAxis2 causes mouse events with zero rotation. 
     if (CGEventGetIntegerValueField(event, kCGScrollWheelEventIsContinuous) == 0) { 
       jscrollType = (jint) org_jnativehook_mouse_NativeMouseWheelEvent_WHEEL_UNIT_SCROLL; 
     } 
     else { 
       jscrollType = (jint) org_jnativehook_mouse_NativeMouseWheelEvent_WHEEL_BLOCK_SCROLL; 
     } 

     // Scrolling data uses a fixed-point 16.16 signed integer format (Ex: 1.0 = 0x00010000). 
     jwheelRotation = (jint) CGEventGetIntegerValueField(event, kCGScrollWheelEventDeltaAxis1) * -1; 

     /* TODO Figure out the scroll wheel amounts are correct. I 
     * suspect that Apples Java implementation maybe reporting a 
     * static "1" inaccurately. 
     */ 
     jscrollAmount = (jint) CGEventGetIntegerValueField(event, kCGScrollWheelEventPointDeltaAxis1) * -1; 

     #ifdef DEBUG 
     fprintf(stdout, "LowLevelProc(): Mouse Wheel Moved (%i, %i, %i)\n", (int) jscrollType, (int) jscrollAmount, (int) jwheelRotation); 
     #endif 

     // Track the number of clicks. 
     if ((long) (CGEventGetTimestamp(event) - click_time)/1000000 <= GetMultiClickTime()) { 
       click_count++; 
     } 
     else { 
       click_count = 1; 
     } 
     click_time = CGEventGetTimestamp(event); 

     jmodifiers = NativeToJEventMask(GetModifiers()); 

     // Fire mouse wheel event. 
     objMouseWheelEvent = (*env)->NewObject(
                     env, 
                     clsMouseWheelEvent, 
                     idMouseWheelEvent, 
                     org_jnativehook_mouse_NativeMouseEvent_NATIVE_MOUSE_WHEEL, 
                     (jlong) event_time, 
                     jmodifiers, 
                     (jint) event_point.x, 
                     (jint) event_point.y, 
                     (jint) click_count, 
                     jscrollType, 
                     jscrollAmount, 
                     jwheelRotation); 
     (*env)->CallVoidMethod(env, objGlobalScreen, idDispatchEvent, objMouseWheelEvent); 
     (*env)->DeleteLocalRef(env, objMouseWheelEvent); 
     break; 

#ifdef DEBUG 
default: 
     fprintf(stderr, "LowLevelProc(): Unhandled Event Type: 0x%X\n", type); 
     break; 
#endif 
} 

應該讓你開始那。

希望有所幫助。

+0

-1僅與切線相關。你的代碼片段大多是無益的。 –

+0

根據你答案頂部的兩個注意事項,我也會給你的答案打電話。就我個人而言,我會讓提問者或其他第三方決定哪些更有用。讓我們看看提問者進入它時的想法。 :)順便說一句,我已經做了很多你在過去提到的答案,並且我最終需要OS系統範圍的鉤子。 – phyatt

+0

您還沒有看過我發佈的代碼,請告訴我吧:) –