diff --git a/gpii/node_modules/WindowsUtilities/WindowsUtilities.js b/gpii/node_modules/WindowsUtilities/WindowsUtilities.js index 47eb266c5..40a689979 100644 --- a/gpii/node_modules/WindowsUtilities/WindowsUtilities.js +++ b/gpii/node_modules/WindowsUtilities/WindowsUtilities.js @@ -394,6 +394,18 @@ windows.user32 = ffi.Library("user32", { ], "GetDisplayConfigBufferSizes": [ "int32", ["uint32", "uint32*", "uint32*"] + ], + // https://docs.microsoft.com/windows/win32/api/winuser/nf-winuser-getasynckeystate + "GetAsyncKeyState": [ + "short", ["int"] + ], + // https://docs.microsoft.com/windows/win32/api/winuser/nf-winuser-sendinput + "SendInput": [ + t.UINT, [t.UINT, t.LP, "int"] + ], + // https://docs.microsoft.com/windows/win32/api/winuser/nf-winuser-oemkeyscan + "OemKeyScan": [ + t.DWORD, [ t.WORD ] ] }); @@ -556,60 +568,154 @@ windows.API_constants = { // https://msdn.microsoft.com/library/dd375731 virtualKeyCodes: { - VK_BACK: 0x08, - VK_TAB: 0x09, - VK_RETURN: 0x0D, - VK_SHIFT: 0x10, - VK_CONTROL: 0x11, - VK_MENU: 0x12, // alt key - VK_ESCAPE: 0x1B, - VK_SPACE: 0x20, - VK_PAGEUP: 0x21, // VK_PRIOR - VK_PAGEDOWN: 0x22, // VK_NEXT - VK_END: 0x23, - VK_HOME: 0x24, - VK_LEFT: 0x25, - VK_UP: 0x26, - VK_RIGHT: 0x27, - VK_DOWN: 0x28, - VK_SELECT: 0x29, - VK_PRINT: 0x2A, - VK_EXECUTE: 0x2B, - VK_SNAPSHOT: 0x2C, - VK_INSERT: 0x2D, - VK_DELETE: 0x2E, - VK_HELP: 0x2F, - VK_LWIN: 0x5B, - VK_RWIN: 0x5C, - VK_APPS: 0x5D, - VK_F1: 0x70, - VK_F2: 0x71, - VK_F3: 0x72, - VK_F4: 0x73, - VK_F5: 0x74, - VK_F6: 0x75, - VK_F7: 0x76, - VK_F8: 0x77, - VK_F9: 0x78, - VK_F10: 0x79, - VK_F11: 0x7A, - VK_F12: 0x7B, - VK_F13: 0x7C, - VK_F14: 0x7D, - VK_F15: 0x7E, - VK_F16: 0x7F, - VK_F17: 0x80, - VK_F18: 0x81, - VK_F19: 0x82, - VK_F20: 0x83, - VK_F21: 0x84, - VK_F22: 0x85, - VK_F23: 0x86, - VK_F24: 0x87, - VK_NUMLOCK: 0x90, - VK_SCROLL: 0x91 + VK_LBUTTON: 0x01, // Left mouse button + VK_RBUTTON: 0x02, // Right mouse button + VK_CANCEL: 0x03, // Control-break processing + VK_MBUTTON: 0x04, // Middle mouse button (three-button mouse) + VK_XBUTTON1: 0x05, // X1 mouse button + VK_XBUTTON2: 0x06, // X2 mouse button + VK_BACK: 0x08, // BACKSPACE key + VK_TAB: 0x09, // TAB key + VK_CLEAR: 0x0C, // CLEAR key + VK_RETURN: 0x0D, // ENTER key + VK_SHIFT: 0x10, // SHIFT key + VK_CONTROL: 0x11, // CTRL key + VK_MENU: 0x12, // ALT key + VK_PAUSE: 0x13, // PAUSE key + VK_CAPITAL: 0x14, // CAPS LOCK key + VK_KANA: 0x15, // IME Kana mode + VK_HANGUL: 0x15, // IME Hangul mode + VK_HANGUEL: 0x15, // IME Hanguel mode (maintained for compatibility; use VK_HANGUL) + VK_JUNJA: 0x17, // IME Junja mode + VK_FINAL: 0x18, // IME final mode + VK_HANJA: 0x19, // IME Hanja mode + VK_KANJI: 0x19, // IME Kanji mode + VK_ESCAPE: 0x1B, // ESC key + VK_CONVERT: 0x1C, // IME convert + VK_NONCONVERT: 0x1D, // IME nonconvert + VK_ACCEPT: 0x1E, // IME accept + VK_MODECHANGE: 0x1F, // IME mode change request + VK_SPACE: 0x20, // SPACEBAR + VK_PAGEUP: 0x21, // PAGE UP key + VK_PAGEDOWN: 0x22, // PAGE DOWN key + VK_PRIOR: 0x21, // PAGE UP key + VK_NEXT: 0x22, // PAGE DOWN key + VK_END: 0x23, // END key + VK_HOME: 0x24, // HOME key + VK_LEFT: 0x25, // LEFT ARROW key + VK_UP: 0x26, // UP ARROW key + VK_RIGHT: 0x27, // RIGHT ARROW key + VK_DOWN: 0x28, // DOWN ARROW key + VK_SELECT: 0x29, // SELECT key + VK_PRINT: 0x2A, // PRINT key + VK_EXECUTE: 0x2B, // EXECUTE key + VK_SNAPSHOT: 0x2C, // PRINT SCREEN key + VK_INSERT: 0x2D, // INS key + VK_DELETE: 0x2E, // DEL key + VK_HELP: 0x2F, // HELP key + VK_LWIN: 0x5B, // Left Windows key (Natural keyboard) + VK_RWIN: 0x5C, // Right Windows key (Natural keyboard) + VK_APPS: 0x5D, // Applications key (Natural keyboard) + VK_SLEEP: 0x5F, // Computer Sleep key + VK_NUMPAD0: 0x60, // Numeric keypad 0 key + VK_NUMPAD1: 0x61, // Numeric keypad 1 key + VK_NUMPAD2: 0x62, // Numeric keypad 2 key + VK_NUMPAD3: 0x63, // Numeric keypad 3 key + VK_NUMPAD4: 0x64, // Numeric keypad 4 key + VK_NUMPAD5: 0x65, // Numeric keypad 5 key + VK_NUMPAD6: 0x66, // Numeric keypad 6 key + VK_NUMPAD7: 0x67, // Numeric keypad 7 key + VK_NUMPAD8: 0x68, // Numeric keypad 8 key + VK_NUMPAD9: 0x69, // Numeric keypad 9 key + VK_MULTIPLY: 0x6A, // Multiply key + VK_ADD: 0x6B, // Add key + VK_SEPARATOR: 0x6C, // Separator key + VK_SUBTRACT: 0x6D, // Subtract key + VK_DECIMAL: 0x6E, // Decimal key + VK_DIVIDE: 0x6F, // Divide key + VK_F1: 0x70, // F1 key + VK_F2: 0x71, // F2 key + VK_F3: 0x72, // F3 key + VK_F4: 0x73, // F4 key + VK_F5: 0x74, // F5 key + VK_F6: 0x75, // F6 key + VK_F7: 0x76, // F7 key + VK_F8: 0x77, // F8 key + VK_F9: 0x78, // F9 key + VK_F10: 0x79, // F10 key + VK_F11: 0x7A, // F11 key + VK_F12: 0x7B, // F12 key + VK_F13: 0x7C, // F13 key + VK_F14: 0x7D, // F14 key + VK_F15: 0x7E, // F15 key + VK_F16: 0x7F, // F16 key + VK_F17: 0x80, // F17 key + VK_F18: 0x81, // F18 key + VK_F19: 0x82, // F19 key + VK_F20: 0x83, // F20 key + VK_F21: 0x84, // F21 key + VK_F22: 0x85, // F22 key + VK_F23: 0x86, // F23 key + VK_F24: 0x87, // F24 key + VK_NUMLOCK: 0x90, // NUM LOCK key + VK_SCROLL: 0x91, // SCROLL LOCK key + VK_LSHIFT: 0xA0, // Left SHIFT key + VK_RSHIFT: 0xA1, // Right SHIFT key + VK_LCONTROL: 0xA2, // Left CONTROL key + VK_RCONTROL: 0xA3, // Right CONTROL key + VK_LMENU: 0xA4, // Left MENU key + VK_RMENU: 0xA5, // Right MENU key + VK_BROWSER_BACK: 0xA6, // Browser Back key + VK_BROWSER_FORWARD: 0xA7, // Browser Forward key + VK_BROWSER_REFRESH: 0xA8, // Browser Refresh key + VK_BROWSER_STOP: 0xA9, // Browser Stop key + VK_BROWSER_SEARCH: 0xAA, // Browser Search key + VK_BROWSER_FAVORITES: 0xAB, // Browser Favorites key + VK_BROWSER_HOME: 0xAC, // Browser Start and Home key + VK_VOLUME_MUTE: 0xAD, // Volume Mute key + VK_VOLUME_DOWN: 0xAE, // Volume Down key + VK_VOLUME_UP: 0xAF, // Volume Up key + VK_MEDIA_NEXT_TRACK: 0xB0, // Next Track key + VK_MEDIA_PREV_TRACK: 0xB1, // Previous Track key + VK_MEDIA_STOP: 0xB2, // Stop Media key + VK_MEDIA_PLAY_PAUSE: 0xB3, // Play/Pause Media key + VK_LAUNCH_MAIL: 0xB4, // Start Mail key + VK_LAUNCH_MEDIA_SELECT: 0xB5, // Select Media key + VK_LAUNCH_APP1: 0xB6, // Start Application 1 key + VK_LAUNCH_APP2: 0xB7, // Start Application 2 key + VK_OEM_1: 0xBA, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key + VK_OEM_PLUS: 0xBB, // For any country/region, the '+' key + VK_OEM_COMMA: 0xBC, // For any country/region, the ',' key + VK_OEM_MINUS: 0xBD, // For any country/region, the '-' key + VK_OEM_PERIOD: 0xBE, // For any country/region, the '.' key + VK_OEM_2: 0xBF, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?' key + VK_OEM_3: 0xC0, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '`~' key + VK_OEM_4: 0xDB, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '[{' key + VK_OEM_5: 0xDC, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '\|' key + VK_OEM_6: 0xDD, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ']}' key + VK_OEM_7: 0xDE, // Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key + VK_OEM_8: 0xDF, // Used for miscellaneous characters; it can vary by keyboard. + VK_OEM_102: 0xE2, // Either the angle bracket key or the backslash key on the RT 102-key keyboard + VK_PROCESSKEY: 0xE5, // IME PROCESS key + VK_PACKET: 0xE7, // Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP: -, // 0xE8 + VK_ATTN: 0xF6, // Attn key + VK_CRSEL: 0xF7, // CrSel key + VK_EXSEL: 0xF8, // ExSel key + VK_EREOF: 0xF9, // Erase EOF key + VK_PLAY: 0xFA, // Play key + VK_ZOOM: 0xFB, // Zoom key + VK_NONAME: 0xFC, // Reserved + VK_PA1: 0xFD, // PA1 key + VK_OEM_CLEAR: 0xFE // Clear key }, + // https://docs.microsoft.com/windows/win32/api/winuser/ns-winuser-keybdinput + KEYEVENTF_EXTENDEDKEY: 1, + KEYEVENTF_KEYUP: 2, + KEYEVENTF_UNICODE: 4, + KEYEVENTF_SCANCODE: 8, + + LLKHF_EXTENDED: 0x01, LLKHF_INJECTED: 0x10, LLKHF_ALTDOWN: 0x20, @@ -933,6 +1039,19 @@ windows.PROCESS_INFORMATION = new Struct([ [t.DWORD, "dwThreadId"] ]); +// https://docs.microsoft.com/windows/win32/api/winuser/ns-winuser-input +// INPUT struct using the ki:KEYBDINPUT union field. +windows.KEY_INPUT = new Struct([ + [t.DWORD, "type"], + // KEYBDINPUT https://docs.microsoft.com/windows/win32/api/winuser/ns-winuser-keybdinput + [t.WORD, "wVk"], + [t.WORD, "wScan"], + [t.DWORD, "dwFlags"], + [t.DWORD, "time"], + [t.ULONG_PTR, "dwExtraInfo"] +]); + + /** * Contains flags used in the "dwFlags" field of various structures * that are used in calls to the SystemParametersInfo function. diff --git a/gpii/node_modules/gpii-userInput/README.md b/gpii/node_modules/gpii-userInput/README.md new file mode 100644 index 000000000..c3b66455e --- /dev/null +++ b/gpii/node_modules/gpii-userInput/README.md @@ -0,0 +1,186 @@ +# User Input + +This module deals with things related to user input devices + +## gpii.windows.sendKeys + +Simulate key presses. The sequence of keys is almost the same as [AutoHotKey's Send command](https://hotkeyit.github.io/v2/docs/commands/Send.htm) + +### Key sequence syntax: + +Each character in the string is a key to press. + +Special (non-character) keys are surrounded by curly braces (`{` and `}`). For example, `{Enter}`. Character letters can +optionally be surrounded in braces (eg, `{a}`). To type curly braces, surround it with braces (eg, `{{}` for `{`, and +`{}}` for `}`). + +#### Modifiers + +Normally, keys are pressed and released. To hold down a key, use `{keyname down}` and to release, `{keyname up`}. For +example, `{Shift down}abc1{Shift up}` would type the text `ABC!`. + +Alternatively, to hold down a modifier key while pressing another key, prefix with one or more of `!`, `+`, `^`, and +`#`. For example, `^C` holds `Ctrl` while pressing `C`. This will affect the next key only - so to send `Ctrl + A` then +`Ctrl + X` use `^A^X`. + +|Modifier prefix|Modifier key| Example | +|---------------|------------|-----------| +| `!` | `Alt` | `!{F4}` sends `Alt + F4` | +| `+` | `Shift` | `+hel+lo` types the text `HelLo` | +| `^` | `Ctrl` | `^o` sends `Ctrl + O` | +| `'` | `LWin` | `#r` holds the Windows key while pressing `r` | + +Modifiers can be combined, for example: `^!{Del}`. + +To send the modifier symbols as a key stroke, surround with curly braces like `{!}`. + +If the current layout requires a modifier to press a key (eg, typing `%` could require `shift + 5`), then that is what +is simulated. This is also the case for uppercase letters. `a` (or `{a}`) will send just the `a` keystroke, but `A` (or +`{A}`) will be equivalent to `{Shift down}a{Shift up}`. + +#### Examples + +`Hello`, `+hello`, `{H}el{l}o`, `{Shift down}h{Shift up}ello` all type `Hello`. + +`#r{Wait window}notepad{enter}` will start notepad. + +`!{space}x` maximise the current window. + +#### Repetition + +Key presses can be repeated by adding a number after the name. For example, `{Enter 10}` will press and release the +`Enter` key 10 times, then `a` for 5 times. + +#### Delay + +Delay can be introduced between each key press using `{Wait n}`, which causes a pause for `n` seconds. For example, +`abc{Wait 5}xyz` will type `abc`, pause for 5 seconds, then `xyz`. + +The `{Wait window}` keyword can be used to wait for a new window to be activated. + +#### Key names + +|Key name| Keystroke | +|-----|-----| +|Single character, `{a}`, `{+}`, `{}}`, etc.|The literal character| +|`{Back}`,`{BS}`,`{BackSpace}`| BACKSPACE key | +|`{Tab}`| TAB key | +|`{Clear}`| CLEAR key | +|`{Enter}`, `{Return}`| ENTER key | +|`{Enter}`, `{Return}`| ENTER key | +|`{Ctrl}`, `{Control}`| CTRL key | +|`{LCtrl}`, `{LControl}`| Left CTRL key | +|`{RCtrl}`, `{RControl}`| Right CTRL key | +|`{Alt}`, `{Menu}`| ALT key | +|`{LAlt}`| Left ALT key | +|`{RAlt}`| Right ALT key | +|`{Shift}`| SHIFT key | +|`{LShift}`| Left SHIFT key | +|`{RShift}`| Right SHIFT key | +|`{Win}`, `{LWin}`| Left Windows key (Natural keyboard) | +|`{RWin}`| Right Windows key (Natural keyboard) | +|`{Pause}`| PAUSE key | +|`{Capital}`| CAPS LOCK key | +|`{Escape}`| ESC key | +|`{Accept}`| IME accept | +|`{Space}`| SPACEBAR | +|`{PageUp}`, `{PgUp}`, `{Prior}`| PAGE UP key | +|`{PageDown}`, `{PgDown}`, `{Next}`| PAGE DOWN key | +|`{End}`| END key | +|`{Home}`| HOME key | +|`{Left}`| LEFT ARROW key | +|`{Up}`| UP ARROW key | +|`{Right}`| RIGHT ARROW key | +|`{Down}`| DOWN ARROW key | +|`{Select}`| SELECT key | +|`{Print}`| PRINT key | +|`{Execute}`| EXECUTE key | +|`{Snapshot}`| PRINT SCREEN key | +|`{Insert}`, `{Ins}`| INS key | +|`{Delete}`, `{Del}`| DEL key | +|`{Help}`| HELP key | +|`{CtrlBreak}`, `{Cancel}`| Control-break processing | +|`{Apps}`| Applications key (Natural keyboard) | +|`{Sleep}`| Computer Sleep key | +|`{Numpad0}`| Numeric keypad 0 key | +|`{Numpad1}`| Numeric keypad 1 key | +|`{Numpad2}`| Numeric keypad 2 key | +|`{Numpad3}`| Numeric keypad 3 key | +|`{Numpad4}`| Numeric keypad 4 key | +|`{Numpad5}`| Numeric keypad 5 key | +|`{Numpad6}`| Numeric keypad 6 key | +|`{Numpad7}`| Numeric keypad 7 key | +|`{Numpad8}`| Numeric keypad 8 key | +|`{Numpad9}`| Numeric keypad 9 key | +|`{Multiply}`| Multiply key | +|`{Add}`| Add key | +|`{Separator}`| Separator key | +|`{Subtract}`| Subtract key | +|`{Decimal}`| Decimal key | +|`{Divide}`| Divide key | +|`{F1}` to `{F24}`| 'F' keys `{F1}` to `{F24}` | +|`{Numlock}`| NUM LOCK key | +|`{Scroll}`| SCROLL LOCK key | +||| +|`{U+###}`|The unicode character, where `###` is the code point in hex. For example, `{U+29ac}`, `{U+7e}` | +||| +|`{Browser_Back}`| Browser Back key | +|`{Browser_Forward}`| Browser Forward key | +|`{Browser_Refresh}`| Browser Refresh key | +|`{Browser_Stop}`| Browser Stop key | +|`{Browser_Search}`| Browser Search key | +|`{Browser_Favorites}`| Browser Favorites key | +|`{Browser_Home}`| Browser Start and Home key | +|`{Volume_Mute}`| Volume Mute key | +|`{Volume_Down}`| Volume Down key | +|`{Volume_Up}`| Volume Up key | +|`{Media_Next}`, `{Media_Next_Track}`| Next Track key | +|`{Media_Prev}`, `{Media_Prev_Track}`| Previous Track key | +|`{Media_Stop}`| Stop Media key | +|`{Media_Play_Pause}`| Play/Pause Media key | +|`{Launch_Mail}`| Start Mail key | +|`{Launch_Media_Select}`| Select Media key | +|`{Launch_App1}`| Start Application 1 key | +|`{Launch_App2}`| Start Application 2 key | +||| +|`{Lbutton}`| Left mouse button | +|`{Rbutton}`| Right mouse button | +|`{Mbutton}`| Middle mouse button (three-button mouse) | +|`{Xbutton1}`| X1 mouse button | +|`{Xbutton2}`| X2 mouse button | +||| +|`{Oem_1}`| Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ';:' key | +|`{Oem_Plus}`| For any country/region, the '+' key | +|`{Oem_Comma}`| For any country/region, the ',' key | +|`{Oem_Minus}`| For any country/region, the '-' key | +|`{Oem_Period}`| For any country/region, the '.' key | +|`{Oem_2}`| Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '/?' key | +|`{Oem_3}`| Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '`~' key | +|`{Oem_4}`| Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '[{' key | +|`{Oem_5}`| Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the '\|' key | +|`{Oem_6}`| Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the ']}' key | +|`{Oem_7}`| Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key | +|`{Oem_8}`| Used for miscellaneous characters; it can vary by keyboard. | +|`{Oem_102}`| Either the angle bracket key or the backslash key on the RT 102-key keyboard | +|`{Processkey}`| IME PROCESS key | +|`{Packet}`| Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP: -, // 0xE8 | +|`{Attn}`| Attn key | +|`{Crsel}`| CrSel key | +|`{Exsel}`| ExSel key | +|`{Ereof}`| Erase EOF key | +|`{Play}`| Play key | +|`{Zoom}`| Zoom key | +|`{Noname}`| Reserved | +|`{Pa1}`| PA1 key | +|`{Oem_Clear}`| lear key | +|`{Kana}`| IME Kana mode | +|`{Hangul}`| IME Hangul mode | +|`{Hanguel}`| IME Hanguel mode (maintained for compatibility; use VK_HANGUL) | +|`{Junja}`| IME Junja mode | +|`{Final}`| IME final mode | +|`{Hanja}`| IME Hanja mode | +|`{Kanji}`| IME Kanji mode | +|`{Convert}`| IME convert | +|`{Nonconvert}`| IME nonconvert | +|`{Modechange}`| IME mode change request | + diff --git a/gpii/node_modules/gpii-userInput/index.js b/gpii/node_modules/gpii-userInput/index.js new file mode 100644 index 000000000..0b37fa6a5 --- /dev/null +++ b/gpii/node_modules/gpii-userInput/index.js @@ -0,0 +1,3 @@ +"use strict"; + +require("./src/sendKeys.js"); diff --git a/gpii/node_modules/gpii-userInput/src/sendKeys.js b/gpii/node_modules/gpii-userInput/src/sendKeys.js new file mode 100644 index 000000000..b362ec5b1 --- /dev/null +++ b/gpii/node_modules/gpii-userInput/src/sendKeys.js @@ -0,0 +1,372 @@ +/* + * Sendkeys - Simulates key presses. + * + * Copyright 2019 Raising the Floor - International + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * The R&D leading to these results received funding from the + * Department of Education - Grant H421A150005 (GPII-APCP). However, + * these results do not necessarily represent the policy of the + * Department of Education, and you should not assume endorsement by the + * Federal Government. + * + * You may obtain a copy of the License at + * https://github.com/GPII/universal/blob/master/LICENSE.txt + */ + +"use strict"; + +var fluid = require("gpii-universal"); + +var gpii = fluid.registerNamespace("gpii"); + +fluid.registerNamespace("gpii.windows.sendKeys"); + +// Other special key names, used in addition to gpii.windows.API_constants.virtualKeyCodes. Used to keep consistent with +// AutoHotKey: https://hotkeyit.github.io/v2/docs/commands/Send.htm +gpii.windows.sendKeys.keyNames = { + "BS": gpii.windows.API_constants.virtualKeyCodes.VK_BACK, + "BACKSPACE": gpii.windows.API_constants.virtualKeyCodes.VK_BACK, + "DEL": gpii.windows.API_constants.virtualKeyCodes.VK_DELETE, + "INS": gpii.windows.API_constants.virtualKeyCodes.VK_INSERT, + "ESC": gpii.windows.API_constants.virtualKeyCodes.VK_ESCAPE, + "ENTER": gpii.windows.API_constants.virtualKeyCodes.VK_RETURN, + + "CTRL": gpii.windows.API_constants.virtualKeyCodes.VK_CONTROL, + "LCTRL": gpii.windows.API_constants.virtualKeyCodes.VK_LCONTROL, + "RCTRL": gpii.windows.API_constants.virtualKeyCodes.VK_RCONTROL, + + "ALT": gpii.windows.API_constants.virtualKeyCodes.VK_MENU, + "LALT": gpii.windows.API_constants.virtualKeyCodes.VK_LMENU, + "RALT": gpii.windows.API_constants.virtualKeyCodes.VK_RMENU, + "WIN": gpii.windows.API_constants.virtualKeyCodes.VK_LWIN, + + "PGUP": gpii.windows.API_constants.virtualKeyCodes.VK_PRIOR, + "PGDOWN": gpii.windows.API_constants.virtualKeyCodes.VK_NEXT, + + "MEDIA_NEXT": gpii.windows.API_constants.virtualKeyCodes.VK_MEDIA_NEXT_TRACK, + "MEDIA_PREV": gpii.windows.API_constants.virtualKeyCodes.VK_MEDIA_PREV_TRACK, + + "PRINTSCREEN": gpii.windows.API_constants.virtualKeyCodes.VK_SNAPSHOT, + "CTRLBREAK": gpii.windows.API_constants.virtualKeyCodes.VK_CANCEL, + "APPSKEY": gpii.windows.API_constants.virtualKeyCodes.VK_APPS +}; + +// Modifier keys which are prefixed to a key +gpii.windows.sendKeys.modifiers = { + "+": gpii.windows.API_constants.virtualKeyCodes.VK_SHIFT, + "^": gpii.windows.API_constants.virtualKeyCodes.VK_CONTROL, + "!": gpii.windows.API_constants.virtualKeyCodes.VK_MENU, + "#": gpii.windows.API_constants.virtualKeyCodes.VK_LWIN +}; + +/** + * Simulated key input. + * @typedef KeyPress {Object} + * @property {String} keyName The key name. + * @property {Array} args The text after the key name, split by whitespace. + * @property {Function} command A function to call, instead of pressing a key. + * @property {Number} virtualKey The virtual key code. + * @property {Number} unicode The character's unicode number (for characters with no corresponding virtual key code) + * @property {Array} modifiers The modifier keys. + * @property {String} state "up", "down", or null to press and release. + * @property {Number} count Number of times to apply the key. + */ + +/** + * Sends a sequence of key presses, as described by a string in a format inspired by AutoHotKey's `Send` command. + * @param {String} keySequence The key sequence. + * @return {Promise} Resolves when the keys have been sent. + */ +gpii.windows.sendKeys.send = function (keySequence) { + var keys = gpii.windows.sendKeys.parse(keySequence); + var promise = fluid.promise(); + + var current = 0; + var nextKey = function () { + if (current >= keys.length) { + promise.resolve(); + } else { + var result = gpii.windows.sendKeys.sendKey(keys[current++]); + var p = fluid.toPromise(result); + p.then(nextKey); + } + }; + + nextKey(); + return promise; +}; + +/** + * Parses a sequence of key presses + * @param {String} keyString The key sequence. + * @return {Array} Key press information for each key in the sequence. + */ +gpii.windows.sendKeys.parse = function (keyString) { + // A single key token can be a literal character or something between "{" and "}", prefixed with zero or more + // modifier key characters (!, +, ^, or #). + var keyMatch = /[!+^#]*(\{(.[^}]*)\}|.)/g; + return fluid.transform(keyString.match(keyMatch), gpii.windows.sendKeys.getKey); +}; + +/** + * Gets key press information for a single key. + * + * @param {String} keyText String identifying a single key. + * @return {KeyPress} The key press information for the given key. + */ +gpii.windows.sendKeys.getKey = function (keyText) { + /** @type KeyPress */ + var key = { + args: [], + modifiers: {} + }; + + // Keys with a modifier prefix (^C, !^{DEL}) + var modifier; + while ((modifier = gpii.windows.sendKeys.modifiers[keyText[0]])) { + key.modifiers[modifier] = true; + keyText = keyText.substr(1); + } + + // {key [up|down] [number of times]} + if (keyText.startsWith("{") && keyText.endsWith("}")) { + var content = keyText.substr(1, keyText.length - 2).split(/\s+/); + key.keyName = content.shift(); + key.args = content; + + fluid.each(key.args, function (arg) { + if (arg.toLowerCase() === "down" || arg.toLowerCase() === "up") { + key.state = arg.toLowerCase(); + } else { + var number = parseInt(arg); + if (number >= 0) { + key.count = number; + } + } + }); + } else { + key.keyName = keyText; + } + + var keyValue; + if (key.keyName.length === 1) { + if (Object.keys(key.modifiers).length > 0) { + // The key-press has explicit modifiers - Lower the letter to avoid an additional 'shift' modifier because + // something like "^C" usually means just ctrl + C + key.keyName = key.keyName.toLowerCase(); + } + keyValue = gpii.windows.sendKeys.getCharacterKey(key.keyName); + } else { + // upper case the key names + key.keyName = key.keyName.toUpperCase(); + keyValue = gpii.windows.sendKeys.getSpecialKey(key.keyName); + } + + if (keyValue.modifiers) { + Object.assign(key.modifiers, keyValue.modifiers); + delete keyValue.modifiers; + } + Object.assign(key, keyValue); + + return key; +}; + +/** + * Gets a virtual key code, and the modifiers required to send a character key. + * + * @param {String} character Character of the key. + * @return {KeyPress} The key press required to generate the given character. + */ +gpii.windows.sendKeys.getCharacterKey = function (character) { + var togo = { + modifiers: {} + }; + + var charCode = character.charCodeAt(0); + var ascii; + if (charCode <= 0xff) { + ascii = charCode; + } + + var modifierBits; + + if (ascii) { + // Get the key+modifiers required to generate the character + var keys = gpii.windows.user32.VkKeyScanW(ascii); + if (keys === -1) { + // Might be an OEM key (non-standard layout) + var scan = gpii.windows.user32.OemKeyScan(charCode); + if (scan !== gpii.windows.API_constants.UINT_MAX) { + togo.unicode = scan & 0xff; + modifierBits = (scan >> 8) & 0xff; + keys = 0; + } + } else { + togo.virtualKey = keys & 0xff; + modifierBits = (keys >> 8) & 0xff; + } + } + + if (!ascii || !(togo.virtualKey || togo.unicode)) { + // no such key combination for this character - send it as a virtual unicode key + togo.unicode = charCode; + } + + // Add the modifiers required for the character. + if (modifierBits & 1) { + togo.modifiers[gpii.windows.API_constants.virtualKeyCodes.VK_SHIFT] = true; + } + if (modifierBits & 2) { + togo.modifiers[gpii.windows.API_constants.virtualKeyCodes.VK_CONTROL] = true; + } + if (modifierBits & 4) { + togo.modifiers[gpii.windows.API_constants.virtualKeyCodes.VK_MENU] = true; + } + + return togo; +}; + +/** + * Gets the key press information from a key's name. + * + * @param {String} keyName The name of the key, such as "ESC". + * @return {KeyPress} The key press required to generate the given character. + */ +gpii.windows.sendKeys.getSpecialKey = function (keyName) { + var togo = {}; + + var command = gpii.windows.sendKeys.commands[keyName.toLowerCase()]; + + if (command) { + togo.command = command; + } else { + keyName = keyName.toUpperCase(keyName); + + var vk = gpii.windows.sendKeys.keyNames[keyName] || gpii.windows.API_constants.virtualKeyCodes["VK_" + keyName]; + if (vk) { + togo.virtualKey = vk; + } else if (keyName.startsWith("U+")) { + // U+nnnn: Send a unicode character + var number = parseInt(keyName.substring(2), 16); + if (number) { + togo.unicode = number; + } + } + } + return togo; +}; + +// Keys which are really function calls. +gpii.windows.sendKeys.commands = {}; + +/** + * Pauses the key input for a given number of seconds, and/or until another window has been activated. + * + * @param {String} window [optional] If "window", wait for another window to be activated. + * @param {String} delay Numeric string specifying the seconds to wait. + * @return {Promise} Resolves after `delay` seconds, or another window to be activated if `window` is set (whichever is + * first). + */ +gpii.windows.sendKeys.commands.wait = function (window, delay) { + var promise = fluid.promise(); + + var waitForWindow = window.toLowerCase() === "window"; + if (!waitForWindow && delay === undefined) { + delay = window; + }; + + var number = parseFloat(delay); + + var timeout; + if (isFinite(number)) { + timeout = number * 1000; + } else { + timeout = waitForWindow ? 10000 : 500; + } + + if (waitForWindow) { + // Wait for the active window to change. + var currentWindow = gpii.windows.user32.GetForegroundWindow(); + return gpii.windows.waitForCondition(function () { + return currentWindow === gpii.windows.user32.GetForegroundWindow(); + }, {dontReject: true, timeout: timeout}); + } else { + setTimeout(promise.resolve, timeout); + } + return promise; +}; + + +/** + * Sends a simulated key press event to the OS. + * + * @param {KeyPress} key Information about the key press. + * @return {Promise|undefined} The result of the key.command function, or undefined. + */ +gpii.windows.sendKeys.sendKey = function (key) { + var INPUT_KEYBOARD = 1; + var inputSize = 28; + + var keyInput = new gpii.windows.KEY_INPUT(); + keyInput.ref().fill(0); + keyInput.type = INPUT_KEYBOARD; + if (key.unicode) { + keyInput.wScan = key.unicode; + keyInput.dwFlags = gpii.windows.API_constants.KEYEVENTF_UNICODE; + } else { + keyInput.wVk = key.virtualKey; + } + + var modifiersApplied = gpii.windows.sendKeys.setModifiers(key.modifiers, true); + + var togo; + if (key.command) { + togo = key.command.apply(null, key.args); + } else { + var count = key.count || 1; + + for (var n = 0; n < count; n++) { + // Press the key + if (key.state !== "up") { + keyInput.dwFlags &= ~gpii.windows.API_constants.KEYEVENTF_KEYUP; + gpii.windows.user32.SendInput(1, keyInput.ref(), inputSize); + } + + // Release the key + if (key.state !== "down") { + keyInput.dwFlags |= gpii.windows.API_constants.KEYEVENTF_KEYUP; + gpii.windows.user32.SendInput(1, keyInput.ref(), inputSize); + } + } + + gpii.windows.sendKeys.setModifiers(modifiersApplied, false); + } + + return togo; +}; + +gpii.windows.sendKeys.setModifiers = function (modifiers) { + var changed = {}; + fluid.each(modifiers, function (state, modifier) { + var currentState = gpii.windows.user32.GetAsyncKeyState(modifier) & 0x8000; + if (!!currentState !== !!state) { + gpii.windows.sendKeys.sendKey({ + virtualKey: modifier, + state: state ? "down" : "up" + }); + changed[modifier] = !!currentState; + } + }); + + return changed; +}; + +fluid.defaults("gpii.windows.sendKeys.send", { + gradeNames: "fluid.function", + argumentMap: { + keys: 0 + } +}); diff --git a/gpii/node_modules/gpii-userInput/test/manualSendKeys.js b/gpii/node_modules/gpii-userInput/test/manualSendKeys.js new file mode 100644 index 000000000..0f3f36789 --- /dev/null +++ b/gpii/node_modules/gpii-userInput/test/manualSendKeys.js @@ -0,0 +1,112 @@ +/* + * Manual tests for Sendkeys + * + * Copyright 2019 Raising the Floor - International + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * The R&D leading to these results received funding from the + * Department of Education - Grant H421A150005 (GPII-APCP). However, + * these results do not necessarily represent the policy of the + * Department of Education, and you should not assume endorsement by the + * Federal Government. + * + * You may obtain a copy of the License at + * https://github.com/GPII/universal/blob/master/LICENSE.txt + */ + +"use strict"; + +var fluid = require("gpii-universal"), + child_process = require("child_process"); + +require("../../WindowsUtilities/WindowsUtilities.js"); +require("../src/sendKeys.js"); + +var ffi = require("ffi-napi"), + readline = require("readline"); + +var gpii = fluid.registerNamespace("gpii"); +fluid.registerNamespace("gpii.tests.windows.sendKeys"); + +var kernel32 = ffi.Library("kernel32", { + // https://docs.microsoft.com/en-us/windows/console/getconsolewindow + "GetConsoleWindow": [ + "uint", [] + ] +}); + +var user32 = ffi.Library("user32", { + // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setforegroundwindow + "SetForegroundWindow": [ + "uint", [ "uint" ] + ] +}); + +gpii.tests.windows.sendKeys.manualTests = [ + "hello", + "+{home}", + "^X", + "^{V 5}", + "{enter}waiting 5 seconds...{wait 5}done{enter}", + "^h{wait window}hello{tab}bye!a{esc}" +]; + +var rl = readline.createInterface({ + input: process.stdin, output: process.stdout +}); + +var currentTest = 0; +var consoleWindow = kernel32.GetConsoleWindow(); +var notepad; +var notepadWindow; + +rl.question("\n\nHit enter to start", function () { + notepad = child_process.spawn("notepad.exe"); + + // Wait for notepad to start, and get the window + console.log("Waiting for notepad..."); + var timer = setInterval(function () { + var windows = gpii.windows.findWindows("Notepad"); + fluid.each(windows, function (hwnd) { + var pid = gpii.windows.getWindowProcessId(hwnd); + if (pid === notepad.pid) { + notepadWindow = hwnd; + clearTimeout(timer); + nextTest(); + } + }); + + }, 1000); +}); + +function activateWindow(hwnd) { + return gpii.windows.waitForCondition(function () { + user32.SetForegroundWindow(hwnd); + return gpii.windows.user32.GetForegroundWindow() === hwnd; + }); +} + +function nextTest() { + + activateWindow(consoleWindow).then(function () { + var test = gpii.tests.windows.sendKeys.manualTests[currentTest++]; + if (!test) { + console.log("the end"); + process.exit(); + } + rl.question("Hit enter to send: " + test, function () { + activateWindow(notepadWindow).then(function () { + setTimeout(function () { + gpii.windows.sendKeys.send(test).then(function () { + setTimeout(nextTest, 500); + }); + }, 500); + }); + }); + }); +} + + +console.log("tasd"); diff --git a/gpii/node_modules/gpii-userInput/test/testSendKeys.js b/gpii/node_modules/gpii-userInput/test/testSendKeys.js new file mode 100644 index 000000000..8b7008142 --- /dev/null +++ b/gpii/node_modules/gpii-userInput/test/testSendKeys.js @@ -0,0 +1,358 @@ +/* + * Tests for Sendkeys + * + * Copyright 2019 Raising the Floor - International + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * The R&D leading to these results received funding from the + * Department of Education - Grant H421A150005 (GPII-APCP). However, + * these results do not necessarily represent the policy of the + * Department of Education, and you should not assume endorsement by the + * Federal Government. + * + * You may obtain a copy of the License at + * https://github.com/GPII/universal/blob/master/LICENSE.txt + */ + +"use strict"; + +var fluid = require("gpii-universal"); + +var jqUnit = fluid.require("node-jqunit"); +var gpii = fluid.registerNamespace("gpii"); + +fluid.require("%gpii-windows"); + +require("../src/sendKeys.js"); + +fluid.registerNamespace("gpii.tests.windows.sendKeys"); + +// These tests assume the '!' character is typed using shift + 1. This is the case for most western language layouts. +gpii.tests.windows.sendKeys.getKeysTests = fluid.freezeRecursive({ + // plain character + "a": { + keyName: "a", + virtualKey: "A".charCodeAt(0), + args: [], + modifiers: {} + }, + // plain character in braces + "{a}": { + keyName: "a", + virtualKey: "A".charCodeAt(0), + args: [], + modifiers: {} + }, + // character requiring shift key + "A": { + keyName: "A", + virtualKey: "A".charCodeAt(0), + args: [], + modifiers: { + 0x10: true // VK_SHIFT + } + }, + // number + "1": { + keyName: "1", + virtualKey: "1".charCodeAt(0), + args: [], + modifiers: {} + }, + // character requiring shift key + "{!}": { + keyName: "!", + virtualKey: "1".charCodeAt(0), + args: [], + modifiers: { + 0x10: true // VK_SHIFT + } + }, + // space (literal) + " ": { + keyName: " ", + virtualKey: gpii.windows.API_constants.virtualKeyCodes.VK_SPACE, + args: [], + modifiers: {} + }, + // space (named) + "{SPACE}": { + keyName: "SPACE", + virtualKey: gpii.windows.API_constants.virtualKeyCodes.VK_SPACE, + args: [], + modifiers: {} + }, + // special key + "{F5}": { + keyName: "F5", + virtualKey: gpii.windows.API_constants.virtualKeyCodes.VK_F5, + args: [], + modifiers: {} + }, + // special key, checking case sensitivity + "{HomE}": { + keyName: "HOME", + virtualKey: gpii.windows.API_constants.virtualKeyCodes.VK_HOME, + args: [], + modifiers: {} + }, + // key with modifier + "^c": { + keyName: "c", + virtualKey: "C".charCodeAt(0), + args: [], + modifiers: { + 0x11: true // VK_CONTROL + } + }, + // upper-case key with modifier (should be the same as the lowercase) + "^C": { + keyName: "c", + virtualKey: "C".charCodeAt(0), + args: [], + modifiers: { + 0x11: true // VK_CONTROL + } + }, + // shift + "+a": { + keyName: "a", + virtualKey: "A".charCodeAt(0), + args: [], + modifiers: { + 0x10: true // VK_SHIFT + } + }, + // alt + "!a": { + keyName: "a", + virtualKey: "A".charCodeAt(0), + args: [], + modifiers: { + 0x12: true // VK_MENU + } + }, + // windows key + "#a": { + keyName: "a", + virtualKey: "A".charCodeAt(0), + args: [], + modifiers: { + 0x5B: true // VK_LWIN + } + }, + // several modifiers + "+^a": { + keyName: "a", + virtualKey: "A".charCodeAt(0), + args: [], + modifiers: { + 0x10: true, // VK_SHIFT + 0x11: true // VK_CONTROL + } + }, + // all modifiers + "+^!#a": { + keyName: "a", + virtualKey: "A".charCodeAt(0), + args: [], + modifiers: { + 0x10: true, // VK_SHIFT + 0x11: true, // VK_CONTROL + 0x12: true, // VK_MENU + 0x5B: true // VK_LWIN + } + }, + // modifiers with special key + "^!{Left}": { + keyName: "LEFT", + virtualKey: gpii.windows.API_constants.virtualKeyCodes.VK_LEFT, + args: [], + modifiers: { + 0x11: true, // VK_CONTROL + 0x12: true // VK_MENU + } + }, + // modifiers with character requiring modifier + "^{!}": { + keyName: "!", + virtualKey: "1".charCodeAt(0), + args: [], + modifiers: { + 0x10: true, // VK_SHIFT + 0x11: true // VK_CONTROL + } + }, + // modifiers with character requiring modifier already given + "+{!}": { + keyName: "!", + virtualKey: "1".charCodeAt(0), + args: [], + modifiers: { + 0x10: true // VK_SHIFT + } + }, + // modifiers with character requiring modifier already given, and one extra + "^+{!}": { + keyName: "!", + virtualKey: "1".charCodeAt(0), + args: [], + modifiers: { + 0x10: true, // VK_SHIFT + 0x11: true // VK_CONTROL + } + }, + // modifiers for the same modifier key + "^{Control}": { + keyName: "CONTROL", + virtualKey: gpii.windows.API_constants.virtualKeyCodes.VK_CONTROL, + args: [], + modifiers: { + 0x11: true // VK_CONTROL + } + }, + // invalid key name + "{unknown-key}": { + keyName: "UNKNOWN-KEY", + args: [], + modifiers: {} + }, + // unicode + "{U+1F4A9}": { + keyName: "U+1F4A9", + unicode: 0x1F4A9, + args: [], + modifiers: {} + }, + // unicode literal + "\u2665": { + keyName: "\u2665", + unicode: 0x2665, + args: [], + modifiers: {} + }, + // key down + "{shift down}": { + keyName: "SHIFT", + virtualKey: gpii.windows.API_constants.virtualKeyCodes.VK_SHIFT, + state: "down", + args: [ "down" ], + modifiers: {} + }, + // key up + "{g UP}": { + keyName: "g", + virtualKey: "G".charCodeAt(0), + state: "up", + args: [ "UP" ], + modifiers: {} + }, + // repetition + "{F3 10}": { + keyName: "F3", + virtualKey: gpii.windows.API_constants.virtualKeyCodes.VK_F3, + count: 10, + args: [ "10" ], + modifiers: {} + }, + // repetition + state + "{F4 5 down}": { + keyName: "F4", + virtualKey: gpii.windows.API_constants.virtualKeyCodes.VK_F4, + count: 5, + state: "down", + args: [ "5", "down" ], + modifiers: {} + }, + // state + repetition + "{F5 down 99}": { + keyName: "F5", + virtualKey: gpii.windows.API_constants.virtualKeyCodes.VK_F5, + count: 99, + state: "down", + args: [ "down", "99" ], + modifiers: {} + }, + // wait command + "{wait window 10}": { + keyName: "WAIT", + command: gpii.windows.sendKeys.commands.wait, + count: 10, + args: [ "window", "10" ], + modifiers: {} + } +}); + +// Tests only the resulting KeyPress.keyName field, as the actual key codes may vary with different keyboard layouts. +gpii.tests.windows.sendKeys.parseTests = fluid.freezeRecursive({ + "abc": [ "a", "b", "c"], + "^a^b^c": [ "a", "b", "c"], + "{a}b{c}": [ "a", "b", "c"], + "{UP}a": [ "UP", "a"], + "^{DOWN}b": [ "DOWN", "b"], + "{{}": [ "{" ], + "{}}": [ "}" ], + "{{}{}}": [ "{", "}" ], + "{}}{{}": [ "}", "{" ], + "{}}{}}{{}{{}{}}{{}": [ "}" , "}" , "{" , "{" , "}" , "{" ], + "{^}": [ "^" ], + "^{^}": [ "^" ], + "{fiXCaSe}": [ "FIXCASE" ], + "{down down}": [ "DOWN" ], + // parser is too forgiving + "}": [ "}" ], + "{": [ "{" ], + "}}": [ "}", "}" ], + "{{": [ "{", "{" ], + "{{{}": [ "{{" ], + "{a}}": [ "a", "}" ], + "{{xy 5}": [ "{XY" ] +}); + + +// Tests the correct parsing of a single key. +jqUnit.test("sendkeys.getKey", function () { + gpii.windows.sendKeys.send("{wait 5}"); + fluid.each(gpii.tests.windows.sendKeys.getKeysTests, function (key, keyText) { + var result = gpii.windows.sendKeys.getKey(keyText); + jqUnit.assertDeepEq("getKey(\"" + keyText + "\") should return the expected result.", key, result); + }); +}); + +// Tests the correct parsing of a sequence of keys. +jqUnit.test("sendkeys.parse (using getKey tests)", function () { + + // Check the individual keys from the getKey test. + fluid.each(gpii.tests.windows.sendKeys.getKeysTests, function (key, keyText) { + var result = gpii.windows.sendKeys.parse(keyText); + var expect = fluid.makeArray(key); + jqUnit.assertDeepEq("parse(\"" + keyText + "\") should return the expected result.", expect, result); + }); + + // Join them all together, and test as a single sequence. + var allInput = ""; + var allExpected = []; + fluid.each(gpii.tests.windows.sendKeys.getKeysTests, function (key, keyText) { + allInput += keyText; + allExpected.push(key); + }); + + var allResult = gpii.windows.sendKeys.parse(allInput); + jqUnit.assertDeepEq("All getKey tests passed to parse() in one go should match all results joined", + allExpected, allResult); + +}); + +// Tests the correct parsing of a sequence of keys. +jqUnit.test("sendkeys.parse (using parse tests)", function () { + + // Check the individual keys from the getKey test. + fluid.each(gpii.tests.windows.sendKeys.parseTests, function (expect, keySequence) { + var result = gpii.windows.sendKeys.parse(keySequence); + var keyNames = fluid.getMembers(result, "keyName"); + jqUnit.assertDeepEq("parse(\"" + keySequence + "\") should return the expected keyNames.", expect, keyNames); + }); + +}); diff --git a/index.js b/index.js index 2eab92473..739810906 100644 --- a/index.js +++ b/index.js @@ -47,5 +47,6 @@ require("./gpii/node_modules/gpii-app-zoom"); require("./gpii/node_modules/wmiSettingsHandler"); require("./gpii/node_modules/gpii-localisation"); require("./gpii/node_modules/gpii-office"); +require("./gpii/node_modules/gpii-userInput"); module.exports = fluid; diff --git a/tests/UnitTests.js b/tests/UnitTests.js index d7dce90ab..79735853c 100644 --- a/tests/UnitTests.js +++ b/tests/UnitTests.js @@ -34,3 +34,4 @@ require("../gpii/node_modules/windowsMetrics/test/WindowsMetricsTests.js"); require("../gpii/node_modules/windowsUtilities/test/WindowsUtilitiesTests.js"); require("../gpii/node_modules/wmiSettingsHandler/test/testWmiSettingsHandler.js"); require("../gpii/node_modules/gpii-office/test/ribbonTest.js"); +require("../gpii/node_modules/gpii-userInput/test/testSendKeys.js");