Simplify pressed key handling
Prefer avoid having the server simulate multiple key presses by refusing to use multiple keysyms for the same physical key.
This commit is contained in:
parent
9fce233d51
commit
ae82053366
|
@ -19,7 +19,7 @@ import * as KeyboardUtil from "./util.js";
|
||||||
//
|
//
|
||||||
|
|
||||||
const Keyboard = function (defaults) {
|
const Keyboard = function (defaults) {
|
||||||
this._keyDownList = []; // List of depressed keys
|
this._keyDownList = {}; // List of depressed keys
|
||||||
// (even if they are happy)
|
// (even if they are happy)
|
||||||
this._pendingKey = null; // Key waiting for keypress
|
this._pendingKey = null; // Key waiting for keypress
|
||||||
|
|
||||||
|
@ -75,6 +75,27 @@ Keyboard.prototype = {
|
||||||
var code = this._getKeyCode(e);
|
var code = this._getKeyCode(e);
|
||||||
var keysym = KeyboardUtil.getKeysym(e);
|
var keysym = KeyboardUtil.getKeysym(e);
|
||||||
|
|
||||||
|
// We cannot handle keys we cannot track, but we also need
|
||||||
|
// to deal with virtual keyboards which omit key info
|
||||||
|
if (code === 'Unidentified') {
|
||||||
|
if (keysym) {
|
||||||
|
// If it's a virtual keyboard then it should be
|
||||||
|
// sufficient to just send press and release right
|
||||||
|
// after each other
|
||||||
|
this._sendKeyEvent(keysym, 'Unidentified', true);
|
||||||
|
this._sendKeyEvent(keysym, 'Unidentified', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopEvent(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is this key already pressed? If so, then we must use the
|
||||||
|
// same keysym or we'll confuse the server
|
||||||
|
if (code in this._keyDownList) {
|
||||||
|
keysym = this._keyDownList[code];
|
||||||
|
}
|
||||||
|
|
||||||
// If this is a legacy browser then we'll need to wait for
|
// If this is a legacy browser then we'll need to wait for
|
||||||
// a keypress event as well
|
// a keypress event as well
|
||||||
if (!keysym) {
|
if (!keysym) {
|
||||||
|
@ -106,22 +127,7 @@ Keyboard.prototype = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var last;
|
this._keyDownList[code] = keysym;
|
||||||
if (this._keyDownList.length === 0) {
|
|
||||||
last = null;
|
|
||||||
} else {
|
|
||||||
last = this._keyDownList[this._keyDownList.length-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert a new entry if last seen key was different.
|
|
||||||
if (!last || code === 'Unidentified' || last.code !== code) {
|
|
||||||
last = {code: code, keysyms: {}};
|
|
||||||
this._keyDownList.push(last);
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure last event contains this keysym (a single "logical" keyevent
|
|
||||||
// can cause multiple key events to be sent to the VNC server)
|
|
||||||
last.keysyms[keysym] = keysym;
|
|
||||||
|
|
||||||
// undo modifiers
|
// undo modifiers
|
||||||
if (escape) {
|
if (escape) {
|
||||||
|
@ -184,23 +190,12 @@ Keyboard.prototype = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var last;
|
|
||||||
if (this._keyDownList.length === 0) {
|
|
||||||
last = null;
|
|
||||||
} else {
|
|
||||||
last = this._keyDownList[this._keyDownList.length-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!last) {
|
|
||||||
last = {code: code, keysyms: {}};
|
|
||||||
this._keyDownList.push(last);
|
|
||||||
}
|
|
||||||
if (!keysym) {
|
if (!keysym) {
|
||||||
console.log('keypress with no keysym:', e);
|
console.log('keypress with no keysym:', e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
last.keysyms[keysym] = keysym;
|
this._keyDownList[code] = keysym;
|
||||||
|
|
||||||
// undo modifiers
|
// undo modifiers
|
||||||
if (escape) {
|
if (escape) {
|
||||||
|
@ -229,37 +224,22 @@ Keyboard.prototype = {
|
||||||
|
|
||||||
var code = this._getKeyCode(e);
|
var code = this._getKeyCode(e);
|
||||||
|
|
||||||
if (this._keyDownList.length === 0) {
|
// Do we really think this key is down?
|
||||||
|
if (!(code in this._keyDownList)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var idx = null;
|
|
||||||
// do we have a matching key tracked as being down?
|
|
||||||
for (var i = 0; i !== this._keyDownList.length; ++i) {
|
|
||||||
if (this._keyDownList[i].code === code) {
|
|
||||||
idx = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if we couldn't find a match (it happens), assume it was the last key pressed
|
|
||||||
if (idx === null) {
|
|
||||||
idx = this._keyDownList.length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var item = this._keyDownList.splice(idx, 1)[0];
|
this._sendKeyEvent(this._keyDownList[code], code, false);
|
||||||
for (var key in item.keysyms) {
|
|
||||||
this._sendKeyEvent(item.keysyms[key], code, false);
|
delete this._keyDownList[code];
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_allKeysUp: function () {
|
_allKeysUp: function () {
|
||||||
Log.Debug(">> Keyboard.allKeysUp");
|
Log.Debug(">> Keyboard.allKeysUp");
|
||||||
for (var i = 0; i < this._keyDownList.length; i++) {
|
for (var code in this._keyDownList) {
|
||||||
var item = this._keyDownList[i];
|
this._sendKeyEvent(this._keyDownList[code], code, false);
|
||||||
for (var key in item.keysyms) {
|
|
||||||
this._sendKeyEvent(item.keysyms[key], 'Unidentified', false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
this._keyDownList = [];
|
this._keyDownList = {};
|
||||||
Log.Debug("<< Keyboard.allKeysUp");
|
Log.Debug("<< Keyboard.allKeysUp");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -125,20 +125,25 @@ describe('Key Event Handling', function() {
|
||||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
|
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
|
||||||
kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'b'}));
|
kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'b'}));
|
||||||
});
|
});
|
||||||
|
it('should send the same keysym for multiple presses', function() {
|
||||||
|
var count = 0;
|
||||||
|
var kbd = new Keyboard({
|
||||||
|
onKeyEvent: function(keysym, code, down) {
|
||||||
|
expect(keysym).to.be.equal(0x61);
|
||||||
|
expect(code).to.be.equal('KeyA');
|
||||||
|
expect(down).to.be.equal(true);
|
||||||
|
count++;
|
||||||
|
}});
|
||||||
|
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
|
||||||
|
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'b'}));
|
||||||
|
expect(count).to.be.equal(2);
|
||||||
|
});
|
||||||
it('should do nothing on keyup events if no keys are down', function() {
|
it('should do nothing on keyup events if no keys are down', function() {
|
||||||
var callback = sinon.spy();
|
var callback = sinon.spy();
|
||||||
var kbd = new Keyboard({onKeyEvent: callback});
|
var kbd = new Keyboard({onKeyEvent: callback});
|
||||||
kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
|
kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
|
||||||
expect(callback).to.not.have.been.called;
|
expect(callback).to.not.have.been.called;
|
||||||
});
|
});
|
||||||
it('should send a key release for each key press with the same code', function() {
|
|
||||||
var callback = sinon.spy();
|
|
||||||
var kbd = new Keyboard({onKeyEvent: callback});
|
|
||||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
|
|
||||||
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'b'}));
|
|
||||||
kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA'}));
|
|
||||||
expect(callback.callCount).to.be.equal(4);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Escape Modifiers', function() {
|
describe('Escape Modifiers', function() {
|
||||||
|
|
Loading…
Reference in New Issue