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 "content/browser/gamepad/gamepad_platform_data_fetcher_win.h" | 5 #include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h" |
6 | 6 |
7 #include <dinput.h> | |
8 #include <dinputd.h> | |
9 | |
7 #include "base/debug/trace_event.h" | 10 #include "base/debug/trace_event.h" |
11 #include "base/stringprintf.h" | |
8 #include "content/common/gamepad_messages.h" | 12 #include "content/common/gamepad_messages.h" |
9 #include "content/common/gamepad_hardware_buffer.h" | 13 #include "content/common/gamepad_hardware_buffer.h" |
10 | 14 |
11 namespace content { | 15 namespace content { |
12 | 16 |
13 using namespace WebKit; | 17 using namespace WebKit; |
14 | 18 |
15 namespace { | 19 namespace { |
16 | 20 |
17 // See http://goo.gl/5VSJR. These are not available in all versions of the | 21 // See http://goo.gl/5VSJR. These are not available in all versions of the |
18 // header, but they can be returned from the driver, so we define our own | 22 // header, but they can be returned from the driver, so we define our own |
19 // versions here. | 23 // versions here. |
20 static const BYTE kDeviceSubTypeGamepad = 1; | 24 static const BYTE kDeviceSubTypeGamepad = 1; |
21 static const BYTE kDeviceSubTypeWheel = 2; | 25 static const BYTE kDeviceSubTypeWheel = 2; |
22 static const BYTE kDeviceSubTypeArcadeStick = 3; | 26 static const BYTE kDeviceSubTypeArcadeStick = 3; |
23 static const BYTE kDeviceSubTypeFlightStick = 4; | 27 static const BYTE kDeviceSubTypeFlightStick = 4; |
24 static const BYTE kDeviceSubTypeDancePad = 5; | 28 static const BYTE kDeviceSubTypeDancePad = 5; |
25 static const BYTE kDeviceSubTypeGuitar = 6; | 29 static const BYTE kDeviceSubTypeGuitar = 6; |
26 static const BYTE kDeviceSubTypeGuitarAlternate = 7; | 30 static const BYTE kDeviceSubTypeGuitarAlternate = 7; |
27 static const BYTE kDeviceSubTypeDrumKit = 8; | 31 static const BYTE kDeviceSubTypeDrumKit = 8; |
28 static const BYTE kDeviceSubTypeGuitarBass = 11; | 32 static const BYTE kDeviceSubTypeGuitarBass = 11; |
29 static const BYTE kDeviceSubTypeArcadePad = 19; | 33 static const BYTE kDeviceSubTypeArcadePad = 19; |
30 | 34 |
31 float NormalizeAxis(SHORT value) { | 35 float NormalizeXInputAxis(SHORT value) { |
32 return ((value + 32768.f) / 32767.5f) - 1.f; | 36 return ((value + 32768.f) / 32767.5f) - 1.f; |
33 } | 37 } |
34 | 38 |
35 const WebUChar* const GamepadSubTypeName(BYTE sub_type) { | 39 const WebUChar* const GamepadSubTypeName(BYTE sub_type) { |
36 switch (sub_type) { | 40 switch (sub_type) { |
37 case kDeviceSubTypeGamepad: return L"GAMEPAD"; | 41 case kDeviceSubTypeGamepad: return L"GAMEPAD"; |
38 case kDeviceSubTypeWheel: return L"WHEEL"; | 42 case kDeviceSubTypeWheel: return L"WHEEL"; |
39 case kDeviceSubTypeArcadeStick: return L"ARCADE_STICK"; | 43 case kDeviceSubTypeArcadeStick: return L"ARCADE_STICK"; |
40 case kDeviceSubTypeFlightStick: return L"FLIGHT_STICK"; | 44 case kDeviceSubTypeFlightStick: return L"FLIGHT_STICK"; |
41 case kDeviceSubTypeDancePad: return L"DANCE_PAD"; | 45 case kDeviceSubTypeDancePad: return L"DANCE_PAD"; |
42 case kDeviceSubTypeGuitar: return L"GUITAR"; | 46 case kDeviceSubTypeGuitar: return L"GUITAR"; |
43 case kDeviceSubTypeGuitarAlternate: return L"GUITAR_ALTERNATE"; | 47 case kDeviceSubTypeGuitarAlternate: return L"GUITAR_ALTERNATE"; |
44 case kDeviceSubTypeDrumKit: return L"DRUM_KIT"; | 48 case kDeviceSubTypeDrumKit: return L"DRUM_KIT"; |
45 case kDeviceSubTypeGuitarBass: return L"GUITAR_BASS"; | 49 case kDeviceSubTypeGuitarBass: return L"GUITAR_BASS"; |
46 case kDeviceSubTypeArcadePad: return L"ARCADE_PAD"; | 50 case kDeviceSubTypeArcadePad: return L"ARCADE_PAD"; |
47 default: return L"<UNKNOWN>"; | 51 default: return L"<UNKNOWN>"; |
48 } | 52 } |
49 } | 53 } |
50 | 54 |
55 bool GetDirectInputVendorProduct(IDirectInputDevice8* gamepad, | |
56 std::string* vendor, | |
57 std::string* product) { | |
58 DIPROPDWORD prop; | |
59 prop.diph.dwSize = sizeof(DIPROPDWORD); | |
60 prop.diph.dwHeaderSize = sizeof(DIPROPHEADER); | |
61 prop.diph.dwObj = 0; | |
62 prop.diph.dwHow = DIPH_DEVICE; | |
63 | |
64 if (FAILED(gamepad->GetProperty(DIPROP_VIDPID, &prop.diph))) | |
65 return false; | |
66 *vendor = base::StringPrintf("%04x", LOWORD(prop.dwData)); | |
67 *product = base::StringPrintf("%04x", HIWORD(prop.dwData)); | |
68 return true; | |
69 } | |
70 | |
71 // Sets the deadzone value for all axes of a gamepad. | |
72 // deadzone values range from 0 (no deadzone) to 10,000 (entire range | |
73 // is dead). | |
74 bool SetDirectInputDeadZone(IDirectInputDevice8* gamepad, | |
75 int deadzone) { | |
76 DIPROPDWORD prop; | |
77 prop.diph.dwSize = sizeof(DIPROPDWORD); | |
78 prop.diph.dwHeaderSize = sizeof(DIPROPHEADER); | |
79 prop.diph.dwObj = 0; | |
80 prop.diph.dwHow = DIPH_DEVICE; | |
81 prop.dwData = deadzone; | |
82 return SUCCEEDED(gamepad->SetProperty(DIPROP_DEADZONE, &prop.diph)); | |
83 } | |
84 | |
85 struct InternalDirectInputDevice { | |
86 IDirectInputDevice8* gamepad; | |
87 GamepadStandardMappingFunction mapper; | |
88 wchar_t id[WebGamepad::idLengthCap]; | |
89 GUID guid; | |
90 }; | |
91 | |
92 struct EnumDevicesContext { | |
93 IDirectInput8* directinput_interface; | |
94 std::vector<InternalDirectInputDevice>* directinput_devices; | |
95 }; | |
96 | |
97 // We define our own data format structure to attempt to get as many | |
98 // axes as possible. | |
99 struct JoyData { | |
100 long axes[10]; | |
101 char buttons[24]; | |
102 DWORD pov; // Often used for D-pads. | |
103 }; | |
104 | |
105 BOOL CALLBACK DirectInputEnumDevicesCallback(const DIDEVICEINSTANCE* instance, | |
106 void* context) { | |
107 EnumDevicesContext* ctxt = (EnumDevicesContext*) context; | |
scottmg
2013/02/19 18:50:52
reinterpret_cast
teravest
2013/02/19 20:53:37
Done.
| |
108 IDirectInputDevice8* gamepad; | |
109 | |
110 if (FAILED(ctxt->directinput_interface->CreateDevice(instance->guidInstance, | |
111 &gamepad, | |
112 NULL))) | |
113 return DIENUM_CONTINUE; | |
114 | |
115 gamepad->Acquire(); | |
116 | |
117 #define MAKE_AXIS(i) \ | |
118 {0, FIELD_OFFSET(JoyData, axes) + 4 * i, \ | |
119 DIDFT_AXIS | DIDFT_MAKEINSTANCE(i) | DIDFT_OPTIONAL, 0} | |
120 #define MAKE_BUTTON(i) \ | |
121 {&GUID_Button, FIELD_OFFSET(JoyData, buttons) + i, \ | |
122 DIDFT_BUTTON | DIDFT_MAKEINSTANCE(i) | DIDFT_OPTIONAL, 0} | |
123 #define MAKE_POV() \ | |
124 {&GUID_POV, FIELD_OFFSET(JoyData, pov), DIDFT_POV | DIDFT_OPTIONAL, 0} | |
125 DIOBJECTDATAFORMAT rgodf[] = { | |
126 MAKE_AXIS(0), | |
127 MAKE_AXIS(1), | |
128 MAKE_AXIS(2), | |
129 MAKE_AXIS(3), | |
130 MAKE_AXIS(4), | |
131 MAKE_AXIS(5), | |
132 MAKE_AXIS(6), | |
133 MAKE_AXIS(7), | |
134 MAKE_AXIS(8), | |
135 MAKE_AXIS(9), | |
136 MAKE_BUTTON(0), | |
137 MAKE_BUTTON(1), | |
138 MAKE_BUTTON(2), | |
139 MAKE_BUTTON(3), | |
140 MAKE_BUTTON(4), | |
141 MAKE_BUTTON(5), | |
142 MAKE_BUTTON(6), | |
143 MAKE_BUTTON(7), | |
144 MAKE_BUTTON(8), | |
145 MAKE_BUTTON(9), | |
146 MAKE_BUTTON(10), | |
147 MAKE_BUTTON(11), | |
148 MAKE_BUTTON(12), | |
149 MAKE_BUTTON(13), | |
150 MAKE_BUTTON(14), | |
151 MAKE_BUTTON(15), | |
152 MAKE_BUTTON(16), | |
153 MAKE_POV(), | |
154 }; | |
155 #undef MAKE_AXIS | |
156 #undef MAKE_BUTTON | |
157 #undef MAKE_POV | |
158 | |
159 DIDATAFORMAT df = { | |
160 sizeof (DIDATAFORMAT), | |
161 sizeof (DIOBJECTDATAFORMAT), | |
162 DIDF_ABSAXIS, | |
163 sizeof (JoyData), | |
164 sizeof (rgodf) / sizeof (rgodf[0]), | |
165 rgodf | |
166 }; | |
167 | |
168 // If we can't set the data format on the device, don't add it to our | |
169 // list, since we won't know how to read data from it. | |
170 if (FAILED(gamepad->SetDataFormat(&df))) { | |
171 gamepad->Release(); | |
172 return DIENUM_CONTINUE; | |
173 } | |
174 | |
175 InternalDirectInputDevice device; | |
176 device.guid = instance->guidInstance; | |
177 device.gamepad = gamepad; | |
178 std::string vendor; | |
179 std::string product; | |
180 if (!GetDirectInputVendorProduct(gamepad, &vendor, &product)) { | |
181 gamepad->Release(); | |
182 return DIENUM_CONTINUE; | |
183 } | |
184 | |
185 // Set the dead zone to 10% of the axis length for all axes. This | |
186 // gives us a larger space for what's "neutral" so the controls don't | |
187 // slowly drift. | |
188 SetDirectInputDeadZone(gamepad, 1000); | |
189 device.mapper = GetGamepadStandardMappingFunction(vendor, product); | |
190 if (device.mapper) { | |
191 base::swprintf(device.id, | |
192 WebGamepad::idLengthCap, | |
193 L"STANDARD GAMEPAD (%ls)", | |
194 instance->tszProductName); | |
195 ctxt->directinput_devices->push_back(device); | |
196 } else { | |
197 gamepad->Release(); | |
198 } | |
199 return DIENUM_CONTINUE; | |
200 } | |
201 | |
51 } // namespace | 202 } // namespace |
52 | 203 |
53 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() | 204 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() |
54 : xinput_dll_(FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))), | 205 : xinput_dll_(FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))), |
55 xinput_available_(GetXinputDllFunctions()) { | 206 xinput_available_(GetXInputDllFunctions()) { |
207 directinput_available_ = | |
208 SUCCEEDED(DirectInput8Create(GetModuleHandle(NULL), | |
209 DIRECTINPUT_VERSION, | |
210 IID_IDirectInput8, | |
211 (void **)&directinput_interface_, | |
scottmg
2013/02/19 18:50:52
reinterpret_cast<void**>(...)
teravest
2013/02/19 20:53:37
Done.
| |
212 NULL)); | |
213 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) | |
214 pad_state_[i].status = DISCONNECTED; | |
56 } | 215 } |
57 | 216 |
58 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { | 217 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { |
59 } | 218 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
219 if (pad_state_[i].status == DIRECTINPUT_CONNECTED) | |
220 pad_state_[i].directinput_gamepad->Release(); | |
221 } | |
222 } | |
223 | |
224 int GamepadPlatformDataFetcherWin::FirstAvailableGamepadId() const { | |
225 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
226 if (pad_state_[i].status == DISCONNECTED) | |
227 return i; | |
228 } | |
229 return -1; | |
230 } | |
231 | |
232 bool GamepadPlatformDataFetcherWin::HasXInputGamepad(int index) const { | |
233 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
234 if (pad_state_[i].status == XINPUT_CONNECTED && | |
235 pad_state_[i].xinput_index == index) | |
236 return true; | |
237 } | |
238 return false; | |
239 } | |
240 | |
241 bool GamepadPlatformDataFetcherWin::HasDirectInputGamepad( | |
242 const GUID &guid) const { | |
scottmg
2013/02/19 18:50:52
nit; GUID&
| |
243 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
244 if (pad_state_[i].status == DIRECTINPUT_CONNECTED && | |
245 pad_state_[i].guid == guid) | |
246 return true; | |
247 } | |
248 return false; | |
249 } | |
250 | |
251 void GamepadPlatformDataFetcherWin::EnumerateDevices( | |
252 WebGamepads* pads) { | |
253 TRACE_EVENT0("GAMEPAD", "EnumerateDevices"); | |
254 | |
255 // Mark all disconnected pads DISCONNECTED. | |
256 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { | |
257 if (!pads->items[i].connected) | |
258 pad_state_[i].status = DISCONNECTED; | |
259 } | |
260 | |
261 for (size_t i = 0; i <= XUSER_MAX_COUNT; ++i) { | |
262 if (HasXInputGamepad(i)) | |
263 continue; | |
264 int pad_index = FirstAvailableGamepadId(); | |
265 if (pad_index == -1) | |
266 return; // We can't add any more gamepads. | |
267 WebGamepad &pad = pads->items[pad_index]; | |
scottmg
2013/02/19 18:50:52
nit; WebGamepad&
teravest
2013/02/19 20:53:37
Done.
| |
268 if (xinput_available_ && GetXInputPadConnectivity(i, &pad)) { | |
269 pad_state_[pad_index].status = XINPUT_CONNECTED; | |
270 pad_state_[pad_index].xinput_index = i; | |
271 } | |
272 } | |
273 | |
274 if (directinput_available_) { | |
275 struct EnumDevicesContext context; | |
276 std::vector<InternalDirectInputDevice> directinput_gamepads; | |
277 context.directinput_interface = directinput_interface_; | |
278 context.directinput_devices = &directinput_gamepads; | |
279 | |
280 directinput_interface_->EnumDevices( | |
281 DI8DEVCLASS_GAMECTRL, | |
282 &DirectInputEnumDevicesCallback, | |
283 &context, | |
284 DIEDFL_ATTACHEDONLY); | |
285 for (size_t i = 0; i < directinput_gamepads.size(); ++i) { | |
286 if (HasDirectInputGamepad(directinput_gamepads[i].guid)) { | |
287 directinput_gamepads[i].gamepad->Release(); | |
288 continue; | |
289 } | |
290 int pad_index = FirstAvailableGamepadId(); | |
291 if (pad_index == -1) | |
292 return; | |
293 WebGamepad &pad = pads->items[pad_index]; | |
scottmg
2013/02/19 18:50:52
nit; WebGamepad&
teravest
2013/02/19 20:53:37
Done.
| |
294 pad.connected = true; | |
295 wcscpy_s(pad.id, WebGamepad::idLengthCap, directinput_gamepads[i].id); | |
296 PadState &state = pad_state_[pad_index]; | |
scottmg
2013/02/19 18:50:52
nit; PadState&
teravest
2013/02/19 20:53:37
Done.
| |
297 state.status = DIRECTINPUT_CONNECTED; | |
298 state.guid = directinput_gamepads[i].guid; | |
299 state.directinput_gamepad = directinput_gamepads[i].gamepad; | |
300 state.mapper = directinput_gamepads[i].mapper; | |
301 } | |
302 } | |
303 } | |
304 | |
60 | 305 |
61 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads, | 306 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads, |
62 bool devices_changed_hint) { | 307 bool devices_changed_hint) { |
63 TRACE_EVENT0("GAMEPAD", "GetGamepadData"); | 308 TRACE_EVENT0("GAMEPAD", "GetGamepadData"); |
64 | 309 |
65 // If there's no XInput DLL on the system, early out so that we don't | 310 if (!xinput_available_ && !directinput_available_) { |
66 // call any other XInput functions. | |
67 if (!xinput_available_) { | |
68 pads->length = 0; | 311 pads->length = 0; |
69 return; | 312 return; |
70 } | 313 } |
71 | 314 |
72 pads->length = WebGamepads::itemsLengthCap; | 315 // A note on XInput devices: |
73 | |
74 // If we got notification that system devices have been updated, then | 316 // If we got notification that system devices have been updated, then |
75 // run GetCapabilities to update the connected status and the device | 317 // run GetCapabilities to update the connected status and the device |
76 // identifier. It can be slow to do to both GetCapabilities and | 318 // identifier. It can be slow to do to both GetCapabilities and |
77 // GetState on unconnected devices, so we want to avoid a 2-5ms pause | 319 // GetState on unconnected devices, so we want to avoid a 2-5ms pause |
78 // here by only doing this when the devices are updated (despite | 320 // here by only doing this when the devices are updated (despite |
79 // documentation claiming it's OK to call it any time). | 321 // documentation claiming it's OK to call it any time). |
80 if (devices_changed_hint) { | 322 if (devices_changed_hint) |
81 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { | 323 EnumerateDevices(pads); |
82 WebGamepad& pad = pads->items[i]; | 324 |
83 TRACE_EVENT1("GAMEPAD", "GetCapabilities", "id", i); | 325 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
84 XINPUT_CAPABILITIES caps; | 326 WebGamepad& pad = pads->items[i]; |
85 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps); | 327 if (pad_state_[i].status == XINPUT_CONNECTED) |
86 if (res == ERROR_DEVICE_NOT_CONNECTED) { | 328 GetXInputPadData(i, &pad); |
87 pad.connected = false; | 329 else if (pad_state_[i].status == DIRECTINPUT_CONNECTED) |
88 } else { | 330 GetDirectInputPadData(i, &pad); |
89 pad.connected = true; | 331 } |
90 base::swprintf(pad.id, | 332 pads->length = WebGamepads::itemsLengthCap; |
91 WebGamepad::idLengthCap, | 333 } |
92 L"Xbox 360 Controller (XInput STANDARD %ls)", | 334 |
93 GamepadSubTypeName(caps.SubType)); | 335 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity( |
94 } | 336 int i, |
337 WebGamepad* pad) const { | |
338 DCHECK(pad); | |
339 TRACE_EVENT1("GAMEPAD", "GetXInputPadConnectivity", "id", i); | |
340 XINPUT_CAPABILITIES caps; | |
341 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps); | |
342 if (res == ERROR_DEVICE_NOT_CONNECTED || | |
343 caps.SubType != kDeviceSubTypeGamepad) { | |
344 pad->connected = false; | |
345 return false; | |
346 } else { | |
347 pad->connected = true; | |
348 base::swprintf(pad->id, | |
349 WebGamepad::idLengthCap, | |
350 L"Xbox 360 Controller (XInput STANDARD %ls)", | |
351 GamepadSubTypeName(caps.SubType)); | |
352 return true; | |
353 } | |
354 } | |
355 | |
356 void GamepadPlatformDataFetcherWin::GetXInputPadData( | |
357 int i, | |
358 WebGamepad* pad) { | |
359 // We rely on device_changed and GetCapabilities to tell us that | |
360 // something's been connected, but we will mark as disconnected if | |
361 // GetState returns that we've lost the pad. | |
362 if (!pad->connected) | |
363 return; | |
364 | |
365 XINPUT_STATE state; | |
366 memset(&state, 0, sizeof(XINPUT_STATE)); | |
367 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); | |
368 DWORD dwResult = xinput_get_state_(pad_state_[i].xinput_index, &state); | |
369 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); | |
370 | |
371 if (dwResult == ERROR_SUCCESS) { | |
372 pad->timestamp = state.dwPacketNumber; | |
373 pad->buttonsLength = 0; | |
374 #define ADD(b) pad->buttons[pad->buttonsLength++] = \ | |
375 ((state.Gamepad.wButtons & (b)) ? 1.0 : 0.0); | |
376 ADD(XINPUT_GAMEPAD_A); | |
377 ADD(XINPUT_GAMEPAD_B); | |
378 ADD(XINPUT_GAMEPAD_X); | |
379 ADD(XINPUT_GAMEPAD_Y); | |
380 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); | |
381 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); | |
382 pad->buttons[pad->buttonsLength++] = state.Gamepad.bLeftTrigger / 255.0; | |
383 pad->buttons[pad->buttonsLength++] = state.Gamepad.bRightTrigger / 255.0; | |
384 ADD(XINPUT_GAMEPAD_BACK); | |
385 ADD(XINPUT_GAMEPAD_START); | |
386 ADD(XINPUT_GAMEPAD_LEFT_THUMB); | |
387 ADD(XINPUT_GAMEPAD_RIGHT_THUMB); | |
388 ADD(XINPUT_GAMEPAD_DPAD_UP); | |
389 ADD(XINPUT_GAMEPAD_DPAD_DOWN); | |
390 ADD(XINPUT_GAMEPAD_DPAD_LEFT); | |
391 ADD(XINPUT_GAMEPAD_DPAD_RIGHT); | |
392 #undef ADD | |
393 pad->axesLength = 0; | |
394 // XInput are +up/+right, -down/-left, we want -up/-left. | |
395 pad->axes[pad->axesLength++] = NormalizeXInputAxis(state.Gamepad.sThumbLX); | |
396 pad->axes[pad->axesLength++] = -NormalizeXInputAxis(state.Gamepad.sThumbLY); | |
397 pad->axes[pad->axesLength++] = NormalizeXInputAxis(state.Gamepad.sThumbRX); | |
398 pad->axes[pad->axesLength++] = -NormalizeXInputAxis(state.Gamepad.sThumbRY); | |
399 } else { | |
400 pad->connected = false; | |
401 } | |
402 } | |
403 | |
404 void GamepadPlatformDataFetcherWin::GetDirectInputPadData( | |
405 int index, | |
406 WebGamepad* pad) { | |
407 if (!pad->connected) | |
408 return; | |
409 | |
410 IDirectInputDevice8* gamepad = pad_state_[index].directinput_gamepad; | |
411 if (FAILED(gamepad->Poll())) { | |
412 // Polling didn't work, try acquiring the gamepad. | |
413 if (FAILED(gamepad->Acquire())) { | |
414 pad->buttonsLength = 0; | |
415 pad->axesLength = 0; | |
416 return; | |
95 } | 417 } |
96 } | 418 // Try polling again. |
97 | 419 if (FAILED(gamepad->Poll())) { |
98 // We've updated the connection state if necessary, now update the actual | 420 pad->buttonsLength = 0; |
99 // data for the devices that are connected. | 421 pad->axesLength = 0; |
100 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { | 422 return; |
101 WebGamepad& pad = pads->items[i]; | |
102 | |
103 // We rely on device_changed and GetCapabilities to tell us that | |
104 // something's been connected, but we will mark as disconnected if | |
105 // GetState returns that we've lost the pad. | |
106 if (!pad.connected) | |
107 continue; | |
108 | |
109 XINPUT_STATE state; | |
110 memset(&state, 0, sizeof(XINPUT_STATE)); | |
111 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); | |
112 DWORD dwResult = xinput_get_state_(i, &state); | |
113 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); | |
114 | |
115 if (dwResult == ERROR_SUCCESS) { | |
116 pad.timestamp = state.dwPacketNumber; | |
117 pad.buttonsLength = 0; | |
118 #define ADD(b) pad.buttons[pad.buttonsLength++] = \ | |
119 ((state.Gamepad.wButtons & (b)) ? 1.0 : 0.0); | |
120 ADD(XINPUT_GAMEPAD_A); | |
121 ADD(XINPUT_GAMEPAD_B); | |
122 ADD(XINPUT_GAMEPAD_X); | |
123 ADD(XINPUT_GAMEPAD_Y); | |
124 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); | |
125 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); | |
126 pad.buttons[pad.buttonsLength++] = state.Gamepad.bLeftTrigger / 255.0; | |
127 pad.buttons[pad.buttonsLength++] = state.Gamepad.bRightTrigger / 255.0; | |
128 ADD(XINPUT_GAMEPAD_BACK); | |
129 ADD(XINPUT_GAMEPAD_START); | |
130 ADD(XINPUT_GAMEPAD_LEFT_THUMB); | |
131 ADD(XINPUT_GAMEPAD_RIGHT_THUMB); | |
132 ADD(XINPUT_GAMEPAD_DPAD_UP); | |
133 ADD(XINPUT_GAMEPAD_DPAD_DOWN); | |
134 ADD(XINPUT_GAMEPAD_DPAD_LEFT); | |
135 ADD(XINPUT_GAMEPAD_DPAD_RIGHT); | |
136 #undef ADD | |
137 pad.axesLength = 0; | |
138 // XInput are +up/+right, -down/-left, we want -up/-left. | |
139 pad.axes[pad.axesLength++] = NormalizeAxis(state.Gamepad.sThumbLX); | |
140 pad.axes[pad.axesLength++] = -NormalizeAxis(state.Gamepad.sThumbLY); | |
141 pad.axes[pad.axesLength++] = NormalizeAxis(state.Gamepad.sThumbRX); | |
142 pad.axes[pad.axesLength++] = -NormalizeAxis(state.Gamepad.sThumbRY); | |
143 } else { | |
144 pad.connected = false; | |
145 } | 423 } |
146 } | 424 } |
147 } | 425 JoyData state; |
148 | 426 if (FAILED(gamepad->GetDeviceState(sizeof(JoyData), &state))) { |
149 bool GamepadPlatformDataFetcherWin::GetXinputDllFunctions() { | 427 pad->connected = false; |
428 return; | |
429 } | |
430 | |
431 WebGamepad raw; | |
432 raw.connected = true; | |
433 for (int i = 0; i < 16; i++) | |
434 raw.buttons[i] = (state.buttons[i] & 0x80) ? 1.0 : 0.0; | |
435 | |
436 // We map the POV (often a D-pad) into the buttons 16-19. | |
437 // DirectInput gives pov measurements in hundredths of degrees, | |
438 // clockwise from "North". | |
439 // We use 22.5 degree slices so we can handle diagonal D-raw presses. | |
440 static const int arc_segment = 2250; // 22.5 degrees = 1/16 circle | |
441 if (state.pov > arc_segment && state.pov < 7 * arc_segment) | |
442 raw.buttons[19] = 1.0; | |
443 else | |
444 raw.buttons[19] = 0.0; | |
445 | |
446 if (state.pov > 5 * arc_segment && state.pov < 11 * arc_segment) | |
447 raw.buttons[17] = 1.0; | |
448 else | |
449 raw.buttons[17] = 0.0; | |
450 | |
451 if (state.pov > 9 * arc_segment && state.pov < 15 * arc_segment) | |
452 raw.buttons[18] = 1.0; | |
453 else | |
454 raw.buttons[18] = 0.0; | |
455 | |
456 if (state.pov < 3 * arc_segment || | |
457 (state.pov > 13 * arc_segment && state.pov < 36000)) | |
458 raw.buttons[16] = 1.0; | |
459 else | |
460 raw.buttons[16] = 0.0; | |
461 | |
462 for (int i = 0; i < 10; i++) | |
463 raw.axes[i] = state.axes[i]; | |
464 pad_state_[index].mapper(raw, pad); | |
465 } | |
466 | |
467 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() { | |
150 xinput_get_capabilities_ = NULL; | 468 xinput_get_capabilities_ = NULL; |
151 xinput_get_state_ = NULL; | 469 xinput_get_state_ = NULL; |
152 xinput_enable_ = static_cast<XInputEnableFunc>( | 470 xinput_enable_ = static_cast<XInputEnableFunc>( |
153 xinput_dll_.GetFunctionPointer("XInputEnable")); | 471 xinput_dll_.GetFunctionPointer("XInputEnable")); |
154 if (!xinput_enable_) | 472 if (!xinput_enable_) |
155 return false; | 473 return false; |
156 xinput_get_capabilities_ = static_cast<XInputGetCapabilitiesFunc>( | 474 xinput_get_capabilities_ = static_cast<XInputGetCapabilitiesFunc>( |
157 xinput_dll_.GetFunctionPointer("XInputGetCapabilities")); | 475 xinput_dll_.GetFunctionPointer("XInputGetCapabilities")); |
158 if (!xinput_get_capabilities_) | 476 if (!xinput_get_capabilities_) |
159 return false; | 477 return false; |
160 xinput_get_state_ = static_cast<XInputGetStateFunc>( | 478 xinput_get_state_ = static_cast<XInputGetStateFunc>( |
161 xinput_dll_.GetFunctionPointer("XInputGetState")); | 479 xinput_dll_.GetFunctionPointer("XInputGetState")); |
162 if (!xinput_get_state_) | 480 if (!xinput_get_state_) |
163 return false; | 481 return false; |
164 xinput_enable_(true); | 482 xinput_enable_(true); |
165 return true; | 483 return true; |
166 } | 484 } |
167 | 485 |
168 } // namespace content | 486 } // namespace content |
OLD | NEW |