本文将指导您创建一个 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 环境中使用。如果您在其他环境中使用,可能需要进行一些调整。

更多信息:

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

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

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