/src/mozilla-central/dom/gamepad/GamepadPlatformService.cpp
Line | Count | Source (jump to first uncovered line) |
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 "mozilla/dom/GamepadPlatformService.h" |
8 | | |
9 | | #include "mozilla/dom/GamepadEventChannelParent.h" |
10 | | #include "mozilla/ipc/BackgroundParent.h" |
11 | | #include "mozilla/Mutex.h" |
12 | | #include "mozilla/Unused.h" |
13 | | |
14 | | #include "nsCOMPtr.h" |
15 | | #include "nsHashKeys.h" |
16 | | #include "nsIThread.h" |
17 | | |
18 | | using namespace mozilla::ipc; |
19 | | |
20 | | namespace mozilla { |
21 | | namespace dom { |
22 | | |
23 | | namespace { |
24 | | |
25 | | // This is the singleton instance of GamepadPlatformService, can be called |
26 | | // by both background and monitor thread. |
27 | | StaticRefPtr<GamepadPlatformService> gGamepadPlatformServiceSingleton; |
28 | | |
29 | | } //namepsace |
30 | | |
31 | | GamepadPlatformService::GamepadPlatformService() |
32 | | : mGamepadIndex(0), |
33 | | mMutex("mozilla::dom::GamepadPlatformService") |
34 | 0 | {} |
35 | | |
36 | | GamepadPlatformService::~GamepadPlatformService() |
37 | 0 | { |
38 | 0 | Cleanup(); |
39 | 0 | } |
40 | | |
41 | | // static |
42 | | already_AddRefed<GamepadPlatformService> |
43 | | GamepadPlatformService::GetParentService() |
44 | 0 | { |
45 | 0 | //GamepadPlatformService can only be accessed in parent process |
46 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
47 | 0 | if (!gGamepadPlatformServiceSingleton) { |
48 | 0 | // Only Background Thread can create new GamepadPlatformService instance. |
49 | 0 | if (IsOnBackgroundThread()) { |
50 | 0 | gGamepadPlatformServiceSingleton = new GamepadPlatformService(); |
51 | 0 | } else { |
52 | 0 | return nullptr; |
53 | 0 | } |
54 | 0 | } |
55 | 0 | RefPtr<GamepadPlatformService> service(gGamepadPlatformServiceSingleton); |
56 | 0 | return service.forget(); |
57 | 0 | } |
58 | | |
59 | | template<class T> |
60 | | void |
61 | | GamepadPlatformService::NotifyGamepadChange(uint32_t aIndex, const T& aInfo) |
62 | 0 | { |
63 | 0 | // This method is called by monitor populated in |
64 | 0 | // platform-dependent backends |
65 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
66 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
67 | 0 |
|
68 | 0 | GamepadChangeEventBody body(aInfo); |
69 | 0 | GamepadChangeEvent e(aIndex, GamepadServiceType::Standard, body); |
70 | 0 |
|
71 | 0 | // mChannelParents may be accessed by background thread in the |
72 | 0 | // same time, we use mutex to prevent possible race condtion |
73 | 0 | MutexAutoLock autoLock(mMutex); |
74 | 0 |
|
75 | 0 | // Buffer all events if we have no Channel to dispatch, which |
76 | 0 | // may happen when performing Mochitest. |
77 | 0 | if (mChannelParents.IsEmpty()) { |
78 | 0 | mPendingEvents.AppendElement(e); |
79 | 0 | return; |
80 | 0 | } |
81 | 0 | |
82 | 0 | for(uint32_t i = 0; i < mChannelParents.Length(); ++i) { |
83 | 0 | mChannelParents[i]->DispatchUpdateEvent(e); |
84 | 0 | } |
85 | 0 | } Unexecuted instantiation: void mozilla::dom::GamepadPlatformService::NotifyGamepadChange<mozilla::dom::GamepadAdded>(unsigned int, mozilla::dom::GamepadAdded const&) Unexecuted instantiation: void mozilla::dom::GamepadPlatformService::NotifyGamepadChange<mozilla::dom::GamepadRemoved>(unsigned int, mozilla::dom::GamepadRemoved const&) Unexecuted instantiation: void mozilla::dom::GamepadPlatformService::NotifyGamepadChange<mozilla::dom::GamepadButtonInformation>(unsigned int, mozilla::dom::GamepadButtonInformation const&) Unexecuted instantiation: void mozilla::dom::GamepadPlatformService::NotifyGamepadChange<mozilla::dom::GamepadAxisInformation>(unsigned int, mozilla::dom::GamepadAxisInformation const&) Unexecuted instantiation: void mozilla::dom::GamepadPlatformService::NotifyGamepadChange<mozilla::dom::GamepadPoseInformation>(unsigned int, mozilla::dom::GamepadPoseInformation const&) |
86 | | |
87 | | uint32_t |
88 | | GamepadPlatformService::AddGamepad(const char* aID, |
89 | | GamepadMappingType aMapping, |
90 | | GamepadHand aHand, |
91 | | uint32_t aNumButtons, uint32_t aNumAxes, |
92 | | uint32_t aHaptics) |
93 | 0 | { |
94 | 0 | // This method is called by monitor thread populated in |
95 | 0 | // platform-dependent backends |
96 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
97 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
98 | 0 |
|
99 | 0 | uint32_t index = ++mGamepadIndex; |
100 | 0 |
|
101 | 0 | // Only VR controllers has displayID, we give 0 to the general gamepads. |
102 | 0 | GamepadAdded a(NS_ConvertUTF8toUTF16(nsDependentCString(aID)), |
103 | 0 | aMapping, aHand, 0, aNumButtons, aNumAxes, aHaptics); |
104 | 0 |
|
105 | 0 | NotifyGamepadChange<GamepadAdded>(index, a); |
106 | 0 | return index; |
107 | 0 | } |
108 | | |
109 | | void |
110 | | GamepadPlatformService::RemoveGamepad(uint32_t aIndex) |
111 | 0 | { |
112 | 0 | // This method is called by monitor thread populated in |
113 | 0 | // platform-dependent backends |
114 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
115 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
116 | 0 | GamepadRemoved a; |
117 | 0 | NotifyGamepadChange<GamepadRemoved>(aIndex, a); |
118 | 0 | } |
119 | | |
120 | | void |
121 | | GamepadPlatformService::NewButtonEvent(uint32_t aIndex, uint32_t aButton, |
122 | | bool aPressed, bool aTouched, |
123 | | double aValue) |
124 | 0 | { |
125 | 0 | // This method is called by monitor thread populated in |
126 | 0 | // platform-dependent backends |
127 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
128 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
129 | 0 | GamepadButtonInformation a(aButton, aValue, aPressed, aTouched); |
130 | 0 | NotifyGamepadChange<GamepadButtonInformation>(aIndex, a); |
131 | 0 | } |
132 | | |
133 | | void |
134 | | GamepadPlatformService::NewButtonEvent(uint32_t aIndex, uint32_t aButton, |
135 | | bool aPressed, bool aTouched) |
136 | 0 | { |
137 | 0 | // This method is called by monitor thread populated in |
138 | 0 | // platform-dependent backends |
139 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
140 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
141 | 0 | // When only a digital button is available the value will be synthesized. |
142 | 0 | NewButtonEvent(aIndex, aButton, aPressed, aTouched, aPressed ? 1.0L : 0.0L); |
143 | 0 | } |
144 | | |
145 | | void |
146 | | GamepadPlatformService::NewButtonEvent(uint32_t aIndex, uint32_t aButton, |
147 | | bool aPressed) |
148 | 0 | { |
149 | 0 | // This method is called by monitor thread populated in |
150 | 0 | // platform-dependent backends |
151 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
152 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
153 | 0 | // When only a digital button is available the value will be synthesized. |
154 | 0 | NewButtonEvent(aIndex, aButton, aPressed, aPressed, aPressed ? 1.0L : 0.0L); |
155 | 0 | } |
156 | | |
157 | | void |
158 | | GamepadPlatformService::NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, |
159 | | double aValue) |
160 | 0 | { |
161 | 0 | // This method is called by monitor thread populated in |
162 | 0 | // platform-dependent backends |
163 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
164 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
165 | 0 | GamepadAxisInformation a(aAxis, aValue); |
166 | 0 | NotifyGamepadChange<GamepadAxisInformation>(aIndex, a); |
167 | 0 | } |
168 | | |
169 | | void |
170 | | GamepadPlatformService::NewPoseEvent(uint32_t aIndex, |
171 | | const GamepadPoseState& aPose) |
172 | 0 | { |
173 | 0 | // This method is called by monitor thread populated in |
174 | 0 | // platform-dependent backends |
175 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
176 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
177 | 0 | GamepadPoseInformation a(aPose); |
178 | 0 | NotifyGamepadChange<GamepadPoseInformation>(aIndex, a); |
179 | 0 | } |
180 | | |
181 | | void |
182 | | GamepadPlatformService::ResetGamepadIndexes() |
183 | 0 | { |
184 | 0 | // This method is called by monitor thread populated in |
185 | 0 | // platform-dependent backends |
186 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
187 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
188 | 0 | mGamepadIndex = 0; |
189 | 0 | } |
190 | | |
191 | | void |
192 | | GamepadPlatformService::AddChannelParent(GamepadEventChannelParent* aParent) |
193 | 0 | { |
194 | 0 | // mChannelParents can only be modified once GamepadEventChannelParent |
195 | 0 | // is created or removed in Background thread |
196 | 0 | AssertIsOnBackgroundThread(); |
197 | 0 | MOZ_ASSERT(aParent); |
198 | 0 | MOZ_ASSERT(!mChannelParents.Contains(aParent)); |
199 | 0 |
|
200 | 0 | // We use mutex here to prevent race condition with monitor thread |
201 | 0 | MutexAutoLock autoLock(mMutex); |
202 | 0 | mChannelParents.AppendElement(aParent); |
203 | 0 | FlushPendingEvents(); |
204 | 0 | } |
205 | | |
206 | | void |
207 | | GamepadPlatformService::FlushPendingEvents() |
208 | 0 | { |
209 | 0 | AssertIsOnBackgroundThread(); |
210 | 0 | MOZ_ASSERT(!mChannelParents.IsEmpty()); |
211 | 0 |
|
212 | 0 | if (mPendingEvents.IsEmpty()) { |
213 | 0 | return; |
214 | 0 | } |
215 | 0 | |
216 | 0 | // NOTE: This method must be called with mMutex held because it accesses |
217 | 0 | // mChannelParents. |
218 | 0 | for (uint32_t i=0; i<mChannelParents.Length(); ++i) { |
219 | 0 | for (uint32_t j=0; j<mPendingEvents.Length();++j) { |
220 | 0 | mChannelParents[i]->DispatchUpdateEvent(mPendingEvents[j]); |
221 | 0 | } |
222 | 0 | } |
223 | 0 | mPendingEvents.Clear(); |
224 | 0 | } |
225 | | |
226 | | void |
227 | | GamepadPlatformService::RemoveChannelParent(GamepadEventChannelParent* aParent) |
228 | 0 | { |
229 | 0 | // mChannelParents can only be modified once GamepadEventChannelParent |
230 | 0 | // is created or removed in Background thread |
231 | 0 | AssertIsOnBackgroundThread(); |
232 | 0 | MOZ_ASSERT(aParent); |
233 | 0 | MOZ_ASSERT(mChannelParents.Contains(aParent)); |
234 | 0 |
|
235 | 0 | // We use mutex here to prevent race condition with monitor thread |
236 | 0 | MutexAutoLock autoLock(mMutex); |
237 | 0 | mChannelParents.RemoveElement(aParent); |
238 | 0 | } |
239 | | |
240 | | bool |
241 | | GamepadPlatformService::HasGamepadListeners() |
242 | 0 | { |
243 | 0 | // mChannelParents may be accessed by background thread in the |
244 | 0 | // same time, we use mutex to prevent possible race condtion |
245 | 0 | AssertIsOnBackgroundThread(); |
246 | 0 |
|
247 | 0 | // We use mutex here to prevent race condition with monitor thread |
248 | 0 | MutexAutoLock autoLock(mMutex); |
249 | 0 | for (uint32_t i = 0; i < mChannelParents.Length(); i++) { |
250 | 0 | if(mChannelParents[i]->HasGamepadListener()) { |
251 | 0 | return true; |
252 | 0 | } |
253 | 0 | } |
254 | 0 | return false; |
255 | 0 | } |
256 | | |
257 | | void |
258 | | GamepadPlatformService::MaybeShutdown() |
259 | 0 | { |
260 | 0 | // This method is invoked in MaybeStopGamepadMonitoring when |
261 | 0 | // an IPDL channel is going to be destroyed |
262 | 0 | AssertIsOnBackgroundThread(); |
263 | 0 |
|
264 | 0 | // We have to release gGamepadPlatformServiceSingleton ouside |
265 | 0 | // the mutex as well as making upcoming GetParentService() call |
266 | 0 | // recreate new singleton, so we use this RefPtr to temporarily |
267 | 0 | // hold the reference, postponing the release process until this |
268 | 0 | // method ends. |
269 | 0 | RefPtr<GamepadPlatformService> kungFuDeathGrip; |
270 | 0 |
|
271 | 0 | bool isChannelParentEmpty; |
272 | 0 | { |
273 | 0 | MutexAutoLock autoLock(mMutex); |
274 | 0 | isChannelParentEmpty = mChannelParents.IsEmpty(); |
275 | 0 | if(isChannelParentEmpty) { |
276 | 0 | kungFuDeathGrip = gGamepadPlatformServiceSingleton; |
277 | 0 | gGamepadPlatformServiceSingleton = nullptr; |
278 | 0 | } |
279 | 0 | } |
280 | 0 | } |
281 | | |
282 | | void |
283 | | GamepadPlatformService::Cleanup() |
284 | 0 | { |
285 | 0 | // This method is called when GamepadPlatformService is |
286 | 0 | // successfully distructed in background thread |
287 | 0 | AssertIsOnBackgroundThread(); |
288 | 0 |
|
289 | 0 | MutexAutoLock autoLock(mMutex); |
290 | 0 | mChannelParents.Clear(); |
291 | 0 | } |
292 | | |
293 | | } // namespace dom |
294 | | } // namespace mozilla |