This commit is contained in:
Pierre Ossman 2020-12-10 10:23:10 +01:00
commit 67ac9f9c0d
5 changed files with 109 additions and 10 deletions

View File

@ -119,7 +119,7 @@ addStandard("WakeUp", KeyTable.XF86XK_WakeUp);
// 2.8. IME and Composition Keys // 2.8. IME and Composition Keys
addStandard("AllCandidates", KeyTable.XK_MultipleCandidate); addStandard("AllCandidates", KeyTable.XK_MultipleCandidate);
addStandard("Alphanumeric", KeyTable.XK_Eisu_Shift); // could also be _Eisu_Toggle addStandard("Alphanumeric", KeyTable.XK_Eisu_toggle);
addStandard("CodeInput", KeyTable.XK_Codeinput); addStandard("CodeInput", KeyTable.XK_Codeinput);
addStandard("Compose", KeyTable.XK_Multi_key); addStandard("Compose", KeyTable.XK_Multi_key);
addStandard("Convert", KeyTable.XK_Henkan); addStandard("Convert", KeyTable.XK_Henkan);
@ -147,7 +147,7 @@ addStandard("KanjiMode", KeyTable.XK_Kanji);
addStandard("Katakana", KeyTable.XK_Katakana); addStandard("Katakana", KeyTable.XK_Katakana);
addStandard("Romaji", KeyTable.XK_Romaji); addStandard("Romaji", KeyTable.XK_Romaji);
addStandard("Zenkaku", KeyTable.XK_Zenkaku); addStandard("Zenkaku", KeyTable.XK_Zenkaku);
addStandard("ZenkakuHanaku", KeyTable.XK_Zenkaku_Hankaku); addStandard("ZenkakuHankaku", KeyTable.XK_Zenkaku_Hankaku);
// 2.9. General-Purpose Function Keys // 2.9. General-Purpose Function Keys

View File

@ -164,6 +164,20 @@ export default class Keyboard {
return; return;
} }
// Windows doesn't send proper key releases for a bunch of
// Japanese IM keys so we have to fake the release right away
const jpBadKeys = [ KeyTable.XK_Zenkaku_Hankaku,
KeyTable.XK_Eisu_toggle,
KeyTable.XK_Katakana,
KeyTable.XK_Hiragana,
KeyTable.XK_Romaji ];
if (browser.isWindows() && jpBadKeys.includes(keysym)) {
this._sendKeyEvent(keysym, code, true);
this._sendKeyEvent(keysym, code, false);
stopEvent(e);
return;
}
stopEvent(e); stopEvent(e);
// Possible start of AltGr sequence? (see above) // Possible start of AltGr sequence? (see above)

View File

@ -157,6 +157,21 @@ export function getKeysym(evt) {
} }
} }
// Windows sends alternating symbols for some keys when using a
// Japanese layout. We have no way of synchronising with the IM
// running on the remote system, so we send some combined keysym
// instead and hope for the best.
if (browser.isWindows()) {
switch (key) {
case 'Zenkaku':
case 'Hankaku':
return KeyTable.XK_Zenkaku_Hankaku;
case 'Romaji':
case 'KanaMode':
return KeyTable.XK_Romaji;
}
}
return DOMKeyTable[key][location]; return DOMKeyTable[key][location];
} }

View File

@ -186,5 +186,38 @@ describe('Helpers', function () {
expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: ',', location: 3})).to.be.equal(0xFFAC); expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: ',', location: 3})).to.be.equal(0xFFAC);
}); });
}); });
describe('Japanese IM keys on Windows', function () {
let 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");
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 = "Windows";
});
afterEach(function () {
if (origNavigator !== undefined) {
Object.defineProperty(window, "navigator", origNavigator);
}
});
const keys = { 'Zenkaku': 0xff2a, 'Hankaku': 0xff2a,
'Romaji': 0xff24, 'KanaMode': 0xff24 };
for (let [key, keysym] of Object.entries(keys)) {
it(`should fake combined key for ${key} on Windows`, function () {
expect(KeyboardUtil.getKeysym({code: 'FakeIM', key: key})).to.be.equal(keysym);
});
}
});
}); });
}); });

