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

Side by Side Diff: content/browser/gamepad/raw_input_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 2014 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/raw_input_data_fetcher_win.h"
6
7 #include <stddef.h>
8
9 #include "base/macros.h"
10 #include "base/trace_event/trace_event.h"
11 #include "content/common/gamepad_hardware_buffer.h"
12 #include "content/common/gamepad_messages.h"
13
14 namespace content {
15
16 using namespace blink;
17
18 namespace {
19
20 float NormalizeAxis(long value, long min, long max) {
21 return (2.f * (value - min) / static_cast<float>(max - min)) - 1.f;
22 }
23
24 unsigned long GetBitmask(unsigned short bits) {
25 return (1 << bits) - 1;
26 }
27
28 // From the HID Usage Tables specification.
29 USHORT DeviceUsages[] = {
30 0x04, // Joysticks
31 0x05, // Gamepads
32 0x08, // Multi Axis
33 };
34
35 const uint32_t kAxisMinimumUsageNumber = 0x30;
36 const uint32_t kGameControlsUsagePage = 0x05;
37 const uint32_t kButtonUsagePage = 0x09;
38
39 } // namespace
40
41 RawGamepadInfo::RawGamepadInfo() {
42 }
43
44 RawGamepadInfo::~RawGamepadInfo() {
45 }
46
47 RawInputDataFetcher::RawInputDataFetcher()
48 : hid_dll_(base::FilePath(FILE_PATH_LITERAL("hid.dll"))),
49 rawinput_available_(GetHidDllFunctions()),
50 filter_xinput_(true),
51 events_monitored_(false) {
52 }
53
54 RawInputDataFetcher::~RawInputDataFetcher() {
55 ClearControllers();
56 DCHECK(!window_);
57 DCHECK(!events_monitored_);
58 }
59
60 void RawInputDataFetcher::WillDestroyCurrentMessageLoop() {
61 StopMonitor();
62 }
63
64 RAWINPUTDEVICE* RawInputDataFetcher::GetRawInputDevices(DWORD flags) {
65 size_t usage_count = arraysize(DeviceUsages);
66 std::unique_ptr<RAWINPUTDEVICE[]> devices(new RAWINPUTDEVICE[usage_count]);
67 for (size_t i = 0; i < usage_count; ++i) {
68 devices[i].dwFlags = flags;
69 devices[i].usUsagePage = 1;
70 devices[i].usUsage = DeviceUsages[i];
71 devices[i].hwndTarget = (flags & RIDEV_REMOVE) ? 0 : window_->hwnd();
72 }
73 return devices.release();
74 }
75
76 void RawInputDataFetcher::StartMonitor() {
77 if (!rawinput_available_ || events_monitored_)
78 return;
79
80 if (!window_) {
81 window_.reset(new base::win::MessageWindow());
82 if (!window_->Create(base::Bind(&RawInputDataFetcher::HandleMessage,
83 base::Unretained(this)))) {
84 PLOG(ERROR) << "Failed to create the raw input window";
85 window_.reset();
86 return;
87 }
88 }
89
90 // Register to receive raw HID input.
91 std::unique_ptr<RAWINPUTDEVICE[]> devices(
92 GetRawInputDevices(RIDEV_INPUTSINK));
93 if (!RegisterRawInputDevices(devices.get(), arraysize(DeviceUsages),
94 sizeof(RAWINPUTDEVICE))) {
95 PLOG(ERROR) << "RegisterRawInputDevices() failed for RIDEV_INPUTSINK";
96 window_.reset();
97 return;
98 }
99
100 // Start observing message loop destruction if we start monitoring the first
101 // event.
102 if (!events_monitored_)
103 base::MessageLoop::current()->AddDestructionObserver(this);
104
105 events_monitored_ = true;
106 }
107
108 void RawInputDataFetcher::StopMonitor() {
109 if (!rawinput_available_ || !events_monitored_)
110 return;
111
112 // Stop receiving raw input.
113 DCHECK(window_);
114 std::unique_ptr<RAWINPUTDEVICE[]> devices(GetRawInputDevices(RIDEV_REMOVE));
115
116 if (!RegisterRawInputDevices(devices.get(), arraysize(DeviceUsages),
117 sizeof(RAWINPUTDEVICE))) {
118 PLOG(INFO) << "RegisterRawInputDevices() failed for RIDEV_REMOVE";
119 }
120
121 events_monitored_ = false;
122 window_.reset();
123
124 // Stop observing message loop destruction if no event is being monitored.
125 base::MessageLoop::current()->RemoveDestructionObserver(this);
126 }
127
128 void RawInputDataFetcher::ClearControllers() {
129 while (!controllers_.empty()) {
130 RawGamepadInfo* gamepad_info = controllers_.begin()->second;
131 controllers_.erase(gamepad_info->handle);
132 delete gamepad_info;
133 }
134 }
135
136 std::vector<RawGamepadInfo*> RawInputDataFetcher::EnumerateDevices() {
137 std::vector<RawGamepadInfo*> valid_controllers;
138
139 ClearControllers();
140
141 UINT count = 0;
142 UINT result = GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST));
143 if (result == static_cast<UINT>(-1)) {
144 PLOG(ERROR) << "GetRawInputDeviceList() failed";
145 return valid_controllers;
146 }
147 DCHECK_EQ(0u, result);
148
149 std::unique_ptr<RAWINPUTDEVICELIST[]> device_list(
150 new RAWINPUTDEVICELIST[count]);
151 result = GetRawInputDeviceList(device_list.get(), &count,
152 sizeof(RAWINPUTDEVICELIST));
153 if (result == static_cast<UINT>(-1)) {
154 PLOG(ERROR) << "GetRawInputDeviceList() failed";
155 return valid_controllers;
156 }
157 DCHECK_EQ(count, result);
158
159 for (UINT i = 0; i < count; ++i) {
160 if (device_list[i].dwType == RIM_TYPEHID) {
161 HANDLE device_handle = device_list[i].hDevice;
162 RawGamepadInfo* gamepad_info = ParseGamepadInfo(device_handle);
163 if (gamepad_info) {
164 controllers_[device_handle] = gamepad_info;
165 valid_controllers.push_back(gamepad_info);
166 }
167 }
168 }
169 return valid_controllers;
170 }
171
172 RawGamepadInfo* RawInputDataFetcher::GetGamepadInfo(HANDLE handle) {
173 std::map<HANDLE, RawGamepadInfo*>::iterator it = controllers_.find(handle);
174 if (it != controllers_.end())
175 return it->second;
176
177 return NULL;
178 }
179
180 RawGamepadInfo* RawInputDataFetcher::ParseGamepadInfo(HANDLE hDevice) {
181 UINT size = 0;
182
183 // Do we already have this device in the map?
184 if (GetGamepadInfo(hDevice))
185 return NULL;
186
187 // Query basic device info.
188 UINT result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICEINFO,
189 NULL, &size);
190 if (result == static_cast<UINT>(-1)) {
191 PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
192 return NULL;
193 }
194 DCHECK_EQ(0u, result);
195
196 std::unique_ptr<uint8_t[]> di_buffer(new uint8_t[size]);
197 RID_DEVICE_INFO* device_info =
198 reinterpret_cast<RID_DEVICE_INFO*>(di_buffer.get());
199 result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICEINFO,
200 di_buffer.get(), &size);
201 if (result == static_cast<UINT>(-1)) {
202 PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
203 return NULL;
204 }
205 DCHECK_EQ(size, result);
206
207 // Make sure this device is of a type that we want to observe.
208 bool valid_type = false;
209 for (USHORT device_usage : DeviceUsages) {
210 if (device_info->hid.usUsage == device_usage) {
211 valid_type = true;
212 break;
213 }
214 }
215
216 if (!valid_type)
217 return NULL;
218
219 std::unique_ptr<RawGamepadInfo> gamepad_info(new RawGamepadInfo);
220 gamepad_info->handle = hDevice;
221 gamepad_info->report_id = 0;
222 gamepad_info->vendor_id = device_info->hid.dwVendorId;
223 gamepad_info->product_id = device_info->hid.dwProductId;
224 gamepad_info->buttons_length = 0;
225 ZeroMemory(gamepad_info->buttons, sizeof(gamepad_info->buttons));
226 gamepad_info->axes_length = 0;
227 ZeroMemory(gamepad_info->axes, sizeof(gamepad_info->axes));
228
229 // Query device identifier
230 result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME,
231 NULL, &size);
232 if (result == static_cast<UINT>(-1)) {
233 PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
234 return NULL;
235 }
236 DCHECK_EQ(0u, result);
237
238 std::unique_ptr<wchar_t[]> name_buffer(new wchar_t[size]);
239 result = GetRawInputDeviceInfo(hDevice, RIDI_DEVICENAME,
240 name_buffer.get(), &size);
241 if (result == static_cast<UINT>(-1)) {
242 PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
243 return NULL;
244 }
245 DCHECK_EQ(size, result);
246
247 // The presence of "IG_" in the device name indicates that this is an XInput
248 // Gamepad. Skip enumerating these devices and let the XInput path handle it.
249 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014.aspx
250 if (filter_xinput_ && wcsstr( name_buffer.get(), L"IG_" ) )
251 return NULL;
252
253 // Get a friendly device name
254 BOOLEAN got_product_string = FALSE;
255 HANDLE hid_handle = CreateFile(name_buffer.get(), GENERIC_READ|GENERIC_WRITE,
256 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
257 if (hid_handle) {
258 got_product_string = hidd_get_product_string_(hid_handle, gamepad_info->id,
259 sizeof(gamepad_info->id));
260 CloseHandle(hid_handle);
261 }
262
263 if (!got_product_string)
264 swprintf(gamepad_info->id, WebGamepad::idLengthCap, L"Unknown Gamepad");
265
266 // Query device capabilities.
267 result = GetRawInputDeviceInfo(hDevice, RIDI_PREPARSEDDATA,
268 NULL, &size);
269 if (result == static_cast<UINT>(-1)) {
270 PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
271 return NULL;
272 }
273 DCHECK_EQ(0u, result);
274
275 gamepad_info->ppd_buffer.reset(new uint8_t[size]);
276 gamepad_info->preparsed_data =
277 reinterpret_cast<PHIDP_PREPARSED_DATA>(gamepad_info->ppd_buffer.get());
278 result = GetRawInputDeviceInfo(hDevice, RIDI_PREPARSEDDATA,
279 gamepad_info->ppd_buffer.get(), &size);
280 if (result == static_cast<UINT>(-1)) {
281 PLOG(ERROR) << "GetRawInputDeviceInfo() failed";
282 return NULL;
283 }
284 DCHECK_EQ(size, result);
285
286 HIDP_CAPS caps;
287 NTSTATUS status = hidp_get_caps_(gamepad_info->preparsed_data, &caps);
288 DCHECK_EQ(HIDP_STATUS_SUCCESS, status);
289
290 // Query button information.
291 USHORT count = caps.NumberInputButtonCaps;
292 if (count > 0) {
293 std::unique_ptr<HIDP_BUTTON_CAPS[]> button_caps(
294 new HIDP_BUTTON_CAPS[count]);
295 status = hidp_get_button_caps_(
296 HidP_Input, button_caps.get(), &count, gamepad_info->preparsed_data);
297 DCHECK_EQ(HIDP_STATUS_SUCCESS, status);
298
299 for (uint32_t i = 0; i < count; ++i) {
300 if (button_caps[i].Range.UsageMin <= WebGamepad::buttonsLengthCap &&
301 button_caps[i].UsagePage == kButtonUsagePage) {
302 uint32_t max_index =
303 std::min(WebGamepad::buttonsLengthCap,
304 static_cast<size_t>(button_caps[i].Range.UsageMax));
305 gamepad_info->buttons_length = std::max(
306 gamepad_info->buttons_length, max_index);
307 }
308 }
309 }
310
311 // Query axis information.
312 count = caps.NumberInputValueCaps;
313 std::unique_ptr<HIDP_VALUE_CAPS[]> axes_caps(new HIDP_VALUE_CAPS[count]);
314 status = hidp_get_value_caps_(HidP_Input, axes_caps.get(), &count,
315 gamepad_info->preparsed_data);
316
317 bool mapped_all_axes = true;
318
319 for (UINT i = 0; i < count; i++) {
320 uint32_t axis_index = axes_caps[i].Range.UsageMin - kAxisMinimumUsageNumber;
321 if (axis_index < WebGamepad::axesLengthCap) {
322 gamepad_info->axes[axis_index].caps = axes_caps[i];
323 gamepad_info->axes[axis_index].value = 0;
324 gamepad_info->axes[axis_index].active = true;
325 gamepad_info->axes[axis_index].bitmask = GetBitmask(axes_caps[i].BitSize);
326 gamepad_info->axes_length =
327 std::max(gamepad_info->axes_length, axis_index + 1);
328 } else {
329 mapped_all_axes = false;
330 }
331 }
332
333 if (!mapped_all_axes) {
334 // For axes who's usage puts them outside the standard axesLengthCap range.
335 uint32_t next_index = 0;
336 for (UINT i = 0; i < count; i++) {
337 uint32_t usage = axes_caps[i].Range.UsageMin - kAxisMinimumUsageNumber;
338 if (usage >= WebGamepad::axesLengthCap &&
339 axes_caps[i].UsagePage <= kGameControlsUsagePage) {
340
341 for (; next_index < WebGamepad::axesLengthCap; ++next_index) {
342 if (!gamepad_info->axes[next_index].active)
343 break;
344 }
345 if (next_index < WebGamepad::axesLengthCap) {
346 gamepad_info->axes[next_index].caps = axes_caps[i];
347 gamepad_info->axes[next_index].value = 0;
348 gamepad_info->axes[next_index].active = true;
349 gamepad_info->axes[next_index].bitmask = GetBitmask(
350 axes_caps[i].BitSize);
351 gamepad_info->axes_length =
352 std::max(gamepad_info->axes_length, next_index + 1);
353 }
354 }
355
356 if (next_index >= WebGamepad::axesLengthCap)
357 break;
358 }
359 }
360
361 return gamepad_info.release();
362 }
363
364 void RawInputDataFetcher::UpdateGamepad(
365 RAWINPUT* input,
366 RawGamepadInfo* gamepad_info) {
367 NTSTATUS status;
368
369 gamepad_info->report_id++;
370
371 // Query button state.
372 if (gamepad_info->buttons_length) {
373 // Clear the button state
374 ZeroMemory(gamepad_info->buttons, sizeof(gamepad_info->buttons));
375 ULONG buttons_length = 0;
376
377 hidp_get_usages_ex_(HidP_Input,
378 0,
379 NULL,
380 &buttons_length,
381 gamepad_info->preparsed_data,
382 reinterpret_cast<PCHAR>(input->data.hid.bRawData),
383 input->data.hid.dwSizeHid);
384
385 std::unique_ptr<USAGE_AND_PAGE[]> usages(
386 new USAGE_AND_PAGE[buttons_length]);
387 status =
388 hidp_get_usages_ex_(HidP_Input,
389 0,
390 usages.get(),
391 &buttons_length,
392 gamepad_info->preparsed_data,
393 reinterpret_cast<PCHAR>(input->data.hid.bRawData),
394 input->data.hid.dwSizeHid);
395
396 if (status == HIDP_STATUS_SUCCESS) {
397 // Set each reported button to true.
398 for (uint32_t j = 0; j < buttons_length; j++) {
399 int32_t button_index = usages[j].Usage - 1;
400 if (usages[j].UsagePage == kButtonUsagePage &&
401 button_index >= 0 &&
402 button_index <
403 static_cast<int>(blink::WebGamepad::buttonsLengthCap)) {
404 gamepad_info->buttons[button_index] = true;
405 }
406 }
407 }
408 }
409
410 // Query axis state.
411 ULONG axis_value = 0;
412 LONG scaled_axis_value = 0;
413 for (uint32_t i = 0; i < gamepad_info->axes_length; i++) {
414 RawGamepadAxis* axis = &gamepad_info->axes[i];
415
416 // If the min is < 0 we have to query the scaled value, otherwise we need
417 // the normal unscaled value.
418 if (axis->caps.LogicalMin < 0) {
419 status = hidp_get_scaled_usage_value_(HidP_Input, axis->caps.UsagePage, 0,
420 axis->caps.Range.UsageMin, &scaled_axis_value,
421 gamepad_info->preparsed_data,
422 reinterpret_cast<PCHAR>(input->data.hid.bRawData),
423 input->data.hid.dwSizeHid);
424 if (status == HIDP_STATUS_SUCCESS) {
425 axis->value = NormalizeAxis(scaled_axis_value,
426 axis->caps.PhysicalMin, axis->caps.PhysicalMax);
427 }
428 } else {
429 status = hidp_get_usage_value_(HidP_Input, axis->caps.UsagePage, 0,
430 axis->caps.Range.UsageMin, &axis_value,
431 gamepad_info->preparsed_data,
432 reinterpret_cast<PCHAR>(input->data.hid.bRawData),
433 input->data.hid.dwSizeHid);
434 if (status == HIDP_STATUS_SUCCESS) {
435 axis->value = NormalizeAxis(axis_value & axis->bitmask,
436 axis->caps.LogicalMin & axis->bitmask,
437 axis->caps.LogicalMax & axis->bitmask);
438 }
439 }
440 }
441 }
442
443 LRESULT RawInputDataFetcher::OnInput(HRAWINPUT input_handle) {
444 // Get the size of the input record.
445 UINT size = 0;
446 UINT result = GetRawInputData(
447 input_handle, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
448 if (result == static_cast<UINT>(-1)) {
449 PLOG(ERROR) << "GetRawInputData() failed";
450 return 0;
451 }
452 DCHECK_EQ(0u, result);
453
454 // Retrieve the input record.
455 std::unique_ptr<uint8_t[]> buffer(new uint8_t[size]);
456 RAWINPUT* input = reinterpret_cast<RAWINPUT*>(buffer.get());
457 result = GetRawInputData(
458 input_handle, RID_INPUT, buffer.get(), &size, sizeof(RAWINPUTHEADER));
459 if (result == static_cast<UINT>(-1)) {
460 PLOG(ERROR) << "GetRawInputData() failed";
461 return 0;
462 }
463 DCHECK_EQ(size, result);
464
465 // Notify the observer about events generated locally.
466 if (input->header.dwType == RIM_TYPEHID && input->header.hDevice != NULL) {
467 RawGamepadInfo* gamepad = GetGamepadInfo(input->header.hDevice);
468 if (gamepad)
469 UpdateGamepad(input, gamepad);
470 }
471
472 return DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER));
473 }
474
475 bool RawInputDataFetcher::HandleMessage(UINT message,
476 WPARAM wparam,
477 LPARAM lparam,
478 LRESULT* result) {
479 switch (message) {
480 case WM_INPUT:
481 *result = OnInput(reinterpret_cast<HRAWINPUT>(lparam));
482 return true;
483
484 default:
485 return false;
486 }
487 }
488
489 bool RawInputDataFetcher::GetHidDllFunctions() {
490 hidp_get_caps_ = NULL;
491 hidp_get_button_caps_ = NULL;
492 hidp_get_value_caps_ = NULL;
493 hidp_get_usages_ex_ = NULL;
494 hidp_get_usage_value_ = NULL;
495 hidp_get_scaled_usage_value_ = NULL;
496 hidd_get_product_string_ = NULL;
497
498 if (!hid_dll_.is_valid()) return false;
499
500 hidp_get_caps_ = reinterpret_cast<HidPGetCapsFunc>(
501 hid_dll_.GetFunctionPointer("HidP_GetCaps"));
502 if (!hidp_get_caps_)
503 return false;
504 hidp_get_button_caps_ = reinterpret_cast<HidPGetButtonCapsFunc>(
505 hid_dll_.GetFunctionPointer("HidP_GetButtonCaps"));
506 if (!hidp_get_button_caps_)
507 return false;
508 hidp_get_value_caps_ = reinterpret_cast<HidPGetValueCapsFunc>(
509 hid_dll_.GetFunctionPointer("HidP_GetValueCaps"));
510 if (!hidp_get_value_caps_)
511 return false;
512 hidp_get_usages_ex_ = reinterpret_cast<HidPGetUsagesExFunc>(
513 hid_dll_.GetFunctionPointer("HidP_GetUsagesEx"));
514 if (!hidp_get_usages_ex_)
515 return false;
516 hidp_get_usage_value_ = reinterpret_cast<HidPGetUsageValueFunc>(
517 hid_dll_.GetFunctionPointer("HidP_GetUsageValue"));
518 if (!hidp_get_usage_value_)
519 return false;
520 hidp_get_scaled_usage_value_ = reinterpret_cast<HidPGetScaledUsageValueFunc>(
521 hid_dll_.GetFunctionPointer("HidP_GetScaledUsageValue"));
522 if (!hidp_get_scaled_usage_value_)
523 return false;
524 hidd_get_product_string_ = reinterpret_cast<HidDGetStringFunc>(
525 hid_dll_.GetFunctionPointer("HidD_GetProductString"));
526 if (!hidd_get_product_string_)
527 return false;
528
529 return true;
530 }
531
532 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698