From 91ad9bdf58784bab78df1d010d25e5cc9a4aea7a Mon Sep 17 00:00:00 2001 From: Daosheng Mu Date: Tue, 7 Apr 2020 22:55:41 +0000 Subject: [PATCH] Bug 1617023 - Part 1: Integrating Gamepad with XRInputSource. r=kip Differential Revision: https://phabricator.services.mozilla.com/D67431 --HG-- extra : moz-landing-system : lando --- dom/gamepad/GamepadManager.cpp | 41 +++++++++++++------------ dom/gamepad/GamepadManager.h | 9 +++--- dom/vr/XRInputSource.cpp | 56 ++++++++++++++++++++++++++++++++-- dom/vr/XRInputSource.h | 3 +- dom/vr/XRInputSourceArray.cpp | 14 +++++++-- dom/vr/XRSession.cpp | 3 ++ gfx/vr/VRDisplayClient.cpp | 16 ++++++++-- 7 files changed, 110 insertions(+), 32 deletions(-) diff --git a/dom/gamepad/GamepadManager.cpp b/dom/gamepad/GamepadManager.cpp index a6d194df7260..7827c0496d1d 100644 --- a/dom/gamepad/GamepadManager.cpp +++ b/dom/gamepad/GamepadManager.cpp @@ -50,6 +50,27 @@ const uint32_t VR_GAMEPAD_IDX_OFFSET = 0x01 << 16; NS_IMPL_ISUPPORTS(GamepadManager, nsIObserver) +/*static*/ +uint32_t GamepadManager::GetGamepadIndexWithServiceType( + uint32_t aIndex, GamepadServiceType aServiceType) { + uint32_t newIndex = 0; + + switch (aServiceType) { + case GamepadServiceType::Standard: + MOZ_ASSERT(aIndex <= VR_GAMEPAD_IDX_OFFSET); + newIndex = aIndex; + break; + case GamepadServiceType::VR: + newIndex = aIndex + VR_GAMEPAD_IDX_OFFSET; + break; + default: + MOZ_ASSERT(false); + break; + } + + return newIndex; +} + GamepadManager::GamepadManager() : mEnabled(false), mNonstandardEventsEnabled(false), @@ -195,26 +216,6 @@ already_AddRefed GamepadManager::GetGamepad( return GetGamepad(GetGamepadIndexWithServiceType(aGamepadId, aServiceType)); } -uint32_t GamepadManager::GetGamepadIndexWithServiceType( - uint32_t aIndex, GamepadServiceType aServiceType) const { - uint32_t newIndex = 0; - - switch (aServiceType) { - case GamepadServiceType::Standard: - MOZ_ASSERT(aIndex <= VR_GAMEPAD_IDX_OFFSET); - newIndex = aIndex; - break; - case GamepadServiceType::VR: - newIndex = aIndex + VR_GAMEPAD_IDX_OFFSET; - break; - default: - MOZ_ASSERT(false); - break; - } - - return newIndex; -} - void GamepadManager::AddGamepad(uint32_t aIndex, const nsAString& aId, GamepadMappingType aMapping, GamepadHand aHand, GamepadServiceType aServiceType, diff --git a/dom/gamepad/GamepadManager.h b/dom/gamepad/GamepadManager.h index 942e8f9e90de..f3535ff5b062 100644 --- a/dom/gamepad/GamepadManager.h +++ b/dom/gamepad/GamepadManager.h @@ -35,6 +35,11 @@ class GamepadManager final : public nsIObserver { // Get the singleton service static already_AddRefed GetService(); + // Our gamepad index has VR_GAMEPAD_IDX_OFFSET while GamepadChannelType + // is from VRManager. + static uint32_t GetGamepadIndexWithServiceType( + uint32_t aIndex, GamepadServiceType aServiceType); + void BeginShutdown(); void StopMonitoring(); @@ -139,10 +144,6 @@ class GamepadManager final : public nsIObserver { // Indicate that a window has received data from a gamepad. void SetWindowHasSeenGamepad(nsGlobalWindowInner* aWindow, uint32_t aIndex, bool aHasSeen = true); - // Our gamepad index has VR_GAMEPAD_IDX_OFFSET while GamepadChannelType - // is from VRManager. - uint32_t GetGamepadIndexWithServiceType( - uint32_t aIndex, GamepadServiceType aServiceType) const; // Gamepads connected to the system. Copies of these are handed out // to each window. diff --git a/dom/vr/XRInputSource.cpp b/dom/vr/XRInputSource.cpp index 061feb048a8b..5471579cc4dc 100644 --- a/dom/vr/XRInputSource.cpp +++ b/dom/vr/XRInputSource.cpp @@ -8,6 +8,9 @@ #include "XRNativeOriginViewer.h" #include "XRNativeOriginTracker.h" +#include "mozilla/dom/Gamepad.h" +#include "mozilla/dom/GamepadManager.h" + namespace mozilla { namespace dom { @@ -102,7 +105,17 @@ nsTArray GetInputSourceProfile(VRControllerType aType) { return profile; } -XRInputSource::XRInputSource(nsISupports* aParent) : mParent(aParent) {} +XRInputSource::XRInputSource(nsISupports* aParent) + : mParent(aParent), + mGamepad(nullptr), + mIndex(-1) {} + +XRInputSource::~XRInputSource() { + mTargetRaySpace = nullptr; + mGripSpace = nullptr; + mGamepad = nullptr; + mozilla::DropJSObjects(this); +} JSObject* XRInputSource::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { @@ -130,8 +143,7 @@ void XRInputSource::GetProfiles(nsTArray& aResult) { } Gamepad* XRInputSource::GetGamepad() { - // TODO (Bug 1617023): Implement Gamepad for XRInputSource. - return nullptr; + return mGamepad; } void XRInputSource::Setup(XRSession* aSession, uint32_t aIndex) { @@ -186,13 +198,51 @@ void XRInputSource::Setup(XRSession* aSession, uint32_t aIndex) { } mTargetRaySpace = new XRSpace(aSession->GetParentObject(), aSession, nativeOriginTargetRay); mGripSpace = new XRSpace(aSession->GetParentObject(), aSession, nativeOriginGrip); + const uint32_t gamepadId = displayInfo.mDisplayID * kVRControllerMaxCount + aIndex; + const uint32_t hashKey = GamepadManager::GetGamepadIndexWithServiceType(gamepadId, GamepadServiceType::VR); + mGamepad = new Gamepad(mParent, NS_ConvertASCIItoUTF16(""), -1, hashKey, GamepadMappingType::Xr_standard, + controllerState.hand, displayInfo.mDisplayID, controllerState.numButtons, controllerState.numAxes, + controllerState.numHaptics, 0, 0); mIndex = aIndex; } void XRInputSource::SetGamepadIsConnected(bool aConnected) { + mGamepad->SetConnected(aConnected); } void XRInputSource::Update(XRSession* aSession) { + MOZ_ASSERT(aSession && mIndex >= 0 && mGamepad); + + gfx::VRDisplayClient* displayClient = aSession->GetDisplayClient(); + if (!displayClient) { + MOZ_ASSERT(displayClient); + return; + } + const gfx::VRDisplayInfo& displayInfo = displayClient->GetDisplayInfo(); + const gfx::VRControllerState& controllerState = displayInfo.mControllerState[mIndex]; + MOZ_ASSERT(controllerState.controllerName[0] != '\0'); + + // Update button values. + nsTArray> buttons; + mGamepad->GetButtons(buttons); + for (uint32_t i = 0; i < controllerState.numButtons; ++i) { + const bool pressed = controllerState.buttonPressed & (1ULL << i); + const bool touched = controllerState.buttonTouched & (1ULL << i); + + if (buttons[i]->Pressed() != pressed || buttons[i]->Touched() != touched || + buttons[i]->Value() != controllerState.triggerValue[i]) { + mGamepad->SetButton(i, pressed, touched, controllerState.triggerValue[i]); + } + } + // Update axis values. + nsTArray axes; + mGamepad->GetAxes(axes); + for (uint32_t i = 0; i < controllerState.numAxes; ++i) { + if (axes[i] != controllerState.axisValue[i]) { + mGamepad->SetAxis(i, controllerState.axisValue[i]); + } + } + } } int32_t XRInputSource::GetIndex() { diff --git a/dom/vr/XRInputSource.h b/dom/vr/XRInputSource.h index 8a1e5d667581..8fe65ef3e0c7 100644 --- a/dom/vr/XRInputSource.h +++ b/dom/vr/XRInputSource.h @@ -49,7 +49,7 @@ class XRInputSource final : public nsWrapperCache { int32_t GetIndex(); protected: - virtual ~XRInputSource() = default; + virtual ~XRInputSource(); nsCOMPtr mParent; @@ -60,6 +60,7 @@ class XRInputSource final : public nsWrapperCache { RefPtr mTargetRaySpace; RefPtr mGripSpace; + RefPtr mGamepad; int32_t mIndex; }; diff --git a/dom/vr/XRInputSourceArray.cpp b/dom/vr/XRInputSourceArray.cpp index 2a8a8a84c658..a613188b32f5 100644 --- a/dom/vr/XRInputSourceArray.cpp +++ b/dom/vr/XRInputSourceArray.cpp @@ -50,16 +50,26 @@ void XRInputSourceArray::Update(XRSession* aSession) { } } // Checking if it is added before. - if (!found) { + if (!found && + (controllerState.numButtons > 0 || controllerState.numAxes > 0)) { inputSource = new XRInputSource(mParent); inputSource->Setup(aSession, i); mInputSources.AppendElement(inputSource); } // If added, updating the current controller states. - inputSource->Update(aSession); + if (inputSource) { + inputSource->Update(aSession); + } } } +void XRInputSourceArray::Clear() { + for (auto& input: mInputSources) { + input->SetGamepadIsConnected(false); + } + mInputSources.Clear(); +} + uint32_t XRInputSourceArray::Length() { return mInputSources.Length(); } XRInputSource* XRInputSourceArray::IndexedGetter(uint32_t aIndex, diff --git a/dom/vr/XRSession.cpp b/dom/vr/XRSession.cpp index 0d5785e33f25..40a139374899 100644 --- a/dom/vr/XRSession.cpp +++ b/dom/vr/XRSession.cpp @@ -396,6 +396,9 @@ void XRSession::Shutdown() { } void XRSession::ExitPresentInternal() { + if (mInputSources) { + mInputSources->Clear(); + } if (mDisplayClient) { mDisplayClient->SessionEnded(this); } diff --git a/gfx/vr/VRDisplayClient.cpp b/gfx/vr/VRDisplayClient.cpp index b5c641d67f00..1d294262c865 100644 --- a/gfx/vr/VRDisplayClient.cpp +++ b/gfx/vr/VRDisplayClient.cpp @@ -15,6 +15,7 @@ #include "mozilla/dom/GamepadManager.h" #include "mozilla/dom/Gamepad.h" #include "mozilla/dom/XRSession.h" +#include "mozilla/dom/XRInputSourceArray.h" #include "mozilla/Preferences.h" #include "mozilla/Unused.h" #include "mozilla/dom/WebXRBinding.h" @@ -140,8 +141,19 @@ void VRDisplayClient::FireEvents() { StartFrame(); } - // We only call FireGamepadEvents() in WebVR instead of WebXR - FireGamepadEvents(); + // In WebXR spec, Gamepad instances returned by an XRInputSource's gamepad attribute + // MUST NOT be included in the array returned by navigator.getGamepads(). + if (!StaticPrefs::dom_vr_webxr_enabled()) { // Checking mWebVR? + FireGamepadEvents(); + } else { + // Update controller states into XRInputSourceArray. + for (auto& session : mSessions) { + dom::XRInputSourceArray* inputs = session->InputSources(); + if (inputs) { + inputs->Update(session); + } + } + } } void VRDisplayClient::GamepadMappingForWebVR(