diff --git a/app/ui.js b/app/ui.js index 29335572..ee831961 100644 --- a/app/ui.js +++ b/app/ui.js @@ -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"); diff --git a/core/rfb.js b/core/rfb.js index 7fc246ee..1ef01833 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -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) { diff --git a/docs/API.md b/docs/API.md index 780001bc..134a9709 100644 --- a/docs/API.md +++ b/docs/API.md @@ -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. diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 41c75404..c71c273a 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -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));