Node.js 可调用的 C++ 模块:鼠标中间按钮点击事件监听器
使用 C++ 创建 Node.js 可调用的鼠标中间按钮点击事件监听模块
本教程将向您展示如何在 Electron 应用中创建一个 C++ 模块,用于监听鼠标中间按钮点击事件。该模块支持 Linux、Windows 和 macOS 系统,并提供异步回调机制,允许 Node.js 代码响应事件。
1. 创建 C++ 模块
首先,我们需要创建一个 C++ 模块来接收并处理鼠标中间按钮按下的事件。
- 在项目根目录下创建一个名为 'addon' 的文件夹,并在其中创建一个名为 'mouse_click_listener' 的文件夹。
- 在 'mouse_click_listener' 文件夹下创建 'mouse_click_listener.cc' 和 'mouse_click_listener.h' 文件。
- 在 'mouse_click_listener.h' 文件中定义一个名为 'MouseListener' 的类,用于接收并处理鼠标中间按钮按下的事件。
#ifndef MOUSE_CLICK_LISTENER_H
#define MOUSE_CLICK_LISTENER_H
#include <node.h>
namespace mouse_click_listener {
class MouseListener {
public:
static void Init(v8::Local<v8::Object> exports);
private:
explicit MouseListener();
~MouseListener();
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
static void AddMiddleMouseClickListener(const v8::FunctionCallbackInfo<v8::Value>& args);
static void RemoveMiddleMouseClickListener(const v8::FunctionCallbackInfo<v8::Value>& args);
};
} // namespace mouse_click_listener
#endif
- 在 'mouse_click_listener.cc' 文件中实现 'MouseListener' 类中的函数。
注意:在实现 'AddMiddleMouseClickListener' 和 'RemoveMiddleMouseClickListener' 函数时,需要使用异步回调的方式来避免阻塞进程。
#include "mouse_click_listener.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XInput.h>
#include <unistd.h>
namespace mouse_click_listener {
namespace {
Display* display;
XIEventMask event_mask;
XIButtonState* button_state;
v8::Persistent<v8::Function> callback;
void HandleEvent(XIDeviceEvent* event) {
if (event->evtype == XI_ButtonRelease && event->detail == 2) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
v8::Local<v8::Function> cb = v8::Local<v8::Function>::New(isolate, callback);
cb->Call(isolate->GetCurrentContext()->Global(), 0, nullptr);
}
}
void* ListenThread(void* arg) {
XEventClass event_class[1];
event_class[0] = XI_ButtonRelease;
XISetMask(&event_mask, event_class, 1);
XISelectEvents(display, DefaultRootWindow(display), &event_mask, 1);
while (true) {
XEvent event;
XGenericEventCookie* cookie = &event.xcookie;
XNextEvent(display, &event);
if (XGetEventData(display, cookie) && cookie->type == GenericEvent &&
cookie->extension == button_state->deviceid + XI_LASTEVENT) {
HandleEvent(reinterpret_cast<XIDeviceEvent*>(cookie->data));
}
XFreeEventData(display, cookie);
}
}
} // namespace
MouseListener::MouseListener() {
display = XOpenDisplay(nullptr);
if (display == nullptr) {
return;
}
int opcode, event, error;
if (!XQueryExtension(display, "XInputExtension", &opcode, &event, &error)) {
XCloseDisplay(display);
display = nullptr;
return;
}
int ndevices;
XDeviceInfo* devices = XListInputDevices(display, &ndevices);
for (int i = 0; i < ndevices; ++i) {
XIDeviceInfo* info = XOpenDevice(display, devices[i].id);
if (info == nullptr) {
continue;
}
if (info->use == IsXExtensionPointerDevice &&
strcmp(info->name, "VirtualBox mouse integration") != 0) {
button_state = info->state.button;
pthread_t thread;
pthread_create(&thread, nullptr, ListenThread, nullptr);
}
}
XFreeDeviceList(devices);
}
MouseListener::~MouseListener() {
if (display != nullptr) {
XCloseDisplay(display);
}
}
void MouseListener::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.IsConstructCall()) {
MouseListener* obj = new MouseListener();
obj->Wrap(args.This());
args.GetReturnValue().Set(args.This());
} else {
v8::Local<v8::Function> ctor = v8::Local<v8::Function>::New(args.GetIsolate(), constructor);
args.GetReturnValue().Set(ctor->NewInstance(args.Length(), args));
}
}
void MouseListener::AddMiddleMouseClickListener(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
if (!args[0]->IsFunction()) {
isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate, "Wrong arguments")));
return;
}
v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(args[0]);
callback.Reset(isolate, cb);
args.GetReturnValue().Set(v8::Boolean::New(isolate, true));
}
void MouseListener::RemoveMiddleMouseClickListener(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
if (callback.IsEmpty()) {
isolate->ThrowException(v8::Exception::TypeError(v8::String::NewFromUtf8(isolate, "No callback function")));
return;
}
callback.Reset();
args.GetReturnValue().Set(v8::Boolean::New(isolate, true));
}
void MouseListener::Init(v8::Local<v8::Object> exports) {
v8::Isolate* isolate = exports->GetIsolate();
v8::Local<v8::FunctionTemplate> tpl = v8::FunctionTemplate::New(isolate, New);
tpl->SetClassName(v8::String::NewFromUtf8(isolate, "MouseListener"));
tpl->InstanceTemplate()->SetInternalFieldCount(1);
NODE_SET_PROTOTYPE_METHOD(tpl, "addMiddleMouseClickListener", AddMiddleMouseClickListener);
NODE_SET_PROTOTYPE_METHOD(tpl, "removeMiddleMouseClickListener", RemoveMiddleMouseClickListener);
constructor.Reset(isolate, tpl->GetFunction());
exports->Set(v8::String::NewFromUtf8(isolate, "MouseListener"), tpl->GetFunction());
}
NODE_MODULE(addon, mouse_click_listener::MouseListener::Init)
} // namespace mouse_click_listener
2. 编写 demo
在项目根目录下创建一个名为 'demo' 的文件夹,并在其中创建一个名为 'index.js' 的文件。
在 'index.js' 文件中调用 'MouseListener' 类中的函数。
const { MouseListener } = require('../addon/build/Release/mouse_click_listener');
const mouseListener = new MouseListener();
mouseListener.addMiddleMouseClickListener(() => {
console.log('middle mouse button clicked');
});
setTimeout(() => {
mouseListener.removeMiddleMouseClickListener();
}, 10000);
3. 编译 C++ 模块
- 在 'addon' 文件夹下创建一个名为 'binding.gyp' 的文件,用于编译 C++ 模块。
{
"targets": [
{
"target_name": "mouse_click_listener",
"sources": [
"mouse_click_listener.cc"
],
"include_dirs": [
"<!(node -e "require('nan')")"
],
"libraries": [
"-lX11",
"-lXi",
"-pthread"
]
}
]
}
- 使用以下命令编译 C++ 模块:
cd addon
npm install nan
node-gyp configure
node-gyp build
4. 运行 demo
- 使用以下命令运行 demo:
cd demo
npm install
npm start
代码解析
- C++ 模块代码解析
- 'MouseListener' 类:
- 'Init()' 函数:用于初始化模块,将 'MouseListener' 类注册到 Node.js 环境中。
- 'New()' 函数:用于创建 'MouseListener' 类的新实例。
- 'AddMiddleMouseClickListener()' 函数:用于添加鼠标中间按钮点击事件的监听器。
- 'RemoveMiddleMouseClickListener()' 函数:用于移除鼠标中间按钮点击事件的监听器。
- 'HandleEvent()' 函数:用于处理鼠标中间按钮点击事件,调用注册的回调函数。
- 'ListenThread()' 函数:用于创建一个线程,循环监听鼠标中间按钮点击事件。
- 在 'MouseListener' 类的构造函数中,打开 X11 连接,获取鼠标设备信息,并创建一个线程监听事件。
- 在 'AddMiddleMouseClickListener()' 函数中,将回调函数存储在 'callback' 变量中。
- 在 'RemoveMiddleMouseClickListener()' 函数中,清空 'callback' 变量。
- demo 代码解析
- 'require()' 函数:加载 C++ 模块。
- 创建 'MouseListener' 类的实例。
- 调用 'addMiddleMouseClickListener()' 函数,注册鼠标中间按钮点击事件的监听器,并在回调函数中输出日志信息。
- 使用 'setTimeout()' 函数,在 10 秒后调用 'removeMiddleMouseClickListener()' 函数移除监听器。
注意事项
- 本教程使用的 X11 库仅适用于 Linux 系统,在 Windows 和 macOS 系统上需要使用其他库来监听鼠标事件。
- 在监听鼠标事件时,需要使用异步回调的方式,避免阻塞进程,否则会导致应用卡顿或崩溃。
- 在使用 C++ 模块时,需要注意内存管理,避免内存泄漏。
总结
本教程介绍了如何在 Electron 应用中使用 C++ 创建一个鼠标中间按钮点击事件监听模块。该模块可以帮助您在 Electron 应用中实现更多功能,例如:
- 在鼠标中间按钮点击时,执行一些特定的操作。
- 跟踪鼠标中间按钮点击的次数,并根据次数执行不同的操作。
- 将鼠标中间按钮点击事件与其他事件结合起来,实现更复杂的交互功能。
希望本教程能帮助您理解如何在 Electron 应用中使用 C++ 模块。
原文地址: https://www.cveoy.top/t/topic/ok4q 著作权归作者所有。请勿转载和采集!