如何检测全局鼠标按钮事件

时间:2013-10-07 16:19:06

标签: c++ macos qt osx-snow-leopard

我想知道如何编写代码来全局监控鼠标按钮。这适用于OS X,我想尝试用Qt / C ++编写它。

首先,我不知道如何捕捉这些全球事件。监视器应用程序不会显示GUI,它只是一个在后台运行并检测到单击鼠标按钮的进程。

在程序的第二部分,我想根据按下的鼠标键启动热键。

我最后的想法是制作一个像steerMouse这样的免费程序,只是为了弄清楚它是如何完成的。

我正在寻求从哪里开始的指导 - 如何全局检测鼠标按钮事件?

2 个答案:

答案 0 :(得分:6)

仅使用Qt是不可能的。有another question详细说明了这些问题。归结为:

  1. QApplication上安装事件过滤器将允许您在光标位于任何应用程序窗口之外时接收鼠标事件,但不在其外部。这对你的情况没有帮助。

  2. 如果小部件使用grabMouse()抓取鼠标,它将全局接收所有鼠标事件,但与其他应用程序的交互变得不可能。

  3. 因此,您需要使用特定于平台的API来执行此操作 - 这意味着Cocoa并使用Objective C / C ++编写。有一个question的答案非常出色,几乎可以提供Qt整合所需的一切。

    缺少的部分,如下所示,是将独立代码与Qt集成。此代码显示一个空小部件,仅用于演示我们正确处理应用程序及其外部的鼠标事件。

    这是一个完整的,有效的例子,使用Cocoa。它需要进入.mm文件;不要忘记将其添加到qmake项目文件中的OBJECTIVE_SOURCESSOURCES!)。

    不幸的是,单个函数/方法不会从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;
    }
    

答案 1 :(得分: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执行以下操作:

它创建一个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
}

这应该让你开始。

希望有所帮助。