Node.js 可调用的 C++ 模块:监听鼠标中间按钮点击事件
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 函数的功能。
原文地址: https://www.cveoy.top/t/topic/okzY 著作权归作者所有。请勿转载和采集!