HID Parser for Win32

This commit is contained in:
Jack Andersen 2017-09-15 12:26:39 -10:00
parent 0f2a838bfb
commit 1f14cc09a1
12 changed files with 289 additions and 48 deletions

View File

@ -5,6 +5,12 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <memory> #include <memory>
#include <vector>
#include "boo/System.hpp"
#if _WIN32
#include <hidsdi.h>
#endif
namespace boo namespace boo
{ {
@ -45,7 +51,11 @@ public:
size_t receiveUSBInterruptTransfer(uint8_t* data, size_t length); size_t receiveUSBInterruptTransfer(uint8_t* data, size_t length);
/* High-Level API */ /* High-Level API */
#if _WIN32
const PHIDP_PREPARSED_DATA getReportDescriptor();
#else
std::vector<uint8_t> getReportDescriptor(); std::vector<uint8_t> getReportDescriptor();
#endif
bool sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message=0); bool sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message=0);
size_t receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message=0); // Prefer callback version size_t receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message=0); // Prefer callback version
virtual void receivedHIDReport(const uint8_t* /*data*/, size_t /*length*/, HIDReportType /*tp*/, uint32_t /*message*/) {} virtual void receivedHIDReport(const uint8_t* /*data*/, size_t /*length*/, HIDReportType /*tp*/, uint32_t /*message*/) {}

View File

@ -20,8 +20,6 @@
namespace boo namespace boo
{ {
static class DeviceFinder* skDevFinder = NULL;
class DeviceFinder class DeviceFinder
{ {
public: public:
@ -31,6 +29,7 @@ public:
static inline DeviceFinder* instance() {return skDevFinder;} static inline DeviceFinder* instance() {return skDevFinder;}
private: private:
static class DeviceFinder* skDevFinder;
/* Types this finder is interested in (immutable) */ /* Types this finder is interested in (immutable) */
DeviceSignature::TDeviceSignatureSet m_types; DeviceSignature::TDeviceSignatureSet m_types;

View File

@ -29,7 +29,7 @@ public:
void initialCycle(); void initialCycle();
void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message); void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message);
void enumerateValues(std::function<bool(const HIDMainItem& item)>& valueCB) const; void enumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const;
}; };
} }

View File

@ -5,6 +5,11 @@
#include <vector> #include <vector>
#include <stack> #include <stack>
#include <functional> #include <functional>
#include <memory>
#if _WIN32
#include <hidsdi.h>
#endif
namespace boo namespace boo
{ {
@ -157,6 +162,8 @@ struct HIDMainItem
HIDMainItem() = default; HIDMainItem() = default;
HIDMainItem(uint32_t flags, const HIDItemState& state, uint32_t reportIdx); HIDMainItem(uint32_t flags, const HIDItemState& state, uint32_t reportIdx);
HIDMainItem(uint32_t flags, HIDUsagePage usagePage, HIDUsage usage,
HIDRange logicalRange, int32_t reportSize);
const char* GetUsagePageName() const; const char* GetUsagePageName() const;
const char* GetUsageName() const; const char* GetUsageName() const;
}; };
@ -171,22 +178,35 @@ public:
Error Error
}; };
private: private:
ParserStatus m_status = ParserStatus::OK;
#if _WIN32
std::vector<HIDMainItem> m_itemPool;
mutable std::vector<HIDP_DATA> m_dataList;
PHIDP_PREPARSED_DATA m_descriptorData = nullptr;
#else
std::unique_ptr<HIDMainItem[]> m_itemPool; std::unique_ptr<HIDMainItem[]> m_itemPool;
using Report = std::pair<uint32_t, std::pair<uint32_t, uint32_t>>; using Report = std::pair<uint32_t, std::pair<uint32_t, uint32_t>>;
std::unique_ptr<Report[]> m_reportPool; std::unique_ptr<Report[]> m_reportPool;
std::pair<uint32_t, uint32_t> m_inputReports = {}; std::pair<uint32_t, uint32_t> m_inputReports = {};
std::pair<uint32_t, uint32_t> m_outputReports = {}; std::pair<uint32_t, uint32_t> m_outputReports = {};
std::pair<uint32_t, uint32_t> m_featureReports = {}; std::pair<uint32_t, uint32_t> m_featureReports = {};
ParserStatus m_status = ParserStatus::OK;
bool m_multipleReports = false; bool m_multipleReports = false;
ParserStatus ParseItem(HIDReports& reportsOut, ParserStatus ParseItem(HIDReports& reportsOut,
std::stack<HIDItemState>& stateStack, std::stack<HIDCollectionItem>& collectionStack, std::stack<HIDItemState>& stateStack,
std::stack<HIDCollectionItem>& collectionStack,
const uint8_t*& it, const uint8_t* end); const uint8_t*& it, const uint8_t* end);
#endif
public: public:
#if _WIN32
ParserStatus Parse(const PHIDP_PREPARSED_DATA descriptorData);
#else
ParserStatus Parse(const uint8_t* descriptorData, size_t len); ParserStatus Parse(const uint8_t* descriptorData, size_t len);
#endif
operator bool() const { return m_status == ParserStatus::Done; } operator bool() const { return m_status == ParserStatus::Done; }
void EnumerateValues(std::function<bool(uint32_t rep, const HIDMainItem& item)>& valueCB) const; void EnumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const;
void ScanValues(std::function<bool(const HIDMainItem& item, int32_t value)>& valueCB, void ScanValues(const std::function<bool(const HIDMainItem& item, int32_t value)>& valueCB,
const uint8_t* data, size_t len) const; const uint8_t* data, size_t len) const;
}; };

