From 795bf7bd01c802aeda4fc7d4dfd0147f8ac458ef Mon Sep 17 00:00:00 2001 From: MDMCK10 <21245760+MDMCK10@users.noreply.github.com> Date: Fri, 2 Feb 2024 23:24:26 +0100 Subject: [PATCH] fix merge conflict --- package.json | 3 +- src/css/style.css | 265 +++++++++++++++---- src/html/index.html | 14 + src/js/keyboard.d.ts | 3 - src/js/keyboard.js | 279 -------------------- src/ts/keyboard.ts | 414 ++++++++++++++++++++++++++++++ src/ts/main.ts | 197 +++++++++++++- src/ts/protocol/CollabVMClient.ts | 6 +- 8 files changed, 840 insertions(+), 341 deletions(-) delete mode 100644 src/js/keyboard.d.ts delete mode 100644 src/js/keyboard.js create mode 100644 src/ts/keyboard.ts diff --git a/package.json b/package.json index 920b6fb..d78f336 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "@hcaptcha/types": "^1.0.3", "@popperjs/core": "^2.11.8", "bootstrap": "^5.3.2", - "nanoevents": "^7.0.1" + "nanoevents": "^7.0.1", + "simple-keyboard": "^3.7.53" }, "overrides": { "@parcel/watcher": "~2.1.0" diff --git a/src/css/style.css b/src/css/style.css index a75cb64..24997a2 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -1,124 +1,281 @@ #vmview { - display: none; + display: none; } /*.vmtile { - text-decoration: none; - color: #FFFFFF; - font-size: 16pt; - border: 2px solid #575757; - border-radius: 15px; - height: fit-content; - width: fit-content; - display: block; - padding: 4px; + text-decoration: none; + color: #FFFFFF; + font-size: 16pt; + border: 2px solid #575757; + border-radius: 15px; + height: fit-content; + width: fit-content; + display: block; + padding: 4px; }*/ #vmDisplay, #btns { - margin-left: auto; - margin-right: auto; - text-align: center; - display: block; - margin-bottom: 10px; + margin-left: auto; + margin-right: auto; + text-align: center; + display: block; + margin-bottom: 10px; } #vmlist > div.row > div { - padding-bottom: 10px; + padding-bottom: 10px; } #vmlist div.col-md-3 > div.card:hover { - cursor: pointer; - border-color: rgb(8, 121, 250); + cursor: pointer; + border-color: rgb(8, 121, 250); } .vmtile > img { - margin-bottom: 2px; + margin-bottom: 2px; } .chat-table, .username-table { - overflow-y: auto; - border: 1px solid #575757; + overflow-y: auto; + border: 1px solid #575757; } .chat-table { - height: 30vh; + height: 30vh; } .username-table { - max-height: 30vh; + max-height: 30vh; } .username-table > table > thead { - position: sticky; - top: 0; + position: sticky; + top: 0; } #turnstatus { - text-align: center; + text-align: center; } #voteResetPanel { - text-align: center; + text-align: center; } .focused { - box-shadow: 0 0 9px 0 rgba(45,213,255,.75); - -moz-box-shadow: 0 0 9px 0 rgba(45,213,255,.75); - -webkit-box-shadow: 0 0 9px 0 rgba(45,213,255,.75) + box-shadow: 0 0 9px 0 rgba(45,213,255,.75); + -moz-box-shadow: 0 0 9px 0 rgba(45,213,255,.75); + -webkit-box-shadow: 0 0 9px 0 rgba(45,213,255,.75) } .waiting { - box-shadow: 0 0 9px 0 rgba(242,255,63,.75); - -moz-box-shadow: 0 0 9px 0 rgba(242,255,63,.75); - -webkit-box-shadow: 0 0 9px 0 rgba(242,255,63,.75) + box-shadow: 0 0 9px 0 rgba(242,255,63,.75); + -moz-box-shadow: 0 0 9px 0 rgba(242,255,63,.75); + -webkit-box-shadow: 0 0 9px 0 rgba(242,255,63,.75) } #staffbtns { - display: none; + display: none; } #staffbtns > button { - display: none; + display: none; } #qemuMonitorOutput { - height: 180px; + height: 180px; } #xssCheckboxContainer { - display: none; + display: none; } #forceVotePanel { - display: none; + display: none; } tr.user-admin > td, .chat-username-admin { - color: #FF0000 !important; + color: #FF0000 !important; } tr.user-moderator > td, .chat-username-moderator { - color: #00FF00 !important; + color: #00FF00 !important; } tr.user-turn > td { - background-color: #cfe2ff !important; - --bs-table-bg-state: #cfe2ff !important; - color: #000000; - --bs-table-color: #000000; + background-color: #cfe2ff !important; + --bs-table-bg-state: #cfe2ff !important; + color: #000000; + --bs-table-color: #000000; } tr.user-turn:hover, tr.user-turn > td:hover { - background-color: #bacbe6 !important; - --bs-table-bg-state: #bacbe6 !important; + background-color: #bacbe6 !important; + --bs-table-bg-state: #bacbe6 !important; } tr.user-waiting > td { - background-color: #fff3cd !important; - --bs-table-bg-state: #fff3cd !important; - color: #000000; - --bs-table-color: #000000; + background-color: #fff3cd !important; + --bs-table-bg-state: #fff3cd !important; + color: #000000; + --bs-table-color: #000000; } .tr.user-waiting:hover, tr.user-waiting > td:hover { - background-color: #ece1be !important; - --bs-table-bg-state: #ece1be !important; + background-color: #ece1be !important; + --bs-table-bg-state: #ece1be !important; } .user-current { - font-style: italic; -} \ No newline at end of file + font-style: italic; +} + +/* Start OSK */ +.osk-container { +display: flex; +justify-content: center; +width: 1024px; +margin: 0 auto; +margin-bottom: 10px; +border-radius: 5px; +} + +.simple-keyboard.hg-theme-default { +display: inline-block; +} + +.osk-main.simple-keyboard { +width: 640px; +min-width: 640px; +background: none; +} + +.osk-main.simple-keyboard .hg-row:first-child { +margin-bottom: 8.51px; +} + +.osk-arrows.simple-keyboard { +align-self: flex-end; +background: none; +} + +.simple-keyboard .hg-button.selectedButton { +background: rgba(5, 25, 70, 0.53); +color: white; +} + +.simple-keyboard .hg-button.emptySpace { +pointer-events: none; +background: none; +border: none; +box-shadow: none; +} + +.osk-arrows .hg-row { +justify-content: center; +} + +.osk-arrows .hg-button { +width: 50px; +flex-grow: 0 !important; +justify-content: center !important; +display: flex !important; +align-items: center !important; +} + +.controlArrows { +display: flex; +align-items: center; +justify-content: space-between; +flex-flow: column; +} + +.osk-control.simple-keyboard { +background: none; +} + +.osk-control.simple-keyboard .hg-row:first-child { +margin-bottom: 8.51px; +} + +.osk-control .hg-button { +width: 50px; +flex-grow: 0 !important; +justify-content: center !important; +display: flex !important; +align-items: center !important; +} + +.numPad { +display: flex; +align-items: flex-end; +} + +.osk-numpad.simple-keyboard { +background: none; +} + +.osk-numpad.simple-keyboard { +width: 160px; +} + +.osk-numpad.simple-keyboard .hg-button { +width: 50px; +justify-content: center; +display: flex; +align-items: center; +} + +.osk-numpadEnd.simple-keyboard { +width: 50px; +background: none; +margin: 0; +padding: 5px 5px 5px 0; +} + +.osk-numpadEnd.simple-keyboard .hg-button { +align-items: center; +justify-content: center; +display: flex; +} + +.osk-numpadEnd .hg-button.hg-standardBtn.hg-button-plus { +height: 85px; +} + +.osk-numpadEnd.simple-keyboard .hg-button.hg-button-enter { +height: 85px; +} + +.simple-keyboard.hg-theme-default .hg-button.hg-selectedButton { +background: rgba(5, 25, 70, 0.53); +color: white; +} + +.hg-button.hg-functionBtn.hg-button-space { +width: 350px; +} + +/* +Theme: cvmDark +*/ + +.simple-keyboard.cvmDark .hg-button { +border-bottom: none; +background: rgba(0, 0, 0, 0.5); +color: white; +} + +.simple-keyboard.cvmDark .hg-button:active { +background: #1c4995; +color: white; +} + +#root .simple-keyboard.cvmDark + .simple-keyboard-preview { +background: #1c4995; +} + +/* +Theme: cvmDisabled +*/ +.simple-keyboard.cvmDisabled .hg-button { +border-bottom: none; +pointer-events: none; +background: gray; +color: white; +} + +/* End OSK */ \ No newline at end of file diff --git a/src/html/index.html b/src/html/index.html index 2ec0446..5d60383 100644 --- a/src/html/index.html +++ b/src/html/index.html @@ -130,6 +130,7 @@
+ @@ -143,6 +144,19 @@
+
+
+ +
+
+
+
+ +
+
+
+
+
diff --git a/src/js/keyboard.d.ts b/src/js/keyboard.d.ts deleted file mode 100644 index 6443271..0000000 --- a/src/js/keyboard.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -declare function GetKeysym(keyCode : number, key : string, location : number) : number | undefined; - -export default GetKeysym; \ No newline at end of file diff --git a/src/js/keyboard.js b/src/js/keyboard.js deleted file mode 100644 index bec91bf..0000000 --- a/src/js/keyboard.js +++ /dev/null @@ -1,279 +0,0 @@ -// Pulled a bunch of functions out of the guac source code to get a keysym -// and then a wrapper -// shitty but it works so /shrug -// THIS SUCKS SO BAD AND I HATE IT PLEASE REWRITE ALL OF THIS - -export default function GetKeysym(keyCode, key, location) { - var keysym = keysym_from_key_identifier(key, location) - || keysym_from_keycode(keyCode, location); - - return keysym; -} - - -function keysym_from_key_identifier(identifier, location) { - - if (!identifier) - return null; - - var typedCharacter; - - // If identifier is U+xxxx, decode Unicode character - var unicodePrefixLocation = identifier.indexOf("U+"); - if (unicodePrefixLocation >= 0) { - var hex = identifier.substring(unicodePrefixLocation+2); - typedCharacter = String.fromCharCode(parseInt(hex, 16)); - } - - // If single character, use that as typed character - else if (identifier.length === 1) - typedCharacter = identifier; - - // Otherwise, look up corresponding keysym - else - return get_keysym(keyidentifier_keysym[identifier], location); - - // Get codepoint - var codepoint = typedCharacter.charCodeAt(0); - return keysym_from_charcode(codepoint); - -} - -function get_keysym(keysyms, location) { - - if (!keysyms) - return null; - - return keysyms[location] || keysyms[0]; -} - -function keysym_from_charcode(codepoint) { - - // Keysyms for control characters - if (isControlCharacter(codepoint)) return 0xFF00 | codepoint; - - // Keysyms for ASCII chars - if (codepoint >= 0x0000 && codepoint <= 0x00FF) - return codepoint; - - // Keysyms for Unicode - if (codepoint >= 0x0100 && codepoint <= 0x10FFFF) - return 0x01000000 | codepoint; - - return null; -} - - -function isControlCharacter(codepoint) { - return codepoint <= 0x1F || (codepoint >= 0x7F && codepoint <= 0x9F); -} - -function keysym_from_keycode(keyCode, location) { - return get_keysym(keycodeKeysyms[keyCode], location); -} - -function key_identifier_sane(keyCode, keyIdentifier) { - - // Missing identifier is not sane - if (!keyIdentifier) - return false; - - // Assume non-Unicode keyIdentifier values are sane - var unicodePrefixLocation = keyIdentifier.indexOf("U+"); - if (unicodePrefixLocation === -1) - return true; - - // If the Unicode codepoint isn't identical to the keyCode, - // then the identifier is likely correct - var codepoint = parseInt(keyIdentifier.substring(unicodePrefixLocation+2), 16); - if (keyCode !== codepoint) - return true; - - // The keyCodes for A-Z and 0-9 are actually identical to their - // Unicode codepoints - if ((keyCode >= 65 && keyCode <= 90) || (keyCode >= 48 && keyCode <= 57)) - return true; - - // The keyIdentifier does NOT appear sane - return false; - -} - -var keycodeKeysyms = { - 8: [0xFF08], // backspace - 9: [0xFF09], // tab - 12: [0xFF0B, 0xFF0B, 0xFF0B, 0xFFB5], // clear / KP 5 - 13: [0xFF0D], // enter - 16: [0xFFE1, 0xFFE1, 0xFFE2], // shift - 17: [0xFFE3, 0xFFE3, 0xFFE4], // ctrl - 18: [0xFFE9, 0xFFE9, 0xFE03], // alt - 19: [0xFF13], // pause/break - 20: [0xFFE5], // caps lock - 27: [0xFF1B], // escape - 32: [0x0020], // space - 33: [0xFF55, 0xFF55, 0xFF55, 0xFFB9], // page up / KP 9 - 34: [0xFF56, 0xFF56, 0xFF56, 0xFFB3], // page down / KP 3 - 35: [0xFF57, 0xFF57, 0xFF57, 0xFFB1], // end / KP 1 - 36: [0xFF50, 0xFF50, 0xFF50, 0xFFB7], // home / KP 7 - 37: [0xFF51, 0xFF51, 0xFF51, 0xFFB4], // left arrow / KP 4 - 38: [0xFF52, 0xFF52, 0xFF52, 0xFFB8], // up arrow / KP 8 - 39: [0xFF53, 0xFF53, 0xFF53, 0xFFB6], // right arrow / KP 6 - 40: [0xFF54, 0xFF54, 0xFF54, 0xFFB2], // down arrow / KP 2 - 45: [0xFF63, 0xFF63, 0xFF63, 0xFFB0], // insert / KP 0 - 46: [0xFFFF, 0xFFFF, 0xFFFF, 0xFFAE], // delete / KP decimal - 91: [0xFFEB], // left window key (hyper_l) - 92: [0xFF67], // right window key (menu key?) - 93: null, // select key - 96: [0xFFB0], // KP 0 - 97: [0xFFB1], // KP 1 - 98: [0xFFB2], // KP 2 - 99: [0xFFB3], // KP 3 - 100: [0xFFB4], // KP 4 - 101: [0xFFB5], // KP 5 - 102: [0xFFB6], // KP 6 - 103: [0xFFB7], // KP 7 - 104: [0xFFB8], // KP 8 - 105: [0xFFB9], // KP 9 - 106: [0xFFAA], // KP multiply - 107: [0xFFAB], // KP add - 109: [0xFFAD], // KP subtract - 110: [0xFFAE], // KP decimal - 111: [0xFFAF], // KP divide - 112: [0xFFBE], // f1 - 113: [0xFFBF], // f2 - 114: [0xFFC0], // f3 - 115: [0xFFC1], // f4 - 116: [0xFFC2], // f5 - 117: [0xFFC3], // f6 - 118: [0xFFC4], // f7 - 119: [0xFFC5], // f8 - 120: [0xFFC6], // f9 - 121: [0xFFC7], // f10 - 122: [0xFFC8], // f11 - 123: [0xFFC9], // f12 - 144: [0xFF7F], // num lock - 145: [0xFF14], // scroll lock - 225: [0xFE03] // altgraph (iso_level3_shift) -}; - -var keyidentifier_keysym = { - "Again": [0xFF66], - "AllCandidates": [0xFF3D], - "Alphanumeric": [0xFF30], - "Alt": [0xFFE9, 0xFFE9, 0xFE03], - "Attn": [0xFD0E], - "AltGraph": [0xFE03], - "ArrowDown": [0xFF54], - "ArrowLeft": [0xFF51], - "ArrowRight": [0xFF53], - "ArrowUp": [0xFF52], - "Backspace": [0xFF08], - "CapsLock": [0xFFE5], - "Cancel": [0xFF69], - "Clear": [0xFF0B], - "Convert": [0xFF21], - "Copy": [0xFD15], - "Crsel": [0xFD1C], - "CrSel": [0xFD1C], - "CodeInput": [0xFF37], - "Compose": [0xFF20], - "Control": [0xFFE3, 0xFFE3, 0xFFE4], - "ContextMenu": [0xFF67], - "DeadGrave": [0xFE50], - "DeadAcute": [0xFE51], - "DeadCircumflex": [0xFE52], - "DeadTilde": [0xFE53], - "DeadMacron": [0xFE54], - "DeadBreve": [0xFE55], - "DeadAboveDot": [0xFE56], - "DeadUmlaut": [0xFE57], - "DeadAboveRing": [0xFE58], - "DeadDoubleacute": [0xFE59], - "DeadCaron": [0xFE5A], - "DeadCedilla": [0xFE5B], - "DeadOgonek": [0xFE5C], - "DeadIota": [0xFE5D], - "DeadVoicedSound": [0xFE5E], - "DeadSemivoicedSound": [0xFE5F], - "Delete": [0xFFFF], - "Down": [0xFF54], - "End": [0xFF57], - "Enter": [0xFF0D], - "EraseEof": [0xFD06], - "Escape": [0xFF1B], - "Execute": [0xFF62], - "Exsel": [0xFD1D], - "ExSel": [0xFD1D], - "F1": [0xFFBE], - "F2": [0xFFBF], - "F3": [0xFFC0], - "F4": [0xFFC1], - "F5": [0xFFC2], - "F6": [0xFFC3], - "F7": [0xFFC4], - "F8": [0xFFC5], - "F9": [0xFFC6], - "F10": [0xFFC7], - "F11": [0xFFC8], - "F12": [0xFFC9], - "F13": [0xFFCA], - "F14": [0xFFCB], - "F15": [0xFFCC], - "F16": [0xFFCD], - "F17": [0xFFCE], - "F18": [0xFFCF], - "F19": [0xFFD0], - "F20": [0xFFD1], - "F21": [0xFFD2], - "F22": [0xFFD3], - "F23": [0xFFD4], - "F24": [0xFFD5], - "Find": [0xFF68], - "GroupFirst": [0xFE0C], - "GroupLast": [0xFE0E], - "GroupNext": [0xFE08], - "GroupPrevious": [0xFE0A], - "FullWidth": null, - "HalfWidth": null, - "HangulMode": [0xFF31], - "Hankaku": [0xFF29], - "HanjaMode": [0xFF34], - "Help": [0xFF6A], - "Hiragana": [0xFF25], - "HiraganaKatakana": [0xFF27], - "Home": [0xFF50], - "Hyper": [0xFFED, 0xFFED, 0xFFEE], - "Insert": [0xFF63], - "JapaneseHiragana": [0xFF25], - "JapaneseKatakana": [0xFF26], - "JapaneseRomaji": [0xFF24], - "JunjaMode": [0xFF38], - "KanaMode": [0xFF2D], - "KanjiMode": [0xFF21], - "Katakana": [0xFF26], - "Left": [0xFF51], - "Meta": [0xFFE7, 0xFFE7, 0xFFE8], - "ModeChange": [0xFF7E], - "NumLock": [0xFF7F], - "PageDown": [0xFF56], - "PageUp": [0xFF55], - "Pause": [0xFF13], - "Play": [0xFD16], - "PreviousCandidate": [0xFF3E], - "PrintScreen": [0xFD1D], - "Redo": [0xFF66], - "Right": [0xFF53], - "RomanCharacters": null, - "Scroll": [0xFF14], - "Select": [0xFF60], - "Separator": [0xFFAC], - "Shift": [0xFFE1, 0xFFE1, 0xFFE2], - "SingleCandidate": [0xFF3C], - "Super": [0xFFEB, 0xFFEB, 0xFFEC], - "Tab": [0xFF09], - "Up": [0xFF52], - "Undo": [0xFF65], - "Win": [0xFFEB], - "Zenkaku": [0xFF28], - "ZenkakuHankaku": [0xFF2A] -}; \ No newline at end of file diff --git a/src/ts/keyboard.ts b/src/ts/keyboard.ts new file mode 100644 index 0000000..d06cb0f --- /dev/null +++ b/src/ts/keyboard.ts @@ -0,0 +1,414 @@ +// Pulled a bunch of functions out of the guac source code to get a keysym +// and then a wrapper +// shitty but it works so /shrug +// THIS SUCKS SO BAD AND I HATE IT PLEASE REWRITE ALL OF THIS + +export default function GetKeysym( + keyCode: number, + key: string, + location: number + ): number | null { + let keysym = + keysym_from_key_identifier(key, location) || + keysym_from_keycode(keyCode, location); + return keysym; + } + + function keysym_from_key_identifier(identifier: string, location: number): number | null { + if (!identifier) return null; + + let typedCharacter: string | undefined; + + // If identifier is U+xxxx, decode Unicode character + const unicodePrefixLocation = identifier.indexOf("U+"); + if (unicodePrefixLocation >= 0) { + const hex = identifier.substring(unicodePrefixLocation + 2); + typedCharacter = String.fromCharCode(parseInt(hex, 16)); + } else if (identifier.length === 1) typedCharacter = identifier; + else return get_keysym(keyidentifier_keysym[identifier], location); + + if (!typedCharacter) return null; + + const codepoint = typedCharacter.charCodeAt(0); + return keysym_from_charcode(codepoint); + } + + function get_keysym(keysyms: number[] | null, location: number): number | null { + if (!keysyms) return null; + return keysyms[location] || keysyms[0]; + } + + function keysym_from_charcode(codepoint: number): number | null { + if (isControlCharacter(codepoint)) return 0xff00 | codepoint; + if (codepoint >= 0x0000 && codepoint <= 0x00ff) return codepoint; + if (codepoint >= 0x0100 && codepoint <= 0x10ffff) return 0x01000000 | codepoint; + return null; + } + + function isControlCharacter(codepoint: number): boolean { + return codepoint <= 0x1f || (codepoint >= 0x7f && codepoint <= 0x9f); + } + + function keysym_from_keycode(keyCode: number, location: number): number | null { + return get_keysym(keycodeKeysyms[keyCode], location); + } + + function key_identifier_sane(keyCode: number, keyIdentifier: string): boolean { + if (!keyIdentifier) return false; + const unicodePrefixLocation = keyIdentifier.indexOf("U+"); + if (unicodePrefixLocation === -1) return true; + + const codepoint = parseInt(keyIdentifier.substring(unicodePrefixLocation + 2), 16); + if (keyCode !== codepoint) return true; + if ((keyCode >= 65 && keyCode <= 90) || (keyCode >= 48 && keyCode <= 57)) return true; + return false; + } + + export function OSK_buttonToKeysym(button: string): number | null { + const keyMapping = OSK_keyMappings.find((mapping) => mapping.includes(button)); + if (keyMapping) { + const [, keyCode, keyIdentifier, key, location] = keyMapping; + return GetKeysym(keyCode, key, location); + } + return null; + } + +interface KeyIdentifierKeysym { + [key: string]: number[] | null; +} + +interface KeyCodeKeysyms { + [key: number]: (number[] | null); +} + +var keycodeKeysyms: KeyCodeKeysyms = { + 8: [0xFF08], // backspace + 9: [0xFF09], // tab + 12: [0xFF0B, 0xFF0B, 0xFF0B, 0xFFB5], // clear / KP 5 + 13: [0xFF0D], // enter + 16: [0xFFE1, 0xFFE1, 0xFFE2], // shift + 17: [0xFFE3, 0xFFE3, 0xFFE4], // ctrl + 18: [0xFFE9, 0xFFE9, 0xFE03], // alt + 19: [0xFF13], // pause/break + 20: [0xFFE5], // caps lock + 27: [0xFF1B], // escape + 32: [0x0020], // space + 33: [0xFF55, 0xFF55, 0xFF55, 0xFFB9], // page up / KP 9 + 34: [0xFF56, 0xFF56, 0xFF56, 0xFFB3], // page down / KP 3 + 35: [0xFF57, 0xFF57, 0xFF57, 0xFFB1], // end / KP 1 + 36: [0xFF50, 0xFF50, 0xFF50, 0xFFB7], // home / KP 7 + 37: [0xFF51, 0xFF51, 0xFF51, 0xFFB4], // left arrow / KP 4 + 38: [0xFF52, 0xFF52, 0xFF52, 0xFFB8], // up arrow / KP 8 + 39: [0xFF53, 0xFF53, 0xFF53, 0xFFB6], // right arrow / KP 6 + 40: [0xFF54, 0xFF54, 0xFF54, 0xFFB2], // down arrow / KP 2 + 45: [0xFF63, 0xFF63, 0xFF63, 0xFFB0], // insert / KP 0 + 46: [0xFFFF, 0xFFFF, 0xFFFF, 0xFFAE], // delete / KP decimal + 91: [0xFFEB], // left window key (hyper_l) + 92: [0xFF67], // right window key (menu key?) + 93: null, // select key + 96: [0xFFB0], // KP 0 + 97: [0xFFB1], // KP 1 + 98: [0xFFB2], // KP 2 + 99: [0xFFB3], // KP 3 + 100: [0xFFB4], // KP 4 + 101: [0xFFB5], // KP 5 + 102: [0xFFB6], // KP 6 + 103: [0xFFB7], // KP 7 + 104: [0xFFB8], // KP 8 + 105: [0xFFB9], // KP 9 + 106: [0xFFAA], // KP multiply + 107: [0xFFAB], // KP add + 109: [0xFFAD], // KP subtract + 110: [0xFFAE], // KP decimal + 111: [0xFFAF], // KP divide + 112: [0xFFBE], // f1 + 113: [0xFFBF], // f2 + 114: [0xFFC0], // f3 + 115: [0xFFC1], // f4 + 116: [0xFFC2], // f5 + 117: [0xFFC3], // f6 + 118: [0xFFC4], // f7 + 119: [0xFFC5], // f8 + 120: [0xFFC6], // f9 + 121: [0xFFC7], // f10 + 122: [0xFFC8], // f11 + 123: [0xFFC9], // f12 + 144: [0xFF7F], // num lock + 145: [0xFF14], // scroll lock + 225: [0xFE03] // altgraph (iso_level3_shift) +}; + +var keyidentifier_keysym: KeyIdentifierKeysym = { + "Again": [0xFF66], + "AllCandidates": [0xFF3D], + "Alphanumeric": [0xFF30], + "Alt": [0xFFE9, 0xFFE9, 0xFE03], + "Attn": [0xFD0E], + "AltGraph": [0xFE03], + "ArrowDown": [0xFF54], + "ArrowLeft": [0xFF51], + "ArrowRight": [0xFF53], + "ArrowUp": [0xFF52], + "Backspace": [0xFF08], + "CapsLock": [0xFFE5], + "Cancel": [0xFF69], + "Clear": [0xFF0B], + "Convert": [0xFF21], + "Copy": [0xFD15], + "Crsel": [0xFD1C], + "CrSel": [0xFD1C], + "CodeInput": [0xFF37], + "Compose": [0xFF20], + "Control": [0xFFE3, 0xFFE3, 0xFFE4], + "ContextMenu": [0xFF67], + "DeadGrave": [0xFE50], + "DeadAcute": [0xFE51], + "DeadCircumflex": [0xFE52], + "DeadTilde": [0xFE53], + "DeadMacron": [0xFE54], + "DeadBreve": [0xFE55], + "DeadAboveDot": [0xFE56], + "DeadUmlaut": [0xFE57], + "DeadAboveRing": [0xFE58], + "DeadDoubleacute": [0xFE59], + "DeadCaron": [0xFE5A], + "DeadCedilla": [0xFE5B], + "DeadOgonek": [0xFE5C], + "DeadIota": [0xFE5D], + "DeadVoicedSound": [0xFE5E], + "DeadSemivoicedSound": [0xFE5F], + "Delete": [0xFFFF], + "Down": [0xFF54], + "End": [0xFF57], + "Enter": [0xFF0D], + "EraseEof": [0xFD06], + "Escape": [0xFF1B], + "Execute": [0xFF62], + "Exsel": [0xFD1D], + "ExSel": [0xFD1D], + "F1": [0xFFBE], + "F2": [0xFFBF], + "F3": [0xFFC0], + "F4": [0xFFC1], + "F5": [0xFFC2], + "F6": [0xFFC3], + "F7": [0xFFC4], + "F8": [0xFFC5], + "F9": [0xFFC6], + "F10": [0xFFC7], + "F11": [0xFFC8], + "F12": [0xFFC9], + "F13": [0xFFCA], + "F14": [0xFFCB], + "F15": [0xFFCC], + "F16": [0xFFCD], + "F17": [0xFFCE], + "F18": [0xFFCF], + "F19": [0xFFD0], + "F20": [0xFFD1], + "F21": [0xFFD2], + "F22": [0xFFD3], + "F23": [0xFFD4], + "F24": [0xFFD5], + "Find": [0xFF68], + "GroupFirst": [0xFE0C], + "GroupLast": [0xFE0E], + "GroupNext": [0xFE08], + "GroupPrevious": [0xFE0A], + "FullWidth": null, + "HalfWidth": null, + "HangulMode": [0xFF31], + "Hankaku": [0xFF29], + "HanjaMode": [0xFF34], + "Help": [0xFF6A], + "Hiragana": [0xFF25], + "HiraganaKatakana": [0xFF27], + "Home": [0xFF50], + "Hyper": [0xFFED, 0xFFED, 0xFFEE], + "Insert": [0xFF63], + "JapaneseHiragana": [0xFF25], + "JapaneseKatakana": [0xFF26], + "JapaneseRomaji": [0xFF24], + "JunjaMode": [0xFF38], + "KanaMode": [0xFF2D], + "KanjiMode": [0xFF21], + "Katakana": [0xFF26], + "Left": [0xFF51], + "Meta": [0xFFE7, 0xFFE7, 0xFFE8], + "ModeChange": [0xFF7E], + "NumLock": [0xFF7F], + "PageDown": [0xFF56], + "PageUp": [0xFF55], + "Pause": [0xFF13], + "Play": [0xFD16], + "PreviousCandidate": [0xFF3E], + "PrintScreen": [0xFD1D], + "Redo": [0xFF66], + "Right": [0xFF53], + "RomanCharacters": null, + "Scroll": [0xFF14], + "Select": [0xFF60], + "Separator": [0xFFAC], + "Shift": [0xFFE1, 0xFFE1, 0xFFE2], + "SingleCandidate": [0xFF3C], + "Super": [0xFFEB, 0xFFEB, 0xFFEC], + "Tab": [0xFF09], + "Up": [0xFF52], + "Undo": [0xFF65], + "Win": [0xFFEB], + "Zenkaku": [0xFF28], + "ZenkakuHankaku": [0xFF2A] +}; + +const OSK_keyMappings: [string, number, string, string, number][] = [ + ["!", 49, "Digit1", "!", 0], + ["#", 51, "Digit3", "#", 0], + ["$", 52, "Digit4", "$", 0], + ["%", 53, "Digit5", "%", 0], + ["&", 55, "Digit7", "&", 0], + ["'", 222, "Quote", "'", 0], + ["(", 57, "Digit9", "(", 0], + [")", 48, "Digit0", ")", 0], + ["*", 56, "Digit8", "*", 0], + ["+", 187, "Equal", "+", 0], + [",", 188, "Comma", ",", 0], + ["-", 189, "Minus", "-", 0], + [".", 190, "Period", ".", 0], + ["/", 191, "Slash", "/", 0], + ["0", 48, "Digit0", "0", 0], + ["1", 49, "Digit1", "1", 0], + ["2", 50, "Digit2", "2", 0], + ["3", 51, "Digit3", "3", 0], + ["4", 52, "Digit4", "4", 0], + ["5", 53, "Digit5", "5", 0], + ["6", 54, "Digit6", "6", 0], + ["7", 55, "Digit7", "7", 0], + ["8", 56, "Digit8", "8", 0], + ["9", 57, "Digit9", "9", 0], + [":", 186, "Semicolon", ":", 0], + [";", 186, "Semicolon", ";", 0], + ["<", 188, "Comma", "<", 0], + ["=", 187, "Equal", "=", 0], + [">", 190, "Period", ">", 0], + ["?", 191, "Slash", "?", 0], + ["@", 50, "Digit2", "@", 0], + ["A", 65, "KeyA", "A", 0], + ["B", 66, "KeyB", "B", 0], + ["C", 67, "KeyC", "C", 0], + ["D", 68, "KeyD", "D", 0], + ["E", 69, "KeyE", "E", 0], + ["F", 70, "KeyF", "F", 0], + ["G", 71, "KeyG", "G", 0], + ["H", 72, "KeyH", "H", 0], + ["I", 73, "KeyI", "I", 0], + ["J", 74, "KeyJ", "J", 0], + ["K", 75, "KeyK", "K", 0], + ["L", 76, "KeyL", "L", 0], + ["M", 77, "KeyM", "M", 0], + ["N", 78, "KeyN", "N", 0], + ["O", 79, "KeyO", "O", 0], + ["P", 80, "KeyP", "P", 0], + ["Q", 81, "KeyQ", "Q", 0], + ["R", 82, "KeyR", "R", 0], + ["S", 83, "KeyS", "S", 0], + ["T", 84, "KeyT", "T", 0], + ["U", 85, "KeyU", "U", 0], + ["V", 86, "KeyV", "V", 0], + ["W", 87, "KeyW", "W", 0], + ["X", 88, "KeyX", "X", 0], + ["Y", 89, "KeyY", "Y", 0], + ["Z", 90, "KeyZ", "Z", 0], + ["[", 219, "BracketLeft", "[", 0], + ["\\", 220, "Backslash", "\\", 0], + ["]", 221, "BracketRight", "]", 0], + ["^", 54, "Digit6", "^", 0], + ["_", 189, "Minus", "_", 0], + ["`", 192, "Backquote", "`", 0], + ["a", 65, "KeyA", "a", 0], + ["b", 66, "KeyB", "b", 0], + ["c", 67, "KeyC", "c", 0], + ["d", 68, "KeyD", "d", 0], + ["e", 69, "KeyE", "e", 0], + ["f", 70, "KeyF", "f", 0], + ["g", 71, "KeyG", "g", 0], + ["h", 72, "KeyH", "h", 0], + ["i", 73, "KeyI", "i", 0], + ["j", 74, "KeyJ", "j", 0], + ["k", 75, "KeyK", "k", 0], + ["l", 76, "KeyL", "l", 0], + ["m", 77, "KeyM", "m", 0], + ["n", 78, "KeyN", "n", 0], + ["o", 79, "KeyO", "o", 0], + ["p", 80, "KeyP", "p", 0], + ["q", 81, "KeyQ", "q", 0], + ["r", 82, "KeyR", "r", 0], + ["s", 83, "KeyS", "s", 0], + ["t", 84, "KeyT", "t", 0], + ["u", 85, "KeyU", "u", 0], + ["v", 86, "KeyV", "v", 0], + ["w", 87, "KeyW", "w", 0], + ["x", 88, "KeyX", "x", 0], + ["y", 89, "KeyY", "y", 0], + ["z", 90, "KeyZ", "z", 0], + ["{", 219, "BracketLeft", "{", 0], + ["{altleft}", 18, "AltLeft", "AltLeft", 1], + ["{altright}", 18, "AltRight", "AltRight", 2], + ["{arrowdown}", 40, "ArrowDown", "ArrowDown", 0], + ["{arrowleft}", 37, "ArrowLeft", "ArrowLeft", 0], + ["{arrowright}", 39, "ArrowRight", "ArrowRight", 0], + ["{arrowup}", 38, "ArrowUp", "ArrowUp", 0], + ["{backspace}", 8, "Backspace", "Backspace", 0], + ["{capslock}", 20, "CapsLock", "CapsLock", 0], + ["{controlleft}", 17, "ControlLeft", "ControlLeft", 1], + ["{controlright}", 17, "ControlRight", "ControlRight", 2], + ["{delete}", 46, "Delete", "Delete", 0], + ["{end}", 35, "End", "End", 0], + ["{enter}", 13, "Enter", "Enter", 0], + ["{escape}", 27, "Escape", "Escape", 0], + ["{f10}", 121, "F10", "F10", 0], + ["{f11}", 122, "F11", "F11", 0], + ["{f12}", 123, "F12", "F12", 0], + ["{f1}", 112, "F1", "F1", 0], + ["{f2}", 113, "F2", "F2", 0], + ["{f3}", 114, "F3", "F3", 0], + ["{f4}", 115, "F4", "F4", 0], + ["{f5}", 116, "F5", "F5", 0], + ["{f6}", 117, "F6", "F6", 0], + ["{f7}", 118, "F7", "F7", 0], + ["{f8}", 119, "F8", "F8", 0], + ["{f9}", 120, "F9", "F9", 0], + ["{home}", 36, "Home", "Home", 0], + ["{insert}", 45, "Insert", "Insert", 0], + ["{metaleft}", 91, "OSLeft", "OSLeft", 1], + ["{metaright}", 92, "OSRight", "OSRight", 2], + ["{numlock}", 144, "NumLock", "NumLock", 0], + ["{numpad0}", 96, "Numpad0", "Numpad0", 3], + ["{numpad1}", 97, "Numpad1", "Numpad1", 3], + ["{numpad2}", 98, "Numpad2", "Numpad2", 3], + ["{numpad3}", 99, "Numpad3", "Numpad3", 3], + ["{numpad4}", 100, "Numpad4", "Numpad4", 3], + ["{numpad5}", 101, "Numpad5", "Numpad5", 3], + ["{numpad6}", 102, "Numpad6", "Numpad6", 3], + ["{numpad7}", 103, "Numpad7", "Numpad7", 3], + ["{numpad8}", 104, "Numpad8", "Numpad8", 3], + ["{numpad9}", 105, "Numpad9", "Numpad9", 3], + ["{numpadadd}", 107, "NumpadAdd", "NumpadAdd", 3], + ["{numpaddecimal}", 110, "NumpadDecimal", "NumpadDecimal", 3], + ["{numpaddivide}", 111, "NumpadDivide", "NumpadDivide", 3], + ["{numpadenter}", 13, "NumpadEnter", "NumpadEnter", 3], + ["{numpadmultiply}", 106, "NumpadMultiply", "NumpadMultiply", 3], + ["{numpadsubtract}", 109, "NumpadSubtract", "NumpadSubtract", 3], + ["{pagedown}", 34, "PageDown", "PageDown", 0], + ["{pageup}", 33, "PageUp", "PageUp", 0], + ["{pause}", 19, "Pause", "Pause", 0], + ["{prtscr}", 44, "PrintScreen", "PrintScreen", 0], + ["{scrolllock}", 145, "ScrollLock", "ScrollLock", 0], + ["{shiftleft}", 16, "ShiftLeft", "ShiftLeft", 1], + ["{shiftright}", 16, "ShiftRight", "ShiftRight", 2], + ["{space}", 32, "Space", "Space", 0], + ["{tab}", 9, "Tab", "Tab", 0], + ["|", 220, "Backslash", "|", 0], + ["}", 221, "BracketRight", "}", 0], + ["~", 192, "Backquote", "~", 0], + ['"', 222, "Quote", '"', 0] +]; diff --git a/src/ts/main.ts b/src/ts/main.ts index acab26e..ea47f20 100644 --- a/src/ts/main.ts +++ b/src/ts/main.ts @@ -4,6 +4,9 @@ import { Config } from "../../Config.js"; import { Rank } from "./protocol/Permissions.js"; import { User } from "./protocol/User.js"; import TurnStatus from "./protocol/TurnStatus.js"; +import Keyboard from "simple-keyboard"; +import { OSK_buttonToKeysym } from "./keyboard"; +import "simple-keyboard/build/css/index.css"; // Elements const w = window as any; @@ -19,11 +22,198 @@ const elements = { username: document.getElementById("username") as HTMLSpanElement, chatinput: document.getElementById("chat-input") as HTMLInputElement, sendChatBtn: document.getElementById("sendChatBtn") as HTMLButtonElement, + takeTurnBtn: document.getElementById("takeTurnBtn") as HTMLButtonElement, changeUsernameBtn: document.getElementById("changeUsernameBtn") as HTMLButtonElement, turnBtnText: document.getElementById("turnBtnText") as HTMLSpanElement, turnstatus: document.getElementById("turnstatus") as HTMLParagraphElement, - takeTurnBtn: document.getElementById("takeTurnBtn") as HTMLButtonElement, + osk: window.document.getElementById("oskBtn") as HTMLButtonElement, + oskContainer: document.getElementById("osk-container") as HTMLDivElement } + +/* Start OSK */ +let commonKeyboardOptions = { + onKeyPress: (button: string) => onKeyPress(button), + theme: "simple-keyboard hg-theme-default cvmDark cvmDisabled hg-layout-default", + syncInstanceInputs: true, + mergeDisplay: true + }; + + let keyboard = new Keyboard(".osk-main", { + ...commonKeyboardOptions, + layout: { + default: [ + "{escape} {f1} {f2} {f3} {f4} {f5} {f6} {f7} {f8} {f9} {f10} {f11} {f12}", + "` 1 2 3 4 5 6 7 8 9 0 - = {backspace}", + "{tab} q w e r t y u i o p [ ] \\", + "{capslock} a s d f g h j k l ; ' {enter}", + "{shiftleft} z x c v b n m , . / {shiftright}", + "{controlleft} {metaleft} {altleft} {space} {altright} {metaright} {controlright}" + ], + shift: [ + "{escape} {f1} {f2} {f3} {f4} {f5} {f6} {f7} {f8} {f9} {f10} {f11} {f12}", + "~ ! @ # $ % ^ & * ( ) _ + {backspace}", + "{tab} Q W E R T Y U I O P { } |", + '{capslock} A S D F G H J K L : " {enter}', + "{shiftleft} Z X C V B N M < > ? {shiftright}", + "{controlleft} {metaleft} {altleft} {space} {altright} {metaright} {controlright}" + ], + capslock: [ + "{escape} {f1} {f2} {f3} {f4} {f5} {f6} {f7} {f8} {f9} {f10} {f11} {f12}", + "` 1 2 3 4 5 6 7 8 9 0 - = {backspace}", + "{tab} Q W E R T Y U I O P [ ] \\", + "{capslock} A S D F G H J K L ; ' {enter}", + "{shiftleft} Z X C V B N M , . / {shiftright}", + "{controlleft} {metaleft} {altleft} {space} {altright} {metaright} {controlright}" + ], + shiftcaps: [ + "{escape} {f1} {f2} {f3} {f4} {f5} {f6} {f7} {f8} {f9} {f10} {f11} {f12}", + "~ ! @ # $ % ^ & * ( ) _ + {backspace}", + "{tab} q w e r t y u i o p { } |", + '{capslock} a s d f g h j k l : " {enter}', + "{shiftleft} z x c v b n m < > ? {shiftright}", + "{controlleft} {metaleft} {altleft} {space} {altright} {metaright} {controlright}" + ] + }, + display: { + "{escape}": "Esc", + "{tab}": "Tab", + "{backspace}": "Back", + "{enter}": "Enter", + "{capslock}": "Caps", + "{shiftleft}": "Shift", + "{shiftright}": "Shift", + "{controlleft}": "Ctrl", + "{controlright}": "Ctrl", + "{altleft}": "Alt", + "{altright}": "Alt", + "{metaleft}": "Super", + "{metaright}": "Menu" + } + }); + + let keyboardControlPad = new Keyboard(".osk-control", { + ...commonKeyboardOptions, + layout: { + default: [ + "{prtscr} {scrolllock} {pause}", + "{insert} {home} {pageup}", + "{delete} {end} {pagedown}" + ] + }, + display: { + "{prtscr}": "Print", + "{scrolllock}": "Scroll", + "{pause}": "Pause", + "{insert}": "Ins", + "{home}": "Home", + "{pageup}": "PgUp", + "{delete}": "Del", + "{end}": "End", + "{pagedown}": "PgDn", + } + }); + + let keyboardArrows = new Keyboard(".osk-arrows", { + ...commonKeyboardOptions, + layout: { + default: ["{arrowup}", "{arrowleft} {arrowdown} {arrowright}"] + } + }); + + let keyboardNumPad = new Keyboard(".osk-numpad", { + ...commonKeyboardOptions, + layout: { + default: [ + "{numlock} {numpaddivide} {numpadmultiply}", + "{numpad7} {numpad8} {numpad9}", + "{numpad4} {numpad5} {numpad6}", + "{numpad1} {numpad2} {numpad3}", + "{numpad0} {numpaddecimal}" + ] + } + }); + + let keyboardNumPadEnd = new Keyboard(".osk-numpadEnd", { + ...commonKeyboardOptions, + layout: { + default: ["{numpadsubtract}", "{numpadadd}", "{numpadenter}"] + } + }); + + let shiftHeld = false; + let ctrlHeld = false; + let capsHeld = false; + let altHeld = false; + let metaHeld = false; + + const setButtonBackground = (selectors: string, condition: boolean) => { + for(let button of document.querySelectorAll(selectors) as NodeListOf) { + button.style.backgroundColor = condition ? "#1c4995" : "rgba(0, 0, 0, 0.5)"; + }; + }; + + const enableOSK = (enable: boolean) => { + const theme = `simple-keyboard hg-theme-default cvmDark ${enable ? "" : "cvmDisabled"} hg-layout-default`; + [keyboard, keyboardControlPad, keyboardArrows, keyboardNumPad, keyboardNumPadEnd].forEach(part => { + part.setOptions({ + theme: theme, + }); + }); + + if(enable) updateOSKStyle(); + } + + const updateOSKStyle = () => { + setButtonBackground(".hg-button-shiftleft, .hg-button-shiftright", shiftHeld); + setButtonBackground(".hg-button-controlleft, .hg-button-controlright", ctrlHeld); + setButtonBackground(".hg-button-capslock", capsHeld); + setButtonBackground(".hg-button-altleft, .hg-button-altright", altHeld); + setButtonBackground(".hg-button-metaleft, .hg-button-metaright", metaHeld); + } + + + function onKeyPress(button: string) { + let keysym = OSK_buttonToKeysym(button); + if (!keysym) { + console.error(`no keysym for ${button}, report this!`); + return; + } + + switch (true) { + case button.startsWith("{shift"): + shiftHeld = !shiftHeld; + VM!.key(keysym, shiftHeld); + break; + case button.startsWith("{control"): + ctrlHeld = !ctrlHeld; + VM!.key(keysym, ctrlHeld); + break; + case button === "{capslock}": + capsHeld = !capsHeld; + VM!.key(keysym, capsHeld); + break; + case button.startsWith("{alt"): + altHeld = !altHeld; + VM!.key(keysym, altHeld); + break; + case button.startsWith("{meta"): + metaHeld = !metaHeld; + VM!.key(keysym, metaHeld); + break; + default: + VM!.key(keysym, true); + VM!.key(keysym, false); + } + + keyboard.setOptions({ + layoutName: shiftHeld && capsHeld ? "shiftcaps" : shiftHeld ? "shift" : capsHeld ? "capslock" : "default" + }); + + updateOSKStyle(); + } + + /* End OSK */ + var expectedClose = false; var turn = -1; // Listed VMs @@ -280,6 +470,8 @@ function turnUpdate(status : TurnStatus) { user.element.setAttribute("data-cvm-turn", "-1"); } elements.turnBtnText.innerHTML = "Take Turn"; + enableOSK(false); + if (status.user !== null) { var el = users.find(u => u.user === status.user)!.element; el!.classList.add("user-turn"); @@ -295,6 +487,7 @@ function turnUpdate(status : TurnStatus) { turnTimer = status.turnTime! / 1000; elements.turnBtnText.innerHTML = "End Turn"; VM!.canvas.classList.add("focused"); + enableOSK(true); } if (status.queue.some(u => u.username === w.username)) { turn = status.queue.findIndex(u => u.username === w.username) + 1; @@ -345,6 +538,8 @@ elements.takeTurnBtn.addEventListener('click', () => { VM?.turn(turn === -1); }); +elements.osk.addEventListener('click', () => elements.oskContainer.classList.toggle('d-none')); + // Public API w.collabvm = { openVM: openVM, diff --git a/src/ts/protocol/CollabVMClient.ts b/src/ts/protocol/CollabVMClient.ts index 54fa4ed..52108ee 100644 --- a/src/ts/protocol/CollabVMClient.ts +++ b/src/ts/protocol/CollabVMClient.ts @@ -5,7 +5,7 @@ import { User } from "./User.js"; import { Rank } from "./Permissions.js"; import TurnStatus from "./TurnStatus.js"; import Mouse from "./mouse.js"; -import GetKeysym from '../../js/keyboard'; +import GetKeysym from '../keyboard.js'; export default class CollabVMClient { // Fields @@ -65,7 +65,7 @@ export default class CollabVMClient { this.canvas.addEventListener('keydown', (e : KeyboardEvent) => { if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return; var keysym = GetKeysym(e.keyCode, e.key, e.location); - if (keysym === undefined) return; + if (keysym === null) return; this.key(keysym, true); }, { capture: true @@ -73,7 +73,7 @@ export default class CollabVMClient { this.canvas.addEventListener('keyup', (e : KeyboardEvent) => { if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return; var keysym = GetKeysym(e.keyCode, e.key, e.location); - if (keysym === undefined) return; + if (keysym === null) return; this.key(keysym, false); }, { capture: true