// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/gamepad/hid_writer_win.h"
#include <Unknwn.h>
#include <windows.h>
#include <WinDef.h>
#include <stdint.h>
#include "base/strings/string_util_win.h"
namespace device {
HidWriterWin::HidWriterWin(HANDLE device) {
UINT size;
UINT result =
::GetRawInputDeviceInfo(device, RIDI_DEVICENAME, nullptr, &size);
if (result == 0U) {
std::wstring name_buffer;
result = ::GetRawInputDeviceInfo(
device, RIDI_DEVICENAME, base::WriteInto(&name_buffer, size), &size);
if (result == size) {
// Open the device handle for asynchronous I/O.
hid_handle_.Set(
::CreateFile(name_buffer.c_str(), GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr));
}
}
}
HidWriterWin::~HidWriterWin() = default;
size_t HidWriterWin::WriteOutputReport(base::span<const uint8_t> report) {
DCHECK_GE(report.size_bytes(), 1U);
if (!hid_handle_.IsValid())
return 0;
base::win::ScopedHandle event_handle(
::CreateEvent(nullptr, false, false, 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.data(), report.size_bytes(),
&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, 100);
if (wait_object == WAIT_OBJECT_0) {
::GetOverlappedResult(hid_handle_.Get(), &overlapped, &bytes_written,
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(2, handles, false, INFINITE);
}
}
}
}
return write_success ? bytes_written : 0;
}
} // namespace device