View File

@ -54,12 +54,21 @@ size_t DeviceBase::receiveUSBInterruptTransfer(uint8_t* data, size_t length)
return false; return false;
} }
#if _WIN32
const PHIDP_PREPARSED_DATA DeviceBase::getReportDescriptor()
{
if (m_hidDev)
return m_hidDev->_getReportDescriptor();
return {};
}
#else
std::vector<uint8_t> DeviceBase::getReportDescriptor() std::vector<uint8_t> DeviceBase::getReportDescriptor()
{ {
if (m_hidDev) if (m_hidDev)
return m_hidDev->_getReportDescriptor(); return m_hidDev->_getReportDescriptor();
return {}; return {};
} }
#endif
bool DeviceBase::sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) bool DeviceBase::sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
{ {

View File

@ -9,6 +9,8 @@
namespace boo namespace boo
{ {
DeviceFinder* DeviceFinder::skDevFinder = nullptr;
#if _WIN32 #if _WIN32
/* Windows-specific WM_DEVICECHANGED handler */ /* Windows-specific WM_DEVICECHANGED handler */
LRESULT DeviceFinder::winDevChangedHandler(WPARAM wParam, LPARAM lParam) LRESULT DeviceFinder::winDevChangedHandler(WPARAM wParam, LPARAM lParam)

View File

@ -20,8 +20,13 @@ void GenericPad::deviceDisconnected()
void GenericPad::initialCycle() void GenericPad::initialCycle()
{ {
#if _WIN32
const PHIDP_PREPARSED_DATA reportDesc = getReportDescriptor();
m_parser.Parse(reportDesc);
#else
std::vector<uint8_t> reportDesc = getReportDescriptor(); std::vector<uint8_t> reportDesc = getReportDescriptor();
m_parser.Parse(reportDesc.data(), reportDesc.size()); m_parser.Parse(reportDesc.data(), reportDesc.size());
#endif
if (m_cb) if (m_cb)
m_cb->controllerConnected(); m_cb->controllerConnected();
} }
@ -39,11 +44,9 @@ void GenericPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReport
m_parser.ScanValues(func, data, length); m_parser.ScanValues(func, data, length);
} }
void GenericPad::enumerateValues(std::function<bool(const HIDMainItem& item)>& valueCB) const void GenericPad::enumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const
{ {
std::function<bool(uint32_t, const HIDMainItem&)> func = m_parser.EnumerateValues(valueCB);
[&](uint32_t rep, const HIDMainItem& item) { return valueCB(item); };
m_parser.EnumerateValues(func);
} }
} }

