diff --git a/include/inputdev/CDeviceFinder.hpp b/include/inputdev/CDeviceFinder.hpp index 7878d01..ec68a78 100644 --- a/include/inputdev/CDeviceFinder.hpp +++ b/include/inputdev/CDeviceFinder.hpp @@ -9,6 +9,12 @@ #include "SDeviceSignature.hpp" #include +#ifdef _WIN32 +#define _WIN32_LEAN_AND_MEAN 1 +#include +#include +#endif + namespace boo { @@ -146,6 +152,47 @@ public: 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 }; diff --git a/include/inputdev/IHIDListener.hpp b/include/inputdev/IHIDListener.hpp index edf1549..a1a26ce 100644 --- a/include/inputdev/IHIDListener.hpp +++ b/include/inputdev/IHIDListener.hpp @@ -23,6 +23,12 @@ public: /* 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 }; diff --git a/libBoo.pro b/libBoo.pro index 496ad73..849f483 100644 --- a/libBoo.pro +++ b/libBoo.pro @@ -5,7 +5,7 @@ CONFIG += console unix:QMAKE_CXXFLAGS += -stdlib=libc++ unix:LIBS += -std=c++11 -stdlib=libc++ -lc++abi -win32:LIBS += Setupapi.lib winusb.lib User32.lib +win32:LIBS += Setupapi.lib winusb.lib User32.lib /SUBSYSTEM:Windows #unix:!macx:CONFIG += link_pkgconfig #unix:!macx:PKGCONFIG += x11 diff --git a/src/inputdev/CDolphinSmashAdapter.cpp b/src/inputdev/CDolphinSmashAdapter.cpp index 4e5e840..ab5106e 100644 --- a/src/inputdev/CDolphinSmashAdapter.cpp +++ b/src/inputdev/CDolphinSmashAdapter.cpp @@ -56,6 +56,12 @@ parseState(SDolphinControllerState* stateOut, uint8_t* payload, bool& rumble) return type; } +void CDolphinSmashAdapter::initialCycle() +{ + uint8_t handshakePayload[] = {0x13}; + sendUSBInterruptTransfer(0, handshakePayload, sizeof(handshakePayload)); +} + void CDolphinSmashAdapter::transferCycle() { uint8_t payload[37]; @@ -107,12 +113,6 @@ void CDolphinSmashAdapter::transferCycle() } } -void CDolphinSmashAdapter::initialCycle() -{ - uint8_t handshakePayload[] = {0x13}; - sendUSBInterruptTransfer(0, handshakePayload, sizeof(handshakePayload)); -} - void CDolphinSmashAdapter::finalCycle() { uint8_t rumbleMessage[5] = {0x11, 0, 0, 0, 0}; diff --git a/src/inputdev/CHIDDeviceWinUSB.cpp b/src/inputdev/CHIDDeviceWinUSB.cpp index f1811ed..d419140 100644 --- a/src/inputdev/CHIDDeviceWinUSB.cpp +++ b/src/inputdev/CHIDDeviceWinUSB.cpp @@ -11,6 +11,8 @@ #define _WIN32_LEAN_AND_MEAN 1 #include #include +#include +#include namespace boo { @@ -37,8 +39,8 @@ class CHIDDeviceWinUSB final : public IHIDDevice { ULONG lengthTransferred = 0; if (!WinUsb_WritePipe(m_usbHandle, m_usbIntfOutPipe, (PUCHAR)data, - (ULONG)length, &lengthTransferred, NULL) - || lengthTransferred != length) + (ULONG)length, &lengthTransferred, NULL) || + lengthTransferred != length) return false; return true; } @@ -72,20 +74,22 @@ class CHIDDeviceWinUSB final : public IHIDDevice OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); - if (INVALID_HANDLE_VALUE == device->m_devHandle) { + 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, GetLastError()); + 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)) { + 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, GetLastError()); + device->m_devPath.c_str(), GetLastError()); device->m_devImp.deviceError(errStr); lk.unlock(); device->m_initCond.notify_one(); @@ -93,6 +97,32 @@ class CHIDDeviceWinUSB final : public IHIDDevice 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 ; im_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(); diff --git a/src/inputdev/CHIDListenerWinUSB.cpp b/src/inputdev/CHIDListenerWinUSB.cpp index 667b81c..e12084d 100644 --- a/src/inputdev/CHIDListenerWinUSB.cpp +++ b/src/inputdev/CHIDListenerWinUSB.cpp @@ -20,15 +20,13 @@ class CHIDListenerWinUSB final : public IHIDListener { CDeviceFinder& m_finder; - std::thread* m_setupThread; - bool m_setupRunning; bool m_scanningEnabled; /* * Reference: https://github.com/pbatard/libwdi/blob/master/libwdi/libwdi.c */ - void _pollDevices() + void _pollDevices(const char* pathFilter) { /* Don't ask */ @@ -133,6 +131,13 @@ class CHIDListenerWinUSB final : public IHIDListener &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, @@ -145,34 +150,16 @@ class CHIDListenerWinUSB final : public IHIDListener } - static void _setupProc(CHIDListenerWinUSB* listener) - { - while (listener->m_setupRunning) - { - if (listener->m_scanningEnabled) - listener->_pollDevices(); - - /* Due to NT derpiness, this needs to be a periodic poll */ - Sleep(1000); - } - } - public: CHIDListenerWinUSB(CDeviceFinder& finder) : m_finder(finder) { - /* Initial HID Device Add */ - _pollDevices(); - + _pollDevices(NULL); } ~CHIDListenerWinUSB() - { - m_setupRunning = false; - m_setupThread->join(); - delete m_setupThread; - } + {} /* Automatic device scanning */ bool startScanning() @@ -189,7 +176,26 @@ public: /* Manual device scanning */ bool scanNow() { - _pollDevices(); + _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; } diff --git a/src/inputdev/IHIDDevice.hpp b/src/inputdev/IHIDDevice.hpp index e65d311..99ca399 100644 --- a/src/inputdev/IHIDDevice.hpp +++ b/src/inputdev/IHIDDevice.hpp @@ -14,7 +14,7 @@ class IHIDDevice 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() {}; + inline virtual ~IHIDDevice() {} }; } diff --git a/test/main.cpp b/test/main.cpp index ba16599..79e4b27 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -1,3 +1,4 @@ +#define _CRT_SECURE_NO_WARNINGS 1 #if __APPLE__ #include @@ -10,7 +11,6 @@ #include #include #include -#include #else #include #endif @@ -61,6 +61,86 @@ public: } +#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) { @@ -77,40 +157,10 @@ int main(int argc, char** argv) #if __APPLE__ CFRunLoopRun(); -#elif _WIN32 - - /* Register hotplug notification with windows */ - DEV_BROADCAST_DEVICEINTERFACE_A hotplugConf = - { - sizeof(DEV_BROADCAST_DEVICEINTERFACE_A), - DBT_DEVTYP_DEVICEINTERFACE, - 0, - GUID_DEVINTERFACE_USB_DEVICE - }; - HWND consoleWnd = GetConsoleWindow(); - HDEVNOTIFY notHandle = RegisterDeviceNotificationA(consoleWnd, &hotplugConf, DEVICE_NOTIFY_WINDOW_HANDLE); - - MSG recvMsg; - while (GetMessage(&recvMsg, consoleWnd, 0, 0)) - { - printf("MSG: %d\n", recvMsg.message); - switch (recvMsg.message) - { - case WM_DEVICECHANGE: - printf("DEVICECHANGE!!\n"); - break; - - default: - TranslateMessage(&recvMsg); - DispatchMessage(&recvMsg); - break; - } - } - -#else - while (true) {sleep(1);} #endif //delete ctx; return 0; } + +#endif