mirror of https://github.com/AxioDL/boo.git
Add Cross-platform HID parser
This commit is contained in:
parent
82fec41f40
commit
0f2a838bfb
|
@ -220,6 +220,7 @@ add_library(boo
|
|||
lib/inputdev/GenericPad.cpp include/boo/inputdev/GenericPad.hpp
|
||||
lib/inputdev/DeviceSignature.cpp include/boo/inputdev/DeviceSignature.hpp
|
||||
lib/inputdev/DeviceFinder.cpp include/boo/inputdev/DeviceFinder.hpp
|
||||
lib/inputdev/HIDParser.cpp include/boo/inputdev/HIDParser.hpp
|
||||
lib/inputdev/IHIDDevice.hpp
|
||||
lib/audiodev/WAVOut.cpp
|
||||
lib/audiodev/AudioMatrix.hpp
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "boo/inputdev/DeviceSignature.hpp"
|
||||
#include "boo/inputdev/DolphinSmashAdapter.hpp"
|
||||
#include "boo/inputdev/DualshockPad.hpp"
|
||||
#include "boo/inputdev/GenericPad.hpp"
|
||||
#include "boo/inputdev/XInputPad.hpp"
|
||||
|
||||
namespace boo
|
||||
|
@ -10,6 +11,7 @@ const DeviceSignature BOO_DEVICE_SIGS[] =
|
|||
{
|
||||
DEVICE_SIG(DolphinSmashAdapter, 0x57e, 0x337, DeviceType::USB),
|
||||
DEVICE_SIG(DualshockPad, 0x54c, 0x268, DeviceType::HID),
|
||||
DEVICE_SIG(GenericPad, 0, 0, DeviceType::HID),
|
||||
DEVICE_SIG(XInputPad, 0, 0, DeviceType::XInput),
|
||||
DEVICE_SIG_SENTINEL()
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "inputdev/DeviceFinder.hpp"
|
||||
#include "inputdev/DolphinSmashAdapter.hpp"
|
||||
#include "inputdev/DualshockPad.hpp"
|
||||
#include "inputdev/GenericPad.hpp"
|
||||
#include "graphicsdev/IGraphicsCommandQueue.hpp"
|
||||
#include "graphicsdev/IGraphicsDataFactory.hpp"
|
||||
#include "DeferredWindowEvents.hpp"
|
||||
|
|
|
@ -22,9 +22,10 @@ class DeviceBase : public std::enable_shared_from_this<DeviceBase>
|
|||
{
|
||||
friend class DeviceToken;
|
||||
friend struct DeviceSignature;
|
||||
friend class HIDDeviceIOKit;
|
||||
|
||||
class DeviceToken* m_token;
|
||||
std::unique_ptr<IHIDDevice> m_hidDev;
|
||||
std::shared_ptr<IHIDDevice> m_hidDev;
|
||||
void _deviceDisconnected();
|
||||
|
||||
public:
|
||||
|
@ -38,13 +39,13 @@ public:
|
|||
virtual void initialCycle() {}
|
||||
virtual void transferCycle() {}
|
||||
virtual void finalCycle() {}
|
||||
virtual size_t getInputBufferSize() const { return 0; }
|
||||
|
||||
/* Low-Level API */
|
||||
bool sendUSBInterruptTransfer(const uint8_t* data, size_t length);
|
||||
size_t receiveUSBInterruptTransfer(uint8_t* data, size_t length);
|
||||
|
||||
/* High-Level API */
|
||||
std::vector<uint8_t> getReportDescriptor();
|
||||
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
|
||||
virtual void receivedHIDReport(const uint8_t* /*data*/, size_t /*length*/, HIDReportType /*tp*/, uint32_t /*message*/) {}
|
||||
|
|
|
@ -182,8 +182,6 @@ public:
|
|||
m_report.leds = led;
|
||||
sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01);
|
||||
}
|
||||
|
||||
size_t getInputBufferSize() const { return 49; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -2,17 +2,34 @@
|
|||
#define CGENERICPAD_HPP
|
||||
|
||||
#include "DeviceBase.hpp"
|
||||
#include "HIDParser.hpp"
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
struct IGenericPadCallback
|
||||
{
|
||||
virtual void controllerConnected() {}
|
||||
virtual void controllerDisconnected() {}
|
||||
virtual void valueUpdate(const HIDMainItem& item, int32_t value) {}
|
||||
};
|
||||
|
||||
class GenericPad final : public DeviceBase
|
||||
{
|
||||
HIDParser m_parser;
|
||||
IGenericPadCallback* m_cb = nullptr;
|
||||
public:
|
||||
GenericPad(DeviceToken* token);
|
||||
~GenericPad();
|
||||
|
||||
void setCallback(IGenericPadCallback* cb) { m_cb = cb; }
|
||||
void deviceDisconnected();
|
||||
void initialCycle();
|
||||
void receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message);
|
||||
|
||||
void enumerateValues(std::function<bool(const HIDMainItem& item)>& valueCB) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
#ifndef HIDPARSER_HPP
|
||||
#define HIDPARSER_HPP
|
||||
|
||||
#include "boo/System.hpp"
|
||||
#include <vector>
|
||||
#include <stack>
|
||||
#include <functional>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
struct HIDItemState;
|
||||
struct HIDCollectionItem;
|
||||
struct HIDReports;
|
||||
|
||||
enum class HIDUsagePage : uint8_t
|
||||
{
|
||||
Undefined = 0,
|
||||
GenericDesktop = 1,
|
||||
Simulation = 2,
|
||||
VR = 3,
|
||||
Sport = 4,
|
||||
Game = 5,
|
||||
GenericDevice = 6,
|
||||
Keyboard = 7,
|
||||
LEDs = 8,
|
||||
Button = 9,
|
||||
Ordinal = 10,
|
||||
Telephony = 11,
|
||||
Consumer = 12,
|
||||
Digitizer = 13
|
||||
};
|
||||
|
||||
enum class HIDUsage : uint8_t
|
||||
{
|
||||
Undefined = 0,
|
||||
|
||||
/* Generic Desktop */
|
||||
Pointer = 1,
|
||||
Mouse = 2,
|
||||
Reserved = 3,
|
||||
Joystick = 4,
|
||||
GamePad = 5,
|
||||
Keyboard = 6,
|
||||
Keypad = 7,
|
||||
MultiAxis = 8,
|
||||
TabletPC = 9,
|
||||
X = 0x30,
|
||||
Y = 0x31,
|
||||
Z = 0x32,
|
||||
Rx = 0x33,
|
||||
Ry = 0x34,
|
||||
Rz = 0x35,
|
||||
Slider = 0x36,
|
||||
Dial = 0x37,
|
||||
Wheel = 0x38,
|
||||
HatSwitch = 0x39,
|
||||
CountedBuffer = 0x3a,
|
||||
ByteCount = 0x3b,
|
||||
MotionWakeup = 0x3c,
|
||||
Start = 0x3d,
|
||||
Select = 0x3e,
|
||||
Vx = 0x40,
|
||||
Vy = 0x41,
|
||||
Vz = 0x42,
|
||||
Vbrx = 0x43,
|
||||
Vbry = 0x44,
|
||||
Vbrz = 0x45,
|
||||
Vno = 0x46,
|
||||
FeatureNotification = 0x47,
|
||||
ResolutionMultiplier = 0x48,
|
||||
SystemControl = 0x80,
|
||||
SystemPowerDown = 0x81,
|
||||
SystemSleep = 0x82,
|
||||
SystemWakeUp = 0x83,
|
||||
SystemContextMenu = 0x84,
|
||||
SystemMainMenu = 0x85,
|
||||
SystemAppMenu = 0x86,
|
||||
SystemMenuHelp = 0x87,
|
||||
SystemMenuExit = 0x88,
|
||||
SystemMenuSelect = 0x89,
|
||||
SystemMenuRight = 0x8a,
|
||||
SystemMenuLeft = 0x8b,
|
||||
SystemMenuUp = 0x8c,
|
||||
SystemMenuDown = 0x8d,
|
||||
SystemColdRestart = 0x8e,
|
||||
SystemWarmRestart = 0x8f,
|
||||
DPadUp = 0x90,
|
||||
DPadDown = 0x91,
|
||||
DPadRight = 0x92,
|
||||
DPadLeft = 0x93,
|
||||
SystemDock = 0xa0,
|
||||
SystemUndock = 0xa1,
|
||||
SystemSetup = 0xa2,
|
||||
SystemBreak = 0xa3,
|
||||
SystemDebuggerBreak = 0xa4,
|
||||
ApplicationBreak = 0xa5,
|
||||
ApplicationDebuggerBreak = 0xa6,
|
||||
SystemSpeakerMute = 0xa7,
|
||||
SystemHibernate = 0xa8,
|
||||
SystemDisplayInvert = 0xb0,
|
||||
SystemDisplayInternal = 0xb1,
|
||||
SystemDisplayExternal = 0xb2,
|
||||
SystemDisplayBoth = 0xb3,
|
||||
SystemDisplayDual = 0xb4,
|
||||
SystemDisplayToggleIntExt = 0xb5,
|
||||
|
||||
/* Game Controls */
|
||||
_3DGameController = 0x1,
|
||||
PinballDevice = 0x2,
|
||||
GunDevice = 0x3,
|
||||
PointOfView = 0x20,
|
||||
TurnLeftRight = 0x21,
|
||||
PitchForwardBackward = 0x22,
|
||||
RollRightLeft = 0x23,
|
||||
MoveRightLeft = 0x24,
|
||||
MoveForwardBackward = 0x25,
|
||||
MoveUpDown = 0x26,
|
||||
LeanLeftRight = 0x27,
|
||||
LeanForwardBackward = 0x28,
|
||||
HeightOfPOV = 0x29,
|
||||
Flipper = 0x2a,
|
||||
SecondaryFlipper = 0x2b,
|
||||
Bump = 0x2c,
|
||||
NewGame = 0x2d,
|
||||
ShootBall = 0x2e,
|
||||
Player = 0x2f,
|
||||
GunBolt = 0x30,
|
||||
GunClip = 0x31,
|
||||
GunSelector = 0x32,
|
||||
GunSingleShot = 0x33,
|
||||
GunBurst = 0x34,
|
||||
GunAutomatic = 0x35,
|
||||
GunSafety = 0x36,
|
||||
GamepadFireJump = 0x37,
|
||||
GamepadTrigger = 0x39,
|
||||
};
|
||||
|
||||
using HIDRange = std::pair<int32_t, int32_t>;
|
||||
|
||||
/* [6.2.2.5] Input, Output, and Feature Items */
|
||||
struct HIDMainItem
|
||||
{
|
||||
uint16_t m_flags;
|
||||
HIDUsagePage m_usagePage;
|
||||
HIDUsage m_usage;
|
||||
HIDRange m_logicalRange;
|
||||
int32_t m_reportSize;
|
||||
bool IsConstant() const { return (m_flags & 0x1) != 0; }
|
||||
bool IsVariable() const { return (m_flags & 0x2) != 0; }
|
||||
bool IsRelative() const { return (m_flags & 0x4) != 0; }
|
||||
bool IsWrap() const { return (m_flags & 0x8) != 0; }
|
||||
bool IsNonlinear() const { return (m_flags & 0x10) != 0; }
|
||||
bool IsNoPreferred() const { return (m_flags & 0x20) != 0; }
|
||||
bool IsNullState() const { return (m_flags & 0x40) != 0; }
|
||||
bool IsVolatile() const { return (m_flags & 0x80) != 0; }
|
||||
bool IsBufferedBytes() const { return (m_flags & 0x100) != 0; }
|
||||
|
||||
HIDMainItem() = default;
|
||||
HIDMainItem(uint32_t flags, const HIDItemState& state, uint32_t reportIdx);
|
||||
const char* GetUsagePageName() const;
|
||||
const char* GetUsageName() const;
|
||||
};
|
||||
|
||||
class HIDParser
|
||||
{
|
||||
public:
|
||||
enum class ParserStatus
|
||||
{
|
||||
OK,
|
||||
Done,
|
||||
Error
|
||||
};
|
||||
private:
|
||||
std::unique_ptr<HIDMainItem[]> m_itemPool;
|
||||
using Report = std::pair<uint32_t, std::pair<uint32_t, uint32_t>>;
|
||||
std::unique_ptr<Report[]> m_reportPool;
|
||||
std::pair<uint32_t, uint32_t> m_inputReports = {};
|
||||
std::pair<uint32_t, uint32_t> m_outputReports = {};
|
||||
std::pair<uint32_t, uint32_t> m_featureReports = {};
|
||||
ParserStatus m_status = ParserStatus::OK;
|
||||
bool m_multipleReports = false;
|
||||
ParserStatus ParseItem(HIDReports& reportsOut,
|
||||
std::stack<HIDItemState>& stateStack, std::stack<HIDCollectionItem>& collectionStack,
|
||||
const uint8_t*& it, const uint8_t* end);
|
||||
public:
|
||||
ParserStatus Parse(const uint8_t* descriptorData, size_t len);
|
||||
operator bool() const { return m_status == ParserStatus::Done; }
|
||||
void EnumerateValues(std::function<bool(uint32_t rep, const HIDMainItem& item)>& valueCB) const;
|
||||
void ScanValues(std::function<bool(const HIDMainItem& item, int32_t value)>& valueCB,
|
||||
const uint8_t* data, size_t len) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HIDPARSER_HPP
|
|
@ -1159,6 +1159,7 @@ struct GLCommandQueue : IGraphicsCommandQueue
|
|||
|
||||
static void RenderingWorker(GLCommandQueue* self)
|
||||
{
|
||||
logvisor::RegisterThreadName("Boo GL Rendering Thread");
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(self->m_initmt);
|
||||
self->m_parent->makeCurrent();
|
||||
|
|
|
@ -54,6 +54,13 @@ size_t DeviceBase::receiveUSBInterruptTransfer(uint8_t* data, size_t length)
|
|||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> DeviceBase::getReportDescriptor()
|
||||
{
|
||||
if (m_hidDev)
|
||||
return m_hidDev->_getReportDescriptor();
|
||||
return {};
|
||||
}
|
||||
|
||||
bool DeviceBase::sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
if (m_hidDev)
|
||||
|
|
|
@ -13,13 +13,16 @@ bool DeviceSignature::DeviceMatchToken(const DeviceToken& token, const TDeviceSi
|
|||
{
|
||||
if (token.getDeviceType() == DeviceType::HID)
|
||||
{
|
||||
bool hasGenericPad = false;
|
||||
for (const DeviceSignature* sig : sigSet)
|
||||
{
|
||||
if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId() &&
|
||||
sig->m_type != DeviceType::HID)
|
||||
return false;
|
||||
if (sig->m_typeIdx == typeid(GenericPad))
|
||||
hasGenericPad = true;
|
||||
}
|
||||
return true;
|
||||
return hasGenericPad;
|
||||
}
|
||||
for (const DeviceSignature* sig : sigSet)
|
||||
{
|
||||
|
@ -29,7 +32,7 @@ bool DeviceSignature::DeviceMatchToken(const DeviceToken& token, const TDeviceSi
|
|||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp);
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp);
|
||||
std::shared_ptr<DeviceBase> DeviceSignature::DeviceNew(DeviceToken& token)
|
||||
{
|
||||
std::shared_ptr<DeviceBase> retval;
|
||||
|
@ -57,7 +60,7 @@ std::shared_ptr<DeviceBase> DeviceSignature::DeviceNew(DeviceToken& token)
|
|||
if (!retval)
|
||||
return nullptr;
|
||||
|
||||
retval->m_hidDev = IHIDDeviceNew(token, *retval);
|
||||
retval->m_hidDev = IHIDDeviceNew(token, retval);
|
||||
if (!retval->m_hidDev)
|
||||
return nullptr;
|
||||
retval->m_hidDev->_startThread();
|
||||
|
@ -74,7 +77,7 @@ std::shared_ptr<DeviceBase> DeviceSignature::DeviceNew(DeviceToken& token)
|
|||
if (!retval)
|
||||
return nullptr;
|
||||
|
||||
retval->m_hidDev = IHIDDeviceNew(token, *retval);
|
||||
retval->m_hidDev = IHIDDeviceNew(token, retval);
|
||||
if (!retval->m_hidDev)
|
||||
return nullptr;
|
||||
retval->m_hidDev->_startThread();
|
||||
|
|
|
@ -5,19 +5,45 @@ namespace boo
|
|||
{
|
||||
|
||||
GenericPad::GenericPad(DeviceToken* token)
|
||||
: DeviceBase(token)
|
||||
: DeviceBase(token)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
GenericPad::~GenericPad()
|
||||
{
|
||||
|
||||
}
|
||||
GenericPad::~GenericPad() {}
|
||||
|
||||
void GenericPad::deviceDisconnected()
|
||||
{
|
||||
if (m_cb)
|
||||
m_cb->controllerDisconnected();
|
||||
}
|
||||
|
||||
void GenericPad::initialCycle()
|
||||
{
|
||||
std::vector<uint8_t> reportDesc = getReportDescriptor();
|
||||
m_parser.Parse(reportDesc.data(), reportDesc.size());
|
||||
if (m_cb)
|
||||
m_cb->controllerConnected();
|
||||
}
|
||||
|
||||
void GenericPad::receivedHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
if (length == 0 || tp != HIDReportType::Input || !m_cb)
|
||||
return;
|
||||
std::function<bool(const HIDMainItem&, int32_t)> func =
|
||||
[this](const HIDMainItem& item, int32_t value)
|
||||
{
|
||||
m_cb->valueUpdate(item, value);
|
||||
return true;
|
||||
};
|
||||
m_parser.ScanValues(func, data, length);
|
||||
}
|
||||
|
||||
void GenericPad::enumerateValues(std::function<bool(const HIDMainItem& item)>& valueCB) const
|
||||
{
|
||||
std::function<bool(uint32_t, const HIDMainItem&)> func =
|
||||
[&](uint32_t rep, const HIDMainItem& item) { return valueCB(item); };
|
||||
m_parser.EnumerateValues(func);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
#include "IHIDDevice.hpp"
|
||||
#include "boo/inputdev/DeviceToken.hpp"
|
||||
#include "boo/inputdev/DeviceBase.hpp"
|
||||
#include <IOKit/hid/IOHIDLib.h>
|
||||
#include <IOKit/hid/IOHIDDevicePlugin.h>
|
||||
#include <IOKit/hid/IOHIDDevice.h>
|
||||
#include <IOKit/usb/IOUSBLib.h>
|
||||
#include <IOKit/IOCFPlugIn.h>
|
||||
#include "IOKitPointer.hpp"
|
||||
#include <thread>
|
||||
|
||||
|
@ -15,7 +11,7 @@ namespace boo
|
|||
class HIDDeviceIOKit : public IHIDDevice
|
||||
{
|
||||
DeviceToken& m_token;
|
||||
DeviceBase& m_devImp;
|
||||
std::shared_ptr<DeviceBase> m_devImp;
|
||||
|
||||
IUnknownPointer<IOUSBInterfaceInterface> m_usbIntf;
|
||||
uint8_t m_usbIntfInPipe = 0;
|
||||
|
@ -52,6 +48,21 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||
return 0;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> _getReportDescriptor()
|
||||
{
|
||||
if (m_hidIntf)
|
||||
{
|
||||
if (CFTypeRef desc = IOHIDDeviceGetProperty(m_hidIntf.get(), CFSTR(kIOHIDReportDescriptorKey)))
|
||||
{
|
||||
CFIndex len = CFDataGetLength(CFDataRef(desc));
|
||||
std::vector<uint8_t> ret(len, '\0');
|
||||
CFDataGetBytes(CFDataRef(desc), CFRangeMake(0, len), &ret[0]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||
{
|
||||
/* HACK: A bug in IOBluetoothGamepadHIDDriver prevents raw output report transmission
|
||||
|
@ -77,7 +88,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void _threadProcUSBLL(HIDDeviceIOKit* device)
|
||||
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceIOKit> device)
|
||||
{
|
||||
char thrName[128];
|
||||
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().c_str());
|
||||
|
@ -102,7 +113,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||
snprintf(errStr, 256, "Unable to find interface for %s@%s\n",
|
||||
device->m_token.getProductName().c_str(),
|
||||
device->m_devPath.c_str());
|
||||
device->m_devImp.deviceError(errStr);
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
|
@ -121,7 +132,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
||||
device->m_devImp.deviceError(errStr);
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
|
@ -134,7 +145,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
||||
device->m_devImp.deviceError(errStr);
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
|
@ -149,13 +160,13 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s: someone else using it\n",
|
||||
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
||||
device->m_devImp.deviceError(errStr);
|
||||
device->m_devImp->deviceError(errStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
||||
device->m_devImp.deviceError(errStr);
|
||||
device->m_devImp->deviceError(errStr);
|
||||
}
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
|
@ -185,17 +196,17 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp.initialCycle();
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp.transferCycle();
|
||||
device->m_devImp.finalCycle();
|
||||
device->m_devImp->transferCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
/* Cleanup */
|
||||
intf->USBInterfaceClose(intf.storage());
|
||||
device->m_usbIntf = nullptr;
|
||||
}
|
||||
|
||||
static void _threadProcBTLL(HIDDeviceIOKit* device)
|
||||
static void _threadProcBTLL(std::shared_ptr<HIDDeviceIOKit> device)
|
||||
{
|
||||
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||
|
||||
|
@ -205,11 +216,17 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp.initialCycle();
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
device->m_devImp.transferCycle();
|
||||
device->m_devImp.finalCycle();
|
||||
device->m_devImp->transferCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
}
|
||||
|
||||
static void _hidRemoveCb(void * _Nullable context,
|
||||
IOReturn result,
|
||||
void * _Nullable sender)
|
||||
{
|
||||
reinterpret_cast<HIDDeviceIOKit*>(context)->m_runningTransferLoop = false;
|
||||
}
|
||||
|
||||
static void _hidReportCb(void * _Nullable context,
|
||||
|
@ -223,7 +240,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||
reinterpret_cast<DeviceBase*>(context)->receivedHIDReport(report, reportLength, HIDReportType(type), reportID);
|
||||
}
|
||||
|
||||
static void _threadProcHID(HIDDeviceIOKit* device)
|
||||
static void _threadProcHID(std::shared_ptr<HIDDeviceIOKit> device)
|
||||
{
|
||||
char thrName[128];
|
||||
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().c_str());
|
||||
|
@ -239,7 +256,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||
snprintf(errStr, 256, "Unable to find interface for %s@%s\n",
|
||||
device->m_token.getProductName().c_str(),
|
||||
device->m_devPath.c_str());
|
||||
device->m_devImp.deviceError(errStr);
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
|
@ -250,7 +267,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
||||
device->m_devImp.deviceError(errStr);
|
||||
device->m_devImp->deviceError(errStr);
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
|
@ -264,30 +281,36 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s: someone else using it\n",
|
||||
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
||||
device->m_devImp.deviceError(errStr);
|
||||
device->m_devImp->deviceError(errStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
||||
device->m_devImp.deviceError(errStr);
|
||||
device->m_devImp->deviceError(errStr);
|
||||
}
|
||||
lk.unlock();
|
||||
device->m_initCond.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Register removal callback */
|
||||
IOHIDDeviceRegisterRemovalCallback(device->m_hidIntf.get(), _hidRemoveCb, device.get());
|
||||
|
||||
/* Make note if device uses bluetooth driver */
|
||||
if (CFTypeRef transport = IOHIDDeviceGetProperty(device->m_hidIntf.get(), CFSTR(kIOHIDTransportKey)))
|
||||
device->m_isBt = CFStringCompare(CFStringRef(transport), CFSTR(kIOHIDTransportBluetoothValue), 0) == kCFCompareEqualTo;
|
||||
|
||||
/* Register input buffer */
|
||||
std::unique_ptr<uint8_t[]> buffer;
|
||||
if (size_t bufSize = device->m_devImp.getInputBufferSize())
|
||||
int bufSize = 0;
|
||||
if (CFTypeRef maxSize = IOHIDDeviceGetProperty(device->m_hidIntf.get(), CFSTR(kIOHIDMaxInputReportSizeKey)))
|
||||
CFNumberGetValue(CFNumberRef(maxSize), kCFNumberIntType, &bufSize);
|
||||
if (bufSize)
|
||||
{
|
||||
buffer = std::unique_ptr<uint8_t[]>(new uint8_t[bufSize]);
|
||||
IOHIDDeviceRegisterInputReportCallback(device->m_hidIntf.get(), buffer.get(), bufSize,
|
||||
_hidReportCb, &device->m_devImp);
|
||||
_hidReportCb, device->m_devImp.get());
|
||||
IOHIDDeviceScheduleWithRunLoop(device->m_hidIntf.get(), CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
}
|
||||
|
||||
|
@ -297,13 +320,14 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||
device->m_initCond.notify_one();
|
||||
|
||||
/* Start transfer loop */
|
||||
device->m_devImp.initialCycle();
|
||||
device->m_devImp->initialCycle();
|
||||
while (device->m_runningTransferLoop)
|
||||
{
|
||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.010, true);
|
||||
device->m_devImp.transferCycle();
|
||||
if (device->m_runningTransferLoop)
|
||||
device->m_devImp->transferCycle();
|
||||
}
|
||||
device->m_devImp.finalCycle();
|
||||
device->m_devImp->finalCycle();
|
||||
|
||||
/* Cleanup */
|
||||
IOHIDDeviceClose(device->m_hidIntf.get(), kIOHIDOptionsTypeNone);
|
||||
|
@ -317,7 +341,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||
|
||||
public:
|
||||
|
||||
HIDDeviceIOKit(DeviceToken& token, DeviceBase& devImp)
|
||||
HIDDeviceIOKit(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
: m_token(token),
|
||||
m_devImp(devImp),
|
||||
m_devPath(token.getDevicePath())
|
||||
|
@ -329,11 +353,11 @@ public:
|
|||
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||
DeviceType dType = m_token.getDeviceType();
|
||||
if (dType == DeviceType::USB)
|
||||
m_thread = std::thread(_threadProcUSBLL, this);
|
||||
m_thread = std::thread(_threadProcUSBLL, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
|
||||
else if (dType == DeviceType::Bluetooth)
|
||||
m_thread = std::thread(_threadProcBTLL, this);
|
||||
m_thread = std::thread(_threadProcBTLL, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
|
||||
else if (dType == DeviceType::HID)
|
||||
m_thread = std::thread(_threadProcHID, this);
|
||||
m_thread = std::thread(_threadProcHID, std::static_pointer_cast<HIDDeviceIOKit>(shared_from_this()));
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "invalid token supplied to device constructor\n");
|
||||
|
@ -346,15 +370,13 @@ public:
|
|||
{
|
||||
m_runningTransferLoop = false;
|
||||
if (m_thread.joinable())
|
||||
m_thread.join();
|
||||
m_thread.detach();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
std::unique_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp)
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
{
|
||||
return std::make_unique<HIDDeviceIOKit>(token, devImp);
|
||||
return std::make_shared<HIDDeviceIOKit>(token, devImp);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -309,9 +309,9 @@ public:
|
|||
|
||||
};
|
||||
|
||||
std::unique_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp)
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
{
|
||||
return std::make_unique<HIDDeviceUdev>(token, devImp);
|
||||
return std::make_shared<HIDDeviceUdev>(token, devImp);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -377,9 +377,9 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp)
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
{
|
||||
return std::make_unique<HIDDeviceWinUSB>(token, devImp);
|
||||
return std::make_shared<HIDDeviceWinUSB>(token, devImp);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,8 +27,8 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
IHIDDevice* IHIDDeviceNew(DeviceToken& token, DeviceBase& devImp)
|
||||
std::shared_ptr<IHIDDevice> IHIDDeviceNew(DeviceToken& token, const std::shared_ptr<DeviceBase>& devImp)
|
||||
{
|
||||
return new HIDDeviceBSD(token, devImp);
|
||||
return std::make_shared<HIDDeviceBSD>(token, devImp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -252,7 +252,7 @@ public:
|
|||
HIDListenerIOKit(DeviceFinder& finder)
|
||||
: m_finder(finder)
|
||||
{
|
||||
m_listenerRunLoop = CFRunLoopGetCurrent();
|
||||
m_listenerRunLoop = CFRunLoopGetMain();
|
||||
m_llPort = IONotificationPortCreate(kIOMasterPortDefault);
|
||||
CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort);
|
||||
CFRunLoopAddSource(m_listenerRunLoop, rlSrc, kCFRunLoopDefaultMode);
|
||||
|
|
|
@ -0,0 +1,656 @@
|
|||
#include "boo/inputdev/HIDParser.hpp"
|
||||
#include <map>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
/* Based on "Device Class Definition for Human Interface Devices (HID)"
|
||||
* http://www.usb.org/developers/hidpage/HID1_11.pdf
|
||||
*/
|
||||
|
||||
static const char* UsagePageNames[] =
|
||||
{
|
||||
"Undefined",
|
||||
"Generic Desktop",
|
||||
"Simulation",
|
||||
"VR",
|
||||
"Sport",
|
||||
"Game Controls",
|
||||
"Generic Device",
|
||||
"Keyboard",
|
||||
"LEDs",
|
||||
"Button",
|
||||
"Ordinal",
|
||||
"Telephony",
|
||||
"Consumer",
|
||||
"Digitizer"
|
||||
};
|
||||
|
||||
static const char* GenericDesktopUsages[] =
|
||||
{
|
||||
"Undefined",
|
||||
"Pointer",
|
||||
"Mouse",
|
||||
"Reserved",
|
||||
"Joystick",
|
||||
"Game Pad",
|
||||
"Keyboard",
|
||||
"Keypad",
|
||||
"Multi-axis Controller",
|
||||
"Tablet PC System Controls",
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
"X",
|
||||
"Y",
|
||||
"Z",
|
||||
"Rx",
|
||||
"Ry",
|
||||
"Rz",
|
||||
"Slider",
|
||||
"Dial",
|
||||
"Wheel",
|
||||
"Hat Switch",
|
||||
"Counted Buffer",
|
||||
"Byte Count",
|
||||
"Motion Wakeup",
|
||||
"Start",
|
||||
"Select",
|
||||
"Reserved",
|
||||
"Vx",
|
||||
"Vy",
|
||||
"Vz",
|
||||
"Vbrx",
|
||||
"Vbry",
|
||||
"Vbrz",
|
||||
"Vno",
|
||||
"Feature Notification",
|
||||
"Resolution Multiplier",
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
"System Control",
|
||||
"System Power Down",
|
||||
"System Sleep",
|
||||
"System Wake Up",
|
||||
"System Context Menu",
|
||||
"System Main Menu",
|
||||
"System App Menu",
|
||||
"System Menu Help",
|
||||
"System Menu Exit",
|
||||
"System Menu Select",
|
||||
"System Menu Right",
|
||||
"System Menu Left",
|
||||
"System Menu Up",
|
||||
"System Menu Down",
|
||||
"System Cold Restart",
|
||||
"System Warm Restart",
|
||||
"D-pad Up",
|
||||
"D-pad Down",
|
||||
"D-pad Right",
|
||||
"D-pad Left",
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
"System Dock",
|
||||
"System Undock",
|
||||
"System Setup",
|
||||
"System Break",
|
||||
"System Debugger Break",
|
||||
"Application Break",
|
||||
"Application Debugger Break",
|
||||
"System Speaker Mute",
|
||||
"System Hibernate",
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
"System Display Invert",
|
||||
"System Display Internal",
|
||||
"System Display External",
|
||||
"System Display Both",
|
||||
"System Display Dual",
|
||||
"System Display Toggle Int/Ext"
|
||||
};
|
||||
|
||||
static const char* GameUsages[] =
|
||||
{
|
||||
"Undefined",
|
||||
"3D Game Controller",
|
||||
"Pinball Device",
|
||||
"Gun Device",
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
"Point of View",
|
||||
"Turn Right/Left",
|
||||
"Pitch Forward/Backward",
|
||||
"Roll Right/Left",
|
||||
"Move Right/Left",
|
||||
"Move Forward/Backward",
|
||||
"Move Up/Down",
|
||||
"Lean Right/Left",
|
||||
"Lean Forward/Backward",
|
||||
"Height of POV",
|
||||
"Flipper",
|
||||
"Secondary Flipper",
|
||||
"Bump",
|
||||
"New Game",
|
||||
"Shoot Ball",
|
||||
"Player",
|
||||
"Gun Bolt",
|
||||
"Gun Clip",
|
||||
"Gun Selector",
|
||||
"Gun Single Shot",
|
||||
"Gun Burst",
|
||||
"Gun Automatic",
|
||||
"Gun Safety",
|
||||
"Gemepad Fire/Jump",
|
||||
nullptr,
|
||||
"Gamepad Trigger"
|
||||
};
|
||||
|
||||
enum class HIDCollectionType : uint8_t
|
||||
{
|
||||
Physical,
|
||||
Application,
|
||||
Logical,
|
||||
Report,
|
||||
NamedArray,
|
||||
UsageSwitch,
|
||||
UsageModifier
|
||||
};
|
||||
|
||||
enum class HIDItemType : uint8_t
|
||||
{
|
||||
Main,
|
||||
Global,
|
||||
Local,
|
||||
Reserved
|
||||
};
|
||||
|
||||
enum class HIDItemTag : uint8_t
|
||||
{
|
||||
/* [6.2.2.4] Main Items */
|
||||
Input = 0b1000,
|
||||
Output = 0b1001,
|
||||
Feature = 0b1011,
|
||||
Collection = 0b1010,
|
||||
EndCollection = 0b1100,
|
||||
|
||||
/* [6.2.2.7] Global Items */
|
||||
UsagePage = 0b0000,
|
||||
LogicalMinimum = 0b0001,
|
||||
LogicalMaximum = 0b0010,
|
||||
PhysicalMinimum = 0b0011,
|
||||
PhysicalMaximum = 0b0100,
|
||||
UnitExponent = 0b0101,
|
||||
Unit = 0b0110,
|
||||
ReportSize = 0b0111,
|
||||
ReportID = 0b1000,
|
||||
ReportCount = 0b1001,
|
||||
Push = 0b1010,
|
||||
Pop = 0b1011,
|
||||
|
||||
/* [6.2.2.8] Local Items */
|
||||
Usage = 0b0000,
|
||||
UsageMinimum = 0b0001,
|
||||
UsageMaximum = 0b0010,
|
||||
DesignatorIndex = 0b0011,
|
||||
DesignatorMinimum = 0b0100,
|
||||
DesignatorMaximum = 0b0101,
|
||||
StringIndex = 0b0111,
|
||||
StringMinimum = 0b1000,
|
||||
StringMaximum = 0b1001,
|
||||
Delimiter = 0b1010,
|
||||
};
|
||||
|
||||
struct HIDItemState
|
||||
{
|
||||
/* [6.2.2.7] Global items */
|
||||
HIDUsagePage m_usagePage = HIDUsagePage::Undefined;
|
||||
HIDRange m_logicalRange = {};
|
||||
HIDRange m_physicalRange = {};
|
||||
int32_t m_unitExponent = 0;
|
||||
uint32_t m_unit = 0;
|
||||
uint32_t m_reportSize = 0; // In bits
|
||||
uint32_t m_reportID = 0;
|
||||
uint32_t m_reportCount = 0;
|
||||
|
||||
/* [6.2.2.8] Local Items */
|
||||
std::vector<HIDUsage> m_usage;
|
||||
HIDRange m_usageRange = {};
|
||||
#if 0
|
||||
std::vector<int32_t> m_designatorIndex;
|
||||
std::vector<HIDRange> m_designatorRange;
|
||||
std::vector<int32_t> m_stringIndex;
|
||||
std::vector<HIDRange> m_stringRange;
|
||||
std::vector<int32_t> m_delimiter;
|
||||
#endif
|
||||
|
||||
void ResetLocalItems()
|
||||
{
|
||||
m_usage.clear();
|
||||
m_usageRange = HIDRange();
|
||||
#if 0
|
||||
m_designatorIndex.clear();
|
||||
m_designatorRange.clear();
|
||||
m_stringIndex.clear();
|
||||
m_stringRange.clear();
|
||||
m_delimiter.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T _GetLocal(const std::vector<T>& v, uint32_t idx)
|
||||
{
|
||||
if (v.empty())
|
||||
return {};
|
||||
if (idx >= v.size())
|
||||
return v[0];
|
||||
return v[idx];
|
||||
}
|
||||
|
||||
HIDUsage GetUsage(uint32_t idx) const
|
||||
{
|
||||
if (m_usageRange.second - m_usageRange.first != 0)
|
||||
return HIDUsage(m_usageRange.first + idx);
|
||||
return _GetLocal(m_usage, idx);
|
||||
}
|
||||
};
|
||||
|
||||
struct HIDCollectionItem
|
||||
{
|
||||
/* [6.2.2.6] Collection, End Collection Items */
|
||||
HIDCollectionType m_type;
|
||||
HIDUsagePage m_usagePage;
|
||||
HIDUsage m_usage;
|
||||
|
||||
HIDCollectionItem(HIDCollectionType type, const HIDItemState& state)
|
||||
: m_type(type), m_usagePage(state.m_usagePage), m_usage(state.GetUsage(0))
|
||||
{}
|
||||
};
|
||||
|
||||
HIDMainItem::HIDMainItem(uint32_t flags, const HIDItemState& state, uint32_t reportIdx)
|
||||
: m_flags(uint16_t(flags))
|
||||
{
|
||||
m_usagePage = state.m_usagePage;
|
||||
m_usage = state.GetUsage(reportIdx);
|
||||
m_logicalRange = state.m_logicalRange;
|
||||
m_reportSize = state.m_reportSize;
|
||||
}
|
||||
|
||||
const char* HIDMainItem::GetUsagePageName() const
|
||||
{
|
||||
if (int(m_usagePage) >= std::extent<decltype(UsagePageNames)>::value)
|
||||
return nullptr;
|
||||
return UsagePageNames[int(m_usagePage)];
|
||||
}
|
||||
|
||||
const char* HIDMainItem::GetUsageName() const
|
||||
{
|
||||
switch (m_usagePage)
|
||||
{
|
||||
case HIDUsagePage::GenericDesktop:
|
||||
if (int(m_usage) >= std::extent<decltype(GenericDesktopUsages)>::value)
|
||||
return nullptr;
|
||||
return GenericDesktopUsages[int(m_usage)];
|
||||
case HIDUsagePage::Game:
|
||||
if (int(m_usage) >= std::extent<decltype(GameUsages)>::value)
|
||||
return nullptr;
|
||||
return GameUsages[int(m_usage)];
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static HIDParser::ParserStatus
|
||||
AdvanceIt(const uint8_t*& it, const uint8_t* end, size_t adv)
|
||||
{
|
||||
it += adv;
|
||||
if (it > end)
|
||||
{
|
||||
it = end;
|
||||
return HIDParser::ParserStatus::Error;
|
||||
}
|
||||
else if (it == end)
|
||||
{
|
||||
return HIDParser::ParserStatus::Done;
|
||||
}
|
||||
return HIDParser::ParserStatus::OK;
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
GetByteValue(const uint8_t*& it, const uint8_t* end, HIDParser::ParserStatus& status)
|
||||
{
|
||||
const uint8_t* oldIt = it;
|
||||
status = AdvanceIt(it, end, 1);
|
||||
if (status == HIDParser::ParserStatus::Error)
|
||||
return 0;
|
||||
return *oldIt;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
GetShortValue(const uint8_t*& it, const uint8_t* end, int adv, HIDParser::ParserStatus& status)
|
||||
{
|
||||
const uint8_t* oldIt = it;
|
||||
switch (adv)
|
||||
{
|
||||
case 1:
|
||||
status = AdvanceIt(it, end, 1);
|
||||
if (status == HIDParser::ParserStatus::Error)
|
||||
return 0;
|
||||
return *oldIt;
|
||||
case 2:
|
||||
status = AdvanceIt(it, end, 2);
|
||||
if (status == HIDParser::ParserStatus::Error)
|
||||
return 0;
|
||||
return *reinterpret_cast<const uint16_t*>(&*oldIt);
|
||||
case 3:
|
||||
status = AdvanceIt(it, end, 4);
|
||||
if (status == HIDParser::ParserStatus::Error)
|
||||
return 0;
|
||||
return *reinterpret_cast<const uint32_t*>(&*oldIt);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct HIDReports
|
||||
{
|
||||
std::map<int32_t, std::vector<HIDMainItem>> m_inputReports;
|
||||
std::map<int32_t, std::vector<HIDMainItem>> m_outputReports;
|
||||
std::map<int32_t, std::vector<HIDMainItem>> m_featureReports;
|
||||
|
||||
static void _AddItem(std::map<int32_t, std::vector<HIDMainItem>>& m, uint32_t flags, const HIDItemState& state)
|
||||
{
|
||||
std::vector<HIDMainItem>& report = m[state.m_reportID];
|
||||
report.reserve(report.size() + state.m_reportCount);
|
||||
for (int i=0 ; i<state.m_reportCount ; ++i)
|
||||
report.emplace_back(flags, state, i);
|
||||
}
|
||||
|
||||
void AddInputItem(uint32_t flags, const HIDItemState& state) { _AddItem(m_inputReports, flags, state); }
|
||||
void AddOutputItem(uint32_t flags, const HIDItemState& state) { _AddItem(m_outputReports, flags, state); }
|
||||
void AddFeatureItem(uint32_t flags, const HIDItemState& state) { _AddItem(m_featureReports, flags, state); }
|
||||
};
|
||||
|
||||
HIDParser::ParserStatus
|
||||
HIDParser::ParseItem(HIDReports& reportsOut,
|
||||
std::stack<HIDItemState>& stateStack, std::stack<HIDCollectionItem>& collectionStack,
|
||||
const uint8_t*& it, const uint8_t* end)
|
||||
{
|
||||
ParserStatus status = ParserStatus::OK;
|
||||
uint8_t head = *it++;
|
||||
if (head == 0b11111110)
|
||||
{
|
||||
/* Long item */
|
||||
uint8_t bDataSize = GetByteValue(it, end, status);
|
||||
if (status == ParserStatus::Error)
|
||||
return ParserStatus::Error;
|
||||
uint8_t bLongItemTag = GetByteValue(it, end, status);
|
||||
if (status == ParserStatus::Error)
|
||||
return ParserStatus::Error;
|
||||
status = AdvanceIt(it, end, bDataSize);
|
||||
if (status == ParserStatus::Error)
|
||||
return ParserStatus::Error;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Short Item */
|
||||
uint32_t data = GetShortValue(it, end, head & 0x3, status);
|
||||
if (status == ParserStatus::Error)
|
||||
return ParserStatus::Error;
|
||||
|
||||
switch (HIDItemType((head >> 2) & 0x3))
|
||||
{
|
||||
case HIDItemType::Main:
|
||||
switch (HIDItemTag(head >> 4))
|
||||
{
|
||||
case HIDItemTag::Input:
|
||||
reportsOut.AddInputItem(data, stateStack.top());
|
||||
break;
|
||||
case HIDItemTag::Output:
|
||||
reportsOut.AddOutputItem(data, stateStack.top());
|
||||
break;
|
||||
case HIDItemTag::Feature:
|
||||
reportsOut.AddFeatureItem(data, stateStack.top());
|
||||
break;
|
||||
case HIDItemTag::Collection:
|
||||
collectionStack.emplace(HIDCollectionType(data), stateStack.top());
|
||||
break;
|
||||
case HIDItemTag::EndCollection:
|
||||
if (collectionStack.empty())
|
||||
return ParserStatus::Error;
|
||||
collectionStack.pop();
|
||||
break;
|
||||
default:
|
||||
return ParserStatus::Error;
|
||||
}
|
||||
stateStack.top().ResetLocalItems();
|
||||
break;
|
||||
case HIDItemType::Global:
|
||||
switch (HIDItemTag(head >> 4))
|
||||
{
|
||||
case HIDItemTag::UsagePage:
|
||||
stateStack.top().m_usagePage = HIDUsagePage(data);
|
||||
break;
|
||||
case HIDItemTag::LogicalMinimum:
|
||||
stateStack.top().m_logicalRange.first = data;
|
||||
break;
|
||||
case HIDItemTag::LogicalMaximum:
|
||||
stateStack.top().m_logicalRange.second = data;
|
||||
break;
|
||||
case HIDItemTag::PhysicalMinimum:
|
||||
stateStack.top().m_physicalRange.first = data;
|
||||
break;
|
||||
case HIDItemTag::PhysicalMaximum:
|
||||
stateStack.top().m_physicalRange.second = data;
|
||||
break;
|
||||
case HIDItemTag::UnitExponent:
|
||||
stateStack.top().m_unitExponent = data;
|
||||
break;
|
||||
case HIDItemTag::Unit:
|
||||
stateStack.top().m_unit = data;
|
||||
break;
|
||||
case HIDItemTag::ReportSize:
|
||||
stateStack.top().m_reportSize = data;
|
||||
break;
|
||||
case HIDItemTag::ReportID:
|
||||
m_multipleReports = true;
|
||||
stateStack.top().m_reportID = data;
|
||||
break;
|
||||
case HIDItemTag::ReportCount:
|
||||
stateStack.top().m_reportCount = data;
|
||||
break;
|
||||
case HIDItemTag::Push:
|
||||
stateStack.push(stateStack.top());
|
||||
break;
|
||||
case HIDItemTag::Pop:
|
||||
if (stateStack.empty())
|
||||
return ParserStatus::Error;
|
||||
stateStack.pop();
|
||||
break;
|
||||
default:
|
||||
return ParserStatus::Error;
|
||||
}
|
||||
break;
|
||||
case HIDItemType::Local:
|
||||
switch (HIDItemTag(head >> 4))
|
||||
{
|
||||
case HIDItemTag::Usage:
|
||||
stateStack.top().m_usage.push_back(HIDUsage(data));
|
||||
break;
|
||||
case HIDItemTag::UsageMinimum:
|
||||
stateStack.top().m_usageRange.first = data;
|
||||
break;
|
||||
case HIDItemTag::UsageMaximum:
|
||||
stateStack.top().m_usageRange.second = data;
|
||||
break;
|
||||
case HIDItemTag::DesignatorIndex:
|
||||
case HIDItemTag::DesignatorMinimum:
|
||||
case HIDItemTag::DesignatorMaximum:
|
||||
case HIDItemTag::StringIndex:
|
||||
case HIDItemTag::StringMinimum:
|
||||
case HIDItemTag::StringMaximum:
|
||||
case HIDItemTag::Delimiter:
|
||||
break;
|
||||
default:
|
||||
return ParserStatus::Error;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return ParserStatus::Error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return it == end ? ParserStatus::Done : ParserStatus::OK;
|
||||
}
|
||||
|
||||
HIDParser::ParserStatus HIDParser::Parse(const uint8_t* descriptorData, size_t len)
|
||||
{
|
||||
std::stack<HIDItemState> stateStack;
|
||||
stateStack.emplace();
|
||||
std::stack<HIDCollectionItem> collectionStack;
|
||||
HIDReports reports;
|
||||
|
||||
const uint8_t* end = descriptorData + len;
|
||||
for (const uint8_t* it = descriptorData; it != end;)
|
||||
if ((m_status = ParseItem(reports, stateStack, collectionStack, it, end)) != ParserStatus::OK)
|
||||
break;
|
||||
|
||||
if (m_status != ParserStatus::Done)
|
||||
return m_status;
|
||||
|
||||
uint32_t itemCount = 0;
|
||||
uint32_t reportCount = uint32_t(reports.m_inputReports.size() + reports.m_outputReports.size() +
|
||||
reports.m_featureReports.size());
|
||||
|
||||
for (const auto& rep : reports.m_inputReports)
|
||||
itemCount += rep.second.size();
|
||||
for (const auto& rep : reports.m_outputReports)
|
||||
itemCount += rep.second.size();
|
||||
for (const auto& rep : reports.m_featureReports)
|
||||
itemCount += rep.second.size();
|
||||
|
||||
m_itemPool.reset(new HIDMainItem[itemCount]);
|
||||
m_reportPool.reset(new Report[reportCount]);
|
||||
|
||||
uint32_t itemIndex = 0;
|
||||
uint32_t reportIndex = 0;
|
||||
|
||||
auto func = [&](std::pair<uint32_t, uint32_t>& out, const std::map<int32_t, std::vector<HIDMainItem>>& in)
|
||||
{
|
||||
out = std::make_pair(reportIndex, reportIndex + in.size());
|
||||
for (const auto& rep : in)
|
||||
{
|
||||
m_reportPool[reportIndex++] =
|
||||
std::make_pair(rep.first, std::make_pair(itemIndex, itemIndex + rep.second.size()));
|
||||
for (const auto& item : rep.second)
|
||||
m_itemPool[itemIndex++] = item;
|
||||
}
|
||||
};
|
||||
func(m_inputReports, reports.m_inputReports);
|
||||
func(m_outputReports, reports.m_outputReports);
|
||||
func(m_featureReports, reports.m_featureReports);
|
||||
|
||||
return m_status;
|
||||
}
|
||||
|
||||
void HIDParser::EnumerateValues(std::function<bool(uint32_t rep, const HIDMainItem& item)>& valueCB) const
|
||||
{
|
||||
if (m_status != ParserStatus::Done)
|
||||
return;
|
||||
|
||||
for (uint32_t i=m_inputReports.first ; i<m_inputReports.second ; ++i)
|
||||
{
|
||||
const Report& rep = m_reportPool[i];
|
||||
for (uint32_t j=rep.second.first ; j<rep.second.second ; ++j)
|
||||
{
|
||||
const HIDMainItem& item = m_itemPool[j];
|
||||
if (!valueCB(rep.first, item))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BitwiseIterator
|
||||
{
|
||||
const uint8_t*& m_it;
|
||||
const uint8_t* m_end;
|
||||
int m_bit = 0;
|
||||
public:
|
||||
BitwiseIterator(const uint8_t*& it, const uint8_t* end)
|
||||
: m_it(it), m_end(end) {}
|
||||
|
||||
uint32_t GetUnsignedValue(int numBits, HIDParser::ParserStatus& status)
|
||||
{
|
||||
uint32_t val = 0;
|
||||
for (int i=0 ; i<numBits ;)
|
||||
{
|
||||
if (m_it >= m_end)
|
||||
{
|
||||
status = HIDParser::ParserStatus::Error;
|
||||
return 0;
|
||||
}
|
||||
int remBits = std::min(8 - m_bit, numBits - i);
|
||||
val |= uint32_t((*m_it >> m_bit) & ((1 << remBits) - 1)) << i;
|
||||
i += remBits;
|
||||
m_bit += remBits;
|
||||
if (m_bit == 8)
|
||||
{
|
||||
m_bit = 0;
|
||||
AdvanceIt(m_it, m_end, 1);
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
void HIDParser::ScanValues(std::function<bool(const HIDMainItem& item, int32_t value)>& valueCB,
|
||||
const uint8_t* data, size_t len) const
|
||||
{
|
||||
if (m_status != ParserStatus::Done)
|
||||
return;
|
||||
|
||||
auto it = data;
|
||||
auto end = data + len;
|
||||
ParserStatus status = ParserStatus::OK;
|
||||
uint8_t reportId = 0;
|
||||
if (m_multipleReports)
|
||||
reportId = GetByteValue(it, end, status);
|
||||
if (status == ParserStatus::Error)
|
||||
return;
|
||||
|
||||
BitwiseIterator bitIt(it, end);
|
||||
|
||||
for (uint32_t i=m_inputReports.first ; i<m_inputReports.second ; ++i)
|
||||
{
|
||||
const Report& rep = m_reportPool[i];
|
||||
if (rep.first != reportId)
|
||||
continue;
|
||||
for (uint32_t j=rep.second.first ; j<rep.second.second ; ++j)
|
||||
{
|
||||
const HIDMainItem& item = m_itemPool[j];
|
||||
int32_t val = bitIt.GetUnsignedValue(item.m_reportSize, status);
|
||||
if (status == ParserStatus::Error)
|
||||
return;
|
||||
if (!valueCB(item, val))
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -3,22 +3,24 @@
|
|||
|
||||
#include "boo/inputdev/DeviceToken.hpp"
|
||||
#include "boo/inputdev/DeviceBase.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
class IHIDDevice
|
||||
class IHIDDevice : public std::enable_shared_from_this<IHIDDevice>
|
||||
{
|
||||
friend class DeviceBase;
|
||||
friend struct DeviceSignature;
|
||||
virtual void _deviceDisconnected()=0;
|
||||
virtual bool _sendUSBInterruptTransfer(const uint8_t* data, size_t length)=0;
|
||||
virtual size_t _receiveUSBInterruptTransfer(uint8_t* data, size_t length)=0;
|
||||
virtual std::vector<uint8_t> _getReportDescriptor()=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 void _startThread()=0;
|
||||
public:
|
||||
inline virtual ~IHIDDevice() {}
|
||||
virtual ~IHIDDevice() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -1282,14 +1282,17 @@ public:
|
|||
m_nsWindow = [[WindowCocoaInternal alloc] initWithBooWindow:windowPtr title:title];
|
||||
#if BOO_HAS_METAL
|
||||
if (metalCtx->m_dev)
|
||||
m_gfxCtx = static_cast<GraphicsContextCocoa*>(_GraphicsContextCocoaMetalNew(IGraphicsContext::EGraphicsAPI::Metal,
|
||||
this, metalCtx, sampleCount));
|
||||
m_gfxCtx = static_cast<GraphicsContextCocoa*>(
|
||||
_GraphicsContextCocoaMetalNew(IGraphicsContext::EGraphicsAPI::Metal,
|
||||
this, metalCtx, sampleCount));
|
||||
else
|
||||
#endif
|
||||
m_gfxCtx = static_cast<GraphicsContextCocoa*>(_GraphicsContextCocoaGLNew(IGraphicsContext::EGraphicsAPI::OpenGL3_3,
|
||||
this, lastGLCtx, sampleCount));
|
||||
m_gfxCtx = static_cast<GraphicsContextCocoa*>(
|
||||
_GraphicsContextCocoaGLNew(IGraphicsContext::EGraphicsAPI::OpenGL3_3,
|
||||
this, lastGLCtx, sampleCount));
|
||||
m_gfxCtx->initializeContext(nullptr);
|
||||
});
|
||||
m_gfxCtx->getMainContextDataFactory();
|
||||
}
|
||||
|
||||
void _clearWindow()
|
||||
|
|
|
@ -74,15 +74,48 @@ class DualshockPadCallback : public IDualshockPadCallback
|
|||
}
|
||||
};
|
||||
|
||||
class GenericPadCallback : public IGenericPadCallback
|
||||
{
|
||||
void controllerConnected()
|
||||
{
|
||||
printf("CONTROLLER CONNECTED\n");
|
||||
}
|
||||
void controllerDisconnected()
|
||||
{
|
||||
printf("CONTROLLER DISCONNECTED\n");
|
||||
}
|
||||
void valueUpdate(const HIDMainItem& item, int32_t value)
|
||||
{
|
||||
const char* pageName = item.GetUsagePageName();
|
||||
const char* usageName = item.GetUsageName();
|
||||
if (pageName)
|
||||
{
|
||||
if (usageName)
|
||||
printf("%s %s %d\n", pageName, usageName, value);
|
||||
else
|
||||
printf("%s %d %d\n", pageName, item.m_usage, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (usageName)
|
||||
printf("page%d %s %d\n", item.m_usagePage, usageName, value);
|
||||
else
|
||||
printf("page%d %d %d\n", item.m_usagePage, item.m_usage, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class TestDeviceFinder : public DeviceFinder
|
||||
{
|
||||
std::shared_ptr<DolphinSmashAdapter> smashAdapter;
|
||||
std::shared_ptr<DualshockPad> ds3;
|
||||
std::shared_ptr<GenericPad> generic;
|
||||
DolphinSmashAdapterCallback m_cb;
|
||||
DualshockPadCallback m_ds3CB;
|
||||
GenericPadCallback m_genericCb;
|
||||
public:
|
||||
TestDeviceFinder()
|
||||
: DeviceFinder({typeid(DolphinSmashAdapter)})
|
||||
: DeviceFinder({typeid(DolphinSmashAdapter), typeid(GenericPad)})
|
||||
{}
|
||||
void deviceConnected(DeviceToken& tok)
|
||||
{
|
||||
|
@ -99,6 +132,11 @@ public:
|
|||
ds3->setCallback(&m_ds3CB);
|
||||
ds3->setLED(EDualshockLED::LED_1);
|
||||
}
|
||||
generic = std::dynamic_pointer_cast<GenericPad>(tok.openAndGetDevice());
|
||||
if (generic)
|
||||
{
|
||||
generic->setCallback(&m_genericCb);
|
||||
}
|
||||
}
|
||||
void deviceDisconnected(DeviceToken&, DeviceBase* device)
|
||||
{
|
||||
|
@ -106,6 +144,8 @@ public:
|
|||
smashAdapter.reset();
|
||||
if (ds3.get() == device)
|
||||
ds3.reset();
|
||||
if (generic.get() == device)
|
||||
generic.reset();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue