Как обнаружить глобальные события кнопки мыши

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

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

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

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

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

2

Решение

Это невозможно, используя только Qt. Там в Другой вопрос это детализирует проблемы. Это сводится к:

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

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

Таким образом, для этого вам придется прибегнуть к использованию API-интерфейсов, специфичных для платформы, то есть к Cocoa и написанию на Objective C / C ++. Есть вопрос с отличными ответами, которые обеспечивают почти все, что нам нужно, кроме интеграции 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;
}
6

Другие решения

Похоже, вы хотите подключить глобальные события мыши на 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 делает следующее:

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

Это должно начать вас.

Надеюсь, это поможет.

1

По вопросам рекламы [email protected]