From a87720602ed91fbc48770d968b725a165d1a387c Mon Sep 17 00:00:00 2001 From: Luis Blanco Date: Sun, 3 Dec 2023 20:44:03 +0400 Subject: [PATCH] Adjust types --- examples/main.ts | 28 ++--- index.d.ts | 105 +++++++++++++------ js/index.js | 259 +++++++++++++++++++++-------------------------- 3 files changed, 197 insertions(+), 195 deletions(-) diff --git a/examples/main.ts b/examples/main.ts index d2f9e73..5f19e29 100644 --- a/examples/main.ts +++ b/examples/main.ts @@ -27,33 +27,27 @@ iohook.on('mousewheel', (msg) => { console.log(msg); }); -iohook.on('mousemove', (msg) => { - // console.log(msg); -}); +// iohook.on('mousemove', (msg) => { +// console.log(msg); +// }); iohook.on('mousedrag', (msg) => { console.log(msg); }); -// const CTRL = 29; -// const ALT = 56; -// const F7 = 65; - -// iohook.registerShortcut([CTRL, F7], (keys) => { -// console.log('Shortcut pressed with keys:', keys); -// }); +const CTRL = 29; +const F7 = 65; -// let shId = iohook.registerShortcut([ALT, F7], (keys) => { -// console.log('This shortcut will be called once. Keys:', keys); -// iohook.unregisterShortcut(shId); -// }); +iohook.shortcut([CTRL, F7], (keys) => { + console.log('Shortcut pressed with keys:', keys); +}); iohook.on('mouseup', (event) => console.log(event)); iohook.start(); // iohook.setDebug(true); // Uncomment this line for see all debug information from iohook -console.log('Hook started. Try input.'); +console.log('Hook started. Try input. CTRL+F7 shortcut.'); setTimeout(() => { @@ -68,6 +62,6 @@ setTimeout(() => { console.log('Hook stopped.'); iohook.stop(); }, 3000); - }, 3000); -}, 3000); + }, 5000); +}, 5000); diff --git a/index.d.ts b/index.d.ts index 3f85697..faa6228 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2,18 +2,59 @@ declare module "iohook-raub" { type EventEmitter = import('node:events').EventEmitter; - // type TEvent = { - // type: string; - // keychar?: number; - // keycode?: number; - // rawcode?: number; - // button?: number; - // clicks?: number; - // x?: number; - // y?: number; - // } + type THookEventCommon = { + mask: number; + time: number; + }; + + type TEventNameKeyboard = 'keypress' | 'keydown' | 'keyup'; + + type TEventNameMouse = 'mouseclick' | 'mousedown' | 'mouseup' | 'mousemove' | 'mousedrag'; + + type TEventNameWheel = 'mousewheel'; + + type THookEventKeyboard = THookEventCommon & { + type: TEventNameKeyboard; + shiftKey: boolean; + altKey: boolean; + ctrlKey: boolean; + metaKey: boolean; + keychar: number; + keycode: number; + rawcode: number; + }; + + type THookEventMouse = THookEventCommon & { + type: TEventNameMouse; + button: number; + clicks: number; + x: number; + y: number; + }; + + type THookEventWheel = THookEventCommon & { + type: TEventNameWheel; + amount: number; + clicks: number; + direction: number; + rotation: number; + x: number; + y: number; + }; + + interface TPossibleSubscriptions { + addListener(eventName: TEventNameKeyboard, listener: (event: THookEventKeyboard) => void): this; + addListener(eventName: TEventNameMouse, listener: (event: THookEventMouse) => void): this; + addListener(eventName: TEventNameWheel, listener: (event: THookEventWheel) => void): this; + on(eventName: TEventNameKeyboard, listener: (event: THookEventKeyboard) => void): this; + on(eventName: TEventNameMouse, listener: (event: THookEventMouse) => void): this; + on(eventName: TEventNameWheel, listener: (event: THookEventWheel) => void): this; + once(eventName: TEventNameKeyboard, listener: (event: THookEventKeyboard) => void): this; + once(eventName: TEventNameMouse, listener: (event: THookEventMouse) => void): this; + once(eventName: TEventNameWheel, listener: (event: THookEventWheel) => void): this; + } - type IoHook = EventEmitter & { + type IoHook = Omit & TPossibleSubscriptions & { /** * Start hooking engine. Call it when you ready to receive events * If `enableLogger` is true, module will publish debug information to stdout @@ -40,34 +81,34 @@ declare module "iohook-raub" { useRawcode(using: boolean): void; /** - * Register global shortcut. When all keys in keys array pressed, callback will be called - * @param {Array} keys Array of keycodes - * @param {Function} callback Callback for when shortcut pressed - * @param {Function} [releaseCallback] Callback for when shortcut released - * @return {number} ShortcutId for unregister - */ - registerShortcut( - keys: Array, - callback: Function, - releaseCallback?: Function - ): number; - - /** - * Unregister shortcut by ShortcutId - * @param {number} shortcutId + * Register a global shortcut. When all keys in keys array pressed, callback will be called + * + * @returns Function that removes this specific shortcut. */ - unregisterShortcut(shortcutId: number): void; + shortcut( + /** + * Array of keycodes. + */ + keys: number[], + /** + * Callback for when shortcut pressedю + */ + onDown: (keys: number[]) => void, + /** + * Callback for when shortcut releasedю + */ + onUp?: (keys: number[]) => void + ): (() => void); /** - * Unregister shortcut via its key codes - * @param {Array} keys + * Unregister shortcut via its key codes. */ - unregisterShortcut(keys: Array): void; + removeShortcut(keys: number[]): void; /** - * Unregister all shortcuts + * Unregister all shortcuts. */ - unregisterAllShortcuts(): void; + clearShortcuts(): void; }; const iohook: IoHook; diff --git a/js/index.js b/js/index.js index b7e813f..5dbb7aa 100644 --- a/js/index.js +++ b/js/index.js @@ -33,7 +33,6 @@ const state = { /** * Handles the shift key. Whenever shift is pressed, all future events would * contain { shiftKey: true } in its object, until the shift key is released. - * @param event Event object */ const handleShift = (event) => { if (event.type === 'keyup' && event.shiftKey) { @@ -52,7 +51,6 @@ const handleShift = (event) => { /** * Handles the alt key. Whenever alt is pressed, all future events would * contain { altKey: true } in its object, until the alt key is released. - * @param event Event object */ const handleAlt = (event) => { if (event.type === 'keyup' && event.altKey) { @@ -71,7 +69,6 @@ const handleAlt = (event) => { /** * Handles the ctrl key. Whenever ctrl is pressed, all future events would * contain { ctrlKey: true } in its object, until the ctrl key is released. - * @param event Event object */ const handleCtrl = (event) => { if (event.type === 'keyup' && event.ctrlKey) { @@ -90,7 +87,6 @@ const handleCtrl = (event) => { /** * Handles the meta key. Whenever meta is pressed, all future events would * contain { metaKey: true } in its object, until the meta key is released. - * @param event Event object */ const handleMeta = (event) => { if (event.type === 'keyup' && event.metaKey) { @@ -106,128 +102,128 @@ const handleMeta = (event) => { } }; +const nonCodeKeys = ['onDown', 'onUp', 'id']; + /** - * Local shortcut event handler - * @param event Event object - * @private + * Local shortcut event handler. Only handles keyboard. */ const handleShortcut = (event) => { - if (state.active === false) { + if (state.active === false || (event.type !== 'keydown' && event.type !== 'keyup')) { return; } // Keep track of shortcuts that are currently active - let activatedShortcuts = state.activatedShortcuts; + const activatedShortcuts = state.activatedShortcuts; if (event.type === 'keydown') { state.shortcuts.forEach((shortcut) => { - if (shortcut[event[state.eventProperty]] !== undefined) { - // Mark this key as currently being pressed - shortcut[event[state.eventProperty]] = true; - - let keysTmpArray = []; - let callme = true; - - // Iterate through each keyboard key in this shortcut - Object.keys(shortcut).forEach((key) => { - if (key === 'callback' || key === 'releaseCallback' || key === 'id') - return; - - // If one of the keys aren't pressed... - if (shortcut[key] === false) { - // Don't call the callback and empty our temp tracking array - callme = false; - keysTmpArray.splice(0, keysTmpArray.length); - - return; - } - - // Otherwise, this key is being pressed. - // Add it to the array of keyboard keys we will send as an argument - // to our callback - keysTmpArray.push(key); - }); - if (callme) { - shortcut.callback(keysTmpArray); - - // Add this shortcut from our activate shortcuts array if not - // already activated - if (activatedShortcuts.indexOf(shortcut) === -1) { - activatedShortcuts.push(shortcut); - } - } - } - }); - } else if (event.type === 'keyup') { - // Mark this key as currently not being pressed in all of our shortcuts - state.shortcuts.forEach((shortcut) => { - if (shortcut[event[state.eventProperty]] !== undefined) { - shortcut[event[state.eventProperty]] = false; + if (shortcut[event[state.eventProperty]] === undefined) { + return; } - }); - - // Check if any of our currently pressed shortcuts have been released - // "released" means that all of the keys that the shortcut defines are no - // longer being pressed - state.activatedShortcuts.forEach((shortcut) => { - if (shortcut[event[state.eventProperty]] === undefined) return; - let shortcutReleased = true; - let keysTmpArray = []; + // Mark this key as currently being pressed + shortcut[event[state.eventProperty]] = true; + + const keysTmpArray = []; + let isTriggered = true; + + // Iterate through each keyboard key in this shortcut Object.keys(shortcut).forEach((key) => { - if (key === 'callback' || key === 'releaseCallback' || key === 'id') + if (nonCodeKeys.includes(key)) { return; - keysTmpArray.push(key); + } - // If any key is true, and thus still pressed, the shortcut is still - // being held - if (shortcut[key]) { - shortcutReleased = false; + // If one of the keys aren't pressed... + if (shortcut[key] === false) { + // Don't call the callback and empty our temp tracking array + isTriggered = false; + keysTmpArray.splice(0, keysTmpArray.length); + return; } + + // Add to the array of keys that we will send as an argument + keysTmpArray.push(+key); }); - if (shortcutReleased) { - // Call the released function handler - if (shortcut.releaseCallback) { - shortcut.releaseCallback(keysTmpArray); - } - - // Remove this shortcut from our activate shortcuts array - const index = state.activatedShortcuts.indexOf(shortcut); - if (index !== -1) state.activatedShortcuts.splice(index, 1); + if (!isTriggered) { + return; + } + + shortcut.onDown(keysTmpArray); + + // Add to `activatedShortcuts` if not already there + if (!activatedShortcuts.includes(shortcut)) { + activatedShortcuts.push(shortcut); } }); + return; } + + // --- KEY UP + + // Mark this key as currently not being pressed in all of our shortcuts + state.shortcuts.forEach((shortcut) => { + if (shortcut[event[state.eventProperty]] !== undefined) { + shortcut[event[state.eventProperty]] = false; + } + }); + + // Check if any of our currently pressed shortcuts have been released + // "released" means that all of the keys that the shortcut defines are no + // longer being pressed + state.activatedShortcuts.forEach((shortcut) => { + if (shortcut[event[state.eventProperty]] === undefined) { + return; + } + + let shortcutReleased = true; + let keysTmpArray = []; + Object.keys(shortcut).forEach((key) => { + if (nonCodeKeys.includes(key)) { + return; + } + keysTmpArray.push(+key); + + // If any key is true, and thus still pressed, the shortcut is still + // being held + if (shortcut[key]) { + shortcutReleased = false; + } + }); + + if (!shortcutReleased) { + return; + } + + // Call the released function handler + shortcut?.onUp(keysTmpArray); + + // Remove this shortcut from our activate shortcuts array + state.activatedShortcuts = state.activatedShortcuts.filter((s) => (s !== shortcut)); + }); }; /** * Local event handler. - * @param msg Raw event message */ const handler = (msg) => { - if (state.active === false || !msg) { + if (state.active === false || !msg || !events[msg.type]) { return; } - if (events[msg.type]) { - const event = msg.mouse || msg.keyboard || msg.wheel; - - event.type = events[msg.type]; - - handleShift(event); - handleAlt(event); - handleCtrl(event); - handleMeta(event); - - iohook.emit(events[msg.type], event); - - // If there is any registered shortcuts then handle them. - if ( - (event.type === 'keydown' || event.type === 'keyup') && - iohook.shortcuts.length > 0 - ) { - handleShortcut(event); - } + const event = msg.mouse || msg.keyboard || msg.wheel; + event.type = events[msg.type]; + + handleShift(event); + handleAlt(event); + handleCtrl(event); + handleMeta(event); + + iohook.emit(event.type, event); + + // If there is any registered shortcuts then handle them. + if (state.shortcuts.length > 0) { + handleShortcut(event); } }; @@ -249,66 +245,37 @@ Object.assign(iohook, { addon.stopHook(); }, - registerShortcut(keys, onDown, onUp) { + shortcut(keys, onDown, onUp) { const shortcut = {}; - const shortcutId = Date.now() + Math.random(); + const shortcutId = `${Date.now()}-${Math.random()}`; keys.forEach((keyCode) => { - shortcut[keyCode] = false; + shortcut[`${keyCode}`] = false; }); shortcut.id = shortcutId; - shortcut.callback = onDown; - shortcut.releaseCallback = onUp; + shortcut.onDown = onDown; + shortcut.onUp = onUp; state.shortcuts.push(shortcut); - return shortcutId; + return () => { + state.shortcuts = state.shortcuts.filter((shortcut) => (shortcut.id !== shortcutId)); + }; }, - unregisterShortcut(shortcutId) { - state.shortcuts.forEach((shortcut, i) => { - if (shortcut.id === shortcutId) { - state.shortcuts.splice(i, 1); - } - }); - }, - - unregisterShortcutByKeys(keyCodes) { - // A traditional loop is used in order to access `this` from inside - for (let i = 0; i < state.shortcuts.length; i++) { - let shortcut = state.shortcuts[i]; - - // Convert any keycode numbers to strings - keyCodes.forEach((key, index) => { - if (typeof key !== 'string' && !(key instanceof String)) { - // Convert to string - keyCodes[index] = key.toString(); - } - }); - - // Check if this is our shortcut - Object.keys(shortcut).every((key) => { - if (key === 'callback' || key === 'id') return; - - // Remove all given keys from keyCodes - // If any are not in this shortcut, then this shortcut does not match - // If at the end we have eliminated all codes in keyCodes, then we have succeeded - let index = keyCodes.indexOf(key); - if (index === -1) return false; // break - - // Remove this key from the given keyCodes array - keyCodes.splice(index, 1); - return true; - }); + removeShortcut(keyCodes) { + const searchKeys = keyCodes.map((code) => `${code}`).sort().join('+'); + + state.shortcuts.filter((shortcut) => { + const shortcutKeys = Object.keys( + shortcut, + ).filter( + (key) => nonCodeKeys.includes(key), + ).sort().join('+'); - // Is this the shortcut we want to remove? - if (keyCodes.length === 0) { - // Unregister this shortcut - state.shortcuts.splice(i, 1); - return; - } - } + return shortcutKeys !== searchKeys; + }); }, - unregisterAllShortcuts() { - state.shortcuts.splice(0, state.shortcuts.length); + clearShortcuts() { + state.shortcuts = []; }, setDebug(mode) {