Use multiline attribute to check for IA2_STATE_MULTILINE.
[chromium-blink-merge.git] / content / browser / gamepad / gamepad_platform_data_fetcher_win.cc
blobfb6c126a9f2eabfbf430d71ff09ff6c6e7ec6570
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"
13 namespace content {
15 using namespace blink;
17 namespace {
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
21 // versions here.
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) {
38 switch (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>";
53 } // namespace
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)
72 return i;
74 return -1;
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)
81 return true;
83 return false;
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)
91 return true;
93 return false;
96 void GamepadPlatformDataFetcherWin::EnumerateDevices(
97 WebGamepads* pads) {
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))
108 continue;
109 int pad_index = FirstAvailableGamepadId();
110 if (pad_index == -1)
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;
117 pads->length++;
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)
127 continue;
128 if (HasRawInputGamepad(gamepad->handle))
129 continue;
130 int pad_index = FirstAvailableGamepadId();
131 if (pad_index == -1)
132 return;
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);
148 if (state.mapper)
149 swprintf(pad.mapping, WebGamepad::mappingLengthCap, L"standard");
150 else
151 pad.mapping[0] = 0;
153 pads->length++;
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()) {
165 pads->length = 0;
166 return;
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)
184 continue;
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) {
194 if (pause)
195 raw_input_fetcher_->StopMonitor();
196 else
197 raw_input_fetcher_->StartMonitor();
200 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity(
201 int i,
202 WebGamepad* pad) const {
203 DCHECK(pad);
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;
209 return false;
210 } else {
211 pad->connected = true;
212 swprintf(pad->id,
213 WebGamepad::idLengthCap,
214 L"Xbox 360 Controller (XInput STANDARD %ls)",
215 GamepadSubTypeName(caps.SubType));
216 swprintf(pad->mapping, WebGamepad::mappingLengthCap, L"standard");
217 return true;
221 void GamepadPlatformDataFetcherWin::GetXInputPadData(
222 int i,
223 WebGamepad* pad) {
224 XINPUT_STATE state;
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);
259 #undef ADD
260 pad->axesLength = 0;
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);
266 } else {
267 pad->connected = false;
271 void GamepadPlatformDataFetcherWin::GetRawInputPadData(
272 int index,
273 WebGamepad* pad) {
274 RawGamepadInfo* gamepad = raw_input_fetcher_->GetGamepadInfo(
275 pad_state_[index].raw_input_handle);
276 if (!gamepad) {
277 pad->connected = false;
278 return;
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);
299 else
300 *pad = raw_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"));
308 if (!xinput_enable_)
309 return false;
310 xinput_get_capabilities_ = reinterpret_cast<XInputGetCapabilitiesFunc>(
311 xinput_dll_.GetFunctionPointer("XInputGetCapabilities"));
312 if (!xinput_get_capabilities_)
313 return false;
314 xinput_get_state_ = reinterpret_cast<XInputGetStateFunc>(
315 xinput_dll_.GetFunctionPointer("XInputGetState"));
316 if (!xinput_get_state_)
317 return false;
318 xinput_enable_(true);
319 return true;
322 } // namespace content