From 1e62bec650ba6beed5349b93642176a5805db33f Mon Sep 17 00:00:00 2001 From: din Date: Mon, 17 Mar 2025 22:22:02 -0500 Subject: [PATCH] hidapi: snek & stac support --- src/CMakeData-arch.cmake | 10 +- src/arch/Lights/LightsDriver_Linux_stac.cpp | 111 -------------- src/arch/Lights/LightsDriver_snek.cpp | 112 ++++++++++++++ src/arch/Lights/LightsDriver_snek.h | 140 ++++++++++++++++++ src/arch/Lights/LightsDriver_stac.cpp | 127 ++++++++++++++++ ...river_Linux_stac.h => LightsDriver_stac.h} | 56 +++---- 6 files changed, 415 insertions(+), 141 deletions(-) delete mode 100644 src/arch/Lights/LightsDriver_Linux_stac.cpp create mode 100644 src/arch/Lights/LightsDriver_snek.cpp create mode 100644 src/arch/Lights/LightsDriver_snek.h create mode 100644 src/arch/Lights/LightsDriver_stac.cpp rename src/arch/Lights/{LightsDriver_Linux_stac.h => LightsDriver_stac.h} (67%) diff --git a/src/CMakeData-arch.cmake b/src/CMakeData-arch.cmake index d16968c4a5..9c9bdba91a 100644 --- a/src/CMakeData-arch.cmake +++ b/src/CMakeData-arch.cmake @@ -180,13 +180,17 @@ list(APPEND SMDATA_ARCH_LIGHTS_SRC "arch/Lights/LightsDriver.cpp" "arch/Lights/LightsDriver_Export.cpp" "arch/Lights/LightsDriver_SextetStream.cpp" "arch/Lights/LightsDriver_SystemMessage.cpp" - "arch/Lights/LightsDriver_HidBlueDot.cpp") + "arch/Lights/LightsDriver_stac.cpp" + "arch/Lights/LightsDriver_snek.cpp" + "arch/Lights/LightsDriver_HidBlueDot.cpp") list(APPEND SMDATA_ARCH_LIGHTS_HPP "arch/Lights/LightsDriver.h" "arch/Lights/LightsDriver_Export.h" "arch/Lights/LightsDriver_SextetStream.h" "arch/Lights/LightsDriver_SystemMessage.h" "arch/Lights/SextetUtils.h" - "arch/Lights/LightsDriver_HidBlueDot.h") + "arch/Lights/LightsDriver_stac.h" + "arch/Lights/LightsDriver_snek.h" + "arch/Lights/LightsDriver_HidBlueDot.h") # TODO: Confirm if Apple can use the export. if(NOT APPLE) @@ -214,7 +218,6 @@ if(NOT APPLE) "arch/Lights/LightsDriver_Linux_PIUIO_Leds.cpp" "arch/Lights/LightsDriver_Linux_PIUIOBTN_Leds.cpp" "arch/Lights/LightsDriver_Linux_ITGIO.cpp" - "arch/Lights/LightsDriver_Linux_stac.cpp" "arch/Lights/LightsDriver_LinuxPacDrive.cpp" "arch/Lights/LightsDriver_LinuxWeedTech.cpp") list(APPEND SMDATA_ARCH_LIGHTS_HPP @@ -223,7 +226,6 @@ if(NOT APPLE) "arch/Lights/LightsDriver_Linux_PIUIO_Leds.h" "arch/Lights/LightsDriver_Linux_PIUIOBTN_Leds.h" "arch/Lights/LightsDriver_Linux_ITGIO.h" - "arch/Lights/LightsDriver_Linux_stac.h" "arch/Lights/LightsDriver_LinuxPacDrive.h" "arch/Lights/LightsDriver_LinuxWeedTech.h") if(WITH_MINIMAID) diff --git a/src/arch/Lights/LightsDriver_Linux_stac.cpp b/src/arch/Lights/LightsDriver_Linux_stac.cpp deleted file mode 100644 index 3737ec906d..0000000000 --- a/src/arch/Lights/LightsDriver_Linux_stac.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "global.h" -#include "LightsDriver_Linux_stac.h" -#include "GameState.h" -#include "Game.h" -#include "RageLog.h" - -#include -#include -#include - -#if defined(HAVE_UNISTD_H) -#include -#endif -#include -#include - -#if defined(HAVE_FCNTL_H) -#include -#endif - -#include -#include -#include -#include - -REGISTER_LIGHTS_DRIVER_CLASS2(stac, Linux_stac); - -LightsDriver_Linux_stac::LightsDriver_Linux_stac() -{ - memset(outputBuffer[GameController_1], 0x00, STAC_HIDREPORT_SIZE); - memset(outputBuffer[GameController_2], 0x00, STAC_HIDREPORT_SIZE); - - // Open the device using the VID, PID, - // and optionally the Serial number. - handleP1 = hid_open(STAC_VID, STAC_PID_P1, NULL); - handleP2 = hid_open(STAC_VID, STAC_PID_P2, NULL); - - if (!handleP1) { - LOG->Warn("Stac P1 device not found."); - hid_exit(); - return; - } -} - -LightsDriver_Linux_stac::~LightsDriver_Linux_stac() -{ - if(handleP1) - hid_close(handleP1); - if (handleP2) - hid_close(handleP2); - - // Finalize the hidapi library - hid_exit(); -} - -void LightsDriver_Linux_stac::SetBuffer(int index, bool lightState, GameController ctrlNum) -{ - //the first byte is the report ID, so we offset it here to adjust. - uint8_t index_offset = index + 1; - - //each index in the array represents a single light, - //the light will turn on for any value that isn't 0x00 - uint8_t val = lightState ? 0xFF : 0x00; - - //ensure the index is valid and the light value has changed. - if (index_offset < STAC_HIDREPORT_SIZE && outputBuffer[ctrlNum][index_offset] != val) - { - outputBuffer[ctrlNum][index_offset] = val; - - //signal the loop to push the new buffer to the device. - stateChanged = true; - } -} - -void LightsDriver_Linux_stac::HandleState(const LightsState *ls, GameController ctrlNum) -{ - if (!handle[ctrlNum]) - return; - - //check to see which game we are running as it can change during gameplay. - const InputScheme *pInput = &GAMESTATE->GetCurrentGame()->m_InputScheme; - RString sInputName = pInput->m_szName; - - if (sInputName.EqualsNoCase("dance")) - { - SetBuffer(STAC_LIGHTINDEX_BTN1, ls->m_bGameButtonLights[ctrlNum][DANCE_BUTTON_UP], ctrlNum); - SetBuffer(STAC_LIGHTINDEX_BTN2, ls->m_bGameButtonLights[ctrlNum][DANCE_BUTTON_DOWN], ctrlNum); - SetBuffer(STAC_LIGHTINDEX_BTN3, ls->m_bGameButtonLights[ctrlNum][DANCE_BUTTON_LEFT], ctrlNum); - SetBuffer(STAC_LIGHTINDEX_BTN4, ls->m_bGameButtonLights[ctrlNum][DANCE_BUTTON_RIGHT], ctrlNum); - } - else if (sInputName.EqualsNoCase("pump")) - { - SetBuffer(STAC_LIGHTINDEX_BTN1, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_UPLEFT], ctrlNum); - SetBuffer(STAC_LIGHTINDEX_BTN2, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_UPRIGHT], ctrlNum); - SetBuffer(STAC_LIGHTINDEX_BTN3, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_CENTER], ctrlNum); - SetBuffer(STAC_LIGHTINDEX_BTN4, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_DOWNLEFT], ctrlNum); - SetBuffer(STAC_LIGHTINDEX_BTN5, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_DOWNRIGHT], ctrlNum); - } - - if (!stateChanged) - return; - - hid_write(handle[ctrlNum], (unsigned char*)&outputBuffer[ctrlNum], STAC_HIDREPORT_SIZE); - stateChanged = false; -} - -void LightsDriver_Linux_stac::Set(const LightsState *ls) -{ - HandleState(ls, GameController_1); - HandleState(ls, GameController_2); -} diff --git a/src/arch/Lights/LightsDriver_snek.cpp b/src/arch/Lights/LightsDriver_snek.cpp new file mode 100644 index 0000000000..cb8bb37f55 --- /dev/null +++ b/src/arch/Lights/LightsDriver_snek.cpp @@ -0,0 +1,112 @@ +#include "global.h" +#include "RageLog.h" +#include "LightsDriver_snek.h" +#include "GameState.h" +#include "Game.h" + +REGISTER_LIGHTS_DRIVER_CLASS(snek); + +LightsDriver_snek::LightsDriver_snek() +{ + struct hid_device_info *devs, *cur_dev; + + memset(outputBuffer, 0x00, SNEK_HIDREPORT_SIZE); + + // Enumerate through the snek device to find the lighting interface. + devs = hid_enumerate(SNEK_VID, SNEK_PID); + cur_dev = devs; + + if (devs && cur_dev) + { + // Look for the desired interface number for lighting. + while (cur_dev) + { + if (cur_dev->vendor_id == SNEK_VID && + cur_dev->product_id == SNEK_PID && + cur_dev->interface_number == SNEK_LIGHTING_INTERFACENUM) + { + // Open the device via its path (only way to get interface) + handle = hid_open_path(cur_dev->path); + + break; + } + + cur_dev = cur_dev->next; + } + } + + if (!handle) + { + LOG->Warn("snek board lighting not found."); + } +} + +LightsDriver_snek::~LightsDriver_snek() +{ + if (handle) + { + hid_close(handle); + } + + // Finalize the hidapi library + hid_exit(); +} + +void LightsDriver_snek::SetBuffer(int index, bool lightState) +{ + // the first byte is the report ID, so we offset it here to adjust. + uint8_t index_offset = index + 1; + + // each index in the array represents a single light, + // the light will turn on for any value that isn't 0x00 + uint8_t val = lightState ? 0xFF : 0x00; + + // ensure report ID is set. + outputBuffer[0] = SNEK_REPORT_ID; + + // ensure the index is valid and the light value has changed. + if (index_offset < SNEK_HIDREPORT_SIZE && outputBuffer[index_offset] != val) + { + outputBuffer[index_offset] = val; + + // signal the loop to push the new buffer to the device. + stateChanged = true; + } +} + +void LightsDriver_snek::Set(const LightsState *ls) +{ + // do not make a message for a non-connected device. + if (!handle) + { + return; + } + + SetBuffer(SNEK_INDEX_DANCE_M_UL, ls->m_bCabinetLights[LIGHT_MARQUEE_UP_LEFT]); + SetBuffer(SNEK_INDEX_DANCE_M_UR, ls->m_bCabinetLights[LIGHT_MARQUEE_UP_RIGHT]); + SetBuffer(SNEK_INDEX_DANCE_M_LL, ls->m_bCabinetLights[LIGHT_MARQUEE_LR_LEFT]); + SetBuffer(SNEK_INDEX_DANCE_M_LR, ls->m_bCabinetLights[LIGHT_MARQUEE_LR_RIGHT]); + + SetBuffer(SNEK_INDEX_DANCE_P1_START, ls->m_bGameButtonLights[GameController_1][GAME_BUTTON_START]); + SetBuffer(SNEK_INDEX_DANCE_P2_START, ls->m_bGameButtonLights[GameController_2][GAME_BUTTON_START]); + + SetBuffer(SNEK_INDEX_DANCE_NEON, ls->m_bCabinetLights[LIGHT_BASS_LEFT] || ls->m_bCabinetLights[LIGHT_BASS_RIGHT]); + + SetBuffer(SNEK_INDEX_DANCE_P1_UP, ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_UP]); + SetBuffer(SNEK_INDEX_DANCE_P1_DOWN, ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_DOWN]); + SetBuffer(SNEK_INDEX_DANCE_P1_LEFT, ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_LEFT]); + SetBuffer(SNEK_INDEX_DANCE_P1_RIGHT, ls->m_bGameButtonLights[GameController_1][DANCE_BUTTON_RIGHT]); + + SetBuffer(SNEK_INDEX_DANCE_P2_UP, ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_UP]); + SetBuffer(SNEK_INDEX_DANCE_P2_DOWN, ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_DOWN]); + SetBuffer(SNEK_INDEX_DANCE_P2_LEFT, ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_LEFT]); + SetBuffer(SNEK_INDEX_DANCE_P2_RIGHT, ls->m_bGameButtonLights[GameController_2][DANCE_BUTTON_RIGHT]); + + // only push on changes. + if (stateChanged) + { + // TODO: Check for error/reconnect. + hid_write(handle, (unsigned char *)&outputBuffer, SNEK_HIDREPORT_SIZE); + stateChanged = false; + } +} diff --git a/src/arch/Lights/LightsDriver_snek.h b/src/arch/Lights/LightsDriver_snek.h new file mode 100644 index 0000000000..55eccbd53a --- /dev/null +++ b/src/arch/Lights/LightsDriver_snek.h @@ -0,0 +1,140 @@ +/* LightsDriver_snek: Control lights for the snek board by icedragon.io using hidapi */ + +#ifndef LightsDriver_snek_H +#define LightsDriver_snek_H + +/* + * -------------------------- NOTE -------------------------- + * + * This driver needs user read/write access to the icedragon.io snek board. + * This can be achieved by using a udev rule like this: + * + * SUBSYSTEMS=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="10a8", OWNER="dance", GROUP="dance", MODE="0660" + * + * Refer to your distribution's documentation on how to properly apply a udev rule. + * + * -------------------------- NOTE -------------------------- + */ + +#include "arch/Lights/LightsDriver.h" + +#include +#include "hidapi.h" + +// static information about the device in question. +#define SNEK_VID 0x2e8a +#define SNEK_PID 0x10a8 +#define SNEK_NUMOF_LIGHTS 32 + +// the first byte of the buffer is a static report id. +#define SNEK_HIDREPORT_SIZE (SNEK_NUMOF_LIGHTS + 1) +#define SNEK_REPORT_ID 0x01 +#define SNEK_LIGHTING_INTERFACENUM 0x02 + +// all indicies contain their respective 573 pinouts +enum SnekLightIndex +{ + SNEK_LIGHTINDEX_CN10_FL1 = 0, + SNEK_LIGHTINDEX_CN10_FL2, + SNEK_LIGHTINDEX_CN10_FL3, + SNEK_LIGHTINDEX_CN10_FL4, + SNEK_LIGHTINDEX_CN10_FL5, + SNEK_LIGHTINDEX_CN10_FL6, + SNEK_LIGHTINDEX_CN10_FL7, + SNEK_LIGHTINDEX_CN10_FL8, + + SNEK_LIGHTINDEX_CN13_FL1, + SNEK_LIGHTINDEX_CN13_FL2, + SNEK_LIGHTINDEX_CN13_FL3, + SNEK_LIGHTINDEX_CN13_FL4, + + SNEK_LIGHTINDEX_CN14_FL1, + SNEK_LIGHTINDEX_CN14_FL2, + SNEK_LIGHTINDEX_CN14_FL3, + SNEK_LIGHTINDEX_CN14_FL4, + + SNEK_LIGHTINDEX_CN12_FL1, + SNEK_LIGHTINDEX_CN12_FL2, + SNEK_LIGHTINDEX_CN12_FL3, + SNEK_LIGHTINDEX_CN12_FL4, + SNEK_LIGHTINDEX_CN12_FL5, + SNEK_LIGHTINDEX_CN12_FL6, + SNEK_LIGHTINDEX_CN12_FL7, + SNEK_LIGHTINDEX_CN12_FL8, + + SNEK_LIGHTINDEX_CN11_FL1, + SNEK_LIGHTINDEX_CN11_FL2, + SNEK_LIGHTINDEX_CN11_FL3, + SNEK_LIGHTINDEX_CN11_FL4, + SNEK_LIGHTINDEX_CN11_FL5, + SNEK_LIGHTINDEX_CN11_FL6, + SNEK_LIGHTINDEX_CN11_FL7, + SNEK_LIGHTINDEX_CN11_FL8, + + SNEK_LIGHTINDEX_MAX +}; + +// helper defines for dance mode. +#define SNEK_INDEX_DANCE_P1_START SNEK_LIGHTINDEX_CN10_FL3 +#define SNEK_INDEX_DANCE_P2_START SNEK_LIGHTINDEX_CN10_FL4 + +#define SNEK_INDEX_DANCE_M_LR SNEK_LIGHTINDEX_CN10_FL5 +#define SNEK_INDEX_DANCE_M_UR SNEK_LIGHTINDEX_CN10_FL6 +#define SNEK_INDEX_DANCE_M_LL SNEK_LIGHTINDEX_CN10_FL7 +#define SNEK_INDEX_DANCE_M_UL SNEK_LIGHTINDEX_CN10_FL8 + +#define SNEK_INDEX_DANCE_NEON SNEK_LIGHTINDEX_CN13_FL1 + +#define SNEK_INDEX_DANCE_P1_UP SNEK_LIGHTINDEX_CN12_FL1 +#define SNEK_INDEX_DANCE_P1_DOWN SNEK_LIGHTINDEX_CN12_FL2 +#define SNEK_INDEX_DANCE_P1_LEFT SNEK_LIGHTINDEX_CN12_FL3 +#define SNEK_INDEX_DANCE_P1_RIGHT SNEK_LIGHTINDEX_CN12_FL4 + +#define SNEK_INDEX_DANCE_P2_UP SNEK_LIGHTINDEX_CN11_FL1 +#define SNEK_INDEX_DANCE_P2_DOWN SNEK_LIGHTINDEX_CN11_FL2 +#define SNEK_INDEX_DANCE_P2_LEFT SNEK_LIGHTINDEX_CN11_FL3 +#define SNEK_INDEX_DANCE_P2_RIGHT SNEK_LIGHTINDEX_CN11_FL4 + +class LightsDriver_snek : public LightsDriver +{ +private: + hid_device *handle = nullptr; + + bool stateChanged = false; + uint8_t outputBuffer[SNEK_HIDREPORT_SIZE] = {0}; + + void SetBuffer(int index, bool lightState); + +public: + LightsDriver_snek(); + virtual ~LightsDriver_snek(); + + virtual void Set(const LightsState *ls); +}; + +#endif + +/* + * (c) 2025 din + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/src/arch/Lights/LightsDriver_stac.cpp b/src/arch/Lights/LightsDriver_stac.cpp new file mode 100644 index 0000000000..80e7deb3b6 --- /dev/null +++ b/src/arch/Lights/LightsDriver_stac.cpp @@ -0,0 +1,127 @@ +#include "global.h" +#include "RageLog.h" +#include "LightsDriver_stac.h" +#include "GameState.h" +#include "Game.h" + +REGISTER_LIGHTS_DRIVER_CLASS(stac); + +LightsDriver_stac::LightsDriver_stac() +{ + struct hid_device_info *devs, *cur_dev; + + memset(outputBuffer[GameController_1], 0x00, STAC_HIDREPORT_SIZE); + memset(outputBuffer[GameController_2], 0x00, STAC_HIDREPORT_SIZE); + + // Enumerate through to find the lighting interface. + devs = hid_enumerate(STAC_VID, 0); + cur_dev = devs; + + if (devs && cur_dev) + { + while (cur_dev) + { + if (cur_dev->vendor_id == STAC_VID && + cur_dev->interface_number == STAC_LIGHTING_INTERFACE) + { + if (cur_dev->product_id == STAC_PID_P1) + { + handle[0] = hid_open_path(cur_dev->path); + } + else if (cur_dev->product_id == STAC_PID_P2) + { + handle[1] = hid_open_path(cur_dev->path); + } + } + + cur_dev = cur_dev->next; + } + } + + if (!handle[0]) + { + LOG->Warn("stac P1 device not found."); + } + + if (!handle[1]) + { + LOG->Warn("stac P2 device not found."); + } +} + +LightsDriver_stac::~LightsDriver_stac() +{ + for (int i = 0; i < STAC_MAX_NUMBER; i++) + { + if (handle[i]) + { + hid_close(handle[i]); + } + } + + // Finalize the hidapi library + hid_exit(); +} + +void LightsDriver_stac::SetBuffer(int index, bool lightState, GameController ctrlNum) +{ + // the first byte is the report ID, so we offset it here to adjust. + uint8_t index_offset = index + 1; + + // each index in the array represents a single light, + // the light will turn on for any value that isn't 0x00 + uint8_t val = lightState ? 0xFF : 0x00; + + // ensure report ID is set. + outputBuffer[ctrlNum][0] = STAC_REPORT_ID; + + // ensure the index is valid and the light value has changed. + if (index_offset < STAC_HIDREPORT_SIZE && outputBuffer[ctrlNum][index_offset] != val) + { + outputBuffer[ctrlNum][index_offset] = val; + + // signal the loop to push the new buffer to the device. + stateChanged[ctrlNum] = true; + } +} + +void LightsDriver_stac::HandleState(const LightsState *ls, GameController ctrlNum) +{ + // do not create a message for an disconnected device. + if (!handle[ctrlNum]) + return; + + // check to see which game we are running as it can change during gameplay. + const InputScheme *pInput = &GAMESTATE->GetCurrentGame()->m_InputScheme; + RString sInputName = pInput->m_szName; + + if (sInputName.EqualsNoCase("dance")) + { + SetBuffer(STAC_LIGHTINDEX_BTN1, ls->m_bGameButtonLights[ctrlNum][DANCE_BUTTON_UP], ctrlNum); + SetBuffer(STAC_LIGHTINDEX_BTN2, ls->m_bGameButtonLights[ctrlNum][DANCE_BUTTON_DOWN], ctrlNum); + SetBuffer(STAC_LIGHTINDEX_BTN3, ls->m_bGameButtonLights[ctrlNum][DANCE_BUTTON_LEFT], ctrlNum); + SetBuffer(STAC_LIGHTINDEX_BTN4, ls->m_bGameButtonLights[ctrlNum][DANCE_BUTTON_RIGHT], ctrlNum); + } + else if (sInputName.EqualsNoCase("pump")) + { + SetBuffer(STAC_LIGHTINDEX_BTN1, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_UPLEFT], ctrlNum); + SetBuffer(STAC_LIGHTINDEX_BTN2, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_UPRIGHT], ctrlNum); + SetBuffer(STAC_LIGHTINDEX_BTN3, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_CENTER], ctrlNum); + SetBuffer(STAC_LIGHTINDEX_BTN4, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_DOWNLEFT], ctrlNum); + SetBuffer(STAC_LIGHTINDEX_BTN5, ls->m_bGameButtonLights[ctrlNum][PUMP_BUTTON_DOWNRIGHT], ctrlNum); + } + + // only push changes. + if (stateChanged[ctrlNum]) + { + // TODO: Check for error/reconnect. + hid_write(handle[ctrlNum], (unsigned char *)&outputBuffer[ctrlNum], STAC_HIDREPORT_SIZE); + stateChanged[ctrlNum] = false; + } +} + +void LightsDriver_stac::Set(const LightsState *ls) +{ + HandleState(ls, GameController_1); + HandleState(ls, GameController_2); +} diff --git a/src/arch/Lights/LightsDriver_Linux_stac.h b/src/arch/Lights/LightsDriver_stac.h similarity index 67% rename from src/arch/Lights/LightsDriver_Linux_stac.h rename to src/arch/Lights/LightsDriver_stac.h index 545628198d..a9ab4e12dc 100644 --- a/src/arch/Lights/LightsDriver_Linux_stac.h +++ b/src/arch/Lights/LightsDriver_stac.h @@ -1,7 +1,7 @@ -/* LightsDriver_Linux_stac: Control lights for the stac by icedragon.io via direct hidraw writes. */ +/* LightsDriver_stac: Control lights for the stac by icedragon.io using hidapi */ -#ifndef LightsDriver_Linux_stac_H -#define LightsDriver_Linux_stac_H +#ifndef LightsDriver_stac_H +#define LightsDriver_stac_H /* * -------------------------- NOTE -------------------------- @@ -13,7 +13,7 @@ * SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="eb5b", OWNER="dance", GROUP="dance", MODE="0660" * SUBSYSTEMS=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="eb5a", OWNER="dance", GROUP="dance", MODE="0660" * - * Refer to your distrobution's documentation on how to properly apply a udev rule. + * Refer to your distribution's documentation on how to properly apply a udev rule. * * -------------------------- NOTE -------------------------- */ @@ -23,48 +23,52 @@ #include #include "hidapi.h" -//static information about the device(s) in question. -#define STAC_VID "04d8" -#define STAC_PID_P1 "ea4b" -#define STAC_PID_P2 "ea4a" +// static information about the device(s) in question. +#define STAC_VID 0x04d8 +#define STAC_PID_P1 0xea4b +#define STAC_PID_P2 0xea4a #define STAC_NUMOF_LIGHTS 5 -//the first byte of the buffer is a static report id. +// the first byte of the buffer is a static report id. #define STAC_HIDREPORT_SIZE (STAC_NUMOF_LIGHTS + 1) #define STAC_REPORT_ID 0x01 +#define STAC_LIGHTING_INTERFACE 0x01 + +// total number of supported devices. +#define STAC_MAX_NUMBER 2 -//all indicies contain their respective 573 pinouts enum StacLightIndex { - STAC_LIGHTINDEX_BTN1 = 0, - STAC_LIGHTINDEX_BTN2 = 1, - STAC_LIGHTINDEX_BTN3 = 2, - STAC_LIGHTINDEX_BTN4 = 3, - STAC_LIGHTINDEX_BTN5 = 4, - STAC_LIGHTINDEX_MAX + STAC_LIGHTINDEX_BTN1 = 0, + STAC_LIGHTINDEX_BTN2 = 1, + STAC_LIGHTINDEX_BTN3 = 2, + STAC_LIGHTINDEX_BTN4 = 3, + STAC_LIGHTINDEX_BTN5 = 4, + STAC_LIGHTINDEX_MAX }; -class LightsDriver_Linux_stac : public LightsDriver +class LightsDriver_stac : public LightsDriver { private: - hid_device* handle[2]; + hid_device *handle[STAC_MAX_NUMBER] = {nullptr}; - bool stateChanged = false; - uint8_t outputBuffer[2][STAC_HIDREPORT_SIZE]; + bool stateChanged[STAC_MAX_NUMBER] = {false}; + uint8_t outputBuffer[STAC_MAX_NUMBER][STAC_HIDREPORT_SIZE] = {0}; - void HandleState(const LightsState *ls, StacDevice *dev, GameController ctrlNum); + void HandleState(const LightsState *ls, GameController ctrlNum); void SetBuffer(int index, bool lightState, GameController ctrlNum); -public: - LightsDriver_Linux_stac(); - virtual ~LightsDriver_Linux_stac(); - virtual void Set(const LightsState *ls); +public: + LightsDriver_stac(); + virtual ~LightsDriver_stac(); + + virtual void Set(const LightsState *ls); }; #endif /* - * (c) 2021 StepMania team + * (c) 2025 din * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a