From 72fa099088fa6f16207ce5cf8fbc4993a34fdd91 Mon Sep 17 00:00:00 2001 From: Matt Silva Date: Tue, 17 Jan 2023 05:41:17 +0000 Subject: [PATCH] Initial commit for HyperX Quadcast S --- .../HyperXQuadcastSController.cpp | 210 ++++++++++++++++++ .../HyperXQuadcastSController.h | 66 ++++++ .../HyperXQuadcastSControllerDetect.cpp | 172 ++++++++++++++ .../RGBController_HyperXQuadcastS.cpp | 181 +++++++++++++++ .../RGBController_HyperXQuadcastS.h | 46 ++++ OpenRGB.pro | 7 + 6 files changed, 682 insertions(+) create mode 100644 Controllers/HyperXQuadcastController/HyperXQuadcastSController.cpp create mode 100644 Controllers/HyperXQuadcastController/HyperXQuadcastSController.h create mode 100644 Controllers/HyperXQuadcastController/HyperXQuadcastSControllerDetect.cpp create mode 100644 Controllers/HyperXQuadcastController/RGBController_HyperXQuadcastS.cpp create mode 100644 Controllers/HyperXQuadcastController/RGBController_HyperXQuadcastS.h diff --git a/Controllers/HyperXQuadcastController/HyperXQuadcastSController.cpp b/Controllers/HyperXQuadcastController/HyperXQuadcastSController.cpp new file mode 100644 index 00000000..14ec6e5d --- /dev/null +++ b/Controllers/HyperXQuadcastController/HyperXQuadcastSController.cpp @@ -0,0 +1,210 @@ +/*-----------------------------------------*\ +| HyperXQuadcastSController.cpp | +| | +| Implementation for the HyperX | +| Quadcast S RGB microphone | +| | +| Matt Silva (thesilvanator) 2022 | +\*-----------------------------------------*/ +#include "HyperXQuadcastSController.h" + +HyperXQuadcastSController::HyperXQuadcastSController(hid_device* dev_handle, HXQS_HIDAPI_WRAPPER wrapper, std::string path) +{ + hidapi_wrapper = wrapper; + dev = dev_handle; + location = path; + + wchar_t serial_string[128]; + int ret = wrapper.get_serial_num_string(dev, serial_string, 128); + + if(ret != 0) + { + serial_number = ""; + } + else + { + std::wstring return_wstring = serial_string; + serial_number = std::string(return_wstring.begin(), return_wstring.end()); + } +} + +HyperXQuadcastSController::~HyperXQuadcastSController() +{ + + if(dev) + { + hidapi_wrapper.close(dev); + } +} + +std::string HyperXQuadcastSController::GetDeviceLocation() +{ + return location; +} + +std::string HyperXQuadcastSController::GetSerialString() +{ + return serial_number; +} + + +void HyperXQuadcastSController::SaveColors(std::vector colors, unsigned int num_frames) +{ + using namespace std::chrono_literals; + + int res; + unsigned int num_color_packets = 0; + unsigned int frame = 0; + unsigned char color[HXQS_PACKET_SIZE] = {0}; + unsigned char empty[HXQS_PACKET_SIZE] = {0}; + + num_color_packets = num_frames/8; + if(num_frames % 8) + { + num_color_packets++; + } + + lock.lock(); + + /*---------------------------------------------------------*\ + | Start Save Transaction | + | 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | + \*---------------------------------------------------------*/ + SendToRegister(0x53, (uint8_t)num_color_packets, 0); + + while(frame < num_frames) + { + memset(color, 0, HXQS_PACKET_SIZE); + + unsigned int i = 0; + while(i < 8 && frame < num_frames) + { + int index = HXQS_FRAME_SIZE * i; + RGBColor top = colors[frame*2]; + RGBColor bot = colors[frame*2 + 1]; + + color[index + 1] = 0x81; + color[index + 2] = RGBGetRValue(top); + color[index + 3] = RGBGetGValue(top); + color[index + 4] = RGBGetBValue(top); + color[index + 5] = 0x81; + color[index + 6] = RGBGetRValue(bot); + color[index + 7] = RGBGetGValue(bot); + color[index + 8] = RGBGetBValue(bot); + + i++; + frame++; + } + + std::this_thread::sleep_for(15ms); + res = hidapi_wrapper.send_feature_report(dev,color,HXQS_PACKET_SIZE); + } + + /*---------------------------------------------------------*\ + | Post Save Transaction | + | 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | + \*---------------------------------------------------------*/ + SendToRegister(0x02, 0, 0); + + /*---------------------------------------------------------*\ + | Stop Save Transaction | + | 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | + \*---------------------------------------------------------*/ + SendToRegister(0x23, 1, 0); + + SendEOT((uint8_t)num_frames); + + /*---------------------------------------------------------*\ + | Post Save Transaction | + | 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 | + \*---------------------------------------------------------*/ + SendToRegister(0x02, 0, 0); + + lock.unlock(); + + // likes to have one temporary direct packet after the save for some reason + SendDirect(colors); +} + +void HyperXQuadcastSController::SendDirect(std::vector colors) +{ + using namespace std::chrono_literals; + + if(colors.size() != 2) + { + LOG_ERROR("[HyperX Quadcast S] Unable to send direct packet, incorrect size: %d", colors.size()); + return; + } + + int res; + RGBColor c1 = colors[0]; + RGBColor c2 = colors[1]; + uint8_t buffer[HXQS_PACKET_SIZE]; + + // colour packet + memset(buffer,0,HXQS_PACKET_SIZE); + + //buffer[0x00] = 0; //buffer requires 0 at index 0 as device does not use ReportIDs + buffer[0x01] = 0x81; + buffer[0x02] = RGBGetRValue(c1); + buffer[0x03] = RGBGetGValue(c1); + buffer[0x04] = RGBGetBValue(c1); + buffer[0x05] = 0x81; + buffer[0x06] = RGBGetRValue(c2); + buffer[0x07] = RGBGetGValue(c2); + buffer[0x08] = RGBGetBValue(c2); + + lock.lock(); + + res = hidapi_wrapper.send_feature_report(dev, buffer, HXQS_PACKET_SIZE); + std::this_thread::sleep_for(15ms); + + SendToRegister(0xF2, 0, 1); + + lock.unlock(); +} + +void HyperXQuadcastSController::SendEOT(uint8_t frame_count) +{ + using namespace std::chrono_literals; + uint8_t buffer[HXQS_PACKET_SIZE]; + + memset(buffer,0,HXQS_PACKET_SIZE); + + //buffer[0x00] = 0; //buffer requires 0 at index 0 as device does not use ReportIDs + buffer[0x01] = 0x08; + buffer[0x3C] = 0x28; + buffer[0x3D] = frame_count; + buffer[0x3E] = 0x00; + buffer[0x3F] = 0xAA; + buffer[0x40] = 0x55; + + int result = hidapi_wrapper.send_feature_report(dev,buffer,HXQS_PACKET_SIZE); + LOG_DEBUG("[HyperX Quadcast S] SendEOT with frame count %02X wrote %d bytes", frame_count, result); + std::this_thread::sleep_for(15ms); +} + +void HyperXQuadcastSController::SendToRegister(uint8_t reg, uint8_t param1, uint8_t param2) +{ + using namespace std::chrono_literals; + uint8_t buffer[HXQS_PACKET_SIZE]; + + memset(buffer,0,HXQS_PACKET_SIZE); + + //buffer[0x00] = 0; //buffer requires 0 at index 0 as device does not use ReportIDs + buffer[0x01] = 0x04; + buffer[0x02] = reg; // 0xF2 Apply, 0x53 Save + buffer[0x08] = param1; + buffer[0x09] = param2; + + int result = hidapi_wrapper.send_feature_report(dev, buffer, HXQS_PACKET_SIZE); + if(result < 0) + { + LOG_ERROR("[HyperX Quadcast S] SendToRegister failed: %ls", hidapi_wrapper.error(dev)); + } + else + { + LOG_DEBUG("[HyperX Quadcast S] SendToRegister %02X with P1 %02X P2 %02X wrote %d bytes", reg, param1, param2, result); + } + std::this_thread::sleep_for(15ms); +} diff --git a/Controllers/HyperXQuadcastController/HyperXQuadcastSController.h b/Controllers/HyperXQuadcastController/HyperXQuadcastSController.h new file mode 100644 index 00000000..b8cc0977 --- /dev/null +++ b/Controllers/HyperXQuadcastController/HyperXQuadcastSController.h @@ -0,0 +1,66 @@ +/*-----------------------------------------*\ +| HyperXQuadcastSController.h | +| | +| Implementation for the HyperX | +| Quadcast S RGB microphone | +| | +| Matt Silva (thesilvanator) 2022 | +\*-----------------------------------------*/ +#pragma once + +#include +#include +#include "LogManager.h" +#include "RGBController.h" + +#define HXQS_PACKET_SIZE 64 + 1 +#define HXQS_FRAME_SIZE 8 + +// wrapper typedefs +typedef int (*HXQS_Report_Send_t)(hid_device*, const unsigned char*, size_t); +typedef int (*HXQS_Report_Get_t)(hid_device*, unsigned char*, size_t); +typedef int (*HXQS_Get_Serial_t)(hid_device*, wchar_t*, size_t); +typedef hid_device* (*HXQS_hid_open_path_t)(const char*); +typedef hid_device_info* (*HXQS_hid_enumerate_t) (unsigned short, unsigned short); +typedef void (*HXQS_hid_free_enumeration_t)(hid_device_info*); +typedef void (*HXQS_hid_close_t)(hid_device*); +typedef const wchar_t* (*HXQS_hid_error_t) (hid_device*); + +/*----------------------------------------------------*\ +| See comment at top of HyperXQuadcastSDetect.cpp for | +| details about the hidapi wrapper for this device | +\*----------------------------------------------------*/ +struct HXQS_HIDAPI_WRAPPER { + void* dyn_handle; + HXQS_Report_Send_t send_feature_report; + HXQS_Report_Get_t get_feature_report; + HXQS_Get_Serial_t get_serial_num_string; + HXQS_hid_open_path_t open_path; + HXQS_hid_enumerate_t enumerate; + HXQS_hid_free_enumeration_t free_enumeration; + HXQS_hid_close_t close; + HXQS_hid_error_t error; +}; + +class HyperXQuadcastSController +{ +public: + HyperXQuadcastSController(hid_device* dev, HXQS_HIDAPI_WRAPPER wrapper, std::string path); + ~HyperXQuadcastSController(); + + std::string GetDeviceLocation(); + std::string GetSerialString(); + + void SendDirect(std::vector color_data); + void SaveColors(std::vector colors, unsigned int num_frames); + +private: + hid_device* dev; + std::string location; + std::string serial_number; + std::mutex lock; + HXQS_HIDAPI_WRAPPER hidapi_wrapper; + + void SendEOT(uint8_t frame_count); + void SendToRegister(uint8_t reg, uint8_t param1, uint8_t param2); +}; diff --git a/Controllers/HyperXQuadcastController/HyperXQuadcastSControllerDetect.cpp b/Controllers/HyperXQuadcastController/HyperXQuadcastSControllerDetect.cpp new file mode 100644 index 00000000..e8dc5d67 --- /dev/null +++ b/Controllers/HyperXQuadcastController/HyperXQuadcastSControllerDetect.cpp @@ -0,0 +1,172 @@ +/*-----------------------------------------*\ +| HyperXQuadcastSControllerDetect.cpp | +| | +| Implementation for the HyperX | +| Quadcast S RGB microphone | +| | +| Matt Silva (thesilvanator) 2022 | +\*-----------------------------------------*/ + +#include "Detector.h" +#include "HyperXQuadcastSController.h" +#include "RGBController.h" +#include "RGBController_HyperXQuadcastS.h" +#include +#include + +#ifdef __linux__ +#include +#endif + +#include + + +#define HYPERX_QS_VID_NA 0x0951 +#define HYPERX_QS_PID_NA 0x171F +#define HYPERX_QS_VID_EU 0x03F0 +#define HYPERX_QS_PID_EU 0x0F8B + +const char* name = "HyperX Quadcast S"; + +#ifdef __linux__ +void FindAndAddHyperXQuadcastSDevice(unsigned int vid, unsigned int pid, + HXQS_HIDAPI_WRAPPER wrapper, + std::vector& rgb_controllers) +{ + std::string path; + + hid_device_info* devs = NULL; + hid_device *dev = NULL; + + HyperXQuadcastSController* controller; + RGBController_HyperXQuadcastS* rgb_controller; + + /*-----------------------------------------*\ + | Iterate over devices with corresponding | + | VID and PID and check if there is one | + | with interface 0 as that is what we use | + \*-----------------------------------------*/ + if(!(devs = wrapper.enumerate(vid, pid))) + { + LOG_DEBUG("[%s] Dynamic call to hid_enumerate failed or couldn't find a device (Linux only)", name); + return; + } + + hid_device_info *curr = devs; + while(curr) + { + if(curr->interface_number == 0) + { + path = curr->path; + LOG_DEBUG("[%s] Found device with correct 0 interface (Linux only): %s", name, path.c_str()); + break; + } + curr = curr->next; + } + + wrapper.free_enumeration(devs); + + if(!curr) + { + LOG_ERROR("[%s] Unable to find device with 0 interface (Linux only)", name); + return; + } + + + if(!(dev = wrapper.open_path(path.c_str()))) + { + LOG_ERROR("[%s] Dynamic call to hid_open_path failed (Linux only)", name); + return; + } + + LOG_DEBUG("[%s] Sucessfully opened device as a hidapi_device (Linux only)", name); + + + controller = new HyperXQuadcastSController(dev, wrapper, path); + rgb_controller = new RGBController_HyperXQuadcastS(controller); + + rgb_controller->name = name; + rgb_controllers.push_back(rgb_controller); +} + +void DetectHyperXQuadcastSControllers(std::vector& rgb_controllers) +{ + /*-----------------------------------------*\ + | Dynamically load hidapi-libusb and setup | + | wrapper for Linux platforms | + \*-----------------------------------------*/ + void* dyn_handle = NULL; + HXQS_HIDAPI_WRAPPER wrapper; + + if(!(dyn_handle = dlopen("libhidapi-libusb.so", RTLD_NOW | RTLD_NODELETE | RTLD_DEEPBIND))) + { + LOG_ERROR("[%s] Couldn't dynamically load hidapi-libusb (Linux only): %s", name, dlerror()); + return; + } + + wrapper = + { + .dyn_handle = dyn_handle, + .send_feature_report = (HXQS_Report_Send_t) dlsym(dyn_handle,"hid_send_feature_report"), + .get_feature_report = (HXQS_Report_Get_t) dlsym(dyn_handle,"hid_get_feature_report"), + .get_serial_num_string = (HXQS_Get_Serial_t) dlsym(dyn_handle,"hid_get_serial_number_string"), + .open_path = (HXQS_hid_open_path_t) dlsym(dyn_handle,"hid_open_path"), + .enumerate = (HXQS_hid_enumerate_t) dlsym(dyn_handle,"hid_enumerate"), + .free_enumeration = (HXQS_hid_free_enumeration_t) dlsym(dyn_handle,"hid_free_enumeration"), + .close = (HXQS_hid_close_t) dlsym(dyn_handle,"hid_close"), + .error = (HXQS_hid_error_t) dlsym(dyn_handle,"hid_free_enumeration") + }; + + if(!(wrapper.send_feature_report && wrapper.get_feature_report && + wrapper.open_path && wrapper.enumerate && wrapper.free_enumeration && + wrapper.close && wrapper.error && wrapper.get_serial_num_string)) + { + LOG_ERROR("[%s] Couldn't dynamically load one of hidapi-libusb functions for the wrapper (Linux only)", name); + return; + } + + FindAndAddHyperXQuadcastSDevice(HYPERX_QS_VID_NA, HYPERX_QS_PID_NA, wrapper, rgb_controllers); + FindAndAddHyperXQuadcastSDevice(HYPERX_QS_VID_EU, HYPERX_QS_PID_EU, wrapper, rgb_controllers); +} + +REGISTER_DETECTOR("HyperX Quadcast S", DetectHyperXQuadcastSControllers); +#else +void DetectHyperXQuadcastSControllers(hid_device_info* info, const std::string& name) +{ + hid_device *dev = hid_open_path(info->path); + + if(!dev) + { + LOG_ERROR("[%s] Unable to open device via hid_open_path(%s): %ls", name.c_str(), info->path, hid_error(dev)); + return; + } + + /*-----------------------------------------*\ + | Setup wrapper for Windows platforms just | + | using the already linked in hidapi | + | functions | + \*-----------------------------------------*/ + HXQS_HIDAPI_WRAPPER wrapper = + { + NULL, // dyn_handle + hid_send_feature_report, + hid_get_feature_report, + hid_get_serial_number_string, + hid_open_path, + hid_enumerate, + hid_free_enumeration, + hid_close, + hid_error + }; + + + HyperXQuadcastSController* controller = new HyperXQuadcastSController(dev, wrapper, info->path); + RGBController_HyperXQuadcastS *rgb_controller = new RGBController_HyperXQuadcastS(controller); + + rgb_controller->name = name; + ResourceManager::get()->RegisterRGBController(rgb_controller); + +} +REGISTER_HID_DETECTOR_IPU("HyperX Quadcast S", DetectHyperXQuadcastSControllers, HYPERX_QS_VID_NA, HYPERX_QS_PID_NA, 0, 0xFF90, 0xFF00); +REGISTER_HID_DETECTOR_IPU("HyperX Quadcast S", DetectHyperXQuadcastSControllers, HYPERX_QS_VID_EU, HYPERX_QS_PID_EU, 0, 0xFF90, 0xFF00); +#endif diff --git a/Controllers/HyperXQuadcastController/RGBController_HyperXQuadcastS.cpp b/Controllers/HyperXQuadcastController/RGBController_HyperXQuadcastS.cpp new file mode 100644 index 00000000..97f144ca --- /dev/null +++ b/Controllers/HyperXQuadcastController/RGBController_HyperXQuadcastS.cpp @@ -0,0 +1,181 @@ +/*-----------------------------------------*\ +| RGBController_HyperXQuadcastS.cpp | +| | +| Implementation for the HyperX | +| Quadcast S RGB microphone | +| | +| Matt Silva (thesilvanator) 2022 | +\*-----------------------------------------*/ + +/**------------------------------------------------------------------*\ + @name HyperX Quadcast S + @type USB + @save :white_check_mark: + @direct :white_check_mark: + @effects :x: + @detectors DetectHyperXQuadcastSControllers + @comment The HyperX Quadcast S has a manufacturer issue + with the interface it uses (0) for controlling its RGB. + HID requires that any HID interface have at least one + Interrupt IN endpoint; however, the HXQS does not, + even though its interface reports itelf as hid and + responds to hid requests. As such Linux doesn't bind + to the usbhid driver and it goes undetected by + hidapi-hidraw. Windows does detect it as hid and hidapi + finds and interacts with it just fine. To work around + the Linux issue, hidapi-libusb is loaded dynamically + using dlopen/dlsym as hidapi using a libusb backend is + able to find the device and interact with it. This + requires that you have support for dlopen/dlsym on your + Linux platform as well as hidapi-libusb (and libusb) + libraries installed in the standard dynamic library + path. + + The controller for this device has a wrapper for hidapi + functions so that the controller can be the same across + all platforms, but call the correct underlying functions + that are defined in the detector under an #ifdef + for that platform. + + Additionally, hidapi-libusb has an error that causes + hid_close() to hang on this device, see: + https://github.com/libusb/hidapi/issues/456 + This will be fixed on newer versions of hidapi-libusb, + but until then, OpenRGB will hang/crash if you try to + rescan devices once a HXQS has been detected during + program session. +\*-------------------------------------------------------------------*/ + +#include "RGBController_HyperXQuadcastS.h" +#include + +using namespace std::chrono_literals; + +RGBController_HyperXQuadcastS::RGBController_HyperXQuadcastS(HyperXQuadcastSController* controller_ptr) +{ + controller = controller_ptr; + + name = "HyperX QuadcastS Device"; + vendor = "HyperX"; + type = DEVICE_TYPE_MICROPHONE; + description = "HyperX QuadcastS Device"; + location = controller->GetDeviceLocation(); + serial = controller->GetSerialString(); + + mode Direct; + Direct.name = "Direct"; + Direct.flags = MODE_FLAG_HAS_BRIGHTNESS | MODE_FLAG_HAS_PER_LED_COLOR | MODE_FLAG_MANUAL_SAVE; + Direct.color_mode = MODE_COLORS_PER_LED; + Direct.brightness_min = 0; + Direct.brightness_max = 100; + Direct.colors.push_back(ToRGBColor(0xFF,0,0)); // Top LED + Direct.colors.push_back(ToRGBColor(0xFF,0,0)); // Bot LED + modes.push_back(Direct); + + mode Off; + Off.name = "Off"; + Off.flags = MODE_FLAG_MANUAL_SAVE; + Off.color_mode = MODE_COLORS_NONE; + Off.colors.push_back(ToRGBColor(0,0,0)); // Top LED + Off.colors.push_back(ToRGBColor(0,0,0)); // Bot LED + modes.push_back(Off); + + SetupZones(); + + keepalive_thread_run = 1; + keepalive_thread = new std::thread(&RGBController_HyperXQuadcastS::KeepaliveThread, this); +}; + +RGBController_HyperXQuadcastS::~RGBController_HyperXQuadcastS() +{ + keepalive_thread_run = 0; + keepalive_thread->join(); + delete keepalive_thread; + + delete controller; +} + +void RGBController_HyperXQuadcastS::SetupZones() +{ + led* Top = new led(); + led* Bot = new led(); + Top->name = std::string("Top"); + Top->value = 0; + Bot->name = std::string("Bottom"); + Bot->value = 1; + leds.push_back(*Top); + leds.push_back(*Bot); + + zone* Mic = new zone(); + Mic->name = std::string("Microphone"); + Mic->type = ZONE_TYPE_LINEAR; + Mic->leds_min = 2; + Mic->leds_max = 2; + Mic->leds_count = 2; + Mic->matrix_map = NULL; + zones.push_back(*Mic); + + SetupColors(); + + // make starting colors be the default starting + // colors for direct + colors = modes[HXQS_MODE_DIRECT].colors; +} + +void RGBController_HyperXQuadcastS::ResizeZone(int /*zone*/, int /*new_size*/) +{ + +} + +void RGBController_HyperXQuadcastS::DeviceUpdateLEDs() +{ + last_update_time = std::chrono::steady_clock::now(); + controller->SendDirect(colors); +} +void RGBController_HyperXQuadcastS::UpdateZoneLEDs(int zone) +{ + +} +void RGBController_HyperXQuadcastS::UpdateSingleLED(int led) +{ + +} +void RGBController_HyperXQuadcastS::DeviceUpdateMode() +{ + switch (active_mode) + { + case HXQS_MODE_DIRECT: + // make current colors this modes colors, in this case, + // the preserved direct colors from before switching off + colors = modes[active_mode].colors; + + break; + case HXQS_MODE_OFF: + modes[HXQS_MODE_DIRECT].colors = colors; // preserve directs previous colours + colors = modes[active_mode].colors; // make current colors this modes colors + + break; + default: + break; + } + + UpdateLEDs(); +} + +void RGBController_HyperXQuadcastS::DeviceSaveMode() +{ + LOG_DEBUG("[%s] Saving current direct colors to device", name.c_str()); + controller->SaveColors(colors,1); +} + +void RGBController_HyperXQuadcastS::KeepaliveThread() +{ + while(keepalive_thread_run.load()) + { + if((std::chrono::steady_clock::now() - last_update_time) > std::chrono::milliseconds(50)) + { + UpdateLEDs(); + } + std::this_thread::sleep_for(15ms); + } +} diff --git a/Controllers/HyperXQuadcastController/RGBController_HyperXQuadcastS.h b/Controllers/HyperXQuadcastController/RGBController_HyperXQuadcastS.h new file mode 100644 index 00000000..b227d429 --- /dev/null +++ b/Controllers/HyperXQuadcastController/RGBController_HyperXQuadcastS.h @@ -0,0 +1,46 @@ +/*-----------------------------------------*\ +| RGBController_HyperXQuadcastS.h | +| | +| Implementation for the HyperX | +| Quadcast S RGB microphone | +| | +| Matt Silva (thesilvanator) 2022 | +\*-----------------------------------------*/ + +#pragma once +#include + +#include "RGBController.h" +#include "HyperXQuadcastSController.h" + +enum +{ + HXQS_MODE_DIRECT = 0, + HXQS_MODE_OFF = 1, +}; + +class RGBController_HyperXQuadcastS : public RGBController +{ +public: + RGBController_HyperXQuadcastS(HyperXQuadcastSController* controller_ptr); + ~RGBController_HyperXQuadcastS(); + + void SetupZones(); + + void ResizeZone(int zone, int new_size); + + void DeviceUpdateLEDs(); + void UpdateZoneLEDs(int zone); + void UpdateSingleLED(int led); + + void DeviceUpdateMode(); + void DeviceSaveMode(); + + void KeepaliveThread(); + +private: + HyperXQuadcastSController* controller; + std::thread* keepalive_thread; + std::atomic keepalive_thread_run; + std::chrono::time_point last_update_time; +}; diff --git a/OpenRGB.pro b/OpenRGB.pro index e1b211e4..63f46a3f 100644 --- a/OpenRGB.pro +++ b/OpenRGB.pro @@ -131,6 +131,7 @@ INCLUDEPATH += Controllers/HyperXKeyboardController/ \ Controllers/HyperXMouseController/ \ Controllers/HyperXMousematController/ \ + Controllers/HyperXQuadcastController/ \ Controllers/IntelArcA770LEController/ \ Controllers/IonicoController/ \ Controllers/LEDStripController/ \ @@ -457,6 +458,8 @@ HEADERS += Controllers/HyperXMouseController/RGBController_HyperXPulsefireRaid.h \ Controllers/HyperXMousematController/HyperXMousematController.h \ Controllers/HyperXMousematController/RGBController_HyperXMousemat.h \ + Controllers/HyperXQuadcastController/HyperXQuadcastSController.h \ + Controllers/HyperXQuadcastController/RGBController_HyperXQuadcastS.h \ Controllers/IntelArcA770LEController/IntelArcA770LEController.h \ Controllers/IntelArcA770LEController/RGBController_IntelArcA770LE.h \ Controllers/IonicoController/IonicoController.h \ @@ -1045,6 +1048,9 @@ SOURCES += Controllers/HyperXMousematController/HyperXMousematController.cpp \ Controllers/HyperXMousematController/HyperXMousematControllerDetect.cpp \ Controllers/HyperXMousematController/RGBController_HyperXMousemat.cpp \ + Controllers/HyperXQuadcastController/HyperXQuadcastSController.cpp \ + Controllers/HyperXQuadcastController/HyperXQuadcastSControllerDetect.cpp \ + Controllers/HyperXQuadcastController/RGBController_HyperXQuadcastS.cpp \ Controllers/IntelArcA770LEController/IntelArcA770LEController.cpp \ Controllers/IntelArcA770LEController/IntelArcA770LEControllerDetect.cpp \ Controllers/IonicoController/IonicoController.cpp \ @@ -1619,6 +1625,7 @@ contains(QMAKE_PLATFORM, linux) { -lmbedx509 \ -lmbedtls \ -lmbedcrypto \ + -ldl \ COMPILER_VERSION = $$system($$QMAKE_CXX " -dumpversion") if (!versionAtLeast(COMPILER_VERSION, "9")) {