Initial re-upload of spice2x-24-08-24

This commit is contained in:
2024-08-28 11:10:34 -04:00
commit caa9e02285
1181 changed files with 380065 additions and 0 deletions

177
rawinput/device.h Normal file
View File

@@ -0,0 +1,177 @@
#pragma once
#include <string>
#include <thread>
#include <mutex>
#include <vector>
#include <windows.h>
extern "C" {
#include <hidusage.h>
#include <hidsdi.h>
}
#include "util/unique_plain_ptr.h"
#include "sextet.h"
#include "smxstage.h"
namespace rawinput {
enum DeviceType {
DESTROYED,
UNKNOWN,
MOUSE,
KEYBOARD,
HID,
MIDI,
SEXTET_OUTPUT,
PIUIO_DEVICE,
SMX_STAGE,
};
enum MouseKeys {
MOUSEBTN_LEFT = 0,
MOUSEBTN_RIGHT = 1,
MOUSEBTN_MIDDLE = 2,
MOUSEBTN_1 = 3,
MOUSEBTN_2 = 4,
MOUSEBTN_3 = 5,
MOUSEBTN_4 = 6,
MOUSEBTN_5 = 7,
};
enum MousePos {
MOUSEPOS_X = 0,
MOUSEPOS_Y = 1,
MOUSEPOS_WHEEL = 2,
};
struct DeviceInfo {
std::string devclass;
std::string subclass;
std::string protocol;
std::string guid_str;
GUID guid;
};
struct DeviceMouseInfo {
bool key_states[16];
bool key_states_bind[16];
double key_up[16];
double key_down[16];
long pos_x, pos_y;
long pos_wheel;
};
struct DeviceKeyboardInfo {
bool key_states[1024];
double key_up[1024];
double key_down[1024];
};
enum class HIDDriver {
Default,
PacDrive,
};
struct HIDTouchPoint {
DWORD id;
bool down;
float x, y;
size_t ttl;
uint64_t last_report; // unix time in ms
};
struct HIDTouchScreenData {
bool valid = false;
bool parsed_elements = false;
size_t remaining_contact_count = 0;
std::vector<int> elements_contact_count;
std::vector<int> elements_contact_identifier;
std::vector<int> elements_x;
std::vector<int> elements_y;
std::vector<int> elements_width;
std::vector<int> elements_height;
std::vector<int> elements_pressed;
std::vector<int> elements_pressure;
std::vector<HIDTouchPoint> touch_points;
};
struct DeviceHIDInfo {
HANDLE handle;
_HIDP_CAPS caps;
HIDD_ATTRIBUTES attributes;
HIDDriver driver = HIDDriver::Default;
HIDTouchScreenData touch {};
std::string usage_name;
util::unique_plain_ptr<_HIDP_PREPARSED_DATA> preparsed_data;
UINT preparsed_size;
std::vector<HIDP_BUTTON_CAPS> button_caps_list;
std::vector<std::string> button_caps_names;
std::vector<HIDP_BUTTON_CAPS> button_output_caps_list;
std::vector<std::string> button_output_caps_names;
std::vector<HIDP_VALUE_CAPS> value_caps_list;
std::vector<std::string> value_caps_names;
std::vector<HIDP_VALUE_CAPS> value_output_caps_list;
std::vector<std::string> value_output_caps_names;
std::vector<std::vector<bool>> button_states;
std::vector<std::vector<double>> button_up, button_down;
std::vector<std::vector<bool>> button_output_states;
std::vector<float> value_states;
std::vector<LONG> value_states_raw;
std::vector<float> value_output_states;
// for config binding function
std::vector<float> bind_value_states;
};
struct DeviceMIDIInfo {
std::vector<bool> states;
std::vector<uint8_t> states_events;
std::vector<bool> bind_states;
std::vector<uint8_t> velocity; // 16*128 x 7 bit resolution
bool freeze;
std::vector<uint16_t> controls_precision; // 16*32 14 bit resolution
std::vector<uint16_t> controls_precision_bind;
std::vector<bool> controls_precision_msb;
std::vector<bool> controls_precision_lsb;
std::vector<bool> controls_precision_set;
std::vector<uint8_t> controls_single; // 16*44 7 bit resolution
std::vector<uint8_t> controls_single_bind;
std::vector<bool> controls_single_set;
std::vector<bool> controls_onoff; // 16*6 1 bit resolution :)
std::vector<bool> controls_onoff_bind;
std::vector<bool> controls_onoff_set;
uint16_t pitch_bend; // 14 bit resolution
};
class PIUIO;
struct Device {
size_t id;
std::string name;
std::string desc;
HANDLE handle = INVALID_HANDLE_VALUE;
DeviceType type = UNKNOWN;
DeviceInfo info;
std::mutex *mutex;
std::mutex *mutex_out;
bool updated = true;
bool output_pending = true;
bool output_enabled = false;
DeviceMouseInfo *mouseInfo = nullptr;
DeviceKeyboardInfo *keyboardInfo = nullptr;
DeviceHIDInfo *hidInfo = nullptr;
DeviceMIDIInfo *midiInfo = nullptr;
SextetDevice *sextetInfo = nullptr;
PIUIO* piuioDev = nullptr;
SmxStageDevice *smxstageInfo = nullptr;
double input_time = 0.0;
double input_hz = 0.f;
double input_hz_max = 0.f;
};
}

