Use "input lock" rather than pointer lock

This new API can now be used to support [keyboard
lock](https://web.dev/keyboard-lock/), although support for that is
limited to Chrome only at the moment.
This commit is contained in:
lhchavez 2021-03-16 06:59:56 -07:00
parent 62a0643ed3
commit 30fda3c948
4 changed files with 73 additions and 39 deletions

View File

@ -1041,7 +1041,7 @@ const UI = {
UI.rfb.addEventListener("clipboard", UI.clipboardReceive);
UI.rfb.addEventListener("bell", UI.bell);
UI.rfb.addEventListener("desktopname", UI.updateDesktopName);
UI.rfb.addEventListener("pointerlock", UI.pointerLockChanged);
UI.rfb.addEventListener("inputlock", UI.inputLockChanged);
UI.rfb.clipViewport = UI.getSetting('view_clip');
UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
@ -1324,7 +1324,7 @@ const UI = {
},
requestPointerLock() {
UI.rfb.requestPointerLock();
UI.rfb.requestInputLock({ pointer: true });
},
/* ------^-------
@ -1695,8 +1695,8 @@ const UI = {
document.title = e.detail.name + " - " + PAGE_TITLE;
},
pointerLockChanged(e) {
if (e.detail.pointerlock) {
inputLockChanged(e) {
if (e.detail.pointer) {
document
.getElementById("noVNC_pointer_lock_button")
.classList.add("noVNC_selected");

View File

@ -170,6 +170,7 @@ export default class RFB extends EventTargetMixin {
windowResize: this._windowResize.bind(this),
handleMouse: this._handleMouse.bind(this),
handlePointerLockChange: this._handlePointerLockChange.bind(this),
handlePointerLockError: this._handlePointerLockError.bind(this),
handleWheel: this._handleWheel.bind(this),
handleGesture: this._handleGesture.bind(this),
};
@ -479,12 +480,22 @@ export default class RFB extends EventTargetMixin {
this._canvas.blur();
}
requestPointerLock() {
if (this._canvas.requestPointerLock) {
this._canvas.requestPointerLock();
} else if (this._canvas.mozRequestPointerLock) {
this._canvas.mozRequestPointerLock();
requestInputLock(locks) {
if (locks.pointer) {
if (this._canvas.requestPointerLock) {
this._canvas.requestPointerLock();
return;
}
if (this._canvas.mozRequestPointerLock) {
this._canvas.mozRequestPointerLock();
return;
}
}
// If we were not able to request any lock, still let the user know
// about the result.
this.dispatchEvent(new CustomEvent(
"inputlock",
{ detail: { pointer: this._pointerLock }, }));
}
clipboardPasteFrom(text) {
@ -549,8 +560,14 @@ export default class RFB extends EventTargetMixin {
// preventDefault() on mousedown doesn't stop this event for some
// reason so we have to explicitly block it
this._canvas.addEventListener('contextmenu', this._eventHandlers.handleMouse);
// This needs to be installed in document instead of the canvas.
document.addEventListener('pointerlockchange', this._eventHandlers.handlePointerLockChange);
// Pointer Lock listeners need to be installed in document instead of the canvas.
if (document.onpointerlockchange !== undefined) {
document.addEventListener('pointerlockchange', this._eventHandlers.handlePointerLockChange, false);
document.addEventListener('pointerlockerror', this._eventHandlers.handlePointerLockError, false);
} else if (document.onmozpointerlockchange !== undefined) {
document.addEventListener('mozpointerlockchange', this._eventHandlers.handlePointerLockChange, false);
document.addEventListener('mozpointerlockerror', this._eventHandlers.handlePointerLockError, false);
}
// Wheel events
this._canvas.addEventListener("wheel", this._eventHandlers.handleWheel);
@ -575,7 +592,13 @@ export default class RFB extends EventTargetMixin {
this._canvas.removeEventListener('mousemove', this._eventHandlers.handleMouse);
this._canvas.removeEventListener('click', this._eventHandlers.handleMouse);
this._canvas.removeEventListener('contextmenu', this._eventHandlers.handleMouse);
document.removeEventListener('pointerlockchange', this._eventHandlers.handlePointerLockChange);
if (document.onpointerlockchange !== undefined) {
document.removeEventListener('pointerlockchange', this._eventHandlers.handlePointerLockChange);
document.removeEventListener('pointerlockerror', this._eventHandlers.handlePointerLockError);
} else if (document.onmozpointerlockchange !== undefined) {
document.removeEventListener('mozpointerlockchange', this._eventHandlers.handlePointerLockChange);
document.removeEventListener('mozpointerlockerror', this._eventHandlers.handlePointerLockError);
}
this._canvas.removeEventListener("mousedown", this._eventHandlers.focusCanvas);
this._canvas.removeEventListener("touchstart", this._eventHandlers.focusCanvas);
window.removeEventListener('resize', this._eventHandlers.windowResize);
@ -1031,8 +1054,14 @@ export default class RFB extends EventTargetMixin {
this._cursor.setEmulateCursor(false);
}
this.dispatchEvent(new CustomEvent(
"pointerlock",
{ detail: { pointerlock: this._pointerLock }, }));
"inputlock",
{ detail: { pointer: this._pointerLock }, }));
}
_handlePointerLockError() {
this.dispatchEvent(new CustomEvent(
"inputlock",
{ detail: { pointer: this._pointerLock }, }));
}
_sendMouse(x, y, mask) {

View File

@ -113,9 +113,9 @@ protocol stream.
- The `capabilities` event is fired when `RFB.capabilities` is
updated.
[`pointerlock`](#pointerlock)
- The `pointerlock` event is fired when the Pointer Lock is acquired (or
released) by the canvas.
[`inputlock`](#inputlock)
- The `inputlock` event is fired when an input lock is acquired (or released)
by the canvas.
### Methods
@ -150,8 +150,8 @@ protocol stream.
[`RFB.clipboardPasteFrom()`](#rfbclipboardPasteFrom)
- Send clipboard contents to server.
[`RFB.requestPointerLock()`](#rfbrequestPointerLock)
- Requests that the RFB canvas acquire a Pointer Lock.
[`RFB.requestInputLock()`](#rfbrequestInputLock)
- Requests that the RFB canvas acquire an input lock.
### Details
@ -269,14 +269,14 @@ The `capabilities` event is fired whenever an entry is added or removed
from `RFB.capabilities`. The `detail` property is an `Object` with the
property `capabilities` containing the new value of `RFB.capabilities`.
#### pointerlock
#### inputlock
The `pointerlock` event is fired when the state of the canvas' Pointer Lock has
changed, either because it has successfully acquired the lock and will have
full control of the mouse pointer, or because the lock was released by the user
pressing the ESC key or performing a browser-specific gesture. The `detail`
property is an `Object` with the property `pointerlock` containing whether the
lock is currently held or not.
The `inputlock` event is fired after a request to acquire an input lock or
whenever the state of the canvas' input lock has changed, the latter typically
occurs because the lock was released by the user pressing the ESC key or
performing a browser-specific gesture. The `detail` property is an `Object`
with the property `pointer` containing whether the Pointer Lock is currently
held or not.
#### RFB.disconnect()
@ -400,18 +400,23 @@ to the remote server.
**`text`**
- A `DOMString` specifying the clipboard data to send.
#### RFB.requestPointerLock()
#### RFB.requestInputLock()
The `RFB.requestPointerLock()` method is used to request that the RFB canvas
hold a [Pointer
Lock](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API), which
hides the mouse cursor and provides relative motion events. This must be called
directly from an event handler where a user has directly interacted with the
browser for the browser to allow this.
If the acquisition of the pointer lock is successful, a `pointerlock` event
will be fired.
The `RFB.requestInputLock()` method is used to request that the RFB canvas hold
an input lock. An `inputlock` event will be fired with the result of the
acquisition of the requested locks.
##### Syntax
RFB.requestPointerLock( );
RFB.requestInputLock( { pointer: true } );
###### Parameters
**`pointer`**
- Requests to acquire a [Pointer
Lock](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API),
which hides the local mouse cursor and provides relative motion events.
This must be called directly from an event handler where a user has
directly interacted with an element through an [engagement
gesture](https://w3c.github.io/pointerlock/#dfn-engagement-gesture) (e.g. a
click or touch event) for the browser to allow this.

View File

@ -2683,14 +2683,14 @@ describe('Remote Frame Buffer Protocol Client', function () {
client._resize(100, 100);
const spy = sinon.spy();
client.addEventListener("pointerlock", spy);
client.addEventListener("inputlock", spy);
let stub = sinon.stub(document, 'pointerLockElement');
stub.get(function () { return client._canvas; });
client._handlePointerLockChange();
stub.restore();
client._sock._websocket._receiveData(new Uint8Array([0x02, 0x02]));
expect(spy).to.have.been.calledOnce;
expect(spy.args[0][0].detail.pointerlock).to.be.true;
expect(spy.args[0][0].detail.pointer).to.be.true;
const cursorSpy = sinon.spy(client, '_handleVMwareCursorPosition');
client._sock._websocket._receiveData(new Uint8Array(incoming));