Merge pull request #7 from RetroView/hid

lots of HID implementation
This commit is contained in:
Jack Andersen 2015-05-03 21:26:24 -10:00
commit cf9e12c650
47 changed files with 2426 additions and 71 deletions

0
.gitmodules vendored Normal file
View File

13
InputDeviceClasses.cpp Normal file
View File

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

View File

@ -3,4 +3,9 @@
#include "IInputWaiter.hpp" #include "IInputWaiter.hpp"
namespace boo
{
}
#endif // CINPUTDEFERREDRELAY_HPP #endif // CINPUTDEFERREDRELAY_HPP

View File

@ -3,9 +3,14 @@
#include "IInputWaiter.hpp" #include "IInputWaiter.hpp"
namespace boo
{
class CInputRelay : IInputWaiter class CInputRelay : IInputWaiter
{ {
}; };
}
#endif // CINPUTRELAY_HPP #endif // CINPUTRELAY_HPP

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,9 @@
#ifndef CCAFEPROPAD_HPP #ifndef CCAFEPROPAD_HPP
#define CCAFEPROPAD_HPP #define CCAFEPROPAD_HPP
namespace boo
{
}
#endif // CCAFEPROPAD_HPP #endif // CCAFEPROPAD_HPP

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,9 @@
#ifndef CDUALSHOCKPAD_HPP #ifndef CDUALSHOCKPAD_HPP
#define CDUALSHOCKPAD_HPP #define CDUALSHOCKPAD_HPP
namespace boo
{
}
#endif // CDUALSHOCKPAD_HPP #endif // CDUALSHOCKPAD_HPP

View File

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

View File

@ -1,5 +1,9 @@
#ifndef CREVOLUTIONPAD_HPP #ifndef CREVOLUTIONPAD_HPP
#define CREVOLUTIONPAD_HPP #define CREVOLUTIONPAD_HPP
namespace boo
{
}
#endif // CREVOLUTIONPAD_HPP #endif // CREVOLUTIONPAD_HPP

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
}
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/win/CWGLContext.cpp \
$$PWD/src/inputdev/CHIDDeviceWin32.cpp \ $$PWD/src/inputdev/CHIDListenerWinUSB.cpp \
$$PWD/src/inputdev/CHIDListenerWin32.cpp $$PWD/src/inputdev/CHIDDeviceWinUSB.cpp
}
INCLUDEPATH += $$PWD/include INCLUDEPATH += $$PWD/include

View File

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

View File

@ -1,11 +1,16 @@
#include "CSurface.hpp" #include "CSurface.hpp"
namespace boo
{
ISurface* CSurfaceNewWindow() ISurface* CSurfaceNewWindow()
{ {
return nullptr;
} }
ISurface* CSurfaceNewQWidget() ISurface* CSurfaceNewQWidget()
{ {
return nullptr;
}
} }

View File

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

View File

@ -1 +0,0 @@
#include "inputdev/CDeviceFinder.hpp"

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
#include "IHIDDevice.hpp"

View File

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

View File

@ -1 +0,0 @@
#include "IHIDDevice.hpp"

View File

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

View File

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

View File

@ -1 +0,0 @@
#include "IHIDListener.hpp"

View File

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

View File

@ -1 +0,0 @@
#include "IHIDListener.hpp"

View File

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

View File

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

View File

@ -1,9 +0,0 @@
#ifndef IHIDLISTENER_HPP
#define IHIDLISTENER_HPP
class IHIDListener
{
};
#endif // IHIDLISTENER_HPP

View File

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

View File

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

View File

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

View File

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

View File

@ -1,2 +1,4 @@
SOURCES += \ SOURCES += \
$$PWD/main.cpp $$PWD/main.cpp
win32:SOURCES +=