Мне интересно, как я могу написать код для мониторинга кнопок мыши во всем мире. Это было бы для OS X, и я хотел бы попробовать написать это на Qt / C ++.
Для начала я не знаю, как запечатлеть эти глобальные события. Приложение монитора не будет отображать графический интерфейс, это будет просто процесс, который выполняется в фоновом режиме и обнаруживает нажатия кнопок мыши.
Во второй части программы я хотел бы запускать горячие клавиши в зависимости от нажатой клавиши мыши.
Моя последняя идея — создать бесплатную программу типа steerMouse, просто чтобы понять, как это можно сделать.
Я спрашиваю руководство о том, с чего начать — как я могу обнаружить события кнопки мыши во всем мире?
Это невозможно, используя только Qt. Там в Другой вопрос это детализирует проблемы. Это сводится к:
Установка фильтра событий на QApplication
позволит вам получать события мыши, когда курсор находится над любым окном приложения, но не за его пределами. Это не полезно в вашем случае.
Если виджет захватывает мышь с помощью 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;
}
Похоже, вы хотите подключить глобальные события мыши на 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
}
Это должно начать вас.
Надеюсь, это поможет.