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

View File

@ -170,6 +170,7 @@ export default class RFB extends EventTargetMixin {
windowResize: this._windowResize.bind(this), windowResize: this._windowResize.bind(this),
handleMouse: this._handleMouse.bind(this), handleMouse: this._handleMouse.bind(this),
handlePointerLockChange: this._handlePointerLockChange.bind(this), handlePointerLockChange: this._handlePointerLockChange.bind(this),
handlePointerLockError: this._handlePointerLockError.bind(this),
handleWheel: this._handleWheel.bind(this), handleWheel: this._handleWheel.bind(this),
handleGesture: this._handleGesture.bind(this), handleGesture: this._handleGesture.bind(this),
}; };
@ -479,12 +480,22 @@ export default class RFB extends EventTargetMixin {
this._canvas.blur(); this._canvas.blur();
} }
requestPointerLock() { requestInputLock(locks) {
if (this._canvas.requestPointerLock) { if (locks.pointer) {
this._canvas.requestPointerLock(); if (this._canvas.requestPointerLock) {
} else if (this._canvas.mozRequestPointerLock) { this._canvas.requestPointerLock();
this._canvas.mozRequestPointerLock(); 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) { clipboardPasteFrom(text) {
@ -549,8 +560,14 @@ export default class RFB extends EventTargetMixin {
// preventDefault() on mousedown doesn't stop this event for some // preventDefault() on mousedown doesn't stop this event for some
// reason so we have to explicitly block it // reason so we have to explicitly block it
this._canvas.addEventListener('contextmenu', this._eventHandlers.handleMouse); this._canvas.addEventListener('contextmenu', this._eventHandlers.handleMouse);
// This needs to be installed in document instead of the canvas. // Pointer Lock listeners need to be installed in document instead of the canvas.
document.addEventListener('pointerlockchange', this._eventHandlers.handlePointerLockChange); 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 // Wheel events
this._canvas.addEventListener("wheel", this._eventHandlers.handleWheel); 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('mousemove', this._eventHandlers.handleMouse);
this._canvas.removeEventListener('click', this._eventHandlers.handleMouse); this._canvas.removeEventListener('click', this._eventHandlers.handleMouse);
this._canvas.removeEventListener('contextmenu', 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("mousedown", this._eventHandlers.focusCanvas);
this._canvas.removeEventListener("touchstart", this._eventHandlers.focusCanvas); this._canvas.removeEventListener("touchstart", this._eventHandlers.focusCanvas);
window.removeEventListener('resize', this._eventHandlers.windowResize); window.removeEventListener('resize', this._eventHandlers.windowResize);
@ -1031,8 +1054,14 @@ export default class RFB extends EventTargetMixin {
this._cursor.setEmulateCursor(false); this._cursor.setEmulateCursor(false);
} }
this.dispatchEvent(new CustomEvent( this.dispatchEvent(new CustomEvent(
"pointerlock", "inputlock",
{ detail: { pointerlock: this._pointerLock }, })); { detail: { pointer: this._pointerLock }, }));
}
_handlePointerLockError() {
this.dispatchEvent(new CustomEvent(
"inputlock",
{ detail: { pointer: this._pointerLock }, }));
} }
_sendMouse(x, y, mask) { _sendMouse(x, y, mask) {

View File

@ -113,9 +113,9 @@ protocol stream.
- The `capabilities` event is fired when `RFB.capabilities` is - The `capabilities` event is fired when `RFB.capabilities` is
updated. updated.
[`pointerlock`](#pointerlock) [`inputlock`](#inputlock)
- The `pointerlock` event is fired when the Pointer Lock is acquired (or - The `inputlock` event is fired when an input lock is acquired (or released)
released) by the canvas. by the canvas.
### Methods ### Methods
@ -150,8 +150,8 @@ protocol stream.
[`RFB.clipboardPasteFrom()`](#rfbclipboardPasteFrom) [`RFB.clipboardPasteFrom()`](#rfbclipboardPasteFrom)
- Send clipboard contents to server. - Send clipboard contents to server.
[`RFB.requestPointerLock()`](#rfbrequestPointerLock) [`RFB.requestInputLock()`](#rfbrequestInputLock)
- Requests that the RFB canvas acquire a Pointer Lock. - Requests that the RFB canvas acquire an input lock.
### Details ### 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 from `RFB.capabilities`. The `detail` property is an `Object` with the
property `capabilities` containing the new value of `RFB.capabilities`. 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 The `inputlock` event is fired after a request to acquire an input lock or
changed, either because it has successfully acquired the lock and will have whenever the state of the canvas' input lock has changed, the latter typically
full control of the mouse pointer, or because the lock was released by the user occurs because the lock was released by the user pressing the ESC key or
pressing the ESC key or performing a browser-specific gesture. The `detail` performing a browser-specific gesture. The `detail` property is an `Object`
property is an `Object` with the property `pointerlock` containing whether the with the property `pointer` containing whether the Pointer Lock is currently
lock is currently held or not. held or not.
#### RFB.disconnect() #### RFB.disconnect()
@ -400,18 +400,23 @@ to the remote server.
**`text`** **`text`**
- A `DOMString` specifying the clipboard data to send. - A `DOMString` specifying the clipboard data to send.
#### RFB.requestPointerLock() #### RFB.requestInputLock()
The `RFB.requestPointerLock()` method is used to request that the RFB canvas The `RFB.requestInputLock()` method is used to request that the RFB canvas hold
hold a [Pointer an input lock. An `inputlock` event will be fired with the result of the
Lock](https://developer.mozilla.org/en-US/docs/Web/API/Pointer_Lock_API), which acquisition of the requested locks.
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.
##### Syntax ##### 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); client._resize(100, 100);
const spy = sinon.spy(); const spy = sinon.spy();
client.addEventListener("pointerlock", spy); client.addEventListener("inputlock", spy);
let stub = sinon.stub(document, 'pointerLockElement'); let stub = sinon.stub(document, 'pointerLockElement');
stub.get(function () { return client._canvas; }); stub.get(function () { return client._canvas; });
client._handlePointerLockChange(); client._handlePointerLockChange();
stub.restore(); stub.restore();
client._sock._websocket._receiveData(new Uint8Array([0x02, 0x02])); client._sock._websocket._receiveData(new Uint8Array([0x02, 0x02]));
expect(spy).to.have.been.calledOnce; 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'); const cursorSpy = sinon.spy(client, '_handleVMwareCursorPosition');
client._sock._websocket._receiveData(new Uint8Array(incoming)); client._sock._websocket._receiveData(new Uint8Array(incoming));