diff --git a/include/input.js b/include/input.js index fa6ba44a..eb4d18a3 100644 --- a/include/input.js +++ b/include/input.js @@ -51,10 +51,18 @@ var Keyboard, Mouse; if (this._onKeyPress) { Util.Debug("onKeyPress " + (e.type == 'keydown' ? "down" : "up") + ", keysym: " + e.keysym.keysym + "(" + e.keysym.keyname + ")"); - this._onKeyPress(e.keysym.keysym, e.type == 'keydown'); + this._onKeyPress(e); } }, + setQEMUVNCKeyboardHandler: function () { + this._handler = new QEMUKeyEventDecoder(kbdUtil.ModifierSync(), + TrackQEMUKeyState( + this._handleRfbEvent.bind(this) + ) + ); + }, + _handleKeyDown: function (e) { if (!this._focused) { return true; } diff --git a/include/rfb.js b/include/rfb.js index bc5555ab..9935962c 100644 --- a/include/rfb.js +++ b/include/rfb.js @@ -58,7 +58,8 @@ var RFB; ['ExtendedDesktopSize', -308 ], ['xvp', -309 ], ['Fence', -312 ], - ['ContinuousUpdates', -313 ] + ['ContinuousUpdates', -313 ], + ['QEMUExtendedKeyEvent', -258 ] ]; this._encHandlers = {}; @@ -129,6 +130,9 @@ var RFB; this._viewportDragPos = {}; this._viewportHasMoved = false; + // QEMU Extended Key Event support - default to false + this._qemuExtKeyEventSupported = false; + // set the default value on user-facing properties Util.set_defaults(this, defaults, { 'target': 'null', // VNC display rendering Canvas object @@ -560,9 +564,22 @@ var RFB; } }, - _handleKeyPress: function (keysym, down) { + _handleKeyPress: function (keyevent) { if (this._view_only) { return; } // View only, skip keyboard, events - RFB.messages.keyEvent(this._sock, keysym, down); + + var down = (keyevent.type == 'keydown'); + if (this._qemuExtKeyEventSupported) { + var scancode = XtScancode[keyevent.code]; + if (scancode) { + var keysym = keyevent.keysym; + RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode); + } else { + Util.Error('Unable to find a xt scancode for code = ' + keyevent.code); + } + } else { + keysym = keyevent.keysym.keysym; + RFB.messages.keyEvent(this._sock, keysym, down); + } }, _handleMouseButton: function (x, y, down, bmask) { @@ -1348,6 +1365,42 @@ var RFB; sock.flush(); }, + QEMUExtendedKeyEvent: function (sock, keysym, down, keycode) { + function getRFBkeycode(xt_scancode) { + var upperByte = (keycode >> 8); + var lowerByte = (keycode & 0x00ff); + if (upperByte === 0xe0 && lowerByte < 0x7f) { + lowerByte = lowerByte | 0x80; + return lowerByte; + } + return xt_scancode + } + + var buff = sock._sQ; + var offset = sock._sQlen; + + buff[offset] = 255; // msg-type + buff[offset + 1] = 0; // sub msg-type + + buff[offset + 2] = (down >> 8); + buff[offset + 3] = down; + + buff[offset + 4] = (keysym >> 24); + buff[offset + 5] = (keysym >> 16); + buff[offset + 6] = (keysym >> 8); + buff[offset + 7] = keysym; + + var RFBkeycode = getRFBkeycode(keycode) + + buff[offset + 8] = (RFBkeycode >> 24); + buff[offset + 9] = (RFBkeycode >> 16); + buff[offset + 10] = (RFBkeycode >> 8); + buff[offset + 11] = RFBkeycode; + + sock._sQlen += 12; + sock.flush(); + }, + pointerEvent: function (sock, x, y, mask) { var buff = sock._sQ; var offset = sock._sQlen; @@ -2259,6 +2312,16 @@ var RFB; compress_lo: function () { Util.Error("Server sent compress level pseudo-encoding"); - } + }, + + QEMUExtendedKeyEvent: function () { + this._FBU.rects--; + + var keyboardEvent = document.createEvent("keyboardEvent"); + if (keyboardEvent.code !== undefined) { + this._qemuExtKeyEventSupported = true; + this._keyboard.setQEMUVNCKeyboardHandler(); + } + }, }; })(); diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 65ce5f88..06eeebe3 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -1927,7 +1927,11 @@ describe('Remote Frame Buffer Protocol Client', function() { }); it('should send a key message on a key press', function () { - client._keyboard._onKeyPress(1234, 1); + var keyevent = {}; + keyevent.type = 'keydown'; + keyevent.keysym = {}; + keyevent.keysym.keysym = 1234; + client._keyboard._onKeyPress(keyevent); var key_msg = {_sQ: new Uint8Array(8), _sQlen: 0, flush: function () {}}; RFB.messages.keyEvent(key_msg, 1234, 1); expect(client._sock).to.have.sent(key_msg._sQ);