KASM-5697 Account for improper keyboard implementation of oculus browser (#103)
* KASM-5697 Account for improper keyboard implementation of oculus browser * fix mouse drag * fix touch gestures on secondary displays --------- Co-authored-by: mattmcclaskey <matt@kasmweb.com>
This commit is contained in:
parent
8ac23d2609
commit
bf7a417ab7
|
@ -427,6 +427,8 @@ const UI = {
|
||||||
addTouchSpecificHandlers() {
|
addTouchSpecificHandlers() {
|
||||||
document.getElementById("noVNC_keyboard_button")
|
document.getElementById("noVNC_keyboard_button")
|
||||||
.addEventListener('click', UI.toggleVirtualKeyboard);
|
.addEventListener('click', UI.toggleVirtualKeyboard);
|
||||||
|
document.getElementById("noVNC_keyboard_button")
|
||||||
|
.addEventListener('touch', UI.toggleVirtualKeyboard);
|
||||||
document.getElementById("noVNC_keyboardinput")
|
document.getElementById("noVNC_keyboardinput")
|
||||||
.addEventListener('focus', UI.onfocusVirtualKeyboard);
|
.addEventListener('focus', UI.onfocusVirtualKeyboard);
|
||||||
document.getElementById("noVNC_keyboardinput")
|
document.getElementById("noVNC_keyboardinput")
|
||||||
|
|
|
@ -67,7 +67,7 @@ export function getKeycode(evt) {
|
||||||
// Get 'KeyboardEvent.key', handling legacy browsers
|
// Get 'KeyboardEvent.key', handling legacy browsers
|
||||||
export function getKey(evt) {
|
export function getKey(evt) {
|
||||||
// Are we getting a proper key value?
|
// Are we getting a proper key value?
|
||||||
if (evt.key !== undefined) {
|
if ((evt.key !== undefined) && (evt.key !== 'Unidentified')) {
|
||||||
// Mozilla isn't fully in sync with the spec yet
|
// Mozilla isn't fully in sync with the spec yet
|
||||||
switch (evt.key) {
|
switch (evt.key) {
|
||||||
case 'OS': return 'Meta';
|
case 'OS': return 'Meta';
|
||||||
|
|
59
core/rfb.js
59
core/rfb.js
|
@ -12,7 +12,7 @@ import { toUnsigned32bit, toSigned32bit } from './util/int.js';
|
||||||
import * as Log from './util/logging.js';
|
import * as Log from './util/logging.js';
|
||||||
import { encodeUTF8, decodeUTF8, uuidv4 } from './util/strings.js';
|
import { encodeUTF8, decodeUTF8, uuidv4 } from './util/strings.js';
|
||||||
import { hashUInt8Array } from './util/int.js';
|
import { hashUInt8Array } from './util/int.js';
|
||||||
import { dragThreshold, supportsCursorURIs, isTouchDevice, isWindows, isMac, isIOS } from './util/browser.js';
|
import { dragThreshold, supportsCursorURIs, isTouchDevice, isWindows, isMac, isIOS, isDesktop } from './util/browser.js';
|
||||||
import { clientToElement } from './util/element.js';
|
import { clientToElement } from './util/element.js';
|
||||||
import { setCapture } from './util/events.js';
|
import { setCapture } from './util/events.js';
|
||||||
import EventTargetMixin from './util/eventtarget.js';
|
import EventTargetMixin from './util/eventtarget.js';
|
||||||
|
@ -2224,7 +2224,7 @@ export default class RFB extends EventTargetMixin {
|
||||||
|
|
||||||
// With multiple displays, it is possible to end up in a state where we lost the mouseup event
|
// With multiple displays, it is possible to end up in a state where we lost the mouseup event
|
||||||
// If a mouse move indicates no buttons are down but the current state shows something down, lets clear the plate
|
// If a mouse move indicates no buttons are down but the current state shows something down, lets clear the plate
|
||||||
if (this._mouseButtonMask !== 0 && !down && !simulated) {
|
if (this._display.screens.length > 1 && this._mouseButtonMask !== 0 && !down && !simulated && isDesktop()) {
|
||||||
this._mouseButtonMask = 0;
|
this._mouseButtonMask = 0;
|
||||||
Log.Debug('Mouse event button down mismatch with current mask, resetting mask to 0.')
|
Log.Debug('Mouse event button down mismatch with current mask, resetting mask to 0.')
|
||||||
}
|
}
|
||||||
|
@ -2375,8 +2375,14 @@ export default class RFB extends EventTargetMixin {
|
||||||
}
|
}
|
||||||
|
|
||||||
_fakeMouseMove(ev, elementX, elementY) {
|
_fakeMouseMove(ev, elementX, elementY) {
|
||||||
|
if (this._isPrimaryDisplay) {
|
||||||
this._handleMouseMove(elementX, elementY, false, true);
|
this._handleMouseMove(elementX, elementY, false, true);
|
||||||
this._cursor.move(ev.detail.clientX, ev.detail.clientY);
|
this._cursor.move(ev.detail.clientX, ev.detail.clientY);
|
||||||
|
} else {
|
||||||
|
this._proxyRFBMessage('mousemove', [ elementX, elementY, true, false ]);
|
||||||
|
this._cursor.move(ev.detail.clientX, ev.detail.clientY);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleTapEvent(ev, bmask) {
|
_handleTapEvent(ev, bmask) {
|
||||||
|
@ -2406,8 +2412,20 @@ export default class RFB extends EventTargetMixin {
|
||||||
this._gestureLastTapTime = Date.now();
|
this._gestureLastTapTime = Date.now();
|
||||||
|
|
||||||
this._fakeMouseMove(this._gestureFirstDoubleTapEv, pos.x, pos.y);
|
this._fakeMouseMove(this._gestureFirstDoubleTapEv, pos.x, pos.y);
|
||||||
this._handleMouseButton(pos.x, pos.y, true, bmask);
|
this._fakeMouseButton(pos.x, pos.y, true, bmask);
|
||||||
this._handleMouseButton(pos.x, pos.y, false, bmask);
|
this._fakeMouseButton(pos.x, pos.y, false, bmask);
|
||||||
|
}
|
||||||
|
|
||||||
|
_fakeMouseButton(x, y, down, mask) {
|
||||||
|
if (this._isPrimaryDisplay) {
|
||||||
|
this._handleMouseButton(x, y, down, mask);
|
||||||
|
} else {
|
||||||
|
if (down) {
|
||||||
|
this._proxyRFBMessage('mousedown', [ x, y, mask ]);
|
||||||
|
} else {
|
||||||
|
this._proxyRFBMessage('mouseup', [ x, y, mask ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleGesture(ev) {
|
_handleGesture(ev) {
|
||||||
|
@ -2429,11 +2447,12 @@ export default class RFB extends EventTargetMixin {
|
||||||
break;
|
break;
|
||||||
case 'drag':
|
case 'drag':
|
||||||
this._fakeMouseMove(ev, pos.x, pos.y);
|
this._fakeMouseMove(ev, pos.x, pos.y);
|
||||||
this._handleMouseButton(pos.x, pos.y, true, 0x1);
|
|
||||||
|
this._fakeMouseButton(pos.x, pos.y, true, 0x1);
|
||||||
break;
|
break;
|
||||||
case 'longpress':
|
case 'longpress':
|
||||||
this._fakeMouseMove(ev, pos.x, pos.y);
|
this._fakeMouseMove(ev, pos.x, pos.y);
|
||||||
this._handleMouseButton(pos.x, pos.y, true, 0x4);
|
this._fakeMouseButton(pos.x, pos.y, true, 0x4);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'twodrag':
|
case 'twodrag':
|
||||||
|
@ -2465,23 +2484,23 @@ export default class RFB extends EventTargetMixin {
|
||||||
// every update.
|
// every update.
|
||||||
this._fakeMouseMove(ev, pos.x, pos.y);
|
this._fakeMouseMove(ev, pos.x, pos.y);
|
||||||
while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) > GESTURE_SCRLSENS) {
|
while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) > GESTURE_SCRLSENS) {
|
||||||
this._handleMouseButton(pos.x, pos.y, true, 0x8);
|
this._fakeMouseButton(pos.x, pos.y, true, 0x8);
|
||||||
this._handleMouseButton(pos.x, pos.y, false, 0x8);
|
this._fakeMouseButton(pos.x, pos.y, false, 0x8);
|
||||||
this._gestureLastMagnitudeY += GESTURE_SCRLSENS;
|
this._gestureLastMagnitudeY += GESTURE_SCRLSENS;
|
||||||
}
|
}
|
||||||
while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) < -GESTURE_SCRLSENS) {
|
while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) < -GESTURE_SCRLSENS) {
|
||||||
this._handleMouseButton(pos.x, pos.y, true, 0x10);
|
this._fakeMouseButton(pos.x, pos.y, true, 0x10);
|
||||||
this._handleMouseButton(pos.x, pos.y, false, 0x10);
|
this._fakeMouseButton(pos.x, pos.y, false, 0x10);
|
||||||
this._gestureLastMagnitudeY -= GESTURE_SCRLSENS;
|
this._gestureLastMagnitudeY -= GESTURE_SCRLSENS;
|
||||||
}
|
}
|
||||||
while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) > GESTURE_SCRLSENS) {
|
while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) > GESTURE_SCRLSENS) {
|
||||||
this._handleMouseButton(pos.x, pos.y, true, 0x20);
|
this._fakeMouseButton(pos.x, pos.y, true, 0x20);
|
||||||
this._handleMouseButton(pos.x, pos.y, false, 0x20);
|
this._fakeMouseButton(pos.x, pos.y, false, 0x20);
|
||||||
this._gestureLastMagnitudeX += GESTURE_SCRLSENS;
|
this._gestureLastMagnitudeX += GESTURE_SCRLSENS;
|
||||||
}
|
}
|
||||||
while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) < -GESTURE_SCRLSENS) {
|
while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) < -GESTURE_SCRLSENS) {
|
||||||
this._handleMouseButton(pos.x, pos.y, true, 0x40);
|
this._fakeMouseButton(pos.x, pos.y, true, 0x40);
|
||||||
this._handleMouseButton(pos.x, pos.y, false, 0x40);
|
this._fakeMouseButton(pos.x, pos.y, false, 0x40);
|
||||||
this._gestureLastMagnitudeX -= GESTURE_SCRLSENS;
|
this._gestureLastMagnitudeX -= GESTURE_SCRLSENS;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -2494,13 +2513,13 @@ export default class RFB extends EventTargetMixin {
|
||||||
if (Math.abs(magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {
|
if (Math.abs(magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {
|
||||||
this._handleKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
|
this._handleKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
|
||||||
while ((magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {
|
while ((magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {
|
||||||
this._handleMouseButton(pos.x, pos.y, true, 0x8);
|
this._fakeMouseButton(pos.x, pos.y, true, 0x8);
|
||||||
this._handleMouseButton(pos.x, pos.y, false, 0x8);
|
this._fakeMouseButton(pos.x, pos.y, false, 0x8);
|
||||||
this._gestureLastMagnitudeX += GESTURE_ZOOMSENS;
|
this._gestureLastMagnitudeX += GESTURE_ZOOMSENS;
|
||||||
}
|
}
|
||||||
while ((magnitude - this._gestureLastMagnitudeX) < -GESTURE_ZOOMSENS) {
|
while ((magnitude - this._gestureLastMagnitudeX) < -GESTURE_ZOOMSENS) {
|
||||||
this._handleMouseButton(pos.x, pos.y, true, 0x10);
|
this._fakeMouseButton(pos.x, pos.y, true, 0x10);
|
||||||
this._handleMouseButton(pos.x, pos.y, false, 0x10);
|
this._fakeMouseButton(pos.x, pos.y, false, 0x10);
|
||||||
this._gestureLastMagnitudeX -= GESTURE_ZOOMSENS;
|
this._gestureLastMagnitudeX -= GESTURE_ZOOMSENS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2519,11 +2538,11 @@ export default class RFB extends EventTargetMixin {
|
||||||
break;
|
break;
|
||||||
case 'drag':
|
case 'drag':
|
||||||
this._fakeMouseMove(ev, pos.x, pos.y);
|
this._fakeMouseMove(ev, pos.x, pos.y);
|
||||||
this._handleMouseButton(pos.x, pos.y, false, 0x1);
|
this._fakeMouseButton(pos.x, pos.y, false, 0x1);
|
||||||
break;
|
break;
|
||||||
case 'longpress':
|
case 'longpress':
|
||||||
this._fakeMouseMove(ev, pos.x, pos.y);
|
this._fakeMouseMove(ev, pos.x, pos.y);
|
||||||
this._handleMouseButton(pos.x, pos.y, false, 0x4);
|
this._fakeMouseButton(pos.x, pos.y, false, 0x4);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -85,6 +85,10 @@ export function isWindows() {
|
||||||
return navigator && !!(/win/i).exec(navigator.platform);
|
return navigator && !!(/win/i).exec(navigator.platform);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isLinux() {
|
||||||
|
return navigator && !!(/linux/i).exec(navigator.platform)
|
||||||
|
}
|
||||||
|
|
||||||
export function isIOS() {
|
export function isIOS() {
|
||||||
return navigator &&
|
return navigator &&
|
||||||
(!!(/ipad/i).exec(navigator.platform) ||
|
(!!(/ipad/i).exec(navigator.platform) ||
|
||||||
|
@ -97,6 +101,18 @@ export function isSafari() {
|
||||||
navigator.userAgent.indexOf('Chrome') === -1);
|
navigator.userAgent.indexOf('Chrome') === -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//is the client a desktop like operating system
|
||||||
|
export function isDesktop() {
|
||||||
|
var userAgent = navigator.userAgent;
|
||||||
|
if (isIOS() || userAgent.indexOf("OculusBrowser") != -1 || userAgent.indexOf("SamsungBrowser") != -1) {
|
||||||
|
return false
|
||||||
|
} else if (userAgent.indexOf("Windows") != -1 || userAgent.indexOf("Mac") != -1 || userAgent.indexOf("X11") != -1 || userAgent.indexOf("Linux") != -1) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns IE version number if IE or older Edge browser
|
// Returns IE version number if IE or older Edge browser
|
||||||
export function isIE() {
|
export function isIE() {
|
||||||
var ua = window.navigator.userAgent;
|
var ua = window.navigator.userAgent;
|
||||||
|
|
|
@ -116,6 +116,8 @@ describe('Helpers', function () {
|
||||||
});
|
});
|
||||||
it('should use charCode if no key', function () {
|
it('should use charCode if no key', function () {
|
||||||
expect(KeyboardUtil.getKey({charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.be.equal('Š');
|
expect(KeyboardUtil.getKey({charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.be.equal('Š');
|
||||||
|
// Broken Oculus browser
|
||||||
|
expect(KeyboardUtil.getKey({charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43, key: 'Unidentified'})).to.be.equal('Š');
|
||||||
});
|
});
|
||||||
it('should return Unidentified when it cannot map the key', function () {
|
it('should return Unidentified when it cannot map the key', function () {
|
||||||
expect(KeyboardUtil.getKey({keycode: 0x42})).to.be.equal('Unidentified');
|
expect(KeyboardUtil.getKey({keycode: 0x42})).to.be.equal('Unidentified');
|
||||||
|
|
Loading…
Reference in New Issue