Node.js 可调用 C++ 模块:监听鼠标中键点击事件
本文将指导您创建一个 Node.js 可调用的 C++ 模块,用于监听鼠标中键点击事件。该模块在 Electron 环境中运行,支持 Linux、Windows 和 Mac 操作系统,并通过 Node.js 注册事件回调 JavaScript 代码。
1. 编写 C++ 模块
首先,创建一个名为 middleMouse.cpp 的 C++ 文件,实现鼠标中键点击事件的监听和回调功能。代码示例如下:
#include <node.h>
#include <v8.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#ifdef _WIN32
#include <windows.h>
#include <winuser.h>
#elif __linux__
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XTest.h>
#elif __APPLE__
#include <ApplicationServices/ApplicationServices.h>
#endif
using namespace v8;
namespace middleMouse {
// 定义回调函数类型
typedef void (*MiddleMouseCallback)(const FunctionCallbackInfo<Value>&);
// 存储回调函数的数组
static std::vector<MiddleMouseCallback> callbacks;
// 中键点击事件的回调函数
#ifdef _WIN32
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
if (wParam == WM_MBUTTONDOWN) {
// 触发回调函数
for (auto& callback : callbacks) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
callback(FunctionCallbackInfo<Value>());
}
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
#elif __linux__
Display* display;
XEvent event;
int x11_fd;
fd_set in_fds;
void* x11_thread_func(void* arg) {
while (1) {
FD_ZERO(&in_fds);
FD_SET(x11_fd, &in_fds);
select(x11_fd + 1, &in_fds, NULL, NULL, NULL);
while (XPending(display)) {
XNextEvent(display, &event);
if (event.type == ButtonPress && event.xbutton.button == Button2) {
// 触发回调函数
for (auto& callback : callbacks) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
callback(FunctionCallbackInfo<Value>());
}
}
}
}
return NULL;
}
#elif __APPLE__
CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
if (type == kCGEventTapDisabledByTimeout) {
CGEventTapEnable((CGEventTapProxy) refcon, event, kCGEventTapOptionListenOnly, kCGEventMaskForAllEvents, NULL);
return event;
}
if (type == kCGEventLeftMouseDown || type == kCGEventRightMouseDown || type == kCGEventOtherMouseDown) {
CGMouseButton mouse = kCGMouseButtonCenter;
CGEventRef click = CGEventCreateMouseEvent(NULL, kCGEventOtherMouseDown, CGPointMake(0, 0), mouse);
CGEventPost(kCGHIDEventTap, click);
CFRelease(click);
// 触发回调函数
for (auto& callback : callbacks) {
Isolate* isolate = Isolate::GetCurrent();
HandleScope scope(isolate);
callback(FunctionCallbackInfo<Value>());
}
}
return event;
}
#endif
// 注册中键点击事件的回调函数
void AddMiddleMouseClickListener(const FunctionCallbackInfo<Value>& args) {
// 解析参数
if (args.Length() != 1 || !args[0]->IsFunction()) {
return;
}
Local<Function> callback = Local<Function>::Cast(args[0]);
// 存储回调函数
MiddleMouseCallback middleMouseCallback = [](const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
callback->Call(isolate->GetCurrentContext(), Null(isolate), 0, NULL);
};
callbacks.push_back(middleMouseCallback);
// 安装钩子
#ifdef _WIN32
SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, NULL, 0);
#elif __linux__
display = XOpenDisplay(NULL);
if (display == NULL) {
return;
}
x11_fd = ConnectionNumber(display);
pthread_t x11_thread;
pthread_create(&x11_thread, NULL, x11_thread_func, NULL);
#elif __APPLE__
CFMachPortRef eventTap;
CFRunLoopSourceRef runLoopSource;
eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0, kCGEventMaskForAllEvents, eventCallback, NULL);
if (!eventTap) {
return;
}
runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
CGEventTapEnable(eventTap, true);
CFRunLoopRun();
#endif
}
// 移除中键点击事件的回调函数
void RemoveMiddleMouseClickListener(const FunctionCallbackInfo<Value>& args) {
// 解析参数
if (args.Length() != 1 || !args[0]->IsFunction()) {
return;
}
Local<Function> callback = Local<Function>::Cast(args[0]);
// 查找并移除回调函数
for (auto it = callbacks.begin(); it != callbacks.end(); ) {
if (*it == callback) {
it = callbacks.erase(it);
} else {
++it;
}
}
}
// 初始化模块
void Init(Local<Object> exports) {
NODE_SET_METHOD(exports, 'addMiddleMouseClickListener', AddMiddleMouseClickListener);
NODE_SET_METHOD(exports, 'removeMiddleMouseClickListener', RemoveMiddleMouseClickListener);
}
NODE_MODULE(middleMouse, Init)
}
2. 编写 Node.js 调用代码
创建一个名为 app.js 的 Node.js 文件,用于调用 C++ 模块。代码示例如下:
const middleMouse = require('./build/Release/middleMouse');
// 注册回调函数
middleMouse.addMiddleMouseClickListener(() => {
console.log('middle mouse button clicked');
});
// 移除回调函数
middleMouse.removeMiddleMouseClickListener(() => {
console.log('middle mouse button clicked');
});
3. 编译 C++ 模块
使用 node-gyp 编译生成可执行文件:
$ node-gyp configure
$ node-gyp build
4. 运行测试
编译完成后,运行 app.js 即可测试鼠标中键点击事件的回调。当您点击鼠标中键时,控制台将输出 'middle mouse button clicked'。
注意:
- 确保您的系统已安装
node-gyp和必要的依赖库。 - 在编译过程中,请根据您的操作系统修改
middleMouse.cpp中的代码,以适应不同的平台。 - C++ 代码中的事件监听部分使用了非阻塞的方式,避免阻塞事件循环。
- 该模块适合在 Electron 环境中使用。如果您在其他环境中使用,可能需要进行一些调整。
更多信息:
原文地址: https://www.cveoy.top/t/topic/ooe8 著作权归作者所有。请勿转载和采集!