Skip to content

Commit 97c1e0d

Browse files
Added support and tests for vrdisplaypointerrestricted events to the a-scene element with the possibility to disable the behavior (on by default)
1 parent 7781f42 commit 97c1e0d

File tree

2 files changed

+193
-1
lines changed

2 files changed

+193
-1
lines changed

src/core/scene/a-scene.js

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@ var warn = utils.debug('core:a-scene:warn');
3131
* @member {object} object3D - Root three.js Scene object.
3232
* @member {object} renderer
3333
* @member {bool} renderStarted
34-
* @member (object) effect - three.js VREffect
34+
* @member {object} effect - three.js VREffect
3535
* @member {object} systems - Registered instantiated systems.
3636
* @member {number} time
37+
* @member {bool} isCanvasPointerLockEnabled - true: set pointerlock on the canvas element in response to/
38+
* false: ignore vrdisplaypointerrestricted event.
3739
*/
3840
module.exports.AScene = registerElement('a-scene', {
3941
prototype: Object.create(AEntity.prototype, {
@@ -56,6 +58,7 @@ module.exports.AScene = registerElement('a-scene', {
5658
this.systems = {};
5759
this.systemNames = [];
5860
this.time = 0;
61+
this.isCanvasPointerLockEnabled = true;
5962
this.init();
6063
}
6164
},
@@ -119,6 +122,8 @@ module.exports.AScene = registerElement('a-scene', {
119122
this.enterVRBound = function () { self.enterVR(); };
120123
this.exitVRBound = function () { self.exitVR(); };
121124
this.exitVRTrueBound = function () { self.exitVR(true); };
125+
this.pointerRestrictedBound = function () { self.pointerRestricted(); };
126+
this.pointerUnrestrictedBound = function () { self.pointerUnrestricted(); };
122127

123128
// Enter VR on `vrdisplayactivate` (e.g. putting on Rift headset).
124129
window.addEventListener('vrdisplayactivate', this.enterVRBound);
@@ -131,6 +136,14 @@ module.exports.AScene = registerElement('a-scene', {
131136

132137
// Exit VR on `vrdisplaydisconnect` (e.g. unplugging Rift headset).
133138
window.addEventListener('vrdisplaydisconnect', this.exitVRTrueBound);
139+
140+
// Register for mouse restricted events while in VR
141+
// (e.g. mouse no longer available on desktop 2D view)
142+
window.addEventListener('vrdisplaypointerrestricted', this.pointerRestrictedBound);
143+
144+
// Register for mouse unrestricted events while in VR
145+
// (e.g. mouse once again available on desktop 2D view)
146+
window.addEventListener('vrdisplaypointerunrestricted', this.pointerUnrestrictedBound);
134147
},
135148
writable: window.debug
136149
},
@@ -178,6 +191,8 @@ module.exports.AScene = registerElement('a-scene', {
178191
window.removeEventListener('vrdisplaydeactivate', this.exitVRBound);
179192
window.removeEventListener('vrdisplayconnect', this.enterVRBound);
180193
window.removeEventListener('vrdisplaydisconnect', this.exitVRTrueBound);
194+
window.removeEventListener('vrdisplaypointerrestricted', this.pointerRestrictedBound);
195+
window.removeEventListener('vrdisplaypointerunrestricted', this.pointerUnrestrictedBound);
181196
}
182197
},
183198

@@ -202,6 +217,16 @@ module.exports.AScene = registerElement('a-scene', {
202217
}
203218
},
204219

220+
/**
221+
* For tests.
222+
*/
223+
getPointerLockElement: {
224+
value: function () {
225+
return document.pointerLockElement;
226+
},
227+
writable: window.debug
228+
},
229+
205230
/**
206231
* For tests.
207232
*/
@@ -317,6 +342,50 @@ module.exports.AScene = registerElement('a-scene', {
317342
writable: window.debug
318343
},
319344

345+
enableCanvasPointerLock: {
346+
value: function () {
347+
this.isCanvasPointerLockEnabled = true;
348+
}
349+
},
350+
351+
disableCanvasPointerLock: {
352+
value: function () {
353+
this.isCanvasPointerLockEnabled = false;
354+
}
355+
},
356+
357+
pointerRestricted: {
358+
value: function () {
359+
if (!this.isCanvasPointerLockEnabled) {
360+
return;
361+
}
362+
363+
if (this.canvas) {
364+
var pointerLockElement = this.getPointerLockElement();
365+
if (pointerLockElement && pointerLockElement !== this.canvas && document.exitPointerLock) {
366+
// Recreate pointer lock on the canvas, if taken on another element.
367+
document.exitPointerLock();
368+
}
369+
370+
if (this.canvas.requestPointerLock) {
371+
this.canvas.requestPointerLock();
372+
}
373+
}
374+
}
375+
},
376+
377+
pointerUnrestricted: {
378+
value: function () {
379+
if (!this.isCanvasPointerLockEnabled) {
380+
return;
381+
}
382+
var pointerLockElement = this.getPointerLockElement();
383+
if (pointerLockElement && pointerLockElement === this.canvas && document.exitPointerLock) {
384+
document.exitPointerLock();
385+
}
386+
}
387+
},
388+
320389
/**
321390
* Handle `vrdisplaypresentchange` event for exiting VR through other means than
322391
* `<ESC>` key. For example, GearVR back button on Oculus Browser.

tests/core/scene/a-scene.test.js

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,129 @@ suite('a-scene (without renderer)', function () {
403403
});
404404
});
405405

406+
suite('pointerRestricted', function () {
407+
setup(function () {
408+
var sceneEl = this.el;
409+
410+
// Stub canvas.
411+
sceneEl.canvas = document.createElement('canvas');
412+
});
413+
414+
test('requests pointerlock when restricted', function (done) {
415+
var sceneEl = this.el;
416+
var event;
417+
var requestPointerLockSpy;
418+
419+
requestPointerLockSpy = this.sinon.spy(sceneEl.canvas, 'requestPointerLock');
420+
event = new CustomEvent('vrdisplaypointerrestricted');
421+
window.dispatchEvent(event);
422+
423+
process.nextTick(function () {
424+
assert.ok(requestPointerLockSpy.called);
425+
done();
426+
});
427+
});
428+
429+
test('ignores pointerlock if isCanvasPointerLockEnabled is false', function (done) {
430+
var sceneEl = this.el;
431+
var event;
432+
var requestPointerLockSpy;
433+
434+
requestPointerLockSpy = this.sinon.spy(sceneEl.canvas, 'requestPointerLock');
435+
sceneEl.disableCanvasPointerLock();
436+
event = new CustomEvent('vrdisplaypointerrestricted');
437+
window.dispatchEvent(event);
438+
439+
process.nextTick(function () {
440+
assert.notOk(requestPointerLockSpy.called);
441+
done();
442+
});
443+
});
444+
445+
test('exits pointerlock when unrestricted', function (done) {
446+
var sceneEl = this.el;
447+
var event;
448+
var exitPointerLockSpy;
449+
450+
exitPointerLockSpy = this.sinon.spy(document, 'exitPointerLock');
451+
452+
event = new CustomEvent('vrdisplaypointerunrestricted');
453+
454+
this.sinon.stub(sceneEl, 'getPointerLockElement', function () {
455+
return sceneEl.canvas;
456+
});
457+
window.dispatchEvent(event);
458+
459+
process.nextTick(function () {
460+
assert.ok(exitPointerLockSpy.called);
461+
done();
462+
});
463+
});
464+
465+
test('does not exit pointerlock when unrestricted on different locked element', function (done) {
466+
var sceneEl = this.el;
467+
var event;
468+
var exitPointerLockSpy;
469+
470+
exitPointerLockSpy = this.sinon.spy(document, 'exitPointerLock');
471+
472+
event = new CustomEvent('vrdisplaypointerunrestricted');
473+
474+
this.sinon.stub(sceneEl, 'getPointerLockElement', function () {
475+
// Mock that pointerlock is taken by the page itself,
476+
// independently of the a-scene handler for vrdisplaypointerrestricted event
477+
return document.createElement('canvas');
478+
});
479+
window.dispatchEvent(event);
480+
481+
process.nextTick(function () {
482+
assert.notOk(exitPointerLockSpy.called);
483+
done();
484+
});
485+
});
486+
487+
test('does not exit pointerlock when unrestricted if isCanvasPointerLockEnabled is false', function (done) {
488+
var sceneEl = this.el;
489+
var event;
490+
var exitPointerLockSpy;
491+
492+
exitPointerLockSpy = this.sinon.spy(document, 'exitPointerLock');
493+
494+
event = new CustomEvent('vrdisplaypointerunrestricted');
495+
sceneEl.disableCanvasPointerLock();
496+
window.dispatchEvent(event);
497+
498+
process.nextTick(function () {
499+
assert.notOk(exitPointerLockSpy.called);
500+
done();
501+
});
502+
});
503+
504+
test('update existing pointerlock target when restricted', function (done) {
505+
var sceneEl = this.el;
506+
var event;
507+
var exitPointerLockSpy;
508+
var requestPointerLockSpy;
509+
510+
exitPointerLockSpy = this.sinon.spy(document, 'exitPointerLock');
511+
requestPointerLockSpy = this.sinon.spy(sceneEl.canvas, 'requestPointerLock');
512+
event = new CustomEvent('vrdisplaypointerrestricted');
513+
514+
this.sinon.stub(sceneEl, 'getPointerLockElement', function () {
515+
// Mock that pointerlock is taken by the page itself,
516+
// independently of the a-scene handler for vrdisplaypointerrestricted event
517+
return document.createElement('canvas');
518+
});
519+
window.dispatchEvent(event);
520+
521+
process.nextTick(function () {
522+
assert.ok(exitPointerLockSpy.called);
523+
assert.ok(requestPointerLockSpy.called);
524+
done();
525+
});
526+
});
527+
});
528+
406529
suite('system', function () {
407530
teardown(function () {
408531
delete components.test;

0 commit comments

Comments
 (0)