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
;
120 if (raw_input_fetcher_
->Available()) {
121 std::vector
<RawGamepadInfo
*> raw_inputs
=
122 raw_input_fetcher_
->EnumerateDevices();
123 for (size_t i
= 0; i
< raw_inputs
.size(); ++i
) {
124 RawGamepadInfo
* gamepad
= raw_inputs
[i
];
125 if (gamepad
->buttons_length
== 0 && gamepad
->axes_length
== 0)
127 if (HasRawInputGamepad(gamepad
->handle
))
129 int pad_index
= FirstAvailableGamepadId();
132 WebGamepad
& pad
= pads
->items
[pad_index
];
133 pad
.connected
= true;
134 PadState
& state
= pad_state_
[pad_index
];
135 state
.status
= RAWINPUT_CONNECTED
;
136 state
.raw_input_handle
= gamepad
->handle
;
138 std::string vendor
= base::StringPrintf("%04x", gamepad
->vendor_id
);
139 std::string product
= base::StringPrintf("%04x", gamepad
->product_id
);
140 state
.mapper
= GetGamepadStandardMappingFunction(vendor
, product
);
142 swprintf(pad
.id
, WebGamepad::idLengthCap
,
143 L
"%ls (%lsVendor: %04x Product: %04x)",
144 gamepad
->id
, state
.mapper
? L
"STANDARD GAMEPAD " : L
"",
145 gamepad
->vendor_id
, gamepad
->product_id
);
148 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
);
178 for (size_t i
= 0; i
< WebGamepads::itemsLengthCap
; ++i
) {
179 // We rely on device_changed and GetCapabilities to tell us that
180 // something's been connected, but we will mark as disconnected if
181 // Get___PadState returns that we've lost the pad.
182 if (!pads
->items
[i
].connected
)
185 if (pad_state_
[i
].status
== XINPUT_CONNECTED
)
186 GetXInputPadData(i
, &pads
->items
[i
]);
187 else if (pad_state_
[i
].status
== RAWINPUT_CONNECTED
)
188 GetRawInputPadData(i
, &pads
->items
[i
]);
190 if (pads
->items
[i
].connected
)
195 void GamepadPlatformDataFetcherWin::PauseHint(bool pause
) {
197 raw_input_fetcher_
->StopMonitor();
199 raw_input_fetcher_
->StartMonitor();
202 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity(
204 WebGamepad
* pad
) const {
206 TRACE_EVENT1("GAMEPAD", "GetXInputPadConnectivity", "id", i
);
207 XINPUT_CAPABILITIES caps
;
208 DWORD res
= xinput_get_capabilities_(i
, XINPUT_FLAG_GAMEPAD
, &caps
);
209 if (res
== ERROR_DEVICE_NOT_CONNECTED
) {
210 pad
->connected
= false;
213 pad
->connected
= true;
215 WebGamepad::idLengthCap
,
216 L
"Xbox 360 Controller (XInput STANDARD %ls)",
217 GamepadSubTypeName(caps
.SubType
));
218 swprintf(pad
->mapping
, WebGamepad::mappingLengthCap
, L
"standard");
223 void GamepadPlatformDataFetcherWin::GetXInputPadData(
227 memset(&state
, 0, sizeof(XINPUT_STATE
));
228 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i
);
229 DWORD dwResult
= xinput_get_state_(pad_state_
[i
].xinput_index
, &state
);
230 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i
);
232 if (dwResult
== ERROR_SUCCESS
) {
233 pad
->timestamp
= state
.dwPacketNumber
;
234 pad
->buttonsLength
= 0;
235 #define ADD(b) pad->buttons[pad->buttonsLength].pressed = \
236 (state.Gamepad.wButtons & (b)) != 0; \
237 pad->buttons[pad->buttonsLength++].value = \
238 ((state.Gamepad.wButtons & (b)) ? 1.f : 0.f);
239 ADD(XINPUT_GAMEPAD_A
);
240 ADD(XINPUT_GAMEPAD_B
);
241 ADD(XINPUT_GAMEPAD_X
);
242 ADD(XINPUT_GAMEPAD_Y
);
243 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER
);
244 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER
);
245 pad
->buttons
[pad
->buttonsLength
].pressed
=
246 state
.Gamepad
.bLeftTrigger
>= XINPUT_GAMEPAD_TRIGGER_THRESHOLD
;
247 pad
->buttons
[pad
->buttonsLength
++].value
=
248 state
.Gamepad
.bLeftTrigger
/ 255.f
;
249 pad
->buttons
[pad
->buttonsLength
].pressed
=
250 state
.Gamepad
.bRightTrigger
>= XINPUT_GAMEPAD_TRIGGER_THRESHOLD
;
251 pad
->buttons
[pad
->buttonsLength
++].value
=
252 state
.Gamepad
.bRightTrigger
/ 255.f
;
253 ADD(XINPUT_GAMEPAD_BACK
);
254 ADD(XINPUT_GAMEPAD_START
);
255 ADD(XINPUT_GAMEPAD_LEFT_THUMB
);
256 ADD(XINPUT_GAMEPAD_RIGHT_THUMB
);
257 ADD(XINPUT_GAMEPAD_DPAD_UP
);
258 ADD(XINPUT_GAMEPAD_DPAD_DOWN
);
259 ADD(XINPUT_GAMEPAD_DPAD_LEFT
);
260 ADD(XINPUT_GAMEPAD_DPAD_RIGHT
);
263 // XInput are +up/+right, -down/-left, we want -up/-left.
264 pad
->axes
[pad
->axesLength
++] = NormalizeXInputAxis(state
.Gamepad
.sThumbLX
);
265 pad
->axes
[pad
->axesLength
++] = -NormalizeXInputAxis(state
.Gamepad
.sThumbLY
);
266 pad
->axes
[pad
->axesLength
++] = NormalizeXInputAxis(state
.Gamepad
.sThumbRX
);
267 pad
->axes
[pad
->axesLength
++] = -NormalizeXInputAxis(state
.Gamepad
.sThumbRY
);
269 pad
->connected
= false;
273 void GamepadPlatformDataFetcherWin::GetRawInputPadData(
276 RawGamepadInfo
* gamepad
= raw_input_fetcher_
->GetGamepadInfo(
277 pad_state_
[index
].raw_input_handle
);
279 pad
->connected
= false;
283 WebGamepad raw_pad
= *pad
;
285 raw_pad
.timestamp
= gamepad
->report_id
;
286 raw_pad
.buttonsLength
= gamepad
->buttons_length
;
287 raw_pad
.axesLength
= gamepad
->axes_length
;
289 for (unsigned int i
= 0; i
< raw_pad
.buttonsLength
; i
++) {
290 raw_pad
.buttons
[i
].pressed
= gamepad
->buttons
[i
];
291 raw_pad
.buttons
[i
].value
= gamepad
->buttons
[i
] ? 1.0 : 0.0;
294 for (unsigned int i
= 0; i
< raw_pad
.axesLength
; i
++)
295 raw_pad
.axes
[i
] = gamepad
->axes
[i
].value
;
297 // Copy to the current state to the output buffer, using the mapping
298 // function, if there is one available.
299 if (pad_state_
[index
].mapper
)
300 pad_state_
[index
].mapper(raw_pad
, pad
);
305 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() {
306 xinput_get_capabilities_
= NULL
;
307 xinput_get_state_
= NULL
;
308 xinput_enable_
= reinterpret_cast<XInputEnableFunc
>(
309 xinput_dll_
.GetFunctionPointer("XInputEnable"));
312 xinput_get_capabilities_
= reinterpret_cast<XInputGetCapabilitiesFunc
>(
313 xinput_dll_
.GetFunctionPointer("XInputGetCapabilities"));
314 if (!xinput_get_capabilities_
)
316 xinput_get_state_
= reinterpret_cast<XInputGetStateFunc
>(
317 xinput_dll_
.GetFunctionPointer("XInputGetState"));
318 if (!xinput_get_state_
)
320 xinput_enable_(true);
324 } // namespace content