From edaebc5e508c79d01f7649d9a69c3cb432f593ed Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Mon, 20 Apr 2015 18:02:43 -1000 Subject: [PATCH] lots of HID implementation (OS X only for now) --- include/boo.hpp | 9 +- include/inputdev/CDeviceBase.hpp | 11 +- include/inputdev/CDeviceFinder.hpp | 126 ++++++++++++++++++++++ include/inputdev/CDeviceToken.hpp | 60 +++++++++-- include/inputdev/CDolphinSmashAdapter.hpp | 12 +++ include/inputdev/DeviceClasses.hpp | 24 +++++ libBoo.pri | 5 +- src/inputdev/CDeviceBase.cpp | 12 +++ src/inputdev/CDeviceFinder.cpp | 1 - src/inputdev/CDolphinSmashAdapter.cpp | 5 + src/inputdev/CHIDDeviceIOKit.mm | 29 +++++ src/inputdev/CHIDListenerIOKit.cpp | 114 ++++++++++++++++++++ src/inputdev/CHIDListenerIOKit.mm | 1 - src/inputdev/DeviceClasses.cpp | 21 ++++ src/inputdev/IHIDDevice.hpp | 6 +- src/inputdev/IHIDListener.hpp | 18 ++++ test/main.cpp | 7 ++ 17 files changed, 443 insertions(+), 18 deletions(-) create mode 100644 include/inputdev/DeviceClasses.hpp delete mode 100644 src/inputdev/CDeviceFinder.cpp create mode 100644 src/inputdev/CHIDListenerIOKit.cpp delete mode 100644 src/inputdev/CHIDListenerIOKit.mm create mode 100644 src/inputdev/DeviceClasses.cpp diff --git a/include/boo.hpp b/include/boo.hpp index 8eba4dc..7821f5c 100644 --- a/include/boo.hpp +++ b/include/boo.hpp @@ -1,16 +1,21 @@ #ifndef BOO_HPP #define BOO_HPP -#include "IGraphicsContext.hpp" - #if defined(_WIN32) #error "No support for WGL" + #elif defined(__APPLE__) #include "mac/CCGLContext.hpp" typedef CCGLContext CGraphicsContext; + + #elif defined(__GNUC__) || defined(__clang__) #include "x11/CGLXContext.hpp" typedef CGLXContext CGraphicsContext; + #endif +#include "IGraphicsContext.hpp" +#include "inputdev/CDeviceFinder.hpp" + #endif // BOO_HPP diff --git a/include/inputdev/CDeviceBase.hpp b/include/inputdev/CDeviceBase.hpp index 0fa5876..22656c3 100644 --- a/include/inputdev/CDeviceBase.hpp +++ b/include/inputdev/CDeviceBase.hpp @@ -1,13 +1,18 @@ #ifndef CDEVICEBASE #define CDEVICEBASE -#include "CDeviceToken.hpp" +class CDeviceToken; +class IHIDDevice; class CDeviceBase { - + CDeviceToken* m_token; + IHIDDevice* m_hidDev; public: - + inline CDeviceBase(CDeviceToken* token, IHIDDevice* hidDev) + : m_token(token), m_hidDev(hidDev) {} + void _deviceDisconnected(); + virtual void deviceDisconnected()=0; }; #endif // CDEVICEBASE diff --git a/include/inputdev/CDeviceFinder.hpp b/include/inputdev/CDeviceFinder.hpp index f096170..54678f9 100644 --- a/include/inputdev/CDeviceFinder.hpp +++ b/include/inputdev/CDeviceFinder.hpp @@ -1,5 +1,131 @@ #ifndef CDEVICEFINDER_HPP #define CDEVICEFINDER_HPP +#include +#include +#include +#include "CDeviceToken.hpp" +#include "IHIDListener.hpp" +#include "DeviceClasses.hpp" + +static class CDeviceFinder* skDevFinder = NULL; + +class CDeviceFinder +{ +public: + friend class CHIDListenerIOKit; + friend class CHIDListenerUdev; + friend class CHIDListenerWin32; + static inline CDeviceFinder* instance() {return skDevFinder;} + +private: + + /* Types this finder is interested in (immutable) */ + EDeviceMask m_types; + + /* Platform-specific USB event registration + * (for auto-scanning, NULL if not registered) */ + IHIDListener* m_listener; + + /* Set of presently-connected device tokens */ + TDeviceTokens m_tokens; + std::mutex m_tokensLock; + + /* Friend methods for platform-listener to find/insert/remove + * tokens with type-filtering */ + inline bool _hasToken(TDeviceHandle handle) + { + auto preCheck = m_tokens.find(handle); + if (preCheck != m_tokens.end()) + return true; + return false; + } + inline void _insertToken(CDeviceToken&& token) + { + if (BooDeviceMatchToken(token, m_types)) { + m_tokensLock.lock(); + m_tokens.insert(std::make_pair(token.getDeviceHandle(), token)); + m_tokensLock.unlock(); + } + } + inline void _removeToken(TDeviceHandle handle) + { + auto preCheck = m_tokens.find(handle); + if (preCheck != m_tokens.end()) + { + CDeviceToken& tok = preCheck->second; + tok._deviceClose(); + m_tokensLock.lock(); + m_tokens.erase(preCheck); + m_tokensLock.unlock(); + } + } + +public: + + class CDeviceTokensHandle + { + CDeviceFinder& m_finder; + public: + inline CDeviceTokensHandle(CDeviceFinder& finder) : m_finder(finder) + {m_finder.m_tokensLock.lock();} + inline ~CDeviceTokensHandle() {m_finder.m_tokensLock.unlock();} + inline TDeviceTokens::iterator begin() {return m_finder.m_tokens.begin();} + inline TDeviceTokens::iterator end() {return m_finder.m_tokens.end();} + }; + + /* Application must specify its interested device-types */ + CDeviceFinder(EDeviceMask types, bool autoScan=true) + : m_types(types), m_listener(NULL) + { + if (skDevFinder) + throw std::runtime_error("only one instance of CDeviceFinder may be constructed"); + skDevFinder = this; + if (autoScan) + startScanning(); + } + ~CDeviceFinder() + { + if (m_listener) + m_listener->stopScanning(); + delete m_listener; + skDevFinder = NULL; + } + + /* Get interested device-type mask */ + inline EDeviceMask getTypes() const {return m_types;} + + /* Iterable set of tokens */ + inline CDeviceTokensHandle getTokens() {return CDeviceTokensHandle(*this);} + + /* Automatic device scanning */ + inline bool startScanning() + { + if (!m_listener) + m_listener = IHIDListenerNew(*this); + if (m_listener) + return m_listener->startScanning(); + return false; + } + inline bool stopScanning() + { + if (!m_listener) + m_listener = IHIDListenerNew(*this); + if (m_listener) + return m_listener->stopScanning(); + return false; + } + + /* Manual device scanning */ + inline bool scanNow() + { + if (!m_listener) + m_listener = IHIDListenerNew(*this); + if (m_listener) + return m_listener->scanNow(); + return false; + } + +}; #endif // CDEVICEFINDER_HPP diff --git a/include/inputdev/CDeviceToken.hpp b/include/inputdev/CDeviceToken.hpp index 253e4fe..b6069c3 100644 --- a/include/inputdev/CDeviceToken.hpp +++ b/include/inputdev/CDeviceToken.hpp @@ -2,21 +2,65 @@ #define CDEVICETOKEN #include -#include +#include "CDeviceBase.hpp" +#include "DeviceClasses.hpp" + +#if __APPLE__ +#include +typedef IOHIDDeviceRef TDeviceHandle; +#elif _WIN32 +#elif __linux__ +#endif class CDeviceBase; class CDeviceToken { - std::string m_name; -#if __APPLE__ + unsigned m_vendorId; + unsigned m_productId; + std::string m_vendorName; + std::string m_productName; + TDeviceHandle m_devHandle; -#elif _WIN32 -#elif __linux__ -#endif + friend CDeviceBase; + CDeviceBase* m_connectedDev; + public: - const std::string& getName() const; - CDeviceBase* getDevice() const; + inline CDeviceToken(unsigned vid, unsigned pid, const char* vname, const char* pname, TDeviceHandle handle) + : m_vendorId(vid), m_productId(pid), m_devHandle(handle), m_connectedDev(NULL) + { + if (vname) + m_vendorName = vname; + if (pname) + m_productName = pname; + } + + inline unsigned getVendorId() const {return m_vendorId;} + inline unsigned getProductId() const {return m_productId;} + inline const std::string& getVendorName() const {return m_vendorName;} + inline const std::string& getProductName() const {return m_productName;} + inline TDeviceHandle getDeviceHandle() const {return m_devHandle;} + inline bool isDeviceOpen() const {return m_connectedDev;} + inline CDeviceBase* openAndGetDevice() + { + if (!m_connectedDev) + m_connectedDev = BooDeviceNew(this); + return m_connectedDev; + } + + inline void _deviceClose() + { + if (m_connectedDev) + m_connectedDev->deviceDisconnected(); + m_connectedDev = NULL; + } + + inline CDeviceToken operator =(const CDeviceToken& other) + {return CDeviceToken(other);} + inline bool operator ==(const CDeviceToken& rhs) const + {return m_devHandle == rhs.m_devHandle;} + inline bool operator <(const CDeviceToken& rhs) const + {return m_devHandle < rhs.m_devHandle;} }; #endif // CDEVICETOKEN diff --git a/include/inputdev/CDolphinSmashAdapter.hpp b/include/inputdev/CDolphinSmashAdapter.hpp index 94fd679..68c7ae9 100644 --- a/include/inputdev/CDolphinSmashAdapter.hpp +++ b/include/inputdev/CDolphinSmashAdapter.hpp @@ -1,5 +1,17 @@ #ifndef CDOLPHINSMASHADAPTER_HPP #define CDOLPHINSMASHADAPTER_HPP +#include "CDeviceBase.hpp" + +class CDolphinSmashAdapter final : public CDeviceBase +{ + void deviceDisconnected(); +public: + CDolphinSmashAdapter(CDeviceToken* token, IHIDDevice* hidDev) + : CDeviceBase(token, hidDev) + { + + } +}; #endif // CDOLPHINSMASHADAPTER_HPP diff --git a/include/inputdev/DeviceClasses.hpp b/include/inputdev/DeviceClasses.hpp new file mode 100644 index 0000000..5b5320c --- /dev/null +++ b/include/inputdev/DeviceClasses.hpp @@ -0,0 +1,24 @@ +#ifndef CDEVICECLASSES_HPP +#define CDEVICECLASSES_HPP + +#include "CDolphinSmashAdapter.hpp" +#include "CRevolutionPad.hpp" +#include "CCafeProPad.hpp" +#include "CDualshockPad.hpp" +#include "CGenericPad.hpp" + +enum EDeviceMask +{ + DEV_NONE = 0, + DEV_DOL_SMASH_ADAPTER = 1<<0, + DEV_RVL_PAD = 1<<1, + DEV_CAFE_PRO_PAD = 1<<2, + DEV_DUALSHOCK_PAD = 1<<3, + DEV_GENERIC_PAD = 1<<4, + DEV_ALL = 0xff +}; + +bool BooDeviceMatchToken(const CDeviceToken& token, EDeviceMask mask); +CDeviceBase* BooDeviceNew(CDeviceToken* token); + +#endif // CDEVICECLASSES_HPP diff --git a/libBoo.pri b/libBoo.pri index 5561ba9..0f36c46 100644 --- a/libBoo.pri +++ b/libBoo.pri @@ -17,6 +17,7 @@ HEADERS += \ $$PWD/include/inputdev/CDeviceFinder.hpp \ $$PWD/include/inputdev/CDeviceToken.hpp \ $$PWD/include/inputdev/CDeviceBase.hpp \ + $$PWD/include/inputdev/DeviceClasses.hpp \ $$PWD/src/inputdev/IHIDDevice.hpp \ $$PWD/src/inputdev/IHIDListener.hpp @@ -39,8 +40,8 @@ SOURCES += \ $$PWD/src/inputdev/CCafeProPad.cpp \ $$PWD/src/inputdev/CDualshockPad.cpp \ $$PWD/src/inputdev/CGenericPad.cpp \ - $$PWD/src/inputdev/CDeviceFinder.cpp \ - $$PWD/src/inputdev/CDeviceBase.cpp + $$PWD/src/inputdev/CDeviceBase.cpp \ + $$PWD/src/inputdev/DeviceClasses.cpp unix:!macx:SOURCES += \ $$PWD/src/x11/CGLXContext.cpp \ diff --git a/src/inputdev/CDeviceBase.cpp b/src/inputdev/CDeviceBase.cpp index 9c5d1fa..2311700 100644 --- a/src/inputdev/CDeviceBase.cpp +++ b/src/inputdev/CDeviceBase.cpp @@ -1,3 +1,15 @@ #include "inputdev/CDeviceBase.hpp" +#include "inputdev/CDeviceToken.hpp" +#include "IHIDDevice.hpp" + + +void CDeviceBase::_deviceDisconnected() +{ + m_token->m_connectedDev = NULL; + m_token = NULL; + m_hidDev->deviceDisconnected(); + m_hidDev = NULL; +} + diff --git a/src/inputdev/CDeviceFinder.cpp b/src/inputdev/CDeviceFinder.cpp deleted file mode 100644 index 5d0b47b..0000000 --- a/src/inputdev/CDeviceFinder.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "inputdev/CDeviceFinder.hpp" diff --git a/src/inputdev/CDolphinSmashAdapter.cpp b/src/inputdev/CDolphinSmashAdapter.cpp index 4f67300..b25f0b4 100644 --- a/src/inputdev/CDolphinSmashAdapter.cpp +++ b/src/inputdev/CDolphinSmashAdapter.cpp @@ -1 +1,6 @@ #include "inputdev/CDolphinSmashAdapter.hpp" + +void CDolphinSmashAdapter::deviceDisconnected() +{ + +} diff --git a/src/inputdev/CHIDDeviceIOKit.mm b/src/inputdev/CHIDDeviceIOKit.mm index 2888b5b..e91faf8 100644 --- a/src/inputdev/CHIDDeviceIOKit.mm +++ b/src/inputdev/CHIDDeviceIOKit.mm @@ -1 +1,30 @@ #include "IHIDDevice.hpp" +#include "inputdev/CDeviceToken.hpp" +#include + +class CHIDDeviceIOKit final : public IHIDDevice +{ + IOHIDDeviceRef m_dev; +public: + + CHIDDeviceIOKit(CDeviceToken* token) + : m_dev(token->getDeviceHandle()) + { + IOHIDDeviceOpen(m_dev, kIOHIDOptionsTypeNone); + } + + CHIDDeviceIOKit() + { + IOHIDDeviceClose(m_dev, kIOHIDOptionsTypeNone); + } + + void deviceDisconnected() + { + IOHIDDeviceClose(m_dev, kIOHIDOptionsTypeNone); + } +}; + +IHIDDevice* IHIDDeviceNew(CDeviceToken* token) +{ + return new CHIDDeviceIOKit(token); +} diff --git a/src/inputdev/CHIDListenerIOKit.cpp b/src/inputdev/CHIDListenerIOKit.cpp new file mode 100644 index 0000000..7ee41df --- /dev/null +++ b/src/inputdev/CHIDListenerIOKit.cpp @@ -0,0 +1,114 @@ +#include "IHIDListener.hpp" +#include "inputdev/CDeviceFinder.hpp" +#include +#include + +class CHIDListenerIOKit final : public IHIDListener +{ + CDeviceFinder& m_finder; + + IOHIDManagerRef m_hidManager; + bool m_scanningEnabled; + + static void deviceConnected(CHIDListenerIOKit* listener, + IOReturn, + void*, + IOHIDDeviceRef device) + { + if (!listener->m_scanningEnabled) + return; + if (listener->m_finder._hasToken(device)) + return; + CFIndex vid, pid; + CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)), kCFNumberCFIndexType, &vid); + CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)), kCFNumberCFIndexType, &pid); + CFStringRef manuf = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManufacturerKey)); + CFStringRef product = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); + listener->m_finder._insertToken(CDeviceToken(vid, pid, + CFStringGetCStringPtr(manuf, kCFStringEncodingUTF8), + CFStringGetCStringPtr(product, kCFStringEncodingUTF8), + device)); + } + + static void deviceDisconnected(CHIDListenerIOKit* listener, + IOReturn, + void*, + IOHIDDeviceRef device) + {listener->m_finder._removeToken(device);} + + static void applyDevice(IOHIDDeviceRef device, CHIDListenerIOKit* listener) + { + auto preCheck = listener->m_finder.m_tokens.find(device); + if (preCheck != listener->m_finder.m_tokens.end()) + return; + CFIndex vid, pid; + CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDVendorIDKey)), kCFNumberCFIndexType, &vid); + CFNumberGetValue((CFNumberRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductIDKey)), kCFNumberCFIndexType, &pid); + CFStringRef manuf = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDManufacturerKey)); + CFStringRef product = (CFStringRef)IOHIDDeviceGetProperty(device, CFSTR(kIOHIDProductKey)); + listener->m_finder.m_tokens.insert(std::make_pair(device, + CDeviceToken(vid, pid, + CFStringGetCStringPtr(manuf, kCFStringEncodingUTF8), + CFStringGetCStringPtr(product, kCFStringEncodingUTF8), + device))); + } + +public: + CHIDListenerIOKit(CDeviceFinder& finder) + : m_finder(finder) + { + + /* Register HID Manager */ + m_hidManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDManagerOptionNone); + IOHIDManagerSetDeviceMatching(m_hidManager, NULL); + IOHIDManagerRegisterDeviceMatchingCallback(m_hidManager, (IOHIDDeviceCallback)deviceConnected, this); + IOHIDManagerRegisterDeviceRemovalCallback(m_hidManager, (IOHIDDeviceCallback)deviceDisconnected, this); + IOHIDManagerScheduleWithRunLoop(m_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOReturn ret = IOHIDManagerOpen(m_hidManager, kIOHIDManagerOptionNone); + if (ret != kIOReturnSuccess) + throw std::runtime_error("error establishing IOHIDManager"); + + /* Initial Device Add */ + m_scanningEnabled = true; + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); + m_scanningEnabled = false; + + } + + ~CHIDListenerIOKit() + { + IOHIDManagerUnscheduleFromRunLoop(m_hidManager, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + IOHIDManagerClose(m_hidManager, kIOHIDManagerOptionNone); + CFRelease(m_hidManager); + } + + /* Automatic device scanning */ + bool startScanning() + { + m_scanningEnabled = true; + return true; + } + bool stopScanning() + { + m_scanningEnabled = false; + return true; + } + + /* Manual device scanning */ + bool scanNow() + { + CFSetRef devs = IOHIDManagerCopyDevices(m_hidManager); + m_finder.m_tokensLock.lock(); + CFSetApplyFunction(devs, (CFSetApplierFunction)applyDevice, this); + m_finder.m_tokensLock.unlock(); + CFRelease(devs); + return true; + } + +}; + +IHIDListener* IHIDListenerNew(CDeviceFinder& finder) +{ + return new CHIDListenerIOKit(finder); +} + diff --git a/src/inputdev/CHIDListenerIOKit.mm b/src/inputdev/CHIDListenerIOKit.mm deleted file mode 100644 index 90f36b2..0000000 --- a/src/inputdev/CHIDListenerIOKit.mm +++ /dev/null @@ -1 +0,0 @@ -#include "IHIDListener.hpp" diff --git a/src/inputdev/DeviceClasses.cpp b/src/inputdev/DeviceClasses.cpp new file mode 100644 index 0000000..be5e530 --- /dev/null +++ b/src/inputdev/DeviceClasses.cpp @@ -0,0 +1,21 @@ +#include "inputdev/DeviceClasses.hpp" +#include "inputdev/CDeviceToken.hpp" + +bool BooDeviceMatchToken(const CDeviceToken& token, EDeviceMask mask) +{ + if (mask & DEV_DOL_SMASH_ADAPTER && + token.getVendorId() == 0x57e && token.getProductId() == 0x337) + return true; + return false; +} + +IHIDDevice* IHIDDeviceNew(CDeviceToken* token); +CDeviceBase* BooDeviceNew(CDeviceToken* token) +{ + IHIDDevice* newDev = IHIDDeviceNew(token); + + if (token->getVendorId() == 0x57e && token->getProductId() == 0x337) + return new CDolphinSmashAdapter(token, newDev); + + return NULL; +} diff --git a/src/inputdev/IHIDDevice.hpp b/src/inputdev/IHIDDevice.hpp index 869b179..9a1da5c 100644 --- a/src/inputdev/IHIDDevice.hpp +++ b/src/inputdev/IHIDDevice.hpp @@ -1,9 +1,13 @@ #ifndef IHIDDEVICE_HPP #define IHIDDEVICE_HPP +#include "inputdev/CDeviceToken.hpp" +class CDeviceBase; + class IHIDDevice { - + friend CDeviceBase; + virtual void deviceDisconnected()=0; }; #endif // IHIDDEVICE_HPP diff --git a/src/inputdev/IHIDListener.hpp b/src/inputdev/IHIDListener.hpp index a1118b1..8fbb46a 100644 --- a/src/inputdev/IHIDListener.hpp +++ b/src/inputdev/IHIDListener.hpp @@ -1,9 +1,27 @@ #ifndef IHIDLISTENER_HPP #define IHIDLISTENER_HPP +#include +#include +#include "CDeviceToken.hpp" +typedef std::map TDeviceTokens; +class CDeviceFinder; + class IHIDListener { +public: + virtual ~IHIDListener() {}; + + /* Automatic device scanning */ + virtual bool startScanning()=0; + virtual bool stopScanning()=0; + + /* Manual device scanning */ + virtual bool scanNow()=0; }; +/* Platform-specific constructor */ +IHIDListener* IHIDListenerNew(CDeviceFinder& finder); + #endif // IHIDLISTENER_HPP diff --git a/test/main.cpp b/test/main.cpp index f31a297..d3d3cef 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,14 +1,21 @@ +#include #include #include int main(int argc, char** argv) { + CDeviceFinder finder(DEV_DOL_SMASH_ADAPTER); + CDeviceToken& smashToken = finder.getTokens().begin()->second; + CDolphinSmashAdapter* smashAdapter = dynamic_cast(smashToken.openAndGetDevice()); + IGraphicsContext* ctx = new CGraphicsContext; if (ctx->create()) { } + + CFRunLoopRun(); delete ctx; return 0;