]> git.proxmox.com Git - mirror_novnc.git/commitdiff
Use standard DOM identifiers for physical keys
authorPierre Ossman <ossman@cendio.se>
Tue, 24 Jan 2017 14:16:10 +0000 (15:16 +0100)
committerPierre Ossman <ossman@cendio.se>
Thu, 4 May 2017 10:13:45 +0000 (12:13 +0200)
core/input/util.js
core/input/vkeys.js [new file with mode: 0644]
core/input/xtscancodes.js
tests/input.html
tests/test.helper.js
tests/test.keyboard.js
tests/vnc_perf.html

index 9124c196a68eb60d7f13ed34dd0d7d7864361e36..508193ee1e75ed4af4ec9e7bb640e2ccc14ee2b0 100644 (file)
@@ -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 (file)
index 0000000..dc784ff
--- /dev/null
@@ -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
+};
index 43ea51b717f67f72e87cf8ae65ef71259aec3c5a..e61b425343ec417c1b23bded8f598546deedcd57 100644 (file)
@@ -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,
index febee5fd9397ce352054e657e0885a74a999d205..cd31f7edfc5bc6493192a924092280e70526586f 100644 (file)
@@ -29,6 +29,7 @@
     <script src="../core/input/keysym.js"></script>
     <script src="../core/input/keysymdef.js"></script> 
     <script src="../core/input/xtscancodes.js"></script>
+    <script src="../core/input/vkeys.js"></script>
     <script src="../core/input/util.js"></script>
     <script src="../core/input/devices.js"></script>
     <script src="../core/display.js"></script>
index a10b8b2f898b926a4edd61707afd6ba48b969729..3d0afb55f969dcb2552d413102ba5e3851e1d0d3 100644 (file)
@@ -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);
index dc8f0dbcffd7c0aa4f2491fa8d363d5d3f18f7dc..aa96142094032477d95798eea078a09d2834cd6a 100644 (file)
@@ -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);
             });
         });
index ce97ca4c34fce5150176e04d97c38f899fadc3fa..36331eb12a3ffb3c3d0eb75c99306b937035c759 100644 (file)
@@ -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 {