hidapi: snek & stac support
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
#include "global.h"
|
||||
#include "LightsDriver_Linux_stac.h"
|
||||
#include "GameState.h"
|
||||
#include "Game.h"
|
||||
#include "RageLog.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
#if defined(HAVE_UNISTD_H)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#if defined(HAVE_FCNTL_H)
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
|
||||
#include <libudev.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/hidraw.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 <cstdint>
|
||||
#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.
|
||||
*/
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 <cstdint>
|
||||
#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
|
||||
Reference in New Issue
Block a user