mirror of
https://github.com/AxioDL/boo.git
synced 2025-07-03 19:55:58 +00:00
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/GenericPad.cpp include/boo/inputdev/GenericPad.hpp
|
||||||
lib/inputdev/DeviceSignature.cpp include/boo/inputdev/DeviceSignature.hpp
|
lib/inputdev/DeviceSignature.cpp include/boo/inputdev/DeviceSignature.hpp
|
||||||
lib/inputdev/DeviceFinder.cpp include/boo/inputdev/DeviceFinder.hpp
|
lib/inputdev/DeviceFinder.cpp include/boo/inputdev/DeviceFinder.hpp
|
||||||
|
lib/inputdev/HIDParser.cpp include/boo/inputdev/HIDParser.hpp
|
||||||
lib/inputdev/IHIDDevice.hpp
|
lib/inputdev/IHIDDevice.hpp
|
||||||
lib/audiodev/WAVOut.cpp
|
lib/audiodev/WAVOut.cpp
|
||||||
lib/audiodev/AudioMatrix.hpp
|
lib/audiodev/AudioMatrix.hpp
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "boo/inputdev/DeviceSignature.hpp"
|
#include "boo/inputdev/DeviceSignature.hpp"
|
||||||
#include "boo/inputdev/DolphinSmashAdapter.hpp"
|
#include "boo/inputdev/DolphinSmashAdapter.hpp"
|
||||||
#include "boo/inputdev/DualshockPad.hpp"
|
#include "boo/inputdev/DualshockPad.hpp"
|
||||||
|
#include "boo/inputdev/GenericPad.hpp"
|
||||||
#include "boo/inputdev/XInputPad.hpp"
|
#include "boo/inputdev/XInputPad.hpp"
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
@ -10,6 +11,7 @@ const DeviceSignature BOO_DEVICE_SIGS[] =
|
|||||||
{
|
{
|
||||||
DEVICE_SIG(DolphinSmashAdapter, 0x57e, 0x337, DeviceType::USB),
|
DEVICE_SIG(DolphinSmashAdapter, 0x57e, 0x337, DeviceType::USB),
|
||||||
DEVICE_SIG(DualshockPad, 0x54c, 0x268, DeviceType::HID),
|
DEVICE_SIG(DualshockPad, 0x54c, 0x268, DeviceType::HID),
|
||||||
|
DEVICE_SIG(GenericPad, 0, 0, DeviceType::HID),
|
||||||
DEVICE_SIG(XInputPad, 0, 0, DeviceType::XInput),
|
DEVICE_SIG(XInputPad, 0, 0, DeviceType::XInput),
|
||||||
DEVICE_SIG_SENTINEL()
|
DEVICE_SIG_SENTINEL()
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "inputdev/DeviceFinder.hpp"
|
#include "inputdev/DeviceFinder.hpp"
|
||||||
#include "inputdev/DolphinSmashAdapter.hpp"
|
#include "inputdev/DolphinSmashAdapter.hpp"
|
||||||
#include "inputdev/DualshockPad.hpp"
|
#include "inputdev/DualshockPad.hpp"
|
||||||
|
#include "inputdev/GenericPad.hpp"
|
||||||
#include "graphicsdev/IGraphicsCommandQueue.hpp"
|
#include "graphicsdev/IGraphicsCommandQueue.hpp"
|
||||||
#include "graphicsdev/IGraphicsDataFactory.hpp"
|
#include "graphicsdev/IGraphicsDataFactory.hpp"
|
||||||
#include "DeferredWindowEvents.hpp"
|
#include "DeferredWindowEvents.hpp"
|
||||||
|
@ -22,9 +22,10 @@ class DeviceBase : public std::enable_shared_from_this<DeviceBase>
|
|||||||
{
|
{
|
||||||
friend class DeviceToken;
|
friend class DeviceToken;
|
||||||
friend struct DeviceSignature;
|
friend struct DeviceSignature;
|
||||||
|
friend class HIDDeviceIOKit;
|
||||||
|
|
||||||
class DeviceToken* m_token;
|
class DeviceToken* m_token;
|
||||||
std::unique_ptr<IHIDDevice> m_hidDev;
|
std::shared_ptr<IHIDDevice> m_hidDev;
|
||||||
void _deviceDisconnected();
|
void _deviceDisconnected();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -38,13 +39,13 @@ public:
|
|||||||
virtual void initialCycle() {}
|
virtual void initialCycle() {}
|
||||||
virtual void transferCycle() {}
|
virtual void transferCycle() {}
|
||||||
virtual void finalCycle() {}
|
virtual void finalCycle() {}
|
||||||
virtual size_t getInputBufferSize() const { return 0; }
|
|
||||||
|
|
||||||
/* Low-Level API */
|
/* Low-Level API */
|
||||||
bool sendUSBInterruptTransfer(const uint8_t* data, size_t length);
|
bool sendUSBInterruptTransfer(const uint8_t* data, size_t length);
|
||||||
size_t receiveUSBInterruptTransfer(uint8_t* data, size_t length);
|
size_t receiveUSBInterruptTransfer(uint8_t* data, size_t length);
|
||||||
|
|
||||||
/* High-Level API */
|
/* High-Level API */
|
||||||
|
std::vector<uint8_t> getReportDescriptor();
|
||||||
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*/) {}
|
||||||
|
@ -182,8 +182,6 @@ public:
|
|||||||
m_report.leds = led;
|
m_report.leds = led;
|
||||||
sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01);
|
sendHIDReport(m_report.buf, sizeof(m_report), HIDReportType::Output, 0x01);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t getInputBufferSize() const { return 49; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,17 +2,34 @@
|
|||||||
#define CGENERICPAD_HPP
|
#define CGENERICPAD_HPP
|
||||||
|
|
||||||
#include "DeviceBase.hpp"
|
#include "DeviceBase.hpp"
|
||||||
|
#include "HIDParser.hpp"
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct IGenericPadCallback
|
||||||
|
{
|
||||||
|
virtual void controllerConnected() {}
|
||||||
|
virtual void controllerDisconnected() {}
|
||||||
|
virtual void valueUpdate(const HIDMainItem& item, int32_t value) {}
|
||||||
|
};
|
||||||
|
|
||||||
class GenericPad final : public DeviceBase
|
class GenericPad final : public DeviceBase
|
||||||
{
|
{
|
||||||
|
HIDParser m_parser;
|
||||||
|
IGenericPadCallback* m_cb = nullptr;
|
||||||
public:
|
public:
|
||||||
GenericPad(DeviceToken* token);
|
GenericPad(DeviceToken* token);
|
||||||
~GenericPad();
|
~GenericPad();
|
||||||
|
|
||||||
|
void setCallback(IGenericPadCallback* cb) { m_cb = cb; }
|
||||||
void deviceDisconnected();
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
195
include/boo/inputdev/HIDParser.hpp
Normal file
195
include/boo/inputdev/HIDParser.hpp
Normal file
@ -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)
|
static void RenderingWorker(GLCommandQueue* self)
|
||||||
{
|
{
|
||||||
|
logvisor::RegisterThreadName("Boo GL Rendering Thread");
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(self->m_initmt);
|
std::unique_lock<std::mutex> lk(self->m_initmt);
|
||||||
self->m_parent->makeCurrent();
|
self->m_parent->makeCurrent();
|
||||||
|
@ -54,6 +54,13 @@ size_t DeviceBase::receiveUSBInterruptTransfer(uint8_t* data, size_t length)
|
|||||||
return false;
|
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)
|
bool DeviceBase::sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||||
{
|
{
|
||||||
if (m_hidDev)
|
if (m_hidDev)
|
||||||
|
@ -13,13 +13,16 @@ bool DeviceSignature::DeviceMatchToken(const DeviceToken& token, const TDeviceSi
|
|||||||
{
|
{
|
||||||
if (token.getDeviceType() == DeviceType::HID)
|
if (token.getDeviceType() == DeviceType::HID)
|
||||||
{
|
{
|
||||||
|
bool hasGenericPad = false;
|
||||||
for (const DeviceSignature* sig : sigSet)
|
for (const DeviceSignature* sig : sigSet)
|
||||||
{
|
{
|
||||||
if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId() &&
|
if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId() &&
|
||||||
sig->m_type != DeviceType::HID)
|
sig->m_type != DeviceType::HID)
|
||||||
return false;
|
return false;
|
||||||
|
if (sig->m_typeIdx == typeid(GenericPad))
|
||||||
|
hasGenericPad = true;
|
||||||
}
|
}
|
||||||
return true;
|
return hasGenericPad;
|
||||||
}
|
}
|
||||||
for (const DeviceSignature* sig : sigSet)
|
for (const DeviceSignature* sig : sigSet)
|
||||||
{
|
{
|
||||||
@ -29,7 +32,7 @@ bool DeviceSignature::DeviceMatchToken(const DeviceToken& token, const TDeviceSi
|
|||||||
return false;
|
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> DeviceSignature::DeviceNew(DeviceToken& token)
|
||||||
{
|
{
|
||||||
std::shared_ptr<DeviceBase> retval;
|
std::shared_ptr<DeviceBase> retval;
|
||||||
@ -57,7 +60,7 @@ std::shared_ptr<DeviceBase> DeviceSignature::DeviceNew(DeviceToken& token)
|
|||||||
if (!retval)
|
if (!retval)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
retval->m_hidDev = IHIDDeviceNew(token, *retval);
|
retval->m_hidDev = IHIDDeviceNew(token, retval);
|
||||||
if (!retval->m_hidDev)
|
if (!retval->m_hidDev)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
retval->m_hidDev->_startThread();
|
retval->m_hidDev->_startThread();
|
||||||
@ -74,7 +77,7 @@ std::shared_ptr<DeviceBase> DeviceSignature::DeviceNew(DeviceToken& token)
|
|||||||
if (!retval)
|
if (!retval)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
retval->m_hidDev = IHIDDeviceNew(token, *retval);
|
retval->m_hidDev = IHIDDeviceNew(token, retval);
|
||||||
if (!retval->m_hidDev)
|
if (!retval->m_hidDev)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
retval->m_hidDev->_startThread();
|
retval->m_hidDev->_startThread();
|
||||||
|
@ -5,19 +5,45 @@ namespace boo
|
|||||||
{
|
{
|
||||||
|
|
||||||
GenericPad::GenericPad(DeviceToken* token)
|
GenericPad::GenericPad(DeviceToken* token)
|
||||||
: DeviceBase(token)
|
: DeviceBase(token)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericPad::~GenericPad()
|
GenericPad::~GenericPad() {}
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void GenericPad::deviceDisconnected()
|
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 "IHIDDevice.hpp"
|
||||||
#include "boo/inputdev/DeviceToken.hpp"
|
|
||||||
#include "boo/inputdev/DeviceBase.hpp"
|
|
||||||
#include <IOKit/hid/IOHIDLib.h>
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
#include <IOKit/hid/IOHIDDevicePlugin.h>
|
#include <IOKit/hid/IOHIDDevicePlugin.h>
|
||||||
#include <IOKit/hid/IOHIDDevice.h>
|
|
||||||
#include <IOKit/usb/IOUSBLib.h>
|
#include <IOKit/usb/IOUSBLib.h>
|
||||||
#include <IOKit/IOCFPlugIn.h>
|
|
||||||
#include "IOKitPointer.hpp"
|
#include "IOKitPointer.hpp"
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
@ -15,7 +11,7 @@ namespace boo
|
|||||||
class HIDDeviceIOKit : public IHIDDevice
|
class HIDDeviceIOKit : public IHIDDevice
|
||||||
{
|
{
|
||||||
DeviceToken& m_token;
|
DeviceToken& m_token;
|
||||||
DeviceBase& m_devImp;
|
std::shared_ptr<DeviceBase> m_devImp;
|
||||||
|
|
||||||
IUnknownPointer<IOUSBInterfaceInterface> m_usbIntf;
|
IUnknownPointer<IOUSBInterfaceInterface> m_usbIntf;
|
||||||
uint8_t m_usbIntfInPipe = 0;
|
uint8_t m_usbIntfInPipe = 0;
|
||||||
@ -52,6 +48,21 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
return 0;
|
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)
|
bool _sendHIDReport(const uint8_t* data, size_t length, HIDReportType tp, uint32_t message)
|
||||||
{
|
{
|
||||||
/* HACK: A bug in IOBluetoothGamepadHIDDriver prevents raw output report transmission
|
/* HACK: A bug in IOBluetoothGamepadHIDDriver prevents raw output report transmission
|
||||||
@ -77,7 +88,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _threadProcUSBLL(HIDDeviceIOKit* device)
|
static void _threadProcUSBLL(std::shared_ptr<HIDDeviceIOKit> device)
|
||||||
{
|
{
|
||||||
char thrName[128];
|
char thrName[128];
|
||||||
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().c_str());
|
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",
|
snprintf(errStr, 256, "Unable to find interface for %s@%s\n",
|
||||||
device->m_token.getProductName().c_str(),
|
device->m_token.getProductName().c_str(),
|
||||||
device->m_devPath.c_str());
|
device->m_devPath.c_str());
|
||||||
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;
|
||||||
@ -121,7 +132,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
{
|
{
|
||||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||||
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
||||||
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;
|
||||||
@ -134,7 +145,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
{
|
{
|
||||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||||
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
||||||
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;
|
||||||
@ -149,13 +160,13 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
{
|
{
|
||||||
snprintf(errStr, 256, "Unable to open %s@%s: someone else using it\n",
|
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_token.getProductName().c_str(), device->m_devPath.c_str());
|
||||||
device->m_devImp.deviceError(errStr);
|
device->m_devImp->deviceError(errStr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||||
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
||||||
device->m_devImp.deviceError(errStr);
|
device->m_devImp->deviceError(errStr);
|
||||||
}
|
}
|
||||||
lk.unlock();
|
lk.unlock();
|
||||||
device->m_initCond.notify_one();
|
device->m_initCond.notify_one();
|
||||||
@ -185,17 +196,17 @@ class HIDDeviceIOKit : 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 */
|
||||||
intf->USBInterfaceClose(intf.storage());
|
intf->USBInterfaceClose(intf.storage());
|
||||||
device->m_usbIntf = nullptr;
|
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);
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
|
|
||||||
@ -205,11 +216,17 @@ class HIDDeviceIOKit : 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _hidRemoveCb(void * _Nullable context,
|
||||||
|
IOReturn result,
|
||||||
|
void * _Nullable sender)
|
||||||
|
{
|
||||||
|
reinterpret_cast<HIDDeviceIOKit*>(context)->m_runningTransferLoop = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _hidReportCb(void * _Nullable context,
|
static void _hidReportCb(void * _Nullable context,
|
||||||
@ -223,7 +240,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
reinterpret_cast<DeviceBase*>(context)->receivedHIDReport(report, reportLength, HIDReportType(type), reportID);
|
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];
|
char thrName[128];
|
||||||
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().c_str());
|
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",
|
snprintf(errStr, 256, "Unable to find interface for %s@%s\n",
|
||||||
device->m_token.getProductName().c_str(),
|
device->m_token.getProductName().c_str(),
|
||||||
device->m_devPath.c_str());
|
device->m_devPath.c_str());
|
||||||
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;
|
||||||
@ -250,7 +267,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
{
|
{
|
||||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||||
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
||||||
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;
|
||||||
@ -264,30 +281,36 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
{
|
{
|
||||||
snprintf(errStr, 256, "Unable to open %s@%s: someone else using it\n",
|
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_token.getProductName().c_str(), device->m_devPath.c_str());
|
||||||
device->m_devImp.deviceError(errStr);
|
device->m_devImp->deviceError(errStr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
snprintf(errStr, 256, "Unable to open %s@%s\n",
|
||||||
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
device->m_token.getProductName().c_str(), device->m_devPath.c_str());
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Register removal callback */
|
||||||
|
IOHIDDeviceRegisterRemovalCallback(device->m_hidIntf.get(), _hidRemoveCb, device.get());
|
||||||
|
|
||||||
/* Make note if device uses bluetooth driver */
|
/* Make note if device uses bluetooth driver */
|
||||||
if (CFTypeRef transport = IOHIDDeviceGetProperty(device->m_hidIntf.get(), CFSTR(kIOHIDTransportKey)))
|
if (CFTypeRef transport = IOHIDDeviceGetProperty(device->m_hidIntf.get(), CFSTR(kIOHIDTransportKey)))
|
||||||
device->m_isBt = CFStringCompare(CFStringRef(transport), CFSTR(kIOHIDTransportBluetoothValue), 0) == kCFCompareEqualTo;
|
device->m_isBt = CFStringCompare(CFStringRef(transport), CFSTR(kIOHIDTransportBluetoothValue), 0) == kCFCompareEqualTo;
|
||||||
|
|
||||||
/* Register input buffer */
|
/* Register input buffer */
|
||||||
std::unique_ptr<uint8_t[]> 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]);
|
buffer = std::unique_ptr<uint8_t[]>(new uint8_t[bufSize]);
|
||||||
IOHIDDeviceRegisterInputReportCallback(device->m_hidIntf.get(), buffer.get(), 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);
|
IOHIDDeviceScheduleWithRunLoop(device->m_hidIntf.get(), CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,13 +320,14 @@ class HIDDeviceIOKit : 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)
|
||||||
{
|
{
|
||||||
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.010, true);
|
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 */
|
/* Cleanup */
|
||||||
IOHIDDeviceClose(device->m_hidIntf.get(), kIOHIDOptionsTypeNone);
|
IOHIDDeviceClose(device->m_hidIntf.get(), kIOHIDOptionsTypeNone);
|
||||||
@ -317,7 +341,7 @@ class HIDDeviceIOKit : public IHIDDevice
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
HIDDeviceIOKit(DeviceToken& token, DeviceBase& devImp)
|
HIDDeviceIOKit(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())
|
||||||
@ -329,11 +353,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<HIDDeviceIOKit>(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<HIDDeviceIOKit>(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<HIDDeviceIOKit>(shared_from_this()));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf(stderr, "invalid token supplied to device constructor\n");
|
fprintf(stderr, "invalid token supplied to device constructor\n");
|
||||||
@ -346,15 +370,13 @@ public:
|
|||||||
{
|
{
|
||||||
m_runningTransferLoop = false;
|
m_runningTransferLoop = false;
|
||||||
if (m_thread.joinable())
|
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)
|
HIDListenerIOKit(DeviceFinder& finder)
|
||||||
: m_finder(finder)
|
: m_finder(finder)
|
||||||
{
|
{
|
||||||
m_listenerRunLoop = CFRunLoopGetCurrent();
|
m_listenerRunLoop = CFRunLoopGetMain();
|
||||||
m_llPort = IONotificationPortCreate(kIOMasterPortDefault);
|
m_llPort = IONotificationPortCreate(kIOMasterPortDefault);
|
||||||
CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort);
|
CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort);
|
||||||
CFRunLoopAddSource(m_listenerRunLoop, rlSrc, kCFRunLoopDefaultMode);
|
CFRunLoopAddSource(m_listenerRunLoop, rlSrc, kCFRunLoopDefaultMode);
|
||||||
|
656
lib/inputdev/HIDParser.cpp
Normal file
656
lib/inputdev/HIDParser.cpp
Normal file
@ -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/DeviceToken.hpp"
|
||||||
#include "boo/inputdev/DeviceBase.hpp"
|
#include "boo/inputdev/DeviceBase.hpp"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace boo
|
namespace boo
|
||||||
{
|
{
|
||||||
|
|
||||||
class IHIDDevice
|
class IHIDDevice : public std::enable_shared_from_this<IHIDDevice>
|
||||||
{
|
{
|
||||||
friend class DeviceBase;
|
friend class DeviceBase;
|
||||||
friend struct DeviceSignature;
|
friend struct DeviceSignature;
|
||||||
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;
|
||||||
|
virtual std::vector<uint8_t> _getReportDescriptor()=0;
|
||||||
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;
|
||||||
public:
|
public:
|
||||||
inline virtual ~IHIDDevice() {}
|
virtual ~IHIDDevice() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1282,14 +1282,17 @@ public:
|
|||||||
m_nsWindow = [[WindowCocoaInternal alloc] initWithBooWindow:windowPtr title:title];
|
m_nsWindow = [[WindowCocoaInternal alloc] initWithBooWindow:windowPtr title:title];
|
||||||
#if BOO_HAS_METAL
|
#if BOO_HAS_METAL
|
||||||
if (metalCtx->m_dev)
|
if (metalCtx->m_dev)
|
||||||
m_gfxCtx = static_cast<GraphicsContextCocoa*>(_GraphicsContextCocoaMetalNew(IGraphicsContext::EGraphicsAPI::Metal,
|
m_gfxCtx = static_cast<GraphicsContextCocoa*>(
|
||||||
this, metalCtx, sampleCount));
|
_GraphicsContextCocoaMetalNew(IGraphicsContext::EGraphicsAPI::Metal,
|
||||||
|
this, metalCtx, sampleCount));
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
m_gfxCtx = static_cast<GraphicsContextCocoa*>(_GraphicsContextCocoaGLNew(IGraphicsContext::EGraphicsAPI::OpenGL3_3,
|
m_gfxCtx = static_cast<GraphicsContextCocoa*>(
|
||||||
this, lastGLCtx, sampleCount));
|
_GraphicsContextCocoaGLNew(IGraphicsContext::EGraphicsAPI::OpenGL3_3,
|
||||||
|
this, lastGLCtx, sampleCount));
|
||||||
m_gfxCtx->initializeContext(nullptr);
|
m_gfxCtx->initializeContext(nullptr);
|
||||||
});
|
});
|
||||||
|
m_gfxCtx->getMainContextDataFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _clearWindow()
|
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
|
class TestDeviceFinder : public DeviceFinder
|
||||||
{
|
{
|
||||||
std::shared_ptr<DolphinSmashAdapter> smashAdapter;
|
std::shared_ptr<DolphinSmashAdapter> smashAdapter;
|
||||||
std::shared_ptr<DualshockPad> ds3;
|
std::shared_ptr<DualshockPad> ds3;
|
||||||
|
std::shared_ptr<GenericPad> generic;
|
||||||
DolphinSmashAdapterCallback m_cb;
|
DolphinSmashAdapterCallback m_cb;
|
||||||
DualshockPadCallback m_ds3CB;
|
DualshockPadCallback m_ds3CB;
|
||||||
|
GenericPadCallback m_genericCb;
|
||||||
public:
|
public:
|
||||||
TestDeviceFinder()
|
TestDeviceFinder()
|
||||||
: DeviceFinder({typeid(DolphinSmashAdapter)})
|
: DeviceFinder({typeid(DolphinSmashAdapter), typeid(GenericPad)})
|
||||||
{}
|
{}
|
||||||
void deviceConnected(DeviceToken& tok)
|
void deviceConnected(DeviceToken& tok)
|
||||||
{
|
{
|
||||||
@ -99,6 +132,11 @@ public:
|
|||||||
ds3->setCallback(&m_ds3CB);
|
ds3->setCallback(&m_ds3CB);
|
||||||
ds3->setLED(EDualshockLED::LED_1);
|
ds3->setLED(EDualshockLED::LED_1);
|
||||||
}
|
}
|
||||||
|
generic = std::dynamic_pointer_cast<GenericPad>(tok.openAndGetDevice());
|
||||||
|
if (generic)
|
||||||
|
{
|
||||||
|
generic->setCallback(&m_genericCb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void deviceDisconnected(DeviceToken&, DeviceBase* device)
|
void deviceDisconnected(DeviceToken&, DeviceBase* device)
|
||||||
{
|
{
|
||||||
@ -106,6 +144,8 @@ public:
|
|||||||
smashAdapter.reset();
|
smashAdapter.reset();
|
||||||
if (ds3.get() == device)
|
if (ds3.get() == device)
|
||||||
ds3.reset();
|
ds3.reset();
|
||||||
|
if (generic.get() == device)
|
||||||
|
generic.reset();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user