132
rawinput/hotplug.cpp Normal file
View File

@@ -0,0 +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_from_file(path, card_data, player)) {
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
rawinput/hotplug.h Normal file
View File

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

394
rawinput/piuio.cpp Normal file
View File

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

78
rawinput/piuio.h Normal file
View File

@@ -0,0 +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;
};
}

2680
rawinput/rawinput.cpp Normal file

File diff suppressed because it is too large Load Diff

140
rawinput/rawinput.h Normal file
View File

@@ -0,0 +1,140 @@
#pragma once
#include <functional>
#include <thread>
#include <condition_variable>
#include <vector>
#include <windows.h>
#include <mmsystem.h>
#include "device.h"
#include "hotplug.h"
namespace rawinput {
// settings
extern bool NOLEGACY;
extern uint8_t HID_LIGHT_BRIGHTNESS;
extern bool ENABLE_SMX_STAGE;
extern int TOUCHSCREEN_RANGE_X;
extern int TOUCHSCREEN_RANGE_Y;
struct DeviceCallback {
void *data;
std::function<void(void*, Device*)> f;
};
struct MidiCallback {
void *data;
std::function<void(void*, Device*, uint8_t cmd, uint8_t ch, uint8_t b1, uint8_t b2)> f;
};
class RawInputManager {
private:
HotplugManager *hotplug;
std::vector<Device> devices;
HWND input_hwnd = nullptr;
WNDCLASSEX input_hwnd_class {};
std::thread *input_thread = nullptr;
std::thread *flush_thread = nullptr;
bool flush_thread_running = false;
std::thread *output_thread = nullptr;
std::mutex output_thread_m;
bool output_thread_ready = false;
bool output_thread_running = false;
std::condition_variable output_thread_cv;
std::vector<DeviceCallback> callback_add;
std::vector<DeviceCallback> callback_change;
std::vector<MidiCallback> callback_midi;
void input_hwnd_create();
void input_hwnd_destroy();
void devices_reload();
void devices_scan_rawinput(RAWINPUTDEVICELIST *device, bool log = true);
void devices_scan_piuio();
void devices_scan_smxstage();
void devices_destruct();
void devices_destruct(Device *device, bool log = true);
void flush_start();
void flush_stop();
void output_start();
void output_stop();
static std::string rawinput_get_device_name(HANDLE hDevice);
static std::string rawinput_get_device_description(const DeviceInfo& info, const std::string &device_name);
static LRESULT CALLBACK input_wnd_proc(HWND, UINT, WPARAM, LPARAM);
static void CALLBACK input_midi_proc(HMIDIIN, UINT, DWORD_PTR, DWORD_PTR, DWORD_PTR);
static DeviceInfo get_device_info(const std::string &device_name);
public:
RawInputManager();
~RawInputManager();
void stop();
void sextet_register(
const std::string &port_name,
const std::string &alias = "Sextet",
bool warn = true);
void devices_scan_rawinput(const std::string &device_name = "");
void devices_scan_midi();
void devices_remove(const std::string &name);
void devices_register();
void devices_unregister();
static void device_write_output(Device *device, bool only_updated = true);
void devices_flush_output(bool optimized = true);
void __stdcall devices_print();
Device *devices_get(const std::string &name, bool updated = false);
inline std::vector<Device> &devices_get() {
return devices;
}
inline std::vector<Device *> devices_get_updated() {
std::vector<Device *> updated;
for (auto &device : devices_get()) {
device.mutex->lock();
if (device.updated) {
device.updated = false;
device.mutex->unlock();
updated.emplace_back(&device);
} else {
device.mutex->unlock();
}
}
return updated;
}
inline void devices_midi_freeze(bool freeze) {
for (auto &device : devices_get()) {
if (device.type == MIDI) {
device.midiInfo->freeze = freeze;
if (!freeze) {
for (unsigned short index = 0; index < 16 * 128; index++) {
device.midiInfo->states[index] = false;
}
}
}
}
}
void add_callback_add(void *data, std::function<void(void *, Device *)> callback);
void remove_callback_add(void *data, const std::function<void(void *, Device *)> &callback);
void add_callback_change(void *data, std::function<void(void *, Device *)> callback);
void remove_callback_change(void *data, const std::function<void(void *, Device *)>& callback);
void add_callback_midi(void *data, std::function<void(void *, Device *,
uint8_t, uint8_t, uint8_t, uint8_t)> callback);
void remove_callback_midi(void *data, const std::function<void(void *, Device *,
uint8_t, uint8_t, uint8_t, uint8_t)>& callback);
};
}

