UNPKG

@epicgames-ps/lib-pixelstreamingfrontend-ue5.4

Version:
355 lines (329 loc) 11.5 kB
// Copyright Epic Games, Inc. All Rights Reserved. import { SpecialKeyCodes } from './SpecialKeyCodes'; import { Logger } from '../Logger/Logger'; import { ActiveKeys } from './InputClassesFactory'; import { StreamMessageController } from '../UeInstanceMessage/StreamMessageController'; import { Config, Flags } from '../Config/Config'; import { EventListenerTracker } from '../Util/EventListenerTracker'; interface ICodeToKeyCode { [key: string]: number; } /** * Handles the Keyboard Inputs for the document */ export class KeyboardController { toStreamerMessagesProvider: StreamMessageController; activeKeysProvider: ActiveKeys; config: Config; // Utility for keeping track of event handlers and unregistering them private keyboardEventListenerTracker = new EventListenerTracker(); /* * New browser APIs have moved away from KeyboardEvent.keyCode to KeyboardEvent.Code. * For details see: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#constants_for_keycode_value * We still use old KeyboardEvent.keyCode integers in the UE C++ side, so we need a way to map the new * string-based KeyboardEvent.Code to the old integers. */ CodeToKeyCode: ICodeToKeyCode = { Escape: 27, Digit0: 48, Digit1: 49, Digit2: 50, Digit3: 51, Digit4: 52, Digit5: 53, Digit6: 54, Digit7: 55, Digit8: 56, Digit9: 57, Minus: 173, Equal: 187, Backspace: 8, Tab: 9, KeyQ: 81, KeyW: 87, KeyE: 69, KeyR: 82, KeyT: 84, KeyY: 89, KeyU: 85, KeyI: 73, KeyO: 79, KeyP: 80, BracketLeft: 219, BracketRight: 221, Enter: 13, ControlLeft: 17, KeyA: 65, KeyS: 83, KeyD: 68, KeyF: 70, KeyG: 71, KeyH: 72, KeyJ: 74, KeyK: 75, KeyL: 76, Semicolon: 186, Quote: 222, Backquote: 192, ShiftLeft: 16, Backslash: 220, KeyZ: 90, KeyX: 88, KeyC: 67, KeyV: 86, KeyB: 66, KeyN: 78, KeyM: 77, Comma: 188, Period: 190, Slash: 191, ShiftRight: 253, AltLeft: 18, Space: 32, CapsLock: 20, F1: 112, F2: 113, F3: 114, F4: 115, F5: 116, F6: 117, F7: 118, F8: 119, F9: 120, F10: 121, F11: 122, F12: 123, Pause: 19, ScrollLock: 145, NumpadDivide: 111, NumpadMultiply: 106, NumpadSubtract: 109, NumpadAdd: 107, NumpadDecimal: 110, Numpad9: 105, Numpad8: 104, Numpad7: 103, Numpad6: 102, Numpad5: 101, Numpad4: 100, Numpad3: 99, Numpad2: 98, Numpad1: 97, Numpad0: 96, NumLock: 144, ControlRight: 254, AltRight: 255, Home: 36, End: 35, ArrowUp: 38, ArrowLeft: 37, ArrowRight: 39, ArrowDown: 40, PageUp: 33, PageDown: 34, Insert: 45, Delete: 46, ContextMenu: 93 }; /** * @param toStreamerMessagesProvider Stream message provider class object * @param config The applications configuration. We're interested in the suppress browser keys option * @param activeKeysProvider Active keys provider class object */ constructor( toStreamerMessagesProvider: StreamMessageController, config: Config, activeKeysProvider: ActiveKeys ) { this.toStreamerMessagesProvider = toStreamerMessagesProvider; this.config = config; this.activeKeysProvider = activeKeysProvider; } /** * Registers document keyboard events with the controller */ registerKeyBoardEvents() { const compositionEndHandler = (ev: CompositionEvent) => this.handleOnCompositionEnd(ev); const keyDownHandler = (ev: KeyboardEvent) => this.handleOnKeyDown(ev); const keyUpHandler = (ev: KeyboardEvent) => this.handleOnKeyUp(ev); const keyPressHandler = (ev: KeyboardEvent) => this.handleOnKeyPress(ev); document.addEventListener("compositionend", compositionEndHandler); document.addEventListener("keydown", keyDownHandler); document.addEventListener("keyup", keyUpHandler); //This has been deprecated as at Jun 13 2021 document.addEventListener("keypress", keyPressHandler); this.keyboardEventListenerTracker.addUnregisterCallback( () => document.removeEventListener("compositionend", compositionEndHandler) ); this.keyboardEventListenerTracker.addUnregisterCallback( () => document.removeEventListener("keydown", keyDownHandler) ); this.keyboardEventListenerTracker.addUnregisterCallback( () => document.removeEventListener("keyup", keyUpHandler) ); this.keyboardEventListenerTracker.addUnregisterCallback( () => document.removeEventListener("keypress", keyPressHandler) ); } /** * Unregisters document keyboard events */ unregisterKeyBoardEvents() { this.keyboardEventListenerTracker.unregisterAll(); } /** * Handles When a key is down * @param keyboardEvent - Keyboard event */ handleOnKeyDown(keyboardEvent: KeyboardEvent) { const keyCode = this.getKeycode(keyboardEvent); if (!keyCode || keyCode === 229) { return; } Logger.Log( Logger.GetStackTrace(), `key down ${keyCode}, repeat = ${keyboardEvent.repeat}`, 6 ); const toStreamerHandlers = this.toStreamerMessagesProvider.toStreamerHandlers; toStreamerHandlers.get('KeyDown')([ this.getKeycode(keyboardEvent), keyboardEvent.repeat ? 1 : 0 ]); const activeKeys = this.activeKeysProvider.getActiveKeys(); activeKeys.push(keyCode); // Backspace is not considered a keypress in JavaScript but we need it // to be so characters may be deleted in a UE text entry field. if (keyCode === SpecialKeyCodes.backSpace) { document.dispatchEvent( new KeyboardEvent('keypress', { charCode: SpecialKeyCodes.backSpace }) ); } if ( this.config.isFlagEnabled(Flags.SuppressBrowserKeys) && this.isKeyCodeBrowserKey(keyCode) ) { keyboardEvent.preventDefault(); } } /** * handles when a key is up * @param keyboardEvent - Keyboard event */ handleOnKeyUp(keyboardEvent: KeyboardEvent) { const keyCode = this.getKeycode(keyboardEvent); if (!keyCode) { return; } Logger.Log(Logger.GetStackTrace(), `key up ${keyCode}`, 6); const toStreamerHandlers = this.toStreamerMessagesProvider.toStreamerHandlers; toStreamerHandlers.get('KeyUp')([ keyCode ]); if ( this.config.isFlagEnabled(Flags.SuppressBrowserKeys) && this.isKeyCodeBrowserKey(keyCode) ) { keyboardEvent.preventDefault(); } } /** * Handles when a key is press * @param keyboard - Keyboard Event */ handleOnKeyPress(keyboard: KeyboardEvent) { if (!('charCode' in keyboard)) { Logger.Warning( Logger.GetStackTrace(), 'KeyboardEvent.charCode is deprecated in this browser, cannot send key press.' ); return; } const charCode = keyboard.charCode; Logger.Log(Logger.GetStackTrace(), `key press ${charCode}`, 6); const toStreamerHandlers = this.toStreamerMessagesProvider.toStreamerHandlers; toStreamerHandlers.get('KeyPress')([charCode]); } /** * Handle whenever composition ends (eg chinese simplified) * @param compositionEvent - the composition event */ handleOnCompositionEnd(compositionEvent: CompositionEvent) { if (compositionEvent.data && compositionEvent.data.length) { compositionEvent.data.split('').forEach((char) => { // This keydown, keypress, keyup flow is required to mimic the way characters are // normally triggered this.handleOnKeyDown( new KeyboardEvent('keydown', { keyCode: char.toUpperCase().charCodeAt(0), charCode: char.charCodeAt(0) }) ); this.handleOnKeyPress( new KeyboardEvent('keypress', { keyCode: char.toUpperCase().charCodeAt(0), charCode: char.charCodeAt(0) }) ); this.handleOnKeyUp( new KeyboardEvent('keyup', { keyCode: char.toUpperCase().charCodeAt(0), charCode: char.charCodeAt(0) }) ); }); } } /** * Gets the Keycode of the Key pressed * @param keyboardEvent - Key board Event * @returns - the key code of the Key */ getKeycode(keyboardEvent: KeyboardEvent) { // If we don't have keyCode property because browser API is deprecated then use KeyboardEvent.code instead. // See: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#constants_for_keycode_value if (!('keyCode' in keyboardEvent)) { // Convert KeyboardEvent.code string into integer-based key code for backwards compatibility reasons. const event = keyboardEvent as KeyboardEvent; if (event.code in this.CodeToKeyCode) { return this.CodeToKeyCode[event.code]; } else { Logger.Warning( Logger.GetStackTrace(), `Keyboard code of ${event.code} is not supported in our mapping, ignoring this key.` ); return null; } } // If we made it here KeyboardEvent.keyCode is still supported so we can safely use it. if ( keyboardEvent.keyCode === SpecialKeyCodes.shift && keyboardEvent.code === 'ShiftRight' ) { return SpecialKeyCodes.rightShift; } else if ( keyboardEvent.keyCode === SpecialKeyCodes.control && keyboardEvent.code === 'ControlRight' ) { return SpecialKeyCodes.rightControl; } else if ( keyboardEvent.keyCode === SpecialKeyCodes.alt && keyboardEvent.code === 'AltRight' ) { return SpecialKeyCodes.rightAlt; } else { return keyboardEvent.keyCode; } } /** * Browser keys do not have a charCode so we only need to test keyCode. * @param keyCode - the browser keycode number */ isKeyCodeBrowserKey(keyCode: number) { // Function keys or tab key are considered "browser keys" that we may wish to suppress by preventing them being process by browser. return (keyCode >= 112 && keyCode <= 123) || keyCode === 9; } }