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/debug/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 // From the HID Usage Tables specification.
22 USHORT DeviceUsages
[] = {
28 const uint32_t kAxisMinimumUsageNumber
= 0x30;
29 const uint32_t kGameControlsUsagePage
= 0x05;
33 RawInputDataFetcher::RawInputDataFetcher()
34 : hid_dll_(base::FilePath(FILE_PATH_LITERAL("hid.dll")))
35 , rawinput_available_(GetHidDllFunctions())
36 , filter_xinput_(true)
37 , events_monitored_(false) {}
39 RawInputDataFetcher::~RawInputDataFetcher() {
41 DCHECK(!events_monitored_
);
44 void RawInputDataFetcher::WillDestroyCurrentMessageLoop() {
48 RAWINPUTDEVICE
* RawInputDataFetcher::GetRawInputDevices(DWORD flags
) {
49 int usage_count
= arraysize(DeviceUsages
);
50 scoped_ptr
<RAWINPUTDEVICE
[]> devices(new RAWINPUTDEVICE
[usage_count
]);
51 for (int i
= 0; i
< usage_count
; ++i
) {
52 devices
[i
].dwFlags
= flags
;
53 devices
[i
].usUsagePage
= 1;
54 devices
[i
].usUsage
= DeviceUsages
[i
];
55 devices
[i
].hwndTarget
= window_
->hwnd();
57 return devices
.release();
60 void RawInputDataFetcher::StartMonitor() {
61 if (!rawinput_available_
|| events_monitored_
)
65 window_
.reset(new base::win::MessageWindow());
66 if (!window_
->Create(base::Bind(&RawInputDataFetcher::HandleMessage
,
67 base::Unretained(this)))) {
68 PLOG(ERROR
) << "Failed to create the raw input window";
74 // Register to receive raw HID input.
75 scoped_ptr
<RAWINPUTDEVICE
[]> devices(GetRawInputDevices(RIDEV_INPUTSINK
));
76 if (!RegisterRawInputDevices(devices
.get(), arraysize(DeviceUsages
),
77 sizeof(RAWINPUTDEVICE
))) {
78 PLOG(ERROR
) << "RegisterRawInputDevices() failed for RIDEV_INPUTSINK";
83 // Start observing message loop destruction if we start monitoring the first
85 if (!events_monitored_
)
86 base::MessageLoop::current()->AddDestructionObserver(this);
88 events_monitored_
= true;
91 void RawInputDataFetcher::StopMonitor() {
92 if (!rawinput_available_
|| !events_monitored_
)
95 // Stop receiving raw input.
97 scoped_ptr
<RAWINPUTDEVICE
[]> devices(GetRawInputDevices(RIDEV_REMOVE
));
99 if (!RegisterRawInputDevices(devices
.get(), arraysize(DeviceUsages
),
100 sizeof(RAWINPUTDEVICE
))) {
101 PLOG(INFO
) << "RegisterRawInputDevices() failed for RIDEV_REMOVE";
104 events_monitored_
= false;
108 // Stop observing message loop destruction if no event is being monitored.
109 base::MessageLoop::current()->RemoveDestructionObserver(this);
112 void RawInputDataFetcher::ClearControllers() {
113 while (!controllers_
.empty()) {
114 RawGamepadInfo
* gamepad_info
= controllers_
.begin()->second
;
115 controllers_
.erase(gamepad_info
->handle
);
120 std::vector
<RawGamepadInfo
*> RawInputDataFetcher::EnumerateDevices() {
121 std::vector
<RawGamepadInfo
*> valid_controllers
;
126 UINT result
= GetRawInputDeviceList(NULL
, &count
, sizeof(RAWINPUTDEVICELIST
));
127 if (result
== static_cast<UINT
>(-1)) {
128 PLOG(ERROR
) << "GetRawInputDeviceList() failed";
129 return valid_controllers
;
131 DCHECK_EQ(0u, result
);
133 scoped_ptr
<RAWINPUTDEVICELIST
[]> device_list(new RAWINPUTDEVICELIST
[count
]);
134 result
= GetRawInputDeviceList(device_list
.get(), &count
,
135 sizeof(RAWINPUTDEVICELIST
));
136 if (result
== static_cast<UINT
>(-1)) {
137 PLOG(ERROR
) << "GetRawInputDeviceList() failed";
138 return valid_controllers
;
140 DCHECK_EQ(count
, result
);
142 for (UINT i
= 0; i
< count
; ++i
) {
143 if (device_list
[i
].dwType
== RIM_TYPEHID
) {
144 HANDLE device_handle
= device_list
[i
].hDevice
;
145 RawGamepadInfo
* gamepad_info
= ParseGamepadInfo(device_handle
);
147 controllers_
[device_handle
] = gamepad_info
;
148 valid_controllers
.push_back(gamepad_info
);
152 return valid_controllers
;
155 RawGamepadInfo
* RawInputDataFetcher::GetGamepadInfo(HANDLE handle
) {
156 std::map
<HANDLE
, RawGamepadInfo
*>::iterator it
= controllers_
.find(handle
);
157 if (it
!= controllers_
.end())
163 RawGamepadInfo
* RawInputDataFetcher::ParseGamepadInfo(HANDLE hDevice
) {
166 // Do we already have this device in the map?
167 if (GetGamepadInfo(hDevice
))
170 // Query basic device info.
171 UINT result
= GetRawInputDeviceInfo(hDevice
, RIDI_DEVICEINFO
,
173 if (result
== static_cast<UINT
>(-1)) {
174 PLOG(ERROR
) << "GetRawInputDeviceInfo() failed";
177 DCHECK_EQ(0u, result
);
179 scoped_ptr
<uint8
[]> di_buffer(new uint8
[size
]);
180 RID_DEVICE_INFO
* device_info
=
181 reinterpret_cast<RID_DEVICE_INFO
*>(di_buffer
.get());
182 result
= GetRawInputDeviceInfo(hDevice
, RIDI_DEVICEINFO
,
183 di_buffer
.get(), &size
);
184 if (result
== static_cast<UINT
>(-1)) {
185 PLOG(ERROR
) << "GetRawInputDeviceInfo() failed";
188 DCHECK_EQ(size
, result
);
190 // Make sure this device is of a type that we want to observe.
191 bool valid_type
= false;
192 for (int i
= 0; i
< arraysize(DeviceUsages
); ++i
) {
193 if (device_info
->hid
.usUsage
== DeviceUsages
[i
]) {
202 scoped_ptr
<RawGamepadInfo
> gamepad_info(new RawGamepadInfo
);
203 gamepad_info
->handle
= hDevice
;
204 gamepad_info
->report_id
= 0;
205 gamepad_info
->vendor_id
= device_info
->hid
.dwVendorId
;
206 gamepad_info
->product_id
= device_info
->hid
.dwProductId
;
207 gamepad_info
->buttons_length
= 0;
208 ZeroMemory(gamepad_info
->buttons
, sizeof(gamepad_info
->buttons
));
209 gamepad_info
->axes_length
= 0;
210 ZeroMemory(gamepad_info
->axes
, sizeof(gamepad_info
->axes
));
212 // Query device identifier
213 result
= GetRawInputDeviceInfo(hDevice
, RIDI_DEVICENAME
,
215 if (result
== static_cast<UINT
>(-1)) {
216 PLOG(ERROR
) << "GetRawInputDeviceInfo() failed";
219 DCHECK_EQ(0u, result
);
221 scoped_ptr
<wchar_t[]> name_buffer(new wchar_t[size
]);
222 result
= GetRawInputDeviceInfo(hDevice
, RIDI_DEVICENAME
,
223 name_buffer
.get(), &size
);
224 if (result
== static_cast<UINT
>(-1)) {
225 PLOG(ERROR
) << "GetRawInputDeviceInfo() failed";
228 DCHECK_EQ(size
, result
);
230 // The presence of "IG_" in the device name indicates that this is an XInput
231 // Gamepad. Skip enumerating these devices and let the XInput path handle it.
232 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee417014.aspx
233 if (filter_xinput_
&& wcsstr( name_buffer
.get(), L
"IG_" ) )
236 // Get a friendly device name
237 BOOLEAN got_product_string
= FALSE
;
238 HANDLE hid_handle
= CreateFile(name_buffer
.get(), GENERIC_READ
|GENERIC_WRITE
,
239 FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, NULL
, NULL
);
241 got_product_string
= hidd_get_product_string_(hid_handle
, gamepad_info
->id
,
242 sizeof(gamepad_info
->id
));
243 CloseHandle(hid_handle
);
246 if (!got_product_string
)
247 swprintf(gamepad_info
->id
, WebGamepad::idLengthCap
, L
"Unknown Gamepad");
249 // Query device capabilities.
250 result
= GetRawInputDeviceInfo(hDevice
, RIDI_PREPARSEDDATA
,
252 if (result
== static_cast<UINT
>(-1)) {
253 PLOG(ERROR
) << "GetRawInputDeviceInfo() failed";
256 DCHECK_EQ(0u, result
);
258 gamepad_info
->ppd_buffer
.reset(new uint8
[size
]);
259 gamepad_info
->preparsed_data
=
260 reinterpret_cast<PHIDP_PREPARSED_DATA
>(gamepad_info
->ppd_buffer
.get());
261 result
= GetRawInputDeviceInfo(hDevice
, RIDI_PREPARSEDDATA
,
262 gamepad_info
->ppd_buffer
.get(), &size
);
263 if (result
== static_cast<UINT
>(-1)) {
264 PLOG(ERROR
) << "GetRawInputDeviceInfo() failed";
267 DCHECK_EQ(size
, result
);
270 NTSTATUS status
= hidp_get_caps_(gamepad_info
->preparsed_data
, &caps
);
271 DCHECK_EQ(HIDP_STATUS_SUCCESS
, status
);
273 // Query button information.
274 USHORT count
= caps
.NumberInputButtonCaps
;
276 scoped_ptr
<HIDP_BUTTON_CAPS
[]> button_caps(new HIDP_BUTTON_CAPS
[count
]);
277 status
= hidp_get_button_caps_(
278 HidP_Input
, button_caps
.get(), &count
, gamepad_info
->preparsed_data
);
279 DCHECK_EQ(HIDP_STATUS_SUCCESS
, status
);
281 for (uint32_t i
= 0; i
< count
; ++i
) {
282 if (button_caps
[i
].Range
.UsageMin
<= WebGamepad::buttonsLengthCap
) {
284 std::min(WebGamepad::buttonsLengthCap
,
285 static_cast<size_t>(button_caps
[i
].Range
.UsageMax
));
286 gamepad_info
->buttons_length
= std::max(
287 gamepad_info
->buttons_length
, max_index
);
292 // Query axis information.
293 count
= caps
.NumberInputValueCaps
;
294 scoped_ptr
<HIDP_VALUE_CAPS
[]> axes_caps(new HIDP_VALUE_CAPS
[count
]);
295 status
= hidp_get_value_caps_(HidP_Input
, axes_caps
.get(), &count
,
296 gamepad_info
->preparsed_data
);
298 bool mapped_all_axes
= true;
300 for (UINT i
= 0; i
< count
; i
++) {
301 uint32_t axis_index
= axes_caps
[i
].Range
.UsageMin
- kAxisMinimumUsageNumber
;
302 if (axis_index
< WebGamepad::axesLengthCap
) {
303 gamepad_info
->axes
[axis_index
].caps
= axes_caps
[i
];
304 gamepad_info
->axes
[axis_index
].value
= 0;
305 gamepad_info
->axes
[axis_index
].active
= true;
306 gamepad_info
->axes_length
=
307 std::max(gamepad_info
->axes_length
, axis_index
+ 1);
309 mapped_all_axes
= false;
313 if (!mapped_all_axes
) {
314 // For axes who's usage puts them outside the standard axesLengthCap range.
315 uint32_t next_index
= 0;
316 for (UINT i
= 0; i
< count
; i
++) {
317 uint32_t usage
= axes_caps
[i
].Range
.UsageMin
- kAxisMinimumUsageNumber
;
318 if (usage
>= WebGamepad::axesLengthCap
&&
319 axes_caps
[i
].UsagePage
<= kGameControlsUsagePage
) {
321 for (; next_index
< WebGamepad::axesLengthCap
; ++next_index
) {
322 if (!gamepad_info
->axes
[next_index
].active
)
325 if (next_index
< WebGamepad::axesLengthCap
) {
326 gamepad_info
->axes
[next_index
].caps
= axes_caps
[i
];
327 gamepad_info
->axes
[next_index
].value
= 0;
328 gamepad_info
->axes
[next_index
].active
= true;
329 gamepad_info
->axes_length
=
330 std::max(gamepad_info
->axes_length
, next_index
+ 1);
334 if (next_index
>= WebGamepad::axesLengthCap
)
339 return gamepad_info
.release();
342 void RawInputDataFetcher::UpdateGamepad(
344 RawGamepadInfo
* gamepad_info
) {
347 gamepad_info
->report_id
++;
349 // Query button state.
350 if (gamepad_info
->buttons_length
) {
351 // Clear the button state
352 ZeroMemory(gamepad_info
->buttons
, sizeof(gamepad_info
->buttons
));
353 ULONG buttons_length
= 0;
355 hidp_get_usages_ex_(HidP_Input
,
359 gamepad_info
->preparsed_data
,
360 reinterpret_cast<PCHAR
>(input
->data
.hid
.bRawData
),
361 input
->data
.hid
.dwSizeHid
);
363 scoped_ptr
<USAGE_AND_PAGE
[]> usages(new USAGE_AND_PAGE
[buttons_length
]);
365 hidp_get_usages_ex_(HidP_Input
,
369 gamepad_info
->preparsed_data
,
370 reinterpret_cast<PCHAR
>(input
->data
.hid
.bRawData
),
371 input
->data
.hid
.dwSizeHid
);
373 if (status
== HIDP_STATUS_SUCCESS
) {
374 // Set each reported button to true.
375 for (uint32_t j
= 0; j
< buttons_length
; j
++) {
376 int32_t button_index
= usages
[j
].Usage
- 1;
377 if (button_index
>= 0 &&
378 button_index
< blink::WebGamepad::buttonsLengthCap
)
379 gamepad_info
->buttons
[button_index
] = true;
385 ULONG axis_value
= 0;
386 LONG scaled_axis_value
= 0;
387 for (uint32_t i
= 0; i
< gamepad_info
->axes_length
; i
++) {
388 RawGamepadAxis
* axis
= &gamepad_info
->axes
[i
];
390 // If the min is < 0 we have to query the scaled value, otherwise we need
391 // the normal unscaled value.
392 if (axis
->caps
.LogicalMin
< 0) {
393 status
= hidp_get_scaled_usage_value_(HidP_Input
, axis
->caps
.UsagePage
, 0,
394 axis
->caps
.Range
.UsageMin
, &scaled_axis_value
,
395 gamepad_info
->preparsed_data
,
396 reinterpret_cast<PCHAR
>(input
->data
.hid
.bRawData
),
397 input
->data
.hid
.dwSizeHid
);
398 if (status
== HIDP_STATUS_SUCCESS
) {
399 axis
->value
= NormalizeAxis(scaled_axis_value
,
400 axis
->caps
.LogicalMin
, axis
->caps
.LogicalMax
);
403 status
= hidp_get_usage_value_(HidP_Input
, axis
->caps
.UsagePage
, 0,
404 axis
->caps
.Range
.UsageMin
, &axis_value
,
405 gamepad_info
->preparsed_data
,
406 reinterpret_cast<PCHAR
>(input
->data
.hid
.bRawData
),
407 input
->data
.hid
.dwSizeHid
);
408 if (status
== HIDP_STATUS_SUCCESS
) {
409 axis
->value
= NormalizeAxis(axis_value
,
410 axis
->caps
.LogicalMin
, axis
->caps
.LogicalMax
);
416 LRESULT
RawInputDataFetcher::OnInput(HRAWINPUT input_handle
) {
417 // Get the size of the input record.
419 UINT result
= GetRawInputData(
420 input_handle
, RID_INPUT
, NULL
, &size
, sizeof(RAWINPUTHEADER
));
421 if (result
== static_cast<UINT
>(-1)) {
422 PLOG(ERROR
) << "GetRawInputData() failed";
425 DCHECK_EQ(0u, result
);
427 // Retrieve the input record.
428 scoped_ptr
<uint8
[]> buffer(new uint8
[size
]);
429 RAWINPUT
* input
= reinterpret_cast<RAWINPUT
*>(buffer
.get());
430 result
= GetRawInputData(
431 input_handle
, RID_INPUT
, buffer
.get(), &size
, sizeof(RAWINPUTHEADER
));
432 if (result
== static_cast<UINT
>(-1)) {
433 PLOG(ERROR
) << "GetRawInputData() failed";
436 DCHECK_EQ(size
, result
);
438 // Notify the observer about events generated locally.
439 if (input
->header
.dwType
== RIM_TYPEHID
&& input
->header
.hDevice
!= NULL
) {
440 RawGamepadInfo
* gamepad
= GetGamepadInfo(input
->header
.hDevice
);
442 UpdateGamepad(input
, gamepad
);
445 return DefRawInputProc(&input
, 1, sizeof(RAWINPUTHEADER
));
448 bool RawInputDataFetcher::HandleMessage(UINT message
,
454 *result
= OnInput(reinterpret_cast<HRAWINPUT
>(lparam
));
462 bool RawInputDataFetcher::GetHidDllFunctions() {
463 hidp_get_caps_
= NULL
;
464 hidp_get_button_caps_
= NULL
;
465 hidp_get_value_caps_
= NULL
;
466 hidp_get_usages_ex_
= NULL
;
467 hidp_get_usage_value_
= NULL
;
468 hidp_get_scaled_usage_value_
= NULL
;
469 hidd_get_product_string_
= NULL
;
471 if (!hid_dll_
.is_valid()) return false;
473 hidp_get_caps_
= reinterpret_cast<HidPGetCapsFunc
>(
474 hid_dll_
.GetFunctionPointer("HidP_GetCaps"));
477 hidp_get_button_caps_
= reinterpret_cast<HidPGetButtonCapsFunc
>(
478 hid_dll_
.GetFunctionPointer("HidP_GetButtonCaps"));
479 if (!hidp_get_button_caps_
)
481 hidp_get_value_caps_
= reinterpret_cast<HidPGetValueCapsFunc
>(
482 hid_dll_
.GetFunctionPointer("HidP_GetValueCaps"));
483 if (!hidp_get_value_caps_
)
485 hidp_get_usages_ex_
= reinterpret_cast<HidPGetUsagesExFunc
>(
486 hid_dll_
.GetFunctionPointer("HidP_GetUsagesEx"));
487 if (!hidp_get_usages_ex_
)
489 hidp_get_usage_value_
= reinterpret_cast<HidPGetUsageValueFunc
>(
490 hid_dll_
.GetFunctionPointer("HidP_GetUsageValue"));
491 if (!hidp_get_usage_value_
)
493 hidp_get_scaled_usage_value_
= reinterpret_cast<HidPGetScaledUsageValueFunc
>(
494 hid_dll_
.GetFunctionPointer("HidP_GetScaledUsageValue"));
495 if (!hidp_get_scaled_usage_value_
)
497 hidd_get_product_string_
= reinterpret_cast<HidDGetStringFunc
>(
498 hid_dll_
.GetFunctionPointer("HidD_GetProductString"));
499 if (!hidd_get_product_string_
)
505 } // namespace content