View File

@ -27,7 +27,7 @@ namespace boo
class HIDDeviceWinUSB final : public IHIDDevice class HIDDeviceWinUSB final : public IHIDDevice
{ {
DeviceToken& m_token; DeviceToken& m_token;
DeviceBase& m_devImp; std::shared_ptr<DeviceBase> m_devImp;
HANDLE m_devHandle = 0; HANDLE m_devHandle = 0;
HANDLE m_hidHandle = 0; HANDLE m_hidHandle = 0;
@ -68,7 +68,7 @@ class HIDDeviceWinUSB final : public IHIDDevice
return 0; return 0;
} }
static void _threadProcUSBLL(HIDDeviceWinUSB* device) static void _threadProcUSBLL(std::shared_ptr<HIDDeviceWinUSB> device)
{ {
unsigned i; unsigned i;
char errStr[256]; char errStr[256];
@ -87,7 +87,7 @@ class HIDDeviceWinUSB final : public IHIDDevice
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n", _snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
device->m_token.getProductName().c_str(), device->m_token.getProductName().c_str(),
device->m_devPath.c_str(), GetLastError()); device->m_devPath.c_str(), GetLastError());
device->m_devImp.deviceError(errStr); device->m_devImp->deviceError(errStr);
lk.unlock(); lk.unlock();
device->m_initCond.notify_one(); device->m_initCond.notify_one();
return; return;
@ -98,7 +98,7 @@ class HIDDeviceWinUSB final : public IHIDDevice
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n", _snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
device->m_token.getProductName().c_str(), device->m_token.getProductName().c_str(),
device->m_devPath.c_str(), GetLastError()); device->m_devPath.c_str(), GetLastError());
device->m_devImp.deviceError(errStr); device->m_devImp->deviceError(errStr);
lk.unlock(); lk.unlock();
device->m_initCond.notify_one(); device->m_initCond.notify_one();
CloseHandle(device->m_devHandle); CloseHandle(device->m_devHandle);
@ -112,7 +112,7 @@ class HIDDeviceWinUSB final : public IHIDDevice
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n", _snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
device->m_token.getProductName().c_str(), device->m_token.getProductName().c_str(),
device->m_devPath.c_str(), GetLastError()); device->m_devPath.c_str(), GetLastError());
device->m_devImp.deviceError(errStr); device->m_devImp->deviceError(errStr);
lk.unlock(); lk.unlock();
device->m_initCond.notify_one(); device->m_initCond.notify_one();
CloseHandle(device->m_devHandle); CloseHandle(device->m_devHandle);
@ -137,10 +137,10 @@ class HIDDeviceWinUSB final : public IHIDDevice
device->m_initCond.notify_one(); device->m_initCond.notify_one();
/* Start transfer loop */ /* Start transfer loop */
device->m_devImp.initialCycle(); device->m_devImp->initialCycle();
while (device->m_runningTransferLoop) while (device->m_runningTransferLoop)
device->m_devImp.transferCycle(); device->m_devImp->transferCycle();
device->m_devImp.finalCycle(); device->m_devImp->finalCycle();
/* Cleanup */ /* Cleanup */
WinUsb_Free(device->m_usbHandle); WinUsb_Free(device->m_usbHandle);
@ -148,7 +148,7 @@ class HIDDeviceWinUSB final : public IHIDDevice
device->m_devHandle = 0; device->m_devHandle = 0;
} }
static void _threadProcBTLL(HIDDeviceWinUSB* device) static void _threadProcBTLL(std::shared_ptr<HIDDeviceWinUSB> device)
{ {
std::unique_lock<std::mutex> lk(device->m_initMutex); std::unique_lock<std::mutex> lk(device->m_initMutex);
@ -158,17 +158,19 @@ class HIDDeviceWinUSB final : public IHIDDevice
device->m_initCond.notify_one(); device->m_initCond.notify_one();
/* Start transfer loop */ /* Start transfer loop */
device->m_devImp.initialCycle(); device->m_devImp->initialCycle();
while (device->m_runningTransferLoop) while (device->m_runningTransferLoop)
device->m_devImp.transferCycle(); device->m_devImp->transferCycle();
device->m_devImp.finalCycle(); device->m_devImp->finalCycle();
} }
size_t m_minFeatureSz = 0; size_t m_minFeatureSz = 0;
size_t m_minInputSz = 0; size_t m_minInputSz = 0;
size_t m_minOutputSz = 0; size_t m_minOutputSz = 0;
static void _threadProcHID(HIDDeviceWinUSB* device) PHIDP_PREPARSED_DATA m_preparsedData = nullptr;
static void _threadProcHID(std::shared_ptr<HIDDeviceWinUSB> device)
{ {
char errStr[256]; char errStr[256];
std::unique_lock<std::mutex> lk(device->m_initMutex); std::unique_lock<std::mutex> lk(device->m_initMutex);
@ -187,30 +189,28 @@ class HIDDeviceWinUSB final : public IHIDDevice
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n", _snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
device->m_token.getProductName().c_str(), device->m_token.getProductName().c_str(),
device->m_devPath.c_str(), GetLastError()); device->m_devPath.c_str(), GetLastError());
device->m_devImp.deviceError(errStr); device->m_devImp->deviceError(errStr);
lk.unlock(); lk.unlock();
device->m_initCond.notify_one(); device->m_initCond.notify_one();
return; return;
} }
PHIDP_PREPARSED_DATA preparsedData; if (!HidD_GetPreparsedData(device->m_hidHandle, &device->m_preparsedData))
if (!HidD_GetPreparsedData(device->m_hidHandle, &preparsedData))
{ {
_snprintf(errStr, 256, "Unable get preparsed data of %s@%s: %d\n", _snprintf(errStr, 256, "Unable get preparsed data of %s@%s: %d\n",
device->m_token.getProductName().c_str(), device->m_token.getProductName().c_str(),
device->m_devPath.c_str(), GetLastError()); device->m_devPath.c_str(), GetLastError());
device->m_devImp.deviceError(errStr); device->m_devImp->deviceError(errStr);
lk.unlock(); lk.unlock();
device->m_initCond.notify_one(); device->m_initCond.notify_one();
return; return;
} }
HIDP_CAPS caps; HIDP_CAPS caps;
HidP_GetCaps(preparsedData, &caps); HidP_GetCaps(device->m_preparsedData, &caps);
device->m_minFeatureSz = caps.FeatureReportByteLength; device->m_minFeatureSz = caps.FeatureReportByteLength;
device->m_minInputSz = caps.InputReportByteLength; device->m_minInputSz = caps.InputReportByteLength;
device->m_minOutputSz = caps.OutputReportByteLength; device->m_minOutputSz = caps.OutputReportByteLength;
HidD_FreePreparsedData(preparsedData);
/* Return control to main thread */ /* Return control to main thread */
device->m_runningTransferLoop = true; device->m_runningTransferLoop = true;
@ -218,21 +218,23 @@ class HIDDeviceWinUSB final : public IHIDDevice
device->m_initCond.notify_one(); device->m_initCond.notify_one();
/* Allocate read buffer */ /* Allocate read buffer */
size_t inBufferSz = std::max(device->m_minInputSz, device->m_devImp.getInputBufferSize()); size_t inBufferSz = device->m_minInputSz;
std::unique_ptr<uint8_t[]> readBuf(new uint8_t[inBufferSz]); std::unique_ptr<uint8_t[]> readBuf(new uint8_t[inBufferSz]);
/* Start transfer loop */ /* Start transfer loop */
device->m_devImp.initialCycle(); device->m_devImp->initialCycle();
while (device->m_runningTransferLoop) while (device->m_runningTransferLoop)
{ {
device->ReadCycle(readBuf.get(), inBufferSz); device->ReadCycle(readBuf.get(), inBufferSz);
device->m_devImp.transferCycle(); if (device->m_runningTransferLoop)
device->m_devImp->transferCycle();
} }
device->m_devImp.finalCycle(); device->m_devImp->finalCycle();
/* Cleanup */ /* Cleanup */
CloseHandle(device->m_overlapped.hEvent); CloseHandle(device->m_overlapped.hEvent);
CloseHandle(device->m_hidHandle); CloseHandle(device->m_hidHandle);
HidD_FreePreparsedData(device->m_preparsedData);
device->m_hidHandle = nullptr; device->m_hidHandle = nullptr;
} }
@ -244,6 +246,11 @@ class HIDDeviceWinUSB final : public IHIDDevice
std::vector<uint8_t> m_sendBuf; std::vector<uint8_t> m_sendBuf;
std::vector<uint8_t> m_recvBuf; std::vector<uint8_t> m_recvBuf;
const PHIDP_PREPARSED_DATA _getReportDescriptor()
{
return m_preparsedData;
}
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message) bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
{ {
size_t maxOut = std::max(m_minFeatureSz, std::max(m_minOutputSz, length)); size_t maxOut = std::max(m_minFeatureSz, std::max(m_minOutputSz, length));
@ -323,7 +330,7 @@ class HIDDeviceWinUSB final : public IHIDDevice
public: public:
HIDDeviceWinUSB(DeviceToken& token, DeviceBase& devImp) HIDDeviceWinUSB(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
: m_token(token), : m_token(token),
m_devImp(devImp), m_devImp(devImp),
m_devPath(token.getDevicePath()) m_devPath(token.getDevicePath())
@ -335,11 +342,11 @@ public:
std::unique_lock<std::mutex> lk(m_initMutex); std::unique_lock<std::mutex> lk(m_initMutex);
DeviceType dType = m_token.getDeviceType(); DeviceType dType = m_token.getDeviceType();
if (dType == DeviceType::USB) if (dType == DeviceType::USB)
m_thread = std::thread(_threadProcUSBLL, this); m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
else if (dType == DeviceType::Bluetooth) else if (dType == DeviceType::Bluetooth)
m_thread = std::thread(_threadProcBTLL, this); m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
else if (dType == DeviceType::HID) else if (dType == DeviceType::HID)
m_thread = std::thread(_threadProcHID, this); m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceWinUSB>(shared_from_this()));
else else
throw std::runtime_error("invalid token supplied to device constructor"); throw std::runtime_error("invalid token supplied to device constructor");
m_initCond.wait(lk); m_initCond.wait(lk);
@ -348,7 +355,8 @@ public:
~HIDDeviceWinUSB() ~HIDDeviceWinUSB()
{ {
m_runningTransferLoop = false; m_runningTransferLoop = false;
m_thread.join(); if (m_thread.joinable())
m_thread.detach();
} }
OVERLAPPED m_overlapped = {}; OVERLAPPED m_overlapped = {};
@ -362,7 +370,12 @@ public:
if (!Result) if (!Result)
{ {
DWORD Error = GetLastError(); DWORD Error = GetLastError();
if (Error != ERROR_IO_PENDING) if (Error == ERROR_DEVICE_NOT_CONNECTED)
{
m_runningTransferLoop = false;
return;
}
else if (Error != ERROR_IO_PENDING)
{ {
fprintf(stderr, "Read Failed: %08X\n", Error); fprintf(stderr, "Read Failed: %08X\n", Error);
return; return;
@ -373,7 +386,7 @@ public:
} }
} }
m_devImp.receivedHIDReport(inBuffer, BytesRead, HIDReportType::Input, inBuffer[0]); m_devImp->receivedHIDReport(inBuffer, BytesRead, HIDReportType::Input, inBuffer[0]);
} }
}; };

