├── .gitignore ├── .vscode └── settings.json ├── mouse.js ├── example.js ├── README.md ├── tsconfig.json ├── binding.gyp ├── tslint.json ├── LICENSE ├── package.json ├── src └── index.ts └── lib └── windows.cc /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log* 3 | build 4 | dist 5 | .vscode/ipch -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "*.rh": "cpp" 4 | } 5 | } -------------------------------------------------------------------------------- /mouse.js: -------------------------------------------------------------------------------- 1 | const addon = require("bindings")("addon"); 2 | 3 | addon.createMouseHook((event, x, y, which) => { 4 | process.send({ event, x, y, button: which }); 5 | }); 6 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | const mouseEvents = require("./dist/index"); 2 | 3 | mouseEvents.default.on("mouse-up", data => { 4 | console.log(data); 5 | }); 6 | 7 | mouseEvents.default.on("mouse-move", data => { 8 | console.log(data); 9 | }); 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mouse-hooks 2 | 3 | Global mouse events listener for Node.js 4 | 5 | # Installation 6 | 7 | ```bash 8 | $ npm install mouse-hooks 9 | ``` 10 | 11 | # Quick example 12 | 13 | ```ts 14 | import mouseHooks from "mouse-hooks"; 15 | 16 | mouseHooks.on("mouse-up", data => { 17 | console.log(data.x, data.y, data.button); 18 | }); 19 | ``` 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./dist/", 4 | "noImplicitAny": true, 5 | "module": "commonjs", 6 | "target": "es2015", 7 | "sourceMap": true, 8 | "moduleResolution": "node", 9 | "allowSyntheticDefaultImports": true, 10 | "declaration": true, 11 | "types": ["node"] 12 | }, 13 | "include": ["./src/**/*.ts"], 14 | "exclude": ["node_modules/**/*"] 15 | } 16 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "cflags!": [ "-fno-exceptions" ], 6 | "cflags_cc!": [ "-fno-exceptions" ], 7 | "conditions": [ 8 | ["OS=='win'", { 9 | "sources": [ "lib/windows.cc" ] 10 | }], 11 | ], 12 | "include_dirs": [ 13 | " { 17 | if (registeredEvents.indexOf(event) !== -1) return; 18 | 19 | if ( 20 | (event === "mouse-up" || 21 | event === "mouse-down" || 22 | event === "mouse-move") && 23 | !mouseProcess 24 | ) { 25 | mouseProcess = fork(join(__dirname, "../mouse.js")); 26 | 27 | mouseProcess.on("message", msg => { 28 | this.emit(msg.event, { x: msg.x, y: msg.y, button: msg.button }); 29 | }); 30 | } else { 31 | return; 32 | } 33 | 34 | registeredEvents.push(event); 35 | }); 36 | 37 | this.on("removeListener", event => { 38 | if (this.listenerCount(event) > 0) return; 39 | 40 | if ( 41 | (event === "mouse-up" || 42 | event === "mouse-down" || 43 | event === "mouse-move") && 44 | mouseProcess 45 | ) { 46 | mouseProcess.kill(); 47 | } 48 | 49 | registeredEvents = registeredEvents.filter(x => x !== event); 50 | }); 51 | } 52 | } 53 | 54 | const mouseEvents = new MouseEvents(); 55 | 56 | export default mouseEvents; 57 | -------------------------------------------------------------------------------- /lib/windows.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | Napi::CallbackInfo *_info; 6 | 7 | HHOOK _hook; 8 | 9 | LRESULT CALLBACK HookCallback(int nCode, WPARAM wParam, LPARAM lParam) { 10 | auto cb = (*_info)[0].As(); 11 | 12 | if (nCode >= 0) { 13 | MSLLHOOKSTRUCT *data = (MSLLHOOKSTRUCT *)lParam; 14 | 15 | auto x = Napi::Number::New((*_info).Env(), data->pt.x); 16 | auto y = Napi::Number::New((*_info).Env(), data->pt.y); 17 | 18 | auto name = ""; 19 | auto which = ""; 20 | 21 | if (wParam == WM_LBUTTONUP || wParam == WM_LBUTTONDOWN) { 22 | which = "mouse1"; 23 | } else if (wParam == WM_RBUTTONUP || wParam == WM_RBUTTONDOWN) { 24 | which = "mouse2"; 25 | } 26 | 27 | if (wParam == WM_LBUTTONUP || wParam == WM_RBUTTONUP) { 28 | name = "mouse-up"; 29 | } else if (wParam == WM_LBUTTONDOWN || wParam == WM_RBUTTONDOWN) { 30 | name = "mouse-down"; 31 | } else if (wParam == WM_MOUSEMOVE) { 32 | name = "mouse-move"; 33 | } 34 | 35 | if (name != "") { 36 | cb.Call((*_info).Env().Global(), 37 | {Napi::String::New((*_info).Env(), name), x, y, 38 | Napi::String::New((*_info).Env(), which)}); 39 | } 40 | } 41 | 42 | return CallNextHookEx(_hook, nCode, wParam, lParam); 43 | } 44 | 45 | Napi::Boolean createMouseHook(const Napi::CallbackInfo &info) { 46 | _info = &(const_cast(info)); 47 | 48 | _hook = SetWindowsHookEx(WH_MOUSE_LL, HookCallback, NULL, 0); 49 | 50 | MSG msg; 51 | while (GetMessage(&msg, NULL, 0, 0) > 0) { 52 | TranslateMessage(&msg); 53 | DispatchMessage(&msg); 54 | } 55 | 56 | return Napi::Boolean::New(info.Env(), true); 57 | } 58 | 59 | Napi::Object Init(Napi::Env env, Napi::Object exports) { 60 | exports.Set(Napi::String::New(env, "createMouseHook"), 61 | Napi::Function::New(env, createMouseHook)); 62 | 63 | return exports; 64 | } 65 | 66 | NODE_API_MODULE(addon, Init) --------------------------------------------------------------------------------