1 | /* |
2 | * Copyright (C) 2016 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "UIGamepadProvider.h" |
28 | |
29 | #if ENABLE(GAMEPAD) |
30 | |
31 | #include "GamepadData.h" |
32 | #include "UIGamepad.h" |
33 | #include "WebPageProxy.h" |
34 | #include "WebProcessPool.h" |
35 | #include <WebCore/MockGamepadProvider.h> |
36 | #include <WebCore/PlatformGamepad.h> |
37 | #include <wtf/NeverDestroyed.h> |
38 | |
39 | namespace WebKit { |
40 | using namespace WebCore; |
41 | |
42 | static const Seconds maximumGamepadUpdateInterval { 1_s / 120. }; |
43 | |
44 | UIGamepadProvider& UIGamepadProvider::singleton() |
45 | { |
46 | static NeverDestroyed<UIGamepadProvider> sharedProvider; |
47 | return sharedProvider; |
48 | } |
49 | |
50 | UIGamepadProvider::UIGamepadProvider() |
51 | : m_gamepadSyncTimer(RunLoop::main(), this, &UIGamepadProvider::gamepadSyncTimerFired) |
52 | { |
53 | platformSetDefaultGamepadProvider(); |
54 | } |
55 | |
56 | UIGamepadProvider::~UIGamepadProvider() |
57 | { |
58 | if (!m_processPoolsUsingGamepads.isEmpty()) |
59 | GamepadProvider::singleton().stopMonitoringGamepads(*this); |
60 | } |
61 | |
62 | void UIGamepadProvider::gamepadSyncTimerFired() |
63 | { |
64 | auto webPageProxy = platformWebPageProxyForGamepadInput(); |
65 | if (!webPageProxy || !m_processPoolsUsingGamepads.contains(&webPageProxy->process().processPool())) |
66 | return; |
67 | |
68 | webPageProxy->gamepadActivity(snapshotGamepads(), m_shouldMakeGamepadsVisibleOnSync); |
69 | m_shouldMakeGamepadsVisibleOnSync = false; |
70 | } |
71 | |
72 | void UIGamepadProvider::scheduleGamepadStateSync() |
73 | { |
74 | if (!m_isMonitoringGamepads || m_gamepadSyncTimer.isActive()) |
75 | return; |
76 | |
77 | if (m_gamepads.isEmpty() || m_processPoolsUsingGamepads.isEmpty()) { |
78 | m_gamepadSyncTimer.stop(); |
79 | return; |
80 | } |
81 | |
82 | m_gamepadSyncTimer.startOneShot(maximumGamepadUpdateInterval); |
83 | } |
84 | |
85 | void UIGamepadProvider::setInitialConnectedGamepads(const Vector<PlatformGamepad*>& initialGamepads) |
86 | { |
87 | ASSERT(!m_hasInitialGamepads); |
88 | |
89 | m_gamepads.resize(initialGamepads.size()); |
90 | |
91 | for (auto* gamepad : initialGamepads) { |
92 | if (!gamepad) |
93 | continue; |
94 | m_gamepads[gamepad->index()] = std::make_unique<UIGamepad>(*gamepad); |
95 | } |
96 | |
97 | for (auto& pool : m_processPoolsUsingGamepads) |
98 | pool->setInitialConnectedGamepads(m_gamepads); |
99 | |
100 | m_hasInitialGamepads = true; |
101 | } |
102 | |
103 | void UIGamepadProvider::platformGamepadConnected(PlatformGamepad& gamepad) |
104 | { |
105 | if (m_gamepads.size() <= gamepad.index()) |
106 | m_gamepads.grow(gamepad.index() + 1); |
107 | |
108 | ASSERT(!m_gamepads[gamepad.index()]); |
109 | m_gamepads[gamepad.index()] = std::make_unique<UIGamepad>(gamepad); |
110 | |
111 | scheduleGamepadStateSync(); |
112 | |
113 | for (auto& pool : m_processPoolsUsingGamepads) |
114 | pool->gamepadConnected(*m_gamepads[gamepad.index()]); |
115 | } |
116 | |
117 | void UIGamepadProvider::platformGamepadDisconnected(PlatformGamepad& gamepad) |
118 | { |
119 | ASSERT(gamepad.index() < m_gamepads.size()); |
120 | ASSERT(m_gamepads[gamepad.index()]); |
121 | |
122 | std::unique_ptr<UIGamepad> disconnectedGamepad = WTFMove(m_gamepads[gamepad.index()]); |
123 | |
124 | scheduleGamepadStateSync(); |
125 | |
126 | for (auto& pool : m_processPoolsUsingGamepads) |
127 | pool->gamepadDisconnected(*disconnectedGamepad); |
128 | } |
129 | |
130 | void UIGamepadProvider::platformGamepadInputActivity(bool shouldMakeGamepadsVisible) |
131 | { |
132 | auto platformGamepads = GamepadProvider::singleton().platformGamepads(); |
133 | ASSERT(platformGamepads.size() == m_gamepads.size()); |
134 | |
135 | for (size_t i = 0; i < platformGamepads.size(); ++i) { |
136 | if (!platformGamepads[i]) { |
137 | ASSERT(!m_gamepads[i]); |
138 | continue; |
139 | } |
140 | |
141 | ASSERT(m_gamepads[i]); |
142 | m_gamepads[i]->updateFromPlatformGamepad(*platformGamepads[i]); |
143 | } |
144 | |
145 | if (shouldMakeGamepadsVisible) |
146 | m_shouldMakeGamepadsVisibleOnSync = true; |
147 | |
148 | scheduleGamepadStateSync(); |
149 | } |
150 | |
151 | void UIGamepadProvider::processPoolStartedUsingGamepads(WebProcessPool& pool) |
152 | { |
153 | ASSERT(!m_processPoolsUsingGamepads.contains(&pool)); |
154 | m_processPoolsUsingGamepads.add(&pool); |
155 | |
156 | if (!m_isMonitoringGamepads && platformWebPageProxyForGamepadInput()) |
157 | startMonitoringGamepads(); |
158 | } |
159 | |
160 | void UIGamepadProvider::processPoolStoppedUsingGamepads(WebProcessPool& pool) |
161 | { |
162 | ASSERT(m_processPoolsUsingGamepads.contains(&pool)); |
163 | m_processPoolsUsingGamepads.remove(&pool); |
164 | |
165 | if (m_isMonitoringGamepads && !platformWebPageProxyForGamepadInput()) |
166 | platformStopMonitoringInput(); |
167 | } |
168 | |
169 | void UIGamepadProvider::viewBecameActive(WebPageProxy& page) |
170 | { |
171 | if (!m_processPoolsUsingGamepads.contains(&page.process().processPool())) |
172 | return; |
173 | |
174 | if (!m_isMonitoringGamepads) |
175 | startMonitoringGamepads(); |
176 | |
177 | if (platformWebPageProxyForGamepadInput()) |
178 | platformStartMonitoringInput(); |
179 | } |
180 | |
181 | void UIGamepadProvider::viewBecameInactive(WebPageProxy& page) |
182 | { |
183 | auto pageForGamepadInput = platformWebPageProxyForGamepadInput(); |
184 | if (pageForGamepadInput == &page) |
185 | platformStopMonitoringInput(); |
186 | } |
187 | |
188 | void UIGamepadProvider::startMonitoringGamepads() |
189 | { |
190 | if (m_isMonitoringGamepads) |
191 | return; |
192 | |
193 | m_isMonitoringGamepads = true; |
194 | ASSERT(!m_processPoolsUsingGamepads.isEmpty()); |
195 | GamepadProvider::singleton().startMonitoringGamepads(*this); |
196 | } |
197 | |
198 | void UIGamepadProvider::stopMonitoringGamepads() |
199 | { |
200 | if (!m_isMonitoringGamepads) |
201 | return; |
202 | |
203 | m_isMonitoringGamepads = false; |
204 | |
205 | ASSERT(m_processPoolsUsingGamepads.isEmpty()); |
206 | GamepadProvider::singleton().stopMonitoringGamepads(*this); |
207 | |
208 | m_gamepads.clear(); |
209 | } |
210 | |
211 | Vector<GamepadData> UIGamepadProvider::snapshotGamepads() |
212 | { |
213 | Vector<GamepadData> gamepadDatas; |
214 | gamepadDatas.reserveInitialCapacity(m_gamepads.size()); |
215 | |
216 | for (auto& gamepad : m_gamepads) { |
217 | if (gamepad) |
218 | gamepadDatas.uncheckedAppend(gamepad->condensedGamepadData()); |
219 | else |
220 | gamepadDatas.uncheckedAppend({ }); |
221 | } |
222 | |
223 | return gamepadDatas; |
224 | } |
225 | |
226 | #if !PLATFORM(COCOA) |
227 | |
228 | void UIGamepadProvider::platformSetDefaultGamepadProvider() |
229 | { |
230 | // FIXME: Implement for other platforms |
231 | } |
232 | |
233 | WebPageProxy* UIGamepadProvider::platformWebPageProxyForGamepadInput() |
234 | { |
235 | // FIXME: Implement for other platforms |
236 | return nullptr; |
237 | } |
238 | |
239 | void UIGamepadProvider::platformStopMonitoringInput() |
240 | { |
241 | } |
242 | |
243 | void UIGamepadProvider::platformStartMonitoringInput() |
244 | { |
245 | } |
246 | |
247 | #endif // !PLATFORM(MAC) |
248 | |
249 | } |
250 | |
251 | #endif // ENABLE(GAMEPAD) |
252 | |