View File

@ -135,6 +135,32 @@ class HIDListenerWinUSB final : public IHIDListener
&devpropType, (BYTE*)manufW, 1024, &manufSz, 0); &devpropType, (BYTE*)manufW, 1024, &manufSz, 0);
WideCharToMultiByte(CP_UTF8, 0, manufW, -1, manuf, 1024, nullptr, nullptr); WideCharToMultiByte(CP_UTF8, 0, manufW, -1, manuf, 1024, nullptr, nullptr);
if (type == DeviceType::HID)
{
HANDLE devHnd = CreateFileA(DeviceInterfaceDetailData.wtf.DevicePath,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (INVALID_HANDLE_VALUE == devHnd)
continue;
PHIDP_PREPARSED_DATA preparsedData;
if (!HidD_GetPreparsedData(devHnd, &preparsedData))
{
CloseHandle(devHnd);
continue;
}
HIDP_CAPS caps;
HidP_GetCaps(preparsedData, &caps);
HidD_FreePreparsedData(preparsedData);
CloseHandle(devHnd);
/* Filter non joysticks and gamepads */
if (caps.UsagePage != 1 || (caps.Usage != 4 && caps.Usage != 5))
continue;
}
/* Store as a shouting string (to keep hash-lookups consistent) */ /* Store as a shouting string (to keep hash-lookups consistent) */
CharUpperA(DeviceInterfaceDetailData.wtf.DevicePath); CharUpperA(DeviceInterfaceDetailData.wtf.DevicePath);

View File

@ -1,5 +1,9 @@
#include "boo/inputdev/HIDParser.hpp" #include "boo/inputdev/HIDParser.hpp"
#include <map> #include <map>
#include <algorithm>
#undef min
#undef max
namespace boo namespace boo
{ {
@ -289,6 +293,12 @@ HIDMainItem::HIDMainItem(uint32_t flags, const HIDItemState& state, uint32_t rep
m_reportSize = state.m_reportSize; m_reportSize = state.m_reportSize;
} }
HIDMainItem::HIDMainItem(uint32_t flags, HIDUsagePage usagePage, HIDUsage usage,
HIDRange logicalRange, int32_t reportSize)
: m_flags(uint16_t(flags)), m_usagePage(usagePage), m_usage(usage),
m_logicalRange(logicalRange), m_reportSize(reportSize)
{}
const char* HIDMainItem::GetUsagePageName() const const char* HIDMainItem::GetUsagePageName() const
{ {
if (int(m_usagePage) >= std::extent<decltype(UsagePageNames)>::value) if (int(m_usagePage) >= std::extent<decltype(UsagePageNames)>::value)
@ -385,9 +395,92 @@ struct HIDReports
void AddFeatureItem(uint32_t flags, const HIDItemState& state) { _AddItem(m_featureReports, flags, state); } void AddFeatureItem(uint32_t flags, const HIDItemState& state) { _AddItem(m_featureReports, flags, state); }
}; };
#if _WIN32
HIDParser::ParserStatus HIDParser::Parse(const PHIDP_PREPARSED_DATA descriptorData)
{
/* User mode HID report descriptor isn't available on Win32.
* Opaque preparsed data must be enumerated and sorted into
* iterable items.
*
* Wine's implementation has a good illustration of what's
* going on here:
* https://github.com/wine-mirror/wine/blob/master/dlls/hidclass.sys/descriptor.c
*
* (Thanks for this pointless pain-in-the-ass Microsoft)
*/
m_descriptorData = descriptorData;
HIDP_CAPS caps;
HidP_GetCaps(descriptorData, &caps);
m_dataList.resize(HidP_MaxDataListLength(HidP_Input, descriptorData));
std::map<uint32_t, HIDMainItem> inputItems;
{
/* First enumerate buttons */
USHORT length = caps.NumberInputButtonCaps;
std::vector<HIDP_BUTTON_CAPS> bCaps(caps.NumberInputButtonCaps, HIDP_BUTTON_CAPS());
HidP_GetButtonCaps(HidP_Input, bCaps.data(), &length, descriptorData);
for (const HIDP_BUTTON_CAPS& caps : bCaps)
{
if (caps.IsRange)
{
int usage = caps.Range.UsageMin;
for (int i=caps.Range.DataIndexMin ; i<=caps.Range.DataIndexMax ; ++i, ++usage)
{
inputItems.insert(std::make_pair(i,
HIDMainItem(caps.BitField, HIDUsagePage(caps.UsagePage),
HIDUsage(usage), std::make_pair(0, 1), 1)));
}
}
else
{
inputItems.insert(std::make_pair(caps.NotRange.DataIndex,
HIDMainItem(caps.BitField, HIDUsagePage(caps.UsagePage),
HIDUsage(caps.NotRange.Usage), std::make_pair(0, 1), 1)));
}
}
}
{
/* Now enumerate values */
USHORT length = caps.NumberInputValueCaps;
std::vector<HIDP_VALUE_CAPS> vCaps(caps.NumberInputValueCaps, HIDP_VALUE_CAPS());
HidP_GetValueCaps(HidP_Input, vCaps.data(), &length, descriptorData);
for (const HIDP_VALUE_CAPS& caps : vCaps)
{
if (caps.IsRange)
{
int usage = caps.Range.UsageMin;
for (int i=caps.Range.DataIndexMin ; i<=caps.Range.DataIndexMax ; ++i, ++usage)
{
inputItems.insert(std::make_pair(i,
HIDMainItem(caps.BitField, HIDUsagePage(caps.UsagePage), HIDUsage(usage),
std::make_pair(caps.LogicalMin, caps.LogicalMax), caps.BitSize)));
}
}
else
{
inputItems.insert(std::make_pair(caps.NotRange.DataIndex,
HIDMainItem(caps.BitField, HIDUsagePage(caps.UsagePage), HIDUsage(caps.NotRange.Usage),
HIDRange(caps.LogicalMin, caps.LogicalMax), caps.BitSize)));
}
}
}
m_itemPool.reserve(inputItems.size());
for (const auto& item : inputItems)
m_itemPool.push_back(item.second);
m_status = ParserStatus::Done;
return ParserStatus::Done;
}
#else
HIDParser::ParserStatus HIDParser::ParserStatus
HIDParser::ParseItem(HIDReports& reportsOut, HIDParser::ParseItem(HIDReports& reportsOut,
std::stack<HIDItemState>& stateStack, std::stack<HIDCollectionItem>& collectionStack, std::stack<HIDItemState>& stateStack,
std::stack<HIDCollectionItem>& collectionStack,
const uint8_t*& it, const uint8_t* end) const uint8_t*& it, const uint8_t* end)
{ {
ParserStatus status = ParserStatus::OK; ParserStatus status = ParserStatus::OK;
@ -567,8 +660,24 @@ HIDParser::ParserStatus HIDParser::Parse(const uint8_t* descriptorData, size_t l
return m_status; return m_status;
} }
#endif
void HIDParser::EnumerateValues(std::function<bool(uint32_t rep, const HIDMainItem& item)>& valueCB) const #if _WIN32
void HIDParser::EnumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const
{
if (m_status != ParserStatus::Done)
return;
for (const HIDMainItem& item : m_itemPool)
{
if (item.IsConstant())
continue;
if (!valueCB(item))
return;
}
}
#else
void HIDParser::EnumerateValues(const std::function<bool(const HIDMainItem& item)>& valueCB) const
{ {
if (m_status != ParserStatus::Done) if (m_status != ParserStatus::Done)
return; return;
@ -579,11 +688,50 @@ void HIDParser::EnumerateValues(std::function<bool(uint32_t rep, const HIDMainIt
for (uint32_t j=rep.second.first ; j<rep.second.second ; ++j) for (uint32_t j=rep.second.first ; j<rep.second.second ; ++j)
{ {
const HIDMainItem& item = m_itemPool[j]; const HIDMainItem& item = m_itemPool[j];
if (!valueCB(rep.first, item)) if (item.IsConstant())
continue;
if (!valueCB(item))
return; return;
} }
} }
} }
#endif
#if _WIN32
void HIDParser::ScanValues(const std::function<bool(const HIDMainItem& item, int32_t value)>& valueCB,
const uint8_t* data, size_t len) const
{
if (m_status != ParserStatus::Done)
return;
ULONG dataLen = m_dataList.size();
if (HidP_GetData(HidP_Input, m_dataList.data(), &dataLen,
m_descriptorData, PCHAR(data), len) != HIDP_STATUS_SUCCESS)
return;
int idx = 0;
auto it = m_dataList.begin();
auto end = m_dataList.begin() + dataLen;
for (const HIDMainItem& item : m_itemPool)
{
if (item.IsConstant())
continue;
int32_t value = 0;
if (it != end)
{
const HIDP_DATA& data = *it;
if (data.DataIndex == idx)
{
value = data.RawValue;
++it;
}
}
if (!valueCB(item, value))
return;
++idx;
}
}
#else
class BitwiseIterator class BitwiseIterator
{ {
@ -618,7 +766,7 @@ public:
} }
}; };
void HIDParser::ScanValues(std::function<bool(const HIDMainItem& item, int32_t value)>& valueCB, void HIDParser::ScanValues(const std::function<bool(const HIDMainItem& item, int32_t value)>& valueCB,
const uint8_t* data, size_t len) const const uint8_t* data, size_t len) const
{ {
if (m_status != ParserStatus::Done) if (m_status != ParserStatus::Done)
@ -646,11 +794,14 @@ void HIDParser::ScanValues(std::function<bool(const HIDMainItem& item, int32_t v
int32_t val = bitIt.GetUnsignedValue(item.m_reportSize, status); int32_t val = bitIt.GetUnsignedValue(item.m_reportSize, status);
if (status == ParserStatus::Error) if (status == ParserStatus::Error)
return; return;
if (item.IsConstant())
continue;
if (!valueCB(item, val)) if (!valueCB(item, val))
return; return;
} }
break; break;
} }
} }
#endif
} }

