Rename Animate as Begin(Main)Frame
[chromium-blink-merge.git] / content / browser / gamepad / gamepad_platform_data_fetcher_win.cc
blobc344e7e79d58c9da5f2ea04cca75b3284a813f25
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"
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 (HasRawInputGamepad(gamepad->handle))
127 continue;
128 int pad_index = FirstAvailableGamepadId();
129 if (pad_index == -1)
130 return;
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);
146 if (state.mapper)
147 swprintf(pad.mapping, WebGamepad::mappingLengthCap, L"standard");
148 else
149 pad.mapping[0] = 0;
151 pads->length++;
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 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)
182 continue;
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) {
192 if (pause)
193 raw_input_fetcher_->StopMonitor();
194 else
195 raw_input_fetcher_->StartMonitor();
198 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity(
199 int i,
200 WebGamepad* pad) const {
201 DCHECK(pad);
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;
207 return false;
208 } else {
209 pad->connected = true;
210 swprintf(pad->id,
211 WebGamepad::idLengthCap,
212 L"Xbox 360 Controller (XInput STANDARD %ls)",
213 GamepadSubTypeName(caps.SubType));
214 swprintf(pad->mapping, WebGamepad::mappingLengthCap, L"standard");
215 return true;
219 void GamepadPlatformDataFetcherWin::GetXInputPadData(
220 int i,
221 WebGamepad* pad) {
222 XINPUT_STATE state;
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);
257 #undef ADD
258 pad->axesLength = 0;
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);
264 } else {
265 pad->connected = false;
269 void GamepadPlatformDataFetcherWin::GetRawInputPadData(
270 int index,
271 WebGamepad* pad) {
272 RawGamepadInfo* gamepad = raw_input_fetcher_->GetGamepadInfo(
273 pad_state_[index].raw_input_handle);
274 if (!gamepad) {
275 pad->connected = false;
276 return;
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);
297 else
298 *pad = raw_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"));
306 if (!xinput_enable_)
307 return false;
308 xinput_get_capabilities_ = reinterpret_cast<XInputGetCapabilitiesFunc>(
309 xinput_dll_.GetFunctionPointer("XInputGetCapabilities"));
310 if (!xinput_get_capabilities_)
311 return false;
312 xinput_get_state_ = reinterpret_cast<XInputGetStateFunc>(
313 xinput_dll_.GetFunctionPointer("XInputGetState"));
314 if (!xinput_get_state_)
315 return false;
316 xinput_enable_(true);
317 return true;
320 } // namespace content