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

Side by Side Diff: device/gamepad/gamepad_platform_data_fetcher_win.cc

Issue 2129003002: Refactored gamepad polling to support dynamic sources (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixed unit test issue and Mac XBoxDataFetcher constructor 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "device/gamepad/gamepad_platform_data_fetcher_win.h" 5 #include "device/gamepad/gamepad_platform_data_fetcher_win.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <string.h> 8 #include <string.h>
9 9
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/single_thread_task_runner.h"
10 #include "base/strings/stringprintf.h" 13 #include "base/strings/stringprintf.h"
11 #include "base/trace_event/trace_event.h" 14 #include "base/trace_event/trace_event.h"
12 #include "base/win/windows_version.h" 15 #include "base/win/windows_version.h"
13 16
14 namespace device { 17 namespace device {
15 18
16 using namespace blink; 19 using namespace blink;
17 20
18 namespace { 21 namespace {
19 22
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 return FILE_PATH_LITERAL("xinput9_1_0.dll"); 76 return FILE_PATH_LITERAL("xinput9_1_0.dll");
74 } else { 77 } else {
75 NOTREACHED(); 78 NOTREACHED();
76 return nullptr; 79 return nullptr;
77 } 80 }
78 } 81 }
79 82
80 } // namespace 83 } // namespace
81 84
82 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin() 85 GamepadPlatformDataFetcherWin::GamepadPlatformDataFetcherWin()
83 : xinput_dll_(base::FilePath(XInputDllFileName())), 86 : xinput_available_(false) {}
84 xinput_available_(GetXInputDllFunctions()) {
85 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
86 platform_pad_state_[i].status = DISCONNECTED;
87 pad_state_[i].mapper = NULL;
88 pad_state_[i].axis_mask = 0;
89 pad_state_[i].button_mask = 0;
90 }
91 87
92 raw_input_fetcher_.reset(new RawInputDataFetcher()); 88 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() {
93 raw_input_fetcher_->StartMonitor();
94 } 89 }
95 90
96 GamepadPlatformDataFetcherWin::~GamepadPlatformDataFetcherWin() { 91 GamepadSource GamepadPlatformDataFetcherWin::source() {
97 raw_input_fetcher_->StopMonitor(); 92 return Factory::static_source();
98 } 93 }
99 94
100 int GamepadPlatformDataFetcherWin::FirstAvailableGamepadId() const { 95 void GamepadPlatformDataFetcherWin::OnAddedToProvider() {
101 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { 96 xinput_dll_.Reset(
102 if (platform_pad_state_[i].status == DISCONNECTED) 97 base::LoadNativeLibrary(base::FilePath(XInputDllFileName()), nullptr));
103 return i; 98 xinput_available_ = GetXInputDllFunctions();
104 }
105 return -1;
106 }
107
108 bool GamepadPlatformDataFetcherWin::HasXInputGamepad(int index) const {
109 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
110 if (platform_pad_state_[i].status == XINPUT_CONNECTED &&
111 platform_pad_state_[i].xinput_index == index)
112 return true;
113 }
114 return false;
115 }
116
117 bool GamepadPlatformDataFetcherWin::HasRawInputGamepad(
118 const HANDLE handle) const {
119 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
120 if (platform_pad_state_[i].status == RAWINPUT_CONNECTED &&
121 platform_pad_state_[i].raw_input_handle == handle)
122 return true;
123 }
124 return false;
125 } 99 }
126 100
127 void GamepadPlatformDataFetcherWin::EnumerateDevices() { 101 void GamepadPlatformDataFetcherWin::EnumerateDevices() {
128 TRACE_EVENT0("GAMEPAD", "EnumerateDevices"); 102 TRACE_EVENT0("GAMEPAD", "EnumerateDevices");
129 103
130 // Mark all disconnected pads DISCONNECTED. 104 if (xinput_available_) {
131 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { 105 for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) {
132 if (!pad_state_[i].data.connected) 106 // Check to see if the xinput device is connected
133 platform_pad_state_[i].status = DISCONNECTED; 107 XINPUT_CAPABILITIES caps;
134 } 108 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps);
109 xinuput_connected_[i] = (res == ERROR_SUCCESS);
110 if (!xinuput_connected_[i])
111 continue;
135 112
136 for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) { 113 PadState* state = GetPadState(i);
137 if (HasXInputGamepad(i)) 114 if (!state)
138 continue; 115 continue; // No slot available for this gamepad.
139 int pad_index = FirstAvailableGamepadId();
140 if (pad_index == -1)
141 return; // We can't add any more gamepads.
142 WebGamepad& pad = pad_state_[pad_index].data;
143 if (xinput_available_ && GetXInputPadConnectivity(i, &pad)) {
144 platform_pad_state_[pad_index].status = XINPUT_CONNECTED;
145 platform_pad_state_[pad_index].xinput_index = i;
146 pad_state_[pad_index].mapper = NULL;
147 pad_state_[pad_index].axis_mask = 0;
148 pad_state_[pad_index].button_mask = 0;
149 }
150 }
151 116
152 if (raw_input_fetcher_->Available()) { 117 WebGamepad& pad = state->data;
153 std::vector<RawGamepadInfo*> raw_inputs =
154 raw_input_fetcher_->EnumerateDevices();
155 for (size_t i = 0; i < raw_inputs.size(); ++i) {
156 RawGamepadInfo* gamepad = raw_inputs[i];
157 if (gamepad->buttons_length == 0 && gamepad->axes_length == 0)
158 continue;
159 if (HasRawInputGamepad(gamepad->handle))
160 continue;
161 int pad_index = FirstAvailableGamepadId();
162 if (pad_index == -1)
163 return;
164 WebGamepad& pad = pad_state_[pad_index].data;
165 pad.connected = true;
166 PadState& state = pad_state_[pad_index];
167 PlatformPadState& platform_state = platform_pad_state_[pad_index];
168 platform_state.status = RAWINPUT_CONNECTED;
169 platform_state.raw_input_handle = gamepad->handle;
170 118
171 std::string vendor = base::StringPrintf("%04x", gamepad->vendor_id); 119 if (state->active_state == GAMEPAD_NEWLY_ACTIVE) {
172 std::string product = base::StringPrintf("%04x", gamepad->product_id); 120 // This is the first time we've seen this device, so do some one-time
173 state.mapper = GetGamepadStandardMappingFunction(vendor, product); 121 // initialization
174 state.axis_mask = 0; 122 pad.connected = true;
175 state.button_mask = 0; 123 swprintf(pad.id, WebGamepad::idLengthCap,
176 124 L"Xbox 360 Controller (XInput STANDARD %ls)",
177 swprintf(pad.id, WebGamepad::idLengthCap, 125 GamepadSubTypeName(caps.SubType));
178 L"%ls (%lsVendor: %04x Product: %04x)", gamepad->id,
179 state.mapper ? L"STANDARD GAMEPAD " : L"", gamepad->vendor_id,
180 gamepad->product_id);
181
182 if (state.mapper)
183 swprintf(pad.mapping, WebGamepad::mappingLengthCap, L"standard"); 126 swprintf(pad.mapping, WebGamepad::mappingLengthCap, L"standard");
184 else 127 }
185 pad.mapping[0] = 0;
186 } 128 }
187 } 129 }
188 } 130 }
189 131
190 void GamepadPlatformDataFetcherWin::GetGamepadData(WebGamepads* pads, 132 void GamepadPlatformDataFetcherWin::GetGamepadData(bool devices_changed_hint) {
191 bool devices_changed_hint) {
192 TRACE_EVENT0("GAMEPAD", "GetGamepadData"); 133 TRACE_EVENT0("GAMEPAD", "GetGamepadData");
193 134
194 if (!xinput_available_ && !raw_input_fetcher_->Available()) { 135 if (!xinput_available_)
195 pads->length = 0;
196 return; 136 return;
197 }
198 137
199 // A note on XInput devices: 138 // A note on XInput devices:
200 // If we got notification that system devices have been updated, then 139 // If we got notification that system devices have been updated, then
201 // run GetCapabilities to update the connected status and the device 140 // run GetCapabilities to update the connected status and the device
202 // identifier. It can be slow to do to both GetCapabilities and 141 // identifier. It can be slow to do to both GetCapabilities and
203 // GetState on unconnected devices, so we want to avoid a 2-5ms pause 142 // GetState on unconnected devices, so we want to avoid a 2-5ms pause
204 // here by only doing this when the devices are updated (despite 143 // here by only doing this when the devices are updated (despite
205 // documentation claiming it's OK to call it any time). 144 // documentation claiming it's OK to call it any time).
206 if (devices_changed_hint) 145 if (devices_changed_hint)
207 EnumerateDevices(); 146 EnumerateDevices();
208 147
209 pads->length = 0; 148 for (size_t i = 0; i < XUSER_MAX_COUNT; ++i) {
210 149 if (xinuput_connected_[i])
211 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { 150 GetXInputPadData(i);
212 // We rely on device_changed and GetCapabilities to tell us that
213 // something's been connected, but we will mark as disconnected if
214 // Get___PadState returns that we've lost the pad.
215 if (!pad_state_[i].data.connected)
216 continue;
217
218 if (platform_pad_state_[i].status == XINPUT_CONNECTED)
219 GetXInputPadData(i, &pad_state_[i].data);
220 else if (platform_pad_state_[i].status == RAWINPUT_CONNECTED)
221 GetRawInputPadData(i, &pad_state_[i].data);
222
223 MapAndSanitizeGamepadData(&pad_state_[i], &pads->items[i]);
224
225 if (pads->items[i].connected)
226 pads->length++;
227 } 151 }
228 } 152 }
229 153
230 void GamepadPlatformDataFetcherWin::PauseHint(bool pause) { 154 void GamepadPlatformDataFetcherWin::GetXInputPadData(int i) {
231 if (pause) 155 PadState* pad_state = provider()->GetPadState(GAMEPAD_SOURCE_WIN_XINPUT, i);
232 raw_input_fetcher_->StopMonitor(); 156 if (!pad_state)
233 else 157 return;
234 raw_input_fetcher_->StartMonitor();
235 }
236 158
237 bool GamepadPlatformDataFetcherWin::GetXInputPadConnectivity( 159 WebGamepad& pad = pad_state->data;
238 int i,
239 WebGamepad* pad) const {
240 DCHECK(pad);
241 TRACE_EVENT1("GAMEPAD", "GetXInputPadConnectivity", "id", i);
242 XINPUT_CAPABILITIES caps;
243 DWORD res = xinput_get_capabilities_(i, XINPUT_FLAG_GAMEPAD, &caps);
244 if (res == ERROR_DEVICE_NOT_CONNECTED) {
245 pad->connected = false;
246 return false;
247 } else {
248 pad->connected = true;
249 swprintf(pad->id, WebGamepad::idLengthCap,
250 L"Xbox 360 Controller (XInput STANDARD %ls)",
251 GamepadSubTypeName(caps.SubType));
252 swprintf(pad->mapping, WebGamepad::mappingLengthCap, L"standard");
253 return true;
254 }
255 }
256 160
257 void GamepadPlatformDataFetcherWin::GetXInputPadData(int i, WebGamepad* pad) {
258 XINPUT_STATE state; 161 XINPUT_STATE state;
259 memset(&state, 0, sizeof(XINPUT_STATE)); 162 memset(&state, 0, sizeof(XINPUT_STATE));
260 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i); 163 TRACE_EVENT_BEGIN1("GAMEPAD", "XInputGetState", "id", i);
261 DWORD dwResult = 164 DWORD dwResult = xinput_get_state_(i, &state);
262 xinput_get_state_(platform_pad_state_[i].xinput_index, &state);
263 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i); 165 TRACE_EVENT_END1("GAMEPAD", "XInputGetState", "id", i);
264 166
265 if (dwResult == ERROR_SUCCESS) { 167 if (dwResult == ERROR_SUCCESS) {
266 pad->timestamp = state.dwPacketNumber; 168 pad.timestamp = state.dwPacketNumber;
267 pad->buttonsLength = 0; 169 pad.buttonsLength = 0;
268 WORD val = state.Gamepad.wButtons; 170 WORD val = state.Gamepad.wButtons;
269 #define ADD(b) \ 171 #define ADD(b) \
270 pad->buttons[pad->buttonsLength].pressed = (val & (b)) != 0; \ 172 pad.buttons[pad.buttonsLength].pressed = (val & (b)) != 0; \
271 pad->buttons[pad->buttonsLength++].value = ((val & (b)) ? 1.f : 0.f); 173 pad.buttons[pad.buttonsLength++].value = ((val & (b)) ? 1.f : 0.f);
272 ADD(XINPUT_GAMEPAD_A); 174 ADD(XINPUT_GAMEPAD_A);
273 ADD(XINPUT_GAMEPAD_B); 175 ADD(XINPUT_GAMEPAD_B);
274 ADD(XINPUT_GAMEPAD_X); 176 ADD(XINPUT_GAMEPAD_X);
275 ADD(XINPUT_GAMEPAD_Y); 177 ADD(XINPUT_GAMEPAD_Y);
276 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER); 178 ADD(XINPUT_GAMEPAD_LEFT_SHOULDER);
277 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER); 179 ADD(XINPUT_GAMEPAD_RIGHT_SHOULDER);
278 180
279 pad->buttons[pad->buttonsLength].pressed = 181 pad.buttons[pad.buttonsLength].pressed =
280 state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD; 182 state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
281 pad->buttons[pad->buttonsLength++].value = 183 pad.buttons[pad.buttonsLength++].value = state.Gamepad.bLeftTrigger / 255.f;
282 state.Gamepad.bLeftTrigger / 255.f;
283 184
284 pad->buttons[pad->buttonsLength].pressed = 185 pad.buttons[pad.buttonsLength].pressed =
285 state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD; 186 state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
286 pad->buttons[pad->buttonsLength++].value = 187 pad.buttons[pad.buttonsLength++].value =
287 state.Gamepad.bRightTrigger / 255.f; 188 state.Gamepad.bRightTrigger / 255.f;
288 189
289 ADD(XINPUT_GAMEPAD_BACK); 190 ADD(XINPUT_GAMEPAD_BACK);
290 ADD(XINPUT_GAMEPAD_START); 191 ADD(XINPUT_GAMEPAD_START);
291 ADD(XINPUT_GAMEPAD_LEFT_THUMB); 192 ADD(XINPUT_GAMEPAD_LEFT_THUMB);
292 ADD(XINPUT_GAMEPAD_RIGHT_THUMB); 193 ADD(XINPUT_GAMEPAD_RIGHT_THUMB);
293 ADD(XINPUT_GAMEPAD_DPAD_UP); 194 ADD(XINPUT_GAMEPAD_DPAD_UP);
294 ADD(XINPUT_GAMEPAD_DPAD_DOWN); 195 ADD(XINPUT_GAMEPAD_DPAD_DOWN);
295 ADD(XINPUT_GAMEPAD_DPAD_LEFT); 196 ADD(XINPUT_GAMEPAD_DPAD_LEFT);
296 ADD(XINPUT_GAMEPAD_DPAD_RIGHT); 197 ADD(XINPUT_GAMEPAD_DPAD_RIGHT);
297 #undef ADD 198 #undef ADD
298 pad->axesLength = 0; 199 pad.axesLength = 0;
299 200
300 float value = 0.0; 201 float value = 0.0;
301 #define ADD(a, factor) \ 202 #define ADD(a, factor) \
302 value = factor * NormalizeXInputAxis(a); \ 203 value = factor * NormalizeXInputAxis(a); \
303 pad->axes[pad->axesLength++] = value; 204 pad.axes[pad.axesLength++] = value;
304 205
305 // XInput are +up/+right, -down/-left, we want -up/-left. 206 // XInput are +up/+right, -down/-left, we want -up/-left.
306 ADD(state.Gamepad.sThumbLX, 1); 207 ADD(state.Gamepad.sThumbLX, 1);
307 ADD(state.Gamepad.sThumbLY, -1); 208 ADD(state.Gamepad.sThumbLY, -1);
308 ADD(state.Gamepad.sThumbRX, 1); 209 ADD(state.Gamepad.sThumbRX, 1);
309 ADD(state.Gamepad.sThumbRY, -1); 210 ADD(state.Gamepad.sThumbRY, -1);
310 #undef ADD 211 #undef ADD
311 } else {
312 pad->connected = false;
313 } 212 }
314 } 213 }
315 214
316 void GamepadPlatformDataFetcherWin::GetRawInputPadData(int index,
317 WebGamepad* pad) {
318 RawGamepadInfo* gamepad = raw_input_fetcher_->GetGamepadInfo(
319 platform_pad_state_[index].raw_input_handle);
320 if (!gamepad) {
321 pad->connected = false;
322 return;
323 }
324
325 pad->timestamp = gamepad->report_id;
326 pad->buttonsLength = gamepad->buttons_length;
327 pad->axesLength = gamepad->axes_length;
328
329 for (unsigned int i = 0; i < pad->buttonsLength; i++) {
330 pad->buttons[i].pressed = gamepad->buttons[i];
331 pad->buttons[i].value = gamepad->buttons[i] ? 1.0 : 0.0;
332 }
333
334 for (unsigned int i = 0; i < pad->axesLength; i++)
335 pad->axes[i] = gamepad->axes[i].value;
336 }
337
338 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() { 215 bool GamepadPlatformDataFetcherWin::GetXInputDllFunctions() {
339 xinput_get_capabilities_ = NULL; 216 xinput_get_capabilities_ = NULL;
340 xinput_get_state_ = NULL; 217 xinput_get_state_ = NULL;
341 XInputEnableFunc xinput_enable = reinterpret_cast<XInputEnableFunc>( 218 XInputEnableFunc xinput_enable = reinterpret_cast<XInputEnableFunc>(
342 xinput_dll_.GetFunctionPointer("XInputEnable")); 219 xinput_dll_.GetFunctionPointer("XInputEnable"));
343 xinput_get_capabilities_ = reinterpret_cast<XInputGetCapabilitiesFunc>( 220 xinput_get_capabilities_ = reinterpret_cast<XInputGetCapabilitiesFunc>(
344 xinput_dll_.GetFunctionPointer("XInputGetCapabilities")); 221 xinput_dll_.GetFunctionPointer("XInputGetCapabilities"));
345 if (!xinput_get_capabilities_) 222 if (!xinput_get_capabilities_)
346 return false; 223 return false;
347 xinput_get_state_ = reinterpret_cast<XInputGetStateFunc>( 224 xinput_get_state_ = reinterpret_cast<XInputGetStateFunc>(
348 xinput_dll_.GetFunctionPointer("XInputGetState")); 225 xinput_dll_.GetFunctionPointer("XInputGetState"));
349 if (!xinput_get_state_) 226 if (!xinput_get_state_)
350 return false; 227 return false;
351 if (xinput_enable) { 228 if (xinput_enable) {
352 // XInputEnable is unavailable before Win8 and deprecated in Win10. 229 // XInputEnable is unavailable before Win8 and deprecated in Win10.
353 xinput_enable(true); 230 xinput_enable(true);
354 } 231 }
355 return true; 232 return true;
356 } 233 }
357 234
358 } // namespace device 235 } // namespace device
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698