Add Cross-platform HID parser

This commit is contained in:
Jack Andersen 2017-09-15 07:20:52 -10:00
parent 82fec41f40
commit 0f2a838bfb
20 changed files with 1039 additions and 64 deletions

View File

@ -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

View File

@ -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()
};

View File

@ -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"

View File

@ -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*/) {}

View File

@ -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; }
};
}

View File

@ -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;
};
}

View 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

View File

@ -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();

View File

@ -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)

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);

656
lib/inputdev/HIDParser.cpp Normal file
View 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;
}
}
}

View File

@ -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;
};
}

View File

@ -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()

View File

@ -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();
}
};