Electron 中使用 Node.js 调用 C++ 模块监听鼠标中键点击事件
本文将介绍如何编写一个 Node.js 可调用的 C++ 模块,用于在 Electron 环境下监听 PC 端鼠标中键按下事件,并通过 Node.js 回调 JavaScript 代码。该模块将兼容 Linux、Windows 和 Mac 操作系统。
模块功能
该 C++ 模块提供以下功能:
- 监听鼠标中键按下事件:在 Electron 环境下,捕获鼠标中键按下事件。
- 跨平台兼容性:支持 Linux、Windows 和 Mac 操作系统。
- Node.js 注册回调函数:通过 Node.js 代码注册一个回调函数,当鼠标中键按下时,触发该回调函数。
- 注册函数:
addMiddleMouseClickListener - 移除注册函数:
removeMiddleMouseClickListener - 允许多次注册: 可以多次注册事件,鼠标中键按下时会依次调用所有注册的回调函数。
- 未注册则不响应: 如果没有注册回调函数,则不会响应鼠标中键按下事件。
模块代码
#include <node.h>
#include <uv.h>
#ifdef _WIN32
#include <Windows.h>
HHOOK hook;
#endif
#ifdef __linux__
#include <X11/Xlib.h>
#include <X11/Xutil.h>
Display* display;
Window root;
Atom atom;
#endif
#ifdef __APPLE__
#include <ApplicationServices/ApplicationServices.h>
CGEventRef event;
#endif
namespace middle_mouse_click {
uv_mutex_t mutex;
uv_async_t async;
bool is_running = false;
bool is_registered = false;
uv_async_t* async_ptr;
void middle_mouse_click_callback(uv_async_t* handle);
#ifdef _WIN32
LRESULT CALLBACK mouse_proc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode < 0) {
return CallNextHookEx(hook, nCode, wParam, lParam);
}
if (wParam == WM_MBUTTONDOWN) {
uv_mutex_lock(&mutex);
if (is_running) {
uv_async_send(&async);
}
uv_mutex_unlock(&mutex);
}
return CallNextHookEx(hook, nCode, wParam, lParam);
}
void middle_mouse_click_thread(void* arg) {
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
#endif
#ifdef __linux__
void middle_mouse_click_thread(void* arg) {
XEvent event;
while (true) {
XNextEvent(display, &event);
if (event.type == ButtonPress && event.xbutton.button == Button2) {
uv_mutex_lock(&mutex);
if (is_running) {
uv_async_send(&async);
}
uv_mutex_unlock(&mutex);
}
}
}
#endif
#ifdef __APPLE__
CGEventRef middle_mouse_click_callback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* refcon) {
if (type == kCGEventOtherMouseDown && CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber) == 2) {
uv_mutex_lock(&mutex);
if (is_running) {
uv_async_send(&async);
}
uv_mutex_unlock(&mutex);
}
return event;
}
void middle_mouse_click_thread(void* arg) {
CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource(NULL, event, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRunLoopRun();
}
#endif
void middle_mouse_click_callback(uv_async_t* handle) {
uv_mutex_lock(&mutex);
if (is_registered && async_ptr) {
node::MakeCallback(node::GetCurrentContext()->Global(), async_ptr->data, 0, NULL);
}
uv_mutex_unlock(&mutex);
}
void addMiddleMouseClickListener(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
if (args.Length() < 1) {
isolate->ThrowException(v8::Exception::TypeError(
v8::String::NewFromUtf8(isolate, "Wrong number of arguments")
));
return;
}
if (!args[0]->IsFunction()) {
isolate->ThrowException(v8::Exception::TypeError(
v8::String::NewFromUtf8(isolate, "Callback must be a function")
));
return;
}
uv_mutex_lock(&mutex);
if (!is_registered) {
#ifdef _WIN32
hook = SetWindowsHookEx(WH_MOUSE_LL, mouse_proc, GetModuleHandle(NULL), 0);
is_running = true;
uv_thread_t thread;
uv_thread_create(&thread, middle_mouse_click_thread, NULL);
#endif
#ifdef __linux__
display = XOpenDisplay(NULL);
root = DefaultRootWindow(display);
atom = XInternAtom(display, "BUTTON2_CLICKED", True);
XSelectInput(display, root, ButtonPressMask);
is_running = true;
uv_thread_t thread;
uv_thread_create(&thread, middle_mouse_click_thread, NULL);
#endif
#ifdef __APPLE__
event = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, kCGEventOtherMouseDown, middle_mouse_click_callback, NULL);
is_running = true;
uv_thread_t thread;
uv_thread_create(&thread, middle_mouse_click_thread, NULL);
#endif
uv_async_init(uv_default_loop(), &async, middle_mouse_click_callback);
}
async_ptr = &async;
is_registered = true;
uv_mutex_unlock(&mutex);
args.GetReturnValue().Set(true);
}
void removeMiddleMouseClickListener(const v8::FunctionCallbackInfo<v8::Value>& args) {
uv_mutex_lock(&mutex);
async_ptr = NULL;
is_registered = false;
uv_mutex_unlock(&mutex);
args.GetReturnValue().Set(true);
}
void init(v8::Local<v8::Object> exports) {
uv_mutex_init(&mutex);
NODE_SET_METHOD(exports, "addMiddleMouseClickListener", addMiddleMouseClickListener);
NODE_SET_METHOD(exports, "removeMiddleMouseClickListener", removeMiddleMouseClickListener);
}
NODE_MODULE(middle_mouse_click, init)
}
使用示例
以下是一个简单的 JavaScript 代码示例,用于调用该 C++ 模块:
const middleMouseClick = require('middle-mouse-click');
middleMouseClick.addMiddleMouseClickListener(() => {
console.log('Middle mouse button clicked');
});
setTimeout(() => {
middleMouseClick.removeMiddleMouseClickListener();
}, 10000);
代码说明
- 跨平台事件监听: 代码使用条件编译分别在 Windows、Linux 和 Mac 系统上使用不同的 API 来监听鼠标中键按下事件。
- 异步回调: 使用
uv_async_t和uv_mutex_t来实现异步回调和线程同步。当鼠标中键按下时,通过uv_async_send()函数触发异步回调。 - 移除事件: 通过
removeMiddleMouseClickListener()函数移除注册的回调函数,并释放资源。
总结
本文介绍了如何在 Electron 中使用 Node.js 调用 C++ 模块监听鼠标中键点击事件,并提供了跨平台的 C++ 代码和示例 JS 代码。这个模块可以方便地将鼠标中键点击事件集成到 Electron 应用中,并与 JavaScript 代码进行交互。
注意: 该代码仅供参考,实际应用中可能需要根据具体情况进行调整。
原文地址: https://www.cveoy.top/t/topic/oof4 著作权归作者所有。请勿转载和采集!