本文将介绍如何编写一个 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);

代码说明

  1. 跨平台事件监听: 代码使用条件编译分别在 Windows、Linux 和 Mac 系统上使用不同的 API 来监听鼠标中键按下事件。
  2. 异步回调: 使用 uv_async_tuv_mutex_t 来实现异步回调和线程同步。当鼠标中键按下时,通过 uv_async_send() 函数触发异步回调。
  3. 移除事件: 通过 removeMiddleMouseClickListener() 函数移除注册的回调函数,并释放资源。

总结

本文介绍了如何在 Electron 中使用 Node.js 调用 C++ 模块监听鼠标中键点击事件,并提供了跨平台的 C++ 代码和示例 JS 代码。这个模块可以方便地将鼠标中键点击事件集成到 Electron 应用中,并与 JavaScript 代码进行交互。

注意: 该代码仅供参考,实际应用中可能需要根据具体情况进行调整。

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

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

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