Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(656)

Side by Side Diff: content/browser/gamepad/gamepad_platform_data_fetcher_win.cc

Issue 2081583002: Migrating majority of gamepad from content/browser/ to device/ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Next attempt Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "content/browser/gamepad/gamepad_platform_data_fetcher_win.h"
6
7 #include <stddef.h>
8 #include <string.h>
9
10 #include "base/strings/stringprintf.h"
11 #include "base/trace_event/trace_event.h"
12 #include "base/win/windows_version.h"
13 #include "content/common/gamepad_hardware_buffer.h"
14 #include "content/common/gamepad_messages.h"
15
16 namespace content {
17
18 using namespace blink;
19
20 namespace {
21
22 // See http://goo.gl/5VSJR. These are not available in all versions of the
23 // header, but they can be returned from the driver, so we define our own
24 // versions here.
25 static const BYTE kDeviceSubTypeGamepad = 1;
26 static const BYTE kDeviceSubTypeWheel = 2;
27 static const BYTE kDeviceSubTypeArcadeStick = 3;
28 static const BYTE kDeviceSubTypeFlightStick = 4;
29 static const BYTE kDeviceSubTypeDancePad = 5;
30 static const BYTE kDeviceSubTypeGuitar = 6;
31 static const BYTE kDeviceSubTypeGuitarAlternate = 7;
32 static const BYTE kDeviceSubTypeDrumKit = 8;
33 static const BYTE kDeviceSubTypeGuitarBass = 11;
34 static const BYTE kDeviceSubTypeArcadePad = 19;
35
36 float NormalizeXInputAxis(SHORT value) {
37 return ((value + 32768.f) / 32767.5f) - 1.f;
38 }
39
40 const WebUChar* GamepadSubTypeName(BYTE sub_type) {
41 switch (sub_type) {
42 case kDeviceSubTypeGamepad: return L"GAMEPAD";
43 case kDeviceSubTypeWheel: return L"WHEEL";
44 case kDeviceSubTypeArcadeStick: return L"ARCADE_STICK";
45 case kDeviceSubTypeFlightStick: return L"FLIGHT_STICK";
46 case kDeviceSubTypeDancePad: return L"DANCE_PAD";
47 case kDeviceSubTypeGuitar: return L"GUITAR";
48 case kDeviceSubTypeGuitarAlternate: return L"GUITAR_ALTERNATE";
49 case kDeviceSubTypeDrumKit: return L"DRUM_KIT";
50 case kDeviceSubTypeGuitarBass: return L"GUITAR_BASS";
51 case kDeviceSubTypeArcadePad: return L"ARCADE_PAD";
52 default: return L"<UNKNOWN>";
53 }
54 }
55
56 const WebUChar* XInputDllFileName() {
57 // Xinput.h defines filename (XINPUT_DLL) on different Windows versions, but
58 // Xinput.h specifies it in build time. Approach here uses the same values
59 // and it is resolving dll filename based on Windows version it is running on.
60 if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
61 // For Windows 8 and 10, XINPUT_DLL is xinput1_4.dll.
62 return FILE_PATH_LITERAL("xinput1_4.dll");
63 } else if (base::win::GetVersion() >= base::win::VERSION_WIN7) {
64 return FILE_PATH_LITERAL("xinput9_1_0.dll");
65 } else {
66 NOTREACHED();
67 return nullptr;
68 }
69 }
70
71 } // namespace
72
73 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin()
74 : xinput_dll_(base::FilePath(XInputDllFileName())),
75 xinput_available_(GetXInputDllFunctions()) {
76 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
77 platform_pad_state_[i].status = DISCONNECTED;
78 pad_state_[i].mapper = NULL;
79 pad_state_[i].axis_mask = 0;
80 pad_state_[i].button_mask = 0;
81 }
82
83 raw_input_fetcher_.reset(new RawInputDataFetcher());
84 raw_input_fetcher_->StartMonitor();
85 }
86
87 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() {
88 raw_input_fetcher_->StopMonitor();
89 }
90
91 int GamepadPlatformDataFetcherWin::FirstAvailableGamepadId() const {
92 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
93 if (platform_pad_state_[i].status == DISCONNECTED)
94 return i;
95 }
96 return -1;
97 }
98
99 bool GamepadPlatformDataFetcherWin::HasXInputGamepad(int index) const {
100 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
101 if (platform_pad_state_[i].status == XINPUT_CONNECTED &&
102 platform_pad_state_[i].xinput_index == index)
103 return true;
104 }
105 return false;
106 }
107
108 bool GamepadPlatformDataFetcherWin::HasRawInputGamepad(
109 const HANDLE handle) const {
110 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
111 if (platform_pad_state_[i].status == RAWINPUT_CONNECTED &&
112 platform_pad_state_[i].raw_input_handle == handle)
113 return true;
114 }
115 return false;
116 }
117
118 void GamepadPlatformDataFetcherWin::EnumerateDevices() {
119 TRACE_EVENT0("GAMEPAD", "EnumerateDevices");
120
121 // Mark all disconnected pads DISCONNECTED.
122 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
123 if (!pad_state_[i].data.connected)
124 platform_pad_state_[i].status = DISCONNECTED;
125 }
126
127 for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) {
128 if (HasXInputGamepad(i))
129 continue;
130 int pad_index = FirstAvailableGamepadId();
131 if (pad_index == -1)
132 return; // We can't add any more gamepads.
133 WebGamepad& pad = pad_state_[pad_index].data;
134 if (xinput_available_ && GetXInputPadConnectivity(i, &pad)) {
135 platform_pad_state_[pad_index].status = XINPUT_CONNECTED;
136 platform_pad_state_[pad_index].xinput_index = i;
137 pad_state_[pad_index].mapper = NULL;
138 pad_state_[pad_index].axis_mask = 0;
139 pad_state_[pad_index].button_mask = 0;
140 }
141 }
142
143 if (raw_input_fetcher_->Available()) {
144 std::vector<RawGamepadInfo*> raw_inputs =
145 raw_input_fetcher_->EnumerateDevices();
146 for (size_t i = 0; i < raw_inputs.size(); ++i) {
147 RawGamepadInfo* gamepad = raw_inputs[i];
148 if (gamepad->buttons_length == 0 && gamepad->axes_length == 0)
149 continue;
150 if (HasRawInputGamepad(gamepad->handle))
151 continue;
152 int pad_index = FirstAvailableGamepadId();
153 if (pad_index == -1)
154 return;
155 WebGamepad& pad = pad_state_[pad_index].data;
156 pad.connected = true;
157 PadState& state = pad_state_[pad_index];
158 PlatformPadState& platform_state = platform_pad_state_[pad_index];
159 platform_state.status = RAWINPUT_CONNECTED;
160 platform_state.raw_input_handle = gamepad->handle;
161
162 std::string vendor = base::StringPrintf("%04x", gamepad->vendor_id);
163 std::string product = base::StringPrintf("%04x", gamepad->product_id);
164 state.mapper = GetGamepadStandardMappingFunction(vendor, product);
165 state.axis_mask = 0;
166 state.button_mask = 0;
167
168 swprintf(pad.id, WebGamepad::idLengthCap,
169 L"%ls (%lsVendor: %04x Product: %04x)",
170 gamepad->id, state.mapper ? L"STANDARD GAMEPAD " : L"",
171 gamepad->vendor_id, gamepad->product_id);
172
173 if (state.mapper)
174 swprintf(pad.mapping, WebGamepad::mappingLengthCap, L"standard");
175 else
176 pad.mapping[0] = 0;
177 }
178 }
179 }
180
181 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads,
182 bool devices_changed_hint) {
183 TRACE_EVENT0("GAMEPAD", "GetGamepadData");
184
185 if (!xinput_available_ &&
186 !raw_input_fetcher_->Available()) {
187 pads->length = 0;
188 return;
189 }
190
191 // A note on XInput devices:
192 // If we got notification that system devices have been updated, then
193 // run GetCapabilities to update the connected status and the device
194 // identifier. It can be slow to do to both GetCapabilities and
195 // GetState on unconnected devices, so we want to avoid a 2-5ms pause
196 // here by only doing this when the devices are updated (despite
197 // documentation claiming it's OK to call it any time).
198 if (devices_changed_hint)
199 EnumerateDevices();
200
201 pads->length = 0;
202
203 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
204 // We rely on device_changed and GetCapabilities to tell us that
205 // something's been connected, but we will mark as disconnected if
206 // Get___PadState returns that we've lost the pad.
207 if (!pad_state_[i].data.connected)
208 continue;
209
210 if (platform_pad_state_[i].status == XINPUT_CONNECTED)
211 GetXInputPadData(i, &pad_state_[i].data);
212 else if (platform_pad_state_[i].status == RAWINPUT_CONNECTED)
213 GetRawInputPadData(i, &pad_state_[i].data);
214
215 MapAndSanitizeGamepadData(&pad_state_[i], &pads->items[i]);
216
217 if (pads->items[i].connected)
218 pads->length++;
219 }
220 }
221
222 void GamepadPlatformDataFetcherWin::PauseHint(bool pause) {
223 if (pause)
224 raw_input_fetcher_->StopMonitor();
225 else
226 raw_input_fetcher_->StartMonitor();
227 }
228
229 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity(
230 int i,
231 WebGamepad* pad) const {
232 DCHECK(pad);
233 TRACE_EVENT1("GAMEPAD", "GetXInputPadConnectivity", "id", i);
234 XINPUT_CAPABILITIES caps;
235 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps);
236 if (res == ERROR_DEVICE_NOT_CONNECTED) {
237 pad->connected = false;
238 return false;
239 } else {
240 pad->connected = true;
241 swprintf(pad->id,
242 WebGamepad::idLengthCap,
243 L"Xbox 360 Controller (XInput STANDARD %ls)",
244 GamepadSubTypeName(caps.SubType));
245 swprintf(pad->mapping, WebGamepad::mappingLengthCap, L"standard");
246 return true;
247 }
248 }
249
250 void GamepadPlatformDataFetcherWin::GetXInputPadData(
251 int i,
252 WebGamepad* pad) {
253 XINPUT_STATE state;
254 memset(&state, 0, sizeof(XINPUT_STATE));
255 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i);
256 DWORD dwResult = xinput_get_state_(platform_pad_state_[i].xinput_index,
257 &state);
258 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i);
259
260 if (dwResult == ERROR_SUCCESS) {
261 pad->timestamp = state.dwPacketNumber;
262 pad->buttonsLength = 0;
263 WORD val = state.Gamepad.wButtons;
264 #define ADD(b) pad->buttons[pad->buttonsLength].pressed = (val & (b)) != 0; \
265 pad->buttons[pad->buttonsLength++].value = ((val & (b)) ? 1.f : 0.f);
266 ADD(XINPUT_GAMEPAD_A);
267 ADD(XINPUT_GAMEPAD_B);
268 ADD(XINPUT_GAMEPAD_X);
269 ADD(XINPUT_GAMEPAD_Y);
270 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER);
271 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER);
272
273 pad->buttons[pad->buttonsLength].pressed =
274 state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
275 pad->buttons[pad->buttonsLength++].value =
276 state.Gamepad.bLeftTrigger / 255.f;
277
278 pad->buttons[pad->buttonsLength].pressed =
279 state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
280 pad->buttons[pad->buttonsLength++].value =
281 state.Gamepad.bRightTrigger / 255.f;
282
283 ADD(XINPUT_GAMEPAD_BACK);
284 ADD(XINPUT_GAMEPAD_START);
285 ADD(XINPUT_GAMEPAD_LEFT_THUMB);
286 ADD(XINPUT_GAMEPAD_RIGHT_THUMB);
287 ADD(XINPUT_GAMEPAD_DPAD_UP);
288 ADD(XINPUT_GAMEPAD_DPAD_DOWN);
289 ADD(XINPUT_GAMEPAD_DPAD_LEFT);
290 ADD(XINPUT_GAMEPAD_DPAD_RIGHT);
291 #undef ADD
292 pad->axesLength = 0;
293
294 float value = 0.0;
295 #define ADD(a, factor) value = factor * NormalizeXInputAxis(a); \
296 pad->axes[pad->axesLength++] = value;
297
298 // XInput are +up/+right, -down/-left, we want -up/-left.
299 ADD(state.Gamepad.sThumbLX, 1);
300 ADD(state.Gamepad.sThumbLY, -1);
301 ADD(state.Gamepad.sThumbRX, 1);
302 ADD(state.Gamepad.sThumbRY, -1);
303 #undef ADD
304 } else {
305 pad->connected = false;
306 }
307 }
308
309 void GamepadPlatformDataFetcherWin::GetRawInputPadData(
310 int index,
311 WebGamepad* pad) {
312 RawGamepadInfo* gamepad = raw_input_fetcher_->GetGamepadInfo(
313 platform_pad_state_[index].raw_input_handle);
314 if (!gamepad) {
315 pad->connected = false;
316 return;
317 }
318
319 pad->timestamp = gamepad->report_id;
320 pad->buttonsLength = gamepad->buttons_length;
321 pad->axesLength = gamepad->axes_length;
322
323 for (unsigned int i = 0; i < pad->buttonsLength; i++) {
324 pad->buttons[i].pressed = gamepad->buttons[i];
325 pad->buttons[i].value = gamepad->buttons[i] ? 1.0 : 0.0;
326 }
327
328 for (unsigned int i = 0; i < pad->axesLength; i++)
329 pad->axes[i] = gamepad->axes[i].value;
330 }
331
332 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() {
333 xinput_get_capabilities_ = NULL;
334 xinput_get_state_ = NULL;
335 XInputEnableFunc xinput_enable = reinterpret_cast<XInputEnableFunc>(
336 xinput_dll_.GetFunctionPointer("XInputEnable"));
337 xinput_get_capabilities_ = reinterpret_cast<XInputGetCapabilitiesFunc>(
338 xinput_dll_.GetFunctionPointer("XInputGetCapabilities"));
339 if (!xinput_get_capabilities_)
340 return false;
341 xinput_get_state_ = reinterpret_cast<XInputGetStateFunc>(
342 xinput_dll_.GetFunctionPointer("XInputGetState"));
343 if (!xinput_get_state_)
344 return false;
345 if (xinput_enable) {
346 // XInputEnable is unavailable before Win8 and deprecated in Win10.
347 xinput_enable(true);
348 }
349 return true;
350 }
351
352 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698