178
rawinput/sextet.cpp Normal file
View File

@@ -0,0 +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;
}

67
rawinput/sextet.h Normal file
View File

@@ -0,0 +1,67 @@
#pragma once
#include <cstdint>
#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();
};
}

63
rawinput/smxstage.cpp Normal file
View File

@@ -0,0 +1,63 @@
#include "smxstage.h"
#include "util/logging.h"
#include <SMX.h>
#include <fmt/format.h>
namespace {
static const std::string PANEL_NAMES[] = {
"Top-Left", "Top-Center", "Top-Right",
"Middle-Left", "Middle-Center", "Middle-Right",
"Bottom-Left", "Bottom-Center", "Bottom-Right"
};
void NullSmxCallback(int pad, SMXUpdateCallbackReason reason, void *pUser) {
}
}
namespace rawinput {
std::string SmxStageDevice::GetLightNameByIndex(size_t index) {
const size_t subpixel = index%3;
const size_t panel = (index/3)%PANEL_COUNT;
const size_t pad = (index/3)/PANEL_COUNT;
switch (subpixel) {
case 0: return fmt::format("Pad {} Panel {} Red", pad+1, PANEL_NAMES[panel]);
case 1: return fmt::format("Pad {} Panel {} Green", pad+1, PANEL_NAMES[panel]);
case 2: return fmt::format("Pad {} Panel {} Blue", pad+1, PANEL_NAMES[panel]);
}
return "StepmaniaX invalid index";
}
SmxStageDevice::SmxStageDevice() :
m_lightData(TOTAL_LIGHT_COUNT*LEDS_PER_PAD),
m_lightDataMutex()
{
}
bool SmxStageDevice::Initialize() {
SMX_Start(&NullSmxCallback, nullptr);
return true;
}
void SmxStageDevice::SetLightByIndex(size_t index, uint8_t value) {
const size_t subpixel = index%3;
const size_t panel = (index/3)%PANEL_COUNT;
const size_t pad = (index/3)/PANEL_COUNT;
m_lightDataMutex.lock();
for(size_t i = 0; i < LEDS_PER_PAD; ++i) {
m_lightData[(pad*PANEL_COUNT*LEDS_PER_PAD*3)+(panel*LEDS_PER_PAD*3)+(i*3)+subpixel] = value;
}
m_lightDataMutex.unlock();
}
void SmxStageDevice::Update() {
m_lightDataMutex.lock();
SMX_SetLights2(reinterpret_cast<const char*>(&m_lightData[0]), m_lightData.size());
m_lightDataMutex.unlock();
}
}

29
rawinput/smxstage.h Normal file
View File

