Node.js 可调用的 C++ 模块:鼠标中间按钮监听

这是一个跨平台的 C++ 模块,用于监听鼠标中间按钮事件,并在 Electron 环境中通过 Node.js 注册回调函数来处理事件。

功能

  • 兼容 Windows、Linux 和 macOS 系统。
  • 通过 addMiddleMouseClickListener 函数注册回调函数,在鼠标中间按钮按下时调用。
  • 通过 removeMiddleMouseClickListener 函数移除某个注册的回调函数。
  • 允许多次注册 addMiddleMouseClickListener,当鼠标中间按钮按下时,所有注册的回调函数都会被调用。
  • 如果没有注册回调函数,则不会响应鼠标中间按钮按下事件。

代码示例

C++ 模块文件 (middleMouseClickListener.cpp)

#include <node.h>
#include <v8.h>
#include <iostream>
#include <thread>

#ifdef _WIN32
    #include <windows.h>
#elif __linux__
    #include <X11/Xlib.h>
    #include <X11/extensions/XInput2.h>
#elif __APPLE__
    #include <ApplicationServices/ApplicationServices.h>
#endif

using namespace v8;

// 定义回调函数类型
typedef std::function<void()> MiddleMouseClickCallback;

// 中间按钮监听类
class MiddleMouseClickListener {
public:
    // 添加回调函数
    void addListener(const MiddleMouseClickCallback& callback) {
        callbacks.push_back(callback);
    }

    // 移除某个回调函数
    void removeListener(const MiddleMouseClickCallback& callback) {
        callbacks.erase(std::remove(callbacks.begin(), callbacks.end(), callback), callbacks.end());
    }

    // 监听中间按钮事件
    void listen() {
#ifdef _WIN32
        // Windows下使用系统API获取鼠标信息
        while (true) {
            if ((GetAsyncKeyState(VK_MBUTTON) & 0x8000) != 0) {
                // 中间按钮按下
                for (auto callback : callbacks) {
                    callback();
                }
            }
            Sleep(10);
        }
#elif __linux__
        // Linux下使用X11扩展库获取鼠标信息
        Display* display = XOpenDisplay(NULL);
        int xi_opcode, event, error;
        if (XQueryExtension(display, "XInputExtension", &xi_opcode, &event, &error)) {
            int major = 2, minor = 0;
            if (XIQueryVersion(display, &major, &minor) == Success) {
                int device_count;
                XIDeviceInfo* devices = XIQueryDevice(display, XIAllDevices, &device_count);
                if (devices != NULL) {
                    // 查找鼠标设备
                    for (int i = 0; i < device_count; i++) {
                        if (devices[i].use == XIMasterPointer) {
                            XIDeviceInfo* device = &devices[i];
                            XEventClass event_class;
                            // 注册中间按钮事件
                            if (XIQueryPointer(display, device->deviceid, DefaultRootWindow(display), &event_class, &event, &error) == Success) {
                                XIEventMask event_mask;
                                unsigned char mask[(XI_LASTEVENT + 7)/8];
                                memset(mask, 0, sizeof(mask));
                                XISetMask(mask, XI_ButtonPress);
                                XISetMask(mask, XI_ButtonRelease);
                                event_mask.deviceid = device->deviceid;
                                event_mask.mask_len = sizeof(mask);
                                event_mask.mask = mask;
                                XISelectEvents(display, DefaultRootWindow(display), &event_mask, 1);
                                while (true) {
                                    XEvent event;
                                    XGenericEventCookie* cookie = (XGenericEventCookie*)&event.xcookie;
                                    XNextEvent(display, &event);
                                    if (cookie->type == GenericEvent && cookie->extension == xi_opcode) {
                                        XIDeviceEvent* xi_event = (XIDeviceEvent*)cookie->data;
                                        if (xi_event->evtype == XI_ButtonPress && xi_event->detail == 2) {
                                            // 中间按钮按下
                                            for (auto callback : callbacks) {
                                                callback();
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    XIFreeDeviceInfo(devices);
                }
            }
        }
        XCloseDisplay(display);
#elif __APPLE__
        // Mac下使用系统API获取鼠标信息
        CGEventMask mask = CGEventMaskBit(kCGEventOtherMouseDown);
        CFMachPortRef eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, mask, NULL);
        CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
        CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
        while (true) {
            CFRunLoopRun();
        }
#endif
    }

private:
    std::vector<MiddleMouseClickCallback> callbacks;
};

// 中间按钮监听器实例
MiddleMouseClickListener listener;

// 初始化中间按钮监听模块
void initModule(Local<Object> exports) {
    // 添加addMiddleMouseClickListener函数
    exports->Set(String::NewFromUtf8(Isolate::GetCurrent(), "addMiddleMouseClickListener"), FunctionTemplate::New(Isolate::GetCurrent(), [](const FunctionCallbackInfo<Value>& args) {
        if (args.Length() > 0 && args[0]->IsFunction()) {
            // 注册回调函数
            Local<Function> callback = Local<Function>::Cast(args[0]);
            listener.addListener([callback]() {
                callback->Call(Null(Isolate::GetCurrent()), 0, nullptr);
            });
        }
        args.GetReturnValue().Set(Undefined(Isolate::GetCurrent()));
    })->GetFunction());

    // 添加removeMiddleMouseClickListener函数
    exports->Set(String::NewFromUtf8(Isolate::GetCurrent(), "removeMiddleMouseClickListener"), FunctionTemplate::New(Isolate::GetCurrent(), [](const FunctionCallbackInfo<Value>& args) {
        if (args.Length() > 0 && args[0]->IsFunction()) {
            // 移除回调函数
            Local<Function> callback = Local<Function>::Cast(args[0]);
            listener.removeListener([callback]() {
                callback->Call(Null(Isolate::GetCurrent()), 0, nullptr);
            });
        }
        args.GetReturnValue().Set(Undefined(Isolate::GetCurrent()));
    })->GetFunction());

    // 启动中间按钮监听线程
    std::thread([&listener]() {
        listener.listen();
    }).detach();
}

// 导出中间按钮监听模块
NODE_MODULE(middleMouseClickListener, initModule)

Demo 文件 (main.js)

const middleMouseClickListener = require('./middleMouseClickListener');

// 添加回调函数
middleMouseClickListener.addMiddleMouseClickListener(() => {
    console.log('中间按钮按下');
});

// 移除回调函数
let callback = () => {
    console.log('这个回调函数会被移除');
};
middleMouseClickListener.addMiddleMouseClickListener(callback);
middleMouseClickListener.removeMiddleMouseClickListener(callback);

注意事项

  • 在 C++ 代码中监听事件时,不要使用阻塞方式,否则会影响 Electron 的运行。
  • 该代码仅供参考,实际应用中可能需要根据具体情况进行调整。

使用方法

  1. middleMouseClickListener.cpp 文件编译成 Node.js 模块。
  2. 在 Electron 应用中使用 require 函数加载该模块。
  3. 使用 addMiddleMouseClickListener 函数注册回调函数。
  4. 使用 removeMiddleMouseClickListener 函数移除回调函数。

总结

该模块提供了一个方便的方式,在 Electron 应用中监听鼠标中间按钮事件,并通过 Node.js 回调函数处理事件。它可以用于各种场景,例如在图片编辑器中实现缩放功能、在网页浏览器中实现滚轮滚动功能等。

Node.js 可调用的 C++ 模块:鼠标中间按钮监听

原文地址: https://www.cveoy.top/t/topic/olDG 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录