blob: 71d031687c1bdcde1ea3a53d06d7e213b29aed5b [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/gamepad/hid_haptic_gamepad_win.h"
#include <Unknwn.h>
#include <WinDef.h>
#include <stdint.h>
#include <windows.h>
namespace device {
HidHapticGamepadWin::HidHapticGamepadWin(HANDLE device_handle,
const HapticReportData& data)
: HidHapticGamepadBase(data) {
// Fetch the size of the RIDI_DEVICENAME string.
UINT size;
UINT result = ::GetRawInputDeviceInfo(device_handle, RIDI_DEVICENAME,
/*pData=*/nullptr, &size);
if (result == 0U) {
// Read RIDI_DEVICENAME into a buffer.
std::unique_ptr<wchar_t[]> name_buffer(new wchar_t[size]);
result = ::GetRawInputDeviceInfo(device_handle, RIDI_DEVICENAME,
name_buffer.get(), &size);
if (result == size) {
// Open the device handle for asynchronous I/O.
hid_handle_.Set(::CreateFile(
name_buffer.get(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
/*lpSecurityAttributes=*/nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED,
/*hTemplateFile=*/nullptr));
}
}
}
HidHapticGamepadWin::~HidHapticGamepadWin() = default;
// static
std::unique_ptr<HidHapticGamepadWin> HidHapticGamepadWin::Create(
uint16_t vendor_id,
uint16_t product_id,
HANDLE device_handle) {
const auto* haptic_data = GetHapticReportData(vendor_id, product_id);
if (!haptic_data)
return nullptr;
return std::make_unique<HidHapticGamepadWin>(device_handle, *haptic_data);
}
void HidHapticGamepadWin::DoShutdown() {
hid_handle_.Close();
}
size_t HidHapticGamepadWin::WriteOutputReport(void* report,
size_t report_length) {
DCHECK(report);
DCHECK_GE(report_length, 1U);
if (!hid_handle_.IsValid())
return 0;
base::win::ScopedHandle event_handle(
::CreateEvent(/*lpEventAttributes=*/nullptr,
/*bManualReset=*/false,
/*bInitialState=*/false,
/*lpName=*/L""));
OVERLAPPED overlapped = {0};
overlapped.hEvent = event_handle.Get();
// Set up an asynchronous write.
DWORD bytes_written = 0;
BOOL write_success = ::WriteFile(hid_handle_.Get(), report, report_length,
&bytes_written, &overlapped);
if (!write_success) {
DWORD error = ::GetLastError();
if (error == ERROR_IO_PENDING) {
// Wait for the write to complete. This causes WriteOutputReport to behave
// synchronously.
DWORD wait_object = ::WaitForSingleObject(overlapped.hEvent,
/*dwMilliseconds=*/100);
if (wait_object == WAIT_OBJECT_0) {
::GetOverlappedResult(hid_handle_.Get(), &overlapped, &bytes_written,
/*bWait=*/true);
} else {
// Wait failed, or the timeout was exceeded before the write completed.
// Cancel the write request.
if (::CancelIo(hid_handle_.Get())) {
HANDLE handles[2];
handles[0] = hid_handle_.Get();
handles[1] = overlapped.hEvent;
::WaitForMultipleObjects(/*nCount=*/2, handles,
/*bWaitAll=*/false, INFINITE);
}
}
}
}
return write_success ? bytes_written : 0;
}
} // namespace device