2013-10-07 5 views
2

Мне интересно, как я могу написать код для контроля за кнопками мыши по всему миру. Это было бы для OS X, и я хотел бы попробовать написать его в Qt/C++.Как обнаружить глобальные события кнопки мыши

Для начала я не знаю, как захватить эти глобальные события. Приложение-монитор не будет отображать графический интерфейс, это просто процесс, который работает в фоновом режиме и обнаруживает нажатия кнопок мыши.

Во второй части программы я хотел бы запустить горячие клавиши в зависимости от нажатой клавиши мыши.

Моя последняя идея - сделать бесплатную программу, например steerMouse, чтобы понять, как это можно сделать.

Я прошу дать указания о том, с чего начать - как я могу определить события мыши в глобальном масштабе?

+0

благодарит Кубу за исправление моего первоначального вопроса, извините за мой английский, все еще изучая. – Angoll

ответ

6

Невозможно использовать только Qt. Там another question, который детализирует проблемы. Она сводится к тому:

  1. Установка фильтра событий на QApplication позволит вам получать события мыши, когда курсор находится над любым окном приложения, но не за его пределами. Это не полезно в вашем случае.

  2. Если виджет захватывает мышь, используя grabMouse(), он будет получать все события мыши глобально, но взаимодействие с другими приложениями становится невозможным.

Итак, вам нужно прибегать к использованию конкретных платформ API, чтобы сделать это - это означает, что какао и писать в Objective C/C++. Есть question с отличными ответами, которые обеспечивают почти все, что нам нужно, но интеграцию Qt.

Недостающая часть, показанная ниже, объединяет автономный код с Qt. Этот код показывает пустой виджет, чтобы продемонстрировать, что мы правильно обрабатываем события мыши как для нашего приложения, так и вне его.

Это полный рабочий пример с использованием какао. Он должен войти в файл .mm; не забудьте добавить его в OBJECTIVE_SOURCES в файл проекта qmake (не - 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 выполняет следующие действия:

Это создает переменную нить с правильный обратный вызов системным функциям, которые обрабатывают мышь. Когда мышь (и клавиатура) обрабатывается системой, обратный вызов получает информацию. Затем информация пересылается в 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

Основываясь на двух оговорках в верхней части вашего ответа, я бы назвал ваш ответ бесполезным. Лично я позволю ассеру или другим третьим лицам решить, что более полезно. Давайте посмотрим, что думает автор, когда он попадает в него. :) Кстати, я уже много сделал, что вы упомянули в своем ответе в прошлом, и я закончил нуждаться в системных крючках системы. – phyatt

+0

Вы не читали код, который я опубликовал, у вас есть :) –