/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "Gamepad.h" #include "nsPIDOMWindow.h" #include "nsTArray.h" #include "nsVariant.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/dom/GamepadBinding.h" namespace mozilla::dom { NS_IMPL_CYCLE_COLLECTING_ADDREF(Gamepad) NS_IMPL_CYCLE_COLLECTING_RELEASE(Gamepad) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Gamepad) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Gamepad, mParent, mButtons, mPose, mHapticActuators, mLightIndicators, mTouchEvents) void Gamepad::UpdateTimestamp() { nsCOMPtr newWindow(do_QueryInterface(mParent)); if (newWindow) { Performance* perf = newWindow->GetPerformance(); if (perf) { mTimestamp = perf->Now(); } } } Gamepad::Gamepad(nsISupports* aParent, const nsAString& aID, int32_t aIndex, GamepadHandle aHandle, GamepadMappingType aMapping, GamepadHand aHand, uint32_t aDisplayID, uint32_t aNumButtons, uint32_t aNumAxes, uint32_t aNumHaptics, uint32_t aNumLightIndicator, uint32_t aNumTouchEvents) : mParent(aParent), mID(aID), mIndex(aIndex), mHandle(aHandle), mDisplayId(aDisplayID), mTouchIdHashValue(0), mMapping(aMapping), mHand(aHand), mConnected(true), mButtons(aNumButtons), mAxes(aNumAxes), mTimestamp(0) { for (unsigned i = 0; i < aNumButtons; i++) { mButtons.InsertElementAt(i, new GamepadButton(mParent)); } mAxes.InsertElementsAt(0, aNumAxes, 0.0f); mPose = new GamepadPose(aParent); for (uint32_t i = 0; i < aNumHaptics; ++i) { mHapticActuators.AppendElement( new GamepadHapticActuator(mParent, mHandle, i)); } for (uint32_t i = 0; i < aNumLightIndicator; ++i) { mLightIndicators.AppendElement( new GamepadLightIndicator(mParent, mHandle, i)); } for (uint32_t i = 0; i < aNumTouchEvents; ++i) { mTouchEvents.AppendElement(new GamepadTouch(mParent)); } // Mapping touchId(0) to touchIdHash(0) by default. mTouchIdHash.Put(0, mTouchIdHashValue); ++mTouchIdHashValue; UpdateTimestamp(); } void Gamepad::SetIndex(int32_t aIndex) { mIndex = aIndex; } void Gamepad::SetConnected(bool aConnected) { mConnected = aConnected; } void Gamepad::SetButton(uint32_t aButton, bool aPressed, bool aTouched, double aValue) { MOZ_ASSERT(aButton < mButtons.Length()); mButtons[aButton]->SetPressed(aPressed); mButtons[aButton]->SetTouched(aTouched); mButtons[aButton]->SetValue(aValue); UpdateTimestamp(); } void Gamepad::SetAxis(uint32_t aAxis, double aValue) { MOZ_ASSERT(aAxis < mAxes.Length()); if (mAxes[aAxis] != aValue) { mAxes[aAxis] = aValue; Gamepad_Binding::ClearCachedAxesValue(this); } UpdateTimestamp(); } void Gamepad::SetPose(const GamepadPoseState& aPose) { mPose->SetPoseState(aPose); UpdateTimestamp(); } void Gamepad::SetLightIndicatorType(uint32_t aLightIndex, GamepadLightIndicatorType aType) { mLightIndicators[aLightIndex]->SetType(aType); UpdateTimestamp(); } void Gamepad::SetTouchEvent(uint32_t aTouchIndex, const GamepadTouchState& aTouch) { if (aTouchIndex >= mTouchEvents.Length()) { MOZ_CRASH("Touch index exceeds the event array."); return; } // Handling cross-origin tracking. GamepadTouchState touchState(aTouch); if (auto hashValue = mTouchIdHash.GetValue(touchState.touchId)) { touchState.touchId = *hashValue; } else { touchState.touchId = mTouchIdHashValue; mTouchIdHash.Put(aTouch.touchId, mTouchIdHashValue); ++mTouchIdHashValue; } mTouchEvents[aTouchIndex]->SetTouchState(touchState); UpdateTimestamp(); } void Gamepad::SetHand(GamepadHand aHand) { mHand = aHand; } void Gamepad::SyncState(Gamepad* aOther) { if (mButtons.Length() != aOther->mButtons.Length() || mAxes.Length() != aOther->mAxes.Length()) { return; } mConnected = aOther->mConnected; for (uint32_t i = 0; i < mButtons.Length(); ++i) { mButtons[i]->SetPressed(aOther->mButtons[i]->Pressed()); mButtons[i]->SetTouched(aOther->mButtons[i]->Touched()); mButtons[i]->SetValue(aOther->mButtons[i]->Value()); } bool changed = false; for (uint32_t i = 0; i < mAxes.Length(); ++i) { changed = changed || (mAxes[i] != aOther->mAxes[i]); mAxes[i] = aOther->mAxes[i]; } if (changed) { Gamepad_Binding::ClearCachedAxesValue(this); } if (StaticPrefs::dom_gamepad_extensions_enabled()) { MOZ_ASSERT(aOther->GetPose()); mPose->SetPoseState(aOther->GetPose()->GetPoseState()); mHand = aOther->Hand(); for (uint32_t i = 0; i < mHapticActuators.Length(); ++i) { mHapticActuators[i]->Set(aOther->mHapticActuators[i]); } if (StaticPrefs::dom_gamepad_extensions_lightindicator()) { for (uint32_t i = 0; i < mLightIndicators.Length(); ++i) { mLightIndicators[i]->Set(aOther->mLightIndicators[i]); } } if (StaticPrefs::dom_gamepad_extensions_multitouch()) { for (uint32_t i = 0; i < mTouchEvents.Length(); ++i) { mTouchEvents[i]->Set(aOther->mTouchEvents[i]); } } } UpdateTimestamp(); } already_AddRefed Gamepad::Clone(nsISupports* aParent) { RefPtr out = new Gamepad(aParent, mID, mIndex, mHandle, mMapping, mHand, mDisplayId, mButtons.Length(), mAxes.Length(), mHapticActuators.Length(), mLightIndicators.Length(), mTouchEvents.Length()); out->SyncState(this); return out.forget(); } /* virtual */ JSObject* Gamepad::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { return Gamepad_Binding::Wrap(aCx, this, aGivenProto); } } // namespace mozilla::dom