@@ -0,0 +1,29 @@
#pragma once
#include <string>
#include <vector>
#include <mutex>
namespace rawinput {
class SmxStageDevice {
public:
static constexpr int PAD_COUNT = 2;
static constexpr int PANEL_COUNT = 9;
static constexpr int LEDS_PER_PAD = 25;
static constexpr int TOTAL_LIGHT_COUNT = PAD_COUNT*PANEL_COUNT*3;
static inline size_t GetPanelIndexR(size_t pad, size_t panel) {return ((pad*PANEL_COUNT)+panel)*3 + 0;};
static inline size_t GetPanelIndexG(size_t pad, size_t panel) {return ((pad*PANEL_COUNT)+panel)*3 + 1;};
static inline size_t GetPanelIndexB(size_t pad, size_t panel) {return ((pad*PANEL_COUNT)+panel)*3 + 2;};
static std::string GetLightNameByIndex(size_t index);
SmxStageDevice();
bool Initialize();
void SetLightByIndex(size_t index, uint8_t value);
void Update();
private:
std::vector<uint8_t> m_lightData;
std::mutex m_lightDataMutex;
};
}

567
rawinput/touch.cpp Normal file
View File

@@ -0,0 +1,567 @@
#include "touch.h"
#include <algorithm>
#include <windows.h>
#include <versionhelpers.h>
#include "util/logging.h"
#include "util/time.h"
#include "touch/touch.h"
// std::min
#ifdef min
#undef min
#endif
// std::max
#ifdef max
#undef max
#endif
namespace rawinput::touch {
// settings
bool DISABLED = false;
bool INVERTED = false;
// state
static bool DISPLAY_INITIALIZED = false;
static DWORD DISPLAY_ORIENTATION = DMDO_DEFAULT;
static long DISPLAY_SIZE_X = 1920L;
static long DISPLAY_SIZE_Y = 1080L;
bool is_touchscreen(Device *device) {
// check if disabled
if (DISABLED) {
return false;
}
// check device type
if (device->type != HID) {
return false;
}
auto *hid = device->hidInfo;
auto &attributes = hid->attributes;
// filter by VID/PID
if (attributes.VendorID > 0) {
// P2418HT
// touch points apparently aren't released and will stay
if (attributes.VendorID == 0x1FD2 && attributes.ProductID == 0x6103) {
return false;
}
}
// ignore the "Touch Pad" (0x05) usage under the "Digitizers" (0x0d) usage page
if (hid->caps.UsagePage == 0x0d && hid->caps.Usage == 0x05) {
return false;
}
// check description
if (device->desc == "HID-compliant touch screen") {
// TODO: this only works on english OS (?)
return true;
}
// we can also check if there's touch point values inside
for (const auto &value_name : hid->value_caps_names) {
if (value_name == "Contact identifier") {
return true;
}
}
// nope this one probably not
return false;
}
void enable(Device *device) {
if (device->type == HID) {
device->hidInfo->touch.valid = true;
log_info("rawinput", "enabled touchscreen device: {} ({})", device->desc, device->name);
} else {
log_fatal("rawinput", "tried to enable touch functionality on non HID device");
}
}
void disable(Device *device) {
if (device->type == HID) {
device->hidInfo->touch.valid = false;
log_info("rawinput", "disabled touchscreen device: {} ({})", device->desc, device->name);
} else
log_fatal("rawinput", "tried to disable touch functionality on non HID device");
}
void update_input(Device *device) {
// check type
if (device->type != HID) {
log_fatal("rawinput", "touch update called on non HID device");
return;
}
// get touch info
auto hid = device->hidInfo;
auto &touch = hid->touch;
if (!touch.valid) {
// not a registered touchscreen
return;
}
// parse elements
if (!touch.parsed_elements) {
log_info("rawinput", "parsing touch elements");
// clear lists first
touch.elements_contact_count.clear();
touch.elements_contact_identifier.clear();
touch.elements_x.clear();
touch.elements_y.clear();
touch.elements_width.clear();
touch.elements_height.clear();
touch.elements_pressed.clear();
touch.elements_pressure.clear();
// get value indices
for (int i = 0; i < (int) hid->value_caps_names.size(); i++) {
auto &name = hid->value_caps_names[i];
if (name == "Contact count") {
touch.elements_contact_count.push_back(i);
} else if (name == "X") {
touch.elements_x.push_back(i);
} else if (name == "Y") {
touch.elements_y.push_back(i);
} else if (name == "Width") {
touch.elements_width.push_back(i);
} else if (name == "Height") {
touch.elements_height.push_back(i);
} else if (name == "Contact identifier") {
touch.elements_contact_identifier.push_back(i);
} else if (name == "Pressure") {
touch.elements_pressure.push_back(i);
}
}
// get button indices
for (int i = 0; i < (int) hid->button_caps_names.size(); i++) {
auto &name = hid->button_caps_names[i];
if (name == "Tip Switch") {
touch.elements_pressed.push_back(i);
}
}
// check sizes
auto touch_size = touch.elements_contact_identifier.size();
if (touch_size != touch.elements_x.size() ||
touch_size != touch.elements_y.size() ||
touch_size != touch.elements_pressed.size())
{
log_info("rawinput", "touch element size mismatch: {}:contacts, {}:x, {}:y, {}:pressed",
touch.elements_contact_identifier.size(),
touch.elements_x.size(),
touch.elements_y.size(),
touch.elements_pressed.size());
disable(device);
return;
}
// mark as parsed
touch.parsed_elements = true;
log_info("rawinput", "touch elements parsed: {} status fields", touch.elements_x.size());
}
// check if display is initialized
if (!DISPLAY_INITIALIZED) {
display_update();
}
// update timeouts here as well so events are in the right order
update_timeouts(device);
// determine the number of touches contained in this report, defaulting to the size of `elements_x`
size_t touch_report_count = touch.elements_x.size();
if (!touch.elements_contact_count.empty()) {
// support devices that have multiple "Contact count" fields, for some reason
size_t contact_count = 0;
for (auto &index : touch.elements_contact_count) {
contact_count = std::max(contact_count, (size_t) hid->value_states_raw[index]);
}
// hybrid mode devices will report a contact count of 0 for subsequent reports that are
// part of the same initial frame
if (contact_count > 0) {
if (contact_count > touch_report_count) {
touch.remaining_contact_count = contact_count - touch_report_count;
} else {
touch_report_count = contact_count;
touch.remaining_contact_count = 0;
}
} else if (touch.remaining_contact_count > 0) {
if (touch.remaining_contact_count > touch_report_count) {
touch.remaining_contact_count -= touch_report_count;
} else {
touch_report_count = touch.remaining_contact_count;
touch.remaining_contact_count = 0;
}
}
}
// iterate all input data and get touch points
std::vector<HIDTouchPoint> touch_points;
touch_points.reserve(touch.elements_x.size());
for (size_t i = 0; i < touch.elements_x.size(); i++) {
// build touch point
HIDTouchPoint hid_tp{};
auto pos_x = hid->value_states[touch.elements_x[i]];
auto pos_y = hid->value_states[touch.elements_y[i]];
switch (DISPLAY_ORIENTATION) {
case DMDO_DEFAULT:
hid_tp.x = pos_x;
hid_tp.y = pos_y;
break;
case DMDO_90:
hid_tp.x = 1.f - pos_y;
hid_tp.y = pos_x;
break;
case DMDO_180:
hid_tp.x = 1.f - pos_x;
hid_tp.y = 1.f - pos_y;
break;
case DMDO_270:
hid_tp.x = pos_y;
hid_tp.y = 1.f - pos_x;
break;
default:
break;
}
// optionally invert
if (INVERTED) {
hid_tp.x = 1.f - hid_tp.x;
hid_tp.y = 1.f - hid_tp.y;
}
// check if this touch point should be considered valid
//
// If "Contact count" reports there are no touch reports remaining and the X and Y
// coordinates of this touch point are zero, then this report element should be
// skipped.
if (touch_report_count == 0 && hid_tp.x == 0.f && hid_tp.y == 0.f) {
continue;
} else if (touch_report_count > 0) {
touch_report_count--;
}
// generate ID (hopefully unique)
hid_tp.id = (DWORD) hid->value_states_raw[touch.elements_contact_identifier[i]];
hid_tp.id += (DWORD) (0xFFFFFF + device->id * 512);
//std::string src = "(none)";
// check if tip switch is down
size_t index_pressed = touch.elements_pressed[i];
for (auto &button_states : hid->button_states) {
if (index_pressed < button_states.size()) {
hid_tp.down = button_states[index_pressed];
/*
if (hid_tp.down)
src = "pressed (index: " + to_string(touch.elements_pressed[i]) + ", state: " + to_string(button_states[index_pressed]) +")";
*/
break;
} else
index_pressed -= button_states.size();
}
// check width/height of touch point to see if pressed
float width = 0.f, height = 0.f;
if (!hid_tp.down && i < touch.elements_width.size() && i < touch.elements_height.size()) {
width = hid->value_states[touch.elements_width[i]];
height = hid->value_states[touch.elements_height[i]];
hid_tp.down = width > 0.f && height > 0.f;
/*
if (hid_tp.down)
src = "width_height (width index: " + to_string(touch.elements_width[i]) + ", height index: " + to_string(touch.elements_height[i]) + ")";
*/
}
// so last thing we can check is the pressure
if (!hid_tp.down && i < touch.elements_pressure.size()) {
auto pressure = hid->value_states[touch.elements_pressure[i]];
hid_tp.down = pressure > 0.f;
/*
if (hid_tp.down)
src = "pressure (index: " + to_string(touch.elements_pressure[i]) + ")";
*/
}
/*
log_info("rawinput",
"touch i: " + to_string(i) +
" (id: " + to_string(hid_tp.id) +
"), ci: " + to_string(hid->value_states_raw[touch.elements_contact_identifier[i]]) +
", x: " + to_string(pos_x) +
", y: " + to_string(pos_y) +
", width: " + to_string(width) +
", height: " + to_string(height) +
", down: " + to_string(hid_tp.down) +
", src: " + src);
*/
// add to touch points
touch_points.emplace_back(hid_tp);
}
// process touch points
std::vector<DWORD> touch_removes;
std::vector<TouchPoint> touch_writes;
std::vector<DWORD> touch_modifications;
for (auto &hid_tp : touch_points) {
// check if existing
auto existing = std::find_if(
touch.touch_points.begin(),
touch.touch_points.end(),
[hid_tp] (const HIDTouchPoint &x) {
return x.id == hid_tp.id;
});
/*
ssize_t ttl = -1;
int64_t last_report = -1;
if (existing != touch.touch_points.end()) {
ttl = existing->ttl;
last_report = existing->last_report;
}
log_info("rawinput",
"id: " + to_string(hid_tp.id) +
", existing: " + to_string(existing != touch.touch_points.end()) +
", ttl: " + to_string(ttl) +
", last_report: " + to_string(last_report) +
", down: " + to_string(hid_tp.down));
*/
// check if pressed
if (!hid_tp.down) {
// only remove if it exists
if (existing != touch.touch_points.end()) {
// remove all touch points with this ID
touch_removes.push_back(hid_tp.id);
touch.touch_points.erase(std::remove_if(
touch.touch_points.begin(),
touch.touch_points.end(),
[hid_tp] (const HIDTouchPoint &x) {
return x.id == hid_tp.id;
}), touch.touch_points.end());
}
} else {
// write touch point
TouchPoint tp {
.id = hid_tp.id,
.x = (long) (hid_tp.x * DISPLAY_SIZE_X),
.y = (long) (hid_tp.y * DISPLAY_SIZE_Y),
.mouse = false,
};
touch_writes.push_back(tp);
touch_modifications.push_back(hid_tp.id);
// check if existing
if (existing == touch.touch_points.end()) {
// add new touch point
touch.touch_points.push_back(hid_tp);
} else {
// update existing touch point
*existing = hid_tp;
}
}
}
// set TTL and last report time
if (!touch.elements_x.empty() && !touch_modifications.empty()) {
auto ttl = touch.touch_points.size() / touch.elements_x.size() + 1;
auto system_time_ms = get_system_milliseconds();
for (auto &hid_tp_id : touch_modifications) {
for (auto &hid_tp : touch.touch_points) {
if (hid_tp.id == hid_tp_id) {
hid_tp.ttl = ttl;
hid_tp.last_report = system_time_ms;
}
}
}
}
// remove dead touch points
auto ttl_it = touch.touch_points.begin();
while (ttl_it != touch.touch_points.end()) {
auto &tp = *ttl_it;
if (tp.ttl == 0) {
touch_removes.push_back(tp.id);
ttl_it = touch.touch_points.erase(ttl_it);
} else {
tp.ttl--;
ttl_it++;
}
}
#ifndef SPICETOOLS_SPICECFG_STANDALONE
// update touch module
touch_remove_points(&touch_removes);
touch_write_points(&touch_writes);
#endif
}
void update_timeouts(Device *device) {
// check type
if (device->type != HID) {
log_fatal("rawinput", "touch timeout update called on non HID device");
return;
}
// get touch info
auto hid = device->hidInfo;
auto &touch = hid->touch;
if (!touch.valid) {
return; // not a registered touchscreen
}
// calculate deadline - we allow the devices 50ms of not sending shit
auto deadline = get_system_milliseconds() - 50;
// check touch points
std::vector<DWORD> touch_removes;
auto touch_it = touch.touch_points.begin();
while (touch_it != touch.touch_points.end()) {
auto &hid_tp = *touch_it;
// see if it's behind the deadline
if (hid_tp.last_report < deadline) {
// oops it's gone
touch_removes.push_back(hid_tp.id);
touch_it = touch.touch_points.erase(touch_it);
} else {
// check the next device
touch_it++;
}
}
#ifndef SPICETOOLS_SPICECFG_STANDALONE
// remove from touch module
touch_remove_points(&touch_removes);
#endif
}
void update_timeouts(RawInputManager *manager) {
// iterate all devices
for (auto &device : manager->devices_get()) {
// check if it's a valid touchscreen
if (device.type == HID && device.hidInfo->touch.valid) {
// lock n' load
device.mutex->lock();
update_timeouts(&device);
device.mutex->unlock();
}
}
}
bool is_enabled(RawInputManager *manager) {
// check if disabled or manager is null
if (DISABLED || manager == nullptr)
return false;
// check if at least one device is marked as valid touchscreen
for (auto &device : manager->devices_get()) {
if (device.type == HID && device.hidInfo->touch.valid) {
return true;
}
}
// no valid touch screen found
return false;
}
void display_update() {
// check if disabled
if (DISABLED)
return;
// determine monitor size
static RECT display_rect;
GetWindowRect(GetDesktopWindow(), &display_rect);
DISPLAY_SIZE_X = display_rect.right - display_rect.left;
DISPLAY_SIZE_Y = display_rect.bottom - display_rect.top;
log_info("rawinput", "display size: {}x{}", (int) DISPLAY_SIZE_X, (int) DISPLAY_SIZE_Y);
// determine monitor orientation
DEVMODE display_mode{};
display_mode.dmSize = sizeof(DEVMODE);
if (!EnumDisplaySettingsEx(nullptr, ENUM_CURRENT_SETTINGS, &display_mode, EDS_RAWMODE)) {
log_info("rawinput", "failed to determine monitor mode");
} else if (display_mode.dmFields & DM_DISPLAYORIENTATION) {
DISPLAY_ORIENTATION = display_mode.dmDisplayOrientation;
switch (DISPLAY_ORIENTATION) {
case DMDO_DEFAULT:
log_info("rawinput", "display rotation: 0");
break;
case DMDO_90:
log_info("rawinput", "display rotation: 90");
break;
case DMDO_180:
log_info("rawinput", "display rotation: 180");
break;
case DMDO_270:
log_info("rawinput", "display rotation: 270");
break;
default:
break;
}
// another XP fix
if (!IsWindowsVistaOrGreater()) {
switch (DISPLAY_ORIENTATION) {
case DMDO_90:
DISPLAY_ORIENTATION = DMDO_180;
log_info("rawinput", "flipping to 180");
break;
case DMDO_270:
DISPLAY_ORIENTATION = DMDO_90;
log_info("rawinput", "flipping to 90");
break;
default:
break;
}
}
} else {
log_info("rawinput", "failed to determine monitor orientation");
}
// mark as initialized
DISPLAY_INITIALIZED = true;
}
}

20
rawinput/touch.h Normal file
View File

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