OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "device/gamepad/gamepad_platform_data_fetcher_win.h" | 5 #include "device/gamepad/gamepad_platform_data_fetcher_win.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <string.h> | 8 #include <string.h> |
9 | 9 |
| 10 #include "base/bind.h" |
| 11 #include "base/bind_helpers.h" |
| 12 #include "base/single_thread_task_runner.h" |
10 #include "base/strings/stringprintf.h" | 13 #include "base/strings/stringprintf.h" |
11 #include "base/trace_event/trace_event.h" | 14 #include "base/trace_event/trace_event.h" |
12 #include "base/win/windows_version.h" | 15 #include "base/win/windows_version.h" |
13 | 16 |
14 namespace device { | 17 namespace device { |
15 | 18 |
16 using namespace blink; | 19 using namespace blink; |
17 | 20 |
18 namespace { | 21 namespace { |
19 | 22 |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
73 return FILE_PATH_LITERAL("xinput9_1_0.dll"); | 76 return FILE_PATH_LITERAL("xinput9_1_0.dll"); |
74 } else { | 77 } else { |
75 NOTREACHED(); | 78 NOTREACHED(); |
76 return nullptr; | 79 return nullptr; |
77 } | 80 } |
78 } | 81 } |
79 | 82 |
80 } // namespace | 83 } // namespace |
81 | 84 |
82 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() | 85 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() |
83 : xinput_dll_(base::FilePath(XInputDllFileName())), | 86 : xinput_available_(false) {} |
84 xinput_available_(GetXInputDllFunctions()) { | |
85 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
86 platform_pad_state_[i].status = DISCONNECTED; | |
87 pad_state_[i].mapper = NULL; | |
88 pad_state_[i].axis_mask = 0; | |
89 pad_state_[i].button_mask = 0; | |
90 } | |
91 | 87 |
92 raw_input_fetcher_.reset(new RawInputDataFetcher()); | 88 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { |
93 raw_input_fetcher_->StartMonitor(); | |
94 } | 89 } |
95 | 90 |
96 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { | 91 GamepadSource GamepadPlatformDataFetcherWin::source() { |
97 raw_input_fetcher_->StopMonitor(); | 92 return Factory::static_source(); |
98 } | 93 } |
99 | 94 |
100 int GamepadPlatformDataFetcherWin::FirstAvailableGamepadId() const { | 95 void GamepadPlatformDataFetcherWin::OnAddedToProvider() { |
101 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | 96 xinput_dll_.Reset( |
102 if (platform_pad_state_[i].status == DISCONNECTED) | 97 base::LoadNativeLibrary(base::FilePath(XInputDllFileName()), nullptr)); |
103 return i; | 98 xinput_available_ = GetXInputDllFunctions(); |
104 } | |
105 return -1; | |
106 } | |
107 | |
108 bool GamepadPlatformDataFetcherWin::HasXInputGamepad(int index) const { | |
109 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
110 if (platform_pad_state_[i].status == XINPUT_CONNECTED && | |
111 platform_pad_state_[i].xinput_index == index) | |
112 return true; | |
113 } | |
114 return false; | |
115 } | |
116 | |
117 bool GamepadPlatformDataFetcherWin::HasRawInputGamepad( | |
118 const HANDLE handle) const { | |
119 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
120 if (platform_pad_state_[i].status == RAWINPUT_CONNECTED && | |
121 platform_pad_state_[i].raw_input_handle == handle) | |
122 return true; | |
123 } | |
124 return false; | |
125 } | 99 } |
126 | 100 |
127 void GamepadPlatformDataFetcherWin::EnumerateDevices() { | 101 void GamepadPlatformDataFetcherWin::EnumerateDevices() { |
128 TRACE_EVENT0("GAMEPAD", "EnumerateDevices"); | 102 TRACE_EVENT0("GAMEPAD", "EnumerateDevices"); |
129 | 103 |
130 // Mark all disconnected pads DISCONNECTED. | 104 if (xinput_available_) { |
131 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | 105 for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) { |
132 if (!pad_state_[i].data.connected) | 106 // Check to see if the xinput device is connected |
133 platform_pad_state_[i].status = DISCONNECTED; | 107 XINPUT_CAPABILITIES caps; |
134 } | 108 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps); |
| 109 xinuput_connected_[i] = (res == ERROR_SUCCESS); |
| 110 if (!xinuput_connected_[i]) |
| 111 continue; |
135 | 112 |
136 for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) { | 113 PadState* state = GetPadState(i); |
137 if (HasXInputGamepad(i)) | 114 if (!state) |
138 continue; | 115 continue; // No slot available for this gamepad. |
139 int pad_index = FirstAvailableGamepadId(); | |
140 if (pad_index == -1) | |
141 return; // We can't add any more gamepads. | |
142 WebGamepad& pad = pad_state_[pad_index].data; | |
143 if (xinput_available_ && GetXInputPadConnectivity(i, &pad)) { | |
144 platform_pad_state_[pad_index].status = XINPUT_CONNECTED; | |
145 platform_pad_state_[pad_index].xinput_index = i; | |
146 pad_state_[pad_index].mapper = NULL; | |
147 pad_state_[pad_index].axis_mask = 0; | |
148 pad_state_[pad_index].button_mask = 0; | |
149 } | |
150 } | |
151 | 116 |
152 if (raw_input_fetcher_->Available()) { | 117 WebGamepad& pad = state->data; |
153 std::vector<RawGamepadInfo*> raw_inputs = | |
154 raw_input_fetcher_->EnumerateDevices(); | |
155 for (size_t i = 0; i < raw_inputs.size(); ++i) { | |
156 RawGamepadInfo* gamepad = raw_inputs[i]; | |
157 if (gamepad->buttons_length == 0 && gamepad->axes_length == 0) | |
158 continue; | |
159 if (HasRawInputGamepad(gamepad->handle)) | |
160 continue; | |
161 int pad_index = FirstAvailableGamepadId(); | |
162 if (pad_index == -1) | |
163 return; | |
164 WebGamepad& pad = pad_state_[pad_index].data; | |
165 pad.connected = true; | |
166 PadState& state = pad_state_[pad_index]; | |
167 PlatformPadState& platform_state = platform_pad_state_[pad_index]; | |
168 platform_state.status = RAWINPUT_CONNECTED; | |
169 platform_state.raw_input_handle = gamepad->handle; | |
170 | 118 |
171 std::string vendor = base::StringPrintf("%04x", gamepad->vendor_id); | 119 if (state->active_state == GAMEPAD_NEWLY_ACTIVE) { |
172 std::string product = base::StringPrintf("%04x", gamepad->product_id); | 120 // This is the first time we've seen this device, so do some one-time |
173 state.mapper = GetGamepadStandardMappingFunction(vendor, product); | 121 // initialization |
174 state.axis_mask = 0; | 122 pad.connected = true; |
175 state.button_mask = 0; | 123 swprintf(pad.id, WebGamepad::idLengthCap, |
176 | 124 L"Xbox 360 Controller (XInput STANDARD %ls)", |
177 swprintf(pad.id, WebGamepad::idLengthCap, | 125 GamepadSubTypeName(caps.SubType)); |
178 L"%ls (%lsVendor: %04x Product: %04x)", gamepad->id, | |
179 state.mapper ? L"STANDARD GAMEPAD " : L"", gamepad->vendor_id, | |
180 gamepad->product_id); | |
181 | |
182 if (state.mapper) | |
183 swprintf(pad.mapping, WebGamepad::mappingLengthCap, L"standard"); | 126 swprintf(pad.mapping, WebGamepad::mappingLengthCap, L"standard"); |
184 else | 127 } |
185 pad.mapping[0] = 0; | |
186 } | 128 } |
187 } | 129 } |
188 } | 130 } |
189 | 131 |
190 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads, | 132 void GamepadPlatformDataFetcherWin::GetGamepadData(bool devices_changed_hint) { |
191 bool devices_changed_hint) { | |
192 TRACE_EVENT0("GAMEPAD", "GetGamepadData"); | 133 TRACE_EVENT0("GAMEPAD", "GetGamepadData"); |
193 | 134 |
194 if (!xinput_available_ && !raw_input_fetcher_->Available()) { | 135 if (!xinput_available_) |
195 pads->length = 0; | |
196 return; | 136 return; |
197 } | |
198 | 137 |
199 // A note on XInput devices: | 138 // A note on XInput devices: |
200 // If we got notification that system devices have been updated, then | 139 // If we got notification that system devices have been updated, then |
201 // run GetCapabilities to update the connected status and the device | 140 // run GetCapabilities to update the connected status and the device |
202 // identifier. It can be slow to do to both GetCapabilities and | 141 // identifier. It can be slow to do to both GetCapabilities and |
203 // GetState on unconnected devices, so we want to avoid a 2-5ms pause | 142 // GetState on unconnected devices, so we want to avoid a 2-5ms pause |
204 // here by only doing this when the devices are updated (despite | 143 // here by only doing this when the devices are updated (despite |
205 // documentation claiming it's OK to call it any time). | 144 // documentation claiming it's OK to call it any time). |
206 if (devices_changed_hint) | 145 if (devices_changed_hint) |
207 EnumerateDevices(); | 146 EnumerateDevices(); |
208 | 147 |
209 pads->length = 0; | 148 for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) { |
210 | 149 if (xinuput_connected_[i]) |
211 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | 150 GetXInputPadData(i); |
212 // We rely on device_changed and GetCapabilities to tell us that | |
213 // something's been connected, but we will mark as disconnected if | |
214 // Get___PadState returns that we've lost the pad. | |
215 if (!pad_state_[i].data.connected) | |
216 continue; | |
217 | |
218 if (platform_pad_state_[i].status == XINPUT_CONNECTED) | |
219 GetXInputPadData(i, &pad_state_[i].data); | |
220 else if (platform_pad_state_[i].status == RAWINPUT_CONNECTED) | |
221 GetRawInputPadData(i, &pad_state_[i].data); | |
222 | |
223 MapAndSanitizeGamepadData(&pad_state_[i], &pads->items[i]); | |
224 | |
225 if (pads->items[i].connected) | |
226 pads->length++; | |
227 } | 151 } |
228 } | 152 } |
229 | 153 |
230 void GamepadPlatformDataFetcherWin::PauseHint(bool pause) { | 154 void GamepadPlatformDataFetcherWin::GetXInputPadData(int i) { |
231 if (pause) | 155 PadState* pad_state = provider()->GetPadState(GAMEPAD_SOURCE_WIN_XINPUT, i); |
232 raw_input_fetcher_->StopMonitor(); | 156 if (!pad_state) |
233 else | 157 return; |
234 raw_input_fetcher_->StartMonitor(); | |
235 } | |
236 | 158 |
237 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity( | 159 WebGamepad& pad = pad_state->data; |
238 int i, | |
239 WebGamepad* pad) const { | |
240 DCHECK(pad); | |
241 TRACE_EVENT1("GAMEPAD", "GetXInputPadConnectivity", "id", i); | |
242 XINPUT_CAPABILITIES caps; | |
243 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps); | |
244 if (res == ERROR_DEVICE_NOT_CONNECTED) { | |
245 pad->connected = false; | |
246 return false; | |
247 } else { | |
248 pad->connected = true; | |
249 swprintf(pad->id, WebGamepad::idLengthCap, | |
250 L"Xbox 360 Controller (XInput STANDARD %ls)", | |
251 GamepadSubTypeName(caps.SubType)); | |
252 swprintf(pad->mapping, WebGamepad::mappingLengthCap, L"standard"); | |
253 return true; | |
254 } | |
255 } | |
256 | 160 |
257 void GamepadPlatformDataFetcherWin::GetXInputPadData(int i, WebGamepad* pad) { | |
258 XINPUT_STATE state; | 161 XINPUT_STATE state; |
259 memset(&state, 0, sizeof(XINPUT_STATE)); | 162 memset(&state, 0, sizeof(XINPUT_STATE)); |
260 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); | 163 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); |
261 DWORD dwResult = | 164 DWORD dwResult = xinput_get_state_(i, &state); |
262 xinput_get_state_(platform_pad_state_[i].xinput_index, &state); | |
263 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); | 165 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); |
264 | 166 |
265 if (dwResult == ERROR_SUCCESS) { | 167 if (dwResult == ERROR_SUCCESS) { |
266 pad->timestamp = state.dwPacketNumber; | 168 pad.timestamp = state.dwPacketNumber; |
267 pad->buttonsLength = 0; | 169 pad.buttonsLength = 0; |
268 WORD val = state.Gamepad.wButtons; | 170 WORD val = state.Gamepad.wButtons; |
269 #define ADD(b) \ | 171 #define ADD(b) \ |
270 pad->buttons[pad->buttonsLength].pressed = (val & (b)) != 0; \ | 172 pad.buttons[pad.buttonsLength].pressed = (val & (b)) != 0; \ |
271 pad->buttons[pad->buttonsLength++].value = ((val & (b)) ? 1.f : 0.f); | 173 pad.buttons[pad.buttonsLength++].value = ((val & (b)) ? 1.f : 0.f); |
272 ADD(XINPUT_GAMEPAD_A); | 174 ADD(XINPUT_GAMEPAD_A); |
273 ADD(XINPUT_GAMEPAD_B); | 175 ADD(XINPUT_GAMEPAD_B); |
274 ADD(XINPUT_GAMEPAD_X); | 176 ADD(XINPUT_GAMEPAD_X); |
275 ADD(XINPUT_GAMEPAD_Y); | 177 ADD(XINPUT_GAMEPAD_Y); |
276 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); | 178 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); |
277 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); | 179 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); |
278 | 180 |
279 pad->buttons[pad->buttonsLength].pressed = | 181 pad.buttons[pad.buttonsLength].pressed = |
280 state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD; | 182 state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD; |
281 pad->buttons[pad->buttonsLength++].value = | 183 pad.buttons[pad.buttonsLength++].value = state.Gamepad.bLeftTrigger / 255.f; |
282 state.Gamepad.bLeftTrigger / 255.f; | |
283 | 184 |
284 pad->buttons[pad->buttonsLength].pressed = | 185 pad.buttons[pad.buttonsLength].pressed = |
285 state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD; | 186 state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD; |
286 pad->buttons[pad->buttonsLength++].value = | 187 pad.buttons[pad.buttonsLength++].value = |
287 state.Gamepad.bRightTrigger / 255.f; | 188 state.Gamepad.bRightTrigger / 255.f; |
288 | 189 |
289 ADD(XINPUT_GAMEPAD_BACK); | 190 ADD(XINPUT_GAMEPAD_BACK); |
290 ADD(XINPUT_GAMEPAD_START); | 191 ADD(XINPUT_GAMEPAD_START); |
291 ADD(XINPUT_GAMEPAD_LEFT_THUMB); | 192 ADD(XINPUT_GAMEPAD_LEFT_THUMB); |
292 ADD(XINPUT_GAMEPAD_RIGHT_THUMB); | 193 ADD(XINPUT_GAMEPAD_RIGHT_THUMB); |
293 ADD(XINPUT_GAMEPAD_DPAD_UP); | 194 ADD(XINPUT_GAMEPAD_DPAD_UP); |
294 ADD(XINPUT_GAMEPAD_DPAD_DOWN); | 195 ADD(XINPUT_GAMEPAD_DPAD_DOWN); |
295 ADD(XINPUT_GAMEPAD_DPAD_LEFT); | 196 ADD(XINPUT_GAMEPAD_DPAD_LEFT); |
296 ADD(XINPUT_GAMEPAD_DPAD_RIGHT); | 197 ADD(XINPUT_GAMEPAD_DPAD_RIGHT); |
297 #undef ADD | 198 #undef ADD |
298 pad->axesLength = 0; | 199 pad.axesLength = 0; |
299 | 200 |
300 float value = 0.0; | 201 float value = 0.0; |
301 #define ADD(a, factor) \ | 202 #define ADD(a, factor) \ |
302 value = factor * NormalizeXInputAxis(a); \ | 203 value = factor * NormalizeXInputAxis(a); \ |
303 pad->axes[pad->axesLength++] = value; | 204 pad.axes[pad.axesLength++] = value; |
304 | 205 |
305 // XInput are +up/+right, -down/-left, we want -up/-left. | 206 // XInput are +up/+right, -down/-left, we want -up/-left. |
306 ADD(state.Gamepad.sThumbLX, 1); | 207 ADD(state.Gamepad.sThumbLX, 1); |
307 ADD(state.Gamepad.sThumbLY, -1); | 208 ADD(state.Gamepad.sThumbLY, -1); |
308 ADD(state.Gamepad.sThumbRX, 1); | 209 ADD(state.Gamepad.sThumbRX, 1); |
309 ADD(state.Gamepad.sThumbRY, -1); | 210 ADD(state.Gamepad.sThumbRY, -1); |
310 #undef ADD | 211 #undef ADD |
311 } else { | |
312 pad->connected = false; | |
313 } | 212 } |
314 } | 213 } |
315 | 214 |
316 void GamepadPlatformDataFetcherWin::GetRawInputPadData(int index, | |
317 WebGamepad* pad) { | |
318 RawGamepadInfo* gamepad = raw_input_fetcher_->GetGamepadInfo( | |
319 platform_pad_state_[index].raw_input_handle); | |
320 if (!gamepad) { | |
321 pad->connected = false; | |
322 return; | |
323 } | |
324 | |
325 pad->timestamp = gamepad->report_id; | |
326 pad->buttonsLength = gamepad->buttons_length; | |
327 pad->axesLength = gamepad->axes_length; | |
328 | |
329 for (unsigned int i = 0; i < pad->buttonsLength; i++) { | |
330 pad->buttons[i].pressed = gamepad->buttons[i]; | |
331 pad->buttons[i].value = gamepad->buttons[i] ? 1.0 : 0.0; | |
332 } | |
333 | |
334 for (unsigned int i = 0; i < pad->axesLength; i++) | |
335 pad->axes[i] = gamepad->axes[i].value; | |
336 } | |
337 | |
338 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() { | 215 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() { |
339 xinput_get_capabilities_ = NULL; | 216 xinput_get_capabilities_ = NULL; |
340 xinput_get_state_ = NULL; | 217 xinput_get_state_ = NULL; |
341 XInputEnableFunc xinput_enable = reinterpret_cast<XInputEnableFunc>( | 218 XInputEnableFunc xinput_enable = reinterpret_cast<XInputEnableFunc>( |
342 xinput_dll_.GetFunctionPointer("XInputEnable")); | 219 xinput_dll_.GetFunctionPointer("XInputEnable")); |
343 xinput_get_capabilities_ = reinterpret_cast<XInputGetCapabilitiesFunc>( | 220 xinput_get_capabilities_ = reinterpret_cast<XInputGetCapabilitiesFunc>( |
344 xinput_dll_.GetFunctionPointer("XInputGetCapabilities")); | 221 xinput_dll_.GetFunctionPointer("XInputGetCapabilities")); |
345 if (!xinput_get_capabilities_) | 222 if (!xinput_get_capabilities_) |
346 return false; | 223 return false; |
347 xinput_get_state_ = reinterpret_cast<XInputGetStateFunc>( | 224 xinput_get_state_ = reinterpret_cast<XInputGetStateFunc>( |
348 xinput_dll_.GetFunctionPointer("XInputGetState")); | 225 xinput_dll_.GetFunctionPointer("XInputGetState")); |
349 if (!xinput_get_state_) | 226 if (!xinput_get_state_) |
350 return false; | 227 return false; |
351 if (xinput_enable) { | 228 if (xinput_enable) { |
352 // XInputEnable is unavailable before Win8 and deprecated in Win10. | 229 // XInputEnable is unavailable before Win8 and deprecated in Win10. |
353 xinput_enable(true); | 230 xinput_enable(true); |
354 } | 231 } |
355 return true; | 232 return true; |
356 } | 233 } |
357 | 234 |
358 } // namespace device | 235 } // namespace device |
OLD | NEW |