Gamepad.cpp (6782B)
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/. */ 6 7 #include "Gamepad.h" 8 9 #include "mozilla/StaticPrefs_dom.h" 10 #include "mozilla/dom/GamepadBinding.h" 11 #include "nsPIDOMWindow.h" 12 #include "nsTArray.h" 13 #include "nsVariant.h" 14 15 namespace mozilla::dom { 16 17 NS_IMPL_CYCLE_COLLECTING_ADDREF(Gamepad) 18 NS_IMPL_CYCLE_COLLECTING_RELEASE(Gamepad) 19 20 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Gamepad) 21 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 22 NS_INTERFACE_MAP_ENTRY(nsISupports) 23 NS_INTERFACE_MAP_END 24 25 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Gamepad, mParent, mButtons, mPose, 26 mHapticActuators, mLightIndicators, 27 mTouchEvents) 28 29 void Gamepad::UpdateTimestamp() { 30 nsCOMPtr<nsPIDOMWindowInner> newWindow(do_QueryInterface(mParent)); 31 if (newWindow) { 32 Performance* perf = newWindow->GetPerformance(); 33 if (perf) { 34 mTimestamp = perf->Now(); 35 } 36 } 37 } 38 39 Gamepad::Gamepad(nsISupports* aParent, const nsAString& aID, int32_t aIndex, 40 GamepadHandle aHandle, GamepadMappingType aMapping, 41 GamepadHand aHand, uint32_t aNumButtons, uint32_t aNumAxes, 42 uint32_t aNumHaptics, uint32_t aNumLightIndicator, 43 uint32_t aNumTouchEvents) 44 : mParent(aParent), 45 mID(aID), 46 mIndex(aIndex), 47 mHandle(aHandle), 48 mTouchIdHashValue(0), 49 mMapping(aMapping), 50 mHand(aHand), 51 mConnected(true), 52 mButtons(aNumButtons), 53 mAxes(aNumAxes), 54 mTimestamp(0) { 55 for (unsigned i = 0; i < aNumButtons; i++) { 56 mButtons.InsertElementAt(i, new GamepadButton(mParent)); 57 } 58 mAxes.InsertElementsAt(0, aNumAxes, 0.0f); 59 mPose = new GamepadPose(aParent); 60 for (uint32_t i = 0; i < aNumHaptics; ++i) { 61 mHapticActuators.AppendElement( 62 new GamepadHapticActuator(mParent, mHandle, i)); 63 } 64 for (uint32_t i = 0; i < aNumLightIndicator; ++i) { 65 mLightIndicators.AppendElement( 66 new GamepadLightIndicator(mParent, mHandle, i)); 67 } 68 for (uint32_t i = 0; i < aNumTouchEvents; ++i) { 69 mTouchEvents.AppendElement(new GamepadTouch(mParent)); 70 } 71 72 // Mapping touchId(0) to touchIdHash(0) by default. 73 mTouchIdHash.InsertOrUpdate(0, mTouchIdHashValue); 74 ++mTouchIdHashValue; 75 UpdateTimestamp(); 76 } 77 78 void Gamepad::SetIndex(int32_t aIndex) { mIndex = aIndex; } 79 80 void Gamepad::SetConnected(bool aConnected) { mConnected = aConnected; } 81 82 void Gamepad::SetButton(uint32_t aButton, bool aPressed, bool aTouched, 83 double aValue) { 84 // Until we fix the synchronization errors in Bug 1682554, this can be 85 // called with a stale index that might overflow. In such a case, we silently 86 // ignore it. 87 if (aButton >= mButtons.Length()) { 88 return; 89 } 90 mButtons[aButton]->SetPressed(aPressed); 91 mButtons[aButton]->SetTouched(aTouched); 92 mButtons[aButton]->SetValue(aValue); 93 UpdateTimestamp(); 94 } 95 96 void Gamepad::SetAxis(uint32_t aAxis, double aValue) { 97 // Until we fix the synchronization errors in Bug 1682554, this can be 98 // called with a stale index that might overflow. In such a case, we silently 99 // ignore it. 100 if (aAxis >= mAxes.Length()) { 101 return; 102 } 103 104 if (mAxes[aAxis] != aValue) { 105 mAxes[aAxis] = aValue; 106 Gamepad_Binding::ClearCachedAxesValue(this); 107 } 108 UpdateTimestamp(); 109 } 110 111 void Gamepad::SetPose(const GamepadPoseState& aPose) { 112 mPose->SetPoseState(aPose); 113 UpdateTimestamp(); 114 } 115 116 void Gamepad::SetLightIndicatorType(uint32_t aLightIndex, 117 GamepadLightIndicatorType aType) { 118 // Until we fix the synchronization errors in Bug 1682554, this can be 119 // called with a stale index that might overflow. In such a case, we silently 120 // ignore it. 121 if (aLightIndex >= mLightIndicators.Length()) { 122 return; 123 } 124 125 mLightIndicators[aLightIndex]->SetType(aType); 126 UpdateTimestamp(); 127 } 128 129 void Gamepad::SetTouchEvent(uint32_t aTouchIndex, 130 const GamepadTouchState& aTouch) { 131 // Until we fix the synchronization errors in Bug 1682554, this can be 132 // called with a stale index that might overflow. In such a case, we silently 133 // ignore it. 134 if (aTouchIndex >= mTouchEvents.Length()) { 135 return; 136 } 137 138 // Handling cross-origin tracking. 139 GamepadTouchState touchState(aTouch); 140 touchState.touchId = mTouchIdHash.LookupOrInsertWith( 141 touchState.touchId, [&] { return mTouchIdHashValue++; }); 142 mTouchEvents[aTouchIndex]->SetTouchState(touchState); 143 UpdateTimestamp(); 144 } 145 146 void Gamepad::SetHand(GamepadHand aHand) { mHand = aHand; } 147 148 void Gamepad::SyncState(Gamepad* aOther) { 149 if (mButtons.Length() != aOther->mButtons.Length() || 150 mAxes.Length() != aOther->mAxes.Length()) { 151 return; 152 } 153 154 mConnected = aOther->mConnected; 155 for (uint32_t i = 0; i < mButtons.Length(); ++i) { 156 mButtons[i]->SetPressed(aOther->mButtons[i]->Pressed()); 157 mButtons[i]->SetTouched(aOther->mButtons[i]->Touched()); 158 mButtons[i]->SetValue(aOther->mButtons[i]->Value()); 159 } 160 161 bool changed = false; 162 for (uint32_t i = 0; i < mAxes.Length(); ++i) { 163 changed = changed || (mAxes[i] != aOther->mAxes[i]); 164 mAxes[i] = aOther->mAxes[i]; 165 } 166 if (changed) { 167 Gamepad_Binding::ClearCachedAxesValue(this); 168 } 169 170 if (StaticPrefs::dom_gamepad_extensions_enabled()) { 171 MOZ_ASSERT(aOther->GetPose()); 172 mPose->SetPoseState(aOther->GetPose()->GetPoseState()); 173 mHand = aOther->Hand(); 174 for (uint32_t i = 0; i < mHapticActuators.Length(); ++i) { 175 mHapticActuators[i]->Set(aOther->mHapticActuators[i]); 176 } 177 178 if (StaticPrefs::dom_gamepad_extensions_lightindicator()) { 179 for (uint32_t i = 0; i < mLightIndicators.Length(); ++i) { 180 mLightIndicators[i]->Set(aOther->mLightIndicators[i]); 181 } 182 } 183 if (StaticPrefs::dom_gamepad_extensions_multitouch()) { 184 for (uint32_t i = 0; i < mTouchEvents.Length(); ++i) { 185 mTouchEvents[i]->Set(aOther->mTouchEvents[i]); 186 } 187 } 188 } 189 190 UpdateTimestamp(); 191 } 192 193 already_AddRefed<Gamepad> Gamepad::Clone(nsISupports* aParent) { 194 RefPtr<Gamepad> out = 195 new Gamepad(aParent, mID, mIndex, mHandle, mMapping, mHand, 196 mButtons.Length(), mAxes.Length(), mHapticActuators.Length(), 197 mLightIndicators.Length(), mTouchEvents.Length()); 198 out->SyncState(this); 199 return out.forget(); 200 } 201 202 /* virtual */ 203 JSObject* Gamepad::WrapObject(JSContext* aCx, 204 JS::Handle<JSObject*> aGivenProto) { 205 return Gamepad_Binding::Wrap(aCx, this, aGivenProto); 206 } 207 208 } // namespace mozilla::dom