1 | const EventEmitter = require('events');
|
2 | const path = require('path');
|
3 |
|
4 | const runtime = process.versions['electron'] ? 'electron' : 'node';
|
5 | const essential =
|
6 | runtime +
|
7 | '-v' +
|
8 | process.versions.modules +
|
9 | '-' +
|
10 | process.platform +
|
11 | '-' +
|
12 | process.arch;
|
13 | const modulePath = path.join(
|
14 | __dirname,
|
15 | 'builds',
|
16 | essential,
|
17 | 'build',
|
18 | 'Release',
|
19 | 'iohook.node'
|
20 | );
|
21 | if (process.env.DEBUG) {
|
22 | console.info('Loading native binary:', modulePath);
|
23 | }
|
24 | let NodeHookAddon = require(modulePath);
|
25 |
|
26 | const events = {
|
27 | 3: 'keypress',
|
28 | 4: 'keydown',
|
29 | 5: 'keyup',
|
30 | 6: 'mouseclick',
|
31 | 7: 'mousedown',
|
32 | 8: 'mouseup',
|
33 | 9: 'mousemove',
|
34 | 10: 'mousedrag',
|
35 | 11: 'mousewheel',
|
36 | };
|
37 |
|
38 | class IOHook extends EventEmitter {
|
39 | constructor() {
|
40 | super();
|
41 | this.active = false;
|
42 | this.shortcuts = [];
|
43 | this.eventProperty = 'keycode';
|
44 | this.activatedShortcuts = [];
|
45 |
|
46 | this.lastKeydownShift = false;
|
47 | this.lastKeydownAlt = false;
|
48 | this.lastKeydownCtrl = false;
|
49 | this.lastKeydownMeta = false;
|
50 |
|
51 | this.load();
|
52 | this.setDebug(false);
|
53 | }
|
54 |
|
55 | |
56 |
|
57 |
|
58 |
|
59 | start(enableLogger) {
|
60 | if (!this.active) {
|
61 | this.active = true;
|
62 | this.setDebug(enableLogger);
|
63 | }
|
64 | }
|
65 |
|
66 | |
67 |
|
68 |
|
69 | stop() {
|
70 | if (this.active) {
|
71 | this.active = false;
|
72 | }
|
73 | }
|
74 |
|
75 | |
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | registerShortcut(keys, callback, releaseCallback) {
|
83 | let shortcut = {};
|
84 | let shortcutId = Date.now() + Math.random();
|
85 | keys.forEach((keyCode) => {
|
86 | shortcut[keyCode] = false;
|
87 | });
|
88 | shortcut.id = shortcutId;
|
89 | shortcut.callback = callback;
|
90 | shortcut.releaseCallback = releaseCallback;
|
91 | this.shortcuts.push(shortcut);
|
92 | return shortcutId;
|
93 | }
|
94 |
|
95 | |
96 |
|
97 |
|
98 |
|
99 | unregisterShortcut(shortcutId) {
|
100 | this.shortcuts.forEach((shortcut, i) => {
|
101 | if (shortcut.id === shortcutId) {
|
102 | this.shortcuts.splice(i, 1);
|
103 | }
|
104 | });
|
105 | }
|
106 |
|
107 | |
108 |
|
109 |
|
110 |
|
111 | unregisterShortcutByKeys(keyCodes) {
|
112 |
|
113 | for (let i = 0; i < this.shortcuts.length; i++) {
|
114 | let shortcut = this.shortcuts[i];
|
115 |
|
116 |
|
117 | keyCodes.forEach((key, index) => {
|
118 | if (typeof key !== 'string' && !(key instanceof String)) {
|
119 |
|
120 | keyCodes[index] = key.toString();
|
121 | }
|
122 | });
|
123 |
|
124 |
|
125 | Object.keys(shortcut).every((key) => {
|
126 | if (key === 'callback' || key === 'id') return;
|
127 |
|
128 |
|
129 |
|
130 |
|
131 | let index = keyCodes.indexOf(key);
|
132 | if (index === -1) return false;
|
133 |
|
134 |
|
135 | keyCodes.splice(index, 1);
|
136 | return true;
|
137 | });
|
138 |
|
139 |
|
140 | if (keyCodes.length === 0) {
|
141 |
|
142 | this.shortcuts.splice(i, 1);
|
143 | return;
|
144 | }
|
145 | }
|
146 | }
|
147 |
|
148 | |
149 |
|
150 |
|
151 | unregisterAllShortcuts() {
|
152 | this.shortcuts.splice(0, this.shortcuts.length);
|
153 | }
|
154 |
|
155 | |
156 |
|
157 |
|
158 | load() {
|
159 | NodeHookAddon.startHook(this._handler.bind(this), this.debug || false);
|
160 | }
|
161 |
|
162 | |
163 |
|
164 |
|
165 | unload() {
|
166 | this.stop();
|
167 | NodeHookAddon.stopHook();
|
168 | }
|
169 |
|
170 | |
171 |
|
172 |
|
173 |
|
174 | setDebug(mode) {
|
175 | NodeHookAddon.debugEnable(mode);
|
176 | }
|
177 |
|
178 | |
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 | useRawcode(using) {
|
187 |
|
188 | this.eventProperty = using ? 'rawcode' : 'keycode';
|
189 | }
|
190 |
|
191 | |
192 |
|
193 |
|
194 |
|
195 | disableClickPropagation() {
|
196 | NodeHookAddon.grabMouseClick(true);
|
197 | }
|
198 |
|
199 | |
200 |
|
201 |
|
202 |
|
203 | enableClickPropagation() {
|
204 | NodeHookAddon.grabMouseClick(false);
|
205 | }
|
206 |
|
207 | |
208 |
|
209 |
|
210 |
|
211 |
|
212 | _handler(msg) {
|
213 | if (this.active === false || !msg) return;
|
214 |
|
215 | if (events[msg.type]) {
|
216 | const event = msg.mouse || msg.keyboard || msg.wheel;
|
217 |
|
218 | event.type = events[msg.type];
|
219 |
|
220 | this._handleShift(event);
|
221 | this._handleAlt(event);
|
222 | this._handleCtrl(event);
|
223 | this._handleMeta(event);
|
224 |
|
225 | this.emit(events[msg.type], event);
|
226 |
|
227 |
|
228 | if (
|
229 | (event.type === 'keydown' || event.type === 'keyup') &&
|
230 | iohook.shortcuts.length > 0
|
231 | ) {
|
232 | this._handleShortcut(event);
|
233 | }
|
234 | }
|
235 | }
|
236 |
|
237 | |
238 |
|
239 |
|
240 |
|
241 |
|
242 |
|
243 | _handleShift(event) {
|
244 | if (event.type === 'keyup' && event.shiftKey) {
|
245 | this.lastKeydownShift = false;
|
246 | }
|
247 |
|
248 | if (event.type === 'keydown' && event.shiftKey) {
|
249 | this.lastKeydownShift = true;
|
250 | }
|
251 |
|
252 | if (this.lastKeydownShift) {
|
253 | event.shiftKey = true;
|
254 | }
|
255 | }
|
256 |
|
257 | |
258 |
|
259 |
|
260 |
|
261 |
|
262 |
|
263 | _handleAlt(event) {
|
264 | if (event.type === 'keyup' && event.altKey) {
|
265 | this.lastKeydownAlt = false;
|
266 | }
|
267 |
|
268 | if (event.type === 'keydown' && event.altKey) {
|
269 | this.lastKeydownAlt = true;
|
270 | }
|
271 |
|
272 | if (this.lastKeydownAlt) {
|
273 | event.altKey = true;
|
274 | }
|
275 | }
|
276 |
|
277 | |
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 | _handleCtrl(event) {
|
284 | if (event.type === 'keyup' && event.ctrlKey) {
|
285 | this.lastKeydownCtrl = false;
|
286 | }
|
287 |
|
288 | if (event.type === 'keydown' && event.ctrlKey) {
|
289 | this.lastKeydownCtrl = true;
|
290 | }
|
291 |
|
292 | if (this.lastKeydownCtrl) {
|
293 | event.ctrlKey = true;
|
294 | }
|
295 | }
|
296 |
|
297 | |
298 |
|
299 |
|
300 |
|
301 |
|
302 |
|
303 | _handleMeta(event) {
|
304 | if (event.type === 'keyup' && event.metaKey) {
|
305 | this.lastKeydownMeta = false;
|
306 | }
|
307 |
|
308 | if (event.type === 'keydown' && event.metaKey) {
|
309 | this.lastKeydownMeta = true;
|
310 | }
|
311 |
|
312 | if (this.lastKeydownMeta) {
|
313 | event.metaKey = true;
|
314 | }
|
315 | }
|
316 |
|
317 | |
318 |
|
319 |
|
320 |
|
321 |
|
322 | _handleShortcut(event) {
|
323 | if (this.active === false) {
|
324 | return;
|
325 | }
|
326 |
|
327 |
|
328 | let activatedShortcuts = this.activatedShortcuts;
|
329 |
|
330 | if (event.type === 'keydown') {
|
331 | this.shortcuts.forEach((shortcut) => {
|
332 | if (shortcut[event[this.eventProperty]] !== undefined) {
|
333 |
|
334 | shortcut[event[this.eventProperty]] = true;
|
335 |
|
336 | let keysTmpArray = [];
|
337 | let callme = true;
|
338 |
|
339 |
|
340 | Object.keys(shortcut).forEach((key) => {
|
341 | if (key === 'callback' || key === 'releaseCallback' || key === 'id')
|
342 | return;
|
343 |
|
344 |
|
345 | if (shortcut[key] === false) {
|
346 |
|
347 | callme = false;
|
348 | keysTmpArray.splice(0, keysTmpArray.length);
|
349 |
|
350 | return;
|
351 | }
|
352 |
|
353 |
|
354 |
|
355 |
|
356 | keysTmpArray.push(key);
|
357 | });
|
358 | if (callme) {
|
359 | shortcut.callback(keysTmpArray);
|
360 |
|
361 |
|
362 |
|
363 | if (activatedShortcuts.indexOf(shortcut) === -1) {
|
364 | activatedShortcuts.push(shortcut);
|
365 | }
|
366 | }
|
367 | }
|
368 | });
|
369 | } else if (event.type === 'keyup') {
|
370 |
|
371 | this.shortcuts.forEach((shortcut) => {
|
372 | if (shortcut[event[this.eventProperty]] !== undefined) {
|
373 | shortcut[event[this.eventProperty]] = false;
|
374 | }
|
375 | });
|
376 |
|
377 |
|
378 |
|
379 |
|
380 | this.activatedShortcuts.forEach((shortcut) => {
|
381 | if (shortcut[event[this.eventProperty]] === undefined) return;
|
382 |
|
383 | let shortcutReleased = true;
|
384 | let keysTmpArray = [];
|
385 | Object.keys(shortcut).forEach((key) => {
|
386 | if (key === 'callback' || key === 'releaseCallback' || key === 'id')
|
387 | return;
|
388 | keysTmpArray.push(key);
|
389 |
|
390 |
|
391 |
|
392 | if (shortcut[key]) {
|
393 | shortcutReleased = false;
|
394 | }
|
395 | });
|
396 |
|
397 | if (shortcutReleased) {
|
398 |
|
399 | if (shortcut.releaseCallback) {
|
400 | shortcut.releaseCallback(keysTmpArray);
|
401 | }
|
402 |
|
403 |
|
404 | const index = this.activatedShortcuts.indexOf(shortcut);
|
405 | if (index !== -1) this.activatedShortcuts.splice(index, 1);
|
406 | }
|
407 | });
|
408 | }
|
409 | }
|
410 | }
|
411 |
|
412 | const iohook = new IOHook();
|
413 |
|
414 | module.exports = iohook;
|