View File

@ -5,6 +5,10 @@
#include "boo/inputdev/DeviceBase.hpp" #include "boo/inputdev/DeviceBase.hpp"
#include <memory> #include <memory>
#if _WIN32
#include <hidsdi.h>
#endif
namespace boo namespace boo
{ {
@ -15,7 +19,11 @@ class IHIDDevice : public std::enable_shared_from_this<IHIDDevice>
virtual void _deviceDisconnected()=0; virtual void _deviceDisconnected()=0;
virtual bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)=0; virtual bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)=0;
virtual size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length)=0; virtual size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length)=0;
#if _WIN32
virtual const PHIDP_PREPARSED_DATA _getReportDescriptor()=0;
#else
virtual std::vector<uint8_t> _getReportDescriptor()=0; virtual std::vector<uint8_t> _getReportDescriptor()=0;
#endif
virtual bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0; virtual bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0;
virtual size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0; virtual size_t _receiveHIDReport(uint8_t* data, size_t length, HIDReportType tp, uint32_t message)=0;
virtual void _startThread()=0; virtual void _startThread()=0;

View File

@ -316,11 +316,11 @@ public:
/* Lookup boo window instance */ /* Lookup boo window instance */
auto search = m_allWindows.find(hwnd); auto search = m_allWindows.find(hwnd);
if (search == m_allWindows.end()) if (search == m_allWindows.end())
return 0; return DefWindowProc(hwnd, uMsg, wParam, lParam);;
std::shared_ptr<IWindow> window = search->second.lock(); std::shared_ptr<IWindow> window = search->second.lock();
if (!window) if (!window)
return 0; return DefWindowProc(hwnd, uMsg, wParam, lParam);
switch (uMsg) switch (uMsg)
{ {
@ -527,7 +527,7 @@ int ApplicationRun(IApplication::EPlatformType platform,
}; };
wndClass.hIcon = LoadIconW(wndClass.hInstance, MAKEINTRESOURCEW(101)); wndClass.hIcon = LoadIconW(wndClass.hInstance, MAKEINTRESOURCEW(101));
wndClass.hCursor = WIN32_CURSORS.m_arrow; wndClass.hCursor = WIN32_CURSORS.m_arrow;
RegisterClassW(&wndClass); ATOM a = RegisterClassW(&wndClass);
APP = new ApplicationWin32(cb, uniqueName, friendlyName, pname, args, singleInstance); APP = new ApplicationWin32(cb, uniqueName, friendlyName, pname, args, singleInstance);
return APP->run(); return APP->run();