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

该模块运行于 Electron 环境下,旨在接收 PC 端鼠标中间按钮按下的事件,并兼容 Linux、Windows 和 Mac 系统。通过 Node.js 注册事件,回调 JavaScript 代码。

功能

  • 注册事件: 使用 addMiddleMouseClickListener 函数注册鼠标中间按钮点击事件的回调函数。
  • 移除事件: 使用 removeMiddleMouseClickListener 函数移除之前注册的事件。
  • 多次注册: 允许多次注册事件,每次鼠标中间按钮按下时都会调用所有注册的回调函数。
  • 未注册事件: 如果没有注册事件,则不会响应鼠标中间按钮的按下事件。

C++ 模块代码

#include <napi.h>
#include <uv.h>
#include <iostream>

#ifdef _WIN32
#include <Windows.h>
#else
#include <X11/Xlib.h>
#endif

// 定义结构体保存回调函数和相关数据
struct CallbackInfo {
    Napi::ThreadSafeFunction tsfn;
    uv_async_t async;
};

// 定义异步回调函数,在鼠标中间按钮点击时被调用
void OnMiddleMouseClick(uv_async_t* handle) {
    // 从异步句柄中获取 CallbackInfo 结构体
    CallbackInfo* callbackInfo = static_cast<CallbackInfo*>(handle->data);

    // 调用回调函数
    callbackInfo->tsfn.BlockingCall([](Napi::Env env, Napi::Function callback) {
        callback.Call({});
    });

    // 删除 CallbackInfo 结构体释放内存
    delete callbackInfo;
}

// 定义鼠标事件处理函数
#ifdef _WIN32
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode == HC_ACTION) {
        if (wParam == WM_MBUTTONDOWN) {
            // 创建新的 CallbackInfo 结构体
            CallbackInfo* callbackInfo = new CallbackInfo();

            // 初始化异步句柄并设置回调函数
            uv_async_init(uv_default_loop(), &callbackInfo->async, OnMiddleMouseClick);

            // 从 CallbackInfo 结构体获取回调函数
            Napi::Env env = callbackInfo->tsfn.Env();
            Napi::Function callback = callbackInfo->tsfn.Function();

            // 将异步句柄排队到主线程运行
            callbackInfo->async.data = callbackInfo;
            uv_async_send(&callbackInfo->async);

            // 返回 1 表示消息已处理
            return 1;
        }
    }

    // 调用下一个钩子
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}
#else
int MouseProc(Display* display, XEvent* event, char* args) {
    if (event->type == ButtonPress && event->xbutton.button == Button2) {
        // 创建新的 CallbackInfo 结构体
        CallbackInfo* callbackInfo = new CallbackInfo();

        // 初始化异步句柄并设置回调函数
        uv_async_init(uv_default_loop(), &callbackInfo->async, OnMiddleMouseClick);

        // 从 CallbackInfo 结构体获取回调函数
        Napi::Env env = callbackInfo->tsfn.Env();
        Napi::Function callback = callbackInfo->tsfn.Function();

        // 将异步句柄排队到主线程运行
        callbackInfo->async.data = callbackInfo;
        uv_async_send(&callbackInfo->async);
    }

    // 调用下一个事件处理程序
    return 0;
}
#endif

// 定义添加鼠标中间按钮点击事件监听器的函数
Napi::Value AddMiddleMouseClickListener(const Napi::CallbackInfo& info) {
    // 从参数中获取回调函数
    Napi::Function callback = info[0].As<Napi::Function>();

    // 创建新的 CallbackInfo 结构体
    CallbackInfo* callbackInfo = new CallbackInfo();

    // 初始化 ThreadSafeFunction,使用回调函数和默认的析构函数
    callbackInfo->tsfn = Napi::ThreadSafeFunction::New(info.Env(), callback, "MouseClickCallback", 0, 1);

    // 返回 CallbackInfo 结构体指针
    return Napi::External<CallbackInfo>::New(info.Env(), callbackInfo);
}

// 定义移除鼠标中间按钮点击事件监听器的函数
void RemoveMiddleMouseClickListener(const Napi::CallbackInfo& info) {
    // 从参数中获取 CallbackInfo 结构体
    CallbackInfo* callbackInfo = info[0].As<Napi::External<CallbackInfo>>().Data();

    // 释放 ThreadSafeFunction
    callbackInfo->tsfn.Release();

    // 删除 CallbackInfo 结构体释放内存
    delete callbackInfo;
}

// 定义模块初始化函数
Napi::Object Init(Napi::Env env, Napi::Object exports) {
    // 定义 addMiddleMouseClickListener 函数
    exports.Set("addMiddleMouseClickListener", Napi::Function::New(env, AddMiddleMouseClickListener));

    // 定义 removeMiddleMouseClickListener 函数
    exports.Set("removeMiddleMouseClickListener", Napi::Function::New(env, RemoveMiddleMouseClickListener));

    // 安装鼠标中间按钮钩子
#ifdef _WIN32
    HHOOK hook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, GetModuleHandle(NULL), 0);
#else
    Display* display = XOpenDisplay(NULL);
    XGrabButton(display, Button2, AnyModifier, DefaultRootWindow(display), True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
#endif

    // 返回 exports 对象
    return exports;
}

// 定义模块
NODE_API_MODULE(middlemouseclick, Init)

Node.js 应用代码

const { addMiddleMouseClickListener, removeMiddleMouseClickListener } = require('./build/Release/middlemouseclick.node');

// 添加鼠标中间按钮点击事件监听器
const listener = addMiddleMouseClickListener(() => {
    console.log('Middle mouse button clicked');
});

// 5 秒后移除事件监听器
setTimeout(() => {
    removeMiddleMouseClickListener(listener);
}, 5000);

工作原理

该模块使用 uv_async_init 函数在鼠标中间按钮按下时通知 Node.js 主线程,并使用 Napi::ThreadSafeFunction 类安全地异步调用 JavaScript 函数。在 Windows 下,模块使用 WH_MOUSE_LL 钩子监听鼠标事件;在 Linux 和 Mac 下,使用 XGrabButton 函数实现监听。

总结

本模块提供了一种高效、可靠的方式,允许 Node.js 应用监听鼠标中间按钮的点击事件。该模块的代码易于理解和维护,并充分利用了 Node.js 的 N-API 和 uv_async_init 函数的功能。

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

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

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