macos-scroll-monitor 1.0.1 → 1.0.2
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/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/macos_scroll_monitor.mm +43 -60
package/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export type ScrollCallback = (event: ScrollEvent) => void;
|
|
|
26
26
|
* @param callback 每次滚动时触发的回调函数
|
|
27
27
|
* @returns 启动成功返回 true,如果已在运行则返回 undefined 或 false
|
|
28
28
|
*/
|
|
29
|
-
export function start(callback: ScrollCallback): boolean
|
|
29
|
+
export function start(callback: ScrollCallback): boolean;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* 停止全局滚动监听并释放原生资源
|
package/package.json
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
class ScrollMonitor : public Napi::Addon<ScrollMonitor> {
|
|
5
5
|
public:
|
|
6
|
-
// 构造函数:关联 JavaScript 方法与 C++ 实现
|
|
7
6
|
ScrollMonitor(Napi::Env env, Napi::Object exports) {
|
|
8
7
|
DefineAddon(exports, {
|
|
9
8
|
InstanceMethod("start", &ScrollMonitor::Start),
|
|
@@ -12,86 +11,70 @@ public:
|
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
private:
|
|
15
|
-
id
|
|
16
|
-
|
|
14
|
+
id _globalMonitor = nil;
|
|
15
|
+
id _localMonitor = nil;
|
|
16
|
+
Napi::ThreadSafeFunction _tsfn;
|
|
17
17
|
|
|
18
|
-
// 内部结构体:封装需要传回 JS 的事件数据
|
|
19
18
|
struct ScrollEventData {
|
|
20
19
|
double deltaY;
|
|
21
|
-
bool command;
|
|
22
|
-
bool shift;
|
|
23
|
-
bool option;
|
|
24
|
-
bool ctrl;
|
|
25
|
-
bool fn;
|
|
20
|
+
bool command, shift, option, ctrl, fn;
|
|
26
21
|
};
|
|
27
22
|
|
|
28
|
-
//
|
|
23
|
+
// 内部通用的事件分发逻辑
|
|
24
|
+
void Dispatch(NSEvent* event) {
|
|
25
|
+
if (!_tsfn) return;
|
|
26
|
+
|
|
27
|
+
NSEventModifierFlags flags = [event modifierFlags];
|
|
28
|
+
ScrollEventData data;
|
|
29
|
+
data.deltaY = [event deltaY];
|
|
30
|
+
data.command = (flags & NSEventModifierFlagCommand) != 0;
|
|
31
|
+
data.shift = (flags & NSEventModifierFlagShift) != 0;
|
|
32
|
+
data.option = (flags & NSEventModifierFlagOption) != 0;
|
|
33
|
+
data.ctrl = (flags & NSEventModifierFlagControl) != 0;
|
|
34
|
+
data.fn = (flags & NSEventModifierFlagFunction) != 0;
|
|
35
|
+
|
|
36
|
+
_tsfn.BlockingCall([data](Napi::Env env, Napi::Function jsCallback) {
|
|
37
|
+
Napi::Object obj = Napi::Object::New(env);
|
|
38
|
+
obj.Set("deltaY", Napi::Number::New(env, data.deltaY));
|
|
39
|
+
obj.Set("command", Napi::Boolean::New(env, data.command));
|
|
40
|
+
obj.Set("shift", Napi::Boolean::New(env, data.shift));
|
|
41
|
+
obj.Set("option", Napi::Boolean::New(env, data.option));
|
|
42
|
+
obj.Set("ctrl", Napi::Boolean::New(env, data.ctrl));
|
|
43
|
+
obj.Set("fn", Napi::Boolean::New(env, data.fn));
|
|
44
|
+
jsCallback.Call({ obj });
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
29
48
|
Napi::Value Start(const Napi::CallbackInfo& info) {
|
|
30
49
|
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
|
-
}
|
|
50
|
+
if (_globalMonitor || _localMonitor || _tsfn) return Napi::Boolean::New(env, false);
|
|
40
51
|
|
|
41
52
|
Napi::Function cb = info[0].As<Napi::Function>();
|
|
42
|
-
|
|
43
|
-
// 创建线程安全函数。Resource Name 设为 MacOSGlobalScrollMonitor 方便调试
|
|
44
53
|
_tsfn = Napi::ThreadSafeFunction::New(env, cb, "MacOSGlobalScrollMonitor", 0, 1);
|
|
45
54
|
|
|
46
|
-
//
|
|
47
|
-
|
|
55
|
+
// 1. 监听外部窗口(Global)
|
|
56
|
+
_globalMonitor = [NSEvent addGlobalMonitorForEventsMatchingMask:NSEventMaskScrollWheel
|
|
48
57
|
handler:^(NSEvent* event) {
|
|
49
|
-
|
|
50
|
-
|
|
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 键监测
|
|
58
|
+
Dispatch(event);
|
|
59
|
+
}];
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
});
|
|
61
|
+
// 2. 监听自身窗口(Local)
|
|
62
|
+
// 因为不需要穿透,我们在这里依然处理逻辑,并返回 event 确保窗口内 UI 响应正常
|
|
63
|
+
_localMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskScrollWheel
|
|
64
|
+
handler:^NSEvent*(NSEvent* event) {
|
|
65
|
+
Dispatch(event);
|
|
66
|
+
return event;
|
|
73
67
|
}];
|
|
74
68
|
|
|
75
69
|
return Napi::Boolean::New(env, true);
|
|
76
70
|
}
|
|
77
71
|
|
|
78
|
-
// 停止监听逻辑
|
|
79
72
|
Napi::Value Stop(const Napi::CallbackInfo& info) {
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
_eventMonitor = nil;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 安全释放线程安全函数,并显式置空
|
|
87
|
-
if (_tsfn) {
|
|
88
|
-
_tsfn.Release();
|
|
89
|
-
_tsfn = nullptr;
|
|
90
|
-
}
|
|
91
|
-
|
|
73
|
+
if (_globalMonitor) { [NSEvent removeMonitor:_globalMonitor]; _globalMonitor = nil; }
|
|
74
|
+
if (_localMonitor) { [NSEvent removeMonitor:_localMonitor]; _localMonitor = nil; }
|
|
75
|
+
if (_tsfn) { _tsfn.Release(); _tsfn = nullptr; }
|
|
92
76
|
return info.Env().Undefined();
|
|
93
77
|
}
|
|
94
78
|
};
|
|
95
79
|
|
|
96
|
-
// 导出插件,类名为 ScrollMonitor
|
|
97
80
|
NODE_API_ADDON(ScrollMonitor)
|