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:
j-travis 2024-03-12 08:22:53 -04:00 committed by GitHub
parent 8ac23d2609
commit bf7a417ab7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 62 additions and 23 deletions

View File

@ -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")

View File

@ -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';

View File

@ -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) {
this._handleMouseMove(elementX, elementY, false, true); if (this._isPrimaryDisplay) {
this._cursor.move(ev.detail.clientX, ev.detail.clientY); this._handleMouseMove(elementX, elementY, false, true);
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;

View File

@ -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;

View File

@ -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');