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"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif // CINPUTDEFERREDRELAY_HPP
|
||||
|
|
|
@ -3,9 +3,14 @@
|
|||
|
||||
#include "IInputWaiter.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
class CInputRelay : IInputWaiter
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CINPUTRELAY_HPP
|
||||
|
|
|
@ -3,7 +3,12 @@
|
|||
|
||||
#include "ISurface.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
ISurface* CSurfaceNewWindow();
|
||||
ISurface* CSurfaceNewQWidget();
|
||||
|
||||
}
|
||||
|
||||
#endif // CSURFACE_HPP
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
class IGraphicsContext
|
||||
{
|
||||
public:
|
||||
|
@ -19,4 +22,6 @@ public:
|
|||
virtual int blueDepth() const=0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // IGRAPHICSCONTEXT_HPP
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
#ifndef IINPUTWAITER_HPP
|
||||
#define IINPUTWAITER_HPP
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
class IInputWaiter
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // IINPUTWAITER_HPP
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
#ifndef IRETRACEWAITER_HPP
|
||||
#define IRETRACEWAITER_HPP
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
class IRetraceWaiter
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
#endif // IRETRACEWAITER_HPP
|
||||
}
|
||||
|
||||
#endif // IRETRACEWAITER_HPP
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
#ifndef ISURFACE_HPP
|
||||
#define ISURFACE_HPP
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
class ISurface
|
||||
{
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
#endif // CSURFACE_HPP
|
||||
}
|
||||
|
||||
#endif // CSURFACE_HPP
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
#ifndef BOO_HPP
|
||||
#define BOO_HPP
|
||||
|
||||
#include "IGraphicsContext.hpp"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#error "No support for WGL"
|
||||
#include "win/CWGLContext.hpp"
|
||||
namespace boo {typedef CWGLContext CGraphicsContext;}
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#include "mac/CCGLContext.hpp"
|
||||
typedef CCGLContext CGraphicsContext;
|
||||
namespace boo {typedef CCGLContext CGraphicsContext;}
|
||||
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#include "x11/CGLXContext.hpp"
|
||||
typedef CGLXContext CGraphicsContext;
|
||||
namespace boo {typedef CGLXContext CGraphicsContext;}
|
||||
|
||||
#endif
|
||||
|
||||
#include "IGraphicsContext.hpp"
|
||||
#include "inputdev/CDeviceFinder.hpp"
|
||||
#include "inputdev/CDolphinSmashAdapter.hpp"
|
||||
|
||||
#endif // BOO_HPP
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#ifndef CCAFEPROPAD_HPP
|
||||
#define CCAFEPROPAD_HPP
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif // CCAFEPROPAD_HPP
|
||||
|
|
|
@ -1,13 +1,44 @@
|
|||
#ifndef CDEVICEBASE
|
||||
#define CDEVICEBASE
|
||||
|
||||
#include "CDeviceToken.hpp"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
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:
|
||||
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
|
||||
|
|
|
@ -1,5 +1,201 @@
|
|||
#ifndef 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
|
||||
|
|
|
@ -2,21 +2,87 @@
|
|||
#define CDEVICETOKEN
|
||||
|
||||
#include <string>
|
||||
#include <IOKit/hid/IOHIDDevice.h>
|
||||
#include "CDeviceBase.hpp"
|
||||
#include "SDeviceSignature.hpp"
|
||||
|
||||
class CDeviceBase;
|
||||
namespace boo
|
||||
{
|
||||
|
||||
class CDeviceToken
|
||||
{
|
||||
std::string m_name;
|
||||
#if __APPLE__
|
||||
|
||||
#elif _WIN32
|
||||
#elif __linux__
|
||||
#endif
|
||||
public:
|
||||
const std::string& getName() const;
|
||||
CDeviceBase* getDevice() const;
|
||||
enum TDeviceType
|
||||
{
|
||||
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
|
||||
|
|
|
@ -1,5 +1,71 @@
|
|||
#ifndef 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
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#ifndef CDUALSHOCKPAD_HPP
|
||||
#define CDUALSHOCKPAD_HPP
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif // CDUALSHOCKPAD_HPP
|
||||
|
|
|
@ -1,5 +1,20 @@
|
|||
#ifndef 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
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
#ifndef CREVOLUTIONPAD_HPP
|
||||
#define CREVOLUTIONPAD_HPP
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#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 <OpenGL/OpenGL.h>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
class CCGLContext final : public IGraphicsContext
|
||||
{
|
||||
public:
|
||||
|
@ -25,5 +28,7 @@ private:
|
|||
int m_majVersion;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __APPLE__
|
||||
#endif // CCGLCONTEXT_HPP
|
||||
|
|
|
@ -3,6 +3,17 @@
|
|||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "IGraphicsContext.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
class CWGLContext : public IGraphicsContext
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
#endif // CWGLCONTEXT_HPP
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
|
||||
#if !defined(__APPLE__) && (defined(__linux__) || defined(BSD))
|
||||
#include <GL/glx.h>
|
||||
#include "IGraphicsContext.hpp"
|
||||
|
||||
#include <IGraphicsContext.hpp>
|
||||
namespace boo
|
||||
{
|
||||
|
||||
class CGLXContext final : public IGraphicsContext
|
||||
{
|
||||
|
@ -28,6 +30,7 @@ private:
|
|||
Display* m_display;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // !defined(__APPLE__) && (defined(__linux__) || defined(BSD))
|
||||
#endif // CGLXCONTEXT_HPP
|
||||
|
|
68
libBoo.pri
68
libBoo.pri
|
@ -1,5 +1,3 @@
|
|||
!contains(CONFIG,c++11):CONFIG += C++11
|
||||
|
||||
HEADERS += \
|
||||
$$PWD/include/boo.hpp \
|
||||
$$PWD/include/IGraphicsContext.hpp \
|
||||
|
@ -17,19 +15,12 @@ HEADERS += \
|
|||
$$PWD/include/inputdev/CDeviceFinder.hpp \
|
||||
$$PWD/include/inputdev/CDeviceToken.hpp \
|
||||
$$PWD/include/inputdev/CDeviceBase.hpp \
|
||||
$$PWD/include/inputdev/IHIDListener.hpp \
|
||||
$$PWD/src/inputdev/IHIDDevice.hpp \
|
||||
$$PWD/src/inputdev/IHIDListener.hpp
|
||||
|
||||
unix:!macx:HEADERS += \
|
||||
$$PWD/include/x11/CGLXContext.hpp
|
||||
|
||||
macx:HEADERS += \
|
||||
$$PWD/include/mac/CCGLContext.hpp
|
||||
|
||||
win32:HEADERS += \
|
||||
$$PWD/include/win/CWGLContext.hpp
|
||||
$$PWD/include/inputdev/SDeviceSignature.hpp
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/InputDeviceClasses.cpp \
|
||||
$$PWD/src/CSurface.cpp \
|
||||
$$PWD/src/CRetraceWaiter.cpp \
|
||||
$$PWD/src/CInputRelay.cpp \
|
||||
|
@ -39,25 +30,46 @@ SOURCES += \
|
|||
$$PWD/src/inputdev/CCafeProPad.cpp \
|
||||
$$PWD/src/inputdev/CDualshockPad.cpp \
|
||||
$$PWD/src/inputdev/CGenericPad.cpp \
|
||||
$$PWD/src/inputdev/CDeviceFinder.cpp \
|
||||
$$PWD/src/inputdev/CDeviceBase.cpp
|
||||
$$PWD/src/inputdev/CDeviceBase.cpp \
|
||||
$$PWD/src/inputdev/SDeviceSignature.cpp
|
||||
|
||||
unix:!macx:SOURCES += \
|
||||
$$PWD/src/x11/CGLXContext.cpp \
|
||||
$$PWD/src/inputdev/CHIDDeviceUdev.cpp \
|
||||
$$PWD/src/inputdev/CHIDListenerUdev.cpp
|
||||
unix:!macx {
|
||||
HEADERS += \
|
||||
$$PWD/include/x11/CGLXContext.hpp
|
||||
|
||||
macx:SOURCES += \
|
||||
$$PWD/src/mac/CCGLContext.cpp
|
||||
SOURCES += \
|
||||
$$PWD/src/x11/CGLXContext.cpp
|
||||
}
|
||||
|
||||
macx:OBJECTIVE_SOURCES += \
|
||||
$$PWD/src/mac/CCGLCocoaView.mm \
|
||||
$$PWD/src/inputdev/CHIDDeviceIOKit.mm \
|
||||
$$PWD/src/inputdev/CHIDListenerIOKit.mm
|
||||
linux {
|
||||
SOURCES += \
|
||||
$$PWD/src/inputdev/CHIDListenerUdev.cpp \
|
||||
$$PWD/src/inputdev/CHIDDeviceUdev.cpp
|
||||
|
||||
win32:SOURCES += \
|
||||
$$PWD/src/win/CWGLContext.cpp \
|
||||
$$PWD/src/inputdev/CHIDDeviceWin32.cpp \
|
||||
$$PWD/src/inputdev/CHIDListenerWin32.cpp
|
||||
LIBS += -ludev
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
|
12
libBoo.pro
12
libBoo.pro
|
@ -1,8 +1,14 @@
|
|||
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
|
||||
unix:!macx:PKGCONFIG += x11
|
||||
win32:LIBS += Setupapi.lib winusb.lib User32.lib /SUBSYSTEM:Windows
|
||||
|
||||
#unix:!macx:CONFIG += link_pkgconfig
|
||||
#unix:!macx:PKGCONFIG += x11
|
||||
|
||||
include(libBoo.pri)
|
||||
include(test/test.pri)
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
#include "CSurface.hpp"
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
ISurface* CSurfaceNewWindow()
|
||||
{
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ISurface* CSurfaceNewQWidget()
|
||||
{
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,3 +1,57 @@
|
|||
#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 <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/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 "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
|
||||
#define IHIDDEVICE_HPP
|
||||
|
||||
#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
|
||||
|
|
|
@ -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 <iostream>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
CCGLContext::CCGLContext()
|
||||
: m_minVersion(3),
|
||||
m_majVersion(3)
|
||||
|
@ -58,4 +61,7 @@ int CCGLContext::blueDepth() const
|
|||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -2,6 +2,9 @@
|
|||
#include "x11/CGLXContext.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace boo
|
||||
{
|
||||
|
||||
CGLXContext::CGLXContext()
|
||||
: m_majVersion(3),
|
||||
m_minVersion(3),
|
||||
|
@ -55,4 +58,6 @@ int CGLXContext::blueDepth() const
|
|||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#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 <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)
|
||||
{
|
||||
IGraphicsContext* ctx = new CGraphicsContext;
|
||||
|
||||
boo::CTestDeviceFinder finder;
|
||||
finder.startScanning();
|
||||
|
||||
#if 0
|
||||
boo::IGraphicsContext* ctx = new boo::CGraphicsContext;
|
||||
|
||||
if (ctx->create())
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __APPLE__
|
||||
CFRunLoopRun();
|
||||
#endif
|
||||
|
||||
delete ctx;
|
||||
//delete ctx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
SOURCES += \
|
||||
$$PWD/main.cpp
|
||||
$$PWD/main.cpp
|
||||
|
||||
win32:SOURCES +=
|
||||
|
|
Loading…
Reference in New Issue