mirror of https://github.com/AxioDL/boo.git
commit
cf9e12c650
|
@ -0,0 +1,13 @@
|
||||||
|
#include "inputdev/SDeviceSignature.hpp"
|
||||||
|
#include "inputdev/CDolphinSmashAdapter.hpp"
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
const SDeviceSignature BOO_DEVICE_SIGS[] =
|
||||||
|
{
|
||||||
|
DEVICE_SIG(CDolphinSmashAdapter, 0x57e, 0x337),
|
||||||
|
DEVICE_SIG_SENTINEL()
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -3,4 +3,9 @@
|
||||||
|
|
||||||
#include "IInputWaiter.hpp"
|
#include "IInputWaiter.hpp"
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // CINPUTDEFERREDRELAY_HPP
|
#endif // CINPUTDEFERREDRELAY_HPP
|
||||||
|
|
|
@ -3,9 +3,14 @@
|
||||||
|
|
||||||
#include "IInputWaiter.hpp"
|
#include "IInputWaiter.hpp"
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
class CInputRelay : IInputWaiter
|
class CInputRelay : IInputWaiter
|
||||||
{
|
{
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // CINPUTRELAY_HPP
|
#endif // CINPUTRELAY_HPP
|
||||||
|
|
|
@ -3,7 +3,12 @@
|
||||||
|
|
||||||
#include "ISurface.hpp"
|
#include "ISurface.hpp"
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
ISurface* CSurfaceNewWindow();
|
ISurface* CSurfaceNewWindow();
|
||||||
ISurface* CSurfaceNewQWidget();
|
ISurface* CSurfaceNewQWidget();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // CSURFACE_HPP
|
#endif // CSURFACE_HPP
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
class IGraphicsContext
|
class IGraphicsContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -19,4 +22,6 @@ public:
|
||||||
virtual int blueDepth() const=0;
|
virtual int blueDepth() const=0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // IGRAPHICSCONTEXT_HPP
|
#endif // IGRAPHICSCONTEXT_HPP
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
#ifndef IINPUTWAITER_HPP
|
#ifndef IINPUTWAITER_HPP
|
||||||
#define IINPUTWAITER_HPP
|
#define IINPUTWAITER_HPP
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
class IInputWaiter
|
class IInputWaiter
|
||||||
{
|
{
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // IINPUTWAITER_HPP
|
#endif // IINPUTWAITER_HPP
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
#ifndef IRETRACEWAITER_HPP
|
#ifndef IRETRACEWAITER_HPP
|
||||||
#define IRETRACEWAITER_HPP
|
#define IRETRACEWAITER_HPP
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
class IRetraceWaiter
|
class IRetraceWaiter
|
||||||
{
|
{
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // IRETRACEWAITER_HPP
|
#endif // IRETRACEWAITER_HPP
|
|
@ -1,10 +1,15 @@
|
||||||
#ifndef ISURFACE_HPP
|
#ifndef ISURFACE_HPP
|
||||||
#define ISURFACE_HPP
|
#define ISURFACE_HPP
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
class ISurface
|
class ISurface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // CSURFACE_HPP
|
#endif // CSURFACE_HPP
|
|
@ -1,16 +1,22 @@
|
||||||
#ifndef BOO_HPP
|
#ifndef BOO_HPP
|
||||||
#define BOO_HPP
|
#define BOO_HPP
|
||||||
|
|
||||||
#include "IGraphicsContext.hpp"
|
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#error "No support for WGL"
|
#include "win/CWGLContext.hpp"
|
||||||
|
namespace boo {typedef CWGLContext CGraphicsContext;}
|
||||||
|
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
#include "mac/CCGLContext.hpp"
|
#include "mac/CCGLContext.hpp"
|
||||||
typedef CCGLContext CGraphicsContext;
|
namespace boo {typedef CCGLContext CGraphicsContext;}
|
||||||
|
|
||||||
#elif defined(__GNUC__) || defined(__clang__)
|
#elif defined(__GNUC__) || defined(__clang__)
|
||||||
#include "x11/CGLXContext.hpp"
|
#include "x11/CGLXContext.hpp"
|
||||||
typedef CGLXContext CGraphicsContext;
|
namespace boo {typedef CGLXContext CGraphicsContext;}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "IGraphicsContext.hpp"
|
||||||
|
#include "inputdev/CDeviceFinder.hpp"
|
||||||
|
#include "inputdev/CDolphinSmashAdapter.hpp"
|
||||||
|
|
||||||
#endif // BOO_HPP
|
#endif // BOO_HPP
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
#ifndef CCAFEPROPAD_HPP
|
#ifndef CCAFEPROPAD_HPP
|
||||||
#define CCAFEPROPAD_HPP
|
#define CCAFEPROPAD_HPP
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // CCAFEPROPAD_HPP
|
#endif // CCAFEPROPAD_HPP
|
||||||
|
|
|
@ -1,13 +1,44 @@
|
||||||
#ifndef CDEVICEBASE
|
#ifndef CDEVICEBASE
|
||||||
#define CDEVICEBASE
|
#define CDEVICEBASE
|
||||||
|
|
||||||
#include "CDeviceToken.hpp"
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
class CDeviceBase
|
class CDeviceBase
|
||||||
{
|
{
|
||||||
|
friend class CDeviceToken;
|
||||||
|
friend class CHIDDeviceIOKit;
|
||||||
|
friend class CHIDDeviceUdev;
|
||||||
|
friend class CHIDDeviceWinUSB;
|
||||||
|
|
||||||
|
class CDeviceToken* m_token;
|
||||||
|
class IHIDDevice* m_hidDev;
|
||||||
|
void _deviceDisconnected();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
CDeviceBase(CDeviceToken* token);
|
||||||
|
virtual ~CDeviceBase();
|
||||||
|
void closeDevice();
|
||||||
|
virtual void deviceDisconnected()=0;
|
||||||
|
virtual void deviceError(const char* error) {fprintf(stderr, "%s\n", error);}
|
||||||
|
|
||||||
|
/* Low-Level API */
|
||||||
|
bool sendUSBInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length);
|
||||||
|
size_t receiveUSBInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length);
|
||||||
|
virtual void initialCycle() {}
|
||||||
|
virtual void transferCycle() {}
|
||||||
|
virtual void finalCycle() {}
|
||||||
|
|
||||||
|
/* High-Level API */
|
||||||
|
bool sendHIDReport(const uint8_t* data, size_t length);
|
||||||
|
virtual size_t receiveReport(uint8_t* data, size_t length) {return 0;}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // CDEVICEBASE
|
#endif // CDEVICEBASE
|
||||||
|
|
|
@ -1,5 +1,201 @@
|
||||||
#ifndef CDEVICEFINDER_HPP
|
#ifndef CDEVICEFINDER_HPP
|
||||||
#define CDEVICEFINDER_HPP
|
#define CDEVICEFINDER_HPP
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <mutex>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include "CDeviceToken.hpp"
|
||||||
|
#include "IHIDListener.hpp"
|
||||||
|
#include "SDeviceSignature.hpp"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define _WIN32_LEAN_AND_MEAN 1
|
||||||
|
#include <windows.h>
|
||||||
|
#include <Dbt.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
static class CDeviceFinder* skDevFinder = NULL;
|
||||||
|
|
||||||
|
class CDeviceFinder
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
friend class CHIDListenerIOKit;
|
||||||
|
friend class CHIDListenerUdev;
|
||||||
|
friend class CHIDListenerWinUSB;
|
||||||
|
static inline CDeviceFinder* instance() {return skDevFinder;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/* Types this finder is interested in (immutable) */
|
||||||
|
SDeviceSignature::TDeviceSignatureSet 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(const std::string& path)
|
||||||
|
{
|
||||||
|
auto preCheck = m_tokens.find(path);
|
||||||
|
if (preCheck != m_tokens.end())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline bool _insertToken(CDeviceToken&& token)
|
||||||
|
{
|
||||||
|
if (SDeviceSignature::DeviceMatchToken(token, m_types)) {
|
||||||
|
m_tokensLock.lock();
|
||||||
|
TInsertedDeviceToken inseredTok =
|
||||||
|
m_tokens.insert(std::make_pair(token.getDevicePath(), std::move(token)));
|
||||||
|
m_tokensLock.unlock();
|
||||||
|
deviceConnected(inseredTok.first->second);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline void _removeToken(const std::string& path)
|
||||||
|
{
|
||||||
|
auto preCheck = m_tokens.find(path);
|
||||||
|
if (preCheck != m_tokens.end())
|
||||||
|
{
|
||||||
|
CDeviceToken& tok = preCheck->second;
|
||||||
|
CDeviceBase* dev = tok.m_connectedDev;
|
||||||
|
tok._deviceClose();
|
||||||
|
deviceDisconnected(tok, dev);
|
||||||
|
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(std::vector<const char*> types)
|
||||||
|
: m_listener(NULL)
|
||||||
|
{
|
||||||
|
if (skDevFinder)
|
||||||
|
throw std::runtime_error("only one instance of CDeviceFinder may be constructed");
|
||||||
|
skDevFinder = this;
|
||||||
|
for (const char* typeName : types)
|
||||||
|
{
|
||||||
|
const SDeviceSignature* sigIter = BOO_DEVICE_SIGS;
|
||||||
|
while (sigIter->m_name)
|
||||||
|
{
|
||||||
|
if (!strcmp(sigIter->m_name, typeName))
|
||||||
|
m_types.push_back(sigIter);
|
||||||
|
++sigIter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
~CDeviceFinder()
|
||||||
|
{
|
||||||
|
if (m_listener)
|
||||||
|
m_listener->stopScanning();
|
||||||
|
delete m_listener;
|
||||||
|
skDevFinder = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get interested device-type mask */
|
||||||
|
inline const SDeviceSignature::TDeviceSignatureSet& 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void deviceConnected(CDeviceToken&) {}
|
||||||
|
virtual void deviceDisconnected(CDeviceToken&, CDeviceBase*) {}
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
/* Windows-specific WM_DEVICECHANGED handler */
|
||||||
|
static LRESULT winDevChangedHandler(WPARAM wParam, LPARAM lParam)
|
||||||
|
{
|
||||||
|
PDEV_BROADCAST_HDR dbh = (PDEV_BROADCAST_HDR)lParam;
|
||||||
|
PDEV_BROADCAST_DEVICEINTERFACE dbhi = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
|
||||||
|
CDeviceFinder* finder = instance();
|
||||||
|
if (!finder)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (wParam == DBT_DEVICEARRIVAL)
|
||||||
|
{
|
||||||
|
if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
|
||||||
|
{
|
||||||
|
#ifdef UNICODE
|
||||||
|
char devPath[1024];
|
||||||
|
wcstombs(devPath, dbhi->dbcc_name, 1024);
|
||||||
|
finder->m_listener->_extDevConnect(devPath);
|
||||||
|
#else
|
||||||
|
finder->m_listener->_extDevConnect(dbhi->dbcc_name);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (wParam == DBT_DEVICEREMOVECOMPLETE)
|
||||||
|
{
|
||||||
|
if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE)
|
||||||
|
{
|
||||||
|
#ifdef UNICODE
|
||||||
|
char devPath[1024];
|
||||||
|
wcstombs(devPath, dbhi->dbcc_name, 1024);
|
||||||
|
finder->m_listener->_extDevDisconnect(devPath);
|
||||||
|
#else
|
||||||
|
finder->m_listener->_extDevDisconnect(dbhi->dbcc_name);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // CDEVICEFINDER_HPP
|
#endif // CDEVICEFINDER_HPP
|
||||||
|
|
|
@ -2,21 +2,87 @@
|
||||||
#define CDEVICETOKEN
|
#define CDEVICETOKEN
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <IOKit/hid/IOHIDDevice.h>
|
#include "CDeviceBase.hpp"
|
||||||
|
#include "SDeviceSignature.hpp"
|
||||||
|
|
||||||
class CDeviceBase;
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
class CDeviceToken
|
class CDeviceToken
|
||||||
{
|
{
|
||||||
std::string m_name;
|
|
||||||
#if __APPLE__
|
|
||||||
|
|
||||||
#elif _WIN32
|
|
||||||
#elif __linux__
|
|
||||||
#endif
|
|
||||||
public:
|
public:
|
||||||
const std::string& getName() const;
|
enum TDeviceType
|
||||||
CDeviceBase* getDevice() const;
|
{
|
||||||
|
DEVTYPE_NONE = 0,
|
||||||
|
DEVTYPE_USB = 1,
|
||||||
|
DEVTYPE_BLUETOOTH = 2,
|
||||||
|
DEVTYPE_GENERICHID = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
TDeviceType m_devType;
|
||||||
|
unsigned m_vendorId;
|
||||||
|
unsigned m_productId;
|
||||||
|
std::string m_vendorName;
|
||||||
|
std::string m_productName;
|
||||||
|
std::string m_devPath;
|
||||||
|
|
||||||
|
friend class CDeviceBase;
|
||||||
|
CDeviceBase* m_connectedDev;
|
||||||
|
|
||||||
|
friend class CDeviceFinder;
|
||||||
|
inline void _deviceClose()
|
||||||
|
{
|
||||||
|
if (m_connectedDev)
|
||||||
|
m_connectedDev->_deviceDisconnected();
|
||||||
|
m_connectedDev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
CDeviceToken(const CDeviceToken&) = delete;
|
||||||
|
CDeviceToken(const CDeviceToken&& other)
|
||||||
|
: m_devType(other.m_devType),
|
||||||
|
m_vendorId(other.m_vendorId),
|
||||||
|
m_productId(other.m_productId),
|
||||||
|
m_vendorName(other.m_vendorName),
|
||||||
|
m_productName(other.m_productName),
|
||||||
|
m_devPath(other.m_devPath),
|
||||||
|
m_connectedDev(other.m_connectedDev)
|
||||||
|
{}
|
||||||
|
inline CDeviceToken(enum TDeviceType devType, unsigned vid, unsigned pid, const char* vname, const char* pname, const char* path)
|
||||||
|
: m_devType(devType),
|
||||||
|
m_vendorId(vid),
|
||||||
|
m_productId(pid),
|
||||||
|
m_devPath(path),
|
||||||
|
m_connectedDev(NULL)
|
||||||
|
{
|
||||||
|
if (vname)
|
||||||
|
m_vendorName = vname;
|
||||||
|
if (pname)
|
||||||
|
m_productName = pname;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline TDeviceType getDeviceType() const {return m_devType;}
|
||||||
|
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 const std::string& getDevicePath() const {return m_devPath;}
|
||||||
|
inline bool isDeviceOpen() const {return (m_connectedDev != NULL);}
|
||||||
|
inline CDeviceBase* openAndGetDevice()
|
||||||
|
{
|
||||||
|
if (!m_connectedDev)
|
||||||
|
m_connectedDev = SDeviceSignature::DeviceNew(*this);
|
||||||
|
return m_connectedDev;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator ==(const CDeviceToken& rhs) const
|
||||||
|
{return m_devPath == rhs.m_devPath;}
|
||||||
|
inline bool operator <(const CDeviceToken& rhs) const
|
||||||
|
{return m_devPath < rhs.m_devPath;}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // CDEVICETOKEN
|
#endif // CDEVICETOKEN
|
||||||
|
|
|
@ -1,5 +1,71 @@
|
||||||
#ifndef CDOLPHINSMASHADAPTER_HPP
|
#ifndef CDOLPHINSMASHADAPTER_HPP
|
||||||
#define CDOLPHINSMASHADAPTER_HPP
|
#define CDOLPHINSMASHADAPTER_HPP
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "CDeviceBase.hpp"
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
enum EDolphinControllerType
|
||||||
|
{
|
||||||
|
DOL_TYPE_NONE = 0,
|
||||||
|
DOL_TYPE_NORMAL = 0x10,
|
||||||
|
DOL_TYPE_WAVEBIRD = 0x20,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum EDolphinControllerButtons
|
||||||
|
{
|
||||||
|
DOL_START = 1<<0,
|
||||||
|
DOL_Z = 1<<1,
|
||||||
|
DOL_L = 1<<2,
|
||||||
|
DOL_R = 1<<3,
|
||||||
|
DOL_A = 1<<8,
|
||||||
|
DOL_B = 1<<9,
|
||||||
|
DOL_X = 1<<10,
|
||||||
|
DOL_Y = 1<<11,
|
||||||
|
DOL_LEFT = 1<<12,
|
||||||
|
DOL_RIGHT = 1<<13,
|
||||||
|
DOL_DOWN = 1<<14,
|
||||||
|
DOL_UP = 1<<15
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SDolphinControllerState
|
||||||
|
{
|
||||||
|
uint8_t m_leftStick[2];
|
||||||
|
uint8_t m_rightStick[2];
|
||||||
|
uint8_t m_analogTriggers[2];
|
||||||
|
uint16_t m_btns;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IDolphinSmashAdapterCallback
|
||||||
|
{
|
||||||
|
virtual void controllerConnected(unsigned idx, EDolphinControllerType type) {}
|
||||||
|
virtual void controllerDisconnected(unsigned idx, EDolphinControllerType type) {}
|
||||||
|
virtual void controllerUpdate(unsigned idx, EDolphinControllerType type,
|
||||||
|
const SDolphinControllerState& state) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CDolphinSmashAdapter final : public CDeviceBase
|
||||||
|
{
|
||||||
|
IDolphinSmashAdapterCallback* m_callback;
|
||||||
|
uint8_t m_knownControllers;
|
||||||
|
uint8_t m_rumbleRequest;
|
||||||
|
uint8_t m_rumbleState;
|
||||||
|
void deviceDisconnected();
|
||||||
|
void initialCycle();
|
||||||
|
void transferCycle();
|
||||||
|
void finalCycle();
|
||||||
|
public:
|
||||||
|
CDolphinSmashAdapter(CDeviceToken* token);
|
||||||
|
~CDolphinSmashAdapter();
|
||||||
|
|
||||||
|
inline void setCallback(IDolphinSmashAdapterCallback* cb)
|
||||||
|
{m_callback = cb; m_knownControllers = 0;}
|
||||||
|
inline void startRumble(unsigned idx) {if (idx >= 4) return; m_rumbleRequest |= 1<<idx;}
|
||||||
|
inline void stopRumble(unsigned idx) {if (idx >= 4) return; m_rumbleRequest &= ~(1<<idx);}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // CDOLPHINSMASHADAPTER_HPP
|
#endif // CDOLPHINSMASHADAPTER_HPP
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
#ifndef CDUALSHOCKPAD_HPP
|
#ifndef CDUALSHOCKPAD_HPP
|
||||||
#define CDUALSHOCKPAD_HPP
|
#define CDUALSHOCKPAD_HPP
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // CDUALSHOCKPAD_HPP
|
#endif // CDUALSHOCKPAD_HPP
|
||||||
|
|
|
@ -1,5 +1,20 @@
|
||||||
#ifndef CGENERICPAD_HPP
|
#ifndef CGENERICPAD_HPP
|
||||||
#define CGENERICPAD_HPP
|
#define CGENERICPAD_HPP
|
||||||
|
|
||||||
|
#include "CDeviceBase.hpp"
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
class CGenericPad final : public CDeviceBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CGenericPad(CDeviceToken* token);
|
||||||
|
~CGenericPad();
|
||||||
|
|
||||||
|
void deviceDisconnected();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // CGENERICPAD_HPP
|
#endif // CGENERICPAD_HPP
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
#ifndef CREVOLUTIONPAD_HPP
|
#ifndef CREVOLUTIONPAD_HPP
|
||||||
#define CREVOLUTIONPAD_HPP
|
#define CREVOLUTIONPAD_HPP
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // CREVOLUTIONPAD_HPP
|
#endif // CREVOLUTIONPAD_HPP
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
#ifndef IHIDLISTENER_HPP
|
||||||
|
#define IHIDLISTENER_HPP
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <mutex>
|
||||||
|
#include "CDeviceToken.hpp"
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
typedef std::unordered_map<std::string, CDeviceToken> TDeviceTokens;
|
||||||
|
typedef std::pair<TDeviceTokens::iterator, bool> TInsertedDeviceToken;
|
||||||
|
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;
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
/* External listener implementation (for Windows) */
|
||||||
|
virtual bool _extDevConnect(const char* path)=0;
|
||||||
|
virtual bool _extDevDisconnect(const char* path)=0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Platform-specific constructor */
|
||||||
|
IHIDListener* IHIDListenerNew(CDeviceFinder& finder);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // IHIDLISTENER_HPP
|
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef SDeviceSignature_HPP
|
||||||
|
#define SDeviceSignature_HPP
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
class CDeviceToken;
|
||||||
|
class CDeviceBase;
|
||||||
|
|
||||||
|
struct SDeviceSignature
|
||||||
|
{
|
||||||
|
typedef std::vector<const SDeviceSignature*> TDeviceSignatureSet;
|
||||||
|
typedef std::function<CDeviceBase*(CDeviceToken*)> TFactoryLambda;
|
||||||
|
const char* m_name;
|
||||||
|
unsigned m_vid, m_pid;
|
||||||
|
TFactoryLambda m_factory;
|
||||||
|
SDeviceSignature() : m_name(NULL) {} /* Sentinel constructor */
|
||||||
|
SDeviceSignature(const char* name, unsigned vid, unsigned pid, TFactoryLambda&& factory)
|
||||||
|
: m_name(name), m_vid(vid), m_pid(pid), m_factory(factory) {}
|
||||||
|
static bool DeviceMatchToken(const CDeviceToken& token, const TDeviceSignatureSet& sigSet);
|
||||||
|
static CDeviceBase* DeviceNew(CDeviceToken& token);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define DEVICE_SIG(name, vid, pid) \
|
||||||
|
SDeviceSignature(#name, vid, pid, [](CDeviceToken* tok) -> CDeviceBase* {return new name(tok);})
|
||||||
|
#define DEVICE_SIG_SENTINEL() SDeviceSignature()
|
||||||
|
|
||||||
|
extern const SDeviceSignature BOO_DEVICE_SIGS[];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SDeviceSignature_HPP
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
#include "IGraphicsContext.hpp"
|
#include "IGraphicsContext.hpp"
|
||||||
#include <OpenGL/OpenGL.h>
|
#include <OpenGL/OpenGL.h>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
class CCGLContext final : public IGraphicsContext
|
class CCGLContext final : public IGraphicsContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -25,5 +28,7 @@ private:
|
||||||
int m_majVersion;
|
int m_majVersion;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // __APPLE__
|
#endif // __APPLE__
|
||||||
#endif // CCGLCONTEXT_HPP
|
#endif // CCGLCONTEXT_HPP
|
||||||
|
|
|
@ -3,6 +3,17 @@
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include "IGraphicsContext.hpp"
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
class CWGLContext : public IGraphicsContext
|
||||||
|
{
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
#endif // CWGLCONTEXT_HPP
|
#endif // CWGLCONTEXT_HPP
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
|
|
||||||
#if !defined(__APPLE__) && (defined(__linux__) || defined(BSD))
|
#if !defined(__APPLE__) && (defined(__linux__) || defined(BSD))
|
||||||
#include <GL/glx.h>
|
#include <GL/glx.h>
|
||||||
|
#include "IGraphicsContext.hpp"
|
||||||
|
|
||||||
#include <IGraphicsContext.hpp>
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
class CGLXContext final : public IGraphicsContext
|
class CGLXContext final : public IGraphicsContext
|
||||||
{
|
{
|
||||||
|
@ -28,6 +30,7 @@ private:
|
||||||
Display* m_display;
|
Display* m_display;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // !defined(__APPLE__) && (defined(__linux__) || defined(BSD))
|
#endif // !defined(__APPLE__) && (defined(__linux__) || defined(BSD))
|
||||||
#endif // CGLXCONTEXT_HPP
|
#endif // CGLXCONTEXT_HPP
|
||||||
|
|
68
libBoo.pri
68
libBoo.pri
|
@ -1,5 +1,3 @@
|
||||||
!contains(CONFIG,c++11):CONFIG += C++11
|
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
$$PWD/include/boo.hpp \
|
$$PWD/include/boo.hpp \
|
||||||
$$PWD/include/IGraphicsContext.hpp \
|
$$PWD/include/IGraphicsContext.hpp \
|
||||||
|
@ -17,19 +15,12 @@ HEADERS += \
|
||||||
$$PWD/include/inputdev/CDeviceFinder.hpp \
|
$$PWD/include/inputdev/CDeviceFinder.hpp \
|
||||||
$$PWD/include/inputdev/CDeviceToken.hpp \
|
$$PWD/include/inputdev/CDeviceToken.hpp \
|
||||||
$$PWD/include/inputdev/CDeviceBase.hpp \
|
$$PWD/include/inputdev/CDeviceBase.hpp \
|
||||||
|
$$PWD/include/inputdev/IHIDListener.hpp \
|
||||||
$$PWD/src/inputdev/IHIDDevice.hpp \
|
$$PWD/src/inputdev/IHIDDevice.hpp \
|
||||||
$$PWD/src/inputdev/IHIDListener.hpp
|
$$PWD/include/inputdev/SDeviceSignature.hpp
|
||||||
|
|
||||||
unix:!macx:HEADERS += \
|
|
||||||
$$PWD/include/x11/CGLXContext.hpp
|
|
||||||
|
|
||||||
macx:HEADERS += \
|
|
||||||
$$PWD/include/mac/CCGLContext.hpp
|
|
||||||
|
|
||||||
win32:HEADERS += \
|
|
||||||
$$PWD/include/win/CWGLContext.hpp
|
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
$$PWD/InputDeviceClasses.cpp \
|
||||||
$$PWD/src/CSurface.cpp \
|
$$PWD/src/CSurface.cpp \
|
||||||
$$PWD/src/CRetraceWaiter.cpp \
|
$$PWD/src/CRetraceWaiter.cpp \
|
||||||
$$PWD/src/CInputRelay.cpp \
|
$$PWD/src/CInputRelay.cpp \
|
||||||
|
@ -39,25 +30,46 @@ SOURCES += \
|
||||||
$$PWD/src/inputdev/CCafeProPad.cpp \
|
$$PWD/src/inputdev/CCafeProPad.cpp \
|
||||||
$$PWD/src/inputdev/CDualshockPad.cpp \
|
$$PWD/src/inputdev/CDualshockPad.cpp \
|
||||||
$$PWD/src/inputdev/CGenericPad.cpp \
|
$$PWD/src/inputdev/CGenericPad.cpp \
|
||||||
$$PWD/src/inputdev/CDeviceFinder.cpp \
|
$$PWD/src/inputdev/CDeviceBase.cpp \
|
||||||
$$PWD/src/inputdev/CDeviceBase.cpp
|
$$PWD/src/inputdev/SDeviceSignature.cpp
|
||||||
|
|
||||||
unix:!macx:SOURCES += \
|
unix:!macx {
|
||||||
$$PWD/src/x11/CGLXContext.cpp \
|
HEADERS += \
|
||||||
$$PWD/src/inputdev/CHIDDeviceUdev.cpp \
|
$$PWD/include/x11/CGLXContext.hpp
|
||||||
$$PWD/src/inputdev/CHIDListenerUdev.cpp
|
|
||||||
|
|
||||||
macx:SOURCES += \
|
SOURCES += \
|
||||||
$$PWD/src/mac/CCGLContext.cpp
|
$$PWD/src/x11/CGLXContext.cpp
|
||||||
|
}
|
||||||
|
|
||||||
macx:OBJECTIVE_SOURCES += \
|
linux {
|
||||||
$$PWD/src/mac/CCGLCocoaView.mm \
|
SOURCES += \
|
||||||
$$PWD/src/inputdev/CHIDDeviceIOKit.mm \
|
$$PWD/src/inputdev/CHIDListenerUdev.cpp \
|
||||||
$$PWD/src/inputdev/CHIDListenerIOKit.mm
|
$$PWD/src/inputdev/CHIDDeviceUdev.cpp
|
||||||
|
|
||||||
win32:SOURCES += \
|
LIBS += -ludev
|
||||||
$$PWD/src/win/CWGLContext.cpp \
|
}
|
||||||
$$PWD/src/inputdev/CHIDDeviceWin32.cpp \
|
|
||||||
$$PWD/src/inputdev/CHIDListenerWin32.cpp
|
macx {
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/include/mac/CCGLContext.hpp
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/src/mac/CCGLContext.cpp \
|
||||||
|
$$PWD/src/inputdev/CHIDDeviceIOKit.cpp \
|
||||||
|
$$PWD/src/inputdev/CHIDListenerIOKit.cpp
|
||||||
|
|
||||||
|
OBJECTIVE_SOURCES += \
|
||||||
|
$$PWD/src/mac/CCGLCocoaView.mm
|
||||||
|
}
|
||||||
|
|
||||||
|
win32 {
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/include/win/CWGLContext.hpp
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/src/win/CWGLContext.cpp \
|
||||||
|
$$PWD/src/inputdev/CHIDListenerWinUSB.cpp \
|
||||||
|
$$PWD/src/inputdev/CHIDDeviceWinUSB.cpp
|
||||||
|
}
|
||||||
|
|
||||||
INCLUDEPATH += $$PWD/include
|
INCLUDEPATH += $$PWD/include
|
||||||
|
|
||||||
|
|
12
libBoo.pro
12
libBoo.pro
|
@ -1,8 +1,14 @@
|
||||||
CONFIG -= Qt
|
CONFIG -= Qt
|
||||||
CONFIG += app c++11
|
CONFIG += console
|
||||||
|
#QMAKE_CXXFLAGS -= -std=c++0x
|
||||||
|
#CONFIG += c++11
|
||||||
|
unix:QMAKE_CXXFLAGS += -std=c++11 -stdlib=libc++
|
||||||
|
unix:LIBS += -std=c++11 -stdlib=libc++ -lc++abi
|
||||||
|
|
||||||
unix:!macx:CONFIG += link_pkgconfig
|
win32:LIBS += Setupapi.lib winusb.lib User32.lib /SUBSYSTEM:Windows
|
||||||
unix:!macx:PKGCONFIG += x11
|
|
||||||
|
#unix:!macx:CONFIG += link_pkgconfig
|
||||||
|
#unix:!macx:PKGCONFIG += x11
|
||||||
|
|
||||||
include(libBoo.pri)
|
include(libBoo.pri)
|
||||||
include(test/test.pri)
|
include(test/test.pri)
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
#include "CSurface.hpp"
|
#include "CSurface.hpp"
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
ISurface* CSurfaceNewWindow()
|
ISurface* CSurfaceNewWindow()
|
||||||
{
|
{
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
ISurface* CSurfaceNewQWidget()
|
ISurface* CSurfaceNewQWidget()
|
||||||
{
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,57 @@
|
||||||
#include "inputdev/CDeviceBase.hpp"
|
#include "inputdev/CDeviceBase.hpp"
|
||||||
|
#include "inputdev/CDeviceToken.hpp"
|
||||||
|
#include "IHIDDevice.hpp"
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
CDeviceBase::CDeviceBase(CDeviceToken* token)
|
||||||
|
: m_token(token), m_hidDev(NULL)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CDeviceBase::~CDeviceBase()
|
||||||
|
{
|
||||||
|
delete m_hidDev;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDeviceBase::_deviceDisconnected()
|
||||||
|
{
|
||||||
|
deviceDisconnected();
|
||||||
|
m_token = NULL;
|
||||||
|
if (m_hidDev)
|
||||||
|
{
|
||||||
|
m_hidDev->_deviceDisconnected();
|
||||||
|
delete m_hidDev;
|
||||||
|
m_hidDev = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDeviceBase::closeDevice()
|
||||||
|
{
|
||||||
|
if (m_token)
|
||||||
|
m_token->_deviceClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeviceBase::sendUSBInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (m_hidDev)
|
||||||
|
return m_hidDev->_sendUSBInterruptTransfer(pipe, data, length);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t CDeviceBase::receiveUSBInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (m_hidDev)
|
||||||
|
return m_hidDev->_receiveUSBInterruptTransfer(pipe, data, length);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CDeviceBase::sendHIDReport(const uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (m_hidDev)
|
||||||
|
return m_hidDev->_sendHIDReport(data, length);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
#include "inputdev/CDeviceFinder.hpp"
|
|
|
@ -1 +1,127 @@
|
||||||
#include "inputdev/CDolphinSmashAdapter.hpp"
|
#include "inputdev/CDolphinSmashAdapter.hpp"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reference: https://github.com/ToadKing/wii-u-gc-adapter/blob/master/wii-u-gc-adapter.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
CDolphinSmashAdapter::CDolphinSmashAdapter(CDeviceToken* token)
|
||||||
|
: CDeviceBase(token),
|
||||||
|
m_callback(NULL),
|
||||||
|
m_knownControllers(0),
|
||||||
|
m_rumbleRequest(0),
|
||||||
|
m_rumbleState(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CDolphinSmashAdapter::~CDolphinSmashAdapter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline EDolphinControllerType parseType(unsigned char status)
|
||||||
|
{
|
||||||
|
unsigned char type = status & (DOL_TYPE_NORMAL | DOL_TYPE_WAVEBIRD);
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case DOL_TYPE_NORMAL:
|
||||||
|
case DOL_TYPE_WAVEBIRD:
|
||||||
|
return (EDolphinControllerType)type;
|
||||||
|
default:
|
||||||
|
return DOL_TYPE_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline EDolphinControllerType
|
||||||
|
parseState(SDolphinControllerState* stateOut, uint8_t* payload, bool& rumble)
|
||||||
|
{
|
||||||
|
memset(stateOut, 0, sizeof(SDolphinControllerState));
|
||||||
|
unsigned char status = payload[0];
|
||||||
|
EDolphinControllerType type = parseType(status);
|
||||||
|
|
||||||
|
rumble = ((status & 0x04) != 0) ? true : false;
|
||||||
|
|
||||||
|
stateOut->m_btns = (uint16_t)payload[1] << 8 | (uint16_t)payload[2];
|
||||||
|
|
||||||
|
stateOut->m_leftStick[0] = payload[3];
|
||||||
|
stateOut->m_leftStick[1] = payload[4];
|
||||||
|
stateOut->m_rightStick[0] = payload[5];
|
||||||
|
stateOut->m_rightStick[1] = payload[6];
|
||||||
|
stateOut->m_analogTriggers[0] = payload[7];
|
||||||
|
stateOut->m_analogTriggers[1] = payload[8];
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDolphinSmashAdapter::initialCycle()
|
||||||
|
{
|
||||||
|
uint8_t handshakePayload[] = {0x13};
|
||||||
|
sendUSBInterruptTransfer(0, handshakePayload, sizeof(handshakePayload));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDolphinSmashAdapter::transferCycle()
|
||||||
|
{
|
||||||
|
uint8_t payload[37];
|
||||||
|
size_t recvSz = receiveUSBInterruptTransfer(0, payload, sizeof(payload));
|
||||||
|
if (recvSz != 37 || payload[0] != 0x21)
|
||||||
|
return;
|
||||||
|
//printf("RECEIVED DATA %zu %02X\n", recvSz, payload[0]);
|
||||||
|
|
||||||
|
if (!m_callback)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Parse controller states */
|
||||||
|
uint8_t* controller = &payload[1];
|
||||||
|
uint8_t rumbleMask = 0;
|
||||||
|
for (int i=0 ; i<4 ; i++, controller += 9)
|
||||||
|
{
|
||||||
|
SDolphinControllerState state;
|
||||||
|
bool rumble = false;
|
||||||
|
EDolphinControllerType type = parseState(&state, controller, rumble);
|
||||||
|
if (type && !(m_knownControllers & 1<<i))
|
||||||
|
{
|
||||||
|
m_knownControllers |= 1<<i;
|
||||||
|
m_callback->controllerConnected(i, type);
|
||||||
|
}
|
||||||
|
else if (!type && (m_knownControllers & 1<<i))
|
||||||
|
{
|
||||||
|
m_knownControllers &= ~(1<<i);
|
||||||
|
m_callback->controllerDisconnected(i, type);
|
||||||
|
}
|
||||||
|
if (m_knownControllers & 1<<i)
|
||||||
|
m_callback->controllerUpdate(i, type, state);
|
||||||
|
rumbleMask |= rumble ? 1<<i : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send rumble message (if needed) */
|
||||||
|
uint8_t rumbleReq = m_rumbleRequest & rumbleMask;
|
||||||
|
if (rumbleReq != m_rumbleState)
|
||||||
|
{
|
||||||
|
uint8_t rumbleMessage[5] = {0x11};
|
||||||
|
for (int i=0 ; i<4 ; ++i)
|
||||||
|
{
|
||||||
|
if (rumbleReq & 1<<i)
|
||||||
|
rumbleMessage[i+1] = 1;
|
||||||
|
else
|
||||||
|
rumbleMessage[i+1] = 0;
|
||||||
|
}
|
||||||
|
sendUSBInterruptTransfer(0, rumbleMessage, sizeof(rumbleMessage));
|
||||||
|
m_rumbleState = rumbleReq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDolphinSmashAdapter::finalCycle()
|
||||||
|
{
|
||||||
|
uint8_t rumbleMessage[5] = {0x11, 0, 0, 0, 0};
|
||||||
|
sendUSBInterruptTransfer(0, rumbleMessage, sizeof(rumbleMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CDolphinSmashAdapter::deviceDisconnected()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,23 @@
|
||||||
#include "inputdev/CGenericPad.hpp"
|
#include "inputdev/CGenericPad.hpp"
|
||||||
|
#include "inputdev/CDeviceToken.hpp"
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
CGenericPad::CGenericPad(CDeviceToken* token)
|
||||||
|
: CDeviceBase(token)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CGenericPad::~CGenericPad()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGenericPad::deviceDisconnected()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,256 @@
|
||||||
|
#include "IHIDDevice.hpp"
|
||||||
|
#include "inputdev/CDeviceToken.hpp"
|
||||||
|
#include "inputdev/CDeviceBase.hpp"
|
||||||
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
|
#include <IOKit/usb/IOUSBLib.h>
|
||||||
|
#include <IOKit/IOCFPlugIn.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
class CHIDDeviceIOKit final : public IHIDDevice
|
||||||
|
{
|
||||||
|
CDeviceToken& m_token;
|
||||||
|
CDeviceBase& m_devImp;
|
||||||
|
|
||||||
|
IOUSBInterfaceInterface** m_usbIntf = NULL;
|
||||||
|
uint8_t m_usbIntfInPipe = 0;
|
||||||
|
uint8_t m_usbIntfOutPipe = 0;
|
||||||
|
bool m_runningTransferLoop = false;
|
||||||
|
|
||||||
|
const std::string& m_devPath;
|
||||||
|
std::mutex m_initMutex;
|
||||||
|
std::condition_variable m_initCond;
|
||||||
|
std::thread* m_thread;
|
||||||
|
|
||||||
|
bool _sendUSBInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (m_usbIntf)
|
||||||
|
{
|
||||||
|
IOReturn res = (*m_usbIntf)->WritePipe(m_usbIntf, m_usbIntfOutPipe, (void*)data, length);
|
||||||
|
return res == kIOReturnSuccess;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _receiveUSBInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (m_usbIntf)
|
||||||
|
{
|
||||||
|
UInt32 readSize = length;
|
||||||
|
IOReturn res = (*m_usbIntf)->ReadPipe(m_usbIntf, m_usbIntfInPipe, data, &readSize);
|
||||||
|
if (res != kIOReturnSuccess)
|
||||||
|
return 0;
|
||||||
|
return readSize;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _threadProcUSBLL(CHIDDeviceIOKit* device)
|
||||||
|
{
|
||||||
|
char thrName[128];
|
||||||
|
snprintf(thrName, 128, "%s Transfer Thread", device->m_token.getProductName().c_str());
|
||||||
|
pthread_setname_np(thrName);
|
||||||
|
char errStr[256];
|
||||||
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
|
|
||||||
|
/* Get the HID element's parent (USB interrupt transfer-interface) */
|
||||||
|
io_iterator_t devIter;
|
||||||
|
io_registry_entry_t devEntry = IORegistryEntryFromPath(kIOMasterPortDefault, device->m_devPath.c_str());
|
||||||
|
IORegistryEntryGetChildIterator(devEntry, kIOServicePlane, &devIter);
|
||||||
|
io_object_t obj, interfaceEntry = 0;
|
||||||
|
while ((obj = IOIteratorNext(devIter)))
|
||||||
|
{
|
||||||
|
if (IOObjectConformsTo(obj, kIOUSBInterfaceClassName))
|
||||||
|
interfaceEntry = obj;
|
||||||
|
else
|
||||||
|
IOObjectRelease(obj);
|
||||||
|
}
|
||||||
|
if (!interfaceEntry)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IOKit Plugin COM interface (WTF Apple???) */
|
||||||
|
IOCFPlugInInterface **iodev;
|
||||||
|
SInt32 score;
|
||||||
|
IOReturn err;
|
||||||
|
err = IOCreatePlugInInterfaceForService(interfaceEntry,
|
||||||
|
kIOUSBInterfaceUserClientTypeID,
|
||||||
|
kIOCFPlugInInterfaceID,
|
||||||
|
&iodev,
|
||||||
|
&score);
|
||||||
|
IOObjectRelease(interfaceEntry);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* USB interface function-pointer table */
|
||||||
|
IOUSBInterfaceInterface** intf = NULL;
|
||||||
|
err = (*iodev)->QueryInterface(iodev,
|
||||||
|
CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
|
||||||
|
(LPVOID*)&intf);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
IODestroyPlugInInterface(iodev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Obtain exclusive lock on device */
|
||||||
|
device->m_usbIntf = intf;
|
||||||
|
err = (*intf)->USBInterfaceOpen(intf);
|
||||||
|
if (err != kIOReturnSuccess)
|
||||||
|
{
|
||||||
|
if (err == kIOReturnExclusiveAccess)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
(*intf)->Release(intf);
|
||||||
|
IODestroyPlugInInterface(iodev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine pipe indices for interrupt I/O */
|
||||||
|
UInt8 numEndpoints = 0;
|
||||||
|
err = (*intf)->GetNumEndpoints(intf, &numEndpoints);
|
||||||
|
for (int i=1 ; i<numEndpoints+1 ; ++i)
|
||||||
|
{
|
||||||
|
UInt8 dir, num, tType, interval;
|
||||||
|
UInt16 mPacketSz;
|
||||||
|
err = (*intf)->GetPipeProperties(intf, i, &dir, &num, &tType, &mPacketSz, &interval);
|
||||||
|
if (tType == kUSBInterrupt)
|
||||||
|
{
|
||||||
|
if (dir == kUSBIn)
|
||||||
|
device->m_usbIntfInPipe = num;
|
||||||
|
else if (dir == kUSBOut)
|
||||||
|
device->m_usbIntfOutPipe = num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return control to main thread */
|
||||||
|
device->m_runningTransferLoop = true;
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
|
||||||
|
/* Start transfer loop */
|
||||||
|
device->m_devImp.initialCycle();
|
||||||
|
while (device->m_runningTransferLoop)
|
||||||
|
device->m_devImp.transferCycle();
|
||||||
|
device->m_devImp.finalCycle();
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
(*intf)->USBInterfaceClose(intf);
|
||||||
|
(*intf)->Release(intf);
|
||||||
|
IODestroyPlugInInterface(iodev);
|
||||||
|
device->m_usbIntf = NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _threadProcBTLL(CHIDDeviceIOKit* device)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
|
|
||||||
|
/* Return control to main thread */
|
||||||
|
device->m_runningTransferLoop = true;
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
|
||||||
|
/* Start transfer loop */
|
||||||
|
device->m_devImp.initialCycle();
|
||||||
|
while (device->m_runningTransferLoop)
|
||||||
|
device->m_devImp.transferCycle();
|
||||||
|
device->m_devImp.finalCycle();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _threadProcHID(CHIDDeviceIOKit* device)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
|
|
||||||
|
/* Return control to main thread */
|
||||||
|
device->m_runningTransferLoop = true;
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
|
||||||
|
/* Start transfer loop */
|
||||||
|
device->m_devImp.initialCycle();
|
||||||
|
while (device->m_runningTransferLoop)
|
||||||
|
device->m_devImp.transferCycle();
|
||||||
|
device->m_devImp.finalCycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _deviceDisconnected()
|
||||||
|
{
|
||||||
|
m_runningTransferLoop = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _sendHIDReport(const uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
CHIDDeviceIOKit(CDeviceToken& token, CDeviceBase& devImp)
|
||||||
|
: m_token(token),
|
||||||
|
m_devImp(devImp),
|
||||||
|
m_devPath(token.getDevicePath())
|
||||||
|
{
|
||||||
|
devImp.m_hidDev = this;
|
||||||
|
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||||
|
CDeviceToken::TDeviceType dType = token.getDeviceType();
|
||||||
|
if (dType == CDeviceToken::DEVTYPE_USB)
|
||||||
|
m_thread = new std::thread(_threadProcUSBLL, this);
|
||||||
|
else if (dType == CDeviceToken::DEVTYPE_BLUETOOTH)
|
||||||
|
m_thread = new std::thread(_threadProcBTLL, this);
|
||||||
|
else if (dType == CDeviceToken::DEVTYPE_GENERICHID)
|
||||||
|
m_thread = new std::thread(_threadProcHID, this);
|
||||||
|
else
|
||||||
|
throw std::runtime_error("invalid token supplied to device constructor");
|
||||||
|
m_initCond.wait(lk);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CHIDDeviceIOKit()
|
||||||
|
{
|
||||||
|
m_runningTransferLoop = false;
|
||||||
|
m_thread->join();
|
||||||
|
delete m_thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp)
|
||||||
|
{
|
||||||
|
return new CHIDDeviceIOKit(token, devImp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
#include "IHIDDevice.hpp"
|
|
|
@ -1 +1,235 @@
|
||||||
#include "IHIDDevice.hpp"
|
#include "IHIDDevice.hpp"
|
||||||
|
#include "inputdev/CDeviceToken.hpp"
|
||||||
|
#include "inputdev/CDeviceBase.hpp"
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
#include <libudev.h>
|
||||||
|
#include <stropts.h>
|
||||||
|
#include <linux/usb/ch9.h>
|
||||||
|
#include <linux/usbdevice_fs.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
udev* GetUdev();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reference: http://tali.admingilde.org/linux-docbook/usb/ch07s06.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
class CHIDDeviceUdev final : public IHIDDevice
|
||||||
|
{
|
||||||
|
CDeviceToken& m_token;
|
||||||
|
CDeviceBase& m_devImp;
|
||||||
|
|
||||||
|
int m_devFd = 0;
|
||||||
|
unsigned m_usbIntfInPipe = 0;
|
||||||
|
unsigned m_usbIntfOutPipe = 0;
|
||||||
|
bool m_runningTransferLoop = false;
|
||||||
|
|
||||||
|
const std::string& m_devPath;
|
||||||
|
std::mutex m_initMutex;
|
||||||
|
std::condition_variable m_initCond;
|
||||||
|
std::thread* m_thread;
|
||||||
|
|
||||||
|
bool _sendUSBInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (m_devFd)
|
||||||
|
{
|
||||||
|
usbdevfs_bulktransfer xfer =
|
||||||
|
{
|
||||||
|
m_usbIntfOutPipe | USB_DIR_OUT,
|
||||||
|
(unsigned)length,
|
||||||
|
0,
|
||||||
|
(void*)data
|
||||||
|
};
|
||||||
|
int ret = ioctl(m_devFd, USBDEVFS_BULK, &xfer);
|
||||||
|
if (ret != (int)length)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _receiveUSBInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (m_devFd)
|
||||||
|
{
|
||||||
|
usbdevfs_bulktransfer xfer =
|
||||||
|
{
|
||||||
|
m_usbIntfInPipe | USB_DIR_IN,
|
||||||
|
(unsigned)length,
|
||||||
|
0,
|
||||||
|
data
|
||||||
|
};
|
||||||
|
return ioctl(m_devFd, USBDEVFS_BULK, &xfer);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _threadProcUSBLL(CHIDDeviceUdev* device)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
char errStr[256];
|
||||||
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
|
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str());
|
||||||
|
|
||||||
|
/* Get device file */
|
||||||
|
const char* dp = udev_device_get_devnode(udevDev);
|
||||||
|
device->m_devFd = open(dp, O_RDWR);
|
||||||
|
if (device->m_devFd < 0)
|
||||||
|
{
|
||||||
|
snprintf(errStr, 256, "Unable to open %s@%s: %s\n",
|
||||||
|
device->m_token.getProductName().c_str(), dp, strerror(errno));
|
||||||
|
device->m_devImp.deviceError(errStr);
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
udev_device_unref(udevDev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
usb_device_descriptor devDesc = {0};
|
||||||
|
read(device->m_devFd, &devDesc, 1);
|
||||||
|
read(device->m_devFd, &devDesc.bDescriptorType, devDesc.bLength-1);
|
||||||
|
if (devDesc.bNumConfigurations)
|
||||||
|
{
|
||||||
|
usb_config_descriptor confDesc = {0};
|
||||||
|
read(device->m_devFd, &confDesc, 1);
|
||||||
|
read(device->m_devFd, &confDesc.bDescriptorType, confDesc.bLength-1);
|
||||||
|
if (confDesc.bNumInterfaces)
|
||||||
|
{
|
||||||
|
usb_interface_descriptor intfDesc = {0};
|
||||||
|
read(device->m_devFd, &intfDesc, 1);
|
||||||
|
read(device->m_devFd, &intfDesc.bDescriptorType, intfDesc.bLength-1);
|
||||||
|
for (i=0 ; i<intfDesc.bNumEndpoints+1 ; ++i)
|
||||||
|
{
|
||||||
|
usb_endpoint_descriptor endpDesc = {0};
|
||||||
|
read(device->m_devFd, &endpDesc, 1);
|
||||||
|
read(device->m_devFd, &endpDesc.bDescriptorType, endpDesc.bLength-1);
|
||||||
|
if ((endpDesc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
|
||||||
|
{
|
||||||
|
if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
|
||||||
|
device->m_usbIntfInPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||||
|
else if ((endpDesc.bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
|
||||||
|
device->m_usbIntfOutPipe = endpDesc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Request that kernel disconnects existing driver */
|
||||||
|
usbdevfs_ioctl disconnectReq = {
|
||||||
|
0,
|
||||||
|
USBDEVFS_DISCONNECT,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
ioctl(device->m_devFd, USBDEVFS_IOCTL, &disconnectReq);
|
||||||
|
|
||||||
|
/* Return control to main thread */
|
||||||
|
device->m_runningTransferLoop = true;
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
|
||||||
|
/* Start transfer loop */
|
||||||
|
device->m_devImp.initialCycle();
|
||||||
|
while (device->m_runningTransferLoop)
|
||||||
|
device->m_devImp.transferCycle();
|
||||||
|
device->m_devImp.finalCycle();
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
close(device->m_devFd);
|
||||||
|
device->m_devFd = 0;
|
||||||
|
udev_device_unref(udevDev);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _threadProcBTLL(CHIDDeviceUdev* device)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
|
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str());
|
||||||
|
|
||||||
|
/* Return control to main thread */
|
||||||
|
device->m_runningTransferLoop = true;
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
|
||||||
|
/* Start transfer loop */
|
||||||
|
device->m_devImp.initialCycle();
|
||||||
|
while (device->m_runningTransferLoop)
|
||||||
|
device->m_devImp.transferCycle();
|
||||||
|
device->m_devImp.finalCycle();
|
||||||
|
|
||||||
|
udev_device_unref(udevDev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _threadProcHID(CHIDDeviceUdev* device)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
|
udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str());
|
||||||
|
|
||||||
|
/* Return control to main thread */
|
||||||
|
device->m_runningTransferLoop = true;
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
|
||||||
|
/* Start transfer loop */
|
||||||
|
device->m_devImp.initialCycle();
|
||||||
|
while (device->m_runningTransferLoop)
|
||||||
|
device->m_devImp.transferCycle();
|
||||||
|
device->m_devImp.finalCycle();
|
||||||
|
|
||||||
|
udev_device_unref(udevDev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _deviceDisconnected()
|
||||||
|
{
|
||||||
|
m_runningTransferLoop = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _sendHIDReport(const uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
CHIDDeviceUdev(CDeviceToken& token, CDeviceBase& devImp)
|
||||||
|
: m_token(token),
|
||||||
|
m_devImp(devImp),
|
||||||
|
m_devPath(token.getDevicePath())
|
||||||
|
{
|
||||||
|
devImp.m_hidDev = this;
|
||||||
|
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||||
|
CDeviceToken::TDeviceType dType = token.getDeviceType();
|
||||||
|
if (dType == CDeviceToken::DEVTYPE_USB)
|
||||||
|
m_thread = new std::thread(_threadProcUSBLL, this);
|
||||||
|
else if (dType == CDeviceToken::DEVTYPE_BLUETOOTH)
|
||||||
|
m_thread = new std::thread(_threadProcBTLL, this);
|
||||||
|
else if (dType == CDeviceToken::DEVTYPE_GENERICHID)
|
||||||
|
m_thread = new std::thread(_threadProcHID, this);
|
||||||
|
else
|
||||||
|
throw std::runtime_error("invalid token supplied to device constructor");
|
||||||
|
m_initCond.wait(lk);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CHIDDeviceUdev()
|
||||||
|
{
|
||||||
|
m_runningTransferLoop = false;
|
||||||
|
m_thread->join();
|
||||||
|
delete m_thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp)
|
||||||
|
{
|
||||||
|
return new CHIDDeviceUdev(token, devImp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
#include "IHIDDevice.hpp"
|
|
|
@ -0,0 +1,224 @@
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS 1 /* STFU MSVC */
|
||||||
|
#include "IHIDDevice.hpp"
|
||||||
|
#include "inputdev/CDeviceToken.hpp"
|
||||||
|
#include "inputdev/CDeviceBase.hpp"
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define _WIN32_LEAN_AND_MEAN 1
|
||||||
|
#include <windows.h>
|
||||||
|
#include <winusb.h>
|
||||||
|
#include <usb100.h>
|
||||||
|
#include <Winusbio.h>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
class CHIDDeviceWinUSB final : public IHIDDevice
|
||||||
|
{
|
||||||
|
CDeviceToken& m_token;
|
||||||
|
CDeviceBase& m_devImp;
|
||||||
|
|
||||||
|
HANDLE m_devHandle = 0;
|
||||||
|
WINUSB_INTERFACE_HANDLE m_usbHandle = NULL;
|
||||||
|
unsigned m_usbIntfInPipe = 0;
|
||||||
|
unsigned m_usbIntfOutPipe = 0;
|
||||||
|
bool m_runningTransferLoop = false;
|
||||||
|
|
||||||
|
const std::string& m_devPath;
|
||||||
|
std::mutex m_initMutex;
|
||||||
|
std::condition_variable m_initCond;
|
||||||
|
std::thread* m_thread;
|
||||||
|
|
||||||
|
bool _sendUSBInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (m_usbHandle)
|
||||||
|
{
|
||||||
|
ULONG lengthTransferred = 0;
|
||||||
|
if (!WinUsb_WritePipe(m_usbHandle, m_usbIntfOutPipe, (PUCHAR)data,
|
||||||
|
(ULONG)length, &lengthTransferred, NULL) ||
|
||||||
|
lengthTransferred != length)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t _receiveUSBInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
if (m_usbHandle)
|
||||||
|
{
|
||||||
|
ULONG lengthTransferred = 0;
|
||||||
|
if (!WinUsb_ReadPipe(m_usbHandle, m_usbIntfInPipe, (PUCHAR)data,
|
||||||
|
(ULONG)length, &lengthTransferred, NULL))
|
||||||
|
return 0;
|
||||||
|
return lengthTransferred;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _threadProcUSBLL(CHIDDeviceWinUSB* device)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
char errStr[256];
|
||||||
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
|
|
||||||
|
/* POSIX.. who needs it?? -MS */
|
||||||
|
device->m_devHandle = CreateFileA(device->m_devPath.c_str(),
|
||||||
|
GENERIC_WRITE | GENERIC_READ,
|
||||||
|
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||||||
|
NULL,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
||||||
|
NULL);
|
||||||
|
if (INVALID_HANDLE_VALUE == device->m_devHandle)
|
||||||
|
{
|
||||||
|
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
||||||
|
device->m_token.getProductName().c_str(),
|
||||||
|
device->m_devPath.c_str(), GetLastError());
|
||||||
|
device->m_devImp.deviceError(errStr);
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WinUsb_Initialize(device->m_devHandle, &device->m_usbHandle))
|
||||||
|
{
|
||||||
|
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
||||||
|
device->m_token.getProductName().c_str(),
|
||||||
|
device->m_devPath.c_str(), GetLastError());
|
||||||
|
device->m_devImp.deviceError(errStr);
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
CloseHandle(device->m_devHandle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Enumerate device pipes */
|
||||||
|
USB_INTERFACE_DESCRIPTOR ifDesc = {0};
|
||||||
|
if (!WinUsb_QueryInterfaceSettings(device->m_usbHandle, 0, &ifDesc))
|
||||||
|
{
|
||||||
|
_snprintf(errStr, 256, "Unable to open %s@%s: %d\n",
|
||||||
|
device->m_token.getProductName().c_str(),
|
||||||
|
device->m_devPath.c_str(), GetLastError());
|
||||||
|
device->m_devImp.deviceError(errStr);
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
CloseHandle(device->m_devHandle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (i=0 ; i<ifDesc.bNumEndpoints ; ++i)
|
||||||
|
{
|
||||||
|
WINUSB_PIPE_INFORMATION pipeDesc;
|
||||||
|
WinUsb_QueryPipe(device->m_usbHandle, 0, i, &pipeDesc);
|
||||||
|
if (pipeDesc.PipeType == UsbdPipeTypeInterrupt)
|
||||||
|
{
|
||||||
|
if (USB_ENDPOINT_DIRECTION_IN(pipeDesc.PipeId))
|
||||||
|
device->m_usbIntfInPipe = pipeDesc.PipeId;
|
||||||
|
else
|
||||||
|
device->m_usbIntfOutPipe = pipeDesc.PipeId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return control to main thread */
|
||||||
|
device->m_runningTransferLoop = true;
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
|
||||||
|
/* Start transfer loop */
|
||||||
|
device->m_devImp.initialCycle();
|
||||||
|
while (device->m_runningTransferLoop)
|
||||||
|
device->m_devImp.transferCycle();
|
||||||
|
device->m_devImp.finalCycle();
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
WinUsb_Free(device->m_usbHandle);
|
||||||
|
CloseHandle(device->m_devHandle);
|
||||||
|
device->m_devHandle = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _threadProcBTLL(CHIDDeviceWinUSB* device)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
|
|
||||||
|
/* Return control to main thread */
|
||||||
|
device->m_runningTransferLoop = true;
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
|
||||||
|
/* Start transfer loop */
|
||||||
|
device->m_devImp.initialCycle();
|
||||||
|
while (device->m_runningTransferLoop)
|
||||||
|
device->m_devImp.transferCycle();
|
||||||
|
device->m_devImp.finalCycle();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _threadProcHID(CHIDDeviceWinUSB* device)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lk(device->m_initMutex);
|
||||||
|
|
||||||
|
/* Return control to main thread */
|
||||||
|
device->m_runningTransferLoop = true;
|
||||||
|
lk.unlock();
|
||||||
|
device->m_initCond.notify_one();
|
||||||
|
|
||||||
|
/* Start transfer loop */
|
||||||
|
device->m_devImp.initialCycle();
|
||||||
|
while (device->m_runningTransferLoop)
|
||||||
|
device->m_devImp.transferCycle();
|
||||||
|
device->m_devImp.finalCycle();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void _deviceDisconnected()
|
||||||
|
{
|
||||||
|
m_runningTransferLoop = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _sendHIDReport(const uint8_t* data, size_t length)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
CHIDDeviceWinUSB(CDeviceToken& token, CDeviceBase& devImp)
|
||||||
|
: m_token(token),
|
||||||
|
m_devImp(devImp),
|
||||||
|
m_devPath(token.getDevicePath())
|
||||||
|
{
|
||||||
|
devImp.m_hidDev = this;
|
||||||
|
std::unique_lock<std::mutex> lk(m_initMutex);
|
||||||
|
CDeviceToken::TDeviceType dType = token.getDeviceType();
|
||||||
|
if (dType == CDeviceToken::DEVTYPE_USB)
|
||||||
|
m_thread = new std::thread(_threadProcUSBLL, this);
|
||||||
|
else if (dType == CDeviceToken::DEVTYPE_BLUETOOTH)
|
||||||
|
m_thread = new std::thread(_threadProcBTLL, this);
|
||||||
|
else if (dType == CDeviceToken::DEVTYPE_GENERICHID)
|
||||||
|
m_thread = new std::thread(_threadProcHID, this);
|
||||||
|
else
|
||||||
|
throw std::runtime_error("invalid token supplied to device constructor");
|
||||||
|
m_initCond.wait(lk);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CHIDDeviceWinUSB()
|
||||||
|
{
|
||||||
|
m_runningTransferLoop = false;
|
||||||
|
m_thread->join();
|
||||||
|
delete m_thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp)
|
||||||
|
{
|
||||||
|
return new CHIDDeviceWinUSB(token, devImp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,217 @@
|
||||||
|
#include "IHIDListener.hpp"
|
||||||
|
#include "inputdev/CDeviceFinder.hpp"
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
#include <IOKit/hid/IOHIDLib.h>
|
||||||
|
#include <IOKit/IOKitLib.h>
|
||||||
|
#include <IOKit/usb/IOUSBLib.h>
|
||||||
|
#include <IOKit/IOCFPlugIn.h>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reference: http://oroboro.com/usb-serial-number-osx/
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool getUSBStringDescriptor(IOUSBDeviceInterface182** usbDevice, UInt8 idx, char* out)
|
||||||
|
{
|
||||||
|
UInt16 buffer[128];
|
||||||
|
|
||||||
|
// wow... we're actually forced to make hard coded bus requests. Its like
|
||||||
|
// hard disk programming in the 80's!
|
||||||
|
IOUSBDevRequest request;
|
||||||
|
|
||||||
|
request.bmRequestType = USBmakebmRequestType(kUSBIn,
|
||||||
|
kUSBStandard,
|
||||||
|
kUSBDevice);
|
||||||
|
request.bRequest = kUSBRqGetDescriptor;
|
||||||
|
request.wValue = (kUSBStringDesc << 8) | idx;
|
||||||
|
request.wIndex = 0x409; // english
|
||||||
|
request.wLength = sizeof(buffer);
|
||||||
|
request.pData = buffer;
|
||||||
|
|
||||||
|
kern_return_t err = (*usbDevice)->DeviceRequest(usbDevice, &request);
|
||||||
|
if (err != 0)
|
||||||
|
{
|
||||||
|
// the request failed... fairly uncommon for the USB disk driver, but not
|
||||||
|
// so uncommon for other devices. This can also be less reliable if your
|
||||||
|
// disk is mounted through an external USB hub. At this level we actually
|
||||||
|
// have to worry about hardware issues like this.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're mallocing this string just as an example. But you probably will want
|
||||||
|
// to do something smarter, like pre-allocated buffers in the info class, or
|
||||||
|
// use a string class.
|
||||||
|
unsigned count = (request.wLenDone - 1) / 2;
|
||||||
|
unsigned i;
|
||||||
|
for (i=0 ; i<count ; ++i)
|
||||||
|
out[i] = buffer[i+1];
|
||||||
|
out[i] = '\0';
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CHIDListenerIOKit final : public IHIDListener
|
||||||
|
{
|
||||||
|
CDeviceFinder& m_finder;
|
||||||
|
|
||||||
|
CFRunLoopRef m_listenerRunLoop;
|
||||||
|
IONotificationPortRef m_llPort;
|
||||||
|
io_iterator_t m_llAddNotif, m_llRemoveNotif;
|
||||||
|
bool m_scanningEnabled;
|
||||||
|
|
||||||
|
static void devicesConnectedUSBLL(CHIDListenerIOKit* listener,
|
||||||
|
io_iterator_t iterator)
|
||||||
|
{
|
||||||
|
io_object_t obj;
|
||||||
|
while ((obj = IOIteratorNext(iterator)))
|
||||||
|
{
|
||||||
|
io_string_t devPath;
|
||||||
|
if (IORegistryEntryGetPath(obj, kIOServicePlane, devPath) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!listener->m_scanningEnabled ||
|
||||||
|
listener->m_finder._hasToken(devPath))
|
||||||
|
{
|
||||||
|
IOObjectRelease(obj);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
IOCFPlugInInterface** devServ;
|
||||||
|
SInt32 score;
|
||||||
|
IOReturn err;
|
||||||
|
err = IOCreatePlugInInterfaceForService(obj, kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &devServ, &score);
|
||||||
|
if (err != kIOReturnSuccess)
|
||||||
|
throw std::runtime_error("unable to open IOKit plugin interface");
|
||||||
|
|
||||||
|
IOUSBDeviceInterface182 **dev;
|
||||||
|
err = (*devServ)->QueryInterface(devServ,
|
||||||
|
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID182),
|
||||||
|
(LPVOID*)&dev);
|
||||||
|
if (err != kIOReturnSuccess)
|
||||||
|
throw std::runtime_error("unable to open IOKit device interface");
|
||||||
|
|
||||||
|
UInt16 vid, pid;
|
||||||
|
(*dev)->GetDeviceVendor(dev, &vid);
|
||||||
|
(*dev)->GetDeviceProduct(dev, &pid);
|
||||||
|
|
||||||
|
UInt8 vstridx, pstridx;
|
||||||
|
(*dev)->USBGetManufacturerStringIndex(dev, &vstridx);
|
||||||
|
(*dev)->USBGetProductStringIndex(dev, &pstridx);
|
||||||
|
|
||||||
|
char vstr[128] = {0};
|
||||||
|
char pstr[128] = {0};
|
||||||
|
getUSBStringDescriptor(dev, vstridx, vstr);
|
||||||
|
getUSBStringDescriptor(dev, pstridx, pstr);
|
||||||
|
|
||||||
|
if (!listener->m_finder._insertToken(CDeviceToken(CDeviceToken::DEVTYPE_USB,
|
||||||
|
vid, pid, vstr, pstr, devPath)))
|
||||||
|
{
|
||||||
|
/* Matched-insertion failed; see if generic HID interface is available */
|
||||||
|
/* TODO: Do */
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("ADDED %08X %s\n", obj, devPath);
|
||||||
|
(*dev)->Release(dev);
|
||||||
|
IODestroyPlugInInterface(devServ);
|
||||||
|
IOObjectRelease(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void devicesDisconnectedUSBLL(CHIDListenerIOKit* listener,
|
||||||
|
io_iterator_t iterator)
|
||||||
|
{
|
||||||
|
if (CFRunLoopGetCurrent() != listener->m_listenerRunLoop)
|
||||||
|
{
|
||||||
|
CFRunLoopPerformBlock(listener->m_listenerRunLoop, kCFRunLoopDefaultMode, ^{
|
||||||
|
devicesDisconnectedUSBLL(listener, iterator);
|
||||||
|
});
|
||||||
|
CFRunLoopWakeUp(listener->m_listenerRunLoop);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
io_object_t obj;
|
||||||
|
while ((obj = IOIteratorNext(iterator)))
|
||||||
|
{
|
||||||
|
io_string_t devPath;
|
||||||
|
if (IORegistryEntryGetPath(obj, kIOServicePlane, devPath) != 0)
|
||||||
|
continue;
|
||||||
|
listener->m_finder._removeToken(devPath);
|
||||||
|
//printf("REMOVED %08X %s\n", obj, devPath);
|
||||||
|
IOObjectRelease(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
CHIDListenerIOKit(CDeviceFinder& finder)
|
||||||
|
: m_finder(finder)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Register Low-Level USB Matcher */
|
||||||
|
m_listenerRunLoop = CFRunLoopGetCurrent();
|
||||||
|
m_llPort = IONotificationPortCreate(kIOMasterPortDefault);
|
||||||
|
CFRunLoopSourceRef rlSrc = IONotificationPortGetRunLoopSource(m_llPort);
|
||||||
|
CFRunLoopAddSource(m_listenerRunLoop, rlSrc, kCFRunLoopDefaultMode);
|
||||||
|
|
||||||
|
CFMutableDictionaryRef matchDict = IOServiceMatching(kIOUSBDeviceClassName);
|
||||||
|
CFRetain(matchDict);
|
||||||
|
|
||||||
|
m_scanningEnabled = true;
|
||||||
|
kern_return_t llRet =
|
||||||
|
IOServiceAddMatchingNotification(m_llPort, kIOMatchedNotification, matchDict,
|
||||||
|
(IOServiceMatchingCallback)devicesConnectedUSBLL, this, &m_llAddNotif);
|
||||||
|
if (llRet == kIOReturnSuccess)
|
||||||
|
devicesConnectedUSBLL(this, m_llAddNotif);
|
||||||
|
|
||||||
|
llRet =
|
||||||
|
IOServiceAddMatchingNotification(m_llPort, kIOTerminatedNotification, matchDict,
|
||||||
|
(IOServiceMatchingCallback)devicesDisconnectedUSBLL, this, &m_llRemoveNotif);
|
||||||
|
if (llRet == kIOReturnSuccess)
|
||||||
|
devicesDisconnectedUSBLL(this, m_llRemoveNotif);
|
||||||
|
|
||||||
|
m_scanningEnabled = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~CHIDListenerIOKit()
|
||||||
|
{
|
||||||
|
CFRunLoopRemoveSource(m_listenerRunLoop, IONotificationPortGetRunLoopSource(m_llPort), kCFRunLoopDefaultMode);
|
||||||
|
IOObjectRelease(m_llAddNotif);
|
||||||
|
IOObjectRelease(m_llRemoveNotif);
|
||||||
|
IONotificationPortDestroy(m_llPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Automatic device scanning */
|
||||||
|
bool startScanning()
|
||||||
|
{
|
||||||
|
m_scanningEnabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool stopScanning()
|
||||||
|
{
|
||||||
|
m_scanningEnabled = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Manual device scanning */
|
||||||
|
bool scanNow()
|
||||||
|
{
|
||||||
|
io_iterator_t iter;
|
||||||
|
if (IOServiceGetMatchingServices(kIOMasterPortDefault,
|
||||||
|
IOServiceMatching(kIOUSBDeviceClassName), &iter) == kIOReturnSuccess)
|
||||||
|
{
|
||||||
|
devicesConnectedUSBLL(this, iter);
|
||||||
|
IOObjectRelease(iter);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
IHIDListener* IHIDListenerNew(CDeviceFinder& finder)
|
||||||
|
{
|
||||||
|
return new CHIDListenerIOKit(finder);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
#include "IHIDListener.hpp"
|
|
|
@ -1 +1,215 @@
|
||||||
#include "IHIDListener.hpp"
|
#include "inputdev/IHIDListener.hpp"
|
||||||
|
#include "inputdev/CDeviceFinder.hpp"
|
||||||
|
#include <libudev.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
static udev* UDEV_INST = NULL;
|
||||||
|
udev* GetUdev()
|
||||||
|
{
|
||||||
|
if (!UDEV_INST)
|
||||||
|
UDEV_INST = udev_new();
|
||||||
|
return UDEV_INST;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CHIDListenerUdev final : public IHIDListener
|
||||||
|
{
|
||||||
|
CDeviceFinder& m_finder;
|
||||||
|
|
||||||
|
udev_monitor* m_udevMon;
|
||||||
|
std::thread* m_udevThread;
|
||||||
|
bool m_udevRunning;
|
||||||
|
bool m_scanningEnabled;
|
||||||
|
|
||||||
|
static void deviceConnected(CHIDListenerUdev* listener,
|
||||||
|
udev_device* device)
|
||||||
|
{
|
||||||
|
if (!listener->m_scanningEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Filter to USB/BT */
|
||||||
|
const char* dt = udev_device_get_devtype(device);
|
||||||
|
CDeviceToken::TDeviceType type;
|
||||||
|
if (!strcmp(dt, "usb_device"))
|
||||||
|
type = CDeviceToken::DEVTYPE_USB;
|
||||||
|
else if (!strcmp(dt, "bluetooth_device"))
|
||||||
|
type = CDeviceToken::DEVTYPE_BLUETOOTH;
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Prevent redundant registration */
|
||||||
|
const char* devPath = udev_device_get_syspath(device);
|
||||||
|
if (listener->m_finder._hasToken(devPath))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int vid = 0, pid = 0;
|
||||||
|
udev_list_entry* attrs = udev_device_get_properties_list_entry(device);
|
||||||
|
#if 0
|
||||||
|
udev_list_entry* att = NULL;
|
||||||
|
udev_list_entry_foreach(att, attrs)
|
||||||
|
{
|
||||||
|
const char* name = udev_list_entry_get_name(att);
|
||||||
|
const char* val = udev_list_entry_get_value(att);
|
||||||
|
fprintf(stderr, "%s %s\n", name, val);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "\n\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
udev_list_entry* vide = udev_list_entry_get_by_name(attrs, "ID_VENDOR_ID");
|
||||||
|
if (vide)
|
||||||
|
vid = strtol(udev_list_entry_get_value(vide), NULL, 16);
|
||||||
|
|
||||||
|
udev_list_entry* pide = udev_list_entry_get_by_name(attrs, "ID_MODEL_ID");
|
||||||
|
if (pide)
|
||||||
|
pid = strtol(udev_list_entry_get_value(pide), NULL, 16);
|
||||||
|
|
||||||
|
const char* manuf = NULL;
|
||||||
|
udev_list_entry* manufe = udev_list_entry_get_by_name(attrs, "ID_VENDOR");
|
||||||
|
if (manufe)
|
||||||
|
manuf = udev_list_entry_get_value(manufe);
|
||||||
|
|
||||||
|
const char* product = NULL;
|
||||||
|
udev_list_entry* producte = udev_list_entry_get_by_name(attrs, "ID_MODEL");
|
||||||
|
if (producte)
|
||||||
|
product = udev_list_entry_get_value(producte);
|
||||||
|
|
||||||
|
if (!listener->m_finder._insertToken(CDeviceToken(type, vid, pid, manuf, product, devPath)))
|
||||||
|
{
|
||||||
|
/* Matched-insertion failed; see if generic HID interface is available */
|
||||||
|
udev_list_entry* devInterfaces = NULL;
|
||||||
|
if (type == CDeviceToken::DEVTYPE_USB)
|
||||||
|
devInterfaces = udev_list_entry_get_by_name(attrs, "ID_USB_INTERFACES");
|
||||||
|
else if (type == CDeviceToken::DEVTYPE_BLUETOOTH)
|
||||||
|
devInterfaces = udev_list_entry_get_by_name(attrs, "ID_BLUETOOTH_INTERFACES");
|
||||||
|
if (devInterfaces)
|
||||||
|
{
|
||||||
|
const char* interfacesStr = udev_list_entry_get_value(devInterfaces);
|
||||||
|
if (strstr(interfacesStr, ":030104") || /* HID / GenericDesktop / Joystick */
|
||||||
|
strstr(interfacesStr, ":030105")) /* HID / GenericDesktop / Gamepad */
|
||||||
|
{
|
||||||
|
udev_enumerate* hidEnum = udev_enumerate_new(UDEV_INST);
|
||||||
|
udev_enumerate_add_match_parent(hidEnum, device);
|
||||||
|
udev_enumerate_add_match_subsystem(hidEnum, "hid");
|
||||||
|
udev_enumerate_scan_devices(hidEnum);
|
||||||
|
udev_list_entry* hidEnt = udev_enumerate_get_list_entry(hidEnum);
|
||||||
|
if (hidEnt)
|
||||||
|
{
|
||||||
|
const char* hidPath = udev_list_entry_get_name(hidEnt);
|
||||||
|
if (!listener->m_finder._hasToken(hidPath))
|
||||||
|
listener->m_finder._insertToken(CDeviceToken(CDeviceToken::DEVTYPE_GENERICHID,
|
||||||
|
vid, pid, manuf, product, hidPath));
|
||||||
|
}
|
||||||
|
udev_enumerate_unref(hidEnum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void deviceDisconnected(CHIDListenerUdev* listener,
|
||||||
|
udev_device* device)
|
||||||
|
{
|
||||||
|
const char* devPath = udev_device_get_syspath(device);
|
||||||
|
listener->m_finder._removeToken(devPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _udevProc(CHIDListenerUdev* listener)
|
||||||
|
{
|
||||||
|
udev_monitor_enable_receiving(listener->m_udevMon);
|
||||||
|
int fd = udev_monitor_get_fd(listener->m_udevMon);
|
||||||
|
while (listener->m_udevRunning)
|
||||||
|
{
|
||||||
|
fd_set fds;
|
||||||
|
FD_ZERO(&fds);
|
||||||
|
FD_SET(fd, &fds);
|
||||||
|
select(fd+1, &fds, NULL, NULL, NULL);
|
||||||
|
udev_device* dev = udev_monitor_receive_device(listener->m_udevMon);
|
||||||
|
if (dev)
|
||||||
|
{
|
||||||
|
const char* action = udev_device_get_action(dev);
|
||||||
|
if (!strcmp(action, "add"))
|
||||||
|
deviceConnected(listener, dev);
|
||||||
|
else if (!strcmp(action, "remove"))
|
||||||
|
deviceDisconnected(listener, dev);
|
||||||
|
udev_device_unref(dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
CHIDListenerUdev(CDeviceFinder& finder)
|
||||||
|
: m_finder(finder)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Setup hotplug events */
|
||||||
|
m_udevMon = udev_monitor_new_from_netlink(GetUdev(), "udev");
|
||||||
|
if (!m_udevMon)
|
||||||
|
throw std::runtime_error("unable to init udev_monitor");
|
||||||
|
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "usb", "usb_device");
|
||||||
|
udev_monitor_filter_add_match_subsystem_devtype(m_udevMon, "bluetooth", "bluetooth_device");
|
||||||
|
udev_monitor_filter_update(m_udevMon);
|
||||||
|
|
||||||
|
/* Initial HID Device Add */
|
||||||
|
m_scanningEnabled = true;
|
||||||
|
scanNow();
|
||||||
|
m_scanningEnabled = false;
|
||||||
|
|
||||||
|
/* Start hotplug thread */
|
||||||
|
m_udevRunning = true;
|
||||||
|
m_udevThread = new std::thread(_udevProc, this);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
~CHIDListenerUdev()
|
||||||
|
{
|
||||||
|
m_udevRunning = false;
|
||||||
|
m_udevThread->join();
|
||||||
|
delete m_udevThread;
|
||||||
|
udev_monitor_unref(m_udevMon);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Automatic device scanning */
|
||||||
|
bool startScanning()
|
||||||
|
{
|
||||||
|
m_scanningEnabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool stopScanning()
|
||||||
|
{
|
||||||
|
m_scanningEnabled = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Manual device scanning */
|
||||||
|
bool scanNow()
|
||||||
|
{
|
||||||
|
udev_enumerate* uenum = udev_enumerate_new(GetUdev());
|
||||||
|
udev_enumerate_add_match_subsystem(uenum, "usb");
|
||||||
|
udev_enumerate_add_match_property(uenum, "DEVTYPE", "usb_device");
|
||||||
|
udev_enumerate_add_match_subsystem(uenum, "bluetooth");
|
||||||
|
udev_enumerate_add_match_property(uenum, "DEVTYPE", "bluetooth_device");
|
||||||
|
udev_enumerate_scan_devices(uenum);
|
||||||
|
udev_list_entry* uenumList = udev_enumerate_get_list_entry(uenum);
|
||||||
|
udev_list_entry* uenumItem;
|
||||||
|
udev_list_entry_foreach(uenumItem, uenumList)
|
||||||
|
{
|
||||||
|
const char* devPath = udev_list_entry_get_name(uenumItem);
|
||||||
|
udev_device* dev = udev_device_new_from_syspath(UDEV_INST, devPath);
|
||||||
|
if (dev)
|
||||||
|
deviceConnected(this, dev);
|
||||||
|
udev_device_unref(dev);
|
||||||
|
}
|
||||||
|
udev_enumerate_unref(uenum);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
IHIDListener* IHIDListenerNew(CDeviceFinder& finder)
|
||||||
|
{
|
||||||
|
return new CHIDListenerUdev(finder);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
#include "IHIDListener.hpp"
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS 1 /* STFU MSVC */
|
||||||
|
#include "inputdev/IHIDListener.hpp"
|
||||||
|
#include "inputdev/CDeviceFinder.hpp"
|
||||||
|
#include <string.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#define _WIN32_LEAN_AND_MEAN 1
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <initguid.h>
|
||||||
|
#include <SetupAPI.h>
|
||||||
|
#include <Cfgmgr32.h>
|
||||||
|
#include <Usbiodef.h>
|
||||||
|
#include <Devpkey.h>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
class CHIDListenerWinUSB final : public IHIDListener
|
||||||
|
{
|
||||||
|
CDeviceFinder& m_finder;
|
||||||
|
|
||||||
|
bool m_scanningEnabled;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reference: https://github.com/pbatard/libwdi/blob/master/libwdi/libwdi.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
void _pollDevices(const char* pathFilter)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Don't ask */
|
||||||
|
static const LPCSTR arPrefix[3] = {"VID_", "PID_", "MI_"};
|
||||||
|
unsigned i, j;
|
||||||
|
CONFIGRET r;
|
||||||
|
ULONG devpropType;
|
||||||
|
DWORD reg_type;
|
||||||
|
HDEVINFO hDevInfo = 0;
|
||||||
|
SP_DEVINFO_DATA DeviceInfoData = {0};
|
||||||
|
DeviceInfoData.cbSize = sizeof(DeviceInfoData);
|
||||||
|
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = {0};
|
||||||
|
DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData);
|
||||||
|
union {
|
||||||
|
SP_DEVICE_INTERFACE_DETAIL_DATA_A wtf;
|
||||||
|
CHAR alloc[2048];
|
||||||
|
} DeviceInterfaceDetailData; /* Stack allocation should be fine for this */
|
||||||
|
DeviceInterfaceDetailData.wtf.cbSize = sizeof(DeviceInterfaceDetailData);
|
||||||
|
CHAR szDeviceInstanceID[MAX_DEVICE_ID_LEN];
|
||||||
|
LPSTR pszToken, pszNextToken;
|
||||||
|
CHAR szVid[MAX_DEVICE_ID_LEN], szPid[MAX_DEVICE_ID_LEN], szMi[MAX_DEVICE_ID_LEN];
|
||||||
|
|
||||||
|
/* List all connected USB devices */
|
||||||
|
hDevInfo = SetupDiGetClassDevs(NULL, 0, 0, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE);
|
||||||
|
if (hDevInfo == INVALID_HANDLE_VALUE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i=0 ; ; ++i)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!SetupDiEnumDeviceInterfaces(hDevInfo,
|
||||||
|
NULL,
|
||||||
|
&GUID_DEVINTERFACE_USB_DEVICE,
|
||||||
|
i,
|
||||||
|
&DeviceInterfaceData))
|
||||||
|
break;
|
||||||
|
|
||||||
|
DeviceInterfaceDetailData.wtf.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||||
|
if (!SetupDiGetDeviceInterfaceDetailA(hDevInfo,
|
||||||
|
&DeviceInterfaceData,
|
||||||
|
&DeviceInterfaceDetailData.wtf,
|
||||||
|
sizeof(DeviceInterfaceDetailData),
|
||||||
|
NULL,
|
||||||
|
&DeviceInfoData))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
r = CM_Get_Device_IDA(DeviceInfoData.DevInst, szDeviceInstanceID , MAX_PATH, 0);
|
||||||
|
if (r != CR_SUCCESS)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Retreive the device description as reported by the device itself */
|
||||||
|
pszToken = strtok_s(szDeviceInstanceID , "\\#&", &pszNextToken);
|
||||||
|
szVid[0] = '\0';
|
||||||
|
szPid[0] = '\0';
|
||||||
|
szMi[0] = '\0';
|
||||||
|
while (pszToken != NULL)
|
||||||
|
{
|
||||||
|
for (j=0 ; j<3 ; ++j)
|
||||||
|
{
|
||||||
|
if (strncmp(pszToken, arPrefix[j], 4) == 0)
|
||||||
|
{
|
||||||
|
switch (j)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
strcpy_s(szVid, MAX_DEVICE_ID_LEN, pszToken);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
strcpy_s(szPid, MAX_DEVICE_ID_LEN, pszToken);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
strcpy_s(szMi, MAX_DEVICE_ID_LEN, pszToken);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pszToken = strtok_s(NULL, "\\#&", &pszNextToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!szVid[0] || !szPid[0])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
unsigned vid = strtol(szVid+4, NULL, 16);
|
||||||
|
unsigned pid = strtol(szPid+4, NULL, 16);
|
||||||
|
|
||||||
|
WCHAR productW[1024] = {0};
|
||||||
|
CHAR product[1024] = {0};
|
||||||
|
DWORD productSz = 0;
|
||||||
|
if (!SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_BusReportedDeviceDesc,
|
||||||
|
&devpropType, (BYTE*)productW, 1024, &productSz, 0)) {
|
||||||
|
/* fallback to SPDRP_DEVICEDESC (USB hubs still use it) */
|
||||||
|
SetupDiGetDeviceRegistryPropertyA(hDevInfo, &DeviceInfoData, SPDRP_DEVICEDESC,
|
||||||
|
®_type, (BYTE*)productW, 1024, &productSz);
|
||||||
|
}
|
||||||
|
wcstombs(product, productW, productSz / 2);
|
||||||
|
|
||||||
|
WCHAR manufW[1024] = L"Someone"; /* Windows Vista and earlier will use this as the vendor */
|
||||||
|
CHAR manuf[1024] = {0};
|
||||||
|
DWORD manufSz = 0;
|
||||||
|
SetupDiGetDevicePropertyW(hDevInfo, &DeviceInfoData, &DEVPKEY_Device_Manufacturer,
|
||||||
|
&devpropType, (BYTE*)manufW, 1024, &manufSz, 0);
|
||||||
|
wcstombs(manuf, manufW, manufSz / 2);
|
||||||
|
|
||||||
|
/* Store as a shouting string (to keep hash-lookups consistent) */
|
||||||
|
CharUpperA(DeviceInterfaceDetailData.wtf.DevicePath);
|
||||||
|
|
||||||
|
/* Filter to specific device (provided by hotplug event) */
|
||||||
|
if (pathFilter && strcmp(pathFilter, DeviceInterfaceDetailData.wtf.DevicePath))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Whew!! that's a single device enumerated!! */
|
||||||
|
if (!m_finder._hasToken(DeviceInterfaceDetailData.wtf.DevicePath))
|
||||||
|
m_finder._insertToken(CDeviceToken(CDeviceToken::DEVTYPE_USB,
|
||||||
|
vid, pid, manuf, product,
|
||||||
|
DeviceInterfaceDetailData.wtf.DevicePath));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SetupDiDestroyDeviceInfoList(hDevInfo);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
CHIDListenerWinUSB(CDeviceFinder& finder)
|
||||||
|
: m_finder(finder)
|
||||||
|
{
|
||||||
|
/* Initial HID Device Add */
|
||||||
|
_pollDevices(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
~CHIDListenerWinUSB()
|
||||||
|
{}
|
||||||
|
|
||||||
|
/* Automatic device scanning */
|
||||||
|
bool startScanning()
|
||||||
|
{
|
||||||
|
m_scanningEnabled = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
bool stopScanning()
|
||||||
|
{
|
||||||
|
m_scanningEnabled = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Manual device scanning */
|
||||||
|
bool scanNow()
|
||||||
|
{
|
||||||
|
_pollDevices(NULL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _extDevConnect(const char* path)
|
||||||
|
{
|
||||||
|
char upperPath[1024];
|
||||||
|
strcpy_s(upperPath, 1024, path);
|
||||||
|
CharUpperA(upperPath);
|
||||||
|
if (m_scanningEnabled && !m_finder._hasToken(upperPath))
|
||||||
|
_pollDevices(upperPath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _extDevDisconnect(const char* path)
|
||||||
|
{
|
||||||
|
char upperPath[1024];
|
||||||
|
strcpy_s(upperPath, 1024, path);
|
||||||
|
CharUpperA(upperPath);
|
||||||
|
m_finder._removeToken(upperPath);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
IHIDListener* IHIDListenerNew(CDeviceFinder& finder)
|
||||||
|
{
|
||||||
|
return new CHIDListenerWinUSB(finder);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,9 +1,22 @@
|
||||||
#ifndef IHIDDEVICE_HPP
|
#ifndef IHIDDEVICE_HPP
|
||||||
#define IHIDDEVICE_HPP
|
#define IHIDDEVICE_HPP
|
||||||
|
|
||||||
class IHIDDevice
|
#include "inputdev/CDeviceToken.hpp"
|
||||||
|
|
||||||
|
namespace boo
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class IHIDDevice
|
||||||
|
{
|
||||||
|
friend class CDeviceBase;
|
||||||
|
virtual void _deviceDisconnected()=0;
|
||||||
|
virtual bool _sendUSBInterruptTransfer(uint8_t pipe, const uint8_t* data, size_t length)=0;
|
||||||
|
virtual size_t _receiveUSBInterruptTransfer(uint8_t pipe, uint8_t* data, size_t length)=0;
|
||||||
|
virtual bool _sendHIDReport(const uint8_t* data, size_t length)=0;
|
||||||
|
public:
|
||||||
|
inline virtual ~IHIDDevice() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // IHIDDEVICE_HPP
|
#endif // IHIDDEVICE_HPP
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
#ifndef IHIDLISTENER_HPP
|
|
||||||
#define IHIDLISTENER_HPP
|
|
||||||
|
|
||||||
class IHIDListener
|
|
||||||
{
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // IHIDLISTENER_HPP
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
#include "inputdev/SDeviceSignature.hpp"
|
||||||
|
#include "inputdev/CDeviceToken.hpp"
|
||||||
|
#include "inputdev/CGenericPad.hpp"
|
||||||
|
#include "IHIDDevice.hpp"
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
extern const SDeviceSignature BOO_DEVICE_SIGS[];
|
||||||
|
|
||||||
|
|
||||||
|
bool SDeviceSignature::DeviceMatchToken(const CDeviceToken& token, const TDeviceSignatureSet& sigSet)
|
||||||
|
{
|
||||||
|
if (token.getDeviceType() == CDeviceToken::DEVTYPE_GENERICHID)
|
||||||
|
return true;
|
||||||
|
for (const SDeviceSignature* sig : sigSet)
|
||||||
|
{
|
||||||
|
if (sig->m_vid == token.getVendorId() && sig->m_pid == token.getProductId())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp);
|
||||||
|
CDeviceBase* SDeviceSignature::DeviceNew(CDeviceToken& token)
|
||||||
|
{
|
||||||
|
CDeviceBase* retval = NULL;
|
||||||
|
|
||||||
|
/* Early-return for generic HID devices */
|
||||||
|
if (token.getDeviceType() == CDeviceToken::DEVTYPE_GENERICHID)
|
||||||
|
{
|
||||||
|
retval = new CGenericPad(&token);
|
||||||
|
if (!retval)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
IHIDDevice* newDev = IHIDDeviceNew(token, *retval);
|
||||||
|
if (!newDev)
|
||||||
|
{
|
||||||
|
delete retval;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise perform signature-matching to find the appropriate device-factory */
|
||||||
|
const SDeviceSignature* foundSig = NULL;
|
||||||
|
const SDeviceSignature* sigIter = BOO_DEVICE_SIGS;
|
||||||
|
unsigned targetVid = token.getVendorId();
|
||||||
|
unsigned targetPid = token.getProductId();
|
||||||
|
while (sigIter->m_name)
|
||||||
|
{
|
||||||
|
if (sigIter->m_vid == targetVid && sigIter->m_pid == targetPid)
|
||||||
|
{
|
||||||
|
foundSig = sigIter;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
++sigIter;
|
||||||
|
}
|
||||||
|
if (!foundSig)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
retval = foundSig->m_factory(&token);
|
||||||
|
if (!retval)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
IHIDDevice* newDev = IHIDDeviceNew(token, *retval);
|
||||||
|
if (!newDev)
|
||||||
|
{
|
||||||
|
delete retval;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,6 +2,9 @@
|
||||||
#include "CCGLContext.hpp"
|
#include "CCGLContext.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
CCGLContext::CCGLContext()
|
CCGLContext::CCGLContext()
|
||||||
: m_minVersion(3),
|
: m_minVersion(3),
|
||||||
m_majVersion(3)
|
m_majVersion(3)
|
||||||
|
@ -58,4 +61,7 @@ int CCGLContext::blueDepth() const
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -2,6 +2,9 @@
|
||||||
#include "x11/CGLXContext.hpp"
|
#include "x11/CGLXContext.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
CGLXContext::CGLXContext()
|
CGLXContext::CGLXContext()
|
||||||
: m_majVersion(3),
|
: m_majVersion(3),
|
||||||
m_minVersion(3),
|
m_minVersion(3),
|
||||||
|
@ -55,4 +58,6 @@ int CGLXContext::blueDepth() const
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // !defined(__APPLE__) && (defined(__linux__) || defined(BSD))
|
#endif // !defined(__APPLE__) && (defined(__linux__) || defined(BSD))
|
||||||
|
|
155
test/main.cpp
155
test/main.cpp
|
@ -1,15 +1,166 @@
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS 1
|
||||||
|
|
||||||
|
#if __APPLE__
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <boo.hpp>
|
#include <boo.hpp>
|
||||||
|
#if _WIN32
|
||||||
|
#define _WIN32_LEAN_AND_MEAN 1
|
||||||
|
#include <windows.h>
|
||||||
|
#include <initguid.h>
|
||||||
|
#include <Usbiodef.h>
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace boo
|
||||||
|
{
|
||||||
|
|
||||||
|
class CDolphinSmashAdapterCallback : public IDolphinSmashAdapterCallback
|
||||||
|
{
|
||||||
|
void controllerConnected(unsigned idx, EDolphinControllerType type)
|
||||||
|
{
|
||||||
|
printf("CONTROLLER %u CONNECTED\n", idx);
|
||||||
|
}
|
||||||
|
void controllerDisconnected(unsigned idx, EDolphinControllerType type)
|
||||||
|
{
|
||||||
|
printf("CONTROLLER %u DISCONNECTED\n", idx);
|
||||||
|
}
|
||||||
|
void controllerUpdate(unsigned idx, EDolphinControllerType type,
|
||||||
|
const SDolphinControllerState& state)
|
||||||
|
{
|
||||||
|
printf("CONTROLLER %u UPDATE %d %d\n", idx, state.m_leftStick[0], state.m_leftStick[1]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CTestDeviceFinder : public CDeviceFinder
|
||||||
|
{
|
||||||
|
CDolphinSmashAdapter* smashAdapter = NULL;
|
||||||
|
CDolphinSmashAdapterCallback m_cb;
|
||||||
|
public:
|
||||||
|
CTestDeviceFinder()
|
||||||
|
: CDeviceFinder({"CDolphinSmashAdapter"})
|
||||||
|
{}
|
||||||
|
void deviceConnected(CDeviceToken& tok)
|
||||||
|
{
|
||||||
|
smashAdapter = dynamic_cast<CDolphinSmashAdapter*>(tok.openAndGetDevice());
|
||||||
|
smashAdapter->setCallback(&m_cb);
|
||||||
|
smashAdapter->startRumble(0);
|
||||||
|
}
|
||||||
|
void deviceDisconnected(CDeviceToken&, CDeviceBase* device)
|
||||||
|
{
|
||||||
|
if (smashAdapter == device)
|
||||||
|
{
|
||||||
|
delete smashAdapter;
|
||||||
|
smashAdapter = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#if _WIN32
|
||||||
|
|
||||||
|
/* This simple 'test' console app needs a full windows
|
||||||
|
* message loop for receiving device connection events
|
||||||
|
*/
|
||||||
|
static const DEV_BROADCAST_DEVICEINTERFACE_A HOTPLUG_CONF =
|
||||||
|
{
|
||||||
|
sizeof(DEV_BROADCAST_DEVICEINTERFACE_A),
|
||||||
|
DBT_DEVTYP_DEVICEINTERFACE,
|
||||||
|
0,
|
||||||
|
GUID_DEVINTERFACE_USB_DEVICE
|
||||||
|
};
|
||||||
|
|
||||||
|
LRESULT CALLBACK WindowProc(
|
||||||
|
_In_ HWND hwnd,
|
||||||
|
_In_ UINT uMsg,
|
||||||
|
_In_ WPARAM wParam,
|
||||||
|
_In_ LPARAM lParam
|
||||||
|
)
|
||||||
|
{
|
||||||
|
switch (uMsg)
|
||||||
|
{
|
||||||
|
case WM_CREATE:
|
||||||
|
/* Register hotplug notification with windows */
|
||||||
|
RegisterDeviceNotificationA(hwnd, (LPVOID)&HOTPLUG_CONF, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case WM_DEVICECHANGE:
|
||||||
|
return boo::CDeviceFinder::winDevChangedHandler(wParam, lParam);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int APIENTRY wWinMain(
|
||||||
|
_In_ HINSTANCE hInstance,
|
||||||
|
_In_ HINSTANCE,
|
||||||
|
_In_ LPTSTR,
|
||||||
|
_In_ int
|
||||||
|
)
|
||||||
|
{
|
||||||
|
AllocConsole();
|
||||||
|
freopen("CONOUT$", "w", stdout);
|
||||||
|
|
||||||
|
WNDCLASS wndClass =
|
||||||
|
{
|
||||||
|
0,
|
||||||
|
WindowProc,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
hInstance,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
L"BooTestWindow"
|
||||||
|
};
|
||||||
|
|
||||||
|
RegisterClassW(&wndClass);
|
||||||
|
|
||||||
|
boo::CTestDeviceFinder finder;
|
||||||
|
finder.startScanning();
|
||||||
|
|
||||||
|
HWND hwnd = CreateWindowW(L"BooTestWindow", L"BooTest", WS_OVERLAPPEDWINDOW,
|
||||||
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
||||||
|
NULL, NULL, hInstance, NULL);
|
||||||
|
|
||||||
|
/* Pump messages */
|
||||||
|
MSG msg = {0};
|
||||||
|
while (GetMessage(&msg, hwnd, 0, 0))
|
||||||
|
{
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessage(&msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
IGraphicsContext* ctx = new CGraphicsContext;
|
|
||||||
|
boo::CTestDeviceFinder finder;
|
||||||
|
finder.startScanning();
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
boo::IGraphicsContext* ctx = new boo::CGraphicsContext;
|
||||||
|
|
||||||
if (ctx->create())
|
if (ctx->create())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
delete ctx;
|
#if __APPLE__
|
||||||
|
CFRunLoopRun();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//delete ctx;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$PWD/main.cpp
|
$$PWD/main.cpp
|
||||||
|
|
||||||
|
win32:SOURCES +=
|
||||||
|
|
Loading…
Reference in New Issue