diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..4c252a7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "extern/libwdi"] + path = extern/libwdi + url = https://github.com/jackoalan/libwdi.git diff --git a/WinUSBInstaller/WinUSBInstaller.manifest b/WinUSBInstaller/WinUSBInstaller.manifest new file mode 100644 index 0000000..c3b4160 --- /dev/null +++ b/WinUSBInstaller/WinUSBInstaller.manifest @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/WinUSBInstaller/WinUSBInstaller.pro b/WinUSBInstaller/WinUSBInstaller.pro new file mode 100644 index 0000000..3a122e2 --- /dev/null +++ b/WinUSBInstaller/WinUSBInstaller.pro @@ -0,0 +1,20 @@ +CONFIG += console +CONFIG -= app_bundle +CONFIG -= qt + +!win32 { + error(this project is designed for windows only) +} + +INCLUDEPATH += $$PWD/../extern/libwdi +LIBS += \ + Shell32.lib \ + Ole32.lib \ + Setupapi.lib \ + Advapi32.lib \ + User32.lib \ + $$PWD/../extern/libwdi/x64/Debug/lib/libwdi.lib + +SOURCES += main.c + + diff --git a/WinUSBInstaller/main.c b/WinUSBInstaller/main.c new file mode 100644 index 0000000..27e3364 --- /dev/null +++ b/WinUSBInstaller/main.c @@ -0,0 +1,39 @@ +#include +#include +#include + +int main(void) +{ + printf("Hello World!\n"); + + struct wdi_device_info *device, *list; + struct wdi_options_create_list WDI_LIST_OPTS = + { + true, false, true + }; + int err = wdi_create_list(&list, &WDI_LIST_OPTS); + if (err == WDI_SUCCESS) + { + for (device = list; device != NULL; device = device->next) + { + if (device->vid == 0x57E && device->pid == 0x337 && + !strcmp(device->driver, "HidUsb")) + { + printf("GC adapter detected; installing driver\n"); + char tempDir[128]; + GetTempPathA(128, tempDir); + err = wdi_prepare_driver(device, tempDir, "winusb_smash.inf", NULL); + if (err == WDI_SUCCESS) + { + err = wdi_install_driver(device, tempDir, "winusb_smash.inf", NULL); + printf(""); + } + break; + } + } + wdi_destroy_list(list); + } + + return 0; +} + diff --git a/extern/libwdi b/extern/libwdi new file mode 160000 index 0000000..aeacb8b --- /dev/null +++ b/extern/libwdi @@ -0,0 +1 @@ +Subproject commit aeacb8b85c8143d0e59b0f6c4206008a017ebde0 diff --git a/include/boo.hpp b/include/boo.hpp index 74883e6..c407f9e 100644 --- a/include/boo.hpp +++ b/include/boo.hpp @@ -2,13 +2,13 @@ #define BOO_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" namespace boo {typedef CCGLContext CGraphicsContext;} - #elif defined(__GNUC__) || defined(__clang__) #include "x11/CGLXContext.hpp" namespace boo {typedef CGLXContext CGraphicsContext;} diff --git a/include/inputdev/CDeviceToken.hpp b/include/inputdev/CDeviceToken.hpp index 7efc3b4..2753081 100644 --- a/include/inputdev/CDeviceToken.hpp +++ b/include/inputdev/CDeviceToken.hpp @@ -41,9 +41,21 @@ private: public: CDeviceToken(const CDeviceToken&) = delete; - CDeviceToken(CDeviceToken&&) = default; + 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) + : m_devType(devType), + m_vendorId(vid), + m_productId(pid), + m_devPath(path), + m_connectedDev(NULL) { if (vname) m_vendorName = vname; diff --git a/include/win/CWGLContext.hpp b/include/win/CWGLContext.hpp index 3c1e57d..ea40ff7 100644 --- a/include/win/CWGLContext.hpp +++ b/include/win/CWGLContext.hpp @@ -3,8 +3,16 @@ #ifdef _WIN32 +#include "IGraphicsContext.hpp" + namespace boo { + +class CWGLContext : public IGraphicsContext +{ + +}; + } #endif // _WIN32 diff --git a/libBoo.pro b/libBoo.pro index 38c4c31..d1460c1 100644 --- a/libBoo.pro +++ b/libBoo.pro @@ -1,8 +1,18 @@ CONFIG -= Qt +CONFIG += console #QMAKE_CXXFLAGS -= -std=c++0x -CONFIG += c++11 -QMAKE_CXXFLAGS += -stdlib=libc++ -LIBS += -std=c++11 -stdlib=libc++ -lc++abi +#CONFIG += c++11 +unix:QMAKE_CXXFLAGS += -stdlib=libc++ +unix:LIBS += -std=c++11 -stdlib=libc++ -lc++abi + +win32:INCLUDEPATH += $$PWD/extern/libwdi +win32:LIBS += \ + Shell32.lib \ + Ole32.lib \ + Setupapi.lib \ + Advapi32.lib \ + User32.lib \ + $$PWD/extern/libwdi/x64/Debug/lib/libwdi.lib #unix:!macx:CONFIG += link_pkgconfig #unix:!macx:PKGCONFIG += x11 diff --git a/src/CSurface.cpp b/src/CSurface.cpp index 4605050..e23af70 100644 --- a/src/CSurface.cpp +++ b/src/CSurface.cpp @@ -5,12 +5,12 @@ namespace boo ISurface* CSurfaceNewWindow() { - + return nullptr; } ISurface* CSurfaceNewQWidget() { - + return nullptr; } } diff --git a/src/inputdev/CHIDDeviceWin32.cpp b/src/inputdev/CHIDDeviceWin32.cpp index 2888b5b..4984fa0 100644 --- a/src/inputdev/CHIDDeviceWin32.cpp +++ b/src/inputdev/CHIDDeviceWin32.cpp @@ -1 +1,234 @@ #include "IHIDDevice.hpp" +#include "inputdev/CDeviceToken.hpp" +#include "inputdev/CDeviceBase.hpp" +#include +#include +#include +#include + +namespace boo +{ + +#if 0 + +udev* GetUdev(); + +#define MAX_REPORT_SIZE 65536 + +/* + * 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; + std::unique_lock lk(device->m_initMutex); + udev_device* udevDev = udev_device_new_from_syspath(GetUdev(), device->m_devPath.c_str()); + + /* Get the HID element's parent (USB interrupt transfer-interface) */ + const char* dp = udev_device_get_devnode(udevDev); + device->m_devFd = open(dp, O_RDWR); + if (device->m_devFd < 0) + { + char errStr[256]; + 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 ; im_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 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 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 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; + } + + +}; + +#endif + +IHIDDevice* IHIDDeviceNew(CDeviceToken& token, CDeviceBase& devImp) +{ + //return new CHIDDeviceUdev(token, devImp); + return nullptr; +} + +} diff --git a/src/inputdev/CHIDListenerWin32.cpp b/src/inputdev/CHIDListenerWin32.cpp index 90f36b2..7398249 100644 --- a/src/inputdev/CHIDListenerWin32.cpp +++ b/src/inputdev/CHIDListenerWin32.cpp @@ -1 +1,222 @@ -#include "IHIDListener.hpp" +#include "inputdev/IHIDListener.hpp" +#include "inputdev/CDeviceFinder.hpp" +#include +#include + +#define _WIN32_LEAN_AND_MEAN 1 +#include + +namespace boo +{ + +#if 0 + +static udev* UDEV_INST = NULL; +udev* GetUdev() +{ + if (!UDEV_INST) + UDEV_INST = udev_new(); + return UDEV_INST; +} + +class CHIDListenerWin32 final : public IHIDListener +{ + CDeviceFinder& m_finder; + + udev_monitor* m_udevMon; + std::thread* m_udevThread; + bool m_udevRunning; + bool m_scanningEnabled; + + static void deviceConnected(CHIDListenerWin32* 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(CHIDListenerWin32* listener, + udev_device* device) + { + const char* devPath = udev_device_get_syspath(device); + listener->m_finder._removeToken(devPath); + } + + static void _udevProc(CHIDListenerWin32* 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: + CHIDListenerWin32(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); + + } + + ~CHIDListenerWin32() + { + 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; + } + +}; + +#endif + +IHIDListener* IHIDListenerNew(CDeviceFinder& finder) +{ + // return new CHIDListenerWin32(finder); + return nullptr; +} + +} diff --git a/test/Win32Elevatedlauncher.c b/test/Win32Elevatedlauncher.c new file mode 100644 index 0000000..3f4445d --- /dev/null +++ b/test/Win32Elevatedlauncher.c @@ -0,0 +1,37 @@ + +#define _WIN32_LEAN_AND_MEAN 1 +#include +#include + +int genWin32ShellExecute(const wchar_t* AppFullPath, + const wchar_t* Verb, + const wchar_t* Params, + bool ShowAppWindow, + bool WaitToFinish) +{ + int Result = 0; + + // Setup the required structure + SHELLEXECUTEINFO ShExecInfo; + memset(&ShExecInfo, 0, sizeof(SHELLEXECUTEINFO)); + ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); + ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; + ShExecInfo.hwnd = NULL; + ShExecInfo.lpVerb = Verb; + ShExecInfo.lpFile = AppFullPath; + ShExecInfo.lpParameters = Params; + ShExecInfo.lpDirectory = NULL; + ShExecInfo.nShow = (ShowAppWindow ? SW_SHOW : SW_HIDE); + ShExecInfo.hInstApp = NULL; + + // Spawn the process + if (ShellExecuteEx(&ShExecInfo) == FALSE) + { + Result = -1; // Failed to execute process + } else if (WaitToFinish) + { + WaitForSingleObject(ShExecInfo.hProcess, INFINITE); + } + + return Result; +} diff --git a/test/main.cpp b/test/main.cpp index 3e3902a..0133f1c 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -4,8 +4,13 @@ #else #endif #include -#include #include +#if _WIN32 +#define _WIN32_LEAN_AND_MEAN 1 +#include +#else +#include +#endif namespace boo { @@ -53,23 +58,62 @@ public: } +#if _WIN32 +extern "C" int genWin32ShellExecute(const wchar_t* AppFullPath, + const wchar_t* Verb, + const wchar_t* Params, + bool ShowAppWindow, + bool WaitToFinish); +#include +static void scanWinUSB() +{ + struct wdi_device_info *device, *list; + struct wdi_options_create_list WDI_LIST_OPTS = + { + true, false, true + }; + int err = wdi_create_list(&list, &WDI_LIST_OPTS); + if (err == WDI_SUCCESS) + { + for (device = list; device != NULL; device = device->next) + { + if (device->vid == 0x57E && device->pid == 0x337 && + !strcmp(device->driver, "HidUsb")) + { + printf("GC adapter detected; installing driver\n"); + + } + } + wdi_destroy_list(list); + } +} +#endif + int main(int argc, char** argv) { +#if _WIN32 + scanWinUSB(); +#endif + boo::CTestDeviceFinder finder; finder.startScanning(); +#if 0 boo::IGraphicsContext* ctx = new boo::CGraphicsContext; if (ctx->create()) { } +#endif #if __APPLE__ CFRunLoopRun(); +#elif _WIN32 + while (true) {Sleep(1000);} #else while (true) {sleep(1);} #endif - delete ctx; + //delete ctx; return 0; } diff --git a/test/test.pri b/test/test.pri index a0eae95..07e0ac2 100644 --- a/test/test.pri +++ b/test/test.pri @@ -1,4 +1,5 @@ SOURCES += \ - $$PWD/main.cpp + $$PWD/main.cpp \ + $$PWD/Win32Elevatedlauncher.c -CONFIG += c++11 \ No newline at end of file +win32:SOURCES +=