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
3 // found in the LICENSE file.
5 #include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h"
7 #include "base/strings/stringprintf.h"
8 #include "base/trace_event/trace_event.h"
9 #include "base/win/windows_version.h"
10 #include "content/common/gamepad_hardware_buffer.h"
11 #include "content/common/gamepad_messages.h"
15 using namespace blink
;
19 // See http://goo.gl/5VSJR. These are not available in all versions of the
20 // header, but they can be returned from the driver, so we define our own
22 static const BYTE kDeviceSubTypeGamepad
= 1;
23 static const BYTE kDeviceSubTypeWheel
= 2;
24 static const BYTE kDeviceSubTypeArcadeStick
= 3;
25 static const BYTE kDeviceSubTypeFlightStick
= 4;
26 static const BYTE kDeviceSubTypeDancePad
= 5;
27 static const BYTE kDeviceSubTypeGuitar
= 6;
28 static const BYTE kDeviceSubTypeGuitarAlternate
= 7;
29 static const BYTE kDeviceSubTypeDrumKit
= 8;
30 static const BYTE kDeviceSubTypeGuitarBass
= 11;
31 static const BYTE kDeviceSubTypeArcadePad
= 19;
33 float NormalizeXInputAxis(SHORT value
) {
34 return ((value
+ 32768.f
) / 32767.5f
) - 1.f
;
37 const WebUChar
* const GamepadSubTypeName(BYTE sub_type
) {
39 case kDeviceSubTypeGamepad
: return L
"GAMEPAD";
40 case kDeviceSubTypeWheel
: return L
"WHEEL";
41 case kDeviceSubTypeArcadeStick
: return L
"ARCADE_STICK";
42 case kDeviceSubTypeFlightStick
: return L
"FLIGHT_STICK";
43 case kDeviceSubTypeDancePad
: return L
"DANCE_PAD";
44 case kDeviceSubTypeGuitar
: return L
"GUITAR";
45 case kDeviceSubTypeGuitarAlternate
: return L
"GUITAR_ALTERNATE";
46 case kDeviceSubTypeDrumKit
: return L
"DRUM_KIT";
47 case kDeviceSubTypeGuitarBass
: return L
"GUITAR_BASS";
48 case kDeviceSubTypeArcadePad
: return L
"ARCADE_PAD";
49 default: return L
"<UNKNOWN>";
55 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin()
56 : xinput_dll_(base::FilePath(FILE_PATH_LITERAL("xinput1_3.dll"))),
57 xinput_available_(GetXInputDllFunctions()) {
58 for (size_t i
= 0; i
< WebGamepads::itemsLengthCap
; ++i
)
59 pad_state_
[i
].status
= DISCONNECTED
;
61 raw_input_fetcher_
.reset(new RawInputDataFetcher());
62 raw_input_fetcher_
->StartMonitor();
65 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() {
66 raw_input_fetcher_
->StopMonitor();
69 int GamepadPlatformDataFetcherWin::FirstAvailableGamepadId() const {
70 for (size_t i
= 0; i
< WebGamepads::itemsLengthCap
; ++i
) {
71 if (pad_state_
[i
].status
== DISCONNECTED
)
77 bool GamepadPlatformDataFetcherWin::HasXInputGamepad(int index
) const {
78 for (size_t i
= 0; i
< WebGamepads::itemsLengthCap
; ++i
) {
79 if (pad_state_
[i
].status
== XINPUT_CONNECTED
&&
80 pad_state_
[i
].xinput_index
== index
)
86 bool GamepadPlatformDataFetcherWin::HasRawInputGamepad(
87 const HANDLE handle
) const {
88 for (size_t i
= 0; i
< WebGamepads::itemsLengthCap
; ++i
) {
89 if (pad_state_
[i
].status
== RAWINPUT_CONNECTED
&&
90 pad_state_
[i
].raw_input_handle
== handle
)
96 void GamepadPlatformDataFetcherWin::EnumerateDevices(
98 TRACE_EVENT0("GAMEPAD", "EnumerateDevices");
100 // Mark all disconnected pads DISCONNECTED.
101 for (size_t i
= 0; i
< WebGamepads::itemsLengthCap
; ++i
) {
102 if (!pads
->items
[i
].connected
)
103 pad_state_
[i
].status
= DISCONNECTED
;
106 for (size_t i
= 0; i
< XUSER_MAX_COUNT
; ++i
) {
107 if (HasXInputGamepad(i
))
109 int pad_index
= FirstAvailableGamepadId();
111 return; // We can't add any more gamepads.
112 WebGamepad
& pad
= pads
->items
[pad_index
];
113 if (xinput_available_
&& GetXInputPadConnectivity(i
, &pad
)) {
114 pad_state_
[pad_index
].status
= XINPUT_CONNECTED
;
115 pad_state_
[pad_index
].xinput_index
= i
;
116 pad_state_
[pad_index
].mapper
= NULL
;
121 if (raw_input_fetcher_
->Available()) {
122 std::vector
<RawGamepadInfo
*> raw_inputs
=
123 raw_input_fetcher_
->EnumerateDevices();
124 for (size_t i
= 0; i
< raw_inputs
.size(); ++i
) {
125 RawGamepadInfo
* gamepad
= raw_inputs
[i
];
126 if (gamepad
->buttons_length
== 0 && gamepad
->axes_length
== 0)
128 if (HasRawInputGamepad(gamepad
->handle
))
130 int pad_index
= FirstAvailableGamepadId();
133 WebGamepad
& pad
= pads
->items
[pad_index
];
134 pad
.connected
= true;
135 PadState
& state
= pad_state_
[pad_index
];
136 state
.status
= RAWINPUT_CONNECTED
;
137 state
.raw_input_handle
= gamepad
->handle
;
139 std::string vendor
= base::StringPrintf("%04x", gamepad
->vendor_id
);
140 std::string product
= base::StringPrintf("%04x", gamepad
->product_id
);
141 state
.mapper
= GetGamepadStandardMappingFunction(vendor
, product
);
143 swprintf(pad
.id
, WebGamepad::idLengthCap
,
144 L
"%ls (%lsVendor: %04x Product: %04x)",
145 gamepad
->id
, state
.mapper
? L
"STANDARD GAMEPAD " : L
"",
146 gamepad
->vendor_id
, gamepad
->product_id
);
149 swprintf(pad
.mapping
, WebGamepad::mappingLengthCap
, L
"standard");
159 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads
* pads
,
160 bool devices_changed_hint
) {
161 TRACE_EVENT0("GAMEPAD", "GetGamepadData");
163 if (!xinput_available_
&&
164 !raw_input_fetcher_
->Available()) {
169 // A note on XInput devices:
170 // If we got notification that system devices have been updated, then
171 // run GetCapabilities to update the connected status and the device
172 // identifier. It can be slow to do to both GetCapabilities and
173 // GetState on unconnected devices, so we want to avoid a 2-5ms pause
174 // here by only doing this when the devices are updated (despite
175 // documentation claiming it's OK to call it any time).
176 if (devices_changed_hint
)
177 EnumerateDevices(pads
);
179 for (size_t i
= 0; i
< WebGamepads::itemsLengthCap
; ++i
) {
180 // We rely on device_changed and GetCapabilities to tell us that
181 // something's been connected, but we will mark as disconnected if
182 // Get___PadState returns that we've lost the pad.
183 if (!pads
->items
[i
].connected
)
186 if (pad_state_
[i
].status
== XINPUT_CONNECTED
)
187 GetXInputPadData(i
, &pads
->items
[i
]);
188 else if (pad_state_
[i
].status
== RAWINPUT_CONNECTED
)
189 GetRawInputPadData(i
, &pads
->items
[i
]);
193 void GamepadPlatformDataFetcherWin::PauseHint(bool pause
) {
195 raw_input_fetcher_
->StopMonitor();
197 raw_input_fetcher_
->StartMonitor();
200 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity(
202 WebGamepad
* pad
) const {
204 TRACE_EVENT1("GAMEPAD", "GetXInputPadConnectivity", "id", i
);
205 XINPUT_CAPABILITIES caps
;
206 DWORD res
= xinput_get_capabilities_(i
, XINPUT_FLAG_GAMEPAD
, &caps
);
207 if (res
== ERROR_DEVICE_NOT_CONNECTED
) {
208 pad
->connected
= false;
211 pad
->connected
= true;
213 WebGamepad::idLengthCap
,
214 L
"Xbox 360 Controller (XInput STANDARD %ls)",
215 GamepadSubTypeName(caps
.SubType
));
216 swprintf(pad
->mapping
, WebGamepad::mappingLengthCap
, L
"standard");
221 void GamepadPlatformDataFetcherWin::GetXInputPadData(
225 memset(&state
, 0, sizeof(XINPUT_STATE
));
226 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i
);
227 DWORD dwResult
= xinput_get_state_(pad_state_
[i
].xinput_index
, &state
);
228 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i
);
230 if (dwResult
== ERROR_SUCCESS
) {
231 pad
->timestamp
= state
.dwPacketNumber
;
232 pad
->buttonsLength
= 0;
233 #define ADD(b) pad->buttons[pad->buttonsLength].pressed = \
234 (state.Gamepad.wButtons & (b)) != 0; \
235 pad->buttons[pad->buttonsLength++].value = \
236 ((state.Gamepad.wButtons & (b)) ? 1.f : 0.f);
237 ADD(XINPUT_GAMEPAD_A
);
238 ADD(XINPUT_GAMEPAD_B
);
239 ADD(XINPUT_GAMEPAD_X
);
240 ADD(XINPUT_GAMEPAD_Y
);
241 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER
);
242 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER
);
243 pad
->buttons
[pad
->buttonsLength
].pressed
=
244 state
.Gamepad
.bLeftTrigger
>= XINPUT_GAMEPAD_TRIGGER_THRESHOLD
;
245 pad
->buttons
[pad
->buttonsLength
++].value
=
246 state
.Gamepad
.bLeftTrigger
/ 255.f
;
247 pad
->buttons
[pad
->buttonsLength
].pressed
=
248 state
.Gamepad
.bRightTrigger
>= XINPUT_GAMEPAD_TRIGGER_THRESHOLD
;
249 pad
->buttons
[pad
->buttonsLength
++].value
=
250 state
.Gamepad
.bRightTrigger
/ 255.f
;
251 ADD(XINPUT_GAMEPAD_BACK
);
252 ADD(XINPUT_GAMEPAD_START
);
253 ADD(XINPUT_GAMEPAD_LEFT_THUMB
);
254 ADD(XINPUT_GAMEPAD_RIGHT_THUMB
);
255 ADD(XINPUT_GAMEPAD_DPAD_UP
);
256 ADD(XINPUT_GAMEPAD_DPAD_DOWN
);
257 ADD(XINPUT_GAMEPAD_DPAD_LEFT
);
258 ADD(XINPUT_GAMEPAD_DPAD_RIGHT
);
261 // XInput are +up/+right, -down/-left, we want -up/-left.
262 pad
->axes
[pad
->axesLength
++] = NormalizeXInputAxis(state
.Gamepad
.sThumbLX
);
263 pad
->axes
[pad
->axesLength
++] = -NormalizeXInputAxis(state
.Gamepad
.sThumbLY
);
264 pad
->axes
[pad
->axesLength
++] = NormalizeXInputAxis(state
.Gamepad
.sThumbRX
);
265 pad
->axes
[pad
->axesLength
++] = -NormalizeXInputAxis(state
.Gamepad
.sThumbRY
);
267 pad
->connected
= false;
271 void GamepadPlatformDataFetcherWin::GetRawInputPadData(
274 RawGamepadInfo
* gamepad
= raw_input_fetcher_
->GetGamepadInfo(
275 pad_state_
[index
].raw_input_handle
);
277 pad
->connected
= false;
281 WebGamepad raw_pad
= *pad
;
283 raw_pad
.timestamp
= gamepad
->report_id
;
284 raw_pad
.buttonsLength
= gamepad
->buttons_length
;
285 raw_pad
.axesLength
= gamepad
->axes_length
;
287 for (unsigned int i
= 0; i
< raw_pad
.buttonsLength
; i
++) {
288 raw_pad
.buttons
[i
].pressed
= gamepad
->buttons
[i
];
289 raw_pad
.buttons
[i
].value
= gamepad
->buttons
[i
] ? 1.0 : 0.0;
292 for (unsigned int i
= 0; i
< raw_pad
.axesLength
; i
++)
293 raw_pad
.axes
[i
] = gamepad
->axes
[i
].value
;
295 // Copy to the current state to the output buffer, using the mapping
296 // function, if there is one available.
297 if (pad_state_
[index
].mapper
)
298 pad_state_
[index
].mapper(raw_pad
, pad
);
303 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() {
304 xinput_get_capabilities_
= NULL
;
305 xinput_get_state_
= NULL
;
306 xinput_enable_
= reinterpret_cast<XInputEnableFunc
>(
307 xinput_dll_
.GetFunctionPointer("XInputEnable"));
310 xinput_get_capabilities_
= reinterpret_cast<XInputGetCapabilitiesFunc
>(
311 xinput_dll_
.GetFunctionPointer("XInputGetCapabilities"));
312 if (!xinput_get_capabilities_
)
314 xinput_get_state_
= reinterpret_cast<XInputGetStateFunc
>(
315 xinput_dll_
.GetFunctionPointer("XInputGetState"));
316 if (!xinput_get_state_
)
318 xinput_enable_(true);
322 } // namespace content