Node.js 可调用的 C++ 模块:跨平台鼠标中键点击事件监听

该模块用于在 Electron 环境下监听 PC 端鼠标中键按下事件,并通过 Node.js 注册事件回调 JavaScript 代码。它支持 Linux、Windows 和 macOS 三个平台,并允许用户多次注册事件回调,并在鼠标中键按下时一次调用所有注册的回调函数。

模块功能

  • 跨平台支持: 支持 Linux、Windows 和 macOS 系统。
  • 事件注册: 通过 addMiddleMouseClickListener 函数注册鼠标中键点击事件回调。
  • 事件移除: 通过 removeMiddleMouseClickListener 函数移除已注册的事件回调。
  • 多回调函数支持: 允许多次注册事件回调,并在鼠标中键按下时依次调用所有注册的回调函数。

代码实现

以下是一个简单的实现示例:

#include <nan.h>
#include <iostream>
#ifdef __linux__
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#elif _WIN32
#include <Windows.h>
#else
// macOS
#include <ApplicationServices/ApplicationServices.h>
#endif

using namespace Nan;
using namespace std;

namespace {

#ifdef __linux__
  Display* dpy;
#elif _WIN32
  HHOOK mouseHook;
#else
  // macOS
  CFMachPortRef eventTap;
#endif

  Persistent<Array> callbacks;

  // 回调函数类型
  typedef void(*Callback)(int);

  // 回调函数列表
  vector<Callback> callbackList;

  // 添加回调函数
  NAN_METHOD(AddMiddleMouseClickListener) {
    Callback callback = [](int button) {
      // 调用 JavaScript 回调函数
      CallbacksHolder(callbacks).Call({
        New<String>('middleMouseClick').ToLocalChecked(),
        New<Number>(button)
      });
    };
    callbackList.push_back(callback);
    info.GetReturnValue().Set(New<Number>(callbackList.size() - 1));
  }

  // 移除回调函数
  NAN_METHOD(RemoveMiddleMouseClickListener) {
    int index = To<int>(info[0]).FromJust();
    if (index >= 0 && index < callbackList.size()) {
      callbackList.erase(callbackList.begin() + index);
    }
  }

#ifdef __linux__
  // Linux 平台下的事件监听函数
  void handleEvent(XEvent* evt) {
    if (evt->type == ButtonPress && evt->xbutton.button == Button2) {
      int count = callbackList.size();
      for (int i = 0; i < count; i++) {
        callbackList[i](Button2);
      }
    }
  }

  // 线程函数,用于监听事件
  void threadFunc() {
    XEvent evt;
    while (true) {
      XNextEvent(dpy, &evt);
      handleEvent(&evt);
    }
  }
#elif _WIN32
  // Windows 平台下的事件监听函数
  LRESULT CALLBACK mouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
    if (nCode == HC_ACTION && wParam == WM_MBUTTONDOWN) {
      int count = callbackList.size();
      for (int i = 0; i < count; i++) {
        callbackList[i](VK_MBUTTON);
      }
    }
    return CallNextHookEx(NULL, nCode, wParam, lParam);
  }

  // 线程函数,用于安装鼠标钩子
  void threadFunc() {
    mouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouseProc, NULL, 0);
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
    UnhookWindowsHookEx(mouseHook);
  }
#else
  // macOS 平台下的事件监听函数
  CGEventRef eventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void* userInfo) {
    if (type == kCGEventOtherMouseDown && CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber) == kCGMouseButtonCenter) {
      int count = callbackList.size();
      for (int i = 0; i < count; i++) {
        callbackList[i](kCGMouseButtonCenter);
      }
    }
    return event;
  }

  // 线程函数,用于监听事件
  void threadFunc() {
    CFRunLoopSourceRef runLoopSource;
    eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, kCGEventOtherMouseDown, eventTapCallback, NULL);
    if (!eventTap) {
      cerr << "Failed to create event tap." << endl;
      return;
    }
    runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
    if (!runLoopSource) {
      cerr << "Failed to create run loop source." << endl;
      CFRelease(eventTap);
      return;
    }
    CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
    CGEventTapEnable(eventTap, true);
    CFRunLoopRun();
    CGEventTapEnable(eventTap, false);
    CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
    CFRelease(runLoopSource);
    CFRelease(eventTap);
  }
#endif

  // 初始化函数
  NAN_MODULE_INIT(init) {
    callbacks.Reset(New<Array>());
    Set(target, New<String>('addMiddleMouseClickListener').ToLocalChecked(), GetFunction(New<FunctionTemplate>(AddMiddleMouseClickListener)).ToLocalChecked());
    Set(target, New<String>('removeMiddleMouseClickListener').ToLocalChecked(), GetFunction(New<FunctionTemplate>(RemoveMiddleMouseClickListener)).ToLocalChecked());
#ifdef __linux__
    dpy = XOpenDisplay(NULL);
    if (dpy) {
      thread t(threadFunc);
      t.detach();
    }
#elif _WIN32
    thread t(threadFunc);
    t.detach();
#else
    // macOS
    thread t(threadFunc);
    t.detach();
#endif
  }

}

NODE_MODULE(mouse, init)

JavaScript 调用示例

const { ipcRenderer } = require('electron');
const mouse = require('./build/Release/mouse.node');

// 注册回调函数
let id = mouse.addMiddleMouseClickListener((button) => {
  console.log('middle mouse button clicked:', button);
  ipcRenderer.send('middleMouseClick', button);
});

// 移除回调函数
setTimeout(() => {
  mouse.removeMiddleMouseClickListener(id);
}, 5000);

在这个示例中,我们使用 ipcRenderer 模块与主进程进行通信,并将鼠标中键按下的事件发送给主进程。在 5 秒后,我们移除这个回调函数。

注意

  • 模块代码需要根据平台进行编译。
  • 确保 Electron 环境已经配置好。
  • 监听事件时不要使用阻塞方式,以免影响程序性能。

总结

该模块提供了一种简单易用的方法来在 Electron 环境下监听鼠标中键点击事件,并通过 Node.js 接口进行回调,方便开发者进行跨平台的鼠标事件处理。

Node.js 可调用的 C++ 模块:跨平台鼠标中键点击事件监听

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

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