View File

@ -219,7 +219,7 @@ describe('Key Event Handling', function () {
} }
}); });
it('should toggle caps lock on key press on iOS', function (done) { it('should toggle caps lock on key press on iOS', function () {
window.navigator.platform = "iPad"; window.navigator.platform = "iPad";
const kbd = new Keyboard(document); const kbd = new Keyboard(document);
kbd.onkeyevent = sinon.spy(); kbd.onkeyevent = sinon.spy();
@ -228,10 +228,9 @@ describe('Key Event Handling', function () {
expect(kbd.onkeyevent).to.have.been.calledTwice; expect(kbd.onkeyevent).to.have.been.calledTwice;
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true);
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false);
done();
}); });
it('should toggle caps lock on key press on mac', function (done) { it('should toggle caps lock on key press on mac', function () {
window.navigator.platform = "Mac"; window.navigator.platform = "Mac";
const kbd = new Keyboard(document); const kbd = new Keyboard(document);
kbd.onkeyevent = sinon.spy(); kbd.onkeyevent = sinon.spy();
@ -240,10 +239,9 @@ describe('Key Event Handling', function () {
expect(kbd.onkeyevent).to.have.been.calledTwice; expect(kbd.onkeyevent).to.have.been.calledTwice;
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true);
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false);
done();
}); });
it('should toggle caps lock on key release on iOS', function (done) { it('should toggle caps lock on key release on iOS', function () {
window.navigator.platform = "iPad"; window.navigator.platform = "iPad";
const kbd = new Keyboard(document); const kbd = new Keyboard(document);
kbd.onkeyevent = sinon.spy(); kbd.onkeyevent = sinon.spy();
@ -252,10 +250,9 @@ describe('Key Event Handling', function () {
expect(kbd.onkeyevent).to.have.been.calledTwice; expect(kbd.onkeyevent).to.have.been.calledTwice;
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true);
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false);
done();
}); });
it('should toggle caps lock on key release on mac', function (done) { it('should toggle caps lock on key release on mac', function () {
window.navigator.platform = "Mac"; window.navigator.platform = "Mac";
const kbd = new Keyboard(document); const kbd = new Keyboard(document);
kbd.onkeyevent = sinon.spy(); kbd.onkeyevent = sinon.spy();
@ -264,10 +261,50 @@ describe('Key Event Handling', function () {
expect(kbd.onkeyevent).to.have.been.calledTwice; expect(kbd.onkeyevent).to.have.been.calledTwice;
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true); expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xFFE5, "CapsLock", true);
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false); expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xFFE5, "CapsLock", false);
done();
}); });
}); });
describe('Japanese IM keys on Windows', function () {
let 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");
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 = "Windows";
});
afterEach(function () {
if (origNavigator !== undefined) {
Object.defineProperty(window, "navigator", origNavigator);
}
});
const keys = { 'Zenkaku': 0xff2a, 'Hankaku': 0xff2a,
'Alphanumeric': 0xff30, 'Katakana': 0xff26,
'Hiragana': 0xff25, 'Romaji': 0xff24,
'KanaMode': 0xff24 };
for (let [key, keysym] of Object.entries(keys)) {
it(`should fake key release for ${key} on Windows`, function () {
let kbd = new Keyboard(document);
kbd.onkeyevent = sinon.spy();
kbd._handleKeyDown(keyevent('keydown', {code: 'FakeIM', key: key}));
expect(kbd.onkeyevent).to.have.been.calledTwice;
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(keysym, "FakeIM", true);
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(keysym, "FakeIM", false);
});
}
});
describe('Escape AltGraph on Windows', function () { describe('Escape AltGraph on Windows', function () {
let origNavigator; let origNavigator;
beforeEach(function () { beforeEach(function () {