chore: CRLF to LF
This commit is contained in:
+132
-132
@@ -1,132 +1,132 @@
|
||||
#include <initguid.h>
|
||||
|
||||
#include "hotplug.h"
|
||||
|
||||
#include <dbt.h>
|
||||
#include <devguid.h>
|
||||
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace rawinput {
|
||||
|
||||
DEFINE_GUID(GUID_HID, 0x4D1E55B2L, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);
|
||||
|
||||
HotplugManager::HotplugManager(RawInputManager *ri_mgr, HWND hWnd) : ri_mgr(ri_mgr) {
|
||||
|
||||
// init settings
|
||||
DEV_BROADCAST_DEVICEINTERFACE settings_hid {
|
||||
.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE),
|
||||
.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE,
|
||||
.dbcc_reserved = 0,
|
||||
.dbcc_classguid = GUID_HID,
|
||||
.dbcc_name = {0},
|
||||
};
|
||||
|
||||
// register notifications
|
||||
this->hotplug_hid = RegisterDeviceNotification(hWnd, &settings_hid, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
if (this->hotplug_hid == nullptr) {
|
||||
log_warning("hotplug", "failed to register HID notifications: {}", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
HotplugManager::~HotplugManager() {
|
||||
|
||||
// unregister notifications
|
||||
if (this->hotplug_hid != nullptr) {
|
||||
UnregisterDeviceNotification(this->hotplug_hid);
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK HotplugManager::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
|
||||
// check for device change
|
||||
if (msg == WM_DEVICECHANGE) {
|
||||
|
||||
// check type
|
||||
switch (wParam) {
|
||||
case DBT_DEVICEARRIVAL: {
|
||||
|
||||
// check arrived device type
|
||||
auto hdr = (DEV_BROADCAST_HDR*) lParam;
|
||||
switch (hdr->dbch_devicetype) {
|
||||
case DBT_DEVTYP_DEVICEINTERFACE: {
|
||||
auto dev = (const DEV_BROADCAST_DEVICEINTERFACE *) hdr;
|
||||
this->ri_mgr->devices_scan_midi();
|
||||
|
||||
// check if class is not HID
|
||||
if (memcmp(&dev->dbcc_classguid, &GUID_HID, sizeof(GUID_HID)) != 0)
|
||||
break;
|
||||
|
||||
// hotplug
|
||||
std::string name(dev->dbcc_name);
|
||||
this->ri_mgr->devices_scan_rawinput(name);
|
||||
this->ri_mgr->devices_register();
|
||||
|
||||
break;
|
||||
}
|
||||
case DBT_DEVTYP_VOLUME: {
|
||||
auto dev = (const DEV_BROADCAST_VOLUME *) hdr;
|
||||
auto unitmask = dev->dbcv_unitmask;
|
||||
|
||||
// check drive
|
||||
unsigned long bit;
|
||||
while (_BitScanForward(&bit, unitmask)) {
|
||||
|
||||
// remove drive from unitmask so it is not scanned again
|
||||
unitmask &= ~(1 << bit);
|
||||
|
||||
// convert bit to drive letter char
|
||||
char drive = 'A' + bit;
|
||||
log_info("hotplug", "detected volume arrival: {}", drive);
|
||||
|
||||
#ifndef SPICETOOLS_SPICECFG_STANDALONE
|
||||
// auto insert cards
|
||||
for (int player = 0; player <= 1; player++) {
|
||||
std::string path = to_string(drive) + ":\\card" + to_string(player) + ".txt";
|
||||
if (fileutils::file_exists(path)) {
|
||||
uint8_t card_data[8];
|
||||
if (eamuse_get_card(path, card_data)) {
|
||||
eamuse_card_insert(player, card_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return TRUE;
|
||||
}
|
||||
case DBT_DEVICEREMOVECOMPLETE: {
|
||||
|
||||
// check arrived device type
|
||||
auto hdr = (DEV_BROADCAST_HDR*) lParam;
|
||||
switch (hdr->dbch_devicetype) {
|
||||
case DBT_DEVTYP_DEVICEINTERFACE: {
|
||||
auto dev = (DEV_BROADCAST_DEVICEINTERFACE*) hdr;
|
||||
std::string name(dev->dbcc_name);
|
||||
|
||||
// destruct device
|
||||
this->ri_mgr->devices_remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return TRUE;
|
||||
}
|
||||
case 7: { // windows 10 reports this for MIDI?
|
||||
this->ri_mgr->devices_scan_midi();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
}
|
||||
#include <initguid.h>
|
||||
|
||||
#include "hotplug.h"
|
||||
|
||||
#include <dbt.h>
|
||||
#include <devguid.h>
|
||||
|
||||
#include "misc/eamuse.h"
|
||||
#include "rawinput.h"
|
||||
#include "util/fileutils.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace rawinput {
|
||||
|
||||
DEFINE_GUID(GUID_HID, 0x4D1E55B2L, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30);
|
||||
|
||||
HotplugManager::HotplugManager(RawInputManager *ri_mgr, HWND hWnd) : ri_mgr(ri_mgr) {
|
||||
|
||||
// init settings
|
||||
DEV_BROADCAST_DEVICEINTERFACE settings_hid {
|
||||
.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE),
|
||||
.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE,
|
||||
.dbcc_reserved = 0,
|
||||
.dbcc_classguid = GUID_HID,
|
||||
.dbcc_name = {0},
|
||||
};
|
||||
|
||||
// register notifications
|
||||
this->hotplug_hid = RegisterDeviceNotification(hWnd, &settings_hid, DEVICE_NOTIFY_WINDOW_HANDLE);
|
||||
if (this->hotplug_hid == nullptr) {
|
||||
log_warning("hotplug", "failed to register HID notifications: {}", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
HotplugManager::~HotplugManager() {
|
||||
|
||||
// unregister notifications
|
||||
if (this->hotplug_hid != nullptr) {
|
||||
UnregisterDeviceNotification(this->hotplug_hid);
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK HotplugManager::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
||||
|
||||
// check for device change
|
||||
if (msg == WM_DEVICECHANGE) {
|
||||
|
||||
// check type
|
||||
switch (wParam) {
|
||||
case DBT_DEVICEARRIVAL: {
|
||||
|
||||
// check arrived device type
|
||||
auto hdr = (DEV_BROADCAST_HDR*) lParam;
|
||||
switch (hdr->dbch_devicetype) {
|
||||
case DBT_DEVTYP_DEVICEINTERFACE: {
|
||||
auto dev = (const DEV_BROADCAST_DEVICEINTERFACE *) hdr;
|
||||
this->ri_mgr->devices_scan_midi();
|
||||
|
||||
// check if class is not HID
|
||||
if (memcmp(&dev->dbcc_classguid, &GUID_HID, sizeof(GUID_HID)) != 0)
|
||||
break;
|
||||
|
||||
// hotplug
|
||||
std::string name(dev->dbcc_name);
|
||||
this->ri_mgr->devices_scan_rawinput(name);
|
||||
this->ri_mgr->devices_register();
|
||||
|
||||
break;
|
||||
}
|
||||
case DBT_DEVTYP_VOLUME: {
|
||||
auto dev = (const DEV_BROADCAST_VOLUME *) hdr;
|
||||
auto unitmask = dev->dbcv_unitmask;
|
||||
|
||||
// check drive
|
||||
unsigned long bit;
|
||||
while (_BitScanForward(&bit, unitmask)) {
|
||||
|
||||
// remove drive from unitmask so it is not scanned again
|
||||
unitmask &= ~(1 << bit);
|
||||
|
||||
// convert bit to drive letter char
|
||||
char drive = 'A' + bit;
|
||||
log_info("hotplug", "detected volume arrival: {}", drive);
|
||||
|
||||
#ifndef SPICETOOLS_SPICECFG_STANDALONE
|
||||
// auto insert cards
|
||||
for (int player = 0; player <= 1; player++) {
|
||||
std::string path = to_string(drive) + ":\\card" + to_string(player) + ".txt";
|
||||
if (fileutils::file_exists(path)) {
|
||||
uint8_t card_data[8];
|
||||
if (eamuse_get_card(path, card_data)) {
|
||||
eamuse_card_insert(player, card_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return TRUE;
|
||||
}
|
||||
case DBT_DEVICEREMOVECOMPLETE: {
|
||||
|
||||
// check arrived device type
|
||||
auto hdr = (DEV_BROADCAST_HDR*) lParam;
|
||||
switch (hdr->dbch_devicetype) {
|
||||
case DBT_DEVTYP_DEVICEINTERFACE: {
|
||||
auto dev = (DEV_BROADCAST_DEVICEINTERFACE*) hdr;
|
||||
std::string name(dev->dbcc_name);
|
||||
|
||||
// destruct device
|
||||
this->ri_mgr->devices_remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
// success
|
||||
return TRUE;
|
||||
}
|
||||
case 7: { // windows 10 reports this for MIDI?
|
||||
this->ri_mgr->devices_scan_midi();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
||||
+20
-20
@@ -1,20 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace rawinput {
|
||||
|
||||
class RawInputManager;
|
||||
|
||||
class HotplugManager {
|
||||
private:
|
||||
RawInputManager *ri_mgr;
|
||||
HANDLE hotplug_hid;
|
||||
|
||||
public:
|
||||
HotplugManager(RawInputManager *ri_mgr, HWND hwnd);
|
||||
~HotplugManager();
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace rawinput {
|
||||
|
||||
class RawInputManager;
|
||||
|
||||
class HotplugManager {
|
||||
private:
|
||||
RawInputManager *ri_mgr;
|
||||
HANDLE hotplug_hid;
|
||||
|
||||
public:
|
||||
HotplugManager(RawInputManager *ri_mgr, HWND hwnd);
|
||||
~HotplugManager();
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||
};
|
||||
}
|
||||
|
||||
+394
-394
@@ -1,394 +1,394 @@
|
||||
#include "piuio.h"
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// it's 2019 and I'm writing code to avoid issues with Windows XP.
|
||||
static HINSTANCE WINUSB_INSTANCE = nullptr;
|
||||
static std::string WINUSB_NAME = "winusb.dll";
|
||||
|
||||
static std::string WinUsb_InitializeStr = "WinUsb_Initialize";
|
||||
typedef BOOL(__stdcall * WinUsb_Initialize_t)(
|
||||
_In_ HANDLE DeviceHandle,
|
||||
_Out_ PWINUSB_INTERFACE_HANDLE InterfaceHandle
|
||||
);
|
||||
|
||||
static std::string WinUsb_ControlTransferStr = "WinUsb_ControlTransfer";
|
||||
typedef BOOL(__stdcall * WinUsb_ControlTransfer_t)(
|
||||
_In_ WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
_In_ WINUSB_SETUP_PACKET SetupPacket,
|
||||
_Out_writes_bytes_to_opt_(BufferLength, *LengthTransferred) PUCHAR Buffer,
|
||||
_In_ ULONG BufferLength,
|
||||
_Out_opt_ PULONG LengthTransferred,
|
||||
_In_opt_ LPOVERLAPPED Overlapped
|
||||
);
|
||||
|
||||
static WinUsb_ControlTransfer_t pWinUsb_ControlTransfer = nullptr;
|
||||
static WinUsb_Initialize_t pWinUsb_Initialize = nullptr;
|
||||
|
||||
const string rawinput::PIUIO::LIGHT_NAMES[rawinput::PIUIO::PIUIO_MAX_NUM_OF_LIGHTS] = {
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"P2 Up",
|
||||
"P2 Down",
|
||||
"P2 Left",
|
||||
"P2 Right",
|
||||
"P2 Center",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Neon",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"P1 Up",
|
||||
"P1 Down",
|
||||
"P1 Left",
|
||||
"P1 Right",
|
||||
"P1 Center",
|
||||
"Marquee Upper Left",
|
||||
"Marquee Lower Right",
|
||||
"Marquee Lower Left",
|
||||
"Marquee Upper Right",
|
||||
"USB Enable",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown"
|
||||
};
|
||||
|
||||
rawinput::PIUIO::PIUIO(Device *device) {
|
||||
this->device = device;
|
||||
button_state = 0;
|
||||
light_data = 0;
|
||||
current_sensor = 0;
|
||||
prev_timeout = 0;
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::LoadWinUSBFunctions() {
|
||||
static bool functions_loaded = false;
|
||||
|
||||
if (functions_loaded)
|
||||
return true;
|
||||
|
||||
// see if we even have winusb available.
|
||||
WINUSB_INSTANCE = libutils::try_library(WINUSB_NAME);
|
||||
|
||||
// if windows didn't load it just now, then it's not on the system.
|
||||
if (WINUSB_INSTANCE != nullptr) {
|
||||
|
||||
// load winusb functions
|
||||
pWinUsb_Initialize = (WinUsb_Initialize_t) libutils::get_proc(
|
||||
WINUSB_INSTANCE, WinUsb_InitializeStr.c_str());
|
||||
pWinUsb_ControlTransfer = (WinUsb_ControlTransfer_t) libutils::get_proc(
|
||||
WINUSB_INSTANCE, WinUsb_ControlTransferStr.c_str());
|
||||
|
||||
// make sure they actually loaded.
|
||||
if(pWinUsb_Initialize != nullptr && pWinUsb_ControlTransfer!= nullptr)
|
||||
functions_loaded = true;
|
||||
} else {
|
||||
log_warning("piuio", "Could not load \'{}\', skipping...", WINUSB_NAME);
|
||||
}
|
||||
|
||||
return functions_loaded;
|
||||
}
|
||||
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA rawinput::PIUIO::GetDevicePath() {
|
||||
|
||||
// modified from https://forum.vellemanprojects.eu/t/k8090-how-to-identify-which-com-port-it-is-connected-to/3810
|
||||
// https://www.velleman.eu/images/tmp/usbfind.c
|
||||
|
||||
HDEVINFO hDevInfo;
|
||||
SP_DEVICE_INTERFACE_DATA DevIntfData;
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA DevIntfDetailData;
|
||||
SP_DEVINFO_DATA DevData;
|
||||
|
||||
DWORD dwSize, dwMemberIdx;
|
||||
|
||||
hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
|
||||
|
||||
if (hDevInfo != INVALID_HANDLE_VALUE) {
|
||||
DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
dwMemberIdx = 0;
|
||||
|
||||
SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE,
|
||||
dwMemberIdx, &DevIntfData);
|
||||
|
||||
while (GetLastError() != ERROR_NO_MORE_ITEMS) {
|
||||
DevData.cbSize = sizeof(DevData);
|
||||
|
||||
SetupDiGetDeviceInterfaceDetail(
|
||||
hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL);
|
||||
|
||||
DevIntfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
|
||||
DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
|
||||
if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData,
|
||||
DevIntfDetailData, dwSize, &dwSize, &DevData)) {
|
||||
if (strstr(DevIntfDetailData->DevicePath, VENDOR_PROD_ID) != 0) {
|
||||
return DevIntfDetailData;
|
||||
}
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, DevIntfDetailData);
|
||||
|
||||
// continue looping
|
||||
SetupDiEnumDeviceInterfaces(
|
||||
hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE, ++dwMemberIdx, &DevIntfData);
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(hDevInfo);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::IsPressed(int iKey) {
|
||||
if (iKey < PIUIO_MAX_NUM_OF_INPUTS) {
|
||||
|
||||
// logic high is pressed, logic low is released.
|
||||
return button_state & (1 << iKey);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::WasPressed(int iKey) {
|
||||
if (iKey < PIUIO_MAX_NUM_OF_INPUTS)
|
||||
return prev_button_state & (1 << iKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
void rawinput::PIUIO::SetUSBPortState(bool bState) {
|
||||
SetLight(USB_ENABLE_BIT, bState);
|
||||
}
|
||||
|
||||
void rawinput::PIUIO::SetLight(int iLight, bool bState) {
|
||||
if (iLight < PIUIO_MAX_NUM_OF_LIGHTS) {
|
||||
|
||||
// turn on a light by setting the bit, turn it off by clearing it.
|
||||
if (bState)
|
||||
light_data |= 1 << iLight;
|
||||
else
|
||||
light_data &= ~(1 << iLight);
|
||||
}
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::Open() {
|
||||
|
||||
// ensure the libs are loaded
|
||||
// if we can't load them, then we shouldn't exist.
|
||||
if (!LoadWinUSBFunctions())
|
||||
return false;
|
||||
|
||||
bool bResult = false;
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA dev_detail;
|
||||
|
||||
dev_detail = GetDevicePath();
|
||||
|
||||
if (dev_detail == 0) {
|
||||
HeapFree(GetProcessHeap(), 0, dev_detail);
|
||||
return false;
|
||||
}
|
||||
|
||||
device_handle = CreateFile(dev_detail->DevicePath,
|
||||
GENERIC_WRITE | GENERIC_READ,
|
||||
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
||||
NULL
|
||||
);
|
||||
|
||||
// no longer need dev_detail
|
||||
HeapFree(GetProcessHeap(), 0, dev_detail);
|
||||
|
||||
if (device_handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
if (pWinUsb_Initialize != nullptr)
|
||||
bResult = pWinUsb_Initialize(device_handle, &win_usb_handle);
|
||||
|
||||
if (!bResult) {
|
||||
CloseHandle(device_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
uint32_t rawinput::PIUIO::Read() {
|
||||
|
||||
WINUSB_SETUP_PACKET SetupPacket;
|
||||
ZeroMemory(&SetupPacket, sizeof(WINUSB_SETUP_PACKET));
|
||||
ULONG cbSent = 0;
|
||||
bool bResult = false;
|
||||
|
||||
// create the setup packet
|
||||
SetupPacket.RequestType = 0x40 | 0x80; // EndPoint In, RequestType Vendor
|
||||
SetupPacket.Request = PIUIO_CTL_REQ;
|
||||
SetupPacket.Value = 0;
|
||||
SetupPacket.Index = 0;
|
||||
SetupPacket.Length = PACKET_SIZE_BYTES;
|
||||
|
||||
// ensure we loaded the pointer
|
||||
unsigned char buffer[PACKET_SIZE_BYTES] {};
|
||||
if (pWinUsb_ControlTransfer != nullptr)
|
||||
bResult = pWinUsb_ControlTransfer(win_usb_handle, SetupPacket, buffer, PACKET_SIZE_BYTES, &cbSent, 0);
|
||||
|
||||
if (!bResult)
|
||||
log_warning("piuio", "read error");
|
||||
|
||||
return Deserialize(buffer);
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::Write(uint32_t p_Data) {
|
||||
|
||||
WINUSB_SETUP_PACKET SetupPacket;
|
||||
ZeroMemory(&SetupPacket, sizeof(WINUSB_SETUP_PACKET));
|
||||
ULONG cbSent = 0;
|
||||
bool bResult = false;
|
||||
|
||||
// create the setup packet
|
||||
SetupPacket.RequestType = 0x40; //EndPoint Out, RequestType Vendor
|
||||
SetupPacket.Request = PIUIO_CTL_REQ;
|
||||
SetupPacket.Value = 0;
|
||||
SetupPacket.Index = 0;
|
||||
SetupPacket.Length = PACKET_SIZE_BYTES;
|
||||
|
||||
// ensure we loaded the pointer
|
||||
if (pWinUsb_ControlTransfer != nullptr) {
|
||||
bResult = pWinUsb_ControlTransfer(
|
||||
win_usb_handle,
|
||||
SetupPacket,
|
||||
Serialize(light_data),
|
||||
PACKET_SIZE_BYTES,
|
||||
&cbSent,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
if (!bResult)
|
||||
log_warning("piuio", "write error");
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
unsigned char * rawinput::PIUIO::Serialize(uint32_t p_Data) {
|
||||
static unsigned char buffer[PACKET_SIZE_BYTES];
|
||||
|
||||
buffer[0] = p_Data;
|
||||
buffer[1] = p_Data >> 8;
|
||||
buffer[2] = p_Data >> 16;
|
||||
buffer[3] = p_Data >> 24;
|
||||
|
||||
// these bits don't matter for the piuio
|
||||
buffer[4] = 0xFF;
|
||||
buffer[5] = 0xFF;
|
||||
buffer[6] = 0xFF;
|
||||
buffer[7] = 0xFF;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
uint32_t rawinput::PIUIO::Deserialize(unsigned char * p_Data) {
|
||||
uint32_t result;
|
||||
|
||||
result = p_Data[0];
|
||||
result |= p_Data[1] << 8;
|
||||
result |= p_Data[2] << 16;
|
||||
result |= p_Data[3] << 24;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void rawinput::PIUIO::IOThread() {
|
||||
while (is_connected)
|
||||
UpdateSingleButtonState();
|
||||
}
|
||||
|
||||
void rawinput::PIUIO::UpdateSingleButtonState() {
|
||||
|
||||
// write a set of sensors to request
|
||||
light_data &= 0xFFFCFFFC;
|
||||
light_data |= (current_sensor | (current_sensor << 16));
|
||||
|
||||
// request this set of sensors
|
||||
// this also updates the light status
|
||||
this->Write(light_data);
|
||||
|
||||
// read from this set of sensors
|
||||
input_data[current_sensor] = this->Read();
|
||||
|
||||
// piuio opens high; invert the input
|
||||
input_data[current_sensor] = ~input_data[current_sensor];
|
||||
|
||||
// update high values ASAP, that's when the button is pressed.
|
||||
// see: https://github.com/djpohly/piuio#usb-protocol-and-multiplexer
|
||||
auto button_state_new = button_state | input_data[current_sensor];
|
||||
if (button_state != button_state_new) {
|
||||
this->device->mutex->lock();
|
||||
button_state |= button_state_new;
|
||||
this->device->updated = true;
|
||||
this->device->mutex->unlock();
|
||||
}
|
||||
|
||||
if (current_sensor >= SENSOR_NUM - 1) {
|
||||
uint32_t temp_btn_state = 0;
|
||||
|
||||
// we have done a full sensor arrangement, let's see if the panel has been pulled away from.
|
||||
// combine all the input.
|
||||
for (int i = 0; i < 4; ++i)
|
||||
temp_btn_state |= input_data[i];
|
||||
|
||||
// only store every 255th previous value since we are polling very quickly.
|
||||
// only used by spicecfg to see if a user pressed a button, so not very important.
|
||||
// unsigned ints rollover
|
||||
prev_timeout++;
|
||||
if (prev_timeout == 0)
|
||||
prev_button_state = button_state;
|
||||
|
||||
if (button_state != temp_btn_state) {
|
||||
this->device->mutex->lock();
|
||||
button_state = temp_btn_state;
|
||||
this->device->updated = true;
|
||||
this->device->mutex->unlock();
|
||||
}
|
||||
current_sensor = 0;
|
||||
} else {
|
||||
current_sensor++;
|
||||
}
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::Init() {
|
||||
is_connected = Open();
|
||||
|
||||
if (is_connected) {
|
||||
|
||||
// force the USB lines to be always on.
|
||||
SetUSBPortState(true);
|
||||
|
||||
IOThreadObject = new std::thread(&rawinput::PIUIO::IOThread, this);
|
||||
}
|
||||
|
||||
return is_connected;
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::Stop() {
|
||||
is_connected = false;
|
||||
|
||||
if (IOThreadObject != NULL) {
|
||||
IOThreadObject->join();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
rawinput::PIUIO::~PIUIO() {
|
||||
Stop();
|
||||
}
|
||||
#include "piuio.h"
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
using namespace std;
|
||||
|
||||
// it's 2019 and I'm writing code to avoid issues with Windows XP.
|
||||
static HINSTANCE WINUSB_INSTANCE = nullptr;
|
||||
static std::string WINUSB_NAME = "winusb.dll";
|
||||
|
||||
static std::string WinUsb_InitializeStr = "WinUsb_Initialize";
|
||||
typedef BOOL(__stdcall * WinUsb_Initialize_t)(
|
||||
_In_ HANDLE DeviceHandle,
|
||||
_Out_ PWINUSB_INTERFACE_HANDLE InterfaceHandle
|
||||
);
|
||||
|
||||
static std::string WinUsb_ControlTransferStr = "WinUsb_ControlTransfer";
|
||||
typedef BOOL(__stdcall * WinUsb_ControlTransfer_t)(
|
||||
_In_ WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
_In_ WINUSB_SETUP_PACKET SetupPacket,
|
||||
_Out_writes_bytes_to_opt_(BufferLength, *LengthTransferred) PUCHAR Buffer,
|
||||
_In_ ULONG BufferLength,
|
||||
_Out_opt_ PULONG LengthTransferred,
|
||||
_In_opt_ LPOVERLAPPED Overlapped
|
||||
);
|
||||
|
||||
static WinUsb_ControlTransfer_t pWinUsb_ControlTransfer = nullptr;
|
||||
static WinUsb_Initialize_t pWinUsb_Initialize = nullptr;
|
||||
|
||||
const string rawinput::PIUIO::LIGHT_NAMES[rawinput::PIUIO::PIUIO_MAX_NUM_OF_LIGHTS] = {
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"P2 Up",
|
||||
"P2 Down",
|
||||
"P2 Left",
|
||||
"P2 Right",
|
||||
"P2 Center",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Neon",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"P1 Up",
|
||||
"P1 Down",
|
||||
"P1 Left",
|
||||
"P1 Right",
|
||||
"P1 Center",
|
||||
"Marquee Upper Left",
|
||||
"Marquee Lower Right",
|
||||
"Marquee Lower Left",
|
||||
"Marquee Upper Right",
|
||||
"USB Enable",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Unknown"
|
||||
};
|
||||
|
||||
rawinput::PIUIO::PIUIO(Device *device) {
|
||||
this->device = device;
|
||||
button_state = 0;
|
||||
light_data = 0;
|
||||
current_sensor = 0;
|
||||
prev_timeout = 0;
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::LoadWinUSBFunctions() {
|
||||
static bool functions_loaded = false;
|
||||
|
||||
if (functions_loaded)
|
||||
return true;
|
||||
|
||||
// see if we even have winusb available.
|
||||
WINUSB_INSTANCE = libutils::try_library(WINUSB_NAME);
|
||||
|
||||
// if windows didn't load it just now, then it's not on the system.
|
||||
if (WINUSB_INSTANCE != nullptr) {
|
||||
|
||||
// load winusb functions
|
||||
pWinUsb_Initialize = (WinUsb_Initialize_t) libutils::get_proc(
|
||||
WINUSB_INSTANCE, WinUsb_InitializeStr.c_str());
|
||||
pWinUsb_ControlTransfer = (WinUsb_ControlTransfer_t) libutils::get_proc(
|
||||
WINUSB_INSTANCE, WinUsb_ControlTransferStr.c_str());
|
||||
|
||||
// make sure they actually loaded.
|
||||
if(pWinUsb_Initialize != nullptr && pWinUsb_ControlTransfer!= nullptr)
|
||||
functions_loaded = true;
|
||||
} else {
|
||||
log_warning("piuio", "Could not load \'{}\', skipping...", WINUSB_NAME);
|
||||
}
|
||||
|
||||
return functions_loaded;
|
||||
}
|
||||
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA rawinput::PIUIO::GetDevicePath() {
|
||||
|
||||
// modified from https://forum.vellemanprojects.eu/t/k8090-how-to-identify-which-com-port-it-is-connected-to/3810
|
||||
// https://www.velleman.eu/images/tmp/usbfind.c
|
||||
|
||||
HDEVINFO hDevInfo;
|
||||
SP_DEVICE_INTERFACE_DATA DevIntfData;
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA DevIntfDetailData;
|
||||
SP_DEVINFO_DATA DevData;
|
||||
|
||||
DWORD dwSize, dwMemberIdx;
|
||||
|
||||
hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, 0, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
|
||||
|
||||
if (hDevInfo != INVALID_HANDLE_VALUE) {
|
||||
DevIntfData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
dwMemberIdx = 0;
|
||||
|
||||
SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE,
|
||||
dwMemberIdx, &DevIntfData);
|
||||
|
||||
while (GetLastError() != ERROR_NO_MORE_ITEMS) {
|
||||
DevData.cbSize = sizeof(DevData);
|
||||
|
||||
SetupDiGetDeviceInterfaceDetail(
|
||||
hDevInfo, &DevIntfData, NULL, 0, &dwSize, NULL);
|
||||
|
||||
DevIntfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
|
||||
DevIntfDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
|
||||
if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &DevIntfData,
|
||||
DevIntfDetailData, dwSize, &dwSize, &DevData)) {
|
||||
if (strstr(DevIntfDetailData->DevicePath, VENDOR_PROD_ID) != 0) {
|
||||
return DevIntfDetailData;
|
||||
}
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, DevIntfDetailData);
|
||||
|
||||
// continue looping
|
||||
SetupDiEnumDeviceInterfaces(
|
||||
hDevInfo, NULL, &GUID_DEVINTERFACE_USB_DEVICE, ++dwMemberIdx, &DevIntfData);
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(hDevInfo);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::IsPressed(int iKey) {
|
||||
if (iKey < PIUIO_MAX_NUM_OF_INPUTS) {
|
||||
|
||||
// logic high is pressed, logic low is released.
|
||||
return button_state & (1 << iKey);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::WasPressed(int iKey) {
|
||||
if (iKey < PIUIO_MAX_NUM_OF_INPUTS)
|
||||
return prev_button_state & (1 << iKey);
|
||||
return false;
|
||||
}
|
||||
|
||||
void rawinput::PIUIO::SetUSBPortState(bool bState) {
|
||||
SetLight(USB_ENABLE_BIT, bState);
|
||||
}
|
||||
|
||||
void rawinput::PIUIO::SetLight(int iLight, bool bState) {
|
||||
if (iLight < PIUIO_MAX_NUM_OF_LIGHTS) {
|
||||
|
||||
// turn on a light by setting the bit, turn it off by clearing it.
|
||||
if (bState)
|
||||
light_data |= 1 << iLight;
|
||||
else
|
||||
light_data &= ~(1 << iLight);
|
||||
}
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::Open() {
|
||||
|
||||
// ensure the libs are loaded
|
||||
// if we can't load them, then we shouldn't exist.
|
||||
if (!LoadWinUSBFunctions())
|
||||
return false;
|
||||
|
||||
bool bResult = false;
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA dev_detail;
|
||||
|
||||
dev_detail = GetDevicePath();
|
||||
|
||||
if (dev_detail == 0) {
|
||||
HeapFree(GetProcessHeap(), 0, dev_detail);
|
||||
return false;
|
||||
}
|
||||
|
||||
device_handle = CreateFile(dev_detail->DevicePath,
|
||||
GENERIC_WRITE | GENERIC_READ,
|
||||
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
|
||||
NULL
|
||||
);
|
||||
|
||||
// no longer need dev_detail
|
||||
HeapFree(GetProcessHeap(), 0, dev_detail);
|
||||
|
||||
if (device_handle == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
if (pWinUsb_Initialize != nullptr)
|
||||
bResult = pWinUsb_Initialize(device_handle, &win_usb_handle);
|
||||
|
||||
if (!bResult) {
|
||||
CloseHandle(device_handle);
|
||||
return false;
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
uint32_t rawinput::PIUIO::Read() {
|
||||
|
||||
WINUSB_SETUP_PACKET SetupPacket;
|
||||
ZeroMemory(&SetupPacket, sizeof(WINUSB_SETUP_PACKET));
|
||||
ULONG cbSent = 0;
|
||||
bool bResult = false;
|
||||
|
||||
// create the setup packet
|
||||
SetupPacket.RequestType = 0x40 | 0x80; // EndPoint In, RequestType Vendor
|
||||
SetupPacket.Request = PIUIO_CTL_REQ;
|
||||
SetupPacket.Value = 0;
|
||||
SetupPacket.Index = 0;
|
||||
SetupPacket.Length = PACKET_SIZE_BYTES;
|
||||
|
||||
// ensure we loaded the pointer
|
||||
unsigned char buffer[PACKET_SIZE_BYTES] {};
|
||||
if (pWinUsb_ControlTransfer != nullptr)
|
||||
bResult = pWinUsb_ControlTransfer(win_usb_handle, SetupPacket, buffer, PACKET_SIZE_BYTES, &cbSent, 0);
|
||||
|
||||
if (!bResult)
|
||||
log_warning("piuio", "read error");
|
||||
|
||||
return Deserialize(buffer);
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::Write(uint32_t p_Data) {
|
||||
|
||||
WINUSB_SETUP_PACKET SetupPacket;
|
||||
ZeroMemory(&SetupPacket, sizeof(WINUSB_SETUP_PACKET));
|
||||
ULONG cbSent = 0;
|
||||
bool bResult = false;
|
||||
|
||||
// create the setup packet
|
||||
SetupPacket.RequestType = 0x40; //EndPoint Out, RequestType Vendor
|
||||
SetupPacket.Request = PIUIO_CTL_REQ;
|
||||
SetupPacket.Value = 0;
|
||||
SetupPacket.Index = 0;
|
||||
SetupPacket.Length = PACKET_SIZE_BYTES;
|
||||
|
||||
// ensure we loaded the pointer
|
||||
if (pWinUsb_ControlTransfer != nullptr) {
|
||||
bResult = pWinUsb_ControlTransfer(
|
||||
win_usb_handle,
|
||||
SetupPacket,
|
||||
Serialize(light_data),
|
||||
PACKET_SIZE_BYTES,
|
||||
&cbSent,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
if (!bResult)
|
||||
log_warning("piuio", "write error");
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
unsigned char * rawinput::PIUIO::Serialize(uint32_t p_Data) {
|
||||
static unsigned char buffer[PACKET_SIZE_BYTES];
|
||||
|
||||
buffer[0] = p_Data;
|
||||
buffer[1] = p_Data >> 8;
|
||||
buffer[2] = p_Data >> 16;
|
||||
buffer[3] = p_Data >> 24;
|
||||
|
||||
// these bits don't matter for the piuio
|
||||
buffer[4] = 0xFF;
|
||||
buffer[5] = 0xFF;
|
||||
buffer[6] = 0xFF;
|
||||
buffer[7] = 0xFF;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
uint32_t rawinput::PIUIO::Deserialize(unsigned char * p_Data) {
|
||||
uint32_t result;
|
||||
|
||||
result = p_Data[0];
|
||||
result |= p_Data[1] << 8;
|
||||
result |= p_Data[2] << 16;
|
||||
result |= p_Data[3] << 24;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void rawinput::PIUIO::IOThread() {
|
||||
while (is_connected)
|
||||
UpdateSingleButtonState();
|
||||
}
|
||||
|
||||
void rawinput::PIUIO::UpdateSingleButtonState() {
|
||||
|
||||
// write a set of sensors to request
|
||||
light_data &= 0xFFFCFFFC;
|
||||
light_data |= (current_sensor | (current_sensor << 16));
|
||||
|
||||
// request this set of sensors
|
||||
// this also updates the light status
|
||||
this->Write(light_data);
|
||||
|
||||
// read from this set of sensors
|
||||
input_data[current_sensor] = this->Read();
|
||||
|
||||
// piuio opens high; invert the input
|
||||
input_data[current_sensor] = ~input_data[current_sensor];
|
||||
|
||||
// update high values ASAP, that's when the button is pressed.
|
||||
// see: https://github.com/djpohly/piuio#usb-protocol-and-multiplexer
|
||||
auto button_state_new = button_state | input_data[current_sensor];
|
||||
if (button_state != button_state_new) {
|
||||
this->device->mutex->lock();
|
||||
button_state |= button_state_new;
|
||||
this->device->updated = true;
|
||||
this->device->mutex->unlock();
|
||||
}
|
||||
|
||||
if (current_sensor >= SENSOR_NUM - 1) {
|
||||
uint32_t temp_btn_state = 0;
|
||||
|
||||
// we have done a full sensor arrangement, let's see if the panel has been pulled away from.
|
||||
// combine all the input.
|
||||
for (int i = 0; i < 4; ++i)
|
||||
temp_btn_state |= input_data[i];
|
||||
|
||||
// only store every 255th previous value since we are polling very quickly.
|
||||
// only used by spicecfg to see if a user pressed a button, so not very important.
|
||||
// unsigned ints rollover
|
||||
prev_timeout++;
|
||||
if (prev_timeout == 0)
|
||||
prev_button_state = button_state;
|
||||
|
||||
if (button_state != temp_btn_state) {
|
||||
this->device->mutex->lock();
|
||||
button_state = temp_btn_state;
|
||||
this->device->updated = true;
|
||||
this->device->mutex->unlock();
|
||||
}
|
||||
current_sensor = 0;
|
||||
} else {
|
||||
current_sensor++;
|
||||
}
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::Init() {
|
||||
is_connected = Open();
|
||||
|
||||
if (is_connected) {
|
||||
|
||||
// force the USB lines to be always on.
|
||||
SetUSBPortState(true);
|
||||
|
||||
IOThreadObject = new std::thread(&rawinput::PIUIO::IOThread, this);
|
||||
}
|
||||
|
||||
return is_connected;
|
||||
}
|
||||
|
||||
bool rawinput::PIUIO::Stop() {
|
||||
is_connected = false;
|
||||
|
||||
if (IOThreadObject != NULL) {
|
||||
IOThreadObject->join();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
rawinput::PIUIO::~PIUIO() {
|
||||
Stop();
|
||||
}
|
||||
|
||||
+78
-78
@@ -1,78 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <windows.h>
|
||||
#include <winusb.h>
|
||||
#include <setupapi.h>
|
||||
#include <string>
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "device.h"
|
||||
|
||||
// This is the GUID for the USB device class
|
||||
DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
|
||||
|
||||
#pragma comment (lib, "setupapi.lib")
|
||||
#pragma comment (lib , "winusb.lib" )
|
||||
|
||||
namespace rawinput {
|
||||
|
||||
class PIUIO
|
||||
{
|
||||
public:
|
||||
|
||||
static const size_t PACKET_SIZE_BYTES = 8;
|
||||
static const size_t SENSOR_NUM = 4;
|
||||
static const size_t USB_ENABLE_BIT = 27;
|
||||
static const UCHAR PIUIO_CTL_REQ = 0xAE;
|
||||
const char *VENDOR_PROD_ID = "vid_0547&pid_1002";
|
||||
|
||||
const static std::string LIGHT_NAMES[];
|
||||
static const int PIUIO_MAX_NUM_OF_LIGHTS = 32;
|
||||
static const int PIUIO_MAX_NUM_OF_INPUTS = 32;
|
||||
|
||||
bool is_connected;
|
||||
Device *device;
|
||||
|
||||
bool IsPressed(int iKey);
|
||||
bool WasPressed(int iKey);
|
||||
void SetLight(int iLight, bool bState);
|
||||
|
||||
PIUIO(Device* device);
|
||||
virtual ~PIUIO();
|
||||
|
||||
bool Init();
|
||||
bool Stop();
|
||||
|
||||
void UpdateSingleButtonState();
|
||||
|
||||
void SetUSBPortState(bool bState);
|
||||
|
||||
private:
|
||||
bool LoadWinUSBFunctions();
|
||||
|
||||
uint32_t light_data;
|
||||
uint32_t input_data[SENSOR_NUM];
|
||||
uint8_t current_sensor;
|
||||
uint32_t button_state, prev_button_state;
|
||||
|
||||
uint8_t prev_timeout;
|
||||
|
||||
LPCWSTR device_path;
|
||||
WINUSB_INTERFACE_HANDLE win_usb_handle = INVALID_HANDLE_VALUE;
|
||||
HANDLE device_handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
bool Open();
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA GetDevicePath();
|
||||
|
||||
void IOThread();
|
||||
|
||||
uint32_t Read();
|
||||
bool Write(uint32_t p_Data);
|
||||
|
||||
unsigned char * Serialize(uint32_t p_Data);
|
||||
uint32_t Deserialize(unsigned char * p_Data);
|
||||
|
||||
std::thread* IOThreadObject;
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <windows.h>
|
||||
#include <winusb.h>
|
||||
#include <setupapi.h>
|
||||
#include <string>
|
||||
#include "util/libutils.h"
|
||||
#include "util/logging.h"
|
||||
#include "device.h"
|
||||
|
||||
// This is the GUID for the USB device class
|
||||
DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
|
||||
|
||||
#pragma comment (lib, "setupapi.lib")
|
||||
#pragma comment (lib , "winusb.lib" )
|
||||
|
||||
namespace rawinput {
|
||||
|
||||
class PIUIO
|
||||
{
|
||||
public:
|
||||
|
||||
static const size_t PACKET_SIZE_BYTES = 8;
|
||||
static const size_t SENSOR_NUM = 4;
|
||||
static const size_t USB_ENABLE_BIT = 27;
|
||||
static const UCHAR PIUIO_CTL_REQ = 0xAE;
|
||||
const char *VENDOR_PROD_ID = "vid_0547&pid_1002";
|
||||
|
||||
const static std::string LIGHT_NAMES[];
|
||||
static const int PIUIO_MAX_NUM_OF_LIGHTS = 32;
|
||||
static const int PIUIO_MAX_NUM_OF_INPUTS = 32;
|
||||
|
||||
bool is_connected;
|
||||
Device *device;
|
||||
|
||||
bool IsPressed(int iKey);
|
||||
bool WasPressed(int iKey);
|
||||
void SetLight(int iLight, bool bState);
|
||||
|
||||
PIUIO(Device* device);
|
||||
virtual ~PIUIO();
|
||||
|
||||
bool Init();
|
||||
bool Stop();
|
||||
|
||||
void UpdateSingleButtonState();
|
||||
|
||||
void SetUSBPortState(bool bState);
|
||||
|
||||
private:
|
||||
bool LoadWinUSBFunctions();
|
||||
|
||||
uint32_t light_data;
|
||||
uint32_t input_data[SENSOR_NUM];
|
||||
uint8_t current_sensor;
|
||||
uint32_t button_state, prev_button_state;
|
||||
|
||||
uint8_t prev_timeout;
|
||||
|
||||
LPCWSTR device_path;
|
||||
WINUSB_INTERFACE_HANDLE win_usb_handle = INVALID_HANDLE_VALUE;
|
||||
HANDLE device_handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
bool Open();
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA GetDevicePath();
|
||||
|
||||
void IOThread();
|
||||
|
||||
uint32_t Read();
|
||||
bool Write(uint32_t p_Data);
|
||||
|
||||
unsigned char * Serialize(uint32_t p_Data);
|
||||
uint32_t Deserialize(unsigned char * p_Data);
|
||||
|
||||
std::thread* IOThreadObject;
|
||||
};
|
||||
}
|
||||
|
||||
+178
-178
@@ -1,178 +1,178 @@
|
||||
#include "sextet.h"
|
||||
#include <cstring>
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include "util/logging.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
const string rawinput::SextetDevice::LIGHT_NAMES[LIGHT_COUNT] = {
|
||||
"Neon",
|
||||
|
||||
"P1 Halogen Upper",
|
||||
"P1 Halogen Lower",
|
||||
"P2 Halogen Upper",
|
||||
"P2 Halogen Lower",
|
||||
|
||||
"P1 Button",
|
||||
"P2 Button",
|
||||
|
||||
"P1 Foot Up",
|
||||
"P1 Foot Down",
|
||||
"P1 Foot Left",
|
||||
"P1 Foot Right",
|
||||
|
||||
"P2 Foot Up",
|
||||
"P2 Foot Down",
|
||||
"P2 Foot Left",
|
||||
"P2 Foot Right"
|
||||
};
|
||||
|
||||
inline void rawinput::SextetDevice::fill_packet(uint8_t *buffer, bool *light_state) {
|
||||
|
||||
/*
|
||||
* Refer to this doucment for more info
|
||||
* https://github.com/stepmania/stepmania/blob/master/src/arch/Lights/LightsDriver_SextetStream.md#bit-meanings
|
||||
*/
|
||||
|
||||
buffer[0] = 0xC0 | (uint8_t) (
|
||||
(light_state[SextetLights::BASS_NEON] << 5) |
|
||||
(light_state[SextetLights::BASS_NEON] << 4) |
|
||||
(light_state[SextetLights::MARQUEE_P2_LOWER] << 3) |
|
||||
(light_state[SextetLights::MARQUEE_P1_LOWER] << 2) |
|
||||
(light_state[SextetLights::MARQUEE_P2_UPPER] << 1) |
|
||||
(light_state[SextetLights::MARQUEE_P1_UPPER] << 0)
|
||||
);
|
||||
|
||||
buffer[1] = 0xC0 | (uint8_t) (
|
||||
(light_state[SextetLights::P1_MENU] << 5) |
|
||||
(light_state[SextetLights::P1_MENU] << 4)
|
||||
);
|
||||
|
||||
buffer[2] = 0xC0;
|
||||
|
||||
buffer[3] = 0xC0 | (uint8_t) (
|
||||
(light_state[SextetLights::P1_DOWN] << 3) |
|
||||
(light_state[SextetLights::P1_UP] << 2) |
|
||||
(light_state[SextetLights::P1_RIGHT] << 1) |
|
||||
(light_state[SextetLights::P1_LEFT] << 0)
|
||||
);
|
||||
|
||||
buffer[4] = 0xC0;
|
||||
buffer[5] = 0xC0;
|
||||
buffer[6] = 0xC0;
|
||||
|
||||
buffer[7] = 0xC0 | (uint8_t) (
|
||||
(light_state[SextetLights::P2_MENU] << 5) |
|
||||
(light_state[SextetLights::P2_MENU] << 4)
|
||||
);
|
||||
|
||||
buffer[8] = 0xC0;
|
||||
|
||||
buffer[9] = 0xC0 | (uint8_t) (
|
||||
(light_state[SextetLights::P2_DOWN] << 3) |
|
||||
(light_state[SextetLights::P2_UP] << 2) |
|
||||
(light_state[SextetLights::P2_RIGHT] << 1) |
|
||||
(light_state[SextetLights::P2_LEFT] << 0)
|
||||
);
|
||||
|
||||
buffer[10] = 0xC0;
|
||||
buffer[11] = 0xC0;
|
||||
buffer[12] = 0xC0;
|
||||
|
||||
// terminate with LF
|
||||
buffer[13] = '\n';
|
||||
}
|
||||
|
||||
rawinput::SextetDevice::SextetDevice(std::string device_name) {
|
||||
this->device_name = device_name;
|
||||
is_connected = false;
|
||||
all_off();
|
||||
}
|
||||
|
||||
rawinput::SextetDevice::~SextetDevice() {
|
||||
disconnect();
|
||||
}
|
||||
|
||||
void rawinput::SextetDevice::set_all(bool state) {
|
||||
for (int i = 0; i < LIGHT_COUNT; i++)
|
||||
light_state[i] = state;
|
||||
}
|
||||
|
||||
void rawinput::SextetDevice::all_on() {
|
||||
set_all(true);
|
||||
}
|
||||
|
||||
void rawinput::SextetDevice::all_off() {
|
||||
set_all(false);
|
||||
}
|
||||
|
||||
bool rawinput::SextetDevice::connect() {
|
||||
|
||||
// open device
|
||||
device = CreateFileA(
|
||||
this->device_name.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
0
|
||||
);
|
||||
|
||||
// check device
|
||||
if (device == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
// get comm state
|
||||
DCB dcb;
|
||||
SecureZeroMemory(&dcb, sizeof(DCB));
|
||||
dcb.DCBlength = sizeof(DCB);
|
||||
if (!GetCommState(device, &dcb)) {
|
||||
log_warning("sextet", "GetCommState failed: 0x{:08x}", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// set comm state
|
||||
dcb.BaudRate = 115200;
|
||||
dcb.ByteSize = 8;
|
||||
dcb.Parity = NOPARITY;
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
if (!SetCommState(device, &dcb)) {
|
||||
log_warning("sextet", "SetCommState failed: 0x{:08x}", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// success
|
||||
is_connected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rawinput::SextetDevice::disconnect() {
|
||||
return CloseHandle(device);
|
||||
}
|
||||
|
||||
bool rawinput::SextetDevice::push_light_state() {
|
||||
|
||||
// build packet
|
||||
uint8_t buffer[FULL_SEXTET_COUNT];
|
||||
this->fill_packet(&(buffer[0]), &(light_state[0]));
|
||||
|
||||
// write data to device
|
||||
DWORD bytes_written;
|
||||
WriteFile(
|
||||
device,
|
||||
buffer,
|
||||
FULL_SEXTET_COUNT,
|
||||
&bytes_written,
|
||||
NULL
|
||||
);
|
||||
|
||||
// verify written bytes count
|
||||
return bytes_written == FULL_SEXTET_COUNT;
|
||||
}
|
||||
#include "sextet.h"
|
||||
#include <cstring>
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include "util/logging.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
const string rawinput::SextetDevice::LIGHT_NAMES[LIGHT_COUNT] = {
|
||||
"Neon",
|
||||
|
||||
"P1 Halogen Upper",
|
||||
"P1 Halogen Lower",
|
||||
"P2 Halogen Upper",
|
||||
"P2 Halogen Lower",
|
||||
|
||||
"P1 Button",
|
||||
"P2 Button",
|
||||
|
||||
"P1 Foot Up",
|
||||
"P1 Foot Down",
|
||||
"P1 Foot Left",
|
||||
"P1 Foot Right",
|
||||
|
||||
"P2 Foot Up",
|
||||
"P2 Foot Down",
|
||||
"P2 Foot Left",
|
||||
"P2 Foot Right"
|
||||
};
|
||||
|
||||
inline void rawinput::SextetDevice::fill_packet(uint8_t *buffer, bool *light_state) {
|
||||
|
||||
/*
|
||||
* Refer to this doucment for more info
|
||||
* https://github.com/stepmania/stepmania/blob/master/src/arch/Lights/LightsDriver_SextetStream.md#bit-meanings
|
||||
*/
|
||||
|
||||
buffer[0] = 0xC0 | (uint8_t) (
|
||||
(light_state[SextetLights::BASS_NEON] << 5) |
|
||||
(light_state[SextetLights::BASS_NEON] << 4) |
|
||||
(light_state[SextetLights::MARQUEE_P2_LOWER] << 3) |
|
||||
(light_state[SextetLights::MARQUEE_P1_LOWER] << 2) |
|
||||
(light_state[SextetLights::MARQUEE_P2_UPPER] << 1) |
|
||||
(light_state[SextetLights::MARQUEE_P1_UPPER] << 0)
|
||||
);
|
||||
|
||||
buffer[1] = 0xC0 | (uint8_t) (
|
||||
(light_state[SextetLights::P1_MENU] << 5) |
|
||||
(light_state[SextetLights::P1_MENU] << 4)
|
||||
);
|
||||
|
||||
buffer[2] = 0xC0;
|
||||
|
||||
buffer[3] = 0xC0 | (uint8_t) (
|
||||
(light_state[SextetLights::P1_DOWN] << 3) |
|
||||
(light_state[SextetLights::P1_UP] << 2) |
|
||||
(light_state[SextetLights::P1_RIGHT] << 1) |
|
||||
(light_state[SextetLights::P1_LEFT] << 0)
|
||||
);
|
||||
|
||||
buffer[4] = 0xC0;
|
||||
buffer[5] = 0xC0;
|
||||
buffer[6] = 0xC0;
|
||||
|
||||
buffer[7] = 0xC0 | (uint8_t) (
|
||||
(light_state[SextetLights::P2_MENU] << 5) |
|
||||
(light_state[SextetLights::P2_MENU] << 4)
|
||||
);
|
||||
|
||||
buffer[8] = 0xC0;
|
||||
|
||||
buffer[9] = 0xC0 | (uint8_t) (
|
||||
(light_state[SextetLights::P2_DOWN] << 3) |
|
||||
(light_state[SextetLights::P2_UP] << 2) |
|
||||
(light_state[SextetLights::P2_RIGHT] << 1) |
|
||||
(light_state[SextetLights::P2_LEFT] << 0)
|
||||
);
|
||||
|
||||
buffer[10] = 0xC0;
|
||||
buffer[11] = 0xC0;
|
||||
buffer[12] = 0xC0;
|
||||
|
||||
// terminate with LF
|
||||
buffer[13] = '\n';
|
||||
}
|
||||
|
||||
rawinput::SextetDevice::SextetDevice(std::string device_name) {
|
||||
this->device_name = device_name;
|
||||
is_connected = false;
|
||||
all_off();
|
||||
}
|
||||
|
||||
rawinput::SextetDevice::~SextetDevice() {
|
||||
disconnect();
|
||||
}
|
||||
|
||||
void rawinput::SextetDevice::set_all(bool state) {
|
||||
for (int i = 0; i < LIGHT_COUNT; i++)
|
||||
light_state[i] = state;
|
||||
}
|
||||
|
||||
void rawinput::SextetDevice::all_on() {
|
||||
set_all(true);
|
||||
}
|
||||
|
||||
void rawinput::SextetDevice::all_off() {
|
||||
set_all(false);
|
||||
}
|
||||
|
||||
bool rawinput::SextetDevice::connect() {
|
||||
|
||||
// open device
|
||||
device = CreateFileA(
|
||||
this->device_name.c_str(),
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
0
|
||||
);
|
||||
|
||||
// check device
|
||||
if (device == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
// get comm state
|
||||
DCB dcb;
|
||||
SecureZeroMemory(&dcb, sizeof(DCB));
|
||||
dcb.DCBlength = sizeof(DCB);
|
||||
if (!GetCommState(device, &dcb)) {
|
||||
log_warning("sextet", "GetCommState failed: 0x{:08x}", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// set comm state
|
||||
dcb.BaudRate = 115200;
|
||||
dcb.ByteSize = 8;
|
||||
dcb.Parity = NOPARITY;
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
if (!SetCommState(device, &dcb)) {
|
||||
log_warning("sextet", "SetCommState failed: 0x{:08x}", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// success
|
||||
is_connected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rawinput::SextetDevice::disconnect() {
|
||||
return CloseHandle(device);
|
||||
}
|
||||
|
||||
bool rawinput::SextetDevice::push_light_state() {
|
||||
|
||||
// build packet
|
||||
uint8_t buffer[FULL_SEXTET_COUNT];
|
||||
this->fill_packet(&(buffer[0]), &(light_state[0]));
|
||||
|
||||
// write data to device
|
||||
DWORD bytes_written;
|
||||
WriteFile(
|
||||
device,
|
||||
buffer,
|
||||
FULL_SEXTET_COUNT,
|
||||
&bytes_written,
|
||||
NULL
|
||||
);
|
||||
|
||||
// verify written bytes count
|
||||
return bytes_written == FULL_SEXTET_COUNT;
|
||||
}
|
||||
|
||||
+66
-66
@@ -1,66 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
|
||||
namespace rawinput {
|
||||
|
||||
namespace SextetLights {
|
||||
enum {
|
||||
BASS_NEON = 0,
|
||||
|
||||
MARQUEE_P1_UPPER,
|
||||
MARQUEE_P1_LOWER,
|
||||
MARQUEE_P2_UPPER,
|
||||
MARQUEE_P2_LOWER,
|
||||
|
||||
P1_MENU,
|
||||
P2_MENU,
|
||||
|
||||
P1_UP,
|
||||
P1_DOWN,
|
||||
P1_LEFT,
|
||||
P1_RIGHT,
|
||||
|
||||
P2_UP,
|
||||
P2_DOWN,
|
||||
P2_LEFT,
|
||||
P2_RIGHT
|
||||
};
|
||||
}
|
||||
|
||||
class SextetDevice {
|
||||
private:
|
||||
|
||||
// COM port handle
|
||||
std::string device_name;
|
||||
HANDLE device;
|
||||
|
||||
// number of bytes to contain the full sextet pack and a trailing LF
|
||||
static const size_t FULL_SEXTET_COUNT = 14;
|
||||
|
||||
void set_all(bool state);
|
||||
void fill_packet(uint8_t *buffer, bool *light_state);
|
||||
|
||||
public:
|
||||
|
||||
// only 15 lights are used on SD DDR Cabinets.
|
||||
static const int LIGHT_COUNT = 15;
|
||||
static const std::string LIGHT_NAMES[];
|
||||
|
||||
// state
|
||||
bool is_connected;
|
||||
bool light_state[LIGHT_COUNT];
|
||||
|
||||
SextetDevice(std::string device_name);
|
||||
~SextetDevice();
|
||||
|
||||
bool connect();
|
||||
bool disconnect();
|
||||
|
||||
bool push_light_state();
|
||||
|
||||
void all_on();
|
||||
void all_off();
|
||||
};
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <windows.h>
|
||||
|
||||
namespace rawinput {
|
||||
|
||||
namespace SextetLights {
|
||||
enum {
|
||||
BASS_NEON = 0,
|
||||
|
||||
MARQUEE_P1_UPPER,
|
||||
MARQUEE_P1_LOWER,
|
||||
MARQUEE_P2_UPPER,
|
||||
MARQUEE_P2_LOWER,
|
||||
|
||||
P1_MENU,
|
||||
P2_MENU,
|
||||
|
||||
P1_UP,
|
||||
P1_DOWN,
|
||||
P1_LEFT,
|
||||
P1_RIGHT,
|
||||
|
||||
P2_UP,
|
||||
P2_DOWN,
|
||||
P2_LEFT,
|
||||
P2_RIGHT
|
||||
};
|
||||
}
|
||||
|
||||
class SextetDevice {
|
||||
private:
|
||||
|
||||
// COM port handle
|
||||
std::string device_name;
|
||||
HANDLE device;
|
||||
|
||||
// number of bytes to contain the full sextet pack and a trailing LF
|
||||
static const size_t FULL_SEXTET_COUNT = 14;
|
||||
|
||||
void set_all(bool state);
|
||||
void fill_packet(uint8_t *buffer, bool *light_state);
|
||||
|
||||
public:
|
||||
|
||||
// only 15 lights are used on SD DDR Cabinets.
|
||||
static const int LIGHT_COUNT = 15;
|
||||
static const std::string LIGHT_NAMES[];
|
||||
|
||||
// state
|
||||
bool is_connected;
|
||||
bool light_state[LIGHT_COUNT];
|
||||
|
||||
SextetDevice(std::string device_name);
|
||||
~SextetDevice();
|
||||
|
||||
bool connect();
|
||||
bool disconnect();
|
||||
|
||||
bool push_light_state();
|
||||
|
||||
void all_on();
|
||||
void all_off();
|
||||
};
|
||||
}
|
||||
|
||||
+567
-567
File diff suppressed because it is too large
Load Diff
+20
-20
@@ -1,20 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "device.h"
|
||||
#include "rawinput.h"
|
||||
|
||||
namespace rawinput::touch {
|
||||
|
||||
// settings
|
||||
extern bool DISABLED;
|
||||
extern bool INVERTED;
|
||||
|
||||
bool is_touchscreen(Device *device);
|
||||
void enable(Device *device);
|
||||
void disable(Device *device);
|
||||
void update_input(Device *device);
|
||||
void update_timeouts(Device *device);
|
||||
void update_timeouts(RawInputManager *manager);
|
||||
bool is_enabled(RawInputManager *manager);
|
||||
void display_update();
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include "device.h"
|
||||
#include "rawinput.h"
|
||||
|
||||
namespace rawinput::touch {
|
||||
|
||||
// settings
|
||||
extern bool DISABLED;
|
||||
extern bool INVERTED;
|
||||
|
||||
bool is_touchscreen(Device *device);
|
||||
void enable(Device *device);
|
||||
void disable(Device *device);
|
||||
void update_input(Device *device);
|
||||
void update_timeouts(Device *device);
|
||||
void update_timeouts(RawInputManager *manager);
|
||||
bool is_enabled(RawInputManager *manager);
|
||||
void display_update();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user