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.
5 #include "content/browser/gamepad/raw_input_data_fetcher_win.h"
7 #include "base/trace_event/trace_event.h"
8 #include "content/common/gamepad_hardware_buffer.h"
9 #include "content/common/gamepad_messages.h"
13 using namespace blink
;
17 float NormalizeAxis(long value
, long min
, long max
) {
18 return (2.f
* (value
- min
) / static_cast<float>(max
- min
)) - 1.f
;
21 unsigned long GetBitmask(unsigned short bits
) {
22 return (1 << bits
) - 1;
25 // From the HID Usage Tables specification.
26 USHORT DeviceUsages
[] = {
32 const uint32_t kAxisMinimumUsageNumber
= 0x30;
33 const uint32_t kGameControlsUsagePage
= 0x05;
37 RawGamepadInfo::RawGamepadInfo() {
40 RawGamepadInfo::~RawGamepadInfo() {
43 RawInputDataFetcher::RawInputDataFetcher()
44 : hid_dll_(base::FilePath(FILE_PATH_LITERAL("hid.dll"))),
45 rawinput_available_(GetHidDllFunctions()),
47 events_monitored_(false) {
50 RawInputDataFetcher::~RawInputDataFetcher() {
53 DCHECK(!events_monitored_
);
56 void RawInputDataFetcher::WillDestroyCurrentMessageLoop() {
60 RAWINPUTDEVICE
* RawInputDataFetcher::GetRawInputDevices(DWORD flags
) {
61 int usage_count
= arraysize(DeviceUsages
);
62 scoped_ptr
<RAWINPUTDEVICE
[]> devices(new RAWINPUTDEVICE
[usage_count
]);
63 for (int i
= 0; i
< usage_count
; ++i
) {
64 devices
[i
].dwFlags
= flags
;
65 devices
[i
].usUsagePage
= 1;
66 devices
[i
].usUsage
= DeviceUsages
[i
];
67 devices
[i
].hwndTarget
= (flags
& RIDEV_REMOVE
) ? 0 : window_
->hwnd();
69 return devices
.release();
72 void RawInputDataFetcher::StartMonitor() {
73 if (!rawinput_available_
|| events_monitored_
)
77 window_
.reset(new base::win::MessageWindow());
78 if (!window_
->Create(base::Bind(&RawInputDataFetcher::HandleMessage
,
79 base::Unretained(this)))) {
80 PLOG(ERROR
) << "Failed to create the raw input window";
86 // Register to receive raw HID input.
87 scoped_ptr
<RAWINPUTDEVICE
[]> devices(GetRawInputDevices(RIDEV_INPUTSINK
));
88 if (!RegisterRawInputDevices(devices
.get(), arraysize(DeviceUsages
),
89 sizeof(RAWINPUTDEVICE
))) {
90 PLOG(ERROR
) << "RegisterRawInputDevices() failed for RIDEV_INPUTSINK";
95 // Start observing message loop destruction if we start monitoring the first
97 if (!events_monitored_
)
98 base::MessageLoop::current()->AddDestructionObserver(this);
100 events_monitored_
= true;
103 void RawInputDataFetcher::StopMonitor() {
104 if (!rawinput_available_
|| !events_monitored_
)
107 // Stop receiving raw input.
109 scoped_ptr
<RAWINPUTDEVICE
[]> devices(GetRawInputDevices(RIDEV_REMOVE
));
111 if (!RegisterRawInputDevices(devices
.get(), arraysize(DeviceUsages
),
112 sizeof(RAWINPUTDEVICE
))) {
113 PLOG(INFO
) << "RegisterRawInputDevices() failed for RIDEV_REMOVE";
116 events_monitored_
= false;
119 // Stop observing message loop destruction if no event is being monitored.
120 base::MessageLoop::current()->RemoveDestructionObserver(this);
123 void RawInputDataFetcher::ClearControllers() {
124 while (!controllers_
.empty()) {
125 RawGamepadInfo
* gamepad_info
= controllers_
.begin()->second
;
126 controllers_
.erase(gamepad_info
->handle
);
131 std::vector
<RawGamepadInfo
*> RawInputDataFetcher::EnumerateDevices() {
132 std::vector
<RawGamepadInfo
*> valid_controllers
;
137 UINT result
= GetRawInputDeviceList(NULL
, &count
, sizeof(RAWINPUTDEVICELIST
));
138 if (result
== static_cast<UINT
>(-1)) {
139 PLOG(ERROR
) << "GetRawInputDeviceList() failed";
140 return valid_controllers
;
142 DCHECK_EQ(0u, result
);
144 scoped_ptr
<RAWINPUTDEVICELIST
[]> device_list(new RAWINPUTDEVICELIST
[count
]);
145 result
= GetRawInputDeviceList(device_list
.get(), &count
,
146 sizeof(RAWINPUTDEVICELIST
));
147 if (result
== static_cast<UINT
>(-1)) {
148 PLOG(ERROR
) << "GetRawInputDeviceList() failed";
149 return valid_controllers
;
151 DCHECK_EQ(count
, result
);
153 for (UINT i
= 0; i
< count
; ++i
) {
154 if (device_list
[i
].dwType
== RIM_TYPEHID
) {
155 HANDLE device_handle
= device_list
[i
].hDevice
;
156 RawGamepadInfo
* gamepad_info
= ParseGamepadInfo(device_handle
);
158 controllers_
[device_handle
] = gamepad_info
;
159 valid_controllers
.push_back(gamepad_info
);
163 return valid_controllers
;
166 RawGamepadInfo
* RawInputDataFetcher::GetGamepadInfo(HANDLE handle
) {
167 std::map
<HANDLE
, RawGamepadInfo
*>::iterator it
= controllers_
.find(handle
);
168 if (it
!= controllers_
.end())
174 RawGamepadInfo
* RawInputDataFetcher::ParseGamepadInfo(HANDLE hDevice
) {
177 // Do we already have this device in the map?
178 if (GetGamepadInfo(hDevice
))
181 // Query basic device info.
182 UINT result
= GetRawInputDeviceInfo(hDevice
, RIDI_DEVICEINFO
,
184 if (result
== static_cast<UINT
>(-1)) {
185 PLOG(ERROR
) << "GetRawInputDeviceInfo() failed";
188 DCHECK_EQ(0u, result
);
190 scoped_ptr
<uint8
[]> di_buffer(new uint8
[size
]);
191 RID_DEVICE_INFO
* device_info
=
192 reinterpret_cast<RID_DEVICE_INFO
*>(di_buffer
.get());
193 result
= GetRawInputDeviceInfo(hDevice
, RIDI_DEVICEINFO
,
194 di_buffer
.get(), &size
);
195 if (result
== static_cast<UINT
>(-1)) {
196 PLOG(ERROR
) << "GetRawInputDeviceInfo() failed";
199 DCHECK_EQ(size
, result
);
201 // Make sure this device is of a type that we want to observe.
202 bool valid_type
= false;
203 for (int i
= 0; i
< arraysize(DeviceUsages
); ++i
) {
204 if (device_info
->hid
.usUsage
== DeviceUsages
[i
]) {
213 scoped_ptr
<RawGamepadInfo
> gamepad_info(new RawGamepadInfo
);
214 gamepad_info
->handle
= hDevice
;
215 gamepad_info
->report_id
= 0;
216 gamepad_info
->vendor_id
= device_info
->hid
.dwVendorId
;
217 gamepad_info
->product_id
= device_info
->hid
.dwProductId
;
218 gamepad_info
->buttons_length
= 0;
219 ZeroMemory(gamepad_info
->buttons
, sizeof(gamepad_info
->buttons
));
220 gamepad_info
->axes_length
= 0;
221 ZeroMemory(gamepad_info
->axes
, sizeof(gamepad_info
->axes
));
223 // Query device identifier
224 result
= GetRawInputDeviceInfo(hDevice
, RIDI_DEVICENAME
,
226 if (result
== static_cast<UINT
>(-1)) {
227 PLOG(ERROR
) << "GetRawInputDeviceInfo() failed";
230 DCHECK_EQ(0u, result
);
232 scoped_ptr
<wchar_t[]> name_buffer(new wchar_t[size
]);
233 result
= GetRawInputDeviceInfo(hDevice
, RIDI_DEVICENAME
,
234 name_buffer
.get(), &size
);
235 if (result
== static_cast<UINT
>(-1)) {
236 PLOG(ERROR
) << "GetRawInputDeviceInfo() failed";
239 DCHECK_EQ(size
, result
);
241 // The presence of "IG_" in the device name indicates that this is an XInput
242 // Gamepad. Skip enumerating these devices and let the XInput path handle it.
243 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014.aspx
244 if (filter_xinput_
&& wcsstr( name_buffer
.get(), L
"IG_" ) )
247 // Get a friendly device name
248 BOOLEAN got_product_string
= FALSE
;
249 HANDLE hid_handle
= CreateFile(name_buffer
.get(), GENERIC_READ
|GENERIC_WRITE
,
250 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, NULL
, NULL
);
252 got_product_string
= hidd_get_product_string_(hid_handle
, gamepad_info
->id
,
253 sizeof(gamepad_info
->id
));
254 CloseHandle(hid_handle
);
257 if (!got_product_string
)
258 swprintf(gamepad_info
->id
, WebGamepad::idLengthCap
, L
"Unknown Gamepad");
260 // Query device capabilities.
261 result
= GetRawInputDeviceInfo(hDevice
, RIDI_PREPARSEDDATA
,
263 if (result
== static_cast<UINT
>(-1)) {
264 PLOG(ERROR
) << "GetRawInputDeviceInfo() failed";
267 DCHECK_EQ(0u, result
);
269 gamepad_info
->ppd_buffer
.reset(new uint8
[size
]);
270 gamepad_info
->preparsed_data
=
271 reinterpret_cast<PHIDP_PREPARSED_DATA
>(gamepad_info
->ppd_buffer
.get());
272 result
= GetRawInputDeviceInfo(hDevice
, RIDI_PREPARSEDDATA
,
273 gamepad_info
->ppd_buffer
.get(), &size
);
274 if (result
== static_cast<UINT
>(-1)) {
275 PLOG(ERROR
) << "GetRawInputDeviceInfo() failed";
278 DCHECK_EQ(size
, result
);
281 NTSTATUS status
= hidp_get_caps_(gamepad_info
->preparsed_data
, &caps
);
282 DCHECK_EQ(HIDP_STATUS_SUCCESS
, status
);
284 // Query button information.
285 USHORT count
= caps
.NumberInputButtonCaps
;
287 scoped_ptr
<HIDP_BUTTON_CAPS
[]> button_caps(new HIDP_BUTTON_CAPS
[count
]);
288 status
= hidp_get_button_caps_(
289 HidP_Input
, button_caps
.get(), &count
, gamepad_info
->preparsed_data
);
290 DCHECK_EQ(HIDP_STATUS_SUCCESS
, status
);
292 for (uint32_t i
= 0; i
< count
; ++i
) {
293 if (button_caps
[i
].Range
.UsageMin
<= WebGamepad::buttonsLengthCap
) {
295 std::min(WebGamepad::buttonsLengthCap
,
296 static_cast<size_t>(button_caps
[i
].Range
.UsageMax
));
297 gamepad_info
->buttons_length
= std::max(
298 gamepad_info
->buttons_length
, max_index
);
303 // Query axis information.
304 count
= caps
.NumberInputValueCaps
;
305 scoped_ptr
<HIDP_VALUE_CAPS
[]> axes_caps(new HIDP_VALUE_CAPS
[count
]);
306 status
= hidp_get_value_caps_(HidP_Input
, axes_caps
.get(), &count
,
307 gamepad_info
->preparsed_data
);
309 bool mapped_all_axes
= true;
311 for (UINT i
= 0; i
< count
; i
++) {
312 uint32_t axis_index
= axes_caps
[i
].Range
.UsageMin
- kAxisMinimumUsageNumber
;
313 if (axis_index
< WebGamepad::axesLengthCap
) {
314 gamepad_info
->axes
[axis_index
].caps
= axes_caps
[i
];
315 gamepad_info
->axes
[axis_index
].value
= 0;
316 gamepad_info
->axes
[axis_index
].active
= true;
317 gamepad_info
->axes
[axis_index
].bitmask
= GetBitmask(axes_caps
[i
].BitSize
);
318 gamepad_info
->axes_length
=
319 std::max(gamepad_info
->axes_length
, axis_index
+ 1);
321 mapped_all_axes
= false;
325 if (!mapped_all_axes
) {
326 // For axes who's usage puts them outside the standard axesLengthCap range.
327 uint32_t next_index
= 0;
328 for (UINT i
= 0; i
< count
; i
++) {
329 uint32_t usage
= axes_caps
[i
].Range
.UsageMin
- kAxisMinimumUsageNumber
;
330 if (usage
>= WebGamepad::axesLengthCap
&&
331 axes_caps
[i
].UsagePage
<= kGameControlsUsagePage
) {
333 for (; next_index
< WebGamepad::axesLengthCap
; ++next_index
) {
334 if (!gamepad_info
->axes
[next_index
].active
)
337 if (next_index
< WebGamepad::axesLengthCap
) {
338 gamepad_info
->axes
[next_index
].caps
= axes_caps
[i
];
339 gamepad_info
->axes
[next_index
].value
= 0;
340 gamepad_info
->axes
[next_index
].active
= true;
341 gamepad_info
->axes
[next_index
].bitmask
= GetBitmask(
342 axes_caps
[i
].BitSize
);
343 gamepad_info
->axes_length
=
344 std::max(gamepad_info
->axes_length
, next_index
+ 1);
348 if (next_index
>= WebGamepad::axesLengthCap
)
353 return gamepad_info
.release();
356 void RawInputDataFetcher::UpdateGamepad(
358 RawGamepadInfo
* gamepad_info
) {
361 gamepad_info
->report_id
++;
363 // Query button state.
364 if (gamepad_info
->buttons_length
) {
365 // Clear the button state
366 ZeroMemory(gamepad_info
->buttons
, sizeof(gamepad_info
->buttons
));
367 ULONG buttons_length
= 0;
369 hidp_get_usages_ex_(HidP_Input
,
373 gamepad_info
->preparsed_data
,
374 reinterpret_cast<PCHAR
>(input
->data
.hid
.bRawData
),
375 input
->data
.hid
.dwSizeHid
);
377 scoped_ptr
<USAGE_AND_PAGE
[]> usages(new USAGE_AND_PAGE
[buttons_length
]);
379 hidp_get_usages_ex_(HidP_Input
,
383 gamepad_info
->preparsed_data
,
384 reinterpret_cast<PCHAR
>(input
->data
.hid
.bRawData
),
385 input
->data
.hid
.dwSizeHid
);
387 if (status
== HIDP_STATUS_SUCCESS
) {
388 // Set each reported button to true.
389 for (uint32_t j
= 0; j
< buttons_length
; j
++) {
390 int32_t button_index
= usages
[j
].Usage
- 1;
391 if (button_index
>= 0 &&
392 button_index
< blink::WebGamepad::buttonsLengthCap
)
393 gamepad_info
->buttons
[button_index
] = true;
399 ULONG axis_value
= 0;
400 LONG scaled_axis_value
= 0;
401 for (uint32_t i
= 0; i
< gamepad_info
->axes_length
; i
++) {
402 RawGamepadAxis
* axis
= &gamepad_info
->axes
[i
];
404 // If the min is < 0 we have to query the scaled value, otherwise we need
405 // the normal unscaled value.
406 if (axis
->caps
.LogicalMin
< 0) {
407 status
= hidp_get_scaled_usage_value_(HidP_Input
, axis
->caps
.UsagePage
, 0,
408 axis
->caps
.Range
.UsageMin
, &scaled_axis_value
,
409 gamepad_info
->preparsed_data
,
410 reinterpret_cast<PCHAR
>(input
->data
.hid
.bRawData
),
411 input
->data
.hid
.dwSizeHid
);
412 if (status
== HIDP_STATUS_SUCCESS
) {
413 axis
->value
= NormalizeAxis(scaled_axis_value
,
414 axis
->caps
.PhysicalMin
, axis
->caps
.PhysicalMax
);
417 status
= hidp_get_usage_value_(HidP_Input
, axis
->caps
.UsagePage
, 0,
418 axis
->caps
.Range
.UsageMin
, &axis_value
,
419 gamepad_info
->preparsed_data
,
420 reinterpret_cast<PCHAR
>(input
->data
.hid
.bRawData
),
421 input
->data
.hid
.dwSizeHid
);
422 if (status
== HIDP_STATUS_SUCCESS
) {
423 axis
->value
= NormalizeAxis(axis_value
& axis
->bitmask
,
424 axis
->caps
.LogicalMin
& axis
->bitmask
,
425 axis
->caps
.LogicalMax
& axis
->bitmask
);
431 LRESULT
RawInputDataFetcher::OnInput(HRAWINPUT input_handle
) {
432 // Get the size of the input record.
434 UINT result
= GetRawInputData(
435 input_handle
, RID_INPUT
, NULL
, &size
, sizeof(RAWINPUTHEADER
));
436 if (result
== static_cast<UINT
>(-1)) {
437 PLOG(ERROR
) << "GetRawInputData() failed";
440 DCHECK_EQ(0u, result
);
442 // Retrieve the input record.
443 scoped_ptr
<uint8
[]> buffer(new uint8
[size
]);
444 RAWINPUT
* input
= reinterpret_cast<RAWINPUT
*>(buffer
.get());
445 result
= GetRawInputData(
446 input_handle
, RID_INPUT
, buffer
.get(), &size
, sizeof(RAWINPUTHEADER
));
447 if (result
== static_cast<UINT
>(-1)) {
448 PLOG(ERROR
) << "GetRawInputData() failed";
451 DCHECK_EQ(size
, result
);
453 // Notify the observer about events generated locally.
454 if (input
->header
.dwType
== RIM_TYPEHID
&& input
->header
.hDevice
!= NULL
) {
455 RawGamepadInfo
* gamepad
= GetGamepadInfo(input
->header
.hDevice
);
457 UpdateGamepad(input
, gamepad
);
460 return DefRawInputProc(&input
, 1, sizeof(RAWINPUTHEADER
));
463 bool RawInputDataFetcher::HandleMessage(UINT message
,
469 *result
= OnInput(reinterpret_cast<HRAWINPUT
>(lparam
));
477 bool RawInputDataFetcher::GetHidDllFunctions() {
478 hidp_get_caps_
= NULL
;
479 hidp_get_button_caps_
= NULL
;
480 hidp_get_value_caps_
= NULL
;
481 hidp_get_usages_ex_
= NULL
;
482 hidp_get_usage_value_
= NULL
;
483 hidp_get_scaled_usage_value_
= NULL
;
484 hidd_get_product_string_
= NULL
;
486 if (!hid_dll_
.is_valid()) return false;
488 hidp_get_caps_
= reinterpret_cast<HidPGetCapsFunc
>(
489 hid_dll_
.GetFunctionPointer("HidP_GetCaps"));
492 hidp_get_button_caps_
= reinterpret_cast<HidPGetButtonCapsFunc
>(
493 hid_dll_
.GetFunctionPointer("HidP_GetButtonCaps"));
494 if (!hidp_get_button_caps_
)
496 hidp_get_value_caps_
= reinterpret_cast<HidPGetValueCapsFunc
>(
497 hid_dll_
.GetFunctionPointer("HidP_GetValueCaps"));
498 if (!hidp_get_value_caps_
)
500 hidp_get_usages_ex_
= reinterpret_cast<HidPGetUsagesExFunc
>(
501 hid_dll_
.GetFunctionPointer("HidP_GetUsagesEx"));
502 if (!hidp_get_usages_ex_
)
504 hidp_get_usage_value_
= reinterpret_cast<HidPGetUsageValueFunc
>(
505 hid_dll_
.GetFunctionPointer("HidP_GetUsageValue"));
506 if (!hidp_get_usage_value_
)
508 hidp_get_scaled_usage_value_
= reinterpret_cast<HidPGetScaledUsageValueFunc
>(
509 hid_dll_
.GetFunctionPointer("HidP_GetScaledUsageValue"));
510 if (!hidp_get_scaled_usage_value_
)
512 hidd_get_product_string_
= reinterpret_cast<HidDGetStringFunc
>(
513 hid_dll_
.GetFunctionPointer("HidD_GetProductString"));
514 if (!hidd_get_product_string_
)
520 } // namespace content