From 80cb8ffddd49d828fe3b9c94b556ad154381b225 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 24 Jan 2017 15:16:10 +0100 Subject: [PATCH] Use standard DOM identifiers for physical keys --- core/input/util.js | 101 +++++++++++---- core/input/vkeys.js | 116 +++++++++++++++++ core/input/xtscancodes.js | 2 - tests/input.html | 1 + tests/test.helper.js | 65 ++++++++++ tests/test.keyboard.js | 256 +++++++++++++++++++------------------- tests/vnc_perf.html | 3 +- 7 files changed, 389 insertions(+), 155 deletions(-) create mode 100644 core/input/vkeys.js diff --git a/core/input/util.js b/core/input/util.js index 9124c19..508193e 100644 --- a/core/input/util.js +++ b/core/input/util.js @@ -1,5 +1,6 @@ import KeyTable from "./keysym.js"; import keysyms from "./keysymdef.js"; +import vkeys from "./vkeys.js"; function isMac() { return navigator && !!(/mac/i).exec(navigator.platform); @@ -131,18 +132,64 @@ export function ModifierSync(charModifier) { }; } -// Get a key ID from a keyboard event -// May be a string or an integer depending on the available properties -export function getKey(evt){ - if ('keyCode' in evt && 'key' in evt) { - return evt.key + ':' + evt.keyCode; - } - else if ('keyCode' in evt) { - return evt.keyCode; +// Get 'KeyboardEvent.code', handling legacy browsers +export function getKeycode(evt){ + // Are we getting proper key identifiers? + // (unfortunately Firefox and Chrome are crappy here and gives + // us an empty string on some platforms, rather than leaving it + // undefined) + if (evt.code) { + // Mozilla isn't fully in sync with the spec yet + switch (evt.code) { + case 'OSLeft': return 'MetaLeft'; + case 'OSRight': return 'MetaRight'; + } + + return evt.code; } - else { - return evt.key; + + // The de-facto standard is to use Windows Virtual-Key codes + // in the 'keyCode' field for non-printable characters. However + // Webkit sets it to the same as charCode in 'keypress' events. + if ((evt.type !== 'keypress') && (evt.keyCode in vkeys)) { + var code = vkeys[evt.keyCode]; + + // macOS has messed up this code for some reason + if (isMac() && (code === 'ContextMenu')) { + code = 'MetaRight'; + } + + // The keyCode doesn't distinguish between left and right + // for the standard modifiers + if (evt.location === 2) { + switch (code) { + case 'ShiftLeft': return 'ShiftRight'; + case 'ControlLeft': return 'ControlRight'; + case 'AltLeft': return 'AltRight'; + } + } + + // Nor a bunch of the numpad keys + if (evt.location === 3) { + switch (code) { + case 'Delete': return 'NumpadDecimal'; + case 'Insert': return 'Numpad0'; + case 'End': return 'Numpad1'; + case 'ArrowDown': return 'Numpad2'; + case 'PageDown': return 'Numpad3'; + case 'ArrowLeft': return 'Numpad4'; + case 'ArrowRight': return 'Numpad6'; + case 'Home': return 'Numpad7'; + case 'ArrowUp': return 'Numpad8'; + case 'PageUp': return 'Numpad9'; + case 'Enter': return 'NumpadEnter'; + } + } + + return code; } + + return 'Unidentified'; } // Get the most reliable keysym value we can get from a key event @@ -290,7 +337,7 @@ export function QEMUKeyEventDecoder (modifierState, next) { function process(evt, type) { var result = {type: type}; - result.code = evt.code; + result.code = getKeycode(evt); result.keysym = 0; if (isNumPadMultiKey(evt)) { @@ -298,7 +345,7 @@ export function QEMUKeyEventDecoder (modifierState, next) { } var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier(); - var isShift = evt.keyCode === 0x10 || evt.key === 'Shift'; + var isShift = result.code === 'ShiftLeft' || result.code === 'ShiftRight'; var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!nonCharacterKey(evt)); @@ -387,7 +434,7 @@ export function TrackQEMUKeyState (next) { // Takes a DOM keyboard event and: // - determines which keysym it represents -// - determines a keyId identifying the key that was pressed (corresponding to the key/keyCode properties on the DOM event) +// - determines a code identifying the key that was pressed (corresponding to the code/keyCode properties on the DOM event) // - synthesizes events to synchronize modifier key state between which modifiers are actually down, and which we thought were down // - marks each event with an 'escape' property if a modifier was down which should be "escaped" // - generates a "stall" event in cases where it might be necessary to wait and see if a keypress event follows a keydown @@ -401,10 +448,16 @@ export function KeyEventDecoder (modifierState, next) { } function process(evt, type) { var result = {type: type}; - var keyId = getKey(evt); - if (keyId) { - result.keyId = keyId; + var code = getKeycode(evt); + if (code === 'Unidentified') { + // Unstable, but we don't have anything else to go on + // (don't use it for 'keypress' events thought since + // WebKit sets it to the same as charCode) + if (evt.keyCode && (evt.type !== 'keypress')) { + code = 'Platform' + evt.keyCode; + } } + result.code = code; var keysym = getKeysym(evt); @@ -416,7 +469,7 @@ export function KeyEventDecoder (modifierState, next) { result.keysym = keysym; } - var isShift = evt.keyCode === 0x10 || evt.key === 'Shift'; + var isShift = code === 'ShiftLeft' || code === 'ShiftRight'; // Should we prevent the browser from handling the event? // Doing so on a keydown (in most browsers) prevents keypress from being generated @@ -546,8 +599,8 @@ export function TrackKeyState (next) { switch (evt.type) { case 'keydown': // insert a new entry if last seen key was different. - if (!last || !evt.keyId || last.keyId !== evt.keyId) { - last = {keyId: evt.keyId, keysyms: {}}; + if (!last || evt.code === 'Unidentified' || last.code !== evt.code) { + last = {code: evt.code, keysyms: {}}; state.push(last); } if (evt.keysym) { @@ -560,7 +613,7 @@ export function TrackKeyState (next) { break; case 'keypress': if (!last) { - last = {keyId: evt.keyId, keysyms: {}}; + last = {code: evt.code, keysyms: {}}; state.push(last); } if (!evt.keysym) { @@ -582,7 +635,7 @@ export function TrackKeyState (next) { var idx = null; // do we have a matching key tracked as being down? for (var i = 0; i !== state.length; ++i) { - if (state[i].keyId === evt.keyId) { + if (state[i].code === evt.code) { idx = i; break; } @@ -609,7 +662,7 @@ export function TrackKeyState (next) { for (var i = 0; i < state.length; ++i) { for (var key in state[i].keysyms) { var keysym = state[i].keysyms[key]; - next({keyId: 0, keysym: keysym, type: 'keyup'}); + next({code: 'Unidentified', keysym: keysym, type: 'keyup'}); } } /* jshint shadow: false */ @@ -629,14 +682,14 @@ export function EscapeModifiers (next) { } // undo modifiers for (var i = 0; i < evt.escape.length; ++i) { - next({type: 'keyup', keyId: 0, keysym: evt.escape[i]}); + next({type: 'keyup', code: 'Unidentified', keysym: evt.escape[i]}); } // send the character event next(evt); // redo modifiers /* jshint shadow: true */ for (var i = 0; i < evt.escape.length; ++i) { - next({type: 'keydown', keyId: 0, keysym: evt.escape[i]}); + next({type: 'keydown', code: 'Unidentified', keysym: evt.escape[i]}); } /* jshint shadow: false */ }; diff --git a/core/input/vkeys.js b/core/input/vkeys.js new file mode 100644 index 0000000..dc784ff --- /dev/null +++ b/core/input/vkeys.js @@ -0,0 +1,116 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2017 Pierre Ossman for Cendio AB + * Licensed under MPL 2.0 or any later version (see LICENSE.txt) + */ + +/* + * Mapping between Microsoft® Windows® Virtual-Key codes and + * HTML key codes. + */ + +export default { + 0x08: 'Backspace', + 0x09: 'Tab', + 0x0a: 'NumpadClear', + 0x0d: 'Enter', + 0x10: 'ShiftLeft', + 0x11: 'ControlLeft', + 0x12: 'AltLeft', + 0x13: 'Pause', + 0x14: 'CapsLock', + 0x15: 'Lang1', + 0x19: 'Lang2', + 0x1b: 'Escape', + 0x1c: 'Convert', + 0x1d: 'NonConvert', + 0x20: 'Space', + 0x21: 'PageUp', + 0x22: 'PageDown', + 0x23: 'End', + 0x24: 'Home', + 0x25: 'ArrowLeft', + 0x26: 'ArrowUp', + 0x27: 'ArrowRight', + 0x28: 'ArrowDown', + 0x29: 'Select', + 0x2c: 'PrintScreen', + 0x2d: 'Insert', + 0x2e: 'Delete', + 0x2f: 'Help', + 0x30: 'Digit0', + 0x31: 'Digit1', + 0x32: 'Digit2', + 0x33: 'Digit3', + 0x34: 'Digit4', + 0x35: 'Digit5', + 0x36: 'Digit6', + 0x37: 'Digit7', + 0x38: 'Digit8', + 0x39: 'Digit9', + 0x5b: 'MetaLeft', + 0x5c: 'MetaRight', + 0x5d: 'ContextMenu', + 0x5f: 'Sleep', + 0x60: 'Numpad0', + 0x61: 'Numpad1', + 0x62: 'Numpad2', + 0x63: 'Numpad3', + 0x64: 'Numpad4', + 0x65: 'Numpad5', + 0x66: 'Numpad6', + 0x67: 'Numpad7', + 0x68: 'Numpad8', + 0x69: 'Numpad9', + 0x6a: 'NumpadMultiply', + 0x6b: 'NumpadAdd', + 0x6c: 'NumpadDecimal', + 0x6d: 'NumpadSubtract', + 0x6e: 'NumpadDecimal', // Duplicate, because buggy on Windows + 0x6f: 'NumpadDivide', + 0x70: 'F1', + 0x71: 'F2', + 0x72: 'F3', + 0x73: 'F4', + 0x74: 'F5', + 0x75: 'F6', + 0x76: 'F7', + 0x77: 'F8', + 0x78: 'F9', + 0x79: 'F10', + 0x7a: 'F11', + 0x7b: 'F12', + 0x7c: 'F13', + 0x7d: 'F14', + 0x7e: 'F15', + 0x7f: 'F16', + 0x80: 'F17', + 0x81: 'F18', + 0x82: 'F19', + 0x83: 'F20', + 0x84: 'F21', + 0x85: 'F22', + 0x86: 'F23', + 0x87: 'F24', + 0x90: 'NumLock', + 0x91: 'ScrollLock', + 0xa6: 'BrowserBack', + 0xa7: 'BrowserForward', + 0xa8: 'BrowserRefresh', + 0xa9: 'BrowserStop', + 0xaa: 'BrowserSearch', + 0xab: 'BrowserFavorites', + 0xac: 'BrowserHome', + 0xad: 'AudioVolumeMute', + 0xae: 'AudioVolumeDown', + 0xaf: 'AudioVolumeUp', + 0xb0: 'MediaTrackNext', + 0xb1: 'MediaTrackPrevious', + 0xb2: 'MediaStop', + 0xb3: 'MediaPlayPause', + 0xb4: 'LaunchMail', + 0xb5: 'MediaSelect', + 0xb6: 'LaunchApp1', + 0xb7: 'LaunchApp2', + 0xe1: 'AltRight', // Only when it is AltGraph +}; diff --git a/core/input/xtscancodes.js b/core/input/xtscancodes.js index 43ea51b..e61b425 100644 --- a/core/input/xtscancodes.js +++ b/core/input/xtscancodes.js @@ -112,8 +112,6 @@ export default { "Delete": 0xE053, "MetaLeft": 0xE05B, "MetaRight": 0xE05C, - "OSLeft": 0xE05B, // OSLeft and OSRight are kept for compatability since - "OSRight": 0xE05C, // Firefox haven't updated to MetaLeft and MetaRight yet "ContextMenu": 0xE05D, "BrowserSearch": 0xE065, "BrowserFavorites": 0xE066, diff --git a/tests/input.html b/tests/input.html index febee5f..cd31f7e 100644 --- a/tests/input.html +++ b/tests/input.html @@ -29,6 +29,7 @@ + diff --git a/tests/test.helper.js b/tests/test.helper.js index a10b8b2..3d0afb5 100644 --- a/tests/test.helper.js +++ b/tests/test.helper.js @@ -33,6 +33,71 @@ describe('Helpers', function() { }); }); + describe('getKeycode', function() { + it('should pass through proper code', function() { + expect(KeyboardUtil.getKeycode({code: 'Semicolon'})).to.be.equal('Semicolon'); + }); + it('should map legacy values', function() { + expect(KeyboardUtil.getKeycode({code: ''})).to.be.equal('Unidentified'); + expect(KeyboardUtil.getKeycode({code: 'OSLeft'})).to.be.equal('MetaLeft'); + }); + it('should map keyCode to code when possible', function() { + expect(KeyboardUtil.getKeycode({keyCode: 0x14})).to.be.equal('CapsLock'); + expect(KeyboardUtil.getKeycode({keyCode: 0x5b})).to.be.equal('MetaLeft'); + expect(KeyboardUtil.getKeycode({keyCode: 0x35})).to.be.equal('Digit5'); + expect(KeyboardUtil.getKeycode({keyCode: 0x65})).to.be.equal('Numpad5'); + }); + it('should map keyCode left/right side', function() { + expect(KeyboardUtil.getKeycode({keyCode: 0x10, location: 1})).to.be.equal('ShiftLeft'); + expect(KeyboardUtil.getKeycode({keyCode: 0x10, location: 2})).to.be.equal('ShiftRight'); + expect(KeyboardUtil.getKeycode({keyCode: 0x11, location: 1})).to.be.equal('ControlLeft'); + expect(KeyboardUtil.getKeycode({keyCode: 0x11, location: 2})).to.be.equal('ControlRight'); + }); + it('should map keyCode on numpad', function() { + expect(KeyboardUtil.getKeycode({keyCode: 0x0d, location: 0})).to.be.equal('Enter'); + expect(KeyboardUtil.getKeycode({keyCode: 0x0d, location: 3})).to.be.equal('NumpadEnter'); + expect(KeyboardUtil.getKeycode({keyCode: 0x23, location: 0})).to.be.equal('End'); + expect(KeyboardUtil.getKeycode({keyCode: 0x23, location: 3})).to.be.equal('Numpad1'); + }); + it('should return Unidentified when it cannot map the keyCode', function() { + expect(KeyboardUtil.getKeycode({keycode: 0x42})).to.be.equal('Unidentified'); + }); + + describe('Fix Meta on macOS', function() { + var origNavigator; + beforeEach(function () { + // window.navigator is a protected read-only property in many + // environments, so we need to redefine it whilst running these + // tests. + origNavigator = Object.getOwnPropertyDescriptor(window, "navigator"); + if (origNavigator === undefined) { + // Object.getOwnPropertyDescriptor() doesn't work + // properly in any version of IE + this.skip(); + } + + Object.defineProperty(window, "navigator", {value: {}}); + if (window.navigator.platform !== undefined) { + // Object.defineProperty() doesn't work properly in old + // versions of Chrome + this.skip(); + } + + window.navigator.platform = "Mac x86_64"; + }); + afterEach(function () { + Object.defineProperty(window, "navigator", origNavigator); + }); + + it('should respect ContextMenu on modern browser', function() { + expect(KeyboardUtil.getKeycode({code: 'ContextMenu', keyCode: 0x5d})).to.be.equal('ContextMenu'); + }); + it('should translate legacy ContextMenu to MetaRight', function() { + expect(KeyboardUtil.getKeycode({keyCode: 0x5d})).to.be.equal('MetaRight'); + }); + }); + }); + describe('getKeysym', function() { it('should prefer char', function() { expect(KeyboardUtil.getKeysym({char : 'a', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.be.equal(0x61); diff --git a/tests/test.keyboard.js b/tests/test.keyboard.js index dc8f0db..aa96142 100644 --- a/tests/test.keyboard.js +++ b/tests/test.keyboard.js @@ -12,26 +12,26 @@ describe('Key Event Pipeline Stages', function() { KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { expect(evt).to.be.an.object; done(); - }).keydown({keyCode: 0x41}); + }).keydown({code: 'KeyA', keyCode: 0x41}); }); it('should pass the right keysym through', function(done) { KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { expect(evt.keysym).to.be.deep.equal(0x61); done(); - }).keypress({keyCode: 0x41}); + }).keypress({code: 'KeyA', keyCode: 0x41}); }); it('should pass the right keyid through', function(done) { KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt).to.have.property('keyId', 0x41); + expect(evt).to.have.property('code', 'KeyA'); done(); - }).keydown({keyCode: 0x41}); + }).keydown({code: 'KeyA', keyCode: 0x41}); }); it('should not sync modifiers on a keypress', function() { // Firefox provides unreliable modifier state on keypress events var count = 0; KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { ++count; - }).keypress({keyCode: 0x41, ctrlKey: true}); + }).keypress({code: 'KeyA', keyCode: 0x41, ctrlKey: true}); expect(count).to.be.equal(1); }); it('should sync modifiers if necessary', function(done) { @@ -43,29 +43,29 @@ describe('Key Event Pipeline Stages', function() { ++count; break; case 1: - expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown', keysym: 0x61}); + expect(evt).to.be.deep.equal({code: 'KeyA', type: 'keydown', keysym: 0x61}); done(); break; } - }).keydown({keyCode: 0x41, ctrlKey: true}); + }).keydown({code: 'KeyA', keyCode: 0x41, ctrlKey: true}); }); it('should forward keydown events with the right type', function(done) { KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown'}); + expect(evt).to.be.deep.equal({code: 'KeyA', type: 'keydown'}); done(); - }).keydown({keyCode: 0x41}); + }).keydown({code: 'KeyA', keyCode: 0x41}); }); it('should forward keyup events with the right type', function(done) { KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt).to.be.deep.equal({keyId: 0x41, keysym: 0x61, type: 'keyup'}); + expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keyup'}); done(); - }).keyup({keyCode: 0x41}); + }).keyup({code: 'KeyA', keyCode: 0x41}); }); it('should forward keypress events with the right type', function(done) { KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt).to.be.deep.equal({keyId: 0x41, keysym: 0x61, type: 'keypress'}); + expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keypress'}); done(); - }).keypress({keyCode: 0x41}); + }).keypress({code: 'KeyA', keyCode: 0x41}); }); it('should generate stalls if a char modifier is down while a key is pressed', function(done) { var count = 0; @@ -82,14 +82,14 @@ describe('Key Event Pipeline Stages', function() { case 2: // 'a' expect(evt).to.be.deep.equal({ type: 'keydown', - keyId: 0x41, + code: 'KeyA', keysym: 0x61 }); done(); break; } - }).keydown({keyCode: 0x41, altGraphKey: true}); + }).keydown({code: 'KeyA', keyCode: 0x41, altGraphKey: true}); }); describe('suppress the right events at the right time', function() { @@ -184,7 +184,7 @@ describe('Key Event Pipeline Stages', function() { }); obj.keydown({keyCode: 0xe1}); // press altgr - obj.keydown({keyCode: 'A'.charCodeAt()}); + obj.keydown({code: 'KeyA', keyCode: 0x41}); }); it('should indicate on events if a single-key char modifier is down', function(done) { @@ -196,8 +196,8 @@ describe('Key Event Pipeline Stages', function() { case 1: // 'a' expect(evt).to.be.deep.equal({ type: 'keypress', - keyId: 'A'.charCodeAt(), - keysym: 'a'.charCodeAt(), + code: 'KeyA', + keysym: 0x61, escape: [0xfe03] }); done(); @@ -206,7 +206,7 @@ describe('Key Event Pipeline Stages', function() { }); obj.keydown({keyCode: 0xe1}); // press altgr - obj.keypress({keyCode: 'A'.charCodeAt()}); + obj.keypress({code: 'KeyA', keyCode: 0x41}); }); it('should indicate on events if a multi-key char modifier is down', function(done) { var times_called = 0; @@ -219,8 +219,8 @@ describe('Key Event Pipeline Stages', function() { case 2: // 'a' expect(evt).to.be.deep.equal({ type: 'keypress', - keyId: 'A'.charCodeAt(), - keysym: 'a'.charCodeAt(), + code: 'KeyA', + keysym: 0x61, escape: [0xffe9, 0xffe3] }); done(); @@ -230,7 +230,7 @@ describe('Key Event Pipeline Stages', function() { obj.keydown({keyCode: 0x11}); // press ctrl obj.keydown({keyCode: 0x12}); // press alt - obj.keypress({keyCode: 'A'.charCodeAt()}); + obj.keypress({code: 'KeyA', keyCode: 0x41}); }); it('should not consider a char modifier to be down on the modifier key itself', function() { var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) { @@ -244,18 +244,18 @@ describe('Key Event Pipeline Stages', function() { describe('add/remove keysym', function() { it('should remove keysym from keydown if a char key and no modifier', function() { KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown'}); - }).keydown({keyCode: 0x41}); + expect(evt).to.be.deep.equal({code: 'KeyA', type: 'keydown'}); + }).keydown({code: 'KeyA', keyCode: 0x41}); }); it('should not remove keysym from keydown if a shortcut modifier is down', function() { var times_called = 0; KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { switch (times_called++) { case 1: - expect(evt).to.be.deep.equal({keyId: 0x41, keysym: 0x61, type: 'keydown'}); + expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keydown'}); break; } - }).keydown({keyCode: 0x41, ctrlKey: true}); + }).keydown({code: 'KeyA', keyCode: 0x41, ctrlKey: true}); expect(times_called).to.be.equal(2); }); it('should not remove keysym from keydown if a char modifier is down', function() { @@ -263,30 +263,30 @@ describe('Key Event Pipeline Stages', function() { KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) { switch (times_called++) { case 2: - expect(evt).to.be.deep.equal({keyId: 0x41, keysym: 0x61, type: 'keydown'}); + expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keydown'}); break; } - }).keydown({keyCode: 0x41, altGraphKey: true}); + }).keydown({code: 'KeyA', keyCode: 0x41, altGraphKey: true}); expect(times_called).to.be.equal(3); }); it('should not remove keysym from keydown if key is noncharacter', function() { KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt, 'bacobjpace').to.be.deep.equal({keyId: 0x09, keysym: 0xff09, type: 'keydown'}); + expect(evt, 'tab').to.be.deep.equal({code: 'Tab', keysym: 0xff09, type: 'keydown'}); }).keydown({keyCode: 0x09}); KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt, 'ctrl').to.be.deep.equal({keyId: 0x11, keysym: 0xffe3, type: 'keydown'}); + expect(evt, 'ctrl').to.be.deep.equal({code: 'ControlLeft', keysym: 0xffe3, type: 'keydown'}); }).keydown({keyCode: 0x11}); }); it('should never remove keysym from keypress', function() { KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt).to.be.deep.equal({keyId: 0x41, keysym: 0x61, type: 'keypress'}); - }).keypress({keyCode: 0x41}); + expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keypress'}); + }).keypress({code: 'KeyA', keyCode: 0x41}); }); it('should never remove keysym from keyup', function() { KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) { - expect(evt).to.be.deep.equal({keyId: 0x41, keysym: 0x61, type: 'keyup'}); - }).keyup({keyCode: 0x41}); + expect(evt).to.be.deep.equal({code: 'KeyA', keysym: 0x61, type: 'keyup'}); + }).keyup({code: 'KeyA', keyCode: 0x41}); }); }); // on keypress, keyup(?), always set keysym @@ -296,31 +296,31 @@ describe('Key Event Pipeline Stages', function() { describe('Verify that char modifiers are active', function() { it('should pass keydown events through if there is no stall', function(done) { var obj = KeyboardUtil.VerifyCharModifier(function(evt){ - expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x41}); + expect(evt).to.deep.equal({type: 'keydown', code: 'KeyA', keysym: 0x41}); done(); - })({type: 'keydown', keyId: 0x41, keysym: 0x41}); + })({type: 'keydown', code: 'KeyA', keysym: 0x41}); }); it('should pass keyup events through if there is no stall', function(done) { var obj = KeyboardUtil.VerifyCharModifier(function(evt){ - expect(evt).to.deep.equal({type: 'keyup', keyId: 0x41, keysym: 0x41}); + expect(evt).to.deep.equal({type: 'keyup', code: 'KeyA', keysym: 0x41}); done(); - })({type: 'keyup', keyId: 0x41, keysym: 0x41}); + })({type: 'keyup', code: 'KeyA', keysym: 0x41}); }); it('should pass keypress events through if there is no stall', function(done) { var obj = KeyboardUtil.VerifyCharModifier(function(evt){ - expect(evt).to.deep.equal({type: 'keypress', keyId: 0x41, keysym: 0x41}); + expect(evt).to.deep.equal({type: 'keypress', code: 'KeyA', keysym: 0x41}); done(); - })({type: 'keypress', keyId: 0x41, keysym: 0x41}); + })({type: 'keypress', code: 'KeyA', keysym: 0x41}); }); it('should not pass stall events through', function(done){ var obj = KeyboardUtil.VerifyCharModifier(function(evt){ // should only be called once, for the keydown - expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x41}); + expect(evt).to.deep.equal({type: 'keydown', code: 'KeyA', keysym: 0x41}); done(); }); obj({type: 'stall'}); - obj({type: 'keydown', keyId: 0x41, keysym: 0x41}); + obj({type: 'keydown', code: 'KeyA', keysym: 0x41}); }); it('should merge keydown and keypress events if they come after a stall', function(done) { var next_called = false; @@ -328,13 +328,13 @@ describe('Key Event Pipeline Stages', function() { // should only be called once, for the keydown expect(next_called).to.be.false; next_called = true; - expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x44}); + expect(evt).to.deep.equal({type: 'keydown', code: 'KeyA', keysym: 0x44}); done(); }); obj({type: 'stall'}); - obj({type: 'keydown', keyId: 0x41, keysym: 0x42}); - obj({type: 'keypress', keyId: 0x43, keysym: 0x44}); + obj({type: 'keydown', code: 'KeyA', keysym: 0x42}); + obj({type: 'keypress', code: 'KeyC', keysym: 0x44}); expect(next_called).to.be.false; }); it('should preserve modifier attribute when merging if keysyms differ', function(done) { @@ -343,13 +343,13 @@ describe('Key Event Pipeline Stages', function() { // should only be called once, for the keydown expect(next_called).to.be.false; next_called = true; - expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x44, escape: [0xffe3]}); + expect(evt).to.deep.equal({type: 'keydown', code: 'KeyA', keysym: 0x44, escape: [0xffe3]}); done(); }); obj({type: 'stall'}); - obj({type: 'keydown', keyId: 0x41, keysym: 0x42}); - obj({type: 'keypress', keyId: 0x43, keysym: 0x44, escape: [0xffe3]}); + obj({type: 'keydown', code: 'KeyA', keysym: 0x42}); + obj({type: 'keypress', code: 'KeyC', keysym: 0x44, escape: [0xffe3]}); expect(next_called).to.be.false; }); it('should not preserve modifier attribute when merging if keysyms are the same', function() { @@ -358,18 +358,18 @@ describe('Key Event Pipeline Stages', function() { }); obj({type: 'stall'}); - obj({type: 'keydown', keyId: 0x41, keysym: 0x42}); - obj({type: 'keypress', keyId: 0x43, keysym: 0x42, escape: [0xffe3]}); + obj({type: 'keydown', code: 'KeyA', keysym: 0x42}); + obj({type: 'keypress', code: 'KeyC', keysym: 0x42, escape: [0xffe3]}); }); it('should not merge keydown and keypress events if there is no stall', function(done) { var times_called = 0; var obj = KeyboardUtil.VerifyCharModifier(function(evt){ switch(times_called) { case 0: - expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x42}); + expect(evt).to.deep.equal({type: 'keydown', code: 'KeyA', keysym: 0x42}); break; case 1: - expect(evt).to.deep.equal({type: 'keypress', keyId: 0x43, keysym: 0x44}); + expect(evt).to.deep.equal({type: 'keypress', code: 'KeyC', keysym: 0x44}); done(); break; } @@ -377,21 +377,21 @@ describe('Key Event Pipeline Stages', function() { ++times_called; }); - obj({type: 'keydown', keyId: 0x41, keysym: 0x42}); - obj({type: 'keypress', keyId: 0x43, keysym: 0x44}); + obj({type: 'keydown', code: 'KeyA', keysym: 0x42}); + obj({type: 'keypress', code: 'KeyC', keysym: 0x44}); }); it('should not merge keydown and keypress events if separated by another event', function(done) { var times_called = 0; var obj = KeyboardUtil.VerifyCharModifier(function(evt){ switch(times_called) { case 0: - expect(evt,1).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x42}); + expect(evt,1).to.deep.equal({type: 'keydown', code: 'KeyA', keysym: 0x42}); break; case 1: - expect(evt,2).to.deep.equal({type: 'keyup', keyId: 0x43, keysym: 0x44}); + expect(evt,2).to.deep.equal({type: 'keyup', code: 'KeyC', keysym: 0x44}); break; case 2: - expect(evt,3).to.deep.equal({type: 'keypress', keyId: 0x45, keysym: 0x46}); + expect(evt,3).to.deep.equal({type: 'keypress', code: 'KeyE', keysym: 0x46}); done(); break; } @@ -400,9 +400,9 @@ describe('Key Event Pipeline Stages', function() { }); obj({type: 'stall'}); - obj({type: 'keydown', keyId: 0x41, keysym: 0x42}); - obj({type: 'keyup', keyId: 0x43, keysym: 0x44}); - obj({type: 'keypress', keyId: 0x45, keysym: 0x46}); + obj({type: 'keydown', code: 'KeyA', keysym: 0x42}); + obj({type: 'keyup', code: 'KeyC', keysym: 0x44}); + obj({type: 'keypress', code: 'KeyE', keysym: 0x46}); }); }); @@ -411,7 +411,7 @@ describe('Key Event Pipeline Stages', function() { var obj = KeyboardUtil.TrackKeyState(function(evt) { expect(true).to.be.false; }); - obj({type: 'keyup', keyId: 0x41}); + obj({type: 'keyup', code: 'KeyA'}); }); it('should insert into the queue on keydown if no keys are down', function() { var times_called = 0; @@ -432,11 +432,11 @@ describe('Key Event Pipeline Stages', function() { }); expect(elem).to.be.null; - elem = {type: 'keydown', keyId: 0x41, keysym: 0x42}; + elem = {type: 'keydown', code: 'KeyA', keysym: 0x42}; keysymsdown[0x42] = true; obj(elem); expect(elem).to.be.null; - elem = {type: 'keyup', keyId: 0x41}; + elem = {type: 'keyup', code: 'KeyA'}; obj(elem); expect(elem).to.be.null; expect(times_called).to.be.equal(2); @@ -460,16 +460,16 @@ describe('Key Event Pipeline Stages', function() { }); expect(elem).to.be.null; - elem = {type: 'keypress', keyId: 0x41, keysym: 0x42}; + elem = {type: 'keypress', code: 'KeyA', keysym: 0x42}; keysymsdown[0x42] = true; obj(elem); expect(elem).to.be.null; - elem = {type: 'keyup', keyId: 0x41}; + elem = {type: 'keyup', code: 'KeyA'}; obj(elem); expect(elem).to.be.null; expect(times_called).to.be.equal(2); }); - it('should add keysym to last key entry if keyId matches', function() { + it('should add keysym to last key entry if code matches', function() { // this implies that a single keyup will release both keysyms var times_called = 0; var elem = null; @@ -489,19 +489,19 @@ describe('Key Event Pipeline Stages', function() { }); expect(elem).to.be.null; - elem = {type: 'keypress', keyId: 0x41, keysym: 0x42}; + elem = {type: 'keypress', code: 'KeyA', keysym: 0x42}; keysymsdown[0x42] = true; obj(elem); expect(elem).to.be.null; - elem = {type: 'keypress', keyId: 0x41, keysym: 0x43}; + elem = {type: 'keypress', code: 'KeyA', keysym: 0x43}; keysymsdown[0x43] = true; obj(elem); expect(elem).to.be.null; - elem = {type: 'keyup', keyId: 0x41}; + elem = {type: 'keyup', code: 'KeyA'}; obj(elem); expect(times_called).to.be.equal(4); }); - it('should create new key entry if keyId matches and keysym does not', function() { + it('should create new key entry if code matches and keysym does not', function() { // this implies that a single keyup will release both keysyms var times_called = 0; var elem = null; @@ -521,23 +521,23 @@ describe('Key Event Pipeline Stages', function() { }); expect(elem).to.be.null; - elem = {type: 'keydown', keyId: 0, keysym: 0x42}; + elem = {type: 'keydown', code: 'Unidentified', keysym: 0x42}; keysymsdown[0x42] = true; obj(elem); expect(elem).to.be.null; - elem = {type: 'keydown', keyId: 0, keysym: 0x43}; + elem = {type: 'keydown', code: 'Unidentified', keysym: 0x43}; keysymsdown[0x43] = true; obj(elem); expect(times_called).to.be.equal(2); expect(elem).to.be.null; - elem = {type: 'keyup', keyId: 0}; + elem = {type: 'keyup', code: 'Unidentified'}; obj(elem); expect(times_called).to.be.equal(3); - elem = {type: 'keyup', keyId: 0}; + elem = {type: 'keyup', code: 'Unidentified'}; obj(elem); expect(times_called).to.be.equal(4); }); - it('should merge key entry if keyIds are zero and keysyms match', function() { + it('should merge key entry if codes are zero and keysyms match', function() { // this implies that a single keyup will release both keysyms var times_called = 0; var elem = null; @@ -557,20 +557,20 @@ describe('Key Event Pipeline Stages', function() { }); expect(elem).to.be.null; - elem = {type: 'keydown', keyId: 0, keysym: 0x42}; + elem = {type: 'keydown', code: 'Unidentified', keysym: 0x42}; keysymsdown[0x42] = true; obj(elem); expect(elem).to.be.null; - elem = {type: 'keydown', keyId: 0, keysym: 0x42}; + elem = {type: 'keydown', code: 'Unidentified', keysym: 0x42}; keysymsdown[0x42] = true; obj(elem); expect(times_called).to.be.equal(2); expect(elem).to.be.null; - elem = {type: 'keyup', keyId: 0}; + elem = {type: 'keyup', code: 'Unidentified'}; obj(elem); expect(times_called).to.be.equal(3); }); - it('should add keysym as separate entry if keyId does not match last event', function() { + it('should add keysym as separate entry if code does not match last event', function() { // this implies that separate keyups are required var times_called = 0; var elem = null; @@ -590,22 +590,22 @@ describe('Key Event Pipeline Stages', function() { }); expect(elem).to.be.null; - elem = {type: 'keypress', keyId: 0x41, keysym: 0x42}; + elem = {type: 'keypress', code: 'KeyA', keysym: 0x42}; keysymsdown[0x42] = true; obj(elem); expect(elem).to.be.null; - elem = {type: 'keypress', keyId: 0x42, keysym: 0x43}; + elem = {type: 'keypress', code: 'KeyB', keysym: 0x43}; keysymsdown[0x43] = true; obj(elem); expect(elem).to.be.null; - elem = {type: 'keyup', keyId: 0x41}; + elem = {type: 'keyup', code: 'KeyA'}; obj(elem); expect(times_called).to.be.equal(4); - elem = {type: 'keyup', keyId: 0x42}; + elem = {type: 'keyup', code: 'KeyB'}; obj(elem); expect(times_called).to.be.equal(4); }); - it('should add keysym as separate entry if keyId does not match last event and first is zero', function() { + it('should add keysym as separate entry if code does not match last event and first is zero', function() { // this implies that separate keyups are required var times_called = 0; var elem = null; @@ -625,23 +625,23 @@ describe('Key Event Pipeline Stages', function() { }); expect(elem).to.be.null; - elem = {type: 'keydown', keyId: 0, keysym: 0x42}; + elem = {type: 'keydown', code: 'Unidentified', keysym: 0x42}; keysymsdown[0x42] = true; obj(elem); expect(elem).to.be.null; - elem = {type: 'keydown', keyId: 0x42, keysym: 0x43}; + elem = {type: 'keydown', code: 'KeyB', keysym: 0x43}; keysymsdown[0x43] = true; obj(elem); expect(elem).to.be.null; expect(times_called).to.be.equal(2); - elem = {type: 'keyup', keyId: 0}; + elem = {type: 'keyup', code: 'Unidentified'}; obj(elem); expect(times_called).to.be.equal(3); - elem = {type: 'keyup', keyId: 0x42}; + elem = {type: 'keyup', code: 'KeyB'}; obj(elem); expect(times_called).to.be.equal(4); }); - it('should add keysym as separate entry if keyId does not match last event and second is zero', function() { + it('should add keysym as separate entry if code does not match last event and second is zero', function() { // this implies that a separate keyups are required var times_called = 0; var elem = null; @@ -661,18 +661,18 @@ describe('Key Event Pipeline Stages', function() { }); expect(elem).to.be.null; - elem = {type: 'keydown', keyId: 0x41, keysym: 0x42}; + elem = {type: 'keydown', code: 'KeyA', keysym: 0x42}; keysymsdown[0x42] = true; obj(elem); expect(elem).to.be.null; - elem = {type: 'keydown', keyId: 0, keysym: 0x43}; + elem = {type: 'keydown', code: 'Unidentified', keysym: 0x43}; keysymsdown[0x43] = true; obj(elem); expect(elem).to.be.null; - elem = {type: 'keyup', keyId: 0x41}; + elem = {type: 'keyup', code: 'KeyA'}; obj(elem); expect(times_called).to.be.equal(3); - elem = {type: 'keyup', keyId: 0}; + elem = {type: 'keyup', code: 'Unidentified'}; obj(elem); expect(times_called).to.be.equal(4); }); @@ -686,18 +686,18 @@ describe('Key Event Pipeline Stages', function() { expect(evt.type).to.be.equal('keydown'); break; case 3: - expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x42, keysym: 0x62}); + expect(evt).to.be.deep.equal({type: 'keyup', code: 'KeyB', keysym: 0x62}); break; } }); - obj({type: 'keydown', keyId: 0x41, keysym: 0x61}); - obj({type: 'keydown', keyId: 0x42, keysym: 0x62}); - obj({type: 'keydown', keyId: 0x43, keysym: 0x63}); - obj({type: 'keyup', keyId: 0x42}); + obj({type: 'keydown', code: 'KeyA', keysym: 0x61}); + obj({type: 'keydown', code: 'KeyB', keysym: 0x62}); + obj({type: 'keydown', code: 'KeyC', keysym: 0x63}); + obj({type: 'keyup', code: 'KeyB'}); expect(times_called).to.equal(4); }); - it('should pop the first zero keyevent on keyup with zero keyId', function() { + it('should pop the first zero keyevent on keyup with zero code', function() { var times_called = 0; var obj = KeyboardUtil.TrackKeyState(function(evt) { switch (times_called++) { @@ -707,18 +707,18 @@ describe('Key Event Pipeline Stages', function() { expect(evt.type).to.be.equal('keydown'); break; case 3: - expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: 0x61}); + expect(evt).to.be.deep.equal({type: 'keyup', code: 'Unidentified', keysym: 0x61}); break; } }); - obj({type: 'keydown', keyId: 0, keysym: 0x61}); - obj({type: 'keydown', keyId: 0, keysym: 0x62}); - obj({type: 'keydown', keyId: 0x41, keysym: 0x63}); - obj({type: 'keyup', keyId: 0x0}); + obj({type: 'keydown', code: 'Unidentified', keysym: 0x61}); + obj({type: 'keydown', code: 'Unidentified', keysym: 0x62}); + obj({type: 'keydown', code: 'KeyA', keysym: 0x63}); + obj({type: 'keyup', code: 'Unidentified'}); expect(times_called).to.equal(4); }); - it('should pop the last keyevents keysym if no match is found for keyId', function() { + it('should pop the last keyevents keysym if no match is found for code', function() { var times_called = 0; var obj = KeyboardUtil.TrackKeyState(function(evt) { switch (times_called++) { @@ -728,15 +728,15 @@ describe('Key Event Pipeline Stages', function() { expect(evt.type).to.be.equal('keydown'); break; case 3: - expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x44, keysym: 0x63}); + expect(evt).to.be.deep.equal({type: 'keyup', code: 'KeyD', keysym: 0x63}); break; } }); - obj({type: 'keydown', keyId: 0x41, keysym: 0x61}); - obj({type: 'keydown', keyId: 0x42, keysym: 0x62}); - obj({type: 'keydown', keyId: 0x43, keysym: 0x63}); - obj({type: 'keyup', keyId: 0x44}); + obj({type: 'keydown', code: 'KeyA', keysym: 0x61}); + obj({type: 'keydown', code: 'KeyB', keysym: 0x62}); + obj({type: 'keydown', code: 'KeyC', keysym: 0x63}); + obj({type: 'keyup', code: 'KeyD'}); expect(times_called).to.equal(4); }); describe('Firefox sends keypress even when keydown is suppressed', function() { @@ -747,9 +747,9 @@ describe('Key Event Pipeline Stages', function() { ++times_called; }); - obj({type: 'keydown', keyId: 0x41, keysym: 0x42}); + obj({type: 'keydown', code: 'KeyA', keysym: 0x42}); expect(times_called).to.be.equal(1); - obj({type: 'keypress', keyId: 0x41, keysym: 0x43}); + obj({type: 'keypress', code: 'KeyA', keysym: 0x43}); }); }); describe('releaseAll', function() { @@ -766,15 +766,15 @@ describe('Key Event Pipeline Stages', function() { var obj = KeyboardUtil.TrackKeyState(function(evt) { switch (times_called++) { case 2: - expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: 0x41}); + expect(evt).to.be.deep.equal({type: 'keyup', code: 'Unidentified', keysym: 0x41}); break; case 3: - expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: 0x42}); + expect(evt).to.be.deep.equal({type: 'keyup', code: 'Unidentified', keysym: 0x42}); break; } }); - obj({type: 'keydown', keyId: 0x41, keysym: 0x41}); - obj({type: 'keydown', keyId: 0x42, keysym: 0x42}); + obj({type: 'keydown', code: 'KeyA', keysym: 0x41}); + obj({type: 'keydown', code: 'KeyB', keysym: 0x42}); expect(times_called).to.be.equal(2); obj({type: 'releaseall'}); expect(times_called).to.be.equal(4); @@ -792,8 +792,8 @@ describe('Key Event Pipeline Stages', function() { KeyboardUtil.EscapeModifiers(function(evt) { expect(times_called).to.be.equal(0); ++times_called; - expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x42}); - })({type: 'keydown', keyId: 0x41, keysym: 0x42}); + expect(evt).to.be.deep.equal({type: 'keydown', code: 'KeyA', keysym: 0x42}); + })({type: 'keydown', code: 'KeyA', keysym: 0x42}); expect(times_called).to.be.equal(1); }); it('should generate fake undo/redo events when a char modifier is down', function() { @@ -801,22 +801,22 @@ describe('Key Event Pipeline Stages', function() { KeyboardUtil.EscapeModifiers(function(evt) { switch(times_called++) { case 0: - expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: 0xffe9}); + expect(evt).to.be.deep.equal({type: 'keyup', code: 'Unidentified', keysym: 0xffe9}); break; case 1: - expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: 0xffe3}); + expect(evt).to.be.deep.equal({type: 'keyup', code: 'Unidentified', keysym: 0xffe3}); break; case 2: - expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0x41, keysym: 0x42, escape: [0xffe9, 0xffe3]}); + expect(evt).to.be.deep.equal({type: 'keydown', code: 'KeyA', keysym: 0x42, escape: [0xffe9, 0xffe3]}); break; case 3: - expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0, keysym: 0xffe9}); + expect(evt).to.be.deep.equal({type: 'keydown', code: 'Unidentified', keysym: 0xffe9}); break; case 4: - expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0, keysym: 0xffe3}); + expect(evt).to.be.deep.equal({type: 'keydown', code: 'Unidentified', keysym: 0xffe3}); break; } - })({type: 'keydown', keyId: 0x41, keysym: 0x42, escape: [0xffe9, 0xffe3]}); + })({type: 'keydown', code: 'KeyA', keysym: 0x42, escape: [0xffe9, 0xffe3]}); expect(times_called).to.be.equal(5); }); }); @@ -826,8 +826,8 @@ describe('Key Event Pipeline Stages', function() { KeyboardUtil.EscapeModifiers(function(evt) { expect(times_called).to.be.equal(0); ++times_called; - expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x41, keysym: 0x42, escape: [0xfe03]}); - })({type: 'keyup', keyId: 0x41, keysym: 0x42, escape: [0xfe03]}); + expect(evt).to.be.deep.equal({type: 'keyup', code: 'KeyA', keysym: 0x42, escape: [0xfe03]}); + })({type: 'keyup', code: 'KeyA', keysym: 0x42, escape: [0xfe03]}); expect(times_called).to.be.equal(1); }); it('should pass through when a char modifier is not down', function() { @@ -835,8 +835,8 @@ describe('Key Event Pipeline Stages', function() { KeyboardUtil.EscapeModifiers(function(evt) { expect(times_called).to.be.equal(0); ++times_called; - expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x41, keysym: 0x42}); - })({type: 'keyup', keyId: 0x41, keysym: 0x42}); + expect(evt).to.be.deep.equal({type: 'keyup', code: 'KeyA', keysym: 0x42}); + })({type: 'keyup', code: 'KeyA', keysym: 0x42}); expect(times_called).to.be.equal(1); }); }); diff --git a/tests/vnc_perf.html b/tests/vnc_perf.html index ce97ca4..36331eb 100644 --- a/tests/vnc_perf.html +++ b/tests/vnc_perf.html @@ -50,7 +50,8 @@ WebUtil.load_scripts({ 'core': ["base64.js", "websock.js", "des.js", "input/keysym.js", "input/keysymdef.js", "input/xtscancodes.js", "input/util.js", - "input/devices.js", "display.js", "rfb.js", "inflator.js"], + "input/devices.js", "display.js", "rfb.js", "inflator.js", + "input/vkeys.js"], 'tests': ["playback.js"], 'recordings': [fname]}); } else { -- 2.39.2