Bug 1700051: part 46) Const-qualify `mozInlineSpellStatus::mAnchorRange`. r=smaug
[gecko.git] / dom / vr / XRInputSource.cpp
blobc57528125220e35f27dd3e7847f1c58ffd32e6bd
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/XRInputSource.h"
8 #include "mozilla/dom/XRInputSourceEvent.h"
9 #include "XRNativeOriginViewer.h"
10 #include "XRNativeOriginTracker.h"
11 #include "XRInputSpace.h"
12 #include "VRDisplayClient.h"
14 #include "mozilla/HoldDropJSObjects.h"
15 #include "mozilla/dom/Gamepad.h"
16 #include "mozilla/dom/GamepadManager.h"
18 namespace mozilla {
19 namespace dom {
21 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(XRInputSource, mParent, mTargetRaySpace,
22 mGripSpace, mGamepad)
23 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(XRInputSource, AddRef)
24 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(XRInputSource, Release)
26 // Follow the controller profile ids from
27 // https://github.com/immersive-web/webxr-input-profiles.
28 nsTArray<nsString> GetInputSourceProfile(gfx::VRControllerType aType) {
29 nsTArray<nsString> profile;
30 nsString id;
32 switch (aType) {
33 case gfx::VRControllerType::HTCVive:
34 id.AssignLiteral("htc-vive");
35 profile.AppendElement(id);
36 id.AssignLiteral("generic-trigger-squeeze-touchpad");
37 profile.AppendElement(id);
38 break;
39 case gfx::VRControllerType::HTCViveCosmos:
40 id.AssignLiteral("htc-vive-cosmos");
41 profile.AppendElement(id);
42 id.AssignLiteral("generic-trigger-squeeze-thumbstick");
43 profile.AppendElement(id);
44 break;
45 case gfx::VRControllerType::HTCViveFocus:
46 id.AssignLiteral("htc-vive-focus");
47 profile.AppendElement(id);
48 id.AssignLiteral("generic-trigger-touchpad");
49 profile.AppendElement(id);
50 break;
51 case gfx::VRControllerType::HTCViveFocusPlus:
52 id.AssignLiteral("htc-vive-focus-plus");
53 profile.AppendElement(id);
54 id.AssignLiteral("generic-trigger-squeeze-touchpad");
55 profile.AppendElement(id);
56 break;
57 case gfx::VRControllerType::MSMR:
58 id.AssignLiteral("microsoft-mixed-reality");
59 profile.AppendElement(id);
60 id.AssignLiteral("generic-trigger-squeeze-touchpad-thumbstick");
61 profile.AppendElement(id);
62 break;
63 case gfx::VRControllerType::ValveIndex:
64 id.AssignLiteral("valve-index");
65 profile.AppendElement(id);
66 id.AssignLiteral("generic-trigger-squeeze-touchpad-thumbstick");
67 profile.AppendElement(id);
68 break;
69 case gfx::VRControllerType::OculusGo:
70 id.AssignLiteral("oculus-go");
71 profile.AppendElement(id);
72 id.AssignLiteral("generic-trigger-touchpad");
73 profile.AppendElement(id);
74 break;
75 case gfx::VRControllerType::OculusTouch:
76 id.AssignLiteral("oculus-touch");
77 profile.AppendElement(id);
78 id.AssignLiteral("generic-trigger-squeeze-thumbstick");
79 profile.AppendElement(id);
80 break;
81 case gfx::VRControllerType::OculusTouch2:
82 id.AssignLiteral("oculus-touch-v2");
83 profile.AppendElement(id);
84 id.AssignLiteral("oculus-touch");
85 profile.AppendElement(id);
86 id.AssignLiteral("generic-trigger-squeeze-thumbstick");
87 profile.AppendElement(id);
88 break;
89 case gfx::VRControllerType::PicoGaze:
90 id.AssignLiteral("pico-gaze");
91 profile.AppendElement(id);
92 id.AssignLiteral("generic-button");
93 profile.AppendElement(id);
94 break;
95 case gfx::VRControllerType::PicoG2:
96 id.AssignLiteral("pico-g2");
97 profile.AppendElement(id);
98 id.AssignLiteral("generic-trigger-touchpad");
99 profile.AppendElement(id);
100 break;
101 case gfx::VRControllerType::PicoNeo2:
102 id.AssignLiteral("pico-neo2");
103 profile.AppendElement(id);
104 id.AssignLiteral("generic-trigger-squeeze-thumbstick");
105 profile.AppendElement(id);
106 break;
107 default:
108 NS_WARNING("Unsupported XR input source profile.\n");
109 break;
111 return profile;
114 XRInputSource::XRInputSource(nsISupports* aParent)
115 : mParent(aParent),
116 mGamepad(nullptr),
117 mIndex(-1),
118 mSelectAction(ActionState::ActionState_Released),
119 mSqueezeAction(ActionState::ActionState_Released) {}
121 XRInputSource::~XRInputSource() {
122 mTargetRaySpace = nullptr;
123 mGripSpace = nullptr;
124 mGamepad = nullptr;
125 mozilla::DropJSObjects(this);
128 JSObject* XRInputSource::WrapObject(JSContext* aCx,
129 JS::Handle<JSObject*> aGivenProto) {
130 return XRInputSource_Binding::Wrap(aCx, this, aGivenProto);
133 XRHandedness XRInputSource::Handedness() { return mHandedness; }
135 XRTargetRayMode XRInputSource::TargetRayMode() { return mTargetRayMode; }
137 XRSpace* XRInputSource::TargetRaySpace() { return mTargetRaySpace; }
139 XRSpace* XRInputSource::GetGripSpace() { return mGripSpace; }
141 void XRInputSource::GetProfiles(nsTArray<nsString>& aResult) {
142 aResult = mProfiles.Clone();
145 Gamepad* XRInputSource::GetGamepad() { return mGamepad; }
147 void XRInputSource::Setup(XRSession* aSession, uint32_t aIndex) {
148 MOZ_ASSERT(aSession);
149 gfx::VRDisplayClient* displayClient = aSession->GetDisplayClient();
150 if (!displayClient) {
151 MOZ_ASSERT(displayClient);
152 return;
154 const gfx::VRDisplayInfo& displayInfo = displayClient->GetDisplayInfo();
155 const gfx::VRControllerState& controllerState =
156 displayInfo.mControllerState[aIndex];
157 MOZ_ASSERT(controllerState.controllerName[0] != '\0');
159 mProfiles = GetInputSourceProfile(controllerState.type);
160 mHandedness = XRHandedness::None;
161 switch (controllerState.hand) {
162 case GamepadHand::_empty:
163 mHandedness = XRHandedness::None;
164 break;
165 case GamepadHand::Left:
166 mHandedness = XRHandedness::Left;
167 break;
168 case GamepadHand::Right:
169 mHandedness = XRHandedness::Right;
170 break;
171 default:
172 MOZ_ASSERT(false && "Unknown GamepadHand type.");
173 break;
176 RefPtr<XRNativeOrigin> nativeOriginTargetRay = nullptr;
177 mTargetRayMode = XRTargetRayMode::Tracked_pointer;
178 switch (controllerState.targetRayMode) {
179 case gfx::TargetRayMode::Gaze:
180 mTargetRayMode = XRTargetRayMode::Gaze;
181 nativeOriginTargetRay = new XRNativeOriginViewer(displayClient);
182 break;
183 case gfx::TargetRayMode::TrackedPointer:
184 mTargetRayMode = XRTargetRayMode::Tracked_pointer;
185 // We use weak pointers of poses in XRNativeOriginTracker to sync their
186 // data internally.
187 nativeOriginTargetRay =
188 new XRNativeOriginTracker(&controllerState.targetRayPose);
189 break;
190 case gfx::TargetRayMode::Screen:
191 mTargetRayMode = XRTargetRayMode::Screen;
192 break;
193 default:
194 MOZ_ASSERT(false && "Undefined TargetRayMode type.");
195 break;
198 mTargetRaySpace = new XRInputSpace(aSession->GetParentObject(), aSession,
199 nativeOriginTargetRay, aIndex);
201 const uint32_t gamepadHandleValue =
202 displayInfo.mDisplayID * gfx::kVRControllerMaxCount + aIndex;
204 const GamepadHandle gamepadHandle{gamepadHandleValue, GamepadHandleKind::VR};
206 mGamepad =
207 new Gamepad(mParent, NS_ConvertASCIItoUTF16(""), -1, gamepadHandle,
208 GamepadMappingType::Xr_standard, controllerState.hand,
209 displayInfo.mDisplayID, controllerState.numButtons,
210 controllerState.numAxes, controllerState.numHaptics, 0, 0);
211 mIndex = aIndex;
213 if (!mGripSpace) {
214 CreateGripSpace(aSession, controllerState);
218 void XRInputSource::SetGamepadIsConnected(bool aConnected,
219 XRSession* aSession) {
220 mGamepad->SetConnected(aConnected);
221 MOZ_ASSERT(aSession);
223 if (!aConnected) {
224 if (mSelectAction != ActionState::ActionState_Released) {
225 DispatchEvent(u"selectend"_ns, aSession);
226 mSelectAction = ActionState::ActionState_Released;
228 if (mSqueezeAction != ActionState::ActionState_Released) {
229 DispatchEvent(u"squeezeend"_ns, aSession);
230 mSqueezeAction = ActionState::ActionState_Released;
235 void XRInputSource::Update(XRSession* aSession) {
236 MOZ_ASSERT(aSession && mIndex >= 0 && mGamepad);
238 gfx::VRDisplayClient* displayClient = aSession->GetDisplayClient();
239 if (!displayClient) {
240 MOZ_ASSERT(displayClient);
241 return;
243 const gfx::VRDisplayInfo& displayInfo = displayClient->GetDisplayInfo();
244 const gfx::VRControllerState& controllerState =
245 displayInfo.mControllerState[mIndex];
246 MOZ_ASSERT(controllerState.controllerName[0] != '\0');
248 // OculusVR and OpenVR controllers need to wait until
249 // update functions to assign GamepadCapabilityFlags::Cap_GripSpacePosition
250 // flag.
251 if (!mGripSpace) {
252 CreateGripSpace(aSession, controllerState);
255 // Update button values.
256 nsTArray<RefPtr<GamepadButton>> buttons;
257 mGamepad->GetButtons(buttons);
258 for (uint32_t i = 0; i < buttons.Length(); ++i) {
259 const bool pressed = controllerState.buttonPressed & (1ULL << i);
260 const bool touched = controllerState.buttonTouched & (1ULL << i);
262 if (buttons[i]->Pressed() != pressed || buttons[i]->Touched() != touched ||
263 buttons[i]->Value() != controllerState.triggerValue[i]) {
264 mGamepad->SetButton(i, pressed, touched, controllerState.triggerValue[i]);
267 // Update axis values.
268 nsTArray<double> axes;
269 mGamepad->GetAxes(axes);
270 for (uint32_t i = 0; i < axes.Length(); ++i) {
271 if (axes[i] != controllerState.axisValue[i]) {
272 mGamepad->SetAxis(i, controllerState.axisValue[i]);
276 // We define 0.85f and 0.15f based on our current finding
277 // for better experience, we can adjust these values if we need.
278 const float completeThreshold = 0.90f;
279 const float startThreshold = 0.85f;
280 const float endThreshold = 0.15f;
281 const uint32_t selectIndex = 0;
282 const uint32_t squeezeIndex = 1;
284 // Checking selectstart, select, selectend
285 if (buttons.Length() > selectIndex) {
286 if (controllerState.selectActionStartFrameId >
287 controllerState.selectActionStopFrameId) {
288 if (mSelectAction == ActionState::ActionState_Released &&
289 controllerState.triggerValue[selectIndex] > endThreshold) {
290 DispatchEvent(u"selectstart"_ns, aSession);
291 mSelectAction = ActionState::ActionState_Pressing;
292 } else if (mSelectAction == ActionState::ActionState_Pressing &&
293 controllerState.triggerValue[selectIndex] >
294 completeThreshold) {
295 mSelectAction = ActionState::ActionState_Pressed;
296 } else if (mSelectAction == ActionState::ActionState_Pressed &&
297 controllerState.triggerValue[selectIndex] < startThreshold) {
298 DispatchEvent(u"select"_ns, aSession);
299 mSelectAction = ActionState::ActionState_Releasing;
300 } else if (mSelectAction <= ActionState::ActionState_Releasing &&
301 controllerState.triggerValue[selectIndex] < endThreshold) {
302 // For a select btn which only has pressed and unpressed status.
303 if (mSelectAction == ActionState::ActionState_Pressed) {
304 DispatchEvent(u"select"_ns, aSession);
306 DispatchEvent(u"selectend"_ns, aSession);
307 mSelectAction = ActionState::ActionState_Released;
309 } else if (mSelectAction <= ActionState::ActionState_Releasing) {
310 // For a select btn which only has pressed and unpressed status.
311 if (mSelectAction == ActionState::ActionState_Pressed) {
312 DispatchEvent(u"select"_ns, aSession);
314 DispatchEvent(u"selectend"_ns, aSession);
315 mSelectAction = ActionState::ActionState_Released;
319 // Checking squeezestart, squeeze, squeezeend
320 if (buttons.Length() > squeezeIndex) {
321 if (controllerState.squeezeActionStartFrameId >
322 controllerState.squeezeActionStopFrameId) {
323 if (mSqueezeAction == ActionState::ActionState_Released &&
324 controllerState.triggerValue[squeezeIndex] > endThreshold) {
325 DispatchEvent(u"squeezestart"_ns, aSession);
326 mSqueezeAction = ActionState::ActionState_Pressing;
327 } else if (mSqueezeAction == ActionState::ActionState_Pressing &&
328 controllerState.triggerValue[squeezeIndex] >
329 completeThreshold) {
330 mSqueezeAction = ActionState::ActionState_Pressed;
331 } else if (mSqueezeAction == ActionState::ActionState_Pressed &&
332 controllerState.triggerValue[squeezeIndex] < startThreshold) {
333 DispatchEvent(u"squeeze"_ns, aSession);
334 mSqueezeAction = ActionState::ActionState_Releasing;
335 } else if (mSqueezeAction <= ActionState::ActionState_Releasing &&
336 controllerState.triggerValue[squeezeIndex] < endThreshold) {
337 // For a squeeze btn which only has pressed and unpressed status.
338 if (mSqueezeAction == ActionState::ActionState_Pressed) {
339 DispatchEvent(u"squeeze"_ns, aSession);
341 DispatchEvent(u"squeezeend"_ns, aSession);
342 mSqueezeAction = ActionState::ActionState_Released;
344 } else if (mSqueezeAction <= ActionState::ActionState_Releasing) {
345 // For a squeeze btn which only has pressed and unpressed status.
346 if (mSqueezeAction == ActionState::ActionState_Pressed) {
347 DispatchEvent(u"squeeze"_ns, aSession);
349 DispatchEvent(u"squeezeend"_ns, aSession);
350 mSqueezeAction = ActionState::ActionState_Released;
355 int32_t XRInputSource::GetIndex() { return mIndex; }
357 void XRInputSource::DispatchEvent(const nsAString& aEvent,
358 XRSession* aSession) {
359 if (!GetParentObject() || !aSession) {
360 return;
362 // Create a XRFrame for its callbacks
363 RefPtr<XRFrame> frame = new XRFrame(GetParentObject(), aSession);
364 frame->StartInputSourceEvent();
366 XRInputSourceEventInit init;
367 init.mBubbles = false;
368 init.mCancelable = false;
369 init.mFrame = frame;
370 init.mInputSource = this;
372 RefPtr<XRInputSourceEvent> event =
373 XRInputSourceEvent::Constructor(aSession, aEvent, init);
375 event->SetTrusted(true);
376 aSession->DispatchEvent(*event);
377 frame->EndInputSourceEvent();
380 void XRInputSource::CreateGripSpace(
381 XRSession* aSession, const gfx::VRControllerState& controllerState) {
382 MOZ_ASSERT(!mGripSpace);
383 MOZ_ASSERT(aSession && mIndex >= 0 && mGamepad);
384 if (mTargetRayMode == XRTargetRayMode::Tracked_pointer &&
385 controllerState.flags & GamepadCapabilityFlags::Cap_GripSpacePosition) {
386 RefPtr<XRNativeOrigin> nativeOriginGrip = nullptr;
387 nativeOriginGrip = new XRNativeOriginTracker(&controllerState.pose);
388 mGripSpace = new XRInputSpace(aSession->GetParentObject(), aSession,
389 nativeOriginGrip, mIndex);
390 } else {
391 mGripSpace = nullptr;
395 } // namespace dom
396 } // namespace mozilla