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/debug/trace_event.h"
8 #include "base/strings/stringprintf.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 (HasRawInputGamepad(gamepad
->handle
))
128 int pad_index
= FirstAvailableGamepadId();
131 WebGamepad
& pad
= pads
->items
[pad_index
];
132 pad
.connected
= true;
133 PadState
& state
= pad_state_
[pad_index
];
134 state
.status
= RAWINPUT_CONNECTED
;
135 state
.raw_input_handle
= gamepad
->handle
;
137 std::string vendor
= base::StringPrintf("%04x", gamepad
->vendor_id
);
138 std::string product
= base::StringPrintf("%04x", gamepad
->product_id
);
139 state
.mapper
= GetGamepadStandardMappingFunction(vendor
, product
);
141 swprintf(pad
.id
, WebGamepad::idLengthCap
,
142 L
"%ls (%lsVendor: %04x Product: %04x)",
143 gamepad
->id
, state
.mapper
? L
"STANDARD GAMEPAD " : L
"",
144 gamepad
->vendor_id
, gamepad
->product_id
);
147 swprintf(pad
.mapping
, WebGamepad::mappingLengthCap
, L
"standard");
157 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads
* pads
,
158 bool devices_changed_hint
) {
159 TRACE_EVENT0("GAMEPAD", "GetGamepadData");
161 if (!xinput_available_
&&
162 !raw_input_fetcher_
->Available()) {
167 // A note on XInput devices:
168 // If we got notification that system devices have been updated, then
169 // run GetCapabilities to update the connected status and the device
170 // identifier. It can be slow to do to both GetCapabilities and
171 // GetState on unconnected devices, so we want to avoid a 2-5ms pause
172 // here by only doing this when the devices are updated (despite
173 // documentation claiming it's OK to call it any time).
174 if (devices_changed_hint
)
175 EnumerateDevices(pads
);
177 for (size_t i
= 0; i
< WebGamepads::itemsLengthCap
; ++i
) {
178 // We rely on device_changed and GetCapabilities to tell us that
179 // something's been connected, but we will mark as disconnected if
180 // Get___PadState returns that we've lost the pad.
181 if (!pads
->items
[i
].connected
)
184 if (pad_state_
[i
].status
== XINPUT_CONNECTED
)
185 GetXInputPadData(i
, &pads
->items
[i
]);
186 else if (pad_state_
[i
].status
== RAWINPUT_CONNECTED
)
187 GetRawInputPadData(i
, &pads
->items
[i
]);
191 void GamepadPlatformDataFetcherWin::PauseHint(bool pause
) {
193 raw_input_fetcher_
->StopMonitor();
195 raw_input_fetcher_
->StartMonitor();
198 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity(
200 WebGamepad
* pad
) const {
202 TRACE_EVENT1("GAMEPAD", "GetXInputPadConnectivity", "id", i
);
203 XINPUT_CAPABILITIES caps
;
204 DWORD res
= xinput_get_capabilities_(i
, XINPUT_FLAG_GAMEPAD
, &caps
);
205 if (res
== ERROR_DEVICE_NOT_CONNECTED
) {
206 pad
->connected
= false;
209 pad
->connected
= true;
211 WebGamepad::idLengthCap
,
212 L
"Xbox 360 Controller (XInput STANDARD %ls)",
213 GamepadSubTypeName(caps
.SubType
));
214 swprintf(pad
->mapping
, WebGamepad::mappingLengthCap
, L
"standard");
219 void GamepadPlatformDataFetcherWin::GetXInputPadData(
223 memset(&state
, 0, sizeof(XINPUT_STATE
));
224 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i
);
225 DWORD dwResult
= xinput_get_state_(pad_state_
[i
].xinput_index
, &state
);
226 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i
);
228 if (dwResult
== ERROR_SUCCESS
) {
229 pad
->timestamp
= state
.dwPacketNumber
;
230 pad
->buttonsLength
= 0;
231 #define ADD(b) pad->buttons[pad->buttonsLength].pressed = \
232 (state.Gamepad.wButtons & (b)) != 0; \
233 pad->buttons[pad->buttonsLength++].value = \
234 ((state.Gamepad.wButtons & (b)) ? 1.f : 0.f);
235 ADD(XINPUT_GAMEPAD_A
);
236 ADD(XINPUT_GAMEPAD_B
);
237 ADD(XINPUT_GAMEPAD_X
);
238 ADD(XINPUT_GAMEPAD_Y
);
239 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER
);
240 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER
);
241 pad
->buttons
[pad
->buttonsLength
].pressed
=
242 state
.Gamepad
.bLeftTrigger
>= XINPUT_GAMEPAD_TRIGGER_THRESHOLD
;
243 pad
->buttons
[pad
->buttonsLength
++].value
=
244 state
.Gamepad
.bLeftTrigger
/ 255.f
;
245 pad
->buttons
[pad
->buttonsLength
].pressed
=
246 state
.Gamepad
.bRightTrigger
>= XINPUT_GAMEPAD_TRIGGER_THRESHOLD
;
247 pad
->buttons
[pad
->buttonsLength
++].value
=
248 state
.Gamepad
.bRightTrigger
/ 255.f
;
249 ADD(XINPUT_GAMEPAD_BACK
);
250 ADD(XINPUT_GAMEPAD_START
);
251 ADD(XINPUT_GAMEPAD_LEFT_THUMB
);
252 ADD(XINPUT_GAMEPAD_RIGHT_THUMB
);
253 ADD(XINPUT_GAMEPAD_DPAD_UP
);
254 ADD(XINPUT_GAMEPAD_DPAD_DOWN
);
255 ADD(XINPUT_GAMEPAD_DPAD_LEFT
);
256 ADD(XINPUT_GAMEPAD_DPAD_RIGHT
);
259 // XInput are +up/+right, -down/-left, we want -up/-left.
260 pad
->axes
[pad
->axesLength
++] = NormalizeXInputAxis(state
.Gamepad
.sThumbLX
);
261 pad
->axes
[pad
->axesLength
++] = -NormalizeXInputAxis(state
.Gamepad
.sThumbLY
);
262 pad
->axes
[pad
->axesLength
++] = NormalizeXInputAxis(state
.Gamepad
.sThumbRX
);
263 pad
->axes
[pad
->axesLength
++] = -NormalizeXInputAxis(state
.Gamepad
.sThumbRY
);
265 pad
->connected
= false;
269 void GamepadPlatformDataFetcherWin::GetRawInputPadData(
272 RawGamepadInfo
* gamepad
= raw_input_fetcher_
->GetGamepadInfo(
273 pad_state_
[index
].raw_input_handle
);
275 pad
->connected
= false;
279 WebGamepad raw_pad
= *pad
;
281 raw_pad
.timestamp
= gamepad
->report_id
;
282 raw_pad
.buttonsLength
= gamepad
->buttons_length
;
283 raw_pad
.axesLength
= gamepad
->axes_length
;
285 for (unsigned int i
= 0; i
< raw_pad
.buttonsLength
; i
++) {
286 raw_pad
.buttons
[i
].pressed
= gamepad
->buttons
[i
];
287 raw_pad
.buttons
[i
].value
= gamepad
->buttons
[i
] ? 1.0 : 0.0;
290 for (unsigned int i
= 0; i
< raw_pad
.axesLength
; i
++)
291 raw_pad
.axes
[i
] = gamepad
->axes
[i
].value
;
293 // Copy to the current state to the output buffer, using the mapping
294 // function, if there is one available.
295 if (pad_state_
[index
].mapper
)
296 pad_state_
[index
].mapper(raw_pad
, pad
);
301 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() {
302 xinput_get_capabilities_
= NULL
;
303 xinput_get_state_
= NULL
;
304 xinput_enable_
= reinterpret_cast<XInputEnableFunc
>(
305 xinput_dll_
.GetFunctionPointer("XInputEnable"));
308 xinput_get_capabilities_
= reinterpret_cast<XInputGetCapabilitiesFunc
>(
309 xinput_dll_
.GetFunctionPointer("XInputGetCapabilities"));
310 if (!xinput_get_capabilities_
)
312 xinput_get_state_
= reinterpret_cast<XInputGetStateFunc
>(
313 xinput_dll_
.GetFunctionPointer("XInputGetState"));
314 if (!xinput_get_state_
)
316 xinput_enable_(true);
320 } // namespace content