macos-scroll-monitor 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/binding.gyp +36 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +17 -0
- package/src/macos_scroll_monitor.mm +97 -0
package/binding.gyp
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"targets": [
|
|
3
|
+
{
|
|
4
|
+
"target_name": "macos_scroll_monitor",
|
|
5
|
+
"sources": [
|
|
6
|
+
"src/macos_scroll_monitor.mm",
|
|
7
|
+
],
|
|
8
|
+
"include_dirs": [
|
|
9
|
+
"<!@(node -p \"require('node-addon-api').include\")",
|
|
10
|
+
"include"
|
|
11
|
+
],
|
|
12
|
+
"dependencies": [
|
|
13
|
+
"<!(node -p \"require('node-addon-api').gyp\")"
|
|
14
|
+
],
|
|
15
|
+
"defines": [
|
|
16
|
+
"NODE_ADDON_API_CPP_EXCEPTIONS"
|
|
17
|
+
],
|
|
18
|
+
"cflags!": ["-fno-exceptions"],
|
|
19
|
+
"cflags_cc!": ["-fno-exceptions"],
|
|
20
|
+
"xcode_settings": {
|
|
21
|
+
"CLANG_ENABLE_OBJC_ARC": "YES",
|
|
22
|
+
"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
|
|
23
|
+
"CLANG_CXX_LIBRARY": "libc++",
|
|
24
|
+
"MACOSX_DEPLOYMENT_TARGET": "10.14"
|
|
25
|
+
},
|
|
26
|
+
"msvs_settings": {
|
|
27
|
+
"VCCLCompilerTool": {
|
|
28
|
+
"ExceptionHandling": 1
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"libraries": [
|
|
32
|
+
"-framework AppKit"
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function isFnPressed(): boolean;
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('bindings')('macos_scroll_monitor')
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "macos-scroll-monitor",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A native addon for Electron",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"clean": "node -e \"require('fs').rmSync('build', { recursive: true, force: true })\"",
|
|
8
|
+
"build": "node-gyp configure && node-gyp build"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"bindings": "^1.5.0",
|
|
12
|
+
"node-addon-api": "^8.3.0"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"node-gyp": "^11.1.0"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#include <napi.h>
|
|
2
|
+
#import <AppKit/AppKit.h>
|
|
3
|
+
|
|
4
|
+
class ScrollMonitor : public Napi::Addon<ScrollMonitor> {
|
|
5
|
+
public:
|
|
6
|
+
// 构造函数:关联 JavaScript 方法与 C++ 实现
|
|
7
|
+
ScrollMonitor(Napi::Env env, Napi::Object exports) {
|
|
8
|
+
DefineAddon(exports, {
|
|
9
|
+
InstanceMethod("start", &ScrollMonitor::Start),
|
|
10
|
+
InstanceMethod("stop", &ScrollMonitor::Stop)
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private:
|
|
15
|
+
id _eventMonitor = nil; // 存储 macOS 原生监听器引用
|
|
16
|
+
Napi::ThreadSafeFunction _tsfn; // 用于跨线程(系统事件线程 -> JS 线程)安全通信
|
|
17
|
+
|
|
18
|
+
// 内部结构体:封装需要传回 JS 的事件数据
|
|
19
|
+
struct ScrollEventData {
|
|
20
|
+
double deltaY;
|
|
21
|
+
bool command;
|
|
22
|
+
bool shift;
|
|
23
|
+
bool option;
|
|
24
|
+
bool ctrl;
|
|
25
|
+
bool fn;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// 启动监听逻辑
|
|
29
|
+
Napi::Value Start(const Napi::CallbackInfo& info) {
|
|
30
|
+
Napi::Env env = info.Env();
|
|
31
|
+
|
|
32
|
+
// 防护逻辑:避免重复启动导致内存泄漏或多次监听
|
|
33
|
+
if (_eventMonitor != nil || _tsfn) return env.Undefined();
|
|
34
|
+
|
|
35
|
+
// 验证参数:确保第一个参数是回调函数
|
|
36
|
+
if (info.Length() < 1 || !info[0].IsFunction()) {
|
|
37
|
+
Napi::TypeError::New(env, "Callback function expected").ThrowAsJavaScriptException();
|
|
38
|
+
return env.Undefined();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Napi::Function cb = info[0].As<Napi::Function>();
|
|
42
|
+
|
|
43
|
+
// 创建线程安全函数。Resource Name 设为 MacOSGlobalScrollMonitor 方便调试
|
|
44
|
+
_tsfn = Napi::ThreadSafeFunction::New(env, cb, "MacOSGlobalScrollMonitor", 0, 1);
|
|
45
|
+
|
|
46
|
+
// 调用 AppKit API 添加全局滚动监控
|
|
47
|
+
_eventMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSEventMaskScrollWheel
|
|
48
|
+
handler:^(NSEvent* event) {
|
|
49
|
+
// 获取修饰键掩码
|
|
50
|
+
NSEventModifierFlags flags = [event modifierFlags];
|
|
51
|
+
|
|
52
|
+
// 填充数据结构
|
|
53
|
+
ScrollEventData data;
|
|
54
|
+
data.deltaY = [event deltaY];
|
|
55
|
+
data.command = (flags & NSEventModifierFlagCommand) != 0;
|
|
56
|
+
data.shift = (flags & NSEventModifierFlagShift) != 0;
|
|
57
|
+
data.option = (flags & NSEventModifierFlagOption) != 0;
|
|
58
|
+
data.ctrl = (flags & NSEventModifierFlagControl) != 0;
|
|
59
|
+
data.fn = (flags & NSEventModifierFlagFunction) != 0; // 原生支持 fn 键监测
|
|
60
|
+
|
|
61
|
+
// 将数据异步推送到 Node.js 主线程
|
|
62
|
+
_tsfn.BlockingCall([data](Napi::Env env, Napi::Function jsCallback) {
|
|
63
|
+
Napi::Object obj = Napi::Object::New(env);
|
|
64
|
+
obj.Set("deltaY", Napi::Number::New(env, data.deltaY));
|
|
65
|
+
obj.Set("command", Napi::Boolean::New(env, data.command));
|
|
66
|
+
obj.Set("shift", Napi::Boolean::New(env, data.shift));
|
|
67
|
+
obj.Set("option", Napi::Boolean::New(env, data.option));
|
|
68
|
+
obj.Set("ctrl", Napi::Boolean::New(env, data.ctrl));
|
|
69
|
+
obj.Set("fn", Napi::Boolean::New(env, data.fn));
|
|
70
|
+
|
|
71
|
+
jsCallback.Call({ obj });
|
|
72
|
+
});
|
|
73
|
+
}];
|
|
74
|
+
|
|
75
|
+
return Napi::Boolean::New(env, true);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 停止监听逻辑
|
|
79
|
+
Napi::Value Stop(const Napi::CallbackInfo& info) {
|
|
80
|
+
// 移除 macOS 系统级监听器
|
|
81
|
+
if (_eventMonitor != nil) {
|
|
82
|
+
[NSEvent removeMonitor:_eventMonitor];
|
|
83
|
+
_eventMonitor = nil;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 安全释放线程安全函数,并显式置空
|
|
87
|
+
if (_tsfn) {
|
|
88
|
+
_tsfn.Release();
|
|
89
|
+
_tsfn = nullptr;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return info.Env().Undefined();
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// 导出插件,类名为 ScrollMonitor
|
|
97
|
+
NODE_API_ADDON(ScrollMonitor)
|