mirror of
https://github.com/AxioDL/boo.git
synced 2025-12-13 23:26:16 +00:00
Win32 input device refinements and XInput support
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
#include <condition_variable>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <algorithm>
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
@@ -15,6 +16,10 @@
|
||||
#include <winusb.h>
|
||||
#include <usb100.h>
|
||||
#include <Winusbio.h>
|
||||
#include <hidsdi.h>
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
namespace boo
|
||||
{
|
||||
@@ -25,7 +30,8 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
||||
DeviceBase& m_devImp;
|
||||
|
||||
HANDLE m_devHandle = 0;
|
||||
WINUSB_INTERFACE_HANDLE m_usbHandle = NULL;
|
||||
HANDLE m_hidHandle = 0;
|
||||
WINUSB_INTERFACE_HANDLE m_usbHandle = nullptr;
|
||||
unsigned m_usbIntfInPipe = 0;
|
||||
unsigned m_usbIntfOutPipe = 0;
|
||||
bool m_runningTransferLoop = false;
|
||||
@@ -140,7 +146,6 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
||||
WinUsb_Free(device->m_usbHandle);
|
||||
CloseHandle(device->m_devHandle);
|
||||
device->m_devHandle = 0;
|
||||
|
||||
}
|
||||
|
||||
static void _threadProcBTLL(HIDDeviceWinUSB* device)
|
||||
@@ -157,24 +162,76 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp.transferCycle();
|
||||
device->m_devImp.finalCycle();
|
||||
|
||||
}
|
||||
|
||||
size_t m_minFeatureSz = 0;
|
||||
size_t m_minInputSz = 0;
|
||||
size_t m_minOutputSz = 0;
|
||||
|
||||
static void _threadProcHID(HIDDeviceWinUSB* device)
|
||||
{
|
||||
char errStr[256];
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
|
||||
/* POSIX.. who needs it?? -MS */
|
||||
device->m_hidHandle = CreateFileA(device->m_devPath.c_str(),
|
||||
GENERIC_WRITE | GENERIC_READ,
|
||||
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
||||
NULL);
|
||||
if (INVALID_HANDLE_VALUE == device->m_hidHandle)
|
||||
{
|
||||
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
||||
device->m_token.getProductName().c_str(),
|
||||
device->m_devPath.c_str(), GetLastError());
|
||||
device->m_devImp.deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
PHIDP_PREPARSED_DATA preparsedData;
|
||||
if (!HidD_GetPreparsedData(device->m_hidHandle, &preparsedData))
|
||||
{
|
||||
_snprintf(errStr, 256, "Unable get preparsed data of %s@%s: %d\n",
|
||||
device->m_token.getProductName().c_str(),
|
||||
device->m_devPath.c_str(), GetLastError());
|
||||
device->m_devImp.deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
HIDP_CAPS caps;
|
||||
HidP_GetCaps(preparsedData, &caps);
|
||||
device->m_minFeatureSz = caps.FeatureReportByteLength;
|
||||
device->m_minInputSz = caps.InputReportByteLength;
|
||||
device->m_minOutputSz = caps.OutputReportByteLength;
|
||||
HidD_FreePreparsedData(preparsedData);
|
||||
|
||||
/* Return control to main thread */
|
||||
device->m_runningTransferLoop = true;
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
||||
/* Allocate read buffer */
|
||||
size_t inBufferSz = std::max(device->m_minInputSz, device->m_devImp.getInputBufferSize());
|
||||
std::unique_ptr<uint8_t[]> readBuf(new uint8_t[inBufferSz]);
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp.initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
{
|
||||
device->ReadCycle(readBuf.get(), inBufferSz);
|
||||
device->m_devImp.transferCycle();
|
||||
}
|
||||
device->m_devImp.finalCycle();
|
||||
|
||||
/* Cleanup */
|
||||
CloseHandle(device->m_hidHandle);
|
||||
device->m_hidHandle = nullptr;
|
||||
}
|
||||
|
||||
void _deviceDisconnected()
|
||||
@@ -182,9 +239,84 @@ class HIDDeviceWinUSB final : public IHIDDevice
|
||||
m_runningTransferLoop = false;
|
||||
}
|
||||
|
||||
bool _sendHIDReport(const uint8_t* data, size_t length, uint16_t message)
|
||||
std::vector<uint8_t> m_sendBuf;
|
||||
std::vector<uint8_t> m_recvBuf;
|
||||
|
||||
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
return false;
|
||||
size_t maxOut = std::max(m_minFeatureSz, std::max(m_minOutputSz, length));
|
||||
if (m_sendBuf.size() < maxOut)
|
||||
m_sendBuf.resize(maxOut);
|
||||
if (maxOut > length)
|
||||
memset(m_sendBuf.data() + length, 0, maxOut - length);
|
||||
memmove(m_sendBuf.data(), data, length);
|
||||
|
||||
if (tp == HIDReportType::Output)
|
||||
{
|
||||
DWORD useLength = DWORD(std::max(length, m_minOutputSz));
|
||||
DWORD BytesWritten;
|
||||
OVERLAPPED Overlapped;
|
||||
ZeroMemory(&Overlapped, sizeof(Overlapped));
|
||||
BOOL Result = WriteFile(m_hidHandle, m_sendBuf.data(), useLength, &BytesWritten, &Overlapped);
|
||||
if (!Result)
|
||||
{
|
||||
DWORD Error = GetLastError();
|
||||
|
||||
if (Error == ERROR_INVALID_USER_BUFFER)
|
||||
{
|
||||
//std::cout << "Falling back to SetOutputReport" << std::endl;
|
||||
if (!HidD_SetOutputReport(m_hidHandle, (PVOID)m_sendBuf.data(), useLength))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Error != ERROR_IO_PENDING)
|
||||
{
|
||||
fprintf(stderr, "Write Failed %08X\n", Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GetOverlappedResult(m_hidHandle, &Overlapped, &BytesWritten, TRUE))
|
||||
{
|
||||
DWORD Error = GetLastError();
|
||||
fprintf(stderr, "Write Failed %08X\n", Error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (tp == HIDReportType::Feature)
|
||||
{
|
||||
DWORD useLength = DWORD(std::max(length, m_minFeatureSz));
|
||||
if (!HidD_SetFeature(m_hidHandle, (PVOID)m_sendBuf.data(), useLength))
|
||||
{
|
||||
int error = GetLastError();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
size_t maxIn = std::max(m_minFeatureSz, std::max(m_minInputSz, length));
|
||||
if (m_recvBuf.size() < maxIn)
|
||||
m_recvBuf.resize(maxIn);
|
||||
memset(m_recvBuf.data(), 0, length);
|
||||
m_recvBuf[0] = message;
|
||||
|
||||
if (tp == HIDReportType::Input)
|
||||
{
|
||||
if (!HidD_GetInputReport(m_hidHandle, m_recvBuf.data(), ULONG(std::max(m_minInputSz, length))))
|
||||
return 0;
|
||||
}
|
||||
else if (tp == HIDReportType::Feature)
|
||||
{
|
||||
if (!HidD_GetFeature(m_hidHandle, m_recvBuf.data(), ULONG(std::max(m_minFeatureSz, length))))
|
||||
return 0;
|
||||
}
|
||||
|
||||
memmove(data, m_recvBuf.data(), length);
|
||||
return length;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -194,14 +326,13 @@ public:
|
||||
m_devImp(devImp),
|
||||
m_devPath(token.getDevicePath())
|
||||
{
|
||||
devImp.m_hidDev = this;
|
||||
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||
DeviceToken::DeviceType dType = token.getDeviceType();
|
||||
if (dType == DeviceToken::DeviceType::USB)
|
||||
DeviceType dType = token.getDeviceType();
|
||||
if (dType == DeviceType::USB)
|
||||
m_thread = std::thread(_threadProcUSBLL, this);
|
||||
else if (dType == DeviceToken::DeviceType::Bluetooth)
|
||||
else if (dType == DeviceType::Bluetooth)
|
||||
m_thread = std::thread(_threadProcBTLL, this);
|
||||
else if (dType == DeviceToken::DeviceType::GenericHID)
|
||||
else if (dType == DeviceType::HID)
|
||||
m_thread = std::thread(_threadProcHID, this);
|
||||
else
|
||||
throw std::runtime_error("invalid token supplied to device constructor");
|
||||
@@ -214,12 +345,55 @@ public:
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
OVERLAPPED m_overlapped = {};
|
||||
|
||||
void ReadCycle(uint8_t* inBuffer, size_t inBufferSz)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ZeroMemory(inBuffer, inBufferSz);
|
||||
DWORD BytesRead = 0;
|
||||
BOOL Result = ReadFile(m_hidHandle, inBuffer, DWORD(inBufferSz), &BytesRead, &m_overlapped);
|
||||
if (!Result)
|
||||
{
|
||||
DWORD Error = GetLastError();
|
||||
if (Error != ERROR_IO_PENDING)
|
||||
{
|
||||
fprintf(stderr, "Read Failed: %08X\n", Error);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!GetOverlappedResult(m_hidHandle, &m_overlapped, &BytesRead, TRUE))
|
||||
{
|
||||
Error = GetLastError();
|
||||
if (Error == ERROR_IO_INCOMPLETE)
|
||||
return;
|
||||
fprintf(stderr, "Read Failed: %08X\n", Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_overlapped.Internal == STATUS_PENDING)
|
||||
{
|
||||
//std::cout << "Read Interrupted" << std::endl;
|
||||
if(!CancelIo(m_hidHandle))
|
||||
{
|
||||
Error = GetLastError();
|
||||
fprintf(stderr, "Cancel IO Failed: %08X\n", Error);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_devImp.receivedHIDReport(inBuffer, BytesRead, HIDReportType::Input, inBuffer[0]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
IHIDDevice* IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp)
|
||||
std::unique_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp)
|
||||
{
|
||||
return new HIDDeviceWinUSB(token, devImp);
|
||||
return std::make_unique<HIDDeviceWinUSB>(token, devImp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user