Add ICU message format support
[chromium-blink-merge.git] / content / browser / gamepad / gamepad_platform_data_fetcher_win.cc
blobaca009605ed3c089039bff3ef0b9b1c783846cff
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;
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)
126 continue;
127 if (HasRawInputGamepad(gamepad->handle))
128 continue;
129 int pad_index = FirstAvailableGamepadId();
130 if (pad_index == -1)
131 return;
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);
147 if (state.mapper)
148 swprintf(pad.mapping, WebGamepad::mappingLengthCap, L"standard");
149 else
150 pad.mapping[0] = 0;
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()) {
163 pads->length = 0;
164 return;
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 pads->length = 0;
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)
183 continue;
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)
191 pads->length++;
195 void GamepadPlatformDataFetcherWin::PauseHint(bool pause) {
196 if (pause)
197 raw_input_fetcher_->StopMonitor();
198 else
199 raw_input_fetcher_->StartMonitor();
202 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity(
203 int i,
204 WebGamepad* pad) const {
205 DCHECK(pad);
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;
211 return false;
212 } else {
213 pad->connected = true;
214 swprintf(pad->id,
215 WebGamepad::idLengthCap,
216 L"Xbox 360 Controller (XInput STANDARD %ls)",
217 GamepadSubTypeName(caps.SubType));
218 swprintf(pad->mapping, WebGamepad::mappingLengthCap, L"standard");
219 return true;
223 void GamepadPlatformDataFetcherWin::GetXInputPadData(
224 int i,
225 WebGamepad* pad) {
226 XINPUT_STATE state;
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);
261 #undef ADD
262 pad->axesLength = 0;
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);
268 } else {
269 pad->connected = false;
273 void GamepadPlatformDataFetcherWin::GetRawInputPadData(
274 int index,
275 WebGamepad* pad) {
276 RawGamepadInfo* gamepad = raw_input_fetcher_->GetGamepadInfo(
277 pad_state_[index].raw_input_handle);
278 if (!gamepad) {
279 pad->connected = false;
280 return;
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);
301 else
302 *pad = raw_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"));
310 if (!xinput_enable_)
311 return false;
312 xinput_get_capabilities_ = reinterpret_cast<XInputGetCapabilitiesFunc>(
313 xinput_dll_.GetFunctionPointer("XInputGetCapabilities"));
314 if (!xinput_get_capabilities_)
315 return false;
316 xinput_get_state_ = reinterpret_cast<XInputGetStateFunc>(
317 xinput_dll_.GetFunctionPointer("XInputGetState"));
318 if (!xinput_get_state_)
319 return false;
320 xinput_enable_(true);
321 return true;
324 } // namespace content