Skip to content

Commit

Permalink
QEMU RFB extension - keyboard.js changes
Browse files Browse the repository at this point in the history
Added a 'QEMUKeyEventDecoder' method to deal with the
key events generated when the QEMU extension is active. Another
method, 'TrackQEMUKeyState', was also created with this same
goal.

Although both methods have similaries with the existing methods
'KeyEventDecoder' and 'TrackKeyState', specially when dealing
with 'supress' and 'releaseall', the logic behind the QEMU extension
does not required keysym generation for most cases (some NumPad keys
are an exception) and, as such, there is no need to treat 'keyPressed'
events and to handle char modifiers.

'TrackQEMUKeyState' also handles a Windows scenario where the
'AltGR' key generates CtrlLeft and AltRight keystrokes. The solution
was to avoid this specific combination to be sent to the VNC server,
discarding the extra 'CtrlLeft' key. Considering that the user can
send CtrlLeft+AltLeft, CtrlRight+AltRight and even CtrlRight+AltLeft,
this workaround to allow Windows users to use AltGR in their noVNC
sessions is worthwhile.

Signed-off-by: Daniel Henrique Barboza <danielhb@linux.vnet.ibm.com>
  • Loading branch information
danielhb committed Aug 26, 2016
1 parent 8f06673 commit 49637e4
Showing 1 changed file with 131 additions and 0 deletions.
131 changes: 131 additions & 0 deletions include/keyboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,137 @@ var kbdUtil = (function() {
};
})();

function QEMUKeyEventDecoder(modifierState, next) {
"use strict";

function sendAll(evts) {
for (var i = 0; i < evts.length; ++i) {
next(evts[i]);
}
}

var numPadCodes = ["Numpad0", "Numpad1", "Numpad2",
"Numpad3", "Numpad4", "Numpad5", "Numpad6",
"Numpad7", "Numpad8", "Numpad9", "NumpadDecimal"];

var numLockOnKeySyms = {
"Numpad0": 0xffb0, "Numpad1": 0xffb1, "Numpad2": 0xffb2,
"Numpad3": 0xffb3, "Numpad4": 0xffb4, "Numpad5": 0xffb5,
"Numpad6": 0xffb6, "Numpad7": 0xffb7, "Numpad8": 0xffb8,
"Numpad9": 0xffb9, "NumpadDecimal": 0xffac
};

var numLockOnKeyCodes = [96, 97, 98, 99, 100, 101, 102,
103, 104, 105, 108, 110];

function isNumPadMultiKey(evt) {
return (numPadCodes.indexOf(evt.code) !== -1);
}

function getNumPadKeySym(evt) {
if (numLockOnKeyCodes.indexOf(evt.keyCode) !== -1) {
return numLockOnKeySyms[evt.code];
}
return 0;
}

function process(evt, type) {
var result = {type: type};
result.code = evt.code;
result.keysym = 0;

if (isNumPadMultiKey(evt)) {
result.keysym = getNumPadKeySym(evt);
}

var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';

var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!kbdUtil.nonCharacterKey(evt));

next(result);
return suppress;
}
return {
keydown: function(evt) {
sendAll(modifierState.keydown(evt));
return process(evt, 'keydown');
},
keypress: function(evt) {
return true;
},
keyup: function(evt) {
sendAll(modifierState.keyup(evt));
return process(evt, 'keyup');
},
syncModifiers: function(evt) {
sendAll(modifierState.syncAny(evt));
},
releaseAll: function() { next({type: 'releaseall'}); }
};
}

function TrackQEMUKeyState(next) {
"use strict";
var state = [];

return function (evt) {
var last = state.length !== 0 ? state[state.length-1] : null;

switch (evt.type) {
case 'keydown':

if (!last || last.code !== evt.code) {
last = {code: evt.code};

if (state.length > 0 && state[state.length-1].code == 'ControlLeft') {
if (evt.code !== 'AltRight') {
next({code: 'ControlLeft', type: 'keydown', keysym: 0});
} else {
state.pop();
}
}
state.push(last);
}
if (evt.code !== 'ControlLeft') {
next(evt);
}
break;

case 'keyup':
if (state.length === 0) {
return;
}
var idx = null;
// do we have a matching key tracked as being down?
for (var i = 0; i !== state.length; ++i) {
if (state[i].code === evt.code) {
idx = i;
break;
}
}
// if we couldn't find a match (it happens), assume it was the last key pressed
if (idx === null) {
if (evt.code === 'ControlLeft') {
return;
}
idx = state.length - 1;
}

state.splice(idx, 1);
next(evt);
break;
case 'releaseall':
/* jshint shadow: true */
for (var i = 0; i < state.length; ++i) {
next({code: state[i].code, keysym: 0, type: 'keyup'});
}
/* jshint shadow: false */
state = [];
}
};
}

// 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)
Expand Down

0 comments on commit 49637e4

Please sign in to comment.